@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,287 @@
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
+ // 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 opamType = Schema.decodeUnknownSync(PackageTypeSchema)("opam");
19
+ /** Build-tooling dependencies to skip. */
20
+ const SKIP_DEPS = new Set(["ocaml", "dune"]);
21
+ /**
22
+ * Parse opam depends field content.
23
+ * Format: depends: [ "pkg1" {>= "ver"} "pkg2" ... ]
24
+ * The constraint syntax uses { } for version constraints.
25
+ */
26
+ const parseOpamDepends = (content, source) => {
27
+ const results = [];
28
+ // Find the depends field - it starts with "depends:" and contains a [ ... ] block
29
+ const dependsRegex = /depends:\s*\[([^\]]*)\]/s;
30
+ const match = dependsRegex.exec(content);
31
+ if (match === null || match[1] === undefined)
32
+ return [];
33
+ // Extract quoted package names and their optional version constraints
34
+ // Pattern: "name" {constraint} or just "name"
35
+ const entryRegex = /"([^"]+)"(?:\s*\{([^}]*)\})?/g;
36
+ let entryMatch;
37
+ while ((entryMatch = entryRegex.exec(match[1])) !== null) {
38
+ const name = entryMatch[1];
39
+ const constraint = entryMatch[2]?.trim();
40
+ if (name === undefined)
41
+ continue;
42
+ if (SKIP_DEPS.has(name))
43
+ continue;
44
+ // Check for exact version: = "version"
45
+ let version;
46
+ if (constraint !== undefined) {
47
+ const exactMatch = /^=\s*"([^"]+)"$/.exec(constraint);
48
+ if (exactMatch?.[1]) {
49
+ version = exactMatch[1];
50
+ }
51
+ }
52
+ const purl = new PackageURL("opam", null, name, version ?? null, null, null);
53
+ const purlParts = decodePurl(purl.toString());
54
+ results.push({ purl: purlParts, type: opamType, source });
55
+ }
56
+ return results;
57
+ };
58
+ /**
59
+ * Extract the contents of a top-level `(depends ...)` s-expression
60
+ * by finding the opening `(depends` and then walking to the matching
61
+ * closing `)` while counting nested parentheses.
62
+ */
63
+ const extractDependsBlock = (content) => {
64
+ const idx = content.indexOf("(depends");
65
+ if (idx === -1)
66
+ return undefined;
67
+ // Move past "(depends"
68
+ let i = idx + "(depends".length;
69
+ let depth = 1;
70
+ const start = i;
71
+ while (i < content.length && depth > 0) {
72
+ if (content[i] === "(")
73
+ depth++;
74
+ else if (content[i] === ")")
75
+ depth--;
76
+ i++;
77
+ }
78
+ // depth === 0 means we found the matching close paren
79
+ if (depth !== 0)
80
+ return undefined;
81
+ // Return everything between "(depends" and the matching ")"
82
+ return content.slice(start, i - 1);
83
+ };
84
+ /**
85
+ * Parse dune-project (depends ...) s-expression.
86
+ * Format: (depends (ocaml (>= 5.0)) (lwt (>= 5.0)) yojson)
87
+ */
88
+ const parseDuneProjectDepends = (content, source) => {
89
+ const results = [];
90
+ const depsBlock = extractDependsBlock(content);
91
+ if (depsBlock === undefined)
92
+ return [];
93
+ const depsContent = depsBlock;
94
+ // Extract dependency entries: (name constraint) or bare name
95
+ // A dependency can be: bare_name, (name constraint_expr)
96
+ const entries = [];
97
+ // Parse the s-expression content to extract dependency names
98
+ let i = 0;
99
+ while (i < depsContent.length) {
100
+ // Skip whitespace
101
+ while (i < depsContent.length && /\s/.test(depsContent[i] ?? ""))
102
+ i++;
103
+ if (i >= depsContent.length)
104
+ break;
105
+ if (depsContent[i] === "(") {
106
+ // Parenthesized entry - extract the name (first atom)
107
+ i++; // skip (
108
+ // Skip whitespace
109
+ while (i < depsContent.length && /\s/.test(depsContent[i] ?? ""))
110
+ i++;
111
+ // Read the name
112
+ let name = "";
113
+ while (i < depsContent.length && !/[\s)]/.test(depsContent[i] ?? "")) {
114
+ name += depsContent[i];
115
+ i++;
116
+ }
117
+ // Check for exact version constraint: (= version)
118
+ let version;
119
+ const remaining = depsContent.slice(i);
120
+ const exactMatch = /^\s*\(\s*=\s*(\S+)\s*\)/.exec(remaining);
121
+ if (exactMatch?.[1]) {
122
+ version = exactMatch[1];
123
+ }
124
+ if (name !== "") {
125
+ entries.push(name);
126
+ if (version !== undefined && !SKIP_DEPS.has(name)) {
127
+ const purl = new PackageURL("opam", null, name, version, null, null);
128
+ const purlParts = decodePurl(purl.toString());
129
+ results.push({ purl: purlParts, type: opamType, source });
130
+ // Skip to closing paren
131
+ let depth = 1;
132
+ while (i < depsContent.length && depth > 0) {
133
+ if (depsContent[i] === "(")
134
+ depth++;
135
+ else if (depsContent[i] === ")")
136
+ depth--;
137
+ i++;
138
+ }
139
+ continue;
140
+ }
141
+ }
142
+ // Skip to closing paren
143
+ let depth = 1;
144
+ while (i < depsContent.length && depth > 0) {
145
+ if (depsContent[i] === "(")
146
+ depth++;
147
+ else if (depsContent[i] === ")")
148
+ depth--;
149
+ i++;
150
+ }
151
+ if (name !== "" && !SKIP_DEPS.has(name) && version === undefined) {
152
+ const purl = new PackageURL("opam", null, name, null, null, null);
153
+ const purlParts = decodePurl(purl.toString());
154
+ results.push({ purl: purlParts, type: opamType, source });
155
+ }
156
+ }
157
+ else {
158
+ // Bare name
159
+ let name = "";
160
+ while (i < depsContent.length && !/[\s)]/.test(depsContent[i] ?? "")) {
161
+ name += depsContent[i];
162
+ i++;
163
+ }
164
+ if (name !== "" && !SKIP_DEPS.has(name)) {
165
+ const purl = new PackageURL("opam", null, name, null, null, null);
166
+ const purlParts = decodePurl(purl.toString());
167
+ results.push({ purl: purlParts, type: opamType, source });
168
+ }
169
+ }
170
+ }
171
+ return results;
172
+ };
173
+ /**
174
+ * opam package detector.
175
+ *
176
+ * Scans `*.opam` files for `depends` fields and `dune-project` for
177
+ * `(depends ...)` s-expressions. Skips `ocaml` and `dune` build tooling.
178
+ * Dependencies are deduplicated by name.
179
+ *
180
+ * @experimental This API is unstable and may change without notice.
181
+ */
182
+ export const opamDetector = {
183
+ type: opamType,
184
+ detect: Effect.fn("detect.opam")(function* (projectDir) {
185
+ const path = yield* Path.Path;
186
+ const fs = yield* FileSystem.FileSystem;
187
+ const allDeps = [];
188
+ const seenNames = new Set();
189
+ const addDeps = (deps) => {
190
+ for (const dep of deps) {
191
+ if (!seenNames.has(dep.purl.name)) {
192
+ seenNames.add(dep.purl.name);
193
+ allDeps.push(dep);
194
+ }
195
+ }
196
+ };
197
+ // Scan for *.opam files
198
+ const entries = yield* fs.readDirectory(projectDir).pipe(Effect.option);
199
+ if (Option.isSome(entries)) {
200
+ const opamFiles = entries.value.filter((e) => e.endsWith(".opam"));
201
+ for (const opamFile of opamFiles) {
202
+ const filePath = path.join(projectDir, opamFile);
203
+ const content = yield* readFileOptional(filePath);
204
+ if (Option.isSome(content)) {
205
+ const deps = parseOpamDepends(content.value, filePath);
206
+ if (deps.length === 0 && content.value.trim().length > 0) {
207
+ if (!content.value.includes("opam-version:") && !content.value.includes("name:")) {
208
+ yield* Effect.logWarning(`Malformed opam file: ${opamFile}, skipping`);
209
+ }
210
+ }
211
+ addDeps(deps);
212
+ }
213
+ }
214
+ }
215
+ // Parse dune-project
216
+ const dunePath = path.join(projectDir, "dune-project");
217
+ const duneContent = yield* readFileOptional(dunePath);
218
+ if (Option.isSome(duneContent)) {
219
+ const duneDeps = parseDuneProjectDepends(duneContent.value, dunePath);
220
+ addDeps(duneDeps);
221
+ }
222
+ return allDeps;
223
+ }, Effect.annotateLogs({ detector: "opam" }), Effect.withSpan("detect.opam")),
224
+ };
225
+ /**
226
+ * Parse x-axm custom fields from opam file content.
227
+ * Returns a record mapping field names (without x-axm- prefix) to values.
228
+ */
229
+ const parseXAxmFields = (content) => {
230
+ const fields = {};
231
+ const regex = /^x-axm-(\S+)\s*:\s*(.+)$/gim;
232
+ let match;
233
+ while ((match = regex.exec(content)) !== null) {
234
+ const fieldName = match[1];
235
+ const rawValue = match[2]?.trim();
236
+ if (fieldName === undefined || rawValue === undefined)
237
+ continue;
238
+ // Try to parse as JSON value (for arrays, etc.)
239
+ try {
240
+ fields[fieldName] = JSON.parse(rawValue);
241
+ }
242
+ catch {
243
+ fields[fieldName] = rawValue;
244
+ }
245
+ }
246
+ return fields;
247
+ };
248
+ /**
249
+ * opam package reader.
250
+ *
251
+ * Reads `x-axm` prefixed custom fields from `.opam` files in the
252
+ * opam switch at `~/.opam/<switch>/lib/<pkg>/opam`.
253
+ *
254
+ * @experimental This API is unstable and may change without notice.
255
+ */
256
+ export const opamReader = {
257
+ type: opamType,
258
+ read: Effect.fn("read.opam")(function* (pkg) {
259
+ const path = yield* Path.Path;
260
+ const fs = yield* FileSystem.FileSystem;
261
+ const pkgName = pkg.purl.name;
262
+ const home = os.homedir();
263
+ const opamDir = path.join(home, ".opam");
264
+ // List available switches
265
+ const switches = yield* fs.readDirectory(opamDir).pipe(Effect.option);
266
+ if (Option.isNone(switches))
267
+ return Option.none();
268
+ // Check each switch for the package
269
+ for (const switchName of switches.value) {
270
+ const opamFilePath = path.join(opamDir, switchName, "lib", pkgName, "opam");
271
+ const content = yield* readFileOptional(opamFilePath);
272
+ if (Option.isNone(content))
273
+ continue;
274
+ const xAxmFields = parseXAxmFields(content.value);
275
+ if (Object.keys(xAxmFields).length === 0)
276
+ continue;
277
+ const metaResult = decodeAxmMeta(xAxmFields);
278
+ if (Result.isFailure(metaResult)) {
279
+ yield* Effect.logWarning(`Invalid axm metadata in ${pkgName}: schema validation failed`);
280
+ return Option.none();
281
+ }
282
+ return Option.some(metaResult.success.extensions);
283
+ }
284
+ return Option.none();
285
+ }, Effect.annotateLogs({ reader: "opam" }), Effect.withSpan("read.opam")),
286
+ };
287
+ //# sourceMappingURL=opam.js.map
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Dart/Flutter pub 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
+ * Pub package detector.
10
+ *
11
+ * Scans `pubspec.yaml` in the project directory and extracts dependencies
12
+ * from `dependencies` and `dev_dependencies`.
13
+ *
14
+ * @experimental This API is unstable and may change without notice.
15
+ */
16
+ export declare const pubDetector: PackageDetector;
17
+ /**
18
+ * Pub package reader.
19
+ *
20
+ * Reads `.dart_tool/package_config.json` to locate the package root,
21
+ * then reads `pubspec.yaml` from that root and extracts the `axm` field.
22
+ *
23
+ * @experimental This API is unstable and may change without notice.
24
+ */
25
+ export declare const pubReader: PackageReader;
26
+ //# sourceMappingURL=pub.d.ts.map
@@ -0,0 +1,357 @@
1
+ /**
2
+ * Dart/Flutter pub 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:url URL <-> path conversion has no @effect/platform equivalent.
8
+ import { fileURLToPath, pathToFileURL } from "node:url";
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 { PackageURL } from "packageurl-js";
15
+ import { PackageTypeSchema } from "@agentxm/extension-model/unstable/packaging/package-type";
16
+ import { decodeAxmMeta, decodePurl, parseJsonOptional, readFileOptional } from "./reader-io.js";
17
+ const pubType = Schema.decodeUnknownSync(PackageTypeSchema)("pub");
18
+ /** Pub package names must be lowercase `[a-z0-9_]` only. */
19
+ const VALID_PUB_NAME = /^[a-z0-9_]+$/;
20
+ /**
21
+ * Returns true if the specifier is an exact semver version (no range operators).
22
+ * Exact versions match: digits and dots only, e.g. "1.2.0", "1.0.0+1".
23
+ */
24
+ const isExactVersion = (specifier) => /^\d+\.\d+\.\d+(?:[+][a-zA-Z0-9._+-]*)?$/.test(specifier);
25
+ /**
26
+ * Minimal line-based YAML parser for pubspec.yaml dependency sections.
27
+ * Extracts `dependencies:` and `dev_dependencies:` maps.
28
+ *
29
+ * Returns a record of dependency name -> value string (version or structured).
30
+ * Only handles flat key-value pairs and detects structured entries (path/git/sdk).
31
+ */
32
+ const parsePubspecDeps = (content) => {
33
+ const lines = content.split("\n");
34
+ const deps = [];
35
+ let inDepSection = false;
36
+ let currentDepName;
37
+ let currentDepIsStructured = false;
38
+ for (const line of lines) {
39
+ // Skip comments
40
+ if (line.trimStart().startsWith("#"))
41
+ continue;
42
+ // Detect top-level section headers (no leading whitespace)
43
+ if (!line.startsWith(" ") && !line.startsWith("\t") && line.trim().length > 0) {
44
+ const trimmed = line.trim();
45
+ if (trimmed === "dependencies:" || trimmed === "dev_dependencies:") {
46
+ inDepSection = true;
47
+ currentDepName = undefined;
48
+ currentDepIsStructured = false;
49
+ continue;
50
+ }
51
+ // Any other top-level key ends the section
52
+ if (trimmed.endsWith(":") || trimmed.includes(":")) {
53
+ inDepSection = false;
54
+ currentDepName = undefined;
55
+ currentDepIsStructured = false;
56
+ continue;
57
+ }
58
+ }
59
+ if (!inDepSection)
60
+ continue;
61
+ const trimmed = line.trim();
62
+ if (trimmed.length === 0)
63
+ continue;
64
+ // Determine indentation level
65
+ const indent = line.length - line.trimStart().length;
66
+ // dep-level entry (typically 2 spaces or 1 tab)
67
+ if (indent > 0 && indent <= 4 && !trimmed.startsWith("-")) {
68
+ const colonIdx = trimmed.indexOf(":");
69
+ if (colonIdx === -1)
70
+ continue;
71
+ const name = trimmed.slice(0, colonIdx).trim();
72
+ const value = trimmed.slice(colonIdx + 1).trim();
73
+ currentDepName = name;
74
+ currentDepIsStructured = false;
75
+ if (value.length === 0) {
76
+ // Structured dependency follows on next lines
77
+ currentDepIsStructured = true;
78
+ continue;
79
+ }
80
+ // Simple version string: `http: ^1.0.0` or `http: 1.2.0` or `http: any`
81
+ deps.push({ name, version: value, isSkipped: false });
82
+ currentDepName = undefined;
83
+ }
84
+ else if (indent > 4 && currentDepName !== undefined && currentDepIsStructured) {
85
+ // Sub-key of a structured dependency
86
+ const colonIdx = trimmed.indexOf(":");
87
+ if (colonIdx === -1)
88
+ continue;
89
+ const subKey = trimmed.slice(0, colonIdx).trim();
90
+ if (subKey === "path" || subKey === "git" || subKey === "sdk") {
91
+ deps.push({ name: currentDepName, version: undefined, isSkipped: true });
92
+ currentDepName = undefined;
93
+ currentDepIsStructured = false;
94
+ }
95
+ else if (subKey === "version") {
96
+ const subValue = trimmed.slice(colonIdx + 1).trim();
97
+ // Hosted dependency with explicit version
98
+ deps.push({ name: currentDepName, version: subValue, isSkipped: false });
99
+ currentDepName = undefined;
100
+ currentDepIsStructured = false;
101
+ }
102
+ }
103
+ }
104
+ return { deps };
105
+ };
106
+ /**
107
+ * Convert a parsed pub dependency to a DetectedPackage, or undefined if skipped.
108
+ */
109
+ const depToPurl = (name, version, isSkipped, source) => {
110
+ if (isSkipped)
111
+ return undefined;
112
+ if (!VALID_PUB_NAME.test(name))
113
+ return undefined;
114
+ const resolvedVersion = version !== undefined && isExactVersion(version) ? version : undefined;
115
+ const purl = new PackageURL("pub", null, name, resolvedVersion ?? null, null, null);
116
+ const purlParts = decodePurl(purl.toString());
117
+ return { purl: purlParts, type: pubType, source };
118
+ };
119
+ /**
120
+ * Pub package detector.
121
+ *
122
+ * Scans `pubspec.yaml` in the project directory and extracts dependencies
123
+ * from `dependencies` and `dev_dependencies`.
124
+ *
125
+ * @experimental This API is unstable and may change without notice.
126
+ */
127
+ export const pubDetector = {
128
+ type: pubType,
129
+ detect: Effect.fn("detect.pub")(function* (projectDir) {
130
+ const path = yield* Path.Path;
131
+ const manifestPath = path.join(projectDir, "pubspec.yaml");
132
+ const content = yield* readFileOptional(manifestPath);
133
+ if (Option.isNone(content))
134
+ return [];
135
+ // Basic validation: check if it looks like YAML
136
+ const trimmed = content.value.trim();
137
+ if (trimmed.length === 0)
138
+ return [];
139
+ // Check for obviously malformed YAML
140
+ if (trimmed.startsWith("{") && !trimmed.includes(":")) {
141
+ yield* Effect.logWarning("Malformed pubspec.yaml, skipping");
142
+ return [];
143
+ }
144
+ const { deps } = parsePubspecDeps(content.value);
145
+ const results = [];
146
+ for (const dep of deps) {
147
+ const detected = depToPurl(dep.name, dep.version, dep.isSkipped, manifestPath);
148
+ if (detected !== undefined)
149
+ results.push(detected);
150
+ }
151
+ return results;
152
+ }, Effect.annotateLogs({ detector: "pub" }), Effect.withSpan("detect.pub")),
153
+ };
154
+ /**
155
+ * Schema for package_config.json package entries.
156
+ */
157
+ const PackageConfigEntrySchema = Schema.Struct({
158
+ name: Schema.String,
159
+ rootUri: Schema.String,
160
+ });
161
+ const PackageConfigSchema = Schema.Struct({
162
+ packages: Schema.optional(Schema.Array(PackageConfigEntrySchema)),
163
+ });
164
+ const decodePackageConfig = Schema.decodeUnknownResult(PackageConfigSchema);
165
+ /**
166
+ * Resolve a `package_config.json` `rootUri` to an absolute filesystem path.
167
+ *
168
+ * `rootUri` is a URI reference resolved against the location of
169
+ * `package_config.json` (the `.dart_tool/` directory). A real `dart pub get`
170
+ * writes absolute `file://` URIs for hosted packages, while path dependencies
171
+ * and hand-written configs may use relative paths; resolving as a URL handles
172
+ * both and decodes any percent-encoding. `path.resolve` cannot be used here —
173
+ * it has no notion of URI schemes and mangles `file://` values into garbage
174
+ * paths. Returns `Option.none` for a `rootUri` that is not a resolvable
175
+ * `file:` location.
176
+ */
177
+ const resolvePackageRoot = (dartToolDir, rootUri) => {
178
+ try {
179
+ const baseUrl = new URL(`${pathToFileURL(dartToolDir).href}/`);
180
+ return Option.some(fileURLToPath(new URL(rootUri, baseUrl)));
181
+ }
182
+ catch {
183
+ return Option.none();
184
+ }
185
+ };
186
+ const parseYamlInlineObject = (value) => {
187
+ const trimmed = value.trim();
188
+ if (!trimmed.startsWith("{") || !trimmed.endsWith("}")) {
189
+ return trimmed.replace(/^["']|["']$/g, "");
190
+ }
191
+ const body = trimmed.slice(1, -1);
192
+ const entry = {};
193
+ for (const part of body.split(",")) {
194
+ const pair = /^\s*([A-Za-z0-9_-]+)\s*:\s*["']([^"']+)["']\s*$/.exec(part);
195
+ const key = pair?.[1];
196
+ const rawValue = pair?.[2];
197
+ if (key === undefined || rawValue === undefined)
198
+ continue;
199
+ entry[key] = rawValue;
200
+ }
201
+ return Object.keys(entry).length > 0 ? entry : value;
202
+ };
203
+ const parseYamlInlineArray = (value) => {
204
+ const inner = value.slice(1, value.lastIndexOf("]")).trim();
205
+ if (inner === "")
206
+ return [];
207
+ const objectMatches = Array.from(inner.matchAll(/\{[^{}]*\}/g), (match) => match[0]);
208
+ if (objectMatches.length > 0)
209
+ return objectMatches.map(parseYamlInlineObject);
210
+ return inner
211
+ .split(",")
212
+ .map((item) => item.trim())
213
+ .filter((item) => item.length > 0)
214
+ .map((item) => item.replace(/^["']|["']$/g, ""));
215
+ };
216
+ /**
217
+ * Extract the `axm` field from a pubspec.yaml content string using line-based parsing.
218
+ * Returns the axm field value as a simple JSON-like structure, or undefined if not found.
219
+ */
220
+ const extractAxmFromPubspec = (content) => {
221
+ const lines = content.split("\n");
222
+ let inAxmSection = false;
223
+ const axmLines = [];
224
+ let baseIndent = 0;
225
+ for (const line of lines) {
226
+ if (line.trimStart().startsWith("#"))
227
+ continue;
228
+ // Detect "axm:" at the top level
229
+ if (!line.startsWith(" ") && !line.startsWith("\t") && line.trim() === "axm:") {
230
+ inAxmSection = true;
231
+ baseIndent = 0;
232
+ continue;
233
+ }
234
+ if (inAxmSection) {
235
+ const trimmed = line.trim();
236
+ if (trimmed.length === 0)
237
+ continue;
238
+ const indent = line.length - line.trimStart().length;
239
+ // If we hit another top-level key, stop
240
+ if (indent === 0 && trimmed.length > 0) {
241
+ break;
242
+ }
243
+ if (baseIndent === 0)
244
+ baseIndent = indent;
245
+ if (indent >= baseIndent) {
246
+ axmLines.push(line);
247
+ }
248
+ else {
249
+ break;
250
+ }
251
+ }
252
+ }
253
+ if (axmLines.length === 0)
254
+ return undefined;
255
+ // Parse the axm section: extract extensions list
256
+ const result = {};
257
+ let currentKey;
258
+ const currentList = [];
259
+ let inList = false;
260
+ for (const line of axmLines) {
261
+ const trimmed = line.trim();
262
+ if (trimmed.startsWith("#"))
263
+ continue;
264
+ // Check for key: value or key:
265
+ const colonIdx = trimmed.indexOf(":");
266
+ if (colonIdx > 0 && !trimmed.startsWith("-")) {
267
+ // Save previous list if any
268
+ if (inList && currentKey !== undefined) {
269
+ result[currentKey] = currentList.slice();
270
+ currentList.length = 0;
271
+ }
272
+ const key = trimmed.slice(0, colonIdx).trim();
273
+ const value = trimmed.slice(colonIdx + 1).trim();
274
+ currentKey = key;
275
+ if (value.length > 0 && !value.startsWith("[")) {
276
+ // Simple scalar value
277
+ result[key] = value;
278
+ inList = false;
279
+ }
280
+ else if (value.startsWith("[")) {
281
+ result[key] = parseYamlInlineArray(value);
282
+ inList = false;
283
+ }
284
+ else {
285
+ // List follows on next lines
286
+ inList = true;
287
+ }
288
+ }
289
+ else if (trimmed.startsWith("- ") && inList && currentKey !== undefined) {
290
+ const item = parseYamlInlineObject(trimmed.slice(2));
291
+ currentList.push(item);
292
+ }
293
+ }
294
+ // Save final list
295
+ if (inList && currentKey !== undefined) {
296
+ result[currentKey] = currentList.slice();
297
+ }
298
+ return result;
299
+ };
300
+ /**
301
+ * Pub package reader.
302
+ *
303
+ * Reads `.dart_tool/package_config.json` to locate the package root,
304
+ * then reads `pubspec.yaml` from that root and extracts the `axm` field.
305
+ *
306
+ * @experimental This API is unstable and may change without notice.
307
+ */
308
+ export const pubReader = {
309
+ type: pubType,
310
+ read: Effect.fn("read.pub")(function* (pkg) {
311
+ const path = yield* Path.Path;
312
+ // Derive project directory from the source manifest path
313
+ const projectDir = path.dirname(pkg.source);
314
+ // Read .dart_tool/package_config.json
315
+ const packageConfigPath = path.join(projectDir, ".dart_tool", "package_config.json");
316
+ const configContent = yield* readFileOptional(packageConfigPath);
317
+ if (Option.isNone(configContent))
318
+ return Option.none();
319
+ const configParsed = yield* parseJsonOptional(configContent.value, "package_config.json");
320
+ if (Option.isNone(configParsed))
321
+ return Option.none();
322
+ const configResult = decodePackageConfig(configParsed.value);
323
+ if (Result.isFailure(configResult))
324
+ return Option.none();
325
+ const packages = configResult.success.packages;
326
+ if (packages === undefined)
327
+ return Option.none();
328
+ // Find the package entry matching our detected package name
329
+ const pkgEntry = packages.find((p) => p.name === pkg.purl.name);
330
+ if (pkgEntry === undefined)
331
+ return Option.none();
332
+ // Resolve rootUri against the .dart_tool/ directory. package_config.json
333
+ // entries are URI references — a real `dart pub get` writes absolute
334
+ // `file://` URIs for hosted packages — so resolve as a URL, not a path.
335
+ const dartToolDir = path.resolve(projectDir, ".dart_tool");
336
+ const packageRoot = resolvePackageRoot(dartToolDir, pkgEntry.rootUri);
337
+ if (Option.isNone(packageRoot))
338
+ return Option.none();
339
+ // Read pubspec.yaml from package root
340
+ const pubspecPath = path.join(packageRoot.value, "pubspec.yaml");
341
+ const pubspecContent = yield* readFileOptional(pubspecPath);
342
+ if (Option.isNone(pubspecContent))
343
+ return Option.none();
344
+ // Extract axm field from pubspec
345
+ const axmRaw = extractAxmFromPubspec(pubspecContent.value);
346
+ if (axmRaw === undefined)
347
+ return Option.none();
348
+ // Validate axm metadata structure
349
+ const metaResult = decodeAxmMeta(axmRaw);
350
+ if (Result.isFailure(metaResult)) {
351
+ yield* Effect.logWarning(`Invalid axm metadata in ${pkg.purl.name}: schema validation failed`);
352
+ return Option.none();
353
+ }
354
+ return Option.some(metaResult.success.extensions);
355
+ }, Effect.annotateLogs({ reader: "pub" }), Effect.withSpan("read.pub")),
356
+ };
357
+ //# sourceMappingURL=pub.js.map
@@ -0,0 +1,23 @@
1
+ /**
2
+ * pypi package detector and reader for package-compatibility discovery.
3
+ *
4
+ * Parses Python dependency files (pyproject.toml, requirements.txt,
5
+ * setup.cfg, Pipfile) and reads axm metadata from installed packages.
6
+ *
7
+ * @experimental This API is unstable and may change without notice.
8
+ * @packageDocumentation
9
+ */
10
+ import type { PackageDetector, PackageReader } from "./types.js";
11
+ /**
12
+ * pypi package detector. Parses Python dependency files in priority order.
13
+ *
14
+ * @experimental This API is unstable and may change without notice.
15
+ */
16
+ export declare const pypiDetector: PackageDetector;
17
+ /**
18
+ * pypi package reader. Reads axm metadata from installed Python packages.
19
+ *
20
+ * @experimental This API is unstable and may change without notice.
21
+ */
22
+ export declare const pypiReader: PackageReader;
23
+ //# sourceMappingURL=pypi.d.ts.map