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

@blockquote-web-components/ajax-provider

v1.6.0

Published

A modern architecture for building composable and reusable Web Components

Readme

AJAX Provider Documentation

A robust, event-driven HTTP request service that bridges the XMLHttpRequest Web API with RxJS 9 cold observables and native Promises.

Table of Contents

Overview

AjaxProvider extends EventTarget (via AjaxProviderMixin) to deliver an event-based, observable HTTP client for modern web components and applications. It allows developers to choose between an async/await Promise interface or a fully reactive RxJS 9 Observable stream.

Architecture: Why XMLHttpRequest + RxJS 9?

Modern web development frequently defaults to fetch(), but XMLHttpRequest (XHR) paired with RxJS 9 provides unique capabilities essential for robust enterprise web applications:

  1. Upload Progress Tracking: Unlike standard fetch(), XMLHttpRequest exposes xhr.upload.onprogress. This allows AjaxProvider to report real-time percentage updates when uploading large files, FormData, or ArrayBuffer payloads.
  2. Immediate Socket Teardown: Calling xhr.abort() instantly terminates the underlying TCP/HTTP connection, guaranteeing zero resource waste upon cancellation.
  3. Cold Observable Execution: Requests created via request$() are lazy. The network request is deferred until a subscriber calls .subscribe(). Re-subscribing re-executes the request cleanly.
  4. Automatic Teardown: Unsubscribing from a request$() observable automatically triggers xhr.abort(), preventing memory leaks and race conditions.

API Selection: Promises vs Observables

AjaxProvider provides two primary execution methods depending on your application needs:

| Feature | generateRequest() (Promise) | request$() (Observable) | | :--- | :--- | :--- | | Return Type | Promise<AjaxResponse> | Observable<AjaxResponse> | | Primary Use Case | Standard single-response async/await flows | Complex reactive flows, streaming & cancellation | | RxJS Operators | N/A | Full support (retry, switchMap, debounceTime) | | Cancellation | Not directly cancelable | Cancelable via unsubscribe() or AbortSignal | | Progress Events | Dispatches ajaxprogress events | Dispatches ajaxprogress & emits progress items | | Internal Engine | Wraps request$() via lastValueFrom() | Native RxJS fromXMLHttpRequest stream |

Note: generateRequest() internally calls lastValueFrom(this.request$()). Both APIs share the exact same underlying RxJS pipeline.

Installation

npm install \@blockquote-web-components/ajax-provider

Usage Examples

1. Promise-based Request (generateRequest)

Ideal for straightforward fetch operations using async/await:

import { AjaxProvider } from '\@blockquote-web-components/ajax-provider';

const provider = new AjaxProvider({
  url: 'https://httpbingo.org',
  path: 'get',
  queryParams: { search: 'lit-element' },
});

try {
  const response = await provider.generateRequest();
  console.log('Response status:', response.status);
  console.log('Response body:', response.response);
} catch (error) {
  console.error('Request failed:', error.message);
}

2. Reactive Request with Retry (request$)

Leverage RxJS operators to handle automatic retries on server failures (e.g., HTTP 500):

import { pipe } from 'rxjs/pipe';
import { retry } from 'rxjs/retry';
import { AjaxProvider } from '\@blockquote-web-components/ajax-provider';

const provider = new AjaxProvider({
  url: 'https://httpbingo.org',
  path: 'status/500',
});

// Automatically retry up to 2 additional times before erroring out
provider
  .request$()
  [pipe]((values) => values[retry]({ count: 2 }))
  .subscribe({
    next: (res) => console.log('Success:', res),
    error: (err) => console.error('Failed after 3 attempts:', err),
  });

3. Progress Monitoring (Upload & Download)

Track progress percentages during file uploads or large payload downloads:

const formData = new FormData();
formData.append('file', fileInput.files[0]);

const provider = new AjaxProvider({
  url: 'https://httpbingo.org',
  path: 'post',
  method: 'POST',
  body: formData,
  includeUploadProgress: true,
  includeDownloadProgress: true,
});

provider.addEventListener('ajaxprogress', ({ detail }) => {
  const { type, loaded, total } = detail;
  const percent = total ? Math.round((loaded / total) * 100) : 0;
  console.log(`[${type}] ${percent}% (${loaded}/${total} bytes)`);
});

await provider.generateRequest();

4. Request Cancellation

Cancel an in-flight HTTP request cleanly using AbortController:

const controller = new AbortController();
const provider = new AjaxProvider({
  url: 'https://httpbingo.org',
  path: 'drip',
  queryParams: 'duration=5&delay=0&numbytes=1024',
});

provider.request$().subscribe({
  next: (res) => console.log('Data:', res),
  error: (err) => console.log('Aborted or failed:', err),
}, { signal: controller.signal });

// Cancel the request after 1 second
setTimeout(() => controller.abort(), 1000);

5. Event-Driven Request Chaining

Chain dependent requests using EventTarget.when() and RxJS switchMap:

import { pipe } from 'rxjs/pipe';
import { switchMap } from 'rxjs/switch-map';

const requestAuth = new AjaxProvider({ url: 'https://httpbingo.org', path: 'uuid' });
const requestData = new AjaxProvider({ url: 'https://httpbingo.org' });

requestAuth
  .when('ajaxresponse')
  [pipe]((values) =>
    values[switchMap](({ detail }) => {
      requestData.path = `anything/${detail.response.uuid}`;
      return requestData.request$();
    })
  )
  .subscribe({
    next: (result) => console.log('Chained response:', result),
  });

requestAuth.generateRequest();

Lifecycle Events

AjaxProvider dispatches standard custom events on its EventTarget context during execution (identically for both generateRequest() and request$()):

  • ajaxpresend: Fired right before the request is opened.
  • ajaxprogress: Fired when upload or download progress events occur.
  • ajaxresponse: Fired when a successful response (status < 400) is received.
  • ajaxresponseend: Fired after a successful response completes.
  • ajaxerror: Fired when a request error or HTTP status >= 400 occurs.
  • ajaxerrorend: Fired after error handling finishes.

API Reference

Request Configuration Options (AjaxProviderConfig)

  • url: The base URL for the AJAX request. (string)
  • path: Path to append to the base URL (e.g. 'users'). (string)
  • method: The HTTP request method (e.g., 'GET', 'POST', 'PUT', 'DELETE', 'PATCH'). Default: 'GET'. (string)
  • headers: Custom headers for the request. Default headers:
    • Accept: 'application/json, text/plain, *\/*; q=0.01'
    • Content-Type: 'application/json'
  • body: Request payload (JSON object, FormData, string, Blob, ArrayBuffer, etc.).
  • queryParams: Query parameters (object or string) appended to the request URL. (Object|string|undefined)
  • async: Whether to send the request asynchronously. Default: true. (boolean)
  • timeout: Request timeout in milliseconds. Default: 0 (no timeout). (number)
  • user: Username for HTTP basic authentication. (string)
  • password: Password for HTTP basic authentication. (string)
  • withCredentials: Indicates whether to include cross-site credentials with the request. Default: false. (boolean)
  • xsrfCookieName: The name of the XSRF cookie. (string)
  • xsrfHeaderName: The name of the XSRF header. (string)
  • responseType: The response type ('json', 'text', 'blob', 'arraybuffer', 'document'). Default: 'json'. (string)
  • includeDownloadProgress: Indicates whether to include download progress events (ajaxprogress). Default: false. (boolean)
  • includeUploadProgress: Indicates whether to include upload progress events (ajaxprogress). Default: false. (boolean)
  • avoidBoundary: Set to true to stop delegating boundary management for multipart requests to the browser. Default: false. (boolean)
  • dispatchEventContext: The context for dispatching custom events. Default: this.
  • customEventPrefix: Custom event prefix for lifecycle events. Default: 'ajax'. (string)

Instance Properties

  • lastResponse: Stores the last AjaxResponse object received. (AjaxResponse|undefined)
  • lastError: Stores the last AjaxError object encountered. (AjaxError|undefined)

Methods

  • generateRequest(): Generates and sends the AJAX request, returning a Promise<AjaxResponse>.
  • request$(): Returns a cold RxJS Observable<AjaxResponse> that executes the AJAX request per subscription.

src/AjaxProvider.ts:

class: AjaxProvider

Mixins

| Name | Module | Package | | ------------------- | ------------------------- | ------- | | AjaxProviderMixin | /src/AjaxProviderMixin.js | |

Fields

| Name | Privacy | Type | Default | Description | Inherited From | | ------------------------- | ------- | -------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------- | | dispatchEventContext | | {dispatchEvent(event: CustomEvent): void} \| undefined | this | The context for dispatching events. | AjaxProviderMixin | | lastResponse | | AjaxResponse<R> \| undefined | undefined | The last AJAX response object. | AjaxProviderMixin | | lastError | | AjaxError \| undefined | undefined | The last error object. | AjaxProviderMixin | | customEventPrefix | | string \| undefined | 'ajax' | A custom event prefix for events related to HTTP requests. | AjaxProviderMixin | | avoidBoundary | | boolean \| undefined | false | Set to `true` to stop delegating the use of boundaries for multipart requests to the browser. Only change this to `true` if you know what you are doing. | AjaxProviderMixin | | url | | string | '' | | AjaxProviderMixin | | path | | string | '' | | AjaxProviderMixin | | body | | unknown | undefined | | AjaxProviderMixin | | async | | boolean | true | | AjaxProviderMixin | | method | | string | 'GET' | | AjaxProviderMixin | | _headers | | Record<string, string> | { Accept: 'application/json, text/plain, */*; q=0.01', 'Content-Type': 'application/json', } | | AjaxProviderMixin | | headers | | Record<string, string> \| undefined | undefined | | AjaxProviderMixin | | timeout | | number | 0 | | AjaxProviderMixin | | user | | string | '' | | AjaxProviderMixin | | password | | string | '' | | AjaxProviderMixin | | withCredentials | | boolean | false | | AjaxProviderMixin | | xsrfCookieName | | string | '' | | AjaxProviderMixin | | xsrfHeaderName | | string | '' | | AjaxProviderMixin | | responseType | | string | '' | | AjaxProviderMixin | | queryParams | | unknown \| undefined | undefined | | AjaxProviderMixin | | includeDownloadProgress | | boolean | false | | AjaxProviderMixin | | includeUploadProgress | | boolean | false | | AjaxProviderMixin |

Methods

| Name | Privacy | Description | Parameters | Return | Inherited From | | --------------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------- | --------------------------------- | ----------------- | | _assignAjaxProviderConfig | | Assigns configuration options to the AjaxProvider instance. | config: AjaxProviderConfig | void | | | request$ | | Returns a cold Observable that performs the AJAX request on each subscription. Progress is reported through the `ajaxprogress` event. Errors are reported through `ajaxerror`/`ajaxerrorend` events before the observable errors. | | ColdObservable<AjaxResponse<R>> | AjaxProviderMixin | | generateRequest | | Generates and sends the AJAX request. | | Promise<AjaxResponse<R>> | AjaxProviderMixin |

Methods

| Name | Privacy | Description | Parameters | Return | Inherited From | | ----------------------- | ------- | --------------------------------------------------------------- | -------------------------- | -------- | ----------------- | | _assignAjaxRxjsConfig | private | Assigns the configuration settings for the AJAX request. | | Object | AjaxProviderMixin | | _joinUrlData | private | Joins the base URL and path to create the complete request URL. | | string | AjaxProviderMixin | | _joinHeaders | private | Joins the default headers with custom headers. | formData: unknown | Object | AjaxProviderMixin | | _dispatchEvent | private | Dispatches a custom event with the specified type and payload. | type: string, payload: * | void | AjaxProviderMixin |

Exports

| Kind | Name | Declaration | Module | Package | | ---- | -------------- | ------------ | ------------------- | ------- | | js | AjaxProvider | AjaxProvider | src/AjaxProvider.ts | |

src/AjaxProviderMixin.ts:

mixin: AjaxProviderMixin

Mixins

| Name | Module | Package | | ------------- | ------ | --------------------- | | dedupeMixin | | @open-wc/dedupe-mixin |

Parameters

| Name | Type | Default | Description | | ------ | ---- | ------- | ----------- | | Base | T | | |

Fields

| Name | Privacy | Type | Default | Description | Inherited From | | ------------------------- | ------- | -------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | ----------- | -------------- | | url | | string | '' | | | | path | | string | '' | | | | body | | unknown | undefined | | | | async | | boolean | true | | | | method | | string | 'GET' | | | | _headers | | Record<string, string> | { Accept: 'application/json, text/plain, */*; q=0.01', 'Content-Type': 'application/json', } | | | | headers | | Record<string, string> \| undefined | undefined | | | | timeout | | number | 0 | | | | user | | string | '' | | | | password | | string | '' | | | | withCredentials | | boolean | false | | | | xsrfCookieName | | string | '' | | | | xsrfHeaderName | | string | '' | | | | responseType | | string | '' | | | | queryParams | | unknown \| undefined | undefined | | | | includeDownloadProgress | | boolean | false | | | | includeUploadProgress | | boolean | false | | | | avoidBoundary | | boolean \| undefined | | | | | dispatchEventContext | | {dispatchEvent(event: CustomEvent): void} \| undefined | | | | | customEventPrefix | | string \| undefined | | | | | lastResponse | | AjaxResponse<R> \| undefined | | | | | lastError | | AjaxError \| undefined | | | |

Methods

| Name | Privacy | Description | Parameters | Return | Inherited From | | ----------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- | --------------------------------- | -------------- | | request$ | | Returns a cold Observable that performs the AJAX request on each subscription. Progress is reported through the `ajaxprogress` event. Errors are reported through `ajaxerror`/`ajaxerrorend` events before the observable errors. | | ColdObservable<AjaxResponse<R>> | | | generateRequest | | Generates and sends the AJAX request. | | Promise<AjaxResponse<R>> | |

Methods

| Name | Privacy | Description | Parameters | Return | Inherited From | | ----------------------- | ------- | --------------------------------------------------------------- | -------------------------- | -------- | -------------- | | _assignAjaxRxjsConfig | private | Assigns the configuration settings for the AJAX request. | | Object | | | _joinUrlData | private | Joins the base URL and path to create the complete request URL. | | string | | | _joinHeaders | private | Joins the default headers with custom headers. | formData: unknown | Object | | | _dispatchEvent | private | Dispatches a custom event with the specified type and payload. | type: string, payload: * | void | |

Exports

| Kind | Name | Declaration | Module | Package | | ---- | ------------------- | ----------------- | ------------------------ | ------- | | js | AjaxProviderMixin | AjaxProviderMixin | src/AjaxProviderMixin.ts | |

src/fromAjax.ts:

class: AjaxResponse

Fields

| Name | Privacy | Type | Default | Description | Inherited From | | ----------------- | ------- | ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | -------------- | | originalEvent | | Event | originalEvent | | | | xhr | | XMLHttpRequest | xhr | | | | request | | AjaxRequest | { url, headers, body, async: config.async, crossDomain: config.crossDomain, withCredentials: config.withCredentials, method: config.method, timeout: config.timeout, responseType: config.responseType as XMLHttpRequestResponseType, user: config.user, password: config.password, xsrfCookieName: config.xsrfCookieName, xsrfHeaderName: config.xsrfHeaderName, queryParams: config.queryParams, includeDownloadProgress: config.includeDownloadProgress, includeUploadProgress: config.includeUploadProgress, } | | | | type | | string | type | | | | status | | number | | | | | responseType | | string | | | | | responseHeaders | | Record<string, string> | | | | | response | | T | getXHRResponse(xhr) | | | | loaded | | number | | | | | total | | number | | | |

class: AjaxError

Fields

| Name | Privacy | Type | Default | Description | Inherited From | | -------------- | ------- | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | -------------- | | name | | string | 'AjaxError' | | | | xhr | | XMLHttpRequest | xhr | | | | request | | AjaxRequest | { url, headers, body, async: config.async, crossDomain: config.crossDomain, withCredentials: config.withCredentials, method: config.method, timeout: config.timeout, responseType: config.responseType as XMLHttpRequestResponseType, user: config.user, password: config.password, xsrfCookieName: config.xsrfCookieName, xsrfHeaderName: config.xsrfHeaderName, queryParams: config.queryParams, includeDownloadProgress: config.includeDownloadProgress, includeUploadProgress: config.includeUploadProgress, } | | | | status | | number | | | | | responseType | | string | | | | | response | | unknown | response | | |

class: AjaxTimeoutError

Fields

| Name | Privacy | Type | Default | Description | Inherited From | | -------------- | ------- | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | -------------- | | name | | string | 'AjaxTimeoutError' | | AjaxError | | xhr | | XMLHttpRequest | xhr | | AjaxError | | request | | AjaxRequest | { url, headers, body, async: config.async, crossDomain: config.crossDomain, withCredentials: config.withCredentials, method: config.method, timeout: config.timeout, responseType: config.responseType as XMLHttpRequestResponseType, user: config.user, password: config.password, xsrfCookieName: config.xsrfCookieName, xsrfHeaderName: config.xsrfHeaderName, queryParams: config.queryParams, includeDownloadProgress: config.includeDownloadProgress, includeUploadProgress: config.includeUploadProgress, } | | AjaxError | | status | | number | | | AjaxError | | responseType | | string | | | AjaxError | | response | | unknown | response | | AjaxError |

Variables

| Name | Description | Type | | --------- | ----------- | ------------------------ | | url | | | | headers | | Record<string, string> | | body | | |

Functions

| Name | Description | Parameters | Return | | -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------- | --------------------------------------- | | fromXMLHttpRequest | Creates an `XMLHttpRequest` observable that emits `AjaxResponse` values, replicating the behavior of the removed `rxjs/ajax` `fromAjax` factory using the Web Platform Observable (`ColdObservable`) model. | init: AjaxConfig | ColdObservable<AjaxResponse<unknown>> | | lastValueFrom | Resolves with the last value emitted by the observable before completion. | source$: Subscribable<T> | Promise<T> |

Exports

| Kind | Name | Declaration | Module | Package | | ---- | -------------------- | ------------------ | --------------- | ------- | | js | fromXMLHttpRequest | fromXMLHttpRequest | src/fromAjax.ts | | | js | lastValueFrom | lastValueFrom | src/fromAjax.ts | | | js | AjaxResponse | AjaxResponse | src/fromAjax.ts | | | js | AjaxError | AjaxError | src/fromAjax.ts | | | js | AjaxTimeoutError | AjaxTimeoutError | src/fromAjax.ts | |

src/index.ts:

Exports

| Kind | Name | Declaration | Module | Package | | ---- | ---------------------------- | -------------------------- | ---------------------- | ------- | | js | AjaxProvider | AjaxProvider | ./AjaxProvider.js | | | js | AjaxProviderConfig | AjaxProviderConfig | ./AjaxProvider.js | | | js | AjaxProviderMixin | AjaxProviderMixin | ./AjaxProviderMixin.js | | | js | AjaxProviderMixinInterface | AjaxProviderMixinInterface | ./AjaxProviderMixin.js | | | js | ColdObservable | ColdObservable | | rxjs | | js | AjaxConfig | AjaxConfig | ./fromAjax.js | | | js | AjaxResponse | AjaxResponse | ./fromAjax.js | | | js | AjaxError | AjaxError | ./fromAjax.js | |

src/utils.ts:

Variables

| Name | Description | Type | | ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---- | | isStandardBrowserEnv | Determines if the code is running in a standard browser environment. This function checks for specific conditions that indicate whether the code is running in a standard browser environment, allowing Axios to work in various environments like web workers, React Native, or NativeScript. | | | isStandardBrowserWebWorkerEnv | Determines if the code is running in a standard browser WebWorker environment. This function checks for specific conditions that indicate whether the code is running in a standard browser WebWorker environment. It takes into account the limitations of the `isStandardBrowserEnv` method when working with WebWorkers. | |

Functions

| Name | Description | Parameters | Return | | ----------------- | --------------------------------------------------------------------------- | ------------------------------------- | --------- | | isFormData | Determine if a value is a FormData | thing: * | boolean | | assignIfDefined | Utility function to assign a property to an object if the value is defined. | obj: Object, prop: string, value: * | void |

Exports

| Kind | Name | Declaration | Module | Package | | ---- | ------------------------------- | ----------------------------- | ------------ | ------- | | js | isStandardBrowserEnv | isStandardBrowserEnv | src/utils.ts | | | js | isStandardBrowserWebWorkerEnv | isStandardBrowserWebWorkerEnv | src/utils.ts | | | js | isFormData | isFormData | src/utils.ts | | | js | assignIfDefined | assignIfDefined | src/utils.ts | |