isthmus-cli 0.1.7 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -22,7 +22,7 @@ isthmus는 조사한 도구들이 언어별로 나눠 보던 이 교차 경계
22
22
 
23
23
  ## 상태
24
24
 
25
- **0.1.7.** bridge-facts 버전 1 파서와 `check`, `query`, `graph`, `diff`,
25
+ **0.2.0.** bridge-facts 버전 1 파서와 `check`, `query`, `graph`, `diff`,
26
26
  cartograph용 외부 보존 근거 왕복을 구현했다. 외부 입력·혼합 target·그래프 크기와
27
27
  Dart/Swift Phase 0 추출 경계를 fail-closed로 강화했고, 조인하지 못한 사실과 근거를
28
28
  만들지 못한 보존 대상이 조용히 사라지지 않도록 소비자 쪽에서 다시 세고, 수신 측이 신고한
@@ -160,7 +160,7 @@ isthmus graph dart-bridges.json swift-bridges.json --format mermaid
160
160
 
161
161
  `-unverified` 종류는 수신 측 문서의 한계에서 나온다. 예를 들어 Flutter 핸들러가
162
162
  Objective-C로 쓰인 플러그인에서 cartograph는 `objective-c-sources:`를 신고하고 핸들러 사실을
163
- 내지 못한다. 이때 "핸들러 없는 호출"을 error로 단정하면 이 도구가 없애려던 오탐을 이 도구가
163
+ 완전히 열거하지 못할 수 있다. 이때 "핸들러 없는 호출"을 error로 단정하면 이 도구가 없애려던 오탐을 이 도구가
164
164
  만든다. 사실과 증거는 그대로 보고하되 `--strict`를 실패시키지 않는다. 공백의 종류는 구분해서,
165
165
  이름이 리터럴이 아닌 채널 등록은 채널 진단만 낮추고 메서드 진단은 낮추지 않는다. 완화 단위는
166
166
  진단의 target이다. 사실은 target별로만 조인되므로 다른 target 수신 문서가 신고한 공백은 현재
@@ -168,6 +168,20 @@ target의 진단을 낮추지 않고, 사실이 없는 수신 문서의 공백
168
168
  없어 모든 target에 적용한다. 호출 측 한계는 네이티브 코드를 가리지 않으므로 심각도에 영향을
169
169
  주지 않으며, 알려지지 않은 한계 문구는 공백으로 해석하지 않는다.
170
170
 
171
+
172
+ 0.2.0은 v1의 선택적 `limitationScopes`를 읽습니다. `{ limitationIndex, channels }`는
173
+ 해당 한계 전체의 보수적 채널 상한이며, 단순히 발견한 리터럴 목록이면 안 됩니다. 스코프가
174
+ 없거나 다른 범위 불명 공백이 공존하면 기존 target 전체 완화를 유지합니다. 빈 채널 집합과
175
+ 잘못된 인덱스는 입력 오류입니다. 스코프는 check/query/graph/diff에서 `channels`로 보존됩니다.
176
+ 생산자의 tool 이름만으로 자체 계수를 신뢰하지 않으며, `unjoined-*`는 소비자가 직접 붙인
177
+ `origin: "consumer"`가 있어야 완화 근거가 됩니다.
178
+
179
+ 선택적 fact `sourceLanguage: "objective-c"`는 `.m`/`.mm`의 ObjC 구현을 Swift 그래프와
180
+ 구분합니다. 이 사실에는 symbol을 붙이지 않습니다. 매치는 check/query/graph에 남고 Swift
181
+ 보존 목록에서는 제외되며, `omittedObjectiveCHandlers`가 제외 수를 알립니다. 표식 없는
182
+ Swift 핸들러의 symbol 누락은 여전히 종료 코드 2입니다. 이 확장을 지원하는 소비자를 먼저
183
+ 배포해야 합니다. 옛 소비자는 스코프를 버리고 넓게 완화하며 ObjC 보존 생성은 실패합니다.
184
+
171
185
  모든 이슈는 관찰된 위치를 `evidence`로 제공한다. 동적 이름, 해석하지 못한 receiver나
172
186
  handler 본문, USR 누락, 입력 생성 시각 차이, 혼합 target은 `limitations`에 출처와 함께
173
187
  남긴다. 이 도구는 삭제 가능 여부를 판정하지 않는다.
@@ -176,7 +190,7 @@ isthmus 출력 문서는 버전 1 안에서 필드 추가나 새 이슈 code를
176
190
  기존 필드의 의미를 바꾸거나 제거할 때 문서 버전을 올린다.
177
191
 
178
192
  `limitations`에는 생산자가 신고한 한계와 isthmus가 직접 센 한계가 함께 들어간다.
179
- 각 항목은 `platform`·`target`·`tool`로 출처와 귀속을 밝히고, `tool`이 `isthmus`인 항목은
193
+ 각 항목은 `platform`·`target`·`tool`로 출처와 귀속을 밝히고, `origin: "consumer"`인 항목은
180
194
  조인 단계에서 관찰한 것이다. 조인하지 못한 사실은 생산자의 신고나 그 개수와 무관하게
181
195
  플랫폼·target별로 다시 센다.
182
196
 
@@ -2,6 +2,16 @@
2
2
  export type BridgePlatform = 'dart' | 'swift' | 'kotlin' | 'js';
3
3
  /** 언어 경계를 잇는 메커니즘이다. */
4
4
  export type BridgeTarget = 'flutter' | 'react-native' | 'capacitor';
5
+ /** Swift 플랫폼 문서에 실린 Objective-C 구현을 Swift 보존 대상과 구분한다. */
6
+ export type BridgeSourceLanguage = 'objective-c';
7
+ /** 특정 한계 전체의 영향을 포함하는 채널 집합. 일부 발견 목록이 아니다. */
8
+ export interface BridgeLimitationScope {
9
+ readonly limitationIndex: number;
10
+ readonly channels: readonly string[];
11
+ }
12
+ /** 확장 메타데이터가 입력 자원 상한을 우회하지 못하게 한다. */
13
+ export declare const MAX_LIMITATION_SCOPES = 1000;
14
+ export declare const MAX_SCOPED_CHANNELS = 10000;
5
15
  /** 교환 형식의 사실 종류다. */
6
16
  export type BridgeFactKind = 'channel-create' | 'channel-register' | 'method-invoke' | 'method-handle' | 'module-export' | 'module-import' | 'component-export' | 'component-require';
7
17
  /** 한 생산 문서가 담을 수 있는 최대 사실 수다. */
@@ -25,6 +35,7 @@ export interface BridgeFact {
25
35
  readonly dynamic: boolean;
26
36
  readonly location: BridgeLocation;
27
37
  readonly symbol?: BridgeSymbol;
38
+ readonly sourceLanguage?: BridgeSourceLanguage;
28
39
  }
29
40
  /** bridge-facts 버전 1 문서다. */
30
41
  export interface BridgeFactsDocument {
@@ -40,6 +51,7 @@ export interface BridgeFactsDocument {
40
51
  readonly project: string;
41
52
  readonly facts: readonly BridgeFact[];
42
53
  readonly limitations: readonly string[];
54
+ readonly limitationScopes?: readonly BridgeLimitationScope[];
43
55
  }
44
56
  /** 외부 교환 문서가 v1 계약을 어겼음을 나타낸다. */
45
57
  export declare class BridgeFactsValidationError extends Error {
@@ -1,3 +1,6 @@
1
+ /** 확장 메타데이터가 입력 자원 상한을 우회하지 못하게 한다. */
2
+ export const MAX_LIMITATION_SCOPES = 1_000;
3
+ export const MAX_SCOPED_CHANNELS = 10_000;
1
4
  /** 한 생산 문서가 담을 수 있는 최대 사실 수다. */
2
5
  export const MAX_FACTS_PER_DOCUMENT = 100_000;
3
6
  /** 외부 교환 문서가 v1 계약을 어겼음을 나타낸다. */
@@ -34,6 +37,12 @@ function normalizeDocument(document) {
34
37
  project: document.project,
35
38
  facts: document.facts.map(normalizeFact),
36
39
  limitations: [...document.limitations],
40
+ ...(document.limitationScopes === undefined ? {} : {
41
+ limitationScopes: document.limitationScopes.map((scope) => ({
42
+ limitationIndex: scope.limitationIndex,
43
+ channels: [...new Set(scope.channels)].sort(),
44
+ })).sort((a, b) => a.limitationIndex - b.limitationIndex),
45
+ }),
37
46
  };
38
47
  }
39
48
  /** 사실과 중첩 위치·심볼에서 계약 필드만 보존한다. */
@@ -57,6 +66,7 @@ function normalizeFact(fact) {
57
66
  column: fact.location.column,
58
67
  },
59
68
  ...symbol,
69
+ ...(fact.sourceLanguage === undefined ? {} : { sourceLanguage: fact.sourceLanguage }),
60
70
  };
61
71
  }
62
72
  /** 문서 수준 필드가 v1 타입과 허용값을 따르는지 검증한다. */
@@ -82,6 +92,7 @@ function validateDocumentMetadata(document) {
82
92
  }
83
93
  if (!isStringArray(document.limitations))
84
94
  fail('Limitations must be strings.');
95
+ validateLimitationScopes(document.limitationScopes, document.limitations.length);
85
96
  const hasUnattributedHandler = document.facts.some((fact) => isJsonObject(fact) &&
86
97
  fact.kind === 'method-handle' &&
87
98
  fact.channel === null);
@@ -90,6 +101,31 @@ function validateDocumentMetadata(document) {
90
101
  fail('Unattributed method handles require a limitation.');
91
102
  }
92
103
  }
104
+ /** 잘못된 범위를 무시하면 거짓 error가 생기므로 입력 단계에서 명시적으로 거부한다. */
105
+ function validateLimitationScopes(value, limitationCount) {
106
+ if (value === undefined)
107
+ return;
108
+ if (!Array.isArray(value) || value.length > MAX_LIMITATION_SCOPES) {
109
+ fail('Invalid limitation scopes array.');
110
+ }
111
+ const indices = new Set();
112
+ let channelCount = 0;
113
+ for (const entry of value) {
114
+ if (!isJsonObject(entry) || !Number.isSafeInteger(entry.limitationIndex) ||
115
+ typeof entry.limitationIndex !== 'number' || entry.limitationIndex < 0 ||
116
+ entry.limitationIndex >= limitationCount || indices.has(entry.limitationIndex)) {
117
+ fail('Invalid or duplicate limitation scope index.');
118
+ }
119
+ if (!Array.isArray(entry.channels) || entry.channels.length === 0 ||
120
+ !entry.channels.every(isSafeNonEmptyString)) {
121
+ fail('Limitation scope channels must be non-empty safe strings.');
122
+ }
123
+ channelCount += entry.channels.length;
124
+ if (channelCount > MAX_SCOPED_CHANNELS)
125
+ fail('Too many scoped channels.');
126
+ indices.add(entry.limitationIndex);
127
+ }
128
+ }
93
129
  /** 사실 하나의 조인 키와 증거 필드를 검증한다. */
94
130
  function validateFact(value, index, platform) {
95
131
  if (!isJsonObject(value))
@@ -117,6 +153,9 @@ function validateFact(value, index, platform) {
117
153
  fail(`Invalid dynamic flag at index ${index}.`);
118
154
  validateLocation(value.location, index);
119
155
  validateSymbol(value.symbol, index);
156
+ if (value.sourceLanguage !== undefined && (value.sourceLanguage !== 'objective-c' || platform !== 'swift' ||
157
+ !/\.(?:m|mm)$/u.test(value.location.path) || (value.symbol !== undefined && !value.symbol.usr?.startsWith('c:'))))
158
+ fail(`Invalid source language at index ${index}.`);
120
159
  }
121
160
  /** 호출 측과 수신 측 플랫폼이 생산할 수 있는 fact 종류인지 확인한다. */
122
161
  function isFactKindForPlatform(platform, kind) {
@@ -1 +1 @@
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 const MAX_FACTS_PER_DOCUMENT = 100_000;\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 if (document.facts.length > MAX_FACTS_PER_DOCUMENT) {\n fail(`Facts exceed the ${MAX_FACTS_PER_DOCUMENT} item limit.`);\n }\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 const hasUnattributedHandler = document.facts.some(\n (fact) =>\n isJsonObject(fact) &&\n fact.kind === 'method-handle' &&\n fact.channel === null,\n );\n if (\n hasUnattributedHandler &&\n !document.limitations.some((message) =>\n message.startsWith('unattributed-method-handles:'))\n ) {\n fail('Unattributed method handles require a limitation.');\n }\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 (!supportedBridgeFactKinds.has(value.kind)) {\n fail(`Fact kind is reserved but not supported in isthmus 0.1 at index ${index}.`);\n }\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 (\n value.channel === null\n ? value.kind !== 'method-handle'\n : !isSafeNonEmptyString(value.channel)\n ) {\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 isCallerPlatform(platform)\n ? callerFactKinds.has(kind)\n : receiverFactKinds.has(kind);\n}\n\n/** 브리지 호출 측 사실을 생산하는 플랫폼인지 확인한다. */\nexport function isCallerPlatform(platform: unknown): platform is 'dart' | 'js' {\n return platform === 'dart' || platform === 'js';\n}\n\n/** 브리지 수신 측 사실을 생산하는 플랫폼인지 확인한다. */\nexport function isReceiverPlatform(\n platform: unknown,\n): platform is 'swift' | 'kotlin' {\n return platform === 'swift' || platform === 'kotlin';\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 (controlCharacterPattern.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\n/** 0.1 조인과 보고가 실제로 처리하는 사실 종류다. */\nconst supportedBridgeFactKinds = new Set<unknown>([\n 'channel-create',\n 'channel-register',\n 'method-invoke',\n 'method-handle',\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-\\u009f\\u2028\\u2029]/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"]}
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/** Swift 플랫폼 문서에 실린 Objective-C 구현을 Swift 보존 대상과 구분한다. */\nexport type BridgeSourceLanguage = 'objective-c';\n\n/** 특정 한계 전체의 영향을 포함하는 채널 집합. 일부 발견 목록이 아니다. */\nexport interface BridgeLimitationScope {\n readonly limitationIndex: number;\n readonly channels: readonly string[];\n}\n\n/** 확장 메타데이터가 입력 자원 상한을 우회하지 못하게 한다. */\nexport const MAX_LIMITATION_SCOPES = 1_000;\nexport const MAX_SCOPED_CHANNELS = 10_000;\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 const MAX_FACTS_PER_DOCUMENT = 100_000;\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 readonly sourceLanguage?: BridgeSourceLanguage;\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 readonly limitationScopes?: readonly BridgeLimitationScope[];\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 ...(document.limitationScopes === undefined ? {} : {\n limitationScopes: document.limitationScopes.map((scope) => ({\n limitationIndex: scope.limitationIndex,\n channels: [...new Set(scope.channels)].sort(),\n })).sort((a, b) => a.limitationIndex - b.limitationIndex),\n }),\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 ...(fact.sourceLanguage === undefined ? {} : { sourceLanguage: fact.sourceLanguage }),\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 if (document.facts.length > MAX_FACTS_PER_DOCUMENT) {\n fail(`Facts exceed the ${MAX_FACTS_PER_DOCUMENT} item limit.`);\n }\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 validateLimitationScopes(document.limitationScopes, document.limitations.length);\n const hasUnattributedHandler = document.facts.some(\n (fact) =>\n isJsonObject(fact) &&\n fact.kind === 'method-handle' &&\n fact.channel === null,\n );\n if (\n hasUnattributedHandler &&\n !document.limitations.some((message) =>\n message.startsWith('unattributed-method-handles:'))\n ) {\n fail('Unattributed method handles require a limitation.');\n }\n}\n\n/** 잘못된 범위를 무시하면 거짓 error가 생기므로 입력 단계에서 명시적으로 거부한다. */\nfunction validateLimitationScopes(value: unknown, limitationCount: number): void {\n if (value === undefined) return;\n if (!Array.isArray(value) || value.length > MAX_LIMITATION_SCOPES) {\n fail('Invalid limitation scopes array.');\n }\n const indices = new Set<number>();\n let channelCount = 0;\n for (const entry of value) {\n if (!isJsonObject(entry) || !Number.isSafeInteger(entry.limitationIndex) ||\n typeof entry.limitationIndex !== 'number' || entry.limitationIndex < 0 ||\n entry.limitationIndex >= limitationCount || indices.has(entry.limitationIndex)) {\n fail('Invalid or duplicate limitation scope index.');\n }\n if (!Array.isArray(entry.channels) || entry.channels.length === 0 ||\n !entry.channels.every(isSafeNonEmptyString)) {\n fail('Limitation scope channels must be non-empty safe strings.');\n }\n channelCount += entry.channels.length;\n if (channelCount > MAX_SCOPED_CHANNELS) fail('Too many scoped channels.');\n indices.add(entry.limitationIndex);\n }\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 (!supportedBridgeFactKinds.has(value.kind)) {\n fail(`Fact kind is reserved but not supported in isthmus 0.1 at index ${index}.`);\n }\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 (\n value.channel === null\n ? value.kind !== 'method-handle'\n : !isSafeNonEmptyString(value.channel)\n ) {\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 if (value.sourceLanguage !== undefined && (\n value.sourceLanguage !== 'objective-c' || platform !== 'swift' ||\n !/\\.(?:m|mm)$/u.test((value.location as BridgeLocation).path) || (value.symbol !== undefined && !(value.symbol as BridgeSymbol).usr?.startsWith('c:'))\n )) fail(`Invalid source language at index ${index}.`);\n}\n\n/** 호출 측과 수신 측 플랫폼이 생산할 수 있는 fact 종류인지 확인한다. */\nfunction isFactKindForPlatform(platform: unknown, kind: unknown): boolean {\n return isCallerPlatform(platform)\n ? callerFactKinds.has(kind)\n : receiverFactKinds.has(kind);\n}\n\n/** 브리지 호출 측 사실을 생산하는 플랫폼인지 확인한다. */\nexport function isCallerPlatform(platform: unknown): platform is 'dart' | 'js' {\n return platform === 'dart' || platform === 'js';\n}\n\n/** 브리지 수신 측 사실을 생산하는 플랫폼인지 확인한다. */\nexport function isReceiverPlatform(\n platform: unknown,\n): platform is 'swift' | 'kotlin' {\n return platform === 'swift' || platform === 'kotlin';\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 (controlCharacterPattern.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\n/** 0.1 조인과 보고가 실제로 처리하는 사실 종류다. */\nconst supportedBridgeFactKinds = new Set<unknown>([\n 'channel-create',\n 'channel-register',\n 'method-invoke',\n 'method-handle',\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-\\u009f\\u2028\\u2029]/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"]}
@@ -1,9 +1,10 @@
1
- import type { BridgeFactsDocument, BridgeLocation, BridgePlatform, BridgeSymbol, BridgeTarget } from '../exchange/parse.ts';
1
+ import type { BridgeFactsDocument, BridgeLocation, BridgePlatform, BridgeSymbol, BridgeSourceLanguage, BridgeTarget } from '../exchange/parse.ts';
2
2
  /** 한 언어 문서가 제공한 브리지 증거 위치다. */
3
3
  export interface BridgeEndpoint {
4
4
  readonly platform: BridgePlatform;
5
5
  readonly location: BridgeLocation;
6
6
  readonly symbol?: BridgeSymbol;
7
+ readonly sourceLanguage?: BridgeSourceLanguage;
7
8
  }
8
9
  /** 논리 채널 하나에 모인 양쪽 생성·등록 증거다. */
9
10
  export interface MatchedChannel {
@@ -59,6 +60,9 @@ export interface JoinLimitation {
59
60
  readonly target: BridgeTarget | null;
60
61
  readonly tool: string;
61
62
  readonly message: string;
63
+ readonly channels?: readonly string[];
64
+ /** 소비자가 직접 센 한계에만 부여하며 생산 문서에서는 복사하지 않는다. */
65
+ readonly origin?: 'consumer';
62
66
  }
63
67
  /** 검증된 교환 문서들의 논리 조인 결과다. */
64
68
  export interface BridgeJoinResult {
package/dist/join/join.js CHANGED
@@ -117,12 +117,19 @@ function emptyJoinResult(limitations) {
117
117
  }
118
118
  /** 입력 limitation에 생산 플랫폼과 도구 이름을 붙이고 조인하지 못한 사실을 함께 센다. */
119
119
  function collectLimitations(documents) {
120
- const limitations = documents.flatMap((document) => document.limitations.map((message) => ({
121
- platform: document.platform,
122
- target: limitationTarget(document),
123
- tool: document.tool.name,
124
- message,
125
- })));
120
+ const limitations = documents.flatMap((document) => {
121
+ const scopes = new Map(document.limitationScopes?.map((scope) => [scope.limitationIndex, scope.channels]));
122
+ return document.limitations.map((message, index) => {
123
+ const channels = scopes.get(index);
124
+ return {
125
+ platform: document.platform,
126
+ target: limitationTarget(document),
127
+ tool: document.tool.name,
128
+ message,
129
+ ...(channels === undefined ? {} : { channels }),
130
+ };
131
+ });
132
+ });
126
133
  limitations.push(...unjoinedFactLimitations(documents));
127
134
  const freshness = freshnessLimitation(documents);
128
135
  if (freshness !== undefined)
@@ -150,6 +157,7 @@ function unjoinedLimitations(documents, matchesFact, prefix, subject) {
150
157
  platform,
151
158
  target,
152
159
  tool: 'isthmus',
160
+ origin: 'consumer',
153
161
  message: `${prefix}: ${count} ${subject} were not joined`,
154
162
  }));
155
163
  }
@@ -241,6 +249,7 @@ function freshnessLimitation(documents) {
241
249
  platform: 'cross-platform',
242
250
  target: null,
243
251
  tool: 'isthmus',
252
+ origin: 'consumer',
244
253
  message: `input-freshness: bridge documents differ by ${hours} hours`,
245
254
  };
246
255
  }
@@ -249,7 +258,9 @@ function compareLimitations(left, right) {
249
258
  return (compareStrings(left.platform, right.platform) ||
250
259
  compareTargets(left.target, right.target) ||
251
260
  compareStrings(left.tool, right.tool) ||
252
- compareStrings(left.message, right.message));
261
+ compareStrings(left.message, right.message) ||
262
+ compareOptionalStrings(left.origin, right.origin) ||
263
+ compareStrings(JSON.stringify(left.channels ?? null), JSON.stringify(right.channels ?? null)));
253
264
  }
254
265
  /** target을 문자열 순으로 고정하고 귀속이 없는(null) 한계를 뒤에 둔다. */
255
266
  function compareTargets(left, right) {
@@ -298,6 +309,9 @@ function compareEndpoints(left, right) {
298
309
  left.location.column - right.location.column;
299
310
  if (locationOrder !== 0)
300
311
  return locationOrder;
312
+ const languageOrder = compareOptionalStrings(left.sourceLanguage, right.sourceLanguage);
313
+ if (languageOrder !== 0)
314
+ return languageOrder;
301
315
  if (left.symbol === undefined)
302
316
  return right.symbol === undefined ? 0 : 1;
303
317
  if (right.symbol === undefined)
@@ -375,9 +389,11 @@ function createChannelGroup(target, channel) {
375
389
  }
376
390
  /** 사실을 플랫폼이 포함된 증거 위치로 바꾼다. */
377
391
  function toEndpoint(platform, fact) {
378
- return fact.symbol === undefined
379
- ? { platform, location: fact.location }
380
- : { platform, location: fact.location, symbol: fact.symbol };
392
+ return {
393
+ platform, location: fact.location,
394
+ ...(fact.symbol === undefined ? {} : { symbol: fact.symbol }),
395
+ ...(fact.sourceLanguage === undefined ? {} : { sourceLanguage: fact.sourceLanguage }),
396
+ };
381
397
  }
382
398
  /** 채널 결과를 target과 이름 순으로 고정한다. */
383
399
  function compareChannels(left, right) {
@@ -1 +1 @@
1
- {"version":3,"file":"join.js","sourceRoot":"","sources":["../../src/join/join.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n BridgeFact,\n BridgeFactsDocument,\n BridgeLocation,\n BridgePlatform,\n BridgeSymbol,\n BridgeTarget,\n} from '../exchange/parse.ts';\nimport { compareStrings } from '../compare.ts';\nimport { isCallerPlatform, isReceiverPlatform } from '../exchange/parse.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 RegistrationWithoutCreation {\n readonly target: BridgeTarget;\n readonly channel: string;\n readonly registrations: 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/**\n * 생산 문서가 밝힌 분석 한계와 출처다.\n *\n * `target`은 이 한계를 신고한 문서의 브리지 메커니즘이다. 사실은 target별로만\n * 조인되므로, 보고서는 이 값으로 한계를 해당 target의 진단에만 귀속시킨다.\n * 사실이 없는 문서의 한계, 선언한 target을 신뢰할 수 없는 mixed-targets 문서의\n * 한계, 교차 입력 한계는 어느 target이 가려졌는지 특정할 수 없어 `null`이다.\n */\nexport interface JoinLimitation {\n readonly platform: BridgePlatform | 'cross-platform';\n readonly target: BridgeTarget | null;\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 registrationsWithoutCreations: readonly RegistrationWithoutCreation[];\n readonly matchedMethods: readonly MatchedMethod[];\n readonly unhandledInvocations: readonly UnhandledInvocation[];\n readonly handlersWithoutInvocations: readonly HandlerWithoutInvocation[];\n readonly limitations: readonly JoinLimitation[];\n}\n\n/** 한 번의 조인에서 허용하는 최대 생산 문서 수다. */\nexport const MAX_DOCUMENTS_PER_JOIN = 256;\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 if (documents.length > MAX_DOCUMENTS_PER_JOIN) {\n throw new BridgeJoinValidationError(\n `Bridge join exceeds the ${MAX_DOCUMENTS_PER_JOIN} document limit.`,\n );\n }\n validateProjects(documents);\n validatePlatformComposition(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 registrationsWithoutCreations = [...groups.values()]\n .filter((group) => group.registrations.length > 0 && group.creations.length === 0)\n .map(({ target, channel, registrations }) => ({\n target,\n channel,\n registrations,\n }))\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 registrationsWithoutCreations,\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; regenerate them from one project root.',\n );\n }\n}\n\n/** 호출 측·수신 측 문서가 모두 있어야 경계 사실로 조인할 수 있다. */\nfunction validatePlatformComposition(\n documents: readonly BridgeFactsDocument[],\n): void {\n const hasCaller = documents.some(({ platform }) => isCallerPlatform(platform));\n const hasReceiver = documents.some(({ platform }) =>\n isReceiverPlatform(platform),\n );\n if (!hasCaller || !hasReceiver) {\n throw new BridgeJoinValidationError(\n 'Bridge documents must include at least one caller platform (dart, js) document '\n + 'and one receiver platform (swift, kotlin) document; run a producer for the missing side.',\n );\n }\n}\n\n/** 사실별 target이 없는 혼합 문서인지 확인한다. */\nfunction hasMixedTargets(document: BridgeFactsDocument): boolean {\n return document.limitations.some((message) =>\n /mixed-targets\\b/i.test(message),\n );\n}\n\n/**\n * 한계를 귀속시킬 target을 결정한다.\n *\n * mixed-targets 문서의 선언 target은 대표값일 뿐 사실별 메커니즘을 복원할 수\n * 없으므로, 그 문서의 한계와 조인 제외 사실 계수는 귀속 없이(null) 남긴다.\n * 귀속을 잃어도 관찰 자체는 보존해야 한다.\n */\nfunction limitationTarget(document: BridgeFactsDocument): BridgeTarget | null {\n return hasMixedTargets(document) ? null : document.target;\n}\n\n/** 안전하게 조인을 보류하면서 입력 한계만 전달한다. */\nfunction emptyJoinResult(limitations: readonly JoinLimitation[]): BridgeJoinResult {\n return {\n deferred: true,\n matchedChannels: [],\n unregisteredChannelCreations: [],\n registrationsWithoutCreations: [],\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 target: limitationTarget(document),\n tool: document.tool.name,\n message,\n })),\n );\n limitations.push(...unjoinedFactLimitations(documents));\n const freshness = freshnessLimitation(documents);\n if (freshness !== undefined) limitations.push(freshness);\n return limitations.sort(compareLimitations);\n}\n\n/**\n * 조인에서 제외한 사실을 isthmus 자신의 관찰 한계로 남긴다.\n *\n * 생산자가 같은 사실을 limitation으로 신고하지 않아도, 또는 신고한 개수가 실제와\n * 달라도 관찰 공백이 보고서에서 사라지지 않아야 한다. 교환 계약은 이 사실들을\n * 세도록 요구하지만 문서의 limitation 문자열은 생산자의 자발적인 신고라\n * 소비자가 신뢰의 근거로 삼을 수 없다.\n */\nfunction unjoinedFactLimitations(\n documents: readonly BridgeFactsDocument[],\n): JoinLimitation[] {\n return [\n ...unjoinedLimitations(\n documents,\n isUnjoinedDynamicChannel,\n 'unjoined-dynamic-channels',\n 'channel facts with a non-literal name',\n ),\n ...unjoinedLimitations(\n documents,\n isUnjoinedDynamicMethod,\n 'unjoined-dynamic-methods',\n 'method facts with a non-literal name',\n ),\n ...unjoinedLimitations(\n documents,\n isUnattributedHandler,\n 'unjoined-unattributed-handlers',\n 'method handler facts without a channel',\n ),\n ];\n}\n\n/** 조건에 맞는 사실 수를 플랫폼·target별로 세어 한계 문장으로 바꾼다. */\nfunction unjoinedLimitations(\n documents: readonly BridgeFactsDocument[],\n matchesFact: (fact: BridgeFact) => boolean,\n prefix: string,\n subject: string,\n): JoinLimitation[] {\n return countFactsByPlatformTarget(documents, matchesFact).map(\n ({ platform, target, count }) => ({\n platform,\n target,\n tool: 'isthmus',\n message: `${prefix}: ${count} ${subject} were not joined`,\n }),\n );\n}\n\n/**\n * 주어진 조건의 서로 다른 사실 수를 생산 플랫폼·target별로 모은다.\n *\n * 사실은 target별로만 조인되므로 다른 target의 사실을 한 한계로 합산하면\n * 관찰 공백의 귀속이 사라진다. mixed-targets 문서의 사실은 귀속 없이(null)\n * 센다. 사실이 없는 문서는 교환 계약상 target이 null이고 사실도 없으므로\n * 여기에서 자연스럽게 제외된다.\n */\nfunction countFactsByPlatformTarget(\n documents: readonly BridgeFactsDocument[],\n matchesFact: (fact: BridgeFact) => boolean,\n): Array<{\n platform: BridgePlatform;\n target: BridgeTarget | null;\n count: number;\n}> {\n const distinctFacts = new Map<\n string,\n {\n platform: BridgePlatform;\n target: BridgeTarget | null;\n keys: Set<string>;\n }\n >();\n for (const document of documents) {\n const target = limitationTarget(document);\n // BridgeTarget은 닫힌 어휘라 문자열화된 null과 충돌하지 않는다.\n const groupKey = `${document.platform}\\u0000${target}`;\n for (const fact of document.facts) {\n if (!matchesFact(fact)) continue;\n const group = distinctFacts.get(groupKey) ?? {\n platform: document.platform,\n target,\n keys: new Set<string>(),\n };\n group.keys.add(unjoinedFactKey(fact));\n distinctFacts.set(groupKey, group);\n }\n }\n return [...distinctFacts.values()].map(({ platform, target, keys }) => ({\n platform,\n target,\n count: keys.size,\n }));\n}\n\n/**\n * 같은 위치의 중복 사실을 한 번만 세는 키를 만든다.\n *\n * 위치 기준은 증거 dedup과 같지만 symbol은 비교하지 않아, 같은 위치의 서로\n * 다른 symbol은 하나로 센다. `channel`은 null일 수 있어 구분자 결합 대신\n * JSON으로 만든다. 그래야 미귀속 핸들러와 이름이 `null`인 채널의 사실이\n * 같은 키로 합쳐지지 않는다.\n */\nfunction unjoinedFactKey(fact: BridgeFact): string {\n const { path, line, column } = fact.location;\n return JSON.stringify([\n fact.kind,\n fact.channel,\n fact.method ?? null,\n path,\n line,\n column,\n ]);\n}\n\n/** 이름이 리터럴이 아니어서 조인하지 못한 채널 사실인지 확인한다. */\nfunction isUnjoinedDynamicChannel(fact: BridgeFact): boolean {\n return (\n fact.dynamic &&\n (fact.kind === 'channel-create' || fact.kind === 'channel-register')\n );\n}\n\n/** 이름이 리터럴이 아니어서 조인하지 못한 메서드 사실인지 확인한다. */\nfunction isUnjoinedDynamicMethod(fact: BridgeFact): boolean {\n return (\n fact.dynamic &&\n (fact.kind === 'method-invoke' || fact.kind === 'method-handle')\n );\n}\n\n/**\n * 어느 채널에 속하는지 몰라 조인하지 못한 핸들러 사실인지 확인한다.\n *\n * dynamic 사실은 위에서 이미 세므로 여기서 다시 세지 않는다.\n */\nfunction isUnattributedHandler(fact: BridgeFact): boolean {\n return fact.kind === 'method-handle' && fact.channel === null && !fact.dynamic;\n}\n\n/** 생성 시각 차이가 하루를 넘을 때 교차 입력 한계를 만든다. */\nfunction freshnessLimitation(\n documents: readonly BridgeFactsDocument[],\n): JoinLimitation | undefined {\n if (documents.length < 2) return undefined;\n let earliest = Number.POSITIVE_INFINITY;\n let latest = Number.NEGATIVE_INFINITY;\n for (const document of documents) {\n const timestamp = Date.parse(document.generatedAt);\n earliest = Math.min(earliest, timestamp);\n latest = Math.max(latest, timestamp);\n }\n const difference = latest - earliest;\n if (difference <= millisecondsPerDay) return undefined;\n const hours = Math.round(difference / millisecondsPerHour);\n return {\n platform: 'cross-platform',\n target: null,\n tool: 'isthmus',\n message: `input-freshness: bridge documents differ by ${hours} hours`,\n };\n}\n\n/** limitation을 플랫폼·target·도구·문장 순으로 고정한다. */\nfunction compareLimitations(left: JoinLimitation, right: JoinLimitation): number {\n return (\n compareStrings(left.platform, right.platform) ||\n compareTargets(left.target, right.target) ||\n compareStrings(left.tool, right.tool) ||\n compareStrings(left.message, right.message)\n );\n}\n\n/** target을 문자열 순으로 고정하고 귀속이 없는(null) 한계를 뒤에 둔다. */\nfunction compareTargets(\n left: BridgeTarget | null,\n right: BridgeTarget | null,\n): number {\n if (left === right) return 0;\n if (left === null) return 1;\n if (right === null) return -1;\n return compareStrings(left, right);\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 if (endpoints.length < 2) return;\n let writeIndex = 1;\n for (let readIndex = 1; readIndex < endpoints.length; readIndex++) {\n const previous = endpoints[writeIndex - 1];\n const current = endpoints[readIndex];\n if (\n previous !== undefined &&\n current !== undefined &&\n compareEndpoints(previous, current) !== 0\n ) {\n endpoints[writeIndex] = current;\n writeIndex++;\n }\n }\n endpoints.length = writeIndex;\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
+ {"version":3,"file":"join.js","sourceRoot":"","sources":["../../src/join/join.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n BridgeFact,\n BridgeFactsDocument,\n BridgeLocation,\n BridgePlatform,\n BridgeSymbol,\n BridgeSourceLanguage,\n BridgeTarget,\n} from '../exchange/parse.ts';\nimport { compareStrings } from '../compare.ts';\nimport { isCallerPlatform, isReceiverPlatform } from '../exchange/parse.ts';\n\n/** 한 언어 문서가 제공한 브리지 증거 위치다. */\nexport interface BridgeEndpoint {\n readonly platform: BridgePlatform;\n readonly location: BridgeLocation;\n readonly symbol?: BridgeSymbol;\n readonly sourceLanguage?: BridgeSourceLanguage;\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 RegistrationWithoutCreation {\n readonly target: BridgeTarget;\n readonly channel: string;\n readonly registrations: 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/**\n * 생산 문서가 밝힌 분석 한계와 출처다.\n *\n * `target`은 이 한계를 신고한 문서의 브리지 메커니즘이다. 사실은 target별로만\n * 조인되므로, 보고서는 이 값으로 한계를 해당 target의 진단에만 귀속시킨다.\n * 사실이 없는 문서의 한계, 선언한 target을 신뢰할 수 없는 mixed-targets 문서의\n * 한계, 교차 입력 한계는 어느 target이 가려졌는지 특정할 수 없어 `null`이다.\n */\nexport interface JoinLimitation {\n readonly platform: BridgePlatform | 'cross-platform';\n readonly target: BridgeTarget | null;\n readonly tool: string;\n readonly message: string;\n readonly channels?: readonly string[];\n /** 소비자가 직접 센 한계에만 부여하며 생산 문서에서는 복사하지 않는다. */\n readonly origin?: 'consumer';\n}\n\n/** 검증된 교환 문서들의 논리 조인 결과다. */\nexport interface BridgeJoinResult {\n readonly deferred: boolean;\n readonly matchedChannels: readonly MatchedChannel[];\n readonly unregisteredChannelCreations: readonly UnregisteredChannelCreation[];\n readonly registrationsWithoutCreations: readonly RegistrationWithoutCreation[];\n readonly matchedMethods: readonly MatchedMethod[];\n readonly unhandledInvocations: readonly UnhandledInvocation[];\n readonly handlersWithoutInvocations: readonly HandlerWithoutInvocation[];\n readonly limitations: readonly JoinLimitation[];\n}\n\n/** 한 번의 조인에서 허용하는 최대 생산 문서 수다. */\nexport const MAX_DOCUMENTS_PER_JOIN = 256;\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 if (documents.length > MAX_DOCUMENTS_PER_JOIN) {\n throw new BridgeJoinValidationError(\n `Bridge join exceeds the ${MAX_DOCUMENTS_PER_JOIN} document limit.`,\n );\n }\n validateProjects(documents);\n validatePlatformComposition(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 registrationsWithoutCreations = [...groups.values()]\n .filter((group) => group.registrations.length > 0 && group.creations.length === 0)\n .map(({ target, channel, registrations }) => ({\n target,\n channel,\n registrations,\n }))\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 registrationsWithoutCreations,\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; regenerate them from one project root.',\n );\n }\n}\n\n/** 호출 측·수신 측 문서가 모두 있어야 경계 사실로 조인할 수 있다. */\nfunction validatePlatformComposition(\n documents: readonly BridgeFactsDocument[],\n): void {\n const hasCaller = documents.some(({ platform }) => isCallerPlatform(platform));\n const hasReceiver = documents.some(({ platform }) =>\n isReceiverPlatform(platform),\n );\n if (!hasCaller || !hasReceiver) {\n throw new BridgeJoinValidationError(\n 'Bridge documents must include at least one caller platform (dart, js) document '\n + 'and one receiver platform (swift, kotlin) document; run a producer for the missing side.',\n );\n }\n}\n\n/** 사실별 target이 없는 혼합 문서인지 확인한다. */\nfunction hasMixedTargets(document: BridgeFactsDocument): boolean {\n return document.limitations.some((message) =>\n /mixed-targets\\b/i.test(message),\n );\n}\n\n/**\n * 한계를 귀속시킬 target을 결정한다.\n *\n * mixed-targets 문서의 선언 target은 대표값일 뿐 사실별 메커니즘을 복원할 수\n * 없으므로, 그 문서의 한계와 조인 제외 사실 계수는 귀속 없이(null) 남긴다.\n * 귀속을 잃어도 관찰 자체는 보존해야 한다.\n */\nfunction limitationTarget(document: BridgeFactsDocument): BridgeTarget | null {\n return hasMixedTargets(document) ? null : document.target;\n}\n\n/** 안전하게 조인을 보류하면서 입력 한계만 전달한다. */\nfunction emptyJoinResult(limitations: readonly JoinLimitation[]): BridgeJoinResult {\n return {\n deferred: true,\n matchedChannels: [],\n unregisteredChannelCreations: [],\n registrationsWithoutCreations: [],\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 const scopes = new Map(document.limitationScopes?.map((scope) => [scope.limitationIndex, scope.channels]));\n return document.limitations.map((message, index) => {\n const channels = scopes.get(index);\n return {\n platform: document.platform,\n target: limitationTarget(document),\n tool: document.tool.name,\n message,\n ...(channels === undefined ? {} : { channels }),\n };\n });\n });\n limitations.push(...unjoinedFactLimitations(documents));\n const freshness = freshnessLimitation(documents);\n if (freshness !== undefined) limitations.push(freshness);\n return limitations.sort(compareLimitations);\n}\n\n/**\n * 조인에서 제외한 사실을 isthmus 자신의 관찰 한계로 남긴다.\n *\n * 생산자가 같은 사실을 limitation으로 신고하지 않아도, 또는 신고한 개수가 실제와\n * 달라도 관찰 공백이 보고서에서 사라지지 않아야 한다. 교환 계약은 이 사실들을\n * 세도록 요구하지만 문서의 limitation 문자열은 생산자의 자발적인 신고라\n * 소비자가 신뢰의 근거로 삼을 수 없다.\n */\nfunction unjoinedFactLimitations(\n documents: readonly BridgeFactsDocument[],\n): JoinLimitation[] {\n return [\n ...unjoinedLimitations(\n documents,\n isUnjoinedDynamicChannel,\n 'unjoined-dynamic-channels',\n 'channel facts with a non-literal name',\n ),\n ...unjoinedLimitations(\n documents,\n isUnjoinedDynamicMethod,\n 'unjoined-dynamic-methods',\n 'method facts with a non-literal name',\n ),\n ...unjoinedLimitations(\n documents,\n isUnattributedHandler,\n 'unjoined-unattributed-handlers',\n 'method handler facts without a channel',\n ),\n ];\n}\n\n/** 조건에 맞는 사실 수를 플랫폼·target별로 세어 한계 문장으로 바꾼다. */\nfunction unjoinedLimitations(\n documents: readonly BridgeFactsDocument[],\n matchesFact: (fact: BridgeFact) => boolean,\n prefix: string,\n subject: string,\n): JoinLimitation[] {\n return countFactsByPlatformTarget(documents, matchesFact).map(\n ({ platform, target, count }) => ({\n platform,\n target,\n tool: 'isthmus',\n origin: 'consumer',\n message: `${prefix}: ${count} ${subject} were not joined`,\n }),\n );\n}\n\n/**\n * 주어진 조건의 서로 다른 사실 수를 생산 플랫폼·target별로 모은다.\n *\n * 사실은 target별로만 조인되므로 다른 target의 사실을 한 한계로 합산하면\n * 관찰 공백의 귀속이 사라진다. mixed-targets 문서의 사실은 귀속 없이(null)\n * 센다. 사실이 없는 문서는 교환 계약상 target이 null이고 사실도 없으므로\n * 여기에서 자연스럽게 제외된다.\n */\nfunction countFactsByPlatformTarget(\n documents: readonly BridgeFactsDocument[],\n matchesFact: (fact: BridgeFact) => boolean,\n): Array<{\n platform: BridgePlatform;\n target: BridgeTarget | null;\n count: number;\n}> {\n const distinctFacts = new Map<\n string,\n {\n platform: BridgePlatform;\n target: BridgeTarget | null;\n keys: Set<string>;\n }\n >();\n for (const document of documents) {\n const target = limitationTarget(document);\n // BridgeTarget은 닫힌 어휘라 문자열화된 null과 충돌하지 않는다.\n const groupKey = `${document.platform}\\u0000${target}`;\n for (const fact of document.facts) {\n if (!matchesFact(fact)) continue;\n const group = distinctFacts.get(groupKey) ?? {\n platform: document.platform,\n target,\n keys: new Set<string>(),\n };\n group.keys.add(unjoinedFactKey(fact));\n distinctFacts.set(groupKey, group);\n }\n }\n return [...distinctFacts.values()].map(({ platform, target, keys }) => ({\n platform,\n target,\n count: keys.size,\n }));\n}\n\n/**\n * 같은 위치의 중복 사실을 한 번만 세는 키를 만든다.\n *\n * 위치 기준은 증거 dedup과 같지만 symbol은 비교하지 않아, 같은 위치의 서로\n * 다른 symbol은 하나로 센다. `channel`은 null일 수 있어 구분자 결합 대신\n * JSON으로 만든다. 그래야 미귀속 핸들러와 이름이 `null`인 채널의 사실이\n * 같은 키로 합쳐지지 않는다.\n */\nfunction unjoinedFactKey(fact: BridgeFact): string {\n const { path, line, column } = fact.location;\n return JSON.stringify([\n fact.kind,\n fact.channel,\n fact.method ?? null,\n path,\n line,\n column,\n ]);\n}\n\n/** 이름이 리터럴이 아니어서 조인하지 못한 채널 사실인지 확인한다. */\nfunction isUnjoinedDynamicChannel(fact: BridgeFact): boolean {\n return (\n fact.dynamic &&\n (fact.kind === 'channel-create' || fact.kind === 'channel-register')\n );\n}\n\n/** 이름이 리터럴이 아니어서 조인하지 못한 메서드 사실인지 확인한다. */\nfunction isUnjoinedDynamicMethod(fact: BridgeFact): boolean {\n return (\n fact.dynamic &&\n (fact.kind === 'method-invoke' || fact.kind === 'method-handle')\n );\n}\n\n/**\n * 어느 채널에 속하는지 몰라 조인하지 못한 핸들러 사실인지 확인한다.\n *\n * dynamic 사실은 위에서 이미 세므로 여기서 다시 세지 않는다.\n */\nfunction isUnattributedHandler(fact: BridgeFact): boolean {\n return fact.kind === 'method-handle' && fact.channel === null && !fact.dynamic;\n}\n\n/** 생성 시각 차이가 하루를 넘을 때 교차 입력 한계를 만든다. */\nfunction freshnessLimitation(\n documents: readonly BridgeFactsDocument[],\n): JoinLimitation | undefined {\n if (documents.length < 2) return undefined;\n let earliest = Number.POSITIVE_INFINITY;\n let latest = Number.NEGATIVE_INFINITY;\n for (const document of documents) {\n const timestamp = Date.parse(document.generatedAt);\n earliest = Math.min(earliest, timestamp);\n latest = Math.max(latest, timestamp);\n }\n const difference = latest - earliest;\n if (difference <= millisecondsPerDay) return undefined;\n const hours = Math.round(difference / millisecondsPerHour);\n return {\n platform: 'cross-platform',\n target: null,\n tool: 'isthmus',\n origin: 'consumer',\n message: `input-freshness: bridge documents differ by ${hours} hours`,\n };\n}\n\n/** limitation을 플랫폼·target·도구·문장 순으로 고정한다. */\nfunction compareLimitations(left: JoinLimitation, right: JoinLimitation): number {\n return (\n compareStrings(left.platform, right.platform) ||\n compareTargets(left.target, right.target) ||\n compareStrings(left.tool, right.tool) ||\n compareStrings(left.message, right.message) ||\n compareOptionalStrings(left.origin, right.origin) ||\n compareStrings(JSON.stringify(left.channels ?? null), JSON.stringify(right.channels ?? null))\n );\n}\n\n/** target을 문자열 순으로 고정하고 귀속이 없는(null) 한계를 뒤에 둔다. */\nfunction compareTargets(\n left: BridgeTarget | null,\n right: BridgeTarget | null,\n): number {\n if (left === right) return 0;\n if (left === null) return 1;\n if (right === null) return -1;\n return compareStrings(left, right);\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 if (endpoints.length < 2) return;\n let writeIndex = 1;\n for (let readIndex = 1; readIndex < endpoints.length; readIndex++) {\n const previous = endpoints[writeIndex - 1];\n const current = endpoints[readIndex];\n if (\n previous !== undefined &&\n current !== undefined &&\n compareEndpoints(previous, current) !== 0\n ) {\n endpoints[writeIndex] = current;\n writeIndex++;\n }\n }\n endpoints.length = writeIndex;\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 const languageOrder = compareOptionalStrings(left.sourceLanguage, right.sourceLanguage);\n if (languageOrder !== 0) return languageOrder;\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 {\n platform, location: fact.location,\n ...(fact.symbol === undefined ? {} : { symbol: fact.symbol }),\n ...(fact.sourceLanguage === undefined ? {} : { sourceLanguage: fact.sourceLanguage }),\n };\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"]}
@@ -13,8 +13,8 @@ export function createCheckReport(joined) {
13
13
  const gaps = receiverCoverageGaps(joined.limitations);
14
14
  const issues = [
15
15
  ...joined.unhandledInvocations.map((item) => ({
16
- severity: gaps.hidesHandlers(item.target) ? 'warning' : 'error',
17
- code: gaps.hidesHandlers(item.target)
16
+ severity: gaps.hidesHandlers(item.target, item.channel) ? 'warning' : 'error',
17
+ code: gaps.hidesHandlers(item.target, item.channel)
18
18
  ? 'unhandled-invocation-unverified'
19
19
  : 'unhandled-invocation',
20
20
  target: item.target,
@@ -23,8 +23,8 @@ export function createCheckReport(joined) {
23
23
  evidence: item.invocations,
24
24
  })),
25
25
  ...joined.unregisteredChannelCreations.map((item) => ({
26
- severity: gaps.hidesRegistrations(item.target) ? 'warning' : 'error',
27
- code: gaps.hidesRegistrations(item.target)
26
+ severity: gaps.hidesRegistrations(item.target, item.channel) ? 'warning' : 'error',
27
+ code: gaps.hidesRegistrations(item.target, item.channel)
28
28
  ? 'unregistered-channel-creation-unverified'
29
29
  : 'unregistered-channel-creation',
30
30
  target: item.target,
@@ -89,21 +89,43 @@ function receiverCoverageGaps(limitations) {
89
89
  const existing = memoized.get(target);
90
90
  if (existing !== undefined)
91
91
  return existing;
92
- const relevant = receiverLimitations.filter(({ target: gapTarget }) => gapTarget === null || gapTarget === target);
93
92
  const gaps = {
94
- handlers: relevant.some(({ tool, message }) => startsWithAny(producerHandlerGapPrefixes)(message) ||
95
- (tool === 'isthmus' &&
96
- startsWithAny(isthmusHandlerGapPrefixes)(message))),
97
- registrations: relevant.some(({ tool, message }) => startsWithAny(sourceCoverageGapPrefixes)(message) ||
98
- (tool === 'isthmus' &&
99
- startsWithAny(isthmusRegistrationGapPrefixes)(message))),
93
+ allHandlers: false, allRegistrations: false,
94
+ handlerChannels: new Set(), registrationChannels: new Set(),
100
95
  };
96
+ for (const { target: gapTarget, tool, message, channels, origin } of receiverLimitations) {
97
+ if (gapTarget !== null && gapTarget !== target)
98
+ continue;
99
+ const handlers = startsWithAny(producerHandlerGapPrefixes)(message) ||
100
+ (origin === 'consumer' && tool === 'isthmus' && startsWithAny(isthmusHandlerGapPrefixes)(message));
101
+ const registrations = startsWithAny(sourceCoverageGapPrefixes)(message) ||
102
+ (origin === 'consumer' && tool === 'isthmus' && startsWithAny(isthmusRegistrationGapPrefixes)(message));
103
+ // 하나라도 범위가 불명확한 공백이 있으면 같은 target의 좁은 범위로 덮지 않는다.
104
+ if (channels === undefined) {
105
+ gaps.allHandlers ||= handlers;
106
+ gaps.allRegistrations ||= registrations;
107
+ }
108
+ else {
109
+ for (const channel of channels) {
110
+ if (handlers)
111
+ gaps.handlerChannels.add(channel);
112
+ if (registrations)
113
+ gaps.registrationChannels.add(channel);
114
+ }
115
+ }
116
+ }
101
117
  memoized.set(target, gaps);
102
118
  return gaps;
103
119
  };
104
120
  return {
105
- hidesHandlers: (target) => gapsFor(target).handlers,
106
- hidesRegistrations: (target) => gapsFor(target).registrations,
121
+ hidesHandlers: (target, channel) => {
122
+ const gaps = gapsFor(target);
123
+ return gaps.allHandlers || gaps.handlerChannels.has(channel);
124
+ },
125
+ hidesRegistrations: (target, channel) => {
126
+ const gaps = gapsFor(target);
127
+ return gaps.allRegistrations || gaps.registrationChannels.has(channel);
128
+ },
107
129
  };
108
130
  }
109
131
  /** 주어진 접두사 중 하나로 시작하는지 검사하는 술어를 만든다. */
@@ -1 +1 @@
1
- {"version":3,"file":"check-report.js","sourceRoot":"","sources":["../../src/report/check-report.ts"],"names":[],"mappings":"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","sourcesContent":["import type { BridgeTarget } from '../exchange/parse.ts';\nimport { isReceiverPlatform } from '../exchange/parse.ts';\nimport type {\n BridgeEndpoint,\n BridgeJoinResult,\n JoinLimitation,\n} from '../join/join.ts';\nimport { isBridgeJoinDeferred } 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/**\n * check가 보고하는 안정적인 진단 종류다.\n *\n * `-unverified` 종류는 수신 측이 스스로 분석 공백을 신고해, 핸들러가 없는 것인지\n * 보지 못한 것인지 구분할 수 없는 경우다. 사실과 증거는 같지만 판정이 아니다.\n */\nexport type CheckIssueCode =\n | 'unhandled-invocation'\n | 'unhandled-invocation-unverified'\n | 'unregistered-channel-creation'\n | 'unregistered-channel-creation-unverified'\n | 'registration-without-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 if (isBridgeJoinDeferred(joined)) {\n throw new Error('Cannot create a check report from a deferred bridge join.');\n }\n const gaps = receiverCoverageGaps(joined.limitations);\n const issues: CheckIssue[] = [\n ...joined.unhandledInvocations.map<CheckIssue>((item) => ({\n severity: gaps.hidesHandlers(item.target) ? 'warning' : 'error',\n code: gaps.hidesHandlers(item.target)\n ? 'unhandled-invocation-unverified'\n : '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: gaps.hidesRegistrations(item.target) ? 'warning' : 'error',\n code: gaps.hidesRegistrations(item.target)\n ? 'unregistered-channel-creation-unverified'\n : 'unregistered-channel-creation',\n target: item.target,\n channel: item.channel,\n evidence: item.creations,\n })),\n ...joined.registrationsWithoutCreations.map<CheckIssue>((item) => ({\n severity: 'warning',\n code: 'registration-without-creation',\n target: item.target,\n channel: item.channel,\n evidence: item.registrations,\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 return {\n format: 'isthmus-check',\n version: 1,\n summary: {\n errors: issues.filter(({ severity }) => severity === 'error').length,\n warnings: issues.filter(({ severity }) => severity === 'warning').length,\n matchedChannels: joined.matchedChannels.length,\n matchedMethods: joined.matchedMethods.length,\n },\n issues,\n limitations: joined.limitations,\n };\n}\n\n/** 수신 측이 스스로 알린 분석 공백이 target별로 무엇을 가리는지 나타낸다. */\ninterface ReceiverCoverageGaps {\n hidesHandlers(target: BridgeTarget): boolean;\n hidesRegistrations(target: BridgeTarget): boolean;\n}\n\n/**\n * 수신 측이 핸들러나 등록을 놓쳤을 수 있다고 스스로 알렸는지 target별로 확인한다.\n *\n * 이때 \"핸들러 없는 호출\"은 경계 불일치가 아니라 판정 불가다. Objective-C로 쓰인\n * Flutter 핸들러처럼 수신 측 분석에 아예 나타나지 않는 코드가 실제로 있어서,\n * error로 단정하면 이 도구가 없애려던 오탐을 이 도구가 만든다.\n *\n * 공백의 종류는 구분한다. 이름이 리터럴이 아닌 채널 등록 하나가 무관한 메서드\n * 진단까지 무르게 하면 안 된다. 호출 측 한계는 네이티브 코드를 가리지 않으므로\n * 수신 측 플랫폼의 한계만 본다.\n *\n * 완화 단위는 진단의 target이다. 사실은 target별로만 조인되므로, 다른 target의\n * 수신 문서가 신고한 공백은 현재 target의 핸들러를 가릴 수 없다. target이 없는\n * (사실이 없는) 수신 문서는 어느 target을 분석했는지 특정할 수 없어 모든 target에\n * 적용한다. 같은 target에 귀속된 수신 문서가 사실과 함께 존재해도 마찬가지다.\n * 수신 문서 여러 개가 소스 트리를 나누어 가졌을 수 있으므로, 귀속 없는 문서가\n * 본 소스가 해당 target의 핸들러를 가릴 가능성을 배제할 수 없기 때문이다.\n *\n * `unjoined-` 접두사는 isthmus가 직접 세어 자신을 출처로 밝힌 한계만 인정한다.\n * 생산자 문자열은 신뢰의 근거가 아니므로, 같은 접두사를 차용한 생산자 신고는\n * 완화 근거가 되지 못한다.\n */\nfunction receiverCoverageGaps(\n limitations: readonly JoinLimitation[],\n): ReceiverCoverageGaps {\n const receiverLimitations = limitations.filter(({ platform }) =>\n isReceiverPlatform(platform),\n );\n const memoized = new Map<\n BridgeTarget,\n { handlers: boolean; registrations: boolean }\n >();\n const gapsFor = (target: BridgeTarget) => {\n const existing = memoized.get(target);\n if (existing !== undefined) return existing;\n const relevant = receiverLimitations.filter(\n ({ target: gapTarget }) => gapTarget === null || gapTarget === target,\n );\n const gaps = {\n handlers: relevant.some(\n ({ tool, message }) =>\n startsWithAny(producerHandlerGapPrefixes)(message) ||\n (tool === 'isthmus' &&\n startsWithAny(isthmusHandlerGapPrefixes)(message)),\n ),\n registrations: relevant.some(\n ({ tool, message }) =>\n startsWithAny(sourceCoverageGapPrefixes)(message) ||\n (tool === 'isthmus' &&\n startsWithAny(isthmusRegistrationGapPrefixes)(message)),\n ),\n };\n memoized.set(target, gaps);\n return gaps;\n };\n return {\n hidesHandlers: (target) => gapsFor(target).handlers,\n hidesRegistrations: (target) => gapsFor(target).registrations,\n };\n}\n\n/** 주어진 접두사 중 하나로 시작하는지 검사하는 술어를 만든다. */\nfunction startsWithAny(\n prefixes: readonly string[],\n): (message: string) => boolean {\n return (message) => prefixes.some((prefix) => message.startsWith(prefix));\n}\n\n/**\n * 수신 측 소스 자체가 분석되지 않아 등록과 핸들러를 모두 가리는 한계다.\n *\n * 알려진 접두사만 인정한다. 모르는 한계를 공백으로 넓게 해석하면 진짜 불일치가\n * 경고로 묻힌다.\n */\nconst sourceCoverageGapPrefixes = [\n 'objective-c-sources:',\n 'shadowed-flutter-method-channel:',\n];\n\n/** 생산자가 신고하는, 핸들러 본문만 가리는 한계다. */\nconst handlerBodyCoverageGapPrefixes = ['opaque-handler-bodies:'];\n\n/** 생산자 신고 중 핸들러를 가릴 수 있는 한계 전체다. */\nconst producerHandlerGapPrefixes = [\n ...sourceCoverageGapPrefixes,\n ...handlerBodyCoverageGapPrefixes,\n];\n\n/**\n * isthmus가 직접 센 한계 중 핸들러를 가리는 접두사다.\n *\n * 생산자의 신고 개수에 의존하지 않으므로 `tool`이 `isthmus`인 항목만 인정한다.\n */\nconst isthmusHandlerGapPrefixes = [\n 'unjoined-dynamic-methods:',\n 'unjoined-unattributed-handlers:',\n];\n\n/** isthmus가 직접 센 한계 중 채널 등록을 가리는 접두사다. */\nconst isthmusRegistrationGapPrefixes = ['unjoined-dynamic-channels:'];\n"]}
1
+ {"version":3,"file":"check-report.js","sourceRoot":"","sources":["../../src/report/check-report.ts"],"names":[],"mappings":"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","sourcesContent":["import type { BridgeTarget } from '../exchange/parse.ts';\nimport { isReceiverPlatform } from '../exchange/parse.ts';\nimport type {\n BridgeEndpoint,\n BridgeJoinResult,\n JoinLimitation,\n} from '../join/join.ts';\nimport { isBridgeJoinDeferred } 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/**\n * check가 보고하는 안정적인 진단 종류다.\n *\n * `-unverified` 종류는 수신 측이 스스로 분석 공백을 신고해, 핸들러가 없는 것인지\n * 보지 못한 것인지 구분할 수 없는 경우다. 사실과 증거는 같지만 판정이 아니다.\n */\nexport type CheckIssueCode =\n | 'unhandled-invocation'\n | 'unhandled-invocation-unverified'\n | 'unregistered-channel-creation'\n | 'unregistered-channel-creation-unverified'\n | 'registration-without-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 if (isBridgeJoinDeferred(joined)) {\n throw new Error('Cannot create a check report from a deferred bridge join.');\n }\n const gaps = receiverCoverageGaps(joined.limitations);\n const issues: CheckIssue[] = [\n ...joined.unhandledInvocations.map<CheckIssue>((item) => ({\n severity: gaps.hidesHandlers(item.target, item.channel) ? 'warning' : 'error',\n code: gaps.hidesHandlers(item.target, item.channel)\n ? 'unhandled-invocation-unverified'\n : '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: gaps.hidesRegistrations(item.target, item.channel) ? 'warning' : 'error',\n code: gaps.hidesRegistrations(item.target, item.channel)\n ? 'unregistered-channel-creation-unverified'\n : 'unregistered-channel-creation',\n target: item.target,\n channel: item.channel,\n evidence: item.creations,\n })),\n ...joined.registrationsWithoutCreations.map<CheckIssue>((item) => ({\n severity: 'warning',\n code: 'registration-without-creation',\n target: item.target,\n channel: item.channel,\n evidence: item.registrations,\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 return {\n format: 'isthmus-check',\n version: 1,\n summary: {\n errors: issues.filter(({ severity }) => severity === 'error').length,\n warnings: issues.filter(({ severity }) => severity === 'warning').length,\n matchedChannels: joined.matchedChannels.length,\n matchedMethods: joined.matchedMethods.length,\n },\n issues,\n limitations: joined.limitations,\n };\n}\n\n/** 수신 측이 스스로 알린 분석 공백이 target별로 무엇을 가리는지 나타낸다. */\ninterface ReceiverCoverageGaps {\n hidesHandlers(target: BridgeTarget, channel: string): boolean;\n hidesRegistrations(target: BridgeTarget, channel: string): boolean;\n}\n\n/**\n * 수신 측이 핸들러나 등록을 놓쳤을 수 있다고 스스로 알렸는지 target별로 확인한다.\n *\n * 이때 \"핸들러 없는 호출\"은 경계 불일치가 아니라 판정 불가다. Objective-C로 쓰인\n * Flutter 핸들러처럼 수신 측 분석에 아예 나타나지 않는 코드가 실제로 있어서,\n * error로 단정하면 이 도구가 없애려던 오탐을 이 도구가 만든다.\n *\n * 공백의 종류는 구분한다. 이름이 리터럴이 아닌 채널 등록 하나가 무관한 메서드\n * 진단까지 무르게 하면 안 된다. 호출 측 한계는 네이티브 코드를 가리지 않으므로\n * 수신 측 플랫폼의 한계만 본다.\n *\n * 완화 단위는 진단의 target이다. 사실은 target별로만 조인되므로, 다른 target의\n * 수신 문서가 신고한 공백은 현재 target의 핸들러를 가릴 수 없다. target이 없는\n * (사실이 없는) 수신 문서는 어느 target을 분석했는지 특정할 수 없어 모든 target에\n * 적용한다. 같은 target에 귀속된 수신 문서가 사실과 함께 존재해도 마찬가지다.\n * 수신 문서 여러 개가 소스 트리를 나누어 가졌을 수 있으므로, 귀속 없는 문서가\n * 본 소스가 해당 target의 핸들러를 가릴 가능성을 배제할 수 없기 때문이다.\n *\n * `unjoined-` 접두사는 isthmus가 직접 세어 자신을 출처로 밝힌 한계만 인정한다.\n * 생산자 문자열은 신뢰의 근거가 아니므로, 같은 접두사를 차용한 생산자 신고는\n * 완화 근거가 되지 못한다.\n */\nfunction receiverCoverageGaps(\n limitations: readonly JoinLimitation[],\n): ReceiverCoverageGaps {\n const receiverLimitations = limitations.filter(({ platform }) =>\n isReceiverPlatform(platform),\n );\n const memoized = new Map<BridgeTarget, {\n allHandlers: boolean; allRegistrations: boolean;\n handlerChannels: Set<string>; registrationChannels: Set<string>;\n }>();\n const gapsFor = (target: BridgeTarget) => {\n const existing = memoized.get(target);\n if (existing !== undefined) return existing;\n const gaps = {\n allHandlers: false, allRegistrations: false,\n handlerChannels: new Set<string>(), registrationChannels: new Set<string>(),\n };\n for (const { target: gapTarget, tool, message, channels, origin } of receiverLimitations) {\n if (gapTarget !== null && gapTarget !== target) continue;\n const handlers = startsWithAny(producerHandlerGapPrefixes)(message) ||\n (origin === 'consumer' && tool === 'isthmus' && startsWithAny(isthmusHandlerGapPrefixes)(message));\n const registrations = startsWithAny(sourceCoverageGapPrefixes)(message) ||\n (origin === 'consumer' && tool === 'isthmus' && startsWithAny(isthmusRegistrationGapPrefixes)(message));\n // 하나라도 범위가 불명확한 공백이 있으면 같은 target의 좁은 범위로 덮지 않는다.\n if (channels === undefined) {\n gaps.allHandlers ||= handlers;\n gaps.allRegistrations ||= registrations;\n } else {\n for (const channel of channels) {\n if (handlers) gaps.handlerChannels.add(channel);\n if (registrations) gaps.registrationChannels.add(channel);\n }\n }\n }\n memoized.set(target, gaps);\n return gaps;\n };\n return {\n hidesHandlers: (target, channel) => {\n const gaps = gapsFor(target);\n return gaps.allHandlers || gaps.handlerChannels.has(channel);\n },\n hidesRegistrations: (target, channel) => {\n const gaps = gapsFor(target);\n return gaps.allRegistrations || gaps.registrationChannels.has(channel);\n },\n };\n}\n\n/** 주어진 접두사 중 하나로 시작하는지 검사하는 술어를 만든다. */\nfunction startsWithAny(\n prefixes: readonly string[],\n): (message: string) => boolean {\n return (message) => prefixes.some((prefix) => message.startsWith(prefix));\n}\n\n/**\n * 수신 측 소스 자체가 분석되지 않아 등록과 핸들러를 모두 가리는 한계다.\n *\n * 알려진 접두사만 인정한다. 모르는 한계를 공백으로 넓게 해석하면 진짜 불일치가\n * 경고로 묻힌다.\n */\nconst sourceCoverageGapPrefixes = [\n 'objective-c-sources:',\n 'shadowed-flutter-method-channel:',\n];\n\n/** 생산자가 신고하는, 핸들러 본문만 가리는 한계다. */\nconst handlerBodyCoverageGapPrefixes = ['opaque-handler-bodies:'];\n\n/** 생산자 신고 중 핸들러를 가릴 수 있는 한계 전체다. */\nconst producerHandlerGapPrefixes = [\n ...sourceCoverageGapPrefixes,\n ...handlerBodyCoverageGapPrefixes,\n];\n\n/**\n * isthmus가 직접 센 한계 중 핸들러를 가리는 접두사다.\n *\n * 생산자의 신고 개수에 의존하지 않으므로 `tool`이 `isthmus`인 항목만 인정한다.\n */\nconst isthmusHandlerGapPrefixes = [\n 'unjoined-dynamic-methods:',\n 'unjoined-unattributed-handlers:',\n];\n\n/** isthmus가 직접 센 한계 중 채널 등록을 가리는 접두사다. */\nconst isthmusRegistrationGapPrefixes = ['unjoined-dynamic-channels:'];\n"]}
@@ -68,7 +68,7 @@ function issueKey(item) {
68
68
  return JSON.stringify([item.code, logicalKey(item)]);
69
69
  }
70
70
  function limitationKey(item) {
71
- return JSON.stringify([item.platform, item.target, item.tool, item.message]);
71
+ return JSON.stringify([item.platform, item.target, item.tool, item.message, item.channels ?? null, item.origin ?? null]);
72
72
  }
73
73
  /** 키 집합 차이를 안정적으로 정렬하고 원래 증거를 보존한다. */
74
74
  function difference(left, right, key) {
@@ -1 +1 @@
1
- {"version":3,"file":"diff.js","sourceRoot":"","sources":["../../src/report/diff.ts"],"names":[],"mappings":"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","sourcesContent":["import type { BridgeFactsDocument, BridgeTarget } from '../exchange/parse.ts';\nimport { compareStrings } from '../compare.ts';\nimport { BridgeJoinValidationError, isBridgeJoinDeferred, joinBridgeDocuments } from '../join/join.ts';\nimport { createCheckReport } from './check-report.ts';\n\n/** 동일 프로젝트의 관찰 결과를 비교하며 삭제 안전성이나 rename을 추측하지 않는다. */\nexport function createBridgeDiff(\n before: readonly BridgeFactsDocument[],\n after: readonly BridgeFactsDocument[],\n) {\n validateSnapshots(before, after);\n const oldJoin = joinBridgeDocuments(before);\n const newJoin = joinBridgeDocuments(after);\n if (isBridgeJoinDeferred(oldJoin) || isBridgeJoinDeferred(newJoin)) {\n throw new BridgeJoinValidationError(\n 'Cannot compare deferred bridge joins; split mixed bridge targets and retry.',\n );\n }\n const oldReport = createCheckReport(oldJoin);\n const newReport = createCheckReport(newJoin);\n const addedMethods = difference(newJoin.matchedMethods, oldJoin.matchedMethods, logicalKey);\n const removedMethods = difference(oldJoin.matchedMethods, newJoin.matchedMethods, logicalKey);\n const introducedIssues = difference(newReport.issues, oldReport.issues, issueKey);\n const resolvedIssues = difference(oldReport.issues, newReport.issues, issueKey);\n return {\n format: 'isthmus-diff' as const,\n version: 1 as const,\n summary: {\n addedMethods: addedMethods.length,\n removedMethods: removedMethods.length,\n introducedErrors: introducedIssues.filter((issue) => issue.severity === 'error').length,\n introducedWarnings: introducedIssues.filter((issue) => issue.severity === 'warning').length,\n resolvedIssues: resolvedIssues.length,\n },\n addedMethods,\n removedMethods,\n introducedIssues,\n resolvedIssues,\n limitations: {\n before: oldReport.limitations,\n after: newReport.limitations,\n added: difference(newReport.limitations, oldReport.limitations, limitationKey),\n removed: difference(oldReport.limitations, newReport.limitations, limitationKey),\n },\n producers: { before: producerVersions(before), after: producerVersions(after) },\n };\n}\n\n/** 플랫폼 누락이나 다른 프로젝트를 코드 삭제로 오해하지 않도록 입력 구성을 고정한다. */\nfunction validateSnapshots(before: readonly BridgeFactsDocument[], after: readonly BridgeFactsDocument[]): void {\n const all = [...before, ...after];\n if (new Set(all.map((doc) => doc.project)).size !== 1 ||\n ![before, after].every((docs) => docs.some((doc) => doc.platform === 'dart') &&\n docs.some((doc) => doc.platform === 'swift')) ||\n JSON.stringify(producerInventory(before)) !== JSON.stringify(producerInventory(after)) ||\n all.some((doc) => (doc.platform !== 'dart' && doc.platform !== 'swift') ||\n (doc.target !== null && doc.target !== 'flutter'))) {\n throw new BridgeJoinValidationError(\n 'Diff requires the same project and matching Flutter dart/swift producer '\n + 'inventories in both snapshots; rebuild both snapshots from one checkout.',\n );\n }\n}\n\n/** 버전 변화는 출력하되 플랫폼·도구별 문서 개수 변화는 허용하지 않는다. */\nfunction producerInventory(docs: readonly BridgeFactsDocument[]): string[] {\n return docs.map((doc) => JSON.stringify([doc.platform, doc.tool.name])).sort(compareStrings);\n}\n\n/** 추출기 업그레이드가 관찰 차이의 원인인지 검토할 버전 근거다. */\nfunction producerVersions(docs: readonly BridgeFactsDocument[]) {\n return docs.map((doc) => ({ platform: doc.platform, ...doc.tool, generatedAt: doc.generatedAt }))\n .sort((a, b) => compareStrings(JSON.stringify(a), JSON.stringify(b)));\n}\n\n/** 충돌 없는 논리 키로 비교해 소스 줄 이동을 추가·삭제로 보고하지 않는다. */\nfunction logicalKey(item: { readonly target: BridgeTarget; readonly channel: string; readonly method?: string }): string {\n return JSON.stringify([item.target, item.channel, item.method ?? null]);\n}\n\nfunction issueKey(item: Parameters<typeof logicalKey>[0] & { readonly code: string }): string {\n return JSON.stringify([item.code, logicalKey(item)]);\n}\n\nfunction limitationKey(item: { readonly platform: string; readonly target: string | null; readonly tool: string; readonly message: string }): string {\n return JSON.stringify([item.platform, item.target, item.tool, item.message]);\n}\n\n/** 키 집합 차이를 안정적으로 정렬하고 원래 증거를 보존한다. */\nfunction difference<T>(left: readonly T[], right: readonly T[], key: (item: T) => string): T[] {\n const existing = new Set(right.map(key));\n return left.filter((item) => !existing.has(key(item)))\n .sort((a, b) => compareStrings(key(a), key(b)));\n}\n"]}
1
+ {"version":3,"file":"diff.js","sourceRoot":"","sources":["../../src/report/diff.ts"],"names":[],"mappings":"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","sourcesContent":["import type { BridgeFactsDocument, BridgeTarget } from '../exchange/parse.ts';\nimport { compareStrings } from '../compare.ts';\nimport { BridgeJoinValidationError, isBridgeJoinDeferred, joinBridgeDocuments } from '../join/join.ts';\nimport { createCheckReport } from './check-report.ts';\n\n/** 동일 프로젝트의 관찰 결과를 비교하며 삭제 안전성이나 rename을 추측하지 않는다. */\nexport function createBridgeDiff(\n before: readonly BridgeFactsDocument[],\n after: readonly BridgeFactsDocument[],\n) {\n validateSnapshots(before, after);\n const oldJoin = joinBridgeDocuments(before);\n const newJoin = joinBridgeDocuments(after);\n if (isBridgeJoinDeferred(oldJoin) || isBridgeJoinDeferred(newJoin)) {\n throw new BridgeJoinValidationError(\n 'Cannot compare deferred bridge joins; split mixed bridge targets and retry.',\n );\n }\n const oldReport = createCheckReport(oldJoin);\n const newReport = createCheckReport(newJoin);\n const addedMethods = difference(newJoin.matchedMethods, oldJoin.matchedMethods, logicalKey);\n const removedMethods = difference(oldJoin.matchedMethods, newJoin.matchedMethods, logicalKey);\n const introducedIssues = difference(newReport.issues, oldReport.issues, issueKey);\n const resolvedIssues = difference(oldReport.issues, newReport.issues, issueKey);\n return {\n format: 'isthmus-diff' as const,\n version: 1 as const,\n summary: {\n addedMethods: addedMethods.length,\n removedMethods: removedMethods.length,\n introducedErrors: introducedIssues.filter((issue) => issue.severity === 'error').length,\n introducedWarnings: introducedIssues.filter((issue) => issue.severity === 'warning').length,\n resolvedIssues: resolvedIssues.length,\n },\n addedMethods,\n removedMethods,\n introducedIssues,\n resolvedIssues,\n limitations: {\n before: oldReport.limitations,\n after: newReport.limitations,\n added: difference(newReport.limitations, oldReport.limitations, limitationKey),\n removed: difference(oldReport.limitations, newReport.limitations, limitationKey),\n },\n producers: { before: producerVersions(before), after: producerVersions(after) },\n };\n}\n\n/** 플랫폼 누락이나 다른 프로젝트를 코드 삭제로 오해하지 않도록 입력 구성을 고정한다. */\nfunction validateSnapshots(before: readonly BridgeFactsDocument[], after: readonly BridgeFactsDocument[]): void {\n const all = [...before, ...after];\n if (new Set(all.map((doc) => doc.project)).size !== 1 ||\n ![before, after].every((docs) => docs.some((doc) => doc.platform === 'dart') &&\n docs.some((doc) => doc.platform === 'swift')) ||\n JSON.stringify(producerInventory(before)) !== JSON.stringify(producerInventory(after)) ||\n all.some((doc) => (doc.platform !== 'dart' && doc.platform !== 'swift') ||\n (doc.target !== null && doc.target !== 'flutter'))) {\n throw new BridgeJoinValidationError(\n 'Diff requires the same project and matching Flutter dart/swift producer '\n + 'inventories in both snapshots; rebuild both snapshots from one checkout.',\n );\n }\n}\n\n/** 버전 변화는 출력하되 플랫폼·도구별 문서 개수 변화는 허용하지 않는다. */\nfunction producerInventory(docs: readonly BridgeFactsDocument[]): string[] {\n return docs.map((doc) => JSON.stringify([doc.platform, doc.tool.name])).sort(compareStrings);\n}\n\n/** 추출기 업그레이드가 관찰 차이의 원인인지 검토할 버전 근거다. */\nfunction producerVersions(docs: readonly BridgeFactsDocument[]) {\n return docs.map((doc) => ({ platform: doc.platform, ...doc.tool, generatedAt: doc.generatedAt }))\n .sort((a, b) => compareStrings(JSON.stringify(a), JSON.stringify(b)));\n}\n\n/** 충돌 없는 논리 키로 비교해 소스 줄 이동을 추가·삭제로 보고하지 않는다. */\nfunction logicalKey(item: { readonly target: BridgeTarget; readonly channel: string; readonly method?: string }): string {\n return JSON.stringify([item.target, item.channel, item.method ?? null]);\n}\n\nfunction issueKey(item: Parameters<typeof logicalKey>[0] & { readonly code: string }): string {\n return JSON.stringify([item.code, logicalKey(item)]);\n}\n\nfunction limitationKey(item: { readonly platform: string; readonly target: string | null; readonly tool: string; readonly message: string; readonly channels?: readonly string[]; readonly origin?: 'consumer' }): string {\n return JSON.stringify([item.platform, item.target, item.tool, item.message, item.channels ?? null, item.origin ?? null]);\n}\n\n/** 키 집합 차이를 안정적으로 정렬하고 원래 증거를 보존한다. */\nfunction difference<T>(left: readonly T[], right: readonly T[], key: (item: T) => string): T[] {\n const existing = new Set(right.map(key));\n return left.filter((item) => !existing.has(key(item)))\n .sort((a, b) => compareStrings(key(a), key(b)));\n}\n"]}
@@ -1,4 +1,4 @@
1
- import type { BridgeLocation, BridgePlatform, BridgeSymbol, BridgeTarget } from '../exchange/parse.ts';
1
+ import type { BridgeLocation, BridgePlatform, BridgeSymbol, BridgeSourceLanguage, BridgeTarget } from '../exchange/parse.ts';
2
2
  import type { BridgeJoinResult, JoinLimitation } from '../join/join.ts';
3
3
  /** 소스 위치 하나를 나타내는 경계 그래프 노드다. */
4
4
  export interface BridgeGraphNode {
@@ -6,6 +6,7 @@ export interface BridgeGraphNode {
6
6
  readonly platform: BridgePlatform;
7
7
  readonly location: BridgeLocation;
8
8
  readonly symbol?: BridgeSymbol;
9
+ readonly sourceLanguage?: BridgeSourceLanguage;
9
10
  }
10
11
  /** 호출 측 위치에서 수신 측 위치로 향하는 경계 간선이다. */
11
12
  export interface BridgeGraphEdge {
@@ -33,7 +33,7 @@ function renderMermaid(graph) {
33
33
  lines.push(` %% limitation: ${limitationComment(limitation)}`);
34
34
  }
35
35
  for (const node of graph.nodes) {
36
- const label = `${mermaidText(node.platform)}<br/>${mermaidText(locationLabel(node.location))}`;
36
+ const label = `${mermaidText(node.sourceLanguage ?? node.platform)}<br/>${mermaidText(locationLabel(node.location))}`;
37
37
  lines.push(` ${identifiers.get(node.id)}["${label}"]`);
38
38
  }
39
39
  for (const edge of graph.edges) {
@@ -61,7 +61,7 @@ function renderDot(graph) {
61
61
  lines.push(` // limitation: ${limitationComment(limitation)}`);
62
62
  }
63
63
  for (const node of graph.nodes) {
64
- const label = `${node.platform}\n${locationLabel(node.location)}`;
64
+ const label = `${node.sourceLanguage ?? node.platform}\n${locationLabel(node.location)}`;
65
65
  lines.push(` ${dotString(node.id)} [label=${dotString(label)}];`);
66
66
  }
67
67
  for (const edge of graph.edges) {
@@ -74,7 +74,9 @@ function renderDot(graph) {
74
74
  /** 분석 한계를 텍스트 그래프의 한 줄 주석으로 안전하게 만든다. */
75
75
  function limitationComment(limitation) {
76
76
  const text = `${limitation.platform}/${limitation.target}/${limitation.tool}: `
77
- + limitation.message;
77
+ + limitation.message
78
+ + (limitation.origin === undefined ? '' : ' [origin: consumer]')
79
+ + (limitation.channels === undefined ? '' : ` [channels: ${JSON.stringify(limitation.channels)}]`);
78
80
  return text.replace(/[\u0000-\u001f\u007f-\u009f\u2028\u2029]+/gu, ' ');
79
81
  }
80
82
  /** DOT 문자열 리터럴로 안전하게 이스케이프한다. */
@@ -167,14 +169,7 @@ function addNode(nodes, endpoint) {
167
169
  const id = endpointId(endpoint);
168
170
  const existing = nodes.get(id);
169
171
  if (existing === undefined) {
170
- nodes.set(id, endpoint.symbol === undefined
171
- ? { id, platform: endpoint.platform, location: endpoint.location }
172
- : {
173
- id,
174
- platform: endpoint.platform,
175
- location: endpoint.location,
176
- symbol: endpoint.symbol,
177
- });
172
+ nodes.set(id, { id, ...endpoint });
178
173
  }
179
174
  else if (endpoint.symbol !== undefined) {
180
175
  const symbol = mergeSymbols(existing.symbol, endpoint.symbol);
@@ -202,7 +197,8 @@ function mergeSymbols(left, right) {
202
197
  /** 플랫폼과 소스 위치로 실행 간 안정적인 노드 ID를 만든다. */
203
198
  function endpointId(endpoint) {
204
199
  const { path, line, column } = endpoint.location;
205
- return `${endpoint.platform}:${path}:${line}:${column}`;
200
+ const suffix = endpoint.sourceLanguage === undefined ? '' : `:${endpoint.sourceLanguage}`;
201
+ return `${endpoint.platform}:${path}:${line}:${column}${suffix}`;
206
202
  }
207
203
  /** 간선을 종류·from·to 순으로 고정한다. */
208
204
  function compareEdges(left, right) {
@@ -1 +1 @@
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 { isBridgeJoinDeferred } 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 =\n `${limitation.platform}/${limitation.target}/${limitation.tool}: `\n + limitation.message;\n return text.replace(/[\\u0000-\\u001f\\u007f-\\u009f\\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 if (isBridgeJoinDeferred(joined)) {\n throw new Error('Cannot create a graph from a deferred bridge join.');\n }\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"]}
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 BridgeSourceLanguage,\n BridgeTarget,\n} from '../exchange/parse.ts';\nimport type {\n BridgeEndpoint,\n BridgeJoinResult,\n JoinLimitation,\n} from '../join/join.ts';\nimport { isBridgeJoinDeferred } 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 readonly sourceLanguage?: BridgeSourceLanguage;\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.sourceLanguage ?? 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.sourceLanguage ?? 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 =\n `${limitation.platform}/${limitation.target}/${limitation.tool}: `\n + limitation.message\n + (limitation.origin === undefined ? '' : ' [origin: consumer]')\n + (limitation.channels === undefined ? '' : ` [channels: ${JSON.stringify(limitation.channels)}]`);\n return text.replace(/[\\u0000-\\u001f\\u007f-\\u009f\\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 if (isBridgeJoinDeferred(joined)) {\n throw new Error('Cannot create a graph from a deferred bridge join.');\n }\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(id, { id, ...endpoint });\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 const suffix = endpoint.sourceLanguage === undefined ? '' : `:${endpoint.sourceLanguage}`;\n return `${endpoint.platform}:${path}:${line}:${column}${suffix}`;\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"]}
@@ -32,6 +32,7 @@ export interface CartographRetentionsDocument {
32
32
  }>;
33
33
  readonly generatedAt: string;
34
34
  readonly retentions: readonly ExternalRetention[];
35
+ readonly omittedObjectiveCHandlers?: number;
35
36
  }
36
37
  /** 불완전한 조인으로 보존 결정을 만들 수 없음을 나타낸다. */
37
38
  export declare class RetentionValidationError extends Error {
@@ -30,16 +30,32 @@ export function createCartographRetentionsDocument(joined, generatedAt, producer
30
30
  throw new RetentionValidationError('Cannot create retentions from a deferred bridge join.');
31
31
  }
32
32
  rejectUnresolvedSwiftHandlers(joined);
33
+ const omittedObjectiveCHandlers = countObjectiveCHandlers(joined);
33
34
  return {
34
35
  format: 'external-retentions',
35
36
  version: 0,
36
37
  producedBy: { name: 'isthmus', version: producerVersion },
37
38
  generatedAt,
38
39
  retentions: collectCartographRetentions(joined),
40
+ ...(omittedObjectiveCHandlers === 0 ? {} : { omittedObjectiveCHandlers }),
39
41
  };
40
42
  }
43
+ /** 그래프 밖의 매치도 사라지지 않게 채널·메서드·위치별로 센다. */
44
+ function countObjectiveCHandlers(joined) {
45
+ const keys = new Set();
46
+ for (const method of joined.matchedMethods) {
47
+ for (const handler of method.handlers) {
48
+ if (handler.sourceLanguage !== 'objective-c')
49
+ continue;
50
+ keys.add(JSON.stringify([method.target, method.channel, method.method,
51
+ handler.location.path, handler.location.line, handler.location.column]));
52
+ }
53
+ }
54
+ return keys.size;
55
+ }
41
56
  /**
42
57
  * 심볼이 없어 보존 근거로 바꿀 수 없는 매치 Swift 핸들러를 거부한다.
58
+ * sourceLanguage로 확인된 Objective-C 구현은 Swift 그래프의 보존 대상이 아니다.
43
59
  *
44
60
  * 교환 계약에서 `symbol`은 선택 필드다. 호출자가 있는데도 근거를 만들지 못한
45
61
  * 핸들러를 조용히 빼면 cartograph는 그 핸들러를 계속 미사용으로 보고하고,
@@ -49,7 +65,7 @@ function rejectUnresolvedSwiftHandlers(joined) {
49
65
  const unresolved = new Set();
50
66
  for (const method of joined.matchedMethods) {
51
67
  for (const handler of method.handlers) {
52
- if (handler.platform !== 'swift' || handler.symbol !== undefined)
68
+ if (handler.platform !== 'swift' || handler.sourceLanguage === 'objective-c' || handler.symbol !== undefined)
53
69
  continue;
54
70
  const { path, line, column } = handler.location;
55
71
  unresolved.add(`${path}\u0000${line}\u0000${column}`);
@@ -70,7 +86,7 @@ function collectCartographRetentions(joined) {
70
86
  if (caller === undefined)
71
87
  continue;
72
88
  for (const handler of method.handlers) {
73
- if (handler.platform !== 'swift' || handler.symbol === undefined)
89
+ if (handler.platform !== 'swift' || handler.sourceLanguage === 'objective-c' || handler.symbol === undefined)
74
90
  continue;
75
91
  const symbolKey = handler.symbol.usr === undefined
76
92
  ? `name:${handler.symbol.qualifiedName}`
@@ -1 +1 @@
1
- {"version":3,"file":"retentions.js","sourceRoot":"","sources":["../../src/report/retentions.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n BridgeFactsDocument,\n BridgePlatform,\n BridgeSymbol,\n} 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/**\n * cartograph가 소비할 수 있는 수신 측 문서가 입력에 있는지 검증한다.\n *\n * cartograph는 Swift 심볼만 보존한다. Swift 문서가 없는 입력은 조인 자체는\n * 성공하므로, 검증하지 않으면 보존할 근거가 없다는 사실이 빈 목록과 코드 0으로\n * 사라진다. 사실이 없는 Swift 문서도 그 플랫폼을 분석했다는 근거로 인정한다.\n */\nexport function validateCartographRetentionInputs(\n documents: readonly BridgeFactsDocument[],\n): void {\n if (documents.some(({ platform }) => platform === 'swift')) return;\n throw new RetentionValidationError(\n 'Retentions for cartograph require at least one swift bridge facts document; '\n + 'run a swift producer for the receiver side.',\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 rejectUnresolvedSwiftHandlers(joined);\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/**\n * 심볼이 없어 보존 근거로 바꿀 수 없는 매치 Swift 핸들러를 거부한다.\n *\n * 교환 계약에서 `symbol`은 선택 필드다. 호출자가 있는데도 근거를 만들지 못한\n * 핸들러를 조용히 빼면 cartograph는 그 핸들러를 계속 미사용으로 보고하고,\n * 소비자는 살아 있는 코드를 지운다. 부분 보존 문서 대신 실패를 돌려준다.\n */\nfunction rejectUnresolvedSwiftHandlers(joined: BridgeJoinResult): void {\n const unresolved = new Set<string>();\n for (const method of joined.matchedMethods) {\n for (const handler of method.handlers) {\n if (handler.platform !== 'swift' || handler.symbol !== undefined) continue;\n const { path, line, column } = handler.location;\n unresolved.add(`${path}\\u0000${line}\\u0000${column}`);\n }\n }\n if (unresolved.size === 0) return;\n throw new RetentionValidationError(\n `Cannot produce retention evidence for ${unresolved.size} matched swift `\n + 'handlers without a symbol; regenerate the swift document with a producer '\n + 'that attaches handler symbols.',\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 === undefined\n ? `name:${handler.symbol.qualifiedName}`\n : `usr:${handler.symbol.usr}`;\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
+ {"version":3,"file":"retentions.js","sourceRoot":"","sources":["../../src/report/retentions.ts"],"names":[],"mappings":"AAKA,OAAO,EACL,oBAAoB,GAErB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAsCpD,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;;;;;;GAMG;AACH,MAAM,UAAU,iCAAiC,CAC/C,SAAyC;IAEzC,IAAI,SAAS,CAAC,IAAI,CAAC,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,QAAQ,KAAK,OAAO,CAAC;QAAE,OAAO;IACnE,MAAM,IAAI,wBAAwB,CAChC,8EAA8E;UAC5E,6CAA6C,CAChD,CAAC;AACJ,CAAC;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,6BAA6B,CAAC,MAAM,CAAC,CAAC;IACtC,MAAM,yBAAyB,GAAG,uBAAuB,CAAC,MAAM,CAAC,CAAC;IAClE,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;QAC/C,GAAG,CAAC,yBAAyB,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,yBAAyB,EAAE,CAAC;KAC1E,CAAC;AACJ,CAAC;AAED,yCAAyC;AACzC,SAAS,uBAAuB,CAAC,MAAwB;IACvD,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,cAAc,EAAE,CAAC;QAC3C,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;YACtC,IAAI,OAAO,CAAC,cAAc,KAAK,aAAa;gBAAE,SAAS;YACvD,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM;gBACnE,OAAO,CAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QAC7E,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC,IAAI,CAAC;AACnB,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,6BAA6B,CAAC,MAAwB;IAC7D,MAAM,UAAU,GAAG,IAAI,GAAG,EAAU,CAAC;IACrC,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,cAAc,EAAE,CAAC;QAC3C,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;YACtC,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,IAAI,OAAO,CAAC,cAAc,KAAK,aAAa,IAAI,OAAO,CAAC,MAAM,KAAK,SAAS;gBAAE,SAAS;YACvH,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,QAAQ,CAAC;YAChD,UAAU,CAAC,GAAG,CAAC,GAAG,IAAI,SAAS,IAAI,SAAS,MAAM,EAAE,CAAC,CAAC;QACxD,CAAC;IACH,CAAC;IACD,IAAI,UAAU,CAAC,IAAI,KAAK,CAAC;QAAE,OAAO;IAClC,MAAM,IAAI,wBAAwB,CAChC,yCAAyC,UAAU,CAAC,IAAI,iBAAiB;UACvE,2EAA2E;UAC3E,gCAAgC,CACnC,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,cAAc,KAAK,aAAa,IAAI,OAAO,CAAC,MAAM,KAAK,SAAS;gBAAE,SAAS;YACvH,MAAM,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,KAAK,SAAS;gBAChD,CAAC,CAAC,QAAQ,OAAO,CAAC,MAAM,CAAC,aAAa,EAAE;gBACxC,CAAC,CAAC,OAAO,OAAO,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC;YAChC,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 {\n BridgeFactsDocument,\n BridgePlatform,\n BridgeSymbol,\n} 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 readonly omittedObjectiveCHandlers?: number;\n}\n\n/** 불완전한 조인으로 보존 결정을 만들 수 없음을 나타낸다. */\nexport class RetentionValidationError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'RetentionValidationError';\n }\n}\n\n/**\n * cartograph가 소비할 수 있는 수신 측 문서가 입력에 있는지 검증한다.\n *\n * cartograph는 Swift 심볼만 보존한다. Swift 문서가 없는 입력은 조인 자체는\n * 성공하므로, 검증하지 않으면 보존할 근거가 없다는 사실이 빈 목록과 코드 0으로\n * 사라진다. 사실이 없는 Swift 문서도 그 플랫폼을 분석했다는 근거로 인정한다.\n */\nexport function validateCartographRetentionInputs(\n documents: readonly BridgeFactsDocument[],\n): void {\n if (documents.some(({ platform }) => platform === 'swift')) return;\n throw new RetentionValidationError(\n 'Retentions for cartograph require at least one swift bridge facts document; '\n + 'run a swift producer for the receiver side.',\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 rejectUnresolvedSwiftHandlers(joined);\n const omittedObjectiveCHandlers = countObjectiveCHandlers(joined);\n return {\n format: 'external-retentions',\n version: 0,\n producedBy: { name: 'isthmus', version: producerVersion },\n generatedAt,\n retentions: collectCartographRetentions(joined),\n ...(omittedObjectiveCHandlers === 0 ? {} : { omittedObjectiveCHandlers }),\n };\n}\n\n/** 그래프 밖의 매치도 사라지지 않게 채널·메서드·위치별로 센다. */\nfunction countObjectiveCHandlers(joined: BridgeJoinResult): number {\n const keys = new Set<string>();\n for (const method of joined.matchedMethods) {\n for (const handler of method.handlers) {\n if (handler.sourceLanguage !== 'objective-c') continue;\n keys.add(JSON.stringify([method.target, method.channel, method.method,\n handler.location.path, handler.location.line, handler.location.column]));\n }\n }\n return keys.size;\n}\n\n/**\n * 심볼이 없어 보존 근거로 바꿀 수 없는 매치 Swift 핸들러를 거부한다.\n * sourceLanguage로 확인된 Objective-C 구현은 Swift 그래프의 보존 대상이 아니다.\n *\n * 교환 계약에서 `symbol`은 선택 필드다. 호출자가 있는데도 근거를 만들지 못한\n * 핸들러를 조용히 빼면 cartograph는 그 핸들러를 계속 미사용으로 보고하고,\n * 소비자는 살아 있는 코드를 지운다. 부분 보존 문서 대신 실패를 돌려준다.\n */\nfunction rejectUnresolvedSwiftHandlers(joined: BridgeJoinResult): void {\n const unresolved = new Set<string>();\n for (const method of joined.matchedMethods) {\n for (const handler of method.handlers) {\n if (handler.platform !== 'swift' || handler.sourceLanguage === 'objective-c' || handler.symbol !== undefined) continue;\n const { path, line, column } = handler.location;\n unresolved.add(`${path}\\u0000${line}\\u0000${column}`);\n }\n }\n if (unresolved.size === 0) return;\n throw new RetentionValidationError(\n `Cannot produce retention evidence for ${unresolved.size} matched swift `\n + 'handlers without a symbol; regenerate the swift document with a producer '\n + 'that attaches handler symbols.',\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.sourceLanguage === 'objective-c' || handler.symbol === undefined) continue;\n const symbolKey = handler.symbol.usr === undefined\n ? `name:${handler.symbol.qualifiedName}`\n : `usr:${handler.symbol.usr}`;\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"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "isthmus-cli",
3
- "version": "0.1.7",
3
+ "version": "0.2.0",
4
4
  "description": "Join bridge facts across cross-platform application boundaries.",
5
5
  "keywords": [
6
6
  "flutter",