@vielzeug/forge 26.11.5 → 26.11.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -18,9 +18,7 @@ import { createForm } from '@vielzeug/forge';
18
18
  const form = createForm({
19
19
  initialValues: { profile: { email: '', name: '' } },
20
20
  validate: (value) =>
21
- value.profile.email.includes('@')
22
- ? undefined
23
- : [{ path: ['profile', 'email'], message: 'Enter a valid email' }],
21
+ value.profile.email.includes('@') ? undefined : [{ path: ['profile', 'email'], message: 'Enter a valid email' }],
24
22
  });
25
23
 
26
24
  const email = form.field('profile').field('email');
package/dist/_field.cjs CHANGED
@@ -1,2 +1,2 @@
1
- const e=require("./errors.cjs"),t=require("./core/path.cjs");let n=require("@vielzeug/arsenal");function makeFieldState(e,r){let i=t.readAtPath(e.value,r),a=t.readAtPath(e.baseline,r);return Object.freeze({dirty:!(0,n.isEqual)(i,a)||t.hasAtPath(e.value,r)!==t.hasAtPath(e.baseline,r),error:t.findIssue(e.issues,r),touched:t.readMeta(e.touched,r),value:i})}function createField(r,i){return Object.assign({get dirty(){return makeFieldState(i.readState(),r).dirty},get error(){return makeFieldState(i.readState(),r).error},reset(){i.ensureActive(`field().reset`),i.abortValidation(),i.resetValue(r)},set(e){i.ensureActive(`field().set`),i.abortValidation(),i.setValue(r,e)},get state(){return makeFieldState(i.readState(),r)},subscribe(e,t={}){i.ensureActive(`field().subscribe`);let n=makeFieldState(i.readState(),r);t.immediate&&i.invokeListener(()=>e(n));let filtered=t=>{let i=makeFieldState(t,r);(i.value!==n.value||i.error!==n.error||i.touched!==n.touched||i.dirty!==n.dirty)&&(n=i,e(i))};return i.addListener(filtered)},touch(){i.ensureActive(`field().touch`),i.setTouched(r,!0)},get touched(){return makeFieldState(i.readState(),r).touched},get value(){return makeFieldState(i.readState(),r).value}},{field(a){if(typeof a==`number`){t.assertArrayIndex(a);let n=t.readAtPath(i.readState().value,r);if(n!==void 0&&!Array.isArray(n))throw new e.ForgeConfigError(`Cannot index ${a} because the current field value is not an array.`);return createField([...r,a],i)}t.assertSafeKey(a);let o=t.readAtPath(i.readState().value,r);if(o!==void 0&&!(0,n.isPlainObject)(o))throw new e.ForgeConfigError(`Cannot select '${a}' because the current field value is not an object.`);return createField([...r,a],i)}})}exports.createField=createField;
1
+ const e=require("./core/path.cjs"),t=require("./errors.cjs");let n=require("@vielzeug/arsenal");function makeFieldState(t,r){let i=e.readAtPath(t.value,r),a=e.readAtPath(t.baseline,r);return Object.freeze({dirty:!(0,n.isEqual)(i,a)||e.hasAtPath(t.value,r)!==e.hasAtPath(t.baseline,r),error:e.findIssue(t.issues,r),touched:e.readMeta(t.touched,r),value:i})}function createField(r,i){return Object.assign({get dirty(){return makeFieldState(i.readState(),r).dirty},get error(){return makeFieldState(i.readState(),r).error},reset(){i.ensureActive(`field().reset`),i.abortValidation(),i.resetValue(r)},set(e){i.ensureActive(`field().set`),i.abortValidation(),i.setValue(r,e)},get state(){return makeFieldState(i.readState(),r)},subscribe(e,t={}){i.ensureActive(`field().subscribe`);let n=makeFieldState(i.readState(),r);t.immediate&&i.invokeListener(()=>e(n));let filtered=t=>{let i=makeFieldState(t,r);(i.value!==n.value||i.error!==n.error||i.touched!==n.touched||i.dirty!==n.dirty)&&(n=i,e(i))};return i.addListener(filtered)},touch(){i.ensureActive(`field().touch`),i.setTouched(r,!0)},get touched(){return makeFieldState(i.readState(),r).touched},get value(){return makeFieldState(i.readState(),r).value}},{field(a){if(typeof a==`number`){e.assertArrayIndex(a);let n=e.readAtPath(i.readState().value,r);if(n!==void 0&&!Array.isArray(n))throw new t.ForgeConfigError(`Cannot index ${a} because the current field value is not an array.`);return createField([...r,a],i)}e.assertSafeKey(a);let o=e.readAtPath(i.readState().value,r);if(o!==void 0&&!(0,n.isPlainObject)(o))throw new t.ForgeConfigError(`Cannot select '${a}' because the current field value is not an object.`);return createField([...r,a],i)}})}exports.createField=createField;
2
2
  //# sourceMappingURL=_field.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"_field.cjs","names":["readAtPath","isEqual","hasAtPath","findIssue","readMeta","ForgeConfigError","isRecord","require_errors","require_path","_vielzeug_arsenal","value","state","path","baseline","access","next","previous","subscribeOptions","listener","key","currentVal"],"sources":["../src/_field.ts"],"sourcesContent":["import { isEqual } from '@vielzeug/arsenal';\n\nimport {\n assertArrayIndex,\n assertSafeKey,\n findIssue,\n hasAtPath,\n isRecord,\n type MetaRoot,\n readAtPath,\n readMeta,\n} from './core/path.js';\nimport { ForgeConfigError } from './errors.js';\nimport type { Field, FieldState, SubscribeOptions, Unsubscribe, ValidationIssue } from './types.js';\n\ntype FieldSourceState = {\n baseline: unknown;\n issues: readonly ValidationIssue[] | undefined;\n touched: MetaRoot;\n value: unknown;\n};\n\nexport interface FieldAccess {\n abortValidation(): void;\n addListener(listener: (state: FieldSourceState) => void): Unsubscribe;\n ensureActive(operation: string): void;\n invokeListener(listener: () => void): void;\n readState(): FieldSourceState;\n resetValue(path: readonly (string | number)[]): void;\n setTouched(path: readonly (string | number)[], touched: boolean): void;\n setValue(path: readonly (string | number)[], next: unknown | ((previous: unknown) => unknown)): void;\n}\n\nfunction makeFieldState<V>(state: FieldSourceState, path: readonly (string | number)[]): FieldState<V> {\n const value = readAtPath<V>(state.value, path);\n const baseline = readAtPath<V>(state.baseline, path);\n\n return Object.freeze({\n dirty: !isEqual(value, baseline) || hasAtPath(state.value, path) !== hasAtPath(state.baseline, path),\n error: findIssue(state.issues, path),\n touched: readMeta(state.touched, path),\n value: value as FieldState<V>['value'],\n });\n}\n\nexport function createField<V>(path: readonly (string | number)[], access: FieldAccess): Field<V> {\n const common = {\n get dirty() {\n return makeFieldState<V>(access.readState(), path).dirty;\n },\n get error() {\n return makeFieldState<V>(access.readState(), path).error;\n },\n reset() {\n access.ensureActive('field().reset');\n access.abortValidation();\n access.resetValue(path);\n },\n set(next: V | ((previous: FieldState<V>['value']) => V)) {\n access.ensureActive('field().set');\n access.abortValidation();\n\n access.setValue(path, next as unknown | ((previous: unknown) => unknown));\n },\n get state() {\n return makeFieldState<V>(access.readState(), path);\n },\n subscribe(listener: (state: FieldState<V>) => void, subscribeOptions: SubscribeOptions = {}): Unsubscribe {\n access.ensureActive('field().subscribe');\n\n let previous = makeFieldState<V>(access.readState(), path);\n\n if (subscribeOptions.immediate) access.invokeListener(() => listener(previous));\n\n const filtered = (state: FieldSourceState) => {\n const next = makeFieldState<V>(state, path);\n\n if (\n next.value === previous.value &&\n next.error === previous.error &&\n next.touched === previous.touched &&\n next.dirty === previous.dirty\n ) {\n return;\n }\n\n previous = next;\n listener(next);\n };\n\n return access.addListener(filtered);\n },\n touch() {\n access.ensureActive('field().touch');\n access.setTouched(path, true);\n },\n get touched() {\n return makeFieldState<V>(access.readState(), path).touched;\n },\n get value() {\n return makeFieldState<V>(access.readState(), path).value;\n },\n };\n\n return Object.assign(common, {\n field(key: string | number): Field<unknown> {\n if (typeof key === 'number') {\n assertArrayIndex(key);\n const currentVal = readAtPath(access.readState().value, path);\n\n if (currentVal !== undefined && !Array.isArray(currentVal)) {\n throw new ForgeConfigError(`Cannot index ${key} because the current field value is not an array.`);\n }\n\n return createField([...path, key], access);\n }\n\n assertSafeKey(key);\n\n const currentVal = readAtPath(access.readState().value, path);\n\n if (currentVal !== undefined && !isRecord(currentVal)) {\n throw new ForgeConfigError(`Cannot select '${key}' because the current field value is not an object.`);\n }\n\n return createField([...path, key], access);\n },\n }) as unknown as Field<V>;\n}\n"],"mappings":"A,gGAiCA,SAAS,eAAkB,EAAyB,EAAmD,CACrG,IAAMU,EAAQV,EAAAA,WAAcW,EAAM,MAAOC,CAAI,EACvCC,EAAWb,EAAAA,WAAcW,EAAM,SAAUC,CAAI,EAEnD,OAAO,OAAO,OAAO,CACnB,MAAO,EAAA,EAACX,EAAAA,QAAAA,CAAQS,EAAOG,CAAQ,GAAKX,EAAAA,UAAUS,EAAM,MAAOC,CAAI,IAAMV,EAAAA,UAAUS,EAAM,SAAUC,CAAI,EACnG,MAAOT,EAAAA,UAAUQ,EAAM,OAAQC,CAAI,EACnC,QAASR,EAAAA,SAASO,EAAM,QAASC,CAAI,EAC9B,OACT,CAAC,CACH,CAEA,SAAgB,YAAe,EAAoC,EAA+B,CA2DhG,OAAO,OAAO,OAAO,CAzDnB,IAAI,OAAQ,CACV,OAAO,eAAkBE,EAAO,UAAU,EAAGF,CAAI,CAAC,CAAC,KACrD,EACA,IAAI,OAAQ,CACV,OAAO,eAAkBE,EAAO,UAAU,EAAGF,CAAI,CAAC,CAAC,KACrD,EACA,OAAQ,CACNE,EAAO,aAAa,eAAe,EACnCA,EAAO,gBAAgB,EACvBA,EAAO,WAAWF,CAAI,CACxB,EACA,IAAI,EAAqD,CACvDE,EAAO,aAAa,aAAa,EACjCA,EAAO,gBAAgB,EAEvBA,EAAO,SAASF,EAAMG,CAAkD,CAC1E,EACA,IAAI,OAAQ,CACV,OAAO,eAAkBD,EAAO,UAAU,EAAGF,CAAI,CACnD,EACA,UAAU,EAA0C,EAAqC,CAAC,EAAgB,CACxGE,EAAO,aAAa,mBAAmB,EAEvC,IAAIE,EAAW,eAAkBF,EAAO,UAAU,EAAGF,CAAI,EAErDK,EAAiB,WAAWH,EAAO,mBAAqBI,EAASF,CAAQ,CAAC,EAE9E,IAAM,SAAY,GAA4B,CAC5C,IAAMD,EAAO,eAAkBJ,EAAOC,CAAI,GAGxCG,EAAK,QAAUC,EAAS,OACxBD,EAAK,QAAUC,EAAS,OACxBD,EAAK,UAAYC,EAAS,SAC1BD,EAAK,QAAUC,EAAS,SAK1B,EAAWD,EACXG,EAASH,CAAI,EACf,EAEA,OAAOD,EAAO,YAAY,QAAQ,CACpC,EACA,OAAQ,CACNA,EAAO,aAAa,eAAe,EACnCA,EAAO,WAAWF,EAAM,EAAI,CAC9B,EACA,IAAI,SAAU,CACZ,OAAO,eAAkBE,EAAO,UAAU,EAAGF,CAAI,CAAC,CAAC,OACrD,EACA,IAAI,OAAQ,CACV,OAAO,eAAkBE,EAAO,UAAU,EAAGF,CAAI,CAAC,CAAC,KACrD,CAGmB,EAAQ,CAC3B,MAAM,EAAsC,CAC1C,GAAI,OAAOO,GAAQ,SAAU,CAC3BX,EAAA,iBAAiBW,CAAG,EACpB,IAAMC,EAAapB,EAAAA,WAAWc,EAAO,UAAU,CAAC,CAAC,MAAOF,CAAI,EAE5D,GAAIQ,IAAe,IAAA,IAAa,CAAC,MAAM,QAAQA,CAAU,EACvD,MAAM,IAAIf,EAAAA,iBAAiB,gBAAgBc,EAAI,kDAAkD,EAGnG,OAAO,YAAY,CAAC,GAAGP,EAAMO,CAAG,EAAGL,CAAM,CAC3C,CAEAN,EAAA,cAAcW,CAAG,EAEjB,IAAMC,EAAapB,EAAAA,WAAWc,EAAO,UAAU,CAAC,CAAC,MAAOF,CAAI,EAE5D,GAAIQ,IAAe,IAAA,IAAa,EAAA,EAACd,EAAAA,cAAAA,CAASc,CAAU,EAClD,MAAM,IAAIf,EAAAA,iBAAiB,kBAAkBc,EAAI,oDAAoD,EAGvG,OAAO,YAAY,CAAC,GAAGP,EAAMO,CAAG,EAAGL,CAAM,CAC3C,CACF,CAAC,CACH,C"}
1
+ {"version":3,"file":"_field.cjs","names":["readAtPath","isEqual","hasAtPath","findIssue","readMeta","ForgeConfigError","isRecord","require_path","require_errors","_vielzeug_arsenal","value","state","path","baseline","access","next","previous","subscribeOptions","listener","key","currentVal"],"sources":["../src/_field.ts"],"sourcesContent":["import { isEqual } from '@vielzeug/arsenal';\n\nimport {\n assertArrayIndex,\n assertSafeKey,\n findIssue,\n hasAtPath,\n isRecord,\n type MetaRoot,\n readAtPath,\n readMeta,\n} from './core/path.js';\nimport { ForgeConfigError } from './errors.js';\nimport type { Field, FieldState, SubscribeOptions, Unsubscribe, ValidationIssue } from './types.js';\n\ntype FieldSourceState = {\n baseline: unknown;\n issues: readonly ValidationIssue[] | undefined;\n touched: MetaRoot;\n value: unknown;\n};\n\nexport interface FieldAccess {\n abortValidation(): void;\n addListener(listener: (state: FieldSourceState) => void): Unsubscribe;\n ensureActive(operation: string): void;\n invokeListener(listener: () => void): void;\n readState(): FieldSourceState;\n resetValue(path: readonly (string | number)[]): void;\n setTouched(path: readonly (string | number)[], touched: boolean): void;\n setValue(path: readonly (string | number)[], next: unknown | ((previous: unknown) => unknown)): void;\n}\n\nfunction makeFieldState<V>(state: FieldSourceState, path: readonly (string | number)[]): FieldState<V> {\n const value = readAtPath<V>(state.value, path);\n const baseline = readAtPath<V>(state.baseline, path);\n\n return Object.freeze({\n dirty: !isEqual(value, baseline) || hasAtPath(state.value, path) !== hasAtPath(state.baseline, path),\n error: findIssue(state.issues, path),\n touched: readMeta(state.touched, path),\n value: value as FieldState<V>['value'],\n });\n}\n\nexport function createField<V>(path: readonly (string | number)[], access: FieldAccess): Field<V> {\n const common = {\n get dirty() {\n return makeFieldState<V>(access.readState(), path).dirty;\n },\n get error() {\n return makeFieldState<V>(access.readState(), path).error;\n },\n reset() {\n access.ensureActive('field().reset');\n access.abortValidation();\n access.resetValue(path);\n },\n set(next: V | ((previous: FieldState<V>['value']) => V)) {\n access.ensureActive('field().set');\n access.abortValidation();\n\n access.setValue(path, next as unknown | ((previous: unknown) => unknown));\n },\n get state() {\n return makeFieldState<V>(access.readState(), path);\n },\n subscribe(listener: (state: FieldState<V>) => void, subscribeOptions: SubscribeOptions = {}): Unsubscribe {\n access.ensureActive('field().subscribe');\n\n let previous = makeFieldState<V>(access.readState(), path);\n\n if (subscribeOptions.immediate) access.invokeListener(() => listener(previous));\n\n const filtered = (state: FieldSourceState) => {\n const next = makeFieldState<V>(state, path);\n\n if (\n next.value === previous.value &&\n next.error === previous.error &&\n next.touched === previous.touched &&\n next.dirty === previous.dirty\n ) {\n return;\n }\n\n previous = next;\n listener(next);\n };\n\n return access.addListener(filtered);\n },\n touch() {\n access.ensureActive('field().touch');\n access.setTouched(path, true);\n },\n get touched() {\n return makeFieldState<V>(access.readState(), path).touched;\n },\n get value() {\n return makeFieldState<V>(access.readState(), path).value;\n },\n };\n\n return Object.assign(common, {\n field(key: string | number): Field<unknown> {\n if (typeof key === 'number') {\n assertArrayIndex(key);\n const currentVal = readAtPath(access.readState().value, path);\n\n if (currentVal !== undefined && !Array.isArray(currentVal)) {\n throw new ForgeConfigError(`Cannot index ${key} because the current field value is not an array.`);\n }\n\n return createField([...path, key], access);\n }\n\n assertSafeKey(key);\n\n const currentVal = readAtPath(access.readState().value, path);\n\n if (currentVal !== undefined && !isRecord(currentVal)) {\n throw new ForgeConfigError(`Cannot select '${key}' because the current field value is not an object.`);\n }\n\n return createField([...path, key], access);\n },\n }) as unknown as Field<V>;\n}\n"],"mappings":"A,gGAiCA,SAAS,eAAkB,EAAyB,EAAmD,CACrG,IAAMU,EAAQV,EAAAA,WAAcW,EAAM,MAAOC,CAAI,EACvCC,EAAWb,EAAAA,WAAcW,EAAM,SAAUC,CAAI,EAEnD,OAAO,OAAO,OAAO,CACnB,MAAO,EAAA,EAACX,EAAAA,QAAAA,CAAQS,EAAOG,CAAQ,GAAKX,EAAAA,UAAUS,EAAM,MAAOC,CAAI,IAAMV,EAAAA,UAAUS,EAAM,SAAUC,CAAI,EACnG,MAAOT,EAAAA,UAAUQ,EAAM,OAAQC,CAAI,EACnC,QAASR,EAAAA,SAASO,EAAM,QAASC,CAAI,EAC9B,OACT,CAAC,CACH,CAEA,SAAgB,YAAe,EAAoC,EAA+B,CA2DhG,OAAO,OAAO,OAAO,CAzDnB,IAAI,OAAQ,CACV,OAAO,eAAkBE,EAAO,UAAU,EAAGF,CAAI,CAAC,CAAC,KACrD,EACA,IAAI,OAAQ,CACV,OAAO,eAAkBE,EAAO,UAAU,EAAGF,CAAI,CAAC,CAAC,KACrD,EACA,OAAQ,CACNE,EAAO,aAAa,eAAe,EACnCA,EAAO,gBAAgB,EACvBA,EAAO,WAAWF,CAAI,CACxB,EACA,IAAI,EAAqD,CACvDE,EAAO,aAAa,aAAa,EACjCA,EAAO,gBAAgB,EAEvBA,EAAO,SAASF,EAAMG,CAAkD,CAC1E,EACA,IAAI,OAAQ,CACV,OAAO,eAAkBD,EAAO,UAAU,EAAGF,CAAI,CACnD,EACA,UAAU,EAA0C,EAAqC,CAAC,EAAgB,CACxGE,EAAO,aAAa,mBAAmB,EAEvC,IAAIE,EAAW,eAAkBF,EAAO,UAAU,EAAGF,CAAI,EAErDK,EAAiB,WAAWH,EAAO,mBAAqBI,EAASF,CAAQ,CAAC,EAE9E,IAAM,SAAY,GAA4B,CAC5C,IAAMD,EAAO,eAAkBJ,EAAOC,CAAI,GAGxCG,EAAK,QAAUC,EAAS,OACxBD,EAAK,QAAUC,EAAS,OACxBD,EAAK,UAAYC,EAAS,SAC1BD,EAAK,QAAUC,EAAS,SAK1B,EAAWD,EACXG,EAASH,CAAI,EACf,EAEA,OAAOD,EAAO,YAAY,QAAQ,CACpC,EACA,OAAQ,CACNA,EAAO,aAAa,eAAe,EACnCA,EAAO,WAAWF,EAAM,EAAI,CAC9B,EACA,IAAI,SAAU,CACZ,OAAO,eAAkBE,EAAO,UAAU,EAAGF,CAAI,CAAC,CAAC,OACrD,EACA,IAAI,OAAQ,CACV,OAAO,eAAkBE,EAAO,UAAU,EAAGF,CAAI,CAAC,CAAC,KACrD,CAGmB,EAAQ,CAC3B,MAAM,EAAsC,CAC1C,GAAI,OAAOO,GAAQ,SAAU,CAC3BZ,EAAA,iBAAiBY,CAAG,EACpB,IAAMC,EAAapB,EAAAA,WAAWc,EAAO,UAAU,CAAC,CAAC,MAAOF,CAAI,EAE5D,GAAIQ,IAAe,IAAA,IAAa,CAAC,MAAM,QAAQA,CAAU,EACvD,MAAM,IAAIf,EAAAA,iBAAiB,gBAAgBc,EAAI,kDAAkD,EAGnG,OAAO,YAAY,CAAC,GAAGP,EAAMO,CAAG,EAAGL,CAAM,CAC3C,CAEAP,EAAA,cAAcY,CAAG,EAEjB,IAAMC,EAAapB,EAAAA,WAAWc,EAAO,UAAU,CAAC,CAAC,MAAOF,CAAI,EAE5D,GAAIQ,IAAe,IAAA,IAAa,EAAA,EAACd,EAAAA,cAAAA,CAASc,CAAU,EAClD,MAAM,IAAIf,EAAAA,iBAAiB,kBAAkBc,EAAI,oDAAoD,EAGvG,OAAO,YAAY,CAAC,GAAGP,EAAMO,CAAG,EAAGL,CAAM,CAC3C,CACF,CAAC,CACH,C"}
package/dist/_field.js CHANGED
@@ -1,66 +1,65 @@
1
- import { ForgeConfigError as e } from "./errors.js";
2
- import { assertArrayIndex as t, assertSafeKey as n, findIssue as r, hasAtPath as i, isRecord as a, readAtPath as o, readMeta as s } from "./core/path.js";
1
+ import { assertArrayIndex as e, assertSafeKey as t, findIssue as n, hasAtPath as r, isRecord as i, readAtPath as a, readMeta as o } from "./core/path.js";
2
+ import { ForgeConfigError as s } from "./errors.js";
3
3
  import { isEqual as c } from "@vielzeug/arsenal";
4
- //#region src/_field.ts
4
+ // src/_field.ts
5
5
  function makeFieldState(e, t) {
6
- let n = o(e.value, t), a = o(e.baseline, t);
6
+ let i = a(e.value, t), s = a(e.baseline, t);
7
7
  return Object.freeze({
8
- dirty: !c(n, a) || i(e.value, t) !== i(e.baseline, t),
9
- error: r(e.issues, t),
10
- touched: s(e.touched, t),
11
- value: n
8
+ dirty: !c(i, s) || r(e.value, t) !== r(e.baseline, t),
9
+ error: n(e.issues, t),
10
+ touched: o(e.touched, t),
11
+ value: i
12
12
  });
13
13
  }
14
- function createField(r, i) {
14
+ function createField(n, r) {
15
15
  return Object.assign({
16
16
  get dirty() {
17
- return makeFieldState(i.readState(), r).dirty;
17
+ return makeFieldState(r.readState(), n).dirty;
18
18
  },
19
19
  get error() {
20
- return makeFieldState(i.readState(), r).error;
20
+ return makeFieldState(r.readState(), n).error;
21
21
  },
22
22
  reset() {
23
- i.ensureActive("field().reset"), i.abortValidation(), i.resetValue(r);
23
+ r.ensureActive("field().reset"), r.abortValidation(), r.resetValue(n);
24
24
  },
25
25
  set(e) {
26
- i.ensureActive("field().set"), i.abortValidation(), i.setValue(r, e);
26
+ r.ensureActive("field().set"), r.abortValidation(), r.setValue(n, e);
27
27
  },
28
28
  get state() {
29
- return makeFieldState(i.readState(), r);
29
+ return makeFieldState(r.readState(), n);
30
30
  },
31
31
  subscribe(e, t = {}) {
32
- i.ensureActive("field().subscribe");
33
- let n = makeFieldState(i.readState(), r);
34
- t.immediate && i.invokeListener(() => e(n));
32
+ r.ensureActive("field().subscribe");
33
+ let i = makeFieldState(r.readState(), n);
34
+ t.immediate && r.invokeListener(() => e(i));
35
35
  let filtered = (t) => {
36
- let i = makeFieldState(t, r);
37
- (i.value !== n.value || i.error !== n.error || i.touched !== n.touched || i.dirty !== n.dirty) && (n = i, e(i));
36
+ let r = makeFieldState(t, n);
37
+ (r.value !== i.value || r.error !== i.error || r.touched !== i.touched || r.dirty !== i.dirty) && (i = r, e(r));
38
38
  };
39
- return i.addListener(filtered);
39
+ return r.addListener(filtered);
40
40
  },
41
41
  touch() {
42
- i.ensureActive("field().touch"), i.setTouched(r, !0);
42
+ r.ensureActive("field().touch"), r.setTouched(n, !0);
43
43
  },
44
44
  get touched() {
45
- return makeFieldState(i.readState(), r).touched;
45
+ return makeFieldState(r.readState(), n).touched;
46
46
  },
47
47
  get value() {
48
- return makeFieldState(i.readState(), r).value;
48
+ return makeFieldState(r.readState(), n).value;
49
49
  }
50
- }, { field(s) {
51
- if (typeof s == "number") {
52
- t(s);
53
- let n = o(i.readState().value, r);
54
- if (n !== void 0 && !Array.isArray(n)) throw new e(`Cannot index ${s} because the current field value is not an array.`);
55
- return createField([...r, s], i);
50
+ }, { field(o) {
51
+ if (typeof o == "number") {
52
+ e(o);
53
+ let t = a(r.readState().value, n);
54
+ if (t !== void 0 && !Array.isArray(t)) throw new s(`Cannot index ${o} because the current field value is not an array.`);
55
+ return createField([...n, o], r);
56
56
  }
57
- n(s);
58
- let c = o(i.readState().value, r);
59
- if (c !== void 0 && !a(c)) throw new e(`Cannot select '${s}' because the current field value is not an object.`);
60
- return createField([...r, s], i);
57
+ t(o);
58
+ let c = a(r.readState().value, n);
59
+ if (c !== void 0 && !i(c)) throw new s(`Cannot select '${o}' because the current field value is not an object.`);
60
+ return createField([...n, o], r);
61
61
  } });
62
62
  }
63
- //#endregion
64
63
  export { createField };
65
64
 
66
65
  //# sourceMappingURL=_field.js.map
package/dist/_notify.js CHANGED
@@ -1,4 +1,4 @@
1
- //#region src/_notify.ts
1
+ // src/_notify.ts
2
2
  function rethrowAsync(e) {
3
3
  queueMicrotask(() => {
4
4
  throw e;
@@ -44,7 +44,6 @@ function createNotifier(e) {
44
44
  }
45
45
  };
46
46
  }
47
- //#endregion
48
47
  export { createNotifier };
49
48
 
50
49
  //# sourceMappingURL=_notify.js.map
package/dist/core/path.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { ForgeConfigError as e } from "../errors.js";
2
2
  import { isPlainObject as t } from "@vielzeug/arsenal";
3
- //#region src/core/path.ts
3
+ // src/core/path.ts
4
4
  var n = /* @__PURE__ */ new Set([
5
5
  "__proto__",
6
6
  "constructor",
@@ -214,7 +214,6 @@ function findIssue(e, t) {
214
214
  for (let n of e) if (pathEquals(n.path, t)) return n.message;
215
215
  }
216
216
  }
217
- //#endregion
218
217
  export { assertArrayIndex, assertSafeKey, findIssue, formatValuePath, hasAtPath, immutable, t as isRecord, isUnsafeKey, pathEquals, readAtPath, readMeta, resetAtPath, toPlainValues, touchAll, writeAtPath, writeMeta };
219
218
 
220
219
  //# sourceMappingURL=path.js.map
package/dist/dom.js CHANGED
@@ -1,4 +1,4 @@
1
- //#region src/dom.ts
1
+ // src/dom.ts
2
2
  function bindField(e, t, n) {
3
3
  let r = n.event ?? "input", i = !1, a = !1, o = t.value, onInput = () => {
4
4
  i = !0;
@@ -14,7 +14,6 @@ function bindField(e, t, n) {
14
14
  e.removeEventListener(r, onInput), e.removeEventListener("blur", onBlur), s();
15
15
  };
16
16
  }
17
- //#endregion
18
17
  export { bindField };
19
18
 
20
19
  //# sourceMappingURL=dom.js.map
package/dist/errors.js CHANGED
@@ -1,4 +1,4 @@
1
- //#region src/errors.ts
1
+ // src/errors.ts
2
2
  var ForgeError = class extends Error {
3
3
  constructor(e, t) {
4
4
  super(e, t), this.name = new.target.name, Object.setPrototypeOf(this, new.target.prototype);
@@ -8,7 +8,6 @@ var ForgeError = class extends Error {
8
8
  super(e ? `Cannot call ${e}() on a disposed form` : "Cannot modify a disposed form");
9
9
  }
10
10
  }, ForgeConfigError = class extends ForgeError {}, ForgeSubmitError = class extends ForgeError {}, ForgeValidationError = class extends ForgeError {};
11
- //#endregion
12
11
  export { ForgeConfigError, ForgeDisposedError, ForgeError, ForgeSubmitError, ForgeValidationError };
13
12
 
14
13
  //# sourceMappingURL=errors.js.map
package/dist/forge.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { abortable as e, isEqual as t, isPlainObject as n } from "@vielzeug/arsenal";
2
- //#region src/errors.ts
2
+ // src/errors.ts
3
3
  var ForgeError = class extends Error {
4
4
  constructor(e, t) {
5
5
  super(e, t), this.name = new.target.name, Object.setPrototypeOf(this, new.target.prototype);
@@ -221,8 +221,7 @@ function findIssue(e, t) {
221
221
  for (let n of e) if (pathEquals(n.path, t)) return n.message;
222
222
  }
223
223
  }
224
- //#endregion
225
- //#region src/_field.ts
224
+ // src/_field.ts
226
225
  function makeFieldState(e, n) {
227
226
  let r = readAtPath(e.value, n), i = readAtPath(e.baseline, n);
228
227
  return Object.freeze({
@@ -281,8 +280,7 @@ function createField(e, t) {
281
280
  return createField([...e, r], t);
282
281
  } });
283
282
  }
284
- //#endregion
285
- //#region src/_notify.ts
283
+ // src/_notify.ts
286
284
  function rethrowAsync(e) {
287
285
  queueMicrotask(() => {
288
286
  throw e;
@@ -328,8 +326,7 @@ function createNotifier(e) {
328
326
  }
329
327
  };
330
328
  }
331
- //#endregion
332
- //#region src/form.ts
329
+ // src/form.ts
333
330
  function normalizeIssues(e) {
334
331
  if (e !== void 0 && e.length !== 0) return Object.freeze(e.map((e) => {
335
332
  if (!e || typeof e.message != "string" || !Array.isArray(e.path)) throw new ForgeConfigError("Validators must return issues with a string message and array path.");
@@ -557,12 +554,10 @@ function createForm(t) {
557
554
  };
558
555
  return g;
559
556
  }
560
- //#endregion
561
- //#region src/plain.ts
557
+ // src/plain.ts
562
558
  function toPlainValues(e) {
563
559
  return toPlainValues$1(e);
564
560
  }
565
- //#endregion
566
561
  export { ForgeConfigError, ForgeDisposedError, ForgeError, ForgeSubmitError, ForgeValidationError, createForm, toPlainValues };
567
562
 
568
563
  //# sourceMappingURL=forge.js.map
package/dist/forge.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"forge.js","names":["toPlainValues","flattenValue","isRecord","message","opts","op","unsafeKeys","key","index","value","out","segment","path","date","time","ancestors","copy","entries","child","object","seen","current","rest","next","fullPath","childTree","baseline","_removed","baselineChild","currentChild","stringKey","meta","updated","i","a","b","issues","issue","state","isEqual","access","previous","subscribeOptions","listener","currentVal","error","listeners","pending","notifying","onError","reporterError","result","part","normalize","options","initial","notifier","disposalController","disposed","dispatching","pendingWrites","validationController","revision","validatedSnapshot","publicState","operation","update","c","touched","raw","controller","signal","externalSignal","snapshot","snapshotRevision","abortable","validity","form","partial","merged","replacement","validated","handler"],"sources":["../src/errors.ts","../src/core/path.ts","../src/_field.ts","../src/_notify.ts","../src/form.ts","../src/plain.ts"],"sourcesContent":["/** Base class for all forge errors. Use `instanceof ForgeError` to catch any forge-originated error. */\nexport class ForgeError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/** Thrown when any method is called on a disposed form. */\nexport class ForgeDisposedError extends ForgeError {\n /** @param op The public method name that was called (e.g. `'set'`): included in the\n * message so a stack trace tells you *what* to guard, not just *that* it's disposed. */\n constructor(op?: string) {\n super(op ? `Cannot call ${op}() on a disposed form` : 'Cannot modify a disposed form');\n }\n}\n\n/** Thrown when form configuration or value structure is invalid. */\nexport class ForgeConfigError extends ForgeError {}\n\n/** Thrown when `submit()` is called while a submission is already in progress. */\nexport class ForgeSubmitError extends ForgeError {}\n\n/** Wraps unexpected exceptions from a form validator without converting validation failures into exceptions. */\nexport class ForgeValidationError extends ForgeError {}\n","import { isPlainObject as isRecord } from '@vielzeug/arsenal';\n\nimport { ForgeConfigError } from '../errors.js';\nimport type { ValidationIssue } from '../types.js';\n\nconst unsafeKeys = new Set(['__proto__', 'constructor', 'prototype']);\n\nexport { isRecord };\n\nexport function isUnsafeKey(key: string): boolean {\n return !key || unsafeKeys.has(key);\n}\n\ntype RecordValue = Record<string, unknown>;\ntype TreeValue = RecordValue | readonly unknown[];\ntype MetaTree = true | MetaNode;\ninterface MetaNode {\n readonly [key: string]: MetaTree;\n}\nexport type MetaRoot = MetaNode;\n\nexport function assertSafeKey(key: string): void {\n if (isUnsafeKey(key)) throw new ForgeConfigError(`Invalid field key '${key}'.`);\n}\n\nexport function assertArrayIndex(index: number): void {\n if (!Number.isSafeInteger(index) || index < 0) throw new ForgeConfigError(`Invalid array index ${index}.`);\n}\n\nfunction tag(value: object): string {\n return Object.prototype.toString.call(value);\n}\n\nfunction isBlobLeaf(value: object): boolean {\n return tag(value) === '[object Blob]' || tag(value) === '[object File]';\n}\n\n/** Formats a value path as `list[0].name` for error messages. */\nexport function formatValuePath(path: readonly (string | number)[]): string {\n let out = '';\n for (const segment of path) {\n if (typeof segment === 'number') out += `[${segment}]`;\n else out += out ? `.${segment}` : segment;\n }\n return out;\n}\n\nfunction pathSuffix(path: readonly (string | number)[]): string {\n return path.length > 0 ? ` at '${formatValuePath(path)}'` : '';\n}\n\nfunction invalidValue(path: readonly (string | number)[]): never {\n throw new ForgeConfigError(\n `Form values must contain only finite JSON primitives, plain objects, arrays, Date, File, or Blob${pathSuffix(path)}.`,\n );\n}\n\nexport function immutable<T>(value: T, ancestors = new Set<object>(), path: (string | number)[] = []): T {\n if (value === null || value === undefined || typeof value === 'string' || typeof value === 'boolean') return value;\n if (typeof value === 'number') return Number.isFinite(value) ? value : invalidValue(path);\n if (typeof value !== 'object') return invalidValue(path);\n\n const date = value as unknown as Date;\n if (tag(value) === '[object Date]' && typeof date.getTime === 'function') {\n const time = date.getTime();\n if (!Number.isFinite(time)) return invalidValue(path);\n return Object.freeze(new Date(time)) as T;\n }\n\n if (isBlobLeaf(value)) return value;\n if (ancestors.has(value))\n throw new ForgeConfigError(`Form values must not contain circular references${pathSuffix(path)}.`);\n\n ancestors.add(value);\n try {\n if (Array.isArray(value)) {\n const copy: unknown[] = [];\n for (let index = 0; index < value.length; index++) {\n path.push(index);\n try {\n if (!Object.hasOwn(value, index))\n throw new ForgeConfigError(`Form arrays must not contain empty slots${pathSuffix(path)}.`);\n copy.push(immutable(value[index], ancestors, path));\n } finally {\n path.pop();\n }\n }\n return Object.freeze(copy) as T;\n }\n\n if (!isRecord(value)) return invalidValue(path);\n const entries = Object.entries(value).map(([key, child]) => {\n assertSafeKey(key);\n path.push(key);\n try {\n return [key, immutable(child, ancestors, path)] as const;\n } finally {\n path.pop();\n }\n });\n return Object.freeze(Object.fromEntries(entries)) as T;\n } finally {\n ancestors.delete(value);\n }\n}\n\n/**\n * Deeply converts a value into the plain shape `immutable()` accepts: primitives pass\n * through, `Date` is cloned, `File`/`Blob` keep identity, arrays map, and class instances\n * flatten to their own enumerable entries. Use as the `normalize` option of `createForm`\n * to hold domain-model classes in form state.\n *\n * Lossy conversions, applied instead of the error `immutable()` would throw: `Map`, `Set`,\n * and other keyless built-ins flatten to `{}` (their entries live on the prototype); sparse\n * array slots are dropped, which shifts later indexes; `NaN`, `Infinity`, functions, and\n * circular branches become `undefined`. Normalize model classes, not arbitrary graphs.\n */\nexport function toPlainValues<T>(value: T, seen = new WeakSet<object>()): T {\n if (value === null || value === undefined || typeof value === 'string' || typeof value === 'boolean') return value;\n if (typeof value === 'number') return Number.isFinite(value) ? value : (undefined as T);\n if (typeof value !== 'object') return undefined as T;\n\n const object = value as unknown as object;\n const date = value as unknown as Date;\n if (tag(object) === '[object Date]' && typeof date.getTime === 'function') {\n const time = date.getTime();\n return (Number.isFinite(time) ? new Date(time) : undefined) as T;\n }\n if (isBlobLeaf(object)) return value;\n if (seen.has(object)) return undefined as T;\n\n seen.add(object);\n try {\n if (Array.isArray(value)) {\n const copy: unknown[] = [];\n for (let index = 0; index < value.length; index++) {\n if (Object.hasOwn(value, index)) copy.push(toPlainValues(value[index], seen));\n }\n return copy as T;\n }\n\n // Plain objects and class instances both flatten to own enumerable entries;\n // unsafe keys are dropped so the result always passes immutable()'s key checks.\n const copy: RecordValue = {};\n for (const [key, child] of Object.entries(object)) {\n if (isUnsafeKey(key)) continue;\n copy[key] = toPlainValues(child, seen);\n }\n return copy as T;\n } finally {\n seen.delete(object);\n }\n}\n\nexport function readAtPath<T>(value: unknown, path: readonly (string | number)[]): T | undefined {\n let current: unknown = value;\n\n for (const key of path) {\n if (typeof key === 'number') {\n if (\n !Array.isArray(current) ||\n !Number.isSafeInteger(key) ||\n key < 0 ||\n key >= current.length ||\n !Object.hasOwn(current, key)\n )\n return undefined;\n\n current = current[key];\n } else {\n if (!isRecord(current) || !Object.hasOwn(current, key)) return undefined;\n\n current = current[key];\n }\n }\n\n return current as T | undefined;\n}\n\nexport function hasAtPath(value: unknown, path: readonly (string | number)[]): boolean {\n let current: unknown = value;\n\n for (const key of path) {\n if (typeof key === 'number') {\n if (\n !Array.isArray(current) ||\n !Number.isSafeInteger(key) ||\n key < 0 ||\n key >= current.length ||\n !Object.hasOwn(current, key)\n )\n return false;\n\n current = current[key];\n } else {\n if (!isRecord(current) || !Object.hasOwn(current, key)) return false;\n\n current = current[key];\n }\n }\n\n return true;\n}\n\nexport function writeAtPath<T extends TreeValue>(\n value: T,\n path: readonly (string | number)[],\n next: unknown,\n fullPath: readonly (string | number)[] = [],\n): T {\n const [key, ...rest] = path;\n\n if (typeof key === 'number') {\n assertArrayIndex(key);\n if (!Array.isArray(value)) {\n throw new ForgeConfigError(`Cannot index ${key} because the current value is not an array.`);\n }\n\n if (key < 0 || key >= value.length) {\n throw new ForgeConfigError(`Index ${key} is out of range for array of length ${value.length}.`);\n }\n\n if (rest.length === 0) {\n const copy = [...value];\n\n copy[key] = immutable(next, new Set<object>(), [...fullPath, key]);\n\n return Object.freeze(copy) as T;\n }\n\n const copy = [...value];\n\n copy[key] = writeAtPath(value[key] as TreeValue, rest, next, [...fullPath, key]);\n\n return Object.freeze(copy) as T;\n }\n\n assertSafeKey(key);\n\n if (!isRecord(value)) {\n throw new ForgeConfigError(`Cannot select '${key}' because the current value is not an object.`);\n }\n\n if (rest.length === 0)\n return Object.freeze({ ...value, [key]: immutable(next, new Set<object>(), [...fullPath, key]) }) as T;\n\n const child = value[key];\n\n if (child !== undefined && !isRecord(child) && !Array.isArray(child)) {\n throw new ForgeConfigError(`Cannot select '${key}' because it is not an object or array.`);\n }\n\n const childTree = (child ?? {}) as TreeValue;\n\n return Object.freeze({ ...value, [key]: writeAtPath(childTree, rest, next, [...fullPath, key]) }) as T;\n}\n\nexport function resetAtPath<T extends TreeValue>(value: T, baseline: T, path: readonly (string | number)[]): T {\n const [key, ...rest] = path;\n\n if (typeof key === 'number') {\n assertArrayIndex(key);\n if (!Array.isArray(value)) return value;\n if (!Array.isArray(baseline) || key >= baseline.length || key >= value.length) return value;\n\n if (rest.length === 0) {\n const copy = [...value];\n\n copy[key] = baseline[key];\n\n return Object.freeze(copy) as T;\n }\n\n const copy = [...value];\n\n copy[key] = resetAtPath(value[key] as TreeValue, baseline[key] as TreeValue, rest);\n\n return Object.freeze(copy) as T;\n }\n\n if (!isRecord(value)) return value;\n\n if (rest.length === 0) {\n if (Object.hasOwn(baseline, key)) return Object.freeze({ ...value, [key]: (baseline as RecordValue)[key] }) as T;\n\n const { [key]: _removed, ...next } = value;\n\n return Object.freeze(next) as T;\n }\n\n const baselineChild = (baseline as RecordValue)[key];\n\n if (!isRecord(baselineChild) && !Array.isArray(baselineChild)) {\n if (Object.hasOwn(baseline, key)) return Object.freeze({ ...value, [key]: baselineChild }) as T;\n\n const { [key]: _removed, ...next } = value;\n\n return Object.freeze(next) as T;\n }\n\n const currentChild = value[key];\n const childTree = (isRecord(currentChild) || Array.isArray(currentChild) ? currentChild : {}) as TreeValue;\n\n return Object.freeze({ ...value, [key]: resetAtPath(childTree, baselineChild as TreeValue, rest) }) as T;\n}\n\nexport function writeMeta(meta: MetaRoot, path: readonly (string | number)[], next: boolean): MetaRoot {\n const [key, ...rest] = path;\n if (typeof key === 'number') assertArrayIndex(key);\n const stringKey = String(key);\n const copy: Record<string, MetaTree> = { ...meta };\n\n if (rest.length === 0) {\n if (next) copy[stringKey] = true;\n else delete copy[stringKey];\n } else {\n const child = isRecord(meta[stringKey]) ? (meta[stringKey] as MetaRoot) : {};\n const updated = writeMeta(child, rest, next);\n\n if (Object.keys(updated).length === 0) delete copy[stringKey];\n else copy[stringKey] = updated;\n }\n\n return Object.freeze(copy);\n}\n\nexport function readMeta(meta: MetaRoot, path: readonly (string | number)[]): boolean {\n let current: MetaTree | undefined = meta;\n\n for (const key of path) {\n if (typeof key === 'number' && (!Number.isSafeInteger(key) || key < 0)) return false;\n const stringKey = String(key);\n\n if (!isRecord(current) || !Object.hasOwn(current, stringKey)) return false;\n\n current = current[stringKey] as MetaTree;\n }\n\n return current === true;\n}\n\nexport function touchAll(value: unknown): MetaRoot {\n if (Array.isArray(value)) {\n return Object.freeze(\n Object.fromEntries(\n value.map((child, i) => [\n String(i),\n isRecord(child) || Array.isArray(child) ? touchAll(child) : (true as const),\n ]),\n ),\n );\n }\n\n if (!isRecord(value)) return {};\n\n return Object.freeze(\n Object.fromEntries(\n Object.entries(value).map(([key, child]) => [\n key,\n isRecord(child) || Array.isArray(child) ? touchAll(child) : (true as const),\n ]),\n ),\n );\n}\n\nexport function pathEquals(a: readonly (string | number)[], b: readonly (string | number)[]): boolean {\n if (a.length !== b.length) return false;\n\n for (let i = 0; i < a.length; i++) {\n if (a[i] !== b[i]) return false;\n }\n\n return true;\n}\n\n/** Derives a single field's error message from the flat issue list. */\nexport function findIssue(\n issues: readonly ValidationIssue[] | undefined,\n path: readonly (string | number)[],\n): string | undefined {\n if (!issues) return undefined;\n\n for (const issue of issues) {\n if (pathEquals(issue.path, path)) return issue.message;\n }\n\n return undefined;\n}\n","import { isEqual } from '@vielzeug/arsenal';\n\nimport {\n assertArrayIndex,\n assertSafeKey,\n findIssue,\n hasAtPath,\n isRecord,\n type MetaRoot,\n readAtPath,\n readMeta,\n} from './core/path.js';\nimport { ForgeConfigError } from './errors.js';\nimport type { Field, FieldState, SubscribeOptions, Unsubscribe, ValidationIssue } from './types.js';\n\ntype FieldSourceState = {\n baseline: unknown;\n issues: readonly ValidationIssue[] | undefined;\n touched: MetaRoot;\n value: unknown;\n};\n\nexport interface FieldAccess {\n abortValidation(): void;\n addListener(listener: (state: FieldSourceState) => void): Unsubscribe;\n ensureActive(operation: string): void;\n invokeListener(listener: () => void): void;\n readState(): FieldSourceState;\n resetValue(path: readonly (string | number)[]): void;\n setTouched(path: readonly (string | number)[], touched: boolean): void;\n setValue(path: readonly (string | number)[], next: unknown | ((previous: unknown) => unknown)): void;\n}\n\nfunction makeFieldState<V>(state: FieldSourceState, path: readonly (string | number)[]): FieldState<V> {\n const value = readAtPath<V>(state.value, path);\n const baseline = readAtPath<V>(state.baseline, path);\n\n return Object.freeze({\n dirty: !isEqual(value, baseline) || hasAtPath(state.value, path) !== hasAtPath(state.baseline, path),\n error: findIssue(state.issues, path),\n touched: readMeta(state.touched, path),\n value: value as FieldState<V>['value'],\n });\n}\n\nexport function createField<V>(path: readonly (string | number)[], access: FieldAccess): Field<V> {\n const common = {\n get dirty() {\n return makeFieldState<V>(access.readState(), path).dirty;\n },\n get error() {\n return makeFieldState<V>(access.readState(), path).error;\n },\n reset() {\n access.ensureActive('field().reset');\n access.abortValidation();\n access.resetValue(path);\n },\n set(next: V | ((previous: FieldState<V>['value']) => V)) {\n access.ensureActive('field().set');\n access.abortValidation();\n\n access.setValue(path, next as unknown | ((previous: unknown) => unknown));\n },\n get state() {\n return makeFieldState<V>(access.readState(), path);\n },\n subscribe(listener: (state: FieldState<V>) => void, subscribeOptions: SubscribeOptions = {}): Unsubscribe {\n access.ensureActive('field().subscribe');\n\n let previous = makeFieldState<V>(access.readState(), path);\n\n if (subscribeOptions.immediate) access.invokeListener(() => listener(previous));\n\n const filtered = (state: FieldSourceState) => {\n const next = makeFieldState<V>(state, path);\n\n if (\n next.value === previous.value &&\n next.error === previous.error &&\n next.touched === previous.touched &&\n next.dirty === previous.dirty\n ) {\n return;\n }\n\n previous = next;\n listener(next);\n };\n\n return access.addListener(filtered);\n },\n touch() {\n access.ensureActive('field().touch');\n access.setTouched(path, true);\n },\n get touched() {\n return makeFieldState<V>(access.readState(), path).touched;\n },\n get value() {\n return makeFieldState<V>(access.readState(), path).value;\n },\n };\n\n return Object.assign(common, {\n field(key: string | number): Field<unknown> {\n if (typeof key === 'number') {\n assertArrayIndex(key);\n const currentVal = readAtPath(access.readState().value, path);\n\n if (currentVal !== undefined && !Array.isArray(currentVal)) {\n throw new ForgeConfigError(`Cannot index ${key} because the current field value is not an array.`);\n }\n\n return createField([...path, key], access);\n }\n\n assertSafeKey(key);\n\n const currentVal = readAtPath(access.readState().value, path);\n\n if (currentVal !== undefined && !isRecord(currentVal)) {\n throw new ForgeConfigError(`Cannot select '${key}' because the current field value is not an object.`);\n }\n\n return createField([...path, key], access);\n },\n }) as unknown as Field<V>;\n}\n","import type { Unsubscribe } from './types.js';\n\nexport interface Notifier<T> {\n add(listener: (value: T) => void): Unsubscribe;\n call(listener: () => void): void;\n clear(): void;\n notify(value: T): void;\n}\n\nfunction rethrowAsync(error: unknown): void {\n queueMicrotask(() => {\n throw error;\n });\n}\n\nexport function createNotifier<T>(onError?: (error: unknown) => void): Notifier<T> {\n const listeners = new Set<(value: T) => void>();\n const pending: T[] = [];\n let notifying = false;\n\n function reportError(error: unknown): void {\n if (!onError) {\n rethrowAsync(error);\n return;\n }\n\n try {\n onError(error);\n } catch (reporterError) {\n rethrowAsync(reporterError);\n }\n }\n\n const call = (listener: () => void): void => {\n try {\n listener();\n } catch (error) {\n reportError(error);\n }\n };\n\n return {\n add(listener) {\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n call,\n clear() {\n listeners.clear();\n pending.length = 0;\n },\n notify(value) {\n pending.push(value);\n if (notifying) return;\n\n notifying = true;\n try {\n while (pending.length > 0) {\n const next = pending.shift()!;\n for (const listener of [...listeners]) {\n if (listeners.has(listener)) call(() => listener(next));\n }\n }\n } finally {\n notifying = false;\n }\n },\n };\n}\n","import { abortable } from '@vielzeug/arsenal';\n\nimport { createField, type FieldAccess } from './_field.js';\nimport { createNotifier } from './_notify.js';\nimport {\n assertSafeKey,\n immutable,\n isRecord,\n type MetaRoot,\n readAtPath,\n resetAtPath,\n touchAll,\n writeAtPath,\n writeMeta,\n} from './core/path.js';\nimport { ForgeConfigError, ForgeDisposedError, ForgeSubmitError, ForgeValidationError } from './errors.js';\nimport type {\n Form,\n FormOptions,\n FormState,\n FormValidator,\n MaybePromise,\n SubmitResult,\n Unsubscribe,\n ValidationIssue,\n ValidationResult,\n} from './types.js';\n\ntype Validity = 'invalid' | 'unknown' | 'valid';\n\ntype InternalState<TValues extends Record<string, unknown>> = Readonly<{\n baseline: TValues;\n issues: readonly ValidationIssue[] | undefined;\n isSubmitting: boolean;\n isValidating: boolean;\n submitCount: number;\n touched: MetaRoot;\n validity: Validity;\n value: TValues;\n}>;\n\nfunction normalizeIssues(result: readonly ValidationIssue[] | undefined): readonly ValidationIssue[] | undefined {\n if (result === undefined || result.length === 0) return undefined;\n\n return Object.freeze(\n result.map((issue) => {\n if (!issue || typeof issue.message !== 'string' || !Array.isArray(issue.path)) {\n throw new ForgeConfigError('Validators must return issues with a string message and array path.');\n }\n\n const path = issue.path.map((part) => {\n if (typeof part === 'number') {\n if (!Number.isSafeInteger(part) || part < 0) throw new ForgeConfigError(`Invalid issue path index ${part}.`);\n return part;\n }\n if (typeof part === 'string') {\n assertSafeKey(part);\n return part;\n }\n throw new ForgeConfigError('Issue paths may contain only string keys and non-negative integer indexes.');\n });\n\n return Object.freeze({ message: issue.message, path: Object.freeze(path) });\n }),\n );\n}\n\nfunction makeFormState<TValues extends Record<string, unknown>>(current: InternalState<TValues>): FormState {\n return Object.freeze({\n formError: current.issues?.find((issue) => issue.path.length === 0)?.message,\n hasErrors: (current.issues?.length ?? 0) > 0,\n issues: current.issues,\n submitCount: current.submitCount,\n submitting: current.isSubmitting,\n touched: Object.keys(current.touched).length > 0,\n validating: current.isValidating,\n validity: current.validity,\n });\n}\n\n/** One immutable value tree and one explicit full-form validator keep form behavior locally understandable. */\nexport function createForm<TValues extends Record<string, unknown>>(options: FormOptions<TValues>): Form<TValues> {\n const normalize = options.normalize;\n const prepare = (value: unknown): unknown => (normalize ? normalize(value) : value);\n const initial = immutable(prepare(options.initialValues) as TValues);\n const notifier = createNotifier<InternalState<TValues>>(options.onSubscriberError);\n const disposalController = new AbortController();\n let disposed = false;\n let dispatching = false;\n const pendingWrites: Array<(current: InternalState<TValues>) => InternalState<TValues>> = [];\n let validationController: AbortController | undefined;\n let revision = 0;\n let validatedSnapshot: { revision: number; value: TValues } | undefined;\n let current: InternalState<TValues> = {\n baseline: initial,\n isSubmitting: false,\n issues: undefined,\n isValidating: false,\n submitCount: 0,\n touched: {},\n validity: 'unknown',\n value: initial,\n };\n let publicState = makeFormState(current);\n\n function ensureActive(operation: string): void {\n if (disposed) throw new ForgeDisposedError(operation);\n }\n\n function write(update: (current: InternalState<TValues>) => InternalState<TValues>): void {\n pendingWrites.push(update);\n if (dispatching) return;\n\n dispatching = true;\n try {\n while (pendingWrites.length > 0) {\n current = Object.freeze(pendingWrites.shift()!(current));\n publicState = makeFormState(current);\n notifier.notify(current);\n }\n } finally {\n dispatching = false;\n }\n }\n\n function abortValidation(): void {\n validationController?.abort();\n }\n\n const access: FieldAccess = {\n abortValidation,\n addListener: notifier.add,\n ensureActive,\n invokeListener: notifier.call,\n readState: () => current,\n resetValue(path) {\n write((c) => {\n revision++;\n validatedSnapshot = undefined;\n return {\n ...c,\n touched: writeMeta(c.touched, path, false),\n validity: 'unknown',\n value: resetAtPath(c.value, c.baseline, path),\n };\n });\n },\n setTouched(path, touched) {\n write((c) => ({ ...c, touched: writeMeta(c.touched, path, touched) }));\n },\n setValue(path, next) {\n write((c) => {\n const previous = readAtPath(c.value, path);\n const raw = typeof next === 'function' ? next(previous) : next;\n const value = prepare(raw);\n revision++;\n validatedSnapshot = undefined;\n return { ...c, validity: 'unknown', value: writeAtPath(c.value, path, value) };\n });\n },\n };\n\n async function validate(externalSignal?: AbortSignal): Promise<ValidationResult> {\n ensureActive('validate');\n abortValidation();\n\n const controller = new AbortController();\n const signal = externalSignal ? AbortSignal.any([controller.signal, externalSignal]) : controller.signal;\n if (signal.aborted) return Object.freeze({ status: 'aborted' });\n\n const snapshot = current.value;\n const snapshotRevision = revision;\n validationController = controller;\n validatedSnapshot = undefined;\n write((c) => ({ ...c, isValidating: true }));\n\n try {\n if (signal.aborted || revision !== snapshotRevision) return Object.freeze({ status: 'aborted' });\n const result: readonly ValidationIssue[] | undefined = options.validate\n ? await abortable(\n Promise.resolve(options.validate(snapshot as Parameters<FormValidator<TValues>>[0], signal)),\n signal,\n )\n : undefined;\n\n if (signal.aborted || validationController !== controller || revision !== snapshotRevision) {\n return Object.freeze({ status: 'aborted' });\n }\n\n const issues = normalizeIssues(result);\n const validity: Validity = issues === undefined ? 'valid' : 'invalid';\n validationController = undefined;\n write((c) => ({ ...c, issues, isValidating: false, validity }));\n\n if (signal.aborted || revision !== snapshotRevision || validationController !== undefined) {\n return Object.freeze({ status: 'aborted' });\n }\n\n validatedSnapshot = validity === 'valid' ? { revision, value: snapshot } : undefined;\n return validity === 'valid'\n ? Object.freeze({ status: 'valid' })\n : Object.freeze({ issues: issues as readonly ValidationIssue[], status: 'invalid' });\n } catch (error) {\n if (signal.aborted || validationController !== controller) return Object.freeze({ status: 'aborted' });\n throw new ForgeValidationError('Form validation failed.', { cause: error });\n } finally {\n if (validationController === controller) {\n validationController = undefined;\n if (!disposed) write((c) => ({ ...c, isValidating: false }));\n }\n }\n }\n\n const form: Form<TValues> = {\n get disposalSignal() {\n return disposalController.signal;\n },\n dispose() {\n if (disposed) return;\n\n disposed = true;\n pendingWrites.length = 0;\n validatedSnapshot = undefined;\n disposalController.abort();\n abortValidation();\n notifier.clear();\n },\n get disposed() {\n return disposed;\n },\n field(key) {\n assertSafeKey(key);\n if (!isRecord(current.value)) throw new ForgeConfigError('Form value must be an object.');\n\n return createField<TValues[typeof key]>([key], access);\n },\n patch(next) {\n ensureActive('patch');\n abortValidation();\n\n write((c) => {\n const partial =\n typeof next === 'function'\n ? (next as (previous: Form<TValues>['value']) => Partial<TValues>)(c.value as Form<TValues>['value'])\n : next;\n const merged = { ...c.value, ...partial };\n revision++;\n validatedSnapshot = undefined;\n return { ...c, issues: undefined, validity: 'unknown', value: immutable(prepare(merged) as TValues) };\n });\n },\n reset(next) {\n ensureActive('reset');\n abortValidation();\n\n const replacement = next === undefined ? undefined : immutable(prepare(next) as TValues);\n write((c) => {\n const baseline = replacement ?? c.baseline;\n revision++;\n validatedSnapshot = undefined;\n return { ...c, baseline, issues: undefined, touched: {}, validity: 'unknown', value: baseline };\n });\n },\n set(next) {\n ensureActive('set');\n abortValidation();\n\n write((c) => {\n const value =\n typeof next === 'function'\n ? (next as (previous: Form<TValues>['value']) => TValues)(c.value as Form<TValues>['value'])\n : next;\n revision++;\n validatedSnapshot = undefined;\n return { ...c, issues: undefined, validity: 'unknown', value: immutable(prepare(value) as TValues) };\n });\n },\n get state() {\n return publicState;\n },\n async submit<TResult = void>(\n handler: (values: Form<TValues>['value'], signal: AbortSignal) => MaybePromise<TResult>,\n externalSignal?: AbortSignal,\n ): Promise<SubmitResult<TResult>> {\n ensureActive('submit');\n\n if (current.isSubmitting) {\n throw new ForgeSubmitError('submit() called while a submission is already in progress');\n }\n\n const signal = externalSignal\n ? AbortSignal.any([disposalController.signal, externalSignal])\n : disposalController.signal;\n\n write((c) => ({\n ...c,\n isSubmitting: true,\n submitCount: c.submitCount + 1,\n touched: touchAll(c.value),\n }));\n\n try {\n const result = await validate(externalSignal);\n\n if (result.status === 'aborted') return Object.freeze({ status: 'aborted' });\n\n if (result.status === 'invalid') {\n return Object.freeze({ issues: result.issues, status: 'invalid' });\n }\n\n const validated = validatedSnapshot;\n if (!validated || validated.revision !== revision || signal.aborted) {\n return Object.freeze({ status: 'aborted' });\n }\n\n try {\n return Object.freeze({\n status: 'ok',\n value: await handler(validated.value as Form<TValues>['value'], signal),\n });\n } catch (error) {\n if (signal.aborted) return Object.freeze({ status: 'aborted' });\n\n throw error;\n }\n } finally {\n if (!disposed) write((c) => ({ ...c, isSubmitting: false }));\n }\n },\n subscribe(listener, subscribeOptions = {}): Unsubscribe {\n ensureActive('subscribe');\n\n if (subscribeOptions.immediate) notifier.call(() => listener(publicState));\n\n return notifier.add(() => listener(publicState));\n },\n [Symbol.dispose]() {\n form.dispose();\n },\n validate,\n get value() {\n return current.value as Form<TValues>['value'];\n },\n };\n\n return form;\n}\n","import { toPlainValues as flattenValue } from './core/path.js';\n\n/**\n * Deeply converts a value into the plain shape Forge accepts: primitives pass through,\n * `Date` is cloned, `File`/`Blob` keep identity, arrays map, and class instances flatten to\n * their own enumerable entries. Unsafe keys (`__proto__`, `constructor`, `prototype`) are\n * dropped, so the output always passes value validation. Use as the `normalize` option of\n * `createForm` to hold domain-model classes in form state.\n *\n * Lossy conversions, applied instead of the error an un-normalized write would throw:\n * `Map`, `Set`, and other keyless built-ins flatten to `{}` (their entries live on the\n * prototype); sparse array slots are dropped, which shifts later indexes; `NaN`, `Infinity`,\n * functions, and circular branches become `undefined`. Normalize model classes, not\n * arbitrary graphs. The type parameter is a convenience cast: the flattened shape differs\n * from the input type whenever class instances are present.\n *\n * This is a public wrapper so the emitted declaration does not name `@vielzeug/arsenal`\n * symbols: exporting the `core/path.ts` original directly pulls arsenal's extensionless\n * re-exports into the NodeNext declaration graph that `test:types` compiles. Keep the\n * implementation in `core/path.ts` beside the `immutable()` leaf rules it mirrors.\n */\nexport function toPlainValues<T>(value: T): T {\n return flattenValue(value);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"forge.js","names":["toPlainValues","flattenValue","isRecord","message","opts","op","unsafeKeys","key","index","value","out","segment","path","date","time","ancestors","copy","entries","child","object","seen","current","rest","next","fullPath","childTree","baseline","_removed","baselineChild","currentChild","stringKey","meta","updated","i","a","b","issues","issue","state","isEqual","access","previous","subscribeOptions","listener","currentVal","error","listeners","pending","notifying","onError","reporterError","result","part","normalize","options","initial","notifier","disposalController","disposed","dispatching","pendingWrites","validationController","revision","validatedSnapshot","publicState","operation","update","c","touched","raw","controller","signal","externalSignal","snapshot","snapshotRevision","abortable","validity","form","partial","merged","replacement","validated","handler"],"sources":["../src/errors.ts","../src/core/path.ts","../src/_field.ts","../src/_notify.ts","../src/form.ts","../src/plain.ts"],"sourcesContent":["/** Base class for all forge errors. Use `instanceof ForgeError` to catch any forge-originated error. */\nexport class ForgeError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/** Thrown when any method is called on a disposed form. */\nexport class ForgeDisposedError extends ForgeError {\n /** @param op The public method name that was called (e.g. `'set'`): included in the\n * message so a stack trace tells you *what* to guard, not just *that* it's disposed. */\n constructor(op?: string) {\n super(op ? `Cannot call ${op}() on a disposed form` : 'Cannot modify a disposed form');\n }\n}\n\n/** Thrown when form configuration or value structure is invalid. */\nexport class ForgeConfigError extends ForgeError {}\n\n/** Thrown when `submit()` is called while a submission is already in progress. */\nexport class ForgeSubmitError extends ForgeError {}\n\n/** Wraps unexpected exceptions from a form validator without converting validation failures into exceptions. */\nexport class ForgeValidationError extends ForgeError {}\n","import { isPlainObject as isRecord } from '@vielzeug/arsenal';\n\nimport { ForgeConfigError } from '../errors.js';\nimport type { ValidationIssue } from '../types.js';\n\nconst unsafeKeys = new Set(['__proto__', 'constructor', 'prototype']);\n\nexport { isRecord };\n\nexport function isUnsafeKey(key: string): boolean {\n return !key || unsafeKeys.has(key);\n}\n\ntype RecordValue = Record<string, unknown>;\ntype TreeValue = RecordValue | readonly unknown[];\ntype MetaTree = true | MetaNode;\ninterface MetaNode {\n readonly [key: string]: MetaTree;\n}\nexport type MetaRoot = MetaNode;\n\nexport function assertSafeKey(key: string): void {\n if (isUnsafeKey(key)) throw new ForgeConfigError(`Invalid field key '${key}'.`);\n}\n\nexport function assertArrayIndex(index: number): void {\n if (!Number.isSafeInteger(index) || index < 0) throw new ForgeConfigError(`Invalid array index ${index}.`);\n}\n\nfunction tag(value: object): string {\n return Object.prototype.toString.call(value);\n}\n\nfunction isBlobLeaf(value: object): boolean {\n return tag(value) === '[object Blob]' || tag(value) === '[object File]';\n}\n\n/** Formats a value path as `list[0].name` for error messages. */\nexport function formatValuePath(path: readonly (string | number)[]): string {\n let out = '';\n for (const segment of path) {\n if (typeof segment === 'number') out += `[${segment}]`;\n else out += out ? `.${segment}` : segment;\n }\n return out;\n}\n\nfunction pathSuffix(path: readonly (string | number)[]): string {\n return path.length > 0 ? ` at '${formatValuePath(path)}'` : '';\n}\n\nfunction invalidValue(path: readonly (string | number)[]): never {\n throw new ForgeConfigError(\n `Form values must contain only finite JSON primitives, plain objects, arrays, Date, File, or Blob${pathSuffix(path)}.`,\n );\n}\n\nexport function immutable<T>(value: T, ancestors = new Set<object>(), path: (string | number)[] = []): T {\n if (value === null || value === undefined || typeof value === 'string' || typeof value === 'boolean') return value;\n if (typeof value === 'number') return Number.isFinite(value) ? value : invalidValue(path);\n if (typeof value !== 'object') return invalidValue(path);\n\n const date = value as unknown as Date;\n if (tag(value) === '[object Date]' && typeof date.getTime === 'function') {\n const time = date.getTime();\n if (!Number.isFinite(time)) return invalidValue(path);\n return Object.freeze(new Date(time)) as T;\n }\n\n if (isBlobLeaf(value)) return value;\n if (ancestors.has(value))\n throw new ForgeConfigError(`Form values must not contain circular references${pathSuffix(path)}.`);\n\n ancestors.add(value);\n try {\n if (Array.isArray(value)) {\n const copy: unknown[] = [];\n for (let index = 0; index < value.length; index++) {\n path.push(index);\n try {\n if (!Object.hasOwn(value, index))\n throw new ForgeConfigError(`Form arrays must not contain empty slots${pathSuffix(path)}.`);\n copy.push(immutable(value[index], ancestors, path));\n } finally {\n path.pop();\n }\n }\n return Object.freeze(copy) as T;\n }\n\n if (!isRecord(value)) return invalidValue(path);\n const entries = Object.entries(value).map(([key, child]) => {\n assertSafeKey(key);\n path.push(key);\n try {\n return [key, immutable(child, ancestors, path)] as const;\n } finally {\n path.pop();\n }\n });\n return Object.freeze(Object.fromEntries(entries)) as T;\n } finally {\n ancestors.delete(value);\n }\n}\n\n/**\n * Deeply converts a value into the plain shape `immutable()` accepts: primitives pass\n * through, `Date` is cloned, `File`/`Blob` keep identity, arrays map, and class instances\n * flatten to their own enumerable entries. Use as the `normalize` option of `createForm`\n * to hold domain-model classes in form state.\n *\n * Lossy conversions, applied instead of the error `immutable()` would throw: `Map`, `Set`,\n * and other keyless built-ins flatten to `{}` (their entries live on the prototype); sparse\n * array slots are dropped, which shifts later indexes; `NaN`, `Infinity`, functions, and\n * circular branches become `undefined`. Normalize model classes, not arbitrary graphs.\n */\nexport function toPlainValues<T>(value: T, seen = new WeakSet<object>()): T {\n if (value === null || value === undefined || typeof value === 'string' || typeof value === 'boolean') return value;\n if (typeof value === 'number') return Number.isFinite(value) ? value : (undefined as T);\n if (typeof value !== 'object') return undefined as T;\n\n const object = value as unknown as object;\n const date = value as unknown as Date;\n if (tag(object) === '[object Date]' && typeof date.getTime === 'function') {\n const time = date.getTime();\n return (Number.isFinite(time) ? new Date(time) : undefined) as T;\n }\n if (isBlobLeaf(object)) return value;\n if (seen.has(object)) return undefined as T;\n\n seen.add(object);\n try {\n if (Array.isArray(value)) {\n const copy: unknown[] = [];\n for (let index = 0; index < value.length; index++) {\n if (Object.hasOwn(value, index)) copy.push(toPlainValues(value[index], seen));\n }\n return copy as T;\n }\n\n // Plain objects and class instances both flatten to own enumerable entries;\n // unsafe keys are dropped so the result always passes immutable()'s key checks.\n const copy: RecordValue = {};\n for (const [key, child] of Object.entries(object)) {\n if (isUnsafeKey(key)) continue;\n copy[key] = toPlainValues(child, seen);\n }\n return copy as T;\n } finally {\n seen.delete(object);\n }\n}\n\nexport function readAtPath<T>(value: unknown, path: readonly (string | number)[]): T | undefined {\n let current: unknown = value;\n\n for (const key of path) {\n if (typeof key === 'number') {\n if (\n !Array.isArray(current) ||\n !Number.isSafeInteger(key) ||\n key < 0 ||\n key >= current.length ||\n !Object.hasOwn(current, key)\n )\n return undefined;\n\n current = current[key];\n } else {\n if (!isRecord(current) || !Object.hasOwn(current, key)) return undefined;\n\n current = current[key];\n }\n }\n\n return current as T | undefined;\n}\n\nexport function hasAtPath(value: unknown, path: readonly (string | number)[]): boolean {\n let current: unknown = value;\n\n for (const key of path) {\n if (typeof key === 'number') {\n if (\n !Array.isArray(current) ||\n !Number.isSafeInteger(key) ||\n key < 0 ||\n key >= current.length ||\n !Object.hasOwn(current, key)\n )\n return false;\n\n current = current[key];\n } else {\n if (!isRecord(current) || !Object.hasOwn(current, key)) return false;\n\n current = current[key];\n }\n }\n\n return true;\n}\n\nexport function writeAtPath<T extends TreeValue>(\n value: T,\n path: readonly (string | number)[],\n next: unknown,\n fullPath: readonly (string | number)[] = [],\n): T {\n const [key, ...rest] = path;\n\n if (typeof key === 'number') {\n assertArrayIndex(key);\n if (!Array.isArray(value)) {\n throw new ForgeConfigError(`Cannot index ${key} because the current value is not an array.`);\n }\n\n if (key < 0 || key >= value.length) {\n throw new ForgeConfigError(`Index ${key} is out of range for array of length ${value.length}.`);\n }\n\n if (rest.length === 0) {\n const copy = [...value];\n\n copy[key] = immutable(next, new Set<object>(), [...fullPath, key]);\n\n return Object.freeze(copy) as T;\n }\n\n const copy = [...value];\n\n copy[key] = writeAtPath(value[key] as TreeValue, rest, next, [...fullPath, key]);\n\n return Object.freeze(copy) as T;\n }\n\n assertSafeKey(key);\n\n if (!isRecord(value)) {\n throw new ForgeConfigError(`Cannot select '${key}' because the current value is not an object.`);\n }\n\n if (rest.length === 0)\n return Object.freeze({ ...value, [key]: immutable(next, new Set<object>(), [...fullPath, key]) }) as T;\n\n const child = value[key];\n\n if (child !== undefined && !isRecord(child) && !Array.isArray(child)) {\n throw new ForgeConfigError(`Cannot select '${key}' because it is not an object or array.`);\n }\n\n const childTree = (child ?? {}) as TreeValue;\n\n return Object.freeze({ ...value, [key]: writeAtPath(childTree, rest, next, [...fullPath, key]) }) as T;\n}\n\nexport function resetAtPath<T extends TreeValue>(value: T, baseline: T, path: readonly (string | number)[]): T {\n const [key, ...rest] = path;\n\n if (typeof key === 'number') {\n assertArrayIndex(key);\n if (!Array.isArray(value)) return value;\n if (!Array.isArray(baseline) || key >= baseline.length || key >= value.length) return value;\n\n if (rest.length === 0) {\n const copy = [...value];\n\n copy[key] = baseline[key];\n\n return Object.freeze(copy) as T;\n }\n\n const copy = [...value];\n\n copy[key] = resetAtPath(value[key] as TreeValue, baseline[key] as TreeValue, rest);\n\n return Object.freeze(copy) as T;\n }\n\n if (!isRecord(value)) return value;\n\n if (rest.length === 0) {\n if (Object.hasOwn(baseline, key)) return Object.freeze({ ...value, [key]: (baseline as RecordValue)[key] }) as T;\n\n const { [key]: _removed, ...next } = value;\n\n return Object.freeze(next) as T;\n }\n\n const baselineChild = (baseline as RecordValue)[key];\n\n if (!isRecord(baselineChild) && !Array.isArray(baselineChild)) {\n if (Object.hasOwn(baseline, key)) return Object.freeze({ ...value, [key]: baselineChild }) as T;\n\n const { [key]: _removed, ...next } = value;\n\n return Object.freeze(next) as T;\n }\n\n const currentChild = value[key];\n const childTree = (isRecord(currentChild) || Array.isArray(currentChild) ? currentChild : {}) as TreeValue;\n\n return Object.freeze({ ...value, [key]: resetAtPath(childTree, baselineChild as TreeValue, rest) }) as T;\n}\n\nexport function writeMeta(meta: MetaRoot, path: readonly (string | number)[], next: boolean): MetaRoot {\n const [key, ...rest] = path;\n if (typeof key === 'number') assertArrayIndex(key);\n const stringKey = String(key);\n const copy: Record<string, MetaTree> = { ...meta };\n\n if (rest.length === 0) {\n if (next) copy[stringKey] = true;\n else delete copy[stringKey];\n } else {\n const child = isRecord(meta[stringKey]) ? (meta[stringKey] as MetaRoot) : {};\n const updated = writeMeta(child, rest, next);\n\n if (Object.keys(updated).length === 0) delete copy[stringKey];\n else copy[stringKey] = updated;\n }\n\n return Object.freeze(copy);\n}\n\nexport function readMeta(meta: MetaRoot, path: readonly (string | number)[]): boolean {\n let current: MetaTree | undefined = meta;\n\n for (const key of path) {\n if (typeof key === 'number' && (!Number.isSafeInteger(key) || key < 0)) return false;\n const stringKey = String(key);\n\n if (!isRecord(current) || !Object.hasOwn(current, stringKey)) return false;\n\n current = current[stringKey] as MetaTree;\n }\n\n return current === true;\n}\n\nexport function touchAll(value: unknown): MetaRoot {\n if (Array.isArray(value)) {\n return Object.freeze(\n Object.fromEntries(\n value.map((child, i) => [\n String(i),\n isRecord(child) || Array.isArray(child) ? touchAll(child) : (true as const),\n ]),\n ),\n );\n }\n\n if (!isRecord(value)) return {};\n\n return Object.freeze(\n Object.fromEntries(\n Object.entries(value).map(([key, child]) => [\n key,\n isRecord(child) || Array.isArray(child) ? touchAll(child) : (true as const),\n ]),\n ),\n );\n}\n\nexport function pathEquals(a: readonly (string | number)[], b: readonly (string | number)[]): boolean {\n if (a.length !== b.length) return false;\n\n for (let i = 0; i < a.length; i++) {\n if (a[i] !== b[i]) return false;\n }\n\n return true;\n}\n\n/** Derives a single field's error message from the flat issue list. */\nexport function findIssue(\n issues: readonly ValidationIssue[] | undefined,\n path: readonly (string | number)[],\n): string | undefined {\n if (!issues) return undefined;\n\n for (const issue of issues) {\n if (pathEquals(issue.path, path)) return issue.message;\n }\n\n return undefined;\n}\n","import { isEqual } from '@vielzeug/arsenal';\n\nimport {\n assertArrayIndex,\n assertSafeKey,\n findIssue,\n hasAtPath,\n isRecord,\n type MetaRoot,\n readAtPath,\n readMeta,\n} from './core/path.js';\nimport { ForgeConfigError } from './errors.js';\nimport type { Field, FieldState, SubscribeOptions, Unsubscribe, ValidationIssue } from './types.js';\n\ntype FieldSourceState = {\n baseline: unknown;\n issues: readonly ValidationIssue[] | undefined;\n touched: MetaRoot;\n value: unknown;\n};\n\nexport interface FieldAccess {\n abortValidation(): void;\n addListener(listener: (state: FieldSourceState) => void): Unsubscribe;\n ensureActive(operation: string): void;\n invokeListener(listener: () => void): void;\n readState(): FieldSourceState;\n resetValue(path: readonly (string | number)[]): void;\n setTouched(path: readonly (string | number)[], touched: boolean): void;\n setValue(path: readonly (string | number)[], next: unknown | ((previous: unknown) => unknown)): void;\n}\n\nfunction makeFieldState<V>(state: FieldSourceState, path: readonly (string | number)[]): FieldState<V> {\n const value = readAtPath<V>(state.value, path);\n const baseline = readAtPath<V>(state.baseline, path);\n\n return Object.freeze({\n dirty: !isEqual(value, baseline) || hasAtPath(state.value, path) !== hasAtPath(state.baseline, path),\n error: findIssue(state.issues, path),\n touched: readMeta(state.touched, path),\n value: value as FieldState<V>['value'],\n });\n}\n\nexport function createField<V>(path: readonly (string | number)[], access: FieldAccess): Field<V> {\n const common = {\n get dirty() {\n return makeFieldState<V>(access.readState(), path).dirty;\n },\n get error() {\n return makeFieldState<V>(access.readState(), path).error;\n },\n reset() {\n access.ensureActive('field().reset');\n access.abortValidation();\n access.resetValue(path);\n },\n set(next: V | ((previous: FieldState<V>['value']) => V)) {\n access.ensureActive('field().set');\n access.abortValidation();\n\n access.setValue(path, next as unknown | ((previous: unknown) => unknown));\n },\n get state() {\n return makeFieldState<V>(access.readState(), path);\n },\n subscribe(listener: (state: FieldState<V>) => void, subscribeOptions: SubscribeOptions = {}): Unsubscribe {\n access.ensureActive('field().subscribe');\n\n let previous = makeFieldState<V>(access.readState(), path);\n\n if (subscribeOptions.immediate) access.invokeListener(() => listener(previous));\n\n const filtered = (state: FieldSourceState) => {\n const next = makeFieldState<V>(state, path);\n\n if (\n next.value === previous.value &&\n next.error === previous.error &&\n next.touched === previous.touched &&\n next.dirty === previous.dirty\n ) {\n return;\n }\n\n previous = next;\n listener(next);\n };\n\n return access.addListener(filtered);\n },\n touch() {\n access.ensureActive('field().touch');\n access.setTouched(path, true);\n },\n get touched() {\n return makeFieldState<V>(access.readState(), path).touched;\n },\n get value() {\n return makeFieldState<V>(access.readState(), path).value;\n },\n };\n\n return Object.assign(common, {\n field(key: string | number): Field<unknown> {\n if (typeof key === 'number') {\n assertArrayIndex(key);\n const currentVal = readAtPath(access.readState().value, path);\n\n if (currentVal !== undefined && !Array.isArray(currentVal)) {\n throw new ForgeConfigError(`Cannot index ${key} because the current field value is not an array.`);\n }\n\n return createField([...path, key], access);\n }\n\n assertSafeKey(key);\n\n const currentVal = readAtPath(access.readState().value, path);\n\n if (currentVal !== undefined && !isRecord(currentVal)) {\n throw new ForgeConfigError(`Cannot select '${key}' because the current field value is not an object.`);\n }\n\n return createField([...path, key], access);\n },\n }) as unknown as Field<V>;\n}\n","import type { Unsubscribe } from './types.js';\n\nexport interface Notifier<T> {\n add(listener: (value: T) => void): Unsubscribe;\n call(listener: () => void): void;\n clear(): void;\n notify(value: T): void;\n}\n\nfunction rethrowAsync(error: unknown): void {\n queueMicrotask(() => {\n throw error;\n });\n}\n\nexport function createNotifier<T>(onError?: (error: unknown) => void): Notifier<T> {\n const listeners = new Set<(value: T) => void>();\n const pending: T[] = [];\n let notifying = false;\n\n function reportError(error: unknown): void {\n if (!onError) {\n rethrowAsync(error);\n return;\n }\n\n try {\n onError(error);\n } catch (reporterError) {\n rethrowAsync(reporterError);\n }\n }\n\n const call = (listener: () => void): void => {\n try {\n listener();\n } catch (error) {\n reportError(error);\n }\n };\n\n return {\n add(listener) {\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n call,\n clear() {\n listeners.clear();\n pending.length = 0;\n },\n notify(value) {\n pending.push(value);\n if (notifying) return;\n\n notifying = true;\n try {\n while (pending.length > 0) {\n const next = pending.shift()!;\n for (const listener of [...listeners]) {\n if (listeners.has(listener)) call(() => listener(next));\n }\n }\n } finally {\n notifying = false;\n }\n },\n };\n}\n","import { abortable } from '@vielzeug/arsenal';\n\nimport { createField, type FieldAccess } from './_field.js';\nimport { createNotifier } from './_notify.js';\nimport {\n assertSafeKey,\n immutable,\n isRecord,\n type MetaRoot,\n readAtPath,\n resetAtPath,\n touchAll,\n writeAtPath,\n writeMeta,\n} from './core/path.js';\nimport { ForgeConfigError, ForgeDisposedError, ForgeSubmitError, ForgeValidationError } from './errors.js';\nimport type {\n Form,\n FormOptions,\n FormState,\n FormValidator,\n MaybePromise,\n SubmitResult,\n Unsubscribe,\n ValidationIssue,\n ValidationResult,\n} from './types.js';\n\ntype Validity = 'invalid' | 'unknown' | 'valid';\n\ntype InternalState<TValues extends Record<string, unknown>> = Readonly<{\n baseline: TValues;\n issues: readonly ValidationIssue[] | undefined;\n isSubmitting: boolean;\n isValidating: boolean;\n submitCount: number;\n touched: MetaRoot;\n validity: Validity;\n value: TValues;\n}>;\n\nfunction normalizeIssues(result: readonly ValidationIssue[] | undefined): readonly ValidationIssue[] | undefined {\n if (result === undefined || result.length === 0) return undefined;\n\n return Object.freeze(\n result.map((issue) => {\n if (!issue || typeof issue.message !== 'string' || !Array.isArray(issue.path)) {\n throw new ForgeConfigError('Validators must return issues with a string message and array path.');\n }\n\n const path = issue.path.map((part) => {\n if (typeof part === 'number') {\n if (!Number.isSafeInteger(part) || part < 0) throw new ForgeConfigError(`Invalid issue path index ${part}.`);\n return part;\n }\n if (typeof part === 'string') {\n assertSafeKey(part);\n return part;\n }\n throw new ForgeConfigError('Issue paths may contain only string keys and non-negative integer indexes.');\n });\n\n return Object.freeze({ message: issue.message, path: Object.freeze(path) });\n }),\n );\n}\n\nfunction makeFormState<TValues extends Record<string, unknown>>(current: InternalState<TValues>): FormState {\n return Object.freeze({\n formError: current.issues?.find((issue) => issue.path.length === 0)?.message,\n hasErrors: (current.issues?.length ?? 0) > 0,\n issues: current.issues,\n submitCount: current.submitCount,\n submitting: current.isSubmitting,\n touched: Object.keys(current.touched).length > 0,\n validating: current.isValidating,\n validity: current.validity,\n });\n}\n\n/** One immutable value tree and one explicit full-form validator keep form behavior locally understandable. */\nexport function createForm<TValues extends Record<string, unknown>>(options: FormOptions<TValues>): Form<TValues> {\n const normalize = options.normalize;\n const prepare = (value: unknown): unknown => (normalize ? normalize(value) : value);\n const initial = immutable(prepare(options.initialValues) as TValues);\n const notifier = createNotifier<InternalState<TValues>>(options.onSubscriberError);\n const disposalController = new AbortController();\n let disposed = false;\n let dispatching = false;\n const pendingWrites: Array<(current: InternalState<TValues>) => InternalState<TValues>> = [];\n let validationController: AbortController | undefined;\n let revision = 0;\n let validatedSnapshot: { revision: number; value: TValues } | undefined;\n let current: InternalState<TValues> = {\n baseline: initial,\n isSubmitting: false,\n issues: undefined,\n isValidating: false,\n submitCount: 0,\n touched: {},\n validity: 'unknown',\n value: initial,\n };\n let publicState = makeFormState(current);\n\n function ensureActive(operation: string): void {\n if (disposed) throw new ForgeDisposedError(operation);\n }\n\n function write(update: (current: InternalState<TValues>) => InternalState<TValues>): void {\n pendingWrites.push(update);\n if (dispatching) return;\n\n dispatching = true;\n try {\n while (pendingWrites.length > 0) {\n current = Object.freeze(pendingWrites.shift()!(current));\n publicState = makeFormState(current);\n notifier.notify(current);\n }\n } finally {\n dispatching = false;\n }\n }\n\n function abortValidation(): void {\n validationController?.abort();\n }\n\n const access: FieldAccess = {\n abortValidation,\n addListener: notifier.add,\n ensureActive,\n invokeListener: notifier.call,\n readState: () => current,\n resetValue(path) {\n write((c) => {\n revision++;\n validatedSnapshot = undefined;\n return {\n ...c,\n touched: writeMeta(c.touched, path, false),\n validity: 'unknown',\n value: resetAtPath(c.value, c.baseline, path),\n };\n });\n },\n setTouched(path, touched) {\n write((c) => ({ ...c, touched: writeMeta(c.touched, path, touched) }));\n },\n setValue(path, next) {\n write((c) => {\n const previous = readAtPath(c.value, path);\n const raw = typeof next === 'function' ? next(previous) : next;\n const value = prepare(raw);\n revision++;\n validatedSnapshot = undefined;\n return { ...c, validity: 'unknown', value: writeAtPath(c.value, path, value) };\n });\n },\n };\n\n async function validate(externalSignal?: AbortSignal): Promise<ValidationResult> {\n ensureActive('validate');\n abortValidation();\n\n const controller = new AbortController();\n const signal = externalSignal ? AbortSignal.any([controller.signal, externalSignal]) : controller.signal;\n if (signal.aborted) return Object.freeze({ status: 'aborted' });\n\n const snapshot = current.value;\n const snapshotRevision = revision;\n validationController = controller;\n validatedSnapshot = undefined;\n write((c) => ({ ...c, isValidating: true }));\n\n try {\n if (signal.aborted || revision !== snapshotRevision) return Object.freeze({ status: 'aborted' });\n const result: readonly ValidationIssue[] | undefined = options.validate\n ? await abortable(\n Promise.resolve(options.validate(snapshot as Parameters<FormValidator<TValues>>[0], signal)),\n signal,\n )\n : undefined;\n\n if (signal.aborted || validationController !== controller || revision !== snapshotRevision) {\n return Object.freeze({ status: 'aborted' });\n }\n\n const issues = normalizeIssues(result);\n const validity: Validity = issues === undefined ? 'valid' : 'invalid';\n validationController = undefined;\n write((c) => ({ ...c, issues, isValidating: false, validity }));\n\n if (signal.aborted || revision !== snapshotRevision || validationController !== undefined) {\n return Object.freeze({ status: 'aborted' });\n }\n\n validatedSnapshot = validity === 'valid' ? { revision, value: snapshot } : undefined;\n return validity === 'valid'\n ? Object.freeze({ status: 'valid' })\n : Object.freeze({ issues: issues as readonly ValidationIssue[], status: 'invalid' });\n } catch (error) {\n if (signal.aborted || validationController !== controller) return Object.freeze({ status: 'aborted' });\n throw new ForgeValidationError('Form validation failed.', { cause: error });\n } finally {\n if (validationController === controller) {\n validationController = undefined;\n if (!disposed) write((c) => ({ ...c, isValidating: false }));\n }\n }\n }\n\n const form: Form<TValues> = {\n get disposalSignal() {\n return disposalController.signal;\n },\n dispose() {\n if (disposed) return;\n\n disposed = true;\n pendingWrites.length = 0;\n validatedSnapshot = undefined;\n disposalController.abort();\n abortValidation();\n notifier.clear();\n },\n get disposed() {\n return disposed;\n },\n field(key) {\n assertSafeKey(key);\n if (!isRecord(current.value)) throw new ForgeConfigError('Form value must be an object.');\n\n return createField<TValues[typeof key]>([key], access);\n },\n patch(next) {\n ensureActive('patch');\n abortValidation();\n\n write((c) => {\n const partial =\n typeof next === 'function'\n ? (next as (previous: Form<TValues>['value']) => Partial<TValues>)(c.value as Form<TValues>['value'])\n : next;\n const merged = { ...c.value, ...partial };\n revision++;\n validatedSnapshot = undefined;\n return { ...c, issues: undefined, validity: 'unknown', value: immutable(prepare(merged) as TValues) };\n });\n },\n reset(next) {\n ensureActive('reset');\n abortValidation();\n\n const replacement = next === undefined ? undefined : immutable(prepare(next) as TValues);\n write((c) => {\n const baseline = replacement ?? c.baseline;\n revision++;\n validatedSnapshot = undefined;\n return { ...c, baseline, issues: undefined, touched: {}, validity: 'unknown', value: baseline };\n });\n },\n set(next) {\n ensureActive('set');\n abortValidation();\n\n write((c) => {\n const value =\n typeof next === 'function'\n ? (next as (previous: Form<TValues>['value']) => TValues)(c.value as Form<TValues>['value'])\n : next;\n revision++;\n validatedSnapshot = undefined;\n return { ...c, issues: undefined, validity: 'unknown', value: immutable(prepare(value) as TValues) };\n });\n },\n get state() {\n return publicState;\n },\n async submit<TResult = void>(\n handler: (values: Form<TValues>['value'], signal: AbortSignal) => MaybePromise<TResult>,\n externalSignal?: AbortSignal,\n ): Promise<SubmitResult<TResult>> {\n ensureActive('submit');\n\n if (current.isSubmitting) {\n throw new ForgeSubmitError('submit() called while a submission is already in progress');\n }\n\n const signal = externalSignal\n ? AbortSignal.any([disposalController.signal, externalSignal])\n : disposalController.signal;\n\n write((c) => ({\n ...c,\n isSubmitting: true,\n submitCount: c.submitCount + 1,\n touched: touchAll(c.value),\n }));\n\n try {\n const result = await validate(externalSignal);\n\n if (result.status === 'aborted') return Object.freeze({ status: 'aborted' });\n\n if (result.status === 'invalid') {\n return Object.freeze({ issues: result.issues, status: 'invalid' });\n }\n\n const validated = validatedSnapshot;\n if (!validated || validated.revision !== revision || signal.aborted) {\n return Object.freeze({ status: 'aborted' });\n }\n\n try {\n return Object.freeze({\n status: 'ok',\n value: await handler(validated.value as Form<TValues>['value'], signal),\n });\n } catch (error) {\n if (signal.aborted) return Object.freeze({ status: 'aborted' });\n\n throw error;\n }\n } finally {\n if (!disposed) write((c) => ({ ...c, isSubmitting: false }));\n }\n },\n subscribe(listener, subscribeOptions = {}): Unsubscribe {\n ensureActive('subscribe');\n\n if (subscribeOptions.immediate) notifier.call(() => listener(publicState));\n\n return notifier.add(() => listener(publicState));\n },\n [Symbol.dispose]() {\n form.dispose();\n },\n validate,\n get value() {\n return current.value as Form<TValues>['value'];\n },\n };\n\n return form;\n}\n","import { toPlainValues as flattenValue } from './core/path.js';\n\n/**\n * Deeply converts a value into the plain shape Forge accepts: primitives pass through,\n * `Date` is cloned, `File`/`Blob` keep identity, arrays map, and class instances flatten to\n * their own enumerable entries. Unsafe keys (`__proto__`, `constructor`, `prototype`) are\n * dropped, so the output always passes value validation. Use as the `normalize` option of\n * `createForm` to hold domain-model classes in form state.\n *\n * Lossy conversions, applied instead of the error an un-normalized write would throw:\n * `Map`, `Set`, and other keyless built-ins flatten to `{}` (their entries live on the\n * prototype); sparse array slots are dropped, which shifts later indexes; `NaN`, `Infinity`,\n * functions, and circular branches become `undefined`. Normalize model classes, not\n * arbitrary graphs. The type parameter is a convenience cast: the flattened shape differs\n * from the input type whenever class instances are present.\n *\n * This is a public wrapper so the emitted declaration does not name `@vielzeug/arsenal`\n * symbols: exporting the `core/path.ts` original directly pulls arsenal's extensionless\n * re-exports into the NodeNext declaration graph that `test:types` compiles. Keep the\n * implementation in `core/path.ts` beside the `immutable()` leaf rules it mirrors.\n */\nexport function toPlainValues<T>(value: T): T {\n return flattenValue(value);\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./errors.cjs"),t=require("./core/path.cjs");let n=require("@vielzeug/arsenal");function flatten(r,i){let a={};for(let[o,s]of Object.entries(r)){if(t.isUnsafeKey(o))continue;if(o.includes(`.`))throw new e.ForgeConfigError(`FormData keys must not contain dots: '${o}'.`);let r=i?`${i}.${o}`:o;(0,n.isPlainObject)(s)?Object.assign(a,flatten(s,r)):a[r]=s}return a}function toFormData(t){let r=new FormData,isBlob=e=>typeof Blob<`u`&&e instanceof Blob,isFile=e=>typeof File<`u`&&e instanceof File,isFileList=e=>typeof FileList<`u`&&e instanceof FileList;for(let[i,a]of Object.entries(flatten(t,``)))if(a!=null){if(isFile(a)||isBlob(a))r.append(i,a);else if(isFileList(a))for(let e=0;e<a.length;e++)r.append(i,a[e]);else if(Array.isArray(a))for(let t of a){if((0,n.isPlainObject)(t)||Array.isArray(t))throw new e.ForgeConfigError(`FormData array '${i}' must contain only scalar or binary values.`);r.append(i,isFile(t)||isBlob(t)?t:String(t))}else r.append(i,String(a))}return r}exports.toFormData=toFormData;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./core/path.cjs"),t=require("./errors.cjs");let n=require("@vielzeug/arsenal");function flatten(r,i){let a={};for(let[o,s]of Object.entries(r)){if(e.isUnsafeKey(o))continue;if(o.includes(`.`))throw new t.ForgeConfigError(`FormData keys must not contain dots: '${o}'.`);let r=i?`${i}.${o}`:o;(0,n.isPlainObject)(s)?Object.assign(a,flatten(s,r)):a[r]=s}return a}function toFormData(e){let r=new FormData,isBlob=e=>typeof Blob<`u`&&e instanceof Blob,isFile=e=>typeof File<`u`&&e instanceof File,isFileList=e=>typeof FileList<`u`&&e instanceof FileList;for(let[i,a]of Object.entries(flatten(e,``)))if(a!=null){if(isFile(a)||isBlob(a))r.append(i,a);else if(isFileList(a))for(let e=0;e<a.length;e++)r.append(i,a[e]);else if(Array.isArray(a))for(let e of a){if((0,n.isPlainObject)(e)||Array.isArray(e))throw new t.ForgeConfigError(`FormData array '${i}' must contain only scalar or binary values.`);r.append(i,isFile(e)||isBlob(e)?e:String(e))}else r.append(i,String(a))}return r}exports.toFormData=toFormData;
2
2
  //# sourceMappingURL=form-data.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"form-data.cjs","names":["isUnsafeKey","ForgeConfigError","isRecord","require_errors","require_path","_vielzeug_arsenal","result","key","value","obj","fullKey","prefix","fd","name","values","i","item"],"sources":["../src/form-data.ts"],"sourcesContent":["import { isRecord, isUnsafeKey } from './core/path.js';\nimport { ForgeConfigError } from './errors.js';\n\nfunction flatten(obj: Record<string, unknown>, prefix: string): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(obj)) {\n if (isUnsafeKey(key)) continue;\n if (key.includes('.')) throw new ForgeConfigError(`FormData keys must not contain dots: '${key}'.`);\n\n const fullKey = prefix ? `${prefix}.${key}` : key;\n\n if (isRecord(value)) {\n Object.assign(result, flatten(value, fullKey));\n } else {\n result[fullKey] = value;\n }\n }\n\n return result;\n}\n\n/**\n * Converts a form values object into a `FormData` instance.\n * Nested objects are flattened to dot-notation keys (e.g. `user.name`).\n * `File`, `Blob`, and `FileList` values are appended as-is; scalar arrays use repeated keys.\n * `null` and `undefined` values are omitted. Ambiguous dotted keys and nested array containers reject.\n */\nexport function toFormData(values: Record<string, unknown>): FormData {\n const fd = new FormData();\n\n const isBlob = (value: unknown): value is Blob => typeof Blob !== 'undefined' && value instanceof Blob;\n const isFile = (value: unknown): value is File => typeof File !== 'undefined' && value instanceof File;\n const isFileList = (value: unknown): value is FileList =>\n typeof FileList !== 'undefined' && value instanceof FileList;\n\n for (const [name, value] of Object.entries(flatten(values, ''))) {\n if (value === null || value === undefined) continue;\n\n if (isFile(value) || isBlob(value)) {\n fd.append(name, value);\n } else if (isFileList(value)) {\n for (let i = 0; i < value.length; i++) fd.append(name, value[i]);\n } else if (Array.isArray(value)) {\n for (const item of value) {\n if (isRecord(item) || Array.isArray(item)) {\n throw new ForgeConfigError(`FormData array '${name}' must contain only scalar or binary values.`);\n }\n fd.append(name, isFile(item) || isBlob(item) ? item : String(item));\n }\n } else {\n fd.append(name, String(value));\n }\n }\n\n return fd;\n}\n"],"mappings":"A,mKAGA,SAAS,QAAQ,EAA8B,EAAyC,CACtF,IAAMM,EAAkC,CAAC,EAEzC,IAAK,GAAM,CAACC,EAAKC,KAAU,OAAO,QAAQC,CAAG,EAAG,CAC9C,GAAIT,EAAAA,YAAYO,CAAG,EAAG,SACtB,GAAIA,EAAI,SAAS,GAAG,EAAG,MAAM,IAAIN,EAAAA,iBAAiB,yCAAyCM,EAAI,GAAG,EAElG,IAAMG,EAAUC,EAAS,GAAGA,EAAO,GAAGJ,IAAQA,GAE9C,EAAIL,EAAAA,cAAAA,CAASM,CAAK,EAChB,OAAO,OAAOF,EAAQ,QAAQE,EAAOE,CAAO,CAAC,EAE7C,EAAOA,GAAWF,CAEtB,CAEA,OAAOF,CACT,CAQA,SAAgB,WAAW,EAA2C,CACpE,IAAMM,EAAK,IAAI,SAET,OAAU,GAAkC,OAAO,KAAS,KAAeJ,aAAiB,KAC5F,OAAU,GAAkC,OAAO,KAAS,KAAeA,aAAiB,KAC5F,WAAc,GAClB,OAAO,SAAa,KAAeA,aAAiB,SAEtD,IAAK,GAAM,CAACK,EAAML,KAAU,OAAO,QAAQ,QAAQM,EAAQ,EAAE,CAAC,EAC5D,GAAIN,GAAU,KAEd,IAAI,OAAOA,CAAK,GAAK,OAAOA,CAAK,EAC/BI,EAAG,OAAOC,EAAML,CAAK,OAChB,GAAI,WAAWA,CAAK,EACzB,IAAK,IAAIO,EAAI,EAAGA,EAAIP,EAAM,OAAQ,IAAKI,EAAG,OAAOC,EAAML,EAAMO,EAAE,OAC1D,GAAI,MAAM,QAAQP,CAAK,EAC5B,IAAK,IAAMQ,KAAQR,EAAO,CACxB,IAAA,EAAIN,EAAAA,cAAAA,CAASc,CAAI,GAAK,MAAM,QAAQA,CAAI,EACtC,MAAM,IAAIf,EAAAA,iBAAiB,mBAAmBY,EAAK,6CAA6C,EAElGD,EAAG,OAAOC,EAAM,OAAOG,CAAI,GAAK,OAAOA,CAAI,EAAIA,EAAO,OAAOA,CAAI,CAAC,CACpE,MAEAJ,EAAG,OAAOC,EAAM,OAAOL,CAAK,CAAC,CAAA,CAIjC,OAAOI,CACT,C"}
1
+ {"version":3,"file":"form-data.cjs","names":["isUnsafeKey","ForgeConfigError","isRecord","require_path","require_errors","_vielzeug_arsenal","result","key","value","obj","fullKey","prefix","fd","name","values","i","item"],"sources":["../src/form-data.ts"],"sourcesContent":["import { isRecord, isUnsafeKey } from './core/path.js';\nimport { ForgeConfigError } from './errors.js';\n\nfunction flatten(obj: Record<string, unknown>, prefix: string): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(obj)) {\n if (isUnsafeKey(key)) continue;\n if (key.includes('.')) throw new ForgeConfigError(`FormData keys must not contain dots: '${key}'.`);\n\n const fullKey = prefix ? `${prefix}.${key}` : key;\n\n if (isRecord(value)) {\n Object.assign(result, flatten(value, fullKey));\n } else {\n result[fullKey] = value;\n }\n }\n\n return result;\n}\n\n/**\n * Converts a form values object into a `FormData` instance.\n * Nested objects are flattened to dot-notation keys (e.g. `user.name`).\n * `File`, `Blob`, and `FileList` values are appended as-is; scalar arrays use repeated keys.\n * `null` and `undefined` values are omitted. Ambiguous dotted keys and nested array containers reject.\n */\nexport function toFormData(values: Record<string, unknown>): FormData {\n const fd = new FormData();\n\n const isBlob = (value: unknown): value is Blob => typeof Blob !== 'undefined' && value instanceof Blob;\n const isFile = (value: unknown): value is File => typeof File !== 'undefined' && value instanceof File;\n const isFileList = (value: unknown): value is FileList =>\n typeof FileList !== 'undefined' && value instanceof FileList;\n\n for (const [name, value] of Object.entries(flatten(values, ''))) {\n if (value === null || value === undefined) continue;\n\n if (isFile(value) || isBlob(value)) {\n fd.append(name, value);\n } else if (isFileList(value)) {\n for (let i = 0; i < value.length; i++) fd.append(name, value[i]);\n } else if (Array.isArray(value)) {\n for (const item of value) {\n if (isRecord(item) || Array.isArray(item)) {\n throw new ForgeConfigError(`FormData array '${name}' must contain only scalar or binary values.`);\n }\n fd.append(name, isFile(item) || isBlob(item) ? item : String(item));\n }\n } else {\n fd.append(name, String(value));\n }\n }\n\n return fd;\n}\n"],"mappings":"A,mKAGA,SAAS,QAAQ,EAA8B,EAAyC,CACtF,IAAMM,EAAkC,CAAC,EAEzC,IAAK,GAAM,CAACC,EAAKC,KAAU,OAAO,QAAQC,CAAG,EAAG,CAC9C,GAAIT,EAAAA,YAAYO,CAAG,EAAG,SACtB,GAAIA,EAAI,SAAS,GAAG,EAAG,MAAM,IAAIN,EAAAA,iBAAiB,yCAAyCM,EAAI,GAAG,EAElG,IAAMG,EAAUC,EAAS,GAAGA,EAAO,GAAGJ,IAAQA,GAE9C,EAAIL,EAAAA,cAAAA,CAASM,CAAK,EAChB,OAAO,OAAOF,EAAQ,QAAQE,EAAOE,CAAO,CAAC,EAE7C,EAAOA,GAAWF,CAEtB,CAEA,OAAOF,CACT,CAQA,SAAgB,WAAW,EAA2C,CACpE,IAAMM,EAAK,IAAI,SAET,OAAU,GAAkC,OAAO,KAAS,KAAeJ,aAAiB,KAC5F,OAAU,GAAkC,OAAO,KAAS,KAAeA,aAAiB,KAC5F,WAAc,GAClB,OAAO,SAAa,KAAeA,aAAiB,SAEtD,IAAK,GAAM,CAACK,EAAML,KAAU,OAAO,QAAQ,QAAQM,EAAQ,EAAE,CAAC,EAC5D,GAAIN,GAAU,KAEd,IAAI,OAAOA,CAAK,GAAK,OAAOA,CAAK,EAC/BI,EAAG,OAAOC,EAAML,CAAK,OAChB,GAAI,WAAWA,CAAK,EACzB,IAAK,IAAIO,EAAI,EAAGA,EAAIP,EAAM,OAAQ,IAAKI,EAAG,OAAOC,EAAML,EAAMO,EAAE,OAC1D,GAAI,MAAM,QAAQP,CAAK,EAC5B,IAAK,IAAMQ,KAAQR,EAAO,CACxB,IAAA,EAAIN,EAAAA,cAAAA,CAASc,CAAI,GAAK,MAAM,QAAQA,CAAI,EACtC,MAAM,IAAIf,EAAAA,iBAAiB,mBAAmBY,EAAK,6CAA6C,EAElGD,EAAG,OAAOC,EAAM,OAAOG,CAAI,GAAK,OAAOA,CAAI,EAAIA,EAAO,OAAOA,CAAI,CAAC,CACpE,MAEAJ,EAAG,OAAOC,EAAM,OAAOL,CAAK,CAAC,CAAA,CAIjC,OAAOI,CACT,C"}
package/dist/form-data.js CHANGED
@@ -1,30 +1,29 @@
1
- import { ForgeConfigError as e } from "./errors.js";
2
- import { isRecord as t, isUnsafeKey as n } from "./core/path.js";
3
- //#region src/form-data.ts
1
+ import { isRecord as e, isUnsafeKey as t } from "./core/path.js";
2
+ import { ForgeConfigError as n } from "./errors.js";
3
+ // src/form-data.ts
4
4
  function flatten(r, i) {
5
5
  let a = {};
6
6
  for (let [o, s] of Object.entries(r)) {
7
- if (n(o)) continue;
8
- if (o.includes(".")) throw new e(`FormData keys must not contain dots: '${o}'.`);
7
+ if (t(o)) continue;
8
+ if (o.includes(".")) throw new n(`FormData keys must not contain dots: '${o}'.`);
9
9
  let r = i ? `${i}.${o}` : o;
10
- t(s) ? Object.assign(a, flatten(s, r)) : a[r] = s;
10
+ e(s) ? Object.assign(a, flatten(s, r)) : a[r] = s;
11
11
  }
12
12
  return a;
13
13
  }
14
- function toFormData(n) {
14
+ function toFormData(t) {
15
15
  let r = new FormData(), isBlob = (e) => typeof Blob < "u" && e instanceof Blob, isFile = (e) => typeof File < "u" && e instanceof File, isFileList = (e) => typeof FileList < "u" && e instanceof FileList;
16
- for (let [i, a] of Object.entries(flatten(n, ""))) if (a != null) {
16
+ for (let [i, a] of Object.entries(flatten(t, ""))) if (a != null) {
17
17
  if (isFile(a) || isBlob(a)) r.append(i, a);
18
18
  else if (isFileList(a)) for (let e = 0; e < a.length; e++) r.append(i, a[e]);
19
- else if (Array.isArray(a)) for (let n of a) {
20
- if (t(n) || Array.isArray(n)) throw new e(`FormData array '${i}' must contain only scalar or binary values.`);
21
- r.append(i, isFile(n) || isBlob(n) ? n : String(n));
19
+ else if (Array.isArray(a)) for (let t of a) {
20
+ if (e(t) || Array.isArray(t)) throw new n(`FormData array '${i}' must contain only scalar or binary values.`);
21
+ r.append(i, isFile(t) || isBlob(t) ? t : String(t));
22
22
  }
23
23
  else r.append(i, String(a));
24
24
  }
25
25
  return r;
26
26
  }
27
- //#endregion
28
27
  export { toFormData };
29
28
 
30
29
  //# sourceMappingURL=form-data.js.map
package/dist/form.cjs CHANGED
@@ -1,2 +1,2 @@
1
- const e=require("./errors.cjs"),t=require("./core/path.cjs"),n=require("./_field.cjs"),r=require("./_notify.cjs");let i=require("@vielzeug/arsenal");function normalizeIssues(n){if(n!==void 0&&n.length!==0)return Object.freeze(n.map(n=>{if(!n||typeof n.message!=`string`||!Array.isArray(n.path))throw new e.ForgeConfigError(`Validators must return issues with a string message and array path.`);let r=n.path.map(n=>{if(typeof n==`number`){if(!Number.isSafeInteger(n)||n<0)throw new e.ForgeConfigError(`Invalid issue path index ${n}.`);return n}if(typeof n==`string`)return t.assertSafeKey(n),n;throw new e.ForgeConfigError(`Issue paths may contain only string keys and non-negative integer indexes.`)});return Object.freeze({message:n.message,path:Object.freeze(r)})}))}function makeFormState(e){return Object.freeze({formError:e.issues?.find(e=>e.path.length===0)?.message,hasErrors:(e.issues?.length??0)>0,issues:e.issues,submitCount:e.submitCount,submitting:e.isSubmitting,touched:Object.keys(e.touched).length>0,validating:e.isValidating,validity:e.validity})}function createForm(a){let o=a.normalize,prepare=e=>o?o(e):e,s=t.immutable(prepare(a.initialValues)),c=r.createNotifier(a.onSubscriberError),l=new AbortController,u=!1,d=!1,f=[],p,m=0,h,g={baseline:s,isSubmitting:!1,issues:void 0,isValidating:!1,submitCount:0,touched:{},validity:`unknown`,value:s},_=makeFormState(g);function ensureActive(t){if(u)throw new e.ForgeDisposedError(t)}function write(e){if(f.push(e),!d){d=!0;try{for(;f.length>0;)g=Object.freeze(f.shift()(g)),_=makeFormState(g),c.notify(g)}finally{d=!1}}}function abortValidation(){p?.abort()}let v={abortValidation,addListener:c.add,ensureActive,invokeListener:c.call,readState:()=>g,resetValue(e){write(n=>(m++,h=void 0,{...n,touched:t.writeMeta(n.touched,e,!1),validity:`unknown`,value:t.resetAtPath(n.value,n.baseline,e)}))},setTouched(e,n){write(r=>({...r,touched:t.writeMeta(r.touched,e,n)}))},setValue(e,n){write(r=>{let i=t.readAtPath(r.value,e),a=typeof n==`function`?n(i):n,o=prepare(a);return m++,h=void 0,{...r,validity:`unknown`,value:t.writeAtPath(r.value,e,o)}})}};async function validate(t){ensureActive(`validate`),abortValidation();let n=new AbortController,r=t?AbortSignal.any([n.signal,t]):n.signal;if(r.aborted)return Object.freeze({status:`aborted`});let o=g.value,s=m;p=n,h=void 0,write(e=>({...e,isValidating:!0}));try{if(r.aborted||m!==s)return Object.freeze({status:`aborted`});let e=a.validate?await(0,i.abortable)(Promise.resolve(a.validate(o,r)),r):void 0;if(r.aborted||p!==n||m!==s)return Object.freeze({status:`aborted`});let t=normalizeIssues(e),c=t===void 0?`valid`:`invalid`;return p=void 0,write(e=>({...e,issues:t,isValidating:!1,validity:c})),r.aborted||m!==s||p!==void 0?Object.freeze({status:`aborted`}):(h=c===`valid`?{revision:m,value:o}:void 0,Object.freeze(c===`valid`?{status:`valid`}:{issues:t,status:`invalid`}))}catch(t){if(r.aborted||p!==n)return Object.freeze({status:`aborted`});throw new e.ForgeValidationError(`Form validation failed.`,{cause:t})}finally{p===n&&(p=void 0,u||write(e=>({...e,isValidating:!1})))}}let y={get disposalSignal(){return l.signal},dispose(){u||(u=!0,f.length=0,h=void 0,l.abort(),abortValidation(),c.clear())},get disposed(){return u},field(r){if(t.assertSafeKey(r),!(0,i.isPlainObject)(g.value))throw new e.ForgeConfigError(`Form value must be an object.`);return n.createField([r],v)},patch(e){ensureActive(`patch`),abortValidation(),write(n=>{let r=typeof e==`function`?e(n.value):e,i={...n.value,...r};return m++,h=void 0,{...n,issues:void 0,validity:`unknown`,value:t.immutable(prepare(i))}})},reset(e){ensureActive(`reset`),abortValidation();let n=e===void 0?void 0:t.immutable(prepare(e));write(e=>{let t=n??e.baseline;return m++,h=void 0,{...e,baseline:t,issues:void 0,touched:{},validity:`unknown`,value:t}})},set(e){ensureActive(`set`),abortValidation(),write(n=>{let r=typeof e==`function`?e(n.value):e;return m++,h=void 0,{...n,issues:void 0,validity:`unknown`,value:t.immutable(prepare(r))}})},get state(){return _},async submit(n,r){if(ensureActive(`submit`),g.isSubmitting)throw new e.ForgeSubmitError(`submit() called while a submission is already in progress`);let i=r?AbortSignal.any([l.signal,r]):l.signal;write(e=>({...e,isSubmitting:!0,submitCount:e.submitCount+1,touched:t.touchAll(e.value)}));try{let e=await validate(r);if(e.status===`aborted`)return Object.freeze({status:`aborted`});if(e.status===`invalid`)return Object.freeze({issues:e.issues,status:`invalid`});let t=h;if(!t||t.revision!==m||i.aborted)return Object.freeze({status:`aborted`});try{return Object.freeze({status:`ok`,value:await n(t.value,i)})}catch(e){if(i.aborted)return Object.freeze({status:`aborted`});throw e}}finally{u||write(e=>({...e,isSubmitting:!1}))}},subscribe(e,t={}){return ensureActive(`subscribe`),t.immediate&&c.call(()=>e(_)),c.add(()=>e(_))},[Symbol.dispose](){y.dispose()},validate,get value(){return g.value}};return y}exports.createForm=createForm;
1
+ const e=require("./_field.cjs"),t=require("./_notify.cjs"),n=require("./core/path.cjs"),r=require("./errors.cjs");let i=require("@vielzeug/arsenal");function normalizeIssues(e){if(e!==void 0&&e.length!==0)return Object.freeze(e.map(e=>{if(!e||typeof e.message!=`string`||!Array.isArray(e.path))throw new r.ForgeConfigError(`Validators must return issues with a string message and array path.`);let t=e.path.map(e=>{if(typeof e==`number`){if(!Number.isSafeInteger(e)||e<0)throw new r.ForgeConfigError(`Invalid issue path index ${e}.`);return e}if(typeof e==`string`)return n.assertSafeKey(e),e;throw new r.ForgeConfigError(`Issue paths may contain only string keys and non-negative integer indexes.`)});return Object.freeze({message:e.message,path:Object.freeze(t)})}))}function makeFormState(e){return Object.freeze({formError:e.issues?.find(e=>e.path.length===0)?.message,hasErrors:(e.issues?.length??0)>0,issues:e.issues,submitCount:e.submitCount,submitting:e.isSubmitting,touched:Object.keys(e.touched).length>0,validating:e.isValidating,validity:e.validity})}function createForm(a){let o=a.normalize,prepare=e=>o?o(e):e,s=n.immutable(prepare(a.initialValues)),c=t.createNotifier(a.onSubscriberError),l=new AbortController,u=!1,d=!1,f=[],p,m=0,h,g={baseline:s,isSubmitting:!1,issues:void 0,isValidating:!1,submitCount:0,touched:{},validity:`unknown`,value:s},_=makeFormState(g);function ensureActive(e){if(u)throw new r.ForgeDisposedError(e)}function write(e){if(f.push(e),!d){d=!0;try{for(;f.length>0;)g=Object.freeze(f.shift()(g)),_=makeFormState(g),c.notify(g)}finally{d=!1}}}function abortValidation(){p?.abort()}let v={abortValidation,addListener:c.add,ensureActive,invokeListener:c.call,readState:()=>g,resetValue(e){write(t=>(m++,h=void 0,{...t,touched:n.writeMeta(t.touched,e,!1),validity:`unknown`,value:n.resetAtPath(t.value,t.baseline,e)}))},setTouched(e,t){write(r=>({...r,touched:n.writeMeta(r.touched,e,t)}))},setValue(e,t){write(r=>{let i=n.readAtPath(r.value,e),a=typeof t==`function`?t(i):t,o=prepare(a);return m++,h=void 0,{...r,validity:`unknown`,value:n.writeAtPath(r.value,e,o)}})}};async function validate(e){ensureActive(`validate`),abortValidation();let t=new AbortController,n=e?AbortSignal.any([t.signal,e]):t.signal;if(n.aborted)return Object.freeze({status:`aborted`});let o=g.value,s=m;p=t,h=void 0,write(e=>({...e,isValidating:!0}));try{if(n.aborted||m!==s)return Object.freeze({status:`aborted`});let e=a.validate?await(0,i.abortable)(Promise.resolve(a.validate(o,n)),n):void 0;if(n.aborted||p!==t||m!==s)return Object.freeze({status:`aborted`});let r=normalizeIssues(e),c=r===void 0?`valid`:`invalid`;return p=void 0,write(e=>({...e,issues:r,isValidating:!1,validity:c})),n.aborted||m!==s||p!==void 0?Object.freeze({status:`aborted`}):(h=c===`valid`?{revision:m,value:o}:void 0,Object.freeze(c===`valid`?{status:`valid`}:{issues:r,status:`invalid`}))}catch(e){if(n.aborted||p!==t)return Object.freeze({status:`aborted`});throw new r.ForgeValidationError(`Form validation failed.`,{cause:e})}finally{p===t&&(p=void 0,u||write(e=>({...e,isValidating:!1})))}}let y={get disposalSignal(){return l.signal},dispose(){u||(u=!0,f.length=0,h=void 0,l.abort(),abortValidation(),c.clear())},get disposed(){return u},field(t){if(n.assertSafeKey(t),!(0,i.isPlainObject)(g.value))throw new r.ForgeConfigError(`Form value must be an object.`);return e.createField([t],v)},patch(e){ensureActive(`patch`),abortValidation(),write(t=>{let r=typeof e==`function`?e(t.value):e,i={...t.value,...r};return m++,h=void 0,{...t,issues:void 0,validity:`unknown`,value:n.immutable(prepare(i))}})},reset(e){ensureActive(`reset`),abortValidation();let t=e===void 0?void 0:n.immutable(prepare(e));write(e=>{let n=t??e.baseline;return m++,h=void 0,{...e,baseline:n,issues:void 0,touched:{},validity:`unknown`,value:n}})},set(e){ensureActive(`set`),abortValidation(),write(t=>{let r=typeof e==`function`?e(t.value):e;return m++,h=void 0,{...t,issues:void 0,validity:`unknown`,value:n.immutable(prepare(r))}})},get state(){return _},async submit(e,t){if(ensureActive(`submit`),g.isSubmitting)throw new r.ForgeSubmitError(`submit() called while a submission is already in progress`);let i=t?AbortSignal.any([l.signal,t]):l.signal;write(e=>({...e,isSubmitting:!0,submitCount:e.submitCount+1,touched:n.touchAll(e.value)}));try{let n=await validate(t);if(n.status===`aborted`)return Object.freeze({status:`aborted`});if(n.status===`invalid`)return Object.freeze({issues:n.issues,status:`invalid`});let r=h;if(!r||r.revision!==m||i.aborted)return Object.freeze({status:`aborted`});try{return Object.freeze({status:`ok`,value:await e(r.value,i)})}catch(e){if(i.aborted)return Object.freeze({status:`aborted`});throw e}}finally{u||write(e=>({...e,isSubmitting:!1}))}},subscribe(e,t={}){return ensureActive(`subscribe`),t.immediate&&c.call(()=>e(_)),c.add(()=>e(_))},[Symbol.dispose](){y.dispose()},validate,get value(){return g.value}};return y}exports.createForm=createForm;
2
2
  //# sourceMappingURL=form.cjs.map
package/dist/form.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"form.cjs","names":["ForgeConfigError","immutable","createNotifier","ForgeDisposedError","writeMeta","resetAtPath","readAtPath","writeAtPath","abortable","ForgeValidationError","isRecord","createField","ForgeSubmitError","touchAll","require_errors","require_path","require__field","require__notify","_vielzeug_arsenal","result","issue","path","part","current","normalize","options","value","initial","notifier","disposalController","disposed","dispatching","pendingWrites","validationController","revision","validatedSnapshot","publicState","operation","update","access","c","touched","previous","raw","next","controller","signal","externalSignal","snapshot","snapshotRevision","issues","validity","error","form","key","partial","merged","replacement","baseline","validated","handler","subscribeOptions","listener"],"sources":["../src/form.ts"],"sourcesContent":["import { abortable } from '@vielzeug/arsenal';\n\nimport { createField, type FieldAccess } from './_field.js';\nimport { createNotifier } from './_notify.js';\nimport {\n assertSafeKey,\n immutable,\n isRecord,\n type MetaRoot,\n readAtPath,\n resetAtPath,\n touchAll,\n writeAtPath,\n writeMeta,\n} from './core/path.js';\nimport { ForgeConfigError, ForgeDisposedError, ForgeSubmitError, ForgeValidationError } from './errors.js';\nimport type {\n Form,\n FormOptions,\n FormState,\n FormValidator,\n MaybePromise,\n SubmitResult,\n Unsubscribe,\n ValidationIssue,\n ValidationResult,\n} from './types.js';\n\ntype Validity = 'invalid' | 'unknown' | 'valid';\n\ntype InternalState<TValues extends Record<string, unknown>> = Readonly<{\n baseline: TValues;\n issues: readonly ValidationIssue[] | undefined;\n isSubmitting: boolean;\n isValidating: boolean;\n submitCount: number;\n touched: MetaRoot;\n validity: Validity;\n value: TValues;\n}>;\n\nfunction normalizeIssues(result: readonly ValidationIssue[] | undefined): readonly ValidationIssue[] | undefined {\n if (result === undefined || result.length === 0) return undefined;\n\n return Object.freeze(\n result.map((issue) => {\n if (!issue || typeof issue.message !== 'string' || !Array.isArray(issue.path)) {\n throw new ForgeConfigError('Validators must return issues with a string message and array path.');\n }\n\n const path = issue.path.map((part) => {\n if (typeof part === 'number') {\n if (!Number.isSafeInteger(part) || part < 0) throw new ForgeConfigError(`Invalid issue path index ${part}.`);\n return part;\n }\n if (typeof part === 'string') {\n assertSafeKey(part);\n return part;\n }\n throw new ForgeConfigError('Issue paths may contain only string keys and non-negative integer indexes.');\n });\n\n return Object.freeze({ message: issue.message, path: Object.freeze(path) });\n }),\n );\n}\n\nfunction makeFormState<TValues extends Record<string, unknown>>(current: InternalState<TValues>): FormState {\n return Object.freeze({\n formError: current.issues?.find((issue) => issue.path.length === 0)?.message,\n hasErrors: (current.issues?.length ?? 0) > 0,\n issues: current.issues,\n submitCount: current.submitCount,\n submitting: current.isSubmitting,\n touched: Object.keys(current.touched).length > 0,\n validating: current.isValidating,\n validity: current.validity,\n });\n}\n\n/** One immutable value tree and one explicit full-form validator keep form behavior locally understandable. */\nexport function createForm<TValues extends Record<string, unknown>>(options: FormOptions<TValues>): Form<TValues> {\n const normalize = options.normalize;\n const prepare = (value: unknown): unknown => (normalize ? normalize(value) : value);\n const initial = immutable(prepare(options.initialValues) as TValues);\n const notifier = createNotifier<InternalState<TValues>>(options.onSubscriberError);\n const disposalController = new AbortController();\n let disposed = false;\n let dispatching = false;\n const pendingWrites: Array<(current: InternalState<TValues>) => InternalState<TValues>> = [];\n let validationController: AbortController | undefined;\n let revision = 0;\n let validatedSnapshot: { revision: number; value: TValues } | undefined;\n let current: InternalState<TValues> = {\n baseline: initial,\n isSubmitting: false,\n issues: undefined,\n isValidating: false,\n submitCount: 0,\n touched: {},\n validity: 'unknown',\n value: initial,\n };\n let publicState = makeFormState(current);\n\n function ensureActive(operation: string): void {\n if (disposed) throw new ForgeDisposedError(operation);\n }\n\n function write(update: (current: InternalState<TValues>) => InternalState<TValues>): void {\n pendingWrites.push(update);\n if (dispatching) return;\n\n dispatching = true;\n try {\n while (pendingWrites.length > 0) {\n current = Object.freeze(pendingWrites.shift()!(current));\n publicState = makeFormState(current);\n notifier.notify(current);\n }\n } finally {\n dispatching = false;\n }\n }\n\n function abortValidation(): void {\n validationController?.abort();\n }\n\n const access: FieldAccess = {\n abortValidation,\n addListener: notifier.add,\n ensureActive,\n invokeListener: notifier.call,\n readState: () => current,\n resetValue(path) {\n write((c) => {\n revision++;\n validatedSnapshot = undefined;\n return {\n ...c,\n touched: writeMeta(c.touched, path, false),\n validity: 'unknown',\n value: resetAtPath(c.value, c.baseline, path),\n };\n });\n },\n setTouched(path, touched) {\n write((c) => ({ ...c, touched: writeMeta(c.touched, path, touched) }));\n },\n setValue(path, next) {\n write((c) => {\n const previous = readAtPath(c.value, path);\n const raw = typeof next === 'function' ? next(previous) : next;\n const value = prepare(raw);\n revision++;\n validatedSnapshot = undefined;\n return { ...c, validity: 'unknown', value: writeAtPath(c.value, path, value) };\n });\n },\n };\n\n async function validate(externalSignal?: AbortSignal): Promise<ValidationResult> {\n ensureActive('validate');\n abortValidation();\n\n const controller = new AbortController();\n const signal = externalSignal ? AbortSignal.any([controller.signal, externalSignal]) : controller.signal;\n if (signal.aborted) return Object.freeze({ status: 'aborted' });\n\n const snapshot = current.value;\n const snapshotRevision = revision;\n validationController = controller;\n validatedSnapshot = undefined;\n write((c) => ({ ...c, isValidating: true }));\n\n try {\n if (signal.aborted || revision !== snapshotRevision) return Object.freeze({ status: 'aborted' });\n const result: readonly ValidationIssue[] | undefined = options.validate\n ? await abortable(\n Promise.resolve(options.validate(snapshot as Parameters<FormValidator<TValues>>[0], signal)),\n signal,\n )\n : undefined;\n\n if (signal.aborted || validationController !== controller || revision !== snapshotRevision) {\n return Object.freeze({ status: 'aborted' });\n }\n\n const issues = normalizeIssues(result);\n const validity: Validity = issues === undefined ? 'valid' : 'invalid';\n validationController = undefined;\n write((c) => ({ ...c, issues, isValidating: false, validity }));\n\n if (signal.aborted || revision !== snapshotRevision || validationController !== undefined) {\n return Object.freeze({ status: 'aborted' });\n }\n\n validatedSnapshot = validity === 'valid' ? { revision, value: snapshot } : undefined;\n return validity === 'valid'\n ? Object.freeze({ status: 'valid' })\n : Object.freeze({ issues: issues as readonly ValidationIssue[], status: 'invalid' });\n } catch (error) {\n if (signal.aborted || validationController !== controller) return Object.freeze({ status: 'aborted' });\n throw new ForgeValidationError('Form validation failed.', { cause: error });\n } finally {\n if (validationController === controller) {\n validationController = undefined;\n if (!disposed) write((c) => ({ ...c, isValidating: false }));\n }\n }\n }\n\n const form: Form<TValues> = {\n get disposalSignal() {\n return disposalController.signal;\n },\n dispose() {\n if (disposed) return;\n\n disposed = true;\n pendingWrites.length = 0;\n validatedSnapshot = undefined;\n disposalController.abort();\n abortValidation();\n notifier.clear();\n },\n get disposed() {\n return disposed;\n },\n field(key) {\n assertSafeKey(key);\n if (!isRecord(current.value)) throw new ForgeConfigError('Form value must be an object.');\n\n return createField<TValues[typeof key]>([key], access);\n },\n patch(next) {\n ensureActive('patch');\n abortValidation();\n\n write((c) => {\n const partial =\n typeof next === 'function'\n ? (next as (previous: Form<TValues>['value']) => Partial<TValues>)(c.value as Form<TValues>['value'])\n : next;\n const merged = { ...c.value, ...partial };\n revision++;\n validatedSnapshot = undefined;\n return { ...c, issues: undefined, validity: 'unknown', value: immutable(prepare(merged) as TValues) };\n });\n },\n reset(next) {\n ensureActive('reset');\n abortValidation();\n\n const replacement = next === undefined ? undefined : immutable(prepare(next) as TValues);\n write((c) => {\n const baseline = replacement ?? c.baseline;\n revision++;\n validatedSnapshot = undefined;\n return { ...c, baseline, issues: undefined, touched: {}, validity: 'unknown', value: baseline };\n });\n },\n set(next) {\n ensureActive('set');\n abortValidation();\n\n write((c) => {\n const value =\n typeof next === 'function'\n ? (next as (previous: Form<TValues>['value']) => TValues)(c.value as Form<TValues>['value'])\n : next;\n revision++;\n validatedSnapshot = undefined;\n return { ...c, issues: undefined, validity: 'unknown', value: immutable(prepare(value) as TValues) };\n });\n },\n get state() {\n return publicState;\n },\n async submit<TResult = void>(\n handler: (values: Form<TValues>['value'], signal: AbortSignal) => MaybePromise<TResult>,\n externalSignal?: AbortSignal,\n ): Promise<SubmitResult<TResult>> {\n ensureActive('submit');\n\n if (current.isSubmitting) {\n throw new ForgeSubmitError('submit() called while a submission is already in progress');\n }\n\n const signal = externalSignal\n ? AbortSignal.any([disposalController.signal, externalSignal])\n : disposalController.signal;\n\n write((c) => ({\n ...c,\n isSubmitting: true,\n submitCount: c.submitCount + 1,\n touched: touchAll(c.value),\n }));\n\n try {\n const result = await validate(externalSignal);\n\n if (result.status === 'aborted') return Object.freeze({ status: 'aborted' });\n\n if (result.status === 'invalid') {\n return Object.freeze({ issues: result.issues, status: 'invalid' });\n }\n\n const validated = validatedSnapshot;\n if (!validated || validated.revision !== revision || signal.aborted) {\n return Object.freeze({ status: 'aborted' });\n }\n\n try {\n return Object.freeze({\n status: 'ok',\n value: await handler(validated.value as Form<TValues>['value'], signal),\n });\n } catch (error) {\n if (signal.aborted) return Object.freeze({ status: 'aborted' });\n\n throw error;\n }\n } finally {\n if (!disposed) write((c) => ({ ...c, isSubmitting: false }));\n }\n },\n subscribe(listener, subscribeOptions = {}): Unsubscribe {\n ensureActive('subscribe');\n\n if (subscribeOptions.immediate) notifier.call(() => listener(publicState));\n\n return notifier.add(() => listener(publicState));\n },\n [Symbol.dispose]() {\n form.dispose();\n },\n validate,\n get value() {\n return current.value as Form<TValues>['value'];\n },\n };\n\n return form;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"form.cjs","names":["ForgeConfigError","immutable","createNotifier","ForgeDisposedError","writeMeta","resetAtPath","readAtPath","writeAtPath","abortable","ForgeValidationError","isRecord","createField","ForgeSubmitError","touchAll","require__field","require__notify","require_path","require_errors","_vielzeug_arsenal","result","issue","path","part","current","normalize","options","value","initial","notifier","disposalController","disposed","dispatching","pendingWrites","validationController","revision","validatedSnapshot","publicState","operation","update","access","c","touched","previous","raw","next","controller","signal","externalSignal","snapshot","snapshotRevision","issues","validity","error","form","key","partial","merged","replacement","baseline","validated","handler","subscribeOptions","listener"],"sources":["../src/form.ts"],"sourcesContent":["import { abortable } from '@vielzeug/arsenal';\n\nimport { createField, type FieldAccess } from './_field.js';\nimport { createNotifier } from './_notify.js';\nimport {\n assertSafeKey,\n immutable,\n isRecord,\n type MetaRoot,\n readAtPath,\n resetAtPath,\n touchAll,\n writeAtPath,\n writeMeta,\n} from './core/path.js';\nimport { ForgeConfigError, ForgeDisposedError, ForgeSubmitError, ForgeValidationError } from './errors.js';\nimport type {\n Form,\n FormOptions,\n FormState,\n FormValidator,\n MaybePromise,\n SubmitResult,\n Unsubscribe,\n ValidationIssue,\n ValidationResult,\n} from './types.js';\n\ntype Validity = 'invalid' | 'unknown' | 'valid';\n\ntype InternalState<TValues extends Record<string, unknown>> = Readonly<{\n baseline: TValues;\n issues: readonly ValidationIssue[] | undefined;\n isSubmitting: boolean;\n isValidating: boolean;\n submitCount: number;\n touched: MetaRoot;\n validity: Validity;\n value: TValues;\n}>;\n\nfunction normalizeIssues(result: readonly ValidationIssue[] | undefined): readonly ValidationIssue[] | undefined {\n if (result === undefined || result.length === 0) return undefined;\n\n return Object.freeze(\n result.map((issue) => {\n if (!issue || typeof issue.message !== 'string' || !Array.isArray(issue.path)) {\n throw new ForgeConfigError('Validators must return issues with a string message and array path.');\n }\n\n const path = issue.path.map((part) => {\n if (typeof part === 'number') {\n if (!Number.isSafeInteger(part) || part < 0) throw new ForgeConfigError(`Invalid issue path index ${part}.`);\n return part;\n }\n if (typeof part === 'string') {\n assertSafeKey(part);\n return part;\n }\n throw new ForgeConfigError('Issue paths may contain only string keys and non-negative integer indexes.');\n });\n\n return Object.freeze({ message: issue.message, path: Object.freeze(path) });\n }),\n );\n}\n\nfunction makeFormState<TValues extends Record<string, unknown>>(current: InternalState<TValues>): FormState {\n return Object.freeze({\n formError: current.issues?.find((issue) => issue.path.length === 0)?.message,\n hasErrors: (current.issues?.length ?? 0) > 0,\n issues: current.issues,\n submitCount: current.submitCount,\n submitting: current.isSubmitting,\n touched: Object.keys(current.touched).length > 0,\n validating: current.isValidating,\n validity: current.validity,\n });\n}\n\n/** One immutable value tree and one explicit full-form validator keep form behavior locally understandable. */\nexport function createForm<TValues extends Record<string, unknown>>(options: FormOptions<TValues>): Form<TValues> {\n const normalize = options.normalize;\n const prepare = (value: unknown): unknown => (normalize ? normalize(value) : value);\n const initial = immutable(prepare(options.initialValues) as TValues);\n const notifier = createNotifier<InternalState<TValues>>(options.onSubscriberError);\n const disposalController = new AbortController();\n let disposed = false;\n let dispatching = false;\n const pendingWrites: Array<(current: InternalState<TValues>) => InternalState<TValues>> = [];\n let validationController: AbortController | undefined;\n let revision = 0;\n let validatedSnapshot: { revision: number; value: TValues } | undefined;\n let current: InternalState<TValues> = {\n baseline: initial,\n isSubmitting: false,\n issues: undefined,\n isValidating: false,\n submitCount: 0,\n touched: {},\n validity: 'unknown',\n value: initial,\n };\n let publicState = makeFormState(current);\n\n function ensureActive(operation: string): void {\n if (disposed) throw new ForgeDisposedError(operation);\n }\n\n function write(update: (current: InternalState<TValues>) => InternalState<TValues>): void {\n pendingWrites.push(update);\n if (dispatching) return;\n\n dispatching = true;\n try {\n while (pendingWrites.length > 0) {\n current = Object.freeze(pendingWrites.shift()!(current));\n publicState = makeFormState(current);\n notifier.notify(current);\n }\n } finally {\n dispatching = false;\n }\n }\n\n function abortValidation(): void {\n validationController?.abort();\n }\n\n const access: FieldAccess = {\n abortValidation,\n addListener: notifier.add,\n ensureActive,\n invokeListener: notifier.call,\n readState: () => current,\n resetValue(path) {\n write((c) => {\n revision++;\n validatedSnapshot = undefined;\n return {\n ...c,\n touched: writeMeta(c.touched, path, false),\n validity: 'unknown',\n value: resetAtPath(c.value, c.baseline, path),\n };\n });\n },\n setTouched(path, touched) {\n write((c) => ({ ...c, touched: writeMeta(c.touched, path, touched) }));\n },\n setValue(path, next) {\n write((c) => {\n const previous = readAtPath(c.value, path);\n const raw = typeof next === 'function' ? next(previous) : next;\n const value = prepare(raw);\n revision++;\n validatedSnapshot = undefined;\n return { ...c, validity: 'unknown', value: writeAtPath(c.value, path, value) };\n });\n },\n };\n\n async function validate(externalSignal?: AbortSignal): Promise<ValidationResult> {\n ensureActive('validate');\n abortValidation();\n\n const controller = new AbortController();\n const signal = externalSignal ? AbortSignal.any([controller.signal, externalSignal]) : controller.signal;\n if (signal.aborted) return Object.freeze({ status: 'aborted' });\n\n const snapshot = current.value;\n const snapshotRevision = revision;\n validationController = controller;\n validatedSnapshot = undefined;\n write((c) => ({ ...c, isValidating: true }));\n\n try {\n if (signal.aborted || revision !== snapshotRevision) return Object.freeze({ status: 'aborted' });\n const result: readonly ValidationIssue[] | undefined = options.validate\n ? await abortable(\n Promise.resolve(options.validate(snapshot as Parameters<FormValidator<TValues>>[0], signal)),\n signal,\n )\n : undefined;\n\n if (signal.aborted || validationController !== controller || revision !== snapshotRevision) {\n return Object.freeze({ status: 'aborted' });\n }\n\n const issues = normalizeIssues(result);\n const validity: Validity = issues === undefined ? 'valid' : 'invalid';\n validationController = undefined;\n write((c) => ({ ...c, issues, isValidating: false, validity }));\n\n if (signal.aborted || revision !== snapshotRevision || validationController !== undefined) {\n return Object.freeze({ status: 'aborted' });\n }\n\n validatedSnapshot = validity === 'valid' ? { revision, value: snapshot } : undefined;\n return validity === 'valid'\n ? Object.freeze({ status: 'valid' })\n : Object.freeze({ issues: issues as readonly ValidationIssue[], status: 'invalid' });\n } catch (error) {\n if (signal.aborted || validationController !== controller) return Object.freeze({ status: 'aborted' });\n throw new ForgeValidationError('Form validation failed.', { cause: error });\n } finally {\n if (validationController === controller) {\n validationController = undefined;\n if (!disposed) write((c) => ({ ...c, isValidating: false }));\n }\n }\n }\n\n const form: Form<TValues> = {\n get disposalSignal() {\n return disposalController.signal;\n },\n dispose() {\n if (disposed) return;\n\n disposed = true;\n pendingWrites.length = 0;\n validatedSnapshot = undefined;\n disposalController.abort();\n abortValidation();\n notifier.clear();\n },\n get disposed() {\n return disposed;\n },\n field(key) {\n assertSafeKey(key);\n if (!isRecord(current.value)) throw new ForgeConfigError('Form value must be an object.');\n\n return createField<TValues[typeof key]>([key], access);\n },\n patch(next) {\n ensureActive('patch');\n abortValidation();\n\n write((c) => {\n const partial =\n typeof next === 'function'\n ? (next as (previous: Form<TValues>['value']) => Partial<TValues>)(c.value as Form<TValues>['value'])\n : next;\n const merged = { ...c.value, ...partial };\n revision++;\n validatedSnapshot = undefined;\n return { ...c, issues: undefined, validity: 'unknown', value: immutable(prepare(merged) as TValues) };\n });\n },\n reset(next) {\n ensureActive('reset');\n abortValidation();\n\n const replacement = next === undefined ? undefined : immutable(prepare(next) as TValues);\n write((c) => {\n const baseline = replacement ?? c.baseline;\n revision++;\n validatedSnapshot = undefined;\n return { ...c, baseline, issues: undefined, touched: {}, validity: 'unknown', value: baseline };\n });\n },\n set(next) {\n ensureActive('set');\n abortValidation();\n\n write((c) => {\n const value =\n typeof next === 'function'\n ? (next as (previous: Form<TValues>['value']) => TValues)(c.value as Form<TValues>['value'])\n : next;\n revision++;\n validatedSnapshot = undefined;\n return { ...c, issues: undefined, validity: 'unknown', value: immutable(prepare(value) as TValues) };\n });\n },\n get state() {\n return publicState;\n },\n async submit<TResult = void>(\n handler: (values: Form<TValues>['value'], signal: AbortSignal) => MaybePromise<TResult>,\n externalSignal?: AbortSignal,\n ): Promise<SubmitResult<TResult>> {\n ensureActive('submit');\n\n if (current.isSubmitting) {\n throw new ForgeSubmitError('submit() called while a submission is already in progress');\n }\n\n const signal = externalSignal\n ? AbortSignal.any([disposalController.signal, externalSignal])\n : disposalController.signal;\n\n write((c) => ({\n ...c,\n isSubmitting: true,\n submitCount: c.submitCount + 1,\n touched: touchAll(c.value),\n }));\n\n try {\n const result = await validate(externalSignal);\n\n if (result.status === 'aborted') return Object.freeze({ status: 'aborted' });\n\n if (result.status === 'invalid') {\n return Object.freeze({ issues: result.issues, status: 'invalid' });\n }\n\n const validated = validatedSnapshot;\n if (!validated || validated.revision !== revision || signal.aborted) {\n return Object.freeze({ status: 'aborted' });\n }\n\n try {\n return Object.freeze({\n status: 'ok',\n value: await handler(validated.value as Form<TValues>['value'], signal),\n });\n } catch (error) {\n if (signal.aborted) return Object.freeze({ status: 'aborted' });\n\n throw error;\n }\n } finally {\n if (!disposed) write((c) => ({ ...c, isSubmitting: false }));\n }\n },\n subscribe(listener, subscribeOptions = {}): Unsubscribe {\n ensureActive('subscribe');\n\n if (subscribeOptions.immediate) notifier.call(() => listener(publicState));\n\n return notifier.add(() => listener(publicState));\n },\n [Symbol.dispose]() {\n form.dispose();\n },\n validate,\n get value() {\n return current.value as Form<TValues>['value'];\n },\n };\n\n return form;\n}\n"],"mappings":"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"}
package/dist/form.js CHANGED
@@ -1,23 +1,23 @@
1
- import { ForgeConfigError as e, ForgeDisposedError as t, ForgeSubmitError as n, ForgeValidationError as r } from "./errors.js";
2
- import { assertSafeKey as i, immutable as a, isRecord as o, readAtPath as s, resetAtPath as c, touchAll as l, writeAtPath as u, writeMeta as d } from "./core/path.js";
3
- import { createField as f } from "./_field.js";
4
- import { createNotifier as p } from "./_notify.js";
1
+ import { createField as e } from "./_field.js";
2
+ import { createNotifier as t } from "./_notify.js";
3
+ import { assertSafeKey as n, immutable as r, isRecord as i, readAtPath as a, resetAtPath as o, touchAll as s, writeAtPath as c, writeMeta as l } from "./core/path.js";
4
+ import { ForgeConfigError as u, ForgeDisposedError as d, ForgeSubmitError as f, ForgeValidationError as p } from "./errors.js";
5
5
  import { abortable as m } from "@vielzeug/arsenal";
6
- //#region src/form.ts
7
- function normalizeIssues(t) {
8
- if (t !== void 0 && t.length !== 0) return Object.freeze(t.map((t) => {
9
- if (!t || typeof t.message != "string" || !Array.isArray(t.path)) throw new e("Validators must return issues with a string message and array path.");
10
- let n = t.path.map((t) => {
11
- if (typeof t == "number") {
12
- if (!Number.isSafeInteger(t) || t < 0) throw new e(`Invalid issue path index ${t}.`);
13
- return t;
6
+ // src/form.ts
7
+ function normalizeIssues(e) {
8
+ if (e !== void 0 && e.length !== 0) return Object.freeze(e.map((e) => {
9
+ if (!e || typeof e.message != "string" || !Array.isArray(e.path)) throw new u("Validators must return issues with a string message and array path.");
10
+ let t = e.path.map((e) => {
11
+ if (typeof e == "number") {
12
+ if (!Number.isSafeInteger(e) || e < 0) throw new u(`Invalid issue path index ${e}.`);
13
+ return e;
14
14
  }
15
- if (typeof t == "string") return i(t), t;
16
- throw new e("Issue paths may contain only string keys and non-negative integer indexes.");
15
+ if (typeof e == "string") return n(e), e;
16
+ throw new u("Issue paths may contain only string keys and non-negative integer indexes.");
17
17
  });
18
18
  return Object.freeze({
19
- message: t.message,
20
- path: Object.freeze(n)
19
+ message: e.message,
20
+ path: Object.freeze(t)
21
21
  });
22
22
  }));
23
23
  }
@@ -34,7 +34,7 @@ function makeFormState(e) {
34
34
  });
35
35
  }
36
36
  function createForm(h) {
37
- let g = h.normalize, prepare = (e) => g ? g(e) : e, _ = a(prepare(h.initialValues)), v = p(h.onSubscriberError), y = new AbortController(), b = !1, x = !1, S = [], C, w = 0, T, E = {
37
+ let g = h.normalize, prepare = (e) => g ? g(e) : e, _ = r(prepare(h.initialValues)), v = t(h.onSubscriberError), y = new AbortController(), b = !1, x = !1, S = [], C, w = 0, T, E = {
38
38
  baseline: _,
39
39
  isSubmitting: !1,
40
40
  issues: void 0,
@@ -45,7 +45,7 @@ function createForm(h) {
45
45
  value: _
46
46
  }, D = makeFormState(E);
47
47
  function ensureActive(e) {
48
- if (b) throw new t(e);
48
+ if (b) throw new d(e);
49
49
  }
50
50
  function write(e) {
51
51
  if (S.push(e), !x) {
@@ -69,24 +69,24 @@ function createForm(h) {
69
69
  resetValue(e) {
70
70
  write((t) => (w++, T = void 0, {
71
71
  ...t,
72
- touched: d(t.touched, e, !1),
72
+ touched: l(t.touched, e, !1),
73
73
  validity: "unknown",
74
- value: c(t.value, t.baseline, e)
74
+ value: o(t.value, t.baseline, e)
75
75
  }));
76
76
  },
77
77
  setTouched(e, t) {
78
78
  write((n) => ({
79
79
  ...n,
80
- touched: d(n.touched, e, t)
80
+ touched: l(n.touched, e, t)
81
81
  }));
82
82
  },
83
83
  setValue(e, t) {
84
84
  write((n) => {
85
- let r = s(n.value, e), i = typeof t == "function" ? t(r) : t, a = prepare(i);
85
+ let r = a(n.value, e), i = typeof t == "function" ? t(r) : t, o = prepare(i);
86
86
  return w++, T = void 0, {
87
87
  ...n,
88
88
  validity: "unknown",
89
- value: u(n.value, e, a)
89
+ value: c(n.value, e, o)
90
90
  };
91
91
  });
92
92
  }
@@ -95,31 +95,31 @@ function createForm(h) {
95
95
  ensureActive("validate"), abortValidation();
96
96
  let t = new AbortController(), n = e ? AbortSignal.any([t.signal, e]) : t.signal;
97
97
  if (n.aborted) return Object.freeze({ status: "aborted" });
98
- let i = E.value, a = w;
98
+ let r = E.value, i = w;
99
99
  C = t, T = void 0, write((e) => ({
100
100
  ...e,
101
101
  isValidating: !0
102
102
  }));
103
103
  try {
104
- if (n.aborted || w !== a) return Object.freeze({ status: "aborted" });
105
- let e = h.validate ? await m(Promise.resolve(h.validate(i, n)), n) : void 0;
106
- if (n.aborted || C !== t || w !== a) return Object.freeze({ status: "aborted" });
107
- let r = normalizeIssues(e), o = r === void 0 ? "valid" : "invalid";
104
+ if (n.aborted || w !== i) return Object.freeze({ status: "aborted" });
105
+ let e = h.validate ? await m(Promise.resolve(h.validate(r, n)), n) : void 0;
106
+ if (n.aborted || C !== t || w !== i) return Object.freeze({ status: "aborted" });
107
+ let a = normalizeIssues(e), o = a === void 0 ? "valid" : "invalid";
108
108
  return C = void 0, write((e) => ({
109
109
  ...e,
110
- issues: r,
110
+ issues: a,
111
111
  isValidating: !1,
112
112
  validity: o
113
- })), n.aborted || w !== a || C !== void 0 ? Object.freeze({ status: "aborted" }) : (T = o === "valid" ? {
113
+ })), n.aborted || w !== i || C !== void 0 ? Object.freeze({ status: "aborted" }) : (T = o === "valid" ? {
114
114
  revision: w,
115
- value: i
115
+ value: r
116
116
  } : void 0, Object.freeze(o === "valid" ? { status: "valid" } : {
117
- issues: r,
117
+ issues: a,
118
118
  status: "invalid"
119
119
  }));
120
120
  } catch (e) {
121
121
  if (n.aborted || C !== t) return Object.freeze({ status: "aborted" });
122
- throw new r("Form validation failed.", { cause: e });
122
+ throw new p("Form validation failed.", { cause: e });
123
123
  } finally {
124
124
  C === t && (C = void 0, b || write((e) => ({
125
125
  ...e,
@@ -138,12 +138,12 @@ function createForm(h) {
138
138
  return b;
139
139
  },
140
140
  field(t) {
141
- if (i(t), !o(E.value)) throw new e("Form value must be an object.");
142
- return f([t], O);
141
+ if (n(t), !i(E.value)) throw new u("Form value must be an object.");
142
+ return e([t], O);
143
143
  },
144
144
  patch(e) {
145
145
  ensureActive("patch"), abortValidation(), write((t) => {
146
- let n = typeof e == "function" ? e(t.value) : e, r = {
146
+ let n = typeof e == "function" ? e(t.value) : e, i = {
147
147
  ...t.value,
148
148
  ...n
149
149
  };
@@ -151,13 +151,13 @@ function createForm(h) {
151
151
  ...t,
152
152
  issues: void 0,
153
153
  validity: "unknown",
154
- value: a(prepare(r))
154
+ value: r(prepare(i))
155
155
  };
156
156
  });
157
157
  },
158
158
  reset(e) {
159
159
  ensureActive("reset"), abortValidation();
160
- let t = e === void 0 ? void 0 : a(prepare(e));
160
+ let t = e === void 0 ? void 0 : r(prepare(e));
161
161
  write((e) => {
162
162
  let n = t ?? e.baseline;
163
163
  return w++, T = void 0, {
@@ -177,7 +177,7 @@ function createForm(h) {
177
177
  ...t,
178
178
  issues: void 0,
179
179
  validity: "unknown",
180
- value: a(prepare(n))
180
+ value: r(prepare(n))
181
181
  };
182
182
  });
183
183
  },
@@ -185,30 +185,30 @@ function createForm(h) {
185
185
  return D;
186
186
  },
187
187
  async submit(e, t) {
188
- if (ensureActive("submit"), E.isSubmitting) throw new n("submit() called while a submission is already in progress");
189
- let r = t ? AbortSignal.any([y.signal, t]) : y.signal;
188
+ if (ensureActive("submit"), E.isSubmitting) throw new f("submit() called while a submission is already in progress");
189
+ let n = t ? AbortSignal.any([y.signal, t]) : y.signal;
190
190
  write((e) => ({
191
191
  ...e,
192
192
  isSubmitting: !0,
193
193
  submitCount: e.submitCount + 1,
194
- touched: l(e.value)
194
+ touched: s(e.value)
195
195
  }));
196
196
  try {
197
- let n = await validate(t);
198
- if (n.status === "aborted") return Object.freeze({ status: "aborted" });
199
- if (n.status === "invalid") return Object.freeze({
200
- issues: n.issues,
197
+ let r = await validate(t);
198
+ if (r.status === "aborted") return Object.freeze({ status: "aborted" });
199
+ if (r.status === "invalid") return Object.freeze({
200
+ issues: r.issues,
201
201
  status: "invalid"
202
202
  });
203
203
  let i = T;
204
- if (!i || i.revision !== w || r.aborted) return Object.freeze({ status: "aborted" });
204
+ if (!i || i.revision !== w || n.aborted) return Object.freeze({ status: "aborted" });
205
205
  try {
206
206
  return Object.freeze({
207
207
  status: "ok",
208
- value: await e(i.value, r)
208
+ value: await e(i.value, n)
209
209
  });
210
210
  } catch (e) {
211
- if (r.aborted) return Object.freeze({ status: "aborted" });
211
+ if (n.aborted) return Object.freeze({ status: "aborted" });
212
212
  throw e;
213
213
  }
214
214
  } finally {
@@ -231,7 +231,6 @@ function createForm(h) {
231
231
  };
232
232
  return k;
233
233
  }
234
- //#endregion
235
234
  export { createForm };
236
235
 
237
236
  //# sourceMappingURL=form.js.map
package/dist/persist.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { abortError as e, abortable as t } from "@vielzeug/arsenal";
2
- //#region src/persist.ts
2
+ // src/persist.ts
3
3
  async function loadForm(n, r, i, a, o, s = {}) {
4
4
  let c = s.signal ? AbortSignal.any([n.disposalSignal, s.signal]) : n.disposalSignal;
5
5
  if (c.aborted) throw e(c);
@@ -13,7 +13,6 @@ async function saveForm(n, r, i, a, o = {}) {
13
13
  if (s.aborted) throw e(s);
14
14
  await t(r.put(i, a.toRecord(n.value)), s);
15
15
  }
16
- //#endregion
17
16
  export { loadForm, saveForm };
18
17
 
19
18
  //# sourceMappingURL=persist.js.map
package/dist/plain.js CHANGED
@@ -1,9 +1,8 @@
1
1
  import { toPlainValues as e } from "./core/path.js";
2
- //#region src/plain.ts
2
+ // src/plain.ts
3
3
  function toPlainValues(t) {
4
4
  return e(t);
5
5
  }
6
- //#endregion
7
6
  export { toPlainValues };
8
7
 
9
8
  //# sourceMappingURL=plain.js.map
package/dist/schema.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { abortable as e } from "@vielzeug/arsenal";
2
- //#region src/schema.ts
2
+ // src/schema.ts
3
3
  var toPath = (e) => {
4
4
  let t = [];
5
5
  for (let n of e ?? []) {
@@ -20,7 +20,6 @@ function schemaValidator(t) {
20
20
  }));
21
21
  };
22
22
  }
23
- //#endregion
24
23
  export { schemaValidator };
25
24
 
26
25
  //# sourceMappingURL=schema.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vielzeug/forge",
3
- "version": "26.11.5",
3
+ "version": "26.11.6",
4
4
  "description": "Typed form state, validation, submission",
5
5
  "repository": {
6
6
  "type": "git",
@@ -67,26 +67,26 @@
67
67
  "registry": "https://registry.npmjs.org/"
68
68
  },
69
69
  "scripts": {
70
- "build": "vite build && pnpm --silent run build:bundle && pnpm --silent run build:types && pnpm --silent run test:types && pnpm --silent run verify:exports",
70
+ "build": "vite build && bun run --silent build:bundle && bun run --silent build:types && bun run --silent test:types && bun run --silent verify:exports",
71
71
  "build:bundle": "vite build --config vite.bundle.config.ts",
72
72
  "build:types": "tsc -p tsconfig.declarations.json",
73
73
  "fix": "biome check --write src",
74
74
  "lint": "biome ci src",
75
- "prepublishOnly": "pnpm --silent run build",
75
+ "prepublishOnly": "bun run --silent build",
76
76
  "preview": "vite preview",
77
77
  "test": "vitest",
78
78
  "test:types": "tsc -p tsconfig.type-tests.json",
79
- "verify:exports": "node verify-exports.mjs"
79
+ "verify:exports": "bun verify-exports.mjs"
80
80
  },
81
81
  "dependencies": {
82
- "@vielzeug/arsenal": "26.11.5"
82
+ "@vielzeug/arsenal": "26.11.6"
83
83
  },
84
84
  "devDependencies": {
85
- "@types/node": "^26.6.5",
86
- "@vielzeug/spell": "26.11.5",
87
- "@vielzeug/vault": "26.11.5",
85
+ "@types/node": "^26.6.6",
86
+ "@vielzeug/spell": "26.11.6",
87
+ "@vielzeug/vault": "26.11.6",
88
88
  "typescript": "^6.0.3",
89
- "vite": "^8.3.4",
90
- "vitest": "^5.0.5"
89
+ "vite": "^8.3.6",
90
+ "vitest": "^5.1.0"
91
91
  }
92
92
  }