attaform 0.21.0 → 0.21.2
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/dist/chunks/dev-key-collision-warnings.cjs +1 -1
- package/dist/chunks/dev-key-collision-warnings.mjs +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/chunks/indexeddb.cjs +1 -1
- package/dist/chunks/indexeddb.mjs +1 -1
- package/dist/chunks/local-storage.cjs +1 -1
- package/dist/chunks/local-storage.mjs +1 -1
- package/dist/chunks/multi-tab-sync.cjs +2 -2
- package/dist/chunks/multi-tab-sync.mjs +2 -2
- package/dist/chunks/session-storage.cjs +1 -1
- package/dist/chunks/session-storage.mjs +1 -1
- package/dist/chunks/wire-persistence.cjs +2 -2
- package/dist/chunks/wire-persistence.mjs +2 -2
- package/dist/index.cjs +37 -24
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +31 -28
- package/dist/index.d.mts +31 -28
- package/dist/index.d.ts +31 -28
- package/dist/index.mjs +38 -25
- 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 +396 -216
- package/dist/runtime/components/DevtoolsValueTree.vue +176 -114
- package/dist/runtime/plugins/attaform.cjs +2 -2
- package/dist/runtime/plugins/attaform.mjs +2 -2
- package/dist/shared/{attaform.BA3vRDos.cjs → attaform.B5LNzqQh.cjs} +349 -277
- package/dist/shared/attaform.B5LNzqQh.cjs.map +1 -0
- package/dist/shared/{attaform.PnqML3xW.cjs → attaform.BBDIKtKY.cjs} +13 -16
- package/dist/shared/attaform.BBDIKtKY.cjs.map +1 -0
- package/dist/shared/{attaform.CRsXyy-Y.d.ts → attaform.BCcrLApm.d.mts} +131 -64
- package/dist/shared/{attaform.BupwXkj_.mjs → attaform.BFWb6hDk.mjs} +29 -23
- package/dist/shared/attaform.BFWb6hDk.mjs.map +1 -0
- package/dist/shared/{attaform.7lzO9pdM.d.mts → attaform.BGf_J22U.d.ts} +131 -64
- package/dist/shared/{attaform.BnK_bfcb.mjs → attaform.BVeLgfEh.mjs} +14 -17
- package/dist/shared/attaform.BVeLgfEh.mjs.map +1 -0
- package/dist/shared/{attaform.BK1RE2ha.d.ts → attaform.BYgioWLF.d.ts} +2 -2
- package/dist/shared/{attaform.F8LMHHWV.d.cts → attaform.BkjJfMvJ.d.cts} +131 -64
- package/dist/shared/{attaform.BDIEq9qP.d.cts → attaform.BoY6RZUl.d.cts} +2 -2
- package/dist/shared/{attaform.CEf6wYfD.cjs → attaform.BwLp9KM7.cjs} +2 -2
- package/dist/shared/{attaform.CEf6wYfD.cjs.map → attaform.BwLp9KM7.cjs.map} +1 -1
- package/dist/shared/{attaform._rsCZy2j.cjs → attaform.BwrowMp2.cjs} +25 -45
- package/dist/shared/attaform.BwrowMp2.cjs.map +1 -0
- package/dist/shared/{attaform.ezb5Nh2t.mjs → attaform.C41gjp-a.mjs} +2 -2
- package/dist/shared/{attaform.ezb5Nh2t.mjs.map → attaform.C41gjp-a.mjs.map} +1 -1
- package/dist/shared/{attaform.BM6YD9kZ.cjs → attaform.CR6wGvNu.cjs} +29 -23
- package/dist/shared/attaform.CR6wGvNu.cjs.map +1 -0
- package/dist/shared/{attaform.DSqO6Db7.mjs → attaform.CTheKoTc.mjs} +705 -282
- package/dist/shared/attaform.CTheKoTc.mjs.map +1 -0
- package/dist/shared/{attaform.BzvOdiSI.cjs → attaform.CcnF1AKJ.cjs} +4 -4
- package/dist/shared/attaform.CcnF1AKJ.cjs.map +1 -0
- package/dist/shared/{attaform.BQ6drorq.d.mts → attaform.CnEl--PF.d.mts} +2 -2
- package/dist/shared/{attaform.CkjTapyq.mjs → attaform.CrD73S4m.mjs} +4 -4
- package/dist/shared/attaform.CrD73S4m.mjs.map +1 -0
- package/dist/shared/{attaform.BUszFoKq.cjs → attaform.D2ZuIOCf.cjs} +711 -287
- package/dist/shared/attaform.D2ZuIOCf.cjs.map +1 -0
- package/dist/shared/{attaform.r3PePkDR.mjs → attaform.D6GYGshL.mjs} +25 -45
- package/dist/shared/attaform.D6GYGshL.mjs.map +1 -0
- package/dist/shared/{attaform.Y_Mgg0Yp.mjs → attaform.DP-u7_tk.mjs} +348 -277
- package/dist/shared/attaform.DP-u7_tk.mjs.map +1 -0
- package/dist/shared/{attaform.B1nyO4ec.d.cts → attaform.ory-3WhV.d.cts} +395 -176
- package/dist/shared/{attaform.B1nyO4ec.d.mts → attaform.ory-3WhV.d.mts} +395 -176
- package/dist/shared/{attaform.B1nyO4ec.d.ts → attaform.ory-3WhV.d.ts} +395 -176
- 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 +3 -4
- package/dist/zod-v3.cjs.map +1 -1
- package/dist/zod-v3.d.cts +4 -4
- package/dist/zod-v3.d.mts +4 -4
- package/dist/zod-v3.d.ts +4 -4
- package/dist/zod-v3.mjs +2 -3
- package/dist/zod-v3.mjs.map +1 -1
- package/dist/zod-v4.cjs +3 -4
- package/dist/zod-v4.cjs.map +1 -1
- package/dist/zod-v4.d.cts +4 -4
- package/dist/zod-v4.d.mts +4 -4
- package/dist/zod-v4.d.ts +4 -4
- package/dist/zod-v4.mjs +2 -3
- package/dist/zod-v4.mjs.map +1 -1
- package/dist/zod.cjs +6 -6
- package/dist/zod.cjs.map +1 -1
- package/dist/zod.d.cts +77 -26
- package/dist/zod.d.mts +77 -26
- package/dist/zod.d.ts +77 -26
- package/dist/zod.mjs +5 -6
- package/dist/zod.mjs.map +1 -1
- package/package.json +3 -11
- package/dist/shared/attaform.BA3vRDos.cjs.map +0 -1
- package/dist/shared/attaform.BM6YD9kZ.cjs.map +0 -1
- package/dist/shared/attaform.BUszFoKq.cjs.map +0 -1
- package/dist/shared/attaform.BnK_bfcb.mjs.map +0 -1
- package/dist/shared/attaform.BupwXkj_.mjs.map +0 -1
- package/dist/shared/attaform.BzvOdiSI.cjs.map +0 -1
- package/dist/shared/attaform.CkjTapyq.mjs.map +0 -1
- package/dist/shared/attaform.DSqO6Db7.mjs.map +0 -1
- package/dist/shared/attaform.PnqML3xW.cjs.map +0 -1
- package/dist/shared/attaform.Y_Mgg0Yp.mjs.map +0 -1
- package/dist/shared/attaform._rsCZy2j.cjs.map +0 -1
- package/dist/shared/attaform.r3PePkDR.mjs.map +0 -1
- package/dist/shared/{attaform.DSD85fHb.d.cts → attaform.nf83TIR5.d.cts} +10 -10
- package/dist/shared/{attaform.DSD85fHb.d.mts → attaform.nf83TIR5.d.mts} +10 -10
- package/dist/shared/{attaform.DSD85fHb.d.ts → attaform.nf83TIR5.d.ts} +10 -10
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { P as PERSISTENCE_MODULE_KEY,
|
|
1
|
+
import { P as PERSISTENCE_MODULE_KEY, n as normalizePersistConfig } from '../shared/attaform.CTheKoTc.mjs';
|
|
2
2
|
|
|
3
3
|
async function warnOnSchemaFingerprintMismatch(key, existing, incoming) {
|
|
4
4
|
let existingFp;
|
package/dist/chunks/devtools.cjs
CHANGED
package/dist/chunks/devtools.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const canonicalStringify = require('../shared/attaform.CQN9R62B.cjs');
|
|
4
|
-
const index = require('../shared/attaform.
|
|
4
|
+
const index = require('../shared/attaform.BBDIKtKY.cjs');
|
|
5
5
|
|
|
6
6
|
const cyclicSentinel = "<cyclic>";
|
|
7
7
|
function fingerprintZodSchema(schema) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { c as canonicalStringify } from '../shared/attaform.BJGA_UOS.mjs';
|
|
2
|
-
import {
|
|
2
|
+
import { k as kindOf, g as getIntersectionLeft, b as getIntersectionRight, c as unwrapLazy, d as unwrapInner, e as unwrapPipe, h as getDiscriminator, i as getDiscriminatedOptions, j as getUnionOptions, l as getRecordKeyType, m as getRecordValueType, n as getTupleItems, o as getSetValueType, p as getArrayElement, q as getObjectShape, r as getEnumValues, s as getLiteralValues, t as getChecks, v as getCatchDefault, x as getDefaultValue } from '../shared/attaform.BVeLgfEh.mjs';
|
|
3
3
|
|
|
4
4
|
const cyclicSentinel = "<cyclic>";
|
|
5
5
|
function fingerprintZodSchema(schema) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const injectWizard = require('../shared/attaform.
|
|
4
|
-
const paths = require('../shared/attaform.
|
|
3
|
+
const injectWizard = require('../shared/attaform.D2ZuIOCf.cjs');
|
|
4
|
+
const paths = require('../shared/attaform.B5LNzqQh.cjs');
|
|
5
5
|
|
|
6
6
|
const PROTOCOL_VERSION = 1;
|
|
7
7
|
const JOIN_COLLECTION_WINDOW_MS = 50;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { s as structuralSnapshot, e as isPlainRecord, f as safeAssign, g as diffAndApply, h as slimKindOf, j as applyPatchesForward } from '../shared/attaform.CTheKoTc.mjs';
|
|
2
|
+
import { a as canonicalizePath } from '../shared/attaform.DP-u7_tk.mjs';
|
|
3
3
|
|
|
4
4
|
const PROTOCOL_VERSION = 1;
|
|
5
5
|
const JOIN_COLLECTION_WINDOW_MS = 50;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const vue = require('vue');
|
|
4
|
-
const injectWizard = require('../shared/attaform.
|
|
5
|
-
const paths = require('../shared/attaform.
|
|
4
|
+
const injectWizard = require('../shared/attaform.D2ZuIOCf.cjs');
|
|
5
|
+
const paths = require('../shared/attaform.B5LNzqQh.cjs');
|
|
6
6
|
|
|
7
7
|
const PERSISTED_ENVELOPE_VERSION = 6;
|
|
8
8
|
function readPersistedPayload(value) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { toRaw } from 'vue';
|
|
2
|
-
import {
|
|
3
|
-
import { _ as __DEV__,
|
|
2
|
+
import { t as getAtPath, v as setAtPath, e as isPlainRecord, f as safeAssign, w as resolveStorageKeyBase, k as normalizeNumericOption, x as DEFAULT_PERSISTENCE_DEBOUNCE_MS, y as cleanupOrphanKeys, z as mergeSparseHydration, B as deleteAtPath } from '../shared/attaform.CTheKoTc.mjs';
|
|
3
|
+
import { _ as __DEV__, s as segmentsForPathKey, l as isPathPrefix, a as canonicalizePath } from '../shared/attaform.DP-u7_tk.mjs';
|
|
4
4
|
|
|
5
5
|
const PERSISTED_ENVELOPE_VERSION = 6;
|
|
6
6
|
function readPersistedPayload(value) {
|
package/dist/index.cjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const paths = require('./shared/attaform.
|
|
4
|
-
const devtoolsShared = require('./shared/attaform.
|
|
5
|
-
const injectWizard = require('./shared/attaform.
|
|
3
|
+
const paths = require('./shared/attaform.B5LNzqQh.cjs');
|
|
4
|
+
const devtoolsShared = require('./shared/attaform.BwLp9KM7.cjs');
|
|
5
|
+
const injectWizard = require('./shared/attaform.D2ZuIOCf.cjs');
|
|
6
6
|
|
|
7
7
|
function escapeForInlineScript(json) {
|
|
8
8
|
return json.replace(/[<>&\u2028\u2029]/g, (char) => {
|
|
@@ -30,6 +30,35 @@ const PARSE_API_ERRORS_DEFAULTS = {
|
|
|
30
30
|
defaultCode: "api:unknown"
|
|
31
31
|
};
|
|
32
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) {
|
|
33
62
|
const maxEntries = injectWizard.normalizeNumericOption({
|
|
34
63
|
value: options.maxEntries ?? PARSE_API_ERRORS_DEFAULTS.maxEntries,
|
|
35
64
|
source: "parseApiErrors.maxEntries",
|
|
@@ -52,25 +81,9 @@ function parseApiErrors(payload, options) {
|
|
|
52
81
|
defaultValue: PARSE_API_ERRORS_DEFAULTS.maxTotalSegments
|
|
53
82
|
});
|
|
54
83
|
const defaultCode = options.defaultCode ?? PARSE_API_ERRORS_DEFAULTS.defaultCode;
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
if (typeof payload !== "object") {
|
|
59
|
-
return { ok: false, errors: [], rejected: `payload was ${typeof payload}, expected object` };
|
|
60
|
-
}
|
|
61
|
-
const extraction = extractDetails(payload);
|
|
62
|
-
if (!extraction.ok) {
|
|
63
|
-
return { ok: false, errors: [], rejected: extraction.reason };
|
|
64
|
-
}
|
|
65
|
-
const { details } = extraction;
|
|
66
|
-
const entryCount = Object.keys(details).length;
|
|
67
|
-
if (entryCount > maxEntries) {
|
|
68
|
-
return {
|
|
69
|
-
ok: false,
|
|
70
|
-
errors: [],
|
|
71
|
-
rejected: `payload has ${entryCount} entries, exceeds maxEntries=${maxEntries}`
|
|
72
|
-
};
|
|
73
|
-
}
|
|
84
|
+
return { maxEntries, maxPathDepth, maxTotalSegments, defaultCode };
|
|
85
|
+
}
|
|
86
|
+
function convertDetailsToErrors(details, formKey, maxPathDepth, maxTotalSegments, defaultCode) {
|
|
74
87
|
const errors = [];
|
|
75
88
|
let totalSegments = 0;
|
|
76
89
|
for (const [key, value] of Object.entries(details)) {
|
|
@@ -98,7 +111,7 @@ function parseApiErrors(payload, options) {
|
|
|
98
111
|
errors.push({
|
|
99
112
|
message,
|
|
100
113
|
path: Array.from(segments),
|
|
101
|
-
formKey
|
|
114
|
+
formKey,
|
|
102
115
|
code
|
|
103
116
|
});
|
|
104
117
|
}
|
|
@@ -174,7 +187,6 @@ exports.isPathPrefix = paths.isPathPrefix;
|
|
|
174
187
|
exports.isRegisterValue = paths.isRegisterValue;
|
|
175
188
|
exports.kAttaformRegistry = paths.kAttaformRegistry;
|
|
176
189
|
exports.parseDottedPath = paths.parseDottedPath;
|
|
177
|
-
exports.useRegister = paths.useRegister;
|
|
178
190
|
exports.useRegistry = paths.useRegistry;
|
|
179
191
|
exports.vRegister = paths.vRegister;
|
|
180
192
|
exports.DEVTOOLS_WINDOW_KEY = devtoolsShared.DEVTOOLS_WINDOW_KEY;
|
|
@@ -192,6 +204,7 @@ exports.lazy = injectWizard.lazy;
|
|
|
192
204
|
exports.makeDefaultDisplayState = injectWizard.makeDefaultDisplayState;
|
|
193
205
|
exports.unset = injectWizard.unset;
|
|
194
206
|
exports.useForm = injectWizard.useAbstractForm;
|
|
207
|
+
exports.useRegister = injectWizard.useRegister;
|
|
195
208
|
exports.useWizard = injectWizard.useWizard;
|
|
196
209
|
exports.PARSE_API_ERRORS_DEFAULTS = PARSE_API_ERRORS_DEFAULTS;
|
|
197
210
|
exports.escapeForInlineScript = escapeForInlineScript;
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","sources":["../src/runtime/core/serialize-script.ts","../src/runtime/core/parse-api-errors.ts"],"sourcesContent":["/**\n * Escape a JSON string so it's safe to embed inside an inline\n * `<script>` tag during SSR. Plain `JSON.stringify` is not safe — a\n * form value containing the literal substring `</script>` would\n * break out of the script tag.\n *\n * ```ts\n * const payload = escapeForInlineScript(JSON.stringify(renderAttaformState(app)))\n * // `<script>window.__ATTAFORM_STATE__ = ${payload}</script>` is safe.\n * ```\n *\n * Output remains valid JSON — `JSON.parse` round-trips back to the\n * original value on the client.\n */\nexport function escapeForInlineScript(json: string): string {\n return json.replace(/[<>&\\u2028\\u2029]/g, (char) => {\n switch (char) {\n case '<':\n return '\\\\u003c'\n case '>':\n return '\\\\u003e'\n case '&':\n return '\\\\u0026'\n case '\\u2028':\n return '\\\\u2028'\n case '\\u2029':\n return '\\\\u2029'\n default:\n return char\n }\n })\n}\n","import type {\n ApiErrorDetails,\n ApiErrorEntry,\n ApiErrorEnvelope,\n FormKey,\n ValidationError,\n} from '../types/types-api'\nimport { normalizeNumericOption } from './defaults'\nimport { InvalidPathError } from './errors'\nimport { canonicalizePath } from './paths'\n\n/**\n * Result of `parseApiErrors`. Branch on `ok` to handle the two cases:\n *\n * ```ts\n * const result = parseApiErrors(payload, { formKey: form.key })\n * if (result.ok) {\n * form.setFieldErrors(result.errors)\n * } else {\n * console.warn('Bad error payload:', result.rejected)\n * }\n * ```\n *\n * `ok: true` means the payload was recognised — `errors` may still be\n * empty if the payload was valid but had no actual errors.\n * `ok: false` means the payload didn't match a known shape; `rejected`\n * carries a one-line description of why.\n */\nexport type ParseApiErrorsResult = {\n /** `true` when the payload was recognised; `false` when the shape was unfamiliar. */\n readonly ok: boolean\n /** Errors extracted from the payload. May be empty even when `ok: true`. */\n readonly errors: ValidationError[]\n /** When `ok: false`, a one-line description of why the payload was rejected. */\n readonly rejected?: string\n}\n\n/**\n * Options for `parseApiErrors`. The size caps protect against\n * misbehaving or hostile servers — exceeding any cap causes the\n * parser to reject the payload wholesale rather than partially apply.\n */\nexport type ParseApiErrorsOptions = {\n /**\n * The form's identifier — pass `form.key`. Stamped on every\n * produced `ValidationError` so errors route to the right form.\n */\n readonly formKey: FormKey\n /**\n * Code stamped on `ValidationError`s synthesized from bare-string\n * entries (the Rails / DRF / Laravel `{ field: [\"msg\"] }` shape).\n * Default `'api:unknown'`. Pick something more specific\n * (`'api:server-validation'`, `'myapp:legacy'`, …) when you know\n * the source.\n *\n * Structured `{ message, code }` entries forward their `code`\n * verbatim and ignore this option.\n */\n readonly defaultCode?: string\n /**\n * Maximum number of distinct keys to accept. Default `1000`.\n * Raise for trusted backends that legitimately produce more.\n */\n readonly maxEntries?: number\n /**\n * Maximum number of path segments per key. Default `32`. Keys\n * deeper than this are dropped (the rest of the payload still\n * applies if it stays under the other caps).\n */\n readonly maxPathDepth?: number\n /**\n * Maximum total path segments summed across every accepted key.\n * Default `10000`. Bounds the worst-case traversal cost.\n */\n readonly maxTotalSegments?: number\n}\n\n/**\n * Default size caps + default fallback code used by `parseApiErrors`.\n * Conservative; pass larger values (or a more specific code) via the\n * options bag for trusted-backend integrations.\n */\nexport const PARSE_API_ERRORS_DEFAULTS = {\n maxEntries: 1000,\n maxPathDepth: 32,\n maxTotalSegments: 10000,\n defaultCode: 'api:unknown',\n} as const\n\n/**\n * Normalise a server-side validation error payload into\n * `ValidationError[]`. Pair with `form.setFieldErrors` /\n * `form.addFieldErrors` to surface server errors on the form:\n *\n * ```ts\n * const response = await fetch('/api/signup', { … })\n * if (!response.ok) {\n * const payload = await response.json()\n * const result = parseApiErrors(payload, { formKey: form.key })\n * if (result.ok) form.setFieldErrors(result.errors)\n * }\n * ```\n *\n * Recognised payload shapes:\n *\n * - Wrapped envelope:\n * `{ error: { details: { email: { message: 'taken', code: 'api:duplicate-email' } } } }`\n * - Unwrapped envelope:\n * `{ details: { email: { message: 'taken', code: 'api:duplicate-email' } } }`\n * - Raw details record:\n * `{ email: { message: 'taken', code: 'api:duplicate-email' } }`\n * - **Bare-string Rails / DRF / Laravel shape:**\n * `{ email: ['Email already taken.'], username: 'too short' }`\n * - `null` / `undefined` — returns `{ ok: true, errors: [] }`\n *\n * Two entry shapes are accepted:\n *\n * 1. **Structured** — `{ message: string, code: string }`. The `code`\n * is forwarded verbatim onto the produced `ValidationError`.\n * 2. **Bare-string** — a plain string. Synthesized into\n * `{ message: <string>, code: <defaultCode> }` where `defaultCode`\n * comes from `options.defaultCode` (default `'api:unknown'`).\n * Useful for the Rails / Django REST Framework / FastAPI / Laravel\n * JSON shape that doesn't carry a per-field code.\n *\n * Each detail key's value can be a single entry, an array, or a mix\n * of structured and bare-string entries; arrays expand into one\n * `ValidationError` per entry. Pick a prefix on the server (`api:`,\n * `auth:`, etc.) and stay consistent so error renderers can branch\n * on `code` — or rely on `defaultCode` when the wire shape is\n * message-only.\n *\n * Dotted keys (`\"address.line1\"`) are split into structured paths\n * automatically. Use a custom server response shape outside these\n * patterns? Build the `ValidationError[]` array yourself and pass\n * it to `setFieldErrors` directly — `parseApiErrors` is just a\n * convenience for the common shapes.\n */\nexport function parseApiErrors(\n payload: ApiErrorEnvelope | ApiErrorDetails | null | undefined | unknown,\n options: ParseApiErrorsOptions\n): ParseApiErrorsResult {\n // Sanitise the caps. Comparison gates (`>` against the count /\n // depth) yield `false` for `NaN`, so without sanitisation a\n // hostile or malformed `NaN` cap would let pathological payloads\n // run unbounded. `Infinity` would do the same. Negatives and\n // non-integers would discard legitimate entries. Falls back to\n // the library default on garbage.\n const maxEntries = normalizeNumericOption({\n value: options.maxEntries ?? PARSE_API_ERRORS_DEFAULTS.maxEntries,\n source: 'parseApiErrors.maxEntries',\n allowInfinity: false,\n min: 0,\n defaultValue: PARSE_API_ERRORS_DEFAULTS.maxEntries,\n })\n const maxPathDepth = normalizeNumericOption({\n value: options.maxPathDepth ?? PARSE_API_ERRORS_DEFAULTS.maxPathDepth,\n source: 'parseApiErrors.maxPathDepth',\n allowInfinity: false,\n min: 0,\n defaultValue: PARSE_API_ERRORS_DEFAULTS.maxPathDepth,\n })\n const maxTotalSegments = normalizeNumericOption({\n value: options.maxTotalSegments ?? PARSE_API_ERRORS_DEFAULTS.maxTotalSegments,\n source: 'parseApiErrors.maxTotalSegments',\n allowInfinity: false,\n min: 0,\n defaultValue: PARSE_API_ERRORS_DEFAULTS.maxTotalSegments,\n })\n const defaultCode = options.defaultCode ?? PARSE_API_ERRORS_DEFAULTS.defaultCode\n\n if (payload === null || payload === undefined) {\n return { ok: true, errors: [] }\n }\n if (typeof payload !== 'object') {\n return { ok: false, errors: [], rejected: `payload was ${typeof payload}, expected object` }\n }\n\n const extraction = extractDetails(payload as Record<string, unknown>)\n if (!extraction.ok) {\n return { ok: false, errors: [], rejected: extraction.reason }\n }\n\n const { details } = extraction\n const entryCount = Object.keys(details).length\n // Enforce the guardrails before we spend time walking the payload.\n // Rejecting wholesale (not partial-applying) keeps the failure visible\n // so consumers can tune the caps or investigate the server payload.\n if (entryCount > maxEntries) {\n return {\n ok: false,\n errors: [],\n rejected: `payload has ${entryCount} entries, exceeds maxEntries=${maxEntries}`,\n }\n }\n\n const errors: ValidationError[] = []\n let totalSegments = 0\n for (const [key, value] of Object.entries(details)) {\n const entryList: ReadonlyArray<string | ApiErrorEntry> = Array.isArray(value) ? value : [value]\n // `canonicalizePath` throws `InvalidPathError` for dotted strings with\n // empty segments (e.g. `'. '`, `'a..b'`). A misbehaving server can\n // genuinely emit such a key; the hydrator is a normaliser, not a\n // validator, so we drop offending keys rather than let the exception\n // escape. Well-formed keys continue as normal.\n let segments: readonly (string | number)[]\n try {\n segments = canonicalizePath(key).segments\n } catch (err) {\n if (err instanceof InvalidPathError) continue\n throw err\n }\n // Per-path depth cap. We drop the offending key (rather than\n // rejecting the whole payload) because a single stray deep path\n // in an otherwise legitimate error set is still worth surfacing\n // the rest. Consumers who want strict rejection can post-filter\n // on `result.errors.length < details entryCount`.\n if (segments.length > maxPathDepth) continue\n // Total-segment cap. Enforced wholesale (not per-key) so a payload\n // that passes the per-key gate but stacks into a pathological\n // total still fails visibly. Mirrors `maxEntries` strictness.\n totalSegments += segments.length\n if (totalSegments > maxTotalSegments) {\n return {\n ok: false,\n errors: [],\n rejected: `payload total path segments exceeds maxTotalSegments=${maxTotalSegments}`,\n }\n }\n for (const entry of entryList) {\n // Bare-string entries (Rails / DRF / Laravel shape) synthesize a\n // `code` from `options.defaultCode`; structured `{ message, code }`\n // entries forward `code` verbatim. Empty messages drop silently\n // (`{ message: '' }` or `''`) — same recoverable-malformed-server\n // policy as before.\n const message = typeof entry === 'string' ? entry : entry.message\n const code = typeof entry === 'string' ? defaultCode : entry.code\n if (message.length === 0) continue\n errors.push({\n message,\n path: Array.from(segments),\n formKey: options.formKey,\n code,\n })\n }\n }\n return { ok: true, errors }\n}\n\ntype ExtractResult = { ok: true; details: ApiErrorDetails } | { ok: false; reason: string }\n\nfunction extractDetails(payload: Record<string, unknown>): ExtractResult {\n const wrappedError = payload['error']\n if (wrappedError !== null && wrappedError !== undefined && typeof wrappedError === 'object') {\n const inner = (wrappedError as { details?: unknown }).details\n if (inner === undefined) {\n // A wrapped envelope without details is considered \"no errors\" — valid shape.\n return { ok: true, details: {} }\n }\n if (isDetailsRecord(inner)) return { ok: true, details: inner }\n return {\n ok: false,\n reason: 'error.details entries must be strings or { message, code } objects',\n }\n }\n\n // `{ error: 'oops' }` / `{ error: 42 }` is a malformed wrapped envelope —\n // the server meant an error object but sent a scalar. Without this guard\n // the payload would fall through to the raw-details branch below, where\n // `{ error: 'oops' }` satisfies `isDetailsRecord` and silently produces\n // a phantom `ValidationError` at path `['error']`.\n if (wrappedError !== null && wrappedError !== undefined && typeof wrappedError !== 'object') {\n return {\n ok: false,\n reason: `payload.error was ${typeof wrappedError}, expected an object with { details }`,\n }\n }\n\n if ('details' in payload) {\n const inner = payload['details']\n if (inner === undefined) return { ok: true, details: {} }\n if (isDetailsRecord(inner)) return { ok: true, details: inner }\n return { ok: false, reason: 'details entries must be strings or { message, code } objects' }\n }\n\n if (isDetailsRecord(payload)) return { ok: true, details: payload }\n\n // Heuristic: if the payload has keys but none of them look like details,\n // it's probably a completely different shape. Reject.\n if (Object.keys(payload).length === 0) return { ok: true, details: {} }\n return { ok: false, reason: 'unrecognised payload shape' }\n}\n\nfunction isStructuredEntry(value: unknown): value is ApiErrorEntry {\n if (value === null || typeof value !== 'object' || Array.isArray(value)) return false\n const obj = value as { message?: unknown; code?: unknown }\n return typeof obj.message === 'string' && typeof obj.code === 'string'\n}\n\n/**\n * Accepts either a structured `{ message, code }` entry OR a bare\n * string. Bare strings synthesize a `code` at parse time\n * (`options.defaultCode`) and are useful for the Rails / Django REST\n * Framework / Laravel JSON shape that doesn't carry a per-field code.\n */\nfunction isAcceptedEntry(value: unknown): value is string | ApiErrorEntry {\n return typeof value === 'string' || isStructuredEntry(value)\n}\n\n/**\n * A record is a \"details\" record when every value is either an\n * accepted entry or an array of accepted entries (mixing structured +\n * bare-string in the same array is fine; the parser normalises per\n * entry). Half-structured objects (e.g. `{ message: 'x' }` missing\n * `code`) are still rejected so the bug surfaces — see the\n * `'rejects entries that are objects but missing required fields'`\n * test for the rationale.\n */\nfunction isDetailsRecord(value: unknown): value is ApiErrorDetails {\n if (value === null || typeof value !== 'object' || Array.isArray(value)) return false\n // Reject prototype-polluted keys — we don't use them here, but downstream\n // spreads shouldn't have to worry about this input.\n const record = value as Record<string, unknown>\n for (const k of Object.keys(record)) {\n const v = record[k]\n if (isAcceptedEntry(v)) continue\n if (Array.isArray(v) && v.every((entry) => isAcceptedEntry(entry))) continue\n return false\n }\n return true\n}\n"],"names":["normalizeNumericOption","canonicalizePath","InvalidPathError"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../src/runtime/core/serialize-script.ts","../src/runtime/core/parse-api-errors.ts"],"sourcesContent":["/**\n * Escape a JSON string so it's safe to embed inside an inline\n * `<script>` tag during SSR. Plain `JSON.stringify` is not safe — a\n * form value containing the literal substring `</script>` would\n * break out of the script tag.\n *\n * ```ts\n * const payload = escapeForInlineScript(JSON.stringify(renderAttaformState(app)))\n * // `<script>window.__ATTAFORM_STATE__ = ${payload}</script>` is safe.\n * ```\n *\n * Output remains valid JSON — `JSON.parse` round-trips back to the\n * original value on the client.\n */\nexport function escapeForInlineScript(json: string): string {\n return json.replace(/[<>&\\u2028\\u2029]/g, (char) => {\n switch (char) {\n case '<':\n return '\\\\u003c'\n case '>':\n return '\\\\u003e'\n case '&':\n return '\\\\u0026'\n case '\\u2028':\n return '\\\\u2028'\n case '\\u2029':\n return '\\\\u2029'\n default:\n return char\n }\n })\n}\n","import type {\n ApiErrorDetails,\n ApiErrorEntry,\n ApiErrorEnvelope,\n FormKey,\n ValidationError,\n} from '../types/types-api'\nimport { normalizeNumericOption } from './defaults'\nimport { InvalidPathError } from './errors'\nimport { canonicalizePath } from './paths'\n\n/**\n * Result of `parseApiErrors`. Branch on `ok` to handle the two cases:\n *\n * ```ts\n * const result = parseApiErrors(payload, { formKey: form.key })\n * if (result.ok) {\n * form.setFieldErrors(result.errors)\n * } else {\n * console.warn('Bad error payload:', result.rejected)\n * }\n * ```\n *\n * `ok: true` means the payload was recognised — `errors` may still be\n * empty if the payload was valid but had no actual errors.\n * `ok: false` means the payload didn't match a known shape; `rejected`\n * carries a one-line description of why.\n */\nexport type ParseApiErrorsResult = {\n /** `true` when the payload was recognised; `false` when the shape was unfamiliar. */\n readonly ok: boolean\n /** Errors extracted from the payload. May be empty even when `ok: true`. */\n readonly errors: ValidationError[]\n /** When `ok: false`, a one-line description of why the payload was rejected. */\n readonly rejected?: string\n}\n\n/**\n * Options for `parseApiErrors`. The size caps protect against\n * misbehaving or hostile servers — exceeding any cap causes the\n * parser to reject the payload wholesale rather than partially apply.\n */\nexport type ParseApiErrorsOptions = {\n /**\n * The form's identifier — pass `form.key`. Stamped on every\n * produced `ValidationError` so errors route to the right form.\n */\n readonly formKey: FormKey\n /**\n * Code stamped on `ValidationError`s synthesized from bare-string\n * entries (the Rails / DRF / Laravel `{ field: [\"msg\"] }` shape).\n * Default `'api:unknown'`. Pick something more specific\n * (`'api:server-validation'`, `'myapp:legacy'`, …) when you know\n * the source.\n *\n * Structured `{ message, code }` entries forward their `code`\n * verbatim and ignore this option.\n */\n readonly defaultCode?: string\n /**\n * Maximum number of distinct keys to accept. Default `1000`.\n * Raise for trusted backends that legitimately produce more.\n */\n readonly maxEntries?: number\n /**\n * Maximum number of path segments per key. Default `32`. Keys\n * deeper than this are dropped (the rest of the payload still\n * applies if it stays under the other caps).\n */\n readonly maxPathDepth?: number\n /**\n * Maximum total path segments summed across every accepted key.\n * Default `10000`. Bounds the worst-case traversal cost.\n */\n readonly maxTotalSegments?: number\n}\n\n/**\n * Default size caps + default fallback code used by `parseApiErrors`.\n * Conservative; pass larger values (or a more specific code) via the\n * options bag for trusted-backend integrations.\n */\nexport const PARSE_API_ERRORS_DEFAULTS = {\n maxEntries: 1000,\n maxPathDepth: 32,\n maxTotalSegments: 10000,\n defaultCode: 'api:unknown',\n} as const\n\n/**\n * Normalise a server-side validation error payload into\n * `ValidationError[]`. Pair with `form.setFieldErrors` /\n * `form.addFieldErrors` to surface server errors on the form:\n *\n * ```ts\n * const response = await fetch('/api/signup', { … })\n * if (!response.ok) {\n * const payload = await response.json()\n * const result = parseApiErrors(payload, { formKey: form.key })\n * if (result.ok) form.setFieldErrors(result.errors)\n * }\n * ```\n *\n * Recognised payload shapes:\n *\n * - Wrapped envelope:\n * `{ error: { details: { email: { message: 'taken', code: 'api:duplicate-email' } } } }`\n * - Unwrapped envelope:\n * `{ details: { email: { message: 'taken', code: 'api:duplicate-email' } } }`\n * - Raw details record:\n * `{ email: { message: 'taken', code: 'api:duplicate-email' } }`\n * - **Bare-string Rails / DRF / Laravel shape:**\n * `{ email: ['Email already taken.'], username: 'too short' }`\n * - `null` / `undefined` — returns `{ ok: true, errors: [] }`\n *\n * Two entry shapes are accepted:\n *\n * 1. **Structured** — `{ message: string, code: string }`. The `code`\n * is forwarded verbatim onto the produced `ValidationError`.\n * 2. **Bare-string** — a plain string. Synthesized into\n * `{ message: <string>, code: <defaultCode> }` where `defaultCode`\n * comes from `options.defaultCode` (default `'api:unknown'`).\n * Useful for the Rails / Django REST Framework / FastAPI / Laravel\n * JSON shape that doesn't carry a per-field code.\n *\n * Each detail key's value can be a single entry, an array, or a mix\n * of structured and bare-string entries; arrays expand into one\n * `ValidationError` per entry. Pick a prefix on the server (`api:`,\n * `auth:`, etc.) and stay consistent so error renderers can branch\n * on `code` — or rely on `defaultCode` when the wire shape is\n * message-only.\n *\n * Dotted keys (`\"address.line1\"`) are split into structured paths\n * automatically. Use a custom server response shape outside these\n * patterns? Build the `ValidationError[]` array yourself and pass\n * it to `setFieldErrors` directly — `parseApiErrors` is just a\n * convenience for the common shapes.\n */\nexport function parseApiErrors(\n payload: ApiErrorEnvelope | ApiErrorDetails | null | undefined | unknown,\n options: ParseApiErrorsOptions\n): ParseApiErrorsResult {\n 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":["normalizeNumericOption","canonicalizePath","InvalidPathError"],"mappings":"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"}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Plugin, App } from 'vue';
|
|
2
|
-
import { S as SSRDetectOptions,
|
|
3
|
-
export {
|
|
4
|
-
import {
|
|
5
|
-
export {
|
|
2
|
+
import { S as SSRDetectOptions, a as SerializedFormData, A as AttaformRegistry } from './shared/attaform.BkjJfMvJ.cjs';
|
|
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.BkjJfMvJ.cjs';
|
|
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, V as ValidationError, e as ApiErrorEnvelope, f as ApiErrorDetails } from './shared/attaform.ory-3WhV.cjs';
|
|
5
|
+
export { g as ApiErrorEntry, h as ArrayItem, i as ArrayPath, C as CoercionEntry, j as CoercionRegistry, k as CoercionResult, l as CustomDirectiveRegisterAssignerFn, m as DeepPartial, n as DefaultValuesResponse, o as DefaultValuesShape, p as DisplayCtx, q as DisplayMachine, r as DisplayState, E as ErrorsProxyShape, s as FieldMetaPayload, t as FieldState, u as FieldStateMap, v as FieldStateMapEntry, w as FlatPath, x as FormErrorRecord, y as FormErrorsSurface, z as FormMeta, B as FormStorage, H as FormStorageKind, I as HandleSubmit, J as HistoryConfig, K as IsTuple, L as IsUnion, M as JoinSegments, N as KeyofUnion, O as LiftedValueShape, P as MetaTrackerValue, Q as NestedReadType, S as NestedType, T as OnError, W as OnInvalidSubmitPolicy, X as OnSubmit, Y as PartialFlatPath, Z as Path, _ as PathKey, $ as PendingValidationStatus, a0 as PersistConfig, a1 as PersistConfigOptions, a2 as PersistIncludeMode, a3 as Primitive, a4 as ROOT_PATH, a5 as ROOT_PATH_KEY, a6 as ReactiveValidationStatus, a7 as RegisterDirective, a8 as RegisterFlatPath, a9 as RegisterOptions, aa as RegisterSelectModifier, ab as RegisterTextModifier, ac as RegisterTransform, ad as Segment, ae as SetValueCallback, af as SetValuePayload, ag as SettledValidationStatus, ah as SlimPrimitiveKind, ai as SlimRuntimeOf, aj as SubmitHandler, ak as Unset, al as ValidateOn, am as ValidateOnConfig, an as ValidationResponse, ao as ValidationResponseWithoutValue, ap as ValueOfUnion, aq as WriteMeta, ar as WriteShape, as as canonicalizePath, at as isPathPrefix, au as isUnset, av as parseDottedPath, aw as unset } from './shared/attaform.ory-3WhV.cjs';
|
|
6
6
|
export { A as AnonPersistError, a as AttaformError, I as InvalidPathError, b as InvalidUseFormConfigError, O as OutsideSetupError, R as RegistryNotInstalledError, c as ReservedFormKeyError, S as SubmitErrorHandlerError } from './shared/attaform.DkA5J8NW.cjs';
|
|
7
7
|
|
|
8
8
|
/**
|
|
@@ -178,20 +178,7 @@ options?: {
|
|
|
178
178
|
* default assigners for text inputs, checkboxes, radios, and selects.
|
|
179
179
|
*/
|
|
180
180
|
declare const assignKey: unique symbol;
|
|
181
|
-
|
|
182
|
-
* Type guard for a `RegisterValue`. Returns `true` when `val` looks
|
|
183
|
-
* like the object returned from `form.register(path)`.
|
|
184
|
-
*
|
|
185
|
-
* ```ts
|
|
186
|
-
* if (isRegisterValue(slotValue)) {
|
|
187
|
-
* // slotValue.innerRef is now a Ref<unknown>
|
|
188
|
-
* }
|
|
189
|
-
* ```
|
|
190
|
-
*
|
|
191
|
-
* Useful when building wrapper components that accept either a
|
|
192
|
-
* `RegisterValue` or a plain ref via the same prop.
|
|
193
|
-
*/
|
|
194
|
-
declare function isRegisterValue<Value = unknown>(val: unknown): val is RegisterValue<Value>;
|
|
181
|
+
|
|
195
182
|
/**
|
|
196
183
|
* The `v-register` directive. Bind a form field to a native input,
|
|
197
184
|
* select, textarea, checkbox, or radio:
|
|
@@ -212,6 +199,21 @@ declare function isRegisterValue<Value = unknown>(val: unknown): val is Register
|
|
|
212
199
|
*/
|
|
213
200
|
declare const vRegister: RegisterModelDynamicCustomDirective;
|
|
214
201
|
|
|
202
|
+
/**
|
|
203
|
+
* Type guard for a `RegisterValue`. Returns `true` when `val` looks
|
|
204
|
+
* like the object returned from `form.register(path)`.
|
|
205
|
+
*
|
|
206
|
+
* ```ts
|
|
207
|
+
* if (isRegisterValue(slotValue)) {
|
|
208
|
+
* // slotValue.innerRef is now a Ref<unknown>
|
|
209
|
+
* }
|
|
210
|
+
* ```
|
|
211
|
+
*
|
|
212
|
+
* Useful when building wrapper components that accept either a
|
|
213
|
+
* `RegisterValue` or a plain ref via the same prop.
|
|
214
|
+
*/
|
|
215
|
+
declare function isRegisterValue<Value = unknown>(val: unknown): val is RegisterValue<Value>;
|
|
216
|
+
|
|
215
217
|
/**
|
|
216
218
|
* Shared building blocks for Attaform's two devtools surfaces — the Vue
|
|
217
219
|
* DevTools (Chrome-extension) inspector wired up in `./devtools.ts`, and
|
|
@@ -264,13 +266,13 @@ declare global {
|
|
|
264
266
|
/**
|
|
265
267
|
* Anti-flash timing for the library-default display reducer.
|
|
266
268
|
*
|
|
267
|
-
* - `showDelay` — how long a validation
|
|
268
|
-
*
|
|
269
|
-
* reveals `'pending'` at all, so a
|
|
270
|
-
* not flash a spinner on every keystroke.
|
|
271
|
-
* - `minVisible` — once shown, the minimum time the spinner stays up.
|
|
272
|
-
*
|
|
273
|
-
*
|
|
269
|
+
* - `showDelay` — how long in-flight work (a validation run or an async
|
|
270
|
+
* register transform) may run before its spinner is allowed to show. Work
|
|
271
|
+
* that settles inside this window never reveals `'pending'` at all, so a
|
|
272
|
+
* fast (often synchronous) check does not flash a spinner on every keystroke.
|
|
273
|
+
* - `minVisible` — once shown, the minimum time the spinner stays up. Work
|
|
274
|
+
* that lands just past `showDelay` is held here so the spinner does not
|
|
275
|
+
* itself flash on and immediately off.
|
|
274
276
|
*
|
|
275
277
|
* Both are milliseconds.
|
|
276
278
|
*/
|
|
@@ -279,9 +281,10 @@ type DisplayTimings = {
|
|
|
279
281
|
readonly minVisible: number;
|
|
280
282
|
};
|
|
281
283
|
/**
|
|
282
|
-
* Library-default anti-flash timings. `showDelay:
|
|
283
|
-
* synchronous, microtask-resolved, and tiny-async
|
|
284
|
-
*
|
|
284
|
+
* Library-default anti-flash timings. `showDelay: 120` cleanly swallows
|
|
285
|
+
* synchronous, microtask-resolved, and tiny-async work (validation runs and
|
|
286
|
+
* async register transforms alike) so none of them flash a spinner;
|
|
287
|
+
* `minVisible: 120` keeps a shown spinner snappy once one does land.
|
|
285
288
|
* Retune via {@link makeDefaultDisplayState} without touching the engine.
|
|
286
289
|
*/
|
|
287
290
|
declare const DEFAULT_TIMINGS: DisplayTimings;
|
package/dist/index.d.mts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Plugin, App } from 'vue';
|
|
2
|
-
import { S as SSRDetectOptions,
|
|
3
|
-
export {
|
|
4
|
-
import {
|
|
5
|
-
export {
|
|
2
|
+
import { S as SSRDetectOptions, a as SerializedFormData, A as AttaformRegistry } from './shared/attaform.BCcrLApm.mjs';
|
|
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.BCcrLApm.mjs';
|
|
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, V as ValidationError, e as ApiErrorEnvelope, f as ApiErrorDetails } from './shared/attaform.ory-3WhV.mjs';
|
|
5
|
+
export { g as ApiErrorEntry, h as ArrayItem, i as ArrayPath, C as CoercionEntry, j as CoercionRegistry, k as CoercionResult, l as CustomDirectiveRegisterAssignerFn, m as DeepPartial, n as DefaultValuesResponse, o as DefaultValuesShape, p as DisplayCtx, q as DisplayMachine, r as DisplayState, E as ErrorsProxyShape, s as FieldMetaPayload, t as FieldState, u as FieldStateMap, v as FieldStateMapEntry, w as FlatPath, x as FormErrorRecord, y as FormErrorsSurface, z as FormMeta, B as FormStorage, H as FormStorageKind, I as HandleSubmit, J as HistoryConfig, K as IsTuple, L as IsUnion, M as JoinSegments, N as KeyofUnion, O as LiftedValueShape, P as MetaTrackerValue, Q as NestedReadType, S as NestedType, T as OnError, W as OnInvalidSubmitPolicy, X as OnSubmit, Y as PartialFlatPath, Z as Path, _ as PathKey, $ as PendingValidationStatus, a0 as PersistConfig, a1 as PersistConfigOptions, a2 as PersistIncludeMode, a3 as Primitive, a4 as ROOT_PATH, a5 as ROOT_PATH_KEY, a6 as ReactiveValidationStatus, a7 as RegisterDirective, a8 as RegisterFlatPath, a9 as RegisterOptions, aa as RegisterSelectModifier, ab as RegisterTextModifier, ac as RegisterTransform, ad as Segment, ae as SetValueCallback, af as SetValuePayload, ag as SettledValidationStatus, ah as SlimPrimitiveKind, ai as SlimRuntimeOf, aj as SubmitHandler, ak as Unset, al as ValidateOn, am as ValidateOnConfig, an as ValidationResponse, ao as ValidationResponseWithoutValue, ap as ValueOfUnion, aq as WriteMeta, ar as WriteShape, as as canonicalizePath, at as isPathPrefix, au as isUnset, av as parseDottedPath, aw as unset } from './shared/attaform.ory-3WhV.mjs';
|
|
6
6
|
export { A as AnonPersistError, a as AttaformError, I as InvalidPathError, b as InvalidUseFormConfigError, O as OutsideSetupError, R as RegistryNotInstalledError, c as ReservedFormKeyError, S as SubmitErrorHandlerError } from './shared/attaform.DkA5J8NW.mjs';
|
|
7
7
|
|
|
8
8
|
/**
|
|
@@ -178,20 +178,7 @@ options?: {
|
|
|
178
178
|
* default assigners for text inputs, checkboxes, radios, and selects.
|
|
179
179
|
*/
|
|
180
180
|
declare const assignKey: unique symbol;
|
|
181
|
-
|
|
182
|
-
* Type guard for a `RegisterValue`. Returns `true` when `val` looks
|
|
183
|
-
* like the object returned from `form.register(path)`.
|
|
184
|
-
*
|
|
185
|
-
* ```ts
|
|
186
|
-
* if (isRegisterValue(slotValue)) {
|
|
187
|
-
* // slotValue.innerRef is now a Ref<unknown>
|
|
188
|
-
* }
|
|
189
|
-
* ```
|
|
190
|
-
*
|
|
191
|
-
* Useful when building wrapper components that accept either a
|
|
192
|
-
* `RegisterValue` or a plain ref via the same prop.
|
|
193
|
-
*/
|
|
194
|
-
declare function isRegisterValue<Value = unknown>(val: unknown): val is RegisterValue<Value>;
|
|
181
|
+
|
|
195
182
|
/**
|
|
196
183
|
* The `v-register` directive. Bind a form field to a native input,
|
|
197
184
|
* select, textarea, checkbox, or radio:
|
|
@@ -212,6 +199,21 @@ declare function isRegisterValue<Value = unknown>(val: unknown): val is Register
|
|
|
212
199
|
*/
|
|
213
200
|
declare const vRegister: RegisterModelDynamicCustomDirective;
|
|
214
201
|
|
|
202
|
+
/**
|
|
203
|
+
* Type guard for a `RegisterValue`. Returns `true` when `val` looks
|
|
204
|
+
* like the object returned from `form.register(path)`.
|
|
205
|
+
*
|
|
206
|
+
* ```ts
|
|
207
|
+
* if (isRegisterValue(slotValue)) {
|
|
208
|
+
* // slotValue.innerRef is now a Ref<unknown>
|
|
209
|
+
* }
|
|
210
|
+
* ```
|
|
211
|
+
*
|
|
212
|
+
* Useful when building wrapper components that accept either a
|
|
213
|
+
* `RegisterValue` or a plain ref via the same prop.
|
|
214
|
+
*/
|
|
215
|
+
declare function isRegisterValue<Value = unknown>(val: unknown): val is RegisterValue<Value>;
|
|
216
|
+
|
|
215
217
|
/**
|
|
216
218
|
* Shared building blocks for Attaform's two devtools surfaces — the Vue
|
|
217
219
|
* DevTools (Chrome-extension) inspector wired up in `./devtools.ts`, and
|
|
@@ -264,13 +266,13 @@ declare global {
|
|
|
264
266
|
/**
|
|
265
267
|
* Anti-flash timing for the library-default display reducer.
|
|
266
268
|
*
|
|
267
|
-
* - `showDelay` — how long a validation
|
|
268
|
-
*
|
|
269
|
-
* reveals `'pending'` at all, so a
|
|
270
|
-
* not flash a spinner on every keystroke.
|
|
271
|
-
* - `minVisible` — once shown, the minimum time the spinner stays up.
|
|
272
|
-
*
|
|
273
|
-
*
|
|
269
|
+
* - `showDelay` — how long in-flight work (a validation run or an async
|
|
270
|
+
* register transform) may run before its spinner is allowed to show. Work
|
|
271
|
+
* that settles inside this window never reveals `'pending'` at all, so a
|
|
272
|
+
* fast (often synchronous) check does not flash a spinner on every keystroke.
|
|
273
|
+
* - `minVisible` — once shown, the minimum time the spinner stays up. Work
|
|
274
|
+
* that lands just past `showDelay` is held here so the spinner does not
|
|
275
|
+
* itself flash on and immediately off.
|
|
274
276
|
*
|
|
275
277
|
* Both are milliseconds.
|
|
276
278
|
*/
|
|
@@ -279,9 +281,10 @@ type DisplayTimings = {
|
|
|
279
281
|
readonly minVisible: number;
|
|
280
282
|
};
|
|
281
283
|
/**
|
|
282
|
-
* Library-default anti-flash timings. `showDelay:
|
|
283
|
-
* synchronous, microtask-resolved, and tiny-async
|
|
284
|
-
*
|
|
284
|
+
* Library-default anti-flash timings. `showDelay: 120` cleanly swallows
|
|
285
|
+
* synchronous, microtask-resolved, and tiny-async work (validation runs and
|
|
286
|
+
* async register transforms alike) so none of them flash a spinner;
|
|
287
|
+
* `minVisible: 120` keeps a shown spinner snappy once one does land.
|
|
285
288
|
* Retune via {@link makeDefaultDisplayState} without touching the engine.
|
|
286
289
|
*/
|
|
287
290
|
declare const DEFAULT_TIMINGS: DisplayTimings;
|