yarramate 0.1.1 → 0.3.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 +19 -4
- package/assets/likec4/specification.likec4 +500 -0
- package/dist/adapters/likec4-cli.js +202 -15
- package/dist/adapters/likec4-export.js +14 -1
- package/dist/adapters/likec4-project.js +16 -2
- package/dist/check-command.js +20 -2
- package/dist/cli-support.d.ts +7 -2
- package/dist/cli-support.js +3 -2
- package/dist/cli.js +30 -10
- package/dist/compiler.js +111 -0
- package/package.json +3 -2
- package/schema/yarramate-check-result.schema.json +15 -0
- package/schema/yarramate-core-contract.schema.json +2 -0
- package/schema/yarramate-document.schema.json +29 -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 +33 -1
|
@@ -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 { 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 <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,149 @@ 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, mappingPath, ...sourcePaths] = args;
|
|
95
|
+
if (sync !== '--sync' ||
|
|
96
|
+
mappingPath === undefined ||
|
|
97
|
+
mappingPath.startsWith('-') ||
|
|
98
|
+
sourcePaths.length === 0 ||
|
|
99
|
+
sourcePaths.some((path) => path.startsWith('-'))) {
|
|
100
|
+
return { exitCode: 2, stdout: '', stderr: usage };
|
|
101
|
+
}
|
|
102
|
+
try {
|
|
103
|
+
const resolved = resolveCliWorkspaceSources(sourcePaths, cwd);
|
|
104
|
+
if (!resolved.ok) {
|
|
105
|
+
return {
|
|
106
|
+
exitCode: 1,
|
|
107
|
+
stdout: diagnosticJson(resolved.diagnostics),
|
|
108
|
+
stderr: '',
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
const compilation = compileWorkspace(resolved.paths.map((path) => ({
|
|
112
|
+
path,
|
|
113
|
+
source: readFileSync(resolve(cwd, path), 'utf8'),
|
|
114
|
+
})));
|
|
115
|
+
if (!compilation.ok) {
|
|
116
|
+
return {
|
|
117
|
+
exitCode: 1,
|
|
118
|
+
stdout: diagnosticJson(compilation.diagnostics),
|
|
119
|
+
stderr: '',
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
const absoluteMappingPath = resolve(cwd, mappingPath);
|
|
123
|
+
const original = readFileSync(absoluteMappingPath, 'utf8');
|
|
124
|
+
const loaded = loadAdapterMapping({
|
|
125
|
+
path: mappingPath,
|
|
126
|
+
source: original,
|
|
127
|
+
});
|
|
128
|
+
if (!loaded.ok) {
|
|
129
|
+
return {
|
|
130
|
+
exitCode: 1,
|
|
131
|
+
stdout: diagnosticJson(loaded.diagnostics),
|
|
132
|
+
stderr: '',
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
if (loaded.mapping.adapter !== 'likec4') {
|
|
136
|
+
const location = adapterMappingLocation(loaded.mapping, 'adapter');
|
|
137
|
+
return {
|
|
138
|
+
exitCode: 1,
|
|
139
|
+
stdout: diagnosticJson([{
|
|
140
|
+
severity: 'error',
|
|
141
|
+
code: 'YMLC101',
|
|
142
|
+
message: `Adapter mapping targets "${loaded.mapping.adapter}", not "likec4"`,
|
|
143
|
+
...location,
|
|
144
|
+
}]),
|
|
145
|
+
stderr: '',
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
const validation = validateAdapterMapping(compilation.graph, loaded.mapping);
|
|
149
|
+
if (!validation.ok) {
|
|
150
|
+
return {
|
|
151
|
+
exitCode: 1,
|
|
152
|
+
stdout: diagnosticJson(validation.diagnostics),
|
|
153
|
+
stderr: '',
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
const mapped = new Set(loaded.mapping.mappings.map(({ native }) => native));
|
|
157
|
+
const claimedExternal = new Set(loaded.mapping.mappings.map(({ external }) => external));
|
|
158
|
+
const architectureStates = new Set(compilation.graph.claims
|
|
159
|
+
.filter(({ predicate }) => predicate === 'yarramate/state/type')
|
|
160
|
+
.map(({ subject }) => subject));
|
|
161
|
+
const additions = compilation.graph.subjects
|
|
162
|
+
.filter(({ id }) => !mapped.has(id) && !architectureStates.has(id))
|
|
163
|
+
.map((subject) => {
|
|
164
|
+
const [documentId, localId] = subject.id.split('#');
|
|
165
|
+
const local = lowerCamel(localId);
|
|
166
|
+
let external = local;
|
|
167
|
+
if (claimedExternal.has(external)) {
|
|
168
|
+
external = `${lowerCamel(documentId)}_${local}`;
|
|
169
|
+
}
|
|
170
|
+
let suffix = 2;
|
|
171
|
+
const base = external;
|
|
172
|
+
while (claimedExternal.has(external)) {
|
|
173
|
+
external = `${base}_${suffix}`;
|
|
174
|
+
suffix += 1;
|
|
175
|
+
}
|
|
176
|
+
claimedExternal.add(external);
|
|
177
|
+
return {
|
|
178
|
+
native: subject.id,
|
|
179
|
+
external,
|
|
180
|
+
type: subject.type,
|
|
181
|
+
};
|
|
182
|
+
});
|
|
183
|
+
if (additions.length === 0) {
|
|
184
|
+
return {
|
|
185
|
+
exitCode: 0,
|
|
186
|
+
stdout: `LikeC4 mapping ${mappingPath} is already synchronized\n`,
|
|
187
|
+
stderr: '',
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
const document = parseDocument(original);
|
|
191
|
+
for (const addition of additions) {
|
|
192
|
+
document.addIn(['mappings'], addition);
|
|
193
|
+
}
|
|
194
|
+
const mappings = document.getIn(['mappings'], true);
|
|
195
|
+
if (isSeq(mappings))
|
|
196
|
+
mappings.flow = false;
|
|
197
|
+
const candidate = document.toString({ lineWidth: 0 });
|
|
198
|
+
const candidateMapping = loadAdapterMapping({
|
|
199
|
+
path: mappingPath,
|
|
200
|
+
source: candidate,
|
|
201
|
+
});
|
|
202
|
+
if (!candidateMapping.ok) {
|
|
203
|
+
return {
|
|
204
|
+
exitCode: 1,
|
|
205
|
+
stdout: diagnosticJson(candidateMapping.diagnostics),
|
|
206
|
+
stderr: '',
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
const candidateValidation = validateAdapterMapping(compilation.graph, candidateMapping.mapping);
|
|
210
|
+
if (!candidateValidation.ok) {
|
|
211
|
+
return {
|
|
212
|
+
exitCode: 1,
|
|
213
|
+
stdout: diagnosticJson(candidateValidation.diagnostics),
|
|
214
|
+
stderr: '',
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
const staged = stageFile(absoluteMappingPath, candidate);
|
|
218
|
+
try {
|
|
219
|
+
staged.publish();
|
|
220
|
+
}
|
|
221
|
+
finally {
|
|
222
|
+
staged.cleanup();
|
|
223
|
+
}
|
|
224
|
+
return {
|
|
225
|
+
exitCode: 0,
|
|
226
|
+
stdout: `Added ${additions.length} LikeC4 mappings to ${mappingPath}\n`,
|
|
227
|
+
stderr: '',
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
catch (error) {
|
|
231
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
232
|
+
return { exitCode: 2, stdout: '', stderr: `${message}\n` };
|
|
233
|
+
}
|
|
234
|
+
};
|
|
89
235
|
const publishGeneratedProject = (cwd, outputDirectory, input) => {
|
|
90
236
|
const projectPath = resolve(cwd, outputDirectory);
|
|
91
237
|
const markerPath = resolve(projectPath, 'yarramate.generated.json');
|
|
@@ -151,7 +297,7 @@ const publishGeneratedProject = (cwd, outputDirectory, input) => {
|
|
|
151
297
|
else {
|
|
152
298
|
mkdirSync(projectPath, { recursive: true });
|
|
153
299
|
}
|
|
154
|
-
const specificationSource = readFileSync(fileURLToPath(new URL('
|
|
300
|
+
const specificationSource = readFileSync(fileURLToPath(new URL('../../assets/likec4/specification.likec4', import.meta.url)));
|
|
155
301
|
const configSource = `${JSON.stringify({
|
|
156
302
|
$schema: 'https://likec4.dev/schemas/config.json',
|
|
157
303
|
name: input.projectName,
|
|
@@ -194,6 +340,9 @@ const publishGeneratedProject = (cwd, outputDirectory, input) => {
|
|
|
194
340
|
};
|
|
195
341
|
};
|
|
196
342
|
export function runLikeC4Cli(args, cwd = process.cwd()) {
|
|
343
|
+
if (args[0] === 'map') {
|
|
344
|
+
return runLikeC4MapSync(args.slice(1), cwd);
|
|
345
|
+
}
|
|
197
346
|
const [command, projectionPath, mappingPath, ...options] = args;
|
|
198
347
|
let projectDefinitionMode = false;
|
|
199
348
|
if ((command === 'check' || command === 'export-project') &&
|
|
@@ -307,25 +456,63 @@ export function runLikeC4Cli(args, cwd = process.cwd()) {
|
|
|
307
456
|
stderr: '',
|
|
308
457
|
};
|
|
309
458
|
}
|
|
310
|
-
const
|
|
459
|
+
const referencedSources = new Map();
|
|
460
|
+
const referenceDiagnostics = [];
|
|
461
|
+
const readProjectReference = (path, label, yamlPath, pointer) => {
|
|
462
|
+
const existing = referencedSources.get(path);
|
|
463
|
+
if (existing !== undefined)
|
|
464
|
+
return existing;
|
|
465
|
+
try {
|
|
466
|
+
const source = {
|
|
467
|
+
path,
|
|
468
|
+
source: readFileSync(resolve(cwd, path), 'utf8'),
|
|
469
|
+
};
|
|
470
|
+
referencedSources.set(path, source);
|
|
471
|
+
return source;
|
|
472
|
+
}
|
|
473
|
+
catch (error) {
|
|
474
|
+
const location = locateSourcePath(projectSource.path, loadedProject.document.yaml, loadedProject.document.lineCounter, yamlPath, pointer);
|
|
475
|
+
const absent = error instanceof Error &&
|
|
476
|
+
'code' in error &&
|
|
477
|
+
error.code === 'ENOENT';
|
|
478
|
+
referenceDiagnostics.push({
|
|
479
|
+
severity: 'error',
|
|
480
|
+
code: 'YMLC110',
|
|
481
|
+
message: absent
|
|
482
|
+
? `LikeC4 project ${label} "${path}" does not exist`
|
|
483
|
+
: `LikeC4 project ${label} "${path}" cannot be read`,
|
|
484
|
+
...location,
|
|
485
|
+
});
|
|
486
|
+
return undefined;
|
|
487
|
+
}
|
|
488
|
+
};
|
|
489
|
+
const subjectMapping = readProjectReference(loadedProject.document.value.mapping, 'mapping', ['mapping'], '/mapping');
|
|
490
|
+
const kindMapping = loadedProject.document.value.kindMapping === undefined
|
|
491
|
+
? undefined
|
|
492
|
+
: readProjectReference(loadedProject.document.value.kindMapping, 'kind mapping', ['kindMapping'], '/kindMapping');
|
|
493
|
+
const projections = loadedProject.document.value.views.map((view, index) => readProjectReference(view.projection, 'projection', ['views', index, 'projection'], `/views/${index}/projection`));
|
|
494
|
+
if (referenceDiagnostics.length > 0 ||
|
|
495
|
+
subjectMapping === undefined) {
|
|
496
|
+
return {
|
|
497
|
+
exitCode: 1,
|
|
498
|
+
stdout: diagnosticOutput(referenceDiagnostics.sort((left, right) => left.path.localeCompare(right.path) ||
|
|
499
|
+
left.line - right.line ||
|
|
500
|
+
left.column - right.column ||
|
|
501
|
+
left.code.localeCompare(right.code) ||
|
|
502
|
+
left.message.localeCompare(right.message))),
|
|
503
|
+
stderr: '',
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
const preparedViews = loadedProject.document.value.views.map((view, index) => ({
|
|
311
507
|
view,
|
|
312
508
|
prepared: prepareLikeC4Export({
|
|
313
509
|
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
|
-
},
|
|
510
|
+
projection: projections[index],
|
|
511
|
+
subjectMapping,
|
|
322
512
|
...(loadedProject.document.value.kindMapping === undefined
|
|
323
513
|
? {}
|
|
324
514
|
: {
|
|
325
|
-
kindMapping:
|
|
326
|
-
path: loadedProject.document.value.kindMapping,
|
|
327
|
-
source: readFileSync(resolve(cwd, loadedProject.document.value.kindMapping), 'utf8'),
|
|
328
|
-
},
|
|
515
|
+
kindMapping: kindMapping,
|
|
329
516
|
}),
|
|
330
517
|
...(view.compare === undefined
|
|
331
518
|
? {}
|
|
@@ -133,6 +133,10 @@ export function exportLikeC4(projection, mapping, kindMapping, options = {}) {
|
|
|
133
133
|
'constraints',
|
|
134
134
|
referencesFor(projection.claims, concept.id, 'yarramate/constraint/requires'),
|
|
135
135
|
],
|
|
136
|
+
[
|
|
137
|
+
'references',
|
|
138
|
+
referencesFor(projection.claims, concept.id, 'yarramate/reference/refers-to'),
|
|
139
|
+
],
|
|
136
140
|
], ' ');
|
|
137
141
|
if (description === undefined && metadata.length === 0) {
|
|
138
142
|
lines.push(` ${external} = ${kind} ${quote(name)}`);
|
|
@@ -159,6 +163,7 @@ export function exportLikeC4(projection, mapping, kindMapping, options = {}) {
|
|
|
159
163
|
if (source === undefined || target === undefined)
|
|
160
164
|
continue;
|
|
161
165
|
const name = valueFor(projection.claims, relationship.id, 'yarramate/relationship/name');
|
|
166
|
+
const description = valueFor(projection.claims, relationship.id, 'yarramate/relationship/description');
|
|
162
167
|
const metadata = metadataLines([
|
|
163
168
|
['yarramateId', relationship.id],
|
|
164
169
|
['yarramateKind', structural.predicate],
|
|
@@ -175,8 +180,16 @@ export function exportLikeC4(projection, mapping, kindMapping, options = {}) {
|
|
|
175
180
|
'content',
|
|
176
181
|
valueFor(projection.claims, relationship.id, 'yarramate/flow/content'),
|
|
177
182
|
],
|
|
183
|
+
[
|
|
184
|
+
'references',
|
|
185
|
+
referencesFor(projection.claims, relationship.id, 'yarramate/reference/refers-to'),
|
|
186
|
+
],
|
|
178
187
|
], ' ');
|
|
179
|
-
lines.push(` ${source} -[${externalRelationshipKind.get(structural.predicate) ?? kindId(structural.predicate)}]-> ${target}${name === undefined ? '' : ` ${quote(name)}`}${metadata.length === 0 ? '' : ' {'}`, ...
|
|
188
|
+
lines.push(` ${source} -[${externalRelationshipKind.get(structural.predicate) ?? kindId(structural.predicate)}]-> ${target}${name === undefined ? '' : ` ${quote(name)}`}${description === undefined && metadata.length === 0 ? '' : ' {'}`, ...(description === undefined
|
|
189
|
+
? []
|
|
190
|
+
: [` description ${quote(description)}`]), ...metadata, ...(description === undefined && metadata.length === 0
|
|
191
|
+
? []
|
|
192
|
+
: [' }']));
|
|
180
193
|
}
|
|
181
194
|
const viewId = identifier.test(projection.projection.split('@')[0] ?? '')
|
|
182
195
|
? projection.projection.split('@')[0]
|
|
@@ -73,7 +73,7 @@ const viewBody = ({ id, prepared, dynamic, deployment, }) => {
|
|
|
73
73
|
}
|
|
74
74
|
if (dynamic !== undefined) {
|
|
75
75
|
const claimsById = new Map(prepared.projection.claims.map((claim) => [claim.id, claim]));
|
|
76
|
-
const lines = dynamic.steps.
|
|
76
|
+
const lines = dynamic.steps.flatMap((step) => {
|
|
77
77
|
const structural = claimsById.get(step.relationship);
|
|
78
78
|
const source = externalByNative.get(structural.subject);
|
|
79
79
|
const target = 'ref' in structural.object
|
|
@@ -86,7 +86,21 @@ const viewBody = ({ id, prepared, dynamic, deployment, }) => {
|
|
|
86
86
|
(declaredTitle !== undefined && 'value' in declaredTitle.object
|
|
87
87
|
? declaredTitle.object.value
|
|
88
88
|
: undefined);
|
|
89
|
-
|
|
89
|
+
const declaredDescription = prepared.projection.claims.find((claim) => claim.subject === step.relationship &&
|
|
90
|
+
claim.predicate === 'yarramate/relationship/description' &&
|
|
91
|
+
'value' in claim.object);
|
|
92
|
+
const description = declaredDescription !== undefined &&
|
|
93
|
+
'value' in declaredDescription.object
|
|
94
|
+
? declaredDescription.object.value
|
|
95
|
+
: undefined;
|
|
96
|
+
const statement = ` ${source} -> ${target}${title === undefined ? '' : ` ${quote(title)}`}`;
|
|
97
|
+
return description === undefined
|
|
98
|
+
? [statement]
|
|
99
|
+
: [
|
|
100
|
+
`${statement} {`,
|
|
101
|
+
` description ${quote(description)}`,
|
|
102
|
+
' }',
|
|
103
|
+
];
|
|
90
104
|
});
|
|
91
105
|
const viewId = id ?? prepared.projection.projection.split('@')[0];
|
|
92
106
|
return [
|
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
|
@@ -5,9 +5,14 @@ export interface CliResult {
|
|
|
5
5
|
readonly stderr: string;
|
|
6
6
|
}
|
|
7
7
|
export declare const isMainModule: (moduleUrl: string, entrypoint: string | undefined) => boolean;
|
|
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> ...] [--present-in <state-ref> ...] [--source <source.yaml> ...]\n yarramate connect <document.yaml> --id <id> --kind <kind> --from <ref> --to <ref> [--name <name>] [--status <status>] [--mode <mode>] [--content <text>] [--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";
|
|
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
|
@@ -14,15 +14,16 @@ export const isMainModule = (moduleUrl, entrypoint) => {
|
|
|
14
14
|
return false;
|
|
15
15
|
}
|
|
16
16
|
};
|
|
17
|
-
export 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> ...] [--present-in <state-ref> ...] [--source <source.yaml> ...]\n yarramate connect <document.yaml> --id <id> --kind <kind> --from <ref> --to <ref> [--name <name>] [--status <status>] [--mode <mode>] [--content <text>] [--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';
|
|
17
|
+
export 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';
|
|
18
18
|
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/dist/cli.js
CHANGED
|
@@ -301,6 +301,7 @@ const oneFlag = (flags, flag) => {
|
|
|
301
301
|
};
|
|
302
302
|
const hasRepeatedSingletonFlag = (flags) => [...flags.entries()].some(([flag, values]) => flag !== '--source' &&
|
|
303
303
|
flag !== '--constraint' &&
|
|
304
|
+
flag !== '--reference' &&
|
|
304
305
|
flag !== '--present-in' &&
|
|
305
306
|
values.length !== 1);
|
|
306
307
|
const appendBlockItem = (document, collection, item) => {
|
|
@@ -310,6 +311,20 @@ const appendBlockItem = (document, collection, item) => {
|
|
|
310
311
|
sequence.flow = false;
|
|
311
312
|
}
|
|
312
313
|
};
|
|
314
|
+
const identifiedReferences = (values) => {
|
|
315
|
+
const parsed = values.map((value) => {
|
|
316
|
+
const separator = value.indexOf('=');
|
|
317
|
+
return separator <= 0 || separator === value.length - 1
|
|
318
|
+
? undefined
|
|
319
|
+
: {
|
|
320
|
+
id: value.slice(0, separator),
|
|
321
|
+
ref: value.slice(separator + 1),
|
|
322
|
+
};
|
|
323
|
+
});
|
|
324
|
+
return parsed.some((reference) => reference === undefined)
|
|
325
|
+
? undefined
|
|
326
|
+
: parsed;
|
|
327
|
+
};
|
|
313
328
|
const runAdd = (options, cwd) => {
|
|
314
329
|
const parsed = parseFlags(options);
|
|
315
330
|
if (parsed === undefined) {
|
|
@@ -323,6 +338,7 @@ const runAdd = (options, cwd) => {
|
|
|
323
338
|
'--description',
|
|
324
339
|
'--owner',
|
|
325
340
|
'--constraint',
|
|
341
|
+
'--reference',
|
|
326
342
|
'--present-in',
|
|
327
343
|
'--source',
|
|
328
344
|
]);
|
|
@@ -336,16 +352,9 @@ const runAdd = (options, cwd) => {
|
|
|
336
352
|
if (id === undefined || kind === undefined || name === undefined) {
|
|
337
353
|
return { exitCode: 2, stdout: '', stderr: usage };
|
|
338
354
|
}
|
|
339
|
-
const constraints = (parsed.flags.get('--constraint') ?? [])
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
? undefined
|
|
343
|
-
: {
|
|
344
|
-
id: value.slice(0, separator),
|
|
345
|
-
ref: value.slice(separator + 1),
|
|
346
|
-
};
|
|
347
|
-
});
|
|
348
|
-
if (constraints.some((constraint) => constraint === undefined)) {
|
|
355
|
+
const constraints = identifiedReferences(parsed.flags.get('--constraint') ?? []);
|
|
356
|
+
const references = identifiedReferences(parsed.flags.get('--reference') ?? []);
|
|
357
|
+
if (constraints === undefined || references === undefined) {
|
|
349
358
|
return { exitCode: 2, stdout: '', stderr: usage };
|
|
350
359
|
}
|
|
351
360
|
try {
|
|
@@ -365,6 +374,7 @@ const runAdd = (options, cwd) => {
|
|
|
365
374
|
? {}
|
|
366
375
|
: { owner: oneFlag(parsed.flags, '--owner') }),
|
|
367
376
|
...(constraints.length === 0 ? {} : { constraints }),
|
|
377
|
+
...(references.length === 0 ? {} : { references }),
|
|
368
378
|
...(parsed.flags.get('--present-in') === undefined
|
|
369
379
|
? {}
|
|
370
380
|
: { presentIn: parsed.flags.get('--present-in') }),
|
|
@@ -417,9 +427,11 @@ const runConnect = (options, cwd) => {
|
|
|
417
427
|
'--from',
|
|
418
428
|
'--to',
|
|
419
429
|
'--name',
|
|
430
|
+
'--description',
|
|
420
431
|
'--status',
|
|
421
432
|
'--mode',
|
|
422
433
|
'--content',
|
|
434
|
+
'--reference',
|
|
423
435
|
'--present-in',
|
|
424
436
|
'--source',
|
|
425
437
|
]);
|
|
@@ -437,6 +449,10 @@ const runConnect = (options, cwd) => {
|
|
|
437
449
|
to === undefined) {
|
|
438
450
|
return { exitCode: 2, stdout: '', stderr: usage };
|
|
439
451
|
}
|
|
452
|
+
const references = identifiedReferences(parsed.flags.get('--reference') ?? []);
|
|
453
|
+
if (references === undefined) {
|
|
454
|
+
return { exitCode: 2, stdout: '', stderr: usage };
|
|
455
|
+
}
|
|
440
456
|
try {
|
|
441
457
|
const absolutePath = resolve(cwd, parsed.path);
|
|
442
458
|
const document = parseDocument(readFileSync(absolutePath, 'utf8'));
|
|
@@ -448,6 +464,9 @@ const runConnect = (options, cwd) => {
|
|
|
448
464
|
...(oneFlag(parsed.flags, '--name') === undefined
|
|
449
465
|
? {}
|
|
450
466
|
: { name: oneFlag(parsed.flags, '--name') }),
|
|
467
|
+
...(oneFlag(parsed.flags, '--description') === undefined
|
|
468
|
+
? {}
|
|
469
|
+
: { description: oneFlag(parsed.flags, '--description') }),
|
|
451
470
|
...(oneFlag(parsed.flags, '--status') === undefined
|
|
452
471
|
? {}
|
|
453
472
|
: { status: oneFlag(parsed.flags, '--status') }),
|
|
@@ -457,6 +476,7 @@ const runConnect = (options, cwd) => {
|
|
|
457
476
|
...(oneFlag(parsed.flags, '--content') === undefined
|
|
458
477
|
? {}
|
|
459
478
|
: { content: oneFlag(parsed.flags, '--content') }),
|
|
479
|
+
...(references.length === 0 ? {} : { references }),
|
|
460
480
|
...(parsed.flags.get('--present-in') === undefined
|
|
461
481
|
? {}
|
|
462
482
|
: { presentIn: parsed.flags.get('--present-in') }),
|