Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
1 change: 1 addition & 0 deletions packages/react/src/components/button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ export function UploadButton<
const { startUpload, isUploading, routeConfig } = __useUploadThingInternal(
resolveMaybeUrlArg($props.url),
$props.endpoint,
$props.fetch ?? globalThis.fetch,
{
signal: acRef.current.signal,
headers: $props.headers,
Expand Down
1 change: 1 addition & 0 deletions packages/react/src/components/dropzone.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ export function UploadDropzone<
const { startUpload, isUploading, routeConfig } = __useUploadThingInternal(
resolveMaybeUrlArg($props.url),
$props.endpoint,
$props.fetch ?? globalThis.fetch,
{
signal: acRef.current.signal,
headers: $props.headers,
Expand Down
21 changes: 18 additions & 3 deletions packages/react/src/components/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,20 @@ export const generateUploadButton = <TRouter extends FileRouter>(
);

const url = resolveMaybeUrlArg(opts?.url);
const fetch = opts?.fetch ?? globalThis.fetch;

const TypedButton = <TEndpoint extends keyof TRouter>(
props: Omit<
UploadButtonProps<TRouter, TEndpoint>,
keyof GenerateTypedHelpersOptions
>,
) => <UploadButton<TRouter, TEndpoint> {...(props as any)} url={url} />;
) => (
<UploadButton<TRouter, TEndpoint>
{...(props as any)}
url={url}
fetch={fetch}
/>
);
return TypedButton;
};

Expand All @@ -54,7 +61,13 @@ export const generateUploadDropzone = <TRouter extends FileRouter>(
UploadDropzoneProps<TRouter, TEndpoint>,
keyof GenerateTypedHelpersOptions
>,
) => <UploadDropzone<TRouter, TEndpoint> {...(props as any)} url={url} />;
) => (
<UploadDropzone<TRouter, TEndpoint>
{...(props as any)}
url={url}
fetch={fetch}
/>
);
return TypedDropzone;
};

Expand All @@ -74,6 +87,8 @@ export const generateUploader = <TRouter extends FileRouter>(
UploadthingComponentProps<TRouter, TEndpoint>,
keyof GenerateTypedHelpersOptions
>,
) => <Uploader<TRouter, TEndpoint> {...(props as any)} url={url} />;
) => (
<Uploader<TRouter, TEndpoint> {...(props as any)} url={url} fetch={fetch} />
);
return TypedUploader;
};
39 changes: 39 additions & 0 deletions packages/react/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type {
ClassListMerger,
ErrorMessage,
ExtendObjectIf,
FetchEsque,
MaybePromise,
UploadThingError,
} from "@uploadthing/shared";
Expand All @@ -26,6 +27,25 @@ export interface GenerateTypedHelpersOptions {
* @default (VERCEL_URL ?? window.location.origin) + "/api/uploadthing"
*/
url?: string | URL;
/**
* Provide a custom fetch implementation.
* @default `globalThis.fetch`
* @example
* ```ts
* fetch: (input, init) => {
* if (input.toString().startsWith(MY_SERVER_URL)) {
* // Include cookies in the request to your API
* return fetch(input, {
* ...init,
* credentials: "include",
* });
* }
*
* return fetch(input, init);
* }
* ```
*/
fetch?: FetchEsque | undefined;
}

export type UseUploadthingProps<
Expand Down Expand Up @@ -122,6 +142,25 @@ export type UploadthingComponentProps<
* @default (VERCEL_URL ?? window.location.origin) + "/api/uploadthing"
*/
url?: string | URL;
/**
* Provide a custom fetch implementation.
* @default `globalThis.fetch`
* @example
* ```ts
* fetch: (input, init) => {
* if (input.toString().startsWith(MY_SERVER_URL)) {
* // Include cookies in the request to your API
* return fetch(input, {
* ...init,
* credentials: "include",
* });
* }
*
* return fetch(input, init);
* }
* ```
*/
fetch?: FetchEsque | undefined;
config?: {
mode?: "auto" | "manual";
appendOnPaste?: boolean;
Expand Down
12 changes: 10 additions & 2 deletions packages/react/src/useUploadThing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { useRef, useState } from "react";
import type {
EndpointMetadata,
ExpandedRouteConfig,
FetchEsque,
} from "@uploadthing/shared";
import {
INTERNAL_DO_NOT_USE__fatalClientError,
Expand Down Expand Up @@ -30,14 +31,17 @@ import useFetch from "./utils/useFetch";

declare const globalThis: {
__UPLOADTHING?: EndpointMetadata;
fetch: FetchEsque;
};

const useRouteConfig = (
fetch: FetchEsque,
url: URL,
endpoint: string,
): ExpandedRouteConfig | undefined => {
const maybeServerData = globalThis.__UPLOADTHING;
const { data } = useFetch<EndpointMetadata>(
fetch,
// Don't fetch if we already have the data
maybeServerData ? undefined : url.href,
);
Expand All @@ -55,11 +59,13 @@ export function __useUploadThingInternal<
>(
url: URL,
endpoint: EndpointArg<TRouter, TEndpoint>,
fetch: FetchEsque,
opts?: UseUploadthingProps<TRouter[TEndpoint]>,
) {
const { uploadFiles, routeRegistry } = genUploader<TRouter>({
url,
package: "@uploadthing/react",
fetch,
});

const [isUploading, setUploading] = useState(false);
Expand Down Expand Up @@ -134,7 +140,7 @@ export function __useUploadThingInternal<
});

const _endpoint = unwrap(endpoint, routeRegistry);
const routeConfig = useRouteConfig(url, _endpoint as string);
const routeConfig = useRouteConfig(fetch, url, _endpoint as string);

return {
startUpload,
Expand All @@ -152,18 +158,20 @@ export const generateReactHelpers = <TRouter extends FileRouter>(
uploadthingClientVersion,
);

const fetch = initOpts?.fetch ?? globalThis.fetch;
const url = resolveMaybeUrlArg(initOpts?.url);

const clientHelpers = genUploader<TRouter>({
url,
package: "@uploadthing/react",
fetch,
});

function useUploadThing<TEndpoint extends keyof TRouter>(
endpoint: EndpointArg<TRouter, TEndpoint>,
opts?: UseUploadthingProps<TRouter[TEndpoint]>,
) {
return __useUploadThingInternal(url, endpoint, opts);
return __useUploadThingInternal(url, endpoint, fetch, opts);
}

function getRouteConfig(slug: EndpointArg<TRouter, keyof TRouter>) {
Expand Down
7 changes: 6 additions & 1 deletion packages/react/src/utils/useFetch.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Ripped from https://usehooks-ts.com/react-hook/use-fetch
import { useEffect, useReducer, useRef } from "react";

import type { FetchEsque } from "@uploadthing/shared";
import { safeParseJSON } from "@uploadthing/shared";

interface State<T> {
Expand All @@ -16,7 +17,11 @@ type Action<T> =
| { type: "fetched"; payload: T }
| { type: "error"; payload: Error };

function useFetch<T = unknown>(url?: string, options?: RequestInit): State<T> {
function useFetch<T = unknown>(
fetch: FetchEsque,
url?: string,
options?: RequestInit,
): State<T> {
const cache = useRef<Cache<T>>({});

// Used to prevent state update if the component is unmounted
Expand Down
10 changes: 6 additions & 4 deletions packages/uploadthing/src/client.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as Arr from "effect/Array";
import * as Micro from "effect/Micro";

import type { ExpandedRouteConfig } from "@uploadthing/shared";
import type { ExpandedRouteConfig, FetchEsque } from "@uploadthing/shared";
import {
createIdentityProxy,
FetchContext,
Expand Down Expand Up @@ -112,6 +112,7 @@ export const genUploader = <TRouter extends FileRouter>(
url: resolveMaybeUrlArg(initOpts?.url),
headers: opts.headers,
});
const fetchFn: FetchEsque = initOpts.fetch ?? window.fetch;

const presigneds = await Micro.runPromise(
utReporter("upload", {
Expand All @@ -123,7 +124,7 @@ export const genUploader = <TRouter extends FileRouter>(
type: f.type,
lastModified: f.lastModified,
})),
}).pipe(Micro.provideService(FetchContext, window.fetch)),
}).pipe(Micro.provideService(FetchContext, fetchFn)),
);

const totalSize = opts.files.reduce((acc, f) => acc + f.size, 0);
Expand All @@ -141,7 +142,7 @@ export const genUploader = <TRouter extends FileRouter>(
totalProgress: Math.round((totalLoaded / totalSize) * 100),
});
},
}).pipe(Micro.provideService(FetchContext, window.fetch));
}).pipe(Micro.provideService(FetchContext, fetchFn));

for (const [i, p] of presigneds.entries()) {
const file = opts.files[i];
Expand Down Expand Up @@ -254,6 +255,7 @@ export const genUploader = <TRouter extends FileRouter>(
>,
) => {
const endpoint = typeof slug === "function" ? slug(routeRegistry) : slug;
const fetchFn: FetchEsque = initOpts.fetch ?? window.fetch;

return uploadFilesInternal<TRouter, TEndpoint>(endpoint, {
...opts,
Expand All @@ -263,7 +265,7 @@ export const genUploader = <TRouter extends FileRouter>(
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
input: (opts as any).input as inferEndpointInput<TRouter[TEndpoint]>,
})
.pipe((effect) =>
.pipe(Micro.provideService(FetchContext, fetchFn), (effect) =>
Micro.runPromiseExit(effect, opts.signal && { signal: opts.signal }),
)
.then((exit) => {
Expand Down
30 changes: 15 additions & 15 deletions packages/uploadthing/src/internal/upload.browser.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { unsafeCoerce } from "effect/Function";
import * as Micro from "effect/Micro";

import type { FetchError } from "@uploadthing/shared";
import { FetchContext, fetchEff, UploadThingError } from "@uploadthing/shared";
import type { FetchContext, FetchError } from "@uploadthing/shared";
import { fetchEff, UploadThingError } from "@uploadthing/shared";

import { version } from "../../package.json";
import type {
Expand Down Expand Up @@ -119,7 +119,8 @@ export const uploadFilesInternal = <
opts: UploadFilesOptions<TRouter[TEndpoint]>,
): Micro.Micro<
ClientUploadedFileData<TServerOutput>[],
UploadThingError | FetchError
UploadThingError | FetchError,
FetchContext
> => {
// classic service right here
const reportEventToUT = createUTReporter({
Expand All @@ -132,16 +133,17 @@ export const uploadFilesInternal = <
const totalSize = opts.files.reduce((acc, f) => acc + f.size, 0);
let totalLoaded = 0;

return reportEventToUT("upload", {
input: "input" in opts ? opts.input : null,
files: opts.files.map((f) => ({
name: f.name,
size: f.size,
type: f.type,
lastModified: f.lastModified,
})),
}).pipe(
Micro.flatMap((presigneds) =>
return Micro.flatMap(
reportEventToUT("upload", {
input: "input" in opts ? opts.input : null,
files: opts.files.map((f) => ({
name: f.name,
size: f.size,
type: f.type,
lastModified: f.lastModified,
})),
}),
(presigneds) =>
Micro.forEach(
presigneds,
(presigned, i) =>
Expand Down Expand Up @@ -174,7 +176,5 @@ export const uploadFilesInternal = <
),
{ concurrency: 6 },
),
),
Micro.provideService(FetchContext, window.fetch),
);
};
19 changes: 19 additions & 0 deletions packages/uploadthing/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,25 @@ export type GenerateUploaderOptions = {
* @default (VERCEL_URL ?? window.location.origin) + "/api/uploadthing"
*/
url?: string | URL;
/**
* Provide a custom fetch implementation.
* @default `globalThis.fetch`
* @example
* ```ts
* fetch: (input, init) => {
* if (input.toString().startsWith(MY_SERVER_URL)) {
* // Include cookies in the request to your API
* return fetch(input, {
* ...init,
* credentials: "include",
* });
* }
*
* return fetch(input, init);
* }
* ```
*/
fetch?: FetchEsque | undefined;
/**
* The uploadthing package that is making this request
* @example "@uploadthing/react"
Expand Down
Loading
Loading