isthmus-cli 0.1.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 +21 -0
- package/README.md +151 -0
- package/Skills/isthmus/SKILL.md +47 -0
- package/dist/cli/check-command.d.ts +12 -0
- package/dist/cli/check-command.js +48 -0
- package/dist/cli/check-command.js.map +1 -0
- package/dist/cli/graph-command.d.ts +5 -0
- package/dist/cli/graph-command.js +62 -0
- package/dist/cli/graph-command.js.map +1 -0
- package/dist/cli/main.d.ts +2 -0
- package/dist/cli/main.js +111 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/cli/query-command.d.ts +5 -0
- package/dist/cli/query-command.js +48 -0
- package/dist/cli/query-command.js.map +1 -0
- package/dist/cli/retentions-command.d.ts +7 -0
- package/dist/cli/retentions-command.js +55 -0
- package/dist/cli/retentions-command.js.map +1 -0
- package/dist/compare.d.ts +2 -0
- package/dist/compare.js +9 -0
- package/dist/compare.js.map +1 -0
- package/dist/exchange/parse.d.ts +48 -0
- package/dist/exchange/parse.js +148 -0
- package/dist/exchange/parse.js.map +1 -0
- package/dist/join/join.d.ts +59 -0
- package/dist/join/join.js +218 -0
- package/dist/join/join.js.map +1 -0
- package/dist/report/check-report.d.ts +32 -0
- package/dist/report/check-report.js +46 -0
- package/dist/report/check-report.js.map +1 -0
- package/dist/report/graph.d.ts +30 -0
- package/dist/report/graph.js +143 -0
- package/dist/report/graph.js.map +1 -0
- package/dist/report/query.d.ts +28 -0
- package/dist/report/query.js +112 -0
- package/dist/report/query.js.map +1 -0
- package/dist/report/retentions.d.ts +39 -0
- package/dist/report/retentions.js +49 -0
- package/dist/report/retentions.js.map +1 -0
- package/dist/report/sorted-json.d.ts +2 -0
- package/dist/report/sorted-json.js +16 -0
- package/dist/report/sorted-json.js.map +1 -0
- package/package.json +51 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 isthmus contributors
|
|
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
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# isthmus
|
|
2
|
+
|
|
3
|
+
크로스플랫폼 앱에서 **언어 경계를 넘는 호출**을 그래프로 잇는 도구. [cartograph](../cartograph)(Swift) · [kartograph](../kartograph)(Kotlin) · [dartograph](../dartograph)(Dart) 가 각자 그린 지도를 하나로 붙인다.
|
|
4
|
+
|
|
5
|
+
이름은 지협(isthmus) — 두 땅덩어리를 잇는 좁은 육교. 지도에서 다리를 뜻하는 말이다.
|
|
6
|
+
|
|
7
|
+
## 무엇을 하려는가
|
|
8
|
+
|
|
9
|
+
React Native 나 Flutter 앱의 네이티브 코드는 JS/Dart 가 **문자열 이름으로** 부른다. `MethodChannel('com.example/camera')`, `NativeModules.CameraModule`. 컴파일러 인덱스는 이 문자열을 못 본다. 그래서:
|
|
10
|
+
|
|
11
|
+
- cartograph 는 Flutter 가 부르는 Swift 핸들러를 **미사용** 이라고 한다 — 오탐
|
|
12
|
+
- 어떤 도구도 "Dart 가 `invokeMethod('takePhoto')` 를 부르는데 Swift 쪽에 그 핸들러가 없다" 를 **빌드 전에** 잡지 못한다 — 런타임 크래시
|
|
13
|
+
- 어떤 도구도 "이 채널은 Swift 에는 있는데 Dart 어디서도 안 부른다" 를 말하지 못한다 — 진짜 미사용
|
|
14
|
+
|
|
15
|
+
isthmus 는 각 언어 도구가 내보낸 **브리지 사실**(채널 이름 · 메서드 이름 · 등록 지점 · 호출 지점)을 문자열 키로 조인해서, 경계를 넘는 간선을 만들고 위 세 가지를 답한다. 그리고 그 결과를 cartograph/kartograph 에 **보존 근거로 돌려준다** — "Swift `CameraHandler.takePhoto` 는 `lib/camera.dart:42` 가 채널 `com.example/camera` 로 부르므로 보존".
|
|
16
|
+
|
|
17
|
+
기존 도구 어느 것도 하지 않는 일이다.
|
|
18
|
+
|
|
19
|
+
## 상태
|
|
20
|
+
|
|
21
|
+
**0.1.0.** bridge-facts 버전 1 파서와 `check`, `query`, `graph`,
|
|
22
|
+
cartograph용 외부 보존 근거 왕복을 구현했다. 다음 단계는 실제 Flutter 앱 도그푸딩과
|
|
23
|
+
React Native 지원이다.
|
|
24
|
+
|
|
25
|
+
| 문서 | 내용 |
|
|
26
|
+
|---|---|
|
|
27
|
+
| [`docs/PRD.md`](docs/PRD.md) | 무엇을 · 누구를 위해 · 어디까지 |
|
|
28
|
+
| [`docs/PLAN.md`](docs/PLAN.md) | 단계별 계획. **cartograph 와 dartograph 에 선행 작업이 있다** |
|
|
29
|
+
| [`docs/GRAPH-EXCHANGE.md`](docs/GRAPH-EXCHANGE.md) | 자매 도구가 내보내는 브리지 사실의 형식. 세 저장소가 공유하는 계약 |
|
|
30
|
+
| [`docs/RESEARCH.md`](docs/RESEARCH.md) | 확인된 사실 · 확인되지 않은 주장 |
|
|
31
|
+
| [`experiments/phase-0/`](experiments/phase-0/) | Dart·Swift 임시 추출기, 고정 JSON, 손 조인 검증 |
|
|
32
|
+
|
|
33
|
+
## 의존 관계
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
cartograph ──bridges──┐
|
|
37
|
+
kartograph ──bridges──┼──▶ isthmus ──▶ 경계 간선 · 불일치 보고 · 보존 근거
|
|
38
|
+
dartograph ──bridges──┤
|
|
39
|
+
JS/TS 추출기 ─bridges──┘
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
isthmus 자체는 작다. 무거운 일(각 언어의 해석)은 자매 도구가 한다.
|
|
43
|
+
|
|
44
|
+
## 설치
|
|
45
|
+
|
|
46
|
+
전역 설치 후 CLI 이름 `isthmus`로 실행한다.
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
npm install --global isthmus-cli
|
|
50
|
+
isthmus --help
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
설치 없이 한 번 실행할 때는 package 이름을 명시한다.
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
npx isthmus-cli --help
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
`npx isthmus`는 이름이 같은 다른 package를 설치하므로 사용하지 않는다.
|
|
60
|
+
|
|
61
|
+
## 개발 빌드에서 사용
|
|
62
|
+
|
|
63
|
+
isthmus는 자매 도구를 직접 실행하지 않는다. 각 도구가 만든 JSON 파일을 전달한다.
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
npm ci
|
|
67
|
+
npm run build
|
|
68
|
+
node dist/cli/main.js check dart-bridges.json swift-bridges.json
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
전체 명령과 현재 package 버전은 다음처럼 확인한다.
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
node dist/cli/main.js --help
|
|
75
|
+
node dist/cli/main.js --version
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
CI에서 브리지 오류가 있으면 실패시키려면 `--strict`를 붙인다.
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
node dist/cli/main.js check dart-bridges.json swift-bridges.json --strict
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
매치된 Swift 핸들러를 cartograph 보존 근거로 돌려주려면:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
node dist/cli/main.js retentions \
|
|
88
|
+
dart-bridges.json swift-bridges.json \
|
|
89
|
+
--for cartograph > external-retentions.json
|
|
90
|
+
|
|
91
|
+
cartograph dead --external-retentions external-retentions.json
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
`retentions`는 핸들러의 USR을 우선 사용하고 없으면 `qualifiedName`을 남긴다. `mixed-targets` 문서는 v1에서 사실별 target을 복원할 수 없어 안전하게 조인을 보류한다. 먼저 생산 단계에서 target별 문서로 분리해야 한다.
|
|
95
|
+
|
|
96
|
+
채널이나 메서드가 경계 반대편의 어느 위치와 연결되는지 조회하려면:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
node dist/cli/main.js query takePhoto dart-bridges.json swift-bridges.json
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
경계 간선만 JSON, Graphviz DOT, Mermaid로 출력하려면:
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
node dist/cli/main.js graph dart-bridges.json swift-bridges.json
|
|
106
|
+
node dist/cli/main.js graph dart-bridges.json swift-bridges.json --format dot
|
|
107
|
+
node dist/cli/main.js graph dart-bridges.json swift-bridges.json --format mermaid
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
`query`는 같은 메서드가 여러 채널에 있으면 후보를 반환하고 임의로 고르지 않는다.
|
|
111
|
+
반환된 `qualifiedName`을 같은 subject 자리에 넣어 정확한 후보를 다시 조회할 수 있다.
|
|
112
|
+
`graph`는 매치된 간선만 내보내며, 입력의 `limitations`를 함께 보존한다.
|
|
113
|
+
|
|
114
|
+
출력은 `isthmus-check` 버전 1 JSON이며 다음 세 사실을 보고한다.
|
|
115
|
+
|
|
116
|
+
- `unhandled-invocation` (error): 호출은 있지만 네이티브 핸들러가 없음
|
|
117
|
+
- `unregistered-channel-creation` (error): 호출 측 채널 생성은 있지만 네이티브 등록이 없음
|
|
118
|
+
- `handler-without-invocation` (warning): 네이티브 핸들러는 있지만 호출 측 사용이 없음
|
|
119
|
+
|
|
120
|
+
모든 이슈는 관찰된 위치를 `evidence`로 제공한다. 동적 이름, USR 누락, 입력 생성 시각 차이, 혼합 target은 `limitations`에 출처와 함께 남긴다. 이 도구는 삭제 가능 여부를 판정하지 않는다.
|
|
121
|
+
|
|
122
|
+
| 종료 코드 | 의미 |
|
|
123
|
+
|---|---|
|
|
124
|
+
| `0` | 실행 성공. 기본 모드에서는 이슈가 있어도 보고만 함 |
|
|
125
|
+
| `1` | `--strict`에서 error 이슈를 발견함 |
|
|
126
|
+
| `2` | 파일 읽기, JSON, 교환 계약 등 도구 실패 |
|
|
127
|
+
| `64` | 잘못된 명령, 옵션, 입력 개수 |
|
|
128
|
+
|
|
129
|
+
개발 검증은 타입 체크, 제품 코드 90% 커버리지, 빌드, 실제 CLI 계약을 함께 실행한다.
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
npm run verify
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
로컬 cartograph 코퍼스와 외부 보존 근거 왕복을 검증하려면 빌드된 cartograph 바이너리와 fixture 루트를 넘긴다.
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
node scripts/verify-cartograph-roundtrip.mjs \
|
|
139
|
+
/path/to/cartograph \
|
|
140
|
+
/path/to/FalsePositiveCorpus
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## 코딩 에이전트 skill
|
|
144
|
+
|
|
145
|
+
네이티브 브리지 핸들러를 지우거나 이름을 바꾸기 전에 `query`로 다른 언어의 호출자를
|
|
146
|
+
확인하도록 가르치는 skill 원문을 [`Skills/isthmus/SKILL.md`](Skills/isthmus/SKILL.md)에 제공한다.
|
|
147
|
+
사용하는 에이전트의 프로젝트 skill 디렉터리에 이 파일을 복사해 사용할 수 있다.
|
|
148
|
+
|
|
149
|
+
## 라이선스
|
|
150
|
+
|
|
151
|
+
[MIT](LICENSE). 상업적 사용을 포함해 영구 무료다.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: isthmus
|
|
3
|
+
description: >-
|
|
4
|
+
Use before deleting or renaming native bridge handlers, or when tracing callers across
|
|
5
|
+
Flutter and other cross-platform language boundaries.
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# isthmus
|
|
9
|
+
|
|
10
|
+
Native bridge calls are string-keyed, so a language-specific symbol index cannot see the
|
|
11
|
+
whole call path. Use isthmus before changing a native handler that Dart, JavaScript, or
|
|
12
|
+
another platform may call.
|
|
13
|
+
|
|
14
|
+
## Before deleting or renaming a bridge handler
|
|
15
|
+
|
|
16
|
+
Obtain current `bridge-facts` JSON from both sides, then run:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
isthmus query <channel-or-method> <bridge-facts.json> <bridge-facts.json> [more...]
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Read the entire response:
|
|
23
|
+
|
|
24
|
+
- `usedBy` is the calling or channel-creation side.
|
|
25
|
+
- `dependsOn` is the handler or channel-registration side.
|
|
26
|
+
- `limitations` records dynamic names, stale inputs, missing symbols, or joins that were
|
|
27
|
+
deliberately deferred.
|
|
28
|
+
- `ambiguous` provides `qualifiedName` candidates. Query the intended candidate rather
|
|
29
|
+
than guessing.
|
|
30
|
+
|
|
31
|
+
A `found` match is evidence of a cross-language dependency, not permission to delete.
|
|
32
|
+
A `notFound` response and exit code 64 do not prove the handler is unused. Check spelling,
|
|
33
|
+
input freshness, dynamic-name limitations, and whether every relevant platform produced a
|
|
34
|
+
facts file. Regenerate facts after source changes before relying on the answer.
|
|
35
|
+
|
|
36
|
+
## Feeding the result back to cartograph
|
|
37
|
+
|
|
38
|
+
For Swift handlers, produce external retention evidence and use the same inputs:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
isthmus retentions <bridge-facts.json> <bridge-facts.json> --for cartograph \
|
|
42
|
+
> external-retentions.json
|
|
43
|
+
cartograph dead --external-retentions external-retentions.json
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Do not commit generated retention files when they contain local source paths unless the
|
|
47
|
+
project has explicitly chosen to version them.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** 파일 경로를 받아 UTF-8 텍스트를 읽는 주입 경계다. */
|
|
2
|
+
export type ReadTextFile = (path: string) => Promise<string>;
|
|
3
|
+
/** CLI가 프로세스 경계에 쓸 출력과 종료 코드다. */
|
|
4
|
+
export interface CommandResult {
|
|
5
|
+
readonly standardOutput: string;
|
|
6
|
+
readonly standardError: string;
|
|
7
|
+
readonly exitCode: 0 | 1 | 2 | 64;
|
|
8
|
+
}
|
|
9
|
+
/** check 인자를 실행해 프로세스에 독립적인 결과를 반환한다. */
|
|
10
|
+
export declare function runCheckCommand(arguments_: readonly string[], readTextFile: ReadTextFile): Promise<CommandResult>;
|
|
11
|
+
/** check 명령의 한 줄 사용법이다. */
|
|
12
|
+
export declare const checkUsage: string;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { parseBridgeFactsDocument } from "../exchange/parse.js";
|
|
2
|
+
import { joinBridgeDocuments } from "../join/join.js";
|
|
3
|
+
import { createCheckReport, encodeCheckReport } from "../report/check-report.js";
|
|
4
|
+
/** check 인자를 실행해 프로세스에 독립적인 결과를 반환한다. */
|
|
5
|
+
export async function runCheckCommand(arguments_, readTextFile) {
|
|
6
|
+
if (arguments_[0] !== 'check')
|
|
7
|
+
return usageError();
|
|
8
|
+
const options = arguments_.slice(1).filter((argument) => argument.startsWith('-'));
|
|
9
|
+
if (options.some((option) => option !== '--strict'))
|
|
10
|
+
return usageError();
|
|
11
|
+
const isStrict = arguments_.includes('--strict');
|
|
12
|
+
const inputPaths = arguments_.slice(1).filter((argument) => argument !== '--strict');
|
|
13
|
+
if (inputPaths.length < 2)
|
|
14
|
+
return usageError();
|
|
15
|
+
try {
|
|
16
|
+
const texts = await Promise.all(inputPaths.map(readTextFile));
|
|
17
|
+
const documents = texts.map((text) => parseBridgeFactsDocument(JSON.parse(text)));
|
|
18
|
+
const report = createCheckReport(joinBridgeDocuments(documents));
|
|
19
|
+
return {
|
|
20
|
+
standardOutput: encodeCheckReport(report),
|
|
21
|
+
standardError: '',
|
|
22
|
+
exitCode: isStrict && report.summary.errors > 0 ? 1 : 0,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return inputError();
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/** 파일·JSON·계약 오류를 민감정보 없는 코드 2 결과로 바꾼다. */
|
|
30
|
+
function inputError() {
|
|
31
|
+
return {
|
|
32
|
+
standardOutput: '',
|
|
33
|
+
standardError: 'Unable to read or validate bridge facts; check the input files.\n',
|
|
34
|
+
exitCode: 2,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
/** 잘못된 CLI 호출을 경로 없는 사용법과 코드 64로 바꾼다. */
|
|
38
|
+
function usageError() {
|
|
39
|
+
return {
|
|
40
|
+
standardOutput: '',
|
|
41
|
+
standardError: `${checkUsage}\n`,
|
|
42
|
+
exitCode: 64,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
/** check 명령의 한 줄 사용법이다. */
|
|
46
|
+
export const checkUsage = 'Usage: isthmus check <bridge-facts.json> <bridge-facts.json> '
|
|
47
|
+
+ '[more...] [--strict]';
|
|
48
|
+
//# sourceMappingURL=check-command.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check-command.js","sourceRoot":"","sources":["../../src/cli/check-command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAChE,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAYjF,yCAAyC;AACzC,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,UAA6B,EAC7B,YAA0B;IAE1B,IAAI,UAAU,CAAC,CAAC,CAAC,KAAK,OAAO;QAAE,OAAO,UAAU,EAAE,CAAC;IACnD,MAAM,OAAO,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC;IACnF,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,KAAK,UAAU,CAAC;QAAE,OAAO,UAAU,EAAE,CAAC;IACzE,MAAM,QAAQ,GAAG,UAAU,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;IACjD,MAAM,UAAU,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,KAAK,UAAU,CAAC,CAAC;IACrF,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,UAAU,EAAE,CAAC;IAC/C,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC;QAC9D,MAAM,SAAS,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CACnC,wBAAwB,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAC3C,CAAC;QACF,MAAM,MAAM,GAAG,iBAAiB,CAAC,mBAAmB,CAAC,SAAS,CAAC,CAAC,CAAC;QACjE,OAAO;YACL,cAAc,EAAE,iBAAiB,CAAC,MAAM,CAAC;YACzC,aAAa,EAAE,EAAE;YACjB,QAAQ,EAAE,QAAQ,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;SACxD,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,UAAU,EAAE,CAAC;IACtB,CAAC;AACH,CAAC;AAED,2CAA2C;AAC3C,SAAS,UAAU;IACjB,OAAO;QACL,cAAc,EAAE,EAAE;QAClB,aAAa,EACX,mEAAmE;QACrE,QAAQ,EAAE,CAAC;KACZ,CAAC;AACJ,CAAC;AAED,yCAAyC;AACzC,SAAS,UAAU;IACjB,OAAO;QACL,cAAc,EAAE,EAAE;QAClB,aAAa,EAAE,GAAG,UAAU,IAAI;QAChC,QAAQ,EAAE,EAAE;KACb,CAAC;AACJ,CAAC;AAED,2BAA2B;AAC3B,MAAM,CAAC,MAAM,UAAU,GACrB,+DAA+D;MAC7D,sBAAsB,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { CommandResult, ReadTextFile } from './check-command.ts';
|
|
2
|
+
/** graph 인자를 실행해 경계 그래프와 종료 코드를 반환한다. */
|
|
3
|
+
export declare function runGraphCommand(arguments_: readonly string[], readTextFile: ReadTextFile): Promise<CommandResult>;
|
|
4
|
+
/** graph 명령의 한 줄 사용법이다. */
|
|
5
|
+
export declare const graphUsage: string;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { parseBridgeFactsDocument } from "../exchange/parse.js";
|
|
2
|
+
import { joinBridgeDocuments } from "../join/join.js";
|
|
3
|
+
import { createBridgeGraph, renderBridgeGraph } from "../report/graph.js";
|
|
4
|
+
/** graph 인자를 실행해 경계 그래프와 종료 코드를 반환한다. */
|
|
5
|
+
export async function runGraphCommand(arguments_, readTextFile) {
|
|
6
|
+
const options = parseGraphArguments(arguments_);
|
|
7
|
+
if (options === undefined)
|
|
8
|
+
return graphUsageError();
|
|
9
|
+
try {
|
|
10
|
+
const texts = await Promise.all(options.inputPaths.map(readTextFile));
|
|
11
|
+
const documents = texts.map((text) => parseBridgeFactsDocument(JSON.parse(text)));
|
|
12
|
+
const graph = createBridgeGraph(joinBridgeDocuments(documents));
|
|
13
|
+
return {
|
|
14
|
+
standardOutput: renderBridgeGraph(graph, options.format),
|
|
15
|
+
standardError: '',
|
|
16
|
+
exitCode: 0,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return graphInputError();
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/** graph의 입력 파일과 선택 출력 형식을 검증한다. */
|
|
24
|
+
function parseGraphArguments(arguments_) {
|
|
25
|
+
if (arguments_[0] !== 'graph')
|
|
26
|
+
return undefined;
|
|
27
|
+
const formatIndex = arguments_.indexOf('--format');
|
|
28
|
+
const inputPaths = arguments_.slice(1, formatIndex < 0 ? undefined : formatIndex);
|
|
29
|
+
if (inputPaths.length < 2 || inputPaths.some((path) => path.startsWith('-'))) {
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
if (formatIndex < 0)
|
|
33
|
+
return { inputPaths, format: 'json' };
|
|
34
|
+
if (formatIndex !== arguments_.length - 2)
|
|
35
|
+
return undefined;
|
|
36
|
+
const format = arguments_[formatIndex + 1];
|
|
37
|
+
return isGraphFormat(format) ? { inputPaths, format } : undefined;
|
|
38
|
+
}
|
|
39
|
+
/** 지원하는 그래프 형식인지 확인한다. */
|
|
40
|
+
function isGraphFormat(value) {
|
|
41
|
+
return value === 'json' || value === 'dot' || value === 'mermaid';
|
|
42
|
+
}
|
|
43
|
+
/** graph 사용 오류를 경로 없는 코드 64로 바꾼다. */
|
|
44
|
+
function graphUsageError() {
|
|
45
|
+
return {
|
|
46
|
+
standardOutput: '',
|
|
47
|
+
standardError: `${graphUsage}\n`,
|
|
48
|
+
exitCode: 64,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/** graph 입력 실패를 경로 없는 코드 2로 바꾼다. */
|
|
52
|
+
function graphInputError() {
|
|
53
|
+
return {
|
|
54
|
+
standardOutput: '',
|
|
55
|
+
standardError: 'Unable to read or validate bridge facts; check the input files.\n',
|
|
56
|
+
exitCode: 2,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
/** graph 명령의 한 줄 사용법이다. */
|
|
60
|
+
export const graphUsage = 'Usage: isthmus graph <bridge-facts.json> <bridge-facts.json> '
|
|
61
|
+
+ '[more...] [--format json|dot|mermaid]';
|
|
62
|
+
//# sourceMappingURL=graph-command.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graph-command.js","sourceRoot":"","sources":["../../src/cli/graph-command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAChE,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAG1E,yCAAyC;AACzC,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,UAA6B,EAC7B,YAA0B;IAE1B,MAAM,OAAO,GAAG,mBAAmB,CAAC,UAAU,CAAC,CAAC;IAChD,IAAI,OAAO,KAAK,SAAS;QAAE,OAAO,eAAe,EAAE,CAAC;IACpD,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC;QACtE,MAAM,SAAS,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CACnC,wBAAwB,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAC3C,CAAC;QACF,MAAM,KAAK,GAAG,iBAAiB,CAAC,mBAAmB,CAAC,SAAS,CAAC,CAAC,CAAC;QAChE,OAAO;YACL,cAAc,EAAE,iBAAiB,CAAC,KAAK,EAAE,OAAO,CAAC,MAAM,CAAC;YACxD,aAAa,EAAE,EAAE;YACjB,QAAQ,EAAE,CAAC;SACZ,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,eAAe,EAAE,CAAC;IAC3B,CAAC;AACH,CAAC;AAED,oCAAoC;AACpC,SAAS,mBAAmB,CAAC,UAA6B;IACxD,IAAI,UAAU,CAAC,CAAC,CAAC,KAAK,OAAO;QAAE,OAAO,SAAS,CAAC;IAChD,MAAM,WAAW,GAAG,UAAU,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IACnD,MAAM,UAAU,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,WAAW,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC;IAClF,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;QAC7E,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,IAAI,WAAW,GAAG,CAAC;QAAE,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;IAC3D,IAAI,WAAW,KAAK,UAAU,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,SAAS,CAAC;IAC5D,MAAM,MAAM,GAAG,UAAU,CAAC,WAAW,GAAG,CAAC,CAAC,CAAC;IAC3C,OAAO,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;AACpE,CAAC;AAED,0BAA0B;AAC1B,SAAS,aAAa,CAAC,KAAyB;IAC9C,OAAO,KAAK,KAAK,MAAM,IAAI,KAAK,KAAK,KAAK,IAAI,KAAK,KAAK,SAAS,CAAC;AACpE,CAAC;AAED,qCAAqC;AACrC,SAAS,eAAe;IACtB,OAAO;QACL,cAAc,EAAE,EAAE;QAClB,aAAa,EAAE,GAAG,UAAU,IAAI;QAChC,QAAQ,EAAE,EAAE;KACb,CAAC;AACJ,CAAC;AAED,oCAAoC;AACpC,SAAS,eAAe;IACtB,OAAO;QACL,cAAc,EAAE,EAAE;QAClB,aAAa,EACX,mEAAmE;QACrE,QAAQ,EAAE,CAAC;KACZ,CAAC;AACJ,CAAC;AAUD,2BAA2B;AAC3B,MAAM,CAAC,MAAM,UAAU,GACrB,+DAA+D;MAC7D,uCAAuC,CAAC"}
|
package/dist/cli/main.js
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readFile } from 'node:fs/promises';
|
|
3
|
+
import { checkUsage, runCheckCommand, } from "./check-command.js";
|
|
4
|
+
import { graphUsage, runGraphCommand } from "./graph-command.js";
|
|
5
|
+
import { queryUsage, runQueryCommand } from "./query-command.js";
|
|
6
|
+
import { retentionUsage, runRetentionsCommand, } from "./retentions-command.js";
|
|
7
|
+
const commandUsages = new Map([
|
|
8
|
+
['check', checkUsage],
|
|
9
|
+
['graph', graphUsage],
|
|
10
|
+
['query', queryUsage],
|
|
11
|
+
['retentions', retentionUsage],
|
|
12
|
+
]);
|
|
13
|
+
const rootHelp = `Usage: isthmus <command> [options]
|
|
14
|
+
|
|
15
|
+
Commands:
|
|
16
|
+
check Report unmatched bridge calls and handlers
|
|
17
|
+
query Find both sides of a channel or method
|
|
18
|
+
graph Render matched boundary edges
|
|
19
|
+
retentions Produce external retention evidence
|
|
20
|
+
|
|
21
|
+
Options:
|
|
22
|
+
-h, --help Show help
|
|
23
|
+
--version Show version
|
|
24
|
+
`;
|
|
25
|
+
const arguments_ = process.argv.slice(2);
|
|
26
|
+
const readTextFile = (path) => readFile(path, 'utf8');
|
|
27
|
+
const informationalResult = await runInformationalCommand(arguments_);
|
|
28
|
+
const result = informationalResult ?? await dispatchCommand(arguments_);
|
|
29
|
+
process.stdout.write(result.standardOutput);
|
|
30
|
+
process.stderr.write(result.standardError);
|
|
31
|
+
process.exitCode = result.exitCode;
|
|
32
|
+
/** 도움말과 버전처럼 입력 문서가 필요 없는 명령을 실행한다. */
|
|
33
|
+
async function runInformationalCommand(commandArguments) {
|
|
34
|
+
const first = commandArguments[0];
|
|
35
|
+
if (commandArguments.length === 1 &&
|
|
36
|
+
(first === '--help' || first === '-h')) {
|
|
37
|
+
return successfulText(rootHelp);
|
|
38
|
+
}
|
|
39
|
+
if (commandArguments.length === 1 && first === '--version') {
|
|
40
|
+
return readVersion();
|
|
41
|
+
}
|
|
42
|
+
if (commandArguments.length === 2 &&
|
|
43
|
+
(commandArguments[1] === '--help' || commandArguments[1] === '-h')) {
|
|
44
|
+
const usage = first === undefined ? undefined : commandUsages.get(first);
|
|
45
|
+
if (usage !== undefined)
|
|
46
|
+
return successfulText(`${usage}\n`);
|
|
47
|
+
}
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
/** 알려진 분석 하위 명령으로만 라우팅한다. */
|
|
51
|
+
async function dispatchCommand(commandArguments) {
|
|
52
|
+
switch (commandArguments[0]) {
|
|
53
|
+
case 'check':
|
|
54
|
+
return runCheckCommand(commandArguments, readTextFile);
|
|
55
|
+
case 'graph':
|
|
56
|
+
return runGraphCommand(commandArguments, readTextFile);
|
|
57
|
+
case 'query':
|
|
58
|
+
return runQueryCommand(commandArguments, readTextFile);
|
|
59
|
+
case 'retentions': {
|
|
60
|
+
const version = await readPackageVersion();
|
|
61
|
+
return version === undefined
|
|
62
|
+
? packageMetadataError()
|
|
63
|
+
: runRetentionsCommand(commandArguments, readTextFile, () => new Date(), version);
|
|
64
|
+
}
|
|
65
|
+
default:
|
|
66
|
+
return {
|
|
67
|
+
standardOutput: '',
|
|
68
|
+
standardError: rootHelp,
|
|
69
|
+
exitCode: 64,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/** 설치된 package.json을 단일 출처로 사용해 버전을 출력한다. */
|
|
74
|
+
async function readVersion() {
|
|
75
|
+
const version = await readPackageVersion();
|
|
76
|
+
return version === undefined
|
|
77
|
+
? packageMetadataError()
|
|
78
|
+
: successfulText(`${version}\n`);
|
|
79
|
+
}
|
|
80
|
+
/** 설치된 package.json에서 검증된 버전 문자열을 읽는다. */
|
|
81
|
+
async function readPackageVersion() {
|
|
82
|
+
try {
|
|
83
|
+
const text = await readFile(new URL('../../package.json', import.meta.url), 'utf8');
|
|
84
|
+
const document = JSON.parse(text);
|
|
85
|
+
return isVersionDocument(document) ? document.version : undefined;
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
return undefined;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/** package metadata가 비어 있지 않은 버전을 제공하는지 확인한다. */
|
|
92
|
+
function isVersionDocument(value) {
|
|
93
|
+
return (typeof value === 'object' &&
|
|
94
|
+
value !== null &&
|
|
95
|
+
'version' in value &&
|
|
96
|
+
typeof value.version === 'string' &&
|
|
97
|
+
value.version.length > 0);
|
|
98
|
+
}
|
|
99
|
+
/** stdout 텍스트와 성공 종료 코드를 만든다. */
|
|
100
|
+
function successfulText(standardOutput) {
|
|
101
|
+
return { standardOutput, standardError: '', exitCode: 0 };
|
|
102
|
+
}
|
|
103
|
+
/** package metadata 실패를 민감정보 없는 코드 2로 바꾼다. */
|
|
104
|
+
function packageMetadataError() {
|
|
105
|
+
return {
|
|
106
|
+
standardOutput: '',
|
|
107
|
+
standardError: 'Unable to read package metadata.\n',
|
|
108
|
+
exitCode: 2,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=main.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.js","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C,OAAO,EACL,UAAU,EACV,eAAe,GAEhB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACjE,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACjE,OAAO,EACL,cAAc,EACd,oBAAoB,GACrB,MAAM,yBAAyB,CAAC;AAEjC,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC;IAC5B,CAAC,OAAO,EAAE,UAAU,CAAC;IACrB,CAAC,OAAO,EAAE,UAAU,CAAC;IACrB,CAAC,OAAO,EAAE,UAAU,CAAC;IACrB,CAAC,YAAY,EAAE,cAAc,CAAC;CAC/B,CAAC,CAAC;AAEH,MAAM,QAAQ,GAAG;;;;;;;;;;;CAWhB,CAAC;AAEF,MAAM,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AACzC,MAAM,YAAY,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;AAC9D,MAAM,mBAAmB,GAAG,MAAM,uBAAuB,CAAC,UAAU,CAAC,CAAC;AACtE,MAAM,MAAM,GAAG,mBAAmB,IAAI,MAAM,eAAe,CAAC,UAAU,CAAC,CAAC;AAExE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;AAC5C,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;AAC3C,OAAO,CAAC,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;AAEnC,uCAAuC;AACvC,KAAK,UAAU,uBAAuB,CACpC,gBAAmC;IAEnC,MAAM,KAAK,GAAG,gBAAgB,CAAC,CAAC,CAAC,CAAC;IAClC,IACE,gBAAgB,CAAC,MAAM,KAAK,CAAC;QAC7B,CAAC,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,CAAC,EACtC,CAAC;QACD,OAAO,cAAc,CAAC,QAAQ,CAAC,CAAC;IAClC,CAAC;IACD,IAAI,gBAAgB,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,KAAK,WAAW,EAAE,CAAC;QAC3D,OAAO,WAAW,EAAE,CAAC;IACvB,CAAC;IACD,IACE,gBAAgB,CAAC,MAAM,KAAK,CAAC;QAC7B,CAAC,gBAAgB,CAAC,CAAC,CAAC,KAAK,QAAQ,IAAI,gBAAgB,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,EAClE,CAAC;QACD,MAAM,KAAK,GAAG,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACzE,IAAI,KAAK,KAAK,SAAS;YAAE,OAAO,cAAc,CAAC,GAAG,KAAK,IAAI,CAAC,CAAC;IAC/D,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,6BAA6B;AAC7B,KAAK,UAAU,eAAe,CAC5B,gBAAmC;IAEnC,QAAQ,gBAAgB,CAAC,CAAC,CAAC,EAAE,CAAC;QAC5B,KAAK,OAAO;YACV,OAAO,eAAe,CAAC,gBAAgB,EAAE,YAAY,CAAC,CAAC;QACzD,KAAK,OAAO;YACV,OAAO,eAAe,CAAC,gBAAgB,EAAE,YAAY,CAAC,CAAC;QACzD,KAAK,OAAO;YACV,OAAO,eAAe,CAAC,gBAAgB,EAAE,YAAY,CAAC,CAAC;QACzD,KAAK,YAAY,CAAC,CAAC,CAAC;YAClB,MAAM,OAAO,GAAG,MAAM,kBAAkB,EAAE,CAAC;YAC3C,OAAO,OAAO,KAAK,SAAS;gBAC1B,CAAC,CAAC,oBAAoB,EAAE;gBACxB,CAAC,CAAC,oBAAoB,CAClB,gBAAgB,EAChB,YAAY,EACZ,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,EAChB,OAAO,CACR,CAAC;QACR,CAAC;QACD;YACE,OAAO;gBACL,cAAc,EAAE,EAAE;gBAClB,aAAa,EAAE,QAAQ;gBACvB,QAAQ,EAAE,EAAE;aACb,CAAC;IACN,CAAC;AACH,CAAC;AAED,6CAA6C;AAC7C,KAAK,UAAU,WAAW;IACxB,MAAM,OAAO,GAAG,MAAM,kBAAkB,EAAE,CAAC;IAC3C,OAAO,OAAO,KAAK,SAAS;QAC1B,CAAC,CAAC,oBAAoB,EAAE;QACxB,CAAC,CAAC,cAAc,CAAC,GAAG,OAAO,IAAI,CAAC,CAAC;AACrC,CAAC;AAED,0CAA0C;AAC1C,KAAK,UAAU,kBAAkB;IAC/B,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,GAAG,CAAC,oBAAoB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,CAAC;QACpF,MAAM,QAAQ,GAAY,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC3C,OAAO,iBAAiB,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;IACpE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,iDAAiD;AACjD,SAAS,iBAAiB,CAAC,KAAc;IACvC,OAAO,CACL,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,KAAK,IAAI;QACd,SAAS,IAAI,KAAK;QAClB,OAAO,KAAK,CAAC,OAAO,KAAK,QAAQ;QACjC,KAAK,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CACzB,CAAC;AACJ,CAAC;AAED,iCAAiC;AACjC,SAAS,cAAc,CAAC,cAAsB;IAC5C,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;AAC5D,CAAC;AAED,8CAA8C;AAC9C,SAAS,oBAAoB;IAC3B,OAAO;QACL,cAAc,EAAE,EAAE;QAClB,aAAa,EAAE,oCAAoC;QACnD,QAAQ,EAAE,CAAC;KACZ,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { CommandResult, ReadTextFile } from './check-command.ts';
|
|
2
|
+
/** query 인자를 실행해 bridge 질의 JSON과 종료 코드를 반환한다. */
|
|
3
|
+
export declare function runQueryCommand(arguments_: readonly string[], readTextFile: ReadTextFile): Promise<CommandResult>;
|
|
4
|
+
/** query 명령의 한 줄 사용법이다. */
|
|
5
|
+
export declare const queryUsage: string;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { parseBridgeFactsDocument } from "../exchange/parse.js";
|
|
2
|
+
import { joinBridgeDocuments } from "../join/join.js";
|
|
3
|
+
import { createBridgeQuery, encodeBridgeQuery } from "../report/query.js";
|
|
4
|
+
/** query 인자를 실행해 bridge 질의 JSON과 종료 코드를 반환한다. */
|
|
5
|
+
export async function runQueryCommand(arguments_, readTextFile) {
|
|
6
|
+
const requested = arguments_[1];
|
|
7
|
+
const inputPaths = arguments_.slice(2);
|
|
8
|
+
if (arguments_[0] !== 'query' ||
|
|
9
|
+
requested === undefined ||
|
|
10
|
+
requested.trim().length === 0 ||
|
|
11
|
+
inputPaths.length < 2 ||
|
|
12
|
+
inputPaths.some((path) => path.startsWith('-'))) {
|
|
13
|
+
return queryUsageError();
|
|
14
|
+
}
|
|
15
|
+
try {
|
|
16
|
+
const texts = await Promise.all(inputPaths.map(readTextFile));
|
|
17
|
+
const documents = texts.map((text) => parseBridgeFactsDocument(JSON.parse(text)));
|
|
18
|
+
const query = createBridgeQuery(joinBridgeDocuments(documents), requested);
|
|
19
|
+
return {
|
|
20
|
+
standardOutput: encodeBridgeQuery(query),
|
|
21
|
+
standardError: '',
|
|
22
|
+
exitCode: query.status === 'found' ? 0 : 64,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return queryInputError();
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/** 입력 실패를 경로 없는 도구 실패 2로 바꾼다. */
|
|
30
|
+
function queryInputError() {
|
|
31
|
+
return {
|
|
32
|
+
standardOutput: '',
|
|
33
|
+
standardError: 'Unable to read or validate bridge facts; check the input files.\n',
|
|
34
|
+
exitCode: 2,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
/** 잘못된 query 호출을 사용법과 코드 64로 바꾼다. */
|
|
38
|
+
function queryUsageError() {
|
|
39
|
+
return {
|
|
40
|
+
standardOutput: '',
|
|
41
|
+
standardError: `${queryUsage}\n`,
|
|
42
|
+
exitCode: 64,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
/** query 명령의 한 줄 사용법이다. */
|
|
46
|
+
export const queryUsage = 'Usage: isthmus query <channel-or-method> <bridge-facts.json> '
|
|
47
|
+
+ '<bridge-facts.json> [more...]';
|
|
48
|
+
//# sourceMappingURL=query-command.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query-command.js","sourceRoot":"","sources":["../../src/cli/query-command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAChE,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAG1E,iDAAiD;AACjD,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,UAA6B,EAC7B,YAA0B;IAE1B,MAAM,SAAS,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC;IAChC,MAAM,UAAU,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACvC,IACE,UAAU,CAAC,CAAC,CAAC,KAAK,OAAO;QACzB,SAAS,KAAK,SAAS;QACvB,SAAS,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC;QAC7B,UAAU,CAAC,MAAM,GAAG,CAAC;QACrB,UAAU,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,EAC/C,CAAC;QACD,OAAO,eAAe,EAAE,CAAC;IAC3B,CAAC;IACD,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC;QAC9D,MAAM,SAAS,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CACnC,wBAAwB,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAC3C,CAAC;QACF,MAAM,KAAK,GAAG,iBAAiB,CAAC,mBAAmB,CAAC,SAAS,CAAC,EAAE,SAAS,CAAC,CAAC;QAC3E,OAAO;YACL,cAAc,EAAE,iBAAiB,CAAC,KAAK,CAAC;YACxC,aAAa,EAAE,EAAE;YACjB,QAAQ,EAAE,KAAK,CAAC,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE;SAC5C,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,eAAe,EAAE,CAAC;IAC3B,CAAC;AACH,CAAC;AAED,iCAAiC;AACjC,SAAS,eAAe;IACtB,OAAO;QACL,cAAc,EAAE,EAAE;QAClB,aAAa,EACX,mEAAmE;QACrE,QAAQ,EAAE,CAAC;KACZ,CAAC;AACJ,CAAC;AAED,qCAAqC;AACrC,SAAS,eAAe;IACtB,OAAO;QACL,cAAc,EAAE,EAAE;QAClB,aAAa,EAAE,GAAG,UAAU,IAAI;QAChC,QAAQ,EAAE,EAAE;KACb,CAAC;AACJ,CAAC;AAED,2BAA2B;AAC3B,MAAM,CAAC,MAAM,UAAU,GACrB,+DAA+D;MAC7D,+BAA+B,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { CommandResult, ReadTextFile } from './check-command.ts';
|
|
2
|
+
/** 생성 시각을 테스트 가능하게 주입하는 시계다. */
|
|
3
|
+
export type Clock = () => Date;
|
|
4
|
+
/** retentions 인자를 실행해 cartograph용 보존 문서를 반환한다. */
|
|
5
|
+
export declare function runRetentionsCommand(arguments_: readonly string[], readTextFile: ReadTextFile, now: Clock, producerVersion: string): Promise<CommandResult>;
|
|
6
|
+
/** retentions 명령의 한 줄 사용법이다. */
|
|
7
|
+
export declare const retentionUsage: string;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { parseBridgeFactsDocument } from "../exchange/parse.js";
|
|
2
|
+
import { joinBridgeDocuments } from "../join/join.js";
|
|
3
|
+
import { createCartographRetentionsDocument, encodeCartographRetentionsDocument, } from "../report/retentions.js";
|
|
4
|
+
/** retentions 인자를 실행해 cartograph용 보존 문서를 반환한다. */
|
|
5
|
+
export async function runRetentionsCommand(arguments_, readTextFile, now, producerVersion) {
|
|
6
|
+
const inputPaths = retentionInputPaths(arguments_);
|
|
7
|
+
if (inputPaths === undefined)
|
|
8
|
+
return retentionUsageError();
|
|
9
|
+
try {
|
|
10
|
+
const texts = await Promise.all(inputPaths.map(readTextFile));
|
|
11
|
+
const documents = texts.map((text) => parseBridgeFactsDocument(JSON.parse(text)));
|
|
12
|
+
const joined = joinBridgeDocuments(documents);
|
|
13
|
+
const retentions = createCartographRetentionsDocument(joined, now().toISOString(), producerVersion);
|
|
14
|
+
return {
|
|
15
|
+
standardOutput: encodeCartographRetentionsDocument(retentions),
|
|
16
|
+
standardError: '',
|
|
17
|
+
exitCode: 0,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return retentionInputError();
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/** 입력 실패를 경로 없는 도구 실패 2로 바꾼다. */
|
|
25
|
+
function retentionInputError() {
|
|
26
|
+
return {
|
|
27
|
+
standardOutput: '',
|
|
28
|
+
standardError: 'Unable to read or validate bridge facts; check the input files.\n',
|
|
29
|
+
exitCode: 2,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/** 지원 대상과 최소 입력 수를 검증해 파일 경로만 돌려준다. */
|
|
33
|
+
function retentionInputPaths(arguments_) {
|
|
34
|
+
const forIndex = arguments_.indexOf('--for');
|
|
35
|
+
if (arguments_[0] !== 'retentions' || forIndex < 3)
|
|
36
|
+
return undefined;
|
|
37
|
+
if (forIndex !== arguments_.length - 2)
|
|
38
|
+
return undefined;
|
|
39
|
+
if (arguments_[forIndex + 1] !== 'cartograph')
|
|
40
|
+
return undefined;
|
|
41
|
+
const paths = arguments_.slice(1, forIndex);
|
|
42
|
+
return paths.some((path) => path.startsWith('-')) ? undefined : paths;
|
|
43
|
+
}
|
|
44
|
+
/** 잘못된 retentions 호출을 사용법과 코드 64로 바꾼다. */
|
|
45
|
+
function retentionUsageError() {
|
|
46
|
+
return {
|
|
47
|
+
standardOutput: '',
|
|
48
|
+
standardError: `${retentionUsage}\n`,
|
|
49
|
+
exitCode: 64,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
/** retentions 명령의 한 줄 사용법이다. */
|
|
53
|
+
export const retentionUsage = 'Usage: isthmus retentions <bridge-facts.json> <bridge-facts.json> '
|
|
54
|
+
+ '[more...] --for cartograph';
|
|
55
|
+
//# sourceMappingURL=retentions-command.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retentions-command.js","sourceRoot":"","sources":["../../src/cli/retentions-command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAChE,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,EACL,kCAAkC,EAClC,kCAAkC,GACnC,MAAM,yBAAyB,CAAC;AAMjC,kDAAkD;AAClD,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,UAA6B,EAC7B,YAA0B,EAC1B,GAAU,EACV,eAAuB;IAEvB,MAAM,UAAU,GAAG,mBAAmB,CAAC,UAAU,CAAC,CAAC;IACnD,IAAI,UAAU,KAAK,SAAS;QAAE,OAAO,mBAAmB,EAAE,CAAC;IAC3D,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC;QAC9D,MAAM,SAAS,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CACnC,wBAAwB,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAC3C,CAAC;QACF,MAAM,MAAM,GAAG,mBAAmB,CAAC,SAAS,CAAC,CAAC;QAC9C,MAAM,UAAU,GAAG,kCAAkC,CACnD,MAAM,EACN,GAAG,EAAE,CAAC,WAAW,EAAE,EACnB,eAAe,CAChB,CAAC;QACF,OAAO;YACL,cAAc,EAAE,kCAAkC,CAAC,UAAU,CAAC;YAC9D,aAAa,EAAE,EAAE;YACjB,QAAQ,EAAE,CAAC;SACZ,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,mBAAmB,EAAE,CAAC;IAC/B,CAAC;AACH,CAAC;AAED,iCAAiC;AACjC,SAAS,mBAAmB;IAC1B,OAAO;QACL,cAAc,EAAE,EAAE;QAClB,aAAa,EACX,mEAAmE;QACrE,QAAQ,EAAE,CAAC;KACZ,CAAC;AACJ,CAAC;AAED,uCAAuC;AACvC,SAAS,mBAAmB,CAAC,UAA6B;IACxD,MAAM,QAAQ,GAAG,UAAU,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAC7C,IAAI,UAAU,CAAC,CAAC,CAAC,KAAK,YAAY,IAAI,QAAQ,GAAG,CAAC;QAAE,OAAO,SAAS,CAAC;IACrE,IAAI,QAAQ,KAAK,UAAU,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,SAAS,CAAC;IACzD,IAAI,UAAU,CAAC,QAAQ,GAAG,CAAC,CAAC,KAAK,YAAY;QAAE,OAAO,SAAS,CAAC;IAChE,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;IAC5C,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC;AACxE,CAAC;AAED,0CAA0C;AAC1C,SAAS,mBAAmB;IAC1B,OAAO;QACL,cAAc,EAAE,EAAE;QAClB,aAAa,EAAE,GAAG,cAAc,IAAI;QACpC,QAAQ,EAAE,EAAE;KACb,CAAC;AACJ,CAAC;AAED,gCAAgC;AAChC,MAAM,CAAC,MAAM,cAAc,GACzB,oEAAoE;MAClE,4BAA4B,CAAC"}
|
package/dist/compare.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compare.js","sourceRoot":"","sources":["../src/compare.ts"],"names":[],"mappings":"AAAA,qDAAqD;AACrD,MAAM,UAAU,cAAc,CAAC,IAAY,EAAE,KAAa;IACxD,IAAI,IAAI,GAAG,KAAK;QAAE,OAAO,CAAC,CAAC,CAAC;IAC5B,IAAI,IAAI,GAAG,KAAK;QAAE,OAAO,CAAC,CAAC;IAC3B,OAAO,CAAC,CAAC;AACX,CAAC"}
|