isthmus-cli 0.5.0 → 0.6.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 (80) hide show
  1. package/README.ko.md +49 -28
  2. package/README.md +55 -31
  3. package/Skills/isthmus/SKILL.md +93 -4
  4. package/dist/cli/command-support.d.ts +1 -1
  5. package/dist/cli/command-support.js +2 -2
  6. package/dist/cli/command-support.js.map +1 -1
  7. package/dist/cli/impact-command.d.ts +5 -0
  8. package/dist/cli/impact-command.js +111 -0
  9. package/dist/cli/impact-command.js.map +1 -0
  10. package/dist/cli/main.js +15 -0
  11. package/dist/cli/main.js.map +1 -1
  12. package/dist/cli/preflight-command.d.ts +5 -0
  13. package/dist/cli/preflight-command.js +83 -0
  14. package/dist/cli/preflight-command.js.map +1 -0
  15. package/dist/cli/runtime-command.d.ts +5 -0
  16. package/dist/cli/runtime-command.js +42 -0
  17. package/dist/cli/runtime-command.js.map +1 -0
  18. package/dist/cli/runtime-json-reader.d.ts +12 -0
  19. package/dist/cli/runtime-json-reader.js +37 -0
  20. package/dist/cli/runtime-json-reader.js.map +1 -0
  21. package/dist/exchange/impact-selection.d.ts +13 -0
  22. package/dist/exchange/impact-selection.js +35 -0
  23. package/dist/exchange/impact-selection.js.map +1 -0
  24. package/dist/exchange/kartograph-impact.d.ts +4 -0
  25. package/dist/exchange/kartograph-impact.js +169 -0
  26. package/dist/exchange/kartograph-impact.js.map +1 -0
  27. package/dist/exchange/messages.d.ts +51 -0
  28. package/dist/exchange/messages.js +126 -0
  29. package/dist/exchange/messages.js.map +1 -0
  30. package/dist/exchange/parse.d.ts +8 -0
  31. package/dist/exchange/parse.js +12 -8
  32. package/dist/exchange/parse.js.map +1 -1
  33. package/dist/exchange/preflight-context.d.ts +71 -0
  34. package/dist/exchange/preflight-context.js +317 -0
  35. package/dist/exchange/preflight-context.js.map +1 -0
  36. package/dist/exchange/producer-impact.d.ts +17 -0
  37. package/dist/exchange/producer-impact.js +226 -0
  38. package/dist/exchange/producer-impact.js.map +1 -0
  39. package/dist/exchange/runtime.d.ts +69 -0
  40. package/dist/exchange/runtime.js +160 -0
  41. package/dist/exchange/runtime.js.map +1 -0
  42. package/dist/join/message-address.d.ts +7 -0
  43. package/dist/join/message-address.js +37 -0
  44. package/dist/join/message-address.js.map +1 -0
  45. package/dist/join/messages.d.ts +23 -0
  46. package/dist/join/messages.js +80 -0
  47. package/dist/join/messages.js.map +1 -0
  48. package/dist/report/diff.js +4 -3
  49. package/dist/report/diff.js.map +1 -1
  50. package/dist/report/impact.d.ts +62 -0
  51. package/dist/report/impact.js +168 -0
  52. package/dist/report/impact.js.map +1 -0
  53. package/dist/report/preflight-runtime.d.ts +42 -0
  54. package/dist/report/preflight-runtime.js +193 -0
  55. package/dist/report/preflight-runtime.js.map +1 -0
  56. package/dist/report/preflight-view.d.ts +146 -0
  57. package/dist/report/preflight-view.js +189 -0
  58. package/dist/report/preflight-view.js.map +1 -0
  59. package/dist/report/preflight.d.ts +104 -0
  60. package/dist/report/preflight.js +296 -0
  61. package/dist/report/preflight.js.map +1 -0
  62. package/dist/report/runtime-impact.d.ts +38 -0
  63. package/dist/report/runtime-impact.js +94 -0
  64. package/dist/report/runtime-impact.js.map +1 -0
  65. package/dist/report/runtime.d.ts +54 -0
  66. package/dist/report/runtime.js +137 -0
  67. package/dist/report/runtime.js.map +1 -0
  68. package/dist/report/sorted-json.d.ts +1 -1
  69. package/dist/report/sorted-json.js +2 -2
  70. package/dist/report/sorted-json.js.map +1 -1
  71. package/docs/BRIDGE-MESSAGES.md +102 -0
  72. package/docs/GRAPH-EXCHANGE.md +255 -0
  73. package/docs/IMPACT.md +96 -0
  74. package/docs/PREFLIGHT.md +295 -0
  75. package/docs/RUNTIME.md +172 -0
  76. package/docs/TOOLCHAIN.md +101 -0
  77. package/package.json +11 -2
  78. package/scripts/build-preflight-toolchain.mjs +186 -0
  79. package/scripts/capture-preflight.mjs +392 -0
  80. package/scripts/run-child.mjs +16 -0
package/dist/cli/main.js CHANGED
@@ -5,6 +5,9 @@ import { checkUsage, runCheckCommand } from "./check-command.js";
5
5
  import { graphUsage, runGraphCommand } from "./graph-command.js";
6
6
  import { diffUsage, runDiffCommand } from "./diff-command.js";
7
7
  import { queryUsage, runQueryCommand } from "./query-command.js";
8
+ import { impactUsage, runImpactCommand } from "./impact-command.js";
9
+ import { runtimeUsage, runRuntimeCommand } from "./runtime-command.js";
10
+ import { preflightUsage, runPreflightCommand } from "./preflight-command.js";
8
11
  import { retentionUsage, runRetentionsCommand, } from "./retentions-command.js";
9
12
  process.stdout.on('error', handleStreamError);
10
13
  process.stderr.on('error', handleStreamError);
@@ -13,6 +16,9 @@ const commandUsages = new Map([
13
16
  ['graph', graphUsage],
14
17
  ['diff', diffUsage],
15
18
  ['query', queryUsage],
19
+ ['impact', impactUsage],
20
+ ['preflight', preflightUsage],
21
+ ['verify-runtime', runtimeUsage],
16
22
  ['retentions', retentionUsage],
17
23
  ]);
18
24
  const rootHelp = `Usage: isthmus <command> [options]
@@ -20,6 +26,9 @@ const rootHelp = `Usage: isthmus <command> [options]
20
26
  Commands:
21
27
  check Report unmatched bridge calls and handlers
22
28
  query Find both sides of a channel or method
29
+ impact Inspect bridge dependencies before changing files or symbols
30
+ preflight Trace cross-language impact from producer analysis context
31
+ verify-runtime Verify recorded calls against scenario expectations
23
32
  graph Render matched boundary edges
24
33
  diff Compare bridge observations before and after a change
25
34
  retentions Produce external retention evidence
@@ -81,6 +90,12 @@ async function dispatchCommand(commandArguments) {
81
90
  return runDiffCommand(commandArguments, readTextFile);
82
91
  case 'query':
83
92
  return runQueryCommand(commandArguments, readTextFile);
93
+ case 'impact':
94
+ return runImpactCommand(commandArguments, readTextFile);
95
+ case 'preflight':
96
+ return runPreflightCommand(commandArguments, readTextFile);
97
+ case 'verify-runtime':
98
+ return runRuntimeCommand(commandArguments, readTextFile);
84
99
  case 'retentions': {
85
100
  const version = await readPackageVersion();
86
101
  return version === undefined
@@ -1 +1 @@
1
- {"version":3,"file":"main.js","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env node\n\nimport { readFile } from 'node:fs/promises';\n\nimport { writeTextAtomically } from './atomic-write.ts';\nimport { checkUsage, runCheckCommand } from './check-command.ts';\nimport type { CommandResult } from './command-support.ts';\nimport { graphUsage, runGraphCommand } from './graph-command.ts';\nimport { diffUsage, runDiffCommand } from './diff-command.ts';\nimport { queryUsage, runQueryCommand } from './query-command.ts';\nimport {\n retentionUsage,\n runRetentionsCommand,\n} from './retentions-command.ts';\n\nprocess.stdout.on('error', handleStreamError);\nprocess.stderr.on('error', handleStreamError);\n\nconst commandUsages = new Map([\n ['check', checkUsage],\n ['graph', graphUsage],\n ['diff', diffUsage],\n ['query', queryUsage],\n ['retentions', retentionUsage],\n]);\n\nconst rootHelp = `Usage: isthmus <command> [options]\n\nCommands:\n check Report unmatched bridge calls and handlers\n query Find both sides of a channel or method\n graph Render matched boundary edges\n diff Compare bridge observations before and after a change\n retentions Produce external retention evidence\n help Show command help\n\nOptions:\n -h, --help Show help; after a command it shows that command's usage\n --version Show version\n`;\n\nconst arguments_ = process.argv.slice(2);\nconst readTextFile = (path: string) => readFile(path, 'utf8');\nconst writeTextFile = (path: string, text: string) =>\n writeTextAtomically(path, text);\nconst informationalResult = await runInformationalCommand(arguments_);\nconst result = informationalResult ?? await dispatchCommand(arguments_);\n\nprocess.stdout.write(result.standardOutput);\nprocess.stderr.write(result.standardError);\nprocess.exitCode = result.exitCode;\n\n/** 파이프 소비자가 먼저 닫힌 정상 상황만 조용히 종료한다. */\nfunction handleStreamError(error: NodeJS.ErrnoException): void {\n if (error.code === 'EPIPE') {\n process.exitCode = 0;\n return;\n }\n throw error;\n}\n\n/** 도움말과 버전처럼 입력 문서가 필요 없는 명령을 실행한다. */\nasync function runInformationalCommand(\n commandArguments: readonly string[],\n): Promise<CommandResult | undefined> {\n // 도움말 플래그는 어느 위치에 있든 이긴다. 긴 호출 끝에 붙여도 사용법을 본다.\n const helpFlagIndex = commandArguments.findIndex(\n (argument) => argument === '--help' || argument === '-h',\n );\n if (helpFlagIndex >= 0) return commandHelp(commandArguments[0]);\n if (commandArguments[0] === 'help') {\n return commandHelp(commandArguments[1]);\n }\n if (\n commandArguments.length === 1 &&\n commandArguments[0] === '--version'\n ) {\n return readVersion();\n }\n return undefined;\n}\n\n/** 명령 이름에 맞는 사용법을, 모르면 루트 도움말을 돌려준다. */\nfunction commandHelp(command: string | undefined): CommandResult {\n if (command === undefined) return successfulText(rootHelp);\n const usage = commandUsages.get(command);\n return usage === undefined\n ? successfulText(rootHelp)\n : successfulText(`${usage}\\n`);\n}\n\n/** 알려진 분석 하위 명령으로만 라우팅한다. */\nasync function dispatchCommand(\n commandArguments: readonly string[],\n): Promise<CommandResult> {\n switch (commandArguments[0]) {\n case 'check':\n // SARIF driver에만 필요하므로 메타데이터 부재는 json 보고서를 막지 않는다.\n return runCheckCommand(\n commandArguments,\n readTextFile,\n writeTextFile,\n undefined,\n await readPackageVersion(),\n );\n case 'graph':\n return runGraphCommand(commandArguments, readTextFile);\n case 'diff':\n return runDiffCommand(commandArguments, readTextFile);\n case 'query':\n return runQueryCommand(commandArguments, readTextFile);\n case 'retentions': {\n const version = await readPackageVersion();\n return version === undefined\n ? packageMetadataError()\n : runRetentionsCommand(\n commandArguments,\n readTextFile,\n () => new Date(),\n version,\n );\n }\n default:\n return {\n standardOutput: '',\n standardError: rootHelp,\n exitCode: 64,\n };\n }\n}\n\n/** 설치된 package.json을 단일 출처로 사용해 버전을 출력한다. */\nasync function readVersion(): Promise<CommandResult> {\n const version = await readPackageVersion();\n return version === undefined\n ? packageMetadataError()\n : successfulText(`${version}\\n`);\n}\n\n/** 설치된 package.json에서 검증된 버전 문자열을 읽는다. */\nasync function readPackageVersion(): Promise<string | undefined> {\n try {\n const text = await readFile(new URL('../../package.json', import.meta.url), 'utf8');\n const document: unknown = JSON.parse(text);\n return isVersionDocument(document) ? document.version : undefined;\n } catch {\n return undefined;\n }\n}\n\n/** package metadata가 비어 있지 않은 버전을 제공하는지 확인한다. */\nfunction isVersionDocument(value: unknown): value is { readonly version: string } {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'version' in value &&\n typeof value.version === 'string' &&\n value.version.length > 0\n );\n}\n\n/** stdout 텍스트와 성공 종료 코드를 만든다. */\nfunction successfulText(standardOutput: string): CommandResult {\n return { standardOutput, standardError: '', exitCode: 0 };\n}\n\n/** package metadata 실패를 민감정보 없는 코드 2로 바꾼다. */\nfunction packageMetadataError(): CommandResult {\n return {\n standardOutput: '',\n standardError: 'Unable to read package metadata.\\n',\n exitCode: 2,\n };\n}\n"]}
1
+ {"version":3,"file":"main.js","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env node\n\nimport { readFile } from 'node:fs/promises';\n\nimport { writeTextAtomically } from './atomic-write.ts';\nimport { checkUsage, runCheckCommand } from './check-command.ts';\nimport type { CommandResult } from './command-support.ts';\nimport { graphUsage, runGraphCommand } from './graph-command.ts';\nimport { diffUsage, runDiffCommand } from './diff-command.ts';\nimport { queryUsage, runQueryCommand } from './query-command.ts';\nimport { impactUsage, runImpactCommand } from './impact-command.ts';\nimport { runtimeUsage, runRuntimeCommand } from './runtime-command.ts';\nimport { preflightUsage, runPreflightCommand } from './preflight-command.ts';\nimport {\n retentionUsage,\n runRetentionsCommand,\n} from './retentions-command.ts';\n\nprocess.stdout.on('error', handleStreamError);\nprocess.stderr.on('error', handleStreamError);\n\nconst commandUsages = new Map([\n ['check', checkUsage],\n ['graph', graphUsage],\n ['diff', diffUsage],\n ['query', queryUsage],\n ['impact', impactUsage],\n ['preflight', preflightUsage],\n ['verify-runtime', runtimeUsage],\n ['retentions', retentionUsage],\n]);\n\nconst rootHelp = `Usage: isthmus <command> [options]\n\nCommands:\n check Report unmatched bridge calls and handlers\n query Find both sides of a channel or method\n impact Inspect bridge dependencies before changing files or symbols\n preflight Trace cross-language impact from producer analysis context\n verify-runtime Verify recorded calls against scenario expectations\n graph Render matched boundary edges\n diff Compare bridge observations before and after a change\n retentions Produce external retention evidence\n help Show command help\n\nOptions:\n -h, --help Show help; after a command it shows that command's usage\n --version Show version\n`;\n\nconst arguments_ = process.argv.slice(2);\nconst readTextFile = (path: string) => readFile(path, 'utf8');\nconst writeTextFile = (path: string, text: string) =>\n writeTextAtomically(path, text);\nconst informationalResult = await runInformationalCommand(arguments_);\nconst result = informationalResult ?? await dispatchCommand(arguments_);\n\nprocess.stdout.write(result.standardOutput);\nprocess.stderr.write(result.standardError);\nprocess.exitCode = result.exitCode;\n\n/** 파이프 소비자가 먼저 닫힌 정상 상황만 조용히 종료한다. */\nfunction handleStreamError(error: NodeJS.ErrnoException): void {\n if (error.code === 'EPIPE') {\n process.exitCode = 0;\n return;\n }\n throw error;\n}\n\n/** 도움말과 버전처럼 입력 문서가 필요 없는 명령을 실행한다. */\nasync function runInformationalCommand(\n commandArguments: readonly string[],\n): Promise<CommandResult | undefined> {\n // 도움말 플래그는 어느 위치에 있든 이긴다. 긴 호출 끝에 붙여도 사용법을 본다.\n const helpFlagIndex = commandArguments.findIndex(\n (argument) => argument === '--help' || argument === '-h',\n );\n if (helpFlagIndex >= 0) return commandHelp(commandArguments[0]);\n if (commandArguments[0] === 'help') {\n return commandHelp(commandArguments[1]);\n }\n if (\n commandArguments.length === 1 &&\n commandArguments[0] === '--version'\n ) {\n return readVersion();\n }\n return undefined;\n}\n\n/** 명령 이름에 맞는 사용법을, 모르면 루트 도움말을 돌려준다. */\nfunction commandHelp(command: string | undefined): CommandResult {\n if (command === undefined) return successfulText(rootHelp);\n const usage = commandUsages.get(command);\n return usage === undefined\n ? successfulText(rootHelp)\n : successfulText(`${usage}\\n`);\n}\n\n/** 알려진 분석 하위 명령으로만 라우팅한다. */\nasync function dispatchCommand(\n commandArguments: readonly string[],\n): Promise<CommandResult> {\n switch (commandArguments[0]) {\n case 'check':\n // SARIF driver에만 필요하므로 메타데이터 부재는 json 보고서를 막지 않는다.\n return runCheckCommand(\n commandArguments,\n readTextFile,\n writeTextFile,\n undefined,\n await readPackageVersion(),\n );\n case 'graph':\n return runGraphCommand(commandArguments, readTextFile);\n case 'diff':\n return runDiffCommand(commandArguments, readTextFile);\n case 'query':\n return runQueryCommand(commandArguments, readTextFile);\n case 'impact':\n return runImpactCommand(commandArguments, readTextFile);\n case 'preflight':\n return runPreflightCommand(commandArguments, readTextFile);\n case 'verify-runtime':\n return runRuntimeCommand(commandArguments, readTextFile);\n case 'retentions': {\n const version = await readPackageVersion();\n return version === undefined\n ? packageMetadataError()\n : runRetentionsCommand(\n commandArguments,\n readTextFile,\n () => new Date(),\n version,\n );\n }\n default:\n return {\n standardOutput: '',\n standardError: rootHelp,\n exitCode: 64,\n };\n }\n}\n\n/** 설치된 package.json을 단일 출처로 사용해 버전을 출력한다. */\nasync function readVersion(): Promise<CommandResult> {\n const version = await readPackageVersion();\n return version === undefined\n ? packageMetadataError()\n : successfulText(`${version}\\n`);\n}\n\n/** 설치된 package.json에서 검증된 버전 문자열을 읽는다. */\nasync function readPackageVersion(): Promise<string | undefined> {\n try {\n const text = await readFile(new URL('../../package.json', import.meta.url), 'utf8');\n const document: unknown = JSON.parse(text);\n return isVersionDocument(document) ? document.version : undefined;\n } catch {\n return undefined;\n }\n}\n\n/** package metadata가 비어 있지 않은 버전을 제공하는지 확인한다. */\nfunction isVersionDocument(value: unknown): value is { readonly version: string } {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'version' in value &&\n typeof value.version === 'string' &&\n value.version.length > 0\n );\n}\n\n/** stdout 텍스트와 성공 종료 코드를 만든다. */\nfunction successfulText(standardOutput: string): CommandResult {\n return { standardOutput, standardError: '', exitCode: 0 };\n}\n\n/** package metadata 실패를 민감정보 없는 코드 2로 바꾼다. */\nfunction packageMetadataError(): CommandResult {\n return {\n standardOutput: '',\n standardError: 'Unable to read package metadata.\\n',\n exitCode: 2,\n };\n}\n"]}
@@ -0,0 +1,5 @@
1
+ import type { CommandResult, ReadTextFile } from './command-support.ts';
2
+ /** 같은 실행 문맥의 언어별 영향 결과를 연결해 사람이거나 AI인 소비자에게 전달한다. */
3
+ export declare function runPreflightCommand(arguments_: readonly string[], readTextFile: ReadTextFile): Promise<CommandResult>;
4
+ /** 입력 생성은 producer workflow가 맡으며 CLI는 JSON만 읽는다. */
5
+ export declare const preflightUsage: string;
@@ -0,0 +1,83 @@
1
+ import { isSafeNonEmptyString } from "../exchange/parse.js";
2
+ import { parsePreflightContext, PreflightValidationError } from "../exchange/preflight-context.js";
3
+ import { parseBridgeRuntime, parseRuntimeExpectations, RuntimeValidationError } from "../exchange/runtime.js";
4
+ import { MAX_DOCUMENTS_PER_JOIN } from "../join/join.js";
5
+ import { createPreflightReport, hasPreflightBlockers, PreflightGraphError } from "../report/preflight.js";
6
+ import { attachPreflightRuntime } from "../report/preflight-runtime.js";
7
+ import { createPreflightExplanation, createPreflightSummary } from "../report/preflight-view.js";
8
+ import { encodeSortedJson } from "../report/sorted-json.js";
9
+ import { inputFailure, inputFailureResult, internalError, isJsonParseFailure, MAX_TOTAL_INPUT_TEXT_LENGTH } from "./command-support.js";
10
+ import { parseCommandArguments } from "./parse-arguments.js";
11
+ import { RuntimeInputError, RuntimeJsonReader } from "./runtime-json-reader.js";
12
+ /** 같은 실행 문맥의 언어별 영향 결과를 연결해 사람이거나 AI인 소비자에게 전달한다. */
13
+ export async function runPreflightCommand(arguments_, readTextFile) {
14
+ const parsed = parseCommandArguments(arguments_.slice(1), ['--revision', '--expectations', '--limit', '--explain'], ['--strict', '--compact', '--summary']);
15
+ const revision = parsed?.valueFlags.get('--revision');
16
+ const expectedPath = parsed?.valueFlags.get('--expectations');
17
+ const summaryRequested = parsed?.booleanFlags.has('--summary') ?? false;
18
+ const explainSelector = parsed?.valueFlags.get('--explain');
19
+ const limitText = parsed?.valueFlags.get('--limit');
20
+ const limit = limitText === undefined ? undefined : Number(limitText);
21
+ if (parsed === undefined || parsed.positionals.length < 1 || parsed.positionals.length > MAX_DOCUMENTS_PER_JOIN + 1 ||
22
+ (expectedPath === undefined && parsed.positionals.length !== 1) ||
23
+ (revision !== undefined && !isSafeNonEmptyString(revision)) ||
24
+ (summaryRequested && explainSelector !== undefined) || (!summaryRequested && limitText !== undefined) ||
25
+ (limitText !== undefined && (!Number.isSafeInteger(limit) || limit < 1 || limit > 100))) {
26
+ return { standardOutput: '', standardError: `${preflightUsage}\n`, exitCode: 64 };
27
+ }
28
+ let text;
29
+ try {
30
+ text = await readTextFile(parsed.positionals[0]);
31
+ }
32
+ catch {
33
+ return inputFailure('Unable to read preflight context; check that the file exists and is readable.\n');
34
+ }
35
+ if (text.length > MAX_TOTAL_INPUT_TEXT_LENGTH)
36
+ return inputFailure('Preflight context exceeds the input size limit.\n');
37
+ let value;
38
+ try {
39
+ value = JSON.parse(text);
40
+ }
41
+ catch (error) {
42
+ return isJsonParseFailure(error) ? inputFailure('Preflight context is not valid JSON.\n') : internalError();
43
+ }
44
+ try {
45
+ const context = parsePreflightContext(value);
46
+ let report = createPreflightReport(revision === undefined || revision === context.revision ? context : {
47
+ ...context, limitations: [...context.limitations, 'stale-preflight-context: capture revision differs from the requested revision'],
48
+ });
49
+ if (expectedPath !== undefined) {
50
+ const reader = new RuntimeJsonReader(readTextFile, text.length);
51
+ const expectations = parseRuntimeExpectations(await reader.read(expectedPath));
52
+ const documents = [];
53
+ for (const path of parsed.positionals.slice(1))
54
+ documents.push(parseBridgeRuntime(await reader.read(path)));
55
+ report = attachPreflightRuntime(context, report, expectations, documents);
56
+ }
57
+ const blocked = parsed.booleanFlags.has('--strict') && hasPreflightBlockers(report);
58
+ const view = summaryRequested
59
+ ? createPreflightSummary(report, limit ?? 20)
60
+ : explainSelector === undefined ? report : createPreflightExplanation(report, explainSelector);
61
+ const viewStatus = explainSelector === undefined ? 'found' : view.status;
62
+ const statusHint = explainSelector === undefined || viewStatus === 'found' ? ''
63
+ : viewStatus === 'ambiguous'
64
+ ? 'The requested preflight selector matches multiple subjects; repeat with an exact subject key or producer id.\n'
65
+ : 'No preflight subject matches the requested selector; query an exact subject key, producer id, or qualifiedName.\n';
66
+ const exitCode = viewStatus === 'found' ? blocked ? 1 : 0 : 64;
67
+ return { standardOutput: encodeSortedJson(view, parsed.booleanFlags.has('--compact')),
68
+ standardError: viewStatus === 'found' && blocked
69
+ ? 'Cross-language preflight requires review: errors, evidence gaps, or unobserved selections remain.\n'
70
+ : statusHint, exitCode };
71
+ }
72
+ catch (error) {
73
+ if (error instanceof PreflightValidationError || error instanceof PreflightGraphError ||
74
+ error instanceof RuntimeValidationError || error instanceof RuntimeInputError) {
75
+ return inputFailure(`Preflight context violates its contract: ${error.message}\n`);
76
+ }
77
+ return inputFailureResult(error) ?? internalError();
78
+ }
79
+ }
80
+ /** 입력 생성은 producer workflow가 맡으며 CLI는 JSON만 읽는다. */
81
+ export const preflightUsage = 'Usage: isthmus preflight <context.json> [<runtime.json> ...] '
82
+ + '[--expectations <checks.json>] [--revision <revision>] [--summary [--limit <1..100>] | --explain <selector>] [--strict] [--compact]';
83
+ //# sourceMappingURL=preflight-command.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preflight-command.js","sourceRoot":"","sources":["../../src/cli/preflight-command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,qBAAqB,EAAE,wBAAwB,EAAE,MAAM,kCAAkC,CAAC;AACnG,OAAO,EAAE,kBAAkB,EAAE,wBAAwB,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AAC9G,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAC1G,OAAO,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AACxE,OAAO,EAAE,0BAA0B,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AACjG,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,aAAa,EAAE,kBAAkB,EAAE,2BAA2B,EAAE,MAAM,sBAAsB,CAAC;AAExI,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAEhF,qDAAqD;AACrD,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,UAA6B,EAAE,YAA0B;IACjG,MAAM,MAAM,GAAG,qBAAqB,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,YAAY,EAAE,gBAAgB,EAAE,SAAS,EAAE,WAAW,CAAC,EAAE,CAAC,UAAU,EAAE,WAAW,EAAE,WAAW,CAAC,CAAC,CAAC;IAC5J,MAAM,QAAQ,GAAG,MAAM,EAAE,UAAU,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;IACtD,MAAM,YAAY,GAAG,MAAM,EAAE,UAAU,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;IAC9D,MAAM,gBAAgB,GAAG,MAAM,EAAE,YAAY,CAAC,GAAG,CAAC,WAAW,CAAC,IAAI,KAAK,CAAC;IACxE,MAAM,eAAe,GAAG,MAAM,EAAE,UAAU,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IAC5D,MAAM,SAAS,GAAG,MAAM,EAAE,UAAU,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IACpD,MAAM,KAAK,GAAG,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IACtE,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,IAAI,MAAM,CAAC,WAAW,CAAC,MAAM,GAAG,sBAAsB,GAAG,CAAC;QACjH,CAAC,YAAY,KAAK,SAAS,IAAI,MAAM,CAAC,WAAW,CAAC,MAAM,KAAK,CAAC,CAAC;QAC/D,CAAC,QAAQ,KAAK,SAAS,IAAI,CAAC,oBAAoB,CAAC,QAAQ,CAAC,CAAC;QAC3D,CAAC,gBAAgB,IAAI,eAAe,KAAK,SAAS,CAAC,IAAI,CAAC,CAAC,gBAAgB,IAAI,SAAS,KAAK,SAAS,CAAC;QACrG,CAAC,SAAS,KAAK,SAAS,IAAI,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,IAAI,KAAM,GAAG,CAAC,IAAI,KAAM,GAAG,GAAG,CAAC,CAAC,EAAE,CAAC;QAC5F,OAAO,EAAE,cAAc,EAAE,EAAE,EAAE,aAAa,EAAE,GAAG,cAAc,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;IACpF,CAAC;IACD,IAAI,IAAY,CAAC;IACjB,IAAI,CAAC;QAAC,IAAI,GAAG,MAAM,YAAY,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,CAAE,CAAC,CAAC;IAAC,CAAC;IAC1D,MAAM,CAAC;QAAC,OAAO,YAAY,CAAC,iFAAiF,CAAC,CAAC;IAAC,CAAC;IACjH,IAAI,IAAI,CAAC,MAAM,GAAG,2BAA2B;QAAE,OAAO,YAAY,CAAC,mDAAmD,CAAC,CAAC;IACxH,IAAI,KAAc,CAAC;IACnB,IAAI,CAAC;QAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAAC,CAAC;IACjC,OAAO,KAAK,EAAE,CAAC;QACb,OAAO,kBAAkB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,wCAAwC,CAAC,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;IAC9G,CAAC;IACD,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,qBAAqB,CAAC,KAAK,CAAC,CAAC;QAC7C,IAAI,MAAM,GAAG,qBAAqB,CAAC,QAAQ,KAAK,SAAS,IAAI,QAAQ,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;YACrG,GAAG,OAAO,EAAE,WAAW,EAAE,CAAC,GAAG,OAAO,CAAC,WAAW,EAAE,+EAA+E,CAAC;SACnI,CAAC,CAAC;QACH,IAAI,YAAY,KAAK,SAAS,EAAE,CAAC;YAC/B,MAAM,MAAM,GAAG,IAAI,iBAAiB,CAAC,YAAY,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;YAChE,MAAM,YAAY,GAAG,wBAAwB,CAAC,MAAM,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC;YAC/E,MAAM,SAAS,GAAG,EAAE,CAAC;YACrB,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC;gBAAE,SAAS,CAAC,IAAI,CAAC,kBAAkB,CAAC,MAAM,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAC5G,MAAM,GAAG,sBAAsB,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,SAAS,CAAC,CAAC;QAC5E,CAAC;QACD,MAAM,OAAO,GAAG,MAAM,CAAC,YAAY,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,oBAAoB,CAAC,MAAM,CAAC,CAAC;QACpF,MAAM,IAAI,GAAG,gBAAgB;YAC3B,CAAC,CAAC,sBAAsB,CAAC,MAAM,EAAE,KAAK,IAAI,EAAE,CAAC;YAC7C,CAAC,CAAC,eAAe,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,0BAA0B,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;QACjG,MAAM,UAAU,GAAG,eAAe,KAAK,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC;QACzE,MAAM,UAAU,GAAG,eAAe,KAAK,SAAS,IAAI,UAAU,KAAK,OAAO,CAAC,CAAC,CAAC,EAAE;YAC7E,CAAC,CAAC,UAAU,KAAK,WAAW;gBAC1B,CAAC,CAAC,gHAAgH;gBAClH,CAAC,CAAC,mHAAmH,CAAC;QAC1H,MAAM,QAAQ,GAAG,UAAU,KAAK,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/D,OAAO,EAAE,cAAc,EAAE,gBAAgB,CAAC,IAAI,EAAE,MAAM,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;YACnF,aAAa,EAAE,UAAU,KAAK,OAAO,IAAI,OAAO;gBAC9C,CAAC,CAAC,qGAAqG;gBACvG,CAAC,CAAC,UAAU,EAAE,QAAQ,EAAE,CAAC;IAC/B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,wBAAwB,IAAI,KAAK,YAAY,mBAAmB;YACnF,KAAK,YAAY,sBAAsB,IAAI,KAAK,YAAY,iBAAiB,EAAE,CAAC;YAChF,OAAO,YAAY,CAAC,4CAA4C,KAAK,CAAC,OAAO,IAAI,CAAC,CAAC;QACrF,CAAC;QACD,OAAO,kBAAkB,CAAC,KAAK,CAAC,IAAI,aAAa,EAAE,CAAC;IACtD,CAAC;AACH,CAAC;AAED,oDAAoD;AACpD,MAAM,CAAC,MAAM,cAAc,GAAG,+DAA+D;MACzF,qIAAqI,CAAC","sourcesContent":["import { isSafeNonEmptyString } from '../exchange/parse.ts';\nimport { parsePreflightContext, PreflightValidationError } from '../exchange/preflight-context.ts';\nimport { parseBridgeRuntime, parseRuntimeExpectations, RuntimeValidationError } from '../exchange/runtime.ts';\nimport { MAX_DOCUMENTS_PER_JOIN } from '../join/join.ts';\nimport { createPreflightReport, hasPreflightBlockers, PreflightGraphError } from '../report/preflight.ts';\nimport { attachPreflightRuntime } from '../report/preflight-runtime.ts';\nimport { createPreflightExplanation, createPreflightSummary } from '../report/preflight-view.ts';\nimport { encodeSortedJson } from '../report/sorted-json.ts';\nimport { inputFailure, inputFailureResult, internalError, isJsonParseFailure, MAX_TOTAL_INPUT_TEXT_LENGTH } from './command-support.ts';\nimport type { CommandResult, ReadTextFile } from './command-support.ts';\nimport { parseCommandArguments } from './parse-arguments.ts';\nimport { RuntimeInputError, RuntimeJsonReader } from './runtime-json-reader.ts';\n\n/** 같은 실행 문맥의 언어별 영향 결과를 연결해 사람이거나 AI인 소비자에게 전달한다. */\nexport async function runPreflightCommand(arguments_: readonly string[], readTextFile: ReadTextFile): Promise<CommandResult> {\n const parsed = parseCommandArguments(arguments_.slice(1), ['--revision', '--expectations', '--limit', '--explain'], ['--strict', '--compact', '--summary']);\n const revision = parsed?.valueFlags.get('--revision');\n const expectedPath = parsed?.valueFlags.get('--expectations');\n const summaryRequested = parsed?.booleanFlags.has('--summary') ?? false;\n const explainSelector = parsed?.valueFlags.get('--explain');\n const limitText = parsed?.valueFlags.get('--limit');\n const limit = limitText === undefined ? undefined : Number(limitText);\n if (parsed === undefined || parsed.positionals.length < 1 || parsed.positionals.length > MAX_DOCUMENTS_PER_JOIN + 1 ||\n (expectedPath === undefined && parsed.positionals.length !== 1) ||\n (revision !== undefined && !isSafeNonEmptyString(revision)) ||\n (summaryRequested && explainSelector !== undefined) || (!summaryRequested && limitText !== undefined) ||\n (limitText !== undefined && (!Number.isSafeInteger(limit) || limit! < 1 || limit! > 100))) {\n return { standardOutput: '', standardError: `${preflightUsage}\\n`, exitCode: 64 };\n }\n let text: string;\n try { text = await readTextFile(parsed.positionals[0]!); }\n catch { return inputFailure('Unable to read preflight context; check that the file exists and is readable.\\n'); }\n if (text.length > MAX_TOTAL_INPUT_TEXT_LENGTH) return inputFailure('Preflight context exceeds the input size limit.\\n');\n let value: unknown;\n try { value = JSON.parse(text); }\n catch (error) {\n return isJsonParseFailure(error) ? inputFailure('Preflight context is not valid JSON.\\n') : internalError();\n }\n try {\n const context = parsePreflightContext(value);\n let report = createPreflightReport(revision === undefined || revision === context.revision ? context : {\n ...context, limitations: [...context.limitations, 'stale-preflight-context: capture revision differs from the requested revision'],\n });\n if (expectedPath !== undefined) {\n const reader = new RuntimeJsonReader(readTextFile, text.length);\n const expectations = parseRuntimeExpectations(await reader.read(expectedPath));\n const documents = [];\n for (const path of parsed.positionals.slice(1)) documents.push(parseBridgeRuntime(await reader.read(path)));\n report = attachPreflightRuntime(context, report, expectations, documents);\n }\n const blocked = parsed.booleanFlags.has('--strict') && hasPreflightBlockers(report);\n const view = summaryRequested\n ? createPreflightSummary(report, limit ?? 20)\n : explainSelector === undefined ? report : createPreflightExplanation(report, explainSelector);\n const viewStatus = explainSelector === undefined ? 'found' : view.status;\n const statusHint = explainSelector === undefined || viewStatus === 'found' ? ''\n : viewStatus === 'ambiguous'\n ? 'The requested preflight selector matches multiple subjects; repeat with an exact subject key or producer id.\\n'\n : 'No preflight subject matches the requested selector; query an exact subject key, producer id, or qualifiedName.\\n';\n const exitCode = viewStatus === 'found' ? blocked ? 1 : 0 : 64;\n return { standardOutput: encodeSortedJson(view, parsed.booleanFlags.has('--compact')),\n standardError: viewStatus === 'found' && blocked\n ? 'Cross-language preflight requires review: errors, evidence gaps, or unobserved selections remain.\\n'\n : statusHint, exitCode };\n } catch (error) {\n if (error instanceof PreflightValidationError || error instanceof PreflightGraphError ||\n error instanceof RuntimeValidationError || error instanceof RuntimeInputError) {\n return inputFailure(`Preflight context violates its contract: ${error.message}\\n`);\n }\n return inputFailureResult(error) ?? internalError();\n }\n}\n\n/** 입력 생성은 producer workflow가 맡으며 CLI는 JSON만 읽는다. */\nexport const preflightUsage = 'Usage: isthmus preflight <context.json> [<runtime.json> ...] '\n + '[--expectations <checks.json>] [--revision <revision>] [--summary [--limit <1..100>] | --explain <selector>] [--strict] [--compact]';\n"]}
@@ -0,0 +1,5 @@
1
+ import type { CommandResult, ReadTextFile } from './command-support.ts';
2
+ /** 시나리오별 실행 기대를 런타임 관찰 JSON과 대조하는 CLI다. */
3
+ export declare function runRuntimeCommand(arguments_: readonly string[], readTextFile: ReadTextFile): Promise<CommandResult>;
4
+ /** 런타임 기대 파일과 하나 이상의 실행 로그가 필요하다. */
5
+ export declare const runtimeUsage: string;
@@ -0,0 +1,42 @@
1
+ import { parseBridgeRuntime, parseRuntimeExpectations, RuntimeValidationError } from "../exchange/runtime.js";
2
+ import { MAX_DOCUMENTS_PER_JOIN } from "../join/join.js";
3
+ import { verifyRuntimeEvidence } from "../report/runtime.js";
4
+ import { encodeSortedJson } from "../report/sorted-json.js";
5
+ import { inputFailure, internalError } from "./command-support.js";
6
+ import { parseCommandArguments } from "./parse-arguments.js";
7
+ import { RuntimeInputError, RuntimeJsonReader } from "./runtime-json-reader.js";
8
+ /** 시나리오별 실행 기대를 런타임 관찰 JSON과 대조하는 CLI다. */
9
+ export async function runRuntimeCommand(arguments_, readTextFile) {
10
+ const parsed = parseCommandArguments(arguments_.slice(1), ['--expectations'], ['--strict', '--compact']);
11
+ const expectedPath = parsed?.valueFlags.get('--expectations');
12
+ if (parsed === undefined || expectedPath === undefined || parsed.positionals.length === 0 ||
13
+ parsed.positionals.length > MAX_DOCUMENTS_PER_JOIN) {
14
+ return { standardOutput: '', standardError: `${runtimeUsage}\n`, exitCode: 64 };
15
+ }
16
+ try {
17
+ const reader = new RuntimeJsonReader(readTextFile);
18
+ const expectations = parseRuntimeExpectations(await reader.read(expectedPath));
19
+ const documents = [];
20
+ for (const path of parsed.positionals)
21
+ documents.push(parseBridgeRuntime(await reader.read(path)));
22
+ const report = verifyRuntimeEvidence(expectations, documents);
23
+ const failed = parsed.booleanFlags.has('--strict') && report.status !== 'passed';
24
+ return {
25
+ standardOutput: encodeSortedJson(report, parsed.booleanFlags.has('--compact')),
26
+ standardError: failed
27
+ ? 'Runtime verification requires review: failed calls, missing scenarios, stale runs, or incomplete evidence remain.\n'
28
+ : '',
29
+ exitCode: failed ? 1 : 0,
30
+ };
31
+ }
32
+ catch (error) {
33
+ if (error instanceof RuntimeValidationError || error instanceof RuntimeInputError) {
34
+ return inputFailure(`${error.message}\n`);
35
+ }
36
+ return internalError();
37
+ }
38
+ }
39
+ /** 런타임 기대 파일과 하나 이상의 실행 로그가 필요하다. */
40
+ export const runtimeUsage = 'Usage: isthmus verify-runtime --expectations <expectations.json> '
41
+ + '<runtime.json> [more...] [--strict] [--compact]';
42
+ //# sourceMappingURL=runtime-command.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime-command.js","sourceRoot":"","sources":["../../src/cli/runtime-command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,wBAAwB,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AAC9G,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAEnE,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAEhF,2CAA2C;AAC3C,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,UAA6B,EAAE,YAA0B;IAEzD,MAAM,MAAM,GAAG,qBAAqB,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,gBAAgB,CAAC,EAAE,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC,CAAC;IACzG,MAAM,YAAY,GAAG,MAAM,EAAE,UAAU,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;IAC9D,IAAI,MAAM,KAAK,SAAS,IAAI,YAAY,KAAK,SAAS,IAAI,MAAM,CAAC,WAAW,CAAC,MAAM,KAAK,CAAC;QACvF,MAAM,CAAC,WAAW,CAAC,MAAM,GAAG,sBAAsB,EAAE,CAAC;QACrD,OAAO,EAAE,cAAc,EAAE,EAAE,EAAE,aAAa,EAAE,GAAG,YAAY,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;IAClF,CAAC;IACD,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,iBAAiB,CAAC,YAAY,CAAC,CAAC;QACnD,MAAM,YAAY,GAAG,wBAAwB,CAAC,MAAM,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC;QAC/E,MAAM,SAAS,GAAG,EAAE,CAAC;QACrB,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,WAAW;YAAE,SAAS,CAAC,IAAI,CAAC,kBAAkB,CAAC,MAAM,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACnG,MAAM,MAAM,GAAG,qBAAqB,CAAC,YAAY,EAAE,SAAS,CAAC,CAAC;QAC9D,MAAM,MAAM,GAAG,MAAM,CAAC,YAAY,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,MAAM,CAAC,MAAM,KAAK,QAAQ,CAAC;QACjF,OAAO;YACL,cAAc,EAAE,gBAAgB,CAAC,MAAM,EAAE,MAAM,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;YAC9E,aAAa,EAAE,MAAM;gBACnB,CAAC,CAAC,qHAAqH;gBACvH,CAAC,CAAC,EAAE;YACN,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;SACzB,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,sBAAsB,IAAI,KAAK,YAAY,iBAAiB,EAAE,CAAC;YAClF,OAAO,YAAY,CAAC,GAAG,KAAK,CAAC,OAAO,IAAI,CAAC,CAAC;QAC5C,CAAC;QACD,OAAO,aAAa,EAAE,CAAC;IACzB,CAAC;AACH,CAAC;AAED,qCAAqC;AACrC,MAAM,CAAC,MAAM,YAAY,GAAG,mEAAmE;MAC3F,iDAAiD,CAAC","sourcesContent":["import { parseBridgeRuntime, parseRuntimeExpectations, RuntimeValidationError } from '../exchange/runtime.ts';\nimport { MAX_DOCUMENTS_PER_JOIN } from '../join/join.ts';\nimport { verifyRuntimeEvidence } from '../report/runtime.ts';\nimport { encodeSortedJson } from '../report/sorted-json.ts';\nimport { inputFailure, internalError } from './command-support.ts';\nimport type { CommandResult, ReadTextFile } from './command-support.ts';\nimport { parseCommandArguments } from './parse-arguments.ts';\nimport { RuntimeInputError, RuntimeJsonReader } from './runtime-json-reader.ts';\n\n/** 시나리오별 실행 기대를 런타임 관찰 JSON과 대조하는 CLI다. */\nexport async function runRuntimeCommand(\n arguments_: readonly string[], readTextFile: ReadTextFile,\n): Promise<CommandResult> {\n const parsed = parseCommandArguments(arguments_.slice(1), ['--expectations'], ['--strict', '--compact']);\n const expectedPath = parsed?.valueFlags.get('--expectations');\n if (parsed === undefined || expectedPath === undefined || parsed.positionals.length === 0 ||\n parsed.positionals.length > MAX_DOCUMENTS_PER_JOIN) {\n return { standardOutput: '', standardError: `${runtimeUsage}\\n`, exitCode: 64 };\n }\n try {\n const reader = new RuntimeJsonReader(readTextFile);\n const expectations = parseRuntimeExpectations(await reader.read(expectedPath));\n const documents = [];\n for (const path of parsed.positionals) documents.push(parseBridgeRuntime(await reader.read(path)));\n const report = verifyRuntimeEvidence(expectations, documents);\n const failed = parsed.booleanFlags.has('--strict') && report.status !== 'passed';\n return {\n standardOutput: encodeSortedJson(report, parsed.booleanFlags.has('--compact')),\n standardError: failed\n ? 'Runtime verification requires review: failed calls, missing scenarios, stale runs, or incomplete evidence remain.\\n'\n : '',\n exitCode: failed ? 1 : 0,\n };\n } catch (error) {\n if (error instanceof RuntimeValidationError || error instanceof RuntimeInputError) {\n return inputFailure(`${error.message}\\n`);\n }\n return internalError();\n }\n}\n\n/** 런타임 기대 파일과 하나 이상의 실행 로그가 필요하다. */\nexport const runtimeUsage = 'Usage: isthmus verify-runtime --expectations <expectations.json> '\n + '<runtime.json> [more...] [--strict] [--compact]';\n"]}
@@ -0,0 +1,12 @@
1
+ import type { ReadTextFile } from './command-support.ts';
2
+ /** 기대·실행 JSON의 예산을 공유하며 이미 읽은 정적 context 크기도 포함한다. */
3
+ export declare class RuntimeJsonReader {
4
+ private readonly readTextFile;
5
+ private totalLength;
6
+ private position;
7
+ constructor(readTextFile: ReadTextFile, initialLength?: number);
8
+ read(path: string): Promise<unknown>;
9
+ }
10
+ /** runtime 입력 I/O·JSON 오류를 내부 결함과 구분한다. */
11
+ export declare class RuntimeInputError extends Error {
12
+ }
@@ -0,0 +1,37 @@
1
+ import { isJsonParseFailure, MAX_INPUT_TEXT_LENGTH, MAX_TOTAL_INPUT_TEXT_LENGTH } from "./command-support.js";
2
+ /** 기대·실행 JSON의 예산을 공유하며 이미 읽은 정적 context 크기도 포함한다. */
3
+ export class RuntimeJsonReader {
4
+ readTextFile;
5
+ totalLength;
6
+ position = 0;
7
+ constructor(readTextFile, initialLength = 0) {
8
+ this.readTextFile = readTextFile;
9
+ this.totalLength = initialLength;
10
+ }
11
+ async read(path) {
12
+ this.position++;
13
+ let text;
14
+ try {
15
+ text = await this.readTextFile(path);
16
+ }
17
+ catch {
18
+ throw new RuntimeInputError(`Unable to read runtime input ${this.position}; check the file exists and is readable.`);
19
+ }
20
+ this.totalLength += text.length;
21
+ if (text.length > MAX_INPUT_TEXT_LENGTH || this.totalLength > MAX_TOTAL_INPUT_TEXT_LENGTH) {
22
+ throw new RuntimeInputError(`Runtime input ${this.position} exceeds the input size limits.`);
23
+ }
24
+ try {
25
+ return JSON.parse(text);
26
+ }
27
+ catch (error) {
28
+ if (isJsonParseFailure(error))
29
+ throw new RuntimeInputError(`Runtime input ${this.position} is not valid JSON.`);
30
+ throw error;
31
+ }
32
+ }
33
+ }
34
+ /** runtime 입력 I/O·JSON 오류를 내부 결함과 구분한다. */
35
+ export class RuntimeInputError extends Error {
36
+ }
37
+ //# sourceMappingURL=runtime-json-reader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime-json-reader.js","sourceRoot":"","sources":["../../src/cli/runtime-json-reader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,2BAA2B,EAAE,MAAM,sBAAsB,CAAC;AAG9G,sDAAsD;AACtD,MAAM,OAAO,iBAAiB;IACX,YAAY,CAAe;IACpC,WAAW,CAAS;IACpB,QAAQ,GAAG,CAAC,CAAC;IAErB,YAAY,YAA0B,EAAE,aAAa,GAAG,CAAC;QACvD,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;QACjC,IAAI,CAAC,WAAW,GAAG,aAAa,CAAC;IACnC,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,IAAY;QACrB,IAAI,CAAC,QAAQ,EAAE,CAAC;QAChB,IAAI,IAAY,CAAC;QACjB,IAAI,CAAC;YAAC,IAAI,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;QAAC,CAAC;QAC7C,MAAM,CAAC;YAAC,MAAM,IAAI,iBAAiB,CAAC,gCAAgC,IAAI,CAAC,QAAQ,0CAA0C,CAAC,CAAC;QAAC,CAAC;QAC/H,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,MAAM,CAAC;QAChC,IAAI,IAAI,CAAC,MAAM,GAAG,qBAAqB,IAAI,IAAI,CAAC,WAAW,GAAG,2BAA2B,EAAE,CAAC;YAC1F,MAAM,IAAI,iBAAiB,CAAC,iBAAiB,IAAI,CAAC,QAAQ,iCAAiC,CAAC,CAAC;QAC/F,CAAC;QACD,IAAI,CAAC;YAAC,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAAC,CAAC;QAChC,OAAO,KAAK,EAAE,CAAC;YACb,IAAI,kBAAkB,CAAC,KAAK,CAAC;gBAAE,MAAM,IAAI,iBAAiB,CAAC,iBAAiB,IAAI,CAAC,QAAQ,qBAAqB,CAAC,CAAC;YAChH,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;CACF;AAED,2CAA2C;AAC3C,MAAM,OAAO,iBAAkB,SAAQ,KAAK;CAAG","sourcesContent":["import { isJsonParseFailure, MAX_INPUT_TEXT_LENGTH, MAX_TOTAL_INPUT_TEXT_LENGTH } from './command-support.ts';\nimport type { ReadTextFile } from './command-support.ts';\n\n/** 기대·실행 JSON의 예산을 공유하며 이미 읽은 정적 context 크기도 포함한다. */\nexport class RuntimeJsonReader {\n private readonly readTextFile: ReadTextFile;\n private totalLength: number;\n private position = 0;\n\n constructor(readTextFile: ReadTextFile, initialLength = 0) {\n this.readTextFile = readTextFile;\n this.totalLength = initialLength;\n }\n\n async read(path: string): Promise<unknown> {\n this.position++;\n let text: string;\n try { text = await this.readTextFile(path); }\n catch { throw new RuntimeInputError(`Unable to read runtime input ${this.position}; check the file exists and is readable.`); }\n this.totalLength += text.length;\n if (text.length > MAX_INPUT_TEXT_LENGTH || this.totalLength > MAX_TOTAL_INPUT_TEXT_LENGTH) {\n throw new RuntimeInputError(`Runtime input ${this.position} exceeds the input size limits.`);\n }\n try { return JSON.parse(text); }\n catch (error) {\n if (isJsonParseFailure(error)) throw new RuntimeInputError(`Runtime input ${this.position} is not valid JSON.`);\n throw error;\n }\n }\n}\n\n/** runtime 입력 I/O·JSON 오류를 내부 결함과 구분한다. */\nexport class RuntimeInputError extends Error {}\n"]}
@@ -0,0 +1,13 @@
1
+ /** 변경 대상의 프로젝트 상대 파일과 정확한 심볼 이름/USR이다. 두 목록은 합집합이다. */
2
+ export interface ImpactSelection {
3
+ readonly files: readonly string[];
4
+ readonly symbols: readonly string[];
5
+ }
6
+ /** 변경 목록이 큰 입력을 통한 선택 집합 증폭을 일으키지 않도록 제한한다. */
7
+ export declare const MAX_IMPACT_SELECTORS = 10000;
8
+ /** 변경 선택 계약 실패를 소스 값이 없는 메시지로 구분한다. */
9
+ export declare class ImpactSelectionValidationError extends Error {
10
+ constructor(message: string);
11
+ }
12
+ /** 외부 변경 목록을 검증하고 위치·심볼 문자열을 손대지 않은 채 중복 제거한다. */
13
+ export declare function parseImpactSelection(input: unknown): ImpactSelection;
@@ -0,0 +1,35 @@
1
+ import { isProjectRelativePath, isSafeNonEmptyString } from "./parse.js";
2
+ import { compareStrings } from "../compare.js";
3
+ /** 변경 목록이 큰 입력을 통한 선택 집합 증폭을 일으키지 않도록 제한한다. */
4
+ export const MAX_IMPACT_SELECTORS = 10_000;
5
+ /** 변경 선택 계약 실패를 소스 값이 없는 메시지로 구분한다. */
6
+ export class ImpactSelectionValidationError extends Error {
7
+ constructor(message) {
8
+ super(message);
9
+ this.name = 'ImpactSelectionValidationError';
10
+ }
11
+ }
12
+ /** 외부 변경 목록을 검증하고 위치·심볼 문자열을 손대지 않은 채 중복 제거한다. */
13
+ export function parseImpactSelection(input) {
14
+ if (typeof input !== 'object' || input === null || Array.isArray(input)) {
15
+ throw new ImpactSelectionValidationError('Changes must be a JSON object.');
16
+ }
17
+ const value = input;
18
+ if (value.format !== 'isthmus-changes' || value.version !== 1) {
19
+ throw new ImpactSelectionValidationError('Expected isthmus-changes version 1.');
20
+ }
21
+ const files = value.files === undefined ? [] : value.files;
22
+ const symbols = value.symbols === undefined ? [] : value.symbols;
23
+ if (!Array.isArray(files) || !Array.isArray(symbols) ||
24
+ files.length + symbols.length === 0 || files.length + symbols.length > MAX_IMPACT_SELECTORS) {
25
+ throw new ImpactSelectionValidationError('Changes require 1 to 10000 file or symbol selectors.');
26
+ }
27
+ if (!files.every(isProjectRelativePath) || !symbols.every(isSafeNonEmptyString)) {
28
+ throw new ImpactSelectionValidationError('Changes require relative file paths and non-empty safe symbol names.');
29
+ }
30
+ return {
31
+ files: [...new Set(files)].sort(compareStrings),
32
+ symbols: [...new Set(symbols)].sort(compareStrings),
33
+ };
34
+ }
35
+ //# sourceMappingURL=impact-selection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"impact-selection.js","sourceRoot":"","sources":["../../src/exchange/impact-selection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AACzE,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAQ/C,+CAA+C;AAC/C,MAAM,CAAC,MAAM,oBAAoB,GAAG,MAAM,CAAC;AAE3C,uCAAuC;AACvC,MAAM,OAAO,8BAA+B,SAAQ,KAAK;IACvD,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,gCAAgC,CAAC;IAC/C,CAAC;CACF;AAED,kDAAkD;AAClD,MAAM,UAAU,oBAAoB,CAAC,KAAc;IACjD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACxE,MAAM,IAAI,8BAA8B,CAAC,gCAAgC,CAAC,CAAC;IAC7E,CAAC;IACD,MAAM,KAAK,GAAG,KAAgC,CAAC;IAC/C,IAAI,KAAK,CAAC,MAAM,KAAK,iBAAiB,IAAI,KAAK,CAAC,OAAO,KAAK,CAAC,EAAE,CAAC;QAC9D,MAAM,IAAI,8BAA8B,CAAC,qCAAqC,CAAC,CAAC;IAClF,CAAC;IACD,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC;IAC3D,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC;IACjE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC;QAClD,KAAK,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,oBAAoB,EAAE,CAAC;QAC9F,MAAM,IAAI,8BAA8B,CAAC,sDAAsD,CAAC,CAAC;IACnG,CAAC;IACD,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,qBAAqB,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,oBAAoB,CAAC,EAAE,CAAC;QAChF,MAAM,IAAI,8BAA8B,CAAC,sEAAsE,CAAC,CAAC;IACnH,CAAC;IACD,OAAO;QACL,KAAK,EAAE,CAAC,GAAG,IAAI,GAAG,CAAC,KAAiB,CAAC,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC;QAC3D,OAAO,EAAE,CAAC,GAAG,IAAI,GAAG,CAAC,OAAmB,CAAC,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC;KAChE,CAAC;AACJ,CAAC","sourcesContent":["import { isProjectRelativePath, isSafeNonEmptyString } from './parse.ts';\nimport { compareStrings } from '../compare.ts';\n\n/** 변경 대상의 프로젝트 상대 파일과 정확한 심볼 이름/USR이다. 두 목록은 합집합이다. */\nexport interface ImpactSelection {\n readonly files: readonly string[];\n readonly symbols: readonly string[];\n}\n\n/** 변경 목록이 큰 입력을 통한 선택 집합 증폭을 일으키지 않도록 제한한다. */\nexport const MAX_IMPACT_SELECTORS = 10_000;\n\n/** 변경 선택 계약 실패를 소스 값이 없는 메시지로 구분한다. */\nexport class ImpactSelectionValidationError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'ImpactSelectionValidationError';\n }\n}\n\n/** 외부 변경 목록을 검증하고 위치·심볼 문자열을 손대지 않은 채 중복 제거한다. */\nexport function parseImpactSelection(input: unknown): ImpactSelection {\n if (typeof input !== 'object' || input === null || Array.isArray(input)) {\n throw new ImpactSelectionValidationError('Changes must be a JSON object.');\n }\n const value = input as Record<string, unknown>;\n if (value.format !== 'isthmus-changes' || value.version !== 1) {\n throw new ImpactSelectionValidationError('Expected isthmus-changes version 1.');\n }\n const files = value.files === undefined ? [] : value.files;\n const symbols = value.symbols === undefined ? [] : value.symbols;\n if (!Array.isArray(files) || !Array.isArray(symbols) ||\n files.length + symbols.length === 0 || files.length + symbols.length > MAX_IMPACT_SELECTORS) {\n throw new ImpactSelectionValidationError('Changes require 1 to 10000 file or symbol selectors.');\n }\n if (!files.every(isProjectRelativePath) || !symbols.every(isSafeNonEmptyString)) {\n throw new ImpactSelectionValidationError('Changes require relative file paths and non-empty safe symbol names.');\n }\n return {\n files: [...new Set(files as string[])].sort(compareStrings),\n symbols: [...new Set(symbols as string[])].sort(compareStrings),\n };\n}\n"]}
@@ -0,0 +1,4 @@
1
+ import { type LanguageImpact } from './preflight-context.ts';
2
+ import type { ProducerImpactMetadata } from './producer-impact.ts';
3
+ /** Kartograph의 현재 시점 영향 근거를 공통 사전 점검 형식으로 연결한다. */
4
+ export declare function adaptKartographImpact(raw: unknown, metadata: ProducerImpactMetadata): LanguageImpact;
@@ -0,0 +1,169 @@
1
+ import { PreflightValidationError, validateLanguageImpact } from "./preflight-context.js";
2
+ import { isProjectRelativePath, isSafeNonEmptyString } from "./parse.js";
3
+ import { compareStrings } from "../compare.js";
4
+ /** Kartograph의 현재 시점 영향 근거를 공통 사전 점검 형식으로 연결한다. */
5
+ export function adaptKartographImpact(raw, metadata) {
6
+ const value = object(raw);
7
+ if (value.format !== 'kartograph-impact' || value.version !== 1)
8
+ fail('Expected kartograph-impact version 1.');
9
+ if (!['found', 'partial', 'notFound', 'noChanges'].includes(string(value.status)))
10
+ fail('Invalid Kartograph impact status.');
11
+ if (!isSafeNonEmptyString(metadata.project))
12
+ fail('Invalid Kartograph project metadata.');
13
+ const inputs = object(value.inputs);
14
+ if (inputs.base !== undefined)
15
+ fail('Kartograph preflight requires a current-only impact report.');
16
+ const limitations = array(value.limitations).map((item) => string(item));
17
+ const flags = object(value.truncated);
18
+ for (const key of ['results', 'depth', 'budget'])
19
+ if (typeof flags[key] !== 'boolean')
20
+ fail('Invalid Kartograph truncation flag.');
21
+ const navigation = value.navigation === undefined ? undefined : object(value.navigation);
22
+ if (navigation !== undefined && (!Number.isSafeInteger(navigation.offset) || navigation.offset < 0 ||
23
+ typeof navigation.hasNext !== 'boolean' || typeof navigation.hasPrevious !== 'boolean'))
24
+ fail('Invalid Kartograph navigation.');
25
+ let truncated = Object.values(flags).some((flag) => flag === true) || value.status === 'partial' ||
26
+ (navigation !== undefined && (navigation.offset !== 0 || navigation.hasNext === true || navigation.hasPrevious === true));
27
+ let missingLocations = 0;
28
+ let partialLocations = 0;
29
+ let omittedPaths = 0;
30
+ let pathItems = 0;
31
+ const symbols = new Map();
32
+ const changed = array(value.changed).map(object);
33
+ const affected = array(value.affected).map(object);
34
+ for (const row of [...changed, ...affected]) {
35
+ const id = string(row.usr);
36
+ if (symbols.has(id))
37
+ fail('Kartograph impact symbol ids must be unique.');
38
+ const current = array(row.presentIn).map(string).includes('current');
39
+ if (!current) {
40
+ omittedPaths++;
41
+ continue;
42
+ }
43
+ let location;
44
+ if (row.location !== null && row.location !== undefined) {
45
+ const source = object(row.location);
46
+ const path = typeof source.path === 'string' && source.path.startsWith(`${metadata.project}/`)
47
+ ? source.path.slice(metadata.project.length + 1) : source.path;
48
+ if (isProjectRelativePath(path)) {
49
+ const line = Number.isSafeInteger(source.line) && source.line > 0 ? source.line : undefined;
50
+ const column = line !== undefined && Number.isSafeInteger(source.column) && source.column > 0
51
+ ? source.column : undefined;
52
+ location = { path, ...(line === undefined ? {} : { line }), ...(column === undefined ? {} : { column }) };
53
+ if (column === undefined)
54
+ partialLocations++;
55
+ }
56
+ }
57
+ if (location === undefined)
58
+ missingLocations++;
59
+ symbols.set(id, { id, qualifiedName: string(row.qualifiedName),
60
+ ...(row.kind === undefined ? {} : { kind: string(row.kind) }), ...(location === undefined ? {} : { location }) });
61
+ }
62
+ const roots = changed.flatMap((row) => symbols.has(string(row.usr)) ? [symbols.get(string(row.usr))] : []);
63
+ const rootIds = new Set(roots.map(({ id }) => id));
64
+ const adjacency = new Map();
65
+ for (const row of affected) {
66
+ omittedPaths += row.pathOmissions === undefined ? 0 : array(row.pathOmissions).length;
67
+ if (row.pathStatus !== undefined && row.pathStatus !== 'complete')
68
+ truncated = true;
69
+ for (const input of array(row.paths)) {
70
+ const path = object(input);
71
+ const revision = string(path.revision);
72
+ if (revision === 'base') {
73
+ omittedPaths++;
74
+ continue;
75
+ }
76
+ if (revision !== 'current')
77
+ fail('Invalid Kartograph path revision.');
78
+ const nodes = array(path.nodes).map(string);
79
+ const edges = array(path.edges).map(object);
80
+ pathItems += nodes.length + edges.length;
81
+ if (pathItems > 1_000_000)
82
+ fail('Kartograph impact paths exceed their budget.');
83
+ if (nodes.length < 2 || nodes.length > 129 || edges.length !== nodes.length - 1 || nodes[0] !== row.usr ||
84
+ nodes.at(-1) !== path.changed || new Set(nodes).size !== nodes.length)
85
+ fail('Invalid Kartograph impact path.');
86
+ const relationships = edges.map((edge, index) => {
87
+ const source = string(edge.source);
88
+ const target = string(edge.target);
89
+ const forward = source === nodes[index] && target === nodes[index + 1];
90
+ const reverse = target === nodes[index] && source === nodes[index + 1];
91
+ if ((!forward && !reverse) || (reverse && edge.kind !== 'override') ||
92
+ edge.traversal !== (forward ? 'dependency' : 'overrideContract')) {
93
+ fail('Kartograph path edges do not match their traversal.');
94
+ }
95
+ return [string(edge.kind), `origin:${string(edge.origin)}`, `traversal:${string(edge.traversal)}`];
96
+ });
97
+ if (!rootIds.has(string(path.changed)) || nodes.some((id) => !symbols.has(id))) {
98
+ omittedPaths++;
99
+ continue;
100
+ }
101
+ for (let index = 0; index < edges.length; index++) {
102
+ const parent = nodes[index + 1];
103
+ const child = nodes[index];
104
+ let children = adjacency.get(parent);
105
+ if (children === undefined) {
106
+ children = new Map();
107
+ adjacency.set(parent, children);
108
+ }
109
+ let reasons = children.get(child);
110
+ if (reasons === undefined) {
111
+ reasons = new Set();
112
+ children.set(child, reasons);
113
+ }
114
+ for (const reason of relationships[index])
115
+ reasons.add(reason);
116
+ }
117
+ }
118
+ }
119
+ // 같은 current 그래프의 관찰 간선만 사용한다. 한 정점의 대표 경로는 가장 짧은 경로다.
120
+ const queue = [...rootIds].sort(compareStrings);
121
+ const depth = new Map(queue.map((id) => [id, 0]));
122
+ const rows = [];
123
+ for (let index = 0; index < queue.length; index++) {
124
+ const parent = queue[index];
125
+ for (const [id, reasons] of [...(adjacency.get(parent) ?? [])].sort(([a], [b]) => compareStrings(a, b))) {
126
+ if (depth.has(id))
127
+ continue;
128
+ const distance = depth.get(parent) + 1;
129
+ if (distance > 128) {
130
+ omittedPaths++;
131
+ continue;
132
+ }
133
+ depth.set(id, distance);
134
+ queue.push(id);
135
+ rows.push({ symbol: symbols.get(id), via: parent, depth: distance, relationships: [...reasons].sort(compareStrings) });
136
+ }
137
+ }
138
+ omittedPaths += affected.filter((row) => symbols.has(string(row.usr)) && !depth.has(string(row.usr))).length;
139
+ const unresolved = array(value.unresolved);
140
+ if (unresolved.length > 0)
141
+ limitations.push(`kartograph-unresolved: ${unresolved.length} requested item(s) require review`);
142
+ if (missingLocations > 0)
143
+ limitations.push(`kartograph-unresolved-locations: ${missingLocations} symbol location(s) omitted`);
144
+ if (partialLocations > 0)
145
+ limitations.push(`kartograph-partial-source-locations: ${partialLocations} JVM symbol location(s) lack exact line or column coordinates`);
146
+ if (omittedPaths > 0)
147
+ limitations.push(`kartograph-unrepresented-paths: ${omittedPaths} path or symbol observation(s) require the original producer report`);
148
+ truncated ||= omittedPaths > 0;
149
+ return validateLanguageImpact({ id: metadata.id, platform: 'kotlin', requested: metadata.requested,
150
+ tool: metadata.tool, ...(metadata.trigger === undefined ? {} : { trigger: metadata.trigger }),
151
+ roots, affected: rows, limitations, truncated });
152
+ }
153
+ function object(value) {
154
+ if (value === null || typeof value !== 'object' || Array.isArray(value))
155
+ fail('Invalid Kartograph impact object.');
156
+ return value;
157
+ }
158
+ function array(value) {
159
+ if (!Array.isArray(value) || value.length > 50_000)
160
+ fail('Invalid Kartograph impact array.');
161
+ return value;
162
+ }
163
+ function string(value) {
164
+ if (!isSafeNonEmptyString(value))
165
+ fail('Invalid Kartograph impact string.');
166
+ return value;
167
+ }
168
+ function fail(message) { throw new PreflightValidationError(message); }
169
+ //# sourceMappingURL=kartograph-impact.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kartograph-impact.js","sourceRoot":"","sources":["../../src/exchange/kartograph-impact.ts"],"names":[],"mappings":"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","sourcesContent":["import { PreflightValidationError, validateLanguageImpact, type LanguageImpact, type ImpactSymbol } from './preflight-context.ts';\nimport type { ProducerImpactMetadata } from './producer-impact.ts';\nimport { isProjectRelativePath, isSafeNonEmptyString } from './parse.ts';\nimport { compareStrings } from '../compare.ts';\n\n/** Kartograph의 현재 시점 영향 근거를 공통 사전 점검 형식으로 연결한다. */\nexport function adaptKartographImpact(raw: unknown, metadata: ProducerImpactMetadata): LanguageImpact {\n const value = object(raw);\n if (value.format !== 'kartograph-impact' || value.version !== 1) fail('Expected kartograph-impact version 1.');\n if (!['found', 'partial', 'notFound', 'noChanges'].includes(string(value.status))) fail('Invalid Kartograph impact status.');\n if (!isSafeNonEmptyString(metadata.project)) fail('Invalid Kartograph project metadata.');\n const inputs = object(value.inputs);\n if (inputs.base !== undefined) fail('Kartograph preflight requires a current-only impact report.');\n const limitations = array(value.limitations).map((item) => string(item));\n const flags = object(value.truncated);\n for (const key of ['results', 'depth', 'budget']) if (typeof flags[key] !== 'boolean') fail('Invalid Kartograph truncation flag.');\n const navigation = value.navigation === undefined ? undefined : object(value.navigation);\n if (navigation !== undefined && (!Number.isSafeInteger(navigation.offset) || (navigation.offset as number) < 0 ||\n typeof navigation.hasNext !== 'boolean' || typeof navigation.hasPrevious !== 'boolean')) fail('Invalid Kartograph navigation.');\n let truncated = Object.values(flags).some((flag) => flag === true) || value.status === 'partial' ||\n (navigation !== undefined && (navigation.offset !== 0 || navigation.hasNext === true || navigation.hasPrevious === true));\n let missingLocations = 0;\n let partialLocations = 0;\n let omittedPaths = 0;\n let pathItems = 0;\n const symbols = new Map<string, ImpactSymbol>();\n const changed = array(value.changed).map(object);\n const affected = array(value.affected).map(object);\n for (const row of [...changed, ...affected]) {\n const id = string(row.usr);\n if (symbols.has(id)) fail('Kartograph impact symbol ids must be unique.');\n const current = array(row.presentIn).map(string).includes('current');\n if (!current) { omittedPaths++; continue; }\n let location;\n if (row.location !== null && row.location !== undefined) {\n const source = object(row.location);\n const path = typeof source.path === 'string' && source.path.startsWith(`${metadata.project}/`)\n ? source.path.slice(metadata.project.length + 1) : source.path;\n if (isProjectRelativePath(path)) {\n const line = Number.isSafeInteger(source.line) && (source.line as number) > 0 ? source.line as number : undefined;\n const column = line !== undefined && Number.isSafeInteger(source.column) && (source.column as number) > 0\n ? source.column as number : undefined;\n location = { path, ...(line === undefined ? {} : { line }), ...(column === undefined ? {} : { column }) };\n if (column === undefined) partialLocations++;\n }\n }\n if (location === undefined) missingLocations++;\n symbols.set(id, { id, qualifiedName: string(row.qualifiedName),\n ...(row.kind === undefined ? {} : { kind: string(row.kind) }), ...(location === undefined ? {} : { location }) });\n }\n const roots = changed.flatMap((row) => symbols.has(string(row.usr)) ? [symbols.get(string(row.usr))!] : []);\n const rootIds = new Set(roots.map(({ id }) => id));\n const adjacency = new Map<string, Map<string, Set<string>>>();\n for (const row of affected) {\n omittedPaths += row.pathOmissions === undefined ? 0 : array(row.pathOmissions).length;\n if (row.pathStatus !== undefined && row.pathStatus !== 'complete') truncated = true;\n for (const input of array(row.paths)) {\n const path = object(input);\n const revision = string(path.revision);\n if (revision === 'base') { omittedPaths++; continue; }\n if (revision !== 'current') fail('Invalid Kartograph path revision.');\n const nodes = array(path.nodes).map(string);\n const edges = array(path.edges).map(object);\n pathItems += nodes.length + edges.length;\n if (pathItems > 1_000_000) fail('Kartograph impact paths exceed their budget.');\n if (nodes.length < 2 || nodes.length > 129 || edges.length !== nodes.length - 1 || nodes[0] !== row.usr ||\n nodes.at(-1) !== path.changed || new Set(nodes).size !== nodes.length) fail('Invalid Kartograph impact path.');\n const relationships = edges.map((edge, index) => {\n const source = string(edge.source); const target = string(edge.target);\n const forward = source === nodes[index] && target === nodes[index + 1];\n const reverse = target === nodes[index] && source === nodes[index + 1];\n if ((!forward && !reverse) || (reverse && edge.kind !== 'override') ||\n edge.traversal !== (forward ? 'dependency' : 'overrideContract')) {\n fail('Kartograph path edges do not match their traversal.');\n }\n return [string(edge.kind), `origin:${string(edge.origin)}`, `traversal:${string(edge.traversal)}`];\n });\n if (!rootIds.has(string(path.changed)) || nodes.some((id) => !symbols.has(id))) { omittedPaths++; continue; }\n for (let index = 0; index < edges.length; index++) {\n const parent = nodes[index + 1]!; const child = nodes[index]!;\n let children = adjacency.get(parent);\n if (children === undefined) { children = new Map(); adjacency.set(parent, children); }\n let reasons = children.get(child);\n if (reasons === undefined) { reasons = new Set(); children.set(child, reasons); }\n for (const reason of relationships[index]!) reasons.add(reason);\n }\n }\n }\n // 같은 current 그래프의 관찰 간선만 사용한다. 한 정점의 대표 경로는 가장 짧은 경로다.\n const queue = [...rootIds].sort(compareStrings);\n const depth = new Map(queue.map((id) => [id, 0]));\n const rows: Array<LanguageImpact['affected'][number]> = [];\n for (let index = 0; index < queue.length; index++) {\n const parent = queue[index]!;\n for (const [id, reasons] of [...(adjacency.get(parent) ?? [])].sort(([a], [b]) => compareStrings(a, b))) {\n if (depth.has(id)) continue;\n const distance = depth.get(parent)! + 1;\n if (distance > 128) { omittedPaths++; continue; }\n depth.set(id, distance); queue.push(id);\n rows.push({ symbol: symbols.get(id)!, via: parent, depth: distance, relationships: [...reasons].sort(compareStrings) });\n }\n }\n omittedPaths += affected.filter((row) => symbols.has(string(row.usr)) && !depth.has(string(row.usr))).length;\n const unresolved = array(value.unresolved);\n if (unresolved.length > 0) limitations.push(`kartograph-unresolved: ${unresolved.length} requested item(s) require review`);\n if (missingLocations > 0) limitations.push(`kartograph-unresolved-locations: ${missingLocations} symbol location(s) omitted`);\n if (partialLocations > 0) limitations.push(`kartograph-partial-source-locations: ${partialLocations} JVM symbol location(s) lack exact line or column coordinates`);\n if (omittedPaths > 0) limitations.push(`kartograph-unrepresented-paths: ${omittedPaths} path or symbol observation(s) require the original producer report`);\n truncated ||= omittedPaths > 0;\n return validateLanguageImpact({ id: metadata.id, platform: 'kotlin', requested: metadata.requested,\n tool: metadata.tool, ...(metadata.trigger === undefined ? {} : { trigger: metadata.trigger }),\n roots, affected: rows, limitations, truncated });\n}\n\nfunction object(value: unknown): Record<string, unknown> {\n if (value === null || typeof value !== 'object' || Array.isArray(value)) fail('Invalid Kartograph impact object.');\n return value as Record<string, unknown>;\n}\nfunction array(value: unknown): unknown[] {\n if (!Array.isArray(value) || value.length > 50_000) fail('Invalid Kartograph impact array.');\n return value;\n}\nfunction string(value: unknown): string {\n if (!isSafeNonEmptyString(value)) fail('Invalid Kartograph impact string.');\n return value;\n}\nfunction fail(message: string): never { throw new PreflightValidationError(message); }\n"]}