attaform 0.17.2 → 0.18.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 (115) hide show
  1. package/README.md +77 -36
  2. package/dist/chunks/devtools.cjs +10 -37
  3. package/dist/chunks/devtools.cjs.map +1 -1
  4. package/dist/chunks/devtools.mjs +10 -37
  5. package/dist/chunks/devtools.mjs.map +1 -1
  6. package/dist/chunks/indexeddb.cjs +4 -4
  7. package/dist/chunks/indexeddb.cjs.map +1 -1
  8. package/dist/chunks/indexeddb.mjs +1 -1
  9. package/dist/chunks/local-storage.cjs +2 -2
  10. package/dist/chunks/local-storage.cjs.map +1 -1
  11. package/dist/chunks/local-storage.mjs +1 -1
  12. package/dist/chunks/session-storage.cjs +2 -2
  13. package/dist/chunks/session-storage.cjs.map +1 -1
  14. package/dist/chunks/session-storage.mjs +1 -1
  15. package/dist/index.cjs +42 -37
  16. package/dist/index.cjs.map +1 -1
  17. package/dist/index.d.cts +159 -196
  18. package/dist/index.d.mts +159 -196
  19. package/dist/index.d.ts +159 -196
  20. package/dist/index.mjs +5 -7
  21. package/dist/index.mjs.map +1 -1
  22. package/dist/nuxt.cjs +31 -40
  23. package/dist/nuxt.cjs.map +1 -1
  24. package/dist/nuxt.d.cts +8 -1
  25. package/dist/nuxt.d.mts +8 -1
  26. package/dist/nuxt.d.ts +8 -1
  27. package/dist/nuxt.mjs +32 -41
  28. package/dist/nuxt.mjs.map +1 -1
  29. package/dist/runtime/components/AttaformDevtoolsPanel.d.vue.ts +7 -0
  30. package/dist/runtime/components/AttaformDevtoolsPanel.vue +453 -0
  31. package/dist/runtime/components/AttaformDevtoolsPanel.vue.d.ts +7 -0
  32. package/dist/runtime/components/DevtoolsValueTree.d.vue.ts +37 -0
  33. package/dist/runtime/components/DevtoolsValueTree.vue +192 -0
  34. package/dist/runtime/components/DevtoolsValueTree.vue.d.ts +37 -0
  35. package/dist/runtime/plugins/attaform.cjs +17 -6
  36. package/dist/runtime/plugins/attaform.cjs.map +1 -1
  37. package/dist/runtime/plugins/attaform.mjs +15 -4
  38. package/dist/runtime/plugins/attaform.mjs.map +1 -1
  39. package/dist/shared/attaform.5UhpSVFI.cjs +63 -0
  40. package/dist/shared/attaform.5UhpSVFI.cjs.map +1 -0
  41. package/dist/shared/attaform.BDdFdjeX.mjs +57 -0
  42. package/dist/shared/attaform.BDdFdjeX.mjs.map +1 -0
  43. package/dist/shared/attaform.Bgu9l6OG.d.cts +1651 -0
  44. package/dist/shared/attaform.BmDBu4ql.d.ts +1651 -0
  45. package/dist/shared/{attaform.Dee2rU1P.cjs → attaform.BqK_L4gK.cjs} +310 -24
  46. package/dist/shared/attaform.BqK_L4gK.cjs.map +1 -0
  47. package/dist/shared/{attaform.C_5aB6EQ.d.ts → attaform.BsMdl-35.d.cts} +754 -146
  48. package/dist/shared/{attaform.C_5aB6EQ.d.mts → attaform.BsMdl-35.d.mts} +754 -146
  49. package/dist/shared/{attaform.C_5aB6EQ.d.cts → attaform.BsMdl-35.d.ts} +754 -146
  50. package/dist/shared/attaform.Bubm_slq.cjs.map +1 -1
  51. package/dist/shared/{attaform.C6lbmMUe.d.ts → attaform.C3x1hKJC.d.mts} +4 -4
  52. package/dist/shared/{attaform.CuE-bS1C.d.mts → attaform.CWs1Z3p7.d.ts} +57 -23
  53. package/dist/shared/attaform.CXpzmj38.mjs.map +1 -1
  54. package/dist/shared/{attaform.C5MH4lNh.d.mts → attaform.CjmJpfLH.d.ts} +4 -4
  55. package/dist/shared/{attaform.Drt6fivF.mjs → attaform.CtNUB9nf.mjs} +74 -76
  56. package/dist/shared/attaform.CtNUB9nf.mjs.map +1 -0
  57. package/dist/shared/{attaform.C0iFnTN0.d.ts → attaform.D-hDvb98.d.cts} +57 -23
  58. package/dist/shared/attaform.DAH3kvav.d.mts +1651 -0
  59. package/dist/shared/{attaform.BPRHR3Zs.cjs → attaform.DUHru0OF.cjs} +83 -85
  60. package/dist/shared/attaform.DUHru0OF.cjs.map +1 -0
  61. package/dist/shared/{attaform.BV40t5y2.cjs → attaform.Dlk1jMuv.cjs} +245 -108
  62. package/dist/shared/attaform.Dlk1jMuv.cjs.map +1 -0
  63. package/dist/shared/{attaform.B3ZaPIzS.mjs → attaform.DsC3rZHG.mjs} +1804 -219
  64. package/dist/shared/attaform.DsC3rZHG.mjs.map +1 -0
  65. package/dist/shared/{attaform.DtMN-MAm.d.cts → attaform.Dzi89x8N.d.cts} +4 -4
  66. package/dist/shared/{attaform.Cer8JO_P.cjs → attaform.II89Pcf4.cjs} +1860 -272
  67. package/dist/shared/attaform.II89Pcf4.cjs.map +1 -0
  68. package/dist/shared/{attaform.CIEQgJnM.mjs → attaform.Xhg0AYNa.mjs} +300 -26
  69. package/dist/shared/attaform.Xhg0AYNa.mjs.map +1 -0
  70. package/dist/shared/{attaform.CpERWz3u.mjs → attaform.Xt0A3QUd.mjs} +232 -95
  71. package/dist/shared/attaform.Xt0A3QUd.mjs.map +1 -0
  72. package/dist/shared/{attaform.CHorcsIU.d.cts → attaform.bH7WvNad.d.mts} +57 -23
  73. package/dist/vite.cjs +270 -2
  74. package/dist/vite.cjs.map +1 -1
  75. package/dist/vite.mjs +266 -2
  76. package/dist/vite.mjs.map +1 -1
  77. package/dist/zod-v3.cjs +11 -8
  78. package/dist/zod-v3.cjs.map +1 -1
  79. package/dist/zod-v3.d.cts +6 -6
  80. package/dist/zod-v3.d.mts +6 -6
  81. package/dist/zod-v3.d.ts +6 -6
  82. package/dist/zod-v3.mjs +3 -3
  83. package/dist/zod-v4.cjs +11 -8
  84. package/dist/zod-v4.cjs.map +1 -1
  85. package/dist/zod-v4.d.cts +5 -5
  86. package/dist/zod-v4.d.mts +5 -5
  87. package/dist/zod-v4.d.ts +5 -5
  88. package/dist/zod-v4.mjs +3 -3
  89. package/dist/zod.cjs +15 -16
  90. package/dist/zod.cjs.map +1 -1
  91. package/dist/zod.d.cts +127 -40
  92. package/dist/zod.d.mts +127 -40
  93. package/dist/zod.d.ts +127 -40
  94. package/dist/zod.mjs +7 -11
  95. package/dist/zod.mjs.map +1 -1
  96. package/package.json +19 -5
  97. package/dist/shared/attaform.B1jvxsOF.d.mts +0 -156
  98. package/dist/shared/attaform.B3ZaPIzS.mjs.map +0 -1
  99. package/dist/shared/attaform.BBM2muQ9.cjs +0 -101
  100. package/dist/shared/attaform.BBM2muQ9.cjs.map +0 -1
  101. package/dist/shared/attaform.BPRHR3Zs.cjs.map +0 -1
  102. package/dist/shared/attaform.BV40t5y2.cjs.map +0 -1
  103. package/dist/shared/attaform.C6qzEdIM.d.cts +0 -156
  104. package/dist/shared/attaform.C8LVFVVe.cjs +0 -32
  105. package/dist/shared/attaform.C8LVFVVe.cjs.map +0 -1
  106. package/dist/shared/attaform.CIEQgJnM.mjs.map +0 -1
  107. package/dist/shared/attaform.CTwNcpLE.d.ts +0 -156
  108. package/dist/shared/attaform.Cer8JO_P.cjs.map +0 -1
  109. package/dist/shared/attaform.CpERWz3u.mjs.map +0 -1
  110. package/dist/shared/attaform.Dee2rU1P.cjs.map +0 -1
  111. package/dist/shared/attaform.Drt6fivF.mjs.map +0 -1
  112. package/dist/shared/attaform.Vo-Kft0t.mjs +0 -29
  113. package/dist/shared/attaform.Vo-Kft0t.mjs.map +0 -1
  114. package/dist/shared/attaform.h1sq3BFu.mjs +0 -92
  115. package/dist/shared/attaform.h1sq3BFu.mjs.map +0 -1
@@ -0,0 +1,192 @@
1
+ <script setup>
2
+ import { computed, nextTick, ref } from "vue";
3
+ const props = defineProps({
4
+ value: { type: null, required: true },
5
+ label: { type: String, required: false },
6
+ depth: { type: Number, required: false },
7
+ path: { type: Array, required: false },
8
+ editable: { type: Boolean, required: false, skipCheck: true },
9
+ onEdit: { type: Function, required: false, skipCheck: true },
10
+ selectedKey: { type: null, required: false },
11
+ onSelectPath: { type: Function, required: false, skipCheck: true }
12
+ });
13
+ const depth = computed(() => props.depth ?? 0);
14
+ const expanded = ref(depth.value < 2);
15
+ const path = computed(() => props.path ?? []);
16
+ const type = computed(() => {
17
+ const v = props.value;
18
+ if (v === null) return "null";
19
+ if (v === void 0) return "undefined";
20
+ if (typeof v === "string") return "string";
21
+ if (typeof v === "number") return "number";
22
+ if (typeof v === "boolean") return "boolean";
23
+ if (Array.isArray(v)) return "array";
24
+ if (typeof v === "object") return "object";
25
+ return "other";
26
+ });
27
+ const isLeaf = computed(() => type.value !== "array" && type.value !== "object");
28
+ const entries = computed(() => {
29
+ const v = props.value;
30
+ if (Array.isArray(v)) return v.map((item, i) => [String(i), item]);
31
+ if (v !== null && typeof v === "object") {
32
+ return Object.entries(v);
33
+ }
34
+ return [];
35
+ });
36
+ const formatted = computed(() => {
37
+ const v = props.value;
38
+ if (v === null) return "null";
39
+ if (v === void 0) return "undefined";
40
+ if (typeof v === "string") return `"${v}"`;
41
+ if (typeof v === "boolean" || typeof v === "number") return String(v);
42
+ return String(v);
43
+ });
44
+ const summary = computed(() => {
45
+ const v = props.value;
46
+ if (Array.isArray(v)) return `Array(${v.length})`;
47
+ if (v !== null && typeof v === "object") {
48
+ const keys = Object.keys(v);
49
+ return `{${keys.length}}`;
50
+ }
51
+ return "";
52
+ });
53
+ const isEditableLeaf = computed(() => {
54
+ if (!props.editable || props.onEdit === void 0) return false;
55
+ return type.value === "string" || type.value === "number" || type.value === "boolean";
56
+ });
57
+ const editing = ref(false);
58
+ const editValue = ref("");
59
+ const editInput = ref(null);
60
+ const editRejected = ref(false);
61
+ function childPath(rawKey) {
62
+ if (type.value === "array") return [...path.value, Number(rawKey)];
63
+ return [...path.value, rawKey];
64
+ }
65
+ async function startEdit() {
66
+ if (!isEditableLeaf.value) return;
67
+ editValue.value = type.value === "boolean" || type.value === "number" ? String(props.value ?? "") : props.value;
68
+ editing.value = true;
69
+ editRejected.value = false;
70
+ await nextTick();
71
+ editInput.value?.focus();
72
+ editInput.value?.select?.();
73
+ }
74
+ function flashRejected() {
75
+ editRejected.value = true;
76
+ setTimeout(() => {
77
+ editRejected.value = false;
78
+ }, 600);
79
+ }
80
+ function commitEdit() {
81
+ if (!editing.value || props.onEdit === void 0) return;
82
+ let next = editValue.value;
83
+ if (type.value === "number") {
84
+ const parsed = Number(editValue.value);
85
+ if (!Number.isFinite(parsed)) {
86
+ flashRejected();
87
+ editing.value = false;
88
+ return;
89
+ }
90
+ next = parsed;
91
+ } else if (type.value === "boolean") {
92
+ next = editValue.value === "true";
93
+ }
94
+ if (next !== props.value) props.onEdit(path.value, next);
95
+ editing.value = false;
96
+ }
97
+ function cancelEdit() {
98
+ editing.value = false;
99
+ editRejected.value = false;
100
+ }
101
+ function toggleBoolean(checked) {
102
+ if (!isEditableLeaf.value || props.onEdit === void 0) return;
103
+ props.onEdit(path.value, checked);
104
+ }
105
+ const ownKey = computed(() => JSON.stringify(path.value));
106
+ const isSelected = computed(
107
+ () => props.selectedKey !== null && props.selectedKey !== void 0 && props.selectedKey === ownKey.value
108
+ );
109
+ function selectThisPath() {
110
+ if (props.onSelectPath === void 0) return;
111
+ props.onSelectPath(path.value);
112
+ }
113
+ </script>
114
+
115
+ <template>
116
+ <div class="tree-node">
117
+ <template v-if="isLeaf">
118
+ <div class="row" :class="{ 'edit-rejected': editRejected, 'row-selected': isSelected }">
119
+ <span
120
+ v-if="label"
121
+ class="key"
122
+ :class="{ 'key-selectable': onSelectPath !== void 0, 'key-selected': isSelected }"
123
+ @click.stop="selectThisPath"
124
+ >{{ label }}:</span
125
+ >
126
+ <template v-if="editing && (type === 'string' || type === 'number')">
127
+ <input
128
+ ref="editInput"
129
+ v-model="editValue"
130
+ class="leaf-input"
131
+ :type="type === 'number' ? 'text' : 'text'"
132
+ :inputmode="type === 'number' ? 'decimal' : 'text'"
133
+ @keydown.enter.prevent="commitEdit"
134
+ @keydown.escape.prevent="cancelEdit"
135
+ @blur="commitEdit"
136
+ />
137
+ </template>
138
+ <template v-else-if="isEditableLeaf && type === 'boolean'">
139
+ <label class="leaf-bool">
140
+ <input
141
+ type="checkbox"
142
+ :checked="value"
143
+ @change="toggleBoolean($event.target.checked)"
144
+ />
145
+ <span class="leaf leaf-boolean">{{ formatted }}</span>
146
+ </label>
147
+ </template>
148
+ <template v-else>
149
+ <span
150
+ class="leaf"
151
+ :class="[`leaf-${type}`, { 'leaf-editable': isEditableLeaf }]"
152
+ :title="isEditableLeaf ? 'Click to edit' : void 0"
153
+ @click="isEditableLeaf ? startEdit() : null"
154
+ >
155
+ {{ formatted }}
156
+ </span>
157
+ </template>
158
+ </div>
159
+ </template>
160
+ <template v-else>
161
+ <div class="row branch" :class="{ 'row-selected': isSelected }" @click="expanded = !expanded">
162
+ <span class="caret" :class="{ open: expanded }">›</span>
163
+ <span
164
+ v-if="label"
165
+ class="key"
166
+ :class="{ 'key-selectable': onSelectPath !== void 0, 'key-selected': isSelected }"
167
+ @click.stop="selectThisPath"
168
+ >{{ label }}:</span
169
+ >
170
+ <span class="branch-summary">{{ summary }}</span>
171
+ </div>
172
+ <div v-if="expanded" class="children">
173
+ <DevtoolsValueTree
174
+ v-for="[k, v] in entries"
175
+ :key="k"
176
+ :value="v"
177
+ :label="k"
178
+ :depth="depth + 1"
179
+ :path="childPath(k)"
180
+ :editable="editable"
181
+ :on-edit="onEdit"
182
+ :selected-key="selectedKey"
183
+ :on-select-path="onSelectPath"
184
+ />
185
+ </div>
186
+ </template>
187
+ </div>
188
+ </template>
189
+
190
+ <style scoped>
191
+ .tree-node{font-family:ui-monospace,SF Mono,Menlo,Consolas,monospace;font-size:12px;line-height:1.6}.row{align-items:baseline;display:flex;gap:.4em;padding:1px 0}.branch{cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none}.branch:hover{background:var(--atf-row-hover,hsla(0,0%,100%,.04))}.caret{color:var(--atf-muted,#64748b);display:inline-block;transform:rotate(0deg);transition:transform .12s ease;width:.7em}.caret.open{transform:rotate(90deg)}.key{color:var(--atf-key,#93c5fd);flex-shrink:0}.key-selectable{border-bottom:1px dotted transparent;cursor:pointer}.key-selectable:hover{border-bottom-color:var(--atf-key,#93c5fd)}.key-selected{border-bottom-color:var(--atf-accent,#5b8def);color:var(--atf-accent,#5b8def);font-weight:600}.row-selected{background:rgba(91,141,239,.08);border-radius:3px;margin:0 -.4em;padding:1px .4em}.leaf-string{color:var(--atf-string,#86efac)}.leaf-number{color:var(--atf-number,#fbbf24)}.leaf-boolean{color:var(--atf-boolean,#f472b6)}.leaf-null,.leaf-undefined{font-style:italic}.branch-summary,.leaf-null,.leaf-undefined{color:var(--atf-muted,#64748b)}.children{border-left:1px dashed var(--atf-border,hsla(0,0%,100%,.08));margin-left:.3em;padding-left:1.4em}.leaf-editable{border-bottom:1px dotted transparent;cursor:text;transition:border-color .12s ease}.leaf-editable:hover{border-bottom-color:var(--atf-muted,#64748b)}.leaf-input{background:var(--atf-bg,#0f172a);border:1px solid var(--atf-accent,#5b8def);border-radius:3px;color:var(--atf-fg,#e2e8f0);flex:1;font:inherit;min-width:8em;outline:none;padding:.05em .4em}.leaf-bool{align-items:center;cursor:pointer;display:inline-flex;gap:.4em}.leaf-bool input{cursor:pointer;margin:0}.edit-rejected{animation:atf-shake .4s ease}@keyframes atf-shake{0%,to{transform:translateX(0)}20%,60%{transform:translateX(-3px)}40%,80%{transform:translateX(3px)}}
192
+ </style>
@@ -0,0 +1,37 @@
1
+ type __VLS_Props = {
2
+ value: unknown;
3
+ label?: string;
4
+ depth?: number;
5
+ /**
6
+ * Path-from-root for this node. Used by edit-aware mounts so the
7
+ * commit handler knows which leaf the user just touched. Default
8
+ * `[]` for non-editable mounts; the panel passes the explicit path
9
+ * when it wires up `onEdit`.
10
+ */
11
+ path?: ReadonlyArray<string | number>;
12
+ /**
13
+ * Edit-mode toggle. When `true` and `onEdit` is wired, leaf cells
14
+ * become click-to-edit. Sensitive (redacted) leaves stay read-only
15
+ * regardless — overwriting with the literal `[redacted]` string
16
+ * would destroy the real value.
17
+ */
18
+ editable?: boolean | undefined;
19
+ onEdit?: ((path: ReadonlyArray<string | number>, next: unknown) => void) | undefined;
20
+ /**
21
+ * Canonical JSON-array key of the currently-selected path (`null`
22
+ * for no selection). Compared against this node's own key so the
23
+ * key label can render in a highlighted state. Plumbed as a key
24
+ * rather than a path array because string equality is cheap and
25
+ * cross-renders consistently.
26
+ */
27
+ selectedKey?: string | null | undefined;
28
+ /**
29
+ * Click handler for the key label. Called with the node's current
30
+ * path-from-root; the panel toggles selection on identical paths.
31
+ * When omitted, the key label is plain text (no selection UX).
32
+ */
33
+ onSelectPath?: ((path: ReadonlyArray<string | number>) => void) | undefined;
34
+ };
35
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
36
+ declare const _default: typeof __VLS_export;
37
+ export default _default;
@@ -1,8 +1,8 @@
1
1
  'use strict';
2
2
 
3
3
  const app = require('nuxt/app');
4
- const plugin = require('../../shared/attaform.Dee2rU1P.cjs');
5
- const serialize = require('../../shared/attaform.C8LVFVVe.cjs');
4
+ const devtoolsShared = require('../../shared/attaform.5UhpSVFI.cjs');
5
+ const paths = require('../../shared/attaform.BqK_L4gK.cjs');
6
6
 
7
7
  var attaform_default = app.defineNuxtPlugin({
8
8
  // `enforce: 'pre'` makes the "we run before any component's setup" claim
@@ -13,17 +13,28 @@ var attaform_default = app.defineNuxtPlugin({
13
13
  enforce: "pre",
14
14
  setup(nuxtApp) {
15
15
  const isServer = undefined;
16
- const { defaults } = app.useRuntimeConfig().public.attaform;
17
- nuxtApp.vueApp.use(plugin.createAttaform({ ssr: isServer, defaults }));
16
+ const config = app.useRuntimeConfig().public;
17
+ const { defaults, version } = config.attaform;
18
+ nuxtApp.vueApp.use(paths.createAttaform({ ssr: isServer, defaults }));
19
+ nuxtApp.vueApp.provide(paths.kAttaformWizardActiveStepResolver, (param) => {
20
+ const value = app.useRoute().query[param];
21
+ if (typeof value === "string") return value;
22
+ if (Array.isArray(value) && typeof value[0] === "string") return value[0];
23
+ return void 0;
24
+ });
18
25
  if (isServer) {
19
26
  nuxtApp.hook("app:rendered", () => {
20
- const state = serialize.renderAttaformState(nuxtApp.vueApp);
27
+ const state = devtoolsShared.renderAttaformState(nuxtApp.vueApp);
21
28
  nuxtApp.payload.attaform = state;
22
29
  });
23
30
  } else {
24
31
  const serialized = nuxtApp.payload.attaform;
25
32
  if (serialized !== void 0) {
26
- serialize.hydrateAttaformState(nuxtApp.vueApp, serialized);
33
+ devtoolsShared.hydrateAttaformState(nuxtApp.vueApp, serialized);
34
+ }
35
+ if (undefined) {
36
+ const registry = paths.getRegistryFromApp(nuxtApp.vueApp);
37
+ window[devtoolsShared.DEVTOOLS_WINDOW_KEY] = { registry, version };
27
38
  }
28
39
  }
29
40
  }
@@ -1 +1 @@
1
- {"version":3,"file":"attaform.cjs","sources":["../../../src/runtime/plugins/attaform.ts"],"sourcesContent":["/**\n * Nuxt plugin: installs the framework-agnostic createAttaform Vue\n * plugin on nuxtApp.vueApp and wires the Nuxt payload mechanism to the\n * registry's SSR serialization helpers. Replaces the old split of\n * register.ts (client-only) + register-stub.ts (server-only).\n *\n * Runs on BOTH server and client — Vue's SSR renderer is a natural no-op\n * for directive lifecycle hooks, so the same plugin works on both sides\n * without a stub.\n */\nimport { defineNuxtPlugin, useRuntimeConfig } from 'nuxt/app'\nimport { createAttaform } from '../core/plugin'\nimport { hydrateAttaformState, renderAttaformState } from '../core/serialize'\nimport type { SerializedAttaformState } from '../core/serialize'\nimport type { AttaformDefaults } from '../types/types-api'\n\nexport default defineNuxtPlugin({\n // `enforce: 'pre'` makes the \"we run before any component's setup\" claim\n // explicit. Combined with `prepend: true` on the addPlugin call in\n // src/nuxt.ts, this guarantees hydration is staged into pendingHydration\n // before any user plugin or page can call `useForm`. Without it, a user\n // plugin running first would observe an empty registry and skip hydration.\n enforce: 'pre',\n setup(nuxtApp) {\n const isServer = import.meta.server\n\n // Read app-level defaults from the Nuxt module's runtime-config slot\n // (populated in src/nuxt.ts). The module ships in the same package\n // as this plugin, so the slot is always present and well-typed.\n const { defaults } = (useRuntimeConfig().public as { attaform: { defaults: AttaformDefaults } })\n .attaform\n\n nuxtApp.vueApp.use(createAttaform({ ssr: isServer, defaults }))\n\n if (isServer) {\n // After the app renders, capture every FormStore into the Nuxt payload\n // so the client can hydrate with matching form values and errors.\n nuxtApp.hook('app:rendered', () => {\n const state = renderAttaformState(nuxtApp.vueApp)\n ;(nuxtApp.payload as unknown as { attaform?: SerializedAttaformState }).attaform = state\n })\n } else {\n // Stage the payload into pendingHydration so `useForm` finds it. The\n // `enforce: 'pre'` + `prepend: true` pair above is what makes it safe\n // to assume this runs before any user setup.\n const serialized = (nuxtApp.payload as unknown as { attaform?: SerializedAttaformState })\n .attaform\n if (serialized !== undefined) {\n hydrateAttaformState(nuxtApp.vueApp, serialized)\n }\n }\n },\n})\n"],"names":["defineNuxtPlugin","useRuntimeConfig","createAttaform","renderAttaformState","hydrateAttaformState"],"mappings":";;;;;;AAgBA,IAAO,mBAAQA,oBAAA,CAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAM9B,OAAA,EAAS,KAAA;AAAA,EACT,MAAM,OAAA,EAAS;AACb,IAAA,MAAM,WAAW,SAAY;AAK7B,IAAA,MAAM,EAAE,QAAA,EAAS,GAAKC,oBAAA,GAAmB,MAAA,CACtC,QAAA;AAEH,IAAA,OAAA,CAAQ,MAAA,CAAO,IAAIC,qBAAA,CAAe,EAAE,KAAK,QAAA,EAAU,QAAA,EAAU,CAAC,CAAA;AAE9D,IAAA,IAAI,QAAA,EAAU;AAGZ,MAAA,OAAA,CAAQ,IAAA,CAAK,gBAAgB,MAAM;AACjC,QAAA,MAAM,KAAA,GAAQC,6BAAA,CAAoB,OAAA,CAAQ,MAAM,CAAA;AAC/C,QAAC,OAAA,CAAQ,QAA8D,QAAA,GAAW,KAAA;AAAA,MACrF,CAAC,CAAA;AAAA,IACH,CAAA,MAAO;AAIL,MAAA,MAAM,UAAA,GAAc,QAAQ,OAAA,CACzB,QAAA;AACH,MAAA,IAAI,eAAe,MAAA,EAAW;AAC5B,QAAAC,8BAAA,CAAqB,OAAA,CAAQ,QAAQ,UAAU,CAAA;AAAA,MACjD;AAAA,IACF;AAAA,EACF;AACF,CAAC;;;;"}
1
+ {"version":3,"file":"attaform.cjs","sources":["../../../src/runtime/plugins/attaform.ts"],"sourcesContent":["/**\n * Nuxt plugin: installs the framework-agnostic createAttaform Vue\n * plugin on nuxtApp.vueApp and wires the Nuxt payload mechanism to the\n * registry's SSR serialization helpers. Replaces the old split of\n * register.ts (client-only) + register-stub.ts (server-only).\n *\n * Runs on BOTH server and client — Vue's SSR renderer is a natural no-op\n * for directive lifecycle hooks, so the same plugin works on both sides\n * without a stub.\n */\nimport { defineNuxtPlugin, useRoute, useRuntimeConfig } from 'nuxt/app'\nimport { DEVTOOLS_WINDOW_KEY } from '../core/devtools-shared'\nimport { createAttaform } from '../core/plugin'\nimport { getRegistryFromApp, kAttaformWizardActiveStepResolver } from '../core/registry'\nimport { hydrateAttaformState, renderAttaformState } from '../core/serialize'\nimport type { SerializedAttaformState } from '../core/serialize'\nimport type { AttaformDefaults } from '../types/types-api'\n\nexport default defineNuxtPlugin({\n // `enforce: 'pre'` makes the \"we run before any component's setup\" claim\n // explicit. Combined with `prepend: true` on the addPlugin call in\n // src/nuxt.ts, this guarantees hydration is staged into pendingHydration\n // before any user plugin or page can call `useForm`. Without it, a user\n // plugin running first would observe an empty registry and skip hydration.\n enforce: 'pre',\n setup(nuxtApp) {\n const isServer = import.meta.server\n\n // Read app-level defaults from the Nuxt module's runtime-config slot\n // (populated in src/nuxt.ts). The module ships in the same package\n // as this plugin, so the slot is always present and well-typed.\n const config = useRuntimeConfig().public as {\n attaform: { defaults: AttaformDefaults; version: string }\n }\n const { defaults, version } = config.attaform\n\n nuxtApp.vueApp.use(createAttaform({ ssr: isServer, defaults }))\n\n // Bridge `useWizard`'s active-step resolution to the Nuxt route so\n // deep-links hydrate without flicker. On the server, `useRoute()`\n // reads the incoming request URL; on the client, it reads the live\n // route — so server and client compute the same initial step and\n // Vue's hydration walks a matching tree. Without this bridge, the\n // wizard would fall back to its first step on the server while the\n // client reads the URL, producing the deep-link mismatch cascade.\n // Consumers can still pass `options.restore` explicitly to override;\n // this just removes the boilerplate for the common case.\n nuxtApp.vueApp.provide(kAttaformWizardActiveStepResolver, (param) => {\n const value = useRoute().query[param]\n if (typeof value === 'string') return value\n if (Array.isArray(value) && typeof value[0] === 'string') return value[0]\n return undefined\n })\n\n if (isServer) {\n // After the app renders, capture every FormStore into the Nuxt payload\n // so the client can hydrate with matching form values and errors.\n nuxtApp.hook('app:rendered', () => {\n const state = renderAttaformState(nuxtApp.vueApp)\n ;(nuxtApp.payload as unknown as { attaform?: SerializedAttaformState }).attaform = state\n })\n } else {\n // Stage the payload into pendingHydration so `useForm` finds it. The\n // `enforce: 'pre'` + `prepend: true` pair above is what makes it safe\n // to assume this runs before any user setup.\n const serialized = (nuxtApp.payload as unknown as { attaform?: SerializedAttaformState })\n .attaform\n if (serialized !== undefined) {\n hydrateAttaformState(nuxtApp.vueApp, serialized)\n }\n\n // Dev-only: attach the registry to window so the Nuxt DevTools overlay\n // panel (which runs in an iframe at /_attaform_devtools) can reach it\n // via `window.parent.__attaform_devtools__`. The bridge holds a live\n // reference to the registry — Vue's reactivity flows across the\n // same-origin iframe boundary, so the panel re-renders on every form\n // mutation without an explicit push channel. `import.meta.dev` is\n // statically replaced by Nuxt at build time, so this whole branch is\n // tree-shaken from production builds.\n if (import.meta.dev) {\n const registry = getRegistryFromApp(nuxtApp.vueApp)\n window[DEVTOOLS_WINDOW_KEY] = { registry, version }\n }\n }\n },\n})\n"],"names":["defineNuxtPlugin","useRuntimeConfig","createAttaform","kAttaformWizardActiveStepResolver","useRoute","renderAttaformState","hydrateAttaformState","getRegistryFromApp","DEVTOOLS_WINDOW_KEY"],"mappings":";;;;;;AAkBA,IAAO,mBAAQA,oBAAA,CAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAM9B,OAAA,EAAS,KAAA;AAAA,EACT,MAAM,OAAA,EAAS;AACb,IAAA,MAAM,WAAW,SAAY;AAK7B,IAAA,MAAM,MAAA,GAASC,sBAAiB,CAAE,MAAA;AAGlC,IAAA,MAAM,EAAE,QAAA,EAAU,OAAA,EAAQ,GAAI,MAAA,CAAO,QAAA;AAErC,IAAA,OAAA,CAAQ,MAAA,CAAO,IAAIC,oBAAA,CAAe,EAAE,KAAK,QAAA,EAAU,QAAA,EAAU,CAAC,CAAA;AAW9D,IAAA,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQC,uCAAA,EAAmC,CAAC,KAAA,KAAU;AACnE,MAAA,MAAM,KAAA,GAAQC,YAAA,EAAS,CAAE,KAAA,CAAM,KAAK,CAAA;AACpC,MAAA,IAAI,OAAO,KAAA,KAAU,QAAA,EAAU,OAAO,KAAA;AACtC,MAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,IAAK,OAAO,KAAA,CAAM,CAAC,CAAA,KAAM,QAAA,EAAU,OAAO,KAAA,CAAM,CAAC,CAAA;AACxE,MAAA,OAAO,MAAA;AAAA,IACT,CAAC,CAAA;AAED,IAAA,IAAI,QAAA,EAAU;AAGZ,MAAA,OAAA,CAAQ,IAAA,CAAK,gBAAgB,MAAM;AACjC,QAAA,MAAM,KAAA,GAAQC,kCAAA,CAAoB,OAAA,CAAQ,MAAM,CAAA;AAC/C,QAAC,OAAA,CAAQ,QAA8D,QAAA,GAAW,KAAA;AAAA,MACrF,CAAC,CAAA;AAAA,IACH,CAAA,MAAO;AAIL,MAAA,MAAM,UAAA,GAAc,QAAQ,OAAA,CACzB,QAAA;AACH,MAAA,IAAI,eAAe,MAAA,EAAW;AAC5B,QAAAC,mCAAA,CAAqB,OAAA,CAAQ,QAAQ,UAAU,CAAA;AAAA,MACjD;AAUA,MAAA,IAAI,SAAY,EAAK;AACnB,QAAA,MAAM,QAAA,GAAWC,wBAAA,CAAmB,OAAA,CAAQ,MAAM,CAAA;AAClD,QAAA,MAAA,CAAOC,kCAAmB,CAAA,GAAI,EAAE,QAAA,EAAU,OAAA,EAAQ;AAAA,MACpD;AAAA,IACF;AAAA,EACF;AACF,CAAC;;;;"}
@@ -1,6 +1,6 @@
1
- import { defineNuxtPlugin, useRuntimeConfig } from 'nuxt/app';
2
- import { c as createAttaform } from '../../shared/attaform.CIEQgJnM.mjs';
3
- import { r as renderAttaformState, h as hydrateAttaformState } from '../../shared/attaform.Vo-Kft0t.mjs';
1
+ import { defineNuxtPlugin, useRuntimeConfig, useRoute } from 'nuxt/app';
2
+ import { r as renderAttaformState, h as hydrateAttaformState, D as DEVTOOLS_WINDOW_KEY } from '../../shared/attaform.BDdFdjeX.mjs';
3
+ import { c as createAttaform, k as kAttaformWizardActiveStepResolver, g as getRegistryFromApp } from '../../shared/attaform.Xhg0AYNa.mjs';
4
4
 
5
5
  var attaform_default = defineNuxtPlugin({
6
6
  // `enforce: 'pre'` makes the "we run before any component's setup" claim
@@ -11,8 +11,15 @@ var attaform_default = defineNuxtPlugin({
11
11
  enforce: "pre",
12
12
  setup(nuxtApp) {
13
13
  const isServer = import.meta.server;
14
- const { defaults } = useRuntimeConfig().public.attaform;
14
+ const config = useRuntimeConfig().public;
15
+ const { defaults, version } = config.attaform;
15
16
  nuxtApp.vueApp.use(createAttaform({ ssr: isServer, defaults }));
17
+ nuxtApp.vueApp.provide(kAttaformWizardActiveStepResolver, (param) => {
18
+ const value = useRoute().query[param];
19
+ if (typeof value === "string") return value;
20
+ if (Array.isArray(value) && typeof value[0] === "string") return value[0];
21
+ return void 0;
22
+ });
16
23
  if (isServer) {
17
24
  nuxtApp.hook("app:rendered", () => {
18
25
  const state = renderAttaformState(nuxtApp.vueApp);
@@ -23,6 +30,10 @@ var attaform_default = defineNuxtPlugin({
23
30
  if (serialized !== void 0) {
24
31
  hydrateAttaformState(nuxtApp.vueApp, serialized);
25
32
  }
33
+ if (import.meta.dev) {
34
+ const registry = getRegistryFromApp(nuxtApp.vueApp);
35
+ window[DEVTOOLS_WINDOW_KEY] = { registry, version };
36
+ }
26
37
  }
27
38
  }
28
39
  });
@@ -1 +1 @@
1
- {"version":3,"file":"attaform.mjs","sources":["../../../src/runtime/plugins/attaform.ts"],"sourcesContent":["/**\n * Nuxt plugin: installs the framework-agnostic createAttaform Vue\n * plugin on nuxtApp.vueApp and wires the Nuxt payload mechanism to the\n * registry's SSR serialization helpers. Replaces the old split of\n * register.ts (client-only) + register-stub.ts (server-only).\n *\n * Runs on BOTH server and client — Vue's SSR renderer is a natural no-op\n * for directive lifecycle hooks, so the same plugin works on both sides\n * without a stub.\n */\nimport { defineNuxtPlugin, useRuntimeConfig } from 'nuxt/app'\nimport { createAttaform } from '../core/plugin'\nimport { hydrateAttaformState, renderAttaformState } from '../core/serialize'\nimport type { SerializedAttaformState } from '../core/serialize'\nimport type { AttaformDefaults } from '../types/types-api'\n\nexport default defineNuxtPlugin({\n // `enforce: 'pre'` makes the \"we run before any component's setup\" claim\n // explicit. Combined with `prepend: true` on the addPlugin call in\n // src/nuxt.ts, this guarantees hydration is staged into pendingHydration\n // before any user plugin or page can call `useForm`. Without it, a user\n // plugin running first would observe an empty registry and skip hydration.\n enforce: 'pre',\n setup(nuxtApp) {\n const isServer = import.meta.server\n\n // Read app-level defaults from the Nuxt module's runtime-config slot\n // (populated in src/nuxt.ts). The module ships in the same package\n // as this plugin, so the slot is always present and well-typed.\n const { defaults } = (useRuntimeConfig().public as { attaform: { defaults: AttaformDefaults } })\n .attaform\n\n nuxtApp.vueApp.use(createAttaform({ ssr: isServer, defaults }))\n\n if (isServer) {\n // After the app renders, capture every FormStore into the Nuxt payload\n // so the client can hydrate with matching form values and errors.\n nuxtApp.hook('app:rendered', () => {\n const state = renderAttaformState(nuxtApp.vueApp)\n ;(nuxtApp.payload as unknown as { attaform?: SerializedAttaformState }).attaform = state\n })\n } else {\n // Stage the payload into pendingHydration so `useForm` finds it. The\n // `enforce: 'pre'` + `prepend: true` pair above is what makes it safe\n // to assume this runs before any user setup.\n const serialized = (nuxtApp.payload as unknown as { attaform?: SerializedAttaformState })\n .attaform\n if (serialized !== undefined) {\n hydrateAttaformState(nuxtApp.vueApp, serialized)\n }\n }\n },\n})\n"],"names":[],"mappings":";;;;AAgBA,IAAO,mBAAQ,gBAAA,CAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAM9B,OAAA,EAAS,KAAA;AAAA,EACT,MAAM,OAAA,EAAS;AACb,IAAA,MAAM,WAAW,MAAA,CAAA,IAAA,CAAY,MAAA;AAK7B,IAAA,MAAM,EAAE,QAAA,EAAS,GAAK,gBAAA,GAAmB,MAAA,CACtC,QAAA;AAEH,IAAA,OAAA,CAAQ,MAAA,CAAO,IAAI,cAAA,CAAe,EAAE,KAAK,QAAA,EAAU,QAAA,EAAU,CAAC,CAAA;AAE9D,IAAA,IAAI,QAAA,EAAU;AAGZ,MAAA,OAAA,CAAQ,IAAA,CAAK,gBAAgB,MAAM;AACjC,QAAA,MAAM,KAAA,GAAQ,mBAAA,CAAoB,OAAA,CAAQ,MAAM,CAAA;AAC/C,QAAC,OAAA,CAAQ,QAA8D,QAAA,GAAW,KAAA;AAAA,MACrF,CAAC,CAAA;AAAA,IACH,CAAA,MAAO;AAIL,MAAA,MAAM,UAAA,GAAc,QAAQ,OAAA,CACzB,QAAA;AACH,MAAA,IAAI,eAAe,MAAA,EAAW;AAC5B,QAAA,oBAAA,CAAqB,OAAA,CAAQ,QAAQ,UAAU,CAAA;AAAA,MACjD;AAAA,IACF;AAAA,EACF;AACF,CAAC;;;;"}
1
+ {"version":3,"file":"attaform.mjs","sources":["../../../src/runtime/plugins/attaform.ts"],"sourcesContent":["/**\n * Nuxt plugin: installs the framework-agnostic createAttaform Vue\n * plugin on nuxtApp.vueApp and wires the Nuxt payload mechanism to the\n * registry's SSR serialization helpers. Replaces the old split of\n * register.ts (client-only) + register-stub.ts (server-only).\n *\n * Runs on BOTH server and client — Vue's SSR renderer is a natural no-op\n * for directive lifecycle hooks, so the same plugin works on both sides\n * without a stub.\n */\nimport { defineNuxtPlugin, useRoute, useRuntimeConfig } from 'nuxt/app'\nimport { DEVTOOLS_WINDOW_KEY } from '../core/devtools-shared'\nimport { createAttaform } from '../core/plugin'\nimport { getRegistryFromApp, kAttaformWizardActiveStepResolver } from '../core/registry'\nimport { hydrateAttaformState, renderAttaformState } from '../core/serialize'\nimport type { SerializedAttaformState } from '../core/serialize'\nimport type { AttaformDefaults } from '../types/types-api'\n\nexport default defineNuxtPlugin({\n // `enforce: 'pre'` makes the \"we run before any component's setup\" claim\n // explicit. Combined with `prepend: true` on the addPlugin call in\n // src/nuxt.ts, this guarantees hydration is staged into pendingHydration\n // before any user plugin or page can call `useForm`. Without it, a user\n // plugin running first would observe an empty registry and skip hydration.\n enforce: 'pre',\n setup(nuxtApp) {\n const isServer = import.meta.server\n\n // Read app-level defaults from the Nuxt module's runtime-config slot\n // (populated in src/nuxt.ts). The module ships in the same package\n // as this plugin, so the slot is always present and well-typed.\n const config = useRuntimeConfig().public as {\n attaform: { defaults: AttaformDefaults; version: string }\n }\n const { defaults, version } = config.attaform\n\n nuxtApp.vueApp.use(createAttaform({ ssr: isServer, defaults }))\n\n // Bridge `useWizard`'s active-step resolution to the Nuxt route so\n // deep-links hydrate without flicker. On the server, `useRoute()`\n // reads the incoming request URL; on the client, it reads the live\n // route — so server and client compute the same initial step and\n // Vue's hydration walks a matching tree. Without this bridge, the\n // wizard would fall back to its first step on the server while the\n // client reads the URL, producing the deep-link mismatch cascade.\n // Consumers can still pass `options.restore` explicitly to override;\n // this just removes the boilerplate for the common case.\n nuxtApp.vueApp.provide(kAttaformWizardActiveStepResolver, (param) => {\n const value = useRoute().query[param]\n if (typeof value === 'string') return value\n if (Array.isArray(value) && typeof value[0] === 'string') return value[0]\n return undefined\n })\n\n if (isServer) {\n // After the app renders, capture every FormStore into the Nuxt payload\n // so the client can hydrate with matching form values and errors.\n nuxtApp.hook('app:rendered', () => {\n const state = renderAttaformState(nuxtApp.vueApp)\n ;(nuxtApp.payload as unknown as { attaform?: SerializedAttaformState }).attaform = state\n })\n } else {\n // Stage the payload into pendingHydration so `useForm` finds it. The\n // `enforce: 'pre'` + `prepend: true` pair above is what makes it safe\n // to assume this runs before any user setup.\n const serialized = (nuxtApp.payload as unknown as { attaform?: SerializedAttaformState })\n .attaform\n if (serialized !== undefined) {\n hydrateAttaformState(nuxtApp.vueApp, serialized)\n }\n\n // Dev-only: attach the registry to window so the Nuxt DevTools overlay\n // panel (which runs in an iframe at /_attaform_devtools) can reach it\n // via `window.parent.__attaform_devtools__`. The bridge holds a live\n // reference to the registry — Vue's reactivity flows across the\n // same-origin iframe boundary, so the panel re-renders on every form\n // mutation without an explicit push channel. `import.meta.dev` is\n // statically replaced by Nuxt at build time, so this whole branch is\n // tree-shaken from production builds.\n if (import.meta.dev) {\n const registry = getRegistryFromApp(nuxtApp.vueApp)\n window[DEVTOOLS_WINDOW_KEY] = { registry, version }\n }\n }\n },\n})\n"],"names":[],"mappings":";;;;AAkBA,IAAO,mBAAQ,gBAAA,CAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAM9B,OAAA,EAAS,KAAA;AAAA,EACT,MAAM,OAAA,EAAS;AACb,IAAA,MAAM,WAAW,MAAA,CAAA,IAAA,CAAY,MAAA;AAK7B,IAAA,MAAM,MAAA,GAAS,kBAAiB,CAAE,MAAA;AAGlC,IAAA,MAAM,EAAE,QAAA,EAAU,OAAA,EAAQ,GAAI,MAAA,CAAO,QAAA;AAErC,IAAA,OAAA,CAAQ,MAAA,CAAO,IAAI,cAAA,CAAe,EAAE,KAAK,QAAA,EAAU,QAAA,EAAU,CAAC,CAAA;AAW9D,IAAA,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,iCAAA,EAAmC,CAAC,KAAA,KAAU;AACnE,MAAA,MAAM,KAAA,GAAQ,QAAA,EAAS,CAAE,KAAA,CAAM,KAAK,CAAA;AACpC,MAAA,IAAI,OAAO,KAAA,KAAU,QAAA,EAAU,OAAO,KAAA;AACtC,MAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,IAAK,OAAO,KAAA,CAAM,CAAC,CAAA,KAAM,QAAA,EAAU,OAAO,KAAA,CAAM,CAAC,CAAA;AACxE,MAAA,OAAO,MAAA;AAAA,IACT,CAAC,CAAA;AAED,IAAA,IAAI,QAAA,EAAU;AAGZ,MAAA,OAAA,CAAQ,IAAA,CAAK,gBAAgB,MAAM;AACjC,QAAA,MAAM,KAAA,GAAQ,mBAAA,CAAoB,OAAA,CAAQ,MAAM,CAAA;AAC/C,QAAC,OAAA,CAAQ,QAA8D,QAAA,GAAW,KAAA;AAAA,MACrF,CAAC,CAAA;AAAA,IACH,CAAA,MAAO;AAIL,MAAA,MAAM,UAAA,GAAc,QAAQ,OAAA,CACzB,QAAA;AACH,MAAA,IAAI,eAAe,MAAA,EAAW;AAC5B,QAAA,oBAAA,CAAqB,OAAA,CAAQ,QAAQ,UAAU,CAAA;AAAA,MACjD;AAUA,MAAA,IAAI,YAAY,GAAA,EAAK;AACnB,QAAA,MAAM,QAAA,GAAW,kBAAA,CAAmB,OAAA,CAAQ,MAAM,CAAA;AAClD,QAAA,MAAA,CAAO,mBAAmB,CAAA,GAAI,EAAE,QAAA,EAAU,OAAA,EAAQ;AAAA,MACpD;AAAA,IACF;AAAA,EACF;AACF,CAAC;;;;"}
@@ -0,0 +1,63 @@
1
+ 'use strict';
2
+
3
+ const paths = require('./attaform.BqK_L4gK.cjs');
4
+
5
+ function renderAttaformState(app) {
6
+ const registry = paths.getRegistryFromApp(app);
7
+ const forms = [];
8
+ for (const [key, state] of registry.forms) {
9
+ const transientList = [];
10
+ for (const pk of state.blankPaths) {
11
+ const d = paths.pathKeyToDotted(pk);
12
+ if (d !== null) transientList.push(d);
13
+ }
14
+ forms.push([
15
+ key,
16
+ {
17
+ form: state.form.value,
18
+ schemaErrors: Array.from(state.schemaErrors.entries()),
19
+ userErrors: Array.from(state.userErrors.entries()),
20
+ fields: Array.from(state.fields.entries()),
21
+ ...transientList.length > 0 ? { blankPaths: transientList } : {}
22
+ }
23
+ ]);
24
+ }
25
+ return { forms };
26
+ }
27
+ function hydrateAttaformState(app, payload) {
28
+ const registry = paths.getRegistryFromApp(app);
29
+ for (const [key, data] of payload.forms) {
30
+ registry.pendingHydration.set(key, data);
31
+ }
32
+ }
33
+
34
+ const REDACTED = "[redacted]";
35
+ function redactSensitiveLeaves(value, matchSensitive) {
36
+ return redactImpl(value, false, matchSensitive);
37
+ }
38
+ function redactImpl(value, inSensitiveSubtree, matchSensitive) {
39
+ if (value === null || value === void 0) return value;
40
+ if (typeof value !== "object") {
41
+ return inSensitiveSubtree ? REDACTED : value;
42
+ }
43
+ if (Array.isArray(value)) {
44
+ return value.map((item) => redactImpl(item, inSensitiveSubtree, matchSensitive));
45
+ }
46
+ if (Object.prototype.toString.call(value) !== "[object Object]") {
47
+ return inSensitiveSubtree ? REDACTED : value;
48
+ }
49
+ const out = {};
50
+ for (const key of Object.keys(value)) {
51
+ const childSensitive = inSensitiveSubtree || matchSensitive(key);
52
+ out[key] = redactImpl(value[key], childSensitive, matchSensitive);
53
+ }
54
+ return out;
55
+ }
56
+ const DEVTOOLS_WINDOW_KEY = "__attaform_devtools__";
57
+
58
+ exports.DEVTOOLS_WINDOW_KEY = DEVTOOLS_WINDOW_KEY;
59
+ exports.REDACTED = REDACTED;
60
+ exports.hydrateAttaformState = hydrateAttaformState;
61
+ exports.redactSensitiveLeaves = redactSensitiveLeaves;
62
+ exports.renderAttaformState = renderAttaformState;
63
+ //# sourceMappingURL=attaform.5UhpSVFI.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attaform.5UhpSVFI.cjs","sources":["../../src/runtime/core/serialize.ts","../../src/runtime/core/devtools-shared.ts"],"sourcesContent":["import type { App } from 'vue'\nimport type { FormKey } from '../types/types-api'\nimport { pathKeyToDotted, type PathKey } from './paths'\nimport { getRegistryFromApp, type SerializedFormData } from './registry'\n\n/**\n * Serialised snapshot of every form in a Vue app, produced by\n * `renderAttaformState` and consumed by `hydrateAttaformState`.\n *\n * JSON-safe — pass to `JSON.stringify`, `devalue`, or any other\n * serialiser before embedding in your SSR payload.\n */\nexport type SerializedAttaformState = {\n /** Tuples of `[formKey, snapshot]` for every form in the app. */\n readonly forms: ReadonlyArray<readonly [FormKey, SerializedFormData]>\n}\n\n/**\n * Snapshot every form on a Vue app for SSR. Call from your server\n * entry after rendering the app:\n *\n * ```ts\n * import { renderToString } from '@vue/server-renderer'\n * import { renderAttaformState, escapeForInlineScript } from 'attaform'\n *\n * const html = await renderToString(app)\n * const state = renderAttaformState(app)\n * const payload = escapeForInlineScript(JSON.stringify(state))\n *\n * return `\n * ${html}\n * <script>window.__ATTAFORM_STATE__ = ${payload}</script>\n * `\n * ```\n *\n * Pair with `hydrateAttaformState` on the client to restore the\n * forms in their server-rendered state. Nuxt users don't need this —\n * `attaform/nuxt` wires SSR automatically.\n */\nexport function renderAttaformState(app: App): SerializedAttaformState {\n const registry = getRegistryFromApp(app)\n const forms: Array<readonly [FormKey, SerializedFormData]> = []\n for (const [key, state] of registry.forms) {\n // Skip the blank field when the set is empty so the\n // wire payload stays minimal for forms that don't use it. The\n // optional shape on the consuming side handles the absence\n // cleanly (defaults to \"no blank paths\"). PathKey → dotted at\n // the boundary so the wire shape matches the rest of the\n // public path notation.\n const transientList: string[] = []\n for (const pk of state.blankPaths) {\n const d = pathKeyToDotted(pk as PathKey)\n if (d !== null) transientList.push(d)\n }\n forms.push([\n key,\n {\n form: state.form.value,\n schemaErrors: Array.from(state.schemaErrors.entries()),\n userErrors: Array.from(state.userErrors.entries()),\n fields: Array.from(state.fields.entries()),\n ...(transientList.length > 0 ? { blankPaths: transientList } : {}),\n },\n ])\n }\n return { forms }\n}\n\n/**\n * Restore forms from a server-rendered snapshot on the client. Call\n * from your client entry before mounting:\n *\n * ```ts\n * import { createApp } from 'vue'\n * import { createAttaform, hydrateAttaformState } from 'attaform'\n *\n * const app = createApp(App).use(createAttaform())\n * hydrateAttaformState(app, window.__ATTAFORM_STATE__)\n * app.mount('#app')\n * ```\n *\n * The next `useForm({ key })` call for each serialised form picks up\n * the snapshot transparently — no further action is required.\n */\nexport function hydrateAttaformState(app: App, payload: SerializedAttaformState): void {\n const registry = getRegistryFromApp(app)\n for (const [key, data] of payload.forms) {\n registry.pendingHydration.set(key, data)\n }\n}\n","/**\n * Shared building blocks for Attaform's two devtools surfaces — the Vue\n * DevTools (Chrome-extension) inspector wired up in `./devtools.ts`, and\n * the Nuxt DevTools (overlay) panel wired up via `../../nuxt.ts` +\n * `../pages/_attaform_devtools.vue`.\n *\n * Centralizing the redaction policy and the window-bridge contract here\n * keeps both surfaces aligned: a future tightening of the sensitive-name\n * heuristic, or a new field added to the bridge, lands in one file.\n */\nimport type { AttaformRegistry } from './registry'\nimport type { Segment } from './paths'\n\nexport const REDACTED = '[redacted]'\n\n/**\n * Walk `value` and replace any leaf whose enclosing path matches the\n * sensitive-name heuristic with the string `'[redacted]'`. Returns a\n * new tree (no mutation of the input). Object keys + array indices are\n * preserved; only the leaf payloads change.\n *\n * Applied to BOTH devtools surfaces' Form-value rendering AND every\n * timeline event payload — leaks via either surface are treatable as\n * \"any developer with the panel open during user testing can read a\n * customer's password,\" which is exactly the failure mode the\n * sensitive-name guard exists to prevent on the storage side.\n *\n * Leaves whose path doesn't match a pattern pass through untouched.\n * `acknowledgeSensitive: true` on persistence does NOT bypass this — if\n * the consumer opted into persisting the value, they still shouldn't\n * see it in DevTools timelines that grow unbounded.\n *\n * Implementation note: tracks an `inSensitiveSubtree` flag through the\n * recursion instead of allocating a fresh path array per node + calling\n * `isSensitivePath` per leaf. Once any ancestor segment matches the\n * heuristic, the flag stays set for every descendant — the leaf simply\n * returns `REDACTED` without re-scanning the path. For a 100-leaf form:\n * ~100 path allocations + ~100 full-path regex sweeps → 0 path\n * allocations + ~100 single-segment regex sweeps, with whole-subtree\n * short-circuit when sensitive ancestors are found early.\n */\nexport function redactSensitiveLeaves(\n value: unknown,\n matchSensitive: (segment: Segment) => boolean\n): unknown {\n return redactImpl(value, false, matchSensitive)\n}\n\nfunction redactImpl(\n value: unknown,\n inSensitiveSubtree: boolean,\n matchSensitive: (segment: Segment) => boolean\n): unknown {\n if (value === null || value === undefined) return value\n if (typeof value !== 'object') {\n return inSensitiveSubtree ? REDACTED : value\n }\n if (Array.isArray(value)) {\n // Numeric segments never match the sensitive-name heuristic\n // (segmentMatchesSensitive rejects non-string segments), so the\n // flag passes through unchanged when descending into arrays.\n return value.map((item) => redactImpl(item, inSensitiveSubtree, matchSensitive))\n }\n // Non-plain object (Map / Set / Date / class instance) — redact\n // wholesale if we're already in a sensitive subtree; otherwise pass\n // through. DevTools rendering of these is already heuristic, so we\n // don't try to descend into them.\n //\n // Use `Object.prototype.toString.call(value)` rather than a\n // `getPrototypeOf` comparison because `Object.prototype` is\n // realm-scoped — the Nuxt DevTools overlay panel runs in an iframe\n // whose Vue runtime is separate from the host's, so the host's\n // reactive proxies have a prototype that doesn't equal the panel's\n // `Object.prototype`. The `toString` tag check is realm-aware via\n // `@@toStringTag` and returns `'[object Object]'` for plain objects\n // (including Vue reactive proxies of plain objects) regardless of\n // which iframe they were created in.\n if (Object.prototype.toString.call(value) !== '[object Object]') {\n return inSensitiveSubtree ? REDACTED : value\n }\n const out: Record<string, unknown> = {}\n for (const key of Object.keys(value as Record<string, unknown>)) {\n const childSensitive = inSensitiveSubtree || matchSensitive(key)\n out[key] = redactImpl((value as Record<string, unknown>)[key], childSensitive, matchSensitive)\n }\n return out\n}\n\n/**\n * Property key on `window` that the Nuxt-side dev plugin attaches the\n * bridge object to. The iframe-mounted overlay panel reads\n * `window.parent[DEVTOOLS_WINDOW_KEY]` to reach the host app's registry.\n *\n * Underscored + namespaced to make accidental collision with consumer\n * globals vanishingly unlikely. Stable across versions — bumping it\n * would silently disconnect older library builds from newer overlay\n * panels in the same browser tab during a library upgrade.\n */\nexport const DEVTOOLS_WINDOW_KEY = '__attaform_devtools__'\n\n/**\n * Shape of the object the host plugin attaches to `window` in dev mode.\n * The iframe overlay panel reads this to discover the live registry and\n * render its forms.\n *\n * Single-registry assumption: the latest `createAttaform()` install\n * wins. Multi-app pages (rare; typically only seen in micro-frontend\n * setups) will only see one app's forms in the panel. Documented but\n * not actively supported — the alternative (a Set of registries with\n * union-rendering) is a future call if a real consumer hits it.\n */\nexport interface AttaformDevtoolsBridge {\n registry: AttaformRegistry\n /**\n * The library version, surfaced in the panel's footer for support /\n * bug-report context. Read from `package.json` at host-plugin init.\n */\n version: string\n}\n\ndeclare global {\n interface Window {\n [DEVTOOLS_WINDOW_KEY]?: AttaformDevtoolsBridge\n }\n}\n"],"names":["getRegistryFromApp","pathKeyToDotted"],"mappings":";;;;AAuCO,SAAS,oBAAoB,GAAA,EAAmC;AACrE,EAAA,MAAM,QAAA,GAAWA,yBAAmB,GAAG,CAAA;AACvC,EAAA,MAAM,QAAuD,EAAC;AAC9D,EAAA,KAAA,MAAW,CAAC,GAAA,EAAK,KAAK,CAAA,IAAK,SAAS,KAAA,EAAO;AAOzC,IAAA,MAAM,gBAA0B,EAAC;AACjC,IAAA,KAAA,MAAW,EAAA,IAAM,MAAM,UAAA,EAAY;AACjC,MAAA,MAAM,CAAA,GAAIC,sBAAgB,EAAa,CAAA;AACvC,MAAA,IAAI,CAAA,KAAM,IAAA,EAAM,aAAA,CAAc,IAAA,CAAK,CAAC,CAAA;AAAA,IACtC;AACA,IAAA,KAAA,CAAM,IAAA,CAAK;AAAA,MACT,GAAA;AAAA,MACA;AAAA,QACE,IAAA,EAAM,MAAM,IAAA,CAAK,KAAA;AAAA,QACjB,cAAc,KAAA,CAAM,IAAA,CAAK,KAAA,CAAM,YAAA,CAAa,SAAS,CAAA;AAAA,QACrD,YAAY,KAAA,CAAM,IAAA,CAAK,KAAA,CAAM,UAAA,CAAW,SAAS,CAAA;AAAA,QACjD,QAAQ,KAAA,CAAM,IAAA,CAAK,KAAA,CAAM,MAAA,CAAO,SAAS,CAAA;AAAA,QACzC,GAAI,cAAc,MAAA,GAAS,CAAA,GAAI,EAAE,UAAA,EAAY,aAAA,KAAkB;AAAC;AAClE,KACD,CAAA;AAAA,EACH;AACA,EAAA,OAAO,EAAE,KAAA,EAAM;AACjB;AAkBO,SAAS,oBAAA,CAAqB,KAAU,OAAA,EAAwC;AACrF,EAAA,MAAM,QAAA,GAAWD,yBAAmB,GAAG,CAAA;AACvC,EAAA,KAAA,MAAW,CAAC,GAAA,EAAK,IAAI,CAAA,IAAK,QAAQ,KAAA,EAAO;AACvC,IAAA,QAAA,CAAS,gBAAA,CAAiB,GAAA,CAAI,GAAA,EAAK,IAAI,CAAA;AAAA,EACzC;AACF;;AC5EO,MAAM,QAAA,GAAW;AA4BjB,SAAS,qBAAA,CACd,OACA,cAAA,EACS;AACT,EAAA,OAAO,UAAA,CAAW,KAAA,EAAO,KAAA,EAAO,cAAc,CAAA;AAChD;AAEA,SAAS,UAAA,CACP,KAAA,EACA,kBAAA,EACA,cAAA,EACS;AACT,EAAA,IAAI,KAAA,KAAU,IAAA,IAAQ,KAAA,KAAU,MAAA,EAAW,OAAO,KAAA;AAClD,EAAA,IAAI,OAAO,UAAU,QAAA,EAAU;AAC7B,IAAA,OAAO,qBAAqB,QAAA,GAAW,KAAA;AAAA,EACzC;AACA,EAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,EAAG;AAIxB,IAAA,OAAO,KAAA,CAAM,IAAI,CAAC,IAAA,KAAS,WAAW,IAAA,EAAM,kBAAA,EAAoB,cAAc,CAAC,CAAA;AAAA,EACjF;AAeA,EAAA,IAAI,OAAO,SAAA,CAAU,QAAA,CAAS,IAAA,CAAK,KAAK,MAAM,iBAAA,EAAmB;AAC/D,IAAA,OAAO,qBAAqB,QAAA,GAAW,KAAA;AAAA,EACzC;AACA,EAAA,MAAM,MAA+B,EAAC;AACtC,EAAA,KAAA,MAAW,GAAA,IAAO,MAAA,CAAO,IAAA,CAAK,KAAgC,CAAA,EAAG;AAC/D,IAAA,MAAM,cAAA,GAAiB,kBAAA,IAAsB,cAAA,CAAe,GAAG,CAAA;AAC/D,IAAA,GAAA,CAAI,GAAG,CAAA,GAAI,UAAA,CAAY,MAAkC,GAAG,CAAA,EAAG,gBAAgB,cAAc,CAAA;AAAA,EAC/F;AACA,EAAA,OAAO,GAAA;AACT;AAYO,MAAM,mBAAA,GAAsB;;;;;;;;"}
@@ -0,0 +1,57 @@
1
+ import { g as getRegistryFromApp, r as pathKeyToDotted } from './attaform.Xhg0AYNa.mjs';
2
+
3
+ function renderAttaformState(app) {
4
+ const registry = getRegistryFromApp(app);
5
+ const forms = [];
6
+ for (const [key, state] of registry.forms) {
7
+ const transientList = [];
8
+ for (const pk of state.blankPaths) {
9
+ const d = pathKeyToDotted(pk);
10
+ if (d !== null) transientList.push(d);
11
+ }
12
+ forms.push([
13
+ key,
14
+ {
15
+ form: state.form.value,
16
+ schemaErrors: Array.from(state.schemaErrors.entries()),
17
+ userErrors: Array.from(state.userErrors.entries()),
18
+ fields: Array.from(state.fields.entries()),
19
+ ...transientList.length > 0 ? { blankPaths: transientList } : {}
20
+ }
21
+ ]);
22
+ }
23
+ return { forms };
24
+ }
25
+ function hydrateAttaformState(app, payload) {
26
+ const registry = getRegistryFromApp(app);
27
+ for (const [key, data] of payload.forms) {
28
+ registry.pendingHydration.set(key, data);
29
+ }
30
+ }
31
+
32
+ const REDACTED = "[redacted]";
33
+ function redactSensitiveLeaves(value, matchSensitive) {
34
+ return redactImpl(value, false, matchSensitive);
35
+ }
36
+ function redactImpl(value, inSensitiveSubtree, matchSensitive) {
37
+ if (value === null || value === void 0) return value;
38
+ if (typeof value !== "object") {
39
+ return inSensitiveSubtree ? REDACTED : value;
40
+ }
41
+ if (Array.isArray(value)) {
42
+ return value.map((item) => redactImpl(item, inSensitiveSubtree, matchSensitive));
43
+ }
44
+ if (Object.prototype.toString.call(value) !== "[object Object]") {
45
+ return inSensitiveSubtree ? REDACTED : value;
46
+ }
47
+ const out = {};
48
+ for (const key of Object.keys(value)) {
49
+ const childSensitive = inSensitiveSubtree || matchSensitive(key);
50
+ out[key] = redactImpl(value[key], childSensitive, matchSensitive);
51
+ }
52
+ return out;
53
+ }
54
+ const DEVTOOLS_WINDOW_KEY = "__attaform_devtools__";
55
+
56
+ export { DEVTOOLS_WINDOW_KEY as D, REDACTED as R, redactSensitiveLeaves as a, hydrateAttaformState as h, renderAttaformState as r };
57
+ //# sourceMappingURL=attaform.BDdFdjeX.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attaform.BDdFdjeX.mjs","sources":["../../src/runtime/core/serialize.ts","../../src/runtime/core/devtools-shared.ts"],"sourcesContent":["import type { App } from 'vue'\nimport type { FormKey } from '../types/types-api'\nimport { pathKeyToDotted, type PathKey } from './paths'\nimport { getRegistryFromApp, type SerializedFormData } from './registry'\n\n/**\n * Serialised snapshot of every form in a Vue app, produced by\n * `renderAttaformState` and consumed by `hydrateAttaformState`.\n *\n * JSON-safe — pass to `JSON.stringify`, `devalue`, or any other\n * serialiser before embedding in your SSR payload.\n */\nexport type SerializedAttaformState = {\n /** Tuples of `[formKey, snapshot]` for every form in the app. */\n readonly forms: ReadonlyArray<readonly [FormKey, SerializedFormData]>\n}\n\n/**\n * Snapshot every form on a Vue app for SSR. Call from your server\n * entry after rendering the app:\n *\n * ```ts\n * import { renderToString } from '@vue/server-renderer'\n * import { renderAttaformState, escapeForInlineScript } from 'attaform'\n *\n * const html = await renderToString(app)\n * const state = renderAttaformState(app)\n * const payload = escapeForInlineScript(JSON.stringify(state))\n *\n * return `\n * ${html}\n * <script>window.__ATTAFORM_STATE__ = ${payload}</script>\n * `\n * ```\n *\n * Pair with `hydrateAttaformState` on the client to restore the\n * forms in their server-rendered state. Nuxt users don't need this —\n * `attaform/nuxt` wires SSR automatically.\n */\nexport function renderAttaformState(app: App): SerializedAttaformState {\n const registry = getRegistryFromApp(app)\n const forms: Array<readonly [FormKey, SerializedFormData]> = []\n for (const [key, state] of registry.forms) {\n // Skip the blank field when the set is empty so the\n // wire payload stays minimal for forms that don't use it. The\n // optional shape on the consuming side handles the absence\n // cleanly (defaults to \"no blank paths\"). PathKey → dotted at\n // the boundary so the wire shape matches the rest of the\n // public path notation.\n const transientList: string[] = []\n for (const pk of state.blankPaths) {\n const d = pathKeyToDotted(pk as PathKey)\n if (d !== null) transientList.push(d)\n }\n forms.push([\n key,\n {\n form: state.form.value,\n schemaErrors: Array.from(state.schemaErrors.entries()),\n userErrors: Array.from(state.userErrors.entries()),\n fields: Array.from(state.fields.entries()),\n ...(transientList.length > 0 ? { blankPaths: transientList } : {}),\n },\n ])\n }\n return { forms }\n}\n\n/**\n * Restore forms from a server-rendered snapshot on the client. Call\n * from your client entry before mounting:\n *\n * ```ts\n * import { createApp } from 'vue'\n * import { createAttaform, hydrateAttaformState } from 'attaform'\n *\n * const app = createApp(App).use(createAttaform())\n * hydrateAttaformState(app, window.__ATTAFORM_STATE__)\n * app.mount('#app')\n * ```\n *\n * The next `useForm({ key })` call for each serialised form picks up\n * the snapshot transparently — no further action is required.\n */\nexport function hydrateAttaformState(app: App, payload: SerializedAttaformState): void {\n const registry = getRegistryFromApp(app)\n for (const [key, data] of payload.forms) {\n registry.pendingHydration.set(key, data)\n }\n}\n","/**\n * Shared building blocks for Attaform's two devtools surfaces — the Vue\n * DevTools (Chrome-extension) inspector wired up in `./devtools.ts`, and\n * the Nuxt DevTools (overlay) panel wired up via `../../nuxt.ts` +\n * `../pages/_attaform_devtools.vue`.\n *\n * Centralizing the redaction policy and the window-bridge contract here\n * keeps both surfaces aligned: a future tightening of the sensitive-name\n * heuristic, or a new field added to the bridge, lands in one file.\n */\nimport type { AttaformRegistry } from './registry'\nimport type { Segment } from './paths'\n\nexport const REDACTED = '[redacted]'\n\n/**\n * Walk `value` and replace any leaf whose enclosing path matches the\n * sensitive-name heuristic with the string `'[redacted]'`. Returns a\n * new tree (no mutation of the input). Object keys + array indices are\n * preserved; only the leaf payloads change.\n *\n * Applied to BOTH devtools surfaces' Form-value rendering AND every\n * timeline event payload — leaks via either surface are treatable as\n * \"any developer with the panel open during user testing can read a\n * customer's password,\" which is exactly the failure mode the\n * sensitive-name guard exists to prevent on the storage side.\n *\n * Leaves whose path doesn't match a pattern pass through untouched.\n * `acknowledgeSensitive: true` on persistence does NOT bypass this — if\n * the consumer opted into persisting the value, they still shouldn't\n * see it in DevTools timelines that grow unbounded.\n *\n * Implementation note: tracks an `inSensitiveSubtree` flag through the\n * recursion instead of allocating a fresh path array per node + calling\n * `isSensitivePath` per leaf. Once any ancestor segment matches the\n * heuristic, the flag stays set for every descendant — the leaf simply\n * returns `REDACTED` without re-scanning the path. For a 100-leaf form:\n * ~100 path allocations + ~100 full-path regex sweeps → 0 path\n * allocations + ~100 single-segment regex sweeps, with whole-subtree\n * short-circuit when sensitive ancestors are found early.\n */\nexport function redactSensitiveLeaves(\n value: unknown,\n matchSensitive: (segment: Segment) => boolean\n): unknown {\n return redactImpl(value, false, matchSensitive)\n}\n\nfunction redactImpl(\n value: unknown,\n inSensitiveSubtree: boolean,\n matchSensitive: (segment: Segment) => boolean\n): unknown {\n if (value === null || value === undefined) return value\n if (typeof value !== 'object') {\n return inSensitiveSubtree ? REDACTED : value\n }\n if (Array.isArray(value)) {\n // Numeric segments never match the sensitive-name heuristic\n // (segmentMatchesSensitive rejects non-string segments), so the\n // flag passes through unchanged when descending into arrays.\n return value.map((item) => redactImpl(item, inSensitiveSubtree, matchSensitive))\n }\n // Non-plain object (Map / Set / Date / class instance) — redact\n // wholesale if we're already in a sensitive subtree; otherwise pass\n // through. DevTools rendering of these is already heuristic, so we\n // don't try to descend into them.\n //\n // Use `Object.prototype.toString.call(value)` rather than a\n // `getPrototypeOf` comparison because `Object.prototype` is\n // realm-scoped — the Nuxt DevTools overlay panel runs in an iframe\n // whose Vue runtime is separate from the host's, so the host's\n // reactive proxies have a prototype that doesn't equal the panel's\n // `Object.prototype`. The `toString` tag check is realm-aware via\n // `@@toStringTag` and returns `'[object Object]'` for plain objects\n // (including Vue reactive proxies of plain objects) regardless of\n // which iframe they were created in.\n if (Object.prototype.toString.call(value) !== '[object Object]') {\n return inSensitiveSubtree ? REDACTED : value\n }\n const out: Record<string, unknown> = {}\n for (const key of Object.keys(value as Record<string, unknown>)) {\n const childSensitive = inSensitiveSubtree || matchSensitive(key)\n out[key] = redactImpl((value as Record<string, unknown>)[key], childSensitive, matchSensitive)\n }\n return out\n}\n\n/**\n * Property key on `window` that the Nuxt-side dev plugin attaches the\n * bridge object to. The iframe-mounted overlay panel reads\n * `window.parent[DEVTOOLS_WINDOW_KEY]` to reach the host app's registry.\n *\n * Underscored + namespaced to make accidental collision with consumer\n * globals vanishingly unlikely. Stable across versions — bumping it\n * would silently disconnect older library builds from newer overlay\n * panels in the same browser tab during a library upgrade.\n */\nexport const DEVTOOLS_WINDOW_KEY = '__attaform_devtools__'\n\n/**\n * Shape of the object the host plugin attaches to `window` in dev mode.\n * The iframe overlay panel reads this to discover the live registry and\n * render its forms.\n *\n * Single-registry assumption: the latest `createAttaform()` install\n * wins. Multi-app pages (rare; typically only seen in micro-frontend\n * setups) will only see one app's forms in the panel. Documented but\n * not actively supported — the alternative (a Set of registries with\n * union-rendering) is a future call if a real consumer hits it.\n */\nexport interface AttaformDevtoolsBridge {\n registry: AttaformRegistry\n /**\n * The library version, surfaced in the panel's footer for support /\n * bug-report context. Read from `package.json` at host-plugin init.\n */\n version: string\n}\n\ndeclare global {\n interface Window {\n [DEVTOOLS_WINDOW_KEY]?: AttaformDevtoolsBridge\n }\n}\n"],"names":[],"mappings":";;AAuCO,SAAS,oBAAoB,GAAA,EAAmC;AACrE,EAAA,MAAM,QAAA,GAAW,mBAAmB,GAAG,CAAA;AACvC,EAAA,MAAM,QAAuD,EAAC;AAC9D,EAAA,KAAA,MAAW,CAAC,GAAA,EAAK,KAAK,CAAA,IAAK,SAAS,KAAA,EAAO;AAOzC,IAAA,MAAM,gBAA0B,EAAC;AACjC,IAAA,KAAA,MAAW,EAAA,IAAM,MAAM,UAAA,EAAY;AACjC,MAAA,MAAM,CAAA,GAAI,gBAAgB,EAAa,CAAA;AACvC,MAAA,IAAI,CAAA,KAAM,IAAA,EAAM,aAAA,CAAc,IAAA,CAAK,CAAC,CAAA;AAAA,IACtC;AACA,IAAA,KAAA,CAAM,IAAA,CAAK;AAAA,MACT,GAAA;AAAA,MACA;AAAA,QACE,IAAA,EAAM,MAAM,IAAA,CAAK,KAAA;AAAA,QACjB,cAAc,KAAA,CAAM,IAAA,CAAK,KAAA,CAAM,YAAA,CAAa,SAAS,CAAA;AAAA,QACrD,YAAY,KAAA,CAAM,IAAA,CAAK,KAAA,CAAM,UAAA,CAAW,SAAS,CAAA;AAAA,QACjD,QAAQ,KAAA,CAAM,IAAA,CAAK,KAAA,CAAM,MAAA,CAAO,SAAS,CAAA;AAAA,QACzC,GAAI,cAAc,MAAA,GAAS,CAAA,GAAI,EAAE,UAAA,EAAY,aAAA,KAAkB;AAAC;AAClE,KACD,CAAA;AAAA,EACH;AACA,EAAA,OAAO,EAAE,KAAA,EAAM;AACjB;AAkBO,SAAS,oBAAA,CAAqB,KAAU,OAAA,EAAwC;AACrF,EAAA,MAAM,QAAA,GAAW,mBAAmB,GAAG,CAAA;AACvC,EAAA,KAAA,MAAW,CAAC,GAAA,EAAK,IAAI,CAAA,IAAK,QAAQ,KAAA,EAAO;AACvC,IAAA,QAAA,CAAS,gBAAA,CAAiB,GAAA,CAAI,GAAA,EAAK,IAAI,CAAA;AAAA,EACzC;AACF;;AC5EO,MAAM,QAAA,GAAW;AA4BjB,SAAS,qBAAA,CACd,OACA,cAAA,EACS;AACT,EAAA,OAAO,UAAA,CAAW,KAAA,EAAO,KAAA,EAAO,cAAc,CAAA;AAChD;AAEA,SAAS,UAAA,CACP,KAAA,EACA,kBAAA,EACA,cAAA,EACS;AACT,EAAA,IAAI,KAAA,KAAU,IAAA,IAAQ,KAAA,KAAU,MAAA,EAAW,OAAO,KAAA;AAClD,EAAA,IAAI,OAAO,UAAU,QAAA,EAAU;AAC7B,IAAA,OAAO,qBAAqB,QAAA,GAAW,KAAA;AAAA,EACzC;AACA,EAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,EAAG;AAIxB,IAAA,OAAO,KAAA,CAAM,IAAI,CAAC,IAAA,KAAS,WAAW,IAAA,EAAM,kBAAA,EAAoB,cAAc,CAAC,CAAA;AAAA,EACjF;AAeA,EAAA,IAAI,OAAO,SAAA,CAAU,QAAA,CAAS,IAAA,CAAK,KAAK,MAAM,iBAAA,EAAmB;AAC/D,IAAA,OAAO,qBAAqB,QAAA,GAAW,KAAA;AAAA,EACzC;AACA,EAAA,MAAM,MAA+B,EAAC;AACtC,EAAA,KAAA,MAAW,GAAA,IAAO,MAAA,CAAO,IAAA,CAAK,KAAgC,CAAA,EAAG;AAC/D,IAAA,MAAM,cAAA,GAAiB,kBAAA,IAAsB,cAAA,CAAe,GAAG,CAAA;AAC/D,IAAA,GAAA,CAAI,GAAG,CAAA,GAAI,UAAA,CAAY,MAAkC,GAAG,CAAA,EAAG,gBAAgB,cAAc,CAAA;AAAA,EAC/F;AACA,EAAA,OAAO,GAAA;AACT;AAYO,MAAM,mBAAA,GAAsB;;;;"}