@living-architecture/riviere-cli 0.2.2 → 0.2.3
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/bin.js +24428 -0
- package/dist/index.js +24444 -7
- package/package.json +2 -2
- package/dist/cli.d.ts +0 -8
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js +0 -65
- package/dist/command-test-fixtures.d.ts +0 -110
- package/dist/command-test-fixtures.d.ts.map +0 -1
- package/dist/command-test-fixtures.js +0 -184
- package/dist/commands/builder/add-component.d.ts +0 -4
- package/dist/commands/builder/add-component.d.ts.map +0 -1
- package/dist/commands/builder/add-component.js +0 -204
- package/dist/commands/builder/add-domain.d.ts +0 -3
- package/dist/commands/builder/add-domain.d.ts.map +0 -1
- package/dist/commands/builder/add-domain.js +0 -56
- package/dist/commands/builder/add-source.d.ts +0 -3
- package/dist/commands/builder/add-source.d.ts.map +0 -1
- package/dist/commands/builder/add-source.js +0 -28
- package/dist/commands/builder/check-consistency.d.ts +0 -3
- package/dist/commands/builder/check-consistency.d.ts.map +0 -1
- package/dist/commands/builder/check-consistency.js +0 -27
- package/dist/commands/builder/component-checklist.d.ts +0 -3
- package/dist/commands/builder/component-checklist.d.ts.map +0 -1
- package/dist/commands/builder/component-checklist.js +0 -43
- package/dist/commands/builder/component-summary.d.ts +0 -3
- package/dist/commands/builder/component-summary.d.ts.map +0 -1
- package/dist/commands/builder/component-summary.js +0 -23
- package/dist/commands/builder/enrich.d.ts +0 -3
- package/dist/commands/builder/enrich.d.ts.map +0 -1
- package/dist/commands/builder/enrich.js +0 -85
- package/dist/commands/builder/finalize.d.ts +0 -3
- package/dist/commands/builder/finalize.d.ts.map +0 -1
- package/dist/commands/builder/finalize.js +0 -37
- package/dist/commands/builder/init.d.ts +0 -3
- package/dist/commands/builder/init.d.ts.map +0 -1
- package/dist/commands/builder/init.js +0 -100
- package/dist/commands/builder/link-external.d.ts +0 -3
- package/dist/commands/builder/link-external.d.ts.map +0 -1
- package/dist/commands/builder/link-external.js +0 -70
- package/dist/commands/builder/link-http.d.ts +0 -3
- package/dist/commands/builder/link-http.d.ts.map +0 -1
- package/dist/commands/builder/link-http.js +0 -130
- package/dist/commands/builder/link-infrastructure.d.ts +0 -7
- package/dist/commands/builder/link-infrastructure.d.ts.map +0 -1
- package/dist/commands/builder/link-infrastructure.js +0 -41
- package/dist/commands/builder/link.d.ts +0 -3
- package/dist/commands/builder/link.d.ts.map +0 -1
- package/dist/commands/builder/link.js +0 -73
- package/dist/commands/builder/validate.d.ts +0 -3
- package/dist/commands/builder/validate.d.ts.map +0 -1
- package/dist/commands/builder/validate.js +0 -29
- package/dist/commands/query/component-output.d.ts +0 -9
- package/dist/commands/query/component-output.d.ts.map +0 -1
- package/dist/commands/query/component-output.js +0 -8
- package/dist/commands/query/components.d.ts +0 -3
- package/dist/commands/query/components.d.ts.map +0 -1
- package/dist/commands/query/components.js +0 -45
- package/dist/commands/query/domains.d.ts +0 -3
- package/dist/commands/query/domains.d.ts.map +0 -1
- package/dist/commands/query/domains.js +0 -22
- package/dist/commands/query/entry-points.d.ts +0 -3
- package/dist/commands/query/entry-points.d.ts.map +0 -1
- package/dist/commands/query/entry-points.js +0 -22
- package/dist/commands/query/load-graph.d.ts +0 -16
- package/dist/commands/query/load-graph.d.ts.map +0 -1
- package/dist/commands/query/load-graph.js +0 -50
- package/dist/commands/query/orphans.d.ts +0 -3
- package/dist/commands/query/orphans.d.ts.map +0 -1
- package/dist/commands/query/orphans.js +0 -22
- package/dist/commands/query/search.d.ts +0 -3
- package/dist/commands/query/search.d.ts.map +0 -1
- package/dist/commands/query/search.js +0 -25
- package/dist/commands/query/trace.d.ts +0 -3
- package/dist/commands/query/trace.d.ts.map +0 -1
- package/dist/commands/query/trace.js +0 -41
- package/dist/component-types.d.ts +0 -15
- package/dist/component-types.d.ts.map +0 -1
- package/dist/component-types.js +0 -39
- package/dist/error-codes.d.ts +0 -15
- package/dist/error-codes.d.ts.map +0 -1
- package/dist/error-codes.js +0 -15
- package/dist/file-existence.d.ts +0 -2
- package/dist/file-existence.d.ts.map +0 -1
- package/dist/file-existence.js +0 -16
- package/dist/graph-path.d.ts +0 -3
- package/dist/graph-path.d.ts.map +0 -1
- package/dist/graph-path.js +0 -8
- package/dist/index.d.ts +0 -5
- package/dist/index.d.ts.map +0 -1
- package/dist/output.d.ts +0 -17
- package/dist/output.d.ts.map +0 -1
- package/dist/output.js +0 -7
- package/dist/validation.d.ts +0 -12
- package/dist/validation.d.ts.map +0 -1
- package/dist/validation.js +0 -51
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"add-source.d.ts","sourceRoot":"","sources":["../../../src/commands/builder/add-source.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAYpC,wBAAgB,sBAAsB,IAAI,OAAO,CA+BhD"}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
import { writeFile } from 'node:fs/promises';
|
|
3
|
-
import { formatSuccess } from '../../output';
|
|
4
|
-
import { getDefaultGraphPathDescription } from '../../graph-path';
|
|
5
|
-
import { withGraphBuilder } from './link-infrastructure';
|
|
6
|
-
export function createAddSourceCommand() {
|
|
7
|
-
return new Command('add-source')
|
|
8
|
-
.description('Add a source repository to the graph')
|
|
9
|
-
.addHelpText('after', `
|
|
10
|
-
Examples:
|
|
11
|
-
$ riviere builder add-source --repository https://github.com/org/orders-service
|
|
12
|
-
$ riviere builder add-source --repository https://github.com/org/payments-api --json
|
|
13
|
-
`)
|
|
14
|
-
.requiredOption('--repository <url>', 'Source repository URL')
|
|
15
|
-
.option('--graph <path>', getDefaultGraphPathDescription())
|
|
16
|
-
.option('--json', 'Output result as JSON')
|
|
17
|
-
.action(async (options) => {
|
|
18
|
-
await withGraphBuilder(options.graph, async (builder, graphPath) => {
|
|
19
|
-
builder.addSource({ repository: options.repository });
|
|
20
|
-
await writeFile(graphPath, builder.serialize(), 'utf-8');
|
|
21
|
-
if (options.json === true) {
|
|
22
|
-
console.log(JSON.stringify(formatSuccess({
|
|
23
|
-
repository: options.repository,
|
|
24
|
-
})));
|
|
25
|
-
}
|
|
26
|
-
});
|
|
27
|
-
});
|
|
28
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"check-consistency.d.ts","sourceRoot":"","sources":["../../../src/commands/builder/check-consistency.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAUpC,wBAAgB,6BAA6B,IAAI,OAAO,CA8BvD"}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
import { getDefaultGraphPathDescription } from '../../graph-path';
|
|
3
|
-
import { formatSuccess } from '../../output';
|
|
4
|
-
import { withGraphBuilder } from './link-infrastructure';
|
|
5
|
-
export function createCheckConsistencyCommand() {
|
|
6
|
-
return new Command('check-consistency')
|
|
7
|
-
.description('Check for structural issues in the graph')
|
|
8
|
-
.addHelpText('after', `
|
|
9
|
-
Examples:
|
|
10
|
-
$ riviere builder check-consistency
|
|
11
|
-
$ riviere builder check-consistency --json
|
|
12
|
-
`)
|
|
13
|
-
.option('--graph <path>', getDefaultGraphPathDescription())
|
|
14
|
-
.option('--json', 'Output result as JSON')
|
|
15
|
-
.action(async (options) => {
|
|
16
|
-
await withGraphBuilder(options.graph, async (builder) => {
|
|
17
|
-
const warnings = builder.warnings();
|
|
18
|
-
const consistent = warnings.length === 0;
|
|
19
|
-
if (options.json === true) {
|
|
20
|
-
console.log(JSON.stringify(formatSuccess({
|
|
21
|
-
consistent,
|
|
22
|
-
warnings,
|
|
23
|
-
})));
|
|
24
|
-
}
|
|
25
|
-
});
|
|
26
|
-
});
|
|
27
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"component-checklist.d.ts","sourceRoot":"","sources":["../../../src/commands/builder/component-checklist.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAapC,wBAAgB,+BAA+B,IAAI,OAAO,CAmDzD"}
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
import { getDefaultGraphPathDescription } from '../../graph-path';
|
|
3
|
-
import { formatError, formatSuccess } from '../../output';
|
|
4
|
-
import { CliErrorCode } from '../../error-codes';
|
|
5
|
-
import { isValidComponentType } from '../../component-types';
|
|
6
|
-
import { withGraphBuilder } from './link-infrastructure';
|
|
7
|
-
export function createComponentChecklistCommand() {
|
|
8
|
-
return new Command('component-checklist')
|
|
9
|
-
.description('List components as a checklist for linking/enrichment')
|
|
10
|
-
.addHelpText('after', `
|
|
11
|
-
Examples:
|
|
12
|
-
$ riviere builder component-checklist
|
|
13
|
-
$ riviere builder component-checklist --type DomainOp
|
|
14
|
-
$ riviere builder component-checklist --type API --json
|
|
15
|
-
`)
|
|
16
|
-
.option('--graph <path>', getDefaultGraphPathDescription())
|
|
17
|
-
.option('--json', 'Output result as JSON')
|
|
18
|
-
.option('--type <type>', 'Filter by component type')
|
|
19
|
-
.action(async (options) => {
|
|
20
|
-
if (options.type !== undefined && !isValidComponentType(options.type)) {
|
|
21
|
-
console.log(JSON.stringify(formatError(CliErrorCode.InvalidComponentType, `Invalid component type: ${options.type}`, [
|
|
22
|
-
'Valid types: UI, API, UseCase, DomainOp, Event, EventHandler, Custom',
|
|
23
|
-
])));
|
|
24
|
-
return;
|
|
25
|
-
}
|
|
26
|
-
await withGraphBuilder(options.graph, async (builder) => {
|
|
27
|
-
const allComponents = builder.query().components();
|
|
28
|
-
const filteredComponents = options.type !== undefined ? allComponents.filter((c) => c.type === options.type) : allComponents;
|
|
29
|
-
const checklistItems = filteredComponents.map((c) => ({
|
|
30
|
-
id: c.id,
|
|
31
|
-
type: c.type,
|
|
32
|
-
name: c.name,
|
|
33
|
-
domain: c.domain,
|
|
34
|
-
}));
|
|
35
|
-
if (options.json === true) {
|
|
36
|
-
console.log(JSON.stringify(formatSuccess({
|
|
37
|
-
total: checklistItems.length,
|
|
38
|
-
components: checklistItems,
|
|
39
|
-
})));
|
|
40
|
-
}
|
|
41
|
-
});
|
|
42
|
-
});
|
|
43
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"component-summary.d.ts","sourceRoot":"","sources":["../../../src/commands/builder/component-summary.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAUpC,wBAAgB,6BAA6B,IAAI,OAAO,CAsBvD"}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
import { getDefaultGraphPathDescription } from '../../graph-path';
|
|
3
|
-
import { formatSuccess } from '../../output';
|
|
4
|
-
import { withGraphBuilder } from './link-infrastructure';
|
|
5
|
-
export function createComponentSummaryCommand() {
|
|
6
|
-
return new Command('component-summary')
|
|
7
|
-
.description('Show component counts by type and domain')
|
|
8
|
-
.addHelpText('after', `
|
|
9
|
-
Examples:
|
|
10
|
-
$ riviere builder component-summary
|
|
11
|
-
$ riviere builder component-summary --json
|
|
12
|
-
`)
|
|
13
|
-
.option('--graph <path>', getDefaultGraphPathDescription())
|
|
14
|
-
.option('--json', 'Output result as JSON')
|
|
15
|
-
.action(async (options) => {
|
|
16
|
-
await withGraphBuilder(options.graph, async (builder) => {
|
|
17
|
-
const stats = builder.stats();
|
|
18
|
-
if (options.json === true) {
|
|
19
|
-
console.log(JSON.stringify(formatSuccess(stats)));
|
|
20
|
-
}
|
|
21
|
-
});
|
|
22
|
-
});
|
|
23
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"enrich.d.ts","sourceRoot":"","sources":["../../../src/commands/builder/enrich.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAoDpC,wBAAgB,mBAAmB,IAAI,OAAO,CAsD7C"}
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
import { writeFile } from 'node:fs/promises';
|
|
3
|
-
import { InvalidEnrichmentTargetError } from '@living-architecture/riviere-builder';
|
|
4
|
-
import { withGraphBuilder, handleComponentNotFoundError } from './link-infrastructure';
|
|
5
|
-
import { formatError, formatSuccess } from '../../output';
|
|
6
|
-
import { CliErrorCode } from '../../error-codes';
|
|
7
|
-
import { getDefaultGraphPathDescription } from '../../graph-path';
|
|
8
|
-
function collectOption(value, previous) {
|
|
9
|
-
return [...previous, value];
|
|
10
|
-
}
|
|
11
|
-
function parseStateChange(input) {
|
|
12
|
-
const [from, to, ...rest] = input.split(':');
|
|
13
|
-
if (from === undefined || to === undefined || rest.length > 0) {
|
|
14
|
-
return undefined;
|
|
15
|
-
}
|
|
16
|
-
return { from, to };
|
|
17
|
-
}
|
|
18
|
-
function parseStateChanges(inputs) {
|
|
19
|
-
const stateChanges = [];
|
|
20
|
-
for (const sc of inputs) {
|
|
21
|
-
const parsed = parseStateChange(sc);
|
|
22
|
-
if (parsed === undefined) {
|
|
23
|
-
return { success: false, invalidInput: sc };
|
|
24
|
-
}
|
|
25
|
-
stateChanges.push(parsed);
|
|
26
|
-
}
|
|
27
|
-
return { success: true, stateChanges };
|
|
28
|
-
}
|
|
29
|
-
function handleEnrichmentError(error) {
|
|
30
|
-
if (error instanceof InvalidEnrichmentTargetError) {
|
|
31
|
-
console.log(JSON.stringify(formatError(CliErrorCode.InvalidComponentType, error.message, [])));
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
34
|
-
handleComponentNotFoundError(error);
|
|
35
|
-
}
|
|
36
|
-
export function createEnrichCommand() {
|
|
37
|
-
return new Command('enrich')
|
|
38
|
-
.description('Enrich a DomainOp component with entity, state changes, and business rules')
|
|
39
|
-
.addHelpText('after', `
|
|
40
|
-
Examples:
|
|
41
|
-
$ riviere builder enrich \\
|
|
42
|
-
--id "orders:checkout:domainop:orderbegin" \\
|
|
43
|
-
--entity Order \\
|
|
44
|
-
--state-change "Draft:Placed" \\
|
|
45
|
-
--business-rule "Order must have at least one item"
|
|
46
|
-
|
|
47
|
-
$ riviere builder enrich \\
|
|
48
|
-
--id "payments:gateway:domainop:paymentprocess" \\
|
|
49
|
-
--state-change "Pending:Processing" \\
|
|
50
|
-
--state-change "Processing:Completed" \\
|
|
51
|
-
--business-rule "Amount must be positive" \\
|
|
52
|
-
--business-rule "Currency must be valid"
|
|
53
|
-
`)
|
|
54
|
-
.requiredOption('--id <component-id>', 'Component ID to enrich')
|
|
55
|
-
.option('--entity <name>', 'Entity name')
|
|
56
|
-
.option('--state-change <from:to>', 'State transition (repeatable)', collectOption, [])
|
|
57
|
-
.option('--business-rule <rule>', 'Business rule (repeatable)', collectOption, [])
|
|
58
|
-
.option('--graph <path>', getDefaultGraphPathDescription())
|
|
59
|
-
.option('--json', 'Output result as JSON')
|
|
60
|
-
.action(async (options) => {
|
|
61
|
-
const parseResult = parseStateChanges(options.stateChange);
|
|
62
|
-
if (!parseResult.success) {
|
|
63
|
-
const msg = `Invalid state-change format: '${parseResult.invalidInput}'. Expected 'from:to'.`;
|
|
64
|
-
console.log(JSON.stringify(formatError(CliErrorCode.ValidationError, msg, [])));
|
|
65
|
-
return;
|
|
66
|
-
}
|
|
67
|
-
await withGraphBuilder(options.graph, async (builder, graphPath) => {
|
|
68
|
-
try {
|
|
69
|
-
builder.enrichComponent(options.id, {
|
|
70
|
-
...(options.entity !== undefined && { entity: options.entity }),
|
|
71
|
-
...(parseResult.stateChanges.length > 0 && { stateChanges: parseResult.stateChanges }),
|
|
72
|
-
...(options.businessRule.length > 0 && { businessRules: options.businessRule }),
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
catch (error) {
|
|
76
|
-
handleEnrichmentError(error);
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
await writeFile(graphPath, builder.serialize(), 'utf-8');
|
|
80
|
-
if (options.json === true) {
|
|
81
|
-
console.log(JSON.stringify(formatSuccess({ componentId: options.id })));
|
|
82
|
-
}
|
|
83
|
-
});
|
|
84
|
-
});
|
|
85
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"finalize.d.ts","sourceRoot":"","sources":["../../../src/commands/builder/finalize.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAapC,wBAAgB,qBAAqB,IAAI,OAAO,CAwC/C"}
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
import { writeFile } from 'node:fs/promises';
|
|
3
|
-
import { formatError, formatSuccess } from '../../output';
|
|
4
|
-
import { CliErrorCode } from '../../error-codes';
|
|
5
|
-
import { getDefaultGraphPathDescription } from '../../graph-path';
|
|
6
|
-
import { withGraphBuilder } from './link-infrastructure';
|
|
7
|
-
export function createFinalizeCommand() {
|
|
8
|
-
return new Command('finalize')
|
|
9
|
-
.description('Validate and export the final graph')
|
|
10
|
-
.addHelpText('after', `
|
|
11
|
-
Examples:
|
|
12
|
-
$ riviere builder finalize
|
|
13
|
-
$ riviere builder finalize --output ./dist/architecture.json
|
|
14
|
-
$ riviere builder finalize --json
|
|
15
|
-
`)
|
|
16
|
-
.option('--graph <path>', getDefaultGraphPathDescription())
|
|
17
|
-
.option('--output <path>', 'Output path for finalized graph (defaults to input path)')
|
|
18
|
-
.option('--json', 'Output result as JSON')
|
|
19
|
-
.action(async (options) => {
|
|
20
|
-
await withGraphBuilder(options.graph, async (builder, graphPath) => {
|
|
21
|
-
const validationResult = builder.validate();
|
|
22
|
-
if (!validationResult.valid) {
|
|
23
|
-
const messages = validationResult.errors.map((e) => e.message).join('; ');
|
|
24
|
-
console.log(JSON.stringify(formatError(CliErrorCode.ValidationError, `Validation failed: ${messages}`, [
|
|
25
|
-
'Fix the validation errors and try again',
|
|
26
|
-
])));
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
29
|
-
const outputPath = options.output ?? graphPath;
|
|
30
|
-
const finalGraph = builder.build();
|
|
31
|
-
await writeFile(outputPath, JSON.stringify(finalGraph, null, 2), 'utf-8');
|
|
32
|
-
if (options.json === true) {
|
|
33
|
-
console.log(JSON.stringify(formatSuccess({ path: outputPath })));
|
|
34
|
-
}
|
|
35
|
-
});
|
|
36
|
-
});
|
|
37
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../src/commands/builder/init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAqDpC,wBAAgB,iBAAiB,IAAI,OAAO,CAiG3C"}
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
import { mkdir, writeFile } from 'node:fs/promises';
|
|
3
|
-
import { dirname } from 'node:path';
|
|
4
|
-
import { RiviereBuilder } from '@living-architecture/riviere-builder';
|
|
5
|
-
import { formatError, formatSuccess } from '../../output';
|
|
6
|
-
import { CliErrorCode } from '../../error-codes';
|
|
7
|
-
import { fileExists } from '../../file-existence';
|
|
8
|
-
import { resolveGraphPath, getDefaultGraphPathDescription } from '../../graph-path';
|
|
9
|
-
import { isValidSystemType } from '../../component-types';
|
|
10
|
-
function isDomainInputParsed(value) {
|
|
11
|
-
if (typeof value !== 'object' || value === null) {
|
|
12
|
-
return false;
|
|
13
|
-
}
|
|
14
|
-
return ('name' in value &&
|
|
15
|
-
typeof value.name === 'string' &&
|
|
16
|
-
'description' in value &&
|
|
17
|
-
typeof value.description === 'string' &&
|
|
18
|
-
'systemType' in value &&
|
|
19
|
-
typeof value.systemType === 'string' &&
|
|
20
|
-
isValidSystemType(value.systemType));
|
|
21
|
-
}
|
|
22
|
-
function parseDomainJson(value, previous) {
|
|
23
|
-
const parsed = JSON.parse(value);
|
|
24
|
-
if (!isDomainInputParsed(parsed)) {
|
|
25
|
-
throw new Error(`Invalid domain JSON: ${value}`);
|
|
26
|
-
}
|
|
27
|
-
return [...previous, parsed];
|
|
28
|
-
}
|
|
29
|
-
function collectSource(value, previous) {
|
|
30
|
-
return [...previous, value];
|
|
31
|
-
}
|
|
32
|
-
export function createInitCommand() {
|
|
33
|
-
return new Command('init')
|
|
34
|
-
.description('Initialize a new graph')
|
|
35
|
-
.addHelpText('after', `
|
|
36
|
-
Examples:
|
|
37
|
-
$ riviere builder init --source https://github.com/org/repo \\
|
|
38
|
-
--domain '{"name":"orders","description":"Order management","systemType":"domain"}'
|
|
39
|
-
|
|
40
|
-
$ riviere builder init --name "ecommerce" \\
|
|
41
|
-
--source https://github.com/org/orders \\
|
|
42
|
-
--source https://github.com/org/payments \\
|
|
43
|
-
--domain '{"name":"orders","description":"Order management","systemType":"domain"}' \\
|
|
44
|
-
--domain '{"name":"payments","description":"Payment processing","systemType":"domain"}'
|
|
45
|
-
`)
|
|
46
|
-
.option('--name <name>', 'System name')
|
|
47
|
-
.option('--graph <path>', getDefaultGraphPathDescription())
|
|
48
|
-
.option('--json', 'Output result as JSON')
|
|
49
|
-
.option('--source <url>', 'Source repository URL (repeatable)', collectSource, [])
|
|
50
|
-
.option('--domain <json>', 'Domain as JSON (repeatable)', parseDomainJson, [])
|
|
51
|
-
.action(async (options) => {
|
|
52
|
-
// Validate required flags
|
|
53
|
-
if (options.source.length === 0) {
|
|
54
|
-
console.log(JSON.stringify(formatError(CliErrorCode.ValidationError, 'At least one source required', [
|
|
55
|
-
'Add --source <url> flag',
|
|
56
|
-
])));
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
if (options.domain.length === 0) {
|
|
60
|
-
console.log(JSON.stringify(formatError(CliErrorCode.ValidationError, 'At least one domain required', [
|
|
61
|
-
'Add --domain <json> flag',
|
|
62
|
-
])));
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
const graphPath = resolveGraphPath(options.graph);
|
|
66
|
-
const graphDir = dirname(graphPath);
|
|
67
|
-
const graphExists = await fileExists(graphPath);
|
|
68
|
-
if (graphExists) {
|
|
69
|
-
console.log(JSON.stringify(formatError(CliErrorCode.GraphExists, `Graph already exists at ${graphPath}`, [
|
|
70
|
-
'Delete the file to reinitialize',
|
|
71
|
-
])));
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
74
|
-
const domains = {};
|
|
75
|
-
for (const d of options.domain) {
|
|
76
|
-
domains[d.name] = {
|
|
77
|
-
description: d.description,
|
|
78
|
-
systemType: d.systemType,
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
const builderOptions = {
|
|
82
|
-
sources: options.source.map((url) => ({ repository: url })),
|
|
83
|
-
domains,
|
|
84
|
-
};
|
|
85
|
-
if (options.name !== undefined) {
|
|
86
|
-
builderOptions.name = options.name;
|
|
87
|
-
}
|
|
88
|
-
const builder = RiviereBuilder.new(builderOptions);
|
|
89
|
-
await mkdir(graphDir, { recursive: true });
|
|
90
|
-
await writeFile(graphPath, builder.serialize(), 'utf-8');
|
|
91
|
-
if (options.json === true) {
|
|
92
|
-
const domainNames = options.domain.map((d) => d.name);
|
|
93
|
-
console.log(JSON.stringify(formatSuccess({
|
|
94
|
-
path: graphPath,
|
|
95
|
-
sources: options.source.length,
|
|
96
|
-
domains: domainNames,
|
|
97
|
-
})));
|
|
98
|
-
}
|
|
99
|
-
});
|
|
100
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"link-external.d.ts","sourceRoot":"","sources":["../../../src/commands/builder/link-external.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAkCpC,wBAAgB,yBAAyB,IAAI,OAAO,CAiEnD"}
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
import { writeFile } from 'node:fs/promises';
|
|
3
|
-
import { getDefaultGraphPathDescription, resolveGraphPath } from '../../graph-path';
|
|
4
|
-
import { fileExists } from '../../file-existence';
|
|
5
|
-
import { formatSuccess } from '../../output';
|
|
6
|
-
import { isValidLinkType } from '../../component-types';
|
|
7
|
-
import { validateLinkType } from '../../validation';
|
|
8
|
-
import { loadGraphBuilder, reportGraphNotFound, tryBuilderOperation } from './link-infrastructure';
|
|
9
|
-
function buildExternalTarget(options) {
|
|
10
|
-
return {
|
|
11
|
-
name: options.targetName,
|
|
12
|
-
...(options.targetDomain && { domain: options.targetDomain }),
|
|
13
|
-
...(options.targetUrl && { url: options.targetUrl }),
|
|
14
|
-
};
|
|
15
|
-
}
|
|
16
|
-
export function createLinkExternalCommand() {
|
|
17
|
-
return new Command('link-external')
|
|
18
|
-
.description('Link a component to an external system')
|
|
19
|
-
.addHelpText('after', `
|
|
20
|
-
Examples:
|
|
21
|
-
$ riviere builder link-external \\
|
|
22
|
-
--from "payments:gateway:usecase:processpayment" \\
|
|
23
|
-
--target-name "Stripe" \\
|
|
24
|
-
--target-url "https://api.stripe.com" \\
|
|
25
|
-
--link-type sync
|
|
26
|
-
|
|
27
|
-
$ riviere builder link-external \\
|
|
28
|
-
--from "shipping:tracking:usecase:updatetracking" \\
|
|
29
|
-
--target-name "FedEx API" \\
|
|
30
|
-
--target-domain "shipping" \\
|
|
31
|
-
--link-type async
|
|
32
|
-
`)
|
|
33
|
-
.requiredOption('--from <component-id>', 'Source component ID')
|
|
34
|
-
.requiredOption('--target-name <name>', 'External target name')
|
|
35
|
-
.option('--target-domain <domain>', 'External target domain')
|
|
36
|
-
.option('--target-url <url>', 'External target URL')
|
|
37
|
-
.option('--link-type <type>', 'Link type (sync, async)')
|
|
38
|
-
.option('--graph <path>', getDefaultGraphPathDescription())
|
|
39
|
-
.option('--json', 'Output result as JSON')
|
|
40
|
-
.action(async (options) => {
|
|
41
|
-
const linkTypeValidation = validateLinkType(options.linkType);
|
|
42
|
-
if (!linkTypeValidation.valid) {
|
|
43
|
-
console.log(linkTypeValidation.errorJson);
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
const graphPath = resolveGraphPath(options.graph);
|
|
47
|
-
const graphExists = await fileExists(graphPath);
|
|
48
|
-
if (!graphExists) {
|
|
49
|
-
reportGraphNotFound(graphPath);
|
|
50
|
-
return;
|
|
51
|
-
}
|
|
52
|
-
const builder = await loadGraphBuilder(graphPath);
|
|
53
|
-
const target = buildExternalTarget(options);
|
|
54
|
-
const externalLinkInput = {
|
|
55
|
-
from: options.from,
|
|
56
|
-
target,
|
|
57
|
-
};
|
|
58
|
-
if (options.linkType !== undefined && isValidLinkType(options.linkType)) {
|
|
59
|
-
externalLinkInput.type = options.linkType;
|
|
60
|
-
}
|
|
61
|
-
const externalLink = tryBuilderOperation(() => builder.linkExternal(externalLinkInput));
|
|
62
|
-
if (externalLink === undefined) {
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
await writeFile(graphPath, builder.serialize(), 'utf-8');
|
|
66
|
-
if (options.json) {
|
|
67
|
-
console.log(JSON.stringify(formatSuccess({ externalLink })));
|
|
68
|
-
}
|
|
69
|
-
});
|
|
70
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"link-http.d.ts","sourceRoot":"","sources":["../../../src/commands/builder/link-http.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAoGpC,wBAAgB,qBAAqB,IAAI,OAAO,CA+F/C"}
|
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
import { writeFile } from 'node:fs/promises';
|
|
3
|
-
import { ComponentId } from '@living-architecture/riviere-builder';
|
|
4
|
-
import { RiviereQuery } from '@living-architecture/riviere-query';
|
|
5
|
-
import { getDefaultGraphPathDescription, resolveGraphPath } from '../../graph-path';
|
|
6
|
-
import { fileExists } from '../../file-existence';
|
|
7
|
-
import { formatError, formatSuccess } from '../../output';
|
|
8
|
-
import { CliErrorCode } from '../../error-codes';
|
|
9
|
-
import { isValidLinkType, normalizeComponentType } from '../../component-types';
|
|
10
|
-
import { isValidHttpMethod, validateComponentType, validateHttpMethod, validateLinkType } from '../../validation';
|
|
11
|
-
import { loadGraphBuilder, reportGraphNotFound } from './link-infrastructure';
|
|
12
|
-
function isRestApiWithPath(component) {
|
|
13
|
-
return component.type === 'API' && 'path' in component && 'httpMethod' in component;
|
|
14
|
-
}
|
|
15
|
-
function findApisByPath(graph, path, method) {
|
|
16
|
-
const query = new RiviereQuery(graph);
|
|
17
|
-
const allComponents = query.componentsByType('API');
|
|
18
|
-
const apis = allComponents.filter(isRestApiWithPath);
|
|
19
|
-
const matchingPath = apis.filter((api) => api.path === path);
|
|
20
|
-
if (method) {
|
|
21
|
-
return matchingPath.filter((api) => api.httpMethod === method);
|
|
22
|
-
}
|
|
23
|
-
return matchingPath;
|
|
24
|
-
}
|
|
25
|
-
function getAllApiPaths(graph) {
|
|
26
|
-
const query = new RiviereQuery(graph);
|
|
27
|
-
const allComponents = query.componentsByType('API');
|
|
28
|
-
const apis = allComponents.filter(isRestApiWithPath);
|
|
29
|
-
return [...new Set(apis.map((api) => api.path))];
|
|
30
|
-
}
|
|
31
|
-
function reportNoApiFoundForPath(path, availablePaths) {
|
|
32
|
-
console.log(JSON.stringify(formatError(CliErrorCode.ComponentNotFound, `No API found with path '${path}'`, availablePaths.length > 0 ? [`Available paths: ${availablePaths.join(', ')}`] : [])));
|
|
33
|
-
}
|
|
34
|
-
function reportAmbiguousApiMatch(path, matchingApis) {
|
|
35
|
-
const apiList = matchingApis.map((api) => `${api.id} (${api.httpMethod})`).join(', ');
|
|
36
|
-
console.log(JSON.stringify(formatError(CliErrorCode.AmbiguousApiMatch, `Multiple APIs match path '${path}': ${apiList}`, [
|
|
37
|
-
'Add --method flag to disambiguate',
|
|
38
|
-
])));
|
|
39
|
-
}
|
|
40
|
-
function validateOptions(options) {
|
|
41
|
-
const componentTypeValidation = validateComponentType(options.toType);
|
|
42
|
-
if (!componentTypeValidation.valid) {
|
|
43
|
-
return componentTypeValidation.errorJson;
|
|
44
|
-
}
|
|
45
|
-
const httpMethodValidation = validateHttpMethod(options.method);
|
|
46
|
-
if (!httpMethodValidation.valid) {
|
|
47
|
-
return httpMethodValidation.errorJson;
|
|
48
|
-
}
|
|
49
|
-
const linkTypeValidation = validateLinkType(options.linkType);
|
|
50
|
-
if (!linkTypeValidation.valid) {
|
|
51
|
-
return linkTypeValidation.errorJson;
|
|
52
|
-
}
|
|
53
|
-
return undefined;
|
|
54
|
-
}
|
|
55
|
-
export function createLinkHttpCommand() {
|
|
56
|
-
return new Command('link-http')
|
|
57
|
-
.description('Find an API by HTTP path and link to a target component')
|
|
58
|
-
.addHelpText('after', `
|
|
59
|
-
Examples:
|
|
60
|
-
$ riviere builder link-http \\
|
|
61
|
-
--path "/orders" --method POST \\
|
|
62
|
-
--to-domain orders --to-module checkout --to-type UseCase --to-name "place-order"
|
|
63
|
-
|
|
64
|
-
$ riviere builder link-http \\
|
|
65
|
-
--path "/users/{id}" --method GET \\
|
|
66
|
-
--to-domain users --to-module queries --to-type UseCase --to-name "get-user" \\
|
|
67
|
-
--link-type sync
|
|
68
|
-
`)
|
|
69
|
-
.requiredOption('--path <http-path>', 'HTTP path to match')
|
|
70
|
-
.requiredOption('--to-domain <domain>', 'Target domain')
|
|
71
|
-
.requiredOption('--to-module <module>', 'Target module')
|
|
72
|
-
.requiredOption('--to-type <type>', 'Target component type')
|
|
73
|
-
.requiredOption('--to-name <name>', 'Target component name')
|
|
74
|
-
.option('--method <method>', 'Filter by HTTP method (GET, POST, PUT, PATCH, DELETE)')
|
|
75
|
-
.option('--link-type <type>', 'Link type (sync, async)')
|
|
76
|
-
.option('--graph <path>', getDefaultGraphPathDescription())
|
|
77
|
-
.option('--json', 'Output result as JSON')
|
|
78
|
-
.action(async (options) => {
|
|
79
|
-
const validationError = validateOptions(options);
|
|
80
|
-
if (validationError) {
|
|
81
|
-
console.log(validationError);
|
|
82
|
-
return;
|
|
83
|
-
}
|
|
84
|
-
const graphPath = resolveGraphPath(options.graph);
|
|
85
|
-
const graphExists = await fileExists(graphPath);
|
|
86
|
-
if (!graphExists) {
|
|
87
|
-
reportGraphNotFound(graphPath);
|
|
88
|
-
return;
|
|
89
|
-
}
|
|
90
|
-
const builder = await loadGraphBuilder(graphPath);
|
|
91
|
-
const graph = builder.build();
|
|
92
|
-
const normalizedMethod = options.method?.toUpperCase();
|
|
93
|
-
const httpMethod = normalizedMethod && isValidHttpMethod(normalizedMethod) ? normalizedMethod : undefined;
|
|
94
|
-
const matchingApis = findApisByPath(graph, options.path, httpMethod);
|
|
95
|
-
const [matchedApi, ...otherApis] = matchingApis;
|
|
96
|
-
if (!matchedApi) {
|
|
97
|
-
reportNoApiFoundForPath(options.path, getAllApiPaths(graph));
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
if (otherApis.length > 0) {
|
|
101
|
-
reportAmbiguousApiMatch(options.path, matchingApis);
|
|
102
|
-
return;
|
|
103
|
-
}
|
|
104
|
-
const targetId = ComponentId.create({
|
|
105
|
-
domain: options.toDomain,
|
|
106
|
-
module: options.toModule,
|
|
107
|
-
type: normalizeComponentType(options.toType),
|
|
108
|
-
name: options.toName,
|
|
109
|
-
}).toString();
|
|
110
|
-
const linkInput = {
|
|
111
|
-
from: matchedApi.id,
|
|
112
|
-
to: targetId,
|
|
113
|
-
};
|
|
114
|
-
if (options.linkType !== undefined && isValidLinkType(options.linkType)) {
|
|
115
|
-
linkInput.type = options.linkType;
|
|
116
|
-
}
|
|
117
|
-
const link = builder.link(linkInput);
|
|
118
|
-
await writeFile(graphPath, builder.serialize(), 'utf-8');
|
|
119
|
-
if (options.json) {
|
|
120
|
-
console.log(JSON.stringify(formatSuccess({
|
|
121
|
-
link,
|
|
122
|
-
matchedApi: {
|
|
123
|
-
id: matchedApi.id,
|
|
124
|
-
path: matchedApi.path,
|
|
125
|
-
method: matchedApi.httpMethod,
|
|
126
|
-
},
|
|
127
|
-
})));
|
|
128
|
-
}
|
|
129
|
-
});
|
|
130
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { RiviereBuilder } from '@living-architecture/riviere-builder';
|
|
2
|
-
export declare function reportGraphNotFound(graphPath: string): void;
|
|
3
|
-
export declare function loadGraphBuilder(graphPath: string): Promise<RiviereBuilder>;
|
|
4
|
-
export declare function withGraphBuilder(graphPathOption: string | undefined, handler: (builder: RiviereBuilder, graphPath: string) => Promise<void>): Promise<void>;
|
|
5
|
-
export declare function handleComponentNotFoundError(error: unknown): void;
|
|
6
|
-
export declare function tryBuilderOperation<T>(operation: () => T): T | undefined;
|
|
7
|
-
//# sourceMappingURL=link-infrastructure.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"link-infrastructure.d.ts","sourceRoot":"","sources":["../../../src/commands/builder/link-infrastructure.ts"],"names":[],"mappings":"AACA,OAAO,EAA0B,cAAc,EAAE,MAAM,sCAAsC,CAAC;AAO9F,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAM3D;AAED,wBAAsB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAKjF;AAED,wBAAsB,gBAAgB,CACpC,eAAe,EAAE,MAAM,GAAG,SAAS,EACnC,OAAO,EAAE,CAAC,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,GACrE,OAAO,CAAC,IAAI,CAAC,CAWf;AAED,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAKjE;AAED,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,CAAC,GAAG,CAAC,GAAG,SAAS,CAOxE"}
|