@victorylabs/params 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/integrations/forms/index.ts","../../src/dev.ts","../../../utils/src/cascade.ts","../../../utils/src/deep.ts","../../../utils/src/path-trie.ts","../../src/name-registry.ts","../../src/schema.ts","../../src/params-store.ts","../../src/store-cache.ts"],"sourcesContent":["import type { SyncAdapter } from '@victorylabs/forms'\n\nimport { acquire, release } from '../../store-cache'\nimport type { ParamsDefinition } from '../../types'\n\n/**\n * Bridge a `@victorylabs/params` definition into `@victorylabs/forms` as a\n * `SyncAdapter`. Use when a single source of truth (the params definition)\n * should drive both:\n * - filter/view-state UI via `useParams(def)`, and\n * - a form (e.g. \"save filter view\") that writes to the same external state\n * via `defineForm({ sync: [paramsToFormSync(def)] })`.\n *\n * The bridge calls `acquire(def)` from the params store cache, so the form\n * shares state with any concurrent `useParams(def)` callers. On dispose\n * (forms unmounts the adapter via `subscribe`'s cleanup), the bridge calls\n * `release(def)`. Standard ref counting; Strict Mode safe.\n *\n * The form's resolver remains the source of truth for validation messages.\n * Params' per-field schemas are only used for storage parsing (silent\n * fallback). See plan §\"Error messages\" for rationale.\n */\nexport function paramsToFormSync<T extends Record<string, unknown> = Record<string, unknown>>(\n def: ParamsDefinition<T>,\n): SyncAdapter<T> {\n // Whether the underlying storage writes to the URL — forms uses this to\n // detect dual URL writers and throw at form-creation time.\n const writesUrl = def.storage.name === 'urlStorage'\n\n // Acquire lazily on first read/subscribe; release in subscribe's cleanup.\n // For read-only forms (no subscribe), the consumer is responsible for\n // calling releaseParamsStore(def) — same as any non-React getParamsStore use.\n let acquired = false\n const ensure = (): ReturnType<typeof acquire<T>> => {\n if (!acquired) {\n acquired = true\n return acquire(def)\n }\n return acquire(def)\n }\n\n return {\n name: `paramsToFormSync(${def.name ?? def.storage.name})`,\n fields: '*',\n clientOnly: def.storage.clientOnly,\n ...(writesUrl && { __writesUrl: true }),\n\n read() {\n const store = acquire(def)\n acquired = true\n const values = { ...store.getValues() } as Partial<T>\n return values\n },\n\n write(values) {\n const store = acquire(def)\n acquired = true\n store.set(values as Partial<T>)\n },\n\n subscribe(callback) {\n const store = ensure()\n const unsubscribe = store.subscribe('', () => {\n callback({ ...store.getValues() } as Partial<T>)\n })\n return () => {\n unsubscribe()\n if (acquired) {\n release(def)\n acquired = false\n }\n }\n },\n\n clear(paths) {\n const store = acquire(def)\n acquired = true\n if (paths.length === 0) {\n store.reset()\n } else {\n for (const path of paths) {\n store.clear(path)\n }\n }\n },\n }\n}\n","// Bundlers (tsup, vite, webpack) replace `process.env.NODE_ENV` at build time.\n// We declare `process` minimally so TypeScript doesn't require @types/node.\n\ndeclare const process:\n | {\n env?: {\n NODE_ENV?: string\n }\n }\n | undefined\n\nconst isDev = ((): boolean => {\n try {\n return typeof process !== 'undefined' && process?.env?.NODE_ENV !== 'production'\n } catch {\n return false\n }\n})()\n\nconst warned = new Set<string>()\n\n/**\n * Emit a one-time dev-mode console warning. Identical messages are\n * suppressed after the first occurrence to avoid log spam.\n */\nexport function warn(message: string): void {\n if (!isDev) return\n if (warned.has(message)) return\n warned.add(message)\n console.warn(`[@victorylabs/params] ${message}`)\n}\n","/**\n * Cascade resolver for `dependsOn` / `excludeDeps` / `onDepChange` field configs.\n * Used by both `@victorylabs/forms` and `@victorylabs/params` v0.2.\n *\n * Pure function — no side effects, no I/O. Consumers wire the result into their\n * own commit phase (params: PathTrie notify; forms: notify + revalidate + dirty/error reset).\n *\n * Algorithm: BFS over the dependency graph.\n * - Each round computes the next layer of cascaded changes.\n * - Visited paths are tracked to detect cycles (warn + skip re-entry).\n * - Depth cap stops runaway chains at 10 levels.\n *\n * The `'*'` wildcard for `dependsOn` is resolved at cascade time (not config\n * time), so adding fields to a definition doesn't invalidate `'*'` consumers.\n */\n\nconst DEPTH_CAP = 10\n\nexport interface CascadeFieldConfig {\n /**\n * Paths whose changes trigger this field's `onDepChange`. `'*'` means\n * \"every other field\"; combine with `excludeDeps` to carve out exceptions.\n */\n dependsOn?: string[] | '*'\n\n /**\n * Paths to exclude from the `'*'` wildcard. Only meaningful when\n * `dependsOn === '*'`. Implicitly always includes the field itself\n * (a field never depends on itself).\n */\n excludeDeps?: string[]\n\n /**\n * What to do when a dep changes. `'reset'` restores the field's default;\n * `'clear'` sets it to `undefined`; a function returns the new value.\n * The function form receives ALL dep values (changed + unchanged) keyed\n * by path, plus the field's own current value.\n */\n onDepChange?: 'reset' | 'clear' | ((deps: Record<string, unknown>, current: unknown) => unknown)\n}\n\nexport interface CascadeContext {\n /** Per-field config keyed by path. Fields without cascade config are inert. */\n fieldConfigs: Record<string, CascadeFieldConfig>\n\n /** Default values keyed by path — resolves `onDepChange === 'reset'`. */\n defaults: Record<string, unknown>\n\n /** Current values keyed by path — resolves the function-form `onDepChange`. */\n currentValues: Record<string, unknown>\n\n /** Every path in the definition — resolves `'*'` wildcard. */\n allPaths: string[]\n}\n\nexport interface CascadeResult {\n /** Initial changes plus all cascaded changes, keyed by path. */\n changes: Record<string, unknown>\n\n /** Cycle-detection / depth-cap warnings (one entry per anomaly). */\n warnings: string[]\n}\n\n/**\n * Resolve a cascade. Given a set of initial changes, compute every dependent\n * field that should also change.\n */\nexport function resolveCascade(\n initialChanges: Record<string, unknown>,\n ctx: CascadeContext,\n): CascadeResult {\n const changes: Record<string, unknown> = { ...initialChanges }\n const warnings: string[] = []\n\n // Working values during BFS — start from current, override with initial changes.\n // Each round's cascaded changes accumulate here so dependent fields see them.\n const working: Record<string, unknown> = { ...ctx.currentValues, ...initialChanges }\n\n // Seed: paths changed in this round become the next round's triggers.\n let frontier = new Set(Object.keys(initialChanges))\n // Cycle guard: if a path is re-targeted within the same cascade, warn + skip.\n const visited = new Set<string>(frontier)\n\n let depth = 0\n while (frontier.size > 0) {\n if (depth >= DEPTH_CAP) {\n warnings.push(\n `cascade depth cap (${DEPTH_CAP}) reached; stopping. Frontier: [${[...frontier].join(', ')}]. Likely a config error — check for accidental long chains.`,\n )\n break\n }\n\n const nextFrontier = new Set<string>()\n\n for (const targetPath of ctx.allPaths) {\n const config = ctx.fieldConfigs[targetPath]\n if (!config?.onDepChange) continue\n if (frontier.has(targetPath)) continue // a path doesn't cascade to itself\n\n const deps = resolveDeps(config, ctx.allPaths, targetPath)\n // Did any of this field's deps change in the current frontier?\n const changedDeps = deps.filter((d) => frontier.has(d))\n if (changedDeps.length === 0) continue\n\n if (visited.has(targetPath)) {\n warnings.push(\n `cycle detected: '${targetPath}' would cascade twice (triggered by [${changedDeps.join(', ')}]). Skipping re-entry.`,\n )\n continue\n }\n\n // Compute the new value via onDepChange.\n const newValue = applyDepChange(config.onDepChange, deps, working, ctx.defaults, targetPath)\n\n // Skip no-op cascades: if the resolved value equals the field's current\n // working value, don't propagate further (avoids spurious re-renders).\n if (Object.is(newValue, working[targetPath])) continue\n\n changes[targetPath] = newValue\n working[targetPath] = newValue\n visited.add(targetPath)\n nextFrontier.add(targetPath)\n }\n\n frontier = nextFrontier\n depth++\n }\n\n return { changes, warnings }\n}\n\n/**\n * Resolve a field's effective dependency list. `'*'` expands to every OTHER\n * path minus `excludeDeps` and the field itself.\n */\nfunction resolveDeps(config: CascadeFieldConfig, allPaths: string[], selfPath: string): string[] {\n if (config.dependsOn === '*') {\n const exclude = new Set(config.excludeDeps ?? [])\n exclude.add(selfPath) // a field doesn't depend on itself\n return allPaths.filter((p) => !exclude.has(p))\n }\n if (Array.isArray(config.dependsOn)) {\n // Filter out self-references defensively (config error otherwise).\n return config.dependsOn.filter((p) => p !== selfPath)\n }\n return []\n}\n\n/**\n * Compute the new value for a cascaded field via its `onDepChange` config.\n */\nfunction applyDepChange(\n onDepChange: NonNullable<CascadeFieldConfig['onDepChange']>,\n deps: string[],\n working: Record<string, unknown>,\n defaults: Record<string, unknown>,\n targetPath: string,\n): unknown {\n if (onDepChange === 'reset') return defaults[targetPath]\n if (onDepChange === 'clear') return undefined\n\n // Function form: build the deps record from the working values\n // (so cascaded changes within the same set() call are visible).\n const depsRecord: Record<string, unknown> = {}\n for (const d of deps) {\n depsRecord[d] = working[d]\n }\n return onDepChange(depsRecord, working[targetPath])\n}\n","/**\n * Path-aware deep helpers. Paths are dot-strings: 'address.street', 'tags.2'.\n * Numeric segments index into arrays; string segments index into objects.\n *\n * Limitation: keys containing literal '.' are not supported. Document this\n * for consumers; forms with such keys are vanishingly rare.\n */\n\nexport function splitPath(path: string): string[] {\n if (path === '') return []\n return path.split('.')\n}\n\nexport function deepGet(obj: unknown, path: string): unknown {\n const segments = splitPath(path)\n let current: unknown = obj\n for (const seg of segments) {\n if (current === null || current === undefined) return undefined\n if (Array.isArray(current)) {\n const idx = Number(seg)\n if (!Number.isInteger(idx) || idx < 0) return undefined\n current = current[idx]\n } else if (typeof current === 'object') {\n current = (current as Record<string, unknown>)[seg]\n } else {\n return undefined\n }\n }\n return current\n}\n\nexport function deepSet<T>(obj: T, path: string, value: unknown): T {\n const segments = splitPath(path)\n if (segments.length === 0) return value as T\n return setRecursive(obj, segments, 0, value) as T\n}\n\nfunction setRecursive(current: unknown, segments: string[], i: number, value: unknown): unknown {\n const seg = segments[i]\n // Sanity: segments.length > i always when this runs (caller ensures).\n if (seg === undefined) return value\n const isLast = i === segments.length - 1\n const idx = Number(seg)\n const isNumericSeg = Number.isInteger(idx) && idx >= 0 && /^\\d+$/.test(seg)\n\n if (isNumericSeg) {\n const arr = Array.isArray(current) ? current.slice() : []\n arr[idx] = isLast ? value : setRecursive(arr[idx], segments, i + 1, value)\n return arr\n }\n\n const next: Record<string, unknown> =\n current && typeof current === 'object' && !Array.isArray(current)\n ? { ...(current as Record<string, unknown>) }\n : {}\n next[seg] = isLast ? value : setRecursive(next[seg], segments, i + 1, value)\n return next\n}\n\nexport function deepEqual(a: unknown, b: unknown): boolean {\n if (Object.is(a, b)) return true\n if (typeof a !== typeof b) return false\n if (a === null || b === null) return false\n if (typeof a !== 'object') return false\n\n if (Array.isArray(a)) {\n if (!Array.isArray(b)) return false\n if (a.length !== b.length) return false\n for (let i = 0; i < a.length; i++) {\n if (!deepEqual(a[i], b[i])) return false\n }\n return true\n }\n if (Array.isArray(b)) return false\n\n if (a instanceof Date) {\n return b instanceof Date && a.getTime() === b.getTime()\n }\n if (b instanceof Date) return false\n\n if (a instanceof RegExp) {\n return b instanceof RegExp && a.source === b.source && a.flags === b.flags\n }\n if (b instanceof RegExp) return false\n\n const aKeys = Object.keys(a as Record<string, unknown>)\n const bKeys = Object.keys(b as Record<string, unknown>)\n if (aKeys.length !== bKeys.length) return false\n for (const key of aKeys) {\n if (!Object.hasOwn(b as object, key)) return false\n if (!deepEqual((a as Record<string, unknown>)[key], (b as Record<string, unknown>)[key])) {\n return false\n }\n }\n return true\n}\n\n/**\n * Deep merge: later sources overwrite earlier ones at scalar/array level;\n * objects merge recursively. Arrays are replaced wholesale (no concatenation),\n * which matches the form-state mental model — an array IS the value.\n */\nexport function deepMerge<T extends object>(...sources: Array<Partial<T> | undefined>): T {\n const result = {} as Record<string, unknown>\n for (const source of sources) {\n if (!source) continue\n for (const [key, value] of Object.entries(source)) {\n const existing = result[key]\n if (\n existing &&\n value &&\n typeof existing === 'object' &&\n typeof value === 'object' &&\n !Array.isArray(existing) &&\n !Array.isArray(value) &&\n !(existing instanceof Date) &&\n !(value instanceof Date)\n ) {\n result[key] = deepMerge(existing as Partial<object>, value as Partial<object>)\n } else {\n result[key] = value\n }\n }\n }\n return result as T\n}\n","import { splitPath } from './deep'\n\ninterface Node {\n listeners: Set<() => void>\n children: Map<string, Node>\n}\n\nconst makeNode = (): Node => ({ listeners: new Set(), children: new Map() })\n\n/**\n * Path-scoped subscription bus.\n *\n * Notification semantics for `notify(path)`:\n * - Subscribers AT `path` fire (exact match).\n * - Subscribers at every ANCESTOR of `path` fire (a parent observing the\n * aggregated subtree should re-render when any descendant changes).\n * - Subscribers at every DESCENDANT of `path` fire (a subtree replacement\n * at `path` invalidates any leaf below it).\n * - Sibling-branch subscribers do NOT fire.\n *\n * notify('') notifies the root + every subscriber in the trie.\n */\nexport class PathTrie {\n private readonly root: Node = makeNode()\n\n subscribe(path: string, listener: () => void): () => void {\n const node = this.ensure(path)\n node.listeners.add(listener)\n return () => {\n node.listeners.delete(listener)\n }\n }\n\n notify(path: string): void {\n const segments = splitPath(path)\n\n let current: Node = this.root\n fire(current)\n\n for (const seg of segments) {\n const next = current.children.get(seg)\n if (!next) return\n current = next\n fire(current)\n }\n\n fireSubtree(current)\n }\n\n /** Test/diagnostic helper — total subscriber count below a given path. */\n size(path = ''): number {\n const node = this.find(path)\n if (!node) return 0\n return countSubtree(node)\n }\n\n private ensure(path: string): Node {\n let node = this.root\n for (const seg of splitPath(path)) {\n let child = node.children.get(seg)\n if (!child) {\n child = makeNode()\n node.children.set(seg, child)\n }\n node = child\n }\n return node\n }\n\n private find(path: string): Node | undefined {\n let node: Node | undefined = this.root\n for (const seg of splitPath(path)) {\n node = node?.children.get(seg)\n if (!node) return undefined\n }\n return node\n }\n}\n\nfunction fire(node: Node): void {\n for (const listener of node.listeners) listener()\n}\n\nfunction fireSubtree(node: Node): void {\n for (const child of node.children.values()) {\n fire(child)\n fireSubtree(child)\n }\n}\n\nfunction countSubtree(node: Node): number {\n let total = node.listeners.size\n for (const child of node.children.values()) {\n total += countSubtree(child)\n }\n return total\n}\n","import type { ParamsDefinition } from './types'\n\n/**\n * Side-effect registry of all definitions that carry a `name`. Populated by\n * `defineParams()` at construction time so `paramsSnapshot()` and\n * `hydrateParams()` can look up defs by name.\n *\n * Last-write-wins: re-defining the same name overwrites silently (the\n * duplicate-name DEV warning is enforced separately at acquire time in\n * `store-cache.ts`).\n */\nconst nameRegistry = new Map<string, ParamsDefinition<unknown>>()\n\n/**\n * Pre-hydration cache for SSR snapshots. `hydrateParams(snapshot)` populates\n * this; `ParamsStore` checks it during initial hydration and uses the seeded\n * values instead of calling the storage backend's `read()`.\n *\n * Once consumed by a store on first hydration the entry is left in place\n * (idempotent re-hydrate) — the consumer can call `hydrateParams()` again\n * with overrides if needed before any store is constructed.\n */\nconst preHydrationCache = new Map<string, unknown>()\n\nexport function registerNamedDef<T>(def: ParamsDefinition<T>): void {\n if (def.name === undefined) return\n nameRegistry.set(def.name, def as ParamsDefinition<unknown>)\n}\n\nexport function getDefByName(name: string): ParamsDefinition<unknown> | undefined {\n return nameRegistry.get(name)\n}\n\nexport function getRegisteredDefs(): ReadonlyArray<ParamsDefinition<unknown>> {\n return Array.from(nameRegistry.values())\n}\n\nexport function setPreHydrationValues(name: string, values: unknown): void {\n preHydrationCache.set(name, values)\n}\n\nexport function takePreHydrationValues<T>(name: string | undefined): Partial<T> | undefined {\n if (name === undefined) return undefined\n if (!preHydrationCache.has(name)) return undefined\n return preHydrationCache.get(name) as Partial<T>\n}\n\n/** Test-only: clear both registries. Not exported from the package. */\nexport function _resetNameRegistry(): void {\n nameRegistry.clear()\n preHydrationCache.clear()\n}\n","import type { StandardSchemaV1 } from '@standard-schema/spec'\n\nimport type { FieldSpec, PlainFieldSpec } from './types'\n\nexport function isStandardSchema(spec: unknown): spec is StandardSchemaV1 {\n return (\n typeof spec === 'object' &&\n spec !== null &&\n '~standard' in spec &&\n typeof (spec as { '~standard'?: unknown })['~standard'] === 'object'\n )\n}\n\nexport function isPlainSpec<T>(spec: FieldSpec<T>): spec is PlainFieldSpec<T> {\n return !isStandardSchema(spec) && typeof spec === 'object' && spec !== null && 'default' in spec\n}\n\n/**\n * Resolve a field's default value.\n *\n * - Plain spec: `spec.default` (always present — required by the type).\n * - Standard Schema (Zod, Valibot, ArkType, …): parse `undefined` and use the\n * schema's `.default()` if it produced a value. Schemas without a default\n * return `undefined`.\n */\nexport function getDefault<T>(spec: FieldSpec<T>): T | undefined {\n if (isStandardSchema(spec)) {\n const result = spec['~standard'].validate(undefined)\n if (result instanceof Promise) return undefined\n if ('value' in result && !result.issues) return result.value as T\n return undefined\n }\n return (spec as PlainFieldSpec<T>).default\n}\n\n/**\n * Parse a raw storage value through the field's spec. Returns the typed value\n * on success, or `undefined` on parse failure (engine falls back to default).\n *\n * The engine records the failure reason in `ParamsStore.storageErrors` for\n * debugging; consumer code doesn't see it.\n */\nexport interface ParseResult<T> {\n ok: boolean\n value?: T\n reason?: string\n}\n\nexport function parseField<T>(spec: FieldSpec<T>, raw: unknown): ParseResult<T> {\n if (isStandardSchema(spec)) {\n const result = spec['~standard'].validate(raw)\n if (result instanceof Promise) {\n return { ok: false, reason: 'async-schema-not-supported-in-v0.1' }\n }\n if ('value' in result && !result.issues) return { ok: true, value: result.value as T }\n return { ok: false, reason: result.issues?.[0]?.message ?? 'parse-failed' }\n }\n\n const plainSpec = spec as PlainFieldSpec<T>\n if (typeof raw === 'string' && plainSpec.parse) {\n try {\n return { ok: true, value: plainSpec.parse(raw) }\n } catch (err) {\n return { ok: false, reason: err instanceof Error ? err.message : String(err) }\n }\n }\n // No custom parse: accept raw value as-is if it's a usable runtime value\n // (string, number, boolean, array, object, etc.). The schema-less plain spec\n // is mostly used for non-string-coerced flows (memory storage with raw values).\n if (raw === undefined) return { ok: false, reason: 'undefined-no-parse' }\n return { ok: true, value: raw as T }\n}\n\n/**\n * Serialize a typed value to its storage string representation. Used by URL\n * storage (every value must be a string) and other string-keyed backends.\n *\n * Plain spec uses its `serialize` if provided, else `String(value)`. Standard\n * schemas don't define an inverse — we fall back to `String(value)` (or\n * `JSON.stringify` for objects) and the consumer can override via per-field\n * `serialize` in the storage backend's options.\n */\nexport function defaultSerialize(value: unknown): string {\n if (value === undefined || value === null) return ''\n if (typeof value === 'string') return value\n if (typeof value === 'boolean') return value ? 'true' : 'false'\n if (typeof value === 'number') return Number.isFinite(value) ? String(value) : ''\n return JSON.stringify(value)\n}\n\n/**\n * Extract the enum values from a field spec, if any. Returns the array of\n * enum members or `undefined` if the spec doesn't expose them.\n *\n * Supports:\n * - Zod `z.enum(['a', 'b'])` — reads `_def.values: ['a', 'b']`.\n * - Zod `z.nativeEnum(MyEnum)` — reads `_def.values: <enum-object>`.\n * For numeric enums (TypeScript bidirectional `enum Color { Red = 0 }`),\n * `Object.values` returns BOTH the numbers and the reverse-mapped strings;\n * this helper detects the case and filters to numeric values only.\n *\n * Standard Schema doesn't define a uniform enum-introspection API, so libraries\n * other than Zod return `undefined` here — `cycle()` then throws and asks the\n * caller to pass options explicitly.\n */\nexport function extractEnumValues(spec: unknown): readonly unknown[] | undefined {\n // Walk Zod wrapper types (ZodDefault, ZodOptional, ZodNullable, …) to find\n // an underlying enum. `.default(...)` / `.optional()` / `.nullable()` produce\n // a wrapper whose `_def.innerType` points to the wrapped schema. Cap the walk\n // at a small depth — pathological self-referential schemas shouldn't loop.\n // biome-ignore lint/suspicious/noExplicitAny: Zod internal `_def` API\n let current: any = spec\n for (let i = 0; i < 8 && current !== null && typeof current === 'object'; i++) {\n const def = current._def\n if (!def) return undefined\n\n // z.enum(['a', 'b']): values is already a clean string array.\n if (Array.isArray(def.values)) return def.values\n\n // z.nativeEnum(MyEnum): values is the enum object. TS numeric enums are\n // BIDIRECTIONAL — Object.values returns numbers + reverse-mapped strings:\n // enum Color { Red = 0 } → Object.values(Color) = [0, 'Red'].\n // Detect numeric and filter; pure string enums are unidirectional and\n // return only strings.\n if (typeof def.values === 'object' && def.values !== null) {\n const all = Object.values(def.values)\n const hasNumber = all.some((v) => typeof v === 'number')\n return hasNumber ? all.filter((v) => typeof v === 'number') : all\n }\n\n if (def.innerType) {\n current = def.innerType\n continue\n }\n return undefined\n }\n return undefined\n}\n","import { deepEqual, deepGet, deepSet, PathTrie, resolveCascade } from '@victorylabs/utils'\n\nimport { warn } from './dev'\nimport { takePreHydrationValues } from './name-registry'\nimport {\n defaultSerialize,\n extractEnumValues,\n getDefault,\n isPlainSpec,\n isStandardSchema,\n parseField,\n} from './schema'\nimport type { ParamsStorage, WriteOptions } from './storage'\nimport type { FieldConfig, FieldSpec, ParamsDefinition } from './types'\n\n/**\n * Per-call write options accepted by `set()` and `clear()`. Currently shapes\n * the URL `history` strategy override; backends ignore fields they don't\n * recognize.\n */\nexport interface SetOptions {\n /** History API override for URL-like backends. `'push'` beats `'replace'` across batched writes. */\n readonly history?: 'push' | 'replace'\n}\n\n/**\n * Frozen, defensive-copied snapshot returned by `ParamsStore.__introspect()`.\n *\n * Surface is **unstable** — fields may be added freely across releases; do\n * not depend on the type identity. Use for devtools panels, debug logging,\n * or test assertions that don't fit through the public observable API.\n */\nexport interface ParamsStoreIntrospection<T = Record<string, unknown>> {\n readonly values: Readonly<Partial<T>>\n readonly defaults: Readonly<Partial<T>>\n readonly fieldsConfigured: Readonly<Record<string, FieldConfig>>\n readonly fieldsBySpecType: Readonly<Record<string, 'zod' | 'standard-schema' | 'plain'>>\n readonly storageErrors: Readonly<Record<string, string>>\n readonly storage: {\n readonly name: string\n readonly clientOnly: boolean\n readonly hasReadAsync: boolean\n readonly hasSubscribe: boolean\n readonly hasClear: boolean\n }\n readonly lastWritten: Readonly<Partial<T>> | undefined\n}\n\nconst isClient = typeof window !== 'undefined'\n\n/**\n * Framework-agnostic store. Owns:\n * - values + storage round-tripping\n * - schema validation on read (silent fallback to defaults)\n * - PathTrie-based subscriptions\n * - all state-mutation helpers (set, toggle, append, …)\n * - loop prevention against external storage echoes\n * - memoized toQuery\n *\n * The React adapter (`@victorylabs/params/react`) wraps this with\n * useSyncExternalStore-style hooks; non-React consumers use the methods\n * directly via `getParamsStore(def)`.\n */\nexport class ParamsStore<T = Record<string, unknown>> {\n private readonly spec: Readonly<Record<string, FieldSpec>>\n private readonly storage: ParamsStorage<T>\n private readonly fieldConfigs: Readonly<Record<string, FieldConfig>>\n\n private values: Partial<T>\n private readonly defaults: Partial<T>\n private readonly trie = new PathTrie()\n private readonly storageErrorMap = new Map<string, string>()\n\n private lastWritten: Partial<T> | undefined\n private storageUnsubscribe: (() => void) | undefined\n private toQueryCache: { values: Partial<T>; query: string; href?: string } | undefined\n private disposed = false\n\n constructor(def: ParamsDefinition<T>) {\n this.spec = def.spec\n this.storage = def.storage\n this.fieldConfigs = def.fields\n\n this.defaults = this.computeDefaults()\n this.values = { ...this.defaults }\n\n // SSR pre-hydration: if hydrateParams() seeded values for this def's name,\n // those win over the storage backend's read() — the snapshot represents\n // the authoritative server-rendered state.\n const seeded = takePreHydrationValues<T>(def.name)\n if (seeded !== undefined) {\n this.values = { ...this.defaults, ...seeded }\n } else if (this.storage.clientOnly && !isClient) {\n // Server: skip read; values stay at defaults\n } else {\n this.hydrateFromStorage()\n }\n\n if (this.storage.subscribe) {\n this.storageUnsubscribe = this.storage.subscribe((raw) => this.onExternalChange(raw))\n }\n }\n\n // ─── Reads (synchronous, framework-agnostic) ──────────────────────────\n\n getValues(): Readonly<Partial<T>> {\n return this.values\n }\n\n getValue<P extends string>(path: P): unknown {\n return deepGet(this.values, path)\n }\n\n /** Storage parse failures discovered on hydrate or external change. */\n get storageErrors(): Readonly<Record<string, string>> {\n const out: Record<string, string> = {}\n for (const [path, reason] of this.storageErrorMap) out[path] = reason\n return out\n }\n\n /** Per-field config (for the React adapter to read debounce settings, etc.). */\n getFieldConfig(path: string): FieldConfig | undefined {\n return this.fieldConfigs[path]\n }\n\n // ─── Writes ───────────────────────────────────────────────────────────\n\n set(path: string, value: unknown, options?: SetOptions): void\n set(partial: Partial<T>, options?: SetOptions): void\n set(\n pathOrPartial: string | Partial<T>,\n valueOrOptions?: unknown,\n maybeOptions?: SetOptions,\n ): void {\n if (this.disposed) return\n\n let updates: Record<string, unknown>\n let options: SetOptions | undefined\n if (typeof pathOrPartial === 'string') {\n updates = { [pathOrPartial]: valueOrOptions }\n options = maybeOptions\n } else {\n updates = pathOrPartial as Record<string, unknown>\n options = valueOrOptions as SetOptions | undefined\n }\n\n // First pass: filter out no-op updates (value already deep-equals current).\n const initialChanges: Record<string, unknown> = {}\n for (const [path, v] of Object.entries(updates)) {\n const old = deepGet(this.values, path)\n if (deepEqual(old, v)) continue\n initialChanges[path] = v\n }\n if (Object.keys(initialChanges).length === 0) return\n\n // Resolve cascading dependencies (no-op when no field has dependsOn).\n const cascade = resolveCascade(initialChanges, {\n fieldConfigs: this.fieldConfigs,\n defaults: this.defaults as Record<string, unknown>,\n currentValues: this.values as Record<string, unknown>,\n allPaths: Object.keys(this.spec),\n })\n\n for (const w of cascade.warnings) warn(w)\n\n // Second pass: apply initial + cascaded changes atomically.\n const changed: string[] = []\n let next = this.values\n for (const [path, v] of Object.entries(cascade.changes)) {\n const old = deepGet(next, path)\n if (deepEqual(old, v)) continue\n next = deepSet(next, path, v) as Partial<T>\n changed.push(path)\n }\n if (changed.length === 0) return\n\n this.values = next\n this.invalidateToQueryCache()\n\n // Notify subscribers (React 18 batches these into one render per consumer)\n for (const path of changed) this.trie.notify(path)\n\n // Persist to storage with omitWhenDefault filtering. Per-call options\n // (history strategy) flow straight through to storage.write.\n this.persistToStorage(changed, options)\n }\n\n /** Boolean-flip helper. */\n toggle(path: string, options?: SetOptions): void {\n const current = this.getValue(path)\n this.set(path, !current, options)\n }\n\n /** Push a value onto an array field. */\n append(path: string, value: unknown, options?: SetOptions): void {\n const current = this.getValue(path)\n if (!Array.isArray(current)) return\n this.set(path, [...current, value], options)\n }\n\n /** Remove the first array element matching `value` by deepEqual. */\n remove(path: string, value: unknown, options?: SetOptions): void {\n const current = this.getValue(path)\n if (!Array.isArray(current)) return\n const idx = current.findIndex((item) => deepEqual(item, value))\n if (idx === -1) return\n this.set(path, [...current.slice(0, idx), ...current.slice(idx + 1)], options)\n }\n\n /** Remove the array element at the given index. */\n removeAt(path: string, index: number, options?: SetOptions): void {\n const current = this.getValue(path)\n if (!Array.isArray(current)) return\n if (index < 0 || index >= current.length) return\n this.set(path, [...current.slice(0, index), ...current.slice(index + 1)], options)\n }\n\n /**\n * Cycle the value through the given values (e.g., 'asc' → 'desc' → 'asc').\n * If the current value isn't in the values, jumps to the first one.\n *\n * v0.4: when called without an explicit values array, derives the rotation\n * from the field's Zod `z.enum` / `z.nativeEnum` schema. Throws if the schema\n * doesn't expose enum metadata.\n *\n * v0.5: optional `SetOptions` arg (history strategy) on every overload.\n */\n cycle(path: string, options?: SetOptions): void\n cycle(path: string, values: ReadonlyArray<unknown>, options?: SetOptions): void\n cycle(\n path: string,\n valuesOrOptions?: ReadonlyArray<unknown> | SetOptions,\n maybeOptions?: SetOptions,\n ): void {\n // Disambiguate the second argument: a ReadonlyArray means explicit values;\n // a non-array object means SetOptions; undefined means \"derive from schema\".\n let resolved: ReadonlyArray<unknown> | undefined\n let setOpts: SetOptions | undefined\n if (Array.isArray(valuesOrOptions)) {\n resolved = valuesOrOptions\n setOpts = maybeOptions\n } else {\n resolved = undefined\n setOpts = valuesOrOptions as SetOptions | undefined\n }\n\n if (resolved === undefined) {\n const fieldSpec = this.spec[path]\n const derived = fieldSpec !== undefined ? extractEnumValues(fieldSpec) : undefined\n if (!derived || derived.length === 0) {\n throw new Error(\n `Cannot derive enum values for params field '${path}'; pass values explicitly to cycle().`,\n )\n }\n resolved = derived\n }\n if (resolved.length === 0) return\n const current = this.getValue(path)\n const idx = resolved.findIndex((o) => deepEqual(o, current))\n const next = idx === -1 ? resolved[0] : resolved[(idx + 1) % resolved.length]\n this.set(path, next, setOpts)\n }\n\n /** Reset a single field to its default. */\n clear(path: string, options?: SetOptions): void {\n const def = deepGet(this.defaults, path)\n this.set(path, def, options)\n }\n\n /** Reset all fields to defaults; optional partial overrides + SetOptions. */\n reset(values?: Partial<T>, options?: SetOptions): void {\n if (this.disposed) return\n const next = values ? { ...this.defaults, ...values } : { ...this.defaults }\n const changed = Object.keys({ ...this.values, ...next })\n this.values = next\n this.invalidateToQueryCache()\n for (const path of changed) this.trie.notify(path)\n const writeOptions: WriteOptions | undefined =\n options?.history !== undefined ? { history: options.history } : undefined\n void this.storage.clear?.([], writeOptions)\n this.lastWritten = undefined\n }\n\n // ─── Subscriptions ────────────────────────────────────────────────────\n\n subscribe(path: string, listener: () => void): () => void {\n return this.trie.subscribe(path, listener)\n }\n\n // ─── URL helpers ──────────────────────────────────────────────────────\n\n toQuery(overrides?: Partial<T>): string {\n const effective = overrides\n ? { ...(this.values as Record<string, unknown>), ...(overrides as Record<string, unknown>) }\n : (this.values as Record<string, unknown>)\n\n if (!overrides && this.toQueryCache && this.toQueryCache.values === this.values) {\n return this.toQueryCache.query\n }\n\n const params = new URLSearchParams()\n const keys = Object.keys(effective).sort()\n for (const key of keys) {\n const value = effective[key]\n if (value === undefined || value === null) continue\n const config = this.fieldConfigs[key]\n const def = deepGet(this.defaults, key)\n if (config?.omitWhenDefault && deepEqual(value, def)) continue\n params.set(key, defaultSerialize(value))\n }\n\n const str = params.toString()\n const out = str ? `?${str}` : ''\n if (!overrides) this.toQueryCache = { values: this.values, query: out }\n return out\n }\n\n href(overrides?: Partial<T>): string {\n // Memoize the no-overrides case alongside toQuery's cache. Pathname is\n // read live (could change via routing within a page), so the cache key\n // includes (values, pathname). With overrides, no caching — fresh string.\n if (!overrides) {\n const query = this.toQuery()\n // toQuery() refreshed the cache for `this.values`; safe to extend it.\n const pathname = isClient ? window.location.pathname : ''\n const cached = this.toQueryCache\n if (cached && cached.values === this.values && cached.href !== undefined) {\n // Confirm pathname hasn't changed — if it has, recompute.\n const expected = pathname + query\n if (cached.href === expected) return cached.href\n }\n const out = pathname + query\n if (cached && cached.values === this.values) cached.href = out\n return out\n }\n const pathname = isClient ? window.location.pathname : ''\n return pathname + this.toQuery(overrides)\n }\n\n // ─── Disposal ─────────────────────────────────────────────────────────\n\n dispose(): void {\n if (this.disposed) return\n this.disposed = true\n this.storageUnsubscribe?.()\n this.storageUnsubscribe = undefined\n }\n\n // ─── Devtools / debugging escape hatch (v0.5) ─────────────────────────\n\n /**\n * Devtools / debugging escape hatch. Returns a frozen, defensive-copied\n * snapshot of all internal state. Pure read — no side effects, no\n * subscriptions registered.\n *\n * Uses `structuredClone` for the values + defaults deep-copy. Because\n * params values round-trip through storage backends, they're already\n * restricted to JSON-safe shapes — so `__introspect()` is safer here than\n * on `FormStore`. Still: marked unstable via the `__` prefix; field set\n * may grow over releases.\n */\n __introspect(): ParamsStoreIntrospection<T> {\n const specTypes: Record<string, 'zod' | 'standard-schema' | 'plain'> = {}\n for (const [path, spec] of Object.entries(this.spec)) {\n if (isPlainSpec(spec)) {\n specTypes[path] = 'plain'\n continue\n }\n // biome-ignore lint/suspicious/noExplicitAny: Zod internal `_def` API\n if ((spec as any)?._def) {\n specTypes[path] = 'zod'\n continue\n }\n if (isStandardSchema(spec)) {\n specTypes[path] = 'standard-schema'\n continue\n }\n specTypes[path] = 'plain'\n }\n\n let valuesClone: Partial<T>\n let defaultsClone: Partial<T>\n let lastWrittenClone: Partial<T> | undefined\n try {\n valuesClone = structuredClone(this.values)\n defaultsClone = structuredClone(this.defaults)\n lastWrittenClone = this.lastWritten ? structuredClone(this.lastWritten) : undefined\n } catch (err) {\n throw new Error(\n 'ParamsStore.__introspect() failed: values contain non-cloneable data ' +\n '(functions, Symbols, DOM nodes are not allowed in params state). ' +\n `Original error: ${err instanceof Error ? err.message : String(err)}`,\n )\n }\n\n return Object.freeze({\n values: valuesClone,\n defaults: defaultsClone,\n fieldsConfigured: Object.freeze({ ...this.fieldConfigs }),\n fieldsBySpecType: Object.freeze(specTypes),\n storageErrors: Object.freeze({ ...this.storageErrors }),\n storage: Object.freeze({\n name: this.storage.name,\n clientOnly: this.storage.clientOnly === true,\n hasReadAsync: this.storage.readAsync !== undefined,\n hasSubscribe: this.storage.subscribe !== undefined,\n hasClear: this.storage.clear !== undefined,\n }),\n lastWritten: lastWrittenClone,\n })\n }\n\n // ─── Internals ────────────────────────────────────────────────────────\n\n private computeDefaults(): Partial<T> {\n const out: Record<string, unknown> = {}\n for (const [key, fieldSpec] of Object.entries(this.spec)) {\n const def = getDefault(fieldSpec)\n if (def !== undefined) out[key] = def\n }\n return out as Partial<T>\n }\n\n private hydrateFromStorage(): void {\n let raw: Partial<T> | undefined\n try {\n raw = this.storage.read()\n } catch (err) {\n // Read failure → keep defaults, record reason\n this.storageErrorMap.set('__read__', err instanceof Error ? err.message : String(err))\n return\n }\n if (!raw) return\n\n for (const [key, rawValue] of Object.entries(raw)) {\n const fieldSpec = this.spec[key]\n if (!fieldSpec) continue // unknown field — ignore\n const result = parseField(fieldSpec, rawValue)\n if (result.ok && result.value !== undefined) {\n ;(this.values as Record<string, unknown>)[key] = result.value\n } else if (result.reason) {\n this.storageErrorMap.set(key, result.reason)\n }\n }\n }\n\n private persistToStorage(changed: string[], options?: SetOptions): void {\n const filtered: Record<string, unknown> = {}\n for (const path of changed) {\n const value = (this.values as Record<string, unknown>)[path]\n const config = this.fieldConfigs[path]\n const def = deepGet(this.defaults, path)\n if (config?.omitWhenDefault && deepEqual(value, def)) {\n // Mark for clear instead of write\n filtered[path] = undefined\n } else {\n filtered[path] = value\n }\n }\n\n // Forward per-call options (history strategy) to the backend. Backends\n // ignore fields they don't recognize.\n const writeOptions: WriteOptions | undefined =\n options?.history !== undefined ? { history: options.history } : undefined\n\n this.lastWritten = { ...this.values }\n try {\n // Storage write is `void | Promise<void>`. Catch sync throws here; for\n // async rejections, attach a `.catch` so the promise doesn't surface\n // as an unhandled rejection. Either way: silent fallback to in-memory\n // state — storage drift accepted (the standard storage error contract).\n const result = this.storage.write(filtered as Partial<T>, changed, writeOptions)\n if (result && typeof (result as Promise<void>).then === 'function') {\n ;(result as Promise<void>).catch(() => undefined)\n }\n } catch {\n // Sync write failure — same silent contract.\n }\n }\n\n private onExternalChange(raw: Partial<T>): void {\n if (this.disposed) return\n // Loop prevention: drop echoes that match what we just wrote\n if (this.lastWritten && deepEqual(raw, this.lastWritten)) return\n\n const changed: string[] = []\n let next = this.values\n for (const [key, rawValue] of Object.entries(raw)) {\n const fieldSpec = this.spec[key]\n if (!fieldSpec) continue\n const result = parseField(fieldSpec, rawValue)\n const newValue = result.ok ? result.value : deepGet(this.defaults, key)\n const oldValue = deepGet(next, key)\n if (deepEqual(oldValue, newValue)) continue\n next = deepSet(next, key, newValue) as Partial<T>\n changed.push(key)\n if (!result.ok && result.reason) {\n this.storageErrorMap.set(key, result.reason)\n } else {\n this.storageErrorMap.delete(key)\n }\n }\n if (changed.length === 0) return\n this.values = next\n this.invalidateToQueryCache()\n for (const path of changed) this.trie.notify(path)\n }\n\n private invalidateToQueryCache(): void {\n this.toQueryCache = undefined\n }\n}\n","import { warn } from './dev'\nimport { ParamsStore } from './params-store'\nimport type { ParamsDefinition } from './types'\n\ninterface CacheEntry<T> {\n store: ParamsStore<T>\n refCount: number\n pendingDispose?: ReturnType<typeof queueMicrotask> | null\n // Microtask scheduled disposal flag — `true` = a release-triggered disposal\n // is queued; `acquire()` cancels it by clearing this and incrementing\n // refCount.\n disposalQueued: boolean\n}\n\nconst cache = new WeakMap<ParamsDefinition, CacheEntry<unknown>>()\nconst seenNames = new Map<string, ParamsDefinition>()\n\n/**\n * Acquire (or reuse) the cached store for a definition. Increments the\n * reference count; the store is disposed only when the last subscriber\n * releases it (and the microtask-deferred disposal isn't preempted by\n * another acquire — Strict Mode safety).\n */\nexport function acquire<T>(def: ParamsDefinition<T>): ParamsStore<T> {\n warnOnDuplicateName(def)\n let entry = cache.get(def) as CacheEntry<T> | undefined\n if (!entry) {\n entry = {\n store: new ParamsStore<T>(def),\n refCount: 0,\n disposalQueued: false,\n }\n cache.set(def, entry as CacheEntry<unknown>)\n } else {\n // Cancel any pending disposal — Strict Mode and rapid mount/unmount/remount\n entry.disposalQueued = false\n }\n entry.refCount++\n return entry.store\n}\n\n/**\n * Release a previously-acquired store. When the last subscriber releases,\n * disposal is **scheduled in a microtask** — if a fresh `acquire()` arrives\n * before the microtask runs (Strict Mode dev double-invoke), the disposal\n * is cancelled and the store survives.\n */\nexport function release<T>(def: ParamsDefinition<T>): void {\n const entry = cache.get(def) as CacheEntry<T> | undefined\n if (!entry) return\n entry.refCount--\n if (entry.refCount > 0) return\n\n entry.disposalQueued = true\n queueMicrotask(() => {\n if (!entry.disposalQueued) return // cancelled by acquire()\n entry.disposalQueued = false\n disposeAndClear(def, entry)\n })\n}\n\nfunction disposeAndClear<T>(def: ParamsDefinition<T>, entry: CacheEntry<T>): void {\n entry.store.dispose()\n cache.delete(def)\n if (def.name !== undefined && seenNames.get(def.name) === def) {\n seenNames.delete(def.name)\n }\n}\n\n/**\n * Public, no-React imperative read of the cached store. The store lives\n * for the program lifetime once acquired (no ref counting). Use\n * `releaseParamsStore(def)` for explicit cleanup in long-running non-React\n * contexts (daemons, electron apps, server processes).\n *\n * Not safe for shared-process SSR contexts — see plan §SSR.\n */\nexport function getCachedStore<T>(def: ParamsDefinition<T>): ParamsStore<T> {\n let entry = cache.get(def) as CacheEntry<T> | undefined\n if (!entry) {\n entry = {\n store: new ParamsStore<T>(def),\n refCount: 0,\n disposalQueued: false,\n }\n cache.set(def, entry as CacheEntry<unknown>)\n }\n return entry.store\n}\n\n/** Explicit cleanup for non-React long-running apps. */\nexport function releaseCachedStore<T>(def: ParamsDefinition<T>): void {\n const entry = cache.get(def) as CacheEntry<T> | undefined\n if (!entry) {\n // Cache entry already gone (e.g., microtask-deferred React release ran\n // first), but the name registration may still be live. Clear it so the\n // same name can be reused without a spurious duplicate-name warning.\n if (def.name !== undefined && seenNames.get(def.name) === def) {\n seenNames.delete(def.name)\n }\n return\n }\n disposeAndClear(def, entry)\n}\n\nfunction warnOnDuplicateName(def: ParamsDefinition): void {\n if (def.name === undefined) return\n const existing = seenNames.get(def.name)\n if (existing !== undefined && existing !== def) {\n warn(\n `Duplicate definition name '${def.name}' detected. Names must be unique across definitions for v0.2 SSR snapshot keys.`,\n )\n return\n }\n seenNames.set(def.name, def)\n}\n"],"mappings":"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","names":["pathname"]}
1
+ {"version":3,"sources":["../../src/integrations/forms/index.ts","../../src/dev.ts","../../../utils/src/cascade.ts","../../../utils/src/deep.ts","../../../utils/src/path-trie.ts","../../src/name-registry.ts","../../src/schema.ts","../../src/params-store.ts","../../src/store-cache.ts"],"sourcesContent":["import type { SyncAdapter } from '@victorylabs/forms'\n\nimport { acquire, release } from '../../store-cache'\nimport type { ParamsDefinition } from '../../types'\n\n/**\n * Bridge a `@victorylabs/params` definition into `@victorylabs/forms` as a\n * `SyncAdapter`. Use when a single source of truth (the params definition)\n * should drive both:\n * - filter/view-state UI via `useParams(def)`, and\n * - a form (e.g. \"save filter view\") that writes to the same external state\n * via `defineForm({ sync: [paramsToFormSync(def)] })`.\n *\n * The bridge calls `acquire(def)` from the params store cache, so the form\n * shares state with any concurrent `useParams(def)` callers. On dispose\n * (forms unmounts the adapter via `subscribe`'s cleanup), the bridge calls\n * `release(def)`. Standard ref counting; Strict Mode safe.\n *\n * The form's resolver remains the source of truth for validation messages.\n * Params' per-field schemas are only used for storage parsing (silent\n * fallback). See plan §\"Error messages\" for rationale.\n */\nexport function paramsToFormSync<T extends Record<string, unknown> = Record<string, unknown>>(\n def: ParamsDefinition<T>,\n): SyncAdapter<T> {\n // Whether the underlying storage writes to the URL — forms uses this to\n // detect dual URL writers and throw at form-creation time.\n const writesUrl = def.storage.name === 'urlStorage'\n\n // Acquire lazily on first read/subscribe; release in subscribe's cleanup.\n // For read-only forms (no subscribe), the consumer is responsible for\n // calling releaseParamsStore(def) — same as any non-React getParamsStore use.\n let acquired = false\n const ensure = (): ReturnType<typeof acquire<T>> => {\n if (!acquired) {\n acquired = true\n return acquire(def)\n }\n return acquire(def)\n }\n\n return {\n name: `paramsToFormSync(${def.name ?? def.storage.name})`,\n fields: '*',\n clientOnly: def.storage.clientOnly,\n ...(writesUrl && { __writesUrl: true }),\n\n read() {\n const store = acquire(def)\n acquired = true\n const values = { ...store.getValues() } as Partial<T>\n return values\n },\n\n write(values) {\n const store = acquire(def)\n acquired = true\n store.set(values as Partial<T>)\n },\n\n subscribe(callback) {\n const store = ensure()\n const unsubscribe = store.subscribe('', () => {\n callback({ ...store.getValues() } as Partial<T>)\n })\n return () => {\n unsubscribe()\n if (acquired) {\n release(def)\n acquired = false\n }\n }\n },\n\n clear(paths) {\n const store = acquire(def)\n acquired = true\n if (paths.length === 0) {\n store.reset()\n } else {\n for (const path of paths) {\n store.clear(path)\n }\n }\n },\n }\n}\n","// Bundlers (tsup, vite, webpack) replace `process.env.NODE_ENV` at build time.\n// We declare `process` minimally so TypeScript doesn't require @types/node.\n\ndeclare const process:\n | {\n env?: {\n NODE_ENV?: string\n }\n }\n | undefined\n\nconst isDev = ((): boolean => {\n try {\n return typeof process !== 'undefined' && process?.env?.NODE_ENV !== 'production'\n } catch {\n return false\n }\n})()\n\nconst warned = new Set<string>()\n\n/**\n * Emit a one-time dev-mode console warning. Identical messages are\n * suppressed after the first occurrence to avoid log spam.\n */\nexport function warn(message: string): void {\n if (!isDev) return\n if (warned.has(message)) return\n warned.add(message)\n console.warn(`[@victorylabs/params] ${message}`)\n}\n","/**\n * Cascade resolver for `dependsOn` / `excludeDeps` / `onDepChange` field configs.\n * Used by both `@victorylabs/forms` and `@victorylabs/params` v0.2.\n *\n * Pure function — no side effects, no I/O. Consumers wire the result into their\n * own commit phase (params: PathTrie notify; forms: notify + revalidate + dirty/error reset).\n *\n * Algorithm: BFS over the dependency graph.\n * - Each round computes the next layer of cascaded changes.\n * - Visited paths are tracked to detect cycles (warn + skip re-entry).\n * - Depth cap stops runaway chains at 10 levels.\n *\n * The `'*'` wildcard for `dependsOn` is resolved at cascade time (not config\n * time), so adding fields to a definition doesn't invalidate `'*'` consumers.\n */\n\nconst DEPTH_CAP = 10\n\nexport interface CascadeFieldConfig {\n /**\n * Paths whose changes trigger this field's `onDepChange`. `'*'` means\n * \"every other field\"; combine with `excludeDeps` to carve out exceptions.\n */\n dependsOn?: string[] | '*'\n\n /**\n * Paths to exclude from the `'*'` wildcard. Only meaningful when\n * `dependsOn === '*'`. Implicitly always includes the field itself\n * (a field never depends on itself).\n */\n excludeDeps?: string[]\n\n /**\n * What to do when a dep changes. `'reset'` restores the field's default;\n * `'clear'` sets it to `undefined`; a function returns the new value.\n * The function form receives ALL dep values (changed + unchanged) keyed\n * by path, plus the field's own current value.\n */\n onDepChange?: 'reset' | 'clear' | ((deps: Record<string, unknown>, current: unknown) => unknown)\n}\n\nexport interface CascadeContext {\n /** Per-field config keyed by path. Fields without cascade config are inert. */\n fieldConfigs: Record<string, CascadeFieldConfig>\n\n /** Default values keyed by path — resolves `onDepChange === 'reset'`. */\n defaults: Record<string, unknown>\n\n /** Current values keyed by path — resolves the function-form `onDepChange`. */\n currentValues: Record<string, unknown>\n\n /** Every path in the definition — resolves `'*'` wildcard. */\n allPaths: string[]\n}\n\nexport interface CascadeResult {\n /** Initial changes plus all cascaded changes, keyed by path. */\n changes: Record<string, unknown>\n\n /** Cycle-detection / depth-cap warnings (one entry per anomaly). */\n warnings: string[]\n}\n\n/**\n * Resolve a cascade. Given a set of initial changes, compute every dependent\n * field that should also change.\n */\nexport function resolveCascade(\n initialChanges: Record<string, unknown>,\n ctx: CascadeContext,\n): CascadeResult {\n const changes: Record<string, unknown> = { ...initialChanges }\n const warnings: string[] = []\n\n // Working values during BFS — start from current, override with initial changes.\n // Each round's cascaded changes accumulate here so dependent fields see them.\n const working: Record<string, unknown> = { ...ctx.currentValues, ...initialChanges }\n\n // Seed: paths changed in this round become the next round's triggers.\n let frontier = new Set(Object.keys(initialChanges))\n // Cycle guard: if a path is re-targeted within the same cascade, warn + skip.\n const visited = new Set<string>(frontier)\n\n let depth = 0\n while (frontier.size > 0) {\n if (depth >= DEPTH_CAP) {\n warnings.push(\n `cascade depth cap (${DEPTH_CAP}) reached; stopping. Frontier: [${[...frontier].join(', ')}]. Likely a config error — check for accidental long chains.`,\n )\n break\n }\n\n const nextFrontier = new Set<string>()\n\n for (const targetPath of ctx.allPaths) {\n const config = ctx.fieldConfigs[targetPath]\n if (!config?.onDepChange) continue\n if (frontier.has(targetPath)) continue // a path doesn't cascade to itself\n\n const deps = resolveDeps(config, ctx.allPaths, targetPath)\n // Did any of this field's deps change in the current frontier?\n const changedDeps = deps.filter((d) => frontier.has(d))\n if (changedDeps.length === 0) continue\n\n if (visited.has(targetPath)) {\n warnings.push(\n `cycle detected: '${targetPath}' would cascade twice (triggered by [${changedDeps.join(', ')}]). Skipping re-entry.`,\n )\n continue\n }\n\n // Compute the new value via onDepChange.\n const newValue = applyDepChange(config.onDepChange, deps, working, ctx.defaults, targetPath)\n\n // Skip no-op cascades: if the resolved value equals the field's current\n // working value, don't propagate further (avoids spurious re-renders).\n if (Object.is(newValue, working[targetPath])) continue\n\n changes[targetPath] = newValue\n working[targetPath] = newValue\n visited.add(targetPath)\n nextFrontier.add(targetPath)\n }\n\n frontier = nextFrontier\n depth++\n }\n\n return { changes, warnings }\n}\n\n/**\n * Resolve a field's effective dependency list. `'*'` expands to every OTHER\n * path minus `excludeDeps` and the field itself.\n */\nfunction resolveDeps(config: CascadeFieldConfig, allPaths: string[], selfPath: string): string[] {\n if (config.dependsOn === '*') {\n const exclude = new Set(config.excludeDeps ?? [])\n exclude.add(selfPath) // a field doesn't depend on itself\n return allPaths.filter((p) => !exclude.has(p))\n }\n if (Array.isArray(config.dependsOn)) {\n // Filter out self-references defensively (config error otherwise).\n return config.dependsOn.filter((p) => p !== selfPath)\n }\n return []\n}\n\n/**\n * Compute the new value for a cascaded field via its `onDepChange` config.\n */\nfunction applyDepChange(\n onDepChange: NonNullable<CascadeFieldConfig['onDepChange']>,\n deps: string[],\n working: Record<string, unknown>,\n defaults: Record<string, unknown>,\n targetPath: string,\n): unknown {\n if (onDepChange === 'reset') return defaults[targetPath]\n if (onDepChange === 'clear') return undefined\n\n // Function form: build the deps record from the working values\n // (so cascaded changes within the same set() call are visible).\n const depsRecord: Record<string, unknown> = {}\n for (const d of deps) {\n depsRecord[d] = working[d]\n }\n return onDepChange(depsRecord, working[targetPath])\n}\n","/**\n * Path-aware deep helpers. Paths are dot-strings: 'address.street', 'tags.2'.\n * Numeric segments index into arrays; string segments index into objects.\n *\n * Limitation: keys containing literal '.' are not supported. Document this\n * for consumers; forms with such keys are vanishingly rare.\n */\n\nexport function splitPath(path: string): string[] {\n if (path === '') return []\n return path.split('.')\n}\n\nexport function deepGet(obj: unknown, path: string): unknown {\n const segments = splitPath(path)\n let current: unknown = obj\n for (const seg of segments) {\n if (current === null || current === undefined) return undefined\n if (Array.isArray(current)) {\n const idx = Number(seg)\n if (!Number.isInteger(idx) || idx < 0) return undefined\n current = current[idx]\n } else if (typeof current === 'object') {\n current = (current as Record<string, unknown>)[seg]\n } else {\n return undefined\n }\n }\n return current\n}\n\nexport function deepSet<T>(obj: T, path: string, value: unknown): T {\n const segments = splitPath(path)\n if (segments.length === 0) return value as T\n return setRecursive(obj, segments, 0, value) as T\n}\n\nfunction setRecursive(current: unknown, segments: string[], i: number, value: unknown): unknown {\n const seg = segments[i]\n // Sanity: segments.length > i always when this runs (caller ensures).\n if (seg === undefined) return value\n const isLast = i === segments.length - 1\n const idx = Number(seg)\n const isNumericSeg = Number.isInteger(idx) && idx >= 0 && /^\\d+$/.test(seg)\n\n if (isNumericSeg) {\n const arr = Array.isArray(current) ? current.slice() : []\n arr[idx] = isLast ? value : setRecursive(arr[idx], segments, i + 1, value)\n return arr\n }\n\n const next: Record<string, unknown> =\n current && typeof current === 'object' && !Array.isArray(current)\n ? { ...(current as Record<string, unknown>) }\n : {}\n next[seg] = isLast ? value : setRecursive(next[seg], segments, i + 1, value)\n return next\n}\n\nexport function deepEqual(a: unknown, b: unknown): boolean {\n if (Object.is(a, b)) return true\n if (typeof a !== typeof b) return false\n if (a === null || b === null) return false\n if (typeof a !== 'object') return false\n\n if (Array.isArray(a)) {\n if (!Array.isArray(b)) return false\n if (a.length !== b.length) return false\n for (let i = 0; i < a.length; i++) {\n if (!deepEqual(a[i], b[i])) return false\n }\n return true\n }\n if (Array.isArray(b)) return false\n\n if (a instanceof Date) {\n return b instanceof Date && a.getTime() === b.getTime()\n }\n if (b instanceof Date) return false\n\n if (a instanceof RegExp) {\n return b instanceof RegExp && a.source === b.source && a.flags === b.flags\n }\n if (b instanceof RegExp) return false\n\n const aKeys = Object.keys(a as Record<string, unknown>)\n const bKeys = Object.keys(b as Record<string, unknown>)\n if (aKeys.length !== bKeys.length) return false\n for (const key of aKeys) {\n if (!Object.hasOwn(b as object, key)) return false\n if (!deepEqual((a as Record<string, unknown>)[key], (b as Record<string, unknown>)[key])) {\n return false\n }\n }\n return true\n}\n\n/**\n * Deep merge: later sources overwrite earlier ones at scalar/array level;\n * objects merge recursively. Arrays are replaced wholesale (no concatenation),\n * which matches the form-state mental model — an array IS the value.\n */\nexport function deepMerge<T extends object>(...sources: Array<Partial<T> | undefined>): T {\n const result = {} as Record<string, unknown>\n for (const source of sources) {\n if (!source) continue\n for (const [key, value] of Object.entries(source)) {\n const existing = result[key]\n if (\n existing &&\n value &&\n typeof existing === 'object' &&\n typeof value === 'object' &&\n !Array.isArray(existing) &&\n !Array.isArray(value) &&\n !(existing instanceof Date) &&\n !(value instanceof Date)\n ) {\n result[key] = deepMerge(existing as Partial<object>, value as Partial<object>)\n } else {\n result[key] = value\n }\n }\n }\n return result as T\n}\n","import { splitPath } from './deep'\n\ninterface Node {\n listeners: Set<() => void>\n children: Map<string, Node>\n}\n\nconst makeNode = (): Node => ({ listeners: new Set(), children: new Map() })\n\n/**\n * Path-scoped subscription bus.\n *\n * Notification semantics for `notify(path)`:\n * - Subscribers AT `path` fire (exact match).\n * - Subscribers at every ANCESTOR of `path` fire (a parent observing the\n * aggregated subtree should re-render when any descendant changes).\n * - Subscribers at every DESCENDANT of `path` fire (a subtree replacement\n * at `path` invalidates any leaf below it).\n * - Sibling-branch subscribers do NOT fire.\n *\n * notify('') notifies the root + every subscriber in the trie.\n */\nexport class PathTrie {\n private readonly root: Node = makeNode()\n\n subscribe(path: string, listener: () => void): () => void {\n const node = this.ensure(path)\n node.listeners.add(listener)\n return () => {\n node.listeners.delete(listener)\n }\n }\n\n notify(path: string): void {\n const segments = splitPath(path)\n\n let current: Node = this.root\n fire(current)\n\n for (const seg of segments) {\n const next = current.children.get(seg)\n if (!next) return\n current = next\n fire(current)\n }\n\n fireSubtree(current)\n }\n\n /** Test/diagnostic helper — total subscriber count below a given path. */\n size(path = ''): number {\n const node = this.find(path)\n if (!node) return 0\n return countSubtree(node)\n }\n\n private ensure(path: string): Node {\n let node = this.root\n for (const seg of splitPath(path)) {\n let child = node.children.get(seg)\n if (!child) {\n child = makeNode()\n node.children.set(seg, child)\n }\n node = child\n }\n return node\n }\n\n private find(path: string): Node | undefined {\n let node: Node | undefined = this.root\n for (const seg of splitPath(path)) {\n node = node?.children.get(seg)\n if (!node) return undefined\n }\n return node\n }\n}\n\nfunction fire(node: Node): void {\n for (const listener of node.listeners) listener()\n}\n\nfunction fireSubtree(node: Node): void {\n for (const child of node.children.values()) {\n fire(child)\n fireSubtree(child)\n }\n}\n\nfunction countSubtree(node: Node): number {\n let total = node.listeners.size\n for (const child of node.children.values()) {\n total += countSubtree(child)\n }\n return total\n}\n","import type { ParamsDefinition } from './types'\n\n/**\n * Side-effect registry of all definitions that carry a `name`. Populated by\n * `defineParams()` at construction time so `paramsSnapshot()` and\n * `hydrateParams()` can look up defs by name.\n *\n * Last-write-wins: re-defining the same name overwrites silently (the\n * duplicate-name DEV warning is enforced separately at acquire time in\n * `store-cache.ts`).\n */\nconst nameRegistry = new Map<string, ParamsDefinition<unknown>>()\n\n/**\n * Pre-hydration cache for SSR snapshots. `hydrateParams(snapshot)` populates\n * this; `ParamsStore` checks it during initial hydration and uses the seeded\n * values instead of calling the storage backend's `read()`.\n *\n * Once consumed by a store on first hydration the entry is left in place\n * (idempotent re-hydrate) — the consumer can call `hydrateParams()` again\n * with overrides if needed before any store is constructed.\n */\nconst preHydrationCache = new Map<string, unknown>()\n\nexport function registerNamedDef<T>(def: ParamsDefinition<T>): void {\n if (def.name === undefined) return\n nameRegistry.set(def.name, def as ParamsDefinition<unknown>)\n}\n\nexport function getDefByName(name: string): ParamsDefinition<unknown> | undefined {\n return nameRegistry.get(name)\n}\n\nexport function getRegisteredDefs(): ReadonlyArray<ParamsDefinition<unknown>> {\n return Array.from(nameRegistry.values())\n}\n\nexport function setPreHydrationValues(name: string, values: unknown): void {\n preHydrationCache.set(name, values)\n}\n\nexport function takePreHydrationValues<T>(name: string | undefined): Partial<T> | undefined {\n if (name === undefined) return undefined\n if (!preHydrationCache.has(name)) return undefined\n return preHydrationCache.get(name) as Partial<T>\n}\n\n/** Test-only: clear both registries. Not exported from the package. */\nexport function _resetNameRegistry(): void {\n nameRegistry.clear()\n preHydrationCache.clear()\n}\n","import type { StandardSchemaV1 } from '@standard-schema/spec'\n\nimport type { FieldSpec, PlainFieldSpec } from './types'\n\nexport function isStandardSchema(spec: unknown): spec is StandardSchemaV1 {\n return (\n typeof spec === 'object' &&\n spec !== null &&\n '~standard' in spec &&\n typeof (spec as { '~standard'?: unknown })['~standard'] === 'object'\n )\n}\n\nexport function isPlainSpec<T>(spec: FieldSpec<T>): spec is PlainFieldSpec<T> {\n return !isStandardSchema(spec) && typeof spec === 'object' && spec !== null && 'default' in spec\n}\n\n/**\n * Resolve a field's default value.\n *\n * - Plain spec: `spec.default` (always present — required by the type).\n * - Standard Schema (Zod, Valibot, ArkType, …): parse `undefined` and use the\n * schema's `.default()` if it produced a value. Schemas without a default\n * return `undefined`.\n */\nexport function getDefault<T>(spec: FieldSpec<T>): T | undefined {\n if (isStandardSchema(spec)) {\n const result = spec['~standard'].validate(undefined)\n if (result instanceof Promise) return undefined\n if ('value' in result && !result.issues) return result.value as T\n return undefined\n }\n return (spec as PlainFieldSpec<T>).default\n}\n\n/**\n * Parse a raw storage value through the field's spec. Returns the typed value\n * on success, or `undefined` on parse failure (engine falls back to default).\n *\n * The engine records the failure reason in `ParamsStore.storageErrors` for\n * debugging; consumer code doesn't see it.\n */\nexport interface ParseResult<T> {\n ok: boolean\n value?: T\n reason?: string\n}\n\nexport function parseField<T>(spec: FieldSpec<T>, raw: unknown): ParseResult<T> {\n if (isStandardSchema(spec)) {\n const result = spec['~standard'].validate(raw)\n if (result instanceof Promise) {\n return { ok: false, reason: 'async-schema-not-supported-in-v0.1' }\n }\n if ('value' in result && !result.issues) return { ok: true, value: result.value as T }\n return { ok: false, reason: result.issues?.[0]?.message ?? 'parse-failed' }\n }\n\n const plainSpec = spec as PlainFieldSpec<T>\n if (typeof raw === 'string' && plainSpec.parse) {\n try {\n return { ok: true, value: plainSpec.parse(raw) }\n } catch (err) {\n return { ok: false, reason: err instanceof Error ? err.message : String(err) }\n }\n }\n // No custom parse: accept raw value as-is if it's a usable runtime value\n // (string, number, boolean, array, object, etc.). The schema-less plain spec\n // is mostly used for non-string-coerced flows (memory storage with raw values).\n if (raw === undefined) return { ok: false, reason: 'undefined-no-parse' }\n return { ok: true, value: raw as T }\n}\n\n/**\n * Serialize a typed value to its storage string representation. Used by URL\n * storage (every value must be a string) and other string-keyed backends.\n *\n * Plain spec uses its `serialize` if provided, else `String(value)`. Standard\n * schemas don't define an inverse — we fall back to `String(value)` (or\n * `JSON.stringify` for objects) and the consumer can override via per-field\n * `serialize` in the storage backend's options.\n */\nexport function defaultSerialize(value: unknown): string {\n if (value === undefined || value === null) return ''\n if (typeof value === 'string') return value\n if (typeof value === 'boolean') return value ? 'true' : 'false'\n if (typeof value === 'number') return Number.isFinite(value) ? String(value) : ''\n return JSON.stringify(value)\n}\n\n/**\n * Probe a schema spec and return its enum members, or `undefined` when the\n * spec doesn't expose any. Used by {@link extractEnumValues} to discover the\n * cycle values for `params.cycle(path)` calls without an explicit list.\n *\n * Built-ins ship for Zod and Valibot. Other Standard Schema libs (ArkType,\n * Effect Schema) lack a uniform introspection API — register a custom\n * extractor via {@link registerEnumExtractor} for those.\n */\nexport type EnumExtractor = (spec: unknown) => readonly unknown[] | undefined\n\nconst customExtractors = new Set<EnumExtractor>()\n\n/**\n * Register a custom enum extractor. Useful when:\n * - Your Standard Schema lib isn't covered by the built-ins (ArkType,\n * Effect Schema, …).\n * - You're using a proprietary or homegrown schema system.\n * - You need to override the built-in detection for a specific shape.\n *\n * Custom extractors run BEFORE the built-ins, so they win on overlap. Returns\n * an unregister function — capture it for clean teardown in tests or HMR.\n *\n * @example\n * ```ts\n * import { registerEnumExtractor } from '@victorylabs/params'\n * import { type } from 'arktype'\n *\n * const off = registerEnumExtractor((spec) => {\n * if (spec instanceof type.Type && spec.json?.length) {\n * return spec.json\n * .filter((node) => 'unit' in node)\n * .map((node) => node.unit)\n * }\n * return undefined\n * })\n * ```\n */\nexport function registerEnumExtractor(extractor: EnumExtractor): () => void {\n customExtractors.add(extractor)\n return () => {\n customExtractors.delete(extractor)\n }\n}\n\n/**\n * Walk Zod wrapper types (ZodDefault, ZodOptional, ZodNullable, …) to find\n * an underlying enum. `.default(...)` / `.optional()` / `.nullable()` produce\n * a wrapper whose `_def.innerType` points to the wrapped schema. Cap the walk\n * at a small depth — pathological self-referential schemas shouldn't loop.\n */\nconst extractZodEnum: EnumExtractor = (spec) => {\n // biome-ignore lint/suspicious/noExplicitAny: Zod internal `_def` API\n let current: any = spec\n for (let i = 0; i < 8 && current !== null && typeof current === 'object'; i++) {\n const def = current._def\n if (!def) return undefined\n\n // z.enum(['a', 'b']): values is already a clean string array.\n if (Array.isArray(def.values)) return def.values\n\n // z.nativeEnum(MyEnum): values is the enum object. TS numeric enums are\n // BIDIRECTIONAL — Object.values returns numbers + reverse-mapped strings:\n // enum Color { Red = 0 } → Object.values(Color) = [0, 'Red'].\n // Detect numeric and filter; pure string enums are unidirectional and\n // return only strings.\n if (typeof def.values === 'object' && def.values !== null) {\n const all = Object.values(def.values)\n const hasNumber = all.some((v) => typeof v === 'number')\n return hasNumber ? all.filter((v) => typeof v === 'number') : all\n }\n\n if (def.innerType) {\n current = def.innerType\n continue\n }\n return undefined\n }\n return undefined\n}\n\n/**\n * Walk Valibot wrapper schemas (`optional`, `nullable`, `nullish`) via the\n * `wrapped` property, then read the enum members from `picklist` / `enum`\n * schemas.\n *\n * - `v.picklist(['a','b'])` → `options: ['a','b']`.\n * - `v.enum_(MyEnum)` (or `v.enum(...)` in v1+) → `options: [...]` already\n * filtered for numeric-enum bidirectional reverse mapping by Valibot.\n *\n * Confirmed against valibot@1.3.1.\n */\nconst extractValibotEnum: EnumExtractor = (spec) => {\n // biome-ignore lint/suspicious/noExplicitAny: Valibot internal schema shape\n let current: any = spec\n for (let i = 0; i < 8 && current !== null && typeof current === 'object'; i++) {\n if (current.kind !== 'schema' || typeof current.type !== 'string') return undefined\n\n if (current.type === 'picklist' || current.type === 'enum') {\n return Array.isArray(current.options) ? current.options : undefined\n }\n\n // Wrapper schemas — `optional`, `nullable`, `nullish`, `undefinedable`.\n // Each carries a `wrapped` property pointing to the inner schema.\n if (current.wrapped) {\n current = current.wrapped\n continue\n }\n return undefined\n }\n return undefined\n}\n\nconst builtInExtractors: readonly EnumExtractor[] = [extractZodEnum, extractValibotEnum]\n\n/**\n * Extract the enum values from a field spec, if any. Returns the array of\n * enum members or `undefined` if the spec doesn't expose them.\n *\n * Built-in support:\n * - Zod `z.enum(['a', 'b'])` and `z.nativeEnum(MyEnum)`, including wrapped\n * variants (`.default()`, `.optional()`, `.nullable()`).\n * - Valibot `v.picklist(['a', 'b'])` and `v.enum_(MyEnum)`, including wrapped\n * variants (`v.optional(...)`, `v.nullable(...)`, `v.nullish(...)`).\n *\n * For other Standard Schema libs (ArkType, Effect Schema) or custom shapes,\n * register a probe via {@link registerEnumExtractor}. Custom extractors run\n * before the built-ins. When nothing matches, `cycle()` throws and asks the\n * caller to pass options explicitly.\n */\nexport function extractEnumValues(spec: unknown): readonly unknown[] | undefined {\n for (const extractor of customExtractors) {\n const result = extractor(spec)\n if (result !== undefined) return result\n }\n for (const extractor of builtInExtractors) {\n const result = extractor(spec)\n if (result !== undefined) return result\n }\n return undefined\n}\n","import { deepEqual, deepGet, deepSet, PathTrie, resolveCascade } from '@victorylabs/utils'\n\nimport { warn } from './dev'\nimport { takePreHydrationValues } from './name-registry'\nimport {\n defaultSerialize,\n extractEnumValues,\n getDefault,\n isPlainSpec,\n isStandardSchema,\n parseField,\n} from './schema'\nimport type { ParamsStorage, WriteOptions } from './storage'\nimport type { FieldConfig, FieldSpec, ParamsDefinition } from './types'\n\n/**\n * Per-call write options accepted by `set()` and `clear()`. Currently shapes\n * the URL `history` strategy override; backends ignore fields they don't\n * recognize.\n */\nexport interface SetOptions {\n /** History API override for URL-like backends. `'push'` beats `'replace'` across batched writes. */\n readonly history?: 'push' | 'replace'\n}\n\n/**\n * Frozen, defensive-copied snapshot returned by `ParamsStore.__introspect()`.\n *\n * Surface is **unstable** — fields may be added freely across releases; do\n * not depend on the type identity. Use for devtools panels, debug logging,\n * or test assertions that don't fit through the public observable API.\n */\nexport interface ParamsStoreIntrospection<T = Record<string, unknown>> {\n readonly values: Readonly<Partial<T>>\n readonly defaults: Readonly<Partial<T>>\n readonly fieldsConfigured: Readonly<Record<string, FieldConfig>>\n readonly fieldsBySpecType: Readonly<Record<string, 'zod' | 'standard-schema' | 'plain'>>\n readonly storageErrors: Readonly<Record<string, string>>\n readonly storage: {\n readonly name: string\n readonly clientOnly: boolean\n readonly hasReadAsync: boolean\n readonly hasSubscribe: boolean\n readonly hasClear: boolean\n }\n readonly lastWritten: Readonly<Partial<T>> | undefined\n}\n\nconst isClient = typeof window !== 'undefined'\n\n/**\n * Framework-agnostic store. Owns:\n * - values + storage round-tripping\n * - schema validation on read (silent fallback to defaults)\n * - PathTrie-based subscriptions\n * - all state-mutation helpers (set, toggle, append, …)\n * - loop prevention against external storage echoes\n * - memoized toQuery\n *\n * The React adapter (`@victorylabs/params/react`) wraps this with\n * useSyncExternalStore-style hooks; non-React consumers use the methods\n * directly via `getParamsStore(def)`.\n */\nexport class ParamsStore<T = Record<string, unknown>> {\n private readonly spec: Readonly<Record<string, FieldSpec>>\n private readonly storage: ParamsStorage<T>\n private readonly fieldConfigs: Readonly<Record<string, FieldConfig>>\n\n private values: Partial<T>\n private readonly defaults: Partial<T>\n private readonly trie = new PathTrie()\n private readonly storageErrorMap = new Map<string, string>()\n\n private lastWritten: Partial<T> | undefined\n private storageUnsubscribe: (() => void) | undefined\n private toQueryCache: { values: Partial<T>; query: string; href?: string } | undefined\n private disposed = false\n\n constructor(def: ParamsDefinition<T>) {\n this.spec = def.spec\n this.storage = def.storage\n this.fieldConfigs = def.fields\n\n this.defaults = this.computeDefaults()\n this.values = { ...this.defaults }\n\n // SSR pre-hydration: if hydrateParams() seeded values for this def's name,\n // those win over the storage backend's read() — the snapshot represents\n // the authoritative server-rendered state.\n const seeded = takePreHydrationValues<T>(def.name)\n if (seeded !== undefined) {\n this.values = { ...this.defaults, ...seeded }\n } else if (this.storage.clientOnly && !isClient) {\n // Server: skip read; values stay at defaults\n } else {\n this.hydrateFromStorage()\n }\n\n if (this.storage.subscribe) {\n this.storageUnsubscribe = this.storage.subscribe((raw) => this.onExternalChange(raw))\n }\n }\n\n // ─── Reads (synchronous, framework-agnostic) ──────────────────────────\n\n getValues(): Readonly<Partial<T>> {\n return this.values\n }\n\n getValue<P extends string>(path: P): unknown {\n return deepGet(this.values, path)\n }\n\n /** Storage parse failures discovered on hydrate or external change. */\n get storageErrors(): Readonly<Record<string, string>> {\n const out: Record<string, string> = {}\n for (const [path, reason] of this.storageErrorMap) out[path] = reason\n return out\n }\n\n /** Per-field config (for the React adapter to read debounce settings, etc.). */\n getFieldConfig(path: string): FieldConfig | undefined {\n return this.fieldConfigs[path]\n }\n\n // ─── Writes ───────────────────────────────────────────────────────────\n\n set(path: string, value: unknown, options?: SetOptions): void\n set(partial: Partial<T>, options?: SetOptions): void\n set(\n pathOrPartial: string | Partial<T>,\n valueOrOptions?: unknown,\n maybeOptions?: SetOptions,\n ): void {\n if (this.disposed) return\n\n let updates: Record<string, unknown>\n let options: SetOptions | undefined\n if (typeof pathOrPartial === 'string') {\n updates = { [pathOrPartial]: valueOrOptions }\n options = maybeOptions\n } else {\n updates = pathOrPartial as Record<string, unknown>\n options = valueOrOptions as SetOptions | undefined\n }\n\n // First pass: filter out no-op updates (value already deep-equals current).\n const initialChanges: Record<string, unknown> = {}\n for (const [path, v] of Object.entries(updates)) {\n const old = deepGet(this.values, path)\n if (deepEqual(old, v)) continue\n initialChanges[path] = v\n }\n if (Object.keys(initialChanges).length === 0) return\n\n // Resolve cascading dependencies (no-op when no field has dependsOn).\n const cascade = resolveCascade(initialChanges, {\n fieldConfigs: this.fieldConfigs,\n defaults: this.defaults as Record<string, unknown>,\n currentValues: this.values as Record<string, unknown>,\n allPaths: Object.keys(this.spec),\n })\n\n for (const w of cascade.warnings) warn(w)\n\n // Second pass: apply initial + cascaded changes atomically.\n const changed: string[] = []\n let next = this.values\n for (const [path, v] of Object.entries(cascade.changes)) {\n const old = deepGet(next, path)\n if (deepEqual(old, v)) continue\n next = deepSet(next, path, v) as Partial<T>\n changed.push(path)\n }\n if (changed.length === 0) return\n\n this.values = next\n this.invalidateToQueryCache()\n\n // Notify subscribers (React 18 batches these into one render per consumer)\n for (const path of changed) this.trie.notify(path)\n\n // Persist to storage with omitWhenDefault filtering. Per-call options\n // (history strategy) flow straight through to storage.write.\n this.persistToStorage(changed, options)\n }\n\n /** Boolean-flip helper. */\n toggle(path: string, options?: SetOptions): void {\n const current = this.getValue(path)\n this.set(path, !current, options)\n }\n\n /** Push a value onto an array field. */\n append(path: string, value: unknown, options?: SetOptions): void {\n const current = this.getValue(path)\n if (!Array.isArray(current)) return\n this.set(path, [...current, value], options)\n }\n\n /** Remove the first array element matching `value` by deepEqual. */\n remove(path: string, value: unknown, options?: SetOptions): void {\n const current = this.getValue(path)\n if (!Array.isArray(current)) return\n const idx = current.findIndex((item) => deepEqual(item, value))\n if (idx === -1) return\n this.set(path, [...current.slice(0, idx), ...current.slice(idx + 1)], options)\n }\n\n /** Remove the array element at the given index. */\n removeAt(path: string, index: number, options?: SetOptions): void {\n const current = this.getValue(path)\n if (!Array.isArray(current)) return\n if (index < 0 || index >= current.length) return\n this.set(path, [...current.slice(0, index), ...current.slice(index + 1)], options)\n }\n\n /**\n * Cycle the value through the given values (e.g., 'asc' → 'desc' → 'asc').\n * If the current value isn't in the values, jumps to the first one.\n *\n * v0.4: when called without an explicit values array, derives the rotation\n * from the field's Zod `z.enum` / `z.nativeEnum` schema. Throws if the schema\n * doesn't expose enum metadata.\n *\n * v0.5: optional `SetOptions` arg (history strategy) on every overload.\n */\n cycle(path: string, options?: SetOptions): void\n cycle(path: string, values: ReadonlyArray<unknown>, options?: SetOptions): void\n cycle(\n path: string,\n valuesOrOptions?: ReadonlyArray<unknown> | SetOptions,\n maybeOptions?: SetOptions,\n ): void {\n // Disambiguate the second argument: a ReadonlyArray means explicit values;\n // a non-array object means SetOptions; undefined means \"derive from schema\".\n let resolved: ReadonlyArray<unknown> | undefined\n let setOpts: SetOptions | undefined\n if (Array.isArray(valuesOrOptions)) {\n resolved = valuesOrOptions\n setOpts = maybeOptions\n } else {\n resolved = undefined\n setOpts = valuesOrOptions as SetOptions | undefined\n }\n\n if (resolved === undefined) {\n const fieldSpec = this.spec[path]\n const derived = fieldSpec !== undefined ? extractEnumValues(fieldSpec) : undefined\n if (!derived || derived.length === 0) {\n throw new Error(\n `Cannot derive enum values for params field '${path}'; pass values explicitly to cycle().`,\n )\n }\n resolved = derived\n }\n if (resolved.length === 0) return\n const current = this.getValue(path)\n const idx = resolved.findIndex((o) => deepEqual(o, current))\n const next = idx === -1 ? resolved[0] : resolved[(idx + 1) % resolved.length]\n this.set(path, next, setOpts)\n }\n\n /** Reset a single field to its default. */\n clear(path: string, options?: SetOptions): void {\n const def = deepGet(this.defaults, path)\n this.set(path, def, options)\n }\n\n /** Reset all fields to defaults; optional partial overrides + SetOptions. */\n reset(values?: Partial<T>, options?: SetOptions): void {\n if (this.disposed) return\n const next = values ? { ...this.defaults, ...values } : { ...this.defaults }\n const changed = Object.keys({ ...this.values, ...next })\n this.values = next\n this.invalidateToQueryCache()\n for (const path of changed) this.trie.notify(path)\n const writeOptions: WriteOptions | undefined =\n options?.history !== undefined ? { history: options.history } : undefined\n void this.storage.clear?.([], writeOptions)\n this.lastWritten = undefined\n }\n\n // ─── Subscriptions ────────────────────────────────────────────────────\n\n subscribe(path: string, listener: () => void): () => void {\n return this.trie.subscribe(path, listener)\n }\n\n // ─── URL helpers ──────────────────────────────────────────────────────\n\n toQuery(overrides?: Partial<T>): string {\n const effective = overrides\n ? { ...(this.values as Record<string, unknown>), ...(overrides as Record<string, unknown>) }\n : (this.values as Record<string, unknown>)\n\n if (!overrides && this.toQueryCache && this.toQueryCache.values === this.values) {\n return this.toQueryCache.query\n }\n\n const params = new URLSearchParams()\n const keys = Object.keys(effective).sort()\n for (const key of keys) {\n const value = effective[key]\n if (value === undefined || value === null) continue\n const config = this.fieldConfigs[key]\n const def = deepGet(this.defaults, key)\n if (config?.omitWhenDefault && deepEqual(value, def)) continue\n params.set(key, defaultSerialize(value))\n }\n\n const str = params.toString()\n const out = str ? `?${str}` : ''\n if (!overrides) this.toQueryCache = { values: this.values, query: out }\n return out\n }\n\n href(overrides?: Partial<T>): string {\n // Memoize the no-overrides case alongside toQuery's cache. Pathname is\n // read live (could change via routing within a page), so the cache key\n // includes (values, pathname). With overrides, no caching — fresh string.\n if (!overrides) {\n const query = this.toQuery()\n // toQuery() refreshed the cache for `this.values`; safe to extend it.\n const pathname = isClient ? window.location.pathname : ''\n const cached = this.toQueryCache\n if (cached && cached.values === this.values && cached.href !== undefined) {\n // Confirm pathname hasn't changed — if it has, recompute.\n const expected = pathname + query\n if (cached.href === expected) return cached.href\n }\n const out = pathname + query\n if (cached && cached.values === this.values) cached.href = out\n return out\n }\n const pathname = isClient ? window.location.pathname : ''\n return pathname + this.toQuery(overrides)\n }\n\n // ─── Disposal ─────────────────────────────────────────────────────────\n\n dispose(): void {\n if (this.disposed) return\n this.disposed = true\n this.storageUnsubscribe?.()\n this.storageUnsubscribe = undefined\n }\n\n // ─── Devtools / debugging escape hatch (v0.5) ─────────────────────────\n\n /**\n * Devtools / debugging escape hatch. Returns a frozen, defensive-copied\n * snapshot of all internal state. Pure read — no side effects, no\n * subscriptions registered.\n *\n * Uses `structuredClone` for the values + defaults deep-copy. Because\n * params values round-trip through storage backends, they're already\n * restricted to JSON-safe shapes — so `__introspect()` is safer here than\n * on `FormStore`. Still: marked unstable via the `__` prefix; field set\n * may grow over releases.\n */\n __introspect(): ParamsStoreIntrospection<T> {\n const specTypes: Record<string, 'zod' | 'standard-schema' | 'plain'> = {}\n for (const [path, spec] of Object.entries(this.spec)) {\n if (isPlainSpec(spec)) {\n specTypes[path] = 'plain'\n continue\n }\n // biome-ignore lint/suspicious/noExplicitAny: Zod internal `_def` API\n if ((spec as any)?._def) {\n specTypes[path] = 'zod'\n continue\n }\n if (isStandardSchema(spec)) {\n specTypes[path] = 'standard-schema'\n continue\n }\n specTypes[path] = 'plain'\n }\n\n let valuesClone: Partial<T>\n let defaultsClone: Partial<T>\n let lastWrittenClone: Partial<T> | undefined\n try {\n valuesClone = structuredClone(this.values)\n defaultsClone = structuredClone(this.defaults)\n lastWrittenClone = this.lastWritten ? structuredClone(this.lastWritten) : undefined\n } catch (err) {\n throw new Error(\n 'ParamsStore.__introspect() failed: values contain non-cloneable data ' +\n '(functions, Symbols, DOM nodes are not allowed in params state). ' +\n `Original error: ${err instanceof Error ? err.message : String(err)}`,\n )\n }\n\n return Object.freeze({\n values: valuesClone,\n defaults: defaultsClone,\n fieldsConfigured: Object.freeze({ ...this.fieldConfigs }),\n fieldsBySpecType: Object.freeze(specTypes),\n storageErrors: Object.freeze({ ...this.storageErrors }),\n storage: Object.freeze({\n name: this.storage.name,\n clientOnly: this.storage.clientOnly === true,\n hasReadAsync: this.storage.readAsync !== undefined,\n hasSubscribe: this.storage.subscribe !== undefined,\n hasClear: this.storage.clear !== undefined,\n }),\n lastWritten: lastWrittenClone,\n })\n }\n\n // ─── Internals ────────────────────────────────────────────────────────\n\n private computeDefaults(): Partial<T> {\n const out: Record<string, unknown> = {}\n for (const [key, fieldSpec] of Object.entries(this.spec)) {\n const def = getDefault(fieldSpec)\n if (def !== undefined) out[key] = def\n }\n return out as Partial<T>\n }\n\n private hydrateFromStorage(): void {\n let raw: Partial<T> | undefined\n try {\n raw = this.storage.read()\n } catch (err) {\n // Read failure → keep defaults, record reason\n this.storageErrorMap.set('__read__', err instanceof Error ? err.message : String(err))\n return\n }\n if (!raw) return\n\n for (const [key, rawValue] of Object.entries(raw)) {\n const fieldSpec = this.spec[key]\n if (!fieldSpec) continue // unknown field — ignore\n const result = parseField(fieldSpec, rawValue)\n if (result.ok && result.value !== undefined) {\n ;(this.values as Record<string, unknown>)[key] = result.value\n } else if (result.reason) {\n this.storageErrorMap.set(key, result.reason)\n }\n }\n }\n\n private persistToStorage(changed: string[], options?: SetOptions): void {\n const filtered: Record<string, unknown> = {}\n for (const path of changed) {\n const value = (this.values as Record<string, unknown>)[path]\n const config = this.fieldConfigs[path]\n const def = deepGet(this.defaults, path)\n if (config?.omitWhenDefault && deepEqual(value, def)) {\n // Mark for clear instead of write\n filtered[path] = undefined\n } else {\n filtered[path] = value\n }\n }\n\n // Forward per-call options (history strategy) to the backend. Backends\n // ignore fields they don't recognize.\n const writeOptions: WriteOptions | undefined =\n options?.history !== undefined ? { history: options.history } : undefined\n\n this.lastWritten = { ...this.values }\n try {\n // Storage write is `void | Promise<void>`. Catch sync throws here; for\n // async rejections, attach a `.catch` so the promise doesn't surface\n // as an unhandled rejection. Either way: silent fallback to in-memory\n // state — storage drift accepted (the standard storage error contract).\n const result = this.storage.write(filtered as Partial<T>, changed, writeOptions)\n if (result && typeof (result as Promise<void>).then === 'function') {\n ;(result as Promise<void>).catch(() => undefined)\n }\n } catch {\n // Sync write failure — same silent contract.\n }\n }\n\n private onExternalChange(raw: Partial<T>): void {\n if (this.disposed) return\n // Loop prevention: drop echoes that match what we just wrote\n if (this.lastWritten && deepEqual(raw, this.lastWritten)) return\n\n const changed: string[] = []\n let next = this.values\n for (const [key, rawValue] of Object.entries(raw)) {\n const fieldSpec = this.spec[key]\n if (!fieldSpec) continue\n const result = parseField(fieldSpec, rawValue)\n const newValue = result.ok ? result.value : deepGet(this.defaults, key)\n const oldValue = deepGet(next, key)\n if (deepEqual(oldValue, newValue)) continue\n next = deepSet(next, key, newValue) as Partial<T>\n changed.push(key)\n if (!result.ok && result.reason) {\n this.storageErrorMap.set(key, result.reason)\n } else {\n this.storageErrorMap.delete(key)\n }\n }\n if (changed.length === 0) return\n this.values = next\n this.invalidateToQueryCache()\n for (const path of changed) this.trie.notify(path)\n }\n\n private invalidateToQueryCache(): void {\n this.toQueryCache = undefined\n }\n}\n","import { warn } from './dev'\nimport { ParamsStore } from './params-store'\nimport type { ParamsDefinition } from './types'\n\ninterface CacheEntry<T> {\n store: ParamsStore<T>\n refCount: number\n pendingDispose?: ReturnType<typeof queueMicrotask> | null\n // Microtask scheduled disposal flag — `true` = a release-triggered disposal\n // is queued; `acquire()` cancels it by clearing this and incrementing\n // refCount.\n disposalQueued: boolean\n}\n\nconst cache = new WeakMap<ParamsDefinition, CacheEntry<unknown>>()\nconst seenNames = new Map<string, ParamsDefinition>()\n\n/**\n * Acquire (or reuse) the cached store for a definition. Increments the\n * reference count; the store is disposed only when the last subscriber\n * releases it (and the microtask-deferred disposal isn't preempted by\n * another acquire — Strict Mode safety).\n */\nexport function acquire<T>(def: ParamsDefinition<T>): ParamsStore<T> {\n warnOnDuplicateName(def)\n let entry = cache.get(def) as CacheEntry<T> | undefined\n if (!entry) {\n entry = {\n store: new ParamsStore<T>(def),\n refCount: 0,\n disposalQueued: false,\n }\n cache.set(def, entry as CacheEntry<unknown>)\n } else {\n // Cancel any pending disposal — Strict Mode and rapid mount/unmount/remount\n entry.disposalQueued = false\n }\n entry.refCount++\n return entry.store\n}\n\n/**\n * Release a previously-acquired store. When the last subscriber releases,\n * disposal is **scheduled in a microtask** — if a fresh `acquire()` arrives\n * before the microtask runs (Strict Mode dev double-invoke), the disposal\n * is cancelled and the store survives.\n */\nexport function release<T>(def: ParamsDefinition<T>): void {\n const entry = cache.get(def) as CacheEntry<T> | undefined\n if (!entry) return\n entry.refCount--\n if (entry.refCount > 0) return\n\n entry.disposalQueued = true\n queueMicrotask(() => {\n if (!entry.disposalQueued) return // cancelled by acquire()\n entry.disposalQueued = false\n disposeAndClear(def, entry)\n })\n}\n\nfunction disposeAndClear<T>(def: ParamsDefinition<T>, entry: CacheEntry<T>): void {\n entry.store.dispose()\n cache.delete(def)\n if (def.name !== undefined && seenNames.get(def.name) === def) {\n seenNames.delete(def.name)\n }\n}\n\n/**\n * Public, no-React imperative read of the cached store. The store lives\n * for the program lifetime once acquired (no ref counting). Use\n * `releaseParamsStore(def)` for explicit cleanup in long-running non-React\n * contexts (daemons, electron apps, server processes).\n *\n * Not safe for shared-process SSR contexts — see plan §SSR.\n */\nexport function getCachedStore<T>(def: ParamsDefinition<T>): ParamsStore<T> {\n let entry = cache.get(def) as CacheEntry<T> | undefined\n if (!entry) {\n entry = {\n store: new ParamsStore<T>(def),\n refCount: 0,\n disposalQueued: false,\n }\n cache.set(def, entry as CacheEntry<unknown>)\n }\n return entry.store\n}\n\n/** Explicit cleanup for non-React long-running apps. */\nexport function releaseCachedStore<T>(def: ParamsDefinition<T>): void {\n const entry = cache.get(def) as CacheEntry<T> | undefined\n if (!entry) {\n // Cache entry already gone (e.g., microtask-deferred React release ran\n // first), but the name registration may still be live. Clear it so the\n // same name can be reused without a spurious duplicate-name warning.\n if (def.name !== undefined && seenNames.get(def.name) === def) {\n seenNames.delete(def.name)\n }\n return\n }\n disposeAndClear(def, entry)\n}\n\nfunction warnOnDuplicateName(def: ParamsDefinition): void {\n if (def.name === undefined) return\n const existing = seenNames.get(def.name)\n if (existing !== undefined && existing !== def) {\n warn(\n `Duplicate definition name '${def.name}' detected. Names must be unique across definitions for v0.2 SSR snapshot keys.`,\n )\n return\n }\n seenNames.set(def.name, def)\n}\n"],"mappings":"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","names":["pathname"]}
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  acquire,
3
3
  release
4
- } from "../chunk-43PUAYQP.js";
4
+ } from "../chunk-S276UVQK.js";
5
5
  import "../chunk-4T4THPFW.js";
6
6
  import "../chunk-5NSLHAHG.js";
7
- import "../chunk-NUO3GOXV.js";
7
+ import "../chunk-5UKBDZTP.js";
8
8
 
9
9
  // src/integrations/forms/index.ts
10
10
  function paramsToFormSync(def) {
package/dist/react.cjs CHANGED
@@ -72,7 +72,8 @@ function defaultSerialize(value) {
72
72
  if (typeof value === "number") return Number.isFinite(value) ? String(value) : "";
73
73
  return JSON.stringify(value);
74
74
  }
75
- function extractEnumValues(spec) {
75
+ var customExtractors = /* @__PURE__ */ new Set();
76
+ var extractZodEnum = (spec) => {
76
77
  let current = spec;
77
78
  for (let i = 0; i < 8 && current !== null && typeof current === "object"; i++) {
78
79
  const def = current._def;
@@ -90,6 +91,33 @@ function extractEnumValues(spec) {
90
91
  return void 0;
91
92
  }
92
93
  return void 0;
94
+ };
95
+ var extractValibotEnum = (spec) => {
96
+ let current = spec;
97
+ for (let i = 0; i < 8 && current !== null && typeof current === "object"; i++) {
98
+ if (current.kind !== "schema" || typeof current.type !== "string") return void 0;
99
+ if (current.type === "picklist" || current.type === "enum") {
100
+ return Array.isArray(current.options) ? current.options : void 0;
101
+ }
102
+ if (current.wrapped) {
103
+ current = current.wrapped;
104
+ continue;
105
+ }
106
+ return void 0;
107
+ }
108
+ return void 0;
109
+ };
110
+ var builtInExtractors = [extractZodEnum, extractValibotEnum];
111
+ function extractEnumValues(spec) {
112
+ for (const extractor of customExtractors) {
113
+ const result = extractor(spec);
114
+ if (result !== void 0) return result;
115
+ }
116
+ for (const extractor of builtInExtractors) {
117
+ const result = extractor(spec);
118
+ if (result !== void 0) return result;
119
+ }
120
+ return void 0;
93
121
  }
94
122
 
95
123
  // src/react/use-field-value.ts