@crvouga/mockingbird-service-junction 0.3.0 → 0.5.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 +17 -1
- package/README.md +1 -1
- package/dist/{chunk-KLBGX23P.js → chunk-CR35KYXY.js} +46 -4
- package/dist/{chunk-KLBGX23P.js.map → chunk-CR35KYXY.js.map} +2 -2
- package/dist/{chunk-NFWUEPW4.js → chunk-ICINEQ7D.js} +353 -71
- package/dist/chunk-ICINEQ7D.js.map +7 -0
- package/dist/cli.js +2 -2
- package/dist/index.d.ts +131 -7
- package/dist/index.js +1 -1
- package/dist/server.d.ts +262 -14
- package/dist/server.js +2 -2
- package/package.json +6 -3
- package/dist/chunk-NFWUEPW4.js.map +0 -7
|
@@ -213,8 +213,21 @@ var createControlPlane = (context) => {
|
|
|
213
213
|
},
|
|
214
214
|
"GET /faults": () => json(200, { faults: context.faults.list() }),
|
|
215
215
|
"POST /faults": ({ body, namespace }) => {
|
|
216
|
-
if (
|
|
217
|
-
|
|
216
|
+
if (isRecord(body) && typeof body.preset === "string") {
|
|
217
|
+
if (!context.applyPreset)
|
|
218
|
+
return adminError(400, `${context.name} has no fault presets`);
|
|
219
|
+
const { preset, ...overrides } = body;
|
|
220
|
+
try {
|
|
221
|
+
return json(201, {
|
|
222
|
+
preset,
|
|
223
|
+
rules: context.applyPreset(preset, namespace, overrides)
|
|
224
|
+
});
|
|
225
|
+
} catch (error) {
|
|
226
|
+
return adminError(404, error instanceof Error ? error.message : String(error));
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
if (!isRecord(body) || typeof body.status !== "number" && typeof body.delayMs !== "number" && typeof body.latencyMs !== "number" && body.drop !== true && typeof body.effect !== "string") {
|
|
230
|
+
return adminError(400, "a fault needs a numeric status, a delayMs/latencyMs, drop: true, an effect, or a preset");
|
|
218
231
|
}
|
|
219
232
|
const rule = {
|
|
220
233
|
// Scoped to the caller's namespace unless it asks for every one, so one worker's
|
|
@@ -333,6 +346,21 @@ var createControlPlane = (context) => {
|
|
|
333
346
|
};
|
|
334
347
|
};
|
|
335
348
|
|
|
349
|
+
// ../core/dist/credentials.js
|
|
350
|
+
var createCredentialRegistry = () => {
|
|
351
|
+
const map = /* @__PURE__ */ new Map();
|
|
352
|
+
return {
|
|
353
|
+
set: (credential, namespace) => {
|
|
354
|
+
map.set(credential, namespace);
|
|
355
|
+
},
|
|
356
|
+
get: (credential) => map.get(credential),
|
|
357
|
+
remove: (credential) => map.delete(credential),
|
|
358
|
+
clear: () => map.clear(),
|
|
359
|
+
entries: () => [...map].map(([credential, namespace]) => ({ credential, namespace })).sort((a, b) => a.credential.localeCompare(b.credential))
|
|
360
|
+
};
|
|
361
|
+
};
|
|
362
|
+
var maskCredential = (credential) => credential.length <= 8 ? `${credential.slice(0, 2)}\u2026` : `${credential.slice(0, 6)}\u2026${credential.slice(-2)}`;
|
|
363
|
+
|
|
336
364
|
// ../core/dist/rng.js
|
|
337
365
|
var seedFrom = (value) => {
|
|
338
366
|
let hash = 2166136261;
|
|
@@ -377,11 +405,14 @@ var matches = (rule, candidate) => {
|
|
|
377
405
|
return true;
|
|
378
406
|
};
|
|
379
407
|
var faultResponse = (rule) => {
|
|
408
|
+
const status = rule.status ?? 500;
|
|
380
409
|
const headers = { "content-type": "application/json", ...rule.headers };
|
|
381
410
|
if (typeof rule.body === "string")
|
|
382
|
-
return new Response(rule.body, { status
|
|
411
|
+
return new Response(rule.body, { status, headers });
|
|
412
|
+
if (rule.body === null)
|
|
413
|
+
return new Response(null, { status, headers: rule.headers ?? {} });
|
|
383
414
|
const body = rule.body === void 0 ? { detail: "Injected by Mockingbird" } : rule.body;
|
|
384
|
-
return new Response(JSON.stringify(body), { status
|
|
415
|
+
return new Response(JSON.stringify(body), { status, headers });
|
|
385
416
|
};
|
|
386
417
|
var createFaultRegistry = (rng = createRng(0)) => {
|
|
387
418
|
const entries = [];
|
|
@@ -407,6 +438,7 @@ var createFaultRegistry = (rng = createRng(0)) => {
|
|
|
407
438
|
entries.length = 0;
|
|
408
439
|
},
|
|
409
440
|
async take(candidate) {
|
|
441
|
+
const hits = [];
|
|
410
442
|
for (const entry of entries) {
|
|
411
443
|
if (entry.remaining === 0)
|
|
412
444
|
continue;
|
|
@@ -418,12 +450,23 @@ var createFaultRegistry = (rng = createRng(0)) => {
|
|
|
418
450
|
entry.hits++;
|
|
419
451
|
if (entry.remaining !== null)
|
|
420
452
|
entry.remaining--;
|
|
421
|
-
|
|
422
|
-
|
|
453
|
+
const delay = entry.rule.delayMs ?? entry.rule.latencyMs;
|
|
454
|
+
if (delay !== void 0 && delay > 0) {
|
|
455
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
423
456
|
}
|
|
424
|
-
|
|
457
|
+
const hit = { id: entry.rule.id };
|
|
458
|
+
if (entry.rule.effect !== void 0) {
|
|
459
|
+
hit.effect = { name: entry.rule.effect, params: entry.rule.params ?? {} };
|
|
460
|
+
}
|
|
461
|
+
if (entry.rule.drop === true)
|
|
462
|
+
hit.drop = true;
|
|
463
|
+
else if (entry.rule.status !== void 0)
|
|
464
|
+
hit.response = faultResponse(entry.rule);
|
|
465
|
+
hits.push(hit);
|
|
466
|
+
if (hit.drop || hit.response)
|
|
467
|
+
break;
|
|
425
468
|
}
|
|
426
|
-
return
|
|
469
|
+
return hits;
|
|
427
470
|
}
|
|
428
471
|
};
|
|
429
472
|
};
|
|
@@ -996,6 +1039,7 @@ var createService = (options) => {
|
|
|
996
1039
|
sqlite: options.sqlite,
|
|
997
1040
|
namespace: options.namespace,
|
|
998
1041
|
operation,
|
|
1042
|
+
document: options.document,
|
|
999
1043
|
now
|
|
1000
1044
|
};
|
|
1001
1045
|
const short = await options.before?.(context);
|
|
@@ -1038,12 +1082,123 @@ var restoreNamespace = (sqlite, namespace, snapshot) => {
|
|
|
1038
1082
|
};
|
|
1039
1083
|
|
|
1040
1084
|
// ../core/dist/version.js
|
|
1041
|
-
var PACKAGE_VERSION = true ? "0.
|
|
1085
|
+
var PACKAGE_VERSION = true ? "0.5.0" : UNRELEASED_VERSION;
|
|
1086
|
+
|
|
1087
|
+
// ../core/dist/webhooks.js
|
|
1088
|
+
var json2 = (status, body) => new Response(JSON.stringify(body), { status, headers: { "content-type": "application/json" } });
|
|
1089
|
+
var adminError2 = (status, message) => json2(status, { error: { type: "mockingbird_admin", message } });
|
|
1090
|
+
var isRecord3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1091
|
+
var parseEndpoint = (value) => {
|
|
1092
|
+
if (!isRecord3(value) || typeof value.url !== "string")
|
|
1093
|
+
return "each endpoint needs a url";
|
|
1094
|
+
try {
|
|
1095
|
+
new URL(value.url);
|
|
1096
|
+
} catch {
|
|
1097
|
+
return `not a URL: ${value.url}`;
|
|
1098
|
+
}
|
|
1099
|
+
const endpoint = { url: value.url };
|
|
1100
|
+
if (typeof value.id === "string")
|
|
1101
|
+
endpoint.id = value.id;
|
|
1102
|
+
if (typeof value.secret === "string")
|
|
1103
|
+
endpoint.secret = value.secret;
|
|
1104
|
+
if (typeof value.signUrl === "string")
|
|
1105
|
+
endpoint.signUrl = value.signUrl;
|
|
1106
|
+
const events = value.events ?? value.enabledEvents;
|
|
1107
|
+
if (Array.isArray(events))
|
|
1108
|
+
endpoint.events = events.map(String);
|
|
1109
|
+
if (isRecord3(value.tags)) {
|
|
1110
|
+
endpoint.tags = Object.fromEntries(Object.entries(value.tags).map(([k, v]) => [k, String(v)]));
|
|
1111
|
+
}
|
|
1112
|
+
if (typeof value.account === "string")
|
|
1113
|
+
endpoint.tags = { ...endpoint.tags, account: value.account };
|
|
1114
|
+
if (isRecord3(value.headers)) {
|
|
1115
|
+
endpoint.headers = Object.fromEntries(Object.entries(value.headers).map(([k, v]) => [k, String(v)]));
|
|
1116
|
+
}
|
|
1117
|
+
return endpoint;
|
|
1118
|
+
};
|
|
1119
|
+
var webhookAdminRoutes = (hub) => ({
|
|
1120
|
+
"GET /webhooks": ({ url, namespace }) => json2(200, {
|
|
1121
|
+
deliveries: hub.deliveries(url.searchParams.get("all") === "1" ? void 0 : namespace).filter((d) => {
|
|
1122
|
+
const type = url.searchParams.get("type");
|
|
1123
|
+
return type === null || d.type === type;
|
|
1124
|
+
})
|
|
1125
|
+
}),
|
|
1126
|
+
"GET /webhooks/events": ({ url, namespace }) => {
|
|
1127
|
+
const type = url.searchParams.get("type");
|
|
1128
|
+
return json2(200, {
|
|
1129
|
+
events: hub.messages(url.searchParams.get("all") === "1" ? void 0 : namespace).filter((m) => type === null || m.type === type).map((m) => ({ ...m, payload: parsePayload(m) }))
|
|
1130
|
+
});
|
|
1131
|
+
},
|
|
1132
|
+
"POST /webhooks/:id/replay": async ({ params }) => {
|
|
1133
|
+
const replayed = await hub.replay(params.id);
|
|
1134
|
+
return replayed ? json2(200, replayed) : adminError2(404, `no delivery ${params.id}`);
|
|
1135
|
+
},
|
|
1136
|
+
"POST /webhooks/flush": async () => {
|
|
1137
|
+
await hub.flush();
|
|
1138
|
+
return json2(200, { status: "ok" });
|
|
1139
|
+
},
|
|
1140
|
+
"POST /webhooks/faults": ({ body, namespace }) => {
|
|
1141
|
+
if (!isRecord3(body) || !["duplicate", "reorder", "drop"].includes(String(body.mode))) {
|
|
1142
|
+
return adminError2(400, "mode must be duplicate, reorder or drop");
|
|
1143
|
+
}
|
|
1144
|
+
const fault = { mode: body.mode };
|
|
1145
|
+
if (typeof body.count === "number")
|
|
1146
|
+
fault.count = body.count;
|
|
1147
|
+
hub.fault(namespace, fault);
|
|
1148
|
+
return json2(201, { namespace, ...fault });
|
|
1149
|
+
},
|
|
1150
|
+
"GET /webhook-endpoints": ({ namespace }) => json2(200, {
|
|
1151
|
+
endpoints: hub.endpoints(namespace).map(({ secret, ...rest }) => ({
|
|
1152
|
+
...rest,
|
|
1153
|
+
secret: secret ? "(set)" : null
|
|
1154
|
+
}))
|
|
1155
|
+
}),
|
|
1156
|
+
"PUT /webhook-endpoints": ({ body, namespace }) => {
|
|
1157
|
+
const list = Array.isArray(body) ? body : isRecord3(body) ? body.endpoints : void 0;
|
|
1158
|
+
if (!Array.isArray(list))
|
|
1159
|
+
return adminError2(400, "expected [{url, secret?, events?}]");
|
|
1160
|
+
const parsed = [];
|
|
1161
|
+
for (const each of list) {
|
|
1162
|
+
const endpoint = parseEndpoint(each);
|
|
1163
|
+
if (typeof endpoint === "string")
|
|
1164
|
+
return adminError2(400, endpoint);
|
|
1165
|
+
parsed.push(endpoint);
|
|
1166
|
+
}
|
|
1167
|
+
const set = hub.setEndpoints(namespace, parsed);
|
|
1168
|
+
return json2(200, { endpoints: set.map((e) => ({ ...e, secret: e.secret ? "(set)" : null })) });
|
|
1169
|
+
},
|
|
1170
|
+
"DELETE /webhook-endpoints": ({ namespace }) => {
|
|
1171
|
+
hub.setEndpoints(namespace, []);
|
|
1172
|
+
return json2(200, { status: "ok" });
|
|
1173
|
+
}
|
|
1174
|
+
});
|
|
1175
|
+
var parsePayload = (message) => {
|
|
1176
|
+
if (message.contentType.startsWith("application/json")) {
|
|
1177
|
+
try {
|
|
1178
|
+
return JSON.parse(message.body);
|
|
1179
|
+
} catch {
|
|
1180
|
+
return message.body;
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1183
|
+
if (message.contentType.startsWith("application/x-www-form-urlencoded")) {
|
|
1184
|
+
return Object.fromEntries(new URLSearchParams(message.body));
|
|
1185
|
+
}
|
|
1186
|
+
return message.body;
|
|
1187
|
+
};
|
|
1042
1188
|
|
|
1043
1189
|
// ../core/dist/runtime.js
|
|
1044
1190
|
var MOCKINGBIRD_HEADER = "x-mockingbird";
|
|
1045
1191
|
var DEFAULT_NAMESPACE = "default";
|
|
1046
1192
|
var NAMESPACE_PATTERN = /^[A-Za-z0-9_.-]{1,64}$/;
|
|
1193
|
+
var PATH_PREFIX = /^\/ns\/([^/]+)(\/.*)?$/;
|
|
1194
|
+
var effects = /* @__PURE__ */ new WeakMap();
|
|
1195
|
+
var DroppedConnectionError = class extends TypeError {
|
|
1196
|
+
code = "MOCKINGBIRD_DROP";
|
|
1197
|
+
constructor() {
|
|
1198
|
+
super("fetch failed: connection dropped by Mockingbird fault");
|
|
1199
|
+
this.name = "TypeError";
|
|
1200
|
+
}
|
|
1201
|
+
};
|
|
1047
1202
|
var operationMatcher = (document2) => {
|
|
1048
1203
|
const matchers = listOperations(document2).map((operation) => ({
|
|
1049
1204
|
operationId: operation.operationId,
|
|
@@ -1062,6 +1217,7 @@ var createRuntime = (options) => {
|
|
|
1062
1217
|
const journal = createJournal(options.journalSize ?? DEFAULT_JOURNAL_SIZE);
|
|
1063
1218
|
const version = options.version ?? PACKAGE_VERSION;
|
|
1064
1219
|
const instances = /* @__PURE__ */ new Map();
|
|
1220
|
+
const credentials = createCredentialRegistry();
|
|
1065
1221
|
const operationIdFor = options.document ? operationMatcher(options.document) : () => void 0;
|
|
1066
1222
|
const storageNamespace = (name) => name === DEFAULT_NAMESPACE ? options.name : `${options.name}:${name}`;
|
|
1067
1223
|
const instance = (name = DEFAULT_NAMESPACE) => {
|
|
@@ -1083,10 +1239,12 @@ var createRuntime = (options) => {
|
|
|
1083
1239
|
};
|
|
1084
1240
|
const reset = async (name = DEFAULT_NAMESPACE) => {
|
|
1085
1241
|
if (name === "*") {
|
|
1242
|
+
options.webhooks?.clear();
|
|
1086
1243
|
for (const each of instances.values())
|
|
1087
1244
|
await each.reset();
|
|
1088
1245
|
return;
|
|
1089
1246
|
}
|
|
1247
|
+
options.webhooks?.clear(name);
|
|
1090
1248
|
const target = instances.get(name);
|
|
1091
1249
|
if (target)
|
|
1092
1250
|
await target.reset();
|
|
@@ -1109,13 +1267,57 @@ var createRuntime = (options) => {
|
|
|
1109
1267
|
metrics,
|
|
1110
1268
|
journal,
|
|
1111
1269
|
rng,
|
|
1270
|
+
credentials,
|
|
1271
|
+
webhooks: options.webhooks,
|
|
1272
|
+
applyPreset: (name, namespace = DEFAULT_NAMESPACE, overrides = {}) => {
|
|
1273
|
+
const preset = options.presets?.[name];
|
|
1274
|
+
if (!preset)
|
|
1275
|
+
throw new RangeError(`no fault preset ${JSON.stringify(name)}`);
|
|
1276
|
+
const added = (preset.rules ?? []).map((rule, index) => faults.add({
|
|
1277
|
+
namespace,
|
|
1278
|
+
...rule,
|
|
1279
|
+
...overrides,
|
|
1280
|
+
preset: name,
|
|
1281
|
+
id: `${overrides.id ?? name}${(preset.rules ?? []).length > 1 ? `_${index + 1}` : ""}`
|
|
1282
|
+
}));
|
|
1283
|
+
if (preset.webhook && options.webhooks) {
|
|
1284
|
+
options.webhooks.fault(namespace, {
|
|
1285
|
+
...preset.webhook,
|
|
1286
|
+
...overrides.count !== void 0 ? { count: overrides.count } : {}
|
|
1287
|
+
});
|
|
1288
|
+
}
|
|
1289
|
+
return added;
|
|
1290
|
+
},
|
|
1112
1291
|
instance,
|
|
1113
1292
|
namespaces: () => [...instances.keys()].sort(),
|
|
1114
1293
|
reset,
|
|
1115
1294
|
snapshot,
|
|
1116
1295
|
restore,
|
|
1117
|
-
fetch: async (
|
|
1118
|
-
|
|
1296
|
+
fetch: async (incoming) => {
|
|
1297
|
+
let request2 = incoming;
|
|
1298
|
+
const prefixed = PATH_PREFIX.exec(new URL(request2.url).pathname);
|
|
1299
|
+
if (prefixed) {
|
|
1300
|
+
const url2 = new URL(request2.url);
|
|
1301
|
+
url2.pathname = prefixed[2] ?? "/";
|
|
1302
|
+
const headers = new Headers(request2.headers);
|
|
1303
|
+
if (!headers.has(NAMESPACE_HEADER)) {
|
|
1304
|
+
headers.set(NAMESPACE_HEADER, decodeURIComponent(prefixed[1]));
|
|
1305
|
+
}
|
|
1306
|
+
const hasBody = request2.method !== "GET" && request2.method !== "HEAD";
|
|
1307
|
+
request2 = new Request(url2, {
|
|
1308
|
+
method: request2.method,
|
|
1309
|
+
headers,
|
|
1310
|
+
...hasBody ? { body: await request2.arrayBuffer() } : {},
|
|
1311
|
+
signal: request2.signal
|
|
1312
|
+
});
|
|
1313
|
+
}
|
|
1314
|
+
let namespace = control.namespaceOf(request2);
|
|
1315
|
+
if (!request2.headers.has(NAMESPACE_HEADER) && options.credential) {
|
|
1316
|
+
const credential = options.credential(request2);
|
|
1317
|
+
const mapped = credential !== void 0 ? credentials.get(credential) : void 0;
|
|
1318
|
+
if (mapped !== void 0)
|
|
1319
|
+
namespace = mapped;
|
|
1320
|
+
}
|
|
1119
1321
|
const stamp = (response2) => {
|
|
1120
1322
|
const value = NAMESPACE_PATTERN.test(namespace) ? `${options.name}@${version}; ns=${namespace}` : `${options.name}@${version}`;
|
|
1121
1323
|
try {
|
|
@@ -1161,18 +1363,26 @@ var createRuntime = (options) => {
|
|
|
1161
1363
|
}
|
|
1162
1364
|
}), { status: 400, headers: { "content-type": "application/json" } }));
|
|
1163
1365
|
}
|
|
1164
|
-
const
|
|
1366
|
+
const hits = await faults.take({
|
|
1165
1367
|
operationId,
|
|
1166
1368
|
method: request2.method,
|
|
1167
1369
|
path: url.pathname,
|
|
1168
1370
|
namespace
|
|
1169
1371
|
});
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1372
|
+
const final = hits.find((hit) => hit.drop || hit.response);
|
|
1373
|
+
if (final?.drop) {
|
|
1374
|
+
log(0, final.id);
|
|
1375
|
+
throw new DroppedConnectionError();
|
|
1173
1376
|
}
|
|
1377
|
+
if (final?.response) {
|
|
1378
|
+
log(final.response.status, final.id);
|
|
1379
|
+
return stamp(final.response);
|
|
1380
|
+
}
|
|
1381
|
+
const fired = hits.filter((hit) => hit.effect !== void 0);
|
|
1382
|
+
if (fired.length > 0)
|
|
1383
|
+
effects.set(request2, fired.map((hit) => hit.effect));
|
|
1174
1384
|
const response = await instance(namespace).fetch(request2);
|
|
1175
|
-
log(response.status,
|
|
1385
|
+
log(response.status, fired[0]?.id, response);
|
|
1176
1386
|
return stamp(response);
|
|
1177
1387
|
}
|
|
1178
1388
|
};
|
|
@@ -1189,11 +1399,83 @@ var createRuntime = (options) => {
|
|
|
1189
1399
|
snapshot: (name) => snapshot(name),
|
|
1190
1400
|
restore: (name, from) => restore(from, name),
|
|
1191
1401
|
describe: options.describe ?? (() => ({})),
|
|
1192
|
-
|
|
1402
|
+
...options.presets ? {
|
|
1403
|
+
applyPreset: (name, namespace, overrides) => runtime.applyPreset(name, namespace, overrides)
|
|
1404
|
+
} : {},
|
|
1405
|
+
routes: {
|
|
1406
|
+
...credentialRoutes(credentials),
|
|
1407
|
+
...options.presets ? presetRoutes(options.presets, runtime) : {},
|
|
1408
|
+
...options.webhooks ? webhookAdminRoutes(options.webhooks) : {},
|
|
1409
|
+
...options.admin?.(runtime) ?? {}
|
|
1410
|
+
},
|
|
1193
1411
|
adminKey: options.adminKey
|
|
1194
1412
|
});
|
|
1195
1413
|
return runtime;
|
|
1196
1414
|
};
|
|
1415
|
+
var adminJson = (status, body) => new Response(JSON.stringify(body), { status, headers: { "content-type": "application/json" } });
|
|
1416
|
+
var adminFail = (status, message) => adminJson(status, { error: { type: "mockingbird_admin", message } });
|
|
1417
|
+
var isObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1418
|
+
var credentialRoutes = (registry) => ({
|
|
1419
|
+
"GET /credentials": () => adminJson(200, {
|
|
1420
|
+
credentials: registry.entries().map(({ credential, namespace }) => ({
|
|
1421
|
+
credential: maskCredential(credential),
|
|
1422
|
+
namespace
|
|
1423
|
+
}))
|
|
1424
|
+
}),
|
|
1425
|
+
"PUT /credentials": ({ body, namespace }) => {
|
|
1426
|
+
const pairs = [];
|
|
1427
|
+
const list = Array.isArray(body) ? body : isObject(body) ? body.credentials : void 0;
|
|
1428
|
+
if (Array.isArray(list)) {
|
|
1429
|
+
for (const each of list) {
|
|
1430
|
+
if (typeof each === "string")
|
|
1431
|
+
pairs.push([each, namespace]);
|
|
1432
|
+
else if (isObject(each) && typeof each.credential === "string") {
|
|
1433
|
+
pairs.push([
|
|
1434
|
+
each.credential,
|
|
1435
|
+
typeof each.namespace === "string" ? each.namespace : namespace
|
|
1436
|
+
]);
|
|
1437
|
+
} else
|
|
1438
|
+
return adminFail(400, "each entry is a credential string or {credential, namespace}");
|
|
1439
|
+
}
|
|
1440
|
+
} else if (isObject(list)) {
|
|
1441
|
+
for (const [credential, target] of Object.entries(list)) {
|
|
1442
|
+
if (typeof target !== "string")
|
|
1443
|
+
return adminFail(400, `namespace for ${credential} must be a string`);
|
|
1444
|
+
pairs.push([credential, target]);
|
|
1445
|
+
}
|
|
1446
|
+
} else if (isObject(body) && typeof body.credential === "string") {
|
|
1447
|
+
pairs.push([body.credential, typeof body.namespace === "string" ? body.namespace : namespace]);
|
|
1448
|
+
} else {
|
|
1449
|
+
return adminFail(400, 'expected {"credentials": {"<credential>": "<namespace>"}}');
|
|
1450
|
+
}
|
|
1451
|
+
for (const [credential, target] of pairs) {
|
|
1452
|
+
if (!NAMESPACE_PATTERN.test(target))
|
|
1453
|
+
return adminFail(400, `bad namespace ${JSON.stringify(target)}`);
|
|
1454
|
+
registry.set(credential, target);
|
|
1455
|
+
}
|
|
1456
|
+
return adminJson(200, { mapped: pairs.length });
|
|
1457
|
+
},
|
|
1458
|
+
"DELETE /credentials": ({ url }) => {
|
|
1459
|
+
const credential = url.searchParams.get("credential");
|
|
1460
|
+
if (credential === null)
|
|
1461
|
+
registry.clear();
|
|
1462
|
+
else
|
|
1463
|
+
registry.remove(credential);
|
|
1464
|
+
return adminJson(200, { status: "ok" });
|
|
1465
|
+
}
|
|
1466
|
+
});
|
|
1467
|
+
var presetRoutes = (presets, runtime) => ({
|
|
1468
|
+
"GET /faults/presets": () => adminJson(200, {
|
|
1469
|
+
presets: Object.entries(presets).map(([name, preset]) => ({ name, ...preset }))
|
|
1470
|
+
}),
|
|
1471
|
+
"POST /faults/presets/:name": ({ params, body, namespace }) => {
|
|
1472
|
+
const name = params.name;
|
|
1473
|
+
if (!presets[name])
|
|
1474
|
+
return adminFail(404, `no fault preset ${name}; GET /__admin/faults/presets`);
|
|
1475
|
+
const overrides = isObject(body) ? body : {};
|
|
1476
|
+
return adminJson(201, { preset: name, rules: runtime.applyPreset(name, namespace, overrides) });
|
|
1477
|
+
}
|
|
1478
|
+
});
|
|
1197
1479
|
|
|
1198
1480
|
// src/catalog.ts
|
|
1199
1481
|
var LAB_TEST_CATALOG = [
|
|
@@ -2626,7 +2908,7 @@ var renderLabAccount = (entry) => ({
|
|
|
2626
2908
|
),
|
|
2627
2909
|
team_id_allowlist: [...entry.team_id_allowlist]
|
|
2628
2910
|
});
|
|
2629
|
-
var
|
|
2911
|
+
var isRecord4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2630
2912
|
var DELEGATED_FLOWS = [
|
|
2631
2913
|
"order_delegated",
|
|
2632
2914
|
"result_delegated",
|
|
@@ -2642,7 +2924,7 @@ var checkStates = (value, where) => {
|
|
|
2642
2924
|
};
|
|
2643
2925
|
var stringList = (value) => Array.isArray(value) && value.every((entry) => typeof entry === "string") ? [...value] : void 0;
|
|
2644
2926
|
var labAccountFromInput = (input, teamId = MOCK_TEAM_ID) => {
|
|
2645
|
-
if (!
|
|
2927
|
+
if (!isRecord4(input)) throw new TypeError("lab account must be an object");
|
|
2646
2928
|
if (typeof input.id !== "string" || input.id === "")
|
|
2647
2929
|
throw new TypeError("lab account needs a non-empty id");
|
|
2648
2930
|
const where = `lab account ${input.id}`;
|
|
@@ -2654,7 +2936,7 @@ var labAccountFromInput = (input, teamId = MOCK_TEAM_ID) => {
|
|
|
2654
2936
|
throw new TypeError(`${where}: delegated_flow must be one of ${DELEGATED_FLOWS.join(", ")}`);
|
|
2655
2937
|
if (input.states !== void 0) checkStates(input.states, `${where}: states`);
|
|
2656
2938
|
if (input.allowed_billing !== void 0) {
|
|
2657
|
-
if (!
|
|
2939
|
+
if (!isRecord4(input.allowed_billing))
|
|
2658
2940
|
throw new TypeError(`${where}: allowed_billing must map billing types to state lists`);
|
|
2659
2941
|
for (const [type, states] of Object.entries(input.allowed_billing)) {
|
|
2660
2942
|
if (!BILLING_TYPES.includes(type))
|
|
@@ -2693,7 +2975,7 @@ var labAccountFromInput = (input, teamId = MOCK_TEAM_ID) => {
|
|
|
2693
2975
|
};
|
|
2694
2976
|
};
|
|
2695
2977
|
var patchLabAccount = (existing, patch, teamId) => {
|
|
2696
|
-
if (!
|
|
2978
|
+
if (!isRecord4(patch)) throw new TypeError("lab account patch must be an object");
|
|
2697
2979
|
if (patch.id !== void 0 && patch.id !== existing.id)
|
|
2698
2980
|
throw new TypeError(`lab account ${existing.id}: id cannot be changed`);
|
|
2699
2981
|
const { states, ...rest } = patch;
|
|
@@ -2713,7 +2995,7 @@ var patchLabAccount = (existing, patch, teamId) => {
|
|
|
2713
2995
|
var labAccountFromClientFacing = (value, recordedTeamId) => {
|
|
2714
2996
|
if (typeof value.id !== "string" || typeof value.lab !== "string") return void 0;
|
|
2715
2997
|
const billing = {};
|
|
2716
|
-
if (
|
|
2998
|
+
if (isRecord4(value.allowed_billing)) {
|
|
2717
2999
|
for (const [type, states] of Object.entries(value.allowed_billing)) {
|
|
2718
3000
|
const list = stringList(states);
|
|
2719
3001
|
if (list) billing[type] = list;
|
|
@@ -2967,18 +3249,18 @@ var resolveTransition = (method, target) => {
|
|
|
2967
3249
|
error: `${JSON.stringify(target)} is not a ${method} status. Use one of: ${names.join(", ")}, or a full status such as ${allowed.slice(0, 2).map((s) => `${s.split(".")[0]}.${method}.${s.split(".")[1]}`).join(", ")}`
|
|
2968
3250
|
};
|
|
2969
3251
|
};
|
|
2970
|
-
var
|
|
2971
|
-
var
|
|
3252
|
+
var adminError3 = (status, message) => jsonRes(status, { error: { type: "mockingbird_admin", message } });
|
|
3253
|
+
var isRecord5 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2972
3254
|
var guarded = (run) => {
|
|
2973
3255
|
try {
|
|
2974
3256
|
return run();
|
|
2975
3257
|
} catch (error) {
|
|
2976
|
-
if (error instanceof TypeError) return
|
|
3258
|
+
if (error instanceof TypeError) return adminError3(400, error.message);
|
|
2977
3259
|
if (error instanceof Error && error.name === "LabAccountConflict") {
|
|
2978
|
-
return
|
|
3260
|
+
return adminError3(409, error.message);
|
|
2979
3261
|
}
|
|
2980
3262
|
const { status, message } = fixtureErrorMessage(error);
|
|
2981
|
-
return
|
|
3263
|
+
return adminError3(status, message);
|
|
2982
3264
|
}
|
|
2983
3265
|
};
|
|
2984
3266
|
var junctionAdminRoutes = (options) => (runtime) => {
|
|
@@ -2994,20 +3276,20 @@ var junctionAdminRoutes = (options) => (runtime) => {
|
|
|
2994
3276
|
}))
|
|
2995
3277
|
}),
|
|
2996
3278
|
"POST /orders/:id/transition": (request2) => {
|
|
2997
|
-
const body =
|
|
3279
|
+
const body = isRecord5(request2.body) ? request2.body : {};
|
|
2998
3280
|
const order = api(request2).order(request2.params.id);
|
|
2999
|
-
if (!order) return
|
|
3281
|
+
if (!order) return adminError3(404, `no order ${request2.params.id} in ${request2.namespace}`);
|
|
3000
3282
|
if (typeof body.to !== "string") {
|
|
3001
|
-
return
|
|
3283
|
+
return adminError3(400, 'body needs "to": a status like "completed" or "at_lab"');
|
|
3002
3284
|
}
|
|
3003
3285
|
const method = methodOf(order);
|
|
3004
3286
|
const resolved = resolveTransition(method, body.to);
|
|
3005
|
-
if ("error" in resolved) return
|
|
3006
|
-
let flags =
|
|
3287
|
+
if ("error" in resolved) return adminError3(400, resolved.error);
|
|
3288
|
+
let flags = isRecord5(body.flags) ? body.flags : null;
|
|
3007
3289
|
if (typeof body.result === "string") {
|
|
3008
3290
|
const named = RESULT_FIXTURES[body.result];
|
|
3009
3291
|
if (!named) {
|
|
3010
|
-
return
|
|
3292
|
+
return adminError3(
|
|
3011
3293
|
400,
|
|
3012
3294
|
`no result fixture ${body.result}; one of ${Object.keys(RESULT_FIXTURES).join(", ")}`
|
|
3013
3295
|
);
|
|
@@ -3027,23 +3309,23 @@ var junctionAdminRoutes = (options) => (runtime) => {
|
|
|
3027
3309
|
"GET /result-fixtures": () => jsonRes(200, { fixtures: RESULT_FIXTURES }),
|
|
3028
3310
|
"GET /results/:id": (request2) => {
|
|
3029
3311
|
const fixture = api(request2).resultFixture(request2.params.id);
|
|
3030
|
-
return fixture ? jsonRes(200, fixture) :
|
|
3312
|
+
return fixture ? jsonRes(200, fixture) : adminError3(404, "no result fixture for that order");
|
|
3031
3313
|
},
|
|
3032
3314
|
"PUT /results/:id": (request2) => {
|
|
3033
|
-
if (!
|
|
3315
|
+
if (!isRecord5(request2.body)) return adminError3(400, "expected a result fixture object");
|
|
3034
3316
|
const fixture = { name: "custom", ...request2.body };
|
|
3035
3317
|
if (fixture.results !== void 0 && !Array.isArray(fixture.results)) {
|
|
3036
|
-
return
|
|
3318
|
+
return adminError3(400, "results must be an array of result lines");
|
|
3037
3319
|
}
|
|
3038
|
-
return api(request2).installResultFixture(request2.params.id, fixture) ? jsonRes(200, fixture) :
|
|
3320
|
+
return api(request2).installResultFixture(request2.params.id, fixture) ? jsonRes(200, fixture) : adminError3(404, `no order ${request2.params.id} in ${request2.namespace}`);
|
|
3039
3321
|
},
|
|
3040
3322
|
"GET /lab-accounts": (request2) => jsonRes(200, { data: api(request2).labAccounts() }),
|
|
3041
3323
|
"PUT /lab-accounts": (request2) => {
|
|
3042
|
-
const body =
|
|
3324
|
+
const body = isRecord5(request2.body) ? request2.body : {};
|
|
3043
3325
|
const accounts = body.accounts;
|
|
3044
3326
|
const presets = body.presets;
|
|
3045
3327
|
if (accounts === void 0 && presets === void 0 || accounts !== void 0 && accounts !== null && !Array.isArray(accounts) || presets !== void 0 && !Array.isArray(presets)) {
|
|
3046
|
-
return
|
|
3328
|
+
return adminError3(
|
|
3047
3329
|
400,
|
|
3048
3330
|
'body needs "accounts": an array (or null to restore the default), and/or "presets": an array of preset names'
|
|
3049
3331
|
);
|
|
@@ -3055,20 +3337,20 @@ var junctionAdminRoutes = (options) => (runtime) => {
|
|
|
3055
3337
|
return guarded(() => jsonRes(200, { data: api(request2).configureLabAccounts(layout) }));
|
|
3056
3338
|
},
|
|
3057
3339
|
"POST /lab-accounts": (request2) => {
|
|
3058
|
-
if (!
|
|
3340
|
+
if (!isRecord5(request2.body)) return adminError3(400, "expected one lab account object");
|
|
3059
3341
|
const input = request2.body;
|
|
3060
3342
|
return guarded(() => jsonRes(201, api(request2).addLabAccount(input)));
|
|
3061
3343
|
},
|
|
3062
3344
|
"PATCH /lab-accounts/:id": (request2) => {
|
|
3063
|
-
if (!
|
|
3064
|
-
return
|
|
3345
|
+
if (!isRecord5(request2.body))
|
|
3346
|
+
return adminError3(400, "expected an object of fields to change");
|
|
3065
3347
|
const patch = request2.body;
|
|
3066
3348
|
return guarded(() => {
|
|
3067
3349
|
const updated = api(request2).patchLabAccount(request2.params.id, patch);
|
|
3068
|
-
return updated ? jsonRes(200, updated) :
|
|
3350
|
+
return updated ? jsonRes(200, updated) : adminError3(404, `no lab account ${request2.params.id} in ${request2.namespace}`);
|
|
3069
3351
|
});
|
|
3070
3352
|
},
|
|
3071
|
-
"DELETE /lab-accounts/:id": (request2) => api(request2).removeLabAccount(request2.params.id) ? jsonRes(200, { status: "ok" }) :
|
|
3353
|
+
"DELETE /lab-accounts/:id": (request2) => api(request2).removeLabAccount(request2.params.id) ? jsonRes(200, { status: "ok" }) : adminError3(404, `no lab account ${request2.params.id} in ${request2.namespace}`),
|
|
3072
3354
|
"GET /lab-accounts/presets": (request2) => jsonRes(200, {
|
|
3073
3355
|
presets: Object.fromEntries(
|
|
3074
3356
|
Object.keys(LAB_ACCOUNT_PRESETS).map((name) => {
|
|
@@ -3078,14 +3360,14 @@ var junctionAdminRoutes = (options) => (runtime) => {
|
|
|
3078
3360
|
)
|
|
3079
3361
|
}),
|
|
3080
3362
|
"POST /lab-accounts/presets/:name": (request2) => {
|
|
3081
|
-
const body =
|
|
3363
|
+
const body = isRecord5(request2.body) ? request2.body : {};
|
|
3082
3364
|
if (body.id !== void 0 && typeof body.id !== "string") {
|
|
3083
|
-
return
|
|
3365
|
+
return adminError3(400, "id must be a string");
|
|
3084
3366
|
}
|
|
3085
3367
|
const name = request2.params.name;
|
|
3086
3368
|
return guarded(() => {
|
|
3087
3369
|
const record = api(request2).addLabAccountPreset(name, body.id);
|
|
3088
|
-
return record ? jsonRes(201, record) :
|
|
3370
|
+
return record ? jsonRes(201, record) : adminError3(
|
|
3089
3371
|
404,
|
|
3090
3372
|
`no lab-account preset ${name}; one of ${Object.keys(LAB_ACCOUNT_PRESETS).join(", ")}`
|
|
3091
3373
|
);
|
|
@@ -3094,34 +3376,34 @@ var junctionAdminRoutes = (options) => (runtime) => {
|
|
|
3094
3376
|
"GET /team": (request2) => jsonRes(200, { teamId: api(request2).teamId }),
|
|
3095
3377
|
"GET /limits": (request2) => jsonRes(200, { limits: api(request2).limits }),
|
|
3096
3378
|
"PUT /limits": (request2) => {
|
|
3097
|
-
if (!
|
|
3379
|
+
if (!isRecord5(request2.body)) return adminError3(400, "expected an object of limits");
|
|
3098
3380
|
const input = request2.body;
|
|
3099
3381
|
return guarded(() => jsonRes(200, { limits: api(request2).configureLimits(input) }));
|
|
3100
3382
|
},
|
|
3101
3383
|
"GET /identity": (request2) => jsonRes(200, { identity: api(request2).identity }),
|
|
3102
3384
|
"PUT /identity": (request2) => {
|
|
3103
|
-
const mode =
|
|
3385
|
+
const mode = isRecord5(request2.body) ? request2.body.mode : void 0;
|
|
3104
3386
|
if (!IDENTITY_MODES.includes(mode)) {
|
|
3105
|
-
return
|
|
3387
|
+
return adminError3(400, `body needs "mode": one of ${IDENTITY_MODES.join(", ")}`);
|
|
3106
3388
|
}
|
|
3107
3389
|
api(request2).identity = mode;
|
|
3108
3390
|
return jsonRes(200, { identity: mode });
|
|
3109
3391
|
},
|
|
3110
3392
|
"POST /users": (request2) => {
|
|
3111
|
-
if (!
|
|
3393
|
+
if (!isRecord5(request2.body)) return adminError3(400, "expected one user object");
|
|
3112
3394
|
const input = request2.body;
|
|
3113
3395
|
return guarded(() => jsonRes(201, api(request2).insertUsers([input])[0]));
|
|
3114
3396
|
},
|
|
3115
3397
|
"POST /users/bulk": (request2) => {
|
|
3116
|
-
const users =
|
|
3117
|
-
if (!Array.isArray(users)) return
|
|
3398
|
+
const users = isRecord5(request2.body) ? request2.body.users : void 0;
|
|
3399
|
+
if (!Array.isArray(users)) return adminError3(400, 'body needs "users": an array');
|
|
3118
3400
|
return guarded(
|
|
3119
3401
|
() => jsonRes(201, { users: api(request2).insertUsers(users) })
|
|
3120
3402
|
);
|
|
3121
3403
|
},
|
|
3122
|
-
"DELETE /users/:id": (request2) => api(request2).hardDeleteUser(request2.params.id) ? jsonRes(200, { status: "ok" }) :
|
|
3404
|
+
"DELETE /users/:id": (request2) => api(request2).hardDeleteUser(request2.params.id) ? jsonRes(200, { status: "ok" }) : adminError3(404, `no user ${request2.params.id} in ${request2.namespace}`),
|
|
3123
3405
|
"POST /orders": (request2) => {
|
|
3124
|
-
if (!
|
|
3406
|
+
if (!isRecord5(request2.body)) return adminError3(400, "expected one order object");
|
|
3125
3407
|
const { emitWebhooks, ...input } = request2.body;
|
|
3126
3408
|
return guarded(
|
|
3127
3409
|
() => jsonRes(
|
|
@@ -3133,8 +3415,8 @@ var junctionAdminRoutes = (options) => (runtime) => {
|
|
|
3133
3415
|
);
|
|
3134
3416
|
},
|
|
3135
3417
|
"POST /import": (request2) => {
|
|
3136
|
-
if (!
|
|
3137
|
-
return
|
|
3418
|
+
if (!isRecord5(request2.body)) {
|
|
3419
|
+
return adminError3(400, 'expected { "users": [...], "orders": [...] }');
|
|
3138
3420
|
}
|
|
3139
3421
|
const { emitWebhooks, ...fixtures } = request2.body;
|
|
3140
3422
|
return guarded(() => {
|
|
@@ -3149,25 +3431,25 @@ var junctionAdminRoutes = (options) => (runtime) => {
|
|
|
3149
3431
|
geo: api(request2).geoMode
|
|
3150
3432
|
}),
|
|
3151
3433
|
"PUT /geo": (request2) => {
|
|
3152
|
-
const mode =
|
|
3434
|
+
const mode = isRecord5(request2.body) ? request2.body.mode : void 0;
|
|
3153
3435
|
if (mode !== "corpus" && mode !== "synthetic") {
|
|
3154
|
-
return
|
|
3436
|
+
return adminError3(400, 'body needs "mode": "corpus" or "synthetic"');
|
|
3155
3437
|
}
|
|
3156
3438
|
api(request2).geoMode = mode;
|
|
3157
3439
|
return jsonRes(200, { geo: mode });
|
|
3158
3440
|
},
|
|
3159
3441
|
"GET /webhooks/events": (request2) => jsonRes(200, { events: api(request2).webhookEvents() }),
|
|
3160
|
-
"GET /webhooks": (request2) => options.webhooks ? jsonRes(200, { deliveries: options.webhooks.deliveries(request2.namespace) }) :
|
|
3442
|
+
"GET /webhooks": (request2) => options.webhooks ? jsonRes(200, { deliveries: options.webhooks.deliveries(request2.namespace) }) : adminError3(
|
|
3161
3443
|
409,
|
|
3162
3444
|
"webhook delivery is off; serve with --webhook-url and --webhook-secret"
|
|
3163
3445
|
),
|
|
3164
3446
|
"POST /webhooks/:id/replay": async (request2) => {
|
|
3165
|
-
if (!options.webhooks) return
|
|
3447
|
+
if (!options.webhooks) return adminError3(409, "webhook delivery is off");
|
|
3166
3448
|
const delivery = await options.webhooks.replay(request2.params.id);
|
|
3167
|
-
return delivery ? jsonRes(200, delivery) :
|
|
3449
|
+
return delivery ? jsonRes(200, delivery) : adminError3(404, `no message ${request2.params.id}`);
|
|
3168
3450
|
},
|
|
3169
3451
|
"POST /webhooks/flush": async () => {
|
|
3170
|
-
if (!options.webhooks) return
|
|
3452
|
+
if (!options.webhooks) return adminError3(409, "webhook delivery is off");
|
|
3171
3453
|
await options.webhooks.flush();
|
|
3172
3454
|
return jsonRes(200, { status: "ok" });
|
|
3173
3455
|
},
|
|
@@ -3175,12 +3457,12 @@ var junctionAdminRoutes = (options) => (runtime) => {
|
|
|
3175
3457
|
"POST /faults/presets/:name": (request2) => {
|
|
3176
3458
|
const preset = FAULT_PRESETS[request2.params.name];
|
|
3177
3459
|
if (!preset) {
|
|
3178
|
-
return
|
|
3460
|
+
return adminError3(
|
|
3179
3461
|
404,
|
|
3180
3462
|
`no preset ${request2.params.name}; one of ${Object.keys(FAULT_PRESETS).join(", ")}`
|
|
3181
3463
|
);
|
|
3182
3464
|
}
|
|
3183
|
-
const overrides =
|
|
3465
|
+
const overrides = isRecord5(request2.body) ? request2.body : {};
|
|
3184
3466
|
const rule = runtime.faults.add({
|
|
3185
3467
|
namespace: request2.namespace,
|
|
3186
3468
|
...preset,
|
|
@@ -5807,7 +6089,7 @@ var FixtureError = class extends Error {
|
|
|
5807
6089
|
}
|
|
5808
6090
|
status;
|
|
5809
6091
|
};
|
|
5810
|
-
var
|
|
6092
|
+
var isRecord6 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
5811
6093
|
var instant = (value, field, fallback) => {
|
|
5812
6094
|
if (value === void 0) return fallback;
|
|
5813
6095
|
if (typeof value !== "string" || Number.isNaN(Date.parse(value)))
|
|
@@ -5831,7 +6113,7 @@ var DEFAULT_PATIENT_ADDRESS = {
|
|
|
5831
6113
|
country: "US"
|
|
5832
6114
|
};
|
|
5833
6115
|
var buildUser = (state, input, now) => {
|
|
5834
|
-
if (!
|
|
6116
|
+
if (!isRecord6(input)) throw new FixtureError(400, "a user fixture must be an object");
|
|
5835
6117
|
if (typeof input.client_user_id !== "string" || input.client_user_id === "")
|
|
5836
6118
|
throw new FixtureError(400, "client_user_id must be a non-empty string");
|
|
5837
6119
|
if (input.user_id !== void 0 && (typeof input.user_id !== "string" || !isUuid3(input.user_id)))
|
|
@@ -5881,7 +6163,7 @@ var insertOrders = (state, inputs, now, options = {}) => {
|
|
|
5881
6163
|
const prepared = [];
|
|
5882
6164
|
const ids = /* @__PURE__ */ new Set();
|
|
5883
6165
|
for (const input of inputs) {
|
|
5884
|
-
if (!
|
|
6166
|
+
if (!isRecord6(input)) throw new FixtureError(400, "an order fixture must be an object");
|
|
5885
6167
|
if (typeof input.user_id !== "string") throw new FixtureError(400, "user_id is required");
|
|
5886
6168
|
if (typeof input.lab_test_id !== "string")
|
|
5887
6169
|
throw new FixtureError(400, "lab_test_id is required");
|
|
@@ -5951,7 +6233,7 @@ var insertOrders = (state, inputs, now, options = {}) => {
|
|
|
5951
6233
|
return prepared.flatMap(({ order }) => state.orders.get(order.id) ?? []);
|
|
5952
6234
|
};
|
|
5953
6235
|
var importFixtures = (state, fixtures, now, options = {}) => {
|
|
5954
|
-
if (!
|
|
6236
|
+
if (!isRecord6(fixtures))
|
|
5955
6237
|
throw new FixtureError(400, 'fixtures must be { "users": [...], "orders": [...] }');
|
|
5956
6238
|
const { users = [], orders = [] } = fixtures;
|
|
5957
6239
|
if (!Array.isArray(users)) throw new FixtureError(400, "users must be an array");
|
|
@@ -5976,7 +6258,7 @@ var requireUser = (state, userId, context, detail = "User does not exist on this
|
|
|
5976
6258
|
var fixtureErrorMessage = (error) => {
|
|
5977
6259
|
if (error instanceof FixtureError) return { status: error.status, message: error.message };
|
|
5978
6260
|
if (error instanceof HttpError) {
|
|
5979
|
-
const detail =
|
|
6261
|
+
const detail = isRecord6(error.body) ? error.body.detail : error.body;
|
|
5980
6262
|
return {
|
|
5981
6263
|
status: error.status,
|
|
5982
6264
|
message: typeof detail === "string" ? detail : JSON.stringify(detail)
|
|
@@ -8846,4 +9128,4 @@ export {
|
|
|
8846
9128
|
JunctionAPI,
|
|
8847
9129
|
LabAccountConflict
|
|
8848
9130
|
};
|
|
8849
|
-
//# sourceMappingURL=chunk-
|
|
9131
|
+
//# sourceMappingURL=chunk-ICINEQ7D.js.map
|