Skip to content

Commit 974189c

Browse files
committed
Hello world
0 parents  commit 974189c

51 files changed

Lines changed: 4442 additions & 0 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/dependabot.yml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
version: 2
2+
updates:
3+
- package-ecosystem: gomod
4+
directory: /
5+
schedule:
6+
interval: weekly
7+
open-pull-requests-limit: 10
8+
9+
- package-ecosystem: github-actions
10+
directory: /
11+
schedule:
12+
interval: weekly
13+
open-pull-requests-limit: 5

.github/workflows/ci.yml

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
9+
permissions: {}
10+
11+
jobs:
12+
test:
13+
runs-on: ubuntu-latest
14+
strategy:
15+
matrix:
16+
go-version: ['1.25']
17+
18+
steps:
19+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
20+
with:
21+
persist-credentials: false
22+
23+
- name: Set up Go
24+
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
25+
with:
26+
go-version: ${{ matrix.go-version }}
27+
28+
- name: Build
29+
run: go build -v ./...
30+
31+
- name: Test
32+
run: go test -v -race ./...
33+
34+
lint:
35+
runs-on: ubuntu-latest
36+
steps:
37+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
38+
with:
39+
persist-credentials: false
40+
41+
- name: Set up Go
42+
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
43+
with:
44+
go-version: '1.25'
45+
46+
- name: golangci-lint
47+
uses: golangci/golangci-lint-action@ba0d7d2ec06a0ea1cb5fa41b2e4a3ab91d21278a # v9
48+
with:
49+
version: latest

.gitignore

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Binaries
2+
*.exe
3+
*.exe~
4+
*.dll
5+
*.so
6+
*.dylib
7+
8+
# Test binaries
9+
*.test
10+
11+
# Output of go coverage
12+
*.out
13+
14+
# Go workspace
15+
go.work
16+
go.work.sum
17+
18+
# IDE
19+
.idea/
20+
.vscode/
21+
*.swp
22+
*.swo
23+
24+
# OS
25+
.DS_Store
26+
Thumbs.db

.golangci.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
version: "2"
2+
3+
linters:
4+
settings:
5+
goconst:
6+
ignore-tests: true

LICENSE

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
MIT License
2+
3+
Copyright (c) 2026 Andrew Nesbitt
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
6+
associated documentation files (the "Software"), to deal in the Software without restriction, including
7+
without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+
copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the
9+
following conditions:
10+
11+
The above copyright notice and this permission notice shall be included in all copies or substantial
12+
portions of the Software.
13+
14+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
15+
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
16+
EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
17+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
18+
USE OR OTHER DEALINGS IN THE SOFTWARE.

README.md

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
# provides
2+
3+
A Go library for mapping package identities to the names used in source code. A Python distribution may provide a differently named module, a Go module may contain several package paths, and a Maven artifact may contain several Java packages. Project bindings retain dependency renames and aliases separately from the canonical package identity.
4+
5+
## Installation
6+
7+
```bash
8+
go get github.com/git-pkgs/provides
9+
```
10+
11+
## Model
12+
13+
`Surface` maps a versioned PURL to its provided source names. `Binding` connects a PURL to the imported and local names used by one project. An aliased binding may also retain the package-side target name. Both retain the evidence used to produce the mapping.
14+
15+
`ProvidedName.Name` is the exact source-visible spelling. Package-manager normalisation does not apply to it, and matching is case-sensitive. `flask` therefore does not match `Flask`.
16+
17+
## Matching names
18+
19+
Exact names match only themselves. Prefix names also match descendants separated by the configured boundary:
20+
21+
```go
22+
pythonModule := provides.ProvidedName{
23+
Language: "python",
24+
Name: "werkzeug",
25+
Kind: "module",
26+
Match: provides.MatchPrefix,
27+
Separator: ".",
28+
}
29+
30+
pythonModule.Matches("werkzeug") // true
31+
pythonModule.Matches("werkzeug.http") // true
32+
pythonModule.Matches("werkzeugx") // false
33+
pythonModule.Matches("Werkzeug.http") // false
34+
```
35+
36+
Explicit export maps can use exact matching so an exported npm subpath does not imply that deeper paths are available:
37+
38+
```go
39+
export := provides.ProvidedName{
40+
Language: "javascript",
41+
Name: "react/jsx-runtime",
42+
Kind: "subpath",
43+
Match: provides.MatchExact,
44+
}
45+
46+
export.Matches("react/jsx-runtime") // true
47+
export.Matches("react/jsx-runtime/private") // false
48+
```
49+
50+
## Merging results
51+
52+
Resolvers can find the same mapping through manifests, installed metadata, artifacts, or curated data. `MergeSurfaceResults` and `MergeBindingResults` deduplicate mappings, combine their evidence, retain conflicting mappings, and return stable ordering.
53+
54+
```go
55+
const purl = "pkg:pypi/pyyaml@6.0.3"
56+
57+
manifest := provides.SurfaceResult{Surface: provides.Surface{
58+
PURL: purl,
59+
Provides: []provides.ProvidedName{{
60+
Language: "python",
61+
Name: "yaml",
62+
Kind: "module",
63+
Evidence: []provides.Evidence{{
64+
Method: provides.EvidenceManifest,
65+
Source: "METADATA",
66+
}},
67+
}},
68+
}}
69+
70+
result := provides.MergeSurfaceResults(purl, manifest)
71+
```
72+
73+
Non-fatal resolver problems are returned as `Diagnostic` values beside any successful mappings.
74+
75+
## Curated Python surfaces
76+
77+
The `curated` package includes local mappings for PyYAML, `brotlipy`, Brotli, Pillow, and Beautiful Soup. `ResolveProjectSurfaces` joins caller-supplied dependency PURLs to those mappings:
78+
79+
```go
80+
packages := []provides.Package{
81+
{PURL: "pkg:pypi/PyYAML@6.0.3"},
82+
{PURL: "pkg:pypi/brotlipy@0.7.0"},
83+
{PURL: "pkg:pypi/Pillow@11.0.0"},
84+
}
85+
86+
result, err := provides.ResolveProjectSurfaces(
87+
context.Background(),
88+
curated.Python(),
89+
packages,
90+
provides.SurfaceOptions{},
91+
)
92+
```
93+
94+
This path reads no files, runs no package-manager commands, and makes no network requests. PyPI distribution names are normalised for catalog lookup, while each returned `Surface.PURL` retains the caller's spelling and version. Unknown packages are omitted without producing a diagnostic.
95+
96+
## Resolving an import
97+
98+
`ResolveImport` combines project-surface resolution with a reverse lookup. Every matching dependency is returned when an import is ambiguous:
99+
100+
```go
101+
result, err := provides.ResolveImport(
102+
context.Background(),
103+
curated.Python(),
104+
provides.ImportRequest{
105+
Language: "python",
106+
Name: "brotli",
107+
Packages: []provides.Package{
108+
{PURL: "pkg:pypi/brotlipy@0.7.0"},
109+
{PURL: "pkg:pypi/brotli@1.1.0"},
110+
},
111+
},
112+
)
113+
```
114+
115+
`result.Matches` contains both PURLs and the curated evidence for the `brotli` module. Supplying only the dependency declared by a project narrows the result without applying a package-name heuristic.
116+
117+
## Rust and Go bindings
118+
119+
The `bindings` package parses package-manager output supplied by the caller. It does not read files, run commands, or use the network.
120+
121+
`ParseCargoManifest` reads dependency, development-dependency, build-dependency, and target-specific tables from `Cargo.toml`. A renamed dependency keeps its canonical package in the PURL and exposes its source-visible crate name through `Binding.Imported`:
122+
123+
```go
124+
result, err := bindings.ParseCargoManifest("Cargo.toml", cargoToml)
125+
```
126+
127+
Manifest bindings use versionless PURLs because Cargo manifest versions are constraints. Path and Git dependencies are omitted when the manifest alone cannot establish a registry identity. `ParseCargoMetadata` reads the resolved root package or default workspace members and returns versioned PURLs. Its `resolve.nodes[].deps[].name` value preserves Cargo renames:
128+
129+
```go
130+
result, err := bindings.ParseCargoMetadata(cargoMetadataJSON)
131+
```
132+
133+
`ParseGoList` accepts the concatenated JSON stream written by `go list -deps -json`. Each non-standard dependency package becomes a binding to its module PURL. `Binding.Imported` contains the full import path and `Binding.Local` contains the declared Go package name, which may differ from the last path component:
134+
135+
```go
136+
result, err := bindings.ParseGoList(goListJSON)
137+
```
138+
139+
The caller controls how those byte slices are obtained. Cargo metadata is more precise than a manifest when both are available because it contains resolved versions and exact crate names.
140+
141+
## JavaScript and TypeScript bindings
142+
143+
`ParseNPMManifest` reads registry dependencies and `npm:` aliases from the dependency sections in `package.json`. Manifest ranges produce versionless PURLs. Local paths, Git sources, and URL dependencies are omitted when their npm identity cannot be established.
144+
145+
```go
146+
result, err := bindings.ParseNPMManifest("package.json", packageJSON)
147+
```
148+
149+
For `"my-react": "npm:react@18"`, `Binding.Imported` is `my-react`, `Binding.Target` is `react`, and the PURL is `pkg:npm/react`.
150+
151+
`ParseNPMPackage` reads a package root and explicit `exports` subpaths. It reports JavaScript and TypeScript names separately, with exact matching for each declared export:
152+
153+
```go
154+
result, err := bindings.ParseNPMPackage(
155+
"pkg:npm/react@19.0.0",
156+
"package.json",
157+
packageJSON,
158+
)
159+
```
160+
161+
Export patterns are returned as diagnostics by the manifest parser. A later artifact resolver can expand them against the files shipped by the package.
162+
163+
`ParseDenoConfig` reads npm targets from the top-level `imports` map in `deno.json`. Keys ending in `/` become literal prefix bindings, while other keys remain exact. `Binding.Target` retains the package-side root or subpath:
164+
165+
```go
166+
result, err := bindings.ParseDenoConfig("deno.json", denoJSON)
167+
```
168+
169+
## Artifact surfaces
170+
171+
The `artifacts` package accepts an `archives.Reader` opened by the caller. It does not download artifacts or add another archive abstraction.
172+
173+
```go
174+
reader, err := archives.OpenBytes("demo.whl", wheelBytes)
175+
if err != nil {
176+
return err
177+
}
178+
defer reader.Close()
179+
180+
result, err := artifacts.ResolvePythonWheel(
181+
context.Background(),
182+
provides.Package{PURL: "pkg:pypi/demo@1.0.0"},
183+
reader,
184+
)
185+
```
186+
187+
The package contains five artifact inspectors:
188+
189+
- `ResolvePythonWheel` reads `Import-Name` and `Import-Namespace`, then falls back to `top_level.txt` and wheel paths when those fields are absent.
190+
- `ResolveJavaArchive` enumerates Java packages and reads explicit or automatic module names.
191+
- `ResolveNPMTarball` reads package roots and exports, expanding export patterns against the tarball file list.
192+
- `ResolveCargoCrate` reads the published library target, with `src/lib.rs` as a fallback.
193+
- `ResolveGoModule` enumerates directories containing non-test Go source files.
194+
195+
Entry extraction failures become diagnostics when other archive evidence can still produce names. Listing failures and invalid PURLs remain errors. The caller owns and closes the archive reader.
196+
197+
## Resolver interfaces
198+
199+
`SurfaceResolver` resolves the names provided by one package. `BindingResolver` resolves dependency bindings for a project directory. The core package defines these interfaces without running package managers or making network requests.
200+
201+
Further acquisition adapters are planned. The current package contains the shared types, matching rules, merge helpers, resolver interfaces, project-surface join, local binding parsers, artifact inspectors, and the built-in Python catalog used by Hyrum.

0 commit comments

Comments
 (0)