{"version":3,"sources":["../src/useFetch.ts","../src/MatsuriError.ts","../src/fetcher.ts"],"sourcesContent":["import { useMemo } from \"react\";\nimport useSWR, {\n  type KeyedMutator,\n  type SWRConfiguration,\n  type SWRHook,\n} from \"swr\";\nimport {\n  AuthTokenHeader,\n  FetcherOptions,\n  FetcherResult,\n  fetcherThrows,\n} from \"./fetcher\";\n\ntype SWRKey = Parameters<SWRHook>[0];\n\nexport interface UseFetchState<\n  T,\n  U = Record<string, unknown>,\n> extends FetcherResult<T, U> {\n  refetch: KeyedMutator<T>;\n  isLoading: boolean;\n  isValidating: boolean;\n}\n\nexport interface UseFetchStateForSuspense<\n  T,\n  U = Record<string, unknown>,\n> extends UseFetchState<T, U> {\n  data: T;\n}\n\ninterface UseFetchKey {\n  url: string;\n  token: string | undefined;\n  body: RequestInit[\"body\"] | undefined;\n  dependencies: SWRKey | undefined;\n}\n\ninterface UseFetchOptions<T> extends Omit<FetcherOptions, \"fetch\"> {\n  /**\n   * keyの型を自動で反映させたければジェネリクスなどを利用してoptions.keyの型を持ってくる必要があるが、\n   * fetchを上書きしたいケースがそこまであるとは思えないため、fetchの引数の型を緩く指定している。\n   */\n  fetch?: (args: UseFetchKey) => Promise<T>;\n  swrConfig?: Partial<SWRConfiguration> & {\n    fallbackData?: never;\n    suspense?: false;\n  };\n  authTokenHeader?: AuthTokenHeader;\n  /**\n   * 依存関係の変更を検知して再フェッチを行いたい場合に指定する。\n   */\n  dependencies?: SWRKey;\n}\n\ninterface UseFetchOptionsWithSuspense<T> extends Omit<\n  UseFetchOptions<T>,\n  \"swrConfig\"\n> {\n  swrConfig: Partial<SWRConfiguration> &\n    (\n      | {\n          fallbackData: unknown;\n        }\n      | {\n          suspense: true;\n          fallbackData?: unknown;\n        }\n    );\n}\n\ninterface UseFetch {\n  <T, U = Record<string, unknown>>(\n    url: string | null,\n    options?: UseFetchOptions<T>,\n  ): UseFetchState<T, U>;\n  <T, U = Record<string, unknown>>(\n    url: string | null,\n    options?: UseFetchOptionsWithSuspense<T>,\n  ): UseFetchStateForSuspense<T, U>;\n}\n\nexport const useFetch: UseFetch = (url: string | null, options = {}) => {\n  type T = ReturnType<UseFetch>[\"data\"];\n  type E = ReturnType<UseFetch>[\"error\"];\n\n  const key = useMemo((): UseFetchKey | null => {\n    if (url === null) {\n      return url;\n    } else {\n      return {\n        url,\n        token: options.token,\n        body: options.body,\n        dependencies: options.dependencies,\n      };\n    }\n  }, [options.body, options.dependencies, options.token, url]);\n  const fetcherWrapper = useMemo(\n    () =>\n      options.fetch ??\n      (({ url, token }: { url: string; token?: string }) => {\n        return fetcherThrows<T>(url, {\n          method: options.method,\n          token,\n          body: options.body,\n          authTokenHeader: options.authTokenHeader,\n        });\n      }),\n    [options.authTokenHeader, options.body, options.fetch, options.method],\n  );\n\n  const { data, error, mutate, isLoading, isValidating } = useSWR<T, E>(\n    key,\n    fetcherWrapper,\n    options.swrConfig,\n  );\n\n  return {\n    data,\n    error,\n    refetch: mutate,\n    isLoading,\n    isValidating,\n  };\n};\n\ninterface UseAuthFetch {\n  <T, U = Record<string, unknown>>(\n    token: string,\n    url: string | null,\n    options?: UseFetchOptions<T>,\n  ): UseFetchState<T, U>;\n  <T, U = Record<string, unknown>>(\n    token: string,\n    url: string | null,\n    options?: UseFetchOptionsWithSuspense<T>,\n  ): UseFetchStateForSuspense<T, U>;\n}\n\nexport const useAuthFetch: UseAuthFetch = (\n  token: string,\n  url: string | null,\n  options = {},\n) => {\n  return useFetch(url === null ? null : token ? url : null, {\n    token,\n    authTokenHeader: \"X-Access-Token\",\n    ...options,\n    //@ts-expect-error UseFetchOptionsWithSuspenseにUseFetchOptionsのswrConfigを渡すことが出来ないので型エラーが出る。これをランタイムに影響を出さずにケアするのが難しいため、このエラーは無視する。\n    swrConfig: {\n      ...(options.swrConfig ?? {}),\n    },\n  });\n};\n\n// Authorization: Bearer [TOKEN]の形式でセットするuseAuthFetch\nexport const useAuthBearerFetch: UseAuthFetch = (\n  token: string,\n  url: string | null,\n  options = {},\n) => {\n  return useAuthFetch(token, url, {\n    authTokenHeader: \"Authorization\",\n    ...options,\n    //@ts-expect-error UseFetchOptionsWithSuspenseにUseFetchOptionsのswrConfigを渡すことが出来ないので型エラーが出る。これをランタイムに影響を出さずにケアするのが難しいため、このエラーは無視する。\n    swrConfig: {\n      ...(options.swrConfig ?? {}),\n    },\n  });\n};\n","export class MatsuriError<T = Record<string, unknown>> extends Error {\n  status?: number;\n\n  private text: string;\n\n  constructor(\n    text: string,\n    options: {\n      status: number;\n    },\n  ) {\n    const { status, ...rest } = options;\n    /**\n     * messageは後方互換性のためにこのような形式にしている\n     */\n    const message = `${status}: ${text}`;\n    super(message, rest);\n    this.name = \"MatsuriError\";\n    this.status = status;\n    this.text = text;\n  }\n  /**\n   * NOTE: メソッドであってもErrorに存在しないプロパティは基本的にundefinedを許容させる。\n   * これは実際はMatsuriErrorでないErrorが投げられたとしてもランタイムエラーを防げるような記述を強制させるために行う。\n   * またMatsuriError|Errorが型としてはより正しいが、一方にしかないプロパティはインスタンスを確認しなければ利用できなくなってしまい冗長な記述を強いられてしまう。\n   */\n  parse: (<S = T>() => S | null) | undefined = <S = T>() => {\n    try {\n      return JSON.parse(this.text) as S;\n    } catch {\n      return null;\n    }\n  };\n}\n","import { MatsuriError } from \"./MatsuriError\";\n\nexport interface FetcherResult<T, U = Record<string, unknown>> {\n  data: T | undefined;\n  error: MatsuriError<U> | undefined;\n}\n\nexport type AuthTokenHeader =\n  | \"Authorization\"\n  | \"X-Access-Token\"\n  | \"x-authorization\"\n  // @see https://github.com/matsuri-tech/m2m-systems\n  | \"X-M2m-Access-Token\";\n\nexport interface FetcherOptions {\n  method?: \"GET\" | \"POST\" | \"PATCH\" | \"PUT\" | \"DELETE\" | \"OPTIONS\" | \"HEAD\";\n  token?: string;\n  body?: RequestInit[\"body\"];\n  fetch?: (info: RequestInfo, init?: RequestInit) => Promise<Response>;\n  authTokenHeader?: AuthTokenHeader;\n}\n\nexport const fetcher = async <T, U = Record<string, unknown>>(\n  url: string,\n  options: FetcherOptions = {},\n): Promise<FetcherResult<T, U>> => {\n  try {\n    return { data: await fetcherThrows(url, options), error: undefined };\n  } catch (e) {\n    return { data: undefined, error: e as MatsuriError<U> };\n  }\n};\n\nexport const bearerFetcher = <T, U = Record<string, unknown>>(\n  token: string,\n  url: string,\n  options?: FetcherOptions,\n): Promise<FetcherResult<T, U>> => {\n  return fetcher(url, { ...options, token, authTokenHeader: \"Authorization\" });\n};\nexport const fetcherThrows = async <T>(\n  url: string,\n  options: FetcherOptions = {},\n): Promise<T> => {\n  const obj: RequestInit = {};\n  if (options.token !== null && options.token !== undefined) {\n    const header = options.authTokenHeader ?? \"X-Access-Token\";\n\n    obj.headers = {\n      [header]:\n        header === \"Authorization\" ? `Bearer ${options.token}` : options.token,\n    };\n  }\n  if (options.method) {\n    obj.method = options.method || \"GET\";\n  }\n\n  const hasBody = !(options?.body === null || options?.body === undefined);\n\n  if (hasBody) {\n    obj.body = options.body;\n  }\n\n  if (hasBody && obj.method === \"GET\") {\n    throw new TypeError(\"The `GET` method cannot be used with a body\");\n  }\n\n  if (typeof obj.body === \"string\") {\n    obj.headers = {\n      \"Content-Type\": \"application/json\",\n      ...(obj.headers ?? {}),\n    };\n  }\n\n  // eslint-disable-next-line no-restricted-globals -- This is the fetcher implementation itself, so direct use of fetch is necessary\n  const resp = await (options.fetch ?? fetch)(url, obj);\n\n  if (!resp.ok) {\n    throw new MatsuriError(await resp.text(), { status: resp.status });\n  }\n\n  const bodyText = await resp.text();\n  // responseが空の場合があるのでケアする\n  return bodyText ? (JSON.parse(bodyText) as T) : (undefined as T);\n};\n"],"mappings":"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","names":["url","useSWR"]}