Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Update fetch input types to account for Request object as first … #1766

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 58 additions & 26 deletions packages/common/src/fetch.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { isInstance } from './utils';

// Define default request options and allow modification using getters, setters

// Reference: https://developer.mozilla.org/en-US/docs/Web/API/Request/Request
const defaultFetchOpts: RequestInit = {
// By default referrer value will be client:origin: above reference link
Expand Down Expand Up @@ -37,7 +40,10 @@
};

/** @ignore */
export async function fetchWrapper(input: RequestInfo, init?: RequestInit): Promise<Response> {
export async function fetchWrapper(
input: RequestInfo | URL,
init?: RequestInit
): Promise<Response> {
const fetchOpts = {};
// Use the provided options in request options along with default or user provided values
Object.assign(fetchOpts, defaultFetchOpts, init);
Expand All @@ -46,11 +52,11 @@
return fetchResult;
}

export type FetchFn = (url: string, init?: RequestInit) => Promise<Response>;
export type FetchFn = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this just be:

export FetchFn = typeof globalThis.fetch;

?


/**
* @ignore Internally used for letting networking functions specify "API" options.
* Should be compatible with the `client`s created by the API and RPC packages.
* Should be compatible with the `client`s created by the API and RPC client packages.
*/
export interface ClientOpts {
baseUrl?: string;
Expand All @@ -65,24 +71,41 @@

export interface RequestContext {
fetch: FetchFn;

/** @deprecated This may in some cases be a Request/RequestInfo object instead of a string. For safety use `.input` instead. */
url: string;

input: RequestInfo | URL;
init: RequestInit;
}

export interface ResponseContext {
fetch: FetchFn;

/** @deprecated This may in some cases be a Request/RequestInfo object instead of a string. For safety use `.input` instead. */
url: string;

input: RequestInfo | URL;
init: RequestInit;

response: Response;
}

/** @deprecated Use {@link FetchArgs} instead. The `.url` property is may also be a Request object. */
export interface FetchParams {
url: string;
init: RequestInit;
}

export interface FetchArgs {
input: RequestInfo | URL;
init?: RequestInit;
}

export interface FetchMiddleware {
pre?: (context: RequestContext) => PromiseLike<FetchParams | void> | FetchParams | void;
pre?: (
context: RequestContext
) => PromiseLike<FetchParams | FetchArgs | void> | FetchParams | FetchArgs | void;
post?: (context: ResponseContext) => Promise<Response | void> | Response | void;
}
export interface ApiKeyMiddlewareOpts {
Expand Down Expand Up @@ -118,8 +141,13 @@
}: ApiKeyMiddlewareOpts): FetchMiddleware {
return {
pre: context => {
const reqUrl = new URL(context.url);
if (!hostMatches(reqUrl.host, host)) return; // Skip middleware if host does not match pattern
const url = isInstance(context.input, URL)
? context.input

Check warning on line 145 in packages/common/src/fetch.ts

View check run for this annotation

Codecov / codecov/patch

packages/common/src/fetch.ts#L145

Added line #L145 was not covered by tests
: typeof context.input === 'string'
? new URL(context.input)
: new URL(context.input.url);

Check warning on line 148 in packages/common/src/fetch.ts

View check run for this annotation

Codecov / codecov/patch

packages/common/src/fetch.ts#L148

Added line #L148 was not covered by tests

if (!hostMatches(url.host, host)) return; // Skip middleware if host does not match pattern

const headers =
context.init.headers instanceof Headers
Expand All @@ -130,16 +158,16 @@
};
}

function argsForCreateFetchFn(args: any[]): { fetchLib: FetchFn; middlewares: FetchMiddleware[] } {
let fetchLib: FetchFn = fetchWrapper;
let middlewares: FetchMiddleware[] = [];
if (args.length > 0 && typeof args[0] === 'function') {
fetchLib = args.shift();
}
if (args.length > 0) {
middlewares = args; // remaining args
/** @internal */
function argsForCreateFetchFn(args: any[]): { fetch: FetchFn; middlewares: FetchMiddleware[] } {
if (typeof args[0] === 'function') {
return {

Check warning on line 164 in packages/common/src/fetch.ts

View check run for this annotation

Codecov / codecov/patch

packages/common/src/fetch.ts#L164

Added line #L164 was not covered by tests
fetch: args.shift(), // first arg is fetch function
middlewares: args,
};
}
return { fetchLib, middlewares };

return { fetch: fetchWrapper, middlewares: args };
}

/**
Expand All @@ -152,35 +180,39 @@
* ```
* @category Network
*/
export function createFetchFn(fetchLib: FetchFn, ...middleware: FetchMiddleware[]): FetchFn;
export function createFetchFn(fetchFn: FetchFn, ...middleware: FetchMiddleware[]): FetchFn;
export function createFetchFn(...middleware: FetchMiddleware[]): FetchFn;
export function createFetchFn(...args: any[]): FetchFn {
const { fetchLib, middlewares } = argsForCreateFetchFn(args);
const { fetch, middlewares } = argsForCreateFetchFn(args);

const fetchFn = async (url: string, init?: RequestInit | undefined): Promise<Response> => {
let fetchParams = { url, init: init ?? {} };
const fetchFn: FetchFn = async (input, init) => {
let fetchParams: FetchArgs = { input, init: init ?? {} };

for (const middleware of middlewares) {
if (typeof middleware.pre === 'function') {
const result = await Promise.resolve(
middleware.pre({
fetch: fetchLib,
...fetchParams,
fetch,
url: fetchParams.input as string, // @deprecated (type mismatch, but this is backwards compatible behavior)
input: fetchParams.input,
init: fetchParams.init ?? {},
})
);
fetchParams = result ?? fetchParams;
if (result && 'url' in result) (result as unknown as FetchArgs).input = result.url;
fetchParams = (result as FetchArgs | void) ?? fetchParams;
}
}

let response = await fetchLib(fetchParams.url, fetchParams.init);
let response = await fetch(fetchParams.input, fetchParams.init);

for (const middleware of middlewares) {
if (typeof middleware.post === 'function') {
const result = await Promise.resolve(
middleware.post({
fetch: fetchLib,
url: fetchParams.url,
init: fetchParams.init,
fetch,
url: fetchParams.input as string, // @deprecated (type mismatch, but this is backwards compatible behavior)
input: fetchParams.input,
init: fetchParams.init ?? {},
response: response?.clone() ?? response,
})
);
Expand Down
Loading