@lark-apaas/nestjs-common 0.1.4-alpha.9 → 0.1.5
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/README.md +2 -0
- package/dist/index.cjs +3 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +75 -1
- package/dist/index.d.ts +75 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
package/dist/index.cjs
CHANGED
|
@@ -24,6 +24,7 @@ __export(index_exports, {
|
|
|
24
24
|
AUTO_TRACE: () => AUTO_TRACE,
|
|
25
25
|
AutoTrace: () => AutoTrace,
|
|
26
26
|
CommonModule: () => CommonModule,
|
|
27
|
+
HTTP_CLIENT_FACTORY: () => HTTP_CLIENT_FACTORY,
|
|
27
28
|
OBSERVABLE_SERVICE: () => OBSERVABLE_SERVICE,
|
|
28
29
|
PLATFORM_HTTP_CLIENT: () => PLATFORM_HTTP_CLIENT,
|
|
29
30
|
RequestContextService: () => RequestContextService,
|
|
@@ -77,6 +78,7 @@ var import_common2 = require("@nestjs/common");
|
|
|
77
78
|
var OBSERVABLE_SERVICE = /* @__PURE__ */ Symbol("OBSERVABLE_SERVICE");
|
|
78
79
|
var PLATFORM_HTTP_CLIENT = /* @__PURE__ */ Symbol("PLATFORM_HTTP_CLIENT");
|
|
79
80
|
var AUTO_TRACE = /* @__PURE__ */ Symbol("AUTO_TRACE");
|
|
81
|
+
var HTTP_CLIENT_FACTORY = /* @__PURE__ */ Symbol("HTTP_CLIENT_FACTORY");
|
|
80
82
|
|
|
81
83
|
// src/decorators/auto-trace.decorator.ts
|
|
82
84
|
var AutoTrace = /* @__PURE__ */ __name((options) => {
|
|
@@ -131,6 +133,7 @@ __name(stripBasePath, "stripBasePath");
|
|
|
131
133
|
AUTO_TRACE,
|
|
132
134
|
AutoTrace,
|
|
133
135
|
CommonModule,
|
|
136
|
+
HTTP_CLIENT_FACTORY,
|
|
134
137
|
OBSERVABLE_SERVICE,
|
|
135
138
|
PLATFORM_HTTP_CLIENT,
|
|
136
139
|
RequestContextService,
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/services/request-context.service.ts","../src/decorators/auto-trace.decorator.ts","../src/tokens.ts","../src/module.ts","../src/utils/strip-base-path.ts"],"sourcesContent":["export { RequestContextService, type RequestContextState } from './services/request-context.service';\nexport { AutoTrace } from './decorators/auto-trace.decorator';\nexport { CommonModule } from './module';\nexport { stripBasePath } from './utils/strip-base-path';\nexport * from './tokens';\nexport * from './type';\n","import { Injectable } from '@nestjs/common';\nimport { AsyncLocalStorage } from 'async_hooks';\n\nexport interface RequestContextState {\n requestId?: string;\n path?: string;\n method?: string;\n userId?: string;\n appId?: string;\n tenantId?: string;\n ip?: string;\n requestRootSpan?: unknown;\n /** x-tt-env header,用于环境标识透传(如灰度/测试环境) */\n ttEnv?: string;\n /** 请求来源页面路由 (X-Page-Route header) */\n pageRoute?: string;\n [key: string]: unknown;\n}\n\n@Injectable()\nexport class RequestContextService {\n private readonly storage = new AsyncLocalStorage<RequestContextState>();\n\n run<T>(context: RequestContextState, callback: () => T): T {\n const store = { ...context };\n return this.storage.run(store, callback);\n }\n\n setContext(partial: Partial<RequestContextState>): void {\n const store = this.storage.getStore();\n if (!store) {\n return;\n }\n Object.assign(store, partial);\n }\n\n getContext(): RequestContextState | undefined {\n return this.storage.getStore();\n }\n\n get<K extends keyof RequestContextState>(key: K): RequestContextState[K] | undefined {\n return this.storage.getStore()?.[key];\n }\n}\n","import { SetMetadata } from '@nestjs/common';\nimport { AUTO_TRACE } from '../tokens';\n\nexport const AutoTrace = (options: string) => {\n return SetMetadata(AUTO_TRACE, options);\n};","/**\n * DI token for ObservableService\n *\n * 注入此 token 获得 ObservableService 实例\n */\nexport const OBSERVABLE_SERVICE = Symbol('OBSERVABLE_SERVICE');\n\n/**\n * DI Token for Platform HttpClient\n *\n * 这些 token 供 PlatformModule 和内部依赖(如 AuthNPaasModule)共享使用,通过创建独立的 nestjs-common 包,避免循环依赖\n *\n * @example\n * ```typescript\n * // nestjs-core 中注册 provider\n * import { PLATFORM_HTTP_CLIENT } from '@lark-apaas/nestjs-common';\n *\n * {\n * provide: PLATFORM_HTTP_CLIENT,\n * useFactory: (svc: PlatformHttpClientService) => svc.instance,\n * inject: [PlatformHttpClientService],\n * }\n * ```\n *\n * @example\n * ```typescript\n * // nestjs-authnpaas 中注入使用\n * import { Injectable, Inject } from '@nestjs/common';\n * import { PLATFORM_HTTP_CLIENT, type SafeHttpClient } from '@lark-apaas/nestjs-common';\n *\n * @Injectable()\n * export class AuthNService {\n * constructor(\n * @Inject(PLATFORM_HTTP_CLIENT) private http: SafeHttpClient\n * ) {}\n * }\n * ```\n */\n\n/**\n * DI token for PlatformHttpClient instance\n *\n * 注入此 token 获得 SafeHttpClient 实例(不暴露 interceptors)\n */\nexport const PLATFORM_HTTP_CLIENT = Symbol('PLATFORM_HTTP_CLIENT');\n\n/**\n * DI token for AutoTraceInterceptor\n *\n * 此 token 用于元数据存取 AUTO_TRACE 装饰器的参数\n */\nexport const AUTO_TRACE = Symbol('AUTO_TRACE');\n","import { Global, Module } from '@nestjs/common';\nimport { RequestContextService } from './services/request-context.service';\n\n@Global()\n@Module({\n providers: [RequestContextService],\n exports: [RequestContextService],\n})\nexport class CommonModule {}\n","/**\n * 从完整路径中去除 basePath 前缀,获取相对页面路由\n *\n * @param fullPath - 完整路径,如 \"/af/p/app_xxx/dashboard\"\n * @param basePath - 基础路径前缀,如 \"/af/p/app_xxx\",通常来自 CLIENT_BASE_PATH 环境变量\n * @returns 去除前缀后的相对路径,如 \"/dashboard\";如果无法处理则返回原值\n *\n * @example\n * stripBasePath('/af/p/app_xxx/dashboard', '/af/p/app_xxx') // => '/dashboard'\n * stripBasePath('/af/p/app_xxx', '/af/p/app_xxx') // => '/'\n * stripBasePath('/other/path', '/af/p/app_xxx') // => '/other/path'\n * stripBasePath(undefined, '/af/p/app_xxx') // => undefined\n * stripBasePath('/dashboard', '') // => '/dashboard'\n */\nexport function stripBasePath(\n fullPath: string | undefined | null,\n basePath: string | undefined | null,\n): string | undefined | null {\n // 如果 fullPath 为空,直接返回\n if (!fullPath) {\n return fullPath;\n }\n\n // 如果 basePath 为空,直接返回原路径\n if (!basePath) {\n return fullPath;\n }\n\n // 如果 fullPath 以 basePath 开头,去除前缀\n // 需要确保是完整的路径段匹配,避免部分匹配(如 /app_xxx 匹配到 /app_xxx_extended)\n if (fullPath.startsWith(basePath)) {\n const stripped = fullPath.slice(basePath.length);\n // 确保 stripped 为空(完全匹配)或以 '/' 开头(路径段边界)\n if (stripped === '' || stripped.startsWith('/')) {\n return stripped || '/';\n }\n }\n\n // 不匹配则返回原路径\n return fullPath;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAAA
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/services/request-context.service.ts","../src/decorators/auto-trace.decorator.ts","../src/tokens.ts","../src/module.ts","../src/utils/strip-base-path.ts"],"sourcesContent":["export { RequestContextService, type RequestContextState } from './services/request-context.service';\nexport { AutoTrace } from './decorators/auto-trace.decorator';\nexport { CommonModule } from './module';\nexport { stripBasePath } from './utils/strip-base-path';\nexport * from './tokens';\nexport * from './type';\n","import { Injectable } from '@nestjs/common';\nimport { AsyncLocalStorage } from 'async_hooks';\n\nexport interface RequestContextState {\n requestId?: string;\n path?: string;\n method?: string;\n userId?: string;\n appId?: string;\n tenantId?: string;\n ip?: string;\n requestRootSpan?: unknown;\n /** x-tt-env header,用于环境标识透传(如灰度/测试环境) */\n ttEnv?: string;\n /** 请求来源页面路由 (X-Page-Route header) */\n pageRoute?: string;\n /** 是否为系统账号 */\n isSystemAccount?: boolean;\n [key: string]: unknown;\n}\n\n@Injectable()\nexport class RequestContextService {\n private readonly storage = new AsyncLocalStorage<RequestContextState>();\n\n run<T>(context: RequestContextState, callback: () => T): T {\n const store = { ...context };\n return this.storage.run(store, callback);\n }\n\n setContext(partial: Partial<RequestContextState>): void {\n const store = this.storage.getStore();\n if (!store) {\n return;\n }\n Object.assign(store, partial);\n }\n\n getContext(): RequestContextState | undefined {\n return this.storage.getStore();\n }\n\n get<K extends keyof RequestContextState>(key: K): RequestContextState[K] | undefined {\n return this.storage.getStore()?.[key];\n }\n}\n","import { SetMetadata } from '@nestjs/common';\nimport { AUTO_TRACE } from '../tokens';\n\nexport const AutoTrace = (options: string) => {\n return SetMetadata(AUTO_TRACE, options);\n};","/**\n * DI token for ObservableService\n *\n * 注入此 token 获得 ObservableService 实例\n */\nexport const OBSERVABLE_SERVICE = Symbol('OBSERVABLE_SERVICE');\n\n/**\n * DI Token for Platform HttpClient\n *\n * 这些 token 供 PlatformModule 和内部依赖(如 AuthNPaasModule)共享使用,通过创建独立的 nestjs-common 包,避免循环依赖\n *\n * @example\n * ```typescript\n * // nestjs-core 中注册 provider\n * import { PLATFORM_HTTP_CLIENT } from '@lark-apaas/nestjs-common';\n *\n * {\n * provide: PLATFORM_HTTP_CLIENT,\n * useFactory: (svc: PlatformHttpClientService) => svc.instance,\n * inject: [PlatformHttpClientService],\n * }\n * ```\n *\n * @example\n * ```typescript\n * // nestjs-authnpaas 中注入使用\n * import { Injectable, Inject } from '@nestjs/common';\n * import { PLATFORM_HTTP_CLIENT, type SafeHttpClient } from '@lark-apaas/nestjs-common';\n *\n * @Injectable()\n * export class AuthNService {\n * constructor(\n * @Inject(PLATFORM_HTTP_CLIENT) private http: SafeHttpClient\n * ) {}\n * }\n * ```\n */\n\n/**\n * DI token for PlatformHttpClient instance\n *\n * 注入此 token 获得 SafeHttpClient 实例(不暴露 interceptors)\n */\nexport const PLATFORM_HTTP_CLIENT = Symbol('PLATFORM_HTTP_CLIENT');\n\n/**\n * DI token for AutoTraceInterceptor\n *\n * 此 token 用于元数据存取 AUTO_TRACE 装饰器的参数\n */\nexport const AUTO_TRACE = Symbol('AUTO_TRACE');\n\n/**\n * DI token for HttpClientFactory\n *\n * 用于创建独立的 HttpClient 实例,支持自定义拦截器\n *\n * @example\n * ```typescript\n * // 注入并使用\n * @Injectable()\n * export class PluginService {\n * constructor(\n * @Inject(HTTP_CLIENT_FACTORY) private factory: HttpClientFactory\n * ) {}\n *\n * createClientForPlugin(pluginKey: string) {\n * const client = this.factory.create();\n * client.interceptors.request.use((config) => {\n * config.headers['x-plugin-key'] = pluginKey;\n * return config;\n * });\n * return client;\n * }\n * }\n * ```\n */\nexport const HTTP_CLIENT_FACTORY = Symbol('HTTP_CLIENT_FACTORY');\n","import { Global, Module } from '@nestjs/common';\nimport { RequestContextService } from './services/request-context.service';\n\n@Global()\n@Module({\n providers: [RequestContextService],\n exports: [RequestContextService],\n})\nexport class CommonModule {}\n","/**\n * 从完整路径中去除 basePath 前缀,获取相对页面路由\n *\n * @param fullPath - 完整路径,如 \"/af/p/app_xxx/dashboard\"\n * @param basePath - 基础路径前缀,如 \"/af/p/app_xxx\",通常来自 CLIENT_BASE_PATH 环境变量\n * @returns 去除前缀后的相对路径,如 \"/dashboard\";如果无法处理则返回原值\n *\n * @example\n * stripBasePath('/af/p/app_xxx/dashboard', '/af/p/app_xxx') // => '/dashboard'\n * stripBasePath('/af/p/app_xxx', '/af/p/app_xxx') // => '/'\n * stripBasePath('/other/path', '/af/p/app_xxx') // => '/other/path'\n * stripBasePath(undefined, '/af/p/app_xxx') // => undefined\n * stripBasePath('/dashboard', '') // => '/dashboard'\n */\nexport function stripBasePath(\n fullPath: string | undefined | null,\n basePath: string | undefined | null,\n): string | undefined | null {\n // 如果 fullPath 为空,直接返回\n if (!fullPath) {\n return fullPath;\n }\n\n // 如果 basePath 为空,直接返回原路径\n if (!basePath) {\n return fullPath;\n }\n\n // 如果 fullPath 以 basePath 开头,去除前缀\n // 需要确保是完整的路径段匹配,避免部分匹配(如 /app_xxx 匹配到 /app_xxx_extended)\n if (fullPath.startsWith(basePath)) {\n const stripped = fullPath.slice(basePath.length);\n // 确保 stripped 为空(完全匹配)或以 '/' 开头(路径段边界)\n if (stripped === '' || stripped.startsWith('/')) {\n return stripped || '/';\n }\n }\n\n // 不匹配则返回原路径\n return fullPath;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAAA;;;;;;;;;;;;;;ACAA,oBAA2B;AAC3B,yBAAkC;;;;;;;;AAqB3B,IAAMA,wBAAN,MAAMA;SAAAA;;;EACMC,UAAU,IAAIC,qCAAAA;EAE/BC,IAAOC,SAA8BC,UAAsB;AACzD,UAAMC,QAAQ;MAAE,GAAGF;IAAQ;AAC3B,WAAO,KAAKH,QAAQE,IAAIG,OAAOD,QAAAA;EACjC;EAEAE,WAAWC,SAA6C;AACtD,UAAMF,QAAQ,KAAKL,QAAQQ,SAAQ;AACnC,QAAI,CAACH,OAAO;AACV;IACF;AACAI,WAAOC,OAAOL,OAAOE,OAAAA;EACvB;EAEAI,aAA8C;AAC5C,WAAO,KAAKX,QAAQQ,SAAQ;EAC9B;EAEAI,IAAyCC,KAA4C;AACnF,WAAO,KAAKb,QAAQQ,SAAQ,IAAKK,GAAAA;EACnC;AACF;;;;;;AC7CA,IAAAC,iBAA4B;;;ACKrB,IAAMC,qBAAqBC,uBAAO,oBAAA;AAuClC,IAAMC,uBAAuBD,uBAAO,sBAAA;AAOpC,IAAME,aAAaF,uBAAO,YAAA;AA2B1B,IAAMG,sBAAsBH,uBAAO,qBAAA;;;AD3EnC,IAAMI,YAAY,wBAACC,YAAAA;AACxB,aAAOC,4BAAYC,YAAYF,OAAAA;AACjC,GAFyB;;;AEHzB,IAAAG,iBAA+B;;;;;;;;AAQxB,IAAMC,eAAN,MAAMA;SAAAA;;;AAAc;;;;IAHzBC,WAAW;MAACC;;IACZC,SAAS;MAACD;;;;;;ACQL,SAASE,cACdC,UACAC,UAAmC;AAGnC,MAAI,CAACD,UAAU;AACb,WAAOA;EACT;AAGA,MAAI,CAACC,UAAU;AACb,WAAOD;EACT;AAIA,MAAIA,SAASE,WAAWD,QAAAA,GAAW;AACjC,UAAME,WAAWH,SAASI,MAAMH,SAASI,MAAM;AAE/C,QAAIF,aAAa,MAAMA,SAASD,WAAW,GAAA,GAAM;AAC/C,aAAOC,YAAY;IACrB;EACF;AAGA,SAAOH;AACT;AA1BgBD;","names":["RequestContextService","storage","AsyncLocalStorage","run","context","callback","store","setContext","partial","getStore","Object","assign","getContext","get","key","import_common","OBSERVABLE_SERVICE","Symbol","PLATFORM_HTTP_CLIENT","AUTO_TRACE","HTTP_CLIENT_FACTORY","AutoTrace","options","SetMetadata","AUTO_TRACE","import_common","CommonModule","providers","RequestContextService","exports","stripBasePath","fullPath","basePath","startsWith","stripped","slice","length"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -13,6 +13,8 @@ interface RequestContextState {
|
|
|
13
13
|
ttEnv?: string;
|
|
14
14
|
/** 请求来源页面路由 (X-Page-Route header) */
|
|
15
15
|
pageRoute?: string;
|
|
16
|
+
/** 是否为系统账号 */
|
|
17
|
+
isSystemAccount?: boolean;
|
|
16
18
|
[key: string]: unknown;
|
|
17
19
|
}
|
|
18
20
|
declare class RequestContextService {
|
|
@@ -72,6 +74,32 @@ declare const PLATFORM_HTTP_CLIENT: unique symbol;
|
|
|
72
74
|
* 此 token 用于元数据存取 AUTO_TRACE 装饰器的参数
|
|
73
75
|
*/
|
|
74
76
|
declare const AUTO_TRACE: unique symbol;
|
|
77
|
+
/**
|
|
78
|
+
* DI token for HttpClientFactory
|
|
79
|
+
*
|
|
80
|
+
* 用于创建独立的 HttpClient 实例,支持自定义拦截器
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* ```typescript
|
|
84
|
+
* // 注入并使用
|
|
85
|
+
* @Injectable()
|
|
86
|
+
* export class PluginService {
|
|
87
|
+
* constructor(
|
|
88
|
+
* @Inject(HTTP_CLIENT_FACTORY) private factory: HttpClientFactory
|
|
89
|
+
* ) {}
|
|
90
|
+
*
|
|
91
|
+
* createClientForPlugin(pluginKey: string) {
|
|
92
|
+
* const client = this.factory.create();
|
|
93
|
+
* client.interceptors.request.use((config) => {
|
|
94
|
+
* config.headers['x-plugin-key'] = pluginKey;
|
|
95
|
+
* return config;
|
|
96
|
+
* });
|
|
97
|
+
* return client;
|
|
98
|
+
* }
|
|
99
|
+
* }
|
|
100
|
+
* ```
|
|
101
|
+
*/
|
|
102
|
+
declare const HTTP_CLIENT_FACTORY: unique symbol;
|
|
75
103
|
|
|
76
104
|
declare const AutoTrace: (options: string) => _nestjs_common.CustomDecorator<typeof AUTO_TRACE>;
|
|
77
105
|
|
|
@@ -171,5 +199,51 @@ interface PlatformHttpClient {
|
|
|
171
199
|
*/
|
|
172
200
|
delete(url: string, config?: any): Promise<Response>;
|
|
173
201
|
}
|
|
202
|
+
/**
|
|
203
|
+
* 带拦截器的 HttpClient 接口
|
|
204
|
+
*
|
|
205
|
+
* 继承 PlatformHttpClient,额外暴露 interceptors 属性
|
|
206
|
+
* 用于需要自定义拦截器的高级场景
|
|
207
|
+
*/
|
|
208
|
+
interface HttpClientWithInterceptors extends PlatformHttpClient {
|
|
209
|
+
interceptors: {
|
|
210
|
+
request: {
|
|
211
|
+
use(onFulfilled: (config: any) => any, onRejected?: (error: any) => any): number;
|
|
212
|
+
};
|
|
213
|
+
response: {
|
|
214
|
+
use(onFulfilled: (response: any) => any, onRejected?: (error: any) => any): number;
|
|
215
|
+
};
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* HttpClient 工厂接口
|
|
220
|
+
*
|
|
221
|
+
* 用于创建独立的 HttpClient 实例,支持自定义拦截器
|
|
222
|
+
*
|
|
223
|
+
* @example
|
|
224
|
+
* ```typescript
|
|
225
|
+
* @Injectable()
|
|
226
|
+
* export class MyService {
|
|
227
|
+
* constructor(
|
|
228
|
+
* @Inject(HTTP_CLIENT_FACTORY) private factory: HttpClientFactory
|
|
229
|
+
* ) {
|
|
230
|
+
* const client = this.factory.create();
|
|
231
|
+
* client.interceptors.request.use((config) => {
|
|
232
|
+
* config.headers['X-Custom'] = 'value';
|
|
233
|
+
* return config;
|
|
234
|
+
* });
|
|
235
|
+
* }
|
|
236
|
+
* }
|
|
237
|
+
* ```
|
|
238
|
+
*/
|
|
239
|
+
interface HttpClientFactory {
|
|
240
|
+
/**
|
|
241
|
+
* 创建新的独立 HttpClient 实例
|
|
242
|
+
*
|
|
243
|
+
* @param options - 可选配置
|
|
244
|
+
* @returns 带拦截器的 HttpClient 实例
|
|
245
|
+
*/
|
|
246
|
+
create(options?: unknown): HttpClientWithInterceptors;
|
|
247
|
+
}
|
|
174
248
|
|
|
175
|
-
export { AUTO_TRACE, AutoTrace, CommonModule, OBSERVABLE_SERVICE, type ObservableService, PLATFORM_HTTP_CLIENT, type PlatformHttpClient, RequestContextService, type RequestContextState, stripBasePath };
|
|
249
|
+
export { AUTO_TRACE, AutoTrace, CommonModule, HTTP_CLIENT_FACTORY, type HttpClientFactory, type HttpClientWithInterceptors, OBSERVABLE_SERVICE, type ObservableService, PLATFORM_HTTP_CLIENT, type PlatformHttpClient, RequestContextService, type RequestContextState, stripBasePath };
|
package/dist/index.d.ts
CHANGED
|
@@ -13,6 +13,8 @@ interface RequestContextState {
|
|
|
13
13
|
ttEnv?: string;
|
|
14
14
|
/** 请求来源页面路由 (X-Page-Route header) */
|
|
15
15
|
pageRoute?: string;
|
|
16
|
+
/** 是否为系统账号 */
|
|
17
|
+
isSystemAccount?: boolean;
|
|
16
18
|
[key: string]: unknown;
|
|
17
19
|
}
|
|
18
20
|
declare class RequestContextService {
|
|
@@ -72,6 +74,32 @@ declare const PLATFORM_HTTP_CLIENT: unique symbol;
|
|
|
72
74
|
* 此 token 用于元数据存取 AUTO_TRACE 装饰器的参数
|
|
73
75
|
*/
|
|
74
76
|
declare const AUTO_TRACE: unique symbol;
|
|
77
|
+
/**
|
|
78
|
+
* DI token for HttpClientFactory
|
|
79
|
+
*
|
|
80
|
+
* 用于创建独立的 HttpClient 实例,支持自定义拦截器
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* ```typescript
|
|
84
|
+
* // 注入并使用
|
|
85
|
+
* @Injectable()
|
|
86
|
+
* export class PluginService {
|
|
87
|
+
* constructor(
|
|
88
|
+
* @Inject(HTTP_CLIENT_FACTORY) private factory: HttpClientFactory
|
|
89
|
+
* ) {}
|
|
90
|
+
*
|
|
91
|
+
* createClientForPlugin(pluginKey: string) {
|
|
92
|
+
* const client = this.factory.create();
|
|
93
|
+
* client.interceptors.request.use((config) => {
|
|
94
|
+
* config.headers['x-plugin-key'] = pluginKey;
|
|
95
|
+
* return config;
|
|
96
|
+
* });
|
|
97
|
+
* return client;
|
|
98
|
+
* }
|
|
99
|
+
* }
|
|
100
|
+
* ```
|
|
101
|
+
*/
|
|
102
|
+
declare const HTTP_CLIENT_FACTORY: unique symbol;
|
|
75
103
|
|
|
76
104
|
declare const AutoTrace: (options: string) => _nestjs_common.CustomDecorator<typeof AUTO_TRACE>;
|
|
77
105
|
|
|
@@ -171,5 +199,51 @@ interface PlatformHttpClient {
|
|
|
171
199
|
*/
|
|
172
200
|
delete(url: string, config?: any): Promise<Response>;
|
|
173
201
|
}
|
|
202
|
+
/**
|
|
203
|
+
* 带拦截器的 HttpClient 接口
|
|
204
|
+
*
|
|
205
|
+
* 继承 PlatformHttpClient,额外暴露 interceptors 属性
|
|
206
|
+
* 用于需要自定义拦截器的高级场景
|
|
207
|
+
*/
|
|
208
|
+
interface HttpClientWithInterceptors extends PlatformHttpClient {
|
|
209
|
+
interceptors: {
|
|
210
|
+
request: {
|
|
211
|
+
use(onFulfilled: (config: any) => any, onRejected?: (error: any) => any): number;
|
|
212
|
+
};
|
|
213
|
+
response: {
|
|
214
|
+
use(onFulfilled: (response: any) => any, onRejected?: (error: any) => any): number;
|
|
215
|
+
};
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* HttpClient 工厂接口
|
|
220
|
+
*
|
|
221
|
+
* 用于创建独立的 HttpClient 实例,支持自定义拦截器
|
|
222
|
+
*
|
|
223
|
+
* @example
|
|
224
|
+
* ```typescript
|
|
225
|
+
* @Injectable()
|
|
226
|
+
* export class MyService {
|
|
227
|
+
* constructor(
|
|
228
|
+
* @Inject(HTTP_CLIENT_FACTORY) private factory: HttpClientFactory
|
|
229
|
+
* ) {
|
|
230
|
+
* const client = this.factory.create();
|
|
231
|
+
* client.interceptors.request.use((config) => {
|
|
232
|
+
* config.headers['X-Custom'] = 'value';
|
|
233
|
+
* return config;
|
|
234
|
+
* });
|
|
235
|
+
* }
|
|
236
|
+
* }
|
|
237
|
+
* ```
|
|
238
|
+
*/
|
|
239
|
+
interface HttpClientFactory {
|
|
240
|
+
/**
|
|
241
|
+
* 创建新的独立 HttpClient 实例
|
|
242
|
+
*
|
|
243
|
+
* @param options - 可选配置
|
|
244
|
+
* @returns 带拦截器的 HttpClient 实例
|
|
245
|
+
*/
|
|
246
|
+
create(options?: unknown): HttpClientWithInterceptors;
|
|
247
|
+
}
|
|
174
248
|
|
|
175
|
-
export { AUTO_TRACE, AutoTrace, CommonModule, OBSERVABLE_SERVICE, type ObservableService, PLATFORM_HTTP_CLIENT, type PlatformHttpClient, RequestContextService, type RequestContextState, stripBasePath };
|
|
249
|
+
export { AUTO_TRACE, AutoTrace, CommonModule, HTTP_CLIENT_FACTORY, type HttpClientFactory, type HttpClientWithInterceptors, OBSERVABLE_SERVICE, type ObservableService, PLATFORM_HTTP_CLIENT, type PlatformHttpClient, RequestContextService, type RequestContextState, stripBasePath };
|
package/dist/index.js
CHANGED
|
@@ -47,6 +47,7 @@ import { SetMetadata } from "@nestjs/common";
|
|
|
47
47
|
var OBSERVABLE_SERVICE = /* @__PURE__ */ Symbol("OBSERVABLE_SERVICE");
|
|
48
48
|
var PLATFORM_HTTP_CLIENT = /* @__PURE__ */ Symbol("PLATFORM_HTTP_CLIENT");
|
|
49
49
|
var AUTO_TRACE = /* @__PURE__ */ Symbol("AUTO_TRACE");
|
|
50
|
+
var HTTP_CLIENT_FACTORY = /* @__PURE__ */ Symbol("HTTP_CLIENT_FACTORY");
|
|
50
51
|
|
|
51
52
|
// src/decorators/auto-trace.decorator.ts
|
|
52
53
|
var AutoTrace = /* @__PURE__ */ __name((options) => {
|
|
@@ -100,6 +101,7 @@ export {
|
|
|
100
101
|
AUTO_TRACE,
|
|
101
102
|
AutoTrace,
|
|
102
103
|
CommonModule,
|
|
104
|
+
HTTP_CLIENT_FACTORY,
|
|
103
105
|
OBSERVABLE_SERVICE,
|
|
104
106
|
PLATFORM_HTTP_CLIENT,
|
|
105
107
|
RequestContextService,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/services/request-context.service.ts","../src/decorators/auto-trace.decorator.ts","../src/tokens.ts","../src/module.ts","../src/utils/strip-base-path.ts"],"sourcesContent":["import { Injectable } from '@nestjs/common';\nimport { AsyncLocalStorage } from 'async_hooks';\n\nexport interface RequestContextState {\n requestId?: string;\n path?: string;\n method?: string;\n userId?: string;\n appId?: string;\n tenantId?: string;\n ip?: string;\n requestRootSpan?: unknown;\n /** x-tt-env header,用于环境标识透传(如灰度/测试环境) */\n ttEnv?: string;\n /** 请求来源页面路由 (X-Page-Route header) */\n pageRoute?: string;\n [key: string]: unknown;\n}\n\n@Injectable()\nexport class RequestContextService {\n private readonly storage = new AsyncLocalStorage<RequestContextState>();\n\n run<T>(context: RequestContextState, callback: () => T): T {\n const store = { ...context };\n return this.storage.run(store, callback);\n }\n\n setContext(partial: Partial<RequestContextState>): void {\n const store = this.storage.getStore();\n if (!store) {\n return;\n }\n Object.assign(store, partial);\n }\n\n getContext(): RequestContextState | undefined {\n return this.storage.getStore();\n }\n\n get<K extends keyof RequestContextState>(key: K): RequestContextState[K] | undefined {\n return this.storage.getStore()?.[key];\n }\n}\n","import { SetMetadata } from '@nestjs/common';\nimport { AUTO_TRACE } from '../tokens';\n\nexport const AutoTrace = (options: string) => {\n return SetMetadata(AUTO_TRACE, options);\n};","/**\n * DI token for ObservableService\n *\n * 注入此 token 获得 ObservableService 实例\n */\nexport const OBSERVABLE_SERVICE = Symbol('OBSERVABLE_SERVICE');\n\n/**\n * DI Token for Platform HttpClient\n *\n * 这些 token 供 PlatformModule 和内部依赖(如 AuthNPaasModule)共享使用,通过创建独立的 nestjs-common 包,避免循环依赖\n *\n * @example\n * ```typescript\n * // nestjs-core 中注册 provider\n * import { PLATFORM_HTTP_CLIENT } from '@lark-apaas/nestjs-common';\n *\n * {\n * provide: PLATFORM_HTTP_CLIENT,\n * useFactory: (svc: PlatformHttpClientService) => svc.instance,\n * inject: [PlatformHttpClientService],\n * }\n * ```\n *\n * @example\n * ```typescript\n * // nestjs-authnpaas 中注入使用\n * import { Injectable, Inject } from '@nestjs/common';\n * import { PLATFORM_HTTP_CLIENT, type SafeHttpClient } from '@lark-apaas/nestjs-common';\n *\n * @Injectable()\n * export class AuthNService {\n * constructor(\n * @Inject(PLATFORM_HTTP_CLIENT) private http: SafeHttpClient\n * ) {}\n * }\n * ```\n */\n\n/**\n * DI token for PlatformHttpClient instance\n *\n * 注入此 token 获得 SafeHttpClient 实例(不暴露 interceptors)\n */\nexport const PLATFORM_HTTP_CLIENT = Symbol('PLATFORM_HTTP_CLIENT');\n\n/**\n * DI token for AutoTraceInterceptor\n *\n * 此 token 用于元数据存取 AUTO_TRACE 装饰器的参数\n */\nexport const AUTO_TRACE = Symbol('AUTO_TRACE');\n","import { Global, Module } from '@nestjs/common';\nimport { RequestContextService } from './services/request-context.service';\n\n@Global()\n@Module({\n providers: [RequestContextService],\n exports: [RequestContextService],\n})\nexport class CommonModule {}\n","/**\n * 从完整路径中去除 basePath 前缀,获取相对页面路由\n *\n * @param fullPath - 完整路径,如 \"/af/p/app_xxx/dashboard\"\n * @param basePath - 基础路径前缀,如 \"/af/p/app_xxx\",通常来自 CLIENT_BASE_PATH 环境变量\n * @returns 去除前缀后的相对路径,如 \"/dashboard\";如果无法处理则返回原值\n *\n * @example\n * stripBasePath('/af/p/app_xxx/dashboard', '/af/p/app_xxx') // => '/dashboard'\n * stripBasePath('/af/p/app_xxx', '/af/p/app_xxx') // => '/'\n * stripBasePath('/other/path', '/af/p/app_xxx') // => '/other/path'\n * stripBasePath(undefined, '/af/p/app_xxx') // => undefined\n * stripBasePath('/dashboard', '') // => '/dashboard'\n */\nexport function stripBasePath(\n fullPath: string | undefined | null,\n basePath: string | undefined | null,\n): string | undefined | null {\n // 如果 fullPath 为空,直接返回\n if (!fullPath) {\n return fullPath;\n }\n\n // 如果 basePath 为空,直接返回原路径\n if (!basePath) {\n return fullPath;\n }\n\n // 如果 fullPath 以 basePath 开头,去除前缀\n // 需要确保是完整的路径段匹配,避免部分匹配(如 /app_xxx 匹配到 /app_xxx_extended)\n if (fullPath.startsWith(basePath)) {\n const stripped = fullPath.slice(basePath.length);\n // 确保 stripped 为空(完全匹配)或以 '/' 开头(路径段边界)\n if (stripped === '' || stripped.startsWith('/')) {\n return stripped || '/';\n }\n }\n\n // 不匹配则返回原路径\n return fullPath;\n}\n"],"mappings":";;;;AAAA,SAASA,kBAAkB;AAC3B,SAASC,yBAAyB;;;;;;;;
|
|
1
|
+
{"version":3,"sources":["../src/services/request-context.service.ts","../src/decorators/auto-trace.decorator.ts","../src/tokens.ts","../src/module.ts","../src/utils/strip-base-path.ts"],"sourcesContent":["import { Injectable } from '@nestjs/common';\nimport { AsyncLocalStorage } from 'async_hooks';\n\nexport interface RequestContextState {\n requestId?: string;\n path?: string;\n method?: string;\n userId?: string;\n appId?: string;\n tenantId?: string;\n ip?: string;\n requestRootSpan?: unknown;\n /** x-tt-env header,用于环境标识透传(如灰度/测试环境) */\n ttEnv?: string;\n /** 请求来源页面路由 (X-Page-Route header) */\n pageRoute?: string;\n /** 是否为系统账号 */\n isSystemAccount?: boolean;\n [key: string]: unknown;\n}\n\n@Injectable()\nexport class RequestContextService {\n private readonly storage = new AsyncLocalStorage<RequestContextState>();\n\n run<T>(context: RequestContextState, callback: () => T): T {\n const store = { ...context };\n return this.storage.run(store, callback);\n }\n\n setContext(partial: Partial<RequestContextState>): void {\n const store = this.storage.getStore();\n if (!store) {\n return;\n }\n Object.assign(store, partial);\n }\n\n getContext(): RequestContextState | undefined {\n return this.storage.getStore();\n }\n\n get<K extends keyof RequestContextState>(key: K): RequestContextState[K] | undefined {\n return this.storage.getStore()?.[key];\n }\n}\n","import { SetMetadata } from '@nestjs/common';\nimport { AUTO_TRACE } from '../tokens';\n\nexport const AutoTrace = (options: string) => {\n return SetMetadata(AUTO_TRACE, options);\n};","/**\n * DI token for ObservableService\n *\n * 注入此 token 获得 ObservableService 实例\n */\nexport const OBSERVABLE_SERVICE = Symbol('OBSERVABLE_SERVICE');\n\n/**\n * DI Token for Platform HttpClient\n *\n * 这些 token 供 PlatformModule 和内部依赖(如 AuthNPaasModule)共享使用,通过创建独立的 nestjs-common 包,避免循环依赖\n *\n * @example\n * ```typescript\n * // nestjs-core 中注册 provider\n * import { PLATFORM_HTTP_CLIENT } from '@lark-apaas/nestjs-common';\n *\n * {\n * provide: PLATFORM_HTTP_CLIENT,\n * useFactory: (svc: PlatformHttpClientService) => svc.instance,\n * inject: [PlatformHttpClientService],\n * }\n * ```\n *\n * @example\n * ```typescript\n * // nestjs-authnpaas 中注入使用\n * import { Injectable, Inject } from '@nestjs/common';\n * import { PLATFORM_HTTP_CLIENT, type SafeHttpClient } from '@lark-apaas/nestjs-common';\n *\n * @Injectable()\n * export class AuthNService {\n * constructor(\n * @Inject(PLATFORM_HTTP_CLIENT) private http: SafeHttpClient\n * ) {}\n * }\n * ```\n */\n\n/**\n * DI token for PlatformHttpClient instance\n *\n * 注入此 token 获得 SafeHttpClient 实例(不暴露 interceptors)\n */\nexport const PLATFORM_HTTP_CLIENT = Symbol('PLATFORM_HTTP_CLIENT');\n\n/**\n * DI token for AutoTraceInterceptor\n *\n * 此 token 用于元数据存取 AUTO_TRACE 装饰器的参数\n */\nexport const AUTO_TRACE = Symbol('AUTO_TRACE');\n\n/**\n * DI token for HttpClientFactory\n *\n * 用于创建独立的 HttpClient 实例,支持自定义拦截器\n *\n * @example\n * ```typescript\n * // 注入并使用\n * @Injectable()\n * export class PluginService {\n * constructor(\n * @Inject(HTTP_CLIENT_FACTORY) private factory: HttpClientFactory\n * ) {}\n *\n * createClientForPlugin(pluginKey: string) {\n * const client = this.factory.create();\n * client.interceptors.request.use((config) => {\n * config.headers['x-plugin-key'] = pluginKey;\n * return config;\n * });\n * return client;\n * }\n * }\n * ```\n */\nexport const HTTP_CLIENT_FACTORY = Symbol('HTTP_CLIENT_FACTORY');\n","import { Global, Module } from '@nestjs/common';\nimport { RequestContextService } from './services/request-context.service';\n\n@Global()\n@Module({\n providers: [RequestContextService],\n exports: [RequestContextService],\n})\nexport class CommonModule {}\n","/**\n * 从完整路径中去除 basePath 前缀,获取相对页面路由\n *\n * @param fullPath - 完整路径,如 \"/af/p/app_xxx/dashboard\"\n * @param basePath - 基础路径前缀,如 \"/af/p/app_xxx\",通常来自 CLIENT_BASE_PATH 环境变量\n * @returns 去除前缀后的相对路径,如 \"/dashboard\";如果无法处理则返回原值\n *\n * @example\n * stripBasePath('/af/p/app_xxx/dashboard', '/af/p/app_xxx') // => '/dashboard'\n * stripBasePath('/af/p/app_xxx', '/af/p/app_xxx') // => '/'\n * stripBasePath('/other/path', '/af/p/app_xxx') // => '/other/path'\n * stripBasePath(undefined, '/af/p/app_xxx') // => undefined\n * stripBasePath('/dashboard', '') // => '/dashboard'\n */\nexport function stripBasePath(\n fullPath: string | undefined | null,\n basePath: string | undefined | null,\n): string | undefined | null {\n // 如果 fullPath 为空,直接返回\n if (!fullPath) {\n return fullPath;\n }\n\n // 如果 basePath 为空,直接返回原路径\n if (!basePath) {\n return fullPath;\n }\n\n // 如果 fullPath 以 basePath 开头,去除前缀\n // 需要确保是完整的路径段匹配,避免部分匹配(如 /app_xxx 匹配到 /app_xxx_extended)\n if (fullPath.startsWith(basePath)) {\n const stripped = fullPath.slice(basePath.length);\n // 确保 stripped 为空(完全匹配)或以 '/' 开头(路径段边界)\n if (stripped === '' || stripped.startsWith('/')) {\n return stripped || '/';\n }\n }\n\n // 不匹配则返回原路径\n return fullPath;\n}\n"],"mappings":";;;;AAAA,SAASA,kBAAkB;AAC3B,SAASC,yBAAyB;;;;;;;;AAqB3B,IAAMC,wBAAN,MAAMA;SAAAA;;;EACMC,UAAU,IAAIC,kBAAAA;EAE/BC,IAAOC,SAA8BC,UAAsB;AACzD,UAAMC,QAAQ;MAAE,GAAGF;IAAQ;AAC3B,WAAO,KAAKH,QAAQE,IAAIG,OAAOD,QAAAA;EACjC;EAEAE,WAAWC,SAA6C;AACtD,UAAMF,QAAQ,KAAKL,QAAQQ,SAAQ;AACnC,QAAI,CAACH,OAAO;AACV;IACF;AACAI,WAAOC,OAAOL,OAAOE,OAAAA;EACvB;EAEAI,aAA8C;AAC5C,WAAO,KAAKX,QAAQQ,SAAQ;EAC9B;EAEAI,IAAyCC,KAA4C;AACnF,WAAO,KAAKb,QAAQQ,SAAQ,IAAKK,GAAAA;EACnC;AACF;;;;;;AC7CA,SAASC,mBAAmB;;;ACKrB,IAAMC,qBAAqBC,uBAAO,oBAAA;AAuClC,IAAMC,uBAAuBD,uBAAO,sBAAA;AAOpC,IAAME,aAAaF,uBAAO,YAAA;AA2B1B,IAAMG,sBAAsBH,uBAAO,qBAAA;;;AD3EnC,IAAMI,YAAY,wBAACC,YAAAA;AACxB,SAAOC,YAAYC,YAAYF,OAAAA;AACjC,GAFyB;;;AEHzB,SAASG,QAAQC,cAAc;;;;;;;;AAQxB,IAAMC,eAAN,MAAMA;SAAAA;;;AAAc;;;;IAHzBC,WAAW;MAACC;;IACZC,SAAS;MAACD;;;;;;ACQL,SAASE,cACdC,UACAC,UAAmC;AAGnC,MAAI,CAACD,UAAU;AACb,WAAOA;EACT;AAGA,MAAI,CAACC,UAAU;AACb,WAAOD;EACT;AAIA,MAAIA,SAASE,WAAWD,QAAAA,GAAW;AACjC,UAAME,WAAWH,SAASI,MAAMH,SAASI,MAAM;AAE/C,QAAIF,aAAa,MAAMA,SAASD,WAAW,GAAA,GAAM;AAC/C,aAAOC,YAAY;IACrB;EACF;AAGA,SAAOH;AACT;AA1BgBD;","names":["Injectable","AsyncLocalStorage","RequestContextService","storage","AsyncLocalStorage","run","context","callback","store","setContext","partial","getStore","Object","assign","getContext","get","key","SetMetadata","OBSERVABLE_SERVICE","Symbol","PLATFORM_HTTP_CLIENT","AUTO_TRACE","HTTP_CLIENT_FACTORY","AutoTrace","options","SetMetadata","AUTO_TRACE","Global","Module","CommonModule","providers","RequestContextService","exports","stripBasePath","fullPath","basePath","startsWith","stripped","slice","length"]}
|