/** * Deterministic schema validation for `.openlore/config.json` * (changes: add-config-schema-validation, fix-config-validation-completeness). * * `readOpenLoreConfig` parses the file with a bare `JSON.parse(...) as OpenLoreConfig` * — a type *assertion* checked by nothing at runtime. A typo'd key (`pancResponse`, * `embeding`) is silently dropped and the default wins, so the user believes a feature * is configured when it is not. This module closes that gap the way the rest of the * substrate already does (the decision store validates on load, the index attests * integrity): an allocation-light, dependency-free validator that recursively checks * every declared field. Unknown keys and version skew remain advisory; missing required * fields and known fields with unusable types are rejected at the read boundary before a * caller can dereference them. * * Two honesty invariants: * - Forward-compatible: an unknown key (including one written by a *newer* OpenLore) is * disclosed and then ignored, so a newer config under an older openlore degrades * gracefully rather than crashing. * - Bound to the type: {@link CONFIG_FIELD_KINDS} is `Record`, * so adding a field to `OpenLoreConfig` without a validator entry fails the build; a * completeness test names any residual drift. */ import type { OpenLoreConfig } from '../../types/index.js'; /** The current config-schema version stamped into `.openlore/config.json`. */ export declare const CONFIG_SCHEMA_VERSION = "1.2.0"; /** * Top-level value shapes retained as the public compatibility map for callers and tests. * The recursive schema below refines object fields without changing this exported shape. */ export type ConfigFieldKind = 'string' | 'string-or-null' | 'boolean' | 'object'; /** * The known keys of `OpenLoreConfig` and the shape each holds. Typed as * `Record` so a field added to the interface without an entry * here fails `tsc` (and CI); {@link config-schema.test.ts} binds it at runtime too. */ export declare const CONFIG_FIELD_KINDS: Record; /** The known top-level config keys, derived from the type-bound field map. */ export declare const KNOWN_CONFIG_KEYS: readonly string[]; /** * A registered non-additive config-schema change: reading a config stamped *before* * `since` should disclose that `fields` need attention (rename/removal). Empty today — * the schema has only ever grown with optional, forward- and backward-compatible fields, * so no older config is misread. An entry is added here (and {@link CONFIG_SCHEMA_VERSION} * bumped) only when a breaking shape change lands. */ export interface ConfigMigration { /** The version at which the breaking change landed (semver). */ since: string; /** The affected fields, for the recovery message. */ fields: string[]; /** Human recovery guidance. */ note: string; } export declare const CONFIG_MIGRATIONS: readonly ConfigMigration[]; /** A single deterministic finding from validating a config object. */ export interface ConfigValidationFinding { kind: 'unknown-key' | 'missing-required' | 'type-mismatch' | 'version-older' | 'version-newer' | 'default-added' | 'value-clamped'; /** The offending key, when the finding is about one. */ key?: string; /** Human-readable message. */ message: string; /** For unknown-key: the closest known key within the edit-distance bound, if any. */ suggestion?: string; /** Whether returning the parsed object would expose an unsafe required runtime shape. */ fatal?: boolean; } export interface ConfigCompatibilityResult { config: unknown; findings: ConfigValidationFinding[]; } /** * Backfill only required fields explicitly marked as upgrade-safe. Nested sections * must already exist in the parsed config; marked top-level fields may be introduced. * This keeps malformed legacy-required structure fatal while allowing deliberate, * default-backed schema additions to remain backward compatible. */ export declare function backfillRequiredConfigDefaults(parsed: unknown, defaults: unknown): ConfigCompatibilityResult; /** * Required fields missing from sections emitted by the canonical defaults. This is a * schema-evolution guard: adding a required field to a default section must update the * defaults in the same change, otherwise older configs cannot be backfilled safely. */ export declare function findRequiredFieldsWithoutDefaults(defaults: unknown): string[]; /** Upgrade-safe fields whose canonical fallback is absent at the same schema path. */ export declare function findCompatibilityFieldsWithoutDefaults(defaults: unknown): string[]; /** Findings that make the parsed object unsafe to expose as `OpenLoreConfig`. */ export declare function isFatalConfigFinding(finding: ConfigValidationFinding): boolean; /** * The closest known key to `unknown` within {@link MAX_SUGGESTION_DISTANCE}, or undefined. * Ties broken alphabetically so the suggestion is deterministic. */ export declare function suggestKey(unknown: string, knownKeys: readonly string[]): string | undefined; /** * Check the `version` stamp against the running schema version. A newer stamp is * disclosed (unknown content is handled by the unknown-key path); an older stamp is * reported only when a registered {@link ConfigMigration} affects the (stamp, current] * range — a purely additive gap stays silent because the config is still forward- and * backward-compatible. Never a hard failure. Exported so the pure version logic is * testable with an injected current version / migration set. */ export declare function checkConfigVersion(stamp: unknown, opts?: { current?: string; migrations?: readonly ConfigMigration[]; }): ConfigValidationFinding[]; /** * Validate a parsed config object against the type-derived schema. Pure and * deterministic: returns findings ordered as unknown keys, missing required fields, * type mismatches, then version skew. Never throws or mutates; the config read boundary * decides which findings are fatal. */ export declare function validateOpenLoreConfig(parsed: unknown, opts?: { current?: string; migrations?: readonly ConfigMigration[]; }): ConfigValidationFinding[]; //# sourceMappingURL=config-schema.d.ts.map