@crvouga/mockingbird-service-junction 1.0.0 → 1.0.2

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.
@@ -1172,6 +1172,16 @@ var operationMetadata = (operation) => {
1172
1172
 
1173
1173
  // ../core/dist/service.js
1174
1174
  import { Hono } from "hono";
1175
+
1176
+ // ../core/dist/validation.js
1177
+ var recorded = /* @__PURE__ */ new WeakMap();
1178
+ var recordedIssues = (request2) => recorded.get(request2);
1179
+
1180
+ // ../core/dist/service.js
1181
+ var withIssues = (request2, response) => {
1182
+ const issues = response.status >= 400 ? recordedIssues(request2) : void 0;
1183
+ return issues && issues.length > 0 ? annotateResponse(response, { issues }) : response;
1184
+ };
1175
1185
  var defineOperations = (handlers) => handlers;
1176
1186
  var OperationRegistryError = class extends Error {
1177
1187
  problems;
@@ -1257,9 +1267,7 @@ var createService = (options) => {
1257
1267
  now
1258
1268
  };
1259
1269
  const short = await options.before?.(context);
1260
- if (short)
1261
- return short;
1262
- return handler(context);
1270
+ return withIssues(request2, short ?? await handler(context));
1263
1271
  };
1264
1272
  app.on(operation.method.toUpperCase(), honoPath(operation.path), route);
1265
1273
  }
@@ -1296,7 +1304,7 @@ var restoreNamespace = (sqlite, namespace, snapshot) => {
1296
1304
  };
1297
1305
 
1298
1306
  // ../core/dist/version.js
1299
- var PACKAGE_VERSION = true ? "1.0.0" : UNRELEASED_VERSION;
1307
+ var PACKAGE_VERSION = true ? "1.0.2" : UNRELEASED_VERSION;
1300
1308
 
1301
1309
  // ../core/dist/webhooks.js
1302
1310
  var json2 = (status, body) => new Response(JSON.stringify(body), { status, headers: { "content-type": "application/json" } });
@@ -1734,8 +1742,15 @@ var createRuntime = (options) => {
1734
1742
  const started = monotonicNow();
1735
1743
  const url = new URL(request2.url);
1736
1744
  const operationId = operationIdFor(request2, url.pathname);
1745
+ const contentLength = request2.headers.get("content-length");
1746
+ const received = {
1747
+ contentType: request2.headers.get("content-type"),
1748
+ bodyBytes: contentLength !== null && /^\d+$/.test(contentLength) ? Number(contentLength) : null,
1749
+ transferEncoding: request2.headers.get("transfer-encoding")
1750
+ };
1737
1751
  const log = (status, faultId, response2) => {
1738
1752
  const noted = response2 ? responseNotes(response2) : void 0;
1753
+ const rejected = status >= 400 && faultId === void 0;
1739
1754
  const entry = {
1740
1755
  service: options.name,
1741
1756
  namespace,
@@ -1747,7 +1762,9 @@ var createRuntime = (options) => {
1747
1762
  unmatched: options.document !== void 0 && operationId === void 0,
1748
1763
  ...faultId !== void 0 ? { faultId } : {},
1749
1764
  ...noted?.ids && Object.keys(noted.ids).length > 0 ? { ids: noted.ids } : {},
1750
- ...noted?.adopted ? { adopted: true } : {}
1765
+ ...noted?.adopted ? { adopted: true } : {},
1766
+ ...rejected ? { request: { ...received } } : {},
1767
+ ...rejected && noted?.issues && noted.issues.length > 0 ? { issues: noted.issues.map((issue) => ({ ...issue })) } : {}
1751
1768
  };
1752
1769
  metrics.record(entry);
1753
1770
  journal.record({ ...entry, at: new Date(clock.now()).toISOString() });
@@ -2808,6 +2825,11 @@ var JunctionState = class {
2808
2825
  bookingKeys;
2809
2826
  pendingSimulateTransitions;
2810
2827
  orderByTransaction;
2828
+ /**
2829
+ * The lab account each order was placed with (`null`: Junction's platform account). Kept
2830
+ * off the order record: Junction's `ClientFacingOrder` has no `lab_account_id`.
2831
+ */
2832
+ orderLabAccounts;
2811
2833
  webhookEvents;
2812
2834
  webhookDeliveryAttempts;
2813
2835
  labTests;
@@ -2822,6 +2844,8 @@ var JunctionState = class {
2822
2844
  teamId = MOCK_TEAM_ID;
2823
2845
  /** Sandbox-only restrictions to enforce; every one is off by default. */
2824
2846
  limits = { ...DEFAULT_LIMITS };
2847
+ /** Lab slug → the `billing_type` an order for that lab gets when it omits one. */
2848
+ defaultBillingTypes = {};
2825
2849
  /** Whether an unknown `user_id` is refused (`strict`) or created on first use. */
2826
2850
  identity = "strict";
2827
2851
  webhooksMuted = 0;
@@ -2857,6 +2881,7 @@ var JunctionState = class {
2857
2881
  "pending_simulate_transitions"
2858
2882
  );
2859
2883
  this.orderByTransaction = new Collection(sqlite, namespace, "orders_by_transaction");
2884
+ this.orderLabAccounts = new Collection(sqlite, namespace, "order_lab_accounts");
2860
2885
  this.webhookEvents = new Collection(sqlite, namespace, "webhook_events");
2861
2886
  this.webhookDeliveryAttempts = new Collection(sqlite, namespace, "webhook_delivery_attempts");
2862
2887
  this.labTests = new Collection(sqlite, namespace, "lab_tests");
@@ -3256,6 +3281,20 @@ var BILLING_TYPES = [
3256
3281
  "patient_bill",
3257
3282
  "upfront_payment"
3258
3283
  ];
3284
+ var defaultBillingTypesFrom = (input) => {
3285
+ if (typeof input !== "object" || input === null || Array.isArray(input))
3286
+ throw new TypeError("default billing types must map lab slugs to billing types");
3287
+ const out = {};
3288
+ for (const [lab, type] of Object.entries(input)) {
3289
+ if (lab.trim() === "") throw new TypeError("default billing types: lab slug is empty");
3290
+ if (!BILLING_TYPES.includes(type))
3291
+ throw new TypeError(
3292
+ `default billing type for ${lab}: ${String(type)} is not a billing type (${BILLING_TYPES.join(", ")})`
3293
+ );
3294
+ out[lab.trim().toLowerCase()] = type;
3295
+ }
3296
+ return out;
3297
+ };
3259
3298
  var LAB_ACCOUNT_STATUSES = [
3260
3299
  "active",
3261
3300
  "pending",
@@ -3282,8 +3321,8 @@ var account = (key, lab, fields) => ({
3282
3321
  team_id_allowlist: fields.team_id_allowlist
3283
3322
  });
3284
3323
  var TEAM_LAB_ACCOUNTS = [
3285
- // Branch 2: exactly one active account linked for `quest`; the commercial-insurance
3286
- // state rule lives on this account.
3324
+ // Exactly one active account linked for `quest`; the commercial-insurance state rule
3325
+ // lives on this account.
3287
3326
  account("quest-primary", "quest", {
3288
3327
  status: "active",
3289
3328
  account_name: "Quest Diagnostics \u2014 primary",
@@ -3301,7 +3340,7 @@ var TEAM_LAB_ACCOUNTS = [
3301
3340
  allowed_billing: CLIENT_BILL_ONLY,
3302
3341
  team_id_allowlist: [MOCK_TEAM_ID]
3303
3342
  }),
3304
- // Branch 3: multiple active accounts linked for `nexus`.
3343
+ // Several active accounts linked for `nexus`: an omitted id selects the first.
3305
3344
  account("nexus-a", "nexus", {
3306
3345
  status: "active",
3307
3346
  account_name: "Nexus \u2014 A",
@@ -3314,7 +3353,7 @@ var TEAM_LAB_ACCOUNTS = [
3314
3353
  allowed_billing: CLIENT_BILL_ONLY,
3315
3354
  team_id_allowlist: [MOCK_TEAM_ID]
3316
3355
  }),
3317
- // Branch 4: linked accounts, none active.
3356
+ // Linked accounts, none active.
3318
3357
  account("mtl-suspended", "mtl", {
3319
3358
  status: "suspended",
3320
3359
  account_name: "Molecular Testing Labs \u2014 suspended",
@@ -3351,8 +3390,6 @@ var selectLabAccount = (labSlug, requestedId, teamId, accounts) => {
3351
3390
  if (!linked) throw new HttpError(400, { detail: "Lab account does not exist" });
3352
3391
  if (!isLinkedToTeam(linked, teamId))
3353
3392
  throw new HttpError(400, { detail: "Lab account is not linked to your team" });
3354
- if (linked.lab !== labSlug)
3355
- throw new HttpError(400, { detail: `Lab account is not associated with lab ${labSlug}` });
3356
3393
  if (linked.status !== "active")
3357
3394
  throw new HttpError(400, { detail: "Lab account is not active" });
3358
3395
  return linked;
@@ -3362,12 +3399,8 @@ var selectLabAccount = (labSlug, requestedId, teamId, accounts) => {
3362
3399
  if (PLATFORM_ACCOUNT_LABS.includes(labSlug)) return "platform";
3363
3400
  throw new HttpError(400, { detail: `No active lab account is available for lab ${labSlug}` });
3364
3401
  }
3365
- const active = candidates.filter((entry) => entry.status === "active");
3366
- if (active.length === 1) return active[0];
3367
- if (active.length > 1)
3368
- throw new HttpError(400, {
3369
- detail: "Multiple active lab accounts are linked to your team for this lab; provide lab_account_id"
3370
- });
3402
+ const active = candidates.find((entry) => entry.status === "active");
3403
+ if (active) return active;
3371
3404
  throw new HttpError(400, { detail: "No active lab account is available for this lab" });
3372
3405
  };
3373
3406
  var renderLabAccount = (entry) => ({
@@ -3498,7 +3531,7 @@ var labAccountFromClientFacing = (value, recordedTeamId) => {
3498
3531
  var except = (...excluded) => US_STATES.filter((state) => !excluded.includes(state));
3499
3532
  var PLATFORM_ACCOUNT_STATES = except("NJ", "NY", "RI");
3500
3533
  var DELEGATED_ACCOUNT_STATES = except("NJ", "NY");
3501
- var bioreferenceCustomer = {
3534
+ var bioreferencePlatform = {
3502
3535
  lab: "bioreference",
3503
3536
  account_name: "Junction BioReference Account",
3504
3537
  delegated_flow: "not_delegated",
@@ -3533,7 +3566,7 @@ var LAB_ACCOUNT_PRESETS = {
3533
3566
  delegated_flow: "order_delegated",
3534
3567
  allowed_billing: { client_bill: DELEGATED_ACCOUNT_STATES }
3535
3568
  },
3536
- bioreference_customer_multi_state: bioreferenceCustomer,
3569
+ bioreference_platform: bioreferencePlatform,
3537
3570
  bioreference_patient_bill_passthrough: {
3538
3571
  lab: "bioreference",
3539
3572
  delegated_flow: "not_delegated",
@@ -3541,13 +3574,22 @@ var LAB_ACCOUNT_PRESETS = {
3541
3574
  },
3542
3575
  quest_platform: questPlatform,
3543
3576
  labcorp_platform: labcorpPlatform,
3544
- suspended_bioreference: { ...bioreferenceCustomer, status: "suspended" },
3577
+ suspended_bioreference: { ...bioreferencePlatform, status: "suspended" },
3545
3578
  suspended_quest: { ...questPlatform, status: "suspended" },
3546
3579
  suspended_labcorp: { ...labcorpPlatform, status: "suspended" }
3547
3580
  };
3581
+ var LAB_ACCOUNT_PRESET_ALIASES = {
3582
+ /** Misnamed: the record is Junction's platform account, not a customer-owned one. */
3583
+ bioreference_customer_multi_state: "bioreference_platform"
3584
+ };
3585
+ var SYNTHETIC_LAB_ACCOUNT_PRESETS = ["bioreference_ny_nj_delegated"];
3586
+ var presetNamed = (name) => {
3587
+ const target = Object.hasOwn(LAB_ACCOUNT_PRESET_ALIASES, name) ? LAB_ACCOUNT_PRESET_ALIASES[name] : name;
3588
+ return target !== void 0 && Object.hasOwn(LAB_ACCOUNT_PRESETS, target) ? LAB_ACCOUNT_PRESETS[target] : void 0;
3589
+ };
3548
3590
  var presetAccountId = (name) => deterministicUuid(`junction:lab-account:${name}`);
3549
3591
  var labAccountFromPreset = (name, options) => {
3550
- const preset = Object.hasOwn(LAB_ACCOUNT_PRESETS, name) ? LAB_ACCOUNT_PRESETS[name] : void 0;
3592
+ const preset = presetNamed(name);
3551
3593
  if (!preset) return void 0;
3552
3594
  return labAccountFromInput({ ...preset, id: options.id ?? presetAccountId(name) }, options.teamId);
3553
3595
  };
@@ -3749,7 +3791,8 @@ var junctionAdminRoutes = (options) => (runtime) => {
3749
3791
  user_id: order.user_id,
3750
3792
  method: methodOf(order),
3751
3793
  status: order.last_event.status,
3752
- updated_at: order.updated_at
3794
+ updated_at: order.updated_at,
3795
+ lab_account_id: api(request2).orderLabAccount(order.id) ?? null
3753
3796
  }))
3754
3797
  }),
3755
3798
  "POST /orders/:id/transition": (request2) => {
@@ -3834,7 +3877,9 @@ var junctionAdminRoutes = (options) => (runtime) => {
3834
3877
  const record = labAccountFromPreset(name, { teamId: api(request2).teamId });
3835
3878
  return [name, record ? renderLabAccount(record) : null];
3836
3879
  })
3837
- )
3880
+ ),
3881
+ aliases: LAB_ACCOUNT_PRESET_ALIASES,
3882
+ synthetic: SYNTHETIC_LAB_ACCOUNT_PRESETS
3838
3883
  }),
3839
3884
  "POST /lab-accounts/presets/:name": (request2) => {
3840
3885
  const body = isRecord5(request2.body) ? request2.body : {};
@@ -3857,6 +3902,19 @@ var junctionAdminRoutes = (options) => (runtime) => {
3857
3902
  const input = request2.body;
3858
3903
  return guarded(() => jsonRes(200, { limits: api(request2).configureLimits(input) }));
3859
3904
  },
3905
+ "GET /default-billing-types": (request2) => jsonRes(200, { defaultBillingTypes: api(request2).defaultBillingTypes }),
3906
+ "PUT /default-billing-types": (request2) => {
3907
+ const input = isRecord5(request2.body) ? request2.body.defaultBillingTypes : void 0;
3908
+ if (!isRecord5(input))
3909
+ return adminError3(
3910
+ 400,
3911
+ 'body needs "defaultBillingTypes": { "<lab slug>": "<billing_type>" } ({} restores client_bill)'
3912
+ );
3913
+ return guarded(() => {
3914
+ api(request2).defaultBillingTypes = input;
3915
+ return jsonRes(200, { defaultBillingTypes: api(request2).defaultBillingTypes });
3916
+ });
3917
+ },
3860
3918
  "GET /identity": (request2) => jsonRes(200, { identity: api(request2).identity }),
3861
3919
  "PUT /identity": (request2) => {
3862
3920
  const mode = isRecord5(request2.body) ? request2.body.mode : void 0;
@@ -4014,6 +4072,9 @@ var cachedResponse = (state, context) => {
4014
4072
  materializeAvailabilityBookingKeys(state, context, cached.body);
4015
4073
  rotateAvailabilityBookingKeys(state, cached.body, context.now());
4016
4074
  }
4075
+ return responseFromCache(cached);
4076
+ };
4077
+ var responseFromCache = (cached) => {
4017
4078
  const headers = new Headers(cached.headers);
4018
4079
  headers.delete("content-encoding");
4019
4080
  headers.delete("content-length");
@@ -4417,6 +4478,47 @@ var radiusOf = (context) => {
4417
4478
  }
4418
4479
  return value;
4419
4480
  };
4481
+ var recordedAtRadius = (state, context, radius) => {
4482
+ const params = new URLSearchParams(context.url.searchParams);
4483
+ params.delete("radius");
4484
+ const lookup = (query) => state.getGetCache(state.cacheKeyForRequest("GET", context.url.pathname, query));
4485
+ if (radius === DEFAULT_RADIUS) {
4486
+ const bare = lookup(params);
4487
+ if (bare) return bare;
4488
+ }
4489
+ params.set("radius", String(radius));
4490
+ return lookup(params);
4491
+ };
4492
+ var derivedFromRecording = (state, context, radius) => {
4493
+ const same = recordedAtRadius(state, context, radius);
4494
+ if (same) return responseFromCache(same);
4495
+ if (!context.url.pathname.endsWith("/psc/info")) return void 0;
4496
+ for (const wider of ALLOWED_RADII) {
4497
+ if (wider <= radius) continue;
4498
+ const recorded2 = recordedAtRadius(state, context, wider);
4499
+ if (!recorded2) continue;
4500
+ const body = recorded2.body;
4501
+ if (recorded2.status !== 200 || !body || !Array.isArray(body.patient_service_centers))
4502
+ return void 0;
4503
+ const centers = body.patient_service_centers.filter(
4504
+ (center) => Number(center.distance) < radius
4505
+ );
4506
+ return responseFromCache({ ...recorded2, body: { ...body, patient_service_centers: centers } });
4507
+ }
4508
+ return void 0;
4509
+ };
4510
+ var requireRecordedRead = (state, context, zip) => {
4511
+ if (state.geoMode !== "corpus") return;
4512
+ const query = [...context.url.searchParams.entries()].map(([key, value]) => `${key}=${value}`).join("&");
4513
+ throw new HttpError(UNKNOWN_ZIP_STATUS, {
4514
+ detail: {
4515
+ error_type: UNKNOWN_ZIP_ERROR_TYPE,
4516
+ error_message: `ZIP ${zip} is in the loaded Junction corpus, but GET ${context.url.pathname}?${query} was not recorded and cannot be derived from what was (a smaller psc/info radius is filtered from a wider recording; area/info needs its own). Record it, or serve with --geo synthetic to invent coverage.`,
4517
+ zip_code: zip,
4518
+ corpus: state.corpusLabel ?? null
4519
+ }
4520
+ });
4521
+ };
4420
4522
  var zipCodeOf = (context, required) => {
4421
4523
  const raw = context.query.zip_code;
4422
4524
  if (raw === void 0) {
@@ -4875,7 +4977,11 @@ var schedulingHandlers = (state) => ({
4875
4977
  }
4876
4978
  const zip = rawZip.slice(0, 5);
4877
4979
  requireCoveredZip(state, zip);
4878
- return jsonRes(200, areaInfoFor(zip, radius, labAccountOf(state, context)));
4980
+ const account2 = labAccountOf(state, context);
4981
+ const derived = derivedFromRecording(state, context, radius);
4982
+ if (derived) return derived;
4983
+ requireRecordedRead(state, context, zip);
4984
+ return jsonRes(200, areaInfoFor(zip, radius, account2));
4879
4985
  },
4880
4986
  get_psc_info_v3_order_psc_info_get: async (context) => {
4881
4987
  const hit = cachedResponse(state, context);
@@ -4952,6 +5058,9 @@ var schedulingHandlers = (state) => ({
4952
5058
  throw new HttpError(404, { detail: "Lab not found." });
4953
5059
  }
4954
5060
  requireCoveredZip(state, zip);
5061
+ const derived = derivedFromRecording(state, context, radius);
5062
+ if (derived) return derived;
5063
+ requireRecordedRead(state, context, zip);
4955
5064
  const within = withinRadiusFor(zip, lab.slug, radius);
4956
5065
  const centerCount = Math.min(within, 30);
4957
5066
  const centers = Array.from({ length: centerCount }, (_, index) => {
@@ -5684,6 +5793,13 @@ var orderValidation = (body, now = Date.now) => {
5684
5793
  input: labAccountId
5685
5794
  });
5686
5795
  }
5796
+ const labTestId = body.lab_test_id;
5797
+ if (labTestId !== void 0 && labTestId !== null) {
5798
+ if (typeof labTestId !== "string")
5799
+ errors.push(stringTypeError(["body", "lab_test_id"], labTestId));
5800
+ else if (!isUuid2(labTestId))
5801
+ errors.push(uuidError2(labTestId, void 0, ["body", "lab_test_id"]));
5802
+ }
5687
5803
  const os = body.order_set;
5688
5804
  const osWrongType = os !== void 0 && (typeof os !== "object" || os === null || Array.isArray(os));
5689
5805
  if (osWrongType) {
@@ -6113,7 +6229,8 @@ var orderHandlers = (state) => ({
6113
6229
  const aoeError = aoeValidationError(state, body);
6114
6230
  if (aoeError) throw new HttpError(400, { detail: aoeError });
6115
6231
  const rawOrderSet = body.order_set;
6116
- if (rawOrderSet === void 0 && body.lab_test_id === void 0) {
6232
+ const singleLabTestId = typeof body.lab_test_id === "string" ? body.lab_test_id : void 0;
6233
+ if (rawOrderSet === void 0 && singleLabTestId === void 0) {
6117
6234
  throw new HttpError(400, { detail: "Either lab_test_id or order_set must be set" });
6118
6235
  }
6119
6236
  if (typeof rawOrderSet === "object" && rawOrderSet !== null && !Array.isArray(rawOrderSet) && rawOrderSet.lab_test_ids === void 0 && rawOrderSet.add_on === void 0) {
@@ -6121,7 +6238,8 @@ var orderHandlers = (state) => ({
6121
6238
  }
6122
6239
  const details = body.patient_details;
6123
6240
  const address = body.patient_address;
6124
- const labTestIds = Array.isArray(body.order_set.lab_test_ids) ? body.order_set.lab_test_ids : [];
6241
+ const orderSetIds = typeof rawOrderSet === "object" && rawOrderSet !== null ? rawOrderSet.lab_test_ids : void 0;
6242
+ const labTestIds = Array.isArray(orderSetIds) ? orderSetIds : rawOrderSet === void 0 && singleLabTestId !== void 0 ? [singleLabTestId] : [];
6125
6243
  if (labTestIds.length === 0) {
6126
6244
  throw new HttpError(400, {
6127
6245
  detail: "No markers found for the supplied lab tests. Please check the lab test ids"
@@ -6155,11 +6273,14 @@ var orderHandlers = (state) => ({
6155
6273
  state.teamId,
6156
6274
  state.listLabAccounts()
6157
6275
  );
6158
- const billingType = typeof body.billing_type === "string" && body.billing_type !== "" ? body.billing_type : "client_bill";
6276
+ const requestedBilling = typeof body.billing_type === "string" && body.billing_type !== "" ? body.billing_type : void 0;
6277
+ const billingType = requestedBilling ?? state.defaultBillingTypes[labSlug] ?? "client_bill";
6159
6278
  const allowedStates = effectiveBilling(labAccount)[billingType];
6160
6279
  if (!allowedStates)
6161
6280
  throw new HttpError(400, {
6162
- detail: `Billing type ${billingType} is not supported by the lab account used for this order`
6281
+ // An omitted billing_type that resolves to one the account lacks is reported against
6282
+ // the lab, as the sandbox words it.
6283
+ detail: requestedBilling === void 0 ? `Lab ${String(labTest.lab?.id ?? labSlug)} does not support billing type ${billingType}` : `Billing type ${billingType} is not supported by the lab account used for this order`
6163
6284
  });
6164
6285
  const patientState = String(address.state ?? "").trim().toUpperCase();
6165
6286
  if (patientState !== "" && !allowedStates.includes(patientState))
@@ -6204,12 +6325,14 @@ var orderHandlers = (state) => ({
6204
6325
  billingType,
6205
6326
  icdCodes,
6206
6327
  clinicalNotes: typeof body.clinical_notes === "string" ? body.clinical_notes : null,
6207
- passthrough: typeof body.passthrough === "string" ? body.passthrough : null,
6208
- ...typeof body.lab_account_id === "string" ? { labAccountId: body.lab_account_id } : {}
6328
+ passthrough: typeof body.passthrough === "string" ? body.passthrough : null
6209
6329
  },
6210
6330
  context.now
6211
6331
  );
6212
6332
  persistOrderRecord(state, order, labTest, details, address);
6333
+ state.orderLabAccounts.insert(orderId, {
6334
+ lab_account_id: labAccount === "platform" ? null : labAccount.id
6335
+ });
6213
6336
  const responseOrder = { ...order };
6214
6337
  if (responseOrder.result_types === null) delete responseOrder.result_types;
6215
6338
  const response = {
@@ -6456,7 +6579,6 @@ var buildOrderRecord = (state, draft, now) => {
6456
6579
  notes: null,
6457
6580
  clinical_notes: draft.clinicalNotes,
6458
6581
  passthrough: draft.passthrough,
6459
- ...draft.labAccountId !== void 0 ? { lab_account_id: draft.labAccountId } : {},
6460
6582
  created_at: nowIso,
6461
6583
  updated_at: nowIso,
6462
6584
  events: [event],
@@ -6678,8 +6800,7 @@ var insertOrders = (state, inputs, now, options = {}) => {
6678
6800
  billingType: input.billing_type ?? "client_bill",
6679
6801
  icdCodes: input.icd_codes ?? null,
6680
6802
  clinicalNotes: input.clinical_notes ?? null,
6681
- passthrough: input.passthrough ?? null,
6682
- ...input.lab_account_id !== void 0 ? { labAccountId: input.lab_account_id } : {}
6803
+ passthrough: input.passthrough ?? null
6683
6804
  },
6684
6805
  createdMs === void 0 ? now : () => createdMs
6685
6806
  );
@@ -6688,6 +6809,7 @@ var insertOrders = (state, inputs, now, options = {}) => {
6688
6809
  const run = () => {
6689
6810
  for (const { input, order, labTest, details, address, status } of prepared) {
6690
6811
  persistOrderRecord(state, order, labTest, details, address);
6812
+ state.orderLabAccounts.insert(order.id, { lab_account_id: input.lab_account_id ?? null });
6691
6813
  state.publishOrderWebhook(order, "labtest.order.created", now());
6692
6814
  if (status !== void 0) {
6693
6815
  const stored = state.orders.get(order.id);
@@ -8774,6 +8896,7 @@ var createRuntime2 = (options = {}) => {
8774
8896
  ...options.teamId !== void 0 ? { teamId: options.teamId } : {},
8775
8897
  ...options.limits ? { limits: options.limits } : {},
8776
8898
  ...options.identity ? { identity: options.identity } : {},
8899
+ ...options.defaultBillingTypes ? { defaultBillingTypes: options.defaultBillingTypes } : {},
8777
8900
  ...options.fixtures ? { fixtures: options.fixtures } : {},
8778
8901
  onWebhook: (event) => {
8779
8902
  options.onWebhook?.(event, publicNamespace);
@@ -9058,6 +9181,50 @@ var scenario = (corpus, withOrders) => {
9058
9181
  }
9059
9182
  );
9060
9183
  }
9184
+ if (withOrders) {
9185
+ const byLab = /* @__PURE__ */ new Map();
9186
+ for (const account2 of corpus.labAccounts) {
9187
+ if (account2.status !== "active") continue;
9188
+ const lab = String(account2.lab).toLowerCase();
9189
+ byLab.set(lab, (byLab.get(lab) ?? 0) + 1);
9190
+ }
9191
+ for (const [lab, count] of byLab) {
9192
+ if (count < 2 || lab === String(openAccount?.lab ?? "").toLowerCase()) continue;
9193
+ const labTest = corpus.catalog.labTests.find(
9194
+ (test) => String(test.lab?.slug ?? "").toLowerCase() === lab
9195
+ );
9196
+ if (!labTest) continue;
9197
+ steps.push({
9198
+ name: `order.create for ${lab} with lab_account_id omitted (${count} active accounts)`,
9199
+ compare: "shape",
9200
+ request: (ctx) => ({
9201
+ method: "POST",
9202
+ path: "/v3/order",
9203
+ body: orderBody(ctx.userId, labTest.id)
9204
+ }),
9205
+ capture: captureExtraOrder
9206
+ });
9207
+ }
9208
+ }
9209
+ if (withOrders) {
9210
+ const labTestFor = (lab) => corpus.catalog.labTests.find(
9211
+ (test) => String(test.lab?.slug ?? "").toLowerCase() === String(lab).toLowerCase()
9212
+ );
9213
+ for (const account2 of corpus.labAccounts) {
9214
+ const labTest = labTestFor(account2.lab);
9215
+ if (account2.status !== "active" || account2 === openAccount || !labTest) continue;
9216
+ steps.push({
9217
+ name: `order.create via ${String(account2.lab)} account ${String(account2.id)} with billing_type omitted`,
9218
+ compare: "shape",
9219
+ request: (ctx) => ({
9220
+ method: "POST",
9221
+ path: "/v3/order",
9222
+ body: orderBody(ctx.userId, labTest.id, { lab_account_id: account2.id })
9223
+ }),
9224
+ capture: captureExtraOrder
9225
+ });
9226
+ }
9227
+ }
9061
9228
  steps.push({
9062
9229
  name: "user.delete",
9063
9230
  compare: "shape",
@@ -9099,21 +9266,21 @@ var verifyAgainstReal = async (options) => {
9099
9266
  progress(`drift: re-fetching ${picked.length} of ${keys.length} recorded observations`);
9100
9267
  for (const key of picked) {
9101
9268
  const path = key.slice("GET ".length);
9102
- const recorded = options.corpus.observations[key];
9103
- if (!recorded) continue;
9269
+ const recorded2 = options.corpus.observations[key];
9270
+ if (!recorded2) continue;
9104
9271
  checked++;
9105
9272
  try {
9106
9273
  const live = await call("real", "GET", path);
9107
- if (live.status !== recorded.status) {
9274
+ if (live.status !== recorded2.status) {
9108
9275
  stale++;
9109
9276
  divergences.push({
9110
9277
  check: `drift ${key}`,
9111
9278
  kind: "status",
9112
9279
  real: live.status,
9113
- mock: recorded.status
9280
+ mock: recorded2.status
9114
9281
  });
9115
9282
  } else {
9116
- const at = firstDifference(live.body, recorded.body);
9283
+ const at = firstDifference(live.body, recorded2.body);
9117
9284
  if (at) {
9118
9285
  stale++;
9119
9286
  divergences.push({
@@ -9121,7 +9288,7 @@ var verifyAgainstReal = async (options) => {
9121
9288
  kind: "body",
9122
9289
  at,
9123
9290
  real: live.body,
9124
- mock: recorded.body
9291
+ mock: recorded2.body
9125
9292
  });
9126
9293
  }
9127
9294
  }
@@ -9249,6 +9416,7 @@ var JunctionAPI = class {
9249
9416
  if (options.identity !== void 0 && !IDENTITY_MODES.includes(options.identity))
9250
9417
  throw new TypeError(`identity must be one of ${IDENTITY_MODES.join(", ")}`);
9251
9418
  state.identity = options.identity ?? "strict";
9419
+ state.defaultBillingTypes = defaultBillingTypesFrom(options.defaultBillingTypes ?? {});
9252
9420
  const handlers = defineOperations({
9253
9421
  ...userHandlers(state),
9254
9422
  ...orderHandlers(state),
@@ -9320,9 +9488,9 @@ var JunctionAPI = class {
9320
9488
  * recorded from a team with no accounts leaves in place).
9321
9489
  */
9322
9490
  seedLabAccounts() {
9323
- const recorded = (this.corpus?.labAccounts ?? []).map((entry) => labAccountFromClientFacing(entry, this.corpus?.teamId)).filter((entry) => entry !== void 0);
9491
+ const recorded2 = (this.corpus?.labAccounts ?? []).map((entry) => labAccountFromClientFacing(entry, this.corpus?.teamId)).filter((entry) => entry !== void 0);
9324
9492
  this.state.replaceLabAccounts(
9325
- this.labAccountConfig ?? (recorded.length > 0 ? recorded : [...TEAM_LAB_ACCOUNTS])
9493
+ this.labAccountConfig ?? (recorded2.length > 0 ? recorded2 : [...TEAM_LAB_ACCOUNTS])
9326
9494
  );
9327
9495
  }
9328
9496
  /** The team this namespace answers as. */
@@ -9383,6 +9551,14 @@ var JunctionAPI = class {
9383
9551
  this.state.limits = mergeLimits(this.state.limits, input);
9384
9552
  return this.limits;
9385
9553
  }
9554
+ /** Lab slug → the `billing_type` an order gets when it omits one. Kept across `reset()`. */
9555
+ get defaultBillingTypes() {
9556
+ return { ...this.state.defaultBillingTypes };
9557
+ }
9558
+ /** Replace the per-lab default billing types (`{}` restores `client_bill` everywhere). */
9559
+ set defaultBillingTypes(input) {
9560
+ this.state.defaultBillingTypes = defaultBillingTypesFrom(input);
9561
+ }
9386
9562
  get identity() {
9387
9563
  return this.state.identity;
9388
9564
  }
@@ -9433,6 +9609,14 @@ var JunctionAPI = class {
9433
9609
  orders() {
9434
9610
  return this.state.orders.list({ order: "oldest" }).map((entry) => entry.value);
9435
9611
  }
9612
+ /**
9613
+ * The lab account an order was placed with: its id, `null` for Junction's platform account,
9614
+ * or `undefined` for an unknown order. Junction's order has no such field, so this is the
9615
+ * only place the mock reports it.
9616
+ */
9617
+ orderLabAccount(id) {
9618
+ return this.state.orderLabAccounts.get(id)?.lab_account_id;
9619
+ }
9436
9620
  /**
9437
9621
  * Put an order straight into a status (a full `<status>.<method>.<event>` value) and
9438
9622
  * publish the webhook a real transition would. `flags` takes the same keys as
@@ -9526,7 +9710,6 @@ var touchedIds = async (request2, body, response) => {
9526
9710
  const order = asRecord3(created?.order);
9527
9711
  put2("orderId", order?.id);
9528
9712
  put2("userId", order?.user_id);
9529
- put2("labAccountId", order?.lab_account_id);
9530
9713
  put2("userId", created?.user_id);
9531
9714
  }
9532
9715
  return ids;
@@ -9563,6 +9746,8 @@ export {
9563
9746
  observationCacheKey,
9564
9747
  MOCK_TEAM_ID,
9565
9748
  US_STATES,
9749
+ BILLING_TYPES,
9750
+ defaultBillingTypesFrom,
9566
9751
  OTHER_TEAM_ID,
9567
9752
  TEAM_LAB_ACCOUNTS,
9568
9753
  isLinkedToTeam,
@@ -9570,6 +9755,8 @@ export {
9570
9755
  PLATFORM_ACCOUNT_STATES,
9571
9756
  DELEGATED_ACCOUNT_STATES,
9572
9757
  LAB_ACCOUNT_PRESETS,
9758
+ LAB_ACCOUNT_PRESET_ALIASES,
9759
+ SYNTHETIC_LAB_ACCOUNT_PRESETS,
9573
9760
  presetAccountId,
9574
9761
  ORDER_STATUSES_BY_METHOD,
9575
9762
  TRANSITION_ALIASES,
@@ -9608,4 +9795,4 @@ export {
9608
9795
  JunctionAPI,
9609
9796
  LabAccountConflict
9610
9797
  };
9611
- //# sourceMappingURL=chunk-MS774AJA.js.map
9798
+ //# sourceMappingURL=chunk-GWXEPT6A.js.map