UNPKG

react-suspense-fetch

Version:

A low-level library for React Suspense for Data Fetching

1 lines 7.31 kB
{"version":3,"file":"index.modern.js","sources":["../src/index.ts"],"sourcesContent":["type FetchFunc<Result, Input> = (\n input: Input,\n options: { signal: AbortSignal },\n) => Promise<Result>;\n\ntype GetOptions = {\n forcePrefetch?: boolean;\n};\n\n/**\n * fetch store\n *\n * `prefetch` will start fetching.\n * `get` will return a result or throw a promise when a result is not ready.\n * `preset` will set a result without fetching.\n * `evict` will remove a result.\n * `abort` will cancel fetching.\n *\n * There are three cache types:\n * - WeakMap: `input` has to be an object in this case\n * - Map: you need to call evict to remove from cache\n * - Map with areEqual: you can specify a custom comparator\n */\nexport type FetchStore<Result, Input> = {\n prefetch: (input: Input) => void;\n get: (input: Input, option?: GetOptions) => Result;\n preset: (input: Input, result: Result) => void;\n evict: (input: Input) => void;\n abort: (input: Input) => void;\n};\n\nconst isObject = (x: unknown): x is object => typeof x === 'object' && x !== null;\n\nconst createMapLikeWithComparator = <K, V>(areEqual: (a: K, b: K) => boolean) => {\n const map = new Map<K, V>();\n const has = (key: K) => {\n for (const [k] of map) {\n if (areEqual(k, key)) {\n return true;\n }\n }\n return false;\n };\n const get = (key: K) => {\n for (const [k, v] of map) {\n if (areEqual(k, key)) {\n return v;\n }\n }\n return undefined;\n };\n const remove = (key: K) => {\n for (const [k] of map) {\n if (areEqual(k, key)) {\n map.delete(k);\n }\n }\n };\n return {\n set: (key: K, value: V) => { map.set(key, value); },\n has,\n get,\n delete: remove,\n };\n};\n\ntype CacheType<Input> =\n | { type: 'WeakMap'}\n | {\n type: 'Map';\n areEqual?: ((a: Input, b: Input) => boolean);\n };\n\nconst createCache = <Input, Instance>(\n cacheType?: CacheType<Input>,\n) => {\n if (cacheType?.type === 'WeakMap') {\n return new WeakMap<object, Instance>() as unknown as Map<Input, Instance>;\n }\n const areEqual = cacheType?.type === 'Map' && cacheType.areEqual;\n if (areEqual) {\n return createMapLikeWithComparator<Input, Instance>(areEqual);\n }\n return new Map<Input, Instance>();\n};\n\nexport function createFetchStore<Result, Input extends object>(\n fetchFunc: FetchFunc<Result, Input>,\n cacheType: { type: 'WeakMap' },\n presets?: Iterable<readonly [Input, Result]>,\n): FetchStore<Result, Input>\n\nexport function createFetchStore<Result, Input>(\n fetchFunc: FetchFunc<Result, Input>,\n cacheType?: {\n type: 'Map';\n areEqual?: ((a: Input, b: Input) => boolean);\n },\n presets?: Iterable<readonly [Input, Result]>,\n): FetchStore<Result, Input>\n\n/**\n * create fetch store\n *\n * @example\n * import { createFetchStore } from 'react-suspense-fetch';\n *\n * const fetchFunc = async (userId) => (await fetch(`https://reqres.in/api/users/${userId}?delay=3`)).json();\n * const store = createFetchStore(fetchFunc);\n * store.prefetch('1');\n */\nexport function createFetchStore<Result, Input>(\n fetchFunc: FetchFunc<Result, Input>,\n cacheType?: CacheType<Input>,\n presets?: Iterable<readonly [Input, Result]>,\n) {\n type Instance = {\n get: () => Result;\n abort: () => void;\n };\n const cache = createCache<Input, Instance>(cacheType);\n const assertObjectInput = (input: Input) => {\n if (cacheType?.type === 'WeakMap' && !isObject(input)) {\n throw new Error('WeakMap requires object input');\n }\n };\n const preset = (input: Input, result: Result) => {\n assertObjectInput(input);\n cache.set(input, {\n get: () => result,\n abort: () => {\n // nothing\n },\n });\n };\n if (presets) {\n for (const [input, result] of presets) {\n preset(input, result);\n }\n }\n const createInstance = (input: Input) => {\n let promise: Promise<void> | null = null;\n let result: Result | null = null;\n let error: unknown | null = null;\n const controller = new AbortController();\n promise = (async () => {\n try {\n result = await fetchFunc(input, { signal: controller.signal });\n } catch (e) {\n error = e;\n } finally {\n promise = null;\n }\n })();\n return {\n get: () => {\n if (promise) throw promise;\n if (error !== null) throw error;\n return result as Result;\n },\n abort: () => {\n controller.abort();\n },\n };\n };\n const prefetch = (input: Input) => {\n assertObjectInput(input);\n if (!cache.has(input)) {\n cache.set(input, createInstance(input));\n }\n };\n const get = (input: Input, options?: GetOptions) => {\n assertObjectInput(input);\n if (options?.forcePrefetch) {\n prefetch(input);\n }\n const instance = cache.get(input);\n if (!instance) {\n throw new Error('prefetch() must be called before get()');\n }\n return instance.get();\n };\n const evict = (input: Input) => {\n assertObjectInput(input);\n cache.delete(input);\n };\n const abort = (input: Input) => {\n assertObjectInput(input);\n cache.get(input)?.abort();\n };\n const store: FetchStore<Result, Input> = {\n prefetch,\n get,\n preset,\n evict,\n abort,\n };\n return store;\n}\n"],"names":["fetchFunc","cacheType","presets","cache","type","WeakMap","areEqual","map","Map","set","key","value","has","k","get","v","undefined","delete","createMapLikeWithComparator","createCache","assertObjectInput","input","x","Error","preset","result","abort","prefetch","error","controller","promise","signal","e","createInstance","options","forcePrefetch","instance","evict","_cache$get"],"mappings":"WAgHEA,EACAC,EACAC,GAMA,MAAMC,EA9CNF,KAEA,GAAwB,aAAX,MAATA,SAAAA,EAAWG,MACb,WACDC,QACD,MAAMC,EAA+B,SAAX,MAATL,SAAAA,EAAWG,OAAkBH,EAAUK,SACxD,OAAIA,EA/CqCA,KACzC,MAAMC,EAAM,IAAIC,IAwBhB,MAAO,CACLC,IAAK,CAACC,EAAQC,KAAeJ,EAAIE,IAAIC,EAAKC,EAAS,EACnDC,IAzBWF,IACX,IAAK,MAAOG,KAAZN,EACE,GAAID,EAASO,EAAGH,GACd,OAAO,EAGX,OAAO,GAoBPI,IAlBWJ,IACX,IAAK,MAAOG,EAAGE,KAAfR,EACE,GAAID,EAASO,EAAGH,GACd,QAGGM,EAaPC,OAXcP,IACd,IAAK,MAAOG,KAAZN,EACMD,EAASO,EAAGH,IACdH,EAAIU,OAAOJ,EAEd,EAEI,EAuB6BK,CAAkBZ,GAE/C,IACRE,KAoCeW,CAA6BlB,GACpBmB,EAAIC,IACzB,GAAwB,mBAApBpB,OAAAA,EAAAA,EAAWG,QA3FwC,iBAAzCkB,EA2FiCD,IA3F0B,OAANC,GA4FjE,MAAUC,IAAAA,MAAM,iCA5FJD,KA6Fb,EAESE,EAAG,CAACH,EAAcI,KAC5BL,EAAkBC,GAClBlB,EAAMM,IAAIY,EAAO,CACfP,IAAK,IAAMW,EACXC,MAAO,UAKX,GAAIxB,EACF,IAAK,MAAOmB,EAAOI,OACjBD,EAAOH,EAAOI,GAGlB,MAyBcE,EAAIN,IAChBD,EAAkBC,GACblB,EAAMS,IAAIS,IACblB,EAAMM,IAAIY,EA5BUA,KACtB,MAAoC,KAC1BI,EAAkB,KACnBG,EAAmB,KAC5B,MAAMC,EAAa,oBAUnB,OATAC,EAAU,WACR,IACEL,QAAwBzB,EAACqB,EAAO,CAAEU,OAAQF,EAAWE,QAKtD,CAJC,MAAOC,GACPJ,EAAQI,CACT,CAJD,QAKEF,EAAU,IACX,CAPO,EAAA,GASH,CACLhB,IAAK,KACH,GAAIgB,EAAS,MAAAA,EACb,GAAc,OAAVF,EAAgB,MAAMA,EAC1B,UAEFF,MAAO,KACLG,EAAWH,OAAX,EAPG,EAcYO,CAAeZ,GACjC,EA4BH,MAPyC,CACvCM,WACAb,IArBU,CAACO,EAAca,KACzBd,EAAkBC,GACda,MAAAA,GAAAA,EAASC,eACXR,EAASN,GAEX,MAAMe,EAAWjC,EAAMW,IAAIO,GAC3B,IAAKe,EACH,MAAUb,IAAAA,MAAM,0CAElB,OAAea,EAACtB,KACjB,EAYCU,SACAa,MAZahB,IACbD,EAAkBC,GAClBlB,EAAMc,OAAOI,EAAb,EAWAK,MATaL,IAAgB,IAAAiB,EAC7BlB,EAAkBC,GACAK,OAAlBvB,EAAAA,EAAMW,IAAIO,KAAQK,EAAAA,OACnB,EASF"}