@alistigo/calm-to-archify 0.0.0-stage → 0.2.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Mikael Labrut
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,81 @@
1
- # Temporary Holding Version
1
+ # @alistigo/calm-to-archify
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ [![npm version](https://img.shields.io/npm/v/@alistigo/calm-to-archify.svg?style=flat)](https://www.npmjs.com/package/@alistigo/calm-to-archify)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+ [![TypeScript](https://img.shields.io/badge/TypeScript-strict-blue?logo=typescript)](https://www.typescriptlang.org/)
6
+ [![CI](https://github.com/alistigo/holos/actions/workflows/ci.yml/badge.svg)](https://github.com/alistigo/holos/actions/workflows/ci.yml)
7
+
8
+ CLI that converts [FINOS CALM](https://calm.finos.org/) architecture instances (`.arch.json`) into interactive HTML diagrams via [archify](https://github.com/tt-a1i/archify).
9
+
10
+ ## Install
11
+
12
+ ```sh
13
+ npm install -g @alistigo/calm-to-archify
14
+ # or
15
+ yarn global add @alistigo/calm-to-archify
16
+ # or
17
+ pnpm add -g @alistigo/calm-to-archify
18
+ ```
19
+
20
+ As a dev dependency:
21
+
22
+ ```sh
23
+ npm install -D @alistigo/calm-to-archify
24
+ ```
25
+
26
+ Requires Node.js ≥ 18.
27
+
28
+ ## Commands
29
+
30
+ ### `build` (default)
31
+
32
+ Full pipeline: CALM JSON → archify JSON-IR → standalone HTML.
33
+
34
+ ```sh
35
+ calm-to-archify systems/*.arch.json
36
+ calm-to-archify build systems/*.arch.json
37
+ ```
38
+
39
+ Shell glob expansion passes all matched files as positional arguments. Each file produces two outputs next to the source: `<unique-id>.archify.json` and `<unique-id>.archify.html`.
40
+
41
+ **Options**
42
+
43
+ | Flag | Description |
44
+ |------|-------------|
45
+ | `--output-dir,-o <dir>` | Write outputs to `<dir>` instead of each file's own directory |
46
+
47
+ ```sh
48
+ calm-to-archify systems/*.arch.json --output-dir .archify-out
49
+ ```
50
+
51
+ ### `transform`
52
+
53
+ CALM JSON → archify JSON-IR only (no HTML rendering).
54
+
55
+ ```sh
56
+ calm-to-archify transform path/to/my.arch.json --output path/to/output.archify.json
57
+ ```
58
+
59
+ **Options**
60
+
61
+ | Flag | Description |
62
+ |------|-------------|
63
+ | `--output,-o <path>` | Output path for the archify JSON file (required) |
64
+
65
+ ## Input format
66
+
67
+ Accepts CALM architecture instances — files that contain `nodes` and `relationships` at the top level. Pattern files (`.pattern.json`) are JSON Schema documents and are not supported.
68
+
69
+ ## About CALM
70
+
71
+ [CALM (Common Architecture Language Model)](https://calm.finos.org/) is an open standard from [FINOS](https://www.finos.org/) for describing software architectures as machine-readable JSON. It lets you capture nodes (services, datastores, people) and the relationships between them in a way that can be validated, queried, and visualised automatically.
72
+
73
+ ## About archify
74
+
75
+ [archify](https://github.com/tt-a1i/archify) is an open-source tool that turns a structured JSON description of a system into a fully interactive standalone HTML diagram — no server required, no external dependencies at runtime. `calm-to-archify` uses archify under the hood to produce the HTML output.
76
+
77
+ ## Thanks
78
+
79
+ A big thank you to the [FINOS CALM team](https://github.com/finos/architecture-as-code) for building and maintaining an open, vendor-neutral standard for architecture-as-code — making tooling like this possible.
80
+
81
+ And to the [archify team](https://github.com/tt-a1i/archify) for creating a lightweight, dependency-free renderer that turns plain JSON into beautiful interactive diagrams — great work.
@@ -0,0 +1,10 @@
1
+ import { Command } from "clipanion";
2
+ export declare class BuildCommand extends Command {
3
+ static paths: string[][];
4
+ static usage: import("clipanion").Usage;
5
+ inputFiles: string[];
6
+ outputDir: string | undefined;
7
+ execute(): Promise<number>;
8
+ private run;
9
+ }
10
+ //# sourceMappingURL=build.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/build.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,OAAO,EAAU,MAAM,WAAW,CAAC;AAS5C,qBAAa,YAAa,SAAQ,OAAO;IACvC,OAAgB,KAAK,aAAgC;IAErD,OAAgB,KAAK,4BAoBlB;IAEH,UAAU,WAA+C;IAEzD,SAAS,qBAGN;IAEH,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC;IAK1B,OAAO,CAAC,GAAG;CA0DZ"}
@@ -0,0 +1,95 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
3
+ import { createRequire } from "node:module";
4
+ import path from "node:path";
5
+ import { Command, Option } from "clipanion";
6
+ import { transformCalmToArchify } from "../lib/calm-to-archify.js";
7
+ function resolveArchifyRenderBin() {
8
+ const req = createRequire(import.meta.url);
9
+ const pkgJson = req.resolve("@alistigo/archify-render/package.json");
10
+ return path.join(path.dirname(pkgJson), "dist/cli.js");
11
+ }
12
+ export class BuildCommand extends Command {
13
+ static paths = [Command.Default, ["build"]];
14
+ static usage = Command.Usage({
15
+ description: "Run the full CALM → archify HTML pipeline for one or more .arch.json files",
16
+ details: `
17
+ Reads each CALM .arch.json file, transforms it to archify JSON-IR, then
18
+ renders a standalone interactive HTML file using the archify renderer.
19
+
20
+ By default each output is written next to its source file.
21
+ Use --output-dir to redirect all outputs to a specific directory.
22
+
23
+ Pattern files (.pattern.json) are JSON Schema documents and are not
24
+ supported — only CALM architecture instances (.arch.json) are accepted.
25
+ `,
26
+ examples: [
27
+ ["Process all architecture files", "calm-to-archify build systems/*.arch.json"],
28
+ ["Process a single file", "calm-to-archify build path/to/my.arch.json"],
29
+ [
30
+ "Output to a specific directory",
31
+ "calm-to-archify build systems/*.arch.json --output-dir .archify-out",
32
+ ],
33
+ ],
34
+ });
35
+ inputFiles = Option.Rest({ required: 1, name: "files" });
36
+ outputDir = Option.String("--output-dir,-o", {
37
+ description: "Output directory (default: same directory as each input file)",
38
+ required: false,
39
+ });
40
+ execute() {
41
+ return Promise.resolve(this.run());
42
+ }
43
+ // fallow-ignore-next-line complexity
44
+ run() {
45
+ let archifyRenderBin;
46
+ try {
47
+ archifyRenderBin = resolveArchifyRenderBin();
48
+ }
49
+ catch (err) {
50
+ this.context.stderr.write(`Error: could not resolve archify-render binary. Make sure @alistigo/archify-render is installed and built: ${String(err)}\n`);
51
+ return 1;
52
+ }
53
+ for (const file of this.inputFiles) {
54
+ const inputPath = path.resolve(process.cwd(), file);
55
+ if (!existsSync(inputPath)) {
56
+ this.context.stderr.write(`Error: file not found: ${inputPath}\n`);
57
+ return 1;
58
+ }
59
+ const outDir = this.outputDir
60
+ ? path.resolve(process.cwd(), this.outputDir)
61
+ : path.dirname(inputPath);
62
+ let calm;
63
+ try {
64
+ calm = JSON.parse(readFileSync(inputPath, "utf-8"));
65
+ }
66
+ catch (err) {
67
+ this.context.stderr.write(`Error: failed to parse ${inputPath}: ${String(err)}\n`);
68
+ return 1;
69
+ }
70
+ if (!calm.nodes || !calm.relationships) {
71
+ this.context.stderr.write(`Error: ${inputPath} does not look like a CALM architecture instance — ` +
72
+ "it's missing 'nodes' or 'relationships'. Pattern files (.pattern.json) are not supported.\n");
73
+ return 1;
74
+ }
75
+ const archifyJson = path.join(outDir, `${calm["unique-id"]}.archify.json`);
76
+ const htmlOutputPath = path.relative(process.cwd(), archifyJson.replace(/\.json$/, ".html"));
77
+ const archify = transformCalmToArchify(calm, htmlOutputPath);
78
+ mkdirSync(outDir, { recursive: true });
79
+ writeFileSync(archifyJson, JSON.stringify(archify, null, 2), "utf-8");
80
+ this.context.stdout.write(`Transformed → ${archifyJson}\n`);
81
+ const r = spawnSync("node", [archifyRenderBin, archifyJson], {
82
+ stdio: "inherit",
83
+ cwd: process.cwd(),
84
+ });
85
+ if (r.error) {
86
+ this.context.stderr.write(`Error running archify-render: ${r.error.message}\n`);
87
+ return 1;
88
+ }
89
+ if ((r.status ?? 1) !== 0)
90
+ return r.status ?? 1;
91
+ }
92
+ return 0;
93
+ }
94
+ }
95
+ //# sourceMappingURL=build.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build.js","sourceRoot":"","sources":["../../../src/cli/commands/build.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import { Command } from "clipanion";
2
+ export declare class TransformCommand extends Command {
3
+ static paths: string[][];
4
+ static usage: import("clipanion").Usage;
5
+ inputFile: string;
6
+ output: string;
7
+ execute(): Promise<number>;
8
+ }
9
+ //# sourceMappingURL=transform.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transform.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/transform.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAU,MAAM,WAAW,CAAC;AAG5C,qBAAa,gBAAiB,SAAQ,OAAO;IAC3C,OAAgB,KAAK,aAAmB;IAExC,OAAgB,KAAK,4BAoBlB;IAEH,SAAS,SAAoD;IAE7D,MAAM,SAGH;IAGG,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC;CAqCjC"}
@@ -0,0 +1,64 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { dirname, relative, resolve } from "node:path";
3
+ import { Command, Option } from "clipanion";
4
+ import { transformCalmToArchify } from "../lib/calm-to-archify.js";
5
+ export class TransformCommand extends Command {
6
+ static paths = [["transform"]];
7
+ static usage = Command.Usage({
8
+ description: "Transform a CALM architecture JSON file into archify diagram JSON",
9
+ details: `
10
+ Reads a CALM .arch.json file and produces an archify JSON-IR file
11
+ that can be rendered to interactive HTML using archify-render or
12
+ 'archify render architecture'.
13
+
14
+ Pattern files (.pattern.json) are JSON Schema documents and are not
15
+ supported — only CALM architecture instances (.arch.json) are accepted.
16
+ `,
17
+ examples: [
18
+ [
19
+ "Transform typical-alistigo-artifact.arch.json",
20
+ "calm-to-archify systems/typical-alistigo-artifact.arch.json --output .archify-out/typical-alistigo-artifact.archify.json",
21
+ ],
22
+ [
23
+ "Transform with explicit output path",
24
+ "calm-to-archify path/to/my.arch.json --output path/to/output.archify.json",
25
+ ],
26
+ ],
27
+ });
28
+ inputFile = Option.String({ required: true, name: "input" });
29
+ output = Option.String("--output,-o", {
30
+ description: "Output path for the archify JSON file",
31
+ required: true,
32
+ });
33
+ // fallow-ignore-next-line complexity
34
+ async execute() {
35
+ const inputPath = resolve(process.cwd(), this.inputFile);
36
+ const outputPath = resolve(process.cwd(), this.output);
37
+ if (!existsSync(inputPath)) {
38
+ this.context.stderr.write(`Error: input file not found: ${inputPath}\n`);
39
+ return 1;
40
+ }
41
+ let calm;
42
+ try {
43
+ calm = JSON.parse(readFileSync(inputPath, "utf-8"));
44
+ }
45
+ catch (err) {
46
+ this.context.stderr.write(`Error: failed to parse ${inputPath}: ${String(err)}\n`);
47
+ return 1;
48
+ }
49
+ if (!calm.nodes || !calm.relationships) {
50
+ this.context.stderr.write(`Error: ${inputPath} does not look like a CALM architecture instance — ` +
51
+ "it's missing 'nodes' or 'relationships'. Pattern files (.pattern.json) are not supported.\n");
52
+ return 1;
53
+ }
54
+ // archify requires meta.output to be a relative path from cwd
55
+ const htmlOutputPath = relative(process.cwd(), outputPath.replace(/\.json$/, ".html"));
56
+ const archify = transformCalmToArchify(calm, htmlOutputPath);
57
+ const outputDir = dirname(outputPath);
58
+ mkdirSync(outputDir, { recursive: true });
59
+ writeFileSync(outputPath, JSON.stringify(archify, null, 2), "utf-8");
60
+ this.context.stdout.write(`Transformed → ${outputPath}\n`);
61
+ return 0;
62
+ }
63
+ }
64
+ //# sourceMappingURL=transform.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transform.js","sourceRoot":"","sources":["../../../src/cli/commands/transform.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACvD,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,EAAqB,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AAEtF,MAAM,OAAO,gBAAiB,SAAQ,OAAO;IAC3C,MAAM,CAAU,KAAK,GAAG,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC;IAExC,MAAM,CAAU,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;QACpC,WAAW,EAAE,mEAAmE;QAChF,OAAO,EAAE;;;;;;;KAOR;QACD,QAAQ,EAAE;YACR;gBACE,+CAA+C;gBAC/C,0HAA0H;aAC3H;YACD;gBACE,qCAAqC;gBACrC,2EAA2E;aAC5E;SACF;KACF,CAAC,CAAC;IAEH,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;IAE7D,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,aAAa,EAAE;QACpC,WAAW,EAAE,uCAAuC;QACpD,QAAQ,EAAE,IAAI;KACf,CAAC,CAAC;IAEH,qCAAqC;IACrC,KAAK,CAAC,OAAO;QACX,MAAM,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;QACzD,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;QAEvD,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YAC3B,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,gCAAgC,SAAS,IAAI,CAAC,CAAC;YACzE,OAAO,CAAC,CAAC;QACX,CAAC;QAED,IAAI,IAAkB,CAAC;QACvB,IAAI,CAAC;YACH,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,SAAS,EAAE,OAAO,CAAC,CAAiB,CAAC;QACtE,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,0BAA0B,SAAS,KAAK,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YACnF,OAAO,CAAC,CAAC;QACX,CAAC;QAED,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC;YACvC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CACvB,UAAU,SAAS,qDAAqD;gBACtE,6FAA6F,CAChG,CAAC;YACF,OAAO,CAAC,CAAC;QACX,CAAC;QAED,8DAA8D;QAC9D,MAAM,cAAc,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,UAAU,CAAC,OAAO,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC,CAAC;QAEvF,MAAM,OAAO,GAAG,sBAAsB,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC;QAE7D,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;QACtC,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC1C,aAAa,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;QAErE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,iBAAiB,UAAU,IAAI,CAAC,CAAC;QAC3D,OAAO,CAAC,CAAC;IACX,CAAC"}
@@ -0,0 +1,73 @@
1
+ export interface CalmNode {
2
+ "unique-id": string;
3
+ "node-type": string;
4
+ name: string;
5
+ description?: string;
6
+ }
7
+ export interface CalmRelationship {
8
+ "unique-id": string;
9
+ description?: string;
10
+ "relationship-type": {
11
+ "composed-of": {
12
+ container: string;
13
+ nodes: string[];
14
+ };
15
+ } | {
16
+ "deployed-in": {
17
+ container: string;
18
+ nodes: string[];
19
+ };
20
+ } | {
21
+ connects: {
22
+ source: {
23
+ node: string;
24
+ };
25
+ destination: {
26
+ node: string;
27
+ };
28
+ };
29
+ } | {
30
+ interacts: {
31
+ actor: string;
32
+ nodes: string[];
33
+ };
34
+ };
35
+ }
36
+ export interface CalmDocument {
37
+ "unique-id": string;
38
+ name: string;
39
+ description?: string;
40
+ nodes: CalmNode[];
41
+ relationships: CalmRelationship[];
42
+ }
43
+ export interface ArchifyComponent {
44
+ id: string;
45
+ type: string;
46
+ label: string;
47
+ pos: [number, number];
48
+ size: [number, number];
49
+ }
50
+ export interface ArchifyBoundary {
51
+ kind: string;
52
+ label: string;
53
+ wraps: string[];
54
+ }
55
+ export interface ArchifyConnection {
56
+ id: string;
57
+ from: string;
58
+ to: string;
59
+ }
60
+ export interface ArchifyDocument {
61
+ schema_version: 1;
62
+ diagram_type: "architecture";
63
+ meta: {
64
+ title: string;
65
+ subtitle?: string;
66
+ output: string;
67
+ };
68
+ components: ArchifyComponent[];
69
+ boundaries: ArchifyBoundary[];
70
+ connections: ArchifyConnection[];
71
+ }
72
+ export declare function transformCalmToArchify(calm: CalmDocument, outputPath: string): ArchifyDocument;
73
+ //# sourceMappingURL=calm-to-archify.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"calm-to-archify.d.ts","sourceRoot":"","sources":["../../../src/cli/lib/calm-to-archify.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,QAAQ;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mBAAmB,EACf;QAAE,aAAa,EAAE;YAAE,SAAS,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,EAAE,CAAA;SAAE,CAAA;KAAE,GACzD;QAAE,aAAa,EAAE;YAAE,SAAS,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,EAAE,CAAA;SAAE,CAAA;KAAE,GACzD;QAAE,QAAQ,EAAE;YAAE,MAAM,EAAE;gBAAE,IAAI,EAAE,MAAM,CAAA;aAAE,CAAC;YAAC,WAAW,EAAE;gBAAE,IAAI,EAAE,MAAM,CAAA;aAAE,CAAA;SAAE,CAAA;KAAE,GACzE;QAAE,SAAS,EAAE;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,EAAE,CAAA;SAAE,CAAA;KAAE,CAAC;CACvD;AAED,MAAM,WAAW,YAAY;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,aAAa,EAAE,gBAAgB,EAAE,CAAC;CACnC;AAED,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtB,IAAI,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACxB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,eAAe;IAC9B,cAAc,EAAE,CAAC,CAAC;IAClB,YAAY,EAAE,cAAc,CAAC;IAC7B,IAAI,EAAE;QACJ,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,UAAU,EAAE,gBAAgB,EAAE,CAAC;IAC/B,UAAU,EAAE,eAAe,EAAE,CAAC;IAC9B,WAAW,EAAE,iBAAiB,EAAE,CAAC;CAClC;AAyCD,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,GAAG,eAAe,CA+F9F"}
@@ -0,0 +1,124 @@
1
+ import path from "node:path";
2
+ import { computeLayout } from "./layout.js";
3
+ // fallow-ignore-next-line complexity
4
+ function mapNodeType(nodeType, name) {
5
+ const lower = name.toLowerCase();
6
+ switch (nodeType) {
7
+ case "actor":
8
+ return "external";
9
+ case "database":
10
+ return "database";
11
+ case "data-asset":
12
+ return "external";
13
+ case "system": {
14
+ return "backend";
15
+ }
16
+ case "service": {
17
+ if (lower.includes("cdn") || lower.includes("npm"))
18
+ return "cloud";
19
+ if (lower.includes("bus") || lower.includes("event"))
20
+ return "messagebus";
21
+ if (lower.includes("presentation") ||
22
+ lower.includes("react") ||
23
+ lower.includes("ui") ||
24
+ lower.includes("frontend"))
25
+ return "frontend";
26
+ return "backend";
27
+ }
28
+ default:
29
+ return "backend";
30
+ }
31
+ }
32
+ function firstSentence(text) {
33
+ const dot = text.indexOf(".");
34
+ const maxLen = 120;
35
+ if (dot === -1 || dot > maxLen)
36
+ return text.length <= maxLen ? text : `${text.slice(0, maxLen - 1)}…`;
37
+ return text.slice(0, dot + 1);
38
+ }
39
+ // fallow-ignore-next-line complexity
40
+ export function transformCalmToArchify(calm, outputPath) {
41
+ const nodeIds = calm.nodes.map((n) => n["unique-id"]);
42
+ const composedOfSets = [];
43
+ const deployedInSets = [];
44
+ const boundaries = [];
45
+ const connections = [];
46
+ const connIdsSeen = new Set();
47
+ function safeConnId(base) {
48
+ let id = base;
49
+ let counter = 1;
50
+ while (connIdsSeen.has(id)) {
51
+ id = `${base}-${counter++}`;
52
+ }
53
+ connIdsSeen.add(id);
54
+ return id;
55
+ }
56
+ for (const rel of calm.relationships) {
57
+ const rt = rel["relationship-type"];
58
+ if ("composed-of" in rt) {
59
+ const { container, nodes: children } = rt["composed-of"];
60
+ const containerNode = calm.nodes.find((n) => n["unique-id"] === container);
61
+ const label = containerNode?.name ?? container;
62
+ composedOfSets.push(children);
63
+ boundaries.push({ kind: "region", label, wraps: children });
64
+ continue;
65
+ }
66
+ if ("deployed-in" in rt) {
67
+ const { container, nodes: children } = rt["deployed-in"];
68
+ const containerNode = calm.nodes.find((n) => n["unique-id"] === container);
69
+ const label = containerNode?.name ?? container;
70
+ deployedInSets.push(children);
71
+ boundaries.push({ kind: "region", label, wraps: children });
72
+ continue;
73
+ }
74
+ if ("connects" in rt) {
75
+ const { source, destination } = rt.connects;
76
+ connections.push({
77
+ id: safeConnId(rel["unique-id"]),
78
+ from: source.node,
79
+ to: destination.node,
80
+ });
81
+ continue;
82
+ }
83
+ if ("interacts" in rt) {
84
+ const { actor, nodes: targets } = rt.interacts;
85
+ for (const target of targets) {
86
+ connections.push({
87
+ id: safeConnId(`${rel["unique-id"]}-${target}`),
88
+ from: actor,
89
+ to: target,
90
+ });
91
+ }
92
+ }
93
+ }
94
+ const layoutInput = {
95
+ nodeIds,
96
+ composedOfSets,
97
+ deployedInSets,
98
+ };
99
+ const positions = computeLayout(layoutInput);
100
+ const components = calm.nodes.map((node) => {
101
+ const pos = positions.get(node["unique-id"]) ?? { x: 0, y: 0, w: 140, h: 55 };
102
+ return {
103
+ id: node["unique-id"],
104
+ type: mapNodeType(node["node-type"], node.name),
105
+ label: node.name,
106
+ pos: [pos.x, pos.y],
107
+ size: [pos.w, pos.h],
108
+ };
109
+ });
110
+ const subtitle = calm.description ? firstSentence(calm.description) : undefined;
111
+ return {
112
+ schema_version: 1,
113
+ diagram_type: "architecture",
114
+ meta: {
115
+ title: calm.name,
116
+ ...(subtitle && { subtitle }),
117
+ output: path.isAbsolute(outputPath) ? outputPath : outputPath,
118
+ },
119
+ components,
120
+ boundaries,
121
+ connections,
122
+ };
123
+ }
124
+ //# sourceMappingURL=calm-to-archify.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"calm-to-archify.js","sourceRoot":"","sources":["../../../src/cli/lib/calm-to-archify.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ export interface LayoutInput {
2
+ nodeIds: string[];
3
+ composedOfSets: string[][];
4
+ deployedInSets: string[][];
5
+ }
6
+ export interface NodePosition {
7
+ x: number;
8
+ y: number;
9
+ w: number;
10
+ h: number;
11
+ }
12
+ export declare function computeLayout(input: LayoutInput): Map<string, NodePosition>;
13
+ //# sourceMappingURL=layout.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"layout.d.ts","sourceRoot":"","sources":["../../../src/cli/lib/layout.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,cAAc,EAAE,MAAM,EAAE,EAAE,CAAC;IAC3B,cAAc,EAAE,MAAM,EAAE,EAAE,CAAC;CAC5B;AAED,MAAM,WAAW,YAAY;IAC3B,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX;AAuBD,wBAAgB,aAAa,CAAC,KAAK,EAAE,WAAW,GAAG,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAsD3E"}
@@ -0,0 +1,75 @@
1
+ const COMP_W = 240;
2
+ const COMP_H = 55;
3
+ const COL_GAP = 40;
4
+ const ROW_GAP = 40;
5
+ const COLS = 2;
6
+ const SECTION_Y_GAP = 70;
7
+ const EXTERNAL_START_X = 30;
8
+ const EXTERNAL_START_Y = 30;
9
+ function layoutRow(ids, startX, startY, cols) {
10
+ const result = new Map();
11
+ ids.forEach((id, i) => {
12
+ const col = i % cols;
13
+ const row = Math.floor(i / cols);
14
+ result.set(id, {
15
+ x: startX + col * (COMP_W + COL_GAP),
16
+ y: startY + row * (COMP_H + ROW_GAP),
17
+ w: COMP_W,
18
+ h: COMP_H,
19
+ });
20
+ });
21
+ return result;
22
+ }
23
+ // fallow-ignore-next-line complexity
24
+ export function computeLayout(input) {
25
+ const { nodeIds, composedOfSets, deployedInSets } = input;
26
+ // Nodes that appear in composed-of children (deepest level)
27
+ const composedChildren = new Set(composedOfSets.flat());
28
+ // Nodes that appear in deployed-in children (middle level, not already composed children)
29
+ const deployedChildren = new Set(deployedInSets.flat().filter((id) => !composedChildren.has(id)));
30
+ // All nodes that are container targets (the "container" node itself, i.e. the node that wraps others)
31
+ // These are nodes referenced as containers in the relationships — we identify them by exclusion:
32
+ // they appear in the overall node list but are not children themselves
33
+ const allChildIds = new Set([...composedChildren, ...deployedChildren]);
34
+ const external = nodeIds.filter((id) => !allChildIds.has(id));
35
+ const positions = new Map();
36
+ // Row 0: external nodes
37
+ const externalLayout = layoutRow(external, EXTERNAL_START_X, EXTERNAL_START_Y, COLS);
38
+ for (const [id, pos] of externalLayout)
39
+ positions.set(id, pos);
40
+ const externalRows = Math.ceil(external.length / COLS);
41
+ const externalHeight = externalRows * (COMP_H + ROW_GAP);
42
+ let nextY = EXTERNAL_START_Y + externalHeight + SECTION_Y_GAP;
43
+ // Row 1: deployed-only nodes (not composed children), laid out before composed children
44
+ const deployedOnlyList = [...deployedChildren];
45
+ if (deployedOnlyList.length > 0) {
46
+ const deployedLayout = layoutRow(deployedOnlyList, EXTERNAL_START_X, nextY, COLS);
47
+ for (const [id, pos] of deployedLayout)
48
+ positions.set(id, pos);
49
+ const deployedRows = Math.ceil(deployedOnlyList.length / COLS);
50
+ nextY += deployedRows * (COMP_H + ROW_GAP) + SECTION_Y_GAP;
51
+ }
52
+ // Row 2+: composed children, laid out per composed-of group
53
+ const processedComposed = new Set();
54
+ for (const group of composedOfSets) {
55
+ const toLayout = group.filter((id) => !processedComposed.has(id));
56
+ if (toLayout.length === 0)
57
+ continue;
58
+ const groupLayout = layoutRow(toLayout, EXTERNAL_START_X, nextY, COLS);
59
+ groupLayout.forEach((pos, id) => {
60
+ positions.set(id, pos);
61
+ processedComposed.add(id);
62
+ });
63
+ const groupRows = Math.ceil(toLayout.length / COLS);
64
+ nextY += groupRows * (COMP_H + ROW_GAP) + SECTION_Y_GAP;
65
+ }
66
+ // Any remaining nodes not yet positioned
67
+ const remaining = nodeIds.filter((id) => !positions.has(id));
68
+ if (remaining.length > 0) {
69
+ const remainingLayout = layoutRow(remaining, EXTERNAL_START_X, nextY, COLS);
70
+ for (const [id, pos] of remainingLayout)
71
+ positions.set(id, pos);
72
+ }
73
+ return positions;
74
+ }
75
+ //# sourceMappingURL=layout.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"layout.js","sourceRoot":"","sources":["../../../src/cli/lib/layout.ts"],"names":[],"mappings":"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"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
package/dist/cli.js ADDED
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env node
2
+ import { Cli } from "clipanion";
3
+ import { BuildCommand } from "./cli/commands/build.js";
4
+ import { TransformCommand } from "./cli/commands/transform.js";
5
+ const cli = new Cli({
6
+ binaryLabel: "calm-to-archify",
7
+ binaryName: "calm-to-archify",
8
+ binaryVersion: "0.1.0",
9
+ });
10
+ cli.register(BuildCommand);
11
+ cli.register(TransformCommand);
12
+ cli.runExit(process.argv.slice(2));
13
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAChC,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAE/D,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC;IAClB,WAAW,EAAE,iBAAiB;IAC9B,UAAU,EAAE,iBAAiB;IAC7B,aAAa,EAAE,OAAO;CACvB,CAAC,CAAC;AAEH,GAAG,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;AAC3B,GAAG,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAC;AAC/B,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC"}
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "@alistigo/calm-to-archify",
3
+ "version": "0.2.0",
4
+ "license": "MIT",
5
+ "author": {
6
+ "name": "Mikael Labrut",
7
+ "email": "contact@alistigo.com"
8
+ },
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/alistigo/holos.git",
12
+ "directory": "cli/calm-to-archify"
13
+ },
14
+ "private": false,
15
+ "type": "module",
16
+ "description": "Convert CALM architecture JSON to archify HTML diagrams",
17
+ "bin": {
18
+ "calm-to-archify": "./dist/cli.js"
19
+ },
20
+ "files": [
21
+ "dist",
22
+ "README.md"
23
+ ],
24
+ "publishConfig": {
25
+ "access": "public"
26
+ },
27
+ "engines": {
28
+ "node": ">=18"
29
+ },
30
+ "dependencies": {
31
+ "@alistigo/archify-render": "workspace:*",
32
+ "clipanion": "4.0.0-rc.4"
33
+ },
34
+ "devDependencies": {
35
+ "@types/node": "^22.0.0"
36
+ },
37
+ "types": "./cli.d.ts",
38
+ "module": "./cli.js",
39
+ "main": "./cli.js"
40
+ }
package/package.json CHANGED
@@ -1,6 +1,37 @@
1
1
  {
2
2
  "name": "@alistigo/calm-to-archify",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.2.0",
4
+ "license": "MIT",
5
+ "author": {
6
+ "name": "Mikael Labrut",
7
+ "email": "contact@alistigo.com"
8
+ },
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/alistigo/holos.git",
12
+ "directory": "cli/calm-to-archify"
13
+ },
14
+ "private": false,
15
+ "type": "module",
16
+ "description": "Convert CALM architecture JSON to archify HTML diagrams",
17
+ "bin": {
18
+ "calm-to-archify": "./dist/cli.js"
19
+ },
20
+ "files": [
21
+ "dist",
22
+ "README.md"
23
+ ],
24
+ "publishConfig": {
25
+ "access": "public"
26
+ },
27
+ "engines": {
28
+ "node": ">=18"
29
+ },
30
+ "dependencies": {
31
+ "clipanion": "4.0.0-rc.4",
32
+ "@alistigo/archify-render": "0.2.0"
33
+ },
34
+ "devDependencies": {
35
+ "@types/node": "^22.0.0"
36
+ }
6
37
  }