aboutsummaryrefslogtreecommitdiffstats
path: root/node_modules/rxjs/src/internal/ajax/ajax.ts
diff options
context:
space:
mode:
authorPinapelz <yukais@pinapelz.com>2025-06-28 17:26:46 -0700
committerPinapelz <yukais@pinapelz.com>2025-06-28 17:43:56 -0700
commite4fa1e69e7ebfb627c7198fd1a9881e9327ec4d4 (patch)
tree06284a538a6008eca75051399e47db4e5d50301c /node_modules/rxjs/src/internal/ajax/ajax.ts
initial commit: scaffolding
Diffstat (limited to 'node_modules/rxjs/src/internal/ajax/ajax.ts')
-rw-r--r--node_modules/rxjs/src/internal/ajax/ajax.ts622
1 files changed, 622 insertions, 0 deletions
diff --git a/node_modules/rxjs/src/internal/ajax/ajax.ts b/node_modules/rxjs/src/internal/ajax/ajax.ts
new file mode 100644
index 0000000..b1628da
--- /dev/null
+++ b/node_modules/rxjs/src/internal/ajax/ajax.ts
@@ -0,0 +1,622 @@
+import { map } from '../operators/map';
+import { Observable } from '../Observable';
+import { AjaxConfig, AjaxRequest, AjaxDirection, ProgressEventType } from './types';
+import { AjaxResponse } from './AjaxResponse';
+import { AjaxTimeoutError, AjaxError } from './errors';
+
+export interface AjaxCreationMethod {
+ /**
+ * Creates an observable that will perform an AJAX request using the
+ * [XMLHttpRequest](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest) in
+ * global scope by default.
+ *
+ * This is the most configurable option, and the basis for all other AJAX calls in the library.
+ *
+ * ## Example
+ *
+ * ```ts
+ * import { ajax } from 'rxjs/ajax';
+ * import { map, catchError, of } from 'rxjs';
+ *
+ * const obs$ = ajax({
+ * method: 'GET',
+ * url: 'https://api.github.com/users?per_page=5',
+ * responseType: 'json'
+ * }).pipe(
+ * map(userResponse => console.log('users: ', userResponse)),
+ * catchError(error => {
+ * console.log('error: ', error);
+ * return of(error);
+ * })
+ * );
+ * ```
+ */
+ <T>(config: AjaxConfig): Observable<AjaxResponse<T>>;
+
+ /**
+ * Perform an HTTP GET using the
+ * [XMLHttpRequest](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest) in
+ * global scope. Defaults to a `responseType` of `"json"`.
+ *
+ * ## Example
+ *
+ * ```ts
+ * import { ajax } from 'rxjs/ajax';
+ * import { map, catchError, of } from 'rxjs';
+ *
+ * const obs$ = ajax('https://api.github.com/users?per_page=5').pipe(
+ * map(userResponse => console.log('users: ', userResponse)),
+ * catchError(error => {
+ * console.log('error: ', error);
+ * return of(error);
+ * })
+ * );
+ * ```
+ */
+ <T>(url: string): Observable<AjaxResponse<T>>;
+
+ /**
+ * Performs an HTTP GET using the
+ * [XMLHttpRequest](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest) in
+ * global scope by default, and a `responseType` of `"json"`.
+ *
+ * @param url The URL to get the resource from
+ * @param headers Optional headers. Case-Insensitive.
+ */
+ get<T>(url: string, headers?: Record<string, string>): Observable<AjaxResponse<T>>;
+
+ /**
+ * Performs an HTTP POST using the
+ * [XMLHttpRequest](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest) in
+ * global scope by default, and a `responseType` of `"json"`.
+ *
+ * Before sending the value passed to the `body` argument, it is automatically serialized
+ * based on the specified `responseType`. By default, a JavaScript object will be serialized
+ * to JSON. A `responseType` of `application/x-www-form-urlencoded` will flatten any provided
+ * dictionary object to a url-encoded string.
+ *
+ * @param url The URL to get the resource from
+ * @param body The content to send. The body is automatically serialized.
+ * @param headers Optional headers. Case-Insensitive.
+ */
+ post<T>(url: string, body?: any, headers?: Record<string, string>): Observable<AjaxResponse<T>>;
+
+ /**
+ * Performs an HTTP PUT using the
+ * [XMLHttpRequest](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest) in
+ * global scope by default, and a `responseType` of `"json"`.
+ *
+ * Before sending the value passed to the `body` argument, it is automatically serialized
+ * based on the specified `responseType`. By default, a JavaScript object will be serialized
+ * to JSON. A `responseType` of `application/x-www-form-urlencoded` will flatten any provided
+ * dictionary object to a url-encoded string.
+ *
+ * @param url The URL to get the resource from
+ * @param body The content to send. The body is automatically serialized.
+ * @param headers Optional headers. Case-Insensitive.
+ */
+ put<T>(url: string, body?: any, headers?: Record<string, string>): Observable<AjaxResponse<T>>;
+
+ /**
+ * Performs an HTTP PATCH using the
+ * [XMLHttpRequest](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest) in
+ * global scope by default, and a `responseType` of `"json"`.
+ *
+ * Before sending the value passed to the `body` argument, it is automatically serialized
+ * based on the specified `responseType`. By default, a JavaScript object will be serialized
+ * to JSON. A `responseType` of `application/x-www-form-urlencoded` will flatten any provided
+ * dictionary object to a url-encoded string.
+ *
+ * @param url The URL to get the resource from
+ * @param body The content to send. The body is automatically serialized.
+ * @param headers Optional headers. Case-Insensitive.
+ */
+ patch<T>(url: string, body?: any, headers?: Record<string, string>): Observable<AjaxResponse<T>>;
+
+ /**
+ * Performs an HTTP DELETE using the
+ * [XMLHttpRequest](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest) in
+ * global scope by default, and a `responseType` of `"json"`.
+ *
+ * @param url The URL to get the resource from
+ * @param headers Optional headers. Case-Insensitive.
+ */
+ delete<T>(url: string, headers?: Record<string, string>): Observable<AjaxResponse<T>>;
+
+ /**
+ * Performs an HTTP GET using the
+ * [XMLHttpRequest](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest) in
+ * global scope by default, and returns the hydrated JavaScript object from the
+ * response.
+ *
+ * @param url The URL to get the resource from
+ * @param headers Optional headers. Case-Insensitive.
+ */
+ getJSON<T>(url: string, headers?: Record<string, string>): Observable<T>;
+}
+
+function ajaxGet<T>(url: string, headers?: Record<string, string>): Observable<AjaxResponse<T>> {
+ return ajax({ method: 'GET', url, headers });
+}
+
+function ajaxPost<T>(url: string, body?: any, headers?: Record<string, string>): Observable<AjaxResponse<T>> {
+ return ajax({ method: 'POST', url, body, headers });
+}
+
+function ajaxDelete<T>(url: string, headers?: Record<string, string>): Observable<AjaxResponse<T>> {
+ return ajax({ method: 'DELETE', url, headers });
+}
+
+function ajaxPut<T>(url: string, body?: any, headers?: Record<string, string>): Observable<AjaxResponse<T>> {
+ return ajax({ method: 'PUT', url, body, headers });
+}
+
+function ajaxPatch<T>(url: string, body?: any, headers?: Record<string, string>): Observable<AjaxResponse<T>> {
+ return ajax({ method: 'PATCH', url, body, headers });
+}
+
+const mapResponse = map((x: AjaxResponse<any>) => x.response);
+
+function ajaxGetJSON<T>(url: string, headers?: Record<string, string>): Observable<T> {
+ return mapResponse(
+ ajax<T>({
+ method: 'GET',
+ url,
+ headers,
+ })
+ );
+}
+
+/**
+ * There is an ajax operator on the Rx object.
+ *
+ * It creates an observable for an Ajax request with either a request object with
+ * url, headers, etc or a string for a URL.
+ *
+ * ## Examples
+ *
+ * Using `ajax()` to fetch the response object that is being returned from API
+ *
+ * ```ts
+ * import { ajax } from 'rxjs/ajax';
+ * import { map, catchError, of } from 'rxjs';
+ *
+ * const obs$ = ajax('https://api.github.com/users?per_page=5').pipe(
+ * map(userResponse => console.log('users: ', userResponse)),
+ * catchError(error => {
+ * console.log('error: ', error);
+ * return of(error);
+ * })
+ * );
+ *
+ * obs$.subscribe({
+ * next: value => console.log(value),
+ * error: err => console.log(err)
+ * });
+ * ```
+ *
+ * Using `ajax.getJSON()` to fetch data from API
+ *
+ * ```ts
+ * import { ajax } from 'rxjs/ajax';
+ * import { map, catchError, of } from 'rxjs';
+ *
+ * const obs$ = ajax.getJSON('https://api.github.com/users?per_page=5').pipe(
+ * map(userResponse => console.log('users: ', userResponse)),
+ * catchError(error => {
+ * console.log('error: ', error);
+ * return of(error);
+ * })
+ * );
+ *
+ * obs$.subscribe({
+ * next: value => console.log(value),
+ * error: err => console.log(err)
+ * });
+ * ```
+ *
+ * Using `ajax()` with object as argument and method POST with a two seconds delay
+ *
+ * ```ts
+ * import { ajax } from 'rxjs/ajax';
+ * import { map, catchError, of } from 'rxjs';
+ *
+ * const users = ajax({
+ * url: 'https://httpbin.org/delay/2',
+ * method: 'POST',
+ * headers: {
+ * 'Content-Type': 'application/json',
+ * 'rxjs-custom-header': 'Rxjs'
+ * },
+ * body: {
+ * rxjs: 'Hello World!'
+ * }
+ * }).pipe(
+ * map(response => console.log('response: ', response)),
+ * catchError(error => {
+ * console.log('error: ', error);
+ * return of(error);
+ * })
+ * );
+ *
+ * users.subscribe({
+ * next: value => console.log(value),
+ * error: err => console.log(err)
+ * });
+ * ```
+ *
+ * Using `ajax()` to fetch. An error object that is being returned from the request
+ *
+ * ```ts
+ * import { ajax } from 'rxjs/ajax';
+ * import { map, catchError, of } from 'rxjs';
+ *
+ * const obs$ = ajax('https://api.github.com/404').pipe(
+ * map(userResponse => console.log('users: ', userResponse)),
+ * catchError(error => {
+ * console.log('error: ', error);
+ * return of(error);
+ * })
+ * );
+ *
+ * obs$.subscribe({
+ * next: value => console.log(value),
+ * error: err => console.log(err)
+ * });
+ * ```
+ */
+export const ajax: AjaxCreationMethod = (() => {
+ const create = <T>(urlOrConfig: string | AjaxConfig) => {
+ const config: AjaxConfig =
+ typeof urlOrConfig === 'string'
+ ? {
+ url: urlOrConfig,
+ }
+ : urlOrConfig;
+ return fromAjax<T>(config);
+ };
+
+ create.get = ajaxGet;
+ create.post = ajaxPost;
+ create.delete = ajaxDelete;
+ create.put = ajaxPut;
+ create.patch = ajaxPatch;
+ create.getJSON = ajaxGetJSON;
+
+ return create;
+})();
+
+const UPLOAD = 'upload';
+const DOWNLOAD = 'download';
+const LOADSTART = 'loadstart';
+const PROGRESS = 'progress';
+const LOAD = 'load';
+
+export function fromAjax<T>(init: AjaxConfig): Observable<AjaxResponse<T>> {
+ return new Observable((destination) => {
+ const config = {
+ // Defaults
+ async: true,
+ crossDomain: false,
+ withCredentials: false,
+ method: 'GET',
+ timeout: 0,
+ responseType: 'json' as XMLHttpRequestResponseType,
+
+ ...init,
+ };
+
+ const { queryParams, body: configuredBody, headers: configuredHeaders } = config;
+
+ let url = config.url;
+ if (!url) {
+ throw new TypeError('url is required');
+ }
+
+ if (queryParams) {
+ let searchParams: URLSearchParams;
+ if (url.includes('?')) {
+ // If the user has passed a URL with a querystring already in it,
+ // we need to combine them. So we're going to split it. There
+ // should only be one `?` in a valid URL.
+ const parts = url.split('?');
+ if (2 < parts.length) {
+ throw new TypeError('invalid url');
+ }
+ // Add the passed queryParams to the params already in the url provided.
+ searchParams = new URLSearchParams(parts[1]);
+ // queryParams is converted to any because the runtime is *much* more permissive than
+ // the types are.
+ new URLSearchParams(queryParams as any).forEach((value, key) => searchParams.set(key, value));
+ // We have to do string concatenation here, because `new URL(url)` does
+ // not like relative URLs like `/this` without a base url, which we can't
+ // specify, nor can we assume `location` will exist, because of node.
+ url = parts[0] + '?' + searchParams;
+ } else {
+ // There is no preexisting querystring, so we can just use URLSearchParams
+ // to convert the passed queryParams into the proper format and encodings.
+ // queryParams is converted to any because the runtime is *much* more permissive than
+ // the types are.
+ searchParams = new URLSearchParams(queryParams as any);
+ url = url + '?' + searchParams;
+ }
+ }
+
+ // Normalize the headers. We're going to make them all lowercase, since
+ // Headers are case insensitive by design. This makes it easier to verify
+ // that we aren't setting or sending duplicates.
+ const headers: Record<string, any> = {};
+ if (configuredHeaders) {
+ for (const key in configuredHeaders) {
+ if (configuredHeaders.hasOwnProperty(key)) {
+ headers[key.toLowerCase()] = configuredHeaders[key];
+ }
+ }
+ }
+
+ const crossDomain = config.crossDomain;
+
+ // Set the x-requested-with header. This is a non-standard header that has
+ // come to be a de facto standard for HTTP requests sent by libraries and frameworks
+ // using XHR. However, we DO NOT want to set this if it is a CORS request. This is
+ // because sometimes this header can cause issues with CORS. To be clear,
+ // None of this is necessary, it's only being set because it's "the thing libraries do"
+ // Starting back as far as JQuery, and continuing with other libraries such as Angular 1,
+ // Axios, et al.
+ if (!crossDomain && !('x-requested-with' in headers)) {
+ headers['x-requested-with'] = 'XMLHttpRequest';
+ }
+
+ // Allow users to provide their XSRF cookie name and the name of a custom header to use to
+ // send the cookie.
+ const { withCredentials, xsrfCookieName, xsrfHeaderName } = config;
+ if ((withCredentials || !crossDomain) && xsrfCookieName && xsrfHeaderName) {
+ const xsrfCookie = document?.cookie.match(new RegExp(`(^|;\\s*)(${xsrfCookieName})=([^;]*)`))?.pop() ?? '';
+ if (xsrfCookie) {
+ headers[xsrfHeaderName] = xsrfCookie;
+ }
+ }
+
+ // Examine the body and determine whether or not to serialize it
+ // and set the content-type in `headers`, if we're able.
+ const body = extractContentTypeAndMaybeSerializeBody(configuredBody, headers);
+
+ // The final request settings.
+ const _request: Readonly<AjaxRequest> = {
+ ...config,
+
+ // Set values we ensured above
+ url,
+ headers,
+ body,
+ };
+
+ let xhr: XMLHttpRequest;
+
+ // Create our XHR so we can get started.
+ xhr = init.createXHR ? init.createXHR() : new XMLHttpRequest();
+
+ {
+ ///////////////////////////////////////////////////
+ // set up the events before open XHR
+ // https://developer.mozilla.org/en/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest
+ // You need to add the event listeners before calling open() on the request.
+ // Otherwise the progress events will not fire.
+ ///////////////////////////////////////////////////
+
+ const { progressSubscriber, includeDownloadProgress = false, includeUploadProgress = false } = init;
+
+ /**
+ * Wires up an event handler that will emit an error when fired. Used
+ * for timeout and abort events.
+ * @param type The type of event we're treating as an error
+ * @param errorFactory A function that creates the type of error to emit.
+ */
+ const addErrorEvent = (type: string, errorFactory: () => any) => {
+ xhr.addEventListener(type, () => {
+ const error = errorFactory();
+ progressSubscriber?.error?.(error);
+ destination.error(error);
+ });
+ };
+
+ // If the request times out, handle errors appropriately.
+ addErrorEvent('timeout', () => new AjaxTimeoutError(xhr, _request));
+
+ // If the request aborts (due to a network disconnection or the like), handle
+ // it as an error.
+ addErrorEvent('abort', () => new AjaxError('aborted', xhr, _request));
+
+ /**
+ * Creates a response object to emit to the consumer.
+ * @param direction the direction related to the event. Prefixes the event `type` in the
+ * `AjaxResponse` object with "upload_" for events related to uploading and "download_"
+ * for events related to downloading.
+ * @param event the actual event object.
+ */
+ const createResponse = (direction: AjaxDirection, event: ProgressEvent) =>
+ new AjaxResponse<T>(event, xhr, _request, `${direction}_${event.type as ProgressEventType}` as const);
+
+ /**
+ * Wires up an event handler that emits a Response object to the consumer, used for
+ * all events that emit responses, loadstart, progress, and load.
+ * Note that download load handling is a bit different below, because it has
+ * more logic it needs to run.
+ * @param target The target, either the XHR itself or the Upload object.
+ * @param type The type of event to wire up
+ * @param direction The "direction", used to prefix the response object that is
+ * emitted to the consumer. (e.g. "upload_" or "download_")
+ */
+ const addProgressEvent = (target: any, type: string, direction: AjaxDirection) => {
+ target.addEventListener(type, (event: ProgressEvent) => {
+ destination.next(createResponse(direction, event));
+ });
+ };
+
+ if (includeUploadProgress) {
+ [LOADSTART, PROGRESS, LOAD].forEach((type) => addProgressEvent(xhr.upload, type, UPLOAD));
+ }
+
+ if (progressSubscriber) {
+ [LOADSTART, PROGRESS].forEach((type) => xhr.upload.addEventListener(type, (e: any) => progressSubscriber?.next?.(e)));
+ }
+
+ if (includeDownloadProgress) {
+ [LOADSTART, PROGRESS].forEach((type) => addProgressEvent(xhr, type, DOWNLOAD));
+ }
+
+ const emitError = (status?: number) => {
+ const msg = 'ajax error' + (status ? ' ' + status : '');
+ destination.error(new AjaxError(msg, xhr, _request));
+ };
+
+ xhr.addEventListener('error', (e) => {
+ progressSubscriber?.error?.(e);
+ emitError();
+ });
+
+ xhr.addEventListener(LOAD, (event) => {
+ const { status } = xhr;
+ // 4xx and 5xx should error (https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html)
+ if (status < 400) {
+ progressSubscriber?.complete?.();
+
+ let response: AjaxResponse<T>;
+ try {
+ // This can throw in IE, because we end up needing to do a JSON.parse
+ // of the response in some cases to produce object we'd expect from
+ // modern browsers.
+ response = createResponse(DOWNLOAD, event);
+ } catch (err) {
+ destination.error(err);
+ return;
+ }
+
+ destination.next(response);
+ destination.complete();
+ } else {
+ progressSubscriber?.error?.(event);
+ emitError(status);
+ }
+ });
+ }
+
+ const { user, method, async } = _request;
+ // open XHR
+ if (user) {
+ xhr.open(method, url, async, user, _request.password);
+ } else {
+ xhr.open(method, url, async);
+ }
+
+ // timeout, responseType and withCredentials can be set once the XHR is open
+ if (async) {
+ xhr.timeout = _request.timeout;
+ xhr.responseType = _request.responseType;
+ }
+
+ if ('withCredentials' in xhr) {
+ xhr.withCredentials = _request.withCredentials;
+ }
+
+ // set headers
+ for (const key in headers) {
+ if (headers.hasOwnProperty(key)) {
+ xhr.setRequestHeader(key, headers[key]);
+ }
+ }
+
+ // finally send the request
+ if (body) {
+ xhr.send(body);
+ } else {
+ xhr.send();
+ }
+
+ return () => {
+ if (xhr && xhr.readyState !== 4 /*XHR done*/) {
+ xhr.abort();
+ }
+ };
+ });
+}
+
+/**
+ * Examines the body to determine if we need to serialize it for them or not.
+ * If the body is a type that XHR handles natively, we just allow it through,
+ * otherwise, if the body is something that *we* can serialize for the user,
+ * we will serialize it, and attempt to set the `content-type` header, if it's
+ * not already set.
+ * @param body The body passed in by the user
+ * @param headers The normalized headers
+ */
+function extractContentTypeAndMaybeSerializeBody(body: any, headers: Record<string, string>) {
+ if (
+ !body ||
+ typeof body === 'string' ||
+ isFormData(body) ||
+ isURLSearchParams(body) ||
+ isArrayBuffer(body) ||
+ isFile(body) ||
+ isBlob(body) ||
+ isReadableStream(body)
+ ) {
+ // The XHR instance itself can handle serializing these, and set the content-type for us
+ // so we don't need to do that. https://xhr.spec.whatwg.org/#the-send()-method
+ return body;
+ }
+
+ if (isArrayBufferView(body)) {
+ // This is a typed array (e.g. Float32Array or Uint8Array), or a DataView.
+ // XHR can handle this one too: https://fetch.spec.whatwg.org/#concept-bodyinit-extract
+ return body.buffer;
+ }
+
+ if (typeof body === 'object') {
+ // If we have made it here, this is an object, probably a POJO, and we'll try
+ // to serialize it for them. If this doesn't work, it will throw, obviously, which
+ // is okay. The workaround for users would be to manually set the body to their own
+ // serialized string (accounting for circular references or whatever), then set
+ // the content-type manually as well.
+ headers['content-type'] = headers['content-type'] ?? 'application/json;charset=utf-8';
+ return JSON.stringify(body);
+ }
+
+ // If we've gotten past everything above, this is something we don't quite know how to
+ // handle. Throw an error. This will be caught and emitted from the observable.
+ throw new TypeError('Unknown body type');
+}
+
+const _toString = Object.prototype.toString;
+
+function toStringCheck(obj: any, name: string): boolean {
+ return _toString.call(obj) === `[object ${name}]`;
+}
+
+function isArrayBuffer(body: any): body is ArrayBuffer {
+ return toStringCheck(body, 'ArrayBuffer');
+}
+
+function isFile(body: any): body is File {
+ return toStringCheck(body, 'File');
+}
+
+function isBlob(body: any): body is Blob {
+ return toStringCheck(body, 'Blob');
+}
+
+function isArrayBufferView(body: any): body is ArrayBufferView {
+ return typeof ArrayBuffer !== 'undefined' && ArrayBuffer.isView(body);
+}
+
+function isFormData(body: any): body is FormData {
+ return typeof FormData !== 'undefined' && body instanceof FormData;
+}
+
+function isURLSearchParams(body: any): body is URLSearchParams {
+ return typeof URLSearchParams !== 'undefined' && body instanceof URLSearchParams;
+}
+
+function isReadableStream(body: any): body is ReadableStream {
+ return typeof ReadableStream !== 'undefined' && body instanceof ReadableStream;
+}
send patches to the email below
yukais@pinapelz.com
include the subject [PATCH repo_name]
pinapelz.com
homepage