@crvouga/mockingbird-service-aha

中文文档
3.11.0发布于 2026/11/11MITTypeScriptES Module构建来源已验证近 30 天下载 1.6万

状态 AHA phlebotomy 仿真器, 上面有签名的 HTTP 命令, 状态 webhooks, 和真正的确定性 SSH/ SFTP 结果服务器 .

中文使用文档
文档目录9 节
  1. 安装
  2. 使用量
  3. 路线
  4. 网络呼号
  5. 管理员(超过标准合同)
  6. 命名空间
  7. SFTP 结果交付
  8. 故意不模拟
  9. API 密码

@crvouga/ mockingbird-service-aha (英语)

本地模拟器。 真正的API合同. 部分 嘲笑鸟。 。 。 .

国家模拟器 AHA (高级卫生学院) 在家理发 用于测试的 API 套接字:HMAC署名的创建顺序并取消,以及——其主要工作——订单状态webhooks AHA 邮递员 销售商没有拉API,所以每个下游效应(EMR预约, storefront status, "blood draw") 起始为 webhook; 仿真器按需发出一个, 每一个 字段,我们的处理器读取, 所以ZIP路线的血液工作路径 终于可以测试.

  • 行动范围: 支助
  • 供应商不公布具体内容:合同openapi.yaml由我们手写 消费者的zod计划 和电线形状.

安装

npm install -D @crvouga/mockingbird-service-aha

仅限无害环境管理。 节点 22或 Bun 1.2. 无土生属地. 给它服务 npx mockingbird-aha serve, (中文(简体) ), createServer 从 ./server (节点),或 createRuntime 带有任意 获取服务器 .

使用量

将应用程序指向模拟器:

信封数值
AHA_API_URLhttp://127.0.0.1:8799 (实验室提供者路径已经允许回回回回;); AhaService 需要 http 例外,参见 G-A1 / S10.2)
AHA_API_KEY 编号 : AHA_API_SECRET什么都行,否则一对就成了 --api-key 编号 : --api-secret 准确验证签名
AHA_USE_LEGACY_AUTHtrue 开关到 X-<Partner>-Auth-Key (例如:. X-Acme-Auth-Key); 两种模式都被接受
AHA_WEBHOOK_SECRET与 --webhook-secret
npx mockingbird-aha serve --port 8799 \
  --webhook-url http://127.0.0.1:3000/bloodwork/aha-webhook \
  --webhook-secret "$AHA_WEBHOOK_SECRET" \
  --api-key "$AHA_API_KEY" --api-secret "$AHA_API_SECRET" \
  --envelope raw --auto-schedule 2000
import { createHmac } from "node:crypto"
import { createServer } from "@crvouga/mockingbird-service-aha/server"

// Test setup: the emulator, posting order-status webhooks to the app's handler.
const server = await createServer({
  webhooks: { url: "http://127.0.0.1:3000/bloodwork/aha-webhook", secret: "aha-webhook-secret" },
})

// Application code: its usual signed request, with AHA_API_URL set to server.url.
const [apiUrl, apiKey, apiSecret] = [server.url, "aha-key", "aha-secret"]
const path = "/v1/acme/create-order"
const timestamp = Date.now().toString()
const signature = createHmac("sha256", apiSecret)
  .update(`${apiKey}:${path}:${timestamp}`)
  .digest("base64")
const created = await fetch(`${apiUrl}${path}`, {
  method: "POST",
  headers: {
    "content-type": "application/json",
    "X-API-KEY": apiKey,
    "X-TIMESTAMP": timestamp,
    "X-SIGNATURE": signature,
  },
  body: JSON.stringify({
    partner_order_id: "AC-101", patient_id: "pat-1", biological_sex: "Female",
    patient_first_name: "Ada", patient_last_name: "Lovelace", patient_dob: "1990-12-10",
    patient_phone_number: "5555550100", patient_address_line1: "501 N 5th St",
    patient_city: "Phoenix", patient_state: "AZ", patient_zipcode: "85004",
    service_type: "Full Service", npi: "1234567893", ordering_physician: "Dr. Grace Hopper",
    test_codes: [{ test_code: "CBC", test_description: "Complete blood count" }],
  }),
})
// → {content: {partner_order_id: "AC-101", order_number: "AHA-…"}, message, status: "SUCCESS"}

// Test control: move the order as AHA would. Each transition posts a webhook to the app.
const transition = (body: unknown) =>
  fetch(`${server.url}/__admin/orders/AC-101/transition`, {
    method: "POST",
    headers: { "content-type": "application/json" },
    body: JSON.stringify(body),
  })
// AHA books the draw: our handler books the EMR appointment for 10:30 Denver time.
await transition({
  status: "Scheduled",
  scheduledAt: "2026-10-01T16:30:00Z",
  timeZone: "America/Denver",
})
// The phlebotomist checks out with a sample: our handler sets vitalBloodDrawn.
await transition({ status: "Check Out", drawStatus: "Sample Collected" })
await server.close()

路线

这个 {partner} 路径段是您的账户的 sult( 例如) . acme);模拟器接受任何.

路线行为
POST /v1/{partner}/create-order鉴定尸体(partner_order_id病人的田园, biological_sex, (中文(简体) ), service_type, (中文(简体) ), npi, (中文(简体) ), ordering_physician, (中文(简体) ), test_codes选项 preferred_schedule_date/time, (中文(简体) ), patient_timezone。创建 或更新: 重复 partner_order_id 保留它 order_number答复 {content: {partner_order_id, order_number}, message, status: "SUCCESS"}。 。 。 .
POST /v1/{partner}/cancel{partner_order_id, notes: [{note_type: "CANCELLATION", notes}]} → {message, status}显示 a Cancelled webhook( 关闭为 cancelWebhook: false) (中文(简体) ). 未知的id → 404;采集样本的命令 → 200 status: "ERROR"AHA协会 order_number 已接受为 partner_order_id 因为我们的实验室提供客户端 把它送到那里(G -A1).

奥特. HMAC 模式 : X-API-KEY, (中文(简体) ), X-TIMESTAMP (划时代,在相隔5分钟后), X-SIGNATURE = 基数64 HMAC-SHA256(机密, "<apiKey>:<path>:<timestamp>"),在其中 path 这是 请求没有主机、正机或路径 /__admin/ns/<name> 前缀。 带有已知密钥 + 密钥 (单位:千美元)--api-key/--api-secret 或者说 credentials (b) 准确核实签名; 未配置任何密钥的签名会被接受,签名只检查形状。 遗产 模式 : X-<Partner>-Auth-Key (+ 数) X-API-Version: 1.0),例如. X-Acme-Auth-Key; 任何伙伴名称为 已接受。 失败者为401人 {status: "ERROR", message}。 。 。 .

信封 (G- A1). AhaService 期待原始数据 {content, message, status}· ; AhaLabProvider 预期 {success: true, data: {…}}。 Raw 是默认值;选择使用 --envelope raw|wrapped 或每个命名空间 PUT /__admin/settings {"envelope": "wrapped"}。 。 。 .

引证无能. X-Idempotency-Key (实验室提供路径):同一键和身体重放 存储的响应( e)idempotent-replayed: true; 不同身体的同一键为409.

网络呼号

POST <webhook-url> (我们的路线 : POST /bloodwork/aha-webhook与 Authorization: Token <AHA_WEBHOOK_SECRET>每一个身体都有 status, (中文(简体) ), partnerOrderId, (中文(简体) ), ahaOrderId, (中文(简体) ), 加上,按地位(顺序的IANA区所有当地时间):

status额外字段
Scheduled, (中文(简体) ), RescheduledscheduleServiceTime (单位:千美元)YYYY-MM-DDTHH:mm:ss,瞬间可解析)和 scheduleServiceTimeZone (IANA)——由我们的处理者要求,尽管没有参加DTO——加 scheduledServiceDate/Time/TimeZone 财务报告和审定财务报表 scheduleConfirmationDate/Time/TimeZone
Check IncheckInDate, (中文(简体) ), checkInTime, (中文(简体) ), checkInTimeZone
Check OutdrawStatus (默认) Sample Collected, (中文(简体) ), drawStatusDate/Time/TimeZone
Lab Testing In ProgressdropOffDate, (中文(简体) ), dropOffTime, (中文(简体) ), dropOffTimeZone
Cancelled, (中文(简体) ), Non Scheduled Update—— 说

drawStatus 值 : Sample Collected, (中文(简体) ), Completed (拖动), Patient Refused, (中文(简体) ), UTO, (中文(简体) ), Patient Not Home, (中文(简体) ), Patient Rescheduled, (中文(简体) ), Order Cancelled, (中文(简体) ), Others, (中文(简体) ), Patient Asked to Reschedule (拖曳失败). 非-2xx 的答案被重审(立即,5 s, 5分30分2小时); GET /__admin/webhooks, (中文(简体) ), …/events, (中文(简体) ), …/replay, (中文(简体) ), …/flush 和往常一样.

管理员(超过标准合同)

路线效果
POST /__admin/orders/:partnerOrderId/transition{status, drawStatus?, scheduledAt?, timeZone?} 发出网钩. status 以上为任何值(例和 _ 原谅;未知值被逐字发送). scheduledAt (ISO 或 ocho ms) 默认命令的首选位置, 否则在接下来的 24 h出局 ; Rescheduled 默认为一天后. timeZone 默认命令 patient_timezone,则 America/New_York。 。 。 . :partnerOrderId 也可能是 order_number。 。 。 .
PUT /__admin/settings{envelope?, credentials?: [{apiKey, apiSecret?}], allowLegacy?, timestampToleranceMs?, defaultTimeZone?, cancelWebhook?, autoSchedule?: {afterMs, leadMs?} | ms | null} 呼叫的命名空间. GET 让他们看到隐藏的秘密.
POST /__admin/tick释放所有 autoSchedule 在模拟器时钟上到期的 webhook( 已安装的模拟器每100 mss) .
GET /__admin/orders取名空间的命令(编号,状态,预约,区-没有病人数据).

错误预设( E)POST /__admin/faults {"preset": "<name>", "count"?: n}: bad_signature (401个), rate_limited (429个) rate_limit, (中文(简体) ), server_error (500个), order_error (200个带内 status: "ERROR", (中文(简体) ), invalid_response (200个身体,zod计划不接受), webhook_duplicate, (中文(简体) ), webhook_reorder, (中文(简体) ), webhook_drop。 。 。 .

命名空间

x-mockingbird-namespace页:1 /__admin/ns/<name> 前缀在 AHA_API_URL (签名仍涵盖 或 API 键: PUT /__admin/credentials {"credentials": {"<AHA_API_KEY>": "<namespace>"}}。 。 。 .

SFTP 结果交付

createAhaSftpServer 从 ./sftp 在麻黄端口启动真实的SSH/SFTP服务器并共享命令状态 createRuntime它支持密码或公用钥匙认证,主机钥匙验证, list编号 :stat, 二进制上下载, 原子临时文件重命名, 删除, 嵌入目录, 稳定的 POSIX 权限和仿真器时钟时间戳 。 决定性的 Ed25519 主机密钥在运行间稳定.

import { Client } from "ssh2"
import { createRuntime } from "@crvouga/mockingbird-service-aha"
import { createAhaSftpServer } from "@crvouga/mockingbird-service-aha/sftp"

// Test setup: the SFTP endpoint, sharing order state with the HTTP emulator, and one result.
const runtime = createRuntime()
const sftp = await createAhaSftpServer({
  runtime,
  accounts: [{ username: "aha", password: "local-test-password" }],
})
sftp.seed("/outbox/AHA-1001_result.pdf", new TextEncoder().encode("%PDF-1.4"))

// Application code: the ssh2 client it already uses. Only host, port and host key change.
const client = new Client()
await new Promise<void>((resolve, reject) =>
  client.once("ready", resolve).once("error", reject).connect({
    host: sftp.host,
    port: sftp.port,
    username: "aha",
    password: "local-test-password",
    hostVerifier: (key: Buffer) => key.equals(sftp.hostPublicKey),
  }),
)
const pdf = await new Promise<Buffer>((resolve, reject) =>
  client.sftp((error, session) => {
    if (error) return reject(error)
    session.readFile("/outbox/AHA-1001_result.pdf", (error, bytes) =>
      error ? reject(error) : resolve(bytes),
    )
  }),
)
client.end()
await sftp.close()

使用 seed() 安装任意的二进制固定件或 publishResult(orderId, bytes) 摆放一个稳定 AHA-…_result.pdf 内 /outbox。第一个完整的下载将链接的 HTTP 命令移动到 Lab Testing In Progress 并发布其网页;重复投票/倒数不重复这一过渡. fault() 强制下一场行动断开、拒绝许可、报告磁盘完整或只接受部分写入. journal() 曝光路径、 操作名称、 字节计数和结果—— 绝不是证书或文件内容 . reset(namespace?) 清除决定性文件系统状态。 复制目的地名称失败, 所以 .tmp ——最终更名为原子.

故意不模拟

  • 下游S3摄入和 aha_results_queue sFTP 交接后处理.
  • 真正的调度: AHA 联系病人; 除非测试转换顺序, 任何动作都不要动 或组合 autoSchedule。 。 。 .
  • 可用- ZIP 列表(我们的应用程序在呼叫 AHA 之前,先决定资格).
  • 患者细节经过验证,从未被存储或回放.
  • 真正销售商使用的哪个信封(G-A1):两者都得到服务,每个命名空间一个.

API 密码

导出仁爱说明
AhaAPI类进程模拟器 : fetch(request), (中文(简体) ), reset(), (中文(简体) ), transition(id, {status, drawStatus?, scheduledAt?, timeZone?}), (中文(简体) ), tick(), (中文(简体) ), orders()选项: sqlite, (中文(简体) ), now, (中文(简体) ), namespace, (中文(简体) ), settings, (中文(简体) ), onWebhook, (中文(简体) ), wallClock。 。 。 .
createRuntime函数服务合同的模拟者。 选项 : webhooks: {url, secret, retryDelaysMs?, fetch?}, (中文(简体) ), settings, (中文(简体) ), tickMs, (中文(简体) ), wallClock, (中文(简体) ), clock, (中文(简体) ), seed, (中文(简体) ), adminKey, (中文(简体) ), onLog。 。 。 .
AHA_PRESETS对象每个命名的断层预设.
AHA_NAMESPACE字符串服务名称, "aha"。 。 。 .
WEBHOOK_PATH字符串"/bloodwork/aha-webhook"接收器的路线.
ORDER_STATUSES, (中文(简体) ), DRAW_STATUSES数组销售商状况拼写了webhooks使用.
verifyAuth函数HMAC / 仿真器的遗留验证( 错误消息, 或 undefined) (中文(简体) ).
apiKeyCredential函数请求所携带的 API 密钥( 如何使用证书映射来命名空间 ) .
isTimeZone, (中文(简体) ), zonedParts, (中文(简体) ), zonedToEpoch函数IANA-zone帮助器曾填补了本地日期/时间字段.
document, (中文(简体) ), operationIds, (中文(简体) ), supportedOperationIds数值销售商为OpenAPI合同及其业务代号.
createServer, (中文(简体) ), serveTarget, (中文(简体) ), DEFAULT_PORT (单位:千美元)./server(中文(简体) )节点服务过来 node:http (表单每100毫秒勾出) serve CLI目标; 第8799口.
createAhaSftpServer (单位:千美元)./sftp(中文(简体) )节点Real SSH/ SFTP 端点带有决定主机密钥/文件系统,共享顺序状态,传输控制,以及一个等离子端口.

部分 嘲笑鸟。 。 。 .

版本与发布记录

@crvouga/mockingbird-service-aha 共发布 42 个版本,首次发布于 2026/09/23;过去 12 个月发布了 42 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
3.11.0latest2026/11/11今天
3.10.92026/11/038 天前
3.10.82026/11/038 天前
3.10.72026/11/038 天前
3.10.62026/11/038 天前
3.10.52026/11/038 天前

关于 @crvouga/mockingbird-service-aha 的常见问题

@crvouga/mockingbird-service-aha 是什么?

状态 AHA phlebotomy 仿真器, 上面有签名的 HTTP 命令, 状态 webhooks, 和真正的确定性 SSH/ SFTP 结果服务器 .

如何安装 @crvouga/mockingbird-service-aha?

在项目目录运行 npm install @crvouga/mockingbird-service-aha,或使用 pnpm add / yarn add / bun add。它还提供命令行工具 mockingbird-aha,可以用 npx mockingbird-aha 直接运行。

@crvouga/mockingbird-service-aha 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@crvouga/mockingbird-service-aha 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@crvouga/mockingbird-service-aha 可以商用吗?

@crvouga/mockingbird-service-aha 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@crvouga/mockingbird-service-aha 的下载量有多少?

根据 npm 官方下载统计,@crvouga/mockingbird-service-aha 在 2026/10/11 – 2026/11/09 这 30 天内被下载 16,247 次,日均约 542 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@crvouga/mockingbird-service-aha 还在维护吗?

最新版本 3.11.0 发布于 2026/11/11(今天);至今共发布 42 个版本,其中过去 12 个月发布了 42 个;首次发布于 2026/09/23。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。