alistair
Version:
111 lines (104 loc) • 3.7 kB
JavaScript
;
// Copyright 2026 Alistair Smith https://github.com/alii/alistair
// package.json
var version = "1.17.0";
// src/constants.ts
var IS_BROWSER = typeof window !== "undefined";
// src/http/utils.ts
function join(base, path) {
const finalBase = base.charCodeAt(base.length - 1) === 47 ? base : base + "/";
const finalPath = path.charCodeAt(0) === 47 ? path.substring(1) : path;
return finalBase + finalPath;
}
function isBodyInit(body) {
return typeof body === "string" || body instanceof ReadableStream || body instanceof Blob || body instanceof FormData || body instanceof URLSearchParams || typeof ArrayBuffer !== "undefined" && (ArrayBuffer.isView(body) || body instanceof ArrayBuffer);
}
function streamToBody(stream) {
return new Response(stream).text();
}
// src/http/client.ts
var HTTPClientError = class _HTTPClientError extends Error {
constructor(count, request, response) {
super(_HTTPClientError.getErrorMessage(count, response));
this.count = count;
this.request = request;
this.response = response;
}
static getErrorMessage = (count, response) => `Request failed after ${count} attempts with status ${response.status}`;
};
var defaultLifecycle = {
before: async (req) => req,
failure: async (count, request, response) => {
throw new HTTPClientError(count, request, response);
}
};
function createHTTPClient(rootOptions) {
const options = {
transform: (res) => res.json(),
...rootOptions,
lifecycle: {
...defaultLifecycle,
...rootOptions.lifecycle
}
};
const run = async (count, request) => {
const response = await fetch(request);
if (!response.ok) {
const failure = await options.lifecycle.failure(count, request, response);
if (failure instanceof Request) {
return run(count + 1, failure);
} else {
throw new HTTPClientError(count, request, response);
}
}
return response;
};
const createMethod = (method) => {
return async (path, config = {}) => {
const { body: userBody = undefined, headers: userHeaders, ...userInit } = config;
const headers = new Headers(userHeaders);
const init = {
...userInit,
headers,
method
};
if (!IS_BROWSER && !headers.has("User-Agent")) {
headers.set("User-Agent", `alistair-http/${version}`);
}
if (userBody !== undefined) {
const setContentType = headers.get("Content-Type");
if (setContentType === null || setContentType === "application/json") {
if (setContentType === null) {
headers.set("Content-Type", "application/json");
}
init.body = JSON.stringify(userBody);
} else if (userBody === null || isBodyInit(userBody)) {
init.body = userBody;
} else {
throw new Error(
"Cannot serialize body when a Content-Type header was set. Maybe you forgot to stringify?"
);
}
}
const request = await options.lifecycle.before(new Request(join(options.base, path), init));
const response = await run(1, request);
const transformed = await options.transform(response);
return transformed;
};
};
return {
get: createMethod("get"),
post: createMethod("post"),
put: createMethod("put"),
patch: createMethod("patch"),
delete: createMethod("delete"),
head: createMethod("head"),
options: createMethod("options")
};
}
exports.HTTPClientError = HTTPClientError;
exports.createHTTPClient = createHTTPClient;
exports.defaultLifecycle = defaultLifecycle;
exports.isBodyInit = isBodyInit;
exports.join = join;
exports.streamToBody = streamToBody;