@crvouga/mockingbird-service-prism

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

Stateful emulator of the Prism Labs body-scan API: subjects, scans, presigned capture upload, processing stages to READY, and deterministic body-composition, measurement, health-report and asset results.

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

@crvouga/mockingbird-service-prism

Local emulators. Real API contracts. Part of Mockingbird.

Stateful emulator of the Prism Labs body-scan API for test suites: subject upsert, scan creation, the presigned capture upload the capture page PUTs its video to, per-stage processing states, and the READY-scan results our backend persists (body composition, measurements, health report with metabolic age, asset URLs). Scans move through the real lifecycle (CREATED → PROCESSING → READY / FAILED) on command or on the emulator clock, and results are computed deterministically from the subject's height, weight, sex and age.

  • Operation coverage: SUPPORT.md
  • The contract (openapi.yaml) is hand-authored from the consumer's zod schemas (prism-scan.adapter.ts) and the capture page's upload (body-scan-capture-page).

Install

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

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

Usage

Point PRISM_API_URL at the emulator (http is allowed) and set PRISM_API_KEY to anything (or pin it with --api-key). Without both, our adapter reports unavailable and never calls out.

npx mockingbird-prism serve --port 8825 --auto-advance 2000
import { createServer } from "@crvouga/mockingbird-service-prism/server"

// Test setup: a local Prism.
const server = await createServer()
const baseUrl = server.url // the app's PRISM_API_URL

// Application code: the requests it sends to Prism.
const prism = async (path: string, body?: unknown) => {
  const response = await fetch(`${baseUrl}${path}`, {
    method: body === undefined ? "GET" : "POST",
    headers: { authorization: "Bearer k", accept: "application/json;v=1", "content-type": "application/json" },
    ...(body === undefined ? {} : { body: JSON.stringify(body) }),
  })
  return (await response.json()) as Record<string, any>
}
await prism("/users", {
  token: "member-1", sex: "female", region: "north_america", birthDate: "1990-04-02",
  weight: { value: 64, unit: "kg" }, height: { value: 1.7, unit: "m" },
  researchConsent: false, termsOfService: { accepted: true, version: "1" },
})
const scan = await prism("/scans", { userToken: "member-1", deviceConfigName: "IPHONE_SCANNER", bodyfatMethod: "coco2" })
const upload = await prism(`/scans/${scan.id}/upload-url`, {})
await fetch(upload.url, { method: "PUT", body: "capture video bytes" }) // the capture page's upload

// Test control: finish processing without waiting.
await fetch(`${baseUrl}/__admin/scans/${scan.id}/advance`, {
  method: "POST",
  headers: { "content-type": "application/json" },
  body: JSON.stringify({ to: "READY" }),
})

// Application code: the READY scan's results.
const bodyfat = await prism(`/scans/${scan.id}/bodyfat`)
// → {bodyfatMethod: "coco2", bodyfatPercentage: 29.6, leanMass: 45.07, fatMass: 18.93, …}

await server.close()

Routes

All with Authorization: Bearer <key> and Accept: application/json;v=1, except the presigned upload and asset URLs.

RouteBehaviour
POST /usersUpsert by token (sex, region, birthDate, weight {value, unit kg|lb}, height {value, unit m|in}, researchConsent, termsOfService) → 201 on create, 200 on update, same id.
POST /scans{userToken, deviceConfigName: IPHONE_SCANNER|ANDROID_SCANNER, bodyfatMethod, assetConfigId?} → 201 scan {id, status: "CREATED", weight, height, …}; unknown user 404.
GET /scans/{id}?unit-system=metric|imperialStatus and the subject's weight/height in that unit system.
POST /scans/{id}/upload-url{url, expirationTime}: a presigned PUT to the emulator itself (15 minutes on the emulator clock; /__admin/ns/<name> kept in the URL). 409 once the capture is uploaded.
PUT /uploads/{id}?expires&signatureThe capture upload. Moves the scan to PROCESSING (captureData succeeded, body started); an empty body fails it. Expired or tampered URLs are S3-style 403 XML.
GET /scans/{id}/scan-assets{captureData, body, fittedBody, measurement} each started / succeeded / failed / null, with …UpdatedAt.
GET /scans/{id}/bodyfatREADY only: {bodyfatMethod, bodyfatPercentage, leanMass, fatMass, skeletalMuscleMass}.
GET /scans/{id}/measurements?unit-system=READY only: {waistFit, hipsFit, chestFit, waistToHipRatio, bodyRoundnessIndex, bmiPredicted} (cm or in).
GET /scans/{id}/health-reportREADY only: {metabolicAgeReport: {metabolicAgeYears, chronologicalAgeYears, ageDeltaYears, percentile}, bodyShapeReport}.
GET /scans/{id}/asset-urlsREADY only: signed URLs for previewImage, model, canonicalBody, texture, material, stripes, served by GET /assets/{id}/{file}.

Results before READY are 404 (our adapter: not_found). Errors are {message, errors?}.

Admin (beyond the standard contract)

RouteEffect
POST /__admin/scans/:id/advanceOne processing stage on ({"to": "READY"} for all of them). The scan must be uploaded.
POST /__admin/scans/:id/failFail the started stage; the scan becomes FAILED.
GET /__admin/scansThe namespace's scans.
GET/PUT /__admin/settings{apiKeys?, uploadUrlTtlMs?, autoAdvance?: {afterMs, failAt?} | null}; auto-advance walks uploaded scans one stage per afterMs of emulator time.
POST /__admin/tickApply due auto-advance steps now (the served emulator ticks every 100 ms).

Fault presets (POST /__admin/faults {"preset": "<name>", "count"?: n}): unauthorized, server_error, scan_not_found, schema_drift (an unknown scan status), stage_states_slow (6 s, past our 5 s timeout), metabolic_age_missing, metabolic_age_implausible, upload_forbidden, connection_drop.

Namespaces

x-mockingbird-namespace, a /__admin/ns/<name> prefix on PRISM_API_URL, or by API key: PUT /__admin/credentials {"credentials": {"<PRISM_API_KEY>": "<namespace>"}}. Presigned upload and asset URLs carry the namespace in their path, since the capture page's PUT has no other carrier.

Deliberately not modelled

  • Webhooks: our backend polls Prism, it has no receiver.
  • Real body reconstruction: results are formulas over the subject's inputs, and the 3D assets are placeholder bytes.
  • Scan deletion, user listing, and every endpoint our adapter does not call.

API

ExportKindDescription
PrismAPIclassThe in-process emulator: fetch(request), reset(), advance(scanId, fail?), tick(), scans(). Options: sqlite, now, namespace, publicNamespace, settings.
createRuntimefunctionThe emulator with the full service contract (health, admin, namespaces, credentials, presets). Options: settings, tickMs, clock, seed, adminKey, onLog, sqlite.
PRISM_PRESETSobjectEvery named fault preset.
PRISM_NAMESPACEstringThe service name, "prism".
STAGESarrayThe processing stages, in order.
scanResultsfunctionThe deterministic results a READY scan answers.
prismErrorfunctionBuild a Prism error response {message, errors?}.
document, operationIds, supportedOperationIdsvaluesThe OpenAPI contract and its operation ids.
createServer, serveTarget, DEFAULT_PORT (./server)NodeServe over node:http (auto-advance ticks every 100 ms); the serve CLI target (--api-key, --auto-advance); port 8825.

Part of mockingbird.

版本与发布记录

@crvouga/mockingbird-service-prism 共发布 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-prism 的常见问题

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

Stateful emulator of the Prism Labs body-scan API: subjects, scans, presigned capture upload, processing stages to READY, and deterministic body-composition, measurement, health-report and asset results.

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

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

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

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

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

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

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

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

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

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

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

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