ecluse
Safe HaskellNone
LanguageGHC2021

Ecluse.Registry

Description

The registry-protocol handle: the sole interface between the proxy core and any specific registry's wire protocol.

This is the ecosystem (protocol) axis — fetch, publish, and parse — and nothing more (see docs/architecture/registry-model.md → "Registry Abstraction"). It is a record of functions (the Handle pattern): a backend's smart constructor returns a RegistryClient whose closures capture that backend's private state (an HTTP manager). The proxy core operates only on PackageInfo (the packument-level view) and PackageDetails (the per-version snapshot the rules engine evaluates); an adapter projects its wire format into those, and nothing above the registry layer sees registry-specific structures.

Two design points are load-bearing:

  • The effectful fields return IO, not App. An adapter closes over its own state (HTTP manager, credentials) and never imports the proxy's Env/App, so backends stay decoupled from the core (no import cycle) — see docs/architecture/technology-stack.md → "Key Decisions". The parse* fields are pure (Either): parsing a fetched response is a total, side-effect-free projection (parse, don't validate).
  • RegistryClient deliberately carries no authentication. Protocol and auth are orthogonal axes: every managed npm registry (AWS CodeArtifact, GCP Artifact Registry, a self-hosted Verdaccio) speaks the same npm protocol and differs only in how a bearer token is minted, which lives behind the separate Ecluse.Credential handle. So one npm RegistryClient is reused across every cloud rather than near-duplicated per provider.

The abstraction is the sole interface, so a new ecosystem backend (PyPI, RubyGems, …) is an additive constructor behind this record rather than a structural change.

Synopsis

Protocol handle

data RegistryClient Source #

The registry-protocol handle — a record of functions over a backend whose private state the closures capture. The effectful fields return IO (decoupled from the core); the parse* fields are pure. See the module header.

Constructors

RegistryClient 

Fields

Fetch payload

newtype RegistryResponse Source #

A raw response fetched from a registry — the unparsed bytes of a metadata document or an artifact, as returned by fetchMetadata / fetchArtifact. It is kept opaque-of-bytes here so the protocol/data plane (fetch) is separate from parsing: a parse* field turns a RegistryResponse into a domain type.

Constructors

RegistryResponse 

Fields

Errors

newtype ParseError Source #

Why parsing a RegistryResponse into a domain type failed. Parsing is the boundary that turns untrusted wire data into the proxy's precise types, so a failure is reported (not thrown): the caller decides how to respond.

Constructors

ParseError 

Fields

Instances

Instances details
Show ParseError Source # 
Instance details

Defined in Ecluse.Registry

Eq ParseError Source # 
Instance details

Defined in Ecluse.Registry

newtype PublishError Source #

Why publishing an artifact to a registry failed — a genuine write fault reported by publishArtifact (an Queue job is then left un-acked and retried; see docs/architecture/cloud-backends.md).

This is the write-path fault and nothing more: forming the request URL is a separate concern (a UrlFormationError), so a read-path fetch can no longer surface a failure mislabelled as a publish.

Constructors

PublishError 

Fields

Instances

Instances details
Show PublishError Source # 
Instance details

Defined in Ecluse.Registry

Eq PublishError Source # 
Instance details

Defined in Ecluse.Registry

data PublishFault Source #

Why a publish could not complete, surfaced as a value rather than thrown so the mirror worker decides retry vs. drop by an exhaustive pattern match rather than by catching (and re-classifying) an exception. The two cases differ in exactly that — retryability — which is the whole reason this is a value: one is worth redelivering and the other never is.

Constructors

PublishUrlUnformable UrlFormationError

The request URL could not be formed (e.g. an empty base URL) — a configuration fault carried as its UrlFormationError. Not retryable: redelivering the job cannot change a misconfigured base URL, so the worker drops (and alerts) rather than re-enqueueing forever.

PublishRejected PublishError

The registry rejected the write (a non-2xx, non-409 status), carried as a PublishError. Retryable: the job is left un-acked and redelivered.

Instances

Instances details
Show PublishFault Source # 
Instance details

Defined in Ecluse.Registry

Eq PublishFault Source # 
Instance details

Defined in Ecluse.Registry

data UrlFormationError Source #

Why an upstream request URL could not be formed from configuration and an already-parsed PackageName.

This is a protocol-independent fault shared by every request an adapter builds — metadata fetch, artifact fetch, and publish alike — so a read-path failure is reported as what it is rather than borrowing the write-path's PublishError. It is distinct from Ecluse.Security's UrlError: that is the pure SSRF/identifier guard (which also rejects unsafe name components), whereas this is the effectful adapter's report that the configured base URL is unusable.

Constructors

EmptyBaseUrl

The configured base URL is empty, so no request URL can be formed.

UnparseableUrl Text

The formed URL string could not be parsed into a request. Carries the offending URL.

Instances

Instances details
Exception UrlFormationError Source #

A UrlFormationError is throwable. The read path (metadata/artifact fetch) treats an unformable URL as a configuration fault and raises it as this typed exception — catchable by type, never laundered into a stringly-typed stringException. The write path does not throw it: it surfaces it as a PublishFault value instead (see below), because the mirror worker must decide retry vs. drop on it.

Instance details

Defined in Ecluse.Registry

Show UrlFormationError Source # 
Instance details

Defined in Ecluse.Registry

Eq UrlFormationError Source # 
Instance details

Defined in Ecluse.Registry