yarramate 0.3.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.
@@ -4,7 +4,7 @@ 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 { isSeq, parseDocument } from 'yaml';
7
+ import { isMap, isSeq, parseDocument } from 'yaml';
8
8
  import { isMainModule, resolveCliWorkspaceSources, } from '../cli-support.js';
9
9
  import { compileWorkspace } from '../compiler.js';
10
10
  import { adapterMappingLocation, loadAdapterMapping, validateAdapterMapping, } from '../adapter-mapping.js';
@@ -73,7 +73,7 @@ const publishFiles = (files) => {
73
73
  }
74
74
  };
75
75
  const usage = 'Usage:\n' +
76
- ' yarramate-likec4 map --sync <mapping.yaml> <workspace-or-source...>\n' +
76
+ ' yarramate-likec4 map --sync [--prune] <mapping.yaml> <workspace-or-source...>\n' +
77
77
  ' yarramate-likec4 check <projection.yaml> <mapping.yaml> [--json] [--kinds <kind-mapping.yaml>] [--compare <from-state> <to-state>] <workspace-or-source...>\n' +
78
78
  ' yarramate-likec4 check <likec4-project.yaml> [--json] <workspace-or-source...>\n' +
79
79
  ' yarramate-likec4 export <projection.yaml> <mapping.yaml> [--kinds <kind-mapping.yaml>] [--compare <from-state> <to-state>] <workspace-or-source...>\n' +
@@ -91,7 +91,10 @@ const checkJson = (ok, diagnostics) => `${JSON.stringify({
91
91
  const sameJson = (left, right) => JSON.stringify(left) === JSON.stringify(right);
92
92
  const lowerCamel = (value) => value.replaceAll(/-([a-z0-9])/g, (_, character) => character.toUpperCase());
93
93
  const runLikeC4MapSync = (args, cwd) => {
94
- const [sync, mappingPath, ...sourcePaths] = args;
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);
95
98
  if (sync !== '--sync' ||
96
99
  mappingPath === undefined ||
97
100
  mappingPath.startsWith('-') ||
@@ -145,16 +148,23 @@ const runLikeC4MapSync = (args, cwd) => {
145
148
  stderr: '',
146
149
  };
147
150
  }
151
+ const graphSubjects = new Set(compilation.graph.subjects.map(({ id }) => id));
152
+ const staleCount = loaded.mapping.mappings.filter(({ native }) => !graphSubjects.has(native)).length;
148
153
  const validation = validateAdapterMapping(compilation.graph, loaded.mapping);
149
- if (!validation.ok) {
154
+ const blockingDiagnostics = validation.ok
155
+ ? []
156
+ : validation.diagnostics.filter(({ code }) => code !== 'YM601');
157
+ if (blockingDiagnostics.length > 0) {
150
158
  return {
151
159
  exitCode: 1,
152
- stdout: diagnosticJson(validation.diagnostics),
160
+ stdout: diagnosticJson(blockingDiagnostics),
153
161
  stderr: '',
154
162
  };
155
163
  }
156
164
  const mapped = new Set(loaded.mapping.mappings.map(({ native }) => native));
157
- const claimedExternal = new Set(loaded.mapping.mappings.map(({ external }) => external));
165
+ const claimedExternal = new Set(loaded.mapping.mappings
166
+ .filter(({ native }) => !prune || graphSubjects.has(native))
167
+ .map(({ external }) => external));
158
168
  const architectureStates = new Set(compilation.graph.claims
159
169
  .filter(({ predicate }) => predicate === 'yarramate/state/type')
160
170
  .map(({ subject }) => subject));
@@ -180,18 +190,29 @@ const runLikeC4MapSync = (args, cwd) => {
180
190
  type: subject.type,
181
191
  };
182
192
  });
183
- if (additions.length === 0) {
193
+ if (additions.length === 0 && (!prune || staleCount === 0)) {
184
194
  return {
185
195
  exitCode: 0,
186
- stdout: `LikeC4 mapping ${mappingPath} is already synchronized\n`,
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`,
187
199
  stderr: '',
188
200
  };
189
201
  }
190
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
+ }
191
213
  for (const addition of additions) {
192
214
  document.addIn(['mappings'], addition);
193
215
  }
194
- const mappings = document.getIn(['mappings'], true);
195
216
  if (isSeq(mappings))
196
217
  mappings.flow = false;
197
218
  const candidate = document.toString({ lineWidth: 0 });
@@ -207,10 +228,13 @@ const runLikeC4MapSync = (args, cwd) => {
207
228
  };
208
229
  }
209
230
  const candidateValidation = validateAdapterMapping(compilation.graph, candidateMapping.mapping);
210
- if (!candidateValidation.ok) {
231
+ const candidateBlockingDiagnostics = candidateValidation.ok
232
+ ? []
233
+ : candidateValidation.diagnostics.filter(({ code }) => prune || code !== 'YM601');
234
+ if (candidateBlockingDiagnostics.length > 0) {
211
235
  return {
212
236
  exitCode: 1,
213
- stdout: diagnosticJson(candidateValidation.diagnostics),
237
+ stdout: diagnosticJson(candidateBlockingDiagnostics),
214
238
  stderr: '',
215
239
  };
216
240
  }
@@ -223,7 +247,14 @@ const runLikeC4MapSync = (args, cwd) => {
223
247
  }
224
248
  return {
225
249
  exitCode: 0,
226
- stdout: `Added ${additions.length} LikeC4 mappings to ${mappingPath}\n`,
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`),
227
258
  stderr: '',
228
259
  };
229
260
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yarramate",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "Tool-neutral semantic architecture engine and guided methodology",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -195,5 +195,15 @@ yarramate-likec4 map --sync \
195
195
  .yarramate/workspace.yaml
196
196
  ```
197
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
206
+ ```
207
+
198
208
  Treat exit `0` as successful execution, `1` as correctness diagnostics, and
199
209
  `2` as invocation or file failure.