formstand 0.21.0 → 0.22.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/CHANGELOG.md +430 -0
- package/README.md +2 -1
- package/dist/{chunk-OB23TUKY.js → chunk-BMXIZEI4.js} +14 -4
- package/dist/chunk-BMXIZEI4.js.map +1 -0
- package/dist/{chunk-MQ6262S3.js → chunk-V3PDMTPP.js} +7 -3
- package/dist/chunk-V3PDMTPP.js.map +1 -0
- package/dist/{createForm-B73p1TgL.d.cts → createForm-BJum0RBu.d.cts} +10 -7
- package/dist/{createForm-B73p1TgL.d.ts → createForm-BJum0RBu.d.ts} +10 -7
- package/dist/devtools/index.cjs +8 -2
- package/dist/devtools/index.cjs.map +1 -1
- package/dist/devtools/index.d.cts +7 -5
- package/dist/devtools/index.d.ts +7 -5
- package/dist/devtools/index.js +2 -3
- package/dist/devtools/index.js.map +1 -1
- package/dist/index.cjs +223 -81
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +18 -15
- package/dist/index.d.ts +18 -15
- package/dist/index.js +212 -84
- package/dist/index.js.map +1 -1
- package/dist/testing/index.cjs +4 -1
- package/dist/testing/index.cjs.map +1 -1
- package/dist/testing/index.d.cts +2 -2
- package/dist/testing/index.d.ts +2 -2
- package/dist/testing/index.js +2 -2
- package/dist/testing/index.js.map +1 -1
- package/package.json +17 -17
- package/dist/chunk-MQ6262S3.js.map +0 -1
- package/dist/chunk-OB23TUKY.js.map +0 -1
|
@@ -50,6 +50,10 @@ type FieldPathRec<T, D extends number> = [D] extends [0] ? never : IsTuple<T> ex
|
|
|
50
50
|
}[keyof T & string] : never;
|
|
51
51
|
type StepValue<T, K extends string> = T extends readonly (infer U)[] ? K extends keyof T ? T[K] : K extends `${number}` ? U : never : T extends object ? K extends keyof T ? T[K] : never : never;
|
|
52
52
|
type FieldValue<T, P extends string> = [T] extends [NonNullable<T>] ? P extends `${infer Head}.${infer Tail}` ? FieldValue<StepValue<T, Head>, Tail> : StepValue<T, P> : FieldValue<NonNullable<T>, P> | undefined;
|
|
53
|
+
type ArrayItemOf<T> = [NonNullable<T>] extends [readonly (infer U)[]] ? number extends NonNullable<T>["length"] ? U : never : never;
|
|
54
|
+
type IsListValue<V> = [NonNullable<V>] extends [readonly unknown[]] ? number extends NonNullable<V>["length"] ? true : false : false;
|
|
55
|
+
type ArrayFieldPath<T, D extends number = DefaultPathDepth> = ArrayPathsWhere<T, FieldPath<T, D>>;
|
|
56
|
+
type ArrayPathsWhere<T, P extends string> = P extends unknown ? IsListValue<FieldValue<T, P>> extends true ? P : never : never;
|
|
53
57
|
|
|
54
58
|
type ValidationMode = "onChange" | "onBlur" | "onSubmit" | "onTouched" | "all";
|
|
55
59
|
type ValidationTrigger = "change" | "blur";
|
|
@@ -137,7 +141,6 @@ type SubmitResult<TOutput> = Readonly<{
|
|
|
137
141
|
kind: "error";
|
|
138
142
|
error: unknown;
|
|
139
143
|
}>;
|
|
140
|
-
type ArrayItemOf<T> = T extends readonly (infer U)[] ? U : never;
|
|
141
144
|
type ErrorPath<TValues, D extends PathDepth = DefaultPathDepth> = FieldPath<TValues, D> | "";
|
|
142
145
|
type ResetOptions = Readonly<{
|
|
143
146
|
keepErrors?: boolean;
|
|
@@ -183,11 +186,11 @@ type Form<TSchema extends z.ZodType, D extends PathDepth = DefaultPathDepth> = R
|
|
|
183
186
|
validateFieldAsync: (path: ErrorPath<z.input<TSchema>, D>) => Promise<SettledFieldValidationResult>;
|
|
184
187
|
validateFieldsAsync: (paths: readonly FieldPath<z.input<TSchema>, D>[]) => Promise<SettledFieldsValidationResult>;
|
|
185
188
|
submit: (onValid: SubmitHandler<TSchema>, onInvalid?: InvalidSubmitHandler, options?: SubmitOptions) => Promise<SubmitResult<z.output<TSchema>>>;
|
|
186
|
-
arrayPush: <P extends
|
|
187
|
-
arrayRemove: (path:
|
|
188
|
-
arrayInsert: <P extends
|
|
189
|
-
arrayMove: (path:
|
|
190
|
-
arraySwap: (path:
|
|
189
|
+
arrayPush: <P extends ArrayFieldPath<z.input<TSchema>, D>>(path: P, item: ArrayItemOf<FieldValue<z.input<TSchema>, P>>) => void;
|
|
190
|
+
arrayRemove: (path: ArrayFieldPath<z.input<TSchema>, D>, index: number) => void;
|
|
191
|
+
arrayInsert: <P extends ArrayFieldPath<z.input<TSchema>, D>>(path: P, index: number, item: ArrayItemOf<FieldValue<z.input<TSchema>, P>>) => void;
|
|
192
|
+
arrayMove: (path: ArrayFieldPath<z.input<TSchema>, D>, from: number, to: number) => void;
|
|
193
|
+
arraySwap: (path: ArrayFieldPath<z.input<TSchema>, D>, a: number, b: number) => void;
|
|
191
194
|
handleSubmit: (onValid: SubmitHandler<TSchema>, onInvalid?: InvalidSubmitHandler, options?: SubmitOptions) => (event?: {
|
|
192
195
|
preventDefault: () => void;
|
|
193
196
|
}) => Promise<SubmitResult<z.output<TSchema>>>;
|
|
@@ -198,4 +201,4 @@ type Form<TSchema extends z.ZodType, D extends PathDepth = DefaultPathDepth> = R
|
|
|
198
201
|
}>;
|
|
199
202
|
declare const createForm: <TSchema extends z.ZodType, D extends PathDepth = DefaultPathDepth>(schema: TSchema, options: CreateFormOptions<TSchema, D>) => Form<TSchema, D>;
|
|
200
203
|
|
|
201
|
-
export { type BoolMap as B, type CreateFormOptions as C, type DefaultPathDepth as D, type ErrorMap as E, type Form as F, type InvalidSubmitHandler as I, type PathDepth as P, type ReadonlyStoreApi as R, type SettledFieldValidationResult as S, type ValidationMode as V, type
|
|
204
|
+
export { type ArrayFieldPath as A, type BoolMap as B, type CreateFormOptions as C, type DefaultPathDepth as D, type ErrorMap as E, type Form as F, type InvalidSubmitHandler as I, type PathDepth as P, type ReadonlyStoreApi as R, type SettledFieldValidationResult as S, type ValidationMode as V, type FormState as a, type FieldValidationResult as b, type FieldPath as c, type FieldValue as d, type ArrayItemOf as e, type FieldSnapshot as f, type FieldsValidationResult as g, type ResetOptions as h, type SettledFieldsValidationResult as i, type SettledValidationResult as j, type SubmitHandler as k, type SubmitOptions as l, type SubmitResult as m, type ValidationResult as n, type ValidationTrigger as o, createForm as p, emptyValueForSchema as q, flattenIssues as r, isAsyncRequiredError as s, isClearableSchema as t, shouldValidateOn as u, validateAsync as v, validateSync as w };
|
package/dist/devtools/index.cjs
CHANGED
|
@@ -7,6 +7,13 @@ var jsxRuntime = require('react/jsx-runtime');
|
|
|
7
7
|
|
|
8
8
|
// src/devtools/FormstandDevtools.tsx
|
|
9
9
|
|
|
10
|
+
// src/core/env.ts
|
|
11
|
+
var isDevToolsBuild = () => {
|
|
12
|
+
if (typeof process === "undefined") return false;
|
|
13
|
+
const env = process.env?.["NODE_ENV"];
|
|
14
|
+
return typeof env === "string" && env !== "production";
|
|
15
|
+
};
|
|
16
|
+
|
|
10
17
|
// src/core/path.ts
|
|
11
18
|
var isIndexSegment = (s) => {
|
|
12
19
|
const n = Number(s);
|
|
@@ -156,7 +163,6 @@ var styles = {
|
|
|
156
163
|
cursor: "pointer"
|
|
157
164
|
}
|
|
158
165
|
};
|
|
159
|
-
var isDevBuild = () => typeof process === "undefined" || process.env?.["NODE_ENV"] !== "production";
|
|
160
166
|
var preview = (value) => {
|
|
161
167
|
if (value === void 0) return "undefined";
|
|
162
168
|
if (value instanceof Date) return value.toISOString();
|
|
@@ -311,7 +317,7 @@ var DevtoolsPanel = ({
|
|
|
311
317
|
}
|
|
312
318
|
) });
|
|
313
319
|
};
|
|
314
|
-
var FormstandDevtools = (props) =>
|
|
320
|
+
var FormstandDevtools = (props) => isDevToolsBuild() ? /* @__PURE__ */ jsxRuntime.jsx(DevtoolsPanel, { ...props }) : null;
|
|
315
321
|
|
|
316
322
|
exports.FormstandDevtools = FormstandDevtools;
|
|
317
323
|
exports.leafPaths = leafPaths;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/core/path.ts","../../src/react/useFormSelector.ts","../../src/devtools/paths.ts","../../src/devtools/styles.ts","../../src/devtools/FormstandDevtools.tsx"],"names":["useStore","useShallow","jsxs","jsx","useState"],"mappings":"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","file":"index.cjs","sourcesContent":["import { isPlainObject } from \"./equality\";\n\nexport type PathSegment = string | number;\n\n// Segment keys containing \".\" are not addressable (paths are split on dots);\n// use nested objects or index-free keys instead.\n\n// Writes above this index are refused — a typo'd path like \"items.4294967296\"\n// must not allocate a multi-gigabyte array.\nconst MAX_ARRAY_INDEX = 100_000;\n\nconst isIndexSegment = (s: string): boolean => {\n const n = Number(s);\n return Number.isInteger(n) && n >= 0 && String(n) === s;\n};\n\n// Hot path: every field subscription re-resolves its path on every store\n// notification (getAtPath in selectors), so parsing is memoized — a form's\n// paths are a small fixed set. Bounded as an LRU over Map insertion order:\n// a hit re-inserts its key (moving it to the back) and an overflow evicts\n// the front, so an app whose LIVE path set exceeds the cap keeps its hot\n// paths cached instead of re-parsing every one of them after each reset\n// (the old wholesale clear). Eviction only costs re-parsing, never\n// correctness. Same delete-then-set recency idiom as useFieldArray's\n// per-path id cache.\nconst PARSE_CACHE_MAX = 4096;\nconst parseCache = new Map<string, readonly PathSegment[]>();\n\nexport const parsePath = (path: string): readonly PathSegment[] => {\n const cached = parseCache.get(path);\n if (cached !== undefined) {\n parseCache.delete(path);\n parseCache.set(path, cached);\n return cached;\n }\n // Frozen because the array is now SHARED across every caller (and cached):\n // parsePath is a public export, and pre-cache each call returned a private\n // array that was safe to mutate. Freezing enforces the `readonly` type at\n // runtime so an external `.sort()`/`.reverse()`/`.push()` throws instead of\n // silently corrupting the cache for every later lookup of this path.\n const segments = Object.freeze(\n path === \"\"\n ? []\n : path.split(\".\").map((s) => (isIndexSegment(s) ? Number(s) : s)),\n );\n if (parseCache.size >= PARSE_CACHE_MAX) {\n const oldest = parseCache.keys().next().value;\n if (oldest !== undefined) parseCache.delete(oldest);\n }\n parseCache.set(path, segments);\n return segments;\n};\n\nconst isPlainRecord = (value: unknown): value is Record<string, unknown> =>\n value !== null && typeof value === \"object\" && !Array.isArray(value);\n\n// The existing container decides how a segment is interpreted: arrays take\n// numeric segments as indices, records take any segment as a string key (so a\n// z.record keyed \"0\" reads/writes the record key, not an array index). Only\n// when the container is absent does the segment type pick what gets created.\nconst readSegment = (obj: unknown, segment: PathSegment): unknown => {\n if (obj == null) return undefined;\n if (Array.isArray(obj)) {\n return typeof segment === \"number\" ? obj[segment] : undefined;\n }\n // Own properties only: a runtime-built path like \"lookup.constructor\" on a\n // z.record must read as absent, not leak Object.prototype members as field\n // values.\n return isPlainRecord(obj) && Object.hasOwn(obj, String(segment))\n ? obj[String(segment)]\n : undefined;\n};\n\nexport const getAtPath = (obj: unknown, path: string): unknown =>\n parsePath(path).reduce<unknown>(readSegment, obj);\n\nexport type SlotAtPath =\n | Readonly<{ exists: true; value: unknown }>\n | Readonly<{ exists: false }>;\n\n// Resolve `path` while checking that every segment is read from a real\n// container: an in-range array index, or a key on a plain record (containers\n// decide, mirroring readSegment). A segment into an absent or non-container\n// value addresses no slot at all — a full-form parse would key that error at\n// the missing ANCESTOR, not here, so field-scoped validation must skip it\n// rather than parse the leaf's subschema against undefined (which would\n// fabricate a spurious \"required\" error the full pass never produces). Note\n// this checks the CONTAINER, not the value: a leaf that is undefined at the\n// FINAL step through a real record is still an addressable (legitimately\n// empty) slot. Returns the resolved value so callers don't walk twice.\nexport const slotAtPath = (obj: unknown, path: string): SlotAtPath => {\n const walked = parsePath(path).reduce<\n Readonly<{ ok: boolean; current: unknown }>\n >(\n (acc, segment) => {\n const container = acc.current;\n const addressable =\n typeof segment === \"number\"\n ? Array.isArray(container)\n ? segment < container.length\n : isPlainRecord(container)\n : isPlainRecord(container);\n return !acc.ok || !addressable\n ? { ok: false, current: undefined }\n : { ok: true, current: readSegment(container, segment) };\n },\n { ok: true, current: obj },\n );\n return walked.ok ? { exists: true, value: walked.current } : { exists: false };\n};\n\n// Whether the path addresses an OWN slot: every step reads a real\n// container's own key / in-range array index, the final segment included.\n// This is stricter than slotAtPath, which treats a MISSING final key on a\n// real record as an addressable (legitimately empty) slot — here it reads\n// false. The consumer is setValue's identity guard: writing `undefined`\n// over an ABSENT key is NOT a no-op (it creates the key, a real state\n// change — key-count dirtiness, persisted shape), while writing it over a\n// key that exists holding undefined is.\nexport const hasOwnSlot = (obj: unknown, path: string): boolean =>\n parsePath(path).reduce<Readonly<{ ok: boolean; current: unknown }>>(\n (acc, segment) => {\n const container = acc.current;\n const owns = Array.isArray(container)\n ? typeof segment === \"number\" && segment < container.length\n : isPlainRecord(container) && Object.hasOwn(container, String(segment));\n return !acc.ok || !owns\n ? { ok: false, current: undefined }\n : { ok: true, current: readSegment(container, segment) };\n },\n { ok: true, current: obj },\n ).ok;\n\nconst writeIntoArray = (\n arr: readonly unknown[],\n index: number,\n rest: readonly PathSegment[],\n value: unknown,\n): readonly unknown[] => {\n const length = Math.max(arr.length, index + 1);\n return Array.from({ length }, (_, i) =>\n i === index ? writeSegments(arr[i], rest, value) : arr[i],\n );\n};\n\nconst refusesHugeIndex = (index: number): boolean => {\n if (index > MAX_ARRAY_INDEX) {\n console.warn(\n `[formstand] refusing to write array index ${index} (max ${MAX_ARRAY_INDEX}); value left unchanged.`,\n );\n return true;\n }\n return false;\n};\n\nconst writeSegments = (\n obj: unknown,\n segments: readonly PathSegment[],\n value: unknown,\n): unknown => {\n const [head, ...rest] = segments;\n if (head === undefined) return value;\n\n if (Array.isArray(obj)) {\n if (typeof head !== \"number\") {\n console.warn(\n `[formstand] cannot write string key \"${head}\" into an array; value left unchanged.`,\n );\n return obj;\n }\n if (refusesHugeIndex(head)) return obj;\n return writeIntoArray(obj, head, rest, value);\n }\n\n // Records take any segment as a string key (mirroring readSegment), so a\n // z.record keyed by a large numeric id is writable — the array-index cap\n // only applies where an array actually exists or would be created.\n if (isPlainRecord(obj)) {\n // Spreading is only safe for PLAIN objects: spreading a Date/Map/Set or\n // class instance keeps own enumerable props but drops the prototype (and\n // for Map/Set, every entry) — the original would be silently destroyed.\n // Refuse, like the other unwritable shapes above.\n if (!isPlainObject(obj)) {\n console.warn(\n `[formstand] cannot write key \"${String(head)}\" through a non-plain object (Date/Map/Set/class instance); value left unchanged.`,\n );\n return obj;\n }\n const key = String(head);\n return { ...obj, [key]: writeSegments(obj[key], rest, value) };\n }\n\n // Absent (or non-container) value: create a container from the segment type.\n if (typeof head === \"number\") {\n if (refusesHugeIndex(head)) return obj;\n return writeIntoArray([], head, rest, value);\n }\n return { [head]: writeSegments(undefined, rest, value) };\n};\n\nexport const setAtPath = <T>(obj: T, path: string, value: unknown): T =>\n writeSegments(obj, parsePath(path), value) as T;\n","import type { z } from \"zod\";\nimport type { StoreApi } from \"zustand/vanilla\";\nimport { useStore } from \"zustand/react\";\nimport { useShallow } from \"zustand/react/shallow\";\nimport type { Form } from \"../core/createForm\";\nimport type { DefaultPathDepth, PathDepth } from \"../core/fieldPath\";\nimport type { FormState } from \"../core/types\";\n\ntype ReadonlyStore<T> = Pick<\n StoreApi<T>,\n \"getState\" | \"getInitialState\" | \"subscribe\"\n>;\n\nexport type FormStateApi = Readonly<{\n store: ReadonlyStore<FormState<unknown>>;\n}>;\n\nexport function useFormSelector<\n TSchema extends z.ZodType,\n U,\n D extends PathDepth = DefaultPathDepth,\n>(\n form: Form<TSchema, D>,\n selector: (state: FormState<z.input<TSchema>>) => U,\n): U;\n// The `schema?: undefined` brand forces Form<TSchema> (which has a real\n// `schema` property) to bind only the typed overload above. Without\n// it, Form<TSchema> would structurally satisfy FormStateApi too and a\n// bad selector could silently widen to FormState<unknown>.\nexport function useFormSelector<U>(\n form: FormStateApi & { readonly schema?: undefined },\n selector: (state: FormState<unknown>) => U,\n): U;\nexport function useFormSelector<U>(\n form: FormStateApi,\n selector: (state: FormState<unknown>) => U,\n): U {\n return useStore(form.store, selector);\n}\n\nexport function useFormSelectorShallow<\n TSchema extends z.ZodType,\n U,\n D extends PathDepth = DefaultPathDepth,\n>(\n form: Form<TSchema, D>,\n selector: (state: FormState<z.input<TSchema>>) => U,\n): U;\nexport function useFormSelectorShallow<U>(\n form: FormStateApi & { readonly schema?: undefined },\n selector: (state: FormState<unknown>) => U,\n): U;\nexport function useFormSelectorShallow<U>(\n form: FormStateApi,\n selector: (state: FormState<unknown>) => U,\n): U {\n return useStore(form.store, useShallow(selector));\n}\n","// Walking a values object into the list of paths the panel shows.\n\nconst isPlainObject = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" &&\n value !== null &&\n !Array.isArray(value) &&\n // A Date is a leaf, not a container to recurse into: its own keys are\n // methods, and useField binds the Date itself.\n !(value instanceof Date);\n\n// The addressable leaf paths of a values object, in declaration order.\n// Arrays contribute one path per ROW rather than one for the array, because\n// that is what a field binds to; the array's own path still shows up in the\n// panel via the error map, which is where `z.array().min(1)` lands.\n//\n// An empty object or array is itself a leaf here. It has no children to\n// describe, and dropping it would hide a field that exists in the schema.\nexport const leafPaths = (value: unknown, base = \"\"): readonly string[] => {\n const join = (key: string | number): string =>\n base === \"\" ? String(key) : `${base}.${String(key)}`;\n\n if (Array.isArray(value)) {\n return value.length === 0\n ? [base]\n : value.flatMap((item, index) => leafPaths(item, join(index)));\n }\n if (isPlainObject(value)) {\n const keys = Object.keys(value);\n return keys.length === 0\n ? [base]\n : keys.flatMap((key) => leafPaths(value[key], join(key)));\n }\n return [base];\n};\n\n// Error keys that no leaf path covers: the root \"\" key (a schema-wide\n// refine), array-level messages keyed at the container, and server verdicts\n// written at a path that holds no value yet. Without this the panel would\n// silently drop exactly the errors that are hardest to debug.\nexport const unmatchedErrorKeys = (\n errorKeys: readonly string[],\n leaves: readonly string[],\n): readonly string[] => {\n const covered = new Set(leaves);\n return errorKeys.filter((key) => !covered.has(key));\n};\n","import type { CSSProperties } from \"react\";\n\n// Inline styles on purpose: a devtools panel that needs the consumer to\n// import a stylesheet is a devtools panel that renders unstyled the first\n// time someone tries it. Inline also means nothing here can leak into or be\n// overridden by the host app's CSS, which matters when the whole job is\n// showing you the truth about your form.\n\nexport type DevtoolsPosition =\n | \"bottom-right\"\n | \"bottom-left\"\n | \"top-right\"\n | \"top-left\";\n\nconst ANCHOR: Readonly<Record<DevtoolsPosition, CSSProperties>> = {\n \"bottom-right\": { bottom: 12, right: 12 },\n \"bottom-left\": { bottom: 12, left: 12 },\n \"top-right\": { top: 12, right: 12 },\n \"top-left\": { top: 12, left: 12 },\n};\n\nconst MONO =\n 'ui-monospace, SFMono-Regular, Menlo, Consolas, \"Liberation Mono\", monospace';\n\nexport const anchorFor = (position: DevtoolsPosition): CSSProperties =>\n ANCHOR[position];\n\nexport const styles = {\n root: {\n position: \"fixed\",\n zIndex: 2147483000,\n fontFamily: MONO,\n fontSize: 11,\n lineHeight: 1.45,\n color: \"#e6e6e6\",\n colorScheme: \"dark\",\n } satisfies CSSProperties,\n toggle: {\n background: \"#1b1b1f\",\n border: \"1px solid #3a3a42\",\n color: \"#e6e6e6\",\n borderRadius: 6,\n padding: \"6px 10px\",\n font: \"inherit\",\n cursor: \"pointer\",\n } satisfies CSSProperties,\n panel: {\n width: \"min(440px, calc(100vw - 24px))\",\n maxHeight: \"min(60vh, 520px)\",\n overflow: \"auto\",\n background: \"#141417\",\n border: \"1px solid #3a3a42\",\n borderRadius: 8,\n boxShadow: \"0 12px 32px rgba(0,0,0,0.45)\",\n } satisfies CSSProperties,\n header: {\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"space-between\",\n gap: 8,\n padding: \"8px 10px\",\n borderBottom: \"1px solid #2a2a31\",\n position: \"sticky\",\n top: 0,\n background: \"#141417\",\n } satisfies CSSProperties,\n section: { padding: \"8px 10px\", borderBottom: \"1px solid #23232a\" },\n sectionTitle: {\n color: \"#9aa0aa\",\n textTransform: \"uppercase\",\n letterSpacing: \"0.06em\",\n fontSize: 10,\n margin: \"0 0 6px\",\n } satisfies CSSProperties,\n table: {\n width: \"100%\",\n borderCollapse: \"collapse\",\n tableLayout: \"fixed\",\n } satisfies CSSProperties,\n th: {\n textAlign: \"left\",\n color: \"#9aa0aa\",\n fontWeight: 400,\n padding: \"2px 4px\",\n borderBottom: \"1px solid #2a2a31\",\n } satisfies CSSProperties,\n td: {\n padding: \"2px 4px\",\n verticalAlign: \"top\",\n wordBreak: \"break-word\",\n } satisfies CSSProperties,\n path: { color: \"#c8b6ff\" } satisfies CSSProperties,\n error: { color: \"#ff8f8f\" } satisfies CSSProperties,\n muted: { color: \"#7d838d\" } satisfies CSSProperties,\n flag: { color: \"#7ddca4\" } satisfies CSSProperties,\n pre: {\n margin: 0,\n whiteSpace: \"pre-wrap\",\n wordBreak: \"break-word\",\n } satisfies CSSProperties,\n button: {\n background: \"#22222a\",\n border: \"1px solid #3a3a42\",\n color: \"#e6e6e6\",\n borderRadius: 5,\n padding: \"3px 8px\",\n font: \"inherit\",\n cursor: \"pointer\",\n } satisfies CSSProperties,\n} as const;\n","import { type ReactNode, useState } from \"react\";\nimport type { z } from \"zod\";\nimport type { Form } from \"../core/createForm\";\nimport type { FieldPath } from \"../core/fieldPath\";\nimport { getAtPath } from \"../core/path\";\nimport type { FormState } from \"../core/types\";\nimport { useFormSelectorShallow } from \"../react/useFormSelector\";\nimport { leafPaths, unmatchedErrorKeys } from \"./paths\";\nimport { type DevtoolsPosition, anchorFor, styles } from \"./styles\";\n\nexport type FormstandDevtoolsProps<TSchema extends z.ZodType> = Readonly<{\n form: Form<TSchema>;\n position?: DevtoolsPosition;\n defaultOpen?: boolean;\n /** Shown on the toggle, for telling two mounted panels apart. */\n label?: string;\n}>;\n\ntype FieldRow = Readonly<{\n path: string;\n value: unknown;\n error: readonly string[] | undefined;\n touched: boolean;\n dirty: boolean;\n isValidating: boolean;\n}>;\n\n// Non-production only, matching createForm's own devtools gate: the panel is\n// a debugging affordance, and shipping it to end users would leak the whole\n// form state into the page. Bundlers that define NODE_ENV drop the panel\n// entirely; the `typeof process` guard keeps it from throwing in runtimes\n// that define no process at all.\nconst isDevBuild = (): boolean =>\n typeof process === \"undefined\" ||\n process.env?.[\"NODE_ENV\"] !== \"production\";\n\nconst preview = (value: unknown): string => {\n if (value === undefined) return \"undefined\";\n if (value instanceof Date) return value.toISOString();\n const json = JSON.stringify(value);\n // Long strings and big row objects would push the value column past the\n // panel; the full value is one expand away in the Values section.\n return json === undefined\n ? String(value)\n : json.length > 60\n ? `${json.slice(0, 57)}…`\n : json;\n};\n\nconst flags = (row: FieldRow): string =>\n [\n row.touched ? \"touched\" : \"\",\n row.dirty ? \"dirty\" : \"\",\n row.isValidating ? \"validating\" : \"\",\n ]\n .filter((flag) => flag !== \"\")\n .join(\" \");\n\n// Reads every slice the panel renders in ONE shallow-compared subscription.\n// Each member is a stored reference the form replaces on write, so shallow\n// equality is stable — deriving anything fresh (an array from dirtyFields())\n// inside the selector would hand the store a new reference every pass and\n// loop it (React #185).\nconst useDevtoolsState = <TSchema extends z.ZodType>(form: Form<TSchema>) =>\n useFormSelectorShallow(form, (state) => ({\n values: state.values,\n errors: state.errors,\n schemaErrors: state.schemaErrors,\n serverErrors: state.serverErrors,\n touched: state.touched,\n isValidating: state.isValidating,\n isSubmitting: state.isSubmitting,\n isValidatingForm: state.isValidatingForm,\n submitCount: state.submitCount,\n mode: state.mode,\n }));\n\nconst Section = ({\n title,\n children,\n}: Readonly<{ title: string; children: ReactNode }>) => (\n <div style={styles.section}>\n <p style={styles.sectionTitle}>{title}</p>\n {children}\n </div>\n);\n\nconst DevtoolsPanel = <TSchema extends z.ZodType>({\n form,\n position = \"bottom-right\",\n defaultOpen = false,\n label = \"formstand\",\n}: FormstandDevtoolsProps<TSchema>) => {\n const state = useDevtoolsState(form);\n const [open, setOpen] = useState(defaultOpen);\n const [held, setHeld] = useState<FormState<z.input<TSchema>> | null>(null);\n\n // Computed every render rather than memoized. getFieldState and diff() read\n // through `form`, not through anything in a dependency array, so a memo\n // would need deps the linter can't see and would go stale the moment one\n // was missed. This component only re-renders when the subscription above\n // reports a real change, so recomputing is both cheap and always right.\n const rows: readonly FieldRow[] = leafPaths(state.values).map((path) => {\n // Runtime-built strings need the cast the typed surface documents.\n // getFieldState rather than re-deriving dirty here, so the panel reports\n // the library's own comparison rules (Dates by timestamp, deep for\n // containers) instead of offering a second opinion.\n const field = form.getFieldState(path as FieldPath<z.input<TSchema>>);\n return {\n path,\n value: field.value,\n error: field.error,\n touched: field.touched,\n dirty: field.dirty,\n isValidating: field.isValidating,\n };\n });\n\n const orphanErrors = unmatchedErrorKeys(\n Object.keys(state.errors),\n rows.map((row) => row.path),\n );\n\n const diff = form.diff();\n\n return (\n <div style={{ ...styles.root, ...anchorFor(position) }}>\n {open ? (\n <div style={styles.panel}>\n <div style={styles.header}>\n <strong>{label}</strong>\n <span style={styles.muted}>\n {state.mode}\n {state.isSubmitting ? \" · submitting\" : \"\"}\n {state.isValidatingForm ? \" · validating\" : \"\"}\n {state.submitCount > 0 ? ` · submits ${state.submitCount}` : \"\"}\n </span>\n <button\n type=\"button\"\n style={styles.button}\n onClick={() => setOpen(false)}\n >\n close\n </button>\n </div>\n\n <Section title={`fields (${rows.length})`}>\n <table style={styles.table}>\n <thead>\n <tr>\n <th style={{ ...styles.th, width: \"38%\" }}>path</th>\n <th style={styles.th}>value</th>\n <th style={{ ...styles.th, width: \"26%\" }}>state</th>\n </tr>\n </thead>\n <tbody>\n {rows.map((row) => (\n <tr key={row.path}>\n <td style={{ ...styles.td, ...styles.path }}>{row.path}</td>\n <td style={styles.td}>\n {preview(row.value)}\n {row.error === undefined ? null : (\n <div style={styles.error}>{row.error.join(\", \")}</div>\n )}\n </td>\n <td style={{ ...styles.td, ...styles.flag }}>\n {flags(row)}\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </Section>\n\n {orphanErrors.length === 0 ? null : (\n // Errors with no field of their own: the root \"\" key, array-level\n // messages, server verdicts on paths that hold no value yet.\n <Section title=\"errors without a field\">\n {orphanErrors.map((key) => (\n <div key={key}>\n <span style={styles.path}>{key === \"\" ? \"(root)\" : key}</span>{\" \"}\n <span style={styles.error}>\n {(state.errors[key] ?? []).join(\", \")}\n </span>\n </div>\n ))}\n </Section>\n )}\n\n <Section title=\"error channels\">\n <div style={styles.muted}>\n schema {Object.keys(state.schemaErrors).length} · server{\" \"}\n {Object.keys(state.serverErrors).length}\n </div>\n {Object.entries(state.serverErrors).map(([key, messages]) => (\n <div key={key}>\n <span style={styles.path}>{key === \"\" ? \"(root)\" : key}</span>{\" \"}\n <span style={styles.error}>{messages.join(\", \")}</span>{\" \"}\n <span style={styles.muted}>\n {getAtPath(state.schemaErrors, key) === undefined\n ? \"(showing)\"\n : \"(behind a schema error)\"}\n </span>\n </div>\n ))}\n </Section>\n\n <Section title=\"diff vs initial\">\n <pre style={styles.pre}>\n {Object.keys(diff).length === 0\n ? \"clean\"\n : JSON.stringify(diff, null, 2)}\n </pre>\n </Section>\n\n <Section title=\"time travel\">\n <button\n type=\"button\"\n style={styles.button}\n onClick={() => setHeld(form.snapshot())}\n >\n snapshot\n </button>{\" \"}\n <button\n type=\"button\"\n style={styles.button}\n disabled={held === null}\n onClick={() => {\n if (held !== null) form.restore(held);\n }}\n >\n restore\n </button>{\" \"}\n <span style={styles.muted}>\n {held === null ? \"nothing held\" : \"snapshot held\"}\n </span>\n </Section>\n </div>\n ) : (\n <button\n type=\"button\"\n style={styles.toggle}\n onClick={() => setOpen(true)}\n >\n {label}\n {Object.keys(state.errors).length > 0\n ? ` · ${String(Object.keys(state.errors).length)} err`\n : \"\"}\n </button>\n )}\n </div>\n );\n};\n\n/**\n * A form-aware debugging panel: every field's value, error, and flags, the\n * two error channels shown separately, the live diff against initial values,\n * and snapshot/restore time travel.\n *\n * Renders nothing in production builds.\n */\nexport const FormstandDevtools = <TSchema extends z.ZodType>(\n props: FormstandDevtoolsProps<TSchema>,\n) => (isDevBuild() ? <DevtoolsPanel {...props} /> : null);\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/core/env.ts","../../src/core/path.ts","../../src/react/useFormSelector.ts","../../src/devtools/paths.ts","../../src/devtools/styles.ts","../../src/devtools/FormstandDevtools.tsx"],"names":["useStore","useShallow","jsxs","jsx","useState"],"mappings":"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","file":"index.cjs","sourcesContent":["// The dev-build test the library consults before emitting a console\n// warning. Bundlers that define NODE_ENV fold it to a constant and drop the\n// dev-only branches; the `typeof process` guard keeps the SAME expression\n// from throwing in a runtime that defines no `process` at all (a CDN/ESM\n// consumer, a test runner without the define). That last case matters most\n// on the submit path: handleSubmit promises never to reject, and the\n// warning that fires only when a save failed was the one line that could\n// break the promise exactly on the failure path. It FAILS OPEN on purpose:\n// an unknowable environment gets the warnings, which cost nothing but a\n// console line.\nexport const isDevBuild = (): boolean =>\n typeof process === \"undefined\" ||\n process.env?.[\"NODE_ENV\"] !== \"production\";\n\n// The gate for the two sites that EXPOSE form state: the FormstandDevtools\n// panel (renders every value and error into the page) and the zustand\n// devtools middleware (streams every write to the Redux extension). These\n// must FAIL CLOSED: isDevBuild's \"no process means dev\" reading let a\n// CDN/ESM, Deno, or worker production build mount the panel and keep the\n// middleware live for any end user with the extension installed. Only a\n// readable NODE_ENV that is a string other than \"production\" counts as\n// development; no process, no env, or an unset NODE_ENV all read as\n// production here.\nexport const isDevToolsBuild = (): boolean => {\n if (typeof process === \"undefined\") return false;\n const env = process.env?.[\"NODE_ENV\"];\n return typeof env === \"string\" && env !== \"production\";\n};\n","import { isPlainObject } from \"./equality\";\n\nexport type PathSegment = string | number;\n\n// Segment keys containing \".\" are not addressable (paths are split on dots);\n// use nested objects or index-free keys instead.\n\n// Writes above this index are refused — a typo'd path like \"items.4294967296\"\n// must not allocate a multi-gigabyte array.\nconst MAX_ARRAY_INDEX = 100_000;\n\nconst isIndexSegment = (s: string): boolean => {\n const n = Number(s);\n return Number.isInteger(n) && n >= 0 && String(n) === s;\n};\n\n// Hot path: every field subscription re-resolves its path on every store\n// notification (getAtPath in selectors), so parsing is memoized — a form's\n// paths are a small fixed set. Bounded as an LRU over Map insertion order:\n// a hit re-inserts its key (moving it to the back) and an overflow evicts\n// the front, so an app whose LIVE path set exceeds the cap keeps its hot\n// paths cached instead of re-parsing every one of them after each reset\n// (the old wholesale clear). Eviction only costs re-parsing, never\n// correctness. Same delete-then-set recency idiom as useFieldArray's\n// per-path id cache.\nconst PARSE_CACHE_MAX = 4096;\nconst parseCache = new Map<string, readonly PathSegment[]>();\n\nexport const parsePath = (path: string): readonly PathSegment[] => {\n const cached = parseCache.get(path);\n if (cached !== undefined) {\n parseCache.delete(path);\n parseCache.set(path, cached);\n return cached;\n }\n // Frozen because the array is now SHARED across every caller (and cached):\n // parsePath is a public export, and pre-cache each call returned a private\n // array that was safe to mutate. Freezing enforces the `readonly` type at\n // runtime so an external `.sort()`/`.reverse()`/`.push()` throws instead of\n // silently corrupting the cache for every later lookup of this path.\n const segments = Object.freeze(\n path === \"\"\n ? []\n : path.split(\".\").map((s) => (isIndexSegment(s) ? Number(s) : s)),\n );\n if (parseCache.size >= PARSE_CACHE_MAX) {\n const oldest = parseCache.keys().next().value;\n if (oldest !== undefined) parseCache.delete(oldest);\n }\n parseCache.set(path, segments);\n return segments;\n};\n\nconst isPlainRecord = (value: unknown): value is Record<string, unknown> =>\n value !== null && typeof value === \"object\" && !Array.isArray(value);\n\n// The existing container decides how a segment is interpreted: arrays take\n// numeric segments as indices, records take any segment as a string key (so a\n// z.record keyed \"0\" reads/writes the record key, not an array index). Only\n// when the container is absent does the segment type pick what gets created.\nconst readSegment = (obj: unknown, segment: PathSegment): unknown => {\n if (obj == null) return undefined;\n if (Array.isArray(obj)) {\n return typeof segment === \"number\" ? obj[segment] : undefined;\n }\n // Own properties only: a runtime-built path like \"lookup.constructor\" on a\n // z.record must read as absent, not leak Object.prototype members as field\n // values.\n return isPlainRecord(obj) && Object.hasOwn(obj, String(segment))\n ? obj[String(segment)]\n : undefined;\n};\n\nexport const getAtPath = (obj: unknown, path: string): unknown =>\n parsePath(path).reduce<unknown>(readSegment, obj);\n\nexport type SlotAtPath =\n | Readonly<{ exists: true; value: unknown }>\n | Readonly<{ exists: false }>;\n\n// Resolve `path` while checking that every segment is read from a real\n// container: an in-range array index, or a key on a plain record (containers\n// decide, mirroring readSegment). A segment into an absent or non-container\n// value addresses no slot at all — a full-form parse would key that error at\n// the missing ANCESTOR, not here, so field-scoped validation must skip it\n// rather than parse the leaf's subschema against undefined (which would\n// fabricate a spurious \"required\" error the full pass never produces). Note\n// this checks the CONTAINER, not the value: a leaf that is undefined at the\n// FINAL step through a real record is still an addressable (legitimately\n// empty) slot. Returns the resolved value so callers don't walk twice.\nexport const slotAtPath = (obj: unknown, path: string): SlotAtPath => {\n const walked = parsePath(path).reduce<\n Readonly<{ ok: boolean; current: unknown }>\n >(\n (acc, segment) => {\n const container = acc.current;\n const addressable =\n typeof segment === \"number\"\n ? Array.isArray(container)\n ? segment < container.length\n : isPlainRecord(container)\n : isPlainRecord(container);\n return !acc.ok || !addressable\n ? { ok: false, current: undefined }\n : { ok: true, current: readSegment(container, segment) };\n },\n { ok: true, current: obj },\n );\n return walked.ok ? { exists: true, value: walked.current } : { exists: false };\n};\n\n// Whether the path addresses an OWN slot: every step reads a real\n// container's own key / in-range array index, the final segment included.\n// This is stricter than slotAtPath, which treats a MISSING final key on a\n// real record as an addressable (legitimately empty) slot — here it reads\n// false. The consumer is setValue's identity guard: writing `undefined`\n// over an ABSENT key is NOT a no-op (it creates the key, a real state\n// change — key-count dirtiness, persisted shape), while writing it over a\n// key that exists holding undefined is.\nexport const hasOwnSlot = (obj: unknown, path: string): boolean =>\n parsePath(path).reduce<Readonly<{ ok: boolean; current: unknown }>>(\n (acc, segment) => {\n const container = acc.current;\n const owns = Array.isArray(container)\n ? typeof segment === \"number\" && segment < container.length\n : isPlainRecord(container) && Object.hasOwn(container, String(segment));\n return !acc.ok || !owns\n ? { ok: false, current: undefined }\n : { ok: true, current: readSegment(container, segment) };\n },\n { ok: true, current: obj },\n ).ok;\n\nconst writeIntoArray = (\n arr: readonly unknown[],\n index: number,\n rest: readonly PathSegment[],\n value: unknown,\n): readonly unknown[] => {\n const length = Math.max(arr.length, index + 1);\n return Array.from({ length }, (_, i) =>\n i === index ? writeSegments(arr[i], rest, value) : arr[i],\n );\n};\n\nconst refusesHugeIndex = (index: number): boolean => {\n if (index > MAX_ARRAY_INDEX) {\n console.warn(\n `[formstand] refusing to write array index ${index} (max ${MAX_ARRAY_INDEX}); value left unchanged.`,\n );\n return true;\n }\n return false;\n};\n\nconst writeSegments = (\n obj: unknown,\n segments: readonly PathSegment[],\n value: unknown,\n): unknown => {\n const [head, ...rest] = segments;\n if (head === undefined) return value;\n\n if (Array.isArray(obj)) {\n if (typeof head !== \"number\") {\n console.warn(\n `[formstand] cannot write string key \"${head}\" into an array; value left unchanged.`,\n );\n return obj;\n }\n if (refusesHugeIndex(head)) return obj;\n return writeIntoArray(obj, head, rest, value);\n }\n\n // Records take any segment as a string key (mirroring readSegment), so a\n // z.record keyed by a large numeric id is writable — the array-index cap\n // only applies where an array actually exists or would be created.\n if (isPlainRecord(obj)) {\n // Spreading is only safe for PLAIN objects: spreading a Date/Map/Set or\n // class instance keeps own enumerable props but drops the prototype (and\n // for Map/Set, every entry) — the original would be silently destroyed.\n // Refuse, like the other unwritable shapes above.\n if (!isPlainObject(obj)) {\n console.warn(\n `[formstand] cannot write key \"${String(head)}\" through a non-plain object (Date/Map/Set/class instance); value left unchanged.`,\n );\n return obj;\n }\n const key = String(head);\n return { ...obj, [key]: writeSegments(obj[key], rest, value) };\n }\n\n // Absent (or non-container) value: create a container from the segment type.\n if (typeof head === \"number\") {\n if (refusesHugeIndex(head)) return obj;\n return writeIntoArray([], head, rest, value);\n }\n return { [head]: writeSegments(undefined, rest, value) };\n};\n\nexport const setAtPath = <T>(obj: T, path: string, value: unknown): T =>\n writeSegments(obj, parsePath(path), value) as T;\n","import type { z } from \"zod\";\nimport type { StoreApi } from \"zustand/vanilla\";\nimport { useStore } from \"zustand/react\";\nimport { useShallow } from \"zustand/react/shallow\";\nimport type { Form } from \"../core/createForm\";\nimport type { DefaultPathDepth, PathDepth } from \"../core/fieldPath\";\nimport type { FormState } from \"../core/types\";\n\ntype ReadonlyStore<T> = Pick<\n StoreApi<T>,\n \"getState\" | \"getInitialState\" | \"subscribe\"\n>;\n\nexport type FormStateApi = Readonly<{\n store: ReadonlyStore<FormState<unknown>>;\n}>;\n\nexport function useFormSelector<\n TSchema extends z.ZodType,\n U,\n D extends PathDepth = DefaultPathDepth,\n>(\n form: Form<TSchema, D>,\n selector: (state: FormState<z.input<TSchema>>) => U,\n): U;\n// The `schema?: undefined` brand forces Form<TSchema> (which has a real\n// `schema` property) to bind only the typed overload above. Without\n// it, Form<TSchema> would structurally satisfy FormStateApi too and a\n// bad selector could silently widen to FormState<unknown>.\nexport function useFormSelector<U>(\n form: FormStateApi & { readonly schema?: undefined },\n selector: (state: FormState<unknown>) => U,\n): U;\nexport function useFormSelector<U>(\n form: FormStateApi,\n selector: (state: FormState<unknown>) => U,\n): U {\n return useStore(form.store, selector);\n}\n\nexport function useFormSelectorShallow<\n TSchema extends z.ZodType,\n U,\n D extends PathDepth = DefaultPathDepth,\n>(\n form: Form<TSchema, D>,\n selector: (state: FormState<z.input<TSchema>>) => U,\n): U;\nexport function useFormSelectorShallow<U>(\n form: FormStateApi & { readonly schema?: undefined },\n selector: (state: FormState<unknown>) => U,\n): U;\nexport function useFormSelectorShallow<U>(\n form: FormStateApi,\n selector: (state: FormState<unknown>) => U,\n): U {\n return useStore(form.store, useShallow(selector));\n}\n","// Walking a values object into the list of paths the panel shows.\n\nconst isPlainObject = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" &&\n value !== null &&\n !Array.isArray(value) &&\n // A Date is a leaf, not a container to recurse into: its own keys are\n // methods, and useField binds the Date itself.\n !(value instanceof Date);\n\n// The addressable leaf paths of a values object, in declaration order.\n// Arrays contribute one path per ROW rather than one for the array, because\n// that is what a field binds to; the array's own path still shows up in the\n// panel via the error map, which is where `z.array().min(1)` lands.\n//\n// An empty object or array is itself a leaf here. It has no children to\n// describe, and dropping it would hide a field that exists in the schema.\nexport const leafPaths = (value: unknown, base = \"\"): readonly string[] => {\n const join = (key: string | number): string =>\n base === \"\" ? String(key) : `${base}.${String(key)}`;\n\n if (Array.isArray(value)) {\n return value.length === 0\n ? [base]\n : value.flatMap((item, index) => leafPaths(item, join(index)));\n }\n if (isPlainObject(value)) {\n const keys = Object.keys(value);\n return keys.length === 0\n ? [base]\n : keys.flatMap((key) => leafPaths(value[key], join(key)));\n }\n return [base];\n};\n\n// Error keys that no leaf path covers: the root \"\" key (a schema-wide\n// refine), array-level messages keyed at the container, and server verdicts\n// written at a path that holds no value yet. Without this the panel would\n// silently drop exactly the errors that are hardest to debug.\nexport const unmatchedErrorKeys = (\n errorKeys: readonly string[],\n leaves: readonly string[],\n): readonly string[] => {\n const covered = new Set(leaves);\n return errorKeys.filter((key) => !covered.has(key));\n};\n","import type { CSSProperties } from \"react\";\n\n// Inline styles on purpose: a devtools panel that needs the consumer to\n// import a stylesheet is a devtools panel that renders unstyled the first\n// time someone tries it. Inline also means nothing here can leak into or be\n// overridden by the host app's CSS, which matters when the whole job is\n// showing you the truth about your form.\n\nexport type DevtoolsPosition =\n | \"bottom-right\"\n | \"bottom-left\"\n | \"top-right\"\n | \"top-left\";\n\nconst ANCHOR: Readonly<Record<DevtoolsPosition, CSSProperties>> = {\n \"bottom-right\": { bottom: 12, right: 12 },\n \"bottom-left\": { bottom: 12, left: 12 },\n \"top-right\": { top: 12, right: 12 },\n \"top-left\": { top: 12, left: 12 },\n};\n\nconst MONO =\n 'ui-monospace, SFMono-Regular, Menlo, Consolas, \"Liberation Mono\", monospace';\n\nexport const anchorFor = (position: DevtoolsPosition): CSSProperties =>\n ANCHOR[position];\n\nexport const styles = {\n root: {\n position: \"fixed\",\n zIndex: 2147483000,\n fontFamily: MONO,\n fontSize: 11,\n lineHeight: 1.45,\n color: \"#e6e6e6\",\n colorScheme: \"dark\",\n } satisfies CSSProperties,\n toggle: {\n background: \"#1b1b1f\",\n border: \"1px solid #3a3a42\",\n color: \"#e6e6e6\",\n borderRadius: 6,\n padding: \"6px 10px\",\n font: \"inherit\",\n cursor: \"pointer\",\n } satisfies CSSProperties,\n panel: {\n width: \"min(440px, calc(100vw - 24px))\",\n maxHeight: \"min(60vh, 520px)\",\n overflow: \"auto\",\n background: \"#141417\",\n border: \"1px solid #3a3a42\",\n borderRadius: 8,\n boxShadow: \"0 12px 32px rgba(0,0,0,0.45)\",\n } satisfies CSSProperties,\n header: {\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"space-between\",\n gap: 8,\n padding: \"8px 10px\",\n borderBottom: \"1px solid #2a2a31\",\n position: \"sticky\",\n top: 0,\n background: \"#141417\",\n } satisfies CSSProperties,\n section: { padding: \"8px 10px\", borderBottom: \"1px solid #23232a\" },\n sectionTitle: {\n color: \"#9aa0aa\",\n textTransform: \"uppercase\",\n letterSpacing: \"0.06em\",\n fontSize: 10,\n margin: \"0 0 6px\",\n } satisfies CSSProperties,\n table: {\n width: \"100%\",\n borderCollapse: \"collapse\",\n tableLayout: \"fixed\",\n } satisfies CSSProperties,\n th: {\n textAlign: \"left\",\n color: \"#9aa0aa\",\n fontWeight: 400,\n padding: \"2px 4px\",\n borderBottom: \"1px solid #2a2a31\",\n } satisfies CSSProperties,\n td: {\n padding: \"2px 4px\",\n verticalAlign: \"top\",\n wordBreak: \"break-word\",\n } satisfies CSSProperties,\n path: { color: \"#c8b6ff\" } satisfies CSSProperties,\n error: { color: \"#ff8f8f\" } satisfies CSSProperties,\n muted: { color: \"#7d838d\" } satisfies CSSProperties,\n flag: { color: \"#7ddca4\" } satisfies CSSProperties,\n pre: {\n margin: 0,\n whiteSpace: \"pre-wrap\",\n wordBreak: \"break-word\",\n } satisfies CSSProperties,\n button: {\n background: \"#22222a\",\n border: \"1px solid #3a3a42\",\n color: \"#e6e6e6\",\n borderRadius: 5,\n padding: \"3px 8px\",\n font: \"inherit\",\n cursor: \"pointer\",\n } satisfies CSSProperties,\n} as const;\n","import { type ReactNode, useState } from \"react\";\nimport { isDevToolsBuild } from \"../core/env\";\nimport type { z } from \"zod\";\nimport type { Form } from \"../core/createForm\";\nimport type {\n DefaultPathDepth,\n FieldPath,\n PathDepth,\n} from \"../core/fieldPath\";\nimport { getAtPath } from \"../core/path\";\nimport type { FormState } from \"../core/types\";\nimport { useFormSelectorShallow } from \"../react/useFormSelector\";\nimport { leafPaths, unmatchedErrorKeys } from \"./paths\";\nimport { type DevtoolsPosition, anchorFor, styles } from \"./styles\";\n\n// D (the form's typed-path depth budget) is carried so a `pathDepth: 12`\n// form is accepted: Form<S, 12> is deliberately not assignable to Form<S, 9>,\n// so the depth-less prop type rejected every widened form (TS2375).\nexport type FormstandDevtoolsProps<\n TSchema extends z.ZodType,\n D extends PathDepth = DefaultPathDepth,\n> = Readonly<{\n form: Form<TSchema, D>;\n position?: DevtoolsPosition;\n defaultOpen?: boolean;\n /** Shown on the toggle, for telling two mounted panels apart. */\n label?: string;\n}>;\n\ntype FieldRow = Readonly<{\n path: string;\n value: unknown;\n error: readonly string[] | undefined;\n touched: boolean;\n dirty: boolean;\n isValidating: boolean;\n}>;\n\n// Non-production only, matching createForm's own devtools gate: the panel is\n// a debugging affordance, and shipping it to end users would leak the whole\n// form state into the page. Bundlers that define NODE_ENV drop the panel\n// entirely. The gate fails CLOSED (isDevToolsBuild): a runtime with no\n// readable NODE_ENV at all (a CDN/ESM consumer, Deno, a worker) renders\n// nothing rather than assuming it is a dev build the way the console\n// warnings may.\n\nconst preview = (value: unknown): string => {\n if (value === undefined) return \"undefined\";\n if (value instanceof Date) return value.toISOString();\n const json = JSON.stringify(value);\n // Long strings and big row objects would push the value column past the\n // panel; the full value is one expand away in the Values section.\n return json === undefined\n ? String(value)\n : json.length > 60\n ? `${json.slice(0, 57)}…`\n : json;\n};\n\nconst flags = (row: FieldRow): string =>\n [\n row.touched ? \"touched\" : \"\",\n row.dirty ? \"dirty\" : \"\",\n row.isValidating ? \"validating\" : \"\",\n ]\n .filter((flag) => flag !== \"\")\n .join(\" \");\n\n// Reads every slice the panel renders in ONE shallow-compared subscription.\n// Each member is a stored reference the form replaces on write, so shallow\n// equality is stable — deriving anything fresh (an array from dirtyFields())\n// inside the selector would hand the store a new reference every pass and\n// loop it (React #185).\nconst useDevtoolsState = <TSchema extends z.ZodType, D extends PathDepth>(\n form: Form<TSchema, D>,\n) =>\n useFormSelectorShallow(form, (state) => ({\n values: state.values,\n errors: state.errors,\n schemaErrors: state.schemaErrors,\n serverErrors: state.serverErrors,\n touched: state.touched,\n isValidating: state.isValidating,\n isSubmitting: state.isSubmitting,\n isValidatingForm: state.isValidatingForm,\n submitCount: state.submitCount,\n mode: state.mode,\n }));\n\nconst Section = ({\n title,\n children,\n}: Readonly<{ title: string; children: ReactNode }>) => (\n <div style={styles.section}>\n <p style={styles.sectionTitle}>{title}</p>\n {children}\n </div>\n);\n\nconst DevtoolsPanel = <TSchema extends z.ZodType, D extends PathDepth>({\n form,\n position = \"bottom-right\",\n defaultOpen = false,\n label = \"formstand\",\n}: FormstandDevtoolsProps<TSchema, D>) => {\n const state = useDevtoolsState(form);\n const [open, setOpen] = useState(defaultOpen);\n const [held, setHeld] = useState<FormState<z.input<TSchema>> | null>(null);\n\n // Computed every render rather than memoized. getFieldState and diff() read\n // through `form`, not through anything in a dependency array, so a memo\n // would need deps the linter can't see and would go stale the moment one\n // was missed. This component only re-renders when the subscription above\n // reports a real change, so recomputing is both cheap and always right.\n const rows: readonly FieldRow[] = leafPaths(state.values).map((path) => {\n // Runtime-built strings need the cast the typed surface documents.\n // getFieldState rather than re-deriving dirty here, so the panel reports\n // the library's own comparison rules (Dates by timestamp, deep for\n // containers) instead of offering a second opinion.\n const field = form.getFieldState(path as FieldPath<z.input<TSchema>, D>);\n return {\n path,\n value: field.value,\n error: field.error,\n touched: field.touched,\n dirty: field.dirty,\n isValidating: field.isValidating,\n };\n });\n\n const orphanErrors = unmatchedErrorKeys(\n Object.keys(state.errors),\n rows.map((row) => row.path),\n );\n\n const diff = form.diff();\n\n return (\n <div style={{ ...styles.root, ...anchorFor(position) }}>\n {open ? (\n <div style={styles.panel}>\n <div style={styles.header}>\n <strong>{label}</strong>\n <span style={styles.muted}>\n {state.mode}\n {state.isSubmitting ? \" · submitting\" : \"\"}\n {state.isValidatingForm ? \" · validating\" : \"\"}\n {state.submitCount > 0 ? ` · submits ${state.submitCount}` : \"\"}\n </span>\n <button\n type=\"button\"\n style={styles.button}\n onClick={() => setOpen(false)}\n >\n close\n </button>\n </div>\n\n <Section title={`fields (${rows.length})`}>\n <table style={styles.table}>\n <thead>\n <tr>\n <th style={{ ...styles.th, width: \"38%\" }}>path</th>\n <th style={styles.th}>value</th>\n <th style={{ ...styles.th, width: \"26%\" }}>state</th>\n </tr>\n </thead>\n <tbody>\n {rows.map((row) => (\n <tr key={row.path}>\n <td style={{ ...styles.td, ...styles.path }}>{row.path}</td>\n <td style={styles.td}>\n {preview(row.value)}\n {row.error === undefined ? null : (\n <div style={styles.error}>{row.error.join(\", \")}</div>\n )}\n </td>\n <td style={{ ...styles.td, ...styles.flag }}>\n {flags(row)}\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </Section>\n\n {orphanErrors.length === 0 ? null : (\n // Errors with no field of their own: the root \"\" key, array-level\n // messages, server verdicts on paths that hold no value yet.\n <Section title=\"errors without a field\">\n {orphanErrors.map((key) => (\n <div key={key}>\n <span style={styles.path}>{key === \"\" ? \"(root)\" : key}</span>{\" \"}\n <span style={styles.error}>\n {(state.errors[key] ?? []).join(\", \")}\n </span>\n </div>\n ))}\n </Section>\n )}\n\n <Section title=\"error channels\">\n <div style={styles.muted}>\n schema {Object.keys(state.schemaErrors).length} · server{\" \"}\n {Object.keys(state.serverErrors).length}\n </div>\n {Object.entries(state.serverErrors).map(([key, messages]) => (\n <div key={key}>\n <span style={styles.path}>{key === \"\" ? \"(root)\" : key}</span>{\" \"}\n <span style={styles.error}>{messages.join(\", \")}</span>{\" \"}\n <span style={styles.muted}>\n {getAtPath(state.schemaErrors, key) === undefined\n ? \"(showing)\"\n : \"(behind a schema error)\"}\n </span>\n </div>\n ))}\n </Section>\n\n <Section title=\"diff vs initial\">\n <pre style={styles.pre}>\n {Object.keys(diff).length === 0\n ? \"clean\"\n : JSON.stringify(diff, null, 2)}\n </pre>\n </Section>\n\n <Section title=\"time travel\">\n <button\n type=\"button\"\n style={styles.button}\n onClick={() => setHeld(form.snapshot())}\n >\n snapshot\n </button>{\" \"}\n <button\n type=\"button\"\n style={styles.button}\n disabled={held === null}\n onClick={() => {\n if (held !== null) form.restore(held);\n }}\n >\n restore\n </button>{\" \"}\n <span style={styles.muted}>\n {held === null ? \"nothing held\" : \"snapshot held\"}\n </span>\n </Section>\n </div>\n ) : (\n <button\n type=\"button\"\n style={styles.toggle}\n onClick={() => setOpen(true)}\n >\n {label}\n {Object.keys(state.errors).length > 0\n ? ` · ${String(Object.keys(state.errors).length)} err`\n : \"\"}\n </button>\n )}\n </div>\n );\n};\n\n/**\n * A form-aware debugging panel: every field's value, error, and flags, the\n * two error channels shown separately, the live diff against initial values,\n * and snapshot/restore time travel.\n *\n * Renders nothing in production builds, and nothing wherever NODE_ENV is not\n * readable (no `process` global, or one without NODE_ENV): the panel exposes\n * the whole form state, so an unknowable environment is treated as shipped.\n */\nexport const FormstandDevtools = <\n TSchema extends z.ZodType,\n D extends PathDepth = DefaultPathDepth,\n>(\n props: FormstandDevtoolsProps<TSchema, D>,\n) => (isDevToolsBuild() ? <DevtoolsPanel {...props} /> : null);\n"]}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
-
import { F as Form } from '../createForm-
|
|
3
|
+
import { P as PathDepth, D as DefaultPathDepth, F as Form } from '../createForm-BJum0RBu.cjs';
|
|
4
4
|
import 'zustand/vanilla';
|
|
5
5
|
|
|
6
6
|
type DevtoolsPosition = "bottom-right" | "bottom-left" | "top-right" | "top-left";
|
|
7
7
|
|
|
8
|
-
type FormstandDevtoolsProps<TSchema extends z.ZodType> = Readonly<{
|
|
9
|
-
form: Form<TSchema>;
|
|
8
|
+
type FormstandDevtoolsProps<TSchema extends z.ZodType, D extends PathDepth = DefaultPathDepth> = Readonly<{
|
|
9
|
+
form: Form<TSchema, D>;
|
|
10
10
|
position?: DevtoolsPosition;
|
|
11
11
|
defaultOpen?: boolean;
|
|
12
12
|
/** Shown on the toggle, for telling two mounted panels apart. */
|
|
@@ -17,9 +17,11 @@ type FormstandDevtoolsProps<TSchema extends z.ZodType> = Readonly<{
|
|
|
17
17
|
* two error channels shown separately, the live diff against initial values,
|
|
18
18
|
* and snapshot/restore time travel.
|
|
19
19
|
*
|
|
20
|
-
* Renders nothing in production builds
|
|
20
|
+
* Renders nothing in production builds, and nothing wherever NODE_ENV is not
|
|
21
|
+
* readable (no `process` global, or one without NODE_ENV): the panel exposes
|
|
22
|
+
* the whole form state, so an unknowable environment is treated as shipped.
|
|
21
23
|
*/
|
|
22
|
-
declare const FormstandDevtools: <TSchema extends z.ZodType>(props: FormstandDevtoolsProps<TSchema>) => react.JSX.Element | null;
|
|
24
|
+
declare const FormstandDevtools: <TSchema extends z.ZodType, D extends PathDepth = DefaultPathDepth>(props: FormstandDevtoolsProps<TSchema, D>) => react.JSX.Element | null;
|
|
23
25
|
|
|
24
26
|
declare const leafPaths: (value: unknown, base?: string) => readonly string[];
|
|
25
27
|
declare const unmatchedErrorKeys: (errorKeys: readonly string[], leaves: readonly string[]) => readonly string[];
|
package/dist/devtools/index.d.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
-
import { F as Form } from '../createForm-
|
|
3
|
+
import { P as PathDepth, D as DefaultPathDepth, F as Form } from '../createForm-BJum0RBu.js';
|
|
4
4
|
import 'zustand/vanilla';
|
|
5
5
|
|
|
6
6
|
type DevtoolsPosition = "bottom-right" | "bottom-left" | "top-right" | "top-left";
|
|
7
7
|
|
|
8
|
-
type FormstandDevtoolsProps<TSchema extends z.ZodType> = Readonly<{
|
|
9
|
-
form: Form<TSchema>;
|
|
8
|
+
type FormstandDevtoolsProps<TSchema extends z.ZodType, D extends PathDepth = DefaultPathDepth> = Readonly<{
|
|
9
|
+
form: Form<TSchema, D>;
|
|
10
10
|
position?: DevtoolsPosition;
|
|
11
11
|
defaultOpen?: boolean;
|
|
12
12
|
/** Shown on the toggle, for telling two mounted panels apart. */
|
|
@@ -17,9 +17,11 @@ type FormstandDevtoolsProps<TSchema extends z.ZodType> = Readonly<{
|
|
|
17
17
|
* two error channels shown separately, the live diff against initial values,
|
|
18
18
|
* and snapshot/restore time travel.
|
|
19
19
|
*
|
|
20
|
-
* Renders nothing in production builds
|
|
20
|
+
* Renders nothing in production builds, and nothing wherever NODE_ENV is not
|
|
21
|
+
* readable (no `process` global, or one without NODE_ENV): the panel exposes
|
|
22
|
+
* the whole form state, so an unknowable environment is treated as shipped.
|
|
21
23
|
*/
|
|
22
|
-
declare const FormstandDevtools: <TSchema extends z.ZodType>(props: FormstandDevtoolsProps<TSchema>) => react.JSX.Element | null;
|
|
24
|
+
declare const FormstandDevtools: <TSchema extends z.ZodType, D extends PathDepth = DefaultPathDepth>(props: FormstandDevtoolsProps<TSchema, D>) => react.JSX.Element | null;
|
|
23
25
|
|
|
24
26
|
declare const leafPaths: (value: unknown, base?: string) => readonly string[];
|
|
25
27
|
declare const unmatchedErrorKeys: (errorKeys: readonly string[], leaves: readonly string[]) => readonly string[];
|
package/dist/devtools/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getAtPath, useFormSelectorShallow } from '../chunk-
|
|
1
|
+
import { isDevToolsBuild, getAtPath, useFormSelectorShallow } from '../chunk-BMXIZEI4.js';
|
|
2
2
|
import { useState } from 'react';
|
|
3
3
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
4
4
|
|
|
@@ -114,7 +114,6 @@ var styles = {
|
|
|
114
114
|
cursor: "pointer"
|
|
115
115
|
}
|
|
116
116
|
};
|
|
117
|
-
var isDevBuild = () => typeof process === "undefined" || process.env?.["NODE_ENV"] !== "production";
|
|
118
117
|
var preview = (value) => {
|
|
119
118
|
if (value === void 0) return "undefined";
|
|
120
119
|
if (value instanceof Date) return value.toISOString();
|
|
@@ -269,7 +268,7 @@ var DevtoolsPanel = ({
|
|
|
269
268
|
}
|
|
270
269
|
) });
|
|
271
270
|
};
|
|
272
|
-
var FormstandDevtools = (props) =>
|
|
271
|
+
var FormstandDevtools = (props) => isDevToolsBuild() ? /* @__PURE__ */ jsx(DevtoolsPanel, { ...props }) : null;
|
|
273
272
|
|
|
274
273
|
export { FormstandDevtools, leafPaths, unmatchedErrorKeys };
|
|
275
274
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/devtools/paths.ts","../../src/devtools/styles.ts","../../src/devtools/FormstandDevtools.tsx"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["// Walking a values object into the list of paths the panel shows.\n\nconst isPlainObject = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" &&\n value !== null &&\n !Array.isArray(value) &&\n // A Date is a leaf, not a container to recurse into: its own keys are\n // methods, and useField binds the Date itself.\n !(value instanceof Date);\n\n// The addressable leaf paths of a values object, in declaration order.\n// Arrays contribute one path per ROW rather than one for the array, because\n// that is what a field binds to; the array's own path still shows up in the\n// panel via the error map, which is where `z.array().min(1)` lands.\n//\n// An empty object or array is itself a leaf here. It has no children to\n// describe, and dropping it would hide a field that exists in the schema.\nexport const leafPaths = (value: unknown, base = \"\"): readonly string[] => {\n const join = (key: string | number): string =>\n base === \"\" ? String(key) : `${base}.${String(key)}`;\n\n if (Array.isArray(value)) {\n return value.length === 0\n ? [base]\n : value.flatMap((item, index) => leafPaths(item, join(index)));\n }\n if (isPlainObject(value)) {\n const keys = Object.keys(value);\n return keys.length === 0\n ? [base]\n : keys.flatMap((key) => leafPaths(value[key], join(key)));\n }\n return [base];\n};\n\n// Error keys that no leaf path covers: the root \"\" key (a schema-wide\n// refine), array-level messages keyed at the container, and server verdicts\n// written at a path that holds no value yet. Without this the panel would\n// silently drop exactly the errors that are hardest to debug.\nexport const unmatchedErrorKeys = (\n errorKeys: readonly string[],\n leaves: readonly string[],\n): readonly string[] => {\n const covered = new Set(leaves);\n return errorKeys.filter((key) => !covered.has(key));\n};\n","import type { CSSProperties } from \"react\";\n\n// Inline styles on purpose: a devtools panel that needs the consumer to\n// import a stylesheet is a devtools panel that renders unstyled the first\n// time someone tries it. Inline also means nothing here can leak into or be\n// overridden by the host app's CSS, which matters when the whole job is\n// showing you the truth about your form.\n\nexport type DevtoolsPosition =\n | \"bottom-right\"\n | \"bottom-left\"\n | \"top-right\"\n | \"top-left\";\n\nconst ANCHOR: Readonly<Record<DevtoolsPosition, CSSProperties>> = {\n \"bottom-right\": { bottom: 12, right: 12 },\n \"bottom-left\": { bottom: 12, left: 12 },\n \"top-right\": { top: 12, right: 12 },\n \"top-left\": { top: 12, left: 12 },\n};\n\nconst MONO =\n 'ui-monospace, SFMono-Regular, Menlo, Consolas, \"Liberation Mono\", monospace';\n\nexport const anchorFor = (position: DevtoolsPosition): CSSProperties =>\n ANCHOR[position];\n\nexport const styles = {\n root: {\n position: \"fixed\",\n zIndex: 2147483000,\n fontFamily: MONO,\n fontSize: 11,\n lineHeight: 1.45,\n color: \"#e6e6e6\",\n colorScheme: \"dark\",\n } satisfies CSSProperties,\n toggle: {\n background: \"#1b1b1f\",\n border: \"1px solid #3a3a42\",\n color: \"#e6e6e6\",\n borderRadius: 6,\n padding: \"6px 10px\",\n font: \"inherit\",\n cursor: \"pointer\",\n } satisfies CSSProperties,\n panel: {\n width: \"min(440px, calc(100vw - 24px))\",\n maxHeight: \"min(60vh, 520px)\",\n overflow: \"auto\",\n background: \"#141417\",\n border: \"1px solid #3a3a42\",\n borderRadius: 8,\n boxShadow: \"0 12px 32px rgba(0,0,0,0.45)\",\n } satisfies CSSProperties,\n header: {\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"space-between\",\n gap: 8,\n padding: \"8px 10px\",\n borderBottom: \"1px solid #2a2a31\",\n position: \"sticky\",\n top: 0,\n background: \"#141417\",\n } satisfies CSSProperties,\n section: { padding: \"8px 10px\", borderBottom: \"1px solid #23232a\" },\n sectionTitle: {\n color: \"#9aa0aa\",\n textTransform: \"uppercase\",\n letterSpacing: \"0.06em\",\n fontSize: 10,\n margin: \"0 0 6px\",\n } satisfies CSSProperties,\n table: {\n width: \"100%\",\n borderCollapse: \"collapse\",\n tableLayout: \"fixed\",\n } satisfies CSSProperties,\n th: {\n textAlign: \"left\",\n color: \"#9aa0aa\",\n fontWeight: 400,\n padding: \"2px 4px\",\n borderBottom: \"1px solid #2a2a31\",\n } satisfies CSSProperties,\n td: {\n padding: \"2px 4px\",\n verticalAlign: \"top\",\n wordBreak: \"break-word\",\n } satisfies CSSProperties,\n path: { color: \"#c8b6ff\" } satisfies CSSProperties,\n error: { color: \"#ff8f8f\" } satisfies CSSProperties,\n muted: { color: \"#7d838d\" } satisfies CSSProperties,\n flag: { color: \"#7ddca4\" } satisfies CSSProperties,\n pre: {\n margin: 0,\n whiteSpace: \"pre-wrap\",\n wordBreak: \"break-word\",\n } satisfies CSSProperties,\n button: {\n background: \"#22222a\",\n border: \"1px solid #3a3a42\",\n color: \"#e6e6e6\",\n borderRadius: 5,\n padding: \"3px 8px\",\n font: \"inherit\",\n cursor: \"pointer\",\n } satisfies CSSProperties,\n} as const;\n","import { type ReactNode, useState } from \"react\";\nimport type { z } from \"zod\";\nimport type { Form } from \"../core/createForm\";\nimport type { FieldPath } from \"../core/fieldPath\";\nimport { getAtPath } from \"../core/path\";\nimport type { FormState } from \"../core/types\";\nimport { useFormSelectorShallow } from \"../react/useFormSelector\";\nimport { leafPaths, unmatchedErrorKeys } from \"./paths\";\nimport { type DevtoolsPosition, anchorFor, styles } from \"./styles\";\n\nexport type FormstandDevtoolsProps<TSchema extends z.ZodType> = Readonly<{\n form: Form<TSchema>;\n position?: DevtoolsPosition;\n defaultOpen?: boolean;\n /** Shown on the toggle, for telling two mounted panels apart. */\n label?: string;\n}>;\n\ntype FieldRow = Readonly<{\n path: string;\n value: unknown;\n error: readonly string[] | undefined;\n touched: boolean;\n dirty: boolean;\n isValidating: boolean;\n}>;\n\n// Non-production only, matching createForm's own devtools gate: the panel is\n// a debugging affordance, and shipping it to end users would leak the whole\n// form state into the page. Bundlers that define NODE_ENV drop the panel\n// entirely; the `typeof process` guard keeps it from throwing in runtimes\n// that define no process at all.\nconst isDevBuild = (): boolean =>\n typeof process === \"undefined\" ||\n process.env?.[\"NODE_ENV\"] !== \"production\";\n\nconst preview = (value: unknown): string => {\n if (value === undefined) return \"undefined\";\n if (value instanceof Date) return value.toISOString();\n const json = JSON.stringify(value);\n // Long strings and big row objects would push the value column past the\n // panel; the full value is one expand away in the Values section.\n return json === undefined\n ? String(value)\n : json.length > 60\n ? `${json.slice(0, 57)}…`\n : json;\n};\n\nconst flags = (row: FieldRow): string =>\n [\n row.touched ? \"touched\" : \"\",\n row.dirty ? \"dirty\" : \"\",\n row.isValidating ? \"validating\" : \"\",\n ]\n .filter((flag) => flag !== \"\")\n .join(\" \");\n\n// Reads every slice the panel renders in ONE shallow-compared subscription.\n// Each member is a stored reference the form replaces on write, so shallow\n// equality is stable — deriving anything fresh (an array from dirtyFields())\n// inside the selector would hand the store a new reference every pass and\n// loop it (React #185).\nconst useDevtoolsState = <TSchema extends z.ZodType>(form: Form<TSchema>) =>\n useFormSelectorShallow(form, (state) => ({\n values: state.values,\n errors: state.errors,\n schemaErrors: state.schemaErrors,\n serverErrors: state.serverErrors,\n touched: state.touched,\n isValidating: state.isValidating,\n isSubmitting: state.isSubmitting,\n isValidatingForm: state.isValidatingForm,\n submitCount: state.submitCount,\n mode: state.mode,\n }));\n\nconst Section = ({\n title,\n children,\n}: Readonly<{ title: string; children: ReactNode }>) => (\n <div style={styles.section}>\n <p style={styles.sectionTitle}>{title}</p>\n {children}\n </div>\n);\n\nconst DevtoolsPanel = <TSchema extends z.ZodType>({\n form,\n position = \"bottom-right\",\n defaultOpen = false,\n label = \"formstand\",\n}: FormstandDevtoolsProps<TSchema>) => {\n const state = useDevtoolsState(form);\n const [open, setOpen] = useState(defaultOpen);\n const [held, setHeld] = useState<FormState<z.input<TSchema>> | null>(null);\n\n // Computed every render rather than memoized. getFieldState and diff() read\n // through `form`, not through anything in a dependency array, so a memo\n // would need deps the linter can't see and would go stale the moment one\n // was missed. This component only re-renders when the subscription above\n // reports a real change, so recomputing is both cheap and always right.\n const rows: readonly FieldRow[] = leafPaths(state.values).map((path) => {\n // Runtime-built strings need the cast the typed surface documents.\n // getFieldState rather than re-deriving dirty here, so the panel reports\n // the library's own comparison rules (Dates by timestamp, deep for\n // containers) instead of offering a second opinion.\n const field = form.getFieldState(path as FieldPath<z.input<TSchema>>);\n return {\n path,\n value: field.value,\n error: field.error,\n touched: field.touched,\n dirty: field.dirty,\n isValidating: field.isValidating,\n };\n });\n\n const orphanErrors = unmatchedErrorKeys(\n Object.keys(state.errors),\n rows.map((row) => row.path),\n );\n\n const diff = form.diff();\n\n return (\n <div style={{ ...styles.root, ...anchorFor(position) }}>\n {open ? (\n <div style={styles.panel}>\n <div style={styles.header}>\n <strong>{label}</strong>\n <span style={styles.muted}>\n {state.mode}\n {state.isSubmitting ? \" · submitting\" : \"\"}\n {state.isValidatingForm ? \" · validating\" : \"\"}\n {state.submitCount > 0 ? ` · submits ${state.submitCount}` : \"\"}\n </span>\n <button\n type=\"button\"\n style={styles.button}\n onClick={() => setOpen(false)}\n >\n close\n </button>\n </div>\n\n <Section title={`fields (${rows.length})`}>\n <table style={styles.table}>\n <thead>\n <tr>\n <th style={{ ...styles.th, width: \"38%\" }}>path</th>\n <th style={styles.th}>value</th>\n <th style={{ ...styles.th, width: \"26%\" }}>state</th>\n </tr>\n </thead>\n <tbody>\n {rows.map((row) => (\n <tr key={row.path}>\n <td style={{ ...styles.td, ...styles.path }}>{row.path}</td>\n <td style={styles.td}>\n {preview(row.value)}\n {row.error === undefined ? null : (\n <div style={styles.error}>{row.error.join(\", \")}</div>\n )}\n </td>\n <td style={{ ...styles.td, ...styles.flag }}>\n {flags(row)}\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </Section>\n\n {orphanErrors.length === 0 ? null : (\n // Errors with no field of their own: the root \"\" key, array-level\n // messages, server verdicts on paths that hold no value yet.\n <Section title=\"errors without a field\">\n {orphanErrors.map((key) => (\n <div key={key}>\n <span style={styles.path}>{key === \"\" ? \"(root)\" : key}</span>{\" \"}\n <span style={styles.error}>\n {(state.errors[key] ?? []).join(\", \")}\n </span>\n </div>\n ))}\n </Section>\n )}\n\n <Section title=\"error channels\">\n <div style={styles.muted}>\n schema {Object.keys(state.schemaErrors).length} · server{\" \"}\n {Object.keys(state.serverErrors).length}\n </div>\n {Object.entries(state.serverErrors).map(([key, messages]) => (\n <div key={key}>\n <span style={styles.path}>{key === \"\" ? \"(root)\" : key}</span>{\" \"}\n <span style={styles.error}>{messages.join(\", \")}</span>{\" \"}\n <span style={styles.muted}>\n {getAtPath(state.schemaErrors, key) === undefined\n ? \"(showing)\"\n : \"(behind a schema error)\"}\n </span>\n </div>\n ))}\n </Section>\n\n <Section title=\"diff vs initial\">\n <pre style={styles.pre}>\n {Object.keys(diff).length === 0\n ? \"clean\"\n : JSON.stringify(diff, null, 2)}\n </pre>\n </Section>\n\n <Section title=\"time travel\">\n <button\n type=\"button\"\n style={styles.button}\n onClick={() => setHeld(form.snapshot())}\n >\n snapshot\n </button>{\" \"}\n <button\n type=\"button\"\n style={styles.button}\n disabled={held === null}\n onClick={() => {\n if (held !== null) form.restore(held);\n }}\n >\n restore\n </button>{\" \"}\n <span style={styles.muted}>\n {held === null ? \"nothing held\" : \"snapshot held\"}\n </span>\n </Section>\n </div>\n ) : (\n <button\n type=\"button\"\n style={styles.toggle}\n onClick={() => setOpen(true)}\n >\n {label}\n {Object.keys(state.errors).length > 0\n ? ` · ${String(Object.keys(state.errors).length)} err`\n : \"\"}\n </button>\n )}\n </div>\n );\n};\n\n/**\n * A form-aware debugging panel: every field's value, error, and flags, the\n * two error channels shown separately, the live diff against initial values,\n * and snapshot/restore time travel.\n *\n * Renders nothing in production builds.\n */\nexport const FormstandDevtools = <TSchema extends z.ZodType>(\n props: FormstandDevtoolsProps<TSchema>,\n) => (isDevBuild() ? <DevtoolsPanel {...props} /> : null);\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/devtools/paths.ts","../../src/devtools/styles.ts","../../src/devtools/FormstandDevtools.tsx"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["// Walking a values object into the list of paths the panel shows.\n\nconst isPlainObject = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" &&\n value !== null &&\n !Array.isArray(value) &&\n // A Date is a leaf, not a container to recurse into: its own keys are\n // methods, and useField binds the Date itself.\n !(value instanceof Date);\n\n// The addressable leaf paths of a values object, in declaration order.\n// Arrays contribute one path per ROW rather than one for the array, because\n// that is what a field binds to; the array's own path still shows up in the\n// panel via the error map, which is where `z.array().min(1)` lands.\n//\n// An empty object or array is itself a leaf here. It has no children to\n// describe, and dropping it would hide a field that exists in the schema.\nexport const leafPaths = (value: unknown, base = \"\"): readonly string[] => {\n const join = (key: string | number): string =>\n base === \"\" ? String(key) : `${base}.${String(key)}`;\n\n if (Array.isArray(value)) {\n return value.length === 0\n ? [base]\n : value.flatMap((item, index) => leafPaths(item, join(index)));\n }\n if (isPlainObject(value)) {\n const keys = Object.keys(value);\n return keys.length === 0\n ? [base]\n : keys.flatMap((key) => leafPaths(value[key], join(key)));\n }\n return [base];\n};\n\n// Error keys that no leaf path covers: the root \"\" key (a schema-wide\n// refine), array-level messages keyed at the container, and server verdicts\n// written at a path that holds no value yet. Without this the panel would\n// silently drop exactly the errors that are hardest to debug.\nexport const unmatchedErrorKeys = (\n errorKeys: readonly string[],\n leaves: readonly string[],\n): readonly string[] => {\n const covered = new Set(leaves);\n return errorKeys.filter((key) => !covered.has(key));\n};\n","import type { CSSProperties } from \"react\";\n\n// Inline styles on purpose: a devtools panel that needs the consumer to\n// import a stylesheet is a devtools panel that renders unstyled the first\n// time someone tries it. Inline also means nothing here can leak into or be\n// overridden by the host app's CSS, which matters when the whole job is\n// showing you the truth about your form.\n\nexport type DevtoolsPosition =\n | \"bottom-right\"\n | \"bottom-left\"\n | \"top-right\"\n | \"top-left\";\n\nconst ANCHOR: Readonly<Record<DevtoolsPosition, CSSProperties>> = {\n \"bottom-right\": { bottom: 12, right: 12 },\n \"bottom-left\": { bottom: 12, left: 12 },\n \"top-right\": { top: 12, right: 12 },\n \"top-left\": { top: 12, left: 12 },\n};\n\nconst MONO =\n 'ui-monospace, SFMono-Regular, Menlo, Consolas, \"Liberation Mono\", monospace';\n\nexport const anchorFor = (position: DevtoolsPosition): CSSProperties =>\n ANCHOR[position];\n\nexport const styles = {\n root: {\n position: \"fixed\",\n zIndex: 2147483000,\n fontFamily: MONO,\n fontSize: 11,\n lineHeight: 1.45,\n color: \"#e6e6e6\",\n colorScheme: \"dark\",\n } satisfies CSSProperties,\n toggle: {\n background: \"#1b1b1f\",\n border: \"1px solid #3a3a42\",\n color: \"#e6e6e6\",\n borderRadius: 6,\n padding: \"6px 10px\",\n font: \"inherit\",\n cursor: \"pointer\",\n } satisfies CSSProperties,\n panel: {\n width: \"min(440px, calc(100vw - 24px))\",\n maxHeight: \"min(60vh, 520px)\",\n overflow: \"auto\",\n background: \"#141417\",\n border: \"1px solid #3a3a42\",\n borderRadius: 8,\n boxShadow: \"0 12px 32px rgba(0,0,0,0.45)\",\n } satisfies CSSProperties,\n header: {\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"space-between\",\n gap: 8,\n padding: \"8px 10px\",\n borderBottom: \"1px solid #2a2a31\",\n position: \"sticky\",\n top: 0,\n background: \"#141417\",\n } satisfies CSSProperties,\n section: { padding: \"8px 10px\", borderBottom: \"1px solid #23232a\" },\n sectionTitle: {\n color: \"#9aa0aa\",\n textTransform: \"uppercase\",\n letterSpacing: \"0.06em\",\n fontSize: 10,\n margin: \"0 0 6px\",\n } satisfies CSSProperties,\n table: {\n width: \"100%\",\n borderCollapse: \"collapse\",\n tableLayout: \"fixed\",\n } satisfies CSSProperties,\n th: {\n textAlign: \"left\",\n color: \"#9aa0aa\",\n fontWeight: 400,\n padding: \"2px 4px\",\n borderBottom: \"1px solid #2a2a31\",\n } satisfies CSSProperties,\n td: {\n padding: \"2px 4px\",\n verticalAlign: \"top\",\n wordBreak: \"break-word\",\n } satisfies CSSProperties,\n path: { color: \"#c8b6ff\" } satisfies CSSProperties,\n error: { color: \"#ff8f8f\" } satisfies CSSProperties,\n muted: { color: \"#7d838d\" } satisfies CSSProperties,\n flag: { color: \"#7ddca4\" } satisfies CSSProperties,\n pre: {\n margin: 0,\n whiteSpace: \"pre-wrap\",\n wordBreak: \"break-word\",\n } satisfies CSSProperties,\n button: {\n background: \"#22222a\",\n border: \"1px solid #3a3a42\",\n color: \"#e6e6e6\",\n borderRadius: 5,\n padding: \"3px 8px\",\n font: \"inherit\",\n cursor: \"pointer\",\n } satisfies CSSProperties,\n} as const;\n","import { type ReactNode, useState } from \"react\";\nimport { isDevToolsBuild } from \"../core/env\";\nimport type { z } from \"zod\";\nimport type { Form } from \"../core/createForm\";\nimport type {\n DefaultPathDepth,\n FieldPath,\n PathDepth,\n} from \"../core/fieldPath\";\nimport { getAtPath } from \"../core/path\";\nimport type { FormState } from \"../core/types\";\nimport { useFormSelectorShallow } from \"../react/useFormSelector\";\nimport { leafPaths, unmatchedErrorKeys } from \"./paths\";\nimport { type DevtoolsPosition, anchorFor, styles } from \"./styles\";\n\n// D (the form's typed-path depth budget) is carried so a `pathDepth: 12`\n// form is accepted: Form<S, 12> is deliberately not assignable to Form<S, 9>,\n// so the depth-less prop type rejected every widened form (TS2375).\nexport type FormstandDevtoolsProps<\n TSchema extends z.ZodType,\n D extends PathDepth = DefaultPathDepth,\n> = Readonly<{\n form: Form<TSchema, D>;\n position?: DevtoolsPosition;\n defaultOpen?: boolean;\n /** Shown on the toggle, for telling two mounted panels apart. */\n label?: string;\n}>;\n\ntype FieldRow = Readonly<{\n path: string;\n value: unknown;\n error: readonly string[] | undefined;\n touched: boolean;\n dirty: boolean;\n isValidating: boolean;\n}>;\n\n// Non-production only, matching createForm's own devtools gate: the panel is\n// a debugging affordance, and shipping it to end users would leak the whole\n// form state into the page. Bundlers that define NODE_ENV drop the panel\n// entirely. The gate fails CLOSED (isDevToolsBuild): a runtime with no\n// readable NODE_ENV at all (a CDN/ESM consumer, Deno, a worker) renders\n// nothing rather than assuming it is a dev build the way the console\n// warnings may.\n\nconst preview = (value: unknown): string => {\n if (value === undefined) return \"undefined\";\n if (value instanceof Date) return value.toISOString();\n const json = JSON.stringify(value);\n // Long strings and big row objects would push the value column past the\n // panel; the full value is one expand away in the Values section.\n return json === undefined\n ? String(value)\n : json.length > 60\n ? `${json.slice(0, 57)}…`\n : json;\n};\n\nconst flags = (row: FieldRow): string =>\n [\n row.touched ? \"touched\" : \"\",\n row.dirty ? \"dirty\" : \"\",\n row.isValidating ? \"validating\" : \"\",\n ]\n .filter((flag) => flag !== \"\")\n .join(\" \");\n\n// Reads every slice the panel renders in ONE shallow-compared subscription.\n// Each member is a stored reference the form replaces on write, so shallow\n// equality is stable — deriving anything fresh (an array from dirtyFields())\n// inside the selector would hand the store a new reference every pass and\n// loop it (React #185).\nconst useDevtoolsState = <TSchema extends z.ZodType, D extends PathDepth>(\n form: Form<TSchema, D>,\n) =>\n useFormSelectorShallow(form, (state) => ({\n values: state.values,\n errors: state.errors,\n schemaErrors: state.schemaErrors,\n serverErrors: state.serverErrors,\n touched: state.touched,\n isValidating: state.isValidating,\n isSubmitting: state.isSubmitting,\n isValidatingForm: state.isValidatingForm,\n submitCount: state.submitCount,\n mode: state.mode,\n }));\n\nconst Section = ({\n title,\n children,\n}: Readonly<{ title: string; children: ReactNode }>) => (\n <div style={styles.section}>\n <p style={styles.sectionTitle}>{title}</p>\n {children}\n </div>\n);\n\nconst DevtoolsPanel = <TSchema extends z.ZodType, D extends PathDepth>({\n form,\n position = \"bottom-right\",\n defaultOpen = false,\n label = \"formstand\",\n}: FormstandDevtoolsProps<TSchema, D>) => {\n const state = useDevtoolsState(form);\n const [open, setOpen] = useState(defaultOpen);\n const [held, setHeld] = useState<FormState<z.input<TSchema>> | null>(null);\n\n // Computed every render rather than memoized. getFieldState and diff() read\n // through `form`, not through anything in a dependency array, so a memo\n // would need deps the linter can't see and would go stale the moment one\n // was missed. This component only re-renders when the subscription above\n // reports a real change, so recomputing is both cheap and always right.\n const rows: readonly FieldRow[] = leafPaths(state.values).map((path) => {\n // Runtime-built strings need the cast the typed surface documents.\n // getFieldState rather than re-deriving dirty here, so the panel reports\n // the library's own comparison rules (Dates by timestamp, deep for\n // containers) instead of offering a second opinion.\n const field = form.getFieldState(path as FieldPath<z.input<TSchema>, D>);\n return {\n path,\n value: field.value,\n error: field.error,\n touched: field.touched,\n dirty: field.dirty,\n isValidating: field.isValidating,\n };\n });\n\n const orphanErrors = unmatchedErrorKeys(\n Object.keys(state.errors),\n rows.map((row) => row.path),\n );\n\n const diff = form.diff();\n\n return (\n <div style={{ ...styles.root, ...anchorFor(position) }}>\n {open ? (\n <div style={styles.panel}>\n <div style={styles.header}>\n <strong>{label}</strong>\n <span style={styles.muted}>\n {state.mode}\n {state.isSubmitting ? \" · submitting\" : \"\"}\n {state.isValidatingForm ? \" · validating\" : \"\"}\n {state.submitCount > 0 ? ` · submits ${state.submitCount}` : \"\"}\n </span>\n <button\n type=\"button\"\n style={styles.button}\n onClick={() => setOpen(false)}\n >\n close\n </button>\n </div>\n\n <Section title={`fields (${rows.length})`}>\n <table style={styles.table}>\n <thead>\n <tr>\n <th style={{ ...styles.th, width: \"38%\" }}>path</th>\n <th style={styles.th}>value</th>\n <th style={{ ...styles.th, width: \"26%\" }}>state</th>\n </tr>\n </thead>\n <tbody>\n {rows.map((row) => (\n <tr key={row.path}>\n <td style={{ ...styles.td, ...styles.path }}>{row.path}</td>\n <td style={styles.td}>\n {preview(row.value)}\n {row.error === undefined ? null : (\n <div style={styles.error}>{row.error.join(\", \")}</div>\n )}\n </td>\n <td style={{ ...styles.td, ...styles.flag }}>\n {flags(row)}\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </Section>\n\n {orphanErrors.length === 0 ? null : (\n // Errors with no field of their own: the root \"\" key, array-level\n // messages, server verdicts on paths that hold no value yet.\n <Section title=\"errors without a field\">\n {orphanErrors.map((key) => (\n <div key={key}>\n <span style={styles.path}>{key === \"\" ? \"(root)\" : key}</span>{\" \"}\n <span style={styles.error}>\n {(state.errors[key] ?? []).join(\", \")}\n </span>\n </div>\n ))}\n </Section>\n )}\n\n <Section title=\"error channels\">\n <div style={styles.muted}>\n schema {Object.keys(state.schemaErrors).length} · server{\" \"}\n {Object.keys(state.serverErrors).length}\n </div>\n {Object.entries(state.serverErrors).map(([key, messages]) => (\n <div key={key}>\n <span style={styles.path}>{key === \"\" ? \"(root)\" : key}</span>{\" \"}\n <span style={styles.error}>{messages.join(\", \")}</span>{\" \"}\n <span style={styles.muted}>\n {getAtPath(state.schemaErrors, key) === undefined\n ? \"(showing)\"\n : \"(behind a schema error)\"}\n </span>\n </div>\n ))}\n </Section>\n\n <Section title=\"diff vs initial\">\n <pre style={styles.pre}>\n {Object.keys(diff).length === 0\n ? \"clean\"\n : JSON.stringify(diff, null, 2)}\n </pre>\n </Section>\n\n <Section title=\"time travel\">\n <button\n type=\"button\"\n style={styles.button}\n onClick={() => setHeld(form.snapshot())}\n >\n snapshot\n </button>{\" \"}\n <button\n type=\"button\"\n style={styles.button}\n disabled={held === null}\n onClick={() => {\n if (held !== null) form.restore(held);\n }}\n >\n restore\n </button>{\" \"}\n <span style={styles.muted}>\n {held === null ? \"nothing held\" : \"snapshot held\"}\n </span>\n </Section>\n </div>\n ) : (\n <button\n type=\"button\"\n style={styles.toggle}\n onClick={() => setOpen(true)}\n >\n {label}\n {Object.keys(state.errors).length > 0\n ? ` · ${String(Object.keys(state.errors).length)} err`\n : \"\"}\n </button>\n )}\n </div>\n );\n};\n\n/**\n * A form-aware debugging panel: every field's value, error, and flags, the\n * two error channels shown separately, the live diff against initial values,\n * and snapshot/restore time travel.\n *\n * Renders nothing in production builds, and nothing wherever NODE_ENV is not\n * readable (no `process` global, or one without NODE_ENV): the panel exposes\n * the whole form state, so an unknowable environment is treated as shipped.\n */\nexport const FormstandDevtools = <\n TSchema extends z.ZodType,\n D extends PathDepth = DefaultPathDepth,\n>(\n props: FormstandDevtoolsProps<TSchema, D>,\n) => (isDevToolsBuild() ? <DevtoolsPanel {...props} /> : null);\n"]}
|