isthmus-cli 0.5.0 → 0.7.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.
Files changed (146) hide show
  1. package/README.ko.md +170 -45
  2. package/README.md +178 -36
  3. package/Skills/isthmus/SKILL.md +104 -5
  4. package/compatibility.json +10 -0
  5. package/dist/cli/check-command.d.ts +2 -2
  6. package/dist/cli/check-command.js +22 -9
  7. package/dist/cli/check-command.js.map +1 -1
  8. package/dist/cli/command-support.d.ts +20 -2
  9. package/dist/cli/command-support.js +38 -9
  10. package/dist/cli/command-support.js.map +1 -1
  11. package/dist/cli/diff-command.js +4 -3
  12. package/dist/cli/diff-command.js.map +1 -1
  13. package/dist/cli/doctor-command.d.ts +11 -0
  14. package/dist/cli/doctor-command.js +128 -0
  15. package/dist/cli/doctor-command.js.map +1 -0
  16. package/dist/cli/extract-js-command.d.ts +33 -0
  17. package/dist/cli/extract-js-command.js +233 -0
  18. package/dist/cli/extract-js-command.js.map +1 -0
  19. package/dist/cli/graph-command.js +13 -6
  20. package/dist/cli/graph-command.js.map +1 -1
  21. package/dist/cli/impact-command.d.ts +5 -0
  22. package/dist/cli/impact-command.js +111 -0
  23. package/dist/cli/impact-command.js.map +1 -0
  24. package/dist/cli/init-command.d.ts +11 -0
  25. package/dist/cli/init-command.js +115 -0
  26. package/dist/cli/init-command.js.map +1 -0
  27. package/dist/cli/main.js +67 -1
  28. package/dist/cli/main.js.map +1 -1
  29. package/dist/cli/mcp-server.d.ts +16 -0
  30. package/dist/cli/mcp-server.js +534 -0
  31. package/dist/cli/mcp-server.js.map +1 -0
  32. package/dist/cli/preflight-command.d.ts +5 -0
  33. package/dist/cli/preflight-command.js +83 -0
  34. package/dist/cli/preflight-command.js.map +1 -0
  35. package/dist/cli/query-command.js +13 -6
  36. package/dist/cli/query-command.js.map +1 -1
  37. package/dist/cli/retentions-command.js +14 -7
  38. package/dist/cli/retentions-command.js.map +1 -1
  39. package/dist/cli/runtime-command.d.ts +5 -0
  40. package/dist/cli/runtime-command.js +42 -0
  41. package/dist/cli/runtime-command.js.map +1 -0
  42. package/dist/cli/runtime-json-reader.d.ts +12 -0
  43. package/dist/cli/runtime-json-reader.js +37 -0
  44. package/dist/cli/runtime-json-reader.js.map +1 -0
  45. package/dist/cli/serve-command.d.ts +13 -0
  46. package/dist/cli/serve-command.js +43 -0
  47. package/dist/cli/serve-command.js.map +1 -0
  48. package/dist/exchange/capture-config.d.ts +18 -0
  49. package/dist/exchange/capture-config.js +100 -0
  50. package/dist/exchange/capture-config.js.map +1 -0
  51. package/dist/exchange/impact-selection.d.ts +13 -0
  52. package/dist/exchange/impact-selection.js +35 -0
  53. package/dist/exchange/impact-selection.js.map +1 -0
  54. package/dist/exchange/kartograph-impact.d.ts +4 -0
  55. package/dist/exchange/kartograph-impact.js +169 -0
  56. package/dist/exchange/kartograph-impact.js.map +1 -0
  57. package/dist/exchange/messages.d.ts +36 -0
  58. package/dist/exchange/messages.js +96 -0
  59. package/dist/exchange/messages.js.map +1 -0
  60. package/dist/exchange/parse.d.ts +69 -0
  61. package/dist/exchange/parse.js +127 -20
  62. package/dist/exchange/parse.js.map +1 -1
  63. package/dist/exchange/preflight-context.d.ts +71 -0
  64. package/dist/exchange/preflight-context.js +317 -0
  65. package/dist/exchange/preflight-context.js.map +1 -0
  66. package/dist/exchange/producer-impact.d.ts +17 -0
  67. package/dist/exchange/producer-impact.js +226 -0
  68. package/dist/exchange/producer-impact.js.map +1 -0
  69. package/dist/exchange/runtime.d.ts +69 -0
  70. package/dist/exchange/runtime.js +160 -0
  71. package/dist/exchange/runtime.js.map +1 -0
  72. package/dist/extract/js-document.d.ts +22 -0
  73. package/dist/extract/js-document.js +230 -0
  74. package/dist/extract/js-document.js.map +1 -0
  75. package/dist/extract/js-scan.d.ts +93 -0
  76. package/dist/extract/js-scan.js +1205 -0
  77. package/dist/extract/js-scan.js.map +1 -0
  78. package/dist/extract/lexer.d.ts +26 -0
  79. package/dist/extract/lexer.js +316 -0
  80. package/dist/extract/lexer.js.map +1 -0
  81. package/dist/join/join.d.ts +66 -1
  82. package/dist/join/join.js +171 -1
  83. package/dist/join/join.js.map +1 -1
  84. package/dist/join/message-address.d.ts +7 -0
  85. package/dist/join/message-address.js +37 -0
  86. package/dist/join/message-address.js.map +1 -0
  87. package/dist/join/messages.d.ts +26 -0
  88. package/dist/join/messages.js +87 -0
  89. package/dist/join/messages.js.map +1 -0
  90. package/dist/report/check-report.d.ts +20 -3
  91. package/dist/report/check-report.js +240 -8
  92. package/dist/report/check-report.js.map +1 -1
  93. package/dist/report/codequality.d.ts +45 -0
  94. package/dist/report/codequality.js +74 -0
  95. package/dist/report/codequality.js.map +1 -0
  96. package/dist/report/diff.d.ts +25 -3
  97. package/dist/report/diff.js +91 -10
  98. package/dist/report/diff.js.map +1 -1
  99. package/dist/report/graph.d.ts +10 -4
  100. package/dist/report/graph.js +60 -6
  101. package/dist/report/graph.js.map +1 -1
  102. package/dist/report/impact.d.ts +62 -0
  103. package/dist/report/impact.js +168 -0
  104. package/dist/report/impact.js.map +1 -0
  105. package/dist/report/preflight-runtime.d.ts +42 -0
  106. package/dist/report/preflight-runtime.js +193 -0
  107. package/dist/report/preflight-runtime.js.map +1 -0
  108. package/dist/report/preflight-view.d.ts +146 -0
  109. package/dist/report/preflight-view.js +216 -0
  110. package/dist/report/preflight-view.js.map +1 -0
  111. package/dist/report/preflight.d.ts +104 -0
  112. package/dist/report/preflight.js +331 -0
  113. package/dist/report/preflight.js.map +1 -0
  114. package/dist/report/query.d.ts +11 -4
  115. package/dist/report/query.js +84 -17
  116. package/dist/report/query.js.map +1 -1
  117. package/dist/report/retentions.d.ts +17 -5
  118. package/dist/report/retentions.js +85 -8
  119. package/dist/report/retentions.js.map +1 -1
  120. package/dist/report/rules.d.ts +16 -0
  121. package/dist/report/rules.js +30 -0
  122. package/dist/report/rules.js.map +1 -0
  123. package/dist/report/runtime-impact.d.ts +38 -0
  124. package/dist/report/runtime-impact.js +94 -0
  125. package/dist/report/runtime-impact.js.map +1 -0
  126. package/dist/report/runtime.d.ts +54 -0
  127. package/dist/report/runtime.js +137 -0
  128. package/dist/report/runtime.js.map +1 -0
  129. package/dist/report/sarif.d.ts +2 -8
  130. package/dist/report/sarif.js +2 -10
  131. package/dist/report/sarif.js.map +1 -1
  132. package/dist/report/sorted-json.d.ts +1 -1
  133. package/dist/report/sorted-json.js +2 -2
  134. package/dist/report/sorted-json.js.map +1 -1
  135. package/docs/BRIDGE-EVENTS.md +87 -0
  136. package/docs/BRIDGE-MESSAGES.md +117 -0
  137. package/docs/GRAPH-EXCHANGE.md +382 -0
  138. package/docs/IMPACT.md +96 -0
  139. package/docs/MCP.md +62 -0
  140. package/docs/PREFLIGHT.md +311 -0
  141. package/docs/RUNTIME.md +172 -0
  142. package/docs/TOOLCHAIN.md +101 -0
  143. package/package.json +14 -2
  144. package/scripts/build-preflight-toolchain.mjs +186 -0
  145. package/scripts/capture-preflight.mjs +380 -0
  146. package/scripts/run-child.mjs +17 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"producer-impact.js","sourceRoot":"","sources":["../../src/exchange/producer-impact.ts"],"names":[],"mappings":"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","sourcesContent":["import { isProjectRelativePath, isSafeNonEmptyString } from './parse.ts';\nimport { parseImpactSelection } from './impact-selection.ts';\nimport type { ImpactSelection } from './impact-selection.ts';\nimport {\n validateLanguageImpact,\n PreflightValidationError,\n type ImpactSymbol,\n type LanguageImpact,\n} from './preflight-context.ts';\nimport type { BridgeLocation } from './parse.ts';\n\n/** producer 영향 문서에 주입할 isthmus 실행 문맥이다. */\nexport interface ProducerImpactMetadata {\n readonly id: string;\n readonly project: string;\n readonly requested: ImpactSelection;\n readonly tool: Readonly<{ name: string; version: string }>;\n readonly trigger?: string;\n}\n\n/** Cartograph `change-impact` v1을 isthmus 공통 영향 형식으로 투영한다. */\nexport function adaptCartographImpact(raw: unknown, metadata: ProducerImpactMetadata): LanguageImpact {\n const value = object(raw, 'Cartograph impact must be a JSON object.');\n if (value.format !== 'change-impact' || value.version !== 1 || value.level !== 'symbol') {\n fail('Expected Cartograph change-impact version 1 at symbol level.');\n }\n const context = normalizeMetadata(metadata, 'swift');\n const limitations = textStrings(value.limitations, 'Invalid Cartograph limitations.');\n for (const [field, code] of [['selectionIssues', 'selection-issues'], ['runtimeReview', 'runtime-review'],\n ['runtimeDependencies', 'runtime-dependencies']] as const) {\n if (value[field] === undefined) continue;\n const rows = rawArray(value[field], `Invalid Cartograph ${field}.`);\n if (rows.length > 0) limitations.push(`cartograph-${code}: ${rows.length} item(s) require review in the producer report`);\n }\n let outsideLocations = 0;\n const roots = rawArray(value.changeScope, 'Invalid Cartograph change scope.').map((item) => {\n const parsed = parseCartographSymbol(item, context.project, () => outsideLocations++);\n return parsed;\n });\n const affected = rawArray(value.affected, 'Invalid Cartograph affected symbols.').map((item) => {\n const row = object(item, 'Invalid Cartograph affected symbol.');\n const symbol = parseCartographSymbol(row.symbol, context.project, () => outsideLocations++);\n const via = safe(row.via, 'Invalid Cartograph affected parent.');\n const depth = positiveDepth(row.depth);\n const relationship = safe(row.relationship, 'Invalid Cartograph relationship.');\n const edges = row.edges === undefined ? [] : rawStrings(row.edges, 'Invalid Cartograph edges.');\n const relationships = [...new Set([relationship, ...edges])];\n if (relationships.length > 32) fail('Cartograph relationships exceed their limit.');\n return { symbol, via, depth, relationships };\n });\n if (outsideLocations > 0) {\n limitations.push(`cartograph-location-outside-project: ${outsideLocations} symbol location(s) omitted`);\n }\n const result = {\n ...context,\n roots,\n affected,\n limitations,\n truncated: truncation(value.truncated),\n } satisfies LanguageImpact;\n return validateLanguageImpact(result);\n}\n\n/** Dartograph `_document` v1을 isthmus 공통 영향 형식으로 투영한다. */\nexport function adaptDartographImpact(raw: unknown, metadata: ProducerImpactMetadata): LanguageImpact {\n const value = object(raw, 'Dartograph impact must be a JSON object.');\n if (value.version !== 1) fail('Expected Dartograph impact version 1.');\n const context = normalizeMetadata(metadata, 'dart');\n const changed = object(value.changed, 'Invalid Dartograph changed scope.');\n const changedSymbols = rawStrings(changed.symbols, 'Invalid Dartograph changed symbols.');\n const changedLibraries = changed.libraries === undefined ? [] : rawStrings(changed.libraries, 'Invalid Dartograph changed libraries.');\n if (changed.sources !== undefined) {\n rawStrings(changed.sources, 'Invalid Dartograph changed sources.').forEach(dartSource);\n }\n const roots: ImpactSymbol[] = [\n ...changedLibraries.map((id) => ({ id, qualifiedName: id, kind: 'library' })),\n ...changedSymbols.map((id) => ({ id, qualifiedName: id, kind: 'declaration' })),\n ];\n const rootIds = new Set(roots.map(({ id }) => id));\n const impacted = rawArray(value.impacted, 'Invalid Dartograph impacted symbols.');\n const rows = impacted.map((item) => {\n const row = object(item, 'Invalid Dartograph impacted symbol.');\n const id = safe(row.id, 'Invalid Dartograph impacted symbol id.');\n const depth = positiveDepth(row.depth);\n const path = rawStrings(row.path, 'Invalid Dartograph impact path.');\n if (path.length !== depth + 1 || path[0] !== id || !rootIds.has(path[path.length - 1]!) ||\n new Set(path).size !== path.length) {\n fail('Dartograph impact path is not a complete parent-safe path.');\n }\n const location = dartLocation(row);\n if (row.source !== undefined) dartSource(row.source);\n return {\n symbol: { id, qualifiedName: id, ...(location === undefined ? {} : { location }),\n ...(row.kind === undefined ? {} : { kind: safe(row.kind, 'Invalid Dartograph impacted kind.') }) },\n via: path[1] as string,\n depth,\n relationships: [],\n path,\n };\n });\n const known = new Set([...roots.map((root) => root.id), ...rows.map((row) => row.symbol.id)]);\n for (const row of rows) {\n if (row.path.some((id) => !known.has(id))) fail('Dartograph impact path contains an omitted intermediate symbol.');\n }\n const limitations = textStrings(value.limitations, 'Invalid Dartograph limitations.');\n const missing = value.missingSymbols === undefined ? [] : rawStrings(value.missingSymbols, 'Invalid Dartograph missing symbols.');\n const unattributed = changed.unattributedSources === undefined ? [] : rawStrings(changed.unattributedSources, 'Invalid Dartograph unattributed sources.');\n unattributed.forEach(dartSource);\n if (missing.length > 0) limitations.push(`dartograph-missing-symbols: ${missing.length} requested symbol(s) were not found`);\n if (unattributed.length > 0) limitations.push(`dartograph-unattributed-sources: ${unattributed.length} changed source(s) could not be attributed`);\n const result: LanguageImpact = {\n ...context,\n roots,\n affected: rows.map(({ path: _path, ...row }) => row),\n limitations,\n truncated: dartTruncation(value.truncated),\n };\n return validateLanguageImpact(result);\n}\n\ntype AdapterContext = {\n readonly id: string;\n readonly project: string;\n readonly requested: ImpactSelection;\n readonly tool: Readonly<{ name: string; version: string }>;\n readonly trigger?: string;\n readonly platform: 'dart' | 'swift';\n};\n\nfunction normalizeMetadata(metadata: ProducerImpactMetadata, platform: 'dart' | 'swift'): AdapterContext {\n const id = safe(metadata.id, 'Invalid producer impact id.');\n const project = safe(metadata.project, 'Invalid producer impact project.');\n const tool = {\n name: safe(metadata.tool?.name, 'Invalid producer impact tool name.'),\n version: safe(metadata.tool?.version, 'Invalid producer impact tool version.'),\n };\n let requested: ImpactSelection;\n try {\n requested = parseImpactSelection({ format: 'isthmus-changes', version: 1, ...metadata.requested });\n } catch {\n fail('Invalid producer impact selection.');\n }\n return { id, project, requested, tool, ...(metadata.trigger === undefined ? {} : { trigger: safe(metadata.trigger, 'Invalid producer impact trigger.') }), platform };\n}\n\nfunction parseCartographSymbol(input: unknown, project: string, outside: () => void): ImpactSymbol {\n const value = object(input, 'Invalid Cartograph symbol.');\n const id = safe(value.usr, 'Cartograph symbols require a stable usr identity.');\n const qualifiedName = safe(value.qualifiedName, 'Invalid Cartograph symbol qualified name.');\n const kind = value.kind === undefined ? undefined : safe(value.kind, 'Invalid Cartograph symbol kind.');\n const rawLocation = value.location;\n if (rawLocation === undefined || rawLocation === null) return { id, qualifiedName, ...(kind === undefined ? {} : { kind }) };\n const location = rebaseLocation(rawLocation, project, outside);\n return location === undefined ? { id, qualifiedName, ...(kind === undefined ? {} : { kind }) } : { id, qualifiedName, ...(kind === undefined ? {} : { kind }), location };\n}\n\nfunction rebaseLocation(input: unknown, project: string, outside: () => void): BridgeLocation | undefined {\n const value = object(input, 'Invalid Cartograph symbol location.');\n const path = safe(value.path, 'Invalid Cartograph symbol location path.');\n if (!Number.isSafeInteger(value.line) || (value.line as number) < 1 ||\n !Number.isSafeInteger(value.column) || (value.column as number) < 1) {\n fail('Invalid Cartograph symbol location coordinates.');\n }\n if (!path.startsWith('/')) fail('Cartograph symbol locations must be absolute paths.');\n const root = project === '/' ? '/' : project.replace(/\\/+$/u, '');\n if (path === root) {\n outside();\n return undefined;\n }\n const prefix = root === '/' ? '/' : `${root}/`;\n if (!path.startsWith(prefix)) {\n outside();\n return undefined;\n }\n const relative = path.slice(prefix.length);\n if (!isProjectRelativePath(relative)) fail('Invalid rebased Cartograph symbol location.');\n return { path: relative, line: value.line as number, column: value.column as number };\n}\n\nfunction dartLocation(value: Record<string, unknown>): BridgeLocation | undefined {\n if (value.line === undefined && value.column === undefined) return undefined;\n if (!Number.isSafeInteger(value.line) || (value.line as number) < 1 ||\n !Number.isSafeInteger(value.column) || (value.column as number) < 1) {\n fail('Invalid Dartograph impact location.');\n }\n // producer가 GraphNode에서 실은 선언 위치만 사용하며 경로나 좌표를 추측하지 않는다.\n return value.source === undefined ? undefined\n : { path: dartSource(value.source), line: value.line as number, column: value.column as number };\n}\n\nfunction dartTruncation(input: unknown): boolean {\n if (!Number.isSafeInteger(input) || (input as number) < 0) fail('Invalid Dartograph truncation count.');\n return (input as number) > 0;\n}\n\nfunction dartSource(input: unknown): string {\n const source = safe(input, 'Invalid Dartograph impact source.');\n const path = source.startsWith('project:') ? source.slice('project:'.length) : source;\n if (!isProjectRelativePath(path)) fail('Dartograph impact source must be project-relative.');\n return path;\n}\n\nfunction truncation(input: unknown): boolean {\n if (typeof input === 'boolean') return input;\n const value = object(input, 'Invalid impact truncation.');\n if (typeof value.depth !== 'boolean' || typeof value.output !== 'boolean' || !Array.isArray(value.sections) ||\n !value.sections.every((section) => isSafeNonEmptyString(section))) fail('Invalid impact truncation.');\n return value.depth || value.output || value.sections.length > 0;\n}\n\nfunction positiveDepth(input: unknown): number {\n if (!Number.isSafeInteger(input) || (input as number) < 1 || (input as number) > 128) fail('Impact depth must be between 1 and 128.');\n return input as number;\n}\n\nfunction object(input: unknown, message: string): Record<string, unknown> {\n if (typeof input !== 'object' || input === null || Array.isArray(input)) fail(message);\n return input as Record<string, unknown>;\n}\n\nfunction rawArray(input: unknown, message: string): unknown[] {\n if (!Array.isArray(input) || input.length > 50_000) fail(message);\n return input;\n}\n\nfunction rawStrings(input: unknown, message: string): string[] {\n if (!Array.isArray(input) || input.length > 50_000 || !input.every((item) => isSafeNonEmptyString(item))) fail(message);\n return [...input] as string[];\n}\n\nfunction textStrings(input: unknown, message: string): string[] {\n if (!Array.isArray(input) || input.length > 50_000 || !input.every((item) => typeof item === 'string')) fail(message);\n return [...input] as string[];\n}\n\nfunction safe(input: unknown, message: string): string {\n if (!isSafeNonEmptyString(input)) fail(message);\n return input;\n}\n\nfunction fail(message: string): never {\n throw new PreflightValidationError(message);\n}\n"]}
@@ -0,0 +1,69 @@
1
+ import type { BridgeLocation } from './parse.ts';
2
+ /** 실제 실행에서 관찰한 플랫폼이다. Dart/Swift 같은 소스 언어와 혼동하지 않는다. */
3
+ export type RuntimePlatform = 'ios' | 'macos' | 'android' | 'linux' | 'windows';
4
+ /** 메시지 형태가 다른 채널을 이름만으로 합치지 않도록 구분한다. */
5
+ export type RuntimeTransport = 'method-channel' | 'basic-message-channel';
6
+ /** 원문 예외나 payload 없이 통신 결과만 분류한다. */
7
+ export type RuntimeOutcome = 'success' | 'missing-handler' | 'error' | 'timeout' | 'pending';
8
+ /** 기대 목록에서 허용할 수 있는 응답 결과다. pending은 종료 결과가 아니므로 제외한다. */
9
+ export type RuntimeTerminalOutcome = Exclude<RuntimeOutcome, 'pending'>;
10
+ /** 결과 허용 목록이 입력을 통한 비용 증폭을 일으키지 않도록 제한한다. */
11
+ export declare const MAX_RUNTIME_ALLOWED_OUTCOMES = 4;
12
+ /** 실제 라우팅 주소다. BasicMessageChannel에는 가상의 method를 만들지 않는다. */
13
+ export interface RuntimeRoute {
14
+ readonly transport: RuntimeTransport;
15
+ readonly channel: string;
16
+ readonly method?: string;
17
+ }
18
+ /** 실행 순서와 엔진 인스턴스를 가진 호출 관찰이다. */
19
+ export interface RuntimeEvent extends RuntimeRoute {
20
+ readonly sequence: number;
21
+ readonly instance: string;
22
+ readonly outcome: RuntimeOutcome;
23
+ readonly caller?: BridgeLocation;
24
+ }
25
+ /** 한 시나리오 실행의 폐쇄 여부와 코드 revision을 선언하는 생산 문서다. */
26
+ export interface BridgeRuntimeDocument {
27
+ readonly format: 'bridge-runtime';
28
+ readonly version: 1;
29
+ readonly project: string;
30
+ readonly revision: string;
31
+ readonly tool: {
32
+ readonly name: string;
33
+ readonly version: string;
34
+ };
35
+ readonly run: {
36
+ readonly id: string;
37
+ readonly scenario: string;
38
+ readonly platform: RuntimePlatform;
39
+ readonly status: 'completed' | 'incomplete';
40
+ readonly startedAt: string;
41
+ readonly finishedAt?: string;
42
+ };
43
+ readonly droppedEvents: number;
44
+ readonly events: readonly RuntimeEvent[];
45
+ }
46
+ /** 지정한 시나리오·플랫폼에서 관찰해야 하는 통신이다. instance 생략은 모든 인스턴스다. */
47
+ export interface RuntimeExpectation extends RuntimeRoute {
48
+ readonly id: string;
49
+ readonly scenario: string;
50
+ readonly platform: RuntimePlatform;
51
+ readonly instance?: string;
52
+ readonly allowedOutcomes?: readonly RuntimeTerminalOutcome[];
53
+ }
54
+ /** CI가 실행해야 하는 검증 목록. 런타임 로그에서 기대값을 역생성하지 않는다. */
55
+ export interface RuntimeExpectations {
56
+ readonly format: 'bridge-expectations';
57
+ readonly version: 1;
58
+ readonly project: string;
59
+ readonly revision: string;
60
+ readonly checks: readonly RuntimeExpectation[];
61
+ }
62
+ /** 입력 계약 실패를 원문 데이터 없는 메시지로 전달한다. */
63
+ export declare class RuntimeValidationError extends Error {
64
+ constructor(message: string);
65
+ }
66
+ /** 신뢰하지 않는 런타임 JSON에서 라우팅·관측 결과·생산 문맥만 보존한다. */
67
+ export declare function parseBridgeRuntime(input: unknown): BridgeRuntimeDocument;
68
+ /** 기대 목록의 빈 통과와 중복 식별자를 거부한다. */
69
+ export declare function parseRuntimeExpectations(input: unknown): RuntimeExpectations;
@@ -0,0 +1,160 @@
1
+ import { isBridgeTimestamp, isProjectRelativePath, isSafeNonEmptyString } from "./parse.js";
2
+ /** 결과 허용 목록이 입력을 통한 비용 증폭을 일으키지 않도록 제한한다. */
3
+ export const MAX_RUNTIME_ALLOWED_OUTCOMES = 4;
4
+ /** 입력 계약 실패를 원문 데이터 없는 메시지로 전달한다. */
5
+ export class RuntimeValidationError extends Error {
6
+ constructor(message) { super(message); this.name = 'RuntimeValidationError'; }
7
+ }
8
+ /** 신뢰하지 않는 런타임 JSON에서 라우팅·관측 결과·생산 문맥만 보존한다. */
9
+ export function parseBridgeRuntime(input) {
10
+ const value = document(input, 'bridge-runtime');
11
+ const tool = object(value.tool);
12
+ const run = object(value.run);
13
+ const status = run.status;
14
+ if (status !== 'completed' && status !== 'incomplete')
15
+ fail('Invalid runtime run status.');
16
+ if (!isBridgeTimestamp(run.startedAt) ||
17
+ (run.finishedAt !== undefined && !isBridgeTimestamp(run.finishedAt)) ||
18
+ (status === 'completed' && run.finishedAt === undefined) ||
19
+ (typeof run.finishedAt === 'string' && Date.parse(run.finishedAt) < Date.parse(run.startedAt))) {
20
+ fail('Invalid runtime run timestamps.');
21
+ }
22
+ const droppedEvents = nonnegativeInteger(value.droppedEvents);
23
+ const rawEvents = array(value.events, 100_000);
24
+ const sequences = new Set();
25
+ let maximumSequence = 0;
26
+ const events = rawEvents.map((item) => {
27
+ const event = object(item);
28
+ const sequence = nonnegativeInteger(event.sequence);
29
+ if (sequence === 0 || sequences.has(sequence))
30
+ fail('Runtime sequences must be positive and unique.');
31
+ sequences.add(sequence);
32
+ maximumSequence = Math.max(maximumSequence, sequence);
33
+ return {
34
+ ...route(event), sequence, instance: safeString(event.instance), outcome: outcome(event.outcome),
35
+ ...(event.caller === undefined ? {} : { caller: location(event.caller) }),
36
+ };
37
+ }).sort((a, b) => a.sequence - b.sequence);
38
+ if (maximumSequence - events.length > droppedEvents) {
39
+ fail('Missing runtime sequences must be counted in droppedEvents.');
40
+ }
41
+ return {
42
+ format: 'bridge-runtime', version: 1,
43
+ project: safeString(value.project), revision: safeString(value.revision),
44
+ tool: { name: safeString(tool.name), version: safeString(tool.version) },
45
+ run: { id: safeString(run.id), scenario: safeString(run.scenario),
46
+ platform: platform(run.platform), status, startedAt: run.startedAt,
47
+ ...(run.finishedAt === undefined ? {} : { finishedAt: run.finishedAt }),
48
+ }, droppedEvents, events,
49
+ };
50
+ }
51
+ /** 기대 목록의 빈 통과와 중복 식별자를 거부한다. */
52
+ export function parseRuntimeExpectations(input) {
53
+ const value = document(input, 'bridge-expectations');
54
+ const items = array(value.checks, 10_000);
55
+ if (items.length === 0)
56
+ fail('Runtime expectations require at least one check.');
57
+ const ids = new Set();
58
+ const checks = items.map((item) => {
59
+ const check = object(item);
60
+ const id = safeString(check.id);
61
+ if (ids.has(id))
62
+ fail('Runtime expectation ids must be unique.');
63
+ ids.add(id);
64
+ return {
65
+ ...route(check), id, scenario: safeString(check.scenario), platform: platform(check.platform),
66
+ ...(check.instance === undefined ? {} : { instance: safeString(check.instance) }),
67
+ allowedOutcomes: allowedOutcomes(check.allowedOutcomes),
68
+ };
69
+ });
70
+ return { format: 'bridge-expectations', version: 1,
71
+ project: safeString(value.project), revision: safeString(value.revision), checks };
72
+ }
73
+ /** 실행 문서와 기대 문서가 공유하는 버전 경계다. */
74
+ function document(input, format) {
75
+ const value = object(input);
76
+ if (value.format !== format || value.version !== 1)
77
+ fail('Unsupported runtime document format or version.');
78
+ return value;
79
+ }
80
+ /** 외부 JSON 객체의 구조만 검증한다. */
81
+ function object(input) {
82
+ if (typeof input !== 'object' || input === null || Array.isArray(input))
83
+ fail('Expected a runtime JSON object.');
84
+ return input;
85
+ }
86
+ /** 상한 검사를 순회보다 먼저 수행한다. */
87
+ function array(input, maximum) {
88
+ if (!Array.isArray(input) || input.length > maximum)
89
+ fail('Invalid runtime array or item limit exceeded.');
90
+ return input;
91
+ }
92
+ /** 채널 이름·문맥 식별자에 기존 교환 문자열 규칙을 적용한다. */
93
+ function safeString(input) {
94
+ if (!isSafeNonEmptyString(input))
95
+ fail('Expected a non-empty safe runtime string.');
96
+ return input;
97
+ }
98
+ /** 유실 개수와 순서를 부동소수점이나 음수로 위조할 수 없게 한다. */
99
+ function nonnegativeInteger(input) {
100
+ if (!Number.isSafeInteger(input) || Number(input) < 0)
101
+ fail('Expected a nonnegative runtime integer.');
102
+ return input;
103
+ }
104
+ /** 플랫폼을 소스 경로나 실행 파일 이름에서 추측하지 않는다. */
105
+ function platform(input) {
106
+ if (input !== 'ios' && input !== 'macos' && input !== 'android' && input !== 'linux' && input !== 'windows') {
107
+ fail('Unsupported runtime platform.');
108
+ }
109
+ return input;
110
+ }
111
+ /** 런타임 채널별 주소 형태를 검증한다. */
112
+ function route(input) {
113
+ const transport = input.transport;
114
+ if (transport !== 'method-channel' && transport !== 'basic-message-channel')
115
+ fail('Unsupported runtime transport.');
116
+ const channel = safeString(input.channel);
117
+ if (transport === 'method-channel')
118
+ return { transport, channel, method: safeString(input.method) };
119
+ if (input.method !== undefined)
120
+ fail('Basic message routes cannot declare a method.');
121
+ return { transport, channel };
122
+ }
123
+ /** 예외 텍스트 대신 고정된 통신 결과만 허용한다. */
124
+ function outcome(input) {
125
+ if (input !== 'success' && input !== 'missing-handler' && input !== 'error' && input !== 'timeout' && input !== 'pending') {
126
+ fail('Unsupported runtime outcome.');
127
+ }
128
+ return input;
129
+ }
130
+ /** 기대 결과는 비어 있지 않은 고유 terminal outcome 목록으로 제한한다. */
131
+ function allowedOutcomes(input) {
132
+ if (input === undefined)
133
+ return ['success'];
134
+ if (!Array.isArray(input) || input.length === 0 || input.length > MAX_RUNTIME_ALLOWED_OUTCOMES) {
135
+ fail('Runtime allowed outcomes require 1 to 4 terminal outcomes.');
136
+ }
137
+ const seen = new Set();
138
+ const outcomes = [];
139
+ for (const item of input) {
140
+ if (item !== 'success' && item !== 'missing-handler' && item !== 'error' && item !== 'timeout') {
141
+ fail('Runtime allowed outcomes must be unique terminal outcomes.');
142
+ }
143
+ if (seen.has(item))
144
+ fail('Runtime allowed outcomes must be unique terminal outcomes.');
145
+ seen.add(item);
146
+ outcomes.push(item);
147
+ }
148
+ return outcomes;
149
+ }
150
+ /** 자동 추측한 호출 스택 대신 생산자가 선언한 상대 소스 위치를 검증한다. */
151
+ function location(input) {
152
+ const value = object(input);
153
+ if (!isProjectRelativePath(value.path) || !Number.isSafeInteger(value.line) || Number(value.line) < 1 ||
154
+ !Number.isSafeInteger(value.column) || Number(value.column) < 1)
155
+ fail('Invalid runtime caller location.');
156
+ return { path: value.path, line: value.line, column: value.column };
157
+ }
158
+ /** 입력 값이나 예외 payload를 메시지에 포함하지 않는다. */
159
+ function fail(message) { throw new RuntimeValidationError(message); }
160
+ //# sourceMappingURL=runtime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.js","sourceRoot":"","sources":["../../src/exchange/runtime.ts"],"names":[],"mappings":"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","sourcesContent":["import { isBridgeTimestamp, isProjectRelativePath, isSafeNonEmptyString } from './parse.ts';\nimport type { BridgeLocation } from './parse.ts';\n\n/** 실제 실행에서 관찰한 플랫폼이다. Dart/Swift 같은 소스 언어와 혼동하지 않는다. */\nexport type RuntimePlatform = 'ios' | 'macos' | 'android' | 'linux' | 'windows';\n/** 메시지 형태가 다른 채널을 이름만으로 합치지 않도록 구분한다. */\nexport type RuntimeTransport = 'method-channel' | 'basic-message-channel';\n/** 원문 예외나 payload 없이 통신 결과만 분류한다. */\nexport type RuntimeOutcome = 'success' | 'missing-handler' | 'error' | 'timeout' | 'pending';\n/** 기대 목록에서 허용할 수 있는 응답 결과다. pending은 종료 결과가 아니므로 제외한다. */\nexport type RuntimeTerminalOutcome = Exclude<RuntimeOutcome, 'pending'>;\n\n/** 결과 허용 목록이 입력을 통한 비용 증폭을 일으키지 않도록 제한한다. */\nexport const MAX_RUNTIME_ALLOWED_OUTCOMES = 4;\n\n/** 실제 라우팅 주소다. BasicMessageChannel에는 가상의 method를 만들지 않는다. */\nexport interface RuntimeRoute {\n readonly transport: RuntimeTransport;\n readonly channel: string;\n readonly method?: string;\n}\n\n/** 실행 순서와 엔진 인스턴스를 가진 호출 관찰이다. */\nexport interface RuntimeEvent extends RuntimeRoute {\n readonly sequence: number;\n readonly instance: string;\n readonly outcome: RuntimeOutcome;\n readonly caller?: BridgeLocation;\n}\n\n/** 한 시나리오 실행의 폐쇄 여부와 코드 revision을 선언하는 생산 문서다. */\nexport interface BridgeRuntimeDocument {\n readonly format: 'bridge-runtime';\n readonly version: 1;\n readonly project: string;\n readonly revision: string;\n readonly tool: { readonly name: string; readonly version: string };\n readonly run: {\n readonly id: string;\n readonly scenario: string;\n readonly platform: RuntimePlatform;\n readonly status: 'completed' | 'incomplete';\n readonly startedAt: string;\n readonly finishedAt?: string;\n };\n readonly droppedEvents: number;\n readonly events: readonly RuntimeEvent[];\n}\n\n/** 지정한 시나리오·플랫폼에서 관찰해야 하는 통신이다. instance 생략은 모든 인스턴스다. */\nexport interface RuntimeExpectation extends RuntimeRoute {\n readonly id: string;\n readonly scenario: string;\n readonly platform: RuntimePlatform;\n readonly instance?: string;\n readonly allowedOutcomes?: readonly RuntimeTerminalOutcome[];\n}\n\n/** CI가 실행해야 하는 검증 목록. 런타임 로그에서 기대값을 역생성하지 않는다. */\nexport interface RuntimeExpectations {\n readonly format: 'bridge-expectations';\n readonly version: 1;\n readonly project: string;\n readonly revision: string;\n readonly checks: readonly RuntimeExpectation[];\n}\n\n/** 입력 계약 실패를 원문 데이터 없는 메시지로 전달한다. */\nexport class RuntimeValidationError extends Error {\n constructor(message: string) { super(message); this.name = 'RuntimeValidationError'; }\n}\n\n/** 신뢰하지 않는 런타임 JSON에서 라우팅·관측 결과·생산 문맥만 보존한다. */\nexport function parseBridgeRuntime(input: unknown): BridgeRuntimeDocument {\n const value = document(input, 'bridge-runtime');\n const tool = object(value.tool);\n const run = object(value.run);\n const status = run.status;\n if (status !== 'completed' && status !== 'incomplete') fail('Invalid runtime run status.');\n if (!isBridgeTimestamp(run.startedAt) ||\n (run.finishedAt !== undefined && !isBridgeTimestamp(run.finishedAt)) ||\n (status === 'completed' && run.finishedAt === undefined) ||\n (typeof run.finishedAt === 'string' && Date.parse(run.finishedAt) < Date.parse(run.startedAt))) {\n fail('Invalid runtime run timestamps.');\n }\n const droppedEvents = nonnegativeInteger(value.droppedEvents);\n const rawEvents = array(value.events, 100_000);\n const sequences = new Set<number>();\n let maximumSequence = 0;\n const events = rawEvents.map((item): RuntimeEvent => {\n const event = object(item);\n const sequence = nonnegativeInteger(event.sequence);\n if (sequence === 0 || sequences.has(sequence)) fail('Runtime sequences must be positive and unique.');\n sequences.add(sequence);\n maximumSequence = Math.max(maximumSequence, sequence);\n return {\n ...route(event), sequence, instance: safeString(event.instance), outcome: outcome(event.outcome),\n ...(event.caller === undefined ? {} : { caller: location(event.caller) }),\n };\n }).sort((a, b) => a.sequence - b.sequence);\n if (maximumSequence - events.length > droppedEvents) {\n fail('Missing runtime sequences must be counted in droppedEvents.');\n }\n return {\n format: 'bridge-runtime', version: 1,\n project: safeString(value.project), revision: safeString(value.revision),\n tool: { name: safeString(tool.name), version: safeString(tool.version) },\n run: { id: safeString(run.id), scenario: safeString(run.scenario),\n platform: platform(run.platform), status, startedAt: run.startedAt,\n ...(run.finishedAt === undefined ? {} : { finishedAt: run.finishedAt as string }),\n }, droppedEvents, events,\n };\n}\n\n/** 기대 목록의 빈 통과와 중복 식별자를 거부한다. */\nexport function parseRuntimeExpectations(input: unknown): RuntimeExpectations {\n const value = document(input, 'bridge-expectations');\n const items = array(value.checks, 10_000);\n if (items.length === 0) fail('Runtime expectations require at least one check.');\n const ids = new Set<string>();\n const checks = items.map((item): RuntimeExpectation => {\n const check = object(item);\n const id = safeString(check.id);\n if (ids.has(id)) fail('Runtime expectation ids must be unique.');\n ids.add(id);\n return {\n ...route(check), id, scenario: safeString(check.scenario), platform: platform(check.platform),\n ...(check.instance === undefined ? {} : { instance: safeString(check.instance) }),\n allowedOutcomes: allowedOutcomes(check.allowedOutcomes),\n };\n });\n return { format: 'bridge-expectations', version: 1,\n project: safeString(value.project), revision: safeString(value.revision), checks };\n}\n\n/** 실행 문서와 기대 문서가 공유하는 버전 경계다. */\nfunction document(input: unknown, format: string): Record<string, unknown> {\n const value = object(input);\n if (value.format !== format || value.version !== 1) fail('Unsupported runtime document format or version.');\n return value;\n}\n\n/** 외부 JSON 객체의 구조만 검증한다. */\nfunction object(input: unknown): Record<string, unknown> {\n if (typeof input !== 'object' || input === null || Array.isArray(input)) fail('Expected a runtime JSON object.');\n return input as Record<string, unknown>;\n}\n\n/** 상한 검사를 순회보다 먼저 수행한다. */\nfunction array(input: unknown, maximum: number): unknown[] {\n if (!Array.isArray(input) || input.length > maximum) fail('Invalid runtime array or item limit exceeded.');\n return input;\n}\n\n/** 채널 이름·문맥 식별자에 기존 교환 문자열 규칙을 적용한다. */\nfunction safeString(input: unknown): string {\n if (!isSafeNonEmptyString(input)) fail('Expected a non-empty safe runtime string.');\n return input;\n}\n\n/** 유실 개수와 순서를 부동소수점이나 음수로 위조할 수 없게 한다. */\nfunction nonnegativeInteger(input: unknown): number {\n if (!Number.isSafeInteger(input) || Number(input) < 0) fail('Expected a nonnegative runtime integer.');\n return input as number;\n}\n\n/** 플랫폼을 소스 경로나 실행 파일 이름에서 추측하지 않는다. */\nfunction platform(input: unknown): RuntimePlatform {\n if (input !== 'ios' && input !== 'macos' && input !== 'android' && input !== 'linux' && input !== 'windows') {\n fail('Unsupported runtime platform.');\n }\n return input;\n}\n\n/** 런타임 채널별 주소 형태를 검증한다. */\nfunction route(input: Record<string, unknown>): RuntimeRoute {\n const transport = input.transport;\n if (transport !== 'method-channel' && transport !== 'basic-message-channel') fail('Unsupported runtime transport.');\n const channel = safeString(input.channel);\n if (transport === 'method-channel') return { transport, channel, method: safeString(input.method) };\n if (input.method !== undefined) fail('Basic message routes cannot declare a method.');\n return { transport, channel };\n}\n\n/** 예외 텍스트 대신 고정된 통신 결과만 허용한다. */\nfunction outcome(input: unknown): RuntimeOutcome {\n if (input !== 'success' && input !== 'missing-handler' && input !== 'error' && input !== 'timeout' && input !== 'pending') {\n fail('Unsupported runtime outcome.');\n }\n return input;\n}\n\n/** 기대 결과는 비어 있지 않은 고유 terminal outcome 목록으로 제한한다. */\nfunction allowedOutcomes(input: unknown): readonly RuntimeTerminalOutcome[] {\n if (input === undefined) return ['success'];\n if (!Array.isArray(input) || input.length === 0 || input.length > MAX_RUNTIME_ALLOWED_OUTCOMES) {\n fail('Runtime allowed outcomes require 1 to 4 terminal outcomes.');\n }\n const seen = new Set<RuntimeTerminalOutcome>();\n const outcomes: RuntimeTerminalOutcome[] = [];\n for (const item of input) {\n if (item !== 'success' && item !== 'missing-handler' && item !== 'error' && item !== 'timeout') {\n fail('Runtime allowed outcomes must be unique terminal outcomes.');\n }\n if (seen.has(item)) fail('Runtime allowed outcomes must be unique terminal outcomes.');\n seen.add(item);\n outcomes.push(item);\n }\n return outcomes;\n}\n\n/** 자동 추측한 호출 스택 대신 생산자가 선언한 상대 소스 위치를 검증한다. */\nfunction location(input: unknown): BridgeLocation {\n const value = object(input);\n if (!isProjectRelativePath(value.path) || !Number.isSafeInteger(value.line) || Number(value.line) < 1 ||\n !Number.isSafeInteger(value.column) || Number(value.column) < 1) fail('Invalid runtime caller location.');\n return { path: value.path, line: value.line as number, column: value.column as number };\n}\n\n/** 입력 값이나 예외 payload를 메시지에 포함하지 않는다. */\nfunction fail(message: string): never { throw new RuntimeValidationError(message); }\n"]}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * extract-js의 문서 조립 레이어다.
3
+ *
4
+ * 파일별 스캔 결과를 모아 상대 경로 import를 스캔 집합 안에서 해석하고,
5
+ * 바인딩이 확정된 식별자의 멤버 호출을 method-invoke 사실로 바꾼다.
6
+ * 파일시스템을 읽지 않는 순수 조립이다 — 경로 해석·읽기는 cli/가 담당한다.
7
+ */
8
+ import type { BridgeFactsDocument } from '../exchange/parse.ts';
9
+ /** 스캔 대상이 되는 JS/TS 확장자다. */
10
+ export declare const JS_SOURCE_EXTENSIONS: readonly ["ts", "tsx", "js", "jsx", "mjs", "cjs", "mts", "cts"];
11
+ /** 조립 입력 — 프로젝트 상대 경로와 UTF-8 텍스트다. */
12
+ export interface JsSourceFile {
13
+ readonly path: string;
14
+ readonly text: string;
15
+ }
16
+ /**
17
+ * 스캔한 JS/TS 파일들을 bridge-facts v1 문서로 조립한다.
18
+ *
19
+ * 사실이 하나도 없으면 계약상 target은 null이다 — 관찰했으나 없음과 관찰
20
+ * 자체가 불가를 구분해 빈 사실이 "청정"으로 읽히지 않게 한다.
21
+ */
22
+ export declare function createJsFactsDocument(files: readonly JsSourceFile[], toolVersion: string, generatedAt: string, project: string): BridgeFactsDocument;
@@ -0,0 +1,230 @@
1
+ /**
2
+ * extract-js의 문서 조립 레이어다.
3
+ *
4
+ * 파일별 스캔 결과를 모아 상대 경로 import를 스캔 집합 안에서 해석하고,
5
+ * 바인딩이 확정된 식별자의 멤버 호출을 method-invoke 사실로 바꾼다.
6
+ * 파일시스템을 읽지 않는 순수 조립이다 — 경로 해석·읽기는 cli/가 담당한다.
7
+ */
8
+ import { scanJsSource } from "./js-scan.js";
9
+ /** 스캔 대상이 되는 JS/TS 확장자다. */
10
+ export const JS_SOURCE_EXTENSIONS = [
11
+ 'ts', 'tsx', 'js', 'jsx', 'mjs', 'cjs', 'mts', 'cts',
12
+ ];
13
+ /**
14
+ * 스캔한 JS/TS 파일들을 bridge-facts v1 문서로 조립한다.
15
+ *
16
+ * 사실이 하나도 없으면 계약상 target은 null이다 — 관찰했으나 없음과 관찰
17
+ * 자체가 불가를 구분해 빈 사실이 "청정"으로 읽히지 않게 한다.
18
+ */
19
+ export function createJsFactsDocument(files, toolVersion, generatedAt, project) {
20
+ const scans = new Map();
21
+ for (const file of files)
22
+ scans.set(file.path, scanJsSource(file.text));
23
+ const facts = [];
24
+ let unattributedRelativeCalls = 0;
25
+ let unattributedPackageCalls = 0;
26
+ for (const file of files) {
27
+ const scan = scans.get(file.path);
28
+ const resolvedIdents = new Map(scan.bindings);
29
+ for (const entry of scan.imports) {
30
+ const bound = resolveImport(scans, file.path, entry);
31
+ if (bound !== undefined) {
32
+ resolvedIdents.set(entry.localName, bound);
33
+ }
34
+ else if (entry.specifier.startsWith('.')) {
35
+ unattributedRelativeCalls += countMemberCalls(scan, entry.localName);
36
+ }
37
+ else {
38
+ unattributedPackageCalls += countMemberCalls(scan, entry.localName);
39
+ }
40
+ }
41
+ for (const fact of scan.facts) {
42
+ facts.push(toFact(file.path, fact));
43
+ }
44
+ for (const call of scan.memberCalls) {
45
+ const bound = resolvedIdents.get(call.ident);
46
+ if (bound === undefined)
47
+ continue;
48
+ const name = 'name' in bound ? bound.name : bound.dynamicExpression;
49
+ facts.push({
50
+ kind: 'method-invoke',
51
+ channel: name,
52
+ method: call.method,
53
+ dynamic: !('name' in bound) || call.dynamicMethod,
54
+ location: locationOf(file.path, call.token),
55
+ });
56
+ }
57
+ }
58
+ const sorted = dedupeFacts(facts);
59
+ const limitations = buildLimitations(files.length, scans, {
60
+ unattributedRelativeCalls,
61
+ unattributedPackageCalls,
62
+ });
63
+ return {
64
+ format: 'bridge-facts',
65
+ version: 1,
66
+ tool: { name: 'isthmus', version: toolVersion },
67
+ generatedAt,
68
+ platform: 'js',
69
+ target: sorted.length === 0 ? null : 'react-native',
70
+ project,
71
+ facts: sorted,
72
+ limitations,
73
+ };
74
+ }
75
+ /** 한 식별자에 대한 멤버 호출 수를 센다(미귀속 한계 계수용). */
76
+ function countMemberCalls(scan, ident) {
77
+ return scan.memberCalls.filter((call) => call.ident === ident).length;
78
+ }
79
+ /**
80
+ * `import X from 'spec'`을 스캔 집합 안에서 해석한다.
81
+ *
82
+ * 상대 specifier만 해석한다 — 패키지 specifier는 스캔 집합(node_modules는
83
+ * 제외됨) 밖이라 귀속 근거가 없다. 배럴 재수출은 순환을 피하기 위해 홉 상한
84
+ * 안에서 따라간다.
85
+ */
86
+ function resolveImport(scans, fromPath, entry) {
87
+ let specifier = entry.specifier;
88
+ let exportedName = entry.exportedName;
89
+ let currentDir = dirnameOf(fromPath);
90
+ for (let hop = 0; hop < 4; hop++) {
91
+ if (!specifier.startsWith('.'))
92
+ return undefined;
93
+ const target = resolveSpecifier(scans, currentDir, specifier);
94
+ if (target === undefined)
95
+ return undefined;
96
+ const scan = scans.get(target);
97
+ const bound = exportedName === 'default'
98
+ ? scan.exports.defaultName
99
+ : scan.exports.named.get(exportedName);
100
+ if (bound !== undefined)
101
+ return bound;
102
+ const reexport = exportedName === 'default'
103
+ ? scan.exports.reexported.get('default')
104
+ : scan.exports.reexported.get(exportedName);
105
+ if (reexport === undefined)
106
+ return undefined;
107
+ specifier = reexport.specifier;
108
+ exportedName = reexport.exportedName;
109
+ currentDir = dirnameOf(target);
110
+ }
111
+ return undefined;
112
+ }
113
+ /** specifier를 디렉터리 기준으로 정규화해 스캔 집합에 있는 파일을 찾는다. */
114
+ function resolveSpecifier(scans, dir, specifier) {
115
+ const base = normalizePath(dir === '' ? specifier : `${dir}/${specifier}`);
116
+ const candidates = [
117
+ base,
118
+ ...JS_SOURCE_EXTENSIONS.map((ext) => `${base}.${ext}`),
119
+ ...JS_SOURCE_EXTENSIONS.map((ext) => `${base}/index.${ext}`),
120
+ ];
121
+ return candidates.find((candidate) => scans.has(candidate));
122
+ }
123
+ /** `.`·`..` 세그먼트를 접은 POSIX 상대 경로다. 스캔 집합 밖 탈출은 해석 불가다. */
124
+ function normalizePath(path) {
125
+ const segments = [];
126
+ for (const segment of path.split('/')) {
127
+ if (segment === '' || segment === '.')
128
+ continue;
129
+ if (segment === '..') {
130
+ if (segments.length === 0)
131
+ return '';
132
+ segments.pop();
133
+ continue;
134
+ }
135
+ segments.push(segment);
136
+ }
137
+ return segments.join('/');
138
+ }
139
+ /** 프로젝트 상대 경로의 디렉터리 부분이다. */
140
+ function dirnameOf(path) {
141
+ const slash = path.lastIndexOf('/');
142
+ return slash === -1 ? '' : path.slice(0, slash);
143
+ }
144
+ /** 스캔 사실을 계약 필드 모양으로 바꾼다. */
145
+ function toFact(path, fact) {
146
+ return {
147
+ kind: fact.kind,
148
+ channel: fact.channel,
149
+ ...(fact.method === undefined ? {} : { method: fact.method }),
150
+ ...(fact.mechanism === undefined ? {} : { mechanism: fact.mechanism }),
151
+ ...(fact.optional === undefined ? {} : { optional: fact.optional }),
152
+ dynamic: fact.dynamic,
153
+ location: locationOf(path, fact.token),
154
+ };
155
+ }
156
+ /** 토큰 위치를 계약 위치로 바꾼다. */
157
+ function locationOf(path, token) {
158
+ return { path, line: token.line, column: token.column };
159
+ }
160
+ /** 같은 위치·같은 키의 중복 사실을 한 번만 남기고 결정적으로 정렬한다. */
161
+ function dedupeFacts(facts) {
162
+ const seen = new Map();
163
+ for (const fact of facts) {
164
+ const key = JSON.stringify([
165
+ fact.kind,
166
+ fact.channel,
167
+ fact.method ?? null,
168
+ fact.location.path,
169
+ fact.location.line,
170
+ fact.location.column,
171
+ fact.dynamic,
172
+ ]);
173
+ if (!seen.has(key))
174
+ seen.set(key, fact);
175
+ }
176
+ return [...seen.values()].sort((left, right) => {
177
+ const byPath = left.location.path.localeCompare(right.location.path);
178
+ if (byPath !== 0)
179
+ return byPath;
180
+ if (left.location.line !== right.location.line) {
181
+ return left.location.line - right.location.line;
182
+ }
183
+ if (left.location.column !== right.location.column) {
184
+ return left.location.column - right.location.column;
185
+ }
186
+ return (left.channel ?? '').localeCompare(right.channel ?? '');
187
+ });
188
+ }
189
+ /** 집계 계수와 고정 범위 설명을 생산자 limitation 문자열로 바꾼다. */
190
+ function buildLimitations(fileCount, scans, extra) {
191
+ let dynamicModuleNames = 0;
192
+ let dynamicComponentNames = 0;
193
+ let dynamicMethodNames = 0;
194
+ for (const scan of scans.values()) {
195
+ dynamicModuleNames += scan.counts.dynamicModuleNames;
196
+ dynamicComponentNames += scan.counts.dynamicComponentNames;
197
+ dynamicMethodNames += scan.counts.dynamicMethodNames;
198
+ }
199
+ const limitations = [
200
+ 'js-binding-scope: member calls through bindings that leave the scan set '
201
+ + '(package imports, function results, instance state) are not attributed '
202
+ + 'to a module name',
203
+ ];
204
+ if (dynamicModuleNames > 0) {
205
+ limitations.push(`dynamic-module-names: ${dynamicModuleNames} module references use a `
206
+ + 'non-literal name');
207
+ }
208
+ if (dynamicComponentNames > 0) {
209
+ limitations.push(`dynamic-component-names: ${dynamicComponentNames} component requires `
210
+ + 'use a non-literal name');
211
+ }
212
+ if (dynamicMethodNames > 0) {
213
+ limitations.push(`dynamic-method-names: ${dynamicMethodNames} bridge method calls use a `
214
+ + 'non-literal method name');
215
+ }
216
+ if (extra.unattributedRelativeCalls > 0) {
217
+ limitations.push(`unattributed-js-relative-calls: ${extra.unattributedRelativeCalls} `
218
+ + 'member calls on imports that did not resolve inside the scanned '
219
+ + 'file set could not be attributed to a module name');
220
+ }
221
+ if (extra.unattributedPackageCalls > 0) {
222
+ limitations.push(`unattributed-js-package-calls: ${extra.unattributedPackageCalls} member `
223
+ + 'calls on package imports could not be attributed to a module name');
224
+ }
225
+ if (fileCount === 0) {
226
+ limitations.push('empty-js-scan: no JS/TS source files were scanned');
227
+ }
228
+ return limitations;
229
+ }
230
+ //# sourceMappingURL=js-document.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"js-document.js","sourceRoot":"","sources":["../../src/extract/js-document.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * extract-js의 문서 조립 레이어다.\n *\n * 파일별 스캔 결과를 모아 상대 경로 import를 스캔 집합 안에서 해석하고,\n * 바인딩이 확정된 식별자의 멤버 호출을 method-invoke 사실로 바꾼다.\n * 파일시스템을 읽지 않는 순수 조립이다 — 경로 해석·읽기는 cli/가 담당한다.\n */\n\nimport type { BridgeFact, BridgeFactsDocument } from '../exchange/parse.ts';\nimport type { BoundName, ScannedImport } from './js-scan.ts';\nimport { scanJsSource } from './js-scan.ts';\nimport type { JsFileScan } from './js-scan.ts';\n\n/** 스캔 대상이 되는 JS/TS 확장자다. */\nexport const JS_SOURCE_EXTENSIONS = [\n 'ts', 'tsx', 'js', 'jsx', 'mjs', 'cjs', 'mts', 'cts',\n] as const;\n\n/** 조립 입력 — 프로젝트 상대 경로와 UTF-8 텍스트다. */\nexport interface JsSourceFile {\n readonly path: string;\n readonly text: string;\n}\n\n/**\n * 스캔한 JS/TS 파일들을 bridge-facts v1 문서로 조립한다.\n *\n * 사실이 하나도 없으면 계약상 target은 null이다 — 관찰했으나 없음과 관찰\n * 자체가 불가를 구분해 빈 사실이 \"청정\"으로 읽히지 않게 한다.\n */\nexport function createJsFactsDocument(\n files: readonly JsSourceFile[],\n toolVersion: string,\n generatedAt: string,\n project: string,\n): BridgeFactsDocument {\n const scans = new Map<string, JsFileScan>();\n for (const file of files) scans.set(file.path, scanJsSource(file.text));\n\n const facts: BridgeFact[] = [];\n let unattributedRelativeCalls = 0;\n let unattributedPackageCalls = 0;\n\n for (const file of files) {\n const scan = scans.get(file.path)!;\n const resolvedIdents = new Map<string, BoundName>(scan.bindings);\n for (const entry of scan.imports) {\n const bound = resolveImport(scans, file.path, entry);\n if (bound !== undefined) {\n resolvedIdents.set(entry.localName, bound);\n } else if (entry.specifier.startsWith('.')) {\n unattributedRelativeCalls += countMemberCalls(scan, entry.localName);\n } else {\n unattributedPackageCalls += countMemberCalls(scan, entry.localName);\n }\n }\n for (const fact of scan.facts) {\n facts.push(toFact(file.path, fact));\n }\n for (const call of scan.memberCalls) {\n const bound = resolvedIdents.get(call.ident);\n if (bound === undefined) continue;\n const name = 'name' in bound ? bound.name : bound.dynamicExpression;\n facts.push({\n kind: 'method-invoke',\n channel: name,\n method: call.method,\n dynamic: !('name' in bound) || call.dynamicMethod,\n location: locationOf(file.path, call.token),\n });\n }\n }\n\n const sorted = dedupeFacts(facts);\n const limitations = buildLimitations(files.length, scans, {\n unattributedRelativeCalls,\n unattributedPackageCalls,\n });\n\n return {\n format: 'bridge-facts',\n version: 1,\n tool: { name: 'isthmus', version: toolVersion },\n generatedAt,\n platform: 'js',\n target: sorted.length === 0 ? null : 'react-native',\n project,\n facts: sorted,\n limitations,\n };\n}\n\n/** 한 식별자에 대한 멤버 호출 수를 센다(미귀속 한계 계수용). */\nfunction countMemberCalls(scan: JsFileScan, ident: string): number {\n return scan.memberCalls.filter((call) => call.ident === ident).length;\n}\n\n/**\n * `import X from 'spec'`을 스캔 집합 안에서 해석한다.\n *\n * 상대 specifier만 해석한다 — 패키지 specifier는 스캔 집합(node_modules는\n * 제외됨) 밖이라 귀속 근거가 없다. 배럴 재수출은 순환을 피하기 위해 홉 상한\n * 안에서 따라간다.\n */\nfunction resolveImport(\n scans: ReadonlyMap<string, JsFileScan>,\n fromPath: string,\n entry: ScannedImport,\n): BoundName | undefined {\n let specifier = entry.specifier;\n let exportedName: string | 'default' = entry.exportedName;\n let currentDir = dirnameOf(fromPath);\n for (let hop = 0; hop < 4; hop++) {\n if (!specifier.startsWith('.')) return undefined;\n const target = resolveSpecifier(scans, currentDir, specifier);\n if (target === undefined) return undefined;\n const scan = scans.get(target)!;\n const bound = exportedName === 'default'\n ? scan.exports.defaultName\n : scan.exports.named.get(exportedName);\n if (bound !== undefined) return bound;\n const reexport = exportedName === 'default'\n ? scan.exports.reexported.get('default')\n : scan.exports.reexported.get(exportedName);\n if (reexport === undefined) return undefined;\n specifier = reexport.specifier;\n exportedName = reexport.exportedName;\n currentDir = dirnameOf(target);\n }\n return undefined;\n}\n\n/** specifier를 디렉터리 기준으로 정규화해 스캔 집합에 있는 파일을 찾는다. */\nfunction resolveSpecifier(\n scans: ReadonlyMap<string, JsFileScan>,\n dir: string,\n specifier: string,\n): string | undefined {\n const base = normalizePath(dir === '' ? specifier : `${dir}/${specifier}`);\n const candidates = [\n base,\n ...JS_SOURCE_EXTENSIONS.map((ext) => `${base}.${ext}`),\n ...JS_SOURCE_EXTENSIONS.map((ext) => `${base}/index.${ext}`),\n ];\n return candidates.find((candidate) => scans.has(candidate));\n}\n\n/** `.`·`..` 세그먼트를 접은 POSIX 상대 경로다. 스캔 집합 밖 탈출은 해석 불가다. */\nfunction normalizePath(path: string): string {\n const segments: string[] = [];\n for (const segment of path.split('/')) {\n if (segment === '' || segment === '.') continue;\n if (segment === '..') {\n if (segments.length === 0) return '';\n segments.pop();\n continue;\n }\n segments.push(segment);\n }\n return segments.join('/');\n}\n\n/** 프로젝트 상대 경로의 디렉터리 부분이다. */\nfunction dirnameOf(path: string): string {\n const slash = path.lastIndexOf('/');\n return slash === -1 ? '' : path.slice(0, slash);\n}\n\n/** 스캔 사실을 계약 필드 모양으로 바꾼다. */\nfunction toFact(path: string, fact: {\n kind: BridgeFact['kind'];\n channel: string;\n method?: string;\n mechanism?: BridgeFact['mechanism'];\n optional?: BridgeFact['optional'];\n dynamic: boolean;\n token: { line: number; column: number };\n}): BridgeFact {\n return {\n kind: fact.kind,\n channel: fact.channel,\n ...(fact.method === undefined ? {} : { method: fact.method }),\n ...(fact.mechanism === undefined ? {} : { mechanism: fact.mechanism }),\n ...(fact.optional === undefined ? {} : { optional: fact.optional }),\n dynamic: fact.dynamic,\n location: locationOf(path, fact.token),\n };\n}\n\n/** 토큰 위치를 계약 위치로 바꾼다. */\nfunction locationOf(\n path: string,\n token: { line: number; column: number },\n): BridgeFact['location'] {\n return { path, line: token.line, column: token.column };\n}\n\n/** 같은 위치·같은 키의 중복 사실을 한 번만 남기고 결정적으로 정렬한다. */\nfunction dedupeFacts(facts: readonly BridgeFact[]): BridgeFact[] {\n const seen = new Map<string, BridgeFact>();\n for (const fact of facts) {\n const key = JSON.stringify([\n fact.kind,\n fact.channel,\n fact.method ?? null,\n fact.location.path,\n fact.location.line,\n fact.location.column,\n fact.dynamic,\n ]);\n if (!seen.has(key)) seen.set(key, fact);\n }\n return [...seen.values()].sort((left, right) => {\n const byPath = left.location.path.localeCompare(right.location.path);\n if (byPath !== 0) return byPath;\n if (left.location.line !== right.location.line) {\n return left.location.line - right.location.line;\n }\n if (left.location.column !== right.location.column) {\n return left.location.column - right.location.column;\n }\n return (left.channel ?? '').localeCompare(right.channel ?? '');\n });\n}\n\n/** 집계 계수와 고정 범위 설명을 생산자 limitation 문자열로 바꾼다. */\nfunction buildLimitations(\n fileCount: number,\n scans: ReadonlyMap<string, JsFileScan>,\n extra: {\n unattributedRelativeCalls: number;\n unattributedPackageCalls: number;\n },\n): string[] {\n let dynamicModuleNames = 0;\n let dynamicComponentNames = 0;\n let dynamicMethodNames = 0;\n for (const scan of scans.values()) {\n dynamicModuleNames += scan.counts.dynamicModuleNames;\n dynamicComponentNames += scan.counts.dynamicComponentNames;\n dynamicMethodNames += scan.counts.dynamicMethodNames;\n }\n const limitations: string[] = [\n 'js-binding-scope: member calls through bindings that leave the scan set '\n + '(package imports, function results, instance state) are not attributed '\n + 'to a module name',\n ];\n if (dynamicModuleNames > 0) {\n limitations.push(\n `dynamic-module-names: ${dynamicModuleNames} module references use a `\n + 'non-literal name',\n );\n }\n if (dynamicComponentNames > 0) {\n limitations.push(\n `dynamic-component-names: ${dynamicComponentNames} component requires `\n + 'use a non-literal name',\n );\n }\n if (dynamicMethodNames > 0) {\n limitations.push(\n `dynamic-method-names: ${dynamicMethodNames} bridge method calls use a `\n + 'non-literal method name',\n );\n }\n if (extra.unattributedRelativeCalls > 0) {\n limitations.push(\n `unattributed-js-relative-calls: ${extra.unattributedRelativeCalls} `\n + 'member calls on imports that did not resolve inside the scanned '\n + 'file set could not be attributed to a module name',\n );\n }\n if (extra.unattributedPackageCalls > 0) {\n limitations.push(\n `unattributed-js-package-calls: ${extra.unattributedPackageCalls} member `\n + 'calls on package imports could not be attributed to a module name',\n );\n }\n if (fileCount === 0) {\n limitations.push('empty-js-scan: no JS/TS source files were scanned');\n }\n return limitations;\n}\n"]}