attaform 0.16.3 → 0.17.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 (86) hide show
  1. package/README.md +4 -2
  2. package/dist/chunks/devtools.cjs +19 -12
  3. package/dist/chunks/devtools.cjs.map +1 -1
  4. package/dist/chunks/devtools.mjs +19 -12
  5. package/dist/chunks/devtools.mjs.map +1 -1
  6. package/dist/chunks/indexeddb.cjs +1 -1
  7. package/dist/chunks/indexeddb.mjs +1 -1
  8. package/dist/chunks/local-storage.cjs +1 -1
  9. package/dist/chunks/local-storage.mjs +1 -1
  10. package/dist/chunks/session-storage.cjs +1 -1
  11. package/dist/chunks/session-storage.mjs +1 -1
  12. package/dist/index.cjs +27 -7
  13. package/dist/index.cjs.map +1 -1
  14. package/dist/index.d.cts +80 -8
  15. package/dist/index.d.mts +80 -8
  16. package/dist/index.d.ts +80 -8
  17. package/dist/index.mjs +28 -9
  18. package/dist/index.mjs.map +1 -1
  19. package/dist/nuxt.d.cts +1 -1
  20. package/dist/nuxt.d.mts +1 -1
  21. package/dist/nuxt.d.ts +1 -1
  22. package/dist/runtime/plugins/attaform.cjs +3 -3
  23. package/dist/runtime/plugins/attaform.cjs.map +1 -1
  24. package/dist/runtime/plugins/attaform.mjs +3 -3
  25. package/dist/runtime/plugins/attaform.mjs.map +1 -1
  26. package/dist/shared/{attaform.KrNw10aW.cjs → attaform.0Wg7UEeX.cjs} +60 -20
  27. package/dist/shared/attaform.0Wg7UEeX.cjs.map +1 -0
  28. package/dist/shared/attaform.AOgGyRoI.d.cts +65 -0
  29. package/dist/shared/{attaform.lFNwBcA3.d.ts → attaform.B0zue7zt.d.ts} +1 -1
  30. package/dist/shared/{attaform.c_NzdRyc.cjs → attaform.BBM2muQ9.cjs} +7 -3
  31. package/dist/shared/attaform.BBM2muQ9.cjs.map +1 -0
  32. package/dist/shared/{attaform.C9Ph2SMx.cjs → attaform.BFumZXY2.cjs} +1514 -391
  33. package/dist/shared/attaform.BFumZXY2.cjs.map +1 -0
  34. package/dist/shared/attaform.BQ-iGGWd.d.mts +65 -0
  35. package/dist/shared/{attaform.DILbdvfo.mjs → attaform.BT55rDNN.mjs} +1514 -393
  36. package/dist/shared/attaform.BT55rDNN.mjs.map +1 -0
  37. package/dist/shared/{attaform._EqYNPYF.d.mts → attaform.BYbsV2Wv.d.cts} +738 -138
  38. package/dist/shared/{attaform._EqYNPYF.d.ts → attaform.BYbsV2Wv.d.mts} +738 -138
  39. package/dist/shared/{attaform._EqYNPYF.d.cts → attaform.BYbsV2Wv.d.ts} +738 -138
  40. package/dist/shared/{attaform.DGuGGNg9.cjs → attaform.C6_zOf8x.cjs} +232 -113
  41. package/dist/shared/attaform.C6_zOf8x.cjs.map +1 -0
  42. package/dist/shared/{attaform.CJttVxRj.cjs → attaform.C8LVFVVe.cjs} +2 -2
  43. package/dist/shared/{attaform.CJttVxRj.cjs.map → attaform.C8LVFVVe.cjs.map} +1 -1
  44. package/dist/shared/{attaform.BfMxsfmE.mjs → attaform.CIEQgJnM.mjs} +143 -78
  45. package/dist/shared/attaform.CIEQgJnM.mjs.map +1 -0
  46. package/dist/shared/attaform.CX9v2M8k.d.ts +65 -0
  47. package/dist/shared/{attaform.XYOMTvuO.mjs → attaform.Cj0pCNVn.mjs} +232 -113
  48. package/dist/shared/attaform.Cj0pCNVn.mjs.map +1 -0
  49. package/dist/shared/{attaform.DLnKT7wk.d.cts → attaform.ClfCi1i2.d.mts} +1 -1
  50. package/dist/shared/{attaform.CFA6y0KF.mjs → attaform.D6Q5ZP8L.mjs} +60 -20
  51. package/dist/shared/attaform.D6Q5ZP8L.mjs.map +1 -0
  52. package/dist/shared/{attaform.Bls_kFR6.d.mts → attaform.D7lomopc.d.cts} +1 -1
  53. package/dist/shared/{attaform.rIRYSUI1.cjs → attaform.Dee2rU1P.cjs} +145 -77
  54. package/dist/shared/attaform.Dee2rU1P.cjs.map +1 -0
  55. package/dist/shared/{attaform.CINUMjPq.mjs → attaform.Vo-Kft0t.mjs} +2 -2
  56. package/dist/shared/{attaform.CINUMjPq.mjs.map → attaform.Vo-Kft0t.mjs.map} +1 -1
  57. package/dist/shared/{attaform.jrxE_xZw.mjs → attaform.h1sq3BFu.mjs} +6 -4
  58. package/dist/shared/attaform.h1sq3BFu.mjs.map +1 -0
  59. package/dist/zod-v3.cjs +3 -3
  60. package/dist/zod-v3.d.cts +5 -5
  61. package/dist/zod-v3.d.mts +5 -5
  62. package/dist/zod-v3.d.ts +5 -5
  63. package/dist/zod-v3.mjs +3 -3
  64. package/dist/zod-v4.cjs +3 -3
  65. package/dist/zod-v4.d.cts +16 -42
  66. package/dist/zod-v4.d.mts +16 -42
  67. package/dist/zod-v4.d.ts +16 -42
  68. package/dist/zod-v4.mjs +3 -3
  69. package/dist/zod.cjs +4 -4
  70. package/dist/zod.cjs.map +1 -1
  71. package/dist/zod.d.cts +6 -5
  72. package/dist/zod.d.mts +6 -5
  73. package/dist/zod.d.ts +6 -5
  74. package/dist/zod.mjs +5 -5
  75. package/dist/zod.mjs.map +1 -1
  76. package/package.json +3 -8
  77. package/dist/shared/attaform.BfMxsfmE.mjs.map +0 -1
  78. package/dist/shared/attaform.C9Ph2SMx.cjs.map +0 -1
  79. package/dist/shared/attaform.CFA6y0KF.mjs.map +0 -1
  80. package/dist/shared/attaform.DGuGGNg9.cjs.map +0 -1
  81. package/dist/shared/attaform.DILbdvfo.mjs.map +0 -1
  82. package/dist/shared/attaform.KrNw10aW.cjs.map +0 -1
  83. package/dist/shared/attaform.XYOMTvuO.mjs.map +0 -1
  84. package/dist/shared/attaform.c_NzdRyc.cjs.map +0 -1
  85. package/dist/shared/attaform.jrxE_xZw.mjs.map +0 -1
  86. package/dist/shared/attaform.rIRYSUI1.cjs.map +0 -1
package/dist/index.d.cts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { App, InjectionKey, Plugin } from 'vue';
2
- import { C as CoercionRegistry, S as SlimPrimitiveKind, a as CoercionEntry, A as AttaformDefaults, F as FormKey, G as GenericForm, U as UseFormConfiguration, b as AbstractSchema, D as DeepPartial, c as DefaultValuesShape, d as UseFormReturnType, R as RegisterValue, e as RegisterModelDynamicCustomDirective, V as ValidationError, f as ApiErrorEnvelope, g as ApiErrorDetails } from './shared/attaform._EqYNPYF.cjs';
3
- export { h as ApiErrorEntry, i as ArrayItem, j as ArrayPath, k as CoercionResult, l as CustomDirectiveRegisterAssignerFn, m as DefaultValuesResponse, n as FieldMetaPayload, o as FieldState, p as FieldStateMap, q as FieldStateMapEntry, r as FlatPath, s as FormErrorRecord, t as FormErrorsSurface, u as FormMeta, v as FormStorage, w as FormStorageKind, H as HandleSubmit, x as HistoryConfig, I as IsTuple, y as IsUnion, J as JoinSegments, K as KeyofUnion, L as LiftedValueShape, M as MetaTrackerValue, N as NestedReadType, z as NestedType, O as OnError, B as OnInvalidSubmitPolicy, E as OnSubmit, P as Path, Q as PathKey, T as PendingValidationStatus, W as PersistConfig, X as PersistConfigOptions, Y as PersistIncludeMode, Z as ROOT_PATH, _ as ROOT_PATH_KEY, $ as ReactiveValidationStatus, a0 as RegisterDirective, a1 as RegisterFlatPath, a2 as RegisterOptions, a3 as RegisterSelectModifier, a4 as RegisterTextModifier, a5 as RegisterTransform, a6 as Segment, a7 as SetValueCallback, a8 as SetValuePayload, a9 as SettledValidationStatus, aa as SlimRuntimeOf, ab as SubmitHandler, ac as Unset, ad as ValidateOn, ae as ValidateOnConfig, af as ValidationResponse, ag as ValidationResponseWithoutValue, ah as ValueOfUnion, ai as WriteMeta, aj as WriteShape, ak as canonicalizePath, al as isPathPrefix, am as isUnset, an as parseDottedPath, ao as unset } from './shared/attaform._EqYNPYF.cjs';
4
- export { A as AttaformErrorCode, i as injectForm, u as useRegister } from './shared/attaform.DLnKT7wk.cjs';
2
+ import { C as CoercionRegistry, S as SlimPrimitiveKind, a as CoercionEntry, A as AttaformDefaults, F as FormKey, G as GenericForm, U as UseFormConfiguration, b as AbstractSchema, D as DeepPartial, c as DefaultValuesShape, d as UseFormReturnType, R as RegisterValue, e as RegisterModelDynamicCustomDirective, f as ShouldShowErrors, V as ValidationError, g as ApiErrorEnvelope, h as ApiErrorDetails } from './shared/attaform.BYbsV2Wv.cjs';
3
+ export { i as ApiErrorEntry, j as ArrayItem, k as ArrayPath, l as CoercionResult, m as CustomDirectiveRegisterAssignerFn, n as DefaultValuesResponse, o as FieldMetaPayload, p as FieldState, q as FieldStateMap, r as FieldStateMapEntry, s as FlatPath, t as FormErrorRecord, u as FormErrorsSurface, v as FormMeta, w as FormStorage, x 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, E as OnInvalidSubmitPolicy, P as OnSubmit, Q as Path, T as PathKey, W as PendingValidationStatus, X as PersistConfig, Y as PersistConfigOptions, Z as PersistIncludeMode, _ as ROOT_PATH, $ as ROOT_PATH_KEY, a0 as ReactiveValidationStatus, a1 as RegisterDirective, a2 as RegisterFlatPath, a3 as RegisterOptions, a4 as RegisterSelectModifier, a5 as RegisterTextModifier, a6 as RegisterTransform, a7 as Segment, a8 as SetValueCallback, a9 as SetValuePayload, aa as SettledValidationStatus, ab as ShouldShowErrorsConfig, ac as SlimRuntimeOf, ad as SubmitHandler, ae as Unset, af as ValidateOn, ag as ValidateOnConfig, ah as ValidationResponse, ai as ValidationResponseWithoutValue, aj as ValueOfUnion, ak as WriteMeta, al as WriteShape, am as canonicalizePath, an as isPathPrefix, ao as isUnset, ap as parseDottedPath, aq as unset } from './shared/attaform.BYbsV2Wv.cjs';
4
+ export { A as AttaformErrorCode, i as injectForm, u as useRegister } from './shared/attaform.D7lomopc.cjs';
5
5
  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';
6
6
 
7
7
  /**
@@ -53,12 +53,20 @@ declare const defaultCoercionRules: CoercionRegistry;
53
53
  * of truth instead of sniffing `import.meta.*` (bundler-specific) at each
54
54
  * call site.
55
55
  *
56
- * Consumers can override explicitly via `createAttaform({ ssr: true })`;
57
- * the default heuristic handles the common Node-vs-browser split without
58
- * relying on any bundler-injected flag.
56
+ * Consumers can override the heuristic explicitly via
57
+ * `createAttaform({ ssr: true })`; the default handles the common
58
+ * Node-vs-browser split without relying on any bundler-injected flag.
59
59
  */
60
60
  interface SSRDetectOptions {
61
- override?: boolean;
61
+ /**
62
+ * Force SSR-vs-client mode, bypassing the `typeof window` heuristic.
63
+ * `true` activates the SSR code paths (no devtools, no persistence
64
+ * wiring, payload serialisation enabled); `false` forces client mode.
65
+ * The Nuxt plugin sets this from `import.meta.server` so SSR detection
66
+ * never depends on whether `window` is polyfilled. Tests that need to
67
+ * exercise the SSR code paths under jsdom pass `ssr: true`.
68
+ */
69
+ ssr?: boolean;
62
70
  }
63
71
 
64
72
  /**
@@ -368,6 +376,70 @@ declare function isRegisterValue<Value = unknown>(val: unknown): val is Register
368
376
  */
369
377
  declare const vRegister: RegisterModelDynamicCustomDirective;
370
378
 
379
+ /**
380
+ * Library-default heuristic for `shouldShowErrors`. Drives
381
+ * `field.showErrors` and `form.meta.showErrors` whenever the consumer
382
+ * has not configured an override at either the plugin or per-form
383
+ * level.
384
+ *
385
+ * Reads "show errors after the first submit attempt, OR after the
386
+ * user has interacted (`touched`) and made a change (`dirty`)." The
387
+ * framework already gates on `errors.length > 0` before invoking the
388
+ * predicate, so the body only decides *when* to surface existing
389
+ * errors — not whether errors exist.
390
+ *
391
+ * Public re-export so adopters can compose with this without
392
+ * copy-pasting the rule body. A layered predicate that adds a
393
+ * special case but otherwise defers to the library default picks up
394
+ * future heuristic refinements automatically:
395
+ *
396
+ * ```ts
397
+ * import { defaultShouldShowErrors } from 'attaform'
398
+ *
399
+ * useForm({
400
+ * schema,
401
+ * shouldShowErrors: (field, formMeta) =>
402
+ * field.path[0] === 'urgent' || defaultShouldShowErrors(field, formMeta),
403
+ * })
404
+ * ```
405
+ */
406
+ declare const defaultShouldShowErrors: ShouldShowErrors;
407
+
408
+ /**
409
+ * The library's built-in conservative set of identifier name stems that
410
+ * flag a path segment as "this looks like data the consumer almost
411
+ * certainly does not want serialised to client-side storage or
412
+ * broadcast across tabs."
413
+ *
414
+ * Each entry is a NAME STEM, not a regex. Matching is case-insensitive
415
+ * and tolerant of separator variants — `'card_number'` matches the
416
+ * segments `'card_number'`, `'card-number'`, `'cardNumber'`, and
417
+ * `'cardnumber'`. Short stems (compact length ≤ 5) get word-boundary
418
+ * anchors to avoid common false positives — `'pin'` matches `'pin'`
419
+ * and `'user_pin'` but not `'pinned'`; `'token'` matches `'token'` but
420
+ * not `'tokenizer'`. Longer stems match anywhere (`'password'` matches
421
+ * `'password'`, `'passwords'`, `'userPassword'`).
422
+ *
423
+ * Consumers extend or replace via per-form or global config:
424
+ *
425
+ * ```ts
426
+ * createAttaform({
427
+ * defaults: { sensitiveNames: [...DEFAULT_SENSITIVE_NAMES, 'mrn', 'tax_id'] }
428
+ * })
429
+ * ```
430
+ *
431
+ * The same resolved predicate gates persistence writes, multi-tab sync
432
+ * broadcasts, AND the DevTools redact walk — one source of truth for
433
+ * "what counts as sensitive" across every surface.
434
+ *
435
+ * **Non-goals.** This is not a soundness guarantee. Adversarial paths
436
+ * (`'sensitive_data'`, `'CCV'` instead of `'CVV'`) can slip through.
437
+ * The intent is a code-review trigger for the common-case footgun
438
+ * plus a defense-in-depth filter on the cross-tab and DevTools
439
+ * surfaces.
440
+ */
441
+ declare const DEFAULT_SENSITIVE_NAMES: readonly string[];
442
+
371
443
  /**
372
444
  * Result of `parseApiErrors`. Branch on `ok` to handle the two cases:
373
445
  *
@@ -494,5 +566,5 @@ declare const PARSE_API_ERRORS_DEFAULTS: {
494
566
  */
495
567
  declare function parseApiErrors(payload: ApiErrorEnvelope | ApiErrorDetails | null | undefined | unknown, options: ParseApiErrorsOptions): ParseApiErrorsResult;
496
568
 
497
- export { AbstractSchema, ApiErrorDetails, ApiErrorEnvelope, AttaformDefaults, CoercionEntry, CoercionRegistry, DeepPartial, DefaultValuesShape, FormKey, GenericForm, PARSE_API_ERRORS_DEFAULTS, RegisterValue, SlimPrimitiveKind, UseFormConfiguration, UseFormReturnType, ValidationError, assignKey, createAttaform, createRegistry, defaultCoercionRules, defineCoercion, escapeForInlineScript, getRegistryFromApp, hydrateAttaformState, isRegisterValue, kAttaformRegistry, parseApiErrors, renderAttaformState, useAbstractForm as useForm, useRegistry, vRegister };
569
+ export { AbstractSchema, ApiErrorDetails, ApiErrorEnvelope, AttaformDefaults, CoercionEntry, CoercionRegistry, DEFAULT_SENSITIVE_NAMES, DeepPartial, DefaultValuesShape, FormKey, GenericForm, PARSE_API_ERRORS_DEFAULTS, RegisterValue, ShouldShowErrors, SlimPrimitiveKind, UseFormConfiguration, UseFormReturnType, ValidationError, assignKey, createAttaform, createRegistry, defaultCoercionRules, defaultShouldShowErrors, defineCoercion, escapeForInlineScript, getRegistryFromApp, hydrateAttaformState, isRegisterValue, kAttaformRegistry, parseApiErrors, renderAttaformState, useAbstractForm as useForm, useRegistry, vRegister };
498
570
  export type { AttaformPluginOptions, AttaformRegistry, ParseApiErrorsOptions, ParseApiErrorsResult, SerializedAttaformState, SerializedFormData };
package/dist/index.d.mts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { App, InjectionKey, Plugin } from 'vue';
2
- import { C as CoercionRegistry, S as SlimPrimitiveKind, a as CoercionEntry, A as AttaformDefaults, F as FormKey, G as GenericForm, U as UseFormConfiguration, b as AbstractSchema, D as DeepPartial, c as DefaultValuesShape, d as UseFormReturnType, R as RegisterValue, e as RegisterModelDynamicCustomDirective, V as ValidationError, f as ApiErrorEnvelope, g as ApiErrorDetails } from './shared/attaform._EqYNPYF.mjs';
3
- export { h as ApiErrorEntry, i as ArrayItem, j as ArrayPath, k as CoercionResult, l as CustomDirectiveRegisterAssignerFn, m as DefaultValuesResponse, n as FieldMetaPayload, o as FieldState, p as FieldStateMap, q as FieldStateMapEntry, r as FlatPath, s as FormErrorRecord, t as FormErrorsSurface, u as FormMeta, v as FormStorage, w as FormStorageKind, H as HandleSubmit, x as HistoryConfig, I as IsTuple, y as IsUnion, J as JoinSegments, K as KeyofUnion, L as LiftedValueShape, M as MetaTrackerValue, N as NestedReadType, z as NestedType, O as OnError, B as OnInvalidSubmitPolicy, E as OnSubmit, P as Path, Q as PathKey, T as PendingValidationStatus, W as PersistConfig, X as PersistConfigOptions, Y as PersistIncludeMode, Z as ROOT_PATH, _ as ROOT_PATH_KEY, $ as ReactiveValidationStatus, a0 as RegisterDirective, a1 as RegisterFlatPath, a2 as RegisterOptions, a3 as RegisterSelectModifier, a4 as RegisterTextModifier, a5 as RegisterTransform, a6 as Segment, a7 as SetValueCallback, a8 as SetValuePayload, a9 as SettledValidationStatus, aa as SlimRuntimeOf, ab as SubmitHandler, ac as Unset, ad as ValidateOn, ae as ValidateOnConfig, af as ValidationResponse, ag as ValidationResponseWithoutValue, ah as ValueOfUnion, ai as WriteMeta, aj as WriteShape, ak as canonicalizePath, al as isPathPrefix, am as isUnset, an as parseDottedPath, ao as unset } from './shared/attaform._EqYNPYF.mjs';
4
- export { A as AttaformErrorCode, i as injectForm, u as useRegister } from './shared/attaform.Bls_kFR6.mjs';
2
+ import { C as CoercionRegistry, S as SlimPrimitiveKind, a as CoercionEntry, A as AttaformDefaults, F as FormKey, G as GenericForm, U as UseFormConfiguration, b as AbstractSchema, D as DeepPartial, c as DefaultValuesShape, d as UseFormReturnType, R as RegisterValue, e as RegisterModelDynamicCustomDirective, f as ShouldShowErrors, V as ValidationError, g as ApiErrorEnvelope, h as ApiErrorDetails } from './shared/attaform.BYbsV2Wv.mjs';
3
+ export { i as ApiErrorEntry, j as ArrayItem, k as ArrayPath, l as CoercionResult, m as CustomDirectiveRegisterAssignerFn, n as DefaultValuesResponse, o as FieldMetaPayload, p as FieldState, q as FieldStateMap, r as FieldStateMapEntry, s as FlatPath, t as FormErrorRecord, u as FormErrorsSurface, v as FormMeta, w as FormStorage, x 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, E as OnInvalidSubmitPolicy, P as OnSubmit, Q as Path, T as PathKey, W as PendingValidationStatus, X as PersistConfig, Y as PersistConfigOptions, Z as PersistIncludeMode, _ as ROOT_PATH, $ as ROOT_PATH_KEY, a0 as ReactiveValidationStatus, a1 as RegisterDirective, a2 as RegisterFlatPath, a3 as RegisterOptions, a4 as RegisterSelectModifier, a5 as RegisterTextModifier, a6 as RegisterTransform, a7 as Segment, a8 as SetValueCallback, a9 as SetValuePayload, aa as SettledValidationStatus, ab as ShouldShowErrorsConfig, ac as SlimRuntimeOf, ad as SubmitHandler, ae as Unset, af as ValidateOn, ag as ValidateOnConfig, ah as ValidationResponse, ai as ValidationResponseWithoutValue, aj as ValueOfUnion, ak as WriteMeta, al as WriteShape, am as canonicalizePath, an as isPathPrefix, ao as isUnset, ap as parseDottedPath, aq as unset } from './shared/attaform.BYbsV2Wv.mjs';
4
+ export { A as AttaformErrorCode, i as injectForm, u as useRegister } from './shared/attaform.ClfCi1i2.mjs';
5
5
  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';
6
6
 
7
7
  /**
@@ -53,12 +53,20 @@ declare const defaultCoercionRules: CoercionRegistry;
53
53
  * of truth instead of sniffing `import.meta.*` (bundler-specific) at each
54
54
  * call site.
55
55
  *
56
- * Consumers can override explicitly via `createAttaform({ ssr: true })`;
57
- * the default heuristic handles the common Node-vs-browser split without
58
- * relying on any bundler-injected flag.
56
+ * Consumers can override the heuristic explicitly via
57
+ * `createAttaform({ ssr: true })`; the default handles the common
58
+ * Node-vs-browser split without relying on any bundler-injected flag.
59
59
  */
60
60
  interface SSRDetectOptions {
61
- override?: boolean;
61
+ /**
62
+ * Force SSR-vs-client mode, bypassing the `typeof window` heuristic.
63
+ * `true` activates the SSR code paths (no devtools, no persistence
64
+ * wiring, payload serialisation enabled); `false` forces client mode.
65
+ * The Nuxt plugin sets this from `import.meta.server` so SSR detection
66
+ * never depends on whether `window` is polyfilled. Tests that need to
67
+ * exercise the SSR code paths under jsdom pass `ssr: true`.
68
+ */
69
+ ssr?: boolean;
62
70
  }
63
71
 
64
72
  /**
@@ -368,6 +376,70 @@ declare function isRegisterValue<Value = unknown>(val: unknown): val is Register
368
376
  */
369
377
  declare const vRegister: RegisterModelDynamicCustomDirective;
370
378
 
379
+ /**
380
+ * Library-default heuristic for `shouldShowErrors`. Drives
381
+ * `field.showErrors` and `form.meta.showErrors` whenever the consumer
382
+ * has not configured an override at either the plugin or per-form
383
+ * level.
384
+ *
385
+ * Reads "show errors after the first submit attempt, OR after the
386
+ * user has interacted (`touched`) and made a change (`dirty`)." The
387
+ * framework already gates on `errors.length > 0` before invoking the
388
+ * predicate, so the body only decides *when* to surface existing
389
+ * errors — not whether errors exist.
390
+ *
391
+ * Public re-export so adopters can compose with this without
392
+ * copy-pasting the rule body. A layered predicate that adds a
393
+ * special case but otherwise defers to the library default picks up
394
+ * future heuristic refinements automatically:
395
+ *
396
+ * ```ts
397
+ * import { defaultShouldShowErrors } from 'attaform'
398
+ *
399
+ * useForm({
400
+ * schema,
401
+ * shouldShowErrors: (field, formMeta) =>
402
+ * field.path[0] === 'urgent' || defaultShouldShowErrors(field, formMeta),
403
+ * })
404
+ * ```
405
+ */
406
+ declare const defaultShouldShowErrors: ShouldShowErrors;
407
+
408
+ /**
409
+ * The library's built-in conservative set of identifier name stems that
410
+ * flag a path segment as "this looks like data the consumer almost
411
+ * certainly does not want serialised to client-side storage or
412
+ * broadcast across tabs."
413
+ *
414
+ * Each entry is a NAME STEM, not a regex. Matching is case-insensitive
415
+ * and tolerant of separator variants — `'card_number'` matches the
416
+ * segments `'card_number'`, `'card-number'`, `'cardNumber'`, and
417
+ * `'cardnumber'`. Short stems (compact length ≤ 5) get word-boundary
418
+ * anchors to avoid common false positives — `'pin'` matches `'pin'`
419
+ * and `'user_pin'` but not `'pinned'`; `'token'` matches `'token'` but
420
+ * not `'tokenizer'`. Longer stems match anywhere (`'password'` matches
421
+ * `'password'`, `'passwords'`, `'userPassword'`).
422
+ *
423
+ * Consumers extend or replace via per-form or global config:
424
+ *
425
+ * ```ts
426
+ * createAttaform({
427
+ * defaults: { sensitiveNames: [...DEFAULT_SENSITIVE_NAMES, 'mrn', 'tax_id'] }
428
+ * })
429
+ * ```
430
+ *
431
+ * The same resolved predicate gates persistence writes, multi-tab sync
432
+ * broadcasts, AND the DevTools redact walk — one source of truth for
433
+ * "what counts as sensitive" across every surface.
434
+ *
435
+ * **Non-goals.** This is not a soundness guarantee. Adversarial paths
436
+ * (`'sensitive_data'`, `'CCV'` instead of `'CVV'`) can slip through.
437
+ * The intent is a code-review trigger for the common-case footgun
438
+ * plus a defense-in-depth filter on the cross-tab and DevTools
439
+ * surfaces.
440
+ */
441
+ declare const DEFAULT_SENSITIVE_NAMES: readonly string[];
442
+
371
443
  /**
372
444
  * Result of `parseApiErrors`. Branch on `ok` to handle the two cases:
373
445
  *
@@ -494,5 +566,5 @@ declare const PARSE_API_ERRORS_DEFAULTS: {
494
566
  */
495
567
  declare function parseApiErrors(payload: ApiErrorEnvelope | ApiErrorDetails | null | undefined | unknown, options: ParseApiErrorsOptions): ParseApiErrorsResult;
496
568
 
497
- export { AbstractSchema, ApiErrorDetails, ApiErrorEnvelope, AttaformDefaults, CoercionEntry, CoercionRegistry, DeepPartial, DefaultValuesShape, FormKey, GenericForm, PARSE_API_ERRORS_DEFAULTS, RegisterValue, SlimPrimitiveKind, UseFormConfiguration, UseFormReturnType, ValidationError, assignKey, createAttaform, createRegistry, defaultCoercionRules, defineCoercion, escapeForInlineScript, getRegistryFromApp, hydrateAttaformState, isRegisterValue, kAttaformRegistry, parseApiErrors, renderAttaformState, useAbstractForm as useForm, useRegistry, vRegister };
569
+ export { AbstractSchema, ApiErrorDetails, ApiErrorEnvelope, AttaformDefaults, CoercionEntry, CoercionRegistry, DEFAULT_SENSITIVE_NAMES, DeepPartial, DefaultValuesShape, FormKey, GenericForm, PARSE_API_ERRORS_DEFAULTS, RegisterValue, ShouldShowErrors, SlimPrimitiveKind, UseFormConfiguration, UseFormReturnType, ValidationError, assignKey, createAttaform, createRegistry, defaultCoercionRules, defaultShouldShowErrors, defineCoercion, escapeForInlineScript, getRegistryFromApp, hydrateAttaformState, isRegisterValue, kAttaformRegistry, parseApiErrors, renderAttaformState, useAbstractForm as useForm, useRegistry, vRegister };
498
570
  export type { AttaformPluginOptions, AttaformRegistry, ParseApiErrorsOptions, ParseApiErrorsResult, SerializedAttaformState, SerializedFormData };
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { App, InjectionKey, Plugin } from 'vue';
2
- import { C as CoercionRegistry, S as SlimPrimitiveKind, a as CoercionEntry, A as AttaformDefaults, F as FormKey, G as GenericForm, U as UseFormConfiguration, b as AbstractSchema, D as DeepPartial, c as DefaultValuesShape, d as UseFormReturnType, R as RegisterValue, e as RegisterModelDynamicCustomDirective, V as ValidationError, f as ApiErrorEnvelope, g as ApiErrorDetails } from './shared/attaform._EqYNPYF.js';
3
- export { h as ApiErrorEntry, i as ArrayItem, j as ArrayPath, k as CoercionResult, l as CustomDirectiveRegisterAssignerFn, m as DefaultValuesResponse, n as FieldMetaPayload, o as FieldState, p as FieldStateMap, q as FieldStateMapEntry, r as FlatPath, s as FormErrorRecord, t as FormErrorsSurface, u as FormMeta, v as FormStorage, w as FormStorageKind, H as HandleSubmit, x as HistoryConfig, I as IsTuple, y as IsUnion, J as JoinSegments, K as KeyofUnion, L as LiftedValueShape, M as MetaTrackerValue, N as NestedReadType, z as NestedType, O as OnError, B as OnInvalidSubmitPolicy, E as OnSubmit, P as Path, Q as PathKey, T as PendingValidationStatus, W as PersistConfig, X as PersistConfigOptions, Y as PersistIncludeMode, Z as ROOT_PATH, _ as ROOT_PATH_KEY, $ as ReactiveValidationStatus, a0 as RegisterDirective, a1 as RegisterFlatPath, a2 as RegisterOptions, a3 as RegisterSelectModifier, a4 as RegisterTextModifier, a5 as RegisterTransform, a6 as Segment, a7 as SetValueCallback, a8 as SetValuePayload, a9 as SettledValidationStatus, aa as SlimRuntimeOf, ab as SubmitHandler, ac as Unset, ad as ValidateOn, ae as ValidateOnConfig, af as ValidationResponse, ag as ValidationResponseWithoutValue, ah as ValueOfUnion, ai as WriteMeta, aj as WriteShape, ak as canonicalizePath, al as isPathPrefix, am as isUnset, an as parseDottedPath, ao as unset } from './shared/attaform._EqYNPYF.js';
4
- export { A as AttaformErrorCode, i as injectForm, u as useRegister } from './shared/attaform.lFNwBcA3.js';
2
+ import { C as CoercionRegistry, S as SlimPrimitiveKind, a as CoercionEntry, A as AttaformDefaults, F as FormKey, G as GenericForm, U as UseFormConfiguration, b as AbstractSchema, D as DeepPartial, c as DefaultValuesShape, d as UseFormReturnType, R as RegisterValue, e as RegisterModelDynamicCustomDirective, f as ShouldShowErrors, V as ValidationError, g as ApiErrorEnvelope, h as ApiErrorDetails } from './shared/attaform.BYbsV2Wv.js';
3
+ export { i as ApiErrorEntry, j as ArrayItem, k as ArrayPath, l as CoercionResult, m as CustomDirectiveRegisterAssignerFn, n as DefaultValuesResponse, o as FieldMetaPayload, p as FieldState, q as FieldStateMap, r as FieldStateMapEntry, s as FlatPath, t as FormErrorRecord, u as FormErrorsSurface, v as FormMeta, w as FormStorage, x 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, E as OnInvalidSubmitPolicy, P as OnSubmit, Q as Path, T as PathKey, W as PendingValidationStatus, X as PersistConfig, Y as PersistConfigOptions, Z as PersistIncludeMode, _ as ROOT_PATH, $ as ROOT_PATH_KEY, a0 as ReactiveValidationStatus, a1 as RegisterDirective, a2 as RegisterFlatPath, a3 as RegisterOptions, a4 as RegisterSelectModifier, a5 as RegisterTextModifier, a6 as RegisterTransform, a7 as Segment, a8 as SetValueCallback, a9 as SetValuePayload, aa as SettledValidationStatus, ab as ShouldShowErrorsConfig, ac as SlimRuntimeOf, ad as SubmitHandler, ae as Unset, af as ValidateOn, ag as ValidateOnConfig, ah as ValidationResponse, ai as ValidationResponseWithoutValue, aj as ValueOfUnion, ak as WriteMeta, al as WriteShape, am as canonicalizePath, an as isPathPrefix, ao as isUnset, ap as parseDottedPath, aq as unset } from './shared/attaform.BYbsV2Wv.js';
4
+ export { A as AttaformErrorCode, i as injectForm, u as useRegister } from './shared/attaform.B0zue7zt.js';
5
5
  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';
6
6
 
7
7
  /**
@@ -53,12 +53,20 @@ declare const defaultCoercionRules: CoercionRegistry;
53
53
  * of truth instead of sniffing `import.meta.*` (bundler-specific) at each
54
54
  * call site.
55
55
  *
56
- * Consumers can override explicitly via `createAttaform({ ssr: true })`;
57
- * the default heuristic handles the common Node-vs-browser split without
58
- * relying on any bundler-injected flag.
56
+ * Consumers can override the heuristic explicitly via
57
+ * `createAttaform({ ssr: true })`; the default handles the common
58
+ * Node-vs-browser split without relying on any bundler-injected flag.
59
59
  */
60
60
  interface SSRDetectOptions {
61
- override?: boolean;
61
+ /**
62
+ * Force SSR-vs-client mode, bypassing the `typeof window` heuristic.
63
+ * `true` activates the SSR code paths (no devtools, no persistence
64
+ * wiring, payload serialisation enabled); `false` forces client mode.
65
+ * The Nuxt plugin sets this from `import.meta.server` so SSR detection
66
+ * never depends on whether `window` is polyfilled. Tests that need to
67
+ * exercise the SSR code paths under jsdom pass `ssr: true`.
68
+ */
69
+ ssr?: boolean;
62
70
  }
63
71
 
64
72
  /**
@@ -368,6 +376,70 @@ declare function isRegisterValue<Value = unknown>(val: unknown): val is Register
368
376
  */
369
377
  declare const vRegister: RegisterModelDynamicCustomDirective;
370
378
 
379
+ /**
380
+ * Library-default heuristic for `shouldShowErrors`. Drives
381
+ * `field.showErrors` and `form.meta.showErrors` whenever the consumer
382
+ * has not configured an override at either the plugin or per-form
383
+ * level.
384
+ *
385
+ * Reads "show errors after the first submit attempt, OR after the
386
+ * user has interacted (`touched`) and made a change (`dirty`)." The
387
+ * framework already gates on `errors.length > 0` before invoking the
388
+ * predicate, so the body only decides *when* to surface existing
389
+ * errors — not whether errors exist.
390
+ *
391
+ * Public re-export so adopters can compose with this without
392
+ * copy-pasting the rule body. A layered predicate that adds a
393
+ * special case but otherwise defers to the library default picks up
394
+ * future heuristic refinements automatically:
395
+ *
396
+ * ```ts
397
+ * import { defaultShouldShowErrors } from 'attaform'
398
+ *
399
+ * useForm({
400
+ * schema,
401
+ * shouldShowErrors: (field, formMeta) =>
402
+ * field.path[0] === 'urgent' || defaultShouldShowErrors(field, formMeta),
403
+ * })
404
+ * ```
405
+ */
406
+ declare const defaultShouldShowErrors: ShouldShowErrors;
407
+
408
+ /**
409
+ * The library's built-in conservative set of identifier name stems that
410
+ * flag a path segment as "this looks like data the consumer almost
411
+ * certainly does not want serialised to client-side storage or
412
+ * broadcast across tabs."
413
+ *
414
+ * Each entry is a NAME STEM, not a regex. Matching is case-insensitive
415
+ * and tolerant of separator variants — `'card_number'` matches the
416
+ * segments `'card_number'`, `'card-number'`, `'cardNumber'`, and
417
+ * `'cardnumber'`. Short stems (compact length ≤ 5) get word-boundary
418
+ * anchors to avoid common false positives — `'pin'` matches `'pin'`
419
+ * and `'user_pin'` but not `'pinned'`; `'token'` matches `'token'` but
420
+ * not `'tokenizer'`. Longer stems match anywhere (`'password'` matches
421
+ * `'password'`, `'passwords'`, `'userPassword'`).
422
+ *
423
+ * Consumers extend or replace via per-form or global config:
424
+ *
425
+ * ```ts
426
+ * createAttaform({
427
+ * defaults: { sensitiveNames: [...DEFAULT_SENSITIVE_NAMES, 'mrn', 'tax_id'] }
428
+ * })
429
+ * ```
430
+ *
431
+ * The same resolved predicate gates persistence writes, multi-tab sync
432
+ * broadcasts, AND the DevTools redact walk — one source of truth for
433
+ * "what counts as sensitive" across every surface.
434
+ *
435
+ * **Non-goals.** This is not a soundness guarantee. Adversarial paths
436
+ * (`'sensitive_data'`, `'CCV'` instead of `'CVV'`) can slip through.
437
+ * The intent is a code-review trigger for the common-case footgun
438
+ * plus a defense-in-depth filter on the cross-tab and DevTools
439
+ * surfaces.
440
+ */
441
+ declare const DEFAULT_SENSITIVE_NAMES: readonly string[];
442
+
371
443
  /**
372
444
  * Result of `parseApiErrors`. Branch on `ok` to handle the two cases:
373
445
  *
@@ -494,5 +566,5 @@ declare const PARSE_API_ERRORS_DEFAULTS: {
494
566
  */
495
567
  declare function parseApiErrors(payload: ApiErrorEnvelope | ApiErrorDetails | null | undefined | unknown, options: ParseApiErrorsOptions): ParseApiErrorsResult;
496
568
 
497
- export { AbstractSchema, ApiErrorDetails, ApiErrorEnvelope, AttaformDefaults, CoercionEntry, CoercionRegistry, DeepPartial, DefaultValuesShape, FormKey, GenericForm, PARSE_API_ERRORS_DEFAULTS, RegisterValue, SlimPrimitiveKind, UseFormConfiguration, UseFormReturnType, ValidationError, assignKey, createAttaform, createRegistry, defaultCoercionRules, defineCoercion, escapeForInlineScript, getRegistryFromApp, hydrateAttaformState, isRegisterValue, kAttaformRegistry, parseApiErrors, renderAttaformState, useAbstractForm as useForm, useRegistry, vRegister };
569
+ export { AbstractSchema, ApiErrorDetails, ApiErrorEnvelope, AttaformDefaults, CoercionEntry, CoercionRegistry, DEFAULT_SENSITIVE_NAMES, DeepPartial, DefaultValuesShape, FormKey, GenericForm, PARSE_API_ERRORS_DEFAULTS, RegisterValue, ShouldShowErrors, SlimPrimitiveKind, UseFormConfiguration, UseFormReturnType, ValidationError, assignKey, createAttaform, createRegistry, defaultCoercionRules, defaultShouldShowErrors, defineCoercion, escapeForInlineScript, getRegistryFromApp, hydrateAttaformState, isRegisterValue, kAttaformRegistry, parseApiErrors, renderAttaformState, useAbstractForm as useForm, useRegistry, vRegister };
498
570
  export type { AttaformPluginOptions, AttaformRegistry, ParseApiErrorsOptions, ParseApiErrorsResult, SerializedAttaformState, SerializedFormData };
package/dist/index.mjs CHANGED
@@ -1,9 +1,10 @@
1
- import { I as InvalidPathError } from './shared/attaform.BfMxsfmE.mjs';
2
- export { A as AnonPersistError, a as AttaformError, b as InvalidUseFormConfigError, O as OutsideSetupError, R as RegistryNotInstalledError, d as ReservedFormKeyError, S as SensitivePersistFieldError, e as SubmitErrorHandlerError, f as assignKey, c as createAttaform, h as createRegistry, g as getRegistryFromApp, j as isRegisterValue, k as kAttaformRegistry, u as useRegister, l as useRegistry, v as vRegister } from './shared/attaform.BfMxsfmE.mjs';
3
- export { h as hydrateAttaformState, r as renderAttaformState } from './shared/attaform.CINUMjPq.mjs';
4
- export { A as AttaformErrorCode, d as defaultCoercionRules, b as defineCoercion, i as injectForm, a as isUnset, u as unset, c as useForm } from './shared/attaform.DILbdvfo.mjs';
5
- import { c as canonicalizePath } from './shared/attaform.jrxE_xZw.mjs';
6
- export { R as ROOT_PATH, a as ROOT_PATH_KEY, i as isPathPrefix, p as parseDottedPath } from './shared/attaform.jrxE_xZw.mjs';
1
+ import { I as InvalidPathError } from './shared/attaform.CIEQgJnM.mjs';
2
+ export { A as AnonPersistError, a as AttaformError, D as DEFAULT_SENSITIVE_NAMES, b as InvalidUseFormConfigError, O as OutsideSetupError, R as RegistryNotInstalledError, d as ReservedFormKeyError, S as SensitivePersistFieldError, e as SubmitErrorHandlerError, f as assignKey, c as createAttaform, h as createRegistry, g as getRegistryFromApp, i as isRegisterValue, k as kAttaformRegistry, u as useRegister, j as useRegistry, v as vRegister } from './shared/attaform.CIEQgJnM.mjs';
3
+ export { h as hydrateAttaformState, r as renderAttaformState } from './shared/attaform.Vo-Kft0t.mjs';
4
+ import { n as normalizeNumericOption } from './shared/attaform.BT55rDNN.mjs';
5
+ export { A as AttaformErrorCode, d as defaultCoercionRules, b as defaultShouldShowErrors, c as defineCoercion, i as injectForm, a as isUnset, u as unset, e as useForm } from './shared/attaform.BT55rDNN.mjs';
6
+ import { c as canonicalizePath } from './shared/attaform.h1sq3BFu.mjs';
7
+ export { R as ROOT_PATH, a as ROOT_PATH_KEY, i as isPathPrefix, p as parseDottedPath } from './shared/attaform.h1sq3BFu.mjs';
7
8
 
8
9
  function escapeForInlineScript(json) {
9
10
  return json.replace(/[<>&\u2028\u2029]/g, (char) => {
@@ -31,9 +32,27 @@ const PARSE_API_ERRORS_DEFAULTS = {
31
32
  defaultCode: "api:unknown"
32
33
  };
33
34
  function parseApiErrors(payload, options) {
34
- const maxEntries = options.maxEntries ?? PARSE_API_ERRORS_DEFAULTS.maxEntries;
35
- const maxPathDepth = options.maxPathDepth ?? PARSE_API_ERRORS_DEFAULTS.maxPathDepth;
36
- const maxTotalSegments = options.maxTotalSegments ?? PARSE_API_ERRORS_DEFAULTS.maxTotalSegments;
35
+ const maxEntries = normalizeNumericOption({
36
+ value: options.maxEntries ?? PARSE_API_ERRORS_DEFAULTS.maxEntries,
37
+ source: "parseApiErrors.maxEntries",
38
+ allowInfinity: false,
39
+ min: 0,
40
+ defaultValue: PARSE_API_ERRORS_DEFAULTS.maxEntries
41
+ });
42
+ const maxPathDepth = normalizeNumericOption({
43
+ value: options.maxPathDepth ?? PARSE_API_ERRORS_DEFAULTS.maxPathDepth,
44
+ source: "parseApiErrors.maxPathDepth",
45
+ allowInfinity: false,
46
+ min: 0,
47
+ defaultValue: PARSE_API_ERRORS_DEFAULTS.maxPathDepth
48
+ });
49
+ const maxTotalSegments = normalizeNumericOption({
50
+ value: options.maxTotalSegments ?? PARSE_API_ERRORS_DEFAULTS.maxTotalSegments,
51
+ source: "parseApiErrors.maxTotalSegments",
52
+ allowInfinity: false,
53
+ min: 0,
54
+ defaultValue: PARSE_API_ERRORS_DEFAULTS.maxTotalSegments
55
+ });
37
56
  const defaultCode = options.defaultCode ?? PARSE_API_ERRORS_DEFAULTS.defaultCode;
38
57
  if (payload === null || payload === void 0) {
39
58
  return { ok: true, errors: [] };
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","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 { 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 const maxEntries = options.maxEntries ?? PARSE_API_ERRORS_DEFAULTS.maxEntries\n const maxPathDepth = options.maxPathDepth ?? PARSE_API_ERRORS_DEFAULTS.maxPathDepth\n const maxTotalSegments = options.maxTotalSegments ?? PARSE_API_ERRORS_DEFAULTS.maxTotalSegments\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":[],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","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":[],"mappings":";;;;;;;;AAcO,SAAS,sBAAsB,IAAA,EAAsB;AAC1D,EAAA,OAAO,IAAA,CAAK,OAAA,CAAQ,oBAAA,EAAsB,CAAC,IAAA,KAAS;AAClD,IAAA,QAAQ,IAAA;AAAM,MACZ,KAAK,GAAA;AACH,QAAA,OAAO,SAAA;AAAA,MACT,KAAK,GAAA;AACH,QAAA,OAAO,SAAA;AAAA,MACT,KAAK,GAAA;AACH,QAAA,OAAO,SAAA;AAAA,MACT,KAAK,QAAA;AACH,QAAA,OAAO,SAAA;AAAA,MACT,KAAK,QAAA;AACH,QAAA,OAAO,SAAA;AAAA,MACT;AACE,QAAA,OAAO,IAAA;AAAA;AACX,EACF,CAAC,CAAA;AACH;;ACmDO,MAAM,yBAAA,GAA4B;AAAA,EACvC,UAAA,EAAY,GAAA;AAAA,EACZ,YAAA,EAAc,EAAA;AAAA,EACd,gBAAA,EAAkB,GAAA;AAAA,EAClB,WAAA,EAAa;AACf;AAmDO,SAAS,cAAA,CACd,SACA,OAAA,EACsB;AAOtB,EAAA,MAAM,aAAa,sBAAA,CAAuB;AAAA,IACxC,KAAA,EAAO,OAAA,CAAQ,UAAA,IAAc,yBAAA,CAA0B,UAAA;AAAA,IACvD,MAAA,EAAQ,2BAAA;AAAA,IACR,aAAA,EAAe,KAAA;AAAA,IACf,GAAA,EAAK,CAAA;AAAA,IACL,cAAc,yBAAA,CAA0B;AAAA,GACzC,CAAA;AACD,EAAA,MAAM,eAAe,sBAAA,CAAuB;AAAA,IAC1C,KAAA,EAAO,OAAA,CAAQ,YAAA,IAAgB,yBAAA,CAA0B,YAAA;AAAA,IACzD,MAAA,EAAQ,6BAAA;AAAA,IACR,aAAA,EAAe,KAAA;AAAA,IACf,GAAA,EAAK,CAAA;AAAA,IACL,cAAc,yBAAA,CAA0B;AAAA,GACzC,CAAA;AACD,EAAA,MAAM,mBAAmB,sBAAA,CAAuB;AAAA,IAC9C,KAAA,EAAO,OAAA,CAAQ,gBAAA,IAAoB,yBAAA,CAA0B,gBAAA;AAAA,IAC7D,MAAA,EAAQ,iCAAA;AAAA,IACR,aAAA,EAAe,KAAA;AAAA,IACf,GAAA,EAAK,CAAA;AAAA,IACL,cAAc,yBAAA,CAA0B;AAAA,GACzC,CAAA;AACD,EAAA,MAAM,WAAA,GAAc,OAAA,CAAQ,WAAA,IAAe,yBAAA,CAA0B,WAAA;AAErE,EAAA,IAAI,OAAA,KAAY,IAAA,IAAQ,OAAA,KAAY,MAAA,EAAW;AAC7C,IAAA,OAAO,EAAE,EAAA,EAAI,IAAA,EAAM,MAAA,EAAQ,EAAC,EAAE;AAAA,EAChC;AACA,EAAA,IAAI,OAAO,YAAY,QAAA,EAAU;AAC/B,IAAA,OAAO,EAAE,EAAA,EAAI,KAAA,EAAO,MAAA,EAAQ,IAAI,QAAA,EAAU,CAAA,YAAA,EAAe,OAAO,OAAO,CAAA,iBAAA,CAAA,EAAoB;AAAA,EAC7F;AAEA,EAAA,MAAM,UAAA,GAAa,eAAe,OAAkC,CAAA;AACpE,EAAA,IAAI,CAAC,WAAW,EAAA,EAAI;AAClB,IAAA,OAAO,EAAE,IAAI,KAAA,EAAO,MAAA,EAAQ,EAAC,EAAG,QAAA,EAAU,WAAW,MAAA,EAAO;AAAA,EAC9D;AAEA,EAAA,MAAM,EAAE,SAAQ,GAAI,UAAA;AACpB,EAAA,MAAM,UAAA,GAAa,MAAA,CAAO,IAAA,CAAK,OAAO,CAAA,CAAE,MAAA;AAIxC,EAAA,IAAI,aAAa,UAAA,EAAY;AAC3B,IAAA,OAAO;AAAA,MACL,EAAA,EAAI,KAAA;AAAA,MACJ,QAAQ,EAAC;AAAA,MACT,QAAA,EAAU,CAAA,YAAA,EAAe,UAAU,CAAA,6BAAA,EAAgC,UAAU,CAAA;AAAA,KAC/E;AAAA,EACF;AAEA,EAAA,MAAM,SAA4B,EAAC;AACnC,EAAA,IAAI,aAAA,GAAgB,CAAA;AACpB,EAAA,KAAA,MAAW,CAAC,GAAA,EAAK,KAAK,KAAK,MAAA,CAAO,OAAA,CAAQ,OAAO,CAAA,EAAG;AAClD,IAAA,MAAM,YAAmD,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,GAAI,KAAA,GAAQ,CAAC,KAAK,CAAA;AAM9F,IAAA,IAAI,QAAA;AACJ,IAAA,IAAI;AACF,MAAA,QAAA,GAAW,gBAAA,CAAiB,GAAG,CAAA,CAAE,QAAA;AAAA,IACnC,SAAS,GAAA,EAAK;AACZ,MAAA,IAAI,eAAe,gBAAA,EAAkB;AACrC,MAAA,MAAM,GAAA;AAAA,IACR;AAMA,IAAA,IAAI,QAAA,CAAS,SAAS,YAAA,EAAc;AAIpC,IAAA,aAAA,IAAiB,QAAA,CAAS,MAAA;AAC1B,IAAA,IAAI,gBAAgB,gBAAA,EAAkB;AACpC,MAAA,OAAO;AAAA,QACL,EAAA,EAAI,KAAA;AAAA,QACJ,QAAQ,EAAC;AAAA,QACT,QAAA,EAAU,wDAAwD,gBAAgB,CAAA;AAAA,OACpF;AAAA,IACF;AACA,IAAA,KAAA,MAAW,SAAS,SAAA,EAAW;AAM7B,MAAA,MAAM,OAAA,GAAU,OAAO,KAAA,KAAU,QAAA,GAAW,QAAQ,KAAA,CAAM,OAAA;AAC1D,MAAA,MAAM,IAAA,GAAO,OAAO,KAAA,KAAU,QAAA,GAAW,cAAc,KAAA,CAAM,IAAA;AAC7D,MAAA,IAAI,OAAA,CAAQ,WAAW,CAAA,EAAG;AAC1B,MAAA,MAAA,CAAO,IAAA,CAAK;AAAA,QACV,OAAA;AAAA,QACA,IAAA,EAAM,KAAA,CAAM,IAAA,CAAK,QAAQ,CAAA;AAAA,QACzB,SAAS,OAAA,CAAQ,OAAA;AAAA,QACjB;AAAA,OACD,CAAA;AAAA,IACH;AAAA,EACF;AACA,EAAA,OAAO,EAAE,EAAA,EAAI,IAAA,EAAM,MAAA,EAAO;AAC5B;AAIA,SAAS,eAAe,OAAA,EAAiD;AACvE,EAAA,MAAM,YAAA,GAAe,QAAQ,OAAO,CAAA;AACpC,EAAA,IAAI,iBAAiB,IAAA,IAAQ,YAAA,KAAiB,MAAA,IAAa,OAAO,iBAAiB,QAAA,EAAU;AAC3F,IAAA,MAAM,QAAS,YAAA,CAAuC,OAAA;AACtD,IAAA,IAAI,UAAU,MAAA,EAAW;AAEvB,MAAA,OAAO,EAAE,EAAA,EAAI,IAAA,EAAM,OAAA,EAAS,EAAC,EAAE;AAAA,IACjC;AACA,IAAA,IAAI,eAAA,CAAgB,KAAK,CAAA,EAAG,OAAO,EAAE,EAAA,EAAI,IAAA,EAAM,SAAS,KAAA,EAAM;AAC9D,IAAA,OAAO;AAAA,MACL,EAAA,EAAI,KAAA;AAAA,MACJ,MAAA,EAAQ;AAAA,KACV;AAAA,EACF;AAOA,EAAA,IAAI,iBAAiB,IAAA,IAAQ,YAAA,KAAiB,MAAA,IAAa,OAAO,iBAAiB,QAAA,EAAU;AAC3F,IAAA,OAAO;AAAA,MACL,EAAA,EAAI,KAAA;AAAA,MACJ,MAAA,EAAQ,CAAA,kBAAA,EAAqB,OAAO,YAAY,CAAA,qCAAA;AAAA,KAClD;AAAA,EACF;AAEA,EAAA,IAAI,aAAa,OAAA,EAAS;AACxB,IAAA,MAAM,KAAA,GAAQ,QAAQ,SAAS,CAAA;AAC/B,IAAA,IAAI,KAAA,KAAU,QAAW,OAAO,EAAE,IAAI,IAAA,EAAM,OAAA,EAAS,EAAC,EAAE;AACxD,IAAA,IAAI,eAAA,CAAgB,KAAK,CAAA,EAAG,OAAO,EAAE,EAAA,EAAI,IAAA,EAAM,SAAS,KAAA,EAAM;AAC9D,IAAA,OAAO,EAAE,EAAA,EAAI,KAAA,EAAO,MAAA,EAAQ,8DAAA,EAA+D;AAAA,EAC7F;AAEA,EAAA,IAAI,eAAA,CAAgB,OAAO,CAAA,EAAG,OAAO,EAAE,EAAA,EAAI,IAAA,EAAM,SAAS,OAAA,EAAQ;AAIlE,EAAA,IAAI,MAAA,CAAO,IAAA,CAAK,OAAO,CAAA,CAAE,MAAA,KAAW,CAAA,EAAG,OAAO,EAAE,EAAA,EAAI,IAAA,EAAM,OAAA,EAAS,EAAC,EAAE;AACtE,EAAA,OAAO,EAAE,EAAA,EAAI,KAAA,EAAO,MAAA,EAAQ,4BAAA,EAA6B;AAC3D;AAEA,SAAS,kBAAkB,KAAA,EAAwC;AACjE,EAAA,IAAI,KAAA,KAAU,QAAQ,OAAO,KAAA,KAAU,YAAY,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,EAAG,OAAO,KAAA;AAChF,EAAA,MAAM,GAAA,GAAM,KAAA;AACZ,EAAA,OAAO,OAAO,GAAA,CAAI,OAAA,KAAY,QAAA,IAAY,OAAO,IAAI,IAAA,KAAS,QAAA;AAChE;AAQA,SAAS,gBAAgB,KAAA,EAAiD;AACxE,EAAA,OAAO,OAAO,KAAA,KAAU,QAAA,IAAY,iBAAA,CAAkB,KAAK,CAAA;AAC7D;AAWA,SAAS,gBAAgB,KAAA,EAA0C;AACjE,EAAA,IAAI,KAAA,KAAU,QAAQ,OAAO,KAAA,KAAU,YAAY,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,EAAG,OAAO,KAAA;AAGhF,EAAA,MAAM,MAAA,GAAS,KAAA;AACf,EAAA,KAAA,MAAW,CAAA,IAAK,MAAA,CAAO,IAAA,CAAK,MAAM,CAAA,EAAG;AACnC,IAAA,MAAM,CAAA,GAAI,OAAO,CAAC,CAAA;AAClB,IAAA,IAAI,eAAA,CAAgB,CAAC,CAAA,EAAG;AACxB,IAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,CAAC,CAAA,IAAK,CAAA,CAAE,KAAA,CAAM,CAAC,KAAA,KAAU,eAAA,CAAgB,KAAK,CAAC,CAAA,EAAG;AACpE,IAAA,OAAO,KAAA;AAAA,EACT;AACA,EAAA,OAAO,IAAA;AACT;;;;"}
package/dist/nuxt.d.cts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as _nuxt_schema from '@nuxt/schema';
2
- import { A as AttaformDefaults } from './shared/attaform._EqYNPYF.cjs';
2
+ import { A as AttaformDefaults } from './shared/attaform.BYbsV2Wv.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 { A as AttaformDefaults } from './shared/attaform._EqYNPYF.mjs';
2
+ import { A as AttaformDefaults } from './shared/attaform.BYbsV2Wv.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 { A as AttaformDefaults } from './shared/attaform._EqYNPYF.js';
2
+ import { A as AttaformDefaults } from './shared/attaform.BYbsV2Wv.js';
3
3
  import 'vue';
4
4
 
5
5
  /**
@@ -1,8 +1,8 @@
1
1
  'use strict';
2
2
 
3
3
  const app = require('nuxt/app');
4
- const plugin = require('../../shared/attaform.rIRYSUI1.cjs');
5
- const serialize = require('../../shared/attaform.CJttVxRj.cjs');
4
+ const plugin = require('../../shared/attaform.Dee2rU1P.cjs');
5
+ const serialize = require('../../shared/attaform.C8LVFVVe.cjs');
6
6
 
7
7
  var attaform_default = app.defineNuxtPlugin({
8
8
  // `enforce: 'pre'` makes the "we run before any component's setup" claim
@@ -14,7 +14,7 @@ var attaform_default = app.defineNuxtPlugin({
14
14
  setup(nuxtApp) {
15
15
  const isServer = undefined;
16
16
  const { defaults } = app.useRuntimeConfig().public.attaform;
17
- nuxtApp.vueApp.use(plugin.createAttaform({ override: isServer, defaults }));
17
+ nuxtApp.vueApp.use(plugin.createAttaform({ ssr: isServer, defaults }));
18
18
  if (isServer) {
19
19
  nuxtApp.hook("app:rendered", () => {
20
20
  const state = serialize.renderAttaformState(nuxtApp.vueApp);
@@ -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({ override: 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,UAAU,QAAA,EAAU,QAAA,EAAU,CAAC,CAAA;AAEnE,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, 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,6 +1,6 @@
1
1
  import { defineNuxtPlugin, useRuntimeConfig } from 'nuxt/app';
2
- import { c as createAttaform } from '../../shared/attaform.BfMxsfmE.mjs';
3
- import { r as renderAttaformState, h as hydrateAttaformState } from '../../shared/attaform.CINUMjPq.mjs';
2
+ import { c as createAttaform } from '../../shared/attaform.CIEQgJnM.mjs';
3
+ import { r as renderAttaformState, h as hydrateAttaformState } from '../../shared/attaform.Vo-Kft0t.mjs';
4
4
 
5
5
  var attaform_default = defineNuxtPlugin({
6
6
  // `enforce: 'pre'` makes the "we run before any component's setup" claim
@@ -12,7 +12,7 @@ var attaform_default = defineNuxtPlugin({
12
12
  setup(nuxtApp) {
13
13
  const isServer = import.meta.server;
14
14
  const { defaults } = useRuntimeConfig().public.attaform;
15
- nuxtApp.vueApp.use(createAttaform({ override: isServer, defaults }));
15
+ nuxtApp.vueApp.use(createAttaform({ ssr: isServer, defaults }));
16
16
  if (isServer) {
17
17
  nuxtApp.hook("app:rendered", () => {
18
18
  const state = renderAttaformState(nuxtApp.vueApp);
@@ -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({ override: 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,UAAU,QAAA,EAAU,QAAA,EAAU,CAAC,CAAA;AAEnE,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, 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;;;;"}