attaform 0.19.0 → 0.20.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 (110) hide show
  1. package/dist/chunks/devtools.cjs +1 -1
  2. package/dist/chunks/devtools.mjs +1 -1
  3. package/dist/chunks/indexeddb.cjs +1 -1
  4. package/dist/chunks/indexeddb.mjs +1 -1
  5. package/dist/chunks/local-storage.cjs +1 -1
  6. package/dist/chunks/local-storage.mjs +1 -1
  7. package/dist/chunks/session-storage.cjs +1 -1
  8. package/dist/chunks/session-storage.mjs +1 -1
  9. package/dist/index.cjs +3 -6
  10. package/dist/index.cjs.map +1 -1
  11. package/dist/index.d.cts +14 -40
  12. package/dist/index.d.mts +14 -40
  13. package/dist/index.d.ts +14 -40
  14. package/dist/index.mjs +5 -5
  15. package/dist/nuxt.d.cts +1 -1
  16. package/dist/nuxt.d.mts +1 -1
  17. package/dist/nuxt.d.ts +1 -1
  18. package/dist/runtime/components/AttaformDevtoolsPanel.vue +2 -2
  19. package/dist/runtime/components/DevtoolsValueTree.d.vue.ts +1 -3
  20. package/dist/runtime/components/DevtoolsValueTree.vue.d.ts +1 -3
  21. package/dist/runtime/plugins/attaform.cjs +2 -2
  22. package/dist/runtime/plugins/attaform.mjs +2 -2
  23. package/dist/shared/{attaform.CrpjyXdO.mjs → attaform.BKozEdTr.mjs} +275 -266
  24. package/dist/shared/attaform.BKozEdTr.mjs.map +1 -0
  25. package/dist/shared/{attaform.Bubm_slq.cjs → attaform.BM6YD9kZ.cjs} +212 -269
  26. package/dist/shared/attaform.BM6YD9kZ.cjs.map +1 -0
  27. package/dist/shared/{attaform.CoxJ8Qm8.cjs → attaform.BPxsYtTe.cjs} +2 -26
  28. package/dist/shared/attaform.BPxsYtTe.cjs.map +1 -0
  29. package/dist/shared/{attaform.BqEfHpVB.cjs → attaform.BPy-4qRx.cjs} +275 -268
  30. package/dist/shared/attaform.BPy-4qRx.cjs.map +1 -0
  31. package/dist/shared/attaform.BWgAFnsj.mjs +770 -0
  32. package/dist/shared/attaform.BWgAFnsj.mjs.map +1 -0
  33. package/dist/shared/{attaform.BTpuvGec.d.ts → attaform.Bh3ACtts.d.ts} +109 -101
  34. package/dist/shared/{attaform.CXpzmj38.mjs → attaform.BupwXkj_.mjs} +213 -270
  35. package/dist/shared/attaform.BupwXkj_.mjs.map +1 -0
  36. package/dist/shared/{attaform.JBx8cfMA.cjs → attaform.CIn4bMsD.cjs} +263 -799
  37. package/dist/shared/attaform.CIn4bMsD.cjs.map +1 -0
  38. package/dist/shared/{attaform.BTi-PsHr.mjs → attaform.CKFbKFb6.mjs} +1818 -1472
  39. package/dist/shared/attaform.CKFbKFb6.mjs.map +1 -0
  40. package/dist/shared/{attaform.ePUcKxId.d.cts → attaform.D5-1XGQU.d.cts} +109 -101
  41. package/dist/shared/{attaform.a3uBo-gw.mjs → attaform.DEBvCjeH.mjs} +257 -793
  42. package/dist/shared/attaform.DEBvCjeH.mjs.map +1 -0
  43. package/dist/shared/{attaform.C1msmO2v.cjs → attaform.DL4CQ-oW.cjs} +1823 -1477
  44. package/dist/shared/attaform.DL4CQ-oW.cjs.map +1 -0
  45. package/dist/shared/{attaform.D4I63aBV.d.ts → attaform.DSD85fHb.d.cts} +1 -19
  46. package/dist/shared/{attaform.CBjmobqk.d.cts → attaform.DSD85fHb.d.mts} +1 -19
  47. package/dist/shared/{attaform.DXYHL99q.d.mts → attaform.DSD85fHb.d.ts} +1 -19
  48. package/dist/shared/{attaform.B7rzpK1U.d.cts → attaform.DkA5J8NW.d.cts} +1 -17
  49. package/dist/shared/{attaform.B7rzpK1U.d.mts → attaform.DkA5J8NW.d.mts} +1 -17
  50. package/dist/shared/{attaform.B7rzpK1U.d.ts → attaform.DkA5J8NW.d.ts} +1 -17
  51. package/dist/shared/{attaform.CJ-e9gYI.d.ts → attaform.Dl5kDY-A.d.ts} +1 -1
  52. package/dist/shared/attaform.Dmb6itxC.cjs +781 -0
  53. package/dist/shared/attaform.Dmb6itxC.cjs.map +1 -0
  54. package/dist/shared/{attaform.CRNA0vrd.d.mts → attaform.DoKXru-a.d.mts} +1 -1
  55. package/dist/shared/attaform.DvA-CJJW.mjs +1876 -0
  56. package/dist/shared/attaform.DvA-CJJW.mjs.map +1 -0
  57. package/dist/shared/{attaform.BtBmfLQN.d.mts → attaform.EMzJcQci.d.mts} +109 -101
  58. package/dist/shared/attaform.EZG6fOFb.mjs +35 -0
  59. package/dist/shared/attaform.EZG6fOFb.mjs.map +1 -0
  60. package/dist/shared/{attaform.QvygsFGh.d.cts → attaform.GbDo_lJi.d.cts} +1 -1
  61. package/dist/shared/{attaform.C0uGZQ4M.d.ts → attaform.SfhU0OEY.d.cts} +134 -30
  62. package/dist/shared/{attaform.C0uGZQ4M.d.cts → attaform.SfhU0OEY.d.mts} +134 -30
  63. package/dist/shared/{attaform.C0uGZQ4M.d.mts → attaform.SfhU0OEY.d.ts} +134 -30
  64. package/dist/shared/attaform.jgzuNZVC.cjs +1882 -0
  65. package/dist/shared/attaform.jgzuNZVC.cjs.map +1 -0
  66. package/dist/transforms.cjs +2 -2
  67. package/dist/transforms.d.cts +22 -13
  68. package/dist/transforms.d.mts +22 -13
  69. package/dist/transforms.d.ts +22 -13
  70. package/dist/transforms.mjs +1 -1
  71. package/dist/vite.cjs +8 -7
  72. package/dist/vite.cjs.map +1 -1
  73. package/dist/vite.mjs +8 -7
  74. package/dist/vite.mjs.map +1 -1
  75. package/dist/zod-v3.cjs +3 -3
  76. package/dist/zod-v3.d.cts +32 -6
  77. package/dist/zod-v3.d.mts +32 -6
  78. package/dist/zod-v3.d.ts +32 -6
  79. package/dist/zod-v3.mjs +3 -3
  80. package/dist/zod-v4.cjs +3 -3
  81. package/dist/zod-v4.d.cts +12 -8
  82. package/dist/zod-v4.d.mts +12 -8
  83. package/dist/zod-v4.d.ts +12 -8
  84. package/dist/zod-v4.mjs +3 -3
  85. package/dist/zod.cjs +8 -8
  86. package/dist/zod.cjs.map +1 -1
  87. package/dist/zod.d.cts +6 -6
  88. package/dist/zod.d.mts +6 -6
  89. package/dist/zod.d.ts +6 -6
  90. package/dist/zod.mjs +6 -6
  91. package/package.json +1 -1
  92. package/dist/shared/attaform.BTi-PsHr.mjs.map +0 -1
  93. package/dist/shared/attaform.BqEfHpVB.cjs.map +0 -1
  94. package/dist/shared/attaform.Bubm_slq.cjs.map +0 -1
  95. package/dist/shared/attaform.C1msmO2v.cjs.map +0 -1
  96. package/dist/shared/attaform.C8CyvYa_.cjs +0 -36
  97. package/dist/shared/attaform.C8CyvYa_.cjs.map +0 -1
  98. package/dist/shared/attaform.CXpzmj38.mjs.map +0 -1
  99. package/dist/shared/attaform.Cghpuav8.mjs +0 -57
  100. package/dist/shared/attaform.Cghpuav8.mjs.map +0 -1
  101. package/dist/shared/attaform.CiMqJHDm.mjs +0 -1594
  102. package/dist/shared/attaform.CiMqJHDm.mjs.map +0 -1
  103. package/dist/shared/attaform.CoxJ8Qm8.cjs.map +0 -1
  104. package/dist/shared/attaform.CrpjyXdO.mjs.map +0 -1
  105. package/dist/shared/attaform.D13GMFgK.mjs +0 -32
  106. package/dist/shared/attaform.D13GMFgK.mjs.map +0 -1
  107. package/dist/shared/attaform.JBx8cfMA.cjs.map +0 -1
  108. package/dist/shared/attaform.OznWyOPy.cjs +0 -1600
  109. package/dist/shared/attaform.OznWyOPy.cjs.map +0 -1
  110. package/dist/shared/attaform.a3uBo-gw.mjs.map +0 -1
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const paths = require('../shared/attaform.BqEfHpVB.cjs');
3
+ const paths = require('../shared/attaform.BPy-4qRx.cjs');
4
4
 
5
5
  const INSPECTOR_ID = "attaform";
6
6
  const TIMELINE_LAYER_ID = "attaform:events";
@@ -1,4 +1,4 @@
1
- import { i as canonicalizePath } from '../shared/attaform.CrpjyXdO.mjs';
1
+ import { j as canonicalizePath } from '../shared/attaform.BKozEdTr.mjs';
2
2
 
3
3
  const INSPECTOR_ID = "attaform";
4
4
  const TIMELINE_LAYER_ID = "attaform:events";
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const paths = require('../shared/attaform.BqEfHpVB.cjs');
3
+ const paths = require('../shared/attaform.BPy-4qRx.cjs');
4
4
 
5
5
  const DB_NAME = "attaform";
6
6
  const STORE_NAME = "kv";
@@ -1,4 +1,4 @@
1
- import { _ as __DEV__ } from '../shared/attaform.CrpjyXdO.mjs';
1
+ import { _ as __DEV__ } from '../shared/attaform.BKozEdTr.mjs';
2
2
 
3
3
  const DB_NAME = "attaform";
4
4
  const STORE_NAME = "kv";
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const paths = require('../shared/attaform.BqEfHpVB.cjs');
3
+ const paths = require('../shared/attaform.BPy-4qRx.cjs');
4
4
 
5
5
  function createLocalStorageAdapter() {
6
6
  const available = typeof localStorage !== "undefined";
@@ -1,4 +1,4 @@
1
- import { _ as __DEV__ } from '../shared/attaform.CrpjyXdO.mjs';
1
+ import { _ as __DEV__ } from '../shared/attaform.BKozEdTr.mjs';
2
2
 
3
3
  function createLocalStorageAdapter() {
4
4
  const available = typeof localStorage !== "undefined";
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const paths = require('../shared/attaform.BqEfHpVB.cjs');
3
+ const paths = require('../shared/attaform.BPy-4qRx.cjs');
4
4
 
5
5
  function createSessionStorageAdapter() {
6
6
  const available = typeof sessionStorage !== "undefined";
@@ -1,4 +1,4 @@
1
- import { _ as __DEV__ } from '../shared/attaform.CrpjyXdO.mjs';
1
+ import { _ as __DEV__ } from '../shared/attaform.BKozEdTr.mjs';
2
2
 
3
3
  function createSessionStorageAdapter() {
4
4
  const available = typeof sessionStorage !== "undefined";
package/dist/index.cjs CHANGED
@@ -1,8 +1,8 @@
1
1
  'use strict';
2
2
 
3
- const paths = require('./shared/attaform.BqEfHpVB.cjs');
4
- const devtoolsShared = require('./shared/attaform.CoxJ8Qm8.cjs');
5
- const injectWizard = require('./shared/attaform.C1msmO2v.cjs');
3
+ const paths = require('./shared/attaform.BPy-4qRx.cjs');
4
+ const devtoolsShared = require('./shared/attaform.BPxsYtTe.cjs');
5
+ const injectWizard = require('./shared/attaform.DL4CQ-oW.cjs');
6
6
 
7
7
  function escapeForInlineScript(json) {
8
8
  return json.replace(/[<>&\u2028\u2029]/g, (char) => {
@@ -164,7 +164,6 @@ exports.ROOT_PATH = paths.ROOT_PATH;
164
164
  exports.ROOT_PATH_KEY = paths.ROOT_PATH_KEY;
165
165
  exports.RegistryNotInstalledError = paths.RegistryNotInstalledError;
166
166
  exports.ReservedFormKeyError = paths.ReservedFormKeyError;
167
- exports.SensitivePersistFieldError = paths.SensitivePersistFieldError;
168
167
  exports.SubmitErrorHandlerError = paths.SubmitErrorHandlerError;
169
168
  exports.assignKey = paths.assignKey;
170
169
  exports.canonicalizePath = paths.canonicalizePath;
@@ -179,9 +178,7 @@ exports.useRegister = paths.useRegister;
179
178
  exports.useRegistry = paths.useRegistry;
180
179
  exports.vRegister = paths.vRegister;
181
180
  exports.DEVTOOLS_WINDOW_KEY = devtoolsShared.DEVTOOLS_WINDOW_KEY;
182
- exports.REDACTED = devtoolsShared.REDACTED;
183
181
  exports.hydrateAttaformState = devtoolsShared.hydrateAttaformState;
184
- exports.redactSensitiveLeaves = devtoolsShared.redactSensitiveLeaves;
185
182
  exports.renderAttaformState = devtoolsShared.renderAttaformState;
186
183
  exports.AttaformErrorCode = injectWizard.AttaformErrorCode;
187
184
  exports.defaultCoercionRules = injectWizard.defaultCoercionRules;
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":["../src/runtime/core/serialize-script.ts","../src/runtime/core/parse-api-errors.ts"],"sourcesContent":["/**\n * Escape a JSON string so it's safe to embed inside an inline\n * `<script>` tag during SSR. Plain `JSON.stringify` is not safe — a\n * form value containing the literal substring `</script>` would\n * break out of the script tag.\n *\n * ```ts\n * const payload = escapeForInlineScript(JSON.stringify(renderAttaformState(app)))\n * // `<script>window.__ATTAFORM_STATE__ = ${payload}</script>` is safe.\n * ```\n *\n * Output remains valid JSON — `JSON.parse` round-trips back to the\n * original value on the client.\n */\nexport function escapeForInlineScript(json: string): string {\n return json.replace(/[<>&\\u2028\\u2029]/g, (char) => {\n switch (char) {\n case '<':\n return '\\\\u003c'\n case '>':\n return '\\\\u003e'\n case '&':\n return '\\\\u0026'\n case '\\u2028':\n return '\\\\u2028'\n case '\\u2029':\n return '\\\\u2029'\n default:\n return char\n }\n })\n}\n","import type {\n ApiErrorDetails,\n ApiErrorEntry,\n ApiErrorEnvelope,\n FormKey,\n ValidationError,\n} from '../types/types-api'\nimport { normalizeNumericOption } from './defaults'\nimport { InvalidPathError } from './errors'\nimport { canonicalizePath } from './paths'\n\n/**\n * Result of `parseApiErrors`. Branch on `ok` to handle the two cases:\n *\n * ```ts\n * const result = parseApiErrors(payload, { formKey: form.key })\n * if (result.ok) {\n * form.setFieldErrors(result.errors)\n * } else {\n * console.warn('Bad error payload:', result.rejected)\n * }\n * ```\n *\n * `ok: true` means the payload was recognised — `errors` may still be\n * empty if the payload was valid but had no actual errors.\n * `ok: false` means the payload didn't match a known shape; `rejected`\n * carries a one-line description of why.\n */\nexport type ParseApiErrorsResult = {\n /** `true` when the payload was recognised; `false` when the shape was unfamiliar. */\n readonly ok: boolean\n /** Errors extracted from the payload. May be empty even when `ok: true`. */\n readonly errors: ValidationError[]\n /** When `ok: false`, a one-line description of why the payload was rejected. */\n readonly rejected?: string\n}\n\n/**\n * Options for `parseApiErrors`. The size caps protect against\n * misbehaving or hostile servers — exceeding any cap causes the\n * parser to reject the payload wholesale rather than partially apply.\n */\nexport type ParseApiErrorsOptions = {\n /**\n * The form's identifier — pass `form.key`. Stamped on every\n * produced `ValidationError` so errors route to the right form.\n */\n readonly formKey: FormKey\n /**\n * Code stamped on `ValidationError`s synthesized from bare-string\n * entries (the Rails / DRF / Laravel `{ field: [\"msg\"] }` shape).\n * Default `'api:unknown'`. Pick something more specific\n * (`'api:server-validation'`, `'myapp:legacy'`, …) when you know\n * the source.\n *\n * Structured `{ message, code }` entries forward their `code`\n * verbatim and ignore this option.\n */\n readonly defaultCode?: string\n /**\n * Maximum number of distinct keys to accept. Default `1000`.\n * Raise for trusted backends that legitimately produce more.\n */\n readonly maxEntries?: number\n /**\n * Maximum number of path segments per key. Default `32`. Keys\n * deeper than this are dropped (the rest of the payload still\n * applies if it stays under the other caps).\n */\n readonly maxPathDepth?: number\n /**\n * Maximum total path segments summed across every accepted key.\n * Default `10000`. Bounds the worst-case traversal cost.\n */\n readonly maxTotalSegments?: number\n}\n\n/**\n * Default size caps + default fallback code used by `parseApiErrors`.\n * Conservative; pass larger values (or a more specific code) via the\n * options bag for trusted-backend integrations.\n */\nexport const PARSE_API_ERRORS_DEFAULTS = {\n maxEntries: 1000,\n maxPathDepth: 32,\n maxTotalSegments: 10000,\n defaultCode: 'api:unknown',\n} as const\n\n/**\n * Normalise a server-side validation error payload into\n * `ValidationError[]`. Pair with `form.setFieldErrors` /\n * `form.addFieldErrors` to surface server errors on the form:\n *\n * ```ts\n * const response = await fetch('/api/signup', { … })\n * if (!response.ok) {\n * const payload = await response.json()\n * const result = parseApiErrors(payload, { formKey: form.key })\n * if (result.ok) form.setFieldErrors(result.errors)\n * }\n * ```\n *\n * Recognised payload shapes:\n *\n * - Wrapped envelope:\n * `{ error: { details: { email: { message: 'taken', code: 'api:duplicate-email' } } } }`\n * - Unwrapped envelope:\n * `{ details: { email: { message: 'taken', code: 'api:duplicate-email' } } }`\n * - Raw details record:\n * `{ email: { message: 'taken', code: 'api:duplicate-email' } }`\n * - **Bare-string Rails / DRF / Laravel shape:**\n * `{ email: ['Email already taken.'], username: 'too short' }`\n * - `null` / `undefined` — returns `{ ok: true, errors: [] }`\n *\n * Two entry shapes are accepted:\n *\n * 1. **Structured** — `{ message: string, code: string }`. The `code`\n * is forwarded verbatim onto the produced `ValidationError`.\n * 2. **Bare-string** — a plain string. Synthesized into\n * `{ message: <string>, code: <defaultCode> }` where `defaultCode`\n * comes from `options.defaultCode` (default `'api:unknown'`).\n * Useful for the Rails / Django REST Framework / FastAPI / Laravel\n * JSON shape that doesn't carry a per-field code.\n *\n * Each detail key's value can be a single entry, an array, or a mix\n * of structured and bare-string entries; arrays expand into one\n * `ValidationError` per entry. Pick a prefix on the server (`api:`,\n * `auth:`, etc.) and stay consistent so error renderers can branch\n * on `code` — or rely on `defaultCode` when the wire shape is\n * message-only.\n *\n * Dotted keys (`\"address.line1\"`) are split into structured paths\n * automatically. Use a custom server response shape outside these\n * patterns? Build the `ValidationError[]` array yourself and pass\n * it to `setFieldErrors` directly — `parseApiErrors` is just a\n * convenience for the common shapes.\n */\nexport function parseApiErrors(\n payload: ApiErrorEnvelope | ApiErrorDetails | null | undefined | unknown,\n options: ParseApiErrorsOptions\n): ParseApiErrorsResult {\n // Sanitise the caps. Comparison gates (`>` against the count /\n // depth) yield `false` for `NaN`, so without sanitisation a\n // hostile or malformed `NaN` cap would let pathological payloads\n // run unbounded. `Infinity` would do the same. Negatives and\n // non-integers would discard legitimate entries. Falls back to\n // the library default on garbage.\n const maxEntries = normalizeNumericOption({\n value: options.maxEntries ?? PARSE_API_ERRORS_DEFAULTS.maxEntries,\n source: 'parseApiErrors.maxEntries',\n allowInfinity: false,\n min: 0,\n defaultValue: PARSE_API_ERRORS_DEFAULTS.maxEntries,\n })\n const maxPathDepth = normalizeNumericOption({\n value: options.maxPathDepth ?? PARSE_API_ERRORS_DEFAULTS.maxPathDepth,\n source: 'parseApiErrors.maxPathDepth',\n allowInfinity: false,\n min: 0,\n defaultValue: PARSE_API_ERRORS_DEFAULTS.maxPathDepth,\n })\n const maxTotalSegments = normalizeNumericOption({\n value: options.maxTotalSegments ?? PARSE_API_ERRORS_DEFAULTS.maxTotalSegments,\n source: 'parseApiErrors.maxTotalSegments',\n allowInfinity: false,\n min: 0,\n defaultValue: PARSE_API_ERRORS_DEFAULTS.maxTotalSegments,\n })\n const defaultCode = options.defaultCode ?? PARSE_API_ERRORS_DEFAULTS.defaultCode\n\n if (payload === null || payload === undefined) {\n return { ok: true, errors: [] }\n }\n if (typeof payload !== 'object') {\n return { ok: false, errors: [], rejected: `payload was ${typeof payload}, expected object` }\n }\n\n const extraction = extractDetails(payload as Record<string, unknown>)\n if (!extraction.ok) {\n return { ok: false, errors: [], rejected: extraction.reason }\n }\n\n const { details } = extraction\n const entryCount = Object.keys(details).length\n // Enforce the guardrails before we spend time walking the payload.\n // Rejecting wholesale (not partial-applying) keeps the failure visible\n // so consumers can tune the caps or investigate the server payload.\n if (entryCount > maxEntries) {\n return {\n ok: false,\n errors: [],\n rejected: `payload has ${entryCount} entries, exceeds maxEntries=${maxEntries}`,\n }\n }\n\n const errors: ValidationError[] = []\n let totalSegments = 0\n for (const [key, value] of Object.entries(details)) {\n const entryList: ReadonlyArray<string | ApiErrorEntry> = Array.isArray(value) ? value : [value]\n // `canonicalizePath` throws `InvalidPathError` for dotted strings with\n // empty segments (e.g. `'. '`, `'a..b'`). A misbehaving server can\n // genuinely emit such a key; the hydrator is a normaliser, not a\n // validator, so we drop offending keys rather than let the exception\n // escape. Well-formed keys continue as normal.\n let segments: readonly (string | number)[]\n try {\n segments = canonicalizePath(key).segments\n } catch (err) {\n if (err instanceof InvalidPathError) continue\n throw err\n }\n // Per-path depth cap. We drop the offending key (rather than\n // rejecting the whole payload) because a single stray deep path\n // in an otherwise legitimate error set is still worth surfacing\n // the rest. Consumers who want strict rejection can post-filter\n // on `result.errors.length < details entryCount`.\n if (segments.length > maxPathDepth) continue\n // Total-segment cap. Enforced wholesale (not per-key) so a payload\n // that passes the per-key gate but stacks into a pathological\n // total still fails visibly. Mirrors `maxEntries` strictness.\n totalSegments += segments.length\n if (totalSegments > maxTotalSegments) {\n return {\n ok: false,\n errors: [],\n rejected: `payload total path segments exceeds maxTotalSegments=${maxTotalSegments}`,\n }\n }\n for (const entry of entryList) {\n // Bare-string entries (Rails / DRF / Laravel shape) synthesize a\n // `code` from `options.defaultCode`; structured `{ message, code }`\n // entries forward `code` verbatim. Empty messages drop silently\n // (`{ message: '' }` or `''`) — same recoverable-malformed-server\n // policy as before.\n const message = typeof entry === 'string' ? entry : entry.message\n const code = typeof entry === 'string' ? defaultCode : entry.code\n if (message.length === 0) continue\n errors.push({\n message,\n path: Array.from(segments),\n formKey: options.formKey,\n code,\n })\n }\n }\n return { ok: true, errors }\n}\n\ntype ExtractResult = { ok: true; details: ApiErrorDetails } | { ok: false; reason: string }\n\nfunction extractDetails(payload: Record<string, unknown>): ExtractResult {\n const wrappedError = payload['error']\n if (wrappedError !== null && wrappedError !== undefined && typeof wrappedError === 'object') {\n const inner = (wrappedError as { details?: unknown }).details\n if (inner === undefined) {\n // A wrapped envelope without details is considered \"no errors\" — valid shape.\n return { ok: true, details: {} }\n }\n if (isDetailsRecord(inner)) return { ok: true, details: inner }\n return {\n ok: false,\n reason: 'error.details entries must be strings or { message, code } objects',\n }\n }\n\n // `{ error: 'oops' }` / `{ error: 42 }` is a malformed wrapped envelope —\n // the server meant an error object but sent a scalar. Without this guard\n // the payload would fall through to the raw-details branch below, where\n // `{ error: 'oops' }` satisfies `isDetailsRecord` and silently produces\n // a phantom `ValidationError` at path `['error']`.\n if (wrappedError !== null && wrappedError !== undefined && typeof wrappedError !== 'object') {\n return {\n ok: false,\n reason: `payload.error was ${typeof wrappedError}, expected an object with { details }`,\n }\n }\n\n if ('details' in payload) {\n const inner = payload['details']\n if (inner === undefined) return { ok: true, details: {} }\n if (isDetailsRecord(inner)) return { ok: true, details: inner }\n return { ok: false, reason: 'details entries must be strings or { message, code } objects' }\n }\n\n if (isDetailsRecord(payload)) return { ok: true, details: payload }\n\n // Heuristic: if the payload has keys but none of them look like details,\n // it's probably a completely different shape. Reject.\n if (Object.keys(payload).length === 0) return { ok: true, details: {} }\n return { ok: false, reason: 'unrecognised payload shape' }\n}\n\nfunction isStructuredEntry(value: unknown): value is ApiErrorEntry {\n if (value === null || typeof value !== 'object' || Array.isArray(value)) return false\n const obj = value as { message?: unknown; code?: unknown }\n return typeof obj.message === 'string' && typeof obj.code === 'string'\n}\n\n/**\n * Accepts either a structured `{ message, code }` entry OR a bare\n * string. Bare strings synthesize a `code` at parse time\n * (`options.defaultCode`) and are useful for the Rails / Django REST\n * Framework / Laravel JSON shape that doesn't carry a per-field code.\n */\nfunction isAcceptedEntry(value: unknown): value is string | ApiErrorEntry {\n return typeof value === 'string' || isStructuredEntry(value)\n}\n\n/**\n * A record is a \"details\" record when every value is either an\n * accepted entry or an array of accepted entries (mixing structured +\n * bare-string in the same array is fine; the parser normalises per\n * entry). Half-structured objects (e.g. `{ message: 'x' }` missing\n * `code`) are still rejected so the bug surfaces — see the\n * `'rejects entries that are objects but missing required fields'`\n * test for the rationale.\n */\nfunction isDetailsRecord(value: unknown): value is ApiErrorDetails {\n if (value === null || typeof value !== 'object' || Array.isArray(value)) return false\n // Reject prototype-polluted keys — we don't use them here, but downstream\n // spreads shouldn't have to worry about this input.\n const record = value as Record<string, unknown>\n for (const k of Object.keys(record)) {\n const v = record[k]\n if (isAcceptedEntry(v)) continue\n if (Array.isArray(v) && v.every((entry) => isAcceptedEntry(entry))) continue\n return false\n }\n return true\n}\n"],"names":["normalizeNumericOption","canonicalizePath","InvalidPathError"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","sources":["../src/runtime/core/serialize-script.ts","../src/runtime/core/parse-api-errors.ts"],"sourcesContent":["/**\n * Escape a JSON string so it's safe to embed inside an inline\n * `<script>` tag during SSR. Plain `JSON.stringify` is not safe — a\n * form value containing the literal substring `</script>` would\n * break out of the script tag.\n *\n * ```ts\n * const payload = escapeForInlineScript(JSON.stringify(renderAttaformState(app)))\n * // `<script>window.__ATTAFORM_STATE__ = ${payload}</script>` is safe.\n * ```\n *\n * Output remains valid JSON — `JSON.parse` round-trips back to the\n * original value on the client.\n */\nexport function escapeForInlineScript(json: string): string {\n return json.replace(/[<>&\\u2028\\u2029]/g, (char) => {\n switch (char) {\n case '<':\n return '\\\\u003c'\n case '>':\n return '\\\\u003e'\n case '&':\n return '\\\\u0026'\n case '\\u2028':\n return '\\\\u2028'\n case '\\u2029':\n return '\\\\u2029'\n default:\n return char\n }\n })\n}\n","import type {\n ApiErrorDetails,\n ApiErrorEntry,\n ApiErrorEnvelope,\n FormKey,\n ValidationError,\n} from '../types/types-api'\nimport { normalizeNumericOption } from './defaults'\nimport { InvalidPathError } from './errors'\nimport { canonicalizePath } from './paths'\n\n/**\n * Result of `parseApiErrors`. Branch on `ok` to handle the two cases:\n *\n * ```ts\n * const result = parseApiErrors(payload, { formKey: form.key })\n * if (result.ok) {\n * form.setFieldErrors(result.errors)\n * } else {\n * console.warn('Bad error payload:', result.rejected)\n * }\n * ```\n *\n * `ok: true` means the payload was recognised — `errors` may still be\n * empty if the payload was valid but had no actual errors.\n * `ok: false` means the payload didn't match a known shape; `rejected`\n * carries a one-line description of why.\n */\nexport type ParseApiErrorsResult = {\n /** `true` when the payload was recognised; `false` when the shape was unfamiliar. */\n readonly ok: boolean\n /** Errors extracted from the payload. May be empty even when `ok: true`. */\n readonly errors: ValidationError[]\n /** When `ok: false`, a one-line description of why the payload was rejected. */\n readonly rejected?: string\n}\n\n/**\n * Options for `parseApiErrors`. The size caps protect against\n * misbehaving or hostile servers — exceeding any cap causes the\n * parser to reject the payload wholesale rather than partially apply.\n */\nexport type ParseApiErrorsOptions = {\n /**\n * The form's identifier — pass `form.key`. Stamped on every\n * produced `ValidationError` so errors route to the right form.\n */\n readonly formKey: FormKey\n /**\n * Code stamped on `ValidationError`s synthesized from bare-string\n * entries (the Rails / DRF / Laravel `{ field: [\"msg\"] }` shape).\n * Default `'api:unknown'`. Pick something more specific\n * (`'api:server-validation'`, `'myapp:legacy'`, …) when you know\n * the source.\n *\n * Structured `{ message, code }` entries forward their `code`\n * verbatim and ignore this option.\n */\n readonly defaultCode?: string\n /**\n * Maximum number of distinct keys to accept. Default `1000`.\n * Raise for trusted backends that legitimately produce more.\n */\n readonly maxEntries?: number\n /**\n * Maximum number of path segments per key. Default `32`. Keys\n * deeper than this are dropped (the rest of the payload still\n * applies if it stays under the other caps).\n */\n readonly maxPathDepth?: number\n /**\n * Maximum total path segments summed across every accepted key.\n * Default `10000`. Bounds the worst-case traversal cost.\n */\n readonly maxTotalSegments?: number\n}\n\n/**\n * Default size caps + default fallback code used by `parseApiErrors`.\n * Conservative; pass larger values (or a more specific code) via the\n * options bag for trusted-backend integrations.\n */\nexport const PARSE_API_ERRORS_DEFAULTS = {\n maxEntries: 1000,\n maxPathDepth: 32,\n maxTotalSegments: 10000,\n defaultCode: 'api:unknown',\n} as const\n\n/**\n * Normalise a server-side validation error payload into\n * `ValidationError[]`. Pair with `form.setFieldErrors` /\n * `form.addFieldErrors` to surface server errors on the form:\n *\n * ```ts\n * const response = await fetch('/api/signup', { … })\n * if (!response.ok) {\n * const payload = await response.json()\n * const result = parseApiErrors(payload, { formKey: form.key })\n * if (result.ok) form.setFieldErrors(result.errors)\n * }\n * ```\n *\n * Recognised payload shapes:\n *\n * - Wrapped envelope:\n * `{ error: { details: { email: { message: 'taken', code: 'api:duplicate-email' } } } }`\n * - Unwrapped envelope:\n * `{ details: { email: { message: 'taken', code: 'api:duplicate-email' } } }`\n * - Raw details record:\n * `{ email: { message: 'taken', code: 'api:duplicate-email' } }`\n * - **Bare-string Rails / DRF / Laravel shape:**\n * `{ email: ['Email already taken.'], username: 'too short' }`\n * - `null` / `undefined` — returns `{ ok: true, errors: [] }`\n *\n * Two entry shapes are accepted:\n *\n * 1. **Structured** — `{ message: string, code: string }`. The `code`\n * is forwarded verbatim onto the produced `ValidationError`.\n * 2. **Bare-string** — a plain string. Synthesized into\n * `{ message: <string>, code: <defaultCode> }` where `defaultCode`\n * comes from `options.defaultCode` (default `'api:unknown'`).\n * Useful for the Rails / Django REST Framework / FastAPI / Laravel\n * JSON shape that doesn't carry a per-field code.\n *\n * Each detail key's value can be a single entry, an array, or a mix\n * of structured and bare-string entries; arrays expand into one\n * `ValidationError` per entry. Pick a prefix on the server (`api:`,\n * `auth:`, etc.) and stay consistent so error renderers can branch\n * on `code` — or rely on `defaultCode` when the wire shape is\n * message-only.\n *\n * Dotted keys (`\"address.line1\"`) are split into structured paths\n * automatically. Use a custom server response shape outside these\n * patterns? Build the `ValidationError[]` array yourself and pass\n * it to `setFieldErrors` directly — `parseApiErrors` is just a\n * convenience for the common shapes.\n */\nexport function parseApiErrors(\n payload: ApiErrorEnvelope | ApiErrorDetails | null | undefined | unknown,\n options: ParseApiErrorsOptions\n): ParseApiErrorsResult {\n // Sanitise the caps. Comparison gates (`>` against the count /\n // depth) yield `false` for `NaN`, so without sanitisation a\n // hostile or malformed `NaN` cap would let pathological payloads\n // run unbounded. `Infinity` would do the same. Negatives and\n // non-integers would discard legitimate entries. Falls back to\n // the library default on garbage.\n const maxEntries = normalizeNumericOption({\n value: options.maxEntries ?? PARSE_API_ERRORS_DEFAULTS.maxEntries,\n source: 'parseApiErrors.maxEntries',\n allowInfinity: false,\n min: 0,\n defaultValue: PARSE_API_ERRORS_DEFAULTS.maxEntries,\n })\n const maxPathDepth = normalizeNumericOption({\n value: options.maxPathDepth ?? PARSE_API_ERRORS_DEFAULTS.maxPathDepth,\n source: 'parseApiErrors.maxPathDepth',\n allowInfinity: false,\n min: 0,\n defaultValue: PARSE_API_ERRORS_DEFAULTS.maxPathDepth,\n })\n const maxTotalSegments = normalizeNumericOption({\n value: options.maxTotalSegments ?? PARSE_API_ERRORS_DEFAULTS.maxTotalSegments,\n source: 'parseApiErrors.maxTotalSegments',\n allowInfinity: false,\n min: 0,\n defaultValue: PARSE_API_ERRORS_DEFAULTS.maxTotalSegments,\n })\n const defaultCode = options.defaultCode ?? PARSE_API_ERRORS_DEFAULTS.defaultCode\n\n if (payload === null || payload === undefined) {\n return { ok: true, errors: [] }\n }\n if (typeof payload !== 'object') {\n return { ok: false, errors: [], rejected: `payload was ${typeof payload}, expected object` }\n }\n\n const extraction = extractDetails(payload as Record<string, unknown>)\n if (!extraction.ok) {\n return { ok: false, errors: [], rejected: extraction.reason }\n }\n\n const { details } = extraction\n const entryCount = Object.keys(details).length\n // Enforce the guardrails before we spend time walking the payload.\n // Rejecting wholesale (not partial-applying) keeps the failure visible\n // so consumers can tune the caps or investigate the server payload.\n if (entryCount > maxEntries) {\n return {\n ok: false,\n errors: [],\n rejected: `payload has ${entryCount} entries, exceeds maxEntries=${maxEntries}`,\n }\n }\n\n const errors: ValidationError[] = []\n let totalSegments = 0\n for (const [key, value] of Object.entries(details)) {\n const entryList: ReadonlyArray<string | ApiErrorEntry> = Array.isArray(value) ? value : [value]\n // `canonicalizePath` throws `InvalidPathError` for dotted strings with\n // empty segments (e.g. `'. '`, `'a..b'`). A misbehaving server can\n // genuinely emit such a key; the hydrator is a normaliser, not a\n // validator, so we drop offending keys rather than let the exception\n // escape. Well-formed keys continue as normal.\n let segments: readonly (string | number)[]\n try {\n segments = canonicalizePath(key).segments\n } catch (err) {\n if (err instanceof InvalidPathError) continue\n throw err\n }\n // Per-path depth cap. We drop the offending key (rather than\n // rejecting the whole payload) because a single stray deep path\n // in an otherwise legitimate error set is still worth surfacing\n // the rest. Consumers who want strict rejection can post-filter\n // on `result.errors.length < details entryCount`.\n if (segments.length > maxPathDepth) continue\n // Total-segment cap. Enforced wholesale (not per-key) so a payload\n // that passes the per-key gate but stacks into a pathological\n // total still fails visibly. Mirrors `maxEntries` strictness.\n totalSegments += segments.length\n if (totalSegments > maxTotalSegments) {\n return {\n ok: false,\n errors: [],\n rejected: `payload total path segments exceeds maxTotalSegments=${maxTotalSegments}`,\n }\n }\n for (const entry of entryList) {\n // Bare-string entries (Rails / DRF / Laravel shape) synthesize a\n // `code` from `options.defaultCode`; structured `{ message, code }`\n // entries forward `code` verbatim. Empty messages drop silently\n // (`{ message: '' }` or `''`) — same recoverable-malformed-server\n // policy as before.\n const message = typeof entry === 'string' ? entry : entry.message\n const code = typeof entry === 'string' ? defaultCode : entry.code\n if (message.length === 0) continue\n errors.push({\n message,\n path: Array.from(segments),\n formKey: options.formKey,\n code,\n })\n }\n }\n return { ok: true, errors }\n}\n\ntype ExtractResult = { ok: true; details: ApiErrorDetails } | { ok: false; reason: string }\n\nfunction extractDetails(payload: Record<string, unknown>): ExtractResult {\n const wrappedError = payload['error']\n if (wrappedError !== null && wrappedError !== undefined && typeof wrappedError === 'object') {\n const inner = (wrappedError as { details?: unknown }).details\n if (inner === undefined) {\n // A wrapped envelope without details is considered \"no errors\" — valid shape.\n return { ok: true, details: {} }\n }\n if (isDetailsRecord(inner)) return { ok: true, details: inner }\n return {\n ok: false,\n reason: 'error.details entries must be strings or { message, code } objects',\n }\n }\n\n // `{ error: 'oops' }` / `{ error: 42 }` is a malformed wrapped envelope —\n // the server meant an error object but sent a scalar. Without this guard\n // the payload would fall through to the raw-details branch below, where\n // `{ error: 'oops' }` satisfies `isDetailsRecord` and silently produces\n // a phantom `ValidationError` at path `['error']`.\n if (wrappedError !== null && wrappedError !== undefined && typeof wrappedError !== 'object') {\n return {\n ok: false,\n reason: `payload.error was ${typeof wrappedError}, expected an object with { details }`,\n }\n }\n\n if ('details' in payload) {\n const inner = payload['details']\n if (inner === undefined) return { ok: true, details: {} }\n if (isDetailsRecord(inner)) return { ok: true, details: inner }\n return { ok: false, reason: 'details entries must be strings or { message, code } objects' }\n }\n\n if (isDetailsRecord(payload)) return { ok: true, details: payload }\n\n // Heuristic: if the payload has keys but none of them look like details,\n // it's probably a completely different shape. Reject.\n if (Object.keys(payload).length === 0) return { ok: true, details: {} }\n return { ok: false, reason: 'unrecognised payload shape' }\n}\n\nfunction isStructuredEntry(value: unknown): value is ApiErrorEntry {\n if (value === null || typeof value !== 'object' || Array.isArray(value)) return false\n const obj = value as { message?: unknown; code?: unknown }\n return typeof obj.message === 'string' && typeof obj.code === 'string'\n}\n\n/**\n * Accepts either a structured `{ message, code }` entry OR a bare\n * string. Bare strings synthesize a `code` at parse time\n * (`options.defaultCode`) and are useful for the Rails / Django REST\n * Framework / Laravel JSON shape that doesn't carry a per-field code.\n */\nfunction isAcceptedEntry(value: unknown): value is string | ApiErrorEntry {\n return typeof value === 'string' || isStructuredEntry(value)\n}\n\n/**\n * A record is a \"details\" record when every value is either an\n * accepted entry or an array of accepted entries (mixing structured +\n * bare-string in the same array is fine; the parser normalises per\n * entry). Half-structured objects (e.g. `{ message: 'x' }` missing\n * `code`) are still rejected so the bug surfaces — see the\n * `'rejects entries that are objects but missing required fields'`\n * test for the rationale.\n */\nfunction isDetailsRecord(value: unknown): value is ApiErrorDetails {\n if (value === null || typeof value !== 'object' || Array.isArray(value)) return false\n // Reject prototype-polluted keys — we don't use them here, but downstream\n // spreads shouldn't have to worry about this input.\n const record = value as Record<string, unknown>\n for (const k of Object.keys(record)) {\n const v = record[k]\n if (isAcceptedEntry(v)) continue\n if (Array.isArray(v) && v.every((entry) => isAcceptedEntry(entry))) continue\n return false\n }\n return true\n}\n"],"names":["normalizeNumericOption","canonicalizePath","InvalidPathError"],"mappings":"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"}
package/dist/index.d.cts CHANGED
@@ -1,9 +1,9 @@
1
1
  import { Plugin, App } from 'vue';
2
- import { S as SSRDetectOptions, d as SerializedFormData, c as AttaformRegistry } from './shared/attaform.ePUcKxId.cjs';
3
- export { A as AggregateError, a as AnyForm, b as AttaformErrorCode, C as CompiledStep, F as FormStatus, I as InjectWizardInput, L as LazyMarker, e as StepSlot, U as UseRegisterReturn, f as UseWizardReturnType, W as WizardCtx, g as WizardCtxForm, h as WizardOnError, i as WizardOnSubmit, j as WizardOptions, k as WizardPersistFn, l as WizardRestoreFn, m as WizardRestoreState, n as WizardStatusesProxy, o as WizardSubmitContext, p as createRegistry, q as defaultCoercionRules, r as defineCoercion, s as getRegistryFromApp, t as injectForm, u as injectWizard, v as kAttaformRegistry, w as lazy, x as useRegister, y as useRegistry, z as useWizard } from './shared/attaform.ePUcKxId.cjs';
4
- import { f as AttaformDefaults, t as FormKey, G as GenericForm, ai as UseFormConfiguration, A as AbstractSchema, j as DefaultValuesInput, aj as UseFormReturnType, a8 as RegisterValue, a3 as RegisterModelDynamicCustomDirective, aa as Segment, x as GetDisplayState, am as ValidationError, c as ApiErrorEnvelope, a as ApiErrorDetails } from './shared/attaform.C0uGZQ4M.cjs';
5
- export { b as ApiErrorEntry, d as ArrayItem, e as ArrayPath, C as CoercionEntry, g as CoercionRegistry, h as CoercionResult, i as CustomDirectiveRegisterAssignerFn, D as DeepPartial, k as DefaultValuesResponse, l as DefaultValuesShape, m as DisplayState, E as ErrorsProxyShape, F as FieldMetaPayload, n as FieldState, o as FieldStateMap, p as FieldStateMapEntry, q as FlatPath, r as FormErrorRecord, s as FormErrorsSurface, u as FormMeta, v as FormStorage, w as FormStorageKind, H as HandleSubmit, y as HistoryConfig, I as IsTuple, z as IsUnion, J as JoinSegments, K as KeyofUnion, L as LiftedValueShape, M as MetaTrackerValue, N as NestedReadType, B as NestedType, O as OnError, P as OnInvalidSubmitPolicy, Q as OnSubmit, R as PartialFlatPath, S as Path, T as PathKey, U as PendingValidationStatus, V as PersistConfig, W as PersistConfigOptions, X as PersistIncludeMode, Z as Primitive, _ as ROOT_PATH, $ as ROOT_PATH_KEY, a0 as ReactiveValidationStatus, a1 as RegisterDirective, a2 as RegisterFlatPath, a4 as RegisterOptions, a5 as RegisterSelectModifier, a6 as RegisterTextModifier, a7 as RegisterTransform, ab as SetValueCallback, ac as SetValuePayload, ad as SettledValidationStatus, ae as SlimPrimitiveKind, af as SlimRuntimeOf, ag as SubmitHandler, ah as Unset, ak as ValidateOn, al as ValidateOnConfig, an as ValidationResponse, ao as ValidationResponseWithoutValue, ap as ValueOfUnion, aq as WriteMeta, ar as WriteShape, as as canonicalizePath, at as isPathPrefix, au as isUnset, av as parseDottedPath, aw as unset } from './shared/attaform.C0uGZQ4M.cjs';
6
- export { A as AnonPersistError, a as AttaformError, I as InvalidPathError, b as InvalidUseFormConfigError, O as OutsideSetupError, R as RegistryNotInstalledError, c as ReservedFormKeyError, S as SensitivePersistFieldError, d as SubmitErrorHandlerError } from './shared/attaform.B7rzpK1U.cjs';
2
+ import { S as SSRDetectOptions, c as SerializedFormData, b as AttaformRegistry } from './shared/attaform.D5-1XGQU.cjs';
3
+ export { A as AnyForm, a as AttaformErrorCode, C as CompiledStep, F as FormStatus, I as InjectWizardInput, L as LazyMarker, d as StepSlot, U as UseRegisterReturn, e as UseWizardReturnType, W as WizardAggregateError, f as WizardCtx, g as WizardCtxForm, h as WizardOnError, i as WizardOnSubmit, j as WizardOptions, k as WizardPersistFn, l as WizardRestoreFn, m as WizardRestoreState, n as WizardStatusesProxy, o as WizardSubmitContext, p as createRegistry, q as defaultCoercionRules, r as defineCoercion, s as getRegistryFromApp, t as injectForm, u as injectWizard, v as kAttaformRegistry, w as lazy, x as useRegister, y as useRegistry, z as useWizard } from './shared/attaform.D5-1XGQU.cjs';
4
+ import { f as AttaformDefaults, t as FormKey, G as GenericForm, ai as UseFormConfiguration, A as AbstractSchema, j as DefaultValuesInput, aj as UseFormReturnType, a8 as RegisterValue, a3 as RegisterModelDynamicCustomDirective, x as GetDisplayState, am as ValidationError, c as ApiErrorEnvelope, a as ApiErrorDetails } from './shared/attaform.SfhU0OEY.cjs';
5
+ export { b as ApiErrorEntry, d as ArrayItem, e as ArrayPath, C as CoercionEntry, g as CoercionRegistry, h as CoercionResult, i as CustomDirectiveRegisterAssignerFn, D as DeepPartial, k as DefaultValuesResponse, l as DefaultValuesShape, m as DisplayState, E as ErrorsProxyShape, F as FieldMetaPayload, n as FieldState, o as FieldStateMap, p as FieldStateMapEntry, q as FlatPath, r as FormErrorRecord, s as FormErrorsSurface, u as FormMeta, v as FormStorage, w as FormStorageKind, H as HandleSubmit, y as HistoryConfig, I as IsTuple, z as IsUnion, J as JoinSegments, K as KeyofUnion, L as LiftedValueShape, M as MetaTrackerValue, N as NestedReadType, B as NestedType, O as OnError, P as OnInvalidSubmitPolicy, Q as OnSubmit, R as PartialFlatPath, S as Path, T as PathKey, U as PendingValidationStatus, V as PersistConfig, W as PersistConfigOptions, X as PersistIncludeMode, Z as Primitive, _ as ROOT_PATH, $ as ROOT_PATH_KEY, a0 as ReactiveValidationStatus, a1 as RegisterDirective, a2 as RegisterFlatPath, a4 as RegisterOptions, a5 as RegisterSelectModifier, a6 as RegisterTextModifier, a7 as RegisterTransform, aa as Segment, ab as SetValueCallback, ac as SetValuePayload, ad as SettledValidationStatus, ae as SlimPrimitiveKind, af as SlimRuntimeOf, ag as SubmitHandler, ah as Unset, ak as ValidateOn, al as ValidateOnConfig, an as ValidationResponse, ao as ValidationResponseWithoutValue, ap as ValueOfUnion, aq as WriteMeta, ar as WriteShape, as as canonicalizePath, at as isPathPrefix, au as isUnset, av as parseDottedPath, aw as unset } from './shared/attaform.SfhU0OEY.cjs';
6
+ export { A as AnonPersistError, a as AttaformError, I as InvalidPathError, b as InvalidUseFormConfigError, O as OutsideSetupError, R as RegistryNotInstalledError, c as ReservedFormKeyError, S as SubmitErrorHandlerError } from './shared/attaform.DkA5J8NW.cjs';
7
7
 
8
8
  /**
9
9
  * Options for `createAttaform()`.
@@ -217,39 +217,13 @@ declare const vRegister: RegisterModelDynamicCustomDirective;
217
217
  * the Nuxt DevTools (overlay) panel wired up via `../../nuxt.ts` +
218
218
  * `../pages/_attaform_devtools.vue`.
219
219
  *
220
- * Centralizing the redaction policy and the window-bridge contract here
221
- * keeps both surfaces aligned: a future tightening of the sensitive-name
222
- * heuristic, or a new field added to the bridge, lands in one file.
220
+ * Houses the window-bridge contract both surfaces consume so a new
221
+ * bridge field lands in one file. Both surfaces render RAW form values
222
+ * by design DevTools is a dev-only surface, and redaction across every
223
+ * place a value surfaces is impractical security theater rather than a
224
+ * real safeguard.
223
225
  */
224
226
 
225
- declare const REDACTED = "[redacted]";
226
- /**
227
- * Walk `value` and replace any leaf whose enclosing path matches the
228
- * sensitive-name heuristic with the string `'[redacted]'`. Returns a
229
- * new tree (no mutation of the input). Object keys + array indices are
230
- * preserved; only the leaf payloads change.
231
- *
232
- * Applied to BOTH devtools surfaces' Form-value rendering AND every
233
- * timeline event payload — leaks via either surface are treatable as
234
- * "any developer with the panel open during user testing can read a
235
- * customer's password," which is exactly the failure mode the
236
- * sensitive-name guard exists to prevent on the storage side.
237
- *
238
- * Leaves whose path doesn't match a pattern pass through untouched.
239
- * `acknowledgeSensitive: true` on persistence does NOT bypass this — if
240
- * the consumer opted into persisting the value, they still shouldn't
241
- * see it in DevTools timelines that grow unbounded.
242
- *
243
- * Implementation note: tracks an `inSensitiveSubtree` flag through the
244
- * recursion instead of allocating a fresh path array per node + calling
245
- * `isSensitivePath` per leaf. Once any ancestor segment matches the
246
- * heuristic, the flag stays set for every descendant — the leaf simply
247
- * returns `REDACTED` without re-scanning the path. For a 100-leaf form:
248
- * ~100 path allocations + ~100 full-path regex sweeps → 0 path
249
- * allocations + ~100 single-segment regex sweeps, with whole-subtree
250
- * short-circuit when sensitive ancestors are found early.
251
- */
252
- declare function redactSensitiveLeaves(value: unknown, matchSensitive: (segment: Segment) => boolean): unknown;
253
227
  /**
254
228
  * Property key on `window` that the Nuxt-side dev plugin attaches the
255
229
  * bridge object to. The iframe-mounted overlay panel reads
@@ -384,9 +358,9 @@ declare const defaultDisplayState: GetDisplayState;
384
358
  * })
385
359
  * ```
386
360
  *
387
- * The same resolved predicate gates persistence writes, multi-tab sync
388
- * broadcasts, AND the DevTools redact walk — one source of truth for
389
- * "what counts as sensitive" across every surface.
361
+ * The same resolved predicate gates persistence writes and multi-tab
362
+ * sync broadcasts — one source of truth for "what counts as sensitive"
363
+ * across those surfaces. (DevTools renders raw values by design.)
390
364
  *
391
365
  * **Non-goals.** This is not a soundness guarantee. Adversarial paths
392
366
  * (`'sensitive_data'`, `'CCV'` instead of `'CVV'`) can slip through.
@@ -522,5 +496,5 @@ declare const PARSE_API_ERRORS_DEFAULTS: {
522
496
  */
523
497
  declare function parseApiErrors(payload: ApiErrorEnvelope | ApiErrorDetails | null | undefined | unknown, options: ParseApiErrorsOptions): ParseApiErrorsResult;
524
498
 
525
- export { AbstractSchema, ApiErrorDetails, ApiErrorEnvelope, AttaformDefaults, AttaformRegistry, DEFAULT_SENSITIVE_NAMES, DEVTOOLS_WINDOW_KEY, DefaultValuesInput, FormKey, GenericForm, GetDisplayState, PARSE_API_ERRORS_DEFAULTS, REDACTED, RegisterValue, Segment, SerializedFormData, UseFormConfiguration, UseFormReturnType, ValidationError, assignKey, createAttaform, defaultDisplayState, escapeForInlineScript, hydrateAttaformState, isRegisterValue, parseApiErrors, redactSensitiveLeaves, renderAttaformState, useAbstractForm as useForm, vRegister };
499
+ export { AbstractSchema, ApiErrorDetails, ApiErrorEnvelope, AttaformDefaults, AttaformRegistry, DEFAULT_SENSITIVE_NAMES, DEVTOOLS_WINDOW_KEY, DefaultValuesInput, FormKey, GenericForm, GetDisplayState, PARSE_API_ERRORS_DEFAULTS, RegisterValue, SerializedFormData, UseFormConfiguration, UseFormReturnType, ValidationError, assignKey, createAttaform, defaultDisplayState, escapeForInlineScript, hydrateAttaformState, isRegisterValue, parseApiErrors, renderAttaformState, useAbstractForm as useForm, vRegister };
526
500
  export type { AttaformDevtoolsBridge, AttaformPluginOptions, ParseApiErrorsOptions, ParseApiErrorsResult, SerializedAttaformState };
package/dist/index.d.mts CHANGED
@@ -1,9 +1,9 @@
1
1
  import { Plugin, App } from 'vue';
2
- import { S as SSRDetectOptions, d as SerializedFormData, c as AttaformRegistry } from './shared/attaform.BtBmfLQN.mjs';
3
- export { A as AggregateError, a as AnyForm, b as AttaformErrorCode, C as CompiledStep, F as FormStatus, I as InjectWizardInput, L as LazyMarker, e as StepSlot, U as UseRegisterReturn, f as UseWizardReturnType, W as WizardCtx, g as WizardCtxForm, h as WizardOnError, i as WizardOnSubmit, j as WizardOptions, k as WizardPersistFn, l as WizardRestoreFn, m as WizardRestoreState, n as WizardStatusesProxy, o as WizardSubmitContext, p as createRegistry, q as defaultCoercionRules, r as defineCoercion, s as getRegistryFromApp, t as injectForm, u as injectWizard, v as kAttaformRegistry, w as lazy, x as useRegister, y as useRegistry, z as useWizard } from './shared/attaform.BtBmfLQN.mjs';
4
- import { f as AttaformDefaults, t as FormKey, G as GenericForm, ai as UseFormConfiguration, A as AbstractSchema, j as DefaultValuesInput, aj as UseFormReturnType, a8 as RegisterValue, a3 as RegisterModelDynamicCustomDirective, aa as Segment, x as GetDisplayState, am as ValidationError, c as ApiErrorEnvelope, a as ApiErrorDetails } from './shared/attaform.C0uGZQ4M.mjs';
5
- export { b as ApiErrorEntry, d as ArrayItem, e as ArrayPath, C as CoercionEntry, g as CoercionRegistry, h as CoercionResult, i as CustomDirectiveRegisterAssignerFn, D as DeepPartial, k as DefaultValuesResponse, l as DefaultValuesShape, m as DisplayState, E as ErrorsProxyShape, F as FieldMetaPayload, n as FieldState, o as FieldStateMap, p as FieldStateMapEntry, q as FlatPath, r as FormErrorRecord, s as FormErrorsSurface, u as FormMeta, v as FormStorage, w as FormStorageKind, H as HandleSubmit, y as HistoryConfig, I as IsTuple, z as IsUnion, J as JoinSegments, K as KeyofUnion, L as LiftedValueShape, M as MetaTrackerValue, N as NestedReadType, B as NestedType, O as OnError, P as OnInvalidSubmitPolicy, Q as OnSubmit, R as PartialFlatPath, S as Path, T as PathKey, U as PendingValidationStatus, V as PersistConfig, W as PersistConfigOptions, X as PersistIncludeMode, Z as Primitive, _ as ROOT_PATH, $ as ROOT_PATH_KEY, a0 as ReactiveValidationStatus, a1 as RegisterDirective, a2 as RegisterFlatPath, a4 as RegisterOptions, a5 as RegisterSelectModifier, a6 as RegisterTextModifier, a7 as RegisterTransform, ab as SetValueCallback, ac as SetValuePayload, ad as SettledValidationStatus, ae as SlimPrimitiveKind, af as SlimRuntimeOf, ag as SubmitHandler, ah as Unset, ak as ValidateOn, al as ValidateOnConfig, an as ValidationResponse, ao as ValidationResponseWithoutValue, ap as ValueOfUnion, aq as WriteMeta, ar as WriteShape, as as canonicalizePath, at as isPathPrefix, au as isUnset, av as parseDottedPath, aw as unset } from './shared/attaform.C0uGZQ4M.mjs';
6
- export { A as AnonPersistError, a as AttaformError, I as InvalidPathError, b as InvalidUseFormConfigError, O as OutsideSetupError, R as RegistryNotInstalledError, c as ReservedFormKeyError, S as SensitivePersistFieldError, d as SubmitErrorHandlerError } from './shared/attaform.B7rzpK1U.mjs';
2
+ import { S as SSRDetectOptions, c as SerializedFormData, b as AttaformRegistry } from './shared/attaform.EMzJcQci.mjs';
3
+ export { A as AnyForm, a as AttaformErrorCode, C as CompiledStep, F as FormStatus, I as InjectWizardInput, L as LazyMarker, d as StepSlot, U as UseRegisterReturn, e as UseWizardReturnType, W as WizardAggregateError, f as WizardCtx, g as WizardCtxForm, h as WizardOnError, i as WizardOnSubmit, j as WizardOptions, k as WizardPersistFn, l as WizardRestoreFn, m as WizardRestoreState, n as WizardStatusesProxy, o as WizardSubmitContext, p as createRegistry, q as defaultCoercionRules, r as defineCoercion, s as getRegistryFromApp, t as injectForm, u as injectWizard, v as kAttaformRegistry, w as lazy, x as useRegister, y as useRegistry, z as useWizard } from './shared/attaform.EMzJcQci.mjs';
4
+ import { f as AttaformDefaults, t as FormKey, G as GenericForm, ai as UseFormConfiguration, A as AbstractSchema, j as DefaultValuesInput, aj as UseFormReturnType, a8 as RegisterValue, a3 as RegisterModelDynamicCustomDirective, x as GetDisplayState, am as ValidationError, c as ApiErrorEnvelope, a as ApiErrorDetails } from './shared/attaform.SfhU0OEY.mjs';
5
+ export { b as ApiErrorEntry, d as ArrayItem, e as ArrayPath, C as CoercionEntry, g as CoercionRegistry, h as CoercionResult, i as CustomDirectiveRegisterAssignerFn, D as DeepPartial, k as DefaultValuesResponse, l as DefaultValuesShape, m as DisplayState, E as ErrorsProxyShape, F as FieldMetaPayload, n as FieldState, o as FieldStateMap, p as FieldStateMapEntry, q as FlatPath, r as FormErrorRecord, s as FormErrorsSurface, u as FormMeta, v as FormStorage, w as FormStorageKind, H as HandleSubmit, y as HistoryConfig, I as IsTuple, z as IsUnion, J as JoinSegments, K as KeyofUnion, L as LiftedValueShape, M as MetaTrackerValue, N as NestedReadType, B as NestedType, O as OnError, P as OnInvalidSubmitPolicy, Q as OnSubmit, R as PartialFlatPath, S as Path, T as PathKey, U as PendingValidationStatus, V as PersistConfig, W as PersistConfigOptions, X as PersistIncludeMode, Z as Primitive, _ as ROOT_PATH, $ as ROOT_PATH_KEY, a0 as ReactiveValidationStatus, a1 as RegisterDirective, a2 as RegisterFlatPath, a4 as RegisterOptions, a5 as RegisterSelectModifier, a6 as RegisterTextModifier, a7 as RegisterTransform, aa as Segment, ab as SetValueCallback, ac as SetValuePayload, ad as SettledValidationStatus, ae as SlimPrimitiveKind, af as SlimRuntimeOf, ag as SubmitHandler, ah as Unset, ak as ValidateOn, al as ValidateOnConfig, an as ValidationResponse, ao as ValidationResponseWithoutValue, ap as ValueOfUnion, aq as WriteMeta, ar as WriteShape, as as canonicalizePath, at as isPathPrefix, au as isUnset, av as parseDottedPath, aw as unset } from './shared/attaform.SfhU0OEY.mjs';
6
+ export { A as AnonPersistError, a as AttaformError, I as InvalidPathError, b as InvalidUseFormConfigError, O as OutsideSetupError, R as RegistryNotInstalledError, c as ReservedFormKeyError, S as SubmitErrorHandlerError } from './shared/attaform.DkA5J8NW.mjs';
7
7
 
8
8
  /**
9
9
  * Options for `createAttaform()`.
@@ -217,39 +217,13 @@ declare const vRegister: RegisterModelDynamicCustomDirective;
217
217
  * the Nuxt DevTools (overlay) panel wired up via `../../nuxt.ts` +
218
218
  * `../pages/_attaform_devtools.vue`.
219
219
  *
220
- * Centralizing the redaction policy and the window-bridge contract here
221
- * keeps both surfaces aligned: a future tightening of the sensitive-name
222
- * heuristic, or a new field added to the bridge, lands in one file.
220
+ * Houses the window-bridge contract both surfaces consume so a new
221
+ * bridge field lands in one file. Both surfaces render RAW form values
222
+ * by design DevTools is a dev-only surface, and redaction across every
223
+ * place a value surfaces is impractical security theater rather than a
224
+ * real safeguard.
223
225
  */
224
226
 
225
- declare const REDACTED = "[redacted]";
226
- /**
227
- * Walk `value` and replace any leaf whose enclosing path matches the
228
- * sensitive-name heuristic with the string `'[redacted]'`. Returns a
229
- * new tree (no mutation of the input). Object keys + array indices are
230
- * preserved; only the leaf payloads change.
231
- *
232
- * Applied to BOTH devtools surfaces' Form-value rendering AND every
233
- * timeline event payload — leaks via either surface are treatable as
234
- * "any developer with the panel open during user testing can read a
235
- * customer's password," which is exactly the failure mode the
236
- * sensitive-name guard exists to prevent on the storage side.
237
- *
238
- * Leaves whose path doesn't match a pattern pass through untouched.
239
- * `acknowledgeSensitive: true` on persistence does NOT bypass this — if
240
- * the consumer opted into persisting the value, they still shouldn't
241
- * see it in DevTools timelines that grow unbounded.
242
- *
243
- * Implementation note: tracks an `inSensitiveSubtree` flag through the
244
- * recursion instead of allocating a fresh path array per node + calling
245
- * `isSensitivePath` per leaf. Once any ancestor segment matches the
246
- * heuristic, the flag stays set for every descendant — the leaf simply
247
- * returns `REDACTED` without re-scanning the path. For a 100-leaf form:
248
- * ~100 path allocations + ~100 full-path regex sweeps → 0 path
249
- * allocations + ~100 single-segment regex sweeps, with whole-subtree
250
- * short-circuit when sensitive ancestors are found early.
251
- */
252
- declare function redactSensitiveLeaves(value: unknown, matchSensitive: (segment: Segment) => boolean): unknown;
253
227
  /**
254
228
  * Property key on `window` that the Nuxt-side dev plugin attaches the
255
229
  * bridge object to. The iframe-mounted overlay panel reads
@@ -384,9 +358,9 @@ declare const defaultDisplayState: GetDisplayState;
384
358
  * })
385
359
  * ```
386
360
  *
387
- * The same resolved predicate gates persistence writes, multi-tab sync
388
- * broadcasts, AND the DevTools redact walk — one source of truth for
389
- * "what counts as sensitive" across every surface.
361
+ * The same resolved predicate gates persistence writes and multi-tab
362
+ * sync broadcasts — one source of truth for "what counts as sensitive"
363
+ * across those surfaces. (DevTools renders raw values by design.)
390
364
  *
391
365
  * **Non-goals.** This is not a soundness guarantee. Adversarial paths
392
366
  * (`'sensitive_data'`, `'CCV'` instead of `'CVV'`) can slip through.
@@ -522,5 +496,5 @@ declare const PARSE_API_ERRORS_DEFAULTS: {
522
496
  */
523
497
  declare function parseApiErrors(payload: ApiErrorEnvelope | ApiErrorDetails | null | undefined | unknown, options: ParseApiErrorsOptions): ParseApiErrorsResult;
524
498
 
525
- export { AbstractSchema, ApiErrorDetails, ApiErrorEnvelope, AttaformDefaults, AttaformRegistry, DEFAULT_SENSITIVE_NAMES, DEVTOOLS_WINDOW_KEY, DefaultValuesInput, FormKey, GenericForm, GetDisplayState, PARSE_API_ERRORS_DEFAULTS, REDACTED, RegisterValue, Segment, SerializedFormData, UseFormConfiguration, UseFormReturnType, ValidationError, assignKey, createAttaform, defaultDisplayState, escapeForInlineScript, hydrateAttaformState, isRegisterValue, parseApiErrors, redactSensitiveLeaves, renderAttaformState, useAbstractForm as useForm, vRegister };
499
+ export { AbstractSchema, ApiErrorDetails, ApiErrorEnvelope, AttaformDefaults, AttaformRegistry, DEFAULT_SENSITIVE_NAMES, DEVTOOLS_WINDOW_KEY, DefaultValuesInput, FormKey, GenericForm, GetDisplayState, PARSE_API_ERRORS_DEFAULTS, RegisterValue, SerializedFormData, UseFormConfiguration, UseFormReturnType, ValidationError, assignKey, createAttaform, defaultDisplayState, escapeForInlineScript, hydrateAttaformState, isRegisterValue, parseApiErrors, renderAttaformState, useAbstractForm as useForm, vRegister };
526
500
  export type { AttaformDevtoolsBridge, AttaformPluginOptions, ParseApiErrorsOptions, ParseApiErrorsResult, SerializedAttaformState };
package/dist/index.d.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  import { Plugin, App } from 'vue';
2
- import { S as SSRDetectOptions, d as SerializedFormData, c as AttaformRegistry } from './shared/attaform.BTpuvGec.js';
3
- export { A as AggregateError, a as AnyForm, b as AttaformErrorCode, C as CompiledStep, F as FormStatus, I as InjectWizardInput, L as LazyMarker, e as StepSlot, U as UseRegisterReturn, f as UseWizardReturnType, W as WizardCtx, g as WizardCtxForm, h as WizardOnError, i as WizardOnSubmit, j as WizardOptions, k as WizardPersistFn, l as WizardRestoreFn, m as WizardRestoreState, n as WizardStatusesProxy, o as WizardSubmitContext, p as createRegistry, q as defaultCoercionRules, r as defineCoercion, s as getRegistryFromApp, t as injectForm, u as injectWizard, v as kAttaformRegistry, w as lazy, x as useRegister, y as useRegistry, z as useWizard } from './shared/attaform.BTpuvGec.js';
4
- import { f as AttaformDefaults, t as FormKey, G as GenericForm, ai as UseFormConfiguration, A as AbstractSchema, j as DefaultValuesInput, aj as UseFormReturnType, a8 as RegisterValue, a3 as RegisterModelDynamicCustomDirective, aa as Segment, x as GetDisplayState, am as ValidationError, c as ApiErrorEnvelope, a as ApiErrorDetails } from './shared/attaform.C0uGZQ4M.js';
5
- export { b as ApiErrorEntry, d as ArrayItem, e as ArrayPath, C as CoercionEntry, g as CoercionRegistry, h as CoercionResult, i as CustomDirectiveRegisterAssignerFn, D as DeepPartial, k as DefaultValuesResponse, l as DefaultValuesShape, m as DisplayState, E as ErrorsProxyShape, F as FieldMetaPayload, n as FieldState, o as FieldStateMap, p as FieldStateMapEntry, q as FlatPath, r as FormErrorRecord, s as FormErrorsSurface, u as FormMeta, v as FormStorage, w as FormStorageKind, H as HandleSubmit, y as HistoryConfig, I as IsTuple, z as IsUnion, J as JoinSegments, K as KeyofUnion, L as LiftedValueShape, M as MetaTrackerValue, N as NestedReadType, B as NestedType, O as OnError, P as OnInvalidSubmitPolicy, Q as OnSubmit, R as PartialFlatPath, S as Path, T as PathKey, U as PendingValidationStatus, V as PersistConfig, W as PersistConfigOptions, X as PersistIncludeMode, Z as Primitive, _ as ROOT_PATH, $ as ROOT_PATH_KEY, a0 as ReactiveValidationStatus, a1 as RegisterDirective, a2 as RegisterFlatPath, a4 as RegisterOptions, a5 as RegisterSelectModifier, a6 as RegisterTextModifier, a7 as RegisterTransform, ab as SetValueCallback, ac as SetValuePayload, ad as SettledValidationStatus, ae as SlimPrimitiveKind, af as SlimRuntimeOf, ag as SubmitHandler, ah as Unset, ak as ValidateOn, al as ValidateOnConfig, an as ValidationResponse, ao as ValidationResponseWithoutValue, ap as ValueOfUnion, aq as WriteMeta, ar as WriteShape, as as canonicalizePath, at as isPathPrefix, au as isUnset, av as parseDottedPath, aw as unset } from './shared/attaform.C0uGZQ4M.js';
6
- export { A as AnonPersistError, a as AttaformError, I as InvalidPathError, b as InvalidUseFormConfigError, O as OutsideSetupError, R as RegistryNotInstalledError, c as ReservedFormKeyError, S as SensitivePersistFieldError, d as SubmitErrorHandlerError } from './shared/attaform.B7rzpK1U.js';
2
+ import { S as SSRDetectOptions, c as SerializedFormData, b as AttaformRegistry } from './shared/attaform.Bh3ACtts.js';
3
+ export { A as AnyForm, a as AttaformErrorCode, C as CompiledStep, F as FormStatus, I as InjectWizardInput, L as LazyMarker, d as StepSlot, U as UseRegisterReturn, e as UseWizardReturnType, W as WizardAggregateError, f as WizardCtx, g as WizardCtxForm, h as WizardOnError, i as WizardOnSubmit, j as WizardOptions, k as WizardPersistFn, l as WizardRestoreFn, m as WizardRestoreState, n as WizardStatusesProxy, o as WizardSubmitContext, p as createRegistry, q as defaultCoercionRules, r as defineCoercion, s as getRegistryFromApp, t as injectForm, u as injectWizard, v as kAttaformRegistry, w as lazy, x as useRegister, y as useRegistry, z as useWizard } from './shared/attaform.Bh3ACtts.js';
4
+ import { f as AttaformDefaults, t as FormKey, G as GenericForm, ai as UseFormConfiguration, A as AbstractSchema, j as DefaultValuesInput, aj as UseFormReturnType, a8 as RegisterValue, a3 as RegisterModelDynamicCustomDirective, x as GetDisplayState, am as ValidationError, c as ApiErrorEnvelope, a as ApiErrorDetails } from './shared/attaform.SfhU0OEY.js';
5
+ export { b as ApiErrorEntry, d as ArrayItem, e as ArrayPath, C as CoercionEntry, g as CoercionRegistry, h as CoercionResult, i as CustomDirectiveRegisterAssignerFn, D as DeepPartial, k as DefaultValuesResponse, l as DefaultValuesShape, m as DisplayState, E as ErrorsProxyShape, F as FieldMetaPayload, n as FieldState, o as FieldStateMap, p as FieldStateMapEntry, q as FlatPath, r as FormErrorRecord, s as FormErrorsSurface, u as FormMeta, v as FormStorage, w as FormStorageKind, H as HandleSubmit, y as HistoryConfig, I as IsTuple, z as IsUnion, J as JoinSegments, K as KeyofUnion, L as LiftedValueShape, M as MetaTrackerValue, N as NestedReadType, B as NestedType, O as OnError, P as OnInvalidSubmitPolicy, Q as OnSubmit, R as PartialFlatPath, S as Path, T as PathKey, U as PendingValidationStatus, V as PersistConfig, W as PersistConfigOptions, X as PersistIncludeMode, Z as Primitive, _ as ROOT_PATH, $ as ROOT_PATH_KEY, a0 as ReactiveValidationStatus, a1 as RegisterDirective, a2 as RegisterFlatPath, a4 as RegisterOptions, a5 as RegisterSelectModifier, a6 as RegisterTextModifier, a7 as RegisterTransform, aa as Segment, ab as SetValueCallback, ac as SetValuePayload, ad as SettledValidationStatus, ae as SlimPrimitiveKind, af as SlimRuntimeOf, ag as SubmitHandler, ah as Unset, ak as ValidateOn, al as ValidateOnConfig, an as ValidationResponse, ao as ValidationResponseWithoutValue, ap as ValueOfUnion, aq as WriteMeta, ar as WriteShape, as as canonicalizePath, at as isPathPrefix, au as isUnset, av as parseDottedPath, aw as unset } from './shared/attaform.SfhU0OEY.js';
6
+ export { A as AnonPersistError, a as AttaformError, I as InvalidPathError, b as InvalidUseFormConfigError, O as OutsideSetupError, R as RegistryNotInstalledError, c as ReservedFormKeyError, S as SubmitErrorHandlerError } from './shared/attaform.DkA5J8NW.js';
7
7
 
8
8
  /**
9
9
  * Options for `createAttaform()`.
@@ -217,39 +217,13 @@ declare const vRegister: RegisterModelDynamicCustomDirective;
217
217
  * the Nuxt DevTools (overlay) panel wired up via `../../nuxt.ts` +
218
218
  * `../pages/_attaform_devtools.vue`.
219
219
  *
220
- * Centralizing the redaction policy and the window-bridge contract here
221
- * keeps both surfaces aligned: a future tightening of the sensitive-name
222
- * heuristic, or a new field added to the bridge, lands in one file.
220
+ * Houses the window-bridge contract both surfaces consume so a new
221
+ * bridge field lands in one file. Both surfaces render RAW form values
222
+ * by design DevTools is a dev-only surface, and redaction across every
223
+ * place a value surfaces is impractical security theater rather than a
224
+ * real safeguard.
223
225
  */
224
226
 
225
- declare const REDACTED = "[redacted]";
226
- /**
227
- * Walk `value` and replace any leaf whose enclosing path matches the
228
- * sensitive-name heuristic with the string `'[redacted]'`. Returns a
229
- * new tree (no mutation of the input). Object keys + array indices are
230
- * preserved; only the leaf payloads change.
231
- *
232
- * Applied to BOTH devtools surfaces' Form-value rendering AND every
233
- * timeline event payload — leaks via either surface are treatable as
234
- * "any developer with the panel open during user testing can read a
235
- * customer's password," which is exactly the failure mode the
236
- * sensitive-name guard exists to prevent on the storage side.
237
- *
238
- * Leaves whose path doesn't match a pattern pass through untouched.
239
- * `acknowledgeSensitive: true` on persistence does NOT bypass this — if
240
- * the consumer opted into persisting the value, they still shouldn't
241
- * see it in DevTools timelines that grow unbounded.
242
- *
243
- * Implementation note: tracks an `inSensitiveSubtree` flag through the
244
- * recursion instead of allocating a fresh path array per node + calling
245
- * `isSensitivePath` per leaf. Once any ancestor segment matches the
246
- * heuristic, the flag stays set for every descendant — the leaf simply
247
- * returns `REDACTED` without re-scanning the path. For a 100-leaf form:
248
- * ~100 path allocations + ~100 full-path regex sweeps → 0 path
249
- * allocations + ~100 single-segment regex sweeps, with whole-subtree
250
- * short-circuit when sensitive ancestors are found early.
251
- */
252
- declare function redactSensitiveLeaves(value: unknown, matchSensitive: (segment: Segment) => boolean): unknown;
253
227
  /**
254
228
  * Property key on `window` that the Nuxt-side dev plugin attaches the
255
229
  * bridge object to. The iframe-mounted overlay panel reads
@@ -384,9 +358,9 @@ declare const defaultDisplayState: GetDisplayState;
384
358
  * })
385
359
  * ```
386
360
  *
387
- * The same resolved predicate gates persistence writes, multi-tab sync
388
- * broadcasts, AND the DevTools redact walk — one source of truth for
389
- * "what counts as sensitive" across every surface.
361
+ * The same resolved predicate gates persistence writes and multi-tab
362
+ * sync broadcasts — one source of truth for "what counts as sensitive"
363
+ * across those surfaces. (DevTools renders raw values by design.)
390
364
  *
391
365
  * **Non-goals.** This is not a soundness guarantee. Adversarial paths
392
366
  * (`'sensitive_data'`, `'CCV'` instead of `'CVV'`) can slip through.
@@ -522,5 +496,5 @@ declare const PARSE_API_ERRORS_DEFAULTS: {
522
496
  */
523
497
  declare function parseApiErrors(payload: ApiErrorEnvelope | ApiErrorDetails | null | undefined | unknown, options: ParseApiErrorsOptions): ParseApiErrorsResult;
524
498
 
525
- export { AbstractSchema, ApiErrorDetails, ApiErrorEnvelope, AttaformDefaults, AttaformRegistry, DEFAULT_SENSITIVE_NAMES, DEVTOOLS_WINDOW_KEY, DefaultValuesInput, FormKey, GenericForm, GetDisplayState, PARSE_API_ERRORS_DEFAULTS, REDACTED, RegisterValue, Segment, SerializedFormData, UseFormConfiguration, UseFormReturnType, ValidationError, assignKey, createAttaform, defaultDisplayState, escapeForInlineScript, hydrateAttaformState, isRegisterValue, parseApiErrors, redactSensitiveLeaves, renderAttaformState, useAbstractForm as useForm, vRegister };
499
+ export { AbstractSchema, ApiErrorDetails, ApiErrorEnvelope, AttaformDefaults, AttaformRegistry, DEFAULT_SENSITIVE_NAMES, DEVTOOLS_WINDOW_KEY, DefaultValuesInput, FormKey, GenericForm, GetDisplayState, PARSE_API_ERRORS_DEFAULTS, RegisterValue, SerializedFormData, UseFormConfiguration, UseFormReturnType, ValidationError, assignKey, createAttaform, defaultDisplayState, escapeForInlineScript, hydrateAttaformState, isRegisterValue, parseApiErrors, renderAttaformState, useAbstractForm as useForm, vRegister };
526
500
  export type { AttaformDevtoolsBridge, AttaformPluginOptions, ParseApiErrorsOptions, ParseApiErrorsResult, SerializedAttaformState };
package/dist/index.mjs CHANGED
@@ -1,8 +1,8 @@
1
- import { i as canonicalizePath, I as InvalidPathError } from './shared/attaform.CrpjyXdO.mjs';
2
- export { A as AnonPersistError, a as AttaformError, D as DEFAULT_SENSITIVE_NAMES, c as InvalidUseFormConfigError, O as OutsideSetupError, R as ROOT_PATH, d as ROOT_PATH_KEY, e as RegistryNotInstalledError, f as ReservedFormKeyError, S as SensitivePersistFieldError, g as SubmitErrorHandlerError, h as assignKey, l as createAttaform, o as createRegistry, s as getRegistryFromApp, t as isPathPrefix, u as isRegisterValue, x as kAttaformRegistry, C as parseDottedPath, J as useRegister, K as useRegistry, L as vRegister } from './shared/attaform.CrpjyXdO.mjs';
3
- export { D as DEVTOOLS_WINDOW_KEY, R as REDACTED, h as hydrateAttaformState, r as redactSensitiveLeaves, a as renderAttaformState } from './shared/attaform.Cghpuav8.mjs';
4
- import { n as normalizeNumericOption } from './shared/attaform.BTi-PsHr.mjs';
5
- export { A as AttaformErrorCode, d as defaultCoercionRules, a as defaultDisplayState, b as defineCoercion, i as injectForm, c as injectWizard, f as isUnset, l as lazy, u as unset, k as useForm, m as useWizard } from './shared/attaform.BTi-PsHr.mjs';
1
+ import { j as canonicalizePath, c as InvalidPathError } from './shared/attaform.BKozEdTr.mjs';
2
+ export { A as AnonPersistError, a as AttaformError, D as DEFAULT_SENSITIVE_NAMES, d as InvalidUseFormConfigError, O as OutsideSetupError, R as ROOT_PATH, e as ROOT_PATH_KEY, f as RegistryNotInstalledError, g as ReservedFormKeyError, S as SubmitErrorHandlerError, i as assignKey, m as createAttaform, p as createRegistry, r as getRegistryFromApp, s as isPathPrefix, t as isRegisterValue, w as kAttaformRegistry, B as parseDottedPath, G as useRegister, H as useRegistry, J as vRegister } from './shared/attaform.BKozEdTr.mjs';
3
+ export { D as DEVTOOLS_WINDOW_KEY, h as hydrateAttaformState, r as renderAttaformState } from './shared/attaform.EZG6fOFb.mjs';
4
+ import { n as normalizeNumericOption } from './shared/attaform.CKFbKFb6.mjs';
5
+ export { A as AttaformErrorCode, d as defaultCoercionRules, a as defaultDisplayState, b as defineCoercion, i as injectForm, c as injectWizard, f as isUnset, l as lazy, u as unset, k as useForm, m as useWizard } from './shared/attaform.CKFbKFb6.mjs';
6
6
 
7
7
  function escapeForInlineScript(json) {
8
8
  return json.replace(/[<>&\u2028\u2029]/g, (char) => {
package/dist/nuxt.d.cts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as _nuxt_schema from '@nuxt/schema';
2
- import { f as AttaformDefaults } from './shared/attaform.C0uGZQ4M.cjs';
2
+ import { f as AttaformDefaults } from './shared/attaform.SfhU0OEY.cjs';
3
3
  import 'vue';
4
4
 
5
5
  /**
package/dist/nuxt.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as _nuxt_schema from '@nuxt/schema';
2
- import { f as AttaformDefaults } from './shared/attaform.C0uGZQ4M.mjs';
2
+ import { f as AttaformDefaults } from './shared/attaform.SfhU0OEY.mjs';
3
3
  import 'vue';
4
4
 
5
5
  /**
package/dist/nuxt.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as _nuxt_schema from '@nuxt/schema';
2
- import { f as AttaformDefaults } from './shared/attaform.C0uGZQ4M.js';
2
+ import { f as AttaformDefaults } from './shared/attaform.SfhU0OEY.js';
3
3
  import 'vue';
4
4
 
5
5
  /**
@@ -58,7 +58,7 @@ function humanizePathKey(key) {
58
58
  try {
59
59
  const parsed = JSON.parse(key);
60
60
  if (Array.isArray(parsed)) {
61
- return parsed.map((seg) => typeof seg === "number" ? String(seg) : String(seg)).join(".");
61
+ return parsed.map((seg) => String(seg)).join(".");
62
62
  }
63
63
  } catch {
64
64
  }
@@ -449,5 +449,5 @@ onUnmounted(() => {
449
449
  </template>
450
450
 
451
451
  <style scoped>
452
- .atf-panel{--atf-bg:#0f172a;--atf-bg-elev:#111c33;--atf-fg:#e2e8f0;--atf-fg-muted:#94a3b8;--atf-border:rgba(148,163,184,.12);--atf-border-strong:rgba(148,163,184,.2);--atf-accent:#5b8def;--atf-key:#93c5fd;--atf-string:#86efac;--atf-number:#fbbf24;--atf-boolean:#f472b6;--atf-redacted:#f87171;--atf-muted:#64748b;--atf-row-hover:hsla(0,0%,100%,.04);--atf-error-bg:rgba(248,113,113,.1);--atf-warn-bg:rgba(251,191,36,.1);background:var(--atf-bg);color:var(--atf-fg);display:flex;flex-direction:column;font-family:system-ui,-apple-system,Segoe UI,sans-serif;font-size:13px;height:100vh;line-height:1.5}@media (prefers-color-scheme:light){.atf-panel{--atf-bg:#fff;--atf-bg-elev:#f8fafc;--atf-fg:#0f172a;--atf-fg-muted:#64748b;--atf-border:rgba(15,23,42,.08);--atf-border-strong:rgba(15,23,42,.16);--atf-key:#2563eb;--atf-string:#16a34a;--atf-number:#d97706;--atf-boolean:#db2777;--atf-redacted:#dc2626;--atf-muted:#94a3b8;--atf-row-hover:rgba(15,23,42,.04);--atf-error-bg:rgba(220,38,38,.08);--atf-warn-bg:rgba(217,119,6,.08)}}.atf-header{background:var(--atf-bg-elev);border-bottom:1px solid var(--atf-border);flex:0 0 auto;padding:.75rem 1rem}.atf-brand{align-items:center;display:flex;gap:.5rem}.atf-logo{display:block;height:22px;width:22px}.atf-title{font-size:14px;font-weight:600}.atf-version{color:var(--atf-fg-muted);font-family:ui-monospace,monospace;font-size:11px}.atf-body{display:grid;flex:1 1 auto;grid-template-columns:200px 1fr;min-height:0}.atf-sidebar{border-right:1px solid var(--atf-border);overflow-y:auto;padding:.75rem 0}.atf-sidebar-title{align-items:center;color:var(--atf-fg-muted);display:flex;font-size:11px;gap:.4em;letter-spacing:.05em;padding:0 1rem .5rem;text-transform:uppercase}.atf-count{background:var(--atf-border-strong);border-radius:999px;color:var(--atf-fg);font-size:10px;padding:0 .4em}.atf-empty{color:var(--atf-fg-muted);list-style:none;margin:0;padding:0 1rem}.atf-empty small{display:block;font-size:11px;margin-top:.4rem}.atf-empty code{background:var(--atf-border);border-radius:3px;font-size:11px;padding:.05em .35em}.atf-form-list{list-style:none;margin:0;padding:0}.atf-form-item{cursor:pointer;font-family:ui-monospace,monospace;font-size:12px;padding:.4rem 1rem;-webkit-user-select:none;-moz-user-select:none;user-select:none}.atf-form-item:hover{background:var(--atf-row-hover)}.atf-form-item.active{background:rgba(91,141,239,.12);border-left:2px solid var(--atf-accent);color:var(--atf-key);padding-left:calc(1rem - 2px)}.atf-detail{overflow-y:auto;padding:1rem 1.25rem}.atf-empty-detail{color:var(--atf-fg-muted);padding:3rem 0;text-align:center}.atf-section{margin-bottom:1.25rem}.atf-section-title{align-items:center;color:var(--atf-fg-muted);display:flex;font-size:11px;font-weight:600;gap:.5em;letter-spacing:.06em;margin:0 0 .5rem;text-transform:uppercase}.atf-section-body{background:var(--atf-bg-elev);border:1px solid var(--atf-border);border-radius:6px;padding:.6rem .8rem}.atf-badge{border-radius:999px;font-size:10px;font-weight:600;letter-spacing:.04em;padding:0 .45em}.atf-badge-error{background:var(--atf-error-bg);color:var(--atf-redacted)}.atf-badge-warn{background:var(--atf-warn-bg);color:var(--atf-number)}.atf-empty-list{color:var(--atf-fg-muted);font-size:12px;margin:0}.atf-error-list{list-style:none;margin:0;padding:0}.atf-error-list>li+li{border-top:1px solid var(--atf-border);margin-top:.6rem;padding-top:.6rem}.atf-path{color:var(--atf-key);display:block;font-family:ui-monospace,monospace;font-size:12px;margin-bottom:.25rem}.atf-error-messages{color:var(--atf-redacted);font-size:12px;list-style:none;margin:0;padding:0}.atf-error-messages>li+li{margin-top:.2rem}.atf-aggregates{display:grid;font-family:ui-monospace,monospace;font-size:12px;gap:.35rem .75rem;grid-template-columns:max-content 1fr;margin:0}.atf-aggregates dt{color:var(--atf-key)}.atf-aggregates dd{color:var(--atf-fg);margin:0}.atf-badge-neutral{background:var(--atf-border-strong);color:var(--atf-fg)}.atf-clear-btn{background:transparent;border:1px solid var(--atf-border);border-radius:4px;cursor:pointer;font:inherit;font-size:11px;padding:.1rem .5rem}.atf-clear-btn,.atf-section-hint{color:var(--atf-fg-muted);margin-left:auto}.atf-section-hint{font-size:10px;font-weight:400;letter-spacing:0;text-transform:none}.atf-fg-muted{color:var(--atf-fg-muted)}.atf-clear-btn:hover{border-color:var(--atf-border-strong);color:var(--atf-fg)}.atf-timeline{list-style:none;margin:0;max-height:18rem;overflow-y:auto;padding:0}.atf-timeline-entry{border-top:1px solid var(--atf-border)}.atf-timeline-entry:first-child{border-top:0}.atf-timeline-row{align-items:baseline;cursor:pointer;display:grid;font-family:ui-monospace,monospace;font-size:11px;gap:.6rem;grid-template-columns:7.5rem 8rem 1fr auto;padding:.4rem 0}.atf-timeline-row:hover{background:var(--atf-row-hover)}.atf-timeline-time{color:var(--atf-fg-muted)}.atf-timeline-type{color:var(--atf-key);font-weight:600}.atf-timeline-form{color:var(--atf-fg);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.atf-timeline-caret{color:var(--atf-fg-muted);text-align:center;width:1em}.atf-timeline-entry.atf-timeline-submit .atf-timeline-type{color:var(--atf-string)}.atf-timeline-entry.atf-timeline-reset .atf-timeline-type{color:var(--atf-redacted)}.atf-timeline-detail{border-top:1px dashed var(--atf-border);margin-top:-1px;padding:.4rem 0 .6rem 7.5rem}
452
+ .atf-panel{--atf-bg:#0f172a;--atf-bg-elev:#111c33;--atf-fg:#e2e8f0;--atf-fg-muted:#94a3b8;--atf-border:rgba(148,163,184,.12);--atf-border-strong:rgba(148,163,184,.2);--atf-accent:#5b8def;--atf-key:#93c5fd;--atf-string:#86efac;--atf-number:#fbbf24;--atf-boolean:#f472b6;--atf-danger:#f87171;--atf-muted:#64748b;--atf-row-hover:hsla(0,0%,100%,.04);--atf-error-bg:rgba(248,113,113,.1);--atf-warn-bg:rgba(251,191,36,.1);background:var(--atf-bg);color:var(--atf-fg);display:flex;flex-direction:column;font-family:system-ui,-apple-system,Segoe UI,sans-serif;font-size:13px;height:100vh;line-height:1.5}@media (prefers-color-scheme:light){.atf-panel{--atf-bg:#fff;--atf-bg-elev:#f8fafc;--atf-fg:#0f172a;--atf-fg-muted:#64748b;--atf-border:rgba(15,23,42,.08);--atf-border-strong:rgba(15,23,42,.16);--atf-key:#2563eb;--atf-string:#16a34a;--atf-number:#d97706;--atf-boolean:#db2777;--atf-danger:#dc2626;--atf-muted:#94a3b8;--atf-row-hover:rgba(15,23,42,.04);--atf-error-bg:rgba(220,38,38,.08);--atf-warn-bg:rgba(217,119,6,.08)}}.atf-header{background:var(--atf-bg-elev);border-bottom:1px solid var(--atf-border);flex:0 0 auto;padding:.75rem 1rem}.atf-brand{align-items:center;display:flex;gap:.5rem}.atf-logo{display:block;height:22px;width:22px}.atf-title{font-size:14px;font-weight:600}.atf-version{color:var(--atf-fg-muted);font-family:ui-monospace,monospace;font-size:11px}.atf-body{display:grid;flex:1 1 auto;grid-template-columns:200px 1fr;min-height:0}.atf-sidebar{border-right:1px solid var(--atf-border);overflow-y:auto;padding:.75rem 0}.atf-sidebar-title{align-items:center;color:var(--atf-fg-muted);display:flex;font-size:11px;gap:.4em;letter-spacing:.05em;padding:0 1rem .5rem;text-transform:uppercase}.atf-count{background:var(--atf-border-strong);border-radius:999px;color:var(--atf-fg);font-size:10px;padding:0 .4em}.atf-empty{color:var(--atf-fg-muted);list-style:none;margin:0;padding:0 1rem}.atf-empty small{display:block;font-size:11px;margin-top:.4rem}.atf-empty code{background:var(--atf-border);border-radius:3px;font-size:11px;padding:.05em .35em}.atf-form-list{list-style:none;margin:0;padding:0}.atf-form-item{cursor:pointer;font-family:ui-monospace,monospace;font-size:12px;padding:.4rem 1rem;-webkit-user-select:none;-moz-user-select:none;user-select:none}.atf-form-item:hover{background:var(--atf-row-hover)}.atf-form-item.active{background:rgba(91,141,239,.12);border-left:2px solid var(--atf-accent);color:var(--atf-key);padding-left:calc(1rem - 2px)}.atf-detail{overflow-y:auto;padding:1rem 1.25rem}.atf-empty-detail{color:var(--atf-fg-muted);padding:3rem 0;text-align:center}.atf-section{margin-bottom:1.25rem}.atf-section-title{align-items:center;color:var(--atf-fg-muted);display:flex;font-size:11px;font-weight:600;gap:.5em;letter-spacing:.06em;margin:0 0 .5rem;text-transform:uppercase}.atf-section-body{background:var(--atf-bg-elev);border:1px solid var(--atf-border);border-radius:6px;padding:.6rem .8rem}.atf-badge{border-radius:999px;font-size:10px;font-weight:600;letter-spacing:.04em;padding:0 .45em}.atf-badge-error{background:var(--atf-error-bg);color:var(--atf-danger)}.atf-badge-warn{background:var(--atf-warn-bg);color:var(--atf-number)}.atf-empty-list{color:var(--atf-fg-muted);font-size:12px;margin:0}.atf-error-list{list-style:none;margin:0;padding:0}.atf-error-list>li+li{border-top:1px solid var(--atf-border);margin-top:.6rem;padding-top:.6rem}.atf-path{color:var(--atf-key);display:block;font-family:ui-monospace,monospace;font-size:12px;margin-bottom:.25rem}.atf-error-messages{color:var(--atf-danger);font-size:12px;list-style:none;margin:0;padding:0}.atf-error-messages>li+li{margin-top:.2rem}.atf-aggregates{display:grid;font-family:ui-monospace,monospace;font-size:12px;gap:.35rem .75rem;grid-template-columns:max-content 1fr;margin:0}.atf-aggregates dt{color:var(--atf-key)}.atf-aggregates dd{color:var(--atf-fg);margin:0}.atf-badge-neutral{background:var(--atf-border-strong);color:var(--atf-fg)}.atf-clear-btn{background:transparent;border:1px solid var(--atf-border);border-radius:4px;cursor:pointer;font:inherit;font-size:11px;padding:.1rem .5rem}.atf-clear-btn,.atf-section-hint{color:var(--atf-fg-muted);margin-left:auto}.atf-section-hint{font-size:10px;font-weight:400;letter-spacing:0;text-transform:none}.atf-fg-muted{color:var(--atf-fg-muted)}.atf-clear-btn:hover{border-color:var(--atf-border-strong);color:var(--atf-fg)}.atf-timeline{list-style:none;margin:0;max-height:18rem;overflow-y:auto;padding:0}.atf-timeline-entry{border-top:1px solid var(--atf-border)}.atf-timeline-entry:first-child{border-top:0}.atf-timeline-row{align-items:baseline;cursor:pointer;display:grid;font-family:ui-monospace,monospace;font-size:11px;gap:.6rem;grid-template-columns:7.5rem 8rem 1fr auto;padding:.4rem 0}.atf-timeline-row:hover{background:var(--atf-row-hover)}.atf-timeline-time{color:var(--atf-fg-muted)}.atf-timeline-type{color:var(--atf-key);font-weight:600}.atf-timeline-form{color:var(--atf-fg);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.atf-timeline-caret{color:var(--atf-fg-muted);text-align:center;width:1em}.atf-timeline-entry.atf-timeline-submit .atf-timeline-type{color:var(--atf-string)}.atf-timeline-entry.atf-timeline-reset .atf-timeline-type{color:var(--atf-danger)}.atf-timeline-detail{border-top:1px dashed var(--atf-border);margin-top:-1px;padding:.4rem 0 .6rem 7.5rem}
453
453
  </style>
@@ -11,9 +11,7 @@ type __VLS_Props = {
11
11
  path?: ReadonlyArray<string | number>;
12
12
  /**
13
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.
14
+ * become click-to-edit. Values render raw (DevTools does not redact).
17
15
  */
18
16
  editable?: boolean | undefined;
19
17
  onEdit?: ((path: ReadonlyArray<string | number>, next: unknown) => void) | undefined;
@@ -11,9 +11,7 @@ type __VLS_Props = {
11
11
  path?: ReadonlyArray<string | number>;
12
12
  /**
13
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.
14
+ * become click-to-edit. Values render raw (DevTools does not redact).
17
15
  */
18
16
  editable?: boolean | undefined;
19
17
  onEdit?: ((path: ReadonlyArray<string | number>, next: unknown) => void) | undefined;
@@ -1,8 +1,8 @@
1
1
  'use strict';
2
2
 
3
3
  const app = require('nuxt/app');
4
- const devtoolsShared = require('../../shared/attaform.CoxJ8Qm8.cjs');
5
- const paths = require('../../shared/attaform.BqEfHpVB.cjs');
4
+ const devtoolsShared = require('../../shared/attaform.BPxsYtTe.cjs');
5
+ const paths = require('../../shared/attaform.BPy-4qRx.cjs');
6
6
 
7
7
  var attaform_default = app.defineNuxtPlugin({
8
8
  // `enforce: 'pre'` makes the "we run before any component's setup" claim
@@ -1,6 +1,6 @@
1
1
  import { defineNuxtPlugin, useRuntimeConfig, useRoute } from 'nuxt/app';
2
- import { a as renderAttaformState, h as hydrateAttaformState, D as DEVTOOLS_WINDOW_KEY } from '../../shared/attaform.Cghpuav8.mjs';
3
- import { l as createAttaform, y as kAttaformWizardActiveStepResolver, s as getRegistryFromApp } from '../../shared/attaform.CrpjyXdO.mjs';
2
+ import { r as renderAttaformState, h as hydrateAttaformState, D as DEVTOOLS_WINDOW_KEY } from '../../shared/attaform.EZG6fOFb.mjs';
3
+ import { m as createAttaform, x as kAttaformWizardActiveStepResolver, r as getRegistryFromApp } from '../../shared/attaform.BKozEdTr.mjs';
4
4
 
5
5
  var attaform_default = defineNuxtPlugin({
6
6
  // `enforce: 'pre'` makes the "we run before any component's setup" claim