attaform 0.23.0 → 0.24.1
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.
- package/README.md +1 -1
- package/dist/chunks/devtools.cjs +1 -1
- package/dist/chunks/devtools.mjs +1 -1
- package/dist/chunks/fingerprint2.cjs +1 -1
- package/dist/chunks/fingerprint2.mjs +1 -1
- package/dist/index.cjs +5 -150
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -132
- package/dist/index.d.mts +6 -132
- package/dist/index.d.ts +6 -132
- package/dist/index.mjs +5 -150
- package/dist/index.mjs.map +1 -1
- package/dist/nuxt.d.cts +1 -1
- package/dist/nuxt.d.mts +1 -1
- package/dist/nuxt.d.ts +1 -1
- package/dist/runtime/components/AttaformDevtoolsPanel.vue +2 -2
- package/dist/runtime/plugins/attaform.cjs +2 -2
- package/dist/runtime/plugins/attaform.mjs +2 -2
- package/dist/shared/{attaform.Dx9-QQE2.mjs → attaform.0-00cYGw.mjs} +2 -2
- package/dist/shared/{attaform.Dx9-QQE2.mjs.map → attaform.0-00cYGw.mjs.map} +1 -1
- package/dist/shared/{attaform.D52oJiYC.d.cts → attaform.4zesozTg.d.mts} +38 -6
- package/dist/shared/{attaform.Db4E4IW6.cjs → attaform.B7UdTs_o.cjs} +79 -79
- package/dist/shared/attaform.B7UdTs_o.cjs.map +1 -0
- package/dist/shared/{attaform.BibT5AS_.cjs → attaform.BOi6n2Pn.cjs} +2 -2
- package/dist/shared/{attaform.BibT5AS_.cjs.map → attaform.BOi6n2Pn.cjs.map} +1 -1
- package/dist/shared/{attaform.DCkSNnPr.d.mts → attaform.Bk7vnQhG.d.cts} +38 -6
- package/dist/shared/{attaform.BGMRvckW.d.ts → attaform.Bq6Copxn.d.cts} +41 -25
- package/dist/shared/{attaform.DuPneYR0.d.cts → attaform.BrFPMFgi.d.ts} +41 -25
- package/dist/shared/{attaform.alpG7rT7.mjs → attaform.BunnTiTw.mjs} +4 -4
- package/dist/shared/attaform.BunnTiTw.mjs.map +1 -0
- package/dist/shared/{attaform.DrY8srOp.d.mts → attaform.BwAcpoRw.d.mts} +41 -25
- package/dist/shared/{attaform.CaYj3ZfY.cjs → attaform.C-1W0T1n.cjs} +6 -4
- package/dist/shared/attaform.C-1W0T1n.cjs.map +1 -0
- package/dist/shared/{attaform.Dd1Kmmaj.mjs → attaform.C-tQKknW.mjs} +6 -4
- package/dist/shared/attaform.C-tQKknW.mjs.map +1 -0
- package/dist/shared/{attaform.BhI9Icek.mjs → attaform.C0au8oXd.mjs} +36 -31
- package/dist/shared/attaform.C0au8oXd.mjs.map +1 -0
- package/dist/shared/attaform.CjdepGnw.cjs +27 -0
- package/dist/shared/attaform.CjdepGnw.cjs.map +1 -0
- package/dist/shared/{attaform.DbyTD8N2.cjs → attaform.Cn6JoG9o.cjs} +8 -6
- package/dist/shared/attaform.Cn6JoG9o.cjs.map +1 -0
- package/dist/shared/{attaform.Cmb_LCie.cjs → attaform.CrrIaHM8.cjs} +4 -4
- package/dist/shared/attaform.CrrIaHM8.cjs.map +1 -0
- package/dist/shared/{attaform.BJnNK75Y.d.mts → attaform.DBhrKb2j.d.cts} +206 -168
- package/dist/shared/{attaform.BJnNK75Y.d.ts → attaform.DBhrKb2j.d.mts} +206 -168
- package/dist/shared/{attaform.BJnNK75Y.d.cts → attaform.DBhrKb2j.d.ts} +206 -168
- package/dist/shared/{attaform.DsQkXE3o.cjs → attaform.DRQjF16I.cjs} +248 -195
- package/dist/shared/attaform.DRQjF16I.cjs.map +1 -0
- package/dist/shared/attaform.DdjDqTah.d.cts +56 -0
- package/dist/shared/attaform.DdjDqTah.d.mts +56 -0
- package/dist/shared/attaform.DdjDqTah.d.ts +56 -0
- package/dist/shared/{attaform.WEwfXcHq.d.ts → attaform.Df4xXKbE.d.ts} +38 -6
- package/dist/shared/{attaform.BFWb6hDk.mjs → attaform.DhXl0Kdr.mjs} +7 -1
- package/dist/shared/attaform.DhXl0Kdr.mjs.map +1 -0
- package/dist/shared/{attaform.CR6wGvNu.cjs → attaform.DwLw3Kzv.cjs} +7 -1
- package/dist/shared/attaform.DwLw3Kzv.cjs.map +1 -0
- package/dist/shared/{attaform.CzVta5o2.mjs → attaform.FY5r1BpA.mjs} +8 -6
- package/dist/shared/attaform.FY5r1BpA.mjs.map +1 -0
- package/dist/shared/{attaform.CtJOd7ox.mjs → attaform.NWrEGrNo.mjs} +247 -193
- package/dist/shared/attaform.NWrEGrNo.mjs.map +1 -0
- package/dist/shared/attaform.WvcckZMD.mjs +21 -0
- package/dist/shared/attaform.WvcckZMD.mjs.map +1 -0
- package/dist/transforms.cjs +1 -1
- package/dist/transforms.mjs +1 -1
- package/dist/vite.cjs +1 -1
- package/dist/vite.mjs +1 -1
- package/dist/zod-v3.cjs +2 -2
- package/dist/zod-v3.d.cts +12 -11
- package/dist/zod-v3.d.mts +12 -11
- package/dist/zod-v3.d.ts +12 -11
- package/dist/zod-v3.mjs +2 -2
- package/dist/zod-v4.cjs +2 -2
- package/dist/zod-v4.d.cts +6 -5
- package/dist/zod-v4.d.mts +6 -5
- package/dist/zod-v4.d.ts +6 -5
- package/dist/zod-v4.mjs +2 -2
- package/dist/zod.cjs +5 -5
- package/dist/zod.cjs.map +1 -1
- package/dist/zod.d.cts +21 -52
- package/dist/zod.d.mts +21 -52
- package/dist/zod.d.ts +21 -52
- package/dist/zod.mjs +5 -5
- package/dist/zod.mjs.map +1 -1
- package/package.json +1 -1
- package/dist/shared/attaform.BFWb6hDk.mjs.map +0 -1
- package/dist/shared/attaform.BhI9Icek.mjs.map +0 -1
- package/dist/shared/attaform.CR6wGvNu.cjs.map +0 -1
- package/dist/shared/attaform.CaYj3ZfY.cjs.map +0 -1
- package/dist/shared/attaform.Cmb_LCie.cjs.map +0 -1
- package/dist/shared/attaform.CtJOd7ox.mjs.map +0 -1
- package/dist/shared/attaform.CzVta5o2.mjs.map +0 -1
- package/dist/shared/attaform.Db4E4IW6.cjs.map +0 -1
- package/dist/shared/attaform.DbyTD8N2.cjs.map +0 -1
- package/dist/shared/attaform.Dd1Kmmaj.mjs.map +0 -1
- package/dist/shared/attaform.DsQkXE3o.cjs.map +0 -1
- package/dist/shared/attaform.alpG7rT7.mjs.map +0 -1
- package/dist/shared/attaform.nf83TIR5.d.cts +0 -35
- package/dist/shared/attaform.nf83TIR5.d.mts +0 -35
- package/dist/shared/attaform.nf83TIR5.d.ts +0 -35
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Plugin, App } from 'vue';
|
|
2
|
-
import { S as SSRDetectOptions, a as SerializedFormData, A as AttaformRegistry } from './shared/attaform.
|
|
3
|
-
export { b as AnyForm, c 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.
|
|
4
|
-
import { A as AttaformDefaults, F as FormKey, G as GenericForm, U as UseFormConfiguration, a as AbstractSchema, D as DefaultValuesInput, b as UseFormReturnType, R as RegisterModelDynamicCustomDirective, c as RegisterValue, d as GetDisplayState
|
|
5
|
-
export {
|
|
2
|
+
import { S as SSRDetectOptions, a as SerializedFormData, A as AttaformRegistry } from './shared/attaform.BrFPMFgi.js';
|
|
3
|
+
export { b as AnyForm, c 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.BrFPMFgi.js';
|
|
4
|
+
import { A as AttaformDefaults, F as FormKey, G as GenericForm, U as UseFormConfiguration, a as AbstractSchema, D as DefaultValuesInput, b as UseFormReturnType, R as RegisterModelDynamicCustomDirective, c as RegisterValue, d as GetDisplayState } from './shared/attaform.DBhrKb2j.js';
|
|
5
|
+
export { e as ArrayItem, f as ArrayPath, C as CoercionEntry, g as CoercionRegistry, h as CoercionResult, i as CustomDirectiveRegisterAssignerFn, j as DeepPartial, k as DefaultValuesResponse, l as DefaultValuesShape, m as DisplayCtx, n as DisplayMachine, o as DisplayState, E as ErrorInput, p as ErrorsProxyShape, q as FieldMetaPayload, r as FieldState, s as FieldStateMap, t as FieldStateMapEntry, u as FlatPath, v as FormErrorRecord, w as FormErrorsSurface, x as FormMeta, H as HandleSubmit, y as HistoryConfig, I as IsTuple, z as IsUnion, J as JoinSegments, B as Json, K as KeyofUnion, L as LiftedValueShape, M as MetaTrackerValue, N as NestedReadType, O as NestedType, P as OnError, Q as OnInvalidSubmitPolicy, S as OnSubmit, T as PartialFlatPath, V as Path, W as PathKey, X as PendingValidationStatus, Y as Primitive, 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 SlimPrimitiveKind, ab as SlimRuntimeOf, ac as SubmitHandler, ad as Unset, ae as ValidateOn, af as ValidateOnConfig, ag as ValidationError, 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.DBhrKb2j.js';
|
|
6
6
|
export { A as AttaformError, I as InvalidPathError, a as InvalidUseFormConfigError, O as OutsideSetupError, R as RegistryNotInstalledError, b as ReservedFormKeyError, S as SubmitErrorHandlerError } from './shared/attaform.CO0e7YVY.js';
|
|
7
7
|
|
|
8
8
|
/**
|
|
@@ -333,131 +333,5 @@ declare function makeDefaultDisplayState({ showDelay, minVisible, }: DisplayTimi
|
|
|
333
333
|
*/
|
|
334
334
|
declare const defaultDisplayState: GetDisplayState;
|
|
335
335
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
*
|
|
339
|
-
* ```ts
|
|
340
|
-
* const result = parseApiErrors(payload, { formKey: form.key })
|
|
341
|
-
* if (result.ok) {
|
|
342
|
-
* form.setFieldErrors(result.errors)
|
|
343
|
-
* } else {
|
|
344
|
-
* console.warn('Bad error payload:', result.rejected)
|
|
345
|
-
* }
|
|
346
|
-
* ```
|
|
347
|
-
*
|
|
348
|
-
* `ok: true` means the payload was recognised — `errors` may still be
|
|
349
|
-
* empty if the payload was valid but had no actual errors.
|
|
350
|
-
* `ok: false` means the payload didn't match a known shape; `rejected`
|
|
351
|
-
* carries a one-line description of why.
|
|
352
|
-
*/
|
|
353
|
-
type ParseApiErrorsResult = {
|
|
354
|
-
/** `true` when the payload was recognised; `false` when the shape was unfamiliar. */
|
|
355
|
-
readonly ok: boolean;
|
|
356
|
-
/** Errors extracted from the payload. May be empty even when `ok: true`. */
|
|
357
|
-
readonly errors: ValidationError[];
|
|
358
|
-
/** When `ok: false`, a one-line description of why the payload was rejected. */
|
|
359
|
-
readonly rejected?: string;
|
|
360
|
-
};
|
|
361
|
-
/**
|
|
362
|
-
* Options for `parseApiErrors`. The size caps protect against
|
|
363
|
-
* misbehaving or hostile servers — exceeding any cap causes the
|
|
364
|
-
* parser to reject the payload wholesale rather than partially apply.
|
|
365
|
-
*/
|
|
366
|
-
type ParseApiErrorsOptions = {
|
|
367
|
-
/**
|
|
368
|
-
* The form's identifier — pass `form.key`. Stamped on every
|
|
369
|
-
* produced `ValidationError` so errors route to the right form.
|
|
370
|
-
*/
|
|
371
|
-
readonly formKey: FormKey;
|
|
372
|
-
/**
|
|
373
|
-
* Code stamped on `ValidationError`s synthesized from bare-string
|
|
374
|
-
* entries (the Rails / DRF / Laravel `{ field: ["msg"] }` shape).
|
|
375
|
-
* Default `'api:unknown'`. Pick something more specific
|
|
376
|
-
* (`'api:server-validation'`, `'myapp:legacy'`, …) when you know
|
|
377
|
-
* the source.
|
|
378
|
-
*
|
|
379
|
-
* Structured `{ message, code }` entries forward their `code`
|
|
380
|
-
* verbatim and ignore this option.
|
|
381
|
-
*/
|
|
382
|
-
readonly defaultCode?: string;
|
|
383
|
-
/**
|
|
384
|
-
* Maximum number of distinct keys to accept. Default `1000`.
|
|
385
|
-
* Raise for trusted backends that legitimately produce more.
|
|
386
|
-
*/
|
|
387
|
-
readonly maxEntries?: number;
|
|
388
|
-
/**
|
|
389
|
-
* Maximum number of path segments per key. Default `32`. Keys
|
|
390
|
-
* deeper than this are dropped (the rest of the payload still
|
|
391
|
-
* applies if it stays under the other caps).
|
|
392
|
-
*/
|
|
393
|
-
readonly maxPathDepth?: number;
|
|
394
|
-
/**
|
|
395
|
-
* Maximum total path segments summed across every accepted key.
|
|
396
|
-
* Default `10000`. Bounds the worst-case traversal cost.
|
|
397
|
-
*/
|
|
398
|
-
readonly maxTotalSegments?: number;
|
|
399
|
-
};
|
|
400
|
-
/**
|
|
401
|
-
* Default size caps + default fallback code used by `parseApiErrors`.
|
|
402
|
-
* Conservative; pass larger values (or a more specific code) via the
|
|
403
|
-
* options bag for trusted-backend integrations.
|
|
404
|
-
*/
|
|
405
|
-
declare const PARSE_API_ERRORS_DEFAULTS: {
|
|
406
|
-
readonly maxEntries: 1000;
|
|
407
|
-
readonly maxPathDepth: 32;
|
|
408
|
-
readonly maxTotalSegments: 10000;
|
|
409
|
-
readonly defaultCode: "api:unknown";
|
|
410
|
-
};
|
|
411
|
-
/**
|
|
412
|
-
* Normalise a server-side validation error payload into
|
|
413
|
-
* `ValidationError[]`. Pair with `form.setFieldErrors` /
|
|
414
|
-
* `form.addFieldErrors` to surface server errors on the form:
|
|
415
|
-
*
|
|
416
|
-
* ```ts
|
|
417
|
-
* const response = await fetch('/api/signup', { … })
|
|
418
|
-
* if (!response.ok) {
|
|
419
|
-
* const payload = await response.json()
|
|
420
|
-
* const result = parseApiErrors(payload, { formKey: form.key })
|
|
421
|
-
* if (result.ok) form.setFieldErrors(result.errors)
|
|
422
|
-
* }
|
|
423
|
-
* ```
|
|
424
|
-
*
|
|
425
|
-
* Recognised payload shapes:
|
|
426
|
-
*
|
|
427
|
-
* - Wrapped envelope:
|
|
428
|
-
* `{ error: { details: { email: { message: 'taken', code: 'api:duplicate-email' } } } }`
|
|
429
|
-
* - Unwrapped envelope:
|
|
430
|
-
* `{ details: { email: { message: 'taken', code: 'api:duplicate-email' } } }`
|
|
431
|
-
* - Raw details record:
|
|
432
|
-
* `{ email: { message: 'taken', code: 'api:duplicate-email' } }`
|
|
433
|
-
* - **Bare-string Rails / DRF / Laravel shape:**
|
|
434
|
-
* `{ email: ['Email already taken.'], username: 'too short' }`
|
|
435
|
-
* - `null` / `undefined` — returns `{ ok: true, errors: [] }`
|
|
436
|
-
*
|
|
437
|
-
* Two entry shapes are accepted:
|
|
438
|
-
*
|
|
439
|
-
* 1. **Structured** — `{ message: string, code: string }`. The `code`
|
|
440
|
-
* is forwarded verbatim onto the produced `ValidationError`.
|
|
441
|
-
* 2. **Bare-string** — a plain string. Synthesized into
|
|
442
|
-
* `{ message: <string>, code: <defaultCode> }` where `defaultCode`
|
|
443
|
-
* comes from `options.defaultCode` (default `'api:unknown'`).
|
|
444
|
-
* Useful for the Rails / Django REST Framework / FastAPI / Laravel
|
|
445
|
-
* JSON shape that doesn't carry a per-field code.
|
|
446
|
-
*
|
|
447
|
-
* Each detail key's value can be a single entry, an array, or a mix
|
|
448
|
-
* of structured and bare-string entries; arrays expand into one
|
|
449
|
-
* `ValidationError` per entry. Pick a prefix on the server (`api:`,
|
|
450
|
-
* `auth:`, etc.) and stay consistent so error renderers can branch
|
|
451
|
-
* on `code` — or rely on `defaultCode` when the wire shape is
|
|
452
|
-
* message-only.
|
|
453
|
-
*
|
|
454
|
-
* Dotted keys (`"address.line1"`) are split into structured paths
|
|
455
|
-
* automatically. Use a custom server response shape outside these
|
|
456
|
-
* patterns? Build the `ValidationError[]` array yourself and pass
|
|
457
|
-
* it to `setFieldErrors` directly — `parseApiErrors` is just a
|
|
458
|
-
* convenience for the common shapes.
|
|
459
|
-
*/
|
|
460
|
-
declare function parseApiErrors(payload: ApiErrorEnvelope | ApiErrorDetails | null | undefined | unknown, options: ParseApiErrorsOptions): ParseApiErrorsResult;
|
|
461
|
-
|
|
462
|
-
export { AbstractSchema, ApiErrorDetails, ApiErrorEnvelope, AttaformDefaults, AttaformRegistry, DEFAULT_TIMINGS, DEVTOOLS_WINDOW_KEY, DefaultValuesInput, FormKey, GenericForm, GetDisplayState, PARSE_API_ERRORS_DEFAULTS, RegisterValue, SerializedFormData, UseFormConfiguration, UseFormReturnType, ValidationError, assignKey, createAttaform, defaultDisplayState, escapeForInlineScript, hydrateAttaformState, isRegisterValue, makeDefaultDisplayState, parseApiErrors, renderAttaformState, useAbstractForm as useForm, vRegister };
|
|
463
|
-
export type { AttaformDevtoolsBridge, AttaformPluginOptions, DisplayTimings, ParseApiErrorsOptions, ParseApiErrorsResult, SerializedAttaformState };
|
|
336
|
+
export { AbstractSchema, AttaformDefaults, AttaformRegistry, DEFAULT_TIMINGS, DEVTOOLS_WINDOW_KEY, DefaultValuesInput, FormKey, GenericForm, GetDisplayState, RegisterValue, SerializedFormData, UseFormConfiguration, UseFormReturnType, assignKey, createAttaform, defaultDisplayState, escapeForInlineScript, hydrateAttaformState, isRegisterValue, makeDefaultDisplayState, renderAttaformState, useAbstractForm as useForm, vRegister };
|
|
337
|
+
export type { AttaformDevtoolsBridge, AttaformPluginOptions, DisplayTimings, SerializedAttaformState };
|
package/dist/index.mjs
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
export {
|
|
3
|
-
export { D as
|
|
4
|
-
|
|
5
|
-
export { A as AttaformErrorCode, D as DEFAULT_TIMINGS, e as defaultCoercionRules, f as defaultDisplayState, g as defineCoercion, i as injectForm, a as injectWizard, b as isUnset, l as lazy, m as makeDefaultDisplayState, u as unset, h as useForm, c as useRegister, d as useWizard } from './shared/attaform.CtJOd7ox.mjs';
|
|
1
|
+
export { A as AttaformError, I as InvalidPathError, a as InvalidUseFormConfigError, O as OutsideSetupError, R as ROOT_PATH, b as ROOT_PATH_KEY, c as RegistryNotInstalledError, d as ReservedFormKeyError, S as SubmitErrorHandlerError, e as assignKey, f as canonicalizePath, g as createAttaform, h as createRegistry, i as getRegistryFromApp, j as isPathPrefix, k as kAttaformRegistry, p as parseDottedPath, u as useRegistry, v as vRegister } from './shared/attaform.C0au8oXd.mjs';
|
|
2
|
+
export { D as DEVTOOLS_WINDOW_KEY, h as hydrateAttaformState, r as renderAttaformState } from './shared/attaform.0-00cYGw.mjs';
|
|
3
|
+
export { A as AttaformErrorCode, D as DEFAULT_TIMINGS, e as defaultCoercionRules, f as defaultDisplayState, g as defineCoercion, i as injectForm, a as injectWizard, b as isUnset, l as lazy, m as makeDefaultDisplayState, u as unset, h as useForm, c as useRegister, d as useWizard } from './shared/attaform.NWrEGrNo.mjs';
|
|
4
|
+
export { i as isRegisterValue } from './shared/attaform.WvcckZMD.mjs';
|
|
6
5
|
|
|
7
6
|
function escapeForInlineScript(json) {
|
|
8
7
|
return json.replace(/[<>&\u2028\u2029]/g, (char) => {
|
|
@@ -23,149 +22,5 @@ function escapeForInlineScript(json) {
|
|
|
23
22
|
});
|
|
24
23
|
}
|
|
25
24
|
|
|
26
|
-
|
|
27
|
-
maxEntries: 1e3,
|
|
28
|
-
maxPathDepth: 32,
|
|
29
|
-
maxTotalSegments: 1e4,
|
|
30
|
-
defaultCode: "api:unknown"
|
|
31
|
-
};
|
|
32
|
-
function parseApiErrors(payload, options) {
|
|
33
|
-
const { maxEntries, maxPathDepth, maxTotalSegments, defaultCode } = normalizeParseCaps(options);
|
|
34
|
-
if (payload === null || payload === void 0) {
|
|
35
|
-
return { ok: true, errors: [] };
|
|
36
|
-
}
|
|
37
|
-
if (typeof payload !== "object") {
|
|
38
|
-
return { ok: false, errors: [], rejected: `payload was ${typeof payload}, expected object` };
|
|
39
|
-
}
|
|
40
|
-
const extraction = extractDetails(payload);
|
|
41
|
-
if (!extraction.ok) {
|
|
42
|
-
return { ok: false, errors: [], rejected: extraction.reason };
|
|
43
|
-
}
|
|
44
|
-
const { details } = extraction;
|
|
45
|
-
const entryCount = Object.keys(details).length;
|
|
46
|
-
if (entryCount > maxEntries) {
|
|
47
|
-
return {
|
|
48
|
-
ok: false,
|
|
49
|
-
errors: [],
|
|
50
|
-
rejected: `payload has ${entryCount} entries, exceeds maxEntries=${maxEntries}`
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
return convertDetailsToErrors(
|
|
54
|
-
details,
|
|
55
|
-
options.formKey,
|
|
56
|
-
maxPathDepth,
|
|
57
|
-
maxTotalSegments,
|
|
58
|
-
defaultCode
|
|
59
|
-
);
|
|
60
|
-
}
|
|
61
|
-
function normalizeParseCaps(options) {
|
|
62
|
-
const maxEntries = normalizeNumericOption({
|
|
63
|
-
value: options.maxEntries ?? PARSE_API_ERRORS_DEFAULTS.maxEntries,
|
|
64
|
-
source: "parseApiErrors.maxEntries",
|
|
65
|
-
allowInfinity: false,
|
|
66
|
-
min: 0,
|
|
67
|
-
defaultValue: PARSE_API_ERRORS_DEFAULTS.maxEntries
|
|
68
|
-
});
|
|
69
|
-
const maxPathDepth = normalizeNumericOption({
|
|
70
|
-
value: options.maxPathDepth ?? PARSE_API_ERRORS_DEFAULTS.maxPathDepth,
|
|
71
|
-
source: "parseApiErrors.maxPathDepth",
|
|
72
|
-
allowInfinity: false,
|
|
73
|
-
min: 0,
|
|
74
|
-
defaultValue: PARSE_API_ERRORS_DEFAULTS.maxPathDepth
|
|
75
|
-
});
|
|
76
|
-
const maxTotalSegments = normalizeNumericOption({
|
|
77
|
-
value: options.maxTotalSegments ?? PARSE_API_ERRORS_DEFAULTS.maxTotalSegments,
|
|
78
|
-
source: "parseApiErrors.maxTotalSegments",
|
|
79
|
-
allowInfinity: false,
|
|
80
|
-
min: 0,
|
|
81
|
-
defaultValue: PARSE_API_ERRORS_DEFAULTS.maxTotalSegments
|
|
82
|
-
});
|
|
83
|
-
const defaultCode = options.defaultCode ?? PARSE_API_ERRORS_DEFAULTS.defaultCode;
|
|
84
|
-
return { maxEntries, maxPathDepth, maxTotalSegments, defaultCode };
|
|
85
|
-
}
|
|
86
|
-
function convertDetailsToErrors(details, formKey, maxPathDepth, maxTotalSegments, defaultCode) {
|
|
87
|
-
const errors = [];
|
|
88
|
-
let totalSegments = 0;
|
|
89
|
-
for (const [key, value] of Object.entries(details)) {
|
|
90
|
-
const entryList = Array.isArray(value) ? value : [value];
|
|
91
|
-
let segments;
|
|
92
|
-
try {
|
|
93
|
-
segments = canonicalizePath(key).segments;
|
|
94
|
-
} catch (err) {
|
|
95
|
-
if (err instanceof InvalidPathError) continue;
|
|
96
|
-
throw err;
|
|
97
|
-
}
|
|
98
|
-
if (segments.length > maxPathDepth) continue;
|
|
99
|
-
totalSegments += segments.length;
|
|
100
|
-
if (totalSegments > maxTotalSegments) {
|
|
101
|
-
return {
|
|
102
|
-
ok: false,
|
|
103
|
-
errors: [],
|
|
104
|
-
rejected: `payload total path segments exceeds maxTotalSegments=${maxTotalSegments}`
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
for (const entry of entryList) {
|
|
108
|
-
const message = typeof entry === "string" ? entry : entry.message;
|
|
109
|
-
const code = typeof entry === "string" ? defaultCode : entry.code;
|
|
110
|
-
if (message.length === 0) continue;
|
|
111
|
-
errors.push({
|
|
112
|
-
message,
|
|
113
|
-
path: Array.from(segments),
|
|
114
|
-
formKey,
|
|
115
|
-
code
|
|
116
|
-
});
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
return { ok: true, errors };
|
|
120
|
-
}
|
|
121
|
-
function extractDetails(payload) {
|
|
122
|
-
const wrappedError = payload["error"];
|
|
123
|
-
if (wrappedError !== null && wrappedError !== void 0 && typeof wrappedError === "object") {
|
|
124
|
-
const inner = wrappedError.details;
|
|
125
|
-
if (inner === void 0) {
|
|
126
|
-
return { ok: true, details: {} };
|
|
127
|
-
}
|
|
128
|
-
if (isDetailsRecord(inner)) return { ok: true, details: inner };
|
|
129
|
-
return {
|
|
130
|
-
ok: false,
|
|
131
|
-
reason: "error.details entries must be strings or { message, code } objects"
|
|
132
|
-
};
|
|
133
|
-
}
|
|
134
|
-
if (wrappedError !== null && wrappedError !== void 0 && typeof wrappedError !== "object") {
|
|
135
|
-
return {
|
|
136
|
-
ok: false,
|
|
137
|
-
reason: `payload.error was ${typeof wrappedError}, expected an object with { details }`
|
|
138
|
-
};
|
|
139
|
-
}
|
|
140
|
-
if ("details" in payload) {
|
|
141
|
-
const inner = payload["details"];
|
|
142
|
-
if (inner === void 0) return { ok: true, details: {} };
|
|
143
|
-
if (isDetailsRecord(inner)) return { ok: true, details: inner };
|
|
144
|
-
return { ok: false, reason: "details entries must be strings or { message, code } objects" };
|
|
145
|
-
}
|
|
146
|
-
if (isDetailsRecord(payload)) return { ok: true, details: payload };
|
|
147
|
-
if (Object.keys(payload).length === 0) return { ok: true, details: {} };
|
|
148
|
-
return { ok: false, reason: "unrecognised payload shape" };
|
|
149
|
-
}
|
|
150
|
-
function isStructuredEntry(value) {
|
|
151
|
-
if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
|
|
152
|
-
const obj = value;
|
|
153
|
-
return typeof obj.message === "string" && typeof obj.code === "string";
|
|
154
|
-
}
|
|
155
|
-
function isAcceptedEntry(value) {
|
|
156
|
-
return typeof value === "string" || isStructuredEntry(value);
|
|
157
|
-
}
|
|
158
|
-
function isDetailsRecord(value) {
|
|
159
|
-
if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
|
|
160
|
-
const record = value;
|
|
161
|
-
for (const k of Object.keys(record)) {
|
|
162
|
-
const v = record[k];
|
|
163
|
-
if (isAcceptedEntry(v)) continue;
|
|
164
|
-
if (Array.isArray(v) && v.every((entry) => isAcceptedEntry(entry))) continue;
|
|
165
|
-
return false;
|
|
166
|
-
}
|
|
167
|
-
return true;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
export { InvalidPathError, PARSE_API_ERRORS_DEFAULTS, canonicalizePath, escapeForInlineScript, parseApiErrors };
|
|
25
|
+
export { escapeForInlineScript };
|
|
171
26
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -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 { 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 const { maxEntries, maxPathDepth, maxTotalSegments, defaultCode } = normalizeParseCaps(options)\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 return convertDetailsToErrors(\n details,\n options.formKey,\n maxPathDepth,\n maxTotalSegments,\n defaultCode\n )\n}\n\n/** Resolved, sanitised size caps plus the fallback code for one `parseApiErrors` call. */\ntype NormalizedParseCaps = {\n readonly maxEntries: number\n readonly maxPathDepth: number\n readonly maxTotalSegments: number\n readonly defaultCode: string\n}\n\n/**\n * Resolve the optional size caps against `PARSE_API_ERRORS_DEFAULTS` and\n * sanitise them into safe integers.\n *\n * Comparison gates (`>` against the count / depth) yield `false` for\n * `NaN`, so without sanitisation a hostile or malformed `NaN` cap would\n * let pathological payloads run unbounded. `Infinity` would do the same.\n * Negatives and non-integers would discard legitimate entries. Falls\n * back to the library default on garbage.\n */\nfunction normalizeParseCaps(options: ParseApiErrorsOptions): NormalizedParseCaps {\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 return { maxEntries, maxPathDepth, maxTotalSegments, defaultCode }\n}\n\n/**\n * Walk an already-extracted, already-entry-capped details record into\n * `ValidationError[]`. Per-key paths are canonicalised, depth-capped,\n * and bounded by a running total-segment budget; each key's entries\n * expand to one `ValidationError` per non-empty message. Returns\n * `ok: false` only when the total-segment budget overflows mid-walk\n * (rejected wholesale, never partially applied).\n */\nfunction convertDetailsToErrors(\n details: ApiErrorDetails,\n formKey: FormKey,\n maxPathDepth: number,\n maxTotalSegments: number,\n defaultCode: string\n): ParseApiErrorsResult {\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 `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,\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"],"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"],"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;;;;"}
|
package/dist/nuxt.d.cts
CHANGED
package/dist/nuxt.d.mts
CHANGED
package/dist/nuxt.d.ts
CHANGED
|
@@ -30,7 +30,7 @@ import { computed, onUnmounted, ref, watch } from 'vue'
|
|
|
30
30
|
// We bump the tick on every host event we DO have a callback for
|
|
31
31
|
// (`onFormChange` / `onSubmitSuccess` / `onReset`), plus a 250ms
|
|
32
32
|
// polling fallback for state that changes outside those events (user
|
|
33
|
-
// errors via `
|
|
33
|
+
// errors via `setErrors`, submit-lifecycle flags). The panel's
|
|
34
34
|
// own reactivity then re-evaluates everything in one pass — cheap
|
|
35
35
|
// because the underlying reads are direct property accesses.
|
|
36
36
|
const updateTick = ref(0)
|
|
@@ -379,7 +379,7 @@ import { computed, onUnmounted, ref, watch } from 'vue'
|
|
|
379
379
|
|
|
380
380
|
// Polling fallback for state that changes outside the `onFormChange` /
|
|
381
381
|
// `onSubmitSuccess` / `onReset` event surface — user errors injected
|
|
382
|
-
// via `
|
|
382
|
+
// via `setErrors`, submit-lifecycle flags between events, or
|
|
383
383
|
// new forms registered in the host's registry. 120ms is faster than
|
|
384
384
|
// a human can notice between an input event and a visible panel
|
|
385
385
|
// refresh; gas-cost is negligible (one ref-bump every 120ms).
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const app = require('nuxt/app');
|
|
4
|
-
const devtoolsShared = require('../../shared/attaform.
|
|
5
|
-
const paths = require('../../shared/attaform.
|
|
4
|
+
const devtoolsShared = require('../../shared/attaform.BOi6n2Pn.cjs');
|
|
5
|
+
const paths = require('../../shared/attaform.B7UdTs_o.cjs');
|
|
6
6
|
|
|
7
7
|
var attaform_default = app.defineNuxtPlugin({
|
|
8
8
|
// `enforce: 'pre'` makes the "we run before any component's setup" claim
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { defineNuxtPlugin, useRuntimeConfig, useRoute } from 'nuxt/app';
|
|
2
|
-
import { r as renderAttaformState, h as hydrateAttaformState, D as DEVTOOLS_WINDOW_KEY } from '../../shared/attaform.
|
|
3
|
-
import {
|
|
2
|
+
import { r as renderAttaformState, h as hydrateAttaformState, D as DEVTOOLS_WINDOW_KEY } from '../../shared/attaform.0-00cYGw.mjs';
|
|
3
|
+
import { g as createAttaform, l as kAttaformWizardActiveStepResolver, i as getRegistryFromApp } from '../../shared/attaform.C0au8oXd.mjs';
|
|
4
4
|
|
|
5
5
|
var attaform_default = defineNuxtPlugin({
|
|
6
6
|
// `enforce: 'pre'` makes the "we run before any component's setup" claim
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { i as getRegistryFromApp, m as pathKeyToDotted } from './attaform.C0au8oXd.mjs';
|
|
2
2
|
|
|
3
3
|
function renderAttaformState(app) {
|
|
4
4
|
const registry = getRegistryFromApp(app);
|
|
@@ -32,4 +32,4 @@ function hydrateAttaformState(app, payload) {
|
|
|
32
32
|
const DEVTOOLS_WINDOW_KEY = "__attaform_devtools__";
|
|
33
33
|
|
|
34
34
|
export { DEVTOOLS_WINDOW_KEY as D, hydrateAttaformState as h, renderAttaformState as r };
|
|
35
|
-
//# sourceMappingURL=attaform.
|
|
35
|
+
//# sourceMappingURL=attaform.0-00cYGw.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attaform.
|
|
1
|
+
{"version":3,"file":"attaform.0-00cYGw.mjs","sources":["../../src/runtime/core/serialize.ts","../../src/runtime/core/devtools-shared.ts"],"sourcesContent":["import type { App } from 'vue'\nimport type { FormKey } from '../types/types-api'\nimport { pathKeyToDotted, type PathKey } from './paths'\nimport { getRegistryFromApp, type SerializedFormData } from './registry'\n\n/**\n * Serialised snapshot of every form in a Vue app, produced by\n * `renderAttaformState` and consumed by `hydrateAttaformState`.\n *\n * JSON-safe — pass to `JSON.stringify`, `devalue`, or any other\n * serialiser before embedding in your SSR payload.\n */\nexport type SerializedAttaformState = {\n /** Tuples of `[formKey, snapshot]` for every form in the app. */\n readonly forms: ReadonlyArray<readonly [FormKey, SerializedFormData]>\n}\n\n/**\n * Snapshot every form on a Vue app for SSR. Call from your server\n * entry after rendering the app:\n *\n * ```ts\n * import { renderToString } from '@vue/server-renderer'\n * import { renderAttaformState, escapeForInlineScript } from 'attaform'\n *\n * const html = await renderToString(app)\n * const state = renderAttaformState(app)\n * const payload = escapeForInlineScript(JSON.stringify(state))\n *\n * return `\n * ${html}\n * <script>window.__ATTAFORM_STATE__ = ${payload}</script>\n * `\n * ```\n *\n * Pair with `hydrateAttaformState` on the client to restore the\n * forms in their server-rendered state. Nuxt users don't need this —\n * `attaform/nuxt` wires SSR automatically.\n */\nexport function renderAttaformState(app: App): SerializedAttaformState {\n const registry = getRegistryFromApp(app)\n const forms: Array<readonly [FormKey, SerializedFormData]> = []\n for (const [key, state] of registry.forms) {\n // Skip the blank field when the set is empty so the\n // wire payload stays minimal for forms that don't use it. The\n // optional shape on the consuming side handles the absence\n // cleanly (defaults to \"no blank paths\"). PathKey → dotted at\n // the boundary so the wire shape matches the rest of the\n // public path notation.\n const transientList: string[] = []\n for (const pk of state.blankPaths) {\n const d = pathKeyToDotted(pk as PathKey)\n if (d !== null) transientList.push(d)\n }\n forms.push([\n key,\n {\n form: state.form.value,\n schemaErrors: Array.from(state.schemaErrors.entries()),\n userErrors: Array.from(state.userErrors.entries()),\n fields: Array.from(state.fields.entries()),\n ...(transientList.length > 0 ? { blankPaths: transientList } : {}),\n },\n ])\n }\n return { forms }\n}\n\n/**\n * Restore forms from a server-rendered snapshot on the client. Call\n * from your client entry before mounting:\n *\n * ```ts\n * import { createApp } from 'vue'\n * import { createAttaform, hydrateAttaformState } from 'attaform'\n *\n * const app = createApp(App).use(createAttaform())\n * hydrateAttaformState(app, window.__ATTAFORM_STATE__)\n * app.mount('#app')\n * ```\n *\n * The next `useForm({ key })` call for each serialised form picks up\n * the snapshot transparently — no further action is required.\n */\nexport function hydrateAttaformState(app: App, payload: SerializedAttaformState): void {\n const registry = getRegistryFromApp(app)\n for (const [key, data] of payload.forms) {\n registry.pendingHydration.set(key, data)\n }\n}\n","/**\n * Shared building blocks for Attaform's two devtools surfaces — the Vue\n * DevTools (Chrome-extension) inspector wired up in `./devtools.ts`, and\n * the Nuxt DevTools (overlay) panel wired up via `../../nuxt.ts` +\n * `../pages/_attaform_devtools.vue`.\n *\n * Houses the window-bridge contract both surfaces consume so a new\n * bridge field lands in one file. Both surfaces render RAW form values\n * by design — DevTools is a dev-only surface, and redaction across every\n * place a value surfaces is impractical security theater rather than a\n * real safeguard.\n */\nimport type { AttaformRegistry } from './registry'\n\n/**\n * Property key on `window` that the Nuxt-side dev plugin attaches the\n * bridge object to. The iframe-mounted overlay panel reads\n * `window.parent[DEVTOOLS_WINDOW_KEY]` to reach the host app's registry.\n *\n * Underscored + namespaced to make accidental collision with consumer\n * globals vanishingly unlikely. Stable across versions — bumping it\n * would silently disconnect older library builds from newer overlay\n * panels in the same browser tab during a library upgrade.\n */\nexport const DEVTOOLS_WINDOW_KEY = '__attaform_devtools__'\n\n/**\n * Shape of the object the host plugin attaches to `window` in dev mode.\n * The iframe overlay panel reads this to discover the live registry and\n * render its forms.\n *\n * Single-registry assumption: the latest `createAttaform()` install\n * wins. Multi-app pages (rare; typically only seen in micro-frontend\n * setups) will only see one app's forms in the panel. Documented but\n * not actively supported — the alternative (a Set of registries with\n * union-rendering) is a future call if a real consumer hits it.\n */\nexport interface AttaformDevtoolsBridge {\n registry: AttaformRegistry\n /**\n * The library version, surfaced in the panel's footer for support /\n * bug-report context. Read from `package.json` at host-plugin init.\n */\n version: string\n}\n\ndeclare global {\n interface Window {\n [DEVTOOLS_WINDOW_KEY]?: AttaformDevtoolsBridge\n }\n}\n"],"names":[],"mappings":";;AAuCO,SAAS,oBAAoB,GAAA,EAAmC;AACrE,EAAA,MAAM,QAAA,GAAW,mBAAmB,GAAG,CAAA;AACvC,EAAA,MAAM,QAAuD,EAAC;AAC9D,EAAA,KAAA,MAAW,CAAC,GAAA,EAAK,KAAK,CAAA,IAAK,SAAS,KAAA,EAAO;AAOzC,IAAA,MAAM,gBAA0B,EAAC;AACjC,IAAA,KAAA,MAAW,EAAA,IAAM,MAAM,UAAA,EAAY;AACjC,MAAA,MAAM,CAAA,GAAI,gBAAgB,EAAa,CAAA;AACvC,MAAA,IAAI,CAAA,KAAM,IAAA,EAAM,aAAA,CAAc,IAAA,CAAK,CAAC,CAAA;AAAA,IACtC;AACA,IAAA,KAAA,CAAM,IAAA,CAAK;AAAA,MACT,GAAA;AAAA,MACA;AAAA,QACE,IAAA,EAAM,MAAM,IAAA,CAAK,KAAA;AAAA,QACjB,cAAc,KAAA,CAAM,IAAA,CAAK,KAAA,CAAM,YAAA,CAAa,SAAS,CAAA;AAAA,QACrD,YAAY,KAAA,CAAM,IAAA,CAAK,KAAA,CAAM,UAAA,CAAW,SAAS,CAAA;AAAA,QACjD,QAAQ,KAAA,CAAM,IAAA,CAAK,KAAA,CAAM,MAAA,CAAO,SAAS,CAAA;AAAA,QACzC,GAAI,cAAc,MAAA,GAAS,CAAA,GAAI,EAAE,UAAA,EAAY,aAAA,KAAkB;AAAC;AAClE,KACD,CAAA;AAAA,EACH;AACA,EAAA,OAAO,EAAE,KAAA,EAAM;AACjB;AAkBO,SAAS,oBAAA,CAAqB,KAAU,OAAA,EAAwC;AACrF,EAAA,MAAM,QAAA,GAAW,mBAAmB,GAAG,CAAA;AACvC,EAAA,KAAA,MAAW,CAAC,GAAA,EAAK,IAAI,CAAA,IAAK,QAAQ,KAAA,EAAO;AACvC,IAAA,QAAA,CAAS,gBAAA,CAAiB,GAAA,CAAI,GAAA,EAAK,IAAI,CAAA;AAAA,EACzC;AACF;;ACjEO,MAAM,mBAAA,GAAsB;;;;"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import { G as GenericForm,
|
|
2
|
+
import { G as GenericForm, u as FlatPath, O as NestedType, F as FormKey, U as UseFormConfiguration, a as AbstractSchema, D as DefaultValuesInput, af as ValidateOnConfig, b as UseFormReturnType } from './attaform.DBhrKb2j.mjs';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* The shape `form.values.<key>` returns at runtime.
|
|
@@ -120,6 +120,38 @@ type StorageLeaf<L> = InputIsUnknown<L> extends true ? unknown : L extends {
|
|
|
120
120
|
};
|
|
121
121
|
} ? Out : never;
|
|
122
122
|
|
|
123
|
+
/**
|
|
124
|
+
* The Zod v4 schema roots `useForm` accepts. A fixed-shape
|
|
125
|
+
* `z.object({ … })` is the common case; `z.record(K, V)` admits a
|
|
126
|
+
* dictionary form (a homogeneous map with runtime-known keys);
|
|
127
|
+
* `z.discriminatedUnion(disc, [...])` admits a variant form (one of
|
|
128
|
+
* several object shapes, picked by a discriminator). Each projects to
|
|
129
|
+
* a `GenericForm`-shaped value the form engine drives: an object root
|
|
130
|
+
* descends into its fixed keys, a record root treats its entries as an
|
|
131
|
+
* open live-keyed container, a variant root lifts the active variant's
|
|
132
|
+
* keys into one addressable surface.
|
|
133
|
+
*
|
|
134
|
+
* Other roots (a bare `z.union`, a root `z.array`, primitives,
|
|
135
|
+
* `z.map` / `z.set`) are not form roots; wrap them under a key. The
|
|
136
|
+
* adapter's runtime construction rejects them with a legible error.
|
|
137
|
+
*
|
|
138
|
+
* `z.ZodObject` MUST keep its `z.ZodRawShape` argument, and the
|
|
139
|
+
* discriminated-union arm MUST stay fully applied. The unified entry's
|
|
140
|
+
* v4 overload constrains on `SupportedRootSchema & ZodV4Internals` to
|
|
141
|
+
* keep v3 schemas out (see `../unified/types-zod-major.ts`). In a
|
|
142
|
+
* single-major (v3-only) consumer install, the published `.d.mts`
|
|
143
|
+
* resolves this `z` to v3, where a bare or under-applied Zod class is
|
|
144
|
+
* missing its required arguments and degrades to an `any`-like error
|
|
145
|
+
* type; `any` then absorbs the union and `any & ZodV4Internals`
|
|
146
|
+
* collapses back to `any`, so the marker stops excluding v3 schemas and
|
|
147
|
+
* the read slot poisons to `never`. `z.ZodRecord` carries defaults for
|
|
148
|
+
* both arguments, so it stays concrete in either major.
|
|
149
|
+
* `z.ZodDiscriminatedUnion` does NOT (v3 requires both arguments, and
|
|
150
|
+
* its parameter order is the reverse of v4's), so the arm is written
|
|
151
|
+
* fully applied; the v3-only bundled-types fixture guards it.
|
|
152
|
+
*/
|
|
153
|
+
type SupportedRootSchema = z.ZodObject<z.ZodRawShape> | z.ZodRecord | z.ZodDiscriminatedUnion<readonly z.ZodObject<z.ZodRawShape>[], string>;
|
|
154
|
+
|
|
123
155
|
/**
|
|
124
156
|
* Zod v4 adapter entry point. Re-exports the adapter + the useForm
|
|
125
157
|
* wrapper that threads zod-v4-specific schema types through
|
|
@@ -188,12 +220,12 @@ type PathOutput<Schema extends z.ZodType, Path extends string> = z.output<Schema
|
|
|
188
220
|
* generics ride on the alias rather than re-evaluating the
|
|
189
221
|
* conditional from scratch.
|
|
190
222
|
*/
|
|
191
|
-
type FormOf<Schema extends
|
|
192
|
-
type OutOf<Schema extends
|
|
193
|
-
type ReadOf<Schema extends
|
|
194
|
-
declare function useForm<Schema extends
|
|
223
|
+
type FormOf<Schema extends SupportedRootSchema> = z.input<Schema> extends GenericForm ? z.input<Schema> : never;
|
|
224
|
+
type OutOf<Schema extends SupportedRootSchema> = z.output<Schema> extends GenericForm ? z.output<Schema> : never;
|
|
225
|
+
type ReadOf<Schema extends SupportedRootSchema> = StorageShape<Schema> extends GenericForm ? StorageShape<Schema> : never;
|
|
226
|
+
declare function useForm<Schema extends SupportedRootSchema, K extends FormKey = FormKey>(configuration: Omit<UseFormConfiguration<FormOf<Schema>, OutOf<Schema>, AbstractSchema<FormOf<Schema>, OutOf<Schema>>, DefaultValuesInput<FormOf<Schema>>, K>, 'schema' | 'validateOn' | 'debounceMs'> & {
|
|
195
227
|
schema: Schema;
|
|
196
228
|
} & ValidateOnConfig): UseFormReturnType<FormOf<Schema>, OutOf<Schema>, ReadOf<Schema>, K>;
|
|
197
229
|
|
|
198
230
|
export { useForm as u };
|
|
199
|
-
export type { PathInput as P,
|
|
231
|
+
export type { PathInput as P, SupportedRootSchema as S, PathOutput as a, StorageShape as b };
|