@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.
- package/LICENSE +21 -0
- package/README.md +125 -605
- package/dist/adapter/plain.d.ts +12 -0
- package/dist/adapter/plain.d.ts.map +1 -0
- package/dist/adapter/react.d.ts +14 -0
- package/dist/adapter/react.d.ts.map +1 -0
- package/dist/adapter/registry.d.ts +16 -0
- package/dist/adapter/registry.d.ts.map +1 -0
- package/dist/adapter/vue.d.ts +15 -0
- package/dist/adapter/vue.d.ts.map +1 -0
- package/dist/chunks/custom-UchOSg_s.js +466 -0
- package/dist/chunks/custom-UchOSg_s.js.map +1 -0
- package/dist/chunks/download-lBC7oOTT.js +160 -0
- package/dist/chunks/download-lBC7oOTT.js.map +1 -0
- package/dist/chunks/object-Ct0w-h4i.js +74 -0
- package/dist/chunks/object-Ct0w-h4i.js.map +1 -0
- package/dist/chunks/plugin-D3RDffDG.js +742 -0
- package/dist/chunks/plugin-D3RDffDG.js.map +1 -0
- package/dist/chunks/react-C1em-gxy.js +62 -0
- package/dist/chunks/react-C1em-gxy.js.map +1 -0
- package/dist/chunks/request-qeRhihEF.js +40 -0
- package/dist/chunks/request-qeRhihEF.js.map +1 -0
- package/dist/chunks/response-D-ubkAFl.js +127 -0
- package/dist/chunks/response-D-ubkAFl.js.map +1 -0
- package/dist/chunks/use-download-FAjtdJ58.js +1816 -0
- package/dist/chunks/use-download-FAjtdJ58.js.map +1 -0
- package/dist/chunks/vue-Dd9EhkQ3.js +34 -0
- package/dist/chunks/vue-Dd9EhkQ3.js.map +1 -0
- package/dist/core/args.d.ts +54 -0
- package/dist/core/args.d.ts.map +1 -0
- package/dist/core/connection.d.ts +15 -0
- package/dist/core/connection.d.ts.map +1 -0
- package/dist/core/context.d.ts +124 -0
- package/dist/core/context.d.ts.map +1 -0
- package/dist/core/http-stream.d.ts +28 -0
- package/dist/core/http-stream.d.ts.map +1 -0
- package/dist/core/logger.d.ts +22 -0
- package/dist/core/logger.d.ts.map +1 -0
- package/dist/core/metadata.d.ts +92 -0
- package/dist/core/metadata.d.ts.map +1 -0
- package/dist/core/metadata.keys.d.ts +41 -0
- package/dist/core/metadata.keys.d.ts.map +1 -0
- package/dist/core/method.d.ts +177 -0
- package/dist/core/method.d.ts.map +1 -0
- package/dist/core/plugin-manager.d.ts +106 -0
- package/dist/core/plugin-manager.d.ts.map +1 -0
- package/dist/core/plugin.d.ts +113 -0
- package/dist/core/plugin.d.ts.map +1 -0
- package/dist/core/reconnect.d.ts +22 -0
- package/dist/core/reconnect.d.ts.map +1 -0
- package/dist/core/resolve-stream.d.ts +45 -0
- package/dist/core/resolve-stream.d.ts.map +1 -0
- package/dist/core/resolve.d.ts +50 -0
- package/dist/core/resolve.d.ts.map +1 -0
- package/dist/core/response.d.ts +60 -0
- package/dist/core/response.d.ts.map +1 -0
- package/dist/core/server.d.ts +132 -0
- package/dist/core/server.d.ts.map +1 -0
- package/dist/core/sse.d.ts +27 -0
- package/dist/core/sse.d.ts.map +1 -0
- package/dist/core/state-bag.d.ts +36 -0
- package/dist/core/state-bag.d.ts.map +1 -0
- package/dist/core/websocket.d.ts +29 -0
- package/dist/core/websocket.d.ts.map +1 -0
- package/dist/decorators/api.d.ts +24 -4
- package/dist/decorators/api.d.ts.map +1 -0
- package/dist/decorators/args.d.ts +77 -14
- package/dist/decorators/args.d.ts.map +1 -0
- package/dist/decorators/custom.d.ts +81 -0
- package/dist/decorators/custom.d.ts.map +1 -0
- package/dist/decorators/header.d.ts +22 -0
- package/dist/decorators/header.d.ts.map +1 -0
- package/dist/decorators/index.d.ts +17 -0
- package/dist/decorators/index.d.ts.map +1 -0
- package/dist/decorators/methods.d.ts +33 -0
- package/dist/decorators/methods.d.ts.map +1 -0
- package/dist/decorators/progress.d.ts +24 -6
- package/dist/decorators/progress.d.ts.map +1 -0
- package/dist/decorators/server.d.ts +28 -7
- package/dist/decorators/server.d.ts.map +1 -0
- package/dist/decorators/stream.d.ts +113 -0
- package/dist/decorators/stream.d.ts.map +1 -0
- package/dist/default/options.d.ts +19 -0
- package/dist/default/options.d.ts.map +1 -0
- package/dist/error/base.d.ts +20 -0
- package/dist/error/base.d.ts.map +1 -0
- package/dist/error/decorator.d.ts +14 -0
- package/dist/error/decorator.d.ts.map +1 -0
- package/dist/error/hook.d.ts +14 -0
- package/dist/error/hook.d.ts.map +1 -0
- package/dist/error/index.d.ts +8 -0
- package/dist/error/index.d.ts.map +1 -0
- package/dist/error/options.d.ts +12 -0
- package/dist/error/options.d.ts.map +1 -0
- package/dist/error/plugin.d.ts +11 -0
- package/dist/error/plugin.d.ts.map +1 -0
- package/dist/error/request.d.ts +29 -0
- package/dist/error/request.d.ts.map +1 -0
- package/dist/error/response.d.ts +36 -0
- package/dist/error/response.d.ts.map +1 -0
- package/dist/index.d.ts +56 -11
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2034 -0
- package/dist/index.js.map +1 -0
- package/dist/locale/en.d.ts +5 -0
- package/dist/locale/en.d.ts.map +1 -0
- package/dist/locale/index.d.ts +57 -0
- package/dist/locale/index.d.ts.map +1 -0
- package/dist/locale/types.d.ts +11 -0
- package/dist/locale/types.d.ts.map +1 -0
- package/dist/locale/zh.d.ts +5 -0
- package/dist/locale/zh.d.ts.map +1 -0
- package/dist/plugins/cache/adapters/indexeddb.d.ts +52 -0
- package/dist/plugins/cache/adapters/indexeddb.d.ts.map +1 -0
- package/dist/plugins/cache/adapters/memory.d.ts +60 -0
- package/dist/plugins/cache/adapters/memory.d.ts.map +1 -0
- package/dist/plugins/cache/adapters/web-storage.d.ts +43 -0
- package/dist/plugins/cache/adapters/web-storage.d.ts.map +1 -0
- package/dist/plugins/cache/decorators.d.ts +62 -0
- package/dist/plugins/cache/decorators.d.ts.map +1 -0
- package/dist/plugins/cache/index.d.ts +41 -0
- package/dist/plugins/cache/index.d.ts.map +1 -0
- package/dist/plugins/cache/key.d.ts +48 -0
- package/dist/plugins/cache/key.d.ts.map +1 -0
- package/dist/plugins/cache/manager.d.ts +124 -0
- package/dist/plugins/cache/manager.d.ts.map +1 -0
- package/dist/plugins/cache/plugin.d.ts +64 -0
- package/dist/plugins/cache/plugin.d.ts.map +1 -0
- package/dist/plugins/cache/type.d.ts +116 -0
- package/dist/plugins/cache/type.d.ts.map +1 -0
- package/dist/plugins/index.d.ts +75 -0
- package/dist/plugins/index.d.ts.map +1 -0
- package/dist/plugins/index.js +2265 -0
- package/dist/plugins/index.js.map +1 -0
- package/dist/plugins/interceptor/decorators.d.ts +44 -0
- package/dist/plugins/interceptor/decorators.d.ts.map +1 -0
- package/dist/plugins/interceptor/index.d.ts +31 -0
- package/dist/plugins/interceptor/index.d.ts.map +1 -0
- package/dist/plugins/interceptor/manager.d.ts +27 -0
- package/dist/plugins/interceptor/manager.d.ts.map +1 -0
- package/dist/plugins/interceptor/plugin.d.ts +65 -0
- package/dist/plugins/interceptor/plugin.d.ts.map +1 -0
- package/dist/plugins/interceptor/type.d.ts +45 -0
- package/dist/plugins/interceptor/type.d.ts.map +1 -0
- package/dist/plugins/pool/index.d.ts +26 -0
- package/dist/plugins/pool/index.d.ts.map +1 -0
- package/dist/plugins/pool/plugin.d.ts +83 -0
- package/dist/plugins/pool/plugin.d.ts.map +1 -0
- package/dist/plugins/pool/scheduler.d.ts +153 -0
- package/dist/plugins/pool/scheduler.d.ts.map +1 -0
- package/dist/plugins/pool/type.d.ts +38 -0
- package/dist/plugins/pool/type.d.ts.map +1 -0
- package/dist/plugins/react/index.d.ts +20 -0
- package/dist/plugins/react/index.d.ts.map +1 -0
- package/dist/plugins/react/index.js +145 -0
- package/dist/plugins/react/index.js.map +1 -0
- package/dist/plugins/react/plugin.d.ts +33 -0
- package/dist/plugins/react/plugin.d.ts.map +1 -0
- package/dist/plugins/react/type.d.ts +32 -0
- package/dist/plugins/react/type.d.ts.map +1 -0
- package/dist/plugins/transform/decorators.d.ts +54 -0
- package/dist/plugins/transform/decorators.d.ts.map +1 -0
- package/dist/plugins/transform/hydrate.d.ts +47 -0
- package/dist/plugins/transform/hydrate.d.ts.map +1 -0
- package/dist/plugins/transform/index.d.ts +41 -0
- package/dist/plugins/transform/index.d.ts.map +1 -0
- package/dist/plugins/transform/plugin.d.ts +20 -0
- package/dist/plugins/transform/plugin.d.ts.map +1 -0
- package/dist/plugins/transform/type.d.ts +67 -0
- package/dist/plugins/transform/type.d.ts.map +1 -0
- package/dist/plugins/validate/decorators.d.ts +24 -0
- package/dist/plugins/validate/decorators.d.ts.map +1 -0
- package/dist/plugins/validate/index.d.ts +40 -0
- package/dist/plugins/validate/index.d.ts.map +1 -0
- package/dist/plugins/validate/plugin.d.ts +13 -0
- package/dist/plugins/validate/plugin.d.ts.map +1 -0
- package/dist/plugins/validate/type.d.ts +54 -0
- package/dist/plugins/validate/type.d.ts.map +1 -0
- package/dist/plugins/version/decorators.d.ts +22 -0
- package/dist/plugins/version/decorators.d.ts.map +1 -0
- package/dist/plugins/version/index.d.ts +23 -0
- package/dist/plugins/version/index.d.ts.map +1 -0
- package/dist/plugins/version/plugin.d.ts +13 -0
- package/dist/plugins/version/plugin.d.ts.map +1 -0
- package/dist/plugins/version/type.d.ts +47 -0
- package/dist/plugins/version/type.d.ts.map +1 -0
- package/dist/plugins/vue/index.d.ts +21 -0
- package/dist/plugins/vue/index.d.ts.map +1 -0
- package/dist/plugins/vue/index.js +114 -0
- package/dist/plugins/vue/index.js.map +1 -0
- package/dist/plugins/vue/plugin.d.ts +15 -0
- package/dist/plugins/vue/plugin.d.ts.map +1 -0
- package/dist/plugins/vue/type.d.ts +34 -0
- package/dist/plugins/vue/type.d.ts.map +1 -0
- package/dist/strategies/index.d.ts +2 -0
- package/dist/strategies/index.d.ts.map +1 -0
- package/dist/strategies/index.js +29 -0
- package/dist/strategies/index.js.map +1 -0
- package/dist/strategies/plain.d.ts +2 -0
- package/dist/strategies/plain.d.ts.map +1 -0
- package/dist/strategies/plain.js +23 -0
- package/dist/strategies/plain.js.map +1 -0
- package/dist/strategies/react.d.ts +2 -0
- package/dist/strategies/react.d.ts.map +1 -0
- package/dist/strategies/react.js +30 -0
- package/dist/strategies/react.js.map +1 -0
- package/dist/strategies/shared/adapter.d.ts +23 -0
- package/dist/strategies/shared/adapter.d.ts.map +1 -0
- package/dist/strategies/shared/dom.d.ts +53 -0
- package/dist/strategies/shared/dom.d.ts.map +1 -0
- package/dist/strategies/shared/error.d.ts +30 -0
- package/dist/strategies/shared/error.d.ts.map +1 -0
- package/dist/strategies/shared/method.d.ts +51 -0
- package/dist/strategies/shared/method.d.ts.map +1 -0
- package/dist/strategies/shared/public.d.ts +33 -0
- package/dist/strategies/shared/public.d.ts.map +1 -0
- package/dist/strategies/shared/queue.d.ts +30 -0
- package/dist/strategies/shared/queue.d.ts.map +1 -0
- package/dist/strategies/shared/retry.d.ts +51 -0
- package/dist/strategies/shared/retry.d.ts.map +1 -0
- package/dist/strategies/shared/state.d.ts +117 -0
- package/dist/strategies/shared/state.d.ts.map +1 -0
- package/dist/strategies/shared/timing.d.ts +53 -0
- package/dist/strategies/shared/timing.d.ts.map +1 -0
- package/dist/strategies/shared/watcher.d.ts +37 -0
- package/dist/strategies/shared/watcher.d.ts.map +1 -0
- package/dist/strategies/use-auto-request.d.ts +77 -0
- package/dist/strategies/use-auto-request.d.ts.map +1 -0
- package/dist/strategies/use-download.d.ts +103 -0
- package/dist/strategies/use-download.d.ts.map +1 -0
- package/dist/strategies/use-fetcher.d.ts +52 -0
- package/dist/strategies/use-fetcher.d.ts.map +1 -0
- package/dist/strategies/use-pagination.d.ts +101 -0
- package/dist/strategies/use-pagination.d.ts.map +1 -0
- package/dist/strategies/use-request.d.ts +50 -0
- package/dist/strategies/use-request.d.ts.map +1 -0
- package/dist/strategies/use-retriable-request.d.ts +46 -0
- package/dist/strategies/use-retriable-request.d.ts.map +1 -0
- package/dist/strategies/use-sse.d.ts +103 -0
- package/dist/strategies/use-sse.d.ts.map +1 -0
- package/dist/strategies/use-token-auth.d.ts +88 -0
- package/dist/strategies/use-token-auth.d.ts.map +1 -0
- package/dist/strategies/use-uploader.d.ts +88 -0
- package/dist/strategies/use-uploader.d.ts.map +1 -0
- package/dist/strategies/use-watcher.d.ts +69 -0
- package/dist/strategies/use-watcher.d.ts.map +1 -0
- package/dist/typings/adapter.d.ts +61 -0
- package/dist/typings/adapter.d.ts.map +1 -0
- package/dist/typings/api.d.ts +122 -0
- package/dist/typings/api.d.ts.map +1 -0
- package/dist/typings/args.d.ts +51 -0
- package/dist/typings/args.d.ts.map +1 -0
- package/dist/typings/index.d.ts +8 -12
- package/dist/typings/index.d.ts.map +1 -0
- package/dist/typings/meta.d.ts +44 -0
- package/dist/typings/meta.d.ts.map +1 -0
- package/dist/typings/plugin.d.ts +114 -0
- package/dist/typings/plugin.d.ts.map +1 -0
- package/dist/typings/response.d.ts +118 -0
- package/dist/typings/response.d.ts.map +1 -0
- package/dist/typings/server.d.ts +83 -0
- package/dist/typings/server.d.ts.map +1 -0
- package/dist/typings/stream.d.ts +150 -0
- package/dist/typings/stream.d.ts.map +1 -0
- package/dist/utils/download.d.ts +97 -0
- package/dist/utils/download.d.ts.map +1 -0
- package/dist/utils/emitter.d.ts +36 -0
- package/dist/utils/emitter.d.ts.map +1 -0
- package/dist/utils/index.d.ts +6 -1
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/is.d.ts +22 -0
- package/dist/utils/is.d.ts.map +1 -0
- package/dist/utils/object.d.ts +44 -0
- package/dist/utils/object.d.ts.map +1 -0
- package/dist/utils/url.d.ts +44 -0
- package/dist/utils/url.d.ts.map +1 -0
- package/package.json +90 -27
- package/README_EN.md +0 -607
- package/dist/cache/index.d.ts +0 -9
- package/dist/cache/indexDBCache.d.ts +0 -13
- package/dist/cache/localstorageCache.d.ts +0 -10
- package/dist/cache/memoryCache.d.ts +0 -11
- package/dist/core/index.d.ts +0 -4
- package/dist/core/snailApi.d.ts +0 -22
- package/dist/core/snailMethod.d.ts +0 -49
- package/dist/core/snailServer.d.ts +0 -35
- package/dist/core/snailSse.d.ts +0 -20
- package/dist/decorators/cache.d.ts +0 -15
- package/dist/decorators/method.d.ts +0 -38
- package/dist/decorators/sse.d.ts +0 -30
- package/dist/decorators/strategy.d.ts +0 -10
- package/dist/decorators/versioning.d.ts +0 -16
- package/dist/eventEmmit/index.d.ts +0 -1
- package/dist/eventEmmit/snail.event.d.ts +0 -10
- package/dist/snail-api.js +0 -4868
- package/dist/snail-api.umd.cjs +0 -4872
- package/dist/strategies/jwtStrategy.d.ts +0 -4
- package/dist/typings/api.option.d.ts +0 -10
- package/dist/typings/apiProxy.d.ts +0 -8
- package/dist/typings/cache.management.option.d.ts +0 -25
- package/dist/typings/cache.type.d.ts +0 -23
- package/dist/typings/content.type.d.ts +0 -5
- package/dist/typings/request.body.d.ts +0 -3
- package/dist/typings/response.data.d.ts +0 -13
- package/dist/typings/snail.event.d.ts +0 -6
- package/dist/typings/snail.method.d.ts +0 -14
- package/dist/typings/snail.option.d.ts +0 -16
- package/dist/typings/sse.d.ts +0 -15
- package/dist/typings/strategy.d.ts +0 -5
- package/dist/typings/versioning.option.d.ts +0 -30
- package/dist/utils/function.d.ts +0 -31
- package/dist/versioning/index.d.ts +0 -1
- package/dist/versioning/versioning.d.ts +0 -12
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { AxiosResponse, InternalAxiosRequestConfig } from "axios";
|
|
2
|
+
import type { SnailContext } from "../../core/context.js";
|
|
3
|
+
/**
|
|
4
|
+
* Shared shapes of the interceptor plugin.
|
|
5
|
+
*
|
|
6
|
+
* ## Why these are not axios' own interceptors
|
|
7
|
+
*
|
|
8
|
+
* axios keeps its interceptor lists on the *instance* (`axios.interceptors`), so
|
|
9
|
+
* `use()` there can only express "every request of this instance". A decorator
|
|
10
|
+
* such as `@BeforeRequest()` needs the opposite granularity — this one method,
|
|
11
|
+
* plus this one api class — and it has to be ordered against the other plugins
|
|
12
|
+
* (see `docs/guide/plugin-lifecycle.md` §2.1). Both are impossible at instance level, which
|
|
13
|
+
* is why interceptors run on the plugin lifecycle instead.
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* One request or response interceptor.
|
|
17
|
+
*
|
|
18
|
+
* `T` is `InternalAxiosRequestConfig` while the request is being prepared and
|
|
19
|
+
* `AxiosResponse` once a response exists. Both callbacks receive the live
|
|
20
|
+
* {@link SnailContext} as a second argument, so an interceptor may read the
|
|
21
|
+
* method identity (`ctx.fullName`) without the plugin having to inject it.
|
|
22
|
+
*
|
|
23
|
+
* Returning `undefined` from `onFulfilled` keeps the value it received: most
|
|
24
|
+
* interceptors mutate in place, and forcing every one of them to return the
|
|
25
|
+
* config would make the common case noisy.
|
|
26
|
+
*/
|
|
27
|
+
export interface InterceptorEntry<T = unknown> {
|
|
28
|
+
/** Runs before the request is handed to axios, or against the response. */
|
|
29
|
+
onFulfilled?: (value: T, ctx: SnailContext) => T | void | Promise<T | void>;
|
|
30
|
+
/**
|
|
31
|
+
* Runs when `onFulfilled` — or an earlier interceptor in the same phase —
|
|
32
|
+
* throws.
|
|
33
|
+
*
|
|
34
|
+
* The plugin calls it with the thrown value while the request is still
|
|
35
|
+
* recoverable, unlike `onError` which only observes a settled failure. Any
|
|
36
|
+
* value other than `undefined` is adopted as the current config/response;
|
|
37
|
+
* returning `undefined` or throwing re-propagates the failure.
|
|
38
|
+
*/
|
|
39
|
+
onRejected?: (error: unknown, ctx: SnailContext) => unknown;
|
|
40
|
+
}
|
|
41
|
+
/** An entry that runs while the outgoing axios config is still mutable. */
|
|
42
|
+
export type RequestInterceptorEntry = InterceptorEntry<InternalAxiosRequestConfig>;
|
|
43
|
+
/** An entry that runs against a response that already exists. */
|
|
44
|
+
export type ResponseInterceptorEntry = InterceptorEntry<AxiosResponse>;
|
|
45
|
+
//# sourceMappingURL=type.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"type.d.ts","sourceRoot":"","sources":["../../../src/plugins/interceptor/type.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,0BAA0B,EAAE,MAAM,OAAO,CAAC;AACvE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEvD;;;;;;;;;;;GAWG;AAEH;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,gBAAgB,CAAC,CAAC,GAAG,OAAO;IAC3C,2EAA2E;IAC3E,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,GAAG,EAAE,YAAY,KAAK,CAAC,GAAG,IAAI,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IAE5E;;;;;;;;OAQG;IACH,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,YAAY,KAAK,OAAO,CAAC;CAC7D;AAED,2EAA2E;AAC3E,MAAM,MAAM,uBAAuB,GAAG,gBAAgB,CAAC,0BAA0B,CAAC,CAAC;AAEnF,iEAAiE;AACjE,MAAM,MAAM,wBAAwB,GAAG,gBAAgB,CAAC,aAAa,CAAC,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Request pool plugin.
|
|
3
|
+
*
|
|
4
|
+
* ```ts
|
|
5
|
+
* import { RequestPool } from "@snail-js/api/plugins";
|
|
6
|
+
*
|
|
7
|
+
* Service.use(RequestPool({ concurrency: 4, maxQueue: 50, queueTimeout: 10_000 }));
|
|
8
|
+
* ```
|
|
9
|
+
*
|
|
10
|
+
* Bounds how many requests are in flight at once, with a priority-ordered, bounded,
|
|
11
|
+
* time-limited queue. It exists because the browser's own request queue is FIFO,
|
|
12
|
+
* invisible and unprioritised — see `RequestPoolOptions` for the full reasoning,
|
|
13
|
+
* including why HTTP/2 does not remove the need for it.
|
|
14
|
+
*
|
|
15
|
+
* `priority: -150` puts it below the cache (`-100`), so it is the last thing to run
|
|
16
|
+
* before the transport and a cache hit never consumes a slot.
|
|
17
|
+
*
|
|
18
|
+
* @packageDocumentation
|
|
19
|
+
*/
|
|
20
|
+
export { RequestPool, POOL_PLUGIN_NAME, POOL_PRIORITY, clearPool, isPoolError, poolStats } from "./plugin.js";
|
|
21
|
+
export type { RequestPoolPlugin } from "./plugin.js";
|
|
22
|
+
export { RequestPoolScheduler } from "./scheduler.js";
|
|
23
|
+
export type { AbortLike, PoolTicket, RequestPoolOptions, RequestPoolStats } from "./scheduler.js";
|
|
24
|
+
export { POOL_ERROR_CODES, SnailPoolError } from "./type.js";
|
|
25
|
+
export type { PoolErrorCode } from "./type.js";
|
|
26
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/plugins/pool/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EACL,WAAW,EACX,gBAAgB,EAChB,aAAa,EACb,SAAS,EACT,WAAW,EACX,SAAS,EACV,MAAM,UAAU,CAAC;AAClB,YAAY,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAElD,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACnD,YAAY,EACV,SAAS,EACT,UAAU,EACV,kBAAkB,EAClB,gBAAgB,EACjB,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AAC1D,YAAY,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { SnailPoolError } from "./type.js";
|
|
2
|
+
import { RequestPoolScheduler, type RequestPoolOptions, type RequestPoolStats } from "./scheduler.js";
|
|
3
|
+
/** Plugin name; also the identity used by `Service.use()` / `Service.remove()`. */
|
|
4
|
+
export declare const POOL_PLUGIN_NAME = "pool";
|
|
5
|
+
/**
|
|
6
|
+
* Priority of the pool band.
|
|
7
|
+
*
|
|
8
|
+
* **Below** the cache (`-100`), which is the whole point: forward hooks run
|
|
9
|
+
* highest-priority first, so the pool is the very last thing to run before the
|
|
10
|
+
* transport. A request that the cache can answer never reaches the pool and never
|
|
11
|
+
* consumes a slot. Putting the pool above the cache would let a handful of cached
|
|
12
|
+
* reads occupy the entire pool and starve the real requests behind them.
|
|
13
|
+
*/
|
|
14
|
+
export declare const POOL_PRIORITY = -150;
|
|
15
|
+
/** The pool plugin object plus live access to its scheduler. */
|
|
16
|
+
export interface RequestPoolPlugin {
|
|
17
|
+
readonly name: string;
|
|
18
|
+
readonly priority: number;
|
|
19
|
+
/**
|
|
20
|
+
* The scheduler, available once `install` has run.
|
|
21
|
+
*
|
|
22
|
+
* Exposed so an application can read {@link RequestPoolScheduler.stats} for a
|
|
23
|
+
* loading indicator, or widen `concurrency` when it learns the backend is
|
|
24
|
+
* coping.
|
|
25
|
+
*/
|
|
26
|
+
readonly scheduler: RequestPoolScheduler | undefined;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Bound how many requests are in flight at once.
|
|
30
|
+
*
|
|
31
|
+
* ```ts
|
|
32
|
+
* Service.use(RequestPool({ concurrency: 4, maxQueue: 50, queueTimeout: 10_000 }));
|
|
33
|
+
*
|
|
34
|
+
* // `ctx.state` is readable from the priority callback, so an interactive request
|
|
35
|
+
* // can jump ahead of a background prefetch.
|
|
36
|
+
* Service.use(RequestPool({ concurrency: 4, priority: (ctx) => (ctx.state.get("prefetch") ? 100 : 0) }));
|
|
37
|
+
* ```
|
|
38
|
+
*
|
|
39
|
+
* ## What problem this solves
|
|
40
|
+
*
|
|
41
|
+
* See {@link RequestPoolOptions} for why the browser's own queue is not enough.
|
|
42
|
+
* In short: the built-in one is FIFO, invisible and unprioritised, so a burst from
|
|
43
|
+
* one screen can starve the request the user is actually waiting for, and nothing
|
|
44
|
+
* fails fast when the backlog grows without bound.
|
|
45
|
+
*
|
|
46
|
+
* ## Where it sits
|
|
47
|
+
*
|
|
48
|
+
* Last in forward order, immediately before the network call, and it holds its slot
|
|
49
|
+
* only for the transport — `next()` resolves once the response has been received,
|
|
50
|
+
* so validation, transformation and the caller's reactive updates happen after the
|
|
51
|
+
* slot is already back in the pool.
|
|
52
|
+
*
|
|
53
|
+
* A cache hit short-circuits `beforeRequest` upstream of this plugin, so cached
|
|
54
|
+
* reads cost no concurrency.
|
|
55
|
+
*
|
|
56
|
+
* ## One path it does not cover
|
|
57
|
+
*
|
|
58
|
+
* `useTokenAuth` replays a request that came back `401` by re-running the transport
|
|
59
|
+
* directly, deliberately without re-entering `beforeRequest` (re-entering would
|
|
60
|
+
* loop). Those replays therefore **do not pass through this pool** and are not
|
|
61
|
+
* counted against `concurrency`.
|
|
62
|
+
*
|
|
63
|
+
* The practical impact is small — replays are bounded by the number of requests
|
|
64
|
+
* that were in flight when the token expired, which were already uncapped — but it
|
|
65
|
+
* is worth knowing before reaching for the pool to protect a backend from an auth
|
|
66
|
+
* storm. If that matters, cap it at the source: use `useRequest`'s own concurrency
|
|
67
|
+
* control, or let `useTokenAuth` refresh proactively rather than on the first 401.
|
|
68
|
+
*/
|
|
69
|
+
export declare function RequestPool(options?: RequestPoolOptions): RequestPoolPlugin;
|
|
70
|
+
/** Read the live counters of an installed pool plugin, or `undefined`. */
|
|
71
|
+
export declare function poolStats(plugin: RequestPoolPlugin): RequestPoolStats | undefined;
|
|
72
|
+
/** Drop every queued request of an installed pool plugin. */
|
|
73
|
+
export declare function clearPool(plugin: RequestPoolPlugin, reason?: unknown): void;
|
|
74
|
+
/**
|
|
75
|
+
* `true` when `error` came from the pool rather than from the transport.
|
|
76
|
+
*
|
|
77
|
+
* Lets a caller tell "refused before it was ever sent" — safe to retry later —
|
|
78
|
+
* apart from a real network failure. Covers all four refusal paths: a full queue,
|
|
79
|
+
* a queue wait that timed out, a request abandoned while waiting, and a queue
|
|
80
|
+
* cleared by an uninstall.
|
|
81
|
+
*/
|
|
82
|
+
export declare function isPoolError(error: unknown): error is SnailPoolError;
|
|
83
|
+
//# sourceMappingURL=plugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../../src/plugins/pool/plugin.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,EACL,oBAAoB,EACpB,KAAK,kBAAkB,EACvB,KAAK,gBAAgB,EACtB,MAAM,aAAa,CAAC;AAErB,mFAAmF;AACnF,eAAO,MAAM,gBAAgB,SAAS,CAAC;AAEvC;;;;;;;;GAQG;AACH,eAAO,MAAM,aAAa,OAAO,CAAC;AAElC,gEAAgE;AAChE,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAE1B;;;;;;OAMG;IACH,QAAQ,CAAC,SAAS,EAAE,oBAAoB,GAAG,SAAS,CAAC;CACtD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,wBAAgB,WAAW,CAAC,OAAO,CAAC,EAAE,kBAAkB,GAAG,iBAAiB,CAgD3E;AAED,0EAA0E;AAC1E,wBAAgB,SAAS,CAAC,MAAM,EAAE,iBAAiB,GAAG,gBAAgB,GAAG,SAAS,CAEjF;AAED,6DAA6D;AAC7D,wBAAgB,SAAS,CAAC,MAAM,EAAE,iBAAiB,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,IAAI,CAE3E;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,cAAc,CAEnE"}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The slice of `AbortSignal` the scheduler uses.
|
|
3
|
+
*
|
|
4
|
+
* Structurally typed rather than declared as `AbortSignal` because axios hands the
|
|
5
|
+
* plugin its own `GenericAbortSignal`, which does not satisfy the DOM interface —
|
|
6
|
+
* it lacks `reason`/`throwIfAborted` and even types the listener methods as
|
|
7
|
+
* optional. In practice the object is a real `AbortSignal`, so the listener path is
|
|
8
|
+
* always available; the optional methods are handled anyway, and a signal that
|
|
9
|
+
* cannot be listened to simply means an already-queued request is not withdrawn
|
|
10
|
+
* early — it still gets its slot and is rejected by the transport.
|
|
11
|
+
*/
|
|
12
|
+
export interface AbortLike {
|
|
13
|
+
readonly aborted: boolean;
|
|
14
|
+
addEventListener?(type: "abort", listener: () => void, options?: {
|
|
15
|
+
once?: boolean;
|
|
16
|
+
}): void;
|
|
17
|
+
removeEventListener?(type: "abort", listener: () => void): void;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Options accepted by the request-pool plugin.
|
|
21
|
+
*
|
|
22
|
+
* ## Why a pool exists at all
|
|
23
|
+
*
|
|
24
|
+
* A browser already queues requests: HTTP/1.1 allows about six connections per
|
|
25
|
+
* origin, and everything beyond that waits inside the network stack. That built-in
|
|
26
|
+
* queue is FIFO, invisible, and unprioritised — an application that fires fifty
|
|
27
|
+
* requests from one screen cannot say that the one the user is looking at should
|
|
28
|
+
* go first, cannot observe how many are waiting, and cannot avoid a burst that
|
|
29
|
+
* starves the rest of the page.
|
|
30
|
+
*
|
|
31
|
+
* HTTP/2 does not remove the need either: multiplexing replaces the six-connection
|
|
32
|
+
* cap with a *stream* limit (commonly 100), and the server still has a finite
|
|
33
|
+
* capacity. A burst of five hundred requests over HTTP/2 will happily saturate it.
|
|
34
|
+
*
|
|
35
|
+
* A pool gives three things the browser cannot: a hard ceiling that holds on both
|
|
36
|
+
* protocols, a priority order, and a bounded queue that fails fast instead of
|
|
37
|
+
* silently piling up.
|
|
38
|
+
*/
|
|
39
|
+
export interface RequestPoolOptions {
|
|
40
|
+
/**
|
|
41
|
+
* Maximum requests in flight at once. Defaults to `6`.
|
|
42
|
+
*
|
|
43
|
+
* Six is the classic HTTP/1.1 per-origin connection cap, so it is the largest
|
|
44
|
+
* value that cannot make things worse on an old server while still bounding an
|
|
45
|
+
* HTTP/2 burst. Raising it helps only when the backend is known to cope.
|
|
46
|
+
*/
|
|
47
|
+
concurrency?: number;
|
|
48
|
+
/**
|
|
49
|
+
* Maximum requests allowed to *wait*. Defaults to `Infinity`.
|
|
50
|
+
*
|
|
51
|
+
* Set a finite value to fail fast: once the queue is full, an extra request
|
|
52
|
+
* rejects immediately rather than joining an unbounded backlog the user will
|
|
53
|
+
* never see resolved.
|
|
54
|
+
*/
|
|
55
|
+
maxQueue?: number;
|
|
56
|
+
/**
|
|
57
|
+
* How long a queued request may wait, in milliseconds. Defaults to `0`, meaning
|
|
58
|
+
* no limit.
|
|
59
|
+
*
|
|
60
|
+
* A request the user has stopped waiting for is worse than a failed one — it
|
|
61
|
+
* still consumes a slot eventually and may overwrite fresher data.
|
|
62
|
+
*/
|
|
63
|
+
queueTimeout?: number;
|
|
64
|
+
/**
|
|
65
|
+
* Ordering weight for a queued request. **Lower runs first**; ties keep arrival
|
|
66
|
+
* order, and a request that finds a free slot never queues at all.
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* ```ts
|
|
70
|
+
* // Requests the user is looking at jump ahead of background prefetches.
|
|
71
|
+
* priority: (ctx) => (ctx.state.get("prefetch") ? 100 : 0)
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
priority?: (ctx: unknown) => number;
|
|
75
|
+
}
|
|
76
|
+
/** Live counters for a {@link RequestPoolScheduler}. */
|
|
77
|
+
export interface RequestPoolStats {
|
|
78
|
+
/** Requests currently holding a slot. */
|
|
79
|
+
active: number;
|
|
80
|
+
/** Requests waiting for a slot. */
|
|
81
|
+
queued: number;
|
|
82
|
+
/** Configured ceiling. */
|
|
83
|
+
concurrency: number;
|
|
84
|
+
}
|
|
85
|
+
/** A resolve/reject pair plus the bookkeeping the queue needs to drop it. */
|
|
86
|
+
export interface PoolTicket {
|
|
87
|
+
/** Give the slot back. Idempotent. */
|
|
88
|
+
readonly release: () => void;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* The scheduling core of the request pool.
|
|
92
|
+
*
|
|
93
|
+
* Deterministic and free of any request or plugin concept: it hands out `release`
|
|
94
|
+
* callbacks and knows nothing about what they guard. That makes the hard part —
|
|
95
|
+
* ordering, fairness and the guarantee that a slot is always returned — testable
|
|
96
|
+
* on its own, without a server or a network.
|
|
97
|
+
*
|
|
98
|
+
* ## Messages come from the plugin
|
|
99
|
+
*
|
|
100
|
+
* The refusal messages are contributed by `RequestPool`'s `setup`, following the
|
|
101
|
+
* plugin-owned-strings convention. Using this class **standalone** therefore yields
|
|
102
|
+
* the raw message keys rather than translated text — the `code` on each
|
|
103
|
+
* {@link SnailPoolError} is always correct, and that is what callers should branch
|
|
104
|
+
* on. Registering a catalogue yourself is enough if you want the text too.
|
|
105
|
+
*
|
|
106
|
+
* ## The invariant that matters
|
|
107
|
+
*
|
|
108
|
+
* `active` must never drift from the number of outstanding tickets. Every failure
|
|
109
|
+
* path (a queue that is full, a wait that times out, a request that is aborted,
|
|
110
|
+
* a waiter dropped by `clear`) either never increments `active` or has already
|
|
111
|
+
* been counted, and `release()` is idempotent so a double release from a `finally`
|
|
112
|
+
* plus an abort listener cannot leak a slot. A leaked slot is invisible until the
|
|
113
|
+
* pool permanently deadlocks at its ceiling, at which point every later request
|
|
114
|
+
* hangs — which is why it is enforced here rather than at the call site.
|
|
115
|
+
*/
|
|
116
|
+
export declare class RequestPoolScheduler {
|
|
117
|
+
private concurrency;
|
|
118
|
+
private readonly maxQueue;
|
|
119
|
+
private readonly queueTimeout;
|
|
120
|
+
private readonly priorityOf;
|
|
121
|
+
private active;
|
|
122
|
+
private sequence;
|
|
123
|
+
private readonly waiters;
|
|
124
|
+
constructor(options?: RequestPoolOptions);
|
|
125
|
+
/** Current counters. */
|
|
126
|
+
get stats(): RequestPoolStats;
|
|
127
|
+
/** Change the ceiling at runtime; raising it immediately admits queued work. */
|
|
128
|
+
setConcurrency(value: number): void;
|
|
129
|
+
/**
|
|
130
|
+
* Take a slot, or wait for one.
|
|
131
|
+
*
|
|
132
|
+
* Resolves with a ticket whose `release()` returns the slot. Rejects when the
|
|
133
|
+
* queue is full or the wait exceeds `queueTimeout`. `signal` lets an abandoned
|
|
134
|
+
* request leave the queue instead of holding a place it will never use.
|
|
135
|
+
*/
|
|
136
|
+
acquire(ctx: unknown, signal?: AbortLike): Promise<PoolTicket>;
|
|
137
|
+
/**
|
|
138
|
+
* Drop every waiter, rejecting each with `reason`.
|
|
139
|
+
*
|
|
140
|
+
* Used on uninstall: a queued request must not sit forever waiting for a pool
|
|
141
|
+
* that will never admit it.
|
|
142
|
+
*/
|
|
143
|
+
clear(reason?: unknown): void;
|
|
144
|
+
/** Hand out one slot. */
|
|
145
|
+
private issue;
|
|
146
|
+
/** Admit as many waiters as there are free slots. */
|
|
147
|
+
private pump;
|
|
148
|
+
/** Lowest priority number first, then arrival order. */
|
|
149
|
+
private sort;
|
|
150
|
+
private priorityOfFor;
|
|
151
|
+
private abortError;
|
|
152
|
+
}
|
|
153
|
+
//# sourceMappingURL=scheduler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scheduler.d.ts","sourceRoot":"","sources":["../../../src/plugins/pool/scheduler.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;GAUG;AACH,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,gBAAgB,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,IAAI,EAAE,OAAO,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;IAC3F,mBAAmB,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,IAAI,GAAG,IAAI,CAAC;CACjE;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,WAAW,kBAAkB;IACjC;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;;;;;;;;OASG;IACH,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,MAAM,CAAC;CACrC;AAED,wDAAwD;AACxD,MAAM,WAAW,gBAAgB;IAC/B,yCAAyC;IACzC,MAAM,EAAE,MAAM,CAAC;IACf,mCAAmC;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,0BAA0B;IAC1B,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,6EAA6E;AAC7E,MAAM,WAAW,UAAU;IACzB,sCAAsC;IACtC,QAAQ,CAAC,OAAO,EAAE,MAAM,IAAI,CAAC;CAC9B;AAWD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAyC;IAEpE,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgB;IAExC,YAAY,OAAO,GAAE,kBAAuB,EAU3C;IAED,wBAAwB;IACxB,IAAI,KAAK,IAAI,gBAAgB,CAE5B;IAED,gFAAgF;IAChF,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAIlC;IAED;;;;;;OAMG;IACH,OAAO,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAqE7D;IAED;;;;;OAKG;IACH,KAAK,CAAC,MAAM,CAAC,EAAE,OAAO,GAAG,IAAI,CAQ5B;IAID,yBAAyB;IACzB,OAAO,CAAC,KAAK;IAcb,qDAAqD;IACrD,OAAO,CAAC,IAAI;IAOZ,wDAAwD;IACxD,OAAO,CAAC,IAAI;IAMZ,OAAO,CAAC,aAAa;IAYrB,OAAO,CAAC,UAAU;CAGnB"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { SnailError } from "../../error/base.js";
|
|
2
|
+
/**
|
|
3
|
+
* The error codes a request-pool refusal can carry.
|
|
4
|
+
*
|
|
5
|
+
* `isPoolError()` is the supported way to test them, but the strings are exported
|
|
6
|
+
* so an application can switch on one precisely — for example to distinguish "the
|
|
7
|
+
* queue was full, try again shortly" from "the user navigated away".
|
|
8
|
+
*/
|
|
9
|
+
export declare const POOL_ERROR_CODES: {
|
|
10
|
+
/** `maxQueue` was reached; the request was never sent. */
|
|
11
|
+
readonly queueFull: "SNAIL_POOL_QUEUE_FULL";
|
|
12
|
+
/** The request waited longer than `queueTimeout` and was dropped. */
|
|
13
|
+
readonly queueTimeout: "SNAIL_POOL_QUEUE_TIMEOUT";
|
|
14
|
+
/** The request was cancelled while it was still waiting for a slot. */
|
|
15
|
+
readonly aborted: "SNAIL_POOL_ABORTED";
|
|
16
|
+
/** The queue was cleared — usually because the plugin was uninstalled. */
|
|
17
|
+
readonly cleared: "SNAIL_POOL_CLEARED";
|
|
18
|
+
};
|
|
19
|
+
/** Any code {@link SnailPoolError} can carry. */
|
|
20
|
+
export type PoolErrorCode = (typeof POOL_ERROR_CODES)[keyof typeof POOL_ERROR_CODES];
|
|
21
|
+
/**
|
|
22
|
+
* Raised when the pool refuses or drops a request.
|
|
23
|
+
*
|
|
24
|
+
* A dedicated class rather than a reuse of `SnailRequestError`: a pool refusal
|
|
25
|
+
* means the request **never reached the network**, which is a materially different
|
|
26
|
+
* situation from a transport failure. Retrying is safe and usually correct, the
|
|
27
|
+
* payload is untouched, and no server state changed — a caller that cannot tell the
|
|
28
|
+
* two apart will either retry a request the server already processed, or give up on
|
|
29
|
+
* one that was merely queued behind a burst.
|
|
30
|
+
*/
|
|
31
|
+
export declare class SnailPoolError extends SnailError {
|
|
32
|
+
/** Which refusal this is. */
|
|
33
|
+
readonly code: PoolErrorCode;
|
|
34
|
+
constructor(message: string, code: PoolErrorCode, options?: {
|
|
35
|
+
cause?: unknown;
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=type.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"type.d.ts","sourceRoot":"","sources":["../../../src/plugins/pool/type.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9C;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB;IAC3B,0DAA0D;aAC1D,SAAS,EAAE,uBAAuB;IAClC,qEAAqE;aACrE,YAAY,EAAE,0BAA0B;IACxC,uEAAuE;aACvE,OAAO,EAAE,oBAAoB;IAC7B,0EAA0E;aAC1E,OAAO,EAAE,oBAAoB;CACrB,CAAC;AAEX,iDAAiD;AACjD,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,OAAO,gBAAgB,CAAC,CAAC;AAErF;;;;;;;;;GASG;AACH,qBAAa,cAAe,SAAQ,UAAU;IAC5C,6BAA6B;IAC7B,SAAiB,IAAI,EAAE,aAAa,CAAC;IAErC,YAAY,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,GAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAO,EAElF;CACF"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* React adapter plugin.
|
|
3
|
+
*
|
|
4
|
+
* ```tsx
|
|
5
|
+
* import { ReactAdapter, useMethodState } from "@snail-js/api/plugins/react";
|
|
6
|
+
*
|
|
7
|
+
* Service.use(ReactAdapter());
|
|
8
|
+
*
|
|
9
|
+
* const { data, loading, error } = useMethodState(userApi.getUser("1"));
|
|
10
|
+
* ```
|
|
11
|
+
*
|
|
12
|
+
* This sits behind its own subpath rather than in `@snail-js/api/plugins`: that
|
|
13
|
+
* barrel must not statically import `react`, or an application using only `Cache`
|
|
14
|
+
* would fail to resolve React at all.
|
|
15
|
+
*
|
|
16
|
+
* @packageDocumentation
|
|
17
|
+
*/
|
|
18
|
+
export { ReactAdapter, useMethodState } from "./plugin.js";
|
|
19
|
+
export type { ReactAdapterOptions, ReactMethodState } from "./type.js";
|
|
20
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/plugins/react/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AACxD,YAAY,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,QAAQ,CAAC"}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { n as createPlugin } from "../../chunks/plugin-D3RDffDG.js";
|
|
2
|
+
import { t as SnailCancelledError } from "../../chunks/request-qeRhihEF.js";
|
|
3
|
+
import { a as readKey, o as unwrapEnvelope } from "../../chunks/response-D-ubkAFl.js";
|
|
4
|
+
import { t as reactStateAdapter } from "../../chunks/react-C1em-gxy.js";
|
|
5
|
+
//#region src/plugins/react/plugin.ts
|
|
6
|
+
/**
|
|
7
|
+
* React adapter plugin.
|
|
8
|
+
*
|
|
9
|
+
* Mirrors every request's state onto `ctx.meta` as subscribable boxes and exposes
|
|
10
|
+
* {@link useMethodState} to bind them during render.
|
|
11
|
+
*
|
|
12
|
+
* ## Why this is not just the Vue plugin with another adapter
|
|
13
|
+
*
|
|
14
|
+
* A Vue `ref` tracks reads by itself, so writing it is enough. A React box has no
|
|
15
|
+
* idea a component rendered it: the component must subscribe during render
|
|
16
|
+
* (`useSyncExternalStore`) or the new value is never painted. That subscription
|
|
17
|
+
* lives in `useMethodState`, not in the plugin hooks.
|
|
18
|
+
*
|
|
19
|
+
* ## Why `initMeta` and not `beforeRequest`
|
|
20
|
+
*
|
|
21
|
+
* `initMeta` runs **once**, when the `SnailMethod` is built, while `beforeCreate`
|
|
22
|
+
* runs per `send()`. Creating the boxes per send is the classic bug where a
|
|
23
|
+
* component keeps rendering the first response forever, because the UI captured
|
|
24
|
+
* box #1 while the second send wrote into box #2.
|
|
25
|
+
*/
|
|
26
|
+
/** Key of the `loading` handle. Fixed, and not part of the envelope. */
|
|
27
|
+
var LOADING_KEY = "loading";
|
|
28
|
+
/** Key of the `error` handle. Fixed, and not part of the envelope. */
|
|
29
|
+
var ERROR_KEY = "error";
|
|
30
|
+
/**
|
|
31
|
+
* Stand-in bound when a handle is missing.
|
|
32
|
+
*
|
|
33
|
+
* `useMethodState` must call the same number of hooks in the same order on every
|
|
34
|
+
* render; a missing handle therefore cannot mean "call one hook fewer".
|
|
35
|
+
*/
|
|
36
|
+
var MISSING_HANDLE = reactStateAdapter.create(void 0);
|
|
37
|
+
/** `useSyncExternalStore` behind a non-optional name, so it can always be called. */
|
|
38
|
+
var bindHandle = reactStateAdapter.useBind;
|
|
39
|
+
/**
|
|
40
|
+
* Create one box unless the caller already holds one for that key.
|
|
41
|
+
*
|
|
42
|
+
* The guard keeps a re-run of `initMeta` (a context built before the plugin was
|
|
43
|
+
* installed) from replacing boxes the UI is already subscribed to.
|
|
44
|
+
*/
|
|
45
|
+
function ensureHandle(ctx, key, initial) {
|
|
46
|
+
if (reactStateAdapter.isState?.(ctx.meta[key])) return;
|
|
47
|
+
ctx.meta[key] = reactStateAdapter.create(initial);
|
|
48
|
+
}
|
|
49
|
+
/** Create the five boxes. Runs once per `SnailMethod`. */
|
|
50
|
+
function createHandles(ctx) {
|
|
51
|
+
const { dataKey, codeKey, messageKey } = ctx.serverOptions;
|
|
52
|
+
ensureHandle(ctx, dataKey, void 0);
|
|
53
|
+
ensureHandle(ctx, codeKey, void 0);
|
|
54
|
+
ensureHandle(ctx, messageKey, void 0);
|
|
55
|
+
ensureHandle(ctx, LOADING_KEY, false);
|
|
56
|
+
ensureHandle(ctx, ERROR_KEY, void 0);
|
|
57
|
+
}
|
|
58
|
+
/** Write one box, ignoring a key this plugin never created. */
|
|
59
|
+
function writeHandle(ctx, key, value) {
|
|
60
|
+
const handle = ctx.meta[key];
|
|
61
|
+
if (handle) reactStateAdapter.write(handle, value);
|
|
62
|
+
}
|
|
63
|
+
/** Copy the envelope of the current response into the caller's boxes. */
|
|
64
|
+
function captureResponse(ctx) {
|
|
65
|
+
const response = ctx.getResponse();
|
|
66
|
+
if (!response) return;
|
|
67
|
+
const { dataKey, codeKey, messageKey } = ctx.serverOptions;
|
|
68
|
+
writeHandle(ctx, dataKey, unwrapEnvelope(response.data, dataKey));
|
|
69
|
+
writeHandle(ctx, codeKey, readKey(response.data, codeKey));
|
|
70
|
+
writeHandle(ctx, messageKey, readKey(response.data, messageKey));
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Read a request method's state inside a component, subscribing it to changes.
|
|
74
|
+
*
|
|
75
|
+
* ```tsx
|
|
76
|
+
* function User({ id }: { id: string }) {
|
|
77
|
+
* const method = useMemo(() => userApi.getUser(id), [id]);
|
|
78
|
+
* const { data, loading, error } = useMethodState(method);
|
|
79
|
+
* useEffect(() => { void method.send(); }, [method]);
|
|
80
|
+
* if (loading) return <Spinner />;
|
|
81
|
+
* return <p>{error ? String(error) : data?.name}</p>;
|
|
82
|
+
* }
|
|
83
|
+
* ```
|
|
84
|
+
*
|
|
85
|
+
* The five handles are bound unconditionally and in a fixed order, because React
|
|
86
|
+
* identifies hooks by call position: a conditional `useBind` throws
|
|
87
|
+
* "rendered fewer hooks than expected" as soon as the second render takes a
|
|
88
|
+
* different branch.
|
|
89
|
+
*/
|
|
90
|
+
function useMethodState(method) {
|
|
91
|
+
const meta = method.meta;
|
|
92
|
+
const { dataKey, codeKey, messageKey } = method.context.serverOptions;
|
|
93
|
+
const data = bindHandle(meta[dataKey] ?? MISSING_HANDLE);
|
|
94
|
+
const code = bindHandle(meta[codeKey] ?? MISSING_HANDLE);
|
|
95
|
+
const message = bindHandle(meta[messageKey] ?? MISSING_HANDLE);
|
|
96
|
+
const loading = bindHandle(meta[LOADING_KEY] ?? MISSING_HANDLE);
|
|
97
|
+
const error = bindHandle(meta[ERROR_KEY] ?? MISSING_HANDLE);
|
|
98
|
+
return {
|
|
99
|
+
data,
|
|
100
|
+
loading: Boolean(loading),
|
|
101
|
+
error,
|
|
102
|
+
code,
|
|
103
|
+
message
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Create the React adapter plugin.
|
|
108
|
+
*
|
|
109
|
+
* ```tsx
|
|
110
|
+
* Service.use(ReactAdapter());
|
|
111
|
+
*
|
|
112
|
+
* const method = Service.createApi(UserApi).getUser("1");
|
|
113
|
+
* const { data, loading } = useMethodState(method);
|
|
114
|
+
* ```
|
|
115
|
+
*/
|
|
116
|
+
var ReactAdapter = createPlugin({
|
|
117
|
+
name: "react-adapter",
|
|
118
|
+
priority: 0,
|
|
119
|
+
setup() {
|
|
120
|
+
return {
|
|
121
|
+
initMeta(ctx) {
|
|
122
|
+
createHandles(ctx);
|
|
123
|
+
},
|
|
124
|
+
beforeCreate(ctx) {
|
|
125
|
+
writeHandle(ctx, LOADING_KEY, true);
|
|
126
|
+
writeHandle(ctx, ERROR_KEY, void 0);
|
|
127
|
+
},
|
|
128
|
+
afterResponse(ctx, next) {
|
|
129
|
+
captureResponse(ctx);
|
|
130
|
+
return next();
|
|
131
|
+
},
|
|
132
|
+
onError(ctx, error) {
|
|
133
|
+
if (error instanceof SnailCancelledError) return;
|
|
134
|
+
writeHandle(ctx, ERROR_KEY, error);
|
|
135
|
+
},
|
|
136
|
+
afterRequest(ctx) {
|
|
137
|
+
writeHandle(ctx, LOADING_KEY, false);
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
//#endregion
|
|
143
|
+
export { ReactAdapter, useMethodState };
|
|
144
|
+
|
|
145
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/plugins/react/plugin.ts"],"sourcesContent":["import { reactStateAdapter } from \"../../adapter/react\";\nimport { createPlugin } from \"../../core/plugin\";\nimport { readKey, unwrapEnvelope } from \"../../core/response\";\nimport { SnailCancelledError } from \"../../error/request\";\nimport type { SnailContext } from \"../../core/context\";\nimport type { SnailMethod } from \"../../core/method\";\nimport type { SnailStateRef } from \"../../typings/adapter\";\nimport type { ReactAdapterOptions, ReactMethodState } from \"./type\";\n\n/**\n * React adapter plugin.\n *\n * Mirrors every request's state onto `ctx.meta` as subscribable boxes and exposes\n * {@link useMethodState} to bind them during render.\n *\n * ## Why this is not just the Vue plugin with another adapter\n *\n * A Vue `ref` tracks reads by itself, so writing it is enough. A React box has no\n * idea a component rendered it: the component must subscribe during render\n * (`useSyncExternalStore`) or the new value is never painted. That subscription\n * lives in `useMethodState`, not in the plugin hooks.\n *\n * ## Why `initMeta` and not `beforeRequest`\n *\n * `initMeta` runs **once**, when the `SnailMethod` is built, while `beforeCreate`\n * runs per `send()`. Creating the boxes per send is the classic bug where a\n * component keeps rendering the first response forever, because the UI captured\n * box #1 while the second send wrote into box #2.\n */\n\n/** Key of the `loading` handle. Fixed, and not part of the envelope. */\nconst LOADING_KEY = \"loading\";\n\n/** Key of the `error` handle. Fixed, and not part of the envelope. */\nconst ERROR_KEY = \"error\";\n\n/**\n * Stand-in bound when a handle is missing.\n *\n * `useMethodState` must call the same number of hooks in the same order on every\n * render; a missing handle therefore cannot mean \"call one hook fewer\".\n */\nconst MISSING_HANDLE = reactStateAdapter.create<unknown>(undefined);\n\n/** `useSyncExternalStore` behind a non-optional name, so it can always be called. */\nconst bindHandle = reactStateAdapter.useBind as (ref: SnailStateRef) => unknown;\n\n/**\n * Create one box unless the caller already holds one for that key.\n *\n * The guard keeps a re-run of `initMeta` (a context built before the plugin was\n * installed) from replacing boxes the UI is already subscribed to.\n */\nfunction ensureHandle(ctx: SnailContext, key: string, initial: unknown): void {\n if (reactStateAdapter.isState?.(ctx.meta[key])) return;\n ctx.meta[key] = reactStateAdapter.create(initial);\n}\n\n/** Create the five boxes. Runs once per `SnailMethod`. */\nfunction createHandles(ctx: SnailContext): void {\n const { dataKey, codeKey, messageKey } = ctx.serverOptions;\n ensureHandle(ctx, dataKey, undefined);\n ensureHandle(ctx, codeKey, undefined);\n ensureHandle(ctx, messageKey, undefined);\n ensureHandle(ctx, LOADING_KEY, false);\n ensureHandle(ctx, ERROR_KEY, undefined);\n}\n\n/** Write one box, ignoring a key this plugin never created. */\nfunction writeHandle(ctx: SnailContext, key: string, value: unknown): void {\n const handle = ctx.meta[key];\n if (handle) reactStateAdapter.write(handle as SnailStateRef, value);\n}\n\n/** Copy the envelope of the current response into the caller's boxes. */\nfunction captureResponse(ctx: SnailContext): void {\n const response = ctx.getResponse();\n if (!response) return;\n\n const { dataKey, codeKey, messageKey } = ctx.serverOptions;\n writeHandle(ctx, dataKey, unwrapEnvelope(response.data, dataKey));\n writeHandle(ctx, codeKey, readKey(response.data, codeKey));\n writeHandle(ctx, messageKey, readKey(response.data, messageKey));\n}\n\n/**\n * Read a request method's state inside a component, subscribing it to changes.\n *\n * ```tsx\n * function User({ id }: { id: string }) {\n * const method = useMemo(() => userApi.getUser(id), [id]);\n * const { data, loading, error } = useMethodState(method);\n * useEffect(() => { void method.send(); }, [method]);\n * if (loading) return <Spinner />;\n * return <p>{error ? String(error) : data?.name}</p>;\n * }\n * ```\n *\n * The five handles are bound unconditionally and in a fixed order, because React\n * identifies hooks by call position: a conditional `useBind` throws\n * \"rendered fewer hooks than expected\" as soon as the second render takes a\n * different branch.\n */\nexport function useMethodState<TData = unknown>(\n method: SnailMethod<any, TData, any, any, any>\n): ReactMethodState<TData> {\n const meta = method.meta;\n const { dataKey, codeKey, messageKey } = method.context.serverOptions;\n\n // Fixed order and fixed count — do not reorder, do not hoist into a loop.\n const data = bindHandle((meta[dataKey] ?? MISSING_HANDLE) as SnailStateRef);\n const code = bindHandle((meta[codeKey] ?? MISSING_HANDLE) as SnailStateRef);\n const message = bindHandle((meta[messageKey] ?? MISSING_HANDLE) as SnailStateRef);\n const loading = bindHandle((meta[LOADING_KEY] ?? MISSING_HANDLE) as SnailStateRef);\n const error = bindHandle((meta[ERROR_KEY] ?? MISSING_HANDLE) as SnailStateRef);\n\n return {\n data: data as TData | undefined,\n loading: Boolean(loading),\n error,\n code,\n message\n };\n}\n\n/**\n * Create the React adapter plugin.\n *\n * ```tsx\n * Service.use(ReactAdapter());\n *\n * const method = Service.createApi(UserApi).getUser(\"1\");\n * const { data, loading } = useMethodState(method);\n * ```\n */\nexport const ReactAdapter = createPlugin<ReactAdapterOptions>({\n name: \"react-adapter\",\n priority: 0,\n\n setup() {\n return {\n initMeta(ctx) {\n createHandles(ctx);\n },\n\n beforeCreate(ctx) {\n writeHandle(ctx, LOADING_KEY, true);\n // A new attempt clears the previous failure; a stale error would otherwise\n // keep rendering next to data that has just loaded successfully.\n writeHandle(ctx, ERROR_KEY, undefined);\n },\n\n afterResponse(ctx, next) {\n captureResponse(ctx);\n return next();\n },\n\n onError(ctx, error) {\n // Cancellation is expected control flow, not a failure: writing it would\n // flash an error state every time a component unmounts mid-request.\n if (error instanceof SnailCancelledError) return;\n writeHandle(ctx, ERROR_KEY, error);\n },\n\n afterRequest(ctx) {\n writeHandle(ctx, LOADING_KEY, false);\n }\n };\n }\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AA+BA,IAAM,cAAc;;AAGpB,IAAM,YAAY;;;;;;;AAQlB,IAAM,iBAAiB,kBAAkB,OAAgB,KAAA,CAAS;;AAGlE,IAAM,aAAa,kBAAkB;;;;;;;AAQrC,SAAS,aAAa,KAAmB,KAAa,SAAwB;CAC5E,IAAI,kBAAkB,UAAU,IAAI,KAAK,IAAI,GAAG;CAChD,IAAI,KAAK,OAAO,kBAAkB,OAAO,OAAO;AAClD;;AAGA,SAAS,cAAc,KAAyB;CAC9C,MAAM,EAAE,SAAS,SAAS,eAAe,IAAI;CAC7C,aAAa,KAAK,SAAS,KAAA,CAAS;CACpC,aAAa,KAAK,SAAS,KAAA,CAAS;CACpC,aAAa,KAAK,YAAY,KAAA,CAAS;CACvC,aAAa,KAAK,aAAa,KAAK;CACpC,aAAa,KAAK,WAAW,KAAA,CAAS;AACxC;;AAGA,SAAS,YAAY,KAAmB,KAAa,OAAsB;CACzE,MAAM,SAAS,IAAI,KAAK;CACxB,IAAI,QAAQ,kBAAkB,MAAM,QAAyB,KAAK;AACpE;;AAGA,SAAS,gBAAgB,KAAyB;CAChD,MAAM,WAAW,IAAI,YAAY;CACjC,IAAI,CAAC,UAAU;CAEf,MAAM,EAAE,SAAS,SAAS,eAAe,IAAI;CAC7C,YAAY,KAAK,SAAS,eAAe,SAAS,MAAM,OAAO,CAAC;CAChE,YAAY,KAAK,SAAS,QAAQ,SAAS,MAAM,OAAO,CAAC;CACzD,YAAY,KAAK,YAAY,QAAQ,SAAS,MAAM,UAAU,CAAC;AACjE;;;;;;;;;;;;;;;;;;;AAoBA,SAAgB,eACd,QACyB;CACzB,MAAM,OAAO,OAAO;CACpB,MAAM,EAAE,SAAS,SAAS,eAAe,OAAO,QAAQ;CAGxD,MAAM,OAAO,WAAY,KAAK,YAAY,cAAgC;CAC1E,MAAM,OAAO,WAAY,KAAK,YAAY,cAAgC;CAC1E,MAAM,UAAU,WAAY,KAAK,eAAe,cAAgC;CAChF,MAAM,UAAU,WAAY,KAAK,gBAAgB,cAAgC;CACjF,MAAM,QAAQ,WAAY,KAAK,cAAc,cAAgC;CAE7E,OAAO;EACC;EACN,SAAS,QAAQ,OAAO;EACxB;EACA;EACA;CACF;AACF;;;;;;;;;;;AAYA,IAAa,eAAe,aAAkC;CAC5D,MAAM;CACN,UAAU;CAEV,QAAQ;EACN,OAAO;GACL,SAAS,KAAK;IACZ,cAAc,GAAG;GACnB;GAEA,aAAa,KAAK;IAChB,YAAY,KAAK,aAAa,IAAI;IAGlC,YAAY,KAAK,WAAW,KAAA,CAAS;GACvC;GAEA,cAAc,KAAK,MAAM;IACvB,gBAAgB,GAAG;IACnB,OAAO,KAAK;GACd;GAEA,QAAQ,KAAK,OAAO;IAGlB,IAAI,iBAAiB,qBAAqB;IAC1C,YAAY,KAAK,WAAW,KAAK;GACnC;GAEA,aAAa,KAAK;IAChB,YAAY,KAAK,aAAa,KAAK;GACrC;EACF;CACF;AACF,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { SnailMethod } from "../../core/method.js";
|
|
2
|
+
import type { ReactAdapterOptions, ReactMethodState } from "./type.js";
|
|
3
|
+
/**
|
|
4
|
+
* Read a request method's state inside a component, subscribing it to changes.
|
|
5
|
+
*
|
|
6
|
+
* ```tsx
|
|
7
|
+
* function User({ id }: { id: string }) {
|
|
8
|
+
* const method = useMemo(() => userApi.getUser(id), [id]);
|
|
9
|
+
* const { data, loading, error } = useMethodState(method);
|
|
10
|
+
* useEffect(() => { void method.send(); }, [method]);
|
|
11
|
+
* if (loading) return <Spinner />;
|
|
12
|
+
* return <p>{error ? String(error) : data?.name}</p>;
|
|
13
|
+
* }
|
|
14
|
+
* ```
|
|
15
|
+
*
|
|
16
|
+
* The five handles are bound unconditionally and in a fixed order, because React
|
|
17
|
+
* identifies hooks by call position: a conditional `useBind` throws
|
|
18
|
+
* "rendered fewer hooks than expected" as soon as the second render takes a
|
|
19
|
+
* different branch.
|
|
20
|
+
*/
|
|
21
|
+
export declare function useMethodState<TData = unknown>(method: SnailMethod<any, TData, any, any, any>): ReactMethodState<TData>;
|
|
22
|
+
/**
|
|
23
|
+
* Create the React adapter plugin.
|
|
24
|
+
*
|
|
25
|
+
* ```tsx
|
|
26
|
+
* Service.use(ReactAdapter());
|
|
27
|
+
*
|
|
28
|
+
* const method = Service.createApi(UserApi).getUser("1");
|
|
29
|
+
* const { data, loading } = useMethodState(method);
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export declare const ReactAdapter: import("../../index.js").SnailPlugin<ReactAdapterOptions>;
|
|
33
|
+
//# sourceMappingURL=plugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../../src/plugins/react/plugin.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,OAAO,KAAK,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,QAAQ,CAAC;AA8EpE;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,cAAc,CAAC,KAAK,GAAG,OAAO,EAC5C,MAAM,EAAE,WAAW,CAAC,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,GAC7C,gBAAgB,CAAC,KAAK,CAAC,CAkBzB;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,YAAY,kDAkCvB,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Options accepted by the React adapter plugin.
|
|
3
|
+
*
|
|
4
|
+
* Deliberately empty: the adapter mirrors the envelope keys the server already
|
|
5
|
+
* declares (`dataKey` / `codeKey` / `messageKey`) and adds the two fixed handles
|
|
6
|
+
* `loading` and `error`. A per-plugin key option was avoided on purpose — the
|
|
7
|
+
* companion `useMethodState` hook has to bind the same handles in a fixed order,
|
|
8
|
+
* and a configurable key would have to be threaded through the hook to stay in
|
|
9
|
+
* sync.
|
|
10
|
+
*/
|
|
11
|
+
export interface ReactAdapterOptions {
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* What {@link useMethodState} returns.
|
|
15
|
+
*
|
|
16
|
+
* Deliberately discriminated from a `SnailResult`: the values update over time and
|
|
17
|
+
* each one is read live from its own state box, so `data` can be `undefined`
|
|
18
|
+
* (nothing loaded yet) even though a successful send always has one.
|
|
19
|
+
*/
|
|
20
|
+
export interface ReactMethodState<TData = unknown> {
|
|
21
|
+
/** Unwrapped payload of the most recent response. */
|
|
22
|
+
data: TData | undefined;
|
|
23
|
+
/** `true` between `send()` and settlement. */
|
|
24
|
+
loading: boolean;
|
|
25
|
+
/** Failure of the most recent send, cleared at the start of the next one. */
|
|
26
|
+
error: unknown;
|
|
27
|
+
/** Business code of the most recent response. */
|
|
28
|
+
code: unknown;
|
|
29
|
+
/** Business message of the most recent response. */
|
|
30
|
+
message: unknown;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=type.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"type.d.ts","sourceRoot":"","sources":["../../../src/plugins/react/type.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,MAAM,WAAW,mBAAmB;CAAG;AAEvC;;;;;;GAMG;AACH,MAAM,WAAW,gBAAgB,CAAC,KAAK,GAAG,OAAO;IAC/C,qDAAqD;IACrD,IAAI,EAAE,KAAK,GAAG,SAAS,CAAC;IAExB,8CAA8C;IAC9C,OAAO,EAAE,OAAO,CAAC;IAEjB,6EAA6E;IAC7E,KAAK,EAAE,OAAO,CAAC;IAEf,iDAAiD;IACjD,IAAI,EAAE,OAAO,CAAC;IAEd,oDAAoD;IACpD,OAAO,EAAE,OAAO,CAAC;CAClB"}
|