@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 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/plugins/cache/adapters/indexeddb.ts","../../src/plugins/cache/adapters/memory.ts","../../src/plugins/cache/adapters/web-storage.ts","../../src/plugins/cache/decorators.ts","../../src/plugins/cache/key.ts","../../src/plugins/cache/manager.ts","../../src/plugins/cache/plugin.ts","../../src/plugins/interceptor/decorators.ts","../../src/plugins/interceptor/manager.ts","../../src/plugins/interceptor/plugin.ts","../../src/plugins/pool/type.ts","../../src/plugins/pool/scheduler.ts","../../src/plugins/pool/plugin.ts","../../src/plugins/transform/decorators.ts","../../src/plugins/transform/hydrate.ts","../../src/plugins/transform/plugin.ts","../../src/plugins/transform/index.ts","../../src/plugins/validate/decorators.ts","../../src/plugins/validate/type.ts","../../src/plugins/validate/plugin.ts","../../src/plugins/validate/index.ts","../../src/plugins/version/decorators.ts","../../src/plugins/version/plugin.ts"],"sourcesContent":["import type { CacheAdapter } from \"../type\";\n\n/** One persisted record, mirroring the in-memory shape. */\ninterface StoredRecord {\n value: unknown;\n /** Absolute ms timestamp; `0` means \"never expires\". */\n expiresAt: number;\n}\n\n/** Options accepted by {@link IndexedDBCacheAdapter}. */\nexport interface IndexedDBCacheAdapterOptions {\n /** Database name. Defaults to `\"snail-js-api\"`. */\n databaseName?: string;\n\n /** Object store name, and therefore the namespace. Defaults to `\"cache\"`. */\n storeName?: string;\n\n /** Schema version, bumped when the store layout changes. */\n version?: number;\n}\n\n/**\n * L2 adapter over IndexedDB.\n *\n * ## Why every method degrades instead of rejecting\n *\n * IndexedDB is absent in Node and can be unavailable in private browsing modes.\n * An L2 store is an optimisation: a request must succeed (and stay correct) when\n * it is missing. Every public method therefore resolves to \"nothing stored\" or a\n * no-op rather than throwing, and the connection is opened lazily so importing\n * this module — or constructing the adapter — never touches a global.\n *\n * ## Why the timeout is stored per record, not as an index\n *\n * A TTL index would make reads of an expired key need a second query anyway. The\n * record carries its own `expiresAt`, which makes the freshness check a pure\n * in-memory comparison on the value already fetched.\n */\nexport class IndexedDBCacheAdapter implements CacheAdapter {\n private readonly databaseName: string;\n private readonly storeName: string;\n private readonly version: number;\n\n private database: IDBDatabase | undefined;\n private opening: Promise<IDBDatabase> | undefined;\n\n constructor(options: IndexedDBCacheAdapterOptions = {}) {\n this.databaseName = options.databaseName ?? \"snail-js-api\";\n this.storeName = options.storeName ?? \"cache\";\n this.version = options.version ?? 1;\n }\n\n /** `false` when the environment has no IndexedDB at all. */\n get available(): boolean {\n try {\n return typeof globalThis.indexedDB !== \"undefined\";\n } catch {\n return false;\n }\n }\n\n async get<T = unknown>(key: string): Promise<T | undefined> {\n try {\n const database = await this.open();\n const record = await this.read(database, key);\n if (record === undefined) return undefined;\n\n if (record.expiresAt !== 0 && record.expiresAt <= Date.now()) {\n await this.delete(key);\n return undefined;\n }\n\n return record.value as T;\n } catch {\n return undefined;\n }\n }\n\n async set(key: string, value: unknown, ttlSeconds: number): Promise<void> {\n // A missing global is an environment fact, not a failure: no-op so a Node\n // test can construct and exercise the adapter. A *real* write error (quota,\n // aborted transaction) propagates — the manager logs it and the request\n // still succeeds.\n if (!this.available) return;\n\n const database = await this.open();\n const record: StoredRecord = {\n value,\n expiresAt: ttlSeconds > 0 ? Date.now() + ttlSeconds * 1000 : 0\n };\n\n await this.transaction(database, \"readwrite\", (store) => {\n store.put(record, key);\n });\n }\n\n async delete(key: string): Promise<void> {\n try {\n const database = await this.open();\n await this.transaction(database, \"readwrite\", (store) => {\n store.delete(key);\n });\n } catch {\n // Removing something from a store that does not exist is not a failure.\n }\n }\n\n async clear(): Promise<void> {\n try {\n const database = await this.open();\n await this.transaction(database, \"readwrite\", (store) => {\n store.clear();\n });\n } catch {\n /* no store to clear */\n }\n }\n\n async keys(): Promise<string[]> {\n try {\n const database = await this.open();\n const transaction = database.transaction(this.storeName, \"readonly\");\n const request = transaction.objectStore(this.storeName).getAllKeys();\n const keys = await requestToPromise<IDBValidKey[]>(request);\n return keys.map(String);\n } catch {\n return [];\n }\n }\n\n // ── internals ─────────────────────────────────────────────────────────────\n\n /**\n * Open (once) and memoise the database connection.\n *\n * The in-flight promise is stored before it settles, so two concurrent\n * requests cannot open the same database twice and leak a connection.\n */\n private open(): Promise<IDBDatabase> {\n if (this.database) return Promise.resolve(this.database);\n if (this.opening) return this.opening;\n\n this.opening = new Promise<IDBDatabase>((resolve, reject) => {\n const factory = globalThis.indexedDB;\n if (!factory) {\n reject(new Error(\"[snail] IndexedDB is not available in this environment\"));\n return;\n }\n\n const request = factory.open(this.databaseName, this.version);\n\n request.onupgradeneeded = () => {\n const database = request.result;\n if (!database.objectStoreNames.contains(this.storeName)) {\n database.createObjectStore(this.storeName);\n }\n };\n request.onsuccess = () => {\n this.database = request.result;\n resolve(request.result);\n };\n request.onerror = () => reject(request.error);\n request.onblocked = () => reject(new Error(\"[snail] IndexedDB upgrade is blocked\"));\n }).finally(() => {\n // A failed open must be retried on the next call rather than cached.\n this.opening = undefined;\n });\n\n return this.opening;\n }\n\n private async read(database: IDBDatabase, key: string): Promise<StoredRecord | undefined> {\n const transaction = database.transaction(this.storeName, \"readonly\");\n const request = transaction.objectStore(this.storeName).get(key);\n const value = await requestToPromise<StoredRecord | undefined>(request);\n if (value === undefined || value === null || typeof value !== \"object\") return undefined;\n if (!(\"value\" in value) || !(\"expiresAt\" in value)) return undefined;\n return value;\n }\n\n private transaction(\n database: IDBDatabase,\n mode: IDBTransactionMode,\n run: (store: IDBObjectStore) => void\n ): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n const transaction = database.transaction(this.storeName, mode);\n run(transaction.objectStore(this.storeName));\n transaction.oncomplete = () => resolve();\n transaction.onerror = () => reject(transaction.error);\n transaction.onabort = () => reject(transaction.error);\n });\n }\n}\n\n/** Bridge a one-shot `IDBRequest` into a promise. */\nfunction requestToPromise<T>(request: IDBRequest<T>): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n request.onsuccess = () => resolve(request.result);\n request.onerror = () => reject(request.error);\n });\n}\n","import type { CacheAdapter } from \"../type\";\n\n/** Options accepted by {@link MemoryCacheAdapter}. */\nexport interface MemoryCacheAdapterOptions {\n /**\n * Maximum number of live entries. Least-recently-used entries are evicted\n * first.\n *\n * A non-positive or non-finite value means {@link DEFAULT_L1_MAX_SIZE}: an\n * unbounded in-memory store is a leak, so it must not be reachable by accident.\n */\n maxSize?: number;\n\n /**\n * Called whenever an entry leaves the store without the owner asking —\n * expiry or capacity eviction.\n *\n * The `CacheManager` uses it to drop the key from its tag index and TTL map;\n * without it, evicted keys would keep their tags forever and\n * `invalidateTags` would keep firing `delete` calls for keys that are gone.\n */\n onEvict?: (key: string) => void;\n}\n\n/** L1 capacity used whenever the configured one is missing or not positive. */\nexport const DEFAULT_L1_MAX_SIZE = 100;\n\n/** One stored value plus the absolute timestamp it stops being fresh. */\ninterface MemoryRecord {\n value: unknown;\n /** `0` means \"never expires\". */\n expiresAt: number;\n}\n\n/**\n * The L1 store: a `Map` with TTL and LRU capacity.\n *\n * ## Why there is no sweep timer\n *\n * The pre-rewrite memory adapter ran a `setInterval` that (a) kept a Node\n * process alive forever and (b) made tests hang until vitest force-killed them.\n * Expiry is therefore swept *lazily*, at the start of every operation: at most\n * `maxSize` records are inspected, which is cheaper than the timer it replaces\n * for any realistic capacity, and it cannot outlive the store.\n *\n * ## Why a `Map` and not a `WeakMap`\n *\n * LRU needs enumeration and ordering. `Map` preserves insertion order, so\n * \"least recently used\" is simply \"first key\" and touching an entry is\n * delete + set — no linked list to keep in sync with the data.\n */\nexport class MemoryCacheAdapter implements CacheAdapter {\n private readonly records = new Map<string, MemoryRecord>();\n private readonly maxSize: number;\n private readonly onEvict: ((key: string) => void) | undefined;\n\n constructor(options: MemoryCacheAdapterOptions = {}) {\n const maxSize = options.maxSize;\n this.maxSize =\n typeof maxSize === \"number\" && Number.isFinite(maxSize) && maxSize > 0\n ? Math.floor(maxSize)\n : DEFAULT_L1_MAX_SIZE;\n this.onEvict = options.onEvict;\n }\n\n /** Number of live entries. */\n get size(): number {\n this.sweep();\n return this.records.size;\n }\n\n /** `true` when the key is present and still fresh. */\n async has(key: string): Promise<boolean> {\n return (await this.get(key)) !== undefined;\n }\n\n async get<T = unknown>(key: string): Promise<T | undefined> {\n this.sweep();\n\n const record = this.records.get(key);\n if (!record) return undefined;\n\n // Re-inserting moves the key to the end of the iteration order, which is\n // what makes the eviction above least-recently-*used* rather than FIFO.\n this.records.delete(key);\n this.records.set(key, record);\n\n return record.value as T;\n }\n\n async set(key: string, value: unknown, ttlSeconds: number): Promise<void> {\n this.sweep();\n\n // Delete first so an update also counts as a \"most recent use\".\n const existed = this.records.delete(key);\n if (!existed) this.evictForCapacity(1);\n\n this.records.set(key, {\n value,\n expiresAt: ttlSeconds > 0 ? Date.now() + ttlSeconds * 1000 : 0\n });\n }\n\n async delete(key: string): Promise<void> {\n this.records.delete(key);\n }\n\n async clear(): Promise<void> {\n this.records.clear();\n }\n\n async keys(): Promise<string[]> {\n this.sweep();\n return [...this.records.keys()];\n }\n\n /** Drop expired records. Called before every operation, never on a timer. */\n private sweep(): void {\n const now = Date.now();\n for (const [key, record] of this.records) {\n if (record.expiresAt !== 0 && record.expiresAt <= now) {\n this.records.delete(key);\n this.onEvict?.(key);\n }\n }\n }\n\n /** Make room for `incoming` new entries by dropping the oldest ones. */\n private evictForCapacity(incoming: number): void {\n while (this.records.size + incoming > this.maxSize) {\n const oldest = this.records.keys().next();\n if (oldest.done) return;\n this.records.delete(oldest.value);\n this.onEvict?.(oldest.value);\n }\n }\n}\n","import type { CacheAdapter } from \"../type\";\n\n/** One persisted record, mirroring the in-memory shape. */\ninterface StoredRecord {\n value: unknown;\n /** Absolute ms timestamp; `0` means \"never expires\". */\n expiresAt: number;\n}\n\n/** Options accepted by {@link WebStorageCacheAdapter}. */\nexport interface WebStorageCacheAdapterOptions {\n /** Prefix applied to every key, so two adapters can share one storage area. */\n prefix?: string;\n\n /** Name used in diagnostics, e.g. `\"localStorage\"`. */\n label?: string;\n}\n\n/**\n * L2 adapter over any Web Storage area.\n *\n * One class drives both `localStorage` and `sessionStorage`: they share the\n * `Storage` interface, and wrapping them twice would duplicate every guard below.\n *\n * ## Why the area is resolved through a getter\n *\n * `localStorage` does not exist in Node, and in a browser it *throws* on access\n * when the user has blocked site data. Reading it in the constructor would make\n * `import`-time behaviour environment-dependent — and a Node test could not even\n * construct the adapter to assert that it degrades. The getter is therefore\n * called per operation and its failure is swallowed; the store behaves as an\n * always-empty cache, which the manager turns into \"L1 only\".\n */\nexport class WebStorageCacheAdapter implements CacheAdapter {\n private readonly resolveStorage: () => Storage | undefined;\n private readonly prefix: string;\n\n /** Name used in diagnostics. */\n readonly label: string;\n\n constructor(\n resolveStorage: () => Storage | undefined,\n options: WebStorageCacheAdapterOptions = {}\n ) {\n this.resolveStorage = resolveStorage;\n this.prefix = options.prefix ?? \"[snail-cache]\";\n this.label = options.label ?? \"web storage\";\n }\n\n /** `false` when the backing area is missing — the manager then drops L2. */\n get available(): boolean {\n return this.storage() !== undefined;\n }\n\n async get<T = unknown>(key: string): Promise<T | undefined> {\n const storage = this.storage();\n if (!storage) return undefined;\n\n const raw = storage.getItem(this.key(key));\n if (raw === null) return undefined;\n\n const record = this.parse(raw);\n if (record === undefined) {\n // Someone else wrote that key, or the JSON is corrupt. Removing it keeps a\n // broken entry from being re-parsed on every request.\n storage.removeItem(this.key(key));\n return undefined;\n }\n\n if (record.expiresAt !== 0 && record.expiresAt <= Date.now()) {\n storage.removeItem(this.key(key));\n return undefined;\n }\n\n return record.value as T;\n }\n\n async set(key: string, value: unknown, ttlSeconds: number): Promise<void> {\n const storage = this.storage();\n if (!storage) return;\n\n const record: StoredRecord = {\n value,\n expiresAt: ttlSeconds > 0 ? Date.now() + ttlSeconds * 1000 : 0\n };\n\n // Let a quota error propagate: the manager logs it and continues, which is\n // the only place that knows the request must not fail because of it.\n storage.setItem(this.key(key), JSON.stringify(record));\n }\n\n async delete(key: string): Promise<void> {\n this.storage()?.removeItem(this.key(key));\n }\n\n async clear(): Promise<void> {\n const storage = this.storage();\n if (!storage) return;\n\n for (const key of this.keysOf(storage)) {\n storage.removeItem(key);\n }\n }\n\n async keys(): Promise<string[]> {\n const storage = this.storage();\n if (!storage) return [];\n\n return this.keysOf(storage).map((key) => key.slice(this.prefix.length));\n }\n\n private storage(): Storage | undefined {\n try {\n return this.resolveStorage();\n } catch {\n // Accessing `localStorage` can throw a SecurityError in a locked-down\n // browser; that is a missing area, not a crash.\n return undefined;\n }\n }\n\n private key(key: string): string {\n return `${this.prefix}${key}`;\n }\n\n /** Every storage key this adapter owns, prefix included. */\n private keysOf(storage: Storage): string[] {\n const owned: string[] = [];\n for (let index = 0; index < storage.length; index++) {\n const key = storage.key(index);\n if (key !== null && key.startsWith(this.prefix)) owned.push(key);\n }\n return owned;\n }\n\n private parse(raw: string): StoredRecord | undefined {\n try {\n const parsed = JSON.parse(raw) as StoredRecord | null;\n if (parsed === null || typeof parsed !== \"object\") return undefined;\n if (!(\"value\" in parsed) || !(\"expiresAt\" in parsed)) return undefined;\n return parsed;\n } catch {\n return undefined;\n }\n }\n}\n","import {\n createClassDecorator,\n createMethodDecorator,\n getClassMetadata,\n getMethodMetadata\n} from \"../../decorators/custom\";\nimport { SnailDecoratorError } from \"../../error\";\nimport type { CacheableOptions } from \"./type\";\n\n/**\n * `@Cacheable()` / `@NoCache()` / `@Invalidates()` / `@HitSource()` — the\n * declarative half of the cache plugin.\n *\n * All four work on a class and on a method. TypeScript distinguishes the two by\n * arity (a class decorator is called with the constructor alone), which is the\n * only signal available now that `emitDecoratorMetadata` emits nothing in TS 7.\n *\n * ## Why `@Invalidates` appends instead of replacing\n *\n * `@Invalidates(\"users\", \"orders\")` is two decorator applications on one target.\n * Using the merging factory means the reader gets `[\"users\", \"orders\"]` without\n * the plugin having to flatten a nested array — and two separate\n * `@Invalidates()` lines compose the same way, which is how a method that\n * touches several caches is naturally written.\n */\n\nconst CACHEABLE_KEY = \"snail-cache/cacheable\";\nconst NO_CACHE_KEY = \"snail-cache/disabled\";\nconst INVALIDATES_KEY = \"snail-cache/invalidates\";\n\ntype DualDecorator = ClassDecorator & MethodDecorator;\n\n// `merge = false` for the single-value keys: applying `@Cacheable()` twice to one\n// target should not produce a list the reader has to reconcile.\nconst addCacheableOnClass = createClassDecorator<CacheableOptions>(CACHEABLE_KEY, false);\nconst addCacheableOnMethod = createMethodDecorator<CacheableOptions>(CACHEABLE_KEY, false);\nconst addNoCacheOnClass = createClassDecorator<boolean>(NO_CACHE_KEY, false);\nconst addNoCacheOnMethod = createMethodDecorator<boolean>(NO_CACHE_KEY, false);\n\n// `merge = true` for tags: one application per tag, accumulated in order.\nconst addInvalidatesOnClass = createClassDecorator<string>(INVALIDATES_KEY);\nconst addInvalidatesOnMethod = createMethodDecorator<string>(INVALIDATES_KEY);\n\n/** Fold a class application and a method application into one decorator. */\nfunction dualDecorator(applyToClass: ClassDecorator, applyToMethod: MethodDecorator): DualDecorator {\n return ((target: any, propertyKey?: string | symbol) => {\n if (propertyKey === undefined) {\n applyToClass(target);\n return;\n }\n applyToMethod(target, propertyKey, undefined as never);\n }) as DualDecorator;\n}\n\n/** Reject the misuse that would otherwise only surface at request time. */\nfunction assertCacheableOptions(options: CacheableOptions): void {\n if (options.ttl !== undefined && !(typeof options.ttl === \"number\" && options.ttl > 0)) {\n throw new SnailDecoratorError(\n \"[snail] @Cacheable({ ttl }) must be a positive number of seconds\"\n );\n }\n\n if (options.key !== undefined && (typeof options.key !== \"string\" || options.key.length === 0)) {\n throw new SnailDecoratorError(\"[snail] @Cacheable({ key }) must be a non-empty string\");\n }\n\n if (options.tags !== undefined) {\n if (!Array.isArray(options.tags)) {\n throw new SnailDecoratorError(\"[snail] @Cacheable({ tags }) must be an array of strings\");\n }\n for (const tag of options.tags) {\n if (typeof tag !== \"string\" || tag.length === 0) {\n throw new SnailDecoratorError(\"[snail] @Cacheable({ tags }) must contain non-empty strings\");\n }\n }\n }\n}\n\n/**\n * Mark this method — or every method of this class — as cacheable.\n *\n * Passing the decorator is an explicit opt-in: it caches even a verb that\n * `CacheOptions.cacheFor` does not list. Without it, `cacheFor` decides, and the\n * reason is that \"cache every POST of this class\" is far too easy to write by\n * accident when the intent was only to attach tags.\n *\n * ```ts\n * @Api(\"/user\")\n * @Cacheable({ tags: [\"users\"] })\n * class UserApi {\n * @Get(\"/\") list(): Promise<User[]> { return null!; }\n *\n * @Get(\"/stats\")\n * @Cacheable({ ttl: 5 })\n * stats(): Promise<Stats> { return null!; }\n * }\n * ```\n */\nexport function Cacheable(options: CacheableOptions = {}): DualDecorator {\n assertCacheableOptions(options);\n\n const value: CacheableOptions = {\n ttl: options.ttl,\n key: options.key,\n tags: options.tags === undefined ? undefined : [...options.tags]\n };\n\n return dualDecorator(addCacheableOnClass(value), addCacheableOnMethod(value));\n}\n\n/**\n * Opt this method — or this entire class — out of caching.\n *\n * A method-level `@NoCache()` beats a class-level `@Cacheable()`, and a\n * class-level `@NoCache()` beats the `cacheFor` default. Opting out is the one\n * decision that may never be overridden by a broader rule, because the cost of\n * being wrong is serving stale data.\n */\nexport function NoCache(): DualDecorator {\n return dualDecorator(addNoCacheOnClass(true), addNoCacheOnMethod(true));\n}\n\n/**\n * Purge every cached entry carrying any of `tags` once this request succeeds.\n *\n * \"Succeeds\" means the HTTP round-trip did: the hook lives on the response path,\n * so a failed or cancelled request invalidates nothing. A method may invalidate a\n * tag it also stores under — the plugin purges before it stores, so the fresh\n * entry survives its own invalidation.\n */\nexport function Invalidates(...tags: string[]): DualDecorator {\n for (const tag of tags) {\n if (typeof tag !== \"string\" || tag.length === 0) {\n throw new SnailDecoratorError(\"[snail] @Invalidates(...tags) needs non-empty strings\");\n }\n }\n\n // One decorator application per tag, so the stored metadata stays a flat\n // `string[]` instead of an array of arrays the reader would have to flatten.\n return ((target: any, propertyKey?: string | symbol) => {\n for (const tag of tags) {\n if (propertyKey === undefined) {\n addInvalidatesOnClass(tag)(target);\n } else {\n addInvalidatesOnMethod(tag)(target, propertyKey, undefined as never);\n }\n }\n }) as DualDecorator;\n}\n\n/**\n * Legacy-compatible alias of {@link Invalidates} for a single source name.\n *\n * The pre-rewrite decorator was named after the *source* of a change rather than\n * the entries it purges; application code written against it keeps working, and\n * new code should prefer `@Invalidates`.\n */\nexport function HitSource(name: string): DualDecorator {\n return Invalidates(name);\n}\n\n// ── readers ─────────────────────────────────────────────────────────────────\n\n/**\n * Read `@Cacheable(...)` from a method (when `methodName` is given) or a class.\n *\n * `undefined` means \"not marked\", which is different from `{}` (\"marked, with no\n * overrides\") — the plugin treats the two differently.\n */\nexport function readCacheable(target: unknown, methodName?: string): CacheableOptions | undefined {\n return methodName === undefined\n ? getClassMetadata<CacheableOptions>(CACHEABLE_KEY, target)\n : getMethodMetadata<CacheableOptions>(CACHEABLE_KEY, target, methodName);\n}\n\n/** `true` when `@NoCache()` was applied to this method or class. */\nexport function readNoCache(target: unknown, methodName?: string): boolean {\n const value =\n methodName === undefined\n ? getClassMetadata<boolean>(NO_CACHE_KEY, target)\n : getMethodMetadata<boolean>(NO_CACHE_KEY, target, methodName);\n\n return value === true;\n}\n\n/** Tags listed by `@Invalidates(...)` / `@HitSource(...)`, in application order. */\nexport function readInvalidates(target: unknown, methodName?: string): string[] {\n return (\n (methodName === undefined\n ? getClassMetadata<string[]>(INVALIDATES_KEY, target)\n : getMethodMetadata<string[]>(INVALIDATES_KEY, target, methodName)) ?? []\n );\n}\n","import type { InternalAxiosRequestConfig } from \"axios\";\nimport type { SnailMethodType } from \"../../typings/api\";\nimport { shortHash, stableStringify } from \"../../utils\";\n\n/**\n * Cache-key construction.\n *\n * ## Why the whole request identity is hashed\n *\n * A key has to answer one question: \"would this request produce the same\n * response as the one I already stored?\". That depends on the verb, the final\n * url, the query params and the body — and on nothing else. Headers are\n * deliberately excluded: an `Authorization` header changes *who* asks, not what\n * is asked, and including it would turn every token refresh into a cache flush.\n *\n * ## Why `stableStringify`\n *\n * `{ a: 1, b: 2 }` and `{ b: 2, a: 1 }` are the same query. Serialising them\n * with `JSON.stringify` produces two different strings and therefore two cache\n * entries for one logical request; `stableStringify` sorts object keys, so the\n * hash — and the key — is order-independent.\n *\n * ## Why the url is final here\n *\n * The cache plugin runs at `priority: -100` — last in forward order — precisely\n * so the interceptor plugin (`100`) and the argument decorators have already\n * rewritten `ctx.request.url` and filled in `:placeholders`. Hashing an earlier\n * stage of the url would key `/user/:id` and `/user/42` differently, or worse,\n * identically.\n */\n\n/** Everything needed to identify one cacheable request. */\nexport interface CacheKeyInput {\n /** Namespace, so two servers on one origin never share entries. */\n prefix: string;\n\n /** The live axios config — url, params and body must already be final. */\n request: InternalAxiosRequestConfig;\n\n /** Fallback verb when the config carries none. */\n methodType: SnailMethodType;\n\n /** `@Cacheable({ key })`: use this verbatim instead of the request identity. */\n explicitKey?: string;\n}\n\n/**\n * Build the key used for one request.\n *\n * The readable part (`prefix:VERB`) is kept outside the hash so a misbehaving\n * cache can be diagnosed from a key alone; only the volatile signature is hashed,\n * which keeps keys short enough for `localStorage`.\n */\nexport function buildCacheKey(input: CacheKeyInput): string {\n const { prefix, request, methodType, explicitKey } = input;\n\n // An explicit key replaces the identity rather than extending it: that is the\n // entire point of declaring one.\n if (explicitKey !== undefined && explicitKey.length > 0) {\n return `${prefix}:${explicitKey}`;\n }\n\n const method = (request.method ?? methodType).toUpperCase();\n const signature = stableStringify({\n method,\n baseURL: request.baseURL ?? \"\",\n url: request.url ?? \"\",\n params: request.params ?? null,\n data: request.data ?? null\n });\n\n return `${prefix}:${method}:${shortHash(signature)}`;\n}\n","import type { SnailLogger } from \"../../core/logger\";\nimport { createLogger } from \"../../core/logger\";\nimport { SnailPluginError } from \"../../error\";\nimport { t } from \"../../locale\";\nimport type { SnailMethodType } from \"../../typings/api\";\nimport { IndexedDBCacheAdapter } from \"./adapters/indexeddb\";\nimport { DEFAULT_L1_MAX_SIZE, MemoryCacheAdapter } from \"./adapters/memory\";\nimport { WebStorageCacheAdapter } from \"./adapters/web-storage\";\nimport type { CacheAdapter, CacheLookup, CacheOptions } from \"./type\";\n\nexport interface CacheManagerOptions extends CacheOptions {\n /**\n * Logger for L2 failures.\n *\n * The manager runs outside any request, so it has no `ctx.logger`; the plugin\n * injects one built from `@Server({ logLevel })`. Defaults to silent, because a\n * cache must not make an application noisy by itself.\n */\n logger?: SnailLogger;\n}\n\n/** `CacheOptions` with every default applied — the manager's public state. */\nexport interface ResolvedCacheOptions {\n ttl: number;\n maxSize: number;\n l1: boolean;\n l2: CacheAdapter | undefined;\n cacheFor: \"all\" | readonly SnailMethodType[];\n prefix: string;\n staleWhileRevalidate: boolean;\n dedupe: boolean;\n}\n\n/** Probe result that can tell \"stored undefined\" from \"nothing stored\". */\ntype Probe<T> = { found: true; value: T } | { found: false };\n\n/** Verbs cached when `cacheFor` is not configured. */\nconst DEFAULT_CACHE_FOR: readonly SnailMethodType[] = [\"GET\"];\n\n/**\n * Storage engine behind the cache plugin.\n *\n * ## Layers\n *\n * - **L1** is a {@link MemoryCacheAdapter}: always present unless `l1: false`,\n * bounded by `maxSize`, and the only place an L2 hit is promoted into.\n * - **L2** is any {@link CacheAdapter}, usually a persistent store.\n *\n * ## What the manager owns that the adapters do not\n *\n * TTL, LRU and tags are policy, not storage. Keeping them here means a custom L2\n * adapter only has to answer \"get/set/delete\" and never has to know what a tag\n * is — which is what makes the adapter interface small enough to implement over\n * anything (IndexedDB, `localStorage`, an HTTP cache).\n *\n * ## Freshness vs. `staleWhileRevalidate`\n *\n * Freshness is always decided by this class, using the `expiresAt` it records on\n * `set`. In stale-while-revalidate mode L1 is told \"never expire\" so the stale\n * copy survives to be served while the plugin refreshes it; otherwise L1 is\n * given the real TTL and sweeps the entry itself. L2 always receives the real TTL\n * so a second tab (which has no shared in-memory index) never reads a stale one.\n */\nexport class CacheManager {\n /** Fully resolved options, exposed so the plugin can apply the same policy. */\n readonly options: ResolvedCacheOptions;\n\n private readonly l1: MemoryCacheAdapter | undefined;\n private readonly logger: SnailLogger;\n\n /** key → absolute expiry timestamp in ms; `0` means \"never expires\". */\n private readonly expiry = new Map<string, number>();\n\n /** tag → every key carrying it. */\n private readonly tagIndex = new Map<string, Set<string>>();\n\n /** key → every tag it carries, so a delete can unindex it in O(tags). */\n private readonly keyTags = new Map<string, Set<string>>();\n\n /** Cache key → the promise of the request currently being sent for it. */\n private readonly flight = new Map<string, Promise<unknown>>();\n\n constructor(options: CacheManagerOptions = {}) {\n this.logger = options.logger ?? createLogger(\"silent\");\n this.options = resolveOptions(options, this.logger);\n\n this.l1 = this.options.l1\n ? new MemoryCacheAdapter({\n maxSize: this.options.maxSize,\n // Evictions the manager did not initiate must still unindex the key.\n onEvict: (key) => this.forgetKey(key)\n })\n : undefined;\n }\n\n /** Number of live **L1** entries. L2 is not enumerated, by design. */\n get size(): number {\n return this.l1?.size ?? 0;\n }\n\n /**\n * Read a fresh value.\n *\n * A stale entry (kept alive only for stale-while-revalidate) is *not* returned:\n * callers that must serve staleness ask {@link lookup} explicitly, so the\n * common path cannot accidentally serve expired data.\n */\n async get<T = unknown>(key: string): Promise<T | undefined> {\n const found = await this.lookup<T>(key);\n return found?.value;\n }\n\n /** `true` when a fresh value is stored under `key`. */\n async has(key: string): Promise<boolean> {\n return (await this.get(key)) !== undefined;\n }\n\n /**\n * Read a value together with its freshness.\n *\n * `allowStale` is the plugin's `staleWhileRevalidate` switch: when the entry is\n * past its TTL but still resident, it is returned with `stale: true` so the\n * caller can serve it now and refresh afterwards.\n */\n async lookup<T = unknown>(key: string, allowStale = false): Promise<CacheLookup<T> | undefined> {\n const fromL1 = await this.readL1<T>(key);\n if (fromL1.found) {\n const stale = this.isStale(key);\n if (!stale || allowStale) return { value: fromL1.value, stale };\n }\n\n const fromL2 = await this.readL2<T>(key);\n if (!fromL2.found) return undefined;\n\n // Promote into L1 so the next hit never pays for L2 again. The remaining L2\n // lifetime is not observable through the adapter contract, so the manager's\n // own TTL is used — a best effort, and never longer than the L2 entry can be\n // assumed fresh.\n this.markExpiry(key, this.options.ttl);\n const l1Ttl = this.options.staleWhileRevalidate ? 0 : this.options.ttl;\n await this.writeL1(key, fromL2.value, l1Ttl);\n\n return { value: fromL2.value, stale: false };\n }\n\n /**\n * Store a value in L1 and L2.\n *\n * `ttlSeconds` defaults to the configured TTL and `tags` may be empty; a tag\n * written here is what `@Invalidates(\"tag\")` later purges.\n */\n async set(\n key: string,\n value: unknown,\n ttlSeconds: number = this.options.ttl,\n tags: readonly string[] = []\n ): Promise<void> {\n this.markExpiry(key, ttlSeconds);\n this.rememberTags(key, tags);\n\n // In stale-while-revalidate mode L1 must keep the entry past its TTL, so the\n // adapter is told \"never expire\" and this class owns the freshness decision.\n await this.writeL1(key, value, this.options.staleWhileRevalidate ? 0 : ttlSeconds);\n await this.writeL2(key, value, ttlSeconds);\n }\n\n /** Remove one entry from every layer. */\n async delete(key: string): Promise<void> {\n this.forgetKey(key);\n await this.l1?.delete(key);\n\n if (!this.options.l2) return;\n try {\n await this.options.l2.delete(key);\n } catch (error) {\n this.warnL2(\"delete\", error);\n }\n }\n\n /** Remove every entry this manager can see. */\n async clear(): Promise<void> {\n await this.l1?.clear();\n this.expiry.clear();\n this.tagIndex.clear();\n this.keyTags.clear();\n\n if (!this.options.l2) return;\n try {\n await this.options.l2.clear();\n } catch (error) {\n this.warnL2(\"clear\", error);\n }\n }\n\n /**\n * Purge every entry carrying any of `tags`.\n *\n * Used by `@Invalidates(...)` / `@HitSource(...)` after a successful request.\n * Keys are collected first because deleting mutates the tags they came from.\n */\n async invalidateTags(tags: readonly string[]): Promise<void> {\n const keys = new Set<string>();\n for (const tag of tags) {\n for (const key of this.tagIndex.get(tag) ?? []) keys.add(key);\n }\n\n for (const key of keys) {\n await this.delete(key);\n }\n }\n\n /** Alias of {@link clear}, spelled the way `@Invalidates(\"*\")`-style callers expect. */\n async invalidateAll(): Promise<void> {\n await this.clear();\n }\n\n // ── in-flight de-duplication ──────────────────────────────────────────────\n\n /** The promise of the request currently being sent for `key`, if any. */\n getInFlight(key: string): Promise<unknown> | undefined {\n return this.flight.get(key);\n }\n\n /**\n * Publish the promise of the request being sent for `key`.\n *\n * The entry removes itself when the promise settles, so a rejected shared\n * promise cannot be picked up by a later, unrelated request. The rejection is\n * also marked as observed: a leader that fails with no follower waiting would\n * otherwise surface as an unhandled rejection in Node.\n */\n setInFlight(key: string, promise: Promise<unknown>): void {\n const tracked = promise.then(\n (value) => {\n // Only one leader can exist per key: a follower never registers, and the\n // entry it would find is always the leader's.\n this.flight.delete(key);\n return value;\n },\n (error) => {\n this.flight.delete(key);\n throw error;\n }\n );\n\n void tracked.catch(() => undefined);\n this.flight.set(key, tracked);\n }\n\n // ── layers ────────────────────────────────────────────────────────────────\n\n private async readL1<T>(key: string): Promise<Probe<T>> {\n if (!this.l1) return { found: false };\n const value = await this.l1.get<T>(key);\n return value === undefined ? { found: false } : { found: true, value };\n }\n\n private async readL2<T>(key: string): Promise<Probe<T>> {\n const l2 = this.options.l2;\n if (!l2) return { found: false };\n\n try {\n const value = await l2.get<T>(key);\n return value === undefined ? { found: false } : { found: true, value };\n } catch (error) {\n this.warnL2(\"read\", error);\n return { found: false };\n }\n }\n\n private async writeL1(key: string, value: unknown, ttlSeconds: number): Promise<void> {\n if (!this.l1) return;\n await this.l1.set(key, value, ttlSeconds);\n }\n\n private async writeL2(key: string, value: unknown, ttlSeconds: number): Promise<void> {\n const l2 = this.options.l2;\n if (!l2) return;\n\n try {\n await l2.set(key, value, ttlSeconds);\n } catch (error) {\n // A persistent store can refuse a write (quota, private mode, offline).\n // That must never fail a request that has already succeeded.\n this.warnL2(\"write\", error);\n }\n }\n\n // ── index bookkeeping ─────────────────────────────────────────────────────\n\n private markExpiry(key: string, ttlSeconds: number): void {\n this.expiry.set(key, ttlSeconds > 0 ? Date.now() + ttlSeconds * 1000 : 0);\n }\n\n private isStale(key: string): boolean {\n const expiresAt = this.expiry.get(key);\n return expiresAt !== undefined && expiresAt !== 0 && expiresAt <= Date.now();\n }\n\n private rememberTags(key: string, tags: readonly string[]): void {\n if (tags.length === 0) return;\n\n const owned = this.keyTags.get(key) ?? new Set<string>();\n for (const tag of tags) {\n owned.add(tag);\n const keys = this.tagIndex.get(tag) ?? new Set<string>();\n keys.add(key);\n this.tagIndex.set(tag, keys);\n }\n this.keyTags.set(key, owned);\n }\n\n private forgetKey(key: string): void {\n this.expiry.delete(key);\n\n const tags = this.keyTags.get(key);\n if (!tags) return;\n\n for (const tag of tags) {\n const keys = this.tagIndex.get(tag);\n if (!keys) continue;\n keys.delete(key);\n if (keys.size === 0) this.tagIndex.delete(tag);\n }\n this.keyTags.delete(key);\n }\n\n private warnL2(operation: string, error: unknown): void {\n this.logger.warn(\n t(\"cache.warn.l2.failed\", this.options.prefix, `${operation}: ${String(error)}`)\n );\n }\n}\n\n// ── option resolution ───────────────────────────────────────────────────────\n\n/** Apply every default and turn the L2 selector into a live adapter. */\nfunction resolveOptions(options: CacheManagerOptions, logger: SnailLogger): ResolvedCacheOptions {\n const prefix = options.prefix ?? \"[snail-cache]\";\n\n return {\n ttl: positive(options.ttl, 60),\n maxSize: positive(options.maxSize, DEFAULT_L1_MAX_SIZE),\n l1: options.l1 ?? true,\n l2: createL2(options.l2, prefix, logger),\n cacheFor: normalizeCacheFor(options.cacheFor),\n prefix,\n staleWhileRevalidate: options.staleWhileRevalidate ?? false,\n dedupe: options.dedupe ?? true\n };\n}\n\n/**\n * Turn the `l2` selector into an adapter.\n *\n * A missing environment global becomes \"no L2 at all\" plus one warning, rather\n * than an adapter that silently misses on every read: the warning is the only\n * signal an application gets that its persistence intent was not honoured.\n */\nfunction createL2(\n selector: CacheOptions[\"l2\"],\n prefix: string,\n logger: SnailLogger\n): CacheAdapter | undefined {\n if (selector === undefined) return undefined;\n if (typeof selector !== \"string\") return selector;\n\n const normalized = selector.toLowerCase();\n if (normalized === \"localstorage\" || normalized === \"sessionstorage\") {\n const area = normalized === \"localstorage\" ? \"localStorage\" : \"sessionStorage\";\n const adapter = new WebStorageCacheAdapter(\n () => (globalThis as unknown as Record<string, Storage | undefined>)[area],\n { prefix, label: area }\n );\n\n if (!adapter.available) {\n logger.warn(t(\"cache.warn.l2.unavailable\", prefix, area));\n return undefined;\n }\n return adapter;\n }\n\n if (normalized === \"indexeddb\") {\n const adapter = new IndexedDBCacheAdapter({ storeName: prefix });\n if (!adapter.available) {\n logger.warn(t(\"cache.warn.l2.unavailable\", prefix, \"indexedDB\"));\n return undefined;\n }\n return adapter;\n }\n\n throw new SnailPluginError(t(\"error.plugin.cache.adapter\", selector), {\n pluginName: \"cache\"\n });\n}\n\n/** Normalise `cacheFor` into `\"all\"` or an uppercase verb list. */\nfunction normalizeCacheFor(\n input: CacheOptions[\"cacheFor\"]\n): \"all\" | readonly SnailMethodType[] {\n if (input === undefined) return DEFAULT_CACHE_FOR;\n if (typeof input === \"string\") {\n return input.toLowerCase() === \"all\"\n ? \"all\"\n : [input.toUpperCase() as SnailMethodType];\n }\n return input.map((verb) => String(verb).toUpperCase() as SnailMethodType);\n}\n\n/** Coerce an optional count to a positive finite number, else `fallback`. */\nfunction positive(value: number | undefined, fallback: number): number {\n return typeof value === \"number\" && Number.isFinite(value) && value > 0\n ? value\n : fallback;\n}\n","import type { AxiosResponse, InternalAxiosRequestConfig } from \"axios\";\nimport type { SnailContext } from \"../../core/context\";\nimport { createLogger } from \"../../core/logger\";\nimport { createPlugin } from \"../../core/plugin\";\nimport { coerceJSONStringBody } from \"../../core/response\";\nimport { t } from \"../../locale\";\nimport type { SnailMethodType } from \"../../typings/api\";\nimport type { SnailNext, SnailPluginObject } from \"../../typings/plugin\";\nimport { deferred } from \"../../utils\";\nimport { readCacheable, readInvalidates, readNoCache } from \"./decorators\";\nimport { buildCacheKey } from \"./key\";\nimport { CacheManager } from \"./manager\";\nimport type { ResolvedCacheOptions } from \"./manager\";\nimport type { CacheOptions } from \"./type\";\n\n/**\n * The cache plugin.\n *\n * ## Where it sits in the pipeline\n *\n * `priority: -100` is the reserved cache band, which buys two things the contract\n * spells out in `docs/guide/plugin-lifecycle.md` §2.2:\n *\n * - **last in forward order** — the interceptor (`100`) and every argument\n * decorator have already run, so `ctx.request` holds the final url, params and\n * body when the key is built. Hashing an earlier shape would key two different\n * requests identically (or one request twice).\n * - **first in unwind order** — the raw envelope is stored before the validation\n * and transformation plugins touch it, so a hit replays exactly what the server\n * sent rather than a processed derivative.\n *\n * ## Stores what the server sent\n *\n * The entry holds `ctx.response.data`, not the `SnailResult`. The result is a\n * projection of the envelope onto the caller's envelope schema; caching it would\n * freeze one server's key names into another's cache.\n */\n\n/** Plugin name; also the identity used by `Service.use()` / `Service.remove()`. */\nexport const CACHE_PLUGIN_NAME = \"cache\";\n\n/** The reserved cache priority band (see `docs/guide/plugin-lifecycle.md` §2.1). */\nexport const CACHE_PRIORITY = -100;\n\n/** `ctx.state` slot holding the {@link CachePlan} of the current send. */\nconst CACHE_PLAN_STATE = \"snail:cache:plan\";\n\n/** The plan resolved once in `beforeRequest` and consumed in `afterResponse`. */\ninterface CachePlan {\n /** Key to read and write, or `undefined` when this request is not cacheable. */\n readonly key: string | undefined;\n\n /** Lifetime to store under. */\n readonly ttl: number;\n\n /** Tags attached to the stored entry. */\n readonly tags: readonly string[];\n\n /** Tags whose entries must be purged once this request succeeded. */\n readonly invalidate: readonly string[];\n}\n\n/** The plugin object plus the manager behind it. */\nexport interface CachePlugin extends SnailPluginObject<CacheOptions> {\n /**\n * The storage engine, available once the plugin has been installed by\n * `Service.use(...)` — `install` is where the server name (the default key\n * prefix) and the resolved log level become known.\n */\n readonly manager: CacheManager | undefined;\n}\n\n/**\n * Create the cache plugin.\n *\n * ```ts\n * const cache = Cache({ ttl: 30, l2: \"localStorage\" });\n * Service.use(cache);\n * await cache.manager?.invalidateAll();\n * ```\n */\nexport function Cache(options?: CacheOptions): CachePlugin {\n let manager: CacheManager | undefined;\n\n const base = createPlugin<CacheOptions>({\n name: CACHE_PLUGIN_NAME,\n priority: CACHE_PRIORITY,\n setup(pluginOptions, api) {\n const instance = new CacheManager({\n ...pluginOptions,\n // The server name is the only prefix that is unique without the\n // application having to think about it, and two servers sharing an origin\n // would otherwise collide inside the same `localStorage`.\n prefix: pluginOptions?.prefix ?? api.serverName,\n logger: createLogger(api.serverOptions.logLevel)\n });\n manager = instance;\n\n // Plugin-owned strings: the core catalogue has no cache diagnostics, and\n // owning them here keeps a future rename from breaking another package.\n api.addMessages({\n \"cache.warn.l2.failed\": \"[%s] L2 cache %s failed; continuing without it\",\n \"cache.warn.l2.unavailable\": \"[%s] %s is unavailable in this environment; the cache plugin uses L1 only\",\n \"cache.warn.store.failed\": \"[%s] the response could not be cached: %s\",\n \"cache.warn.revalidate.failed\": \"[%s] background revalidation failed: %s\",\n \"info.cache.stale\": \"[%s] served a stale entry; revalidating in the background\"\n });\n\n return {\n beforeRequest: (ctx, next) => serveFromCache(instance, ctx, next),\n afterResponse: (ctx, next) => storeResponse(instance, ctx, next)\n };\n }\n })(options);\n\n // A getter rather than `Object.assign`: the manager does not exist until\n // `install` runs, and `Object.assign` would copy today's `undefined` into the\n // property for good.\n Object.defineProperty(base, \"manager\", {\n enumerable: true,\n configurable: true,\n get: () => manager\n });\n\n return base as CachePlugin;\n}\n\n// ── forward phase: read ─────────────────────────────────────────────────────\n\n/**\n * Serve a cache hit, or register this request as the one that will fill the gap.\n *\n * Three outcomes, in order of cost:\n *\n * 1. **fresh hit** — `ctx.interrupt(response)` and no `next()`, so axios is never\n * reached;\n * 2. **stale hit** (only with `staleWhileRevalidate`) — the stale body is served\n * immediately and the refresh happens out of band;\n * 3. **miss** — `next()` runs the request, and a follower of an identical\n * in-flight request waits for its leader instead of sending a second one.\n */\nasync function serveFromCache(\n manager: CacheManager,\n ctx: SnailContext,\n next: SnailNext\n): Promise<void> {\n const plan = resolveCachePlan(ctx, manager.options);\n ctx.state.set(CACHE_PLAN_STATE, plan);\n\n // Everything below the dynamic checks, so `@NoCache` and a non-cacheable verb\n // cost one metadata read and never build a key.\n const key = plan.key;\n if (key === undefined) return next();\n\n const found = await manager.lookup(key, manager.options.staleWhileRevalidate);\n if (found) {\n ctx.markCacheHit();\n ctx.interrupt(makeCachedResponse(found.value, ctx.request));\n ctx.logger.debug(t(\"info.cache.hit\", ctx.fullName));\n\n if (found.stale) {\n ctx.logger.debug(t(\"info.cache.stale\", ctx.fullName));\n revalidate(manager, ctx, plan, key);\n }\n return;\n }\n\n if (!manager.options.dedupe) return next();\n\n const pending = manager.getInFlight(key);\n if (pending) {\n const shared = await pending.catch(() => undefined);\n if (shared !== undefined) {\n ctx.markCacheHit();\n ctx.interrupt(makeCachedResponse(shared, ctx.request));\n return;\n }\n // The leader failed. This request is not its follower in any useful sense,\n // so it goes to the network on its own rather than inheriting the failure.\n return next();\n }\n\n const flight = deferred<unknown>();\n manager.setInFlight(key, flight.promise);\n\n try {\n await next();\n flight.resolve(ctx.getResponse()?.data);\n } catch (error) {\n // The rejection only reaches followers; `setInFlight` marks it as observed\n // so a failed request with no follower is not an unhandled rejection.\n flight.reject(error);\n throw error;\n }\n}\n\n// ── unwind phase: write ─────────────────────────────────────────────────────\n\n/**\n * Invalidate first, then store, then continue the chain.\n *\n * The order of the first two is load-bearing: a method that is both cacheable\n * and declares `@Invalidates` for one of its own tags stores *after* the purge,\n * so it cannot delete the entry it just wrote. Storing before `next()` is what\n * makes the cached value the raw envelope — later plugins see the response\n * afterwards, and the cache keeps what the server actually sent.\n */\nasync function storeResponse(\n manager: CacheManager,\n ctx: SnailContext,\n next: SnailNext\n): Promise<void> {\n const plan = ctx.state.get<CachePlan>(CACHE_PLAN_STATE);\n\n // A hit performed no network work, so it must neither store nor invalidate.\n //\n // This matters most for a stale-while-revalidate hit: the response in hand is\n // the *old* body while a background refresh is already fetching the new one.\n // Storing here would overwrite the refresh's result with the stale value it was\n // called to replace. The same reasoning applies to `@Invalidates` — the mutation\n // this request was supposed to perform never reached the server.\n if (plan && !ctx.isCacheHit) {\n if (plan.invalidate.length > 0) {\n await manager.invalidateTags(plan.invalidate);\n ctx.logger.debug(\n t(\"info.cache.invalidate\", ctx.fullName, plan.invalidate.join(\", \"))\n );\n }\n\n if (plan.key !== undefined && ctx.response) {\n try {\n // Snapshotted so a later `afterResponse` plugin — the transform plugin\n // hydrates the payload in place — cannot rewrite what was stored.\n await manager.set(plan.key, snapshot(ctx.response.data), plan.ttl, plan.tags);\n ctx.logger.debug(t(\"info.cache.set\", ctx.fullName));\n } catch (error) {\n ctx.logger.warn(t(\"cache.warn.store.failed\", ctx.fullName, String(error)));\n }\n }\n }\n\n // Chain hook, unlike the interceptors it wraps: the remaining unwind plugins\n // must still see the response.\n await next();\n}\n\n// ── policy ──────────────────────────────────────────────────────────────────\n\n/**\n * Decide what the cache should do with this request, from the decorators and the\n * configured verb list.\n *\n * Precedence, and why:\n *\n * 1. `@NoCache()` on the method — an explicit opt-out is never overridden;\n * 2. `@Cacheable()` on the method — an explicit opt-in beats a class-wide\n * decision, in both directions (this is what \"method-level wins\" means);\n * 3. `@NoCache()` on the class;\n * 4. `@Cacheable()` on the class — every method of the class;\n * 5. otherwise, the verb must be listed in `cacheFor`, which defaults to `GET`.\n *\n * Tags from both levels are merged, class first, so a method can add its own\n * without losing the class's. `ttl` and `key` are *not* merged: the most specific\n * declaration wins outright.\n */\nfunction resolveCachePlan(ctx: SnailContext, options: ResolvedCacheOptions): CachePlan {\n const classCacheable = readCacheable(ctx.apiClass);\n const methodCacheable = readCacheable(ctx.apiClass, ctx.methodName);\n\n const cacheable = readNoCache(ctx.apiClass, ctx.methodName)\n ? false\n : methodCacheable !== undefined\n ? true\n : readNoCache(ctx.apiClass)\n ? false\n : classCacheable !== undefined\n ? true\n : matchesCacheFor(ctx.methodType, options.cacheFor);\n\n const explicitKey = methodCacheable?.key ?? classCacheable?.key;\n\n return {\n key: cacheable\n ? buildCacheKey({\n prefix: options.prefix,\n request: ctx.request,\n methodType: ctx.methodType,\n explicitKey\n })\n : undefined,\n ttl: methodCacheable?.ttl ?? classCacheable?.ttl ?? options.ttl,\n tags: [\n ...new Set([...(classCacheable?.tags ?? []), ...(methodCacheable?.tags ?? [])])\n ],\n invalidate: [\n ...new Set([\n ...readInvalidates(ctx.apiClass),\n ...readInvalidates(ctx.apiClass, ctx.methodName)\n ])\n ]\n };\n}\n\n/** `true` when `cacheFor` covers this verb. */\nfunction matchesCacheFor(\n method: SnailMethodType,\n cacheFor: ResolvedCacheOptions[\"cacheFor\"]\n): boolean {\n return cacheFor === \"all\" || cacheFor.includes(method);\n}\n\n// ── helpers ─────────────────────────────────────────────────────────────────\n\n/**\n * Wrap a cached body into the axios response a plugin must hand back.\n *\n * `headers` is empty on purpose: the stored entry is only the body, and inventing\n * headers would make downstream plugins react to values the server never sent.\n * `config` is the live request, so anything reading `response.config` sees the\n * request that produced the hit.\n *\n * The body is **copied**. Without that, the object handed to the caller would be\n * the very object the cache holds, so one `result.data.name = \"x\"` would silently\n * rewrite the cache entry — and, worse, a response transformer that hydrates the\n * payload in place would corrupt the stored value for every later hit.\n */\nexport function makeCachedResponse<T>(\n body: T,\n config: InternalAxiosRequestConfig\n): AxiosResponse<T> {\n return {\n data: snapshot(body),\n status: 200,\n statusText: \"Cache Hit (snail)\",\n headers: {},\n config\n };\n}\n\n/**\n * Copy a value so the cache and the caller can never share a mutable reference.\n *\n * `structuredClone` is preferred because it preserves `Date`, `Map`, `Set` and\n * typed arrays, which a JSON round-trip flattens. JSON is the fallback for older\n * engines, and if both fail (a function-valued field, a class instance with\n * private state) the original reference is returned — a shared reference is a\n * far smaller problem than throwing inside a cache read.\n */\nfunction snapshot<T>(value: T): T {\n if (value === null || typeof value !== \"object\") return value;\n\n const cloner = (globalThis as { structuredClone?: <V>(input: V) => V })\n .structuredClone;\n if (typeof cloner === \"function\") {\n try {\n return cloner(value);\n } catch {\n /* not cloneable — fall through to JSON */\n }\n }\n\n try {\n return JSON.parse(JSON.stringify(value)) as T;\n } catch {\n return value;\n }\n}\n\n/**\n * Refresh an entry after serving stale data.\n *\n * The refresh deliberately does **not** call this request's `next()`: the caller\n * is still reading `ctx.response`, and a detached chain step would write the\n * fresh response into the very same context — corrupting the result the caller is\n * about to receive, or a later `send()` that reused the context. Instead the\n * final config is replayed through the server's own axios instance and the\n * response is normalised the same way `dispatch` normalises it, so the refreshed\n * entry is byte-for-byte what a normal miss would have stored.\n */\nfunction revalidate(\n manager: CacheManager,\n ctx: SnailContext,\n plan: CachePlan,\n key: string\n): void {\n // `requestInterceptor` is the last chance to rewrite the config before\n // transport, and the hit path skipped `dispatch`, so it has not run yet.\n const config = ctx.server.pluginManager.reduce(\"requestInterceptor\", ctx.request, ctx);\n\n void (async () => {\n try {\n let response = await ctx.server.axios.request(config);\n response = coerceJSONStringBody(response, ctx.serverOptions.coerceJSONString);\n response = ctx.server.pluginManager.reduce(\"responseInterceptor\", response, ctx);\n\n await manager.set(key, response.data, plan.ttl, plan.tags);\n } catch (error) {\n ctx.logger.warn(t(\"cache.warn.revalidate.failed\", ctx.fullName, String(error)));\n }\n })();\n}\n","import type { AxiosResponse, InternalAxiosRequestConfig } from \"axios\";\nimport type { SnailContext } from \"../../core/context\";\nimport {\n createClassDecorator,\n createMethodDecorator,\n getClassMetadata,\n getMethodMetadata\n} from \"../../decorators/custom\";\nimport { SnailDecoratorError } from \"../../error\";\nimport type { InterceptorEntry } from \"./type\";\n\n/**\n * `@BeforeRequest()` / `@AfterResponse()` — the decorator half of the\n * interceptor plugin.\n *\n * ## Storage\n *\n * Both targets share one metadata key per phase: a class application lands in the\n * class slot, a method application in that method's slot\n * (`src/core/metadata.ts` §storage model). Reading them back is therefore one\n * `getClassMetadata` plus one `getMethodMetadata`, with no \"is this a class or a\n * proto\" branching at the call site.\n *\n * ## Why the key name is namespaced\n *\n * `createClassDecorator` turns `\"snail-interceptor/before\"` into\n * `Symbol.for(\"@snail-js/api:custom:snail-interceptor/before\")`. A `Symbol.for`\n * key survives the two-copies-of-the-package situation a monorepo creates, so a\n * decorator applied from the hoisted copy is still visible to a plugin loaded\n * from the nested one.\n */\n\nconst BEFORE_KEY = \"snail-interceptor/before\";\nconst AFTER_KEY = \"snail-interceptor/after\";\n\ntype AnyEntry = InterceptorEntry<any>;\ntype DualDecorator = ClassDecorator & MethodDecorator;\n\nconst addBeforeOnClass = createClassDecorator<AnyEntry>(BEFORE_KEY);\nconst addBeforeOnMethod = createMethodDecorator<AnyEntry>(BEFORE_KEY);\nconst addAfterOnClass = createClassDecorator<AnyEntry>(AFTER_KEY);\nconst addAfterOnMethod = createMethodDecorator<AnyEntry>(AFTER_KEY);\n\n/**\n * Fold a class application and a method application into one decorator.\n *\n * TypeScript tells the two apart by arity: a class decorator is called with the\n * constructor alone, a method decorator with `(prototype, key, descriptor)`.\n * That is the only reliable signal available without `reflect-metadata`.\n */\nfunction dualDecorator(applyToClass: ClassDecorator, applyToMethod: MethodDecorator): DualDecorator {\n return ((target: any, propertyKey?: string | symbol) => {\n if (propertyKey === undefined) {\n applyToClass(target);\n return;\n }\n applyToMethod(target, propertyKey, undefined as never);\n }) as DualDecorator;\n}\n\n/** Reject the misuse that would otherwise fail silently at request time. */\nfunction assertCallback(value: unknown, decorator: string): void {\n if (typeof value !== \"function\") {\n throw new SnailDecoratorError(\n `[snail] @${decorator}() needs a function; got ${typeof value}`\n );\n }\n}\n\n/**\n * Run `onFulfilled` on the request config of this class / this method, in series\n * with every other request interceptor.\n *\n * ```ts\n * @Api(\"/user\")\n * @BeforeRequest<UserConfig>((config) => { config.headers.set(\"x-trace\", \"1\"); })\n * class UserApi {\n * @Get(\"/\")\n * @BeforeRequest((config) => { config.timeout = 5000; })\n * list(): Promise<User[]> { return null!; }\n * }\n * ```\n *\n * Class-level interceptors run before method-level ones, so a method may refine\n * what its class established. `onRejected` may recover a failure by returning a\n * replacement config; returning `undefined` (or throwing) lets the failure\n * through, and the request is then never sent.\n */\nexport function BeforeRequest<T = InternalAxiosRequestConfig>(\n onFulfilled: (value: T, ctx: SnailContext) => T | void | Promise<T | void>,\n onRejected?: (error: unknown, ctx: SnailContext) => unknown\n): DualDecorator {\n assertCallback(onFulfilled, \"BeforeRequest\");\n if (onRejected !== undefined) assertCallback(onRejected, \"BeforeRequest\");\n\n const entry: AnyEntry = { onFulfilled, onRejected };\n return dualDecorator(addBeforeOnClass(entry), addBeforeOnMethod(entry));\n}\n\n/**\n * Run `onFulfilled` against the response of this class / this method.\n *\n * The callback receives the axios response, **not** a `next`: unlike\n * `beforeRequest`, this phase is an unwind hook, so the response already exists\n * and nothing here decides whether the request happens. Return a replacement\n * response, or `undefined` to keep the one that was passed in.\n */\nexport function AfterResponse<T = AxiosResponse>(\n onFulfilled: (value: T, ctx: SnailContext) => T | void | Promise<T | void>,\n onRejected?: (error: unknown, ctx: SnailContext) => unknown\n): DualDecorator {\n assertCallback(onFulfilled, \"AfterResponse\");\n if (onRejected !== undefined) assertCallback(onRejected, \"AfterResponse\");\n\n const entry: AnyEntry = { onFulfilled, onRejected };\n return dualDecorator(addAfterOnClass(entry), addAfterOnMethod(entry));\n}\n\n/** Class-level `@BeforeRequest` entries, in application order. */\nexport function classBeforeEntries(target: unknown): AnyEntry[] {\n return getClassMetadata<AnyEntry[]>(BEFORE_KEY, target) ?? [];\n}\n\n/** Method-level `@BeforeRequest` entries, in application order. */\nexport function methodBeforeEntries(target: unknown, methodName: string): AnyEntry[] {\n return getMethodMetadata<AnyEntry[]>(BEFORE_KEY, target, methodName) ?? [];\n}\n\n/** Class-level `@AfterResponse` entries, in application order. */\nexport function classAfterEntries(target: unknown): AnyEntry[] {\n return getClassMetadata<AnyEntry[]>(AFTER_KEY, target) ?? [];\n}\n\n/** Method-level `@AfterResponse` entries, in application order. */\nexport function methodAfterEntries(target: unknown, methodName: string): AnyEntry[] {\n return getMethodMetadata<AnyEntry[]>(AFTER_KEY, target, methodName) ?? [];\n}\n","import { SnailPluginError } from \"../../error\";\nimport type { InterceptorEntry } from \"./type\";\n\n/**\n * Runtime registry of programmatic interceptors.\n *\n * Decorators cover interceptors that are known at class-definition time. This\n * class covers the other half — an application that decides at runtime \"from now\n * on, sign every request\" — and it is also what the plugin uses internally to\n * hold the server-wide entries passed to `Interceptor({ request })`.\n *\n * Entries keep insertion order: that order *is* the execution order, since the\n * interceptor contract is deliberately sequential rather than priority based.\n */\nexport class InterceptorManager<T = unknown> {\n private readonly registry = new Map<number, InterceptorEntry<T>>();\n private sequence = 0;\n\n /** Register an entry. Returns the id {@link eject} needs to remove it again. */\n use(entry: InterceptorEntry<T>): number {\n if (!entry || (typeof entry.onFulfilled !== \"function\" && typeof entry.onRejected !== \"function\")) {\n throw new SnailPluginError(\n \"[snail] InterceptorManager.use() expects an entry with onFulfilled and/or onRejected\",\n { pluginName: \"interceptor\" }\n );\n }\n\n // Ids are never reused: an `eject` racing a late `use` must not remove the\n // wrong entry, which array-index ids in the pre-rewrite implementation did.\n this.sequence += 1;\n this.registry.set(this.sequence, entry);\n return this.sequence;\n }\n\n /** Remove an entry by the id returned from {@link use}. Returns whether it existed. */\n eject(id: number): boolean {\n return this.registry.delete(id);\n }\n\n /** Drop every entry. */\n clear(): void {\n this.registry.clear();\n }\n\n /** Registered entries, in execution order. */\n get entries(): InterceptorEntry<T>[] {\n return [...this.registry.values()];\n }\n\n /** Number of registered entries. */\n get size(): number {\n return this.registry.size;\n }\n}\n","import type { AxiosResponse, InternalAxiosRequestConfig } from \"axios\";\nimport type { SnailContext } from \"../../core/context\";\nimport { createPlugin } from \"../../core/plugin\";\nimport { t } from \"../../locale\";\nimport type { SnailNext, SnailPluginObject } from \"../../typings/plugin\";\nimport {\n classAfterEntries,\n classBeforeEntries,\n methodAfterEntries,\n methodBeforeEntries\n} from \"./decorators\";\nimport { InterceptorManager } from \"./manager\";\nimport type { InterceptorEntry } from \"./type\";\n\n/**\n * The interceptor plugin.\n *\n * ## Where it sits in the pipeline\n *\n * `priority: 100` is the reserved interceptor band, so this plugin is the first\n * to see the request in forward order and the last to see the response in unwind\n * order. That is what lets `@BeforeRequest()` rewrite the config *before* the\n * cache plugin (`-100`) hashes the final url, params and body into a cache key.\n *\n * ## Request vs response\n *\n * `beforeRequest` is a chain hook: the interceptors run first, and only then\n * does the hook hand control on with `next()`. An interceptor that fails\n * unrecoverably therefore stops the request before anything reaches the network.\n *\n * `afterResponse` is an **unwind** hook. The response already exists when it\n * runs, so calling `next()` *first* would let later plugins observe a response\n * the interceptors had not rewritten yet. The interceptors run against\n * `ctx.response` first and the chain is advanced afterwards. The interceptor\n * callbacks themselves are not chain hooks — they receive the response rather\n * than a `next` — so nothing inside one of them can advance or stop the chain.\n */\n\n/** Plugin name; also the identity used by `Service.use()` / `Service.remove()`. */\nexport const INTERCEPTOR_PLUGIN_NAME = \"interceptor\";\n\n/** The reserved interceptor priority band (see `docs/guide/plugin-lifecycle.md` §2.1). */\nexport const INTERCEPTOR_PRIORITY = 100;\n\n/** Programmatic interceptors shared by every request of one server. */\nexport interface InterceptorOptions {\n /** Entries that run on every request, after the class- and method-level ones. */\n request?: InterceptorEntry<InternalAxiosRequestConfig>[];\n\n /** Entries that run on every response, after the class- and method-level ones. */\n response?: InterceptorEntry<AxiosResponse>[];\n}\n\n/**\n * The plugin object plus the two registries behind it.\n *\n * `use()` / `eject()` are exposed on the instance because an interceptor added\n * from `Interceptor({ request })` is fixed at construction time, and a real\n * application usually learns its token or tenant long after the server module\n * has been evaluated.\n */\nexport interface InterceptorPlugin extends SnailPluginObject<InterceptorOptions> {\n /** Server-wide request interceptors. */\n readonly request: InterceptorManager<InternalAxiosRequestConfig>;\n\n /** Server-wide response interceptors. */\n readonly response: InterceptorManager<AxiosResponse>;\n}\n\n/**\n * Create the interceptor plugin.\n *\n * ```ts\n * const interceptors = Interceptor({\n * request: [{ onFulfilled: (config) => { config.headers.set(\"x-app\", \"web\"); } }]\n * });\n * Service.use(interceptors);\n * interceptors.request.use({ onFulfilled: (config, ctx) => ctx.logger.debug(ctx.fullName) });\n * ```\n */\nexport function Interceptor(options?: InterceptorOptions): InterceptorPlugin {\n const request = new InterceptorManager<InternalAxiosRequestConfig>();\n const response = new InterceptorManager<AxiosResponse>();\n\n for (const entry of options?.request ?? []) request.use(entry);\n for (const entry of options?.response ?? []) response.use(entry);\n\n const base = createPlugin<InterceptorOptions>({\n name: INTERCEPTOR_PLUGIN_NAME,\n priority: INTERCEPTOR_PRIORITY,\n setup(_pluginOptions, api) {\n // Plugin-owned diagnostics: the core catalogue has no interceptor keys and\n // contributing them here keeps the two lists from drifting apart.\n api.addMessages({\n \"info.interceptor.request\": \"[%s] %s request interceptor(s) applied\",\n \"info.interceptor.response\": \"[%s] %s response interceptor(s) applied\"\n });\n\n return {\n beforeRequest: (ctx, next) => runRequestInterceptors(request, ctx, next),\n afterResponse: (ctx, next) => runResponseInterceptors(response, ctx, next)\n };\n }\n })(options);\n\n return Object.assign(base, { request, response });\n}\n\n// ── request phase ───────────────────────────────────────────────────────────\n\n/**\n * Run every request interceptor, then continue the chain.\n *\n * Order is class → method → server-wide, each list in its own application order.\n * One interceptor's failure is offered to that same interceptor's `onRejected`\n * before it is allowed to abort the request, which is the only recovery point in\n * the whole pipeline — `onError` merely observes.\n */\nasync function runRequestInterceptors(\n manager: InterceptorManager<InternalAxiosRequestConfig>,\n ctx: SnailContext,\n next: SnailNext\n): Promise<void> {\n const entries: InterceptorEntry<InternalAxiosRequestConfig>[] = [\n ...classBeforeEntries(ctx.apiClass),\n ...methodBeforeEntries(ctx.apiClass, ctx.methodName),\n ...manager.entries\n ];\n\n if (entries.length > 0) {\n ctx.logger.debug(t(\"info.interceptor.request\", ctx.fullName, String(entries.length)));\n }\n\n for (const entry of entries) {\n if (typeof entry.onFulfilled !== \"function\") continue;\n\n try {\n const replaced = await entry.onFulfilled(ctx.request, ctx);\n if (replaced !== undefined) ctx.setRequest(replaced);\n } catch (error) {\n ctx.setRequest(await recover<InternalAxiosRequestConfig>(entry, error, ctx));\n }\n }\n\n await next();\n}\n\n// ── response phase ──────────────────────────────────────────────────────────\n\n/**\n * Run every response interceptor against the response that already exists.\n *\n * The callbacks are not chain hooks: they get the response, not a `next`, so a\n * response interceptor can rewrite but never interrupt. The plugin's own\n * `afterResponse` hook still advances the chain at the end, which is what lets\n * lower-priority plugins (validation, transformation) see the rewritten value.\n */\nasync function runResponseInterceptors(\n manager: InterceptorManager<AxiosResponse>,\n ctx: SnailContext,\n next: SnailNext\n): Promise<void> {\n const entries: InterceptorEntry<AxiosResponse>[] = [\n ...classAfterEntries(ctx.apiClass),\n ...methodAfterEntries(ctx.apiClass, ctx.methodName),\n ...manager.entries\n ];\n\n if (entries.length > 0) {\n ctx.logger.debug(t(\"info.interceptor.response\", ctx.fullName, String(entries.length)));\n }\n\n for (const entry of entries) {\n if (typeof entry.onFulfilled !== \"function\") continue;\n\n try {\n const replaced = await entry.onFulfilled(ctx.requireResponse(), ctx);\n if (replaced !== undefined) ctx.setResponse(replaced);\n } catch (error) {\n ctx.setResponse(await recover<AxiosResponse>(entry, error, ctx));\n }\n }\n\n await next();\n}\n\n// ── shared ──────────────────────────────────────────────────────────────────\n\n/**\n * Give an interceptor's `onRejected` a chance to recover from `error`.\n *\n * Only a *value* recovers: `undefined` means \"I handled the notification but\n * have nothing to continue with\", which must not silently produce an undefined\n * config. The original error is rethrown in that case, so the caller sees the\n * real cause rather than a downstream symptom.\n */\nasync function recover<T>(\n entry: InterceptorEntry<T>,\n error: unknown,\n ctx: SnailContext\n): Promise<T> {\n if (typeof entry.onRejected !== \"function\") throw error;\n\n const recovered = await entry.onRejected(error, ctx);\n if (recovered === undefined) throw error;\n\n return recovered as T;\n}\n","import { SnailError } from \"../../error/base\";\n\n/**\n * The error codes a request-pool refusal can carry.\n *\n * `isPoolError()` is the supported way to test them, but the strings are exported\n * so an application can switch on one precisely — for example to distinguish \"the\n * queue was full, try again shortly\" from \"the user navigated away\".\n */\nexport const POOL_ERROR_CODES = {\n /** `maxQueue` was reached; the request was never sent. */\n queueFull: \"SNAIL_POOL_QUEUE_FULL\",\n /** The request waited longer than `queueTimeout` and was dropped. */\n queueTimeout: \"SNAIL_POOL_QUEUE_TIMEOUT\",\n /** The request was cancelled while it was still waiting for a slot. */\n aborted: \"SNAIL_POOL_ABORTED\",\n /** The queue was cleared — usually because the plugin was uninstalled. */\n cleared: \"SNAIL_POOL_CLEARED\"\n} as const;\n\n/** Any code {@link SnailPoolError} can carry. */\nexport type PoolErrorCode = (typeof POOL_ERROR_CODES)[keyof typeof POOL_ERROR_CODES];\n\n/**\n * Raised when the pool refuses or drops a request.\n *\n * A dedicated class rather than a reuse of `SnailRequestError`: a pool refusal\n * means the request **never reached the network**, which is a materially different\n * situation from a transport failure. Retrying is safe and usually correct, the\n * payload is untouched, and no server state changed — a caller that cannot tell the\n * two apart will either retry a request the server already processed, or give up on\n * one that was merely queued behind a burst.\n */\nexport class SnailPoolError extends SnailError {\n /** Which refusal this is. */\n declare readonly code: PoolErrorCode;\n\n constructor(message: string, code: PoolErrorCode, options: { cause?: unknown } = {}) {\n super(message, { code, cause: options.cause });\n }\n}\n","import { t } from \"../../locale\";\nimport { POOL_ERROR_CODES, SnailPoolError } from \"./type\";\n\n/**\n * The slice of `AbortSignal` the scheduler uses.\n *\n * Structurally typed rather than declared as `AbortSignal` because axios hands the\n * plugin its own `GenericAbortSignal`, which does not satisfy the DOM interface —\n * it lacks `reason`/`throwIfAborted` and even types the listener methods as\n * optional. In practice the object is a real `AbortSignal`, so the listener path is\n * always available; the optional methods are handled anyway, and a signal that\n * cannot be listened to simply means an already-queued request is not withdrawn\n * early — it still gets its slot and is rejected by the transport.\n */\nexport interface AbortLike {\n readonly aborted: boolean;\n addEventListener?(type: \"abort\", listener: () => void, options?: { once?: boolean }): void;\n removeEventListener?(type: \"abort\", listener: () => void): void;\n}\n\n/**\n * Options accepted by the request-pool plugin.\n *\n * ## Why a pool exists at all\n *\n * A browser already queues requests: HTTP/1.1 allows about six connections per\n * origin, and everything beyond that waits inside the network stack. That built-in\n * queue is FIFO, invisible, and unprioritised — an application that fires fifty\n * requests from one screen cannot say that the one the user is looking at should\n * go first, cannot observe how many are waiting, and cannot avoid a burst that\n * starves the rest of the page.\n *\n * HTTP/2 does not remove the need either: multiplexing replaces the six-connection\n * cap with a *stream* limit (commonly 100), and the server still has a finite\n * capacity. A burst of five hundred requests over HTTP/2 will happily saturate it.\n *\n * A pool gives three things the browser cannot: a hard ceiling that holds on both\n * protocols, a priority order, and a bounded queue that fails fast instead of\n * silently piling up.\n */\nexport interface RequestPoolOptions {\n /**\n * Maximum requests in flight at once. Defaults to `6`.\n *\n * Six is the classic HTTP/1.1 per-origin connection cap, so it is the largest\n * value that cannot make things worse on an old server while still bounding an\n * HTTP/2 burst. Raising it helps only when the backend is known to cope.\n */\n concurrency?: number;\n\n /**\n * Maximum requests allowed to *wait*. Defaults to `Infinity`.\n *\n * Set a finite value to fail fast: once the queue is full, an extra request\n * rejects immediately rather than joining an unbounded backlog the user will\n * never see resolved.\n */\n maxQueue?: number;\n\n /**\n * How long a queued request may wait, in milliseconds. Defaults to `0`, meaning\n * no limit.\n *\n * A request the user has stopped waiting for is worse than a failed one — it\n * still consumes a slot eventually and may overwrite fresher data.\n */\n queueTimeout?: number;\n\n /**\n * Ordering weight for a queued request. **Lower runs first**; ties keep arrival\n * order, and a request that finds a free slot never queues at all.\n *\n * @example\n * ```ts\n * // Requests the user is looking at jump ahead of background prefetches.\n * priority: (ctx) => (ctx.state.get(\"prefetch\") ? 100 : 0)\n * ```\n */\n priority?: (ctx: unknown) => number;\n}\n\n/** Live counters for a {@link RequestPoolScheduler}. */\nexport interface RequestPoolStats {\n /** Requests currently holding a slot. */\n active: number;\n /** Requests waiting for a slot. */\n queued: number;\n /** Configured ceiling. */\n concurrency: number;\n}\n\n/** A resolve/reject pair plus the bookkeeping the queue needs to drop it. */\nexport interface PoolTicket {\n /** Give the slot back. Idempotent. */\n readonly release: () => void;\n}\n\ninterface Waiter {\n readonly priority: number;\n readonly sequence: number;\n settle(ticket: PoolTicket): void;\n fail(error: unknown): void;\n /** Remove the queue's own bookkeeping (timer, abort listener). */\n dispose(): void;\n}\n\n/**\n * The scheduling core of the request pool.\n *\n * Deterministic and free of any request or plugin concept: it hands out `release`\n * callbacks and knows nothing about what they guard. That makes the hard part —\n * ordering, fairness and the guarantee that a slot is always returned — testable\n * on its own, without a server or a network.\n *\n * ## Messages come from the plugin\n *\n * The refusal messages are contributed by `RequestPool`'s `setup`, following the\n * plugin-owned-strings convention. Using this class **standalone** therefore yields\n * the raw message keys rather than translated text — the `code` on each\n * {@link SnailPoolError} is always correct, and that is what callers should branch\n * on. Registering a catalogue yourself is enough if you want the text too.\n *\n * ## The invariant that matters\n *\n * `active` must never drift from the number of outstanding tickets. Every failure\n * path (a queue that is full, a wait that times out, a request that is aborted,\n * a waiter dropped by `clear`) either never increments `active` or has already\n * been counted, and `release()` is idempotent so a double release from a `finally`\n * plus an abort listener cannot leak a slot. A leaked slot is invisible until the\n * pool permanently deadlocks at its ceiling, at which point every later request\n * hangs — which is why it is enforced here rather than at the call site.\n */\nexport class RequestPoolScheduler {\n private concurrency: number;\n private readonly maxQueue: number;\n private readonly queueTimeout: number;\n private readonly priorityOf: ((ctx: unknown) => number) | undefined;\n\n private active = 0;\n private sequence = 0;\n private readonly waiters: Waiter[] = [];\n\n constructor(options: RequestPoolOptions = {}) {\n const concurrency = Number.isFinite(options.concurrency)\n ? Math.floor(options.concurrency as number)\n : 6;\n this.concurrency = Math.max(1, concurrency);\n this.maxQueue = Number.isFinite(options.maxQueue)\n ? Math.max(0, Math.floor(options.maxQueue as number))\n : Number.POSITIVE_INFINITY;\n this.queueTimeout = Math.max(0, options.queueTimeout ?? 0);\n this.priorityOf = options.priority;\n }\n\n /** Current counters. */\n get stats(): RequestPoolStats {\n return { active: this.active, queued: this.waiters.length, concurrency: this.concurrency };\n }\n\n /** Change the ceiling at runtime; raising it immediately admits queued work. */\n setConcurrency(value: number): void {\n if (!Number.isFinite(value)) return;\n this.concurrency = Math.max(1, Math.floor(value));\n this.pump();\n }\n\n /**\n * Take a slot, or wait for one.\n *\n * Resolves with a ticket whose `release()` returns the slot. Rejects when the\n * queue is full or the wait exceeds `queueTimeout`. `signal` lets an abandoned\n * request leave the queue instead of holding a place it will never use.\n */\n acquire(ctx: unknown, signal?: AbortLike): Promise<PoolTicket> {\n if (signal?.aborted) {\n return Promise.reject(this.abortError());\n }\n\n if (this.active < this.concurrency) {\n return Promise.resolve(this.issue());\n }\n\n if (this.waiters.length >= this.maxQueue) {\n return Promise.reject(\n new SnailPoolError(\n t(\"error.pool.queueFull\", this.waiters.length),\n POOL_ERROR_CODES.queueFull\n )\n );\n }\n\n return new Promise<PoolTicket>((resolve, reject) => {\n let timer: ReturnType<typeof setTimeout> | undefined;\n let onAbort: (() => void) | undefined;\n let done = false;\n\n const waiter: Waiter = {\n priority: this.priorityOfFor(ctx),\n sequence: this.sequence++,\n settle: (ticket) => {\n if (done) return;\n done = true;\n waiter.dispose();\n resolve(ticket);\n },\n fail: (error) => {\n if (done) return;\n done = true;\n waiter.dispose();\n reject(error);\n },\n dispose: () => {\n if (timer) clearTimeout(timer);\n if (onAbort) signal?.removeEventListener?.(\"abort\", onAbort);\n }\n };\n\n if (this.queueTimeout > 0) {\n timer = setTimeout(() => {\n remove(this.waiters, waiter);\n waiter.fail(\n new SnailPoolError(\n t(\"error.pool.queueTimeout\", this.queueTimeout),\n POOL_ERROR_CODES.queueTimeout\n )\n );\n }, this.queueTimeout);\n // A pending queue timer must not hold a Node process open.\n (timer as unknown as { unref?: () => void }).unref?.();\n }\n\n if (signal && typeof signal.addEventListener === \"function\") {\n onAbort = () => {\n remove(this.waiters, waiter);\n waiter.fail(this.abortError());\n };\n signal.addEventListener(\"abort\", onAbort, { once: true });\n }\n\n this.waiters.push(waiter);\n this.sort();\n });\n }\n\n /**\n * Drop every waiter, rejecting each with `reason`.\n *\n * Used on uninstall: a queued request must not sit forever waiting for a pool\n * that will never admit it.\n */\n clear(reason?: unknown): void {\n const pending = this.waiters.splice(0, this.waiters.length);\n for (const waiter of pending) {\n waiter.fail(\n reason ??\n new SnailPoolError(t(\"error.pool.cleared\"), POOL_ERROR_CODES.cleared)\n );\n }\n }\n\n // ── internals ─────────────────────────────────────────────────────────────\n\n /** Hand out one slot. */\n private issue(): PoolTicket {\n this.active += 1;\n let released = false;\n\n return {\n release: () => {\n if (released) return;\n released = true;\n this.active = Math.max(0, this.active - 1);\n this.pump();\n }\n };\n }\n\n /** Admit as many waiters as there are free slots. */\n private pump(): void {\n while (this.active < this.concurrency && this.waiters.length > 0) {\n const waiter = this.waiters.shift()!;\n waiter.settle(this.issue());\n }\n }\n\n /** Lowest priority number first, then arrival order. */\n private sort(): void {\n this.waiters.sort((a, b) =>\n a.priority === b.priority ? a.sequence - b.sequence : a.priority - b.priority\n );\n }\n\n private priorityOfFor(ctx: unknown): number {\n if (!this.priorityOf) return 0;\n try {\n const value = this.priorityOf(ctx);\n return Number.isFinite(value) ? value : 0;\n } catch {\n // A throwing priority function is a caller bug, but it must not take down the\n // queue: fall back to the default rather than leaving the request unstoppable.\n return 0;\n }\n }\n\n private abortError(): unknown {\n return new SnailPoolError(t(\"error.pool.aborted\"), POOL_ERROR_CODES.aborted);\n }\n}\n\n/** Remove one waiter by identity, wherever it sits in the queue. */\nfunction remove(waiters: Waiter[], waiter: Waiter): void {\n const index = waiters.indexOf(waiter);\n if (index !== -1) waiters.splice(index, 1);\n}\n","import { createPlugin } from \"../../core/plugin\";\nimport { SnailPoolError } from \"./type\";\nimport {\n RequestPoolScheduler,\n type RequestPoolOptions,\n type RequestPoolStats\n} from \"./scheduler\";\n\n/** Plugin name; also the identity used by `Service.use()` / `Service.remove()`. */\nexport const POOL_PLUGIN_NAME = \"pool\";\n\n/**\n * Priority of the pool band.\n *\n * **Below** the cache (`-100`), which is the whole point: forward hooks run\n * highest-priority first, so the pool is the very last thing to run before the\n * transport. A request that the cache can answer never reaches the pool and never\n * consumes a slot. Putting the pool above the cache would let a handful of cached\n * reads occupy the entire pool and starve the real requests behind them.\n */\nexport const POOL_PRIORITY = -150;\n\n/** The pool plugin object plus live access to its scheduler. */\nexport interface RequestPoolPlugin {\n readonly name: string;\n readonly priority: number;\n\n /**\n * The scheduler, available once `install` has run.\n *\n * Exposed so an application can read {@link RequestPoolScheduler.stats} for a\n * loading indicator, or widen `concurrency` when it learns the backend is\n * coping.\n */\n readonly scheduler: RequestPoolScheduler | undefined;\n}\n\n/**\n * Bound how many requests are in flight at once.\n *\n * ```ts\n * Service.use(RequestPool({ concurrency: 4, maxQueue: 50, queueTimeout: 10_000 }));\n *\n * // `ctx.state` is readable from the priority callback, so an interactive request\n * // can jump ahead of a background prefetch.\n * Service.use(RequestPool({ concurrency: 4, priority: (ctx) => (ctx.state.get(\"prefetch\") ? 100 : 0) }));\n * ```\n *\n * ## What problem this solves\n *\n * See {@link RequestPoolOptions} for why the browser's own queue is not enough.\n * In short: the built-in one is FIFO, invisible and unprioritised, so a burst from\n * one screen can starve the request the user is actually waiting for, and nothing\n * fails fast when the backlog grows without bound.\n *\n * ## Where it sits\n *\n * Last in forward order, immediately before the network call, and it holds its slot\n * only for the transport — `next()` resolves once the response has been received,\n * so validation, transformation and the caller's reactive updates happen after the\n * slot is already back in the pool.\n *\n * A cache hit short-circuits `beforeRequest` upstream of this plugin, so cached\n * reads cost no concurrency.\n *\n * ## One path it does not cover\n *\n * `useTokenAuth` replays a request that came back `401` by re-running the transport\n * directly, deliberately without re-entering `beforeRequest` (re-entering would\n * loop). Those replays therefore **do not pass through this pool** and are not\n * counted against `concurrency`.\n *\n * The practical impact is small — replays are bounded by the number of requests\n * that were in flight when the token expired, which were already uncapped — but it\n * is worth knowing before reaching for the pool to protect a backend from an auth\n * storm. If that matters, cap it at the source: use `useRequest`'s own concurrency\n * control, or let `useTokenAuth` refresh proactively rather than on the first 401.\n */\nexport function RequestPool(options?: RequestPoolOptions): RequestPoolPlugin {\n let scheduler: RequestPoolScheduler | undefined;\n\n const base = createPlugin<RequestPoolOptions>({\n name: POOL_PLUGIN_NAME,\n priority: POOL_PRIORITY,\n\n setup(_pluginOptions, api) {\n const instance = new RequestPoolScheduler(options);\n scheduler = instance;\n\n // Plugin-owned diagnostics, so a future rename in this file cannot break a\n // message another package owns.\n api.addMessages({\n \"error.pool.queueFull\": \"请求池队列已满(等待中 %s 个),请稍后重试\",\n \"error.pool.queueTimeout\": \"请求池排队超时(已等待 %sms)\",\n \"error.pool.cleared\": \"请求池已清空,排队的请求被取消\",\n \"error.pool.aborted\": \"请求在排队期间被取消\"\n });\n\n // A queued request must not outlive the plugin that would eventually admit it.\n api.onDispose(() => instance.clear());\n\n return {\n async beforeRequest(ctx, next) {\n const signal = ctx.request.signal ?? undefined;\n const ticket = await instance.acquire(ctx, signal);\n\n try {\n await next();\n } finally {\n // `finally`, not a success path: a transport failure, a validation\n // throw and a cancellation must all return the slot, or the pool\n // silently shrinks by one every time something goes wrong.\n ticket.release();\n }\n }\n };\n }\n })(options);\n\n return Object.defineProperty(base, \"scheduler\", {\n enumerable: true,\n configurable: true,\n // A getter, not `Object.assign`: the scheduler does not exist until `install`\n // runs, and `Object.assign` would freeze today's `undefined` onto the object.\n get: () => scheduler\n }) as RequestPoolPlugin;\n}\n\n/** Read the live counters of an installed pool plugin, or `undefined`. */\nexport function poolStats(plugin: RequestPoolPlugin): RequestPoolStats | undefined {\n return plugin.scheduler?.stats;\n}\n\n/** Drop every queued request of an installed pool plugin. */\nexport function clearPool(plugin: RequestPoolPlugin, reason?: unknown): void {\n plugin.scheduler?.clear(reason);\n}\n\n/**\n * `true` when `error` came from the pool rather than from the transport.\n *\n * Lets a caller tell \"refused before it was ever sent\" — safe to retry later —\n * apart from a real network failure. Covers all four refusal paths: a full queue,\n * a queue wait that timed out, a request abandoned while waiting, and a queue\n * cleared by an uninstall.\n */\nexport function isPoolError(error: unknown): error is SnailPoolError {\n return error instanceof SnailPoolError;\n}\n","import { defineMetadata, getMetadata } from \"../../core/metadata\";\nimport { createPropertyDecorator, customMetadataKey } from \"../../decorators/custom\";\nimport { SnailDecoratorError } from \"../../error/decorator\";\nimport type { DtoType, PropertyTypeOptions, PropertyTypeSpec } from \"./type\";\n\n/**\n * Transform decorators.\n *\n * `@Transform(DtoClass)` chooses the class a response is hydrated into;\n * `@PropertyType()` and `@ExposeName()` describe the shape of that class. All\n * three are metadata-only — they never touch a request or a server, so a DTO\n * shared between two api classes cannot leak state from one to the other.\n */\n\n/** Metadata slot written by `@Transform`. */\nconst TRANSFORM_KEY = customMetadataKey(\"transform/dto\");\n\n/** Metadata slot written by `@PropertyType`. */\nexport const PROPERTY_TYPE_KEY = customMetadataKey(\"transform/property-type\");\n\n/** Metadata slot written by `@ExposeName`. */\nexport const EXPOSE_NAME_KEY = customMetadataKey(\"transform/expose-name\");\n\n/**\n * Choose the DTO a response payload is hydrated into.\n *\n * Applies to an api class and to a single method; the method wins, so one\n * endpoint can answer with a different shape than its siblings.\n *\n * ```ts\n * @Api(\"/user\")\n * @Transform(UserDto)\n * class UserApi {\n * @Get(\"/:id\")\n * getUser(@Params(\"id\") id: string): Promise<UserDto> { return null!; }\n * }\n * ```\n */\nexport function Transform(dto: DtoType): ClassDecorator & MethodDecorator {\n if (typeof dto !== \"function\") {\n throw new SnailDecoratorError(\n \"[snail] @Transform() expects a DTO class, not an instance or a plain object\"\n );\n }\n\n return ((target: unknown, propertyKey?: string | symbol) => {\n defineMetadata(TRANSFORM_KEY, dto, target, propertyKey);\n }) as ClassDecorator & MethodDecorator;\n}\n\nconst definePropertyType = createPropertyDecorator<PropertyTypeSpec>(\n \"transform/property-type\"\n);\n\n/**\n * Declare the runtime type of one DTO property.\n *\n * ```ts\n * class OrderDto {\n * @PropertyType(() => UserDto) user!: UserDto;\n * @PropertyType(() => ItemDto, { array: true }) items!: ItemDto[];\n * @PropertyType(() => Date) createdAt!: Date;\n * }\n * ```\n *\n * A property without `@PropertyType` is assigned as-is, so primitives, plain\n * objects and arrays of primitives need no declaration — but they *do* need\n * something that makes the property known, otherwise the unknown-key rule drops\n * them.\n */\nexport function PropertyType(\n type: () => unknown,\n options?: PropertyTypeOptions\n): PropertyDecorator {\n if (typeof type !== \"function\") {\n throw new SnailDecoratorError(\n \"[snail] @PropertyType() expects a lazy type resolver, e.g. @PropertyType(() => ChildDto)\"\n );\n }\n\n return definePropertyType(options ? { type, options } : { type });\n}\n\nconst defineExposeName = createPropertyDecorator<string>(\"transform/expose-name\");\n\n/**\n * Read a property from a differently-named JSON key.\n *\n * ```ts\n * class UserDto {\n * @ExposeName(\"user_name\") userName!: string;\n * }\n * // { \"user_name\": \"ada\" } → new UserDto().userName === \"ada\"\n * ```\n *\n * Excluding a key needs no decorator: undeclared JSON keys are already dropped.\n */\nexport function ExposeName(jsonKey: string): PropertyDecorator {\n if (typeof jsonKey !== \"string\" || jsonKey.length === 0) {\n throw new SnailDecoratorError(\n \"[snail] @ExposeName() requires a non-empty JSON key\"\n );\n }\n\n return defineExposeName(jsonKey);\n}\n\n/** Read the DTO of one method: method first, then the api class. */\nexport function resolveDto(\n apiClass: unknown,\n methodName: string\n): DtoType | undefined {\n return (\n getMetadata<DtoType>(TRANSFORM_KEY, apiClass, methodName) ??\n getMetadata<DtoType>(TRANSFORM_KEY, apiClass)\n );\n}\n","import { collectMethodKeys, getMetadata } from \"../../core/metadata\";\nimport { EXPOSE_NAME_KEY, PROPERTY_TYPE_KEY } from \"./decorators\";\nimport type { SnailContext } from \"../../core/context\";\nimport type { DtoType, PropertyTypeSpec } from \"./type\";\n\n/**\n * The JSON → class hydration engine.\n *\n * Hand-written on purpose: `class-transformer` would be a second runtime\n * dependency next to axios, and `reflect-metadata` cannot help at all here —\n * TypeScript 7 never emits `design:type`, so *no* library can discover a property\n * type without a compiler plugin. The declarations this plugin reads\n * (`@PropertyType`) are therefore not a workaround but the only runtime source of\n * truth available.\n *\n * ## Model\n *\n * ```text\n * hydrate(raw, DtoClass)\n * raw is a primitive / null / too deep → returned unchanged\n * raw is an array → one instance per item\n * DtoClass has static fromJSON → fromJSON(raw, ctx) wins outright\n * otherwise → new DtoClass() + declared properties\n * ```\n */\n\n/** Depth used when the caller does not set `maxDepth`. */\nexport const DEFAULT_MAX_DEPTH = 32;\n\n/** Options accepted by {@link hydrate}. */\nexport interface HydrateOptions {\n /** Keep JSON keys the DTO does not declare. Defaults to `false`. */\n keepUnknown?: boolean;\n\n /** Maximum object depth to descend. Defaults to {@link DEFAULT_MAX_DEPTH}. */\n maxDepth?: number;\n\n /** Forwarded to a DTO's `static fromJSON(raw, ctx)`. */\n ctx?: SnailContext;\n}\n\n/** {@link HydrateOptions} with every default applied. */\ninterface ResolvedHydrateOptions {\n keepUnknown: boolean;\n maxDepth: number;\n ctx: SnailContext | undefined;\n}\n\n/** `true` for an own (not inherited) property. */\nfunction hasOwn(target: object, key: string): boolean {\n return Object.prototype.hasOwnProperty.call(target, key);\n}\n\n/**\n * Revive a JSON date.\n *\n * A string that `new Date()` rejects is returned unchanged: an `Invalid Date` is\n * truthy, serialises to `null` and fails every later check somewhere far from the\n * cause, so keeping the original string at least leaves the bug where it is.\n */\nfunction reviveDate(value: unknown): unknown {\n if (value instanceof Date) return value;\n if (typeof value !== \"string\" && typeof value !== \"number\") return value;\n\n const date = new Date(value);\n return Number.isNaN(date.getTime()) ? value : date;\n}\n\n/**\n * The property names a DTO declares.\n *\n * Two sources, and both are needed:\n * - the property decorators, which are the only way to see a `field!: T`\n * declaration (TypeScript erases those at compile time);\n * - the instance's own keys, which cover initialised fields and constructor\n * assignments and let a partially decorated DTO keep working.\n *\n * An empty result means the class declares nothing at all — see\n * {@link buildInstance}.\n */\nfunction knownPropertyNames(DtoClass: DtoType, instance: object): Set<string> {\n const names = new Set<string>([\n ...collectMethodKeys(PROPERTY_TYPE_KEY, DtoClass),\n ...collectMethodKeys(EXPOSE_NAME_KEY, DtoClass)\n ]);\n\n for (const key of Object.keys(instance)) names.add(key);\n return names;\n}\n\n/** Shallow-copy every own key of the JSON onto the instance. */\nfunction copyOwnKeys(\n target: Record<string, unknown>,\n source: Record<string, unknown>\n): void {\n for (const key of Object.keys(source)) target[key] = source[key];\n}\n\n/**\n * Hydrate one property value using the type declared for it.\n *\n * `childDepth` is the depth of `value` itself, so the depth guard in\n * {@link hydrateInto} can stop the descent before the stack does.\n */\nfunction hydrateProperty(\n value: unknown,\n spec: PropertyTypeSpec | undefined,\n options: ResolvedHydrateOptions,\n childDepth: number,\n seen: WeakSet<object>\n): unknown {\n if (!spec || value === null || value === undefined) return value;\n\n const resolved = spec.type();\n\n if (spec.options?.array === true) {\n if (!Array.isArray(value)) return value;\n return value.map((item) =>\n hydrateValue(item, resolved, options, childDepth, seen)\n );\n }\n\n return hydrateValue(value, resolved, options, childDepth, seen);\n}\n\n/**\n * Hydrate a single value against a resolved constructor.\n *\n * Primitives need no work — `JSON.parse` already produced the right JavaScript\n * type — and a non-function resolver is treated as \"no type declared\" rather than\n * a crash, because a typo in a decorator must not break every response.\n */\nfunction hydrateValue(\n value: unknown,\n resolved: unknown,\n options: ResolvedHydrateOptions,\n depth: number,\n seen: WeakSet<object>\n): unknown {\n if (value === null || value === undefined) return value;\n if (resolved === Date) return reviveDate(value);\n if (resolved === String || resolved === Number || resolved === Boolean) return value;\n if (typeof resolved !== \"function\") return value;\n\n return hydrateInto(value, resolved as DtoType, options, depth, seen);\n}\n\n/**\n * Build one instance, honouring `fromJSON`, the declared properties and\n * `keepUnknown`.\n */\nfunction buildInstance<T>(\n raw: Record<string, unknown>,\n DtoClass: DtoType<T>,\n options: ResolvedHydrateOptions,\n depth: number,\n seen: WeakSet<object>\n): T {\n // A hand-written factory knows more than the decorators do, so it wins. It also\n // bypasses the whitelist: the class, not the JSON shape, decides its own fields.\n if (typeof DtoClass.fromJSON === \"function\") {\n return DtoClass.fromJSON(raw, options.ctx);\n }\n\n const instance = new DtoClass() as Record<string, unknown>;\n const known = knownPropertyNames(DtoClass, instance);\n\n // Nothing is declared: there is no whitelist to apply, so every own key of the\n // JSON is assigned. This is the documented behaviour for a DTO written as a bare\n // `class UserDto { id!: number }`.\n if (known.size === 0) {\n copyOwnKeys(instance, raw);\n return instance as T;\n }\n\n const consumed = new Set<string>();\n\n for (const property of known) {\n const jsonKey =\n getMetadata<string>(EXPOSE_NAME_KEY, DtoClass, property) ?? property;\n if (!hasOwn(raw, jsonKey)) continue;\n\n consumed.add(jsonKey);\n const spec = getMetadata<PropertyTypeSpec>(\n PROPERTY_TYPE_KEY,\n DtoClass,\n property\n );\n instance[property] = hydrateProperty(\n raw[jsonKey],\n spec,\n options,\n depth + 1,\n seen\n );\n }\n\n if (options.keepUnknown) {\n for (const key of Object.keys(raw)) {\n if (consumed.has(key)) continue;\n instance[key] = raw[key];\n }\n }\n\n return instance as T;\n}\n\n/**\n * Recursive worker behind {@link hydrate}.\n *\n * Three guards, each fixing a way this could hang or lie:\n * - a non-object is returned unchanged, so a primitive payload passes through;\n * - `depth > maxDepth` stops a self-referencing `@PropertyType` chain;\n * - `seen` stops a *cyclic* JSON graph, which depth alone would only delay\n * (branching recursion is exponential, not linear).\n */\nfunction hydrateInto<T>(\n raw: unknown,\n DtoClass: DtoType<T>,\n options: ResolvedHydrateOptions,\n depth: number,\n seen: WeakSet<object>\n): T {\n if (raw === null || typeof raw !== \"object\") return raw as T;\n if (depth > options.maxDepth) return raw as T;\n\n if (Array.isArray(raw)) {\n return raw.map((item) =>\n hydrateInto(item, DtoClass, options, depth + 1, seen)\n ) as T;\n }\n\n if (seen.has(raw)) return raw as T;\n\n seen.add(raw);\n try {\n return buildInstance(raw as Record<string, unknown>, DtoClass, options, depth, seen);\n } finally {\n // Removed again so a value referenced from two places is still hydrated in\n // both; only a cycle on the current path is short-circuited.\n seen.delete(raw);\n }\n}\n\n/**\n * Turn a plain JSON payload into an instance of `DtoClass`.\n *\n * A primitive, `null`, an unknown class or an over-deep value is returned\n * unchanged rather than wrapped: the caller asked for a class, but a response that\n * does not look like one is more useful as-is than as an empty instance.\n *\n * ```ts\n * const user = hydrate(raw, UserDto);\n * user instanceof UserDto; // true\n * ```\n */\nexport function hydrate<T>(\n raw: unknown,\n DtoClass: DtoType<T>,\n options: HydrateOptions = {}\n): T {\n return hydrateInto(\n raw,\n DtoClass,\n {\n keepUnknown: options.keepUnknown ?? false,\n maxDepth: options.maxDepth ?? DEFAULT_MAX_DEPTH,\n ctx: options.ctx\n },\n 0,\n new WeakSet<object>()\n );\n}\n","import { createPlugin } from \"../../core/plugin\";\nimport { looksLikeEnvelope, unwrapEnvelope } from \"../../core/response\";\nimport { t } from \"../../locale\";\nimport { resolveDto } from \"./decorators\";\nimport { DEFAULT_MAX_DEPTH, hydrate } from \"./hydrate\";\nimport type { SnailContext } from \"../../core/context\";\nimport type { DtoType, TransformOptions } from \"./type\";\n\n/**\n * JSON → class transform plugin.\n *\n * Replaces the unwrapped payload of a response with instances of the DTO declared\n * by `@Transform(DtoClass)` before the caller (or a strategy) ever sees it, so\n * `result.data instanceof UserDto` holds and methods on the DTO are callable.\n *\n * ## Why `afterResponse` and not after the result is built\n *\n * `afterResponse` runs while the response is still the source of truth: the\n * envelope validation, `buildResult` and the `success` event all read\n * `ctx.response`, so rewriting the response there means every later step — and\n * `result.data` in particular — is consistent without a second code path.\n *\n * ## Priority\n *\n * `0`. On the unwind side that puts it *after* the validate plugin (`-50`), which\n * is deliberate: the schema describes the JSON the backend sends, so validating\n * the hydrated instance instead would compare DTO instances against a JSON schema\n * and fail on every `Date`.\n */\n\n/** Transform options with defaults applied, captured once per server. */\ninterface ResolvedTransform {\n keepUnknown: boolean;\n maxDepth: number;\n}\n\n/**\n * Hydrate the live response payload in place (by replacing the response body).\n *\n * A hydration failure must not corrupt the response: a throwing DTO constructor or\n * a broken `fromJSON` is reported and the raw JSON is left exactly as it arrived,\n * because a partially hydrated body is worse than an unhydrated one.\n */\nfunction transformResponse(\n ctx: SnailContext,\n Dto: DtoType,\n options: ResolvedTransform\n): void {\n const response = ctx.getResponse();\n if (!response) return;\n\n const { dataKey } = ctx.serverOptions;\n\n try {\n const envelope = response.data;\n const payload = unwrapEnvelope(envelope, dataKey);\n const hydrated = hydrate(payload, Dto, {\n keepUnknown: options.keepUnknown,\n maxDepth: options.maxDepth,\n ctx\n });\n\n // A raw (non-envelope) body has no key to rewrite, so the whole body is\n // replaced instead of digging for a `data` field that does not exist.\n ctx.setResponse({\n ...response,\n data: looksLikeEnvelope(envelope, dataKey)\n ? { ...(envelope as Record<string, unknown>), [dataKey]: hydrated }\n : hydrated\n });\n } catch (error) {\n ctx.logger.warn(t(\"error.plugin.transform\", ctx.fullName, String(error)));\n }\n}\n\n/**\n * Create the transform plugin.\n *\n * ```ts\n * Service.use(Transform());\n *\n * @Api(\"/user\")\n * @Transform(UserDto)\n * class UserApi {\n * @Get(\"/:id\")\n * getUser(@Params(\"id\") id: string): Promise<UserDto> { return null!; }\n * }\n * ```\n *\n * Without a DTO — no decorator and no `dto` option — the plugin is a no-op and the\n * payload stays the plain object JSON.parse produced.\n */\nexport const transformPlugin = createPlugin<TransformOptions>({\n name: \"transform\",\n priority: 0,\n\n setup(options) {\n const fallbackDto = options?.dto;\n const resolved: ResolvedTransform = {\n keepUnknown: options?.keepUnknown ?? false,\n maxDepth: options?.maxDepth ?? DEFAULT_MAX_DEPTH\n };\n\n return {\n afterResponse(ctx, next) {\n const Dto = resolveDto(ctx.apiClass, ctx.methodName) ?? fallbackDto;\n if (Dto) transformResponse(ctx, Dto, resolved);\n return next();\n }\n };\n }\n});\n","import { Transform as TransformDto, PropertyType, ExposeName } from \"./decorators\";\nimport { transformPlugin } from \"./plugin\";\nimport type { SnailPluginObject } from \"../../typings/plugin\";\nimport type { DtoType, TransformOptions } from \"./type\";\n\n/**\n * JSON → class transform plugin.\n *\n * ```ts\n * import { Transform, PropertyType, ExposeName } from \"@snail-js/api/plugins\";\n *\n * Service.use(Transform());\n *\n * class UserDto {\n * @ExposeName(\"user_name\") userName!: string;\n * @PropertyType(() => Date) createdAt!: Date;\n * }\n *\n * @Api(\"/user\")\n * @Transform(UserDto)\n * class UserApi {\n * @Get(\"/:id\")\n * getUser(@Params(\"id\") id: string): Promise<UserDto> { return null!; }\n * }\n * ```\n *\n * @packageDocumentation\n */\n\n/**\n * The transform plugin factory, and — called with a DTO class — the DTO decorator.\n *\n * Two things share one name because the specification gives both of them the name\n * `Transform`: the factory (`Service.use(Transform())`) and the decorator\n * (`@Transform(UserDto)`). They are told apart by their argument — a DTO is a\n * constructor function with no options of its own, options are never callable —\n * so neither call style has to be renamed.\n */\nexport function Transform(dto: DtoType): ClassDecorator & MethodDecorator;\nexport function Transform(options?: TransformOptions): SnailPluginObject<TransformOptions>;\nexport function Transform(\n input?: DtoType | TransformOptions\n): ClassDecorator & MethodDecorator | SnailPluginObject<TransformOptions> {\n return typeof input === \"function\" ? TransformDto(input) : transformPlugin(input);\n}\n\nexport { ExposeName, PropertyType } from \"./decorators\";\nexport { hydrate } from \"./hydrate\";\nexport type { HydrateOptions } from \"./hydrate\";\nexport type {\n DtoType,\n PropertyTypeOptions,\n PropertyTypeSpec,\n TransformOptions\n} from \"./type\";\n","import { defineMetadata, getMetadata } from \"../../core/metadata\";\nimport { customMetadataKey } from \"../../decorators/custom\";\nimport { SnailDecoratorError } from \"../../error/decorator\";\nimport type { ZodType } from \"zod\";\n\n/**\n * `@Validate(schema)` / `@ValidateResponse(schema)` — attach a zod schema to a\n * request or to a response body.\n *\n * Both work on an api class and on a single method. A class-level schema applies\n * to every method of the class; a method-level one overrides it, which is the only\n * ordering that lets one endpoint deviate without redeclaring the class default on\n * every sibling.\n */\n\n/** Metadata slot written by `@Validate`. */\nconst REQUEST_SCHEMA_KEY = customMetadataKey(\"validate/request\");\n\n/** Metadata slot written by `@ValidateResponse`. */\nconst RESPONSE_SCHEMA_KEY = customMetadataKey(\"validate/response\");\n\n/**\n * Build a class/method decorator that stores one schema.\n *\n * The value is checked at decoration time, not at request time: a typo like\n * `@Validate({})` would otherwise stay silent until a request ran, and then read\n * as \"the payload is malformed\" instead of \"the decorator is wrong\".\n */\nfunction schemaDecorator(\n key: symbol,\n label: string\n): (schema: ZodType) => ClassDecorator & MethodDecorator {\n return (schema: ZodType) => {\n if (!schema || typeof schema.safeParse !== \"function\") {\n throw new SnailDecoratorError(\n `[snail] @${label}() expects a zod schema with a safeParse() method`\n );\n }\n\n return ((target: unknown, propertyKey?: string | symbol) => {\n defineMetadata(key, schema, target, propertyKey);\n }) as ClassDecorator & MethodDecorator;\n };\n}\n\nconst defineRequestSchema = schemaDecorator(REQUEST_SCHEMA_KEY, \"Validate\");\nconst defineResponseSchema = schemaDecorator(RESPONSE_SCHEMA_KEY, \"ValidateResponse\");\n\n/**\n * Validate the outgoing request body (or query) against a zod schema.\n *\n * ```ts\n * @Post(\"/\")\n * @Validate(z.object({ name: z.string().min(1) }))\n * create(@Data() body: CreateUser): Promise<User> { return null!; }\n * ```\n */\nexport function Validate(schema: ZodType): ClassDecorator & MethodDecorator {\n return defineRequestSchema(schema);\n}\n\n/**\n * Validate the response payload against a zod schema.\n *\n * Response validation only ever warns: the backend, not the caller, decides what\n * it sends, and throwing away a usable payload because one field is unexpected\n * turns a cosmetic backend drift into a broken page.\n */\nexport function ValidateResponse(schema: ZodType): ClassDecorator & MethodDecorator {\n return defineResponseSchema(schema);\n}\n\n/** Read the request schema of one method: method first, then the api class. */\nexport function resolveRequestSchema(\n apiClass: unknown,\n methodName: string\n): ZodType | undefined {\n return (\n getMetadata<ZodType>(REQUEST_SCHEMA_KEY, apiClass, methodName) ??\n getMetadata<ZodType>(REQUEST_SCHEMA_KEY, apiClass)\n );\n}\n\n/** Read the response schema of one method: method first, then the api class. */\nexport function resolveResponseSchema(\n apiClass: unknown,\n methodName: string\n): ZodType | undefined {\n return (\n getMetadata<ZodType>(RESPONSE_SCHEMA_KEY, apiClass, methodName) ??\n getMetadata<ZodType>(RESPONSE_SCHEMA_KEY, apiClass)\n );\n}\n","import type { ZodType, core } from \"zod\";\nimport { SnailError } from \"../../error/base\";\n\n/**\n * Zod validation plugin types.\n *\n * `zod` is an optional peer dependency and this directory is the only place in the\n * library allowed to reference it. Everything here is a *type* reference, so\n * importing the plugin never pulls zod's runtime into the bundle: the schemas are\n * supplied by the application, which already has zod.\n */\n\n/**\n * One problem zod reported.\n *\n * Re-exported under a library-owned name so application code can type an error\n * handler without importing zod's internal `core` namespace — the deprecation\n * churn of that namespace should not reach callers through this library.\n */\nexport type SnailValidationIssue = core.$ZodIssue;\n\n/** Options accepted by the `Validate` plugin factory. */\nexport interface ValidateOptions {\n /** Schema applied when no `@Validate()` decorator declares one. */\n request?: ZodType;\n\n /** Schema applied when no `@ValidateResponse()` decorator declares one. */\n response?: ZodType;\n\n /**\n * Whether an invalid *request* aborts the call.\n *\n * Defaults to `true`. `false` downgrades the failure to a warning and lets the\n * request go out — useful while developing against a backend whose contract is\n * still moving, and never a good idea in production. Responses are never\n * validated strictly.\n */\n strict?: boolean;\n}\n\n/**\n * Thrown when the request payload does not satisfy its schema.\n *\n * The request is abandoned **before** anything reaches the network, which is the\n * whole point of validating a request: an invalid body that is still sent produces\n * a backend error that looks like a server bug, and a `4xx` that pollutes the\n * application's error handling for what is really a programmer mistake.\n *\n * `issues` is zod's own list, kept verbatim so the application can render field\n * paths and messages instead of parsing a string.\n */\nexport class SnailValidationError extends SnailError {\n /** Every issue zod reported, in zod's original shape. */\n readonly issues: readonly SnailValidationIssue[];\n\n constructor(\n message: string,\n options: { issues: readonly SnailValidationIssue[]; cause?: unknown }\n ) {\n super(message, { code: \"SNAIL_VALIDATION_ERROR\", cause: options.cause });\n this.issues = options.issues;\n }\n}\n","import { createPlugin } from \"../../core/plugin\";\nimport { unwrapEnvelope } from \"../../core/response\";\nimport { t } from \"../../locale\";\nimport { resolveRequestSchema, resolveResponseSchema } from \"./decorators\";\nimport { SnailValidationError } from \"./type\";\nimport type { ZodType } from \"zod\";\nimport type { SnailContext } from \"../../core/context\";\nimport type { ValidateOptions } from \"./type\";\n\n/**\n * Zod validation plugin.\n *\n * ## The asymmetry, and why it is deliberate\n *\n * **An invalid request aborts the call.** The request never reaches the network:\n * a body that fails its own schema is a programmer mistake, and the backend error\n * it would produce points at the wrong layer.\n *\n * **An invalid response only warns.** The response is already here and the caller\n * asked for it; throwing it away because the backend added, renamed or retyped a\n * field turns a cosmetic drift into a broken page. The warning carries zod's\n * issues so the drift is still visible in the console.\n *\n * ## Priority\n *\n * `-50` — the reserved validate band. In the forward phase it runs after the\n * version (`50`) and the adapters (`0`) have finished shaping the request, and\n * before the cache (`-100`) is asked for a key. On the unwind side it runs after\n * the cache and before the adapters, so the response is validated *before* the\n * transform plugin replaces the payload with class instances.\n */\n\n/**\n * The value a request schema validates.\n *\n * Write verbs carry a body, read verbs carry query params. Validating the body\n * when there is one and the query otherwise means one decorator covers both, and\n * a request that carries neither (`undefined`) is left to the backend rather than\n * failing against an empty object.\n */\nfunction requestTarget(ctx: SnailContext): unknown {\n return ctx.request.data !== undefined ? ctx.request.data : ctx.request.params;\n}\n\n/**\n * Validate the outgoing request, throwing when `strict`.\n *\n * Never calls `next()` on failure: the chain ends, `send()` rejects with the\n * {@link SnailValidationError}, and no axios adapter is ever reached.\n */\nfunction validateRequest(\n ctx: SnailContext,\n schema: ZodType,\n strict: boolean,\n next: () => Promise<void>\n): Promise<void> | void {\n const target = requestTarget(ctx);\n if (target === undefined) return next();\n\n const parsed = schema.safeParse(target);\n if (parsed.success) return next();\n\n const message = t(\"error.plugin.validate.request\", ctx.fullName);\n\n if (!strict) {\n ctx.logger.warn(message, parsed.error.issues);\n return next();\n }\n\n throw new SnailValidationError(message, { issues: parsed.error.issues });\n}\n\n/**\n * Validate the response payload and warn about it.\n *\n * A throwing logger or a malformed schema must not fail the request, so the whole\n * check is defensive: the caller still receives the payload no matter what.\n */\nfunction warnOnInvalidResponse(ctx: SnailContext, schema: ZodType): void {\n try {\n const response = ctx.getResponse();\n if (!response) return;\n\n const payload = unwrapEnvelope(response.data, ctx.serverOptions.dataKey);\n const parsed = schema.safeParse(payload);\n if (parsed.success) return;\n\n ctx.logger.warn(\n t(\"error.plugin.validate.response\", ctx.fullName),\n parsed.error.issues\n );\n } catch (error) {\n ctx.logger.warn(t(\"error.plugin.validate.response\", ctx.fullName), error);\n }\n}\n\n/**\n * Create the zod validation plugin.\n *\n * ```ts\n * Service.use(Validate({ strict: false }));\n * ```\n *\n * It is normally used without options, together with the decorators:\n * `Service.use(Validate())`.\n */\nexport const validatePlugin = createPlugin<ValidateOptions>({\n name: \"validate\",\n priority: -50,\n\n setup(options) {\n const fallbackRequest = options?.request;\n const fallbackResponse = options?.response;\n const strict = options?.strict ?? true;\n\n return {\n beforeRequest(ctx, next) {\n const schema =\n resolveRequestSchema(ctx.apiClass, ctx.methodName) ?? fallbackRequest;\n if (!schema) return next();\n return validateRequest(ctx, schema, strict, next);\n },\n\n afterResponse(ctx, next) {\n const schema =\n resolveResponseSchema(ctx.apiClass, ctx.methodName) ?? fallbackResponse;\n if (schema) warnOnInvalidResponse(ctx, schema);\n return next();\n }\n };\n }\n});\n","import { Validate as ValidateRequest } from \"./decorators\";\nimport { validatePlugin } from \"./plugin\";\nimport type { ZodType } from \"zod\";\nimport type { SnailPluginObject } from \"../../typings/plugin\";\nimport type { ValidateOptions } from \"./type\";\n\n/**\n * Zod validation plugin.\n *\n * ```ts\n * import { Validate, ValidateResponse } from \"@snail-js/api/plugins\";\n *\n * Service.use(Validate());\n *\n * @Api(\"/user\")\n * class UserApi {\n * @Post(\"/\")\n * @Validate(z.object({ name: z.string().min(1) }))\n * @ValidateResponse(z.object({ id: z.number() }))\n * create(@Data() body: CreateUser): Promise<User> { return null!; }\n * }\n * ```\n *\n * @packageDocumentation\n */\n\n/**\n * The validate plugin factory, and — called with a zod schema — the request\n * schema decorator.\n *\n * Two things share one name because the brief that specifies this plugin gives\n * both of them the name `Validate`: the factory (`Service.use(Validate())`) and\n * the decorator (`@Validate(schema)`). Rather than rename one of them and break\n * one of the two call styles, the two are told apart by their argument: a zod\n * schema is an object with `safeParse`, options never have one. `Validate()` and\n * `Validate({ request: schema })` return the plugin, `@Validate(schema)` returns\n * the decorator.\n */\nexport function Validate(schema: ZodType): ClassDecorator & MethodDecorator;\nexport function Validate(options?: ValidateOptions): SnailPluginObject<ValidateOptions>;\nexport function Validate(\n input?: ZodType | ValidateOptions\n): ClassDecorator & MethodDecorator | SnailPluginObject<ValidateOptions> {\n return isZodSchema(input) ? ValidateRequest(input) : validatePlugin(input);\n}\n\n/** `true` for a zod schema — the only shape the decorator overload accepts. */\nfunction isZodSchema(value: unknown): value is ZodType {\n return (\n typeof (value as { safeParse?: unknown } | undefined)?.safeParse === \"function\"\n );\n}\n\nexport { ValidateResponse } from \"./decorators\";\nexport { SnailValidationError } from \"./type\";\nexport type { SnailValidationIssue, ValidateOptions } from \"./type\";\n","import { defineMetadata, getMetadata } from \"../../core/metadata\";\nimport { customMetadataKey } from \"../../decorators/custom\";\nimport { SnailDecoratorError } from \"../../error/decorator\";\n\n/**\n * `@Version(...)` — declare the api version of a class or of one method.\n *\n * Method wins over class, class wins over the plugin's `defaultVersion`, which is\n * the only ordering that lets one endpoint of a class deviate without forcing\n * every sibling to restate the class version.\n *\n * The decorator is intentionally metadata-only: it never touches the request or\n * the server, so decorating a class from a shared module cannot leak a version\n * into another server instance.\n */\n\n/**\n * Metadata slot written by `@Version`.\n *\n * One key covers both levels: a class decorator stores under the class slot and a\n * method decorator under the method name, so a reader never has to know which\n * decorator wrote the value.\n */\nconst VERSION_KEY = customMetadataKey(\"versioning/version\");\n\n/**\n * Declare the version of an api class or of a single request method.\n *\n * ```ts\n * @Api(\"/user\")\n * @Version(\"1.2.0\")\n * class UserApi {\n * @Get(\"/legacy\")\n * @Version(\"0.9.0\")\n * legacy(): Promise<void> { return null!; }\n * }\n * ```\n */\nexport function Version(version: string): ClassDecorator & MethodDecorator {\n if (typeof version !== \"string\" || version.length === 0) {\n throw new SnailDecoratorError(\n \"[snail] @Version() requires a non-empty version string\"\n );\n }\n\n return ((target: unknown, propertyKey?: string | symbol) => {\n defineMetadata(VERSION_KEY, version, target, propertyKey);\n }) as ClassDecorator & MethodDecorator;\n}\n\n/**\n * Read the version declared for one method.\n *\n * Reads the method slot first, then the class slot, and both walk the prototype\n * chain so a subclass inherits the version of its base api class.\n */\nexport function resolveDeclaredVersion(\n apiClass: unknown,\n methodName: string\n): string | undefined {\n return (\n getMetadata<string>(VERSION_KEY, apiClass, methodName) ??\n getMetadata<string>(VERSION_KEY, apiClass)\n );\n}\n","import { AxiosHeaders } from \"axios\";\nimport { SnailPluginError } from \"../../error/plugin\";\nimport { createPlugin } from \"../../core/plugin\";\nimport { t } from \"../../locale\";\nimport { isPlainObject } from \"../../utils/is\";\nimport { isAbsoluteURL } from \"../../utils/url\";\nimport { resolveDeclaredVersion } from \"./decorators\";\nimport type { SnailContext } from \"../../core/context\";\nimport type { VersioningOptions, VersioningPatch, VersioningType } from \"./type\";\n\n/**\n * Version management plugin.\n *\n * ## What it rewrites\n *\n * Only the live request on the context — never `server.defaults.baseURL`, never\n * the shared axios instance.\n *\n * The pre-rewrite implementation baked the version into the server's `baseURL`\n * the first time any request ran. That leaked: the first method to run decided\n * the version of every later request on that server, so calling a `v2` endpoint\n * once silently moved the whole application to `v2`. Resolving the version per\n * request from the method/class metadata makes that unrepresentable.\n *\n * ## Priority\n *\n * `50` — inside the reserved version band. It runs after an interceptor (`100`)\n * so it sees the url the interceptor produced, and before the cache (`-100`) so\n * the cache hashes the versioned url instead of a url that changes under it.\n */\n\n/**\n * Default key or url segment name per transport.\n *\n * `url` keeps the conventional `v` marker, so a bare\n * `Versioning({ type: \"url\", defaultVersion: \"1.0.0\" })` turns `/user/1` into\n * `/v1.0.0/user/1`. Pass `key: \"\"` for a bare `/1.0.0/user/1` segment.\n */\nconst DEFAULT_KEYS: Record<Exclude<VersioningType, \"custom\">, string> = {\n url: \"v\",\n header: \"x-api-version\",\n query: \"v\"\n};\n\n/** Options after defaults are applied, so the hot path never re-reads them. */\ninterface ResolvedVersioning {\n type: VersioningType;\n defaultVersion: string;\n key: string;\n extractor?: VersioningOptions[\"extractor\"];\n}\n\n/**\n * Validate and normalise the factory options.\n *\n * Runs once per server, inside `setup`. Throwing here rolls the registration back,\n * so a misconfigured plugin is never half-installed and silently doing nothing.\n */\nfunction resolveVersioningOptions(\n options: VersioningOptions | undefined\n): ResolvedVersioning {\n const type = options?.type ?? \"url\";\n\n if (type !== \"url\" && type !== \"header\" && type !== \"query\" && type !== \"custom\") {\n throw new SnailPluginError(\n `[snail] Versioning() received an unknown type \"${String(type)}\"`,\n { pluginName: \"versioning\" }\n );\n }\n\n const defaultVersion = options?.defaultVersion;\n if (typeof defaultVersion !== \"string\" || defaultVersion.length === 0) {\n throw new SnailPluginError(\n \"[snail] Versioning() requires a non-empty `defaultVersion`\",\n { pluginName: \"versioning\" }\n );\n }\n\n if (type === \"custom\" && typeof options?.extractor !== \"function\") {\n throw new SnailPluginError(\n '[snail] Versioning({ type: \"custom\" }) requires an `extractor` function',\n { pluginName: \"versioning\" }\n );\n }\n\n return {\n type,\n defaultVersion,\n key:\n type === \"custom\"\n ? (options?.key ?? \"\")\n : (options?.key ?? DEFAULT_KEYS[type]),\n extractor: options?.extractor\n };\n}\n\n/**\n * The mutable header bag of the request, created when a plugin or a decorator\n * has not produced an `AxiosHeaders` yet.\n */\nfunction headerBag(ctx: SnailContext): AxiosHeaders {\n if (!(ctx.request.headers instanceof AxiosHeaders)) {\n ctx.request.headers = AxiosHeaders.from(ctx.request.headers ?? {});\n }\n return ctx.request.headers;\n}\n\n/** `true` when `segment` already is one of the url's path segments. */\nfunction hasSegment(url: string, segment: string): boolean {\n const path = url.split(/[?#]/, 1)[0] ?? \"\";\n return path.split(\"/\").includes(segment);\n}\n\n/**\n * Prepend `<key><version>` as the first path segment.\n *\n * Absolute urls are left untouched — prefixing them would corrupt the host — and\n * a url that already carries the segment is returned as-is, so a method whose path\n * hard-codes the version is not rewritten twice.\n */\nfunction applyURLVersion(url: string, version: string, key: string): string {\n const segment = `${key}${version}`;\n if (isAbsoluteURL(url) || hasSegment(url, segment)) return url;\n if (url.length === 0) return `/${segment}`;\n return `/${segment}${url.startsWith(\"/\") ? \"\" : \"/\"}${url}`;\n}\n\n/** Write the version header, replacing any value the caller already set. */\nfunction applyHeaderVersion(ctx: SnailContext, version: string, key: string): void {\n headerBag(ctx).set(key, version);\n}\n\n/** Merge the version into the query params without dropping the existing ones. */\nfunction applyQueryVersion(ctx: SnailContext, version: string, key: string): void {\n const current = ctx.request.params;\n ctx.request.params = {\n ...(isPlainObject(current) ? current : {}),\n [key]: version\n };\n}\n\n/** Delegate to the caller's extractor and merge whatever patch it returns. */\nfunction applyCustomVersion(\n ctx: SnailContext,\n version: string,\n extractor: NonNullable<VersioningOptions[\"extractor\"]>\n): void {\n const patch = extractor(version, ctx) as VersioningPatch | void;\n if (!patch) return;\n\n if (typeof patch.url === \"string\") ctx.request.url = patch.url;\n\n if (patch.headers) {\n const headers = headerBag(ctx);\n for (const [key, value] of Object.entries(patch.headers)) {\n headers.set(key, value);\n }\n }\n\n if (patch.params) {\n const current = ctx.request.params;\n ctx.request.params = {\n ...(isPlainObject(current) ? current : {}),\n ...patch.params\n };\n }\n}\n\n/**\n * Resolve the effective version and rewrite the live request.\n *\n * The default version is applied too, not skipped: the backend wants `/v1.0.0`\n * for an undeclared method, and nothing else in the pipeline knows that. Only the\n * *logging* distinguishes an explicit version from the default.\n */\nfunction applyVersion(ctx: SnailContext, options: ResolvedVersioning): void {\n const version =\n resolveDeclaredVersion(ctx.apiClass, ctx.methodName) ?? options.defaultVersion;\n\n if (version !== options.defaultVersion) {\n ctx.logger.info(t(\"info.version.change\", ctx.fullName, version));\n ctx.logger.warn(\n t(\"warn.version.change\", ctx.fullName, options.defaultVersion, version)\n );\n }\n\n switch (options.type) {\n case \"url\":\n ctx.request.url = applyURLVersion(\n ctx.request.url ?? ctx.route,\n version,\n options.key\n );\n return;\n case \"header\":\n applyHeaderVersion(ctx, version, options.key);\n return;\n case \"query\":\n applyQueryVersion(ctx, version, options.key);\n return;\n case \"custom\":\n applyCustomVersion(ctx, version, options.extractor!);\n return;\n }\n}\n\n/**\n * Create the version management plugin.\n *\n * ```ts\n * Service.use(Versioning({ type: \"url\", defaultVersion: \"1.0.0\" }));\n * ```\n *\n * The rewrite happens in `beforeRequest`, so it is the last thing that can change\n * the url before the request is hashed, cached and sent.\n */\nexport const Versioning = createPlugin<VersioningOptions>({\n name: \"versioning\",\n priority: 50,\n\n setup(options) {\n const resolved = resolveVersioningOptions(options);\n\n return {\n beforeRequest(ctx, next) {\n applyVersion(ctx, resolved);\n return next();\n }\n };\n }\n});\n"],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { AxiosResponse, InternalAxiosRequestConfig } from "axios";
|
|
2
|
+
import type { SnailContext } from "../../core/context.js";
|
|
3
|
+
import type { InterceptorEntry } from "./type.js";
|
|
4
|
+
type AnyEntry = InterceptorEntry<any>;
|
|
5
|
+
type DualDecorator = ClassDecorator & MethodDecorator;
|
|
6
|
+
/**
|
|
7
|
+
* Run `onFulfilled` on the request config of this class / this method, in series
|
|
8
|
+
* with every other request interceptor.
|
|
9
|
+
*
|
|
10
|
+
* ```ts
|
|
11
|
+
* @Api("/user")
|
|
12
|
+
* @BeforeRequest<UserConfig>((config) => { config.headers.set("x-trace", "1"); })
|
|
13
|
+
* class UserApi {
|
|
14
|
+
* @Get("/")
|
|
15
|
+
* @BeforeRequest((config) => { config.timeout = 5000; })
|
|
16
|
+
* list(): Promise<User[]> { return null!; }
|
|
17
|
+
* }
|
|
18
|
+
* ```
|
|
19
|
+
*
|
|
20
|
+
* Class-level interceptors run before method-level ones, so a method may refine
|
|
21
|
+
* what its class established. `onRejected` may recover a failure by returning a
|
|
22
|
+
* replacement config; returning `undefined` (or throwing) lets the failure
|
|
23
|
+
* through, and the request is then never sent.
|
|
24
|
+
*/
|
|
25
|
+
export declare function BeforeRequest<T = InternalAxiosRequestConfig>(onFulfilled: (value: T, ctx: SnailContext) => T | void | Promise<T | void>, onRejected?: (error: unknown, ctx: SnailContext) => unknown): DualDecorator;
|
|
26
|
+
/**
|
|
27
|
+
* Run `onFulfilled` against the response of this class / this method.
|
|
28
|
+
*
|
|
29
|
+
* The callback receives the axios response, **not** a `next`: unlike
|
|
30
|
+
* `beforeRequest`, this phase is an unwind hook, so the response already exists
|
|
31
|
+
* and nothing here decides whether the request happens. Return a replacement
|
|
32
|
+
* response, or `undefined` to keep the one that was passed in.
|
|
33
|
+
*/
|
|
34
|
+
export declare function AfterResponse<T = AxiosResponse>(onFulfilled: (value: T, ctx: SnailContext) => T | void | Promise<T | void>, onRejected?: (error: unknown, ctx: SnailContext) => unknown): DualDecorator;
|
|
35
|
+
/** Class-level `@BeforeRequest` entries, in application order. */
|
|
36
|
+
export declare function classBeforeEntries(target: unknown): AnyEntry[];
|
|
37
|
+
/** Method-level `@BeforeRequest` entries, in application order. */
|
|
38
|
+
export declare function methodBeforeEntries(target: unknown, methodName: string): AnyEntry[];
|
|
39
|
+
/** Class-level `@AfterResponse` entries, in application order. */
|
|
40
|
+
export declare function classAfterEntries(target: unknown): AnyEntry[];
|
|
41
|
+
/** Method-level `@AfterResponse` entries, in application order. */
|
|
42
|
+
export declare function methodAfterEntries(target: unknown, methodName: string): AnyEntry[];
|
|
43
|
+
export {};
|
|
44
|
+
//# sourceMappingURL=decorators.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decorators.d.ts","sourceRoot":"","sources":["../../../src/plugins/interceptor/decorators.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,0BAA0B,EAAE,MAAM,OAAO,CAAC;AACvE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAQvD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,QAAQ,CAAC;AA0B/C,KAAK,QAAQ,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;AACtC,KAAK,aAAa,GAAG,cAAc,GAAG,eAAe,CAAC;AAiCtD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,aAAa,CAAC,CAAC,GAAG,0BAA0B,EAC1D,WAAW,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,GAAG,EAAE,YAAY,KAAK,CAAC,GAAG,IAAI,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,EAC1E,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,YAAY,KAAK,OAAO,GAC1D,aAAa,CAMf;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,CAAC,GAAG,aAAa,EAC7C,WAAW,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,GAAG,EAAE,YAAY,KAAK,CAAC,GAAG,IAAI,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,EAC1E,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,YAAY,KAAK,OAAO,GAC1D,aAAa,CAMf;AAED,kEAAkE;AAClE,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,OAAO,GAAG,QAAQ,EAAE,CAE9D;AAED,mEAAmE;AACnE,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,GAAG,QAAQ,EAAE,CAEnF;AAED,kEAAkE;AAClE,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,OAAO,GAAG,QAAQ,EAAE,CAE7D;AAED,mEAAmE;AACnE,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,GAAG,QAAQ,EAAE,CAElF"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interceptor plugin.
|
|
3
|
+
*
|
|
4
|
+
* ```ts
|
|
5
|
+
* import { Interceptor, BeforeRequest, AfterResponse } from "@snail-js/api/plugins";
|
|
6
|
+
*
|
|
7
|
+
* const interceptors = Interceptor();
|
|
8
|
+
* Service.use(interceptors);
|
|
9
|
+
* interceptors.request.use({ onFulfilled: (config) => { config.timeout = 5000; } });
|
|
10
|
+
*
|
|
11
|
+
* @Api("/user")
|
|
12
|
+
* @BeforeRequest((config, ctx) => { config.headers.set("x-trace", ctx.fullName); })
|
|
13
|
+
* class UserApi {
|
|
14
|
+
* @Get("/:id")
|
|
15
|
+
* getUser(@Params("id") id: string): Promise<User> { return null!; }
|
|
16
|
+
* }
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* Runs on the plugin lifecycle, never on `axios.interceptors`: an axios
|
|
20
|
+
* interceptor lives on the instance, so it can neither target one method nor be
|
|
21
|
+
* ordered against the other plugins. `priority: 100` is the reserved interceptor
|
|
22
|
+
* band, so `@BeforeRequest()` always sees the request before the cache hashes it.
|
|
23
|
+
*
|
|
24
|
+
* @packageDocumentation
|
|
25
|
+
*/
|
|
26
|
+
export { AfterResponse, BeforeRequest, classAfterEntries, classBeforeEntries, methodAfterEntries, methodBeforeEntries } from "./decorators.js";
|
|
27
|
+
export { InterceptorManager } from "./manager.js";
|
|
28
|
+
export { INTERCEPTOR_PLUGIN_NAME, INTERCEPTOR_PRIORITY, Interceptor } from "./plugin.js";
|
|
29
|
+
export type { InterceptorOptions, InterceptorPlugin } from "./plugin.js";
|
|
30
|
+
export type { InterceptorEntry, RequestInterceptorEntry, ResponseInterceptorEntry } from "./type.js";
|
|
31
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/plugins/interceptor/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,EACL,aAAa,EACb,aAAa,EACb,iBAAiB,EACjB,kBAAkB,EAClB,kBAAkB,EAClB,mBAAmB,EACpB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,EAAE,uBAAuB,EAAE,oBAAoB,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACtF,YAAY,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AACtE,YAAY,EACV,gBAAgB,EAChB,uBAAuB,EACvB,wBAAwB,EACzB,MAAM,QAAQ,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { InterceptorEntry } from "./type.js";
|
|
2
|
+
/**
|
|
3
|
+
* Runtime registry of programmatic interceptors.
|
|
4
|
+
*
|
|
5
|
+
* Decorators cover interceptors that are known at class-definition time. This
|
|
6
|
+
* class covers the other half — an application that decides at runtime "from now
|
|
7
|
+
* on, sign every request" — and it is also what the plugin uses internally to
|
|
8
|
+
* hold the server-wide entries passed to `Interceptor({ request })`.
|
|
9
|
+
*
|
|
10
|
+
* Entries keep insertion order: that order *is* the execution order, since the
|
|
11
|
+
* interceptor contract is deliberately sequential rather than priority based.
|
|
12
|
+
*/
|
|
13
|
+
export declare class InterceptorManager<T = unknown> {
|
|
14
|
+
private readonly registry;
|
|
15
|
+
private sequence;
|
|
16
|
+
/** Register an entry. Returns the id {@link eject} needs to remove it again. */
|
|
17
|
+
use(entry: InterceptorEntry<T>): number;
|
|
18
|
+
/** Remove an entry by the id returned from {@link use}. Returns whether it existed. */
|
|
19
|
+
eject(id: number): boolean;
|
|
20
|
+
/** Drop every entry. */
|
|
21
|
+
clear(): void;
|
|
22
|
+
/** Registered entries, in execution order. */
|
|
23
|
+
get entries(): InterceptorEntry<T>[];
|
|
24
|
+
/** Number of registered entries. */
|
|
25
|
+
get size(): number;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../../src/plugins/interceptor/manager.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,QAAQ,CAAC;AAE/C;;;;;;;;;;GAUG;AACH,qBAAa,kBAAkB,CAAC,CAAC,GAAG,OAAO;IACzC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA0C;IACnE,OAAO,CAAC,QAAQ,CAAK;IAErB,gFAAgF;IAChF,GAAG,CAAC,KAAK,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAAG,MAAM,CAatC;IAED,uFAAuF;IACvF,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAEzB;IAED,wBAAwB;IACxB,KAAK,IAAI,IAAI,CAEZ;IAED,8CAA8C;IAC9C,IAAI,OAAO,IAAI,gBAAgB,CAAC,CAAC,CAAC,EAAE,CAEnC;IAED,oCAAoC;IACpC,IAAI,IAAI,IAAI,MAAM,CAEjB;CACF"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { AxiosResponse, InternalAxiosRequestConfig } from "axios";
|
|
2
|
+
import type { SnailPluginObject } from "../../typings/plugin.js";
|
|
3
|
+
import { InterceptorManager } from "./manager.js";
|
|
4
|
+
import type { InterceptorEntry } from "./type.js";
|
|
5
|
+
/**
|
|
6
|
+
* The interceptor plugin.
|
|
7
|
+
*
|
|
8
|
+
* ## Where it sits in the pipeline
|
|
9
|
+
*
|
|
10
|
+
* `priority: 100` is the reserved interceptor band, so this plugin is the first
|
|
11
|
+
* to see the request in forward order and the last to see the response in unwind
|
|
12
|
+
* order. That is what lets `@BeforeRequest()` rewrite the config *before* the
|
|
13
|
+
* cache plugin (`-100`) hashes the final url, params and body into a cache key.
|
|
14
|
+
*
|
|
15
|
+
* ## Request vs response
|
|
16
|
+
*
|
|
17
|
+
* `beforeRequest` is a chain hook: the interceptors run first, and only then
|
|
18
|
+
* does the hook hand control on with `next()`. An interceptor that fails
|
|
19
|
+
* unrecoverably therefore stops the request before anything reaches the network.
|
|
20
|
+
*
|
|
21
|
+
* `afterResponse` is an **unwind** hook. The response already exists when it
|
|
22
|
+
* runs, so calling `next()` *first* would let later plugins observe a response
|
|
23
|
+
* the interceptors had not rewritten yet. The interceptors run against
|
|
24
|
+
* `ctx.response` first and the chain is advanced afterwards. The interceptor
|
|
25
|
+
* callbacks themselves are not chain hooks — they receive the response rather
|
|
26
|
+
* than a `next` — so nothing inside one of them can advance or stop the chain.
|
|
27
|
+
*/
|
|
28
|
+
/** Plugin name; also the identity used by `Service.use()` / `Service.remove()`. */
|
|
29
|
+
export declare const INTERCEPTOR_PLUGIN_NAME = "interceptor";
|
|
30
|
+
/** The reserved interceptor priority band (see `docs/guide/plugin-lifecycle.md` §2.1). */
|
|
31
|
+
export declare const INTERCEPTOR_PRIORITY = 100;
|
|
32
|
+
/** Programmatic interceptors shared by every request of one server. */
|
|
33
|
+
export interface InterceptorOptions {
|
|
34
|
+
/** Entries that run on every request, after the class- and method-level ones. */
|
|
35
|
+
request?: InterceptorEntry<InternalAxiosRequestConfig>[];
|
|
36
|
+
/** Entries that run on every response, after the class- and method-level ones. */
|
|
37
|
+
response?: InterceptorEntry<AxiosResponse>[];
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* The plugin object plus the two registries behind it.
|
|
41
|
+
*
|
|
42
|
+
* `use()` / `eject()` are exposed on the instance because an interceptor added
|
|
43
|
+
* from `Interceptor({ request })` is fixed at construction time, and a real
|
|
44
|
+
* application usually learns its token or tenant long after the server module
|
|
45
|
+
* has been evaluated.
|
|
46
|
+
*/
|
|
47
|
+
export interface InterceptorPlugin extends SnailPluginObject<InterceptorOptions> {
|
|
48
|
+
/** Server-wide request interceptors. */
|
|
49
|
+
readonly request: InterceptorManager<InternalAxiosRequestConfig>;
|
|
50
|
+
/** Server-wide response interceptors. */
|
|
51
|
+
readonly response: InterceptorManager<AxiosResponse>;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Create the interceptor plugin.
|
|
55
|
+
*
|
|
56
|
+
* ```ts
|
|
57
|
+
* const interceptors = Interceptor({
|
|
58
|
+
* request: [{ onFulfilled: (config) => { config.headers.set("x-app", "web"); } }]
|
|
59
|
+
* });
|
|
60
|
+
* Service.use(interceptors);
|
|
61
|
+
* interceptors.request.use({ onFulfilled: (config, ctx) => ctx.logger.debug(ctx.fullName) });
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
export declare function Interceptor(options?: InterceptorOptions): InterceptorPlugin;
|
|
65
|
+
//# sourceMappingURL=plugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../../src/plugins/interceptor/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,0BAA0B,EAAE,MAAM,OAAO,CAAC;AAIvE,OAAO,KAAK,EAAa,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAOzE,OAAO,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,QAAQ,CAAC;AAE/C;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,mFAAmF;AACnF,eAAO,MAAM,uBAAuB,gBAAgB,CAAC;AAErD,0FAA0F;AAC1F,eAAO,MAAM,oBAAoB,MAAM,CAAC;AAExC,uEAAuE;AACvE,MAAM,WAAW,kBAAkB;IACjC,iFAAiF;IACjF,OAAO,CAAC,EAAE,gBAAgB,CAAC,0BAA0B,CAAC,EAAE,CAAC;IAEzD,kFAAkF;IAClF,QAAQ,CAAC,EAAE,gBAAgB,CAAC,aAAa,CAAC,EAAE,CAAC;CAC9C;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,iBAAkB,SAAQ,iBAAiB,CAAC,kBAAkB,CAAC;IAC9E,wCAAwC;IACxC,QAAQ,CAAC,OAAO,EAAE,kBAAkB,CAAC,0BAA0B,CAAC,CAAC;IAEjE,yCAAyC;IACzC,QAAQ,CAAC,QAAQ,EAAE,kBAAkB,CAAC,aAAa,CAAC,CAAC;CACtD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,OAAO,CAAC,EAAE,kBAAkB,GAAG,iBAAiB,CA0B3E"}
|