import { flow } from './fp'; import { ApiResponse, handleFetchResponse, HandleResponse } from './response'; import { isDefined, OmitStrict } from './typescript'; import { buildUrl, BuildUrlParams } from './url'; type FetchParams = Pick; /** * The params generated by the library */ type BaseRequestParams = BuildUrlParams & FetchParams & // `headers` is not part of FetchParams because we want to allow headers in the additional params as well Pick; /** * Additional fetch options provided by the user on a per-call basis */ export interface AdditionalFetchOptions extends OmitStrict {} export type CompleteRequestParams = BaseRequestParams & AdditionalFetchOptions; type HandleRequest = ( a: Args, additionalFetchOptions?: AdditionalFetchOptions, ) => CompleteRequestParams; /** * helper used to type-check the arguments, and add default params for all requests */ export const createRequestHandler = ( fn: (a: Args) => BaseRequestParams, ): HandleRequest => (a, additionalFetchOptions = {}) => { const { headers, query, ...baseReqParams } = fn(a); return { ...baseReqParams, ...additionalFetchOptions, query, headers: { ...headers, ...additionalFetchOptions.headers, }, }; }; /** * Initial parameters that apply to all calls */ export type InitParams = { apiVersion?: string; fetch?: typeof fetch; } & OmitStrict & ({ accessKey: string; apiUrl?: never } | { apiUrl: string; accessKey?: never }); type RequestGenerator = { handleRequest: HandleRequest; handleResponse: HandleResponse; }; type Endpoint = { getPathname: (params: PathnameParams) => string; } & RequestGenerator; export const makeEndpoint = ( endpoint: Endpoint, ) => endpoint; type GeneratedRequestFunction = ( ...a: Parameters> ) => Promise>; type InitMakeRequest = ( args: InitParams, ) => ( handlers: RequestGenerator, ) => GeneratedRequestFunction; export const initMakeRequest: InitMakeRequest = ({ accessKey, apiVersion = 'v1', apiUrl = 'https://api.unsplash.com', headers: generalHeaders, fetch: providedFetch, ...generalFetchOptions }) => ({ handleResponse, handleRequest }) => flow( handleRequest, ({ pathname, query, method = 'GET', headers: endpointHeaders, body, signal }) => { const url = buildUrl({ pathname, query })(apiUrl); const fetchOptions: RequestInit = { method, headers: { ...generalHeaders, ...endpointHeaders, 'Accept-Version': apiVersion, ...(isDefined(accessKey) ? { Authorization: `Client-ID ${accessKey}` } : {}), }, body, signal, ...generalFetchOptions, }; const fetchToUse = providedFetch ?? fetch; return fetchToUse(url, fetchOptions).then(handleFetchResponse(handleResponse)); }, );