isthmus-cli 0.1.0 → 0.1.1

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 CHANGED
@@ -18,9 +18,10 @@ isthmus 는 각 언어 도구가 내보낸 **브리지 사실**(채널 이름 ·
18
18
 
19
19
  ## 상태
20
20
 
21
- **0.1.0.** bridge-facts 버전 1 파서와 `check`, `query`, `graph`,
22
- cartograph용 외부 보존 근거 왕복을 구현했다. 다음 단계는 실제 Flutter 앱 도그푸딩과
23
- React Native 지원이다.
21
+ **0.1.1.** bridge-facts 버전 1 파서와 `check`, `query`, `graph`,
22
+ cartograph용 외부 보존 근거 왕복을 구현했다. 외부 입력·혼합 target·그래프 크기와
23
+ Dart/Swift Phase 0 추출 경계를 fail-closed로 강화했다. 다음 단계는 실제 Flutter 앱
24
+ 도그푸딩과 React Native 지원이다.
24
25
 
25
26
  | 문서 | 내용 |
26
27
  |---|---|
@@ -91,7 +92,7 @@ node dist/cli/main.js retentions \
91
92
  cartograph dead --external-retentions external-retentions.json
92
93
  ```
93
94
 
94
- `retentions`는 핸들러의 USR을 우선 사용하고 없으면 `qualifiedName`을 남긴다. `mixed-targets` 문서는 v1에서 사실별 target을 복원할 수 없어 안전하게 조인을 보류한다. 먼저 생산 단계에서 target별 문서로 분리해야 한다.
95
+ `retentions`는 핸들러의 USR을 우선 사용하고 없으면 `qualifiedName`을 남긴다. `mixed-targets` 문서는 v1에서 사실별 target을 복원할 수 없어 모든 소비 명령이 종료 코드 2로 조인을 보류한다. 먼저 생산 단계에서 target별 문서로 분리해야 한다.
95
96
 
96
97
  채널이나 메서드가 경계 반대편의 어느 위치와 연결되는지 조회하려면:
97
98
 
@@ -109,7 +110,9 @@ node dist/cli/main.js graph dart-bridges.json swift-bridges.json --format mermai
109
110
 
110
111
  `query`는 같은 메서드가 여러 채널에 있으면 후보를 반환하고 임의로 고르지 않는다.
111
112
  반환된 `qualifiedName`을 같은 subject 자리에 넣어 정확한 후보를 다시 조회할 수 있다.
112
- `graph`는 매치된 간선만 내보내며, 입력의 `limitations`를 함께 보존한다.
113
+ `graph`는 매치된 간선만 내보내며, 입력의 `limitations`를 JSON 필드 또는 DOT/Mermaid
114
+ 주석으로 보존한다. 증거의 Cartesian 곱이 100,000개 간선을 넘으면 메모리 폭증을 막기
115
+ 위해 입력 오류로 종료한다.
113
116
 
114
117
  출력은 `isthmus-check` 버전 1 JSON이며 다음 세 사실을 보고한다.
115
118
 
@@ -123,8 +126,8 @@ node dist/cli/main.js graph dart-bridges.json swift-bridges.json --format mermai
123
126
  |---|---|
124
127
  | `0` | 실행 성공. 기본 모드에서는 이슈가 있어도 보고만 함 |
125
128
  | `1` | `--strict`에서 error 이슈를 발견함 |
126
- | `2` | 파일 읽기, JSON, 교환 계약 등 도구 실패 |
127
- | `64` | 잘못된 명령, 옵션, 입력 개수 |
129
+ | `2` | 파일 읽기, JSON, 교환 계약, 보류된 조인 등 도구 실패 |
130
+ | `64` | 잘못된 명령·옵션·입력 개수 또는 `query`의 `notFound`·`ambiguous` |
128
131
 
129
132
  개발 검증은 타입 체크, 제품 코드 90% 커버리지, 빌드, 실제 CLI 계약을 함께 실행한다.
130
133
 
@@ -8,5 +8,7 @@ export interface CommandResult {
8
8
  }
9
9
  /** check 인자를 실행해 프로세스에 독립적인 결과를 반환한다. */
10
10
  export declare function runCheckCommand(arguments_: readonly string[], readTextFile: ReadTextFile): Promise<CommandResult>;
11
+ /** 전체 조인 보류를 깨끗한 결과와 구분하는 코드 2 결과로 바꾼다. */
12
+ export declare function bridgeJoinDeferredError(): CommandResult;
11
13
  /** check 명령의 한 줄 사용법이다. */
12
14
  export declare const checkUsage: string;
@@ -1,5 +1,5 @@
1
1
  import { parseBridgeFactsDocument } from "../exchange/parse.js";
2
- import { joinBridgeDocuments } from "../join/join.js";
2
+ import { isBridgeJoinDeferred, joinBridgeDocuments } from "../join/join.js";
3
3
  import { createCheckReport, encodeCheckReport } from "../report/check-report.js";
4
4
  /** check 인자를 실행해 프로세스에 독립적인 결과를 반환한다. */
5
5
  export async function runCheckCommand(arguments_, readTextFile) {
@@ -15,7 +15,10 @@ export async function runCheckCommand(arguments_, readTextFile) {
15
15
  try {
16
16
  const texts = await Promise.all(inputPaths.map(readTextFile));
17
17
  const documents = texts.map((text) => parseBridgeFactsDocument(JSON.parse(text)));
18
- const report = createCheckReport(joinBridgeDocuments(documents));
18
+ const joined = joinBridgeDocuments(documents);
19
+ if (isBridgeJoinDeferred(joined))
20
+ return bridgeJoinDeferredError();
21
+ const report = createCheckReport(joined);
19
22
  return {
20
23
  standardOutput: encodeCheckReport(report),
21
24
  standardError: '',
@@ -26,6 +29,14 @@ export async function runCheckCommand(arguments_, readTextFile) {
26
29
  return inputError();
27
30
  }
28
31
  }
32
+ /** 전체 조인 보류를 깨끗한 결과와 구분하는 코드 2 결과로 바꾼다. */
33
+ export function bridgeJoinDeferredError() {
34
+ return {
35
+ standardOutput: '',
36
+ standardError: 'Bridge facts could not be joined; split mixed bridge targets and retry.\n',
37
+ exitCode: 2,
38
+ };
39
+ }
29
40
  /** 파일·JSON·계약 오류를 민감정보 없는 코드 2 결과로 바꾼다. */
30
41
  function inputError() {
31
42
  return {
@@ -1 +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"}
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,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAC5E,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,mBAAmB,CAAC,SAAS,CAAC,CAAC;QAC9C,IAAI,oBAAoB,CAAC,MAAM,CAAC;YAAE,OAAO,uBAAuB,EAAE,CAAC;QACnE,MAAM,MAAM,GAAG,iBAAiB,CAAC,MAAM,CAAC,CAAC;QACzC,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,MAAM,UAAU,uBAAuB;IACrC,OAAO;QACL,cAAc,EAAE,EAAE;QAClB,aAAa,EACX,2EAA2E;QAC7E,QAAQ,EAAE,CAAC;KACZ,CAAC;AACJ,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","sourcesContent":["import { parseBridgeFactsDocument } from '../exchange/parse.ts';\nimport { isBridgeJoinDeferred, joinBridgeDocuments } from '../join/join.ts';\nimport { createCheckReport, encodeCheckReport } from '../report/check-report.ts';\n\n/** 파일 경로를 받아 UTF-8 텍스트를 읽는 주입 경계다. */\nexport type ReadTextFile = (path: string) => Promise<string>;\n\n/** CLI가 프로세스 경계에 쓸 출력과 종료 코드다. */\nexport interface CommandResult {\n readonly standardOutput: string;\n readonly standardError: string;\n readonly exitCode: 0 | 1 | 2 | 64;\n}\n\n/** check 인자를 실행해 프로세스에 독립적인 결과를 반환한다. */\nexport async function runCheckCommand(\n arguments_: readonly string[],\n readTextFile: ReadTextFile,\n): Promise<CommandResult> {\n if (arguments_[0] !== 'check') return usageError();\n const options = arguments_.slice(1).filter((argument) => argument.startsWith('-'));\n if (options.some((option) => option !== '--strict')) return usageError();\n const isStrict = arguments_.includes('--strict');\n const inputPaths = arguments_.slice(1).filter((argument) => argument !== '--strict');\n if (inputPaths.length < 2) return usageError();\n try {\n const texts = await Promise.all(inputPaths.map(readTextFile));\n const documents = texts.map((text) =>\n parseBridgeFactsDocument(JSON.parse(text)),\n );\n const joined = joinBridgeDocuments(documents);\n if (isBridgeJoinDeferred(joined)) return bridgeJoinDeferredError();\n const report = createCheckReport(joined);\n return {\n standardOutput: encodeCheckReport(report),\n standardError: '',\n exitCode: isStrict && report.summary.errors > 0 ? 1 : 0,\n };\n } catch {\n return inputError();\n }\n}\n\n/** 전체 조인 보류를 깨끗한 결과와 구분하는 코드 2 결과로 바꾼다. */\nexport function bridgeJoinDeferredError(): CommandResult {\n return {\n standardOutput: '',\n standardError:\n 'Bridge facts could not be joined; split mixed bridge targets and retry.\\n',\n exitCode: 2,\n };\n}\n\n/** 파일·JSON·계약 오류를 민감정보 없는 코드 2 결과로 바꾼다. */\nfunction inputError(): CommandResult {\n return {\n standardOutput: '',\n standardError:\n 'Unable to read or validate bridge facts; check the input files.\\n',\n exitCode: 2,\n };\n}\n\n/** 잘못된 CLI 호출을 경로 없는 사용법과 코드 64로 바꾼다. */\nfunction usageError(): CommandResult {\n return {\n standardOutput: '',\n standardError: `${checkUsage}\\n`,\n exitCode: 64,\n };\n}\n\n/** check 명령의 한 줄 사용법이다. */\nexport const checkUsage =\n 'Usage: isthmus check <bridge-facts.json> <bridge-facts.json> '\n + '[more...] [--strict]';\n"]}
@@ -1,4 +1,4 @@
1
- import type { CommandResult, ReadTextFile } from './check-command.ts';
1
+ import { type CommandResult, type ReadTextFile } from './check-command.ts';
2
2
  /** graph 인자를 실행해 경계 그래프와 종료 코드를 반환한다. */
3
3
  export declare function runGraphCommand(arguments_: readonly string[], readTextFile: ReadTextFile): Promise<CommandResult>;
4
4
  /** graph 명령의 한 줄 사용법이다. */
@@ -1,6 +1,7 @@
1
1
  import { parseBridgeFactsDocument } from "../exchange/parse.js";
2
- import { joinBridgeDocuments } from "../join/join.js";
2
+ import { isBridgeJoinDeferred, joinBridgeDocuments } from "../join/join.js";
3
3
  import { createBridgeGraph, renderBridgeGraph } from "../report/graph.js";
4
+ import { bridgeJoinDeferredError, } from "./check-command.js";
4
5
  /** graph 인자를 실행해 경계 그래프와 종료 코드를 반환한다. */
5
6
  export async function runGraphCommand(arguments_, readTextFile) {
6
7
  const options = parseGraphArguments(arguments_);
@@ -9,7 +10,10 @@ export async function runGraphCommand(arguments_, readTextFile) {
9
10
  try {
10
11
  const texts = await Promise.all(options.inputPaths.map(readTextFile));
11
12
  const documents = texts.map((text) => parseBridgeFactsDocument(JSON.parse(text)));
12
- const graph = createBridgeGraph(joinBridgeDocuments(documents));
13
+ const joined = joinBridgeDocuments(documents);
14
+ if (isBridgeJoinDeferred(joined))
15
+ return bridgeJoinDeferredError();
16
+ const graph = createBridgeGraph(joined);
13
17
  return {
14
18
  standardOutput: renderBridgeGraph(graph, options.format),
15
19
  standardError: '',
@@ -1 +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"}
1
+ {"version":3,"file":"graph-command.js","sourceRoot":"","sources":["../../src/cli/graph-command.ts"],"names":[],"mappings":"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","sourcesContent":["import { parseBridgeFactsDocument } from '../exchange/parse.ts';\nimport { isBridgeJoinDeferred, joinBridgeDocuments } from '../join/join.ts';\nimport { createBridgeGraph, renderBridgeGraph } from '../report/graph.ts';\nimport {\n bridgeJoinDeferredError,\n type CommandResult,\n type ReadTextFile,\n} from './check-command.ts';\n\n/** graph 인자를 실행해 경계 그래프와 종료 코드를 반환한다. */\nexport async function runGraphCommand(\n arguments_: readonly string[],\n readTextFile: ReadTextFile,\n): Promise<CommandResult> {\n const options = parseGraphArguments(arguments_);\n if (options === undefined) return graphUsageError();\n try {\n const texts = await Promise.all(options.inputPaths.map(readTextFile));\n const documents = texts.map((text) =>\n parseBridgeFactsDocument(JSON.parse(text)),\n );\n const joined = joinBridgeDocuments(documents);\n if (isBridgeJoinDeferred(joined)) return bridgeJoinDeferredError();\n const graph = createBridgeGraph(joined);\n return {\n standardOutput: renderBridgeGraph(graph, options.format),\n standardError: '',\n exitCode: 0,\n };\n } catch {\n return graphInputError();\n }\n}\n\n/** graph의 입력 파일과 선택 출력 형식을 검증한다. */\nfunction parseGraphArguments(arguments_: readonly string[]): GraphOptions | undefined {\n if (arguments_[0] !== 'graph') return undefined;\n const formatIndex = arguments_.indexOf('--format');\n const inputPaths = arguments_.slice(1, formatIndex < 0 ? undefined : formatIndex);\n if (inputPaths.length < 2 || inputPaths.some((path) => path.startsWith('-'))) {\n return undefined;\n }\n if (formatIndex < 0) return { inputPaths, format: 'json' };\n if (formatIndex !== arguments_.length - 2) return undefined;\n const format = arguments_[formatIndex + 1];\n return isGraphFormat(format) ? { inputPaths, format } : undefined;\n}\n\n/** 지원하는 그래프 형식인지 확인한다. */\nfunction isGraphFormat(value: string | undefined): value is GraphFormat {\n return value === 'json' || value === 'dot' || value === 'mermaid';\n}\n\n/** graph 사용 오류를 경로 없는 코드 64로 바꾼다. */\nfunction graphUsageError(): CommandResult {\n return {\n standardOutput: '',\n standardError: `${graphUsage}\\n`,\n exitCode: 64,\n };\n}\n\n/** graph 입력 실패를 경로 없는 코드 2로 바꾼다. */\nfunction graphInputError(): CommandResult {\n return {\n standardOutput: '',\n standardError:\n 'Unable to read or validate bridge facts; check the input files.\\n',\n exitCode: 2,\n };\n}\n\n/** 검증된 graph 명령 옵션이다. */\ninterface GraphOptions {\n readonly inputPaths: readonly string[];\n readonly format: GraphFormat;\n}\n\ntype GraphFormat = 'json' | 'dot' | 'mermaid';\n\n/** graph 명령의 한 줄 사용법이다. */\nexport const graphUsage =\n 'Usage: isthmus graph <bridge-facts.json> <bridge-facts.json> '\n + '[more...] [--format json|dot|mermaid]';\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"main.js","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"main.js","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env node\n\nimport { readFile } from 'node:fs/promises';\n\nimport {\n checkUsage,\n runCheckCommand,\n type CommandResult,\n} from './check-command.ts';\nimport { graphUsage, runGraphCommand } from './graph-command.ts';\nimport { queryUsage, runQueryCommand } from './query-command.ts';\nimport {\n retentionUsage,\n runRetentionsCommand,\n} from './retentions-command.ts';\n\nconst commandUsages = new Map([\n ['check', checkUsage],\n ['graph', graphUsage],\n ['query', queryUsage],\n ['retentions', retentionUsage],\n]);\n\nconst rootHelp = `Usage: isthmus <command> [options]\n\nCommands:\n check Report unmatched bridge calls and handlers\n query Find both sides of a channel or method\n graph Render matched boundary edges\n retentions Produce external retention evidence\n\nOptions:\n -h, --help Show help\n --version Show version\n`;\n\nconst arguments_ = process.argv.slice(2);\nconst readTextFile = (path: string) => readFile(path, 'utf8');\nconst informationalResult = await runInformationalCommand(arguments_);\nconst result = informationalResult ?? await dispatchCommand(arguments_);\n\nprocess.stdout.write(result.standardOutput);\nprocess.stderr.write(result.standardError);\nprocess.exitCode = result.exitCode;\n\n/** 도움말과 버전처럼 입력 문서가 필요 없는 명령을 실행한다. */\nasync function runInformationalCommand(\n commandArguments: readonly string[],\n): Promise<CommandResult | undefined> {\n const first = commandArguments[0];\n if (\n commandArguments.length === 1 &&\n (first === '--help' || first === '-h')\n ) {\n return successfulText(rootHelp);\n }\n if (commandArguments.length === 1 && first === '--version') {\n return readVersion();\n }\n if (\n commandArguments.length === 2 &&\n (commandArguments[1] === '--help' || commandArguments[1] === '-h')\n ) {\n const usage = first === undefined ? undefined : commandUsages.get(first);\n if (usage !== undefined) return successfulText(`${usage}\\n`);\n }\n return undefined;\n}\n\n/** 알려진 분석 하위 명령으로만 라우팅한다. */\nasync function dispatchCommand(\n commandArguments: readonly string[],\n): Promise<CommandResult> {\n switch (commandArguments[0]) {\n case 'check':\n return runCheckCommand(commandArguments, readTextFile);\n case 'graph':\n return runGraphCommand(commandArguments, readTextFile);\n case 'query':\n return runQueryCommand(commandArguments, readTextFile);\n case 'retentions': {\n const version = await readPackageVersion();\n return version === undefined\n ? packageMetadataError()\n : runRetentionsCommand(\n commandArguments,\n readTextFile,\n () => new Date(),\n version,\n );\n }\n default:\n return {\n standardOutput: '',\n standardError: rootHelp,\n exitCode: 64,\n };\n }\n}\n\n/** 설치된 package.json을 단일 출처로 사용해 버전을 출력한다. */\nasync function readVersion(): Promise<CommandResult> {\n const version = await readPackageVersion();\n return version === undefined\n ? packageMetadataError()\n : successfulText(`${version}\\n`);\n}\n\n/** 설치된 package.json에서 검증된 버전 문자열을 읽는다. */\nasync function readPackageVersion(): Promise<string | undefined> {\n try {\n const text = await readFile(new URL('../../package.json', import.meta.url), 'utf8');\n const document: unknown = JSON.parse(text);\n return isVersionDocument(document) ? document.version : undefined;\n } catch {\n return undefined;\n }\n}\n\n/** package metadata가 비어 있지 않은 버전을 제공하는지 확인한다. */\nfunction isVersionDocument(value: unknown): value is { readonly version: string } {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'version' in value &&\n typeof value.version === 'string' &&\n value.version.length > 0\n );\n}\n\n/** stdout 텍스트와 성공 종료 코드를 만든다. */\nfunction successfulText(standardOutput: string): CommandResult {\n return { standardOutput, standardError: '', exitCode: 0 };\n}\n\n/** package metadata 실패를 민감정보 없는 코드 2로 바꾼다. */\nfunction packageMetadataError(): CommandResult {\n return {\n standardOutput: '',\n standardError: 'Unable to read package metadata.\\n',\n exitCode: 2,\n };\n}\n"]}
@@ -1,4 +1,4 @@
1
- import type { CommandResult, ReadTextFile } from './check-command.ts';
1
+ import { type CommandResult, type ReadTextFile } from './check-command.ts';
2
2
  /** query 인자를 실행해 bridge 질의 JSON과 종료 코드를 반환한다. */
3
3
  export declare function runQueryCommand(arguments_: readonly string[], readTextFile: ReadTextFile): Promise<CommandResult>;
4
4
  /** query 명령의 한 줄 사용법이다. */
@@ -1,6 +1,7 @@
1
1
  import { parseBridgeFactsDocument } from "../exchange/parse.js";
2
- import { joinBridgeDocuments } from "../join/join.js";
2
+ import { isBridgeJoinDeferred, joinBridgeDocuments } from "../join/join.js";
3
3
  import { createBridgeQuery, encodeBridgeQuery } from "../report/query.js";
4
+ import { bridgeJoinDeferredError, } from "./check-command.js";
4
5
  /** query 인자를 실행해 bridge 질의 JSON과 종료 코드를 반환한다. */
5
6
  export async function runQueryCommand(arguments_, readTextFile) {
6
7
  const requested = arguments_[1];
@@ -15,7 +16,10 @@ export async function runQueryCommand(arguments_, readTextFile) {
15
16
  try {
16
17
  const texts = await Promise.all(inputPaths.map(readTextFile));
17
18
  const documents = texts.map((text) => parseBridgeFactsDocument(JSON.parse(text)));
18
- const query = createBridgeQuery(joinBridgeDocuments(documents), requested);
19
+ const joined = joinBridgeDocuments(documents);
20
+ if (isBridgeJoinDeferred(joined))
21
+ return bridgeJoinDeferredError();
22
+ const query = createBridgeQuery(joined, requested);
19
23
  return {
20
24
  standardOutput: encodeBridgeQuery(query),
21
25
  standardError: '',
@@ -1 +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"}
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,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAC5E,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC1E,OAAO,EACL,uBAAuB,GAGxB,MAAM,oBAAoB,CAAC;AAE5B,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,MAAM,GAAG,mBAAmB,CAAC,SAAS,CAAC,CAAC;QAC9C,IAAI,oBAAoB,CAAC,MAAM,CAAC;YAAE,OAAO,uBAAuB,EAAE,CAAC;QACnE,MAAM,KAAK,GAAG,iBAAiB,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;QACnD,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","sourcesContent":["import { parseBridgeFactsDocument } from '../exchange/parse.ts';\nimport { isBridgeJoinDeferred, joinBridgeDocuments } from '../join/join.ts';\nimport { createBridgeQuery, encodeBridgeQuery } from '../report/query.ts';\nimport {\n bridgeJoinDeferredError,\n type CommandResult,\n type ReadTextFile,\n} from './check-command.ts';\n\n/** query 인자를 실행해 bridge 질의 JSON과 종료 코드를 반환한다. */\nexport async function runQueryCommand(\n arguments_: readonly string[],\n readTextFile: ReadTextFile,\n): Promise<CommandResult> {\n const requested = arguments_[1];\n const inputPaths = arguments_.slice(2);\n if (\n arguments_[0] !== 'query' ||\n requested === undefined ||\n requested.trim().length === 0 ||\n inputPaths.length < 2 ||\n inputPaths.some((path) => path.startsWith('-'))\n ) {\n return queryUsageError();\n }\n try {\n const texts = await Promise.all(inputPaths.map(readTextFile));\n const documents = texts.map((text) =>\n parseBridgeFactsDocument(JSON.parse(text)),\n );\n const joined = joinBridgeDocuments(documents);\n if (isBridgeJoinDeferred(joined)) return bridgeJoinDeferredError();\n const query = createBridgeQuery(joined, requested);\n return {\n standardOutput: encodeBridgeQuery(query),\n standardError: '',\n exitCode: query.status === 'found' ? 0 : 64,\n };\n } catch {\n return queryInputError();\n }\n}\n\n/** 입력 실패를 경로 없는 도구 실패 2로 바꾼다. */\nfunction queryInputError(): CommandResult {\n return {\n standardOutput: '',\n standardError:\n 'Unable to read or validate bridge facts; check the input files.\\n',\n exitCode: 2,\n };\n}\n\n/** 잘못된 query 호출을 사용법과 코드 64로 바꾼다. */\nfunction queryUsageError(): CommandResult {\n return {\n standardOutput: '',\n standardError: `${queryUsage}\\n`,\n exitCode: 64,\n };\n}\n\n/** query 명령의 한 줄 사용법이다. */\nexport const queryUsage =\n 'Usage: isthmus query <channel-or-method> <bridge-facts.json> '\n + '<bridge-facts.json> [more...]';\n"]}
@@ -1,4 +1,4 @@
1
- import type { CommandResult, ReadTextFile } from './check-command.ts';
1
+ import { type CommandResult, type ReadTextFile } from './check-command.ts';
2
2
  /** 생성 시각을 테스트 가능하게 주입하는 시계다. */
3
3
  export type Clock = () => Date;
4
4
  /** retentions 인자를 실행해 cartograph용 보존 문서를 반환한다. */
@@ -1,6 +1,7 @@
1
1
  import { parseBridgeFactsDocument } from "../exchange/parse.js";
2
- import { joinBridgeDocuments } from "../join/join.js";
2
+ import { isBridgeJoinDeferred, joinBridgeDocuments } from "../join/join.js";
3
3
  import { createCartographRetentionsDocument, encodeCartographRetentionsDocument, } from "../report/retentions.js";
4
+ import { bridgeJoinDeferredError, } from "./check-command.js";
4
5
  /** retentions 인자를 실행해 cartograph용 보존 문서를 반환한다. */
5
6
  export async function runRetentionsCommand(arguments_, readTextFile, now, producerVersion) {
6
7
  const inputPaths = retentionInputPaths(arguments_);
@@ -10,6 +11,8 @@ export async function runRetentionsCommand(arguments_, readTextFile, now, produc
10
11
  const texts = await Promise.all(inputPaths.map(readTextFile));
11
12
  const documents = texts.map((text) => parseBridgeFactsDocument(JSON.parse(text)));
12
13
  const joined = joinBridgeDocuments(documents);
14
+ if (isBridgeJoinDeferred(joined))
15
+ return bridgeJoinDeferredError();
13
16
  const retentions = createCartographRetentionsDocument(joined, now().toISOString(), producerVersion);
14
17
  return {
15
18
  standardOutput: encodeCartographRetentionsDocument(retentions),
@@ -1 +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"}
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,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAC5E,OAAO,EACL,kCAAkC,EAClC,kCAAkC,GACnC,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,uBAAuB,GAGxB,MAAM,oBAAoB,CAAC;AAK5B,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,IAAI,oBAAoB,CAAC,MAAM,CAAC;YAAE,OAAO,uBAAuB,EAAE,CAAC;QACnE,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","sourcesContent":["import { parseBridgeFactsDocument } from '../exchange/parse.ts';\nimport { isBridgeJoinDeferred, joinBridgeDocuments } from '../join/join.ts';\nimport {\n createCartographRetentionsDocument,\n encodeCartographRetentionsDocument,\n} from '../report/retentions.ts';\nimport {\n bridgeJoinDeferredError,\n type CommandResult,\n type ReadTextFile,\n} from './check-command.ts';\n\n/** 생성 시각을 테스트 가능하게 주입하는 시계다. */\nexport type Clock = () => Date;\n\n/** retentions 인자를 실행해 cartograph용 보존 문서를 반환한다. */\nexport async function runRetentionsCommand(\n arguments_: readonly string[],\n readTextFile: ReadTextFile,\n now: Clock,\n producerVersion: string,\n): Promise<CommandResult> {\n const inputPaths = retentionInputPaths(arguments_);\n if (inputPaths === undefined) return retentionUsageError();\n try {\n const texts = await Promise.all(inputPaths.map(readTextFile));\n const documents = texts.map((text) =>\n parseBridgeFactsDocument(JSON.parse(text)),\n );\n const joined = joinBridgeDocuments(documents);\n if (isBridgeJoinDeferred(joined)) return bridgeJoinDeferredError();\n const retentions = createCartographRetentionsDocument(\n joined,\n now().toISOString(),\n producerVersion,\n );\n return {\n standardOutput: encodeCartographRetentionsDocument(retentions),\n standardError: '',\n exitCode: 0,\n };\n } catch {\n return retentionInputError();\n }\n}\n\n/** 입력 실패를 경로 없는 도구 실패 2로 바꾼다. */\nfunction retentionInputError(): CommandResult {\n return {\n standardOutput: '',\n standardError:\n 'Unable to read or validate bridge facts; check the input files.\\n',\n exitCode: 2,\n };\n}\n\n/** 지원 대상과 최소 입력 수를 검증해 파일 경로만 돌려준다. */\nfunction retentionInputPaths(arguments_: readonly string[]): string[] | undefined {\n const forIndex = arguments_.indexOf('--for');\n if (arguments_[0] !== 'retentions' || forIndex < 3) return undefined;\n if (forIndex !== arguments_.length - 2) return undefined;\n if (arguments_[forIndex + 1] !== 'cartograph') return undefined;\n const paths = arguments_.slice(1, forIndex);\n return paths.some((path) => path.startsWith('-')) ? undefined : paths;\n}\n\n/** 잘못된 retentions 호출을 사용법과 코드 64로 바꾼다. */\nfunction retentionUsageError(): CommandResult {\n return {\n standardOutput: '',\n standardError: `${retentionUsage}\\n`,\n exitCode: 64,\n };\n}\n\n/** retentions 명령의 한 줄 사용법이다. */\nexport const retentionUsage =\n 'Usage: isthmus retentions <bridge-facts.json> <bridge-facts.json> '\n + '[more...] --for cartograph';\n"]}
@@ -1 +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"}
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","sourcesContent":["/** locale과 ICU 버전에 무관한 UTF-16 코드 단위 문자열 순서를 반환한다. */\nexport function compareStrings(left: string, right: string): number {\n if (left < right) return -1;\n if (left > right) return 1;\n return 0;\n}\n"]}
@@ -18,7 +18,44 @@ export function parseBridgeFactsDocument(input) {
18
18
  throw new BridgeFactsValidationError('Unsupported bridge-facts version; expected version 1.');
19
19
  }
20
20
  validateDocumentMetadata(input);
21
- return input;
21
+ return normalizeDocument(input);
22
+ }
23
+ /** 검증된 입력에서 v1 허용 필드만 복사해 신뢰 경계 밖 데이터를 제거한다. */
24
+ function normalizeDocument(document) {
25
+ return {
26
+ format: 'bridge-facts',
27
+ version: 1,
28
+ tool: { name: document.tool.name, version: document.tool.version },
29
+ generatedAt: document.generatedAt,
30
+ platform: document.platform,
31
+ target: document.target,
32
+ project: document.project,
33
+ facts: document.facts.map(normalizeFact),
34
+ limitations: [...document.limitations],
35
+ };
36
+ }
37
+ /** 사실과 중첩 위치·심볼에서 계약 필드만 보존한다. */
38
+ function normalizeFact(fact) {
39
+ const symbol = fact.symbol === undefined
40
+ ? {}
41
+ : {
42
+ symbol: {
43
+ qualifiedName: fact.symbol.qualifiedName,
44
+ ...(fact.symbol.usr === undefined ? {} : { usr: fact.symbol.usr }),
45
+ },
46
+ };
47
+ return {
48
+ kind: fact.kind,
49
+ channel: fact.channel,
50
+ ...(fact.method === undefined ? {} : { method: fact.method }),
51
+ dynamic: fact.dynamic,
52
+ location: {
53
+ path: fact.location.path,
54
+ line: fact.location.line,
55
+ column: fact.location.column,
56
+ },
57
+ ...symbol,
58
+ };
22
59
  }
23
60
  /** 문서 수준 필드가 v1 타입과 허용값을 따르는지 검증한다. */
24
61
  function validateDocumentMetadata(document) {
@@ -30,20 +67,29 @@ function validateDocumentMetadata(document) {
30
67
  if (document.target !== null && !bridgeTargets.has(document.target)) {
31
68
  fail('Unsupported bridge target.');
32
69
  }
33
- if (!isNonEmptyString(document.project))
70
+ if (!isSafeNonEmptyString(document.project))
34
71
  fail('Invalid project path.');
35
72
  if (!Array.isArray(document.facts))
36
73
  fail('Facts must be an array.');
37
- document.facts.forEach(validateFact);
74
+ document.facts.forEach((fact, index) => validateFact(fact, index, document.platform));
75
+ if ((document.target === null) !== (document.facts.length === 0)) {
76
+ fail('Target must be set exactly when facts are present.');
77
+ }
38
78
  if (!isStringArray(document.limitations))
39
79
  fail('Limitations must be strings.');
40
80
  }
41
81
  /** 사실 하나의 조인 키와 증거 필드를 검증한다. */
42
- function validateFact(value, index) {
82
+ function validateFact(value, index, platform) {
43
83
  if (!isJsonObject(value))
44
84
  fail(`Fact at index ${index} must be a JSON object.`);
45
85
  if (!bridgeFactKinds.has(value.kind))
46
86
  fail(`Invalid fact kind at index ${index}.`);
87
+ if (!isFactKindForPlatform(platform, value.kind)) {
88
+ fail(`Fact kind is not valid for platform at index ${index}.`);
89
+ }
90
+ if (!methodFactKinds.has(value.kind) && value.method !== undefined) {
91
+ fail(`Unexpected method at index ${index}.`);
92
+ }
47
93
  if (value.channel !== null && !isSafeNonEmptyString(value.channel)) {
48
94
  fail(`Invalid fact channel at index ${index}.`);
49
95
  }
@@ -55,6 +101,12 @@ function validateFact(value, index) {
55
101
  validateLocation(value.location, index);
56
102
  validateSymbol(value.symbol, index);
57
103
  }
104
+ /** 호출 측과 수신 측 플랫폼이 생산할 수 있는 fact 종류인지 확인한다. */
105
+ function isFactKindForPlatform(platform, kind) {
106
+ return platform === 'dart' || platform === 'js'
107
+ ? callerFactKinds.has(kind)
108
+ : receiverFactKinds.has(kind);
109
+ }
58
110
  /** 사실 위치가 상대 경로와 1부터 시작하는 줄·열을 갖는지 검증한다. */
59
111
  function validateLocation(value, index) {
60
112
  if (!isJsonObject(value) ||
@@ -87,16 +139,39 @@ function validateSymbol(value, index) {
87
139
  /** 생산 도구 이름과 버전이 비어 있지 않은지 검증한다. */
88
140
  function validateTool(value) {
89
141
  if (!isJsonObject(value) ||
90
- !isNonEmptyString(value.name) ||
91
- !isNonEmptyString(value.version)) {
142
+ !isSafeNonEmptyString(value.name) ||
143
+ !isSafeNonEmptyString(value.version)) {
92
144
  fail('Invalid tool metadata.');
93
145
  }
94
146
  }
95
147
  /** ISO 계열 생성 시각으로 해석할 수 있는지 확인한다. */
96
148
  function isTimestamp(value) {
97
- return (typeof value === 'string' &&
98
- timestampPattern.test(value) &&
99
- !Number.isNaN(Date.parse(value)));
149
+ if (typeof value !== 'string')
150
+ return false;
151
+ const match = timestampPattern.exec(value);
152
+ if (match === null || Number.isNaN(Date.parse(value)))
153
+ return false;
154
+ const year = Number(match[1]);
155
+ const month = Number(match[2]);
156
+ const day = Number(match[3]);
157
+ const hour = Number(match[4]);
158
+ const minute = Number(match[5]);
159
+ const second = Number(match[6]);
160
+ return (month >= 1 &&
161
+ month <= 12 &&
162
+ day >= 1 &&
163
+ day <= daysInMonth(year, month) &&
164
+ hour <= 23 &&
165
+ minute <= 59 &&
166
+ second <= 59);
167
+ }
168
+ /** 윤년을 포함한 주어진 달의 실제 일수를 반환한다. */
169
+ function daysInMonth(year, month) {
170
+ if (month === 2) {
171
+ const isLeapYear = year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
172
+ return isLeapYear ? 29 : 28;
173
+ }
174
+ return [4, 6, 9, 11].includes(month) ? 30 : 31;
100
175
  }
101
176
  /** 공백만 있지 않은 문자열인지 확인한다. */
102
177
  function isNonEmptyString(value) {
@@ -112,7 +187,7 @@ function isStringArray(value) {
112
187
  }
113
188
  /** 1부터 시작하는 정수인지 확인한다. */
114
189
  function isPositiveInteger(value) {
115
- return Number.isInteger(value) && Number(value) >= 1;
190
+ return Number.isSafeInteger(value) && Number(value) >= 1;
116
191
  }
117
192
  /** 입력 값을 포함하지 않는 검증 오류를 던진다. */
118
193
  function fail(message) {
@@ -137,9 +212,21 @@ const bridgeFactKinds = new Set([
137
212
  'component-export',
138
213
  'component-require',
139
214
  ]);
215
+ const callerFactKinds = new Set([
216
+ 'channel-create',
217
+ 'method-invoke',
218
+ 'module-import',
219
+ 'component-require',
220
+ ]);
221
+ const receiverFactKinds = new Set([
222
+ 'channel-register',
223
+ 'method-handle',
224
+ 'module-export',
225
+ 'component-export',
226
+ ]);
140
227
  /** method 필드가 필수인 사실 종류다. */
141
228
  const methodFactKinds = new Set(['method-invoke', 'method-handle']);
142
- const timestampPattern = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/u;
229
+ const timestampPattern = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/u;
143
230
  const controlCharacterPattern = /[\u0000-\u001f\u007f]/u;
144
231
  /** 배열과 null을 제외한 JSON 객체인지 확인한다. */
145
232
  function isJsonObject(value) {
@@ -1 +1 @@
1
- {"version":3,"file":"parse.js","sourceRoot":"","sources":["../../src/exchange/parse.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"parse.js","sourceRoot":"","sources":["../../src/exchange/parse.ts"],"names":[],"mappings":"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","sourcesContent":["/** bridge-facts 생산 플랫폼이다. */\nexport type BridgePlatform = 'dart' | 'swift' | 'kotlin' | 'js';\n\n/** 언어 경계를 잇는 메커니즘이다. */\nexport type BridgeTarget = 'flutter' | 'react-native' | 'capacitor';\n\n/** 교환 형식의 사실 종류다. */\nexport type BridgeFactKind =\n | 'channel-create'\n | 'channel-register'\n | 'method-invoke'\n | 'method-handle'\n | 'module-export'\n | 'module-import'\n | 'component-export'\n | 'component-require';\n\n/** 사실의 프로젝트 상대 소스 위치다. */\nexport interface BridgeLocation {\n readonly path: string;\n readonly line: number;\n readonly column: number;\n}\n\n/** 사실을 감싸는 언어별 선언 식별자다. */\nexport interface BridgeSymbol {\n readonly qualifiedName: string;\n readonly usr?: string;\n}\n\n/** 생산 도구 하나가 관찰한 언어 경계 사실이다. */\nexport interface BridgeFact {\n readonly kind: BridgeFactKind;\n readonly channel: string | null;\n readonly method?: string;\n readonly dynamic: boolean;\n readonly location: BridgeLocation;\n readonly symbol?: BridgeSymbol;\n}\n\n/** bridge-facts 버전 1 문서다. */\nexport interface BridgeFactsDocument {\n readonly format: 'bridge-facts';\n readonly version: 1;\n readonly tool: Readonly<{ name: string; version: string }>;\n readonly generatedAt: string;\n readonly platform: BridgePlatform;\n readonly target: BridgeTarget | null;\n readonly project: string;\n readonly facts: readonly BridgeFact[];\n readonly limitations: readonly string[];\n}\n\n/** 외부 교환 문서가 v1 계약을 어겼음을 나타낸다. */\nexport class BridgeFactsValidationError extends Error {\n /** 입력 내용을 노출하지 않는 안전한 메시지를 보존한다. */\n constructor(message: string) {\n super(message);\n this.name = 'BridgeFactsValidationError';\n }\n}\n\n/** 신뢰하지 않는 JSON 값을 검증된 bridge-facts v1 문서로 바꾼다. */\nexport function parseBridgeFactsDocument(input: unknown): BridgeFactsDocument {\n if (!isJsonObject(input)) {\n throw new BridgeFactsValidationError(\n 'Bridge facts must be a JSON object.',\n );\n }\n if (input.format !== 'bridge-facts') {\n throw new BridgeFactsValidationError('Expected format \"bridge-facts\".');\n }\n if (input.version !== 1) {\n throw new BridgeFactsValidationError(\n 'Unsupported bridge-facts version; expected version 1.',\n );\n }\n validateDocumentMetadata(input);\n return normalizeDocument(input);\n}\n\n/** 검증된 입력에서 v1 허용 필드만 복사해 신뢰 경계 밖 데이터를 제거한다. */\nfunction normalizeDocument(document: BridgeFactsDocument): BridgeFactsDocument {\n return {\n format: 'bridge-facts',\n version: 1,\n tool: { name: document.tool.name, version: document.tool.version },\n generatedAt: document.generatedAt,\n platform: document.platform,\n target: document.target,\n project: document.project,\n facts: document.facts.map(normalizeFact),\n limitations: [...document.limitations],\n };\n}\n\n/** 사실과 중첩 위치·심볼에서 계약 필드만 보존한다. */\nfunction normalizeFact(fact: BridgeFact): BridgeFact {\n const symbol = fact.symbol === undefined\n ? {}\n : {\n symbol: {\n qualifiedName: fact.symbol.qualifiedName,\n ...(fact.symbol.usr === undefined ? {} : { usr: fact.symbol.usr }),\n },\n };\n return {\n kind: fact.kind,\n channel: fact.channel,\n ...(fact.method === undefined ? {} : { method: fact.method }),\n dynamic: fact.dynamic,\n location: {\n path: fact.location.path,\n line: fact.location.line,\n column: fact.location.column,\n },\n ...symbol,\n };\n}\n\n/** 문서 수준 필드가 v1 타입과 허용값을 따르는지 검증한다. */\nfunction validateDocumentMetadata(\n document: Record<string, unknown>,\n): asserts document is Record<string, unknown> & BridgeFactsDocument {\n validateTool(document.tool);\n if (!isTimestamp(document.generatedAt)) fail('Invalid generatedAt timestamp.');\n if (!bridgePlatforms.has(document.platform)) fail('Unsupported bridge platform.');\n if (document.target !== null && !bridgeTargets.has(document.target)) {\n fail('Unsupported bridge target.');\n }\n if (!isSafeNonEmptyString(document.project)) fail('Invalid project path.');\n if (!Array.isArray(document.facts)) fail('Facts must be an array.');\n document.facts.forEach((fact, index) =>\n validateFact(fact, index, document.platform),\n );\n if ((document.target === null) !== (document.facts.length === 0)) {\n fail('Target must be set exactly when facts are present.');\n }\n if (!isStringArray(document.limitations)) fail('Limitations must be strings.');\n}\n\n/** 사실 하나의 조인 키와 증거 필드를 검증한다. */\nfunction validateFact(value: unknown, index: number, platform: unknown): void {\n if (!isJsonObject(value)) fail(`Fact at index ${index} must be a JSON object.`);\n if (!bridgeFactKinds.has(value.kind)) fail(`Invalid fact kind at index ${index}.`);\n if (!isFactKindForPlatform(platform, value.kind)) {\n fail(`Fact kind is not valid for platform at index ${index}.`);\n }\n if (!methodFactKinds.has(value.kind) && value.method !== undefined) {\n fail(`Unexpected method at index ${index}.`);\n }\n if (value.channel !== null && !isSafeNonEmptyString(value.channel)) {\n fail(`Invalid fact channel at index ${index}.`);\n }\n if (methodFactKinds.has(value.kind) && !isSafeNonEmptyString(value.method)) {\n fail(`Method fact at index ${index} requires a method name.`);\n }\n if (typeof value.dynamic !== 'boolean') fail(`Invalid dynamic flag at index ${index}.`);\n validateLocation(value.location, index);\n validateSymbol(value.symbol, index);\n}\n\n/** 호출 측과 수신 측 플랫폼이 생산할 수 있는 fact 종류인지 확인한다. */\nfunction isFactKindForPlatform(platform: unknown, kind: unknown): boolean {\n return platform === 'dart' || platform === 'js'\n ? callerFactKinds.has(kind)\n : receiverFactKinds.has(kind);\n}\n\n/** 사실 위치가 상대 경로와 1부터 시작하는 줄·열을 갖는지 검증한다. */\nfunction validateLocation(value: unknown, index: number): void {\n if (\n !isJsonObject(value) ||\n !isProjectRelativePath(value.path) ||\n !isPositiveInteger(value.line) ||\n !isPositiveInteger(value.column)\n ) {\n fail(`Invalid fact location at index ${index}.`);\n }\n}\n\n/** 절대·상위 경로와 제어 문자를 제외한 프로젝트 상대 경로인지 확인한다. */\nfunction isProjectRelativePath(value: unknown): value is string {\n if (!isNonEmptyString(value)) return false;\n if (/^(?:[/\\\\]|[A-Za-z]:)/u.test(value)) return false;\n if (/[\\u0000-\\u001f\\u007f]/u.test(value)) return false;\n return !value.split(/[/\\\\]/u).includes('..');\n}\n\n/** 선택 symbol의 이름과 USR이 비어 있지 않은지 검증한다. */\nfunction validateSymbol(value: unknown, index: number): void {\n if (value === undefined) return;\n if (\n !isJsonObject(value) ||\n !isSafeNonEmptyString(value.qualifiedName) ||\n (value.usr !== undefined && !isSafeNonEmptyString(value.usr))\n ) {\n fail(`Invalid fact symbol at index ${index}.`);\n }\n}\n\n/** 생산 도구 이름과 버전이 비어 있지 않은지 검증한다. */\nfunction validateTool(value: unknown): void {\n if (\n !isJsonObject(value) ||\n !isSafeNonEmptyString(value.name) ||\n !isSafeNonEmptyString(value.version)\n ) {\n fail('Invalid tool metadata.');\n }\n}\n\n/** ISO 계열 생성 시각으로 해석할 수 있는지 확인한다. */\nfunction isTimestamp(value: unknown): value is string {\n if (typeof value !== 'string') return false;\n const match = timestampPattern.exec(value);\n if (match === null || Number.isNaN(Date.parse(value))) return false;\n const year = Number(match[1]);\n const month = Number(match[2]);\n const day = Number(match[3]);\n const hour = Number(match[4]);\n const minute = Number(match[5]);\n const second = Number(match[6]);\n return (\n month >= 1 &&\n month <= 12 &&\n day >= 1 &&\n day <= daysInMonth(year, month) &&\n hour <= 23 &&\n minute <= 59 &&\n second <= 59\n );\n}\n\n/** 윤년을 포함한 주어진 달의 실제 일수를 반환한다. */\nfunction daysInMonth(year: number, month: number): number {\n if (month === 2) {\n const isLeapYear = year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);\n return isLeapYear ? 29 : 28;\n }\n return [4, 6, 9, 11].includes(month) ? 30 : 31;\n}\n\n/** 공백만 있지 않은 문자열인지 확인한다. */\nfunction isNonEmptyString(value: unknown): value is string {\n return typeof value === 'string' && value.trim().length > 0;\n}\n\n/** 조인 키를 깨뜨리는 제어 문자가 없는 비어 있지 않은 문자열인지 확인한다. */\nfunction isSafeNonEmptyString(value: unknown): value is string {\n return isNonEmptyString(value) && !controlCharacterPattern.test(value);\n}\n\n/** 모든 원소가 문자열인 배열인지 확인한다. */\nfunction isStringArray(value: unknown): value is readonly string[] {\n return Array.isArray(value) && value.every((item) => typeof item === 'string');\n}\n\n/** 1부터 시작하는 정수인지 확인한다. */\nfunction isPositiveInteger(value: unknown): value is number {\n return Number.isSafeInteger(value) && Number(value) >= 1;\n}\n\n/** 입력 값을 포함하지 않는 검증 오류를 던진다. */\nfunction fail(message: string): never {\n throw new BridgeFactsValidationError(message);\n}\n\n/** 지원하는 생산 플랫폼 집합이다. */\nconst bridgePlatforms = new Set<unknown>(['dart', 'swift', 'kotlin', 'js']);\n\n/** 지원하는 브리지 메커니즘 집합이다. */\nconst bridgeTargets = new Set<unknown>([\n 'flutter',\n 'react-native',\n 'capacitor',\n]);\n\n/** 버전 1이 정의한 사실 종류 집합이다. */\nconst bridgeFactKinds = new Set<unknown>([\n 'channel-create',\n 'channel-register',\n 'method-invoke',\n 'method-handle',\n 'module-export',\n 'module-import',\n 'component-export',\n 'component-require',\n]);\n\nconst callerFactKinds = new Set<unknown>([\n 'channel-create',\n 'method-invoke',\n 'module-import',\n 'component-require',\n]);\nconst receiverFactKinds = new Set<unknown>([\n 'channel-register',\n 'method-handle',\n 'module-export',\n 'component-export',\n]);\n\n/** method 필드가 필수인 사실 종류다. */\nconst methodFactKinds = new Set<unknown>(['method-invoke', 'method-handle']);\n\nconst timestampPattern =\n /^(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2}):(\\d{2})(?:\\.\\d+)?(?:Z|[+-]\\d{2}:\\d{2})$/u;\nconst controlCharacterPattern = /[\\u0000-\\u001f\\u007f]/u;\n\n/** 배열과 null을 제외한 JSON 객체인지 확인한다. */\nfunction isJsonObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n"]}
@@ -48,6 +48,7 @@ export interface JoinLimitation {
48
48
  }
49
49
  /** 검증된 교환 문서들의 논리 조인 결과다. */
50
50
  export interface BridgeJoinResult {
51
+ readonly deferred: boolean;
51
52
  readonly matchedChannels: readonly MatchedChannel[];
52
53
  readonly unregisteredChannelCreations: readonly UnregisteredChannelCreation[];
53
54
  readonly matchedMethods: readonly MatchedMethod[];
@@ -55,5 +56,12 @@ export interface BridgeJoinResult {
55
56
  readonly handlersWithoutInvocations: readonly HandlerWithoutInvocation[];
56
57
  readonly limitations: readonly JoinLimitation[];
57
58
  }
59
+ /** 함께 조인할 입력 문서 집합이 논리 계약을 위반했음을 나타낸다. */
60
+ export declare class BridgeJoinValidationError extends Error {
61
+ /** 입력 경로를 노출하지 않는 안전한 메시지를 보존한다. */
62
+ constructor(message: string);
63
+ }
58
64
  /** 위치가 아니라 문자열 키로 검증된 교환 문서를 조인한다. */
59
65
  export declare function joinBridgeDocuments(documents: readonly BridgeFactsDocument[]): BridgeJoinResult;
66
+ /** 입력 한계 때문에 조인 전체가 보류된 결과인지 확인한다. */
67
+ export declare function isBridgeJoinDeferred(joined: BridgeJoinResult): boolean;
package/dist/join/join.js CHANGED
@@ -1,6 +1,15 @@
1
1
  import { compareStrings } from "../compare.js";
2
+ /** 함께 조인할 입력 문서 집합이 논리 계약을 위반했음을 나타낸다. */
3
+ export class BridgeJoinValidationError extends Error {
4
+ /** 입력 경로를 노출하지 않는 안전한 메시지를 보존한다. */
5
+ constructor(message) {
6
+ super(message);
7
+ this.name = 'BridgeJoinValidationError';
8
+ }
9
+ }
2
10
  /** 위치가 아니라 문자열 키로 검증된 교환 문서를 조인한다. */
3
11
  export function joinBridgeDocuments(documents) {
12
+ validateProjects(documents);
4
13
  const limitations = collectLimitations(documents);
5
14
  if (documents.some(hasMixedTargets))
6
15
  return emptyJoinResult(limitations);
@@ -35,6 +44,7 @@ export function joinBridgeDocuments(documents) {
35
44
  }))
36
45
  .sort(compareMethodKeys);
37
46
  return {
47
+ deferred: false,
38
48
  matchedChannels,
39
49
  unregisteredChannelCreations,
40
50
  matchedMethods,
@@ -43,6 +53,16 @@ export function joinBridgeDocuments(documents) {
43
53
  limitations,
44
54
  };
45
55
  }
56
+ /** 입력 한계 때문에 조인 전체가 보류된 결과인지 확인한다. */
57
+ export function isBridgeJoinDeferred(joined) {
58
+ return joined.deferred;
59
+ }
60
+ /** 한 번의 조인 입력이 정확히 하나의 project를 기술하는지 검증한다. */
61
+ function validateProjects(documents) {
62
+ if (new Set(documents.map(({ project }) => project)).size > 1) {
63
+ throw new BridgeJoinValidationError('Bridge documents must describe the same project.');
64
+ }
65
+ }
46
66
  /** 사실별 target이 없는 혼합 문서인지 확인한다. */
47
67
  function hasMixedTargets(document) {
48
68
  return document.limitations.some((message) => message.startsWith('mixed-targets:'));
@@ -50,6 +70,7 @@ function hasMixedTargets(document) {
50
70
  /** 안전하게 조인을 보류하면서 입력 한계만 전달한다. */
51
71
  function emptyJoinResult(limitations) {
52
72
  return {
73
+ deferred: true,
53
74
  matchedChannels: [],
54
75
  unregisteredChannelCreations: [],
55
76
  matchedMethods: [],
@@ -1 +1 @@
1
- {"version":3,"file":"join.js","sourceRoot":"","sources":["../../src/join/join.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"join.js","sourceRoot":"","sources":["../../src/join/join.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n BridgeFactsDocument,\n BridgeLocation,\n BridgePlatform,\n BridgeSymbol,\n BridgeTarget,\n} from '../exchange/parse.ts';\nimport { compareStrings } from '../compare.ts';\n\n/** 한 언어 문서가 제공한 브리지 증거 위치다. */\nexport interface BridgeEndpoint {\n readonly platform: BridgePlatform;\n readonly location: BridgeLocation;\n readonly symbol?: BridgeSymbol;\n}\n\n/** 논리 채널 하나에 모인 양쪽 생성·등록 증거다. */\nexport interface MatchedChannel {\n readonly target: BridgeTarget;\n readonly channel: string;\n readonly creations: readonly BridgeEndpoint[];\n readonly registrations: readonly BridgeEndpoint[];\n}\n\n/** 등록을 찾지 못한 논리 채널과 모든 생성 증거다. */\nexport interface UnregisteredChannelCreation {\n readonly target: BridgeTarget;\n readonly channel: string;\n readonly creations: readonly BridgeEndpoint[];\n}\n\n/** 논리 메서드 하나에 모인 양쪽 호출·핸들러 증거다. */\nexport interface MatchedMethod {\n readonly target: BridgeTarget;\n readonly channel: string;\n readonly method: string;\n readonly invocations: readonly BridgeEndpoint[];\n readonly handlers: readonly BridgeEndpoint[];\n}\n\n/** 핸들러를 찾지 못한 논리 호출과 모든 호출 증거다. */\nexport interface UnhandledInvocation {\n readonly target: BridgeTarget;\n readonly channel: string;\n readonly method: string;\n readonly invocations: readonly BridgeEndpoint[];\n}\n\n/** 호출자를 찾지 못한 논리 핸들러와 모든 네이티브 증거다. */\nexport interface HandlerWithoutInvocation {\n readonly target: BridgeTarget;\n readonly channel: string;\n readonly method: string;\n readonly handlers: readonly BridgeEndpoint[];\n}\n\n/** 생산 문서가 밝힌 분석 한계와 출처다. */\nexport interface JoinLimitation {\n readonly platform: BridgePlatform | 'cross-platform';\n readonly tool: string;\n readonly message: string;\n}\n\n/** 검증된 교환 문서들의 논리 조인 결과다. */\nexport interface BridgeJoinResult {\n readonly deferred: boolean;\n readonly matchedChannels: readonly MatchedChannel[];\n readonly unregisteredChannelCreations: readonly UnregisteredChannelCreation[];\n readonly matchedMethods: readonly MatchedMethod[];\n readonly unhandledInvocations: readonly UnhandledInvocation[];\n readonly handlersWithoutInvocations: readonly HandlerWithoutInvocation[];\n readonly limitations: readonly JoinLimitation[];\n}\n\n/** 함께 조인할 입력 문서 집합이 논리 계약을 위반했음을 나타낸다. */\nexport class BridgeJoinValidationError extends Error {\n /** 입력 경로를 노출하지 않는 안전한 메시지를 보존한다. */\n constructor(message: string) {\n super(message);\n this.name = 'BridgeJoinValidationError';\n }\n}\n\n/** 위치가 아니라 문자열 키로 검증된 교환 문서를 조인한다. */\nexport function joinBridgeDocuments(\n documents: readonly BridgeFactsDocument[],\n): BridgeJoinResult {\n validateProjects(documents);\n const limitations = collectLimitations(documents);\n if (documents.some(hasMixedTargets)) return emptyJoinResult(limitations);\n const groups = collectChannelGroups(documents);\n const matchedChannels = [...groups.values()]\n .filter((group) => group.creations.length > 0 && group.registrations.length > 0)\n .sort(compareChannels);\n const unregisteredChannelCreations = [...groups.values()]\n .filter((group) => group.creations.length > 0 && group.registrations.length === 0)\n .map(({ target, channel, creations }) => ({ target, channel, creations }))\n .sort(compareChannels);\n const methodGroups = collectMethodGroups(documents);\n const matchedMethods = [...methodGroups.values()]\n .filter((group) => group.invocations.length > 0 && group.handlers.length > 0)\n .sort(compareMethodKeys);\n const unhandledInvocations = [...methodGroups.values()]\n .filter((group) => group.invocations.length > 0 && group.handlers.length === 0)\n .map(({ target, channel, method, invocations }) => ({\n target,\n channel,\n method,\n invocations,\n }))\n .sort(compareMethodKeys);\n const handlersWithoutInvocations = [...methodGroups.values()]\n .filter((group) => group.handlers.length > 0 && group.invocations.length === 0)\n .map(({ target, channel, method, handlers }) => ({\n target,\n channel,\n method,\n handlers,\n }))\n .sort(compareMethodKeys);\n return {\n deferred: false,\n matchedChannels,\n unregisteredChannelCreations,\n matchedMethods,\n unhandledInvocations,\n handlersWithoutInvocations,\n limitations,\n };\n}\n\n/** 입력 한계 때문에 조인 전체가 보류된 결과인지 확인한다. */\nexport function isBridgeJoinDeferred(joined: BridgeJoinResult): boolean {\n return joined.deferred;\n}\n\n/** 한 번의 조인 입력이 정확히 하나의 project를 기술하는지 검증한다. */\nfunction validateProjects(documents: readonly BridgeFactsDocument[]): void {\n if (new Set(documents.map(({ project }) => project)).size > 1) {\n throw new BridgeJoinValidationError(\n 'Bridge documents must describe the same project.',\n );\n }\n}\n\n/** 사실별 target이 없는 혼합 문서인지 확인한다. */\nfunction hasMixedTargets(document: BridgeFactsDocument): boolean {\n return document.limitations.some((message) => message.startsWith('mixed-targets:'));\n}\n\n/** 안전하게 조인을 보류하면서 입력 한계만 전달한다. */\nfunction emptyJoinResult(limitations: readonly JoinLimitation[]): BridgeJoinResult {\n return {\n deferred: true,\n matchedChannels: [],\n unregisteredChannelCreations: [],\n matchedMethods: [],\n unhandledInvocations: [],\n handlersWithoutInvocations: [],\n limitations,\n };\n}\n\n/** 입력 limitation에 생산 플랫폼과 도구 이름을 붙여 정렬한다. */\nfunction collectLimitations(\n documents: readonly BridgeFactsDocument[],\n): JoinLimitation[] {\n const limitations: JoinLimitation[] = documents.flatMap((document) =>\n document.limitations.map((message) => ({\n platform: document.platform,\n tool: document.tool.name,\n message,\n })),\n );\n const freshness = freshnessLimitation(documents);\n if (freshness !== undefined) limitations.push(freshness);\n return limitations.sort(compareLimitations);\n}\n\n/** 생성 시각 차이가 하루를 넘을 때 교차 입력 한계를 만든다. */\nfunction freshnessLimitation(\n documents: readonly BridgeFactsDocument[],\n): JoinLimitation | undefined {\n if (documents.length < 2) return undefined;\n const timestamps = documents.map((document) => Date.parse(document.generatedAt));\n const difference = Math.max(...timestamps) - Math.min(...timestamps);\n if (difference <= millisecondsPerDay) return undefined;\n const hours = Math.round(difference / millisecondsPerHour);\n return {\n platform: 'cross-platform',\n tool: 'isthmus',\n message: `input-freshness: bridge documents differ by ${hours} hours`,\n };\n}\n\n/** limitation을 플랫폼·도구·문장 순으로 고정한다. */\nfunction compareLimitations(left: JoinLimitation, right: JoinLimitation): number {\n return (\n compareStrings(left.platform, right.platform) ||\n compareStrings(left.tool, right.tool) ||\n compareStrings(left.message, right.message)\n );\n}\n\n/** 메서드 키별로 호출과 핸들러 증거를 모은다. */\nfunction collectMethodGroups(\n documents: readonly BridgeFactsDocument[],\n): Map<string, MutableMethodGroup> {\n const groups = new Map<string, MutableMethodGroup>();\n for (const document of documents) collectDocumentMethods(document, groups);\n for (const group of groups.values()) {\n sortUniqueEndpoints(group.invocations);\n sortUniqueEndpoints(group.handlers);\n }\n return groups;\n}\n\n/** 증거 위치와 심볼을 정렬하고 같은 증거를 한 번만 남긴다. */\nfunction sortUniqueEndpoints(endpoints: BridgeEndpoint[]): void {\n endpoints.sort(compareEndpoints);\n let previous: BridgeEndpoint | undefined;\n const unique = endpoints.filter((endpoint) => {\n const isDuplicate =\n previous !== undefined && compareEndpoints(previous, endpoint) === 0;\n previous = endpoint;\n return !isDuplicate;\n });\n endpoints.splice(0, endpoints.length, ...unique);\n}\n\n/** 증거 위치와 선택 심볼을 완전한 결정 순서로 비교한다. */\nfunction compareEndpoints(left: BridgeEndpoint, right: BridgeEndpoint): number {\n const locationOrder =\n compareStrings(left.platform, right.platform) ||\n compareStrings(left.location.path, right.location.path) ||\n left.location.line - right.location.line ||\n left.location.column - right.location.column;\n if (locationOrder !== 0) return locationOrder;\n if (left.symbol === undefined) return right.symbol === undefined ? 0 : 1;\n if (right.symbol === undefined) return -1;\n return (\n compareStrings(left.symbol.qualifiedName, right.symbol.qualifiedName) ||\n compareOptionalStrings(left.symbol.usr, right.symbol.usr)\n );\n}\n\n/** 존재하는 선택 문자열을 없는 값보다 먼저 두고 비교한다. */\nfunction compareOptionalStrings(\n left: string | undefined,\n right: string | undefined,\n): number {\n if (left === undefined) return right === undefined ? 0 : 1;\n if (right === undefined) return -1;\n return compareStrings(left, right);\n}\n\n/** 문서 하나의 정적 메서드 사실을 그룹에 추가한다. */\nfunction collectDocumentMethods(\n document: BridgeFactsDocument,\n groups: Map<string, MutableMethodGroup>,\n): void {\n if (document.target === null) return;\n for (const fact of document.facts) {\n if (!isStaticMethodFact(fact)) continue;\n const key = `${document.target}\\u0000${fact.channel}\\u0000${fact.method}`;\n const group = groups.get(key) ?? createMethodGroup(document.target, fact.channel, fact.method);\n const endpoints = fact.kind === 'method-invoke' ? group.invocations : group.handlers;\n endpoints.push(toEndpoint(document.platform, fact));\n groups.set(key, group);\n }\n}\n\n/** 조인 가능한 정적 메서드 호출·핸들러 사실인지 확인한다. */\nfunction isStaticMethodFact(\n fact: BridgeFactsDocument['facts'][number],\n): fact is BridgeFactsDocument['facts'][number] & {\n channel: string;\n method: string;\n} {\n return (\n (fact.kind === 'method-invoke' || fact.kind === 'method-handle') &&\n !fact.dynamic &&\n fact.channel !== null &&\n fact.method !== undefined\n );\n}\n\n/** 빈 메서드 그룹을 만든다. */\nfunction createMethodGroup(\n target: BridgeTarget,\n channel: string,\n method: string,\n): MutableMethodGroup {\n return { target, channel, method, invocations: [], handlers: [] };\n}\n\n/** 채널 키별로 생성과 등록 증거를 모은다. */\nfunction collectChannelGroups(\n documents: readonly BridgeFactsDocument[],\n): Map<string, MutableChannelGroup> {\n const groups = new Map<string, MutableChannelGroup>();\n for (const document of documents) collectDocumentChannels(document, groups);\n for (const group of groups.values()) {\n sortUniqueEndpoints(group.creations);\n sortUniqueEndpoints(group.registrations);\n }\n return groups;\n}\n\n/** 문서 하나의 정적 채널 사실을 그룹에 추가한다. */\nfunction collectDocumentChannels(\n document: BridgeFactsDocument,\n groups: Map<string, MutableChannelGroup>,\n): void {\n if (document.target === null) return;\n for (const fact of document.facts) {\n if (!isStaticChannelFact(fact)) continue;\n const key = `${document.target}\\u0000${fact.channel}`;\n const group = groups.get(key) ?? createChannelGroup(document.target, fact.channel);\n const endpoints = fact.kind === 'channel-create' ? group.creations : group.registrations;\n endpoints.push(toEndpoint(document.platform, fact));\n groups.set(key, group);\n }\n}\n\n/** 조인 가능한 정적 채널 생성·등록 사실인지 확인한다. */\nfunction isStaticChannelFact(\n fact: BridgeFactsDocument['facts'][number],\n): fact is BridgeFactsDocument['facts'][number] & { channel: string } {\n return (\n (fact.kind === 'channel-create' || fact.kind === 'channel-register') &&\n !fact.dynamic &&\n fact.channel !== null\n );\n}\n\n/** 빈 채널 그룹을 만든다. */\nfunction createChannelGroup(\n target: BridgeTarget,\n channel: string,\n): MutableChannelGroup {\n return { target, channel, creations: [], registrations: [] };\n}\n\n/** 사실을 플랫폼이 포함된 증거 위치로 바꾼다. */\nfunction toEndpoint(\n platform: BridgePlatform,\n fact: BridgeFactsDocument['facts'][number],\n): BridgeEndpoint {\n return fact.symbol === undefined\n ? { platform, location: fact.location }\n : { platform, location: fact.location, symbol: fact.symbol };\n}\n\n/** 채널 결과를 target과 이름 순으로 고정한다. */\nfunction compareChannels(left: ChannelKey, right: ChannelKey): number {\n return (\n compareStrings(left.target, right.target) ||\n compareStrings(left.channel, right.channel)\n );\n}\n\n/** 결정적 정렬에 필요한 논리 채널 키다. */\ntype ChannelKey = Pick<MatchedChannel, 'target' | 'channel'>;\n\n/** 메서드 결과를 target·채널·메서드 순으로 고정한다. */\nfunction compareMethodKeys(left: MethodKey, right: MethodKey): number {\n return (\n compareStrings(left.target, right.target) ||\n compareStrings(left.channel, right.channel) ||\n compareStrings(left.method, right.method)\n );\n}\n\n/** 결정적 정렬에 필요한 논리 메서드 키다. */\ntype MethodKey = Pick<MatchedMethod, 'target' | 'channel' | 'method'>;\n\n/** 조립 중인 채널 증거 그룹이다. */\ninterface MutableChannelGroup extends MatchedChannel {\n readonly creations: BridgeEndpoint[];\n readonly registrations: BridgeEndpoint[];\n}\n\n/** 조립 중인 메서드 증거 그룹이다. */\ninterface MutableMethodGroup extends MatchedMethod {\n readonly invocations: BridgeEndpoint[];\n readonly handlers: BridgeEndpoint[];\n}\n\nconst millisecondsPerHour = 60 * 60 * 1_000;\nconst millisecondsPerDay = 24 * millisecondsPerHour;\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"check-report.js","sourceRoot":"","sources":["../../src/report/check-report.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAmCpD,sCAAsC;AACtC,MAAM,UAAU,iBAAiB,CAAC,MAAmB;IACnD,OAAO,gBAAgB,CAAC,MAAM,CAAC,CAAC;AAClC,CAAC;AAED,wCAAwC;AACxC,MAAM,UAAU,iBAAiB,CAAC,MAAwB;IACxD,OAAO;QACL,MAAM,EAAE,eAAe;QACvB,OAAO,EAAE,CAAC;QACV,OAAO,EAAE;YACP,MAAM,EACJ,MAAM,CAAC,oBAAoB,CAAC,MAAM;gBAClC,MAAM,CAAC,4BAA4B,CAAC,MAAM;YAC5C,QAAQ,EAAE,MAAM,CAAC,0BAA0B,CAAC,MAAM;YAClD,eAAe,EAAE,MAAM,CAAC,eAAe,CAAC,MAAM;YAC9C,cAAc,EAAE,MAAM,CAAC,cAAc,CAAC,MAAM;SAC7C;QACD,MAAM,EAAE;YACN,GAAG,MAAM,CAAC,oBAAoB,CAAC,GAAG,CAAa,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;gBACxD,QAAQ,EAAE,OAAO;gBACjB,IAAI,EAAE,sBAAsB;gBAC5B,MAAM,EAAE,IAAI,CAAC,MAAM;gBACnB,OAAO,EAAE,IAAI,CAAC,OAAO;gBACrB,MAAM,EAAE,IAAI,CAAC,MAAM;gBACnB,QAAQ,EAAE,IAAI,CAAC,WAAW;aAC3B,CAAC,CAAC;YACH,GAAG,MAAM,CAAC,4BAA4B,CAAC,GAAG,CAAa,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;gBAChE,QAAQ,EAAE,OAAO;gBACjB,IAAI,EAAE,+BAA+B;gBACrC,MAAM,EAAE,IAAI,CAAC,MAAM;gBACnB,OAAO,EAAE,IAAI,CAAC,OAAO;gBACrB,QAAQ,EAAE,IAAI,CAAC,SAAS;aACzB,CAAC,CAAC;YACH,GAAG,MAAM,CAAC,0BAA0B,CAAC,GAAG,CAAa,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;gBAC9D,QAAQ,EAAE,SAAS;gBACnB,IAAI,EAAE,4BAA4B;gBAClC,MAAM,EAAE,IAAI,CAAC,MAAM;gBACnB,OAAO,EAAE,IAAI,CAAC,OAAO;gBACrB,MAAM,EAAE,IAAI,CAAC,MAAM;gBACnB,QAAQ,EAAE,IAAI,CAAC,QAAQ;aACxB,CAAC,CAAC;SACJ;QACD,WAAW,EAAE,MAAM,CAAC,WAAW;KAChC,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"check-report.js","sourceRoot":"","sources":["../../src/report/check-report.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAmCpD,sCAAsC;AACtC,MAAM,UAAU,iBAAiB,CAAC,MAAmB;IACnD,OAAO,gBAAgB,CAAC,MAAM,CAAC,CAAC;AAClC,CAAC;AAED,wCAAwC;AACxC,MAAM,UAAU,iBAAiB,CAAC,MAAwB;IACxD,OAAO;QACL,MAAM,EAAE,eAAe;QACvB,OAAO,EAAE,CAAC;QACV,OAAO,EAAE;YACP,MAAM,EACJ,MAAM,CAAC,oBAAoB,CAAC,MAAM;gBAClC,MAAM,CAAC,4BAA4B,CAAC,MAAM;YAC5C,QAAQ,EAAE,MAAM,CAAC,0BAA0B,CAAC,MAAM;YAClD,eAAe,EAAE,MAAM,CAAC,eAAe,CAAC,MAAM;YAC9C,cAAc,EAAE,MAAM,CAAC,cAAc,CAAC,MAAM;SAC7C;QACD,MAAM,EAAE;YACN,GAAG,MAAM,CAAC,oBAAoB,CAAC,GAAG,CAAa,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;gBACxD,QAAQ,EAAE,OAAO;gBACjB,IAAI,EAAE,sBAAsB;gBAC5B,MAAM,EAAE,IAAI,CAAC,MAAM;gBACnB,OAAO,EAAE,IAAI,CAAC,OAAO;gBACrB,MAAM,EAAE,IAAI,CAAC,MAAM;gBACnB,QAAQ,EAAE,IAAI,CAAC,WAAW;aAC3B,CAAC,CAAC;YACH,GAAG,MAAM,CAAC,4BAA4B,CAAC,GAAG,CAAa,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;gBAChE,QAAQ,EAAE,OAAO;gBACjB,IAAI,EAAE,+BAA+B;gBACrC,MAAM,EAAE,IAAI,CAAC,MAAM;gBACnB,OAAO,EAAE,IAAI,CAAC,OAAO;gBACrB,QAAQ,EAAE,IAAI,CAAC,SAAS;aACzB,CAAC,CAAC;YACH,GAAG,MAAM,CAAC,0BAA0B,CAAC,GAAG,CAAa,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;gBAC9D,QAAQ,EAAE,SAAS;gBACnB,IAAI,EAAE,4BAA4B;gBAClC,MAAM,EAAE,IAAI,CAAC,MAAM;gBACnB,OAAO,EAAE,IAAI,CAAC,OAAO;gBACrB,MAAM,EAAE,IAAI,CAAC,MAAM;gBACnB,QAAQ,EAAE,IAAI,CAAC,QAAQ;aACxB,CAAC,CAAC;SACJ;QACD,WAAW,EAAE,MAAM,CAAC,WAAW;KAChC,CAAC;AACJ,CAAC","sourcesContent":["import type { BridgeTarget } from '../exchange/parse.ts';\nimport type {\n BridgeEndpoint,\n BridgeJoinResult,\n JoinLimitation,\n} from '../join/join.ts';\nimport { encodeSortedJson } from './sorted-json.ts';\n\n/** check 결과 개수를 빠르게 판단할 요약이다. */\nexport interface CheckSummary {\n readonly errors: number;\n readonly warnings: number;\n readonly matchedChannels: number;\n readonly matchedMethods: number;\n}\n\n/** check가 보고하는 안정적인 진단 종류다. */\nexport type CheckIssueCode =\n | 'unhandled-invocation'\n | 'unregistered-channel-creation'\n | 'handler-without-invocation';\n\n/** 삭제 판정 없이 경계 불일치 사실과 증거만 전달한다. */\nexport interface CheckIssue {\n readonly severity: 'error' | 'warning';\n readonly code: CheckIssueCode;\n readonly target: BridgeTarget;\n readonly channel: string;\n readonly method?: string;\n readonly evidence: readonly BridgeEndpoint[];\n}\n\n/** 에이전트와 CI가 소비할 check 문서다. */\nexport interface CheckReport {\n readonly format: 'isthmus-check';\n readonly version: 1;\n readonly summary: CheckSummary;\n readonly issues: readonly CheckIssue[];\n readonly limitations: readonly JoinLimitation[];\n}\n\n/** check 문서를 결정적인 JSON 문자열로 인코딩한다. */\nexport function encodeCheckReport(report: CheckReport): string {\n return encodeSortedJson(report);\n}\n\n/** 조인 결과를 정책 심각도가 포함된 check 문서로 바꾼다. */\nexport function createCheckReport(joined: BridgeJoinResult): CheckReport {\n return {\n format: 'isthmus-check',\n version: 1,\n summary: {\n errors:\n joined.unhandledInvocations.length +\n joined.unregisteredChannelCreations.length,\n warnings: joined.handlersWithoutInvocations.length,\n matchedChannels: joined.matchedChannels.length,\n matchedMethods: joined.matchedMethods.length,\n },\n issues: [\n ...joined.unhandledInvocations.map<CheckIssue>((item) => ({\n severity: 'error',\n code: 'unhandled-invocation',\n target: item.target,\n channel: item.channel,\n method: item.method,\n evidence: item.invocations,\n })),\n ...joined.unregisteredChannelCreations.map<CheckIssue>((item) => ({\n severity: 'error',\n code: 'unregistered-channel-creation',\n target: item.target,\n channel: item.channel,\n evidence: item.creations,\n })),\n ...joined.handlersWithoutInvocations.map<CheckIssue>((item) => ({\n severity: 'warning',\n code: 'handler-without-invocation',\n target: item.target,\n channel: item.channel,\n method: item.method,\n evidence: item.handlers,\n })),\n ],\n limitations: joined.limitations,\n };\n}\n"]}
@@ -24,7 +24,19 @@ export interface BridgeGraphDocument {
24
24
  readonly edges: readonly BridgeGraphEdge[];
25
25
  readonly limitations: readonly JoinLimitation[];
26
26
  }
27
+ /** 한 번에 생성할 수 있는 경계 그래프 간선 수다. */
28
+ export declare const MAX_GRAPH_EDGES = 100000;
29
+ /** 입력 증거의 곱이 안전한 그래프 크기를 넘었음을 나타낸다. */
30
+ export declare class BridgeGraphLimitError extends Error {
31
+ constructor();
32
+ }
33
+ /** 같은 위치 노드가 서로 모순된 심볼을 가졌음을 나타낸다. */
34
+ export declare class BridgeGraphValidationError extends Error {
35
+ constructor(message: string);
36
+ }
27
37
  /** 경계 그래프를 요청한 출력 형식으로 렌더링한다. */
28
38
  export declare function renderBridgeGraph(graph: BridgeGraphDocument, format: 'json' | 'dot' | 'mermaid'): string;
29
39
  /** 조인 결과의 매치만 경계 그래프로 바꾼다. */
30
40
  export declare function createBridgeGraph(joined: BridgeJoinResult): BridgeGraphDocument;
41
+ /** 모든 Cartesian 간선 수를 할당 전에 계산해 메모리 폭증을 막는다. */
42
+ export declare function assertBridgeGraphSize(joined: BridgeJoinResult): void;
@@ -1,5 +1,21 @@
1
1
  import { compareStrings } from "../compare.js";
2
2
  import { encodeSortedJson } from "./sorted-json.js";
3
+ /** 한 번에 생성할 수 있는 경계 그래프 간선 수다. */
4
+ export const MAX_GRAPH_EDGES = 100_000;
5
+ /** 입력 증거의 곱이 안전한 그래프 크기를 넘었음을 나타낸다. */
6
+ export class BridgeGraphLimitError extends Error {
7
+ constructor() {
8
+ super(`Bridge graph exceeds the ${MAX_GRAPH_EDGES} edge limit.`);
9
+ this.name = 'BridgeGraphLimitError';
10
+ }
11
+ }
12
+ /** 같은 위치 노드가 서로 모순된 심볼을 가졌음을 나타낸다. */
13
+ export class BridgeGraphValidationError extends Error {
14
+ constructor(message) {
15
+ super(message);
16
+ this.name = 'BridgeGraphValidationError';
17
+ }
18
+ }
3
19
  /** 경계 그래프를 요청한 출력 형식으로 렌더링한다. */
4
20
  export function renderBridgeGraph(graph, format) {
5
21
  if (format === 'json')
@@ -12,6 +28,9 @@ export function renderBridgeGraph(graph, format) {
12
28
  function renderMermaid(graph) {
13
29
  const identifiers = new Map(graph.nodes.map((node, index) => [node.id, `n${index}`]));
14
30
  const lines = ['flowchart LR'];
31
+ for (const limitation of graph.limitations) {
32
+ lines.push(` %% limitation: ${limitationComment(limitation)}`);
33
+ }
15
34
  for (const node of graph.nodes) {
16
35
  const label = `${mermaidText(node.platform)}<br/>${mermaidText(locationLabel(node.location))}`;
17
36
  lines.push(` ${identifiers.get(node.id)}["${label}"]`);
@@ -37,6 +56,9 @@ function mermaidText(value) {
37
56
  /** Graphviz DOT 문법으로 경계 그래프를 렌더링한다. */
38
57
  function renderDot(graph) {
39
58
  const lines = ['digraph isthmus {', ' rankdir=LR;'];
59
+ for (const limitation of graph.limitations) {
60
+ lines.push(` // limitation: ${limitationComment(limitation)}`);
61
+ }
40
62
  for (const node of graph.nodes) {
41
63
  const label = `${node.platform}\n${locationLabel(node.location)}`;
42
64
  lines.push(` ${dotString(node.id)} [label=${dotString(label)}];`);
@@ -48,6 +70,11 @@ function renderDot(graph) {
48
70
  lines.push('}');
49
71
  return `${lines.join('\n')}\n`;
50
72
  }
73
+ /** 분석 한계를 텍스트 그래프의 한 줄 주석으로 안전하게 만든다. */
74
+ function limitationComment(limitation) {
75
+ const text = `${limitation.platform}/${limitation.tool}: ${limitation.message}`;
76
+ return text.replace(/[\u0000-\u001f\u007f\u2028\u2029]+/gu, ' ');
77
+ }
51
78
  /** DOT 문자열 리터럴로 안전하게 이스케이프한다. */
52
79
  function dotString(value) {
53
80
  return JSON.stringify(value);
@@ -65,6 +92,7 @@ function edgeLabel(edge) {
65
92
  }
66
93
  /** 조인 결과의 매치만 경계 그래프로 바꾼다. */
67
94
  export function createBridgeGraph(joined) {
95
+ assertBridgeGraphSize(joined);
68
96
  const nodes = new Map();
69
97
  const edges = [];
70
98
  addChannelEdges(joined, nodes, edges);
@@ -77,6 +105,21 @@ export function createBridgeGraph(joined) {
77
105
  limitations: joined.limitations,
78
106
  };
79
107
  }
108
+ /** 모든 Cartesian 간선 수를 할당 전에 계산해 메모리 폭증을 막는다. */
109
+ export function assertBridgeGraphSize(joined) {
110
+ let edgeCount = 0;
111
+ const endpointCounts = [
112
+ ...joined.matchedChannels.map(({ creations, registrations }) => [creations.length, registrations.length]),
113
+ ...joined.matchedMethods.map(({ invocations, handlers }) => [invocations.length, handlers.length]),
114
+ ];
115
+ for (const [fromCount, toCount] of endpointCounts) {
116
+ if (toCount > 0 &&
117
+ fromCount > Math.floor((MAX_GRAPH_EDGES - edgeCount) / toCount)) {
118
+ throw new BridgeGraphLimitError();
119
+ }
120
+ edgeCount += fromCount * toCount;
121
+ }
122
+ }
80
123
  /** 매치된 채널의 생성→등록 간선을 추가한다. */
81
124
  function addChannelEdges(joined, nodes, edges) {
82
125
  for (const match of joined.matchedChannels) {
@@ -117,7 +160,8 @@ function addMethodEdges(joined, nodes, edges) {
117
160
  /** 증거 위치를 그래프 노드로 추가하고 안정 ID를 돌려준다. */
118
161
  function addNode(nodes, endpoint) {
119
162
  const id = endpointId(endpoint);
120
- if (!nodes.has(id)) {
163
+ const existing = nodes.get(id);
164
+ if (existing === undefined) {
121
165
  nodes.set(id, endpoint.symbol === undefined
122
166
  ? { id, platform: endpoint.platform, location: endpoint.location }
123
167
  : {
@@ -127,8 +171,29 @@ function addNode(nodes, endpoint) {
127
171
  symbol: endpoint.symbol,
128
172
  });
129
173
  }
174
+ else if (endpoint.symbol !== undefined) {
175
+ const symbol = mergeSymbols(existing.symbol, endpoint.symbol);
176
+ if (symbol === undefined) {
177
+ throw new BridgeGraphValidationError('Bridge graph node has conflicting symbols.');
178
+ }
179
+ nodes.set(id, { ...existing, symbol });
180
+ }
130
181
  return id;
131
182
  }
183
+ /** 호환되는 두 심볼을 더 구체적인 선언 식별자로 병합한다. */
184
+ function mergeSymbols(left, right) {
185
+ if (left === undefined)
186
+ return right;
187
+ if (left.qualifiedName !== right.qualifiedName)
188
+ return undefined;
189
+ if (left.usr !== undefined && right.usr !== undefined && left.usr !== right.usr) {
190
+ return undefined;
191
+ }
192
+ const usr = left.usr ?? right.usr;
193
+ return usr === undefined
194
+ ? { qualifiedName: left.qualifiedName }
195
+ : { qualifiedName: left.qualifiedName, usr };
196
+ }
132
197
  /** 플랫폼과 소스 위치로 실행 간 안정적인 노드 ID를 만든다. */
133
198
  function endpointId(endpoint) {
134
199
  const { path, line, column } = endpoint.location;
@@ -1 +1 @@
1
- {"version":3,"file":"graph.js","sourceRoot":"","sources":["../../src/report/graph.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"graph.js","sourceRoot":"","sources":["../../src/report/graph.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n BridgeLocation,\n BridgePlatform,\n BridgeSymbol,\n BridgeTarget,\n} from '../exchange/parse.ts';\nimport type {\n BridgeEndpoint,\n BridgeJoinResult,\n JoinLimitation,\n} from '../join/join.ts';\nimport { compareStrings } from '../compare.ts';\nimport { encodeSortedJson } from './sorted-json.ts';\n\n/** 소스 위치 하나를 나타내는 경계 그래프 노드다. */\nexport interface BridgeGraphNode {\n readonly id: string;\n readonly platform: BridgePlatform;\n readonly location: BridgeLocation;\n readonly symbol?: BridgeSymbol;\n}\n\n/** 호출 측 위치에서 수신 측 위치로 향하는 경계 간선이다. */\nexport interface BridgeGraphEdge {\n readonly from: string;\n readonly to: string;\n readonly kind: 'channel' | 'method';\n readonly target: BridgeTarget;\n readonly channel: string;\n readonly method?: string;\n}\n\n/** 매치된 경계 간선만 담는 그래프 문서다. */\nexport interface BridgeGraphDocument {\n readonly format: 'isthmus-graph';\n readonly version: 1;\n readonly nodes: readonly BridgeGraphNode[];\n readonly edges: readonly BridgeGraphEdge[];\n readonly limitations: readonly JoinLimitation[];\n}\n\n/** 한 번에 생성할 수 있는 경계 그래프 간선 수다. */\nexport const MAX_GRAPH_EDGES = 100_000;\n\n/** 입력 증거의 곱이 안전한 그래프 크기를 넘었음을 나타낸다. */\nexport class BridgeGraphLimitError extends Error {\n constructor() {\n super(`Bridge graph exceeds the ${MAX_GRAPH_EDGES} edge limit.`);\n this.name = 'BridgeGraphLimitError';\n }\n}\n\n/** 같은 위치 노드가 서로 모순된 심볼을 가졌음을 나타낸다. */\nexport class BridgeGraphValidationError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'BridgeGraphValidationError';\n }\n}\n\n/** 경계 그래프를 요청한 출력 형식으로 렌더링한다. */\nexport function renderBridgeGraph(\n graph: BridgeGraphDocument,\n format: 'json' | 'dot' | 'mermaid',\n): string {\n if (format === 'json') return encodeSortedJson(graph);\n if (format === 'dot') return renderDot(graph);\n return renderMermaid(graph);\n}\n\n/** Mermaid flowchart 문법으로 경계 그래프를 렌더링한다. */\nfunction renderMermaid(graph: BridgeGraphDocument): string {\n const identifiers = new Map(\n graph.nodes.map((node, index) => [node.id, `n${index}`]),\n );\n const lines = ['flowchart LR'];\n for (const limitation of graph.limitations) {\n lines.push(` %% limitation: ${limitationComment(limitation)}`);\n }\n for (const node of graph.nodes) {\n const label = `${mermaidText(node.platform)}<br/>${mermaidText(locationLabel(node.location))}`;\n lines.push(` ${identifiers.get(node.id)}[\"${label}\"]`);\n }\n for (const edge of graph.edges) {\n const from = identifiers.get(edge.from);\n const to = identifiers.get(edge.to);\n lines.push(` ${from} -->|\"${mermaidText(edgeLabel(edge))}\"| ${to}`);\n }\n return `${lines.join('\\n')}\\n`;\n}\n\n/** Mermaid 인용 라벨에서 제어 문자를 HTML 엔터티로 바꾼다. */\nfunction mermaidText(value: string): string {\n return value\n .replaceAll('&', '&amp;')\n .replaceAll('\"', '&quot;')\n .replaceAll('<', '&lt;')\n .replaceAll('>', '&gt;')\n .replaceAll('|', '&#124;')\n .replaceAll('\\r', '&#13;')\n .replaceAll('\\n', '&#10;');\n}\n\n/** Graphviz DOT 문법으로 경계 그래프를 렌더링한다. */\nfunction renderDot(graph: BridgeGraphDocument): string {\n const lines = ['digraph isthmus {', ' rankdir=LR;'];\n for (const limitation of graph.limitations) {\n lines.push(` // limitation: ${limitationComment(limitation)}`);\n }\n for (const node of graph.nodes) {\n const label = `${node.platform}\\n${locationLabel(node.location)}`;\n lines.push(` ${dotString(node.id)} [label=${dotString(label)}];`);\n }\n for (const edge of graph.edges) {\n lines.push(\n ` ${dotString(edge.from)} -> ${dotString(edge.to)} `\n + `[label=${dotString(edgeLabel(edge))}];`,\n );\n }\n lines.push('}');\n return `${lines.join('\\n')}\\n`;\n}\n\n/** 분석 한계를 텍스트 그래프의 한 줄 주석으로 안전하게 만든다. */\nfunction limitationComment(limitation: JoinLimitation): string {\n const text = `${limitation.platform}/${limitation.tool}: ${limitation.message}`;\n return text.replace(/[\\u0000-\\u001f\\u007f\\u2028\\u2029]+/gu, ' ');\n}\n\n/** DOT 문자열 리터럴로 안전하게 이스케이프한다. */\nfunction dotString(value: string): string {\n return JSON.stringify(value);\n}\n\n/** 위치를 사람이 읽을 수 있는 경로:줄:열로 만든다. */\nfunction locationLabel(location: BridgeLocation): string {\n return `${location.path}:${location.line}:${location.column}`;\n}\n\n/** 간선 종류와 문자열 키를 한 줄 라벨로 만든다. */\nfunction edgeLabel(edge: BridgeGraphEdge): string {\n const key = edge.method === undefined\n ? edge.channel\n : `${edge.channel}#${edge.method}`;\n return `${edge.kind} ${key}`;\n}\n\n/** 조인 결과의 매치만 경계 그래프로 바꾼다. */\nexport function createBridgeGraph(joined: BridgeJoinResult): BridgeGraphDocument {\n assertBridgeGraphSize(joined);\n const nodes = new Map<string, BridgeGraphNode>();\n const edges: BridgeGraphEdge[] = [];\n addChannelEdges(joined, nodes, edges);\n addMethodEdges(joined, nodes, edges);\n return {\n format: 'isthmus-graph',\n version: 1,\n nodes: [...nodes.values()].sort((left, right) => compareStrings(left.id, right.id)),\n edges: edges.sort(compareEdges),\n limitations: joined.limitations,\n };\n}\n\n/** 모든 Cartesian 간선 수를 할당 전에 계산해 메모리 폭증을 막는다. */\nexport function assertBridgeGraphSize(joined: BridgeJoinResult): void {\n let edgeCount = 0;\n const endpointCounts = [\n ...joined.matchedChannels.map(({ creations, registrations }) =>\n [creations.length, registrations.length] as const,\n ),\n ...joined.matchedMethods.map(({ invocations, handlers }) =>\n [invocations.length, handlers.length] as const,\n ),\n ];\n for (const [fromCount, toCount] of endpointCounts) {\n if (\n toCount > 0 &&\n fromCount > Math.floor((MAX_GRAPH_EDGES - edgeCount) / toCount)\n ) {\n throw new BridgeGraphLimitError();\n }\n edgeCount += fromCount * toCount;\n }\n}\n\n/** 매치된 채널의 생성→등록 간선을 추가한다. */\nfunction addChannelEdges(\n joined: BridgeJoinResult,\n nodes: Map<string, BridgeGraphNode>,\n edges: BridgeGraphEdge[],\n): void {\n for (const match of joined.matchedChannels) {\n for (const creation of match.creations) {\n for (const registration of match.registrations) {\n const from = addNode(nodes, creation);\n const to = addNode(nodes, registration);\n edges.push({\n from,\n to,\n kind: 'channel',\n target: match.target,\n channel: match.channel,\n });\n }\n }\n }\n}\n\n/** 매치된 메서드의 호출→핸들러 간선을 추가한다. */\nfunction addMethodEdges(\n joined: BridgeJoinResult,\n nodes: Map<string, BridgeGraphNode>,\n edges: BridgeGraphEdge[],\n): void {\n for (const match of joined.matchedMethods) {\n for (const invocation of match.invocations) {\n for (const handler of match.handlers) {\n const from = addNode(nodes, invocation);\n const to = addNode(nodes, handler);\n edges.push({\n from,\n to,\n kind: 'method',\n target: match.target,\n channel: match.channel,\n method: match.method,\n });\n }\n }\n }\n}\n\n/** 증거 위치를 그래프 노드로 추가하고 안정 ID를 돌려준다. */\nfunction addNode(\n nodes: Map<string, BridgeGraphNode>,\n endpoint: BridgeEndpoint,\n): string {\n const id = endpointId(endpoint);\n const existing = nodes.get(id);\n if (existing === undefined) {\n nodes.set(\n id,\n endpoint.symbol === undefined\n ? { id, platform: endpoint.platform, location: endpoint.location }\n : {\n id,\n platform: endpoint.platform,\n location: endpoint.location,\n symbol: endpoint.symbol,\n },\n );\n } else if (endpoint.symbol !== undefined) {\n const symbol = mergeSymbols(existing.symbol, endpoint.symbol);\n if (symbol === undefined) {\n throw new BridgeGraphValidationError(\n 'Bridge graph node has conflicting symbols.',\n );\n }\n nodes.set(id, { ...existing, symbol });\n }\n return id;\n}\n\n/** 호환되는 두 심볼을 더 구체적인 선언 식별자로 병합한다. */\nfunction mergeSymbols(\n left: BridgeSymbol | undefined,\n right: BridgeSymbol,\n): BridgeSymbol | undefined {\n if (left === undefined) return right;\n if (left.qualifiedName !== right.qualifiedName) return undefined;\n if (left.usr !== undefined && right.usr !== undefined && left.usr !== right.usr) {\n return undefined;\n }\n const usr = left.usr ?? right.usr;\n return usr === undefined\n ? { qualifiedName: left.qualifiedName }\n : { qualifiedName: left.qualifiedName, usr };\n}\n\n/** 플랫폼과 소스 위치로 실행 간 안정적인 노드 ID를 만든다. */\nfunction endpointId(endpoint: BridgeEndpoint): string {\n const { path, line, column } = endpoint.location;\n return `${endpoint.platform}:${path}:${line}:${column}`;\n}\n\n/** 간선을 종류·from·to 순으로 고정한다. */\nfunction compareEdges(left: BridgeGraphEdge, right: BridgeGraphEdge): number {\n return (\n compareStrings(left.kind, right.kind) ||\n compareStrings(left.from, right.from) ||\n compareStrings(left.to, right.to)\n );\n}\n"]}
@@ -6,34 +6,17 @@ export function encodeBridgeQuery(document) {
6
6
  }
7
7
  /** 조인된 브리지에서 채널 또는 메서드 문자열을 질의한다. */
8
8
  export function createBridgeQuery(joined, requested) {
9
- const channels = channelResults(joined, requested);
10
- if (channels.length > 1) {
11
- return ambiguousQuery(joined, requested, channels);
12
- }
13
- const channel = channels[0];
14
- if (channel !== undefined) {
15
- return {
16
- status: 'found',
17
- requested,
18
- level: 'bridge',
19
- limitations: joined.limitations,
20
- result: channel,
21
- };
22
- }
23
- const methods = methodResults(joined, requested);
24
- if (methods.length > 1) {
25
- return ambiguousQuery(joined, requested, methods);
26
- }
27
- const method = methods[0];
28
- if (method !== undefined) {
29
- return {
30
- status: 'found',
31
- requested,
32
- level: 'bridge',
33
- limitations: joined.limitations,
34
- result: method,
35
- };
36
- }
9
+ const results = [...channelResults(joined), ...methodResults(joined)];
10
+ const exact = results.filter(({ subject }) => subject.qualifiedName === requested);
11
+ if (exact.length > 1)
12
+ return ambiguousQuery(joined, requested, exact);
13
+ if (exact[0] !== undefined)
14
+ return foundQuery(joined, requested, exact[0]);
15
+ const named = results.filter(({ subject }) => subject.name === requested);
16
+ if (named.length > 1)
17
+ return ambiguousQuery(joined, requested, named);
18
+ if (named[0] !== undefined)
19
+ return foundQuery(joined, requested, named[0]);
37
20
  return {
38
21
  status: 'notFound',
39
22
  requested,
@@ -41,6 +24,16 @@ export function createBridgeQuery(joined, requested) {
41
24
  limitations: joined.limitations,
42
25
  };
43
26
  }
27
+ /** 유일하게 식별된 논리 키와 증거를 found 문서로 감싼다. */
28
+ function foundQuery(joined, requested, result) {
29
+ return {
30
+ status: 'found',
31
+ requested,
32
+ level: 'bridge',
33
+ limitations: joined.limitations,
34
+ result,
35
+ };
36
+ }
44
37
  /** 여러 논리 키가 같은 요청과 맞을 때 선택 가능한 정규화 이름을 돌려준다. */
45
38
  function ambiguousQuery(joined, requested, results) {
46
39
  return {
@@ -54,16 +47,13 @@ function ambiguousQuery(joined, requested, results) {
54
47
  };
55
48
  }
56
49
  /** 요청 문자열과 정확히 같은 논리 메서드 결과를 만든다. */
57
- function methodResults(joined, requested) {
50
+ function methodResults(joined) {
58
51
  const matched = joined.matchedMethods
59
- .map(({ target, channel, method, invocations, handlers }) => makeMethodResult(target, channel, method, invocations, handlers))
60
- .filter((result) => matchesRequested(result, requested));
52
+ .map(({ target, channel, method, invocations, handlers }) => makeMethodResult(target, channel, method, invocations, handlers));
61
53
  const unhandled = joined.unhandledInvocations
62
- .map(({ target, channel, method, invocations }) => makeMethodResult(target, channel, method, invocations, []))
63
- .filter((result) => matchesRequested(result, requested));
54
+ .map(({ target, channel, method, invocations }) => makeMethodResult(target, channel, method, invocations, []));
64
55
  const handlers = joined.handlersWithoutInvocations
65
- .map(({ target, channel, method, handlers }) => makeMethodResult(target, channel, method, [], handlers))
66
- .filter((result) => matchesRequested(result, requested));
56
+ .map(({ target, channel, method, handlers }) => makeMethodResult(target, channel, method, [], handlers));
67
57
  return [...matched, ...unhandled, ...handlers];
68
58
  }
69
59
  /** 메서드 키와 양쪽 증거를 query result로 바꾼다. */
@@ -79,20 +69,13 @@ function makeMethodResult(target, channel, method, usedBy, dependsOn) {
79
69
  };
80
70
  }
81
71
  /** 요청 문자열과 정확히 같은 논리 채널 결과를 만든다. */
82
- function channelResults(joined, requested) {
72
+ function channelResults(joined) {
83
73
  const matched = joined.matchedChannels
84
- .map(({ target, channel, creations, registrations }) => makeQueryResult(target, channel, 'channel', creations, registrations))
85
- .filter((result) => matchesRequested(result, requested));
74
+ .map(({ target, channel, creations, registrations }) => makeQueryResult(target, channel, 'channel', creations, registrations));
86
75
  const unregistered = joined.unregisteredChannelCreations
87
- .map(({ target, channel, creations }) => makeQueryResult(target, channel, 'channel', creations, []))
88
- .filter((result) => matchesRequested(result, requested));
76
+ .map(({ target, channel, creations }) => makeQueryResult(target, channel, 'channel', creations, []));
89
77
  return [...matched, ...unregistered];
90
78
  }
91
- /** 짧은 이름 또는 모호성 해소용 qualifiedName과 정확히 일치하는지 확인한다. */
92
- function matchesRequested(result, requested) {
93
- return (result.subject.name === requested ||
94
- result.subject.qualifiedName === requested);
95
- }
96
79
  /** 조인 키와 양쪽 증거를 query result 골격으로 바꾼다. */
97
80
  function makeQueryResult(target, name, kind, usedBy, dependsOn) {
98
81
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"query.js","sourceRoot":"","sources":["../../src/report/query.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"query.js","sourceRoot":"","sources":["../../src/report/query.ts"],"names":[],"mappings":"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","sourcesContent":["import type { BridgeTarget } from '../exchange/parse.ts';\nimport { compareStrings } from '../compare.ts';\nimport type {\n BridgeEndpoint,\n BridgeJoinResult,\n JoinLimitation,\n} from '../join/join.ts';\nimport { encodeSortedJson } from './sorted-json.ts';\n\n/** query가 식별한 채널 또는 메서드 키다. */\nexport interface BridgeQuerySubject {\n readonly name: string;\n readonly qualifiedName: string;\n readonly kind: 'channel' | 'method';\n}\n\n/** 한 브리지 키에서 본 호출 측과 수신 측 증거다. */\nexport interface BridgeQueryResult {\n readonly subject: BridgeQuerySubject;\n readonly usedBy: readonly BridgeEndpoint[];\n readonly dependsOn: readonly BridgeEndpoint[];\n}\n\n/** cartograph query와 같은 상태 외피를 쓰는 브리지 질의 문서다. */\nexport interface BridgeQueryDocument {\n readonly status: 'found' | 'ambiguous' | 'notFound';\n readonly requested: string;\n readonly level: 'bridge';\n readonly limitations: readonly JoinLimitation[];\n readonly result?: BridgeQueryResult;\n readonly candidates?: ReadonlyArray<{\n readonly qualifiedName: string;\n }>;\n}\n\n/** query 문서를 결정적인 JSON 문자열로 인코딩한다. */\nexport function encodeBridgeQuery(document: BridgeQueryDocument): string {\n return encodeSortedJson(document);\n}\n\n/** 조인된 브리지에서 채널 또는 메서드 문자열을 질의한다. */\nexport function createBridgeQuery(\n joined: BridgeJoinResult,\n requested: string,\n): BridgeQueryDocument {\n const results = [...channelResults(joined), ...methodResults(joined)];\n const exact = results.filter(\n ({ subject }) => subject.qualifiedName === requested,\n );\n if (exact.length > 1) return ambiguousQuery(joined, requested, exact);\n if (exact[0] !== undefined) return foundQuery(joined, requested, exact[0]);\n\n const named = results.filter(({ subject }) => subject.name === requested);\n if (named.length > 1) return ambiguousQuery(joined, requested, named);\n if (named[0] !== undefined) return foundQuery(joined, requested, named[0]);\n return {\n status: 'notFound',\n requested,\n level: 'bridge',\n limitations: joined.limitations,\n };\n}\n\n/** 유일하게 식별된 논리 키와 증거를 found 문서로 감싼다. */\nfunction foundQuery(\n joined: BridgeJoinResult,\n requested: string,\n result: BridgeQueryResult,\n): BridgeQueryDocument {\n return {\n status: 'found',\n requested,\n level: 'bridge',\n limitations: joined.limitations,\n result,\n };\n}\n\n/** 여러 논리 키가 같은 요청과 맞을 때 선택 가능한 정규화 이름을 돌려준다. */\nfunction ambiguousQuery(\n joined: BridgeJoinResult,\n requested: string,\n results: readonly BridgeQueryResult[],\n): BridgeQueryDocument {\n return {\n status: 'ambiguous',\n requested,\n level: 'bridge',\n limitations: joined.limitations,\n candidates: results\n .map(({ subject }) => ({ qualifiedName: subject.qualifiedName }))\n .sort((left, right) => compareStrings(left.qualifiedName, right.qualifiedName)),\n };\n}\n\n/** 요청 문자열과 정확히 같은 논리 메서드 결과를 만든다. */\nfunction methodResults(\n joined: BridgeJoinResult,\n): BridgeQueryResult[] {\n const matched = joined.matchedMethods\n .map(({ target, channel, method, invocations, handlers }) =>\n makeMethodResult(target, channel, method, invocations, handlers),\n );\n const unhandled = joined.unhandledInvocations\n .map(({ target, channel, method, invocations }) =>\n makeMethodResult(target, channel, method, invocations, []),\n );\n const handlers = joined.handlersWithoutInvocations\n .map(({ target, channel, method, handlers }) =>\n makeMethodResult(target, channel, method, [], handlers),\n );\n return [...matched, ...unhandled, ...handlers];\n}\n\n/** 메서드 키와 양쪽 증거를 query result로 바꾼다. */\nfunction makeMethodResult(\n target: BridgeTarget,\n channel: string,\n method: string,\n usedBy: readonly BridgeEndpoint[],\n dependsOn: readonly BridgeEndpoint[],\n): BridgeQueryResult {\n return {\n subject: {\n name: method,\n qualifiedName:\n `${target}:${encodeSubjectComponent(channel)}#${encodeSubjectComponent(method)}`,\n kind: 'method',\n },\n usedBy,\n dependsOn,\n };\n}\n\n/** 요청 문자열과 정확히 같은 논리 채널 결과를 만든다. */\nfunction channelResults(\n joined: BridgeJoinResult,\n): BridgeQueryResult[] {\n const matched = joined.matchedChannels\n .map(({ target, channel, creations, registrations }) =>\n makeQueryResult(target, channel, 'channel', creations, registrations),\n );\n const unregistered = joined.unregisteredChannelCreations\n .map(({ target, channel, creations }) =>\n makeQueryResult(target, channel, 'channel', creations, []),\n );\n return [...matched, ...unregistered];\n}\n\n/** 조인 키와 양쪽 증거를 query result 골격으로 바꾼다. */\nfunction makeQueryResult(\n target: BridgeTarget,\n name: string,\n kind: 'channel' | 'method',\n usedBy: readonly BridgeEndpoint[],\n dependsOn: readonly BridgeEndpoint[],\n): BridgeQueryResult {\n return {\n subject: {\n name,\n qualifiedName: `${target}:${encodeSubjectComponent(name)}`,\n kind,\n },\n usedBy,\n dependsOn,\n };\n}\n\n/** qualifiedName 구분자와 이스케이프 문자를 가역적인 퍼센트 표기로 바꾼다. */\nfunction encodeSubjectComponent(value: string): string {\n return value.replaceAll('%', '%25').replaceAll('#', '%23');\n}\n"]}
@@ -1,5 +1,5 @@
1
1
  import type { BridgePlatform, BridgeSymbol } from '../exchange/parse.ts';
2
- import type { BridgeJoinResult } from '../join/join.ts';
2
+ import { type BridgeJoinResult } from '../join/join.ts';
3
3
  /** cartograph가 보존할 Swift 선언 식별자다. */
4
4
  export interface RetentionSymbol extends BridgeSymbol {
5
5
  readonly usr?: string;
@@ -33,6 +33,10 @@ export interface CartographRetentionsDocument {
33
33
  readonly generatedAt: string;
34
34
  readonly retentions: readonly ExternalRetention[];
35
35
  }
36
+ /** 불완전한 조인으로 보존 결정을 만들 수 없음을 나타낸다. */
37
+ export declare class RetentionValidationError extends Error {
38
+ constructor(message: string);
39
+ }
36
40
  /** cartograph 보존 문서를 결정적인 JSON으로 인코딩한다. */
37
41
  export declare function encodeCartographRetentionsDocument(document: CartographRetentionsDocument): string;
38
42
  /** 매치된 브리지 메서드를 cartograph 보존 근거로 바꾼다. */
@@ -1,10 +1,21 @@
1
+ import { isBridgeJoinDeferred, } from "../join/join.js";
1
2
  import { encodeSortedJson } from "./sorted-json.js";
3
+ /** 불완전한 조인으로 보존 결정을 만들 수 없음을 나타낸다. */
4
+ export class RetentionValidationError extends Error {
5
+ constructor(message) {
6
+ super(message);
7
+ this.name = 'RetentionValidationError';
8
+ }
9
+ }
2
10
  /** cartograph 보존 문서를 결정적인 JSON으로 인코딩한다. */
3
11
  export function encodeCartographRetentionsDocument(document) {
4
12
  return encodeSortedJson(document);
5
13
  }
6
14
  /** 매치된 브리지 메서드를 cartograph 보존 근거로 바꾼다. */
7
15
  export function createCartographRetentionsDocument(joined, generatedAt, producerVersion) {
16
+ if (isBridgeJoinDeferred(joined)) {
17
+ throw new RetentionValidationError('Cannot create retentions from a deferred bridge join.');
18
+ }
8
19
  return {
9
20
  format: 'external-retentions',
10
21
  version: 0,
@@ -1 +1 @@
1
- {"version":3,"file":"retentions.js","sourceRoot":"","sources":["../../src/report/retentions.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAqCpD,2CAA2C;AAC3C,MAAM,UAAU,kCAAkC,CAChD,QAAsC;IAEtC,OAAO,gBAAgB,CAAC,QAAQ,CAAC,CAAC;AACpC,CAAC;AAED,0CAA0C;AAC1C,MAAM,UAAU,kCAAkC,CAChD,MAAwB,EACxB,WAAmB,EACnB,eAAuB;IAEvB,OAAO;QACL,MAAM,EAAE,qBAAqB;QAC7B,OAAO,EAAE,CAAC;QACV,UAAU,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,eAAe,EAAE;QACzD,WAAW;QACX,UAAU,EAAE,2BAA2B,CAAC,MAAM,CAAC;KAChD,CAAC;AACJ,CAAC;AAED,mDAAmD;AACnD,SAAS,2BAA2B,CAClC,MAAwB;IAExB,MAAM,UAAU,GAAwB,EAAE,CAAC;IAC3C,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,cAAc,EAAE,CAAC;QAC3C,MAAM,MAAM,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;QACrC,IAAI,MAAM,KAAK,SAAS;YAAE,SAAS;QACnC,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;YACtC,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,IAAI,OAAO,CAAC,MAAM,KAAK,SAAS;gBAAE,SAAS;YAC3E,MAAM,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,IAAI,QAAQ,OAAO,CAAC,MAAM,CAAC,aAAa,EAAE,CAAC;YAC/E,MAAM,YAAY,GAAG,GAAG,SAAS,SAAS,MAAM,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,CAAC;YACjF,IAAI,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC;gBAAE,SAAS;YACrC,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;YACvB,UAAU,CAAC,IAAI,CAAC;gBACd,MAAM,EAAE,OAAO,CAAC,MAAM;gBACtB,MAAM,EAAE,QAAQ;gBAChB,QAAQ,EAAE;oBACR,OAAO,EAAE,MAAM,CAAC,OAAO;oBACvB,MAAM,EAAE,MAAM,CAAC,MAAM;oBACrB,MAAM,EAAE;wBACN,QAAQ,EAAE,MAAM,CAAC,QAAQ;wBACzB,IAAI,EAAE,MAAM,CAAC,QAAQ,CAAC,IAAI;wBAC1B,IAAI,EAAE,MAAM,CAAC,QAAQ,CAAC,IAAI;qBAC3B;iBACF;aACF,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IACD,OAAO,UAAU,CAAC;AACpB,CAAC"}
1
+ {"version":3,"file":"retentions.js","sourceRoot":"","sources":["../../src/report/retentions.ts"],"names":[],"mappings":"AACA,OAAO,EACL,oBAAoB,GAErB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAqCpD,sCAAsC;AACtC,MAAM,OAAO,wBAAyB,SAAQ,KAAK;IACjD,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,0BAA0B,CAAC;IACzC,CAAC;CACF;AAED,2CAA2C;AAC3C,MAAM,UAAU,kCAAkC,CAChD,QAAsC;IAEtC,OAAO,gBAAgB,CAAC,QAAQ,CAAC,CAAC;AACpC,CAAC;AAED,0CAA0C;AAC1C,MAAM,UAAU,kCAAkC,CAChD,MAAwB,EACxB,WAAmB,EACnB,eAAuB;IAEvB,IAAI,oBAAoB,CAAC,MAAM,CAAC,EAAE,CAAC;QACjC,MAAM,IAAI,wBAAwB,CAChC,uDAAuD,CACxD,CAAC;IACJ,CAAC;IACD,OAAO;QACL,MAAM,EAAE,qBAAqB;QAC7B,OAAO,EAAE,CAAC;QACV,UAAU,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,eAAe,EAAE;QACzD,WAAW;QACX,UAAU,EAAE,2BAA2B,CAAC,MAAM,CAAC;KAChD,CAAC;AACJ,CAAC;AAED,mDAAmD;AACnD,SAAS,2BAA2B,CAClC,MAAwB;IAExB,MAAM,UAAU,GAAwB,EAAE,CAAC;IAC3C,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,cAAc,EAAE,CAAC;QAC3C,MAAM,MAAM,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;QACrC,IAAI,MAAM,KAAK,SAAS;YAAE,SAAS;QACnC,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;YACtC,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,IAAI,OAAO,CAAC,MAAM,KAAK,SAAS;gBAAE,SAAS;YAC3E,MAAM,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,IAAI,QAAQ,OAAO,CAAC,MAAM,CAAC,aAAa,EAAE,CAAC;YAC/E,MAAM,YAAY,GAAG,GAAG,SAAS,SAAS,MAAM,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,CAAC;YACjF,IAAI,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC;gBAAE,SAAS;YACrC,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;YACvB,UAAU,CAAC,IAAI,CAAC;gBACd,MAAM,EAAE,OAAO,CAAC,MAAM;gBACtB,MAAM,EAAE,QAAQ;gBAChB,QAAQ,EAAE;oBACR,OAAO,EAAE,MAAM,CAAC,OAAO;oBACvB,MAAM,EAAE,MAAM,CAAC,MAAM;oBACrB,MAAM,EAAE;wBACN,QAAQ,EAAE,MAAM,CAAC,QAAQ;wBACzB,IAAI,EAAE,MAAM,CAAC,QAAQ,CAAC,IAAI;wBAC1B,IAAI,EAAE,MAAM,CAAC,QAAQ,CAAC,IAAI;qBAC3B;iBACF;aACF,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IACD,OAAO,UAAU,CAAC;AACpB,CAAC","sourcesContent":["import type { BridgePlatform, BridgeSymbol } from '../exchange/parse.ts';\nimport {\n isBridgeJoinDeferred,\n type BridgeJoinResult,\n} from '../join/join.ts';\nimport { encodeSortedJson } from './sorted-json.ts';\n\n/** cartograph가 보존할 Swift 선언 식별자다. */\nexport interface RetentionSymbol extends BridgeSymbol {\n readonly usr?: string;\n}\n\n/** 언어 경계 너머 호출자의 증거 위치다. */\nexport interface RetentionCaller {\n readonly platform: BridgePlatform;\n readonly path: string;\n readonly line: number;\n}\n\n/** 보존 판단을 설명할 채널·메서드·호출자 근거다. */\nexport interface RetentionEvidence {\n readonly channel: string;\n readonly method: string;\n readonly caller: RetentionCaller;\n}\n\n/** cartograph 외부 보존 근거 하나다. */\nexport interface ExternalRetention {\n readonly symbol: RetentionSymbol;\n readonly reason: 'bridge';\n readonly evidence: RetentionEvidence;\n}\n\n/** cartograph가 읽는 external-retentions 버전 0 문서다. */\nexport interface CartographRetentionsDocument {\n readonly format: 'external-retentions';\n readonly version: 0;\n readonly producedBy: Readonly<{ name: 'isthmus'; version: string }>;\n readonly generatedAt: string;\n readonly retentions: readonly ExternalRetention[];\n}\n\n/** 불완전한 조인으로 보존 결정을 만들 수 없음을 나타낸다. */\nexport class RetentionValidationError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'RetentionValidationError';\n }\n}\n\n/** cartograph 보존 문서를 결정적인 JSON으로 인코딩한다. */\nexport function encodeCartographRetentionsDocument(\n document: CartographRetentionsDocument,\n): string {\n return encodeSortedJson(document);\n}\n\n/** 매치된 브리지 메서드를 cartograph 보존 근거로 바꾼다. */\nexport function createCartographRetentionsDocument(\n joined: BridgeJoinResult,\n generatedAt: string,\n producerVersion: string,\n): CartographRetentionsDocument {\n if (isBridgeJoinDeferred(joined)) {\n throw new RetentionValidationError(\n 'Cannot create retentions from a deferred bridge join.',\n );\n }\n return {\n format: 'external-retentions',\n version: 0,\n producedBy: { name: 'isthmus', version: producerVersion },\n generatedAt,\n retentions: collectCartographRetentions(joined),\n };\n}\n\n/** 매치별 Dart 호출자와 Swift 심볼을 cartograph 근거로 결합한다. */\nfunction collectCartographRetentions(\n joined: BridgeJoinResult,\n): ExternalRetention[] {\n const retentions: ExternalRetention[] = [];\n const seen = new Set<string>();\n for (const method of joined.matchedMethods) {\n const caller = method.invocations[0];\n if (caller === undefined) continue;\n for (const handler of method.handlers) {\n if (handler.platform !== 'swift' || handler.symbol === undefined) continue;\n const symbolKey = handler.symbol.usr ?? `name:${handler.symbol.qualifiedName}`;\n const retentionKey = `${symbolKey}\\u0000${method.channel}\\u0000${method.method}`;\n if (seen.has(retentionKey)) continue;\n seen.add(retentionKey);\n retentions.push({\n symbol: handler.symbol,\n reason: 'bridge',\n evidence: {\n channel: method.channel,\n method: method.method,\n caller: {\n platform: caller.platform,\n path: caller.location.path,\n line: caller.location.line,\n },\n },\n });\n }\n }\n return retentions;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"sorted-json.js","sourceRoot":"","sources":["../../src/report/sorted-json.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAE/C,2CAA2C;AAC3C,MAAM,UAAU,gBAAgB,CAAC,KAAc;IAC7C,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC;AACzD,CAAC;AAED,sCAAsC;AACtC,SAAS,QAAQ,CAAC,KAAc;IAC9B,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACrD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAC9D,OAAO,MAAM,CAAC,WAAW,CACvB,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC;SAClB,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,cAAc,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;SACtD,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAC/C,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"sorted-json.js","sourceRoot":"","sources":["../../src/report/sorted-json.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAE/C,2CAA2C;AAC3C,MAAM,UAAU,gBAAgB,CAAC,KAAc;IAC7C,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC;AACzD,CAAC;AAED,sCAAsC;AACtC,SAAS,QAAQ,CAAC,KAAc;IAC9B,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACrD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAC9D,OAAO,MAAM,CAAC,WAAW,CACvB,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC;SAClB,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,cAAc,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;SACtD,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAC/C,CAAC;AACJ,CAAC","sourcesContent":["import { compareStrings } from '../compare.ts';\n\n/** JSON 객체 키를 모든 깊이에서 정렬하고 마지막 개행을 붙인다. */\nexport function encodeSortedJson(value: unknown): string {\n return `${JSON.stringify(sortJson(value), null, 2)}\\n`;\n}\n\n/** JSON 배열 순서는 보존하고 객체 키만 재귀 정렬한다. */\nfunction sortJson(value: unknown): unknown {\n if (Array.isArray(value)) return value.map(sortJson);\n if (typeof value !== 'object' || value === null) return value;\n return Object.fromEntries(\n Object.entries(value)\n .sort(([left], [right]) => compareStrings(left, right))\n .map(([key, item]) => [key, sortJson(item)]),\n );\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "isthmus-cli",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Join bridge facts across cross-platform application boundaries.",
5
5
  "keywords": [
6
6
  "flutter",
@@ -39,9 +39,10 @@
39
39
  "build": "tsc -p tsconfig.build.json",
40
40
  "typecheck": "tsc --noEmit",
41
41
  "test": "node scripts/run-tests.mjs",
42
+ "test:phase0:join": "node --test experiments/phase-0/join/join.test.mjs",
42
43
  "verify:cli": "npm run build && node scripts/verify-cli-contract.mjs",
43
44
  "verify:package": "node scripts/verify-package-contract.mjs",
44
- "verify": "npm run typecheck && npm test && npm run verify:cli && npm run verify:package",
45
+ "verify": "npm run typecheck && npm test && npm run test:phase0:join && npm run verify:cli && npm run verify:package",
45
46
  "prepublishOnly": "npm run verify"
46
47
  },
47
48
  "devDependencies": {