@snail-js/api 0.1.27 → 1.0.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 (313) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +125 -605
  3. package/dist/adapter/plain.d.ts +12 -0
  4. package/dist/adapter/plain.d.ts.map +1 -0
  5. package/dist/adapter/react.d.ts +14 -0
  6. package/dist/adapter/react.d.ts.map +1 -0
  7. package/dist/adapter/registry.d.ts +16 -0
  8. package/dist/adapter/registry.d.ts.map +1 -0
  9. package/dist/adapter/vue.d.ts +15 -0
  10. package/dist/adapter/vue.d.ts.map +1 -0
  11. package/dist/chunks/custom-UchOSg_s.js +466 -0
  12. package/dist/chunks/custom-UchOSg_s.js.map +1 -0
  13. package/dist/chunks/download-lBC7oOTT.js +160 -0
  14. package/dist/chunks/download-lBC7oOTT.js.map +1 -0
  15. package/dist/chunks/object-Ct0w-h4i.js +74 -0
  16. package/dist/chunks/object-Ct0w-h4i.js.map +1 -0
  17. package/dist/chunks/plugin-D3RDffDG.js +742 -0
  18. package/dist/chunks/plugin-D3RDffDG.js.map +1 -0
  19. package/dist/chunks/react-C1em-gxy.js +62 -0
  20. package/dist/chunks/react-C1em-gxy.js.map +1 -0
  21. package/dist/chunks/request-qeRhihEF.js +40 -0
  22. package/dist/chunks/request-qeRhihEF.js.map +1 -0
  23. package/dist/chunks/response-D-ubkAFl.js +127 -0
  24. package/dist/chunks/response-D-ubkAFl.js.map +1 -0
  25. package/dist/chunks/use-download-FAjtdJ58.js +1816 -0
  26. package/dist/chunks/use-download-FAjtdJ58.js.map +1 -0
  27. package/dist/chunks/vue-Dd9EhkQ3.js +34 -0
  28. package/dist/chunks/vue-Dd9EhkQ3.js.map +1 -0
  29. package/dist/core/args.d.ts +54 -0
  30. package/dist/core/args.d.ts.map +1 -0
  31. package/dist/core/connection.d.ts +15 -0
  32. package/dist/core/connection.d.ts.map +1 -0
  33. package/dist/core/context.d.ts +124 -0
  34. package/dist/core/context.d.ts.map +1 -0
  35. package/dist/core/http-stream.d.ts +28 -0
  36. package/dist/core/http-stream.d.ts.map +1 -0
  37. package/dist/core/logger.d.ts +22 -0
  38. package/dist/core/logger.d.ts.map +1 -0
  39. package/dist/core/metadata.d.ts +92 -0
  40. package/dist/core/metadata.d.ts.map +1 -0
  41. package/dist/core/metadata.keys.d.ts +41 -0
  42. package/dist/core/metadata.keys.d.ts.map +1 -0
  43. package/dist/core/method.d.ts +177 -0
  44. package/dist/core/method.d.ts.map +1 -0
  45. package/dist/core/plugin-manager.d.ts +106 -0
  46. package/dist/core/plugin-manager.d.ts.map +1 -0
  47. package/dist/core/plugin.d.ts +113 -0
  48. package/dist/core/plugin.d.ts.map +1 -0
  49. package/dist/core/reconnect.d.ts +22 -0
  50. package/dist/core/reconnect.d.ts.map +1 -0
  51. package/dist/core/resolve-stream.d.ts +45 -0
  52. package/dist/core/resolve-stream.d.ts.map +1 -0
  53. package/dist/core/resolve.d.ts +50 -0
  54. package/dist/core/resolve.d.ts.map +1 -0
  55. package/dist/core/response.d.ts +60 -0
  56. package/dist/core/response.d.ts.map +1 -0
  57. package/dist/core/server.d.ts +132 -0
  58. package/dist/core/server.d.ts.map +1 -0
  59. package/dist/core/sse.d.ts +27 -0
  60. package/dist/core/sse.d.ts.map +1 -0
  61. package/dist/core/state-bag.d.ts +36 -0
  62. package/dist/core/state-bag.d.ts.map +1 -0
  63. package/dist/core/websocket.d.ts +29 -0
  64. package/dist/core/websocket.d.ts.map +1 -0
  65. package/dist/decorators/api.d.ts +24 -4
  66. package/dist/decorators/api.d.ts.map +1 -0
  67. package/dist/decorators/args.d.ts +77 -14
  68. package/dist/decorators/args.d.ts.map +1 -0
  69. package/dist/decorators/custom.d.ts +81 -0
  70. package/dist/decorators/custom.d.ts.map +1 -0
  71. package/dist/decorators/header.d.ts +22 -0
  72. package/dist/decorators/header.d.ts.map +1 -0
  73. package/dist/decorators/index.d.ts +17 -0
  74. package/dist/decorators/index.d.ts.map +1 -0
  75. package/dist/decorators/methods.d.ts +33 -0
  76. package/dist/decorators/methods.d.ts.map +1 -0
  77. package/dist/decorators/progress.d.ts +24 -6
  78. package/dist/decorators/progress.d.ts.map +1 -0
  79. package/dist/decorators/server.d.ts +28 -7
  80. package/dist/decorators/server.d.ts.map +1 -0
  81. package/dist/decorators/stream.d.ts +113 -0
  82. package/dist/decorators/stream.d.ts.map +1 -0
  83. package/dist/default/options.d.ts +19 -0
  84. package/dist/default/options.d.ts.map +1 -0
  85. package/dist/error/base.d.ts +20 -0
  86. package/dist/error/base.d.ts.map +1 -0
  87. package/dist/error/decorator.d.ts +14 -0
  88. package/dist/error/decorator.d.ts.map +1 -0
  89. package/dist/error/hook.d.ts +14 -0
  90. package/dist/error/hook.d.ts.map +1 -0
  91. package/dist/error/index.d.ts +8 -0
  92. package/dist/error/index.d.ts.map +1 -0
  93. package/dist/error/options.d.ts +12 -0
  94. package/dist/error/options.d.ts.map +1 -0
  95. package/dist/error/plugin.d.ts +11 -0
  96. package/dist/error/plugin.d.ts.map +1 -0
  97. package/dist/error/request.d.ts +29 -0
  98. package/dist/error/request.d.ts.map +1 -0
  99. package/dist/error/response.d.ts +36 -0
  100. package/dist/error/response.d.ts.map +1 -0
  101. package/dist/index.d.ts +56 -11
  102. package/dist/index.d.ts.map +1 -0
  103. package/dist/index.js +2034 -0
  104. package/dist/index.js.map +1 -0
  105. package/dist/locale/en.d.ts +5 -0
  106. package/dist/locale/en.d.ts.map +1 -0
  107. package/dist/locale/index.d.ts +57 -0
  108. package/dist/locale/index.d.ts.map +1 -0
  109. package/dist/locale/types.d.ts +11 -0
  110. package/dist/locale/types.d.ts.map +1 -0
  111. package/dist/locale/zh.d.ts +5 -0
  112. package/dist/locale/zh.d.ts.map +1 -0
  113. package/dist/plugins/cache/adapters/indexeddb.d.ts +52 -0
  114. package/dist/plugins/cache/adapters/indexeddb.d.ts.map +1 -0
  115. package/dist/plugins/cache/adapters/memory.d.ts +60 -0
  116. package/dist/plugins/cache/adapters/memory.d.ts.map +1 -0
  117. package/dist/plugins/cache/adapters/web-storage.d.ts +43 -0
  118. package/dist/plugins/cache/adapters/web-storage.d.ts.map +1 -0
  119. package/dist/plugins/cache/decorators.d.ts +62 -0
  120. package/dist/plugins/cache/decorators.d.ts.map +1 -0
  121. package/dist/plugins/cache/index.d.ts +41 -0
  122. package/dist/plugins/cache/index.d.ts.map +1 -0
  123. package/dist/plugins/cache/key.d.ts +48 -0
  124. package/dist/plugins/cache/key.d.ts.map +1 -0
  125. package/dist/plugins/cache/manager.d.ts +124 -0
  126. package/dist/plugins/cache/manager.d.ts.map +1 -0
  127. package/dist/plugins/cache/plugin.d.ts +64 -0
  128. package/dist/plugins/cache/plugin.d.ts.map +1 -0
  129. package/dist/plugins/cache/type.d.ts +116 -0
  130. package/dist/plugins/cache/type.d.ts.map +1 -0
  131. package/dist/plugins/index.d.ts +75 -0
  132. package/dist/plugins/index.d.ts.map +1 -0
  133. package/dist/plugins/index.js +2265 -0
  134. package/dist/plugins/index.js.map +1 -0
  135. package/dist/plugins/interceptor/decorators.d.ts +44 -0
  136. package/dist/plugins/interceptor/decorators.d.ts.map +1 -0
  137. package/dist/plugins/interceptor/index.d.ts +31 -0
  138. package/dist/plugins/interceptor/index.d.ts.map +1 -0
  139. package/dist/plugins/interceptor/manager.d.ts +27 -0
  140. package/dist/plugins/interceptor/manager.d.ts.map +1 -0
  141. package/dist/plugins/interceptor/plugin.d.ts +65 -0
  142. package/dist/plugins/interceptor/plugin.d.ts.map +1 -0
  143. package/dist/plugins/interceptor/type.d.ts +45 -0
  144. package/dist/plugins/interceptor/type.d.ts.map +1 -0
  145. package/dist/plugins/pool/index.d.ts +26 -0
  146. package/dist/plugins/pool/index.d.ts.map +1 -0
  147. package/dist/plugins/pool/plugin.d.ts +83 -0
  148. package/dist/plugins/pool/plugin.d.ts.map +1 -0
  149. package/dist/plugins/pool/scheduler.d.ts +153 -0
  150. package/dist/plugins/pool/scheduler.d.ts.map +1 -0
  151. package/dist/plugins/pool/type.d.ts +38 -0
  152. package/dist/plugins/pool/type.d.ts.map +1 -0
  153. package/dist/plugins/react/index.d.ts +20 -0
  154. package/dist/plugins/react/index.d.ts.map +1 -0
  155. package/dist/plugins/react/index.js +145 -0
  156. package/dist/plugins/react/index.js.map +1 -0
  157. package/dist/plugins/react/plugin.d.ts +33 -0
  158. package/dist/plugins/react/plugin.d.ts.map +1 -0
  159. package/dist/plugins/react/type.d.ts +32 -0
  160. package/dist/plugins/react/type.d.ts.map +1 -0
  161. package/dist/plugins/transform/decorators.d.ts +54 -0
  162. package/dist/plugins/transform/decorators.d.ts.map +1 -0
  163. package/dist/plugins/transform/hydrate.d.ts +47 -0
  164. package/dist/plugins/transform/hydrate.d.ts.map +1 -0
  165. package/dist/plugins/transform/index.d.ts +41 -0
  166. package/dist/plugins/transform/index.d.ts.map +1 -0
  167. package/dist/plugins/transform/plugin.d.ts +20 -0
  168. package/dist/plugins/transform/plugin.d.ts.map +1 -0
  169. package/dist/plugins/transform/type.d.ts +67 -0
  170. package/dist/plugins/transform/type.d.ts.map +1 -0
  171. package/dist/plugins/validate/decorators.d.ts +24 -0
  172. package/dist/plugins/validate/decorators.d.ts.map +1 -0
  173. package/dist/plugins/validate/index.d.ts +40 -0
  174. package/dist/plugins/validate/index.d.ts.map +1 -0
  175. package/dist/plugins/validate/plugin.d.ts +13 -0
  176. package/dist/plugins/validate/plugin.d.ts.map +1 -0
  177. package/dist/plugins/validate/type.d.ts +54 -0
  178. package/dist/plugins/validate/type.d.ts.map +1 -0
  179. package/dist/plugins/version/decorators.d.ts +22 -0
  180. package/dist/plugins/version/decorators.d.ts.map +1 -0
  181. package/dist/plugins/version/index.d.ts +23 -0
  182. package/dist/plugins/version/index.d.ts.map +1 -0
  183. package/dist/plugins/version/plugin.d.ts +13 -0
  184. package/dist/plugins/version/plugin.d.ts.map +1 -0
  185. package/dist/plugins/version/type.d.ts +47 -0
  186. package/dist/plugins/version/type.d.ts.map +1 -0
  187. package/dist/plugins/vue/index.d.ts +21 -0
  188. package/dist/plugins/vue/index.d.ts.map +1 -0
  189. package/dist/plugins/vue/index.js +114 -0
  190. package/dist/plugins/vue/index.js.map +1 -0
  191. package/dist/plugins/vue/plugin.d.ts +15 -0
  192. package/dist/plugins/vue/plugin.d.ts.map +1 -0
  193. package/dist/plugins/vue/type.d.ts +34 -0
  194. package/dist/plugins/vue/type.d.ts.map +1 -0
  195. package/dist/strategies/index.d.ts +2 -0
  196. package/dist/strategies/index.d.ts.map +1 -0
  197. package/dist/strategies/index.js +29 -0
  198. package/dist/strategies/index.js.map +1 -0
  199. package/dist/strategies/plain.d.ts +2 -0
  200. package/dist/strategies/plain.d.ts.map +1 -0
  201. package/dist/strategies/plain.js +23 -0
  202. package/dist/strategies/plain.js.map +1 -0
  203. package/dist/strategies/react.d.ts +2 -0
  204. package/dist/strategies/react.d.ts.map +1 -0
  205. package/dist/strategies/react.js +30 -0
  206. package/dist/strategies/react.js.map +1 -0
  207. package/dist/strategies/shared/adapter.d.ts +23 -0
  208. package/dist/strategies/shared/adapter.d.ts.map +1 -0
  209. package/dist/strategies/shared/dom.d.ts +53 -0
  210. package/dist/strategies/shared/dom.d.ts.map +1 -0
  211. package/dist/strategies/shared/error.d.ts +30 -0
  212. package/dist/strategies/shared/error.d.ts.map +1 -0
  213. package/dist/strategies/shared/method.d.ts +51 -0
  214. package/dist/strategies/shared/method.d.ts.map +1 -0
  215. package/dist/strategies/shared/public.d.ts +33 -0
  216. package/dist/strategies/shared/public.d.ts.map +1 -0
  217. package/dist/strategies/shared/queue.d.ts +30 -0
  218. package/dist/strategies/shared/queue.d.ts.map +1 -0
  219. package/dist/strategies/shared/retry.d.ts +51 -0
  220. package/dist/strategies/shared/retry.d.ts.map +1 -0
  221. package/dist/strategies/shared/state.d.ts +117 -0
  222. package/dist/strategies/shared/state.d.ts.map +1 -0
  223. package/dist/strategies/shared/timing.d.ts +53 -0
  224. package/dist/strategies/shared/timing.d.ts.map +1 -0
  225. package/dist/strategies/shared/watcher.d.ts +37 -0
  226. package/dist/strategies/shared/watcher.d.ts.map +1 -0
  227. package/dist/strategies/use-auto-request.d.ts +77 -0
  228. package/dist/strategies/use-auto-request.d.ts.map +1 -0
  229. package/dist/strategies/use-download.d.ts +103 -0
  230. package/dist/strategies/use-download.d.ts.map +1 -0
  231. package/dist/strategies/use-fetcher.d.ts +52 -0
  232. package/dist/strategies/use-fetcher.d.ts.map +1 -0
  233. package/dist/strategies/use-pagination.d.ts +101 -0
  234. package/dist/strategies/use-pagination.d.ts.map +1 -0
  235. package/dist/strategies/use-request.d.ts +50 -0
  236. package/dist/strategies/use-request.d.ts.map +1 -0
  237. package/dist/strategies/use-retriable-request.d.ts +46 -0
  238. package/dist/strategies/use-retriable-request.d.ts.map +1 -0
  239. package/dist/strategies/use-sse.d.ts +103 -0
  240. package/dist/strategies/use-sse.d.ts.map +1 -0
  241. package/dist/strategies/use-token-auth.d.ts +88 -0
  242. package/dist/strategies/use-token-auth.d.ts.map +1 -0
  243. package/dist/strategies/use-uploader.d.ts +88 -0
  244. package/dist/strategies/use-uploader.d.ts.map +1 -0
  245. package/dist/strategies/use-watcher.d.ts +69 -0
  246. package/dist/strategies/use-watcher.d.ts.map +1 -0
  247. package/dist/typings/adapter.d.ts +61 -0
  248. package/dist/typings/adapter.d.ts.map +1 -0
  249. package/dist/typings/api.d.ts +122 -0
  250. package/dist/typings/api.d.ts.map +1 -0
  251. package/dist/typings/args.d.ts +51 -0
  252. package/dist/typings/args.d.ts.map +1 -0
  253. package/dist/typings/index.d.ts +8 -12
  254. package/dist/typings/index.d.ts.map +1 -0
  255. package/dist/typings/meta.d.ts +44 -0
  256. package/dist/typings/meta.d.ts.map +1 -0
  257. package/dist/typings/plugin.d.ts +114 -0
  258. package/dist/typings/plugin.d.ts.map +1 -0
  259. package/dist/typings/response.d.ts +118 -0
  260. package/dist/typings/response.d.ts.map +1 -0
  261. package/dist/typings/server.d.ts +83 -0
  262. package/dist/typings/server.d.ts.map +1 -0
  263. package/dist/typings/stream.d.ts +150 -0
  264. package/dist/typings/stream.d.ts.map +1 -0
  265. package/dist/utils/download.d.ts +97 -0
  266. package/dist/utils/download.d.ts.map +1 -0
  267. package/dist/utils/emitter.d.ts +36 -0
  268. package/dist/utils/emitter.d.ts.map +1 -0
  269. package/dist/utils/index.d.ts +6 -1
  270. package/dist/utils/index.d.ts.map +1 -0
  271. package/dist/utils/is.d.ts +22 -0
  272. package/dist/utils/is.d.ts.map +1 -0
  273. package/dist/utils/object.d.ts +44 -0
  274. package/dist/utils/object.d.ts.map +1 -0
  275. package/dist/utils/url.d.ts +44 -0
  276. package/dist/utils/url.d.ts.map +1 -0
  277. package/package.json +90 -27
  278. package/README_EN.md +0 -607
  279. package/dist/cache/index.d.ts +0 -9
  280. package/dist/cache/indexDBCache.d.ts +0 -13
  281. package/dist/cache/localstorageCache.d.ts +0 -10
  282. package/dist/cache/memoryCache.d.ts +0 -11
  283. package/dist/core/index.d.ts +0 -4
  284. package/dist/core/snailApi.d.ts +0 -22
  285. package/dist/core/snailMethod.d.ts +0 -49
  286. package/dist/core/snailServer.d.ts +0 -35
  287. package/dist/core/snailSse.d.ts +0 -20
  288. package/dist/decorators/cache.d.ts +0 -15
  289. package/dist/decorators/method.d.ts +0 -38
  290. package/dist/decorators/sse.d.ts +0 -30
  291. package/dist/decorators/strategy.d.ts +0 -10
  292. package/dist/decorators/versioning.d.ts +0 -16
  293. package/dist/eventEmmit/index.d.ts +0 -1
  294. package/dist/eventEmmit/snail.event.d.ts +0 -10
  295. package/dist/snail-api.js +0 -4868
  296. package/dist/snail-api.umd.cjs +0 -4872
  297. package/dist/strategies/jwtStrategy.d.ts +0 -4
  298. package/dist/typings/api.option.d.ts +0 -10
  299. package/dist/typings/apiProxy.d.ts +0 -8
  300. package/dist/typings/cache.management.option.d.ts +0 -25
  301. package/dist/typings/cache.type.d.ts +0 -23
  302. package/dist/typings/content.type.d.ts +0 -5
  303. package/dist/typings/request.body.d.ts +0 -3
  304. package/dist/typings/response.data.d.ts +0 -13
  305. package/dist/typings/snail.event.d.ts +0 -6
  306. package/dist/typings/snail.method.d.ts +0 -14
  307. package/dist/typings/snail.option.d.ts +0 -16
  308. package/dist/typings/sse.d.ts +0 -15
  309. package/dist/typings/strategy.d.ts +0 -5
  310. package/dist/typings/versioning.option.d.ts +0 -30
  311. package/dist/utils/function.d.ts +0 -31
  312. package/dist/versioning/index.d.ts +0 -1
  313. package/dist/versioning/versioning.d.ts +0 -12
@@ -0,0 +1,117 @@
1
+ import type { SnailStateAdapter, SnailStateRef } from "../../typings/adapter.js";
2
+ /** Partial write accepted by {@link StrategyState.update}. */
3
+ export interface StrategyStatePatch<TData> {
4
+ data?: TData;
5
+ loading?: boolean;
6
+ error?: unknown;
7
+ code?: number | string;
8
+ message?: string;
9
+ }
10
+ /** The resolved snapshot {@link StrategyState.bind} returns. */
11
+ export interface StrategyBoundState<TData> {
12
+ loading: boolean;
13
+ data: TData | undefined;
14
+ error: unknown;
15
+ code: number | string | undefined;
16
+ message: string | undefined;
17
+ }
18
+ /**
19
+ * The state surface every request strategy exposes.
20
+ *
21
+ * ## Why handles and not values
22
+ *
23
+ * A strategy cannot know whether it is being read by a Vue render effect, a React
24
+ * render or a plain script. Handing back a `SnailStateRef` keeps the strategy
25
+ * framework-free: a Vue `Ref` already satisfies the interface, React goes through
26
+ * `bind()`, and a script may read `.value` directly.
27
+ *
28
+ * ## Why `update()` exists
29
+ *
30
+ * An optimistic update has to write the cache entry into the state *before* the
31
+ * server confirms it, and has to roll it back when the request fails. Without
32
+ * `update()` every caller would reach into the refs and bypass the adapter.
33
+ */
34
+ export interface StrategyState<TData> {
35
+ /** `true` from the start of a send until it settles. */
36
+ readonly loading: SnailStateRef<boolean>;
37
+ /** Payload of the most recent successful send. */
38
+ readonly data: SnailStateRef<TData | undefined>;
39
+ /** Failure of the most recent send. Never set for a cancellation. */
40
+ readonly error: SnailStateRef<unknown>;
41
+ /** Business/HTTP code of the most recent send. */
42
+ readonly code: SnailStateRef<number | string | undefined>;
43
+ /** Business message of the most recent send. */
44
+ readonly message: SnailStateRef<string | undefined>;
45
+ /** Abort the in-flight request, if any. */
46
+ abort(): void;
47
+ /** Patch state directly — e.g. after an optimistic update. */
48
+ update(patch: StrategyStatePatch<TData>): void;
49
+ /** Resolved values, subscribing the current component when the adapter supports it. */
50
+ bind(): StrategyBoundState<TData>;
51
+ /** Called after a successful send. Returns an unsubscribe function. */
52
+ onSuccess(callback: (data: TData) => void): () => void;
53
+ /** Called after a failed send. Cancellations are not failures. */
54
+ onError(callback: (error: unknown) => void): () => void;
55
+ /** Called once a send settles, successfully or not. */
56
+ onFinish(callback: () => void): () => void;
57
+ }
58
+ /**
59
+ * Events a state controller emits.
60
+ *
61
+ * A type alias rather than an interface so it satisfies the `Record<string,
62
+ * unknown>` constraint `Emitter` requires — interfaces do not get implicit index
63
+ * signatures.
64
+ */
65
+ export type StrategyStateEvents<TData> = {
66
+ success: TData;
67
+ error: unknown;
68
+ finish: undefined;
69
+ };
70
+ /** Options accepted by {@link createStrategyState}. */
71
+ export interface StrategyStateOptions<TData> {
72
+ adapter?: SnailStateAdapter;
73
+ /** Value `data` starts at. Defaults to `undefined`. */
74
+ initialData?: TData;
75
+ /** Invoked by `state.abort()`. The hook owns what "abort" means. */
76
+ onAbort?: () => void;
77
+ }
78
+ /**
79
+ * The {@link StrategyState} plus the write side only the hook should use.
80
+ *
81
+ * Keeping the writers off the caller-visible object is what stops application code
82
+ * from poking `data` mid-flight: the handle is readable, the transitions are not.
83
+ */
84
+ export interface StrategyStateController<TData> {
85
+ readonly adapter: SnailStateAdapter;
86
+ readonly state: StrategyState<TData>;
87
+ setLoading(value: boolean): void;
88
+ setData(value: TData | undefined): void;
89
+ setError(value: unknown): void;
90
+ setCode(value: number | string | undefined): void;
91
+ setMessage(value: string | undefined): void;
92
+ /** Clear the failure fields at the start of a send. `data` is left alone. */
93
+ resetForSend(): void;
94
+ /** Write `data`/`code`/`message` from a successful result and clear `error`. */
95
+ applySuccess(result: {
96
+ data: TData;
97
+ code?: number | string | undefined;
98
+ message?: string | undefined;
99
+ }): TData;
100
+ /** Write `error` — plus the code/message the error carries. */
101
+ applyFailure(error: unknown): void;
102
+ emitSuccess(data: TData): void;
103
+ emitError(error: unknown): void;
104
+ emitFinish(): void;
105
+ /** Release adapter resources and drop every listener. */
106
+ dispose(): void;
107
+ }
108
+ /**
109
+ * Build the state handles, the listeners and the writers for one hook.
110
+ *
111
+ * All five refs are created here, even for a hook that will not use them: a
112
+ * `useFetcher({ withState: false })` still has to answer `abort()` and
113
+ * `onFinish()`, and creating state lazily would hand the UI handles that appear
114
+ * only after the first request.
115
+ */
116
+ export declare function createStrategyState<TData>(options?: StrategyStateOptions<TData>): StrategyStateController<TData>;
117
+ //# sourceMappingURL=state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../../src/strategies/shared/state.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAK9E,8DAA8D;AAC9D,MAAM,WAAW,kBAAkB,CAAC,KAAK;IACvC,IAAI,CAAC,EAAE,KAAK,CAAC;IACb,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,gEAAgE;AAChE,MAAM,WAAW,kBAAkB,CAAC,KAAK;IACvC,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,KAAK,GAAG,SAAS,CAAC;IACxB,KAAK,EAAE,OAAO,CAAC;IACf,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IAClC,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;CAC7B;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,aAAa,CAAC,KAAK;IAClC,wDAAwD;IACxD,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC;IAEzC,kDAAkD;IAClD,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC,KAAK,GAAG,SAAS,CAAC,CAAC;IAEhD,qEAAqE;IACrE,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC;IAEvC,kDAAkD;IAClD,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC,CAAC;IAE1D,gDAAgD;IAChD,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IAEpD,2CAA2C;IAC3C,KAAK,IAAI,IAAI,CAAC;IAEd,8DAA8D;IAC9D,MAAM,CAAC,KAAK,EAAE,kBAAkB,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;IAE/C,uFAAuF;IACvF,IAAI,IAAI,kBAAkB,CAAC,KAAK,CAAC,CAAC;IAElC,uEAAuE;IACvE,SAAS,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,KAAK,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IAEvD,kEAAkE;IAClE,OAAO,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IAExD,uDAAuD;IACvD,QAAQ,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC;CAC5C;AAED;;;;;;GAMG;AACH,MAAM,MAAM,mBAAmB,CAAC,KAAK,IAAI;IACvC,OAAO,EAAE,KAAK,CAAC;IACf,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,SAAS,CAAC;CACnB,CAAC;AAEF,uDAAuD;AACvD,MAAM,WAAW,oBAAoB,CAAC,KAAK;IACzC,OAAO,CAAC,EAAE,iBAAiB,CAAC;IAC5B,uDAAuD;IACvD,WAAW,CAAC,EAAE,KAAK,CAAC;IACpB,oEAAoE;IACpE,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB;AAED;;;;;GAKG;AACH,MAAM,WAAW,uBAAuB,CAAC,KAAK;IAC5C,QAAQ,CAAC,OAAO,EAAE,iBAAiB,CAAC;IACpC,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC,KAAK,CAAC,CAAC;IAErC,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;IACjC,OAAO,CAAC,KAAK,EAAE,KAAK,GAAG,SAAS,GAAG,IAAI,CAAC;IACxC,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;IAC/B,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,GAAG,IAAI,CAAC;IAClD,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAAC;IAE5C,6EAA6E;IAC7E,YAAY,IAAI,IAAI,CAAC;IAErB,gFAAgF;IAChF,YAAY,CAAC,MAAM,EAAE;QACnB,IAAI,EAAE,KAAK,CAAC;QACZ,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;QACnC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;KAC9B,GAAG,KAAK,CAAC;IAEV,+DAA+D;IAC/D,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;IAEnC,WAAW,CAAC,IAAI,EAAE,KAAK,GAAG,IAAI,CAAC;IAC/B,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;IAChC,UAAU,IAAI,IAAI,CAAC;IAEnB,yDAAyD;IACzD,OAAO,IAAI,IAAI,CAAC;CACjB;AAOD;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EACvC,OAAO,GAAE,oBAAoB,CAAC,KAAK,CAAM,GACxC,uBAAuB,CAAC,KAAK,CAAC,CA0HhC"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Sleep for `ms`, rejecting with a {@link SnailCancelledError} the moment the
3
+ * signal aborts.
4
+ *
5
+ * A plain `setTimeout` promise is the wrong primitive for a backoff: `abort()`
6
+ * during a 30 second retry delay would leave the loop asleep and the caller's
7
+ * promise pending long after it cancelled. The listener is `{ once: true }` and
8
+ * removed on either outcome so a long-lived signal cannot accumulate handlers.
9
+ */
10
+ export declare function cancellableDelay(ms: number, signal?: AbortSignal): Promise<void>;
11
+ /**
12
+ * Detach a Node timer from the event loop, when it has an `unref`.
13
+ *
14
+ * A polling strategy that is never stopped would otherwise keep a Node process
15
+ * (or a test worker) alive forever. In a browser `setTimeout` returns a number,
16
+ * which has no `unref` — hence the optional call rather than a feature test.
17
+ */
18
+ export declare function unrefTimer(timer: unknown): void;
19
+ /** Scheduler contract used by `useWatcher`. */
20
+ export interface RequestScheduler {
21
+ /** Queue `task` for the next allowed run. The latest task always wins. */
22
+ schedule(task: () => void): void;
23
+ /** `true` while a run is still pending on a timer. */
24
+ readonly pending: boolean;
25
+ /** Drop a pending run without executing it. */
26
+ cancel(): void;
27
+ }
28
+ /** Timing options accepted by {@link createRequestScheduler}. */
29
+ export interface SchedulerOptions {
30
+ /** Wait for quiet before running. Wins over `throttle` when both are set. */
31
+ debounce?: number;
32
+ /** Run at most once per window, on the leading edge. */
33
+ throttle?: number;
34
+ }
35
+ /**
36
+ * Collapse a burst of `schedule()` calls into at most one request.
37
+ *
38
+ * Two modes, one rule — **the most recent task wins**, because a request must be
39
+ * sent with the latest arguments, never with the ones captured when the burst
40
+ * started:
41
+ *
42
+ * - `debounce` waits for `debounce` ms of quiet. Every new call pushes the run
43
+ * back, so ten keystrokes produce one request, not ten.
44
+ * - `throttle` runs on the leading edge and then books exactly one trailing run
45
+ * per window. Without the trailing run the last change in a burst would be
46
+ * silently dropped — a search box that ignores the final character.
47
+ *
48
+ * `debounce` is preferred when both are configured (see `useWatcher`): the two
49
+ * express contradictory intents, and picking one deterministically beats a
50
+ * half-debounced, half-throttled hybrid nobody can reason about.
51
+ */
52
+ export declare function createRequestScheduler(options?: SchedulerOptions): RequestScheduler;
53
+ //# sourceMappingURL=timing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timing.d.ts","sourceRoot":"","sources":["../../../src/strategies/shared/timing.ts"],"names":[],"mappings":"AAEA;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAqBhF;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAE/C;AAED,+CAA+C;AAC/C,MAAM,WAAW,gBAAgB;IAC/B,0EAA0E;IAC1E,QAAQ,CAAC,IAAI,EAAE,MAAM,IAAI,GAAG,IAAI,CAAC;IAEjC,sDAAsD;IACtD,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAE1B,+CAA+C;IAC/C,MAAM,IAAI,IAAI,CAAC;CAChB;AAED,iEAAiE;AACjE,MAAM,WAAW,gBAAgB;IAC/B,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,wDAAwD;IACxD,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,GAAE,gBAAqB,GAAG,gBAAgB,CA8DvF"}
@@ -0,0 +1,37 @@
1
+ import type { SnailStateAdapter } from "../../typings/adapter.js";
2
+ /**
3
+ * Read one entry of a `watching()` result.
4
+ *
5
+ * `docs/guide/plugin-lifecycle.md`'s adapter contract exposes no way to ask "is this a state
6
+ * handle?" portably: the Vue adapter has `isState`, React's has none, and the
7
+ * plain adapter's boxes are bare `{ value }` objects. So three cases are handled,
8
+ * in order of confidence:
9
+ *
10
+ * 1. `adapter.isState(value)` says yes — unwrap it (Vue).
11
+ * 2. the value is a plain object whose *only* own key is `value` — the exact shape
12
+ * the plain and React adapters allocate — unwrap it.
13
+ * 3. anything else is the value itself.
14
+ *
15
+ * Case 2 is a heuristic, and it is the reason `watching: () => [{ value: 1 }]`
16
+ * should be written as `() => [{ value: 1 }.value]` if the object is genuine
17
+ * data rather than a handle. Without it, `() => [pageRef]` — the natural thing to
18
+ * write — would compare the ref object itself and never detect a change.
19
+ */
20
+ export declare function unwrapWatchedValue(adapter: SnailStateAdapter, value: unknown): unknown;
21
+ /**
22
+ * Evaluate a `watching()` function into the plain values that get compared.
23
+ *
24
+ * A watcher is user code, so a non-array return is normalised to a one-element
25
+ * list rather than iterated blindly — spreading a string would compare its
26
+ * characters, and spreading `undefined` would throw inside the hook.
27
+ */
28
+ export declare function readWatchedValues(adapter: SnailStateAdapter, watching: () => readonly unknown[]): unknown[];
29
+ /**
30
+ * Compare two watched snapshots with `Object.is`.
31
+ *
32
+ * `Object.is` rather than `===` so `NaN` does not look like a change on every
33
+ * render — a watcher over a numeric field that happens to be `NaN` would
34
+ * otherwise re-send forever.
35
+ */
36
+ export declare function shallowEqual(a: readonly unknown[], b: readonly unknown[]): boolean;
37
+ //# sourceMappingURL=watcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"watcher.d.ts","sourceRoot":"","sources":["../../../src/strategies/shared/watcher.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAiB,MAAM,uBAAuB,CAAC;AAE9E;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,iBAAiB,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAatF;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,iBAAiB,EAC1B,QAAQ,EAAE,MAAM,SAAS,OAAO,EAAE,GACjC,OAAO,EAAE,CAIX;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,SAAS,OAAO,EAAE,EAAE,CAAC,EAAE,SAAS,OAAO,EAAE,GAAG,OAAO,CAOlF"}
@@ -0,0 +1,77 @@
1
+ import type { SnailStateRef } from "../typings/adapter.js";
2
+ import type { StrategyMethod } from "./shared/method.js";
3
+ import type { UseRequestOptions, UseRequestResult } from "./use-request.js";
4
+ /** Options accepted by {@link useAutoRequest}. */
5
+ export interface UseAutoRequestOptions<TData> extends UseRequestOptions<TData> {
6
+ /**
7
+ * Poll every `pollingInterval` ms.
8
+ *
9
+ * The next tick is scheduled only **after** the previous request settles, so a
10
+ * backend slower than the interval produces a queue of one, not an unbounded
11
+ * pile of overlapping requests.
12
+ */
13
+ pollingInterval?: number;
14
+ /** Refresh when the window regains focus. */
15
+ enableFocusRefresh?: boolean;
16
+ /** Refresh when the browser reports the network is back. */
17
+ enableReconnectRefresh?: boolean;
18
+ /** Refresh when the tab becomes visible again. */
19
+ refreshOnVisible?: boolean;
20
+ }
21
+ /** What {@link useAutoRequest} returns. */
22
+ export interface UseAutoRequestResult<TData, TArgs extends readonly unknown[] = readonly unknown[]> extends UseRequestResult<TData, TArgs> {
23
+ /** `true` between `start()` and `stop()`. */
24
+ readonly running: SnailStateRef<boolean>;
25
+ /**
26
+ * Arm polling and the refresh listeners.
27
+ *
28
+ * Also fires the first request immediately — waiting a whole interval before the
29
+ * first call would leave the view empty for no reason. Because that first request
30
+ * uses {@link refresh}, it carries no arguments unless a `send()` already
31
+ * provided some.
32
+ */
33
+ start(): void;
34
+ /** Disarm polling and remove every listener this hook registered. */
35
+ stop(): void;
36
+ /**
37
+ * Run one request now, reusing the arguments of the previous send.
38
+ *
39
+ * Before any `send()`, that means no arguments at all — a method whose `@Params`
40
+ * cannot be satisfied would reject, which is the same contract `immediate` has.
41
+ */
42
+ refresh(): Promise<TData>;
43
+ /** `stop()` plus a permanent flag: a disposed hook ignores `start()`. */
44
+ dispose(): void;
45
+ }
46
+ /**
47
+ * Keep a request fresh by itself: polling plus the three "the user is back"
48
+ * signals a dashboard actually needs.
49
+ *
50
+ * ```ts
51
+ * const stats = useAutoRequest(statsApi.get, {
52
+ * pollingInterval: 5000,
53
+ * refreshOnVisible: true
54
+ * });
55
+ * stats.start();
56
+ * stats.dispose(); // on unmount
57
+ * ```
58
+ *
59
+ * ## Lifecycle
60
+ *
61
+ * `stop()` is the single release point: it halts the polling timer **and** removes
62
+ * every listener, so an unmounted view cannot be kept alive by a focus handler.
63
+ * `start()` re-arms both. The refresh listeners are attached at creation rather
64
+ * than in `start()` — a hook whose only job is "refresh when the user comes back"
65
+ * should not need an extra call — while polling itself only runs between `start()`
66
+ * and `stop()`.
67
+ *
68
+ * ## Node safety
69
+ *
70
+ * `window`/`document` are looked up lazily and may be absent, so creating the hook
71
+ * during SSR is harmless. Every listener goes through one listener scope, which is
72
+ * what makes `stop()` remove exactly what was added; a listener removed with a
73
+ * fresh closure reference would leak silently. The polling timer is `unref`'d on
74
+ * Node so it never holds the process (or a test worker) open.
75
+ */
76
+ export declare function useAutoRequest<TArgs extends readonly unknown[], TData>(method: StrategyMethod<TArgs, TData>, options?: UseAutoRequestOptions<TData>): UseAutoRequestResult<TData, TArgs>;
77
+ //# sourceMappingURL=use-auto-request.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-auto-request.d.ts","sourceRoot":"","sources":["../../src/strategies/use-auto-request.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAGxD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAGtD,OAAO,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEzE,kDAAkD;AAClD,MAAM,WAAW,qBAAqB,CAAC,KAAK,CAAE,SAAQ,iBAAiB,CAAC,KAAK,CAAC;IAC5E;;;;;;OAMG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB,6CAA6C;IAC7C,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAE7B,4DAA4D;IAC5D,sBAAsB,CAAC,EAAE,OAAO,CAAC;IAEjC,kDAAkD;IAClD,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,2CAA2C;AAC3C,MAAM,WAAW,oBAAoB,CACnC,KAAK,EACL,KAAK,SAAS,SAAS,OAAO,EAAE,GAAG,SAAS,OAAO,EAAE,CACrD,SAAQ,gBAAgB,CAAC,KAAK,EAAE,KAAK,CAAC;IACtC,6CAA6C;IAC7C,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC;IAEzC;;;;;;;OAOG;IACH,KAAK,IAAI,IAAI,CAAC;IAEd,qEAAqE;IACrE,IAAI,IAAI,IAAI,CAAC;IAEb;;;;;OAKG;IACH,OAAO,IAAI,OAAO,CAAC,KAAK,CAAC,CAAC;IAE1B,yEAAyE;IACzE,OAAO,IAAI,IAAI,CAAC;CACjB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,cAAc,CAAC,KAAK,SAAS,SAAS,OAAO,EAAE,EAAE,KAAK,EACpE,MAAM,EAAE,cAAc,CAAC,KAAK,EAAE,KAAK,CAAC,EACpC,OAAO,GAAE,qBAAqB,CAAC,KAAK,CAAM,GACzC,oBAAoB,CAAC,KAAK,EAAE,KAAK,CAAC,CAkGpC"}
@@ -0,0 +1,103 @@
1
+ import type { SnailStateRef, SnailStrategyCommonOptions } from "../typings/adapter.js";
2
+ import { type TriggerDownloadResult } from "../utils/download.js";
3
+ import { type StrategyMethod } from "./shared/method.js";
4
+ import { type StrategyState } from "./shared/state.js";
5
+ /**
6
+ * The download descriptor a server is expected to return.
7
+ *
8
+ * The shape is intentionally tiny, because the whole design rests on the server
9
+ * doing the work: it prepares the file out of band and answers with a short-lived
10
+ * URL. The client never receives the bytes.
11
+ */
12
+ export interface DownloadDescriptor {
13
+ /** Where to fetch the file from. A relative path, an absolute URL or a `blob:` URL. */
14
+ url: string;
15
+ /** Filename to suggest. Honoured only for a same-origin URL — see `triggerDownload`. */
16
+ filename?: string;
17
+ }
18
+ /** Options accepted by {@link useDownload}. */
19
+ export interface UseDownloadOptions<TPayload> extends SnailStrategyCommonOptions {
20
+ /**
21
+ * Read the descriptor out of the payload your endpoint returned.
22
+ *
23
+ * The default accepts a bare URL string, or an object carrying `url`,
24
+ * `downloadUrl` or `fileUrl` plus an optional `filename`/`name`. Supply this when
25
+ * your backend wraps them differently, so a naming choice never forces you to
26
+ * reshape the response.
27
+ */
28
+ pick?: (payload: TPayload) => DownloadDescriptor;
29
+ /**
30
+ * Trigger the browser download as soon as the URL arrives. Defaults to `true`.
31
+ *
32
+ * Turn it off to inspect the descriptor first — for instance to show a
33
+ * confirmation, or to hand the URL to a different download manager.
34
+ */
35
+ autoTrigger?: boolean;
36
+ /** Open the URL in a new tab rather than downloading it. */
37
+ openInNewTab?: boolean;
38
+ /**
39
+ * Filename override, taking precedence over whatever `pick` produced.
40
+ *
41
+ * Only `useDownload` can do this: a hook knows the local file's intended name,
42
+ * the server does not.
43
+ */
44
+ filename?: string;
45
+ /** Anchor container, for the old Firefox "must be in the document" behaviour. */
46
+ container?: HTMLElement;
47
+ /** `referrerpolicy` for the navigation. */
48
+ referrerPolicy?: string;
49
+ }
50
+ /** What {@link useDownload} returns. */
51
+ export interface UseDownloadResult<TPayload> extends StrategyState<TPayload> {
52
+ /**
53
+ * Ask the server for the download URL, then hand it to the browser.
54
+ *
55
+ * Resolves once the download has been *started* — not completed. There is no
56
+ * completion signal, and that is deliberate: the browser's own download manager
57
+ * owns the transfer, which is exactly what makes a multi-gigabyte file work where
58
+ * a buffered `blob` would exhaust memory.
59
+ */
60
+ download(...args: readonly unknown[]): Promise<TriggerDownloadResult>;
61
+ /** The most recently resolved descriptor. `undefined` until the first success. */
62
+ readonly info: SnailStateRef<DownloadDescriptor | undefined>;
63
+ /** Called with the descriptor once it has been resolved AND triggered. */
64
+ onDownload(callback: (info: DownloadDescriptor) => void): () => void;
65
+ }
66
+ /**
67
+ * Drive a server-prepared download.
68
+ *
69
+ * ```ts
70
+ * @Api("/report")
71
+ * class ReportApi {
72
+ * /** Prepares the export server-side and answers with a temp url. *\/
73
+ * @Post("/export")
74
+ * create(@Data() query: ReportQuery): Promise<{ url: string; filename: string }> {
75
+ * return null!;
76
+ * }
77
+ * }
78
+ *
79
+ * const { download, loading, error } = useDownload(reportApi.create);
80
+ * await download({ from: "2026-01-01" });
81
+ * ```
82
+ *
83
+ * ## Why it does not fetch the file
84
+ *
85
+ * `useDownload` awaits the request that *mints* a download URL, and only that. It
86
+ * never fetches the file itself. Buffering a response into a `Blob` costs the whole
87
+ * file in JavaScript memory — twice, once for the body and once for the object URL
88
+ * — with no progress and no resume. Letting the browser fetch the URL instead gives
89
+ * you the native download manager, streaming to disk, resume support and a download
90
+ * that survives navigation.
91
+ *
92
+ * ## Why this is a strategy and not a plugin
93
+ *
94
+ * A plugin is for a cross-cutting concern that applies to requests an application
95
+ * did not write specially: caching, interceptors, validation. A download is the
96
+ * opposite — one explicit user action, with its own visible state and its own
97
+ * failure modes, invoked from a click handler. That is the definition of a request
98
+ * strategy. The reusable half that is *not* about reactivity — clicking a temporary
99
+ * anchor — lives in `triggerDownload()`, which is exported from the package root so
100
+ * a non-hook caller can use it directly.
101
+ */
102
+ export declare function useDownload<TArgs extends readonly unknown[], TPayload>(method: StrategyMethod<TArgs, TPayload>, options?: UseDownloadOptions<TPayload>): UseDownloadResult<TPayload>;
103
+ //# sourceMappingURL=use-download.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-download.d.ts","sourceRoot":"","sources":["../../src/strategies/use-download.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,0BAA0B,EAAE,MAAM,oBAAoB,CAAC;AACpF,OAAO,EAGL,KAAK,qBAAqB,EAC3B,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAC1E,OAAO,EAAuB,KAAK,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAEzE;;;;;;GAMG;AACH,MAAM,WAAW,kBAAkB;IACjC,uFAAuF;IACvF,GAAG,EAAE,MAAM,CAAC;IAEZ,wFAAwF;IACxF,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,+CAA+C;AAC/C,MAAM,WAAW,kBAAkB,CAAC,QAAQ,CAAE,SAAQ,0BAA0B;IAC9E;;;;;;;OAOG;IACH,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,QAAQ,KAAK,kBAAkB,CAAC;IAEjD;;;;;OAKG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IAEtB,4DAA4D;IAC5D,YAAY,CAAC,EAAE,OAAO,CAAC;IAEvB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,iFAAiF;IACjF,SAAS,CAAC,EAAE,WAAW,CAAC;IAExB,2CAA2C;IAC3C,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,wCAAwC;AACxC,MAAM,WAAW,iBAAiB,CAAC,QAAQ,CAAE,SAAQ,aAAa,CAAC,QAAQ,CAAC;IAC1E;;;;;;;OAOG;IACH,QAAQ,CAAC,GAAG,IAAI,EAAE,SAAS,OAAO,EAAE,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAAC;IAEtE,kFAAkF;IAClF,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC,kBAAkB,GAAG,SAAS,CAAC,CAAC;IAE7D,0EAA0E;IAC1E,UAAU,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,kBAAkB,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;CACtE;AAKD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,wBAAgB,WAAW,CAAC,KAAK,SAAS,SAAS,OAAO,EAAE,EAAE,QAAQ,EACpE,MAAM,EAAE,cAAc,CAAC,KAAK,EAAE,QAAQ,CAAC,EACvC,OAAO,GAAE,kBAAkB,CAAC,QAAQ,CAAM,GACzC,iBAAiB,CAAC,QAAQ,CAAC,CA4F7B"}
@@ -0,0 +1,52 @@
1
+ import type { SnailStrategyCommonOptions } from "../typings/adapter.js";
2
+ import type { StrategyMethod } from "./shared/method.js";
3
+ import type { StrategyState } from "./shared/state.js";
4
+ /** Options accepted by {@link useFetcher}. */
5
+ export interface UseFetcherOptions<TData> extends SnailStrategyCommonOptions {
6
+ /**
7
+ * Mirror each request into `loading`/`data`/`error`/`code`/`message`.
8
+ *
9
+ * Off by default: a fetcher is usually a *background* request — a prefetch, an
10
+ * SSR pass, a silent refresh — and writing to the caller's state would make a
11
+ * spinner appear for work nobody asked to watch. Turn it on when the fetcher is
12
+ * the only thing driving a visible view.
13
+ */
14
+ withState?: boolean;
15
+ }
16
+ /**
17
+ * The always-present surface of {@link useFetcher}.
18
+ *
19
+ * Returned whether or not state is enabled, because `abort()` and the lifecycle
20
+ * callbacks have nothing to do with rendering.
21
+ */
22
+ export interface UseFetcherCore<TData, TArgs extends readonly unknown[] = readonly unknown[]> {
23
+ /** Run the request. Resolves with the unwrapped payload. */
24
+ fetch(...args: TArgs): Promise<TData>;
25
+ /** Abort the in-flight request, if any. */
26
+ abort(): void;
27
+ onSuccess(callback: (data: TData) => void): () => void;
28
+ onError(callback: (error: unknown) => void): () => void;
29
+ onFinish(callback: () => void): () => void;
30
+ }
31
+ /** {@link useFetcher} with `withState: true`. */
32
+ export interface UseFetcherResult<TData, TArgs extends readonly unknown[] = readonly unknown[]> extends UseFetcherCore<TData, TArgs>, StrategyState<TData> {
33
+ }
34
+ /** Options accepted by {@link useFetcher} when state is requested. */
35
+ export type UseFetcherStateOptions<TData> = UseFetcherOptions<TData> & {
36
+ withState: true;
37
+ };
38
+ /**
39
+ * Run an api method without a view.
40
+ *
41
+ * ```ts
42
+ * const prefetch = useFetcher(userApi.getUser);
43
+ * await prefetch.fetch("1"); // warms the cache, touches no state
44
+ * ```
45
+ *
46
+ * This is the hook for prefetching, SSR and background refresh: it deliberately
47
+ * has no `loading`/`data`/`error` handles unless `withState` asks for them, so
48
+ * calling it can never make an unrelated spinner appear.
49
+ */
50
+ export declare function useFetcher<TArgs extends readonly unknown[], TData>(method: StrategyMethod<TArgs, TData>, options: UseFetcherStateOptions<TData>): UseFetcherResult<TData, TArgs>;
51
+ export declare function useFetcher<TArgs extends readonly unknown[], TData>(method: StrategyMethod<TArgs, TData>, options?: UseFetcherOptions<TData>): UseFetcherCore<TData, TArgs>;
52
+ //# sourceMappingURL=use-fetcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-fetcher.d.ts","sourceRoot":"","sources":["../../src/strategies/use-fetcher.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,oBAAoB,CAAC;AAIrE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEtD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAEpD,8CAA8C;AAC9C,MAAM,WAAW,iBAAiB,CAAC,KAAK,CAAE,SAAQ,0BAA0B;IAC1E;;;;;;;OAOG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;;;;GAKG;AACH,MAAM,WAAW,cAAc,CAC7B,KAAK,EACL,KAAK,SAAS,SAAS,OAAO,EAAE,GAAG,SAAS,OAAO,EAAE;IAErD,4DAA4D;IAC5D,KAAK,CAAC,GAAG,IAAI,EAAE,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;IAEtC,2CAA2C;IAC3C,KAAK,IAAI,IAAI,CAAC;IAEd,SAAS,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,KAAK,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IACvD,OAAO,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IACxD,QAAQ,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC;CAC5C;AAED,iDAAiD;AACjD,MAAM,WAAW,gBAAgB,CAC/B,KAAK,EACL,KAAK,SAAS,SAAS,OAAO,EAAE,GAAG,SAAS,OAAO,EAAE,CACrD,SAAQ,cAAc,CAAC,KAAK,EAAE,KAAK,CAAC,EAClC,aAAa,CAAC,KAAK,CAAC;CAAG;AAE3B,sEAAsE;AACtE,MAAM,MAAM,sBAAsB,CAAC,KAAK,IAAI,iBAAiB,CAAC,KAAK,CAAC,GAAG;IAAE,SAAS,EAAE,IAAI,CAAA;CAAE,CAAC;AAE3F;;;;;;;;;;;GAWG;AACH,wBAAgB,UAAU,CAAC,KAAK,SAAS,SAAS,OAAO,EAAE,EAAE,KAAK,EAChE,MAAM,EAAE,cAAc,CAAC,KAAK,EAAE,KAAK,CAAC,EACpC,OAAO,EAAE,sBAAsB,CAAC,KAAK,CAAC,GACrC,gBAAgB,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;AAClC,wBAAgB,UAAU,CAAC,KAAK,SAAS,SAAS,OAAO,EAAE,EAAE,KAAK,EAChE,MAAM,EAAE,cAAc,CAAC,KAAK,EAAE,KAAK,CAAC,EACpC,OAAO,CAAC,EAAE,iBAAiB,CAAC,KAAK,CAAC,GACjC,cAAc,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC"}
@@ -0,0 +1,101 @@
1
+ import type { SnailStateRef, SnailStrategyCommonOptions } from "../typings/adapter.js";
2
+ import type { StrategyMethod } from "./shared/method.js";
3
+ import type { StrategyState } from "./shared/state.js";
4
+ /**
5
+ * The request descriptor a paginated method receives as its single argument.
6
+ *
7
+ * ```ts
8
+ * @Get("/users")
9
+ * list(@Query() query: PageRequest): Promise<Page<User>> { return null!; }
10
+ * ```
11
+ *
12
+ * The hook owns paging, so it — not the caller — supplies these two values. A
13
+ * method that declares extra query fields (`PageRequest & { keyword: string }`)
14
+ * still matches, because the hook's argument is assignable to the wider shape.
15
+ */
16
+ export interface PageRequest {
17
+ /** 1-based page number. */
18
+ page: number;
19
+ /** Items per page. */
20
+ pageSize: number;
21
+ }
22
+ /** Options accepted by {@link usePagination}. */
23
+ export interface UsePaginationOptions<TData> extends SnailStrategyCommonOptions {
24
+ /** Page the hook starts on. Defaults to `1`. */
25
+ initialPage?: number;
26
+ /** Items per page the hook starts with. Defaults to `10`. */
27
+ initialPageSize?: number;
28
+ /**
29
+ * Read the total row count out of the payload.
30
+ *
31
+ * Defaults to `payload.total ?? payload.count ?? payload.length` — the three
32
+ * shapes backends actually return. Without a trustworthy total the hook falls
33
+ * back to "a short page is the last page", which cannot detect the final page
34
+ * when it happens to be exactly full.
35
+ */
36
+ total?: (payload: TData) => number;
37
+ /**
38
+ * Read the page's rows out of the payload.
39
+ *
40
+ * Defaults to the payload itself when it is an array, otherwise
41
+ * `payload.list ?? payload.items`.
42
+ */
43
+ list?: (payload: TData) => unknown[];
44
+ /**
45
+ * Append each page to `list` instead of replacing it.
46
+ *
47
+ * This is the infinite-scroll mode. It is off by default because the *replacing*
48
+ * behaviour is what a table with page controls needs, and appending silently
49
+ * grows the array forever.
50
+ */
51
+ append?: boolean;
52
+ /**
53
+ * Fetch the next page in the background and serve it instantly on `next()`.
54
+ *
55
+ * Costs one extra request per page, so it is off by default; useful for a
56
+ * "next" button a user is likely to press. A preload never touches
57
+ * `loading`/`data`: it is speculative work and must not flash a spinner.
58
+ */
59
+ preloadNext?: boolean;
60
+ }
61
+ /** What {@link usePagination} returns. */
62
+ export interface UsePaginationResult<TData> extends StrategyState<TData> {
63
+ /** Current page, 1-based. */
64
+ readonly page: SnailStateRef<number>;
65
+ /** Items per page. */
66
+ readonly pageSize: SnailStateRef<number>;
67
+ /** Total row count, as read by the `total` extractor. */
68
+ readonly total: SnailStateRef<number>;
69
+ /** Rows, accumulated or replaced according to `append`. */
70
+ readonly list: SnailStateRef<unknown[]>;
71
+ /** `true` once the last page has been loaded. */
72
+ readonly isLastPage: SnailStateRef<boolean>;
73
+ /** Load the next page. A no-op at the last page. */
74
+ next(): Promise<TData | undefined>;
75
+ /** Load the previous page. A no-op at the first page. */
76
+ prev(): Promise<TData | undefined>;
77
+ /** Jump to a page, clamped into range. A no-op on the current page. */
78
+ goTo(page: number): Promise<TData | undefined>;
79
+ /** Go back to the first page and re-fetch it. */
80
+ reload(): Promise<TData | undefined>;
81
+ /** Change the page size, reset to page 1 and re-fetch. */
82
+ changePageSize(pageSize: number): Promise<TData | undefined>;
83
+ }
84
+ /**
85
+ * Page through one api method.
86
+ *
87
+ * ```ts
88
+ * const users = usePagination(userApi.list, {
89
+ * total: (payload) => payload.total,
90
+ * list: (payload) => payload.rows
91
+ * });
92
+ * await users.reload();
93
+ * await users.next();
94
+ * ```
95
+ *
96
+ * `next()`/`prev()` are **no-ops at the bounds and fire no request**: a user
97
+ * holding down the "next" button at the last page must not hammer the server, and
98
+ * the promise still resolves (with `undefined`) so an `await` never hangs.
99
+ */
100
+ export declare function usePagination<TData>(method: StrategyMethod<[PageRequest], TData>, options?: UsePaginationOptions<TData>): UsePaginationResult<TData>;
101
+ //# sourceMappingURL=use-pagination.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-pagination.d.ts","sourceRoot":"","sources":["../../src/strategies/use-pagination.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,0BAA0B,EAAE,MAAM,oBAAoB,CAAC;AAGpF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEtD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAEpD;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,WAAW;IAC1B,2BAA2B;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,sBAAsB;IACtB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,iDAAiD;AACjD,MAAM,WAAW,oBAAoB,CAAC,KAAK,CAAE,SAAQ,0BAA0B;IAC7E,gDAAgD;IAChD,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,6DAA6D;IAC7D,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB;;;;;;;OAOG;IACH,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,KAAK,KAAK,MAAM,CAAC;IAEnC;;;;;OAKG;IACH,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,KAAK,KAAK,OAAO,EAAE,CAAC;IAErC;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,0CAA0C;AAC1C,MAAM,WAAW,mBAAmB,CAAC,KAAK,CAAE,SAAQ,aAAa,CAAC,KAAK,CAAC;IACtE,6BAA6B;IAC7B,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAErC,sBAAsB;IACtB,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAEzC,yDAAyD;IACzD,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAEtC,2DAA2D;IAC3D,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC,OAAO,EAAE,CAAC,CAAC;IAExC,iDAAiD;IACjD,QAAQ,CAAC,UAAU,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC;IAE5C,oDAAoD;IACpD,IAAI,IAAI,OAAO,CAAC,KAAK,GAAG,SAAS,CAAC,CAAC;IAEnC,yDAAyD;IACzD,IAAI,IAAI,OAAO,CAAC,KAAK,GAAG,SAAS,CAAC,CAAC;IAEnC,uEAAuE;IACvE,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,GAAG,SAAS,CAAC,CAAC;IAE/C,iDAAiD;IACjD,MAAM,IAAI,OAAO,CAAC,KAAK,GAAG,SAAS,CAAC,CAAC;IAErC,0DAA0D;IAC1D,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,GAAG,SAAS,CAAC,CAAC;CAC9D;AAoBD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,aAAa,CAAC,KAAK,EACjC,MAAM,EAAE,cAAc,CAAC,CAAC,WAAW,CAAC,EAAE,KAAK,CAAC,EAC5C,OAAO,GAAE,oBAAoB,CAAC,KAAK,CAAM,GACxC,mBAAmB,CAAC,KAAK,CAAC,CA4K5B"}
@@ -0,0 +1,50 @@
1
+ import type { SnailStrategyCommonOptions } from "../typings/adapter.js";
2
+ import type { StrategyMethod } from "./shared/method.js";
3
+ import type { StrategyState } from "./shared/state.js";
4
+ /** Options accepted by {@link useRequest}. */
5
+ export interface UseRequestOptions<TData> extends SnailStrategyCommonOptions {
6
+ /** Value `data` starts at, before the first successful send. */
7
+ initialData?: TData;
8
+ /**
9
+ * Reset `data` back to `initialData` before every send.
10
+ *
11
+ * Off by default because the previous payload is usually still worth rendering
12
+ * while the next one is in flight (no flicker on refresh). Turn it on for a
13
+ * detail pane that must not show the *previous* record's data while loading the
14
+ * next one.
15
+ */
16
+ resetOnSend?: boolean;
17
+ }
18
+ /**
19
+ * What {@link useRequest} returns.
20
+ *
21
+ * `send` resolves with the **unwrapped payload** (`result.data`) rather than the
22
+ * whole `SnailResult`: the envelope's `code`/`message` are already on the state
23
+ * handles, and every caller that wants more can still reach the method's own
24
+ * `result`.
25
+ */
26
+ export interface UseRequestResult<TData, TArgs extends readonly unknown[] = readonly unknown[]> extends StrategyState<TData> {
27
+ send(...args: TArgs): Promise<TData>;
28
+ }
29
+ /**
30
+ * Drive one api method from component state.
31
+ *
32
+ * ```ts
33
+ * const user = useRequest(userApi.getUser);
34
+ * await user.send("1"); // → payload
35
+ * user.data.value; // → the same payload
36
+ * ```
37
+ *
38
+ * The hook owns exactly one `SnailMethod`, built from the arguments of the first
39
+ * send and reused afterwards. That is what keeps the reactive handles stable
40
+ * across re-sends — see `shared/method.ts` for why a second instance would break
41
+ * the UI — while `send("2")` still overrides the arguments per call.
42
+ *
43
+ * One instance also means one request at a time: `SnailMethod` resets its context
44
+ * at the start of every `send()`, so a second `send()` issued while the first is
45
+ * still in flight would leave the first reading a context that belongs to the
46
+ * second. Call `abort()` first, or use `useWatcher`/`useAutoRequest`, which
47
+ * collapse bursts for exactly this reason.
48
+ */
49
+ export declare function useRequest<TArgs extends readonly unknown[], TData>(method: StrategyMethod<TArgs, TData>, options?: UseRequestOptions<TData>): UseRequestResult<TData, TArgs>;
50
+ //# sourceMappingURL=use-request.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-request.d.ts","sourceRoot":"","sources":["../../src/strategies/use-request.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,oBAAoB,CAAC;AAIrE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEtD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAEpD,8CAA8C;AAC9C,MAAM,WAAW,iBAAiB,CAAC,KAAK,CAAE,SAAQ,0BAA0B;IAC1E,gEAAgE;IAChE,WAAW,CAAC,EAAE,KAAK,CAAC;IAEpB;;;;;;;OAOG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,gBAAgB,CAC/B,KAAK,EACL,KAAK,SAAS,SAAS,OAAO,EAAE,GAAG,SAAS,OAAO,EAAE,CACrD,SAAQ,aAAa,CAAC,KAAK,CAAC;IAC5B,IAAI,CAAC,GAAG,IAAI,EAAE,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;CACtC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,UAAU,CAAC,KAAK,SAAS,SAAS,OAAO,EAAE,EAAE,KAAK,EAChE,MAAM,EAAE,cAAc,CAAC,KAAK,EAAE,KAAK,CAAC,EACpC,OAAO,GAAE,iBAAiB,CAAC,KAAK,CAAM,GACrC,gBAAgB,CAAC,KAAK,EAAE,KAAK,CAAC,CAuDhC"}