API Reference

Module

The PackageURLs module provides the PURL type and related functions for working with Package URLs.

using PackageURLs

Types

PackageURLs.PURLType
PURL

Represents a Package URL (PURL) as specified in ECMA-427.

A PURL is a URL string used to identify and locate a software package in a mostly universal and uniform way across programming languages, package managers, and packaging conventions.

Fields

  • type::String: The package type or protocol (e.g., "julia", "npm", "pypi")
  • namespace::Union{String, Nothing}: Optional namespace or organization
  • name::String: The package name
  • version::Union{String, Nothing}: Optional package version
  • qualifiers::Union{Dict{String,String}, Nothing}: Optional key-value metadata
  • subpath::Union{String, Nothing}: Optional path within the package

Examples

# Parse from string
purl = parse(PURL, "pkg:julia/Example@1.0.0")

# Construct programmatically
purl = PURL("julia", nothing, "Example", "1.0.0", nothing, nothing)

# Use string macro
purl = purl"pkg:julia/Example@1.0.0"

# Convert to string
string(purl)  # => "pkg:julia/Example@1.0.0"
source
PackageURLs.PURLErrorType
PURLError <: Exception

Exception thrown when PURL parsing or validation fails.

Fields

  • message::String: Human-readable error description
  • position::Union{Int, Nothing}: Character position where error occurred (1-based)

Examples

try
    parse(PURL, "invalid")
catch e::PURLError
    println("Error at position $(e.position): $(e.message)")
end
source

String Macro

PackageURLs.@purl_strMacro
@purl_str(s)

Create a PURL from a string literal with compile-time validation.

Examples

purl"pkg:julia/Example@1.0.0"
purl"pkg:npm/%40angular/core@15.0.0"

Invalid PURLs will cause a compile-time error.

source

Bundled Artifact Paths

Functions to access the bundled purl-spec v1.0.0 artifact containing official type definitions and test fixtures.

PackageURLs.purl_spec_pathFunction
purl_spec_path() -> String

Return the path to the bundled purl-spec v1.0.0 artifact root.

This directory contains the full purl-spec repository including type definitions, test fixtures, JSON schemas, and documentation.

Example

root = purl_spec_path()
# ~/.julia/artifacts/<hash>/purl-spec-1.0.0/
source
PackageURLs.type_definitions_pathFunction
type_definitions_path() -> String

Return the path to the bundled PURL type definitions directory.

Contains 37 official type definition JSON files (e.g., pypi-definition.json).

Example

types_dir = type_definitions_path()
# ~/.julia/artifacts/<hash>/purl-spec-1.0.0/types/
source
PackageURLs.test_fixtures_pathFunction
test_fixtures_path() -> String

Return the path to the bundled PURL test fixtures directory.

Contains official test cases for each type (e.g., types/pypi-test.json).

Example

tests_dir = test_fixtures_path()
# ~/.julia/artifacts/<hash>/purl-spec-1.0.0/tests/
source

Type Definitions

Types and functions for loading and registering custom type definitions from JSON.

PackageURLs.TypeDefinitionType
TypeDefinition

Represents a PURL type's rules loaded from JSON per ECMA-427 Section 6.

Fields

  • type::String: Package ecosystem identifier (e.g., "cargo", "swift")
  • description::Union{String, Nothing}: Human-readable description
  • name_normalize::Vector{String}: Normalization operations to apply to names
  • required_qualifiers::Vector{String}: Qualifiers that must be present
  • known_qualifiers::Vector{String}: Recognized qualifier keys

Supported Normalization Operations

  • "lowercase": Convert name to lowercase
  • "replace_underscore": Replace _ with -
  • "replace_dot": Replace . with -
  • "collapse_hyphens": Collapse multiple - to single -

Example

def = TypeDefinition(
    "cargo",
    "Rust crates from crates.io",
    ["lowercase"],
    String[],
    ["arch", "os"]
)
source
PackageURLs.JsonTypeRulesType
JsonTypeRules <: TypeRules

Type rules loaded from a JSON definition. Used by the typerules() dispatcher when a type is found in TYPEREGISTRY.

source
PackageURLs.load_type_definitionFunction
load_type_definition(path::AbstractString) -> TypeDefinition

Load a type definition from a JSON file per ECMA-427 Section 6 schema.

ECMA-427 Format

{
    "type": "pypi",
    "description": "PyPI packages",
    "name_definition": {
        "case_sensitive": false,
        "normalization_rules": ["Replace underscore _ with dash -"]
    },
    "qualifiers_definition": [
        {"key": "file_name", "requirement": "optional"}
    ]
}

Normalization Derivation

  • name_definition.case_sensitive: false → "lowercase" normalization
  • normalization_rules with "underscore" AND "dash" → "replace_underscore"
  • normalization_rules with "dot" AND "dash"/"hyphen" → "replace_dot"

Arguments

  • path: Path to the JSON file

Returns

  • TypeDefinition: The loaded type definition

Throws

  • ArgumentError: If the file does not exist
  • PURLError: If the JSON is missing required fields or has invalid values

Example

def = load_type_definition("data/type_definitions/pypi.json")
register_type_definition!(def)
source
PackageURLs.register_type_definition!Function
register_type_definition!(def::TypeDefinition)

Register a type definition in the global registry. The type name is stored in lowercase for consistent lookup.

Registered types take priority over hardcoded type rules.

Example

def = TypeDefinition("mytype", "My custom type", ["lowercase"], String[], String[])
register_type_definition!(def)
source
PackageURLs.list_type_definitionsFunction
list_type_definitions() -> Dict{String, TypeDefinition}

Return a copy of the type registry containing all registered type definitions.

Example

defs = list_type_definitions()
for (name, def) in defs
    println("$name: $(def.description)")
end
source
PackageURLs.clear_type_registry!Function
clear_type_registry!()

Remove all registered type definitions from the registry. This restores the system to use only hardcoded type rules.

Example

register_type_definition!(my_def)
# ... use custom type ...
clear_type_registry!()  # Back to default rules
source

Parsing and Serialization

The following standard Julia functions work with PURL:

parse

parse(PURL, s::AbstractString) -> PURL

Parse a PURL string into a PURL object. Throws PURLError if the string is not a valid PURL.

purl = parse(PURL, "pkg:npm/lodash@4.17.21")

tryparse

tryparse(PURL, s::AbstractString) -> Union{PURL, Nothing}

Try to parse a PURL string, returning nothing on failure instead of throwing an exception.

result = tryparse(PURL, "invalid")  # nothing
result = tryparse(PURL, "pkg:npm/lodash@4.17.21")  # PURL

string

string(purl::PURL) -> String

Convert a PURL back to its canonical string form.

purl = purl"pkg:npm/lodash@4.17.21"
string(purl)  # "pkg:npm/lodash@4.17.21"

print

print(io::IO, purl::PURL)

Print the PURL string to an IO stream.

show

show(io::IO, purl::PURL)

Display a PURL in the REPL with type information.

PURL Fields

The PURL struct has the following fields:

FieldTypeDescription
typeStringPackage ecosystem (e.g., "julia", "npm")
namespaceUnion{String, Nothing}Organizational grouping
nameStringPackage name
versionUnion{String, Nothing}Package version
qualifiersUnion{Dict{String,String}, Nothing}Key-value metadata
subpathUnion{String, Nothing}Path within package

Access fields directly:

purl = parse(PURL, "pkg:maven/org.apache.commons/commons-lang3@3.12.0")
purl.type       # "maven"
purl.namespace  # "org.apache.commons"
purl.name       # "commons-lang3"
purl.version    # "3.12.0"
purl.qualifiers # nothing
purl.subpath    # nothing