@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.
Files changed (72) hide show
  1. package/LICENSE +110 -0
  2. package/dist/src/discover.d.ts +34 -0
  3. package/dist/src/discover.js +133 -0
  4. package/dist/src/index.d.ts +13 -0
  5. package/dist/src/index.js +12 -0
  6. package/dist/src/internal/environment.d.ts +14 -0
  7. package/dist/src/internal/environment.js +15 -0
  8. package/dist/src/packaging/bazel.d.ts +27 -0
  9. package/dist/src/packaging/bazel.js +125 -0
  10. package/dist/src/packaging/cargo.d.ts +32 -0
  11. package/dist/src/packaging/cargo.js +270 -0
  12. package/dist/src/packaging/cocoapods.d.ts +26 -0
  13. package/dist/src/packaging/cocoapods.js +187 -0
  14. package/dist/src/packaging/composer.d.ts +26 -0
  15. package/dist/src/packaging/composer.js +144 -0
  16. package/dist/src/packaging/conan.d.ts +30 -0
  17. package/dist/src/packaging/conan.js +238 -0
  18. package/dist/src/packaging/conda.d.ts +26 -0
  19. package/dist/src/packaging/conda.js +320 -0
  20. package/dist/src/packaging/cpan.d.ts +29 -0
  21. package/dist/src/packaging/cpan.js +189 -0
  22. package/dist/src/packaging/cran.d.ts +29 -0
  23. package/dist/src/packaging/cran.js +176 -0
  24. package/dist/src/packaging/detect.d.ts +16 -0
  25. package/dist/src/packaging/detect.js +25 -0
  26. package/dist/src/packaging/detected-package.d.ts +14 -0
  27. package/dist/src/packaging/detected-package.js +20 -0
  28. package/dist/src/packaging/docker.d.ts +27 -0
  29. package/dist/src/packaging/docker.js +256 -0
  30. package/dist/src/packaging/gem.d.ts +26 -0
  31. package/dist/src/packaging/gem.js +234 -0
  32. package/dist/src/packaging/golang.d.ts +26 -0
  33. package/dist/src/packaging/golang.js +169 -0
  34. package/dist/src/packaging/hackage.d.ts +26 -0
  35. package/dist/src/packaging/hackage.js +305 -0
  36. package/dist/src/packaging/hex.d.ts +28 -0
  37. package/dist/src/packaging/hex.js +186 -0
  38. package/dist/src/packaging/huggingface.d.ts +20 -0
  39. package/dist/src/packaging/huggingface.js +112 -0
  40. package/dist/src/packaging/index.d.ts +44 -0
  41. package/dist/src/packaging/index.js +120 -0
  42. package/dist/src/packaging/jsr.d.ts +30 -0
  43. package/dist/src/packaging/jsr.js +227 -0
  44. package/dist/src/packaging/julia.d.ts +27 -0
  45. package/dist/src/packaging/julia.js +165 -0
  46. package/dist/src/packaging/luarocks.d.ts +28 -0
  47. package/dist/src/packaging/luarocks.js +159 -0
  48. package/dist/src/packaging/maven.d.ts +27 -0
  49. package/dist/src/packaging/maven.js +471 -0
  50. package/dist/src/packaging/mojo.d.ts +29 -0
  51. package/dist/src/packaging/mojo.js +148 -0
  52. package/dist/src/packaging/npm.d.ts +26 -0
  53. package/dist/src/packaging/npm.js +190 -0
  54. package/dist/src/packaging/nuget.d.ts +26 -0
  55. package/dist/src/packaging/nuget.js +240 -0
  56. package/dist/src/packaging/opam.d.ts +27 -0
  57. package/dist/src/packaging/opam.js +287 -0
  58. package/dist/src/packaging/pub.d.ts +26 -0
  59. package/dist/src/packaging/pub.js +357 -0
  60. package/dist/src/packaging/pypi.d.ts +23 -0
  61. package/dist/src/packaging/pypi.js +448 -0
  62. package/dist/src/packaging/read.d.ts +20 -0
  63. package/dist/src/packaging/read.js +31 -0
  64. package/dist/src/packaging/reader-io.d.ts +29 -0
  65. package/dist/src/packaging/reader-io.js +29 -0
  66. package/dist/src/packaging/swift.d.ts +26 -0
  67. package/dist/src/packaging/swift.js +141 -0
  68. package/dist/src/packaging/types.d.ts +43 -0
  69. package/dist/src/packaging/types.js +8 -0
  70. package/dist/src/packaging/zig.d.ts +32 -0
  71. package/dist/src/packaging/zig.js +151 -0
  72. package/package.json +54 -0
@@ -0,0 +1,169 @@
1
+ /**
2
+ * Go module 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 Option from "effect/Option";
11
+ import * as Path from "effect/Path";
12
+ import * as Result from "effect/Result";
13
+ import * as Schema from "effect/Schema";
14
+ import { readEnv } from "../internal/environment.js";
15
+ import { makeDetectedPackage } from "./detected-package.js";
16
+ import { PackageTypeSchema } from "@agentxm/extension-model/unstable/packaging/package-type";
17
+ import { decodeAxmMeta, parseJsonOptional, readFileOptional } from "./reader-io.js";
18
+ const golangType = Schema.decodeUnknownSync(PackageTypeSchema)("golang");
19
+ /** Regex matching a major-version path suffix like /v2, /v3, etc. */
20
+ const MAJOR_VERSION_SUFFIX = /\/v\d+$/;
21
+ /**
22
+ * Strip major version path suffix (/v2, /v3, etc.) from a Go module path.
23
+ */
24
+ const stripMajorVersionSuffix = (modulePath) => modulePath.replace(MAJOR_VERSION_SUFFIX, "");
25
+ /**
26
+ * Split a Go module path into purl namespace and name.
27
+ * The namespace is the path prefix (lowercased), and the name is the last segment.
28
+ */
29
+ const parseModulePath = (rawModulePath) => {
30
+ const modulePath = stripMajorVersionSuffix(rawModulePath);
31
+ const lastSlash = modulePath.lastIndexOf("/");
32
+ if (lastSlash === -1) {
33
+ return { namespace: "", name: modulePath.toLowerCase() };
34
+ }
35
+ return {
36
+ namespace: modulePath.slice(0, lastSlash).toLowerCase(),
37
+ name: modulePath.slice(lastSlash + 1).toLowerCase(),
38
+ };
39
+ };
40
+ /**
41
+ * Parse a single require line into a DetectedPackage, or undefined if invalid/indirect.
42
+ * A require line has the form: module_path version [// indirect]
43
+ */
44
+ const parseRequireLine = (line, source) => {
45
+ const trimmed = line.trim();
46
+ if (trimmed === "" || trimmed.startsWith("//"))
47
+ return undefined;
48
+ // Filter out indirect dependencies
49
+ if (trimmed.includes("// indirect"))
50
+ return undefined;
51
+ // Split into module path and version
52
+ const parts = trimmed.split(/\s+/);
53
+ if (parts.length < 2)
54
+ return undefined;
55
+ const modulePath = parts[0];
56
+ const version = parts[1];
57
+ if (modulePath === undefined || version === undefined)
58
+ return undefined;
59
+ const { namespace, name } = parseModulePath(modulePath);
60
+ return Option.getOrUndefined(makeDetectedPackage({
61
+ type: golangType,
62
+ ...(namespace === "" ? {} : { namespace }),
63
+ name,
64
+ version,
65
+ source,
66
+ }));
67
+ };
68
+ /**
69
+ * Parse go.mod content and extract direct dependencies.
70
+ */
71
+ const parseGoMod = (content, source) => {
72
+ const lines = content.split("\n");
73
+ const results = [];
74
+ let inRequireBlock = false;
75
+ for (const line of lines) {
76
+ const trimmed = line.trim();
77
+ // Single-line require: require module_path version
78
+ if (trimmed.startsWith("require ") && !trimmed.includes("(")) {
79
+ const rest = trimmed.slice("require ".length);
80
+ const detected = parseRequireLine(rest, source);
81
+ if (detected !== undefined)
82
+ results.push(detected);
83
+ continue;
84
+ }
85
+ // Start of require block
86
+ if (trimmed.startsWith("require (") || trimmed === "require (") {
87
+ inRequireBlock = true;
88
+ continue;
89
+ }
90
+ // End of require block
91
+ if (inRequireBlock && trimmed === ")") {
92
+ inRequireBlock = false;
93
+ continue;
94
+ }
95
+ // Line inside require block
96
+ if (inRequireBlock) {
97
+ const detected = parseRequireLine(trimmed, source);
98
+ if (detected !== undefined)
99
+ results.push(detected);
100
+ }
101
+ }
102
+ return results;
103
+ };
104
+ /**
105
+ * Resolve the GOPATH, defaulting to ~/go when not set.
106
+ */
107
+ const resolveGopath = () => Effect.sync(() => readEnv("GOPATH") ?? `${os.homedir()}/go`);
108
+ /**
109
+ * Go module package detector.
110
+ *
111
+ * Scans `go.mod` in the project directory and extracts direct dependencies
112
+ * from `require` directives, filtering out `// indirect` entries.
113
+ *
114
+ * @experimental This API is unstable and may change without notice.
115
+ */
116
+ export const golangDetector = {
117
+ type: golangType,
118
+ detect: Effect.fn("detect.golang")(function* (projectDir) {
119
+ const path = yield* Path.Path;
120
+ const goModPath = path.join(projectDir, "go.mod");
121
+ const content = yield* readFileOptional(goModPath);
122
+ if (Option.isNone(content))
123
+ return [];
124
+ const deps = parseGoMod(content.value, goModPath);
125
+ if (deps.length === 0 && content.value.trim().length > 0) {
126
+ // Content exists but no deps found - could be valid go.mod with no deps,
127
+ // or malformed. Only warn if it looks malformed (no module directive).
128
+ if (!content.value.includes("module ")) {
129
+ yield* Effect.logWarning("Malformed go.mod: missing module directive, skipping");
130
+ }
131
+ }
132
+ return deps;
133
+ }, Effect.annotateLogs({ detector: "golang" }), Effect.withSpan("detect.golang")),
134
+ };
135
+ /**
136
+ * Go module package reader.
137
+ *
138
+ * Reads `axm.json` from `$GOPATH/pkg/mod/<module>@<version>/` for each
139
+ * detected Go module and extracts recommendation metadata.
140
+ *
141
+ * @experimental This API is unstable and may change without notice.
142
+ */
143
+ export const golangReader = {
144
+ type: golangType,
145
+ read: Effect.fn("read.golang")(function* (pkg) {
146
+ const path = yield* Path.Path;
147
+ const gopath = yield* resolveGopath();
148
+ // Reconstruct the module path from purl parts
149
+ const modulePath = pkg.purl.namespace
150
+ ? `${pkg.purl.namespace}/${pkg.purl.name}`
151
+ : pkg.purl.name;
152
+ const version = pkg.purl.version ?? "v0.0.0";
153
+ const axmJsonPath = path.join(gopath, "pkg", "mod", `${modulePath}@${version}`, "axm.json");
154
+ const content = yield* readFileOptional(axmJsonPath);
155
+ if (Option.isNone(content))
156
+ return Option.none();
157
+ const parsed = yield* parseJsonOptional(content.value, `${modulePath}@${version}/axm.json`);
158
+ if (Option.isNone(parsed))
159
+ return Option.none();
160
+ // Validate axm metadata structure
161
+ const metaResult = decodeAxmMeta(parsed.value);
162
+ if (Result.isFailure(metaResult)) {
163
+ yield* Effect.logWarning(`Invalid axm metadata in ${modulePath}@${version}: schema validation failed`);
164
+ return Option.none();
165
+ }
166
+ return Option.some(metaResult.success.extensions);
167
+ }, Effect.annotateLogs({ reader: "golang" }), Effect.withSpan("read.golang")),
168
+ };
169
+ //# sourceMappingURL=golang.js.map
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Hackage (Haskell) 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
+ * Hackage package detector.
10
+ *
11
+ * Scans `*.cabal` files for `build-depends` fields and `stack.yaml` for
12
+ * `extra-deps` entries. Dependencies are deduplicated by name.
13
+ *
14
+ * @experimental This API is unstable and may change without notice.
15
+ */
16
+ export declare const hackageDetector: PackageDetector;
17
+ /**
18
+ * Hackage package reader.
19
+ *
20
+ * Reads `x-axm` prefixed custom fields from `.cabal` files in
21
+ * `~/.cabal/store/ghc-<version>/<pkg>-<version>/` or `dist-newstyle/`.
22
+ *
23
+ * @experimental This API is unstable and may change without notice.
24
+ */
25
+ export declare const hackageReader: PackageReader;
26
+ //# sourceMappingURL=hackage.d.ts.map
@@ -0,0 +1,305 @@
1
+ /**
2
+ * Hackage (Haskell) 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 { PackageTypeSchema } from "@agentxm/extension-model/unstable/packaging/package-type";
17
+ import { decodeAxmMeta, decodePurl, readFileOptional } from "./reader-io.js";
18
+ const hackageType = Schema.decodeUnknownSync(PackageTypeSchema)("hackage");
19
+ /**
20
+ * Parse a cabal build-depends line into individual dependency entries.
21
+ * build-depends can span multiple lines with comma separation.
22
+ * Each entry is: package-name [version-constraint]
23
+ */
24
+ const parseBuildDependsEntry = (entry) => {
25
+ const trimmed = entry.trim();
26
+ if (trimmed === "")
27
+ return undefined;
28
+ // Split on whitespace to get name and optional version constraint
29
+ const parts = trimmed.split(/\s+/);
30
+ const name = parts[0];
31
+ if (name === undefined || name === "")
32
+ return undefined;
33
+ // Check for exact version pin: ==version
34
+ const rest = parts.slice(1).join(" ").trim();
35
+ const exactMatch = /^==\s*(\S+)$/.exec(rest);
36
+ if (exactMatch?.[1]) {
37
+ return { name, version: exactMatch[1] };
38
+ }
39
+ // Any other constraint or no constraint => versionless
40
+ return { name };
41
+ };
42
+ /**
43
+ * Parse all build-depends fields from a cabal file.
44
+ * Extracts dependencies from all sections (library, executable, etc.)
45
+ * and all conditional branches.
46
+ */
47
+ const parseCabalBuildDepends = (content, source) => {
48
+ const results = [];
49
+ const seen = new Set();
50
+ // Match all build-depends fields, handling multi-line values.
51
+ // build-depends: entries are comma-separated and may span multiple lines
52
+ // (continuation lines are indented further than the field name).
53
+ const buildDependsRegex = /build-depends\s*:\s*([^\n]*(?:\n[ \t]+[^\n]*)*)/gi;
54
+ let match;
55
+ while ((match = buildDependsRegex.exec(content)) !== null) {
56
+ const depsBlock = match[1];
57
+ if (depsBlock === undefined)
58
+ continue;
59
+ // Split on commas, handling multi-line
60
+ const entries = depsBlock.split(",");
61
+ for (const entry of entries) {
62
+ const parsed = parseBuildDependsEntry(entry);
63
+ if (parsed === undefined)
64
+ continue;
65
+ if (seen.has(parsed.name))
66
+ continue;
67
+ seen.add(parsed.name);
68
+ const purl = new PackageURL("hackage", null, parsed.name, parsed.version ?? null, null, null);
69
+ const purlParts = decodePurl(purl.toString());
70
+ results.push({ purl: purlParts, type: hackageType, source });
71
+ }
72
+ }
73
+ return results;
74
+ };
75
+ /**
76
+ * Parse stack.yaml extra-deps entries.
77
+ * extra-deps entries have the format: package-version (e.g. aeson-2.1.0.0)
78
+ * The last hyphen-separated segment that starts with a digit is the version.
79
+ */
80
+ const parseExtraDep = (entry) => {
81
+ const trimmed = entry.trim().replace(/^-\s*/, "");
82
+ if (trimmed === "" || trimmed.startsWith("#"))
83
+ return undefined;
84
+ // Skip entries that look like git references or URLs
85
+ if (trimmed.includes("/") || trimmed.includes(":"))
86
+ return undefined;
87
+ // Find the last hyphen that separates name from version
88
+ // Version starts with a digit
89
+ const lastHyphen = trimmed.lastIndexOf("-");
90
+ if (lastHyphen === -1)
91
+ return undefined;
92
+ const possibleVersion = trimmed.slice(lastHyphen + 1);
93
+ if (!/^\d/.test(possibleVersion))
94
+ return undefined;
95
+ const name = trimmed.slice(0, lastHyphen);
96
+ if (name === "")
97
+ return undefined;
98
+ return { name, version: possibleVersion };
99
+ };
100
+ /**
101
+ * Parse stack.yaml content for extra-deps.
102
+ */
103
+ const parseStackYaml = (content, source) => {
104
+ const results = [];
105
+ const seen = new Set();
106
+ const lines = content.split("\n");
107
+ let inExtraDeps = false;
108
+ for (const line of lines) {
109
+ const trimmed = line.trim();
110
+ // Start of extra-deps block (YAML list)
111
+ if (trimmed.startsWith("extra-deps:")) {
112
+ inExtraDeps = true;
113
+ // Check inline list format: extra-deps: [item1, item2]
114
+ const inlineMatch = /extra-deps:\s*\[([^\]]*)\]/.exec(trimmed);
115
+ if (inlineMatch?.[1]) {
116
+ const entries = inlineMatch[1].split(",");
117
+ for (const entry of entries) {
118
+ const parsed = parseExtraDep(entry);
119
+ if (parsed === undefined)
120
+ continue;
121
+ if (seen.has(parsed.name))
122
+ continue;
123
+ seen.add(parsed.name);
124
+ const purl = new PackageURL("hackage", null, parsed.name, parsed.version, null, null);
125
+ const purlParts = decodePurl(purl.toString());
126
+ results.push({ purl: purlParts, type: hackageType, source });
127
+ }
128
+ inExtraDeps = false;
129
+ }
130
+ continue;
131
+ }
132
+ // End of extra-deps block (next top-level key)
133
+ if (inExtraDeps &&
134
+ !line.startsWith(" ") &&
135
+ !line.startsWith("\t") &&
136
+ trimmed !== "" &&
137
+ !trimmed.startsWith("-") &&
138
+ !trimmed.startsWith("#")) {
139
+ inExtraDeps = false;
140
+ continue;
141
+ }
142
+ // List item in extra-deps
143
+ if (inExtraDeps && trimmed.startsWith("-")) {
144
+ const parsed = parseExtraDep(trimmed);
145
+ if (parsed === undefined)
146
+ continue;
147
+ if (seen.has(parsed.name))
148
+ continue;
149
+ seen.add(parsed.name);
150
+ const purl = new PackageURL("hackage", null, parsed.name, parsed.version, null, null);
151
+ const purlParts = decodePurl(purl.toString());
152
+ results.push({ purl: purlParts, type: hackageType, source });
153
+ }
154
+ }
155
+ return results;
156
+ };
157
+ /**
158
+ * Hackage package detector.
159
+ *
160
+ * Scans `*.cabal` files for `build-depends` fields and `stack.yaml` for
161
+ * `extra-deps` entries. Dependencies are deduplicated by name.
162
+ *
163
+ * @experimental This API is unstable and may change without notice.
164
+ */
165
+ export const hackageDetector = {
166
+ type: hackageType,
167
+ detect: Effect.fn("detect.hackage")(function* (projectDir) {
168
+ const path = yield* Path.Path;
169
+ const fs = yield* FileSystem.FileSystem;
170
+ const allDeps = [];
171
+ const seenNames = new Set();
172
+ const addDeps = (deps) => {
173
+ for (const dep of deps) {
174
+ if (!seenNames.has(dep.purl.name)) {
175
+ seenNames.add(dep.purl.name);
176
+ allDeps.push(dep);
177
+ }
178
+ }
179
+ };
180
+ // Scan for *.cabal files
181
+ const entries = yield* fs.readDirectory(projectDir).pipe(Effect.option);
182
+ if (Option.isSome(entries)) {
183
+ const cabalFiles = entries.value.filter((e) => e.endsWith(".cabal"));
184
+ for (const cabalFile of cabalFiles) {
185
+ const cabalPath = path.join(projectDir, cabalFile);
186
+ const content = yield* readFileOptional(cabalPath);
187
+ if (Option.isSome(content)) {
188
+ const deps = parseCabalBuildDepends(content.value, cabalPath);
189
+ if (deps.length === 0 && content.value.trim().length > 0) {
190
+ // Check if it looks like a valid cabal file
191
+ if (!content.value.includes("name:") && !content.value.includes("cabal-version:")) {
192
+ yield* Effect.logWarning(`Malformed cabal file: ${cabalFile}, skipping`);
193
+ }
194
+ }
195
+ addDeps(deps);
196
+ }
197
+ }
198
+ }
199
+ // Parse stack.yaml
200
+ const stackPath = path.join(projectDir, "stack.yaml");
201
+ const stackContent = yield* readFileOptional(stackPath);
202
+ if (Option.isSome(stackContent)) {
203
+ const stackDeps = parseStackYaml(stackContent.value, stackPath);
204
+ addDeps(stackDeps);
205
+ }
206
+ return allDeps;
207
+ }, Effect.annotateLogs({ detector: "hackage" }), Effect.withSpan("detect.hackage")),
208
+ };
209
+ /**
210
+ * Parse x-axm custom fields from a cabal file content.
211
+ * Returns a record mapping field names (without x-axm- prefix) to values.
212
+ */
213
+ const parseXAxmFields = (content) => {
214
+ const fields = {};
215
+ const regex = /^x-axm-(\S+)\s*:\s*(.+)$/gim;
216
+ let match;
217
+ while ((match = regex.exec(content)) !== null) {
218
+ const fieldName = match[1];
219
+ const rawValue = match[2]?.trim();
220
+ if (fieldName === undefined || rawValue === undefined)
221
+ continue;
222
+ // Try to parse JSON value, otherwise use as string
223
+ try {
224
+ fields[fieldName] = JSON.parse(rawValue);
225
+ }
226
+ catch {
227
+ fields[fieldName] = rawValue;
228
+ }
229
+ }
230
+ return fields;
231
+ };
232
+ /**
233
+ * Hackage package reader.
234
+ *
235
+ * Reads `x-axm` prefixed custom fields from `.cabal` files in
236
+ * `~/.cabal/store/ghc-<version>/<pkg>-<version>/` or `dist-newstyle/`.
237
+ *
238
+ * @experimental This API is unstable and may change without notice.
239
+ */
240
+ export const hackageReader = {
241
+ type: hackageType,
242
+ read: Effect.fn("read.hackage")(function* (pkg) {
243
+ const path = yield* Path.Path;
244
+ const fs = yield* FileSystem.FileSystem;
245
+ const pkgName = pkg.purl.name;
246
+ const version = pkg.purl.version;
247
+ const home = os.homedir();
248
+ // Try to find the .cabal file in known locations
249
+ // 1. Try ~/.cabal/store/ghc-*/pkg-version/
250
+ const cabalStoreDir = path.join(home, ".cabal", "store");
251
+ const storeExists = yield* fs.readDirectory(cabalStoreDir).pipe(Effect.option);
252
+ if (Option.isSome(storeExists)) {
253
+ // Look for ghc-* directories
254
+ for (const ghcDir of storeExists.value) {
255
+ if (!ghcDir.startsWith("ghc-"))
256
+ continue;
257
+ const pkgDirName = version ? `${pkgName}-${version}` : pkgName;
258
+ const pkgDir = path.join(cabalStoreDir, ghcDir, pkgDirName);
259
+ const pkgEntries = yield* fs.readDirectory(pkgDir).pipe(Effect.option);
260
+ if (Option.isSome(pkgEntries)) {
261
+ const cabalFile = pkgEntries.value.find((e) => e.endsWith(".cabal"));
262
+ if (cabalFile !== undefined) {
263
+ const cabalPath = path.join(pkgDir, cabalFile);
264
+ const content = yield* readFileOptional(cabalPath);
265
+ if (Option.isSome(content)) {
266
+ const xAxmFields = parseXAxmFields(content.value);
267
+ if (Object.keys(xAxmFields).length === 0)
268
+ return Option.none();
269
+ const metaResult = decodeAxmMeta(xAxmFields);
270
+ if (Result.isFailure(metaResult)) {
271
+ yield* Effect.logWarning(`Invalid axm metadata in ${pkgName}: schema validation failed`);
272
+ return Option.none();
273
+ }
274
+ return Option.some(metaResult.success.extensions);
275
+ }
276
+ }
277
+ }
278
+ }
279
+ }
280
+ // 2. Try dist-newstyle/ relative to project dir
281
+ const projectDir = path.dirname(pkg.source);
282
+ const distDir = path.join(projectDir, "dist-newstyle");
283
+ const distExists = yield* fs.access(distDir).pipe(Effect.option);
284
+ if (Option.isSome(distExists)) {
285
+ // Search recursively for the package's .cabal file
286
+ // dist-newstyle has a deep structure, so look for any .cabal file matching the package name
287
+ const pkgDirName = version ? `${pkgName}-${version}` : pkgName;
288
+ const candidatePath = path.join(distDir, "build", pkgDirName, `${pkgName}.cabal`);
289
+ const content = yield* readFileOptional(candidatePath);
290
+ if (Option.isSome(content)) {
291
+ const xAxmFields = parseXAxmFields(content.value);
292
+ if (Object.keys(xAxmFields).length === 0)
293
+ return Option.none();
294
+ const metaResult = decodeAxmMeta(xAxmFields);
295
+ if (Result.isFailure(metaResult)) {
296
+ yield* Effect.logWarning(`Invalid axm metadata in ${pkgName}: schema validation failed`);
297
+ return Option.none();
298
+ }
299
+ return Option.some(metaResult.success.extensions);
300
+ }
301
+ }
302
+ return Option.none();
303
+ }, Effect.annotateLogs({ reader: "hackage" }), Effect.withSpan("read.hackage")),
304
+ };
305
+ //# sourceMappingURL=hackage.js.map
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Hex package detector and reader for package-compatibility discovery.
3
+ *
4
+ * Supports both Elixir (mix.exs) and Gleam (gleam.toml) projects.
5
+ *
6
+ * @experimental This API is unstable and may change without notice.
7
+ * @packageDocumentation
8
+ */
9
+ import type { PackageDetector, PackageReader } from "./types.js";
10
+ /**
11
+ * Hex package detector.
12
+ *
13
+ * Scans `mix.exs` and `gleam.toml` in the project directory and extracts
14
+ * dependencies from Elixir dependency tuples and Gleam TOML sections.
15
+ *
16
+ * @experimental This API is unstable and may change without notice.
17
+ */
18
+ export declare const hexDetector: PackageDetector;
19
+ /**
20
+ * Hex package reader.
21
+ *
22
+ * Reads `deps/<package-name>/axm.json` as the primary source, falling back to
23
+ * parsing `hex_metadata.config` for each detected Hex package.
24
+ *
25
+ * @experimental This API is unstable and may change without notice.
26
+ */
27
+ export declare const hexReader: PackageReader;
28
+ //# sourceMappingURL=hex.d.ts.map