ecluse:ecluse-core
Safe HaskellNone
LanguageGHC2021

Ecluse.Core.Registry.Npm

Description

The npm data plane: the effectful Ecluse.Core.Registry fields over http-client.

This module is the network half of the npm 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 fetch and publish: newNpmClient assembles a Ecluse.Core.Registry.RegistryClient whose effectful fields talk to a registry over plain HTTP, and whose parse* fields are the pure projection re-exported through the handle.

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

artifactRequest marks its request non-decompressing so a tarball is opaque binary that must reach the client byte-for-byte. The request is exposed so the web layer can relay the open body __without buffering the whole artifact in memory__. The handle's fetchArtifact field, by contrast, buffers (its RegistryResponse return is whole bytes) and is for the mirror worker, which must read the entire artifact to verify its integrity before publishing.

Authentication

The client accepts an injected bearer token and attaches it to every request; it 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 newNpmClient 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

defaultNpmConfig :: Manager -> NpmClientConfig Source #

An anonymous client config against the public registry (publicRegistryBaseUrl), using the given shared Manager and the secure-default response bounds (defaultLimits). Override npmBaseUrlnpmTokennpmLimits for a managed backend or a per-deployment budget.

publicRegistryBaseUrl :: Text Source #

The canonical public npm registry base URL, https://registry.npmjs.org. The default target when no managed backend is configured.

publicRegistryUrl :: RegistryUrl Source #

The canonical public npm registry as an https RegistryUrl: the publicRegistryBaseUrl text, https by construction. The default ECLUSE_PUBLIC_UPSTREAM when none is configured.

newNpmClient :: NpmClientConfig -> IO RegistryClient Source #

Assemble a Ecluse.Core.Registry.RegistryClient for the npm protocol over the given configuration.

The effectful fields close over the config's Manager and token and speak npm over HTTP; the parse* fields are the pure projection from Ecluse.Core.Registry.Npm.Project, re-exported through the handle. The handle's fetchMetadata requests the Abbreviated form unconditionally; the richer fetchMetadataForm (for the full packument and relayed validators) is exposed separately for the request pipeline.

newNpmPublishClient :: NpmClientConfig -> IO (Maybe Secret) -> IO RegistryClient Source #

Build an npm RegistryClient whose publishArtifact and fetchMetadata fields mint a fresh token per call via the provided IO action; the remaining fields use the token in the config. The metadata read mints because the worker's mirror-presence probe reads the mirror target through this handle, and a managed mirror (CodeArtifact) requires auth on reads as on writes -- an anonymous probe would be refused and the dedup would never confirm anything. For newNpmClient the mint is the configured token, so its behaviour is unchanged.

Lower-level fetch

fetchMetadataForm :: NpmClientConfig -> MetadataForm -> Validators -> PackageName -> IO RegistryResponse Source #

Fetch a package's metadata in the requested MetadataForm, relaying any conditional-GET Validators. The bounded-read fetch used by the handle's fetchMetadata; the request pipeline calls this directly when it needs the full packument or wants to revalidate against an ETag.

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 aborted fail-closed rather than exhausting memory.

First-party publish relay

relayPublishDocument :: NpmClientConfig -> PackageName -> ByteString -> IO (Either UrlFormationError 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.

Response-bound breach

newtype ResponseBoundExceeded Source #

Raised when an upstream metadata body breaches a Limits ceiling: the body-size guard here, or: surfaced through the same type by the serve pipeline: the version-count or nesting-depth guard.