@vielzeug/forge 1.4.0 → 2.0.1
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 +21 -53
- package/dist/_utils.cjs +1 -2
- package/dist/_utils.d.ts +1 -10
- package/dist/_utils.d.ts.map +1 -1
- package/dist/_utils.js +2 -33
- package/dist/adapters/form-data.cjs +1 -1
- package/dist/adapters/form-data.cjs.map +1 -1
- package/dist/adapters/form-data.d.ts.map +1 -1
- package/dist/adapters/form-data.js +5 -5
- package/dist/adapters/form-data.js.map +1 -1
- package/dist/core/path.cjs +2 -0
- package/dist/core/path.cjs.map +1 -0
- package/dist/core/path.d.ts +22 -0
- package/dist/core/path.d.ts.map +1 -0
- package/dist/core/path.js +125 -0
- package/dist/core/path.js.map +1 -0
- package/dist/devtools.cjs +1 -1
- package/dist/devtools.cjs.map +1 -1
- package/dist/devtools.d.ts +3 -36
- package/dist/devtools.d.ts.map +1 -1
- package/dist/devtools.js +6 -24
- package/dist/devtools.js.map +1 -1
- package/dist/dom.cjs +2 -0
- package/dist/dom.cjs.map +1 -0
- package/dist/dom.d.ts +9 -0
- package/dist/dom.d.ts.map +1 -0
- package/dist/dom.js +24 -0
- package/dist/dom.js.map +1 -0
- package/dist/errors.cjs +1 -1
- package/dist/errors.cjs.map +1 -1
- package/dist/errors.d.ts +1 -3
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +1 -6
- package/dist/errors.js.map +1 -1
- package/dist/forge.cjs +1 -1
- package/dist/forge.cjs.map +1 -1
- package/dist/forge.iife.js +1 -1
- package/dist/forge.iife.js.map +1 -1
- package/dist/forge.js +1 -1
- package/dist/forge.js.map +1 -1
- package/dist/form.cjs +1 -1
- package/dist/form.cjs.map +1 -1
- package/dist/form.d.ts +3 -5
- package/dist/form.d.ts.map +1 -1
- package/dist/form.js +223 -58
- package/dist/form.js.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.js +3 -4
- package/dist/spell.cjs +2 -0
- package/dist/spell.cjs.map +1 -0
- package/dist/spell.d.ts +5 -0
- package/dist/spell.d.ts.map +1 -0
- package/dist/spell.js +57 -0
- package/dist/spell.js.map +1 -0
- package/dist/types.d.ts +70 -392
- package/dist/types.d.ts.map +1 -1
- package/dist/vault.cjs +2 -0
- package/dist/vault.cjs.map +1 -0
- package/dist/vault.d.ts +10 -0
- package/dist/vault.d.ts.map +1 -0
- package/dist/vault.js +14 -0
- package/dist/vault.js.map +1 -0
- package/package.json +49 -13
- package/dist/_dev.cjs +0 -2
- package/dist/_dev.cjs.map +0 -1
- package/dist/_dev.js +0 -12
- package/dist/_dev.js.map +0 -1
- package/dist/_utils.cjs.map +0 -1
- package/dist/_utils.js.map +0 -1
- package/dist/adapters/validators.cjs +0 -2
- package/dist/adapters/validators.cjs.map +0 -1
- package/dist/adapters/validators.d.ts +0 -44
- package/dist/adapters/validators.d.ts.map +0 -1
- package/dist/adapters/validators.js +0 -20
- package/dist/adapters/validators.js.map +0 -1
- package/dist/core/array.cjs +0 -2
- package/dist/core/array.cjs.map +0 -1
- package/dist/core/array.d.ts +0 -10
- package/dist/core/array.d.ts.map +0 -1
- package/dist/core/array.js +0 -77
- package/dist/core/array.js.map +0 -1
- package/dist/core/asyncQueue.cjs +0 -2
- package/dist/core/asyncQueue.cjs.map +0 -1
- package/dist/core/asyncQueue.d.ts +0 -22
- package/dist/core/asyncQueue.d.ts.map +0 -1
- package/dist/core/asyncQueue.js +0 -67
- package/dist/core/asyncQueue.js.map +0 -1
- package/dist/core/context.cjs +0 -2
- package/dist/core/context.cjs.map +0 -1
- package/dist/core/context.d.ts +0 -7
- package/dist/core/context.d.ts.map +0 -1
- package/dist/core/context.js +0 -139
- package/dist/core/context.js.map +0 -1
- package/dist/core/fields.cjs +0 -2
- package/dist/core/fields.cjs.map +0 -1
- package/dist/core/fields.d.ts +0 -52
- package/dist/core/fields.d.ts.map +0 -1
- package/dist/core/fields.js +0 -310
- package/dist/core/fields.js.map +0 -1
- package/dist/core/lifecycle.cjs +0 -2
- package/dist/core/lifecycle.cjs.map +0 -1
- package/dist/core/lifecycle.d.ts +0 -15
- package/dist/core/lifecycle.d.ts.map +0 -1
- package/dist/core/lifecycle.js +0 -38
- package/dist/core/lifecycle.js.map +0 -1
- package/dist/core/notifier.cjs +0 -2
- package/dist/core/notifier.cjs.map +0 -1
- package/dist/core/notifier.d.ts +0 -33
- package/dist/core/notifier.d.ts.map +0 -1
- package/dist/core/notifier.js +0 -78
- package/dist/core/notifier.js.map +0 -1
- package/dist/core/observe.cjs +0 -2
- package/dist/core/observe.cjs.map +0 -1
- package/dist/core/observe.d.ts +0 -20
- package/dist/core/observe.d.ts.map +0 -1
- package/dist/core/observe.js +0 -79
- package/dist/core/observe.js.map +0 -1
- package/dist/core/scope.cjs +0 -2
- package/dist/core/scope.cjs.map +0 -1
- package/dist/core/scope.d.ts +0 -11
- package/dist/core/scope.d.ts.map +0 -1
- package/dist/core/scope.js +0 -235
- package/dist/core/scope.js.map +0 -1
- package/dist/types.cjs +0 -2
- package/dist/types.cjs.map +0 -1
- package/dist/types.js +0 -21
- package/dist/types.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vielzeug/forge",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "2.0.1",
|
|
4
|
+
"description": "Immutable typed form state with focused fields and explicit full-form validation",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
7
7
|
"dist"
|
|
@@ -11,22 +11,29 @@
|
|
|
11
11
|
"types": "dist/index.d.ts",
|
|
12
12
|
"exports": {
|
|
13
13
|
".": {
|
|
14
|
-
"source": "./src/index.ts",
|
|
15
14
|
"types": "./dist/index.d.ts",
|
|
16
15
|
"import": "./dist/index.js",
|
|
17
16
|
"require": "./dist/index.cjs"
|
|
18
17
|
},
|
|
19
18
|
"./devtools": {
|
|
20
|
-
"source": "./src/devtools.ts",
|
|
21
19
|
"types": "./dist/devtools.d.ts",
|
|
22
20
|
"import": "./dist/devtools.js",
|
|
23
21
|
"require": "./dist/devtools.cjs"
|
|
24
22
|
},
|
|
25
|
-
"./
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
|
|
23
|
+
"./dom": {
|
|
24
|
+
"types": "./dist/dom.d.ts",
|
|
25
|
+
"import": "./dist/dom.js",
|
|
26
|
+
"require": "./dist/dom.cjs"
|
|
27
|
+
},
|
|
28
|
+
"./spell": {
|
|
29
|
+
"types": "./dist/spell.d.ts",
|
|
30
|
+
"import": "./dist/spell.js",
|
|
31
|
+
"require": "./dist/spell.cjs"
|
|
32
|
+
},
|
|
33
|
+
"./vault": {
|
|
34
|
+
"types": "./dist/vault.d.ts",
|
|
35
|
+
"import": "./dist/vault.js",
|
|
36
|
+
"require": "./dist/vault.cjs"
|
|
30
37
|
}
|
|
31
38
|
},
|
|
32
39
|
"scripts": {
|
|
@@ -47,13 +54,42 @@
|
|
|
47
54
|
"node": ">=22"
|
|
48
55
|
},
|
|
49
56
|
"dependencies": {
|
|
50
|
-
"@vielzeug/arsenal": "
|
|
51
|
-
"@vielzeug/ripple": "1.3.0"
|
|
57
|
+
"@vielzeug/arsenal": "2.0.1"
|
|
52
58
|
},
|
|
53
59
|
"devDependencies": {
|
|
54
|
-
"@types/node": "^26.1.
|
|
60
|
+
"@types/node": "^26.1.2",
|
|
61
|
+
"@vielzeug/spell": "2.0.1",
|
|
62
|
+
"@vielzeug/vault": "2.0.1",
|
|
55
63
|
"typescript": "^6.0.3",
|
|
56
|
-
"vite": "^8.
|
|
64
|
+
"vite": "^8.2.0",
|
|
57
65
|
"vitest": "^4.1.10"
|
|
66
|
+
},
|
|
67
|
+
"peerDependencies": {
|
|
68
|
+
"@vielzeug/spell": "2.0.1",
|
|
69
|
+
"@vielzeug/vault": "2.0.1"
|
|
70
|
+
},
|
|
71
|
+
"peerDependenciesMeta": {
|
|
72
|
+
"@vielzeug/spell": {
|
|
73
|
+
"optional": true
|
|
74
|
+
},
|
|
75
|
+
"@vielzeug/vault": {
|
|
76
|
+
"optional": true
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
"typesVersions": {
|
|
80
|
+
"*": {
|
|
81
|
+
"devtools": [
|
|
82
|
+
"dist/devtools.d.ts"
|
|
83
|
+
],
|
|
84
|
+
"dom": [
|
|
85
|
+
"dist/dom.d.ts"
|
|
86
|
+
],
|
|
87
|
+
"spell": [
|
|
88
|
+
"dist/spell.d.ts"
|
|
89
|
+
],
|
|
90
|
+
"vault": [
|
|
91
|
+
"dist/vault.d.ts"
|
|
92
|
+
]
|
|
93
|
+
}
|
|
58
94
|
}
|
|
59
95
|
}
|
package/dist/_dev.cjs
DELETED
package/dist/_dev.cjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"_dev.cjs","names":[],"sources":["../src/_dev.ts"],"sourcesContent":["const isDev = !(globalThis as { __FORGE_PROD__?: boolean }).__FORGE_PROD__;\n\n/** @internal @security Messages may include user-supplied form field paths. */\nexport function warn(msg: string): void {\n if (isDev) console.warn(`[@vielzeug/forge] ${msg}`);\n}\n\n/** @internal — Run fn only in dev builds. Use when dev-only logic goes beyond a single warn() / error() call. */\nexport function devOnly(fn: () => void): void {\n if (isDev) fn();\n}\n"],"mappings":"AAAA,IAAM,EAAQ,CAAE,WAA4C,eAG5D,SAAgB,EAAK,EAAmB,CAClC,GAAO,QAAQ,KAAK,qBAAqB,GAAK,CACpD,CAGA,SAAgB,EAAQ,EAAsB,CACxC,GAAO,EAAG,CAChB"}
|
package/dist/_dev.js
DELETED
package/dist/_dev.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"_dev.js","names":[],"sources":["../src/_dev.ts"],"sourcesContent":["const isDev = !(globalThis as { __FORGE_PROD__?: boolean }).__FORGE_PROD__;\n\n/** @internal @security Messages may include user-supplied form field paths. */\nexport function warn(msg: string): void {\n if (isDev) console.warn(`[@vielzeug/forge] ${msg}`);\n}\n\n/** @internal — Run fn only in dev builds. Use when dev-only logic goes beyond a single warn() / error() call. */\nexport function devOnly(fn: () => void): void {\n if (isDev) fn();\n}\n"],"mappings":";AAAA,IAAM,IAAQ,CAAE,WAA4C;AAG5D,SAAgB,EAAK,GAAmB;CACtC,AAAI,KAAO,QAAQ,KAAK,qBAAqB,GAAK;AACpD;AAGA,SAAgB,EAAQ,GAAsB;CAC5C,AAAI,KAAO,EAAG;AAChB"}
|
package/dist/_utils.cjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"_utils.cjs","names":[],"sources":["../src/_utils.ts"],"sourcesContent":["import { devOnly, warn } from './_dev';\nimport { ForgeConfigError } from './errors';\nimport { MAX_TYPED_PATH_DEPTH } from './types';\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 (see\n * `MAX_TYPED_PATH_DEPTH` in types.ts). Warn once per deep key in dev so callers notice\n * 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 <= MAX_TYPED_PATH_DEPTH || 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":"4GAMA,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,IAO5B,SAAS,EAA2B,EAAmB,CAChD,EAAI,SAAS,GAAG,IAEP,EAAI,MAAM,GAAG,CAAC,CAAC,QAAA,GAEQ,EAAgB,IAAI,CAAG,IAE5D,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"}
|
package/dist/_utils.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"_utils.js","names":[],"sources":["../src/_utils.ts"],"sourcesContent":["import { devOnly, warn } from './_dev';\nimport { ForgeConfigError } from './errors';\nimport { MAX_TYPED_PATH_DEPTH } from './types';\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 (see\n * `MAX_TYPED_PATH_DEPTH` in types.ts). Warn once per deep key in dev so callers notice\n * 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 <= MAX_TYPED_PATH_DEPTH || 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":";;;;;AAMA,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;AAOxC,SAAS,EAA2B,GAAmB;CAChD,EAAI,SAAS,GAAG,MAEP,EAAI,MAAM,GAAG,CAAC,CAAC,UAAA,KAEQ,EAAgB,IAAI,CAAG,MAE5D,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"}
|
package/dist/core/array.cjs
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
const e=require("../_dev.cjs"),t=require("../_utils.cjs");function n(n,r,i){let a=n;function o(n,r){e.devOnly(()=>{e.warn(`array('${t.sanitizeForLog(a,80)}').${n}(): field is not an array (got ${r===null?`null`:typeof r}) — no-op.`)})}return{append(e){let t=r.get(a);if(t!==void 0&&!Array.isArray(t)){o(`append`,t);return}i(n,Array.isArray(t)?[...t,e]:[e])},insert(e,t){let s=r.get(a);if(!Array.isArray(s)){o(`insert`,s);return}let c=[...s];c.splice(e,0,t),i(n,c)},move(e,t){let s=r.get(a);if(!Array.isArray(s)){o(`move`,s);return}let c=[...s];c.splice(t,0,c.splice(e,1)[0]),i(n,c)},prepend(e){let t=r.get(a);if(t!==void 0&&!Array.isArray(t)){o(`prepend`,t);return}i(n,Array.isArray(t)?[e,...t]:[e])},remove(e){let t=r.get(a);if(!Array.isArray(t)){o(`remove`,t);return}i(n,t.filter((t,n)=>n!==e))},replace(e,t){let s=r.get(a);if(!Array.isArray(s)){o(`replace`,s);return}let c=[...s];c[e]=t,i(n,c)},swap(e,t){let s=r.get(a);if(!Array.isArray(s)){o(`swap`,s);return}let c=[...s];[c[e],c[t]]=[c[t],c[e]],i(n,c)}}}exports.createArrayField=n;
|
|
2
|
-
//# sourceMappingURL=array.cjs.map
|
package/dist/core/array.cjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"array.cjs","names":[],"sources":["../../src/core/array.ts"],"sourcesContent":["import type { ArrayField, FlatKeyOf, TypeAtPath } from '../types';\n\nimport { devOnly, warn } from '../_dev';\nimport { sanitizeForLog } from '../_utils';\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 // Every mutating method below no-ops when the field isn't (yet) an array, instead of\n // throwing — a field can legitimately not exist yet. But a silent no-op with zero signal\n // is a debugging trap for the common mistake (typo'd field name, forgot to append() first\n // to seed the array). Warn once per call in dev so it's discoverable without being a\n // breaking runtime error for the legitimate \"not initialized yet\" case.\n function warnNotArray(apiLabel: string, current: unknown): void {\n devOnly(() => {\n warn(\n `array('${sanitizeForLog(key, 80)}').${apiLabel}(): field is not an array ` +\n `(got ${current === null ? 'null' : typeof current}) — no-op.`,\n );\n });\n }\n\n return {\n append(value: T) {\n const current = store.get(key);\n\n if (current !== undefined && !Array.isArray(current)) {\n warnNotArray('append', current);\n\n return;\n }\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)) {\n warnNotArray('insert', current);\n\n return;\n }\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)) {\n warnNotArray('move', current);\n\n return;\n }\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)) {\n warnNotArray('prepend', current);\n\n return;\n }\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)) {\n warnNotArray('remove', current);\n\n return;\n }\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)) {\n warnNotArray('replace', current);\n\n return;\n }\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)) {\n warnNotArray('swap', current);\n\n return;\n }\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":"0DAaA,SAAgB,EACd,EACA,EACA,EAC+C,CAG/C,IAAM,EAAM,EAOZ,SAAS,EAAa,EAAkB,EAAwB,CAC9D,EAAA,YAAc,CACZ,EAAA,KACE,UAAU,EAAA,eAAe,EAAK,EAAE,EAAE,KAAK,EAAS,iCACtC,IAAY,KAAO,OAAS,OAAO,EAAQ,WACvD,CACF,CAAC,CACH,CAEA,MAAO,CACL,OAAO,EAAU,CACf,IAAM,EAAU,EAAM,IAAI,CAAG,EAE7B,GAAI,IAAY,IAAA,IAAa,CAAC,MAAM,QAAQ,CAAO,EAAG,CACpD,EAAa,SAAU,CAAO,EAE9B,MACF,CAEA,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,CAC3B,EAAa,SAAU,CAAO,EAE9B,MACF,CAEA,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,CAC3B,EAAa,OAAQ,CAAO,EAE5B,MACF,CAEA,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,EAE7B,GAAI,IAAY,IAAA,IAAa,CAAC,MAAM,QAAQ,CAAO,EAAG,CACpD,EAAa,UAAW,CAAO,EAE/B,MACF,CAEA,EAAI,EAAO,MAAM,QAAQ,CAAO,EAAI,CAAC,EAAO,GAAG,CAAO,EAAI,CAAC,CAAK,CAA4B,CAC9F,EACA,OAAO,EAAe,CACpB,IAAM,EAAU,EAAM,IAAI,CAAG,EAE7B,GAAI,CAAC,MAAM,QAAQ,CAAO,EAAG,CAC3B,EAAa,SAAU,CAAO,EAE9B,MACF,CAEA,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,CAC3B,EAAa,UAAW,CAAO,EAE/B,MACF,CAEA,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,CAC3B,EAAa,OAAQ,CAAO,EAE5B,MACF,CAEA,IAAM,EAAO,CAAC,GAAG,CAAO,EAExB,CAAC,EAAK,GAAI,EAAK,IAAM,CAAC,EAAK,GAAI,EAAK,EAAE,EACtC,EAAI,EAAM,CAA8B,CAC1C,CACF,CACF"}
|
package/dist/core/array.d.ts
DELETED
|
@@ -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
|
package/dist/core/array.d.ts.map
DELETED
|
@@ -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;AAMlE,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,CA8G/C"}
|
package/dist/core/array.js
DELETED
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import { devOnly as e, warn as t } from "../_dev.js";
|
|
2
|
-
import { sanitizeForLog as n } from "../_utils.js";
|
|
3
|
-
//#region src/core/array.ts
|
|
4
|
-
function r(r, i, a) {
|
|
5
|
-
let o = r;
|
|
6
|
-
function s(r, i) {
|
|
7
|
-
e(() => {
|
|
8
|
-
t(`array('${n(o, 80)}').${r}(): field is not an array (got ${i === null ? "null" : typeof i}) — no-op.`);
|
|
9
|
-
});
|
|
10
|
-
}
|
|
11
|
-
return {
|
|
12
|
-
append(e) {
|
|
13
|
-
let t = i.get(o);
|
|
14
|
-
if (t !== void 0 && !Array.isArray(t)) {
|
|
15
|
-
s("append", t);
|
|
16
|
-
return;
|
|
17
|
-
}
|
|
18
|
-
a(r, Array.isArray(t) ? [...t, e] : [e]);
|
|
19
|
-
},
|
|
20
|
-
insert(e, t) {
|
|
21
|
-
let n = i.get(o);
|
|
22
|
-
if (!Array.isArray(n)) {
|
|
23
|
-
s("insert", n);
|
|
24
|
-
return;
|
|
25
|
-
}
|
|
26
|
-
let c = [...n];
|
|
27
|
-
c.splice(e, 0, t), a(r, c);
|
|
28
|
-
},
|
|
29
|
-
move(e, t) {
|
|
30
|
-
let n = i.get(o);
|
|
31
|
-
if (!Array.isArray(n)) {
|
|
32
|
-
s("move", n);
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
35
|
-
let c = [...n];
|
|
36
|
-
c.splice(t, 0, c.splice(e, 1)[0]), a(r, c);
|
|
37
|
-
},
|
|
38
|
-
prepend(e) {
|
|
39
|
-
let t = i.get(o);
|
|
40
|
-
if (t !== void 0 && !Array.isArray(t)) {
|
|
41
|
-
s("prepend", t);
|
|
42
|
-
return;
|
|
43
|
-
}
|
|
44
|
-
a(r, Array.isArray(t) ? [e, ...t] : [e]);
|
|
45
|
-
},
|
|
46
|
-
remove(e) {
|
|
47
|
-
let t = i.get(o);
|
|
48
|
-
if (!Array.isArray(t)) {
|
|
49
|
-
s("remove", t);
|
|
50
|
-
return;
|
|
51
|
-
}
|
|
52
|
-
a(r, t.filter((t, n) => n !== e));
|
|
53
|
-
},
|
|
54
|
-
replace(e, t) {
|
|
55
|
-
let n = i.get(o);
|
|
56
|
-
if (!Array.isArray(n)) {
|
|
57
|
-
s("replace", n);
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
let c = [...n];
|
|
61
|
-
c[e] = t, a(r, c);
|
|
62
|
-
},
|
|
63
|
-
swap(e, t) {
|
|
64
|
-
let n = i.get(o);
|
|
65
|
-
if (!Array.isArray(n)) {
|
|
66
|
-
s("swap", n);
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
let c = [...n];
|
|
70
|
-
[c[e], c[t]] = [c[t], c[e]], a(r, c);
|
|
71
|
-
}
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
//#endregion
|
|
75
|
-
export { r as createArrayField };
|
|
76
|
-
|
|
77
|
-
//# sourceMappingURL=array.js.map
|
package/dist/core/array.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"array.js","names":[],"sources":["../../src/core/array.ts"],"sourcesContent":["import type { ArrayField, FlatKeyOf, TypeAtPath } from '../types';\n\nimport { devOnly, warn } from '../_dev';\nimport { sanitizeForLog } from '../_utils';\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 // Every mutating method below no-ops when the field isn't (yet) an array, instead of\n // throwing — a field can legitimately not exist yet. But a silent no-op with zero signal\n // is a debugging trap for the common mistake (typo'd field name, forgot to append() first\n // to seed the array). Warn once per call in dev so it's discoverable without being a\n // breaking runtime error for the legitimate \"not initialized yet\" case.\n function warnNotArray(apiLabel: string, current: unknown): void {\n devOnly(() => {\n warn(\n `array('${sanitizeForLog(key, 80)}').${apiLabel}(): field is not an array ` +\n `(got ${current === null ? 'null' : typeof current}) — no-op.`,\n );\n });\n }\n\n return {\n append(value: T) {\n const current = store.get(key);\n\n if (current !== undefined && !Array.isArray(current)) {\n warnNotArray('append', current);\n\n return;\n }\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)) {\n warnNotArray('insert', current);\n\n return;\n }\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)) {\n warnNotArray('move', current);\n\n return;\n }\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)) {\n warnNotArray('prepend', current);\n\n return;\n }\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)) {\n warnNotArray('remove', current);\n\n return;\n }\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)) {\n warnNotArray('replace', current);\n\n return;\n }\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)) {\n warnNotArray('swap', current);\n\n return;\n }\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":";;;AAaA,SAAgB,EACd,GACA,GACA,GAC+C;CAG/C,IAAM,IAAM;CAOZ,SAAS,EAAa,GAAkB,GAAwB;EAC9D,QAAc;GACZ,EACE,UAAU,EAAe,GAAK,EAAE,EAAE,KAAK,EAAS,iCACtC,MAAY,OAAO,SAAS,OAAO,EAAQ,WACvD;EACF,CAAC;CACH;CAEA,OAAO;EACL,OAAO,GAAU;GACf,IAAM,IAAU,EAAM,IAAI,CAAG;GAE7B,IAAI,MAAY,KAAA,KAAa,CAAC,MAAM,QAAQ,CAAO,GAAG;IACpD,EAAa,UAAU,CAAO;IAE9B;GACF;GAEA,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;IAC3B,EAAa,UAAU,CAAO;IAE9B;GACF;GAEA,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;IAC3B,EAAa,QAAQ,CAAO;IAE5B;GACF;GAEA,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;GAE7B,IAAI,MAAY,KAAA,KAAa,CAAC,MAAM,QAAQ,CAAO,GAAG;IACpD,EAAa,WAAW,CAAO;IAE/B;GACF;GAEA,EAAI,GAAO,MAAM,QAAQ,CAAO,IAAI,CAAC,GAAO,GAAG,CAAO,IAAI,CAAC,CAAK,CAA4B;EAC9F;EACA,OAAO,GAAe;GACpB,IAAM,IAAU,EAAM,IAAI,CAAG;GAE7B,IAAI,CAAC,MAAM,QAAQ,CAAO,GAAG;IAC3B,EAAa,UAAU,CAAO;IAE9B;GACF;GAEA,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;IAC3B,EAAa,WAAW,CAAO;IAE/B;GACF;GAEA,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;IAC3B,EAAa,QAAQ,CAAO;IAE5B;GACF;GAEA,IAAM,IAAO,CAAC,GAAG,CAAO;GAGxB,AADA,CAAC,EAAK,IAAI,EAAK,MAAM,CAAC,EAAK,IAAI,EAAK,EAAE,GACtC,EAAI,GAAM,CAA8B;EAC1C;CACF;AACF"}
|
package/dist/core/asyncQueue.cjs
DELETED
|
@@ -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 * Used by `form[Symbol.asyncIterator]()` to turn a `subscribe()` callback into a pull-based\n * `for await` stream without hand-rolling `waitingResolve`/`done` plumbing at the call site.\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
|
-
* Used by `form[Symbol.asyncIterator]()` to turn a `subscribe()` callback into a pull-based
|
|
4
|
-
* `for await` stream without hand-rolling `waitingResolve`/`done` plumbing at the call site.
|
|
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"}
|
package/dist/core/asyncQueue.js
DELETED
|
@@ -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 * Used by `form[Symbol.asyncIterator]()` to turn a `subscribe()` callback into a pull-based\n * `for await` stream without hand-rolling `waitingResolve`/`done` plumbing at the call site.\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"}
|
package/dist/core/context.cjs
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
const e=require("../errors.cjs");require("../types.cjs");const t=require("../_utils.cjs"),n=require("./notifier.cjs");let r=require("@vielzeug/ripple"),i=require("@vielzeug/arsenal");function a(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 o(o){let s=new Map;for(let[e,n]of Object.entries(o.validators??{}))t.assertSafeKey(e),s.set(e,n);let c=a(o.validator),l=typeof o.defaultValues==`function`,u=typeof o.defaultValues==`function`?void 0:o.defaultValues,d=new Map(Object.entries((0,i.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=!1,w=0,T=!1;function E(){b=null}function D(){x=null}function O(){return x??=Object.freeze(Object.fromEntries(p))}function k(){return Object.freeze({errors:O(),isDirty:h.size>0,isLoading:l,isSubmitting:C,isTouched:m.size>0,isValid:p.size===0,isValidating:g.size>0,submitCount:w,touchedFields:Object.freeze([...m]),validatingFields:Object.freeze([...g.keys()])})}function A(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)}}let j=n.createNotifier({buildFieldState:A,computeState:k,invalidateCaches(){E(),D()}});function M(t){if(T)throw new e.ForgeDisposedError(t)}function N(e){M(`batch`);try{(0,r.batch)(e)}catch(e){throw j.requestNotify(),e}}function P(){T=!0,y.abort();for(let e of v.values())e.abort();v.clear(),_.clear(),S.clear(),j.dispose()}return{arrayCache:S,baseline:d,batch:N,get cachedValues(){return b},set cachedValues(e){b=e},computeErrors:O,dirty:h,dispose:P,disposeController:y,get disposed(){return T},ensureNotDisposed:M,fieldCtrls:v,fieldErrors:p,formValidator:c,getFieldSnapshot:j.getFieldSnapshot,getStateSnapshot:j.getStateSnapshot,invalidateErrors:D,invalidateValues:E,get isSubmittingState(){return C},set isSubmittingState(e){C=e},get loadingState(){return l},set loadingState(e){l=e},requestNotify:j.requestNotify,runCtrls:_,store:f,get submitCount(){return w},set submitCount(e){w=e},subscribe:j.subscribe,subscribeField:j.subscribeField,touched:m,validatingRuns:g,validators:s}}exports.createFormContext=o;
|
|
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 } from '@vielzeug/ripple';\n\nimport { assertSafeKey, flattenValues, isSafeKey } from '../_utils';\nimport { ForgeDisposedError } from '../errors';\nimport {\n type ArrayField,\n type FieldState,\n type FieldValidator,\n FORM_ERROR,\n type FormOptions,\n type FormState,\n type FormValidator,\n type SafeParseSchema,\n type SubscribeOptions,\n type Unsubscribe,\n} from '../types';\nimport { createNotifier } from './notifier';\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 for the field/validator Maps + lifecycle primitives, passed to every\n * `createForm()` sub-module factory (`createFieldOps`, `createObserveOps`, `createLifecycleOps`).\n * Mirrors the `ScopeContext` pattern already established in `scope.ts`.\n *\n * Notably absent: signals, subscriptions, and derived-state caching. Those have no dependency\n * on the field/validator Maps below — only on \"what does current state look like,\" computed\n * here via `computeState`/`buildFieldState` — so they live in their own `Notifier` instance\n * (`core/notifier.ts`) instead of this shared bag. `requestNotify`/`getStateSnapshot`/\n * `getFieldSnapshot`/`subscribe`/`subscribeField` below are thin pass-throughs to that\n * notifier, kept on `FormContext` purely so the other ops modules don't need two objects.\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 cachedValues: TValues | null;\n computeErrors(): Readonly<Record<string, string>>;\n dirty: Set<string>;\n dispose(): void;\n disposeController: AbortController;\n disposed: boolean;\n ensureNotDisposed(op?: string): void;\n fieldCtrls: Map<string, AbortController>;\n fieldErrors: Map<string, string>;\n formValidator: FormValidator<TValues> | undefined;\n getFieldSnapshot(name: string): 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 runCtrls: Set<AbortController>;\n store: Map<string, unknown>;\n submitCount: number;\n subscribe(listener: (state: FormState) => void, options?: SubscribeOptions): Unsubscribe;\n subscribeField(name: string, listener: (state: FieldState<unknown>) => void, options?: SubscribeOptions): Unsubscribe;\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: the field/validator Maps,\n * the notifier that turns mutations into signal updates, and the caches derived from both.\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\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 arrayCache = new Map<string, ArrayField>();\n\n /* ---- Submission state ---- */\n\n let isSubmittingState = false;\n let submitCount = 0;\n\n /* ---- Lifecycle ---- */\n\n let disposed = false;\n\n /* ======== Derived state (fed to the notifier below) ======== */\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 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 const notifier = createNotifier({\n buildFieldState,\n computeState,\n invalidateCaches() {\n invalidateValues();\n invalidateErrors();\n },\n });\n\n /* ======== Lifecycle guards ======== */\n\n function ensureNotDisposed(op?: string): void {\n if (disposed) throw new ForgeDisposedError(op);\n }\n\n /* ======== Batch ======== */\n\n function batch(fn: () => void): void {\n ensureNotDisposed('batch');\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 notifier.requestNotify();\n throw e;\n }\n }\n\n function dispose(): void {\n disposed = true;\n disposeController.abort();\n\n for (const ctrl of fieldCtrls.values()) ctrl.abort();\n\n fieldCtrls.clear();\n runCtrls.clear();\n arrayCache.clear();\n notifier.dispose();\n }\n\n return {\n arrayCache,\n baseline,\n batch,\n get cachedValues() {\n return cachedValues;\n },\n set cachedValues(value) {\n cachedValues = value;\n },\n computeErrors,\n dirty,\n dispose,\n disposeController,\n get disposed() {\n return disposed;\n },\n ensureNotDisposed,\n fieldCtrls,\n fieldErrors,\n formValidator,\n getFieldSnapshot: notifier.getFieldSnapshot,\n getStateSnapshot: notifier.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: notifier.requestNotify,\n runCtrls,\n store,\n get submitCount() {\n return submitCount;\n },\n set submitCount(value) {\n submitCount = value;\n },\n subscribe: notifier.subscribe,\n subscribeField: notifier.subscribeField,\n touched,\n validatingRuns,\n validators,\n };\n}\n"],"mappings":"uLAuBA,SAAS,EACP,EACoC,CACpC,GAAI,CAAC,EAAK,OAEV,GAAI,OAAO,GAAQ,WAAY,OAAO,EAEtC,IAAM,EAAS,EAEf,OAAQ,EAAQ,IAAW,CACzB,GAAI,GAAQ,QAAS,OAErB,IAAM,EAAS,EAAO,UAAU,CAAM,EAEtC,GAAI,EAAO,QAAS,OAEpB,IAAM,EAA0C,CAAC,EAEjD,IAAK,IAAM,KAAS,EAAO,MAAM,OAAQ,CACvC,IAAM,EAAM,EAAM,KAAK,KAAK,GAAG,GAAA,QAI1B,EAAA,UAAU,CAAG,IAEb,EAAO,KAAM,EAAO,GAAO,EAAM,SACxC,CAEA,OAAO,CACT,CACF,CAkDA,SAAgB,EACd,EACsB,CAGtB,IAAM,EAAa,IAAI,IAEvB,IAAK,GAAM,CAAC,EAAM,KAAc,OAAO,QAAQ,EAAK,YAAc,CAAC,CAAC,EAClE,EAAA,cAAc,CAAI,EAClB,EAAW,IAAI,EAAM,CAAoC,EAG3D,IAAM,EAAoD,EAAqB,EAAK,SAAS,EAIzF,EAAe,OAAO,EAAK,eAAkB,WAE3C,EAAe,OAAO,EAAK,eAAkB,WAAkC,IAAA,GAArB,EAAK,cAC/D,EAAW,IAAI,IAAqB,OAAO,SAAA,EAAA,EAAA,aAAA,CAAsB,GAAgB,CAAC,CAAC,CAAC,CAAC,EACrF,EAAQ,IAAI,IAAqB,CAAQ,EACzC,EAAc,IAAI,IAClB,EAAU,IAAI,IACd,EAAQ,IAAI,IASZ,EAAiB,IAAI,IACrB,EAAW,IAAI,IACf,EAAa,IAAI,IACjB,EAAoB,IAAI,gBAI1B,EAA+B,KAC/B,EAAwD,KACtD,EAAa,IAAI,IAInB,EAAoB,GACpB,EAAc,EAId,EAAW,GAIf,SAAS,GAAyB,CAChC,EAAe,IACjB,CAEA,SAAS,GAAyB,CAChC,EAAe,IACjB,CAEA,SAAS,GAAkD,CACzD,MAAQ,KAAiB,OAAO,OAAO,OAAO,YAAY,CAAW,CAAC,CACxE,CAEA,SAAS,GAA0B,CACjC,OAAO,OAAO,OAAO,CACnB,OAAQ,EAAc,EACtB,QAAS,EAAM,KAAO,EACtB,UAAW,EACX,aAAc,EACd,UAAW,EAAQ,KAAO,EAC1B,QAAS,EAAY,OAAS,EAC9B,aAAc,EAAe,KAAO,EACpC,cACA,cAAe,OAAO,OAAO,CAAC,GAAG,CAAO,CAAC,EACzC,iBAAkB,OAAO,OAAO,CAAC,GAAG,EAAe,KAAK,CAAC,CAAC,CAC5D,CAAC,CACH,CAEA,SAAS,EAAgB,EAAmC,CAC1D,IAAM,EAAQ,EAAY,IAAI,CAAI,EAElC,MAAO,CACL,MAAO,EAAM,IAAI,CAAI,EACrB,QACA,SAAU,IAAU,IAAA,GACpB,QAAS,EAAQ,IAAI,CAAI,EACzB,MAAO,EAAM,IAAI,CAAI,CACvB,CACF,CAEA,IAAM,EAAW,EAAA,eAAe,CAC9B,kBACA,eACA,kBAAmB,CACjB,EAAiB,EACjB,EAAiB,CACnB,CACF,CAAC,EAID,SAAS,EAAkB,EAAmB,CAC5C,GAAI,EAAU,MAAM,IAAI,EAAA,mBAAmB,CAAE,CAC/C,CAIA,SAAS,EAAM,EAAsB,CACnC,EAAkB,OAAO,EAEzB,GAAI,EACF,EAAA,EAAA,MAAA,CAAY,CAAE,CAChB,OAAS,EAAG,CAKV,MADA,EAAS,cAAc,EACjB,CACR,CACF,CAEA,SAAS,GAAgB,CACvB,EAAW,GACX,EAAkB,MAAM,EAExB,IAAK,IAAM,KAAQ,EAAW,OAAO,EAAG,EAAK,MAAM,EAEnD,EAAW,MAAM,EACjB,EAAS,MAAM,EACf,EAAW,MAAM,EACjB,EAAS,QAAQ,CACnB,CAEA,MAAO,CACL,aACA,WACA,QACA,IAAI,cAAe,CACjB,OAAO,CACT,EACA,IAAI,aAAa,EAAO,CACtB,EAAe,CACjB,EACA,gBACA,QACA,UACA,oBACA,IAAI,UAAW,CACb,OAAO,CACT,EACA,oBACA,aACA,cACA,gBACA,iBAAkB,EAAS,iBAC3B,iBAAkB,EAAS,iBAC3B,mBACA,mBACA,IAAI,mBAAoB,CACtB,OAAO,CACT,EACA,IAAI,kBAAkB,EAAO,CAC3B,EAAoB,CACtB,EACA,IAAI,cAAe,CACjB,OAAO,CACT,EACA,IAAI,aAAa,EAAO,CACtB,EAAe,CACjB,EACA,cAAe,EAAS,cACxB,WACA,QACA,IAAI,aAAc,CAChB,OAAO,CACT,EACA,IAAI,YAAY,EAAO,CACrB,EAAc,CAChB,EACA,UAAW,EAAS,UACpB,eAAgB,EAAS,eACzB,UACA,iBACA,YACF,CACF"}
|
package/dist/core/context.d.ts
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { type FormOptions } from '../types';
|
|
2
|
-
/**
|
|
3
|
-
* Builds the raw mutable state + shared primitives for a form: the field/validator Maps,
|
|
4
|
-
* the notifier that turns mutations into signal updates, and the caches derived from both.
|
|
5
|
-
*/
|
|
6
|
-
export declare function createFormContext<TValues extends Record<string, unknown> = Record<string, unknown>>(init: FormOptions<TValues>): FormContext<TValues>;
|
|
7
|
-
//# 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,EAKL,KAAK,WAAW,EAMjB,MAAM,UAAU,CAAC;AAoFlB;;;GAGG;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,CA0LtB"}
|