@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.
- package/CHANGELOG.md +14 -4
- package/README.md +80 -20
- package/dist/{chunk-TG2EOH3J.js → chunk-A4KJJ5V3.js} +25 -3
- package/dist/chunk-A4KJJ5V3.js.map +7 -0
- package/dist/{chunk-MS774AJA.js → chunk-GWXEPT6A.js} +230 -43
- package/dist/chunk-GWXEPT6A.js.map +7 -0
- package/dist/cli.js +5 -5
- package/dist/cli.js.map +2 -2
- package/dist/index.d.ts +161 -98
- package/dist/index.js +7 -1
- package/dist/server.d.ts +250 -168
- package/dist/server.js +2 -2
- package/package.json +1 -1
- package/dist/chunk-MS774AJA.js.map +0 -7
- package/dist/chunk-TG2EOH3J.js.map +0 -7
|
@@ -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
|
-
|
|
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.
|
|
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
|
-
//
|
|
3286
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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.
|
|
3366
|
-
if (active
|
|
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
|
|
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
|
-
|
|
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: { ...
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
9103
|
-
if (!
|
|
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 !==
|
|
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:
|
|
9280
|
+
mock: recorded2.status
|
|
9114
9281
|
});
|
|
9115
9282
|
} else {
|
|
9116
|
-
const at = firstDifference(live.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:
|
|
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
|
|
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 ?? (
|
|
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-
|
|
9798
|
+
//# sourceMappingURL=chunk-GWXEPT6A.js.map
|