@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,46 @@
1
+ import type { SnailStateRef } from "../typings/adapter.js";
2
+ import type { StrategyMethod } from "./shared/method.js";
3
+ import type { RetryOptions, RetryPredicate } from "./shared/retry.js";
4
+ import type { StrategyState } from "./shared/state.js";
5
+ import type { UseRequestOptions } from "./use-request.js";
6
+ /** Options accepted by {@link useRetriableRequest}. */
7
+ export interface UseRetriableRequestOptions<TData> extends UseRequestOptions<TData>, RetryOptions {
8
+ /**
9
+ * Decide whether one failed attempt deserves another.
10
+ *
11
+ * Receives the attempted number of the *next* try (1-based), so a caller can
12
+ * cap retries by reason as well as by count. The default retries everything
13
+ * except a cancellation, which is never retried: the caller asked for the
14
+ * request to stop, and a retry would ignore that instruction.
15
+ */
16
+ retryOn?: RetryPredicate;
17
+ }
18
+ /** What {@link useRetriableRequest} returns. */
19
+ export interface UseRetriableRequestResult<TData, TArgs extends readonly unknown[] = readonly unknown[]> extends StrategyState<TData> {
20
+ send(...args: TArgs): Promise<TData>;
21
+ /** Attempts made by the most recent `send()`. Starts at `0`. */
22
+ readonly attempts: SnailStateRef<number>;
23
+ }
24
+ /**
25
+ * A request that heals itself.
26
+ *
27
+ * ```ts
28
+ * const save = useRetriableRequest(api.save, { retries: 3, delayMs: 200 });
29
+ * await save.send(payload); // may hit the network up to 4 times
30
+ * save.attempts.value; // how many tries it actually took
31
+ * ```
32
+ *
33
+ * ## Cancellation is the hard part
34
+ *
35
+ * The hook keeps its own `AbortController` for the backoff *between* attempts,
36
+ * because at that moment there is no in-flight request to cancel. `abort()` trips
37
+ * both, so a `abort()` during a 30 second delay rejects immediately instead of
38
+ * leaving the caller's promise pending until the timer fires. A cancellation is
39
+ * never counted as a failed attempt and never written to `error`.
40
+ *
41
+ * `attempts` is exposed because it is genuinely useful for telemetry ("this call
42
+ * needed three tries") and because it makes the retry loop testable without
43
+ * counting requests.
44
+ */
45
+ export declare function useRetriableRequest<TArgs extends readonly unknown[], TData>(method: StrategyMethod<TArgs, TData>, options?: UseRetriableRequestOptions<TData>): UseRetriableRequestResult<TData, TArgs>;
46
+ //# sourceMappingURL=use-retriable-request.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-retriable-request.d.ts","sourceRoot":"","sources":["../../src/strategies/use-retriable-request.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAGxD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAMtD,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAEnE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAEpD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAEvD,uDAAuD;AACvD,MAAM,WAAW,0BAA0B,CAAC,KAAK,CAC/C,SAAQ,iBAAiB,CAAC,KAAK,CAAC,EAC9B,YAAY;IACd;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,cAAc,CAAC;CAC1B;AAED,gDAAgD;AAChD,MAAM,WAAW,yBAAyB,CACxC,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;IAErC,gEAAgE;IAChE,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;CAC1C;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,SAAS,SAAS,OAAO,EAAE,EAAE,KAAK,EACzE,MAAM,EAAE,cAAc,CAAC,KAAK,EAAE,KAAK,CAAC,EACpC,OAAO,GAAE,0BAA0B,CAAC,KAAK,CAAM,GAC9C,yBAAyB,CAAC,KAAK,EAAE,KAAK,CAAC,CA2EzC"}
@@ -0,0 +1,103 @@
1
+ import type { SnailStateAdapter, SnailStateRef } from "../typings/adapter.js";
2
+ import type { SnailSseEndpoint, SnailSseMessage } from "../typings/stream.js";
3
+ /**
4
+ * A connection that can report the events it parsed.
5
+ *
6
+ * `SnailConnection` deliberately exposes only `close`/`connected`/`opened`/
7
+ * `closed`, because a connection whose messages are delivered to `@SseEvent()`
8
+ * handlers on the endpoint class needs no other surface. `useSSE` has no such
9
+ * class, so it looks for this extension on the connection and, failing that, on
10
+ * the endpoint. Without either, `connected` still tracks the transport but
11
+ * `messages` stays empty — see the note on {@link useSSE}.
12
+ */
13
+ export interface SseConnectionTap {
14
+ /** Register a message listener. Returns an unsubscribe function. */
15
+ onMessage?(listener: (message: SnailSseMessage) => void): () => void;
16
+ }
17
+ /** A `SnailSseEndpoint` that can also hand out messages directly. */
18
+ export interface SseEndpoint extends SnailSseEndpoint {
19
+ /**
20
+ * Register a message listener for the connections this endpoint opens.
21
+ *
22
+ * Preferred over {@link SseConnectionTap} because it can be called once, before
23
+ * `open()`, which is the only way to be sure no message between the connect and
24
+ * the subscription is lost.
25
+ */
26
+ subscribe?(listener: (message: SnailSseMessage) => void): () => void;
27
+ }
28
+ /** Options accepted by {@link useSSE}. */
29
+ export interface UseSseOptions {
30
+ /** State adapter. Defaults to the globally registered one. */
31
+ adapter?: SnailStateAdapter;
32
+ /**
33
+ * Open the connection as soon as the hook is created. Defaults to `false`,
34
+ * matching `SnailSseEndpoint`'s "nothing connects until `open()`" contract.
35
+ */
36
+ immediate?: boolean;
37
+ /**
38
+ * Maximum buffered messages. Defaults to `100`, dropping the oldest first.
39
+ *
40
+ * Bounded on purpose: an SSE feed that runs for hours with an unbounded array
41
+ * is a memory leak that ends in a frozen tab, and no view can render a hundred
42
+ * thousand rows anyway.
43
+ */
44
+ maxMessages?: number;
45
+ /** Keep only the messages this returns `true` for. */
46
+ filter?: (message: SnailSseMessage) => boolean;
47
+ /** Called for every accepted message, after the buffer was updated. */
48
+ onMessage?: (message: SnailSseMessage) => void;
49
+ }
50
+ /** What {@link useSSE} returns. */
51
+ export interface UseSseResult {
52
+ /** Buffered messages, oldest first. */
53
+ readonly messages: SnailStateRef<SnailSseMessage[]>;
54
+ /** The most recent accepted message. */
55
+ readonly lastMessage: SnailStateRef<SnailSseMessage | undefined>;
56
+ /** `true` between a successful connect and the next close. */
57
+ readonly connected: SnailStateRef<boolean>;
58
+ /** Why the connection failed, or why a filter/handler threw. */
59
+ readonly error: SnailStateRef<unknown>;
60
+ /** Connect. A no-op while an open connection is already live. */
61
+ open(): void;
62
+ /** Disconnect, stop the reconnect loop and detach the listener. */
63
+ close(): void;
64
+ /** Empty the message buffer. Leaves the connection alone. */
65
+ clear(): void;
66
+ /** Resolved values, subscribing the current component when the adapter supports it. */
67
+ bind(): {
68
+ messages: SnailSseMessage[];
69
+ lastMessage: SnailSseMessage | undefined;
70
+ connected: boolean;
71
+ error: unknown;
72
+ };
73
+ }
74
+ /**
75
+ * Consume a Server-Sent Events endpoint as reactive state.
76
+ *
77
+ * ```ts
78
+ * const events = Service.createSse(Events);
79
+ * const feed = useSSE(events, { maxMessages: 50 });
80
+ * feed.open();
81
+ * feed.messages.value; // SnailSseMessage[]
82
+ * feed.close();
83
+ * ```
84
+ *
85
+ * ## The message tap
86
+ *
87
+ * `Service.createSse()` returns an object whose only member is `open()`, and the
88
+ * messages it parses are dispatched to the decorated class's methods
89
+ * (`@SseEvent()`), not to the caller. `useSSE` therefore accepts an endpoint that
90
+ * additionally implements `subscribe(listener)`, or a connection that implements
91
+ * `onMessage(listener)`. Both extend the core contract without changing it; an
92
+ * endpoint that implements neither still reports `connected`/`error` faithfully,
93
+ * it just never fills `messages`.
94
+ *
95
+ * ## Never an unhandled rejection
96
+ *
97
+ * A rejected `opened` promise is exactly how a failed SSE connect is reported, and
98
+ * nothing else in the application will ever await it. Both `opened` and `closed`
99
+ * are handled here, so a dead server cannot take the process down with an
100
+ * unhandled rejection.
101
+ */
102
+ export declare function useSSE(endpoint: SseEndpoint, options?: UseSseOptions): UseSseResult;
103
+ //# sourceMappingURL=use-sse.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-sse.d.ts","sourceRoot":"","sources":["../../src/strategies/use-sse.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,aAAa,EAA8B,MAAM,oBAAoB,CAAC;AACvG,OAAO,KAAK,EAAmB,gBAAgB,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAI5F;;;;;;;;;GASG;AACH,MAAM,WAAW,gBAAgB;IAC/B,oEAAoE;IACpE,SAAS,CAAC,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;CACtE;AAED,qEAAqE;AACrE,MAAM,WAAW,WAAY,SAAQ,gBAAgB;IACnD;;;;;;OAMG;IACH,SAAS,CAAC,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;CACtE;AAED,0CAA0C;AAC1C,MAAM,WAAW,aAAa;IAC5B,8DAA8D;IAC9D,OAAO,CAAC,EAAE,iBAAiB,CAAC;IAE5B;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,sDAAsD;IACtD,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,OAAO,CAAC;IAE/C,uEAAuE;IACvE,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,IAAI,CAAC;CAChD;AAED,mCAAmC;AACnC,MAAM,WAAW,YAAY;IAC3B,uCAAuC;IACvC,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC,eAAe,EAAE,CAAC,CAAC;IAEpD,wCAAwC;IACxC,QAAQ,CAAC,WAAW,EAAE,aAAa,CAAC,eAAe,GAAG,SAAS,CAAC,CAAC;IAEjE,8DAA8D;IAC9D,QAAQ,CAAC,SAAS,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC;IAE3C,gEAAgE;IAChE,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC;IAEvC,iEAAiE;IACjE,IAAI,IAAI,IAAI,CAAC;IAEb,mEAAmE;IACnE,KAAK,IAAI,IAAI,CAAC;IAEd,6DAA6D;IAC7D,KAAK,IAAI,IAAI,CAAC;IAEd,uFAAuF;IACvF,IAAI,IAAI;QACN,QAAQ,EAAE,eAAe,EAAE,CAAC;QAC5B,WAAW,EAAE,eAAe,GAAG,SAAS,CAAC;QACzC,SAAS,EAAE,OAAO,CAAC;QACnB,KAAK,EAAE,OAAO,CAAC;KAChB,CAAC;CACH;AAsBD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,MAAM,CAAC,QAAQ,EAAE,WAAW,EAAE,OAAO,GAAE,aAAkB,GAAG,YAAY,CAyHvF"}
@@ -0,0 +1,88 @@
1
+ import type { SnailPluginObject } from "../typings/plugin.js";
2
+ /** Options accepted by {@link useTokenAuth}. */
3
+ export interface TokenAuthOptions {
4
+ /**
5
+ * Current token, from wherever the application keeps it.
6
+ *
7
+ * May be async (a secure store, a cookie API). Returning `null`/`undefined` is
8
+ * normal — it means "not logged in", and the header is simply not injected.
9
+ */
10
+ token: () => string | null | undefined | Promise<string | null | undefined>;
11
+ /**
12
+ * Obtain a fresh token.
13
+ *
14
+ * Called **at most once** per wave of 401s; see the class documentation on
15
+ * {@link useTokenAuth}. It must resolve with the new token; rejecting aborts the
16
+ * whole queue.
17
+ */
18
+ refresh: () => Promise<string>;
19
+ /** Header to write. Defaults to `"authorization"`. */
20
+ header?: string;
21
+ /** Prefix before the token. Defaults to `"Bearer"`; pass `""` for a raw token. */
22
+ scheme?: string;
23
+ /** Called when a 401 could not be recovered — the refresh failed, or the replay 401'd again. */
24
+ onUnauthorized?: (error: unknown) => void;
25
+ }
26
+ /** What {@link useTokenAuth} hands back. */
27
+ export interface TokenAuthHandle {
28
+ /** Install with `Service.use(auth.plugin)`. */
29
+ readonly plugin: SnailPluginObject<TokenAuthOptions>;
30
+ /** Replace the cached token, e.g. after a successful login. */
31
+ setToken(token: string | null | undefined): void;
32
+ /** The cached token. Synchronous: an async `token()` cannot be awaited here. */
33
+ getToken(): string | undefined;
34
+ /** Forget the cached token so `token()` is consulted again on the next request. */
35
+ clearToken(): void;
36
+ }
37
+ /**
38
+ * Bearer-token authentication with single-flight refresh, as a plugin.
39
+ *
40
+ * ```ts
41
+ * const auth = useTokenAuth({
42
+ * token: () => localStorage.getItem("token"),
43
+ * refresh: async () => (await api.refresh().send()).token,
44
+ * onUnauthorized: () => router.push("/login")
45
+ * });
46
+ *
47
+ * Service.use(auth.plugin);
48
+ * auth.setToken("...");
49
+ * ```
50
+ *
51
+ * ## Why a plugin and not a hook
52
+ *
53
+ * A token is global: every request of the service has to carry it, and a 401 on
54
+ * any one of them has to invalidate the token for all of them. A per-method hook
55
+ * could not see the other requests, so refresh coordination would be impossible.
56
+ *
57
+ * ## Exactly one refresh per wave of 401s
58
+ *
59
+ * Three parallel requests, one expired token, three 401s. Refreshing per request
60
+ * would fire three refreshes and, with a rotating refresh token, two of them would
61
+ * fail and log the user out. Two mechanisms prevent that:
62
+ *
63
+ * 1. a request that 401s while a refresh is already in flight **joins** it instead
64
+ * of starting a second one;
65
+ * 2. a request whose 401 was produced by a token that has since been replaced
66
+ * replays directly, without refreshing again.
67
+ *
68
+ * The token each request was sent with is remembered in `ctx.state` (which the core
69
+ * clears per send), and that is what makes rule 2 decidable without keeping
70
+ * timers around.
71
+ *
72
+ * ## Replaying
73
+ *
74
+ * `onError` cannot recover a failure — the core rethrows after every `onError`
75
+ * hook — so the recovery lives in `beforeRequest`, around `await next()`. The
76
+ * replay re-runs the `requestInterceptor` reduce, the transport and the
77
+ * `afterResponse` chain, so headers a plugin signs are recomputed for the retried
78
+ * call. `beforeRequest` hooks are deliberately *not* re-entered: the refresh
79
+ * decision has already been made, and re-entering could loop.
80
+ *
81
+ * ## What this cannot recover
82
+ *
83
+ * Only a **transport** 401 (HTTP 401). A backend that reports `{ code: 401 }` with
84
+ * HTTP 200 fails in `finalize`, after the chain, where no hook can intervene; that
85
+ * response reaches the caller as a `SnailResponseError`.
86
+ */
87
+ export declare function useTokenAuth(options: TokenAuthOptions): TokenAuthHandle;
88
+ //# sourceMappingURL=use-token-auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-token-auth.d.ts","sourceRoot":"","sources":["../../src/strategies/use-token-auth.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAa,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAEtE,gDAAgD;AAChD,MAAM,WAAW,gBAAgB;IAC/B;;;;;OAKG;IACH,KAAK,EAAE,MAAM,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;IAE5E;;;;;;OAMG;IACH,OAAO,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IAE/B,sDAAsD;IACtD,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB,kFAAkF;IAClF,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB,gGAAgG;IAChG,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CAC3C;AAED,4CAA4C;AAC5C,MAAM,WAAW,eAAe;IAC9B,+CAA+C;IAC/C,QAAQ,CAAC,MAAM,EAAE,iBAAiB,CAAC,gBAAgB,CAAC,CAAC;IAErD,+DAA+D;IAC/D,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,IAAI,CAAC;IAEjD,gFAAgF;IAChF,QAAQ,IAAI,MAAM,GAAG,SAAS,CAAC;IAE/B,mFAAmF;IACnF,UAAU,IAAI,IAAI,CAAC;CACpB;AA4BD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,gBAAgB,GAAG,eAAe,CAoKvE"}
@@ -0,0 +1,88 @@
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
+ /** Lifecycle of one queued file. */
5
+ export type UploadFileStatus = "pending" | "uploading" | "success" | "error";
6
+ /** Everything the hook tracks about one file. */
7
+ export interface UploadFileState {
8
+ /** Stable id, used by `retry(id)`. */
9
+ readonly id: string;
10
+ /** The file itself. */
11
+ readonly file: File;
12
+ readonly status: UploadFileStatus;
13
+ /** `0`–`1`, from the transport when it reports progress, otherwise completed-or-not. */
14
+ readonly progress: number;
15
+ /** Failure of this file. Untouched for a cancellation. */
16
+ readonly error: unknown;
17
+ /** Unwrapped payload of this file's successful response. */
18
+ readonly response: unknown;
19
+ }
20
+ /** Snapshot handed to `onProgress`. */
21
+ export interface UploaderProgress {
22
+ /** Average progress across every queued file, `0`–`1`. */
23
+ progress: number;
24
+ files: readonly UploadFileState[];
25
+ }
26
+ /** Options accepted by {@link useUploader}. */
27
+ export interface UseUploaderOptions<TData> extends SnailStrategyCommonOptions {
28
+ /** Files in flight at once. Defaults to `3`. */
29
+ concurrency?: number;
30
+ /**
31
+ * Accept more than one file per `upload()` call.
32
+ *
33
+ * Defaults to `true`. When `false`, only the first entry of a `FileList` is
34
+ * queued and the rest are dropped — a single-file avatar input should not
35
+ * silently upload 40 holiday photos because a user multi-selected.
36
+ */
37
+ multiple?: boolean;
38
+ /** Called whenever aggregate or per-file progress changes. */
39
+ onProgress?: (state: UploaderProgress) => void;
40
+ /** FormData field the file is written to. Defaults to `"file"`. */
41
+ fieldName?: string;
42
+ }
43
+ /** What {@link useUploader} returns. */
44
+ export interface UseUploaderResult<TData> extends StrategyState<TData> {
45
+ /**
46
+ * Queue files and resolve once the queue drains.
47
+ *
48
+ * Never rejects: per-file failures live on `files[i].error`, and one bad file
49
+ * must not abort the batch — that is the difference between "3 of 5 uploaded"
50
+ * and "nothing happened".
51
+ */
52
+ upload(files: File | File[] | FileList | null | undefined): Promise<void>;
53
+ /** Every queued file, in queue order. */
54
+ readonly files: SnailStateRef<UploadFileState[]>;
55
+ /** Aggregate progress, `0`–`1`. Reaches `1` when every file succeeded. */
56
+ readonly progress: SnailStateRef<number>;
57
+ /** Re-queue one failed file. */
58
+ retry(id: string): void;
59
+ }
60
+ /**
61
+ * Upload files with a bounded number of parallel requests.
62
+ *
63
+ * ```ts
64
+ * const uploader = useUploader(api.upload, { concurrency: 2 });
65
+ * await uploader.upload(input.files);
66
+ * uploader.files.value; // per-file status, progress, error, response
67
+ * uploader.retry(id); // re-queue one failure
68
+ * ```
69
+ *
70
+ * ## Progress
71
+ *
72
+ * Per-file progress comes from the transport via axios' `onUploadProgress`, which
73
+ * the hook attaches to the **live** request config of each file (see
74
+ * `shared/method.ts` for why that has to happen right after `send()` starts). The
75
+ * aggregate is the mean of the per-file values, and a finished file counts as `1`
76
+ * regardless of what the transport reported — a mocked or `fetch`-based adapter
77
+ * reports nothing at all, and without that rule the bar would stop at `0`.
78
+ *
79
+ * ## `data` and `code`
80
+ *
81
+ * The state handles are shared by the whole batch, so `data` holds the payload of
82
+ * the most recently finished file. Use `files[i].response` for anything
83
+ * per-file. `error` mirrors the file list: it holds the first failed file's error
84
+ * while any file is in the `error` state, and clears itself once the last failure
85
+ * has been retried successfully.
86
+ */
87
+ export declare function useUploader<TData>(method: StrategyMethod<[FormData], TData>, options?: UseUploaderOptions<TData>): UseUploaderResult<TData>;
88
+ //# sourceMappingURL=use-uploader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-uploader.d.ts","sourceRoot":"","sources":["../../src/strategies/use-uploader.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,0BAA0B,EAAE,MAAM,oBAAoB,CAAC;AAGpF,OAAO,KAAK,EAAgB,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAGpE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAEpD,oCAAoC;AACpC,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,OAAO,CAAC;AAE7E,iDAAiD;AACjD,MAAM,WAAW,eAAe;IAC9B,sCAAsC;IACtC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IAEpB,uBAAuB;IACvB,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;IAEpB,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAC;IAElC,wFAAwF;IACxF,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAE1B,0DAA0D;IAC1D,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IAExB,4DAA4D;IAC5D,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;CAC5B;AAED,uCAAuC;AACvC,MAAM,WAAW,gBAAgB;IAC/B,0DAA0D;IAC1D,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,SAAS,eAAe,EAAE,CAAC;CACnC;AAED,+CAA+C;AAC/C,MAAM,WAAW,kBAAkB,CAAC,KAAK,CAAE,SAAQ,0BAA0B;IAC3E,gDAAgD;IAChD,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB,8DAA8D;IAC9D,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAE/C,mEAAmE;IACnE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wCAAwC;AACxC,MAAM,WAAW,iBAAiB,CAAC,KAAK,CAAE,SAAQ,aAAa,CAAC,KAAK,CAAC;IACpE;;;;;;OAMG;IACH,MAAM,CAAC,KAAK,EAAE,IAAI,GAAG,IAAI,EAAE,GAAG,QAAQ,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE1E,yCAAyC;IACzC,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC,eAAe,EAAE,CAAC,CAAC;IAEjD,0EAA0E;IAC1E,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAEzC,gCAAgC;IAChC,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAmBD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAC/B,MAAM,EAAE,cAAc,CAAC,CAAC,QAAQ,CAAC,EAAE,KAAK,CAAC,EACzC,OAAO,GAAE,kBAAkB,CAAC,KAAK,CAAM,GACtC,iBAAiB,CAAC,KAAK,CAAC,CAkJ1B"}
@@ -0,0 +1,69 @@
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 useWatcher}. */
5
+ export interface UseWatcherOptions<TData> extends UseRequestOptions<TData> {
6
+ /**
7
+ * Values that trigger a re-send when they change.
8
+ *
9
+ * Must be a function returning **plain values** — `() => [page.value]`. State
10
+ * handles are unwrapped too when the adapter can identify them (see
11
+ * `shared/watcher.ts`), but the handle form is a heuristic: only a `{ value }`
12
+ * object is recognised, so a genuine data object with a lone `value` key is
13
+ * indistinguishable from a handle.
14
+ */
15
+ watching: () => readonly unknown[];
16
+ /**
17
+ * Wait for this many milliseconds of quiet, then send once.
18
+ *
19
+ * Takes precedence over `throttle` when both are set: the two express opposite
20
+ * intents ("wait until it settles" vs "send immediately, then at most once per
21
+ * window"), and silently honouring one of them is better than a hybrid whose
22
+ * behaviour depends on the call order.
23
+ */
24
+ debounce?: number;
25
+ /** Send on the leading edge, then at most once per window. */
26
+ throttle?: number;
27
+ }
28
+ /** What {@link useWatcher} returns. */
29
+ export interface UseWatcherResult<TData, TArgs extends readonly unknown[] = readonly unknown[]> extends UseRequestResult<TData, TArgs> {
30
+ /**
31
+ * Whether `send()` collapses unchanged watched values.
32
+ *
33
+ * `true` (default) is the watching behaviour. Setting it to `false` turns every
34
+ * `send()` into an unconditional request, which is the escape hatch for a manual
35
+ * "refresh" button while the same hook keeps watching fields.
36
+ */
37
+ readonly watching: SnailStateRef<boolean>;
38
+ }
39
+ /**
40
+ * Re-send a request when watched values move.
41
+ *
42
+ * ```ts
43
+ * const keyword = createState("");
44
+ * const search = useWatcher(searchApi.find, {
45
+ * watching: () => [keyword.value],
46
+ * debounce: 200
47
+ * });
48
+ *
49
+ * keyword.value = "a"; await search.send(); // sends
50
+ * await search.send(); // same values → no request
51
+ * keyword.value = "ab"; await search.send(); // sends again
52
+ * ```
53
+ *
54
+ * ## Why `send()` is the evaluation trigger
55
+ *
56
+ * `SnailStateAdapter` exposes `create`/`read`/`write` and an optional
57
+ * `subscribe`; it has no `watch` or `effect`, and the core deliberately keeps
58
+ * framework reactivity out of the strategy layer. So there is nothing to fire at
59
+ * the *moment* a value changes. The hook therefore evaluates `watching()` when
60
+ * `send()` is called and treats an unchanged snapshot as a no-op, which keeps the
61
+ * behaviour deterministic and framework-free: a Vue/React integration can call
62
+ * `send()` from its own reactive effect, and a script calls it directly.
63
+ *
64
+ * `debounce`/`throttle` then collapse the *bursts* of `send()` calls a reactive
65
+ * effect produces. Every collapsed caller's promise settles with the single
66
+ * request's outcome, so no `await` is ever left hanging.
67
+ */
68
+ export declare function useWatcher<TArgs extends readonly unknown[], TData>(method: StrategyMethod<TArgs, TData>, options: UseWatcherOptions<TData>): UseWatcherResult<TData, TArgs>;
69
+ //# sourceMappingURL=use-watcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-watcher.d.ts","sourceRoot":"","sources":["../../src/strategies/use-watcher.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAIxD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAItD,OAAO,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEzE,8CAA8C;AAC9C,MAAM,WAAW,iBAAiB,CAAC,KAAK,CAAE,SAAQ,iBAAiB,CAAC,KAAK,CAAC;IACxE;;;;;;;;OAQG;IACH,QAAQ,EAAE,MAAM,SAAS,OAAO,EAAE,CAAC;IAEnC;;;;;;;OAOG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,8DAA8D;IAC9D,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,uCAAuC;AACvC,MAAM,WAAW,gBAAgB,CAC/B,KAAK,EACL,KAAK,SAAS,SAAS,OAAO,EAAE,GAAG,SAAS,OAAO,EAAE,CACrD,SAAQ,gBAAgB,CAAC,KAAK,EAAE,KAAK,CAAC;IACtC;;;;;;OAMG;IACH,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC;CAC3C;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,UAAU,CAAC,KAAK,SAAS,SAAS,OAAO,EAAE,EAAE,KAAK,EAChE,MAAM,EAAE,cAAc,CAAC,KAAK,EAAE,KAAK,CAAC,EACpC,OAAO,EAAE,iBAAiB,CAAC,KAAK,CAAC,GAChC,gBAAgB,CAAC,KAAK,EAAE,KAAK,CAAC,CA4GhC"}
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Framework state abstraction used by the request strategies.
3
+ *
4
+ * A strategy needs to hand the caller values that update as the request
5
+ * progresses. "Updates" means something different in every framework, so the
6
+ * strategies never touch `ref()` or `useState()` directly — they go through a
7
+ * {@link SnailStateAdapter}.
8
+ *
9
+ * A state handle is intentionally minimal: anything with a mutable `value`
10
+ * property qualifies, which is exactly what a Vue `Ref<T>` is.
11
+ */
12
+ /** A tracked value. Vue's `Ref<T>` structurally satisfies this. */
13
+ export interface SnailStateRef<T = unknown> {
14
+ value: T;
15
+ }
16
+ /** Bridge between the strategies and one UI framework's reactivity system. */
17
+ export interface SnailStateAdapter {
18
+ /** Identifier used in error messages, e.g. `"vue"`. */
19
+ readonly name: string;
20
+ /** Create a tracked value with an initial value. */
21
+ create<T>(initial: T): SnailStateRef<T>;
22
+ /** Read the current value. */
23
+ read<T>(ref: SnailStateRef<T>): T;
24
+ /** Write a new value. */
25
+ write<T>(ref: SnailStateRef<T>, value: T): void;
26
+ /**
27
+ * Subscribe to changes.
28
+ *
29
+ * Required by frameworks that re-render on subscription (React). Vue's
30
+ * `ref()` is tracked by the render effect itself, so its adapter omits this.
31
+ */
32
+ subscribe?<T>(ref: SnailStateRef<T>, listener: (value: T) => void): () => void;
33
+ /**
34
+ * Read a state during render, subscribing the current component.
35
+ *
36
+ * React's `useSyncExternalStore` lives here; Vue needs nothing.
37
+ */
38
+ useBind?<T>(ref: SnailStateRef<T>): T;
39
+ /** `true` when a value already is a state handle of this framework. */
40
+ isState?(value: unknown): boolean;
41
+ /** Release whatever the adapter allocated for this state. */
42
+ dispose?<T>(ref: SnailStateRef<T>): void;
43
+ }
44
+ /** Options shared by every request strategy. */
45
+ export interface SnailStrategyCommonOptions {
46
+ /** Run the request as soon as the strategy is created. Defaults to `false`. */
47
+ immediate?: boolean;
48
+ /**
49
+ * State adapter to use.
50
+ *
51
+ * Defaults to the globally registered adapter (Vue in the default entry).
52
+ */
53
+ adapter?: SnailStateAdapter;
54
+ /** Called after a successful request. */
55
+ onSuccess?: (data: unknown) => void;
56
+ /** Called after a failed request. */
57
+ onError?: (error: unknown) => void;
58
+ /** Called once the request settles, successfully or not. */
59
+ onFinish?: () => void;
60
+ }
61
+ //# sourceMappingURL=adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../src/typings/adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,mEAAmE;AACnE,MAAM,WAAW,aAAa,CAAC,CAAC,GAAG,OAAO;IACxC,KAAK,EAAE,CAAC,CAAC;CACV;AAED,8EAA8E;AAC9E,MAAM,WAAW,iBAAiB;IAChC,uDAAuD;IACvD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB,oDAAoD;IACpD,MAAM,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,CAAC;IAExC,8BAA8B;IAC9B,IAAI,CAAC,CAAC,EAAE,GAAG,EAAE,aAAa,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IAElC,yBAAyB;IACzB,KAAK,CAAC,CAAC,EAAE,GAAG,EAAE,aAAa,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC;IAEhD;;;;;OAKG;IACH,SAAS,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,aAAa,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IAE/E;;;;OAIG;IACH,OAAO,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,aAAa,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IAEtC,uEAAuE;IACvE,OAAO,CAAC,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC;IAElC,6DAA6D;IAC7D,OAAO,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,aAAa,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;CAC1C;AAED,gDAAgD;AAChD,MAAM,WAAW,0BAA0B;IACzC,+EAA+E;IAC/E,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB;;;;OAIG;IACH,OAAO,CAAC,EAAE,iBAAiB,CAAC;IAE5B,yCAAyC;IACzC,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IAEpC,qCAAqC;IACrC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IAEnC,4DAA4D;IAC5D,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;CACvB"}
@@ -0,0 +1,122 @@
1
+ import type { AxiosRequestConfig, Method } from "axios";
2
+ import type { SnailEnvelopeSchema, SnailResult } from "./response.js";
3
+ /** Every request verb this library can decorate. */
4
+ export type SnailMethodType = "GET" | "POST" | "PUT" | "DELETE" | "PATCH" | "HEAD" | "OPTIONS";
5
+ /** `SnailMethodType` in the lowercase form axios expects. */
6
+ export type SnailMethodTypeLower = Lowercase<SnailMethodType>;
7
+ /** Options accepted by `@Api(...)`. */
8
+ export interface SnailApiOptions {
9
+ /**
10
+ * Path prefix shared by every method of the class.
11
+ *
12
+ * Joined with the server `baseURL` and the method path, in that order.
13
+ */
14
+ url?: string;
15
+ /**
16
+ * Unique identifier of this api class.
17
+ *
18
+ * Defaults to the class name. Used for logging, cache namespacing and
19
+ * `@HitSource` resolution.
20
+ */
21
+ name?: string;
22
+ /** Per-api timeout, overriding the server timeout. */
23
+ timeout?: number;
24
+ /** Per-api adapter, overriding the server adapter. */
25
+ adapter?: AxiosRequestConfig["adapter"];
26
+ /** Per-api `responseType`, overriding the server value. */
27
+ responseType?: AxiosRequestConfig["responseType"];
28
+ /** Per-api `withCredentials`, overriding the server value. */
29
+ withCredentials?: boolean;
30
+ }
31
+ /**
32
+ * Options accepted by the request-method decorators.
33
+ *
34
+ * Extends `AxiosRequestConfig` so any axios knob can be set per method, minus
35
+ * the fields the library owns (`url` and `method`).
36
+ */
37
+ export interface SnailMethodOptions extends Omit<AxiosRequestConfig, "url" | "method" | "params" | "data"> {
38
+ /** Extra query params baked into the request. */
39
+ params?: Record<string, any>;
40
+ /** Static body baked into the request (mutually exclusive with `@Data()`). */
41
+ data?: unknown;
42
+ }
43
+ /** What `@Get("/x", { ... })` receives as its second argument. */
44
+ export type SnailMethodDecoratorOptions = SnailMethodOptions;
45
+ /** A `Method` value axios understands. */
46
+ export type SnailAxiosMethod = Method;
47
+ /**
48
+ * The payload type of a decorated method, inferred from its declared return type.
49
+ *
50
+ * ```ts
51
+ * @Get("/:id")
52
+ * getUser(@Params("id") id: string): Promise<User> { return null!; }
53
+ * // ^^^^^^^^^^^^^^ → User
54
+ * ```
55
+ *
56
+ * A method that declares nothing (implicitly `void`) yields `unknown`, which the
57
+ * caller can still override with an explicit generic:
58
+ * `userApi.getUser<MyShape>("1")`.
59
+ */
60
+ export type SnailPayloadOf<R> = Awaited<R> extends void ? unknown : Awaited<R> extends undefined ? unknown : Awaited<R>;
61
+ /**
62
+ * The proxy returned by `Service.createApi(UserApi)`.
63
+ *
64
+ * Every method carrying a request-method decorator becomes a function that
65
+ * builds a `SnailMethod` instead of running the original body — the decorated
66
+ * method exists only to declare the request's argument and return types:
67
+ *
68
+ * ```ts
69
+ * @Api("/user")
70
+ * class UserApi {
71
+ * @Get("/:id")
72
+ * getUser(@Params("id") id: string): Promise<User> { return null!; }
73
+ * }
74
+ *
75
+ * const userApi = Service.createApi(UserApi);
76
+ * const method = userApi.getUser("1"); // no request yet
77
+ * const { data } = await method.send(); // `data` is `User`
78
+ * ```
79
+ *
80
+ * Methods without a request-method decorator are passed through untouched, so an
81
+ * api class may keep helpers next to its endpoints.
82
+ */
83
+ export type SnailApiProxy<TClass, S = SnailEnvelopeSchema, D extends string = "data", C extends string = "code", M extends string = "message"> = {
84
+ [K in keyof TClass]: TClass[K] extends (...args: infer A) => infer R ? <TData = SnailPayloadOf<R>>(...args: A) => import("../core/method.js").SnailMethod<S, TData, D, C, M> : TClass[K];
85
+ };
86
+ /** Static description of one decorated method, extracted from its decorators. */
87
+ export interface SnailMethodMeta {
88
+ /** Server name. */
89
+ serverName: string;
90
+ /** Api class name. */
91
+ apiName: string;
92
+ /** Method name on the api class. */
93
+ methodName: string;
94
+ /** Fully qualified name: `server.api.method`. */
95
+ fullName: string;
96
+ /** Request verb. */
97
+ method: SnailMethodType;
98
+ /** Api prefix joined with the method path. */
99
+ url: string;
100
+ }
101
+ /**
102
+ * Per-call overrides.
103
+ *
104
+ * Mostly a convenience: prefer passing arguments to the proxied method. Kept for
105
+ * the cases where a strategy needs to inject a value the signature does not
106
+ * carry.
107
+ */
108
+ export interface SnailSendOptions<TData = unknown> {
109
+ /** Override the body for this single call. */
110
+ data?: TData;
111
+ /** Override query params for this single call. */
112
+ query?: Record<string, any>;
113
+ /** Override path params for this single call. */
114
+ pathParams?: Record<string, any>;
115
+ /** Override headers for this single call. */
116
+ headers?: Record<string, any>;
117
+ /** Abort signal for this single call. */
118
+ signal?: AbortSignal;
119
+ }
120
+ /** Re-exported for convenience. */
121
+ export type { SnailResult };
122
+ //# sourceMappingURL=api.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/typings/api.ts"],"names":[],"mappings":"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"}