@agentxm/extension-discovery 0.28.4-bootstrap.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 +110 -0
- package/dist/src/discover.d.ts +34 -0
- package/dist/src/discover.js +133 -0
- package/dist/src/index.d.ts +13 -0
- package/dist/src/index.js +12 -0
- package/dist/src/internal/environment.d.ts +14 -0
- package/dist/src/internal/environment.js +15 -0
- package/dist/src/packaging/bazel.d.ts +27 -0
- package/dist/src/packaging/bazel.js +125 -0
- package/dist/src/packaging/cargo.d.ts +32 -0
- package/dist/src/packaging/cargo.js +270 -0
- package/dist/src/packaging/cocoapods.d.ts +26 -0
- package/dist/src/packaging/cocoapods.js +187 -0
- package/dist/src/packaging/composer.d.ts +26 -0
- package/dist/src/packaging/composer.js +144 -0
- package/dist/src/packaging/conan.d.ts +30 -0
- package/dist/src/packaging/conan.js +238 -0
- package/dist/src/packaging/conda.d.ts +26 -0
- package/dist/src/packaging/conda.js +320 -0
- package/dist/src/packaging/cpan.d.ts +29 -0
- package/dist/src/packaging/cpan.js +189 -0
- package/dist/src/packaging/cran.d.ts +29 -0
- package/dist/src/packaging/cran.js +176 -0
- package/dist/src/packaging/detect.d.ts +16 -0
- package/dist/src/packaging/detect.js +25 -0
- package/dist/src/packaging/detected-package.d.ts +14 -0
- package/dist/src/packaging/detected-package.js +20 -0
- package/dist/src/packaging/docker.d.ts +27 -0
- package/dist/src/packaging/docker.js +256 -0
- package/dist/src/packaging/gem.d.ts +26 -0
- package/dist/src/packaging/gem.js +234 -0
- package/dist/src/packaging/golang.d.ts +26 -0
- package/dist/src/packaging/golang.js +169 -0
- package/dist/src/packaging/hackage.d.ts +26 -0
- package/dist/src/packaging/hackage.js +305 -0
- package/dist/src/packaging/hex.d.ts +28 -0
- package/dist/src/packaging/hex.js +186 -0
- package/dist/src/packaging/huggingface.d.ts +20 -0
- package/dist/src/packaging/huggingface.js +112 -0
- package/dist/src/packaging/index.d.ts +44 -0
- package/dist/src/packaging/index.js +120 -0
- package/dist/src/packaging/jsr.d.ts +30 -0
- package/dist/src/packaging/jsr.js +227 -0
- package/dist/src/packaging/julia.d.ts +27 -0
- package/dist/src/packaging/julia.js +165 -0
- package/dist/src/packaging/luarocks.d.ts +28 -0
- package/dist/src/packaging/luarocks.js +159 -0
- package/dist/src/packaging/maven.d.ts +27 -0
- package/dist/src/packaging/maven.js +471 -0
- package/dist/src/packaging/mojo.d.ts +29 -0
- package/dist/src/packaging/mojo.js +148 -0
- package/dist/src/packaging/npm.d.ts +26 -0
- package/dist/src/packaging/npm.js +190 -0
- package/dist/src/packaging/nuget.d.ts +26 -0
- package/dist/src/packaging/nuget.js +240 -0
- package/dist/src/packaging/opam.d.ts +27 -0
- package/dist/src/packaging/opam.js +287 -0
- package/dist/src/packaging/pub.d.ts +26 -0
- package/dist/src/packaging/pub.js +357 -0
- package/dist/src/packaging/pypi.d.ts +23 -0
- package/dist/src/packaging/pypi.js +448 -0
- package/dist/src/packaging/read.d.ts +20 -0
- package/dist/src/packaging/read.js +31 -0
- package/dist/src/packaging/reader-io.d.ts +29 -0
- package/dist/src/packaging/reader-io.js +29 -0
- package/dist/src/packaging/swift.d.ts +26 -0
- package/dist/src/packaging/swift.js +141 -0
- package/dist/src/packaging/types.d.ts +43 -0
- package/dist/src/packaging/types.js +8 -0
- package/dist/src/packaging/zig.d.ts +32 -0
- package/dist/src/packaging/zig.js +151 -0
- package/package.json +54 -0
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* npm package detector and reader for package-compatibility discovery.
|
|
3
|
+
*
|
|
4
|
+
* @experimental This API is unstable and may change without notice.
|
|
5
|
+
* @packageDocumentation
|
|
6
|
+
*/
|
|
7
|
+
import * as Effect from "effect/Effect";
|
|
8
|
+
import * as Option from "effect/Option";
|
|
9
|
+
import * as Path from "effect/Path";
|
|
10
|
+
import * as Result from "effect/Result";
|
|
11
|
+
import * as Schema from "effect/Schema";
|
|
12
|
+
import { makeDetectedPackage } from "./detected-package.js";
|
|
13
|
+
import { PackageTypeSchema } from "@agentxm/extension-model/unstable/packaging/package-type";
|
|
14
|
+
import { decodeAxmMeta, parseJsonOptional, readFileOptional } from "./reader-io.js";
|
|
15
|
+
const npmType = Schema.decodeUnknownSync(PackageTypeSchema)("npm");
|
|
16
|
+
/** Schema to extract the optional "axm" field from a package.json object. */
|
|
17
|
+
const AxmContainerSchema = Schema.Struct({
|
|
18
|
+
axm: Schema.optional(Schema.Unknown),
|
|
19
|
+
});
|
|
20
|
+
const decodeAxmContainer = Schema.decodeUnknownResult(AxmContainerSchema);
|
|
21
|
+
/** Prefixes that indicate non-registry specifiers to skip. */
|
|
22
|
+
const SKIP_PREFIXES = ["file:", "link:", "workspace:", "git+", "git:", "github:"];
|
|
23
|
+
/** Returns true if the specifier should be skipped (non-registry). */
|
|
24
|
+
const isSkippedSpecifier = (specifier) => {
|
|
25
|
+
if (SKIP_PREFIXES.some((prefix) => specifier.startsWith(prefix)))
|
|
26
|
+
return true;
|
|
27
|
+
// URL-based specifiers
|
|
28
|
+
if (specifier.startsWith("http://") || specifier.startsWith("https://"))
|
|
29
|
+
return true;
|
|
30
|
+
return false;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Returns true if the specifier is an exact semver version (no range operators).
|
|
34
|
+
* Exact versions match: digits and dots only, e.g. "18.2.0", "1.0.0-beta.1".
|
|
35
|
+
*/
|
|
36
|
+
const isExactVersion = (specifier) => /^\d+\.\d+\.\d+(?:[-+][a-zA-Z0-9._+-]*)?$/.test(specifier);
|
|
37
|
+
/**
|
|
38
|
+
* Parse an npm alias specifier like "npm:real-name@version" or "npm:@scope/name@version".
|
|
39
|
+
* Returns { name, specifier } where name is the real package name and specifier is the version part.
|
|
40
|
+
*/
|
|
41
|
+
const parseNpmAlias = (specifier) => {
|
|
42
|
+
if (!specifier.startsWith("npm:"))
|
|
43
|
+
return undefined;
|
|
44
|
+
const rest = specifier.slice(4); // Remove "npm:" prefix
|
|
45
|
+
// Handle scoped: npm:@scope/name@version
|
|
46
|
+
if (rest.startsWith("@")) {
|
|
47
|
+
const atIdx = rest.indexOf("@", 1);
|
|
48
|
+
if (atIdx === -1)
|
|
49
|
+
return { name: rest, specifier: "*" };
|
|
50
|
+
return { name: rest.slice(0, atIdx), specifier: rest.slice(atIdx + 1) };
|
|
51
|
+
}
|
|
52
|
+
// Handle unscoped: npm:name@version
|
|
53
|
+
const atIdx = rest.indexOf("@");
|
|
54
|
+
if (atIdx === -1)
|
|
55
|
+
return { name: rest, specifier: "*" };
|
|
56
|
+
return { name: rest.slice(0, atIdx), specifier: rest.slice(atIdx + 1) };
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Parse a package name into namespace and name parts.
|
|
60
|
+
* "@scope/name" -> { namespace: "@scope", name: "name" }
|
|
61
|
+
* "name" -> { name: "name" }
|
|
62
|
+
*/
|
|
63
|
+
const parsePackageName = (pkgName) => {
|
|
64
|
+
if (pkgName.startsWith("@")) {
|
|
65
|
+
const slashIdx = pkgName.indexOf("/");
|
|
66
|
+
if (slashIdx > 0) {
|
|
67
|
+
return { namespace: pkgName.slice(0, slashIdx), name: pkgName.slice(slashIdx + 1) };
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return { name: pkgName };
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* Convert a single dependency entry to a DetectedPackage, or undefined if skipped.
|
|
74
|
+
*/
|
|
75
|
+
const depToPurl = (depName, specifier, source) => {
|
|
76
|
+
if (isSkippedSpecifier(specifier))
|
|
77
|
+
return undefined;
|
|
78
|
+
// Resolve npm aliases to real package name
|
|
79
|
+
const alias = parseNpmAlias(specifier);
|
|
80
|
+
const resolvedName = alias ? alias.name : depName;
|
|
81
|
+
const resolvedSpecifier = alias ? alias.specifier : specifier;
|
|
82
|
+
const { namespace, name } = parsePackageName(resolvedName);
|
|
83
|
+
const version = isExactVersion(resolvedSpecifier) ? resolvedSpecifier : undefined;
|
|
84
|
+
return Option.getOrUndefined(makeDetectedPackage({
|
|
85
|
+
type: npmType,
|
|
86
|
+
...(namespace === undefined ? {} : { namespace }),
|
|
87
|
+
name,
|
|
88
|
+
...(version === undefined ? {} : { version }),
|
|
89
|
+
source,
|
|
90
|
+
}));
|
|
91
|
+
};
|
|
92
|
+
/**
|
|
93
|
+
* Schema to loosely decode the dependency-relevant shape of package.json.
|
|
94
|
+
* Accepts any object shape and extracts the three dependency sections
|
|
95
|
+
* as optional string-to-string records.
|
|
96
|
+
*/
|
|
97
|
+
const DependencySectionsSchema = Schema.Struct({
|
|
98
|
+
dependencies: Schema.optional(Schema.Record(Schema.String, Schema.String)),
|
|
99
|
+
devDependencies: Schema.optional(Schema.Record(Schema.String, Schema.String)),
|
|
100
|
+
peerDependencies: Schema.optional(Schema.Record(Schema.String, Schema.String)),
|
|
101
|
+
});
|
|
102
|
+
const decodeDependencySections = Schema.decodeUnknownResult(DependencySectionsSchema);
|
|
103
|
+
/**
|
|
104
|
+
* Extract dependencies from a parsed package.json value.
|
|
105
|
+
*/
|
|
106
|
+
const extractDeps = (manifest, source) => {
|
|
107
|
+
const decoded = decodeDependencySections(manifest);
|
|
108
|
+
if (Result.isFailure(decoded))
|
|
109
|
+
return [];
|
|
110
|
+
const sections = [
|
|
111
|
+
decoded.success.dependencies,
|
|
112
|
+
decoded.success.devDependencies,
|
|
113
|
+
decoded.success.peerDependencies,
|
|
114
|
+
];
|
|
115
|
+
const results = [];
|
|
116
|
+
for (const deps of sections) {
|
|
117
|
+
if (deps === undefined)
|
|
118
|
+
continue;
|
|
119
|
+
for (const [name, specifier] of Object.entries(deps)) {
|
|
120
|
+
const detected = depToPurl(name, specifier, source);
|
|
121
|
+
if (detected !== undefined)
|
|
122
|
+
results.push(detected);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return results;
|
|
126
|
+
};
|
|
127
|
+
/**
|
|
128
|
+
* npm package detector.
|
|
129
|
+
*
|
|
130
|
+
* Scans `package.json` in the project directory and extracts dependencies
|
|
131
|
+
* from `dependencies`, `devDependencies`, and `peerDependencies`.
|
|
132
|
+
*
|
|
133
|
+
* @experimental This API is unstable and may change without notice.
|
|
134
|
+
*/
|
|
135
|
+
export const npmDetector = {
|
|
136
|
+
type: npmType,
|
|
137
|
+
detect: Effect.fn("detect.npm")(function* (projectDir) {
|
|
138
|
+
const path = yield* Path.Path;
|
|
139
|
+
const manifestPath = path.join(projectDir, "package.json");
|
|
140
|
+
const content = yield* readFileOptional(manifestPath);
|
|
141
|
+
if (Option.isNone(content))
|
|
142
|
+
return [];
|
|
143
|
+
const parsed = yield* parseJsonOptional(content.value, "package.json");
|
|
144
|
+
if (Option.isNone(parsed))
|
|
145
|
+
return [];
|
|
146
|
+
return extractDeps(parsed.value, manifestPath);
|
|
147
|
+
}, Effect.annotateLogs({ detector: "npm" }), Effect.withSpan("detect.npm")),
|
|
148
|
+
};
|
|
149
|
+
/**
|
|
150
|
+
* npm package reader.
|
|
151
|
+
*
|
|
152
|
+
* Reads `node_modules/<name>/package.json` for each detected npm package
|
|
153
|
+
* and extracts the `"axm"` field containing recommendation metadata.
|
|
154
|
+
*
|
|
155
|
+
* @experimental This API is unstable and may change without notice.
|
|
156
|
+
*/
|
|
157
|
+
export const npmReader = {
|
|
158
|
+
type: npmType,
|
|
159
|
+
read: Effect.fn("read.npm")(function* (pkg) {
|
|
160
|
+
const path = yield* Path.Path;
|
|
161
|
+
// Derive project directory from the source manifest path
|
|
162
|
+
const projectDir = path.dirname(pkg.source);
|
|
163
|
+
// Reconstruct the package name from purl parts
|
|
164
|
+
const pkgName = pkg.purl.namespace ? `${pkg.purl.namespace}/${pkg.purl.name}` : pkg.purl.name;
|
|
165
|
+
const pkgJsonPath = path.join(projectDir, "node_modules", pkgName, "package.json");
|
|
166
|
+
const content = yield* readFileOptional(pkgJsonPath);
|
|
167
|
+
if (Option.isNone(content))
|
|
168
|
+
return Option.none();
|
|
169
|
+
const parsed = yield* parseJsonOptional(content.value, pkgName);
|
|
170
|
+
if (Option.isNone(parsed))
|
|
171
|
+
return Option.none();
|
|
172
|
+
// Extract and validate the "axm" field using schema
|
|
173
|
+
const axmContainerResult = decodeAxmContainer(parsed.value);
|
|
174
|
+
if (Result.isFailure(axmContainerResult)) {
|
|
175
|
+
// No valid axm field present (or missing entirely)
|
|
176
|
+
return Option.none();
|
|
177
|
+
}
|
|
178
|
+
const axmRaw = axmContainerResult.success.axm;
|
|
179
|
+
if (axmRaw === undefined)
|
|
180
|
+
return Option.none();
|
|
181
|
+
// Validate axm metadata structure
|
|
182
|
+
const metaResult = decodeAxmMeta(axmRaw);
|
|
183
|
+
if (Result.isFailure(metaResult)) {
|
|
184
|
+
yield* Effect.logWarning(`Invalid axm metadata in ${pkgName}: schema validation failed`);
|
|
185
|
+
return Option.none();
|
|
186
|
+
}
|
|
187
|
+
return Option.some(metaResult.success.extensions);
|
|
188
|
+
}, Effect.annotateLogs({ reader: "npm" }), Effect.withSpan("read.npm")),
|
|
189
|
+
};
|
|
190
|
+
//# sourceMappingURL=npm.js.map
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NuGet package detector and reader for package-compatibility discovery.
|
|
3
|
+
*
|
|
4
|
+
* @experimental This API is unstable and may change without notice.
|
|
5
|
+
* @packageDocumentation
|
|
6
|
+
*/
|
|
7
|
+
import type { PackageDetector, PackageReader } from "./types.js";
|
|
8
|
+
/**
|
|
9
|
+
* NuGet package detector.
|
|
10
|
+
*
|
|
11
|
+
* Scans `.csproj`, `.fsproj`, `.vbproj`, `Directory.Packages.props`,
|
|
12
|
+
* and `packages.config` files in the project directory.
|
|
13
|
+
*
|
|
14
|
+
* @experimental This API is unstable and may change without notice.
|
|
15
|
+
*/
|
|
16
|
+
export declare const nugetDetector: PackageDetector;
|
|
17
|
+
/**
|
|
18
|
+
* NuGet package reader.
|
|
19
|
+
*
|
|
20
|
+
* Reads `axm.json` from `~/.nuget/packages/{id}/{version}/` for each
|
|
21
|
+
* detected NuGet package and extracts recommendation metadata.
|
|
22
|
+
*
|
|
23
|
+
* @experimental This API is unstable and may change without notice.
|
|
24
|
+
*/
|
|
25
|
+
export declare const nugetReader: PackageReader;
|
|
26
|
+
//# sourceMappingURL=nuget.d.ts.map
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NuGet package detector and reader for package-compatibility discovery.
|
|
3
|
+
*
|
|
4
|
+
* @experimental This API is unstable and may change without notice.
|
|
5
|
+
* @packageDocumentation
|
|
6
|
+
*/
|
|
7
|
+
// Intentional escape hatch: node:os homedir() has no @effect/platform equivalent.
|
|
8
|
+
import * as os from "node:os";
|
|
9
|
+
import * as Effect from "effect/Effect";
|
|
10
|
+
import * as FileSystem from "effect/FileSystem";
|
|
11
|
+
import * as Option from "effect/Option";
|
|
12
|
+
import * as Path from "effect/Path";
|
|
13
|
+
import * as Result from "effect/Result";
|
|
14
|
+
import * as Schema from "effect/Schema";
|
|
15
|
+
import { readEnv } from "../internal/environment.js";
|
|
16
|
+
import { makeDetectedPackage } from "./detected-package.js";
|
|
17
|
+
import { PackageTypeSchema } from "@agentxm/extension-model/unstable/packaging/package-type";
|
|
18
|
+
import { decodeAxmMeta, parseJsonOptional, readFileOptional } from "./reader-io.js";
|
|
19
|
+
const nugetType = Schema.decodeUnknownSync(PackageTypeSchema)("nuget");
|
|
20
|
+
/**
|
|
21
|
+
* Returns true if the version string is a NuGet version range.
|
|
22
|
+
* Ranges use brackets/parens like [1.0,2.0), (,1.0], etc.
|
|
23
|
+
*/
|
|
24
|
+
const isNugetVersionRange = (version) => /^[[(]/.test(version) || /[)\]]$/.test(version);
|
|
25
|
+
/**
|
|
26
|
+
* Returns true if the version string is a floating version (e.g., "1.0.*").
|
|
27
|
+
*/
|
|
28
|
+
const isFloatingVersion = (version) => version.includes("*");
|
|
29
|
+
/**
|
|
30
|
+
* Returns true if the version is an exact version (not a range or floating).
|
|
31
|
+
*/
|
|
32
|
+
const isExactVersion = (version) => !isNugetVersionRange(version) && !isFloatingVersion(version) && version.trim() !== "";
|
|
33
|
+
/**
|
|
34
|
+
* Create a DetectedPackage from a NuGet package name and optional version.
|
|
35
|
+
* Names are lowercased per NuGet case-insensitivity.
|
|
36
|
+
*/
|
|
37
|
+
const makeNugetPackage = (name, version, source) => {
|
|
38
|
+
const loweredName = name.toLowerCase();
|
|
39
|
+
const resolvedVersion = version !== undefined && isExactVersion(version) ? version : undefined;
|
|
40
|
+
return Option.getOrUndefined(makeDetectedPackage({
|
|
41
|
+
type: nugetType,
|
|
42
|
+
name: loweredName,
|
|
43
|
+
...(resolvedVersion === undefined ? {} : { version: resolvedVersion }),
|
|
44
|
+
source,
|
|
45
|
+
}));
|
|
46
|
+
};
|
|
47
|
+
const appendNugetPackage = (results, name, version, source) => {
|
|
48
|
+
const detected = makeNugetPackage(name, version, source);
|
|
49
|
+
if (detected !== undefined)
|
|
50
|
+
results.push(detected);
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* Extract an XML attribute value from a tag string.
|
|
54
|
+
*/
|
|
55
|
+
const extractAttribute = (tag, attrName) => {
|
|
56
|
+
const regex = new RegExp(`${attrName}\\s*=\\s*"([^"]*)"`, "i");
|
|
57
|
+
const match = regex.exec(tag);
|
|
58
|
+
return match?.[1];
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* Parse .csproj/.fsproj/.vbproj content and extract PackageReference elements.
|
|
62
|
+
*/
|
|
63
|
+
const parseProjectFile = (content, source) => {
|
|
64
|
+
const results = [];
|
|
65
|
+
// Match self-closing PackageReference tags
|
|
66
|
+
const selfClosingRegex = /<PackageReference\s+[^>]*\/>/gi;
|
|
67
|
+
let match;
|
|
68
|
+
while ((match = selfClosingRegex.exec(content)) !== null) {
|
|
69
|
+
const tag = match[0];
|
|
70
|
+
const name = extractAttribute(tag, "Include");
|
|
71
|
+
if (name === undefined)
|
|
72
|
+
continue;
|
|
73
|
+
const version = extractAttribute(tag, "Version");
|
|
74
|
+
appendNugetPackage(results, name, version, source);
|
|
75
|
+
}
|
|
76
|
+
// Match PackageReference with child elements (e.g. <Version> child)
|
|
77
|
+
const blockRegex = /<PackageReference\s+([^>]*)>([\s\S]*?)<\/PackageReference>/gi;
|
|
78
|
+
while ((match = blockRegex.exec(content)) !== null) {
|
|
79
|
+
const attrs = match[1];
|
|
80
|
+
const body = match[2];
|
|
81
|
+
if (attrs === undefined)
|
|
82
|
+
continue;
|
|
83
|
+
const name = extractAttribute(attrs, "Include");
|
|
84
|
+
if (name === undefined)
|
|
85
|
+
continue;
|
|
86
|
+
// Try attribute first, then child element
|
|
87
|
+
let version = extractAttribute(attrs, "Version");
|
|
88
|
+
if (version === undefined && body !== undefined) {
|
|
89
|
+
const versionMatch = /<Version>\s*(.*?)\s*<\/Version>/i.exec(body);
|
|
90
|
+
version = versionMatch?.[1];
|
|
91
|
+
}
|
|
92
|
+
appendNugetPackage(results, name, version, source);
|
|
93
|
+
}
|
|
94
|
+
return results;
|
|
95
|
+
};
|
|
96
|
+
/**
|
|
97
|
+
* Parse Directory.Packages.props content and extract PackageVersion elements.
|
|
98
|
+
*/
|
|
99
|
+
const parseDirectoryPackagesProps = (content, source) => {
|
|
100
|
+
const results = [];
|
|
101
|
+
const pkgVersionRegex = /<PackageVersion\s+[^>]*\/>/gi;
|
|
102
|
+
let match;
|
|
103
|
+
while ((match = pkgVersionRegex.exec(content)) !== null) {
|
|
104
|
+
const tag = match[0];
|
|
105
|
+
const name = extractAttribute(tag, "Include");
|
|
106
|
+
if (name === undefined)
|
|
107
|
+
continue;
|
|
108
|
+
const version = extractAttribute(tag, "Version");
|
|
109
|
+
appendNugetPackage(results, name, version, source);
|
|
110
|
+
}
|
|
111
|
+
return results;
|
|
112
|
+
};
|
|
113
|
+
/**
|
|
114
|
+
* Parse packages.config content and extract package elements.
|
|
115
|
+
*/
|
|
116
|
+
const parsePackagesConfig = (content, source) => {
|
|
117
|
+
const results = [];
|
|
118
|
+
const pkgRegex = /<package\s+[^>]*\/>/gi;
|
|
119
|
+
let match;
|
|
120
|
+
while ((match = pkgRegex.exec(content)) !== null) {
|
|
121
|
+
const tag = match[0];
|
|
122
|
+
const name = extractAttribute(tag, "id");
|
|
123
|
+
if (name === undefined)
|
|
124
|
+
continue;
|
|
125
|
+
const version = extractAttribute(tag, "version");
|
|
126
|
+
appendNugetPackage(results, name, version, source);
|
|
127
|
+
}
|
|
128
|
+
return results;
|
|
129
|
+
};
|
|
130
|
+
/**
|
|
131
|
+
* Deduplicate packages by lowercased name.
|
|
132
|
+
*/
|
|
133
|
+
const deduplicatePackages = (packages) => {
|
|
134
|
+
const seen = new Set();
|
|
135
|
+
const results = [];
|
|
136
|
+
for (const pkg of packages) {
|
|
137
|
+
const key = pkg.purl.name.toLowerCase();
|
|
138
|
+
if (!seen.has(key)) {
|
|
139
|
+
seen.add(key);
|
|
140
|
+
results.push(pkg);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return results;
|
|
144
|
+
};
|
|
145
|
+
/**
|
|
146
|
+
* NuGet package detector.
|
|
147
|
+
*
|
|
148
|
+
* Scans `.csproj`, `.fsproj`, `.vbproj`, `Directory.Packages.props`,
|
|
149
|
+
* and `packages.config` files in the project directory.
|
|
150
|
+
*
|
|
151
|
+
* @experimental This API is unstable and may change without notice.
|
|
152
|
+
*/
|
|
153
|
+
export const nugetDetector = {
|
|
154
|
+
type: nugetType,
|
|
155
|
+
detect: Effect.fn("detect.nuget")(function* (projectDir) {
|
|
156
|
+
const path = yield* Path.Path;
|
|
157
|
+
const fs = yield* FileSystem.FileSystem;
|
|
158
|
+
const allPackages = [];
|
|
159
|
+
// Find and parse project files (*.csproj, *.fsproj, *.vbproj)
|
|
160
|
+
const entries = yield* fs.readDirectory(projectDir).pipe(Effect.option);
|
|
161
|
+
if (Option.isSome(entries)) {
|
|
162
|
+
const projectFiles = entries.value.filter((e) => e.endsWith(".csproj") || e.endsWith(".fsproj") || e.endsWith(".vbproj"));
|
|
163
|
+
for (const file of projectFiles) {
|
|
164
|
+
const filePath = path.join(projectDir, file);
|
|
165
|
+
const content = yield* readFileOptional(filePath);
|
|
166
|
+
if (Option.isSome(content)) {
|
|
167
|
+
if (content.value.includes("<")) {
|
|
168
|
+
const deps = parseProjectFile(content.value, filePath);
|
|
169
|
+
allPackages.push(...deps);
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
yield* Effect.logWarning(`Malformed ${file}: not valid XML, skipping`);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
// Parse Directory.Packages.props
|
|
178
|
+
const propsPath = path.join(projectDir, "Directory.Packages.props");
|
|
179
|
+
const propsContent = yield* readFileOptional(propsPath);
|
|
180
|
+
if (Option.isSome(propsContent)) {
|
|
181
|
+
if (propsContent.value.includes("<")) {
|
|
182
|
+
const propsDeps = parseDirectoryPackagesProps(propsContent.value, propsPath);
|
|
183
|
+
allPackages.push(...propsDeps);
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
yield* Effect.logWarning("Malformed Directory.Packages.props: not valid XML, skipping");
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
// Parse packages.config
|
|
190
|
+
const packagesConfigPath = path.join(projectDir, "packages.config");
|
|
191
|
+
const packagesConfigContent = yield* readFileOptional(packagesConfigPath);
|
|
192
|
+
if (Option.isSome(packagesConfigContent)) {
|
|
193
|
+
if (packagesConfigContent.value.includes("<")) {
|
|
194
|
+
const configDeps = parsePackagesConfig(packagesConfigContent.value, packagesConfigPath);
|
|
195
|
+
allPackages.push(...configDeps);
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
yield* Effect.logWarning("Malformed packages.config: not valid XML, skipping");
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return deduplicatePackages(allPackages);
|
|
202
|
+
}, Effect.annotateLogs({ detector: "nuget" }), Effect.withSpan("detect.nuget")),
|
|
203
|
+
};
|
|
204
|
+
/**
|
|
205
|
+
* Resolve the NuGet packages folder path.
|
|
206
|
+
*/
|
|
207
|
+
const resolveNugetPackagesFolder = () => Effect.sync(() => readEnv("NUGET_PACKAGES") ?? `${os.homedir()}/.nuget/packages`);
|
|
208
|
+
/**
|
|
209
|
+
* NuGet package reader.
|
|
210
|
+
*
|
|
211
|
+
* Reads `axm.json` from `~/.nuget/packages/{id}/{version}/` for each
|
|
212
|
+
* detected NuGet package and extracts recommendation metadata.
|
|
213
|
+
*
|
|
214
|
+
* @experimental This API is unstable and may change without notice.
|
|
215
|
+
*/
|
|
216
|
+
export const nugetReader = {
|
|
217
|
+
type: nugetType,
|
|
218
|
+
read: Effect.fn("read.nuget")(function* (pkg) {
|
|
219
|
+
const path = yield* Path.Path;
|
|
220
|
+
const packagesFolder = yield* resolveNugetPackagesFolder();
|
|
221
|
+
// NuGet uses lowercased package IDs in directory names
|
|
222
|
+
const pkgId = pkg.purl.name.toLowerCase();
|
|
223
|
+
const version = pkg.purl.version ?? "0.0.0";
|
|
224
|
+
const axmJsonPath = path.join(packagesFolder, pkgId, version, "axm.json");
|
|
225
|
+
const content = yield* readFileOptional(axmJsonPath);
|
|
226
|
+
if (Option.isNone(content))
|
|
227
|
+
return Option.none();
|
|
228
|
+
const parsed = yield* parseJsonOptional(content.value, `${pkgId}@${version}/axm.json`);
|
|
229
|
+
if (Option.isNone(parsed))
|
|
230
|
+
return Option.none();
|
|
231
|
+
// Validate axm metadata structure
|
|
232
|
+
const metaResult = decodeAxmMeta(parsed.value);
|
|
233
|
+
if (Result.isFailure(metaResult)) {
|
|
234
|
+
yield* Effect.logWarning(`Invalid axm metadata in ${pkgId}@${version}: schema validation failed`);
|
|
235
|
+
return Option.none();
|
|
236
|
+
}
|
|
237
|
+
return Option.some(metaResult.success.extensions);
|
|
238
|
+
}, Effect.annotateLogs({ reader: "nuget" }), Effect.withSpan("read.nuget")),
|
|
239
|
+
};
|
|
240
|
+
//# sourceMappingURL=nuget.js.map
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* opam (OCaml) package detector and reader for package-compatibility discovery.
|
|
3
|
+
*
|
|
4
|
+
* @experimental This API is unstable and may change without notice.
|
|
5
|
+
* @packageDocumentation
|
|
6
|
+
*/
|
|
7
|
+
import type { PackageDetector, PackageReader } from "./types.js";
|
|
8
|
+
/**
|
|
9
|
+
* opam package detector.
|
|
10
|
+
*
|
|
11
|
+
* Scans `*.opam` files for `depends` fields and `dune-project` for
|
|
12
|
+
* `(depends ...)` s-expressions. Skips `ocaml` and `dune` build tooling.
|
|
13
|
+
* Dependencies are deduplicated by name.
|
|
14
|
+
*
|
|
15
|
+
* @experimental This API is unstable and may change without notice.
|
|
16
|
+
*/
|
|
17
|
+
export declare const opamDetector: PackageDetector;
|
|
18
|
+
/**
|
|
19
|
+
* opam package reader.
|
|
20
|
+
*
|
|
21
|
+
* Reads `x-axm` prefixed custom fields from `.opam` files in the
|
|
22
|
+
* opam switch at `~/.opam/<switch>/lib/<pkg>/opam`.
|
|
23
|
+
*
|
|
24
|
+
* @experimental This API is unstable and may change without notice.
|
|
25
|
+
*/
|
|
26
|
+
export declare const opamReader: PackageReader;
|
|
27
|
+
//# sourceMappingURL=opam.d.ts.map
|