yarramate 0.9.0 → 0.11.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/dist/adapters/likec4-cli.js +44 -2
- package/dist/adapters/likec4-export.d.ts +8 -0
- package/dist/adapters/likec4-export.js +21 -0
- package/dist/adapters/likec4-project.d.ts +5 -2
- package/dist/adapters/likec4-project.js +63 -5
- package/dist/changed.d.ts +3 -0
- package/dist/changed.js +20 -10
- package/dist/cli-support.d.ts +1 -1
- package/dist/cli-support.js +1 -1
- package/dist/export-command.js +2 -0
- package/dist/projection.d.ts +1 -0
- package/dist/projection.js +21 -0
- package/package.json +1 -1
- package/schema/yarramate-ask-result.schema.json +7 -0
- package/schema/yarramate-projection.schema.json +8 -0
|
@@ -6,6 +6,7 @@ import { createHash, randomUUID } from 'node:crypto';
|
|
|
6
6
|
import Ajv2020Module from 'ajv/dist/2020.js';
|
|
7
7
|
import { isMap, isSeq, parseDocument } from 'yaml';
|
|
8
8
|
import { isMainModule, resolveCliWorkspaceSources, versionResult, } from '../cli-support.js';
|
|
9
|
+
import { deriveChangedSubjects } from '../changed.js';
|
|
9
10
|
import { compileWorkspace } from '../compiler.js';
|
|
10
11
|
import { adapterMappingLocation, loadAdapterMapping, validateAdapterMapping, } from '../adapter-mapping.js';
|
|
11
12
|
import { locateSourcePath } from '../source-document.js';
|
|
@@ -78,7 +79,7 @@ const usage = 'Usage:\n' +
|
|
|
78
79
|
' yarramate-likec4 check <likec4-project.yaml> [--json] <workspace-or-source...>\n' +
|
|
79
80
|
' yarramate-likec4 export <projection.yaml> <mapping.yaml> [--kinds <kind-mapping.yaml>] [--compare <from-state> <to-state>] <workspace-or-source...>\n' +
|
|
80
81
|
' yarramate-likec4 export-project [--check] <projection.yaml> <mapping.yaml> <output-dir> [--kinds <kind-mapping.yaml>] [--compare <from-state> <to-state>] <workspace-or-source...>\n' +
|
|
81
|
-
' yarramate-likec4 export-project [--check] <likec4-project.yaml> <output-dir> <workspace-or-source...>\n';
|
|
82
|
+
' yarramate-likec4 export-project [--check] <likec4-project.yaml> <output-dir> [--changed <git-range>] <workspace-or-source...>\n';
|
|
82
83
|
const diagnosticJson = (diagnostics) => `${JSON.stringify({
|
|
83
84
|
format: 'yarramate/likec4-diagnostic-result/v1',
|
|
84
85
|
diagnostics,
|
|
@@ -528,6 +529,7 @@ export function runLikeC4Cli(args, cwd = process.cwd()) {
|
|
|
528
529
|
let invalidOptions = false;
|
|
529
530
|
let kindMappingPath;
|
|
530
531
|
let comparison;
|
|
532
|
+
let changedRange;
|
|
531
533
|
const sourcePaths = [];
|
|
532
534
|
for (let index = 0; index < sourceOptions.length; index += 1) {
|
|
533
535
|
const option = sourceOptions[index];
|
|
@@ -549,6 +551,19 @@ export function runLikeC4Cli(args, cwd = process.cwd()) {
|
|
|
549
551
|
}
|
|
550
552
|
continue;
|
|
551
553
|
}
|
|
554
|
+
if (option === '--changed') {
|
|
555
|
+
const value = sourceOptions[index + 1];
|
|
556
|
+
if (value === undefined ||
|
|
557
|
+
value.startsWith('-') ||
|
|
558
|
+
changedRange !== undefined) {
|
|
559
|
+
invalidOptions = true;
|
|
560
|
+
}
|
|
561
|
+
else {
|
|
562
|
+
changedRange = value;
|
|
563
|
+
index += 1;
|
|
564
|
+
}
|
|
565
|
+
continue;
|
|
566
|
+
}
|
|
552
567
|
if (option === '--compare') {
|
|
553
568
|
const from = sourceOptions[index + 1];
|
|
554
569
|
const to = sourceOptions[index + 2];
|
|
@@ -581,6 +596,8 @@ export function runLikeC4Cli(args, cwd = process.cwd()) {
|
|
|
581
596
|
(command === 'export-project' && outputDirectory === undefined) ||
|
|
582
597
|
(projectDefinitionMode &&
|
|
583
598
|
(kindMappingPath !== undefined || comparison !== undefined)) ||
|
|
599
|
+
(changedRange !== undefined &&
|
|
600
|
+
!(projectDefinitionMode && command === 'export-project')) ||
|
|
584
601
|
invalidOptions ||
|
|
585
602
|
sourcePaths.length === 0 ||
|
|
586
603
|
sourcePaths.some((argument) => argument.startsWith('-'))) {
|
|
@@ -886,7 +903,32 @@ export function runLikeC4Cli(args, cwd = process.cwd()) {
|
|
|
886
903
|
}
|
|
887
904
|
renderedViewIds.add(renderedId);
|
|
888
905
|
}
|
|
889
|
-
|
|
906
|
+
let gitChange;
|
|
907
|
+
if (changedRange !== undefined) {
|
|
908
|
+
const modelDocuments = sources.flatMap((candidate) => {
|
|
909
|
+
const parsed = parseDocument(candidate.source);
|
|
910
|
+
if (parsed.get('format') !== 'yarramate/v1')
|
|
911
|
+
return [];
|
|
912
|
+
const documentId = parsed.get('id');
|
|
913
|
+
return typeof documentId === 'string'
|
|
914
|
+
? [{ ...candidate, documentId }]
|
|
915
|
+
: [];
|
|
916
|
+
});
|
|
917
|
+
const derived = deriveChangedSubjects(cwd, changedRange, modelDocuments);
|
|
918
|
+
if (!derived.ok) {
|
|
919
|
+
return { exitCode: 2, stdout: '', stderr: `${derived.message}\n` };
|
|
920
|
+
}
|
|
921
|
+
const all = [
|
|
922
|
+
...derived.changed.concepts,
|
|
923
|
+
...derived.changed.relationships,
|
|
924
|
+
];
|
|
925
|
+
gitChange = {
|
|
926
|
+
range: derived.changed.range,
|
|
927
|
+
added: derived.changed.added,
|
|
928
|
+
modified: all.filter((id) => !derived.changed.added.includes(id)),
|
|
929
|
+
};
|
|
930
|
+
}
|
|
931
|
+
const exported = exportLikeC4Project(loadedProject.document.value, successfulViews, gitChange === undefined ? {} : { gitChange });
|
|
890
932
|
if (!exported.ok) {
|
|
891
933
|
return {
|
|
892
934
|
exitCode: 1,
|
|
@@ -21,5 +21,13 @@ export type LikeC4ExportResult = {
|
|
|
21
21
|
};
|
|
22
22
|
export interface LikeC4ExportOptions {
|
|
23
23
|
readonly comparison?: StateComparison;
|
|
24
|
+
/** Git-derived review overlay (ADR 0066): subjects new or changed in a
|
|
25
|
+
* ref range carry metadata and view styling; nothing is authored. */
|
|
26
|
+
readonly gitChange?: GitChangeOverlay;
|
|
27
|
+
}
|
|
28
|
+
export interface GitChangeOverlay {
|
|
29
|
+
readonly range: string;
|
|
30
|
+
readonly added: readonly string[];
|
|
31
|
+
readonly modified: readonly string[];
|
|
24
32
|
}
|
|
25
33
|
export declare function exportLikeC4(projection: ProjectionResult, mapping: AdapterMapping, kindMapping?: LikeC4KindMapping, options?: LikeC4ExportOptions): LikeC4ExportResult;
|
|
@@ -62,6 +62,13 @@ export function exportLikeC4(projection, mapping, kindMapping, options = {}) {
|
|
|
62
62
|
const concepts = projection.subjects
|
|
63
63
|
.filter(({ type }) => type === 'concept')
|
|
64
64
|
.sort((left, right) => left.id.localeCompare(right.id));
|
|
65
|
+
const gitChangeOf = (id) => options.gitChange === undefined
|
|
66
|
+
? undefined
|
|
67
|
+
: options.gitChange.added.includes(id)
|
|
68
|
+
? 'new'
|
|
69
|
+
: options.gitChange.modified.includes(id)
|
|
70
|
+
? 'changed'
|
|
71
|
+
: undefined;
|
|
65
72
|
const comparisonChange = new Map(options.comparison === undefined
|
|
66
73
|
? []
|
|
67
74
|
: [
|
|
@@ -121,6 +128,7 @@ export function exportLikeC4(projection, mapping, kindMapping, options = {}) {
|
|
|
121
128
|
['yarramateId', concept.id],
|
|
122
129
|
['yarramateKind', semanticKind],
|
|
123
130
|
['yarramateChange', comparisonChange.get(concept.id)],
|
|
131
|
+
['yarramateGitChange', gitChangeOf(concept.id)],
|
|
124
132
|
[
|
|
125
133
|
'status',
|
|
126
134
|
valueFor(projection.claims, concept.id, 'yarramate/lifecycle/status'),
|
|
@@ -168,6 +176,7 @@ export function exportLikeC4(projection, mapping, kindMapping, options = {}) {
|
|
|
168
176
|
['yarramateId', relationship.id],
|
|
169
177
|
['yarramateKind', structural.predicate],
|
|
170
178
|
['yarramateChange', comparisonChange.get(relationship.id)],
|
|
179
|
+
['yarramateGitChange', gitChangeOf(relationship.id)],
|
|
171
180
|
[
|
|
172
181
|
'status',
|
|
173
182
|
valueFor(projection.claims, relationship.id, 'yarramate/lifecycle/status'),
|
|
@@ -212,6 +221,18 @@ export function exportLikeC4(projection, mapping, kindMapping, options = {}) {
|
|
|
212
221
|
return ` style ${external} { color red; border dashed }`;
|
|
213
222
|
}
|
|
214
223
|
return ` style ${external} { color gray }`;
|
|
224
|
+
})), ...(options.gitChange === undefined
|
|
225
|
+
? []
|
|
226
|
+
: concepts.flatMap((concept) => {
|
|
227
|
+
const external = externalByNative.get(concept.id);
|
|
228
|
+
const change = gitChangeOf(concept.id);
|
|
229
|
+
if (change === 'new') {
|
|
230
|
+
return [` style ${external} { color green }`];
|
|
231
|
+
}
|
|
232
|
+
if (change === 'changed') {
|
|
233
|
+
return [` style ${external} { color amber }`];
|
|
234
|
+
}
|
|
235
|
+
return [];
|
|
215
236
|
})), ' autoLayout LeftRight', ' }', '}', '');
|
|
216
237
|
return { ok: true, source: lines.join('\n') };
|
|
217
238
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { WorkspaceSource } from '../compiler.js';
|
|
2
2
|
import type { LikeC4PreparationResult } from './likec4-prepare.js';
|
|
3
|
-
import { type LikeC4ExportResult } from './likec4-export.js';
|
|
3
|
+
import { type GitChangeOverlay, type LikeC4ExportResult } from './likec4-export.js';
|
|
4
4
|
export interface LikeC4ProjectDefinition {
|
|
5
5
|
readonly format: 'yarramate/likec4-project/v1';
|
|
6
6
|
readonly id: string;
|
|
@@ -55,4 +55,7 @@ export interface PreparedLikeC4ProjectView {
|
|
|
55
55
|
};
|
|
56
56
|
readonly deployment?: LikeC4Deployment;
|
|
57
57
|
}
|
|
58
|
-
export
|
|
58
|
+
export interface LikeC4ProjectExportOptions {
|
|
59
|
+
readonly gitChange?: GitChangeOverlay;
|
|
60
|
+
}
|
|
61
|
+
export declare function exportLikeC4Project(project: LikeC4ProjectDefinition, views: readonly PreparedLikeC4ProjectView[], options?: LikeC4ProjectExportOptions): LikeC4ExportResult;
|
|
@@ -28,7 +28,7 @@ const unionProjection = (project, views) => ({
|
|
|
28
28
|
...new Map(views.flatMap(({ prepared }) => prepared.projection.claims.map((claim) => [claim.id, claim]))).values(),
|
|
29
29
|
].sort((left, right) => left.id.localeCompare(right.id)),
|
|
30
30
|
});
|
|
31
|
-
const viewBody = ({ id, prepared, dynamic, deployment, }) => {
|
|
31
|
+
const viewBody = ({ id, prepared, dynamic, deployment, }, gitChange) => {
|
|
32
32
|
const source = prepared.source;
|
|
33
33
|
const startToken = '\nviews {\n';
|
|
34
34
|
const start = source.indexOf(startToken);
|
|
@@ -117,10 +117,28 @@ const viewBody = ({ id, prepared, dynamic, deployment, }) => {
|
|
|
117
117
|
' }',
|
|
118
118
|
].join('\n');
|
|
119
119
|
}
|
|
120
|
+
const overlayRules = gitChange === undefined
|
|
121
|
+
? []
|
|
122
|
+
: prepared.projection.subjects
|
|
123
|
+
.filter(({ type }) => type === 'concept')
|
|
124
|
+
.flatMap(({ id: nativeId }) => {
|
|
125
|
+
const external = externalByNative.get(nativeId);
|
|
126
|
+
if (external === undefined)
|
|
127
|
+
return [];
|
|
128
|
+
if (gitChange.added.includes(nativeId)) {
|
|
129
|
+
return [` style ${external} { color green }`];
|
|
130
|
+
}
|
|
131
|
+
if (gitChange.modified.includes(nativeId)) {
|
|
132
|
+
return [` style ${external} { color amber }`];
|
|
133
|
+
}
|
|
134
|
+
return [];
|
|
135
|
+
})
|
|
136
|
+
.sort();
|
|
120
137
|
const membershipRules = [
|
|
121
138
|
includeRule,
|
|
122
139
|
' exclude * -> *',
|
|
123
140
|
...relationshipRules,
|
|
141
|
+
...overlayRules,
|
|
124
142
|
].join('\n');
|
|
125
143
|
return source
|
|
126
144
|
.slice(start + startToken.length, end)
|
|
@@ -168,12 +186,48 @@ const deploymentBody = ({ deployment, prepared, }) => {
|
|
|
168
186
|
'}',
|
|
169
187
|
].join('\n');
|
|
170
188
|
};
|
|
171
|
-
|
|
189
|
+
// The synthetic review view (ADR 0066): every changed subject with its
|
|
190
|
+
// highlight, and a description that doubles as the legend.
|
|
191
|
+
const reviewChangesView = (views, gitChange) => {
|
|
192
|
+
const externalByNative = new Map(views.flatMap((view) => view.prepared.subjectMapping.mappings
|
|
193
|
+
.filter(({ type }) => type === 'concept')
|
|
194
|
+
.map(({ native, external }) => [native, external])));
|
|
195
|
+
const changedConcepts = [
|
|
196
|
+
...new Set([...gitChange.added, ...gitChange.modified].flatMap((nativeId) => {
|
|
197
|
+
const external = externalByNative.get(nativeId);
|
|
198
|
+
return external === undefined ? [] : [external];
|
|
199
|
+
})),
|
|
200
|
+
].sort();
|
|
201
|
+
if (changedConcepts.length === 0)
|
|
202
|
+
return undefined;
|
|
203
|
+
const changedRelationships = [
|
|
204
|
+
...new Set([...gitChange.added, ...gitChange.modified].filter((nativeId) => !externalByNative.has(nativeId))),
|
|
205
|
+
].sort();
|
|
206
|
+
const styles = changedConcepts.map((external) => {
|
|
207
|
+
const nativeId = [...externalByNative.entries()].find(([, candidate]) => candidate === external)[0];
|
|
208
|
+
return gitChange.added.includes(nativeId)
|
|
209
|
+
? ` style ${external} { color green }`
|
|
210
|
+
: ` style ${external} { color amber }`;
|
|
211
|
+
});
|
|
212
|
+
return [
|
|
213
|
+
' view review-changes {',
|
|
214
|
+
` title ${quote(`Review: ${gitChange.range}`)}`,
|
|
215
|
+
` description ${quote(`Subjects touched in ${gitChange.range}. Legend: green = new, amber = changed; connected context unhighlighted. Derived from git - nothing here is authored (ADR 0066).`)}`,
|
|
216
|
+
` include ${changedConcepts.join(', ')}`,
|
|
217
|
+
...changedRelationships.map((id) => ` include * -> * where metadata.yarramateId is '${id}'`),
|
|
218
|
+
...styles,
|
|
219
|
+
' autoLayout LeftRight',
|
|
220
|
+
' }',
|
|
221
|
+
].join('\n');
|
|
222
|
+
};
|
|
223
|
+
export function exportLikeC4Project(project, views, options = {}) {
|
|
172
224
|
const first = views[0];
|
|
173
225
|
if (first === undefined) {
|
|
174
226
|
throw new Error('LikeC4 project requires at least one view');
|
|
175
227
|
}
|
|
176
|
-
const model = exportLikeC4(unionProjection(project, views), first.prepared.subjectMapping, first.prepared.kindMapping
|
|
228
|
+
const model = exportLikeC4(unionProjection(project, views), first.prepared.subjectMapping, first.prepared.kindMapping, options.gitChange === undefined
|
|
229
|
+
? {}
|
|
230
|
+
: { gitChange: options.gitChange });
|
|
177
231
|
if (!model.ok)
|
|
178
232
|
return model;
|
|
179
233
|
const startToken = '\nviews {\n';
|
|
@@ -182,9 +236,13 @@ export function exportLikeC4Project(project, views) {
|
|
|
182
236
|
const rendered = deploymentBody(view);
|
|
183
237
|
return rendered === undefined ? [] : [rendered];
|
|
184
238
|
});
|
|
185
|
-
const renderedViews = views.map(viewBody);
|
|
239
|
+
const renderedViews = views.map((view) => viewBody(view, options.gitChange));
|
|
240
|
+
const review = options.gitChange === undefined
|
|
241
|
+
? undefined
|
|
242
|
+
: reviewChangesView(views, options.gitChange);
|
|
243
|
+
const allViews = review === undefined ? renderedViews : [...renderedViews, review];
|
|
186
244
|
return {
|
|
187
245
|
ok: true,
|
|
188
|
-
source: `${model.source.slice(0, modelEnd)}${deployments.length === 0 ? '' : `\n${deployments.join('\n')}\n`}\nviews {\n${
|
|
246
|
+
source: `${model.source.slice(0, modelEnd)}${deployments.length === 0 ? '' : `\n${deployments.join('\n')}\n`}\nviews {\n${allViews.join('\n')}\n}\n`,
|
|
189
247
|
};
|
|
190
248
|
}
|
package/dist/changed.d.ts
CHANGED
|
@@ -2,6 +2,9 @@ export interface ChangedSubjects {
|
|
|
2
2
|
readonly range: string;
|
|
3
3
|
readonly concepts: readonly string[];
|
|
4
4
|
readonly relationships: readonly string[];
|
|
5
|
+
/** Subjects whose entire declaration is new in the range (subset of
|
|
6
|
+
* concepts + relationships); the rest changed in place. */
|
|
7
|
+
readonly added: readonly string[];
|
|
5
8
|
}
|
|
6
9
|
export type ChangedResult = {
|
|
7
10
|
readonly ok: true;
|
package/dist/changed.js
CHANGED
|
@@ -52,16 +52,21 @@ const itemSpans = (source) => {
|
|
|
52
52
|
}
|
|
53
53
|
return spans;
|
|
54
54
|
};
|
|
55
|
-
// New-side line ranges from `git diff --unified=0`. A pure deletion has a
|
|
56
|
-
// zero count; it still touches the position it collapsed onto.
|
|
57
55
|
const changedLineRanges = (diff) => {
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
const
|
|
62
|
-
|
|
56
|
+
const touched = [];
|
|
57
|
+
const inserted = [];
|
|
58
|
+
for (const match of diff.matchAll(/^@@ -\d+(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/gm)) {
|
|
59
|
+
const oldCount = match[1] === undefined ? 1 : Number(match[1]);
|
|
60
|
+
const start = Number(match[2]);
|
|
61
|
+
const count = match[3] === undefined ? 1 : Number(match[3]);
|
|
62
|
+
const range = count === 0
|
|
63
|
+
? [Math.max(start, 1), Math.max(start, 1)]
|
|
64
|
+
: [start, start + count - 1];
|
|
65
|
+
touched.push(range);
|
|
66
|
+
if (oldCount === 0 && count > 0)
|
|
67
|
+
inserted.push(range);
|
|
63
68
|
}
|
|
64
|
-
return
|
|
69
|
+
return { touched, inserted };
|
|
65
70
|
};
|
|
66
71
|
export function deriveChangedSubjects(cwd, range, documents) {
|
|
67
72
|
const probe = spawnSync('git', ['-C', cwd, 'rev-parse', '--git-dir'], {
|
|
@@ -75,6 +80,7 @@ export function deriveChangedSubjects(cwd, range, documents) {
|
|
|
75
80
|
}
|
|
76
81
|
const concepts = new Set();
|
|
77
82
|
const relationships = new Set();
|
|
83
|
+
const added = new Set();
|
|
78
84
|
for (const document of documents) {
|
|
79
85
|
const diffed = spawnSync('git', ['-C', cwd, 'diff', '--unified=0', range, '--', document.path], { encoding: 'utf8' });
|
|
80
86
|
if (diffed.status !== 0) {
|
|
@@ -84,13 +90,13 @@ export function deriveChangedSubjects(cwd, range, documents) {
|
|
|
84
90
|
};
|
|
85
91
|
}
|
|
86
92
|
const ranges = changedLineRanges(diffed.stdout ?? '');
|
|
87
|
-
if (ranges.length === 0)
|
|
93
|
+
if (ranges.touched.length === 0)
|
|
88
94
|
continue;
|
|
89
95
|
const spans = itemSpans(document.source);
|
|
90
96
|
for (const span of spans) {
|
|
91
97
|
if (span.collection === 'states')
|
|
92
98
|
continue;
|
|
93
|
-
const touched = ranges.some(([from, to]) => from <= span.endLine && to >= span.startLine);
|
|
99
|
+
const touched = ranges.touched.some(([from, to]) => from <= span.endLine && to >= span.startLine);
|
|
94
100
|
if (!touched)
|
|
95
101
|
continue;
|
|
96
102
|
const qualified = `${document.documentId}#${span.id}`;
|
|
@@ -98,6 +104,9 @@ export function deriveChangedSubjects(cwd, range, documents) {
|
|
|
98
104
|
concepts.add(qualified);
|
|
99
105
|
else
|
|
100
106
|
relationships.add(qualified);
|
|
107
|
+
const whollyInserted = ranges.inserted.some(([from, to]) => from <= span.startLine && to >= span.endLine);
|
|
108
|
+
if (whollyInserted)
|
|
109
|
+
added.add(qualified);
|
|
101
110
|
}
|
|
102
111
|
}
|
|
103
112
|
return {
|
|
@@ -106,6 +115,7 @@ export function deriveChangedSubjects(cwd, range, documents) {
|
|
|
106
115
|
range,
|
|
107
116
|
concepts: [...concepts].sort(),
|
|
108
117
|
relationships: [...relationships].sort(),
|
|
118
|
+
added: [...added].sort(),
|
|
109
119
|
},
|
|
110
120
|
};
|
|
111
121
|
}
|
package/dist/cli-support.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ export interface CliResult {
|
|
|
7
7
|
export declare const isMainModule: (moduleUrl: string, entrypoint: string | undefined) => boolean;
|
|
8
8
|
export declare const packageVersion: string;
|
|
9
9
|
export declare const versionResult: (binary: string) => CliResult;
|
|
10
|
-
export declare const usage = "Usage:\n yarramate init <directory> [--no-pointer]\n yarramate design <workspace.yaml> [--subject <document-id>#<local-id>] [--catalogue <catalogue.yaml>] [--json]\n yarramate apply <operations.yaml> <workspace.yaml> [--json]\n yarramate ask <workspace.yaml> [--json]\n yarramate ask <workspace.yaml> \"<free text>\" | <document-id>#<local-id> ... | <projection.yaml> [--budget <tokens>] [--json]\n yarramate ask <workspace.yaml> --subjects [--kind <term>] [--status <status>] [--json]\n yarramate ask <workspace.yaml> --kinds [--json]\n yarramate ask <workspace.yaml> --advise \"<topic>\" [--budget <tokens>] [--catalogue <catalogue.yaml>] [--json]\n yarramate ask <workspace.yaml> --next [--json]\n yarramate ask <workspace.yaml> --open [--catalogue <catalogue.yaml>] [--json]\n yarramate ask <workspace.yaml> --compare <from-state> <to-state> [--json]\n yarramate ask <workspace.yaml> --changed <git-range> [--budget <tokens>] [--json]\n yarramate check <source.yaml> [source.yaml ...] [--json] [--strict]\n yarramate reconcile <workspace.yaml>\n yarramate export graph <workspace.yaml> [--out <file>]\n yarramate export markdown <projection.yaml> <workspace.yaml> [--out <file>]\n yarramate export markdown --changed <git-range> <workspace.yaml> [--out <file>]\n yarramate export briefs <projection.yaml> <workspace.yaml> --out <directory> [--budget <tokens>]\n yarramate export briefs --changed <git-range> <workspace.yaml> --out <directory> [--budget <tokens>]\n yarramate export likec4 <likec4-project.yaml> <output-dir> <workspace.yaml
|
|
10
|
+
export declare const usage = "Usage:\n yarramate init <directory> [--no-pointer]\n yarramate design <workspace.yaml> [--subject <document-id>#<local-id>] [--catalogue <catalogue.yaml>] [--json]\n yarramate apply <operations.yaml> <workspace.yaml> [--json]\n yarramate ask <workspace.yaml> [--json]\n yarramate ask <workspace.yaml> \"<free text>\" | <document-id>#<local-id> ... | <projection.yaml> [--budget <tokens>] [--json]\n yarramate ask <workspace.yaml> --subjects [--kind <term>] [--status <status>] [--json]\n yarramate ask <workspace.yaml> --kinds [--json]\n yarramate ask <workspace.yaml> --advise \"<topic>\" [--budget <tokens>] [--catalogue <catalogue.yaml>] [--json]\n yarramate ask <workspace.yaml> --next [--json]\n yarramate ask <workspace.yaml> --open [--catalogue <catalogue.yaml>] [--json]\n yarramate ask <workspace.yaml> --compare <from-state> <to-state> [--json]\n yarramate ask <workspace.yaml> --changed <git-range> [--budget <tokens>] [--json]\n yarramate check <source.yaml> [source.yaml ...] [--json] [--strict]\n yarramate reconcile <workspace.yaml>\n yarramate export graph <workspace.yaml> [--out <file>]\n yarramate export markdown <projection.yaml> <workspace.yaml> [--out <file>]\n yarramate export markdown --changed <git-range> <workspace.yaml> [--out <file>]\n yarramate export briefs <projection.yaml> <workspace.yaml> --out <directory> [--budget <tokens>]\n yarramate export briefs --changed <git-range> <workspace.yaml> --out <directory> [--budget <tokens>]\n yarramate export likec4 <likec4-project.yaml> <output-dir> <workspace.yaml> [--changed <git-range>]\n";
|
|
11
11
|
export declare const diagnosticJson: (diagnostics: unknown) => string;
|
|
12
12
|
export declare const checkResultJson: (ok: boolean, diagnostics: unknown, counted?: {
|
|
13
13
|
readonly documents: number;
|
package/dist/cli-support.js
CHANGED
|
@@ -23,7 +23,7 @@ export const versionResult = (binary) => ({
|
|
|
23
23
|
stdout: `${binary} ${packageVersion}\n`,
|
|
24
24
|
stderr: '',
|
|
25
25
|
});
|
|
26
|
-
export const usage = 'Usage:\n yarramate init <directory> [--no-pointer]\n yarramate design <workspace.yaml> [--subject <document-id>#<local-id>] [--catalogue <catalogue.yaml>] [--json]\n yarramate apply <operations.yaml> <workspace.yaml> [--json]\n yarramate ask <workspace.yaml> [--json]\n yarramate ask <workspace.yaml> "<free text>" | <document-id>#<local-id> ... | <projection.yaml> [--budget <tokens>] [--json]\n yarramate ask <workspace.yaml> --subjects [--kind <term>] [--status <status>] [--json]\n yarramate ask <workspace.yaml> --kinds [--json]\n yarramate ask <workspace.yaml> --advise "<topic>" [--budget <tokens>] [--catalogue <catalogue.yaml>] [--json]\n yarramate ask <workspace.yaml> --next [--json]\n yarramate ask <workspace.yaml> --open [--catalogue <catalogue.yaml>] [--json]\n yarramate ask <workspace.yaml> --compare <from-state> <to-state> [--json]\n yarramate ask <workspace.yaml> --changed <git-range> [--budget <tokens>] [--json]\n yarramate check <source.yaml> [source.yaml ...] [--json] [--strict]\n yarramate reconcile <workspace.yaml>\n yarramate export graph <workspace.yaml> [--out <file>]\n yarramate export markdown <projection.yaml> <workspace.yaml> [--out <file>]\n yarramate export markdown --changed <git-range> <workspace.yaml> [--out <file>]\n yarramate export briefs <projection.yaml> <workspace.yaml> --out <directory> [--budget <tokens>]\n yarramate export briefs --changed <git-range> <workspace.yaml> --out <directory> [--budget <tokens>]\n yarramate export likec4 <likec4-project.yaml> <output-dir> <workspace.yaml
|
|
26
|
+
export const usage = 'Usage:\n yarramate init <directory> [--no-pointer]\n yarramate design <workspace.yaml> [--subject <document-id>#<local-id>] [--catalogue <catalogue.yaml>] [--json]\n yarramate apply <operations.yaml> <workspace.yaml> [--json]\n yarramate ask <workspace.yaml> [--json]\n yarramate ask <workspace.yaml> "<free text>" | <document-id>#<local-id> ... | <projection.yaml> [--budget <tokens>] [--json]\n yarramate ask <workspace.yaml> --subjects [--kind <term>] [--status <status>] [--json]\n yarramate ask <workspace.yaml> --kinds [--json]\n yarramate ask <workspace.yaml> --advise "<topic>" [--budget <tokens>] [--catalogue <catalogue.yaml>] [--json]\n yarramate ask <workspace.yaml> --next [--json]\n yarramate ask <workspace.yaml> --open [--catalogue <catalogue.yaml>] [--json]\n yarramate ask <workspace.yaml> --compare <from-state> <to-state> [--json]\n yarramate ask <workspace.yaml> --changed <git-range> [--budget <tokens>] [--json]\n yarramate check <source.yaml> [source.yaml ...] [--json] [--strict]\n yarramate reconcile <workspace.yaml>\n yarramate export graph <workspace.yaml> [--out <file>]\n yarramate export markdown <projection.yaml> <workspace.yaml> [--out <file>]\n yarramate export markdown --changed <git-range> <workspace.yaml> [--out <file>]\n yarramate export briefs <projection.yaml> <workspace.yaml> --out <directory> [--budget <tokens>]\n yarramate export briefs --changed <git-range> <workspace.yaml> --out <directory> [--budget <tokens>]\n yarramate export likec4 <likec4-project.yaml> <output-dir> <workspace.yaml> [--changed <git-range>]\n';
|
|
27
27
|
export const diagnosticJson = (diagnostics) => `${JSON.stringify({
|
|
28
28
|
format: 'yarramate/diagnostic-result/v1',
|
|
29
29
|
diagnostics,
|
package/dist/export-command.js
CHANGED
|
@@ -91,6 +91,7 @@ export function runExportCommand(options, cwd) {
|
|
|
91
91
|
parsed.json) {
|
|
92
92
|
return { exitCode: 2, stdout: '', stderr: usage };
|
|
93
93
|
}
|
|
94
|
+
const changedArguments = parsed.changed === undefined ? [] : ['--changed', parsed.changed];
|
|
94
95
|
if (!existsSync(likec4AdapterEntry)) {
|
|
95
96
|
return {
|
|
96
97
|
exitCode: 2,
|
|
@@ -105,6 +106,7 @@ export function runExportCommand(options, cwd) {
|
|
|
105
106
|
projectDefinition,
|
|
106
107
|
outputDirectory,
|
|
107
108
|
workspacePath,
|
|
109
|
+
...changedArguments,
|
|
108
110
|
], { cwd, encoding: 'utf8' });
|
|
109
111
|
const exitCode = delegated.status === 0 ? 0 : delegated.status === 1 ? 1 : 2;
|
|
110
112
|
return {
|
package/dist/projection.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ export interface ProjectionDefinition {
|
|
|
9
9
|
readonly documents?: readonly string[];
|
|
10
10
|
readonly kinds?: readonly string[];
|
|
11
11
|
readonly statuses?: readonly LifecycleStatus[];
|
|
12
|
+
readonly excludeStatuses?: readonly LifecycleStatus[];
|
|
12
13
|
readonly states?: readonly string[];
|
|
13
14
|
readonly owners?: readonly string[];
|
|
14
15
|
readonly constraints?: readonly string[];
|
package/dist/projection.js
CHANGED
|
@@ -65,6 +65,9 @@ export function evaluateProjection(graph, projection, profileContext) {
|
|
|
65
65
|
(projection.query.statuses === undefined ||
|
|
66
66
|
(status !== undefined &&
|
|
67
67
|
projection.query.statuses.includes(status))) &&
|
|
68
|
+
(projection.query.excludeStatuses === undefined ||
|
|
69
|
+
status === undefined ||
|
|
70
|
+
!projection.query.excludeStatuses.includes(status)) &&
|
|
68
71
|
(projection.query.owners === undefined ||
|
|
69
72
|
(owner !== undefined &&
|
|
70
73
|
projection.query.owners.includes(owner))) &&
|
|
@@ -80,8 +83,12 @@ export function evaluateProjection(graph, projection, profileContext) {
|
|
|
80
83
|
if (subject.type !== 'relationship')
|
|
81
84
|
continue;
|
|
82
85
|
const relationship = graph.claims.find((claim) => claim.id === subject.id);
|
|
86
|
+
const relationshipStatus = claimValue(graph.claims, subject.id, 'yarramate/lifecycle/status');
|
|
83
87
|
if (relationship === undefined ||
|
|
84
88
|
!('ref' in relationship.object) ||
|
|
89
|
+
(projection.query.excludeStatuses !== undefined &&
|
|
90
|
+
relationshipStatus !== undefined &&
|
|
91
|
+
projection.query.excludeStatuses.includes(relationshipStatus)) ||
|
|
85
92
|
(projection.query.relationshipKinds !== undefined &&
|
|
86
93
|
!projection.query.relationshipKinds.some((selectedKind) => selectedKind === relationship.predicate ||
|
|
87
94
|
(projection.query.kindMatching === 'descendants' &&
|
|
@@ -93,6 +100,20 @@ export function evaluateProjection(graph, projection, profileContext) {
|
|
|
93
100
|
}
|
|
94
101
|
const sourceSelected = initiallySelectedConceptIds.has(relationship.subject);
|
|
95
102
|
const targetSelected = initiallySelectedConceptIds.has(relationship.object.ref);
|
|
103
|
+
// excludeStatuses also vetoes connected expansion: an excluded
|
|
104
|
+
// concept is never pulled in as a neighbour, and edges touching
|
|
105
|
+
// one are dropped rather than left dangling.
|
|
106
|
+
const endpointExcluded = (id) => {
|
|
107
|
+
if (projection.query.excludeStatuses === undefined)
|
|
108
|
+
return false;
|
|
109
|
+
const endpointStatus = claimValue(graph.claims, id, 'yarramate/lifecycle/status');
|
|
110
|
+
return (endpointStatus !== undefined &&
|
|
111
|
+
projection.query.excludeStatuses.includes(endpointStatus));
|
|
112
|
+
};
|
|
113
|
+
if (endpointExcluded(relationship.subject) ||
|
|
114
|
+
endpointExcluded(relationship.object.ref)) {
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
96
117
|
if ((relationshipMode === 'between' &&
|
|
97
118
|
sourceSelected &&
|
|
98
119
|
targetSelected) ||
|
package/package.json
CHANGED
|
@@ -49,6 +49,14 @@
|
|
|
49
49
|
"enum": ["planned", "current", "retired"]
|
|
50
50
|
}
|
|
51
51
|
},
|
|
52
|
+
"excludeStatuses": {
|
|
53
|
+
"description": "Drop concepts carrying one of these lifecycle statuses while keeping concepts that declare no status at all - 'everything except retired' for viewpoint projections, where a bare statuses filter would wrongly drop unstatused actors and motivation elements.",
|
|
54
|
+
"type": "array",
|
|
55
|
+
"uniqueItems": true,
|
|
56
|
+
"items": {
|
|
57
|
+
"enum": ["planned", "current", "retired"]
|
|
58
|
+
}
|
|
59
|
+
},
|
|
52
60
|
"states": {
|
|
53
61
|
"type": "array",
|
|
54
62
|
"minItems": 1,
|