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.
- package/LICENSE +21 -0
- package/README.md +174 -0
- package/dist/adapter-mapping.d.ts +40 -0
- package/dist/adapter-mapping.js +200 -0
- package/dist/adapters/graphify-cli.d.ts +3 -0
- package/dist/adapters/graphify-cli.js +127 -0
- package/dist/adapters/graphify-entry.d.ts +1 -0
- package/dist/adapters/graphify-entry.js +1 -0
- package/dist/adapters/graphify.d.ts +23 -0
- package/dist/adapters/graphify.js +47 -0
- package/dist/adapters/likec4-cli.d.ts +3 -0
- package/dist/adapters/likec4-cli.js +662 -0
- package/dist/adapters/likec4-export.d.ts +25 -0
- package/dist/adapters/likec4-export.js +204 -0
- package/dist/adapters/likec4-kind-mapping.d.ts +22 -0
- package/dist/adapters/likec4-kind-mapping.js +60 -0
- package/dist/adapters/likec4-prepare.d.ts +28 -0
- package/dist/adapters/likec4-prepare.js +154 -0
- package/dist/adapters/likec4-project.d.ts +58 -0
- package/dist/adapters/likec4-project.js +176 -0
- package/dist/adapters/likec4.d.ts +4 -0
- package/dist/adapters/likec4.js +4 -0
- package/dist/architecture-state.d.ts +22 -0
- package/dist/architecture-state.js +63 -0
- package/dist/check-command.d.ts +2 -0
- package/dist/check-command.js +232 -0
- package/dist/cli-support.d.ts +24 -0
- package/dist/cli-support.js +81 -0
- package/dist/cli.d.ts +4 -0
- package/dist/cli.js +575 -0
- package/dist/compiler.d.ts +66 -0
- package/dist/compiler.js +940 -0
- package/dist/core-contract.d.ts +43 -0
- package/dist/core-contract.js +162 -0
- package/dist/evidence.d.ts +58 -0
- package/dist/evidence.js +161 -0
- package/dist/graph.d.ts +2 -0
- package/dist/graph.js +37 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +9 -0
- package/dist/profile.d.ts +30 -0
- package/dist/profile.js +162 -0
- package/dist/projection.d.ts +42 -0
- package/dist/projection.js +183 -0
- package/dist/reconciliation.d.ts +26 -0
- package/dist/reconciliation.js +47 -0
- package/dist/source-document.d.ts +24 -0
- package/dist/source-document.js +80 -0
- package/dist/workspace.d.ts +29 -0
- package/dist/workspace.js +114 -0
- package/docs/CONSUMING-YARRAMATE.md +145 -0
- package/package.json +110 -0
- package/schema/yarramate-adapter-mapping.schema.json +59 -0
- package/schema/yarramate-check-result.schema.json +92 -0
- package/schema/yarramate-core-contract.schema.json +132 -0
- package/schema/yarramate-diagnostic-result.schema.json +64 -0
- package/schema/yarramate-document.schema.json +168 -0
- package/schema/yarramate-evidence-report.schema.json +73 -0
- package/schema/yarramate-evidence.schema.json +92 -0
- package/schema/yarramate-graph-v2.schema.json +170 -0
- package/schema/yarramate-likec4-check-result.schema.json +50 -0
- package/schema/yarramate-likec4-diagnostic-result.schema.json +69 -0
- package/schema/yarramate-likec4-generated-project-v2.schema.json +100 -0
- package/schema/yarramate-likec4-generated-project.schema.json +76 -0
- package/schema/yarramate-likec4-kind-mapping.schema.json +65 -0
- package/schema/yarramate-likec4-project.schema.json +160 -0
- package/schema/yarramate-profile.schema.json +113 -0
- package/schema/yarramate-projection-result.schema.json +177 -0
- package/schema/yarramate-projection.schema.json +126 -0
- package/schema/yarramate-reconciliation-report.schema.json +90 -0
- package/schema/yarramate-state-comparison.schema.json +61 -0
- package/schema/yarramate-workspace.schema.json +55 -0
- package/skills/yarramate-architecture/SKILL.md +135 -0
- package/skills/yarramate-architecture/agents/openai.yaml +4 -0
- package/skills/yarramate-architecture/references/journey-checklists.md +57 -0
- package/skills/yarramate-architecture/references/native-authoring.md +167 -0
package/dist/profile.js
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
export const layers = [
|
|
2
|
+
'motivation',
|
|
3
|
+
'strategy',
|
|
4
|
+
'business',
|
|
5
|
+
'application',
|
|
6
|
+
'technology',
|
|
7
|
+
'physical',
|
|
8
|
+
'implementation',
|
|
9
|
+
'composite',
|
|
10
|
+
];
|
|
11
|
+
export const aspects = [
|
|
12
|
+
'motivation',
|
|
13
|
+
'active-structure',
|
|
14
|
+
'behavior',
|
|
15
|
+
'passive-structure',
|
|
16
|
+
'composite',
|
|
17
|
+
];
|
|
18
|
+
const kind = (layer, aspect, entries) => entries.map(([id, name]) => ({
|
|
19
|
+
id,
|
|
20
|
+
name,
|
|
21
|
+
layer,
|
|
22
|
+
aspect,
|
|
23
|
+
inspiredBy: `ArchiMate-inspired:${name}`,
|
|
24
|
+
}));
|
|
25
|
+
export const conceptKinds = [
|
|
26
|
+
...kind('motivation', 'motivation', [
|
|
27
|
+
['stakeholder', 'Stakeholder'],
|
|
28
|
+
['driver', 'Driver'],
|
|
29
|
+
['assessment', 'Assessment'],
|
|
30
|
+
['goal', 'Goal'],
|
|
31
|
+
['outcome', 'Outcome'],
|
|
32
|
+
['principle', 'Principle'],
|
|
33
|
+
['requirement', 'Requirement'],
|
|
34
|
+
['constraint', 'Constraint'],
|
|
35
|
+
['meaning', 'Meaning'],
|
|
36
|
+
['value', 'Value'],
|
|
37
|
+
]),
|
|
38
|
+
...kind('strategy', 'active-structure', [['resource', 'Resource']]),
|
|
39
|
+
...kind('strategy', 'behavior', [
|
|
40
|
+
['capability', 'Capability'],
|
|
41
|
+
['valueStream', 'Value stream'],
|
|
42
|
+
['courseOfAction', 'Course of action'],
|
|
43
|
+
]),
|
|
44
|
+
...kind('business', 'active-structure', [
|
|
45
|
+
['businessActor', 'Business actor'],
|
|
46
|
+
['businessRole', 'Business role'],
|
|
47
|
+
['businessCollaboration', 'Business collaboration'],
|
|
48
|
+
['businessInterface', 'Business interface'],
|
|
49
|
+
]),
|
|
50
|
+
...kind('business', 'behavior', [
|
|
51
|
+
['businessProcess', 'Business process'],
|
|
52
|
+
['businessFunction', 'Business function'],
|
|
53
|
+
['businessInteraction', 'Business interaction'],
|
|
54
|
+
['businessEvent', 'Business event'],
|
|
55
|
+
['businessService', 'Business service'],
|
|
56
|
+
]),
|
|
57
|
+
...kind('business', 'passive-structure', [
|
|
58
|
+
['businessObject', 'Business object'],
|
|
59
|
+
['contract', 'Contract'],
|
|
60
|
+
['representation', 'Representation'],
|
|
61
|
+
['product', 'Product'],
|
|
62
|
+
]),
|
|
63
|
+
...kind('application', 'active-structure', [
|
|
64
|
+
['applicationComponent', 'Application component'],
|
|
65
|
+
['applicationCollaboration', 'Application collaboration'],
|
|
66
|
+
['applicationInterface', 'Application interface'],
|
|
67
|
+
]),
|
|
68
|
+
...kind('application', 'behavior', [
|
|
69
|
+
['applicationFunction', 'Application function'],
|
|
70
|
+
['applicationInteraction', 'Application interaction'],
|
|
71
|
+
['applicationProcess', 'Application process'],
|
|
72
|
+
['applicationEvent', 'Application event'],
|
|
73
|
+
['applicationService', 'Application service'],
|
|
74
|
+
]),
|
|
75
|
+
...kind('application', 'passive-structure', [['dataObject', 'Data object']]),
|
|
76
|
+
...kind('technology', 'active-structure', [
|
|
77
|
+
['node', 'Node'],
|
|
78
|
+
['device', 'Device'],
|
|
79
|
+
['systemSoftware', 'System software'],
|
|
80
|
+
['technologyCollaboration', 'Technology collaboration'],
|
|
81
|
+
['technologyInterface', 'Technology interface'],
|
|
82
|
+
['path', 'Path'],
|
|
83
|
+
['communicationNetwork', 'Communication network'],
|
|
84
|
+
]),
|
|
85
|
+
...kind('technology', 'behavior', [
|
|
86
|
+
['technologyFunction', 'Technology function'],
|
|
87
|
+
['technologyProcess', 'Technology process'],
|
|
88
|
+
['technologyInteraction', 'Technology interaction'],
|
|
89
|
+
['technologyEvent', 'Technology event'],
|
|
90
|
+
['technologyService', 'Technology service'],
|
|
91
|
+
]),
|
|
92
|
+
...kind('technology', 'passive-structure', [['artifact', 'Artifact']]),
|
|
93
|
+
...kind('physical', 'active-structure', [
|
|
94
|
+
['equipment', 'Equipment'],
|
|
95
|
+
['facility', 'Facility'],
|
|
96
|
+
['distributionNetwork', 'Distribution network'],
|
|
97
|
+
]),
|
|
98
|
+
...kind('physical', 'passive-structure', [['material', 'Material']]),
|
|
99
|
+
...kind('implementation', 'behavior', [
|
|
100
|
+
['workPackage', 'Work package'],
|
|
101
|
+
['implementationEvent', 'Implementation event'],
|
|
102
|
+
]),
|
|
103
|
+
...kind('implementation', 'passive-structure', [
|
|
104
|
+
['deliverable', 'Deliverable'],
|
|
105
|
+
['plateau', 'Plateau'],
|
|
106
|
+
['gap', 'Gap'],
|
|
107
|
+
]),
|
|
108
|
+
...kind('composite', 'composite', [
|
|
109
|
+
['grouping', 'Grouping'],
|
|
110
|
+
['location', 'Location'],
|
|
111
|
+
['andJunction', 'AND junction'],
|
|
112
|
+
['orJunction', 'OR junction'],
|
|
113
|
+
]),
|
|
114
|
+
];
|
|
115
|
+
export const relationshipKinds = [
|
|
116
|
+
'composition',
|
|
117
|
+
'aggregation',
|
|
118
|
+
'assignment',
|
|
119
|
+
'realization',
|
|
120
|
+
'serving',
|
|
121
|
+
'access',
|
|
122
|
+
'influence',
|
|
123
|
+
'association',
|
|
124
|
+
'triggering',
|
|
125
|
+
'flow',
|
|
126
|
+
'specialization',
|
|
127
|
+
];
|
|
128
|
+
/**
|
|
129
|
+
* Safe, intentionally broad semantic constraints. A future licensed
|
|
130
|
+
* compatibility package may layer an exact external relationship matrix over
|
|
131
|
+
* these native YarraMate rules.
|
|
132
|
+
*/
|
|
133
|
+
export const relationshipPolicies = [
|
|
134
|
+
{ id: 'composition', intent: 'Strong whole-part structure' },
|
|
135
|
+
{ id: 'aggregation', intent: 'Weak whole-part structure' },
|
|
136
|
+
{
|
|
137
|
+
id: 'assignment',
|
|
138
|
+
intent: 'Allocate an active structure to behavior or responsibility',
|
|
139
|
+
sourceAspects: ['active-structure'],
|
|
140
|
+
},
|
|
141
|
+
{ id: 'realization', intent: 'Fulfil a more abstract concept' },
|
|
142
|
+
{ id: 'serving', intent: 'Make behavior or an interface available' },
|
|
143
|
+
{
|
|
144
|
+
id: 'access',
|
|
145
|
+
intent: 'Read, write, create, or use passive structure',
|
|
146
|
+
targetAspects: ['passive-structure'],
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
id: 'influence',
|
|
150
|
+
intent: 'Affect a motivation concept',
|
|
151
|
+
targetAspects: ['motivation'],
|
|
152
|
+
},
|
|
153
|
+
{ id: 'association', intent: 'Relevant connection with no stronger meaning' },
|
|
154
|
+
{
|
|
155
|
+
id: 'triggering',
|
|
156
|
+
intent: 'Express temporal or causal precedence',
|
|
157
|
+
sourceAspects: ['behavior'],
|
|
158
|
+
targetAspects: ['behavior'],
|
|
159
|
+
},
|
|
160
|
+
{ id: 'flow', intent: 'Transfer information, value, goods, or material' },
|
|
161
|
+
{ id: 'specialization', intent: 'Express a more specific form' },
|
|
162
|
+
];
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { Diagnostic, GraphClaim, ResolvedProfileContext, SemanticGraph, WorkspaceSource } from './compiler.js';
|
|
2
|
+
export type LifecycleStatus = 'planned' | 'current' | 'retired';
|
|
3
|
+
export interface ProjectionDefinition {
|
|
4
|
+
readonly format: 'yarramate/projection/v1';
|
|
5
|
+
readonly id: string;
|
|
6
|
+
readonly version: string;
|
|
7
|
+
readonly query: {
|
|
8
|
+
readonly subjects?: readonly string[];
|
|
9
|
+
readonly documents?: readonly string[];
|
|
10
|
+
readonly kinds?: readonly string[];
|
|
11
|
+
readonly statuses?: readonly LifecycleStatus[];
|
|
12
|
+
readonly states?: readonly string[];
|
|
13
|
+
readonly owners?: readonly string[];
|
|
14
|
+
readonly constraints?: readonly string[];
|
|
15
|
+
readonly relationshipKinds?: readonly string[];
|
|
16
|
+
readonly kindMatching?: 'exact' | 'descendants';
|
|
17
|
+
readonly relationships?: 'between' | 'connected' | 'none';
|
|
18
|
+
readonly isolatedConcepts?: 'include' | 'exclude';
|
|
19
|
+
};
|
|
20
|
+
readonly presentation?: {
|
|
21
|
+
readonly title?: string;
|
|
22
|
+
readonly description?: string;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
export interface ProjectionResult {
|
|
26
|
+
readonly format: 'yarramate/projection-result/v1';
|
|
27
|
+
readonly projection: string;
|
|
28
|
+
readonly presentation?: ProjectionDefinition['presentation'];
|
|
29
|
+
readonly documents: SemanticGraph['documents'];
|
|
30
|
+
readonly subjects: SemanticGraph['subjects'];
|
|
31
|
+
readonly claims: readonly GraphClaim[];
|
|
32
|
+
}
|
|
33
|
+
export type ProjectionLoadResult = {
|
|
34
|
+
readonly ok: true;
|
|
35
|
+
readonly projection: ProjectionDefinition;
|
|
36
|
+
} | {
|
|
37
|
+
readonly ok: false;
|
|
38
|
+
readonly diagnostics: readonly Diagnostic[];
|
|
39
|
+
};
|
|
40
|
+
export declare function loadProjection(source: WorkspaceSource): ProjectionLoadResult;
|
|
41
|
+
export declare function evaluateProjection(graph: SemanticGraph, projection: ProjectionDefinition, profileContext?: ResolvedProfileContext): ProjectionResult;
|
|
42
|
+
export declare function renderProjectionMarkdown(result: ProjectionResult): string;
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import Ajv2020Module from 'ajv/dist/2020.js';
|
|
2
|
+
import { loadSourceDocument } from './source-document.js';
|
|
3
|
+
import projectionSchema from '../schema/yarramate-projection.schema.json' with {
|
|
4
|
+
type: 'json'
|
|
5
|
+
};
|
|
6
|
+
const Ajv2020 = Ajv2020Module.default;
|
|
7
|
+
const validateProjection = new Ajv2020({ allErrors: true }).compile(projectionSchema);
|
|
8
|
+
export function loadProjection(source) {
|
|
9
|
+
const loaded = loadSourceDocument(source, validateProjection, 'Projection');
|
|
10
|
+
return loaded.ok
|
|
11
|
+
? { ok: true, projection: loaded.document.value }
|
|
12
|
+
: loaded;
|
|
13
|
+
}
|
|
14
|
+
const claimValue = (claims, subject, predicate) => {
|
|
15
|
+
const object = claims.find((claim) => claim.subject === subject && claim.predicate === predicate)?.object;
|
|
16
|
+
return object !== undefined && 'value' in object ? object.value : undefined;
|
|
17
|
+
};
|
|
18
|
+
const claimReference = (claims, subject, predicate) => {
|
|
19
|
+
const object = claims.find((claim) => claim.subject === subject && claim.predicate === predicate)?.object;
|
|
20
|
+
return object !== undefined && 'ref' in object ? object.ref : undefined;
|
|
21
|
+
};
|
|
22
|
+
const claimReferences = (claims, subject, predicate) => claims.flatMap((claim) => claim.subject === subject &&
|
|
23
|
+
claim.predicate === predicate &&
|
|
24
|
+
'ref' in claim.object
|
|
25
|
+
? [claim.object.ref]
|
|
26
|
+
: []);
|
|
27
|
+
export function evaluateProjection(graph, projection, profileContext) {
|
|
28
|
+
const architectureStateIds = new Set(graph.claims
|
|
29
|
+
.filter(({ predicate }) => predicate === 'yarramate/state/type')
|
|
30
|
+
.map(({ subject }) => subject));
|
|
31
|
+
const selectedStateIds = projection.query.states === undefined
|
|
32
|
+
? undefined
|
|
33
|
+
: projection.query.states.filter((state) => architectureStateIds.has(state));
|
|
34
|
+
const participatesInSelectedState = (subject) => {
|
|
35
|
+
if (selectedStateIds === undefined)
|
|
36
|
+
return true;
|
|
37
|
+
if (selectedStateIds.length === 0)
|
|
38
|
+
return false;
|
|
39
|
+
const presence = claimReferences(graph.claims, subject, 'yarramate/state/present-in');
|
|
40
|
+
return (presence.length === 0 ||
|
|
41
|
+
presence.some((state) => selectedStateIds.includes(state)));
|
|
42
|
+
};
|
|
43
|
+
const initiallySelectedConceptIds = new Set(graph.subjects
|
|
44
|
+
.filter(({ type }) => type === 'concept')
|
|
45
|
+
.filter(({ id }) => {
|
|
46
|
+
const documentId = id.slice(0, id.indexOf('#'));
|
|
47
|
+
const kind = claimValue(graph.claims, id, 'yarramate/concept/kind');
|
|
48
|
+
const status = claimValue(graph.claims, id, 'yarramate/lifecycle/status');
|
|
49
|
+
const owner = claimReference(graph.claims, id, 'yarramate/ownership/owner');
|
|
50
|
+
const constraints = claimReferences(graph.claims, id, 'yarramate/constraint/requires');
|
|
51
|
+
return ((projection.query.states === undefined ||
|
|
52
|
+
(!architectureStateIds.has(id) &&
|
|
53
|
+
participatesInSelectedState(id))) &&
|
|
54
|
+
(projection.query.subjects === undefined ||
|
|
55
|
+
projection.query.subjects.includes(id)) &&
|
|
56
|
+
(projection.query.documents === undefined ||
|
|
57
|
+
projection.query.documents.includes(documentId)) &&
|
|
58
|
+
(projection.query.kinds === undefined ||
|
|
59
|
+
(kind !== undefined &&
|
|
60
|
+
projection.query.kinds.some((selectedKind) => selectedKind === kind ||
|
|
61
|
+
(projection.query.kindMatching === 'descendants' &&
|
|
62
|
+
profileContext?.conceptKindLineages
|
|
63
|
+
.get(kind)
|
|
64
|
+
?.includes(selectedKind) === true)))) &&
|
|
65
|
+
(projection.query.statuses === undefined ||
|
|
66
|
+
(status !== undefined &&
|
|
67
|
+
projection.query.statuses.includes(status))) &&
|
|
68
|
+
(projection.query.owners === undefined ||
|
|
69
|
+
(owner !== undefined &&
|
|
70
|
+
projection.query.owners.includes(owner))) &&
|
|
71
|
+
(projection.query.constraints === undefined ||
|
|
72
|
+
constraints.some((constraint) => projection.query.constraints?.includes(constraint))));
|
|
73
|
+
})
|
|
74
|
+
.map(({ id }) => id));
|
|
75
|
+
const selectedConceptIds = new Set(initiallySelectedConceptIds);
|
|
76
|
+
const selectedRelationshipIds = new Set();
|
|
77
|
+
const relationshipMode = projection.query.relationships ?? 'between';
|
|
78
|
+
if (relationshipMode !== 'none') {
|
|
79
|
+
for (const subject of graph.subjects) {
|
|
80
|
+
if (subject.type !== 'relationship')
|
|
81
|
+
continue;
|
|
82
|
+
const relationship = graph.claims.find((claim) => claim.id === subject.id);
|
|
83
|
+
if (relationship === undefined ||
|
|
84
|
+
!('ref' in relationship.object) ||
|
|
85
|
+
(projection.query.relationshipKinds !== undefined &&
|
|
86
|
+
!projection.query.relationshipKinds.some((selectedKind) => selectedKind === relationship.predicate ||
|
|
87
|
+
(projection.query.kindMatching === 'descendants' &&
|
|
88
|
+
profileContext?.relationshipKindLineages
|
|
89
|
+
.get(relationship.predicate)
|
|
90
|
+
?.includes(selectedKind) === true))) ||
|
|
91
|
+
!participatesInSelectedState(subject.id)) {
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
const sourceSelected = initiallySelectedConceptIds.has(relationship.subject);
|
|
95
|
+
const targetSelected = initiallySelectedConceptIds.has(relationship.object.ref);
|
|
96
|
+
if ((relationshipMode === 'between' &&
|
|
97
|
+
sourceSelected &&
|
|
98
|
+
targetSelected) ||
|
|
99
|
+
(relationshipMode === 'connected' &&
|
|
100
|
+
(sourceSelected || targetSelected))) {
|
|
101
|
+
selectedRelationshipIds.add(subject.id);
|
|
102
|
+
if (relationshipMode === 'connected') {
|
|
103
|
+
selectedConceptIds.add(relationship.subject);
|
|
104
|
+
selectedConceptIds.add(relationship.object.ref);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
if (projection.query.isolatedConcepts === 'exclude') {
|
|
110
|
+
const relationshipEndpoints = new Set();
|
|
111
|
+
for (const relationshipId of selectedRelationshipIds) {
|
|
112
|
+
const relationship = graph.claims.find(({ id, object }) => id === relationshipId && 'ref' in object);
|
|
113
|
+
if (relationship !== undefined && 'ref' in relationship.object) {
|
|
114
|
+
relationshipEndpoints.add(relationship.subject);
|
|
115
|
+
relationshipEndpoints.add(relationship.object.ref);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
for (const conceptId of selectedConceptIds) {
|
|
119
|
+
if (!relationshipEndpoints.has(conceptId)) {
|
|
120
|
+
selectedConceptIds.delete(conceptId);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
const selectedSubjectIds = new Set([
|
|
125
|
+
...selectedConceptIds,
|
|
126
|
+
...selectedRelationshipIds,
|
|
127
|
+
]);
|
|
128
|
+
const relationshipIds = new Set(graph.subjects
|
|
129
|
+
.filter(({ type }) => type === 'relationship')
|
|
130
|
+
.map(({ id }) => id));
|
|
131
|
+
const subjects = graph.subjects.filter(({ id }) => selectedSubjectIds.has(id));
|
|
132
|
+
const claims = graph.claims.filter((claim) => (selectedConceptIds.has(claim.subject) &&
|
|
133
|
+
!relationshipIds.has(claim.id)) ||
|
|
134
|
+
selectedRelationshipIds.has(claim.subject) ||
|
|
135
|
+
selectedRelationshipIds.has(claim.id));
|
|
136
|
+
const selectedDocuments = new Set([...selectedConceptIds].map((id) => id.slice(0, id.indexOf('#'))));
|
|
137
|
+
return {
|
|
138
|
+
format: 'yarramate/projection-result/v1',
|
|
139
|
+
projection: `${projection.id}@${projection.version}`,
|
|
140
|
+
...(projection.presentation === undefined
|
|
141
|
+
? {}
|
|
142
|
+
: {
|
|
143
|
+
presentation: {
|
|
144
|
+
...(projection.presentation.title === undefined
|
|
145
|
+
? {}
|
|
146
|
+
: { title: projection.presentation.title }),
|
|
147
|
+
...(projection.presentation.description === undefined
|
|
148
|
+
? {}
|
|
149
|
+
: { description: projection.presentation.description }),
|
|
150
|
+
},
|
|
151
|
+
}),
|
|
152
|
+
documents: graph.documents.filter(({ id }) => selectedDocuments.has(id)),
|
|
153
|
+
subjects,
|
|
154
|
+
claims,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
const markdownText = (value) => value.replaceAll('\n', ' ').replaceAll('`', '\\`');
|
|
158
|
+
export function renderProjectionMarkdown(result) {
|
|
159
|
+
const title = result.presentation?.title ?? result.projection;
|
|
160
|
+
const concepts = result.subjects.filter(({ type }) => type === 'concept');
|
|
161
|
+
const relationships = result.subjects.filter(({ type }) => type === 'relationship');
|
|
162
|
+
const lines = [`# ${markdownText(title)}`, ''];
|
|
163
|
+
if (result.presentation?.description !== undefined) {
|
|
164
|
+
lines.push(markdownText(result.presentation.description), '');
|
|
165
|
+
}
|
|
166
|
+
lines.push('## Concepts', '');
|
|
167
|
+
for (const concept of concepts) {
|
|
168
|
+
const name = claimValue(result.claims, concept.id, 'yarramate/concept/name') ??
|
|
169
|
+
concept.id;
|
|
170
|
+
const kind = claimValue(result.claims, concept.id, 'yarramate/concept/kind') ??
|
|
171
|
+
'unknown';
|
|
172
|
+
const status = claimValue(result.claims, concept.id, 'yarramate/lifecycle/status');
|
|
173
|
+
lines.push(`- ${markdownText(name)} (\`${concept.id}\`) — \`${kind}\`${status === undefined ? '' : ` — ${status}`}`);
|
|
174
|
+
}
|
|
175
|
+
lines.push('', '## Relationships', '');
|
|
176
|
+
for (const relationship of relationships) {
|
|
177
|
+
const claim = result.claims.find(({ id, object }) => id === relationship.id && 'ref' in object);
|
|
178
|
+
if (claim !== undefined && 'ref' in claim.object) {
|
|
179
|
+
lines.push(`- \`${claim.subject}\` — \`${claim.predicate}\` → \`${claim.object.ref}\` (\`${relationship.id}\`)`);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return `${lines.join('\n')}\n`;
|
|
183
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { EvidenceLocator, EvidenceReport, EvidenceResult } from './evidence.js';
|
|
2
|
+
export interface ReconciliationFinding {
|
|
3
|
+
readonly target: {
|
|
4
|
+
readonly type: 'subject' | 'claim';
|
|
5
|
+
readonly id: string;
|
|
6
|
+
};
|
|
7
|
+
readonly result: Exclude<EvidenceResult, 'confirmed'>;
|
|
8
|
+
readonly provider: string;
|
|
9
|
+
readonly evidenceDocument: string;
|
|
10
|
+
readonly evidence: EvidenceLocator;
|
|
11
|
+
}
|
|
12
|
+
export interface ReconciliationReport {
|
|
13
|
+
readonly format: 'yarramate/reconciliation-report/v1';
|
|
14
|
+
readonly workspace: string;
|
|
15
|
+
readonly summary: {
|
|
16
|
+
readonly evidenceDocuments: number;
|
|
17
|
+
readonly observations: number;
|
|
18
|
+
readonly confirmed: number;
|
|
19
|
+
readonly findings: number;
|
|
20
|
+
readonly contradicted: number;
|
|
21
|
+
readonly unknown: number;
|
|
22
|
+
readonly notObserved: number;
|
|
23
|
+
};
|
|
24
|
+
readonly findings: readonly ReconciliationFinding[];
|
|
25
|
+
}
|
|
26
|
+
export declare function reconcileEvidenceReports(workspace: string, reports: readonly EvidenceReport[]): ReconciliationReport;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export function reconcileEvidenceReports(workspace, reports) {
|
|
2
|
+
const summary = {
|
|
3
|
+
evidenceDocuments: reports.length,
|
|
4
|
+
observations: 0,
|
|
5
|
+
confirmed: 0,
|
|
6
|
+
findings: 0,
|
|
7
|
+
contradicted: 0,
|
|
8
|
+
unknown: 0,
|
|
9
|
+
notObserved: 0,
|
|
10
|
+
};
|
|
11
|
+
const findings = [];
|
|
12
|
+
for (const report of reports) {
|
|
13
|
+
summary.observations += report.observations.length;
|
|
14
|
+
for (const observation of report.observations) {
|
|
15
|
+
if (observation.result === 'confirmed') {
|
|
16
|
+
summary.confirmed += 1;
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
if (observation.result === 'not-observed') {
|
|
20
|
+
summary.notObserved += 1;
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
summary[observation.result] += 1;
|
|
24
|
+
}
|
|
25
|
+
findings.push({
|
|
26
|
+
target: 'subject' in observation
|
|
27
|
+
? { type: 'subject', id: observation.subject }
|
|
28
|
+
: { type: 'claim', id: observation.claim },
|
|
29
|
+
result: observation.result,
|
|
30
|
+
provider: report.provider,
|
|
31
|
+
evidenceDocument: report.evidence,
|
|
32
|
+
evidence: observation.evidence,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
findings.sort((left, right) => left.target.id.localeCompare(right.target.id) ||
|
|
37
|
+
left.target.type.localeCompare(right.target.type) ||
|
|
38
|
+
left.provider.localeCompare(right.provider) ||
|
|
39
|
+
left.evidenceDocument.localeCompare(right.evidenceDocument));
|
|
40
|
+
summary.findings = findings.length;
|
|
41
|
+
return {
|
|
42
|
+
format: 'yarramate/reconciliation-report/v1',
|
|
43
|
+
workspace,
|
|
44
|
+
summary,
|
|
45
|
+
findings,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { ValidateFunction } from 'ajv';
|
|
2
|
+
import { LineCounter, parseDocument } from 'yaml';
|
|
3
|
+
import type { Diagnostic, WorkspaceSource } from './compiler.js';
|
|
4
|
+
export interface SourceLocation {
|
|
5
|
+
readonly path: string;
|
|
6
|
+
readonly pointer: string;
|
|
7
|
+
readonly line: number;
|
|
8
|
+
readonly column: number;
|
|
9
|
+
}
|
|
10
|
+
export interface ValidatedSourceDocument<T> {
|
|
11
|
+
readonly value: T;
|
|
12
|
+
readonly yaml: ReturnType<typeof parseDocument>;
|
|
13
|
+
readonly lineCounter: LineCounter;
|
|
14
|
+
}
|
|
15
|
+
export type SourceDocumentResult<T> = {
|
|
16
|
+
readonly ok: true;
|
|
17
|
+
readonly document: ValidatedSourceDocument<T>;
|
|
18
|
+
} | {
|
|
19
|
+
readonly ok: false;
|
|
20
|
+
readonly diagnostics: readonly Diagnostic[];
|
|
21
|
+
};
|
|
22
|
+
export declare const diagnosticOrder: (left: Diagnostic, right: Diagnostic) => number;
|
|
23
|
+
export declare function locateSourcePath(sourcePath: string, yaml: ReturnType<typeof parseDocument>, lineCounter: LineCounter, yamlPath: readonly (string | number)[], pointer: string): SourceLocation;
|
|
24
|
+
export declare function loadSourceDocument<T>(source: WorkspaceSource, validate: ValidateFunction, schemaLabel: string): SourceDocumentResult<T>;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { LineCounter, parseDocument } from 'yaml';
|
|
2
|
+
export const diagnosticOrder = (left, right) => left.path.localeCompare(right.path) ||
|
|
3
|
+
left.line - right.line ||
|
|
4
|
+
left.column - right.column ||
|
|
5
|
+
left.code.localeCompare(right.code) ||
|
|
6
|
+
left.message.localeCompare(right.message);
|
|
7
|
+
export function locateSourcePath(sourcePath, yaml, lineCounter, yamlPath, pointer) {
|
|
8
|
+
const node = yaml.getIn(yamlPath, true);
|
|
9
|
+
const offset = typeof node === 'object' &&
|
|
10
|
+
node !== null &&
|
|
11
|
+
'range' in node &&
|
|
12
|
+
Array.isArray(node.range)
|
|
13
|
+
? node.range[0]
|
|
14
|
+
: 0;
|
|
15
|
+
const position = lineCounter.linePos(offset);
|
|
16
|
+
return {
|
|
17
|
+
path: sourcePath,
|
|
18
|
+
pointer,
|
|
19
|
+
line: position.line,
|
|
20
|
+
column: position.col,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export function loadSourceDocument(source, validate, schemaLabel) {
|
|
24
|
+
const lineCounter = new LineCounter();
|
|
25
|
+
const yaml = parseDocument(source.source, { lineCounter });
|
|
26
|
+
if (yaml.errors.length > 0) {
|
|
27
|
+
return {
|
|
28
|
+
ok: false,
|
|
29
|
+
diagnostics: yaml.errors.map((error) => {
|
|
30
|
+
const position = error.linePos?.[0] ?? { line: 1, col: 1 };
|
|
31
|
+
return {
|
|
32
|
+
severity: 'error',
|
|
33
|
+
code: 'YM101',
|
|
34
|
+
message: error.message.split(' at line ')[0] ?? error.message,
|
|
35
|
+
path: source.path,
|
|
36
|
+
pointer: '/',
|
|
37
|
+
line: position.line,
|
|
38
|
+
column: position.col,
|
|
39
|
+
};
|
|
40
|
+
}),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
const value = yaml.toJS();
|
|
44
|
+
if (!validate(value)) {
|
|
45
|
+
return {
|
|
46
|
+
ok: false,
|
|
47
|
+
diagnostics: (validate.errors ?? [])
|
|
48
|
+
.map((error) => {
|
|
49
|
+
const property = error.keyword === 'additionalProperties'
|
|
50
|
+
? String(error.params.additionalProperty)
|
|
51
|
+
: undefined;
|
|
52
|
+
const pointer = property
|
|
53
|
+
? `${error.instancePath}/${property}`
|
|
54
|
+
: error.instancePath || '/';
|
|
55
|
+
const yamlPath = pointer
|
|
56
|
+
.split('/')
|
|
57
|
+
.slice(1)
|
|
58
|
+
.map((segment) => /^\d+$/.test(segment) ? Number(segment) : segment);
|
|
59
|
+
const location = locateSourcePath(source.path, yaml, lineCounter, yamlPath, pointer);
|
|
60
|
+
return {
|
|
61
|
+
severity: 'error',
|
|
62
|
+
code: 'YM201',
|
|
63
|
+
message: property
|
|
64
|
+
? `Property "${property}" is not allowed`
|
|
65
|
+
: `${schemaLabel} schema violation: ${error.message ?? error.keyword}`,
|
|
66
|
+
...location,
|
|
67
|
+
};
|
|
68
|
+
})
|
|
69
|
+
.sort(diagnosticOrder),
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
ok: true,
|
|
74
|
+
document: {
|
|
75
|
+
value: value,
|
|
76
|
+
yaml,
|
|
77
|
+
lineCounter,
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { Diagnostic, WorkspaceSource } from './compiler.js';
|
|
2
|
+
export interface WorkspaceManifest {
|
|
3
|
+
readonly format: 'yarramate/workspace/v1';
|
|
4
|
+
readonly id: string;
|
|
5
|
+
readonly documents: readonly string[];
|
|
6
|
+
readonly profiles: readonly string[];
|
|
7
|
+
readonly projections: readonly string[];
|
|
8
|
+
readonly adapterMappings: readonly string[];
|
|
9
|
+
readonly evidence?: readonly string[];
|
|
10
|
+
readonly contracts?: readonly string[];
|
|
11
|
+
}
|
|
12
|
+
export interface ResolvedWorkspace {
|
|
13
|
+
readonly id: string;
|
|
14
|
+
readonly documents: readonly string[];
|
|
15
|
+
readonly profiles: readonly string[];
|
|
16
|
+
readonly projections: readonly string[];
|
|
17
|
+
readonly adapterMappings: readonly string[];
|
|
18
|
+
readonly evidence: readonly string[];
|
|
19
|
+
readonly contracts: readonly string[];
|
|
20
|
+
}
|
|
21
|
+
export type WorkspaceManifestResult = {
|
|
22
|
+
readonly ok: true;
|
|
23
|
+
readonly manifest: WorkspaceManifest;
|
|
24
|
+
readonly workspace: ResolvedWorkspace;
|
|
25
|
+
} | {
|
|
26
|
+
readonly ok: false;
|
|
27
|
+
readonly diagnostics: readonly Diagnostic[];
|
|
28
|
+
};
|
|
29
|
+
export declare function loadWorkspaceManifest(source: WorkspaceSource, cwd: string): WorkspaceManifestResult;
|