@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,270 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cargo (Rust) 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 { PackageURL } from "packageurl-js";
|
|
16
|
+
import { parseTomlValue } from "@agentxm/extension-workspace";
|
|
17
|
+
import { envWithDefault } from "../internal/environment.js";
|
|
18
|
+
import { PackageTypeSchema } from "@agentxm/extension-model/unstable/packaging/package-type";
|
|
19
|
+
import { decodeAxmMeta, decodePurl, readFileOptional } from "./reader-io.js";
|
|
20
|
+
const cargoType = Schema.decodeUnknownSync(PackageTypeSchema)("cargo");
|
|
21
|
+
/**
|
|
22
|
+
* Returns true if the version specifier is an exact pin (starts with `=`).
|
|
23
|
+
* In Cargo, `=1.0.193` means exact; bare `1.0` is a caret range.
|
|
24
|
+
*/
|
|
25
|
+
const isExactVersion = (specifier) => specifier.startsWith("=");
|
|
26
|
+
/**
|
|
27
|
+
* Strip leading `=` from an exact version pin.
|
|
28
|
+
*/
|
|
29
|
+
const stripExactPrefix = (specifier) => specifier.startsWith("=") ? specifier.slice(1) : specifier;
|
|
30
|
+
/** Regex to detect section headers like [dependencies] or [build-dependencies]. */
|
|
31
|
+
const SECTION_RE = /^\[([^\]]+)\]/;
|
|
32
|
+
/** Dependency section names we care about. */
|
|
33
|
+
const DEP_SECTIONS = new Set(["dependencies", "dev-dependencies", "build-dependencies"]);
|
|
34
|
+
/**
|
|
35
|
+
* Parse a dependency value (the right-hand side of `name = ...`).
|
|
36
|
+
* Handles:
|
|
37
|
+
* - Shorthand string: `"1.0"` or `"=1.0.193"`
|
|
38
|
+
* - Inline table: `{ version = "1.0", features = ["derive"] }`
|
|
39
|
+
*/
|
|
40
|
+
const parseDependencyValue = (name, value) => {
|
|
41
|
+
const trimmed = value.trim();
|
|
42
|
+
// Shorthand string syntax: name = "1.0"
|
|
43
|
+
if (trimmed.startsWith('"') || trimmed.startsWith("'")) {
|
|
44
|
+
const inner = trimmed.slice(1, -1);
|
|
45
|
+
return { name, version: inner, isPathOrGit: false, packageName: undefined };
|
|
46
|
+
}
|
|
47
|
+
// Inline table syntax: name = { version = "1.0", ... }
|
|
48
|
+
if (trimmed.startsWith("{")) {
|
|
49
|
+
const hasPath = /\bpath\s*=/.test(trimmed);
|
|
50
|
+
const hasGit = /\bgit\s*=/.test(trimmed);
|
|
51
|
+
if (hasPath || hasGit) {
|
|
52
|
+
return { name, version: undefined, isPathOrGit: true, packageName: undefined };
|
|
53
|
+
}
|
|
54
|
+
// Extract version
|
|
55
|
+
const versionMatch = /\bversion\s*=\s*"([^"]*)"/.exec(trimmed);
|
|
56
|
+
const version = versionMatch?.[1];
|
|
57
|
+
// Extract package rename
|
|
58
|
+
const packageMatch = /\bpackage\s*=\s*"([^"]*)"/.exec(trimmed);
|
|
59
|
+
const packageName = packageMatch?.[1];
|
|
60
|
+
return { name, version, isPathOrGit: false, packageName };
|
|
61
|
+
}
|
|
62
|
+
// Unknown format, skip
|
|
63
|
+
return { name, version: undefined, isPathOrGit: false, packageName: undefined };
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* Parse Cargo.toml content and extract dependencies.
|
|
67
|
+
*/
|
|
68
|
+
const parseCargoToml = (content, source) => {
|
|
69
|
+
const lines = content.split("\n");
|
|
70
|
+
const results = [];
|
|
71
|
+
let currentSection = "";
|
|
72
|
+
for (const line of lines) {
|
|
73
|
+
const trimmed = line.trim();
|
|
74
|
+
// Skip empty lines and comments
|
|
75
|
+
if (trimmed === "" || trimmed.startsWith("#"))
|
|
76
|
+
continue;
|
|
77
|
+
// Check for section header
|
|
78
|
+
const sectionMatch = SECTION_RE.exec(trimmed);
|
|
79
|
+
if (sectionMatch !== undefined && sectionMatch !== null && sectionMatch[1] !== undefined) {
|
|
80
|
+
currentSection = sectionMatch[1];
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
// Only process lines in dependency sections
|
|
84
|
+
if (!DEP_SECTIONS.has(currentSection))
|
|
85
|
+
continue;
|
|
86
|
+
// Parse key = value
|
|
87
|
+
const eqIdx = trimmed.indexOf("=");
|
|
88
|
+
if (eqIdx === -1)
|
|
89
|
+
continue;
|
|
90
|
+
const depName = trimmed.slice(0, eqIdx).trim();
|
|
91
|
+
const depValue = trimmed.slice(eqIdx + 1).trim();
|
|
92
|
+
if (depName === "" || depValue === "")
|
|
93
|
+
continue;
|
|
94
|
+
const dep = parseDependencyValue(depName, depValue);
|
|
95
|
+
// Skip path and git dependencies
|
|
96
|
+
if (dep.isPathOrGit)
|
|
97
|
+
continue;
|
|
98
|
+
// Use the real package name if renamed
|
|
99
|
+
const resolvedName = dep.packageName ?? dep.name;
|
|
100
|
+
// Determine version: only exact pins get a version in the purl
|
|
101
|
+
const version = dep.version !== undefined && isExactVersion(dep.version)
|
|
102
|
+
? stripExactPrefix(dep.version)
|
|
103
|
+
: undefined;
|
|
104
|
+
const purl = new PackageURL("cargo", null, resolvedName, version ?? null, null, null);
|
|
105
|
+
const purlParts = decodePurl(purl.toString());
|
|
106
|
+
results.push({ purl: purlParts, type: cargoType, source });
|
|
107
|
+
}
|
|
108
|
+
return results;
|
|
109
|
+
};
|
|
110
|
+
/**
|
|
111
|
+
* Cargo package detector.
|
|
112
|
+
*
|
|
113
|
+
* Scans `Cargo.toml` in the project directory and extracts dependencies
|
|
114
|
+
* from `[dependencies]`, `[dev-dependencies]`, and `[build-dependencies]`.
|
|
115
|
+
*
|
|
116
|
+
* @experimental This API is unstable and may change without notice.
|
|
117
|
+
*/
|
|
118
|
+
export const cargoDetector = {
|
|
119
|
+
type: cargoType,
|
|
120
|
+
detect: Effect.fn("detect.cargo")(function* (projectDir) {
|
|
121
|
+
const path = yield* Path.Path;
|
|
122
|
+
const cargoTomlPath = path.join(projectDir, "Cargo.toml");
|
|
123
|
+
const content = yield* readFileOptional(cargoTomlPath);
|
|
124
|
+
if (Option.isNone(content))
|
|
125
|
+
return [];
|
|
126
|
+
// Validate that it looks like TOML (basic check)
|
|
127
|
+
const trimmed = content.value.trim();
|
|
128
|
+
if (trimmed.length > 0 && !trimmed.includes("=") && !trimmed.includes("[")) {
|
|
129
|
+
yield* Effect.logWarning("Malformed Cargo.toml, skipping");
|
|
130
|
+
return [];
|
|
131
|
+
}
|
|
132
|
+
return parseCargoToml(content.value, cargoTomlPath);
|
|
133
|
+
}, Effect.annotateLogs({ detector: "cargo" }), Effect.withSpan("detect.cargo")),
|
|
134
|
+
};
|
|
135
|
+
/**
|
|
136
|
+
* Resolve the CARGO_HOME, defaulting to ~/.cargo when not set.
|
|
137
|
+
*/
|
|
138
|
+
const resolveCargoHome = () => envWithDefault("CARGO_HOME", `${os.homedir()}/.cargo`);
|
|
139
|
+
/**
|
|
140
|
+
* Parse the `[package.metadata.axm]` table from a Cargo.toml string.
|
|
141
|
+
*
|
|
142
|
+
* Returns `undefined` when the section is absent. `Cargo.toml` is TOML; we
|
|
143
|
+
* scan section headers line-by-line rather than depending on a full TOML
|
|
144
|
+
* parser, mirroring `julia.ts`/`parseAxmSection`. Supported forms:
|
|
145
|
+
*
|
|
146
|
+
* [package.metadata.axm]
|
|
147
|
+
* extensions = [{ ref = "@owner/packs/example", versionRange = "^1.0.0" }]
|
|
148
|
+
*
|
|
149
|
+
* [[package.metadata.axm.extensions]]
|
|
150
|
+
* ref = "@owner/packs/example"
|
|
151
|
+
* versionRange = "^1.0.0"
|
|
152
|
+
*/
|
|
153
|
+
const parsePackageMetadataAxm = (content) => {
|
|
154
|
+
const lines = content.split("\n");
|
|
155
|
+
let inAxmSection = false;
|
|
156
|
+
let found = false;
|
|
157
|
+
const fields = {};
|
|
158
|
+
const extensionEntries = [];
|
|
159
|
+
let currentTable;
|
|
160
|
+
for (const line of lines) {
|
|
161
|
+
const trimmed = line.trim();
|
|
162
|
+
// Skip empty lines and comments
|
|
163
|
+
if (trimmed === "" || trimmed.startsWith("#"))
|
|
164
|
+
continue;
|
|
165
|
+
// Section headers (`[section.path]` or `[[array.of.tables]]`)
|
|
166
|
+
if (trimmed.startsWith("[")) {
|
|
167
|
+
const arrayTableMatch = /^\[\[package\.metadata\.axm\.([A-Za-z0-9_-]+)\]\]$/.exec(trimmed);
|
|
168
|
+
const arrayTableKey = arrayTableMatch?.[1];
|
|
169
|
+
if (arrayTableKey !== undefined) {
|
|
170
|
+
found = true;
|
|
171
|
+
inAxmSection = false;
|
|
172
|
+
if (arrayTableKey === "extensions") {
|
|
173
|
+
const entry = {};
|
|
174
|
+
extensionEntries.push(entry);
|
|
175
|
+
fields["extensions"] = extensionEntries;
|
|
176
|
+
currentTable = entry;
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
currentTable = undefined;
|
|
180
|
+
}
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
const axmTableMatch = /^\[package\.metadata\.axm(?:\.([A-Za-z0-9_-]+))?\]$/.exec(trimmed);
|
|
184
|
+
if (axmTableMatch !== null) {
|
|
185
|
+
found = true;
|
|
186
|
+
inAxmSection = axmTableMatch[1] === undefined;
|
|
187
|
+
currentTable = undefined;
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
inAxmSection = false;
|
|
191
|
+
currentTable = undefined;
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
const target = currentTable ?? (inAxmSection ? fields : undefined);
|
|
195
|
+
if (target === undefined)
|
|
196
|
+
continue;
|
|
197
|
+
const match = /^([^=\s]+)\s*=\s*(.+)$/.exec(trimmed);
|
|
198
|
+
if (match === null)
|
|
199
|
+
continue;
|
|
200
|
+
const key = match[1];
|
|
201
|
+
const rawValue = match[2]?.trim();
|
|
202
|
+
if (key === undefined || rawValue === undefined)
|
|
203
|
+
continue;
|
|
204
|
+
target[key] = parseTomlValue(rawValue);
|
|
205
|
+
}
|
|
206
|
+
return found ? fields : undefined;
|
|
207
|
+
};
|
|
208
|
+
/**
|
|
209
|
+
* Cargo package reader.
|
|
210
|
+
*
|
|
211
|
+
* Reads `[package.metadata.axm]` from
|
|
212
|
+
* `$CARGO_HOME/registry/src/<index>/<crate>-<version>/Cargo.toml` for each
|
|
213
|
+
* detected cargo crate. `[package.metadata.*]` is Cargo's standard
|
|
214
|
+
* extensibility mechanism for third-party tools (used by docs.rs, cargo-deb,
|
|
215
|
+
* cargo-bundle, etc.).
|
|
216
|
+
*
|
|
217
|
+
* When the crate version is unknown, scans the registry source directory
|
|
218
|
+
* for any matching crate directory.
|
|
219
|
+
*
|
|
220
|
+
* @experimental This API is unstable and may change without notice.
|
|
221
|
+
*/
|
|
222
|
+
export const cargoReader = {
|
|
223
|
+
type: cargoType,
|
|
224
|
+
read: Effect.fn("read.cargo")(function* (pkg) {
|
|
225
|
+
const fs = yield* FileSystem.FileSystem;
|
|
226
|
+
const path = yield* Path.Path;
|
|
227
|
+
const cargoHome = yield* resolveCargoHome();
|
|
228
|
+
const crateName = pkg.purl.name;
|
|
229
|
+
const version = pkg.purl.version;
|
|
230
|
+
const registrySrcDir = path.join(cargoHome, "registry", "src");
|
|
231
|
+
const indexDirs = yield* fs.readDirectory(registrySrcDir).pipe(Effect.option);
|
|
232
|
+
if (Option.isNone(indexDirs))
|
|
233
|
+
return Option.none();
|
|
234
|
+
for (const indexDir of indexDirs.value) {
|
|
235
|
+
const indexPath = path.join(registrySrcDir, indexDir);
|
|
236
|
+
const crateDir = version !== undefined
|
|
237
|
+
? `${crateName}-${version}`
|
|
238
|
+
: yield* findMatchingCrateDir(fs, indexPath, crateName);
|
|
239
|
+
if (crateDir === undefined)
|
|
240
|
+
continue;
|
|
241
|
+
const cargoTomlPath = path.join(indexPath, crateDir, "Cargo.toml");
|
|
242
|
+
const content = yield* readFileOptional(cargoTomlPath);
|
|
243
|
+
if (Option.isNone(content)) {
|
|
244
|
+
if (version !== undefined)
|
|
245
|
+
return Option.none();
|
|
246
|
+
continue;
|
|
247
|
+
}
|
|
248
|
+
const axmFields = parsePackageMetadataAxm(content.value);
|
|
249
|
+
if (axmFields === undefined) {
|
|
250
|
+
if (version !== undefined)
|
|
251
|
+
return Option.none();
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
254
|
+
const metaResult = decodeAxmMeta(axmFields);
|
|
255
|
+
if (Result.isFailure(metaResult)) {
|
|
256
|
+
yield* Effect.logWarning(`Invalid axm metadata in ${crateDir}/Cargo.toml: schema validation failed`);
|
|
257
|
+
return Option.none();
|
|
258
|
+
}
|
|
259
|
+
return Option.some(metaResult.success.extensions);
|
|
260
|
+
}
|
|
261
|
+
return Option.none();
|
|
262
|
+
}, Effect.annotateLogs({ reader: "cargo" }), Effect.withSpan("read.cargo")),
|
|
263
|
+
};
|
|
264
|
+
const findMatchingCrateDir = (fs, indexPath, crateName) => Effect.gen(function* () {
|
|
265
|
+
const entries = yield* fs.readDirectory(indexPath).pipe(Effect.option);
|
|
266
|
+
if (Option.isNone(entries))
|
|
267
|
+
return undefined;
|
|
268
|
+
return entries.value.find((entry) => entry === crateName || entry.startsWith(`${crateName}-`));
|
|
269
|
+
});
|
|
270
|
+
//# sourceMappingURL=cargo.js.map
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CocoaPods 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
|
+
* CocoaPods package detector.
|
|
10
|
+
*
|
|
11
|
+
* Scans `Podfile` and `*.podspec` files in the project directory
|
|
12
|
+
* for pod dependencies.
|
|
13
|
+
*
|
|
14
|
+
* @experimental This API is unstable and may change without notice.
|
|
15
|
+
*/
|
|
16
|
+
export declare const cocoapodsDetector: PackageDetector;
|
|
17
|
+
/**
|
|
18
|
+
* CocoaPods package reader.
|
|
19
|
+
*
|
|
20
|
+
* Reads `Pods/<pod-name>/axm.json` for each detected pod
|
|
21
|
+
* and extracts recommendation metadata.
|
|
22
|
+
*
|
|
23
|
+
* @experimental This API is unstable and may change without notice.
|
|
24
|
+
*/
|
|
25
|
+
export declare const cocoapodsReader: PackageReader;
|
|
26
|
+
//# sourceMappingURL=cocoapods.d.ts.map
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CocoaPods 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 FileSystem from "effect/FileSystem";
|
|
9
|
+
import * as Option from "effect/Option";
|
|
10
|
+
import * as Path from "effect/Path";
|
|
11
|
+
import * as Result from "effect/Result";
|
|
12
|
+
import * as Schema from "effect/Schema";
|
|
13
|
+
import { PackageURL } from "packageurl-js";
|
|
14
|
+
import { PackageTypeSchema } from "@agentxm/extension-model/unstable/packaging/package-type";
|
|
15
|
+
import { decodeAxmMeta, decodePurl, parseJsonOptional, readFileOptional } from "./reader-io.js";
|
|
16
|
+
const cocoapodsType = Schema.decodeUnknownSync(PackageTypeSchema)("cocoapods");
|
|
17
|
+
/**
|
|
18
|
+
* Returns true if the specifier is an exact semver version (no range operators).
|
|
19
|
+
*/
|
|
20
|
+
const isExactVersion = (specifier) => /^\d+\.\d+\.\d+(?:[-+][a-zA-Z0-9._+-]*)?$/.test(specifier);
|
|
21
|
+
/**
|
|
22
|
+
* Convert a pod name (possibly with subspec) and version to a DetectedPackage.
|
|
23
|
+
*/
|
|
24
|
+
const podToPurl = (podFullName, version, source) => {
|
|
25
|
+
// Split on '/' to separate name from subspecs
|
|
26
|
+
const parts = podFullName.split("/");
|
|
27
|
+
const name = parts[0];
|
|
28
|
+
if (name === undefined || name.length === 0)
|
|
29
|
+
return undefined;
|
|
30
|
+
const subpath = parts.length > 1 ? parts.slice(1).join("/") : null;
|
|
31
|
+
const resolvedVersion = version !== undefined && isExactVersion(version) ? version : undefined;
|
|
32
|
+
const purl = new PackageURL("cocoapods", null, name, resolvedVersion ?? null, null, subpath);
|
|
33
|
+
const purlParts = decodePurl(purl.toString());
|
|
34
|
+
return { purl: purlParts, type: cocoapodsType, source };
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Parse a Podfile and extract pod directives.
|
|
38
|
+
*
|
|
39
|
+
* Matches lines like:
|
|
40
|
+
* - pod 'Alamofire', '~> 5.0'
|
|
41
|
+
* - pod 'Alamofire', '5.6.2'
|
|
42
|
+
* - pod 'Alamofire'
|
|
43
|
+
* - pod 'ShareKit/Twitter'
|
|
44
|
+
* - pod 'MyPod', :path => '../MyPod' (skipped)
|
|
45
|
+
* - pod 'MyPod', :git => 'https://...' (skipped)
|
|
46
|
+
*/
|
|
47
|
+
const parsePodfile = (content, source) => {
|
|
48
|
+
const lines = content.split("\n");
|
|
49
|
+
const results = [];
|
|
50
|
+
// Match: pod 'Name' or pod "Name" with optional version and options
|
|
51
|
+
const podPattern = /^\s*pod\s+['"]([^'"]+)['"]\s*(?:,\s*(.*))?$/;
|
|
52
|
+
for (const line of lines) {
|
|
53
|
+
const trimmed = line.trim();
|
|
54
|
+
if (trimmed.startsWith("#"))
|
|
55
|
+
continue;
|
|
56
|
+
const match = podPattern.exec(trimmed);
|
|
57
|
+
if (match === null)
|
|
58
|
+
continue;
|
|
59
|
+
const podName = match[1];
|
|
60
|
+
const rest = match[2];
|
|
61
|
+
if (podName === undefined)
|
|
62
|
+
continue;
|
|
63
|
+
// Skip :path and :git dependencies
|
|
64
|
+
if (rest !== undefined) {
|
|
65
|
+
if (rest.includes(":path") || rest.includes(":git"))
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
// Extract version from rest
|
|
69
|
+
let version;
|
|
70
|
+
if (rest !== undefined) {
|
|
71
|
+
// Match version string: '5.6.2' or '~> 5.0' or '>= 5.0'
|
|
72
|
+
const versionMatch = /^['"]([^'"]+)['"]/.exec(rest.trim());
|
|
73
|
+
if (versionMatch !== null && versionMatch[1] !== undefined) {
|
|
74
|
+
const versionStr = versionMatch[1];
|
|
75
|
+
// Only use exact versions, not ranges
|
|
76
|
+
if (isExactVersion(versionStr)) {
|
|
77
|
+
version = versionStr;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
const detected = podToPurl(podName, version, source);
|
|
82
|
+
if (detected !== undefined)
|
|
83
|
+
results.push(detected);
|
|
84
|
+
}
|
|
85
|
+
return results;
|
|
86
|
+
};
|
|
87
|
+
/**
|
|
88
|
+
* Parse a .podspec file and extract dependency directives.
|
|
89
|
+
*
|
|
90
|
+
* Matches lines like:
|
|
91
|
+
* - s.dependency 'Alamofire', '~> 5.0'
|
|
92
|
+
* - spec.dependency 'SwiftyJSON'
|
|
93
|
+
* - ss.dependency 'Something'
|
|
94
|
+
*/
|
|
95
|
+
const parsePodspec = (content, source) => {
|
|
96
|
+
const lines = content.split("\n");
|
|
97
|
+
const results = [];
|
|
98
|
+
// Match: <var>.dependency 'Name', 'version' or <var>.dependency 'Name'
|
|
99
|
+
const depPattern = /^\s*\w+\.dependency\s+['"]([^'"]+)['"]\s*(?:,\s*['"]([^'"]+)['"])?/;
|
|
100
|
+
for (const line of lines) {
|
|
101
|
+
const trimmed = line.trim();
|
|
102
|
+
if (trimmed.startsWith("#"))
|
|
103
|
+
continue;
|
|
104
|
+
const match = depPattern.exec(trimmed);
|
|
105
|
+
if (match === null)
|
|
106
|
+
continue;
|
|
107
|
+
const depName = match[1];
|
|
108
|
+
const versionStr = match[2];
|
|
109
|
+
if (depName === undefined)
|
|
110
|
+
continue;
|
|
111
|
+
const version = versionStr !== undefined && isExactVersion(versionStr) ? versionStr : undefined;
|
|
112
|
+
const detected = podToPurl(depName, version, source);
|
|
113
|
+
if (detected !== undefined)
|
|
114
|
+
results.push(detected);
|
|
115
|
+
}
|
|
116
|
+
return results;
|
|
117
|
+
};
|
|
118
|
+
/**
|
|
119
|
+
* CocoaPods package detector.
|
|
120
|
+
*
|
|
121
|
+
* Scans `Podfile` and `*.podspec` files in the project directory
|
|
122
|
+
* for pod dependencies.
|
|
123
|
+
*
|
|
124
|
+
* @experimental This API is unstable and may change without notice.
|
|
125
|
+
*/
|
|
126
|
+
export const cocoapodsDetector = {
|
|
127
|
+
type: cocoapodsType,
|
|
128
|
+
detect: Effect.fn("detect.cocoapods")(function* (projectDir) {
|
|
129
|
+
const fs = yield* FileSystem.FileSystem;
|
|
130
|
+
const path = yield* Path.Path;
|
|
131
|
+
const results = [];
|
|
132
|
+
// Parse Podfile
|
|
133
|
+
const podfilePath = path.join(projectDir, "Podfile");
|
|
134
|
+
const podfileContent = yield* readFileOptional(podfilePath);
|
|
135
|
+
if (Option.isSome(podfileContent)) {
|
|
136
|
+
const deps = parsePodfile(podfileContent.value, podfilePath);
|
|
137
|
+
results.push(...deps);
|
|
138
|
+
}
|
|
139
|
+
// Parse *.podspec files
|
|
140
|
+
const dirEntries = yield* fs.readDirectory(projectDir).pipe(Effect.option);
|
|
141
|
+
if (Option.isSome(dirEntries)) {
|
|
142
|
+
const podspecFiles = dirEntries.value.filter((f) => f.endsWith(".podspec"));
|
|
143
|
+
for (const podspecFile of podspecFiles) {
|
|
144
|
+
const podspecPath = path.join(projectDir, podspecFile);
|
|
145
|
+
const podspecContent = yield* readFileOptional(podspecPath);
|
|
146
|
+
if (Option.isSome(podspecContent)) {
|
|
147
|
+
const deps = parsePodspec(podspecContent.value, podspecPath);
|
|
148
|
+
results.push(...deps);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return results;
|
|
153
|
+
}, Effect.annotateLogs({ detector: "cocoapods" }), Effect.withSpan("detect.cocoapods")),
|
|
154
|
+
};
|
|
155
|
+
/**
|
|
156
|
+
* CocoaPods package reader.
|
|
157
|
+
*
|
|
158
|
+
* Reads `Pods/<pod-name>/axm.json` for each detected pod
|
|
159
|
+
* and extracts recommendation metadata.
|
|
160
|
+
*
|
|
161
|
+
* @experimental This API is unstable and may change without notice.
|
|
162
|
+
*/
|
|
163
|
+
export const cocoapodsReader = {
|
|
164
|
+
type: cocoapodsType,
|
|
165
|
+
read: Effect.fn("read.cocoapods")(function* (pkg) {
|
|
166
|
+
const path = yield* Path.Path;
|
|
167
|
+
// Derive project directory from the source manifest path
|
|
168
|
+
const projectDir = path.dirname(pkg.source);
|
|
169
|
+
// Pod name from purl
|
|
170
|
+
const podName = pkg.purl.name;
|
|
171
|
+
const axmJsonPath = path.join(projectDir, "Pods", podName, "axm.json");
|
|
172
|
+
const content = yield* readFileOptional(axmJsonPath);
|
|
173
|
+
if (Option.isNone(content))
|
|
174
|
+
return Option.none();
|
|
175
|
+
const parsed = yield* parseJsonOptional(content.value, `${podName}/axm.json`);
|
|
176
|
+
if (Option.isNone(parsed))
|
|
177
|
+
return Option.none();
|
|
178
|
+
// Validate axm metadata structure
|
|
179
|
+
const metaResult = decodeAxmMeta(parsed.value);
|
|
180
|
+
if (Result.isFailure(metaResult)) {
|
|
181
|
+
yield* Effect.logWarning(`Invalid axm metadata in ${podName}: schema validation failed`);
|
|
182
|
+
return Option.none();
|
|
183
|
+
}
|
|
184
|
+
return Option.some(metaResult.success.extensions);
|
|
185
|
+
}, Effect.annotateLogs({ reader: "cocoapods" }), Effect.withSpan("read.cocoapods")),
|
|
186
|
+
};
|
|
187
|
+
//# sourceMappingURL=cocoapods.js.map
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Composer 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
|
+
* Composer package detector.
|
|
10
|
+
*
|
|
11
|
+
* Scans `composer.json` in the project directory and extracts dependencies
|
|
12
|
+
* from `require` and `require-dev`.
|
|
13
|
+
*
|
|
14
|
+
* @experimental This API is unstable and may change without notice.
|
|
15
|
+
*/
|
|
16
|
+
export declare const composerDetector: PackageDetector;
|
|
17
|
+
/**
|
|
18
|
+
* Composer package reader.
|
|
19
|
+
*
|
|
20
|
+
* Reads `vendor/<namespace>/<name>/composer.json` for each detected Composer
|
|
21
|
+
* package and extracts the `"extra"."axm"` field containing recommendation metadata.
|
|
22
|
+
*
|
|
23
|
+
* @experimental This API is unstable and may change without notice.
|
|
24
|
+
*/
|
|
25
|
+
export declare const composerReader: PackageReader;
|
|
26
|
+
//# sourceMappingURL=composer.d.ts.map
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Composer 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 composerType = Schema.decodeUnknownSync(PackageTypeSchema)("composer");
|
|
16
|
+
/** Schema to extract the optional "extra" field from a composer.json object. */
|
|
17
|
+
const ExtraContainerSchema = Schema.Struct({
|
|
18
|
+
extra: Schema.optional(Schema.Struct({
|
|
19
|
+
axm: Schema.optional(Schema.Unknown),
|
|
20
|
+
})),
|
|
21
|
+
});
|
|
22
|
+
const decodeExtraContainer = Schema.decodeUnknownResult(ExtraContainerSchema);
|
|
23
|
+
/**
|
|
24
|
+
* Returns true if the specifier is an exact semver version (no range operators).
|
|
25
|
+
* Exact versions match: digits and dots only, e.g. "7.5.0", "1.0.0-beta.1".
|
|
26
|
+
*/
|
|
27
|
+
const isExactVersion = (specifier) => /^\d+\.\d+\.\d+(?:[-+][a-zA-Z0-9._+-]*)?$/.test(specifier);
|
|
28
|
+
/** Returns true if the dependency key is a platform requirement. */
|
|
29
|
+
const isPlatformRequirement = (name) => name === "php" || name.startsWith("ext-");
|
|
30
|
+
/**
|
|
31
|
+
* Convert a single composer dependency entry to a DetectedPackage, or undefined if skipped.
|
|
32
|
+
*/
|
|
33
|
+
const depToPurl = (depName, specifier, source) => {
|
|
34
|
+
if (isPlatformRequirement(depName))
|
|
35
|
+
return undefined;
|
|
36
|
+
// Composer packages must be vendor/name format
|
|
37
|
+
const slashIdx = depName.indexOf("/");
|
|
38
|
+
if (slashIdx <= 0)
|
|
39
|
+
return undefined;
|
|
40
|
+
const namespace = depName.slice(0, slashIdx).toLowerCase();
|
|
41
|
+
const name = depName.slice(slashIdx + 1).toLowerCase();
|
|
42
|
+
const version = isExactVersion(specifier) ? specifier : undefined;
|
|
43
|
+
return Option.getOrUndefined(makeDetectedPackage({
|
|
44
|
+
type: composerType,
|
|
45
|
+
namespace,
|
|
46
|
+
name,
|
|
47
|
+
...(version === undefined ? {} : { version }),
|
|
48
|
+
source,
|
|
49
|
+
}));
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* Schema to loosely decode the dependency-relevant shape of composer.json.
|
|
53
|
+
*/
|
|
54
|
+
const DependencySectionsSchema = Schema.Struct({
|
|
55
|
+
require: Schema.optional(Schema.Record(Schema.String, Schema.String)),
|
|
56
|
+
"require-dev": Schema.optional(Schema.Record(Schema.String, Schema.String)),
|
|
57
|
+
});
|
|
58
|
+
const decodeDependencySections = Schema.decodeUnknownResult(DependencySectionsSchema);
|
|
59
|
+
/**
|
|
60
|
+
* Extract dependencies from a parsed composer.json value.
|
|
61
|
+
*/
|
|
62
|
+
const extractDeps = (manifest, source) => {
|
|
63
|
+
const decoded = decodeDependencySections(manifest);
|
|
64
|
+
if (Result.isFailure(decoded))
|
|
65
|
+
return [];
|
|
66
|
+
const sections = [decoded.success.require, decoded.success["require-dev"]];
|
|
67
|
+
const results = [];
|
|
68
|
+
for (const deps of sections) {
|
|
69
|
+
if (deps === undefined)
|
|
70
|
+
continue;
|
|
71
|
+
for (const [name, specifier] of Object.entries(deps)) {
|
|
72
|
+
const detected = depToPurl(name, specifier, source);
|
|
73
|
+
if (detected !== undefined)
|
|
74
|
+
results.push(detected);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return results;
|
|
78
|
+
};
|
|
79
|
+
/**
|
|
80
|
+
* Composer package detector.
|
|
81
|
+
*
|
|
82
|
+
* Scans `composer.json` in the project directory and extracts dependencies
|
|
83
|
+
* from `require` and `require-dev`.
|
|
84
|
+
*
|
|
85
|
+
* @experimental This API is unstable and may change without notice.
|
|
86
|
+
*/
|
|
87
|
+
export const composerDetector = {
|
|
88
|
+
type: composerType,
|
|
89
|
+
detect: Effect.fn("detect.composer")(function* (projectDir) {
|
|
90
|
+
const path = yield* Path.Path;
|
|
91
|
+
const manifestPath = path.join(projectDir, "composer.json");
|
|
92
|
+
const content = yield* readFileOptional(manifestPath);
|
|
93
|
+
if (Option.isNone(content))
|
|
94
|
+
return [];
|
|
95
|
+
const parsed = yield* parseJsonOptional(content.value, "composer.json");
|
|
96
|
+
if (Option.isNone(parsed))
|
|
97
|
+
return [];
|
|
98
|
+
return extractDeps(parsed.value, manifestPath);
|
|
99
|
+
}, Effect.annotateLogs({ detector: "composer" }), Effect.withSpan("detect.composer")),
|
|
100
|
+
};
|
|
101
|
+
/**
|
|
102
|
+
* Composer package reader.
|
|
103
|
+
*
|
|
104
|
+
* Reads `vendor/<namespace>/<name>/composer.json` for each detected Composer
|
|
105
|
+
* package and extracts the `"extra"."axm"` field containing recommendation metadata.
|
|
106
|
+
*
|
|
107
|
+
* @experimental This API is unstable and may change without notice.
|
|
108
|
+
*/
|
|
109
|
+
export const composerReader = {
|
|
110
|
+
type: composerType,
|
|
111
|
+
read: Effect.fn("read.composer")(function* (pkg) {
|
|
112
|
+
const path = yield* Path.Path;
|
|
113
|
+
// Derive project directory from the source manifest path
|
|
114
|
+
const projectDir = path.dirname(pkg.source);
|
|
115
|
+
// Reconstruct the package path from purl parts
|
|
116
|
+
const pkgPath = pkg.purl.namespace ? `${pkg.purl.namespace}/${pkg.purl.name}` : pkg.purl.name;
|
|
117
|
+
const composerJsonPath = path.join(projectDir, "vendor", pkgPath, "composer.json");
|
|
118
|
+
const content = yield* readFileOptional(composerJsonPath);
|
|
119
|
+
if (Option.isNone(content))
|
|
120
|
+
return Option.none();
|
|
121
|
+
const parsed = yield* parseJsonOptional(content.value, pkgPath);
|
|
122
|
+
if (Option.isNone(parsed))
|
|
123
|
+
return Option.none();
|
|
124
|
+
// Extract and validate the "extra"."axm" field using schema
|
|
125
|
+
const extraContainerResult = decodeExtraContainer(parsed.value);
|
|
126
|
+
if (Result.isFailure(extraContainerResult)) {
|
|
127
|
+
return Option.none();
|
|
128
|
+
}
|
|
129
|
+
const extra = extraContainerResult.success.extra;
|
|
130
|
+
if (extra === undefined)
|
|
131
|
+
return Option.none();
|
|
132
|
+
const axmRaw = extra.axm;
|
|
133
|
+
if (axmRaw === undefined)
|
|
134
|
+
return Option.none();
|
|
135
|
+
// Validate axm metadata structure
|
|
136
|
+
const metaResult = decodeAxmMeta(axmRaw);
|
|
137
|
+
if (Result.isFailure(metaResult)) {
|
|
138
|
+
yield* Effect.logWarning(`Invalid axm metadata in ${pkgPath}: schema validation failed`);
|
|
139
|
+
return Option.none();
|
|
140
|
+
}
|
|
141
|
+
return Option.some(metaResult.success.extensions);
|
|
142
|
+
}, Effect.annotateLogs({ reader: "composer" }), Effect.withSpan("read.composer")),
|
|
143
|
+
};
|
|
144
|
+
//# sourceMappingURL=composer.js.map
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Conan (C++) package detector and reader for package-compatibility discovery.
|
|
3
|
+
*
|
|
4
|
+
* Parses `conanfile.txt` `[requires]` sections and `conanfile.py` `requires`
|
|
5
|
+
* attributes. Reads axm metadata from Conan cache `conandata.yml` or
|
|
6
|
+
* `extension_properties`.
|
|
7
|
+
*
|
|
8
|
+
* @experimental This API is unstable and may change without notice.
|
|
9
|
+
* @packageDocumentation
|
|
10
|
+
*/
|
|
11
|
+
import type { PackageDetector, PackageReader } from "./types.js";
|
|
12
|
+
/**
|
|
13
|
+
* Conan package detector.
|
|
14
|
+
*
|
|
15
|
+
* Scans `conanfile.txt` `[requires]` section and `conanfile.py` `requires`
|
|
16
|
+
* attribute in the project directory.
|
|
17
|
+
*
|
|
18
|
+
* @experimental This API is unstable and may change without notice.
|
|
19
|
+
*/
|
|
20
|
+
export declare const conanDetector: PackageDetector;
|
|
21
|
+
/**
|
|
22
|
+
* Conan package reader.
|
|
23
|
+
*
|
|
24
|
+
* Reads axm metadata from `conandata.yml` in the Conan cache or
|
|
25
|
+
* `extension_properties` for each detected Conan package.
|
|
26
|
+
*
|
|
27
|
+
* @experimental This API is unstable and may change without notice.
|
|
28
|
+
*/
|
|
29
|
+
export declare const conanReader: PackageReader;
|
|
30
|
+
//# sourceMappingURL=conan.d.ts.map
|