{"_id":"@ben_wangfeng/health-sdk-test","name":"@ben_wangfeng/health-sdk-test","dist-tags":{"latest":"0.3.1"},"versions":{"0.3.1":{"name":"@ben_wangfeng/health-sdk-test","version":"0.3.1","description":"Benai Health SDK","author":{"name":"benai"},"license":"MIT","repository":{"type":"git","url":"https://cnb.cool/benaimed/benai-health-sdk"},"homepage":"https://cnb.cool/benaimed/benai-health-sdk","type":"module","engines":{"node":">=20"},"main":"./dist/index.js","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":"./dist/index.js","./package.json":"./package.json"},"scripts":{"build":"tsdown","dev":"tsdown --watch","test":"vitest","test:ci":"vitest --run","test:unit":"vitest tests/unit --run","test:integration":"vitest tests/integration --run","test:browser":"bun run build && bun tests/browser/redirect-policy.browser.ts","test:e2e:mock":"vitest --config vitest.e2e.mock.config.ts --run","test:e2e:real":"bun tests/e2e/real/require-env.ts && vitest --config vitest.e2e.real.config.ts --run","test:coverage":"vitest --coverage --run --exclude tests/unit/docs-generation.test.ts --exclude tests/unit/build-output.test.ts --exclude tests/unit/readme-examples.test.ts","lint":"biome check .","lint:fix":"biome check --write .","format":"biome format --write .","typecheck":"tsc --noEmit && tsc --noEmit -p tsconfig.browser-tests.json && tsc -p tsconfig.scripts.json && tsc -p tsconfig.tooling.json","openapi:members-me:fetch":"bun scripts/openapi/fetch-members-me-openapi.ts","openapi:members-me:generate":"bun scripts/openapi/generate-members-me-types.ts","openapi:members-me:drift":"bun scripts/openapi/check-members-me-drift.ts","openapi:members-me:check-types":"tsc --noEmit -p tsconfig.contracts.json","openapi:members-me:test":"vitest tests/contract --run","openapi:members-me:check":"bun run openapi:members-me:check-types && bun run openapi:members-me:test && bun run openapi:members-me:drift","openapi:full:fetch":"bun scripts/openapi/fetch-openapi.ts","openapi:sdk-public:fetch":"bun scripts/openapi/fetch-sdk-public-openapi.ts","openapi:sdk-public:drift":"bun scripts/openapi/check-sdk-public-drift.ts","openapi:sdk-public:check":"bun run openapi:sdk-public:drift","docs:generate":"bun scripts/docs/generate-sdk-docs.ts","docs:check":"bun scripts/docs/generate-sdk-docs.ts --check","version:sync":"bun scripts/release/sync-version.ts","prepack":"bun run build","prepublishOnly":"bun run verify:release","release":"bun run verify:release && bumpp --all --git-check --no-push --execute \"bun run version:sync\" && bun run verify:release && bun publish --ignore-scripts && git push --follow-tags","prepare":"bunx husky","analyze":"bun run build && bun run scripts/analyze-bundle.ts","analyze:visual":"bun run build && bun run scripts/visualize-bundle.ts","analyze:breakdown":"bun run scripts/bundle-breakdown.ts","size":"bun run build && size-limit","size:why":"bun run build && bun x size-limit --save-bundle bundle-stats.json && bun x webpack-bundle-analyzer bundle-stats.json/stats.json -m static -r bundle-report.html -O && echo \"Report saved to bundle-report.html\"","verify":"bun run typecheck && bun run build && bun run test:unit && bun run test:integration && bun run lint && bun run docs:check","verify:release":"bun run verify && bun run test:coverage && bun run openapi:members-me:check && bun run openapi:sdk-public:check && bun run test:e2e:mock && bun run test:browser && bun run size && bun pm pack --dry-run --ignore-scripts"},"dependencies":{"axios":"^1.18.1","loglevel":"^1.9.2","zod":"^4.4.3"},"devDependencies":{"@biomejs/biome":"2.2.4","@size-limit/preset-big-lib":"^11.2.0","@size-limit/webpack-why":"11.2.0","@types/bun":"latest","@types/node":"^24.13.2","@vitest/coverage-v8":"^3.2.6","bumpp":"^10.4.1","bundle-analyzer":"^0.0.6","husky":"^9.1.7","lint-staged":"^16.4.0","msw":"^2.14.6","openapi-typescript":"^7.13.0","size-limit":"^11.2.0","tsdown":"^0.15.12","typescript":"^5.9.2","ultracite":"5.4.4","vitest":"^3.2.6","webpack-bundle-analyzer":"^4.10.2"},"lint-staged":{"*.{js,jsx,ts,tsx,json,jsonc,css,scss,md,mdx}":["bun x ultracite fix"]},"publishConfig":{"registry":"https://registry.npmjs.org/","access":"public","tag":"latest"},"_id":"@ben_wangfeng/health-sdk-test@0.3.1","gitHead":"0a0fee96621ce125eeedb701e31a574179d3aaab","_nodeVersion":"22.22.2","_npmVersion":"10.9.7","dist":{"integrity":"sha512-+UEvBiuRQ1UrXNC/V5I/Y9Bbv+FGVfS13hdpTJYSNN1II6yL+RkSRW3AK0RH8w5Zih4kTbq60GXsKk7P11SS0A==","shasum":"4341b9b30371953b0975520b196c41fb837f2a5e","tarball":"https://registry.npmjs.org/@ben_wangfeng/health-sdk-test/-/health-sdk-test-0.3.1.tgz","fileCount":8,"unpackedSize":2885917,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIHl2BzzDW+o5HkIVuxmR7V5f2rewSL9HV1yFCkqU0oNCAiEA9Sl/63GeF3C40AHghW3EW6ZtpWqeYtdamElwmvejDYc="}]},"_npmUser":{"name":"ben_wangfeng","email":"wangfengaf@gmail.com"},"directories":{},"maintainers":[{"name":"ben_wangfeng","email":"wangfengaf@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/health-sdk-test_0.3.1_1785726067609_0.9242296857469221"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-03T03:01:07.410Z","0.3.1":"2026-08-03T03:01:07.783Z","modified":"2026-08-03T03:01:08.054Z"},"maintainers":[{"name":"ben_wangfeng","email":"wangfengaf@gmail.com"}],"description":"Benai Health SDK","homepage":"https://cnb.cool/benaimed/benai-health-sdk","repository":{"type":"git","url":"https://cnb.cool/benaimed/benai-health-sdk"},"author":{"name":"benai"},"license":"MIT","readme":"# @benai/health-sdk\n\n[![TypeScript](https://img.shields.io/badge/TypeScript-5.9+-blue.svg)](https://www.typescriptlang.org/)\n[![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)\n\n> 本爱健康 TypeScript SDK - 为诊后生活康复APP提供完整的医疗健康功能集成\n\n## 📋 概述\n\n本爱健康 SDK 是一个功能完整的 TypeScript SDK，专为医疗健康应用设计。它提供 Course、Dashboard、Enrollment、Exercise、FollowupAppointments、IM、InhalationVideos、Medals、MedicalRecord、Medication、PersonalizedPlan、Report、Alerts、Notifications、Profile、Onboarding、ConsentForm、Questionnaire 等 18 个公开模块，覆盖课程、首页健康数据、入组、运动跟练、随访预约、即时通信、宣教视频、勋章、病历、用药、个性化方案、报告、告警、通知、用户资料、业务流程、知情同意和问卷等能力。\n\n## 📘 文档入口\n\n- [完整 SDK 文档](docs/SDK_DOC.md) - 当前 `0.3.1` public API、迁移说明和 DX 约束。\n- v0 历史文档：`docs/v0/SDK_DOC.md` 是 `0.1.x` 时代的归档快照，不再作为当前实现依据。\n\n### ✨ 核心特性\n\n- 🏥 **医疗健康功能** - Course、Dashboard、Enrollment、Exercise、FollowupAppointments、IM、InhalationVideos、Medals、MedicalRecord、Medication、PersonalizedPlan、Report、Alerts、Notifications、Profile、Onboarding、ConsentForm、Questionnaire 等公开模块\n- 🔐 **安全认证** - 完整的 JWT 认证和自动 Token 刷新机制\n- 🎭 **双模式支持** - 同时支持 Mock 和 API 模式，便于开发和测试\n- 📱 **类型安全** - 100% TypeScript 编写，提供完整的类型定义\n- 🧪 **测试驱动** - 覆盖单元、集成、Mock E2E 和 OpenAPI contract 的稳定本地验证\n- 🚀 **高性能** - 基于现代 JavaScript 运行时，支持并发请求\n- 🛠️ **开发友好** - 完整的错误处理、日志系统和调试支持\n\n## 🚀 快速开始\n\n### 安装\n\n先在项目或用户级 `.npmrc` 中配置 Benai registry 和组织签发的访问令牌：\n\n```ini\n@benai:registry=https://npm.benaimed.com/\n//npm.benaimed.com/:_authToken=${BENAI_NPM_TOKEN}\n```\n\n```bash\nnpm install @benai/health-sdk\n# 或\nyarn add @benai/health-sdk\n# 或\npnpm add @benai/health-sdk\n# 或\nbun add @benai/health-sdk\n```\n\n### 运行时兼容性\n\n`@benai/health-sdk` 是 ESM-only package，要求 Node.js 20 或更高版本。Node.js 应用必须使用 `import` 或动态 `import()`；不支持 CommonJS `require()`。\n\n浏览器应用需要使用支持 ESM 的 bundler，并运行在提供 `fetch`、`AbortController` 和 `URL` 的现代浏览器环境中。本包不提供 CommonJS 构建或运行时 polyfill。\n\n### 基础使用\n\n```typescript\nimport { BenaiHealthSDK } from \"@benai/health-sdk\";\n\nconst sdk = new BenaiHealthSDK({\n  baseUrl: \"https://api.yourservice.com\",\n  logLevel: \"info\",\n});\n\nsdk.setAuthTokens({\n  accessToken: \"your-access-token\",\n  refreshToken: \"your-refresh-token\",\n});\n\n// 使用功能模块\nconst courses = await sdk.course.listToday();\nconst reports = await sdk.report.list({ reportType: \"service_weekly\" });\nconst medicationSchedules = await sdk.medication.listTodaySchedules();\nconst enrollment = await sdk.enrollment.getCurrent();\nconst userSig = await sdk.im.getUserSig();\n```\n\n## 🎭 Mock Overrides\n\n在 `mock: true` 模式下，可以使用 `sdk.mock.set()` 覆盖任意 mock provider 的返回值，便于单元测试和本地开发：\n\n```typescript\nconst sdk = new BenaiHealthSDK({\n  baseUrl: \"https://api.yourservice.com\",\n  mock: true,\n});\n\nsdk.mock.set(\"im\", \"getUserSig\", () => ({\n  usersig: \"test-user-sig\",\n  sdkappid: \"1400000000\",\n  userId: \"mock-user\",\n  expiresIn: 3600,\n}));\n\nconst userSig = await sdk.im.getUserSig();\n```\n\n使用 `sdk.mock.clear()` 可以移除所有通过 `sdk.mock.set()` 注册的覆盖。\n\n## 📚 功能模块\n\nSDK 通过 `sdk.<module>` 暴露以下模块：\n\n- `course` - 课程学习\n- `dashboard` - 首页健康数据\n- `exercise` - 运动跟练\n- `enrollment` - 入组管理\n- `followupAppointments` - 随访预约\n- `im` - 即时通信\n- `inhalationVideos` - 宣教/吸入视频\n- `medals` - 勋章激励\n- `medicalRecord` - 病历/就诊记录\n- `medication` - 用药管理\n- `personalizedPlan` - 个性化康复方案\n- `report` - 报告管理\n- `alerts` - 告警管理\n- `notifications` - 通知管理\n- `profile` - 用户资料\n- `onboarding` - 业务流程\n- `consentForm` - 知情同意\n- `questionnaire` - 问卷\n\n### 🎓 课程模块 (Course)\n\n- 获取今日课程列表\n- 分页获取课程历史\n- 查看课程详情\n- 标记课程为已读\n\n```typescript\n// 获取今日课程\nconst todayCourses = await sdk.course.listToday();\n\n// 分页获取课程，默认使用 { page: 1, pageSize: 20 }\nconst courses = await sdk.course.list({ status: \"completed\" });\n\n// 查看课程详情\nconst courseDetail = await sdk.course.getDetail(\"progress-id\");\n\n// 标记为已读\nawait sdk.course.markAsRead(\"progress-id\");\n```\n\n### 📊 报告模块 (Report)\n\n- 分页获取报告列表\n- 查看报告详情\n- 标记报告为已读\n\n```typescript\n// 获取报告列表\nconst reports = await sdk.report.list({ reportType: \"service_weekly\" });\n\n// 查看报告详情\nconst reportDetail = await sdk.report.getDetail(\"report-id\");\n\n// 标记为已读\nawait sdk.report.markAsRead(\"report-id\");\n```\n\n### 💊 用药模块 (Medication)\n\n- 获取今日用药计划\n- 分页获取用药历史\n- 完成用药打卡\n\n```typescript\n// 获取今日用药\nconst todaySchedules = await sdk.medication.listTodaySchedules();\nconst checkedMedicationIds: string[] = [];\n\n// 完成用药打卡\nconst firstSchedule = todaySchedules[0];\n\nif (firstSchedule) {\n  const checkIn = await sdk.medication.checkIn(firstSchedule);\n  checkedMedicationIds.push(checkIn.id);\n}\n```\n\n### 🏥 病历模块 (Medical Record)\n\n- 创建和查询就诊记录\n- 生成文件预签名上传 URL\n- 确认文件上传和触发解析\n- 获取文件预览链接\n\n```typescript\n// 创建就诊记录\nconst visit = await sdk.medicalRecord.createVisit({\n  visitDate: \"2026-06-04\",\n  visitType: \"outpatient\",\n  medicalInstitutionName: \"Test Hospital\",\n});\n\n// 为文件生成预签名上传 URL\nconst upload = await sdk.medicalRecord.generatePresignedUrl(visit.id, {\n  filename: \"lab-report.pdf\",\n  contentType: \"application/pdf\",\n  recordType: \"LAB_REPORT\",\n});\n\n// 上传文件到 upload.uploadUrl 后，确认文件和就诊记录\nawait sdk.medicalRecord.finalizeFileUpload(visit.id, upload.fileId);\nawait sdk.medicalRecord.confirmVisit(visit.id, [upload.fileId]);\n\n// 查询就诊记录详情，approved 后可能包含结构化病历\nconst detail = await sdk.medicalRecord.getVisit(visit.id);\n```\n\n### 🎯 个性化方案模块 (Personalized Plan)\n\n- 获取个性化康复方案\n\n```typescript\nconst plan = await sdk.personalizedPlan.get();\n```\n\n### 🫁 运动跟练模块 (Exercise)\n\n- 开始运动跟练会话\n- 上报运动进度\n- 完成会话并提交反馈\n- 查看会话历史和统计\n\n```typescript\nconst started = await sdk.exercise.start(\"course-progress-id\", { preSpo2: 98 });\nawait sdk.exercise.updateProgress(\"course-progress-id\", started.sessionId, {\n  durationMinutes: 10,\n});\nawait sdk.exercise.complete(\"course-progress-id\", started.sessionId, {\n  durationMinutes: 10,\n  afterSpo2: 97,\n});\n```\n\n### 🧾 当前入组模块 (Enrollment)\n\n- 获取当前入组信息\n- 申请入组\n- 查看入组历史\n\n```typescript\nconst enrollment = await sdk.enrollment.getCurrent();\nconst enrollmentStatuses: string[] = [];\n\nif (enrollment) {\n  enrollmentStatuses.push(enrollment.status);\n}\n```\n\n### 💬 IM 模块 (IM)\n\n- 获取 IM UserSig\n- 查询群组信息\n\n### 📈 首页模块 (Dashboard)\n\n- 获取首页健康数据\n- 查看心率基线和最新指标摘要\n\n### 📅 随访预约模块 (Followup Appointments)\n\n- 获取最新随访预约\n- 分页查看历史随访预约\n\n### 🎬 宣教视频模块 (Inhalation Videos)\n\n- 生成视频上传预签名 URL\n- 完成上传确认\n- 查看视频列表、详情和播放地址\n\n### 🏅 勋章模块 (Medals)\n\n- 获取勋章列表\n- 查看勋章详情\n- 标记勋章已查看\n\n### 🔔 告警模块 (Alerts)\n\n- 获取告警列表\n- 查看告警详情\n- 确认和解决告警\n\n### 🔔 通知模块 (Notifications)\n\n- 获取通知列表\n- 标记通知已读\n- 删除通知\n\n### 👤 用户资料模块 (Profile)\n\n- 获取用户资料\n- 更新用户资料\n\n### 🧭 入组模块 (Onboarding)\n\n- 查询业务流程进度\n\n### 📝 知情同意模块 (Consent Form)\n\n- 按项目查询知情同意书\n\n### 📋 问卷模块 (Questionnaire)\n\n- 获取初始问卷\n- 提交初始问卷答案\n\n## 🔐 认证系统\n\nSDK 提供完整的认证管理功能：\n\n```typescript\n// 设置认证令牌\nsdk.setAuthTokens({\n  accessToken: \"access-token\",\n  refreshToken: \"refresh-token\",\n});\n\n// 检查当前是否已认证且访问令牌未过期\nconst isAuthenticated = sdk.isAuthenticated();\n\n// 应用层完成登录后，使用 setAuthTokens 将令牌交给 SDK 管理\n\n// 登出\nsdk.logout();\n```\n\n## 🔌 插件与中间件\n\n```typescript\nimport {\n  BenaiHealthSDK,\n  createRetryPlugin,\n  createRequestIdPlugin,\n  createLoggerPlugin,\n  createTimeoutPlugin,\n  composePlugins,\n} from \"@benai/health-sdk\";\n\nconst sdk = new BenaiHealthSDK({ baseUrl: \"https://api.yourservice.com\", logLevel: \"info\" });\n\n// 内置插件（显式传入选项）\nsdk.use(createRetryPlugin({ count: 3 }));\nsdk.use(createRequestIdPlugin());\nsdk.use(createLoggerPlugin({ level: \"debug\" }));\n\n// 模块作用域插件\nsdk.use(\n  {\n    name: \"course-only\",\n    beforeRequest: (ctx) => {\n      ctx.request.headers = { ...ctx.request.headers, \"X-Course-Tag\": \"tag\" };\n    },\n  },\n  { modules: [\"course\"] }\n);\n\n// 单次请求生命周期插件\nconst observedStatuses: number[] = [];\n\nawait sdk.course.listToday({\n  plugins: [\n    {\n      name: \"one-off\",\n      afterResponse: (ctx) => {\n        const responseStatus = ctx.response?.status;\n        if (responseStatus !== undefined) {\n          observedStatuses.push(responseStatus);\n        }\n      },\n    },\n  ],\n});\n\n// 组合多个插件\nsdk.use(\n  composePlugins(\n    createRetryPlugin({ count: 3 }),\n    createTimeoutPlugin({ timeoutMs: 10_000 })\n  )\n);\n```\n\n#### 行为变更\n\n- `createCachePlugin()` 不传入选项时，默认 `ttlMs: 300_000`，`maxSize: 100`；所有缓存键都会自动附加 session 版本，登录用户切换、登出和 token refresh 后不会复用旧会话缓存。\n- 分页列表推荐使用 `list(filter?)` / `getHistory(options?)`；默认分页下需要传请求选项时使用 `list(filter, options?)` / `getHistory(options, requestOptions?)`；需要显式分页时使用 `list(filter, pagination, options?)` / `getHistory(options, pagination, requestOptions?)`：\n  `course.list`、`course.listHistory`、`report.list`、`alerts.list`、`notifications.list`、`medication.listSchedules`、`inhalationVideos.list`、`medals.list`、`followupAppointments.getHistory`、`medicalRecord.listVisits`、`enrollment.listHistory`、`personalizedPlan.listHistory` 已支持这些签名；旧的分页签名和旧 options object 仍保留兼容。\n\n```typescript\nconst signal = new AbortController().signal;\n\nconst visits = await sdk.medicalRecord.listVisits({ status: \"created\" }, { signal });\nconst followups = await sdk.followupAppointments.getHistory({ requireFresh: true }, { signal });\nconst enrollmentHistory = await sdk.enrollment.listHistory(\n  { status: \"active\" },\n  { page: 1, pageSize: 20 },\n  { signal }\n);\nconst planHistory = await sdk.personalizedPlan.listHistory(\n  { planType: \"exercise\", status: \"approved\" },\n  { signal }\n);\n```\n\n- 废弃兼容签名仅用于 0.1.x 代码迁移：旧分页签名和旧 options 类型会在 public `.d.ts` 中以 `@deprecated` 标记保留，不会在 patch/minor 版本删除；新代码应使用 README 中推荐的新签名，真正删除安排在后续 major 版本。\n- 分页默认值：\n  - `sdk.medicalRecord.listVisits(filter)` 默认 `pageSize: 10`。\n  - `sdk.course.list(filter)`、`sdk.course.listHistory(filter)`、`sdk.report.list(filter)`、`sdk.alerts.list(filter)`、`sdk.notifications.list(filter)`、`sdk.medication.listSchedules(filter)`、`sdk.inhalationVideos.list(filter)`、`sdk.medals.list(filter)`、`sdk.followupAppointments.getHistory(options)`、`sdk.enrollment.listHistory(filter)` 与 `sdk.personalizedPlan.listHistory(filter)` 默认 `pageSize: 20`。\n\n## ⚠️ 错误处理\n\n```typescript\nimport {\n  BenaiHealthSDK,\n  isBenaiSDKError,\n  isRetryableError,\n  formatSDKError,\n} from \"@benai/health-sdk\";\n\nconst sdk = new BenaiHealthSDK({\n  baseUrl: \"https://api.yourservice.com\",\n});\nconst retryableErrorMessages: string[] = [];\nconst nonRetryableErrorMessages: string[] = [];\n\ntry {\n  await sdk.course.listToday();\n} catch (error) {\n  if (isBenaiSDKError(error)) {\n    const formatted = formatSDKError(error);\n    const formattedError = `${formatted.code}: ${formatted.message}`;\n    if (isRetryableError(error)) {\n      retryableErrorMessages.push(formattedError);\n    } else {\n      nonRetryableErrorMessages.push(formattedError);\n    }\n  }\n}\n```\n\n## 🎭 Mock 模式\n\nSDK 内置完整的 Mock 系统，便于开发和测试：\n\n```typescript\nconst sdk = new BenaiHealthSDK({\n  baseUrl: \"https://api.yourservice.com\",\n  mock: true, // 启用 Mock 模式\n  logLevel: \"debug\",\n});\n\n// 在 Mock 模式下，所有 API 调用都会返回模拟数据\nconst courses = await sdk.course.listToday(); // 返回模拟的课程数据\n```\n\nMock provider 与 API provider 使用相同的 plugin、middleware、requestId、错误处理和 request telemetry 生命周期；request telemetry 使用 `mock://<module>/<operation>` URL，并记录对应远端接口的 HTTP method。\n\n## 🛠️ 开发工具\n\n### 测试\n\n```bash\n# 运行稳定本地测试（不包含真实后端 E2E）\nbun run test\n\n# 单元测试 / 集成测试 / Mock E2E\nbun run test:unit\nbun run test:integration\nbun run test:e2e:mock\n\n# 调试 SDK logger 输出\nBENAI_TEST_VERBOSE=1 bun run test:unit\nBENAI_TEST_VERBOSE=1 bun run test:e2e:mock\n\n# 运行测试并生成覆盖率报告\nbun run test:coverage\n```\n\n真实后端 E2E 使用 `bun run test:e2e:real`，需要显式配置真实后端环境变量；本地默认验证不依赖它。\n\n### SDK 文档生成\n\n[完整 SDK 文档](docs/SDK_DOC.md) 的 API reference 区块由 public TSDoc 注释生成。维护 public facade 时，先更新 `src/index.ts`、`src/core/config.ts` 或 `src/modules/*/*.module.ts` 中的中文 TSDoc，再运行：\n\n```bash\n# 根据 public TSDoc 重写 docs/SDK_DOC.md 的生成区块\nbun run docs:generate\n\n# 检查生成区块是否和源码注释一致\nbun run docs:check\n```\n\n`bun run verify` 会执行 `docs:check`，发布前会阻止文档和 public API 注释漂移。\n\n### 代码质量\n\n```bash\n# 代码检查\nbun run lint\n\n# 自动修复代码问题\nbun run lint:fix\n\n# 代码格式化\nbun run format\n\n# TypeScript 类型检查\nbun run typecheck\n```\n\n### 构建\n\n```bash\n# 构建项目\nbun run build\n\n# 开发模式（监听文件变化）\nbun run dev\n```\n\n## 📊 项目统计\n\n- **稳定本地验证**: 单元测试、集成测试、Mock E2E、OpenAPI contract\n- **模块数量**: 18 个公开模块 getter\n- **类型定义**: 由 package root 统一导出并通过声明产物校验\n\n## 🏗️ 架构设计\n\nSDK 采用 public facade + internal capability context 的分层架构：\n\n```text\n┌────────────────────────────────────────────────────┐\n│ App / Consumer                                     │\n├────────────────────────────────────────────────────┤\n│ BenaiHealthSDK public facade                       │\n│ modules + auth tokens + module mock controls       │\n├────────────────────────────────────────────────────┤\n│ Module layer                                       │\n│ Course | IM | MedicalRecord | Medication | ...     │\n├────────────────────────────────────────────────────┤\n│ Provider layer                                     │\n│ ApiProvider | MockProvider                         │\n├────────────────────────────────────────────────────┤\n│ Internal capability context                        │\n│ config | logger | auth token reader | http client  │\n├────────────────────────────────────────────────────┤\n│ Runtime internals                                  │\n│ HttpClient + SessionManager | Mock data stores     │\n└────────────────────────────────────────────────────┘\n```\n\nPublic API 以 `BenaiHealthSDK` facade 和 package root exports 为准。应用代码可以依赖模块 getter、`setAuthTokens`、`isAuthenticated`、`getAccessToken`、`getRefreshToken`、`logout`、`setModuleMock`、`getModuleMock`、配置项、导出的类型、`BenaiSDKError`、其具体子类（如 `AuthExpiredError`、`InvalidParameterError`、`NetworkError`、`ServerError` 等）、错误辅助函数（`isBenaiSDKError`、`isRetryableError`、`formatSDKError`）、`UploadProgressManager` 以及插件工厂。\n\n`session`、`httpClient`、`refreshPromise`、`isLoggedOut`、`ProviderRegistry`、`SDKInternalContext`、`SessionManager` 和 `HttpClient` 都是 implementation details，不属于 package public API。\n\n## 🔧 配置选项\n\n```typescript\ninterface SDKConfig {\n  baseUrl: string;                                    // API 基础 URL\n  mock?: boolean | ModuleMockConfig;                  // 是否使用 Mock 模式\n  logLevel?: \"silent\" | \"error\" | \"warn\" | \"info\" | \"debug\";\n  onTokensRefreshed?: (tokens: AuthTokens) => void;   // Token 刷新回调\n  onGlobalError?: (error: BenaiSDKError) => void;    // 全局错误处理器\n  schemaValidation?: SchemaValidationMode;            // 后端响应结构校验模式\n  onSchemaValidationError?: (issue: SchemaValidationIssue) => void; // 校验失败回调\n  axiosAdapter?: AxiosAdapter;                        // Axios 适配器\n  mockDelayMs?: number;                               // Mock 模式固定网络延迟（毫秒）\n  middleware?: SDKMiddleware[];                       // 全局中间件\n  retry?: RetryConfig;                                // 全局重试策略\n  generateRequestId?: boolean | (() => string);       // 是否自动生成 requestId\n  requestIdHeader?: string;                           // requestId propagation 的有效 HTTP header 名称\n  locale?: \"zh\" | \"en\";                               // SDK 内部错误消息语言，默认 'zh'\n  onTelemetry?: TelemetryListener;                    // 全局 telemetry 回调\n  enableTracing?: boolean;                            // 是否产出 span_started/span_ended\n  generateTraceId?: () => string;                     // 自定义 W3C trace ID 生成函数\n  tracePropagation?: boolean;                         // 是否向 baseUrl 下的 Benai API 注入 traceparent/tracestate/requestId header\n  traceSampled?: boolean;                             // SDK 创建根 trace 时是否设置 sampled flag，默认 true\n  sendSdkMetadataHeaders?: boolean;                   // 是否向 baseUrl 下的 Benai API 注入 SDK 名称、版本和运行时 header\n  telemetrySampleRate?: number;                       // telemetry 采样率，默认 1\n  telemetryIncludeHeaders?: boolean | string[];       // request_started 是否包含请求 header\n  telemetryIncludeErrorMessages?: boolean;            // telemetry 是否包含 SDK 已脱敏错误消息，默认 false\n  plugins?: SDKPlugin[];                              // 初始插件列表，等价于构造后 sdk.use()\n}\n```\n\n## 📝 类型定义\n\nSDK 提供完整的 TypeScript 类型定义：\n\n```typescript\nimport type { CourseSummary, PaginatedResponse } from \"@benai/health-sdk\";\n\ntype CoursePage = PaginatedResponse<CourseSummary>;\n```\n\n## 🤝 贡献指南\n\n1. Fork 项目\n2. 创建功能分支 (`git checkout -b feature/amazing-feature`)\n3. 提交更改 (`git commit -m 'Add some amazing feature'`)\n4. 推送到分支 (`git push origin feature/amazing-feature`)\n5. 创建 Pull Request\n\n## 📄 许可证\n\n本项目采用 MIT 许可证 - 查看 [LICENSE](LICENSE) 文件了解详情。\n\n## 📞 支持\n\n如有问题或建议，请：\n\n1. 查看 [完整 SDK 文档](docs/SDK_DOC.md)\n2. 在 [项目仓库](https://cnb.cool/benaimed/benai-health-sdk) 提交反馈\n3. 联系开发团队\n\n---\n\n**本爱健康 SDK** - 让医疗健康应用开发更简单 🏥✨\n","readmeFilename":"README.md","_rev":"1-3b58eb2b490dfa1294e5e4e321880839"}