@lark-apaas/nestjs-common 0.1.4-alpha.32 → 0.1.4-alpha.34

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.
@@ -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","../src/utils/create-timer.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 { createTimer } from './utils/create-timer';\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 /** 前端参数化页面路由 (Rpc-Persist-Apaas-Observability-Referer-Path) */\n refererPath?: string;\n /** 前端参数化 API 标识 (Rpc-Persist-Apaas-Observability-Api) */\n observabilityApi?: 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","/**\n * 创建高精度计时器\n * 使用 process.hrtime 正确计算秒 + 纳秒,支持亚毫秒测量\n * 返回一个函数,调用后返回经过的毫秒数(保留两位小数)\n */\nexport function createTimer(): () => number {\n const start = process.hrtime();\n return () => {\n const diff = process.hrtime(start);\n return Math.round((diff[0] * 1000 + diff[1] / 1e6) * 100) / 100;\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAAA;;;;;;;;;;;;;;;ACAA,oBAA2B;AAC3B,yBAAkC;;;;;;;;AAuB3B,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;;;;;;AC/CA,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;;;ACTT,SAASO,cAAAA;AACd,QAAMC,QAAQC,QAAQC,OAAM;AAC5B,SAAO,MAAA;AACL,UAAMC,OAAOF,QAAQC,OAAOF,KAAAA;AAC5B,WAAOI,KAAKC,OAAOF,KAAK,CAAA,IAAK,MAAOA,KAAK,CAAA,IAAK,OAAO,GAAA,IAAO;EAC9D;AACF;AANgBJ;","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","createTimer","start","process","hrtime","diff","Math","round"]}
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","../src/utils/create-timer.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 { createTimer } from './utils/create-timer';\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 /** 前端参数化页面路由 (Rpc-Persist-Apaas-Observability-Referer-Path) */\n refererPath?: string;\n /** 前端参数化 API 标识 (Rpc-Persist-Apaas-Observability-Api) */\n observabilityApi?: 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","/**\n * 创建高精度计时器\n * 使用 process.hrtime 正确计算秒 + 纳秒,支持亚毫秒测量\n * 返回一个函数,调用后返回经过的毫秒数(保留两位小数)\n */\nexport function createTimer(): () => number {\n const start = process.hrtime();\n return () => {\n const diff = process.hrtime(start);\n return Math.round((diff[0] * 1000 + diff[1] / 1e6) * 100) / 100;\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAAA;;;;;;;;;;;;;;;ACAA,oBAA2B;AAC3B,yBAAkC;;;;;;;;AAyB3B,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;;;;;;ACjDA,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;;;ACTT,SAASO,cAAAA;AACd,QAAMC,QAAQC,QAAQC,OAAM;AAC5B,SAAO,MAAA;AACL,UAAMC,OAAOF,QAAQC,OAAOF,KAAAA;AAC5B,WAAOI,KAAKC,OAAOF,KAAK,CAAA,IAAK,MAAOA,KAAK,CAAA,IAAK,OAAO,GAAA,IAAO;EAC9D;AACF;AANgBJ;","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","createTimer","start","process","hrtime","diff","Math","round"]}
package/dist/index.d.cts CHANGED
@@ -11,6 +11,8 @@ interface RequestContextState {
11
11
  requestRootSpan?: unknown;
12
12
  /** x-tt-env header,用于环境标识透传(如灰度/测试环境) */
13
13
  ttEnv?: string;
14
+ /** 请求来源页面路由 (X-Page-Route header) */
15
+ pageRoute?: string;
14
16
  /** 前端参数化页面路由 (Rpc-Persist-Apaas-Observability-Referer-Path) */
15
17
  refererPath?: string;
16
18
  /** 前端参数化 API 标识 (Rpc-Persist-Apaas-Observability-Api) */
package/dist/index.d.ts CHANGED
@@ -11,6 +11,8 @@ interface RequestContextState {
11
11
  requestRootSpan?: unknown;
12
12
  /** x-tt-env header,用于环境标识透传(如灰度/测试环境) */
13
13
  ttEnv?: string;
14
+ /** 请求来源页面路由 (X-Page-Route header) */
15
+ pageRoute?: string;
14
16
  /** 前端参数化页面路由 (Rpc-Persist-Apaas-Observability-Referer-Path) */
15
17
  refererPath?: string;
16
18
  /** 前端参数化 API 标识 (Rpc-Persist-Apaas-Observability-Api) */
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","../src/utils/create-timer.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 /** 前端参数化页面路由 (Rpc-Persist-Apaas-Observability-Referer-Path) */\n refererPath?: string;\n /** 前端参数化 API 标识 (Rpc-Persist-Apaas-Observability-Api) */\n observabilityApi?: 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","/**\n * 创建高精度计时器\n * 使用 process.hrtime 正确计算秒 + 纳秒,支持亚毫秒测量\n * 返回一个函数,调用后返回经过的毫秒数(保留两位小数)\n */\nexport function createTimer(): () => number {\n const start = process.hrtime();\n return () => {\n const diff = process.hrtime(start);\n return Math.round((diff[0] * 1000 + diff[1] / 1e6) * 100) / 100;\n };\n}\n"],"mappings":";;;;AAAA,SAASA,kBAAkB;AAC3B,SAASC,yBAAyB;;;;;;;;AAuB3B,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;;;;;;AC/CA,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;;;ACTT,SAASO,cAAAA;AACd,QAAMC,QAAQC,QAAQC,OAAM;AAC5B,SAAO,MAAA;AACL,UAAMC,OAAOF,QAAQC,OAAOF,KAAAA;AAC5B,WAAOI,KAAKC,OAAOF,KAAK,CAAA,IAAK,MAAOA,KAAK,CAAA,IAAK,OAAO,GAAA,IAAO;EAC9D;AACF;AANgBJ;","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","createTimer","start","process","hrtime","diff","Math","round"]}
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","../src/utils/create-timer.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 /** 前端参数化页面路由 (Rpc-Persist-Apaas-Observability-Referer-Path) */\n refererPath?: string;\n /** 前端参数化 API 标识 (Rpc-Persist-Apaas-Observability-Api) */\n observabilityApi?: 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","/**\n * 创建高精度计时器\n * 使用 process.hrtime 正确计算秒 + 纳秒,支持亚毫秒测量\n * 返回一个函数,调用后返回经过的毫秒数(保留两位小数)\n */\nexport function createTimer(): () => number {\n const start = process.hrtime();\n return () => {\n const diff = process.hrtime(start);\n return Math.round((diff[0] * 1000 + diff[1] / 1e6) * 100) / 100;\n };\n}\n"],"mappings":";;;;AAAA,SAASA,kBAAkB;AAC3B,SAASC,yBAAyB;;;;;;;;AAyB3B,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;;;;;;ACjDA,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;;;ACTT,SAASO,cAAAA;AACd,QAAMC,QAAQC,QAAQC,OAAM;AAC5B,SAAO,MAAA;AACL,UAAMC,OAAOF,QAAQC,OAAOF,KAAAA;AAC5B,WAAOI,KAAKC,OAAOF,KAAK,CAAA,IAAK,MAAOA,KAAK,CAAA,IAAK,OAAO,GAAA,IAAO;EAC9D;AACF;AANgBJ;","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","createTimer","start","process","hrtime","diff","Math","round"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lark-apaas/nestjs-common",
3
- "version": "0.1.4-alpha.32",
3
+ "version": "0.1.4-alpha.34",
4
4
  "description": "Common NestJS utilities",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",