@mandujs/cli 0.9.22 → 0.9.23
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.ko.md +57 -4
- package/README.md +47 -15
- package/package.json +1 -1
- package/src/commands/check.ts +41 -5
- package/src/commands/contract.ts +135 -9
- package/src/commands/dev.ts +155 -95
- package/src/commands/guard-arch.ts +39 -9
- package/src/commands/guard-check.ts +3 -3
- package/src/commands/init.ts +264 -9
- package/src/commands/monitor.ts +21 -0
- package/src/main.ts +386 -344
- package/templates/default/app/globals.css +37 -0
- package/templates/default/app/layout.tsx +27 -0
- package/templates/default/app/page.tsx +27 -49
- package/templates/default/package.json +15 -6
- package/templates/default/postcss.config.js +6 -0
- package/templates/default/src/client/app/index.ts +1 -0
- package/templates/default/src/client/entities/index.ts +1 -0
- package/templates/default/src/client/features/index.ts +1 -0
- package/templates/default/src/client/pages/index.ts +1 -0
- package/templates/default/src/client/shared/index.ts +1 -0
- package/templates/default/src/client/shared/lib/utils.ts +16 -0
- package/templates/default/src/client/shared/ui/button.tsx +57 -0
- package/templates/default/src/client/shared/ui/card.tsx +78 -0
- package/templates/default/src/client/shared/ui/index.ts +21 -0
- package/templates/default/src/client/shared/ui/input.tsx +24 -0
- package/templates/default/src/client/widgets/index.ts +1 -0
- package/templates/default/src/server/api/index.ts +1 -0
- package/templates/default/src/server/application/index.ts +1 -0
- package/templates/default/src/server/core/index.ts +1 -0
- package/templates/default/src/server/domain/index.ts +1 -0
- package/templates/default/src/server/infra/index.ts +1 -0
- package/templates/default/src/shared/contracts/index.ts +1 -0
- package/templates/default/src/shared/env/index.ts +1 -0
- package/templates/default/src/shared/schema/index.ts +1 -0
- package/templates/default/src/shared/types/index.ts +1 -0
- package/templates/default/src/shared/utils/client/index.ts +1 -0
- package/templates/default/src/shared/utils/server/index.ts +1 -0
- package/templates/default/tailwind.config.ts +64 -0
- package/templates/default/tsconfig.json +14 -3
package/README.ko.md
CHANGED
|
@@ -44,12 +44,35 @@ bunx @mandujs/cli init my-app
|
|
|
44
44
|
생성되는 구조:
|
|
45
45
|
```
|
|
46
46
|
my-app/
|
|
47
|
+
├── app/ # FS Routes
|
|
48
|
+
│ └── page.tsx # /
|
|
49
|
+
├── src/ # 아키텍처 레이어
|
|
50
|
+
│ ├── client/ # 클라이언트 (FSD)
|
|
51
|
+
│ │ ├── app/
|
|
52
|
+
│ │ ├── pages/
|
|
53
|
+
│ │ ├── widgets/
|
|
54
|
+
│ │ ├── features/
|
|
55
|
+
│ │ ├── entities/
|
|
56
|
+
│ │ └── shared/
|
|
57
|
+
│ ├── server/ # 서버 (Clean)
|
|
58
|
+
│ │ ├── api/
|
|
59
|
+
│ │ ├── application/
|
|
60
|
+
│ │ ├── domain/
|
|
61
|
+
│ │ ├── infra/
|
|
62
|
+
│ │ └── core/
|
|
63
|
+
│ └── shared/ # 공용
|
|
64
|
+
│ ├── contracts/ # client-safe 계약
|
|
65
|
+
│ ├── types/
|
|
66
|
+
│ ├── utils/
|
|
67
|
+
│ │ ├── client/ # 클라이언트 safe 유틸
|
|
68
|
+
│ │ └── server/ # 서버 전용 유틸
|
|
69
|
+
│ ├── schema/ # 서버 전용 스키마
|
|
70
|
+
│ └── env/ # 서버 전용 환경
|
|
47
71
|
├── apps/
|
|
48
72
|
│ ├── server/main.ts # 서버 진입점
|
|
49
73
|
│ └── web/entry.tsx # 클라이언트 진입점
|
|
50
74
|
├── spec/
|
|
51
75
|
│ └── routes.manifest.json # SSOT - 라우트 정의
|
|
52
|
-
├── tests/ # 테스트 템플릿
|
|
53
76
|
├── package.json
|
|
54
77
|
└── tsconfig.json
|
|
55
78
|
```
|
|
@@ -82,20 +105,50 @@ bun run generate
|
|
|
82
105
|
|
|
83
106
|
### `mandu guard`
|
|
84
107
|
|
|
85
|
-
아키텍처 규칙을
|
|
108
|
+
아키텍처 규칙을 검사합니다. (기본: mandu 프리셋)
|
|
86
109
|
|
|
87
110
|
```bash
|
|
88
111
|
bun run guard
|
|
89
112
|
|
|
113
|
+
# 프리셋 변경
|
|
114
|
+
bunx mandu guard --preset fsd
|
|
115
|
+
|
|
116
|
+
# CI 모드 (warning도 실패 처리)
|
|
117
|
+
bunx mandu guard --ci
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### `mandu guard legacy`
|
|
121
|
+
|
|
122
|
+
레거시 Spec Guard 검사 + 자동 수정입니다.
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
bunx mandu guard legacy
|
|
126
|
+
|
|
90
127
|
# 자동 수정 비활성화
|
|
91
|
-
bunx mandu guard --no-auto-correct
|
|
128
|
+
bunx mandu guard legacy --no-auto-correct
|
|
92
129
|
```
|
|
93
130
|
|
|
94
|
-
자동 수정 가능한
|
|
131
|
+
자동 수정 가능한 규칙(legacy):
|
|
95
132
|
- `SPEC_HASH_MISMATCH` → lock 파일 갱신
|
|
96
133
|
- `GENERATED_MANUAL_EDIT` → 코드 재생성
|
|
97
134
|
- `SLOT_NOT_FOUND` → slot 파일 생성
|
|
98
135
|
|
|
136
|
+
### `mandu contract build`
|
|
137
|
+
|
|
138
|
+
계약 레지스트리(`.mandu/contracts.json`)를 생성합니다.
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
bunx mandu contract build
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### `mandu contract diff`
|
|
145
|
+
|
|
146
|
+
계약 변경사항(major/minor/patch)을 비교합니다.
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
bunx mandu contract diff
|
|
150
|
+
```
|
|
151
|
+
|
|
99
152
|
## Spec 파일 작성
|
|
100
153
|
|
|
101
154
|
`spec/routes.manifest.json`이 모든 라우트의 단일 진실 공급원(SSOT)입니다.
|
package/README.md
CHANGED
|
@@ -61,6 +61,34 @@ app/
|
|
|
61
61
|
bunx mandu build
|
|
62
62
|
```
|
|
63
63
|
|
|
64
|
+
### Default Architecture Layout
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
app/ # FS Routes
|
|
68
|
+
src/
|
|
69
|
+
client/ # Client (FSD)
|
|
70
|
+
app/
|
|
71
|
+
pages/
|
|
72
|
+
widgets/
|
|
73
|
+
features/
|
|
74
|
+
entities/
|
|
75
|
+
shared/
|
|
76
|
+
server/ # Server (Clean)
|
|
77
|
+
api/
|
|
78
|
+
application/
|
|
79
|
+
domain/
|
|
80
|
+
infra/
|
|
81
|
+
core/
|
|
82
|
+
shared/ # Universal shared
|
|
83
|
+
contracts/ # Client-safe contracts
|
|
84
|
+
types/
|
|
85
|
+
utils/
|
|
86
|
+
client/ # Client-safe utils
|
|
87
|
+
server/ # Server-only utils
|
|
88
|
+
schema/ # Server-only schema
|
|
89
|
+
env/ # Server-only env
|
|
90
|
+
```
|
|
91
|
+
|
|
64
92
|
That's it!
|
|
65
93
|
|
|
66
94
|
---
|
|
@@ -87,11 +115,12 @@ That's it!
|
|
|
87
115
|
|
|
88
116
|
| Command | Description |
|
|
89
117
|
|---------|-------------|
|
|
90
|
-
| `mandu guard
|
|
91
|
-
| `mandu guard
|
|
92
|
-
| `mandu guard
|
|
93
|
-
| `mandu guard
|
|
94
|
-
| `mandu guard
|
|
118
|
+
| `mandu guard` | Run architecture check (default: mandu preset) |
|
|
119
|
+
| `mandu guard --watch` | Watch mode |
|
|
120
|
+
| `mandu guard --ci` | CI mode (exit 1 on errors/warnings) |
|
|
121
|
+
| `mandu guard --preset fsd` | Use specific preset |
|
|
122
|
+
| `mandu guard --output report.md` | Generate report |
|
|
123
|
+
| `mandu guard legacy` | Legacy Spec guard (auto-correct) |
|
|
95
124
|
|
|
96
125
|
### Transaction Commands
|
|
97
126
|
|
|
@@ -119,6 +148,8 @@ That's it!
|
|
|
119
148
|
|---------|-------------|
|
|
120
149
|
| `mandu contract create <routeId>` | Create contract for route |
|
|
121
150
|
| `mandu contract validate` | Validate contract-slot consistency |
|
|
151
|
+
| `mandu contract build` | Build contract registry |
|
|
152
|
+
| `mandu contract diff` | Diff contracts against registry |
|
|
122
153
|
| `mandu openapi generate` | Generate OpenAPI 3.0 spec |
|
|
123
154
|
| `mandu openapi serve` | Start Swagger UI server |
|
|
124
155
|
|
|
@@ -149,7 +180,7 @@ bun run dev
|
|
|
149
180
|
bunx mandu dev --guard
|
|
150
181
|
|
|
151
182
|
# Or run Guard separately
|
|
152
|
-
bunx mandu guard
|
|
183
|
+
bunx mandu guard --watch
|
|
153
184
|
```
|
|
154
185
|
|
|
155
186
|
### CI/CD Integration
|
|
@@ -157,7 +188,7 @@ bunx mandu guard arch --watch
|
|
|
157
188
|
```bash
|
|
158
189
|
# Build and check
|
|
159
190
|
bunx mandu build --minify
|
|
160
|
-
bunx mandu guard
|
|
191
|
+
bunx mandu guard --ci --format json
|
|
161
192
|
```
|
|
162
193
|
|
|
163
194
|
---
|
|
@@ -209,10 +240,10 @@ app/
|
|
|
209
240
|
|
|
210
241
|
```bash
|
|
211
242
|
# List all presets
|
|
212
|
-
bunx mandu guard
|
|
243
|
+
bunx mandu guard --list-presets
|
|
213
244
|
|
|
214
245
|
# Use specific preset
|
|
215
|
-
bunx mandu guard
|
|
246
|
+
bunx mandu guard --preset fsd
|
|
216
247
|
```
|
|
217
248
|
|
|
218
249
|
---
|
|
@@ -235,13 +266,13 @@ bunx mandu guard arch --preset fsd
|
|
|
235
266
|
| `--sourcemap` | Generate sourcemaps |
|
|
236
267
|
| `--watch` | Watch mode |
|
|
237
268
|
|
|
238
|
-
### `mandu guard
|
|
269
|
+
### `mandu guard`
|
|
239
270
|
|
|
240
271
|
| Option | Description |
|
|
241
272
|
|--------|-------------|
|
|
242
273
|
| `--preset <p>` | Preset: fsd, clean, hexagonal, atomic, mandu |
|
|
243
274
|
| `--watch` | Watch mode |
|
|
244
|
-
| `--ci` | CI mode (exit 1 on errors) |
|
|
275
|
+
| `--ci` | CI mode (exit 1 on errors/warnings) |
|
|
245
276
|
| `--quiet` | Summary only |
|
|
246
277
|
| `--format <f>` | Output: console, agent, json |
|
|
247
278
|
| `--output <path>` | Report file path |
|
|
@@ -284,10 +315,11 @@ bunx mandu routes list
|
|
|
284
315
|
bunx mandu routes generate
|
|
285
316
|
|
|
286
317
|
# Guard
|
|
287
|
-
bunx mandu guard
|
|
288
|
-
bunx mandu guard
|
|
289
|
-
bunx mandu guard
|
|
290
|
-
bunx mandu guard
|
|
318
|
+
bunx mandu guard
|
|
319
|
+
bunx mandu guard --watch
|
|
320
|
+
bunx mandu guard --ci --format json
|
|
321
|
+
bunx mandu guard --output report.md
|
|
322
|
+
bunx mandu guard legacy
|
|
291
323
|
|
|
292
324
|
# Transactions
|
|
293
325
|
bunx mandu change begin --message "Add users API"
|
package/package.json
CHANGED
package/src/commands/check.ts
CHANGED
|
@@ -34,6 +34,7 @@ export async function check(options: CheckOptions = {}): Promise<boolean> {
|
|
|
34
34
|
const preset = options.preset ?? "mandu";
|
|
35
35
|
const format = resolveOutputFormat(options.format);
|
|
36
36
|
const quiet = options.quiet === true;
|
|
37
|
+
const strictWarnings = options.ci === true;
|
|
37
38
|
const enableFsRoutes = !options.legacy && await isDirectory(path.resolve(rootDir, "app"));
|
|
38
39
|
const specPath = resolveFromCwd("spec/routes.manifest.json");
|
|
39
40
|
const hasSpec = await pathExists(specPath);
|
|
@@ -122,14 +123,45 @@ export async function check(options: CheckOptions = {}): Promise<boolean> {
|
|
|
122
123
|
fsRoutes: enableFsRoutes
|
|
123
124
|
? {
|
|
124
125
|
noPageToPage: true,
|
|
125
|
-
pageCanImport: [
|
|
126
|
-
|
|
126
|
+
pageCanImport: [
|
|
127
|
+
"client/pages",
|
|
128
|
+
"client/widgets",
|
|
129
|
+
"client/features",
|
|
130
|
+
"client/entities",
|
|
131
|
+
"client/shared",
|
|
132
|
+
"shared/contracts",
|
|
133
|
+
"shared/types",
|
|
134
|
+
"shared/utils/client",
|
|
135
|
+
],
|
|
136
|
+
layoutCanImport: [
|
|
137
|
+
"client/app",
|
|
138
|
+
"client/widgets",
|
|
139
|
+
"client/shared",
|
|
140
|
+
"shared/contracts",
|
|
141
|
+
"shared/types",
|
|
142
|
+
"shared/utils/client",
|
|
143
|
+
],
|
|
144
|
+
routeCanImport: [
|
|
145
|
+
"server/api",
|
|
146
|
+
"server/application",
|
|
147
|
+
"server/domain",
|
|
148
|
+
"server/infra",
|
|
149
|
+
"server/core",
|
|
150
|
+
"shared/contracts",
|
|
151
|
+
"shared/schema",
|
|
152
|
+
"shared/types",
|
|
153
|
+
"shared/utils/client",
|
|
154
|
+
"shared/utils/server",
|
|
155
|
+
"shared/env",
|
|
156
|
+
],
|
|
127
157
|
}
|
|
128
158
|
: undefined,
|
|
129
159
|
};
|
|
130
160
|
|
|
131
161
|
const report = await checkDirectory(guardConfig, rootDir);
|
|
132
|
-
|
|
162
|
+
const hasArchErrors = report.bySeverity.error > 0;
|
|
163
|
+
const hasArchWarnings = report.bySeverity.warn > 0;
|
|
164
|
+
if (hasArchErrors || (strictWarnings && hasArchWarnings)) {
|
|
133
165
|
success = false;
|
|
134
166
|
}
|
|
135
167
|
|
|
@@ -166,7 +198,11 @@ export async function check(options: CheckOptions = {}): Promise<boolean> {
|
|
|
166
198
|
const checkResult = await runGuardCheck(manifestResult.data, rootDir);
|
|
167
199
|
legacySummary.passed = checkResult.passed;
|
|
168
200
|
legacySummary.violations = checkResult.violations.length;
|
|
169
|
-
|
|
201
|
+
if (strictWarnings && checkResult.violations.length > 0) {
|
|
202
|
+
success = false;
|
|
203
|
+
} else {
|
|
204
|
+
success = success && checkResult.passed;
|
|
205
|
+
}
|
|
170
206
|
|
|
171
207
|
if (format === "console") {
|
|
172
208
|
const legacyReport = buildGuardReport(checkResult);
|
|
@@ -200,5 +236,5 @@ export async function check(options: CheckOptions = {}): Promise<boolean> {
|
|
|
200
236
|
console.log(JSON.stringify(summary, null, 2));
|
|
201
237
|
}
|
|
202
238
|
|
|
203
|
-
return
|
|
239
|
+
return success;
|
|
204
240
|
}
|
package/src/commands/contract.ts
CHANGED
|
@@ -3,17 +3,36 @@
|
|
|
3
3
|
* Contract 생성 및 검증 명령어
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
import {
|
|
7
|
+
loadManifest,
|
|
8
|
+
runContractGuardCheck,
|
|
9
|
+
generateContractTemplate,
|
|
10
|
+
buildContractRegistry,
|
|
11
|
+
writeContractRegistry,
|
|
12
|
+
readContractRegistry,
|
|
13
|
+
diffContractRegistry,
|
|
14
|
+
} from "@mandujs/core";
|
|
15
|
+
import path from "path";
|
|
16
|
+
import fs from "fs/promises";
|
|
9
17
|
|
|
10
18
|
interface ContractCreateOptions {
|
|
11
19
|
routeId: string;
|
|
12
20
|
}
|
|
13
21
|
|
|
14
|
-
interface ContractValidateOptions {
|
|
15
|
-
verbose?: boolean;
|
|
16
|
-
}
|
|
22
|
+
interface ContractValidateOptions {
|
|
23
|
+
verbose?: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface ContractBuildOptions {
|
|
27
|
+
output?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
interface ContractDiffOptions {
|
|
31
|
+
from?: string;
|
|
32
|
+
to?: string;
|
|
33
|
+
output?: string;
|
|
34
|
+
json?: boolean;
|
|
35
|
+
}
|
|
17
36
|
|
|
18
37
|
/**
|
|
19
38
|
* Create a new contract file for a route
|
|
@@ -85,7 +104,7 @@ export async function contractCreate(options: ContractCreateOptions): Promise<bo
|
|
|
85
104
|
/**
|
|
86
105
|
* Validate all contracts against their slot implementations
|
|
87
106
|
*/
|
|
88
|
-
export async function contractValidate(options: ContractValidateOptions = {}): Promise<boolean> {
|
|
107
|
+
export async function contractValidate(options: ContractValidateOptions = {}): Promise<boolean> {
|
|
89
108
|
const rootDir = process.cwd();
|
|
90
109
|
const manifestPath = path.join(rootDir, "spec/routes.manifest.json");
|
|
91
110
|
|
|
@@ -148,5 +167,112 @@ export async function contractValidate(options: ContractValidateOptions = {}): P
|
|
|
148
167
|
console.log(`💡 Use --verbose for fix suggestions\n`);
|
|
149
168
|
}
|
|
150
169
|
|
|
151
|
-
return false;
|
|
152
|
-
}
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Build contract registry (.mandu/contracts.json)
|
|
175
|
+
*/
|
|
176
|
+
export async function contractBuild(options: ContractBuildOptions = {}): Promise<boolean> {
|
|
177
|
+
const rootDir = process.cwd();
|
|
178
|
+
const manifestPath = path.join(rootDir, "spec/routes.manifest.json");
|
|
179
|
+
const outputPath = options.output || path.join(rootDir, ".mandu", "contracts.json");
|
|
180
|
+
|
|
181
|
+
console.log(`\n📦 Building contract registry...\n`);
|
|
182
|
+
|
|
183
|
+
const manifestResult = await loadManifest(manifestPath);
|
|
184
|
+
if (!manifestResult.success) {
|
|
185
|
+
console.error("❌ Failed to load manifest:", manifestResult.errors);
|
|
186
|
+
return false;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const manifest = manifestResult.data!;
|
|
190
|
+
const { registry, warnings } = await buildContractRegistry(manifest, rootDir);
|
|
191
|
+
|
|
192
|
+
if (warnings.length > 0) {
|
|
193
|
+
console.log(`⚠️ ${warnings.length} warning(s):`);
|
|
194
|
+
for (const warning of warnings) {
|
|
195
|
+
console.log(` - ${warning}`);
|
|
196
|
+
}
|
|
197
|
+
console.log();
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
await fs.mkdir(path.dirname(outputPath), { recursive: true });
|
|
201
|
+
await writeContractRegistry(outputPath, registry);
|
|
202
|
+
|
|
203
|
+
console.log(`✅ Registry generated: ${path.relative(rootDir, outputPath)}`);
|
|
204
|
+
console.log(`📊 Contracts: ${registry.contracts.length}`);
|
|
205
|
+
|
|
206
|
+
return true;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Diff current contracts against a registry
|
|
211
|
+
*/
|
|
212
|
+
export async function contractDiff(options: ContractDiffOptions = {}): Promise<boolean> {
|
|
213
|
+
const rootDir = process.cwd();
|
|
214
|
+
const manifestPath = path.join(rootDir, "spec/routes.manifest.json");
|
|
215
|
+
const fromPath = options.from || path.join(rootDir, ".mandu", "contracts.json");
|
|
216
|
+
|
|
217
|
+
console.log(`\n🔍 Diffing contracts...\n`);
|
|
218
|
+
|
|
219
|
+
const fromRegistry = await readContractRegistry(fromPath);
|
|
220
|
+
if (!fromRegistry) {
|
|
221
|
+
console.error(`❌ Registry not found: ${path.relative(rootDir, fromPath)}`);
|
|
222
|
+
console.log(`💡 Run \`mandu contract build\` first.`);
|
|
223
|
+
return false;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
let toRegistry = options.to ? await readContractRegistry(options.to) : null;
|
|
227
|
+
|
|
228
|
+
if (!toRegistry) {
|
|
229
|
+
const manifestResult = await loadManifest(manifestPath);
|
|
230
|
+
if (!manifestResult.success) {
|
|
231
|
+
console.error("❌ Failed to load manifest:", manifestResult.errors);
|
|
232
|
+
return false;
|
|
233
|
+
}
|
|
234
|
+
const { registry } = await buildContractRegistry(manifestResult.data!, rootDir);
|
|
235
|
+
toRegistry = registry;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const diff = diffContractRegistry(fromRegistry, toRegistry);
|
|
239
|
+
|
|
240
|
+
if (options.output) {
|
|
241
|
+
await fs.mkdir(path.dirname(options.output), { recursive: true });
|
|
242
|
+
await Bun.write(options.output, JSON.stringify(diff, null, 2));
|
|
243
|
+
console.log(`✅ Diff saved: ${path.relative(rootDir, options.output)}`);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
if (options.json) {
|
|
247
|
+
console.log(JSON.stringify(diff, null, 2));
|
|
248
|
+
return diff.summary.major === 0;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
console.log(`📊 Summary: major ${diff.summary.major}, minor ${diff.summary.minor}, patch ${diff.summary.patch}`);
|
|
252
|
+
|
|
253
|
+
if (diff.added.length > 0) {
|
|
254
|
+
console.log(`\n🟢 Added (${diff.added.length})`);
|
|
255
|
+
for (const entry of diff.added) {
|
|
256
|
+
console.log(` - ${entry.id} (${entry.routeId})`);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
if (diff.removed.length > 0) {
|
|
261
|
+
console.log(`\n🔴 Removed (${diff.removed.length})`);
|
|
262
|
+
for (const entry of diff.removed) {
|
|
263
|
+
console.log(` - ${entry.id} (${entry.routeId})`);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
if (diff.changed.length > 0) {
|
|
268
|
+
console.log(`\n🟡 Changed (${diff.changed.length})`);
|
|
269
|
+
for (const change of diff.changed) {
|
|
270
|
+
console.log(` - ${change.id} (${change.routeId}) [${change.severity}]`);
|
|
271
|
+
for (const detail of change.changes) {
|
|
272
|
+
console.log(` • ${detail}`);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
return diff.summary.major === 0;
|
|
278
|
+
}
|