kirograph 0.16.1 → 0.19.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 +1 -1
- package/README.md +103 -1605
- package/dist/architecture/layers/dart.js +112 -0
- package/dist/architecture/layers/dart.js.map +7 -0
- package/dist/architecture/layers/index.js +3 -1
- package/dist/architecture/layers/index.js.map +2 -2
- package/dist/bin/banner.js +74 -6
- package/dist/bin/banner.js.map +2 -2
- package/dist/bin/commands/affected.js +40 -5
- package/dist/bin/commands/affected.js.map +2 -2
- package/dist/bin/commands/attack-surface.js +157 -0
- package/dist/bin/commands/attack-surface.js.map +7 -0
- package/dist/bin/commands/benchmark.js +206 -0
- package/dist/bin/commands/benchmark.js.map +7 -0
- package/dist/bin/commands/budget.js +70 -0
- package/dist/bin/commands/budget.js.map +7 -0
- package/dist/bin/commands/communities.js +77 -0
- package/dist/bin/commands/communities.js.map +7 -0
- package/dist/bin/commands/data.js +665 -0
- package/dist/bin/commands/data.js.map +7 -0
- package/dist/bin/commands/dep-confusion.js +119 -0
- package/dist/bin/commands/dep-confusion.js.map +7 -0
- package/dist/bin/commands/export.js +304 -10
- package/dist/bin/commands/export.js.map +2 -2
- package/dist/bin/commands/flows.js +97 -0
- package/dist/bin/commands/flows.js.map +7 -0
- package/dist/bin/commands/help.js +386 -100
- package/dist/bin/commands/help.js.map +2 -2
- package/dist/bin/commands/install.js +90 -8
- package/dist/bin/commands/install.js.map +2 -2
- package/dist/bin/commands/licenses.js +201 -0
- package/dist/bin/commands/licenses.js.map +7 -0
- package/dist/bin/commands/reachability.js +160 -0
- package/dist/bin/commands/reachability.js.map +7 -0
- package/dist/bin/commands/read.js +100 -0
- package/dist/bin/commands/read.js.map +7 -0
- package/dist/bin/commands/refactor.js +116 -0
- package/dist/bin/commands/refactor.js.map +7 -0
- package/dist/bin/commands/remediation.js +133 -0
- package/dist/bin/commands/remediation.js.map +7 -0
- package/dist/bin/commands/sbom.js +76 -0
- package/dist/bin/commands/sbom.js.map +7 -0
- package/dist/bin/commands/security-ci-report.js +267 -0
- package/dist/bin/commands/security-ci-report.js.map +7 -0
- package/dist/bin/commands/security-export.js +1349 -0
- package/dist/bin/commands/security-export.js.map +7 -0
- package/dist/bin/commands/security-flows.js +126 -0
- package/dist/bin/commands/security-flows.js.map +7 -0
- package/dist/bin/commands/security-secrets.js +170 -0
- package/dist/bin/commands/security-secrets.js.map +7 -0
- package/dist/bin/commands/security.js +193 -0
- package/dist/bin/commands/security.js.map +7 -0
- package/dist/bin/commands/staleness.js +144 -0
- package/dist/bin/commands/staleness.js.map +7 -0
- package/dist/bin/commands/status.js +102 -2
- package/dist/bin/commands/status.js.map +2 -2
- package/dist/bin/commands/supply-chain.js +138 -0
- package/dist/bin/commands/supply-chain.js.map +7 -0
- package/dist/bin/commands/uninit.js +109 -60
- package/dist/bin/commands/uninit.js.map +2 -2
- package/dist/bin/commands/vex.js +76 -0
- package/dist/bin/commands/vex.js.map +7 -0
- package/dist/bin/commands/vuln-suppress.js +95 -0
- package/dist/bin/commands/vuln-suppress.js.map +7 -0
- package/dist/bin/commands/vulns.js +382 -0
- package/dist/bin/commands/vulns.js.map +7 -0
- package/dist/bin/installer/auto-detect.js +125 -0
- package/dist/bin/installer/auto-detect.js.map +7 -0
- package/dist/bin/installer/cli-agent.js +28 -5
- package/dist/bin/installer/cli-agent.js.map +2 -2
- package/dist/bin/installer/common.js +47 -0
- package/dist/bin/installer/common.js.map +2 -2
- package/dist/bin/installer/config-prompt.js +39 -1
- package/dist/bin/installer/config-prompt.js.map +2 -2
- package/dist/bin/installer/detect.js +309 -0
- package/dist/bin/installer/detect.js.map +7 -0
- package/dist/bin/installer/index.js +36 -1
- package/dist/bin/installer/index.js.map +2 -2
- package/dist/bin/installer/instructions.js +195 -3
- package/dist/bin/installer/instructions.js.map +2 -2
- package/dist/bin/installer/steering.js +440 -0
- package/dist/bin/installer/steering.js.map +2 -2
- package/dist/bin/installer/targets/aider.js +3 -3
- package/dist/bin/installer/targets/aider.js.map +2 -2
- package/dist/bin/installer/targets/amp.js +3 -3
- package/dist/bin/installer/targets/amp.js.map +2 -2
- package/dist/bin/installer/targets/antigravity.js +35 -6
- package/dist/bin/installer/targets/antigravity.js.map +2 -2
- package/dist/bin/installer/targets/augment.js +3 -3
- package/dist/bin/installer/targets/augment.js.map +2 -2
- package/dist/bin/installer/targets/claude.js +49 -5
- package/dist/bin/installer/targets/claude.js.map +2 -2
- package/dist/bin/installer/targets/cline.js +32 -6
- package/dist/bin/installer/targets/cline.js.map +2 -2
- package/dist/bin/installer/targets/codex.js +64 -12
- package/dist/bin/installer/targets/codex.js.map +2 -2
- package/dist/bin/installer/targets/continue.js +2 -2
- package/dist/bin/installer/targets/continue.js.map +2 -2
- package/dist/bin/installer/targets/copilot-cli.js +101 -0
- package/dist/bin/installer/targets/copilot-cli.js.map +7 -0
- package/dist/bin/installer/targets/copilot.js +31 -5
- package/dist/bin/installer/targets/copilot.js.map +2 -2
- package/dist/bin/installer/targets/cursor.js +4 -4
- package/dist/bin/installer/targets/cursor.js.map +2 -2
- package/dist/bin/installer/targets/devin.js +2 -2
- package/dist/bin/installer/targets/devin.js.map +2 -2
- package/dist/bin/installer/targets/gemini-cli.js +2 -2
- package/dist/bin/installer/targets/gemini-cli.js.map +2 -2
- package/dist/bin/installer/targets/generic.js +2 -14
- package/dist/bin/installer/targets/generic.js.map +2 -2
- package/dist/bin/installer/targets/goose.js +3 -3
- package/dist/bin/installer/targets/goose.js.map +2 -2
- package/dist/bin/installer/targets/index.js +250 -0
- package/dist/bin/installer/targets/index.js.map +2 -2
- package/dist/bin/installer/targets/junie.js +2 -2
- package/dist/bin/installer/targets/junie.js.map +2 -2
- package/dist/bin/installer/targets/kilo.js +2 -2
- package/dist/bin/installer/targets/kilo.js.map +2 -2
- package/dist/bin/installer/targets/kiro.js +3 -3
- package/dist/bin/installer/targets/kiro.js.map +2 -2
- package/dist/bin/installer/targets/opencode.js +2 -2
- package/dist/bin/installer/targets/opencode.js.map +2 -2
- package/dist/bin/installer/targets/openhands.js +3 -3
- package/dist/bin/installer/targets/openhands.js.map +2 -2
- package/dist/bin/installer/targets/qoder.js +91 -0
- package/dist/bin/installer/targets/qoder.js.map +7 -0
- package/dist/bin/installer/targets/qwen.js +96 -0
- package/dist/bin/installer/targets/qwen.js.map +7 -0
- package/dist/bin/installer/targets/replit.js +3 -3
- package/dist/bin/installer/targets/replit.js.map +2 -2
- package/dist/bin/installer/targets/roo.js +2 -2
- package/dist/bin/installer/targets/roo.js.map +2 -2
- package/dist/bin/installer/targets/tabnine.js +3 -3
- package/dist/bin/installer/targets/tabnine.js.map +2 -2
- package/dist/bin/installer/targets/trae.js +5 -5
- package/dist/bin/installer/targets/trae.js.map +2 -2
- package/dist/bin/installer/targets/warp.js +2 -2
- package/dist/bin/installer/targets/warp.js.map +2 -2
- package/dist/bin/installer/targets/windsurf.js +35 -6
- package/dist/bin/installer/targets/windsurf.js.map +2 -2
- package/dist/bin/kirograph.js +74 -6
- package/dist/bin/kirograph.js.map +3 -3
- package/dist/bin/progress.js +30 -0
- package/dist/bin/progress.js.map +2 -2
- package/dist/compression/naive-cost.js +25 -0
- package/dist/compression/naive-cost.js.map +2 -2
- package/dist/compression/tracker.js +91 -2
- package/dist/compression/tracker.js.map +2 -2
- package/dist/compression/types.js.map +1 -1
- package/dist/config.js +126 -3
- package/dist/config.js.map +2 -2
- package/dist/core/pipeline.js +92 -4
- package/dist/core/pipeline.js.map +2 -2
- package/dist/data/filters.js +105 -0
- package/dist/data/filters.js.map +7 -0
- package/dist/data/indexer.js +225 -0
- package/dist/data/indexer.js.map +7 -0
- package/dist/data/linker.js +150 -0
- package/dist/data/linker.js.map +7 -0
- package/dist/data/lint.js +109 -0
- package/dist/data/lint.js.map +7 -0
- package/dist/data/parsers/csv.js +132 -0
- package/dist/data/parsers/csv.js.map +7 -0
- package/dist/data/parsers/excel.js +88 -0
- package/dist/data/parsers/excel.js.map +7 -0
- package/dist/data/parsers/index.js +79 -0
- package/dist/data/parsers/index.js.map +7 -0
- package/dist/data/parsers/json-array.js +89 -0
- package/dist/data/parsers/json-array.js.map +7 -0
- package/dist/data/parsers/jsonl.js +84 -0
- package/dist/data/parsers/jsonl.js.map +7 -0
- package/dist/data/parsers/parquet.js +95 -0
- package/dist/data/parsers/parquet.js.map +7 -0
- package/dist/data/profiler.js +182 -0
- package/dist/data/profiler.js.map +7 -0
- package/dist/data/queries.js +512 -0
- package/dist/data/queries.js.map +7 -0
- package/dist/data/types.js +17 -0
- package/dist/data/types.js.map +7 -0
- package/dist/db/data-schema.sql +61 -0
- package/dist/db/database.js +67 -5
- package/dist/db/database.js.map +2 -2
- package/dist/db/memory-schema.sql +5 -1
- package/dist/db/schema.sql +3 -1
- package/dist/db/security-schema.sql +72 -0
- package/dist/extraction/extractor.js +81 -0
- package/dist/extraction/extractor.js.map +2 -2
- package/dist/extraction/grammars.js +32 -8
- package/dist/extraction/grammars.js.map +2 -2
- package/dist/extraction/languages.js +43 -1
- package/dist/extraction/languages.js.map +2 -2
- package/dist/extraction/notebook.js +300 -0
- package/dist/extraction/notebook.js.map +7 -0
- package/dist/extraction/wasm/tree-sitter-astro.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-gdscript.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-julia.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-nix.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-perl.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-powershell.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-r.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-sql.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-verilog.wasm +0 -0
- package/dist/frameworks/flutter.js +130 -0
- package/dist/frameworks/flutter.js.map +7 -0
- package/dist/frameworks/index.js +7 -1
- package/dist/frameworks/index.js.map +3 -3
- package/dist/graph/communities.js +317 -0
- package/dist/graph/communities.js.map +7 -0
- package/dist/graph/flows.js +138 -0
- package/dist/graph/flows.js.map +7 -0
- package/dist/graph/refactor.js +155 -0
- package/dist/graph/refactor.js.map +7 -0
- package/dist/index.js +1 -1
- package/dist/index.js.map +2 -2
- package/dist/mcp/cache.js +172 -0
- package/dist/mcp/cache.js.map +7 -0
- package/dist/mcp/read-modes.js +295 -0
- package/dist/mcp/read-modes.js.map +7 -0
- package/dist/mcp/server.js +2 -1
- package/dist/mcp/server.js.map +2 -2
- package/dist/mcp/tool-names.js +29 -1
- package/dist/mcp/tool-names.js.map +2 -2
- package/dist/mcp/tools.js +1588 -6
- package/dist/mcp/tools.js.map +3 -3
- package/dist/memory/database.js +32 -2
- package/dist/memory/database.js.map +2 -2
- package/dist/memory/types.js.map +1 -1
- package/dist/resolution/bridges/android-rn.js +298 -0
- package/dist/resolution/bridges/android-rn.js.map +7 -0
- package/dist/resolution/bridges/expo-modules.js +155 -0
- package/dist/resolution/bridges/expo-modules.js.map +7 -0
- package/dist/resolution/bridges/flutter-channel.js +385 -0
- package/dist/resolution/bridges/flutter-channel.js.map +7 -0
- package/dist/resolution/bridges/index.js +80 -0
- package/dist/resolution/bridges/index.js.map +7 -0
- package/dist/resolution/bridges/native-events.js +188 -0
- package/dist/resolution/bridges/native-events.js.map +7 -0
- package/dist/resolution/bridges/native-views.js +244 -0
- package/dist/resolution/bridges/native-views.js.map +7 -0
- package/dist/resolution/bridges/react-native.js +161 -0
- package/dist/resolution/bridges/react-native.js.map +7 -0
- package/dist/resolution/bridges/swift-objc.js +227 -0
- package/dist/resolution/bridges/swift-objc.js.map +7 -0
- package/dist/resolution/bridges/turbomodules.js +216 -0
- package/dist/resolution/bridges/turbomodules.js.map +7 -0
- package/dist/resolution/index.js +61 -2
- package/dist/resolution/index.js.map +2 -2
- package/dist/security/attack-surface.js +164 -0
- package/dist/security/attack-surface.js.map +7 -0
- package/dist/security/context-warnings.js +123 -0
- package/dist/security/context-warnings.js.map +7 -0
- package/dist/security/context-warnings.test.js +300 -0
- package/dist/security/context-warnings.test.js.map +7 -0
- package/dist/security/data-flows.js +228 -0
- package/dist/security/data-flows.js.map +7 -0
- package/dist/security/dep-confusion.js +255 -0
- package/dist/security/dep-confusion.js.map +7 -0
- package/dist/security/errors.js +51 -0
- package/dist/security/errors.js.map +7 -0
- package/dist/security/export/fix-suggestions.js +58 -0
- package/dist/security/export/fix-suggestions.js.map +7 -0
- package/dist/security/export/fix-suggestions.test.js +74 -0
- package/dist/security/export/fix-suggestions.test.js.map +7 -0
- package/dist/security/export/sbom.js +230 -0
- package/dist/security/export/sbom.js.map +7 -0
- package/dist/security/export/sbom.test.js +288 -0
- package/dist/security/export/sbom.test.js.map +7 -0
- package/dist/security/export/serialization.js +142 -0
- package/dist/security/export/serialization.js.map +7 -0
- package/dist/security/export/serialization.test.js +310 -0
- package/dist/security/export/serialization.test.js.map +7 -0
- package/dist/security/export/vex.js +220 -0
- package/dist/security/export/vex.js.map +7 -0
- package/dist/security/export/vex.test.js +278 -0
- package/dist/security/export/vex.test.js.map +7 -0
- package/dist/security/index.js +67 -0
- package/dist/security/index.js.map +7 -0
- package/dist/security/integrator-transitives.test.js +359 -0
- package/dist/security/integrator-transitives.test.js.map +7 -0
- package/dist/security/integrator.js +491 -0
- package/dist/security/integrator.js.map +7 -0
- package/dist/security/integrator.test.js +237 -0
- package/dist/security/integrator.test.js.map +7 -0
- package/dist/security/license.js +70 -0
- package/dist/security/license.js.map +7 -0
- package/dist/security/manifest/adapter.js +243 -0
- package/dist/security/manifest/adapter.js.map +7 -0
- package/dist/security/manifest/adapter.test.js +272 -0
- package/dist/security/manifest/adapter.test.js.map +7 -0
- package/dist/security/manifest/parser.js +376 -0
- package/dist/security/manifest/parser.js.map +7 -0
- package/dist/security/manifest/parser.test.js +110 -0
- package/dist/security/manifest/parser.test.js.map +7 -0
- package/dist/security/manifest/plugins/cargo.js +233 -0
- package/dist/security/manifest/plugins/cargo.js.map +7 -0
- package/dist/security/manifest/plugins/cargo.test.js +304 -0
- package/dist/security/manifest/plugins/cargo.test.js.map +7 -0
- package/dist/security/manifest/plugins/composer.js +152 -0
- package/dist/security/manifest/plugins/composer.js.map +7 -0
- package/dist/security/manifest/plugins/go.js +146 -0
- package/dist/security/manifest/plugins/go.js.map +7 -0
- package/dist/security/manifest/plugins/go.test.js +341 -0
- package/dist/security/manifest/plugins/go.test.js.map +7 -0
- package/dist/security/manifest/plugins/gradle.js +159 -0
- package/dist/security/manifest/plugins/gradle.js.map +7 -0
- package/dist/security/manifest/plugins/hex.js +159 -0
- package/dist/security/manifest/plugins/hex.js.map +7 -0
- package/dist/security/manifest/plugins/maven.js +128 -0
- package/dist/security/manifest/plugins/maven.js.map +7 -0
- package/dist/security/manifest/plugins/maven.test.js +389 -0
- package/dist/security/manifest/plugins/maven.test.js.map +7 -0
- package/dist/security/manifest/plugins/npm.js +284 -0
- package/dist/security/manifest/plugins/npm.js.map +7 -0
- package/dist/security/manifest/plugins/npm.test.js +298 -0
- package/dist/security/manifest/plugins/npm.test.js.map +7 -0
- package/dist/security/manifest/plugins/nuget.js +186 -0
- package/dist/security/manifest/plugins/nuget.js.map +7 -0
- package/dist/security/manifest/plugins/pip.js +123 -0
- package/dist/security/manifest/plugins/pip.js.map +7 -0
- package/dist/security/manifest/plugins/pip.test.js +208 -0
- package/dist/security/manifest/plugins/pip.test.js.map +7 -0
- package/dist/security/manifest/plugins/pubspec.js +212 -0
- package/dist/security/manifest/plugins/pubspec.js.map +7 -0
- package/dist/security/manifest/plugins/pyproject.js +261 -0
- package/dist/security/manifest/plugins/pyproject.js.map +7 -0
- package/dist/security/manifest/plugins/rubygems.js +185 -0
- package/dist/security/manifest/plugins/rubygems.js.map +7 -0
- package/dist/security/manifest/plugins/swift.js +200 -0
- package/dist/security/manifest/plugins/swift.js.map +7 -0
- package/dist/security/owasp.js +104 -0
- package/dist/security/owasp.js.map +7 -0
- package/dist/security/pipeline.js +125 -0
- package/dist/security/pipeline.js.map +7 -0
- package/dist/security/reachability.js +276 -0
- package/dist/security/reachability.js.map +7 -0
- package/dist/security/reachability.test.js +394 -0
- package/dist/security/reachability.test.js.map +7 -0
- package/dist/security/remediation.js +126 -0
- package/dist/security/remediation.js.map +7 -0
- package/dist/security/secrets.js +176 -0
- package/dist/security/secrets.js.map +7 -0
- package/dist/security/staleness.js +231 -0
- package/dist/security/staleness.js.map +7 -0
- package/dist/security/supply-chain.js +301 -0
- package/dist/security/supply-chain.js.map +7 -0
- package/dist/security/suppressions.js +106 -0
- package/dist/security/suppressions.js.map +7 -0
- package/dist/security/types.js +17 -0
- package/dist/security/types.js.map +7 -0
- package/dist/security/vuln/client.js +355 -0
- package/dist/security/vuln/client.js.map +7 -0
- package/dist/security/vuln/client.test.js +288 -0
- package/dist/security/vuln/client.test.js.map +7 -0
- package/dist/security/vuln/epss-client.js +95 -0
- package/dist/security/vuln/epss-client.js.map +7 -0
- package/dist/security/vuln/index.js +29 -0
- package/dist/security/vuln/index.js.map +7 -0
- package/dist/security/vuln/osv-adapter.js +321 -0
- package/dist/security/vuln/osv-adapter.js.map +7 -0
- package/dist/security/vuln/osv-adapter.test.js +391 -0
- package/dist/security/vuln/osv-adapter.test.js.map +7 -0
- package/dist/security/vuln/types.js +17 -0
- package/dist/security/vuln/types.js.map +7 -0
- package/dist/types.js.map +2 -2
- package/package.json +5 -2
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
var npm_exports = {};
|
|
30
|
+
__export(npm_exports, {
|
|
31
|
+
parseNpmManifest: () => parseNpmManifest
|
|
32
|
+
});
|
|
33
|
+
module.exports = __toCommonJS(npm_exports);
|
|
34
|
+
var fs = __toESM(require("fs"));
|
|
35
|
+
var path = __toESM(require("path"));
|
|
36
|
+
var import_errors = require("../../../errors");
|
|
37
|
+
const SCOPE_FIELDS = [
|
|
38
|
+
{ field: "dependencies", scope: "production" },
|
|
39
|
+
{ field: "devDependencies", scope: "development" },
|
|
40
|
+
{ field: "optionalDependencies", scope: "optional" }
|
|
41
|
+
];
|
|
42
|
+
async function parseNpmManifest(manifestPath, projectRoot) {
|
|
43
|
+
const manifestDir = path.dirname(manifestPath);
|
|
44
|
+
const relativeManifest = path.relative(projectRoot, manifestPath).replace(/\\/g, "/");
|
|
45
|
+
let pkg;
|
|
46
|
+
try {
|
|
47
|
+
const raw = fs.readFileSync(manifestPath, "utf8");
|
|
48
|
+
const parsed = JSON.parse(raw);
|
|
49
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
50
|
+
(0, import_errors.logWarn)(`[sec:npm] Invalid package.json structure at ${relativeManifest}`);
|
|
51
|
+
return [];
|
|
52
|
+
}
|
|
53
|
+
pkg = parsed;
|
|
54
|
+
} catch (err) {
|
|
55
|
+
(0, import_errors.logWarn)(`[sec:npm] Failed to read/parse ${relativeManifest}: ${err instanceof Error ? err.message : String(err)}`);
|
|
56
|
+
return [];
|
|
57
|
+
}
|
|
58
|
+
const resolvedVersions = loadResolvedVersions(manifestDir);
|
|
59
|
+
const license = extractNpmLicense(pkg);
|
|
60
|
+
const dependencies = [];
|
|
61
|
+
const warnings = [];
|
|
62
|
+
for (const { field, scope } of SCOPE_FIELDS) {
|
|
63
|
+
const depsObj = pkg[field];
|
|
64
|
+
if (depsObj === void 0 || depsObj === null) continue;
|
|
65
|
+
if (typeof depsObj !== "object" || Array.isArray(depsObj)) {
|
|
66
|
+
(0, import_errors.logWarn)(`[sec:npm] Invalid "${field}" field in ${relativeManifest} \u2014 expected object, got ${typeof depsObj}`);
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
const entries = Object.entries(depsObj);
|
|
70
|
+
for (const [name, constraint] of entries) {
|
|
71
|
+
if (!isValidDependencyName(name)) {
|
|
72
|
+
(0, import_errors.logWarn)(`[sec:npm] Invalid dependency name "${name}" in ${relativeManifest} (${field}) \u2014 skipping`);
|
|
73
|
+
warnings.push({ name, reason: "invalid name" });
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
if (typeof constraint !== "string" || constraint.trim() === "") {
|
|
77
|
+
(0, import_errors.logWarn)(`[sec:npm] Invalid version constraint for "${name}" in ${relativeManifest} (${field}) \u2014 skipping`);
|
|
78
|
+
warnings.push({ name, reason: "invalid constraint" });
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
const resolvedVersion = resolvedVersions.get(name);
|
|
82
|
+
dependencies.push({
|
|
83
|
+
name,
|
|
84
|
+
declaredConstraint: constraint,
|
|
85
|
+
resolvedVersion,
|
|
86
|
+
scope,
|
|
87
|
+
ecosystem: "npm",
|
|
88
|
+
sourceManifest: relativeManifest,
|
|
89
|
+
...license !== void 0 ? { license } : {}
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
const peerDeps = pkg.peerDependencies;
|
|
94
|
+
if (peerDeps && typeof peerDeps === "object" && !Array.isArray(peerDeps)) {
|
|
95
|
+
const peerEntries = Object.entries(peerDeps);
|
|
96
|
+
for (const [name, constraint] of peerEntries) {
|
|
97
|
+
if (!isValidDependencyName(name)) {
|
|
98
|
+
(0, import_errors.logWarn)(`[sec:npm] Invalid peer dependency name "${name}" in ${relativeManifest} \u2014 skipping`);
|
|
99
|
+
warnings.push({ name, reason: "invalid name" });
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
if (typeof constraint !== "string" || constraint.trim() === "") {
|
|
103
|
+
(0, import_errors.logWarn)(`[sec:npm] Invalid version constraint for peer dependency "${name}" in ${relativeManifest} \u2014 skipping`);
|
|
104
|
+
warnings.push({ name, reason: "invalid constraint" });
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
const alreadyDeclared = dependencies.some((d) => d.name === name);
|
|
108
|
+
if (!alreadyDeclared) {
|
|
109
|
+
const resolvedVersion = resolvedVersions.get(name);
|
|
110
|
+
dependencies.push({
|
|
111
|
+
name,
|
|
112
|
+
declaredConstraint: constraint,
|
|
113
|
+
resolvedVersion,
|
|
114
|
+
scope: "production",
|
|
115
|
+
ecosystem: "npm",
|
|
116
|
+
sourceManifest: relativeManifest,
|
|
117
|
+
...license !== void 0 ? { license } : {}
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return dependencies;
|
|
123
|
+
}
|
|
124
|
+
function loadResolvedVersions(manifestDir) {
|
|
125
|
+
const map = /* @__PURE__ */ new Map();
|
|
126
|
+
const lockPath = path.join(manifestDir, "package-lock.json");
|
|
127
|
+
if (fs.existsSync(lockPath)) {
|
|
128
|
+
try {
|
|
129
|
+
const lockContent = fs.readFileSync(lockPath, "utf8");
|
|
130
|
+
const lockData = JSON.parse(lockContent);
|
|
131
|
+
extractFromPackageLock(lockData, map);
|
|
132
|
+
return map;
|
|
133
|
+
} catch (err) {
|
|
134
|
+
(0, import_errors.logWarn)(`[sec:npm] Failed to parse package-lock.json: ${err instanceof Error ? err.message : String(err)}`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
const pnpmLockPath = path.join(manifestDir, "pnpm-lock.yaml");
|
|
138
|
+
if (fs.existsSync(pnpmLockPath)) {
|
|
139
|
+
try {
|
|
140
|
+
const pnpmContent = fs.readFileSync(pnpmLockPath, "utf8");
|
|
141
|
+
extractFromPnpmLock(pnpmContent, map);
|
|
142
|
+
return map;
|
|
143
|
+
} catch (err) {
|
|
144
|
+
(0, import_errors.logWarn)(`[sec:npm] Failed to parse pnpm-lock.yaml: ${err instanceof Error ? err.message : String(err)}`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
const yarnLockPath = path.join(manifestDir, "yarn.lock");
|
|
148
|
+
if (fs.existsSync(yarnLockPath)) {
|
|
149
|
+
try {
|
|
150
|
+
const yarnContent = fs.readFileSync(yarnLockPath, "utf8");
|
|
151
|
+
extractFromYarnLock(yarnContent, map);
|
|
152
|
+
return map;
|
|
153
|
+
} catch (err) {
|
|
154
|
+
(0, import_errors.logWarn)(`[sec:npm] Failed to parse yarn.lock: ${err instanceof Error ? err.message : String(err)}`);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return map;
|
|
158
|
+
}
|
|
159
|
+
function extractFromPnpmLock(content, map) {
|
|
160
|
+
for (const m of content.matchAll(/^[\s]*\/?(@?[a-zA-Z0-9._-]+(?:\/[a-zA-Z0-9._-]+)?)[@/]([0-9][a-zA-Z0-9._-]*)(?:\(.*?\))?:/gm)) {
|
|
161
|
+
const name = m[1].replace(/^\//, "");
|
|
162
|
+
const version = m[2];
|
|
163
|
+
if (name && version && !map.has(name)) {
|
|
164
|
+
map.set(name, version);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
function extractFromPackageLock(lockData, map) {
|
|
169
|
+
if (typeof lockData !== "object" || lockData === null) return;
|
|
170
|
+
const lock = lockData;
|
|
171
|
+
const packages = lock.packages;
|
|
172
|
+
if (typeof packages === "object" && packages !== null) {
|
|
173
|
+
for (const [key, value] of Object.entries(packages)) {
|
|
174
|
+
if (key === "") continue;
|
|
175
|
+
if (typeof value !== "object" || value === null) continue;
|
|
176
|
+
const pkg = value;
|
|
177
|
+
const version = pkg.version;
|
|
178
|
+
if (typeof version !== "string") continue;
|
|
179
|
+
const name = key.replace(/^.*node_modules\//, "");
|
|
180
|
+
if (name && !map.has(name)) {
|
|
181
|
+
map.set(name, version);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
const dependencies = lock.dependencies;
|
|
187
|
+
if (typeof dependencies === "object" && dependencies !== null) {
|
|
188
|
+
extractFromLockV1Dependencies(dependencies, map);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
function extractFromLockV1Dependencies(deps, map) {
|
|
192
|
+
for (const [name, value] of Object.entries(deps)) {
|
|
193
|
+
if (typeof value !== "object" || value === null) continue;
|
|
194
|
+
const dep = value;
|
|
195
|
+
const version = dep.version;
|
|
196
|
+
if (typeof version === "string" && !map.has(name)) {
|
|
197
|
+
map.set(name, version);
|
|
198
|
+
}
|
|
199
|
+
const nested = dep.dependencies;
|
|
200
|
+
if (typeof nested === "object" && nested !== null) {
|
|
201
|
+
extractFromLockV1Dependencies(nested, map);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
function extractFromYarnLock(content, map) {
|
|
206
|
+
const lines = content.split("\n");
|
|
207
|
+
let currentPackageName = null;
|
|
208
|
+
for (const line of lines) {
|
|
209
|
+
if (line.startsWith("#") || line.trim() === "") {
|
|
210
|
+
currentPackageName = null;
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
if (!line.startsWith(" ") && line.endsWith(":")) {
|
|
214
|
+
const header = line.slice(0, -1);
|
|
215
|
+
const name = extractPackageNameFromYarnHeader(header);
|
|
216
|
+
currentPackageName = name;
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
if (currentPackageName && line.trim().startsWith("version ")) {
|
|
220
|
+
const versionMatch = line.match(/version\s+"([^"]+)"/);
|
|
221
|
+
if (versionMatch && !map.has(currentPackageName)) {
|
|
222
|
+
map.set(currentPackageName, versionMatch[1]);
|
|
223
|
+
}
|
|
224
|
+
currentPackageName = null;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
function extractPackageNameFromYarnHeader(header) {
|
|
229
|
+
const firstEntry = header.split(",")[0].trim();
|
|
230
|
+
const cleaned = firstEntry.replace(/^["']|["']$/g, "");
|
|
231
|
+
const lastAtIndex = cleaned.lastIndexOf("@");
|
|
232
|
+
if (lastAtIndex <= 0) return null;
|
|
233
|
+
if (cleaned.startsWith("@")) {
|
|
234
|
+
const afterScope = cleaned.indexOf("/", 1);
|
|
235
|
+
if (afterScope === -1) return null;
|
|
236
|
+
const atAfterName = cleaned.indexOf("@", afterScope);
|
|
237
|
+
if (atAfterName === -1) return cleaned;
|
|
238
|
+
return cleaned.slice(0, atAfterName);
|
|
239
|
+
}
|
|
240
|
+
return cleaned.slice(0, lastAtIndex);
|
|
241
|
+
}
|
|
242
|
+
function extractNpmLicense(pkg) {
|
|
243
|
+
const raw = pkg["license"];
|
|
244
|
+
if (typeof raw === "string" && raw.trim() !== "") {
|
|
245
|
+
return raw.trim();
|
|
246
|
+
}
|
|
247
|
+
if (typeof raw === "object" && raw !== null && !Array.isArray(raw)) {
|
|
248
|
+
const licObj = raw;
|
|
249
|
+
if (typeof licObj["type"] === "string" && licObj["type"].trim() !== "") {
|
|
250
|
+
return licObj["type"].trim();
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
const licenses = pkg["licenses"];
|
|
254
|
+
if (Array.isArray(licenses) && licenses.length > 0) {
|
|
255
|
+
const first = licenses[0];
|
|
256
|
+
if (typeof first === "object" && first !== null) {
|
|
257
|
+
const lic = first;
|
|
258
|
+
if (typeof lic["type"] === "string" && lic["type"].trim() !== "") {
|
|
259
|
+
return lic["type"].trim();
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
if (typeof first === "string" && first.trim() !== "") {
|
|
263
|
+
return first.trim();
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
return void 0;
|
|
267
|
+
}
|
|
268
|
+
function isValidDependencyName(name) {
|
|
269
|
+
if (!name || typeof name !== "string") return false;
|
|
270
|
+
if (name.length > 214) return false;
|
|
271
|
+
if (name.startsWith("@")) {
|
|
272
|
+
const parts = name.split("/");
|
|
273
|
+
if (parts.length !== 2) return false;
|
|
274
|
+
if (!parts[0] || !parts[1]) return false;
|
|
275
|
+
return true;
|
|
276
|
+
}
|
|
277
|
+
if (name.startsWith(".") || name.startsWith("_")) return false;
|
|
278
|
+
return true;
|
|
279
|
+
}
|
|
280
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
281
|
+
0 && (module.exports = {
|
|
282
|
+
parseNpmManifest
|
|
283
|
+
});
|
|
284
|
+
//# sourceMappingURL=npm.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/security/manifest/plugins/npm.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * NPM Version Extraction Plugin for KiroGraph-Sec\n *\n * Extends the existing architecture npm parser to extract version constraints,\n * resolved versions (from lock files), and dependency scopes for security analysis.\n *\n * Reuses `npmParser` from `src/architecture/manifest/npm.ts` for discovery and\n * basic parsing, then layers on version/scope extraction.\n */\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport type { ParsedDependency } from '../../types';\nimport { logWarn } from '../../../errors';\n\n/** Scope fields in package.json mapped to their ParsedDependency scope */\nconst SCOPE_FIELDS: Array<{ field: string; scope: ParsedDependency['scope'] }> = [\n { field: 'dependencies', scope: 'production' },\n { field: 'devDependencies', scope: 'development' },\n { field: 'optionalDependencies', scope: 'optional' },\n];\n\n/**\n * Resolved version map: package name \u2192 resolved version string.\n * Built from package-lock.json or yarn.lock.\n */\ntype ResolvedVersionMap = Map<string, string>;\n\n/**\n * Parse an npm package.json manifest and extract dependency declarations\n * with version constraints, scopes, and resolved versions from lock files.\n *\n * @param manifestPath - Absolute path to the package.json file\n * @param projectRoot - Absolute path to the project root directory\n * @returns Array of parsed dependencies with version and scope information\n */\nexport async function parseNpmManifest(\n manifestPath: string,\n projectRoot: string,\n): Promise<ParsedDependency[]> {\n const manifestDir = path.dirname(manifestPath);\n const relativeManifest = path.relative(projectRoot, manifestPath).replace(/\\\\/g, '/');\n\n // Read and parse the package.json\n let pkg: Record<string, unknown>;\n try {\n const raw = fs.readFileSync(manifestPath, 'utf8');\n const parsed = JSON.parse(raw);\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n logWarn(`[sec:npm] Invalid package.json structure at ${relativeManifest}`);\n return [];\n }\n pkg = parsed as Record<string, unknown>;\n } catch (err) {\n logWarn(`[sec:npm] Failed to read/parse ${relativeManifest}: ${err instanceof Error ? err.message : String(err)}`);\n return [];\n }\n\n // Attempt to load resolved versions from lock file\n const resolvedVersions = loadResolvedVersions(manifestDir);\n\n // Extract the package-level license\n const license = extractNpmLicense(pkg);\n\n // Extract dependencies from each scope field\n const dependencies: ParsedDependency[] = [];\n const warnings: Array<{ name: string; reason: string }> = [];\n\n for (const { field, scope } of SCOPE_FIELDS) {\n const depsObj = pkg[field];\n if (depsObj === undefined || depsObj === null) continue;\n\n if (typeof depsObj !== 'object' || Array.isArray(depsObj)) {\n logWarn(`[sec:npm] Invalid \"${field}\" field in ${relativeManifest} \u2014 expected object, got ${typeof depsObj}`);\n continue;\n }\n\n const entries = Object.entries(depsObj as Record<string, unknown>);\n for (const [name, constraint] of entries) {\n // Validate the dependency entry\n if (!isValidDependencyName(name)) {\n logWarn(`[sec:npm] Invalid dependency name \"${name}\" in ${relativeManifest} (${field}) \u2014 skipping`);\n warnings.push({ name, reason: 'invalid name' });\n continue;\n }\n\n if (typeof constraint !== 'string' || constraint.trim() === '') {\n logWarn(`[sec:npm] Invalid version constraint for \"${name}\" in ${relativeManifest} (${field}) \u2014 skipping`);\n warnings.push({ name, reason: 'invalid constraint' });\n continue;\n }\n\n const resolvedVersion = resolvedVersions.get(name);\n\n dependencies.push({\n name,\n declaredConstraint: constraint,\n resolvedVersion,\n scope,\n ecosystem: 'npm',\n sourceManifest: relativeManifest,\n ...(license !== undefined ? { license } : {}),\n });\n }\n }\n\n // Also handle peerDependencies \u2014 default to production scope\n const peerDeps = pkg.peerDependencies;\n if (peerDeps && typeof peerDeps === 'object' && !Array.isArray(peerDeps)) {\n const peerEntries = Object.entries(peerDeps as Record<string, unknown>);\n for (const [name, constraint] of peerEntries) {\n if (!isValidDependencyName(name)) {\n logWarn(`[sec:npm] Invalid peer dependency name \"${name}\" in ${relativeManifest} \u2014 skipping`);\n warnings.push({ name, reason: 'invalid name' });\n continue;\n }\n\n if (typeof constraint !== 'string' || constraint.trim() === '') {\n logWarn(`[sec:npm] Invalid version constraint for peer dependency \"${name}\" in ${relativeManifest} \u2014 skipping`);\n warnings.push({ name, reason: 'invalid constraint' });\n continue;\n }\n\n // Only add if not already declared in another scope\n const alreadyDeclared = dependencies.some(d => d.name === name);\n if (!alreadyDeclared) {\n const resolvedVersion = resolvedVersions.get(name);\n dependencies.push({\n name,\n declaredConstraint: constraint,\n resolvedVersion,\n scope: 'production',\n ecosystem: 'npm',\n sourceManifest: relativeManifest,\n ...(license !== undefined ? { license } : {}),\n });\n }\n }\n }\n\n return dependencies;\n}\n\n/**\n * Attempt to load resolved versions from package-lock.json, pnpm-lock.yaml,\n * or yarn.lock in the given directory. Returns an empty map if none is available.\n */\nfunction loadResolvedVersions(manifestDir: string): ResolvedVersionMap {\n const map: ResolvedVersionMap = new Map();\n\n // Try package-lock.json first (preferred \u2014 npm)\n const lockPath = path.join(manifestDir, 'package-lock.json');\n if (fs.existsSync(lockPath)) {\n try {\n const lockContent = fs.readFileSync(lockPath, 'utf8');\n const lockData = JSON.parse(lockContent);\n extractFromPackageLock(lockData, map);\n return map;\n } catch (err) {\n logWarn(`[sec:npm] Failed to parse package-lock.json: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n\n // Try pnpm-lock.yaml (pnpm)\n const pnpmLockPath = path.join(manifestDir, 'pnpm-lock.yaml');\n if (fs.existsSync(pnpmLockPath)) {\n try {\n const pnpmContent = fs.readFileSync(pnpmLockPath, 'utf8');\n extractFromPnpmLock(pnpmContent, map);\n return map;\n } catch (err) {\n logWarn(`[sec:npm] Failed to parse pnpm-lock.yaml: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n\n // Fall back to yarn.lock\n const yarnLockPath = path.join(manifestDir, 'yarn.lock');\n if (fs.existsSync(yarnLockPath)) {\n try {\n const yarnContent = fs.readFileSync(yarnLockPath, 'utf8');\n extractFromYarnLock(yarnContent, map);\n return map;\n } catch (err) {\n logWarn(`[sec:npm] Failed to parse yarn.lock: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n\n return map;\n}\n\n/**\n * Extract resolved versions from pnpm-lock.yaml (v5, v6, v9 formats).\n *\n * v5/v6: packages section with keys like \"/express/4.18.2\" or \"/express@4.18.2\"\n * v9: snapshots section with keys like \"express@4.18.2\" and\n * packages section with \"express@4.18.2: {version: ...}\" (optional)\n */\nfunction extractFromPnpmLock(content: string, map: ResolvedVersionMap): void {\n // v9 format: package entries under `packages:` or `snapshots:`\n // Key format: \"name@version\" or \"@scope/name@version\"\n // We extract name@version pairs from both sections.\n\n // Match lines like: /express/4.18.2: or /express@4.18.2: or express@4.18.2:\n for (const m of content.matchAll(/^[\\s]*\\/?(@?[a-zA-Z0-9._-]+(?:\\/[a-zA-Z0-9._-]+)?)[@/]([0-9][a-zA-Z0-9._-]*)(?:\\(.*?\\))?:/gm)) {\n const name = m[1].replace(/^\\//, ''); // strip leading slash for scoped packages\n const version = m[2];\n if (name && version && !map.has(name)) {\n map.set(name, version);\n }\n }\n}\n\n/**\n * Extract resolved versions from package-lock.json (supports v2 and v3 format).\n */\nfunction extractFromPackageLock(lockData: unknown, map: ResolvedVersionMap): void {\n if (typeof lockData !== 'object' || lockData === null) return;\n const lock = lockData as Record<string, unknown>;\n\n // lockfileVersion 2/3: uses \"packages\" field with \"\" as root\n const packages = lock.packages;\n if (typeof packages === 'object' && packages !== null) {\n for (const [key, value] of Object.entries(packages as Record<string, unknown>)) {\n if (key === '') continue; // skip root\n if (typeof value !== 'object' || value === null) continue;\n const pkg = value as Record<string, unknown>;\n const version = pkg.version;\n if (typeof version !== 'string') continue;\n\n // Key format: \"node_modules/<name>\" or \"node_modules/@scope/name\"\n const name = key.replace(/^.*node_modules\\//, '');\n if (name && !map.has(name)) {\n map.set(name, version);\n }\n }\n return;\n }\n\n // lockfileVersion 1: uses \"dependencies\" field\n const dependencies = lock.dependencies;\n if (typeof dependencies === 'object' && dependencies !== null) {\n extractFromLockV1Dependencies(dependencies as Record<string, unknown>, map);\n }\n}\n\n/**\n * Recursively extract versions from lockfileVersion 1 dependencies.\n */\nfunction extractFromLockV1Dependencies(\n deps: Record<string, unknown>,\n map: ResolvedVersionMap,\n): void {\n for (const [name, value] of Object.entries(deps)) {\n if (typeof value !== 'object' || value === null) continue;\n const dep = value as Record<string, unknown>;\n const version = dep.version;\n if (typeof version === 'string' && !map.has(name)) {\n map.set(name, version);\n }\n // Recurse into nested dependencies\n const nested = dep.dependencies;\n if (typeof nested === 'object' && nested !== null) {\n extractFromLockV1Dependencies(nested as Record<string, unknown>, map);\n }\n }\n}\n\n/**\n * Extract resolved versions from yarn.lock (v1 format).\n * yarn.lock uses a custom format, not JSON.\n */\nfunction extractFromYarnLock(content: string, map: ResolvedVersionMap): void {\n const lines = content.split('\\n');\n let currentPackageName: string | null = null;\n\n for (const line of lines) {\n // Skip comments and empty lines\n if (line.startsWith('#') || line.trim() === '') {\n currentPackageName = null;\n continue;\n }\n\n // Package header line: \"package-name@version-range:\" or \"@scope/name@range:\"\n // Can also be: \"package-name@range1, package-name@range2:\"\n if (!line.startsWith(' ') && line.endsWith(':')) {\n const header = line.slice(0, -1); // remove trailing ':'\n // Extract package name from the first entry (before @version)\n const name = extractPackageNameFromYarnHeader(header);\n currentPackageName = name;\n continue;\n }\n\n // Version line: \" version \"x.y.z\"\"\n if (currentPackageName && line.trim().startsWith('version ')) {\n const versionMatch = line.match(/version\\s+\"([^\"]+)\"/);\n if (versionMatch && !map.has(currentPackageName)) {\n map.set(currentPackageName, versionMatch[1]);\n }\n currentPackageName = null;\n }\n }\n}\n\n/**\n * Extract the package name from a yarn.lock header line.\n * Handles formats like:\n * - \"express@^4.17.1\"\n * - \"@types/node@^18.0.0\"\n * - \"express@^4.17.1, express@^4.18.0\"\n * - '\"express@^4.17.1\"'\n */\nfunction extractPackageNameFromYarnHeader(header: string): string | null {\n // Take the first entry if comma-separated\n const firstEntry = header.split(',')[0].trim();\n // Remove surrounding quotes if present\n const cleaned = firstEntry.replace(/^[\"']|[\"']$/g, '');\n\n // Find the last @ that separates name from version\n // For scoped packages (@scope/name@version), we need the last @\n const lastAtIndex = cleaned.lastIndexOf('@');\n if (lastAtIndex <= 0) return null; // no @ or starts with @ (scoped without version)\n\n // For scoped packages, check if the first char is @\n if (cleaned.startsWith('@')) {\n // @scope/name@version \u2014 find the @ after the scope\n const afterScope = cleaned.indexOf('/', 1);\n if (afterScope === -1) return null;\n const atAfterName = cleaned.indexOf('@', afterScope);\n if (atAfterName === -1) return cleaned; // no version part\n return cleaned.slice(0, atAfterName);\n }\n\n return cleaned.slice(0, lastAtIndex);\n}\n\n/**\n * Extract the license field from a parsed package.json object.\n * Handles both string format (`\"MIT\"`) and object format (`{\"type\":\"MIT\"}`).\n */\nfunction extractNpmLicense(pkg: Record<string, unknown>): string | undefined {\n const raw = pkg['license'];\n if (typeof raw === 'string' && raw.trim() !== '') {\n return raw.trim();\n }\n if (typeof raw === 'object' && raw !== null && !Array.isArray(raw)) {\n const licObj = raw as Record<string, unknown>;\n if (typeof licObj['type'] === 'string' && licObj['type'].trim() !== '') {\n return licObj['type'].trim();\n }\n }\n // Handle legacy \"licenses\" array\n const licenses = pkg['licenses'];\n if (Array.isArray(licenses) && licenses.length > 0) {\n const first = licenses[0];\n if (typeof first === 'object' && first !== null) {\n const lic = first as Record<string, unknown>;\n if (typeof lic['type'] === 'string' && lic['type'].trim() !== '') {\n return lic['type'].trim();\n }\n }\n if (typeof first === 'string' && first.trim() !== '') {\n return first.trim();\n }\n }\n return undefined;\n}\n\n/**\n * Validate that a dependency name follows npm naming conventions.\n * Must be non-empty, not start with a dot or underscore (unless scoped),\n * and contain only valid characters.\n */\nfunction isValidDependencyName(name: string): boolean {\n if (!name || typeof name !== 'string') return false;\n if (name.length > 214) return false;\n\n // Scoped packages: @scope/name\n if (name.startsWith('@')) {\n const parts = name.split('/');\n if (parts.length !== 2) return false;\n if (!parts[0] || !parts[1]) return false;\n return true;\n }\n\n // Unscoped: must not start with . or _\n if (name.startsWith('.') || name.startsWith('_')) return false;\n\n return true;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __copyProps = (to, from, except, desc) => {
|
|
9
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
10
|
+
for (let key of __getOwnPropNames(from))
|
|
11
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
12
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
13
|
+
}
|
|
14
|
+
return to;
|
|
15
|
+
};
|
|
16
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
17
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
18
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
19
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
20
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
21
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
22
|
+
mod
|
|
23
|
+
));
|
|
24
|
+
var import_vitest = require("vitest");
|
|
25
|
+
var fs = __toESM(require("fs"));
|
|
26
|
+
var path = __toESM(require("path"));
|
|
27
|
+
var os = __toESM(require("os"));
|
|
28
|
+
var import_npm = require("./npm");
|
|
29
|
+
(0, import_vitest.describe)("parseNpmManifest", () => {
|
|
30
|
+
let tmpDir;
|
|
31
|
+
(0, import_vitest.beforeEach)(() => {
|
|
32
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "kirograph-npm-test-"));
|
|
33
|
+
});
|
|
34
|
+
(0, import_vitest.afterEach)(() => {
|
|
35
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
36
|
+
});
|
|
37
|
+
function writePackageJson(content, dir) {
|
|
38
|
+
const targetDir = dir ?? tmpDir;
|
|
39
|
+
const filePath = path.join(targetDir, "package.json");
|
|
40
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
41
|
+
fs.writeFileSync(filePath, JSON.stringify(content, null, 2));
|
|
42
|
+
return filePath;
|
|
43
|
+
}
|
|
44
|
+
(0, import_vitest.it)("extracts production dependencies with correct scope", async () => {
|
|
45
|
+
const manifestPath = writePackageJson({
|
|
46
|
+
name: "test-project",
|
|
47
|
+
version: "1.0.0",
|
|
48
|
+
dependencies: {
|
|
49
|
+
express: "^4.18.2",
|
|
50
|
+
lodash: "~4.17.21"
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
const deps = await (0, import_npm.parseNpmManifest)(manifestPath, tmpDir);
|
|
54
|
+
(0, import_vitest.expect)(deps).toHaveLength(2);
|
|
55
|
+
const express = deps.find((d) => d.name === "express");
|
|
56
|
+
(0, import_vitest.expect)(express).toBeDefined();
|
|
57
|
+
(0, import_vitest.expect)(express.declaredConstraint).toBe("^4.18.2");
|
|
58
|
+
(0, import_vitest.expect)(express.scope).toBe("production");
|
|
59
|
+
(0, import_vitest.expect)(express.ecosystem).toBe("npm");
|
|
60
|
+
const lodash = deps.find((d) => d.name === "lodash");
|
|
61
|
+
(0, import_vitest.expect)(lodash).toBeDefined();
|
|
62
|
+
(0, import_vitest.expect)(lodash.declaredConstraint).toBe("~4.17.21");
|
|
63
|
+
(0, import_vitest.expect)(lodash.scope).toBe("production");
|
|
64
|
+
});
|
|
65
|
+
(0, import_vitest.it)("extracts devDependencies with development scope", async () => {
|
|
66
|
+
const manifestPath = writePackageJson({
|
|
67
|
+
name: "test-project",
|
|
68
|
+
devDependencies: {
|
|
69
|
+
vitest: "^1.0.0",
|
|
70
|
+
typescript: "^5.0.0"
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
const deps = await (0, import_npm.parseNpmManifest)(manifestPath, tmpDir);
|
|
74
|
+
(0, import_vitest.expect)(deps).toHaveLength(2);
|
|
75
|
+
(0, import_vitest.expect)(deps.every((d) => d.scope === "development")).toBe(true);
|
|
76
|
+
});
|
|
77
|
+
(0, import_vitest.it)("extracts optionalDependencies with optional scope", async () => {
|
|
78
|
+
const manifestPath = writePackageJson({
|
|
79
|
+
name: "test-project",
|
|
80
|
+
optionalDependencies: {
|
|
81
|
+
"better-sqlite3": "^11.0.0"
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
const deps = await (0, import_npm.parseNpmManifest)(manifestPath, tmpDir);
|
|
85
|
+
(0, import_vitest.expect)(deps).toHaveLength(1);
|
|
86
|
+
(0, import_vitest.expect)(deps[0].scope).toBe("optional");
|
|
87
|
+
(0, import_vitest.expect)(deps[0].name).toBe("better-sqlite3");
|
|
88
|
+
});
|
|
89
|
+
(0, import_vitest.it)("handles all scope fields together", async () => {
|
|
90
|
+
const manifestPath = writePackageJson({
|
|
91
|
+
name: "test-project",
|
|
92
|
+
dependencies: { express: "^4.18.0" },
|
|
93
|
+
devDependencies: { vitest: "^1.0.0" },
|
|
94
|
+
optionalDependencies: { sqlite3: "^5.0.0" }
|
|
95
|
+
});
|
|
96
|
+
const deps = await (0, import_npm.parseNpmManifest)(manifestPath, tmpDir);
|
|
97
|
+
(0, import_vitest.expect)(deps).toHaveLength(3);
|
|
98
|
+
(0, import_vitest.expect)(deps.find((d) => d.name === "express").scope).toBe("production");
|
|
99
|
+
(0, import_vitest.expect)(deps.find((d) => d.name === "vitest").scope).toBe("development");
|
|
100
|
+
(0, import_vitest.expect)(deps.find((d) => d.name === "sqlite3").scope).toBe("optional");
|
|
101
|
+
});
|
|
102
|
+
(0, import_vitest.it)("resolves versions from package-lock.json (v2/v3 format)", async () => {
|
|
103
|
+
writePackageJson({
|
|
104
|
+
name: "test-project",
|
|
105
|
+
dependencies: { express: "^4.18.0" }
|
|
106
|
+
});
|
|
107
|
+
const lockContent = {
|
|
108
|
+
lockfileVersion: 3,
|
|
109
|
+
packages: {
|
|
110
|
+
"": { name: "test-project", version: "1.0.0" },
|
|
111
|
+
"node_modules/express": { version: "4.18.2" }
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
fs.writeFileSync(
|
|
115
|
+
path.join(tmpDir, "package-lock.json"),
|
|
116
|
+
JSON.stringify(lockContent)
|
|
117
|
+
);
|
|
118
|
+
const manifestPath = path.join(tmpDir, "package.json");
|
|
119
|
+
const deps = await (0, import_npm.parseNpmManifest)(manifestPath, tmpDir);
|
|
120
|
+
(0, import_vitest.expect)(deps).toHaveLength(1);
|
|
121
|
+
(0, import_vitest.expect)(deps[0].resolvedVersion).toBe("4.18.2");
|
|
122
|
+
(0, import_vitest.expect)(deps[0].declaredConstraint).toBe("^4.18.0");
|
|
123
|
+
});
|
|
124
|
+
(0, import_vitest.it)("resolves versions from package-lock.json (v1 format)", async () => {
|
|
125
|
+
writePackageJson({
|
|
126
|
+
name: "test-project",
|
|
127
|
+
dependencies: { lodash: "^4.17.0" }
|
|
128
|
+
});
|
|
129
|
+
const lockContent = {
|
|
130
|
+
lockfileVersion: 1,
|
|
131
|
+
dependencies: {
|
|
132
|
+
lodash: { version: "4.17.21" }
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
fs.writeFileSync(
|
|
136
|
+
path.join(tmpDir, "package-lock.json"),
|
|
137
|
+
JSON.stringify(lockContent)
|
|
138
|
+
);
|
|
139
|
+
const manifestPath = path.join(tmpDir, "package.json");
|
|
140
|
+
const deps = await (0, import_npm.parseNpmManifest)(manifestPath, tmpDir);
|
|
141
|
+
(0, import_vitest.expect)(deps).toHaveLength(1);
|
|
142
|
+
(0, import_vitest.expect)(deps[0].resolvedVersion).toBe("4.17.21");
|
|
143
|
+
});
|
|
144
|
+
(0, import_vitest.it)("resolves versions from yarn.lock", async () => {
|
|
145
|
+
writePackageJson({
|
|
146
|
+
name: "test-project",
|
|
147
|
+
dependencies: { react: "^18.0.0" }
|
|
148
|
+
});
|
|
149
|
+
const yarnLock = `# yarn lockfile v1
|
|
150
|
+
|
|
151
|
+
react@^18.0.0:
|
|
152
|
+
version "18.2.0"
|
|
153
|
+
resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz"
|
|
154
|
+
integrity sha512-abc123
|
|
155
|
+
`;
|
|
156
|
+
fs.writeFileSync(path.join(tmpDir, "yarn.lock"), yarnLock);
|
|
157
|
+
const manifestPath = path.join(tmpDir, "package.json");
|
|
158
|
+
const deps = await (0, import_npm.parseNpmManifest)(manifestPath, tmpDir);
|
|
159
|
+
(0, import_vitest.expect)(deps).toHaveLength(1);
|
|
160
|
+
(0, import_vitest.expect)(deps[0].resolvedVersion).toBe("18.2.0");
|
|
161
|
+
});
|
|
162
|
+
(0, import_vitest.it)("handles scoped packages in yarn.lock", async () => {
|
|
163
|
+
writePackageJson({
|
|
164
|
+
name: "test-project",
|
|
165
|
+
dependencies: { "@types/node": "^20.0.0" }
|
|
166
|
+
});
|
|
167
|
+
const yarnLock = `# yarn lockfile v1
|
|
168
|
+
|
|
169
|
+
"@types/node@^20.0.0":
|
|
170
|
+
version "20.11.5"
|
|
171
|
+
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.11.5.tgz"
|
|
172
|
+
`;
|
|
173
|
+
fs.writeFileSync(path.join(tmpDir, "yarn.lock"), yarnLock);
|
|
174
|
+
const manifestPath = path.join(tmpDir, "package.json");
|
|
175
|
+
const deps = await (0, import_npm.parseNpmManifest)(manifestPath, tmpDir);
|
|
176
|
+
(0, import_vitest.expect)(deps).toHaveLength(1);
|
|
177
|
+
(0, import_vitest.expect)(deps[0].name).toBe("@types/node");
|
|
178
|
+
(0, import_vitest.expect)(deps[0].resolvedVersion).toBe("20.11.5");
|
|
179
|
+
});
|
|
180
|
+
(0, import_vitest.it)("skips invalid dependency entries gracefully", async () => {
|
|
181
|
+
const manifestPath = writePackageJson({
|
|
182
|
+
name: "test-project",
|
|
183
|
+
dependencies: {
|
|
184
|
+
express: "^4.18.0",
|
|
185
|
+
"": "^1.0.0",
|
|
186
|
+
// invalid: empty name
|
|
187
|
+
".hidden": "^1.0.0",
|
|
188
|
+
// invalid: starts with dot
|
|
189
|
+
validPkg: "",
|
|
190
|
+
// invalid: empty constraint
|
|
191
|
+
anotherValid: "^2.0.0",
|
|
192
|
+
// valid
|
|
193
|
+
badConstraint: 123
|
|
194
|
+
// invalid: non-string constraint
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
const deps = await (0, import_npm.parseNpmManifest)(manifestPath, tmpDir);
|
|
198
|
+
const names = deps.map((d) => d.name);
|
|
199
|
+
(0, import_vitest.expect)(names).toContain("express");
|
|
200
|
+
(0, import_vitest.expect)(names).toContain("anotherValid");
|
|
201
|
+
(0, import_vitest.expect)(names).not.toContain("");
|
|
202
|
+
(0, import_vitest.expect)(names).not.toContain(".hidden");
|
|
203
|
+
(0, import_vitest.expect)(names).not.toContain("validPkg");
|
|
204
|
+
(0, import_vitest.expect)(names).not.toContain("badConstraint");
|
|
205
|
+
});
|
|
206
|
+
(0, import_vitest.it)("returns empty array for invalid JSON", async () => {
|
|
207
|
+
const filePath = path.join(tmpDir, "package.json");
|
|
208
|
+
fs.writeFileSync(filePath, "not valid json {{{");
|
|
209
|
+
const deps = await (0, import_npm.parseNpmManifest)(filePath, tmpDir);
|
|
210
|
+
(0, import_vitest.expect)(deps).toEqual([]);
|
|
211
|
+
});
|
|
212
|
+
(0, import_vitest.it)("returns empty array for non-object JSON", async () => {
|
|
213
|
+
const filePath = path.join(tmpDir, "package.json");
|
|
214
|
+
fs.writeFileSync(filePath, '"just a string"');
|
|
215
|
+
const deps = await (0, import_npm.parseNpmManifest)(filePath, tmpDir);
|
|
216
|
+
(0, import_vitest.expect)(deps).toEqual([]);
|
|
217
|
+
});
|
|
218
|
+
(0, import_vitest.it)("returns empty array for array JSON", async () => {
|
|
219
|
+
const filePath = path.join(tmpDir, "package.json");
|
|
220
|
+
fs.writeFileSync(filePath, "[1, 2, 3]");
|
|
221
|
+
const deps = await (0, import_npm.parseNpmManifest)(filePath, tmpDir);
|
|
222
|
+
(0, import_vitest.expect)(deps).toEqual([]);
|
|
223
|
+
});
|
|
224
|
+
(0, import_vitest.it)("handles package.json with no dependency fields", async () => {
|
|
225
|
+
const manifestPath = writePackageJson({
|
|
226
|
+
name: "empty-project",
|
|
227
|
+
version: "1.0.0"
|
|
228
|
+
});
|
|
229
|
+
const deps = await (0, import_npm.parseNpmManifest)(manifestPath, tmpDir);
|
|
230
|
+
(0, import_vitest.expect)(deps).toEqual([]);
|
|
231
|
+
});
|
|
232
|
+
(0, import_vitest.it)("sets sourceManifest as relative path", async () => {
|
|
233
|
+
const subDir = path.join(tmpDir, "packages", "sub");
|
|
234
|
+
const manifestPath = writePackageJson(
|
|
235
|
+
{ name: "sub", dependencies: { foo: "^1.0.0" } },
|
|
236
|
+
subDir
|
|
237
|
+
);
|
|
238
|
+
const deps = await (0, import_npm.parseNpmManifest)(manifestPath, tmpDir);
|
|
239
|
+
(0, import_vitest.expect)(deps).toHaveLength(1);
|
|
240
|
+
(0, import_vitest.expect)(deps[0].sourceManifest).toBe("packages/sub/package.json");
|
|
241
|
+
});
|
|
242
|
+
(0, import_vitest.it)("handles peerDependencies with production scope", async () => {
|
|
243
|
+
const manifestPath = writePackageJson({
|
|
244
|
+
name: "test-project",
|
|
245
|
+
peerDependencies: { react: ">=16.0.0" }
|
|
246
|
+
});
|
|
247
|
+
const deps = await (0, import_npm.parseNpmManifest)(manifestPath, tmpDir);
|
|
248
|
+
(0, import_vitest.expect)(deps).toHaveLength(1);
|
|
249
|
+
(0, import_vitest.expect)(deps[0].name).toBe("react");
|
|
250
|
+
(0, import_vitest.expect)(deps[0].scope).toBe("production");
|
|
251
|
+
(0, import_vitest.expect)(deps[0].declaredConstraint).toBe(">=16.0.0");
|
|
252
|
+
});
|
|
253
|
+
(0, import_vitest.it)("does not duplicate peerDependencies already in dependencies", async () => {
|
|
254
|
+
const manifestPath = writePackageJson({
|
|
255
|
+
name: "test-project",
|
|
256
|
+
dependencies: { react: "^18.0.0" },
|
|
257
|
+
peerDependencies: { react: ">=16.0.0" }
|
|
258
|
+
});
|
|
259
|
+
const deps = await (0, import_npm.parseNpmManifest)(manifestPath, tmpDir);
|
|
260
|
+
const reactDeps = deps.filter((d) => d.name === "react");
|
|
261
|
+
(0, import_vitest.expect)(reactDeps).toHaveLength(1);
|
|
262
|
+
(0, import_vitest.expect)(reactDeps[0].scope).toBe("production");
|
|
263
|
+
(0, import_vitest.expect)(reactDeps[0].declaredConstraint).toBe("^18.0.0");
|
|
264
|
+
});
|
|
265
|
+
(0, import_vitest.it)("prefers package-lock.json over yarn.lock", async () => {
|
|
266
|
+
writePackageJson({
|
|
267
|
+
name: "test-project",
|
|
268
|
+
dependencies: { express: "^4.0.0" }
|
|
269
|
+
});
|
|
270
|
+
const lockContent = {
|
|
271
|
+
lockfileVersion: 3,
|
|
272
|
+
packages: {
|
|
273
|
+
"": { name: "test-project" },
|
|
274
|
+
"node_modules/express": { version: "4.18.2" }
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
fs.writeFileSync(path.join(tmpDir, "package-lock.json"), JSON.stringify(lockContent));
|
|
278
|
+
const yarnLock = `# yarn lockfile v1
|
|
279
|
+
|
|
280
|
+
express@^4.0.0:
|
|
281
|
+
version "4.17.0"
|
|
282
|
+
`;
|
|
283
|
+
fs.writeFileSync(path.join(tmpDir, "yarn.lock"), yarnLock);
|
|
284
|
+
const manifestPath = path.join(tmpDir, "package.json");
|
|
285
|
+
const deps = await (0, import_npm.parseNpmManifest)(manifestPath, tmpDir);
|
|
286
|
+
(0, import_vitest.expect)(deps[0].resolvedVersion).toBe("4.18.2");
|
|
287
|
+
});
|
|
288
|
+
(0, import_vitest.it)("leaves resolvedVersion undefined when no lock file exists", async () => {
|
|
289
|
+
const manifestPath = writePackageJson({
|
|
290
|
+
name: "test-project",
|
|
291
|
+
dependencies: { express: "^4.18.0" }
|
|
292
|
+
});
|
|
293
|
+
const deps = await (0, import_npm.parseNpmManifest)(manifestPath, tmpDir);
|
|
294
|
+
(0, import_vitest.expect)(deps).toHaveLength(1);
|
|
295
|
+
(0, import_vitest.expect)(deps[0].resolvedVersion).toBeUndefined();
|
|
296
|
+
});
|
|
297
|
+
});
|
|
298
|
+
//# sourceMappingURL=npm.test.js.map
|