/** * The catalogue's shape (UIG-12, seam S2 in docs/GATES.md §16). * * A **data source read by machines first** and rendered second — not a * documentation generator. The skill (UIG-20), the copy detector (UIG-25) and * the merged search (UIG-19) all read this and nothing else, so the field * names are the contract and `schemaVersion` is what makes a change to them * detectable. * * Names are plain and ours. Open-sourcing is far off; when it comes, a * renaming pass is one version bump, and guessing at a public vocabulary now * would cost every reader between here and there. */ /** * Raise this whenever a field is added, removed or given a new meaning. A * reader that knows version N and is handed N+1 must be able to say so rather * than silently read a field that moved. */ export declare const SCHEMA_VERSION = 2; /** What a name is, which decides how a reader offers it. */ export type EntryKind = /** Draws something. Used in JSX. */ 'component' /** A React hook: `use…`, callable only inside a component. */ | 'hook' /** A function or a constant. Neither of the above. */ | 'helper'; /** Where an entry's one-line purpose was read from. */ export type PurposeSource = /** The `.mdx` page's opening paragraph — the name has a page of its own. */ 'page' /** The doc comment above the export — the name is documented on a sibling's page. */ | 'comment' /** An app's file that holds this one part: the comment at the top of the file (UIG-13). */ | 'file' /** An app's pass-on: the part is the package's, and says so (UIG-13). */ | 'package'; /** * Which of four kinds an app's part is (UIG-13), or that nothing uses it. * * Worked out from the code, never from the folder a file sits in: Peek's * `components/ui` holds one-line pass-ons beside 187-line components. */ export type EntryClass = /** Its file only hands a package part on, so the app keeps its own import path. */ 're-export' /** Used in two or more places in the app. Gets a usage page (UIG-17, UIG-18). */ | 'reusable' /** Used in one place. Needs only its purpose line. */ | 'one-off' /** Reusable, and everything it uses is already in the package: it could move there. Katerina rules. */ | 'promote-candidate' /** No file of the app uses it: only its stories or tests, or nothing. Listed, never deleted by the catalogue. */ | 'unused'; /** * What the catalogue knows about a part that lives in an app (UIG-13). `null` * on the package's own entries. */ export interface AppFacts { class: EntryClass; /** Why it has that class, in words: the evidence, or the written reason. */ reason: string; /** * `true` when the class is written beside the part (`@registry : ` * in its comment) rather than worked out — "general, though one screen uses it yet". */ written: boolean; /** A pass-on's part, as `Name` for the package's and `Name from ` for another's. `null` otherwise. */ handsOn: string | null; /** * The app's files that use it, relative to the app: every file that imports it, * and its own file when something else there draws it. Stories and tests are not * uses — a part only they reach is unused. */ usedIn: string[]; /** * What ties it to this app: each import that is neither in the package's own * dependencies nor an app file that is itself untied. Empty means it could * live in the package as it is. */ tiedTo: string[]; /** The package part with the same name, when there is one: the reader should see both. */ packageNamesake: string | null; /** Imported as `import X from …` rather than `import { X } from …`. */ defaultExport: boolean; } /** A file in an app's target set that holds no part, and why, in words. */ export interface FileWithoutPart { file: string; reason: string; } export type EntryStatus = 'stable' | 'migrating' | 'deprecated'; /** A prop that takes one of a known set of words, and the set. */ export interface EntryVariant { prop: string; values: string[]; } /** * One prop a component declares itself. * * **Itself**, not what it inherits: a component that extends * `ComponentPropsWithRef<'div'>` would otherwise drag in every DOM attribute, * which is noise in a catalogue whose whole point is a short answer. */ export interface EntryProp { name: string; /** * What it takes, in a word a person reads: `true/false`, `number`, `text`, * `anything`, `a handler`, the words themselves for a set (`left | right`), * or the type as written where none of those fit. */ takes: string; required: boolean; /** The first sentence of the prop's own comment, where it has one. */ note: string | null; } /** A behaviour this component owns, from UIG-8's enumeration. */ export interface EntryBehaviour { /** `OwnedBehaviour.id` — `portal`, `walking`, `scroll`… */ id: string; /** What a person notices it doing, in words. */ behaviour: string; } export interface RegistryEntry { /** The exported name, exactly as `index.ts` exports it. */ name: string; /** Which library it came from: `estiva-ui`, or the app that owns it. */ repo: string; kind: EntryKind; /** What a caller actually types. */ importPath: string; /** The file the name is declared in, relative to the repo. Several names share one file. */ sourceFile: string; /** One line: what it is for. Never empty — the build fails instead. */ purpose: string; purposeFrom: PurposeSource; /** * Every prop it declares itself — what it can do. This is the field a reader * asks "does it already do X?" of, before writing X by hand. */ props: EntryProp[]; /** * The subset of `props` that takes one of a known set of words, structured. * Kept beside `props` because it is what a search ranks well on, and what a * caller most often needs named back at them. */ variants: EntryVariant[]; /** What it owns, so a caller never rebuilds it (UIG-8). Empty for most. */ ownsBehaviours: EntryBehaviour[]; status: EntryStatus; /** * Which migration stage plans to change it. * * Always `null` here. The migration's plan lives in `K:/Estiva/migration * docs`, which is not in this repo and is not in CI — and a copy kept here by * hand is the drifting list this whole project exists to remove. The field * stays in the schema because a later ticket may have a source a build can * read; until then a reader gets an honest `null` rather than a stale number. */ migrationStage: number | null; /** The `.mdx` page's Storybook id, or `null` where the name has no page of its own. */ docsId: string | null; /** A story that shows it, by Storybook id. `null` where nothing renders it alone. */ storyId: string | null; /** The `.mdx` path, relative to the repo, or `null`. */ docPage: string | null; /** An app's part: its class and the evidence for it (UIG-13). `null` on the package's own entries. */ app: AppFacts | null; /** * What it looks like: each class list it writes, as its look words * (colour, text, border, corner, shadow — never placement), those with four * or more (UIG-25). What `estiva/no-copied-look` compares a hand-typed list * against. Empty for a part that only places others; absent in a catalogue * built before 0.34.0. */ looks?: string[]; } /** A name that is exported and deliberately not an entry. Empty today; the count still has to reconcile. */ export interface RegistryExclusion { name: string; reason: string; } export interface Registry { schemaVersion: number; builtFrom: { /** * `package`: the package's catalogue, committed and shipped. `app`: one app's, * built fresh from its code each time and never committed — Peek and Ship are * private, the package is public (Katerina, 18 September 2026). */ kind: 'package' | 'app'; repo: string; /** The package the entries are imported from, or the app's own name. */ package: string; packageVersion: string; /** * The package: value exports counted in `index.ts`. An app: parts found in its * files. `entries + excluded` must equal it. */ exports: number; /** Type-only exports, which are not entries. Recorded so the count reconciles. `0` for an app. */ typeExports: number; /** * An app only: its `.tsx` files outside stories and tests. Each one either holds * an entry or is in `filesWithoutParts`. `null` for the package. */ files: number | null; }; /** * How to turn an id into a link. There is no Storybook on the internet yet * (UIG-19), so an absolute URL committed here would resolve nowhere: the * reader joins its own base to these. */ storybook: { docsPath: string; storyPath: string; devUrl: string; }; entries: RegistryEntry[]; excluded: RegistryExclusion[]; /** An app's `.tsx` files that hold no part, each with its reason. Empty for the package. */ filesWithoutParts: FileWithoutPart[]; } export declare const CLASSES: EntryClass[]; /** * Check a registry against this schema. Returns every problem, not the first: * a build that reports one missing purpose per run costs one run per entry. * * An empty list is the only pass. CI runs this on the committed file. */ export declare function validateRegistry(value: unknown): string[]; //# sourceMappingURL=schema.d.ts.map