UNPKG

@procore/core-http

Version:

A HTTP Client

192 lines (185 loc) • 5.33 kB
// src/index.ts import { isJsonifiable, isResponseError as isResponseError2, isValidationError } from "up-fetch"; // src/authPlugin.ts import { isResponseError } from "up-fetch"; import { SystemEvents, SystemEventNames } from "@procore/web-sdk-events"; var systemEvents = new SystemEvents("core-http"); function detectAndPublishStepUp(response, request2) { if (response.status === 401 && response.headers.get("www-authenticate")) { const eventData = { type: SystemEventNames.NETWORKING_STEPUP_REQUIRED, url: request2?.url, status: response.status, wwwAuthenticate: response.headers.get("www-authenticate") || "", timestamp: Date.now() }; systemEvents.publish(SystemEventNames.NETWORKING_EXCEPTION, eventData); } } var authPlugin = { onSuccess: (response, request2) => { if (response instanceof Response) { detectAndPublishStepUp(response, request2); } }, onError: (error, request2) => { if (isResponseError(error)) { const response = error.response; if (response instanceof Response) { detectAndPublishStepUp(response, request2); } } } }; // src/create-client.ts import { SystemEvents as SystemEvents3 } from "@procore/web-sdk-events"; import { up } from "up-fetch"; // src/utils.ts function collectFunctions(plugins, fnName) { const functions = []; for (const plugin of plugins) { if (plugin[fnName]) { functions.push(plugin[fnName]); } } return functions; } function createFunctionChain(fns) { return fns.length === 0 ? void 0 : fns.length === 1 ? fns[0] : async (...args) => { for (const fn of fns) { await fn(...args); } }; } // src/error-reporter-plugin.ts import { SystemEventNames as SystemEventNames2 } from "@procore/web-sdk-events"; function normalizeError(error) { if (error instanceof Error) { return { name: error.name, message: error.message, stack: error.stack }; } return { name: "UnknownError", message: String(error), stack: void 0 }; } function createErrorReporterPlugin(systemEvents2, errorReportingApiKey) { return { onError: (error) => { if (!errorReportingApiKey) return; systemEvents2.publish(SystemEventNames2.UNCAUGHT_EXCEPTION, { errorReportingApiKey, error: normalizeError(error) }); } }; } // src/create-client.ts var SERVICE_NAME = "core-http"; function getCSRFToken() { const token = document.cookie.match("(^|;)\\s*csrf_token\\s*=\\s*([^;]+)"); if (token?.[2] && token[2] !== "") { return decodeURIComponent(token[2]); } return void 0; } function resolveDefaultErrorHandling(options, fallback) { if (options && typeof options === "object" && "defaultErrorHandling" in options) { const v = options.defaultErrorHandling; if (typeof v === "boolean") return v; } return fallback; } function createClient({ // This grabs a reference to window.fetch on each request, rather than hard-coding it at initialization time. fetchFn = (input, options) => fetch(input, options), defaults = () => ({}), plugins = [], defaultErrorHandling = true, errorReportingApiKey, systemEvents: systemEvents2 }) { const upFetch = up(fetchFn, async (input, fetcherOpts, ctx) => { const defaultOptions = await defaults(input, fetcherOpts, ctx); const shouldReportErrors = resolveDefaultErrorHandling(fetcherOpts, defaultErrorHandling) && Boolean(errorReportingApiKey); const allPlugins = [ defaultOptions, ...plugins, ...shouldReportErrors ? [ createErrorReporterPlugin( systemEvents2 ?? new SystemEvents3(SERVICE_NAME), errorReportingApiKey ) ] : [] ]; const onError = createFunctionChain(collectFunctions(allPlugins, "onError")); const onRequest = createFunctionChain( collectFunctions(allPlugins, "onRequest") ); const onSuccess = createFunctionChain( collectFunctions(allPlugins, "onSuccess") ); const csrfToken = getCSRFToken(); return { credentials: "same-origin", mode: "same-origin", ...defaultOptions, headers: { ...defaultOptions.headers, ...csrfToken ? { "X-CSRF-TOKEN": csrfToken } : {} }, onError, onRequest, onSuccess }; }); return (input, options) => upFetch(input, options); } // src/request.ts function request(url, requestParams = {}) { const { signal, baseUrl, errorReportingApiKey = "", systemEvents: systemEvents2, ...options } = requestParams; return createClient({ errorReportingApiKey, ...systemEvents2 && { systemEvents: systemEvents2 }, defaults: () => ({ ...baseUrl ? { baseUrl: new URL(baseUrl, document.baseURI).toString() } : {}, // Skips the default parseResponse and reject so that // the function behaves like default fetch. parseResponse(response) { return response; }, reject: () => false }) })(url, { ...options, signal: signal ?? void 0 }); } function requestJSON(url, requestParams = {}) { return request(url, requestParams).then( (response) => response.json() ); } export { authPlugin, createClient, isJsonifiable, isResponseError2 as isResponseError, isValidationError, request, requestJSON };