isthmus-cli 0.2.0 → 0.3.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.ko.md +322 -0
- package/README.md +254 -176
- package/Skills/isthmus/SKILL.md +7 -0
- package/dist/cli/check-command.d.ts +5 -1
- package/dist/cli/check-command.js +170 -10
- package/dist/cli/check-command.js.map +1 -1
- package/dist/cli/main.js +19 -2
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/retentions-command.d.ts +2 -3
- package/dist/cli/retentions-command.js.map +1 -1
- package/dist/exchange/parse.d.ts +8 -0
- package/dist/exchange/parse.js +12 -8
- package/dist/exchange/parse.js.map +1 -1
- package/dist/report/baseline.d.ts +61 -0
- package/dist/report/baseline.js +170 -0
- package/dist/report/baseline.js.map +1 -0
- package/dist/report/check-report.d.ts +15 -1
- package/dist/report/check-report.js +14 -0
- package/dist/report/check-report.js.map +1 -1
- package/dist/report/diff.js +3 -5
- package/dist/report/diff.js.map +1 -1
- package/package.json +3 -2
|
@@ -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/** 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
|
+
{"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 (!isBridgeTimestamp(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/** 값이 계약이 정한 브리지 메커니즘 이름인지 확인한다. */\nexport function isBridgeTarget(value: unknown): value is BridgeTarget {\n return bridgeTargets.has(value);\n}\n\n/** 조인 키를 깨뜨리는 제어 문자가 없는 비어 있지 않은 문자열인지 확인한다. */\nexport function isSafeNonEmptyString(value: unknown): value is string {\n return isNonEmptyString(value) && !controlCharacterPattern.test(value);\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 계열 생성 시각으로 해석할 수 있는지 확인한다. */\nexport function isBridgeTimestamp(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 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 객체인지 확인한다. isthmus 소유 문서 검증도 재사용한다. */\nexport function isJsonObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n"]}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { type BridgeTarget } from '../exchange/parse.ts';
|
|
2
|
+
import type { CheckIssue, CheckIssueCode, CheckReport } from './check-report.ts';
|
|
3
|
+
/**
|
|
4
|
+
* 베이스라인이 억제하는 논리 이슈 하나의 식별자다.
|
|
5
|
+
*
|
|
6
|
+
* 위치가 아니라 논리 키(code+target+channel+method)다. 소스 줄 이동이나
|
|
7
|
+
* 증거 위치 변화는 억제를 깨지 않고, 새 채널·메서드 불일치는 새 키라
|
|
8
|
+
* 억제되지 않는다. diff가 논리 연결을 비교하는 키와 같은 철학이다.
|
|
9
|
+
*/
|
|
10
|
+
export interface BaselineEntry {
|
|
11
|
+
readonly code: CheckIssueCode;
|
|
12
|
+
readonly target: BridgeTarget;
|
|
13
|
+
readonly channel: string;
|
|
14
|
+
readonly method?: string;
|
|
15
|
+
}
|
|
16
|
+
/** check 실행 사이에서 인정된 이슈를 보존하는 isthmus 소유 문서다. */
|
|
17
|
+
export interface BaselineDocument {
|
|
18
|
+
readonly format: 'isthmus-baseline';
|
|
19
|
+
readonly version: 1;
|
|
20
|
+
readonly generatedAt: string;
|
|
21
|
+
readonly entries: readonly BaselineEntry[];
|
|
22
|
+
}
|
|
23
|
+
/** 한 베이스라인 문서가 담을 수 있는 최대 항목 수다. */
|
|
24
|
+
export declare const MAX_BASELINE_ENTRIES = 10000;
|
|
25
|
+
/** 외부 베이스라인 문서가 v1 계약을 어겼음을 나타낸다. */
|
|
26
|
+
export declare class BridgeBaselineValidationError extends Error {
|
|
27
|
+
/** 입력 내용을 노출하지 않는 안전한 메시지를 보존한다. */
|
|
28
|
+
constructor(message: string);
|
|
29
|
+
}
|
|
30
|
+
/** 신뢰하지 않는 JSON 값을 검증된 isthmus-baseline v1 문서로 바꾼다. */
|
|
31
|
+
export declare function parseBaselineDocument(input: unknown): BaselineDocument;
|
|
32
|
+
/**
|
|
33
|
+
* 현재 보고서의 이슈 전체를 새 베이스라인 문서로 만든다.
|
|
34
|
+
*
|
|
35
|
+
* 읽기 상한과 같은 상한을 쓰기에도 적용한다. 상한 없이 쓰면 도구가
|
|
36
|
+
* 스스로 소비할 수 없는 산출물을 만들고, 그 파일은 다음 `--baseline`
|
|
37
|
+
* 실행에서 거부된다.
|
|
38
|
+
*/
|
|
39
|
+
export declare function createBaselineDocument(issues: readonly CheckIssue[], generatedAt: string): BaselineDocument;
|
|
40
|
+
/** 베이스라인 문서를 결정적인 JSON 문자열로 인코딩한다. */
|
|
41
|
+
export declare function encodeBaselineDocument(document: BaselineDocument): string;
|
|
42
|
+
/**
|
|
43
|
+
* 베이스라인 항목과 맞는 이슈를 표시한 보고서와 남은 항목 수를 만든다.
|
|
44
|
+
*
|
|
45
|
+
* 억제된 이슈는 목록에서 지우지 않는다. 사실·증거·심각도는 보존되고
|
|
46
|
+
* 요약의 error·warning과 `--strict` 판단에서만 빠진다. 어떤 항목도 맞지
|
|
47
|
+
* 않는 이슈를 만들지 않으며, 맞지 않는 항목은 오래된 것으로 센다 —
|
|
48
|
+
* 해결된 이슈가 베이스라인에 남으면 다음 악화를 가릴 수 있기 때문이다.
|
|
49
|
+
*
|
|
50
|
+
* 판정은 전부 논리 키 교집합이라 이미 억제 표시가 있는 보고서에 다시
|
|
51
|
+
* 적용해도 결과가 같다(멱등). suppressed 표시가 아닌 키로 세어야 stale과
|
|
52
|
+
* suppressed 계수가 이중 적용에 흔들리지 않는다.
|
|
53
|
+
*/
|
|
54
|
+
export declare function applyBaseline(report: CheckReport, entries: readonly BaselineEntry[]): CheckReport;
|
|
55
|
+
/** 논리 이슈 식별자를 충돌 없는 JSON 문자열 키로 만든다. */
|
|
56
|
+
export declare function baselineEntryKey(issue: {
|
|
57
|
+
readonly code: string;
|
|
58
|
+
readonly target: string;
|
|
59
|
+
readonly channel: string;
|
|
60
|
+
readonly method?: string;
|
|
61
|
+
}): string;
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { isBridgeTarget, isBridgeTimestamp, isJsonObject, isSafeNonEmptyString, } from "../exchange/parse.js";
|
|
2
|
+
import { compareStrings } from "../compare.js";
|
|
3
|
+
import { checkIssueCodes } from "./check-report.js";
|
|
4
|
+
import { encodeSortedJson } from "./sorted-json.js";
|
|
5
|
+
/** 한 베이스라인 문서가 담을 수 있는 최대 항목 수다. */
|
|
6
|
+
export const MAX_BASELINE_ENTRIES = 10_000;
|
|
7
|
+
/** 외부 베이스라인 문서가 v1 계약을 어겼음을 나타낸다. */
|
|
8
|
+
export class BridgeBaselineValidationError extends Error {
|
|
9
|
+
/** 입력 내용을 노출하지 않는 안전한 메시지를 보존한다. */
|
|
10
|
+
constructor(message) {
|
|
11
|
+
super(message);
|
|
12
|
+
this.name = 'BridgeBaselineValidationError';
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
/** 신뢰하지 않는 JSON 값을 검증된 isthmus-baseline v1 문서로 바꾼다. */
|
|
16
|
+
export function parseBaselineDocument(input) {
|
|
17
|
+
if (!isJsonObject(input))
|
|
18
|
+
fail('Baseline must be a JSON object.');
|
|
19
|
+
if (input.format !== 'isthmus-baseline') {
|
|
20
|
+
fail('Expected format "isthmus-baseline".');
|
|
21
|
+
}
|
|
22
|
+
// 숫자 비교라 `1.0`도 통과한다. bridge-facts의 version 검증과 같은 의도적 관용이다.
|
|
23
|
+
if (input.version !== 1) {
|
|
24
|
+
fail('Unsupported isthmus-baseline version; expected version 1.');
|
|
25
|
+
}
|
|
26
|
+
if (!isBridgeTimestamp(input.generatedAt)) {
|
|
27
|
+
fail('Invalid generatedAt timestamp.');
|
|
28
|
+
}
|
|
29
|
+
if (!Array.isArray(input.entries))
|
|
30
|
+
fail('Entries must be an array.');
|
|
31
|
+
if (input.entries.length > MAX_BASELINE_ENTRIES) {
|
|
32
|
+
fail(`Entries exceed the ${MAX_BASELINE_ENTRIES} item limit.`);
|
|
33
|
+
}
|
|
34
|
+
const entries = [];
|
|
35
|
+
const seen = new Set();
|
|
36
|
+
for (const [index, value] of input.entries.entries()) {
|
|
37
|
+
const entry = normalizeEntry(value, index);
|
|
38
|
+
const key = baselineEntryKey(entry);
|
|
39
|
+
if (seen.has(key))
|
|
40
|
+
continue;
|
|
41
|
+
seen.add(key);
|
|
42
|
+
entries.push(entry);
|
|
43
|
+
}
|
|
44
|
+
entries.sort(compareEntries);
|
|
45
|
+
return {
|
|
46
|
+
format: 'isthmus-baseline',
|
|
47
|
+
version: 1,
|
|
48
|
+
generatedAt: input.generatedAt,
|
|
49
|
+
entries,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* 현재 보고서의 이슈 전체를 새 베이스라인 문서로 만든다.
|
|
54
|
+
*
|
|
55
|
+
* 읽기 상한과 같은 상한을 쓰기에도 적용한다. 상한 없이 쓰면 도구가
|
|
56
|
+
* 스스로 소비할 수 없는 산출물을 만들고, 그 파일은 다음 `--baseline`
|
|
57
|
+
* 실행에서 거부된다.
|
|
58
|
+
*/
|
|
59
|
+
export function createBaselineDocument(issues, generatedAt) {
|
|
60
|
+
const entries = [];
|
|
61
|
+
const seen = new Set();
|
|
62
|
+
for (const issue of issues) {
|
|
63
|
+
const entry = issue.method === undefined
|
|
64
|
+
? { code: issue.code, target: issue.target, channel: issue.channel }
|
|
65
|
+
: {
|
|
66
|
+
code: issue.code,
|
|
67
|
+
target: issue.target,
|
|
68
|
+
channel: issue.channel,
|
|
69
|
+
method: issue.method,
|
|
70
|
+
};
|
|
71
|
+
const key = baselineEntryKey(entry);
|
|
72
|
+
if (seen.has(key))
|
|
73
|
+
continue;
|
|
74
|
+
seen.add(key);
|
|
75
|
+
entries.push(entry);
|
|
76
|
+
}
|
|
77
|
+
if (entries.length > MAX_BASELINE_ENTRIES) {
|
|
78
|
+
fail(`Baseline entries exceed the ${MAX_BASELINE_ENTRIES} item limit; `
|
|
79
|
+
+ 'narrow the join inputs.');
|
|
80
|
+
}
|
|
81
|
+
entries.sort(compareEntries);
|
|
82
|
+
return { format: 'isthmus-baseline', version: 1, generatedAt, entries };
|
|
83
|
+
}
|
|
84
|
+
/** 베이스라인 문서를 결정적인 JSON 문자열로 인코딩한다. */
|
|
85
|
+
export function encodeBaselineDocument(document) {
|
|
86
|
+
return encodeSortedJson(document);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* 베이스라인 항목과 맞는 이슈를 표시한 보고서와 남은 항목 수를 만든다.
|
|
90
|
+
*
|
|
91
|
+
* 억제된 이슈는 목록에서 지우지 않는다. 사실·증거·심각도는 보존되고
|
|
92
|
+
* 요약의 error·warning과 `--strict` 판단에서만 빠진다. 어떤 항목도 맞지
|
|
93
|
+
* 않는 이슈를 만들지 않으며, 맞지 않는 항목은 오래된 것으로 센다 —
|
|
94
|
+
* 해결된 이슈가 베이스라인에 남으면 다음 악화를 가릴 수 있기 때문이다.
|
|
95
|
+
*
|
|
96
|
+
* 판정은 전부 논리 키 교집합이라 이미 억제 표시가 있는 보고서에 다시
|
|
97
|
+
* 적용해도 결과가 같다(멱등). suppressed 표시가 아닌 키로 세어야 stale과
|
|
98
|
+
* suppressed 계수가 이중 적용에 흔들리지 않는다.
|
|
99
|
+
*/
|
|
100
|
+
export function applyBaseline(report, entries) {
|
|
101
|
+
const entryKeys = new Set(entries.map(baselineEntryKey));
|
|
102
|
+
const issueKeys = new Set(report.issues.map(baselineEntryKey));
|
|
103
|
+
const issues = report.issues.map((issue) => entryKeys.has(baselineEntryKey(issue))
|
|
104
|
+
? { ...issue, suppressed: true }
|
|
105
|
+
: issue);
|
|
106
|
+
const suppressed = issues.filter((issue) => issue.suppressed === true).length;
|
|
107
|
+
const staleBaselineEntries = [...entryKeys].filter((key) => !issueKeys.has(key)).length;
|
|
108
|
+
return {
|
|
109
|
+
...report,
|
|
110
|
+
summary: {
|
|
111
|
+
...report.summary,
|
|
112
|
+
errors: countUnsuppressed(issues, 'error'),
|
|
113
|
+
warnings: countUnsuppressed(issues, 'warning'),
|
|
114
|
+
suppressed,
|
|
115
|
+
staleBaselineEntries,
|
|
116
|
+
},
|
|
117
|
+
issues,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
/** 논리 이슈 식별자를 충돌 없는 JSON 문자열 키로 만든다. */
|
|
121
|
+
export function baselineEntryKey(issue) {
|
|
122
|
+
return JSON.stringify([
|
|
123
|
+
issue.code,
|
|
124
|
+
issue.target,
|
|
125
|
+
issue.channel,
|
|
126
|
+
issue.method ?? null,
|
|
127
|
+
]);
|
|
128
|
+
}
|
|
129
|
+
/** 억제되지 않은 이슈 중 주어진 심각도의 수를 센다. */
|
|
130
|
+
function countUnsuppressed(issues, severity) {
|
|
131
|
+
return issues.filter((issue) => issue.severity === severity && issue.suppressed !== true).length;
|
|
132
|
+
}
|
|
133
|
+
/** 항목 하나의 종류·키 필드를 검증하고 계약 필드만 복사한다. */
|
|
134
|
+
function normalizeEntry(value, index) {
|
|
135
|
+
if (!isJsonObject(value)) {
|
|
136
|
+
fail(`Baseline entry at index ${index} must be a JSON object.`);
|
|
137
|
+
}
|
|
138
|
+
if (typeof value.code !== 'string' || !checkIssueCodeSet.has(value.code)) {
|
|
139
|
+
fail(`Unknown issue code in baseline entry at index ${index}.`);
|
|
140
|
+
}
|
|
141
|
+
if (!isBridgeTarget(value.target)) {
|
|
142
|
+
fail(`Unsupported bridge target in baseline entry at index ${index}.`);
|
|
143
|
+
}
|
|
144
|
+
if (!isSafeNonEmptyString(value.channel)) {
|
|
145
|
+
fail(`Invalid channel in baseline entry at index ${index}.`);
|
|
146
|
+
}
|
|
147
|
+
if (value.method !== undefined &&
|
|
148
|
+
!isSafeNonEmptyString(value.method)) {
|
|
149
|
+
fail(`Invalid method in baseline entry at index ${index}.`);
|
|
150
|
+
}
|
|
151
|
+
const code = value.code;
|
|
152
|
+
return value.method === undefined
|
|
153
|
+
? { code, target: value.target, channel: value.channel }
|
|
154
|
+
: { code, target: value.target, channel: value.channel, method: value.method };
|
|
155
|
+
}
|
|
156
|
+
/** 항목을 code·target·channel·method 순으로 고정한다. */
|
|
157
|
+
function compareEntries(left, right) {
|
|
158
|
+
return (compareStrings(left.code, right.code) ||
|
|
159
|
+
compareStrings(left.target, right.target) ||
|
|
160
|
+
compareStrings(left.channel, right.channel) ||
|
|
161
|
+
compareStrings(left.method ?? '', right.method ?? ''));
|
|
162
|
+
}
|
|
163
|
+
/** 입력 값을 포함하지 않는 검증 오류를 던진다. */
|
|
164
|
+
function fail(message) {
|
|
165
|
+
throw new BridgeBaselineValidationError(message);
|
|
166
|
+
}
|
|
167
|
+
/** 배열과 null을 제외한 JSON 객체 확인은 exchange/parse의 검증을 재사용한다. */
|
|
168
|
+
/** 검증에 쓰는 진단 코드 집합이다. */
|
|
169
|
+
const checkIssueCodeSet = new Set(checkIssueCodes);
|
|
170
|
+
//# sourceMappingURL=baseline.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"baseline.js","sourceRoot":"","sources":["../../src/report/baseline.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n isBridgeTarget,\n isBridgeTimestamp,\n isJsonObject,\n isSafeNonEmptyString,\n type BridgeTarget,\n} from '../exchange/parse.ts';\nimport { compareStrings } from '../compare.ts';\nimport type {\n CheckIssue,\n CheckIssueCode,\n CheckReport,\n} from './check-report.ts';\nimport { checkIssueCodes } from './check-report.ts';\nimport { encodeSortedJson } from './sorted-json.ts';\n\n/**\n * 베이스라인이 억제하는 논리 이슈 하나의 식별자다.\n *\n * 위치가 아니라 논리 키(code+target+channel+method)다. 소스 줄 이동이나\n * 증거 위치 변화는 억제를 깨지 않고, 새 채널·메서드 불일치는 새 키라\n * 억제되지 않는다. diff가 논리 연결을 비교하는 키와 같은 철학이다.\n */\nexport interface BaselineEntry {\n readonly code: CheckIssueCode;\n readonly target: BridgeTarget;\n readonly channel: string;\n readonly method?: string;\n}\n\n/** check 실행 사이에서 인정된 이슈를 보존하는 isthmus 소유 문서다. */\nexport interface BaselineDocument {\n readonly format: 'isthmus-baseline';\n readonly version: 1;\n readonly generatedAt: string;\n readonly entries: readonly BaselineEntry[];\n}\n\n/** 한 베이스라인 문서가 담을 수 있는 최대 항목 수다. */\nexport const MAX_BASELINE_ENTRIES = 10_000;\n\n/** 외부 베이스라인 문서가 v1 계약을 어겼음을 나타낸다. */\nexport class BridgeBaselineValidationError extends Error {\n /** 입력 내용을 노출하지 않는 안전한 메시지를 보존한다. */\n constructor(message: string) {\n super(message);\n this.name = 'BridgeBaselineValidationError';\n }\n}\n\n/** 신뢰하지 않는 JSON 값을 검증된 isthmus-baseline v1 문서로 바꾼다. */\nexport function parseBaselineDocument(input: unknown): BaselineDocument {\n if (!isJsonObject(input)) fail('Baseline must be a JSON object.');\n if (input.format !== 'isthmus-baseline') {\n fail('Expected format \"isthmus-baseline\".');\n }\n // 숫자 비교라 `1.0`도 통과한다. bridge-facts의 version 검증과 같은 의도적 관용이다.\n if (input.version !== 1) {\n fail('Unsupported isthmus-baseline version; expected version 1.');\n }\n if (!isBridgeTimestamp(input.generatedAt)) {\n fail('Invalid generatedAt timestamp.');\n }\n if (!Array.isArray(input.entries)) fail('Entries must be an array.');\n if (input.entries.length > MAX_BASELINE_ENTRIES) {\n fail(`Entries exceed the ${MAX_BASELINE_ENTRIES} item limit.`);\n }\n const entries: BaselineEntry[] = [];\n const seen = new Set<string>();\n for (const [index, value] of input.entries.entries()) {\n const entry = normalizeEntry(value, index);\n const key = baselineEntryKey(entry);\n if (seen.has(key)) continue;\n seen.add(key);\n entries.push(entry);\n }\n entries.sort(compareEntries);\n return {\n format: 'isthmus-baseline',\n version: 1,\n generatedAt: input.generatedAt,\n entries,\n };\n}\n\n/**\n * 현재 보고서의 이슈 전체를 새 베이스라인 문서로 만든다.\n *\n * 읽기 상한과 같은 상한을 쓰기에도 적용한다. 상한 없이 쓰면 도구가\n * 스스로 소비할 수 없는 산출물을 만들고, 그 파일은 다음 `--baseline`\n * 실행에서 거부된다.\n */\nexport function createBaselineDocument(\n issues: readonly CheckIssue[],\n generatedAt: string,\n): BaselineDocument {\n const entries: BaselineEntry[] = [];\n const seen = new Set<string>();\n for (const issue of issues) {\n const entry: BaselineEntry = issue.method === undefined\n ? { code: issue.code, target: issue.target, channel: issue.channel }\n : {\n code: issue.code,\n target: issue.target,\n channel: issue.channel,\n method: issue.method,\n };\n const key = baselineEntryKey(entry);\n if (seen.has(key)) continue;\n seen.add(key);\n entries.push(entry);\n }\n if (entries.length > MAX_BASELINE_ENTRIES) {\n fail(\n `Baseline entries exceed the ${MAX_BASELINE_ENTRIES} item limit; `\n + 'narrow the join inputs.',\n );\n }\n entries.sort(compareEntries);\n return { format: 'isthmus-baseline', version: 1, generatedAt, entries };\n}\n\n/** 베이스라인 문서를 결정적인 JSON 문자열로 인코딩한다. */\nexport function encodeBaselineDocument(document: BaselineDocument): string {\n return encodeSortedJson(document);\n}\n\n/**\n * 베이스라인 항목과 맞는 이슈를 표시한 보고서와 남은 항목 수를 만든다.\n *\n * 억제된 이슈는 목록에서 지우지 않는다. 사실·증거·심각도는 보존되고\n * 요약의 error·warning과 `--strict` 판단에서만 빠진다. 어떤 항목도 맞지\n * 않는 이슈를 만들지 않으며, 맞지 않는 항목은 오래된 것으로 센다 —\n * 해결된 이슈가 베이스라인에 남으면 다음 악화를 가릴 수 있기 때문이다.\n *\n * 판정은 전부 논리 키 교집합이라 이미 억제 표시가 있는 보고서에 다시\n * 적용해도 결과가 같다(멱등). suppressed 표시가 아닌 키로 세어야 stale과\n * suppressed 계수가 이중 적용에 흔들리지 않는다.\n */\nexport function applyBaseline(\n report: CheckReport,\n entries: readonly BaselineEntry[],\n): CheckReport {\n const entryKeys = new Set(entries.map(baselineEntryKey));\n const issueKeys = new Set(report.issues.map(baselineEntryKey));\n const issues = report.issues.map((issue) =>\n entryKeys.has(baselineEntryKey(issue))\n ? { ...issue, suppressed: true as const }\n : issue,\n );\n const suppressed = issues.filter(\n (issue) => issue.suppressed === true,\n ).length;\n const staleBaselineEntries = [...entryKeys].filter(\n (key) => !issueKeys.has(key),\n ).length;\n return {\n ...report,\n summary: {\n ...report.summary,\n errors: countUnsuppressed(issues, 'error'),\n warnings: countUnsuppressed(issues, 'warning'),\n suppressed,\n staleBaselineEntries,\n },\n issues,\n };\n}\n\n/** 논리 이슈 식별자를 충돌 없는 JSON 문자열 키로 만든다. */\nexport function baselineEntryKey(issue: {\n readonly code: string;\n readonly target: string;\n readonly channel: string;\n readonly method?: string;\n}): string {\n return JSON.stringify([\n issue.code,\n issue.target,\n issue.channel,\n issue.method ?? null,\n ]);\n}\n\n/** 억제되지 않은 이슈 중 주어진 심각도의 수를 센다. */\nfunction countUnsuppressed(\n issues: readonly CheckIssue[],\n severity: 'error' | 'warning',\n): number {\n return issues.filter(\n (issue) => issue.severity === severity && issue.suppressed !== true,\n ).length;\n}\n\n/** 항목 하나의 종류·키 필드를 검증하고 계약 필드만 복사한다. */\nfunction normalizeEntry(value: unknown, index: number): BaselineEntry {\n if (!isJsonObject(value)) {\n fail(`Baseline entry at index ${index} must be a JSON object.`);\n }\n if (typeof value.code !== 'string' || !checkIssueCodeSet.has(value.code)) {\n fail(`Unknown issue code in baseline entry at index ${index}.`);\n }\n if (!isBridgeTarget(value.target)) {\n fail(`Unsupported bridge target in baseline entry at index ${index}.`);\n }\n if (!isSafeNonEmptyString(value.channel)) {\n fail(`Invalid channel in baseline entry at index ${index}.`);\n }\n if (\n value.method !== undefined &&\n !isSafeNonEmptyString(value.method)\n ) {\n fail(`Invalid method in baseline entry at index ${index}.`);\n }\n const code = value.code as CheckIssueCode;\n return value.method === undefined\n ? { code, target: value.target, channel: value.channel }\n : { code, target: value.target, channel: value.channel, method: value.method };\n}\n\n/** 항목을 code·target·channel·method 순으로 고정한다. */\nfunction compareEntries(left: BaselineEntry, right: BaselineEntry): number {\n return (\n compareStrings(left.code, right.code) ||\n compareStrings(left.target, right.target) ||\n compareStrings(left.channel, right.channel) ||\n compareStrings(left.method ?? '', right.method ?? '')\n );\n}\n\n/** 입력 값을 포함하지 않는 검증 오류를 던진다. */\nfunction fail(message: string): never {\n throw new BridgeBaselineValidationError(message);\n}\n\n/** 배열과 null을 제외한 JSON 객체 확인은 exchange/parse의 검증을 재사용한다. */\n\n/** 검증에 쓰는 진단 코드 집합이다. */\nconst checkIssueCodeSet = new Set<string>(checkIssueCodes);\n"]}
|
|
@@ -6,6 +6,10 @@ export interface CheckSummary {
|
|
|
6
6
|
readonly warnings: number;
|
|
7
7
|
readonly matchedChannels: number;
|
|
8
8
|
readonly matchedMethods: number;
|
|
9
|
+
/** 베이스라인이 적용된 실행에서만 실리는 억제된 이슈 수다. */
|
|
10
|
+
readonly suppressed?: number;
|
|
11
|
+
/** 베이스라인이 적용된 실행에서만 실리는, 현재 이슈와 맞지 않는 항목 수다. */
|
|
12
|
+
readonly staleBaselineEntries?: number;
|
|
9
13
|
}
|
|
10
14
|
/**
|
|
11
15
|
* check가 보고하는 안정적인 진단 종류다.
|
|
@@ -13,7 +17,9 @@ export interface CheckSummary {
|
|
|
13
17
|
* `-unverified` 종류는 수신 측이 스스로 분석 공백을 신고해, 핸들러가 없는 것인지
|
|
14
18
|
* 보지 못한 것인지 구분할 수 없는 경우다. 사실과 증거는 같지만 판정이 아니다.
|
|
15
19
|
*/
|
|
16
|
-
export
|
|
20
|
+
export declare const checkIssueCodes: readonly ["unhandled-invocation", "unhandled-invocation-unverified", "unregistered-channel-creation", "unregistered-channel-creation-unverified", "registration-without-creation", "handler-without-invocation"];
|
|
21
|
+
/** check가 보고하는 안정적인 진단 종류다. */
|
|
22
|
+
export type CheckIssueCode = (typeof checkIssueCodes)[number];
|
|
17
23
|
/** 삭제 판정 없이 경계 불일치 사실과 증거만 전달한다. */
|
|
18
24
|
export interface CheckIssue {
|
|
19
25
|
readonly severity: 'error' | 'warning';
|
|
@@ -22,6 +28,14 @@ export interface CheckIssue {
|
|
|
22
28
|
readonly channel: string;
|
|
23
29
|
readonly method?: string;
|
|
24
30
|
readonly evidence: readonly BridgeEndpoint[];
|
|
31
|
+
/**
|
|
32
|
+
* 베이스라인이 이 이슈를 인정된 상태로 억제했다는 표시다.
|
|
33
|
+
*
|
|
34
|
+
* 사실·증거·심각도는 그대로 보존하고, 요약의 error·warning 계산과
|
|
35
|
+
* `--strict`에서만 빼는다. 억제 자체를 지우면 베이스라인이 무엇을
|
|
36
|
+
* 삼켰는지 보고서에서 사라진다.
|
|
37
|
+
*/
|
|
38
|
+
readonly suppressed?: true;
|
|
25
39
|
}
|
|
26
40
|
/** 에이전트와 CI가 소비할 check 문서다. */
|
|
27
41
|
export interface CheckReport {
|
|
@@ -1,6 +1,20 @@
|
|
|
1
1
|
import { isReceiverPlatform } from "../exchange/parse.js";
|
|
2
2
|
import { isBridgeJoinDeferred } from "../join/join.js";
|
|
3
3
|
import { encodeSortedJson } from "./sorted-json.js";
|
|
4
|
+
/**
|
|
5
|
+
* check가 보고하는 안정적인 진단 종류다.
|
|
6
|
+
*
|
|
7
|
+
* `-unverified` 종류는 수신 측이 스스로 분석 공백을 신고해, 핸들러가 없는 것인지
|
|
8
|
+
* 보지 못한 것인지 구분할 수 없는 경우다. 사실과 증거는 같지만 판정이 아니다.
|
|
9
|
+
*/
|
|
10
|
+
export const checkIssueCodes = [
|
|
11
|
+
'unhandled-invocation',
|
|
12
|
+
'unhandled-invocation-unverified',
|
|
13
|
+
'unregistered-channel-creation',
|
|
14
|
+
'unregistered-channel-creation-unverified',
|
|
15
|
+
'registration-without-creation',
|
|
16
|
+
'handler-without-invocation',
|
|
17
|
+
];
|
|
4
18
|
/** check 문서를 결정적인 JSON 문자열로 인코딩한다. */
|
|
5
19
|
export function encodeCheckReport(report) {
|
|
6
20
|
return encodeSortedJson(report);
|
|
@@ -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, 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"]}
|
|
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 readonly suppressed?: number;\n /** 베이스라인이 적용된 실행에서만 실리는, 현재 이슈와 맞지 않는 항목 수다. */\n readonly staleBaselineEntries?: number;\n}\n\n/**\n * check가 보고하는 안정적인 진단 종류다.\n *\n * `-unverified` 종류는 수신 측이 스스로 분석 공백을 신고해, 핸들러가 없는 것인지\n * 보지 못한 것인지 구분할 수 없는 경우다. 사실과 증거는 같지만 판정이 아니다.\n */\nexport const checkIssueCodes = [\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] as const;\n\n/** check가 보고하는 안정적인 진단 종류다. */\nexport type CheckIssueCode = (typeof checkIssueCodes)[number];\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 *\n * 사실·증거·심각도는 그대로 보존하고, 요약의 error·warning 계산과\n * `--strict`에서만 빼는다. 억제 자체를 지우면 베이스라인이 무엇을\n * 삼켰는지 보고서에서 사라진다.\n */\n readonly suppressed?: true;\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"]}
|
package/dist/report/diff.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { compareStrings } from "../compare.js";
|
|
2
2
|
import { BridgeJoinValidationError, isBridgeJoinDeferred, joinBridgeDocuments } from "../join/join.js";
|
|
3
|
+
import { baselineEntryKey } from "./baseline.js";
|
|
3
4
|
import { createCheckReport } from "./check-report.js";
|
|
4
5
|
/** 동일 프로젝트의 관찰 결과를 비교하며 삭제 안전성이나 rename을 추측하지 않는다. */
|
|
5
6
|
export function createBridgeDiff(before, after) {
|
|
@@ -13,8 +14,8 @@ export function createBridgeDiff(before, after) {
|
|
|
13
14
|
const newReport = createCheckReport(newJoin);
|
|
14
15
|
const addedMethods = difference(newJoin.matchedMethods, oldJoin.matchedMethods, logicalKey);
|
|
15
16
|
const removedMethods = difference(oldJoin.matchedMethods, newJoin.matchedMethods, logicalKey);
|
|
16
|
-
const introducedIssues = difference(newReport.issues, oldReport.issues,
|
|
17
|
-
const resolvedIssues = difference(oldReport.issues, newReport.issues,
|
|
17
|
+
const introducedIssues = difference(newReport.issues, oldReport.issues, baselineEntryKey);
|
|
18
|
+
const resolvedIssues = difference(oldReport.issues, newReport.issues, baselineEntryKey);
|
|
18
19
|
return {
|
|
19
20
|
format: 'isthmus-diff',
|
|
20
21
|
version: 1,
|
|
@@ -64,9 +65,6 @@ function producerVersions(docs) {
|
|
|
64
65
|
function logicalKey(item) {
|
|
65
66
|
return JSON.stringify([item.target, item.channel, item.method ?? null]);
|
|
66
67
|
}
|
|
67
|
-
function issueKey(item) {
|
|
68
|
-
return JSON.stringify([item.code, logicalKey(item)]);
|
|
69
|
-
}
|
|
70
68
|
function limitationKey(item) {
|
|
71
69
|
return JSON.stringify([item.platform, item.target, item.tool, item.message, item.channels ?? null, item.origin ?? null]);
|
|
72
70
|
}
|
package/dist/report/diff.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"diff.js","sourceRoot":"","sources":["../../src/report/diff.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,yBAAyB,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACvG,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAEtD,sDAAsD;AACtD,MAAM,UAAU,gBAAgB,CAC9B,MAAsC,EACtC,KAAqC;IAErC,iBAAiB,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IACjC,MAAM,OAAO,GAAG,mBAAmB,CAAC,MAAM,CAAC,CAAC;IAC5C,MAAM,OAAO,GAAG,mBAAmB,CAAC,KAAK,CAAC,CAAC;IAC3C,IAAI,oBAAoB,CAAC,OAAO,CAAC,IAAI,oBAAoB,CAAC,OAAO,CAAC,EAAE,CAAC;QACnE,MAAM,IAAI,yBAAyB,CACjC,6EAA6E,CAC9E,CAAC;IACJ,CAAC;IACD,MAAM,SAAS,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAC;IAC7C,MAAM,SAAS,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAC;IAC7C,MAAM,YAAY,GAAG,UAAU,CAAC,OAAO,CAAC,cAAc,EAAE,OAAO,CAAC,cAAc,EAAE,UAAU,CAAC,CAAC;IAC5F,MAAM,cAAc,GAAG,UAAU,CAAC,OAAO,CAAC,cAAc,EAAE,OAAO,CAAC,cAAc,EAAE,UAAU,CAAC,CAAC;IAC9F,MAAM,gBAAgB,GAAG,UAAU,CAAC,SAAS,CAAC,MAAM,EAAE,SAAS,CAAC,MAAM,EAAE,
|
|
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 { baselineEntryKey } from './baseline.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, baselineEntryKey);\n const resolvedIssues = difference(oldReport.issues, newReport.issues, baselineEntryKey);\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 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"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "isthmus-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Join bridge facts across cross-platform application boundaries.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"flutter",
|
|
@@ -29,7 +29,8 @@
|
|
|
29
29
|
"dist",
|
|
30
30
|
"Skills",
|
|
31
31
|
"LICENSE",
|
|
32
|
-
"README.md"
|
|
32
|
+
"README.md",
|
|
33
|
+
"README.ko.md"
|
|
33
34
|
],
|
|
34
35
|
"publishConfig": {
|
|
35
36
|
"access": "public"
|