@alfacapital-tech/c4builder 0.4.0 → 0.5.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 +69 -615
- package/dist/cli/commands/check.js +43 -26
- package/dist/cli/commands/help.js +5 -2
- package/dist/cli/commands/jre.js +6 -6
- package/dist/cli/commands/list.js +4 -1
- package/dist/cli/commands/new.js +6 -6
- package/dist/cli/commands/site.js +3 -2
- package/dist/cli/dispatch.js +38 -18
- package/dist/cli/wizard/collect.js +16 -10
- package/dist/config/defaults.js +1 -0
- package/dist/config/options.js +1 -1
- package/dist/config/schema.js +5 -2
- package/dist/core/build.js +15 -6
- package/dist/core/compose/docsify.template.js +0 -1
- package/dist/core/compose/markdown.js +85 -19
- package/dist/core/plugins/hooks.js +1 -1
- package/dist/core/plugins/load.js +6 -6
- package/dist/core/plugins/source.js +6 -6
- package/dist/core/plugins/tree.js +5 -5
- package/dist/core/render/bpmnrenderer.js +522 -0
- package/dist/core/render/d2renderer.js +19 -19
- package/dist/core/render/diagrams.js +64 -45
- package/dist/core/render/jre.js +14 -14
- package/dist/core/render/pngraster.js +4 -4
- package/dist/{plugins/openspec → core/scan}/fences.js +11 -10
- package/dist/core/scan/tree.js +37 -14
- package/dist/plugins/openapi/index.js +17 -7
- package/dist/plugins/openspec/index.js +1 -1
- package/dist/plugins/openspec/render.js +5 -2
- package/dist/util/archive.js +1 -1
- package/dist/util/http.js +3 -3
- package/dist/util/lock.js +3 -3
- package/dist/util/paths.js +1 -1
- package/package.json +9 -4
- package/template/AGENTS.md +52 -0
- package/template/readme.md +3 -3
- package/template/src/5 BPMN Example/account-opening.bpmn +69 -0
- package/template/src/5 BPMN Example/account-opening.md +7 -0
- package/dist/util/glob.js +0 -69
package/dist/util/lock.js
CHANGED
|
@@ -48,11 +48,11 @@ export const acquireBuildLock = (lockPath) => {
|
|
|
48
48
|
const alive = typeof meta.pid === 'number' && ownerAlive(meta.pid);
|
|
49
49
|
const fresh = typeof meta.at === 'number' && Date.now() - meta.at < STALE_MS;
|
|
50
50
|
if (alive && fresh) {
|
|
51
|
-
throw new BuildLockHeldError(
|
|
52
|
-
|
|
51
|
+
throw new BuildLockHeldError(`a build is already running in this folder (pid ${meta.pid}) — wait for it to finish ` +
|
|
52
|
+
`or delete ${lockPath} if it is left over from a crashed process`);
|
|
53
53
|
}
|
|
54
54
|
fs.rmSync(lockPath, { force: true });
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
|
-
throw new BuildLockHeldError(
|
|
57
|
+
throw new BuildLockHeldError(`failed to acquire the build lock: ${lockPath}`);
|
|
58
58
|
};
|
package/dist/util/paths.js
CHANGED
|
@@ -13,7 +13,7 @@ const findPackageRoot = (startDir) => {
|
|
|
13
13
|
return dir;
|
|
14
14
|
const parent = path.dirname(dir);
|
|
15
15
|
if (parent === dir)
|
|
16
|
-
throw new Error(`package.json
|
|
16
|
+
throw new Error(`package.json not found above ${startDir}`);
|
|
17
17
|
dir = parent;
|
|
18
18
|
}
|
|
19
19
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alfacapital-tech/c4builder",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "A CLI tool designed to compile a folder structure of markdowns and plant uml files into a site, single file markdown or a collection of markdowns with links",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -17,9 +17,9 @@
|
|
|
17
17
|
"license"
|
|
18
18
|
],
|
|
19
19
|
"engines": {
|
|
20
|
-
"node": ">=
|
|
20
|
+
"node": ">=22.17"
|
|
21
21
|
},
|
|
22
|
-
"homepage": "https://github.
|
|
22
|
+
"homepage": "https://alfacapital-tech.github.io/C4-Builder/",
|
|
23
23
|
"bugs": {
|
|
24
24
|
"url": "https://github.com/AlfaCapital-Tech/C4-Builder/issues"
|
|
25
25
|
},
|
|
@@ -80,6 +80,11 @@
|
|
|
80
80
|
"vitest": "^4.1.9"
|
|
81
81
|
},
|
|
82
82
|
"optionalDependencies": {
|
|
83
|
-
"@terrastruct/d2": "0.1.33"
|
|
83
|
+
"@terrastruct/d2": "0.1.33",
|
|
84
|
+
"bpmn-auto-layout": "2.0.0-alpha.2",
|
|
85
|
+
"bpmn-js": "18.31.0",
|
|
86
|
+
"bpmn-moddle": "10.3.1",
|
|
87
|
+
"bpmnlint": "11.14.0",
|
|
88
|
+
"jsdom": "29.1.1"
|
|
84
89
|
}
|
|
85
90
|
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
Architecture documentation built with [c4builder](https://github.com/AlfaCapital-Tech/C4-Builder):
|
|
4
|
+
C4 diagrams (PlantUML, D2), BPMN processes and markdown in `src/` are compiled into `docs/`.
|
|
5
|
+
|
|
6
|
+
## Commands
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
c4builder # build docs/ (non-interactive with a complete .c4builder)
|
|
10
|
+
c4builder check <file...> # validate .puml / .iuml / .d2 / .bpmn files, exit code 0 = OK
|
|
11
|
+
c4builder --site -w # live preview on http://localhost:3000 - never exits
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Not installed: `npm i -g @alfacapital-tech/c4builder` (Node.js 22.17+; Java 17+ is used
|
|
15
|
+
when present, otherwise c4builder downloads a private JRE).
|
|
16
|
+
|
|
17
|
+
## Workflow
|
|
18
|
+
|
|
19
|
+
1. Edit files in `src/` only — `docs/` is generated and wiped on every build.
|
|
20
|
+
2. Run `c4builder check` on every diagram you changed, `src/styles.iuml` included. Errors
|
|
21
|
+
look like `✗ <file>: line <N>: <message>` (BPMN: `✗ <file>: <element id> [<rule>] <message>`).
|
|
22
|
+
Fix until the exit code is 0.
|
|
23
|
+
3. Run `c4builder` and make sure it exits with code 0. Leave `c4builder --site -w` to a
|
|
24
|
+
human or run it in the background.
|
|
25
|
+
|
|
26
|
+
## Layout
|
|
27
|
+
|
|
28
|
+
- One folder = one page. All `.md` files of a folder form its text; every `.puml` / `.d2` /
|
|
29
|
+
`.bpmn` in it is rendered onto the page, or placed inline with ``.
|
|
30
|
+
- Files and folders starting with `_` are not published (shared libraries, drafts).
|
|
31
|
+
- `.c4builder` is the config (JSON); unknown keys are silently ignored.
|
|
32
|
+
|
|
33
|
+
## Diagram conventions
|
|
34
|
+
|
|
35
|
+
- C4 comes from the PlantUML stdlib — `!include <C4/C4_Context>` (`C4_Container`,
|
|
36
|
+
`C4_Component`, `C4_Dynamic`, `C4_Deployment`). It is bundled, rendering is offline;
|
|
37
|
+
never include C4 by URL.
|
|
38
|
+
- Shared styles live in `src/styles.iuml`, included after the C4 include by a relative
|
|
39
|
+
path: `!include styles.iuml` in `src/`, `!include ../styles.iuml` one level down.
|
|
40
|
+
- D2 diagrams share classes through `src/_c4lib.d2` (`...@../_c4lib`).
|
|
41
|
+
- BPMN: write only the semantic BPMN 2.0 XML, never `bpmndi` coordinates — the build lays the
|
|
42
|
+
diagram out. Pool = organization, lane = role inside it, only message flows between pools,
|
|
43
|
+
label gateway branches. Example: `src/5 BPMN Example/account-opening.bpmn`.
|
|
44
|
+
|
|
45
|
+
## Agent skills
|
|
46
|
+
|
|
47
|
+
Detailed guidance (project layout, config keys, plugins, installation) as skills for
|
|
48
|
+
Claude Code, Codex, Cursor and other agents:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
npx skills add AlfaCapital-Tech/C4-Builder/skills
|
|
52
|
+
```
|
package/template/readme.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Introduction
|
|
2
2
|
|
|
3
|
-
This project was created using [c4builder](https://
|
|
3
|
+
This project was created using [c4builder](https://alfacapital-tech.github.io/C4-Builder/)
|
|
4
4
|
|
|
5
5
|
Take a look at
|
|
6
6
|
|
|
@@ -12,12 +12,12 @@ Take a look at
|
|
|
12
12
|
|
|
13
13
|
- [C4-PlantUML](https://github.com/RicardoNiepel/C4-PlantUML) C4 syntax support for generating plantuml diagrams
|
|
14
14
|
|
|
15
|
-
-
|
|
15
|
+
- `c4builder --site -w` live preview: rebuilds on save and reloads the browser; `c4builder check <file>` validates a single diagram
|
|
16
16
|
|
|
17
17
|
Open the terminal and run the following commands to start compiling the documentation
|
|
18
18
|
|
|
19
19
|
```bash
|
|
20
|
-
npm i -g c4builder
|
|
20
|
+
npm i -g @alfacapital-tech/c4builder
|
|
21
21
|
c4builder
|
|
22
22
|
```
|
|
23
23
|
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<!-- Только семантическая часть BPMN: координат (bpmndi) нет, раскладку делает c4builder. -->
|
|
3
|
+
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL"
|
|
4
|
+
id="Definitions_AccountOpening"
|
|
5
|
+
targetNamespace="https://example.com/bpmn/account-opening">
|
|
6
|
+
<bpmn:collaboration id="Collaboration_AccountOpening">
|
|
7
|
+
<bpmn:participant id="Pool_Bank" name="Банк" processRef="Process_AccountOpening" />
|
|
8
|
+
<bpmn:participant id="Pool_CreditBureau" name="Бюро кредитных историй" />
|
|
9
|
+
<bpmn:messageFlow id="Message_HistoryRequest" name="Запрос истории"
|
|
10
|
+
sourceRef="Task_RequestHistory" targetRef="Pool_CreditBureau" />
|
|
11
|
+
<bpmn:messageFlow id="Message_CreditReport" name="Кредитный отчёт"
|
|
12
|
+
sourceRef="Pool_CreditBureau" targetRef="Task_ReceiveReport" />
|
|
13
|
+
</bpmn:collaboration>
|
|
14
|
+
|
|
15
|
+
<bpmn:process id="Process_AccountOpening" name="Открытие счёта" isExecutable="false">
|
|
16
|
+
<bpmn:laneSet id="LaneSet_Bank">
|
|
17
|
+
<bpmn:lane id="Lane_Client" name="Клиент">
|
|
18
|
+
<bpmn:flowNodeRef>Start_AccountNeeded</bpmn:flowNodeRef>
|
|
19
|
+
<bpmn:flowNodeRef>Task_SubmitApplication</bpmn:flowNodeRef>
|
|
20
|
+
<bpmn:flowNodeRef>End_AccountOpened</bpmn:flowNodeRef>
|
|
21
|
+
<bpmn:flowNodeRef>End_Rejected</bpmn:flowNodeRef>
|
|
22
|
+
</bpmn:lane>
|
|
23
|
+
<bpmn:lane id="Lane_FrontOffice" name="Фронт-офис">
|
|
24
|
+
<bpmn:flowNodeRef>Task_CheckDocuments</bpmn:flowNodeRef>
|
|
25
|
+
<bpmn:flowNodeRef>Task_RequestHistory</bpmn:flowNodeRef>
|
|
26
|
+
<bpmn:flowNodeRef>Task_ReceiveReport</bpmn:flowNodeRef>
|
|
27
|
+
</bpmn:lane>
|
|
28
|
+
<bpmn:lane id="Lane_BackOffice" name="Бэк-офис">
|
|
29
|
+
<bpmn:flowNodeRef>Task_Assess</bpmn:flowNodeRef>
|
|
30
|
+
<bpmn:flowNodeRef>Timer_AssessOverdue</bpmn:flowNodeRef>
|
|
31
|
+
<bpmn:flowNodeRef>Task_Escalate</bpmn:flowNodeRef>
|
|
32
|
+
<bpmn:flowNodeRef>End_Escalated</bpmn:flowNodeRef>
|
|
33
|
+
<bpmn:flowNodeRef>Gateway_Decision</bpmn:flowNodeRef>
|
|
34
|
+
<bpmn:flowNodeRef>Task_OpenAccount</bpmn:flowNodeRef>
|
|
35
|
+
<bpmn:flowNodeRef>Task_PrepareRejection</bpmn:flowNodeRef>
|
|
36
|
+
</bpmn:lane>
|
|
37
|
+
</bpmn:laneSet>
|
|
38
|
+
|
|
39
|
+
<bpmn:startEvent id="Start_AccountNeeded" name="Нужен счёт" />
|
|
40
|
+
<bpmn:userTask id="Task_SubmitApplication" name="Подать заявку" />
|
|
41
|
+
<bpmn:userTask id="Task_CheckDocuments" name="Проверить документы" />
|
|
42
|
+
<bpmn:sendTask id="Task_RequestHistory" name="Запросить кредитную историю" />
|
|
43
|
+
<bpmn:receiveTask id="Task_ReceiveReport" name="Получить кредитный отчёт" />
|
|
44
|
+
<bpmn:userTask id="Task_Assess" name="Оценить заявку" />
|
|
45
|
+
<bpmn:boundaryEvent id="Timer_AssessOverdue" name="3 рабочих дня" attachedToRef="Task_Assess">
|
|
46
|
+
<bpmn:timerEventDefinition id="TimerDef_AssessOverdue" />
|
|
47
|
+
</bpmn:boundaryEvent>
|
|
48
|
+
<bpmn:task id="Task_Escalate" name="Эскалировать руководителю" />
|
|
49
|
+
<bpmn:endEvent id="End_Escalated" name="Заявка у руководителя" />
|
|
50
|
+
<bpmn:exclusiveGateway id="Gateway_Decision" name="Одобрить?" />
|
|
51
|
+
<bpmn:serviceTask id="Task_OpenAccount" name="Открыть счёт" />
|
|
52
|
+
<bpmn:task id="Task_PrepareRejection" name="Подготовить отказ" />
|
|
53
|
+
<bpmn:endEvent id="End_AccountOpened" name="Счёт открыт" />
|
|
54
|
+
<bpmn:endEvent id="End_Rejected" name="Получен отказ" />
|
|
55
|
+
|
|
56
|
+
<bpmn:sequenceFlow id="Flow_Start" sourceRef="Start_AccountNeeded" targetRef="Task_SubmitApplication" />
|
|
57
|
+
<bpmn:sequenceFlow id="Flow_Submitted" sourceRef="Task_SubmitApplication" targetRef="Task_CheckDocuments" />
|
|
58
|
+
<bpmn:sequenceFlow id="Flow_Checked" sourceRef="Task_CheckDocuments" targetRef="Task_RequestHistory" />
|
|
59
|
+
<bpmn:sequenceFlow id="Flow_Requested" sourceRef="Task_RequestHistory" targetRef="Task_ReceiveReport" />
|
|
60
|
+
<bpmn:sequenceFlow id="Flow_Received" sourceRef="Task_ReceiveReport" targetRef="Task_Assess" />
|
|
61
|
+
<bpmn:sequenceFlow id="Flow_Assessed" sourceRef="Task_Assess" targetRef="Gateway_Decision" />
|
|
62
|
+
<bpmn:sequenceFlow id="Flow_Approved" name="Да" sourceRef="Gateway_Decision" targetRef="Task_OpenAccount" />
|
|
63
|
+
<bpmn:sequenceFlow id="Flow_Declined" name="Нет" sourceRef="Gateway_Decision" targetRef="Task_PrepareRejection" />
|
|
64
|
+
<bpmn:sequenceFlow id="Flow_Opened" sourceRef="Task_OpenAccount" targetRef="End_AccountOpened" />
|
|
65
|
+
<bpmn:sequenceFlow id="Flow_Rejected" sourceRef="Task_PrepareRejection" targetRef="End_Rejected" />
|
|
66
|
+
<bpmn:sequenceFlow id="Flow_Overdue" sourceRef="Timer_AssessOverdue" targetRef="Task_Escalate" />
|
|
67
|
+
<bpmn:sequenceFlow id="Flow_Escalated" sourceRef="Task_Escalate" targetRef="End_Escalated" />
|
|
68
|
+
</bpmn:process>
|
|
69
|
+
</bpmn:definitions>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
**Бизнес-процесс в нотации BPMN (третий бэкенд рендера)**
|
|
2
|
+
|
|
3
|
+
Эта страница демонстрирует BPMN-диаграммы: процесс «Открытие счёта» с пулом «Банк» и его дорожками «Клиент», «Фронт-офис», «Бэк-офис», внешним пулом «Бюро кредитных историй», шлюзом с подписанными ветками, граничным таймером и message flow между пулами.
|
|
4
|
+
|
|
5
|
+
Файл `.bpmn` содержит только семантическую часть BPMN 2.0 XML: пулы, дорожки, события, задачи, шлюзы и потоки. Координат (секции `bpmndi`) в нём нет — раскладку делает сборка: пулы и дорожки горизонтальными полосами, поток слева направо. Если координаты в файле есть, они игнорируются. Перед раскладкой модель проверяется (несвязанные узлы, узел вне дорожки, sequence flow между пулами и т.п.), ошибки ссылаются на id элемента. Отдельный файл проверяется командой `c4builder check account-opening.bpmn`.
|
|
6
|
+
|
|
7
|
+
**Область применения**: показать, что `.bpmn`-диаграммы, их раскладка и кириллица корректно попадают в собранный сайт, SVG и PNG без обращений в интернет.
|
package/dist/util/glob.js
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
// Мини-glob без зависимостей: `**` (любые каталоги), `*` (в пределах сегмента),
|
|
2
|
-
// `?`, `{a,b}`; остальное — буквально. Покрывает `*/openapi.yaml` и
|
|
3
|
-
// `**/openapi.{yaml,yml,json}`; `fs.glob` появился только в Node 22, engines — ≥20.19.
|
|
4
|
-
import fs from 'node:fs';
|
|
5
|
-
import path from 'node:path';
|
|
6
|
-
const convert = (glob) => {
|
|
7
|
-
let re = '';
|
|
8
|
-
for (let i = 0; i < glob.length; i++) {
|
|
9
|
-
const c = glob[i];
|
|
10
|
-
if (c === '*') {
|
|
11
|
-
if (glob[i + 1] === '*') {
|
|
12
|
-
// `**/` — ноль и более каталогов; одиночное `**` в конце — что угодно
|
|
13
|
-
i++;
|
|
14
|
-
if (glob[i + 1] === '/') {
|
|
15
|
-
i++;
|
|
16
|
-
re += '(?:.*/)?';
|
|
17
|
-
}
|
|
18
|
-
else
|
|
19
|
-
re += '.*';
|
|
20
|
-
}
|
|
21
|
-
else
|
|
22
|
-
re += '[^/]*';
|
|
23
|
-
}
|
|
24
|
-
else if (c === '?')
|
|
25
|
-
re += '[^/]';
|
|
26
|
-
else if (c === '{') {
|
|
27
|
-
const end = glob.indexOf('}', i);
|
|
28
|
-
if (end === -1)
|
|
29
|
-
re += '\\{';
|
|
30
|
-
else {
|
|
31
|
-
// Альтернативы — тем же конвертером: `{*.yaml,*.yml}` должен работать
|
|
32
|
-
// как glob, а не как буквальная звёздочка.
|
|
33
|
-
re += `(?:${glob
|
|
34
|
-
.slice(i + 1, end)
|
|
35
|
-
.split(',')
|
|
36
|
-
.map(convert)
|
|
37
|
-
.join('|')})`;
|
|
38
|
-
i = end;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
else
|
|
42
|
-
re += c.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
43
|
-
}
|
|
44
|
-
return re;
|
|
45
|
-
};
|
|
46
|
-
export const globToRegExp = (glob) => new RegExp(`^${convert(glob)}$`);
|
|
47
|
-
/**
|
|
48
|
-
* Файлы под root, чьи относительные posix-пути совпадают с glob (сортировано).
|
|
49
|
-
* `.git`/`node_modules` и абсолютные каталоги `skipDirs` не обходятся.
|
|
50
|
-
*/
|
|
51
|
-
export const globFiles = (root, glob, skipDirs = []) => {
|
|
52
|
-
const re = globToRegExp(glob);
|
|
53
|
-
const skip = new Set(skipDirs.map((d) => path.resolve(d)));
|
|
54
|
-
const out = [];
|
|
55
|
-
const walk = (rel) => {
|
|
56
|
-
for (const e of fs.readdirSync(path.join(root, rel), { withFileTypes: true })) {
|
|
57
|
-
const r = rel ? `${rel}/${e.name}` : e.name;
|
|
58
|
-
if (e.isDirectory()) {
|
|
59
|
-
if (e.name === '.git' || e.name === 'node_modules' || skip.has(path.resolve(root, r)))
|
|
60
|
-
continue;
|
|
61
|
-
walk(r);
|
|
62
|
-
}
|
|
63
|
-
else if (re.test(r))
|
|
64
|
-
out.push(r);
|
|
65
|
-
}
|
|
66
|
-
};
|
|
67
|
-
walk('');
|
|
68
|
-
return out.sort();
|
|
69
|
-
};
|