ecluse:ecluse-core
Safe HaskellNone
LanguageGHC2021

Ecluse.Core.Registry.Npm

Description

The npm read and relay data plane: the effectful metadata fetch and the first-party publish relay over http-client.

This module is the network half of the npm read-side protocol boundary. Where Ecluse.Core.Registry.Npm.Wire and Ecluse.Core.Registry.Npm.Project are the pure decode and projection, this is the side-effecting exchange: fetchMetadataFormBounded reads a metadata document bounded with every failure in its typed channel, and relayPublishDocument forwards a client's own publish to the publication target. The mirror write is not here: its protocol codec lives in Ecluse.Core.Registry.Npm.Publish and executes through the shared transport (Ecluse.Core.Registry.Publish).

It speaks the npm registry protocol directly with http-client, never amazonka: the control plane (the GetAuthorizationToken mint, the mirror queue) is amazonka's job behind separate handles, but the data plane: fetch metadata, stream a tarball, publish: is ordinary HTTPS+JSON, identical across every npm-speaking backend. Keeping the streaming path off amazonka's conduit/ResourceT machinery is exactly what makes bounded-memory artifact proxying tractable.

Streaming and buffering

The artifact request builders (artifactRequestByFile and artifactRequestByUrl) mark their requests non-decompressing so a tarball is opaque binary that must reach the client byte-for-byte, and are exposed so the web layer can relay the open body without buffering the whole artifact in memory. The mirror worker, which must read the whole artifact to verify its integrity before publishing, buffers it (bounded) through fetchArtifactBytes instead.

Authentication

Every request here carries an injected bearer token (or none); this module never originates credential policy. Which token to send on which request is the request pipeline's authority model, decided upstream of this module.

Synopsis

Construction

data NpmClientConfig Source #

Everything this data plane needs to talk to one npm-speaking registry: the base URL, the shared HTTP Manager, and an optional injected bearer token.

The Manager is shared (it owns the connection pool), so it is taken rather than built here: the same one the composition root reuses across requests. The token is whatever the request pipeline decided this client should present; this module never chooses it.

Constructors

NpmClientConfig 

Fields

Bounded metadata fetch

fetchMetadataFormBounded :: NpmClientConfig -> MetadataForm -> Validators -> PackageName -> IO (Either FetchFault RegistryResponse) Source #

Fetch a package's metadata in the requested MetadataForm, relaying any conditional-GET Validators, reporting every fetch failure as a FetchFault value: an unformable request URL, a response-bound breach, or a transport fault (classifyTransport folds the http-client exception into the typed channel at this edge). Total: no fetch failure escapes as an exception, so the serve read adapter (Ecluse.Core.Registry.Npm.Metadata) threads it straight into its own typed channel with no throw-then-catch round-trip.

The body is read chunk-by-chunk through boundedRead against the config's npmLimits, not buffered whole: a hostile or compromised upstream returning a body larger than maxBodyBytes is refused fail-closed as a FetchBoundExceeded rather than exhausting memory. The transport wrap covers the whole exchange, the body read included: metadata is buffered before anything is served, so a connection lost mid-body is still a pre-commit fault with a value representation, not a half-delivered response.

First-party publish relay

relayPublishDocument :: NpmClientConfig -> PackageName -> ByteString -> IO (Either PublishRelayFault PublishRelayResponse) Source #

Relay a client's npm publish document to the publication target and return the target's own response: the first-party publish primitive behind the PUT /{pkg} serve path.