attaform 0.27.6 → 0.29.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/abstract.d.mts +5 -4
- package/dist/abstract.mjs +4 -3
- package/dist/chunks/fingerprint.mjs +1 -2
- package/dist/chunks/fingerprint2.mjs +2 -3
- package/dist/dev/abstract.mjs +4 -0
- package/dist/{chunks → dev/chunks}/dev-key-collision-warnings.mjs +0 -1
- package/dist/{chunks → dev/chunks}/devtools.mjs +5 -6
- package/dist/{chunks/fingerprint.cjs → dev/chunks/fingerprint.mjs} +7 -10
- package/dist/{chunks/fingerprint2.cjs → dev/chunks/fingerprint2.mjs} +32 -35
- package/dist/dev/directive.mjs +1 -0
- package/dist/dev/history.mjs +89 -0
- package/dist/dev/index.mjs +5 -0
- package/dist/dev/runtime/plugins/attaform.mjs +40 -0
- package/dist/dev/shared/attaform-dev.A-TpJW0B.mjs +207 -0
- package/dist/dev/shared/attaform-dev.BO-71R54.mjs +820 -0
- package/dist/{shared/attaform.D6tk90Rc.cjs → dev/shared/attaform-dev.BgM6FdtA.mjs} +324 -73
- package/dist/dev/shared/attaform-dev.ButTkwHd.mjs +375 -0
- package/dist/{shared/attaform.BJGA_UOS.mjs → dev/shared/attaform-dev.BxfsuwBR.mjs} +8 -1
- package/dist/{shared/attaform.nMaz2C5P.mjs → dev/shared/attaform-dev.D8aoKb89.mjs} +190 -400
- package/dist/dev/shared/attaform-dev.DD5m2W51.mjs +220 -0
- package/dist/{shared/attaform.CKH2lGdL.mjs → dev/shared/attaform-dev.DHuydLAC.mjs} +3867 -4731
- package/dist/{shared/attaform.DYEr28Pt.mjs → dev/shared/attaform-dev.DrzObAZo.mjs} +6 -7
- package/dist/{shared/attaform.B1ZYt2Li.mjs → dev/shared/attaform-dev.q6UutkCx.mjs} +274 -672
- package/dist/dev/zod-v3.mjs +17 -0
- package/dist/dev/zod-v4.mjs +16 -0
- package/dist/dev/zod.mjs +5 -0
- package/dist/directive.d.mts +20 -0
- package/dist/directive.mjs +1 -0
- package/dist/esbuild.mjs +0 -1
- package/dist/history.d.mts +59 -0
- package/dist/history.mjs +89 -0
- package/dist/index.d.mts +14 -6
- package/dist/index.mjs +5 -4
- package/dist/nuxt.d.mts +10 -10
- package/dist/nuxt.mjs +5 -2
- package/dist/rollup.mjs +0 -1
- package/dist/rspack.d.mts +6 -1
- package/dist/rspack.mjs +0 -1
- package/dist/runtime/components/AttaformDevtoolsPanel.vue +9 -8
- package/dist/runtime/plugins/attaform.mjs +1 -2
- package/dist/shared/{attaform.CoR4mWfv.mjs → attaform.B0dKL8ST.mjs} +309 -46
- package/dist/shared/{attaform.Cs2Rbdlo.mjs → attaform.BK0cgu2K.mjs} +123 -140
- package/dist/shared/{attaform.B9Wyhfro.cjs → attaform.BndjUUYX.mjs} +11 -16
- package/dist/shared/{attaform.CQN9R62B.cjs → attaform.BxfsuwBR.mjs} +9 -4
- package/dist/shared/attaform.C2MVyuxC.mjs +811 -0
- package/dist/shared/{attaform.Fp3sCrVs.d.ts → attaform.CIWLMY-R.d.mts} +2 -2
- package/dist/shared/attaform.CZMAYwSK.mjs +6383 -0
- package/dist/shared/attaform.CZYt_5N_.mjs +207 -0
- package/dist/shared/{attaform.DniawQLP.d.cts → attaform.CsoeRi3P.d.mts} +490 -98
- package/dist/shared/attaform.Cvao8bW5.mjs +358 -0
- package/dist/shared/attaform.D0x-qSXm.mjs +197 -0
- package/dist/shared/{attaform.D8kliaKw.cjs → attaform.D1fnTifF.mjs} +279 -692
- package/dist/shared/{attaform.CHlJLAiv.d.mts → attaform.DDtqHYv1.d.mts} +155 -184
- package/dist/shared/{attaform.Biytrv0-.cjs → attaform.DffRL5pP.mjs} +215 -547
- package/dist/shared/attaform.DhnPb6nK.mjs +0 -1
- package/dist/shared/attaform.KvlEedlF.mjs +0 -1
- package/dist/shared/attaform.axHXVQO7.d.mts +44 -0
- package/dist/shared/attaform.v0fQtWyA.mjs +0 -1
- package/dist/transforms.d.mts +0 -22
- package/dist/transforms.mjs +1 -2
- package/dist/vite.d.mts +30 -9
- package/dist/vite.mjs +27 -4
- package/dist/webpack.d.mts +6 -1
- package/dist/webpack.mjs +0 -1
- package/dist/zod-v3.d.mts +13 -6
- package/dist/zod-v3.mjs +17 -4
- package/dist/zod-v4.d.mts +19 -26
- package/dist/zod-v4.mjs +16 -4
- package/dist/zod.d.mts +4 -3
- package/dist/zod.mjs +5 -4
- package/package.json +38 -20
- package/skills/attaform/SKILL.md +6 -2
- package/skills/attaform/references/custom-components.md +7 -2
- package/skills/attaform/references/errors.md +8 -3
- package/skills/attaform/references/saving.md +77 -0
- package/skills/attaform/references/wizards.md +10 -8
- package/dist/abstract.cjs +0 -48
- package/dist/abstract.cjs.map +0 -1
- package/dist/abstract.d.cts +0 -42
- package/dist/abstract.d.ts +0 -42
- package/dist/abstract.mjs.map +0 -1
- package/dist/chunks/dev-key-collision-warnings.cjs +0 -25
- package/dist/chunks/dev-key-collision-warnings.cjs.map +0 -1
- package/dist/chunks/dev-key-collision-warnings.mjs.map +0 -1
- package/dist/chunks/devtools.cjs +0 -157
- package/dist/chunks/devtools.cjs.map +0 -1
- package/dist/chunks/devtools.mjs.map +0 -1
- package/dist/chunks/fingerprint.cjs.map +0 -1
- package/dist/chunks/fingerprint.mjs.map +0 -1
- package/dist/chunks/fingerprint2.cjs.map +0 -1
- package/dist/chunks/fingerprint2.mjs.map +0 -1
- package/dist/esbuild.cjs +0 -28
- package/dist/esbuild.cjs.map +0 -1
- package/dist/esbuild.d.cts +0 -56
- package/dist/esbuild.d.ts +0 -56
- package/dist/esbuild.mjs.map +0 -1
- package/dist/index.cjs +0 -51
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.cts +0 -249
- package/dist/index.d.ts +0 -249
- package/dist/index.mjs.map +0 -1
- package/dist/nuxt.cjs +0 -84
- package/dist/nuxt.cjs.map +0 -1
- package/dist/nuxt.d.cts +0 -66
- package/dist/nuxt.d.ts +0 -66
- package/dist/nuxt.mjs.map +0 -1
- package/dist/rollup.cjs +0 -24
- package/dist/rollup.cjs.map +0 -1
- package/dist/rollup.d.cts +0 -35
- package/dist/rollup.d.ts +0 -35
- package/dist/rollup.mjs.map +0 -1
- package/dist/rspack.cjs +0 -10
- package/dist/rspack.cjs.map +0 -1
- package/dist/rspack.d.cts +0 -40
- package/dist/rspack.d.ts +0 -40
- package/dist/rspack.mjs.map +0 -1
- package/dist/runtime/components/AttaformDevtoolsPanel.vue.d.ts +0 -7
- package/dist/runtime/components/DevtoolsValueTree.vue.d.ts +0 -35
- package/dist/runtime/plugins/attaform.cjs +0 -43
- package/dist/runtime/plugins/attaform.cjs.map +0 -1
- package/dist/runtime/plugins/attaform.d.cts +0 -5
- package/dist/runtime/plugins/attaform.d.ts +0 -5
- package/dist/runtime/plugins/attaform.mjs.map +0 -1
- package/dist/shared/attaform.B1ZYt2Li.mjs.map +0 -1
- package/dist/shared/attaform.B9Wyhfro.cjs.map +0 -1
- package/dist/shared/attaform.BGDV42hI.d.cts +0 -232
- package/dist/shared/attaform.BJGA_UOS.mjs.map +0 -1
- package/dist/shared/attaform.Biytrv0-.cjs.map +0 -1
- package/dist/shared/attaform.Bn0IzhD_.cjs +0 -800
- package/dist/shared/attaform.Bn0IzhD_.cjs.map +0 -1
- package/dist/shared/attaform.C9fPTxjB.d.cts +0 -41
- package/dist/shared/attaform.C9fPTxjB.d.ts +0 -41
- package/dist/shared/attaform.CKH2lGdL.mjs.map +0 -1
- package/dist/shared/attaform.CQN9R62B.cjs.map +0 -1
- package/dist/shared/attaform.CoR4mWfv.mjs.map +0 -1
- package/dist/shared/attaform.Cs2Rbdlo.mjs.map +0 -1
- package/dist/shared/attaform.D6tk90Rc.cjs.map +0 -1
- package/dist/shared/attaform.D8kliaKw.cjs.map +0 -1
- package/dist/shared/attaform.DLknk9nF.d.mts +0 -232
- package/dist/shared/attaform.DMxM2kol.mjs +0 -1275
- package/dist/shared/attaform.DMxM2kol.mjs.map +0 -1
- package/dist/shared/attaform.DRUT703x.d.cts +0 -56
- package/dist/shared/attaform.DRUT703x.d.ts +0 -56
- package/dist/shared/attaform.DYEr28Pt.mjs.map +0 -1
- package/dist/shared/attaform.DdQN7sgN.cjs +0 -1302
- package/dist/shared/attaform.DdQN7sgN.cjs.map +0 -1
- package/dist/shared/attaform.DhnPb6nK.mjs.map +0 -1
- package/dist/shared/attaform.DniawQLP.d.mts +0 -4763
- package/dist/shared/attaform.DniawQLP.d.ts +0 -4763
- package/dist/shared/attaform.DoQm_Vkd.cjs +0 -58
- package/dist/shared/attaform.DoQm_Vkd.cjs.map +0 -1
- package/dist/shared/attaform.DrvMscNX.cjs +0 -7697
- package/dist/shared/attaform.DrvMscNX.cjs.map +0 -1
- package/dist/shared/attaform.F2Q8iv38.d.ts +0 -2494
- package/dist/shared/attaform.FmqKO1Ex.d.cts +0 -2494
- package/dist/shared/attaform.KvlEedlF.mjs.map +0 -1
- package/dist/shared/attaform.RaTzp1WF.cjs +0 -26
- package/dist/shared/attaform.RaTzp1WF.cjs.map +0 -1
- package/dist/shared/attaform.nMaz2C5P.mjs.map +0 -1
- package/dist/shared/attaform.v0fQtWyA.mjs.map +0 -1
- package/dist/shared/attaform.yUrz7C2z.cjs +0 -29
- package/dist/shared/attaform.yUrz7C2z.cjs.map +0 -1
- package/dist/transforms.cjs +0 -12
- package/dist/transforms.cjs.map +0 -1
- package/dist/transforms.d.cts +0 -73
- package/dist/transforms.d.ts +0 -73
- package/dist/transforms.mjs.map +0 -1
- package/dist/vite.cjs +0 -363
- package/dist/vite.cjs.map +0 -1
- package/dist/vite.d.cts +0 -133
- package/dist/vite.d.ts +0 -133
- package/dist/vite.mjs.map +0 -1
- package/dist/webpack.cjs +0 -10
- package/dist/webpack.cjs.map +0 -1
- package/dist/webpack.d.cts +0 -37
- package/dist/webpack.d.ts +0 -37
- package/dist/webpack.mjs.map +0 -1
- package/dist/zod-v3.cjs +0 -53
- package/dist/zod-v3.cjs.map +0 -1
- package/dist/zod-v3.d.cts +0 -246
- package/dist/zod-v3.d.ts +0 -246
- package/dist/zod-v3.mjs.map +0 -1
- package/dist/zod-v4.cjs +0 -55
- package/dist/zod-v4.cjs.map +0 -1
- package/dist/zod-v4.d.cts +0 -172
- package/dist/zod-v4.d.ts +0 -172
- package/dist/zod-v4.mjs.map +0 -1
- package/dist/zod.cjs +0 -51
- package/dist/zod.cjs.map +0 -1
- package/dist/zod.d.cts +0 -7
- package/dist/zod.d.ts +0 -7
- package/dist/zod.mjs.map +0 -1
package/package.json
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "attaform",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"main": "./dist/index.mjs",
|
|
3
|
+
"version": "0.29.0",
|
|
5
4
|
"types": "./dist/index.d.mts",
|
|
6
5
|
"files": [
|
|
7
6
|
"dist",
|
|
7
|
+
"!dist/**/*.map",
|
|
8
|
+
"!dist/**/*.cjs",
|
|
9
|
+
"!dist/**/*.d.cts",
|
|
10
|
+
"!dist/**/*.d.ts",
|
|
8
11
|
"skills",
|
|
9
12
|
"bin"
|
|
10
13
|
],
|
|
@@ -34,12 +37,12 @@
|
|
|
34
37
|
"exports": {
|
|
35
38
|
".": {
|
|
36
39
|
"types": "./dist/index.d.mts",
|
|
40
|
+
"development": "./dist/dev/index.mjs",
|
|
37
41
|
"import": "./dist/index.mjs"
|
|
38
42
|
},
|
|
39
43
|
"./nuxt": {
|
|
40
44
|
"types": "./dist/nuxt.d.mts",
|
|
41
|
-
"import": "./dist/nuxt.mjs"
|
|
42
|
-
"require": "./dist/nuxt.cjs"
|
|
45
|
+
"import": "./dist/nuxt.mjs"
|
|
43
46
|
},
|
|
44
47
|
"./vite": {
|
|
45
48
|
"types": "./dist/vite.d.mts",
|
|
@@ -70,22 +73,33 @@
|
|
|
70
73
|
},
|
|
71
74
|
"./zod": {
|
|
72
75
|
"types": "./dist/zod.d.mts",
|
|
76
|
+
"development": "./dist/dev/zod.mjs",
|
|
73
77
|
"import": "./dist/zod.mjs"
|
|
74
78
|
},
|
|
75
79
|
"./zod-v3": {
|
|
76
80
|
"types": "./dist/zod-v3.d.mts",
|
|
81
|
+
"development": "./dist/dev/zod-v3.mjs",
|
|
77
82
|
"import": "./dist/zod-v3.mjs"
|
|
78
83
|
},
|
|
79
84
|
"./zod-v4": {
|
|
80
85
|
"types": "./dist/zod-v4.d.mts",
|
|
86
|
+
"development": "./dist/dev/zod-v4.mjs",
|
|
81
87
|
"import": "./dist/zod-v4.mjs"
|
|
82
88
|
},
|
|
83
89
|
"./abstract": {
|
|
84
90
|
"types": "./dist/abstract.d.mts",
|
|
91
|
+
"development": "./dist/dev/abstract.mjs",
|
|
85
92
|
"import": "./dist/abstract.mjs"
|
|
86
93
|
},
|
|
87
|
-
"./
|
|
88
|
-
"types": "./dist/
|
|
94
|
+
"./directive": {
|
|
95
|
+
"types": "./dist/directive.d.mts",
|
|
96
|
+
"development": "./dist/dev/directive.mjs",
|
|
97
|
+
"import": "./dist/directive.mjs"
|
|
98
|
+
},
|
|
99
|
+
"./history": {
|
|
100
|
+
"types": "./dist/history.d.mts",
|
|
101
|
+
"development": "./dist/dev/history.mjs",
|
|
102
|
+
"import": "./dist/history.mjs"
|
|
89
103
|
},
|
|
90
104
|
"./package.json": "./package.json"
|
|
91
105
|
},
|
|
@@ -116,17 +130,18 @@
|
|
|
116
130
|
"@commitlint/config-conventional": "^21.0.1",
|
|
117
131
|
"@eslint/js": "^10.0.1",
|
|
118
132
|
"@fast-check/vitest": "^0.4.0",
|
|
133
|
+
"@nuxt/devtools": "^3.4.2",
|
|
119
134
|
"@nuxt/devtools-kit": "^3.2.0",
|
|
120
135
|
"@nuxt/kit": "^4.4.2",
|
|
121
136
|
"@nuxt/schema": "^4.4.2",
|
|
122
137
|
"@nuxt/test-utils": "^4.0.2",
|
|
123
|
-
"@nuxt/ui": "^4.
|
|
138
|
+
"@nuxt/ui": "^4.11.1",
|
|
124
139
|
"@rollup/plugin-alias": "^6.0.0",
|
|
125
140
|
"@size-limit/preset-small-lib": "^12.1.0",
|
|
126
|
-
"@types/jsdom": "^
|
|
127
|
-
"@types/node": "^
|
|
141
|
+
"@types/jsdom": "^30.0.0",
|
|
142
|
+
"@types/node": "^26.5.1",
|
|
128
143
|
"@vitejs/plugin-vue": "^6.0.6",
|
|
129
|
-
"@vitest/coverage-v8": "^4.1.
|
|
144
|
+
"@vitest/coverage-v8": "^4.1.11",
|
|
130
145
|
"@vue/compiler-core": "^3.5.33",
|
|
131
146
|
"@vue/compiler-sfc": "^3.5.33",
|
|
132
147
|
"@vue/devtools-api": "^8.1.1",
|
|
@@ -143,19 +158,20 @@
|
|
|
143
158
|
"fast-check": "^4.7.0",
|
|
144
159
|
"globals": "^17.5.0",
|
|
145
160
|
"husky": "^9.1.7",
|
|
146
|
-
"jsdom": "^
|
|
161
|
+
"jsdom": "^30.0.1",
|
|
147
162
|
"lint-staged": "^17.0.4",
|
|
148
|
-
"nuxt": "^4.
|
|
163
|
+
"nuxt": "^4.5.2",
|
|
149
164
|
"prettier": "^3.8.3",
|
|
150
|
-
"primevue": "
|
|
151
|
-
"reka-ui": "^2.
|
|
165
|
+
"primevue": "4.5.5",
|
|
166
|
+
"reka-ui": "^2.10.4",
|
|
167
|
+
"rollup": "^4.63.1",
|
|
152
168
|
"rollup-plugin-dts": "^6.4.1",
|
|
153
169
|
"size-limit": "^12.1.0",
|
|
154
170
|
"typescript": "6.0.3",
|
|
155
171
|
"typescript-eslint": "^8.59.0",
|
|
156
172
|
"unbuild": "^3.6.1",
|
|
157
|
-
"vite": "^8.0
|
|
158
|
-
"vitest": "^4.1.
|
|
173
|
+
"vite": "^8.3.0",
|
|
174
|
+
"vitest": "^4.1.11",
|
|
159
175
|
"vue": "^3.5.33",
|
|
160
176
|
"vue-eslint-parser": "^10.4.0",
|
|
161
177
|
"vue-tsc": "^3.2.7",
|
|
@@ -194,11 +210,11 @@
|
|
|
194
210
|
"dev:prepare": "unbuild --stub && nuxi prepare apps/site",
|
|
195
211
|
"dev:site": "pnpm --filter attaform-site dev",
|
|
196
212
|
"build:site": "pnpm --filter attaform-site build",
|
|
197
|
-
"lint": "eslint --cache --concurrency=auto . && pnpm format:check",
|
|
198
|
-
"lint:fix": "eslint --cache --concurrency=auto --fix . && pnpm format",
|
|
213
|
+
"lint": "eslint --cache --concurrency=auto --max-warnings 0 . && pnpm format:check",
|
|
214
|
+
"lint:fix": "eslint --cache --concurrency=auto --max-warnings 0 --fix . && pnpm format",
|
|
199
215
|
"format": "prettier --write '**/*.{ts,js,vue,css,scss,md,json,yml,yaml}'",
|
|
200
216
|
"format:check": "prettier --check '**/*.{ts,js,vue,css,scss,md,json,yml,yaml}'",
|
|
201
|
-
"check": "pnpm lint && pnpm typecheck && pnpm check:site && pnpm test && pnpm test:nuxt-ui && pnpm check:size && pnpm check:eager && pnpm check:bench && pnpm check:coverage",
|
|
217
|
+
"check": "pnpm check:security-floors && pnpm lint && pnpm typecheck && pnpm check:site && pnpm test && pnpm test:nuxt-ui && pnpm check:size && pnpm check:tarball && pnpm check:eager && pnpm check:bench && pnpm check:coverage",
|
|
202
218
|
"fix": "pnpm lint:fix",
|
|
203
219
|
"typecheck": "tsc --noEmit",
|
|
204
220
|
"prepack:watch": "chokidar 'src/**/*' -c 'pnpm prepack' --initial",
|
|
@@ -208,7 +224,9 @@
|
|
|
208
224
|
"test:types": "pnpm dev:prepare && vue-tsc --noEmit && cd apps/site && vue-tsc --noEmit",
|
|
209
225
|
"test:watch": "node scripts/run-with-webstorage-flag.mjs vitest watch",
|
|
210
226
|
"bench": "node scripts/run-with-webstorage-flag.mjs vitest bench --run",
|
|
227
|
+
"check:security-floors": "node scripts/check-security-floors.mjs",
|
|
211
228
|
"check:size": "pnpm prepack && size-limit",
|
|
229
|
+
"check:tarball": "node scripts/check-tarball-size.mjs",
|
|
212
230
|
"check:eager": "node scripts/check-eager-size.mjs",
|
|
213
231
|
"check:coverage": "node scripts/run-with-webstorage-flag.mjs vitest run --coverage",
|
|
214
232
|
"check:bench": "node scripts/check-bench.mjs",
|
|
@@ -216,6 +234,6 @@
|
|
|
216
234
|
"check:doc-snippets": "node scripts/check-doc-snippets.mjs",
|
|
217
235
|
"check:site": "pnpm dev:prepare && pnpm --filter attaform-site typecheck",
|
|
218
236
|
"fallow": "DO_NOT_TRACK=1 FALLOW_TELEMETRY=0 npx --yes fallow@2.88.3 --score",
|
|
219
|
-
"version": "node scripts/promote-changelog.mjs && (node scripts/generate-release-notes.mjs || echo '[version] release-notes step failed; continuing') && node scripts/sync-doc-versions.mjs && git add CHANGELOG.md RELEASES.md docs/scorecard/cii-best-practices-answers.md"
|
|
237
|
+
"version": "node scripts/promote-changelog.mjs && (node scripts/generate-release-notes.mjs || echo '[version] release-notes step failed; continuing') && node scripts/sync-doc-versions.mjs && node scripts/build-bestpractices-json.mjs && git add CHANGELOG.md RELEASES.md docs/scorecard/cii-best-practices-answers.md .bestpractices.json"
|
|
220
238
|
}
|
|
221
239
|
}
|
package/skills/attaform/SKILL.md
CHANGED
|
@@ -24,13 +24,15 @@ import {
|
|
|
24
24
|
fieldMeta,
|
|
25
25
|
withMeta,
|
|
26
26
|
lazy,
|
|
27
|
+
gate,
|
|
27
28
|
} from 'attaform'
|
|
28
29
|
```
|
|
29
30
|
|
|
30
31
|
- **Do not import `useForm` from `attaform/abstract`.** That entry is the bring-your-own-adapter escape hatch: it exports `useAbstractForm`, which needs a schema adapter wired by hand. A Zod project wants `useForm` from `attaform`. This mismatch is the single most common first-try mistake.
|
|
31
32
|
- Types come from the same barrel: `import type { AnyForm, WizardCtx, Json, ValidationError } from 'attaform'`.
|
|
32
33
|
- `attaform/zod` is the same surface named explicitly; `attaform/zod-v3` and `attaform/zod-v4` pin a Zod major. Any of these works; default to `attaform`.
|
|
33
|
-
- In **Nuxt** with the module installed (`attaform/nuxt`),
|
|
34
|
+
- In **Nuxt** with the module installed (`attaform/nuxt`), the whole block above auto-imports and the `v-register` directive is bound into each compiled template, so a component needs no import lines at all. Writing them anyway is always safe and always portable: an explicit import beats an auto-import of the same name, which is why every snippet below keeps its import line. In **plain Vite**, add the Attaform plugin from `attaform/vite` (it binds `v-register` the same way) plus the auto-import preset it exports. Without either build plugin, register the directive once per app: `import { installVRegister } from 'attaform/directive'` then `installVRegister(app)`.
|
|
35
|
+
- Undo/redo ships from its own entry: `import { historyPlugin } from 'attaform/history'`, then `useForm({ schema, history: historyPlugin({ max: 200 }) })` unlocks a live `form.history` namespace (`undo` / `redo` / `clear` / `canUndo` / `canRedo` / `size`). Without the plugin the namespace is inert stubs.
|
|
34
36
|
|
|
35
37
|
## Build a form
|
|
36
38
|
|
|
@@ -85,7 +87,8 @@ Prefer an explicit `key` (`useForm({ schema, key: 'sign-in' })`): it makes the f
|
|
|
85
87
|
- **Reach with `?.` on injected forms.** `injectForm()` and `injectWizard()` return `T | null`; chain optional access (`form?.register('email')`) at every consumption site.
|
|
86
88
|
- **Put labels on the schema, not the template.** `z.string().register(fieldMeta, { label: 'Email' })`; read it back through `form.fields.email.label` (resolved, with a humanized-path fallback).
|
|
87
89
|
- **Native inputs first.** Bind `<input>`, `<select>`, `<textarea>` with `v-register`. Reach for `useRegister` only inside a custom input component. See `references/custom-components.md`.
|
|
88
|
-
- **`v-register` alone does binding, SSR value injection, and ARIA.** Do not stack
|
|
90
|
+
- **`v-register` alone does binding, SSR value injection, and ARIA.** Do not stack a second _writer_ on it: no `v-model`, no handler that writes the field back, no reset-signal prop. If a control seems to need that scaffolding, the idiomatic shape is being missed.
|
|
91
|
+
- **Observing the field is fine, and is how a per-interaction save is built.** A `@change` that only reads, or a `watch(form.toRef('path'), ...)`, sits beside `v-register` without fighting it: the directive writes the field before your handler runs, so the value you read is the committed one. Reach for this whenever a surface persists each decision as it is made rather than on submit. See `references/saving.md`.
|
|
89
92
|
|
|
90
93
|
## Wizards
|
|
91
94
|
|
|
@@ -119,6 +122,7 @@ Load one when the task reaches its area:
|
|
|
119
122
|
- **`references/custom-components.md`**: wrapping an input or binding a third-party component. `useRegister`, the three orthogonal primitives, attribute fallthrough, and the optional `form-key` prop.
|
|
120
123
|
- **`references/ssr.md`**: debugging SSR and hydration. Why the value injection is a build-time transform, how to confirm it faithfully, and the test traps to avoid.
|
|
121
124
|
- **`references/validation.md`**: designing the schema. Client-is-UX / server-is-truth, keeping closed sets in sync, and why a clearable edit field is a required string.
|
|
125
|
+
- **`references/saving.md`**: persisting each decision as it is made. Choosing between an observing listener and a `watch`, the ordering that makes a listener safe, and the four things a production autosave adds.
|
|
122
126
|
|
|
123
127
|
## Going deeper
|
|
124
128
|
|
|
@@ -49,11 +49,16 @@ For a **compound** component that binds _multiple_ paths (a date range exposing
|
|
|
49
49
|
|
|
50
50
|
- Read the display signals straight in the template: `field.showErrors` gates the error row, `field.showPending` gates an async "checking" indicator (it is anti-flash timed), `field.firstError?.message` is the text.
|
|
51
51
|
- `v-register`'s `autoAria` (on by default) keeps `aria-invalid`, `aria-busy`, and `aria-required` in sync. It wires the _error_ id, and only while the field is in its error state, so author `aria-describedby` yourself if a _static_ hint should stay associated too.
|
|
52
|
-
- **`v-register` alone does binding, SSR value injection, and ARIA.** Do not stack
|
|
52
|
+
- **`v-register` alone does binding, SSR value injection, and ARIA.** Do not stack a second _writer_ on it: no `v-model`, no handler that writes the field back, no `:reset-signal` prop, no redundant directive import. If a control seems to need that scaffolding, find the idiomatic shape rather than hand-rolling around it.
|
|
53
|
+
- **An _observer_ is not a second writer.** A `@change` that only reads, or a `watch(form.toRef('path'), ...)`, is supported beside `v-register`. The directive attaches its own listener in the `created` hook, before Vue applies yours, so it writes the field first and your handler reads committed state. That is the shape for a surface that saves each decision as it is made instead of on submit; see `references/saving.md`.
|
|
53
54
|
|
|
54
55
|
## Third-party components
|
|
55
56
|
|
|
56
|
-
`v-register` binds a third-party component host, not just a native element
|
|
57
|
+
`v-register` binds a third-party component host, not just a native element. The directive marks the host and injects the same binding, SSR, and ARIA it gives a native input.
|
|
58
|
+
|
|
59
|
+
When the component renders exactly one real form control and forwards attributes to it, that control becomes the field's anchor: focus and blur tracking, ARIA, and the invalid-submit focus target all land on it. A **composite** widget (a PIN input of several boxes) or a **control-less** one (a slider built from `<div>`s) binds too; the directive declines the single-element latch and tracks focus at the widget root instead. Do not reach for an escape hatch on account of either.
|
|
60
|
+
|
|
61
|
+
The one shape that loses the directive half is a component whose root is a **fragment**, because Vue hands a runtime directive only to a single-element root. The value channel still works there; wrap it in an element you control to get the rest back.
|
|
57
62
|
|
|
58
63
|
## Do not re-declare native attributes as props
|
|
59
64
|
|
|
@@ -17,7 +17,8 @@ const onSubmit = form.handleSubmit(async (values) => {
|
|
|
17
17
|
})
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
-
- `setErrors(errors | updater | (path, errors))` is a **whole-layer replace
|
|
20
|
+
- `setErrors(errors | updater | (path, errors))` is a **whole-layer replace** of the user layer, so `clearErrors()` at the top of a fresh attempt drops errors a previous submit set. `clearErrors(path?)` scopes the clear.
|
|
21
|
+
- `clearErrors` clears the **schema layer too**, at whatever path it targets, not just the user one. Inside a submit callback that costs nothing, since validation has already passed to get there, and with always-on validation the schema half re-populates on the next mutation if the value is still wrong. Pass `setErrors([])` instead when you mean to empty the user layer alone.
|
|
21
22
|
- `handleSubmit` focuses the first offending field for user-set errors exactly as it does for client-invalid fields, so a `setErrors` call inside the callback needs no separate focus step.
|
|
22
23
|
|
|
23
24
|
## The `ValidationError` envelope
|
|
@@ -27,7 +28,11 @@ If your backend emits Attaform's `ValidationError` shape, it pipes straight into
|
|
|
27
28
|
```ts
|
|
28
29
|
import type { ValidationError, Json } from 'attaform'
|
|
29
30
|
|
|
30
|
-
//
|
|
31
|
+
// ValidationError, what you READ BACK:
|
|
32
|
+
// { message: string; path: (string | number)[]; code: string; data?: Json | null }
|
|
33
|
+
// What setErrors ACCEPTS is looser: every field optional, plus a bare
|
|
34
|
+
// Error. A missing message becomes 'Unknown error', a missing path is
|
|
35
|
+
// [] (form level), a missing code is 'atta:user-error'.
|
|
31
36
|
```
|
|
32
37
|
|
|
33
38
|
- One entry per message at `path: [field]`; a form-level error is `path: []`; a dotted key splits into segments.
|
|
@@ -55,7 +60,7 @@ A form-level banner wants the root's **own** bucket, not the aggregate:
|
|
|
55
60
|
const banner = computed(() => form.meta.firstOwnError)
|
|
56
61
|
```
|
|
57
62
|
|
|
58
|
-
Do not use `form.errors([])` for a banner: `errors(path)` uniformly means "path plus descendants", so `errors([])` is the whole-form aggregate and would surface individual field errors in the summary. `form.meta.firstOwnError` is the correct root-only read. The same own axis surfaces a container-level `.refine()` error,
|
|
63
|
+
Do not use `form.errors([])` for a banner: `errors(path)` uniformly means "path plus descendants", so `errors([])` is the whole-form aggregate and would surface individual field errors in the summary. `form.meta.firstOwnError` is the correct root-only read. The same own axis surfaces a container-level `.refine()` error, through the CALL form: `form.fields('address').firstOwnError`. Dot access on a container (`form.fields.address`) is navigation toward leaves and carries no error state of its own, so the dotted spelling reads `undefined` no matter what the container holds.
|
|
59
64
|
|
|
60
65
|
## One normalizer, always non-empty
|
|
61
66
|
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Saving as the user goes
|
|
2
|
+
|
|
3
|
+
A surface that persists each decision as it is made, instead of on a submit, is a supported shape. It needs no scaffolding around `v-register` and no redesign toward a batch Save button. Two shapes fit it, and the one to reach for follows what the reaction belongs to.
|
|
4
|
+
|
|
5
|
+
- The reaction belongs to the **interaction** (one pick is one whole decision: a `<select>`, a checkbox, a radio, a star rating) → a plain listener beside `v-register`.
|
|
6
|
+
- The reaction belongs to the **value** (typing, or anything that must also catch a programmatic write) → `watch(form.toRef(path), ...)`.
|
|
7
|
+
|
|
8
|
+
The deciding fact when a form both saves per change and hydrates itself from a saved record: a DOM listener never sees `form.setValue` or `form.reset()`, because neither fires a DOM event. Only the watch does.
|
|
9
|
+
|
|
10
|
+
## The listener shape
|
|
11
|
+
|
|
12
|
+
```vue
|
|
13
|
+
<script setup lang="ts">
|
|
14
|
+
import { z } from 'zod'
|
|
15
|
+
import { useForm } from 'attaform'
|
|
16
|
+
|
|
17
|
+
const schema = z.object({ choice: z.string() })
|
|
18
|
+
const form = useForm({ schema })
|
|
19
|
+
|
|
20
|
+
async function save() {
|
|
21
|
+
// Already the newly picked value: the directive wrote it first.
|
|
22
|
+
await fetch('/api/choice', {
|
|
23
|
+
method: 'PATCH',
|
|
24
|
+
body: JSON.stringify({ value: form.values.choice }),
|
|
25
|
+
})
|
|
26
|
+
}
|
|
27
|
+
</script>
|
|
28
|
+
|
|
29
|
+
<template>
|
|
30
|
+
<select v-register="form.register('choice')" @change="save">
|
|
31
|
+
<option value="yes">Yes</option>
|
|
32
|
+
<option value="no">No</option>
|
|
33
|
+
</select>
|
|
34
|
+
</template>
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
`v-register` attaches its own listener in Vue's `created` hook, which runs before Vue applies the handlers you wrote. The directive's listener is therefore registered first, fires first, and has written the field by the time yours is called, so the handler reads committed state rather than the previous value. That ordering is a pinned contract, not an accident of timing, and it holds for `@change`, `@input`, a `v-register.lazy` control, a checkbox, and a radio.
|
|
38
|
+
|
|
39
|
+
An observing handler is **not** a second writer. Nothing is stacked, and neither the compile-time nor the runtime redundant-binding guard flags it. What stays forbidden is a second _writer_: a `v-model`, a handler that writes the field back, a reset-signal prop.
|
|
40
|
+
|
|
41
|
+
## The watch shape
|
|
42
|
+
|
|
43
|
+
```ts
|
|
44
|
+
import { watch } from 'vue'
|
|
45
|
+
import { z } from 'zod'
|
|
46
|
+
import { useForm } from 'attaform'
|
|
47
|
+
|
|
48
|
+
const schema = z.object({ bio: z.string() })
|
|
49
|
+
const form = useForm({ schema })
|
|
50
|
+
|
|
51
|
+
async function save(value: string): Promise<void> {
|
|
52
|
+
await fetch('/api/bio', { method: 'PATCH', body: JSON.stringify({ value }) })
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// `form.toRef(path)` is a read-only ref over that path's value, so this
|
|
56
|
+
// fires on every committed change, including `setValue` and `reset`.
|
|
57
|
+
watch(form.toRef('bio'), (value) => {
|
|
58
|
+
void save(value)
|
|
59
|
+
})
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Registered inside `setup`, so the watchers stop on unmount with no manual teardown. Writing the same value back is a no-op in Attaform, so a watcher never fires for a write that changed nothing. For a whole container path, add `{ deep: true }`: an in-place leaf edit keeps the container's reference stable.
|
|
63
|
+
|
|
64
|
+
## What a real one adds
|
|
65
|
+
|
|
66
|
+
The full recipe at https://attaform.dev/docs/cross-cutting-state/autosave is copy-paste and carries the parts a production autosave needs. Reproduce these rather than shipping the bare watch above:
|
|
67
|
+
|
|
68
|
+
- **Debounce per path**, because typing is continuous and a keystroke is not a decision. For a per-interaction surface the wait is pointless: keep the recipe and pass `{ debounceMs: 0 }`.
|
|
69
|
+
- **A validity gate.** Run `form.parse(path, { commit: true })` and skip the write when it fails, so an invalid value never reaches the server and async refinements run as part of the same check. Turn it off per path for a true draft save.
|
|
70
|
+
- **Abort on supersede.** One `AbortController` per path, handed to `fetch` as its `signal`, so a slow request cancels when a newer edit lands and the latest write wins.
|
|
71
|
+
- **A pause for your own writes.** This is the one that bites. A watch reacts to every change including yours, so hydrating ten fields from a saved record echoes ten saves straight back at the server you just loaded from. Wrap a hydrating `setValue` or `reset` so the watchers skip it. The pause belongs to your composable, never to the form.
|
|
72
|
+
|
|
73
|
+
## Do not trade away the rendering to get the saving
|
|
74
|
+
|
|
75
|
+
Both shapes leave `v-register` in place, so the control keeps its binding, its ARIA, and its SSR value injection. That last one is worth naming, because a row of server-rendered `<select>` elements is where it shows: a selection is a DOM property rather than markup, so the server can only express it as `selected` on the right `<option>`, and hand-binding that per call site is a step every new row can forget. `v-register` emits it for every option it owns.
|
|
76
|
+
|
|
77
|
+
Saving per decision never costs you that. Guidance that implies otherwise, and sends an author to a batch submit to keep their server-rendered state correct, is wrong.
|
|
@@ -12,6 +12,8 @@ A step slot is one of:
|
|
|
12
12
|
- a **function** returning any of those, for runtime branching,
|
|
13
13
|
- a `lazy((ctx) => ...)`-wrapped function, which memoizes its resolution and re-fires only when its own tracked reads change.
|
|
14
14
|
|
|
15
|
+
`gate(slot)` wraps any of them rather than being a kind of its own: it marks that position a hard prerequisite and leaves how the slot resolves alone. A function slot may return one, so whether a position is a gate can itself be a runtime decision.
|
|
16
|
+
|
|
15
17
|
```ts
|
|
16
18
|
import { useForm, useWizard } from 'attaform'
|
|
17
19
|
|
|
@@ -24,7 +26,7 @@ const wizard = useWizard({ key: 'onboarding', steps: [account, 'review', profile
|
|
|
24
26
|
## Navigation vs submission
|
|
25
27
|
|
|
26
28
|
- **`wizard.tryNext(): Promise<boolean>`** is the gated Next. It validates the active step and advances only on a clean pass, revealing that step's errors in place otherwise. It resolves to whether it advanced, and it is inline-bindable: `@click="wizard.tryNext()"`.
|
|
27
|
-
- **`wizard.next()` / `wizard.back()` / `wizard.goTo(key)`** are positional moves with no validation gate. Use them for a Back button or a jump; use `tryNext` for a forward move that should validate.
|
|
29
|
+
- **`wizard.next()` / `wizard.back()` / `wizard.goTo(key)`** are positional moves with no validation gate. Use them for a Back button or a jump; use `tryNext` for a forward move that should validate. Two exceptions: they refuse while a submit is in flight, and `next()` on an **uncleared `gate()` step behaves as `tryNext()`**, so wiring Next straight to it can never skip the gate's confirmation. Once that gate clears, `next()` is plain navigation again and does not re-submit.
|
|
28
30
|
- **`wizard.handleSubmit(onSubmit, onError?)`** validates **every** step from any position and calls `onSubmit` once with all forms' values. It **never advances**. Wire it to the final Submit.
|
|
29
31
|
|
|
30
32
|
For a forward move that runs a custom callback before advancing, compose the step form's own submit with `next`:
|
|
@@ -58,9 +60,9 @@ const onComplete = wizard.handleSubmit(async (ctx) => {
|
|
|
58
60
|
|
|
59
61
|
The wizard handle exposes the flow's rolled-up state, all reactive:
|
|
60
62
|
|
|
61
|
-
- `wizard.forms[key]`: the
|
|
63
|
+
- `wizard.forms[key]`: the step's own form handle, identity-equal to the `useForm` ref that was slotted in.
|
|
62
64
|
- `wizard.allValues` / `wizard.allErrors`: every form's values / aggregate errors, keyed.
|
|
63
|
-
- `wizard.activeForm`:
|
|
65
|
+
- `wizard.activeForm`: a live facade over whichever step is active, so a handler captured once retargets as the pin moves. Not the handle itself: use `wizard.forms[key]` for that.
|
|
64
66
|
- `wizard.statuses`: per-form status. Plus `wizard.progress`, `wizard.canAdvance`, `wizard.canGoBack`, `wizard.isFinalStep`, `wizard.visited`.
|
|
65
67
|
|
|
66
68
|
## A declarative step registry
|
|
@@ -94,11 +96,11 @@ const wizard = useWizard({
|
|
|
94
96
|
|
|
95
97
|
Everything derives from the registry: the step slots, the titles, any persisted key list. Add, remove, reorder, or gate a step by editing the registry alone. Do not add speculative registry fields with no consumer; add a hook when the second consumer arrives.
|
|
96
98
|
|
|
97
|
-
## Keyed injection resolves by
|
|
99
|
+
## Keyed injection resolves by name and timing
|
|
98
100
|
|
|
99
|
-
A string key passed to `injectForm('account')` or `injectWizard('onboarding')`
|
|
101
|
+
A string key passed to `injectForm('account')` or `injectWizard('onboarding')` is a name in Attaform's app-level registry, not a location in the component tree:
|
|
100
102
|
|
|
101
|
-
-
|
|
102
|
-
-
|
|
103
|
+
- **Position-independent.** The lookup reaches any component in the app: a sibling branch, a floating toolbar, a teleported modal. Extracting a wrapper or changing how deep a component sits never changes what a key resolves to. The no-key ambient form is the opposite, and is the one that runs on `provide` / `inject`: it reaches descendants of the owning `useForm` only.
|
|
104
|
+
- **Time-dependent.** The registry entry appears when the owner's own `useForm({ key })` / `useWizard({ key })` setup runs, and not before.
|
|
103
105
|
|
|
104
|
-
|
|
106
|
+
Together they give one rule: **resolve downward.** Vue runs a parent's setup before its children's, so a descendant reaching an ancestor's keyed form always resolves, while an ancestor reaching a descendant's gets `null`. The stable shape is to **lift a shared form's creation to the coordinating ancestor** (the component that owns the wizard) and reach for it at or below that point. Always chain `?.` on the result, which is `T | null`.
|
package/dist/abstract.cjs
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const serializeScript = require('./shared/attaform.DrvMscNX.cjs');
|
|
4
|
-
const devtoolsShared = require('./shared/attaform.Biytrv0-.cjs');
|
|
5
|
-
const registerProtocol = require('./shared/attaform.yUrz7C2z.cjs');
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
exports.AttaformErrorCode = serializeScript.AttaformErrorCode;
|
|
10
|
-
exports.DEFAULT_TIMINGS = serializeScript.DEFAULT_TIMINGS;
|
|
11
|
-
exports.defaultCoercionRules = serializeScript.defaultCoercionRules;
|
|
12
|
-
exports.defaultDisplayState = serializeScript.defaultDisplayState;
|
|
13
|
-
exports.defineCoercion = serializeScript.defineCoercion;
|
|
14
|
-
exports.escapeForInlineScript = serializeScript.escapeForInlineScript;
|
|
15
|
-
exports.gate = serializeScript.gate;
|
|
16
|
-
exports.injectForm = serializeScript.injectForm;
|
|
17
|
-
exports.injectWizard = serializeScript.injectWizard;
|
|
18
|
-
exports.isUnset = serializeScript.isUnset;
|
|
19
|
-
exports.lazy = serializeScript.lazy;
|
|
20
|
-
exports.makeDefaultDisplayState = serializeScript.makeDefaultDisplayState;
|
|
21
|
-
exports.unset = serializeScript.unset;
|
|
22
|
-
exports.useAbstractForm = serializeScript.useAbstractForm;
|
|
23
|
-
exports.useRegister = serializeScript.useRegister;
|
|
24
|
-
exports.useWizard = serializeScript.useWizard;
|
|
25
|
-
exports.AttaformError = devtoolsShared.AttaformError;
|
|
26
|
-
exports.DEVTOOLS_WINDOW_KEY = devtoolsShared.DEVTOOLS_WINDOW_KEY;
|
|
27
|
-
exports.InvalidPathError = devtoolsShared.InvalidPathError;
|
|
28
|
-
exports.InvalidUseFormConfigError = devtoolsShared.InvalidUseFormConfigError;
|
|
29
|
-
exports.OutsideSetupError = devtoolsShared.OutsideSetupError;
|
|
30
|
-
exports.ROOT_PATH = devtoolsShared.ROOT_PATH;
|
|
31
|
-
exports.ROOT_PATH_KEY = devtoolsShared.ROOT_PATH_KEY;
|
|
32
|
-
exports.RegistryNotInstalledError = devtoolsShared.RegistryNotInstalledError;
|
|
33
|
-
exports.ReservedFormKeyError = devtoolsShared.ReservedFormKeyError;
|
|
34
|
-
exports.SubmitErrorHandlerError = devtoolsShared.SubmitErrorHandlerError;
|
|
35
|
-
exports.assignKey = devtoolsShared.assignKey;
|
|
36
|
-
exports.canonicalizePath = devtoolsShared.canonicalizePath;
|
|
37
|
-
exports.createAttaform = devtoolsShared.createAttaform;
|
|
38
|
-
exports.createRegistry = devtoolsShared.createRegistry;
|
|
39
|
-
exports.getRegistryFromApp = devtoolsShared.getRegistryFromApp;
|
|
40
|
-
exports.hydrateAttaformState = devtoolsShared.hydrateAttaformState;
|
|
41
|
-
exports.isPathPrefix = devtoolsShared.isPathPrefix;
|
|
42
|
-
exports.kAttaformRegistry = devtoolsShared.kAttaformRegistry;
|
|
43
|
-
exports.parseDottedPath = devtoolsShared.parseDottedPath;
|
|
44
|
-
exports.renderAttaformState = devtoolsShared.renderAttaformState;
|
|
45
|
-
exports.useRegistry = devtoolsShared.useRegistry;
|
|
46
|
-
exports.vRegister = devtoolsShared.vRegister;
|
|
47
|
-
exports.isRegisterValue = registerProtocol.isRegisterValue;
|
|
48
|
-
//# sourceMappingURL=abstract.cjs.map
|
package/dist/abstract.cjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"abstract.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
package/dist/abstract.d.cts
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { A as AttaformRegistry } from './shared/attaform.FmqKO1Ex.cjs';
|
|
2
|
-
export { a as AnyForm, b as AttaformDevtoolsBridge, c as AttaformError, d as AttaformErrorCode, e as AttaformPluginOptions, C as CompiledStep, D as DEFAULT_TIMINGS, f as DEVTOOLS_WINDOW_KEY, g as DisplayTimings, F as FormStatus, h as FormStatusSeed, G as GateMarker, I as InjectWizardInput, i as InvalidPathError, j as InvalidUseFormConfigError, L as LazyMarker, O as OutsideSetupError, R as RegistryNotInstalledError, k as ReservedFormKeyError, S as SerializedAttaformState, l as SerializedFormData, m as StepSlot, n as SubmitErrorHandlerError, U as UseRegisterReturn, o as UseWizardReturnType, W as WizardAggregateError, p as WizardCtx, q as WizardCtxForm, r as WizardOnError, s as WizardOnSubmit, t as WizardOptions, u as WizardPersistFn, v as WizardRestoreFn, w as WizardRestoreState, x as WizardStatusesProxy, y as WizardSubmitContext, z as assignKey, B as createAttaform, E as createRegistry, H as defaultCoercionRules, J as defaultDisplayState, K as defineCoercion, M as escapeForInlineScript, N as gate, P as getRegistryFromApp, Q as hydrateAttaformState, T as injectForm, V as injectWizard, X as isRegisterValue, Y as kAttaformRegistry, Z as lazy, _ as makeDefaultDisplayState, $ as renderAttaformState, a0 as useRegister, a1 as useRegistry, a2 as useWizard, a3 as vRegister } from './shared/attaform.FmqKO1Ex.cjs';
|
|
3
|
-
import { G as GenericForm, F as FormKey, U as UseFormConfiguration, A as AbstractSchema, D as DefaultValuesInput, a as UseFormReturnType } from './shared/attaform.DniawQLP.cjs';
|
|
4
|
-
export { b as ArrayItem, c as ArrayPath, d as AttaformDefaults, C as CoercionEntry, e as CoercionRegistry, f as CoercionResult, g as CustomDirectiveRegisterAssignerFn, h as DeepPartial, i as DefaultValuesResponse, j as DefaultValuesShape, k as DisplayCtx, l as DisplayMachine, m as DisplayState, E as ErrorInput, n as ErrorsProxyShape, o as FieldMetaPayload, p as FieldState, q as FieldStateMap, r as FieldStateMapEntry, s as FlatPath, t as FormErrorRecord, u as FormErrorsSurface, v as FormMeta, w as GetDisplayState, H as HandleSubmit, x as HistoryConfig, I as IsTuple, y as IsUnion, J as JoinSegments, z as Json, K as KeyofUnion, L as LiftedValueShape, M as MetaTrackerValue, N as NestedReadType, B as NestedType, O as OnError, P as OnInvalidSubmitPolicy, Q as OnSubmit, R as PartialFlatPath, S as Path, T as PathKey, V as PendingValidationStatus, W as Primitive, X as ROOT_PATH, Y as ROOT_PATH_KEY, Z as ReactiveValidationStatus, _ as RegisterDirective, $ as RegisterFlatPath, a0 as RegisterOptions, a1 as RegisterSelectModifier, a2 as RegisterTextModifier, a3 as RegisterTransform, a4 as RegisterValue, a5 as Segment, a6 as SetValueCallback, a7 as SetValuePayload, a8 as SettledValidationStatus, a9 as SlimPrimitiveKind, aa as SlimRuntimeOf, ab as SubmitHandler, ac as Unset, ad as ValidateOn, ae as ValidateOnConfig, af as ValidationError, ag as ValidationResponse, ah as ValidationResponseWithoutValue, ai as ValueOfUnion, aj as WriteMeta, ak as WriteShape, al as canonicalizePath, am as isPathPrefix, an as isUnset, ao as parseDottedPath, ap as unset } from './shared/attaform.DniawQLP.cjs';
|
|
5
|
-
import 'vue';
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Schema-agnostic `useForm`. Accepts any object that implements
|
|
9
|
-
* `AbstractSchema` — useful when integrating a custom schema
|
|
10
|
-
* adapter or a third-party validation library.
|
|
11
|
-
*
|
|
12
|
-
* ```ts
|
|
13
|
-
* import { useForm } from 'attaform'
|
|
14
|
-
*
|
|
15
|
-
* const form = useForm({
|
|
16
|
-
* schema: myCustomAdapter,
|
|
17
|
-
* defaultValues: { name: '' },
|
|
18
|
-
* })
|
|
19
|
-
* ```
|
|
20
|
-
*
|
|
21
|
-
* Most consumers prefer a typed entry point that wraps the underlying
|
|
22
|
-
* library's schema with the matching adapter automatically; see the
|
|
23
|
-
* subpath documentation for the available adapters.
|
|
24
|
-
*
|
|
25
|
-
* Returns the same form API as the typed entry points; see
|
|
26
|
-
* `UseFormReturnType` for the full surface.
|
|
27
|
-
*/
|
|
28
|
-
declare function useAbstractForm<Form extends GenericForm, GetValueFormType extends GenericForm = Form, ReadForm extends GenericForm = Form, K extends FormKey = FormKey>(configuration: UseFormConfiguration<Form, GetValueFormType, AbstractSchema<Form, GetValueFormType>, DefaultValuesInput<Form>, K>,
|
|
29
|
-
/**
|
|
30
|
-
* Internal escape hatch for callers that already hold a registry
|
|
31
|
-
* reference and need to construct a form outside Vue's setup context
|
|
32
|
-
* (e.g. the wizard's lazy noop builder, which runs inside a
|
|
33
|
-
* `computed` re-eval). Passing this skips the strict `useRegistry()`
|
|
34
|
-
* call; everything else (FormStore allocation, registry presence,
|
|
35
|
-
* consumer ref-counting via `onScopeDispose`) goes through the same
|
|
36
|
-
* path eager calls follow. Not part of the public surface.
|
|
37
|
-
*/
|
|
38
|
-
options?: {
|
|
39
|
-
readonly registry?: AttaformRegistry;
|
|
40
|
-
}): UseFormReturnType<Form, GetValueFormType, ReadForm, K>;
|
|
41
|
-
|
|
42
|
-
export { AbstractSchema, AttaformRegistry, DefaultValuesInput, FormKey, GenericForm, UseFormConfiguration, UseFormReturnType, useAbstractForm };
|
package/dist/abstract.d.ts
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { A as AttaformRegistry } from './shared/attaform.F2Q8iv38.js';
|
|
2
|
-
export { a as AnyForm, b as AttaformDevtoolsBridge, c as AttaformError, d as AttaformErrorCode, e as AttaformPluginOptions, C as CompiledStep, D as DEFAULT_TIMINGS, f as DEVTOOLS_WINDOW_KEY, g as DisplayTimings, F as FormStatus, h as FormStatusSeed, G as GateMarker, I as InjectWizardInput, i as InvalidPathError, j as InvalidUseFormConfigError, L as LazyMarker, O as OutsideSetupError, R as RegistryNotInstalledError, k as ReservedFormKeyError, S as SerializedAttaformState, l as SerializedFormData, m as StepSlot, n as SubmitErrorHandlerError, U as UseRegisterReturn, o as UseWizardReturnType, W as WizardAggregateError, p as WizardCtx, q as WizardCtxForm, r as WizardOnError, s as WizardOnSubmit, t as WizardOptions, u as WizardPersistFn, v as WizardRestoreFn, w as WizardRestoreState, x as WizardStatusesProxy, y as WizardSubmitContext, z as assignKey, B as createAttaform, E as createRegistry, H as defaultCoercionRules, J as defaultDisplayState, K as defineCoercion, M as escapeForInlineScript, N as gate, P as getRegistryFromApp, Q as hydrateAttaformState, T as injectForm, V as injectWizard, X as isRegisterValue, Y as kAttaformRegistry, Z as lazy, _ as makeDefaultDisplayState, $ as renderAttaformState, a0 as useRegister, a1 as useRegistry, a2 as useWizard, a3 as vRegister } from './shared/attaform.F2Q8iv38.js';
|
|
3
|
-
import { G as GenericForm, F as FormKey, U as UseFormConfiguration, A as AbstractSchema, D as DefaultValuesInput, a as UseFormReturnType } from './shared/attaform.DniawQLP.js';
|
|
4
|
-
export { b as ArrayItem, c as ArrayPath, d as AttaformDefaults, C as CoercionEntry, e as CoercionRegistry, f as CoercionResult, g as CustomDirectiveRegisterAssignerFn, h as DeepPartial, i as DefaultValuesResponse, j as DefaultValuesShape, k as DisplayCtx, l as DisplayMachine, m as DisplayState, E as ErrorInput, n as ErrorsProxyShape, o as FieldMetaPayload, p as FieldState, q as FieldStateMap, r as FieldStateMapEntry, s as FlatPath, t as FormErrorRecord, u as FormErrorsSurface, v as FormMeta, w as GetDisplayState, H as HandleSubmit, x as HistoryConfig, I as IsTuple, y as IsUnion, J as JoinSegments, z as Json, K as KeyofUnion, L as LiftedValueShape, M as MetaTrackerValue, N as NestedReadType, B as NestedType, O as OnError, P as OnInvalidSubmitPolicy, Q as OnSubmit, R as PartialFlatPath, S as Path, T as PathKey, V as PendingValidationStatus, W as Primitive, X as ROOT_PATH, Y as ROOT_PATH_KEY, Z as ReactiveValidationStatus, _ as RegisterDirective, $ as RegisterFlatPath, a0 as RegisterOptions, a1 as RegisterSelectModifier, a2 as RegisterTextModifier, a3 as RegisterTransform, a4 as RegisterValue, a5 as Segment, a6 as SetValueCallback, a7 as SetValuePayload, a8 as SettledValidationStatus, a9 as SlimPrimitiveKind, aa as SlimRuntimeOf, ab as SubmitHandler, ac as Unset, ad as ValidateOn, ae as ValidateOnConfig, af as ValidationError, ag as ValidationResponse, ah as ValidationResponseWithoutValue, ai as ValueOfUnion, aj as WriteMeta, ak as WriteShape, al as canonicalizePath, am as isPathPrefix, an as isUnset, ao as parseDottedPath, ap as unset } from './shared/attaform.DniawQLP.js';
|
|
5
|
-
import 'vue';
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Schema-agnostic `useForm`. Accepts any object that implements
|
|
9
|
-
* `AbstractSchema` — useful when integrating a custom schema
|
|
10
|
-
* adapter or a third-party validation library.
|
|
11
|
-
*
|
|
12
|
-
* ```ts
|
|
13
|
-
* import { useForm } from 'attaform'
|
|
14
|
-
*
|
|
15
|
-
* const form = useForm({
|
|
16
|
-
* schema: myCustomAdapter,
|
|
17
|
-
* defaultValues: { name: '' },
|
|
18
|
-
* })
|
|
19
|
-
* ```
|
|
20
|
-
*
|
|
21
|
-
* Most consumers prefer a typed entry point that wraps the underlying
|
|
22
|
-
* library's schema with the matching adapter automatically; see the
|
|
23
|
-
* subpath documentation for the available adapters.
|
|
24
|
-
*
|
|
25
|
-
* Returns the same form API as the typed entry points; see
|
|
26
|
-
* `UseFormReturnType` for the full surface.
|
|
27
|
-
*/
|
|
28
|
-
declare function useAbstractForm<Form extends GenericForm, GetValueFormType extends GenericForm = Form, ReadForm extends GenericForm = Form, K extends FormKey = FormKey>(configuration: UseFormConfiguration<Form, GetValueFormType, AbstractSchema<Form, GetValueFormType>, DefaultValuesInput<Form>, K>,
|
|
29
|
-
/**
|
|
30
|
-
* Internal escape hatch for callers that already hold a registry
|
|
31
|
-
* reference and need to construct a form outside Vue's setup context
|
|
32
|
-
* (e.g. the wizard's lazy noop builder, which runs inside a
|
|
33
|
-
* `computed` re-eval). Passing this skips the strict `useRegistry()`
|
|
34
|
-
* call; everything else (FormStore allocation, registry presence,
|
|
35
|
-
* consumer ref-counting via `onScopeDispose`) goes through the same
|
|
36
|
-
* path eager calls follow. Not part of the public surface.
|
|
37
|
-
*/
|
|
38
|
-
options?: {
|
|
39
|
-
readonly registry?: AttaformRegistry;
|
|
40
|
-
}): UseFormReturnType<Form, GetValueFormType, ReadForm, K>;
|
|
41
|
-
|
|
42
|
-
export { AbstractSchema, AttaformRegistry, DefaultValuesInput, FormKey, GenericForm, UseFormConfiguration, UseFormReturnType, useAbstractForm };
|
package/dist/abstract.mjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"abstract.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;"}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
async function warnOnSchemaFingerprintMismatch(key, existing, incoming) {
|
|
4
|
-
let existingFp;
|
|
5
|
-
let incomingFp;
|
|
6
|
-
try {
|
|
7
|
-
existingFp = await existing.fingerprint();
|
|
8
|
-
incomingFp = await incoming.fingerprint();
|
|
9
|
-
} catch (error) {
|
|
10
|
-
console.error(
|
|
11
|
-
`[attaform] fingerprint() rejected for key "${key}"; skipping mismatch check.`,
|
|
12
|
-
error
|
|
13
|
-
);
|
|
14
|
-
return;
|
|
15
|
-
}
|
|
16
|
-
if (existingFp === incomingFp) return;
|
|
17
|
-
console.warn(
|
|
18
|
-
`[attaform] useForm() calls with key "${key}" use different schemas; first wins, second is ignored. Use identical schemas or unique keys.
|
|
19
|
-
existing: ${existingFp}
|
|
20
|
-
incoming: ${incomingFp}`
|
|
21
|
-
);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
exports.warnOnSchemaFingerprintMismatch = warnOnSchemaFingerprintMismatch;
|
|
25
|
-
//# sourceMappingURL=dev-key-collision-warnings.cjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"dev-key-collision-warnings.cjs","sources":["../../src/runtime/core/dev-key-collision-warnings.ts"],"sourcesContent":["/**\n * Dev-only shared-key collision diagnostics for `useForm`. When two\n * `useForm({ key })` calls land on the same key, they resolve to one\n * `FormStore` by design (the shared-store semantic), and the second\n * call's schema is silently dropped in favour of the first's wiring.\n * The warnings here surface that divergence so a genuine collision (two\n * unrelated call sites that happen to agree on a key) is diagnosable\n * rather than silent.\n *\n * This whole module is loaded behind a `__DEV__`-gated dynamic import in\n * `use-abstract-form.ts`. A production build folds that gate to `false`,\n * dead-code-eliminates the `import()` call, and leaves this module an\n * unreferenced chunk the consumer's bundler drops. Keeping the warnings\n * here (rather than as plain functions called from the gated block)\n * matters because esbuild removes the inline dead branch but NOT a\n * top-level function it is the sole caller of: tree-shaking runs before\n * the define-fold, so the function survives. A separately-imported\n * module sidesteps that and keeps the cluster out of every consumer's\n * production bundle.\n */\nimport type { AbstractSchema, FormKey } from '../types/types-api'\nimport type { GenericForm } from '../types/types-core'\n\n/**\n * Dev-only: warn when a second `useForm` lands on the same key with\n * a structurally-different schema. Two schemas resolve their own\n * fingerprints; we compare the strings and flag mismatches. An adapter\n * `fingerprint()` that rejects is caught (never crashes the form) and\n * surfaced as a `console.error` in dev: the mismatch check is skipped,\n * matching the \"allow the inconsistency\" failure mode. See\n * `AbstractSchema.fingerprint()` in types-api.ts for the contract.\n */\nexport async function warnOnSchemaFingerprintMismatch(\n key: FormKey,\n existing: AbstractSchema<GenericForm, GenericForm>,\n incoming: AbstractSchema<GenericForm, GenericForm>\n): Promise<void> {\n let existingFp: string\n let incomingFp: string\n try {\n existingFp = await existing.fingerprint()\n incomingFp = await incoming.fingerprint()\n } catch (error) {\n console.error(\n `[attaform] fingerprint() rejected for key \"${key}\"; skipping mismatch check.`,\n error\n )\n return\n }\n if (existingFp === incomingFp) return\n console.warn(\n `[attaform] useForm() calls with key \"${key}\" use different schemas; first wins, second is ignored. Use identical schemas or unique keys.\\n existing: ${existingFp}\\n incoming: ${incomingFp}`\n )\n}\n"],"names":[],"mappings":";;AAgCA,eAAsB,+BAAA,CACpB,GAAA,EACA,QAAA,EACA,QAAA,EACe;AACf,EAAA,IAAI,UAAA;AACJ,EAAA,IAAI,UAAA;AACJ,EAAA,IAAI;AACF,IAAA,UAAA,GAAa,MAAM,SAAS,WAAA,EAAY;AACxC,IAAA,UAAA,GAAa,MAAM,SAAS,WAAA,EAAY;AAAA,EAC1C,SAAS,KAAA,EAAO;AACd,IAAA,OAAA,CAAQ,KAAA;AAAA,MACN,8CAA8C,GAAG,CAAA,2BAAA,CAAA;AAAA,MACjD;AAAA,KACF;AACA,IAAA;AAAA,EACF;AACA,EAAA,IAAI,eAAe,UAAA,EAAY;AAC/B,EAAA,OAAA,CAAQ,IAAA;AAAA,IACN,wCAAwC,GAAG,CAAA;AAAA,YAAA,EAA8G,UAAU;AAAA,YAAA,EAAiB,UAAU,CAAA;AAAA,GAChM;AACF;;;;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"dev-key-collision-warnings.mjs","sources":["../../src/runtime/core/dev-key-collision-warnings.ts"],"sourcesContent":["/**\n * Dev-only shared-key collision diagnostics for `useForm`. When two\n * `useForm({ key })` calls land on the same key, they resolve to one\n * `FormStore` by design (the shared-store semantic), and the second\n * call's schema is silently dropped in favour of the first's wiring.\n * The warnings here surface that divergence so a genuine collision (two\n * unrelated call sites that happen to agree on a key) is diagnosable\n * rather than silent.\n *\n * This whole module is loaded behind a `__DEV__`-gated dynamic import in\n * `use-abstract-form.ts`. A production build folds that gate to `false`,\n * dead-code-eliminates the `import()` call, and leaves this module an\n * unreferenced chunk the consumer's bundler drops. Keeping the warnings\n * here (rather than as plain functions called from the gated block)\n * matters because esbuild removes the inline dead branch but NOT a\n * top-level function it is the sole caller of: tree-shaking runs before\n * the define-fold, so the function survives. A separately-imported\n * module sidesteps that and keeps the cluster out of every consumer's\n * production bundle.\n */\nimport type { AbstractSchema, FormKey } from '../types/types-api'\nimport type { GenericForm } from '../types/types-core'\n\n/**\n * Dev-only: warn when a second `useForm` lands on the same key with\n * a structurally-different schema. Two schemas resolve their own\n * fingerprints; we compare the strings and flag mismatches. An adapter\n * `fingerprint()` that rejects is caught (never crashes the form) and\n * surfaced as a `console.error` in dev: the mismatch check is skipped,\n * matching the \"allow the inconsistency\" failure mode. See\n * `AbstractSchema.fingerprint()` in types-api.ts for the contract.\n */\nexport async function warnOnSchemaFingerprintMismatch(\n key: FormKey,\n existing: AbstractSchema<GenericForm, GenericForm>,\n incoming: AbstractSchema<GenericForm, GenericForm>\n): Promise<void> {\n let existingFp: string\n let incomingFp: string\n try {\n existingFp = await existing.fingerprint()\n incomingFp = await incoming.fingerprint()\n } catch (error) {\n console.error(\n `[attaform] fingerprint() rejected for key \"${key}\"; skipping mismatch check.`,\n error\n )\n return\n }\n if (existingFp === incomingFp) return\n console.warn(\n `[attaform] useForm() calls with key \"${key}\" use different schemas; first wins, second is ignored. Use identical schemas or unique keys.\\n existing: ${existingFp}\\n incoming: ${incomingFp}`\n )\n}\n"],"names":[],"mappings":"AAgCA,eAAsB,+BAAA,CACpB,GAAA,EACA,QAAA,EACA,QAAA,EACe;AACf,EAAA,IAAI,UAAA;AACJ,EAAA,IAAI,UAAA;AACJ,EAAA,IAAI;AACF,IAAA,UAAA,GAAa,MAAM,SAAS,WAAA,EAAY;AACxC,IAAA,UAAA,GAAa,MAAM,SAAS,WAAA,EAAY;AAAA,EAC1C,SAAS,KAAA,EAAO;AACd,IAAA,OAAA,CAAQ,KAAA;AAAA,MACN,8CAA8C,GAAG,CAAA,2BAAA,CAAA;AAAA,MACjD;AAAA,KACF;AACA,IAAA;AAAA,EACF;AACA,EAAA,IAAI,eAAe,UAAA,EAAY;AAC/B,EAAA,OAAA,CAAQ,IAAA;AAAA,IACN,wCAAwC,GAAG,CAAA;AAAA,YAAA,EAA8G,UAAU;AAAA,YAAA,EAAiB,UAAU,CAAA;AAAA,GAChM;AACF;;;;"}
|