yarramate 0.2.0 → 0.3.1
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 +232 -14
- package/dist/check-command.js +20 -2
- package/dist/cli-support.d.ts +6 -1
- package/dist/cli-support.js +2 -1
- package/package.json +1 -1
- package/schema/yarramate-check-result.schema.json +15 -0
- package/schema/yarramate-likec4-diagnostic-result.schema.json +1 -1
- package/schema/yarramate-likec4-project.schema.json +1 -0
- package/skills/yarramate-architecture/references/native-authoring.md +13 -0
|
@@ -4,8 +4,10 @@ import { resolve } from 'node:path';
|
|
|
4
4
|
import { fileURLToPath } from 'node:url';
|
|
5
5
|
import { createHash, randomUUID } from 'node:crypto';
|
|
6
6
|
import Ajv2020Module from 'ajv/dist/2020.js';
|
|
7
|
-
import { parseDocument } from 'yaml';
|
|
7
|
+
import { isMap, isSeq, parseDocument } from 'yaml';
|
|
8
8
|
import { isMainModule, resolveCliWorkspaceSources, } from '../cli-support.js';
|
|
9
|
+
import { compileWorkspace } from '../compiler.js';
|
|
10
|
+
import { adapterMappingLocation, loadAdapterMapping, validateAdapterMapping, } from '../adapter-mapping.js';
|
|
9
11
|
import { locateSourcePath } from '../source-document.js';
|
|
10
12
|
import { prepareLikeC4Export, } from './likec4-prepare.js';
|
|
11
13
|
import { exportLikeC4Project, loadLikeC4ProjectDefinition, } from './likec4-project.js';
|
|
@@ -71,6 +73,7 @@ const publishFiles = (files) => {
|
|
|
71
73
|
}
|
|
72
74
|
};
|
|
73
75
|
const usage = 'Usage:\n' +
|
|
76
|
+
' yarramate-likec4 map --sync [--prune] <mapping.yaml> <workspace-or-source...>\n' +
|
|
74
77
|
' yarramate-likec4 check <projection.yaml> <mapping.yaml> [--json] [--kinds <kind-mapping.yaml>] [--compare <from-state> <to-state>] <workspace-or-source...>\n' +
|
|
75
78
|
' yarramate-likec4 check <likec4-project.yaml> [--json] <workspace-or-source...>\n' +
|
|
76
79
|
' yarramate-likec4 export <projection.yaml> <mapping.yaml> [--kinds <kind-mapping.yaml>] [--compare <from-state> <to-state>] <workspace-or-source...>\n' +
|
|
@@ -86,6 +89,180 @@ const checkJson = (ok, diagnostics) => `${JSON.stringify({
|
|
|
86
89
|
diagnostics,
|
|
87
90
|
}, null, 2)}\n`;
|
|
88
91
|
const sameJson = (left, right) => JSON.stringify(left) === JSON.stringify(right);
|
|
92
|
+
const lowerCamel = (value) => value.replaceAll(/-([a-z0-9])/g, (_, character) => character.toUpperCase());
|
|
93
|
+
const runLikeC4MapSync = (args, cwd) => {
|
|
94
|
+
const sync = args[0];
|
|
95
|
+
const prune = args[1] === '--prune';
|
|
96
|
+
const mappingPath = args[prune ? 2 : 1];
|
|
97
|
+
const sourcePaths = args.slice(prune ? 3 : 2);
|
|
98
|
+
if (sync !== '--sync' ||
|
|
99
|
+
mappingPath === undefined ||
|
|
100
|
+
mappingPath.startsWith('-') ||
|
|
101
|
+
sourcePaths.length === 0 ||
|
|
102
|
+
sourcePaths.some((path) => path.startsWith('-'))) {
|
|
103
|
+
return { exitCode: 2, stdout: '', stderr: usage };
|
|
104
|
+
}
|
|
105
|
+
try {
|
|
106
|
+
const resolved = resolveCliWorkspaceSources(sourcePaths, cwd);
|
|
107
|
+
if (!resolved.ok) {
|
|
108
|
+
return {
|
|
109
|
+
exitCode: 1,
|
|
110
|
+
stdout: diagnosticJson(resolved.diagnostics),
|
|
111
|
+
stderr: '',
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
const compilation = compileWorkspace(resolved.paths.map((path) => ({
|
|
115
|
+
path,
|
|
116
|
+
source: readFileSync(resolve(cwd, path), 'utf8'),
|
|
117
|
+
})));
|
|
118
|
+
if (!compilation.ok) {
|
|
119
|
+
return {
|
|
120
|
+
exitCode: 1,
|
|
121
|
+
stdout: diagnosticJson(compilation.diagnostics),
|
|
122
|
+
stderr: '',
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
const absoluteMappingPath = resolve(cwd, mappingPath);
|
|
126
|
+
const original = readFileSync(absoluteMappingPath, 'utf8');
|
|
127
|
+
const loaded = loadAdapterMapping({
|
|
128
|
+
path: mappingPath,
|
|
129
|
+
source: original,
|
|
130
|
+
});
|
|
131
|
+
if (!loaded.ok) {
|
|
132
|
+
return {
|
|
133
|
+
exitCode: 1,
|
|
134
|
+
stdout: diagnosticJson(loaded.diagnostics),
|
|
135
|
+
stderr: '',
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
if (loaded.mapping.adapter !== 'likec4') {
|
|
139
|
+
const location = adapterMappingLocation(loaded.mapping, 'adapter');
|
|
140
|
+
return {
|
|
141
|
+
exitCode: 1,
|
|
142
|
+
stdout: diagnosticJson([{
|
|
143
|
+
severity: 'error',
|
|
144
|
+
code: 'YMLC101',
|
|
145
|
+
message: `Adapter mapping targets "${loaded.mapping.adapter}", not "likec4"`,
|
|
146
|
+
...location,
|
|
147
|
+
}]),
|
|
148
|
+
stderr: '',
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
const graphSubjects = new Set(compilation.graph.subjects.map(({ id }) => id));
|
|
152
|
+
const staleCount = loaded.mapping.mappings.filter(({ native }) => !graphSubjects.has(native)).length;
|
|
153
|
+
const validation = validateAdapterMapping(compilation.graph, loaded.mapping);
|
|
154
|
+
const blockingDiagnostics = validation.ok
|
|
155
|
+
? []
|
|
156
|
+
: validation.diagnostics.filter(({ code }) => code !== 'YM601');
|
|
157
|
+
if (blockingDiagnostics.length > 0) {
|
|
158
|
+
return {
|
|
159
|
+
exitCode: 1,
|
|
160
|
+
stdout: diagnosticJson(blockingDiagnostics),
|
|
161
|
+
stderr: '',
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
const mapped = new Set(loaded.mapping.mappings.map(({ native }) => native));
|
|
165
|
+
const claimedExternal = new Set(loaded.mapping.mappings
|
|
166
|
+
.filter(({ native }) => !prune || graphSubjects.has(native))
|
|
167
|
+
.map(({ external }) => external));
|
|
168
|
+
const architectureStates = new Set(compilation.graph.claims
|
|
169
|
+
.filter(({ predicate }) => predicate === 'yarramate/state/type')
|
|
170
|
+
.map(({ subject }) => subject));
|
|
171
|
+
const additions = compilation.graph.subjects
|
|
172
|
+
.filter(({ id }) => !mapped.has(id) && !architectureStates.has(id))
|
|
173
|
+
.map((subject) => {
|
|
174
|
+
const [documentId, localId] = subject.id.split('#');
|
|
175
|
+
const local = lowerCamel(localId);
|
|
176
|
+
let external = local;
|
|
177
|
+
if (claimedExternal.has(external)) {
|
|
178
|
+
external = `${lowerCamel(documentId)}_${local}`;
|
|
179
|
+
}
|
|
180
|
+
let suffix = 2;
|
|
181
|
+
const base = external;
|
|
182
|
+
while (claimedExternal.has(external)) {
|
|
183
|
+
external = `${base}_${suffix}`;
|
|
184
|
+
suffix += 1;
|
|
185
|
+
}
|
|
186
|
+
claimedExternal.add(external);
|
|
187
|
+
return {
|
|
188
|
+
native: subject.id,
|
|
189
|
+
external,
|
|
190
|
+
type: subject.type,
|
|
191
|
+
};
|
|
192
|
+
});
|
|
193
|
+
if (additions.length === 0 && (!prune || staleCount === 0)) {
|
|
194
|
+
return {
|
|
195
|
+
exitCode: 0,
|
|
196
|
+
stdout: staleCount === 0
|
|
197
|
+
? `LikeC4 mapping ${mappingPath} is already synchronized\n`
|
|
198
|
+
: `LikeC4 mapping ${mappingPath} has ${staleCount} stale ${staleCount === 1 ? 'mapping' : 'mappings'} (use --prune)\n`,
|
|
199
|
+
stderr: '',
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
const document = parseDocument(original);
|
|
203
|
+
const mappings = document.getIn(['mappings'], true);
|
|
204
|
+
if (prune && isSeq(mappings)) {
|
|
205
|
+
const firstMappingWasPruned = mappings.items.length > 0 &&
|
|
206
|
+
isMap(mappings.items[0]) &&
|
|
207
|
+
!graphSubjects.has(String(mappings.items[0].get('native')));
|
|
208
|
+
mappings.items = mappings.items.filter((item) => !isMap(item) ||
|
|
209
|
+
graphSubjects.has(String(item.get('native'))));
|
|
210
|
+
if (firstMappingWasPruned)
|
|
211
|
+
mappings.commentBefore = undefined;
|
|
212
|
+
}
|
|
213
|
+
for (const addition of additions) {
|
|
214
|
+
document.addIn(['mappings'], addition);
|
|
215
|
+
}
|
|
216
|
+
if (isSeq(mappings))
|
|
217
|
+
mappings.flow = false;
|
|
218
|
+
const candidate = document.toString({ lineWidth: 0 });
|
|
219
|
+
const candidateMapping = loadAdapterMapping({
|
|
220
|
+
path: mappingPath,
|
|
221
|
+
source: candidate,
|
|
222
|
+
});
|
|
223
|
+
if (!candidateMapping.ok) {
|
|
224
|
+
return {
|
|
225
|
+
exitCode: 1,
|
|
226
|
+
stdout: diagnosticJson(candidateMapping.diagnostics),
|
|
227
|
+
stderr: '',
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
const candidateValidation = validateAdapterMapping(compilation.graph, candidateMapping.mapping);
|
|
231
|
+
const candidateBlockingDiagnostics = candidateValidation.ok
|
|
232
|
+
? []
|
|
233
|
+
: candidateValidation.diagnostics.filter(({ code }) => prune || code !== 'YM601');
|
|
234
|
+
if (candidateBlockingDiagnostics.length > 0) {
|
|
235
|
+
return {
|
|
236
|
+
exitCode: 1,
|
|
237
|
+
stdout: diagnosticJson(candidateBlockingDiagnostics),
|
|
238
|
+
stderr: '',
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
const staged = stageFile(absoluteMappingPath, candidate);
|
|
242
|
+
try {
|
|
243
|
+
staged.publish();
|
|
244
|
+
}
|
|
245
|
+
finally {
|
|
246
|
+
staged.cleanup();
|
|
247
|
+
}
|
|
248
|
+
return {
|
|
249
|
+
exitCode: 0,
|
|
250
|
+
stdout: prune
|
|
251
|
+
? additions.length > 0
|
|
252
|
+
? `Added ${additions.length} and pruned ${staleCount} stale LikeC4 ${staleCount === 1 ? 'mapping' : 'mappings'} in ${mappingPath}\n`
|
|
253
|
+
: `Pruned ${staleCount} stale LikeC4 ${staleCount === 1 ? 'mapping' : 'mappings'} from ${mappingPath}\n`
|
|
254
|
+
: `Added ${additions.length} LikeC4 ${additions.length === 1 ? 'mapping' : 'mappings'} to ${mappingPath}` +
|
|
255
|
+
(staleCount === 0
|
|
256
|
+
? '\n'
|
|
257
|
+
: `; left ${staleCount} stale ${staleCount === 1 ? 'mapping' : 'mappings'} (use --prune)\n`),
|
|
258
|
+
stderr: '',
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
catch (error) {
|
|
262
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
263
|
+
return { exitCode: 2, stdout: '', stderr: `${message}\n` };
|
|
264
|
+
}
|
|
265
|
+
};
|
|
89
266
|
const publishGeneratedProject = (cwd, outputDirectory, input) => {
|
|
90
267
|
const projectPath = resolve(cwd, outputDirectory);
|
|
91
268
|
const markerPath = resolve(projectPath, 'yarramate.generated.json');
|
|
@@ -194,6 +371,9 @@ const publishGeneratedProject = (cwd, outputDirectory, input) => {
|
|
|
194
371
|
};
|
|
195
372
|
};
|
|
196
373
|
export function runLikeC4Cli(args, cwd = process.cwd()) {
|
|
374
|
+
if (args[0] === 'map') {
|
|
375
|
+
return runLikeC4MapSync(args.slice(1), cwd);
|
|
376
|
+
}
|
|
197
377
|
const [command, projectionPath, mappingPath, ...options] = args;
|
|
198
378
|
let projectDefinitionMode = false;
|
|
199
379
|
if ((command === 'check' || command === 'export-project') &&
|
|
@@ -307,25 +487,63 @@ export function runLikeC4Cli(args, cwd = process.cwd()) {
|
|
|
307
487
|
stderr: '',
|
|
308
488
|
};
|
|
309
489
|
}
|
|
310
|
-
const
|
|
490
|
+
const referencedSources = new Map();
|
|
491
|
+
const referenceDiagnostics = [];
|
|
492
|
+
const readProjectReference = (path, label, yamlPath, pointer) => {
|
|
493
|
+
const existing = referencedSources.get(path);
|
|
494
|
+
if (existing !== undefined)
|
|
495
|
+
return existing;
|
|
496
|
+
try {
|
|
497
|
+
const source = {
|
|
498
|
+
path,
|
|
499
|
+
source: readFileSync(resolve(cwd, path), 'utf8'),
|
|
500
|
+
};
|
|
501
|
+
referencedSources.set(path, source);
|
|
502
|
+
return source;
|
|
503
|
+
}
|
|
504
|
+
catch (error) {
|
|
505
|
+
const location = locateSourcePath(projectSource.path, loadedProject.document.yaml, loadedProject.document.lineCounter, yamlPath, pointer);
|
|
506
|
+
const absent = error instanceof Error &&
|
|
507
|
+
'code' in error &&
|
|
508
|
+
error.code === 'ENOENT';
|
|
509
|
+
referenceDiagnostics.push({
|
|
510
|
+
severity: 'error',
|
|
511
|
+
code: 'YMLC110',
|
|
512
|
+
message: absent
|
|
513
|
+
? `LikeC4 project ${label} "${path}" does not exist`
|
|
514
|
+
: `LikeC4 project ${label} "${path}" cannot be read`,
|
|
515
|
+
...location,
|
|
516
|
+
});
|
|
517
|
+
return undefined;
|
|
518
|
+
}
|
|
519
|
+
};
|
|
520
|
+
const subjectMapping = readProjectReference(loadedProject.document.value.mapping, 'mapping', ['mapping'], '/mapping');
|
|
521
|
+
const kindMapping = loadedProject.document.value.kindMapping === undefined
|
|
522
|
+
? undefined
|
|
523
|
+
: readProjectReference(loadedProject.document.value.kindMapping, 'kind mapping', ['kindMapping'], '/kindMapping');
|
|
524
|
+
const projections = loadedProject.document.value.views.map((view, index) => readProjectReference(view.projection, 'projection', ['views', index, 'projection'], `/views/${index}/projection`));
|
|
525
|
+
if (referenceDiagnostics.length > 0 ||
|
|
526
|
+
subjectMapping === undefined) {
|
|
527
|
+
return {
|
|
528
|
+
exitCode: 1,
|
|
529
|
+
stdout: diagnosticOutput(referenceDiagnostics.sort((left, right) => left.path.localeCompare(right.path) ||
|
|
530
|
+
left.line - right.line ||
|
|
531
|
+
left.column - right.column ||
|
|
532
|
+
left.code.localeCompare(right.code) ||
|
|
533
|
+
left.message.localeCompare(right.message))),
|
|
534
|
+
stderr: '',
|
|
535
|
+
};
|
|
536
|
+
}
|
|
537
|
+
const preparedViews = loadedProject.document.value.views.map((view, index) => ({
|
|
311
538
|
view,
|
|
312
539
|
prepared: prepareLikeC4Export({
|
|
313
540
|
sources,
|
|
314
|
-
projection:
|
|
315
|
-
|
|
316
|
-
source: readFileSync(resolve(cwd, view.projection), 'utf8'),
|
|
317
|
-
},
|
|
318
|
-
subjectMapping: {
|
|
319
|
-
path: loadedProject.document.value.mapping,
|
|
320
|
-
source: readFileSync(resolve(cwd, loadedProject.document.value.mapping), 'utf8'),
|
|
321
|
-
},
|
|
541
|
+
projection: projections[index],
|
|
542
|
+
subjectMapping,
|
|
322
543
|
...(loadedProject.document.value.kindMapping === undefined
|
|
323
544
|
? {}
|
|
324
545
|
: {
|
|
325
|
-
kindMapping:
|
|
326
|
-
path: loadedProject.document.value.kindMapping,
|
|
327
|
-
source: readFileSync(resolve(cwd, loadedProject.document.value.kindMapping), 'utf8'),
|
|
328
|
-
},
|
|
546
|
+
kindMapping: kindMapping,
|
|
329
547
|
}),
|
|
330
548
|
...(view.compare === undefined
|
|
331
549
|
? {}
|
package/dist/check-command.js
CHANGED
|
@@ -171,14 +171,28 @@ export function runCheckCommand(options, cwd) {
|
|
|
171
171
|
const diagnostics = result.ok
|
|
172
172
|
? optionalDiagnostics
|
|
173
173
|
: result.diagnostics;
|
|
174
|
+
const counted = result.ok
|
|
175
|
+
? (() => {
|
|
176
|
+
const states = new Set(result.graph.claims
|
|
177
|
+
.filter(({ predicate }) => predicate === 'yarramate/state/type')
|
|
178
|
+
.map(({ subject }) => subject));
|
|
179
|
+
return {
|
|
180
|
+
documents: result.graph.documents.length,
|
|
181
|
+
concepts: result.graph.subjects.filter(({ id, type }) => type === 'concept' && !states.has(id)).length,
|
|
182
|
+
relationships: result.graph.subjects.filter(({ type }) => type === 'relationship').length,
|
|
183
|
+
states: states.size,
|
|
184
|
+
};
|
|
185
|
+
})()
|
|
186
|
+
: undefined;
|
|
174
187
|
if (json) {
|
|
175
188
|
return {
|
|
176
189
|
exitCode: ok ? 0 : 1,
|
|
177
|
-
stdout: checkResultJson(ok, ok ? [] : diagnostics),
|
|
190
|
+
stdout: checkResultJson(ok, ok ? [] : diagnostics, ok ? counted : undefined),
|
|
178
191
|
stderr: '',
|
|
179
192
|
};
|
|
180
193
|
}
|
|
181
194
|
if (ok && result.ok) {
|
|
195
|
+
const successfulCounts = counted;
|
|
182
196
|
const documentCount = result.graph.documents.length;
|
|
183
197
|
const profileCount = coreSources.length - documentCount;
|
|
184
198
|
const mappingCount = mappingSources.length;
|
|
@@ -215,7 +229,11 @@ export function runCheckCommand(options, cwd) {
|
|
|
215
229
|
].join(' and ');
|
|
216
230
|
return {
|
|
217
231
|
exitCode: 0,
|
|
218
|
-
stdout: `Checked ${checked}
|
|
232
|
+
stdout: `Checked ${checked} (` +
|
|
233
|
+
`${successfulCounts.concepts} ${successfulCounts.concepts === 1 ? 'concept' : 'concepts'}, ` +
|
|
234
|
+
`${successfulCounts.relationships} ${successfulCounts.relationships === 1 ? 'relationship' : 'relationships'}, ` +
|
|
235
|
+
`${successfulCounts.states} ${successfulCounts.states === 1 ? 'state' : 'states'}` +
|
|
236
|
+
'): no errors\n',
|
|
219
237
|
stderr: '',
|
|
220
238
|
};
|
|
221
239
|
}
|
package/dist/cli-support.d.ts
CHANGED
|
@@ -7,7 +7,12 @@ export interface CliResult {
|
|
|
7
7
|
export declare const isMainModule: (moduleUrl: string, entrypoint: string | undefined) => boolean;
|
|
8
8
|
export declare const usage = "Usage:\n yarramate init <directory>\n yarramate add <document.yaml> --id <id> --kind <kind> --name <name> [--status <status>] [--description <text>] [--owner <ref>] [--constraint <id>=<ref> ...] [--reference <id>=<ref> ...] [--present-in <state-ref> ...] [--source <source.yaml> ...]\n yarramate connect <document.yaml> --id <id> --kind <kind> --from <ref> --to <ref> [--name <name>] [--description <text>] [--status <status>] [--mode <mode>] [--content <text>] [--reference <id>=<ref> ...] [--present-in <state-ref> ...] [--source <source.yaml> ...]\n yarramate check <source.yaml> [source.yaml ...] [--json]\n yarramate compile <source.yaml> [source.yaml ...]\n yarramate context <projection.yaml> <source.yaml> [source.yaml ...]\n yarramate view <projection.yaml> <source.yaml> [source.yaml ...]\n yarramate compare <from-state> <to-state> <source.yaml> [source.yaml ...]\n yarramate evidence <evidence.yaml> <source.yaml> [source.yaml ...]\n yarramate reconcile <workspace.yaml>\n";
|
|
9
9
|
export declare const diagnosticJson: (diagnostics: unknown) => string;
|
|
10
|
-
export declare const checkResultJson: (ok: boolean, diagnostics: unknown
|
|
10
|
+
export declare const checkResultJson: (ok: boolean, diagnostics: unknown, counted?: {
|
|
11
|
+
readonly documents: number;
|
|
12
|
+
readonly concepts: number;
|
|
13
|
+
readonly relationships: number;
|
|
14
|
+
readonly states: number;
|
|
15
|
+
}) => string;
|
|
11
16
|
export declare const humanDiagnostics: (diagnostics: readonly Pick<Diagnostic, "path" | "line" | "column" | "code" | "message">[]) => string;
|
|
12
17
|
export declare const sortDiagnostics: <T extends Diagnostic>(diagnostics: readonly T[]) => T[];
|
|
13
18
|
export declare const resolveCliWorkspaceSources: (paths: readonly string[], cwd: string, options?: {
|
package/dist/cli-support.js
CHANGED
|
@@ -19,10 +19,11 @@ export const diagnosticJson = (diagnostics) => `${JSON.stringify({
|
|
|
19
19
|
format: 'yarramate/diagnostic-result/v1',
|
|
20
20
|
diagnostics,
|
|
21
21
|
}, null, 2)}\n`;
|
|
22
|
-
export const checkResultJson = (ok, diagnostics) => `${JSON.stringify({
|
|
22
|
+
export const checkResultJson = (ok, diagnostics, counted) => `${JSON.stringify({
|
|
23
23
|
format: 'yarramate/check-result/v1',
|
|
24
24
|
ok,
|
|
25
25
|
diagnostics,
|
|
26
|
+
...(counted === undefined ? {} : { counted }),
|
|
26
27
|
}, null, 2)}\n`;
|
|
27
28
|
export const humanDiagnostics = (diagnostics) => diagnostics
|
|
28
29
|
.map((diagnostic) => `${diagnostic.path}:${diagnostic.line}:${diagnostic.column} error ${diagnostic.code} ${diagnostic.message}\n`)
|
package/package.json
CHANGED
|
@@ -17,6 +17,9 @@
|
|
|
17
17
|
"items": {
|
|
18
18
|
"$ref": "#/$defs/diagnostic"
|
|
19
19
|
}
|
|
20
|
+
},
|
|
21
|
+
"counted": {
|
|
22
|
+
"$ref": "#/$defs/counted"
|
|
20
23
|
}
|
|
21
24
|
},
|
|
22
25
|
"allOf": [
|
|
@@ -30,6 +33,7 @@
|
|
|
30
33
|
"required": ["ok"]
|
|
31
34
|
},
|
|
32
35
|
"then": {
|
|
36
|
+
"required": ["counted"],
|
|
33
37
|
"properties": {
|
|
34
38
|
"diagnostics": {
|
|
35
39
|
"maxItems": 0
|
|
@@ -46,6 +50,17 @@
|
|
|
46
50
|
}
|
|
47
51
|
],
|
|
48
52
|
"$defs": {
|
|
53
|
+
"counted": {
|
|
54
|
+
"type": "object",
|
|
55
|
+
"additionalProperties": false,
|
|
56
|
+
"required": ["documents", "concepts", "relationships", "states"],
|
|
57
|
+
"properties": {
|
|
58
|
+
"documents": { "type": "integer", "minimum": 0 },
|
|
59
|
+
"concepts": { "type": "integer", "minimum": 0 },
|
|
60
|
+
"relationships": { "type": "integer", "minimum": 0 },
|
|
61
|
+
"states": { "type": "integer", "minimum": 0 }
|
|
62
|
+
}
|
|
63
|
+
},
|
|
49
64
|
"diagnostic": {
|
|
50
65
|
"type": "object",
|
|
51
66
|
"additionalProperties": false,
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"const": "error"
|
|
35
35
|
},
|
|
36
36
|
"code": {
|
|
37
|
-
"enum": ["YMLC101", "YMLC102", "YMLC103", "YMLC104", "YMLC105", "YMLC106"]
|
|
37
|
+
"enum": ["YMLC101", "YMLC102", "YMLC103", "YMLC104", "YMLC105", "YMLC106", "YMLC107", "YMLC108", "YMLC109", "YMLC110"]
|
|
38
38
|
},
|
|
39
39
|
"message": {
|
|
40
40
|
"type": "string",
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
},
|
|
42
42
|
"path": {
|
|
43
43
|
"type": "string",
|
|
44
|
+
"description": "Repository-relative path resolved from the CLI working directory. Parent traversal, absolute paths, and backslashes are rejected.",
|
|
44
45
|
"pattern": "^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$))(?!.*\\\\).+$"
|
|
45
46
|
},
|
|
46
47
|
"subjectIdentity": {
|
|
@@ -190,6 +190,19 @@ yarramate view <projection.yaml> .yarramate/workspace.yaml
|
|
|
190
190
|
yarramate compare <from-state> <to-state> .yarramate/workspace.yaml
|
|
191
191
|
yarramate evidence <evidence.yaml> .yarramate/workspace.yaml
|
|
192
192
|
yarramate reconcile .yarramate/workspace.yaml
|
|
193
|
+
yarramate-likec4 map --sync \
|
|
194
|
+
.yarramate/integrations/likec4/subject-mapping.yaml \
|
|
195
|
+
.yarramate/workspace.yaml
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Sync preserves and reports mappings for native subjects that no longer exist.
|
|
199
|
+
After confirming that those subjects were intentionally removed or renamed,
|
|
200
|
+
delete the stale entries while adding missing mappings with:
|
|
201
|
+
|
|
202
|
+
```sh
|
|
203
|
+
yarramate-likec4 map --sync --prune \
|
|
204
|
+
.yarramate/integrations/likec4/subject-mapping.yaml \
|
|
205
|
+
.yarramate/workspace.yaml
|
|
193
206
|
```
|
|
194
207
|
|
|
195
208
|
Treat exit `0` as successful execution, `1` as correctness diagnostics, and
|