yarramate 0.18.0 → 0.20.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/README.md +9 -1
- package/catalogues/core-enrichment.yaml +52 -1
- package/dist/adapters/graphify-cli.js +0 -0
- package/dist/adapters/likec4-cli.js +0 -0
- package/dist/adapters/likec4-export.d.ts +1 -1
- package/dist/adapters/likec4-export.js +32 -0
- package/dist/adapters/mcp-cli.js +0 -0
- package/dist/adapters/visual/client.js +0 -1
- package/dist/adapters/visual/protocol-contract.d.ts +118 -35
- package/dist/adapters/visual/protocol-contract.js +1 -11
- package/dist/adapters/visual/protocol.d.ts +8 -1
- package/dist/adapters/visual/protocol.js +86 -46
- package/dist/adapters/visual/request.d.ts +37 -0
- package/dist/adapters/visual/request.js +131 -0
- package/dist/adapters/visual/session-server.d.ts +12 -14
- package/dist/adapters/visual/session-server.js +641 -283
- package/dist/adapters/visual/session-store.d.ts +31 -29
- package/dist/adapters/visual/session-store.js +94 -94
- package/dist/adapters/visual/wire.d.ts +36 -10
- package/dist/adapters/visual-cli.d.ts +3 -0
- package/dist/adapters/visual-cli.js +66 -1
- package/dist/apply-command.d.ts +10 -0
- package/dist/apply-command.js +399 -247
- package/dist/cli.js +0 -0
- package/dist/compiler.d.ts +66 -0
- package/dist/compiler.js +209 -89
- package/dist/graph-projection.d.ts +62 -0
- package/dist/graph-projection.js +222 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/interrogate-command.d.ts +2 -0
- package/dist/interrogate-command.js +22 -0
- package/dist/kind-label.d.ts +8 -0
- package/dist/kind-label.js +8 -0
- package/dist/operations.d.ts +139 -0
- package/dist/operations.js +11 -0
- package/dist/projection.d.ts +2 -0
- package/dist/projection.js +8 -2
- package/dist/reconciliation.js +8 -7
- package/dist/source-document.d.ts +10 -0
- package/dist/source-document.js +21 -1
- package/dist/visual-app/assets/index-6oSR9RGM.js +354 -0
- package/dist/visual-app/assets/index-dwA9AOr5.css +1 -0
- package/dist/visual-app/index.html +2 -2
- package/docs/CONSUMING-YARRAMATE.md +25 -19
- package/package.json +9 -4
- package/schema/yarramate-adapter-mapping.schema.json +1 -1
- package/schema/yarramate-apply-result.schema.json +8 -2
- package/schema/yarramate-document.schema.json +1 -1
- package/schema/yarramate-likec4-diagnostic-result.schema.json +1 -1
- package/schema/yarramate-operations.schema.json +203 -1
- package/schema/yarramate-projection.schema.json +12 -3
- package/schema/yarramate-question-catalogue.schema.json +15 -2
- package/schema/yarramate-visual-event.schema.json +143 -0
- package/schema/yarramate-visual-graph.schema.json +272 -0
- package/schema/yarramate-visual-layout.schema.json +41 -0
- package/schema/yarramate-visual-model.schema.json +6 -44
- package/schema/yarramate-visual-response.schema.json +170 -19
- package/schema/yarramate-visual-session-descriptor.schema.json +1 -1
- package/schema/yarramate-visual-session-request.schema.json +0 -70
- package/schema/yarramate-visual-session-started.schema.json +2 -9
- package/schema/yarramate-visual-status.schema.json +3 -2
- package/skills/yarramate-architecture/SKILL.md +20 -13
- package/skills/yarramate-architecture/references/journey-checklists.md +4 -2
- package/skills/yarramate-architecture/references/native-authoring.md +30 -0
- package/skills/yarramate-architecture/references/visual-conversations.md +149 -150
- package/dist/adapters/visual/likec4-compiler.d.ts +0 -55
- package/dist/adapters/visual/likec4-compiler.js +0 -460
- package/dist/visual-app/assets/index-CGvC8e3R.css +0 -1
- package/dist/visual-app/assets/index-CXuW1tOj.js +0 -209
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import { ATTESTATION_PREDICATE_PREFIX, parseAttestationClaimValue, parseConstraintExpectsValue, } from './compiler.js';
|
|
2
|
+
import { kindLabelOf } from './kind-label.js';
|
|
3
|
+
const CONCEPT_KIND_PREDICATE = 'yarramate/concept/kind';
|
|
4
|
+
const CONCEPT_NAME_PREDICATE = 'yarramate/concept/name';
|
|
5
|
+
const CONCEPT_DESCRIPTION_PREDICATE = 'yarramate/concept/description';
|
|
6
|
+
const CONCEPT_ALIAS_PREDICATE = 'yarramate/concept/alias';
|
|
7
|
+
const LIFECYCLE_STATUS_PREDICATE = 'yarramate/lifecycle/status';
|
|
8
|
+
const IDENTITY_DISTINCT_FROM_PREDICATE = 'yarramate/identity/distinct-from';
|
|
9
|
+
const LINEAGE_SUPERSEDES_PREDICATE = 'yarramate/lineage/supersedes';
|
|
10
|
+
const OWNERSHIP_OWNER_PREDICATE = 'yarramate/ownership/owner';
|
|
11
|
+
const CONSTRAINT_REQUIRES_PREDICATE = 'yarramate/constraint/requires';
|
|
12
|
+
const REFERENCE_REFERS_TO_PREDICATE = 'yarramate/reference/refers-to';
|
|
13
|
+
const STATE_PRESENT_IN_PREDICATE = 'yarramate/state/present-in';
|
|
14
|
+
const RELATIONSHIP_NAME_PREDICATE = 'yarramate/relationship/name';
|
|
15
|
+
const RELATIONSHIP_DESCRIPTION_PREDICATE = 'yarramate/relationship/description';
|
|
16
|
+
const ACCESS_MODE_PREDICATE = 'yarramate/access/mode';
|
|
17
|
+
const FLOW_CONTENT_PREDICATE = 'yarramate/flow/content';
|
|
18
|
+
// This is compiler-internal data: every claim's object narrows deterministically
|
|
19
|
+
// by predicate. A mismatch means a real bug upstream in src/compiler.ts, not
|
|
20
|
+
// malformed input this function should tolerate — so it throws rather than
|
|
21
|
+
// silently coercing to an empty string.
|
|
22
|
+
const claimValue = (claim) => {
|
|
23
|
+
if ('value' in claim.object) {
|
|
24
|
+
return claim.object.value;
|
|
25
|
+
}
|
|
26
|
+
throw new Error(`Expected a value-typed object for claim "${claim.id}" (subject "${claim.subject}", predicate "${claim.predicate}"), got a ref-typed object`);
|
|
27
|
+
};
|
|
28
|
+
const claimRef = (claim) => {
|
|
29
|
+
if ('ref' in claim.object) {
|
|
30
|
+
return claim.object.ref;
|
|
31
|
+
}
|
|
32
|
+
throw new Error(`Expected a ref-typed object for claim "${claim.id}" (subject "${claim.subject}", predicate "${claim.predicate}"), got a value-typed object`);
|
|
33
|
+
};
|
|
34
|
+
// The compiler qualifies every subject id as `<document id>#<authored id>`
|
|
35
|
+
// (`qualifyReference` in src/compiler.ts), and a document's authored ids never
|
|
36
|
+
// contain `#`. An operation addresses a subject by the id someone wrote in the
|
|
37
|
+
// file, not by the qualified identity the compile derived, so the projection
|
|
38
|
+
// carries both rather than leaving the browser to re-derive one from the other.
|
|
39
|
+
const authoredIdOf = (subjectId) => {
|
|
40
|
+
const boundary = subjectId.indexOf('#');
|
|
41
|
+
return boundary === -1 ? subjectId : subjectId.slice(boundary + 1);
|
|
42
|
+
};
|
|
43
|
+
const compareById = (left, right) => left.id.localeCompare(right.id);
|
|
44
|
+
const groupClaimsBySubject = (claims) => {
|
|
45
|
+
const groups = new Map();
|
|
46
|
+
for (const claim of claims) {
|
|
47
|
+
const group = groups.get(claim.subject);
|
|
48
|
+
if (group === undefined) {
|
|
49
|
+
groups.set(claim.subject, [claim]);
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
group.push(claim);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return groups;
|
|
56
|
+
};
|
|
57
|
+
const projectConcept = (subjectId, claims, profileContext) => {
|
|
58
|
+
const kindClaim = claims.find((claim) => claim.predicate === CONCEPT_KIND_PREDICATE);
|
|
59
|
+
if (kindClaim === undefined) {
|
|
60
|
+
throw new Error(`Concept "${subjectId}" is missing its required yarramate/concept/kind claim`);
|
|
61
|
+
}
|
|
62
|
+
const kind = claimValue(kindClaim);
|
|
63
|
+
const nameClaim = claims.find((claim) => claim.predicate === CONCEPT_NAME_PREDICATE);
|
|
64
|
+
if (nameClaim === undefined) {
|
|
65
|
+
throw new Error(`Concept "${subjectId}" is missing its required yarramate/concept/name claim`);
|
|
66
|
+
}
|
|
67
|
+
const descriptionClaim = claims.find((claim) => claim.predicate === CONCEPT_DESCRIPTION_PREDICATE);
|
|
68
|
+
const statusClaim = claims.find((claim) => claim.predicate === LIFECYCLE_STATUS_PREDICATE);
|
|
69
|
+
const ownerClaim = claims.find((claim) => claim.predicate === OWNERSHIP_OWNER_PREDICATE);
|
|
70
|
+
const constraints = claims
|
|
71
|
+
.filter((claim) => claim.predicate === CONSTRAINT_REQUIRES_PREDICATE)
|
|
72
|
+
.map((requiresClaim) => {
|
|
73
|
+
// The constraint's local id lives between the `~constraint-` marker
|
|
74
|
+
// and the end of the requires claim's own id; the paired expects
|
|
75
|
+
// claim (if the constraint declared one) reuses that same local id
|
|
76
|
+
// under a `~expects-` marker in the same claim group.
|
|
77
|
+
const localId = requiresClaim.id.split('~constraint-')[1];
|
|
78
|
+
if (localId === undefined) {
|
|
79
|
+
throw new Error(`Constraint claim "${requiresClaim.id}" on concept "${subjectId}" does not follow the "<subject>~constraint-<id>" id convention`);
|
|
80
|
+
}
|
|
81
|
+
const expectsClaim = claims.find((claim) => claim.id === `${subjectId}~expects-${localId}`);
|
|
82
|
+
let expects;
|
|
83
|
+
if (expectsClaim === undefined) {
|
|
84
|
+
expects = null;
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
const parsed = parseConstraintExpectsValue(claimValue(expectsClaim));
|
|
88
|
+
if (parsed === undefined) {
|
|
89
|
+
throw new Error(`Constraint expects claim "${expectsClaim.id}" on concept "${subjectId}" does not follow the "provider key value" encoding`);
|
|
90
|
+
}
|
|
91
|
+
expects = parsed;
|
|
92
|
+
}
|
|
93
|
+
return {
|
|
94
|
+
id: localId,
|
|
95
|
+
ref: claimRef(requiresClaim),
|
|
96
|
+
expects,
|
|
97
|
+
};
|
|
98
|
+
});
|
|
99
|
+
const attestations = claims
|
|
100
|
+
.filter((claim) => claim.predicate.startsWith(ATTESTATION_PREDICATE_PREFIX))
|
|
101
|
+
.map((claim) => {
|
|
102
|
+
const parsed = parseAttestationClaimValue(claimValue(claim));
|
|
103
|
+
if (parsed === undefined) {
|
|
104
|
+
throw new Error(`Attestation claim "${claim.id}" on concept "${subjectId}" does not follow the "by on [recordedBy]" encoding`);
|
|
105
|
+
}
|
|
106
|
+
return {
|
|
107
|
+
topic: claim.predicate.slice(ATTESTATION_PREDICATE_PREFIX.length),
|
|
108
|
+
by: parsed.by,
|
|
109
|
+
on: parsed.on,
|
|
110
|
+
recordedBy: parsed.recordedBy ?? null,
|
|
111
|
+
};
|
|
112
|
+
});
|
|
113
|
+
return {
|
|
114
|
+
id: subjectId,
|
|
115
|
+
localId: authoredIdOf(subjectId),
|
|
116
|
+
document: kindClaim.source.path,
|
|
117
|
+
kind,
|
|
118
|
+
kindLabel: kindLabelOf(kind),
|
|
119
|
+
layer: profileContext.conceptKindLayers.get(kind) ?? null,
|
|
120
|
+
aspect: profileContext.conceptKindAspects.get(kind) ?? null,
|
|
121
|
+
name: claimValue(nameClaim),
|
|
122
|
+
description: descriptionClaim === undefined ? null : claimValue(descriptionClaim),
|
|
123
|
+
aka: claims
|
|
124
|
+
.filter((claim) => claim.predicate === CONCEPT_ALIAS_PREDICATE)
|
|
125
|
+
.map(claimValue)
|
|
126
|
+
.sort(),
|
|
127
|
+
status: statusClaim === undefined ? null : claimValue(statusClaim),
|
|
128
|
+
owner: ownerClaim === undefined ? null : claimRef(ownerClaim),
|
|
129
|
+
distinctFrom: claims
|
|
130
|
+
.filter((claim) => claim.predicate === IDENTITY_DISTINCT_FROM_PREDICATE)
|
|
131
|
+
.map(claimRef)
|
|
132
|
+
.sort(),
|
|
133
|
+
supersedes: claims
|
|
134
|
+
.filter((claim) => claim.predicate === LINEAGE_SUPERSEDES_PREDICATE)
|
|
135
|
+
.map(claimRef)
|
|
136
|
+
.sort(),
|
|
137
|
+
constraints,
|
|
138
|
+
references: claims
|
|
139
|
+
.filter((claim) => claim.predicate === REFERENCE_REFERS_TO_PREDICATE)
|
|
140
|
+
.map((referencesClaim) => {
|
|
141
|
+
const localId = referencesClaim.id.split('~reference-')[1];
|
|
142
|
+
if (localId === undefined) {
|
|
143
|
+
throw new Error(`Reference claim "${referencesClaim.id}" on concept "${subjectId}" does not follow the "<subject>~reference-<id>" id convention`);
|
|
144
|
+
}
|
|
145
|
+
return {
|
|
146
|
+
id: localId,
|
|
147
|
+
ref: claimRef(referencesClaim),
|
|
148
|
+
};
|
|
149
|
+
})
|
|
150
|
+
.sort((left, right) => left.ref.localeCompare(right.ref)),
|
|
151
|
+
presentIn: claims
|
|
152
|
+
.filter((claim) => claim.predicate === STATE_PRESENT_IN_PREDICATE)
|
|
153
|
+
.map(claimRef)
|
|
154
|
+
.sort(),
|
|
155
|
+
attestations,
|
|
156
|
+
};
|
|
157
|
+
};
|
|
158
|
+
const projectRelationship = (relationshipId, allClaims, ownClaims, profileContext) => {
|
|
159
|
+
// A relationship's defining claim is asymmetric: its id is the
|
|
160
|
+
// relationship's subject id, but its own `subject` field is the
|
|
161
|
+
// from-concept, so it lives in the from-concept's claim group, not this
|
|
162
|
+
// relationship's — it has to be found by scanning every claim.
|
|
163
|
+
const definingClaim = allClaims.find((claim) => claim.id === relationshipId);
|
|
164
|
+
if (definingClaim === undefined) {
|
|
165
|
+
throw new Error(`Relationship "${relationshipId}" is missing its defining claim`);
|
|
166
|
+
}
|
|
167
|
+
const kind = definingClaim.predicate;
|
|
168
|
+
const nameClaim = ownClaims.find((claim) => claim.predicate === RELATIONSHIP_NAME_PREDICATE);
|
|
169
|
+
const descriptionClaim = ownClaims.find((claim) => claim.predicate === RELATIONSHIP_DESCRIPTION_PREDICATE);
|
|
170
|
+
const modeClaim = ownClaims.find((claim) => claim.predicate === ACCESS_MODE_PREDICATE);
|
|
171
|
+
const contentClaim = ownClaims.find((claim) => claim.predicate === FLOW_CONTENT_PREDICATE);
|
|
172
|
+
const statusClaim = ownClaims.find((claim) => claim.predicate === LIFECYCLE_STATUS_PREDICATE);
|
|
173
|
+
return {
|
|
174
|
+
id: relationshipId,
|
|
175
|
+
localId: authoredIdOf(relationshipId),
|
|
176
|
+
document: definingClaim.source.path,
|
|
177
|
+
kind,
|
|
178
|
+
kindLabel: kindLabelOf(kind),
|
|
179
|
+
coreKindLabel: kindLabelOf(profileContext.relationshipKindLineages.get(kind)?.[0] ?? kind),
|
|
180
|
+
from: definingClaim.subject,
|
|
181
|
+
to: claimRef(definingClaim),
|
|
182
|
+
name: nameClaim === undefined ? null : claimValue(nameClaim),
|
|
183
|
+
description: descriptionClaim === undefined ? null : claimValue(descriptionClaim),
|
|
184
|
+
mode: modeClaim === undefined ? null : claimValue(modeClaim),
|
|
185
|
+
content: contentClaim === undefined ? null : claimValue(contentClaim),
|
|
186
|
+
status: statusClaim === undefined ? null : claimValue(statusClaim),
|
|
187
|
+
references: ownClaims
|
|
188
|
+
.filter((claim) => claim.predicate === REFERENCE_REFERS_TO_PREDICATE)
|
|
189
|
+
.map((referencesClaim) => {
|
|
190
|
+
const localId = referencesClaim.id.split('~reference-')[1];
|
|
191
|
+
if (localId === undefined) {
|
|
192
|
+
throw new Error(`Reference claim "${referencesClaim.id}" on relationship "${relationshipId}" does not follow the "<subject>~reference-<id>" id convention`);
|
|
193
|
+
}
|
|
194
|
+
return {
|
|
195
|
+
id: localId,
|
|
196
|
+
ref: claimRef(referencesClaim),
|
|
197
|
+
};
|
|
198
|
+
})
|
|
199
|
+
.sort((left, right) => left.ref.localeCompare(right.ref)),
|
|
200
|
+
presentIn: ownClaims
|
|
201
|
+
.filter((claim) => claim.predicate === STATE_PRESENT_IN_PREDICATE)
|
|
202
|
+
.map(claimRef)
|
|
203
|
+
.sort(),
|
|
204
|
+
};
|
|
205
|
+
};
|
|
206
|
+
export function projectGraphForCanvas(graph, profileContext) {
|
|
207
|
+
const claimsBySubject = groupClaimsBySubject(graph.claims);
|
|
208
|
+
const nodes = [];
|
|
209
|
+
const edges = [];
|
|
210
|
+
for (const subject of graph.subjects) {
|
|
211
|
+
const ownClaims = claimsBySubject.get(subject.id) ?? [];
|
|
212
|
+
if (subject.type === 'concept') {
|
|
213
|
+
nodes.push(projectConcept(subject.id, ownClaims, profileContext));
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
edges.push(projectRelationship(subject.id, graph.claims, ownClaims, profileContext));
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
nodes.sort(compareById);
|
|
220
|
+
edges.sort(compareById);
|
|
221
|
+
return { nodes, edges };
|
|
222
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { compileWorkspace, compileWorkspaceWithProfileContext, } from './compiler.js';
|
|
1
|
+
export { compileWorkspace, compileWorkspaceIncremental, compileWorkspaceWithProfileContext, } from './compiler.js';
|
|
2
2
|
export { serializeSemanticGraph } from './graph.js';
|
|
3
3
|
export { checkCoreContract, loadCoreContract, type CoreContract, type CoreContractCommand, type CoreContractFormat, type CoreContractLoadResult, type CoreContractSurface, } from './core-contract.js';
|
|
4
4
|
export { compareArchitectureStates, type StateComparison, type StateComparisonIssue, type StateComparisonResult, } from './architecture-state.js';
|
|
@@ -7,7 +7,7 @@ export { evaluateEvidence, evaluateEvidenceWorkspace, loadEvidence, type Evidenc
|
|
|
7
7
|
export { constraintExpectsPredicate, reconcileEvidenceReports, type AssertedRelationship, type AttestationStaleness, type DeclaredSource, type EvidenceFinding, type ExpectationComparison, type ReconciliationFinding, type ReconciliationReport, type StaleAttestationFinding, type UnobservedExpectation, } from './reconciliation.js';
|
|
8
8
|
export { deriveAttestationStaleness } from './attestation-staleness.js';
|
|
9
9
|
export { buildRtm, renderRtmMarkdown, type RequirementsTraceabilityMatrix, type RtmAttestation, type RtmContextEntry, type RtmDescopedEntry, type RtmEvidenceVerdict, type RtmLineageEntry, type RtmRealizer, type RtmRow, type RtmSource, } from './rtm.js';
|
|
10
|
-
export type { CompilationResult, ContextualCompilationResult, Diagnostic, GraphClaim, GraphSource, SemanticGraph, ResolvedProfileContext, WorkspaceSource, } from './compiler.js';
|
|
10
|
+
export type { CompilationCache, CompilationResult, ContextualCompilationResult, IncrementalCompilationResult, ParsedWorkspaceSource, Diagnostic, GraphClaim, GraphSource, SemanticGraph, ResolvedProfileContext, WorkspaceSource, } from './compiler.js';
|
|
11
11
|
export { canonicalProjection, evaluateProjection, loadProjection, renderProjectionMarkdown, } from './projection.js';
|
|
12
12
|
export { loadAdapterMapping, validateAdapterMapping, validateAdapterMappings, type AdapterMapping, type AdapterMappingLoadResult, type AdapterMappingValidationResult, type AdapterMappingsValidationResult, type AdapterSubjectMapping, } from './adapter-mapping.js';
|
|
13
13
|
export type { LifecycleStatus, ProjectionDefinition, ProjectionLoadResult, ProjectionResult, } from './projection.js';
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { compileWorkspace, compileWorkspaceWithProfileContext, } from './compiler.js';
|
|
1
|
+
export { compileWorkspace, compileWorkspaceIncremental, compileWorkspaceWithProfileContext, } from './compiler.js';
|
|
2
2
|
export { serializeSemanticGraph } from './graph.js';
|
|
3
3
|
export { checkCoreContract, loadCoreContract, } from './core-contract.js';
|
|
4
4
|
export { compareArchitectureStates, } from './architecture-state.js';
|
|
@@ -224,6 +224,28 @@ const conditionHolds = (index, condition, subjectId, profileContext) => {
|
|
|
224
224
|
// index, so this reads exactly like every other condition: the claim's
|
|
225
225
|
// existence is the whole signal (ADR 0056).
|
|
226
226
|
return (index.nearDuplicates().get(subjectId) ?? []).length > 0;
|
|
227
|
+
case 'unconstrained-kind': {
|
|
228
|
+
// A compiled graph already satisfies every aspect rule it is subject to
|
|
229
|
+
// (YM404), so participation in a kind that pins the aspect at this
|
|
230
|
+
// subject's end is the whole test: reclassify the subject to another
|
|
231
|
+
// aspect and the workspace stops compiling. Where no such claim exists
|
|
232
|
+
// the kind is a label the engine can never contradict (ADR 0083).
|
|
233
|
+
// Without a profile context the rules are unknown, not absent, so the
|
|
234
|
+
// condition reports nothing rather than inventing a finding.
|
|
235
|
+
if (profileContext === undefined)
|
|
236
|
+
return false;
|
|
237
|
+
return !index.relationshipClaims.some((claim) => {
|
|
238
|
+
const endpoints = profileContext.relationshipKindEndpointAspects.get(claim.predicate);
|
|
239
|
+
if (endpoints === undefined)
|
|
240
|
+
return false;
|
|
241
|
+
if (endpoints.source !== undefined && claim.subject === subjectId) {
|
|
242
|
+
return true;
|
|
243
|
+
}
|
|
244
|
+
return (endpoints.target !== undefined &&
|
|
245
|
+
'ref' in claim.object &&
|
|
246
|
+
claim.object.ref === subjectId);
|
|
247
|
+
});
|
|
248
|
+
}
|
|
227
249
|
}
|
|
228
250
|
};
|
|
229
251
|
// Shared with design: question templates (standard and askPlain alike)
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A kind identity is `<profile>#<local id>`, and its label is that local id.
|
|
3
|
+
*
|
|
4
|
+
* This lives apart from `graph-projection.ts` because the browser needs it:
|
|
5
|
+
* the projection imports the compiler, and the compiler reaches for Ajv
|
|
6
|
+
* through `node:module` at load, which a bundle cannot honour.
|
|
7
|
+
*/
|
|
8
|
+
export declare const kindLabelOf: (kind: string) => string;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A kind identity is `<profile>#<local id>`, and its label is that local id.
|
|
3
|
+
*
|
|
4
|
+
* This lives apart from `graph-projection.ts` because the browser needs it:
|
|
5
|
+
* the projection imports the compiler, and the compiler reaches for Ajv
|
|
6
|
+
* through `node:module` at load, which a bundle cannot honour.
|
|
7
|
+
*/
|
|
8
|
+
export const kindLabelOf = (kind) => kind.slice(kind.lastIndexOf('#') + 1);
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `yarramate/operations/v1` document shape, and the result of landing one.
|
|
3
|
+
*
|
|
4
|
+
* These declarations are the typed twin of `schema/yarramate-operations.schema.json`
|
|
5
|
+
* and `schema/yarramate-apply-result.schema.json`. They live apart from
|
|
6
|
+
* `./apply-command.js` - which owns the machinery that validates, splices, and
|
|
7
|
+
* writes a batch - because the visual protocol carries the same documents to a
|
|
8
|
+
* browser that must not pull Ajv, `yaml`, or `node:fs` into its bundle or its
|
|
9
|
+
* typecheck. Types only: this module has no runtime dependency of any kind.
|
|
10
|
+
*/
|
|
11
|
+
export interface IdentifiedReference {
|
|
12
|
+
readonly id: string;
|
|
13
|
+
readonly ref: string;
|
|
14
|
+
}
|
|
15
|
+
export interface ConstraintReference extends IdentifiedReference {
|
|
16
|
+
readonly expects?: {
|
|
17
|
+
readonly provider: string;
|
|
18
|
+
readonly key: string;
|
|
19
|
+
readonly value: string;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export interface ConceptFields {
|
|
23
|
+
readonly id: string;
|
|
24
|
+
readonly kind?: string;
|
|
25
|
+
readonly name?: string;
|
|
26
|
+
readonly description?: string;
|
|
27
|
+
readonly aka?: readonly string[];
|
|
28
|
+
readonly status?: string;
|
|
29
|
+
readonly owner?: string;
|
|
30
|
+
readonly distinctFrom?: readonly string[];
|
|
31
|
+
readonly supersedes?: readonly string[];
|
|
32
|
+
readonly constraints?: readonly ConstraintReference[];
|
|
33
|
+
readonly references?: readonly IdentifiedReference[];
|
|
34
|
+
readonly presentIn?: readonly string[];
|
|
35
|
+
readonly attestations?: ReadonlyArray<{
|
|
36
|
+
readonly topic: string;
|
|
37
|
+
readonly by: string;
|
|
38
|
+
readonly recordedBy: string;
|
|
39
|
+
readonly on: string;
|
|
40
|
+
}>;
|
|
41
|
+
}
|
|
42
|
+
export interface RelationshipFields {
|
|
43
|
+
readonly id: string;
|
|
44
|
+
readonly kind?: string;
|
|
45
|
+
readonly from?: string;
|
|
46
|
+
readonly to?: string;
|
|
47
|
+
readonly name?: string;
|
|
48
|
+
readonly description?: string;
|
|
49
|
+
readonly status?: string;
|
|
50
|
+
readonly mode?: string;
|
|
51
|
+
readonly content?: string;
|
|
52
|
+
readonly references?: readonly IdentifiedReference[];
|
|
53
|
+
readonly presentIn?: readonly string[];
|
|
54
|
+
}
|
|
55
|
+
export interface ObservationTarget {
|
|
56
|
+
readonly subject?: string;
|
|
57
|
+
readonly claim?: string;
|
|
58
|
+
readonly key?: string;
|
|
59
|
+
}
|
|
60
|
+
export interface ObservationFields extends ObservationTarget {
|
|
61
|
+
readonly value?: string;
|
|
62
|
+
readonly result?: 'confirmed' | 'contradicted' | 'unknown' | 'not-observed';
|
|
63
|
+
readonly evidence?: {
|
|
64
|
+
readonly uri: string;
|
|
65
|
+
readonly message?: string;
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
export interface ObservationChange extends ObservationTarget {
|
|
69
|
+
readonly value?: string;
|
|
70
|
+
readonly result?: 'confirmed' | 'contradicted' | 'unknown' | 'not-observed';
|
|
71
|
+
readonly evidence?: {
|
|
72
|
+
readonly uri?: string;
|
|
73
|
+
readonly message?: string;
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
export type YarramateOperation = {
|
|
77
|
+
readonly op: 'add-concept';
|
|
78
|
+
readonly document: string;
|
|
79
|
+
readonly concept: ConceptFields;
|
|
80
|
+
} | {
|
|
81
|
+
readonly op: 'add-relationship';
|
|
82
|
+
readonly document: string;
|
|
83
|
+
readonly relationship: RelationshipFields;
|
|
84
|
+
} | {
|
|
85
|
+
readonly op: 'update-concept';
|
|
86
|
+
readonly document: string;
|
|
87
|
+
readonly concept: ConceptFields;
|
|
88
|
+
readonly remove?: readonly string[];
|
|
89
|
+
} | {
|
|
90
|
+
readonly op: 'update-relationship';
|
|
91
|
+
readonly document: string;
|
|
92
|
+
readonly relationship: RelationshipFields;
|
|
93
|
+
readonly remove?: readonly string[];
|
|
94
|
+
} | {
|
|
95
|
+
readonly op: 'delete-concept';
|
|
96
|
+
readonly document: string;
|
|
97
|
+
readonly concept: {
|
|
98
|
+
readonly id: string;
|
|
99
|
+
};
|
|
100
|
+
} | {
|
|
101
|
+
readonly op: 'delete-relationship';
|
|
102
|
+
readonly document: string;
|
|
103
|
+
readonly relationship: {
|
|
104
|
+
readonly id: string;
|
|
105
|
+
};
|
|
106
|
+
} | {
|
|
107
|
+
readonly op: 'add-observation';
|
|
108
|
+
readonly document: string;
|
|
109
|
+
readonly observation: ObservationFields;
|
|
110
|
+
} | {
|
|
111
|
+
readonly op: 'update-observation';
|
|
112
|
+
readonly document: string;
|
|
113
|
+
readonly observation: ObservationChange;
|
|
114
|
+
readonly remove?: readonly string[];
|
|
115
|
+
} | {
|
|
116
|
+
readonly op: 'delete-observation';
|
|
117
|
+
readonly document: string;
|
|
118
|
+
readonly observation: ObservationTarget;
|
|
119
|
+
};
|
|
120
|
+
export interface OperationsDocument {
|
|
121
|
+
readonly format: 'yarramate/operations/v1';
|
|
122
|
+
readonly operations: readonly YarramateOperation[];
|
|
123
|
+
}
|
|
124
|
+
export interface YarramateApplyResult {
|
|
125
|
+
readonly format: 'yarramate/apply-result/v1';
|
|
126
|
+
readonly workspace: string;
|
|
127
|
+
readonly applied: {
|
|
128
|
+
readonly addedConcepts: number;
|
|
129
|
+
readonly addedRelationships: number;
|
|
130
|
+
readonly updatedConcepts: number;
|
|
131
|
+
readonly updatedRelationships: number;
|
|
132
|
+
readonly deletedConcepts: number;
|
|
133
|
+
readonly deletedRelationships: number;
|
|
134
|
+
readonly addedObservations: number;
|
|
135
|
+
readonly updatedObservations: number;
|
|
136
|
+
readonly deletedObservations: number;
|
|
137
|
+
};
|
|
138
|
+
readonly documents: readonly string[];
|
|
139
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `yarramate/operations/v1` document shape, and the result of landing one.
|
|
3
|
+
*
|
|
4
|
+
* These declarations are the typed twin of `schema/yarramate-operations.schema.json`
|
|
5
|
+
* and `schema/yarramate-apply-result.schema.json`. They live apart from
|
|
6
|
+
* `./apply-command.js` - which owns the machinery that validates, splices, and
|
|
7
|
+
* writes a batch - because the visual protocol carries the same documents to a
|
|
8
|
+
* browser that must not pull Ajv, `yaml`, or `node:fs` into its bundle or its
|
|
9
|
+
* typecheck. Types only: this module has no runtime dependency of any kind.
|
|
10
|
+
*/
|
|
11
|
+
export {};
|
package/dist/projection.d.ts
CHANGED
|
@@ -28,8 +28,10 @@ export interface ProjectionDefinition {
|
|
|
28
28
|
readonly showLifecycle?: boolean;
|
|
29
29
|
readonly showEvidence?: boolean;
|
|
30
30
|
readonly showOwnership?: boolean;
|
|
31
|
+
readonly notation?: 'native' | 'archimate';
|
|
31
32
|
};
|
|
32
33
|
}
|
|
34
|
+
export type ProjectionQuery = ProjectionDefinition['query'];
|
|
33
35
|
export interface ProjectionResult {
|
|
34
36
|
readonly format: 'yarramate/projection-result/v1';
|
|
35
37
|
readonly projection: string;
|
package/dist/projection.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { createRequire } from 'node:module';
|
|
2
2
|
import { isDeclaredNonGoal } from './brief.js';
|
|
3
3
|
import { loadSourceDocument } from './source-document.js';
|
|
4
4
|
import projectionSchema from '../schema/yarramate-projection.schema.json' with { type: 'json'
|
|
5
5
|
};
|
|
6
|
-
const
|
|
6
|
+
const require = createRequire(import.meta.url);
|
|
7
|
+
const ajv2020Module = require('ajv/dist/2020.js');
|
|
8
|
+
const Ajv2020 = ajv2020Module.default ?? ajv2020Module;
|
|
7
9
|
const validateProjection = new Ajv2020({ allErrors: true }).compile(projectionSchema);
|
|
8
10
|
export function loadProjection(source) {
|
|
9
11
|
const loaded = loadSourceDocument(source, validateProjection, 'Projection');
|
|
@@ -45,6 +47,7 @@ export function canonicalProjection(projection) {
|
|
|
45
47
|
...(presentation.showLifecycle === undefined ? {} : { showLifecycle: presentation.showLifecycle }),
|
|
46
48
|
...(presentation.showEvidence === undefined ? {} : { showEvidence: presentation.showEvidence }),
|
|
47
49
|
...(presentation.showOwnership === undefined ? {} : { showOwnership: presentation.showOwnership }),
|
|
50
|
+
...(presentation.notation === undefined ? {} : { notation: presentation.notation }),
|
|
48
51
|
},
|
|
49
52
|
}),
|
|
50
53
|
};
|
|
@@ -226,6 +229,9 @@ export function evaluateProjection(graph, projection, profileContext) {
|
|
|
226
229
|
...(projection.presentation.showOwnership === undefined
|
|
227
230
|
? {}
|
|
228
231
|
: { showOwnership: projection.presentation.showOwnership }),
|
|
232
|
+
...(projection.presentation.notation === undefined
|
|
233
|
+
? {}
|
|
234
|
+
: { notation: projection.presentation.notation }),
|
|
229
235
|
},
|
|
230
236
|
}),
|
|
231
237
|
documents: graph.documents.filter(({ id }) => selectedDocuments.has(id)),
|
package/dist/reconciliation.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ATTESTATION_PREDICATE_PREFIX, parseAttestationClaimValue, } from './compiler.js';
|
|
1
|
+
import { ATTESTATION_PREDICATE_PREFIX, parseAttestationClaimValue, parseConstraintExpectsValue, } from './compiler.js';
|
|
2
2
|
const assertedRelationshipsByClaim = (graph) => {
|
|
3
3
|
const asserted = new Map();
|
|
4
4
|
if (graph === undefined)
|
|
@@ -30,7 +30,8 @@ export const constraintExpectsPredicate = 'yarramate/constraint/expects';
|
|
|
30
30
|
// A finding is now a union (ADR 0074); only the evidence arm can carry an
|
|
31
31
|
// expectation, so ordering reads it through one narrowing helper.
|
|
32
32
|
const expectationOf = (finding) => 'expectation' in finding ? finding.expectation : undefined;
|
|
33
|
-
//
|
|
33
|
+
// The compiler's parseConstraintExpectsValue is the sole authority for
|
|
34
|
+
// decoding the encoded value (ADR 0075). Provider and key admit no
|
|
34
35
|
// whitespace, so the first two spaces delimit them and everything after the
|
|
35
36
|
// second space is the expected value verbatim, spaces included.
|
|
36
37
|
const parseExpectation = (claim) => {
|
|
@@ -38,15 +39,15 @@ const parseExpectation = (claim) => {
|
|
|
38
39
|
!('value' in claim.object)) {
|
|
39
40
|
return undefined;
|
|
40
41
|
}
|
|
41
|
-
const
|
|
42
|
-
if (
|
|
42
|
+
const parts = parseConstraintExpectsValue(claim.object.value);
|
|
43
|
+
if (parts === undefined)
|
|
43
44
|
return undefined;
|
|
44
45
|
return {
|
|
45
46
|
claim: claim.id,
|
|
46
47
|
subject: claim.subject,
|
|
47
|
-
provider:
|
|
48
|
-
key:
|
|
49
|
-
expected:
|
|
48
|
+
provider: parts.provider,
|
|
49
|
+
key: parts.key,
|
|
50
|
+
expected: parts.value,
|
|
50
51
|
declared: {
|
|
51
52
|
document: claim.source.document,
|
|
52
53
|
path: claim.source.path,
|
|
@@ -21,6 +21,16 @@ export type SourceDocumentResult<T> = {
|
|
|
21
21
|
};
|
|
22
22
|
export declare const diagnosticOrder: (left: Diagnostic, right: Diagnostic) => number;
|
|
23
23
|
export declare const describeSchemaViolation: (error: Pick<ErrorObject, 'keyword' | 'message' | 'params'>) => string;
|
|
24
|
+
/**
|
|
25
|
+
* The errors worth reading, in the order Ajv found them.
|
|
26
|
+
*
|
|
27
|
+
* A discriminated union raises two errors when the tag itself is missing or
|
|
28
|
+
* mistyped: the `required`/`type` fault that names the property, and a
|
|
29
|
+
* `discriminator` row restating it. The second says nothing the first did not,
|
|
30
|
+
* so it is dropped. A tag that is present but names no branch keeps its row -
|
|
31
|
+
* that one is the only report of the fault.
|
|
32
|
+
*/
|
|
33
|
+
export declare const readableSchemaErrors: <T extends Pick<ErrorObject, 'keyword' | 'params'>>(errors: readonly T[]) => readonly T[];
|
|
24
34
|
export declare const closestCandidate: (value: string, candidates: Iterable<string>) => string | undefined;
|
|
25
35
|
export declare function locateSourcePath(sourcePath: string, yaml: ReturnType<typeof parseDocument>, lineCounter: LineCounter, yamlPath: readonly (string | number)[], pointer: string): SourceLocation;
|
|
26
36
|
export declare function loadSourceDocument<T>(source: WorkspaceSource, validate: ValidateFunction, schemaLabel: string): SourceDocumentResult<T>;
|
package/dist/source-document.js
CHANGED
|
@@ -18,8 +18,28 @@ export const describeSchemaViolation = (error) => {
|
|
|
18
18
|
.join(', ');
|
|
19
19
|
return `${base}: ${shown}${allowed.length > 8 ? ', …' : ''}`;
|
|
20
20
|
}
|
|
21
|
+
// The one pattern in these schemas that is a rule rather than a shape:
|
|
22
|
+
// `must match pattern "\S"` names the regex, not what the author did wrong.
|
|
23
|
+
if (error.keyword === 'pattern' && error.params.pattern === '\\S') {
|
|
24
|
+
return 'must not be blank';
|
|
25
|
+
}
|
|
26
|
+
// A tag naming no branch reads as a search failure ("must be in oneOf").
|
|
27
|
+
// Say what was named instead, since that is the value to correct.
|
|
28
|
+
if (error.keyword === 'discriminator' && error.params.error === 'mapping') {
|
|
29
|
+
return `unknown ${JSON.stringify(error.params.tag)} value ${JSON.stringify(error.params.tagValue)}`;
|
|
30
|
+
}
|
|
21
31
|
return base;
|
|
22
32
|
};
|
|
33
|
+
/**
|
|
34
|
+
* The errors worth reading, in the order Ajv found them.
|
|
35
|
+
*
|
|
36
|
+
* A discriminated union raises two errors when the tag itself is missing or
|
|
37
|
+
* mistyped: the `required`/`type` fault that names the property, and a
|
|
38
|
+
* `discriminator` row restating it. The second says nothing the first did not,
|
|
39
|
+
* so it is dropped. A tag that is present but names no branch keeps its row -
|
|
40
|
+
* that one is the only report of the fault.
|
|
41
|
+
*/
|
|
42
|
+
export const readableSchemaErrors = (errors) => errors.filter((error) => error.keyword !== 'discriminator' || error.params.error === 'mapping');
|
|
23
43
|
const editDistance = (left, right) => {
|
|
24
44
|
const previous = Array.from({ length: right.length + 1 }, (_, index) => index);
|
|
25
45
|
for (let i = 1; i <= left.length; i += 1) {
|
|
@@ -86,7 +106,7 @@ export function loadSourceDocument(source, validate, schemaLabel) {
|
|
|
86
106
|
if (!validate(value)) {
|
|
87
107
|
return {
|
|
88
108
|
ok: false,
|
|
89
|
-
diagnostics: (validate.errors ?? [])
|
|
109
|
+
diagnostics: readableSchemaErrors(validate.errors ?? [])
|
|
90
110
|
.map((error) => {
|
|
91
111
|
const property = error.keyword === 'additionalProperties'
|
|
92
112
|
? String(error.params.additionalProperty)
|