import type { Diagnostic, GraphClaim, ResolvedProfileContext, SemanticGraph, WorkspaceSource } from './compiler.js'; export type LifecycleStatus = 'planned' | 'current' | 'retired'; export interface ProjectionDefinition { readonly format: 'yarramate/projection/v1'; readonly id: string; readonly version: string; readonly query: { readonly subjects?: readonly string[]; /** * Pattern instances whose CONTENTS this query wants (#473, ADR 0144). * * Each id names an instance, and the facet selects that instance together * with everything the fold tree would draw inside it - the same closure * `presentation.fold: instances` collapses into one box, read through the * view's own `nesting`. A view that wanted the box wanted what is in it. * * This is the ONE facet that adds rather than narrows, and it is why * `subjects` and this are read as a single identity facet whose values * combine with OR (`docs/PROJECTIONS.md`). Every other field still ANDs * over the union: `instances` says which subjects are in play, and `kinds` * or `statuses` narrow that set the way they always did. * * Hand-listing the members instead is what this replaces, and the list goes * stale the moment the pattern binds another slot. The names here are the * instance, not its parts, so the view follows the model. */ readonly instances?: readonly string[]; /** * Subjects this query would otherwise select and the author has taken out * (#267, ADR 0122). A facet view states a rule, and every interesting rule * has an exception someone would rather state than abandon the rule for; * this is where that exception is written down instead of being the silent * absence a hand-enumerated list produces. Applied after every other facet * AND after `relationships: connected` expansion, so an excluded subject is * out whichever way it would have come back in. */ readonly exclude?: readonly string[]; readonly documents?: readonly string[]; readonly kinds?: readonly string[]; readonly layers?: readonly string[]; readonly statuses?: readonly LifecycleStatus[]; readonly excludeStatuses?: readonly LifecycleStatus[]; readonly states?: readonly string[]; readonly owners?: readonly string[]; readonly constraints?: readonly string[]; readonly relationshipKinds?: readonly string[]; readonly kindMatching?: 'exact' | 'descendants'; readonly relationships?: 'between' | 'connected' | 'none'; readonly isolatedConcepts?: 'include' | 'exclude'; }; readonly presentation?: { readonly title?: string; readonly description?: string; /** * How this view arranges itself: `layered`, `routed`, `served-by` or * `bands`, a ladder where each keeps everything below it (ADR 0147). A * view that says nothing lays out `served-by`. The canvas offers the * choice on screen and a save writes what is in force. */ readonly layout?: LayoutMode; /** * Which way this view runs its layers. Read by the LikeC4 export for its * `autoLayout` and by the canvas for ELK's `elk.direction` (ADR 0121); a * view that says nothing runs `top-down`, which is what ArchiMate's layer * bands read as. */ readonly direction?: LayoutDirection; /** * The relationship kinds that draw as nesting in this view, in precedence * order (ADR 0101). Absent means `['composition']`, which is the behaviour * that shipped before a view could say; `[]` draws everything as a line. */ readonly nesting?: readonly NestingKind[]; /** * Whether this view draws pattern instances FOLDED by default (#473). * * `instances` collapses every instance to one node carrying its members; * `none`, the default, draws everything. A reader opens what they want, so * this says where to START rather than what may be seen — the same shape * `nesting` has, and for the same reason: a view is an opinion about a * first look, not a restriction. */ readonly fold?: import('./fold-tree.js').FoldMode; readonly showLifecycle?: boolean; readonly showEvidence?: boolean; readonly showOwnership?: boolean; /** * Whether responsibility edges draw on the canvas (#557, ADR 0159). Off * when absent: the subject's properties read the letters either way. */ readonly showResponsibility?: boolean; /** * Whether an unnamed relationship is labelled with its reading - "serves", * "served by", "realizes" - or left to its line style and arrowhead * (ADR 0147). A named relationship keeps its name either way. On when * absent, which is what shipped. */ readonly showKindLabels?: boolean; /** * The folder this view files itself under in an editor's rail: a label the * author declares, nested with `/`, never the directory the projection * sits in (ADR 0104). The same word `yarramate/likec4-project/v1` uses for * the same thing. */ readonly folder?: string; /** * The notation this view draws in. `archimate` is the only one, and the * field is kept rather than dropped so a second notation has somewhere to * land - the same reason `layout` stayed an enum when `radial` and `force` * went (ADR 0086, ADR 0087). */ readonly notation?: 'archimate'; }; } /** * A relationship kind a view may draw as nesting, and the default. Defined in * `./nesting.js`, which imports nothing, and re-exported here so a consumer * that already reads projection types keeps finding them. The browser should * still import them from there rather than from here: this module loads Ajv * and a schema, which is a great deal of bundle for one constant (ADR 0101). */ export { DEFAULT_NESTING, type NestingKind } from './nesting.js'; /** * Whether a view folds instances, and the default. Defined in `./fold-tree.js`, * which imports nothing, and re-exported here on the same terms as the nesting * vocabulary above (#473). */ export { DEFAULT_FOLD, type FoldMode } from './fold-tree.js'; import type { NestingKind } from './nesting.js'; import { type FoldMembership } from './fold-tree.js'; /** * Which way a view runs, and the default. Split out for the same reason as the * nesting vocabulary above, and re-exported here on the same terms (ADR 0121). */ export { DEFAULT_DIRECTION, type LayoutDirection } from './layout-direction.js'; import type { LayoutDirection } from './layout-direction.js'; /** * How a view arranges itself, and the default. Same split, same terms * (ADR 0147). */ export { DEFAULT_LAYOUT, LAYOUT_MODES, type LayoutMode } from './layout-mode.js'; import type { LayoutMode } from './layout-mode.js'; export type ProjectionQuery = ProjectionDefinition['query']; export interface ProjectionResult { readonly format: 'yarramate/projection-result/v1'; readonly projection: string; readonly presentation?: ProjectionDefinition['presentation']; readonly documents: SemanticGraph['documents']; readonly subjects: SemanticGraph['subjects']; readonly claims: readonly GraphClaim[]; } export type ProjectionLoadResult = { readonly ok: true; readonly projection: ProjectionDefinition; } | { readonly ok: false; readonly diagnostics: readonly Diagnostic[]; }; export declare function loadProjection(source: WorkspaceSource): ProjectionLoadResult; export declare function canonicalProjection(projection: ProjectionDefinition): ProjectionDefinition; /** * A facet of a query, named the way the query names it. What * {@link explainProjection} reports as the reason a subject is not in a view. */ export type ConceptFacet = 'exclude' | 'states' | 'subjects' | 'instances' | 'documents' | 'kinds' | 'layers' | 'statuses' | 'excludeStatuses' | 'owners' | 'constraints'; /** One subject a query dropped, and the facet that dropped it. */ export interface ProjectionExclusion { readonly id: string; readonly facet: ConceptFacet; } /** * How a query decides about concepts, built once and shared by the two things * that ask. * * `evaluateProjection` asks whether a subject is in; `explainProjection` asks * why one is out. They must never be able to disagree, which is why there is * one selector rather than a filter here and a reason-finder somewhere else. */ /** * Which subjects each named pattern instance holds, transitively. * * The SAME tree the canvas folds, computed from the same module over the same * inputs, because a view that names an instance must select exactly what the * box would have contained. Two implementations of "what is inside this * instance" would be two answers to one question, and the one the reader sees * is whichever they happened to open (rule: a test must go through the seam it * is testing - so must a second caller). * * Nesting comes from THIS view's `presentation.nesting`, not the default, for * the reason folding reads it too: containment is a property of the view, and a * view that nests on composition alone holds less than one that also nests on * assignment. * * Without `memberships` the closure is the named instances ALONE. That is a * degradation, not an answer, and it is why `check` refuses rather than * quietly returning a smaller view (#447, #450). */ export declare function instanceClosureOf(graph: SemanticGraph, projection: ProjectionDefinition, profileContext?: ResolvedProfileContext, memberships?: readonly FoldMembership[]): ReadonlyMap; /** A query facet naming something the model does not have. */ export interface UnmatchedSelector { readonly facet: string; readonly value: string; /** The closest name the facet does offer, when one is close enough to be a likely typo. */ readonly nearest?: string; } /** * Every value in a projection query that names nothing. * * A projection is a DOCUMENT, and a query holds references the same way a * relationship does. YarraMate refuses a relationship pointing at a concept * that does not exist; it did not refuse a query naming a state that does not * exist, and the symptom is silent. `states: [target-stat]` selects no state, * which selects no subject, which exports a clean empty artifact with exit 0. * Someone hands that to a client. * * Checked at `check`, not at `export`, because the typo is in a file rather * than in an invocation: CI catches it, and every verb over the same * projection inherits the guard instead of each growing its own. * * ONLY FACETS WITH A CLOSED NAMESPACE ARE CHECKED. `statuses` and * `excludeStatuses` are schema enums, refused upstream before this runs. * Everything else names something: `owners` and `constraints` are REFS to * concepts, which the compiler itself proves by refusing an unresolved owner * with YM304, so their namespace is the subject list like `subjects`. * * Each namespace is derived the way the FILTER derives it, so the check cannot * drift from what it guards: `documents` reads the same provenance the * `documents` facet compares against, and `states` is the same * `yarramate/state/type` scan `conceptSelector` runs. * * A kind whose profile is not loaded is DORMANT rather than wrong, the same * distinction #351 drew for question catalogues, so the kind facets are * checked only when a profile context is present. * * An empty RESULT is not reported here and must not be. A query whose every * name resolves and which selects nothing is a real answer to a real question: * a target state nobody has populated yet is empty, correctly. */ export declare function unmatchedSelectors(graph: SemanticGraph, projection: ProjectionDefinition, profileContext?: ResolvedProfileContext): readonly UnmatchedSelector[]; /** * {@link unmatchedSelectors} as diagnostics, built HERE rather than at each * caller so `check`, and anything that adopts this later, refuse in identical * words with an identical code. */ export declare function projectionReferenceDiagnostics(source: WorkspaceSource, projection: ProjectionDefinition, graph: SemanticGraph, profileContext?: ResolvedProfileContext, instances?: ReadonlySet): readonly Diagnostic[]; /** * Every concept a query leaves out, and the facet that left it out. * * The editor needs this to say why a subject is not on the canvas (#248): a * query that selects nothing, or that quietly drops the one subject the * reviewer was looking for, is otherwise indistinguishable from a model that * does not hold it. Relationships are not reported - they enter a view through * their endpoints rather than by matching a facet of their own, so "why" for a * relationship is a statement about the concepts it joins. * * Separate from `evaluateProjection` rather than a field on its result, * because `yarramate/projection-result/v1` is a published document with * `additionalProperties: false` and this is a question about a query rather * than part of what a projection IS. */ export declare function explainProjection(graph: SemanticGraph, projection: ProjectionDefinition, profileContext?: ResolvedProfileContext, memberships?: readonly FoldMembership[]): readonly ProjectionExclusion[]; export declare function evaluateProjection(graph: SemanticGraph, projection: ProjectionDefinition, profileContext?: ResolvedProfileContext, memberships?: readonly FoldMembership[]): ProjectionResult; export declare function renderProjectionMarkdown(result: ProjectionResult, profileContext?: ResolvedProfileContext): string; export declare function renderBudgetedContext(result: ProjectionResult, budgetTokens: number): string;