{
  "version": "1.9.0",
  "generatedAt": "2026-06-10T06:43:00.194Z",
  "hooks": [
    {
      "name": "toAwaitFetch",
      "description": "一个强大的 HTTP 请求工具，支持链式调用和解构两种使用方式，提供了完整的错误处理、重试机制和全局配置功能。",
      "typeDefinitions": "export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS';\n\nexport type RequestType = 'json' | 'form' | 'text' | 'blob' | 'arraybuffer';\n\nexport type ResponseType = 'json' | 'text' | 'blob' | 'arraybuffer' | 'formData';\n\nexport type HeadersConfig = \n\nexport type ValidateStatusFn = (status: number) => boolean;\n\nexport type ValidateResponseFn<T = any> = (response: T) => boolean;\n\nexport type TransformResponseFn<TRaw = any, TTransformed = any> = (data: TRaw) => TTransformed;\n\nexport type HttpErrorHandler<T = any> = (\n  error: Error,\n  status: number,\n  response: T,\n  suppressError: () => void,\n) => void | Promise<void>;\n\nexport type BusinessErrorHandler<T = any> = (\n  error: Error,\n  response: T,\n  suppressError: () => void\n) => void | Promise<void>;\n\nexport type NetworkErrorHandler = (\n  error: Error,\n  suppressError: () => void,\n) => void | Promise<void>;\n\nexport type ShouldRetry = (attempt: number, error: FetchError) => boolean | Promise<boolean>;\n\nexport interface RetryConfig {\n  /**\n   * 是否开启重试\n   * @default false\n   */\n  enabled?: boolean;\n  /**\n   * 最大重试次数\n   * @default 3\n   */\n  maxRetryCount?: number;\n  /**\n   * 重试延迟时间（毫秒）\n   * @default 1000\n   */\n  delay?: number;\n  /**\n   * 自定义重试判断函数，返回 true 表示继续重试，false 表示停止重试\n   * 如果未提供，将使用默认的重试逻辑\n   */\n  shouldRetry?: ShouldRetry;\n}\n\nexport interface RequestConfig<TRequest = any, TResponse = any> {\n  /**\n   * 请求 URL（必需）\n   */\n  url: string;\n  /**\n   * HTTP 方法\n   * @default 'GET'\n   */\n  method?: HttpMethod;\n  /**\n   * 请求数据\n   * - GET 请求时，data 会转换为 URL 查询参数\n   * - POST/PUT/PATCH 等请求时，data 作为请求体数据\n   * - POST 请求的查询参数应该放在 URL 中（如 '/api/users?page=1'）\n   */\n  data?: TRequest;\n  /**\n   * 验证请求状态码是否符合要求的函数，返回布尔类型\n   */\n  validateStatus?: ValidateStatusFn;\n  /**\n   * 验证请求内容是否符合业务要求，返回布尔类型\n   */\n  validateResponse?: ValidateResponseFn<TResponse>;\n  /**\n   * 响应数据转换函数\n   * 用于将原始响应数据转换为业务中使用的数据格式\n   * 在 validateResponse 验证通过后执行\n   * @example\n   * transformResponse: (data) => data.result.list\n   */\n  transformResponse?: TransformResponseFn<TResponse, TResponse>;\n  /**\n   * 用来捕获 validateStatus 为 false 时进行错误处理的函数\n   */\n  onHttpError?: HttpErrorHandler<TResponse>;\n  /**\n   * 用来捕获 validateResponse 为 false 时进行错误处理的函数\n   * 主要用来处理业务场景的错误\n   */\n  onBusinessError?: BusinessErrorHandler<TResponse>;\n  /**\n   * 用来捕获网络异常的错误处理函数\n   * 主要用来处理无网络、跨域等报错问题\n   */\n  onNetworkError?: NetworkErrorHandler;\n  /**\n   * 重试配置\n   */\n  retry?: RetryConfig;\n  /**\n   * 请求超时时间（毫秒）\n   */\n  timeout?: number;\n  /**\n   * 请求头配置\n   * 可以是 HeadersInit、Record<string, string> 或返回这些类型的函数\n   * 函数接收 { url, method } 作为参数，可用于动态生成请求头\n   */\n  headers?: HeadersConfig;\n  /**\n   * 取消请求的信号（可与返回的 cancel 一起使用）\n   */\n  signal?: AbortSignal;\n  /**\n   * 是否发送凭证（Cookie/授权头）\n   * @default true\n   */\n  withCredentials?: boolean;\n  /**\n   * 请求数据类型\n   * @default 'json'\n   */\n  requestType?: RequestType;\n  /**\n   * 响应数据类型\n   * @default 'json'\n   */\n  responseType?: ResponseType;\n}\n\nexport interface GlobalConfig<TResponse = any> {\n  /**\n   * 基础 URL，会与请求的 url 拼接\n   */\n  baseURL?: string;\n  /**\n   * 验证请求状态码是否符合要求的函数，返回布尔类型\n   */\n  validateStatus?: ValidateStatusFn;\n  /**\n   * 验证请求内容是否符合业务要求，返回布尔类型\n   */\n  validateResponse?: ValidateResponseFn<TResponse>;\n  /**\n   * 响应数据转换函数\n   * 用于将原始响应数据转换为业务中使用的数据格式\n   * 在 validateResponse 验证通过后执行\n   * @example\n   * // 全局配置：提取通用的 data 字段\n   * transformResponse: (response) => response.data\n   */\n  transformResponse?: TransformResponseFn<TResponse, TResponse>;\n  /**\n   * 用来捕获 validateStatus 为 false 时进行错误处理的函数\n   */\n  onHttpError?: HttpErrorHandler<TResponse>;\n  /**\n   * 用来捕获 validateResponse 为 false 时进行错误处理的函数\n   * 主要用来处理业务场景的错误\n   */\n  onBusinessError?: BusinessErrorHandler<TResponse>;\n  /**\n   * 用来捕获网络异常的错误处理函数\n   * 主要用来处理无网络、跨域等报错问题\n   */\n  onNetworkError?: NetworkErrorHandler;\n  /**\n   * 重试配置\n   */\n  retry?: RetryConfig;\n  /**\n   * 请求超时时间（毫秒）\n   */\n  timeout?: number;\n  /**\n   * 请求头配置\n   * 可以是 HeadersInit、Record<string, string> 或返回这些类型的函数\n   * 函数接收 { url, method } 作为参数，可用于动态生成请求头\n   */\n  headers?: HeadersConfig;\n  /**\n   * 是否发送凭证（Cookie/授权头）\n   * @default true\n   */\n  withCredentials?: boolean;\n  /**\n   * 请求数据类型\n   * @default 'json'\n   */\n  requestType?: RequestType;\n  /**\n   * 响应数据类型\n   * @default 'json'\n   */\n  responseType?: ResponseType;\n}\n\nexport interface FetchResponse<T = any> {\n  /**\n   * 响应数据\n   */\n  data: T;\n  /**\n   * HTTP 状态码\n   */\n  status: number;\n  /**\n   * 状态文本\n   */\n  statusText: string;\n  /**\n   * 响应头\n   */\n  headers: Headers;\n  /**\n   * 原始 Response 对象\n   */\n  response: Response;\n}\n\nexport interface FetchError<T = any> {\n  /**\n   * 错误信息\n   */\n  message: string;\n  /**\n   * 错误类型\n   */\n  type: 'http' | 'business' | 'network' | 'timeout';\n  /**\n   * HTTP 状态码（如果是 HTTP 错误）\n   */\n  status?: number;\n  /**\n   * 响应数据（如果有）\n   */\n  response?: T;\n  /**\n   * 原始错误对象\n   */\n  error?: Error | TypeError | DOMException;\n}\n\nexport type FetchResult<T = any> = [T | undefined, FetchError | undefined, boolean, FetchResponse<T> | undefined];\n\nexport interface FetchResultPromise<T = any> extends Promise<FetchResult<T>> {\n  /**\n   * 成功回调（重载 Promise 的 then 方法）\n   * @param callback 成功时的回调函数，接收数据和可选的完整响应对象\n   * @returns 返回自身，支持链式调用\n   */\n  then(callback: (data: T, response: FetchResponse<T>) => void): this;\n  /**\n   * 取消当前请求（触发 AbortController）\n   */\n  cancel(): void;\n  \n  /**\n   * Promise 的 then 方法（保持兼容性）\n   */\n  then<TResult1 = FetchResult<T>, TResult2 = never>(\n    onfulfilled?: ((value: FetchResult<T>) => TResult1 | PromiseLike<TResult1>) | null | undefined,\n    onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null | undefined\n  ): Promise<TResult1 | TResult2>;\n  \n  /**\n   * HTTP 验证错误回调\n   * @param callback HTTP 验证错误时的回调函数（validateStatus 返回 false）\n   * @returns 返回自身，支持链式调用\n   */\n  catchHttp(callback: (error: FetchError) => void): this;\n  \n  /**\n   * 业务验证错误回调\n   * @param callback 业务验证错误时的回调函数（validateResponse 返回 false）\n   * @returns 返回自身，支持链式调用\n   */\n  catchBusiness(callback: (error: FetchError) => void): this;\n  \n  /**\n   * 网络错误回调（网络错误、超时等）\n   * @param callback 网络错误时的回调函数\n   * @returns 返回自身，支持链式调用\n   */\n  catchNetwork(callback: (error: FetchError) => void): this;\n  \n  /**\n   * Promise 的 catch 方法（保持兼容性）\n   */\n  catch<TResult = never>(\n    onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | null | undefined\n  ): Promise<FetchResult<T> | TResult>;\n}\n\nexport interface FetchInstance {\n  /**\n   * 发送请求\n   * @param config 请求配置\n   * @returns FetchResultPromise<TResponse> 支持链式调用和解构\n   */\n  request<TRequest = any, TResponse = any>(\n    config: RequestConfig<TRequest, TResponse>\n  ): FetchResultPromise<TResponse>;\n\n  /**\n   * 设置全局配置\n   * @param config 全局配置\n   */\n  setGlobalConfig(config: GlobalConfig): void;\n  /**\n   * 获取当前全局配置\n   */\n  getGlobalConfig(): GlobalConfig;\n  /**\n   * 合并全局配置\n   * @param config 部分全局配置\n   */\n  mergeGlobalConfig(config: Partial<GlobalConfig>): void;\n\n  /**\n   * GET 请求（支持链式调用）\n   * @param url 请求 URL\n   * @param params 请求参数（会转换为 URL 查询参数）\n   * @param config 请求配置（不包含 url、method 和 data）\n   * @returns FetchResultPromise<TResponse> 支持链式调用和解构\n   */\n  sendGet<TResponse = any, TRequest = any>(\n    url: string,\n    params?: TRequest,\n    config?: Omit<RequestConfig<TRequest, TResponse>, 'url' | 'method' | 'data'>\n  ): FetchResultPromise<TResponse>;\n\n  /**\n   * POST 请求（支持链式调用）\n   * 默认使用 JSON 格式发送数据\n   * @param url 请求 URL\n   * @param data 请求数据\n   * @param config 请求配置（不包含 url、method 和 data）\n   * @returns FetchResultPromise<TResponse> 支持链式调用和解构\n   */\n  sendPost<TResponse = any, TRequest = any>(\n    url: string,\n    data?: TRequest,\n    config?: Omit<RequestConfig<TRequest, TResponse>, 'url' | 'method' | 'data'>\n  ): FetchResultPromise<TResponse>;\n\n  /**\n   * POST 请求，使用 FormData 格式发送数据（支持文件上传）\n   * @param url 请求 URL\n   * @param data 请求数据（对象会自动转换为 FormData）\n   * @param config 请求配置（不包含 url、method 和 data）\n   * @returns FetchResultPromise<TResponse> 支持链式调用和解构\n   */\n  sendPostForm<TResponse = any, TRequest = any>(\n    url: string,\n    data?: TRequest,\n    config?: Omit<RequestConfig<TRequest, TResponse>, 'url' | 'method' | 'data'>\n  ): FetchResultPromise<TResponse>;\n\n  /**\n   * POST 请求，发送 Blob 数据\n   * @param url 请求 URL\n   * @param data 请求体（JSON/可序列化对象）\n   * @param config 请求配置（不包含 url、method 和 data）\n   * @returns FetchResultPromise<Blob> 支持链式调用和解构\n   */\n  sendPostBlob<TRequest = any>(\n    url: string,\n    data?: TRequest,\n    config?: Omit<RequestConfig<TRequest, Blob>, 'url' | 'method' | 'data'>\n  ): FetchResultPromise<Blob>;\n\n  /**\n   * GET 请求，响应返回 Blob 数据（用于下载文件）\n   * @param url 请求 URL\n   * @param params 请求参数（会转换为 URL 查询参数）\n   * @param config 请求配置（不包含 url、method 和 data）\n   * @returns FetchResultPromise<Blob> 支持链式调用和解构\n   */\n  sendGetBlob<TRequest = any>(\n    url: string,\n    params?: TRequest,\n    config?: Omit<RequestConfig<TRequest, Blob>, 'url' | 'method' | 'data'>\n  ): FetchResultPromise<Blob>;\n}\n\nexport type CreateFetch = (globalConfig?: GlobalConfig) => FetchInstance;\n\nexport type ToAwaitFetch = CreateFetch &",
      "templates": {
        "default": "import { sendGet, sendPost } from 'xn-fe-tools'\n\ninterface User {\n  id: number\n  name: string\n}\n\ninterface CreateUserRequest {\n  name: string\n}\n\n// GET 请求（响应类型明确，请求参数省略）\nconst [data, error, success] = await sendGet<User[]>('/api/users')\n\n// POST 请求\nconst [created] = await sendPost<User, CreateUserRequest>('/api/users', { name: 'John' })"
      },
      "docPath": "docs/tools/toAwaitFetch.md",
      "rulePath": "docs/rules/toAwaitFetch.mdc",
      "hasDoc": true,
      "hasRule": true
    },
    {
      "name": "toSentrySetup",
      "description": "Sentry 统一初始化工具集，支持 Vue 3、Vue 2、React 和纯浏览器环境。通过 sub-path exports 实现模块隔离，主入口 `xn-fe-tools` 不包含任何 Sentry 代码，Sentry SDK 已内置打包，**无需单独安装任何 Sentry 包**。",
      "typeDefinitions": "export interface BreadcrumbsConfig {\n  /** 是否记录 console 输出，默认 true */\n  console?: boolean\n  /** 是否记录 DOM 交互事件，默认 true */\n  dom?: boolean\n  /** 是否记录 fetch 请求，默认 true */\n  fetch?: boolean\n  /** 是否记录 XMLHttpRequest 请求，默认 true */\n  xhr?: boolean\n  /** 是否记录浏览器导航历史，默认 true */\n  history?: boolean\n}\n\nexport interface SentryBaseOptions {\n  /** Sentry DSN，必填 */\n  dsn: string\n  /** 运行环境，默认 'production' */\n  environment?: string\n  /** 是否启用 Sentry，默认 true */\n  enabled?: boolean\n  /** 性能追踪采样率，默认 0.1（10%） */\n  tracesSampleRate?: number\n  /** 会话回放采样率，默认 0.1（10%） */\n  replaysSessionSampleRate?: number\n  /** 错误回放采样率，默认 1.0（100%） */\n  replaysOnErrorSampleRate?: number\n  /**\n   * 是否启用 browserTracingIntegration，默认 true\n   * 简化配置参数，优先级高于 integrations 数组\n   */\n  enableTracing?: boolean\n  /**\n   * 是否启用 replayIntegration，默认 true\n   * 简化配置参数，优先级高于 integrations 数组\n   */\n  enableReplay?: boolean\n  /**\n   * 面包屑配置，传入时会使用该配置创建 breadcrumbsIntegration 并覆盖默认面包屑行为\n   * 简化配置参数，优先级高于 integrations 数组\n   */\n  breadcrumbs?: BreadcrumbsConfig\n  /** 事件上报前的回调，可用于过滤或修改事件 */\n  beforeSend?: (event: unknown) => unknown | null\n  /** 自定义 integrations 数组，与默认 integrations 合并 */\n  integrations?: unknown[]\n  /** 其他 Sentry 原生 init 配置项（透传） */\n  [key: string]: unknown\n}\n\nexport interface Vue3SentryOptions extends SentryBaseOptions {\n  /** Vue 3 应用实例，必填 */\n  app: unknown\n  /** Vue Router 实例，可选；提供时配置路由级别性能追踪 */\n  router?: unknown\n}\n\nexport interface Vue2SentryOptions extends SentryBaseOptions {\n  /** Vue 2 构造函数，必填 */\n  Vue: unknown\n  /** Vue Router 实例，可选；提供时配置路由级别性能追踪 */\n  router?: unknown\n}\n\nexport interface ReactSentryOptions extends SentryBaseOptions {\n  /** React Router 相关配置，可选；提供时配置路由级别性能追踪 */\n  reactRouter?: unknown\n}\n\nexport interface BrowserSentryOptions extends SentryBaseOptions {}\n\n\nexport interface SentrySetupReturn {\n  /** Sentry 是否已成功初始化 */\n  isInitialized: boolean\n  /** 手动上报异常，返回 event ID */\n  captureException: (error: unknown) => string | undefined\n  /** 手动上报消息，返回 event ID */\n  captureMessage: (message: string) => string | undefined\n  /** 设置/清除当前用户信息 */\n  setUser: (user: { id?: string; email?: string; username?: string } | null) => void\n}\n\nexport interface SentryViteConfig {\n  /** Sentry 组织 slug */\n  org: string\n  /** Sentry 项目 slug */\n  project: string\n  /** Sentry Auth Token */\n  authToken: string\n  /** Source Map 配置 */\n  sourcemaps?: {\n    /** 构建后自动删除的文件 glob，默认 ['./dist/\\*\\*\\/*.map'] */\n    filesToDeleteAfterUpload?: string[]\n  }\n  /** 其他自定义配置 */\n  [key: string]: unknown\n}\n\nexport function isValidDsn(dsn: string | undefined): dsn is string;\n\nexport function createUninitializedReturn(): SentrySetupReturn;\n\nexport function buildIntegrations(\n  options: SentryBaseOptions,\n  frameworkIntegrations: unknown[],\n  sentry:;\n\nexport function mergeConfig<T extends SentryBaseOptions>(\n  userConfig: T\n): Omit<T, 'enableTracing' | 'enableReplay' | 'breadcrumbs'>;\n\nexport function getSentryViteConfig(config: SentryViteConfig): SentryViteConfig;\n\nexport function toReactSentrySetup(options: ReactSentryOptions): SentrySetupReturn;\n\nexport function toSentrySetup(options: BrowserSentryOptions): SentrySetupReturn;\n\nexport function toVue2SentrySetup(options: Vue2SentryOptions): SentrySetupReturn;\n\nexport function toVue3SentrySetup(options: Vue3SentryOptions): SentrySetupReturn;",
      "templates": {
        "default": "import { toVue3SentrySetup, replayIntegration, breadcrumbsIntegration } from 'xn-fe-tools/sentry/vue3'\n\ntoVue3SentrySetup({\n  dsn: 'https://xxx@sentry.io/xxx',\n  app,\n  enableReplay: false, // 关闭默认 replay\n  integrations: [\n    replayIntegration({ maskAllText: false }), // 自定义 replay 配置\n    breadcrumbsIntegration({ console: false }),\n  ],\n})"
      },
      "docPath": "docs/tools/toSentrySetup.md",
      "rulePath": "docs/rules/toSentrySetup.mdc",
      "hasDoc": true,
      "hasRule": true
    },
    {
      "name": "useInputNumber",
      "description": "一个用于管理数字输入、精度控制和格式化展示的 Vue Hook。",
      "typeDefinitions": "export interface UseInputNumberOptions {\n  precision?: MaybeRefOrGetter<number>; // 小数点位数\n  decimalLimitMode?: MaybeRefOrGetter<'discard' | 'ceil' | 'round'>; // 处理小数位超限的方式\n  autoFixed?: MaybeRefOrGetter<boolean | 'pad' | 'trim'>; // 是否自动补全/修正小数点后的位数\n  max?: number; // 最大值\n  min?: number; // 最小值\n  allowNegative?: MaybeRefOrGetter<boolean>; // 是否允许负数\n  showThousands?: MaybeRefOrGetter<boolean>; // 是否显示千分位\n}\n\nexport interface UseInputNumberReturn {\n  value: Ref<string>;\n  displayValue: ComputedRef<string>;\n  handleInput: (val: string | number | undefined) => void;\n  handleBlur: () => void;\n  formatter: (val: string) => string;\n  parser: (val: string) => string;\n  clean: (val: string | number | undefined) => string;\n}\n\nexport function useInputNumber(options?: UseInputNumberOptions): UseInputNumberReturn;\n\nexport function useInputNumber(value: MaybeRefOrGetter<string | number | undefined>, options?: UseInputNumberOptions): UseInputNumberReturn;\n\nexport function useInputNumber(\n  valueOrOptions?: MaybeRefOrGetter<string | number | undefined> | UseInputNumberOptions,\n  maybeOptions?: UseInputNumberOptions\n): UseInputNumberReturn;",
      "templates": {
        "default": "<script setup lang=\"ts\">\nimport { useInputNumber } from 'xn-fe-tools'\n\n// 基础用法：第一个参数传入初始值，第二个参数传入配置选项\nconst { value, displayValue, handleInput, handleBlur } = useInputNumber('100.50', {\n  precision: 2,\n  showThousands: true,\n})\n</script>\n\n<template>\n  <div>\n    <input\n      :value=\"displayValue\"\n      @input=\"e => handleInput((e.target as HTMLInputElement).value)\"\n      @blur=\"handleBlur\"\n    />\n    <p>原始数值: {{ value }}</p>\n  </div>\n</template>",
        "element-plus": "<script setup lang=\"ts\">\nimport { watch } from 'vue'\nimport { useInputNumber } from 'xn-fe-tools'\n\nconst props = withDefaults(\n  defineProps<{\n    modelValue?: string | number\n    displayValue?: string | number\n    precision?: number\n    autoFixed?: boolean\n    max?: number\n    min?: number\n    allowNegative?: boolean\n    showThousands?: boolean\n  }>(),\n  {\n    modelValue: '',\n    precision: 0,\n    autoFixed: true,\n    showThousands: false,\n    allowNegative: false,\n  }\n)\n\nconst emits = defineEmits(['update:modelValue', 'update:displayValue', 'change', 'blur', 'input'])\n\n// 使用 Hook：第一个参数传 Getter，第二个参数传 props\nconst {\n  value,\n  displayValue,\n  handleBlur: _handleBlur,\n  parser,\n  formatter,\n  handleInput,\n} = useInputNumber(() => props.modelValue, props)\n\nwatch(value, val => {\n  emits('update:modelValue', val)\n  emits('change', val)\n})\n\nwatch(displayValue, val => {\n  emits('update:displayValue', val)\n})\n\nconst handleBlur = () => {\n  _handleBlur()\n  emits('blur')\n}\n\n// 注意：在使用 v-model=\"value\" 时，el-input 会自动处理输入\n</script>\n\n<template>\n  <el-input\n    v-bind=\"$attrs\"\n    v-model=\"value\"\n    @blur=\"handleBlur\"\n    @input=\"handleInput\"\n    :formatter=\"formatter\"\n    :parser=\"parser\"\n  >\n    <!-- 透传插槽 -->\n    <template v-for=\"(_, name) in $slots\" #[name]=\"scope\">\n      <slot :name=\"name\" v-bind=\"scope || {}\" />\n    </template>\n  </el-input>\n</template>",
        "ant-design-vue": "<script setup lang=\"ts\">\nimport { watch } from 'vue'\nimport { Input } from 'ant-design-vue'\nimport { useInputNumber } from 'xn-fe-tools'\n\nconst props = withDefaults(\n  defineProps<{\n    modelValue?: string | number\n    displayValue?: string | number\n    precision?: number\n    autoFixed?: boolean\n    max?: number\n    min?: number\n    allowNegative?: boolean\n    showThousands?: boolean\n  }>(),\n  {\n    modelValue: '',\n    displayValue: '',\n    precision: 0,\n    autoFixed: true,\n    showThousands: false,\n    allowNegative: false,\n  }\n)\n\nconst emits = defineEmits(['update:modelValue', 'update:displayValue', 'change', 'blur'])\n\nconst {\n  value,\n  displayValue,\n  handleBlur: _handleBlur,\n  handleInput: _handleInput,\n  clean: _clean,\n} = useInputNumber(() => props.modelValue, props)\n\n// 同步状态输出\nwatch(value, val => {\n  emits('update:modelValue', val)\n})\n\nwatch(displayValue, val => {\n  emits('update:displayValue', val)\n})\n\nconst handleBlur = () => {\n  _handleBlur()\n  emits('blur')\n}\n\nconst handleChange = (e: any) => {\n  const inputEl = e.target\n  const oldRawValue = inputEl.value\n  const oldCursorPos = inputEl.selectionStart || 0\n\n  // 使用 hook 内部的 clean 逻辑来计算光标前真正会保留的字符数量\n  // 这样就能自动处理“中间输入负号被过滤”等情况，保持计数的一致性\n  const validBeforeCursor = _clean(oldRawValue.slice(0, oldCursorPos)).length\n\n  // 执行 hook 清洗逻辑\n  _handleInput(oldRawValue)\n\n  const newVal = displayValue.value\n\n  // 强制同步 DOM\n  if (inputEl.value !== newVal) {\n    inputEl.value = newVal\n\n    // 恢复光标位置：找到第 validBeforeCursor 个有效字符在 newVal 中的位置\n    let newPos = 0\n    let validCount = 0\n    while (validCount < validBeforeCursor && newPos < newVal.length) {\n      // 这里的判断逻辑要与 clean 保持一致：只有数字、小数点、负号被视为有效字符\n      if (/[\\d.-]/.test(newVal[newPos])) {\n        validCount++\n      }\n      newPos++\n    }\n\n    inputEl.setSelectionRange(newPos, newPos)\n  }\n\n  emits('change', e)\n}\n</script>\n\n<template>\n  <Input v-bind=\"$attrs\" :value=\"displayValue\" @change=\"handleChange\" @blur=\"handleBlur\">\n    <!-- 透传 Ant Design Vue 的插槽 (prefix, suffix, addonBefore, addonAfter 等) -->\n    <template v-for=\"(_, name) in $slots\" #[name]=\"scope\">\n      <slot :name=\"name\" v-bind=\"scope || {}\" />\n    </template>\n  </Input>\n</template>"
      },
      "docPath": "docs/tools/useInputNumber.md",
      "rulePath": "docs/rules/useInputNumber.mdc",
      "hasDoc": true,
      "hasRule": true
    },
    {
      "name": "useRequestCache",
      "description": "用于处理请求缓存的 Hook，支持内存缓存、持久化缓存（LocalStorage/SessionStorage）、请求去重（并发共享）和结果转换。",
      "typeDefinitions": "export type CacheStrategy = 'memory' | 'localStorage' | 'sessionStorage' | 'indexedDB'\n\nexport type CacheKeyGenerator<P> = string | ((params: P) => string)\n\nexport type CacheTimeValue = number | boolean\n\nexport interface UseRequestCacheOptions<T = any, P = any, R = any> {\n  /** 请求函数 */\n  requestFn: (params: P) => Promise<R>\n  \n  /** 缓存唯一标记，用于区分不同请求。可以是字符串或生成函数 */\n  cacheKey: CacheKeyGenerator<P>\n  \n  /** 缓存时间。`true` = 永久缓存，`false`/`0` = 不缓存，数字 = 毫秒数。默认 5 分钟 (300000ms) */\n  cacheTime?: CacheTimeValue\n  \n  /** Storage 策略，默认为 'memory' */\n  storage?: MaybeRefOrGetter<CacheStrategy>\n  \n  /** 请求参数（响应式或普通对象），用于生成动态 key */\n  params?: MaybeRefOrGetter<P>\n  \n  /** 结果转换函数，在返回数据前进行转换 */\n  transformResult?: (data: R) => T\n  \n  /** 是否立即执行，默认 true */\n  immediate?: boolean\n\n  /** 手动指定的 storage key 前缀，默认 'req_cache_' */\n  storageKeyPrefix?: string\n\n  /** 首次请求是否强制更新（跳过缓存），默认为 false */\n  forceRefreshOnFirst?: MaybeRefOrGetter<boolean>\n\n  /** IndexedDB 数据库名称，仅在 storage 为 'indexedDB' 时有效，默认 'RequestCacheDB' */\n  dbName?: string\n\n  /** IndexedDB 存储对象名称，仅在 storage 为 'indexedDB' 时有效，默认 'request_cache' */\n  storeName?: string\n\n  /** 请求成功的回调 */\n  onSuccess?: (data: T) => void\n\n  /** 请求失败的回调 */\n  onError?: (error: any) => void\n}\n\nexport interface UseRequestCacheReturn<T> {\n  data: Ref<T | undefined>\n  loading: Ref<boolean>\n  error: Ref<any>\n\n  /** 执行请求 (优先使用缓存) */\n  run: () => Promise<T | undefined>\n\n  /** 刷新（忽略缓存，强制请求） */\n  refresh: () => Promise<T | undefined>\n\n  /** 清除指定 key 的缓存，如果不传则清除当前自动生成的 key */\n  clearCache: (key?: string) => Promise<void>\n}\n\nexport interface CacheSnapshot {\n  /** 缓存的原始 key（不含 storageKeyPrefix） */\n  key: string\n  /** 缓存的原始数据（未经 transformResult 处理） */\n  data: unknown\n  /** 写入时间戳（毫秒） */\n  timestamp: number\n  /** 所在 storage 策略 */\n  storage: CacheStrategy\n  /** 是否已过期（但尚未被清除） */\n  isExpired: boolean\n}\n\nexport interface RequestCacheFilter {\n  /** 指定要操作的 key 列表，不传则匹配所有 */\n  keys?: string[]\n  /** 按 storage 策略过滤，不传则匹配所有 */\n  storage?: CacheStrategy | CacheStrategy[]\n  /** 按 storageKeyPrefix 过滤，不传则匹配所有 */\n  storageKeyPrefix?: string\n}\n\nexport function useRequestCache<T = any, P = any, R = any>(\n  options: UseRequestCacheOptions<T, P, R>\n): UseRequestCacheReturn<T>;\n\nexport async function clearRequestCache(filter?: RequestCacheFilter): Promise<void>;\n\nexport async function getRequestCache(filter?: RequestCacheFilter): Promise<CacheSnapshot[]>;\n\nexport interface SetRequestCacheEntry {\n  /** 缓存 key，需与对应 useRequestCache 实例的 cacheKey 保持一致 */\n  key: string\n  /** 要注入的原始数据（与 requestFn 返回类型一致） */\n  data: unknown\n  /** 缓存时间。`true`/不传 = 永久缓存，`false`/`0` = 不缓存，数字 = 毫秒数。默认永久不过期 */\n  cacheTime?: CacheTimeValue\n  /** Storage 策略，默认 'memory' */\n  storage?: CacheStrategy\n  /** storage key 前缀，默认 'req_cache_' */\n  storageKeyPrefix?: string\n  /** IndexedDB 数据库名，默认 'RequestCacheDB' */\n  dbName?: string\n  /** IndexedDB store 名，默认 'request_cache' */\n  storeName?: string\n}\n\nexport async function setRequestCache(\n  entries: SetRequestCacheEntry | SetRequestCacheEntry[]\n): Promise<void>;",
      "templates": {
        "default": "<script setup lang=\"ts\">\nimport { useRequestCache } from 'xn-fe-tools'\nimport { ref, computed } from 'vue'\n\n// 模拟请求函数\nconst fetchUserInfo = async (params: { id: number }) => {\n  return new Promise(resolve =>\n    setTimeout(() => {\n      resolve({ id: params.id, name: `User ${params.id}`, role: 'admin' })\n    }, 500)\n  )\n}\n\nconst userId = ref(1)\n\nconst { data, loading, run, refresh } = useRequestCache({\n  // 请求函数\n  requestFn: fetchUserInfo,\n  // 参数 (响应式)\n  params: computed(() => ({ id: userId.value })),\n  // 缓存 Key (动态)\n  cacheKey: params => `user-info-${params.id}`,\n  // 缓存时间 5 分钟\n  cacheTime: 5 * 60 * 1000,\n  // 自动执行\n  immediate: true,\n})\n</script>\n\n<template>\n  <div>\n    <div v-if=\"loading\">Loading...</div>\n    <div v-else>\n      <pre>{{ data }}</pre>\n    </div>\n\n    <button @click=\"userId++\">Next User (New Request)</button>\n    <button @click=\"userId--\">Prev User (Cached?)</button>\n    <button @click=\"refresh\">Force Refresh</button>\n  </div>\n</template>"
      },
      "docPath": "docs/tools/useRequestCache.md",
      "rulePath": "docs/rules/useRequestCache.mdc",
      "hasDoc": true,
      "hasRule": true
    },
    {
      "name": "useTableRequest",
      "description": "用于表格请求的 Hook，支持分页、防抖、自动/手动请求、参数变化自动刷新等功能。",
      "typeDefinitions": "export type RequestFunction<P, R> = (params: P) => Promise<R>\n\nexport interface PaginationConfig {\n  /** 默认页码，默认 1 */\n  defaultPage?: number\n  /** 默认每页数量，默认 10 */\n  defaultPageSize?: number\n  /** 页码字段名，默认 'page' */\n  pageKey?: string\n  /** 每页数量字段名，默认 'pageSize' */\n  pageSizeKey?: string\n}\n\nexport interface TransformResultData<T> {\n  /** 列表数据 */\n  list: T[]\n  /** 总数 */\n  total: number\n}\n\nexport interface UseTableRequestOptions<T = any, R = any, P = any> {\n  /** 请求参数（响应式） */\n  params?: MaybeRefOrGetter<P>\n  /** 是否立即执行，默认 true */\n  immediate?: boolean\n  /** 防抖延迟（毫秒），默认 0（不防抖） */\n  debounceDelay?: number\n  /** 分页配置 */\n  pagination?: PaginationConfig\n  /** 数据列表字段名，默认空（直接使用 response），支持点号分隔的嵌套路径，如 \"data\" 或 \"data.list\" */\n  dataField?: string\n  /** 总数字段名，默认空（自动推断），支持点号分隔的嵌套路径，如 \"total\" 或 \"data.total\" */\n  totalField?: string\n  /** 当前页码字段名，默认空（不修正），支持点号分隔的嵌套路径，如 \"page\" 或 \"data.currentPage\" */\n  currentPageField?: string\n  /** 请求前转换参数 */\n  transformParams?: (params: P, pagination: { page: number; pageSize: number }) => any\n  /** 请求后转换结果，返回 { list, total } */\n  transformResult?: (response: R) => TransformResultData<T>\n  /** 请求成功回调 */\n  onSuccess?: (data: T[], response: R) => void\n  /** 请求失败回调 */\n  onError?: (error: Error) => void\n  /** 是否开启增量模式（滚动加载），默认 false */\n  incremental?: boolean\n}\n\nexport interface PaginationState {\n  /** 当前页码 */\n  page: number\n  /** 每页数量 */\n  pageSize: number\n  /** 总数 */\n  total: number\n}\n\nexport interface UseTableRequestReturn<T> {\n  /** 列表数据 */\n  data: Ref<T[]>\n  /** 加载状态 */\n  loading: Ref<boolean>\n  /** 错误信息 */\n  error: Ref<Error | null>\n  /** 分页信息 */\n  pagination: PaginationState\n  /** 手动触发请求 */\n  run: () => Promise<void>\n  /** 翻页 */\n  onPageChange: (page: number) => Promise<void>\n  /** 改变每页数量 */\n  onSizeChange: (size: number) => Promise<void>\n  /** 刷新数据（增量模式下清空数据并重置页码，普通模式下等同于 run） */\n  refresh: () => Promise<void>\n}\n\nexport interface UseTableRequestGlobalConfig {\n  /** 是否立即执行，默认 true */\n  immediate?: boolean\n  /** 防抖延迟（毫秒），默认 0（不防抖） */\n  debounceDelay?: number\n  /** 分页配置 */\n  pagination?: PaginationConfig\n  /** 数据列表字段名，默认空（直接使用 response），支持点号分隔的嵌套路径，如 \"data\" 或 \"data.list\" */\n  dataField?: string\n  /** 总数字段名，默认空（自动推断），支持点号分隔的嵌套路径，如 \"total\" 或 \"data.total\" */\n  totalField?: string\n  /** 当前页码字段名，默认空（不修正），支持点号分隔的嵌套路径，如 \"page\" 或 \"data.currentPage\" */\n  currentPageField?: string\n  /** 是否开启增量模式（滚动加载），默认 false */\n  incremental?: boolean\n}\n\nexport function setTableRequestGlobalConfig(config: UseTableRequestGlobalConfig): void;\n\nexport function getTableRequestGlobalConfig(): UseTableRequestGlobalConfig;\n\nexport function useTableRequest<T = any, R = any, P extends object = any>(\n  requestFn: RequestFunction<any, R>,\n  options: UseTableRequestOptions<T, R, P> = {}\n): UseTableRequestReturn<T>;",
      "templates": {
        "default": "<script setup lang=\"ts\">\nimport { ref, computed } from 'vue'\nimport { useTableRequest } from 'xn-fe-tools'\n\ninterface User {\n  id: number\n  name: string\n}\n\ninterface ApiResponse {\n  code: number\n  data: {\n    records: User[]\n    total: number\n  }\n}\n\ninterface QueryParams {\n  keyword: string\n}\n\n// 模拟请求函数\nconst fetchUserList = (params: QueryParams) => {\n  // 这里的 fetch 返回 Promise<ApiResponse>\n  return fetch(`/api/users?${new URLSearchParams(params as any)}`).then(\n    res => res.json() as Promise<ApiResponse>\n  )\n}\n\nconst search = ref('')\n\nconst { data, loading, error, pagination, run, onPageChange, onSizeChange } = useTableRequest<\n  User,\n  ApiResponse,\n  QueryParams\n>(fetchUserList, {\n  params: computed(() => ({ keyword: search.value })),\n  immediate: true,\n  debounceDelay: 300,\n  transformResult: (res: ApiResponse) => ({\n    list: res.data.records,\n    total: res.data.total,\n  }),\n})\n</script>\n\n<template>\n  <div>\n    <input v-model=\"search\" placeholder=\"搜索...\" />\n\n    <div v-if=\"loading\">加载中...</div>\n    <div v-else-if=\"error\">{{ error.message }}</div>\n    <table v-else>\n      <tr v-for=\"item in data\" :key=\"item.id\">\n        <td>{{ item.name }}</td>\n      </tr>\n    </table>\n\n    <div class=\"pagination\">\n      <span>共 {{ pagination.total }} 条</span>\n      <button @click=\"onPageChange(pagination.page - 1)\" :disabled=\"pagination.page <= 1\">\n        上一页\n      </button>\n      <span>第 {{ pagination.page }} 页</span>\n      <button\n        @click=\"onPageChange(pagination.page + 1)\"\n        :disabled=\"pagination.page * pagination.pageSize >= pagination.total\"\n      >\n        下一页\n      </button>\n    </div>\n  </div>\n</template>",
        "element-plus": "<script setup lang=\"ts\">\nimport { useTableRequest } from 'xn-fe-tools'\n\nconst { data, loading, pagination, onPageChange, onSizeChange } = useTableRequest(fetchData, {\n  transformResult: res => ({ list: res.data, total: res.total }),\n})\n</script>\n\n<template>\n  <el-table :data=\"data\" v-loading=\"loading\">\n    <!-- columns -->\n  </el-table>\n\n  <el-pagination\n    v-model:current-page=\"pagination.page\"\n    v-model:page-size=\"pagination.pageSize\"\n    :total=\"pagination.total\"\n    :page-sizes=\"[10, 20, 50, 100]\"\n    layout=\"total, sizes, prev, pager, next\"\n  />\n</template>",
        "ant-design-vue": "<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport { useTableRequest } from 'xn-fe-tools'\n\nconst { data, loading, pagination, run, onPageChange, onSizeChange } = useTableRequest(fetchData, {\n  // ... options\n})\n\n// 适配 Ant Design Vue 的分页对象\nconst antdPagination = computed(() => ({\n  current: pagination.page,\n  pageSize: pagination.pageSize,\n  total: pagination.total,\n  showSizeChanger: true,\n  onChange: onPageChange,\n  onShowSizeChange: (_: number, size: number) => onSizeChange(size),\n}))\n\nconst handleTableChange = (pag: any) => {\n  onPageChange(pag.current)\n  if (pag.pageSize !== pagination.pageSize) {\n    onSizeChange(pag.pageSize)\n  }\n}\n</script>\n\n<template>\n  <a-table\n    :dataSource=\"data\"\n    :loading=\"loading\"\n    :pagination=\"antdPagination\"\n    @change=\"handleTableChange\"\n  >\n    <!-- columns -->\n  </a-table>\n</template>"
      },
      "docPath": "docs/tools/useTableRequest.md",
      "rulePath": "docs/rules/useTableRequest.mdc",
      "hasDoc": true,
      "hasRule": true
    }
  ]
}