@crvouga/mockingbird-service-makor-cpg

中文文档
0.2.0发布于 2026/09/24MITTypeScriptES Module构建来源已验证近 30 天下载 1.4万

Stateful mock of the legacy Makor AI (CPG) API: care plans, plus-user, bloodwork webhook, subscriptions, Wholescripts orders, AI patient summaries and async-review scripts (processing → complete on the mock clock), with permissive CORS for browser-direct

原始使用文档
文档目录8 节
  1. Install
  2. Usage
  3. Routes
  4. CORS
  5. Admin (beyond the standard contract)
  6. Namespaces
  7. Deliberately not modelled
  8. API

@crvouga/mockingbird-service-makor-cpg

Stateful mock of the legacy Makor AI API ("CPG") for test suites. It covers:

  • care-plan details and the plus-user toggle (Intercom sync, user-actions queue);
  • the bloodwork-results webhook (ODX);
  • subscription and Wholescripts order reads;
  • AI patient summaries and async-review scripts, which the EMR frontend calls straight from the browser.

Generation answers at once and deterministically. Review scripts move from processing to complete on the mock clock, so the EMR panels and the backend jobs run with no Railway staging server and no 30–50 s LLM wait.

This is not the in-repo makor-ecosystem gateway (MAKOR_GATEWAY_URL). That gateway is out of scope; see "Deliberately not modelled".

  • Operation coverage: SUPPORT.md
  • The vendor publishes no spec. The contract (openapi.yaml) is hand-authored from our consumers' wire shapes: makor-ai.types.ts and the EMR async-review-script.types.ts / user-summary.service.ts.

Install

npm install -D @crvouga/mockingbird-service-makor-cpg

ESM only. Node >= 22 or Bun >= 1.2. No native dependencies. Serve it with npx mockingbird-makor-cpg serve, createServer from ./server (Node), or createRuntime with any Fetch server.

Usage

npx mockingbird-makor-cpg serve --port 8806 --processing-ms 2000
ConsumerSetting
Backend MakorAiClientServiceMAKOR_AI_API_URL=http://127.0.0.1:8806, MAKOR_AI_API_KEY=<any>. Seam G-K1: validation.schema.ts accepts https only today, so loopback http must be allowed.
EMR frontend (cpg-api.ts)NEXT_PUBLIC_MAKOR_API_URL=http://127.0.0.1:8806, NEXT_PUBLIC_MAKOR_API_KEY=<any>. The browser sends the key.
import { createRuntime } from "@crvouga/mockingbird-service-makor-cpg"

const cpg = createRuntime({ settings: { processingMs: 45_000 } })
const admin = (method: string, path: string, body?: unknown) =>
  cpg.fetch(
    new Request(`http://makor.test/__admin${path}`, {
      method,
      headers: { "content-type": "application/json" },
      ...(body === undefined ? {} : { body: JSON.stringify(body) }),
    }),
  )

// Member 1652 has an approved plan (the Intercom sync reads it); nobody else does (404, normal).
await admin("PUT", "/care-plans/1652", { status: "Approved", state: "CA" })
// …the EMR generates a review script: it answers `processing` at once…
cpg.clock.advance(45_000)
// …and the next GET /api/async-review-script/1652/<labTestId> answers `complete`.

Routes

Every route needs x-api-key. Any non-empty key is accepted unless apiKeys is set. Errors are {error, message, statusCode}.

RouteBehaviour
GET /api/care-plans/current-care-plan-details/{userId}{carePlanId, status, pricing{…7 fields}, state} for a seeded plan. With no plan it answers 404 {error: "Not Found", message: "No active or approved care plan found for this user"}. The backend treats that exact message as an expected absence and logs it at warn level.
PATCH /api/care-plans/plus-user/{userId}{plusUser: boolean} → {success: true, message}, or the same 404 when there is no plan.
POST /api/bloodwork/webhook{userId, labResultsId} → 202 {message: "Webhook received"}. The ids are recorded (GET /__admin/bloodwork).
GET /api/subscription/status/{userId}{success: true, data: <subscription row>} while a seeded subscription is active, else {success: true, data: {hasActiveSubscription: false, message}}.
POST /api/subscription/cancel/{userId}{success: true, data: {subscription_id, status: "canceled", message}}. Already cancelled gives data.alreadyCancelled: true; none gives data.success: false.
GET /api/wholescripts-orders/user/{userId}?page=&count={data, page, count, total, totalPages} over seeded orders. Defaults are page 1 and count 10.
POST /api/v2/generate-user-summary{user_id, intake_forms?, free_text_entries?, llm_model?} → {user_id, summary{general_summary, past_visits, intake_summary}, biomarker_analysis}, stored as the user's most recent summary. It answers at once; the real server takes 30–50 s (see slow_generation). The content comes from a fixture, or is a deterministic placeholder that never echoes the input.
GET /api/v2/user-summary/{cpgUserId}{cpgUserId, summary, isMostRecent: true, createdAt}, or 404 when there is no summary (normal).
GET /api/async-review-script/{userId}/{labTestId}{review: {id, labTestId, reviewType, status, scriptContent, errorMessage, metadata, isMostRecent, createdAt, updatedAt}} for the most recent review of that lab test, or 404 when there is none (normal).
POST /api/async-review-script/generate{userId, labTestId, intakeForm?, chartingNotes?, demographics?} → {message, reviewId, status, reviewType, scriptContent}. It answers at once. With processingMs 0 (the default) the status is complete and the content is included. Otherwise it is processing with scriptContent: null, and it becomes complete once processingMs has passed on the mock clock. reviewType is comparative when the user already has a review for another lab test.
POST /api/async-review-script/regenerate/{userId}/{labTestId}The same, as a new version (keeping the type), or 404 when nothing was ever generated.

Script content comes from the most specific fixture: user + lab test, then user, then lab test, then *. Without a fixture it is a deterministic placeholder ScriptContent, with every labFindings group, nutrition recommendations and fiber guidance filled in. Intake text, charting notes and demographics are validated but never stored, echoed or journaled.

CORS

The EMR frontend calls this API straight from the browser. cpg-api.ts sends x-api-key and Content-Type: application/json from the page, so every call is preceded by a CORS preflight. The mock:

  • answers every OPTIONS request with 204, unauthenticated, since browsers send no custom headers on a preflight;
  • echoes Origin in Access-Control-Allow-Origin, or sends * when there is no Origin;
  • allows GET, POST, PUT, PATCH, DELETE, OPTIONS;
  • echoes Access-Control-Request-Headers, defaulting to content-type, x-api-key, x-mockingbird-namespace, authorization;
  • exposes x-mockingbird and sets Access-Control-Max-Age: 600.

It stamps the same headers on every response: vendor, 401, fault presets, admin and health. An injected 500 then reaches the page as a 500, not as a CORS "network error". This is deliberately permissive, because the key is public in the browser bundle anyway. withCors(request, response) and CORS_HEADERS are exported.

Admin (beyond the standard contract)

RouteEffect
PUT /__admin/care-plans/:userIdSeed a plan with {carePlanId?, status? (default Active), pricing? (merged over defaults), state? (default TX)}. GET reads it (including plusUser); DELETE removes it.
PUT /__admin/subscriptions/:userIdSeed a subscription row. The given fields are merged over an active default.
PUT /__admin/wholescripts-orders/:userIdSeed orders: [order, …] or {orders: [...]}.
GET /__admin/bloodworkThe bloodwork webhooks received (ids only).
PUT /__admin/fixtures/summary{userId? (default *), summary, biomarker_analysis?} sets what generation returns.
PUT /__admin/fixtures/review-script{userId? (*), labTestId? (*), scriptContent} sets what review generation produces.
POST /__admin/summariesSeed a stored summary: {cpgUserId, summary?: {summary, biomarker_analysis}, createdAt?}.
POST /__admin/reviewsSeed a stored review: {userId, labTestId, status? (complete), scriptContent?, reviewType?, errorMessage?, readyInMs?}. GET /__admin/reviews?userId= lists them.
GET/PUT /__admin/settings{apiKeys?: string[], processingMs?: number} for the calling namespace.
POST /__admin/tickComplete every review that is due now. Reads also settle due reviews lazily.

Fault presets (POST /__admin/faults {"preset": "<name>", "count"?: n, "latencyMs"?: ms}):

PresetEffect
slow_generationThe three generation calls take 40 s: the real 30–50 s, which is at the edge of the EMR's 50 s summary timeout.
generation_failedA summary answers 500. A review script answers 500 {status: "failed", errorMessage} and is stored as failed; the panel shows errorMessage.
care_plan_invalid_shapeA 200 with no pricing. The backend's schema check fails, and it logs an error and returns null.
bloodwork_not_acceptedThe webhook answers 200 instead of 202. The backend warns and returns null.
route_missingEvery route answers 404 Cannot GET …. This is a wrong route, which the backend logs at error level, unlike the no-plan 404.
unauthorizedEvery call answers 401.
server_errorEvery call answers 500.

Namespaces

  • the x-mockingbird-namespace header (the EMR's axios instance can add it);
  • a /ns/<name> prefix on the base URL;
  • the API key: PUT /__admin/credentials {"credentials": {"<x-api-key>": "<namespace>"}}.

The request journal records operation ids, statuses and the user, lab test and review ids. It never records request bodies.

Deliberately not modelled

  • The makor-ecosystem gateway (MAKOR_GATEWAY_URL: /v1/makor/*, /v1/care-team-chat/*, /v1/plans/*). It is in-repo, not third-party, and a stub for it (an SSE replayer) is tracked separately.
  • Real AI output. Summaries and scripts are fixtures or deterministic placeholders, and biomarker_analysis is passed through as given.
  • The real server's internal polling. The 30–50 s latency is only a preset.
  • Stripe side effects of subscription cancel. Only the stored row changes.
  • Review history listing (ReviewHistoryResponse), which no live caller reaches.

API

ExportKindDescription
MakorCpgAPIclassThe in-process mock: fetch(request), reset(), tick(), state. Options: sqlite, now, namespace, settings.
createRuntimefunctionThe mock with the full service contract plus CORS (health, admin, namespaces, credentials, presets). Options: settings, clock, seed, adminKey, onLog, sqlite.
MAKOR_CPG_PRESETSobjectEvery named fault preset.
MAKOR_CPG_NAMESPACEstringThe service name, "makor-cpg".
NO_CARE_PLAN_MESSAGEstringThe exact 404 message the backend treats as an expected absence.
CORS_HEADERS, withCorsvaluesThe permissive CORS headers, and the helper that stamps them on a response.
apiKeyCredentialfunctionThe x-api-key a request carries (how keys map to namespaces).
defaultScriptContentfunctionThe placeholder review script for a lab test.
DEFAULT_SETTINGSobject{apiKeys: [], processingMs: 0}.
document, operationIds, supportedOperationIdsvaluesThe vendored OpenAPI contract and its operation ids.
createServer, serveTarget, DEFAULT_PORT (./server)NodeServe over node:http; the serve CLI target (--api-key, --processing-ms); port 8806.

Part of mockingbird.

版本与发布记录

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

版本发布日期距今
0.2.0latest2026/09/241 个月前
0.1.02026/09/231 个月前

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

@crvouga/mockingbird-service-makor-cpg 是什么?

Stateful mock of the legacy Makor AI (CPG) API: care plans, plus-user, bloodwork webhook, subscriptions, Wholescripts orders, AI patient summaries and async-review scripts (processing → complete on the mock clock), with permissive CORS for browser-direct

如何安装 @crvouga/mockingbird-service-makor-cpg?

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

@crvouga/mockingbird-service-makor-cpg 支持 TypeScript 吗?

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

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

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

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

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

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

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

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

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