@gravity-ui/data-source 0.6.1 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (278) hide show
  1. package/README.md +752 -15
  2. package/build/cjs/core/index.d.ts +3 -1
  3. package/build/cjs/core/index.js +7 -0
  4. package/build/cjs/core/index.js.map +1 -1
  5. package/build/cjs/core/types/DataManager.d.ts +5 -0
  6. package/build/cjs/core/types/DataManager.js.map +1 -1
  7. package/build/cjs/core/types/DataSource.d.ts +15 -19
  8. package/build/cjs/core/types/DataSource.js.map +1 -1
  9. package/build/cjs/core/types/Normalizer.d.ts +29 -0
  10. package/build/cjs/core/types/Normalizer.js +6 -0
  11. package/build/cjs/core/types/Normalizer.js.map +1 -0
  12. package/build/cjs/core/utils/__tests__/composeFullKey.test.js +34 -0
  13. package/build/cjs/core/utils/__tests__/composeFullKey.test.js.map +1 -0
  14. package/build/cjs/core/utils/__tests__/composeKey.test.js +32 -0
  15. package/build/cjs/core/utils/__tests__/composeKey.test.js.map +1 -0
  16. package/build/cjs/core/utils/__tests__/getError.test.js +31 -0
  17. package/build/cjs/core/utils/__tests__/getError.test.js.map +1 -0
  18. package/build/cjs/core/utils/__tests__/getStatus.test.js +49 -0
  19. package/build/cjs/core/utils/__tests__/getStatus.test.js.map +1 -0
  20. package/build/cjs/core/utils/__tests__/hasTag.test.js +31 -0
  21. package/build/cjs/core/utils/__tests__/hasTag.test.js.map +1 -0
  22. package/build/cjs/core/utils/__tests__/mergeStatuses.test.js +25 -0
  23. package/build/cjs/core/utils/__tests__/mergeStatuses.test.js.map +1 -0
  24. package/build/cjs/core/utils/__tests__/skipContext.test.js +70 -0
  25. package/build/cjs/core/utils/__tests__/skipContext.test.js.map +1 -0
  26. package/build/cjs/core/utils/__tests__/withCancellation.test.js +106 -0
  27. package/build/cjs/core/utils/__tests__/withCancellation.test.js.map +1 -0
  28. package/build/cjs/core/utils/__tests__/withCatch.test.js +208 -0
  29. package/build/cjs/core/utils/__tests__/withCatch.test.js.map +1 -0
  30. package/build/cjs/core/utils/withCatch.d.ts +26 -0
  31. package/build/cjs/core/utils/withCatch.js +37 -0
  32. package/build/cjs/core/utils/withCatch.js.map +1 -0
  33. package/build/cjs/react/DataManagerProvider.d.ts +7 -0
  34. package/build/cjs/react/DataManagerProvider.js +19 -0
  35. package/build/cjs/react/DataManagerProvider.js.map +1 -0
  36. package/build/cjs/react/__tests__/DataManagerContext.test.js +47 -0
  37. package/build/cjs/react/__tests__/DataManagerContext.test.js.map +1 -0
  38. package/build/cjs/react/__tests__/withDataManager.test.js +61 -0
  39. package/build/cjs/react/__tests__/withDataManager.test.js.map +1 -0
  40. package/build/cjs/react/components/DataInfiniteLoader/__tests__/DataInfiniteLoader.test.js +187 -0
  41. package/build/cjs/react/components/DataInfiniteLoader/__tests__/DataInfiniteLoader.test.js.map +1 -0
  42. package/build/cjs/react/components/DataLoader/__tests__/DataLoader.test.js +119 -0
  43. package/build/cjs/react/components/DataLoader/__tests__/DataLoader.test.js.map +1 -0
  44. package/build/cjs/react/index.d.ts +2 -0
  45. package/build/cjs/react/index.js +8 -0
  46. package/build/cjs/react/index.js.map +1 -1
  47. package/build/cjs/react/withDataManager.d.ts +1 -1
  48. package/build/cjs/react/withDataManager.js +3 -3
  49. package/build/cjs/react/withDataManager.js.map +1 -1
  50. package/build/cjs/react-query/ClientDataManager.d.ts +11 -2
  51. package/build/cjs/react-query/ClientDataManager.js +76 -9
  52. package/build/cjs/react-query/ClientDataManager.js.map +1 -1
  53. package/build/cjs/react-query/DataSourceProvider.d.ts +7 -0
  54. package/build/cjs/react-query/DataSourceProvider.js +35 -0
  55. package/build/cjs/react-query/DataSourceProvider.js.map +1 -0
  56. package/build/cjs/react-query/__tests__/createQueryNormalizer.test.js +177 -0
  57. package/build/cjs/react-query/__tests__/createQueryNormalizer.test.js.map +1 -0
  58. package/build/cjs/react-query/__tests__/normalizationEdgeCases.test.js +100 -0
  59. package/build/cjs/react-query/__tests__/normalizationEdgeCases.test.js.map +1 -0
  60. package/build/cjs/react-query/__tests__/subscriptions.test.js +1180 -0
  61. package/build/cjs/react-query/__tests__/subscriptions.test.js.map +1 -0
  62. package/build/cjs/react-query/__tests__/threeLevelIntegration.test.js +514 -0
  63. package/build/cjs/react-query/__tests__/threeLevelIntegration.test.js.map +1 -0
  64. package/build/cjs/react-query/__tests__/updateQueriesFromMutationData.test.js +229 -0
  65. package/build/cjs/react-query/__tests__/updateQueriesFromMutationData.test.js.map +1 -0
  66. package/build/cjs/react-query/constants.d.ts +2 -0
  67. package/build/cjs/react-query/constants.js +9 -0
  68. package/build/cjs/react-query/constants.js.map +1 -0
  69. package/build/cjs/react-query/hooks/__tests__/useQueryContext.test.js +55 -0
  70. package/build/cjs/react-query/hooks/__tests__/useQueryContext.test.js.map +1 -0
  71. package/build/cjs/react-query/hooks/__tests__/useQueryData.refetch.test.js +195 -0
  72. package/build/cjs/react-query/hooks/__tests__/useQueryData.refetch.test.js.map +1 -0
  73. package/build/cjs/react-query/hooks/__tests__/useQueryData.test.js +97 -0
  74. package/build/cjs/react-query/hooks/__tests__/useQueryData.test.js.map +1 -0
  75. package/build/cjs/react-query/hooks/__tests__/useQueryResponses.test.js +77 -0
  76. package/build/cjs/react-query/hooks/__tests__/useQueryResponses.test.js.map +1 -0
  77. package/build/cjs/react-query/hooks/__tests__/useRefetchAll.test.js +79 -0
  78. package/build/cjs/react-query/hooks/__tests__/useRefetchAll.test.js.map +1 -0
  79. package/build/cjs/react-query/hooks/__tests__/useRefetchErrored.test.js +117 -0
  80. package/build/cjs/react-query/hooks/__tests__/useRefetchErrored.test.js.map +1 -0
  81. package/build/cjs/react-query/hooks/__tests__/useRefetchInterval.test.js +156 -0
  82. package/build/cjs/react-query/hooks/__tests__/useRefetchInterval.test.js.map +1 -0
  83. package/build/cjs/react-query/hooks/useRefetchInterval.d.ts +1 -1
  84. package/build/cjs/react-query/impl/infinite/factory.d.ts +1 -1
  85. package/build/cjs/react-query/impl/infinite/factory.js.map +1 -1
  86. package/build/cjs/react-query/impl/infinite/hooks.js +4 -1
  87. package/build/cjs/react-query/impl/infinite/hooks.js.map +1 -1
  88. package/build/cjs/react-query/impl/infinite/types.d.ts +9 -9
  89. package/build/cjs/react-query/impl/infinite/types.js.map +1 -1
  90. package/build/cjs/react-query/impl/infinite/utils.js +11 -22
  91. package/build/cjs/react-query/impl/infinite/utils.js.map +1 -1
  92. package/build/cjs/react-query/impl/plain/factory.d.ts +1 -1
  93. package/build/cjs/react-query/impl/plain/factory.js.map +1 -1
  94. package/build/cjs/react-query/impl/plain/hooks.js +4 -1
  95. package/build/cjs/react-query/impl/plain/hooks.js.map +1 -1
  96. package/build/cjs/react-query/impl/plain/types.d.ts +6 -6
  97. package/build/cjs/react-query/impl/plain/types.js.map +1 -1
  98. package/build/cjs/react-query/impl/plain/utils.js +12 -23
  99. package/build/cjs/react-query/impl/plain/utils.js.map +1 -1
  100. package/build/cjs/react-query/index.d.ts +2 -0
  101. package/build/cjs/react-query/index.js +7 -0
  102. package/build/cjs/react-query/index.js.map +1 -1
  103. package/build/cjs/react-query/types/normalizer.d.ts +21 -0
  104. package/build/cjs/react-query/types/normalizer.js +6 -0
  105. package/build/cjs/react-query/types/normalizer.js.map +1 -0
  106. package/build/cjs/react-query/types/options.d.ts +12 -0
  107. package/build/cjs/react-query/types/options.js.map +1 -1
  108. package/build/cjs/react-query/utils/__tests__/formatNullableValue.test.js +27 -0
  109. package/build/cjs/react-query/utils/__tests__/formatNullableValue.test.js.map +1 -0
  110. package/build/cjs/react-query/utils/__tests__/getProgressiveRefetch.test.js +76 -0
  111. package/build/cjs/react-query/utils/__tests__/getProgressiveRefetch.test.js.map +1 -0
  112. package/build/cjs/react-query/utils/__tests__/normalizeStatus.test.js +30 -0
  113. package/build/cjs/react-query/utils/__tests__/normalizeStatus.test.js.map +1 -0
  114. package/build/cjs/react-query/utils/__tests__/notReachable.test.js +35 -0
  115. package/build/cjs/react-query/utils/__tests__/notReachable.test.js.map +1 -0
  116. package/build/cjs/react-query/utils/__tests__/parseNullableValue.test.js +27 -0
  117. package/build/cjs/react-query/utils/__tests__/parseNullableValue.test.js.map +1 -0
  118. package/build/cjs/react-query/utils/formatNullableValue.d.ts +2 -0
  119. package/build/cjs/react-query/utils/formatNullableValue.js +17 -0
  120. package/build/cjs/react-query/utils/formatNullableValue.js.map +1 -0
  121. package/build/cjs/react-query/utils/getProgressiveRefetch.d.ts +2 -2
  122. package/build/cjs/react-query/utils/normalize.d.ts +22 -0
  123. package/build/cjs/react-query/utils/normalize.js +150 -0
  124. package/build/cjs/react-query/utils/normalize.js.map +1 -0
  125. package/build/cjs/react-query/utils/parseNullableValue.d.ts +2 -0
  126. package/build/cjs/react-query/utils/parseNullableValue.js +17 -0
  127. package/build/cjs/react-query/utils/parseNullableValue.js.map +1 -0
  128. package/build/cjs/react-query/utils/warn.d.ts +1 -0
  129. package/build/cjs/react-query/utils/warn.js +15 -0
  130. package/build/cjs/react-query/utils/warn.js.map +1 -0
  131. package/build/cjs/react-query/utils/warnDisabledRefetch.d.ts +1 -0
  132. package/build/cjs/react-query/utils/warnDisabledRefetch.js +11 -0
  133. package/build/cjs/react-query/utils/warnDisabledRefetch.js.map +1 -0
  134. package/build/cjs/setupTests.d.ts +1 -0
  135. package/build/cjs/setupTests.js +4 -0
  136. package/build/cjs/setupTests.js.map +1 -0
  137. package/build/esm/core/index.d.ts +3 -1
  138. package/build/esm/core/index.js +1 -0
  139. package/build/esm/core/index.js.map +1 -1
  140. package/build/esm/core/types/DataManager.d.ts +5 -0
  141. package/build/esm/core/types/DataManager.js.map +1 -1
  142. package/build/esm/core/types/DataSource.d.ts +15 -19
  143. package/build/esm/core/types/DataSource.js.map +1 -1
  144. package/build/esm/core/types/Normalizer.d.ts +29 -0
  145. package/build/esm/core/types/Normalizer.js +2 -0
  146. package/build/esm/core/types/Normalizer.js.map +1 -0
  147. package/build/esm/core/utils/__tests__/composeFullKey.test.js +32 -0
  148. package/build/esm/core/utils/__tests__/composeFullKey.test.js.map +1 -0
  149. package/build/esm/core/utils/__tests__/composeKey.test.js +30 -0
  150. package/build/esm/core/utils/__tests__/composeKey.test.js.map +1 -0
  151. package/build/esm/core/utils/__tests__/getError.test.js +29 -0
  152. package/build/esm/core/utils/__tests__/getError.test.js.map +1 -0
  153. package/build/esm/core/utils/__tests__/getStatus.test.js +47 -0
  154. package/build/esm/core/utils/__tests__/getStatus.test.js.map +1 -0
  155. package/build/esm/core/utils/__tests__/hasTag.test.js +29 -0
  156. package/build/esm/core/utils/__tests__/hasTag.test.js.map +1 -0
  157. package/build/esm/core/utils/__tests__/mergeStatuses.test.js +23 -0
  158. package/build/esm/core/utils/__tests__/mergeStatuses.test.js.map +1 -0
  159. package/build/esm/core/utils/__tests__/skipContext.test.js +67 -0
  160. package/build/esm/core/utils/__tests__/skipContext.test.js.map +1 -0
  161. package/build/esm/core/utils/__tests__/withCancellation.test.js +104 -0
  162. package/build/esm/core/utils/__tests__/withCancellation.test.js.map +1 -0
  163. package/build/esm/core/utils/__tests__/withCatch.test.js +205 -0
  164. package/build/esm/core/utils/__tests__/withCatch.test.js.map +1 -0
  165. package/build/esm/core/utils/withCatch.d.ts +26 -0
  166. package/build/esm/core/utils/withCatch.js +31 -0
  167. package/build/esm/core/utils/withCatch.js.map +1 -0
  168. package/build/esm/react/DataManagerProvider.d.ts +7 -0
  169. package/build/esm/react/DataManagerProvider.js +12 -0
  170. package/build/esm/react/DataManagerProvider.js.map +1 -0
  171. package/build/esm/react/__tests__/DataManagerContext.test.js +44 -0
  172. package/build/esm/react/__tests__/DataManagerContext.test.js.map +1 -0
  173. package/build/esm/react/__tests__/withDataManager.test.js +58 -0
  174. package/build/esm/react/__tests__/withDataManager.test.js.map +1 -0
  175. package/build/esm/react/components/DataInfiniteLoader/__tests__/DataInfiniteLoader.test.js +184 -0
  176. package/build/esm/react/components/DataInfiniteLoader/__tests__/DataInfiniteLoader.test.js.map +1 -0
  177. package/build/esm/react/components/DataLoader/__tests__/DataLoader.test.js +116 -0
  178. package/build/esm/react/components/DataLoader/__tests__/DataLoader.test.js.map +1 -0
  179. package/build/esm/react/index.d.ts +2 -0
  180. package/build/esm/react/index.js +1 -0
  181. package/build/esm/react/index.js.map +1 -1
  182. package/build/esm/react/withDataManager.d.ts +1 -1
  183. package/build/esm/react/withDataManager.js +3 -3
  184. package/build/esm/react/withDataManager.js.map +1 -1
  185. package/build/esm/react-query/ClientDataManager.d.ts +11 -2
  186. package/build/esm/react-query/ClientDataManager.js +70 -3
  187. package/build/esm/react-query/ClientDataManager.js.map +1 -1
  188. package/build/esm/react-query/DataSourceProvider.d.ts +7 -0
  189. package/build/esm/react-query/DataSourceProvider.js +28 -0
  190. package/build/esm/react-query/DataSourceProvider.js.map +1 -0
  191. package/build/esm/react-query/__tests__/createQueryNormalizer.test.js +174 -0
  192. package/build/esm/react-query/__tests__/createQueryNormalizer.test.js.map +1 -0
  193. package/build/esm/react-query/__tests__/normalizationEdgeCases.test.js +98 -0
  194. package/build/esm/react-query/__tests__/normalizationEdgeCases.test.js.map +1 -0
  195. package/build/esm/react-query/__tests__/subscriptions.test.js +1176 -0
  196. package/build/esm/react-query/__tests__/subscriptions.test.js.map +1 -0
  197. package/build/esm/react-query/__tests__/threeLevelIntegration.test.js +511 -0
  198. package/build/esm/react-query/__tests__/threeLevelIntegration.test.js.map +1 -0
  199. package/build/esm/react-query/__tests__/updateQueriesFromMutationData.test.js +227 -0
  200. package/build/esm/react-query/__tests__/updateQueriesFromMutationData.test.js.map +1 -0
  201. package/build/esm/react-query/constants.d.ts +2 -0
  202. package/build/esm/react-query/constants.js +3 -0
  203. package/build/esm/react-query/constants.js.map +1 -0
  204. package/build/esm/react-query/hooks/__tests__/useQueryContext.test.js +52 -0
  205. package/build/esm/react-query/hooks/__tests__/useQueryContext.test.js.map +1 -0
  206. package/build/esm/react-query/hooks/__tests__/useQueryData.refetch.test.js +192 -0
  207. package/build/esm/react-query/hooks/__tests__/useQueryData.refetch.test.js.map +1 -0
  208. package/build/esm/react-query/hooks/__tests__/useQueryData.test.js +95 -0
  209. package/build/esm/react-query/hooks/__tests__/useQueryData.test.js.map +1 -0
  210. package/build/esm/react-query/hooks/__tests__/useQueryResponses.test.js +74 -0
  211. package/build/esm/react-query/hooks/__tests__/useQueryResponses.test.js.map +1 -0
  212. package/build/esm/react-query/hooks/__tests__/useRefetchAll.test.js +77 -0
  213. package/build/esm/react-query/hooks/__tests__/useRefetchAll.test.js.map +1 -0
  214. package/build/esm/react-query/hooks/__tests__/useRefetchErrored.test.js +115 -0
  215. package/build/esm/react-query/hooks/__tests__/useRefetchErrored.test.js.map +1 -0
  216. package/build/esm/react-query/hooks/__tests__/useRefetchInterval.test.js +154 -0
  217. package/build/esm/react-query/hooks/__tests__/useRefetchInterval.test.js.map +1 -0
  218. package/build/esm/react-query/hooks/useRefetchInterval.d.ts +1 -1
  219. package/build/esm/react-query/impl/infinite/factory.d.ts +1 -1
  220. package/build/esm/react-query/impl/infinite/factory.js.map +1 -1
  221. package/build/esm/react-query/impl/infinite/hooks.js +5 -2
  222. package/build/esm/react-query/impl/infinite/hooks.js.map +1 -1
  223. package/build/esm/react-query/impl/infinite/types.d.ts +9 -9
  224. package/build/esm/react-query/impl/infinite/types.js.map +1 -1
  225. package/build/esm/react-query/impl/infinite/utils.js +9 -20
  226. package/build/esm/react-query/impl/infinite/utils.js.map +1 -1
  227. package/build/esm/react-query/impl/plain/factory.d.ts +1 -1
  228. package/build/esm/react-query/impl/plain/factory.js.map +1 -1
  229. package/build/esm/react-query/impl/plain/hooks.js +5 -2
  230. package/build/esm/react-query/impl/plain/hooks.js.map +1 -1
  231. package/build/esm/react-query/impl/plain/types.d.ts +6 -6
  232. package/build/esm/react-query/impl/plain/types.js.map +1 -1
  233. package/build/esm/react-query/impl/plain/utils.js +10 -21
  234. package/build/esm/react-query/impl/plain/utils.js.map +1 -1
  235. package/build/esm/react-query/index.d.ts +2 -0
  236. package/build/esm/react-query/index.js +1 -0
  237. package/build/esm/react-query/index.js.map +1 -1
  238. package/build/esm/react-query/types/normalizer.d.ts +21 -0
  239. package/build/esm/react-query/types/normalizer.js +2 -0
  240. package/build/esm/react-query/types/normalizer.js.map +1 -0
  241. package/build/esm/react-query/types/options.d.ts +12 -0
  242. package/build/esm/react-query/types/options.js.map +1 -1
  243. package/build/esm/react-query/utils/__tests__/formatNullableValue.test.js +25 -0
  244. package/build/esm/react-query/utils/__tests__/formatNullableValue.test.js.map +1 -0
  245. package/build/esm/react-query/utils/__tests__/getProgressiveRefetch.test.js +74 -0
  246. package/build/esm/react-query/utils/__tests__/getProgressiveRefetch.test.js.map +1 -0
  247. package/build/esm/react-query/utils/__tests__/normalizeStatus.test.js +28 -0
  248. package/build/esm/react-query/utils/__tests__/normalizeStatus.test.js.map +1 -0
  249. package/build/esm/react-query/utils/__tests__/notReachable.test.js +33 -0
  250. package/build/esm/react-query/utils/__tests__/notReachable.test.js.map +1 -0
  251. package/build/esm/react-query/utils/__tests__/parseNullableValue.test.js +25 -0
  252. package/build/esm/react-query/utils/__tests__/parseNullableValue.test.js.map +1 -0
  253. package/build/esm/react-query/utils/formatNullableValue.d.ts +2 -0
  254. package/build/esm/react-query/utils/formatNullableValue.js +11 -0
  255. package/build/esm/react-query/utils/formatNullableValue.js.map +1 -0
  256. package/build/esm/react-query/utils/getProgressiveRefetch.d.ts +2 -2
  257. package/build/esm/react-query/utils/normalize.d.ts +22 -0
  258. package/build/esm/react-query/utils/normalize.js +143 -0
  259. package/build/esm/react-query/utils/normalize.js.map +1 -0
  260. package/build/esm/react-query/utils/parseNullableValue.d.ts +2 -0
  261. package/build/esm/react-query/utils/parseNullableValue.js +11 -0
  262. package/build/esm/react-query/utils/parseNullableValue.js.map +1 -0
  263. package/build/esm/react-query/utils/warn.d.ts +1 -0
  264. package/build/esm/react-query/utils/warn.js +9 -0
  265. package/build/esm/react-query/utils/warn.js.map +1 -0
  266. package/build/esm/react-query/utils/warnDisabledRefetch.d.ts +1 -0
  267. package/build/esm/react-query/utils/warnDisabledRefetch.js +5 -0
  268. package/build/esm/react-query/utils/warnDisabledRefetch.js.map +1 -0
  269. package/build/esm/setupTests.d.ts +1 -0
  270. package/build/esm/setupTests.js +2 -0
  271. package/build/esm/setupTests.js.map +1 -0
  272. package/package.json +18 -13
  273. package/build/cjs/react-query/impl/utils.d.ts +0 -4
  274. package/build/cjs/react-query/impl/utils.js +0 -27
  275. package/build/cjs/react-query/impl/utils.js.map +0 -1
  276. package/build/esm/react-query/impl/utils.d.ts +0 -4
  277. package/build/esm/react-query/impl/utils.js +0 -21
  278. package/build/esm/react-query/impl/utils.js.map +0 -1
package/README.md CHANGED
@@ -10,16 +10,45 @@ npm install @gravity-ui/data-source @tanstack/react-query
10
10
 
11
11
  `@tanstack/react-query` is a peer dependency.
12
12
 
13
- ## Getting started
13
+ ## Quick Start
14
14
 
15
- Firstly, define a type of error and make your constructors for data sources and your error based on default constructors (makePlainQueryDataSource / makeInfiniteQueryDataSource). For example:
15
+ ### 1. Setup DataManager
16
+
17
+ First, create and provide a `DataManager` in your application:
18
+
19
+ ```tsx
20
+ import React from 'react';
21
+ import {ClientDataManager, DataManagerContext} from '@gravity-ui/data-source';
22
+
23
+ const dataManager = new ClientDataManager({
24
+ defaultOptions: {
25
+ queries: {
26
+ staleTime: 5 * 60 * 1000, // 5 minutes
27
+ retry: 3,
28
+ },
29
+ // ... other react-query options
30
+ },
31
+ });
32
+
33
+ function App() {
34
+ return (
35
+ <DataManagerContext.Provider value={dataManager}>
36
+ <YourApplication />
37
+ </DataManagerContext.Provider>
38
+ );
39
+ }
40
+ ```
41
+
42
+ ### 2. Define Error Types and Wrappers
43
+
44
+ Define a type of error and make your constructors for data sources based on default constructors:
16
45
 
17
46
  ```ts
18
47
  import {makePlainQueryDataSource as makePlainQueryDataSourceBase} from '@gravity-ui/data-source';
19
48
 
20
49
  export interface ApiError {
50
+ code: number;
21
51
  title: string;
22
- code?: number;
23
52
  description?: string;
24
53
  }
25
54
 
@@ -30,7 +59,9 @@ export const makePlainQueryDataSource = <TParams, TRequest, TResponse, TData, TE
30
59
  };
31
60
  ```
32
61
 
33
- Write a `DataLoader` component based on default. This is convenient to define your display of the loading status and errors. For example:
62
+ ### 3. Create Custom DataLoader Component
63
+
64
+ Write a `DataLoader` component based on default to define your display of loading status and errors:
34
65
 
35
66
  ```tsx
36
67
  import {
@@ -46,37 +77,743 @@ export interface DataLoaderProps
46
77
  }
47
78
 
48
79
  export const DataLoader: React.FC<DataLoaderProps> = ({
49
- LoadingView = YourLoader,
50
- ErrorView = YourError,
80
+ LoadingView = YourLoader, // You can use your own loader component
81
+ ErrorView = YourError, // You can use your own error component
51
82
  ...restProps
52
83
  }) => {
53
84
  return <DataLoaderBase LoadingView={LoadingView} ErrorView={ErrorView} {...restProps} />;
54
85
  };
55
86
  ```
56
87
 
57
- Define your first data source:
88
+ ### 4. Define Your First Data Source
58
89
 
59
90
  ```ts
60
- export const objectDataSource = makePlainQueryDataSource({
91
+ import {skipContext} from '@gravity-ui/data-source';
92
+
93
+ // Your API function
94
+ import {fetchUser} from './api';
95
+
96
+ export const userDataSource = makePlainQueryDataSource({
61
97
  // Keys have to be unique. Maybe you should create a helper for making names of data sources
62
- name: 'object',
63
- // skipContext is just a helper to skip 2 first parameters in the function (context and fetchContext)
64
- fetch: skipContext(objectFetch),
98
+ name: 'user',
99
+ // skipContext is a helper to skip 2 first parameters in the function (context and fetchContext)
100
+ fetch: skipContext(fetchUser),
101
+ // Optional: generate tags for advanced cache invalidation
102
+ tags: (params) => [`user:${params.userId}`, 'users'],
65
103
  });
66
104
  ```
67
105
 
68
- Use it in the application:
106
+ ### 5. Use in Components
69
107
 
70
108
  ```tsx
71
109
  import {useQueryData} from '@gravity-ui/data-source';
72
110
 
73
- export const SomeComponent: React.FC = () => {
74
- const {data, status, error, refetch} = useQueryData(objectDataSource, {objectId: 1});
111
+ export const UserProfile: React.FC<{userId: number}> = ({userId}) => {
112
+ const {data, status, error, refetch} = useQueryData(userDataSource, {userId});
75
113
 
76
114
  return (
77
115
  <DataLoader status={status} error={error} errorAction={refetch}>
78
- {data && <ObjectComponent object={data} />}
116
+ {data && <UserCard user={data} />}
117
+ </DataLoader>
118
+ );
119
+ };
120
+ ```
121
+
122
+ ## Core Concepts
123
+
124
+ ### Data Source Types
125
+
126
+ The library provides two main types of data sources:
127
+
128
+ #### Plain Query Data Source
129
+
130
+ For simple request/response patterns:
131
+
132
+ ```ts
133
+ const userDataSource = makePlainQueryDataSource({
134
+ name: 'user',
135
+ fetch: skipContext(async (params: {userId: number}) => {
136
+ const response = await fetch(`/api/users/${params.userId}`);
137
+ return response.json();
138
+ }),
139
+ });
140
+ ```
141
+
142
+ #### Infinite Query Data Source
143
+
144
+ For pagination and infinite scrolling:
145
+
146
+ ```ts
147
+ const postsDataSource = makeInfiniteQueryDataSource({
148
+ name: 'posts',
149
+ fetch: skipContext(async (params: {page: number; limit: number}) => {
150
+ const response = await fetch(`/api/posts?page=${params.page}&limit=${params.limit}`);
151
+ return response.json();
152
+ }),
153
+ next: (lastPage, allPages) => {
154
+ if (lastPage.hasNext) {
155
+ return {page: allPages.length + 1, limit: 20};
156
+ }
157
+ return undefined;
158
+ },
159
+ });
160
+ ```
161
+
162
+ ### Status Management
163
+
164
+ The library normalizes query states into three simple statuses:
165
+
166
+ - `loading` - Actual data loading. The same as `isLoading` in React Query
167
+ - `success` - Data available (may be skipped using idle)
168
+ - `error` - Failed to fetch data
169
+
170
+ ### Idle Concept
171
+
172
+ The library provides a special `idle` symbol for skipping query execution:
173
+
174
+ ```ts
175
+ import {idle} from '@gravity-ui/data-source';
176
+
177
+ const UserProfile: React.FC<{userId?: number}> = ({userId}) => {
178
+ // Query won't execute if userId is not defined
179
+ const {data, status} = useQueryData(userDataSource, userId ? {userId} : idle);
180
+
181
+ return (
182
+ <DataLoader status={status} error={null}>
183
+ {data && <UserCard user={data} />}
79
184
  </DataLoader>
80
185
  );
81
186
  };
82
187
  ```
188
+
189
+ When parameters equal `idle`:
190
+
191
+ - Query doesn't execute
192
+ - Status remains `success`
193
+ - Data remains `undefined`
194
+ - Component can safely render without loading
195
+
196
+ **Benefits of `idle`:**
197
+
198
+ 1. **Type Safety** - TypeScript correctly infers types for conditional parameters
199
+ 2. **Performance** - Avoids unnecessary server requests
200
+ 3. **Logic Simplicity** - No need to manage additional `enabled` state
201
+ 4. **Consistency** - Unified approach for all conditional queries
202
+
203
+ This is especially useful for conditional queries when you want to load data only under certain conditions while maintaining type safety.
204
+
205
+ ## API Reference
206
+
207
+ ### Creating Data Sources
208
+
209
+ #### `makePlainQueryDataSource(config)`
210
+
211
+ Creates a plain query data source for simple request/response patterns.
212
+
213
+ ```ts
214
+ const dataSource = makePlainQueryDataSource({
215
+ name: 'unique-name',
216
+ fetch: skipContext(fetchFunction),
217
+ transformParams: (params) => transformedRequest,
218
+ transformResponse: (response) => transformedData,
219
+ tags: (params) => ['tag1', 'tag2'],
220
+ options: {
221
+ staleTime: 60000,
222
+ retry: 3,
223
+ // ... other react-query options
224
+ },
225
+ });
226
+ ```
227
+
228
+ **Parameters:**
229
+
230
+ - `name` - Unique identifier for the data source
231
+ - `fetch` - Function that performs the actual data fetching
232
+ - `transformParams` (optional) - Transform input parameters before request
233
+ - `transformResponse` (optional) - Transform response data
234
+ - `tags` (optional) - Generate cache tags for invalidation
235
+ - `options` (optional) - React Query options
236
+
237
+ #### `makeInfiniteQueryDataSource(config)`
238
+
239
+ Creates an infinite query data source for pagination and infinite scrolling patterns.
240
+
241
+ ```ts
242
+ const infiniteDataSource = makeInfiniteQueryDataSource({
243
+ name: 'infinite-data',
244
+ fetch: skipContext(fetchFunction),
245
+ next: (lastPage, allPages) => nextPageParam || undefined,
246
+ prev: (firstPage, allPages) => prevPageParam || undefined,
247
+ // ... other options same as plain
248
+ });
249
+ ```
250
+
251
+ **Additional Parameters:**
252
+
253
+ - `next` - Function to determine next page parameters
254
+ - `prev` (optional) - Function to determine previous page parameters
255
+
256
+ ### React Hooks
257
+
258
+ #### `useQueryData(dataSource, params, options?)`
259
+
260
+ Main hook for fetching data with a data source.
261
+
262
+ ```ts
263
+ const {data, status, error, refetch, ...rest} = useQueryData(
264
+ userDataSource,
265
+ {userId: 123},
266
+ {
267
+ enabled: true,
268
+ refetchInterval: 30000,
269
+ },
270
+ );
271
+ ```
272
+
273
+ **Returns:**
274
+
275
+ - `data` - The fetched data
276
+ - `status` - Current status ('loading' | 'success' | 'error')
277
+ - `error` - Error object if request failed
278
+ - `refetch` - Function to manually refetch data
279
+ - Other React Query properties
280
+
281
+ #### `useQueryResponses(responses)`
282
+
283
+ Combines multiple query responses into a single state.
284
+
285
+ ```ts
286
+ const user = useQueryData(userDataSource, {userId});
287
+ const posts = useQueryData(postsDataSource, {userId});
288
+
289
+ const {status, error, refetch, refetchErrored} = useQueryResponses([user, posts]);
290
+ ```
291
+
292
+ **Returns:**
293
+
294
+ - `status` - Combined status of all queries
295
+ - `error` - First error encountered
296
+ - `refetch` - Function to refetch all queries
297
+ - `refetchErrored` - Function to refetch only failed queries
298
+
299
+ #### `useRefetchAll(states)`
300
+
301
+ Creates a callback to refetch multiple queries.
302
+
303
+ ```ts
304
+ const refetchAll = useRefetchAll([user, posts, comments]);
305
+ // refetchAll() will trigger refetch for all queries
306
+ ```
307
+
308
+ #### `useRefetchErrored(states)`
309
+
310
+ Creates a callback to refetch only failed queries.
311
+
312
+ ```ts
313
+ const refetchErrored = useRefetchErrored([user, posts, comments]);
314
+ // refetchErrored() will only refetch queries with errors
315
+ ```
316
+
317
+ #### `useDataManager()`
318
+
319
+ Returns the DataManager from context.
320
+
321
+ ```ts
322
+ const dataManager = useDataManager();
323
+ await dataManager.invalidateTag('users');
324
+ ```
325
+
326
+ #### `useQueryContext()`
327
+
328
+ Returns the query context (for building custom data hooks base on react-query).
329
+
330
+ ### React Components
331
+
332
+ #### `<DataLoader />`
333
+
334
+ Component for handling loading states and errors.
335
+
336
+ ```tsx
337
+ <DataLoader
338
+ status={status}
339
+ error={error}
340
+ errorAction={refetch}
341
+ LoadingView={SpinnerComponent}
342
+ ErrorView={ErrorComponent}
343
+ loadingViewProps={{size: 'large'}}
344
+ errorViewProps={{showDetails: true}}
345
+ >
346
+ {data && <YourContent data={data} />}
347
+ </DataLoader>
348
+ ```
349
+
350
+ **Props:**
351
+
352
+ - `status` - Current loading status
353
+ - `error` - Error object
354
+ - `errorAction` - Function or action config for error retry
355
+ - `LoadingView` - Component to show during loading
356
+ - `ErrorView` - Component to show on error
357
+ - `loadingViewProps` - Props passed to LoadingView
358
+ - `errorViewProps` - Props passed to ErrorView
359
+
360
+ #### `<DataInfiniteLoader />`
361
+
362
+ Specialized component for infinite queries.
363
+
364
+ ```tsx
365
+ <DataInfiniteLoader
366
+ status={status}
367
+ error={error}
368
+ hasNextPage={hasNextPage}
369
+ fetchNextPage={fetchNextPage}
370
+ isFetchingNextPage={isFetchingNextPage}
371
+ LoadingView={SpinnerComponent}
372
+ ErrorView={ErrorComponent}
373
+ MoreView={LoadMoreButton}
374
+ >
375
+ {data.map((item) => (
376
+ <Item key={item.id} data={item} />
377
+ ))}
378
+ </DataInfiniteLoader>
379
+ ```
380
+
381
+ **Additional Props:**
382
+
383
+ - `hasNextPage` - Whether more pages are available
384
+ - `fetchNextPage` - Function to fetch next page
385
+ - `isFetchingNextPage` - Whether next page is being fetched
386
+ - `MoreView` - Component for "load more" button
387
+
388
+ #### `withDataManager(Component)`
389
+
390
+ HOC that injects DataManager as a prop.
391
+
392
+ ```tsx
393
+ const MyComponent = withDataManager<Props>(({dataManager, ...props}) => {
394
+ // Component has access to dataManager
395
+ return <div>...</div>;
396
+ });
397
+ ```
398
+
399
+ ### Data Management
400
+
401
+ #### `ClientDataManager`
402
+
403
+ Main class for data management.
404
+
405
+ ```ts
406
+ const dataManager = new ClientDataManager({
407
+ defaultOptions: {
408
+ queries: {
409
+ staleTime: 300000, // 5 minutes
410
+ retry: 3,
411
+ refetchOnWindowFocus: false,
412
+ },
413
+ },
414
+ });
415
+ ```
416
+
417
+ **Methods:**
418
+
419
+ ##### `invalidateTag(tag, options?)`
420
+
421
+ Invalidate all queries with a specific tag.
422
+
423
+ ```ts
424
+ await dataManager.invalidateTag('users');
425
+ await dataManager.invalidateTag('posts', {
426
+ repeat: {count: 3, interval: 1000}, // Retry invalidation
427
+ });
428
+ ```
429
+
430
+ ##### `invalidateTags(tags, options?)`
431
+
432
+ Invalidate queries that have all specified tags.
433
+
434
+ ```ts
435
+ await dataManager.invalidateTags(['user', 'profile']);
436
+ ```
437
+
438
+ ##### `invalidateSource(dataSource, options?)`
439
+
440
+ Invalidate all queries for a data source.
441
+
442
+ ```ts
443
+ await dataManager.invalidateSource(userDataSource);
444
+ ```
445
+
446
+ ##### `invalidateParams(dataSource, params, options?)`
447
+
448
+ Invalidate a specific query with exact parameters.
449
+
450
+ ```ts
451
+ await dataManager.invalidateParams(userDataSource, {userId: 123});
452
+ ```
453
+
454
+ ##### `resetSource(dataSource)`
455
+
456
+ Reset (clear) all cached data for a data source.
457
+
458
+ ```ts
459
+ await dataManager.resetSource(userDataSource);
460
+ ```
461
+
462
+ ##### `resetParams(dataSource, params)`
463
+
464
+ Reset cached data for specific parameters.
465
+
466
+ ```ts
467
+ await dataManager.resetParams(userDataSource, {userId: 123});
468
+ ```
469
+
470
+ ##### `invalidateSourceTags(dataSource, params, options?)`
471
+
472
+ Invalidate queries based on tags generated by a data source.
473
+
474
+ ```ts
475
+ await dataManager.invalidateSourceTags(userDataSource, {userId: 123});
476
+ ```
477
+
478
+ ### Utilities
479
+
480
+ #### `skipContext(fetchFunction)`
481
+
482
+ Utility to adapt existing fetch functions to data source interface.
483
+
484
+ ```ts
485
+ // Existing function
486
+ async function fetchUser(params: {userId: number}) {
487
+ // ...
488
+ }
489
+
490
+ // Adapted for data source
491
+ const dataSource = makePlainQueryDataSource({
492
+ name: 'user',
493
+ fetch: skipContext(fetchUser), // Skips context and fetchContext params
494
+ });
495
+ ```
496
+
497
+ #### `withCatch(fetchFunction, errorHandler)`
498
+
499
+ Adds standardized error handling to fetch functions.
500
+
501
+ ```ts
502
+ const safeFetch = withCatch(fetchUser, (error) => ({error: true, message: error.message}));
503
+ ```
504
+
505
+ #### `withCancellation(fetchFunction)`
506
+
507
+ Adds cancellation support to fetch functions.
508
+
509
+ ```ts
510
+ const cancellableFetch = withCancellation(fetchFunction);
511
+ // Automatically handles AbortSignal from React Query
512
+ ```
513
+
514
+ #### `getProgressiveRefetch(options)`
515
+
516
+ Creates a progressive refetch interval function.
517
+
518
+ ```ts
519
+ const progressiveRefetch = getProgressiveRefetch({
520
+ minInterval: 1000, // Start with 1 second
521
+ maxInterval: 30000, // Max 30 seconds
522
+ multiplier: 2, // Double each time
523
+ });
524
+
525
+ const dataSource = makePlainQueryDataSource({
526
+ name: 'data',
527
+ fetch: skipContext(fetchData),
528
+ options: {
529
+ refetchInterval: progressiveRefetch,
530
+ },
531
+ });
532
+ ```
533
+
534
+ #### `normalizeStatus(status, fetchStatus)`
535
+
536
+ Converts React Query statuses to DataLoader status.
537
+
538
+ ```ts
539
+ const status = normalizeStatus('pending', 'fetching'); // 'loading'
540
+ ```
541
+
542
+ #### Status and Error Utilities
543
+
544
+ ```ts
545
+ // Get combined status from multiple states
546
+ const status = getStatus([user, posts, comments]);
547
+
548
+ // Get first error from multiple states
549
+ const error = getError([user, posts, comments]);
550
+
551
+ // Merge multiple statuses
552
+ const combinedStatus = mergeStatuses(['loading', 'success', 'error']); // 'error'
553
+
554
+ // Check if query key has a tag
555
+ const hasUserTag = hasTag(queryKey, 'users');
556
+ ```
557
+
558
+ #### Key Composition Utilities
559
+
560
+ ```ts
561
+ // Compose cache key for a data source
562
+ const key = composeKey(userDataSource, {userId: 123});
563
+
564
+ // Compose full key including tags
565
+ const fullKey = composeFullKey(userDataSource, {userId: 123});
566
+ ```
567
+
568
+ #### Constants
569
+
570
+ ```ts
571
+ import {idle} from '@gravity-ui/data-source';
572
+
573
+ // Special symbol for skipping query execution
574
+ const params = shouldFetch ? {userId: 123} : idle;
575
+
576
+ // Type-safe alternative to enabled: false
577
+ // Instead of:
578
+ const {data} = useQueryData(userDataSource, {userId: userId || ''}, {enabled: Boolean(userId)});
579
+
580
+ // Use:
581
+ const {data} = useQueryData(userDataSource, userId ? {userId} : idle);
582
+ // TypeScript correctly infers types for both branches
583
+ ```
584
+
585
+ #### Query Options Composition
586
+
587
+ ```ts
588
+ // Compose React Query options for plain queries
589
+ const plainOptions = composePlainQueryOptions(context, dataSource, params, options);
590
+
591
+ // Compose React Query options for infinite queries
592
+ const infiniteOptions = composeInfiniteQueryOptions(context, dataSource, params, options);
593
+ ```
594
+
595
+ **Note:** These functions are primarily for internal use when creating custom data source implementations.
596
+
597
+ ## Advanced Patterns
598
+
599
+ ### Conditional Queries with Idle
600
+
601
+ Use `idle` to create conditional queries:
602
+
603
+ ```ts
604
+ import {idle} from '@gravity-ui/data-source';
605
+
606
+ const ConditionalDataComponent: React.FC<{
607
+ userId?: number;
608
+ shouldLoadPosts: boolean;
609
+ }> = ({userId, shouldLoadPosts}) => {
610
+ // Load user only if userId is defined
611
+ const user = useQueryData(
612
+ userDataSource,
613
+ userId ? {userId} : idle
614
+ );
615
+
616
+ // Load posts only if user is loaded and flag is enabled
617
+ const posts = useQueryData(
618
+ userPostsDataSource,
619
+ user.data && shouldLoadPosts ? {userId: user.data.id} : idle
620
+ );
621
+
622
+ const combined = useQueryResponses([user, posts]);
623
+
624
+ return (
625
+ <DataLoader status={combined.status} error={combined.error}>
626
+ <div>
627
+ {user.data && <UserInfo user={user.data} />}
628
+ {posts.data && <UserPosts posts={posts.data} />}
629
+ </div>
630
+ </DataLoader>
631
+ );
632
+ };
633
+ ```
634
+
635
+ ### Data Transformation
636
+
637
+ Transform request parameters and response data:
638
+
639
+ ```ts
640
+ const apiDataSource = makePlainQueryDataSource({
641
+ name: 'api-data',
642
+ transformParams: (params: {id: number}) => ({
643
+ userId: params.id,
644
+ apiVersion: 'v2',
645
+ format: 'json',
646
+ }),
647
+ transformResponse: (response: ApiResponse) => ({
648
+ user: response.data.user,
649
+ metadata: response.meta,
650
+ }),
651
+ fetch: skipContext(apiFetch),
652
+ });
653
+ ```
654
+
655
+ ### Tag-Based Cache Invalidation
656
+
657
+ Use tags for sophisticated cache management:
658
+
659
+ ```ts
660
+ const userDataSource = makePlainQueryDataSource({
661
+ name: 'user',
662
+ tags: (params) => [`user:${params.userId}`, 'users', 'profiles'],
663
+ fetch: skipContext(fetchUser),
664
+ });
665
+
666
+ const userPostsDataSource = makePlainQueryDataSource({
667
+ name: 'user-posts',
668
+ tags: (params) => [`user:${params.userId}`, 'posts'],
669
+ fetch: skipContext(fetchUserPosts),
670
+ });
671
+
672
+ // Invalidate all data for specific user
673
+ await dataManager.invalidateTag('user:123');
674
+
675
+ // Invalidate all user-related data
676
+ await dataManager.invalidateTag('users');
677
+ ```
678
+
679
+ ### Error Handling with Types
680
+
681
+ Create type-safe error handling:
682
+
683
+ ```ts
684
+ interface ApiError {
685
+ code: number;
686
+ message: string;
687
+ details?: Record<string, unknown>;
688
+ }
689
+
690
+ const ErrorView: React.FC<ErrorViewProps<ApiError>> = ({error, action}) => (
691
+ <div className="error">
692
+ <h3>Error {error?.code}</h3>
693
+ <p>{error?.message}</p>
694
+ {action && (
695
+ <button onClick={action.handler}>
696
+ {action.children || 'Retry'}
697
+ </button>
698
+ )}
699
+ </div>
700
+ );
701
+ ```
702
+
703
+ ### Infinite Queries with Complex Pagination
704
+
705
+ Handle complex pagination scenarios:
706
+
707
+ ```ts
708
+ interface PaginationParams {
709
+ cursor?: string;
710
+ limit?: number;
711
+ filters?: Record<string, unknown>;
712
+ }
713
+
714
+ interface PaginatedResponse<T> {
715
+ data: T[];
716
+ nextCursor?: string;
717
+ hasMore: boolean;
718
+ }
719
+
720
+ const infiniteDataSource = makeInfiniteQueryDataSource({
721
+ name: 'paginated-data',
722
+ fetch: skipContext(async (params: PaginationParams) => {
723
+ const response = await fetch(`/api/data?${new URLSearchParams(params)}`);
724
+ return response.json() as PaginatedResponse<DataItem>;
725
+ }),
726
+ next: (lastPage) => {
727
+ if (lastPage.hasMore && lastPage.nextCursor) {
728
+ return {cursor: lastPage.nextCursor, limit: 20};
729
+ }
730
+ return undefined;
731
+ },
732
+ });
733
+ ```
734
+
735
+ ### Combining Multiple Data Sources
736
+
737
+ Combine data from multiple sources:
738
+
739
+ ```ts
740
+ const UserProfile: React.FC<{userId: number}> = ({userId}) => {
741
+ const user = useQueryData(userDataSource, {userId});
742
+ const posts = useQueryData(userPostsDataSource, {userId});
743
+ const followers = useQueryData(userFollowersDataSource, {userId});
744
+
745
+ const combined = useQueryResponses([user, posts, followers]);
746
+
747
+ return (
748
+ <DataLoader
749
+ status={combined.status}
750
+ error={combined.error}
751
+ errorAction={combined.refetchErrored} // Only retry failed requests
752
+ LoadingView={ProfileSkeleton}
753
+ ErrorView={ProfileError}
754
+ >
755
+ {user && posts && followers && (
756
+ <div>
757
+ <UserInfo user={user.data} />
758
+ <UserPosts posts={posts.data} />
759
+ <UserFollowers followers={followers.data} />
760
+ </div>
761
+ )}
762
+ </DataLoader>
763
+ );
764
+ };
765
+ ```
766
+
767
+ ## TypeScript Support
768
+
769
+ The library is built with TypeScript-first approach and provides full type inference:
770
+
771
+ ```ts
772
+ // Types are automatically inferred
773
+ const userDataSource = makePlainQueryDataSource({
774
+ name: 'user',
775
+ fetch: skipContext(async (params: {userId: number}): Promise<User> => {
776
+ // Return type is inferred as User
777
+ }),
778
+ });
779
+
780
+ // Hook return type is automatically typed
781
+ const {data} = useQueryData(userDataSource, {userId: 123});
782
+ // data is typed as User | undefined
783
+ ```
784
+
785
+ ### Custom Error Types
786
+
787
+ Define and use custom error types:
788
+
789
+ ```ts
790
+ interface ValidationError {
791
+ field: string;
792
+ message: string;
793
+ }
794
+
795
+ interface ApiError {
796
+ type: 'network' | 'validation' | 'server';
797
+ message: string;
798
+ validation?: ValidationError[];
799
+ }
800
+
801
+ const typedDataSource = makePlainQueryDataSource<
802
+ {id: number}, // Params type
803
+ {id: number}, // Request type
804
+ ApiResponse, // Response type
805
+ User, // Data type
806
+ ApiError // Error type
807
+ >({
808
+ name: 'typed-user',
809
+ fetch: skipContext(fetchUser),
810
+ });
811
+ ```
812
+
813
+ ## Contributing
814
+
815
+ Please read [CONTRIBUTING.md](CONTRIBUTING.md) for details on our code of conduct and the process for submitting pull requests.
816
+
817
+ ## License
818
+
819
+ MIT License. See [LICENSE](LICENSE) file for details.