2 * @typedef FormattedResponse
3 * @property {Headers} headers
4 * @property {Response} original
5 * @property {Object|String} data
6 * @property {Boolean} redirected
7 * @property {Number} status
8 * @property {string} statusText
9 * @property {string} url
13 * Get the content from a fetch response.
14 * Checks the content-type header to determine the format.
15 * @param {Response} response
16 * @returns {Promise<Object|String>}
18 async function getResponseContent(response) {
19 if (response.status === 204) {
23 const responseContentType = response.headers.get('Content-Type') || '';
24 const subType = responseContentType.split(';')[0].split('/').pop();
26 if (subType === 'javascript' || subType === 'json') {
27 return response.json();
30 return response.text();
33 export class HttpError extends Error {
35 constructor(response, content) {
36 super(response.statusText);
38 this.headers = response.headers;
39 this.redirected = response.redirected;
40 this.status = response.status;
41 this.statusText = response.statusText;
42 this.url = response.url;
43 this.original = response;
49 * Create a new HTTP request, setting the required CSRF information
50 * to communicate with the back-end. Parses & formats the response.
52 * @param {Object} options
53 * @returns {Promise<FormattedResponse>}
55 async function request(url, options = {}) {
58 if (!requestUrl.startsWith('http')) {
59 requestUrl = window.baseUrl(requestUrl);
63 const urlObj = new URL(requestUrl);
64 for (const paramName of Object.keys(options.params)) {
65 const value = options.params[paramName];
66 if (typeof value !== 'undefined' && value !== null) {
67 urlObj.searchParams.set(paramName, value);
70 requestUrl = urlObj.toString();
73 const csrfToken = document.querySelector('meta[name=token]').getAttribute('content');
74 const requestOptions = {...options, credentials: 'same-origin'};
75 requestOptions.headers = {
76 ...requestOptions.headers || {},
77 baseURL: window.baseUrl(''),
78 'X-CSRF-TOKEN': csrfToken,
81 const response = await fetch(requestUrl, requestOptions);
82 const content = await getResponseContent(response);
85 headers: response.headers,
86 redirected: response.redirected,
87 status: response.status,
88 statusText: response.statusText,
94 throw new HttpError(response, content);
101 * Perform a HTTP request to the back-end that includes data in the body.
102 * Parses the body to JSON if an object, setting the correct headers.
103 * @param {String} method
104 * @param {String} url
105 * @param {Object} data
106 * @returns {Promise<FormattedResponse>}
108 async function dataRequest(method, url, data = null) {
114 // Send data as JSON if a plain object
115 if (typeof data === 'object' && !(data instanceof FormData)) {
117 'Content-Type': 'application/json',
118 'X-Requested-With': 'XMLHttpRequest',
120 options.body = JSON.stringify(data);
123 // Ensure FormData instances are sent over POST
124 // Since Laravel does not read multipart/form-data from other types
125 // of request. Hence the addition of the magic _method value.
126 if (data instanceof FormData && method !== 'post') {
127 data.append('_method', method);
128 options.method = 'post';
131 return request(url, options);
135 * Perform a HTTP GET request.
136 * Can easily pass query parameters as the second parameter.
137 * @param {String} url
138 * @param {Object} params
139 * @returns {Promise<FormattedResponse>}
141 export async function get(url, params = {}) {
142 return request(url, {
149 * Perform a HTTP POST request.
150 * @param {String} url
151 * @param {Object} data
152 * @returns {Promise<FormattedResponse>}
154 export async function post(url, data = null) {
155 return dataRequest('POST', url, data);
159 * Perform a HTTP PUT request.
160 * @param {String} url
161 * @param {Object} data
162 * @returns {Promise<FormattedResponse>}
164 export async function put(url, data = null) {
165 return dataRequest('PUT', url, data);
169 * Perform a HTTP PATCH request.
170 * @param {String} url
171 * @param {Object} data
172 * @returns {Promise<FormattedResponse>}
174 export async function patch(url, data = null) {
175 return dataRequest('PATCH', url, data);
179 * Perform a HTTP DELETE request.
180 * @param {String} url
181 * @param {Object} data
182 * @returns {Promise<FormattedResponse>}
184 async function performDelete(url, data = null) {
185 return dataRequest('DELETE', url, data);
188 export {performDelete as delete};