@crvouga/mockingbird-service-junction 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,6 +1,12 @@
1
1
  # Changelog — @crvouga/mockingbird-service-junction
2
2
 
3
- ## 0.7.0 (2026-09-23)
3
+ ## 0.8.0 (2026-09-24)
4
+
5
+ ### Features
6
+
7
+ - replace self-hosted Vault and Turborepo cache with GitHub ([963ecd8](https://github.com/crvouga/mockingbird/commit/963ecd862e50f2782acf16aaef0d25c518f87afc))
8
+
9
+ ## 0.7.0 (2026-09-22)
4
10
 
5
11
  ### Features
6
12
 
package/README.md CHANGED
@@ -817,7 +817,7 @@ bun test junction.sdk.property.test.ts # drives the served mock through @try
817
817
 
818
818
  bun run mock:serve # env-configured `serve`: HOST, PORT, MOCKINGBIRD_JUNCTION_CORPUS,
819
819
  # MOCKINGBIRD_JUNCTION_WEBHOOK_URL / _SECRET / _SCOPE
820
- bun run corpus:record -- --force # re-record the shipped corpus/sandbox-sealed.json (Vault key)
820
+ bun run corpus:record -- --force # re-record the shipped corpus/sandbox-sealed.json (MOCKINGBIRD_JUNCTION_API_KEY)
821
821
  bun run verify -- --real-key "$KEY" # `mockingbird-junction verify` from source
822
822
  ```
823
823
 
@@ -1296,7 +1296,7 @@ var restoreNamespace = (sqlite, namespace, snapshot) => {
1296
1296
  };
1297
1297
 
1298
1298
  // ../core/dist/version.js
1299
- var PACKAGE_VERSION = true ? "0.7.0" : UNRELEASED_VERSION;
1299
+ var PACKAGE_VERSION = true ? "0.8.0" : UNRELEASED_VERSION;
1300
1300
 
1301
1301
  // ../core/dist/webhooks.js
1302
1302
  var json2 = (status, body) => new Response(JSON.stringify(body), { status, headers: { "content-type": "application/json" } });
package/dist/cli.js CHANGED
@@ -23,7 +23,7 @@ import {
23
23
  // src/cli.ts
24
24
  import { readFile, writeFile } from "node:fs/promises";
25
25
  var asString = (value) => typeof value === "string" && value !== "" ? value : void 0;
26
- var realKeyOf = (values) => asString(values["real-key"]) ?? process.env.MOCKINGBIRD_JUNCTION_API_KEY ?? process.env.JUNCTION_API_KEY;
26
+ var realKeyOf = (values) => asString(values["real-key"]) ?? asString(process.env.MOCKINGBIRD_JUNCTION_API_KEY) ?? asString(process.env.JUNCTION_API_KEY);
27
27
  var readCorpus = async (source) => source === "default" ? defaultCorpus : parseSealedCorpus(JSON.parse(await readFile(source, "utf8")));
28
28
  var guardKey = (key, values) => {
29
29
  if (key === void 0) {
package/dist/cli.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/cli.ts"],
4
- "sourcesContent": ["#!/usr/bin/env node\n/// <reference types=\"node\" />\nimport { readFile, writeFile } from \"node:fs/promises\"\nimport { type CliValues, runCli, serveCommand } from \"@crvouga/mockingbird-adapter-node\"\nimport { defaultCorpus } from \"./corpus.js\"\nimport { DEFAULT_JUNCTION_BASE_URL, diffCorpus, isSandboxKey, pullCorpus } from \"./corpus-tools.js\"\nimport { createRuntime } from \"./runtime.js\"\nimport { parseSealedCorpus, type SealedCorpus } from \"./sealed-corpus.js\"\nimport { loadCorpus, serveTarget } from \"./server.js\"\nimport { verifyAgainstReal } from \"./verify.js\"\n\nconst asString = (value: string | boolean | undefined): string | undefined =>\n typeof value === \"string\" && value !== \"\" ? value : undefined\n\nconst realKeyOf = (values: CliValues): string | undefined =>\n asString(values[\"real-key\"]) ??\n process.env.MOCKINGBIRD_JUNCTION_API_KEY ??\n process.env.JUNCTION_API_KEY\n\nconst readCorpus = async (source: string): Promise<SealedCorpus> =>\n source === \"default\"\n ? defaultCorpus\n : parseSealedCorpus(JSON.parse(await readFile(source, \"utf8\")))\n\nconst guardKey = (key: string | undefined, values: CliValues): string | undefined => {\n if (key === undefined) {\n console.error(\"missing --real-key (or env MOCKINGBIRD_JUNCTION_API_KEY)\")\n return undefined\n }\n if (!isSandboxKey(key) && values[\"allow-any-key\"] !== true) {\n console.error(\n \"refusing a key that is not a sandbox team key (sk_us_\u2026 / sk_eu_\u2026); pass --allow-any-key to override\",\n )\n return undefined\n }\n return key\n}\n\nconst REAL_OPTIONS = {\n \"real-key\": {\n type: \"string\",\n value: \"<key>\",\n description: \"Junction API key (env MOCKINGBIRD_JUNCTION_API_KEY)\",\n },\n \"real-url\": {\n type: \"string\",\n value: \"<url>\",\n description: \"Junction API base URL\",\n default: DEFAULT_JUNCTION_BASE_URL,\n },\n \"allow-any-key\": {\n type: \"boolean\",\n description: \"Allow a key that is not a sandbox team key\",\n },\n} as const\n\nconst code = await runCli(\n {\n bin: \"mockingbird-junction\",\n description: \"stateful Junction (Vital) API mock\",\n commands: {\n serve: serveCommand(serveTarget),\n\n \"corpus pull\": {\n summary: \"Record a corpus from a real Junction team (read-only GETs)\",\n usage: \"mockingbird-junction corpus pull --out <file> [--zip 10001,94105] [--base <file>]\",\n options: {\n ...REAL_OPTIONS,\n out: { type: \"string\", value: \"<file>\", description: \"Where to write the corpus\" },\n zip: {\n type: \"string\",\n value: \"<zip,zip,\u2026>\",\n description: \"ZIPs to record coverage for (default: the shipped set)\",\n },\n base: {\n type: \"string\",\n value: \"<file|default>\",\n description: \"Only refresh --zip on top of this corpus\",\n },\n \"coverage-only\": {\n type: \"boolean\",\n description: \"Skip the team's catalog and lab accounts\",\n },\n },\n async run(values) {\n const key = guardKey(realKeyOf(values), values)\n if (key === undefined) return 2\n const out = asString(values.out)\n if (out === undefined) {\n console.error(\"missing --out <file>\")\n return 2\n }\n const zips = asString(values.zip)\n ?.split(\",\")\n .map((zip) => zip.trim())\n .filter(Boolean)\n if (zips?.some((zip) => !/^\\d{5}$/.test(zip))) {\n console.error(\"--zip takes comma-separated 5-digit ZIPs\")\n return 2\n }\n const baseSource = asString(values.base)\n const corpus = await pullCorpus({\n apiKey: key,\n baseUrl: asString(values[\"real-url\"]) ?? DEFAULT_JUNCTION_BASE_URL,\n ...(zips ? { zips } : {}),\n ...(baseSource ? { base: await readCorpus(baseSource) } : {}),\n coverageOnly: values[\"coverage-only\"] === true,\n onProgress: (message) => console.log(`corpus pull: ${message}`),\n })\n await writeFile(out, `${JSON.stringify(corpus, null, 2)}\\n`)\n console.log(\n `corpus pull: wrote ${out} \u2014 ${Object.keys(corpus.observations).length} observations, ${corpus.catalog.labTests.length} lab tests, ${corpus.labAccounts.length} lab accounts, fingerprint ${corpus.fingerprint?.slice(0, 12)}`,\n )\n return 0\n },\n },\n\n \"corpus diff\": {\n summary: \"Show what changed between two corpora (exit 1 when they differ)\",\n usage: \"mockingbird-junction corpus diff <before|default> <after|default> [--json]\",\n options: { json: { type: \"boolean\", description: \"Print the diff as JSON\" } },\n async run(values, positionals) {\n const [beforePath, afterPath] = positionals\n if (beforePath === undefined || afterPath === undefined) {\n console.error(\"usage: mockingbird-junction corpus diff <before> <after>\")\n return 2\n }\n const diff = diffCorpus(await readCorpus(beforePath), await readCorpus(afterPath))\n if (values.json === true) {\n console.log(JSON.stringify(diff, null, 2))\n return diff.identical ? 0 : 1\n }\n if (diff.identical) {\n console.log(\"corpora are identical\")\n return 0\n }\n const section = (\n title: string,\n d: { added: string[]; removed: string[]; changed?: string[] },\n ) => {\n const total = d.added.length + d.removed.length + (d.changed?.length ?? 0)\n if (total === 0) return\n console.log(`${title}:`)\n for (const id of d.added) console.log(` + ${id}`)\n for (const id of d.removed) console.log(` - ${id}`)\n for (const id of d.changed ?? []) console.log(` ~ ${id}`)\n }\n section(\"ZIPs\", diff.zips)\n section(\"lab tests\", diff.labTests)\n section(\"lab accounts\", diff.labAccounts)\n const o = diff.observations\n console.log(\n `observations: +${o.added.length} -${o.removed.length} ~${o.changed.length} (--json lists them)`,\n )\n return 1\n },\n },\n\n verify: {\n summary: \"Replay the corpus and a stateful scenario against real Junction and the mock\",\n usage: \"mockingbird-junction verify --real-key <sk_us_\u2026> [--corpus <file>] [--orders]\",\n options: {\n ...REAL_OPTIONS,\n corpus: {\n type: \"string\",\n value: \"<default|file>\",\n description: \"Corpus the mock serves and drift is checked against\",\n default: \"default\",\n },\n sample: {\n type: \"string\",\n value: \"<n>\",\n description: \"Re-fetch at most n recorded observations (default: all)\",\n },\n \"skip-drift\": { type: \"boolean\", description: \"Only run the stateful scenario\" },\n orders: {\n type: \"boolean\",\n description: \"Also place and cancel an order on the real team\",\n },\n json: { type: \"boolean\", description: \"Print the report as JSON\" },\n },\n async run(values) {\n const key = guardKey(realKeyOf(values), values)\n if (key === undefined) return 2\n const corpus = await loadCorpus(asString(values.corpus) ?? \"default\")\n if (!corpus) {\n console.error(\"verify needs a corpus (--corpus default or a file)\")\n return 2\n }\n const sample = asString(values.sample)\n const report = await verifyAgainstReal({\n realKey: key,\n realUrl: asString(values[\"real-url\"]) ?? DEFAULT_JUNCTION_BASE_URL,\n mock: createRuntime({ corpus }),\n corpus,\n ...(sample !== undefined ? { sample: Number.parseInt(sample, 10) } : {}),\n skipDrift: values[\"skip-drift\"] === true,\n orders: values.orders === true,\n ...(values.json === true\n ? {}\n : { onProgress: (message: string) => console.log(`verify: ${message}`) }),\n })\n if (values.json === true) {\n console.log(JSON.stringify(report, null, 2))\n } else {\n for (const d of report.divergences) {\n console.log(`DIVERGED ${d.check} (${d.kind}${d.at ? ` at ${d.at}` : \"\"})`)\n console.log(` real: ${JSON.stringify(d.real)?.slice(0, 400)}`)\n console.log(` mock: ${JSON.stringify(d.mock)?.slice(0, 400)}`)\n }\n console.log(\n `verify: drift ${report.drift.stale}/${report.drift.checked} stale; scenario ${report.scenario.divergent}/${report.scenario.steps} divergent \u2014 ${report.ok ? \"OK\" : \"DIVERGED\"}`,\n )\n }\n return report.ok ? 0 : 1\n },\n },\n },\n },\n process.argv.slice(2),\n)\nif (code !== 0) process.exitCode = code\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["#!/usr/bin/env node\n/// <reference types=\"node\" />\nimport { readFile, writeFile } from \"node:fs/promises\"\nimport { type CliValues, runCli, serveCommand } from \"@crvouga/mockingbird-adapter-node\"\nimport { defaultCorpus } from \"./corpus.js\"\nimport { DEFAULT_JUNCTION_BASE_URL, diffCorpus, isSandboxKey, pullCorpus } from \"./corpus-tools.js\"\nimport { createRuntime } from \"./runtime.js\"\nimport { parseSealedCorpus, type SealedCorpus } from \"./sealed-corpus.js\"\nimport { loadCorpus, serveTarget } from \"./server.js\"\nimport { verifyAgainstReal } from \"./verify.js\"\n\nconst asString = (value: string | boolean | undefined): string | undefined =>\n typeof value === \"string\" && value !== \"\" ? value : undefined\n\nconst realKeyOf = (values: CliValues): string | undefined =>\n asString(values[\"real-key\"]) ??\n asString(process.env.MOCKINGBIRD_JUNCTION_API_KEY) ??\n asString(process.env.JUNCTION_API_KEY)\n\nconst readCorpus = async (source: string): Promise<SealedCorpus> =>\n source === \"default\"\n ? defaultCorpus\n : parseSealedCorpus(JSON.parse(await readFile(source, \"utf8\")))\n\nconst guardKey = (key: string | undefined, values: CliValues): string | undefined => {\n if (key === undefined) {\n console.error(\"missing --real-key (or env MOCKINGBIRD_JUNCTION_API_KEY)\")\n return undefined\n }\n if (!isSandboxKey(key) && values[\"allow-any-key\"] !== true) {\n console.error(\n \"refusing a key that is not a sandbox team key (sk_us_\u2026 / sk_eu_\u2026); pass --allow-any-key to override\",\n )\n return undefined\n }\n return key\n}\n\nconst REAL_OPTIONS = {\n \"real-key\": {\n type: \"string\",\n value: \"<key>\",\n description: \"Junction API key (env MOCKINGBIRD_JUNCTION_API_KEY)\",\n },\n \"real-url\": {\n type: \"string\",\n value: \"<url>\",\n description: \"Junction API base URL\",\n default: DEFAULT_JUNCTION_BASE_URL,\n },\n \"allow-any-key\": {\n type: \"boolean\",\n description: \"Allow a key that is not a sandbox team key\",\n },\n} as const\n\nconst code = await runCli(\n {\n bin: \"mockingbird-junction\",\n description: \"stateful Junction (Vital) API mock\",\n commands: {\n serve: serveCommand(serveTarget),\n\n \"corpus pull\": {\n summary: \"Record a corpus from a real Junction team (read-only GETs)\",\n usage: \"mockingbird-junction corpus pull --out <file> [--zip 10001,94105] [--base <file>]\",\n options: {\n ...REAL_OPTIONS,\n out: { type: \"string\", value: \"<file>\", description: \"Where to write the corpus\" },\n zip: {\n type: \"string\",\n value: \"<zip,zip,\u2026>\",\n description: \"ZIPs to record coverage for (default: the shipped set)\",\n },\n base: {\n type: \"string\",\n value: \"<file|default>\",\n description: \"Only refresh --zip on top of this corpus\",\n },\n \"coverage-only\": {\n type: \"boolean\",\n description: \"Skip the team's catalog and lab accounts\",\n },\n },\n async run(values) {\n const key = guardKey(realKeyOf(values), values)\n if (key === undefined) return 2\n const out = asString(values.out)\n if (out === undefined) {\n console.error(\"missing --out <file>\")\n return 2\n }\n const zips = asString(values.zip)\n ?.split(\",\")\n .map((zip) => zip.trim())\n .filter(Boolean)\n if (zips?.some((zip) => !/^\\d{5}$/.test(zip))) {\n console.error(\"--zip takes comma-separated 5-digit ZIPs\")\n return 2\n }\n const baseSource = asString(values.base)\n const corpus = await pullCorpus({\n apiKey: key,\n baseUrl: asString(values[\"real-url\"]) ?? DEFAULT_JUNCTION_BASE_URL,\n ...(zips ? { zips } : {}),\n ...(baseSource ? { base: await readCorpus(baseSource) } : {}),\n coverageOnly: values[\"coverage-only\"] === true,\n onProgress: (message) => console.log(`corpus pull: ${message}`),\n })\n await writeFile(out, `${JSON.stringify(corpus, null, 2)}\\n`)\n console.log(\n `corpus pull: wrote ${out} \u2014 ${Object.keys(corpus.observations).length} observations, ${corpus.catalog.labTests.length} lab tests, ${corpus.labAccounts.length} lab accounts, fingerprint ${corpus.fingerprint?.slice(0, 12)}`,\n )\n return 0\n },\n },\n\n \"corpus diff\": {\n summary: \"Show what changed between two corpora (exit 1 when they differ)\",\n usage: \"mockingbird-junction corpus diff <before|default> <after|default> [--json]\",\n options: { json: { type: \"boolean\", description: \"Print the diff as JSON\" } },\n async run(values, positionals) {\n const [beforePath, afterPath] = positionals\n if (beforePath === undefined || afterPath === undefined) {\n console.error(\"usage: mockingbird-junction corpus diff <before> <after>\")\n return 2\n }\n const diff = diffCorpus(await readCorpus(beforePath), await readCorpus(afterPath))\n if (values.json === true) {\n console.log(JSON.stringify(diff, null, 2))\n return diff.identical ? 0 : 1\n }\n if (diff.identical) {\n console.log(\"corpora are identical\")\n return 0\n }\n const section = (\n title: string,\n d: { added: string[]; removed: string[]; changed?: string[] },\n ) => {\n const total = d.added.length + d.removed.length + (d.changed?.length ?? 0)\n if (total === 0) return\n console.log(`${title}:`)\n for (const id of d.added) console.log(` + ${id}`)\n for (const id of d.removed) console.log(` - ${id}`)\n for (const id of d.changed ?? []) console.log(` ~ ${id}`)\n }\n section(\"ZIPs\", diff.zips)\n section(\"lab tests\", diff.labTests)\n section(\"lab accounts\", diff.labAccounts)\n const o = diff.observations\n console.log(\n `observations: +${o.added.length} -${o.removed.length} ~${o.changed.length} (--json lists them)`,\n )\n return 1\n },\n },\n\n verify: {\n summary: \"Replay the corpus and a stateful scenario against real Junction and the mock\",\n usage: \"mockingbird-junction verify --real-key <sk_us_\u2026> [--corpus <file>] [--orders]\",\n options: {\n ...REAL_OPTIONS,\n corpus: {\n type: \"string\",\n value: \"<default|file>\",\n description: \"Corpus the mock serves and drift is checked against\",\n default: \"default\",\n },\n sample: {\n type: \"string\",\n value: \"<n>\",\n description: \"Re-fetch at most n recorded observations (default: all)\",\n },\n \"skip-drift\": { type: \"boolean\", description: \"Only run the stateful scenario\" },\n orders: {\n type: \"boolean\",\n description: \"Also place and cancel an order on the real team\",\n },\n json: { type: \"boolean\", description: \"Print the report as JSON\" },\n },\n async run(values) {\n const key = guardKey(realKeyOf(values), values)\n if (key === undefined) return 2\n const corpus = await loadCorpus(asString(values.corpus) ?? \"default\")\n if (!corpus) {\n console.error(\"verify needs a corpus (--corpus default or a file)\")\n return 2\n }\n const sample = asString(values.sample)\n const report = await verifyAgainstReal({\n realKey: key,\n realUrl: asString(values[\"real-url\"]) ?? DEFAULT_JUNCTION_BASE_URL,\n mock: createRuntime({ corpus }),\n corpus,\n ...(sample !== undefined ? { sample: Number.parseInt(sample, 10) } : {}),\n skipDrift: values[\"skip-drift\"] === true,\n orders: values.orders === true,\n ...(values.json === true\n ? {}\n : { onProgress: (message: string) => console.log(`verify: ${message}`) }),\n })\n if (values.json === true) {\n console.log(JSON.stringify(report, null, 2))\n } else {\n for (const d of report.divergences) {\n console.log(`DIVERGED ${d.check} (${d.kind}${d.at ? ` at ${d.at}` : \"\"})`)\n console.log(` real: ${JSON.stringify(d.real)?.slice(0, 400)}`)\n console.log(` mock: ${JSON.stringify(d.mock)?.slice(0, 400)}`)\n }\n console.log(\n `verify: drift ${report.drift.stale}/${report.drift.checked} stale; scenario ${report.scenario.divergent}/${report.scenario.steps} divergent \u2014 ${report.ok ? \"OK\" : \"DIVERGED\"}`,\n )\n }\n return report.ok ? 0 : 1\n },\n },\n },\n },\n process.argv.slice(2),\n)\nif (code !== 0) process.exitCode = code\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crvouga/mockingbird-service-junction",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "description": "Stateful mock of the Junction (Vital) user API driven by its OpenAPI contract and verified by differential property tests against the Junction sandbox.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -86,7 +86,7 @@
86
86
  "@crvouga/mockingbird-openapi-codegen": "0.1.0",
87
87
  "@crvouga/mockingbird-adapter-node": "0.0.0-development",
88
88
  "@crvouga/mockingbird-openapi-metadata": "0.0.0-development",
89
- "@crvouga/mockingbird-openbao": "0.0.0-development",
89
+ "@crvouga/mockingbird-credentials": "0.0.0-development",
90
90
  "@crvouga/mockingbird-testing": "0.1.0",
91
91
  "@crvouga/mockingbird-service-sqlite": "0.0.0-development",
92
92
  "@tryvital/vital-node": "3.1.511",