npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

mte-relay-angular

v0.1.0-beta.1

Published

Drop-in Angular HttpClient integration for Eclypses MTE Relay v5. Replaces the HttpBackend so existing HttpClient code and interceptors transparently use the MTE Relay binary protocol.

Readme

mte-relay-angular

Drop-in Angular integration for Eclypses MTE Relay v5.

Add one provider to your bootstrap and your existing HttpClient code — services, interceptors, error handling, httpResource, everything built on Angular's HTTP stack — transparently carries its traffic to configured relay origins over the MTE Relay binary protocol (MTE/MKE encoding, Kyber-1024 key exchange, pair pools, automatic session repair). Traffic to every other origin is untouched.

import { provideHttpClient, withInterceptors } from '@angular/common/http';
import { bootstrapApplication } from '@angular/platform-browser';
import { provideMteRelay } from 'mte-relay-angular';

bootstrapApplication(AppComponent, {
  providers: [
    provideHttpClient(withInterceptors([authInterceptor])),
    // MUST come AFTER provideHttpClient — see "Provider ordering" below.
    provideMteRelay({
      companyName: 'your-company',
      licenseKey: 'your-license-key',
      relayOrigins: ['https://relay.example.com'],
    }),
  ],
});

No call-site changes:

// This service is unmodified — requests to relay.example.com are now
// MTE-encoded end to end; requests to other origins behave exactly as before.
@Injectable({ providedIn: 'root' })
export class UserService {
  private http = inject(HttpClient);
  getUsers() {
    return this.http.get<User[]>('https://relay.example.com/users');
  }
}

Supported: Angular 17 through current (peer range >=17 <23, widened after each new Angular major is verified) · zone.js and zoneless apps · standalone and NgModule bootstrap · XHR and fetch passthrough transports · MTE Relay server v5 only (mte-relay, the Go rewrite). There is no v4 support.


How it works

Angular's HttpClient sends every request through an interceptor chain that terminates in an HttpBackend. This library replaces that backend:

HttpClient  →  your interceptors (unchanged)  →  MteRelayBackend
                                                    │
                                     origin in relayOrigins?
                                       │                  │
                                      yes                 no
                                       │                  │
                          MTE Relay engine          passthrough backend
                     (mte-relay-browser: WASM,     (HttpXhrBackend or
                      Kyber pairing, pair pool,      FetchBackend, per
                      binary frames, repair,         fallbackTransport)
                      keep-alive, MKE streaming)
                                       │
                        POST / (application/octet-stream)
                                to the relay

Because interceptors run above the backend, your auth headers, logging, retry and error-mapping interceptors keep working — they see plaintext requests and decoded responses; only the wire transport changes.

The cryptographic engine is mte-relay-browser (≥ 5.0.0-beta.7), used as an instance-based dependency. This package adds no protocol logic of its own; it adapts HttpRequest/HttpEvent semantics to the engine and manages Angular concerns (DI, zones, SSR, testing).

What a relayed request does

  1. First request to a relay origin lazily bootstraps the MTE WASM runtime and license, authenticates (GET /api/mte-relay → signed clientId), and creates a pool of Kyber-paired encoder/decoder pairs (POST /api/mte-pair).
  2. The request method, path, query, headers, and body are packed and MTE/MKE-encoded into a binary frame, POSTed to the relay origin as application/octet-stream.
  3. The response frame is decoded (progressively, for MKE streams) and surfaced as a normal Angular HttpResponse / HttpEvent sequence.
  4. The engine maintains keep-alive pings, refills the pair pool in the background, and repairs sessions after relay errors (HTTP 559–569).

Installation

npm install mte-relay-angular

Two peer requirements beyond Angular itself:

  1. The MTE WASM library (mte, an npm alias to a private @eclypses/... package). This package is bring-your-own-WASM: install the mte build that matches your relay server's library family (client and server libraries must match — a mismatch typically manifests as relay error 562). Your .npmrc needs the Eclypses registry for @eclypses scope:

    @eclypses:registry=https://npm.eclypses.com
    npm install mte@npm:@eclypses/<your-matched-mte-build>@^4.2.1
  2. mte-relay-browser ≥ 5.0.0-beta.7 — installed automatically as a dependency.

Required app configuration: the crypto reference

The MTE WASM library contains a Node-only require("crypto") behind a runtime guard that never executes in browsers. The Angular production builder still tries to resolve it. Add this to your application's angular.json build options (production configuration is sufficient; ng serve does not need it):

"options": {
  "allowedCommonJsDependencies": ["mte"]   // silences the CJS optimization warning
},
"configurations": {
  "production": {
    "externalDependencies": ["crypto"]     // required for ng build
  }
}

Without externalDependencies, ng build fails with Could not resolve "crypto". This is safe: the reference becomes an inert shim that the browser code path never calls. Do not put externalDependencies on a configuration used by ng serve — the vite dev server handles the builtin itself and breaks if the module is marked external.

Provider ordering (important)

Both provideHttpClient() and provideMteRelay() bind HttpBackend, and the last binding wins. provideMteRelay() must come after provideHttpClient() or no traffic is protected. The library asserts this at startup and fails with a descriptive error if the order is wrong — a misconfigured app will not silently send plaintext.

For NgModule-based apps:

@NgModule({
  imports: [
    BrowserModule,
    HttpClientModule, // or provideHttpClient in providers
    MteRelayModule.forRoot({ companyName: '...', licenseKey: '...', relayOrigins: ['...'] }),
  ],
})
export class AppModule {}

Note: once installed, this library owns the transport. withFetch() on provideHttpClient() becomes inert; use the fallbackTransport option instead to choose the passthrough transport.

Configuration reference

provideMteRelay({
  // Required
  companyName: string,           // MTE license company
  licenseKey: string,            // MTE license key
  relayOrigins: (string | RegExp)[],  // origins running MTE Relay v5.
                                 // Strings normalize to their origin; RegExps
                                 // test against the resolved, lowercased
                                 // origin and MUST be anchored, e.g.
                                 // /^https:\/\/relay-\d+\.example\.com$/ —
                                 // unanchored patterns match substrings of
                                 // other origins and log a warning.
                                 // Mandatory allowlist — no "relay everything".

  // Engine tuning (defaults from mte-relay-browser)
  defaultEncodeType?: 'MTE' | 'MKE', // default 'MKE'
  minPairs?: number,             // default 5  — refill watermark
  initialPairs?: number,         // default 8  — pairs created at session start
  maxPairs?: number,             // default 15 — hard pool ceiling
  sequenceWindow?: number,       // default -63
  timeWindow?: number,           // default 1000 (ms)
  httpTimeoutMs?: number,        // default 30000
  keepAliveIntervalMs?: number,  // default 300000; clamped 60000–600000

  // Angular-side behavior
  ssr?: 'passthrough' | 'block', // default 'passthrough' (see SSR below)
  fallbackTransport?: 'xhr' | 'fetch', // default: framework default (see below)
  autoRetry?: boolean | { idempotentMethods?: string[] },
                                 // default true (see Auto-retry below)
  eagerInit?: boolean,           // default false — warm up WASM at app start
  streamDiagnostics?: ...,       // engine diagnostics passthrough
  onPoolEvent?: (event) => void, // pool lifecycle; called OUTSIDE the zone
})

fallbackTransport

Requests that do not target a relay origin are delegated to a normal backend. There is no public API to detect whether the app used withFetch(), so this library owns the choice.

When fallbackTransport is omitted, the default mirrors the running framework's own default HttpBackend, so passthrough traffic behaves as if this library were not installed: XHR on Angular 17–21, fetch on Angular 22+ (where FetchBackend became Angular's default and withFetch() was deprecated). Set 'xhr' or 'fetch' explicitly to pin one — 'xhr' preserves upload-progress events for passthrough traffic. During SSR the passthrough is always FetchBackend regardless of this setting — XHR does not exist on the server.

Auto-retry

Relay pair-state errors (HTTP 559–563, and local protocol failures) cause the engine to replace the failing pair or fully repair the session, then surface the error. By default this library then retries the request once, for idempotent methods only (GET/HEAD/OPTIONS) — the retry lands on the already-repaired session, so a transient pair expiry does not surface as a user-visible failure.

Mutating methods are never retried automatically: relay error 563 is raised while encoding the response, which means the upstream request may already have executed; only the caller can judge replay safety. Options:

  • Extend the idempotent list: autoRetry: { idempotentMethods: ['GET', 'HEAD', 'OPTIONS', 'PUT', 'DELETE'] }
  • Opt in a single request: context: new HttpContext().set(MTE_RETRY_UNSAFE, true)
  • Disable entirely: autoRetry: false

Per-request controls (HttpContext)

import { MTE_BYPASS, MTE_ENCODE_TYPE, MTE_RETRY_UNSAFE } from 'mte-relay-angular';

// Force strict MTE encoding for one request (default is MKE):
this.http.post(url, body, { context: new HttpContext().set(MTE_ENCODE_TYPE, 'MTE') });

// Send one request to a relay origin WITHOUT the relay transport:
this.http.get(url, { context: new HttpContext().set(MTE_BYPASS, true) });

// Allow auto-retry for one non-idempotent request:
this.http.post(url, body, { context: new HttpContext().set(MTE_RETRY_UNSAFE, true) });

Streaming and server-sent events

Native EventSource cannot traverse the relay (relay traffic is binary POST frames). Two supported paths:

1. HttpClient streaming — decoded relay streams (MKE-progressive bodies, SSE passthrough) surface exactly like stock Angular streaming responses:

this.http.request('GET', url, {
  observe: 'events', responseType: 'text', reportProgress: true,
}).subscribe((event) => {
  if (event.type === HttpEventType.DownloadProgress) {
    console.log((event as HttpDownloadProgressEvent).partialText);
  }
});

2. MteEventSource — an Observable EventSource replacement with parsed events:

private sse = inject(MteEventSource);

this.sse.connect('https://relay.example.com/sse/counter').subscribe((event) => {
  console.log(event.type, event.data, event.id);
});
// Unsubscribe to close the stream (aborts the underlying relay request).

Caveats, by design (v1):

  • No automatic reconnection and no Last-Event-ID replay — resubscribe to reconnect.
  • Each active stream reserves one encoder/decoder pair for its lifetime. Many concurrent streams can exhaust the pool (maxPairs, default 15) and fail with MteRelayCapacityError. Budget maxPairs accordingly.

Error handling

All failures surface as standard HttpErrorResponse, so existing error interceptors keep working. For relay-specific failures, the typed engine error rides in .error:

import { MteRelayHttpError, MteRelayCapacityError, isRecoveredRelayError } from 'mte-relay-angular';

catchError((err: HttpErrorResponse) => {
  if (err.error instanceof MteRelayCapacityError) {
    // pair pool exhausted — back off and retry later
  }
  if (err.error instanceof MteRelayHttpError) {
    // err.status is the relay code (559–569);
    // err.error.repaired / .pairReplaced report the recovery already performed
  }
  ...
})

| Failure | status | .error | | --- | --- | --- | | Relay error 559–569 | the relay code | MteRelayHttpError (repaired/pairReplaced flags) | | Local MTE protocol failure | 0 | MteRelayProtocolError | | Pair pool exhausted | 0 | MteRelayCapacityError | | Upstream non-2xx | upstream status | decoded upstream body (stock behavior) |

Zones, zoneless, SSR

  • All engine work (WASM, pairing, keep-alive timers, stream pumps) runs outside the Angular zone — it never blocks ApplicationRef.isStable (SSR serialization, hydration) and never triggers gratuitous change detection.
  • Events are delivered back in the zone you subscribed from, so change detection works in zoneful apps; zoneless apps work naturally via observable emissions.
  • onPoolEvent and streamDiagnostics callbacks fire outside the zone.
  • SSR: the MTE engine is browser-only. During server rendering, relay-origin requests either go plaintext via the passthrough backend (ssr: 'passthrough', default — document this in your threat model) or fail fast (ssr: 'block'). The server-side passthrough is always fetch-based automatically.
  • eagerInit: true warms up WASM + license at startup via APP_INITIALIZER; a warm-up failure logs and falls back to lazy init rather than blocking bootstrap.

Testing your app

Use provideMteRelayTesting() from the secondary entry point — a pure bypass that composes with Angular's standard HTTP testing:

import { provideMteRelayTesting, createMteRelayHttpErrorResponse } from 'mte-relay-angular/testing';

TestBed.configureTestingModule({
  providers: [
    provideHttpClient(),
    provideHttpClientTesting(), // HttpTestingController works untouched
    provideMteRelayTesting(),
  ],
});

The WASM runtime is never loaded in tests (it cannot load under jsdom/Node). To test relay-failure handling, throw factory-built errors from your mocks:

httpSpy.get.and.returnValue(throwError(() =>
  createMteRelayHttpErrorResponse(562, { pairReplaced: true })));

Known limitations

  • No HttpEventType.UploadProgress for relayed requests. The engine MTE-encodes the entire request body before transmitting, so no progress exists during the encode phase; transmit-phase progress would be technically possible with an XHR transport but would report encoded (not original) byte counts, and is not currently implemented. This matches Angular 22+'s default FetchBackend, which also has no upload progress. Passthrough (non-relay) traffic still gets upload progress on the 'xhr' fallback.
  • MteEventSource v1: no auto-reconnect / Last-Event-ID (see above).
  • The engine has no teardown API yet — keep-alive timers run for the app's lifetime (they are outside the zone and harmless, but note it for tests/embedded scenarios).
  • Server v5 only.

Repository layout

projects/mte-relay-angular/       the library (ng-packagr, APF)
  src/lib/
    provide-mte-relay.ts          providers + ordering assertion + eager init
    config.ts                     config types, validation, origin matching
    context-tokens.ts             MTE_ENCODE_TYPE / MTE_BYPASS / MTE_RETRY_UNSAFE
    engine.service.ts             owns the MteRelayClient instance (zone-safe)
    backend/
      mte-relay-backend.ts        HttpBackend: route → relay | passthrough; retry
      request-adapter.ts          HttpRequest → engine input (HttpXhrBackend parity)
      response-adapter.ts         engine Response → HttpEvent sequence
      passthrough.ts              passthrough backend token
    sse/                          SseParser + MteEventSource
  testing/                        secondary entry point: bypass provider + error factories
projects/demo/                    dev harness app (see below)

Developing this library

npm install            # needs @eclypses registry access for the mte WASM package
npm run build          # ng-packagr build → dist/mte-relay-angular
npm run serve:demo     # demo app on http://localhost:4200
npm run pack           # build + npm pack tarball for local consumer testing

Local dev currently consumes mte-relay-browser via file:../mte-relay-browser (workspace sibling) because the MteRelayClient export ships in 5.0.0-beta.7, which is not yet published. After it is published, switch the root package.json dependency to the registry version.

To exercise the demo against a real stack: run a local MTE Relay v5 server (../mte-relay, e.g. Dockerfile-mrs) with its upstream pointed at jsonplaceholder (../jsonplaceholder), put a valid license + relay origin in projects/demo/src/main.ts / demo-config.ts, then npm run serve:demo.

The library is pinned to the Angular 17 toolchain on purpose: ng-packagr partial-Ivy output built on the lowest supported major is consumable by every later major. Do not raise the toolchain version without also raising the minimum supported Angular.

Versioning

Semantic versioning. The peer range for @angular/* widens with each Angular major after compatibility verification. See CHANGELOG.md.