attaform 0.30.0 → 0.31.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/dist/abstract.d.mts +15 -21
  2. package/dist/abstract.mjs +3 -3
  3. package/dist/dev/abstract.mjs +4 -4
  4. package/dist/dev/chunks/devtools.mjs +4 -4
  5. package/dist/dev/directive.mjs +1 -1
  6. package/dist/dev/history.mjs +2 -2
  7. package/dist/dev/index.mjs +5 -5
  8. package/dist/dev/runtime/plugins/attaform.mjs +1 -1
  9. package/dist/dev/shared/{attaform-dev.DUZckyV8.mjs → attaform-dev.B1q-x9R9.mjs} +4 -4
  10. package/dist/dev/shared/{attaform-dev.BcYT6tFh.mjs → attaform-dev.CZz4GYZ6.mjs} +21 -9
  11. package/dist/dev/shared/{attaform-dev.BisFstxh.mjs → attaform-dev.Caibt7sb.mjs} +16 -15
  12. package/dist/dev/shared/{attaform-dev.DTrHonpc.mjs → attaform-dev.CtyqGia7.mjs} +176 -260
  13. package/dist/dev/shared/{attaform-dev.BK6Ge2qR.mjs → attaform-dev.DJ3nsWw6.mjs} +86 -122
  14. package/dist/dev/shared/{attaform-dev.BcNtyOmf.mjs → attaform-dev.DlaT4xGd.mjs} +4 -4
  15. package/dist/dev/shared/{attaform-dev.D6CLvaQT.mjs → attaform-dev.k5X47GMD.mjs} +520 -395
  16. package/dist/dev/shared/{attaform-dev.CQ2rJiAX.mjs → attaform-dev.y9x9Vp9F.mjs} +324 -234
  17. package/dist/dev/shared/{attaform-dev.BQc5QV3Y.mjs → attaform-dev.zlvDr0WB.mjs} +2 -2
  18. package/dist/dev/zod-v3.mjs +6 -6
  19. package/dist/dev/zod-v4.mjs +6 -6
  20. package/dist/dev/zod.mjs +5 -5
  21. package/dist/directive.d.mts +4 -4
  22. package/dist/directive.mjs +1 -1
  23. package/dist/esbuild.d.mts +11 -0
  24. package/dist/esbuild.mjs +1 -1
  25. package/dist/history.d.mts +3 -32
  26. package/dist/history.mjs +2 -2
  27. package/dist/index.d.mts +67 -70
  28. package/dist/index.mjs +4 -4
  29. package/dist/nuxt.d.mts +4 -3
  30. package/dist/nuxt.mjs +3 -6
  31. package/dist/rollup.d.mts +11 -0
  32. package/dist/rollup.mjs +1 -1
  33. package/dist/rspack.d.mts +19 -11
  34. package/dist/rspack.mjs +1 -1
  35. package/dist/runtime/components/AttaformDevtoolsPanel.vue +15 -15
  36. package/dist/runtime/components/DevtoolsValueTree.vue +2 -2
  37. package/dist/runtime/plugins/attaform.mjs +1 -1
  38. package/dist/shared/{attaform.BRDDQeHV.mjs → attaform.8mfKZCwH.mjs} +2 -2
  39. package/dist/shared/{attaform.DRUT703x.d.mts → attaform.B-Tb62qV.d.mts} +12 -12
  40. package/dist/shared/{attaform.v0fQtWyA.mjs → attaform.B1rH9ht6.mjs} +1 -1
  41. package/dist/shared/{attaform.DhnPb6nK.mjs → attaform.BAf3UNn4.mjs} +1 -1
  42. package/dist/shared/{attaform.BXneBxEj.mjs → attaform.BWUn8Kjy.mjs} +85 -121
  43. package/dist/shared/{attaform.WiGdIqgx.mjs → attaform.C4shmwyJ.mjs} +2 -2
  44. package/dist/shared/{attaform.Rc9u4Vfw.d.mts → attaform.CRVe8s82.d.mts} +83 -95
  45. package/dist/shared/attaform.D9RqPgGu.d.mts +42 -0
  46. package/dist/shared/attaform.DAo0QBmS.d.mts +2173 -0
  47. package/dist/shared/{attaform.BK0cgu2K.mjs → attaform.DBCbxlyk.mjs} +62 -4
  48. package/dist/shared/{attaform.CUfrMK7t.mjs → attaform.DL2nl_QR.mjs} +15 -14
  49. package/dist/shared/{attaform.DNJOWjQw.mjs → attaform.DNPJPOO7.mjs} +299 -218
  50. package/dist/shared/attaform.DZnrDrNb.d.mts +4657 -0
  51. package/dist/shared/{attaform.B3resMQ1.mjs → attaform.DxKQsqFZ.mjs} +496 -371
  52. package/dist/shared/{attaform.ByWP6kSx.mjs → attaform.L011tHgJ.mjs} +3 -3
  53. package/dist/shared/{attaform.CJ8DpRGj.mjs → attaform.LUPC-2Qg.mjs} +175 -257
  54. package/dist/transforms.d.mts +153 -26
  55. package/dist/transforms.mjs +1 -1
  56. package/dist/vite.d.mts +29 -52
  57. package/dist/vite.mjs +3 -3
  58. package/dist/webpack.d.mts +20 -12
  59. package/dist/webpack.mjs +1 -1
  60. package/dist/zod-v3.d.mts +90 -96
  61. package/dist/zod-v3.mjs +5 -5
  62. package/dist/zod-v4.d.mts +120 -118
  63. package/dist/zod-v4.mjs +5 -5
  64. package/dist/zod.d.mts +5 -5
  65. package/dist/zod.mjs +4 -4
  66. package/package.json +7 -6
  67. package/skills/attaform/SKILL.md +1 -1
  68. package/skills/attaform/references/custom-components.md +4 -0
  69. package/dist/shared/attaform.Clz0ArIi.d.mts +0 -44
  70. package/dist/shared/attaform.UGyOK83T.d.mts +0 -2366
  71. package/dist/shared/attaform.bKLzMU0o.d.mts +0 -4761
@@ -1,9 +1,347 @@
1
- import { reactive, markRaw, effectScope, watch, nextTick } from 'vue';
1
+ import { toRaw, reactive, markRaw, effectScope, watch, nextTick } from 'vue';
2
2
  import { i as isRegisterValue, a as isTransforming, S as SSR_COMPONENT_HOST_MODIFIER, R as REGISTER_OWNER_MARKER, b as V_REGISTER_MARKER } from './attaform.KvlEedlF.mjs';
3
3
  import { e as canonicalizePath } from './attaform.CdZIEf7_.mjs';
4
4
 
5
+ function activeElementOf(node) {
6
+ const rootNode = node.getRootNode();
7
+ return rootNode instanceof Document || rootNode instanceof ShadowRoot ? rootNode.activeElement : null;
8
+ }
9
+
5
10
  const INTERACTIVE_TAG_NAMES = /* @__PURE__ */ new Set(["INPUT", "SELECT", "TEXTAREA"]);
6
11
 
12
+ const isArray = Array.isArray;
13
+ function isFunction(value) {
14
+ return typeof value === "function";
15
+ }
16
+ function toTypeString(value) {
17
+ return Object.prototype.toString.call(value);
18
+ }
19
+ function isSet(value) {
20
+ return toTypeString(value) === "[object Set]";
21
+ }
22
+ function isDate(value) {
23
+ return toTypeString(value) === "[object Date]";
24
+ }
25
+ function isSymbol(value) {
26
+ return typeof value === "symbol";
27
+ }
28
+ function isObject(value) {
29
+ return value !== null && typeof value === "object";
30
+ }
31
+ function looseToNumber(val) {
32
+ const n = parseFloat(val);
33
+ return isNaN(n) ? val : n;
34
+ }
35
+ function looseCompareArrays(a, b) {
36
+ if (a.length !== b.length) return false;
37
+ let equal = true;
38
+ for (let i = 0; equal && i < a.length; i++) {
39
+ equal = looseEqual(a[i], b[i]);
40
+ }
41
+ return equal;
42
+ }
43
+ function looseEqual(a, b) {
44
+ if (a === b) return true;
45
+ const aValidType = isDate(a);
46
+ const bValidType = isDate(b);
47
+ if (aValidType || bValidType) {
48
+ return aValidType && bValidType ? a.getTime() === b.getTime() : false;
49
+ }
50
+ const aSymbol = isSymbol(a);
51
+ const bSymbol = isSymbol(b);
52
+ if (aSymbol || bSymbol) return a === b;
53
+ const aIsArray = isArray(a);
54
+ const bIsArray = isArray(b);
55
+ if (aIsArray || bIsArray) {
56
+ return aIsArray && bIsArray ? looseCompareArrays(a, b) : false;
57
+ }
58
+ const aIsObject = isObject(a);
59
+ const bIsObject = isObject(b);
60
+ if (aIsObject !== bIsObject) return false;
61
+ if (aIsObject && bIsObject) {
62
+ const keysA = Object.keys(a);
63
+ if (keysA.length !== Object.keys(b).length) return false;
64
+ for (const key of keysA) {
65
+ const hasA = Object.prototype.hasOwnProperty.call(a, key);
66
+ const hasB = Object.prototype.hasOwnProperty.call(b, key);
67
+ if (!hasA || !hasB || !looseEqual(a[key], b[key])) return false;
68
+ }
69
+ return true;
70
+ }
71
+ return String(a) === String(b);
72
+ }
73
+ function looseIndexOf(arr, val) {
74
+ return arr.findIndex((item) => looseEqual(item, val));
75
+ }
76
+ function invokeArrayFns(fns, ...args) {
77
+ for (let i = 0; i < fns.length; i++) {
78
+ const fn = fns[i];
79
+ if (fn) fn(...args);
80
+ }
81
+ }
82
+
83
+ const assignKey = /* @__PURE__ */ Symbol.for("attaform:assign-key");
84
+ const DEFAULT_ASSIGNER_TAG = /* @__PURE__ */ Symbol.for("attaform:default-assigner-tag");
85
+ function isDefaultAssigner(fn) {
86
+ return typeof fn === "function" && fn[DEFAULT_ASSIGNER_TAG] === true;
87
+ }
88
+ const CONSUMER_WRAPPED_TAG = /* @__PURE__ */ Symbol.for("attaform:consumer-wrapped-assigner");
89
+ function isConsumerWrapped(fn) {
90
+ return typeof fn === "function" && fn[CONSUMER_WRAPPED_TAG] === true;
91
+ }
92
+ function fireAssigner(el, registerValue, value) {
93
+ const fn = el[assignKey];
94
+ if (fn === void 0) return void 0;
95
+ if (isDefaultAssigner(fn) || isConsumerWrapped(fn)) {
96
+ return fn(value);
97
+ }
98
+ if (!isRegisterValue(registerValue)) {
99
+ return fn(value, void 0);
100
+ }
101
+ return wrapWithTransforms(
102
+ value,
103
+ registerValue,
104
+ (coerced) => fn(coerced, registerValue),
105
+ void 0
106
+ );
107
+ }
108
+ function isThenable(x) {
109
+ return x !== null && (typeof x === "object" || typeof x === "function") && typeof x.then === "function";
110
+ }
111
+ function makeTransformContext() {
112
+ const holder = { controller: null, aborted: false };
113
+ const ctx = {
114
+ get signal() {
115
+ if (holder.controller === null) {
116
+ holder.controller = new AbortController();
117
+ if (holder.aborted) holder.controller.abort();
118
+ }
119
+ return holder.controller.signal;
120
+ }
121
+ };
122
+ return { ctx, holder };
123
+ }
124
+ function runTransforms(initial, registerValue) {
125
+ const transforms = registerValue.transforms;
126
+ if (transforms === void 0 || transforms.length === 0) {
127
+ return { kind: "sync", ok: true, value: initial };
128
+ }
129
+ const { ctx, holder } = makeTransformContext();
130
+ let v = initial;
131
+ for (let i = 0; i < transforms.length; i++) {
132
+ const fn = transforms[i];
133
+ let out;
134
+ try {
135
+ out = fn(v, ctx);
136
+ } catch (err) {
137
+ logTransformFailure(registerValue.path);
138
+ return { kind: "sync", ok: false };
139
+ }
140
+ if (isThenable(out)) {
141
+ const rest = transforms.slice(i + 1);
142
+ const seed = out;
143
+ const run = () => rest.reduce(
144
+ (acc, next) => acc.then((value) => next(value, ctx)),
145
+ Promise.resolve(seed)
146
+ );
147
+ return { kind: "async", run, holder };
148
+ }
149
+ v = out;
150
+ }
151
+ return { kind: "sync", ok: true, value: v };
152
+ }
153
+ function kickoffAsyncTransform(rv, holder, run, commit, syncDom) {
154
+ const token = rv.beginTransform(holder);
155
+ void run().then(
156
+ (value) => {
157
+ const live = rv.isCurrentTransform(token);
158
+ rv.endTransform(token);
159
+ if (!live) return;
160
+ const coerced = applyCoerce(value, rv);
161
+ const wrote = commit(coerced);
162
+ if (wrote === false) rv.setTransformError(transformGateRejectedError(rv.path));
163
+ else syncDom?.();
164
+ },
165
+ (err) => {
166
+ if (rv.isCurrentTransform(token)) rv.setTransformError(toTransformError(err));
167
+ rv.endTransform(token);
168
+ }
169
+ );
170
+ }
171
+ function logTransformFailure(path, index, fn, err) {
172
+ {
173
+ console.error("[attaform] AF14 attaform.dev/e/af14");
174
+ }
175
+ }
176
+ function applyCoerce(value, registerValue) {
177
+ return registerValue.coerce !== void 0 ? registerValue.coerce(value) : value;
178
+ }
179
+ function wrapWithTransforms(value, registerValue, commit, syncDom) {
180
+ const r = runTransforms(value, registerValue);
181
+ if (r.kind === "async") {
182
+ kickoffAsyncTransform(registerValue, r.holder, r.run, commit, syncDom);
183
+ return true;
184
+ }
185
+ if (!r.ok) return false;
186
+ const coerced = applyCoerce(r.value, registerValue);
187
+ return commit(coerced);
188
+ }
189
+ function toTransformError(value) {
190
+ if (value instanceof Error) return value;
191
+ const message = typeof value === "string" && value.length > 0 ? value : `Transform rejected with a non-Error value (${typeof value})`;
192
+ return new Error(message, { cause: value });
193
+ }
194
+ function transformGateRejectedError(path) {
195
+ return new Error(
196
+ `[attaform] transform result for path '${path}' was rejected by the field's type gate (the resolved value did not fit the schema slot).`
197
+ );
198
+ }
199
+ const getModelAssigner = (el, vnode, registerValue) => {
200
+ const fn = vnode.props?.["onUpdate:registerValue"] ?? vnode.props?.["on:update:registerValue"];
201
+ if (isArray(fn)) {
202
+ const fnArr = fn.filter((x) => isFunction(x));
203
+ const wrapped = (value) => {
204
+ return wrapWithTransforms(
205
+ value,
206
+ registerValue,
207
+ (coerced) => {
208
+ invokeArrayFns(fnArr, coerced, registerValue);
209
+ return void 0;
210
+ },
211
+ void 0
212
+ );
213
+ };
214
+ wrapped[CONSUMER_WRAPPED_TAG] = true;
215
+ return wrapped;
216
+ }
217
+ if (isFunction(fn)) {
218
+ const handler = fn;
219
+ const wrapped = (value) => {
220
+ return wrapWithTransforms(
221
+ value,
222
+ registerValue,
223
+ (coerced) => handler(coerced, registerValue),
224
+ void 0
225
+ );
226
+ };
227
+ wrapped[CONSUMER_WRAPPED_TAG] = true;
228
+ return wrapped;
229
+ }
230
+ const defaultAssigner = (value) => {
231
+ if (value === void 0 && registerValue.acceptsUndefined) {
232
+ return registerValue.setValueWithInternalPath(void 0);
233
+ }
234
+ return wrapWithTransforms(
235
+ value,
236
+ registerValue,
237
+ (coerced) => registerValue.setValueWithInternalPath(coerced),
238
+ el._syncFromStorage
239
+ );
240
+ };
241
+ defaultAssigner[DEFAULT_ASSIGNER_TAG] = true;
242
+ return defaultAssigner;
243
+ };
244
+ function makeNoopAssigner() {
245
+ const noop = (_) => void 0;
246
+ noop[DEFAULT_ASSIGNER_TAG] = true;
247
+ return noop;
248
+ }
249
+ function setAssignFunction(el, vnode, value) {
250
+ const current = el[assignKey];
251
+ if (current !== void 0 && !isDefaultAssigner(current) && !isConsumerWrapped(current)) {
252
+ return;
253
+ }
254
+ if (value === void 0) {
255
+ el[assignKey] = makeNoopAssigner();
256
+ return;
257
+ }
258
+ if (!isRegisterValue(value)) {
259
+ el[assignKey] = makeNoopAssigner();
260
+ return;
261
+ }
262
+ el[assignKey] = getModelAssigner(el, vnode, value);
263
+ }
264
+
265
+ const inherited = /* @__PURE__ */ new WeakMap();
266
+ function recordInheritedModifiers(rv, modifiers) {
267
+ const lazy = modifiers.lazy === true;
268
+ const trim = modifiers.trim === true;
269
+ const number = modifiers.number === true;
270
+ if ((lazy || trim || number) && typeof rv === "object" && rv !== null) {
271
+ inherited.set(toRaw(rv), { lazy, trim, number });
272
+ }
273
+ }
274
+ function withInheritedModifiers(rv, own) {
275
+ const found = typeof rv === "object" && rv !== null ? inherited.get(toRaw(rv)) : void 0;
276
+ return found === void 0 ? own : { ...found, ...own };
277
+ }
278
+
279
+ function commit(rv, raw, modifiers) {
280
+ let value = raw;
281
+ if (modifiers.number === true && typeof value === "string") {
282
+ const cast = looseToNumber(value);
283
+ if (typeof cast !== "number") return rv.markBlank();
284
+ if (!Number.isFinite(cast)) return false;
285
+ value = cast;
286
+ }
287
+ if (value == null || value === "" && !rv.acceptsString) return rv.commitFromHost(value);
288
+ return wrapWithTransforms(value, rv, (coerced) => rv.commitFromHost(coerced), void 0) !== false;
289
+ }
290
+ function createHostChannel() {
291
+ const editing = /* @__PURE__ */ new Set();
292
+ const pending = /* @__PURE__ */ new Map();
293
+ const heldDraft = (rv) => {
294
+ const draft = pending.get(rv.path);
295
+ return draft?.basis === rv.innerRef.value ? draft : void 0;
296
+ };
297
+ return {
298
+ write(rv, value, ownModifiers) {
299
+ const modifiers = withInheritedModifiers(rv, ownModifiers);
300
+ if (modifiers.lazy === true && editing.has(rv.path)) {
301
+ pending.set(rv.path, { value, basis: rv.innerRef.value });
302
+ return true;
303
+ }
304
+ return commit(rv, value, modifiers);
305
+ },
306
+ writeInput(rv, payload, modifiers) {
307
+ if (typeof payload !== "object" || payload === null || payload instanceof Event || !("value" in payload) || !Object.hasOwn(payload, "value")) {
308
+ return false;
309
+ }
310
+ return rv.setValueFromHost(payload.value, modifiers);
311
+ },
312
+ markEditing(rv, isEditing, modifiers) {
313
+ if (isEditing) {
314
+ editing.add(rv.path);
315
+ return;
316
+ }
317
+ editing.delete(rv.path);
318
+ const draft = heldDraft(rv);
319
+ if (pending.delete(rv.path)) {
320
+ if (draft !== void 0) {
321
+ const buffered = draft.value;
322
+ commit(
323
+ rv,
324
+ modifiers.trim === true && typeof buffered === "string" ? buffered.trim() : buffered,
325
+ modifiers
326
+ );
327
+ }
328
+ return;
329
+ }
330
+ if (modifiers.trim === true) {
331
+ const current = rv.innerRef.value;
332
+ if (typeof current === "string" && current !== current.trim()) {
333
+ commit(rv, current.trim(), modifiers);
334
+ }
335
+ }
336
+ },
337
+ holdsDraft: (rv) => heldDraft(rv) !== void 0,
338
+ release(rv) {
339
+ editing.delete(rv.path);
340
+ pending.delete(rv.path);
341
+ }
342
+ };
343
+ }
344
+
7
345
  const attaformListenersSymbol = /* @__PURE__ */ Symbol.for("attaform:focus-listeners");
8
346
  const HOST_FOCUS_TARGET_SELECTOR = [
9
347
  'input:not([type="hidden"])',
@@ -33,9 +371,7 @@ function attachFocusListeners(kernel, segments, element, instanceMeta) {
33
371
  element.addEventListener("focus", handleFocus);
34
372
  element.addEventListener("blur", handleBlur);
35
373
  target[attaformListenersSymbol] = { handleFocus, handleBlur };
36
- const rootNode = element.getRootNode();
37
- const activeElement = rootNode instanceof Document || rootNode instanceof ShadowRoot ? rootNode.activeElement : null;
38
- if (activeElement === element) {
374
+ if (activeElementOf(element) === element) {
39
375
  kernel.markFocused(segments, true, focusMeta);
40
376
  }
41
377
  }
@@ -101,124 +437,54 @@ function createDomBinding(kernel) {
101
437
  }
102
438
  }
103
439
  function rebuildSortedRegistrations() {
104
- const flat = [];
105
- for (const [, record] of elements) {
106
- for (const el of record.elements) {
107
- flat.push({
108
- path: record.path,
109
- element: el,
110
- host: false,
111
- formInstanceId: elementToFormInstance.get(el) ?? ""
112
- });
113
- }
114
- }
115
- for (const [key, target] of hostTargets) {
116
- if (elements.has(key)) continue;
117
- flat.push({
118
- path: target.path,
119
- element: target.hostRoot,
120
- host: true,
121
- formInstanceId: target.formInstanceId
122
- });
123
- }
124
- flat.sort(
125
- (a, b) => a.element.compareDocumentPosition(b.element) & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1
126
- );
127
- return flat;
128
- }
129
- function getFirstErrorElement(formInstanceId) {
130
- sortedRegistrationsCache ?? (sortedRegistrationsCache = rebuildSortedRegistrations());
131
- for (const entry of sortedRegistrationsCache) {
132
- if (entry.formInstanceId !== formInstanceId) continue;
133
- if (!entry.element.isConnected) continue;
134
- if (entry.element.offsetParent === null) continue;
135
- if (kernel.getErrorsForPath(entry.path).length === 0) continue;
136
- const element = entry.host ? resolveHostFocusTarget(entry.element) : entry.element;
137
- return { path: entry.path, element };
138
- }
139
- return null;
140
- }
141
- return {
142
- elements,
143
- attach,
144
- detach,
145
- markHostConnected,
146
- getFirstErrorElement
147
- };
148
- }
149
- function armDomBinding(value) {
150
- value.ensureDomBinding?.(createDomBinding);
151
- }
152
-
153
- const isArray = Array.isArray;
154
- function isFunction(value) {
155
- return typeof value === "function";
156
- }
157
- function toTypeString(value) {
158
- return Object.prototype.toString.call(value);
159
- }
160
- function isSet(value) {
161
- return toTypeString(value) === "[object Set]";
162
- }
163
- function isDate(value) {
164
- return toTypeString(value) === "[object Date]";
165
- }
166
- function isSymbol(value) {
167
- return typeof value === "symbol";
168
- }
169
- function isObject(value) {
170
- return value !== null && typeof value === "object";
171
- }
172
- function looseToNumber(val) {
173
- const n = parseFloat(val);
174
- return isNaN(n) ? val : n;
175
- }
176
- function looseCompareArrays(a, b) {
177
- if (a.length !== b.length) return false;
178
- let equal = true;
179
- for (let i = 0; equal && i < a.length; i++) {
180
- equal = looseEqual(a[i], b[i]);
181
- }
182
- return equal;
183
- }
184
- function looseEqual(a, b) {
185
- if (a === b) return true;
186
- const aValidType = isDate(a);
187
- const bValidType = isDate(b);
188
- if (aValidType || bValidType) {
189
- return aValidType && bValidType ? a.getTime() === b.getTime() : false;
190
- }
191
- const aSymbol = isSymbol(a);
192
- const bSymbol = isSymbol(b);
193
- if (aSymbol || bSymbol) return a === b;
194
- const aIsArray = isArray(a);
195
- const bIsArray = isArray(b);
196
- if (aIsArray || bIsArray) {
197
- return aIsArray && bIsArray ? looseCompareArrays(a, b) : false;
440
+ const flat = [];
441
+ for (const [, record] of elements) {
442
+ for (const el of record.elements) {
443
+ flat.push({
444
+ path: record.path,
445
+ element: el,
446
+ host: false,
447
+ formInstanceId: elementToFormInstance.get(el) ?? ""
448
+ });
449
+ }
450
+ }
451
+ for (const [key, target] of hostTargets) {
452
+ if (elements.has(key)) continue;
453
+ flat.push({
454
+ path: target.path,
455
+ element: target.hostRoot,
456
+ host: true,
457
+ formInstanceId: target.formInstanceId
458
+ });
459
+ }
460
+ flat.sort(
461
+ (a, b) => a.element.compareDocumentPosition(b.element) & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1
462
+ );
463
+ return flat;
198
464
  }
199
- const aIsObject = isObject(a);
200
- const bIsObject = isObject(b);
201
- if (aIsObject !== bIsObject) return false;
202
- if (aIsObject && bIsObject) {
203
- const keysA = Object.keys(a);
204
- if (keysA.length !== Object.keys(b).length) return false;
205
- for (const key of keysA) {
206
- const hasA = Object.prototype.hasOwnProperty.call(a, key);
207
- const hasB = Object.prototype.hasOwnProperty.call(b, key);
208
- if (!hasA || !hasB || !looseEqual(a[key], b[key])) return false;
465
+ function getFirstErrorElement(formInstanceId) {
466
+ sortedRegistrationsCache ?? (sortedRegistrationsCache = rebuildSortedRegistrations());
467
+ for (const entry of sortedRegistrationsCache) {
468
+ if (entry.formInstanceId !== formInstanceId) continue;
469
+ if (!entry.element.isConnected) continue;
470
+ if (entry.element.offsetParent === null) continue;
471
+ if (kernel.getErrorsForPath(entry.path).length === 0) continue;
472
+ const element = entry.host ? resolveHostFocusTarget(entry.element) : entry.element;
473
+ return { path: entry.path, element };
209
474
  }
210
- return true;
475
+ return null;
211
476
  }
212
- return String(a) === String(b);
213
- }
214
- function looseIndexOf(arr, val) {
215
- return arr.findIndex((item) => looseEqual(item, val));
477
+ return {
478
+ elements,
479
+ attach,
480
+ detach,
481
+ markHostConnected,
482
+ getFirstErrorElement,
483
+ hostChannel: createHostChannel()
484
+ };
216
485
  }
217
- function invokeArrayFns(fns, ...args) {
218
- for (let i = 0; i < fns.length; i++) {
219
- const fn = fns[i];
220
- if (fn) fn(...args);
221
- }
486
+ function armDomBinding(value) {
487
+ return value.ensureDomBinding?.(createDomBinding);
222
488
  }
223
489
 
224
490
  const MANAGED_ARIA_ATTRS = [
@@ -369,188 +635,6 @@ function isRegisterValueLike(val) {
369
635
  return typeof val === "object" && val !== null && "markInteracted" in val && typeof val.markInteracted === "function";
370
636
  }
371
637
 
372
- const assignKey = /* @__PURE__ */ Symbol.for("attaform:assign-key");
373
- const DEFAULT_ASSIGNER_TAG = /* @__PURE__ */ Symbol.for("attaform:default-assigner-tag");
374
- function isDefaultAssigner(fn) {
375
- return typeof fn === "function" && fn[DEFAULT_ASSIGNER_TAG] === true;
376
- }
377
- const CONSUMER_WRAPPED_TAG = /* @__PURE__ */ Symbol.for("attaform:consumer-wrapped-assigner");
378
- function isConsumerWrapped(fn) {
379
- return typeof fn === "function" && fn[CONSUMER_WRAPPED_TAG] === true;
380
- }
381
- function fireAssigner(el, registerValue, value) {
382
- const fn = el[assignKey];
383
- if (fn === void 0) return void 0;
384
- if (isDefaultAssigner(fn) || isConsumerWrapped(fn)) {
385
- return fn(value);
386
- }
387
- if (!isRegisterValue(registerValue)) {
388
- return fn(value, void 0);
389
- }
390
- return wrapWithTransforms(
391
- value,
392
- registerValue,
393
- (coerced) => fn(coerced, registerValue),
394
- void 0
395
- );
396
- }
397
- function isThenable(x) {
398
- return x !== null && (typeof x === "object" || typeof x === "function") && typeof x.then === "function";
399
- }
400
- function makeTransformContext() {
401
- const holder = { controller: null, aborted: false };
402
- const ctx = {
403
- get signal() {
404
- if (holder.controller === null) {
405
- holder.controller = new AbortController();
406
- if (holder.aborted) holder.controller.abort();
407
- }
408
- return holder.controller.signal;
409
- }
410
- };
411
- return { ctx, holder };
412
- }
413
- function runTransforms(initial, registerValue) {
414
- const transforms = registerValue.transforms;
415
- if (transforms === void 0 || transforms.length === 0) {
416
- return { kind: "sync", ok: true, value: initial };
417
- }
418
- const { ctx, holder } = makeTransformContext();
419
- let v = initial;
420
- for (let i = 0; i < transforms.length; i++) {
421
- const fn = transforms[i];
422
- let out;
423
- try {
424
- out = fn(v, ctx);
425
- } catch (err) {
426
- logTransformFailure(registerValue.path);
427
- return { kind: "sync", ok: false };
428
- }
429
- if (isThenable(out)) {
430
- const rest = transforms.slice(i + 1);
431
- const seed = out;
432
- const run = () => rest.reduce(
433
- (acc, next) => acc.then((value) => next(value, ctx)),
434
- Promise.resolve(seed)
435
- );
436
- return { kind: "async", run, holder };
437
- }
438
- v = out;
439
- }
440
- return { kind: "sync", ok: true, value: v };
441
- }
442
- function kickoffAsyncTransform(rv, holder, run, commit, syncDom) {
443
- const token = rv.beginTransform(holder);
444
- void run().then(
445
- (value) => {
446
- const live = rv.isCurrentTransform(token);
447
- rv.endTransform(token);
448
- if (!live) return;
449
- const coerced = applyCoerce(value, rv);
450
- const wrote = commit(coerced);
451
- if (wrote === false) rv.setTransformError(transformGateRejectedError(rv.path));
452
- else syncDom?.();
453
- },
454
- (err) => {
455
- if (rv.isCurrentTransform(token)) rv.setTransformError(toTransformError(err));
456
- rv.endTransform(token);
457
- }
458
- );
459
- }
460
- function logTransformFailure(path, index, fn, err) {
461
- {
462
- console.error("[attaform] AF14 attaform.dev/e/af14");
463
- }
464
- }
465
- function applyCoerce(value, registerValue) {
466
- return registerValue.coerce !== void 0 ? registerValue.coerce(value) : value;
467
- }
468
- function wrapWithTransforms(value, registerValue, commit, syncDom) {
469
- const r = runTransforms(value, registerValue);
470
- if (r.kind === "async") {
471
- kickoffAsyncTransform(registerValue, r.holder, r.run, commit, syncDom);
472
- return true;
473
- }
474
- if (!r.ok) return false;
475
- const coerced = applyCoerce(r.value, registerValue);
476
- return commit(coerced);
477
- }
478
- function toTransformError(value) {
479
- if (value instanceof Error) return value;
480
- const message = typeof value === "string" && value.length > 0 ? value : `Transform rejected with a non-Error value (${typeof value})`;
481
- return new Error(message, { cause: value });
482
- }
483
- function transformGateRejectedError(path) {
484
- return new Error(
485
- `[attaform] transform result for path '${path}' was rejected by the field's type gate (the resolved value did not fit the schema slot).`
486
- );
487
- }
488
- const getModelAssigner = (el, vnode, registerValue) => {
489
- const fn = vnode.props?.["onUpdate:registerValue"] ?? vnode.props?.["on:update:registerValue"];
490
- if (isArray(fn)) {
491
- const fnArr = fn.filter((x) => isFunction(x));
492
- const wrapped = (value) => {
493
- return wrapWithTransforms(
494
- value,
495
- registerValue,
496
- (coerced) => {
497
- invokeArrayFns(fnArr, coerced, registerValue);
498
- return void 0;
499
- },
500
- void 0
501
- );
502
- };
503
- wrapped[CONSUMER_WRAPPED_TAG] = true;
504
- return wrapped;
505
- }
506
- if (isFunction(fn)) {
507
- const handler = fn;
508
- const wrapped = (value) => {
509
- return wrapWithTransforms(
510
- value,
511
- registerValue,
512
- (coerced) => handler(coerced, registerValue),
513
- void 0
514
- );
515
- };
516
- wrapped[CONSUMER_WRAPPED_TAG] = true;
517
- return wrapped;
518
- }
519
- const defaultAssigner = (value) => {
520
- if (value === void 0 && registerValue.acceptsUndefined) {
521
- return registerValue.setValueWithInternalPath(void 0);
522
- }
523
- return wrapWithTransforms(
524
- value,
525
- registerValue,
526
- (coerced) => registerValue.setValueWithInternalPath(coerced),
527
- el._syncFromStorage
528
- );
529
- };
530
- defaultAssigner[DEFAULT_ASSIGNER_TAG] = true;
531
- return defaultAssigner;
532
- };
533
- function makeNoopAssigner() {
534
- const noop = (_) => void 0;
535
- noop[DEFAULT_ASSIGNER_TAG] = true;
536
- return noop;
537
- }
538
- function setAssignFunction(el, vnode, value) {
539
- const current = el[assignKey];
540
- if (current !== void 0 && !isDefaultAssigner(current) && !isConsumerWrapped(current)) {
541
- return;
542
- }
543
- if (value === void 0) {
544
- el[assignKey] = makeNoopAssigner();
545
- return;
546
- }
547
- if (!isRegisterValue(value)) {
548
- el[assignKey] = makeNoopAssigner();
549
- return;
550
- }
551
- el[assignKey] = getModelAssigner(el, vnode, value);
552
- }
553
-
554
638
  function isBlankFileValue(value) {
555
639
  if (value === null || value === void 0) return true;
556
640
  if (Array.isArray(value) && value.length === 0) return true;
@@ -642,11 +726,6 @@ function syncElementRegistration(el, value, oldValue) {
642
726
  }
643
727
 
644
728
  const valueSyncScopeKey = /* @__PURE__ */ Symbol.for("attaform:value-sync-scope");
645
- function isElementFocused(el) {
646
- const rootNode = el.getRootNode();
647
- const activeElement = rootNode instanceof Document || rootNode instanceof ShadowRoot ? rootNode.activeElement : null;
648
- return activeElement === el;
649
- }
650
729
  function setupValueSync(el, source, apply, options = {}) {
651
730
  const skipWhileFocused = options.skipWhileFocused === true;
652
731
  const scope = effectScope(true);
@@ -655,7 +734,7 @@ function setupValueSync(el, source, apply, options = {}) {
655
734
  source,
656
735
  () => {
657
736
  if (el.composing === true) return;
658
- if (skipWhileFocused && isElementFocused(el)) return;
737
+ if (skipWhileFocused && activeElementOf(el) === el) return;
659
738
  apply();
660
739
  },
661
740
  { flush: "post" }
@@ -714,6 +793,28 @@ function onCompositionEnd(e) {
714
793
  target.dispatchEvent(new Event("input"));
715
794
  }
716
795
  }
796
+ const heldDrafts = /* @__PURE__ */ new WeakMap();
797
+ function holdDraft(owner, control, stale) {
798
+ heldDrafts.set(owner, {
799
+ control,
800
+ text: control.value,
801
+ stale,
802
+ start: control.selectionStart,
803
+ end: control.selectionEnd
804
+ });
805
+ }
806
+ function restoreDraft(owner) {
807
+ const held = heldDrafts.get(owner);
808
+ if (held === void 0) return;
809
+ heldDrafts.delete(owner);
810
+ const { control, text, stale, start, end } = held;
811
+ if (control.value !== stale || stale === text) return;
812
+ control.value = text;
813
+ if (start !== null && end !== null) control.setSelectionRange(start, end);
814
+ }
815
+ function textControl(node) {
816
+ return node instanceof HTMLInputElement || node instanceof HTMLTextAreaElement ? node : null;
817
+ }
717
818
  const vRegisterText = {
718
819
  created(el, { value, modifiers: { lazy, trim, number } }, vnode) {
719
820
  const castToNumberAtCreated = number === true || vnode.props?.["type"] === "number";
@@ -836,12 +937,14 @@ const vRegisterText = {
836
937
  mounted(el, { value }) {
837
938
  if (!isRegisterValue(value)) return;
838
939
  el.value = value.displayValue.value;
940
+ el._lastAppliedModel = value.innerRef.value;
839
941
  setupValueSync(
840
942
  el,
841
943
  value.displayValue,
842
944
  () => {
843
945
  const next = value.displayValue.value;
844
946
  if (el.value !== next) el.value = next;
947
+ el._lastAppliedModel = value.innerRef.value;
845
948
  },
846
949
  { skipWhileFocused: true }
847
950
  );
@@ -850,17 +953,16 @@ const vRegisterText = {
850
953
  setAssignFunction(el, vnode, value);
851
954
  if (el.composing === true) return;
852
955
  if (!isRegisterValue(value)) return;
956
+ const model = value.innerRef.value;
957
+ const modelMoved = model !== el._lastAppliedModel || !isRegisterValue(oldValue) || oldValue.path !== value.path;
958
+ el._lastAppliedModel = model;
853
959
  const target = value.displayValue.value;
854
960
  if (el.value === target) {
855
961
  return;
856
962
  }
857
- const rootNode = el.getRootNode();
858
- const activeElement = rootNode instanceof Document || rootNode instanceof ShadowRoot ? rootNode.activeElement : null;
859
- if (activeElement === el && el.type !== "range") {
860
- if (lazy === true && value.innerRef.value === oldValue) {
861
- return;
862
- }
863
- if (trim === true && el.value.trim() === target) {
963
+ if (activeElementOf(el) === el && el.type !== "range") {
964
+ if (lazy === true && !modelMoved || trim === true && el.value.trim() === target) {
965
+ holdDraft(el, el, target);
864
966
  return;
865
967
  }
866
968
  }
@@ -931,20 +1033,15 @@ const vRegisterCheckbox = {
931
1033
  el._lastAppliedModel = value.innerRef.value;
932
1034
  });
933
1035
  },
934
- // Skip the DOM sync when the model is identity-unchanged from the
935
- // last application. Pre-fix the scalar branch in `setChecked`
936
- // gated on `originalValue === oldValue`, comparing a primitive
937
- // scalar against the wrapper RegisterValue object — always !==,
938
- // so the guard was a silent no-op. Array / Set branches lacked
939
- // any guard. The per-render re-apply mirrors the just-fixed
940
- // `vRegisterSelect` shape: a sibling's reactive write triggers
941
- // `beforeUpdate` mid-click, `setChecked` re-applies the prior
942
- // model state, and the in-flight user toggle is clobbered before
943
- // the browser fires `change`. Identity comparison on
944
- // `innerRef.value` is sound for the same reason as multi-select —
945
- // every form write produces a fresh value at the path (new
946
- // primitives; new array/Set references along the spine), so
947
- // reference equality tracks "did the model move" exactly.
1036
+ // Skip the DOM sync when the model is identity-unchanged since the last
1037
+ // application. Without the guard, a sibling's reactive write triggers
1038
+ // `beforeUpdate` mid-click, `setChecked` re-applies the prior model state,
1039
+ // and the in-flight toggle is clobbered before the browser fires `change`.
1040
+ // Compare `innerRef.value` by identity: every form write produces a fresh
1041
+ // value at the path, new primitives and new array or Set references along
1042
+ // the spine, so reference equality tracks "did the model move" exactly. The
1043
+ // guard must NOT compare against `oldValue`, which is the previous binding's
1044
+ // wrapper RegisterValue and never equals a model scalar.
948
1045
  beforeUpdate(el, binding, vnode) {
949
1046
  setAssignFunction(el, vnode, binding.value);
950
1047
  if (!isRegisterValue(binding.value)) return;
@@ -994,13 +1091,11 @@ const vRegisterRadio = {
994
1091
  }
995
1092
  });
996
1093
  },
997
- // Initial checked-state sync runs in `mounted`, NOT `created` —
998
- // Vue's directive lifecycle fires `created` BEFORE the element's
999
- // attributes are patched (`type`, `value`, `_value` etc. aren't on
1000
- // the element yet), so `getValue(el)` would return `undefined` and
1001
- // every radio in a group would mount unchecked regardless of the
1002
- // model. Checkbox already uses `mounted: setChecked` for the same
1003
- // reason.
1094
+ // The initial checked sync runs in `mounted`, NOT `created`: Vue fires
1095
+ // `created` before the element's attributes are patched, so `type`, `value`
1096
+ // and `_value` are absent, `getValue(el)` returns undefined, and every radio
1097
+ // in a group mounts unchecked whatever the model says. Checkbox uses
1098
+ // `mounted` for the same reason.
1004
1099
  mounted(el, { value }) {
1005
1100
  if (!isRegisterValue(value)) return;
1006
1101
  el.checked = looseEqual(value.innerRef.value, applyCoerce(getValue(el), value));
@@ -1010,15 +1105,12 @@ const vRegisterRadio = {
1010
1105
  el._lastAppliedModel = value.innerRef.value;
1011
1106
  });
1012
1107
  },
1013
- // Skip the DOM sync when the model is identity-unchanged from the
1014
- // last application. Pre-fix the guard read `value.innerRef.value
1015
- // !== oldValue`, comparing a primitive scalar against the previous
1016
- // binding's wrapper RegisterValue object — always !==, so the
1017
- // guard was a silent no-op and `el.checked = …` re-applied on
1018
- // every parent re-render. Same shape as the just-fixed
1019
- // `vRegisterSelect` and `setChecked` bugs: a sibling's reactive
1020
- // write triggers `beforeUpdate` mid-click and writes back the
1021
- // prior model state, clobbering the in-flight selection.
1108
+ // Skip the DOM sync when the model is identity-unchanged since the last
1109
+ // application, tracked on `_lastAppliedModel`. Comparing against `oldValue`
1110
+ // instead compares a primitive scalar to the previous binding's wrapper
1111
+ // RegisterValue, never equal, so `el.checked` re-applies on every parent
1112
+ // re-render and a sibling's reactive write clobbers an in-flight selection
1113
+ // mid-click.
1022
1114
  beforeUpdate(el, { value }, vnode) {
1023
1115
  if (!isRegisterValue(value)) return;
1024
1116
  setAssignFunction(el, vnode, value);
@@ -1077,22 +1169,20 @@ const vRegisterSelect = {
1077
1169
  beforeUpdate(el, binding, vnode) {
1078
1170
  setAssignFunction(el, vnode, binding.value);
1079
1171
  },
1080
- // Skip the DOM sync when the model is identity-unchanged from the
1081
- // last application. Parent re-renders fire `updated` whether or not
1082
- // the bound model actually moved (a typed character in a sibling,
1083
- // an async-validation tick, any reactive read elsewhere on the
1084
- // page). Without this guard, every such render unconditionally re-
1085
- // applies `setSelected` against the prior model, which on a
1086
- // `<select multiple>` clobbers any in-progress user selection
1087
- // between mousedown and the browser's change-event decision — the
1088
- // browser then sees no net change, never fires `change`, and the
1089
- // model never updates. Identity comparison is sound: every form
1090
- // write produces a new array/Set reference at the path (the diff-
1091
- // apply replacement of `form.value` rolls forward fresh structures
1092
- // along the spine), so reference equality on `innerRef.value`
1093
- // tracks "did the model move" exactly. The `_assigning` gate stays
1094
- // — it short-circuits the immediate post-write render where the
1095
- // DOM is already in sync from the user's click.
1172
+ // Skip the DOM sync when the model is identity-unchanged since the last
1173
+ // application. A parent re-render fires `updated` whether or not the bound
1174
+ // model moved: a typed character in a sibling, an async-validation tick, any
1175
+ // reactive read elsewhere on the page. Re-applying `setSelected` against the
1176
+ // prior model on each of those clobbers an in-progress `<select multiple>`
1177
+ // selection between mousedown and the browser's change decision, after which
1178
+ // the browser sees no net change, never fires `change`, and the model never
1179
+ // updates.
1180
+ //
1181
+ // Identity comparison is sound: every form write produces a new array or Set
1182
+ // reference at the path, the replacement of `form.value` rolling fresh
1183
+ // structures forward along the spine. The `_assigning` gate stays as well,
1184
+ // short-circuiting the immediate post-write render where the DOM is already
1185
+ // in sync from the user's click.
1096
1186
  updated(el, { value }) {
1097
1187
  if (el._assigning === true) return;
1098
1188
  if (!isRegisterValue(value)) return;
@@ -1229,6 +1319,29 @@ function observeForLateHostControl(el, rv) {
1229
1319
  observer.observe(el, { childList: true, subtree: true });
1230
1320
  hostHealObservers.set(el, observer);
1231
1321
  }
1322
+ const hostEdits = /* @__PURE__ */ new WeakMap();
1323
+ function trackHostEdits(el, rv, modifiers) {
1324
+ if (modifiers.lazy !== true && modifiers.trim !== true) return;
1325
+ if (!INTERACTIVE_TAG_NAMES.has(el.tagName) && rv.hasRegisteredDescendant(el)) return;
1326
+ const channel = armDomBinding(rv)?.hostChannel;
1327
+ if (channel === void 0) return;
1328
+ const edits = { channel, rv, base: void 0 };
1329
+ const edge = (editing) => (event) => {
1330
+ if (editing && !channel.holdsDraft(rv)) edits.base = textControl(event.target)?.value;
1331
+ const other = event.relatedTarget;
1332
+ if (other instanceof Node && el.contains(other)) return;
1333
+ channel.markEditing(rv, editing, modifiers);
1334
+ };
1335
+ addTrackedListener(el, "focus", edge(true), { capture: true });
1336
+ addTrackedListener(el, "blur", edge(false), { capture: true });
1337
+ hostEdits.set(el, edits);
1338
+ }
1339
+ function holdHostDraft(el) {
1340
+ const edits = hostEdits.get(el);
1341
+ if (edits?.base === void 0 || !edits.channel.holdsDraft(edits.rv)) return;
1342
+ const control = textControl(activeElementOf(el));
1343
+ if (control !== null && el.contains(control)) holdDraft(el, control, edits.base);
1344
+ }
1232
1345
  function activateComponentHost(el, rv) {
1233
1346
  if (rv.hasRegisteredDescendant(el)) return;
1234
1347
  el[REGISTER_OWNER_MARKER] = true;
@@ -1243,25 +1356,34 @@ function activateComponentHost(el, rv) {
1243
1356
  setupNoLatchHost(el, rv);
1244
1357
  scheduleHostSelfHeal(el, rv);
1245
1358
  }
1359
+ function inheritModifiers(binding) {
1360
+ const modifiers = withInheritedModifiers(binding.value, binding.modifiers);
1361
+ return modifiers === binding.modifiers ? binding : { ...binding, modifiers };
1362
+ }
1246
1363
  const vRegisterDynamic = {
1247
- created(el, binding, vnode) {
1364
+ created(el, ownBinding, vnode) {
1365
+ const binding = inheritModifiers(ownBinding);
1248
1366
  if (isRegisterValue(binding.value)) armDomBinding(binding.value);
1249
1367
  callModelHook(el, binding, vnode, null, "created");
1250
1368
  if (isRegisterValue(binding.value)) setupAria(el, binding.value, vnode);
1251
1369
  },
1252
- mounted(el, binding, vnode) {
1370
+ mounted(el, ownBinding, vnode) {
1371
+ const binding = inheritModifiers(ownBinding);
1253
1372
  callModelHook(el, binding, vnode, null, "mounted");
1254
1373
  if (isRegisterValue(binding.value) && INTERACTIVE_TAG_NAMES.has(el.tagName) && binding.modifiers[SSR_COMPONENT_HOST_MODIFIER] !== true && vnode.props?.["disabled"] === void 0) {
1255
1374
  setupDisabledSync(el, binding.value.disabled);
1256
1375
  }
1257
1376
  if (binding.modifiers[SSR_COMPONENT_HOST_MODIFIER] === true && isRegisterValue(binding.value)) {
1377
+ trackHostEdits(el, binding.value, binding.modifiers);
1258
1378
  activateComponentHost(el, binding.value);
1259
1379
  }
1260
1380
  },
1261
- beforeUpdate(el, binding, vnode, prevVNode) {
1381
+ beforeUpdate(el, ownBinding, vnode, prevVNode) {
1382
+ const binding = inheritModifiers(ownBinding);
1262
1383
  if (isRegisterValue(binding.value)) armDomBinding(binding.value);
1263
1384
  syncElementRegistration(el, binding.value, binding.oldValue);
1264
1385
  callModelHook(el, binding, vnode, prevVNode, "beforeUpdate");
1386
+ holdHostDraft(el);
1265
1387
  const ariaEl = el;
1266
1388
  const value = binding.value;
1267
1389
  if (!isRegisterValue(value) || value.ariaDisplayState === void 0) {
@@ -1279,13 +1401,17 @@ const vRegisterDynamic = {
1279
1401
  }
1280
1402
  },
1281
1403
  updated(el, binding, vnode, prevVNode) {
1282
- callModelHook(el, binding, vnode, prevVNode, "updated");
1404
+ callModelHook(el, inheritModifiers(binding), vnode, prevVNode, "updated");
1405
+ restoreDraft(el);
1283
1406
  },
1284
1407
  beforeUnmount(el, { value }) {
1285
1408
  removeTrackedListeners(el);
1286
1409
  teardownAria(el);
1287
1410
  teardownValueSync(el);
1288
1411
  teardownDisabledSync(el);
1412
+ const edits = hostEdits.get(el);
1413
+ edits?.channel.release(edits.rv);
1414
+ hostEdits.delete(el);
1289
1415
  if (!isRegisterValue(value)) return;
1290
1416
  value.deregisterElement(el);
1291
1417
  if (componentHostLatch.has(el)) {
@@ -1306,13 +1432,12 @@ const vRegisterDynamic = {
1306
1432
  delete el._syncFromStorage;
1307
1433
  delete el[assignKey];
1308
1434
  },
1309
- // The lifecycle hooks above don't run on the server (Vue skips
1310
- // directive lifecycle during SSR), so emit the same aria attributes
1311
- // here from the SSR-time gated display state. Honors authored attrs
1312
- // (vnode-level lockout), touches no DOM, and shares
1313
- // `resolveAriaValue` with the client path. Ids are SSR-stable
1314
- // (formInstanceId derives from Vue's useId), so a server-rendered
1315
- // describedby matches the client after hydration.
1435
+ // Vue skips directive lifecycle during SSR, so the hooks above never run on
1436
+ // the server and the same aria attributes are emitted here from the SSR-time
1437
+ // gated display state. Honours authored attrs through the vnode-level
1438
+ // lockout, touches no DOM, and shares `resolveAriaValue` with the client
1439
+ // path. Ids are SSR-stable, `formInstanceId` deriving from Vue's `useId`, so
1440
+ // a server-rendered describedby matches the client after hydration.
1316
1441
  getSSRProps(binding, vnode) {
1317
1442
  const rv = binding.value;
1318
1443
  if (!isRegisterValue(rv)) return void 0;
@@ -1377,4 +1502,4 @@ function installVRegister(app) {
1377
1502
  }
1378
1503
  }
1379
1504
 
1380
- export { assignKey as a, vRegisterFile as b, armDomBinding as c, installVRegister as i, vRegister as v };
1505
+ export { assignKey as a, vRegisterFile as b, armDomBinding as c, installVRegister as i, recordInheritedModifiers as r, vRegister as v, withInheritedModifiers as w };