@sitecore-jss/sitecore-jss
Version:
This module is provided as a part of Sitecore JavaScript Rendering SDK. It contains the core JSS APIs (layout service) and utilities.
205 lines (204 loc) • 9.01 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.NativeDataFetcher = void 0;
const debug_1 = __importDefault(require("./debug"));
const timeout_promise_1 = __importDefault(require("./utils/timeout-promise"));
class NativeDataFetcher {
constructor(config = {}) {
this.config = config;
}
/**
* Implements a data fetcher.
* @param {string} url The URL to request (may include query string)
* @param {RequestInit} [options] Optional fetch options
* @returns {Promise<NativeDataFetcherResponse<T>>} response
*/
fetch(url_1) {
return __awaiter(this, arguments, void 0, function* (url, options = {}) {
var _a;
const _b = this.config, { debugger: debugOverride, fetch: fetchOverride } = _b, init = __rest(_b, ["debugger", "fetch"]);
const startTimestamp = Date.now();
const fetchImpl = fetchOverride || fetch;
const debug = debugOverride || debug_1.default.http;
const requestInit = this.getRequestInit(Object.assign(Object.assign({}, init), options));
const fetchWithOptionalTimeout = [fetchImpl(url, requestInit)];
if (init.timeout) {
this.abortTimeout = new timeout_promise_1.default(init.timeout);
fetchWithOptionalTimeout.push(this.abortTimeout.start);
}
debug('Request initiated: %o', Object.assign({ url, headers: this.extractDebugHeaders(requestInit.headers) }, requestInit));
try {
const response = yield Promise.race(fetchWithOptionalTimeout).then((res) => {
var _a;
(_a = this.abortTimeout) === null || _a === void 0 ? void 0 : _a.clear();
return res;
});
const respData = yield this.parseResponse(response, debug);
if (!response.ok) {
const error = this.createError(response, respData);
debug('Response error: %o', error.response);
throw error;
}
debug('Response in %dms: %o', Date.now() - startTimestamp, {
status: response.status,
statusText: response.statusText,
headers: this.extractDebugHeaders(response.headers),
url: response.url,
data: respData,
});
return {
data: respData,
status: response.status,
statusText: response.statusText,
headers: response.headers,
};
}
catch (error) {
(_a = this.abortTimeout) === null || _a === void 0 ? void 0 : _a.clear();
debug('Request failed: %o', error);
throw error;
}
});
}
/**
* Perform a GET request
* @param {string} url The URL to request (may include query string)
* @param {RequestInit} [options] Fetch options
* @returns {Promise<NativeDataFetcherResponse<T>>} response
*/
get(url_1) {
return __awaiter(this, arguments, void 0, function* (url, options = {}) {
return this.fetch(url, Object.assign({ method: 'GET' }, options));
});
}
/**
* Perform a POST request
* @param {string} url The URL to request (may include query string)
* @param {unknown} body The data to send with the request
* @param {RequestInit} [options] Fetch options
* @returns {Promise<NativeDataFetcherResponse<T>>} response
*/
post(url_1, body_1) {
return __awaiter(this, arguments, void 0, function* (url, body, options = {}) {
return this.fetch(url, Object.assign({ method: 'POST', body: JSON.stringify(body) }, options));
});
}
/**
* Perform a DELETE request
* @param {string} url The URL to request (may include query string)
* @param {RequestInit} [options] Fetch options
* @returns {Promise<NativeDataFetcherResponse<T>>} response
*/
delete(url_1) {
return __awaiter(this, arguments, void 0, function* (url, options = {}) {
return this.fetch(url, Object.assign({ method: 'DELETE' }, options));
});
}
/**
* Perform a PUT request
* @param {string} url The URL to request (may include query string)
* @param {unknown} body The data to send with the request
* @param {RequestInit} [options] Fetch options
* @returns {Promise<NativeDataFetcherResponse<T>>} response
*/
put(url_1, body_1) {
return __awaiter(this, arguments, void 0, function* (url, body, options = {}) {
return this.fetch(url, Object.assign({ method: 'PUT', body: JSON.stringify(body) }, options));
});
}
/**
* Perform a HEAD request
* @param {string} url The URL to request (may include query string)
* @param {RequestInit} [options] Fetch options
* @returns {Promise<NativeDataFetcherResponse<T>>} response
*/
head(url, options = {}) {
return this.fetch(url, Object.assign({ method: 'HEAD' }, options));
}
/**
* Determines settings for the request
* @param {RequestInit} init Custom settings for request
* @returns {RequestInit} The final request settings
*/
getRequestInit(init = {}) {
const headers = new Headers(init.headers);
if (!init.method) {
init.method = init.body ? 'POST' : 'GET';
}
init.headers = headers;
return init;
}
/**
* Safely extract all headers for debug logging
* @param {HeadersInit} incomingHeaders Incoming headers
* @returns Object with headers as key/value pairs
*/
extractDebugHeaders(incomingHeaders = {}) {
const headers = {};
if (typeof (incomingHeaders === null || incomingHeaders === void 0 ? void 0 : incomingHeaders.forEach) !== 'string' && incomingHeaders.forEach) {
incomingHeaders === null || incomingHeaders === void 0 ? void 0 : incomingHeaders.forEach((value, key) => {
headers[key] = value;
});
}
return headers;
}
/**
* Parses the response data.
* @param {Response} response - The fetch response object.
* @param {Function} debug - The debug logger function.
* @returns {Promise<unknown>} - The parsed response data.
*/
parseResponse(response, debug) {
return __awaiter(this, void 0, void 0, function* () {
const contentType = response.headers.get('Content-Type') || '';
try {
if (contentType.includes('application/json')) {
return yield response.json();
}
return yield response.text();
}
catch (error) {
debug('Response parsing error: %o', error);
return undefined;
}
});
}
/**
* Creates a custom error for fetch failures.
* @param {Response} response - The fetch response object.
* @param {unknown} data - The parsed response data.
* @returns {NativeDataFetcherError} - The constructed error object.
*/
createError(response, data) {
return Object.assign(Object.assign({}, new Error(`HTTP ${response.status} ${response.statusText}`)), { response: {
status: response.status,
statusText: response.statusText,
headers: this.extractDebugHeaders(response.headers),
data,
} });
}
}
exports.NativeDataFetcher = NativeDataFetcher;