-
Notifications
You must be signed in to change notification settings - Fork 4.1k
/
index.js
158 lines (138 loc) · 4.32 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
/**
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';
/**
* Internal dependencies
*/
import createNonceMiddleware from './middlewares/nonce';
import createRootURLMiddleware from './middlewares/root-url';
import createPreloadingMiddleware from './middlewares/preloading';
import fetchAllMiddleware from './middlewares/fetch-all-middleware';
import namespaceEndpointMiddleware from './middlewares/namespace-endpoint';
import httpV1Middleware from './middlewares/http-v1';
import userLocaleMiddleware from './middlewares/user-locale';
import mediaUploadMiddleware from './middlewares/media-upload';
import {
parseResponseAndNormalizeError,
parseAndThrowError,
} from './utils/response';
/**
* Default set of header values which should be sent with every request unless
* explicitly provided through apiFetch options.
*
* @type {Object}
*/
const DEFAULT_HEADERS = {
// The backend uses the Accept header as a condition for considering an
// incoming request as a REST request.
//
// See: https://core.trac.wordpress.org/ticket/44534
Accept: 'application/json, */*;q=0.1',
};
/**
* Default set of fetch option values which should be sent with every request
* unless explicitly provided through apiFetch options.
*
* @type {Object}
*/
const DEFAULT_OPTIONS = {
credentials: 'include',
};
const middlewares = [
userLocaleMiddleware,
namespaceEndpointMiddleware,
httpV1Middleware,
fetchAllMiddleware,
];
function registerMiddleware( middleware ) {
middlewares.unshift( middleware );
}
const checkStatus = ( response ) => {
if ( response.status >= 200 && response.status < 300 ) {
return response;
}
throw response;
};
const defaultFetchHandler = ( nextOptions ) => {
const { url, path, data, parse = true, ...remainingOptions } = nextOptions;
let { body, headers } = nextOptions;
// Merge explicitly-provided headers with default values.
headers = { ...DEFAULT_HEADERS, ...headers };
// The `data` property is a shorthand for sending a JSON body.
if ( data ) {
body = JSON.stringify( data );
headers[ 'Content-Type' ] = 'application/json';
}
const responsePromise = window.fetch( url || path, {
...DEFAULT_OPTIONS,
...remainingOptions,
body,
headers,
} );
return (
responsePromise
// Return early if fetch errors. If fetch error, there is most likely no
// network connection. Unfortunately fetch just throws a TypeError and
// the message might depend on the browser.
.then(
( value ) =>
Promise.resolve( value )
.then( checkStatus )
.catch( ( response ) =>
parseAndThrowError( response, parse )
)
.then( ( response ) =>
parseResponseAndNormalizeError( response, parse )
),
() => {
throw {
code: 'fetch_error',
message: __( 'You are probably offline.' ),
};
}
)
);
};
let fetchHandler = defaultFetchHandler;
/**
* Defines a custom fetch handler for making the requests that will override
* the default one using window.fetch
*
* @param {Function} newFetchHandler The new fetch handler
*/
function setFetchHandler( newFetchHandler ) {
fetchHandler = newFetchHandler;
}
function apiFetch( options ) {
// creates a nested function chain that calls all middlewares and finally the `fetchHandler`,
// converting `middlewares = [ m1, m2, m3 ]` into:
// ```
// opts1 => m1( opts1, opts2 => m2( opts2, opts3 => m3( opts3, fetchHandler ) ) );
// ```
const enhancedHandler = middlewares.reduceRight( ( next, middleware ) => {
return ( workingOptions ) => middleware( workingOptions, next );
}, fetchHandler );
return enhancedHandler( options ).catch( ( error ) => {
if ( error.code !== 'rest_cookie_invalid_nonce' ) {
return Promise.reject( error );
}
// If the nonce is invalid, refresh it and try again.
return window
.fetch( apiFetch.nonceEndpoint )
.then( checkStatus )
.then( ( data ) => data.text() )
.then( ( text ) => {
apiFetch.nonceMiddleware.nonce = text;
return apiFetch( options );
} );
} );
}
apiFetch.use = registerMiddleware;
apiFetch.setFetchHandler = setFetchHandler;
apiFetch.createNonceMiddleware = createNonceMiddleware;
apiFetch.createPreloadingMiddleware = createPreloadingMiddleware;
apiFetch.createRootURLMiddleware = createRootURLMiddleware;
apiFetch.fetchAllMiddleware = fetchAllMiddleware;
apiFetch.mediaUploadMiddleware = mediaUploadMiddleware;
export default apiFetch;