attaform 0.24.2 → 0.24.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/README.md +27 -122
  2. package/dist/chunks/devtools.cjs +1 -1
  3. package/dist/chunks/devtools.mjs +1 -1
  4. package/dist/chunks/fingerprint2.cjs +1 -1
  5. package/dist/chunks/fingerprint2.mjs +1 -1
  6. package/dist/index.cjs +3 -3
  7. package/dist/index.mjs +3 -3
  8. package/dist/nuxt.cjs +1 -1
  9. package/dist/nuxt.cjs.map +1 -1
  10. package/dist/nuxt.mjs +1 -1
  11. package/dist/nuxt.mjs.map +1 -1
  12. package/dist/runtime/plugins/attaform.cjs +2 -2
  13. package/dist/runtime/plugins/attaform.mjs +2 -2
  14. package/dist/shared/{attaform.DdfLrIgM.cjs → attaform.BVjueUPu.cjs} +3 -3
  15. package/dist/shared/{attaform.DdfLrIgM.cjs.map → attaform.BVjueUPu.cjs.map} +1 -1
  16. package/dist/shared/{attaform.BI7M3wK8.mjs → attaform.B_F61Rx1.mjs} +2 -2
  17. package/dist/shared/{attaform.BI7M3wK8.mjs.map → attaform.B_F61Rx1.mjs.map} +1 -1
  18. package/dist/shared/{attaform.B_-nZHtM.mjs → attaform.ByIB7i15.mjs} +4 -4
  19. package/dist/shared/{attaform.B_-nZHtM.mjs.map → attaform.ByIB7i15.mjs.map} +1 -1
  20. package/dist/shared/{attaform.C0au8oXd.mjs → attaform.C5qkSN6t.mjs} +2 -2
  21. package/dist/shared/attaform.C5qkSN6t.mjs.map +1 -0
  22. package/dist/shared/{attaform.Bos7wVZw.cjs → attaform.CB1kYKUB.cjs} +2 -2
  23. package/dist/shared/{attaform.Bos7wVZw.cjs.map → attaform.CB1kYKUB.cjs.map} +1 -1
  24. package/dist/shared/{attaform.D-gYGj5z.cjs → attaform.CqlLImc1.cjs} +4 -4
  25. package/dist/shared/{attaform.D-gYGj5z.cjs.map → attaform.CqlLImc1.cjs.map} +1 -1
  26. package/dist/shared/{attaform.BjUaX7s8.mjs → attaform.D4ebpNbS.mjs} +3 -3
  27. package/dist/shared/{attaform.BjUaX7s8.mjs.map → attaform.D4ebpNbS.mjs.map} +1 -1
  28. package/dist/shared/{attaform.B7UdTs_o.cjs → attaform.DQcdvxKZ.cjs} +2 -2
  29. package/dist/shared/attaform.DQcdvxKZ.cjs.map +1 -0
  30. package/dist/shared/{attaform.BOi6n2Pn.cjs → attaform.DRWrVSkH.cjs} +2 -2
  31. package/dist/shared/{attaform.BOi6n2Pn.cjs.map → attaform.DRWrVSkH.cjs.map} +1 -1
  32. package/dist/shared/{attaform.K1MbxmIu.mjs → attaform.Dbzwtn4g.mjs} +4 -4
  33. package/dist/shared/{attaform.K1MbxmIu.mjs.map → attaform.Dbzwtn4g.mjs.map} +1 -1
  34. package/dist/shared/{attaform.B57G7ePY.cjs → attaform.DfnwaGW2.cjs} +4 -4
  35. package/dist/shared/{attaform.B57G7ePY.cjs.map → attaform.DfnwaGW2.cjs.map} +1 -1
  36. package/dist/shared/{attaform.0-00cYGw.mjs → attaform.DgbHNLf_.mjs} +2 -2
  37. package/dist/shared/{attaform.0-00cYGw.mjs.map → attaform.DgbHNLf_.mjs.map} +1 -1
  38. package/dist/zod-v3.cjs +2 -2
  39. package/dist/zod-v3.mjs +2 -2
  40. package/dist/zod-v4.cjs +2 -2
  41. package/dist/zod-v4.mjs +2 -2
  42. package/dist/zod.cjs +5 -5
  43. package/dist/zod.mjs +5 -5
  44. package/package.json +2 -2
  45. package/dist/shared/attaform.B7UdTs_o.cjs.map +0 -1
  46. package/dist/shared/attaform.C0au8oXd.mjs.map +0 -1
package/README.md CHANGED
@@ -16,86 +16,28 @@ A type-safe, schema-driven form library for Vue 3 and Nuxt with first-class Zod
16
16
  >
17
17
  > Because Vue and Nuxt devs deserve nice things, too.
18
18
 
19
- **Try it live.** Tweak a schema, edit the template, and watch the form rebind in the browser at [attaform.com/demos](https://www.attaform.com/demos). No install needed.
19
+ **[Try it live →](https://attaform.dev/demos)** Tweak a schema, edit the template, and watch the form rebind in your browser. No install needed.
20
20
 
21
- ## Installation
21
+ ## Quick start
22
22
 
23
23
  ```bash
24
24
  npm install attaform zod
25
25
  ```
26
26
 
27
- That's it for client-side rendering. Forms render and validate the moment you call `useForm`; the registry self-installs on first use.
28
-
29
- ### Going further
30
-
31
- **Nuxt 3 / 4.** Add the module:
32
-
33
- ```ts
34
- // nuxt.config.ts
35
- export default defineNuxtConfig({
36
- modules: ['attaform/nuxt'],
37
- })
38
- ```
39
-
40
- **Bare Vue + SSR.** Add the Vite plugin so server-rendered HTML matches the hydrated client (the plugin injects `:value` / `:checked` bindings at compile time):
41
-
42
- ```ts
43
- // vite.config.ts
44
- import vue from '@vitejs/plugin-vue'
45
- import { attaform } from 'attaform/vite'
46
-
47
- export default defineConfig({
48
- plugins: [vue(), attaform()],
49
- })
50
- ```
51
-
52
- The Vite plugin also rewrites `attaform/zod` imports at build time to `attaform/zod-v3` or `attaform/zod-v4`, so your bundle ships only the adapter you actually use.
53
-
54
- **App-wide options.** Install the Vue plugin if you want to set defaults or disable devtools:
55
-
56
- ```ts
57
- // main.ts
58
- import { createApp } from 'vue'
59
- import { createAttaform } from 'attaform'
60
-
61
- createApp(App)
62
- .use(createAttaform({ defaults: { debounceMs: 100 } }))
63
- .mount('#app')
64
- ```
65
-
66
- ### Recommended tsconfig
67
-
68
- Attaform pairs well with `noUncheckedIndexedAccess: true`:
69
-
70
- ```json
71
- {
72
- "compilerOptions": {
73
- "strict": true,
74
- "noUncheckedIndexedAccess": true
75
- }
76
- }
77
- ```
78
-
79
- It catches stale `form.values.contacts[N]` reads at compile time. Nuxt 3 / 4 sets this for you.
80
-
81
- > **Try it live.** Skip the install and tweak a schema in your browser at [attaform.com/demos](https://www.attaform.com/demos).
82
-
83
- ## Quick start
84
-
85
27
  ```vue
86
28
  <script setup lang="ts">
87
29
  import { z } from 'zod'
88
- import { useForm } from 'attaform/zod' // auto-detects Zod major
30
+ import { useForm } from 'attaform/zod' // auto-detects your Zod major
89
31
 
90
32
  const schema = z.object({
91
33
  username: z.string().min(2, 'At least 2 characters'),
92
34
  password: z.string().min(8, 'At least 8 characters'),
93
35
  })
94
36
 
95
- const form = useForm({ schema, key: 'signup' })
37
+ const form = useForm({ schema })
96
38
 
97
39
  const onSubmit = form.handleSubmit(async (values) => {
98
- await $fetch('/api/signup', { method: 'POST', body: JSON.stringify(values) })
40
+ await fetch('/api/signup', { method: 'POST', body: JSON.stringify(values) })
99
41
  })
100
42
  </script>
101
43
 
@@ -112,78 +54,41 @@ It catches stale `form.values.contacts[N]` reads at compile time. Nuxt 3 / 4 set
112
54
  </template>
113
55
  ```
114
56
 
115
- `useForm({ schema, key })` returns a Pinia-style reactive form. Read leaves directly, no `.value`:
116
-
117
- - **`form.values`**: current values. `form.values.username`, `form.values.address.city`.
118
- - **`form.errors`**: per-field errors, keyed by dotted path. `form.errors.username?.[0]?.message`.
119
- - **`form.fields`**: per-field flags (`dirty`, `touched`, `errors`, `blank`, …). `form.fields.username.dirty`.
120
- - **`form.meta`**: form-level flags and counters (`submitting`, `valid`, `dirty`, `submissionAttempts`, the flat `meta.errors` aggregate, the per-mount `instanceId`, …).
121
- - **`form.history`**: consolidated undo/redo namespace (`undo()`, `redo()`, `clear()`, `canUndo`, `canRedo`, `size`).
122
- - **`form.register(path)`**: typed two-way binding. Pair with `v-register` on `<input>` / `<textarea>` / `<select>`.
123
- - **`form.handleSubmit(onValid, onInvalid?)`**: runs validation, dispatches. The valid callback receives the strict Zod-inferred type.
124
- - **`form.setValue(path, value)`**, **`form.reset()`**, field-array helpers: see [Entry points](https://www.attaform.com/docs/reference/entry-points).
125
-
126
- > **Try it live.** Tweak this schema, edit the template, and watch the form rebind in the browser at [attaform.com/demos](https://www.attaform.com/demos). No install needed.
127
-
128
- ## `v-register`: one line, every option
129
-
130
- `v-register` stays on the same native `<input>`. Every option you add opts into another runtime feature without touching the template structure.
131
-
132
- ```vue
133
- <input v-register="form.register('email')" />
134
- ```
135
-
136
- Typed two-way binding to `form.values.email`, with schema-driven coercion at the directive layer.
57
+ Hand the schema to `useForm`, bind each input with `v-register`, and Attaform owns the values, the coercion, the live errors, and the typed submit payload. That is the whole loop.
137
58
 
138
- ```vue
139
- <input v-register="form.register('slug', { transforms: [lowercase] })" />
140
- ```
141
-
142
- Same line. The field now runs a sync write-time transform, normalizing the value before it reaches form state.
143
-
144
- ```vue
145
- <input v-register="form.register('slug', { transforms: [lowercase, dashify], autoAria: false })" />
146
- ```
147
-
148
- Same line. Compose a transform pipeline and opt this field out of automatic aria wiring, all without touching the markup elsewhere.
59
+ **[Full walkthrough →](https://attaform.dev/docs/getting-started/quick-start)** Setting up Nuxt, Vite, or bare Vue? See the [installation guide](https://attaform.dev/docs/getting-started/installation).
149
60
 
150
- ## Features
61
+ ## Highlights
151
62
 
152
- - **Schema-driven types.** Every path, value, and error is inferred from your Zod schema. No `any`, no manual type plumbing.
153
- - **Live validation.** `validateOn: 'change'` by default with synchronous `debounceMs: 0`. `'blur'` and `'submit'` (opt-out) modes available. Async refinements await before submit dispatches.
154
- - **Schema-driven coercion.** String DOM input flows into the schema's typed slot (`string→number`, `string→boolean`) at the directive layer. Default-on; pass `useForm({ coerce: false })` to disable or a custom `CoercionRegistry` to extend.
155
- - **Register transforms.** `form.register('slug', { transforms: [lowercase, dashify] })` runs sync write-time normalization before storage commit.
156
- - **First-class multistep.** `useWizard` composes `useForm` instances into a flow with shared navigation, per-step validation, state retained across steps, and deep-link restore.
157
- - **DevTools panel.** Auto-wired in Nuxt. Walk history, edit values live, inspect every form on the page. No probes to install.
158
- - **Discriminated-union variant memory.** Switching a discriminator (`notify.channel: 'email' → 'sms' → 'email'`) restores the previous variant's typed subtree by default. Pass `useForm({ rememberVariants: false })` to drop on switch.
159
- - **Field arrays.** `append` / `prepend` / `insert` / `remove` / `swap` / `move` / `replace`, fully typed at the call site.
160
- - **Undo / redo.** A bounded undo stack via `form.history` (`undo()` / `redo()` / `canUndo` / `canRedo`).
161
- - **Server errors.** `form.setErrors(response.errors)` mounts a server's `ValidationError[]` into the same reactive surface your template already reads. Each error carries an optional `data` payload (a captcha challenge, a lockout time); user errors survive schema revalidation.
162
- - **Stable error codes.** Every `ValidationError` carries `code: string`. Attaform codes (`atta:`) live on the exported `AttaformErrorCode` enum; adapter codes use a `zod:` prefix; consumers pick their own (`api:`, `auth:`, …).
163
- - **Clearable required fields.** The `unset` sentinel marks a field displayed-empty while storage holds the schema's slim default. Submit fails with `'No value supplied'` for required schemas; `.optional()` / `.nullable()` / `.default(N)` opt out.
164
- - **SSR.** Nuxt handles the payload round-trip automatically; bare Vue uses `renderAttaformState` / `hydrateAttaformState`.
63
+ - **The schema is the form.** Types, defaults, validation, per-field errors, and the submit payload all flow from one Zod schema. No `any`, no manual type plumbing, on both Zod v3 and v4 from a single `attaform/zod` import. [Why Attaform →](https://attaform.dev/docs/getting-started/why-attaform)
64
+ - **`useForm` is the core.** A reactive, fully typed form: read `form.values`, `form.errors`, `form.fields`, and `form.meta` directly, then write with `form.setValue`, `form.reset`, and typed field-array helpers. [The form →](https://attaform.dev/docs/reading-the-form/the-form)
65
+ - **`useWizard` for multistep.** Compose forms into a flow with shared navigation, per-step validation, state retained across steps, and deep-link restore. [useWizard →](https://attaform.dev/docs/multistep/use-wizard)
66
+ - **SSR-native.** Server-rendered HTML matches the hydrated client with no flash. Auto-wired in Nuxt, one Vite plugin for bare Vue. [SSR in Nuxt →](https://attaform.dev/docs/server-and-ssr/ssr-nuxt)
67
+ - **DevTools built in.** Inspect every form on the page, walk its history, and edit values live. No probes to install. [DevTools panel →](https://attaform.dev/docs/devtools-and-debugging/devtools-panel)
68
+ - **Secure by construction.** Zero runtime dependencies (no supply-chain surface), prototype-pollution-safe deep writes, and Attaform never throws into your app. [Security policy →](./SECURITY.md)
165
69
 
166
70
  ## Documentation
167
71
 
168
- Full docs live at [www.attaform.com](https://www.attaform.com):
72
+ Full docs live at **[attaform.dev](https://attaform.dev)**.
169
73
 
170
- - [**Quick start**](https://www.attaform.com/docs/getting-started/quick-start): schema, form, submit, in one page.
171
- - [**Why Attaform**](https://www.attaform.com/docs/getting-started/why-attaform): the convictions behind the design.
172
- - [**Entry points**](https://www.attaform.com/docs/reference/entry-points): every public export, grouped by subpath.
173
- - [**useWizard**](https://www.attaform.com/docs/multistep/use-wizard): multistep flows with shared state and validation.
174
- - [**Performance**](https://www.attaform.com/docs/server-and-ssr/performance): how it scales; when to worry.
175
- - [**Troubleshooting**](https://www.attaform.com/docs/devtools-and-debugging/troubleshooting): common gotchas and fixes.
176
- - [**Changelog**](./CHANGELOG.md): full release history.
74
+ - [Quick start](https://attaform.dev/docs/getting-started/quick-start): schema, form, submit, in one page.
75
+ - [Installation](https://attaform.dev/docs/getting-started/installation): Vue, Nuxt, and Vite setup.
76
+ - [Entry points](https://attaform.dev/docs/reference/entry-points): every public export, grouped by subpath.
77
+ - [Performance](https://attaform.dev/docs/server-and-ssr/performance): how it scales, when to worry.
78
+ - [Troubleshooting](https://attaform.dev/docs/devtools-and-debugging/troubleshooting): common gotchas and fixes.
79
+ - [Changelog](./CHANGELOG.md): full release history.
177
80
 
178
81
  ## Status
179
82
 
180
- Pre-1.0. SemVer applies from `v1.0` onward; 0.x minor bumps may still include breaking changes, each documented in the [Changelog](./CHANGELOG.md).
83
+ Pre-1.0. SemVer applies from `v1.0` onward; `0.x` minor bumps may still include breaking changes, each documented in the [changelog](./CHANGELOG.md).
181
84
 
182
- ## License
85
+ ## Security
183
86
 
184
- MIT; see [LICENSE](./LICENSE).
87
+ Found a vulnerability? Please report it privately through the [security policy](./SECURITY.md).
88
+
89
+ ## License
185
90
 
186
- <!-- Badges -->
91
+ © 2026 [Oswald Chisala](https://www.linkedin.com/in/chisalao/). Released under the [MIT License](./LICENSE).
187
92
 
188
93
  [npm-version-src]: https://img.shields.io/npm/v/attaform/latest.svg?style=flat&colorA=020420&colorB=00DC82
189
94
  [npm-version-href]: https://npmjs.com/package/attaform
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const paths = require('../shared/attaform.B7UdTs_o.cjs');
3
+ const paths = require('../shared/attaform.DQcdvxKZ.cjs');
4
4
 
5
5
  const INSPECTOR_ID = "attaform";
6
6
  const TIMELINE_LAYER_ID = "attaform:events";
@@ -1,4 +1,4 @@
1
- import { f as canonicalizePath } from '../shared/attaform.C0au8oXd.mjs';
1
+ import { f as canonicalizePath } from '../shared/attaform.C5qkSN6t.mjs';
2
2
 
3
3
  const INSPECTOR_ID = "attaform";
4
4
  const TIMELINE_LAYER_ID = "attaform:events";
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  const canonicalStringify = require('../shared/attaform.CQN9R62B.cjs');
4
- const index = require('../shared/attaform.D-gYGj5z.cjs');
4
+ const index = require('../shared/attaform.CqlLImc1.cjs');
5
5
 
6
6
  const cyclicSentinel = "<cyclic>";
7
7
  function fingerprintZodSchema(schema) {
@@ -1,5 +1,5 @@
1
1
  import { c as canonicalStringify } from '../shared/attaform.BJGA_UOS.mjs';
2
- import { k as kindOf, g as getIntersectionLeft, b as getIntersectionRight, c as unwrapLazy, d as unwrapInner, e as unwrapPipe, h as getDiscriminator, i as getDiscriminatedOptions, j as getUnionOptions, l as getRecordKeyType, m as getRecordValueType, n as getTupleItems, o as getSetValueType, p as getArrayElement, q as getObjectShape, r as getEnumValues, s as getLiteralValues, t as getChecks, v as getCatchDefault, x as getDefaultValue } from '../shared/attaform.K1MbxmIu.mjs';
2
+ import { k as kindOf, g as getIntersectionLeft, b as getIntersectionRight, c as unwrapLazy, d as unwrapInner, e as unwrapPipe, h as getDiscriminator, i as getDiscriminatedOptions, j as getUnionOptions, l as getRecordKeyType, m as getRecordValueType, n as getTupleItems, o as getSetValueType, p as getArrayElement, q as getObjectShape, r as getEnumValues, s as getLiteralValues, t as getChecks, v as getCatchDefault, x as getDefaultValue } from '../shared/attaform.Dbzwtn4g.mjs';
3
3
 
4
4
  const cyclicSentinel = "<cyclic>";
5
5
  function fingerprintZodSchema(schema) {
package/dist/index.cjs CHANGED
@@ -1,8 +1,8 @@
1
1
  'use strict';
2
2
 
3
- const paths = require('./shared/attaform.B7UdTs_o.cjs');
4
- const devtoolsShared = require('./shared/attaform.BOi6n2Pn.cjs');
5
- const injectWizard = require('./shared/attaform.Bos7wVZw.cjs');
3
+ const paths = require('./shared/attaform.DQcdvxKZ.cjs');
4
+ const devtoolsShared = require('./shared/attaform.DRWrVSkH.cjs');
5
+ const injectWizard = require('./shared/attaform.CB1kYKUB.cjs');
6
6
  const registerProtocol = require('./shared/attaform.CjdepGnw.cjs');
7
7
 
8
8
  function escapeForInlineScript(json) {
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
- export { A as AttaformError, I as InvalidPathError, a as InvalidUseFormConfigError, O as OutsideSetupError, R as ROOT_PATH, b as ROOT_PATH_KEY, c as RegistryNotInstalledError, d as ReservedFormKeyError, S as SubmitErrorHandlerError, e as assignKey, f as canonicalizePath, g as createAttaform, h as createRegistry, i as getRegistryFromApp, j as isPathPrefix, k as kAttaformRegistry, p as parseDottedPath, u as useRegistry, v as vRegister } from './shared/attaform.C0au8oXd.mjs';
2
- export { D as DEVTOOLS_WINDOW_KEY, h as hydrateAttaformState, r as renderAttaformState } from './shared/attaform.0-00cYGw.mjs';
3
- export { A as AttaformErrorCode, D as DEFAULT_TIMINGS, e as defaultCoercionRules, f as defaultDisplayState, g as defineCoercion, i as injectForm, a as injectWizard, b as isUnset, l as lazy, m as makeDefaultDisplayState, u as unset, h as useForm, c as useRegister, d as useWizard } from './shared/attaform.BI7M3wK8.mjs';
1
+ export { A as AttaformError, I as InvalidPathError, a as InvalidUseFormConfigError, O as OutsideSetupError, R as ROOT_PATH, b as ROOT_PATH_KEY, c as RegistryNotInstalledError, d as ReservedFormKeyError, S as SubmitErrorHandlerError, e as assignKey, f as canonicalizePath, g as createAttaform, h as createRegistry, i as getRegistryFromApp, j as isPathPrefix, k as kAttaformRegistry, p as parseDottedPath, u as useRegistry, v as vRegister } from './shared/attaform.C5qkSN6t.mjs';
2
+ export { D as DEVTOOLS_WINDOW_KEY, h as hydrateAttaformState, r as renderAttaformState } from './shared/attaform.DgbHNLf_.mjs';
3
+ export { A as AttaformErrorCode, D as DEFAULT_TIMINGS, e as defaultCoercionRules, f as defaultDisplayState, g as defineCoercion, i as injectForm, a as injectWizard, b as isUnset, l as lazy, m as makeDefaultDisplayState, u as unset, h as useForm, c as useRegister, d as useWizard } from './shared/attaform.B_F61Rx1.mjs';
4
4
  export { i as isRegisterValue } from './shared/attaform.WvcckZMD.mjs';
5
5
 
6
6
  function escapeForInlineScript(json) {
package/dist/nuxt.cjs CHANGED
@@ -25,7 +25,7 @@ var nuxt_default = kit.defineNuxtModule({
25
25
  name: "Attaform",
26
26
  configKey: "attaform",
27
27
  version: pkgVersion,
28
- docs: "https://www.attaform.com/docs",
28
+ docs: "https://attaform.dev/docs",
29
29
  compatibility: {
30
30
  nuxt: ">=3.0.0"
31
31
  }
package/dist/nuxt.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"nuxt.cjs","sources":["../src/nuxt.ts"],"sourcesContent":["import { createRequire } from 'node:module'\nimport { join } from 'node:path'\nimport { pathToFileURL } from 'node:url'\nimport { addImports, addPlugin, addVitePlugin, createResolver, defineNuxtModule } from '@nuxt/kit'\nimport type { AttaformDefaults } from './runtime/types/types-api'\nimport { attaform as attaformVitePlugin } from './vite'\n\n// Read the published version from the package's own package.json so the\n// module's DevTools panel surfaces the live version pill without a\n// build-time string injection. `createRequire` reads sync at module load —\n// run once, free at steady state. Works under unbuild's Rollup output\n// without bundler-specific JSON-import handling.\nconst pkgVersion = (createRequire(import.meta.url)('../package.json') as { version: string })\n .version\n\n/**\n * Options accepted by `attaform/nuxt` under the `attaform`\n * config key.\n *\n * ```ts\n * // nuxt.config.ts\n * export default defineNuxtConfig({\n * modules: ['attaform/nuxt'],\n * attaform: {\n * defaults: { debounceMs: 100 },\n * },\n * })\n * ```\n */\nexport interface AttaformModuleOptions {\n /**\n * App-level defaults applied to every `useForm` call. Per-form\n * options always win. See `AttaformDefaults` for the\n * supported option set and merge rules.\n */\n defaults?: AttaformDefaults\n /**\n * Forwarded to `attaform/vite`'s `resolveZodAlias` option.\n * Default `true` — `attaform/zod` imports are rewritten at build\n * time to either `attaform/zod-v3` or `attaform/zod-v4`, based on\n * the consumer's installed Zod major. Set to `false` to bypass\n * the rewrite and ship the runtime-dispatch unified entry instead.\n */\n resolveZodAlias?: boolean\n}\n\n/**\n * Shape of the Nuxt public runtime-config slot the module populates.\n * Reach it via `useRuntimeConfig().public.attaform` if you need to\n * read the configured defaults outside the form library itself.\n */\nexport type AttaformRuntimeConfig = {\n defaults: AttaformDefaults\n /**\n * Library version, read from the package's own `package.json` at\n * module setup. Surfaced to the runtime plugin so the DevTools\n * overlay panel can render a version pill — single source of truth\n * matches the `meta.version` Nuxt DevTools sees in the Modules panel.\n */\n version: string\n}\n\n/**\n * Whether `specifier` is resolvable using ESM resolution from either the\n * consumer's project root or attaform's own module location. Returning\n * true matches what Vite's resolver would do for `optimizeDeps.include`,\n * so a true here means Vite will pre-bundle the dep without warning.\n *\n * Why two probe locations:\n * - Consumer-rootDir probe finds direct deps + their declared peers\n * (the standard pnpm strict-isolation visibility).\n * - attaform-module probe finds peers attaform itself declares —\n * specifically the optional `@vue/devtools-api`, which lands in\n * attaform's own node_modules tree (or pnpm virtual store) when\n * installed, even if the consumer never references it directly.\n *\n * Why ESM resolution (`import.meta.resolve`) rather than CJS\n * (`createRequire(...).resolve`):\n * - attaform's exports map declares only `import` conditions for\n * non-`/nuxt` entries. CJS resolve hits ERR_PACKAGE_PATH_NOT_EXPORTED\n * for `attaform` and its sub-entries.\n * - pnpm strict isolation hides hoisted transitives behind the\n * virtual store. CJS resolve walks the bare node_modules chain and\n * misses them; ESM resolve follows pnpm's symlinks correctly.\n * - `import.meta.resolve(spec, parentURL)` is sync and stable in\n * Node 20.6+, which attaform already requires (engines.node).\n */\nfunction isResolvableForVite(specifier: string, consumerRootDir: string): boolean {\n const consumerURL = pathToFileURL(join(consumerRootDir, 'package.json')).href\n return canResolve(specifier, consumerURL) || canResolve(specifier, import.meta.url)\n}\n\nfunction canResolve(specifier: string, fromURL: string): boolean {\n try {\n import.meta.resolve(specifier, fromURL)\n return true\n } catch {\n return false\n }\n}\n\nexport default defineNuxtModule<AttaformModuleOptions>({\n meta: {\n name: 'Attaform',\n configKey: 'attaform',\n version: pkgVersion,\n docs: 'https://www.attaform.com/docs',\n compatibility: {\n nuxt: '>=3.0.0',\n },\n },\n defaults: {},\n setup(_options, nuxt) {\n // Register `attaform/vite` so the same plugin handles every Vite-\n // surface concern: pushing the compile-time node transforms into\n // `@vitejs/plugin-vue`'s `api.options.template.compilerOptions.\n // nodeTransforms`, AND rewriting `attaform/zod` imports at build\n // time to either `/zod-v3` or `/zod-v4` based on the consumer's\n // installed Zod major. Consolidating both behaviors in one Vite\n // plugin instance keeps the Nuxt-side wiring minimal and ensures\n // the consumer's Nuxt build sees the exact same DX as a bare-Vite\n // consumer.\n addVitePlugin(attaformVitePlugin({ resolveZodAlias: _options.resolveZodAlias !== false }))\n\n // Publish module options to public runtime config so the plugin can\n // read them at install time on both server and client. Frozen-empty\n // by default — the plugin's merge code reads this slot directly\n // without a `?? {}` guard at every call site.\n const runtimePublic = nuxt.options.runtimeConfig.public as Record<string, unknown>\n runtimePublic['attaform'] = {\n defaults: _options.defaults ?? {},\n version: pkgVersion,\n } satisfies AttaformRuntimeConfig\n\n // Force-include attaform's own peers that Vite's startup crawl\n // tends to miss for Nuxt projects. Vite scans `index.html` + the\n // statically-known entry points but doesn't deeply follow into\n // pages that get loaded via Nuxt's dynamic router; deps imported\n // exclusively from page chunks are discovered when the page first\n // requests, the optimizer rebundles, and Vite silently broadcasts\n // `{\"type\":\"full-reload\",\"path\":\"*\"}` over the HMR WebSocket — what\n // consumers see as \"the page loads, then reloads itself a second\n // later.\" Vite's own \"discovered new dependencies at runtime\"\n // warning recommends exactly this remediation.\n //\n // We declare here only deps attaform itself owns the relationship\n // with — `@vue/devtools-api` (attaform's DevTools integration\n // peer) and `zod` (the adapter peer for `/zod`, `/zod-v3`, and\n // `/zod-v4`). Consumer-\n // side deps (vue-query, immer, etc.) are the consumer's\n // responsibility — they declare them in their own\n // `vite.optimizeDeps.include`. Each push is gated on the spec\n // being resolvable from the consumer's project (or attaform's\n // own module context for attaform's optional peers like\n // devtools-api), so consumers without the optional peer don't see\n // a \"failed to resolve\" warning at boot.\n nuxt.options.vite.optimizeDeps ??= {}\n nuxt.options.vite.optimizeDeps.include ??= []\n const include = nuxt.options.vite.optimizeDeps.include\n for (const spec of ['@vue/devtools-api', 'zod']) {\n if (!isResolvableForVite(spec, nuxt.options.rootDir)) continue\n if (!include.includes(spec)) include.push(spec)\n }\n\n const resolver = createResolver(import.meta.url)\n\n // Auto-import `useForm` — the framework-agnostic core composable (same\n // binding as `attaform`'s top-level `useForm` export, which\n // is the abstract form composable). Consumers who want the zod-typed\n // wrapper must import from `attaform/zod` or `/zod-v3`\n // explicitly.\n //\n // We point at the public package entry rather than a relative\n // `./runtime/…` path on purpose: in the published package the\n // `src/runtime/composables/use-abstract-form` path has no matching\n // `dist/runtime/…` file (build.config's entries don't include it),\n // so a `resolver.resolve(...)` would raise ENOENT at Nuxt's auto-\n // import step. Importing from `attaform` resolves through\n // the shared chunk, identical to what `attaform/zod`\n // consumers bundle — single registry instance across both import\n // surfaces.\n addImports([{ name: 'useForm', from: 'attaform' }])\n\n // Plugin that installs `createAttaform()` on the Vue app and\n // wires the payload serialize/hydrate bridge. Uses a physical\n // `src/runtime/plugins/attaform.ts` file (shipped to\n // `dist/runtime/plugins/attaform.mjs` via an explicit entry in\n // build.config.ts) rather than an inline plugin template, because a\n // template's `import { createAttaform } from 'attaform'`\n // resolves through the `attaform` package entry — which in\n // local dev (`unbuild --stub`) is a jiti runtime transpiler whose\n // `node:module`/`createRequire` imports Nitro's Rollup build cannot\n // bundle. A physical file lets Nitro follow its imports directly\n // (TS source in dev, ESM in the published package), avoiding the\n // jiti indirection entirely. Unbuild's shared-chunk splitter keeps\n // `core/plugin` + `core/serialize` deduplicated with `src/zod` /\n // `src/index`, so there's still only one `registry` module at runtime.\n //\n // `addPlugin` defaults to PREPEND so the plugin runs before any\n // user plugin / page; `enforce: 'pre'` inside the plugin body makes\n // that ordering explicit at the Nuxt-plugin layer too. Together\n // they guarantee the registry is installed (and SSR payload staged\n // into `pendingHydration`) before any `useForm` call runs.\n addPlugin({\n src: resolver.resolve('./runtime/plugins/attaform'),\n })\n\n // Dev-only: register a Nuxt DevTools overlay tab pointing at an\n // iframe that mounts the Attaform inspector. The iframe URL\n // (`/_attaform_devtools`) is served by a Vite-layer middleware\n // wired up in `attaform/vite` — by intercepting at the Vite layer\n // rather than via `extendPages`, the route stays invisible to\n // vue-router and works regardless of whether the consumer uses\n // a `pages/` directory or `app.vue`-only mode.\n //\n // `@nuxt/devtools-kit` is NOT a transitive peer of `@nuxt/kit`; it\n // ships alongside `@nuxt/devtools`. Consumers who don't install\n // Nuxt DevTools (rare in real Nuxt projects, common in test\n // fixtures) get a silent no-op instead of an \"unresolved import\"\n // error. Mirrors the same try/import pattern used for\n // `@vue/devtools-api` in the runtime devtools wire-up.\n if (nuxt.options.dev) {\n nuxt.hook('ready', async () => {\n try {\n const { addCustomTab } = await import('@nuxt/devtools-kit')\n addCustomTab({\n name: 'attaform',\n title: 'Attaform',\n // Brand mark — purple square with a white \"A\" silhouette,\n // matching `apps/site/public/favicon.svg`. Served by the\n // `attaform/vite` middleware at the same `/_attaform_devtools`\n // route family (sibling to the panel HTML). A real URL\n // renders reliably across Nuxt DevTools versions where\n // `data:` URIs don't.\n icon: '/_attaform_devtools/icon.svg',\n view: {\n type: 'iframe',\n src: '/_attaform_devtools',\n persistent: true,\n },\n })\n } catch {\n // Nuxt DevTools (and thus @nuxt/devtools-kit) isn't installed\n // in this consumer — silently skip. The library still works;\n // only the overlay tab is missing.\n }\n })\n }\n },\n})\n"],"names":["createRequire","pathToFileURL","join","defineNuxtModule","addVitePlugin","attaformVitePlugin","createResolver","addImports","addPlugin"],"mappings":"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"}
1
+ {"version":3,"file":"nuxt.cjs","sources":["../src/nuxt.ts"],"sourcesContent":["import { createRequire } from 'node:module'\nimport { join } from 'node:path'\nimport { pathToFileURL } from 'node:url'\nimport { addImports, addPlugin, addVitePlugin, createResolver, defineNuxtModule } from '@nuxt/kit'\nimport type { AttaformDefaults } from './runtime/types/types-api'\nimport { attaform as attaformVitePlugin } from './vite'\n\n// Read the published version from the package's own package.json so the\n// module's DevTools panel surfaces the live version pill without a\n// build-time string injection. `createRequire` reads sync at module load —\n// run once, free at steady state. Works under unbuild's Rollup output\n// without bundler-specific JSON-import handling.\nconst pkgVersion = (createRequire(import.meta.url)('../package.json') as { version: string })\n .version\n\n/**\n * Options accepted by `attaform/nuxt` under the `attaform`\n * config key.\n *\n * ```ts\n * // nuxt.config.ts\n * export default defineNuxtConfig({\n * modules: ['attaform/nuxt'],\n * attaform: {\n * defaults: { debounceMs: 100 },\n * },\n * })\n * ```\n */\nexport interface AttaformModuleOptions {\n /**\n * App-level defaults applied to every `useForm` call. Per-form\n * options always win. See `AttaformDefaults` for the\n * supported option set and merge rules.\n */\n defaults?: AttaformDefaults\n /**\n * Forwarded to `attaform/vite`'s `resolveZodAlias` option.\n * Default `true` — `attaform/zod` imports are rewritten at build\n * time to either `attaform/zod-v3` or `attaform/zod-v4`, based on\n * the consumer's installed Zod major. Set to `false` to bypass\n * the rewrite and ship the runtime-dispatch unified entry instead.\n */\n resolveZodAlias?: boolean\n}\n\n/**\n * Shape of the Nuxt public runtime-config slot the module populates.\n * Reach it via `useRuntimeConfig().public.attaform` if you need to\n * read the configured defaults outside the form library itself.\n */\nexport type AttaformRuntimeConfig = {\n defaults: AttaformDefaults\n /**\n * Library version, read from the package's own `package.json` at\n * module setup. Surfaced to the runtime plugin so the DevTools\n * overlay panel can render a version pill — single source of truth\n * matches the `meta.version` Nuxt DevTools sees in the Modules panel.\n */\n version: string\n}\n\n/**\n * Whether `specifier` is resolvable using ESM resolution from either the\n * consumer's project root or attaform's own module location. Returning\n * true matches what Vite's resolver would do for `optimizeDeps.include`,\n * so a true here means Vite will pre-bundle the dep without warning.\n *\n * Why two probe locations:\n * - Consumer-rootDir probe finds direct deps + their declared peers\n * (the standard pnpm strict-isolation visibility).\n * - attaform-module probe finds peers attaform itself declares —\n * specifically the optional `@vue/devtools-api`, which lands in\n * attaform's own node_modules tree (or pnpm virtual store) when\n * installed, even if the consumer never references it directly.\n *\n * Why ESM resolution (`import.meta.resolve`) rather than CJS\n * (`createRequire(...).resolve`):\n * - attaform's exports map declares only `import` conditions for\n * non-`/nuxt` entries. CJS resolve hits ERR_PACKAGE_PATH_NOT_EXPORTED\n * for `attaform` and its sub-entries.\n * - pnpm strict isolation hides hoisted transitives behind the\n * virtual store. CJS resolve walks the bare node_modules chain and\n * misses them; ESM resolve follows pnpm's symlinks correctly.\n * - `import.meta.resolve(spec, parentURL)` is sync and stable in\n * Node 20.6+, which attaform already requires (engines.node).\n */\nfunction isResolvableForVite(specifier: string, consumerRootDir: string): boolean {\n const consumerURL = pathToFileURL(join(consumerRootDir, 'package.json')).href\n return canResolve(specifier, consumerURL) || canResolve(specifier, import.meta.url)\n}\n\nfunction canResolve(specifier: string, fromURL: string): boolean {\n try {\n import.meta.resolve(specifier, fromURL)\n return true\n } catch {\n return false\n }\n}\n\nexport default defineNuxtModule<AttaformModuleOptions>({\n meta: {\n name: 'Attaform',\n configKey: 'attaform',\n version: pkgVersion,\n docs: 'https://attaform.dev/docs',\n compatibility: {\n nuxt: '>=3.0.0',\n },\n },\n defaults: {},\n setup(_options, nuxt) {\n // Register `attaform/vite` so the same plugin handles every Vite-\n // surface concern: pushing the compile-time node transforms into\n // `@vitejs/plugin-vue`'s `api.options.template.compilerOptions.\n // nodeTransforms`, AND rewriting `attaform/zod` imports at build\n // time to either `/zod-v3` or `/zod-v4` based on the consumer's\n // installed Zod major. Consolidating both behaviors in one Vite\n // plugin instance keeps the Nuxt-side wiring minimal and ensures\n // the consumer's Nuxt build sees the exact same DX as a bare-Vite\n // consumer.\n addVitePlugin(attaformVitePlugin({ resolveZodAlias: _options.resolveZodAlias !== false }))\n\n // Publish module options to public runtime config so the plugin can\n // read them at install time on both server and client. Frozen-empty\n // by default — the plugin's merge code reads this slot directly\n // without a `?? {}` guard at every call site.\n const runtimePublic = nuxt.options.runtimeConfig.public as Record<string, unknown>\n runtimePublic['attaform'] = {\n defaults: _options.defaults ?? {},\n version: pkgVersion,\n } satisfies AttaformRuntimeConfig\n\n // Force-include attaform's own peers that Vite's startup crawl\n // tends to miss for Nuxt projects. Vite scans `index.html` + the\n // statically-known entry points but doesn't deeply follow into\n // pages that get loaded via Nuxt's dynamic router; deps imported\n // exclusively from page chunks are discovered when the page first\n // requests, the optimizer rebundles, and Vite silently broadcasts\n // `{\"type\":\"full-reload\",\"path\":\"*\"}` over the HMR WebSocket — what\n // consumers see as \"the page loads, then reloads itself a second\n // later.\" Vite's own \"discovered new dependencies at runtime\"\n // warning recommends exactly this remediation.\n //\n // We declare here only deps attaform itself owns the relationship\n // with — `@vue/devtools-api` (attaform's DevTools integration\n // peer) and `zod` (the adapter peer for `/zod`, `/zod-v3`, and\n // `/zod-v4`). Consumer-\n // side deps (vue-query, immer, etc.) are the consumer's\n // responsibility — they declare them in their own\n // `vite.optimizeDeps.include`. Each push is gated on the spec\n // being resolvable from the consumer's project (or attaform's\n // own module context for attaform's optional peers like\n // devtools-api), so consumers without the optional peer don't see\n // a \"failed to resolve\" warning at boot.\n nuxt.options.vite.optimizeDeps ??= {}\n nuxt.options.vite.optimizeDeps.include ??= []\n const include = nuxt.options.vite.optimizeDeps.include\n for (const spec of ['@vue/devtools-api', 'zod']) {\n if (!isResolvableForVite(spec, nuxt.options.rootDir)) continue\n if (!include.includes(spec)) include.push(spec)\n }\n\n const resolver = createResolver(import.meta.url)\n\n // Auto-import `useForm` — the framework-agnostic core composable (same\n // binding as `attaform`'s top-level `useForm` export, which\n // is the abstract form composable). Consumers who want the zod-typed\n // wrapper must import from `attaform/zod` or `/zod-v3`\n // explicitly.\n //\n // We point at the public package entry rather than a relative\n // `./runtime/…` path on purpose: in the published package the\n // `src/runtime/composables/use-abstract-form` path has no matching\n // `dist/runtime/…` file (build.config's entries don't include it),\n // so a `resolver.resolve(...)` would raise ENOENT at Nuxt's auto-\n // import step. Importing from `attaform` resolves through\n // the shared chunk, identical to what `attaform/zod`\n // consumers bundle — single registry instance across both import\n // surfaces.\n addImports([{ name: 'useForm', from: 'attaform' }])\n\n // Plugin that installs `createAttaform()` on the Vue app and\n // wires the payload serialize/hydrate bridge. Uses a physical\n // `src/runtime/plugins/attaform.ts` file (shipped to\n // `dist/runtime/plugins/attaform.mjs` via an explicit entry in\n // build.config.ts) rather than an inline plugin template, because a\n // template's `import { createAttaform } from 'attaform'`\n // resolves through the `attaform` package entry — which in\n // local dev (`unbuild --stub`) is a jiti runtime transpiler whose\n // `node:module`/`createRequire` imports Nitro's Rollup build cannot\n // bundle. A physical file lets Nitro follow its imports directly\n // (TS source in dev, ESM in the published package), avoiding the\n // jiti indirection entirely. Unbuild's shared-chunk splitter keeps\n // `core/plugin` + `core/serialize` deduplicated with `src/zod` /\n // `src/index`, so there's still only one `registry` module at runtime.\n //\n // `addPlugin` defaults to PREPEND so the plugin runs before any\n // user plugin / page; `enforce: 'pre'` inside the plugin body makes\n // that ordering explicit at the Nuxt-plugin layer too. Together\n // they guarantee the registry is installed (and SSR payload staged\n // into `pendingHydration`) before any `useForm` call runs.\n addPlugin({\n src: resolver.resolve('./runtime/plugins/attaform'),\n })\n\n // Dev-only: register a Nuxt DevTools overlay tab pointing at an\n // iframe that mounts the Attaform inspector. The iframe URL\n // (`/_attaform_devtools`) is served by a Vite-layer middleware\n // wired up in `attaform/vite` — by intercepting at the Vite layer\n // rather than via `extendPages`, the route stays invisible to\n // vue-router and works regardless of whether the consumer uses\n // a `pages/` directory or `app.vue`-only mode.\n //\n // `@nuxt/devtools-kit` is NOT a transitive peer of `@nuxt/kit`; it\n // ships alongside `@nuxt/devtools`. Consumers who don't install\n // Nuxt DevTools (rare in real Nuxt projects, common in test\n // fixtures) get a silent no-op instead of an \"unresolved import\"\n // error. Mirrors the same try/import pattern used for\n // `@vue/devtools-api` in the runtime devtools wire-up.\n if (nuxt.options.dev) {\n nuxt.hook('ready', async () => {\n try {\n const { addCustomTab } = await import('@nuxt/devtools-kit')\n addCustomTab({\n name: 'attaform',\n title: 'Attaform',\n // Brand mark — purple square with a white \"A\" silhouette,\n // matching `apps/site/public/favicon.svg`. Served by the\n // `attaform/vite` middleware at the same `/_attaform_devtools`\n // route family (sibling to the panel HTML). A real URL\n // renders reliably across Nuxt DevTools versions where\n // `data:` URIs don't.\n icon: '/_attaform_devtools/icon.svg',\n view: {\n type: 'iframe',\n src: '/_attaform_devtools',\n persistent: true,\n },\n })\n } catch {\n // Nuxt DevTools (and thus @nuxt/devtools-kit) isn't installed\n // in this consumer — silently skip. The library still works;\n // only the overlay tab is missing.\n }\n })\n }\n },\n})\n"],"names":["createRequire","pathToFileURL","join","defineNuxtModule","addVitePlugin","attaformVitePlugin","createResolver","addImports","addPlugin"],"mappings":"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"}
package/dist/nuxt.mjs CHANGED
@@ -22,7 +22,7 @@ var nuxt_default = defineNuxtModule({
22
22
  name: "Attaform",
23
23
  configKey: "attaform",
24
24
  version: pkgVersion,
25
- docs: "https://www.attaform.com/docs",
25
+ docs: "https://attaform.dev/docs",
26
26
  compatibility: {
27
27
  nuxt: ">=3.0.0"
28
28
  }
package/dist/nuxt.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"nuxt.mjs","sources":["../src/nuxt.ts"],"sourcesContent":["import { createRequire } from 'node:module'\nimport { join } from 'node:path'\nimport { pathToFileURL } from 'node:url'\nimport { addImports, addPlugin, addVitePlugin, createResolver, defineNuxtModule } from '@nuxt/kit'\nimport type { AttaformDefaults } from './runtime/types/types-api'\nimport { attaform as attaformVitePlugin } from './vite'\n\n// Read the published version from the package's own package.json so the\n// module's DevTools panel surfaces the live version pill without a\n// build-time string injection. `createRequire` reads sync at module load —\n// run once, free at steady state. Works under unbuild's Rollup output\n// without bundler-specific JSON-import handling.\nconst pkgVersion = (createRequire(import.meta.url)('../package.json') as { version: string })\n .version\n\n/**\n * Options accepted by `attaform/nuxt` under the `attaform`\n * config key.\n *\n * ```ts\n * // nuxt.config.ts\n * export default defineNuxtConfig({\n * modules: ['attaform/nuxt'],\n * attaform: {\n * defaults: { debounceMs: 100 },\n * },\n * })\n * ```\n */\nexport interface AttaformModuleOptions {\n /**\n * App-level defaults applied to every `useForm` call. Per-form\n * options always win. See `AttaformDefaults` for the\n * supported option set and merge rules.\n */\n defaults?: AttaformDefaults\n /**\n * Forwarded to `attaform/vite`'s `resolveZodAlias` option.\n * Default `true` — `attaform/zod` imports are rewritten at build\n * time to either `attaform/zod-v3` or `attaform/zod-v4`, based on\n * the consumer's installed Zod major. Set to `false` to bypass\n * the rewrite and ship the runtime-dispatch unified entry instead.\n */\n resolveZodAlias?: boolean\n}\n\n/**\n * Shape of the Nuxt public runtime-config slot the module populates.\n * Reach it via `useRuntimeConfig().public.attaform` if you need to\n * read the configured defaults outside the form library itself.\n */\nexport type AttaformRuntimeConfig = {\n defaults: AttaformDefaults\n /**\n * Library version, read from the package's own `package.json` at\n * module setup. Surfaced to the runtime plugin so the DevTools\n * overlay panel can render a version pill — single source of truth\n * matches the `meta.version` Nuxt DevTools sees in the Modules panel.\n */\n version: string\n}\n\n/**\n * Whether `specifier` is resolvable using ESM resolution from either the\n * consumer's project root or attaform's own module location. Returning\n * true matches what Vite's resolver would do for `optimizeDeps.include`,\n * so a true here means Vite will pre-bundle the dep without warning.\n *\n * Why two probe locations:\n * - Consumer-rootDir probe finds direct deps + their declared peers\n * (the standard pnpm strict-isolation visibility).\n * - attaform-module probe finds peers attaform itself declares —\n * specifically the optional `@vue/devtools-api`, which lands in\n * attaform's own node_modules tree (or pnpm virtual store) when\n * installed, even if the consumer never references it directly.\n *\n * Why ESM resolution (`import.meta.resolve`) rather than CJS\n * (`createRequire(...).resolve`):\n * - attaform's exports map declares only `import` conditions for\n * non-`/nuxt` entries. CJS resolve hits ERR_PACKAGE_PATH_NOT_EXPORTED\n * for `attaform` and its sub-entries.\n * - pnpm strict isolation hides hoisted transitives behind the\n * virtual store. CJS resolve walks the bare node_modules chain and\n * misses them; ESM resolve follows pnpm's symlinks correctly.\n * - `import.meta.resolve(spec, parentURL)` is sync and stable in\n * Node 20.6+, which attaform already requires (engines.node).\n */\nfunction isResolvableForVite(specifier: string, consumerRootDir: string): boolean {\n const consumerURL = pathToFileURL(join(consumerRootDir, 'package.json')).href\n return canResolve(specifier, consumerURL) || canResolve(specifier, import.meta.url)\n}\n\nfunction canResolve(specifier: string, fromURL: string): boolean {\n try {\n import.meta.resolve(specifier, fromURL)\n return true\n } catch {\n return false\n }\n}\n\nexport default defineNuxtModule<AttaformModuleOptions>({\n meta: {\n name: 'Attaform',\n configKey: 'attaform',\n version: pkgVersion,\n docs: 'https://www.attaform.com/docs',\n compatibility: {\n nuxt: '>=3.0.0',\n },\n },\n defaults: {},\n setup(_options, nuxt) {\n // Register `attaform/vite` so the same plugin handles every Vite-\n // surface concern: pushing the compile-time node transforms into\n // `@vitejs/plugin-vue`'s `api.options.template.compilerOptions.\n // nodeTransforms`, AND rewriting `attaform/zod` imports at build\n // time to either `/zod-v3` or `/zod-v4` based on the consumer's\n // installed Zod major. Consolidating both behaviors in one Vite\n // plugin instance keeps the Nuxt-side wiring minimal and ensures\n // the consumer's Nuxt build sees the exact same DX as a bare-Vite\n // consumer.\n addVitePlugin(attaformVitePlugin({ resolveZodAlias: _options.resolveZodAlias !== false }))\n\n // Publish module options to public runtime config so the plugin can\n // read them at install time on both server and client. Frozen-empty\n // by default — the plugin's merge code reads this slot directly\n // without a `?? {}` guard at every call site.\n const runtimePublic = nuxt.options.runtimeConfig.public as Record<string, unknown>\n runtimePublic['attaform'] = {\n defaults: _options.defaults ?? {},\n version: pkgVersion,\n } satisfies AttaformRuntimeConfig\n\n // Force-include attaform's own peers that Vite's startup crawl\n // tends to miss for Nuxt projects. Vite scans `index.html` + the\n // statically-known entry points but doesn't deeply follow into\n // pages that get loaded via Nuxt's dynamic router; deps imported\n // exclusively from page chunks are discovered when the page first\n // requests, the optimizer rebundles, and Vite silently broadcasts\n // `{\"type\":\"full-reload\",\"path\":\"*\"}` over the HMR WebSocket — what\n // consumers see as \"the page loads, then reloads itself a second\n // later.\" Vite's own \"discovered new dependencies at runtime\"\n // warning recommends exactly this remediation.\n //\n // We declare here only deps attaform itself owns the relationship\n // with — `@vue/devtools-api` (attaform's DevTools integration\n // peer) and `zod` (the adapter peer for `/zod`, `/zod-v3`, and\n // `/zod-v4`). Consumer-\n // side deps (vue-query, immer, etc.) are the consumer's\n // responsibility — they declare them in their own\n // `vite.optimizeDeps.include`. Each push is gated on the spec\n // being resolvable from the consumer's project (or attaform's\n // own module context for attaform's optional peers like\n // devtools-api), so consumers without the optional peer don't see\n // a \"failed to resolve\" warning at boot.\n nuxt.options.vite.optimizeDeps ??= {}\n nuxt.options.vite.optimizeDeps.include ??= []\n const include = nuxt.options.vite.optimizeDeps.include\n for (const spec of ['@vue/devtools-api', 'zod']) {\n if (!isResolvableForVite(spec, nuxt.options.rootDir)) continue\n if (!include.includes(spec)) include.push(spec)\n }\n\n const resolver = createResolver(import.meta.url)\n\n // Auto-import `useForm` — the framework-agnostic core composable (same\n // binding as `attaform`'s top-level `useForm` export, which\n // is the abstract form composable). Consumers who want the zod-typed\n // wrapper must import from `attaform/zod` or `/zod-v3`\n // explicitly.\n //\n // We point at the public package entry rather than a relative\n // `./runtime/…` path on purpose: in the published package the\n // `src/runtime/composables/use-abstract-form` path has no matching\n // `dist/runtime/…` file (build.config's entries don't include it),\n // so a `resolver.resolve(...)` would raise ENOENT at Nuxt's auto-\n // import step. Importing from `attaform` resolves through\n // the shared chunk, identical to what `attaform/zod`\n // consumers bundle — single registry instance across both import\n // surfaces.\n addImports([{ name: 'useForm', from: 'attaform' }])\n\n // Plugin that installs `createAttaform()` on the Vue app and\n // wires the payload serialize/hydrate bridge. Uses a physical\n // `src/runtime/plugins/attaform.ts` file (shipped to\n // `dist/runtime/plugins/attaform.mjs` via an explicit entry in\n // build.config.ts) rather than an inline plugin template, because a\n // template's `import { createAttaform } from 'attaform'`\n // resolves through the `attaform` package entry — which in\n // local dev (`unbuild --stub`) is a jiti runtime transpiler whose\n // `node:module`/`createRequire` imports Nitro's Rollup build cannot\n // bundle. A physical file lets Nitro follow its imports directly\n // (TS source in dev, ESM in the published package), avoiding the\n // jiti indirection entirely. Unbuild's shared-chunk splitter keeps\n // `core/plugin` + `core/serialize` deduplicated with `src/zod` /\n // `src/index`, so there's still only one `registry` module at runtime.\n //\n // `addPlugin` defaults to PREPEND so the plugin runs before any\n // user plugin / page; `enforce: 'pre'` inside the plugin body makes\n // that ordering explicit at the Nuxt-plugin layer too. Together\n // they guarantee the registry is installed (and SSR payload staged\n // into `pendingHydration`) before any `useForm` call runs.\n addPlugin({\n src: resolver.resolve('./runtime/plugins/attaform'),\n })\n\n // Dev-only: register a Nuxt DevTools overlay tab pointing at an\n // iframe that mounts the Attaform inspector. The iframe URL\n // (`/_attaform_devtools`) is served by a Vite-layer middleware\n // wired up in `attaform/vite` — by intercepting at the Vite layer\n // rather than via `extendPages`, the route stays invisible to\n // vue-router and works regardless of whether the consumer uses\n // a `pages/` directory or `app.vue`-only mode.\n //\n // `@nuxt/devtools-kit` is NOT a transitive peer of `@nuxt/kit`; it\n // ships alongside `@nuxt/devtools`. Consumers who don't install\n // Nuxt DevTools (rare in real Nuxt projects, common in test\n // fixtures) get a silent no-op instead of an \"unresolved import\"\n // error. Mirrors the same try/import pattern used for\n // `@vue/devtools-api` in the runtime devtools wire-up.\n if (nuxt.options.dev) {\n nuxt.hook('ready', async () => {\n try {\n const { addCustomTab } = await import('@nuxt/devtools-kit')\n addCustomTab({\n name: 'attaform',\n title: 'Attaform',\n // Brand mark — purple square with a white \"A\" silhouette,\n // matching `apps/site/public/favicon.svg`. Served by the\n // `attaform/vite` middleware at the same `/_attaform_devtools`\n // route family (sibling to the panel HTML). A real URL\n // renders reliably across Nuxt DevTools versions where\n // `data:` URIs don't.\n icon: '/_attaform_devtools/icon.svg',\n view: {\n type: 'iframe',\n src: '/_attaform_devtools',\n persistent: true,\n },\n })\n } catch {\n // Nuxt DevTools (and thus @nuxt/devtools-kit) isn't installed\n // in this consumer — silently skip. The library still works;\n // only the overlay tab is missing.\n }\n })\n }\n },\n})\n"],"names":["attaformVitePlugin"],"mappings":";;;;;;AAYA,MAAM,aAAc,aAAA,CAAc,MAAA,CAAA,IAAA,CAAY,GAAG,CAAA,CAAE,iBAAiB,CAAA,CACjE,OAAA;AA0EH,SAAS,mBAAA,CAAoB,WAAmB,eAAA,EAAkC;AAChF,EAAA,MAAM,cAAc,aAAA,CAAc,IAAA,CAAK,eAAA,EAAiB,cAAc,CAAC,CAAA,CAAE,IAAA;AACzE,EAAA,OAAO,WAAW,SAAA,EAAW,WAAW,KAAK,UAAA,CAAW,SAAA,EAAW,YAAY,GAAG,CAAA;AACpF;AAEA,SAAS,UAAA,CAAW,WAAmB,OAAA,EAA0B;AAC/D,EAAA,IAAI;AACF,IAAA,MAAA,CAAA,IAAA,CAAY,OAAA,CAAQ,WAAW,OAAO,CAAA;AACtC,IAAA,OAAO,IAAA;AAAA,EACT,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,KAAA;AAAA,EACT;AACF;AAEA,IAAO,eAAQ,gBAAA,CAAwC;AAAA,EACrD,IAAA,EAAM;AAAA,IACJ,IAAA,EAAM,UAAA;AAAA,IACN,SAAA,EAAW,UAAA;AAAA,IACX,OAAA,EAAS,UAAA;AAAA,IACT,IAAA,EAAM,+BAAA;AAAA,IACN,aAAA,EAAe;AAAA,MACb,IAAA,EAAM;AAAA;AACR,GACF;AAAA,EACA,UAAU,EAAC;AAAA,EACX,KAAA,CAAM,UAAU,IAAA,EAAM;AAhHxB,IAAA,IAAA,EAAA,EAAA,EAAA;AA0HI,IAAA,aAAA,CAAcA,SAAmB,EAAE,eAAA,EAAiB,SAAS,eAAA,KAAoB,KAAA,EAAO,CAAC,CAAA;AAMzF,IAAA,MAAM,aAAA,GAAgB,IAAA,CAAK,OAAA,CAAQ,aAAA,CAAc,MAAA;AACjD,IAAA,aAAA,CAAc,UAAU,CAAA,GAAI;AAAA,MAC1B,QAAA,EAAU,QAAA,CAAS,QAAA,IAAY,EAAC;AAAA,MAChC,OAAA,EAAS;AAAA,KACX;AAwBA,IAAA,CAAA,EAAA,GAAA,IAAA,CAAK,OAAA,CAAQ,IAAA,EAAK,YAAA,KAAlB,EAAA,CAAkB,eAAiB,EAAC,CAAA;AACpC,IAAA,CAAA,EAAA,GAAA,IAAA,CAAK,QAAQ,IAAA,CAAK,YAAA,EAAa,OAAA,KAA/B,EAAA,CAA+B,UAAY,EAAC,CAAA;AAC5C,IAAA,MAAM,OAAA,GAAU,IAAA,CAAK,OAAA,CAAQ,IAAA,CAAK,YAAA,CAAa,OAAA;AAC/C,IAAA,KAAA,MAAW,IAAA,IAAQ,CAAC,mBAAA,EAAqB,KAAK,CAAA,EAAG;AAC/C,MAAA,IAAI,CAAC,mBAAA,CAAoB,IAAA,EAAM,IAAA,CAAK,OAAA,CAAQ,OAAO,CAAA,EAAG;AACtD,MAAA,IAAI,CAAC,OAAA,CAAQ,QAAA,CAAS,IAAI,CAAA,EAAG,OAAA,CAAQ,KAAK,IAAI,CAAA;AAAA,IAChD;AAEA,IAAA,MAAM,QAAA,GAAW,cAAA,CAAe,MAAA,CAAA,IAAA,CAAY,GAAG,CAAA;AAiB/C,IAAA,UAAA,CAAW,CAAC,EAAE,IAAA,EAAM,WAAW,IAAA,EAAM,UAAA,EAAY,CAAC,CAAA;AAsBlD,IAAA,SAAA,CAAU;AAAA,MACR,GAAA,EAAK,QAAA,CAAS,OAAA,CAAQ,4BAA4B;AAAA,KACnD,CAAA;AAgBD,IAAA,IAAI,IAAA,CAAK,QAAQ,GAAA,EAAK;AACpB,MAAA,IAAA,CAAK,IAAA,CAAK,SAAS,YAAY;AAC7B,QAAA,IAAI;AACF,UAAA,MAAM,EAAE,YAAA,EAAa,GAAI,MAAM,OAAO,oBAAoB,CAAA;AAC1D,UAAA,YAAA,CAAa;AAAA,YACX,IAAA,EAAM,UAAA;AAAA,YACN,KAAA,EAAO,UAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,YAOP,IAAA,EAAM,8BAAA;AAAA,YACN,IAAA,EAAM;AAAA,cACJ,IAAA,EAAM,QAAA;AAAA,cACN,GAAA,EAAK,qBAAA;AAAA,cACL,UAAA,EAAY;AAAA;AACd,WACD,CAAA;AAAA,QACH,CAAA,CAAA,MAAQ;AAAA,QAIR;AAAA,MACF,CAAC,CAAA;AAAA,IACH;AAAA,EACF;AACF,CAAC;;;;"}
1
+ {"version":3,"file":"nuxt.mjs","sources":["../src/nuxt.ts"],"sourcesContent":["import { createRequire } from 'node:module'\nimport { join } from 'node:path'\nimport { pathToFileURL } from 'node:url'\nimport { addImports, addPlugin, addVitePlugin, createResolver, defineNuxtModule } from '@nuxt/kit'\nimport type { AttaformDefaults } from './runtime/types/types-api'\nimport { attaform as attaformVitePlugin } from './vite'\n\n// Read the published version from the package's own package.json so the\n// module's DevTools panel surfaces the live version pill without a\n// build-time string injection. `createRequire` reads sync at module load —\n// run once, free at steady state. Works under unbuild's Rollup output\n// without bundler-specific JSON-import handling.\nconst pkgVersion = (createRequire(import.meta.url)('../package.json') as { version: string })\n .version\n\n/**\n * Options accepted by `attaform/nuxt` under the `attaform`\n * config key.\n *\n * ```ts\n * // nuxt.config.ts\n * export default defineNuxtConfig({\n * modules: ['attaform/nuxt'],\n * attaform: {\n * defaults: { debounceMs: 100 },\n * },\n * })\n * ```\n */\nexport interface AttaformModuleOptions {\n /**\n * App-level defaults applied to every `useForm` call. Per-form\n * options always win. See `AttaformDefaults` for the\n * supported option set and merge rules.\n */\n defaults?: AttaformDefaults\n /**\n * Forwarded to `attaform/vite`'s `resolveZodAlias` option.\n * Default `true` — `attaform/zod` imports are rewritten at build\n * time to either `attaform/zod-v3` or `attaform/zod-v4`, based on\n * the consumer's installed Zod major. Set to `false` to bypass\n * the rewrite and ship the runtime-dispatch unified entry instead.\n */\n resolveZodAlias?: boolean\n}\n\n/**\n * Shape of the Nuxt public runtime-config slot the module populates.\n * Reach it via `useRuntimeConfig().public.attaform` if you need to\n * read the configured defaults outside the form library itself.\n */\nexport type AttaformRuntimeConfig = {\n defaults: AttaformDefaults\n /**\n * Library version, read from the package's own `package.json` at\n * module setup. Surfaced to the runtime plugin so the DevTools\n * overlay panel can render a version pill — single source of truth\n * matches the `meta.version` Nuxt DevTools sees in the Modules panel.\n */\n version: string\n}\n\n/**\n * Whether `specifier` is resolvable using ESM resolution from either the\n * consumer's project root or attaform's own module location. Returning\n * true matches what Vite's resolver would do for `optimizeDeps.include`,\n * so a true here means Vite will pre-bundle the dep without warning.\n *\n * Why two probe locations:\n * - Consumer-rootDir probe finds direct deps + their declared peers\n * (the standard pnpm strict-isolation visibility).\n * - attaform-module probe finds peers attaform itself declares —\n * specifically the optional `@vue/devtools-api`, which lands in\n * attaform's own node_modules tree (or pnpm virtual store) when\n * installed, even if the consumer never references it directly.\n *\n * Why ESM resolution (`import.meta.resolve`) rather than CJS\n * (`createRequire(...).resolve`):\n * - attaform's exports map declares only `import` conditions for\n * non-`/nuxt` entries. CJS resolve hits ERR_PACKAGE_PATH_NOT_EXPORTED\n * for `attaform` and its sub-entries.\n * - pnpm strict isolation hides hoisted transitives behind the\n * virtual store. CJS resolve walks the bare node_modules chain and\n * misses them; ESM resolve follows pnpm's symlinks correctly.\n * - `import.meta.resolve(spec, parentURL)` is sync and stable in\n * Node 20.6+, which attaform already requires (engines.node).\n */\nfunction isResolvableForVite(specifier: string, consumerRootDir: string): boolean {\n const consumerURL = pathToFileURL(join(consumerRootDir, 'package.json')).href\n return canResolve(specifier, consumerURL) || canResolve(specifier, import.meta.url)\n}\n\nfunction canResolve(specifier: string, fromURL: string): boolean {\n try {\n import.meta.resolve(specifier, fromURL)\n return true\n } catch {\n return false\n }\n}\n\nexport default defineNuxtModule<AttaformModuleOptions>({\n meta: {\n name: 'Attaform',\n configKey: 'attaform',\n version: pkgVersion,\n docs: 'https://attaform.dev/docs',\n compatibility: {\n nuxt: '>=3.0.0',\n },\n },\n defaults: {},\n setup(_options, nuxt) {\n // Register `attaform/vite` so the same plugin handles every Vite-\n // surface concern: pushing the compile-time node transforms into\n // `@vitejs/plugin-vue`'s `api.options.template.compilerOptions.\n // nodeTransforms`, AND rewriting `attaform/zod` imports at build\n // time to either `/zod-v3` or `/zod-v4` based on the consumer's\n // installed Zod major. Consolidating both behaviors in one Vite\n // plugin instance keeps the Nuxt-side wiring minimal and ensures\n // the consumer's Nuxt build sees the exact same DX as a bare-Vite\n // consumer.\n addVitePlugin(attaformVitePlugin({ resolveZodAlias: _options.resolveZodAlias !== false }))\n\n // Publish module options to public runtime config so the plugin can\n // read them at install time on both server and client. Frozen-empty\n // by default — the plugin's merge code reads this slot directly\n // without a `?? {}` guard at every call site.\n const runtimePublic = nuxt.options.runtimeConfig.public as Record<string, unknown>\n runtimePublic['attaform'] = {\n defaults: _options.defaults ?? {},\n version: pkgVersion,\n } satisfies AttaformRuntimeConfig\n\n // Force-include attaform's own peers that Vite's startup crawl\n // tends to miss for Nuxt projects. Vite scans `index.html` + the\n // statically-known entry points but doesn't deeply follow into\n // pages that get loaded via Nuxt's dynamic router; deps imported\n // exclusively from page chunks are discovered when the page first\n // requests, the optimizer rebundles, and Vite silently broadcasts\n // `{\"type\":\"full-reload\",\"path\":\"*\"}` over the HMR WebSocket — what\n // consumers see as \"the page loads, then reloads itself a second\n // later.\" Vite's own \"discovered new dependencies at runtime\"\n // warning recommends exactly this remediation.\n //\n // We declare here only deps attaform itself owns the relationship\n // with — `@vue/devtools-api` (attaform's DevTools integration\n // peer) and `zod` (the adapter peer for `/zod`, `/zod-v3`, and\n // `/zod-v4`). Consumer-\n // side deps (vue-query, immer, etc.) are the consumer's\n // responsibility — they declare them in their own\n // `vite.optimizeDeps.include`. Each push is gated on the spec\n // being resolvable from the consumer's project (or attaform's\n // own module context for attaform's optional peers like\n // devtools-api), so consumers without the optional peer don't see\n // a \"failed to resolve\" warning at boot.\n nuxt.options.vite.optimizeDeps ??= {}\n nuxt.options.vite.optimizeDeps.include ??= []\n const include = nuxt.options.vite.optimizeDeps.include\n for (const spec of ['@vue/devtools-api', 'zod']) {\n if (!isResolvableForVite(spec, nuxt.options.rootDir)) continue\n if (!include.includes(spec)) include.push(spec)\n }\n\n const resolver = createResolver(import.meta.url)\n\n // Auto-import `useForm` — the framework-agnostic core composable (same\n // binding as `attaform`'s top-level `useForm` export, which\n // is the abstract form composable). Consumers who want the zod-typed\n // wrapper must import from `attaform/zod` or `/zod-v3`\n // explicitly.\n //\n // We point at the public package entry rather than a relative\n // `./runtime/…` path on purpose: in the published package the\n // `src/runtime/composables/use-abstract-form` path has no matching\n // `dist/runtime/…` file (build.config's entries don't include it),\n // so a `resolver.resolve(...)` would raise ENOENT at Nuxt's auto-\n // import step. Importing from `attaform` resolves through\n // the shared chunk, identical to what `attaform/zod`\n // consumers bundle — single registry instance across both import\n // surfaces.\n addImports([{ name: 'useForm', from: 'attaform' }])\n\n // Plugin that installs `createAttaform()` on the Vue app and\n // wires the payload serialize/hydrate bridge. Uses a physical\n // `src/runtime/plugins/attaform.ts` file (shipped to\n // `dist/runtime/plugins/attaform.mjs` via an explicit entry in\n // build.config.ts) rather than an inline plugin template, because a\n // template's `import { createAttaform } from 'attaform'`\n // resolves through the `attaform` package entry — which in\n // local dev (`unbuild --stub`) is a jiti runtime transpiler whose\n // `node:module`/`createRequire` imports Nitro's Rollup build cannot\n // bundle. A physical file lets Nitro follow its imports directly\n // (TS source in dev, ESM in the published package), avoiding the\n // jiti indirection entirely. Unbuild's shared-chunk splitter keeps\n // `core/plugin` + `core/serialize` deduplicated with `src/zod` /\n // `src/index`, so there's still only one `registry` module at runtime.\n //\n // `addPlugin` defaults to PREPEND so the plugin runs before any\n // user plugin / page; `enforce: 'pre'` inside the plugin body makes\n // that ordering explicit at the Nuxt-plugin layer too. Together\n // they guarantee the registry is installed (and SSR payload staged\n // into `pendingHydration`) before any `useForm` call runs.\n addPlugin({\n src: resolver.resolve('./runtime/plugins/attaform'),\n })\n\n // Dev-only: register a Nuxt DevTools overlay tab pointing at an\n // iframe that mounts the Attaform inspector. The iframe URL\n // (`/_attaform_devtools`) is served by a Vite-layer middleware\n // wired up in `attaform/vite` — by intercepting at the Vite layer\n // rather than via `extendPages`, the route stays invisible to\n // vue-router and works regardless of whether the consumer uses\n // a `pages/` directory or `app.vue`-only mode.\n //\n // `@nuxt/devtools-kit` is NOT a transitive peer of `@nuxt/kit`; it\n // ships alongside `@nuxt/devtools`. Consumers who don't install\n // Nuxt DevTools (rare in real Nuxt projects, common in test\n // fixtures) get a silent no-op instead of an \"unresolved import\"\n // error. Mirrors the same try/import pattern used for\n // `@vue/devtools-api` in the runtime devtools wire-up.\n if (nuxt.options.dev) {\n nuxt.hook('ready', async () => {\n try {\n const { addCustomTab } = await import('@nuxt/devtools-kit')\n addCustomTab({\n name: 'attaform',\n title: 'Attaform',\n // Brand mark — purple square with a white \"A\" silhouette,\n // matching `apps/site/public/favicon.svg`. Served by the\n // `attaform/vite` middleware at the same `/_attaform_devtools`\n // route family (sibling to the panel HTML). A real URL\n // renders reliably across Nuxt DevTools versions where\n // `data:` URIs don't.\n icon: '/_attaform_devtools/icon.svg',\n view: {\n type: 'iframe',\n src: '/_attaform_devtools',\n persistent: true,\n },\n })\n } catch {\n // Nuxt DevTools (and thus @nuxt/devtools-kit) isn't installed\n // in this consumer — silently skip. The library still works;\n // only the overlay tab is missing.\n }\n })\n }\n },\n})\n"],"names":["attaformVitePlugin"],"mappings":";;;;;;AAYA,MAAM,aAAc,aAAA,CAAc,MAAA,CAAA,IAAA,CAAY,GAAG,CAAA,CAAE,iBAAiB,CAAA,CACjE,OAAA;AA0EH,SAAS,mBAAA,CAAoB,WAAmB,eAAA,EAAkC;AAChF,EAAA,MAAM,cAAc,aAAA,CAAc,IAAA,CAAK,eAAA,EAAiB,cAAc,CAAC,CAAA,CAAE,IAAA;AACzE,EAAA,OAAO,WAAW,SAAA,EAAW,WAAW,KAAK,UAAA,CAAW,SAAA,EAAW,YAAY,GAAG,CAAA;AACpF;AAEA,SAAS,UAAA,CAAW,WAAmB,OAAA,EAA0B;AAC/D,EAAA,IAAI;AACF,IAAA,MAAA,CAAA,IAAA,CAAY,OAAA,CAAQ,WAAW,OAAO,CAAA;AACtC,IAAA,OAAO,IAAA;AAAA,EACT,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,KAAA;AAAA,EACT;AACF;AAEA,IAAO,eAAQ,gBAAA,CAAwC;AAAA,EACrD,IAAA,EAAM;AAAA,IACJ,IAAA,EAAM,UAAA;AAAA,IACN,SAAA,EAAW,UAAA;AAAA,IACX,OAAA,EAAS,UAAA;AAAA,IACT,IAAA,EAAM,2BAAA;AAAA,IACN,aAAA,EAAe;AAAA,MACb,IAAA,EAAM;AAAA;AACR,GACF;AAAA,EACA,UAAU,EAAC;AAAA,EACX,KAAA,CAAM,UAAU,IAAA,EAAM;AAhHxB,IAAA,IAAA,EAAA,EAAA,EAAA;AA0HI,IAAA,aAAA,CAAcA,SAAmB,EAAE,eAAA,EAAiB,SAAS,eAAA,KAAoB,KAAA,EAAO,CAAC,CAAA;AAMzF,IAAA,MAAM,aAAA,GAAgB,IAAA,CAAK,OAAA,CAAQ,aAAA,CAAc,MAAA;AACjD,IAAA,aAAA,CAAc,UAAU,CAAA,GAAI;AAAA,MAC1B,QAAA,EAAU,QAAA,CAAS,QAAA,IAAY,EAAC;AAAA,MAChC,OAAA,EAAS;AAAA,KACX;AAwBA,IAAA,CAAA,EAAA,GAAA,IAAA,CAAK,OAAA,CAAQ,IAAA,EAAK,YAAA,KAAlB,EAAA,CAAkB,eAAiB,EAAC,CAAA;AACpC,IAAA,CAAA,EAAA,GAAA,IAAA,CAAK,QAAQ,IAAA,CAAK,YAAA,EAAa,OAAA,KAA/B,EAAA,CAA+B,UAAY,EAAC,CAAA;AAC5C,IAAA,MAAM,OAAA,GAAU,IAAA,CAAK,OAAA,CAAQ,IAAA,CAAK,YAAA,CAAa,OAAA;AAC/C,IAAA,KAAA,MAAW,IAAA,IAAQ,CAAC,mBAAA,EAAqB,KAAK,CAAA,EAAG;AAC/C,MAAA,IAAI,CAAC,mBAAA,CAAoB,IAAA,EAAM,IAAA,CAAK,OAAA,CAAQ,OAAO,CAAA,EAAG;AACtD,MAAA,IAAI,CAAC,OAAA,CAAQ,QAAA,CAAS,IAAI,CAAA,EAAG,OAAA,CAAQ,KAAK,IAAI,CAAA;AAAA,IAChD;AAEA,IAAA,MAAM,QAAA,GAAW,cAAA,CAAe,MAAA,CAAA,IAAA,CAAY,GAAG,CAAA;AAiB/C,IAAA,UAAA,CAAW,CAAC,EAAE,IAAA,EAAM,WAAW,IAAA,EAAM,UAAA,EAAY,CAAC,CAAA;AAsBlD,IAAA,SAAA,CAAU;AAAA,MACR,GAAA,EAAK,QAAA,CAAS,OAAA,CAAQ,4BAA4B;AAAA,KACnD,CAAA;AAgBD,IAAA,IAAI,IAAA,CAAK,QAAQ,GAAA,EAAK;AACpB,MAAA,IAAA,CAAK,IAAA,CAAK,SAAS,YAAY;AAC7B,QAAA,IAAI;AACF,UAAA,MAAM,EAAE,YAAA,EAAa,GAAI,MAAM,OAAO,oBAAoB,CAAA;AAC1D,UAAA,YAAA,CAAa;AAAA,YACX,IAAA,EAAM,UAAA;AAAA,YACN,KAAA,EAAO,UAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,YAOP,IAAA,EAAM,8BAAA;AAAA,YACN,IAAA,EAAM;AAAA,cACJ,IAAA,EAAM,QAAA;AAAA,cACN,GAAA,EAAK,qBAAA;AAAA,cACL,UAAA,EAAY;AAAA;AACd,WACD,CAAA;AAAA,QACH,CAAA,CAAA,MAAQ;AAAA,QAIR;AAAA,MACF,CAAC,CAAA;AAAA,IACH;AAAA,EACF;AACF,CAAC;;;;"}
@@ -1,8 +1,8 @@
1
1
  'use strict';
2
2
 
3
3
  const app = require('nuxt/app');
4
- const devtoolsShared = require('../../shared/attaform.BOi6n2Pn.cjs');
5
- const paths = require('../../shared/attaform.B7UdTs_o.cjs');
4
+ const devtoolsShared = require('../../shared/attaform.DRWrVSkH.cjs');
5
+ const paths = require('../../shared/attaform.DQcdvxKZ.cjs');
6
6
 
7
7
  var attaform_default = app.defineNuxtPlugin({
8
8
  // `enforce: 'pre'` makes the "we run before any component's setup" claim
@@ -1,6 +1,6 @@
1
1
  import { defineNuxtPlugin, useRuntimeConfig, useRoute } from 'nuxt/app';
2
- import { r as renderAttaformState, h as hydrateAttaformState, D as DEVTOOLS_WINDOW_KEY } from '../../shared/attaform.0-00cYGw.mjs';
3
- import { g as createAttaform, l as kAttaformWizardActiveStepResolver, i as getRegistryFromApp } from '../../shared/attaform.C0au8oXd.mjs';
2
+ import { r as renderAttaformState, h as hydrateAttaformState, D as DEVTOOLS_WINDOW_KEY } from '../../shared/attaform.DgbHNLf_.mjs';
3
+ import { g as createAttaform, l as kAttaformWizardActiveStepResolver, i as getRegistryFromApp } from '../../shared/attaform.C5qkSN6t.mjs';
4
4
 
5
5
  var attaform_default = defineNuxtPlugin({
6
6
  // `enforce: 'pre'` makes the "we run before any component's setup" claim
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
- const injectWizard = require('./attaform.Bos7wVZw.cjs');
4
- const paths = require('./attaform.B7UdTs_o.cjs');
3
+ const injectWizard = require('./attaform.CB1kYKUB.cjs');
4
+ const paths = require('./attaform.DQcdvxKZ.cjs');
5
5
 
6
6
  const PATH_SEPARATOR = ".";
7
7
  function createAbstractSchema(rootSchema, intro, services, formKey, options) {
@@ -916,4 +916,4 @@ exports.mergeDeep = mergeDeep;
916
916
  exports.peelEmbeddedDefault = peelEmbeddedDefault;
917
917
  exports.slimPrimitivesWalk = slimPrimitivesWalk;
918
918
  exports.walkPathSegments = walkPathSegments;
919
- //# sourceMappingURL=attaform.DdfLrIgM.cjs.map
919
+ //# sourceMappingURL=attaform.BVjueUPu.cjs.map