@zodal/dials-core 0.1.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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/model.ts","../src/util.ts","../src/merge.ts","../src/cascade.ts","../src/patch.ts","../src/schema.ts","../src/secrets.ts","../src/constraints.ts","../src/derive.ts","../src/define-dials.ts"],"sourcesContent":["/**\n * @zodal/dials-core — the canonical settings model + cascade engine for zodal-dials.\n *\n * Public surface:\n * - `defineDials(schema, config?)` — the entry point (a settings doc = a degenerate one-item zodal\n * collection); exposes `resolve`/`explain`/`validate`/`withDependentDefaults`/`getCapabilities`.\n * - The cascade: `resolve(stack, options)` → effective values + provenance; the `UNSET` sentinel;\n * `Layer`/`Scope`/`ScopedLayer`/`Provenance`/`EffectiveResult` types.\n * - Patch utils: RFC 7386 `applyMergePatch`, lossless `serializeLayer`/`deserializeLayer`, RFC 6902\n * `applyJsonPatch`/`diffJsonPatch`/`invertJsonPatch`.\n * - Constraints + dependent defaults: `evaluateConstraints`, `applyDependentDefaults`.\n * - Secrets: `SecretRef`, `maskSecrets`, `splitBySensitivity`, `redactSecretsFromLayer`, `SecretBackend`.\n * - Schema introspection: `extractDefaults`, `keyMergeStrategy`, `classifySensitivity`, `baseType`.\n */\n\nexport {\n UNSET,\n isUnset,\n isSecretRef,\n} from './model.js';\nexport type {\n SettingKey,\n SettingValue,\n Unset,\n Layer,\n ScopedLayer,\n MergeStrategy,\n ShadowedLayer,\n KeyProvenance,\n Conflict,\n EffectiveResult,\n Sensitivity,\n SecretRef,\n} from './model.js';\n\nexport { resolve } from './cascade.js';\nexport type { ResolveOptions } from './cascade.js';\n\nexport { mergeValues } from './merge.js';\n\nexport {\n applyMergePatch,\n serializeLayer,\n deserializeLayer,\n layerToMergePatch,\n applyJsonPatch,\n diffJsonPatch,\n invertJsonPatch,\n} from './patch.js';\nexport type { SerializedLayer, JsonPatchOp } from './patch.js';\n\nexport {\n getObjectShape,\n readMeta,\n baseType,\n extractDefaults,\n keyMergeStrategy,\n classifySensitivity,\n} from './schema.js';\n\nexport {\n makeSecretRef,\n maskSecrets,\n maskEffectiveResult,\n splitBySensitivity,\n redactSecretsFromLayer,\n} from './secrets.js';\nexport type { SecretBackend } from './secrets.js';\n\nexport { evaluateConstraints } from './constraints.js';\nexport type {\n Assertion,\n Warning,\n ConstraintsConfig,\n ConstraintError,\n ConstraintResult,\n} from './constraints.js';\n\nexport { applyDependentDefaults } from './derive.js';\nexport type { DependentDefault, DeriveOptions, DeriveResult } from './derive.js';\n\nexport { defineDials } from './define-dials.js';\nexport type {\n DefineDialsConfig,\n DialsResolveOptions,\n DialsCapabilities,\n DialsDefinition,\n} from './define-dials.js';\n\nexport type { LayerStore, LayerStoreCapabilities } from './store.js';\n","/**\n * Core model types for zodal-dials: settings, layers, scopes, the cascade result, and provenance.\n *\n * A SETTING is a typed named parameter identified by a stable dotted-path KEY. A LAYER is a partial\n * map of key -> value (or the UNSET sentinel) from one source. A SCOPE names an ordered source of\n * layers; the CASCADE merges an ordered stack of scoped layers into an EFFECTIVE value per key,\n * paired with PROVENANCE (which scope won, what it shadowed, whether it is policy-managed).\n *\n * UNSET is the explicit deletion/reset sentinel — never raw `null` — so a layer can declare \"I do\n * not contribute this key\" (re-exposing a lower scope) without colliding with a legitimate `null`\n * value. See `docs/zodal-dials-concept.md` and `docs/dev-plan.md` §4.\n */\n\n/** A setting's stable, serialization-independent identity (a dotted path, e.g. \"editor.fontSize\"). */\nexport type SettingKey = string;\n\n/** A setting value: a scalar, an array, or an irreducible nested object. */\nexport type SettingValue = unknown;\n\n/**\n * The deletion/reset sentinel. In a layer, `UNSET` marks a key as explicitly not contributed by\n * that scope, re-exposing the value from a lower-precedence scope (or its absence). Distinct from\n * `null`/`undefined`, which are legitimate setting values. Uses a registered symbol so it survives\n * realm boundaries and bundler duplication.\n */\nexport const UNSET: unique symbol = Symbol.for('@zodal/dials.UNSET');\nexport type Unset = typeof UNSET;\n\n/** Type guard for the UNSET sentinel. */\nexport function isUnset(v: unknown): v is Unset {\n return v === UNSET;\n}\n\n/** A partial/sparse set of setting values from one source — the unit that gets merged. */\nexport type Layer = Record<SettingKey, SettingValue | Unset>;\n\n/**\n * A named layer in the cascade. Scopes are DATA, not constants: the resolver is handed an ordered\n * stack (lowest precedence first) and never hardcodes the ladder. `managed: true` elevates a layer\n * into the policy band — it wins over every non-managed layer regardless of position and marks the\n * resulting value as non-overridable (the UI should lock the control).\n */\nexport interface ScopedLayer {\n /** The scope id this layer comes from (e.g. \"default\", \"preset\", \"profile\", \"user\", \"policy\"). */\n scope: string;\n /** The (sparse) layer of values. */\n layer: Layer;\n /** Policy/managed band: wins over all non-managed layers and marks the value non-overridable. */\n managed?: boolean;\n}\n\n/** Per-key merge strategy. Type-directed by default; `.meta({ mergeStrategy })`-overridable. */\nexport type MergeStrategy = 'replace' | 'deep-merge' | 'append';\n\n/** A lower layer that set a key but did not win (or that explicitly UNSET it). */\nexport interface ShadowedLayer {\n scope: string;\n /** The shadowed value, or 'UNSET' if that layer reset the key. */\n value: SettingValue | 'UNSET';\n managed: boolean;\n}\n\n/**\n * The cascade's first-class explanation of an effective value: which scope won, how the value was\n * produced, what it shadowed, whether it is policy-managed, and (for deep-merged objects) which\n * scopes contributed. This is the deliberate differentiator — provenance is never a debug\n * afterthought.\n */\nexport interface KeyProvenance {\n key: SettingKey;\n /** The scope id that supplied the effective value. */\n winningScope: string;\n /** The resolved value (also present in `effective`). */\n value: SettingValue;\n /** True when the winning scope is in the managed/policy band (control should be locked). */\n managed: boolean;\n /** How the value was produced from the contributing layers. */\n mergeStrategy: MergeStrategy;\n /** Other layers that set this key, highest precedence first (each value or 'UNSET'). */\n shadowed: ShadowedLayer[];\n /** For deep-merged object values: the scope ids whose objects were merged, low -> high. */\n mergedFrom?: string[];\n}\n\n/** A key set to differing values by more than one layer (surfaced for the UI). */\nexport interface Conflict {\n key: SettingKey;\n /** Contributing scopes (highest precedence first) with their values. */\n contributors: Array<{ scope: string; value: SettingValue | 'UNSET'; managed: boolean }>;\n /** True when a managed/policy scope overrode a differing non-managed value. */\n overriddenByPolicy: boolean;\n}\n\n/** The complete result of resolving a cascade. */\nexport interface EffectiveResult {\n /** The resolved value per key (keys that resolve to UNSET/absent are omitted). */\n effective: Record<SettingKey, SettingValue>;\n /** Provenance per resolved key. */\n provenance: Record<SettingKey, KeyProvenance>;\n /** Keys set by multiple layers to differing values (informational). */\n conflicts: Conflict[];\n}\n\n/** Sensitivity classification of a setting. */\nexport type Sensitivity = 'public' | 'sensitive' | 'secret';\n\n/**\n * A masked reference to a secret value — mirrors zodal's `ContentRef`. Reads of a secret setting\n * return a `SecretRef`, never plaintext; the plaintext is fetched via an explicit reveal call.\n */\nexport interface SecretRef {\n readonly _tag: 'SecretRef';\n /** The setting key this secret belongs to. */\n key: SettingKey;\n /** Whether a value is set (without revealing it). */\n isSet: boolean;\n /** A display mask, e.g. \"•••• (set)\" or \"not set\". */\n masked: string;\n}\n\n/** Type guard for SecretRef. */\nexport function isSecretRef(v: unknown): v is SecretRef {\n return typeof v === 'object' && v !== null && (v as { _tag?: unknown })._tag === 'SecretRef';\n}\n","/**\n * Internal value utilities shared across the cascade engine: plain-object detection, structural\n * deep clone, deep equality, and structural deep merge. Pure and dependency-free. All object\n * construction goes through `setOwn` (a defineProperty-based assignment) so a value carrying a\n * literal `__proto__` / `constructor` / `prototype` own key cannot corrupt a prototype or pollute\n * the global `Object.prototype`. Not part of the public API.\n */\n\n/** Keys whose plain assignment could mutate a prototype — always assigned via defineProperty. */\nconst PROTO_KEYS = new Set(['__proto__', 'constructor', 'prototype']);\n\n/** Assign an own, enumerable property safely (never triggers the `__proto__` setter). */\nfunction setOwn(obj: Record<string, unknown>, key: string, value: unknown): void {\n if (PROTO_KEYS.has(key)) {\n Object.defineProperty(obj, key, { value, writable: true, enumerable: true, configurable: true });\n } else {\n obj[key] = value;\n }\n}\n\n/** True for a non-null, non-array object with a plain prototype (a JSON-style object). */\nexport function isPlainObject(v: unknown): v is Record<string, unknown> {\n if (typeof v !== 'object' || v === null || Array.isArray(v)) return false;\n const proto = Object.getPrototypeOf(v);\n return proto === Object.prototype || proto === null;\n}\n\n/** Structural deep clone of JSON-shaped values (objects, arrays, primitives). Proto-safe. */\nexport function deepClone<T>(v: T): T {\n if (Array.isArray(v)) return v.map((x) => deepClone(x)) as unknown as T;\n if (isPlainObject(v)) {\n const out: Record<string, unknown> = {};\n for (const [k, val] of Object.entries(v)) setOwn(out, k, deepClone(val));\n return out as unknown as T;\n }\n return v;\n}\n\n/** Structural deep equality for JSON-shaped values (key-order-insensitive). */\nexport function deepEqual(a: unknown, b: unknown): boolean {\n if (a === b) return true;\n if (Array.isArray(a) && Array.isArray(b)) {\n return a.length === b.length && a.every((x, i) => deepEqual(x, b[i]));\n }\n if (isPlainObject(a) && isPlainObject(b)) {\n const ak = Object.keys(a);\n const bk = Object.keys(b);\n return ak.length === bk.length && ak.every((k) => Object.prototype.hasOwnProperty.call(b, k) && deepEqual(a[k], b[k]));\n }\n return false;\n}\n\n/**\n * Structural deep merge: `b` wins per leaf; nested plain objects merge recursively; arrays and\n * scalars from `b` replace `a`. Pure (inputs never mutated) and proto-safe.\n */\nexport function deepMerge(a: unknown, b: unknown): unknown {\n if (isPlainObject(a) && isPlainObject(b)) {\n const out: Record<string, unknown> = deepClone(a);\n for (const [k, vb] of Object.entries(b)) {\n setOwn(out, k, Object.prototype.hasOwnProperty.call(out, k) ? deepMerge(out[k], vb) : deepClone(vb));\n }\n return out;\n }\n return deepClone(b);\n}\n","/**\n * Value combination under a merge strategy. Given an ordered (low -> high precedence) list of\n * contributing values for one key, produce the merged value: `replace` (highest wins), `deep-merge`\n * (structural merge of object contributors), or `append` (concatenate array contributors). Pure.\n */\n\nimport { deepMerge, deepClone, isPlainObject } from './util.js';\nimport type { MergeStrategy, SettingValue } from './model.js';\n\n/** Combine an ordered (low -> high precedence) list of contributing values under a strategy. */\nexport function mergeValues(values: SettingValue[], strategy: MergeStrategy): SettingValue {\n if (values.length === 0) return undefined;\n switch (strategy) {\n case 'replace':\n return deepClone(values[values.length - 1]);\n case 'deep-merge':\n return deepClone(\n values.reduce((acc, v) => (isPlainObject(acc) && isPlainObject(v) ? deepMerge(acc, v) : deepClone(v))),\n );\n case 'append': {\n const out: unknown[] = [];\n let sawArray = false;\n for (const v of values) {\n if (Array.isArray(v)) {\n sawArray = true;\n out.push(...deepClone(v));\n }\n }\n return sawArray ? out : deepClone(values[values.length - 1]);\n }\n }\n}\n","/**\n * The cascade resolver: merge an ordered stack of scoped layers (lowest precedence first) into an\n * effective value per key, paired with provenance.\n *\n * Precedence is the stack order, with managed/policy layers elevated into a top band that wins over\n * every non-managed layer regardless of position (and marks the value non-overridable). The UNSET\n * sentinel is a fall-through: a layer that UNSETs a key ABSTAINS (contributes nothing for it),\n * re-exposing the next lower scope — at the top of a stack this is exactly \"reset to default\". Under\n * `deep-merge`/`append`, an abstaining (UNSET) layer simply does not participate in the merge; it\n * does NOT sever the contributors below it — to override a lower object wholesale, use the `replace`\n * strategy or set the full value. Provenance reports the winning scope, the shadowed layers\n * (including higher resets), the merge strategy used, and — for merges — only the scopes that\n * actually changed the result (`mergedFrom`, computed by leave-one-out, so fully-shadowed scopes are\n * not falsely claimed).\n */\n\nimport type {\n Conflict,\n EffectiveResult,\n KeyProvenance,\n MergeStrategy,\n ScopedLayer,\n SettingKey,\n SettingValue,\n ShadowedLayer,\n} from './model.js';\nimport { isUnset } from './model.js';\nimport { mergeValues } from './merge.js';\nimport { deepClone, deepEqual, isPlainObject } from './util.js';\n\nexport interface ResolveOptions {\n /** Per-key merge strategy resolver. Default: 'replace' for every key. */\n strategyFor?: (key: SettingKey) => MergeStrategy;\n}\n\ninterface Entry {\n scope: string;\n /** The raw layer value for this key; `undefined` here means UNSET (tracked via `isUnset`). */\n value: SettingValue;\n isUnset: boolean;\n managed: boolean;\n /** Computed precedence; higher wins. Managed layers occupy a top band. */\n precedence: number;\n}\n\n/** Resolve a stack of scoped layers (lowest precedence first) into effective values + provenance. */\nexport function resolve(stack: ScopedLayer[], options: ResolveOptions = {}): EffectiveResult {\n const strategyFor = options.strategyFor ?? (() => 'replace' as MergeStrategy);\n const band = stack.length + 1; // strictly greater than any non-managed index\n\n // Gather, per key, the layers that set it (with computed precedence).\n const byKey = new Map<SettingKey, Entry[]>();\n stack.forEach((sl, index) => {\n const managed = sl.managed === true;\n const precedence = (managed ? band : 0) + index;\n for (const [key, raw] of Object.entries(sl.layer)) {\n const entry: Entry = { scope: sl.scope, value: isUnset(raw) ? undefined : raw, isUnset: isUnset(raw), managed, precedence };\n const list = byKey.get(key);\n if (list) list.push(entry);\n else byKey.set(key, [entry]);\n }\n });\n\n const effective: Record<SettingKey, SettingValue> = {};\n const provenance: Record<SettingKey, KeyProvenance> = {};\n const conflicts: Conflict[] = [];\n\n for (const [key, rawEntries] of byKey) {\n // Highest precedence first.\n const entries = [...rawEntries].sort((a, b) => b.precedence - a.precedence);\n const contributors = entries.filter((e) => !e.isUnset); // UNSET = no contribution (fall-through)\n\n const shadowed: ShadowedLayer[] = entries.map((e) => ({\n scope: e.scope,\n value: e.isUnset ? 'UNSET' : e.value,\n managed: e.managed,\n }));\n\n if (contributors.length === 0) {\n // Every layer that touched this key reset it -> key is absent from the effective set.\n continue;\n }\n\n const strategy = strategyFor(key);\n const winner = contributors[0];\n let value: SettingValue;\n let mergedFrom: string[] | undefined;\n\n if (strategy === 'deep-merge') {\n const lowToHigh = [...contributors].reverse();\n value = mergeValues(lowToHigh.map((e) => e.value), 'deep-merge');\n // Attribute by surviving-leaf origin (not naive leave-one-out, which under-reports when two\n // scopes set an identical leaf): a scope is a contributor if it owns >=1 leaf in the result.\n const contributing = deepMergeContributors(contributors, value);\n mergedFrom = contributing.length > 1 ? contributing : undefined;\n } else if (strategy === 'append') {\n const lowToHigh = [...contributors].reverse();\n value = mergeValues(lowToHigh.map((e) => e.value), 'append');\n const contributing = contributingScopes(lowToHigh, 'append', value);\n mergedFrom = contributing.length > 1 ? contributing : undefined;\n } else {\n value = deepClone(winner.value);\n }\n\n effective[key] = value;\n // The winning scope is the one whose entry is shown in the UI as \"set by\"; for merges this is\n // the highest-precedence contributor (it dominates conflicting leaves).\n provenance[key] = {\n key,\n winningScope: winner.scope,\n value,\n managed: winner.managed,\n mergeStrategy: strategy,\n shadowed: shadowed.filter((s, i) => !(entries[i].scope === winner.scope && entries[i].precedence === winner.precedence)),\n mergedFrom,\n };\n\n // Conflict detection: more than one contributor with a differing value.\n const distinct: SettingValue[] = [];\n for (const c of contributors) if (!distinct.some((d) => deepEqual(d, c.value))) distinct.push(c.value);\n if (distinct.length > 1) {\n const overriddenByPolicy =\n winner.managed && contributors.some((c) => !c.managed && !deepEqual(c.value, winner.value));\n conflicts.push({\n key,\n contributors: contributors.map((c) => ({ scope: c.scope, value: c.value, managed: c.managed })),\n overriddenByPolicy,\n });\n }\n }\n\n return { effective, provenance, conflicts };\n}\n\n/** Flatten an object's scalar/array leaves into a path -> value map (arrays are leaves). */\nfunction flattenLeaves(value: SettingValue, prefix: string, out: Map<string, SettingValue>): void {\n if (isPlainObject(value)) {\n for (const [k, v] of Object.entries(value)) flattenLeaves(v, `${prefix}/${k}`, out);\n } else {\n out.set(prefix, value);\n }\n}\n\n/**\n * Honest `mergedFrom` for deep-merge: for each surviving leaf of the merged value, the owner is the\n * highest-precedence contributor that supplied that exact leaf; the contributor set is returned\n * low -> high. This correctly attributes a value merged from distinct scopes even when a lower scope\n * set an identical (redundant) leaf.\n */\nfunction deepMergeContributors(contributorsHighToLow: Entry[], merged: SettingValue): string[] {\n const mergedLeaves = new Map<string, SettingValue>();\n flattenLeaves(merged, '', mergedLeaves);\n const perEntry = contributorsHighToLow.map((e) => {\n const m = new Map<string, SettingValue>();\n flattenLeaves(e.value, '', m);\n return m;\n });\n const owners = new Set<string>();\n for (const [path, val] of mergedLeaves) {\n for (let i = 0; i < contributorsHighToLow.length; i += 1) {\n const leaf = perEntry[i].get(path);\n if (perEntry[i].has(path) && deepEqual(leaf, val)) {\n owners.add(contributorsHighToLow[i].scope);\n break;\n }\n }\n }\n return [...contributorsHighToLow].reverse().filter((e) => owners.has(e.scope)).map((e) => e.scope);\n}\n\n/**\n * Honest `mergedFrom` for append (and other replace-fold strategies): the scopes that actually\n * changed the merged result, computed by leave-one-out. Fully-shadowed scopes are excluded.\n */\nfunction contributingScopes(lowToHigh: Entry[], strategy: MergeStrategy, full: SettingValue): string[] {\n const values = lowToHigh.map((e) => e.value);\n const out: string[] = [];\n for (let i = 0; i < lowToHigh.length; i += 1) {\n const without = mergeValues(\n values.filter((_, j) => j !== i),\n strategy,\n );\n if (!deepEqual(without, full)) out.push(lowToHigh[i].scope);\n }\n return out;\n}\n","/**\n * Patch & serialization utilities for layers.\n *\n * - RFC 7386 JSON Merge Patch (`applyMergePatch`) — the ergonomic, mirror-shaped delta format.\n * - Lossless layer serialization (`serializeLayer`/`deserializeLayer`) — encodes the UNSET sentinel\n * separately from values so a layer round-trips with zero drift (UNSET is NOT conflated with a\n * literal `null`, the standard RFC 7386 footgun).\n * - `layerToMergePatch` — a layer AS a standard RFC 7386 patch (UNSET -> null) for interop (lossy\n * only for the rare literal-`null` value).\n * - RFC 6902 JSON Patch (`applyJsonPatch`) + `diffJsonPatch` + `invertJsonPatch` — for history/undo.\n *\n * All functions are pure; inputs are never mutated. Pointer traversal rejects the prototype-polluting\n * keys `__proto__`/`constructor`/`prototype`, and array indices are validated per RFC 6902 §4.\n */\n\nimport { isPlainObject, deepClone, deepEqual } from './util.js';\nimport { UNSET, isUnset } from './model.js';\nimport type { Layer } from './model.js';\n\n// ---------------------------------------------------------------------------\n// RFC 7386 — JSON Merge Patch (https://www.rfc-editor.org/rfc/rfc7386)\n// ---------------------------------------------------------------------------\n\nconst PROTO_KEYS = new Set(['__proto__', 'constructor', 'prototype']);\n\n/** Apply an RFC 7386 JSON Merge Patch to a target. `null` in the patch deletes a member. Pure. */\nexport function applyMergePatch(target: unknown, patch: unknown): unknown {\n if (!isPlainObject(patch)) return deepClone(patch);\n const base: Record<string, unknown> = isPlainObject(target) ? deepClone(target) : {};\n for (const [key, val] of Object.entries(patch)) {\n if (PROTO_KEYS.has(key)) continue; // never let a patch reach a prototype\n if (val === null) delete base[key];\n else base[key] = applyMergePatch(base[key], val);\n }\n return base;\n}\n\n// ---------------------------------------------------------------------------\n// Lossless layer serialization (UNSET kept distinct from null)\n// ---------------------------------------------------------------------------\n\n/** The on-disk/wire shape of a layer: values plus an explicit list of reset (UNSET) keys. */\nexport interface SerializedLayer {\n values: Record<string, unknown>;\n unset: string[];\n}\n\n/** Serialize a layer losslessly (UNSET keys recorded separately from values). */\nexport function serializeLayer(layer: Layer): SerializedLayer {\n const values: Record<string, unknown> = {};\n const unset: string[] = [];\n for (const [k, v] of Object.entries(layer)) {\n if (isUnset(v)) unset.push(k);\n else values[k] = deepClone(v);\n }\n return { values, unset };\n}\n\n/** Deserialize a layer produced by `serializeLayer` (the inverse; round-trips with zero drift). */\nexport function deserializeLayer(s: SerializedLayer): Layer {\n const layer: Layer = {};\n for (const [k, v] of Object.entries(s.values ?? {})) layer[k] = deepClone(v);\n for (const k of s.unset ?? []) layer[k] = UNSET;\n return layer;\n}\n\n/**\n * Express a layer as a standard RFC 7386 merge patch (UNSET -> null). Lossy ONLY for the rare case\n * of a setting whose legitimate value is `null` (indistinguishable from a reset under RFC 7386); use\n * `serializeLayer` when that distinction must be preserved.\n */\nexport function layerToMergePatch(layer: Layer): Record<string, unknown> {\n const patch: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(layer)) patch[k] = isUnset(v) ? null : deepClone(v);\n return patch;\n}\n\n// ---------------------------------------------------------------------------\n// RFC 6901 — JSON Pointer\n// ---------------------------------------------------------------------------\n\n/** RFC 6902 §4: an array index is `0` or a non-zero-leading run of digits (no leading zeros, signs, spaces). */\nconst ARRAY_INDEX = /^(?:0|[1-9][0-9]*)$/;\n\nfunction parsePointer(pointer: string): string[] {\n if (pointer === '') return [];\n if (pointer[0] !== '/') throw new Error(`Invalid JSON Pointer (must start with \"/\"): ${pointer}`);\n return pointer\n .slice(1)\n .split('/')\n .map((tok) => tok.replace(/~1/g, '/').replace(/~0/g, '~'));\n}\n\nfunction arrayIndex(token: string, length: number, mode: 'add' | 'access'): number {\n if (mode === 'add' && token === '-') return length;\n if (!ARRAY_INDEX.test(token)) throw new Error(`Invalid array index \"${token}\"`);\n const idx = Number(token);\n if (mode === 'add' ? idx > length : idx >= length) {\n throw new Error(`Array index out of bounds: ${token} (length ${length})`);\n }\n return idx;\n}\n\nfunction getAtPointer(doc: unknown, tokens: string[]): unknown {\n let cur: unknown = doc;\n for (const tok of tokens) {\n if (Array.isArray(cur)) {\n if (tok === '-' || !ARRAY_INDEX.test(tok)) return undefined;\n cur = cur[Number(tok)];\n } else if (isPlainObject(cur)) {\n if (PROTO_KEYS.has(tok)) return undefined;\n cur = cur[tok];\n } else {\n return undefined;\n }\n }\n return cur;\n}\n\n/** Set/insert at a pointer, mutating `doc` (callers always pass a fresh clone). */\nfunction setAtPointer(doc: unknown, tokens: string[], value: unknown, insert: boolean): unknown {\n if (tokens.length === 0) return value;\n const last = tokens[tokens.length - 1];\n const parent = getAtPointer(doc, tokens.slice(0, -1));\n if (Array.isArray(parent)) {\n const idx = arrayIndex(last, parent.length, insert ? 'add' : 'access');\n if (insert) parent.splice(idx, 0, value);\n else parent[idx] = value;\n } else if (isPlainObject(parent)) {\n if (PROTO_KEYS.has(last)) throw new Error(`Refusing to set prototype-polluting key: ${last}`);\n parent[last] = value;\n } else {\n throw new Error(`Cannot set at pointer; parent is not a container: /${tokens.slice(0, -1).join('/')}`);\n }\n return doc;\n}\n\nfunction removeAtPointer(doc: unknown, tokens: string[]): unknown {\n if (tokens.length === 0) throw new Error('Cannot remove the document root');\n const last = tokens[tokens.length - 1];\n const parent = getAtPointer(doc, tokens.slice(0, -1));\n if (Array.isArray(parent)) {\n const idx = arrayIndex(last, parent.length, 'access');\n parent.splice(idx, 1);\n } else if (isPlainObject(parent)) {\n if (PROTO_KEYS.has(last) || !Object.prototype.hasOwnProperty.call(parent, last)) {\n throw new Error(`Cannot remove; member does not exist: /${tokens.join('/')}`);\n }\n delete parent[last];\n } else {\n throw new Error(`Cannot remove at pointer; parent is not a container: /${tokens.slice(0, -1).join('/')}`);\n }\n return doc;\n}\n\nfunction pointerExists(doc: unknown, tokens: string[]): boolean {\n if (tokens.length === 0) return true;\n const parent = getAtPointer(doc, tokens.slice(0, -1));\n const last = tokens[tokens.length - 1];\n if (Array.isArray(parent)) return ARRAY_INDEX.test(last) && Number(last) < parent.length;\n if (isPlainObject(parent)) return !PROTO_KEYS.has(last) && Object.prototype.hasOwnProperty.call(parent, last);\n return false;\n}\n\n// ---------------------------------------------------------------------------\n// RFC 6902 — JSON Patch (https://datatracker.ietf.org/doc/html/rfc6902)\n// ---------------------------------------------------------------------------\n\n/** An RFC 6902 JSON Patch operation. */\nexport type JsonPatchOp =\n | { op: 'add'; path: string; value: unknown }\n | { op: 'remove'; path: string }\n | { op: 'replace'; path: string; value: unknown }\n | { op: 'move'; from: string; path: string }\n | { op: 'copy'; from: string; path: string }\n | { op: 'test'; path: string; value: unknown };\n\n/** Apply an ordered RFC 6902 JSON Patch to a document. Pure; throws on a failed `test` or bad path. */\nexport function applyJsonPatch(doc: unknown, ops: JsonPatchOp[]): unknown {\n let result = deepClone(doc);\n for (const op of ops) {\n switch (op.op) {\n case 'add': {\n result = setAtPointer(result, parsePointer(op.path), deepClone(op.value), true);\n break;\n }\n case 'remove': {\n result = removeAtPointer(result, parsePointer(op.path));\n break;\n }\n case 'replace': {\n const tokens = parsePointer(op.path);\n if (!pointerExists(result, tokens)) throw new Error(`replace target does not exist: ${op.path}`);\n result = setAtPointer(result, tokens, deepClone(op.value), false);\n break;\n }\n case 'move': {\n const fromTokens = parsePointer(op.from);\n if (!pointerExists(result, fromTokens)) throw new Error(`move source does not exist: ${op.from}`);\n const val = deepClone(getAtPointer(result, fromTokens));\n result = removeAtPointer(result, fromTokens);\n result = setAtPointer(result, parsePointer(op.path), val, true);\n break;\n }\n case 'copy': {\n const fromTokens = parsePointer(op.from);\n if (!pointerExists(result, fromTokens)) throw new Error(`copy source does not exist: ${op.from}`);\n const val = deepClone(getAtPointer(result, fromTokens));\n result = setAtPointer(result, parsePointer(op.path), val, true);\n break;\n }\n case 'test': {\n const tokens = parsePointer(op.path);\n if (!pointerExists(result, tokens) || !deepEqual(getAtPointer(result, tokens), op.value)) {\n throw new Error(`test failed at ${op.path}`);\n }\n break;\n }\n default: {\n throw new Error(`Unknown JSON Patch op: ${(op as { op: string }).op}`);\n }\n }\n }\n return result;\n}\n\n/**\n * Compute an RFC 6902 patch turning `before` into `after`, at object-member granularity (recursing\n * into nested plain objects; arrays and scalars are replaced wholesale). The patches we generate are\n * always add/remove/replace, which `invertJsonPatch` inverts exactly — ideal for settings history.\n */\nexport function diffJsonPatch(before: unknown, after: unknown, basePath = ''): JsonPatchOp[] {\n if (deepEqual(before, after)) return [];\n if (!isPlainObject(before) || !isPlainObject(after)) {\n return [{ op: 'replace', path: basePath, value: deepClone(after) }];\n }\n const ops: JsonPatchOp[] = [];\n const enc = (k: string) => k.replace(/~/g, '~0').replace(/\\//g, '~1');\n for (const k of Object.keys(before)) {\n const path = `${basePath}/${enc(k)}`;\n if (!Object.prototype.hasOwnProperty.call(after, k)) ops.push({ op: 'remove', path });\n else ops.push(...diffJsonPatch(before[k], after[k], path));\n }\n for (const k of Object.keys(after)) {\n if (!Object.prototype.hasOwnProperty.call(before, k)) {\n ops.push({ op: 'add', path: `${basePath}/${enc(k)}`, value: deepClone(after[k]) });\n }\n }\n return ops;\n}\n\n/**\n * Produce the inverse of a patch (relative to the document it was applied to), so applying the\n * inverse undoes it. Exact for add/remove/replace/test/copy; `move` is inverted as a reverse move.\n */\nexport function invertJsonPatch(ops: JsonPatchOp[], before: unknown): JsonPatchOp[] {\n const inverse: JsonPatchOp[] = [];\n let state = deepClone(before);\n for (const op of ops) {\n const tokens = 'path' in op ? parsePointer(op.path) : [];\n switch (op.op) {\n case 'add': {\n const parent = getAtPointer(state, tokens.slice(0, -1));\n if (Array.isArray(parent)) {\n inverse.unshift({ op: 'remove', path: op.path }); // inserts shift back on remove\n } else {\n const existed = pointerExists(state, tokens);\n inverse.unshift(\n existed ? { op: 'replace', path: op.path, value: deepClone(getAtPointer(state, tokens)) } : { op: 'remove', path: op.path },\n );\n }\n break;\n }\n case 'remove': {\n inverse.unshift({ op: 'add', path: op.path, value: deepClone(getAtPointer(state, tokens)) });\n break;\n }\n case 'replace': {\n inverse.unshift({ op: 'replace', path: op.path, value: deepClone(getAtPointer(state, tokens)) });\n break;\n }\n case 'move': {\n inverse.unshift({ op: 'move', from: op.path, path: op.from });\n break;\n }\n case 'copy': {\n const existed = pointerExists(state, tokens);\n inverse.unshift(\n existed ? { op: 'replace', path: op.path, value: deepClone(getAtPointer(state, tokens)) } : { op: 'remove', path: op.path },\n );\n break;\n }\n case 'test': {\n inverse.unshift(op);\n break;\n }\n }\n state = applyJsonPatch(state, [op]);\n }\n return inverse;\n}\n","/**\n * Zod v4 introspection helpers for settings schemas: reading the object shape, per-field `.meta()`\n * (unwrapping wrappers so wrapped metadata survives), extracting declared defaults (robustly, by\n * parsing `undefined` — no reliance on private internals), the type-directed merge strategy, and the\n * sensitivity classification (name heuristics + `.meta()` override). Uses `@zodal/core` helpers\n * where they apply and stable `instanceof` checks for base typing.\n */\n\nimport { z } from 'zod';\nimport { unwrapZodSchema } from '@zodal/core';\nimport type { MergeStrategy, Sensitivity } from './model.js';\n\n/** Get the field map of a Zod object schema. Prefers `_zod.def.shape` (the workspace Zod-v4 rule),\n * falling back to the public `.shape`. */\nexport function getObjectShape(schema: z.ZodObject<z.ZodRawShape>): Record<string, z.ZodType> {\n const anySchema = schema as unknown as {\n shape?: Record<string, z.ZodType>;\n _zod?: { def?: { shape?: Record<string, z.ZodType> } };\n };\n return anySchema._zod?.def?.shape ?? anySchema.shape ?? {};\n}\n\n/** Read a field's `.meta()` metadata, unwrapping wrappers so metadata on the inner schema is found. */\nexport function readMeta(schema: z.ZodType): Record<string, unknown> {\n const tryMeta = (s: z.ZodType): Record<string, unknown> | undefined => {\n const m = (s as unknown as { meta?: () => unknown }).meta?.();\n return m && typeof m === 'object' ? (m as Record<string, unknown>) : undefined;\n };\n return tryMeta(schema) ?? tryMeta(unwrapZodSchema(schema)) ?? {};\n}\n\n/** The stable base type of a field (after unwrapping optional/default/nullable). */\nexport function baseType(field: z.ZodType): string {\n const s = unwrapZodSchema(field);\n if (s instanceof z.ZodObject) return 'object';\n if (s instanceof z.ZodRecord) return 'object';\n if (s instanceof z.ZodArray) return 'array';\n if (s instanceof z.ZodNumber) return 'number';\n if (s instanceof z.ZodBoolean) return 'boolean';\n if (s instanceof z.ZodString) return 'string';\n if (s instanceof z.ZodEnum) return 'enum';\n return 'unknown';\n}\n\n/** Extract the per-key default values declared in the schema (the lowest cascade layer). */\nexport function extractDefaults(schema: z.ZodObject<z.ZodRawShape>): Record<string, unknown> {\n const defaults: Record<string, unknown> = {};\n for (const [key, field] of Object.entries(getObjectShape(schema))) {\n const r = field.safeParse(undefined);\n if (r.success && r.data !== undefined) defaults[key] = r.data;\n }\n return defaults;\n}\n\n/** The type-directed default merge strategy for a field, overridable via `.meta({ mergeStrategy })`. */\nexport function keyMergeStrategy(field: z.ZodType): MergeStrategy {\n const override = readMeta(field).mergeStrategy;\n if (override === 'replace' || override === 'deep-merge' || override === 'append') return override;\n return baseType(field) === 'object' ? 'deep-merge' : 'replace';\n}\n\n/** Split a dotted/snake/camelCase key into lowercased word-separated form for heuristic matching. */\nfunction normalizeKey(key: string): string {\n return key\n .replace(/([a-z0-9])([A-Z])/g, '$1 $2')\n .replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2')\n .replace(/[._\\-/]+/g, ' ')\n .toLowerCase();\n}\n\n/**\n * Field-name heuristic for secret values. Matched against the de-camelCased, separator-normalized\n * key so `apiKey`, `api_key`, `apiKeys`, `refreshToken`, `privateKeyPath`, `passwordHash`,\n * `client_secret`, `secretId` all trip it, while `keyboard`, `tokenize`, `secretary` do not. Errs\n * toward classifying-as-secret (fail-safe).\n */\nconst SECRET_NAME =\n /(?:^| )(secret|secrets|secret ?id|passwords?|passwd|credentials?|tokens?|api ?keys?|access ?keys?|private ?keys?|client ?secret|refresh ?token|access ?token|auth ?token)(?: |$)/;\n\nfunction secretByName(key: string): boolean {\n return SECRET_NAME.test(normalizeKey(key));\n}\n\n/** Maximum nesting depth searched for a nested secret (settings schemas are rarely deeper). */\nconst MAX_SECRET_DEPTH = 8;\n\n/** Immediate child schemas of a container type (array element, record value, tuple items, union\n * options), read defensively from public getters and `_zod.def`. Objects are handled separately\n * because their children carry KEY names the heuristic needs. */\nfunction childSchemas(inner: z.ZodType): z.ZodType[] {\n const a = inner as unknown as Record<string, unknown> & { _zod?: { def?: Record<string, unknown> } };\n const def = a._zod?.def ?? {};\n const kids: z.ZodType[] = [];\n const push = (c: unknown): void => {\n if (c && typeof c === 'object' && '_zod' in (c as object)) kids.push(c as z.ZodType);\n };\n push(a.element);\n push(def.element);\n push(a.valueType);\n push(def.valueType);\n for (const arr of [a.items, def.items, a.options, def.options]) {\n if (Array.isArray(arr)) for (const c of arr) push(c);\n }\n return kids;\n}\n\n/** True if a (possibly nested) schema declares a secret — by `.meta`, by a secret-named object key,\n * or recursively inside an object/array/record/tuple/union. Fail-safe basis for whole-value masking. */\nfunction schemaContainsSecret(field: z.ZodType, depth: number): boolean {\n if (depth > MAX_SECRET_DEPTH) return false;\n const meta = readMeta(field);\n if (meta.secret === true || meta.sensitivity === 'secret') return true;\n const inner = unwrapZodSchema(field);\n if (inner instanceof z.ZodObject) {\n for (const [subKey, subField] of Object.entries(getObjectShape(inner))) {\n if (secretByName(subKey)) return true;\n if (schemaContainsSecret(subField, depth + 1)) return true;\n }\n return false;\n }\n for (const kid of childSchemas(inner)) {\n if (schemaContainsSecret(kid, depth + 1)) return true;\n }\n return false;\n}\n\n/**\n * Classify a setting's sensitivity: `.meta()` override first, then field-name heuristics, then — for\n * a container-valued setting (object/array/record/tuple/union) — a fail-safe recursion: if any\n * nested field is secret, the WHOLE setting is classified `secret`, so masking/redaction protects\n * the nested value (an irreducible nested value cannot be masked field-by-field). `field` is optional\n * so out-of-schema (ad-hoc) layer keys are still name-classified rather than defaulting to `public`.\n */\nexport function classifySensitivity(key: string, field?: z.ZodType): Sensitivity {\n if (field) {\n const meta = readMeta(field);\n if (meta.secret === true || meta.sensitivity === 'secret') return 'secret';\n if (meta.sensitivity === 'sensitive') return 'sensitive';\n if (meta.sensitivity === 'public') return 'public';\n }\n if (secretByName(key)) return 'secret';\n if (field && schemaContainsSecret(field, 0)) return 'secret';\n return 'public';\n}\n","/**\n * Secret handling — the model-side of zodal's content/metadata bifurcation applied to settings.\n *\n * A setting classified `secret` (by name heuristic or `.meta({ secret: true })`) must never appear\n * in the queryable config store, an exported layer/patch, or an audit log as plaintext. These pure\n * helpers enforce that: `splitBySensitivity` routes secret values out of the config layer to a\n * secret backend; `redactSecretsFromLayer` strips them from anything serialized for export/audit;\n * `maskSecrets` replaces secret effective values with a masked `SecretRef`. The concrete secret\n * backend (OS keychain / Vault / encrypted store) is a satellite store package; here we define the\n * seam and the guarantees.\n */\n\nimport type { EffectiveResult, Layer, SecretRef, Sensitivity, SettingKey, SettingValue } from './model.js';\nimport type { SerializedLayer } from './patch.js';\nimport { isUnset } from './model.js';\n\n/** A masked stand-in for any secret value carried in provenance/conflicts (never plaintext). */\nconst SECRET_MASK = '••••';\n\n/** Build a masked reference to a secret value (never carries the plaintext). */\nexport function makeSecretRef(key: SettingKey, isSet: boolean): SecretRef {\n return { _tag: 'SecretRef', key, isSet, masked: isSet ? '•••• (set)' : 'not set' };\n}\n\n/** Whether a raw value counts as \"set\" for masking purposes (not undefined/null/empty-string/UNSET). */\nfunction isMeaningfullySet(v: SettingValue): boolean {\n return !isUnset(v) && v !== undefined && v !== null && v !== '';\n}\n\n/**\n * Replace the effective values of secret settings with a masked `SecretRef`. Pure. The plaintext is\n * never copied into the output — reveal is an explicit, separate operation against the secret backend.\n */\nexport function maskSecrets(\n effective: Record<SettingKey, SettingValue>,\n sensitivityFor: (key: SettingKey) => Sensitivity,\n): Record<SettingKey, SettingValue> {\n const out: Record<SettingKey, SettingValue> = {};\n for (const [k, v] of Object.entries(effective)) {\n out[k] = sensitivityFor(k) === 'secret' ? makeSecretRef(k, isMeaningfullySet(v)) : v;\n }\n return out;\n}\n\n/**\n * Mask EVERY surface of a resolved result that could carry a secret as plaintext: `effective`,\n * `provenance[key].value`, every `provenance[key].shadowed[].value`, and `conflicts[].contributors[].value`.\n * This is the function `defineDials.resolve({ maskSecrets: true })` and `explain()` go through — so\n * the audit/provenance path can never leak. Pure.\n */\nexport function maskEffectiveResult(\n result: EffectiveResult,\n sensitivityFor: (key: SettingKey) => Sensitivity,\n): EffectiveResult {\n const isSecret = (key: SettingKey): boolean => sensitivityFor(key) === 'secret';\n const maskShadowValue = (v: SettingValue | 'UNSET'): SettingValue | 'UNSET' => (v === 'UNSET' ? 'UNSET' : SECRET_MASK);\n\n const effective: Record<SettingKey, SettingValue> = {};\n for (const [k, v] of Object.entries(result.effective)) {\n effective[k] = isSecret(k) ? makeSecretRef(k, isMeaningfullySet(v)) : v;\n }\n\n const provenance: EffectiveResult['provenance'] = {};\n for (const [k, p] of Object.entries(result.provenance)) {\n provenance[k] = isSecret(k)\n ? {\n ...p,\n value: makeSecretRef(k, isMeaningfullySet(p.value)),\n shadowed: p.shadowed.map((s) => ({ ...s, value: maskShadowValue(s.value) })),\n }\n : p;\n }\n\n const conflicts = result.conflicts.map((c) =>\n isSecret(c.key)\n ? { ...c, contributors: c.contributors.map((ct) => ({ ...ct, value: maskShadowValue(ct.value) })) }\n : c,\n );\n\n return { effective, provenance, conflicts };\n}\n\n/**\n * Partition a layer into the config values (safe to persist/query) and the secret values (to route\n * to a secret backend). Secret keys never appear in `config`. Pure.\n */\nexport function splitBySensitivity(\n layer: Layer,\n sensitivityFor: (key: SettingKey) => Sensitivity,\n): { config: Layer; secrets: Layer } {\n const config: Layer = {};\n const secrets: Layer = {};\n for (const [k, v] of Object.entries(layer)) {\n if (sensitivityFor(k) === 'secret') secrets[k] = v;\n else config[k] = v;\n }\n return { config, secrets };\n}\n\n/** Strip secret keys from a serialized layer before export/audit (they must never be written). */\nexport function redactSecretsFromLayer(\n serialized: SerializedLayer,\n sensitivityFor: (key: SettingKey) => Sensitivity,\n): SerializedLayer {\n const values: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(serialized.values ?? {})) {\n if (sensitivityFor(k) !== 'secret') values[k] = v;\n }\n const unset = (serialized.unset ?? []).filter((k) => sensitivityFor(k) !== 'secret');\n return { values, unset };\n}\n\n/**\n * The contract a secret backend implements (OS keychain, Vault, encrypted store). Mirrors zodal's\n * bifurcation \"content provider\". Reads return a masked ref; the plaintext is fetched only via an\n * explicit `reveal`. Implemented by satellite `@zodal/dials-store-*` packages.\n */\nexport interface SecretBackend {\n has(key: SettingKey): Promise<boolean>;\n get(key: SettingKey): Promise<SecretRef>;\n /** Explicit, audited plaintext reveal — separate from ordinary reads. */\n reveal(key: SettingKey): Promise<string | undefined>;\n set(key: SettingKey, value: string): Promise<SecretRef>;\n delete(key: SettingKey): Promise<void>;\n /** The keys this backend holds (so a bifurcation provider can surface masked refs on load). */\n list(): Promise<SettingKey[]>;\n}\n","/**\n * Linked validation — one model of relations over setting keys, evaluated to VALIDATE. Hard\n * constraints are authored in Zod (`.refine`/`.superRefine`, validated over the effective values)\n * and/or as a serializable `{ message, keys, check }` list (the NixOS `assertions`/`warnings` shape,\n * a mirror that could be exported to a CSP/SAT solver). Soft `warnings` are advisory and never fail.\n * Evaluation is fail-fast: it returns all errors so the UI can show them at resolve time.\n */\n\nimport type { z } from 'zod';\nimport type { SettingKey } from './model.js';\n\n/** A serializable hard constraint: a predicate over the effective values plus a message + the keys it concerns. */\nexport interface Assertion {\n id?: string;\n message: string;\n /** The keys this constraint relates (for highlighting + solver export). */\n keys?: SettingKey[];\n /** The predicate: returns true when satisfied. A throw counts as unsatisfied. */\n check: (values: Record<string, unknown>) => boolean;\n}\n\n/** A soft warning: advisory guidance shown when `when` holds. Never fails validation. */\nexport interface Warning {\n message: string;\n keys?: SettingKey[];\n when: (values: Record<string, unknown>) => boolean;\n}\n\nexport interface ConstraintsConfig {\n /** A Zod schema validated against the effective values (cross-field via `.superRefine`). */\n schema?: z.ZodType;\n /** Declarative hard constraints (serializable mirror; solver-exportable). */\n assertions?: Assertion[];\n /** Soft, advisory warnings. */\n warnings?: Warning[];\n}\n\nexport interface ConstraintError {\n message: string;\n keys: SettingKey[];\n}\n\nexport interface ConstraintResult {\n ok: boolean;\n errors: ConstraintError[];\n warnings: string[];\n}\n\n/** Evaluate constraints + warnings over a resolved values map. Pure; collects all errors. */\nexport function evaluateConstraints(\n values: Record<string, unknown>,\n config: ConstraintsConfig = {},\n): ConstraintResult {\n const errors: ConstraintError[] = [];\n const warnings: string[] = [];\n\n if (config.schema) {\n const r = config.schema.safeParse(values);\n if (!r.success) {\n for (const issue of r.error.issues) {\n errors.push({ message: issue.message, keys: issue.path.map((p) => String(p)) });\n }\n }\n }\n\n for (const a of config.assertions ?? []) {\n let satisfied = false;\n try {\n satisfied = a.check(values);\n } catch {\n satisfied = false;\n }\n if (!satisfied) errors.push({ message: a.message, keys: a.keys ?? [] });\n }\n\n for (const w of config.warnings ?? []) {\n let hit = false;\n try {\n hit = w.when(values);\n } catch {\n hit = false;\n }\n if (hit) warnings.push(w.message);\n }\n\n return { ok: errors.length === 0, errors, warnings };\n}\n","/**\n * Dependent (smart) defaults — the SUGGEST mode of the same field-relation model that constraints\n * use to VALIDATE. A dependent default computes an advisory value for one key from the current\n * values of others (\"C is usually near f(A,B), but any C is valid\"). It honors OVERRIDE-STICKINESS:\n * once the user has set the target key (it is dirty), its dependent default is never recomputed, so\n * a user's choice is never silently clobbered. Pure.\n */\n\nimport type { SettingKey } from './model.js';\n\nexport interface DependentDefault {\n /** The key this default applies to. */\n key: SettingKey;\n /** Keys this default depends on (documented; drives recompute scheduling in reactive consumers). */\n dependsOn: SettingKey[];\n /** Compute the suggested value from the current values. Return `undefined` to suggest nothing. */\n derive: (values: Record<string, unknown>) => unknown;\n}\n\nexport interface DeriveOptions {\n /** Keys the user has explicitly set (dirty) — their dependent defaults are NOT recomputed. */\n dirtyKeys?: Iterable<SettingKey>;\n}\n\nexport interface DeriveResult {\n values: Record<string, unknown>;\n /** Keys whose dependent default was applied this pass. */\n applied: SettingKey[];\n}\n\n/**\n * Fill dependent defaults into a values map, returning a new map. A dirty target key is never\n * overwritten (stickiness). Defaults are applied in declaration order, each seeing the results of\n * earlier ones.\n */\nexport function applyDependentDefaults(\n values: Record<string, unknown>,\n defaults: DependentDefault[],\n options: DeriveOptions = {},\n): DeriveResult {\n const dirty = new Set<SettingKey>(options.dirtyKeys ?? []);\n const out: Record<string, unknown> = { ...values };\n const applied: SettingKey[] = [];\n for (const d of defaults) {\n if (dirty.has(d.key)) continue;\n let suggestion: unknown;\n try {\n suggestion = d.derive(out);\n } catch {\n continue;\n }\n if (suggestion !== undefined) {\n out[d.key] = suggestion;\n applied.push(d.key);\n }\n }\n return { values: out, applied };\n}\n","/**\n * `defineDials` — the entry point. A settings document is modeled as a degenerate one-item zodal\n * collection, so the Zod schema is the single source of truth and zodal's `defineCollection` supplies\n * per-field affordances (for the UI layer). On top of that this binds the cascade engine: declared\n * defaults become the lowest scope, the type-directed merge strategy and sensitivity classification\n * are precomputed per key, and `resolve`/`explain`/`validate`/`withDependentDefaults` operate over an\n * ordered stack of scoped layers.\n */\n\nimport type { z } from 'zod';\nimport { defineCollection } from '@zodal/core';\nimport type { CollectionConfig, CollectionDefinition } from '@zodal/core';\nimport type {\n EffectiveResult,\n KeyProvenance,\n MergeStrategy,\n ScopedLayer,\n Sensitivity,\n SettingKey,\n} from './model.js';\nimport { resolve } from './cascade.js';\nimport type { ResolveOptions } from './cascade.js';\nimport { classifySensitivity, extractDefaults, getObjectShape, keyMergeStrategy } from './schema.js';\nimport { maskEffectiveResult } from './secrets.js';\nimport { evaluateConstraints } from './constraints.js';\nimport type { ConstraintResult, ConstraintsConfig } from './constraints.js';\nimport { applyDependentDefaults } from './derive.js';\nimport type { DependentDefault } from './derive.js';\n\nexport interface DefineDialsConfig<TSchema extends z.ZodObject<z.ZodRawShape> = z.ZodObject<z.ZodRawShape>> {\n /** Hard/soft constraints evaluated over the effective values. */\n constraints?: ConstraintsConfig;\n /** Dependent (smart) defaults. */\n dependentDefaults?: DependentDefault[];\n /** Passed through to `defineCollection` (escape hatch for affordance config). */\n collection?: CollectionConfig<z.infer<TSchema>>;\n}\n\nexport interface DialsResolveOptions {\n /** Prepend the schema defaults as the lowest scope. Default: true. */\n includeDefaults?: boolean;\n /** Replace secret effective values with a masked `SecretRef`. Default: false. */\n maskSecrets?: boolean;\n}\n\nexport interface DialsCapabilities {\n keyCount: number;\n hasSecrets: boolean;\n hasConstraints: boolean;\n hasDependentDefaults: boolean;\n mergeStrategies: Record<string, MergeStrategy>;\n}\n\nexport interface DialsDefinition<TSchema extends z.ZodObject<z.ZodRawShape>> {\n schema: TSchema;\n /** The underlying zodal collection (affordance source for the UI). `undefined` if the schema is\n * not a shape `defineCollection` accepts — the cascade does not depend on it. */\n collection: CollectionDefinition<TSchema> | undefined;\n /** The declared defaults (the lowest cascade layer). */\n defaults: Record<string, unknown>;\n keys: SettingKey[];\n mergeStrategyFor(key: SettingKey): MergeStrategy;\n sensitivityFor(key: SettingKey): Sensitivity;\n /** Resolve an ordered stack of scoped layers (lowest precedence first). */\n resolve(stack: ScopedLayer[], options?: DialsResolveOptions): EffectiveResult;\n /** Explain how one key resolved (its provenance), or undefined if no scope set it. */\n explain(key: SettingKey, stack: ScopedLayer[], options?: DialsResolveOptions): KeyProvenance | undefined;\n /** Evaluate constraints over a resolved values map. */\n validate(values: Record<string, unknown>): ConstraintResult;\n /** Fill dependent defaults into a values map, honoring dirty stickiness. */\n withDependentDefaults(values: Record<string, unknown>, dirtyKeys?: SettingKey[]): Record<string, unknown>;\n getCapabilities(): DialsCapabilities;\n}\n\n/** Define a settings surface from a Zod object schema. */\nexport function defineDials<TSchema extends z.ZodObject<z.ZodRawShape>>(\n schema: TSchema,\n config: DefineDialsConfig<TSchema> = {},\n): DialsDefinition<TSchema> {\n const shape = getObjectShape(schema);\n const keys = Object.keys(shape);\n const defaults = extractDefaults(schema);\n\n // Reuse zodal's affordance inference (a settings doc = a degenerate one-item collection). The\n // cascade never depends on this, so a schema defineCollection rejects must not break resolution.\n let collection: CollectionDefinition<TSchema> | undefined;\n try {\n collection = defineCollection(schema, config.collection);\n } catch {\n collection = undefined;\n }\n\n const mergeCache = new Map<string, MergeStrategy>();\n const sensCache = new Map<string, Sensitivity>();\n\n const mergeStrategyFor = (key: SettingKey): MergeStrategy => {\n if (mergeCache.has(key)) return mergeCache.get(key) as MergeStrategy;\n const value = shape[key] ? keyMergeStrategy(shape[key]) : 'replace';\n mergeCache.set(key, value);\n return value;\n };\n // Out-of-schema (ad-hoc) layer keys still get name-classified (fail-safe toward secret) rather\n // than defaulting to public — `classifySensitivity` falls back to the heuristic when no field.\n const sensitivityFor = (key: SettingKey): Sensitivity => {\n if (sensCache.has(key)) return sensCache.get(key) as Sensitivity;\n const value = classifySensitivity(key, shape[key]);\n sensCache.set(key, value);\n return value;\n };\n\n const buildStack = (stack: ScopedLayer[], options: DialsResolveOptions): ScopedLayer[] =>\n options.includeDefaults === false ? stack : [{ scope: 'default', layer: defaults }, ...stack];\n\n const doResolve = (stack: ScopedLayer[], options: DialsResolveOptions = {}): EffectiveResult => {\n const resolveOptions: ResolveOptions = { strategyFor: mergeStrategyFor };\n const result = resolve(buildStack(stack, options), resolveOptions);\n return options.maskSecrets ? maskEffectiveResult(result, sensitivityFor) : result;\n };\n\n return {\n schema,\n collection,\n defaults,\n keys,\n mergeStrategyFor,\n sensitivityFor,\n resolve: doResolve,\n explain: (key, stack, options) => doResolve(stack, options).provenance[key],\n validate: (values) => evaluateConstraints(values, config.constraints),\n withDependentDefaults: (values, dirtyKeys) =>\n applyDependentDefaults(values, config.dependentDefaults ?? [], { dirtyKeys }).values,\n getCapabilities: () => {\n const mergeStrategies: Record<string, MergeStrategy> = {};\n for (const k of keys) mergeStrategies[k] = mergeStrategyFor(k);\n return {\n keyCount: keys.length,\n hasSecrets: keys.some((k) => sensitivityFor(k) === 'secret'),\n hasConstraints: Boolean(config.constraints?.schema || config.constraints?.assertions?.length),\n hasDependentDefaults: (config.dependentDefaults?.length ?? 0) > 0,\n mergeStrategies,\n };\n },\n };\n}\n"],"mappings":"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","names":["PROTO_KEYS","import_core"]}
@@ -0,0 +1,457 @@
1
+ import { z } from 'zod';
2
+ import { CollectionConfig, CollectionDefinition } from '@zodal/core';
3
+
4
+ /**
5
+ * Core model types for zodal-dials: settings, layers, scopes, the cascade result, and provenance.
6
+ *
7
+ * A SETTING is a typed named parameter identified by a stable dotted-path KEY. A LAYER is a partial
8
+ * map of key -> value (or the UNSET sentinel) from one source. A SCOPE names an ordered source of
9
+ * layers; the CASCADE merges an ordered stack of scoped layers into an EFFECTIVE value per key,
10
+ * paired with PROVENANCE (which scope won, what it shadowed, whether it is policy-managed).
11
+ *
12
+ * UNSET is the explicit deletion/reset sentinel — never raw `null` — so a layer can declare "I do
13
+ * not contribute this key" (re-exposing a lower scope) without colliding with a legitimate `null`
14
+ * value. See `docs/zodal-dials-concept.md` and `docs/dev-plan.md` §4.
15
+ */
16
+ /** A setting's stable, serialization-independent identity (a dotted path, e.g. "editor.fontSize"). */
17
+ type SettingKey = string;
18
+ /** A setting value: a scalar, an array, or an irreducible nested object. */
19
+ type SettingValue = unknown;
20
+ /**
21
+ * The deletion/reset sentinel. In a layer, `UNSET` marks a key as explicitly not contributed by
22
+ * that scope, re-exposing the value from a lower-precedence scope (or its absence). Distinct from
23
+ * `null`/`undefined`, which are legitimate setting values. Uses a registered symbol so it survives
24
+ * realm boundaries and bundler duplication.
25
+ */
26
+ declare const UNSET: unique symbol;
27
+ type Unset = typeof UNSET;
28
+ /** Type guard for the UNSET sentinel. */
29
+ declare function isUnset(v: unknown): v is Unset;
30
+ /** A partial/sparse set of setting values from one source — the unit that gets merged. */
31
+ type Layer = Record<SettingKey, SettingValue | Unset>;
32
+ /**
33
+ * A named layer in the cascade. Scopes are DATA, not constants: the resolver is handed an ordered
34
+ * stack (lowest precedence first) and never hardcodes the ladder. `managed: true` elevates a layer
35
+ * into the policy band — it wins over every non-managed layer regardless of position and marks the
36
+ * resulting value as non-overridable (the UI should lock the control).
37
+ */
38
+ interface ScopedLayer {
39
+ /** The scope id this layer comes from (e.g. "default", "preset", "profile", "user", "policy"). */
40
+ scope: string;
41
+ /** The (sparse) layer of values. */
42
+ layer: Layer;
43
+ /** Policy/managed band: wins over all non-managed layers and marks the value non-overridable. */
44
+ managed?: boolean;
45
+ }
46
+ /** Per-key merge strategy. Type-directed by default; `.meta({ mergeStrategy })`-overridable. */
47
+ type MergeStrategy = 'replace' | 'deep-merge' | 'append';
48
+ /** A lower layer that set a key but did not win (or that explicitly UNSET it). */
49
+ interface ShadowedLayer {
50
+ scope: string;
51
+ /** The shadowed value, or 'UNSET' if that layer reset the key. */
52
+ value: SettingValue | 'UNSET';
53
+ managed: boolean;
54
+ }
55
+ /**
56
+ * The cascade's first-class explanation of an effective value: which scope won, how the value was
57
+ * produced, what it shadowed, whether it is policy-managed, and (for deep-merged objects) which
58
+ * scopes contributed. This is the deliberate differentiator — provenance is never a debug
59
+ * afterthought.
60
+ */
61
+ interface KeyProvenance {
62
+ key: SettingKey;
63
+ /** The scope id that supplied the effective value. */
64
+ winningScope: string;
65
+ /** The resolved value (also present in `effective`). */
66
+ value: SettingValue;
67
+ /** True when the winning scope is in the managed/policy band (control should be locked). */
68
+ managed: boolean;
69
+ /** How the value was produced from the contributing layers. */
70
+ mergeStrategy: MergeStrategy;
71
+ /** Other layers that set this key, highest precedence first (each value or 'UNSET'). */
72
+ shadowed: ShadowedLayer[];
73
+ /** For deep-merged object values: the scope ids whose objects were merged, low -> high. */
74
+ mergedFrom?: string[];
75
+ }
76
+ /** A key set to differing values by more than one layer (surfaced for the UI). */
77
+ interface Conflict {
78
+ key: SettingKey;
79
+ /** Contributing scopes (highest precedence first) with their values. */
80
+ contributors: Array<{
81
+ scope: string;
82
+ value: SettingValue | 'UNSET';
83
+ managed: boolean;
84
+ }>;
85
+ /** True when a managed/policy scope overrode a differing non-managed value. */
86
+ overriddenByPolicy: boolean;
87
+ }
88
+ /** The complete result of resolving a cascade. */
89
+ interface EffectiveResult {
90
+ /** The resolved value per key (keys that resolve to UNSET/absent are omitted). */
91
+ effective: Record<SettingKey, SettingValue>;
92
+ /** Provenance per resolved key. */
93
+ provenance: Record<SettingKey, KeyProvenance>;
94
+ /** Keys set by multiple layers to differing values (informational). */
95
+ conflicts: Conflict[];
96
+ }
97
+ /** Sensitivity classification of a setting. */
98
+ type Sensitivity = 'public' | 'sensitive' | 'secret';
99
+ /**
100
+ * A masked reference to a secret value — mirrors zodal's `ContentRef`. Reads of a secret setting
101
+ * return a `SecretRef`, never plaintext; the plaintext is fetched via an explicit reveal call.
102
+ */
103
+ interface SecretRef {
104
+ readonly _tag: 'SecretRef';
105
+ /** The setting key this secret belongs to. */
106
+ key: SettingKey;
107
+ /** Whether a value is set (without revealing it). */
108
+ isSet: boolean;
109
+ /** A display mask, e.g. "•••• (set)" or "not set". */
110
+ masked: string;
111
+ }
112
+ /** Type guard for SecretRef. */
113
+ declare function isSecretRef(v: unknown): v is SecretRef;
114
+
115
+ /**
116
+ * The cascade resolver: merge an ordered stack of scoped layers (lowest precedence first) into an
117
+ * effective value per key, paired with provenance.
118
+ *
119
+ * Precedence is the stack order, with managed/policy layers elevated into a top band that wins over
120
+ * every non-managed layer regardless of position (and marks the value non-overridable). The UNSET
121
+ * sentinel is a fall-through: a layer that UNSETs a key ABSTAINS (contributes nothing for it),
122
+ * re-exposing the next lower scope — at the top of a stack this is exactly "reset to default". Under
123
+ * `deep-merge`/`append`, an abstaining (UNSET) layer simply does not participate in the merge; it
124
+ * does NOT sever the contributors below it — to override a lower object wholesale, use the `replace`
125
+ * strategy or set the full value. Provenance reports the winning scope, the shadowed layers
126
+ * (including higher resets), the merge strategy used, and — for merges — only the scopes that
127
+ * actually changed the result (`mergedFrom`, computed by leave-one-out, so fully-shadowed scopes are
128
+ * not falsely claimed).
129
+ */
130
+
131
+ interface ResolveOptions {
132
+ /** Per-key merge strategy resolver. Default: 'replace' for every key. */
133
+ strategyFor?: (key: SettingKey) => MergeStrategy;
134
+ }
135
+ /** Resolve a stack of scoped layers (lowest precedence first) into effective values + provenance. */
136
+ declare function resolve(stack: ScopedLayer[], options?: ResolveOptions): EffectiveResult;
137
+
138
+ /**
139
+ * Value combination under a merge strategy. Given an ordered (low -> high precedence) list of
140
+ * contributing values for one key, produce the merged value: `replace` (highest wins), `deep-merge`
141
+ * (structural merge of object contributors), or `append` (concatenate array contributors). Pure.
142
+ */
143
+
144
+ /** Combine an ordered (low -> high precedence) list of contributing values under a strategy. */
145
+ declare function mergeValues(values: SettingValue[], strategy: MergeStrategy): SettingValue;
146
+
147
+ /**
148
+ * Patch & serialization utilities for layers.
149
+ *
150
+ * - RFC 7386 JSON Merge Patch (`applyMergePatch`) — the ergonomic, mirror-shaped delta format.
151
+ * - Lossless layer serialization (`serializeLayer`/`deserializeLayer`) — encodes the UNSET sentinel
152
+ * separately from values so a layer round-trips with zero drift (UNSET is NOT conflated with a
153
+ * literal `null`, the standard RFC 7386 footgun).
154
+ * - `layerToMergePatch` — a layer AS a standard RFC 7386 patch (UNSET -> null) for interop (lossy
155
+ * only for the rare literal-`null` value).
156
+ * - RFC 6902 JSON Patch (`applyJsonPatch`) + `diffJsonPatch` + `invertJsonPatch` — for history/undo.
157
+ *
158
+ * All functions are pure; inputs are never mutated. Pointer traversal rejects the prototype-polluting
159
+ * keys `__proto__`/`constructor`/`prototype`, and array indices are validated per RFC 6902 §4.
160
+ */
161
+
162
+ /** Apply an RFC 7386 JSON Merge Patch to a target. `null` in the patch deletes a member. Pure. */
163
+ declare function applyMergePatch(target: unknown, patch: unknown): unknown;
164
+ /** The on-disk/wire shape of a layer: values plus an explicit list of reset (UNSET) keys. */
165
+ interface SerializedLayer {
166
+ values: Record<string, unknown>;
167
+ unset: string[];
168
+ }
169
+ /** Serialize a layer losslessly (UNSET keys recorded separately from values). */
170
+ declare function serializeLayer(layer: Layer): SerializedLayer;
171
+ /** Deserialize a layer produced by `serializeLayer` (the inverse; round-trips with zero drift). */
172
+ declare function deserializeLayer(s: SerializedLayer): Layer;
173
+ /**
174
+ * Express a layer as a standard RFC 7386 merge patch (UNSET -> null). Lossy ONLY for the rare case
175
+ * of a setting whose legitimate value is `null` (indistinguishable from a reset under RFC 7386); use
176
+ * `serializeLayer` when that distinction must be preserved.
177
+ */
178
+ declare function layerToMergePatch(layer: Layer): Record<string, unknown>;
179
+ /** An RFC 6902 JSON Patch operation. */
180
+ type JsonPatchOp = {
181
+ op: 'add';
182
+ path: string;
183
+ value: unknown;
184
+ } | {
185
+ op: 'remove';
186
+ path: string;
187
+ } | {
188
+ op: 'replace';
189
+ path: string;
190
+ value: unknown;
191
+ } | {
192
+ op: 'move';
193
+ from: string;
194
+ path: string;
195
+ } | {
196
+ op: 'copy';
197
+ from: string;
198
+ path: string;
199
+ } | {
200
+ op: 'test';
201
+ path: string;
202
+ value: unknown;
203
+ };
204
+ /** Apply an ordered RFC 6902 JSON Patch to a document. Pure; throws on a failed `test` or bad path. */
205
+ declare function applyJsonPatch(doc: unknown, ops: JsonPatchOp[]): unknown;
206
+ /**
207
+ * Compute an RFC 6902 patch turning `before` into `after`, at object-member granularity (recursing
208
+ * into nested plain objects; arrays and scalars are replaced wholesale). The patches we generate are
209
+ * always add/remove/replace, which `invertJsonPatch` inverts exactly — ideal for settings history.
210
+ */
211
+ declare function diffJsonPatch(before: unknown, after: unknown, basePath?: string): JsonPatchOp[];
212
+ /**
213
+ * Produce the inverse of a patch (relative to the document it was applied to), so applying the
214
+ * inverse undoes it. Exact for add/remove/replace/test/copy; `move` is inverted as a reverse move.
215
+ */
216
+ declare function invertJsonPatch(ops: JsonPatchOp[], before: unknown): JsonPatchOp[];
217
+
218
+ /**
219
+ * Zod v4 introspection helpers for settings schemas: reading the object shape, per-field `.meta()`
220
+ * (unwrapping wrappers so wrapped metadata survives), extracting declared defaults (robustly, by
221
+ * parsing `undefined` — no reliance on private internals), the type-directed merge strategy, and the
222
+ * sensitivity classification (name heuristics + `.meta()` override). Uses `@zodal/core` helpers
223
+ * where they apply and stable `instanceof` checks for base typing.
224
+ */
225
+
226
+ /** Get the field map of a Zod object schema. Prefers `_zod.def.shape` (the workspace Zod-v4 rule),
227
+ * falling back to the public `.shape`. */
228
+ declare function getObjectShape(schema: z.ZodObject<z.ZodRawShape>): Record<string, z.ZodType>;
229
+ /** Read a field's `.meta()` metadata, unwrapping wrappers so metadata on the inner schema is found. */
230
+ declare function readMeta(schema: z.ZodType): Record<string, unknown>;
231
+ /** The stable base type of a field (after unwrapping optional/default/nullable). */
232
+ declare function baseType(field: z.ZodType): string;
233
+ /** Extract the per-key default values declared in the schema (the lowest cascade layer). */
234
+ declare function extractDefaults(schema: z.ZodObject<z.ZodRawShape>): Record<string, unknown>;
235
+ /** The type-directed default merge strategy for a field, overridable via `.meta({ mergeStrategy })`. */
236
+ declare function keyMergeStrategy(field: z.ZodType): MergeStrategy;
237
+ /**
238
+ * Classify a setting's sensitivity: `.meta()` override first, then field-name heuristics, then — for
239
+ * a container-valued setting (object/array/record/tuple/union) — a fail-safe recursion: if any
240
+ * nested field is secret, the WHOLE setting is classified `secret`, so masking/redaction protects
241
+ * the nested value (an irreducible nested value cannot be masked field-by-field). `field` is optional
242
+ * so out-of-schema (ad-hoc) layer keys are still name-classified rather than defaulting to `public`.
243
+ */
244
+ declare function classifySensitivity(key: string, field?: z.ZodType): Sensitivity;
245
+
246
+ /**
247
+ * Secret handling — the model-side of zodal's content/metadata bifurcation applied to settings.
248
+ *
249
+ * A setting classified `secret` (by name heuristic or `.meta({ secret: true })`) must never appear
250
+ * in the queryable config store, an exported layer/patch, or an audit log as plaintext. These pure
251
+ * helpers enforce that: `splitBySensitivity` routes secret values out of the config layer to a
252
+ * secret backend; `redactSecretsFromLayer` strips them from anything serialized for export/audit;
253
+ * `maskSecrets` replaces secret effective values with a masked `SecretRef`. The concrete secret
254
+ * backend (OS keychain / Vault / encrypted store) is a satellite store package; here we define the
255
+ * seam and the guarantees.
256
+ */
257
+
258
+ /** Build a masked reference to a secret value (never carries the plaintext). */
259
+ declare function makeSecretRef(key: SettingKey, isSet: boolean): SecretRef;
260
+ /**
261
+ * Replace the effective values of secret settings with a masked `SecretRef`. Pure. The plaintext is
262
+ * never copied into the output — reveal is an explicit, separate operation against the secret backend.
263
+ */
264
+ declare function maskSecrets(effective: Record<SettingKey, SettingValue>, sensitivityFor: (key: SettingKey) => Sensitivity): Record<SettingKey, SettingValue>;
265
+ /**
266
+ * Mask EVERY surface of a resolved result that could carry a secret as plaintext: `effective`,
267
+ * `provenance[key].value`, every `provenance[key].shadowed[].value`, and `conflicts[].contributors[].value`.
268
+ * This is the function `defineDials.resolve({ maskSecrets: true })` and `explain()` go through — so
269
+ * the audit/provenance path can never leak. Pure.
270
+ */
271
+ declare function maskEffectiveResult(result: EffectiveResult, sensitivityFor: (key: SettingKey) => Sensitivity): EffectiveResult;
272
+ /**
273
+ * Partition a layer into the config values (safe to persist/query) and the secret values (to route
274
+ * to a secret backend). Secret keys never appear in `config`. Pure.
275
+ */
276
+ declare function splitBySensitivity(layer: Layer, sensitivityFor: (key: SettingKey) => Sensitivity): {
277
+ config: Layer;
278
+ secrets: Layer;
279
+ };
280
+ /** Strip secret keys from a serialized layer before export/audit (they must never be written). */
281
+ declare function redactSecretsFromLayer(serialized: SerializedLayer, sensitivityFor: (key: SettingKey) => Sensitivity): SerializedLayer;
282
+ /**
283
+ * The contract a secret backend implements (OS keychain, Vault, encrypted store). Mirrors zodal's
284
+ * bifurcation "content provider". Reads return a masked ref; the plaintext is fetched only via an
285
+ * explicit `reveal`. Implemented by satellite `@zodal/dials-store-*` packages.
286
+ */
287
+ interface SecretBackend {
288
+ has(key: SettingKey): Promise<boolean>;
289
+ get(key: SettingKey): Promise<SecretRef>;
290
+ /** Explicit, audited plaintext reveal — separate from ordinary reads. */
291
+ reveal(key: SettingKey): Promise<string | undefined>;
292
+ set(key: SettingKey, value: string): Promise<SecretRef>;
293
+ delete(key: SettingKey): Promise<void>;
294
+ /** The keys this backend holds (so a bifurcation provider can surface masked refs on load). */
295
+ list(): Promise<SettingKey[]>;
296
+ }
297
+
298
+ /**
299
+ * Linked validation — one model of relations over setting keys, evaluated to VALIDATE. Hard
300
+ * constraints are authored in Zod (`.refine`/`.superRefine`, validated over the effective values)
301
+ * and/or as a serializable `{ message, keys, check }` list (the NixOS `assertions`/`warnings` shape,
302
+ * a mirror that could be exported to a CSP/SAT solver). Soft `warnings` are advisory and never fail.
303
+ * Evaluation is fail-fast: it returns all errors so the UI can show them at resolve time.
304
+ */
305
+
306
+ /** A serializable hard constraint: a predicate over the effective values plus a message + the keys it concerns. */
307
+ interface Assertion {
308
+ id?: string;
309
+ message: string;
310
+ /** The keys this constraint relates (for highlighting + solver export). */
311
+ keys?: SettingKey[];
312
+ /** The predicate: returns true when satisfied. A throw counts as unsatisfied. */
313
+ check: (values: Record<string, unknown>) => boolean;
314
+ }
315
+ /** A soft warning: advisory guidance shown when `when` holds. Never fails validation. */
316
+ interface Warning {
317
+ message: string;
318
+ keys?: SettingKey[];
319
+ when: (values: Record<string, unknown>) => boolean;
320
+ }
321
+ interface ConstraintsConfig {
322
+ /** A Zod schema validated against the effective values (cross-field via `.superRefine`). */
323
+ schema?: z.ZodType;
324
+ /** Declarative hard constraints (serializable mirror; solver-exportable). */
325
+ assertions?: Assertion[];
326
+ /** Soft, advisory warnings. */
327
+ warnings?: Warning[];
328
+ }
329
+ interface ConstraintError {
330
+ message: string;
331
+ keys: SettingKey[];
332
+ }
333
+ interface ConstraintResult {
334
+ ok: boolean;
335
+ errors: ConstraintError[];
336
+ warnings: string[];
337
+ }
338
+ /** Evaluate constraints + warnings over a resolved values map. Pure; collects all errors. */
339
+ declare function evaluateConstraints(values: Record<string, unknown>, config?: ConstraintsConfig): ConstraintResult;
340
+
341
+ /**
342
+ * Dependent (smart) defaults — the SUGGEST mode of the same field-relation model that constraints
343
+ * use to VALIDATE. A dependent default computes an advisory value for one key from the current
344
+ * values of others ("C is usually near f(A,B), but any C is valid"). It honors OVERRIDE-STICKINESS:
345
+ * once the user has set the target key (it is dirty), its dependent default is never recomputed, so
346
+ * a user's choice is never silently clobbered. Pure.
347
+ */
348
+
349
+ interface DependentDefault {
350
+ /** The key this default applies to. */
351
+ key: SettingKey;
352
+ /** Keys this default depends on (documented; drives recompute scheduling in reactive consumers). */
353
+ dependsOn: SettingKey[];
354
+ /** Compute the suggested value from the current values. Return `undefined` to suggest nothing. */
355
+ derive: (values: Record<string, unknown>) => unknown;
356
+ }
357
+ interface DeriveOptions {
358
+ /** Keys the user has explicitly set (dirty) — their dependent defaults are NOT recomputed. */
359
+ dirtyKeys?: Iterable<SettingKey>;
360
+ }
361
+ interface DeriveResult {
362
+ values: Record<string, unknown>;
363
+ /** Keys whose dependent default was applied this pass. */
364
+ applied: SettingKey[];
365
+ }
366
+ /**
367
+ * Fill dependent defaults into a values map, returning a new map. A dirty target key is never
368
+ * overwritten (stickiness). Defaults are applied in declaration order, each seeing the results of
369
+ * earlier ones.
370
+ */
371
+ declare function applyDependentDefaults(values: Record<string, unknown>, defaults: DependentDefault[], options?: DeriveOptions): DeriveResult;
372
+
373
+ /**
374
+ * `defineDials` — the entry point. A settings document is modeled as a degenerate one-item zodal
375
+ * collection, so the Zod schema is the single source of truth and zodal's `defineCollection` supplies
376
+ * per-field affordances (for the UI layer). On top of that this binds the cascade engine: declared
377
+ * defaults become the lowest scope, the type-directed merge strategy and sensitivity classification
378
+ * are precomputed per key, and `resolve`/`explain`/`validate`/`withDependentDefaults` operate over an
379
+ * ordered stack of scoped layers.
380
+ */
381
+
382
+ interface DefineDialsConfig<TSchema extends z.ZodObject<z.ZodRawShape> = z.ZodObject<z.ZodRawShape>> {
383
+ /** Hard/soft constraints evaluated over the effective values. */
384
+ constraints?: ConstraintsConfig;
385
+ /** Dependent (smart) defaults. */
386
+ dependentDefaults?: DependentDefault[];
387
+ /** Passed through to `defineCollection` (escape hatch for affordance config). */
388
+ collection?: CollectionConfig<z.infer<TSchema>>;
389
+ }
390
+ interface DialsResolveOptions {
391
+ /** Prepend the schema defaults as the lowest scope. Default: true. */
392
+ includeDefaults?: boolean;
393
+ /** Replace secret effective values with a masked `SecretRef`. Default: false. */
394
+ maskSecrets?: boolean;
395
+ }
396
+ interface DialsCapabilities {
397
+ keyCount: number;
398
+ hasSecrets: boolean;
399
+ hasConstraints: boolean;
400
+ hasDependentDefaults: boolean;
401
+ mergeStrategies: Record<string, MergeStrategy>;
402
+ }
403
+ interface DialsDefinition<TSchema extends z.ZodObject<z.ZodRawShape>> {
404
+ schema: TSchema;
405
+ /** The underlying zodal collection (affordance source for the UI). `undefined` if the schema is
406
+ * not a shape `defineCollection` accepts — the cascade does not depend on it. */
407
+ collection: CollectionDefinition<TSchema> | undefined;
408
+ /** The declared defaults (the lowest cascade layer). */
409
+ defaults: Record<string, unknown>;
410
+ keys: SettingKey[];
411
+ mergeStrategyFor(key: SettingKey): MergeStrategy;
412
+ sensitivityFor(key: SettingKey): Sensitivity;
413
+ /** Resolve an ordered stack of scoped layers (lowest precedence first). */
414
+ resolve(stack: ScopedLayer[], options?: DialsResolveOptions): EffectiveResult;
415
+ /** Explain how one key resolved (its provenance), or undefined if no scope set it. */
416
+ explain(key: SettingKey, stack: ScopedLayer[], options?: DialsResolveOptions): KeyProvenance | undefined;
417
+ /** Evaluate constraints over a resolved values map. */
418
+ validate(values: Record<string, unknown>): ConstraintResult;
419
+ /** Fill dependent defaults into a values map, honoring dirty stickiness. */
420
+ withDependentDefaults(values: Record<string, unknown>, dirtyKeys?: SettingKey[]): Record<string, unknown>;
421
+ getCapabilities(): DialsCapabilities;
422
+ }
423
+ /** Define a settings surface from a Zod object schema. */
424
+ declare function defineDials<TSchema extends z.ZodObject<z.ZodRawShape>>(schema: TSchema, config?: DefineDialsConfig<TSchema>): DialsDefinition<TSchema>;
425
+
426
+ /**
427
+ * The `LayerStore` contract — how a scope sources (and optionally persists) its layer. The cascade
428
+ * resolves an ordered stack of `{ scope, layer }`; a `LayerStore` is what produces one scope's layer
429
+ * from a backing source (env vars, a JSONC/TOML/YAML file, a remote store, a secret backend, …).
430
+ * Concrete adapters live in satellite `@zodal/dials-store-*` packages; this is just the interface +
431
+ * an honest capability report. Pure types — no runtime/Node dependency.
432
+ */
433
+
434
+ /** What a store can do, reported honestly (mirrors the spirit of zodal `ProviderCapabilities`). */
435
+ interface LayerStoreCapabilities {
436
+ /** Can produce a layer via `load()`. */
437
+ readable: boolean;
438
+ /** Can persist a layer via `save()`. */
439
+ writable: boolean;
440
+ /** Can notify on external change via `subscribe()`. */
441
+ watchable: boolean;
442
+ }
443
+ /** A source/sink for a single scope's layer. */
444
+ interface LayerStore {
445
+ /** The scope id this store provides a layer for (e.g. 'env', 'user', 'workspace'). */
446
+ readonly scope: string;
447
+ /** Honest capability report. */
448
+ getCapabilities(): LayerStoreCapabilities;
449
+ /** Load the current layer from the backing source. */
450
+ load(): Promise<Layer>;
451
+ /** Persist a layer to the backing source (present only when `writable`). */
452
+ save?(layer: Layer): Promise<void>;
453
+ /** Subscribe to external changes (present only when `watchable`); returns an unsubscribe function. */
454
+ subscribe?(onChange: (layer: Layer) => void): () => void;
455
+ }
456
+
457
+ export { type Assertion, type Conflict, type ConstraintError, type ConstraintResult, type ConstraintsConfig, type DefineDialsConfig, type DependentDefault, type DeriveOptions, type DeriveResult, type DialsCapabilities, type DialsDefinition, type DialsResolveOptions, type EffectiveResult, type JsonPatchOp, type KeyProvenance, type Layer, type LayerStore, type LayerStoreCapabilities, type MergeStrategy, type ResolveOptions, type ScopedLayer, type SecretBackend, type SecretRef, type Sensitivity, type SerializedLayer, type SettingKey, type SettingValue, type ShadowedLayer, UNSET, type Unset, type Warning, applyDependentDefaults, applyJsonPatch, applyMergePatch, baseType, classifySensitivity, defineDials, deserializeLayer, diffJsonPatch, evaluateConstraints, extractDefaults, getObjectShape, invertJsonPatch, isSecretRef, isUnset, keyMergeStrategy, layerToMergePatch, makeSecretRef, maskEffectiveResult, maskSecrets, mergeValues, readMeta, redactSecretsFromLayer, resolve, serializeLayer, splitBySensitivity };