@snail-js/api 0.1.28 → 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 -4871
- package/dist/snail-api.umd.cjs +0 -4875
- 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 @@
|
|
|
1
|
+
{"version":3,"file":"web-storage.d.ts","sourceRoot":"","sources":["../../../../src/plugins/cache/adapters/web-storage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAS5C,0DAA0D;AAC1D,MAAM,WAAW,6BAA6B;IAC5C,+EAA+E;IAC/E,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB,uDAAuD;IACvD,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;;;;;GAcG;AACH,qBAAa,sBAAuB,YAAW,YAAY;IACzD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA4B;IAC3D,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAEhC,gCAAgC;IAChC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAEvB,YACE,cAAc,EAAE,MAAM,OAAO,GAAG,SAAS,EACzC,OAAO,GAAE,6BAAkC,EAK5C;IAED,4EAA4E;IAC5E,IAAI,SAAS,IAAI,OAAO,CAEvB;IAEK,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAqB1D;IAEK,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAYxE;IAEK,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEvC;IAEK,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAO3B;IAEK,IAAI,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAK9B;IAED,OAAO,CAAC,OAAO;IAUf,OAAO,CAAC,GAAG;IAIX,4DAA4D;IAC5D,OAAO,CAAC,MAAM;IASd,OAAO,CAAC,KAAK;CAUd"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { CacheableOptions } from "./type.js";
|
|
2
|
+
type DualDecorator = ClassDecorator & MethodDecorator;
|
|
3
|
+
/**
|
|
4
|
+
* Mark this method — or every method of this class — as cacheable.
|
|
5
|
+
*
|
|
6
|
+
* Passing the decorator is an explicit opt-in: it caches even a verb that
|
|
7
|
+
* `CacheOptions.cacheFor` does not list. Without it, `cacheFor` decides, and the
|
|
8
|
+
* reason is that "cache every POST of this class" is far too easy to write by
|
|
9
|
+
* accident when the intent was only to attach tags.
|
|
10
|
+
*
|
|
11
|
+
* ```ts
|
|
12
|
+
* @Api("/user")
|
|
13
|
+
* @Cacheable({ tags: ["users"] })
|
|
14
|
+
* class UserApi {
|
|
15
|
+
* @Get("/") list(): Promise<User[]> { return null!; }
|
|
16
|
+
*
|
|
17
|
+
* @Get("/stats")
|
|
18
|
+
* @Cacheable({ ttl: 5 })
|
|
19
|
+
* stats(): Promise<Stats> { return null!; }
|
|
20
|
+
* }
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export declare function Cacheable(options?: CacheableOptions): DualDecorator;
|
|
24
|
+
/**
|
|
25
|
+
* Opt this method — or this entire class — out of caching.
|
|
26
|
+
*
|
|
27
|
+
* A method-level `@NoCache()` beats a class-level `@Cacheable()`, and a
|
|
28
|
+
* class-level `@NoCache()` beats the `cacheFor` default. Opting out is the one
|
|
29
|
+
* decision that may never be overridden by a broader rule, because the cost of
|
|
30
|
+
* being wrong is serving stale data.
|
|
31
|
+
*/
|
|
32
|
+
export declare function NoCache(): DualDecorator;
|
|
33
|
+
/**
|
|
34
|
+
* Purge every cached entry carrying any of `tags` once this request succeeds.
|
|
35
|
+
*
|
|
36
|
+
* "Succeeds" means the HTTP round-trip did: the hook lives on the response path,
|
|
37
|
+
* so a failed or cancelled request invalidates nothing. A method may invalidate a
|
|
38
|
+
* tag it also stores under — the plugin purges before it stores, so the fresh
|
|
39
|
+
* entry survives its own invalidation.
|
|
40
|
+
*/
|
|
41
|
+
export declare function Invalidates(...tags: string[]): DualDecorator;
|
|
42
|
+
/**
|
|
43
|
+
* Legacy-compatible alias of {@link Invalidates} for a single source name.
|
|
44
|
+
*
|
|
45
|
+
* The pre-rewrite decorator was named after the *source* of a change rather than
|
|
46
|
+
* the entries it purges; application code written against it keeps working, and
|
|
47
|
+
* new code should prefer `@Invalidates`.
|
|
48
|
+
*/
|
|
49
|
+
export declare function HitSource(name: string): DualDecorator;
|
|
50
|
+
/**
|
|
51
|
+
* Read `@Cacheable(...)` from a method (when `methodName` is given) or a class.
|
|
52
|
+
*
|
|
53
|
+
* `undefined` means "not marked", which is different from `{}` ("marked, with no
|
|
54
|
+
* overrides") — the plugin treats the two differently.
|
|
55
|
+
*/
|
|
56
|
+
export declare function readCacheable(target: unknown, methodName?: string): CacheableOptions | undefined;
|
|
57
|
+
/** `true` when `@NoCache()` was applied to this method or class. */
|
|
58
|
+
export declare function readNoCache(target: unknown, methodName?: string): boolean;
|
|
59
|
+
/** Tags listed by `@Invalidates(...)` / `@HitSource(...)`, in application order. */
|
|
60
|
+
export declare function readInvalidates(target: unknown, methodName?: string): string[];
|
|
61
|
+
export {};
|
|
62
|
+
//# sourceMappingURL=decorators.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decorators.d.ts","sourceRoot":"","sources":["../../../src/plugins/cache/decorators.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,QAAQ,CAAC;AAuB/C,KAAK,aAAa,GAAG,cAAc,GAAG,eAAe,CAAC;AAgDtD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,SAAS,CAAC,OAAO,GAAE,gBAAqB,GAAG,aAAa,CAUvE;AAED;;;;;;;GAOG;AACH,wBAAgB,OAAO,IAAI,aAAa,CAEvC;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,GAAG,IAAI,EAAE,MAAM,EAAE,GAAG,aAAa,CAkB5D;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,CAErD;AAID;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,OAAO,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS,CAIhG;AAED,oEAAoE;AACpE,wBAAgB,WAAW,CAAC,MAAM,EAAE,OAAO,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAOzE;AAED,oFAAoF;AACpF,wBAAgB,eAAe,CAAC,MAAM,EAAE,OAAO,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAM9E"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cache plugin.
|
|
3
|
+
*
|
|
4
|
+
* ```ts
|
|
5
|
+
* import { Cache, Cacheable, Invalidates, NoCache } from "@snail-js/api/plugins";
|
|
6
|
+
*
|
|
7
|
+
* Service.use(Cache({ ttl: 30, l2: "localStorage" }));
|
|
8
|
+
*
|
|
9
|
+
* @Api("/user")
|
|
10
|
+
* class UserApi {
|
|
11
|
+
* @Get("/")
|
|
12
|
+
* @Cacheable({ tags: ["users"] })
|
|
13
|
+
* list(): Promise<User[]> { return null!; }
|
|
14
|
+
*
|
|
15
|
+
* @Post("/")
|
|
16
|
+
* @Invalidates("users")
|
|
17
|
+
* create(@Data() body: NewUser): Promise<User> { return null!; }
|
|
18
|
+
* }
|
|
19
|
+
* ```
|
|
20
|
+
*
|
|
21
|
+
* `priority: -100` is the reserved cache band: last in forward order, so the key
|
|
22
|
+
* is hashed from the final url/params/body, and first in unwind order, so the raw
|
|
23
|
+
* envelope is stored before validation and transformation touch it.
|
|
24
|
+
*
|
|
25
|
+
* @packageDocumentation
|
|
26
|
+
*/
|
|
27
|
+
export { IndexedDBCacheAdapter } from "./adapters/indexeddb.js";
|
|
28
|
+
export type { IndexedDBCacheAdapterOptions } from "./adapters/indexeddb.js";
|
|
29
|
+
export { DEFAULT_L1_MAX_SIZE, MemoryCacheAdapter } from "./adapters/memory.js";
|
|
30
|
+
export type { MemoryCacheAdapterOptions } from "./adapters/memory.js";
|
|
31
|
+
export { WebStorageCacheAdapter } from "./adapters/web-storage.js";
|
|
32
|
+
export type { WebStorageCacheAdapterOptions } from "./adapters/web-storage.js";
|
|
33
|
+
export { Cacheable, HitSource, Invalidates, NoCache, readCacheable, readInvalidates, readNoCache } from "./decorators.js";
|
|
34
|
+
export { buildCacheKey } from "./key.js";
|
|
35
|
+
export type { CacheKeyInput } from "./key.js";
|
|
36
|
+
export { CacheManager } from "./manager.js";
|
|
37
|
+
export type { CacheManagerOptions, ResolvedCacheOptions } from "./manager.js";
|
|
38
|
+
export { CACHE_PLUGIN_NAME, CACHE_PRIORITY, Cache, makeCachedResponse } from "./plugin.js";
|
|
39
|
+
export type { CachePlugin } from "./plugin.js";
|
|
40
|
+
export type { CacheableOptions, CacheAdapter, CacheLookup, CacheOptions } from "./type.js";
|
|
41
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/plugins/cache/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,YAAY,EAAE,4BAA4B,EAAE,MAAM,sBAAsB,CAAC;AACzE,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAC5E,YAAY,EAAE,yBAAyB,EAAE,MAAM,mBAAmB,CAAC;AACnE,OAAO,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AAChE,YAAY,EAAE,6BAA6B,EAAE,MAAM,wBAAwB,CAAC;AAC5E,OAAO,EACL,SAAS,EACT,SAAS,EACT,WAAW,EACX,OAAO,EACP,aAAa,EACb,eAAe,EACf,WAAW,EACZ,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AACtC,YAAY,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACzC,YAAY,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AAC3E,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,KAAK,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AACxF,YAAY,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAC5C,YAAY,EACV,gBAAgB,EAChB,YAAY,EACZ,WAAW,EACX,YAAY,EACb,MAAM,QAAQ,CAAC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { InternalAxiosRequestConfig } from "axios";
|
|
2
|
+
import type { SnailMethodType } from "../../typings/api.js";
|
|
3
|
+
/**
|
|
4
|
+
* Cache-key construction.
|
|
5
|
+
*
|
|
6
|
+
* ## Why the whole request identity is hashed
|
|
7
|
+
*
|
|
8
|
+
* A key has to answer one question: "would this request produce the same
|
|
9
|
+
* response as the one I already stored?". That depends on the verb, the final
|
|
10
|
+
* url, the query params and the body — and on nothing else. Headers are
|
|
11
|
+
* deliberately excluded: an `Authorization` header changes *who* asks, not what
|
|
12
|
+
* is asked, and including it would turn every token refresh into a cache flush.
|
|
13
|
+
*
|
|
14
|
+
* ## Why `stableStringify`
|
|
15
|
+
*
|
|
16
|
+
* `{ a: 1, b: 2 }` and `{ b: 2, a: 1 }` are the same query. Serialising them
|
|
17
|
+
* with `JSON.stringify` produces two different strings and therefore two cache
|
|
18
|
+
* entries for one logical request; `stableStringify` sorts object keys, so the
|
|
19
|
+
* hash — and the key — is order-independent.
|
|
20
|
+
*
|
|
21
|
+
* ## Why the url is final here
|
|
22
|
+
*
|
|
23
|
+
* The cache plugin runs at `priority: -100` — last in forward order — precisely
|
|
24
|
+
* so the interceptor plugin (`100`) and the argument decorators have already
|
|
25
|
+
* rewritten `ctx.request.url` and filled in `:placeholders`. Hashing an earlier
|
|
26
|
+
* stage of the url would key `/user/:id` and `/user/42` differently, or worse,
|
|
27
|
+
* identically.
|
|
28
|
+
*/
|
|
29
|
+
/** Everything needed to identify one cacheable request. */
|
|
30
|
+
export interface CacheKeyInput {
|
|
31
|
+
/** Namespace, so two servers on one origin never share entries. */
|
|
32
|
+
prefix: string;
|
|
33
|
+
/** The live axios config — url, params and body must already be final. */
|
|
34
|
+
request: InternalAxiosRequestConfig;
|
|
35
|
+
/** Fallback verb when the config carries none. */
|
|
36
|
+
methodType: SnailMethodType;
|
|
37
|
+
/** `@Cacheable({ key })`: use this verbatim instead of the request identity. */
|
|
38
|
+
explicitKey?: string;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Build the key used for one request.
|
|
42
|
+
*
|
|
43
|
+
* The readable part (`prefix:VERB`) is kept outside the hash so a misbehaving
|
|
44
|
+
* cache can be diagnosed from a key alone; only the volatile signature is hashed,
|
|
45
|
+
* which keeps keys short enough for `localStorage`.
|
|
46
|
+
*/
|
|
47
|
+
export declare function buildCacheKey(input: CacheKeyInput): string;
|
|
48
|
+
//# sourceMappingURL=key.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"key.d.ts","sourceRoot":"","sources":["../../../src/plugins/cache/key.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,OAAO,CAAC;AACxD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAGzD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,2DAA2D;AAC3D,MAAM,WAAW,aAAa;IAC5B,mEAAmE;IACnE,MAAM,EAAE,MAAM,CAAC;IAEf,0EAA0E;IAC1E,OAAO,EAAE,0BAA0B,CAAC;IAEpC,kDAAkD;IAClD,UAAU,EAAE,eAAe,CAAC;IAE5B,gFAAgF;IAChF,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,aAAa,GAAG,MAAM,CAmB1D"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import type { SnailLogger } from "../../core/logger.js";
|
|
2
|
+
import type { SnailMethodType } from "../../typings/api.js";
|
|
3
|
+
import type { CacheAdapter, CacheLookup, CacheOptions } from "./type.js";
|
|
4
|
+
export interface CacheManagerOptions extends CacheOptions {
|
|
5
|
+
/**
|
|
6
|
+
* Logger for L2 failures.
|
|
7
|
+
*
|
|
8
|
+
* The manager runs outside any request, so it has no `ctx.logger`; the plugin
|
|
9
|
+
* injects one built from `@Server({ logLevel })`. Defaults to silent, because a
|
|
10
|
+
* cache must not make an application noisy by itself.
|
|
11
|
+
*/
|
|
12
|
+
logger?: SnailLogger;
|
|
13
|
+
}
|
|
14
|
+
/** `CacheOptions` with every default applied — the manager's public state. */
|
|
15
|
+
export interface ResolvedCacheOptions {
|
|
16
|
+
ttl: number;
|
|
17
|
+
maxSize: number;
|
|
18
|
+
l1: boolean;
|
|
19
|
+
l2: CacheAdapter | undefined;
|
|
20
|
+
cacheFor: "all" | readonly SnailMethodType[];
|
|
21
|
+
prefix: string;
|
|
22
|
+
staleWhileRevalidate: boolean;
|
|
23
|
+
dedupe: boolean;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Storage engine behind the cache plugin.
|
|
27
|
+
*
|
|
28
|
+
* ## Layers
|
|
29
|
+
*
|
|
30
|
+
* - **L1** is a {@link MemoryCacheAdapter}: always present unless `l1: false`,
|
|
31
|
+
* bounded by `maxSize`, and the only place an L2 hit is promoted into.
|
|
32
|
+
* - **L2** is any {@link CacheAdapter}, usually a persistent store.
|
|
33
|
+
*
|
|
34
|
+
* ## What the manager owns that the adapters do not
|
|
35
|
+
*
|
|
36
|
+
* TTL, LRU and tags are policy, not storage. Keeping them here means a custom L2
|
|
37
|
+
* adapter only has to answer "get/set/delete" and never has to know what a tag
|
|
38
|
+
* is — which is what makes the adapter interface small enough to implement over
|
|
39
|
+
* anything (IndexedDB, `localStorage`, an HTTP cache).
|
|
40
|
+
*
|
|
41
|
+
* ## Freshness vs. `staleWhileRevalidate`
|
|
42
|
+
*
|
|
43
|
+
* Freshness is always decided by this class, using the `expiresAt` it records on
|
|
44
|
+
* `set`. In stale-while-revalidate mode L1 is told "never expire" so the stale
|
|
45
|
+
* copy survives to be served while the plugin refreshes it; otherwise L1 is
|
|
46
|
+
* given the real TTL and sweeps the entry itself. L2 always receives the real TTL
|
|
47
|
+
* so a second tab (which has no shared in-memory index) never reads a stale one.
|
|
48
|
+
*/
|
|
49
|
+
export declare class CacheManager {
|
|
50
|
+
/** Fully resolved options, exposed so the plugin can apply the same policy. */
|
|
51
|
+
readonly options: ResolvedCacheOptions;
|
|
52
|
+
private readonly l1;
|
|
53
|
+
private readonly logger;
|
|
54
|
+
/** key → absolute expiry timestamp in ms; `0` means "never expires". */
|
|
55
|
+
private readonly expiry;
|
|
56
|
+
/** tag → every key carrying it. */
|
|
57
|
+
private readonly tagIndex;
|
|
58
|
+
/** key → every tag it carries, so a delete can unindex it in O(tags). */
|
|
59
|
+
private readonly keyTags;
|
|
60
|
+
/** Cache key → the promise of the request currently being sent for it. */
|
|
61
|
+
private readonly flight;
|
|
62
|
+
constructor(options?: CacheManagerOptions);
|
|
63
|
+
/** Number of live **L1** entries. L2 is not enumerated, by design. */
|
|
64
|
+
get size(): number;
|
|
65
|
+
/**
|
|
66
|
+
* Read a fresh value.
|
|
67
|
+
*
|
|
68
|
+
* A stale entry (kept alive only for stale-while-revalidate) is *not* returned:
|
|
69
|
+
* callers that must serve staleness ask {@link lookup} explicitly, so the
|
|
70
|
+
* common path cannot accidentally serve expired data.
|
|
71
|
+
*/
|
|
72
|
+
get<T = unknown>(key: string): Promise<T | undefined>;
|
|
73
|
+
/** `true` when a fresh value is stored under `key`. */
|
|
74
|
+
has(key: string): Promise<boolean>;
|
|
75
|
+
/**
|
|
76
|
+
* Read a value together with its freshness.
|
|
77
|
+
*
|
|
78
|
+
* `allowStale` is the plugin's `staleWhileRevalidate` switch: when the entry is
|
|
79
|
+
* past its TTL but still resident, it is returned with `stale: true` so the
|
|
80
|
+
* caller can serve it now and refresh afterwards.
|
|
81
|
+
*/
|
|
82
|
+
lookup<T = unknown>(key: string, allowStale?: boolean): Promise<CacheLookup<T> | undefined>;
|
|
83
|
+
/**
|
|
84
|
+
* Store a value in L1 and L2.
|
|
85
|
+
*
|
|
86
|
+
* `ttlSeconds` defaults to the configured TTL and `tags` may be empty; a tag
|
|
87
|
+
* written here is what `@Invalidates("tag")` later purges.
|
|
88
|
+
*/
|
|
89
|
+
set(key: string, value: unknown, ttlSeconds?: number, tags?: readonly string[]): Promise<void>;
|
|
90
|
+
/** Remove one entry from every layer. */
|
|
91
|
+
delete(key: string): Promise<void>;
|
|
92
|
+
/** Remove every entry this manager can see. */
|
|
93
|
+
clear(): Promise<void>;
|
|
94
|
+
/**
|
|
95
|
+
* Purge every entry carrying any of `tags`.
|
|
96
|
+
*
|
|
97
|
+
* Used by `@Invalidates(...)` / `@HitSource(...)` after a successful request.
|
|
98
|
+
* Keys are collected first because deleting mutates the tags they came from.
|
|
99
|
+
*/
|
|
100
|
+
invalidateTags(tags: readonly string[]): Promise<void>;
|
|
101
|
+
/** Alias of {@link clear}, spelled the way `@Invalidates("*")`-style callers expect. */
|
|
102
|
+
invalidateAll(): Promise<void>;
|
|
103
|
+
/** The promise of the request currently being sent for `key`, if any. */
|
|
104
|
+
getInFlight(key: string): Promise<unknown> | undefined;
|
|
105
|
+
/**
|
|
106
|
+
* Publish the promise of the request being sent for `key`.
|
|
107
|
+
*
|
|
108
|
+
* The entry removes itself when the promise settles, so a rejected shared
|
|
109
|
+
* promise cannot be picked up by a later, unrelated request. The rejection is
|
|
110
|
+
* also marked as observed: a leader that fails with no follower waiting would
|
|
111
|
+
* otherwise surface as an unhandled rejection in Node.
|
|
112
|
+
*/
|
|
113
|
+
setInFlight(key: string, promise: Promise<unknown>): void;
|
|
114
|
+
private readL1;
|
|
115
|
+
private readL2;
|
|
116
|
+
private writeL1;
|
|
117
|
+
private writeL2;
|
|
118
|
+
private markExpiry;
|
|
119
|
+
private isStale;
|
|
120
|
+
private rememberTags;
|
|
121
|
+
private forgetKey;
|
|
122
|
+
private warnL2;
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../../src/plugins/cache/manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { AxiosResponse, InternalAxiosRequestConfig } from "axios";
|
|
2
|
+
import type { SnailPluginObject } from "../../typings/plugin.js";
|
|
3
|
+
import { CacheManager } from "./manager.js";
|
|
4
|
+
import type { CacheOptions } from "./type.js";
|
|
5
|
+
/**
|
|
6
|
+
* The cache plugin.
|
|
7
|
+
*
|
|
8
|
+
* ## Where it sits in the pipeline
|
|
9
|
+
*
|
|
10
|
+
* `priority: -100` is the reserved cache band, which buys two things the contract
|
|
11
|
+
* spells out in `docs/guide/plugin-lifecycle.md` §2.2:
|
|
12
|
+
*
|
|
13
|
+
* - **last in forward order** — the interceptor (`100`) and every argument
|
|
14
|
+
* decorator have already run, so `ctx.request` holds the final url, params and
|
|
15
|
+
* body when the key is built. Hashing an earlier shape would key two different
|
|
16
|
+
* requests identically (or one request twice).
|
|
17
|
+
* - **first in unwind order** — the raw envelope is stored before the validation
|
|
18
|
+
* and transformation plugins touch it, so a hit replays exactly what the server
|
|
19
|
+
* sent rather than a processed derivative.
|
|
20
|
+
*
|
|
21
|
+
* ## Stores what the server sent
|
|
22
|
+
*
|
|
23
|
+
* The entry holds `ctx.response.data`, not the `SnailResult`. The result is a
|
|
24
|
+
* projection of the envelope onto the caller's envelope schema; caching it would
|
|
25
|
+
* freeze one server's key names into another's cache.
|
|
26
|
+
*/
|
|
27
|
+
/** Plugin name; also the identity used by `Service.use()` / `Service.remove()`. */
|
|
28
|
+
export declare const CACHE_PLUGIN_NAME = "cache";
|
|
29
|
+
/** The reserved cache priority band (see `docs/guide/plugin-lifecycle.md` §2.1). */
|
|
30
|
+
export declare const CACHE_PRIORITY = -100;
|
|
31
|
+
/** The plugin object plus the manager behind it. */
|
|
32
|
+
export interface CachePlugin extends SnailPluginObject<CacheOptions> {
|
|
33
|
+
/**
|
|
34
|
+
* The storage engine, available once the plugin has been installed by
|
|
35
|
+
* `Service.use(...)` — `install` is where the server name (the default key
|
|
36
|
+
* prefix) and the resolved log level become known.
|
|
37
|
+
*/
|
|
38
|
+
readonly manager: CacheManager | undefined;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Create the cache plugin.
|
|
42
|
+
*
|
|
43
|
+
* ```ts
|
|
44
|
+
* const cache = Cache({ ttl: 30, l2: "localStorage" });
|
|
45
|
+
* Service.use(cache);
|
|
46
|
+
* await cache.manager?.invalidateAll();
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
export declare function Cache(options?: CacheOptions): CachePlugin;
|
|
50
|
+
/**
|
|
51
|
+
* Wrap a cached body into the axios response a plugin must hand back.
|
|
52
|
+
*
|
|
53
|
+
* `headers` is empty on purpose: the stored entry is only the body, and inventing
|
|
54
|
+
* headers would make downstream plugins react to values the server never sent.
|
|
55
|
+
* `config` is the live request, so anything reading `response.config` sees the
|
|
56
|
+
* request that produced the hit.
|
|
57
|
+
*
|
|
58
|
+
* The body is **copied**. Without that, the object handed to the caller would be
|
|
59
|
+
* the very object the cache holds, so one `result.data.name = "x"` would silently
|
|
60
|
+
* rewrite the cache entry — and, worse, a response transformer that hydrates the
|
|
61
|
+
* payload in place would corrupt the stored value for every later hit.
|
|
62
|
+
*/
|
|
63
|
+
export declare function makeCachedResponse<T>(body: T, config: InternalAxiosRequestConfig): AxiosResponse<T>;
|
|
64
|
+
//# sourceMappingURL=plugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../../src/plugins/cache/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,0BAA0B,EAAE,MAAM,OAAO,CAAC;AAOvE,OAAO,KAAK,EAAa,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAIzE,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAEzC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAE3C;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,mFAAmF;AACnF,eAAO,MAAM,iBAAiB,UAAU,CAAC;AAEzC,oFAAoF;AACpF,eAAO,MAAM,cAAc,OAAO,CAAC;AAoBnC,oDAAoD;AACpD,MAAM,WAAW,WAAY,SAAQ,iBAAiB,CAAC,YAAY,CAAC;IAClE;;;;OAIG;IACH,QAAQ,CAAC,OAAO,EAAE,YAAY,GAAG,SAAS,CAAC;CAC5C;AAED;;;;;;;;GAQG;AACH,wBAAgB,KAAK,CAAC,OAAO,CAAC,EAAE,YAAY,GAAG,WAAW,CA4CzD;AA4LD;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,EAClC,IAAI,EAAE,CAAC,EACP,MAAM,EAAE,0BAA0B,GACjC,aAAa,CAAC,CAAC,CAAC,CAQlB"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import type { SnailMethodType } from "../../typings/api.js";
|
|
2
|
+
/**
|
|
3
|
+
* Shared shapes of the cache plugin.
|
|
4
|
+
*
|
|
5
|
+
* The design splits into three layers, each with one job:
|
|
6
|
+
*
|
|
7
|
+
* - a **decorator** says what to do (`@Cacheable`, `@NoCache`, `@Invalidates`),
|
|
8
|
+
* - the **plugin** turns that metadata plus the cache options into a per-request
|
|
9
|
+
* plan, and
|
|
10
|
+
* - a **`CacheManager`** owns storage: L1, optional L2, TTL, LRU and tag index.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* A persistent (or at least outliving) cache store — "L2".
|
|
14
|
+
*
|
|
15
|
+
* Every method is asynchronous on purpose. `localStorage` is synchronous and
|
|
16
|
+
* IndexedDB is not; forcing the synchronous one to pretend does not cost
|
|
17
|
+
* anything, while letting an async store pretend to be synchronous is impossible.
|
|
18
|
+
* IndexedDB therefore needs no special case anywhere in the plugin.
|
|
19
|
+
*
|
|
20
|
+
* An implementation must never throw for a missing environment — the built-in
|
|
21
|
+
* IndexedDB and Web Storage adapters degrade to no-ops — but the manager guards
|
|
22
|
+
* the calls anyway, because a quota error (`QuotaExceededError`) is a normal
|
|
23
|
+
* outcome that must not fail the request.
|
|
24
|
+
*/
|
|
25
|
+
export interface CacheAdapter {
|
|
26
|
+
/** Read a value. `undefined` means "not stored" or "expired". */
|
|
27
|
+
get<T = unknown>(key: string): Promise<T | undefined>;
|
|
28
|
+
/** Store a value. `ttlSeconds <= 0` means "no expiry". */
|
|
29
|
+
set(key: string, value: unknown, ttlSeconds: number): Promise<void>;
|
|
30
|
+
/** Remove one key. Removing an absent key is not an error. */
|
|
31
|
+
delete(key: string): Promise<void>;
|
|
32
|
+
/** Remove everything this adapter owns. */
|
|
33
|
+
clear(): Promise<void>;
|
|
34
|
+
/**
|
|
35
|
+
* Every key currently stored, when the backend can enumerate them.
|
|
36
|
+
*
|
|
37
|
+
* Optional because a custom adapter may front a store that cannot list keys
|
|
38
|
+
* (an HTTP cache, for example). The plugin never requires it; it exists for
|
|
39
|
+
* maintenance tooling and for tests.
|
|
40
|
+
*/
|
|
41
|
+
keys?(): Promise<string[]>;
|
|
42
|
+
}
|
|
43
|
+
/** Options accepted by `Cache(...)`. */
|
|
44
|
+
export interface CacheOptions {
|
|
45
|
+
/**
|
|
46
|
+
* Entry lifetime in seconds. Default 60.
|
|
47
|
+
*
|
|
48
|
+
* Must be positive; a zero, negative or non-finite value falls back to the
|
|
49
|
+
* default rather than silently storing an entry that never expires.
|
|
50
|
+
*/
|
|
51
|
+
ttl?: number;
|
|
52
|
+
/**
|
|
53
|
+
* L1 LRU capacity. Default 100.
|
|
54
|
+
*
|
|
55
|
+
* A non-positive value also means 100 — an unbounded in-memory cache is a leak
|
|
56
|
+
* with extra steps, so it is not reachable by accident.
|
|
57
|
+
*/
|
|
58
|
+
maxSize?: number;
|
|
59
|
+
/** L1 on/off. Default true. */
|
|
60
|
+
l1?: boolean;
|
|
61
|
+
/**
|
|
62
|
+
* L2 store.
|
|
63
|
+
*
|
|
64
|
+
* A string selects a built-in adapter and resolves its global lazily; an
|
|
65
|
+
* object is used as-is. When the selected environment global is missing
|
|
66
|
+
* (Node has neither `localStorage` nor `indexedDB`), the plugin warns once and
|
|
67
|
+
* continues with L1 only instead of throwing.
|
|
68
|
+
*/
|
|
69
|
+
l2?: "localStorage" | "sessionStorage" | "indexedDB" | CacheAdapter;
|
|
70
|
+
/** Which verbs are cacheable. Default `["GET"]`. Accepts `"all"`. */
|
|
71
|
+
cacheFor?: "all" | SnailMethodType | SnailMethodType[];
|
|
72
|
+
/** Key prefix, defaults to the server name. */
|
|
73
|
+
prefix?: string;
|
|
74
|
+
/** Serve a stale entry immediately and refresh in the background. Default false. */
|
|
75
|
+
staleWhileRevalidate?: boolean;
|
|
76
|
+
/** Collapse concurrent identical requests into one. Default true. */
|
|
77
|
+
dedupe?: boolean;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Options accepted by `@Cacheable(...)`.
|
|
81
|
+
*
|
|
82
|
+
* Passing this decorator is an explicit opt-in: a method (or every method of a
|
|
83
|
+
* class) marked with it is cached even for a verb that `cacheFor` does not list.
|
|
84
|
+
* Without it, `cacheFor` decides.
|
|
85
|
+
*/
|
|
86
|
+
export interface CacheableOptions {
|
|
87
|
+
/**
|
|
88
|
+
* Entry lifetime in seconds for this target, overriding `CacheOptions.ttl`.
|
|
89
|
+
*
|
|
90
|
+
* Per-target rather than per-call because the decorator is static metadata: a
|
|
91
|
+
* method that needs a different lifetime for different arguments wants two
|
|
92
|
+
* methods.
|
|
93
|
+
*/
|
|
94
|
+
ttl?: number;
|
|
95
|
+
/**
|
|
96
|
+
* Tags attached to the stored entry, so `@Invalidates("tag")` can purge it
|
|
97
|
+
* without knowing its key.
|
|
98
|
+
*/
|
|
99
|
+
tags?: readonly string[];
|
|
100
|
+
/**
|
|
101
|
+
* Explicit cache key, bypassing the method/url/params/body hash.
|
|
102
|
+
*
|
|
103
|
+
* Every call of that method then shares one entry. Reach for it when the
|
|
104
|
+
* request has volatile parts that must not take part in the identity — a
|
|
105
|
+
* nonce, a timestamp — and accept that those parts are ignored.
|
|
106
|
+
*/
|
|
107
|
+
key?: string;
|
|
108
|
+
}
|
|
109
|
+
/** One cached value plus its freshness, as returned by `CacheManager.lookup`. */
|
|
110
|
+
export interface CacheLookup<T = unknown> {
|
|
111
|
+
/** The stored body. */
|
|
112
|
+
value: T;
|
|
113
|
+
/** `true` when the entry is past its TTL but was kept for `staleWhileRevalidate`. */
|
|
114
|
+
stale: boolean;
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=type.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"type.d.ts","sourceRoot":"","sources":["../../../src/plugins/cache/type.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEzD;;;;;;;;;GASG;AAEH;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,YAAY;IAC3B,iEAAiE;IACjE,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC;IAEtD,0DAA0D;IAC1D,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEpE,8DAA8D;IAC9D,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEnC,2CAA2C;IAC3C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAEvB;;;;;;OAMG;IACH,IAAI,CAAC,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;CAC5B;AAED,wCAAwC;AACxC,MAAM,WAAW,YAAY;IAC3B;;;;;OAKG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,+BAA+B;IAC/B,EAAE,CAAC,EAAE,OAAO,CAAC;IAEb;;;;;;;OAOG;IACH,EAAE,CAAC,EAAE,cAAc,GAAG,gBAAgB,GAAG,WAAW,GAAG,YAAY,CAAC;IAEpE,qEAAqE;IACrE,QAAQ,CAAC,EAAE,KAAK,GAAG,eAAe,GAAG,eAAe,EAAE,CAAC;IAEvD,+CAA+C;IAC/C,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB,oFAAoF;IACpF,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAE/B,qEAAqE;IACrE,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;;;;;OAMG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb;;;OAGG;IACH,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAEzB;;;;;;OAMG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,iFAAiF;AACjF,MAAM,WAAW,WAAW,CAAC,CAAC,GAAG,OAAO;IACtC,uBAAuB;IACvB,KAAK,EAAE,CAAC,CAAC;IAET,qFAAqF;IACrF,KAAK,EAAE,OAAO,CAAC;CAChB"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@snail-js/api/plugins` — the built-in optional plugins.
|
|
3
|
+
*
|
|
4
|
+
* ```ts
|
|
5
|
+
* import {
|
|
6
|
+
* Cache,
|
|
7
|
+
* Cacheable,
|
|
8
|
+
* Invalidates,
|
|
9
|
+
* Interceptor,
|
|
10
|
+
* BeforeRequest,
|
|
11
|
+
* Versioning,
|
|
12
|
+
* Version,
|
|
13
|
+
* Validate,
|
|
14
|
+
* Transform
|
|
15
|
+
* } from "@snail-js/api/plugins";
|
|
16
|
+
*
|
|
17
|
+
* Service
|
|
18
|
+
* .use(Interceptor())
|
|
19
|
+
* .use(Versioning({ type: "header", defaultVersion: "1.0.0" }))
|
|
20
|
+
* .use(Validate())
|
|
21
|
+
* .use(Transform())
|
|
22
|
+
* .use(Cache({ ttl: 60, l2: "localStorage" }));
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* ## Why this is a separate entry point
|
|
26
|
+
*
|
|
27
|
+
* Nothing here is in the core. Keeping the plugins behind `@snail-js/api/plugins`
|
|
28
|
+
* means an application that imports only the root entry pays for none of it — no
|
|
29
|
+
* cache, no validation and no JSON→class hydration. Each plugin lives in its own
|
|
30
|
+
* directory with its own barrel, so a bundler can drop the ones a given app never
|
|
31
|
+
* calls.
|
|
32
|
+
*
|
|
33
|
+
* ## The framework adapters are *not* here
|
|
34
|
+
*
|
|
35
|
+
* `VueAdapter` and `ReactAdapter` live behind their own subpaths:
|
|
36
|
+
*
|
|
37
|
+
* ```ts
|
|
38
|
+
* import { VueAdapter } from "@snail-js/api/plugins/vue";
|
|
39
|
+
* import { ReactAdapter } from "@snail-js/api/plugins/react";
|
|
40
|
+
* ```
|
|
41
|
+
*
|
|
42
|
+
* They are kept out of this barrel deliberately. A re-export would make this
|
|
43
|
+
* module statically import both `vue` and `react`, so a React application that
|
|
44
|
+
* only wanted `Cache` would fail to resolve `vue` at all — and a framework-free
|
|
45
|
+
* one would pull both frameworks into its bundle. Separate subpaths keep the
|
|
46
|
+
* optional peers genuinely optional.
|
|
47
|
+
*
|
|
48
|
+
* ## Registration order does not matter
|
|
49
|
+
*
|
|
50
|
+
* Plugins are ordered by `priority`, not by the order `use()` was called in:
|
|
51
|
+
*
|
|
52
|
+
* | Priority | Plugin |
|
|
53
|
+
* | --- | --- |
|
|
54
|
+
* | `100` | interceptor |
|
|
55
|
+
* | `50` | versioning |
|
|
56
|
+
* | `0` | framework adapter, transform |
|
|
57
|
+
* | `-50` | validate |
|
|
58
|
+
* | `-100` | cache |
|
|
59
|
+
* | `-150` | request pool |
|
|
60
|
+
*
|
|
61
|
+
* Forward hooks run highest-priority first, so the interceptor sees the request
|
|
62
|
+
* before the cache hashes it and the pool — dead last — only ever gates requests
|
|
63
|
+
* the cache could not answer. Unwind hooks run in the opposite direction, so the
|
|
64
|
+
* cache stores the raw envelope before validation and transformation touch it.
|
|
65
|
+
* See `docs/guide/plugin-lifecycle.md` §2.1 for the reasoning.
|
|
66
|
+
*
|
|
67
|
+
* @packageDocumentation
|
|
68
|
+
*/
|
|
69
|
+
export * from "./cache/index.js";
|
|
70
|
+
export * from "./interceptor/index.js";
|
|
71
|
+
export * from "./pool/index.js";
|
|
72
|
+
export * from "./transform/index.js";
|
|
73
|
+
export * from "./validate/index.js";
|
|
74
|
+
export * from "./version/index.js";
|
|
75
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/plugins/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmEG;AAEH,cAAc,SAAS,CAAC;AACxB,cAAc,eAAe,CAAC;AAC9B,cAAc,QAAQ,CAAC;AACvB,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC"}
|