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,331 @@
1
+ import { compareStrings } from "../compare.js";
2
+ import { joinBridgeDocuments } from "../join/join.js";
3
+ import { createCheckReport } from "./check-report.js";
4
+ import { encodeSortedJson } from "./sorted-json.js";
5
+ import { joinMessageBridges } from "../join/messages.js";
6
+ /** 잘못된 그래프 투영과 자원 상한 초과를 입력 값 없이 구분한다. */
7
+ export class PreflightGraphError extends Error {
8
+ }
9
+ const MAX_RELATIONS = 1_000_000;
10
+ /** 전체 언어 그래프가 아닌 producer의 영향 숲을 경계에서 연결한다. */
11
+ export function createPreflightReport(context) {
12
+ const joined = joinBridgeDocuments(context.bridges);
13
+ const messages = joinMessageBridges(context.messages ?? [], context.project);
14
+ if (joined.deferred)
15
+ throw new PreflightGraphError('Preflight cannot use mixed-target bridge documents.');
16
+ const nodes = new Map();
17
+ const roots = new Set();
18
+ const consumers = new Map();
19
+ let relationCount = 0;
20
+ const limits = context.limitations.map((message) => ({ code: 'capture-limitation', message }));
21
+ const bindings = new Map(context.bindings.map((binding) => [locationKey('dart', binding.location), binding.symbol]));
22
+ const analyses = [...context.analyses].sort((a, b) => compareStrings(a.id, b.id));
23
+ const dartRoots = new Set(analyses.filter(({ platform }) => platform === 'dart')
24
+ .flatMap(({ roots }) => roots.map(({ id }) => symbolKey('dart', id))));
25
+ function addSymbol(platform, symbol) {
26
+ const key = symbolKey(platform, symbol.id);
27
+ const existing = nodes.get(key);
28
+ if (existing !== undefined && existing.kind === 'symbol') {
29
+ const before = existing.symbol.location;
30
+ const after = symbol.location;
31
+ if (existing.symbol.qualifiedName !== symbol.qualifiedName ||
32
+ (before !== undefined && after !== undefined && (before.path !== after.path ||
33
+ (before.line !== undefined && after.line !== undefined && before.line !== after.line) ||
34
+ (before.column !== undefined && after.column !== undefined && before.column !== after.column)))) {
35
+ throw new PreflightGraphError('Conflicting symbol identities in producer impact inputs.');
36
+ }
37
+ if (before !== undefined && after !== undefined) {
38
+ nodes.set(key, { ...existing, symbol: { ...existing.symbol, location: {
39
+ path: before.path,
40
+ ...(before.line === undefined && after.line === undefined ? {} : { line: before.line ?? after.line }),
41
+ ...(before.column === undefined && after.column === undefined ? {} : { column: before.column ?? after.column }),
42
+ } } });
43
+ return key;
44
+ }
45
+ if (before !== undefined || after === undefined)
46
+ return key;
47
+ nodes.set(key, { ...existing, symbol: { ...existing.symbol, location: after } });
48
+ return key;
49
+ }
50
+ nodes.set(key, { key, kind: 'symbol', platform, symbol });
51
+ return key;
52
+ }
53
+ function link(dependency, consumer, relation) {
54
+ let outgoing = consumers.get(dependency);
55
+ if (outgoing === undefined) {
56
+ outgoing = new Map();
57
+ consumers.set(dependency, outgoing);
58
+ }
59
+ let reasons = outgoing.get(consumer);
60
+ if (reasons === undefined) {
61
+ reasons = new Map();
62
+ outgoing.set(consumer, reasons);
63
+ }
64
+ const key = encodeSortedJson(relation, true);
65
+ if (!reasons.has(key) && ++relationCount > MAX_RELATIONS)
66
+ throw new PreflightGraphError('Preflight relation budget exceeded.');
67
+ reasons.set(key, relation);
68
+ }
69
+ for (const analysis of analyses) {
70
+ for (const symbol of analysis.roots) {
71
+ const key = addSymbol(analysis.platform, symbol);
72
+ if (analysis.trigger === undefined)
73
+ roots.add(key);
74
+ }
75
+ for (const item of analysis.affected)
76
+ addSymbol(analysis.platform, item.symbol);
77
+ for (const item of analysis.affected)
78
+ link(symbolKey(analysis.platform, item.via), symbolKey(analysis.platform, item.symbol.id), { kind: 'language', analysis: analysis.id, relationships: item.relationships });
79
+ }
80
+ function endpointKey(endpoint) {
81
+ if (endpoint.platform !== 'dart' && endpoint.platform !== 'swift' && endpoint.platform !== 'kotlin')
82
+ return undefined;
83
+ if (endpoint.sourceLanguage === 'objective-c')
84
+ return undefined;
85
+ const binding = endpoint.platform === 'dart' ? bindings.get(locationKey('dart', endpoint.location)) : undefined;
86
+ const id = endpoint.symbol?.usr ?? binding?.id;
87
+ if (id === undefined)
88
+ return undefined;
89
+ const key = symbolKey(endpoint.platform, id);
90
+ if (binding !== undefined) {
91
+ if (binding.id !== id)
92
+ throw new PreflightGraphError('Conflicting symbol identities in Dart bridge bindings.');
93
+ // 영향 root는 위치를 생략할 수 있다. query가 관찰한 선언 위치도 병합·대조한다.
94
+ addSymbol(endpoint.platform, binding);
95
+ }
96
+ else if (!nodes.has(key) && endpoint.symbol !== undefined) {
97
+ addSymbol(endpoint.platform, { id, qualifiedName: endpoint.symbol.qualifiedName });
98
+ }
99
+ return nodes.has(key) ? key : undefined;
100
+ }
101
+ const channelRecords = [
102
+ ...joined.matchedChannels,
103
+ ...joined.unregisteredChannelCreations.map((item) => ({ ...item, registrations: [] })),
104
+ ...joined.registrationsWithoutCreations.map((item) => ({ ...item, creations: [] })),
105
+ ];
106
+ const registrations = new Map(channelRecords.map((item) => [JSON.stringify([item.target, item.channel]), item.registrations]));
107
+ const routes = [];
108
+ function boundary(target, channel, method, callers, handlers, wire, transport) {
109
+ const key = transport === undefined ? JSON.stringify(['bridge', target, channel, method ?? null])
110
+ : JSON.stringify(['bridge', target, transport.kind, transport.matching, channel]);
111
+ const subject = { key, kind: 'bridge', target, channel, ...(method === undefined ? {} : { method }),
112
+ ...(transport === undefined ? {} : { transport: transport.kind, matching: transport.matching }) };
113
+ nodes.set(key, subject);
114
+ const route = { subject, callers, receivers: [...handlers, ...wire], callerKeys: [], receiverKeys: [],
115
+ missing: [], imprecise: [] };
116
+ const handlerKind = transport === undefined ? 'bridge-handler'
117
+ : transport.kind === 'event-channel' ? 'bridge-stream-handler' : 'bridge-message-handler';
118
+ const dependencyKind = transport === undefined ? 'bridge-method-dependency'
119
+ : transport.kind === 'event-channel' ? 'bridge-stream-dependency' : 'bridge-message-dependency';
120
+ const receiverOf = new Map();
121
+ for (const endpoint of [...handlers, ...wire]) {
122
+ const receiver = endpointKey(endpoint);
123
+ if (receiver === undefined)
124
+ route.missing.push(endpoint);
125
+ else
126
+ receiverOf.set(endpoint, receiver);
127
+ }
128
+ // 언어 심볼로 귀속 가능한 수신 사실이 전부 완전한 분기 근거를 가질 때만
129
+ // 등록 배선을 직접 선택 게이트로 좁힌다. Objective-C처럼 귀속 불가능한
130
+ // 끝점은 근거를 가질 수 없으므로 판정에서 제외하고 공백 증거로 남는다.
131
+ const scopedHandlers = handlers.filter((endpoint) => receiverOf.has(endpoint) && endpoint.sourceLanguage === undefined);
132
+ const routeScoped = scopedHandlers.length > 0 && scopedHandlers.every((endpoint) => endpoint.handlerScope?.complete === true && endpoint.dependencies !== undefined);
133
+ // 근거 필드를 아예 싣지 않는 예전 생산자는 분기 한계를 신고하지 않는다 —
134
+ // 분기 근거를 지원하는 생산자가 일부만 채웠을 때만 정밀도 공백을 알린다.
135
+ const anyScopeEvidence = handlers.some((endpoint) => endpoint.handlerScope !== undefined);
136
+ for (const endpoint of handlers) {
137
+ const receiver = receiverOf.get(endpoint);
138
+ if (receiver === undefined)
139
+ continue;
140
+ const { handlerScope, dependencies, ...evidence } = endpoint;
141
+ if (handlerScope?.complete === true && dependencies !== undefined) {
142
+ // 공통 등록·디스패치 선언은 직접 선택했을 때 전체 배선을 검토한다. 다른 분기의
143
+ // 호출로 선언에 도달한 경우에는 발생 위치로 구분한 사용 관계만 해당 boundary로 전파한다.
144
+ if (roots.has(receiver)) {
145
+ route.receiverKeys.push(receiver);
146
+ link(receiver, key, { kind: handlerKind, evidence });
147
+ }
148
+ for (const { dispatchTargets, ...dependency } of dependencies) {
149
+ for (const target of [dependency.symbol, ...(dispatchTargets ?? [])]) {
150
+ if (endpoint.platform !== 'swift' && endpoint.platform !== 'kotlin')
151
+ continue;
152
+ const dependencyKey = symbolKey(endpoint.platform, target.usr);
153
+ if (!nodes.has(dependencyKey))
154
+ continue;
155
+ route.receiverKeys.push(dependencyKey);
156
+ link(dependencyKey, key, { kind: dependencyKind, evidence, dependency,
157
+ ...(target === dependency.symbol ? {} : { dispatchTarget: target }) });
158
+ }
159
+ }
160
+ continue;
161
+ }
162
+ if (transport !== undefined || anyScopeEvidence)
163
+ route.imprecise.push(evidence);
164
+ route.receiverKeys.push(receiver);
165
+ link(receiver, key, { kind: handlerKind, evidence });
166
+ }
167
+ for (const endpoint of wire) {
168
+ const receiver = receiverOf.get(endpoint);
169
+ if (receiver === undefined)
170
+ continue;
171
+ // 분기 근거가 완전한 경로의 등록 배선은 직접 선택했을 때만 경계를 연다.
172
+ if (routeScoped && !roots.has(receiver))
173
+ continue;
174
+ route.receiverKeys.push(receiver);
175
+ link(receiver, key, { kind: 'bridge-registration', evidence: endpoint });
176
+ }
177
+ for (const endpoint of callers) {
178
+ const caller = endpointKey(endpoint);
179
+ if (caller === undefined) {
180
+ route.missing.push(endpoint);
181
+ continue;
182
+ }
183
+ route.callerKeys.push(caller);
184
+ link(key, caller, { kind: transport === undefined
185
+ ? (method === undefined ? 'bridge-creation' : 'bridge-invocation')
186
+ : transport.kind === 'event-channel' ? 'bridge-stream-listen' : 'bridge-message-send', evidence: endpoint });
187
+ }
188
+ routes.push(route);
189
+ }
190
+ for (const item of channelRecords)
191
+ boundary(item.target, item.channel, undefined, item.creations, [], item.registrations);
192
+ for (const item of [
193
+ ...joined.matchedMethods,
194
+ ...joined.unhandledInvocations.map((value) => ({ ...value, handlers: [] })),
195
+ ...joined.handlersWithoutInvocations.map((value) => ({ ...value, invocations: [] })),
196
+ ])
197
+ boundary(item.target, item.channel, item.method, item.invocations, item.handlers, registrations.get(JSON.stringify([item.target, item.channel])) ?? []);
198
+ for (const route of messages.routes)
199
+ boundary('flutter', route.channel, undefined, route.senders, route.handlers, [], { kind: route.transport, matching: route.matching });
200
+ const depths = new Map([...roots].map((key) => [key, 0]));
201
+ const visits = new Map();
202
+ const queue = [...roots].sort(compareStrings);
203
+ for (let head = 0; head < queue.length; head++) {
204
+ const dependency = queue[head];
205
+ for (const [key, reasons] of [...(consumers.get(dependency) ?? [])].sort(([a], [b]) => compareStrings(a, b))) {
206
+ if (depths.has(key))
207
+ continue;
208
+ const subject = nodes.get(key);
209
+ if (subject === undefined)
210
+ throw new PreflightGraphError('Producer impact refers to an unknown symbol.');
211
+ const depth = depths.get(dependency) + 1;
212
+ depths.set(key, depth);
213
+ visits.set(key, { subject, depth, via: dependency,
214
+ relations: [...reasons.entries()].sort(([a], [b]) => compareStrings(a, b)).map(([, reason]) => reason) });
215
+ queue.push(key);
216
+ }
217
+ }
218
+ const rootSubjects = [...roots].sort(compareStrings).map((key) => nodes.get(key));
219
+ const affected = [...visits.values()].sort((a, b) => a.depth - b.depth || compareStrings(a.subject.key, b.subject.key));
220
+ const reachedFiles = new Set([...rootSubjects, ...affected.map(({ subject }) => subject)]
221
+ .flatMap((item) => item.kind === 'symbol' && item.symbol.location !== undefined ? [item.symbol.location.path] : []));
222
+ for (const selection of Object.values(context.selection))
223
+ for (const path of selection.files)
224
+ reachedFiles.add(path);
225
+ const relevant = routes.filter((route) => depths.has(route.subject.key) || route.callerKeys.some((key) => depths.has(key)) ||
226
+ route.receiverKeys.some((key) => depths.has(key)) || route.missing.some(({ location }) => reachedFiles.has(location.path)));
227
+ for (const route of relevant) {
228
+ if (route.subject.transport === 'basic-message-channel' || route.subject.transport === 'event-channel') {
229
+ const stream = route.subject.transport === 'event-channel';
230
+ for (const evidence of route.imprecise)
231
+ limits.push({ code: stream ? 'unresolved-stream-handler-scope' : 'unresolved-message-handler-scope',
232
+ message: 'Handler dependencies could not be fully attributed in the observed index; enclosing declaration impact remains conservative.', evidence });
233
+ if (route.subject.matching === 'prefix')
234
+ limits.push({ code: stream ? 'dynamic-stream-address' : 'dynamic-message-address',
235
+ message: stream
236
+ ? 'Event channel prefixes describe possible routes; suffix and instance wiring are not resolved.'
237
+ : 'Message channel prefixes describe possible routes; suffix and instance wiring are not resolved.' });
238
+ if (route.callers.length === 0 || route.receivers.length === 0)
239
+ limits.push({ code: stream ? 'unmatched-stream-boundary' : 'unmatched-message-boundary',
240
+ message: stream
241
+ ? 'A related stream boundary has no observed counterpart in the EventChannel extraction scope.'
242
+ : 'A related message boundary has no observed counterpart in the Basic extraction scope.' });
243
+ }
244
+ else if (route.subject.method !== undefined) {
245
+ for (const evidence of route.imprecise)
246
+ limits.push({ code: 'unresolved-method-handler-scope',
247
+ message: 'Method handler branches could not be fully attributed in the observed index; enclosing declaration impact remains conservative.', evidence });
248
+ }
249
+ for (const evidence of route.missing)
250
+ limits.push({ code: 'unresolved-bridge-binding',
251
+ message: 'A bridge endpoint has no verified language symbol binding.', evidence });
252
+ if (depths.has(route.subject.key))
253
+ for (const key of route.callerKeys) {
254
+ if (!dartRoots.has(key)) {
255
+ const subject = nodes.get(key);
256
+ const location = subject?.kind === 'symbol' ? subject.symbol.location : undefined;
257
+ limits.push({ code: 'missing-language-continuation', message: 'Dart consumer impact was not supplied for a reached bridge caller.',
258
+ ...(location?.line !== undefined && location.column !== undefined
259
+ ? { evidence: { platform: 'dart', location: { path: location.path, line: location.line, column: location.column } } } : {}) });
260
+ }
261
+ }
262
+ }
263
+ for (const analysis of analyses) {
264
+ if (analysis.trigger !== undefined && !depths.has(symbolKey(analysis.platform, analysis.trigger)))
265
+ continue;
266
+ for (const message of analysis.limitations)
267
+ limits.push({ code: 'producer-limitation', analysis: analysis.id, message });
268
+ if (analysis.truncated)
269
+ limits.push({ code: 'producer-truncated', analysis: analysis.id, message: 'The producer impact traversal or output was truncated.' });
270
+ if (analysis.trigger === undefined && analysis.roots.length === 0)
271
+ limits.push({ code: 'unobserved-selection', analysis: analysis.id,
272
+ message: 'The producer did not resolve a requested change selection.' });
273
+ }
274
+ for (const document of context.bridges)
275
+ for (const fact of document.facts) {
276
+ if ((fact.dynamic || fact.channel === null) && reachedFiles.has(fact.location.path))
277
+ limits.push({
278
+ code: 'unresolved-dynamic-boundary', message: 'A related dynamic or unattributed bridge fact could not be connected.',
279
+ evidence: { platform: document.platform, location: fact.location },
280
+ });
281
+ }
282
+ for (const evidence of messages.unresolved)
283
+ if (reachedFiles.has(evidence.location.path))
284
+ limits.push({
285
+ code: 'unresolved-message-boundary', message: 'A related message address could not be resolved to a literal or proven prefix.', evidence,
286
+ });
287
+ if (relevant.some(({ subject }) => subject.transport === undefined)) {
288
+ for (const limit of joined.limitations)
289
+ limits.push({ code: 'bridge-limitation', message: limit.message });
290
+ }
291
+ if (relevant.some(({ subject }) => subject.transport === 'basic-message-channel' || subject.transport === 'event-channel')) {
292
+ for (const limit of messages.limitations)
293
+ limits.push({ code: 'message-producer-limitation', message: limit.message });
294
+ }
295
+ const uniqueLimits = [...new Map(limits.map((item) => [encodeSortedJson(item, true), item])).entries()]
296
+ .sort(([a], [b]) => compareStrings(a, b)).map(([, item]) => item);
297
+ const routeKeys = new Set(relevant.filter(({ subject }) => subject.transport === undefined)
298
+ .map(({ subject }) => JSON.stringify([subject.target, subject.channel, subject.method ?? null])));
299
+ const issues = createCheckReport(joined).issues.filter((issue) => routeKeys.has(JSON.stringify([issue.target, issue.channel, issue.method ?? null])));
300
+ for (const route of relevant)
301
+ for (const endpoint of [...route.callers, ...route.receivers])
302
+ reachedFiles.add(endpoint.location.path);
303
+ const reviewFiles = [...reachedFiles].sort(compareStrings);
304
+ const hasSelection = Object.keys(context.selection).length > 0;
305
+ return {
306
+ format: 'isthmus-preflight', version: 1, project: context.project, revision: context.revision,
307
+ scope: 'cross-language-impact', complete: false,
308
+ status: !hasSelection ? 'noChanges' : roots.size === 0 ? 'unobserved' : 'observed',
309
+ selection: context.selection, roots: rootSubjects, affected,
310
+ boundaries: relevant.sort((a, b) => compareStrings(a.subject.key, b.subject.key)).map((route) => ({
311
+ subject: route.subject, relationship: depths.has(route.subject.key) ? 'consumer' : 'dependency', callers: route.callers, receivers: route.receivers,
312
+ })), reviewFiles, issues, limitations: uniqueLimits, bridgeLimitations: joined.limitations,
313
+ ...(context.messages === undefined ? {} : { messageLimitations: messages.limitations }),
314
+ producers: analyses.map(({ id, platform, tool, truncated }) => ({ analysis: id, platform, tool, truncated })),
315
+ summary: { selectedSymbols: roots.size, affectedSymbols: affected.filter(({ subject }) => subject.kind === 'symbol').length,
316
+ bridgeBoundaries: affected.filter(({ subject }) => subject.kind === 'bridge').length, reviewFiles: reviewFiles.length,
317
+ errors: issues.filter(({ severity }) => severity === 'error').length,
318
+ warnings: issues.filter(({ severity }) => severity === 'warning').length, evidenceGaps: uniqueLimits.length },
319
+ };
320
+ }
321
+ /** 관찰된 영향과 "영향 없음"의 보증을 구분하며 관련 공백은 CI 검토를 요구한다. */
322
+ export function hasPreflightBlockers(report) {
323
+ return report.status === 'unobserved' || report.summary.errors > 0 || report.summary.evidenceGaps > 0;
324
+ }
325
+ /** 언어 ID를 다른 언어의 같은 문자열과 혼동하지 않는 키다. */
326
+ function symbolKey(platform, id) { return JSON.stringify([platform, id]); }
327
+ /** 호출 위치와 선언 위치의 의미를 바꾸지 않고 정확한 fact binding만 찾는다. */
328
+ function locationKey(platform, location) {
329
+ return JSON.stringify([platform, location.path, location.line, location.column]);
330
+ }
331
+ //# sourceMappingURL=preflight.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preflight.js","sourceRoot":"","sources":["../../src/report/preflight.ts"],"names":[],"mappings":"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","sourcesContent":["import { compareStrings } from '../compare.ts';\nimport type { ImpactSymbol, PreflightContext } from '../exchange/preflight-context.ts';\nimport type { BridgeEndpoint, JoinLimitation } from '../join/join.ts';\nimport { joinBridgeDocuments } from '../join/join.ts';\nimport type { BridgeTarget } from '../exchange/parse.ts';\nimport { createCheckReport } from './check-report.ts';\nimport type { CheckIssue } from './check-report.ts';\nimport { encodeSortedJson } from './sorted-json.ts';\nimport type { PreflightRuntimeReport } from './preflight-runtime.ts';\nimport { joinMessageBridges } from '../join/messages.ts';\nimport type { MessageEndpoint } from '../join/messages.ts';\nimport type { BridgeHandlerDependency, BridgeMessageTransport } from '../exchange/messages.ts';\nimport type { BridgeSymbol } from '../exchange/parse.ts';\n\ntype Language = 'dart' | 'swift' | 'kotlin';\n\n/** 언어 심볼과 경계의 키 공간을 분리한다. 원래 producer ID는 symbol.id에 보존한다. */\nexport type PreflightSubject = {\n readonly key: string; readonly kind: 'symbol'; readonly platform: Language; readonly symbol: ImpactSymbol;\n} | {\n readonly key: string; readonly kind: 'bridge'; readonly target: BridgeTarget;\n readonly channel: string; readonly method?: string;\n readonly transport?: BridgeMessageTransport;\n readonly matching?: 'literal' | 'prefix';\n};\n\n/** 실제 producer 사용 관계와 문자열 브리지 관계를 서로 다른 근거로 표시한다. */\nexport type PreflightRelation = {\n readonly kind: 'language'; readonly analysis: string; readonly relationships: readonly string[];\n} | {\n readonly kind: 'bridge-message-dependency' | 'bridge-stream-dependency' | 'bridge-method-dependency'; readonly evidence: BridgeEndpoint;\n readonly dependency: Omit<BridgeHandlerDependency, 'dispatchTargets'>;\n readonly dispatchTarget?: BridgeSymbol;\n} | {\n readonly kind: 'bridge-handler' | 'bridge-registration' | 'bridge-invocation' | 'bridge-creation'\n | 'bridge-message-send' | 'bridge-message-handler' | 'bridge-stream-listen' | 'bridge-stream-handler';\n readonly evidence: BridgeEndpoint;\n};\n\n/** 가장 가까운 변경 씨앗까지의 선행 정점을 공유해 긴 경로 복사를 피한다. */\nexport interface PreflightAffected {\n readonly subject: PreflightSubject;\n readonly depth: number;\n readonly via: string;\n readonly relations: readonly PreflightRelation[];\n}\n\n/** 근거를 연결할 수 없는 부분과 발생 위치다. */\nexport interface PreflightLimitation {\n readonly code: string;\n readonly message: string;\n readonly analysis?: string;\n readonly evidence?: BridgeEndpoint;\n}\n\n/** 언어별 영향 투영과 브리지 관계를 연결한 사전 검토 문서다. */\nexport interface PreflightReport {\n readonly format: 'isthmus-preflight'; readonly version: 1;\n readonly project: string; readonly revision: string;\n readonly scope: 'cross-language-impact'; readonly complete: false;\n readonly status: 'observed' | 'unobserved' | 'noChanges';\n readonly selection: PreflightContext['selection'];\n readonly roots: readonly PreflightSubject[];\n readonly affected: readonly PreflightAffected[];\n readonly boundaries: readonly {\n subject: Extract<PreflightSubject, { kind: 'bridge' }>;\n relationship: 'consumer' | 'dependency';\n callers: readonly BridgeEndpoint[];\n receivers: readonly BridgeEndpoint[];\n }[];\n readonly reviewFiles: readonly string[];\n readonly issues: readonly CheckIssue[];\n readonly limitations: readonly PreflightLimitation[];\n readonly bridgeLimitations: readonly JoinLimitation[];\n readonly messageLimitations?: readonly JoinLimitation[];\n readonly runtime?: PreflightRuntimeReport;\n readonly producers: ReadonlyArray<{ analysis: string; platform: Language; tool: { name: string; version: string }; truncated: boolean }>;\n readonly summary: {\n selectedSymbols: number; affectedSymbols: number; bridgeBoundaries: number;\n reviewFiles: number; errors: number; warnings: number; evidenceGaps: number;\n };\n}\n\n/** 잘못된 그래프 투영과 자원 상한 초과를 입력 값 없이 구분한다. */\nexport class PreflightGraphError extends Error {}\n\nconst MAX_RELATIONS = 1_000_000;\n\n/** 전체 언어 그래프가 아닌 producer의 영향 숲을 경계에서 연결한다. */\nexport function createPreflightReport(context: PreflightContext): PreflightReport {\n const joined = joinBridgeDocuments(context.bridges);\n const messages = joinMessageBridges(context.messages ?? [], context.project);\n if (joined.deferred) throw new PreflightGraphError('Preflight cannot use mixed-target bridge documents.');\n const nodes = new Map<string, PreflightSubject>();\n const roots = new Set<string>();\n const consumers = new Map<string, Map<string, Map<string, PreflightRelation>>>();\n let relationCount = 0;\n const limits: PreflightLimitation[] = context.limitations.map((message) => ({ code: 'capture-limitation', message }));\n const bindings = new Map(context.bindings.map((binding) => [locationKey('dart', binding.location), binding.symbol]));\n const analyses = [...context.analyses].sort((a, b) => compareStrings(a.id, b.id));\n const dartRoots = new Set(analyses.filter(({ platform }) => platform === 'dart')\n .flatMap(({ roots }) => roots.map(({ id }) => symbolKey('dart', id))));\n\n function addSymbol(platform: Language, symbol: ImpactSymbol): string {\n const key = symbolKey(platform, symbol.id);\n const existing = nodes.get(key);\n if (existing !== undefined && existing.kind === 'symbol') {\n const before = existing.symbol.location;\n const after = symbol.location;\n if (existing.symbol.qualifiedName !== symbol.qualifiedName ||\n (before !== undefined && after !== undefined && (before.path !== after.path ||\n (before.line !== undefined && after.line !== undefined && before.line !== after.line) ||\n (before.column !== undefined && after.column !== undefined && before.column !== after.column)))) {\n throw new PreflightGraphError('Conflicting symbol identities in producer impact inputs.');\n }\n if (before !== undefined && after !== undefined) {\n nodes.set(key, { ...existing, symbol: { ...existing.symbol, location: {\n path: before.path,\n ...(before.line === undefined && after.line === undefined ? {} : { line: before.line ?? after.line }),\n ...(before.column === undefined && after.column === undefined ? {} : { column: before.column ?? after.column }),\n } } });\n return key;\n }\n if (before !== undefined || after === undefined) return key;\n nodes.set(key, { ...existing, symbol: { ...existing.symbol, location: after } });\n return key;\n }\n nodes.set(key, { key, kind: 'symbol', platform, symbol });\n return key;\n }\n\n function link(dependency: string, consumer: string, relation: PreflightRelation): void {\n let outgoing = consumers.get(dependency);\n if (outgoing === undefined) { outgoing = new Map(); consumers.set(dependency, outgoing); }\n let reasons = outgoing.get(consumer);\n if (reasons === undefined) { reasons = new Map(); outgoing.set(consumer, reasons); }\n const key = encodeSortedJson(relation, true);\n if (!reasons.has(key) && ++relationCount > MAX_RELATIONS) throw new PreflightGraphError('Preflight relation budget exceeded.');\n reasons.set(key, relation);\n }\n\n for (const analysis of analyses) {\n for (const symbol of analysis.roots) {\n const key = addSymbol(analysis.platform, symbol);\n if (analysis.trigger === undefined) roots.add(key);\n }\n for (const item of analysis.affected) addSymbol(analysis.platform, item.symbol);\n for (const item of analysis.affected) link(symbolKey(analysis.platform, item.via), symbolKey(analysis.platform, item.symbol.id),\n { kind: 'language', analysis: analysis.id, relationships: item.relationships });\n }\n\n function endpointKey(endpoint: BridgeEndpoint): string | undefined {\n if (endpoint.platform !== 'dart' && endpoint.platform !== 'swift' && endpoint.platform !== 'kotlin') return undefined;\n if (endpoint.sourceLanguage === 'objective-c') return undefined;\n const binding = endpoint.platform === 'dart' ? bindings.get(locationKey('dart', endpoint.location)) : undefined;\n const id = endpoint.symbol?.usr ?? binding?.id;\n if (id === undefined) return undefined;\n const key = symbolKey(endpoint.platform, id);\n if (binding !== undefined) {\n if (binding.id !== id) throw new PreflightGraphError('Conflicting symbol identities in Dart bridge bindings.');\n // 영향 root는 위치를 생략할 수 있다. query가 관찰한 선언 위치도 병합·대조한다.\n addSymbol(endpoint.platform, binding);\n } else if (!nodes.has(key) && endpoint.symbol !== undefined) {\n addSymbol(endpoint.platform, { id, qualifiedName: endpoint.symbol.qualifiedName });\n }\n return nodes.has(key) ? key : undefined;\n }\n\n const channelRecords = [\n ...joined.matchedChannels,\n ...joined.unregisteredChannelCreations.map((item) => ({ ...item, registrations: [] })),\n ...joined.registrationsWithoutCreations.map((item) => ({ ...item, creations: [] })),\n ];\n const registrations = new Map(channelRecords.map((item) => [JSON.stringify([item.target, item.channel]), item.registrations]));\n const routes: Array<{\n subject: Extract<PreflightSubject, { kind: 'bridge' }>;\n callers: readonly BridgeEndpoint[]; receivers: readonly BridgeEndpoint[];\n callerKeys: string[]; receiverKeys: string[]; missing: BridgeEndpoint[]; imprecise: BridgeEndpoint[];\n }> = [];\n\n function boundary(target: BridgeTarget, channel: string, method: string | undefined,\n callers: readonly BridgeEndpoint[], handlers: readonly MessageEndpoint[], wire: readonly BridgeEndpoint[],\n transport?: { kind: BridgeMessageTransport; matching: 'literal' | 'prefix' }): void {\n const key = transport === undefined ? JSON.stringify(['bridge', target, channel, method ?? null])\n : JSON.stringify(['bridge', target, transport.kind, transport.matching, channel]);\n const subject = { key, kind: 'bridge' as const, target, channel, ...(method === undefined ? {} : { method }),\n ...(transport === undefined ? {} : { transport: transport.kind, matching: transport.matching }) };\n nodes.set(key, subject);\n const route = { subject, callers, receivers: [...handlers, ...wire], callerKeys: [] as string[], receiverKeys: [] as string[],\n missing: [] as BridgeEndpoint[], imprecise: [] as BridgeEndpoint[] };\n const handlerKind = transport === undefined ? 'bridge-handler'\n : transport.kind === 'event-channel' ? 'bridge-stream-handler' : 'bridge-message-handler';\n const dependencyKind = transport === undefined ? 'bridge-method-dependency'\n : transport.kind === 'event-channel' ? 'bridge-stream-dependency' : 'bridge-message-dependency';\n const receiverOf = new Map<BridgeEndpoint, string>();\n for (const endpoint of [...handlers, ...wire]) {\n const receiver = endpointKey(endpoint);\n if (receiver === undefined) route.missing.push(endpoint); else receiverOf.set(endpoint, receiver);\n }\n // 언어 심볼로 귀속 가능한 수신 사실이 전부 완전한 분기 근거를 가질 때만\n // 등록 배선을 직접 선택 게이트로 좁힌다. Objective-C처럼 귀속 불가능한\n // 끝점은 근거를 가질 수 없으므로 판정에서 제외하고 공백 증거로 남는다.\n const scopedHandlers = handlers.filter((endpoint) => receiverOf.has(endpoint) && endpoint.sourceLanguage === undefined);\n const routeScoped = scopedHandlers.length > 0 && scopedHandlers.every(\n (endpoint) => endpoint.handlerScope?.complete === true && endpoint.dependencies !== undefined);\n // 근거 필드를 아예 싣지 않는 예전 생산자는 분기 한계를 신고하지 않는다 —\n // 분기 근거를 지원하는 생산자가 일부만 채웠을 때만 정밀도 공백을 알린다.\n const anyScopeEvidence = handlers.some((endpoint) => endpoint.handlerScope !== undefined);\n for (const endpoint of handlers) {\n const receiver = receiverOf.get(endpoint);\n if (receiver === undefined) continue;\n const { handlerScope, dependencies, ...evidence } = endpoint as MessageEndpoint;\n if (handlerScope?.complete === true && dependencies !== undefined) {\n // 공통 등록·디스패치 선언은 직접 선택했을 때 전체 배선을 검토한다. 다른 분기의\n // 호출로 선언에 도달한 경우에는 발생 위치로 구분한 사용 관계만 해당 boundary로 전파한다.\n if (roots.has(receiver)) {\n route.receiverKeys.push(receiver);\n link(receiver, key, { kind: handlerKind, evidence });\n }\n for (const { dispatchTargets, ...dependency } of dependencies) {\n for (const target of [dependency.symbol, ...(dispatchTargets ?? [])]) {\n if (endpoint.platform !== 'swift' && endpoint.platform !== 'kotlin') continue;\n const dependencyKey = symbolKey(endpoint.platform, target.usr!);\n if (!nodes.has(dependencyKey)) continue;\n route.receiverKeys.push(dependencyKey);\n link(dependencyKey, key, { kind: dependencyKind, evidence, dependency,\n ...(target === dependency.symbol ? {} : { dispatchTarget: target }) });\n }\n }\n continue;\n }\n if (transport !== undefined || anyScopeEvidence) route.imprecise.push(evidence);\n route.receiverKeys.push(receiver);\n link(receiver, key, { kind: handlerKind, evidence });\n }\n for (const endpoint of wire) {\n const receiver = receiverOf.get(endpoint);\n if (receiver === undefined) continue;\n // 분기 근거가 완전한 경로의 등록 배선은 직접 선택했을 때만 경계를 연다.\n if (routeScoped && !roots.has(receiver)) continue;\n route.receiverKeys.push(receiver);\n link(receiver, key, { kind: 'bridge-registration', evidence: endpoint });\n }\n for (const endpoint of callers) {\n const caller = endpointKey(endpoint);\n if (caller === undefined) { route.missing.push(endpoint); continue; }\n route.callerKeys.push(caller);\n link(key, caller, { kind: transport === undefined\n ? (method === undefined ? 'bridge-creation' : 'bridge-invocation')\n : transport.kind === 'event-channel' ? 'bridge-stream-listen' : 'bridge-message-send', evidence: endpoint });\n }\n routes.push(route);\n }\n\n for (const item of channelRecords) boundary(item.target, item.channel, undefined, item.creations, [], item.registrations);\n for (const item of [\n ...joined.matchedMethods,\n ...joined.unhandledInvocations.map((value) => ({ ...value, handlers: [] })),\n ...joined.handlersWithoutInvocations.map((value) => ({ ...value, invocations: [] })),\n ]) boundary(item.target, item.channel, item.method, item.invocations, item.handlers,\n registrations.get(JSON.stringify([item.target, item.channel])) ?? []);\n for (const route of messages.routes) boundary('flutter', route.channel, undefined, route.senders, route.handlers, [],\n { kind: route.transport, matching: route.matching });\n\n const depths = new Map([...roots].map((key) => [key, 0]));\n const visits = new Map<string, PreflightAffected>();\n const queue = [...roots].sort(compareStrings);\n for (let head = 0; head < queue.length; head++) {\n const dependency = queue[head]!;\n for (const [key, reasons] of [...(consumers.get(dependency) ?? [])].sort(([a], [b]) => compareStrings(a, b))) {\n if (depths.has(key)) continue;\n const subject = nodes.get(key);\n if (subject === undefined) throw new PreflightGraphError('Producer impact refers to an unknown symbol.');\n const depth = depths.get(dependency)! + 1;\n depths.set(key, depth);\n visits.set(key, { subject, depth, via: dependency,\n relations: [...reasons.entries()].sort(([a], [b]) => compareStrings(a, b)).map(([, reason]) => reason) });\n queue.push(key);\n }\n }\n const rootSubjects = [...roots].sort(compareStrings).map((key) => nodes.get(key)!);\n const affected = [...visits.values()].sort((a, b) => a.depth - b.depth || compareStrings(a.subject.key, b.subject.key));\n const reachedFiles = new Set([...rootSubjects, ...affected.map(({ subject }) => subject)]\n .flatMap((item) => item.kind === 'symbol' && item.symbol.location !== undefined ? [item.symbol.location.path] : []));\n for (const selection of Object.values(context.selection)) for (const path of selection.files) reachedFiles.add(path);\n\n const relevant = routes.filter((route) => depths.has(route.subject.key) || route.callerKeys.some((key) => depths.has(key)) ||\n route.receiverKeys.some((key) => depths.has(key)) || route.missing.some(({ location }) => reachedFiles.has(location.path)));\n for (const route of relevant) {\n if (route.subject.transport === 'basic-message-channel' || route.subject.transport === 'event-channel') {\n const stream = route.subject.transport === 'event-channel';\n for (const evidence of route.imprecise) limits.push({ code: stream ? 'unresolved-stream-handler-scope' : 'unresolved-message-handler-scope',\n message: 'Handler dependencies could not be fully attributed in the observed index; enclosing declaration impact remains conservative.', evidence });\n if (route.subject.matching === 'prefix') limits.push({ code: stream ? 'dynamic-stream-address' : 'dynamic-message-address',\n message: stream\n ? 'Event channel prefixes describe possible routes; suffix and instance wiring are not resolved.'\n : 'Message channel prefixes describe possible routes; suffix and instance wiring are not resolved.' });\n if (route.callers.length === 0 || route.receivers.length === 0) limits.push({ code: stream ? 'unmatched-stream-boundary' : 'unmatched-message-boundary',\n message: stream\n ? 'A related stream boundary has no observed counterpart in the EventChannel extraction scope.'\n : 'A related message boundary has no observed counterpart in the Basic extraction scope.' });\n } else if (route.subject.method !== undefined) {\n for (const evidence of route.imprecise) limits.push({ code: 'unresolved-method-handler-scope',\n message: 'Method handler branches could not be fully attributed in the observed index; enclosing declaration impact remains conservative.', evidence });\n }\n for (const evidence of route.missing) limits.push({ code: 'unresolved-bridge-binding',\n message: 'A bridge endpoint has no verified language symbol binding.', evidence });\n if (depths.has(route.subject.key)) for (const key of route.callerKeys) {\n if (!dartRoots.has(key)) {\n const subject = nodes.get(key);\n const location = subject?.kind === 'symbol' ? subject.symbol.location : undefined;\n limits.push({ code: 'missing-language-continuation', message: 'Dart consumer impact was not supplied for a reached bridge caller.',\n ...(location?.line !== undefined && location.column !== undefined\n ? { evidence: { platform: 'dart' as const, location: { path: location.path, line: location.line, column: location.column } } } : {}) });\n }\n }\n }\n for (const analysis of analyses) {\n if (analysis.trigger !== undefined && !depths.has(symbolKey(analysis.platform, analysis.trigger))) continue;\n for (const message of analysis.limitations) limits.push({ code: 'producer-limitation', analysis: analysis.id, message });\n if (analysis.truncated) limits.push({ code: 'producer-truncated', analysis: analysis.id, message: 'The producer impact traversal or output was truncated.' });\n if (analysis.trigger === undefined && analysis.roots.length === 0) limits.push({ code: 'unobserved-selection', analysis: analysis.id,\n message: 'The producer did not resolve a requested change selection.' });\n }\n for (const document of context.bridges) for (const fact of document.facts) {\n if ((fact.dynamic || fact.channel === null) && reachedFiles.has(fact.location.path)) limits.push({\n code: 'unresolved-dynamic-boundary', message: 'A related dynamic or unattributed bridge fact could not be connected.',\n evidence: { platform: document.platform, location: fact.location },\n });\n }\n for (const evidence of messages.unresolved) if (reachedFiles.has(evidence.location.path)) limits.push({\n code: 'unresolved-message-boundary', message: 'A related message address could not be resolved to a literal or proven prefix.', evidence,\n });\n if (relevant.some(({ subject }) => subject.transport === undefined)) {\n for (const limit of joined.limitations) limits.push({ code: 'bridge-limitation', message: limit.message });\n }\n if (relevant.some(({ subject }) => subject.transport === 'basic-message-channel' || subject.transport === 'event-channel')) {\n for (const limit of messages.limitations) limits.push({ code: 'message-producer-limitation', message: limit.message });\n }\n const uniqueLimits = [...new Map(limits.map((item) => [encodeSortedJson(item, true), item])).entries()]\n .sort(([a], [b]) => compareStrings(a, b)).map(([, item]) => item);\n const routeKeys = new Set(relevant.filter(({ subject }) => subject.transport === undefined)\n .map(({ subject }) => JSON.stringify([subject.target, subject.channel, subject.method ?? null])));\n const issues = createCheckReport(joined).issues.filter((issue) => routeKeys.has(JSON.stringify([issue.target, issue.channel, issue.method ?? null])));\n for (const route of relevant) for (const endpoint of [...route.callers, ...route.receivers]) reachedFiles.add(endpoint.location.path);\n const reviewFiles = [...reachedFiles].sort(compareStrings);\n const hasSelection = Object.keys(context.selection).length > 0;\n return {\n format: 'isthmus-preflight', version: 1, project: context.project, revision: context.revision,\n scope: 'cross-language-impact', complete: false,\n status: !hasSelection ? 'noChanges' : roots.size === 0 ? 'unobserved' : 'observed',\n selection: context.selection, roots: rootSubjects, affected,\n boundaries: relevant.sort((a, b) => compareStrings(a.subject.key, b.subject.key)).map((route) => ({\n subject: route.subject, relationship: depths.has(route.subject.key) ? 'consumer' : 'dependency', callers: route.callers, receivers: route.receivers,\n })), reviewFiles, issues, limitations: uniqueLimits, bridgeLimitations: joined.limitations,\n ...(context.messages === undefined ? {} : { messageLimitations: messages.limitations }),\n producers: analyses.map(({ id, platform, tool, truncated }) => ({ analysis: id, platform, tool, truncated })),\n summary: { selectedSymbols: roots.size, affectedSymbols: affected.filter(({ subject }) => subject.kind === 'symbol').length,\n bridgeBoundaries: affected.filter(({ subject }) => subject.kind === 'bridge').length, reviewFiles: reviewFiles.length,\n errors: issues.filter(({ severity }) => severity === 'error').length,\n warnings: issues.filter(({ severity }) => severity === 'warning').length, evidenceGaps: uniqueLimits.length },\n };\n}\n\n/** 관찰된 영향과 \"영향 없음\"의 보증을 구분하며 관련 공백은 CI 검토를 요구한다. */\nexport function hasPreflightBlockers(report: PreflightReport): boolean {\n return report.status === 'unobserved' || report.summary.errors > 0 || report.summary.evidenceGaps > 0;\n}\n\n/** 언어 ID를 다른 언어의 같은 문자열과 혼동하지 않는 키다. */\nfunction symbolKey(platform: Language, id: string): string { return JSON.stringify([platform, id]); }\n\n/** 호출 위치와 선언 위치의 의미를 바꾸지 않고 정확한 fact binding만 찾는다. */\nfunction locationKey(platform: string, location: { path: string; line?: number; column?: number }): string {\n return JSON.stringify([platform, location.path, location.line, location.column]);\n}\n"]}
@@ -1,9 +1,10 @@
1
1
  import type { BridgeEndpoint, BridgeJoinResult, JoinLimitation } from '../join/join.ts';
2
- /** query가 식별한 채널 또는 메서드 키다. */
2
+ import type { MessageBridgeJoin } from '../join/messages.ts';
3
+ /** query가 식별한 채널·메서드·모듈·컴포넌트·메시지·스트림 키다. */
3
4
  export interface BridgeQuerySubject {
4
5
  readonly name: string;
5
6
  readonly qualifiedName: string;
6
- readonly kind: 'channel' | 'method';
7
+ readonly kind: 'channel' | 'method' | 'module' | 'component' | 'message' | 'stream';
7
8
  }
8
9
  /** 한 브리지 키에서 본 호출 측과 수신 측 증거다. */
9
10
  export interface BridgeQueryResult {
@@ -24,5 +25,11 @@ export interface BridgeQueryDocument {
24
25
  }
25
26
  /** query 문서를 결정적인 JSON 문자열로 인코딩한다. */
26
27
  export declare function encodeBridgeQuery(document: BridgeQueryDocument): string;
27
- /** 조인된 브리지에서 채널 또는 메서드 문자열을 질의한다. */
28
- export declare function createBridgeQuery(joined: BridgeJoinResult, requested: string): BridgeQueryDocument;
28
+ /**
29
+ * 조인된 브리지에서 채널 또는 메서드 문자열을 질의한다.
30
+ *
31
+ * `messages`를 주면 BasicMessageChannel·EventChannel 경계도 `message`·`stream`
32
+ * 주체로 질의할 수 있다. v1 채널과 이름이 같아도 qualifiedName의 kind 세그먼트로
33
+ * 구분된다.
34
+ */
35
+ export declare function createBridgeQuery(joined: BridgeJoinResult, requested: string, messages?: MessageBridgeJoin): BridgeQueryDocument;
@@ -1,55 +1,88 @@
1
1
  import { compareStrings } from "../compare.js";
2
- import { isBridgeJoinDeferred } from "../join/join.js";
2
+ import { compareLimitations, isBridgeJoinDeferred } from "../join/join.js";
3
3
  import { encodeSortedJson } from "./sorted-json.js";
4
4
  /** query 문서를 결정적인 JSON 문자열로 인코딩한다. */
5
5
  export function encodeBridgeQuery(document) {
6
6
  return encodeSortedJson(document);
7
7
  }
8
- /** 조인된 브리지에서 채널 또는 메서드 문자열을 질의한다. */
9
- export function createBridgeQuery(joined, requested) {
8
+ /**
9
+ * 조인된 브리지에서 채널 또는 메서드 문자열을 질의한다.
10
+ *
11
+ * `messages`를 주면 BasicMessageChannel·EventChannel 경계도 `message`·`stream`
12
+ * 주체로 질의할 수 있다. v1 채널과 이름이 같아도 qualifiedName의 kind 세그먼트로
13
+ * 구분된다.
14
+ */
15
+ export function createBridgeQuery(joined, requested, messages) {
10
16
  if (isBridgeJoinDeferred(joined)) {
11
17
  throw new Error('Cannot query a deferred bridge join.');
12
18
  }
13
- const results = [...channelResults(joined), ...methodResults(joined)];
19
+ const limitations = messages === undefined
20
+ ? joined.limitations
21
+ : [...joined.limitations, ...messages.limitations].sort(compareLimitations);
22
+ const results = [
23
+ ...channelResults(joined),
24
+ ...methodResults(joined),
25
+ ...nameResults(joined.matchedModules, joined.moduleImportsWithoutExports, joined.moduleExportsWithoutImports, 'module'),
26
+ ...nameResults(joined.matchedComponents, joined.componentRequiresWithoutExports, joined.componentExportsWithoutRequires, 'component'),
27
+ ...(messages === undefined ? [] : messageResults(messages)),
28
+ ];
14
29
  const exact = results.filter(({ subject }) => subject.qualifiedName === requested);
15
30
  if (exact.length > 1)
16
- return ambiguousQuery(joined, requested, exact);
31
+ return ambiguousQuery(limitations, requested, exact);
17
32
  if (exact[0] !== undefined)
18
- return foundQuery(joined, requested, exact[0]);
33
+ return foundQuery(limitations, requested, exact[0]);
19
34
  const named = results.filter(({ subject }) => subject.name === requested);
20
35
  if (named.length > 1)
21
- return ambiguousQuery(joined, requested, named);
36
+ return ambiguousQuery(limitations, requested, named);
22
37
  if (named[0] !== undefined)
23
- return foundQuery(joined, requested, named[0]);
38
+ return foundQuery(limitations, requested, named[0]);
24
39
  return {
25
40
  status: 'notFound',
26
41
  requested,
27
42
  level: 'bridge',
28
- limitations: joined.limitations,
43
+ limitations,
29
44
  };
30
45
  }
31
46
  /** 유일하게 식별된 논리 키와 증거를 found 문서로 감싼다. */
32
- function foundQuery(joined, requested, result) {
47
+ function foundQuery(limitations, requested, result) {
33
48
  return {
34
49
  status: 'found',
35
50
  requested,
36
51
  level: 'bridge',
37
- limitations: joined.limitations,
52
+ limitations,
38
53
  result,
39
54
  };
40
55
  }
41
56
  /** 여러 논리 키가 같은 요청과 맞을 때 선택 가능한 정규화 이름을 돌려준다. */
42
- function ambiguousQuery(joined, requested, results) {
57
+ function ambiguousQuery(limitations, requested, results) {
43
58
  return {
44
59
  status: 'ambiguous',
45
60
  requested,
46
61
  level: 'bridge',
47
- limitations: joined.limitations,
48
- candidates: results
49
- .map(({ subject }) => ({ qualifiedName: subject.qualifiedName }))
50
- .sort((left, right) => compareStrings(left.qualifiedName, right.qualifiedName)),
62
+ limitations,
63
+ candidates: [
64
+ ...new Map(results.map(({ subject }) => [
65
+ subject.qualifiedName,
66
+ { qualifiedName: subject.qualifiedName },
67
+ ])).values(),
68
+ ].sort((left, right) => compareStrings(left.qualifiedName, right.qualifiedName)),
51
69
  };
52
70
  }
71
+ /** v2 메시지·스트림 경계를 query result로 만든다. 양쪽 증거를 그대로 보존한다. */
72
+ function messageResults(messages) {
73
+ return messages.routes.map((route) => {
74
+ const kind = route.transport === 'event-channel' ? 'stream' : 'message';
75
+ return {
76
+ subject: {
77
+ name: route.channel,
78
+ qualifiedName: `flutter:${kind}:${encodeSubjectComponent(route.channel)}`,
79
+ kind,
80
+ },
81
+ usedBy: route.senders,
82
+ dependsOn: route.handlers,
83
+ };
84
+ });
85
+ }
53
86
  /** 요청 문자열과 정확히 같은 논리 메서드 결과를 만든다. */
54
87
  function methodResults(joined) {
55
88
  const matched = joined.matchedMethods
@@ -82,12 +115,46 @@ function channelResults(joined) {
82
115
  .map(({ target, channel, registrations }) => makeQueryResult(target, channel, 'channel', [], registrations));
83
116
  return [...matched, ...unregistered, ...registrations];
84
117
  }
118
+ /**
119
+ * 요청 문자열과 정확히 같은 논리 모듈·컴포넌트 이름 결과를 만든다.
120
+ *
121
+ * mechanism이 섞인 이름은 매치·미수출·미호출 컬렉션에 동시에 나타날 수
122
+ * 있으므로 (target, 이름)별로 합친다 — 같은 qualifiedName의 결과가 여럿이면
123
+ * 재질의로도 풀리지 않는 영구 모호 상태가 된다.
124
+ */
125
+ function nameResults(matched, unexported, unrequired, kind) {
126
+ const merged = new Map();
127
+ const merge = (target, name, usedBy, dependsOn) => {
128
+ // 합침 키의 NUL 구분자는 파서가 이름의 제어문자를 거부한다는 데 의존한다 —
129
+ // parse를 우회해 조립하는 경로가 생기면 이 구분자는 안전하지 않다.
130
+ const key = `${target}\u0000${name}`;
131
+ const entry = merged.get(key) ??
132
+ { target, name, usedBy: [], dependsOn: [] };
133
+ entry.usedBy.push(...usedBy);
134
+ entry.dependsOn.push(...dependsOn);
135
+ merged.set(key, entry);
136
+ };
137
+ for (const { target, channel, callers, receivers } of matched) {
138
+ merge(target, channel, callers, receivers);
139
+ }
140
+ for (const { target, channel, callers } of unexported) {
141
+ merge(target, channel, callers, []);
142
+ }
143
+ for (const { target, channel, receivers } of unrequired) {
144
+ merge(target, channel, [], receivers);
145
+ }
146
+ return [...merged.values()].map(({ target, name, usedBy, dependsOn }) => makeQueryResult(target, name, kind, usedBy, dependsOn));
147
+ }
85
148
  /** 조인 키와 양쪽 증거를 query result 골격으로 바꾼다. */
86
149
  function makeQueryResult(target, name, kind, usedBy, dependsOn) {
87
150
  return {
88
151
  subject: {
89
152
  name,
90
- qualifiedName: `${target}:${encodeSubjectComponent(name)}`,
153
+ // 모듈·컴포넌트는 kind 세그먼트를 넣어야 같은 이름의 채널·서로 다른 종류와
154
+ // qualifiedName이 충돌하지 않아 모호성을 재질의로 풀 수 있다.
155
+ qualifiedName: kind === 'channel'
156
+ ? `${target}:${encodeSubjectComponent(name)}`
157
+ : `${target}:${kind}:${encodeSubjectComponent(name)}`,
91
158
  kind,
92
159
  },
93
160
  usedBy,