@vielzeug/forge 1.3.1 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. package/README.md +21 -53
  2. package/dist/_utils.cjs +1 -2
  3. package/dist/_utils.d.ts +1 -10
  4. package/dist/_utils.d.ts.map +1 -1
  5. package/dist/_utils.js +2 -32
  6. package/dist/adapters/form-data.cjs +1 -1
  7. package/dist/adapters/form-data.cjs.map +1 -1
  8. package/dist/adapters/form-data.d.ts.map +1 -1
  9. package/dist/adapters/form-data.js +5 -5
  10. package/dist/adapters/form-data.js.map +1 -1
  11. package/dist/core/path.cjs +2 -0
  12. package/dist/core/path.cjs.map +1 -0
  13. package/dist/core/path.d.ts +22 -0
  14. package/dist/core/path.d.ts.map +1 -0
  15. package/dist/core/path.js +125 -0
  16. package/dist/core/path.js.map +1 -0
  17. package/dist/devtools.cjs +1 -1
  18. package/dist/devtools.cjs.map +1 -1
  19. package/dist/devtools.d.ts +3 -36
  20. package/dist/devtools.d.ts.map +1 -1
  21. package/dist/devtools.js +6 -24
  22. package/dist/devtools.js.map +1 -1
  23. package/dist/dom.cjs +2 -0
  24. package/dist/dom.cjs.map +1 -0
  25. package/dist/dom.d.ts +9 -0
  26. package/dist/dom.d.ts.map +1 -0
  27. package/dist/dom.js +24 -0
  28. package/dist/dom.js.map +1 -0
  29. package/dist/errors.cjs +1 -1
  30. package/dist/errors.cjs.map +1 -1
  31. package/dist/errors.d.ts +4 -4
  32. package/dist/errors.d.ts.map +1 -1
  33. package/dist/errors.js +2 -7
  34. package/dist/errors.js.map +1 -1
  35. package/dist/forge.cjs +1 -1
  36. package/dist/forge.cjs.map +1 -1
  37. package/dist/forge.iife.js +1 -1
  38. package/dist/forge.iife.js.map +1 -1
  39. package/dist/forge.js +1 -1
  40. package/dist/forge.js.map +1 -1
  41. package/dist/form.cjs +1 -1
  42. package/dist/form.cjs.map +1 -1
  43. package/dist/form.d.ts +3 -5
  44. package/dist/form.d.ts.map +1 -1
  45. package/dist/form.js +225 -71
  46. package/dist/form.js.map +1 -1
  47. package/dist/index.cjs +1 -1
  48. package/dist/index.js +3 -4
  49. package/dist/spell.cjs +2 -0
  50. package/dist/spell.cjs.map +1 -0
  51. package/dist/spell.d.ts +5 -0
  52. package/dist/spell.d.ts.map +1 -0
  53. package/dist/spell.js +57 -0
  54. package/dist/spell.js.map +1 -0
  55. package/dist/types.d.ts +70 -375
  56. package/dist/types.d.ts.map +1 -1
  57. package/dist/vault.cjs +2 -0
  58. package/dist/vault.cjs.map +1 -0
  59. package/dist/vault.d.ts +10 -0
  60. package/dist/vault.d.ts.map +1 -0
  61. package/dist/vault.js +14 -0
  62. package/dist/vault.js.map +1 -0
  63. package/package.json +36 -11
  64. package/dist/_dev.cjs +0 -2
  65. package/dist/_dev.cjs.map +0 -1
  66. package/dist/_dev.js +0 -12
  67. package/dist/_dev.js.map +0 -1
  68. package/dist/_utils.cjs.map +0 -1
  69. package/dist/_utils.js.map +0 -1
  70. package/dist/adapters/validators.cjs +0 -2
  71. package/dist/adapters/validators.cjs.map +0 -1
  72. package/dist/adapters/validators.d.ts +0 -44
  73. package/dist/adapters/validators.d.ts.map +0 -1
  74. package/dist/adapters/validators.js +0 -20
  75. package/dist/adapters/validators.js.map +0 -1
  76. package/dist/core/array.cjs +0 -2
  77. package/dist/core/array.cjs.map +0 -1
  78. package/dist/core/array.d.ts +0 -10
  79. package/dist/core/array.d.ts.map +0 -1
  80. package/dist/core/array.js +0 -46
  81. package/dist/core/array.js.map +0 -1
  82. package/dist/core/asyncQueue.cjs +0 -2
  83. package/dist/core/asyncQueue.cjs.map +0 -1
  84. package/dist/core/asyncQueue.d.ts +0 -22
  85. package/dist/core/asyncQueue.d.ts.map +0 -1
  86. package/dist/core/asyncQueue.js +0 -67
  87. package/dist/core/asyncQueue.js.map +0 -1
  88. package/dist/core/context.cjs +0 -2
  89. package/dist/core/context.cjs.map +0 -1
  90. package/dist/core/context.d.ts +0 -10
  91. package/dist/core/context.d.ts.map +0 -1
  92. package/dist/core/context.js +0 -176
  93. package/dist/core/context.js.map +0 -1
  94. package/dist/core/lifecycle.cjs +0 -2
  95. package/dist/core/lifecycle.cjs.map +0 -1
  96. package/dist/core/lifecycle.d.ts +0 -13
  97. package/dist/core/lifecycle.d.ts.map +0 -1
  98. package/dist/core/lifecycle.js +0 -42
  99. package/dist/core/lifecycle.js.map +0 -1
  100. package/dist/core/observe.cjs +0 -2
  101. package/dist/core/observe.cjs.map +0 -1
  102. package/dist/core/observe.d.ts +0 -18
  103. package/dist/core/observe.d.ts.map +0 -1
  104. package/dist/core/observe.js +0 -90
  105. package/dist/core/observe.js.map +0 -1
  106. package/dist/core/scope.cjs +0 -2
  107. package/dist/core/scope.cjs.map +0 -1
  108. package/dist/core/scope.d.ts +0 -8
  109. package/dist/core/scope.d.ts.map +0 -1
  110. package/dist/core/scope.js +0 -286
  111. package/dist/core/scope.js.map +0 -1
  112. package/dist/core/validation.cjs +0 -2
  113. package/dist/core/validation.cjs.map +0 -1
  114. package/dist/core/validation.d.ts +0 -23
  115. package/dist/core/validation.d.ts.map +0 -1
  116. package/dist/core/validation.js +0 -173
  117. package/dist/core/validation.js.map +0 -1
  118. package/dist/core/values.cjs +0 -2
  119. package/dist/core/values.cjs.map +0 -1
  120. package/dist/core/values.d.ts +0 -31
  121. package/dist/core/values.d.ts.map +0 -1
  122. package/dist/core/values.js +0 -149
  123. package/dist/core/values.js.map +0 -1
  124. package/dist/types.cjs +0 -2
  125. package/dist/types.cjs.map +0 -1
  126. package/dist/types.js +0 -21
  127. package/dist/types.js.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"file":"_utils.cjs","names":[],"sources":["../src/_utils.ts"],"sourcesContent":["import { devOnly, warn } from './_dev';\nimport { ForgeConfigError } from './errors';\n\nexport { flattenPaths as flattenValues, isPlainObject, unflattenPaths as unflattenValues } from '@vielzeug/arsenal';\n\nconst UNSAFE = new Set(['__proto__', 'constructor', 'prototype']);\n\n/** Returns `true` when a dot-notation path contains no prototype-polluting segments. */\nexport function isSafeKey(path: string): boolean {\n return path.split('.').every((segment) => !UNSAFE.has(segment));\n}\n\n/** R2: Single assertion helper — replaces 5 copy-pasted guard blocks. */\nexport function assertSafeKey(key: string): void {\n warnIfDeepPathTypeFallback(key);\n\n if (!isSafeKey(key))\n throw new ForgeConfigError(`Unsafe key '${key}': segments __proto__, constructor, and prototype are reserved.`);\n}\n\nconst warnedDeepPaths = new Set<string>();\n\n/**\n * FlatKeyOf intentionally caps recursive path typing depth for TS performance.\n * Warn once per deep key in dev so callers notice when path type inference likely fell back to `string`.\n */\nfunction warnIfDeepPathTypeFallback(key: string): void {\n if (!key.includes('.')) return;\n\n const depth = key.split('.').length;\n\n if (depth <= 5 || warnedDeepPaths.has(key)) return;\n\n warnedDeepPaths.add(key);\n\n devOnly(() => {\n warn(`Path '${sanitizeForLog(key, 120)}' exceeds typed depth limits and may be treated as an untyped string path.`);\n });\n}\n\n/**\n * Strips control characters (which could otherwise inject terminal escape sequences into\n * `console.warn` output) and caps length before interpolating caller-controlled text into a\n * dev warning. Mirrors the sanitization already applied to field keys in `warnIfArrayItemKey`.\n */\nexport function sanitizeForLog(text: string, maxLength = 200): string {\n return text.replace(/\\p{C}/gu, '?').slice(0, maxLength);\n}\n\n/**\n * Merges multiple AbortSignals into one that aborts when any of them aborts.\n * Nullish values are silently ignored.\n * Returns `undefined` when no non-null signals are provided.\n *\n * @internal Inlined here — `anySignal` was removed from `@vielzeug/arsenal`.\n */\nexport function anySignal(...signals: ReadonlyArray<AbortSignal | null | undefined>): AbortSignal | undefined {\n const active = signals.filter((s): s is AbortSignal => s != null);\n\n if (active.length === 0) return undefined;\n\n if (active.length === 1) return active[0];\n\n return AbortSignal.any(active);\n}\n"],"mappings":"qFAKA,IAAM,EAAS,IAAI,IAAI,CAAC,YAAa,cAAe,WAAW,CAAC,EAGhE,SAAgB,EAAU,EAAuB,CAC/C,OAAO,EAAK,MAAM,GAAG,CAAC,CAAC,MAAO,GAAY,CAAC,EAAO,IAAI,CAAO,CAAC,CAChE,CAGA,SAAgB,EAAc,EAAmB,CAG/C,GAFA,EAA2B,CAAG,EAE1B,CAAC,EAAU,CAAG,EAChB,MAAM,IAAI,EAAA,iBAAiB,eAAe,EAAI,gEAAgE,CAClH,CAEA,IAAM,EAAkB,IAAI,IAM5B,SAAS,EAA2B,EAAmB,CAChD,EAAI,SAAS,GAAG,IAEP,EAAI,MAAM,GAAG,CAAC,CAAC,QAEhB,GAAK,EAAgB,IAAI,CAAG,IAEzC,EAAgB,IAAI,CAAG,EAEvB,EAAA,YAAc,CACZ,EAAA,KAAK,SAAS,EAAe,EAAK,GAAG,EAAE,2EAA2E,CACpH,CAAC,GACH,CAOA,SAAgB,EAAe,EAAc,EAAY,IAAa,CACpE,OAAO,EAAK,QAAQ,UAAW,GAAG,CAAC,CAAC,MAAM,EAAG,CAAS,CACxD,CASA,SAAgB,EAAU,GAAG,EAAiF,CAC5G,IAAM,EAAS,EAAQ,OAAQ,GAAwB,GAAK,IAAI,EAE5D,KAAO,SAAW,EAItB,OAFI,EAAO,SAAW,EAAU,EAAO,GAEhC,YAAY,IAAI,CAAM,CAC/B"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"_utils.js","names":[],"sources":["../src/_utils.ts"],"sourcesContent":["import { devOnly, warn } from './_dev';\nimport { ForgeConfigError } from './errors';\n\nexport { flattenPaths as flattenValues, isPlainObject, unflattenPaths as unflattenValues } from '@vielzeug/arsenal';\n\nconst UNSAFE = new Set(['__proto__', 'constructor', 'prototype']);\n\n/** Returns `true` when a dot-notation path contains no prototype-polluting segments. */\nexport function isSafeKey(path: string): boolean {\n return path.split('.').every((segment) => !UNSAFE.has(segment));\n}\n\n/** R2: Single assertion helper — replaces 5 copy-pasted guard blocks. */\nexport function assertSafeKey(key: string): void {\n warnIfDeepPathTypeFallback(key);\n\n if (!isSafeKey(key))\n throw new ForgeConfigError(`Unsafe key '${key}': segments __proto__, constructor, and prototype are reserved.`);\n}\n\nconst warnedDeepPaths = new Set<string>();\n\n/**\n * FlatKeyOf intentionally caps recursive path typing depth for TS performance.\n * Warn once per deep key in dev so callers notice when path type inference likely fell back to `string`.\n */\nfunction warnIfDeepPathTypeFallback(key: string): void {\n if (!key.includes('.')) return;\n\n const depth = key.split('.').length;\n\n if (depth <= 5 || warnedDeepPaths.has(key)) return;\n\n warnedDeepPaths.add(key);\n\n devOnly(() => {\n warn(`Path '${sanitizeForLog(key, 120)}' exceeds typed depth limits and may be treated as an untyped string path.`);\n });\n}\n\n/**\n * Strips control characters (which could otherwise inject terminal escape sequences into\n * `console.warn` output) and caps length before interpolating caller-controlled text into a\n * dev warning. Mirrors the sanitization already applied to field keys in `warnIfArrayItemKey`.\n */\nexport function sanitizeForLog(text: string, maxLength = 200): string {\n return text.replace(/\\p{C}/gu, '?').slice(0, maxLength);\n}\n\n/**\n * Merges multiple AbortSignals into one that aborts when any of them aborts.\n * Nullish values are silently ignored.\n * Returns `undefined` when no non-null signals are provided.\n *\n * @internal Inlined here — `anySignal` was removed from `@vielzeug/arsenal`.\n */\nexport function anySignal(...signals: ReadonlyArray<AbortSignal | null | undefined>): AbortSignal | undefined {\n const active = signals.filter((s): s is AbortSignal => s != null);\n\n if (active.length === 0) return undefined;\n\n if (active.length === 1) return active[0];\n\n return AbortSignal.any(active);\n}\n"],"mappings":";;;;AAKA,IAAM,oBAAS,IAAI,IAAI;CAAC;CAAa;CAAe;AAAW,CAAC;AAGhE,SAAgB,EAAU,GAAuB;CAC/C,OAAO,EAAK,MAAM,GAAG,CAAC,CAAC,OAAO,MAAY,CAAC,EAAO,IAAI,CAAO,CAAC;AAChE;AAGA,SAAgB,EAAc,GAAmB;CAG/C,IAFA,EAA2B,CAAG,GAE1B,CAAC,EAAU,CAAG,GAChB,MAAM,IAAI,EAAiB,eAAe,EAAI,gEAAgE;AAClH;AAEA,IAAM,oBAAkB,IAAI,IAAY;AAMxC,SAAS,EAA2B,GAAmB;CAChD,EAAI,SAAS,GAAG,MAEP,EAAI,MAAM,GAAG,CAAC,CAAC,UAEhB,KAAK,EAAgB,IAAI,CAAG,MAEzC,EAAgB,IAAI,CAAG,GAEvB,QAAc;EACZ,EAAK,SAAS,EAAe,GAAK,GAAG,EAAE,2EAA2E;CACpH,CAAC;AACH;AAOA,SAAgB,EAAe,GAAc,IAAY,KAAa;CACpE,OAAO,EAAK,QAAQ,WAAW,GAAG,CAAC,CAAC,MAAM,GAAG,CAAS;AACxD;AASA,SAAgB,EAAU,GAAG,GAAiF;CAC5G,IAAM,IAAS,EAAQ,QAAQ,MAAwB,KAAK,IAAI;CAE5D,MAAO,WAAW,GAItB,OAFI,EAAO,WAAW,IAAU,EAAO,KAEhC,YAAY,IAAI,CAAM;AAC/B"}
@@ -1,2 +0,0 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});function e(...e){return async(t,n)=>{for(let r of e){if(n?.aborted)return;let e=await r(t,n);if(e!==void 0)return e}}}function t(e){return t=>{let n=e.safeParse(t);if(!n.success)return n.error.issues[0]?.message}}exports.composeValidators=e,exports.fieldValidator=t;
2
- //# sourceMappingURL=validators.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"validators.cjs","names":[],"sources":["../../src/adapters/validators.ts"],"sourcesContent":["import type { FieldValidator, SafeParseSchema } from '../types';\n\n/**\n * Chains multiple field validators, running them in order and returning the first error message.\n * Validators after the first error are skipped (short-circuit). Signal aborts are respected between steps.\n *\n * Works naturally with {@link fieldValidator} — mix schema-based and custom validators in one chain:\n *\n * @example\n * import { s } from '@vielzeug/spell';\n * import { composeValidators, fieldValidator } from '@vielzeug/forge/validators';\n *\n * const passwordValidator = composeValidators(\n * fieldValidator(s.string().min(8, 'At least 8 characters')),\n * async (value, signal) => {\n * const isCompromised = await checkBreachedPasswords(value, signal);\n * return isCompromised ? 'Password found in breach database' : undefined;\n * },\n * );\n *\n * const form = createForm({\n * validators: { password: passwordValidator },\n * });\n */\nexport function composeValidators<V = unknown>(...validators: FieldValidator<V>[]): FieldValidator<V> {\n return async (value, signal) => {\n for (const validator of validators) {\n if (signal?.aborted) return undefined;\n\n const result = await validator(value, signal);\n\n if (result !== undefined) return result;\n }\n\n return undefined;\n };\n}\n\n/**\n * Wraps a `safeParse`-compatible field schema into a `FieldValidator`.\n * The first validation error message becomes the field error.\n *\n * Works with `@vielzeug/spell`, Zod, Valibot, and any Standard Schema compliant library —\n * no hard dependency required; resolved structurally via `SafeParseSchema`.\n *\n * @example\n * import { s } from '@vielzeug/spell';\n * import { fieldValidator } from '@vielzeug/forge/validators';\n *\n * const form = createForm({\n * validators: {\n * email: fieldValidator(s.string().email('Invalid email address')),\n * age: fieldValidator(s.number().min(18, 'Must be 18 or older')),\n * },\n * });\n */\nexport function fieldValidator<V = unknown>(schema: SafeParseSchema): FieldValidator<V> {\n return (value) => {\n const result = schema.safeParse(value);\n\n if (result.success) return undefined;\n\n return result.error.issues[0]?.message;\n };\n}\n"],"mappings":"mEAwBA,SAAgB,EAA+B,GAAG,EAAoD,CACpG,OAAO,MAAO,EAAO,IAAW,CAC9B,IAAK,IAAM,KAAa,EAAY,CAClC,GAAI,GAAQ,QAAS,OAErB,IAAM,EAAS,MAAM,EAAU,EAAO,CAAM,EAE5C,GAAI,IAAW,IAAA,GAAW,OAAO,CACnC,CAGF,CACF,CAoBA,SAAgB,EAA4B,EAA4C,CACtF,MAAQ,IAAU,CAChB,IAAM,EAAS,EAAO,UAAU,CAAK,EAEjC,MAAO,QAEX,OAAO,EAAO,MAAM,OAAO,EAAE,EAAE,OACjC,CACF"}
@@ -1,44 +0,0 @@
1
- import type { FieldValidator, SafeParseSchema } from '../types';
2
- /**
3
- * Chains multiple field validators, running them in order and returning the first error message.
4
- * Validators after the first error are skipped (short-circuit). Signal aborts are respected between steps.
5
- *
6
- * Works naturally with {@link fieldValidator} — mix schema-based and custom validators in one chain:
7
- *
8
- * @example
9
- * import { s } from '@vielzeug/spell';
10
- * import { composeValidators, fieldValidator } from '@vielzeug/forge/validators';
11
- *
12
- * const passwordValidator = composeValidators(
13
- * fieldValidator(s.string().min(8, 'At least 8 characters')),
14
- * async (value, signal) => {
15
- * const isCompromised = await checkBreachedPasswords(value, signal);
16
- * return isCompromised ? 'Password found in breach database' : undefined;
17
- * },
18
- * );
19
- *
20
- * const form = createForm({
21
- * validators: { password: passwordValidator },
22
- * });
23
- */
24
- export declare function composeValidators<V = unknown>(...validators: FieldValidator<V>[]): FieldValidator<V>;
25
- /**
26
- * Wraps a `safeParse`-compatible field schema into a `FieldValidator`.
27
- * The first validation error message becomes the field error.
28
- *
29
- * Works with `@vielzeug/spell`, Zod, Valibot, and any Standard Schema compliant library —
30
- * no hard dependency required; resolved structurally via `SafeParseSchema`.
31
- *
32
- * @example
33
- * import { s } from '@vielzeug/spell';
34
- * import { fieldValidator } from '@vielzeug/forge/validators';
35
- *
36
- * const form = createForm({
37
- * validators: {
38
- * email: fieldValidator(s.string().email('Invalid email address')),
39
- * age: fieldValidator(s.number().min(18, 'Must be 18 or older')),
40
- * },
41
- * });
42
- */
43
- export declare function fieldValidator<V = unknown>(schema: SafeParseSchema): FieldValidator<V>;
44
- //# sourceMappingURL=validators.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"validators.d.ts","sourceRoot":"","sources":["../../src/adapters/validators.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAEhE;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,UAAU,EAAE,cAAc,CAAC,CAAC,CAAC,EAAE,GAAG,cAAc,CAAC,CAAC,CAAC,CAYpG;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,cAAc,CAAC,CAAC,GAAG,OAAO,EAAE,MAAM,EAAE,eAAe,GAAG,cAAc,CAAC,CAAC,CAAC,CAQtF"}
@@ -1,20 +0,0 @@
1
- //#region src/adapters/validators.ts
2
- function e(...e) {
3
- return async (t, n) => {
4
- for (let r of e) {
5
- if (n?.aborted) return;
6
- let e = await r(t, n);
7
- if (e !== void 0) return e;
8
- }
9
- };
10
- }
11
- function t(e) {
12
- return (t) => {
13
- let n = e.safeParse(t);
14
- if (!n.success) return n.error.issues[0]?.message;
15
- };
16
- }
17
- //#endregion
18
- export { e as composeValidators, t as fieldValidator };
19
-
20
- //# sourceMappingURL=validators.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"validators.js","names":[],"sources":["../../src/adapters/validators.ts"],"sourcesContent":["import type { FieldValidator, SafeParseSchema } from '../types';\n\n/**\n * Chains multiple field validators, running them in order and returning the first error message.\n * Validators after the first error are skipped (short-circuit). Signal aborts are respected between steps.\n *\n * Works naturally with {@link fieldValidator} — mix schema-based and custom validators in one chain:\n *\n * @example\n * import { s } from '@vielzeug/spell';\n * import { composeValidators, fieldValidator } from '@vielzeug/forge/validators';\n *\n * const passwordValidator = composeValidators(\n * fieldValidator(s.string().min(8, 'At least 8 characters')),\n * async (value, signal) => {\n * const isCompromised = await checkBreachedPasswords(value, signal);\n * return isCompromised ? 'Password found in breach database' : undefined;\n * },\n * );\n *\n * const form = createForm({\n * validators: { password: passwordValidator },\n * });\n */\nexport function composeValidators<V = unknown>(...validators: FieldValidator<V>[]): FieldValidator<V> {\n return async (value, signal) => {\n for (const validator of validators) {\n if (signal?.aborted) return undefined;\n\n const result = await validator(value, signal);\n\n if (result !== undefined) return result;\n }\n\n return undefined;\n };\n}\n\n/**\n * Wraps a `safeParse`-compatible field schema into a `FieldValidator`.\n * The first validation error message becomes the field error.\n *\n * Works with `@vielzeug/spell`, Zod, Valibot, and any Standard Schema compliant library —\n * no hard dependency required; resolved structurally via `SafeParseSchema`.\n *\n * @example\n * import { s } from '@vielzeug/spell';\n * import { fieldValidator } from '@vielzeug/forge/validators';\n *\n * const form = createForm({\n * validators: {\n * email: fieldValidator(s.string().email('Invalid email address')),\n * age: fieldValidator(s.number().min(18, 'Must be 18 or older')),\n * },\n * });\n */\nexport function fieldValidator<V = unknown>(schema: SafeParseSchema): FieldValidator<V> {\n return (value) => {\n const result = schema.safeParse(value);\n\n if (result.success) return undefined;\n\n return result.error.issues[0]?.message;\n };\n}\n"],"mappings":";AAwBA,SAAgB,EAA+B,GAAG,GAAoD;CACpG,OAAO,OAAO,GAAO,MAAW;EAC9B,KAAK,IAAM,KAAa,GAAY;GAClC,IAAI,GAAQ,SAAS;GAErB,IAAM,IAAS,MAAM,EAAU,GAAO,CAAM;GAE5C,IAAI,MAAW,KAAA,GAAW,OAAO;EACnC;CAGF;AACF;AAoBA,SAAgB,EAA4B,GAA4C;CACtF,QAAQ,MAAU;EAChB,IAAM,IAAS,EAAO,UAAU,CAAK;EAEjC,OAAO,SAEX,OAAO,EAAO,MAAM,OAAO,EAAE,EAAE;CACjC;AACF"}
@@ -1,2 +0,0 @@
1
- function e(e,t,n){let r=e;return{append(i){let a=t.get(r);a!==void 0&&!Array.isArray(a)||n(e,Array.isArray(a)?[...a,i]:[i])},insert(i,a){let o=t.get(r);if(!Array.isArray(o))return;let s=[...o];s.splice(i,0,a),n(e,s)},move(i,a){let o=t.get(r);if(!Array.isArray(o))return;let s=[...o];s.splice(a,0,s.splice(i,1)[0]),n(e,s)},prepend(i){let a=t.get(r);a!==void 0&&!Array.isArray(a)||n(e,Array.isArray(a)?[i,...a]:[i])},remove(i){let a=t.get(r);Array.isArray(a)&&n(e,a.filter((e,t)=>t!==i))},replace(i,a){let o=t.get(r);if(!Array.isArray(o))return;let s=[...o];s[i]=a,n(e,s)},swap(i,a){let o=t.get(r);if(!Array.isArray(o))return;let s=[...o];[s[i],s[a]]=[s[a],s[i]],n(e,s)}}}exports.createArrayField=e;
2
- //# sourceMappingURL=array.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"array.cjs","names":[],"sources":["../../src/core/array.ts"],"sourcesContent":["import type { ArrayField, FlatKeyOf, TypeAtPath } from '../types';\n\n// Resolves the element type of an array field — mirrored from types.ts.\ntype ElementOf<T> = T extends readonly (infer E)[] ? E : unknown;\n\n/**\n * Builds a cached typed ArrayField helper for a specific field name.\n * The helper closes over `store` and `set` from the parent form closure.\n * R4: Return type is now `ArrayField<ElementOf<V>>` so callers get type-safe append/insert/etc.\n */\nexport function createArrayField<TValues extends Record<string, unknown>, K extends FlatKeyOf<TValues>>(\n name: K,\n store: Map<string, unknown>,\n set: (name: string, value: unknown) => void,\n): ArrayField<ElementOf<TypeAtPath<TValues, K>>> {\n type T = ElementOf<TypeAtPath<TValues, K>>;\n\n const key = name as string;\n\n return {\n append(value: T) {\n const current = store.get(key);\n\n if (current !== undefined && !Array.isArray(current)) return;\n\n set(name, (Array.isArray(current) ? [...current, value] : [value]) as TypeAtPath<TValues, K>);\n },\n insert(index: number, value: T) {\n const current = store.get(key);\n\n if (!Array.isArray(current)) return;\n\n const next = [...current];\n\n next.splice(index, 0, value);\n set(name, next as TypeAtPath<TValues, K>);\n },\n move(from: number, to: number) {\n const current = store.get(key);\n\n if (!Array.isArray(current)) return;\n\n const next = [...current];\n\n next.splice(to, 0, next.splice(from, 1)[0]);\n set(name, next as TypeAtPath<TValues, K>);\n },\n prepend(value: T) {\n const current = store.get(key);\n\n if (current !== undefined && !Array.isArray(current)) return;\n\n set(name, (Array.isArray(current) ? [value, ...current] : [value]) as TypeAtPath<TValues, K>);\n },\n remove(index: number) {\n const current = store.get(key);\n\n if (!Array.isArray(current)) return;\n\n set(name, current.filter((_, i) => i !== index) as TypeAtPath<TValues, K>);\n },\n replace(index: number, value: T) {\n const current = store.get(key);\n\n if (!Array.isArray(current)) return;\n\n const next = [...current];\n\n next[index] = value;\n set(name, next as TypeAtPath<TValues, K>);\n },\n swap(a: number, b: number) {\n const current = store.get(key);\n\n if (!Array.isArray(current)) return;\n\n const next = [...current];\n\n [next[a], next[b]] = [next[b], next[a]];\n set(name, next as TypeAtPath<TValues, K>);\n },\n };\n}\n"],"mappings":"AAUA,SAAgB,EACd,EACA,EACA,EAC+C,CAG/C,IAAM,EAAM,EAEZ,MAAO,CACL,OAAO,EAAU,CACf,IAAM,EAAU,EAAM,IAAI,CAAG,EAEzB,IAAY,IAAA,IAAa,CAAC,MAAM,QAAQ,CAAO,GAEnD,EAAI,EAAO,MAAM,QAAQ,CAAO,EAAI,CAAC,GAAG,EAAS,CAAK,EAAI,CAAC,CAAK,CAA4B,CAC9F,EACA,OAAO,EAAe,EAAU,CAC9B,IAAM,EAAU,EAAM,IAAI,CAAG,EAE7B,GAAI,CAAC,MAAM,QAAQ,CAAO,EAAG,OAE7B,IAAM,EAAO,CAAC,GAAG,CAAO,EAExB,EAAK,OAAO,EAAO,EAAG,CAAK,EAC3B,EAAI,EAAM,CAA8B,CAC1C,EACA,KAAK,EAAc,EAAY,CAC7B,IAAM,EAAU,EAAM,IAAI,CAAG,EAE7B,GAAI,CAAC,MAAM,QAAQ,CAAO,EAAG,OAE7B,IAAM,EAAO,CAAC,GAAG,CAAO,EAExB,EAAK,OAAO,EAAI,EAAG,EAAK,OAAO,EAAM,CAAC,CAAC,CAAC,EAAE,EAC1C,EAAI,EAAM,CAA8B,CAC1C,EACA,QAAQ,EAAU,CAChB,IAAM,EAAU,EAAM,IAAI,CAAG,EAEzB,IAAY,IAAA,IAAa,CAAC,MAAM,QAAQ,CAAO,GAEnD,EAAI,EAAO,MAAM,QAAQ,CAAO,EAAI,CAAC,EAAO,GAAG,CAAO,EAAI,CAAC,CAAK,CAA4B,CAC9F,EACA,OAAO,EAAe,CACpB,IAAM,EAAU,EAAM,IAAI,CAAG,EAExB,MAAM,QAAQ,CAAO,GAE1B,EAAI,EAAM,EAAQ,QAAQ,EAAG,IAAM,IAAM,CAAK,CAA2B,CAC3E,EACA,QAAQ,EAAe,EAAU,CAC/B,IAAM,EAAU,EAAM,IAAI,CAAG,EAE7B,GAAI,CAAC,MAAM,QAAQ,CAAO,EAAG,OAE7B,IAAM,EAAO,CAAC,GAAG,CAAO,EAExB,EAAK,GAAS,EACd,EAAI,EAAM,CAA8B,CAC1C,EACA,KAAK,EAAW,EAAW,CACzB,IAAM,EAAU,EAAM,IAAI,CAAG,EAE7B,GAAI,CAAC,MAAM,QAAQ,CAAO,EAAG,OAE7B,IAAM,EAAO,CAAC,GAAG,CAAO,EAExB,CAAC,EAAK,GAAI,EAAK,IAAM,CAAC,EAAK,GAAI,EAAK,EAAE,EACtC,EAAI,EAAM,CAA8B,CAC1C,CACF,CACF"}
@@ -1,10 +0,0 @@
1
- import type { ArrayField, FlatKeyOf, TypeAtPath } from '../types';
2
- type ElementOf<T> = T extends readonly (infer E)[] ? E : unknown;
3
- /**
4
- * Builds a cached typed ArrayField helper for a specific field name.
5
- * The helper closes over `store` and `set` from the parent form closure.
6
- * R4: Return type is now `ArrayField<ElementOf<V>>` so callers get type-safe append/insert/etc.
7
- */
8
- export declare function createArrayField<TValues extends Record<string, unknown>, K extends FlatKeyOf<TValues>>(name: K, store: Map<string, unknown>, set: (name: string, value: unknown) => void): ArrayField<ElementOf<TypeAtPath<TValues, K>>>;
9
- export {};
10
- //# sourceMappingURL=array.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"array.d.ts","sourceRoot":"","sources":["../../src/core/array.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAGlE,KAAK,SAAS,CAAC,CAAC,IAAI,CAAC,SAAS,SAAS,CAAC,MAAM,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,OAAO,CAAC;AAEjE;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,SAAS,SAAS,CAAC,OAAO,CAAC,EACpG,IAAI,EAAE,CAAC,EACP,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,EAC3B,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,GAC1C,UAAU,CAAC,SAAS,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,CAoE/C"}
@@ -1,46 +0,0 @@
1
- //#region src/core/array.ts
2
- function e(e, t, n) {
3
- let r = e;
4
- return {
5
- append(i) {
6
- let a = t.get(r);
7
- a !== void 0 && !Array.isArray(a) || n(e, Array.isArray(a) ? [...a, i] : [i]);
8
- },
9
- insert(i, a) {
10
- let o = t.get(r);
11
- if (!Array.isArray(o)) return;
12
- let s = [...o];
13
- s.splice(i, 0, a), n(e, s);
14
- },
15
- move(i, a) {
16
- let o = t.get(r);
17
- if (!Array.isArray(o)) return;
18
- let s = [...o];
19
- s.splice(a, 0, s.splice(i, 1)[0]), n(e, s);
20
- },
21
- prepend(i) {
22
- let a = t.get(r);
23
- a !== void 0 && !Array.isArray(a) || n(e, Array.isArray(a) ? [i, ...a] : [i]);
24
- },
25
- remove(i) {
26
- let a = t.get(r);
27
- Array.isArray(a) && n(e, a.filter((e, t) => t !== i));
28
- },
29
- replace(i, a) {
30
- let o = t.get(r);
31
- if (!Array.isArray(o)) return;
32
- let s = [...o];
33
- s[i] = a, n(e, s);
34
- },
35
- swap(i, a) {
36
- let o = t.get(r);
37
- if (!Array.isArray(o)) return;
38
- let s = [...o];
39
- [s[i], s[a]] = [s[a], s[i]], n(e, s);
40
- }
41
- };
42
- }
43
- //#endregion
44
- export { e as createArrayField };
45
-
46
- //# sourceMappingURL=array.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"array.js","names":[],"sources":["../../src/core/array.ts"],"sourcesContent":["import type { ArrayField, FlatKeyOf, TypeAtPath } from '../types';\n\n// Resolves the element type of an array field — mirrored from types.ts.\ntype ElementOf<T> = T extends readonly (infer E)[] ? E : unknown;\n\n/**\n * Builds a cached typed ArrayField helper for a specific field name.\n * The helper closes over `store` and `set` from the parent form closure.\n * R4: Return type is now `ArrayField<ElementOf<V>>` so callers get type-safe append/insert/etc.\n */\nexport function createArrayField<TValues extends Record<string, unknown>, K extends FlatKeyOf<TValues>>(\n name: K,\n store: Map<string, unknown>,\n set: (name: string, value: unknown) => void,\n): ArrayField<ElementOf<TypeAtPath<TValues, K>>> {\n type T = ElementOf<TypeAtPath<TValues, K>>;\n\n const key = name as string;\n\n return {\n append(value: T) {\n const current = store.get(key);\n\n if (current !== undefined && !Array.isArray(current)) return;\n\n set(name, (Array.isArray(current) ? [...current, value] : [value]) as TypeAtPath<TValues, K>);\n },\n insert(index: number, value: T) {\n const current = store.get(key);\n\n if (!Array.isArray(current)) return;\n\n const next = [...current];\n\n next.splice(index, 0, value);\n set(name, next as TypeAtPath<TValues, K>);\n },\n move(from: number, to: number) {\n const current = store.get(key);\n\n if (!Array.isArray(current)) return;\n\n const next = [...current];\n\n next.splice(to, 0, next.splice(from, 1)[0]);\n set(name, next as TypeAtPath<TValues, K>);\n },\n prepend(value: T) {\n const current = store.get(key);\n\n if (current !== undefined && !Array.isArray(current)) return;\n\n set(name, (Array.isArray(current) ? [value, ...current] : [value]) as TypeAtPath<TValues, K>);\n },\n remove(index: number) {\n const current = store.get(key);\n\n if (!Array.isArray(current)) return;\n\n set(name, current.filter((_, i) => i !== index) as TypeAtPath<TValues, K>);\n },\n replace(index: number, value: T) {\n const current = store.get(key);\n\n if (!Array.isArray(current)) return;\n\n const next = [...current];\n\n next[index] = value;\n set(name, next as TypeAtPath<TValues, K>);\n },\n swap(a: number, b: number) {\n const current = store.get(key);\n\n if (!Array.isArray(current)) return;\n\n const next = [...current];\n\n [next[a], next[b]] = [next[b], next[a]];\n set(name, next as TypeAtPath<TValues, K>);\n },\n };\n}\n"],"mappings":";AAUA,SAAgB,EACd,GACA,GACA,GAC+C;CAG/C,IAAM,IAAM;CAEZ,OAAO;EACL,OAAO,GAAU;GACf,IAAM,IAAU,EAAM,IAAI,CAAG;GAEzB,MAAY,KAAA,KAAa,CAAC,MAAM,QAAQ,CAAO,KAEnD,EAAI,GAAO,MAAM,QAAQ,CAAO,IAAI,CAAC,GAAG,GAAS,CAAK,IAAI,CAAC,CAAK,CAA4B;EAC9F;EACA,OAAO,GAAe,GAAU;GAC9B,IAAM,IAAU,EAAM,IAAI,CAAG;GAE7B,IAAI,CAAC,MAAM,QAAQ,CAAO,GAAG;GAE7B,IAAM,IAAO,CAAC,GAAG,CAAO;GAGxB,AADA,EAAK,OAAO,GAAO,GAAG,CAAK,GAC3B,EAAI,GAAM,CAA8B;EAC1C;EACA,KAAK,GAAc,GAAY;GAC7B,IAAM,IAAU,EAAM,IAAI,CAAG;GAE7B,IAAI,CAAC,MAAM,QAAQ,CAAO,GAAG;GAE7B,IAAM,IAAO,CAAC,GAAG,CAAO;GAGxB,AADA,EAAK,OAAO,GAAI,GAAG,EAAK,OAAO,GAAM,CAAC,CAAC,CAAC,EAAE,GAC1C,EAAI,GAAM,CAA8B;EAC1C;EACA,QAAQ,GAAU;GAChB,IAAM,IAAU,EAAM,IAAI,CAAG;GAEzB,MAAY,KAAA,KAAa,CAAC,MAAM,QAAQ,CAAO,KAEnD,EAAI,GAAO,MAAM,QAAQ,CAAO,IAAI,CAAC,GAAO,GAAG,CAAO,IAAI,CAAC,CAAK,CAA4B;EAC9F;EACA,OAAO,GAAe;GACpB,IAAM,IAAU,EAAM,IAAI,CAAG;GAExB,MAAM,QAAQ,CAAO,KAE1B,EAAI,GAAM,EAAQ,QAAQ,GAAG,MAAM,MAAM,CAAK,CAA2B;EAC3E;EACA,QAAQ,GAAe,GAAU;GAC/B,IAAM,IAAU,EAAM,IAAI,CAAG;GAE7B,IAAI,CAAC,MAAM,QAAQ,CAAO,GAAG;GAE7B,IAAM,IAAO,CAAC,GAAG,CAAO;GAGxB,AADA,EAAK,KAAS,GACd,EAAI,GAAM,CAA8B;EAC1C;EACA,KAAK,GAAW,GAAW;GACzB,IAAM,IAAU,EAAM,IAAI,CAAG;GAE7B,IAAI,CAAC,MAAM,QAAQ,CAAO,GAAG;GAE7B,IAAM,IAAO,CAAC,GAAG,CAAO;GAGxB,AADA,CAAC,EAAK,IAAI,EAAK,MAAM,CAAC,EAAK,IAAI,EAAK,EAAE,GACtC,EAAI,GAAM,CAA8B;EAC1C;CACF;AACF"}
@@ -1,2 +0,0 @@
1
- function e(e){let t=[],n=null,r=null,i=!1,a=null;function o(e){if(!i)if(n){let t=n;n=null,r=null,t({done:!1,value:e})}else t.push(e)}function s(e){if(!i)if(i=!0,r){let t=r;n=null,r=null,t(e)}else a={err:e}}function c(){if(!i&&(i=!0,n)){let e=n;n=null,r=null,e({done:!0,value:void 0})}}function l(){i||(t=[],c(),e?.())}return{fail:s,finish:c,iterator:{next(){if(a){let{err:e}=a;return a=null,Promise.reject(e)}return t.length>0?Promise.resolve({done:!1,value:t.shift()}):i?Promise.resolve({done:!0,value:void 0}):new Promise((e,t)=>{n=e,r=t})},return(){return l(),Promise.resolve({done:!0,value:void 0})},[Symbol.asyncIterator](){return this}},push:o,terminate:l}}exports.createAsyncQueue=e;
2
- //# sourceMappingURL=asyncQueue.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"asyncQueue.cjs","names":[],"sources":["../../src/core/asyncQueue.ts"],"sourcesContent":["/**\n * A minimal push-based async queue exposing an `AsyncIterableIterator`.\n * Shared by `validateStream()` and `form[Symbol.asyncIterator]()`, which otherwise hand-roll\n * near-identical `queue`/`waitingResolve`/`done` plumbing.\n *\n * - `push()` delivers an item to a waiting `next()` call, or buffers it for the next one.\n * - `finish()` marks the queue complete; any items already buffered are still drained by\n * subsequent `next()` calls before `{ done: true }` is returned.\n * - `fail()` marks the queue complete and rejects the next `next()` call with `err`.\n * - Calling `iterator.return()` (e.g. via `for-await...of` `break`) hard-terminates the queue:\n * any buffered-but-undelivered items are discarded and `onTerminate` fires.\n */\nexport type AsyncQueue<T> = {\n fail(err: unknown): void;\n finish(): void;\n iterator: AsyncIterableIterator<T>;\n push(item: T): void;\n /** Hard-terminates the queue: drops any buffered items, marks it done, and fires `onTerminate`. */\n terminate(): void;\n};\n\nexport function createAsyncQueue<T>(onTerminate?: () => void): AsyncQueue<T> {\n type Resolver = (result: IteratorResult<T, undefined>) => void;\n type Rejecter = (err: unknown) => void;\n\n let buffer: T[] = [];\n let waitingResolve: Resolver | null = null;\n let waitingReject: Rejecter | null = null;\n let done = false;\n let pendingError: { err: unknown } | null = null;\n\n function push(item: T): void {\n if (done) return;\n\n if (waitingResolve) {\n const resolve = waitingResolve;\n\n waitingResolve = null;\n waitingReject = null;\n resolve({ done: false, value: item });\n } else {\n buffer.push(item);\n }\n }\n\n function fail(err: unknown): void {\n if (done) return;\n\n done = true;\n\n if (waitingReject) {\n const reject = waitingReject;\n\n waitingResolve = null;\n waitingReject = null;\n reject(err);\n } else {\n pendingError = { err };\n }\n }\n\n function finish(): void {\n if (done) return;\n\n done = true;\n\n if (waitingResolve) {\n const resolve = waitingResolve;\n\n waitingResolve = null;\n waitingReject = null;\n resolve({ done: true, value: undefined });\n }\n }\n\n function terminate(): void {\n if (done) return;\n\n buffer = [];\n finish();\n onTerminate?.();\n }\n\n const iterator: AsyncIterableIterator<T> = {\n next(): Promise<IteratorResult<T, undefined>> {\n if (pendingError) {\n const { err } = pendingError;\n\n pendingError = null;\n\n return Promise.reject(err);\n }\n\n if (buffer.length > 0) return Promise.resolve({ done: false, value: buffer.shift()! });\n\n if (done) return Promise.resolve({ done: true, value: undefined });\n\n return new Promise<IteratorResult<T, undefined>>((resolve, reject) => {\n waitingResolve = resolve;\n waitingReject = reject;\n });\n },\n\n return(): Promise<IteratorResult<T, undefined>> {\n terminate();\n\n return Promise.resolve({ done: true, value: undefined });\n },\n\n [Symbol.asyncIterator]() {\n return this;\n },\n };\n\n return { fail, finish, iterator, push, terminate };\n}\n"],"mappings":"AAqBA,SAAgB,EAAoB,EAAyC,CAI3E,IAAI,EAAc,CAAC,EACf,EAAkC,KAClC,EAAiC,KACjC,EAAO,GACP,EAAwC,KAE5C,SAAS,EAAK,EAAe,CACvB,MAEJ,GAAI,EAAgB,CAClB,IAAM,EAAU,EAEhB,EAAiB,KACjB,EAAgB,KAChB,EAAQ,CAAE,KAAM,GAAO,MAAO,CAAK,CAAC,CACtC,MACE,EAAO,KAAK,CAAI,CAEpB,CAEA,SAAS,EAAK,EAAoB,CAC5B,MAIJ,GAFA,EAAO,GAEH,EAAe,CACjB,IAAM,EAAS,EAEf,EAAiB,KACjB,EAAgB,KAChB,EAAO,CAAG,CACZ,KACE,GAAe,CAAE,KAAI,CAEzB,CAEA,SAAS,GAAe,CAClB,QAEJ,EAAO,GAEH,GAAgB,CAClB,IAAM,EAAU,EAEhB,EAAiB,KACjB,EAAgB,KAChB,EAAQ,CAAE,KAAM,GAAM,MAAO,IAAA,EAAU,CAAC,CAC1C,CACF,CAEA,SAAS,GAAkB,CACrB,IAEJ,EAAS,CAAC,EACV,EAAO,EACP,IAAc,EAChB,CAiCA,MAAO,CAAE,OAAM,SAAQ,SAAA,CA9BrB,MAA8C,CAC5C,GAAI,EAAc,CAChB,GAAM,CAAE,OAAQ,EAIhB,MAFA,GAAe,KAER,QAAQ,OAAO,CAAG,CAC3B,CAMA,OAJI,EAAO,OAAS,EAAU,QAAQ,QAAQ,CAAE,KAAM,GAAO,MAAO,EAAO,MAAM,CAAG,CAAC,EAEjF,EAAa,QAAQ,QAAQ,CAAE,KAAM,GAAM,MAAO,IAAA,EAAU,CAAC,EAE1D,IAAI,SAAuC,EAAS,IAAW,CACpE,EAAiB,EACjB,EAAgB,CAClB,CAAC,CACH,EAEA,QAAgD,CAG9C,OAFA,EAAU,EAEH,QAAQ,QAAQ,CAAE,KAAM,GAAM,MAAO,IAAA,EAAU,CAAC,CACzD,EAEA,CAAC,OAAO,gBAAiB,CACvB,OAAO,IACT,CAGqB,EAAU,OAAM,WAAU,CACnD"}
@@ -1,22 +0,0 @@
1
- /**
2
- * A minimal push-based async queue exposing an `AsyncIterableIterator`.
3
- * Shared by `validateStream()` and `form[Symbol.asyncIterator]()`, which otherwise hand-roll
4
- * near-identical `queue`/`waitingResolve`/`done` plumbing.
5
- *
6
- * - `push()` delivers an item to a waiting `next()` call, or buffers it for the next one.
7
- * - `finish()` marks the queue complete; any items already buffered are still drained by
8
- * subsequent `next()` calls before `{ done: true }` is returned.
9
- * - `fail()` marks the queue complete and rejects the next `next()` call with `err`.
10
- * - Calling `iterator.return()` (e.g. via `for-await...of` `break`) hard-terminates the queue:
11
- * any buffered-but-undelivered items are discarded and `onTerminate` fires.
12
- */
13
- export type AsyncQueue<T> = {
14
- fail(err: unknown): void;
15
- finish(): void;
16
- iterator: AsyncIterableIterator<T>;
17
- push(item: T): void;
18
- /** Hard-terminates the queue: drops any buffered items, marks it done, and fires `onTerminate`. */
19
- terminate(): void;
20
- };
21
- export declare function createAsyncQueue<T>(onTerminate?: () => void): AsyncQueue<T>;
22
- //# sourceMappingURL=asyncQueue.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"asyncQueue.d.ts","sourceRoot":"","sources":["../../src/core/asyncQueue.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,UAAU,CAAC,CAAC,IAAI;IAC1B,IAAI,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAAC;IACzB,MAAM,IAAI,IAAI,CAAC;IACf,QAAQ,EAAE,qBAAqB,CAAC,CAAC,CAAC,CAAC;IACnC,IAAI,CAAC,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC;IACpB,mGAAmG;IACnG,SAAS,IAAI,IAAI,CAAC;CACnB,CAAC;AAEF,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,WAAW,CAAC,EAAE,MAAM,IAAI,GAAG,UAAU,CAAC,CAAC,CAAC,CA8F3E"}
@@ -1,67 +0,0 @@
1
- //#region src/core/asyncQueue.ts
2
- function e(e) {
3
- let t = [], n = null, r = null, i = !1, a = null;
4
- function o(e) {
5
- if (!i) if (n) {
6
- let t = n;
7
- n = null, r = null, t({
8
- done: !1,
9
- value: e
10
- });
11
- } else t.push(e);
12
- }
13
- function s(e) {
14
- if (!i) if (i = !0, r) {
15
- let t = r;
16
- n = null, r = null, t(e);
17
- } else a = { err: e };
18
- }
19
- function c() {
20
- if (!i && (i = !0, n)) {
21
- let e = n;
22
- n = null, r = null, e({
23
- done: !0,
24
- value: void 0
25
- });
26
- }
27
- }
28
- function l() {
29
- i || (t = [], c(), e?.());
30
- }
31
- return {
32
- fail: s,
33
- finish: c,
34
- iterator: {
35
- next() {
36
- if (a) {
37
- let { err: e } = a;
38
- return a = null, Promise.reject(e);
39
- }
40
- return t.length > 0 ? Promise.resolve({
41
- done: !1,
42
- value: t.shift()
43
- }) : i ? Promise.resolve({
44
- done: !0,
45
- value: void 0
46
- }) : new Promise((e, t) => {
47
- n = e, r = t;
48
- });
49
- },
50
- return() {
51
- return l(), Promise.resolve({
52
- done: !0,
53
- value: void 0
54
- });
55
- },
56
- [Symbol.asyncIterator]() {
57
- return this;
58
- }
59
- },
60
- push: o,
61
- terminate: l
62
- };
63
- }
64
- //#endregion
65
- export { e as createAsyncQueue };
66
-
67
- //# sourceMappingURL=asyncQueue.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"asyncQueue.js","names":[],"sources":["../../src/core/asyncQueue.ts"],"sourcesContent":["/**\n * A minimal push-based async queue exposing an `AsyncIterableIterator`.\n * Shared by `validateStream()` and `form[Symbol.asyncIterator]()`, which otherwise hand-roll\n * near-identical `queue`/`waitingResolve`/`done` plumbing.\n *\n * - `push()` delivers an item to a waiting `next()` call, or buffers it for the next one.\n * - `finish()` marks the queue complete; any items already buffered are still drained by\n * subsequent `next()` calls before `{ done: true }` is returned.\n * - `fail()` marks the queue complete and rejects the next `next()` call with `err`.\n * - Calling `iterator.return()` (e.g. via `for-await...of` `break`) hard-terminates the queue:\n * any buffered-but-undelivered items are discarded and `onTerminate` fires.\n */\nexport type AsyncQueue<T> = {\n fail(err: unknown): void;\n finish(): void;\n iterator: AsyncIterableIterator<T>;\n push(item: T): void;\n /** Hard-terminates the queue: drops any buffered items, marks it done, and fires `onTerminate`. */\n terminate(): void;\n};\n\nexport function createAsyncQueue<T>(onTerminate?: () => void): AsyncQueue<T> {\n type Resolver = (result: IteratorResult<T, undefined>) => void;\n type Rejecter = (err: unknown) => void;\n\n let buffer: T[] = [];\n let waitingResolve: Resolver | null = null;\n let waitingReject: Rejecter | null = null;\n let done = false;\n let pendingError: { err: unknown } | null = null;\n\n function push(item: T): void {\n if (done) return;\n\n if (waitingResolve) {\n const resolve = waitingResolve;\n\n waitingResolve = null;\n waitingReject = null;\n resolve({ done: false, value: item });\n } else {\n buffer.push(item);\n }\n }\n\n function fail(err: unknown): void {\n if (done) return;\n\n done = true;\n\n if (waitingReject) {\n const reject = waitingReject;\n\n waitingResolve = null;\n waitingReject = null;\n reject(err);\n } else {\n pendingError = { err };\n }\n }\n\n function finish(): void {\n if (done) return;\n\n done = true;\n\n if (waitingResolve) {\n const resolve = waitingResolve;\n\n waitingResolve = null;\n waitingReject = null;\n resolve({ done: true, value: undefined });\n }\n }\n\n function terminate(): void {\n if (done) return;\n\n buffer = [];\n finish();\n onTerminate?.();\n }\n\n const iterator: AsyncIterableIterator<T> = {\n next(): Promise<IteratorResult<T, undefined>> {\n if (pendingError) {\n const { err } = pendingError;\n\n pendingError = null;\n\n return Promise.reject(err);\n }\n\n if (buffer.length > 0) return Promise.resolve({ done: false, value: buffer.shift()! });\n\n if (done) return Promise.resolve({ done: true, value: undefined });\n\n return new Promise<IteratorResult<T, undefined>>((resolve, reject) => {\n waitingResolve = resolve;\n waitingReject = reject;\n });\n },\n\n return(): Promise<IteratorResult<T, undefined>> {\n terminate();\n\n return Promise.resolve({ done: true, value: undefined });\n },\n\n [Symbol.asyncIterator]() {\n return this;\n },\n };\n\n return { fail, finish, iterator, push, terminate };\n}\n"],"mappings":";AAqBA,SAAgB,EAAoB,GAAyC;CAI3E,IAAI,IAAc,CAAC,GACf,IAAkC,MAClC,IAAiC,MACjC,IAAO,IACP,IAAwC;CAE5C,SAAS,EAAK,GAAe;EACvB,QAEJ,IAAI,GAAgB;GAClB,IAAM,IAAU;GAIhB,AAFA,IAAiB,MACjB,IAAgB,MAChB,EAAQ;IAAE,MAAM;IAAO,OAAO;GAAK,CAAC;EACtC,OACE,EAAO,KAAK,CAAI;CAEpB;CAEA,SAAS,EAAK,GAAoB;EAC5B,QAIJ,IAFA,IAAO,IAEH,GAAe;GACjB,IAAM,IAAS;GAIf,AAFA,IAAiB,MACjB,IAAgB,MAChB,EAAO,CAAG;EACZ,OACE,IAAe,EAAE,OAAI;CAEzB;CAEA,SAAS,IAAe;EAClB,WAEJ,IAAO,IAEH,IAAgB;GAClB,IAAM,IAAU;GAIhB,AAFA,IAAiB,MACjB,IAAgB,MAChB,EAAQ;IAAE,MAAM;IAAM,OAAO,KAAA;GAAU,CAAC;EAC1C;CACF;CAEA,SAAS,IAAkB;EACrB,MAEJ,IAAS,CAAC,GACV,EAAO,GACP,IAAc;CAChB;CAiCA,OAAO;EAAE;EAAM;EAAQ,UAAA;GA9BrB,OAA8C;IAC5C,IAAI,GAAc;KAChB,IAAM,EAAE,WAAQ;KAIhB,OAFA,IAAe,MAER,QAAQ,OAAO,CAAG;IAC3B;IAMA,OAJI,EAAO,SAAS,IAAU,QAAQ,QAAQ;KAAE,MAAM;KAAO,OAAO,EAAO,MAAM;IAAG,CAAC,IAEjF,IAAa,QAAQ,QAAQ;KAAE,MAAM;KAAM,OAAO,KAAA;IAAU,CAAC,IAE1D,IAAI,SAAuC,GAAS,MAAW;KAEpE,AADA,IAAiB,GACjB,IAAgB;IAClB,CAAC;GACH;GAEA,SAAgD;IAG9C,OAFA,EAAU,GAEH,QAAQ,QAAQ;KAAE,MAAM;KAAM,OAAO,KAAA;IAAU,CAAC;GACzD;GAEA,CAAC,OAAO,iBAAiB;IACvB,OAAO;GACT;EAGqB;EAAU;EAAM;CAAU;AACnD"}
@@ -1,2 +0,0 @@
1
- const e=require("../errors.cjs"),t=require("../_utils.cjs");require("../types.cjs");let n=require("@vielzeug/ripple"),r=require("@vielzeug/arsenal");function i(e){if(!e)return;if(typeof e==`function`)return e;let n=e;return(e,r)=>{if(r?.aborted)return;let i=n.safeParse(e);if(i.success)return;let a={};for(let e of i.error.issues){let n=e.path.join(`.`)||`_form`;t.isSafeKey(n)&&(a[n]||(a[n]=e.message))}return a}}function a(a){let o=new Map;for(let[e,n]of Object.entries(a.validators??{}))t.assertSafeKey(e),o.set(e,n);let s=i(a.validator),c=a.connect??{},l=typeof a.defaultValues==`function`,u=typeof a.defaultValues==`function`?void 0:a.defaultValues,d=new Map(Object.entries((0,r.flattenPaths)(u??{}))),f=new Map(d),p=new Map,m=new Set,h=new Set,g=new Map,_=new Set,v=new Map,y=new AbortController,b=null,x=null,S=new Map,C=new Map,w=new Map,T=!1,E=0,D=!1,O=new Map,k=new Set;function A(e){return O.has(e)||O.set(e,(0,n.signal)(L(e),{equals:(e,t)=>e.value===t.value&&e.error===t.error&&e.hasError===t.hasError&&e.touched===t.touched&&e.dirty===t.dirty})),O.get(e)}function j(){b=null}function M(){x=null}function N(){return x??=Object.freeze(Object.fromEntries(p))}function P(){return Object.freeze({errors:N(),isDirty:h.size>0,isLoading:l,isSubmitting:T,isTouched:m.size>0,isValid:p.size===0,isValidating:g.size>0,submitCount:E,touchedFields:Object.freeze([...m]),validatingFields:Object.freeze([...g.keys()])})}let F=(0,n.signal)(P());function I(){return F.value}function L(e){let t=p.get(e);return{dirty:h.has(e),error:t,hasError:t!==void 0,touched:m.has(e),value:f.get(e)}}function R(e){let t=S.get(e);if(t)return t;let n=Object.freeze(L(e));return S.set(e,n),n}function z(e){if(!D)if(j(),x=null,e===void 0)S.clear(),(0,n.batch)(()=>{F.value=P();for(let[e,t]of O)t.value=L(e)});else if(typeof e==`string`)S.delete(e),(0,n.batch)(()=>{F.value=P();let t=O.get(e);t&&(t.value=L(e))});else{let t=[...e];for(let e of t)S.delete(e);(0,n.batch)(()=>{F.value=P();for(let e of t){let t=O.get(e);t&&(t.value=L(e))}})}}function B(){if(D)throw new e.ForgeDisposedError}function V(e){B();try{(0,n.batch)(e)}catch(e){throw z(),e}}return{arrayCache:C,baseline:d,batch:V,buildFieldState:L,get cachedErrors(){return x},set cachedErrors(e){x=e},get cachedValues(){return b},set cachedValues(e){b=e},computeErrors:N,computeState:P,connectDefaults:c,dirty:h,disposeController:y,get disposed(){return D},set disposed(e){D=e},ensureNotDisposed:B,fieldCtrls:v,fieldErrors:p,fieldSignals:O,fieldStateCache:S,formStateSignal:F,formValidator:s,getFieldSnapshot:R,getOrCreateFieldSignal:A,getStateSnapshot:I,invalidateErrors:M,invalidateValues:j,get isSubmittingState(){return T},set isSubmittingState(e){T=e},get loadingState(){return l},set loadingState(e){l=e},requestNotify:z,rippleSubs:k,runCtrls:_,scopeCache:w,store:f,get submitCount(){return E},set submitCount(e){E=e},touched:m,validatingRuns:g,validators:o}}exports.createFormContext=a;
2
- //# sourceMappingURL=context.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"context.cjs","names":[],"sources":["../../src/core/context.ts"],"sourcesContent":["import { batch as rippleBatch, signal, type Signal } from '@vielzeug/ripple';\n\nimport { assertSafeKey, flattenValues, isSafeKey } from '../_utils';\nimport { ForgeDisposedError } from '../errors';\nimport {\n type ArrayField,\n type ConnectOptions,\n type FieldState,\n type FieldValidator,\n FORM_ERROR,\n type Form,\n type FormOptions,\n type FormState,\n type FormValidator,\n type SafeParseSchema,\n} from '../types';\n\n/**\n * Resolves the `validator` option to a FormValidator.\n * Auto-detects SafeParseSchema objects (duck-typed via `safeParse`) and wraps them,\n * enabling direct use of @vielzeug/spell, Zod, Valibot schemas without an explicit wrapper call.\n */\nfunction resolveFormValidator<TValues extends Record<string, unknown>>(\n raw: FormValidator<TValues> | SafeParseSchema | undefined,\n): FormValidator<TValues> | undefined {\n if (!raw) return undefined;\n\n if (typeof raw === 'function') return raw;\n\n const schema = raw as SafeParseSchema;\n\n return (values, signal) => {\n if (signal?.aborted) return undefined;\n\n const result = schema.safeParse(values);\n\n if (result.success) return undefined;\n\n const errors: Partial<Record<string, string>> = {};\n\n for (const issue of result.error.issues) {\n const key = issue.path.join('.') || FORM_ERROR;\n\n // Security: `issue.path` is schema-controlled (untrusted duck-typed input, per\n // SafeParseSchema) — reject reserved segments before it can become an error key.\n if (!isSafeKey(key)) continue;\n\n if (!errors[key]) errors[key] = issue.message;\n }\n\n return errors as Partial<Record<string, string>>;\n };\n}\n\n/**\n * Shared mutable state + primitives passed to every createForm() sub-module factory\n * (`createValueOps`, `createValidationOps`, `createObserveOps`, `createLifecycleOps`).\n * Mirrors the `ScopeContext` pattern already established in `internal/scope.ts`.\n * @internal\n */\nexport type FormContext<TValues extends Record<string, unknown> = Record<string, unknown>> = {\n arrayCache: Map<string, ArrayField>;\n baseline: Map<string, unknown>;\n batch(fn: () => void): void;\n buildFieldState(name: string): FieldState<unknown>;\n cachedErrors: Readonly<Record<string, string>> | null;\n cachedValues: TValues | null;\n computeErrors(): Readonly<Record<string, string>>;\n computeState(): FormState;\n connectDefaults: ConnectOptions;\n dirty: Set<string>;\n disposeController: AbortController;\n disposed: boolean;\n ensureNotDisposed(): void;\n fieldCtrls: Map<string, AbortController>;\n fieldErrors: Map<string, string>;\n fieldSignals: Map<string, Signal<FieldState<unknown>>>;\n fieldStateCache: Map<string, FieldState<unknown>>;\n formStateSignal: Signal<FormState>;\n formValidator: FormValidator<TValues> | undefined;\n getFieldSnapshot(name: string): FieldState<unknown>;\n getOrCreateFieldSignal(key: string): Signal<FieldState<unknown>>;\n getStateSnapshot(): FormState;\n invalidateErrors(): void;\n invalidateValues(): void;\n isSubmittingState: boolean;\n loadingState: boolean;\n requestNotify(target?: string | Iterable<string>): void;\n rippleSubs: Set<{ dispose(): void }>;\n runCtrls: Set<AbortController>;\n scopeCache: Map<string, Form<never>>;\n store: Map<string, unknown>;\n submitCount: number;\n touched: Set<string>;\n validatingRuns: Map<string, Set<symbol>>;\n validators: Map<string, FieldValidator<unknown>>;\n};\n\n/**\n * Builds the raw mutable state + shared primitives for a form. This is the \"Core state\" +\n * \"Notification helpers\" sections of the original monolithic `createForm()` body, factored\n * out so `internal/values.ts`, `internal/validation.ts`, `internal/observe.ts`, and\n * `internal/lifecycle.ts` can each operate on the same shared state without closing over\n * ad-hoc free variables.\n */\nexport function createFormContext<TValues extends Record<string, unknown> = Record<string, unknown>>(\n init: FormOptions<TValues>,\n): FormContext<TValues> {\n /* ---- Validators ---- */\n\n const validators = new Map<string, FieldValidator<unknown>>();\n\n for (const [name, validator] of Object.entries(init.validators ?? {})) {\n assertSafeKey(name);\n validators.set(name, validator as FieldValidator<unknown>);\n }\n\n const formValidator: FormValidator<TValues> | undefined = resolveFormValidator(init.validator);\n const connectDefaults: ConnectOptions = init.connect ?? {};\n\n /* ---- Core state ---- */\n\n let loadingState = typeof init.defaultValues === 'function';\n\n const staticValues = typeof init.defaultValues !== 'function' ? init.defaultValues : undefined;\n const baseline = new Map<string, unknown>(Object.entries(flattenValues(staticValues ?? {})));\n const store = new Map<string, unknown>(baseline);\n const fieldErrors = new Map<string, string>();\n const touched = new Set<string>();\n const dirty = new Set<string>();\n\n /* ---- Validation tracking ---- */\n\n /**\n * Symbol-based ref-counting for concurrent validation runs per field.\n * Each runValidationCore() call mints a unique symbol and adds it to the field's Set.\n * The field leaves `validatingFields` only when its Set is empty — impossible to miscount.\n */\n const validatingRuns = new Map<string, Set<symbol>>();\n const runCtrls = new Set<AbortController>();\n const fieldCtrls = new Map<string, AbortController>();\n const disposeController = new AbortController();\n\n /* ---- Caches ---- */\n\n let cachedValues: TValues | null = null;\n let cachedErrors: Readonly<Record<string, string>> | null = null;\n const fieldStateCache = new Map<string, FieldState<unknown>>();\n const arrayCache = new Map<string, ArrayField>();\n const scopeCache = new Map<string, Form<never>>();\n\n /* ---- Submission state ---- */\n\n let isSubmittingState = false;\n let submitCount = 0;\n\n /* ---- Lifecycle ---- */\n\n let disposed = false;\n\n /* ---- Reactive signals ---- */\n\n const fieldSignals = new Map<string, Signal<FieldState<unknown>>>();\n const rippleSubs = new Set<{ dispose(): void }>();\n\n function getOrCreateFieldSignal(key: string): Signal<FieldState<unknown>> {\n if (!fieldSignals.has(key)) {\n fieldSignals.set(\n key,\n signal<FieldState<unknown>>(buildFieldState(key), {\n equals: (a, b) =>\n a.value === b.value &&\n a.error === b.error &&\n a.hasError === b.hasError &&\n a.touched === b.touched &&\n a.dirty === b.dirty,\n }),\n );\n }\n\n return fieldSignals.get(key)!;\n }\n\n /* ======== Notification helpers ======== */\n\n function invalidateValues(): void {\n cachedValues = null;\n }\n\n function invalidateErrors(): void {\n cachedErrors = null;\n }\n\n function computeErrors(): Readonly<Record<string, string>> {\n return (cachedErrors ??= Object.freeze(Object.fromEntries(fieldErrors)));\n }\n\n function computeState(): FormState {\n return Object.freeze({\n errors: computeErrors(),\n isDirty: dirty.size > 0,\n isLoading: loadingState,\n isSubmitting: isSubmittingState,\n isTouched: touched.size > 0,\n isValid: fieldErrors.size === 0,\n isValidating: validatingRuns.size > 0,\n submitCount,\n touchedFields: Object.freeze([...touched]) as readonly string[],\n validatingFields: Object.freeze([...validatingRuns.keys()]) as readonly string[],\n });\n }\n\n const formStateSignal = signal<FormState>(computeState());\n\n function getStateSnapshot(): FormState {\n return formStateSignal.value;\n }\n\n function buildFieldState(name: string): FieldState<unknown> {\n const error = fieldErrors.get(name);\n\n return {\n dirty: dirty.has(name),\n error,\n hasError: error !== undefined,\n touched: touched.has(name),\n value: store.get(name),\n };\n }\n\n function getFieldSnapshot(name: string): FieldState<unknown> {\n const cached = fieldStateCache.get(name);\n\n if (cached) return cached;\n\n const snap = Object.freeze(buildFieldState(name));\n\n fieldStateCache.set(name, snap);\n\n return snap;\n }\n\n /**\n * R4: Unified notification — replaces requestNotify(field?) + requestNotifyFields(fields).\n * - undefined → full-form refresh (all field signals + form signal)\n * - string → single field + form signal\n * - Iterable → targeted set of fields + form signal\n * rippleBatch deduplicates signal writes within the synchronous call stack.\n */\n function requestNotify(target?: string | Iterable<string>): void {\n if (disposed) return;\n\n invalidateValues();\n cachedErrors = null;\n\n if (target === undefined) {\n fieldStateCache.clear();\n rippleBatch(() => {\n formStateSignal.value = computeState();\n\n for (const [name, sig] of fieldSignals) sig.value = buildFieldState(name);\n });\n } else if (typeof target === 'string') {\n fieldStateCache.delete(target);\n rippleBatch(() => {\n formStateSignal.value = computeState();\n\n const sig = fieldSignals.get(target);\n\n if (sig) sig.value = buildFieldState(target);\n });\n } else {\n // Materialize before rippleBatch so the iterable isn't consumed inside the callback.\n const fields = [...target];\n\n for (const field of fields) fieldStateCache.delete(field);\n\n rippleBatch(() => {\n formStateSignal.value = computeState();\n\n for (const field of fields) {\n const sig = fieldSignals.get(field);\n\n if (sig) sig.value = buildFieldState(field);\n }\n });\n }\n }\n\n /* ======== Lifecycle guards ======== */\n\n function ensureNotDisposed(): void {\n if (disposed) throw new ForgeDisposedError();\n }\n\n /* ======== Batch ======== */\n\n function batch(fn: () => void): void {\n ensureNotDisposed();\n\n try {\n rippleBatch(fn);\n } catch (e) {\n // rippleBatch clears its pending-subscriber queue on error to prevent\n // stale flushes on future unrelated writes. Re-notify here so subscribers\n // still see the partial mutations that succeeded before the throw.\n requestNotify();\n throw e;\n }\n }\n\n return {\n arrayCache,\n baseline,\n batch,\n buildFieldState,\n get cachedErrors() {\n return cachedErrors;\n },\n set cachedErrors(value) {\n cachedErrors = value;\n },\n get cachedValues() {\n return cachedValues;\n },\n set cachedValues(value) {\n cachedValues = value;\n },\n computeErrors,\n computeState,\n connectDefaults,\n dirty,\n disposeController,\n get disposed() {\n return disposed;\n },\n set disposed(value) {\n disposed = value;\n },\n ensureNotDisposed,\n fieldCtrls,\n fieldErrors,\n fieldSignals,\n fieldStateCache,\n formStateSignal,\n formValidator,\n getFieldSnapshot,\n getOrCreateFieldSignal,\n getStateSnapshot,\n invalidateErrors,\n invalidateValues,\n get isSubmittingState() {\n return isSubmittingState;\n },\n set isSubmittingState(value) {\n isSubmittingState = value;\n },\n get loadingState() {\n return loadingState;\n },\n set loadingState(value) {\n loadingState = value;\n },\n requestNotify,\n rippleSubs,\n runCtrls,\n scopeCache,\n store,\n get submitCount() {\n return submitCount;\n },\n set submitCount(value) {\n submitCount = value;\n },\n touched,\n validatingRuns,\n validators,\n };\n}\n"],"mappings":"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"}
@@ -1,10 +0,0 @@
1
- import { type FormOptions } from '../types';
2
- /**
3
- * Builds the raw mutable state + shared primitives for a form. This is the "Core state" +
4
- * "Notification helpers" sections of the original monolithic `createForm()` body, factored
5
- * out so `internal/values.ts`, `internal/validation.ts`, `internal/observe.ts`, and
6
- * `internal/lifecycle.ts` can each operate on the same shared state without closing over
7
- * ad-hoc free variables.
8
- */
9
- export declare function createFormContext<TValues extends Record<string, unknown> = Record<string, unknown>>(init: FormOptions<TValues>): FormContext<TValues>;
10
- //# sourceMappingURL=context.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/core/context.ts"],"names":[],"mappings":"AAIA,OAAO,EAOL,KAAK,WAAW,EAIjB,MAAM,UAAU,CAAC;AAmFlB;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjG,IAAI,EAAE,WAAW,CAAC,OAAO,CAAC,GACzB,WAAW,CAAC,OAAO,CAAC,CA+QtB"}
@@ -1,176 +0,0 @@
1
- import { ForgeDisposedError as e } from "../errors.js";
2
- import { assertSafeKey as t, flattenValues as n, isSafeKey as r } from "../_utils.js";
3
- import "../types.js";
4
- import { batch as i, signal as a } from "@vielzeug/ripple";
5
- //#region src/core/context.ts
6
- function o(e) {
7
- if (!e) return;
8
- if (typeof e == "function") return e;
9
- let t = e;
10
- return (e, n) => {
11
- if (n?.aborted) return;
12
- let i = t.safeParse(e);
13
- if (i.success) return;
14
- let a = {};
15
- for (let e of i.error.issues) {
16
- let t = e.path.join(".") || "_form";
17
- r(t) && (a[t] || (a[t] = e.message));
18
- }
19
- return a;
20
- };
21
- }
22
- function s(r) {
23
- let s = /* @__PURE__ */ new Map();
24
- for (let [e, n] of Object.entries(r.validators ?? {})) t(e), s.set(e, n);
25
- let c = o(r.validator), l = r.connect ?? {}, u = typeof r.defaultValues == "function", d = typeof r.defaultValues == "function" ? void 0 : r.defaultValues, f = new Map(Object.entries(n(d ?? {}))), p = new Map(f), m = /* @__PURE__ */ new Map(), h = /* @__PURE__ */ new Set(), g = /* @__PURE__ */ new Set(), _ = /* @__PURE__ */ new Map(), v = /* @__PURE__ */ new Set(), y = /* @__PURE__ */ new Map(), b = new AbortController(), x = null, S = null, C = /* @__PURE__ */ new Map(), w = /* @__PURE__ */ new Map(), T = /* @__PURE__ */ new Map(), E = !1, D = 0, O = !1, k = /* @__PURE__ */ new Map(), A = /* @__PURE__ */ new Set();
26
- function j(e) {
27
- return k.has(e) || k.set(e, a(R(e), { equals: (e, t) => e.value === t.value && e.error === t.error && e.hasError === t.hasError && e.touched === t.touched && e.dirty === t.dirty })), k.get(e);
28
- }
29
- function M() {
30
- x = null;
31
- }
32
- function N() {
33
- S = null;
34
- }
35
- function P() {
36
- return S ??= Object.freeze(Object.fromEntries(m));
37
- }
38
- function F() {
39
- return Object.freeze({
40
- errors: P(),
41
- isDirty: g.size > 0,
42
- isLoading: u,
43
- isSubmitting: E,
44
- isTouched: h.size > 0,
45
- isValid: m.size === 0,
46
- isValidating: _.size > 0,
47
- submitCount: D,
48
- touchedFields: Object.freeze([...h]),
49
- validatingFields: Object.freeze([..._.keys()])
50
- });
51
- }
52
- let I = a(F());
53
- function L() {
54
- return I.value;
55
- }
56
- function R(e) {
57
- let t = m.get(e);
58
- return {
59
- dirty: g.has(e),
60
- error: t,
61
- hasError: t !== void 0,
62
- touched: h.has(e),
63
- value: p.get(e)
64
- };
65
- }
66
- function z(e) {
67
- let t = C.get(e);
68
- if (t) return t;
69
- let n = Object.freeze(R(e));
70
- return C.set(e, n), n;
71
- }
72
- function B(e) {
73
- if (!O) if (M(), S = null, e === void 0) C.clear(), i(() => {
74
- I.value = F();
75
- for (let [e, t] of k) t.value = R(e);
76
- });
77
- else if (typeof e == "string") C.delete(e), i(() => {
78
- I.value = F();
79
- let t = k.get(e);
80
- t && (t.value = R(e));
81
- });
82
- else {
83
- let t = [...e];
84
- for (let e of t) C.delete(e);
85
- i(() => {
86
- I.value = F();
87
- for (let e of t) {
88
- let t = k.get(e);
89
- t && (t.value = R(e));
90
- }
91
- });
92
- }
93
- }
94
- function V() {
95
- if (O) throw new e();
96
- }
97
- function H(e) {
98
- V();
99
- try {
100
- i(e);
101
- } catch (e) {
102
- throw B(), e;
103
- }
104
- }
105
- return {
106
- arrayCache: w,
107
- baseline: f,
108
- batch: H,
109
- buildFieldState: R,
110
- get cachedErrors() {
111
- return S;
112
- },
113
- set cachedErrors(e) {
114
- S = e;
115
- },
116
- get cachedValues() {
117
- return x;
118
- },
119
- set cachedValues(e) {
120
- x = e;
121
- },
122
- computeErrors: P,
123
- computeState: F,
124
- connectDefaults: l,
125
- dirty: g,
126
- disposeController: b,
127
- get disposed() {
128
- return O;
129
- },
130
- set disposed(e) {
131
- O = e;
132
- },
133
- ensureNotDisposed: V,
134
- fieldCtrls: y,
135
- fieldErrors: m,
136
- fieldSignals: k,
137
- fieldStateCache: C,
138
- formStateSignal: I,
139
- formValidator: c,
140
- getFieldSnapshot: z,
141
- getOrCreateFieldSignal: j,
142
- getStateSnapshot: L,
143
- invalidateErrors: N,
144
- invalidateValues: M,
145
- get isSubmittingState() {
146
- return E;
147
- },
148
- set isSubmittingState(e) {
149
- E = e;
150
- },
151
- get loadingState() {
152
- return u;
153
- },
154
- set loadingState(e) {
155
- u = e;
156
- },
157
- requestNotify: B,
158
- rippleSubs: A,
159
- runCtrls: v,
160
- scopeCache: T,
161
- store: p,
162
- get submitCount() {
163
- return D;
164
- },
165
- set submitCount(e) {
166
- D = e;
167
- },
168
- touched: h,
169
- validatingRuns: _,
170
- validators: s
171
- };
172
- }
173
- //#endregion
174
- export { s as createFormContext };
175
-
176
- //# sourceMappingURL=context.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"context.js","names":[],"sources":["../../src/core/context.ts"],"sourcesContent":["import { batch as rippleBatch, signal, type Signal } from '@vielzeug/ripple';\n\nimport { assertSafeKey, flattenValues, isSafeKey } from '../_utils';\nimport { ForgeDisposedError } from '../errors';\nimport {\n type ArrayField,\n type ConnectOptions,\n type FieldState,\n type FieldValidator,\n FORM_ERROR,\n type Form,\n type FormOptions,\n type FormState,\n type FormValidator,\n type SafeParseSchema,\n} from '../types';\n\n/**\n * Resolves the `validator` option to a FormValidator.\n * Auto-detects SafeParseSchema objects (duck-typed via `safeParse`) and wraps them,\n * enabling direct use of @vielzeug/spell, Zod, Valibot schemas without an explicit wrapper call.\n */\nfunction resolveFormValidator<TValues extends Record<string, unknown>>(\n raw: FormValidator<TValues> | SafeParseSchema | undefined,\n): FormValidator<TValues> | undefined {\n if (!raw) return undefined;\n\n if (typeof raw === 'function') return raw;\n\n const schema = raw as SafeParseSchema;\n\n return (values, signal) => {\n if (signal?.aborted) return undefined;\n\n const result = schema.safeParse(values);\n\n if (result.success) return undefined;\n\n const errors: Partial<Record<string, string>> = {};\n\n for (const issue of result.error.issues) {\n const key = issue.path.join('.') || FORM_ERROR;\n\n // Security: `issue.path` is schema-controlled (untrusted duck-typed input, per\n // SafeParseSchema) — reject reserved segments before it can become an error key.\n if (!isSafeKey(key)) continue;\n\n if (!errors[key]) errors[key] = issue.message;\n }\n\n return errors as Partial<Record<string, string>>;\n };\n}\n\n/**\n * Shared mutable state + primitives passed to every createForm() sub-module factory\n * (`createValueOps`, `createValidationOps`, `createObserveOps`, `createLifecycleOps`).\n * Mirrors the `ScopeContext` pattern already established in `internal/scope.ts`.\n * @internal\n */\nexport type FormContext<TValues extends Record<string, unknown> = Record<string, unknown>> = {\n arrayCache: Map<string, ArrayField>;\n baseline: Map<string, unknown>;\n batch(fn: () => void): void;\n buildFieldState(name: string): FieldState<unknown>;\n cachedErrors: Readonly<Record<string, string>> | null;\n cachedValues: TValues | null;\n computeErrors(): Readonly<Record<string, string>>;\n computeState(): FormState;\n connectDefaults: ConnectOptions;\n dirty: Set<string>;\n disposeController: AbortController;\n disposed: boolean;\n ensureNotDisposed(): void;\n fieldCtrls: Map<string, AbortController>;\n fieldErrors: Map<string, string>;\n fieldSignals: Map<string, Signal<FieldState<unknown>>>;\n fieldStateCache: Map<string, FieldState<unknown>>;\n formStateSignal: Signal<FormState>;\n formValidator: FormValidator<TValues> | undefined;\n getFieldSnapshot(name: string): FieldState<unknown>;\n getOrCreateFieldSignal(key: string): Signal<FieldState<unknown>>;\n getStateSnapshot(): FormState;\n invalidateErrors(): void;\n invalidateValues(): void;\n isSubmittingState: boolean;\n loadingState: boolean;\n requestNotify(target?: string | Iterable<string>): void;\n rippleSubs: Set<{ dispose(): void }>;\n runCtrls: Set<AbortController>;\n scopeCache: Map<string, Form<never>>;\n store: Map<string, unknown>;\n submitCount: number;\n touched: Set<string>;\n validatingRuns: Map<string, Set<symbol>>;\n validators: Map<string, FieldValidator<unknown>>;\n};\n\n/**\n * Builds the raw mutable state + shared primitives for a form. This is the \"Core state\" +\n * \"Notification helpers\" sections of the original monolithic `createForm()` body, factored\n * out so `internal/values.ts`, `internal/validation.ts`, `internal/observe.ts`, and\n * `internal/lifecycle.ts` can each operate on the same shared state without closing over\n * ad-hoc free variables.\n */\nexport function createFormContext<TValues extends Record<string, unknown> = Record<string, unknown>>(\n init: FormOptions<TValues>,\n): FormContext<TValues> {\n /* ---- Validators ---- */\n\n const validators = new Map<string, FieldValidator<unknown>>();\n\n for (const [name, validator] of Object.entries(init.validators ?? {})) {\n assertSafeKey(name);\n validators.set(name, validator as FieldValidator<unknown>);\n }\n\n const formValidator: FormValidator<TValues> | undefined = resolveFormValidator(init.validator);\n const connectDefaults: ConnectOptions = init.connect ?? {};\n\n /* ---- Core state ---- */\n\n let loadingState = typeof init.defaultValues === 'function';\n\n const staticValues = typeof init.defaultValues !== 'function' ? init.defaultValues : undefined;\n const baseline = new Map<string, unknown>(Object.entries(flattenValues(staticValues ?? {})));\n const store = new Map<string, unknown>(baseline);\n const fieldErrors = new Map<string, string>();\n const touched = new Set<string>();\n const dirty = new Set<string>();\n\n /* ---- Validation tracking ---- */\n\n /**\n * Symbol-based ref-counting for concurrent validation runs per field.\n * Each runValidationCore() call mints a unique symbol and adds it to the field's Set.\n * The field leaves `validatingFields` only when its Set is empty — impossible to miscount.\n */\n const validatingRuns = new Map<string, Set<symbol>>();\n const runCtrls = new Set<AbortController>();\n const fieldCtrls = new Map<string, AbortController>();\n const disposeController = new AbortController();\n\n /* ---- Caches ---- */\n\n let cachedValues: TValues | null = null;\n let cachedErrors: Readonly<Record<string, string>> | null = null;\n const fieldStateCache = new Map<string, FieldState<unknown>>();\n const arrayCache = new Map<string, ArrayField>();\n const scopeCache = new Map<string, Form<never>>();\n\n /* ---- Submission state ---- */\n\n let isSubmittingState = false;\n let submitCount = 0;\n\n /* ---- Lifecycle ---- */\n\n let disposed = false;\n\n /* ---- Reactive signals ---- */\n\n const fieldSignals = new Map<string, Signal<FieldState<unknown>>>();\n const rippleSubs = new Set<{ dispose(): void }>();\n\n function getOrCreateFieldSignal(key: string): Signal<FieldState<unknown>> {\n if (!fieldSignals.has(key)) {\n fieldSignals.set(\n key,\n signal<FieldState<unknown>>(buildFieldState(key), {\n equals: (a, b) =>\n a.value === b.value &&\n a.error === b.error &&\n a.hasError === b.hasError &&\n a.touched === b.touched &&\n a.dirty === b.dirty,\n }),\n );\n }\n\n return fieldSignals.get(key)!;\n }\n\n /* ======== Notification helpers ======== */\n\n function invalidateValues(): void {\n cachedValues = null;\n }\n\n function invalidateErrors(): void {\n cachedErrors = null;\n }\n\n function computeErrors(): Readonly<Record<string, string>> {\n return (cachedErrors ??= Object.freeze(Object.fromEntries(fieldErrors)));\n }\n\n function computeState(): FormState {\n return Object.freeze({\n errors: computeErrors(),\n isDirty: dirty.size > 0,\n isLoading: loadingState,\n isSubmitting: isSubmittingState,\n isTouched: touched.size > 0,\n isValid: fieldErrors.size === 0,\n isValidating: validatingRuns.size > 0,\n submitCount,\n touchedFields: Object.freeze([...touched]) as readonly string[],\n validatingFields: Object.freeze([...validatingRuns.keys()]) as readonly string[],\n });\n }\n\n const formStateSignal = signal<FormState>(computeState());\n\n function getStateSnapshot(): FormState {\n return formStateSignal.value;\n }\n\n function buildFieldState(name: string): FieldState<unknown> {\n const error = fieldErrors.get(name);\n\n return {\n dirty: dirty.has(name),\n error,\n hasError: error !== undefined,\n touched: touched.has(name),\n value: store.get(name),\n };\n }\n\n function getFieldSnapshot(name: string): FieldState<unknown> {\n const cached = fieldStateCache.get(name);\n\n if (cached) return cached;\n\n const snap = Object.freeze(buildFieldState(name));\n\n fieldStateCache.set(name, snap);\n\n return snap;\n }\n\n /**\n * R4: Unified notification — replaces requestNotify(field?) + requestNotifyFields(fields).\n * - undefined → full-form refresh (all field signals + form signal)\n * - string → single field + form signal\n * - Iterable → targeted set of fields + form signal\n * rippleBatch deduplicates signal writes within the synchronous call stack.\n */\n function requestNotify(target?: string | Iterable<string>): void {\n if (disposed) return;\n\n invalidateValues();\n cachedErrors = null;\n\n if (target === undefined) {\n fieldStateCache.clear();\n rippleBatch(() => {\n formStateSignal.value = computeState();\n\n for (const [name, sig] of fieldSignals) sig.value = buildFieldState(name);\n });\n } else if (typeof target === 'string') {\n fieldStateCache.delete(target);\n rippleBatch(() => {\n formStateSignal.value = computeState();\n\n const sig = fieldSignals.get(target);\n\n if (sig) sig.value = buildFieldState(target);\n });\n } else {\n // Materialize before rippleBatch so the iterable isn't consumed inside the callback.\n const fields = [...target];\n\n for (const field of fields) fieldStateCache.delete(field);\n\n rippleBatch(() => {\n formStateSignal.value = computeState();\n\n for (const field of fields) {\n const sig = fieldSignals.get(field);\n\n if (sig) sig.value = buildFieldState(field);\n }\n });\n }\n }\n\n /* ======== Lifecycle guards ======== */\n\n function ensureNotDisposed(): void {\n if (disposed) throw new ForgeDisposedError();\n }\n\n /* ======== Batch ======== */\n\n function batch(fn: () => void): void {\n ensureNotDisposed();\n\n try {\n rippleBatch(fn);\n } catch (e) {\n // rippleBatch clears its pending-subscriber queue on error to prevent\n // stale flushes on future unrelated writes. Re-notify here so subscribers\n // still see the partial mutations that succeeded before the throw.\n requestNotify();\n throw e;\n }\n }\n\n return {\n arrayCache,\n baseline,\n batch,\n buildFieldState,\n get cachedErrors() {\n return cachedErrors;\n },\n set cachedErrors(value) {\n cachedErrors = value;\n },\n get cachedValues() {\n return cachedValues;\n },\n set cachedValues(value) {\n cachedValues = value;\n },\n computeErrors,\n computeState,\n connectDefaults,\n dirty,\n disposeController,\n get disposed() {\n return disposed;\n },\n set disposed(value) {\n disposed = value;\n },\n ensureNotDisposed,\n fieldCtrls,\n fieldErrors,\n fieldSignals,\n fieldStateCache,\n formStateSignal,\n formValidator,\n getFieldSnapshot,\n getOrCreateFieldSignal,\n getStateSnapshot,\n invalidateErrors,\n invalidateValues,\n get isSubmittingState() {\n return isSubmittingState;\n },\n set isSubmittingState(value) {\n isSubmittingState = value;\n },\n get loadingState() {\n return loadingState;\n },\n set loadingState(value) {\n loadingState = value;\n },\n requestNotify,\n rippleSubs,\n runCtrls,\n scopeCache,\n store,\n get submitCount() {\n return submitCount;\n },\n set submitCount(value) {\n submitCount = value;\n },\n touched,\n validatingRuns,\n validators,\n };\n}\n"],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- const e=require("../_utils.cjs");function t(t){function n(){t.disposed=!0,t.disposeController.abort();for(let e of t.fieldCtrls.values())e.abort();t.fieldCtrls.clear(),t.runCtrls.clear(),t.arrayCache.clear(),t.scopeCache.clear();for(let e of t.rippleSubs)e.dispose();t.rippleSubs.clear(),t.fieldSignals.clear()}function r(){return t.ensureNotDisposed(),Object.freeze({baseline:Object.freeze(Object.fromEntries(t.baseline)),dirty:Object.freeze([...t.dirty]),errors:Object.freeze(Object.fromEntries(t.fieldErrors)),store:Object.freeze(Object.fromEntries(t.store)),submitCount:t.submitCount,touched:Object.freeze([...t.touched])})}function i(n){t.ensureNotDisposed();for(let e of t.runCtrls)e.abort();for(let e of t.fieldCtrls.values())e.abort();t.runCtrls.clear(),t.fieldCtrls.clear(),t.validatingRuns.clear(),t.store.clear(),t.baseline.clear(),t.fieldErrors.clear(),t.touched.clear(),t.dirty.clear();for(let[r,i]of Object.entries(n.store))e.isSafeKey(r)&&t.store.set(r,i);for(let[r,i]of Object.entries(n.baseline))e.isSafeKey(r)&&t.baseline.set(r,i);for(let[r,i]of Object.entries(n.errors))e.isSafeKey(r)&&t.fieldErrors.set(r,i);for(let r of n.touched)e.isSafeKey(r)&&t.touched.add(r);for(let r of n.dirty)e.isSafeKey(r)&&t.dirty.add(r);t.submitCount=n.submitCount,t.invalidateErrors(),t.invalidateValues(),t.requestNotify()}return{dispose:n,restore:i,snapshot:r}}exports.createLifecycleOps=t;
2
- //# sourceMappingURL=lifecycle.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"lifecycle.cjs","names":[],"sources":["../../src/core/lifecycle.ts"],"sourcesContent":["import type { FormSnapshot } from '../types';\nimport type { FormContext } from './context';\n\nimport { isSafeKey } from '../_utils';\n\n/**\n * Lifecycle operations: `dispose`, `snapshot`, `restore`. Moved verbatim from `createForm()`'s\n * \"Lifecycle: dispose\" and \"Snapshot / Restore\" sections.\n */\nexport function createLifecycleOps<TValues extends Record<string, unknown>>(ctx: FormContext<TValues>) {\n function dispose(): void {\n ctx.disposed = true;\n ctx.disposeController.abort();\n\n for (const ctrl of ctx.fieldCtrls.values()) ctrl.abort();\n\n ctx.fieldCtrls.clear();\n ctx.runCtrls.clear();\n ctx.arrayCache.clear();\n ctx.scopeCache.clear();\n\n for (const sub of ctx.rippleSubs) sub.dispose();\n\n ctx.rippleSubs.clear();\n ctx.fieldSignals.clear();\n }\n\n function snapshot(): FormSnapshot<TValues> {\n ctx.ensureNotDisposed();\n\n // Cast is irreducible: baseline/store are typed Partial<Record<FlatKeyOf<TValues>, unknown>>, a\n // TValues-dependent mapped type TS cannot verify a plain Object.fromEntries() result against for\n // a generic TValues param -- no runtime behavior is affected, only static verification.\n return Object.freeze({\n baseline: Object.freeze(Object.fromEntries(ctx.baseline)),\n dirty: Object.freeze([...ctx.dirty]),\n errors: Object.freeze(Object.fromEntries(ctx.fieldErrors)),\n store: Object.freeze(Object.fromEntries(ctx.store)),\n submitCount: ctx.submitCount,\n touched: Object.freeze([...ctx.touched]),\n }) as FormSnapshot<TValues>;\n }\n\n function restore(snap: FormSnapshot<TValues>): void {\n ctx.ensureNotDisposed();\n\n for (const ctrl of ctx.runCtrls) ctrl.abort();\n for (const ctrl of ctx.fieldCtrls.values()) ctrl.abort();\n\n ctx.runCtrls.clear();\n ctx.fieldCtrls.clear();\n ctx.validatingRuns.clear();\n ctx.store.clear();\n ctx.baseline.clear();\n ctx.fieldErrors.clear();\n ctx.touched.clear();\n ctx.dirty.clear();\n\n // Security: snap may be an externally-sourced blob (e.g. JSON.parse'd persisted state).\n // Unlike patch()/replace(), restore() writes flat keys directly into the internal Maps —\n // it must apply the same silent-drop defense-in-depth as flattenValues() does for those.\n for (const [k, v] of Object.entries(snap.store)) if (isSafeKey(k)) ctx.store.set(k, v);\n for (const [k, v] of Object.entries(snap.baseline)) if (isSafeKey(k)) ctx.baseline.set(k, v);\n for (const [k, v] of Object.entries(snap.errors)) if (isSafeKey(k)) ctx.fieldErrors.set(k, v);\n for (const t of snap.touched) if (isSafeKey(t)) ctx.touched.add(t);\n for (const d of snap.dirty) if (isSafeKey(d)) ctx.dirty.add(d);\n\n ctx.submitCount = snap.submitCount;\n\n ctx.invalidateErrors();\n ctx.invalidateValues();\n ctx.requestNotify();\n }\n\n return { dispose, restore, snapshot };\n}\n\nexport type LifecycleOps<TValues extends Record<string, unknown>> = ReturnType<typeof createLifecycleOps<TValues>>;\n"],"mappings":"iCASA,SAAgB,EAA4D,EAA2B,CACrG,SAAS,GAAgB,CACvB,EAAI,SAAW,GACf,EAAI,kBAAkB,MAAM,EAE5B,IAAK,IAAM,KAAQ,EAAI,WAAW,OAAO,EAAG,EAAK,MAAM,EAEvD,EAAI,WAAW,MAAM,EACrB,EAAI,SAAS,MAAM,EACnB,EAAI,WAAW,MAAM,EACrB,EAAI,WAAW,MAAM,EAErB,IAAK,IAAM,KAAO,EAAI,WAAY,EAAI,QAAQ,EAE9C,EAAI,WAAW,MAAM,EACrB,EAAI,aAAa,MAAM,CACzB,CAEA,SAAS,GAAkC,CAMzC,OALA,EAAI,kBAAkB,EAKf,OAAO,OAAO,CACnB,SAAU,OAAO,OAAO,OAAO,YAAY,EAAI,QAAQ,CAAC,EACxD,MAAO,OAAO,OAAO,CAAC,GAAG,EAAI,KAAK,CAAC,EACnC,OAAQ,OAAO,OAAO,OAAO,YAAY,EAAI,WAAW,CAAC,EACzD,MAAO,OAAO,OAAO,OAAO,YAAY,EAAI,KAAK,CAAC,EAClD,YAAa,EAAI,YACjB,QAAS,OAAO,OAAO,CAAC,GAAG,EAAI,OAAO,CAAC,CACzC,CAAC,CACH,CAEA,SAAS,EAAQ,EAAmC,CAClD,EAAI,kBAAkB,EAEtB,IAAK,IAAM,KAAQ,EAAI,SAAU,EAAK,MAAM,EAC5C,IAAK,IAAM,KAAQ,EAAI,WAAW,OAAO,EAAG,EAAK,MAAM,EAEvD,EAAI,SAAS,MAAM,EACnB,EAAI,WAAW,MAAM,EACrB,EAAI,eAAe,MAAM,EACzB,EAAI,MAAM,MAAM,EAChB,EAAI,SAAS,MAAM,EACnB,EAAI,YAAY,MAAM,EACtB,EAAI,QAAQ,MAAM,EAClB,EAAI,MAAM,MAAM,EAKhB,IAAK,GAAM,CAAC,EAAG,KAAM,OAAO,QAAQ,EAAK,KAAK,EAAO,EAAA,UAAU,CAAC,GAAG,EAAI,MAAM,IAAI,EAAG,CAAC,EACrF,IAAK,GAAM,CAAC,EAAG,KAAM,OAAO,QAAQ,EAAK,QAAQ,EAAO,EAAA,UAAU,CAAC,GAAG,EAAI,SAAS,IAAI,EAAG,CAAC,EAC3F,IAAK,GAAM,CAAC,EAAG,KAAM,OAAO,QAAQ,EAAK,MAAM,EAAO,EAAA,UAAU,CAAC,GAAG,EAAI,YAAY,IAAI,EAAG,CAAC,EAC5F,IAAK,IAAM,KAAK,EAAK,QAAa,EAAA,UAAU,CAAC,GAAG,EAAI,QAAQ,IAAI,CAAC,EACjE,IAAK,IAAM,KAAK,EAAK,MAAW,EAAA,UAAU,CAAC,GAAG,EAAI,MAAM,IAAI,CAAC,EAE7D,EAAI,YAAc,EAAK,YAEvB,EAAI,iBAAiB,EACrB,EAAI,iBAAiB,EACrB,EAAI,cAAc,CACpB,CAEA,MAAO,CAAE,UAAS,UAAS,UAAS,CACtC"}