yarramate 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.
Files changed (76) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +174 -0
  3. package/dist/adapter-mapping.d.ts +40 -0
  4. package/dist/adapter-mapping.js +200 -0
  5. package/dist/adapters/graphify-cli.d.ts +3 -0
  6. package/dist/adapters/graphify-cli.js +127 -0
  7. package/dist/adapters/graphify-entry.d.ts +1 -0
  8. package/dist/adapters/graphify-entry.js +1 -0
  9. package/dist/adapters/graphify.d.ts +23 -0
  10. package/dist/adapters/graphify.js +47 -0
  11. package/dist/adapters/likec4-cli.d.ts +3 -0
  12. package/dist/adapters/likec4-cli.js +662 -0
  13. package/dist/adapters/likec4-export.d.ts +25 -0
  14. package/dist/adapters/likec4-export.js +204 -0
  15. package/dist/adapters/likec4-kind-mapping.d.ts +22 -0
  16. package/dist/adapters/likec4-kind-mapping.js +60 -0
  17. package/dist/adapters/likec4-prepare.d.ts +28 -0
  18. package/dist/adapters/likec4-prepare.js +154 -0
  19. package/dist/adapters/likec4-project.d.ts +58 -0
  20. package/dist/adapters/likec4-project.js +176 -0
  21. package/dist/adapters/likec4.d.ts +4 -0
  22. package/dist/adapters/likec4.js +4 -0
  23. package/dist/architecture-state.d.ts +22 -0
  24. package/dist/architecture-state.js +63 -0
  25. package/dist/check-command.d.ts +2 -0
  26. package/dist/check-command.js +232 -0
  27. package/dist/cli-support.d.ts +24 -0
  28. package/dist/cli-support.js +81 -0
  29. package/dist/cli.d.ts +4 -0
  30. package/dist/cli.js +575 -0
  31. package/dist/compiler.d.ts +66 -0
  32. package/dist/compiler.js +940 -0
  33. package/dist/core-contract.d.ts +43 -0
  34. package/dist/core-contract.js +162 -0
  35. package/dist/evidence.d.ts +58 -0
  36. package/dist/evidence.js +161 -0
  37. package/dist/graph.d.ts +2 -0
  38. package/dist/graph.js +37 -0
  39. package/dist/index.d.ts +11 -0
  40. package/dist/index.js +9 -0
  41. package/dist/profile.d.ts +30 -0
  42. package/dist/profile.js +162 -0
  43. package/dist/projection.d.ts +42 -0
  44. package/dist/projection.js +183 -0
  45. package/dist/reconciliation.d.ts +26 -0
  46. package/dist/reconciliation.js +47 -0
  47. package/dist/source-document.d.ts +24 -0
  48. package/dist/source-document.js +80 -0
  49. package/dist/workspace.d.ts +29 -0
  50. package/dist/workspace.js +114 -0
  51. package/docs/CONSUMING-YARRAMATE.md +145 -0
  52. package/package.json +110 -0
  53. package/schema/yarramate-adapter-mapping.schema.json +59 -0
  54. package/schema/yarramate-check-result.schema.json +92 -0
  55. package/schema/yarramate-core-contract.schema.json +132 -0
  56. package/schema/yarramate-diagnostic-result.schema.json +64 -0
  57. package/schema/yarramate-document.schema.json +168 -0
  58. package/schema/yarramate-evidence-report.schema.json +73 -0
  59. package/schema/yarramate-evidence.schema.json +92 -0
  60. package/schema/yarramate-graph-v2.schema.json +170 -0
  61. package/schema/yarramate-likec4-check-result.schema.json +50 -0
  62. package/schema/yarramate-likec4-diagnostic-result.schema.json +69 -0
  63. package/schema/yarramate-likec4-generated-project-v2.schema.json +100 -0
  64. package/schema/yarramate-likec4-generated-project.schema.json +76 -0
  65. package/schema/yarramate-likec4-kind-mapping.schema.json +65 -0
  66. package/schema/yarramate-likec4-project.schema.json +160 -0
  67. package/schema/yarramate-profile.schema.json +113 -0
  68. package/schema/yarramate-projection-result.schema.json +177 -0
  69. package/schema/yarramate-projection.schema.json +126 -0
  70. package/schema/yarramate-reconciliation-report.schema.json +90 -0
  71. package/schema/yarramate-state-comparison.schema.json +61 -0
  72. package/schema/yarramate-workspace.schema.json +55 -0
  73. package/skills/yarramate-architecture/SKILL.md +135 -0
  74. package/skills/yarramate-architecture/agents/openai.yaml +4 -0
  75. package/skills/yarramate-architecture/references/journey-checklists.md +57 -0
  76. package/skills/yarramate-architecture/references/native-authoring.md +167 -0
@@ -0,0 +1,204 @@
1
+ import { adapterMappingEntryLocation, adapterMappingLocation, } from '../adapter-mapping.js';
2
+ import { diagnosticOrder } from '../source-document.js';
3
+ const identifier = /^[A-Za-z_][A-Za-z0-9_-]*$/;
4
+ const valueFor = (claims, subject, predicate) => {
5
+ const object = claims.find((claim) => claim.subject === subject && claim.predicate === predicate)?.object;
6
+ return object !== undefined && 'value' in object
7
+ ? object.value
8
+ : undefined;
9
+ };
10
+ const referencesFor = (claims, subject, predicate) => claims
11
+ .flatMap((claim) => claim.subject === subject &&
12
+ claim.predicate === predicate &&
13
+ 'ref' in claim.object
14
+ ? [claim.object.ref]
15
+ : [])
16
+ .sort();
17
+ const sourceForConcept = (claims, subject) => claims.find((claim) => claim.subject === subject &&
18
+ claim.predicate === 'yarramate/concept/kind')?.source;
19
+ const metadataLines = (entries, indentation) => {
20
+ const present = entries.filter((entry) => entry[1] !== undefined &&
21
+ (typeof entry[1] === 'string' || entry[1].length > 0));
22
+ if (present.length === 0)
23
+ return [];
24
+ return [
25
+ `${indentation}metadata {`,
26
+ ...present.map(([key, value]) => Array.isArray(value)
27
+ ? `${indentation} ${key} [${value.map(quote).join(', ')}]`
28
+ : `${indentation} ${key} ${quote(value)}`),
29
+ `${indentation}}`,
30
+ ];
31
+ };
32
+ const kindId = (identity) => {
33
+ const separator = identity.lastIndexOf('#');
34
+ return separator === -1 ? identity : identity.slice(separator + 1);
35
+ };
36
+ const quote = (value) => `'${value.replaceAll('\\', '\\\\').replaceAll("'", "\\'").replaceAll('\n', '\\n')}'`;
37
+ export function exportLikeC4(projection, mapping, kindMapping, options = {}) {
38
+ const diagnostics = [];
39
+ if (mapping.adapter !== 'likec4') {
40
+ const source = adapterMappingLocation(mapping, 'adapter');
41
+ diagnostics.push({
42
+ severity: 'error',
43
+ code: 'YMLC101',
44
+ message: `Adapter mapping "${mapping.id}@${mapping.version}" targets "${mapping.adapter}", not "likec4"`,
45
+ path: source.path,
46
+ pointer: source.pointer,
47
+ line: source.line,
48
+ column: source.column,
49
+ });
50
+ }
51
+ const externalByNative = new Map(mapping.mappings
52
+ .filter(({ type }) => type === 'concept')
53
+ .map(({ native, external }) => [native, external]));
54
+ const externalConceptKind = new Map(kindMapping?.conceptKinds.map(({ native, external }) => [
55
+ native,
56
+ external,
57
+ ]) ?? []);
58
+ const externalRelationshipKind = new Map(kindMapping?.relationshipKinds.map(({ native, external }) => [
59
+ native,
60
+ external,
61
+ ]) ?? []);
62
+ const concepts = projection.subjects
63
+ .filter(({ type }) => type === 'concept')
64
+ .sort((left, right) => left.id.localeCompare(right.id));
65
+ const comparisonChange = new Map(options.comparison === undefined
66
+ ? []
67
+ : [
68
+ ...options.comparison.added.map(({ id }) => [id, 'added']),
69
+ ...options.comparison.removed.map(({ id }) => [id, 'removed']),
70
+ ...options.comparison.retained.map(({ id }) => [id, 'retained']),
71
+ ]);
72
+ for (const concept of concepts) {
73
+ const external = externalByNative.get(concept.id);
74
+ if (external === undefined) {
75
+ const source = sourceForConcept(projection.claims, concept.id);
76
+ if (source === undefined)
77
+ continue;
78
+ diagnostics.push({
79
+ severity: 'error',
80
+ code: 'YMLC102',
81
+ message: `Projected concept "${concept.id}" has no LikeC4 mapping`,
82
+ subject: concept.id,
83
+ path: source.path,
84
+ pointer: source.pointer,
85
+ line: source.line,
86
+ column: source.column,
87
+ });
88
+ }
89
+ else if (!identifier.test(external)) {
90
+ const entry = mapping.mappings.find(({ native, type }) => native === concept.id && type === 'concept');
91
+ const source = adapterMappingEntryLocation(mapping, entry, 'external');
92
+ diagnostics.push({
93
+ severity: 'error',
94
+ code: 'YMLC103',
95
+ message: `LikeC4 identity "${external}" is not a valid identifier`,
96
+ subject: concept.id,
97
+ path: source.path,
98
+ pointer: source.pointer,
99
+ line: source.line,
100
+ column: source.column,
101
+ });
102
+ }
103
+ }
104
+ if (diagnostics.length > 0) {
105
+ return {
106
+ ok: false,
107
+ diagnostics: diagnostics.sort(diagnosticOrder),
108
+ };
109
+ }
110
+ const lines = [
111
+ '// Generated by YarraMate. Edit the native documents, not this file.',
112
+ 'model {',
113
+ ];
114
+ for (const concept of concepts) {
115
+ const external = externalByNative.get(concept.id);
116
+ const semanticKind = valueFor(projection.claims, concept.id, 'yarramate/concept/kind') ?? 'element';
117
+ const kind = externalConceptKind.get(semanticKind) ?? kindId(semanticKind);
118
+ const name = valueFor(projection.claims, concept.id, 'yarramate/concept/name') ?? concept.id;
119
+ const description = valueFor(projection.claims, concept.id, 'yarramate/concept/description');
120
+ const metadata = metadataLines([
121
+ ['yarramateId', concept.id],
122
+ ['yarramateKind', semanticKind],
123
+ ['yarramateChange', comparisonChange.get(concept.id)],
124
+ [
125
+ 'status',
126
+ valueFor(projection.claims, concept.id, 'yarramate/lifecycle/status'),
127
+ ],
128
+ [
129
+ 'owner',
130
+ referencesFor(projection.claims, concept.id, 'yarramate/ownership/owner')[0],
131
+ ],
132
+ [
133
+ 'constraints',
134
+ referencesFor(projection.claims, concept.id, 'yarramate/constraint/requires'),
135
+ ],
136
+ ], ' ');
137
+ if (description === undefined && metadata.length === 0) {
138
+ lines.push(` ${external} = ${kind} ${quote(name)}`);
139
+ }
140
+ else {
141
+ lines.push(` ${external} = ${kind} ${quote(name)} {`, ...(description === undefined
142
+ ? []
143
+ : [` description ${quote(description)}`]), ...metadata, ' }');
144
+ }
145
+ }
146
+ const relationships = projection.subjects
147
+ .filter(({ type }) => type === 'relationship')
148
+ .sort((left, right) => left.id.localeCompare(right.id));
149
+ if (relationships.length > 0)
150
+ lines.push('');
151
+ for (const relationship of relationships) {
152
+ const structural = projection.claims.find((claim) => claim.id === relationship.id);
153
+ if (structural === undefined ||
154
+ !('ref' in structural.object)) {
155
+ continue;
156
+ }
157
+ const source = externalByNative.get(structural.subject);
158
+ const target = externalByNative.get(structural.object.ref);
159
+ if (source === undefined || target === undefined)
160
+ continue;
161
+ const name = valueFor(projection.claims, relationship.id, 'yarramate/relationship/name');
162
+ const metadata = metadataLines([
163
+ ['yarramateId', relationship.id],
164
+ ['yarramateKind', structural.predicate],
165
+ ['yarramateChange', comparisonChange.get(relationship.id)],
166
+ [
167
+ 'status',
168
+ valueFor(projection.claims, relationship.id, 'yarramate/lifecycle/status'),
169
+ ],
170
+ [
171
+ 'mode',
172
+ valueFor(projection.claims, relationship.id, 'yarramate/access/mode'),
173
+ ],
174
+ [
175
+ 'content',
176
+ valueFor(projection.claims, relationship.id, 'yarramate/flow/content'),
177
+ ],
178
+ ], ' ');
179
+ lines.push(` ${source} -[${externalRelationshipKind.get(structural.predicate) ?? kindId(structural.predicate)}]-> ${target}${name === undefined ? '' : ` ${quote(name)}`}${metadata.length === 0 ? '' : ' {'}`, ...metadata, ...(metadata.length === 0 ? [] : [' }']));
180
+ }
181
+ const viewId = identifier.test(projection.projection.split('@')[0] ?? '')
182
+ ? projection.projection.split('@')[0]
183
+ : 'index';
184
+ lines.push('}', '', 'views {', ` view ${viewId} {`, ...(projection.presentation?.title === undefined
185
+ ? []
186
+ : [` title ${quote(projection.presentation.title)}`]), ...(projection.presentation?.description === undefined
187
+ ? []
188
+ : [
189
+ ` description ${quote(projection.presentation.description)}`,
190
+ ]), ' include *', ...(options.comparison === undefined
191
+ ? []
192
+ : concepts.map((concept) => {
193
+ const external = externalByNative.get(concept.id);
194
+ const change = comparisonChange.get(concept.id);
195
+ if (change === 'added') {
196
+ return ` style ${external} { color green }`;
197
+ }
198
+ if (change === 'removed') {
199
+ return ` style ${external} { color red; border dashed }`;
200
+ }
201
+ return ` style ${external} { color gray }`;
202
+ })), ' autoLayout LeftRight', ' }', '}', '');
203
+ return { ok: true, source: lines.join('\n') };
204
+ }
@@ -0,0 +1,22 @@
1
+ import type { Diagnostic, WorkspaceSource } from '../compiler.js';
2
+ import { type SourceLocation } from '../source-document.js';
3
+ export interface LikeC4KindEntry {
4
+ readonly native: string;
5
+ readonly external: string;
6
+ }
7
+ export interface LikeC4KindMapping {
8
+ readonly format: 'yarramate/likec4-kind-mapping/v1';
9
+ readonly id: string;
10
+ readonly version: string;
11
+ readonly conceptKinds: readonly LikeC4KindEntry[];
12
+ readonly relationshipKinds: readonly LikeC4KindEntry[];
13
+ }
14
+ export type LikeC4KindMappingLoadResult = {
15
+ readonly ok: true;
16
+ readonly mapping: LikeC4KindMapping;
17
+ } | {
18
+ readonly ok: false;
19
+ readonly diagnostics: readonly Diagnostic[];
20
+ };
21
+ export declare function loadLikeC4KindMapping(source: WorkspaceSource): LikeC4KindMappingLoadResult;
22
+ export declare function likeC4KindMappingExternalLocation(mapping: LikeC4KindMapping, category: 'conceptKinds' | 'relationshipKinds', native: string): SourceLocation | undefined;
@@ -0,0 +1,60 @@
1
+ import Ajv2020Module from 'ajv/dist/2020.js';
2
+ import { diagnosticOrder, loadSourceDocument, locateSourcePath, } from '../source-document.js';
3
+ import kindMappingSchema from '../../schema/yarramate-likec4-kind-mapping.schema.json' with {
4
+ type: 'json'
5
+ };
6
+ const Ajv2020 = Ajv2020Module.default;
7
+ const validateSchema = new Ajv2020({ allErrors: true }).compile(kindMappingSchema);
8
+ const kindMappingLocations = new WeakMap();
9
+ const entryOrder = (left, right) => left.native.localeCompare(right.native) ||
10
+ left.external.localeCompare(right.external);
11
+ export function loadLikeC4KindMapping(source) {
12
+ const loaded = loadSourceDocument(source, validateSchema, 'LikeC4 kind mapping');
13
+ if (!loaded.ok)
14
+ return loaded;
15
+ const { value, yaml, lineCounter } = loaded.document;
16
+ const diagnostics = [];
17
+ const checkDuplicates = (field, label) => {
18
+ const seen = new Set();
19
+ for (const [index, entry] of value[field].entries()) {
20
+ if (seen.has(entry.native)) {
21
+ const location = locateSourcePath(source.path, yaml, lineCounter, [field, index, 'native'], `/${field}/${index}/native`);
22
+ diagnostics.push({
23
+ severity: 'error',
24
+ code: 'YMLC201',
25
+ message: `Native ${label} kind "${entry.native}" is mapped more than once`,
26
+ ...location,
27
+ });
28
+ }
29
+ seen.add(entry.native);
30
+ }
31
+ };
32
+ checkDuplicates('conceptKinds', 'concept');
33
+ checkDuplicates('relationshipKinds', 'relationship');
34
+ if (diagnostics.length > 0) {
35
+ return {
36
+ ok: false,
37
+ diagnostics: diagnostics.sort(diagnosticOrder),
38
+ };
39
+ }
40
+ const mapping = {
41
+ ...value,
42
+ conceptKinds: [...value.conceptKinds].sort(entryOrder),
43
+ relationshipKinds: [...value.relationshipKinds].sort(entryOrder),
44
+ };
45
+ const locateExternal = (field, entry) => {
46
+ const authoredIndex = value[field].indexOf(entry);
47
+ return locateSourcePath(source.path, yaml, lineCounter, [field, authoredIndex, 'external'], `/${field}/${authoredIndex}/external`);
48
+ };
49
+ kindMappingLocations.set(mapping, {
50
+ conceptKinds: mapping.conceptKinds.map((entry) => locateExternal('conceptKinds', entry)),
51
+ relationshipKinds: mapping.relationshipKinds.map((entry) => locateExternal('relationshipKinds', entry)),
52
+ });
53
+ return { ok: true, mapping };
54
+ }
55
+ export function likeC4KindMappingExternalLocation(mapping, category, native) {
56
+ const index = mapping[category].findIndex((entry) => entry.native === native);
57
+ return index === -1
58
+ ? undefined
59
+ : kindMappingLocations.get(mapping)?.[category][index];
60
+ }
@@ -0,0 +1,28 @@
1
+ import { type AdapterMapping } from '../adapter-mapping.js';
2
+ import { type Diagnostic, type WorkspaceSource } from '../compiler.js';
3
+ import { type ProjectionResult } from '../projection.js';
4
+ import { type LikeC4ExportDiagnostic } from './likec4-export.js';
5
+ import { type LikeC4KindMapping } from './likec4-kind-mapping.js';
6
+ export interface LikeC4PreparationInput {
7
+ readonly sources: readonly WorkspaceSource[];
8
+ readonly projection: WorkspaceSource;
9
+ readonly subjectMapping: WorkspaceSource;
10
+ readonly kindMapping?: WorkspaceSource;
11
+ readonly comparison?: {
12
+ readonly from: string;
13
+ readonly to: string;
14
+ };
15
+ readonly vocabulary: 'bundled' | 'consumer';
16
+ }
17
+ export type LikeC4PreparationDiagnostic = Diagnostic | LikeC4ExportDiagnostic;
18
+ export type LikeC4PreparationResult = {
19
+ readonly ok: true;
20
+ readonly source: string;
21
+ readonly projection: ProjectionResult;
22
+ readonly subjectMapping: AdapterMapping;
23
+ readonly kindMapping?: LikeC4KindMapping;
24
+ } | {
25
+ readonly ok: false;
26
+ readonly diagnostics: readonly LikeC4PreparationDiagnostic[];
27
+ };
28
+ export declare function prepareLikeC4Export(input: LikeC4PreparationInput): LikeC4PreparationResult;
@@ -0,0 +1,154 @@
1
+ import { LineCounter, parseDocument } from 'yaml';
2
+ import { loadAdapterMapping, validateAdapterMapping, } from '../adapter-mapping.js';
3
+ import { compileWorkspaceWithProfileContext, } from '../compiler.js';
4
+ import { compareArchitectureStates } from '../architecture-state.js';
5
+ import { conceptKinds, relationshipPolicies } from '../profile.js';
6
+ import { evaluateProjection, loadProjection, } from '../projection.js';
7
+ import { diagnosticOrder, locateSourcePath, } from '../source-document.js';
8
+ import { exportLikeC4, } from './likec4-export.js';
9
+ import { likeC4KindMappingExternalLocation, loadLikeC4KindMapping, } from './likec4-kind-mapping.js';
10
+ const terminalKind = (identity) => {
11
+ const separator = identity.lastIndexOf('#');
12
+ return separator === -1 ? identity : identity.slice(separator + 1);
13
+ };
14
+ const unsupportedBundledKinds = (projection, kindMapping) => {
15
+ const supportedConceptKinds = new Set(conceptKinds.map(({ id }) => id));
16
+ const supportedRelationshipKinds = new Set(relationshipPolicies.map(({ id }) => id));
17
+ const mappedConceptKinds = new Map(kindMapping?.conceptKinds.map(({ native, external }) => [
18
+ native,
19
+ external,
20
+ ]) ?? []);
21
+ const mappedRelationshipKinds = new Map(kindMapping?.relationshipKinds.map(({ native, external }) => [
22
+ native,
23
+ external,
24
+ ]) ?? []);
25
+ const diagnostics = [];
26
+ for (const subject of projection.subjects) {
27
+ const semanticKind = subject.type === 'concept'
28
+ ? projection.claims.find((claim) => claim.subject === subject.id &&
29
+ claim.predicate === 'yarramate/concept/kind' &&
30
+ 'value' in claim.object)?.object
31
+ : projection.claims.find((claim) => claim.id === subject.id)
32
+ ?.predicate;
33
+ const identity = typeof semanticKind === 'string'
34
+ ? semanticKind
35
+ : semanticKind !== undefined && 'value' in semanticKind
36
+ ? semanticKind.value
37
+ : undefined;
38
+ if (identity === undefined)
39
+ continue;
40
+ const external = subject.type === 'concept'
41
+ ? (mappedConceptKinds.get(identity) ?? terminalKind(identity))
42
+ : (mappedRelationshipKinds.get(identity) ?? terminalKind(identity));
43
+ const supported = subject.type === 'concept'
44
+ ? supportedConceptKinds.has(external)
45
+ : supportedRelationshipKinds.has(external);
46
+ if (!supported) {
47
+ const mappedSource = kindMapping === undefined
48
+ ? undefined
49
+ : likeC4KindMappingExternalLocation(kindMapping, subject.type === 'concept'
50
+ ? 'conceptKinds'
51
+ : 'relationshipKinds', identity);
52
+ const sourceClaim = subject.type === 'concept'
53
+ ? projection.claims.find((claim) => claim.subject === subject.id &&
54
+ claim.predicate === 'yarramate/concept/kind')
55
+ : projection.claims.find((claim) => claim.id === subject.id);
56
+ const source = mappedSource ?? sourceClaim?.source;
57
+ if (source === undefined)
58
+ continue;
59
+ diagnostics.push({
60
+ severity: 'error',
61
+ code: 'YMLC104',
62
+ message: `Semantic ${subject.type} kind "${identity}" resolves to unsupported bundled LikeC4 kind "${external}"`,
63
+ subject: subject.id,
64
+ path: source.path,
65
+ pointer: source.pointer,
66
+ line: source.line,
67
+ column: source.column,
68
+ });
69
+ }
70
+ }
71
+ return diagnostics.sort(diagnosticOrder);
72
+ };
73
+ export function prepareLikeC4Export(input) {
74
+ const compilation = compileWorkspaceWithProfileContext(input.sources);
75
+ if (!compilation.ok)
76
+ return compilation;
77
+ const projection = loadProjection(input.projection);
78
+ if (!projection.ok)
79
+ return projection;
80
+ const projectionStateLocation = (state) => {
81
+ const states = projection.projection.query.states ?? [];
82
+ const index = states.indexOf(state);
83
+ const pointer = index === -1 ? '/query/states' : `/query/states/${index}`;
84
+ const lineCounter = new LineCounter();
85
+ const yaml = parseDocument(input.projection.source, {
86
+ lineCounter,
87
+ });
88
+ return locateSourcePath(input.projection.path, yaml, lineCounter, index === -1 ? ['query', 'states'] : ['query', 'states', index], pointer);
89
+ };
90
+ const subjectMapping = loadAdapterMapping(input.subjectMapping);
91
+ if (!subjectMapping.ok)
92
+ return subjectMapping;
93
+ const subjectValidation = validateAdapterMapping(compilation.graph, subjectMapping.mapping);
94
+ if (!subjectValidation.ok)
95
+ return subjectValidation;
96
+ const kindMapping = input.kindMapping === undefined
97
+ ? undefined
98
+ : loadLikeC4KindMapping(input.kindMapping);
99
+ if (kindMapping !== undefined && !kindMapping.ok)
100
+ return kindMapping;
101
+ if (input.comparison !== undefined) {
102
+ const selectedStates = projection.projection.query.states ?? [];
103
+ const omittedStates = [
104
+ ...new Set([input.comparison.from, input.comparison.to]),
105
+ ].filter((state) => !selectedStates.includes(state));
106
+ if (omittedStates.length > 0) {
107
+ return {
108
+ ok: false,
109
+ diagnostics: omittedStates.map((state) => ({
110
+ severity: 'error',
111
+ code: 'YMLC106',
112
+ message: `Comparison state "${state}" is not selected by projection "${projection.projection.id}@${projection.projection.version}"`,
113
+ subject: state,
114
+ ...projectionStateLocation(state),
115
+ })),
116
+ };
117
+ }
118
+ }
119
+ const projectionResult = evaluateProjection(compilation.graph, projection.projection, compilation.profileContext);
120
+ const comparison = input.comparison === undefined
121
+ ? undefined
122
+ : compareArchitectureStates(compilation.graph, input.comparison.from, input.comparison.to);
123
+ if (comparison !== undefined && !comparison.ok) {
124
+ return {
125
+ ok: false,
126
+ diagnostics: comparison.issues.map((issue) => ({
127
+ severity: 'error',
128
+ code: 'YMLC105',
129
+ message: issue.message,
130
+ subject: issue.state,
131
+ ...projectionStateLocation(issue.state),
132
+ })),
133
+ };
134
+ }
135
+ if (input.vocabulary === 'bundled') {
136
+ const diagnostics = unsupportedBundledKinds(projectionResult, kindMapping?.mapping);
137
+ if (diagnostics.length > 0)
138
+ return { ok: false, diagnostics };
139
+ }
140
+ const exported = exportLikeC4(projectionResult, subjectMapping.mapping, kindMapping?.mapping, comparison === undefined
141
+ ? undefined
142
+ : { comparison: comparison.comparison });
143
+ if (!exported.ok)
144
+ return exported;
145
+ return {
146
+ ok: true,
147
+ source: exported.source,
148
+ projection: projectionResult,
149
+ subjectMapping: subjectMapping.mapping,
150
+ ...(kindMapping === undefined
151
+ ? {}
152
+ : { kindMapping: kindMapping.mapping }),
153
+ };
154
+ }
@@ -0,0 +1,58 @@
1
+ import type { WorkspaceSource } from '../compiler.js';
2
+ import type { LikeC4PreparationResult } from './likec4-prepare.js';
3
+ import { type LikeC4ExportResult } from './likec4-export.js';
4
+ export interface LikeC4ProjectDefinition {
5
+ readonly format: 'yarramate/likec4-project/v1';
6
+ readonly id: string;
7
+ readonly version: string;
8
+ readonly title: string;
9
+ readonly mapping: string;
10
+ readonly kindMapping?: string;
11
+ readonly views: ReadonlyArray<{
12
+ readonly id?: string;
13
+ readonly projection: string;
14
+ readonly compare?: {
15
+ readonly from: string;
16
+ readonly to: string;
17
+ };
18
+ readonly dynamic?: {
19
+ readonly steps: ReadonlyArray<{
20
+ readonly relationship: string;
21
+ readonly title?: string;
22
+ }>;
23
+ };
24
+ readonly deployment?: LikeC4Deployment;
25
+ }>;
26
+ }
27
+ export interface LikeC4Deployment {
28
+ readonly nodes: ReadonlyArray<{
29
+ readonly id: string;
30
+ readonly kind: 'environment' | 'zone' | 'host' | 'runtime';
31
+ readonly name: string;
32
+ readonly parent?: string;
33
+ }>;
34
+ readonly instances: ReadonlyArray<{
35
+ readonly id: string;
36
+ readonly subject: string;
37
+ readonly node: string;
38
+ }>;
39
+ }
40
+ export declare const loadLikeC4ProjectDefinition: (source: WorkspaceSource) => import("../source-document.js").SourceDocumentResult<LikeC4ProjectDefinition>;
41
+ export interface PreparedLikeC4ProjectView {
42
+ readonly id?: string;
43
+ readonly prepared: Extract<LikeC4PreparationResult, {
44
+ readonly ok: true;
45
+ }>;
46
+ readonly comparison?: {
47
+ readonly from: string;
48
+ readonly to: string;
49
+ };
50
+ readonly dynamic?: {
51
+ readonly steps: ReadonlyArray<{
52
+ readonly relationship: string;
53
+ readonly title?: string;
54
+ }>;
55
+ };
56
+ readonly deployment?: LikeC4Deployment;
57
+ }
58
+ export declare function exportLikeC4Project(project: LikeC4ProjectDefinition, views: readonly PreparedLikeC4ProjectView[]): LikeC4ExportResult;