mergestory 0.0.0-stage → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/NOTICE +2 -0
- package/README.md +48 -2
- package/assets/kokoro-plugin/package-lock.json +1139 -0
- package/dist/analyzer/analyze.d.ts +8 -0
- package/dist/analyzer/analyze.js +328 -0
- package/dist/analyzer/components.d.ts +18 -0
- package/dist/analyzer/components.js +266 -0
- package/dist/analyzer/dependencies.d.ts +13 -0
- package/dist/analyzer/dependencies.js +349 -0
- package/dist/analyzer/evidence.d.ts +15 -0
- package/dist/analyzer/evidence.js +73 -0
- package/dist/analyzer/hunks.d.ts +21 -0
- package/dist/analyzer/hunks.js +88 -0
- package/dist/analyzer/imports.d.ts +46 -0
- package/dist/analyzer/imports.js +87 -0
- package/dist/analyzer/index.d.ts +14 -0
- package/dist/analyzer/index.js +16 -0
- package/dist/analyzer/languages/go.d.ts +2 -0
- package/dist/analyzer/languages/go.js +76 -0
- package/dist/analyzer/languages/index.d.ts +18 -0
- package/dist/analyzer/languages/index.js +72 -0
- package/dist/analyzer/languages/javascript.d.ts +2 -0
- package/dist/analyzer/languages/javascript.js +336 -0
- package/dist/analyzer/languages/jvm.d.ts +2 -0
- package/dist/analyzer/languages/jvm.js +157 -0
- package/dist/analyzer/languages/others.d.ts +2 -0
- package/dist/analyzer/languages/others.js +40 -0
- package/dist/analyzer/languages/php.d.ts +2 -0
- package/dist/analyzer/languages/php.js +81 -0
- package/dist/analyzer/languages/python.d.ts +2 -0
- package/dist/analyzer/languages/python.js +180 -0
- package/dist/analyzer/languages/ruby.d.ts +2 -0
- package/dist/analyzer/languages/ruby.js +99 -0
- package/dist/analyzer/languages/rust.d.ts +2 -0
- package/dist/analyzer/languages/rust.js +164 -0
- package/dist/analyzer/languages/shared.d.ts +35 -0
- package/dist/analyzer/languages/shared.js +43 -0
- package/dist/analyzer/languages/types.d.ts +78 -0
- package/dist/analyzer/languages/types.js +2 -0
- package/dist/analyzer/pack-format.d.ts +27 -0
- package/dist/analyzer/pack-format.js +139 -0
- package/dist/analyzer/pack.d.ts +26 -0
- package/dist/analyzer/pack.js +498 -0
- package/dist/analyzer/relationships.d.ts +17 -0
- package/dist/analyzer/relationships.js +34 -0
- package/dist/analyzer/risk-rules.d.ts +287 -0
- package/dist/analyzer/risk-rules.js +373 -0
- package/dist/analyzer/risks.d.ts +36 -0
- package/dist/analyzer/risks.js +480 -0
- package/dist/analyzer/roles.d.ts +11 -0
- package/dist/analyzer/roles.js +280 -0
- package/dist/analyzer/scan.d.ts +26 -0
- package/dist/analyzer/scan.js +157 -0
- package/dist/analyzer/signals.d.ts +20 -0
- package/dist/analyzer/signals.js +51 -0
- package/dist/analyzer/symbol-changes.d.ts +11 -0
- package/dist/analyzer/symbol-changes.js +101 -0
- package/dist/analyzer/symbol-types.d.ts +17 -0
- package/dist/analyzer/symbol-types.js +9 -0
- package/dist/analyzer/symbols.d.ts +4 -0
- package/dist/analyzer/symbols.js +16 -0
- package/dist/analyzer/trivial.d.ts +13 -0
- package/dist/analyzer/trivial.js +137 -0
- package/dist/analyzer/util.d.ts +6 -0
- package/dist/analyzer/util.js +24 -0
- package/dist/cli/commands/authoring.d.ts +9 -0
- package/dist/cli/commands/authoring.js +98 -0
- package/dist/cli/commands/cache.d.ts +4 -0
- package/dist/cli/commands/cache.js +37 -0
- package/dist/cli/commands/doctor.d.ts +5 -0
- package/dist/cli/commands/doctor.js +37 -0
- package/dist/cli/commands/generate.d.ts +8 -0
- package/dist/cli/commands/generate.js +69 -0
- package/dist/cli/commands/jobs.d.ts +8 -0
- package/dist/cli/commands/jobs.js +70 -0
- package/dist/cli/commands/publish.d.ts +8 -0
- package/dist/cli/commands/publish.js +120 -0
- package/dist/cli/commands/serve.d.ts +4 -0
- package/dist/cli/commands/serve.js +69 -0
- package/dist/cli/commands/setup.d.ts +14 -0
- package/dist/cli/commands/setup.js +108 -0
- package/dist/cli/commands/shared.d.ts +18 -0
- package/dist/cli/commands/shared.js +24 -0
- package/dist/cli/files.d.ts +26 -0
- package/dist/cli/files.js +98 -0
- package/dist/cli/format.d.ts +55 -0
- package/dist/cli/format.js +295 -0
- package/dist/cli/inputs.d.ts +23 -0
- package/dist/cli/inputs.js +96 -0
- package/dist/cli/io.d.ts +19 -0
- package/dist/cli/io.js +31 -0
- package/dist/cli/main.d.ts +2 -0
- package/dist/cli/main.js +15 -0
- package/dist/cli/options.d.ts +105 -0
- package/dist/cli/options.js +324 -0
- package/dist/cli/program.d.ts +12 -0
- package/dist/cli/program.js +146 -0
- package/dist/cli/runtime.d.ts +59 -0
- package/dist/cli/runtime.js +83 -0
- package/dist/cli/style.d.ts +13 -0
- package/dist/cli/style.js +27 -0
- package/dist/core/binaries.d.ts +7 -0
- package/dist/core/binaries.js +33 -0
- package/dist/core/binary-paths.d.ts +5 -0
- package/dist/core/binary-paths.js +92 -0
- package/dist/core/cache.d.ts +14 -0
- package/dist/core/cache.js +43 -0
- package/dist/core/child-env.d.ts +9 -0
- package/dist/core/child-env.js +61 -0
- package/dist/core/concurrency.d.ts +13 -0
- package/dist/core/concurrency.js +58 -0
- package/dist/core/config.d.ts +223 -0
- package/dist/core/config.js +454 -0
- package/dist/core/context.d.ts +39 -0
- package/dist/core/context.js +44 -0
- package/dist/core/cost.d.ts +46 -0
- package/dist/core/cost.js +108 -0
- package/dist/core/diagram-metrics.d.ts +57 -0
- package/dist/core/diagram-metrics.js +63 -0
- package/dist/core/diff-view.d.ts +44 -0
- package/dist/core/diff-view.js +136 -0
- package/dist/core/errors.d.ts +47 -0
- package/dist/core/errors.js +94 -0
- package/dist/core/ids.d.ts +11 -0
- package/dist/core/ids.js +36 -0
- package/dist/core/json.d.ts +8 -0
- package/dist/core/json.js +17 -0
- package/dist/core/languages.d.ts +19 -0
- package/dist/core/languages.js +292 -0
- package/dist/core/logger.d.ts +29 -0
- package/dist/core/logger.js +68 -0
- package/dist/core/net.d.ts +16 -0
- package/dist/core/net.js +80 -0
- package/dist/core/path-policy.d.ts +20 -0
- package/dist/core/path-policy.js +202 -0
- package/dist/core/paths.d.ts +18 -0
- package/dist/core/paths.js +53 -0
- package/dist/core/posix.d.ts +12 -0
- package/dist/core/posix.js +50 -0
- package/dist/core/process.d.ts +22 -0
- package/dist/core/process.js +117 -0
- package/dist/core/providers.d.ts +204 -0
- package/dist/core/providers.js +2 -0
- package/dist/core/redact.d.ts +11 -0
- package/dist/core/redact.js +70 -0
- package/dist/core/render-plan.d.ts +307 -0
- package/dist/core/render-plan.js +7 -0
- package/dist/core/retry.d.ts +54 -0
- package/dist/core/retry.js +116 -0
- package/dist/core/run-folder.d.ts +74 -0
- package/dist/core/run-folder.js +129 -0
- package/dist/core/secrets.d.ts +56 -0
- package/dist/core/secrets.js +301 -0
- package/dist/core/storyboard-facts.d.ts +22 -0
- package/dist/core/storyboard-facts.js +61 -0
- package/dist/core/storyboard-schema.d.ts +1279 -0
- package/dist/core/storyboard-schema.js +342 -0
- package/dist/core/text.d.ts +43 -0
- package/dist/core/text.js +94 -0
- package/dist/core/types.d.ts +459 -0
- package/dist/core/types.js +17 -0
- package/dist/doctor/check.d.ts +6 -0
- package/dist/doctor/check.js +18 -0
- package/dist/doctor/deps.d.ts +2 -0
- package/dist/doctor/deps.js +90 -0
- package/dist/doctor/format.d.ts +9 -0
- package/dist/doctor/format.js +19 -0
- package/dist/doctor/github.d.ts +2 -0
- package/dist/doctor/github.js +13 -0
- package/dist/doctor/home.d.ts +2 -0
- package/dist/doctor/home.js +26 -0
- package/dist/doctor/index.d.ts +18 -0
- package/dist/doctor/index.js +81 -0
- package/dist/doctor/llm.d.ts +2 -0
- package/dist/doctor/llm.js +32 -0
- package/dist/doctor/publish.d.ts +2 -0
- package/dist/doctor/publish.js +16 -0
- package/dist/doctor/s3.d.ts +2 -0
- package/dist/doctor/s3.js +125 -0
- package/dist/doctor/speech.d.ts +2 -0
- package/dist/doctor/speech.js +112 -0
- package/dist/doctor/tools.d.ts +5 -0
- package/dist/doctor/tools.js +58 -0
- package/dist/doctor/types.d.ts +92 -0
- package/dist/doctor/types.js +2 -0
- package/dist/doctor/youtube.d.ts +2 -0
- package/dist/doctor/youtube.js +73 -0
- package/dist/github/client.d.ts +108 -0
- package/dist/github/client.js +499 -0
- package/dist/github/errors.d.ts +37 -0
- package/dist/github/errors.js +155 -0
- package/dist/github/index.d.ts +13 -0
- package/dist/github/index.js +9 -0
- package/dist/github/linked-issues.d.ts +37 -0
- package/dist/github/linked-issues.js +131 -0
- package/dist/github/model.d.ts +81 -0
- package/dist/github/model.js +114 -0
- package/dist/github/notify.d.ts +27 -0
- package/dist/github/notify.js +118 -0
- package/dist/github/pr-url.d.ts +27 -0
- package/dist/github/pr-url.js +122 -0
- package/dist/github/token.d.ts +16 -0
- package/dist/github/token.js +69 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.js +19 -0
- package/dist/jobs/input-rules.d.ts +20 -0
- package/dist/jobs/input-rules.js +100 -0
- package/dist/jobs/model.d.ts +47 -0
- package/dist/jobs/model.js +18 -0
- package/dist/jobs/runner.d.ts +43 -0
- package/dist/jobs/runner.js +165 -0
- package/dist/jobs/store.d.ts +67 -0
- package/dist/jobs/store.js +314 -0
- package/dist/jobs/view.d.ts +48 -0
- package/dist/jobs/view.js +111 -0
- package/dist/jobs/worker.d.ts +24 -0
- package/dist/jobs/worker.js +47 -0
- package/dist/llm/anthropic.d.ts +11 -0
- package/dist/llm/anthropic.js +225 -0
- package/dist/llm/index.d.ts +10 -0
- package/dist/llm/index.js +27 -0
- package/dist/llm/openai-compatible.d.ts +33 -0
- package/dist/llm/openai-compatible.js +378 -0
- package/dist/llm/prices.d.ts +26 -0
- package/dist/llm/prices.js +41 -0
- package/dist/llm/shared.d.ts +30 -0
- package/dist/llm/shared.js +115 -0
- package/dist/narration/assemble.d.ts +36 -0
- package/dist/narration/assemble.js +282 -0
- package/dist/narration/audio.d.ts +61 -0
- package/dist/narration/audio.js +301 -0
- package/dist/narration/captions.d.ts +37 -0
- package/dist/narration/captions.js +195 -0
- package/dist/narration/chunking.d.ts +6 -0
- package/dist/narration/chunking.js +46 -0
- package/dist/narration/convert.d.ts +9 -0
- package/dist/narration/convert.js +90 -0
- package/dist/narration/elevenlabs.d.ts +5 -0
- package/dist/narration/elevenlabs.js +53 -0
- package/dist/narration/http-errors.d.ts +14 -0
- package/dist/narration/http-errors.js +74 -0
- package/dist/narration/index.d.ts +21 -0
- package/dist/narration/index.js +22 -0
- package/dist/narration/kokoro-install.d.ts +30 -0
- package/dist/narration/kokoro-install.js +284 -0
- package/dist/narration/kokoro-model.d.ts +54 -0
- package/dist/narration/kokoro-model.js +168 -0
- package/dist/narration/kokoro-plugin.d.ts +78 -0
- package/dist/narration/kokoro-plugin.js +245 -0
- package/dist/narration/kokoro.d.ts +22 -0
- package/dist/narration/kokoro.js +231 -0
- package/dist/narration/openai.d.ts +4 -0
- package/dist/narration/openai.js +54 -0
- package/dist/narration/pricing.d.ts +8 -0
- package/dist/narration/pricing.js +17 -0
- package/dist/narration/remote-speech.d.ts +53 -0
- package/dist/narration/remote-speech.js +100 -0
- package/dist/narration/say.d.ts +28 -0
- package/dist/narration/say.js +178 -0
- package/dist/narration/select.d.ts +27 -0
- package/dist/narration/select.js +80 -0
- package/dist/narration/silent.d.ts +2 -0
- package/dist/narration/silent.js +18 -0
- package/dist/narration/speakable.d.ts +20 -0
- package/dist/narration/speakable.js +398 -0
- package/dist/narration/synthesize.d.ts +25 -0
- package/dist/narration/synthesize.js +220 -0
- package/dist/narration/wav.d.ts +37 -0
- package/dist/narration/wav.js +170 -0
- package/dist/pipeline/authoring.d.ts +8 -0
- package/dist/pipeline/authoring.js +117 -0
- package/dist/pipeline/commands.d.ts +9 -0
- package/dist/pipeline/commands.js +21 -0
- package/dist/pipeline/fit-duration.d.ts +41 -0
- package/dist/pipeline/fit-duration.js +150 -0
- package/dist/pipeline/index.d.ts +9 -0
- package/dist/pipeline/index.js +9 -0
- package/dist/pipeline/inputs.d.ts +42 -0
- package/dist/pipeline/inputs.js +171 -0
- package/dist/pipeline/plan.d.ts +8 -0
- package/dist/pipeline/plan.js +85 -0
- package/dist/pipeline/planned-egress.d.ts +21 -0
- package/dist/pipeline/planned-egress.js +43 -0
- package/dist/pipeline/prepare.d.ts +26 -0
- package/dist/pipeline/prepare.js +84 -0
- package/dist/pipeline/presentation.d.ts +26 -0
- package/dist/pipeline/presentation.js +110 -0
- package/dist/pipeline/publish-run.d.ts +71 -0
- package/dist/pipeline/publish-run.js +295 -0
- package/dist/pipeline/run.d.ts +7 -0
- package/dist/pipeline/run.js +196 -0
- package/dist/pipeline/storyboard-choice.d.ts +34 -0
- package/dist/pipeline/storyboard-choice.js +129 -0
- package/dist/pipeline/types.d.ts +76 -0
- package/dist/pipeline/types.js +2 -0
- package/dist/publish/access-probe.d.ts +55 -0
- package/dist/publish/access-probe.js +249 -0
- package/dist/publish/bundle.d.ts +20 -0
- package/dist/publish/bundle.js +57 -0
- package/dist/publish/comment.d.ts +10 -0
- package/dist/publish/comment.js +184 -0
- package/dist/publish/content-types.d.ts +15 -0
- package/dist/publish/content-types.js +41 -0
- package/dist/publish/index.d.ts +18 -0
- package/dist/publish/index.js +30 -0
- package/dist/publish/keys.d.ts +10 -0
- package/dist/publish/keys.js +30 -0
- package/dist/publish/local.d.ts +33 -0
- package/dist/publish/local.js +143 -0
- package/dist/publish/privacy.d.ts +20 -0
- package/dist/publish/privacy.js +21 -0
- package/dist/publish/s3/bucket.d.ts +22 -0
- package/dist/publish/s3/bucket.js +82 -0
- package/dist/publish/s3/client.d.ts +33 -0
- package/dist/publish/s3/client.js +134 -0
- package/dist/publish/s3/failures.d.ts +22 -0
- package/dist/publish/s3/failures.js +110 -0
- package/dist/publish/s3/index.d.ts +5 -0
- package/dist/publish/s3/index.js +6 -0
- package/dist/publish/s3/links.d.ts +38 -0
- package/dist/publish/s3/links.js +60 -0
- package/dist/publish/s3/protected-links.d.ts +29 -0
- package/dist/publish/s3/protected-links.js +67 -0
- package/dist/publish/s3/publisher.d.ts +32 -0
- package/dist/publish/s3/publisher.js +123 -0
- package/dist/publish/s3/settings.d.ts +29 -0
- package/dist/publish/s3/settings.js +64 -0
- package/dist/publish/s3/upload.d.ts +29 -0
- package/dist/publish/s3/upload.js +145 -0
- package/dist/publish/severity.d.ts +9 -0
- package/dist/publish/severity.js +18 -0
- package/dist/publish/text.d.ts +23 -0
- package/dist/publish/text.js +94 -0
- package/dist/publish/types.d.ts +67 -0
- package/dist/publish/types.js +12 -0
- package/dist/publish/vtt.d.ts +9 -0
- package/dist/publish/vtt.js +43 -0
- package/dist/publish/watch-page.d.ts +4 -0
- package/dist/publish/watch-page.js +463 -0
- package/dist/publish/youtube/credentials.d.ts +33 -0
- package/dist/publish/youtube/credentials.js +162 -0
- package/dist/publish/youtube/description.d.ts +56 -0
- package/dist/publish/youtube/description.js +197 -0
- package/dist/publish/youtube/google-api.d.ts +95 -0
- package/dist/publish/youtube/google-api.js +339 -0
- package/dist/publish/youtube/index.d.ts +7 -0
- package/dist/publish/youtube/index.js +8 -0
- package/dist/publish/youtube/loopback.d.ts +13 -0
- package/dist/publish/youtube/loopback.js +144 -0
- package/dist/publish/youtube/oauth.d.ts +68 -0
- package/dist/publish/youtube/oauth.js +159 -0
- package/dist/publish/youtube/prompt.d.ts +27 -0
- package/dist/publish/youtube/prompt.js +94 -0
- package/dist/publish/youtube/publisher.d.ts +38 -0
- package/dist/publish/youtube/publisher.js +346 -0
- package/dist/publish/youtube/scopes.d.ts +32 -0
- package/dist/publish/youtube/scopes.js +55 -0
- package/dist/publish/youtube/sign-in.d.ts +37 -0
- package/dist/publish/youtube/sign-in.js +137 -0
- package/dist/publish/youtube/upload.d.ts +43 -0
- package/dist/publish/youtube/upload.js +210 -0
- package/dist/render/browser.d.ts +23 -0
- package/dist/render/browser.js +219 -0
- package/dist/render/chromium-renderer.d.ts +56 -0
- package/dist/render/chromium-renderer.js +519 -0
- package/dist/render/ffmpeg.d.ts +55 -0
- package/dist/render/ffmpeg.js +212 -0
- package/dist/render/fonts.d.ts +10 -0
- package/dist/render/fonts.js +42 -0
- package/dist/render/index.d.ts +9 -0
- package/dist/render/index.js +11 -0
- package/dist/render/page.d.ts +46 -0
- package/dist/render/page.js +123 -0
- package/dist/render/playwright.d.ts +3 -0
- package/dist/render/playwright.js +6 -0
- package/dist/render/runtime-bundle.d.ts +2 -0
- package/dist/render/runtime-bundle.js +72 -0
- package/dist/runtime/runtime.js +1626 -0
- package/dist/server/auth.d.ts +17 -0
- package/dist/server/auth.js +58 -0
- package/dist/server/http.d.ts +43 -0
- package/dist/server/http.js +119 -0
- package/dist/server/index.d.ts +46 -0
- package/dist/server/index.js +310 -0
- package/dist/server/queue.d.ts +30 -0
- package/dist/server/queue.js +76 -0
- package/dist/server/schema.d.ts +103 -0
- package/dist/server/schema.js +121 -0
- package/dist/server/source.d.ts +17 -0
- package/dist/server/source.js +100 -0
- package/dist/sources/assemble.d.ts +86 -0
- package/dist/sources/assemble.js +117 -0
- package/dist/sources/diff-parser.d.ts +14 -0
- package/dist/sources/diff-parser.js +84 -0
- package/dist/sources/git.d.ts +28 -0
- package/dist/sources/git.js +108 -0
- package/dist/sources/github.d.ts +20 -0
- package/dist/sources/github.js +192 -0
- package/dist/sources/hunk-selection.d.ts +25 -0
- package/dist/sources/hunk-selection.js +46 -0
- package/dist/sources/index.d.ts +23 -0
- package/dist/sources/index.js +62 -0
- package/dist/sources/limits.d.ts +27 -0
- package/dist/sources/limits.js +22 -0
- package/dist/sources/local.d.ts +34 -0
- package/dist/sources/local.js +555 -0
- package/dist/sources/reader.d.ts +13 -0
- package/dist/sources/reader.js +45 -0
- package/dist/sources/remote.d.ts +13 -0
- package/dist/sources/remote.js +41 -0
- package/dist/sources/repo-context.d.ts +34 -0
- package/dist/sources/repo-context.js +144 -0
- package/dist/sources/text.d.ts +25 -0
- package/dist/sources/text.js +71 -0
- package/dist/storyboard/authoring.d.ts +108 -0
- package/dist/storyboard/authoring.js +196 -0
- package/dist/storyboard/claims.d.ts +35 -0
- package/dist/storyboard/claims.js +318 -0
- package/dist/storyboard/coderef.d.ts +28 -0
- package/dist/storyboard/coderef.js +118 -0
- package/dist/storyboard/generate.d.ts +25 -0
- package/dist/storyboard/generate.js +194 -0
- package/dist/storyboard/heuristic-code.d.ts +91 -0
- package/dist/storyboard/heuristic-code.js +524 -0
- package/dist/storyboard/heuristic-context.d.ts +42 -0
- package/dist/storyboard/heuristic-context.js +102 -0
- package/dist/storyboard/heuristic-facts.d.ts +106 -0
- package/dist/storyboard/heuristic-facts.js +415 -0
- package/dist/storyboard/heuristic-overview.d.ts +4 -0
- package/dist/storyboard/heuristic-overview.js +151 -0
- package/dist/storyboard/heuristic-review.d.ts +10 -0
- package/dist/storyboard/heuristic-review.js +158 -0
- package/dist/storyboard/heuristic-scenes.d.ts +8 -0
- package/dist/storyboard/heuristic-scenes.js +357 -0
- package/dist/storyboard/heuristic-shared.d.ts +43 -0
- package/dist/storyboard/heuristic-shared.js +136 -0
- package/dist/storyboard/heuristic.d.ts +15 -0
- package/dist/storyboard/heuristic.js +324 -0
- package/dist/storyboard/identifiers.d.ts +51 -0
- package/dist/storyboard/identifiers.js +195 -0
- package/dist/storyboard/index.d.ts +10 -0
- package/dist/storyboard/index.js +10 -0
- package/dist/storyboard/prompts.d.ts +16 -0
- package/dist/storyboard/prompts.js +61 -0
- package/dist/storyboard/run.d.ts +82 -0
- package/dist/storyboard/run.js +332 -0
- package/dist/storyboard/text.d.ts +16 -0
- package/dist/storyboard/text.js +103 -0
- package/dist/storyboard/types.d.ts +47 -0
- package/dist/storyboard/types.js +2 -0
- package/dist/storyboard/urls.d.ts +15 -0
- package/dist/storyboard/urls.js +83 -0
- package/dist/storyboard/validate.d.ts +8 -0
- package/dist/storyboard/validate.js +525 -0
- package/dist/storyboard/visuals.d.ts +23 -0
- package/dist/storyboard/visuals.js +324 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.js +3 -0
- package/dist/visuals/code.d.ts +68 -0
- package/dist/visuals/code.js +385 -0
- package/dist/visuals/highlight.d.ts +17 -0
- package/dist/visuals/highlight.js +85 -0
- package/dist/visuals/image.d.ts +12 -0
- package/dist/visuals/image.js +96 -0
- package/dist/visuals/index.d.ts +10 -0
- package/dist/visuals/index.js +10 -0
- package/dist/visuals/layout.d.ts +89 -0
- package/dist/visuals/layout.js +506 -0
- package/dist/visuals/plan.d.ts +33 -0
- package/dist/visuals/plan.js +511 -0
- package/dist/visuals/timeline.d.ts +31 -0
- package/dist/visuals/timeline.js +96 -0
- package/package.json +74 -4
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
/** Dependency changes read from manifest files at base and head. */
|
|
2
|
+
import { basename } from '../core/posix.js';
|
|
3
|
+
/** A requirements file for development: the file name says so, or it sits in a requirements/ folder. */
|
|
4
|
+
const DEV_REQUIREMENTS = /(?:^|\/)requirements[-_.]?(?:dev|test)[^/]*\.txt$|(?:^|\/)requirements\/(?:dev|test)[^/]*$/i;
|
|
5
|
+
const MANIFEST_KINDS = {
|
|
6
|
+
'package.json': {
|
|
7
|
+
ecosystem: 'npm',
|
|
8
|
+
parse: (c) => parsePackageJson(c, [
|
|
9
|
+
['dependencies', false],
|
|
10
|
+
['optionalDependencies', false],
|
|
11
|
+
['peerDependencies', false],
|
|
12
|
+
['devDependencies', true],
|
|
13
|
+
]),
|
|
14
|
+
},
|
|
15
|
+
composer: {
|
|
16
|
+
ecosystem: 'other',
|
|
17
|
+
parse: (c) => parsePackageJson(c, [
|
|
18
|
+
['require', false],
|
|
19
|
+
['require-dev', true],
|
|
20
|
+
]),
|
|
21
|
+
},
|
|
22
|
+
requirements: { ecosystem: 'pypi', parse: (c, p) => parseRequirements(c, DEV_REQUIREMENTS.test(p)) },
|
|
23
|
+
'go.mod': { ecosystem: 'go', parse: parseGoMod },
|
|
24
|
+
cargo: { ecosystem: 'cargo', parse: parseCargo },
|
|
25
|
+
pyproject: { ecosystem: 'pypi', parse: parsePyproject },
|
|
26
|
+
pipfile: { ecosystem: 'pypi', parse: parsePipfile },
|
|
27
|
+
gemfile: { ecosystem: 'rubygems', parse: parseGemfile },
|
|
28
|
+
pom: { ecosystem: 'maven', parse: parsePom },
|
|
29
|
+
};
|
|
30
|
+
export function manifestKind(path) {
|
|
31
|
+
const name = basename(path).toLowerCase();
|
|
32
|
+
if (name === 'package.json')
|
|
33
|
+
return 'package.json';
|
|
34
|
+
if (name === 'composer.json')
|
|
35
|
+
return 'composer';
|
|
36
|
+
if (/^requirements([-_.][\w.-]+)?\.txt$/.test(name) || /(^|\/)requirements\/[^/]+\.txt$/.test(path))
|
|
37
|
+
return 'requirements';
|
|
38
|
+
if (name === 'go.mod')
|
|
39
|
+
return 'go.mod';
|
|
40
|
+
if (name === 'cargo.toml')
|
|
41
|
+
return 'cargo';
|
|
42
|
+
if (name === 'pyproject.toml')
|
|
43
|
+
return 'pyproject';
|
|
44
|
+
if (name === 'pipfile')
|
|
45
|
+
return 'pipfile';
|
|
46
|
+
if (name === 'gemfile')
|
|
47
|
+
return 'gemfile';
|
|
48
|
+
if (name === 'pom.xml')
|
|
49
|
+
return 'pom';
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
function parsePackageJson(content, sections) {
|
|
53
|
+
let json;
|
|
54
|
+
try {
|
|
55
|
+
json = JSON.parse(content);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return [];
|
|
59
|
+
}
|
|
60
|
+
if (!json || typeof json !== 'object')
|
|
61
|
+
return [];
|
|
62
|
+
const seen = new Set();
|
|
63
|
+
const out = [];
|
|
64
|
+
for (const [section, dev] of sections) {
|
|
65
|
+
const table = json[section];
|
|
66
|
+
if (!table || typeof table !== 'object')
|
|
67
|
+
continue;
|
|
68
|
+
for (const [name, version] of Object.entries(table)) {
|
|
69
|
+
if (typeof version !== 'string' || name === 'php' || name.startsWith('ext-'))
|
|
70
|
+
continue;
|
|
71
|
+
const key = `${dev ? 'dev' : 'prod'}:${name}`;
|
|
72
|
+
if (seen.has(key) || (!dev && seen.has(`prod:${name}`)))
|
|
73
|
+
continue;
|
|
74
|
+
seen.add(key);
|
|
75
|
+
out.push({ name, version, dev });
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return out;
|
|
79
|
+
}
|
|
80
|
+
const REQUIREMENT = /^([A-Za-z0-9][A-Za-z0-9._-]*)(?:\[[^\]]*\])?\s*([<>=!~][^;#]*)?/;
|
|
81
|
+
function requirementEntry(text, dev) {
|
|
82
|
+
const m = REQUIREMENT.exec(text.trim());
|
|
83
|
+
if (!m)
|
|
84
|
+
return null;
|
|
85
|
+
const spec = (m[2] ?? '').replace(/\s+/g, '');
|
|
86
|
+
return {
|
|
87
|
+
name: m[1].toLowerCase().replace(/_/g, '-'),
|
|
88
|
+
version: spec.replace(/^==/, '') || '*',
|
|
89
|
+
dev,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
function parseRequirements(content, dev) {
|
|
93
|
+
const out = [];
|
|
94
|
+
for (const raw of content.split(/\r?\n/)) {
|
|
95
|
+
const line = raw.trim();
|
|
96
|
+
if (!line || line.startsWith('#') || line.startsWith('-') || /^(git\+|https?:|\.|\/)/.test(line))
|
|
97
|
+
continue;
|
|
98
|
+
const entry = requirementEntry(line, dev);
|
|
99
|
+
if (entry)
|
|
100
|
+
out.push(entry);
|
|
101
|
+
}
|
|
102
|
+
return out;
|
|
103
|
+
}
|
|
104
|
+
function parseGoMod(content) {
|
|
105
|
+
const out = [];
|
|
106
|
+
let inBlock = false;
|
|
107
|
+
for (const raw of content.split(/\r?\n/)) {
|
|
108
|
+
const line = raw.trim();
|
|
109
|
+
if (/^require\s*\($/.test(line)) {
|
|
110
|
+
inBlock = true;
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
if (inBlock && line === ')') {
|
|
114
|
+
inBlock = false;
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
const single = /^require\s+(\S+)\s+(\S+)(.*)$/.exec(line);
|
|
118
|
+
const block = inBlock ? /^(\S+)\s+(v\S+)(.*)$/.exec(line) : null;
|
|
119
|
+
const m = single ?? block;
|
|
120
|
+
if (m && !/\/\/\s*indirect/.test(m[3]))
|
|
121
|
+
out.push({ name: m[1], version: m[2], dev: false });
|
|
122
|
+
}
|
|
123
|
+
return out;
|
|
124
|
+
}
|
|
125
|
+
function stripTomlComment(line) {
|
|
126
|
+
let quote = '';
|
|
127
|
+
for (let i = 0; i < line.length; i++) {
|
|
128
|
+
const ch = line[i];
|
|
129
|
+
if (quote) {
|
|
130
|
+
if (ch === '\\' && quote === '"')
|
|
131
|
+
i++;
|
|
132
|
+
else if (ch === quote)
|
|
133
|
+
quote = '';
|
|
134
|
+
}
|
|
135
|
+
else if (ch === '"' || ch === "'")
|
|
136
|
+
quote = ch;
|
|
137
|
+
else if (ch === '#')
|
|
138
|
+
return line.slice(0, i);
|
|
139
|
+
}
|
|
140
|
+
return line;
|
|
141
|
+
}
|
|
142
|
+
function bracketBalance(text) {
|
|
143
|
+
let depth = 0;
|
|
144
|
+
let quote = '';
|
|
145
|
+
for (let i = 0; i < text.length; i++) {
|
|
146
|
+
const ch = text[i];
|
|
147
|
+
if (quote) {
|
|
148
|
+
if (ch === '\\' && quote === '"')
|
|
149
|
+
i++;
|
|
150
|
+
else if (ch === quote)
|
|
151
|
+
quote = '';
|
|
152
|
+
}
|
|
153
|
+
else if (ch === '"' || ch === "'")
|
|
154
|
+
quote = ch;
|
|
155
|
+
else if (ch === '[' || ch === '{')
|
|
156
|
+
depth++;
|
|
157
|
+
else if (ch === ']' || ch === '}')
|
|
158
|
+
depth--;
|
|
159
|
+
}
|
|
160
|
+
return depth;
|
|
161
|
+
}
|
|
162
|
+
function scanToml(content) {
|
|
163
|
+
const items = [];
|
|
164
|
+
const lines = content.split(/\r?\n/);
|
|
165
|
+
let table = '';
|
|
166
|
+
for (let i = 0; i < lines.length; i++) {
|
|
167
|
+
const line = stripTomlComment(lines[i]).trim();
|
|
168
|
+
if (!line)
|
|
169
|
+
continue;
|
|
170
|
+
const header = /^\[\[?\s*([^\]]+?)\s*\]\]?$/.exec(line);
|
|
171
|
+
if (header) {
|
|
172
|
+
table = header[1].replace(/\s+/g, '').replace(/["']/g, '');
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
const kv = /^([A-Za-z0-9_."'-]+)\s*=\s*(.*)$/.exec(line);
|
|
176
|
+
if (!kv)
|
|
177
|
+
continue;
|
|
178
|
+
let value = kv[2];
|
|
179
|
+
while (bracketBalance(value) > 0 && i + 1 < lines.length) {
|
|
180
|
+
i++;
|
|
181
|
+
value += ` ${stripTomlComment(lines[i]).trim()}`;
|
|
182
|
+
}
|
|
183
|
+
items.push({ table, key: kv[1].replace(/["']/g, ''), value: value.trim() });
|
|
184
|
+
}
|
|
185
|
+
return items;
|
|
186
|
+
}
|
|
187
|
+
function tomlVersion(value) {
|
|
188
|
+
const plain = /^["']([^"']*)["']$/.exec(value);
|
|
189
|
+
if (plain)
|
|
190
|
+
return plain[1] || '*';
|
|
191
|
+
const v = /\bversion\s*=\s*["']([^"']*)["']/.exec(value);
|
|
192
|
+
if (v)
|
|
193
|
+
return v[1];
|
|
194
|
+
if (/\bworkspace\s*=\s*true/.test(value))
|
|
195
|
+
return 'workspace';
|
|
196
|
+
if (/\bpath\s*=/.test(value))
|
|
197
|
+
return 'path';
|
|
198
|
+
if (/\bgit\s*=/.test(value))
|
|
199
|
+
return 'git';
|
|
200
|
+
return '*';
|
|
201
|
+
}
|
|
202
|
+
function parseCargo(content) {
|
|
203
|
+
const out = [];
|
|
204
|
+
for (const { table, key, value } of scanToml(content)) {
|
|
205
|
+
const dev = /dev-dependencies/.test(table);
|
|
206
|
+
if (/(^|\.)(dev-|build-)?dependencies$/.test(table)) {
|
|
207
|
+
const dotted = /^([^.]+)\.workspace$/.exec(key);
|
|
208
|
+
if (dotted)
|
|
209
|
+
out.push({ name: dotted[1], version: 'workspace', dev });
|
|
210
|
+
else
|
|
211
|
+
out.push({ name: key, version: tomlVersion(value), dev });
|
|
212
|
+
}
|
|
213
|
+
else {
|
|
214
|
+
const sub = /^(?:.*\.)?(dev-|build-)?dependencies\.([^.]+)$/.exec(table);
|
|
215
|
+
if (sub && key === 'version')
|
|
216
|
+
out.push({ name: sub[2], version: tomlVersion(value), dev: sub[1] === 'dev-' });
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
return out;
|
|
220
|
+
}
|
|
221
|
+
function arrayEntries(value, dev) {
|
|
222
|
+
const out = [];
|
|
223
|
+
for (const m of value.matchAll(/["']([^"']+)["']/g)) {
|
|
224
|
+
const entry = requirementEntry(m[1], dev);
|
|
225
|
+
if (entry)
|
|
226
|
+
out.push(entry);
|
|
227
|
+
}
|
|
228
|
+
return out;
|
|
229
|
+
}
|
|
230
|
+
function parsePyproject(content) {
|
|
231
|
+
const out = [];
|
|
232
|
+
for (const { table, key, value } of scanToml(content)) {
|
|
233
|
+
if (table === 'project' && key === 'dependencies')
|
|
234
|
+
out.push(...arrayEntries(value, false));
|
|
235
|
+
else if (table === 'project.optional-dependencies')
|
|
236
|
+
out.push(...arrayEntries(value, /dev|test|lint|doc|type/i.test(key)));
|
|
237
|
+
else if (table === 'dependency-groups')
|
|
238
|
+
out.push(...arrayEntries(value, true));
|
|
239
|
+
else if (table === 'tool.uv' && key === 'dev-dependencies')
|
|
240
|
+
out.push(...arrayEntries(value, true));
|
|
241
|
+
else if (/^tool\.poetry\.(dependencies|dev-dependencies|group\.[^.]+\.dependencies)$/.test(table) &&
|
|
242
|
+
key !== 'python') {
|
|
243
|
+
out.push({
|
|
244
|
+
name: key.toLowerCase().replace(/_/g, '-'),
|
|
245
|
+
version: tomlVersion(value),
|
|
246
|
+
dev: !/^tool\.poetry\.dependencies$/.test(table),
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
return out;
|
|
251
|
+
}
|
|
252
|
+
function parsePipfile(content) {
|
|
253
|
+
const out = [];
|
|
254
|
+
for (const { table, key, value } of scanToml(content)) {
|
|
255
|
+
if (table === 'packages' || table === 'dev-packages') {
|
|
256
|
+
out.push({
|
|
257
|
+
name: key.toLowerCase().replace(/_/g, '-'),
|
|
258
|
+
version: tomlVersion(value).replace(/^==/, ''),
|
|
259
|
+
dev: table === 'dev-packages',
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
return out;
|
|
264
|
+
}
|
|
265
|
+
function parseGemfile(content) {
|
|
266
|
+
const out = [];
|
|
267
|
+
const groups = [];
|
|
268
|
+
for (const raw of content.split(/\r?\n/)) {
|
|
269
|
+
const line = raw.trim();
|
|
270
|
+
if (/^group\b.*\bdo\b/.test(line)) {
|
|
271
|
+
groups.push(/:(development|test)\b/.test(line));
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
if (/^end\b/.test(line) && groups.length > 0) {
|
|
275
|
+
groups.pop();
|
|
276
|
+
continue;
|
|
277
|
+
}
|
|
278
|
+
const gem = /^gem\s+['"]([^'"]+)['"](?:\s*,\s*['"]([^'"]+)['"])?/.exec(line);
|
|
279
|
+
if (gem)
|
|
280
|
+
out.push({ name: gem[1], version: gem[2] ?? '*', dev: groups.some(Boolean) });
|
|
281
|
+
}
|
|
282
|
+
return out;
|
|
283
|
+
}
|
|
284
|
+
function parsePom(content) {
|
|
285
|
+
const out = [];
|
|
286
|
+
for (const block of content.matchAll(/<dependency>([\s\S]*?)<\/dependency>/g)) {
|
|
287
|
+
const text = block[1];
|
|
288
|
+
const tag = (name) => new RegExp(`<${name}>\\s*([^<]*?)\\s*</${name}>`).exec(text)?.[1] ?? null;
|
|
289
|
+
const group = tag('groupId');
|
|
290
|
+
const artifact = tag('artifactId');
|
|
291
|
+
if (!group || !artifact)
|
|
292
|
+
continue;
|
|
293
|
+
out.push({ name: `${group}:${artifact}`, version: tag('version') ?? '*', dev: tag('scope') === 'test' });
|
|
294
|
+
}
|
|
295
|
+
return out;
|
|
296
|
+
}
|
|
297
|
+
/** Dependency changes in one manifest file. `base` and `head` are file contents (null when absent). */
|
|
298
|
+
export function diffManifest(file, base, head) {
|
|
299
|
+
const kind = manifestKind(file.path);
|
|
300
|
+
if (!kind)
|
|
301
|
+
return [];
|
|
302
|
+
const { ecosystem, parse } = MANIFEST_KINDS[kind];
|
|
303
|
+
const toMap = (content, path) => {
|
|
304
|
+
const map = new Map();
|
|
305
|
+
if (content === null)
|
|
306
|
+
return map;
|
|
307
|
+
for (const e of parse(content, path))
|
|
308
|
+
if (!map.has(`${e.dev ? 'dev' : 'prod'}:${e.name}`))
|
|
309
|
+
map.set(`${e.dev ? 'dev' : 'prod'}:${e.name}`, e);
|
|
310
|
+
return map;
|
|
311
|
+
};
|
|
312
|
+
const before = toMap(base, file.oldPath ?? file.path);
|
|
313
|
+
const after = toMap(head, file.path);
|
|
314
|
+
const out = [];
|
|
315
|
+
const make = (change, entry, from, to) => ({
|
|
316
|
+
ecosystem,
|
|
317
|
+
name: entry.name,
|
|
318
|
+
change,
|
|
319
|
+
from,
|
|
320
|
+
to,
|
|
321
|
+
dev: entry.dev,
|
|
322
|
+
fileId: file.id,
|
|
323
|
+
});
|
|
324
|
+
for (const [key, h] of after) {
|
|
325
|
+
const b = before.get(key);
|
|
326
|
+
if (!b)
|
|
327
|
+
out.push(make('added', h, null, h.version));
|
|
328
|
+
else if (b.version !== h.version)
|
|
329
|
+
out.push(make('updated', h, b.version, h.version));
|
|
330
|
+
}
|
|
331
|
+
for (const [key, b] of before)
|
|
332
|
+
if (!after.has(key))
|
|
333
|
+
out.push(make('removed', b, b.version, null));
|
|
334
|
+
// A dependency that moved between dev and prod with the same version is not a change.
|
|
335
|
+
const moved = new Set();
|
|
336
|
+
for (const a of out) {
|
|
337
|
+
if (a.change !== 'added')
|
|
338
|
+
continue;
|
|
339
|
+
const r = out.find((x) => x.change === 'removed' && x.name === a.name && x.from === a.to && x.dev !== a.dev);
|
|
340
|
+
if (r) {
|
|
341
|
+
moved.add(`added:${a.name}:${a.dev}`);
|
|
342
|
+
moved.add(`removed:${r.name}:${r.dev}`);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
return out
|
|
346
|
+
.filter((c) => !moved.has(`${c.change}:${c.name}:${c.dev}`))
|
|
347
|
+
.sort((a, b) => a.name.localeCompare(b.name) || a.change.localeCompare(b.change));
|
|
348
|
+
}
|
|
349
|
+
//# sourceMappingURL=dependencies.js.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** The evidence index: every citable fact, with a short label and a link when one exists. */
|
|
2
|
+
import type { ChangeSet, Component, ContextInputs, DependencyChange, EvidenceItem, RiskSignal, SymbolChange } from '../core/types.ts';
|
|
3
|
+
/** Mask secrets and e-mail addresses in text that ends up in a label. */
|
|
4
|
+
export declare function cleanLabel(text: string, max: number): string;
|
|
5
|
+
export interface EvidenceSources {
|
|
6
|
+
changeSet: ChangeSet;
|
|
7
|
+
inputs: ContextInputs;
|
|
8
|
+
/** File ids whose content is hidden: they get a file item but no hunk items. */
|
|
9
|
+
hiddenFileIds: ReadonlySet<string>;
|
|
10
|
+
components: readonly Component[];
|
|
11
|
+
symbols: readonly SymbolChange[];
|
|
12
|
+
risks: readonly RiskSignal[];
|
|
13
|
+
dependencies: readonly DependencyChange[];
|
|
14
|
+
}
|
|
15
|
+
export declare function buildEvidenceIndex(src: EvidenceSources): EvidenceItem[];
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/** The evidence index: every citable fact, with a short label and a link when one exists. */
|
|
2
|
+
import { maskPrivate } from '../core/secrets.js';
|
|
3
|
+
import { truncate } from '../core/text.js';
|
|
4
|
+
import { hunkPath, hunkRange } from './hunks.js';
|
|
5
|
+
import { blobUrl, shortSha } from './util.js';
|
|
6
|
+
/** Mask secrets and e-mail addresses in text that ends up in a label. */
|
|
7
|
+
export function cleanLabel(text, max) {
|
|
8
|
+
// Labels always mask e-mail addresses, whatever the privacy setting.
|
|
9
|
+
return truncate(maskPrivate(text.replace(/\s+/g, ' ').trim(), { maskEmails: true }), max);
|
|
10
|
+
}
|
|
11
|
+
export function buildEvidenceIndex(src) {
|
|
12
|
+
const { changeSet, inputs } = src;
|
|
13
|
+
const web = changeSet.repo.webUrl;
|
|
14
|
+
const headSha = changeSet.head.sha;
|
|
15
|
+
const baseSha = changeSet.base.sha;
|
|
16
|
+
const urlFor = (ref) => blobUrl(web, ref.side === 'head' ? headSha : baseSha, ref.path, ref.startLine, ref.endLine);
|
|
17
|
+
const items = [];
|
|
18
|
+
const add = (id, kind, label, ref, url) => {
|
|
19
|
+
items.push({ id, kind, label, ref, url });
|
|
20
|
+
};
|
|
21
|
+
if (changeSet.pr)
|
|
22
|
+
add('PR', 'pr', 'PR description', null, changeSet.pr.url || null);
|
|
23
|
+
if (inputs.task?.trim())
|
|
24
|
+
add('TASK', 'task', 'Task description', null, null);
|
|
25
|
+
if (inputs.notes?.trim())
|
|
26
|
+
add('NOTES', 'notes', 'Implementation notes', null, null);
|
|
27
|
+
for (const t of inputs.tests) {
|
|
28
|
+
add(t.id, 'test', `Test run: ${cleanLabel(t.command, 60)} (${t.passed ? 'passed' : 'failed'}, reported by the caller)`, null, null);
|
|
29
|
+
}
|
|
30
|
+
for (const issue of changeSet.linkedIssues) {
|
|
31
|
+
add(issue.id, 'issue', `Issue #${issue.number}: ${cleanLabel(issue.title, 60)}`, null, issue.url || null);
|
|
32
|
+
}
|
|
33
|
+
for (const c of changeSet.commits) {
|
|
34
|
+
const url = web ? `${web.replace(/\/+$/, '')}/commit/${c.sha}` : null;
|
|
35
|
+
add(c.id, 'commit', `Commit ${shortSha(c.sha)}: ${cleanLabel(c.subject, 70)}`, null, url);
|
|
36
|
+
}
|
|
37
|
+
for (const file of changeSet.files) {
|
|
38
|
+
const side = file.status === 'deleted' ? 'base' : 'head';
|
|
39
|
+
add(file.id, 'file', cleanLabel(file.path, 120), null, blobUrl(web, side === 'head' ? headSha : baseSha, file.path));
|
|
40
|
+
if (src.hiddenFileIds.has(file.id))
|
|
41
|
+
continue;
|
|
42
|
+
for (const hunk of file.hunks) {
|
|
43
|
+
const range = hunkRange(hunk);
|
|
44
|
+
const path = hunkPath(file, range);
|
|
45
|
+
const ref = { path, ...range };
|
|
46
|
+
add(hunk.id, 'hunk', `${cleanLabel(path, 100)}:${range.startLine}-${range.endLine}`, ref, urlFor(ref));
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
for (const s of src.symbols) {
|
|
50
|
+
const ref = { path: s.path, side: s.side, startLine: s.startLine, endLine: s.endLine };
|
|
51
|
+
add(s.id, 'symbol', `${cleanLabel(s.name, 80)} (${cleanLabel(s.path, 80)}:${s.startLine}-${s.endLine})`, ref, urlFor(ref));
|
|
52
|
+
}
|
|
53
|
+
for (const m of src.components) {
|
|
54
|
+
const url = web && m.path !== '.'
|
|
55
|
+
? `${web.replace(/\/+$/, '')}/tree/${headSha}/${m.path.split('/').map(encodeURIComponent).join('/')}`
|
|
56
|
+
: null;
|
|
57
|
+
add(m.id, 'component', `${cleanLabel(m.name, 60)} (${cleanLabel(m.path, 80)})`, null, m.status === 'removed' ? null : url);
|
|
58
|
+
}
|
|
59
|
+
for (const r of src.risks)
|
|
60
|
+
add(r.id, 'risk', cleanLabel(r.title, 100), r.ref, r.ref ? urlFor(r.ref) : null);
|
|
61
|
+
const fileById = new Map(changeSet.files.map((f) => [f.id, f]));
|
|
62
|
+
for (const d of src.dependencies) {
|
|
63
|
+
const file = fileById.get(d.fileId);
|
|
64
|
+
const detail = d.change === 'removed' ? 'removed' : `${d.change} ${d.to ?? ''}`.trim();
|
|
65
|
+
const url = file ? blobUrl(web, file.status === 'deleted' ? baseSha : headSha, file.path) : null;
|
|
66
|
+
add(d.id, 'dependency', `${cleanLabel(d.name, 80)} (${detail})`, null, url);
|
|
67
|
+
}
|
|
68
|
+
for (const s of inputs.screenshots)
|
|
69
|
+
add(s.id, 'screenshot', cleanLabel(s.caption || `Screenshot (${s.moment})`, 100), null, null);
|
|
70
|
+
add('REPO', 'repo', 'Repository structure', null, web);
|
|
71
|
+
return items;
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=evidence.js.map
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/** Hunk ranges and key-hunk ranking. */
|
|
2
|
+
import type { ChangedFile, DiffHunk, FileRole, KeyHunk, RiskSignal, Side, SymbolChange } from '../core/types.ts';
|
|
3
|
+
export declare const MAX_KEY_HUNKS = 12;
|
|
4
|
+
export interface HunkRange {
|
|
5
|
+
side: Side;
|
|
6
|
+
startLine: number;
|
|
7
|
+
endLine: number;
|
|
8
|
+
}
|
|
9
|
+
/** The lines a hunk covers: head side unless the hunk only deletes. */
|
|
10
|
+
export declare function hunkRange(hunk: DiffHunk): HunkRange;
|
|
11
|
+
/** The path a hunk is read at: a hunk that only removes lines is read at base, where a renamed file still has its old path. */
|
|
12
|
+
export declare function hunkPath(file: ChangedFile, range: HunkRange): string;
|
|
13
|
+
export declare function changedLineCount(hunk: DiffHunk): number;
|
|
14
|
+
export interface RankFile {
|
|
15
|
+
file: ChangedFile;
|
|
16
|
+
role: FileRole;
|
|
17
|
+
trivial: boolean;
|
|
18
|
+
hidden: boolean;
|
|
19
|
+
}
|
|
20
|
+
/** Score hunks by size, role and bonuses, and keep the best ones. */
|
|
21
|
+
export declare function rankKeyHunks(files: readonly RankFile[], symbols: readonly SymbolChange[], risks: readonly RiskSignal[]): KeyHunk[];
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { ROLE_WEIGHT } from './roles.js';
|
|
2
|
+
import { commentOnlyHunk } from './trivial.js';
|
|
3
|
+
import { round } from './util.js';
|
|
4
|
+
export const MAX_KEY_HUNKS = 12;
|
|
5
|
+
/** The lines a hunk covers: head side unless the hunk only deletes. */
|
|
6
|
+
export function hunkRange(hunk) {
|
|
7
|
+
const hasAdd = hunk.lines.some((l) => l.kind === 'add');
|
|
8
|
+
if (hasAdd || hunk.oldLines === 0) {
|
|
9
|
+
const start = Math.max(1, hunk.newStart);
|
|
10
|
+
return { side: 'head', startLine: start, endLine: Math.max(start, hunk.newStart + hunk.newLines - 1) };
|
|
11
|
+
}
|
|
12
|
+
const start = Math.max(1, hunk.oldStart);
|
|
13
|
+
return { side: 'base', startLine: start, endLine: Math.max(start, hunk.oldStart + hunk.oldLines - 1) };
|
|
14
|
+
}
|
|
15
|
+
/** The path a hunk is read at: a hunk that only removes lines is read at base, where a renamed file still has its old path. */
|
|
16
|
+
export function hunkPath(file, range) {
|
|
17
|
+
return range.side === 'base' && file.oldPath ? file.oldPath : file.path;
|
|
18
|
+
}
|
|
19
|
+
export function changedLineCount(hunk) {
|
|
20
|
+
let n = 0;
|
|
21
|
+
for (const l of hunk.lines)
|
|
22
|
+
if (l.kind !== 'ctx')
|
|
23
|
+
n++;
|
|
24
|
+
return n;
|
|
25
|
+
}
|
|
26
|
+
/** Score hunks by size, role and bonuses, and keep the best ones. */
|
|
27
|
+
export function rankKeyHunks(files, symbols, risks) {
|
|
28
|
+
const out = [];
|
|
29
|
+
let order = 0;
|
|
30
|
+
for (const { file, role, trivial, hidden } of files) {
|
|
31
|
+
if (hidden || trivial || file.binary)
|
|
32
|
+
continue;
|
|
33
|
+
const weight = ROLE_WEIGHT[role];
|
|
34
|
+
if (weight <= 0)
|
|
35
|
+
continue;
|
|
36
|
+
for (const hunk of file.hunks) {
|
|
37
|
+
order++;
|
|
38
|
+
const changed = changedLineCount(hunk);
|
|
39
|
+
if (changed === 0)
|
|
40
|
+
continue;
|
|
41
|
+
const range = hunkRange(hunk);
|
|
42
|
+
const reasons = [`${changed} changed ${changed === 1 ? 'line' : 'lines'}`];
|
|
43
|
+
let score = (Math.log1p(changed) / Math.log1p(30)) * weight;
|
|
44
|
+
if (role !== 'source')
|
|
45
|
+
reasons.push(`${role} file`);
|
|
46
|
+
if (commentOnlyHunk(file.path, hunk)) {
|
|
47
|
+
score *= 0.3;
|
|
48
|
+
reasons.push('comment-only');
|
|
49
|
+
}
|
|
50
|
+
const newExport = symbols.find((s) => s.fileId === file.id &&
|
|
51
|
+
s.exported &&
|
|
52
|
+
s.change === 'added' &&
|
|
53
|
+
s.side === 'head' &&
|
|
54
|
+
range.side === 'head' &&
|
|
55
|
+
s.startLine <= range.endLine &&
|
|
56
|
+
s.endLine >= range.startLine);
|
|
57
|
+
if (newExport) {
|
|
58
|
+
score += 0.35;
|
|
59
|
+
reasons.push(`new exported ${newExport.kind} ${newExport.name}`);
|
|
60
|
+
}
|
|
61
|
+
const flagged = risks.filter((r) => r.evidence.includes(hunk.id));
|
|
62
|
+
if (flagged.length > 0) {
|
|
63
|
+
score += flagged.some((r) => r.severity === 'high') ? 0.4 : 0.3;
|
|
64
|
+
reasons.push(`risk ${flagged.map((r) => r.id).join(', ')}`);
|
|
65
|
+
}
|
|
66
|
+
if (file.status === 'added') {
|
|
67
|
+
score += 0.2;
|
|
68
|
+
reasons.push('new file');
|
|
69
|
+
}
|
|
70
|
+
out.push({
|
|
71
|
+
id: hunk.id,
|
|
72
|
+
fileId: file.id,
|
|
73
|
+
path: hunkPath(file, range),
|
|
74
|
+
side: range.side,
|
|
75
|
+
startLine: range.startLine,
|
|
76
|
+
endLine: range.endLine,
|
|
77
|
+
score: round(score),
|
|
78
|
+
reasons,
|
|
79
|
+
order,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return out
|
|
84
|
+
.sort((a, b) => b.score - a.score || a.order - b.order)
|
|
85
|
+
.slice(0, MAX_KEY_HUNKS)
|
|
86
|
+
.map(({ order: _order, ...hunk }) => hunk);
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=hunks.js.map
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { ImportIndex, ImportLang, ImportRef } from './languages/types.ts';
|
|
2
|
+
/** The language of the imports in a file, or null when the analyzer does not read them. */
|
|
3
|
+
export declare function importLang(path: string): ImportLang | null;
|
|
4
|
+
/** Raw import references found in a file. Only statements that sit outside full-line comments count. */
|
|
5
|
+
export declare function extractImports(path: string, content: string): ImportRef[];
|
|
6
|
+
export interface ResolverOptions {
|
|
7
|
+
/** Every path known to exist at head: changed files plus the repository tree excerpt. */
|
|
8
|
+
knownPaths: Iterable<string>;
|
|
9
|
+
/** Go modules: module path and the directory holding go.mod ("" for the repository root). */
|
|
10
|
+
goModules?: {
|
|
11
|
+
module: string;
|
|
12
|
+
dir: string;
|
|
13
|
+
}[];
|
|
14
|
+
/** Workspace package name (package.json "name") to its directory. */
|
|
15
|
+
packageDirs?: Map<string, string>;
|
|
16
|
+
}
|
|
17
|
+
/** Resolves import references to repository paths, through the language that made them. */
|
|
18
|
+
export declare class ImportResolver implements ImportIndex {
|
|
19
|
+
readonly known: ReadonlySet<string>;
|
|
20
|
+
readonly byBase: Map<string, string[]>;
|
|
21
|
+
readonly goModules: readonly {
|
|
22
|
+
module: string;
|
|
23
|
+
dir: string;
|
|
24
|
+
}[];
|
|
25
|
+
readonly packageDirs: ReadonlyMap<string, string>;
|
|
26
|
+
constructor(options: ResolverOptions);
|
|
27
|
+
firstKnown(candidates: readonly string[]): string | null;
|
|
28
|
+
resolve(importer: string, ref: ImportRef): string[];
|
|
29
|
+
}
|
|
30
|
+
export interface ResolverOptionsInput {
|
|
31
|
+
/** Directories of the components of the change; the workspace ones among them are looked at. */
|
|
32
|
+
componentPaths: readonly string[];
|
|
33
|
+
/** Which manifests are worth reading: package.json for "js", go.mod for "go". */
|
|
34
|
+
languages: ReadonlySet<ImportLang>;
|
|
35
|
+
/** A workspace manifest need not be part of the change, so no changed file carries a verdict for it: this decides whether it is asked for at all. */
|
|
36
|
+
isExcluded: (path: string) => boolean;
|
|
37
|
+
/** Reads a file at head; null when it does not exist. */
|
|
38
|
+
readHead: (path: string) => Promise<string | null>;
|
|
39
|
+
concurrency: number;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Workspace package names and Go modules, read from the manifests of the workspace directories.
|
|
43
|
+
* The results follow the order of the directories, so the same change always resolves the same way;
|
|
44
|
+
* when two workspaces share a package name, the first one keeps it.
|
|
45
|
+
*/
|
|
46
|
+
export declare function loadResolverOptions(input: ResolverOptionsInput): Promise<Required<Pick<ResolverOptions, 'goModules' | 'packageDirs'>>>;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/** Import statement parsing and resolution, used to draw component-to-component relationships. */
|
|
2
|
+
import { mapLimit } from '../core/concurrency.js';
|
|
3
|
+
import { basename, normalizePosix } from '../core/posix.js';
|
|
4
|
+
import { importSupport, languageSupport } from './languages/index.js';
|
|
5
|
+
const MAX_PARSE_CHARS = 400_000;
|
|
6
|
+
const MAX_WORKSPACE_READS = 12;
|
|
7
|
+
const WORKSPACE_DIR = /^(packages|apps|services|libs|modules|cmd|crates)\/[^/]+$/;
|
|
8
|
+
/** The language of the imports in a file, or null when the analyzer does not read them. */
|
|
9
|
+
export function importLang(path) {
|
|
10
|
+
return languageSupport(path)?.imports?.lang ?? null;
|
|
11
|
+
}
|
|
12
|
+
/** Raw import references found in a file. Only statements that sit outside full-line comments count. */
|
|
13
|
+
export function extractImports(path, content) {
|
|
14
|
+
const imports = languageSupport(path)?.imports;
|
|
15
|
+
if (!imports)
|
|
16
|
+
return [];
|
|
17
|
+
return imports.extract(content.length > MAX_PARSE_CHARS ? content.slice(0, MAX_PARSE_CHARS) : content);
|
|
18
|
+
}
|
|
19
|
+
/** Resolves import references to repository paths, through the language that made them. */
|
|
20
|
+
export class ImportResolver {
|
|
21
|
+
known;
|
|
22
|
+
byBase = new Map();
|
|
23
|
+
goModules;
|
|
24
|
+
packageDirs;
|
|
25
|
+
constructor(options) {
|
|
26
|
+
this.known = new Set(options.knownPaths);
|
|
27
|
+
// Longest module path first, so the most specific module wins whatever order they were found in.
|
|
28
|
+
this.goModules = [...(options.goModules ?? [])].sort((a, b) => b.module.length - a.module.length || a.module.localeCompare(b.module));
|
|
29
|
+
this.packageDirs = new Map([...(options.packageDirs ?? new Map())].sort(([a], [b]) => a.localeCompare(b)));
|
|
30
|
+
for (const p of this.known) {
|
|
31
|
+
const b = basename(p);
|
|
32
|
+
this.byBase.set(b, [...(this.byBase.get(b) ?? []), p]);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
firstKnown(candidates) {
|
|
36
|
+
for (const c of candidates) {
|
|
37
|
+
const n = normalizePosix(c);
|
|
38
|
+
if (n !== null && this.known.has(n))
|
|
39
|
+
return n;
|
|
40
|
+
}
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
resolve(importer, ref) {
|
|
44
|
+
return importSupport(ref.lang)?.resolve(importer, ref, this) ?? [];
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Workspace package names and Go modules, read from the manifests of the workspace directories.
|
|
49
|
+
* The results follow the order of the directories, so the same change always resolves the same way;
|
|
50
|
+
* when two workspaces share a package name, the first one keeps it.
|
|
51
|
+
*/
|
|
52
|
+
export async function loadResolverOptions(input) {
|
|
53
|
+
const { readHead, concurrency } = input;
|
|
54
|
+
const dirs = input.componentPaths
|
|
55
|
+
.filter((p) => WORKSPACE_DIR.test(p))
|
|
56
|
+
.filter((p) => !input.isExcluded(`${p}/package.json`) && !input.isExcluded(`${p}/go.mod`))
|
|
57
|
+
.slice(0, MAX_WORKSPACE_READS);
|
|
58
|
+
const [names, modules] = await Promise.all([
|
|
59
|
+
input.languages.has('js')
|
|
60
|
+
? mapLimit(dirs, concurrency, async (dir) => {
|
|
61
|
+
const text = await readHead(`${dir}/package.json`);
|
|
62
|
+
try {
|
|
63
|
+
const name = JSON.parse(text ?? 'null')?.name;
|
|
64
|
+
return typeof name === 'string' && name ? name : null;
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
return null; // not JSON: ignore
|
|
68
|
+
}
|
|
69
|
+
})
|
|
70
|
+
: [],
|
|
71
|
+
input.languages.has('go')
|
|
72
|
+
? mapLimit(['', ...dirs], concurrency, async (dir) => {
|
|
73
|
+
const text = await readHead(dir ? `${dir}/go.mod` : 'go.mod');
|
|
74
|
+
return text ? (/^module\s+(\S+)/m.exec(text)?.[1] ?? null) : null;
|
|
75
|
+
})
|
|
76
|
+
: [],
|
|
77
|
+
]);
|
|
78
|
+
const packageDirs = new Map();
|
|
79
|
+
names.forEach((name, i) => {
|
|
80
|
+
if (name && !packageDirs.has(name))
|
|
81
|
+
packageDirs.set(name, dirs[i]);
|
|
82
|
+
});
|
|
83
|
+
const goDirs = ['', ...dirs];
|
|
84
|
+
const goModules = modules.flatMap((module, i) => (module ? [{ module, dir: goDirs[i] }] : []));
|
|
85
|
+
return { goModules, packageDirs };
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=imports.js.map
|