@crvouga/mockingbird-service-rxvortex 1.5.0 → 1.6.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 +10 -0
- package/dist/{chunk-KQEUUCLU.js → chunk-7IB3GSYU.js} +1424 -620
- package/dist/chunk-7IB3GSYU.js.map +7 -0
- package/dist/{chunk-VHD526HH.js → chunk-TPY3O7YZ.js} +2 -2
- package/dist/cli.js +2 -2
- package/dist/index.js +1 -1
- package/dist/server.js +2 -2
- package/package.json +3 -3
- package/dist/chunk-KQEUUCLU.js.map +0 -7
- /package/dist/{chunk-VHD526HH.js.map → chunk-TPY3O7YZ.js.map} +0 -0
|
@@ -540,10 +540,10 @@ var OpenAPIReferenceError = class extends Error {
|
|
|
540
540
|
};
|
|
541
541
|
var unescapePointer = (segment) => segment.replace(/~1/g, "/").replace(/~0/g, "~");
|
|
542
542
|
var isReference = (value) => typeof value === "object" && value !== null && typeof value.$ref === "string";
|
|
543
|
-
var resolveRef = (
|
|
543
|
+
var resolveRef = (document3, ref) => {
|
|
544
544
|
if (!ref.startsWith("#/"))
|
|
545
545
|
throw new OpenAPIReferenceError(ref);
|
|
546
|
-
let cursor =
|
|
546
|
+
let cursor = document3;
|
|
547
547
|
for (const raw of ref.slice(2).split("/")) {
|
|
548
548
|
const segment = unescapePointer(raw);
|
|
549
549
|
if (typeof cursor !== "object" || cursor === null || !(segment in cursor)) {
|
|
@@ -555,14 +555,14 @@ var resolveRef = (document2, ref) => {
|
|
|
555
555
|
throw new OpenAPIReferenceError(ref);
|
|
556
556
|
return cursor;
|
|
557
557
|
};
|
|
558
|
-
var deref = (
|
|
558
|
+
var deref = (document3, value) => {
|
|
559
559
|
let current = value;
|
|
560
560
|
const seen = /* @__PURE__ */ new Set();
|
|
561
561
|
while (isReference(current)) {
|
|
562
562
|
if (seen.has(current.$ref))
|
|
563
563
|
throw new OpenAPIReferenceError(`${current.$ref} (cycle)`);
|
|
564
564
|
seen.add(current.$ref);
|
|
565
|
-
current = resolveRef(
|
|
565
|
+
current = resolveRef(document3, current.$ref);
|
|
566
566
|
}
|
|
567
567
|
return current;
|
|
568
568
|
};
|
|
@@ -580,36 +580,36 @@ var HTTP_METHODS = [
|
|
|
580
580
|
];
|
|
581
581
|
|
|
582
582
|
// ../../openapi/core/dist/document.js
|
|
583
|
-
var mergeParameters = (
|
|
583
|
+
var mergeParameters = (document3, item, own) => {
|
|
584
584
|
const merged = /* @__PURE__ */ new Map();
|
|
585
585
|
for (const raw of item.parameters ?? []) {
|
|
586
|
-
const parameter = deref(
|
|
586
|
+
const parameter = deref(document3, raw);
|
|
587
587
|
merged.set(`${parameter.in}:${parameter.name}`, parameter);
|
|
588
588
|
}
|
|
589
589
|
for (const raw of own ?? []) {
|
|
590
|
-
const parameter = deref(
|
|
590
|
+
const parameter = deref(document3, raw);
|
|
591
591
|
merged.set(`${parameter.in}:${parameter.name}`, parameter);
|
|
592
592
|
}
|
|
593
593
|
return [...merged.values()];
|
|
594
594
|
};
|
|
595
|
-
var listOperations = (
|
|
595
|
+
var listOperations = (document3) => {
|
|
596
596
|
const operations = [];
|
|
597
|
-
for (const [path, item] of Object.entries(
|
|
597
|
+
for (const [path, item] of Object.entries(document3.paths)) {
|
|
598
598
|
for (const method of HTTP_METHODS) {
|
|
599
599
|
const operation = item[method];
|
|
600
600
|
if (operation?.operationId === void 0)
|
|
601
601
|
continue;
|
|
602
602
|
const responses = {};
|
|
603
603
|
for (const [status, response] of Object.entries(operation.responses)) {
|
|
604
|
-
responses[status] = deref(
|
|
604
|
+
responses[status] = deref(document3, response);
|
|
605
605
|
}
|
|
606
606
|
operations.push({
|
|
607
607
|
operationId: operation.operationId,
|
|
608
608
|
method,
|
|
609
609
|
path,
|
|
610
610
|
operation,
|
|
611
|
-
parameters: mergeParameters(
|
|
612
|
-
requestBody: operation.requestBody === void 0 ? void 0 : deref(
|
|
611
|
+
parameters: mergeParameters(document3, item, operation.parameters),
|
|
612
|
+
requestBody: operation.requestBody === void 0 ? void 0 : deref(document3, operation.requestBody),
|
|
613
613
|
responses
|
|
614
614
|
});
|
|
615
615
|
}
|
|
@@ -618,7 +618,7 @@ var listOperations = (document2) => {
|
|
|
618
618
|
};
|
|
619
619
|
|
|
620
620
|
// ../../openapi/core/dist/schema.js
|
|
621
|
-
var resolveSchema = (
|
|
621
|
+
var resolveSchema = (document3, schema) => {
|
|
622
622
|
let current = schema;
|
|
623
623
|
const seen = /* @__PURE__ */ new Set();
|
|
624
624
|
while (typeof current.$ref === "string") {
|
|
@@ -627,7 +627,7 @@ var resolveSchema = (document2, schema) => {
|
|
|
627
627
|
break;
|
|
628
628
|
seen.add(ref);
|
|
629
629
|
const { $ref: _ignored, ...siblings } = current;
|
|
630
|
-
const target = resolveRef(
|
|
630
|
+
const target = resolveRef(document3, ref);
|
|
631
631
|
current = { ...target, ...siblings };
|
|
632
632
|
}
|
|
633
633
|
if (current.nullable === true) {
|
|
@@ -698,9 +698,9 @@ var FORMAT_PATTERNS = {
|
|
|
698
698
|
ipv4: /^(25[0-5]|2[0-4]\d|1?\d?\d)(\.(25[0-5]|2[0-4]\d|1?\d?\d)){3}$/
|
|
699
699
|
};
|
|
700
700
|
var graphemeLength = (value) => [...value].length;
|
|
701
|
-
var validateValue = (
|
|
701
|
+
var validateValue = (document3, schema, value, path = []) => {
|
|
702
702
|
const errors = [];
|
|
703
|
-
const s = resolveSchema(
|
|
703
|
+
const s = resolveSchema(document3, schema);
|
|
704
704
|
const fail2 = (message) => errors.push({ path, message });
|
|
705
705
|
const actual = jsonTypeOf(value);
|
|
706
706
|
if (actual === "undefined") {
|
|
@@ -762,7 +762,7 @@ var validateValue = (document2, schema, value, path = []) => {
|
|
|
762
762
|
value.forEach((item, i) => {
|
|
763
763
|
const itemSchema = s.prefixItems?.[i] ?? s.items;
|
|
764
764
|
if (itemSchema)
|
|
765
|
-
errors.push(...validateValue(
|
|
765
|
+
errors.push(...validateValue(document3, itemSchema, item, [...path, i]));
|
|
766
766
|
});
|
|
767
767
|
}
|
|
768
768
|
if (actual === "object") {
|
|
@@ -778,16 +778,16 @@ var validateValue = (document2, schema, value, path = []) => {
|
|
|
778
778
|
for (const key of keys) {
|
|
779
779
|
const property = s.properties?.[key];
|
|
780
780
|
if (property) {
|
|
781
|
-
errors.push(...validateValue(
|
|
781
|
+
errors.push(...validateValue(document3, property, record2[key], [...path, key]));
|
|
782
782
|
continue;
|
|
783
783
|
}
|
|
784
784
|
if (s.additionalProperties === false)
|
|
785
785
|
fail2(`unexpected property ${key}`);
|
|
786
786
|
else if (typeof s.additionalProperties === "object") {
|
|
787
|
-
errors.push(...validateValue(
|
|
787
|
+
errors.push(...validateValue(document3, s.additionalProperties, record2[key], [...path, key]));
|
|
788
788
|
}
|
|
789
789
|
if (s.propertyNames) {
|
|
790
|
-
const nameErrors = validateValue(
|
|
790
|
+
const nameErrors = validateValue(document3, s.propertyNames, key, [...path, key]);
|
|
791
791
|
if (nameErrors.length > 0)
|
|
792
792
|
fail2(`property name ${key} is invalid: ${nameErrors[0]?.message}`);
|
|
793
793
|
}
|
|
@@ -795,15 +795,15 @@ var validateValue = (document2, schema, value, path = []) => {
|
|
|
795
795
|
}
|
|
796
796
|
if (s.allOf)
|
|
797
797
|
for (const branch of s.allOf)
|
|
798
|
-
errors.push(...validateValue(
|
|
799
|
-
if (s.anyOf && !s.anyOf.some((branch) => validateValue(
|
|
798
|
+
errors.push(...validateValue(document3, branch, value, path));
|
|
799
|
+
if (s.anyOf && !s.anyOf.some((branch) => validateValue(document3, branch, value).length === 0))
|
|
800
800
|
fail2("matches no anyOf branch");
|
|
801
801
|
if (s.oneOf) {
|
|
802
|
-
const matches2 = s.oneOf.filter((branch) => validateValue(
|
|
802
|
+
const matches2 = s.oneOf.filter((branch) => validateValue(document3, branch, value).length === 0).length;
|
|
803
803
|
if (matches2 !== 1)
|
|
804
804
|
fail2(`matches ${matches2} oneOf branches, expected exactly 1`);
|
|
805
805
|
}
|
|
806
|
-
if (s.not && validateValue(
|
|
806
|
+
if (s.not && validateValue(document3, s.not, value).length === 0)
|
|
807
807
|
fail2("matches forbidden `not` schema");
|
|
808
808
|
return errors;
|
|
809
809
|
};
|
|
@@ -1336,6 +1336,1232 @@ var clearNamespace = (sqlite, namespace) => {
|
|
|
1336
1336
|
});
|
|
1337
1337
|
};
|
|
1338
1338
|
|
|
1339
|
+
// ../../ui/dist/reset.js
|
|
1340
|
+
var CSS_RESET = `*,
|
|
1341
|
+
*::before,
|
|
1342
|
+
*::after {
|
|
1343
|
+
box-sizing: border-box;
|
|
1344
|
+
margin: 0;
|
|
1345
|
+
padding: 0;
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1348
|
+
* {
|
|
1349
|
+
scrollbar-width: thin;
|
|
1350
|
+
scrollbar-color: color-mix(in srgb, CanvasText 35%, Canvas) Canvas;
|
|
1351
|
+
}
|
|
1352
|
+
|
|
1353
|
+
*::-webkit-scrollbar {
|
|
1354
|
+
width: 12px;
|
|
1355
|
+
height: 12px;
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1358
|
+
*::-webkit-scrollbar-track,
|
|
1359
|
+
*::-webkit-scrollbar-corner {
|
|
1360
|
+
background: Canvas;
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1363
|
+
*::-webkit-scrollbar-thumb {
|
|
1364
|
+
background: color-mix(in srgb, CanvasText 35%, Canvas);
|
|
1365
|
+
border: 3px solid Canvas;
|
|
1366
|
+
border-radius: 999px;
|
|
1367
|
+
}
|
|
1368
|
+
|
|
1369
|
+
html {
|
|
1370
|
+
hanging-punctuation: first allow-end last;
|
|
1371
|
+
-webkit-text-size-adjust: none;
|
|
1372
|
+
text-size-adjust: none;
|
|
1373
|
+
tab-size: 4;
|
|
1374
|
+
font-family:
|
|
1375
|
+
ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI Variable", "Segoe UI",
|
|
1376
|
+
"Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
|
|
1377
|
+
font-size: 16px;
|
|
1378
|
+
line-height: 1.5;
|
|
1379
|
+
-webkit-font-smoothing: antialiased;
|
|
1380
|
+
-moz-osx-font-smoothing: grayscale;
|
|
1381
|
+
text-rendering: optimizeLegibility;
|
|
1382
|
+
-webkit-tap-highlight-color: transparent;
|
|
1383
|
+
scroll-behavior: smooth;
|
|
1384
|
+
background: Canvas;
|
|
1385
|
+
color: CanvasText;
|
|
1386
|
+
}
|
|
1387
|
+
|
|
1388
|
+
body {
|
|
1389
|
+
min-height: 100svh;
|
|
1390
|
+
line-height: 1.5;
|
|
1391
|
+
background: Canvas;
|
|
1392
|
+
color: CanvasText;
|
|
1393
|
+
}
|
|
1394
|
+
|
|
1395
|
+
h1 {
|
|
1396
|
+
font-size: 2em;
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
h2 {
|
|
1400
|
+
font-size: 1.5em;
|
|
1401
|
+
}
|
|
1402
|
+
|
|
1403
|
+
h3 {
|
|
1404
|
+
font-size: 1.17em;
|
|
1405
|
+
}
|
|
1406
|
+
|
|
1407
|
+
h4 {
|
|
1408
|
+
font-size: 1em;
|
|
1409
|
+
}
|
|
1410
|
+
|
|
1411
|
+
h5 {
|
|
1412
|
+
font-size: 0.83em;
|
|
1413
|
+
}
|
|
1414
|
+
|
|
1415
|
+
h6 {
|
|
1416
|
+
font-size: 0.67em;
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1419
|
+
h1,
|
|
1420
|
+
h2,
|
|
1421
|
+
h3,
|
|
1422
|
+
h4,
|
|
1423
|
+
h5,
|
|
1424
|
+
h6 {
|
|
1425
|
+
font-weight: 700;
|
|
1426
|
+
line-height: 1.2;
|
|
1427
|
+
text-wrap: balance;
|
|
1428
|
+
overflow-wrap: break-word;
|
|
1429
|
+
}
|
|
1430
|
+
|
|
1431
|
+
p,
|
|
1432
|
+
li,
|
|
1433
|
+
figcaption,
|
|
1434
|
+
dd,
|
|
1435
|
+
dt,
|
|
1436
|
+
blockquote {
|
|
1437
|
+
overflow-wrap: break-word;
|
|
1438
|
+
}
|
|
1439
|
+
|
|
1440
|
+
p,
|
|
1441
|
+
li,
|
|
1442
|
+
figcaption,
|
|
1443
|
+
blockquote {
|
|
1444
|
+
text-wrap: pretty;
|
|
1445
|
+
}
|
|
1446
|
+
|
|
1447
|
+
ul,
|
|
1448
|
+
ol,
|
|
1449
|
+
menu {
|
|
1450
|
+
list-style: none;
|
|
1451
|
+
}
|
|
1452
|
+
|
|
1453
|
+
a {
|
|
1454
|
+
color: inherit;
|
|
1455
|
+
text-decoration: underline;
|
|
1456
|
+
text-decoration-thickness: 1px;
|
|
1457
|
+
text-underline-offset: 0.15em;
|
|
1458
|
+
text-decoration-skip-ink: auto;
|
|
1459
|
+
}
|
|
1460
|
+
|
|
1461
|
+
b,
|
|
1462
|
+
strong {
|
|
1463
|
+
font-weight: 700;
|
|
1464
|
+
}
|
|
1465
|
+
|
|
1466
|
+
small {
|
|
1467
|
+
font-size: 0.875em;
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1470
|
+
code,
|
|
1471
|
+
kbd,
|
|
1472
|
+
samp,
|
|
1473
|
+
pre {
|
|
1474
|
+
font-family: ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, monospace;
|
|
1475
|
+
font-size: 1em;
|
|
1476
|
+
}
|
|
1477
|
+
|
|
1478
|
+
sub,
|
|
1479
|
+
sup {
|
|
1480
|
+
font-size: 75%;
|
|
1481
|
+
line-height: 0;
|
|
1482
|
+
position: relative;
|
|
1483
|
+
vertical-align: baseline;
|
|
1484
|
+
}
|
|
1485
|
+
|
|
1486
|
+
sub {
|
|
1487
|
+
bottom: -0.25em;
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1490
|
+
sup {
|
|
1491
|
+
top: -0.5em;
|
|
1492
|
+
}
|
|
1493
|
+
|
|
1494
|
+
abbr[title] {
|
|
1495
|
+
text-decoration: underline dotted;
|
|
1496
|
+
text-underline-offset: 0.15em;
|
|
1497
|
+
}
|
|
1498
|
+
|
|
1499
|
+
mark {
|
|
1500
|
+
background: Mark;
|
|
1501
|
+
color: MarkText;
|
|
1502
|
+
}
|
|
1503
|
+
|
|
1504
|
+
hr {
|
|
1505
|
+
height: 0;
|
|
1506
|
+
border: 0;
|
|
1507
|
+
border-top: 1px solid color-mix(in srgb, CanvasText 20%, transparent);
|
|
1508
|
+
color: inherit;
|
|
1509
|
+
}
|
|
1510
|
+
|
|
1511
|
+
img,
|
|
1512
|
+
picture,
|
|
1513
|
+
video,
|
|
1514
|
+
canvas {
|
|
1515
|
+
display: block;
|
|
1516
|
+
max-width: 100%;
|
|
1517
|
+
}
|
|
1518
|
+
|
|
1519
|
+
img,
|
|
1520
|
+
video {
|
|
1521
|
+
height: auto;
|
|
1522
|
+
}
|
|
1523
|
+
|
|
1524
|
+
svg {
|
|
1525
|
+
overflow: hidden;
|
|
1526
|
+
}
|
|
1527
|
+
|
|
1528
|
+
iframe {
|
|
1529
|
+
border: 0;
|
|
1530
|
+
max-width: 100%;
|
|
1531
|
+
}
|
|
1532
|
+
|
|
1533
|
+
table {
|
|
1534
|
+
border-collapse: collapse;
|
|
1535
|
+
border-spacing: 0;
|
|
1536
|
+
}
|
|
1537
|
+
|
|
1538
|
+
fieldset {
|
|
1539
|
+
border: 0;
|
|
1540
|
+
min-inline-size: 0;
|
|
1541
|
+
}
|
|
1542
|
+
|
|
1543
|
+
legend {
|
|
1544
|
+
padding: 0;
|
|
1545
|
+
}
|
|
1546
|
+
|
|
1547
|
+
button,
|
|
1548
|
+
input,
|
|
1549
|
+
select,
|
|
1550
|
+
textarea,
|
|
1551
|
+
optgroup {
|
|
1552
|
+
font: inherit;
|
|
1553
|
+
letter-spacing: inherit;
|
|
1554
|
+
color: inherit;
|
|
1555
|
+
margin: 0;
|
|
1556
|
+
vertical-align: middle;
|
|
1557
|
+
}
|
|
1558
|
+
|
|
1559
|
+
button,
|
|
1560
|
+
input[type="button"],
|
|
1561
|
+
input[type="submit"],
|
|
1562
|
+
input[type="reset"] {
|
|
1563
|
+
appearance: none;
|
|
1564
|
+
background: none;
|
|
1565
|
+
border: 0;
|
|
1566
|
+
padding: 0;
|
|
1567
|
+
cursor: pointer;
|
|
1568
|
+
text-align: center;
|
|
1569
|
+
}
|
|
1570
|
+
|
|
1571
|
+
input,
|
|
1572
|
+
textarea,
|
|
1573
|
+
select {
|
|
1574
|
+
background-color: Canvas;
|
|
1575
|
+
border: 1px solid color-mix(in srgb, CanvasText 28%, transparent);
|
|
1576
|
+
border-radius: 0;
|
|
1577
|
+
padding: 0.5em 0.65em;
|
|
1578
|
+
}
|
|
1579
|
+
|
|
1580
|
+
input[type="checkbox"],
|
|
1581
|
+
input[type="radio"],
|
|
1582
|
+
input[type="range"],
|
|
1583
|
+
input[type="file"],
|
|
1584
|
+
input[type="color"],
|
|
1585
|
+
input[type="button"],
|
|
1586
|
+
input[type="submit"],
|
|
1587
|
+
input[type="reset"],
|
|
1588
|
+
input[type="image"],
|
|
1589
|
+
input[type="hidden"] {
|
|
1590
|
+
background: none;
|
|
1591
|
+
border: 0;
|
|
1592
|
+
padding: 0;
|
|
1593
|
+
border-radius: 0;
|
|
1594
|
+
}
|
|
1595
|
+
|
|
1596
|
+
input[type="checkbox"],
|
|
1597
|
+
input[type="radio"] {
|
|
1598
|
+
accent-color: CanvasText;
|
|
1599
|
+
}
|
|
1600
|
+
|
|
1601
|
+
input[type="search"] {
|
|
1602
|
+
appearance: none;
|
|
1603
|
+
background-color: Canvas;
|
|
1604
|
+
border: 1px solid color-mix(in srgb, CanvasText 28%, transparent);
|
|
1605
|
+
}
|
|
1606
|
+
|
|
1607
|
+
input[type="search"]::-webkit-search-decoration,
|
|
1608
|
+
input[type="search"]::-webkit-search-cancel-button,
|
|
1609
|
+
input[type="search"]::-webkit-search-results-button,
|
|
1610
|
+
input[type="search"]::-webkit-search-results-decoration {
|
|
1611
|
+
-webkit-appearance: none;
|
|
1612
|
+
display: none;
|
|
1613
|
+
}
|
|
1614
|
+
|
|
1615
|
+
input[type="number"] {
|
|
1616
|
+
appearance: textfield;
|
|
1617
|
+
background-color: Canvas;
|
|
1618
|
+
border: 1px solid color-mix(in srgb, CanvasText 28%, transparent);
|
|
1619
|
+
}
|
|
1620
|
+
|
|
1621
|
+
input[type="number"]::-webkit-inner-spin-button,
|
|
1622
|
+
input[type="number"]::-webkit-outer-spin-button {
|
|
1623
|
+
-webkit-appearance: none;
|
|
1624
|
+
margin: 0;
|
|
1625
|
+
}
|
|
1626
|
+
|
|
1627
|
+
textarea {
|
|
1628
|
+
resize: vertical;
|
|
1629
|
+
}
|
|
1630
|
+
|
|
1631
|
+
::placeholder {
|
|
1632
|
+
opacity: 1;
|
|
1633
|
+
color: color-mix(in srgb, CanvasText 45%, transparent);
|
|
1634
|
+
}
|
|
1635
|
+
|
|
1636
|
+
:focus-visible {
|
|
1637
|
+
outline: 2px solid CanvasText;
|
|
1638
|
+
outline-offset: 2px;
|
|
1639
|
+
}
|
|
1640
|
+
|
|
1641
|
+
:focus:not(:focus-visible) {
|
|
1642
|
+
outline: none;
|
|
1643
|
+
}
|
|
1644
|
+
|
|
1645
|
+
:disabled,
|
|
1646
|
+
[disabled] {
|
|
1647
|
+
cursor: default;
|
|
1648
|
+
opacity: 0.6;
|
|
1649
|
+
}
|
|
1650
|
+
|
|
1651
|
+
label,
|
|
1652
|
+
summary,
|
|
1653
|
+
select,
|
|
1654
|
+
[role="button"] {
|
|
1655
|
+
cursor: pointer;
|
|
1656
|
+
}
|
|
1657
|
+
|
|
1658
|
+
summary {
|
|
1659
|
+
display: list-item;
|
|
1660
|
+
}
|
|
1661
|
+
|
|
1662
|
+
details {
|
|
1663
|
+
display: block;
|
|
1664
|
+
}
|
|
1665
|
+
|
|
1666
|
+
dialog {
|
|
1667
|
+
margin: auto;
|
|
1668
|
+
padding: 1rem;
|
|
1669
|
+
border: 1px solid color-mix(in srgb, CanvasText 20%, transparent);
|
|
1670
|
+
background: Canvas;
|
|
1671
|
+
color: CanvasText;
|
|
1672
|
+
max-width: calc(100vw - 2rem);
|
|
1673
|
+
max-height: calc(100svh - 2rem);
|
|
1674
|
+
}
|
|
1675
|
+
|
|
1676
|
+
dialog:not([open]) {
|
|
1677
|
+
display: none;
|
|
1678
|
+
}
|
|
1679
|
+
|
|
1680
|
+
::backdrop {
|
|
1681
|
+
background: color-mix(in srgb, CanvasText 40%, transparent);
|
|
1682
|
+
}
|
|
1683
|
+
|
|
1684
|
+
[hidden] {
|
|
1685
|
+
display: none !important;
|
|
1686
|
+
}
|
|
1687
|
+
|
|
1688
|
+
::selection {
|
|
1689
|
+
background: Highlight;
|
|
1690
|
+
color: HighlightText;
|
|
1691
|
+
}
|
|
1692
|
+
|
|
1693
|
+
@media (prefers-reduced-motion: reduce) {
|
|
1694
|
+
*,
|
|
1695
|
+
*::before,
|
|
1696
|
+
*::after {
|
|
1697
|
+
animation-duration: 0.01ms !important;
|
|
1698
|
+
animation-iteration-count: 1 !important;
|
|
1699
|
+
transition-duration: 0.01ms !important;
|
|
1700
|
+
scroll-behavior: auto !important;
|
|
1701
|
+
}
|
|
1702
|
+
}
|
|
1703
|
+
|
|
1704
|
+
@media (pointer: coarse) {
|
|
1705
|
+
input,
|
|
1706
|
+
select,
|
|
1707
|
+
textarea {
|
|
1708
|
+
font-size: max(16px, 1em);
|
|
1709
|
+
}
|
|
1710
|
+
}`;
|
|
1711
|
+
|
|
1712
|
+
// ../core/dist/admin-client.js
|
|
1713
|
+
function isRecord2(value) {
|
|
1714
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1715
|
+
}
|
|
1716
|
+
function mountAdminBrand(doc, host, service, catalog) {
|
|
1717
|
+
const clip = (value, max) => {
|
|
1718
|
+
if (typeof value !== "string")
|
|
1719
|
+
return null;
|
|
1720
|
+
const trimmed = value.trim();
|
|
1721
|
+
if (trimmed === "")
|
|
1722
|
+
return null;
|
|
1723
|
+
return trimmed.length > max ? trimmed.slice(0, max) : trimmed;
|
|
1724
|
+
};
|
|
1725
|
+
const httpUrl = (value) => {
|
|
1726
|
+
if (typeof value !== "string" || value.length > 2e3)
|
|
1727
|
+
return null;
|
|
1728
|
+
try {
|
|
1729
|
+
const url = new URL(value);
|
|
1730
|
+
if (url.protocol !== "https:" && url.protocol !== "http:")
|
|
1731
|
+
return null;
|
|
1732
|
+
return url.href;
|
|
1733
|
+
} catch {
|
|
1734
|
+
return null;
|
|
1735
|
+
}
|
|
1736
|
+
};
|
|
1737
|
+
const hexColor = (value) => {
|
|
1738
|
+
if (typeof value !== "string")
|
|
1739
|
+
return null;
|
|
1740
|
+
const raw = value.trim();
|
|
1741
|
+
return /^#[0-9a-fA-F]{3,8}$/.test(raw) ? raw : null;
|
|
1742
|
+
};
|
|
1743
|
+
const hostname = (value) => {
|
|
1744
|
+
try {
|
|
1745
|
+
return new URL(value).hostname.replace(/^www\./, "");
|
|
1746
|
+
} catch {
|
|
1747
|
+
return value;
|
|
1748
|
+
}
|
|
1749
|
+
};
|
|
1750
|
+
host.replaceChildren();
|
|
1751
|
+
host.hidden = true;
|
|
1752
|
+
if (!isRecord2(catalog) || !Object.hasOwn(catalog, service))
|
|
1753
|
+
return;
|
|
1754
|
+
const row = catalog[service];
|
|
1755
|
+
if (!isRecord2(row))
|
|
1756
|
+
return;
|
|
1757
|
+
const vendor = clip(row.vendor, 80);
|
|
1758
|
+
const website = httpUrl(row.website);
|
|
1759
|
+
const guide = httpUrl(row.guide);
|
|
1760
|
+
const docs = httpUrl(row.docs);
|
|
1761
|
+
const logo = httpUrl(row.logo);
|
|
1762
|
+
const color = hexColor(row.color);
|
|
1763
|
+
const description = clip(row.description, 280);
|
|
1764
|
+
if (vendor === null || website === null && guide === null && docs === null)
|
|
1765
|
+
return;
|
|
1766
|
+
const add = (node) => {
|
|
1767
|
+
host.append(node);
|
|
1768
|
+
return node;
|
|
1769
|
+
};
|
|
1770
|
+
if (logo !== null) {
|
|
1771
|
+
const img = doc.createElement("img");
|
|
1772
|
+
img.alt = "";
|
|
1773
|
+
img.src = logo;
|
|
1774
|
+
img.width = 16;
|
|
1775
|
+
img.height = 16;
|
|
1776
|
+
img.decoding = "async";
|
|
1777
|
+
img.referrerPolicy = "no-referrer";
|
|
1778
|
+
if (color !== null)
|
|
1779
|
+
img.style.borderColor = color;
|
|
1780
|
+
add(img);
|
|
1781
|
+
}
|
|
1782
|
+
const name = doc.createElement("span");
|
|
1783
|
+
name.className = "vendor-name";
|
|
1784
|
+
name.textContent = vendor;
|
|
1785
|
+
if (description !== null)
|
|
1786
|
+
name.title = description;
|
|
1787
|
+
add(name);
|
|
1788
|
+
const sep = () => {
|
|
1789
|
+
const node = doc.createElement("span");
|
|
1790
|
+
node.className = "vendor-sep";
|
|
1791
|
+
node.setAttribute("aria-hidden", "true");
|
|
1792
|
+
node.textContent = "\xB7";
|
|
1793
|
+
return node;
|
|
1794
|
+
};
|
|
1795
|
+
const link = (href, label, title) => {
|
|
1796
|
+
const node = doc.createElement("a");
|
|
1797
|
+
node.className = "vendor-link";
|
|
1798
|
+
node.href = href;
|
|
1799
|
+
node.target = "_blank";
|
|
1800
|
+
node.rel = "noopener noreferrer";
|
|
1801
|
+
node.referrerPolicy = "no-referrer";
|
|
1802
|
+
node.textContent = label;
|
|
1803
|
+
node.title = title;
|
|
1804
|
+
return node;
|
|
1805
|
+
};
|
|
1806
|
+
if (website !== null) {
|
|
1807
|
+
add(sep());
|
|
1808
|
+
add(link(website, hostname(website), `${vendor} website`));
|
|
1809
|
+
}
|
|
1810
|
+
if (guide !== null) {
|
|
1811
|
+
add(sep());
|
|
1812
|
+
add(link(guide, "Docs", `Docs for ${vendor}`));
|
|
1813
|
+
}
|
|
1814
|
+
if (docs !== null) {
|
|
1815
|
+
add(sep());
|
|
1816
|
+
add(link(docs, "API", `${vendor} API reference`));
|
|
1817
|
+
}
|
|
1818
|
+
host.removeAttribute("hidden");
|
|
1819
|
+
host.hidden = false;
|
|
1820
|
+
}
|
|
1821
|
+
function startAdminBrand(env) {
|
|
1822
|
+
const requested = env.params.get("brands");
|
|
1823
|
+
let brandsUrl = env.brandsUrl;
|
|
1824
|
+
if (requested) {
|
|
1825
|
+
try {
|
|
1826
|
+
const url = new URL(requested, env.location.href);
|
|
1827
|
+
const local = url.hostname === "localhost" || url.hostname === "127.0.0.1" || url.hostname === "::1" || url.hostname === "[::1]" || url.origin === env.location.origin;
|
|
1828
|
+
if ((url.protocol === "https:" || url.protocol === "http:") && local)
|
|
1829
|
+
brandsUrl = url.href;
|
|
1830
|
+
} catch {
|
|
1831
|
+
brandsUrl = env.brandsUrl;
|
|
1832
|
+
}
|
|
1833
|
+
}
|
|
1834
|
+
const vendorHost = env.$("vendor");
|
|
1835
|
+
const brandRoot = env.document.querySelector("[data-mockingbird-admin]");
|
|
1836
|
+
const brandService = brandRoot?.getAttribute("data-service") || "";
|
|
1837
|
+
if (vendorHost === null)
|
|
1838
|
+
return;
|
|
1839
|
+
env.fetch(brandsUrl, {
|
|
1840
|
+
headers: { accept: "application/json" },
|
|
1841
|
+
signal: AbortSignal.timeout(5e3)
|
|
1842
|
+
}).then(async (response) => {
|
|
1843
|
+
if (!response.ok)
|
|
1844
|
+
return null;
|
|
1845
|
+
const catalog = await response.json();
|
|
1846
|
+
return catalog;
|
|
1847
|
+
}).then((catalog) => {
|
|
1848
|
+
mountAdminBrand(env.document, vendorHost, brandService, catalog);
|
|
1849
|
+
}).catch(() => {
|
|
1850
|
+
});
|
|
1851
|
+
}
|
|
1852
|
+
function faultPresetList(value) {
|
|
1853
|
+
if (!isRecord2(value))
|
|
1854
|
+
throw new Error("Fault presets response was not an object");
|
|
1855
|
+
if (!Object.hasOwn(value, "presets") || value.presets === void 0 || value.presets === null) {
|
|
1856
|
+
return { presets: [] };
|
|
1857
|
+
}
|
|
1858
|
+
if (!Array.isArray(value.presets))
|
|
1859
|
+
throw new Error("Fault presets response was not a list");
|
|
1860
|
+
return {
|
|
1861
|
+
presets: value.presets.flatMap((item) => {
|
|
1862
|
+
if (!isRecord2(item) || typeof item.name !== "string")
|
|
1863
|
+
return [];
|
|
1864
|
+
return [{ name: item.name }];
|
|
1865
|
+
})
|
|
1866
|
+
};
|
|
1867
|
+
}
|
|
1868
|
+
function bootAdmin(config) {
|
|
1869
|
+
const fieldKinds = ["string", "number", "boolean", "null", "object", "array", "unknown"];
|
|
1870
|
+
const sources = ["declared", "inferred", "mixed"];
|
|
1871
|
+
const isFieldKind = (value) => typeof value === "string" && fieldKinds.some((kind) => kind === value);
|
|
1872
|
+
const isSource = (value) => typeof value === "string" && sources.some((source) => source === value);
|
|
1873
|
+
const readJson2 = (text) => {
|
|
1874
|
+
const value = JSON.parse(text);
|
|
1875
|
+
return value;
|
|
1876
|
+
};
|
|
1877
|
+
const esc = (value) => String(value ?? "").replace(/[&<>"']/g, (char) => {
|
|
1878
|
+
switch (char) {
|
|
1879
|
+
case "&":
|
|
1880
|
+
return "&";
|
|
1881
|
+
case "<":
|
|
1882
|
+
return "<";
|
|
1883
|
+
case ">":
|
|
1884
|
+
return ">";
|
|
1885
|
+
case '"':
|
|
1886
|
+
return """;
|
|
1887
|
+
default:
|
|
1888
|
+
return "'";
|
|
1889
|
+
}
|
|
1890
|
+
});
|
|
1891
|
+
const el = (id, kind) => {
|
|
1892
|
+
const node = document.getElementById(id);
|
|
1893
|
+
if (!(node instanceof kind))
|
|
1894
|
+
throw new Error(`admin shell is missing #${id}`);
|
|
1895
|
+
return node;
|
|
1896
|
+
};
|
|
1897
|
+
const errorMessage = (data, fallback) => {
|
|
1898
|
+
if (!isRecord2(data))
|
|
1899
|
+
return fallback;
|
|
1900
|
+
const error = data.error;
|
|
1901
|
+
if (!isRecord2(error) || typeof error.message !== "string" || error.message === "") {
|
|
1902
|
+
return fallback;
|
|
1903
|
+
}
|
|
1904
|
+
return error.message;
|
|
1905
|
+
};
|
|
1906
|
+
const requireList = (value, label) => {
|
|
1907
|
+
if (value === void 0 || value === null)
|
|
1908
|
+
return [];
|
|
1909
|
+
if (!Array.isArray(value))
|
|
1910
|
+
throw new Error(`${label} was not a list`);
|
|
1911
|
+
return value;
|
|
1912
|
+
};
|
|
1913
|
+
const state = {
|
|
1914
|
+
namespace: "default",
|
|
1915
|
+
key: sessionStorage.getItem("mockingbird-admin-key") || "",
|
|
1916
|
+
view: "overview",
|
|
1917
|
+
collection: "",
|
|
1918
|
+
shape: null,
|
|
1919
|
+
route: null,
|
|
1920
|
+
editing: null
|
|
1921
|
+
};
|
|
1922
|
+
const standard = new Set(config.standardRoutes);
|
|
1923
|
+
const banner = el("banner", HTMLElement);
|
|
1924
|
+
const keyInput = el("admin-key", HTMLInputElement);
|
|
1925
|
+
keyInput.value = state.key;
|
|
1926
|
+
const params = new URLSearchParams(location.search);
|
|
1927
|
+
const queryKey = params.get("key");
|
|
1928
|
+
if (queryKey) {
|
|
1929
|
+
state.key = queryKey;
|
|
1930
|
+
keyInput.value = state.key;
|
|
1931
|
+
sessionStorage.setItem("mockingbird-admin-key", state.key);
|
|
1932
|
+
params.delete("key");
|
|
1933
|
+
const next = params.toString();
|
|
1934
|
+
history.replaceState(null, "", location.pathname + (next ? `?${next}` : ""));
|
|
1935
|
+
}
|
|
1936
|
+
const queryNamespace = params.get("namespace");
|
|
1937
|
+
if (queryNamespace)
|
|
1938
|
+
state.namespace = queryNamespace;
|
|
1939
|
+
const showError = (error) => {
|
|
1940
|
+
banner.textContent = error instanceof Error ? error.message : String(error);
|
|
1941
|
+
banner.classList.add("show");
|
|
1942
|
+
};
|
|
1943
|
+
const clearError = () => {
|
|
1944
|
+
banner.classList.remove("show");
|
|
1945
|
+
};
|
|
1946
|
+
const api = async (method, path, body) => {
|
|
1947
|
+
const headers = { accept: "application/json" };
|
|
1948
|
+
if (state.key !== "")
|
|
1949
|
+
headers[config.adminKeyHeader] = state.key;
|
|
1950
|
+
if (state.namespace !== "")
|
|
1951
|
+
headers["x-mockingbird-namespace"] = state.namespace;
|
|
1952
|
+
if (body !== void 0)
|
|
1953
|
+
headers["content-type"] = "application/json";
|
|
1954
|
+
const init = { method, headers };
|
|
1955
|
+
if (body !== void 0)
|
|
1956
|
+
init.body = JSON.stringify(body);
|
|
1957
|
+
const response = await fetch(config.adminPrefix + path, init);
|
|
1958
|
+
const text = await response.text();
|
|
1959
|
+
let data = null;
|
|
1960
|
+
if (text !== "") {
|
|
1961
|
+
try {
|
|
1962
|
+
data = readJson2(text);
|
|
1963
|
+
} catch {
|
|
1964
|
+
data = { raw: text };
|
|
1965
|
+
}
|
|
1966
|
+
}
|
|
1967
|
+
if (response.status === 401) {
|
|
1968
|
+
keyInput.focus();
|
|
1969
|
+
throw new Error(errorMessage(data, "Admin key required"));
|
|
1970
|
+
}
|
|
1971
|
+
if (!response.ok) {
|
|
1972
|
+
throw new Error(errorMessage(data, `${response.status} ${method} ${path}`));
|
|
1973
|
+
}
|
|
1974
|
+
return data;
|
|
1975
|
+
};
|
|
1976
|
+
const originGet = async (path) => {
|
|
1977
|
+
const response = await fetch(path, { headers: { accept: "application/json" } });
|
|
1978
|
+
if (!response.ok)
|
|
1979
|
+
throw new Error(`${response.status} ${path}`);
|
|
1980
|
+
const value = await response.json();
|
|
1981
|
+
return value;
|
|
1982
|
+
};
|
|
1983
|
+
const panelApi = {
|
|
1984
|
+
get: (path) => api("GET", path),
|
|
1985
|
+
send: (method, path, body) => api(method, path, body),
|
|
1986
|
+
namespace: () => state.namespace
|
|
1987
|
+
};
|
|
1988
|
+
const isPanelScript = (value) => typeof value === "function";
|
|
1989
|
+
const setTheme = (theme) => {
|
|
1990
|
+
if (theme)
|
|
1991
|
+
document.documentElement.dataset.theme = theme;
|
|
1992
|
+
else
|
|
1993
|
+
delete document.documentElement.dataset.theme;
|
|
1994
|
+
};
|
|
1995
|
+
const storedTheme = localStorage.getItem("mockingbird-admin-theme");
|
|
1996
|
+
if (storedTheme)
|
|
1997
|
+
setTheme(storedTheme);
|
|
1998
|
+
el("theme", HTMLButtonElement).addEventListener("click", () => {
|
|
1999
|
+
const current = document.documentElement.dataset.theme;
|
|
2000
|
+
const dark = current ? current === "dark" : matchMedia("(prefers-color-scheme: dark)").matches;
|
|
2001
|
+
const next = dark ? "light" : "dark";
|
|
2002
|
+
localStorage.setItem("mockingbird-admin-theme", next);
|
|
2003
|
+
setTheme(next);
|
|
2004
|
+
});
|
|
2005
|
+
const show = (view) => {
|
|
2006
|
+
state.view = view;
|
|
2007
|
+
for (const button of document.querySelectorAll("#nav button")) {
|
|
2008
|
+
if (!(button instanceof HTMLButtonElement))
|
|
2009
|
+
continue;
|
|
2010
|
+
button.setAttribute("aria-current", button.dataset.view === view ? "true" : "false");
|
|
2011
|
+
}
|
|
2012
|
+
for (const section of document.querySelectorAll("main .view")) {
|
|
2013
|
+
if (!(section instanceof HTMLElement))
|
|
2014
|
+
continue;
|
|
2015
|
+
section.hidden = section.id !== `view-${view}`;
|
|
2016
|
+
}
|
|
2017
|
+
};
|
|
2018
|
+
el("nav", HTMLElement).addEventListener("click", (event) => {
|
|
2019
|
+
const target = event.target;
|
|
2020
|
+
if (!(target instanceof Element))
|
|
2021
|
+
return;
|
|
2022
|
+
const button = target.closest("button[data-view]");
|
|
2023
|
+
if (!(button instanceof HTMLButtonElement))
|
|
2024
|
+
return;
|
|
2025
|
+
const view = button.dataset.view;
|
|
2026
|
+
if (view === void 0)
|
|
2027
|
+
return;
|
|
2028
|
+
show(view);
|
|
2029
|
+
refresh().catch(showError);
|
|
2030
|
+
});
|
|
2031
|
+
const cards = (items) => `<div class="grid">${items.map((item) => `<article class="card"><div class="k">${item.k}</div><div class="v">${item.v}</div></article>`).join("")}</div>`;
|
|
2032
|
+
const table = (columns, rows) => {
|
|
2033
|
+
if (rows.length === 0)
|
|
2034
|
+
return '<p class="empty">Nothing here yet.</p>';
|
|
2035
|
+
const head = `<tr>${columns.map((column) => `<th>${column.label}</th>`).join("")}</tr>`;
|
|
2036
|
+
const body = rows.map((row) => {
|
|
2037
|
+
const cells = columns.map((column) => `<td data-label="${column.label}">${column.cell(row)}</td>`).join("");
|
|
2038
|
+
const attrs = "attrs" in row && typeof row.attrs === "string" ? row.attrs : "";
|
|
2039
|
+
return `<tr${attrs}>${cells}</tr>`;
|
|
2040
|
+
}).join("");
|
|
2041
|
+
return `<table><thead>${head}</thead><tbody>${body}</tbody></table>`;
|
|
2042
|
+
};
|
|
2043
|
+
const preview = (value) => {
|
|
2044
|
+
const text = JSON.stringify(value) ?? "null";
|
|
2045
|
+
return esc(text.length > 180 ? `${text.slice(0, 180)}\u2026` : text);
|
|
2046
|
+
};
|
|
2047
|
+
const when = (epoch) => {
|
|
2048
|
+
const date = new Date(epoch);
|
|
2049
|
+
return Number.isNaN(date.getTime()) ? String(epoch) : date.toISOString();
|
|
2050
|
+
};
|
|
2051
|
+
const clockCard = (epoch) => {
|
|
2052
|
+
const date = new Date(epoch);
|
|
2053
|
+
if (Number.isNaN(date.getTime()))
|
|
2054
|
+
return esc(String(epoch));
|
|
2055
|
+
const iso = date.toISOString();
|
|
2056
|
+
return `${esc(iso.slice(0, 10))}<span class="clock-time">${esc(iso.slice(11, 19))} UTC</span>`;
|
|
2057
|
+
};
|
|
2058
|
+
const decodeClock = (value) => {
|
|
2059
|
+
if (!isRecord2(value) || typeof value.now !== "number" || typeof value.offsetMs !== "number" || typeof value.frozen !== "boolean") {
|
|
2060
|
+
throw new Error("Clock response was not a clock");
|
|
2061
|
+
}
|
|
2062
|
+
return { now: value.now, offsetMs: value.offsetMs, frozen: value.frozen };
|
|
2063
|
+
};
|
|
2064
|
+
const decodeField = (value) => {
|
|
2065
|
+
if (!isRecord2(value) || typeof value.name !== "string" || !isFieldKind(value.kind)) {
|
|
2066
|
+
return void 0;
|
|
2067
|
+
}
|
|
2068
|
+
const field = {
|
|
2069
|
+
name: value.name,
|
|
2070
|
+
kind: value.kind,
|
|
2071
|
+
optional: value.optional === true
|
|
2072
|
+
};
|
|
2073
|
+
if (typeof value.description === "string")
|
|
2074
|
+
field.description = value.description;
|
|
2075
|
+
if (Array.isArray(value.fields)) {
|
|
2076
|
+
field.fields = value.fields.flatMap((item) => {
|
|
2077
|
+
const child = decodeField(item);
|
|
2078
|
+
return child === void 0 ? [] : [child];
|
|
2079
|
+
});
|
|
2080
|
+
}
|
|
2081
|
+
return field;
|
|
2082
|
+
};
|
|
2083
|
+
const decodeCollection = (value) => {
|
|
2084
|
+
if (!isRecord2(value) || typeof value.name !== "string")
|
|
2085
|
+
return void 0;
|
|
2086
|
+
const fields = Array.isArray(value.fields) ? value.fields.flatMap((item) => {
|
|
2087
|
+
const field = decodeField(item);
|
|
2088
|
+
return field === void 0 ? [] : [field];
|
|
2089
|
+
}) : [];
|
|
2090
|
+
const collection = {
|
|
2091
|
+
name: value.name,
|
|
2092
|
+
label: typeof value.label === "string" ? value.label : value.name,
|
|
2093
|
+
count: typeof value.count === "number" ? value.count : 0,
|
|
2094
|
+
source: isSource(value.source) ? value.source : "inferred",
|
|
2095
|
+
fields
|
|
2096
|
+
};
|
|
2097
|
+
if (typeof value.description === "string")
|
|
2098
|
+
collection.description = value.description;
|
|
2099
|
+
return collection;
|
|
2100
|
+
};
|
|
2101
|
+
const decodeState = (value) => {
|
|
2102
|
+
if (!isRecord2(value) || !Array.isArray(value.collections)) {
|
|
2103
|
+
throw new Error("State response has no collection list");
|
|
2104
|
+
}
|
|
2105
|
+
return {
|
|
2106
|
+
namespace: typeof value.namespace === "string" ? value.namespace : state.namespace,
|
|
2107
|
+
storageNamespace: typeof value.storageNamespace === "string" ? value.storageNamespace : "",
|
|
2108
|
+
collections: value.collections.flatMap((item) => {
|
|
2109
|
+
const collection = decodeCollection(item);
|
|
2110
|
+
return collection === void 0 ? [] : [collection];
|
|
2111
|
+
})
|
|
2112
|
+
};
|
|
2113
|
+
};
|
|
2114
|
+
const decodeRecords = (value) => requireList(isRecord2(value) ? value.records : void 0, "Records").flatMap((item) => {
|
|
2115
|
+
if (!isRecord2(item) || typeof item.id !== "string")
|
|
2116
|
+
return [];
|
|
2117
|
+
return [
|
|
2118
|
+
{
|
|
2119
|
+
id: item.id,
|
|
2120
|
+
seq: typeof item.seq === "number" ? item.seq : 0,
|
|
2121
|
+
value: item.value,
|
|
2122
|
+
attrs: ` class="clickable" data-id="${esc(item.id)}"`
|
|
2123
|
+
}
|
|
2124
|
+
];
|
|
2125
|
+
});
|
|
2126
|
+
const textOf = (value) => typeof value === "string" || typeof value === "number" || typeof value === "boolean" ? String(value) : "";
|
|
2127
|
+
const decodeJournal = (value) => requireList(isRecord2(value) ? value.requests : void 0, "Journal").flatMap((item) => {
|
|
2128
|
+
if (!isRecord2(item))
|
|
2129
|
+
return [];
|
|
2130
|
+
return [
|
|
2131
|
+
{
|
|
2132
|
+
at: textOf(item.at),
|
|
2133
|
+
method: textOf(item.method),
|
|
2134
|
+
path: textOf(item.path),
|
|
2135
|
+
status: textOf(item.status),
|
|
2136
|
+
durationMs: textOf(item.durationMs),
|
|
2137
|
+
operationId: textOf(item.operationId)
|
|
2138
|
+
}
|
|
2139
|
+
];
|
|
2140
|
+
});
|
|
2141
|
+
const decodeFaults = (value) => requireList(isRecord2(value) ? value.faults : void 0, "Faults").flatMap((item) => {
|
|
2142
|
+
if (!isRecord2(item))
|
|
2143
|
+
return [];
|
|
2144
|
+
return [
|
|
2145
|
+
{
|
|
2146
|
+
id: textOf(item.id),
|
|
2147
|
+
method: typeof item.method === "string" ? item.method : void 0,
|
|
2148
|
+
pathPrefix: typeof item.pathPrefix === "string" ? item.pathPrefix : void 0,
|
|
2149
|
+
operationId: typeof item.operationId === "string" ? item.operationId : void 0,
|
|
2150
|
+
status: typeof item.status === "number" ? item.status : void 0,
|
|
2151
|
+
effect: typeof item.effect === "string" ? item.effect : void 0,
|
|
2152
|
+
drop: typeof item.drop === "boolean" ? item.drop : void 0,
|
|
2153
|
+
latencyMs: typeof item.latencyMs === "number" ? item.latencyMs : void 0
|
|
2154
|
+
}
|
|
2155
|
+
];
|
|
2156
|
+
});
|
|
2157
|
+
const loadNamespaces = async () => {
|
|
2158
|
+
const data = await api("GET", "/namespaces");
|
|
2159
|
+
const select = el("namespace", HTMLSelectElement);
|
|
2160
|
+
const listed = requireList(isRecord2(data) ? data.namespaces : void 0, "Namespaces");
|
|
2161
|
+
const fallback = isRecord2(data) && typeof data.default === "string" && data.default !== "" ? data.default : "default";
|
|
2162
|
+
const names = (listed.length > 0 ? listed.map((name) => String(name)) : [fallback]).slice();
|
|
2163
|
+
if (!names.includes(state.namespace))
|
|
2164
|
+
names.unshift(state.namespace);
|
|
2165
|
+
select.innerHTML = names.map((name) => `<option value="${esc(name)}">${esc(name)}</option>`).join("");
|
|
2166
|
+
select.value = state.namespace;
|
|
2167
|
+
};
|
|
2168
|
+
const renderOverview = async () => {
|
|
2169
|
+
const [health, shape, clock, journal] = await Promise.all([
|
|
2170
|
+
originGet("/health"),
|
|
2171
|
+
api("GET", "/state"),
|
|
2172
|
+
api("GET", "/clock"),
|
|
2173
|
+
api("GET", "/requests?limit=8")
|
|
2174
|
+
]);
|
|
2175
|
+
if (!isRecord2(health))
|
|
2176
|
+
throw new Error("Health response was not an object");
|
|
2177
|
+
const decoded = decodeState(shape);
|
|
2178
|
+
state.shape = decoded;
|
|
2179
|
+
const time = decodeClock(clock);
|
|
2180
|
+
el("overview-cards", HTMLElement).innerHTML = cards([
|
|
2181
|
+
{
|
|
2182
|
+
k: "Service",
|
|
2183
|
+
v: esc(typeof health.service === "string" && health.service !== "" ? health.service : config.service)
|
|
2184
|
+
},
|
|
2185
|
+
{
|
|
2186
|
+
k: "Status",
|
|
2187
|
+
v: esc(typeof health.status === "string" && health.status !== "" ? health.status : "ok")
|
|
2188
|
+
},
|
|
2189
|
+
{ k: "Namespace", v: esc(state.namespace) },
|
|
2190
|
+
{ k: "Collections", v: String(decoded.collections.length) },
|
|
2191
|
+
{
|
|
2192
|
+
k: "Records",
|
|
2193
|
+
v: String(decoded.collections.reduce((sum, item) => sum + item.count, 0))
|
|
2194
|
+
},
|
|
2195
|
+
{ k: "Clock", v: clockCard(time.now) }
|
|
2196
|
+
]);
|
|
2197
|
+
el("overview-requests", HTMLElement).innerHTML = table([
|
|
2198
|
+
{ label: "Method", cell: (row) => esc(row.method) },
|
|
2199
|
+
{ label: "Path", cell: (row) => esc(row.path) },
|
|
2200
|
+
{
|
|
2201
|
+
label: "Status",
|
|
2202
|
+
cell: (row) => `<span class="status-${row.status.charAt(0)}">${esc(row.status)}</span>`
|
|
2203
|
+
},
|
|
2204
|
+
{ label: "Operation", cell: (row) => esc(row.operationId) }
|
|
2205
|
+
], decodeJournal(journal));
|
|
2206
|
+
};
|
|
2207
|
+
const schemaChips = (collection) => {
|
|
2208
|
+
if (collection.fields.length === 0) {
|
|
2209
|
+
return '<p class="empty">No fields yet. Add a record or declare the collection on the service.</p>';
|
|
2210
|
+
}
|
|
2211
|
+
return `<div class="chips">${collection.fields.map((field) => `<span class="chip">${esc(field.name)} <i>${esc(field.kind)}${field.optional ? "?" : ""}</i></span>`).join("")}</div>`;
|
|
2212
|
+
};
|
|
2213
|
+
const renderState = async () => {
|
|
2214
|
+
const shape = decodeState(await api("GET", "/state"));
|
|
2215
|
+
state.shape = shape;
|
|
2216
|
+
const host = el("collections", HTMLElement);
|
|
2217
|
+
if (shape.collections.length === 0) {
|
|
2218
|
+
host.innerHTML = '<p class="empty">This namespace has no collections yet.</p>';
|
|
2219
|
+
el("schema", HTMLElement).innerHTML = "";
|
|
2220
|
+
el("records", HTMLElement).innerHTML = "";
|
|
2221
|
+
return;
|
|
2222
|
+
}
|
|
2223
|
+
if (!shape.collections.some((item) => item.name === state.collection)) {
|
|
2224
|
+
const first = shape.collections[0];
|
|
2225
|
+
if (first === void 0)
|
|
2226
|
+
return;
|
|
2227
|
+
state.collection = first.name;
|
|
2228
|
+
}
|
|
2229
|
+
host.innerHTML = shape.collections.map((item) => `<button type="button" class="collection" data-collection="${esc(item.name)}" aria-current="${item.name === state.collection ? "true" : "false"}"><strong>${esc(item.label)}</strong><small>${esc(item.count)} \xB7 ${esc(item.source)}</small></button>`).join("");
|
|
2230
|
+
const selected = shape.collections.find((item) => item.name === state.collection);
|
|
2231
|
+
if (selected === void 0)
|
|
2232
|
+
return;
|
|
2233
|
+
const description = selected.description ? `<p class="lede">${esc(selected.description)}</p>` : "";
|
|
2234
|
+
el("schema", HTMLElement).innerHTML = `<h3>${esc(selected.label)}</h3>${description}${schemaChips(selected)}`;
|
|
2235
|
+
const page = await api("GET", `/state/${encodeURIComponent(state.collection)}?limit=50`);
|
|
2236
|
+
el("records", HTMLElement).innerHTML = table([
|
|
2237
|
+
{ label: "Id", cell: (row) => `<code>${esc(row.id)}</code>` },
|
|
2238
|
+
{ label: "Seq", cell: (row) => esc(row.seq) },
|
|
2239
|
+
{ label: "Value", cell: (row) => preview(row.value) }
|
|
2240
|
+
], decodeRecords(page));
|
|
2241
|
+
};
|
|
2242
|
+
const renderClock = async () => {
|
|
2243
|
+
const clock = decodeClock(await api("GET", "/clock"));
|
|
2244
|
+
el("clock-readout", HTMLElement).innerHTML = `<div class="k">Now</div><div class="v">${esc(when(clock.now))}</div><p>${esc(clock.now)} ms \xB7 offset ${esc(clock.offsetMs)} ms ${clock.frozen ? "<span class='badge warn'>frozen</span>" : "<span class='badge ok'>live</span>"}</p>`;
|
|
2245
|
+
const freeze = el("freeze", HTMLButtonElement);
|
|
2246
|
+
freeze.textContent = clock.frozen ? "Unfreeze" : "Freeze";
|
|
2247
|
+
freeze.dataset.frozen = clock.frozen ? "1" : "0";
|
|
2248
|
+
};
|
|
2249
|
+
const renderFaults = async () => {
|
|
2250
|
+
const [faults, presets] = await Promise.all([
|
|
2251
|
+
api("GET", "/faults"),
|
|
2252
|
+
api("GET", "/faults/presets").catch(() => ({ presets: [] }))
|
|
2253
|
+
]);
|
|
2254
|
+
const select = el("fault-preset", HTMLSelectElement);
|
|
2255
|
+
const current = select.value;
|
|
2256
|
+
const list = faultPresetList(presets).presets;
|
|
2257
|
+
select.innerHTML = `<option value="">Custom rule</option>${list.map((preset) => `<option value="${esc(preset.name)}">${esc(preset.name)}</option>`).join("")}`;
|
|
2258
|
+
select.value = current;
|
|
2259
|
+
el("fault-list", HTMLElement).innerHTML = table([
|
|
2260
|
+
{ label: "Id", cell: (row) => esc(row.id) },
|
|
2261
|
+
{
|
|
2262
|
+
label: "Match",
|
|
2263
|
+
cell: (row) => esc([row.method, row.pathPrefix, row.operationId].filter((part) => Boolean(part)).join(" ") || "all")
|
|
2264
|
+
},
|
|
2265
|
+
{
|
|
2266
|
+
label: "Effect",
|
|
2267
|
+
cell: (row) => esc(row.status ?? row.effect ?? (row.drop ? "drop" : row.latencyMs || ""))
|
|
2268
|
+
}
|
|
2269
|
+
], decodeFaults(faults));
|
|
2270
|
+
};
|
|
2271
|
+
const renderJournal = async () => {
|
|
2272
|
+
const journal = await api("GET", "/requests?limit=100");
|
|
2273
|
+
el("journal", HTMLElement).innerHTML = table([
|
|
2274
|
+
{ label: "When", cell: (row) => esc(row.at) },
|
|
2275
|
+
{ label: "Method", cell: (row) => esc(row.method) },
|
|
2276
|
+
{ label: "Path", cell: (row) => esc(row.path) },
|
|
2277
|
+
{ label: "Status", cell: (row) => esc(row.status) },
|
|
2278
|
+
{ label: "ms", cell: (row) => esc(row.durationMs) }
|
|
2279
|
+
], decodeJournal(journal));
|
|
2280
|
+
};
|
|
2281
|
+
const renderRoutes = async () => {
|
|
2282
|
+
const data = await api("GET", "/");
|
|
2283
|
+
if (!isRecord2(data))
|
|
2284
|
+
throw new Error("Route list was not an object");
|
|
2285
|
+
const routes = requireList(data.routes, "Routes").map((route) => String(route));
|
|
2286
|
+
el("route-list", HTMLElement).innerHTML = routes.map((route) => {
|
|
2287
|
+
const extra = standard.has(route) ? "" : " <span class='badge'>extension</span>";
|
|
2288
|
+
const parts = route.split(" ");
|
|
2289
|
+
const method = parts[0] ?? "";
|
|
2290
|
+
const path = parts.slice(1).join(" ");
|
|
2291
|
+
return `<div class="route"><span class="method">${esc(method)}</span><span>${esc(path)}${extra}</span><button class="btn" type="button" data-route="${esc(route)}">Use</button></div>`;
|
|
2292
|
+
}).join("");
|
|
2293
|
+
};
|
|
2294
|
+
const renderers = {
|
|
2295
|
+
overview: renderOverview,
|
|
2296
|
+
state: renderState,
|
|
2297
|
+
clock: renderClock,
|
|
2298
|
+
faults: renderFaults,
|
|
2299
|
+
journal: renderJournal,
|
|
2300
|
+
routes: renderRoutes
|
|
2301
|
+
};
|
|
2302
|
+
async function refresh() {
|
|
2303
|
+
clearError();
|
|
2304
|
+
const render = renderers[state.view];
|
|
2305
|
+
if (render)
|
|
2306
|
+
await render();
|
|
2307
|
+
}
|
|
2308
|
+
el("namespace", HTMLSelectElement).addEventListener("change", () => {
|
|
2309
|
+
state.namespace = el("namespace", HTMLSelectElement).value;
|
|
2310
|
+
refresh().catch(showError);
|
|
2311
|
+
});
|
|
2312
|
+
keyInput.addEventListener("change", () => {
|
|
2313
|
+
state.key = keyInput.value.trim();
|
|
2314
|
+
sessionStorage.setItem("mockingbird-admin-key", state.key);
|
|
2315
|
+
refresh().catch(showError);
|
|
2316
|
+
});
|
|
2317
|
+
el("refresh", HTMLButtonElement).addEventListener("click", () => {
|
|
2318
|
+
refresh().catch(showError);
|
|
2319
|
+
});
|
|
2320
|
+
el("collections", HTMLElement).addEventListener("click", (event) => {
|
|
2321
|
+
const target = event.target;
|
|
2322
|
+
if (!(target instanceof Element))
|
|
2323
|
+
return;
|
|
2324
|
+
const button = target.closest("[data-collection]");
|
|
2325
|
+
if (!(button instanceof HTMLElement))
|
|
2326
|
+
return;
|
|
2327
|
+
const collection = button.dataset.collection;
|
|
2328
|
+
if (collection === void 0)
|
|
2329
|
+
return;
|
|
2330
|
+
state.collection = collection;
|
|
2331
|
+
renderState().catch(showError);
|
|
2332
|
+
});
|
|
2333
|
+
el("records", HTMLElement).addEventListener("click", (event) => {
|
|
2334
|
+
const target = event.target;
|
|
2335
|
+
if (!(target instanceof Element))
|
|
2336
|
+
return;
|
|
2337
|
+
const row = target.closest("[data-id]");
|
|
2338
|
+
if (!(row instanceof HTMLElement))
|
|
2339
|
+
return;
|
|
2340
|
+
const id = row.dataset.id;
|
|
2341
|
+
if (id === void 0)
|
|
2342
|
+
return;
|
|
2343
|
+
openEditor(id).catch(showError);
|
|
2344
|
+
});
|
|
2345
|
+
const editor = el("editor", HTMLDialogElement);
|
|
2346
|
+
const editorId = el("editor-id", HTMLInputElement);
|
|
2347
|
+
const editorValue = el("editor-value", HTMLTextAreaElement);
|
|
2348
|
+
const editorDelete = el("editor-delete", HTMLButtonElement);
|
|
2349
|
+
async function openEditor(id) {
|
|
2350
|
+
state.editing = id;
|
|
2351
|
+
el("editor-title", HTMLElement).textContent = id ? "Edit record" : "New record";
|
|
2352
|
+
editorId.value = id ?? "";
|
|
2353
|
+
editorId.readOnly = id !== null;
|
|
2354
|
+
editorDelete.hidden = id === null;
|
|
2355
|
+
if (id) {
|
|
2356
|
+
const record2 = await api("GET", `/state/${encodeURIComponent(state.collection)}/${encodeURIComponent(id)}`);
|
|
2357
|
+
if (!isRecord2(record2))
|
|
2358
|
+
throw new Error("Record response was not an object");
|
|
2359
|
+
editorValue.value = JSON.stringify(record2.value, null, 2) ?? "";
|
|
2360
|
+
} else {
|
|
2361
|
+
editorValue.value = "{\n \n}";
|
|
2362
|
+
}
|
|
2363
|
+
editor.showModal();
|
|
2364
|
+
}
|
|
2365
|
+
el("new-record", HTMLButtonElement).addEventListener("click", () => {
|
|
2366
|
+
openEditor(null).catch(showError);
|
|
2367
|
+
});
|
|
2368
|
+
el("editor-form", HTMLFormElement).addEventListener("submit", (event) => {
|
|
2369
|
+
const submitter = event.submitter;
|
|
2370
|
+
if (!(submitter instanceof HTMLButtonElement) || submitter.value !== "save")
|
|
2371
|
+
return;
|
|
2372
|
+
event.preventDefault();
|
|
2373
|
+
let value;
|
|
2374
|
+
try {
|
|
2375
|
+
value = readJson2(editorValue.value);
|
|
2376
|
+
} catch {
|
|
2377
|
+
showError(new Error("Value is not JSON"));
|
|
2378
|
+
return;
|
|
2379
|
+
}
|
|
2380
|
+
const id = editorId.value.trim();
|
|
2381
|
+
const path = `/state/${encodeURIComponent(state.collection)}`;
|
|
2382
|
+
const request = state.editing ? api("PUT", `${path}/${encodeURIComponent(id)}`, { value }) : api("POST", path, { id: id || void 0, value });
|
|
2383
|
+
request.then(() => {
|
|
2384
|
+
editor.close();
|
|
2385
|
+
return renderState();
|
|
2386
|
+
}).catch(showError);
|
|
2387
|
+
});
|
|
2388
|
+
editorDelete.addEventListener("click", () => {
|
|
2389
|
+
const id = state.editing;
|
|
2390
|
+
if (!id)
|
|
2391
|
+
return;
|
|
2392
|
+
api("DELETE", `/state/${encodeURIComponent(state.collection)}/${encodeURIComponent(id)}`).then(() => {
|
|
2393
|
+
editor.close();
|
|
2394
|
+
return renderState();
|
|
2395
|
+
}).catch(showError);
|
|
2396
|
+
});
|
|
2397
|
+
for (const button of document.querySelectorAll("[data-advance]")) {
|
|
2398
|
+
if (!(button instanceof HTMLButtonElement))
|
|
2399
|
+
continue;
|
|
2400
|
+
button.addEventListener("click", () => {
|
|
2401
|
+
const raw = button.dataset.advance;
|
|
2402
|
+
if (raw === void 0)
|
|
2403
|
+
return;
|
|
2404
|
+
api("POST", "/clock", { advance: Number(raw) }).then(() => renderClock()).catch(showError);
|
|
2405
|
+
});
|
|
2406
|
+
}
|
|
2407
|
+
el("freeze", HTMLButtonElement).addEventListener("click", () => {
|
|
2408
|
+
const frozen = el("freeze", HTMLButtonElement).dataset.frozen === "1";
|
|
2409
|
+
api("POST", "/clock", { freeze: !frozen }).then(() => renderClock()).catch(showError);
|
|
2410
|
+
});
|
|
2411
|
+
el("clock-reset", HTMLButtonElement).addEventListener("click", () => {
|
|
2412
|
+
api("POST", "/clock", { reset: true }).then(() => renderClock()).catch(showError);
|
|
2413
|
+
});
|
|
2414
|
+
el("clock-set", HTMLFormElement).addEventListener("submit", (event) => {
|
|
2415
|
+
event.preventDefault();
|
|
2416
|
+
const raw = el("clock-instant", HTMLInputElement).value.trim();
|
|
2417
|
+
const set = /^-?\d+$/.test(raw) ? Number(raw) : raw;
|
|
2418
|
+
api("POST", "/clock", { set }).then(() => renderClock()).catch(showError);
|
|
2419
|
+
});
|
|
2420
|
+
el("fault-form", HTMLFormElement).addEventListener("submit", (event) => {
|
|
2421
|
+
event.preventDefault();
|
|
2422
|
+
const preset = el("fault-preset", HTMLSelectElement).value;
|
|
2423
|
+
if (preset !== "") {
|
|
2424
|
+
api("POST", "/faults", { preset }).then(() => renderFaults()).catch(showError);
|
|
2425
|
+
return;
|
|
2426
|
+
}
|
|
2427
|
+
const status = Number(el("fault-status", HTMLInputElement).value);
|
|
2428
|
+
if (!Number.isFinite(status)) {
|
|
2429
|
+
showError(new Error("Status must be a number, or choose a preset"));
|
|
2430
|
+
return;
|
|
2431
|
+
}
|
|
2432
|
+
const path = el("fault-path", HTMLInputElement).value;
|
|
2433
|
+
const method = el("fault-method", HTMLInputElement).value;
|
|
2434
|
+
api("POST", "/faults", {
|
|
2435
|
+
status,
|
|
2436
|
+
...path !== "" ? { pathPrefix: path } : {},
|
|
2437
|
+
...method !== "" ? { method } : {}
|
|
2438
|
+
}).then(() => renderFaults()).catch(showError);
|
|
2439
|
+
});
|
|
2440
|
+
el("fault-clear", HTMLButtonElement).addEventListener("click", () => {
|
|
2441
|
+
api("DELETE", "/faults").then(() => renderFaults()).catch(showError);
|
|
2442
|
+
});
|
|
2443
|
+
el("journal-clear", HTMLButtonElement).addEventListener("click", () => {
|
|
2444
|
+
api("DELETE", "/requests").then(() => renderJournal()).catch(showError);
|
|
2445
|
+
});
|
|
2446
|
+
el("route-list", HTMLElement).addEventListener("click", (event) => {
|
|
2447
|
+
const target = event.target;
|
|
2448
|
+
if (!(target instanceof Element))
|
|
2449
|
+
return;
|
|
2450
|
+
const button = target.closest("[data-route]");
|
|
2451
|
+
if (!(button instanceof HTMLElement))
|
|
2452
|
+
return;
|
|
2453
|
+
const route = button.dataset.route;
|
|
2454
|
+
if (route === void 0)
|
|
2455
|
+
return;
|
|
2456
|
+
state.route = route;
|
|
2457
|
+
el("route-title", HTMLElement).textContent = route;
|
|
2458
|
+
const method = route.split(" ")[0] ?? "";
|
|
2459
|
+
el("route-body", HTMLTextAreaElement).disabled = method === "GET" || method === "DELETE";
|
|
2460
|
+
});
|
|
2461
|
+
el("route-form", HTMLFormElement).addEventListener("submit", (event) => {
|
|
2462
|
+
event.preventDefault();
|
|
2463
|
+
const send = async () => {
|
|
2464
|
+
if (state.route === null)
|
|
2465
|
+
throw new Error("Choose a route first");
|
|
2466
|
+
const parts = state.route.split(" ");
|
|
2467
|
+
const method = parts[0];
|
|
2468
|
+
const path = parts.slice(1).join(" ");
|
|
2469
|
+
if (method === void 0)
|
|
2470
|
+
throw new Error("Choose a route first");
|
|
2471
|
+
const concrete = path.replace(/:([A-Za-z0-9_]+)/g, (_match, key) => prompt(`Value for ${key}`) || "");
|
|
2472
|
+
let body;
|
|
2473
|
+
const routeBody = el("route-body", HTMLTextAreaElement);
|
|
2474
|
+
if (method !== "GET" && method !== "DELETE" && routeBody.value.trim() !== "") {
|
|
2475
|
+
try {
|
|
2476
|
+
body = readJson2(routeBody.value);
|
|
2477
|
+
} catch {
|
|
2478
|
+
throw new Error("Body is not JSON");
|
|
2479
|
+
}
|
|
2480
|
+
}
|
|
2481
|
+
const data = await api(method, concrete, body);
|
|
2482
|
+
el("route-result", HTMLElement).textContent = JSON.stringify(data, null, 2);
|
|
2483
|
+
};
|
|
2484
|
+
send().catch(showError);
|
|
2485
|
+
});
|
|
2486
|
+
const decodePanel = (value) => {
|
|
2487
|
+
if (!isRecord2(value) || typeof value.id !== "string" || typeof value.title !== "string") {
|
|
2488
|
+
return void 0;
|
|
2489
|
+
}
|
|
2490
|
+
const panel = {
|
|
2491
|
+
id: value.id,
|
|
2492
|
+
title: value.title,
|
|
2493
|
+
description: typeof value.description === "string" ? value.description : "",
|
|
2494
|
+
html: typeof value.html === "string" ? value.html : ""
|
|
2495
|
+
};
|
|
2496
|
+
if (typeof value.script === "string")
|
|
2497
|
+
panel.script = value.script;
|
|
2498
|
+
return panel;
|
|
2499
|
+
};
|
|
2500
|
+
const mountPanels = (panels) => {
|
|
2501
|
+
const nav = el("nav", HTMLElement);
|
|
2502
|
+
const views = el("views", HTMLElement);
|
|
2503
|
+
for (const panel of panels) {
|
|
2504
|
+
const button = document.createElement("button");
|
|
2505
|
+
button.type = "button";
|
|
2506
|
+
button.dataset.view = panel.id;
|
|
2507
|
+
button.textContent = panel.title;
|
|
2508
|
+
nav.append(button);
|
|
2509
|
+
const section = document.createElement("section");
|
|
2510
|
+
section.className = "view";
|
|
2511
|
+
section.id = `view-${panel.id}`;
|
|
2512
|
+
section.hidden = true;
|
|
2513
|
+
const title = document.createElement("h2");
|
|
2514
|
+
title.textContent = panel.title;
|
|
2515
|
+
const lede = document.createElement("p");
|
|
2516
|
+
lede.className = "lede";
|
|
2517
|
+
lede.textContent = panel.description;
|
|
2518
|
+
const body = document.createElement("div");
|
|
2519
|
+
body.className = "panel-body";
|
|
2520
|
+
body.id = `panel-${panel.id}`;
|
|
2521
|
+
body.innerHTML = panel.html;
|
|
2522
|
+
section.append(title, lede, body);
|
|
2523
|
+
views.append(section);
|
|
2524
|
+
if (panel.script === void 0)
|
|
2525
|
+
continue;
|
|
2526
|
+
try {
|
|
2527
|
+
const compiled = new Function("root", "api", panel.script);
|
|
2528
|
+
if (!isPanelScript(compiled))
|
|
2529
|
+
throw new Error("panel script did not compile");
|
|
2530
|
+
compiled(body, panelApi);
|
|
2531
|
+
} catch (error) {
|
|
2532
|
+
const note = document.createElement("p");
|
|
2533
|
+
note.className = "banner show";
|
|
2534
|
+
note.textContent = error instanceof Error ? error.message : String(error);
|
|
2535
|
+
body.append(note);
|
|
2536
|
+
}
|
|
2537
|
+
}
|
|
2538
|
+
};
|
|
2539
|
+
startAdminBrand({
|
|
2540
|
+
document,
|
|
2541
|
+
$: (id) => document.getElementById(id),
|
|
2542
|
+
params,
|
|
2543
|
+
location: { href: location.href, origin: location.origin },
|
|
2544
|
+
fetch,
|
|
2545
|
+
brandsUrl: config.brandsUrl
|
|
2546
|
+
});
|
|
2547
|
+
loadNamespaces().then(() => api("GET", "/ui/manifest")).then((manifest) => {
|
|
2548
|
+
const panels = requireList(isRecord2(manifest) ? manifest.panels : void 0, "Panels").flatMap((panel) => {
|
|
2549
|
+
const decoded = decodePanel(panel);
|
|
2550
|
+
return decoded === void 0 ? [] : [decoded];
|
|
2551
|
+
});
|
|
2552
|
+
mountPanels(panels);
|
|
2553
|
+
return refresh();
|
|
2554
|
+
}).catch(showError);
|
|
2555
|
+
}
|
|
2556
|
+
var clientPrelude = [isRecord2, mountAdminBrand, startAdminBrand, faultPresetList, bootAdmin].map((fn) => fn.toString()).join("\n");
|
|
2557
|
+
var adminClientSource = (config) => {
|
|
2558
|
+
const payload = JSON.stringify(config).replace(/</g, "\\u003c");
|
|
2559
|
+
return `<script>
|
|
2560
|
+
${clientPrelude}
|
|
2561
|
+
bootAdmin(${payload});
|
|
2562
|
+
</script>`;
|
|
2563
|
+
};
|
|
2564
|
+
|
|
1339
2565
|
// ../core/dist/surface.js
|
|
1340
2566
|
var defineMock = (runtime) => runtime;
|
|
1341
2567
|
var STANDARD_ADMIN_ROUTES = [
|
|
@@ -1404,94 +2630,106 @@ var escapeHtml = (value) => value.replace(/[&<>"']/g, (char) => {
|
|
|
1404
2630
|
return "'";
|
|
1405
2631
|
}
|
|
1406
2632
|
});
|
|
2633
|
+
var ADMIN_BRANDS_URL = "https://mockingbird.chrisvouga.dev/brands.json";
|
|
1407
2634
|
var renderAdminDocument = (service) => {
|
|
1408
2635
|
const name = escapeHtml(service);
|
|
1409
|
-
const
|
|
2636
|
+
const script = adminClientSource({
|
|
2637
|
+
standardRoutes: STANDARD_ADMIN_ROUTES,
|
|
2638
|
+
adminPrefix: ADMIN_PREFIX,
|
|
2639
|
+
adminKeyHeader: ADMIN_KEY_HEADER,
|
|
2640
|
+
brandsUrl: ADMIN_BRANDS_URL,
|
|
2641
|
+
service
|
|
2642
|
+
});
|
|
1410
2643
|
return `<!DOCTYPE html>
|
|
1411
2644
|
<html lang="en">
|
|
1412
2645
|
<head>
|
|
1413
2646
|
<meta charset="utf-8">
|
|
1414
2647
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
1415
2648
|
<meta name="color-scheme" content="light dark">
|
|
1416
|
-
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Crect width='16' height='16' rx='
|
|
2649
|
+
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Crect width='16' height='16' rx='2' fill='%2327272a'/%3E%3C/svg%3E">
|
|
1417
2650
|
<title>${name} admin</title>
|
|
1418
2651
|
<style>
|
|
2652
|
+
${CSS_RESET}
|
|
1419
2653
|
:root {
|
|
1420
2654
|
color-scheme: light;
|
|
1421
|
-
--bg: #
|
|
2655
|
+
--bg: #fafafa;
|
|
1422
2656
|
--bg-elev: #ffffff;
|
|
1423
|
-
--bg-muted: #
|
|
1424
|
-
--bg-inset: #
|
|
1425
|
-
--line: #
|
|
1426
|
-
--line-strong: #
|
|
1427
|
-
--ink: #
|
|
1428
|
-
--muted: #
|
|
1429
|
-
--faint: #
|
|
1430
|
-
--accent: #
|
|
1431
|
-
--accent-ink: #
|
|
1432
|
-
--accent-soft: #
|
|
1433
|
-
--ok: #
|
|
1434
|
-
--ok-soft: #
|
|
1435
|
-
--warn: #
|
|
1436
|
-
--warn-soft: #
|
|
1437
|
-
--err: #
|
|
1438
|
-
--err-soft: #
|
|
1439
|
-
--shadow:
|
|
1440
|
-
--sans: ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
2657
|
+
--bg-muted: #f4f4f5;
|
|
2658
|
+
--bg-inset: #f4f4f5;
|
|
2659
|
+
--line: #dedee3;
|
|
2660
|
+
--line-strong: #c4c4cc;
|
|
2661
|
+
--ink: #18181b;
|
|
2662
|
+
--muted: #62626b;
|
|
2663
|
+
--faint: #71717a;
|
|
2664
|
+
--accent: #27272a;
|
|
2665
|
+
--accent-ink: #ffffff;
|
|
2666
|
+
--accent-soft: #f4f4f5;
|
|
2667
|
+
--ok: #166534;
|
|
2668
|
+
--ok-soft: #f0fdf4;
|
|
2669
|
+
--warn: #a16207;
|
|
2670
|
+
--warn-soft: #fefce8;
|
|
2671
|
+
--err: #a82d32;
|
|
2672
|
+
--err-soft: #fef2f2;
|
|
2673
|
+
--shadow: none;
|
|
2674
|
+
--sans: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
1441
2675
|
--mono: ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, monospace;
|
|
1442
|
-
--radius:
|
|
1443
|
-
--header:
|
|
1444
|
-
--nav:
|
|
2676
|
+
--radius: 10px;
|
|
2677
|
+
--header: 56px;
|
|
2678
|
+
--nav: 200px;
|
|
1445
2679
|
}
|
|
1446
2680
|
:root[data-theme="dark"] {
|
|
1447
2681
|
color-scheme: dark;
|
|
1448
|
-
--bg: #
|
|
1449
|
-
--bg-elev: #
|
|
1450
|
-
--bg-muted: #
|
|
1451
|
-
--bg-inset: #
|
|
1452
|
-
--line: #
|
|
1453
|
-
--line-strong: #
|
|
1454
|
-
--ink: #
|
|
1455
|
-
--muted: #
|
|
1456
|
-
--faint: #
|
|
1457
|
-
--accent: #
|
|
1458
|
-
--accent-ink: #
|
|
1459
|
-
--accent-soft: #
|
|
1460
|
-
--ok: #
|
|
1461
|
-
--ok-soft: #
|
|
1462
|
-
--warn: #
|
|
1463
|
-
--warn-soft: #
|
|
1464
|
-
--err: #
|
|
1465
|
-
--err-soft: #
|
|
1466
|
-
--shadow:
|
|
2682
|
+
--bg: #111113;
|
|
2683
|
+
--bg-elev: #19191c;
|
|
2684
|
+
--bg-muted: #242428;
|
|
2685
|
+
--bg-inset: #111113;
|
|
2686
|
+
--line: #36363c;
|
|
2687
|
+
--line-strong: #52525b;
|
|
2688
|
+
--ink: #f4f4f5;
|
|
2689
|
+
--muted: #a9a9b2;
|
|
2690
|
+
--faint: #71717a;
|
|
2691
|
+
--accent: #e4e4e7;
|
|
2692
|
+
--accent-ink: #18181b;
|
|
2693
|
+
--accent-soft: #242428;
|
|
2694
|
+
--ok: #86efac;
|
|
2695
|
+
--ok-soft: #14241b;
|
|
2696
|
+
--warn: #fde68a;
|
|
2697
|
+
--warn-soft: #2a2410;
|
|
2698
|
+
--err: #ffaaaa;
|
|
2699
|
+
--err-soft: #2c1516;
|
|
2700
|
+
--shadow: none;
|
|
1467
2701
|
}
|
|
1468
2702
|
@media (prefers-color-scheme: dark) {
|
|
1469
2703
|
:root:not([data-theme="light"]) {
|
|
1470
2704
|
color-scheme: dark;
|
|
1471
|
-
--bg: #
|
|
1472
|
-
--bg-elev: #
|
|
1473
|
-
--bg-muted: #
|
|
1474
|
-
--bg-inset: #
|
|
1475
|
-
--line: #
|
|
1476
|
-
--line-strong: #
|
|
1477
|
-
--ink: #
|
|
1478
|
-
--muted: #
|
|
1479
|
-
--faint: #
|
|
1480
|
-
--accent: #
|
|
1481
|
-
--accent-ink: #
|
|
1482
|
-
--accent-soft: #
|
|
1483
|
-
--ok: #
|
|
1484
|
-
--ok-soft: #
|
|
1485
|
-
--warn: #
|
|
1486
|
-
--warn-soft: #
|
|
1487
|
-
--err: #
|
|
1488
|
-
--err-soft: #
|
|
1489
|
-
--shadow:
|
|
2705
|
+
--bg: #111113;
|
|
2706
|
+
--bg-elev: #19191c;
|
|
2707
|
+
--bg-muted: #242428;
|
|
2708
|
+
--bg-inset: #111113;
|
|
2709
|
+
--line: #36363c;
|
|
2710
|
+
--line-strong: #52525b;
|
|
2711
|
+
--ink: #f4f4f5;
|
|
2712
|
+
--muted: #a9a9b2;
|
|
2713
|
+
--faint: #71717a;
|
|
2714
|
+
--accent: #e4e4e7;
|
|
2715
|
+
--accent-ink: #18181b;
|
|
2716
|
+
--accent-soft: #242428;
|
|
2717
|
+
--ok: #86efac;
|
|
2718
|
+
--ok-soft: #14241b;
|
|
2719
|
+
--warn: #fde68a;
|
|
2720
|
+
--warn-soft: #2a2410;
|
|
2721
|
+
--err: #ffaaaa;
|
|
2722
|
+
--err-soft: #2c1516;
|
|
2723
|
+
--shadow: none;
|
|
1490
2724
|
}
|
|
1491
2725
|
}
|
|
1492
|
-
|
|
1493
|
-
html
|
|
2726
|
+
/* Fill the viewport, then grow with the content so the background covers the scroll. */
|
|
2727
|
+
html { height: 100%; }
|
|
1494
2728
|
body {
|
|
2729
|
+
margin: 0;
|
|
2730
|
+
min-height: 100%;
|
|
2731
|
+
display: flex;
|
|
2732
|
+
flex-direction: column;
|
|
1495
2733
|
font-family: var(--sans);
|
|
1496
2734
|
background: var(--bg);
|
|
1497
2735
|
color: var(--ink);
|
|
@@ -1500,24 +2738,37 @@ var renderAdminDocument = (service) => {
|
|
|
1500
2738
|
padding-right: env(safe-area-inset-right);
|
|
1501
2739
|
-webkit-text-size-adjust: 100%;
|
|
1502
2740
|
}
|
|
1503
|
-
button, input, select, textarea { font: inherit; color: inherit; }
|
|
1504
|
-
button { cursor: pointer; }
|
|
1505
2741
|
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
|
|
1506
|
-
|
|
1507
|
-
* { scroll-behavior: auto !important; transition: none !important; }
|
|
1508
|
-
}
|
|
1509
|
-
.app { min-height: 100%; display: flex; flex-direction: column; }
|
|
2742
|
+
.app { flex: 1 0 auto; display: flex; flex-direction: column; min-width: 0; width: 100%; }
|
|
1510
2743
|
header.top {
|
|
1511
2744
|
position: sticky; top: 0; z-index: 5;
|
|
1512
2745
|
display: flex; align-items: center; gap: 12px;
|
|
1513
2746
|
min-height: var(--header); padding: 10px 16px;
|
|
1514
|
-
background:
|
|
2747
|
+
background: var(--bg-elev);
|
|
1515
2748
|
border-bottom: 1px solid var(--line);
|
|
1516
|
-
|
|
1517
|
-
}
|
|
1518
|
-
.brand {
|
|
1519
|
-
|
|
1520
|
-
|
|
2749
|
+
min-width: 0; max-width: 100%;
|
|
2750
|
+
}
|
|
2751
|
+
.brand {
|
|
2752
|
+
display: flex; flex-flow: row wrap; align-items: center; gap: 14px;
|
|
2753
|
+
min-width: 0; max-width: 100%; margin-right: auto;
|
|
2754
|
+
}
|
|
2755
|
+
.brand-id { display: flex; flex-direction: column; min-width: 0; flex: none; }
|
|
2756
|
+
.brand-id strong { font-size: 15px; letter-spacing: -0.01em; }
|
|
2757
|
+
.brand-id span { color: var(--faint); font-size: 12px; }
|
|
2758
|
+
.vendor {
|
|
2759
|
+
display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
|
|
2760
|
+
min-width: 0; max-width: 100%; font-size: 12px; color: var(--muted);
|
|
2761
|
+
}
|
|
2762
|
+
.vendor[hidden] { display: none; }
|
|
2763
|
+
/* Logos are drawn for a light tile, same as the docs site, so the plate stays white. */
|
|
2764
|
+
.vendor img {
|
|
2765
|
+
width: 16px; height: 16px; padding: 2px; box-sizing: content-box;
|
|
2766
|
+
border-radius: 4px; border: 1px solid var(--line); background: #fff; object-fit: contain;
|
|
2767
|
+
}
|
|
2768
|
+
.vendor-name { color: var(--ink); font-weight: 600; }
|
|
2769
|
+
.vendor-sep { color: var(--faint); }
|
|
2770
|
+
.vendor-link { color: inherit; text-decoration: none; }
|
|
2771
|
+
.vendor-link:hover { color: var(--ink); text-decoration: underline; }
|
|
1521
2772
|
.controls { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
|
|
1522
2773
|
label.field { display: flex; flex-direction: column; gap: 2px; font-size: 11px; color: var(--faint); }
|
|
1523
2774
|
label.field input, label.field select, .controls input[type="password"] {
|
|
@@ -1535,7 +2786,7 @@ var renderAdminDocument = (service) => {
|
|
|
1535
2786
|
record or the full nav row would widen the page past the viewport. */
|
|
1536
2787
|
.layout {
|
|
1537
2788
|
display: grid; grid-template-columns: var(--nav) minmax(0, 1fr);
|
|
1538
|
-
flex: 1
|
|
2789
|
+
flex: 1 0 auto; min-width: 0;
|
|
1539
2790
|
}
|
|
1540
2791
|
nav.side {
|
|
1541
2792
|
border-right: 1px solid var(--line); padding: 12px;
|
|
@@ -1589,8 +2840,8 @@ var renderAdminDocument = (service) => {
|
|
|
1589
2840
|
.banner.show { display: block; }
|
|
1590
2841
|
.empty { color: var(--muted); padding: 24px 8px; }
|
|
1591
2842
|
dialog {
|
|
1592
|
-
border: 1px solid var(--line); border-radius:
|
|
1593
|
-
color: var(--ink); width: min(640px, calc(100vw - 24px)); box-shadow:
|
|
2843
|
+
border: 1px solid var(--line); border-radius: var(--radius); padding: 0; background: var(--bg-elev);
|
|
2844
|
+
color: var(--ink); width: min(640px, calc(100vw - 24px)); box-shadow: none;
|
|
1594
2845
|
}
|
|
1595
2846
|
dialog::backdrop { background: rgb(20 19 17 / 0.45); }
|
|
1596
2847
|
dialog form, .dialog-body { padding: 16px; display: flex; flex-direction: column; gap: 10px; }
|
|
@@ -1617,7 +2868,7 @@ var renderAdminDocument = (service) => {
|
|
|
1617
2868
|
padding-top: max(10px, env(safe-area-inset-top));
|
|
1618
2869
|
}
|
|
1619
2870
|
input, select, textarea { font-size: 16px; }
|
|
1620
|
-
.controls { width: 100%; }
|
|
2871
|
+
.brand, .vendor, .controls { width: 100%; }
|
|
1621
2872
|
label.field, label.field select, label.field input { flex: 1; min-width: 0; }
|
|
1622
2873
|
.layout { grid-template-columns: minmax(0, 1fr); }
|
|
1623
2874
|
nav.side {
|
|
@@ -1648,8 +2899,11 @@ var renderAdminDocument = (service) => {
|
|
|
1648
2899
|
<div class="app" data-service="${name}" data-mockingbird-admin>
|
|
1649
2900
|
<header class="top">
|
|
1650
2901
|
<div class="brand">
|
|
1651
|
-
<
|
|
1652
|
-
|
|
2902
|
+
<div class="brand-id">
|
|
2903
|
+
<strong>${name}</strong>
|
|
2904
|
+
<span>Admin</span>
|
|
2905
|
+
</div>
|
|
2906
|
+
<div class="vendor" id="vendor" hidden></div>
|
|
1653
2907
|
</div>
|
|
1654
2908
|
<div class="controls">
|
|
1655
2909
|
<label class="field">Namespace
|
|
@@ -1779,460 +3033,7 @@ var renderAdminDocument = (service) => {
|
|
|
1779
3033
|
</div>
|
|
1780
3034
|
</form>
|
|
1781
3035
|
</dialog>
|
|
1782
|
-
|
|
1783
|
-
(() => {
|
|
1784
|
-
const STANDARD = new Set(${standard})
|
|
1785
|
-
const state = {
|
|
1786
|
-
namespace: "default",
|
|
1787
|
-
key: sessionStorage.getItem("mockingbird-admin-key") || "",
|
|
1788
|
-
view: "overview",
|
|
1789
|
-
collection: "",
|
|
1790
|
-
shape: null,
|
|
1791
|
-
route: null,
|
|
1792
|
-
editing: null
|
|
1793
|
-
}
|
|
1794
|
-
const $ = (id) => document.getElementById(id)
|
|
1795
|
-
const banner = $("banner")
|
|
1796
|
-
const keyInput = $("admin-key")
|
|
1797
|
-
keyInput.value = state.key
|
|
1798
|
-
const params = new URLSearchParams(location.search)
|
|
1799
|
-
if (params.get("key")) {
|
|
1800
|
-
state.key = params.get("key")
|
|
1801
|
-
keyInput.value = state.key
|
|
1802
|
-
sessionStorage.setItem("mockingbird-admin-key", state.key)
|
|
1803
|
-
params.delete("key")
|
|
1804
|
-
const next = params.toString()
|
|
1805
|
-
history.replaceState(null, "", location.pathname + (next ? "?" + next : ""))
|
|
1806
|
-
}
|
|
1807
|
-
if (params.get("namespace")) state.namespace = params.get("namespace")
|
|
1808
|
-
|
|
1809
|
-
const showError = (error) => {
|
|
1810
|
-
banner.textContent = error instanceof Error ? error.message : String(error)
|
|
1811
|
-
banner.classList.add("show")
|
|
1812
|
-
}
|
|
1813
|
-
const clearError = () => banner.classList.remove("show")
|
|
1814
|
-
|
|
1815
|
-
async function originGet(path) {
|
|
1816
|
-
const response = await fetch(path, { headers: { accept: "application/json" } })
|
|
1817
|
-
if (!response.ok) throw new Error(response.status + " " + path)
|
|
1818
|
-
return response.json()
|
|
1819
|
-
}
|
|
1820
|
-
async function api(method, path, body) {
|
|
1821
|
-
const headers = { accept: "application/json" }
|
|
1822
|
-
if (state.key) headers["${ADMIN_KEY_HEADER}"] = state.key
|
|
1823
|
-
if (state.namespace) headers["x-mockingbird-namespace"] = state.namespace
|
|
1824
|
-
if (body !== undefined) headers["content-type"] = "application/json"
|
|
1825
|
-
const response = await fetch("${ADMIN_PREFIX}" + path, {
|
|
1826
|
-
method,
|
|
1827
|
-
headers,
|
|
1828
|
-
body: body === undefined ? undefined : JSON.stringify(body)
|
|
1829
|
-
})
|
|
1830
|
-
const text = await response.text()
|
|
1831
|
-
let data = null
|
|
1832
|
-
if (text) {
|
|
1833
|
-
try { data = JSON.parse(text) } catch { data = { raw: text } }
|
|
1834
|
-
}
|
|
1835
|
-
if (response.status === 401) {
|
|
1836
|
-
keyInput.focus()
|
|
1837
|
-
throw new Error((data && data.error && data.error.message) || "Admin key required")
|
|
1838
|
-
}
|
|
1839
|
-
if (!response.ok) {
|
|
1840
|
-
throw new Error((data && data.error && data.error.message) || (response.status + " " + method + " " + path))
|
|
1841
|
-
}
|
|
1842
|
-
return data
|
|
1843
|
-
}
|
|
1844
|
-
const panelApi = {
|
|
1845
|
-
get: (path) => api("GET", path),
|
|
1846
|
-
send: (method, path, body) => api(method, path, body),
|
|
1847
|
-
namespace: () => state.namespace
|
|
1848
|
-
}
|
|
1849
|
-
|
|
1850
|
-
function setTheme(theme) {
|
|
1851
|
-
if (theme) document.documentElement.dataset.theme = theme
|
|
1852
|
-
else delete document.documentElement.dataset.theme
|
|
1853
|
-
}
|
|
1854
|
-
const storedTheme = localStorage.getItem("mockingbird-admin-theme")
|
|
1855
|
-
if (storedTheme) setTheme(storedTheme)
|
|
1856
|
-
$("theme").addEventListener("click", () => {
|
|
1857
|
-
const current = document.documentElement.dataset.theme
|
|
1858
|
-
const dark = current ? current === "dark" : matchMedia("(prefers-color-scheme: dark)").matches
|
|
1859
|
-
const next = dark ? "light" : "dark"
|
|
1860
|
-
localStorage.setItem("mockingbird-admin-theme", next)
|
|
1861
|
-
setTheme(next)
|
|
1862
|
-
})
|
|
1863
|
-
|
|
1864
|
-
function show(view) {
|
|
1865
|
-
state.view = view
|
|
1866
|
-
for (const button of document.querySelectorAll("#nav button")) {
|
|
1867
|
-
button.setAttribute("aria-current", button.dataset.view === view ? "true" : "false")
|
|
1868
|
-
}
|
|
1869
|
-
for (const section of document.querySelectorAll("main .view")) {
|
|
1870
|
-
section.hidden = section.id !== "view-" + view
|
|
1871
|
-
}
|
|
1872
|
-
}
|
|
1873
|
-
document.getElementById("nav").addEventListener("click", (event) => {
|
|
1874
|
-
const button = event.target.closest("button[data-view]")
|
|
1875
|
-
if (!button) return
|
|
1876
|
-
show(button.dataset.view)
|
|
1877
|
-
refresh().catch(showError)
|
|
1878
|
-
})
|
|
1879
|
-
|
|
1880
|
-
function cards(items) {
|
|
1881
|
-
return '<div class="grid">' + items.map((item) =>
|
|
1882
|
-
'<article class="card"><div class="k">' + item.k + '</div><div class="v">' + item.v + '</div></article>'
|
|
1883
|
-
).join("") + "</div>"
|
|
1884
|
-
}
|
|
1885
|
-
function table(columns, rows) {
|
|
1886
|
-
if (rows.length === 0) return '<p class="empty">Nothing here yet.</p>'
|
|
1887
|
-
const head = "<tr>" + columns.map((column) => "<th>" + column.label + "</th>").join("") + "</tr>"
|
|
1888
|
-
const body = rows.map((row) => "<tr" + (row.attrs || "") + ">" + columns.map((column) =>
|
|
1889
|
-
'<td data-label="' + column.label + '">' + column.cell(row) + "</td>"
|
|
1890
|
-
).join("") + "</tr>").join("")
|
|
1891
|
-
return "<table><thead>" + head + "</thead><tbody>" + body + "</tbody></table>"
|
|
1892
|
-
}
|
|
1893
|
-
const esc = (value) => String(value ?? "").replace(/[&<>"']/g, (char) => ({
|
|
1894
|
-
"&": "&", "<": "<", ">": ">", '"': """, "'": "'"
|
|
1895
|
-
}[char]))
|
|
1896
|
-
const preview = (value) => {
|
|
1897
|
-
const text = JSON.stringify(value)
|
|
1898
|
-
return esc(text.length > 180 ? text.slice(0, 180) + "\u2026" : text)
|
|
1899
|
-
}
|
|
1900
|
-
const when = (epoch) => {
|
|
1901
|
-
const date = new Date(epoch)
|
|
1902
|
-
return Number.isNaN(date.getTime()) ? String(epoch) : date.toISOString()
|
|
1903
|
-
}
|
|
1904
|
-
const clockCard = (epoch) => {
|
|
1905
|
-
const date = new Date(epoch)
|
|
1906
|
-
if (Number.isNaN(date.getTime())) return esc(String(epoch))
|
|
1907
|
-
const iso = date.toISOString()
|
|
1908
|
-
return esc(iso.slice(0, 10)) + '<span class="clock-time">' + esc(iso.slice(11, 19)) + " UTC</span>"
|
|
1909
|
-
}
|
|
1910
|
-
|
|
1911
|
-
async function loadNamespaces() {
|
|
1912
|
-
const data = await api("GET", "/namespaces")
|
|
1913
|
-
const select = $("namespace")
|
|
1914
|
-
const names = data.namespaces && data.namespaces.length ? data.namespaces : [data.default || "default"]
|
|
1915
|
-
if (!names.includes(state.namespace)) names.unshift(state.namespace)
|
|
1916
|
-
select.innerHTML = names.map((name) =>
|
|
1917
|
-
'<option value="' + esc(name) + '">' + esc(name) + "</option>"
|
|
1918
|
-
).join("")
|
|
1919
|
-
select.value = state.namespace
|
|
1920
|
-
}
|
|
1921
|
-
|
|
1922
|
-
async function renderOverview() {
|
|
1923
|
-
const [health, shape, clock, journal] = await Promise.all([
|
|
1924
|
-
originGet("/health"),
|
|
1925
|
-
api("GET", "/state"),
|
|
1926
|
-
api("GET", "/clock"),
|
|
1927
|
-
api("GET", "/requests?limit=8")
|
|
1928
|
-
])
|
|
1929
|
-
state.shape = shape
|
|
1930
|
-
$("overview-cards").innerHTML = cards([
|
|
1931
|
-
{ k: "Service", v: esc(health.service || "${name}") },
|
|
1932
|
-
{ k: "Status", v: esc(health.status || "ok") },
|
|
1933
|
-
{ k: "Namespace", v: esc(state.namespace) },
|
|
1934
|
-
{ k: "Collections", v: String(shape.collections.length) },
|
|
1935
|
-
{ k: "Records", v: String(shape.collections.reduce((sum, item) => sum + item.count, 0)) },
|
|
1936
|
-
{ k: "Clock", v: clockCard(clock.now) }
|
|
1937
|
-
])
|
|
1938
|
-
const requests = (journal && journal.requests) || []
|
|
1939
|
-
$("overview-requests").innerHTML = table(
|
|
1940
|
-
[
|
|
1941
|
-
{ label: "Method", cell: (row) => esc(row.method) },
|
|
1942
|
-
{ label: "Path", cell: (row) => esc(row.path) },
|
|
1943
|
-
{ label: "Status", cell: (row) => '<span class="status-' + String(row.status).charAt(0) + '">' + esc(row.status) + "</span>" },
|
|
1944
|
-
{ label: "Operation", cell: (row) => esc(row.operationId || "") }
|
|
1945
|
-
],
|
|
1946
|
-
requests
|
|
1947
|
-
)
|
|
1948
|
-
}
|
|
1949
|
-
|
|
1950
|
-
function schemaChips(collection) {
|
|
1951
|
-
if (!collection || collection.fields.length === 0) {
|
|
1952
|
-
return '<p class="empty">No fields yet. Add a record or declare the collection on the service.</p>'
|
|
1953
|
-
}
|
|
1954
|
-
return '<div class="chips">' + collection.fields.map((field) =>
|
|
1955
|
-
'<span class="chip">' + esc(field.name) + ' <i>' + esc(field.kind) + (field.optional ? "?" : "") + "</i></span>"
|
|
1956
|
-
).join("") + "</div>"
|
|
1957
|
-
}
|
|
1958
|
-
|
|
1959
|
-
async function renderState() {
|
|
1960
|
-
const shape = await api("GET", "/state")
|
|
1961
|
-
state.shape = shape
|
|
1962
|
-
const host = $("collections")
|
|
1963
|
-
if (shape.collections.length === 0) {
|
|
1964
|
-
host.innerHTML = '<p class="empty">This namespace has no collections yet.</p>'
|
|
1965
|
-
$("schema").innerHTML = ""
|
|
1966
|
-
$("records").innerHTML = ""
|
|
1967
|
-
return
|
|
1968
|
-
}
|
|
1969
|
-
if (!shape.collections.some((item) => item.name === state.collection)) {
|
|
1970
|
-
state.collection = shape.collections[0].name
|
|
1971
|
-
}
|
|
1972
|
-
host.innerHTML = shape.collections.map((item) =>
|
|
1973
|
-
'<button type="button" class="collection" data-collection="' + esc(item.name) + '" aria-current="' +
|
|
1974
|
-
(item.name === state.collection ? "true" : "false") + '"><strong>' + esc(item.label) +
|
|
1975
|
-
'</strong><small>' + esc(item.count) + " \xB7 " + esc(item.source) + "</small></button>"
|
|
1976
|
-
).join("")
|
|
1977
|
-
const selected = shape.collections.find((item) => item.name === state.collection)
|
|
1978
|
-
$("schema").innerHTML = "<h3>" + esc(selected.label) + "</h3>" +
|
|
1979
|
-
(selected.description ? "<p class=" + '"lede">' + esc(selected.description) + "</p>" : "") +
|
|
1980
|
-
schemaChips(selected)
|
|
1981
|
-
const page = await api("GET", "/state/" + encodeURIComponent(state.collection) + "?limit=50")
|
|
1982
|
-
const rows = page.records.map((record) => ({
|
|
1983
|
-
...record,
|
|
1984
|
-
attrs: ' class="clickable" data-id="' + esc(record.id) + '"'
|
|
1985
|
-
}))
|
|
1986
|
-
$("records").innerHTML = table(
|
|
1987
|
-
[
|
|
1988
|
-
{ label: "Id", cell: (row) => "<code>" + esc(row.id) + "</code>" },
|
|
1989
|
-
{ label: "Seq", cell: (row) => esc(row.seq) },
|
|
1990
|
-
{ label: "Value", cell: (row) => preview(row.value) }
|
|
1991
|
-
],
|
|
1992
|
-
rows
|
|
1993
|
-
)
|
|
1994
|
-
}
|
|
1995
|
-
|
|
1996
|
-
async function renderClock() {
|
|
1997
|
-
const clock = await api("GET", "/clock")
|
|
1998
|
-
$("clock-readout").innerHTML = "<div class='k'>Now</div><div class='v'>" + esc(when(clock.now)) +
|
|
1999
|
-
"</div><p>" + esc(clock.now) + " ms \xB7 offset " + esc(clock.offsetMs) + " ms " +
|
|
2000
|
-
(clock.frozen ? "<span class='badge warn'>frozen</span>" : "<span class='badge ok'>live</span>") + "</p>"
|
|
2001
|
-
$("freeze").textContent = clock.frozen ? "Unfreeze" : "Freeze"
|
|
2002
|
-
$("freeze").dataset.frozen = clock.frozen ? "1" : "0"
|
|
2003
|
-
}
|
|
2004
|
-
|
|
2005
|
-
async function renderFaults() {
|
|
2006
|
-
const [faults, presets] = await Promise.all([
|
|
2007
|
-
api("GET", "/faults"),
|
|
2008
|
-
api("GET", "/faults/presets").catch(() => ({ presets: [] }))
|
|
2009
|
-
])
|
|
2010
|
-
const select = $("fault-preset")
|
|
2011
|
-
const current = select.value
|
|
2012
|
-
const list = presets.presets || []
|
|
2013
|
-
select.innerHTML = '<option value="">Custom rule</option>' + list.map((preset) =>
|
|
2014
|
-
'<option value="' + esc(preset.name) + '">' + esc(preset.name) + "</option>"
|
|
2015
|
-
).join("")
|
|
2016
|
-
select.value = current
|
|
2017
|
-
const rules = faults.faults || []
|
|
2018
|
-
$("fault-list").innerHTML = table(
|
|
2019
|
-
[
|
|
2020
|
-
{ label: "Id", cell: (row) => esc(row.id) },
|
|
2021
|
-
{ label: "Match", cell: (row) => esc([row.method, row.pathPrefix, row.operationId].filter(Boolean).join(" ") || "all") },
|
|
2022
|
-
{ label: "Effect", cell: (row) => esc(row.status ?? row.effect ?? (row.drop ? "drop" : row.latencyMs || "")) }
|
|
2023
|
-
],
|
|
2024
|
-
rules
|
|
2025
|
-
)
|
|
2026
|
-
}
|
|
2027
|
-
|
|
2028
|
-
async function renderJournal() {
|
|
2029
|
-
const journal = await api("GET", "/requests?limit=100")
|
|
2030
|
-
$("journal").innerHTML = table(
|
|
2031
|
-
[
|
|
2032
|
-
{ label: "When", cell: (row) => esc(row.at || "") },
|
|
2033
|
-
{ label: "Method", cell: (row) => esc(row.method) },
|
|
2034
|
-
{ label: "Path", cell: (row) => esc(row.path) },
|
|
2035
|
-
{ label: "Status", cell: (row) => esc(row.status) },
|
|
2036
|
-
{ label: "ms", cell: (row) => esc(row.durationMs) }
|
|
2037
|
-
],
|
|
2038
|
-
journal.requests || []
|
|
2039
|
-
)
|
|
2040
|
-
}
|
|
2041
|
-
|
|
2042
|
-
async function renderRoutes() {
|
|
2043
|
-
const data = await api("GET", "/")
|
|
2044
|
-
const routes = data.routes || []
|
|
2045
|
-
$("route-list").innerHTML = routes.map((route) => {
|
|
2046
|
-
const extra = STANDARD.has(route) ? "" : " <span class='badge'>extension</span>"
|
|
2047
|
-
return '<div class="route"><span class="method">' + esc(route.split(" ")[0]) +
|
|
2048
|
-
'</span><span>' + esc(route.split(" ").slice(1).join(" ")) + extra +
|
|
2049
|
-
'</span><button class="btn" type="button" data-route="' + esc(route) + '">Use</button></div>'
|
|
2050
|
-
}).join("")
|
|
2051
|
-
}
|
|
2052
|
-
|
|
2053
|
-
const renderers = {
|
|
2054
|
-
overview: renderOverview,
|
|
2055
|
-
state: renderState,
|
|
2056
|
-
clock: renderClock,
|
|
2057
|
-
faults: renderFaults,
|
|
2058
|
-
journal: renderJournal,
|
|
2059
|
-
routes: renderRoutes
|
|
2060
|
-
}
|
|
2061
|
-
|
|
2062
|
-
async function refresh() {
|
|
2063
|
-
clearError()
|
|
2064
|
-
if (state.view in renderers) await renderers[state.view]()
|
|
2065
|
-
}
|
|
2066
|
-
|
|
2067
|
-
$("namespace").addEventListener("change", () => {
|
|
2068
|
-
state.namespace = $("namespace").value
|
|
2069
|
-
refresh().catch(showError)
|
|
2070
|
-
})
|
|
2071
|
-
keyInput.addEventListener("change", () => {
|
|
2072
|
-
state.key = keyInput.value.trim()
|
|
2073
|
-
sessionStorage.setItem("mockingbird-admin-key", state.key)
|
|
2074
|
-
refresh().catch(showError)
|
|
2075
|
-
})
|
|
2076
|
-
$("refresh").addEventListener("click", () => refresh().catch(showError))
|
|
2077
|
-
|
|
2078
|
-
$("collections").addEventListener("click", (event) => {
|
|
2079
|
-
const button = event.target.closest("[data-collection]")
|
|
2080
|
-
if (!button) return
|
|
2081
|
-
state.collection = button.dataset.collection
|
|
2082
|
-
renderState().catch(showError)
|
|
2083
|
-
})
|
|
2084
|
-
$("records").addEventListener("click", (event) => {
|
|
2085
|
-
const row = event.target.closest("[data-id]")
|
|
2086
|
-
if (!row) return
|
|
2087
|
-
openEditor(row.dataset.id).catch(showError)
|
|
2088
|
-
})
|
|
2089
|
-
|
|
2090
|
-
const editor = $("editor")
|
|
2091
|
-
async function openEditor(id) {
|
|
2092
|
-
state.editing = id || null
|
|
2093
|
-
$("editor-title").textContent = id ? "Edit record" : "New record"
|
|
2094
|
-
$("editor-id").value = id || ""
|
|
2095
|
-
$("editor-id").readOnly = Boolean(id)
|
|
2096
|
-
$("editor-delete").hidden = !id
|
|
2097
|
-
if (id) {
|
|
2098
|
-
const record = await api("GET", "/state/" + encodeURIComponent(state.collection) + "/" + encodeURIComponent(id))
|
|
2099
|
-
$("editor-value").value = JSON.stringify(record.value, null, 2)
|
|
2100
|
-
} else {
|
|
2101
|
-
$("editor-value").value = "{\\n \\n}"
|
|
2102
|
-
}
|
|
2103
|
-
editor.showModal()
|
|
2104
|
-
}
|
|
2105
|
-
$("new-record").addEventListener("click", () => openEditor(null).catch(showError))
|
|
2106
|
-
$("editor-form").addEventListener("submit", (event) => {
|
|
2107
|
-
const submitter = event.submitter
|
|
2108
|
-
if (!submitter || submitter.value !== "save") return
|
|
2109
|
-
event.preventDefault()
|
|
2110
|
-
let value
|
|
2111
|
-
try { value = JSON.parse($("editor-value").value) }
|
|
2112
|
-
catch { showError(new Error("Value is not JSON")); return }
|
|
2113
|
-
const id = $("editor-id").value.trim()
|
|
2114
|
-
const path = "/state/" + encodeURIComponent(state.collection)
|
|
2115
|
-
const request = state.editing
|
|
2116
|
-
? api("PUT", path + "/" + encodeURIComponent(id), { value })
|
|
2117
|
-
: api("POST", path, { id: id || undefined, value })
|
|
2118
|
-
request.then(() => { editor.close(); return renderState() }).catch(showError)
|
|
2119
|
-
})
|
|
2120
|
-
$("editor-delete").addEventListener("click", () => {
|
|
2121
|
-
const id = state.editing
|
|
2122
|
-
if (!id) return
|
|
2123
|
-
api("DELETE", "/state/" + encodeURIComponent(state.collection) + "/" + encodeURIComponent(id))
|
|
2124
|
-
.then(() => { editor.close(); return renderState() })
|
|
2125
|
-
.catch(showError)
|
|
2126
|
-
})
|
|
2127
|
-
|
|
2128
|
-
document.querySelectorAll("[data-advance]").forEach((button) => {
|
|
2129
|
-
button.addEventListener("click", () => {
|
|
2130
|
-
api("POST", "/clock", { advance: Number(button.dataset.advance) }).then(renderClock).catch(showError)
|
|
2131
|
-
})
|
|
2132
|
-
})
|
|
2133
|
-
$("freeze").addEventListener("click", () => {
|
|
2134
|
-
const frozen = $("freeze").dataset.frozen === "1"
|
|
2135
|
-
api("POST", "/clock", { freeze: !frozen }).then(renderClock).catch(showError)
|
|
2136
|
-
})
|
|
2137
|
-
$("clock-reset").addEventListener("click", () => {
|
|
2138
|
-
api("POST", "/clock", { reset: true }).then(renderClock).catch(showError)
|
|
2139
|
-
})
|
|
2140
|
-
$("clock-set").addEventListener("submit", (event) => {
|
|
2141
|
-
event.preventDefault()
|
|
2142
|
-
const raw = $("clock-instant").value.trim()
|
|
2143
|
-
const set = /^-?\\d+$/.test(raw) ? Number(raw) : raw
|
|
2144
|
-
api("POST", "/clock", { set }).then(renderClock).catch(showError)
|
|
2145
|
-
})
|
|
2146
|
-
|
|
2147
|
-
$("fault-form").addEventListener("submit", (event) => {
|
|
2148
|
-
event.preventDefault()
|
|
2149
|
-
const preset = $("fault-preset").value
|
|
2150
|
-
const body = preset
|
|
2151
|
-
? { preset }
|
|
2152
|
-
: {
|
|
2153
|
-
status: Number($("fault-status").value),
|
|
2154
|
-
...($("fault-path").value ? { pathPrefix: $("fault-path").value } : {}),
|
|
2155
|
-
...($("fault-method").value ? { method: $("fault-method").value } : {})
|
|
2156
|
-
}
|
|
2157
|
-
if (!preset && !Number.isFinite(body.status)) {
|
|
2158
|
-
showError(new Error("Status must be a number, or choose a preset"))
|
|
2159
|
-
return
|
|
2160
|
-
}
|
|
2161
|
-
api("POST", "/faults", body).then(renderFaults).catch(showError)
|
|
2162
|
-
})
|
|
2163
|
-
$("fault-clear").addEventListener("click", () => api("DELETE", "/faults").then(renderFaults).catch(showError))
|
|
2164
|
-
$("journal-clear").addEventListener("click", () => api("DELETE", "/requests").then(renderJournal).catch(showError))
|
|
2165
|
-
|
|
2166
|
-
$("route-list").addEventListener("click", (event) => {
|
|
2167
|
-
const button = event.target.closest("[data-route]")
|
|
2168
|
-
if (!button) return
|
|
2169
|
-
state.route = button.dataset.route
|
|
2170
|
-
$("route-title").textContent = state.route
|
|
2171
|
-
const method = state.route.split(" ")[0]
|
|
2172
|
-
$("route-body").disabled = method === "GET" || method === "DELETE"
|
|
2173
|
-
})
|
|
2174
|
-
$("route-form").addEventListener("submit", async (event) => {
|
|
2175
|
-
event.preventDefault()
|
|
2176
|
-
if (!state.route) return showError(new Error("Choose a route first"))
|
|
2177
|
-
const [method, path] = [state.route.split(" ")[0], state.route.split(" ").slice(1).join(" ")]
|
|
2178
|
-
const concrete = path.replace(/:([A-Za-z0-9_]+)/g, (_, key) => prompt("Value for " + key) || "")
|
|
2179
|
-
let body
|
|
2180
|
-
if (method !== "GET" && method !== "DELETE" && $("route-body").value.trim()) {
|
|
2181
|
-
try { body = JSON.parse($("route-body").value) }
|
|
2182
|
-
catch { return showError(new Error("Body is not JSON")) }
|
|
2183
|
-
}
|
|
2184
|
-
try {
|
|
2185
|
-
const data = await api(method, concrete, body)
|
|
2186
|
-
$("route-result").textContent = JSON.stringify(data, null, 2)
|
|
2187
|
-
} catch (error) {
|
|
2188
|
-
showError(error)
|
|
2189
|
-
}
|
|
2190
|
-
})
|
|
2191
|
-
|
|
2192
|
-
function mountPanels(panels) {
|
|
2193
|
-
const nav = $("nav")
|
|
2194
|
-
const views = $("views")
|
|
2195
|
-
for (const panel of panels) {
|
|
2196
|
-
const button = document.createElement("button")
|
|
2197
|
-
button.type = "button"
|
|
2198
|
-
button.dataset.view = panel.id
|
|
2199
|
-
button.textContent = panel.title
|
|
2200
|
-
nav.append(button)
|
|
2201
|
-
const section = document.createElement("section")
|
|
2202
|
-
section.className = "view"
|
|
2203
|
-
section.id = "view-" + panel.id
|
|
2204
|
-
section.hidden = true
|
|
2205
|
-
const title = document.createElement("h2")
|
|
2206
|
-
title.textContent = panel.title
|
|
2207
|
-
const lede = document.createElement("p")
|
|
2208
|
-
lede.className = "lede"
|
|
2209
|
-
lede.textContent = panel.description || ""
|
|
2210
|
-
const body = document.createElement("div")
|
|
2211
|
-
body.className = "panel-body"
|
|
2212
|
-
body.id = "panel-" + panel.id
|
|
2213
|
-
body.innerHTML = panel.html || ""
|
|
2214
|
-
section.append(title, lede, body)
|
|
2215
|
-
views.append(section)
|
|
2216
|
-
if (panel.script) {
|
|
2217
|
-
try {
|
|
2218
|
-
const run = new Function("root", "api", panel.script)
|
|
2219
|
-
run(body, panelApi)
|
|
2220
|
-
} catch (error) {
|
|
2221
|
-
const note = document.createElement("p")
|
|
2222
|
-
note.className = "banner show"
|
|
2223
|
-
note.textContent = error instanceof Error ? error.message : String(error)
|
|
2224
|
-
body.append(note)
|
|
2225
|
-
}
|
|
2226
|
-
}
|
|
2227
|
-
}
|
|
2228
|
-
}
|
|
2229
|
-
|
|
2230
|
-
loadNamespaces()
|
|
2231
|
-
.then(() => api("GET", "/ui/manifest"))
|
|
2232
|
-
.then((manifest) => { mountPanels((manifest && manifest.panels) || []); return refresh() })
|
|
2233
|
-
.catch(showError)
|
|
2234
|
-
})()
|
|
2235
|
-
</script>
|
|
3036
|
+
${script}
|
|
2236
3037
|
</body>
|
|
2237
3038
|
</html>`;
|
|
2238
3039
|
};
|
|
@@ -2249,10 +3050,10 @@ var json2 = (body) => new Response(JSON.stringify(body), {
|
|
|
2249
3050
|
});
|
|
2250
3051
|
var adminUiRoutes = (service, ui) => {
|
|
2251
3052
|
const shell = () => renderAdminDocument(service);
|
|
2252
|
-
const
|
|
3053
|
+
const document3 = () => ui?.render ? ui.render({ service, defaultHtml: shell }) : shell();
|
|
2253
3054
|
return {
|
|
2254
|
-
"GET /ui": () => htmlResponse(
|
|
2255
|
-
"GET /ui/": () => htmlResponse(
|
|
3055
|
+
"GET /ui": () => htmlResponse(document3()),
|
|
3056
|
+
"GET /ui/": () => htmlResponse(document3()),
|
|
2256
3057
|
"GET /ui/manifest": () => json2({
|
|
2257
3058
|
service,
|
|
2258
3059
|
panels: (ui?.panels ?? []).map((panel) => ({
|
|
@@ -2278,11 +3079,11 @@ var EXTENSION_KEYS = {
|
|
|
2278
3079
|
};
|
|
2279
3080
|
|
|
2280
3081
|
// ../../openapi/metadata/dist/read.js
|
|
2281
|
-
var
|
|
3082
|
+
var isRecord3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2282
3083
|
var extensionOf = (holder, key) => holder[key];
|
|
2283
3084
|
var operationMetadata = (operation) => {
|
|
2284
3085
|
const raw = extensionOf(operation, EXTENSION_KEYS.operation);
|
|
2285
|
-
const ext =
|
|
3086
|
+
const ext = isRecord3(raw) ? raw : {};
|
|
2286
3087
|
const supported = ext.supported ?? true;
|
|
2287
3088
|
const parity = ext.parity ?? {};
|
|
2288
3089
|
return {
|
|
@@ -2402,9 +3203,9 @@ ${problems.map((p) => ` - ${p}`).join("\n")}`);
|
|
|
2402
3203
|
this.name = "OperationRegistryError";
|
|
2403
3204
|
}
|
|
2404
3205
|
};
|
|
2405
|
-
var verifyOperations = (
|
|
3206
|
+
var verifyOperations = (document3, handlers) => {
|
|
2406
3207
|
const problems = [];
|
|
2407
|
-
const operations = listOperations(
|
|
3208
|
+
const operations = listOperations(document3);
|
|
2408
3209
|
const seen = /* @__PURE__ */ new Set();
|
|
2409
3210
|
for (const operation of operations) {
|
|
2410
3211
|
if (seen.has(operation.operationId))
|
|
@@ -2520,7 +3321,7 @@ var SAMPLE = 25;
|
|
|
2520
3321
|
var WALK_LIMIT = 1e4;
|
|
2521
3322
|
var SKIP_CTORS = /* @__PURE__ */ new Set(["Hono", "HonoRequest", "Router"]);
|
|
2522
3323
|
var fail = (status, message) => ({ ok: false, status, message });
|
|
2523
|
-
var
|
|
3324
|
+
var isRecord4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2524
3325
|
var kindOf = (value) => {
|
|
2525
3326
|
if (value === null)
|
|
2526
3327
|
return "null";
|
|
@@ -2619,7 +3420,7 @@ var absorb = (accum, value) => {
|
|
|
2619
3420
|
const kind = kindOf(value);
|
|
2620
3421
|
accum.kinds.add(kind);
|
|
2621
3422
|
accum.present++;
|
|
2622
|
-
if (!
|
|
3423
|
+
if (!isRecord4(value))
|
|
2623
3424
|
return;
|
|
2624
3425
|
for (const [key, child] of Object.entries(value)) {
|
|
2625
3426
|
const nested = accum.nested.get(key) ?? blankAccum(key);
|
|
@@ -2644,7 +3445,7 @@ var inferFields = (records) => {
|
|
|
2644
3445
|
if (records.length === 0)
|
|
2645
3446
|
return [];
|
|
2646
3447
|
const sample = records.slice(0, SAMPLE);
|
|
2647
|
-
const objects = sample.filter((record2) =>
|
|
3448
|
+
const objects = sample.filter((record2) => isRecord4(record2.value));
|
|
2648
3449
|
if (objects.length === 0) {
|
|
2649
3450
|
const kinds = new Set(sample.map((record2) => kindOf(record2.value)));
|
|
2650
3451
|
const list = [...kinds];
|
|
@@ -2831,7 +3632,7 @@ var patchStateRecord = (scope, name, id, value) => {
|
|
|
2831
3632
|
const parsed = jsonValue(value);
|
|
2832
3633
|
if (!parsed.ok)
|
|
2833
3634
|
return parsed;
|
|
2834
|
-
const next =
|
|
3635
|
+
const next = isRecord4(current.value.value) && isRecord4(parsed.value) ? { ...current.value.value, ...parsed.value } : parsed.value;
|
|
2835
3636
|
return replaceStateRecord(scope, name, id, next);
|
|
2836
3637
|
};
|
|
2837
3638
|
var deleteStateRecord = (scope, name, id) => {
|
|
@@ -2899,7 +3700,7 @@ var stateAdminRoutes = (routes) => {
|
|
|
2899
3700
|
const scope = open(namespace);
|
|
2900
3701
|
if (!scope.ok)
|
|
2901
3702
|
return fromResult(scope, 201);
|
|
2902
|
-
if (!
|
|
3703
|
+
if (!isRecord4(body) || !("value" in body)) {
|
|
2903
3704
|
return adminError2(400, 'expected {"id?": "<id>", "value": <json>}');
|
|
2904
3705
|
}
|
|
2905
3706
|
const id = body.id;
|
|
@@ -2917,7 +3718,7 @@ var stateAdminRoutes = (routes) => {
|
|
|
2917
3718
|
const scope = open(namespace);
|
|
2918
3719
|
if (!scope.ok)
|
|
2919
3720
|
return fromResult(scope, 200);
|
|
2920
|
-
if (!
|
|
3721
|
+
if (!isRecord4(body) || !("value" in body))
|
|
2921
3722
|
return adminError2(400, 'expected {"value": <json>}');
|
|
2922
3723
|
const replaced = replaceStateRecord(scope.value, params.collection ?? "", params.id ?? "", body.value);
|
|
2923
3724
|
if (replaced.ok)
|
|
@@ -2928,7 +3729,7 @@ var stateAdminRoutes = (routes) => {
|
|
|
2928
3729
|
const scope = open(namespace);
|
|
2929
3730
|
if (!scope.ok)
|
|
2930
3731
|
return fromResult(scope, 200);
|
|
2931
|
-
if (!
|
|
3732
|
+
if (!isRecord4(body) || !("value" in body))
|
|
2932
3733
|
return adminError2(400, 'expected {"value": <json>}');
|
|
2933
3734
|
const patched = patchStateRecord(scope.value, params.collection ?? "", params.id ?? "", body.value);
|
|
2934
3735
|
if (patched.ok)
|
|
@@ -2948,7 +3749,7 @@ var stateAdminRoutes = (routes) => {
|
|
|
2948
3749
|
};
|
|
2949
3750
|
|
|
2950
3751
|
// ../core/dist/version.js
|
|
2951
|
-
var PACKAGE_VERSION = true ? "1.
|
|
3752
|
+
var PACKAGE_VERSION = true ? "1.6.0" : UNRELEASED_VERSION;
|
|
2952
3753
|
|
|
2953
3754
|
// ../core/dist/signing.js
|
|
2954
3755
|
var encoder = new TextEncoder();
|
|
@@ -3285,9 +4086,9 @@ var createWebhookHub = (options) => {
|
|
|
3285
4086
|
};
|
|
3286
4087
|
var json4 = (status, body) => new Response(JSON.stringify(body), { status, headers: { "content-type": "application/json" } });
|
|
3287
4088
|
var adminError3 = (status, message) => json4(status, { error: { type: "mockingbird_admin", message } });
|
|
3288
|
-
var
|
|
4089
|
+
var isRecord5 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3289
4090
|
var parseEndpoint = (value) => {
|
|
3290
|
-
if (!
|
|
4091
|
+
if (!isRecord5(value) || typeof value.url !== "string")
|
|
3291
4092
|
return "each endpoint needs a url";
|
|
3292
4093
|
try {
|
|
3293
4094
|
new URL(value.url);
|
|
@@ -3304,12 +4105,12 @@ var parseEndpoint = (value) => {
|
|
|
3304
4105
|
const events = value.events ?? value.enabledEvents;
|
|
3305
4106
|
if (Array.isArray(events))
|
|
3306
4107
|
endpoint.events = events.map(String);
|
|
3307
|
-
if (
|
|
4108
|
+
if (isRecord5(value.tags)) {
|
|
3308
4109
|
endpoint.tags = Object.fromEntries(Object.entries(value.tags).map(([k, v]) => [k, String(v)]));
|
|
3309
4110
|
}
|
|
3310
4111
|
if (typeof value.account === "string")
|
|
3311
4112
|
endpoint.tags = { ...endpoint.tags, account: value.account };
|
|
3312
|
-
if (
|
|
4113
|
+
if (isRecord5(value.headers)) {
|
|
3313
4114
|
endpoint.headers = Object.fromEntries(Object.entries(value.headers).map(([k, v]) => [k, String(v)]));
|
|
3314
4115
|
}
|
|
3315
4116
|
return endpoint;
|
|
@@ -3336,7 +4137,7 @@ var webhookAdminRoutes = (hub) => ({
|
|
|
3336
4137
|
return json4(200, { status: "ok" });
|
|
3337
4138
|
},
|
|
3338
4139
|
"POST /webhooks/faults": ({ body, namespace }) => {
|
|
3339
|
-
if (!
|
|
4140
|
+
if (!isRecord5(body) || !["duplicate", "reorder", "drop"].includes(String(body.mode))) {
|
|
3340
4141
|
return adminError3(400, "mode must be duplicate, reorder or drop");
|
|
3341
4142
|
}
|
|
3342
4143
|
const fault = { mode: body.mode };
|
|
@@ -3352,7 +4153,7 @@ var webhookAdminRoutes = (hub) => ({
|
|
|
3352
4153
|
}))
|
|
3353
4154
|
}),
|
|
3354
4155
|
"PUT /webhook-endpoints": ({ body, namespace }) => {
|
|
3355
|
-
const list = Array.isArray(body) ? body :
|
|
4156
|
+
const list = Array.isArray(body) ? body : isRecord5(body) ? body.endpoints : void 0;
|
|
3356
4157
|
if (!Array.isArray(list))
|
|
3357
4158
|
return adminError3(400, "expected [{url, secret?, events?}]");
|
|
3358
4159
|
const parsed = [];
|
|
@@ -3422,8 +4223,8 @@ var DroppedConnectionError = class extends TypeError {
|
|
|
3422
4223
|
this.name = "TypeError";
|
|
3423
4224
|
}
|
|
3424
4225
|
};
|
|
3425
|
-
var operationMatcher = (
|
|
3426
|
-
const matchers = listOperations(
|
|
4226
|
+
var operationMatcher = (document3) => {
|
|
4227
|
+
const matchers = listOperations(document3).map((operation) => ({
|
|
3427
4228
|
operationId: operation.operationId,
|
|
3428
4229
|
method: operation.method.toUpperCase(),
|
|
3429
4230
|
pattern: new RegExp(`^${operation.path.split("/").map((segment) => segment.startsWith("{") ? "[^/]+" : segment.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("/")}/?$`),
|
|
@@ -3518,10 +4319,10 @@ var createRuntime = (options) => {
|
|
|
3518
4319
|
const ensureBranch = (namespace, branch2, at) => {
|
|
3519
4320
|
if (!BRANCH_PATTERN2.test(branch2))
|
|
3520
4321
|
throw new RangeError(`branch must match ${BRANCH_PATTERN2}`);
|
|
3521
|
-
const
|
|
4322
|
+
const history2 = timeline(namespace);
|
|
3522
4323
|
if (branch2 === "main") {
|
|
3523
4324
|
if (at !== void 0) {
|
|
3524
|
-
const point =
|
|
4325
|
+
const point = history2.checkout("main", at);
|
|
3525
4326
|
restoreNamespace(sqlite, storageNamespace(namespace), point.value.snapshot);
|
|
3526
4327
|
captured.set(namespace, point.value.snapshot);
|
|
3527
4328
|
rng.setState(point.value.rngState);
|
|
@@ -3534,10 +4335,10 @@ var createRuntime = (options) => {
|
|
|
3534
4335
|
return namespace;
|
|
3535
4336
|
}
|
|
3536
4337
|
const storage = physicalBranch(namespace, branch2);
|
|
3537
|
-
if (!
|
|
4338
|
+
if (!history2.hasBranch(branch2)) {
|
|
3538
4339
|
if (at === void 0)
|
|
3539
|
-
|
|
3540
|
-
const point =
|
|
4340
|
+
history2.commit(capture(namespace));
|
|
4341
|
+
const point = history2.fork(branch2, at === void 0 ? {} : { from: at });
|
|
3541
4342
|
const branchRng = createRng(options.seed ?? 0);
|
|
3542
4343
|
if (point)
|
|
3543
4344
|
branchRng.setState(point.value.rngState);
|
|
@@ -3546,8 +4347,8 @@ var createRuntime = (options) => {
|
|
|
3546
4347
|
restoreNamespace(sqlite, storageNamespace(storage), point.value.snapshot);
|
|
3547
4348
|
if (point)
|
|
3548
4349
|
captured.set(storage, point.value.snapshot);
|
|
3549
|
-
} else if (at !== void 0 &&
|
|
3550
|
-
const point =
|
|
4350
|
+
} else if (at !== void 0 && history2.head(branch2)?.id !== at) {
|
|
4351
|
+
const point = history2.checkout(branch2, at);
|
|
3551
4352
|
if (!instances.has(storage)) {
|
|
3552
4353
|
const branchRng = createRng(options.seed ?? 0);
|
|
3553
4354
|
branchRng.setState(point.value.rngState);
|
|
@@ -3558,7 +4359,7 @@ var createRuntime = (options) => {
|
|
|
3558
4359
|
captured.set(storage, point.value.snapshot);
|
|
3559
4360
|
} else {
|
|
3560
4361
|
if (!instances.has(storage)) {
|
|
3561
|
-
const point =
|
|
4362
|
+
const point = history2.head(branch2);
|
|
3562
4363
|
const branchRng = createRng(options.seed ?? 0);
|
|
3563
4364
|
if (point)
|
|
3564
4365
|
branchRng.setState(point.value.rngState);
|
|
@@ -3582,8 +4383,8 @@ var createRuntime = (options) => {
|
|
|
3582
4383
|
const checkout = (checkpointId, checkoutOptions = {}) => {
|
|
3583
4384
|
const namespace = checkoutOptions.namespace ?? DEFAULT_NAMESPACE;
|
|
3584
4385
|
const branchName = checkoutOptions.branch ?? "main";
|
|
3585
|
-
const
|
|
3586
|
-
const point =
|
|
4386
|
+
const history2 = timeline(namespace);
|
|
4387
|
+
const point = history2.checkout(branchName, checkpointId);
|
|
3587
4388
|
const storage = ensureBranch(namespace, branchName);
|
|
3588
4389
|
restoreNamespace(sqlite, storageNamespace(storage), point.value.snapshot);
|
|
3589
4390
|
captured.set(storage, point.value.snapshot);
|
|
@@ -3633,9 +4434,9 @@ var createRuntime = (options) => {
|
|
|
3633
4434
|
instance(name);
|
|
3634
4435
|
restoreNamespace(sqlite, storageNamespace(name), from);
|
|
3635
4436
|
captured.set(name, from);
|
|
3636
|
-
const
|
|
3637
|
-
if (
|
|
3638
|
-
|
|
4437
|
+
const history2 = timelines.get(name);
|
|
4438
|
+
if (history2)
|
|
4439
|
+
history2.commit(capture(name), { branch: "main" });
|
|
3639
4440
|
else
|
|
3640
4441
|
timeline(name);
|
|
3641
4442
|
};
|
|
@@ -3864,10 +4665,10 @@ var createRuntime = (options) => {
|
|
|
3864
4665
|
},
|
|
3865
4666
|
release: (name, checkpointId) => timeline(name).release(checkpointId),
|
|
3866
4667
|
inspect: (name) => {
|
|
3867
|
-
const
|
|
4668
|
+
const history2 = timeline(name);
|
|
3868
4669
|
return {
|
|
3869
|
-
branches:
|
|
3870
|
-
checkpoints:
|
|
4670
|
+
branches: history2.branches(),
|
|
4671
|
+
checkpoints: history2.checkpoints().map(({ id, branch: branchName, parent, at }) => ({
|
|
3871
4672
|
id,
|
|
3872
4673
|
branch: branchName,
|
|
3873
4674
|
parent,
|
|
@@ -3882,11 +4683,11 @@ var createRuntime = (options) => {
|
|
|
3882
4683
|
} : {},
|
|
3883
4684
|
routes: {
|
|
3884
4685
|
...credentialRoutes(credentials),
|
|
3885
|
-
...options.presets ? presetRoutes(options.presets, runtime) : {},
|
|
3886
4686
|
...options.webhooks ? webhookAdminRoutes(options.webhooks) : {},
|
|
3887
4687
|
...options.admin?.(runtime) ?? {},
|
|
3888
|
-
//
|
|
3889
|
-
//
|
|
4688
|
+
// Shell routes win a colliding key. Fault presets are one of them: the admin page
|
|
4689
|
+
// maps `presets`, and a service route must not replace that list with a record.
|
|
4690
|
+
...options.presets ? presetRoutes(options.presets, runtime) : {},
|
|
3890
4691
|
...stateAdminRoutes({
|
|
3891
4692
|
open: stateScope,
|
|
3892
4693
|
afterWrite: (namespace) => {
|
|
@@ -3961,9 +4762,12 @@ var credentialRoutes = (registry) => ({
|
|
|
3961
4762
|
}
|
|
3962
4763
|
});
|
|
3963
4764
|
var presetRoutes = (presets, runtime) => ({
|
|
3964
|
-
"GET /faults/presets": () =>
|
|
3965
|
-
|
|
3966
|
-
|
|
4765
|
+
"GET /faults/presets": () => {
|
|
4766
|
+
const body = {
|
|
4767
|
+
presets: Object.entries(presets).map(([name, preset]) => ({ name, ...preset }))
|
|
4768
|
+
};
|
|
4769
|
+
return adminJson(200, body);
|
|
4770
|
+
},
|
|
3967
4771
|
"POST /faults/presets/:name": ({ params, body, namespace }) => {
|
|
3968
4772
|
const name = params.name;
|
|
3969
4773
|
if (!presets[name])
|
|
@@ -3974,7 +4778,7 @@ var presetRoutes = (presets, runtime) => ({
|
|
|
3974
4778
|
});
|
|
3975
4779
|
|
|
3976
4780
|
// src/generated/openapi.ts
|
|
3977
|
-
var document = JSON.parse(`{"openapi":"3.1.0","info":{"title":"RxVortex (Strive) pharmacy API (Mockingbird subset)","description":"Stateful mock subset of the RxVortex / Strive compounding-pharmacy API: client-credentials\\ntoken, order submit, status, cancel, lookup by sender order id (recovery), and the preset\\ncatalog. Hand-authored from the consumer's adapters (the vendor publishes no spec).\\n","version":"1","x-mockingbird-upstream":{"note":"Hand-authored from the wire shapes our consumer reads and writes (rxvortex-auth.service.ts, rxvortex-fulfillment.adapter.ts, rxvortex-live.client.ts)."}},"servers":[{"url":"https://sandbox.rxvortex.com"}],"security":[{"bearerAuth":[]}],"paths":{"/api/v1/generate-access-token":{"post":{"operationId":"GenerateAccessToken","security":[],"x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["client_id","client_secret"],"properties":{"client_id":{"type":"string","minLength":1,"maxLength":64},"client_secret":{"type":"string","minLength":1,"maxLength":64}}}}}},"responses":{"200":{"description":"Access token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenResponse"}}}},"401":{"description":"Bad credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}}}}},"/api/v1/orders":{"post":{"operationId":"CreateOrder","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":false}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateOrderBody"}}}},"responses":{"200":{"description":"Order accepted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateOrderResponse"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"409":{"description":"Duplicate sender_order_id","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}}}}},"/api/v1/orders/{orderId}":{"parameters":[{"name":"orderId","in":"path","required":true,"description":"The vendor order tracking id, or the sender order id (our payment id).","schema":{"type":"string","x-mockingbird-resource-ref":{"type":"order","missing":"RXV-00000000-MISSING"}}}],"get":{"operationId":"GetOrder","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"responses":{"200":{"description":"Order status","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderStatus"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"Unknown order","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}}},"delete":{"operationId":"CancelOrder","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":false}},"responses":{"200":{"description":"Cancelled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CancelResponse"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"Unknown order","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"409":{"description":"Not cancellable in its current status","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}}}},"/api/v1/preset-catalog-items":{"get":{"operationId":"ListPresetCatalogItems","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"responses":{"200":{"description":"Preset catalog","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CatalogItem"}}}}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer"}},"schemas":{"TokenResponse":{"type":"object","required":["access_token","token_type","expires_in"],"properties":{"access_token":{"type":"string","x-mockingbird-volatile":{"kind":"token"}},"token_type":{"type":"string","enum":["Bearer"]},"expires_in":{"type":"integer"}}},"Address":{"type":"object","required":["line1","city","state","postal_code","country"],"properties":{"line1":{"type":"string","minLength":1,"maxLength":80},"line2":{"type":"string","maxLength":80},"city":{"type":"string","minLength":1,"maxLength":40},"state":{"type":"string","pattern":"^[A-Z]{2}$"},"postal_code":{"type":"string","pattern":"^[0-9]{5}(-[0-9]{4})?$"},"country":{"type":"string","enum":["US"]}}},"Patient":{"type":"object","required":["sender_patient_id","first_name","last_name","dob","gender","email","phone","address"],"properties":{"sender_patient_id":{"type":"string","minLength":1,"maxLength":64},"first_name":{"type":"string","minLength":1,"maxLength":40},"last_name":{"type":"string","minLength":1,"maxLength":40},"dob":{"type":"string","pattern":"^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$"},"gender":{"type":"string","enum":["male","female"]},"email":{"type":"string","format":"email","maxLength":120},"phone":{"type":"string","pattern":"^[0-9]{3}-[0-9]{3}-[0-9]{4}$"},"address":{"$ref":"#/components/schemas/Address"}}},"Prescriber":{"type":"object","required":["first_name","last_name","npi"],"properties":{"first_name":{"type":"string","minLength":1,"maxLength":40},"last_name":{"type":"string","minLength":1,"maxLength":40},"npi":{"type":"string","pattern":"^[0-9]{10}$"},"dea_number":{"type":"string","maxLength":20},"license_number":{"type":"string","maxLength":20},"license_state":{"type":"string","maxLength":2},"phone":{"type":"string","maxLength":20},"address":{"$ref":"#/components/schemas/Address"}}},"MedicationRequest":{"type":"object","required":["type","preset_catalog_id","sender_med_request_id","medication_name","quantity","quantity_units","days_supply_duration","refills","instructions"],"properties":{"type":{"type":"string","enum":["new"]},"preset_catalog_id":{"type":"string","x-mockingbird-resource-ref":{"type":"catalog_item","missing":"00000000-0000-4000-8000-000000000000"}},"sender_med_request_id":{"type":"string","minLength":1,"maxLength":64},"medication_name":{"type":"string","minLength":1,"maxLength":120},"medication_strength":{"type":"string","maxLength":60},"medication_form":{"type":"string","maxLength":40},"quantity":{"type":"number","exclusiveMinimum":0,"maximum":1000},"quantity_units":{"type":"string","enum":["mL","grams","each"]},"days_supply_duration":{"type":"integer","minimum":1,"maximum":365},"refills":{"type":"integer","minimum":0,"maximum":12},"instructions":{"type":"string","minLength":1,"maxLength":500},"note":{"type":"string","maxLength":1000},"authored_on_datetime":{"type":"string","maxLength":40},"clinical_difference":{"type":"string","maxLength":1000},"schedule_code":{"type":"string","enum":["2","3","4","5"]}}},"ClinicalList":{"type":"object","properties":{"has_known_allergies":{"type":"boolean"},"has_known_diseases":{"type":"boolean"},"has_known_medications":{"type":"boolean"},"entries":{"type":"array","maxItems":5,"items":{"type":"object","required":["description"],"properties":{"description":{"type":"string","maxLength":200}}}}}},"CreateOrderBody":{"type":"object","required":["patient","prescriber","order","medication_requests"],"properties":{"patient":{"$ref":"#/components/schemas/Patient"},"prescriber":{"$ref":"#/components/schemas/Prescriber"},"order":{"type":"object","required":["bill_to","ship_to","sender_order_id"],"properties":{"bill_to":{"type":"string","enum":["practice","patient"]},"ship_to":{"type":"string","enum":["patient","practice"]},"sender_order_id":{"type":"string","minLength":1,"maxLength":64}}},"medication_requests":{"type":"array","minItems":1,"maxItems":3,"items":{"$ref":"#/components/schemas/MedicationRequest"}},"clinical":{"type":"object","properties":{"allergies":{"$ref":"#/components/schemas/ClinicalList"},"diseases":{"$ref":"#/components/schemas/ClinicalList"},"medications":{"$ref":"#/components/schemas/ClinicalList"}}},"shipment":{"type":"object","properties":{"recipient_first_name":{"type":"string","maxLength":40},"recipient_last_name":{"type":"string","maxLength":40},"recipient_email":{"type":"string","maxLength":80},"recipient_phone":{"type":"string","maxLength":20},"address":{"$ref":"#/components/schemas/Address"}}}}},"CreateOrderResponse":{"type":"object","required":["success","message","order_tracking_id","sender_order_id","status"],"properties":{"success":{"type":"boolean"},"message":{"type":"string"},"order_tracking_id":{"type":"string","x-mockingbird-resource":{"type":"order","identity":true},"x-mockingbird-volatile":{"kind":"id"}},"sender_order_id":{"type":"string"},"status":{"type":"string"}}},"OrderStatus":{"type":"object","required":["order_tracking_id","tracking_id","orderReferenceID","sender_order_id","rxstatus","orderstatus","shipping_status","cancellable","created_at","updated_at"],"properties":{"order_tracking_id":{"type":"string","x-mockingbird-volatile":{"kind":"id"}},"tracking_id":{"type":"string","x-mockingbird-volatile":{"kind":"id"}},"orderReferenceID":{"type":"string","x-mockingbird-volatile":{"kind":"id"}},"sender_order_id":{"type":"string"},"rxstatus":{"type":"string"},"orderstatus":{"type":"string"},"shipping_status":{"type":"string"},"delivered_date":{"type":["string","null"]},"trackingnumber":{"type":["string","null"]},"shippingservice":{"type":["string","null"]},"shippingcarrier":{"type":["string","null"]},"shipmenttrackingurl":{"type":["string","null"]},"cancellable":{"type":"boolean"},"created_at":{"type":"string","x-mockingbird-volatile":{"kind":"timestamp"}},"updated_at":{"type":"string","x-mockingbird-volatile":{"kind":"timestamp"}}}},"CancelResponse":{"type":"object","required":["success","message","order_tracking_id","rxstatus"],"properties":{"success":{"type":"boolean"},"message":{"type":"string"},"order_tracking_id":{"type":"string","x-mockingbird-volatile":{"kind":"id"}},"rxstatus":{"type":"string"}}},"CatalogItem":{"type":"object","required":["catalog_id","medication_name","status"],"properties":{"catalog_id":{"type":"string","x-mockingbird-resource":{"type":"catalog_item","identity":true}},"medication_name":{"type":"string"},"medication_strength":{"type":["string","null"]},"package_size":{"type":["string","null"]},"quantity":{"type":["number","null"]},"quantity_units":{"type":["string","null"]},"medication_form":{"type":["string","null"]},"route":{"type":["string","null"]},"states":{"type":"array","items":{"type":"string"}},"status":{"type":"string","enum":["active","inactive"]}}},"ErrorBody":{"type":"object","required":["message"],"properties":{"message":{"type":"string"},"success":{"type":"boolean"}}},"ValidationError":{"type":"object","required":["message","errors"],"properties":{"message":{"type":"string"},"errors":{"oneOf":[{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}},{"type":"array","items":{"type":"object"}}]}}}}}}`);
|
|
4781
|
+
var document2 = JSON.parse(`{"openapi":"3.1.0","info":{"title":"RxVortex (Strive) pharmacy API (Mockingbird subset)","description":"Stateful mock subset of the RxVortex / Strive compounding-pharmacy API: client-credentials\\ntoken, order submit, status, cancel, lookup by sender order id (recovery), and the preset\\ncatalog. Hand-authored from the consumer's adapters (the vendor publishes no spec).\\n","version":"1","x-mockingbird-upstream":{"note":"Hand-authored from the wire shapes our consumer reads and writes (rxvortex-auth.service.ts, rxvortex-fulfillment.adapter.ts, rxvortex-live.client.ts)."}},"servers":[{"url":"https://sandbox.rxvortex.com"}],"security":[{"bearerAuth":[]}],"paths":{"/api/v1/generate-access-token":{"post":{"operationId":"GenerateAccessToken","security":[],"x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["client_id","client_secret"],"properties":{"client_id":{"type":"string","minLength":1,"maxLength":64},"client_secret":{"type":"string","minLength":1,"maxLength":64}}}}}},"responses":{"200":{"description":"Access token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenResponse"}}}},"401":{"description":"Bad credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}}}}},"/api/v1/orders":{"post":{"operationId":"CreateOrder","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":false}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateOrderBody"}}}},"responses":{"200":{"description":"Order accepted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateOrderResponse"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"409":{"description":"Duplicate sender_order_id","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}}}}},"/api/v1/orders/{orderId}":{"parameters":[{"name":"orderId","in":"path","required":true,"description":"The vendor order tracking id, or the sender order id (our payment id).","schema":{"type":"string","x-mockingbird-resource-ref":{"type":"order","missing":"RXV-00000000-MISSING"}}}],"get":{"operationId":"GetOrder","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"responses":{"200":{"description":"Order status","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderStatus"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"Unknown order","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}}},"delete":{"operationId":"CancelOrder","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":false}},"responses":{"200":{"description":"Cancelled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CancelResponse"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"Unknown order","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"409":{"description":"Not cancellable in its current status","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}}}},"/api/v1/preset-catalog-items":{"get":{"operationId":"ListPresetCatalogItems","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"responses":{"200":{"description":"Preset catalog","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CatalogItem"}}}}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer"}},"schemas":{"TokenResponse":{"type":"object","required":["access_token","token_type","expires_in"],"properties":{"access_token":{"type":"string","x-mockingbird-volatile":{"kind":"token"}},"token_type":{"type":"string","enum":["Bearer"]},"expires_in":{"type":"integer"}}},"Address":{"type":"object","required":["line1","city","state","postal_code","country"],"properties":{"line1":{"type":"string","minLength":1,"maxLength":80},"line2":{"type":"string","maxLength":80},"city":{"type":"string","minLength":1,"maxLength":40},"state":{"type":"string","pattern":"^[A-Z]{2}$"},"postal_code":{"type":"string","pattern":"^[0-9]{5}(-[0-9]{4})?$"},"country":{"type":"string","enum":["US"]}}},"Patient":{"type":"object","required":["sender_patient_id","first_name","last_name","dob","gender","email","phone","address"],"properties":{"sender_patient_id":{"type":"string","minLength":1,"maxLength":64},"first_name":{"type":"string","minLength":1,"maxLength":40},"last_name":{"type":"string","minLength":1,"maxLength":40},"dob":{"type":"string","pattern":"^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$"},"gender":{"type":"string","enum":["male","female"]},"email":{"type":"string","format":"email","maxLength":120},"phone":{"type":"string","pattern":"^[0-9]{3}-[0-9]{3}-[0-9]{4}$"},"address":{"$ref":"#/components/schemas/Address"}}},"Prescriber":{"type":"object","required":["first_name","last_name","npi"],"properties":{"first_name":{"type":"string","minLength":1,"maxLength":40},"last_name":{"type":"string","minLength":1,"maxLength":40},"npi":{"type":"string","pattern":"^[0-9]{10}$"},"dea_number":{"type":"string","maxLength":20},"license_number":{"type":"string","maxLength":20},"license_state":{"type":"string","maxLength":2},"phone":{"type":"string","maxLength":20},"address":{"$ref":"#/components/schemas/Address"}}},"MedicationRequest":{"type":"object","required":["type","preset_catalog_id","sender_med_request_id","medication_name","quantity","quantity_units","days_supply_duration","refills","instructions"],"properties":{"type":{"type":"string","enum":["new"]},"preset_catalog_id":{"type":"string","x-mockingbird-resource-ref":{"type":"catalog_item","missing":"00000000-0000-4000-8000-000000000000"}},"sender_med_request_id":{"type":"string","minLength":1,"maxLength":64},"medication_name":{"type":"string","minLength":1,"maxLength":120},"medication_strength":{"type":"string","maxLength":60},"medication_form":{"type":"string","maxLength":40},"quantity":{"type":"number","exclusiveMinimum":0,"maximum":1000},"quantity_units":{"type":"string","enum":["mL","grams","each"]},"days_supply_duration":{"type":"integer","minimum":1,"maximum":365},"refills":{"type":"integer","minimum":0,"maximum":12},"instructions":{"type":"string","minLength":1,"maxLength":500},"note":{"type":"string","maxLength":1000},"authored_on_datetime":{"type":"string","maxLength":40},"clinical_difference":{"type":"string","maxLength":1000},"schedule_code":{"type":"string","enum":["2","3","4","5"]}}},"ClinicalList":{"type":"object","properties":{"has_known_allergies":{"type":"boolean"},"has_known_diseases":{"type":"boolean"},"has_known_medications":{"type":"boolean"},"entries":{"type":"array","maxItems":5,"items":{"type":"object","required":["description"],"properties":{"description":{"type":"string","maxLength":200}}}}}},"CreateOrderBody":{"type":"object","required":["patient","prescriber","order","medication_requests"],"properties":{"patient":{"$ref":"#/components/schemas/Patient"},"prescriber":{"$ref":"#/components/schemas/Prescriber"},"order":{"type":"object","required":["bill_to","ship_to","sender_order_id"],"properties":{"bill_to":{"type":"string","enum":["practice","patient"]},"ship_to":{"type":"string","enum":["patient","practice"]},"sender_order_id":{"type":"string","minLength":1,"maxLength":64}}},"medication_requests":{"type":"array","minItems":1,"maxItems":3,"items":{"$ref":"#/components/schemas/MedicationRequest"}},"clinical":{"type":"object","properties":{"allergies":{"$ref":"#/components/schemas/ClinicalList"},"diseases":{"$ref":"#/components/schemas/ClinicalList"},"medications":{"$ref":"#/components/schemas/ClinicalList"}}},"shipment":{"type":"object","properties":{"recipient_first_name":{"type":"string","maxLength":40},"recipient_last_name":{"type":"string","maxLength":40},"recipient_email":{"type":"string","maxLength":80},"recipient_phone":{"type":"string","maxLength":20},"address":{"$ref":"#/components/schemas/Address"}}}}},"CreateOrderResponse":{"type":"object","required":["success","message","order_tracking_id","sender_order_id","status"],"properties":{"success":{"type":"boolean"},"message":{"type":"string"},"order_tracking_id":{"type":"string","x-mockingbird-resource":{"type":"order","identity":true},"x-mockingbird-volatile":{"kind":"id"}},"sender_order_id":{"type":"string"},"status":{"type":"string"}}},"OrderStatus":{"type":"object","required":["order_tracking_id","tracking_id","orderReferenceID","sender_order_id","rxstatus","orderstatus","shipping_status","cancellable","created_at","updated_at"],"properties":{"order_tracking_id":{"type":"string","x-mockingbird-volatile":{"kind":"id"}},"tracking_id":{"type":"string","x-mockingbird-volatile":{"kind":"id"}},"orderReferenceID":{"type":"string","x-mockingbird-volatile":{"kind":"id"}},"sender_order_id":{"type":"string"},"rxstatus":{"type":"string"},"orderstatus":{"type":"string"},"shipping_status":{"type":"string"},"delivered_date":{"type":["string","null"]},"trackingnumber":{"type":["string","null"]},"shippingservice":{"type":["string","null"]},"shippingcarrier":{"type":["string","null"]},"shipmenttrackingurl":{"type":["string","null"]},"cancellable":{"type":"boolean"},"created_at":{"type":"string","x-mockingbird-volatile":{"kind":"timestamp"}},"updated_at":{"type":"string","x-mockingbird-volatile":{"kind":"timestamp"}}}},"CancelResponse":{"type":"object","required":["success","message","order_tracking_id","rxstatus"],"properties":{"success":{"type":"boolean"},"message":{"type":"string"},"order_tracking_id":{"type":"string","x-mockingbird-volatile":{"kind":"id"}},"rxstatus":{"type":"string"}}},"CatalogItem":{"type":"object","required":["catalog_id","medication_name","status"],"properties":{"catalog_id":{"type":"string","x-mockingbird-resource":{"type":"catalog_item","identity":true}},"medication_name":{"type":"string"},"medication_strength":{"type":["string","null"]},"package_size":{"type":["string","null"]},"quantity":{"type":["number","null"]},"quantity_units":{"type":["string","null"]},"medication_form":{"type":["string","null"]},"route":{"type":["string","null"]},"states":{"type":"array","items":{"type":"string"}},"status":{"type":"string","enum":["active","inactive"]}}},"ErrorBody":{"type":"object","required":["message"],"properties":{"message":{"type":"string"},"success":{"type":"boolean"}}},"ValidationError":{"type":"object","required":["message","errors"],"properties":{"message":{"type":"string"},"errors":{"oneOf":[{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}},{"type":"array","items":{"type":"object"}}]}}}}}}`);
|
|
3978
4782
|
var operationIds = ["GenerateAccessToken", "CreateOrder", "GetOrder", "CancelOrder", "ListPresetCatalogItems"];
|
|
3979
4783
|
var supportedOperationIds = ["GenerateAccessToken", "CreateOrder", "GetOrder", "CancelOrder", "ListPresetCatalogItems"];
|
|
3980
4784
|
|
|
@@ -4061,7 +4865,7 @@ var DEFAULT_CATALOG = [
|
|
|
4061
4865
|
status: "inactive"
|
|
4062
4866
|
}
|
|
4063
4867
|
];
|
|
4064
|
-
var
|
|
4868
|
+
var isRecord6 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4065
4869
|
var optionalString = (row, key) => {
|
|
4066
4870
|
const value = row[key];
|
|
4067
4871
|
if (value === void 0 || value === null) return null;
|
|
@@ -4069,7 +4873,7 @@ var optionalString = (row, key) => {
|
|
|
4069
4873
|
return value;
|
|
4070
4874
|
};
|
|
4071
4875
|
var parseCatalogItem = (value) => {
|
|
4072
|
-
if (!
|
|
4876
|
+
if (!isRecord6(value)) throw new Error("a catalog row must be an object");
|
|
4073
4877
|
const { catalog_id, medication_name, quantity, states, status } = value;
|
|
4074
4878
|
if (typeof catalog_id !== "string" || catalog_id.length === 0) {
|
|
4075
4879
|
throw new Error("catalog_id must be a non-empty string");
|
|
@@ -4104,7 +4908,7 @@ var parseCatalogItem = (value) => {
|
|
|
4104
4908
|
}
|
|
4105
4909
|
};
|
|
4106
4910
|
var parseCatalog = (value) => {
|
|
4107
|
-
const rows = Array.isArray(value) ? value :
|
|
4911
|
+
const rows = Array.isArray(value) ? value : isRecord6(value) ? value.data : void 0;
|
|
4108
4912
|
if (!Array.isArray(rows)) throw new Error("a catalog must be an array or {data: [...]}");
|
|
4109
4913
|
return rows.map(parseCatalogItem);
|
|
4110
4914
|
};
|
|
@@ -4262,10 +5066,10 @@ var RXVORTEX_PRESETS = {
|
|
|
4262
5066
|
};
|
|
4263
5067
|
var json5 = (status, body) => new Response(JSON.stringify(body), { status, headers: { "content-type": "application/json" } });
|
|
4264
5068
|
var adminError4 = (status, message) => json5(status, { error: { type: "mockingbird_admin", message } });
|
|
4265
|
-
var
|
|
5069
|
+
var isRecord7 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4266
5070
|
var parseAutoAdvance = (value) => {
|
|
4267
5071
|
if (value === null) return null;
|
|
4268
|
-
if (!
|
|
5072
|
+
if (!isRecord7(value)) return "autoAdvance must be {afterMs, path} or null";
|
|
4269
5073
|
if (typeof value.afterMs !== "number" || value.afterMs < 0)
|
|
4270
5074
|
return "autoAdvance.afterMs must be ms";
|
|
4271
5075
|
if (!Array.isArray(value.path) || value.path.some((s) => typeof s !== "string")) {
|
|
@@ -4276,7 +5080,7 @@ var parseAutoAdvance = (value) => {
|
|
|
4276
5080
|
var adminRoutes = (runtime) => ({
|
|
4277
5081
|
"GET /orders": ({ namespace }) => json5(200, { orders: runtime.instance(namespace).orders() }),
|
|
4278
5082
|
"POST /orders/:id/transition": ({ params, body, namespace }) => {
|
|
4279
|
-
if (!
|
|
5083
|
+
if (!isRecord7(body) || typeof body.to !== "string") {
|
|
4280
5084
|
return adminError4(
|
|
4281
5085
|
400,
|
|
4282
5086
|
'expected {"to": "<vendor status>", "trackingnumber"?, "shippingcarrier"?}'
|
|
@@ -4294,8 +5098,8 @@ var adminRoutes = (runtime) => ({
|
|
|
4294
5098
|
},
|
|
4295
5099
|
"GET /catalog": ({ namespace }) => json5(200, { data: runtime.instance(namespace).state.catalogRows() }),
|
|
4296
5100
|
"PUT /catalog": ({ body, namespace }) => {
|
|
4297
|
-
const rows =
|
|
4298
|
-
if (!
|
|
5101
|
+
const rows = isRecord7(body) ? body.items ?? body.data : void 0;
|
|
5102
|
+
if (!isRecord7(body) || !Array.isArray(rows)) {
|
|
4299
5103
|
return adminError4(400, 'expected {"items": [{catalog_id, medication_name, \u2026}], "mode"?}');
|
|
4300
5104
|
}
|
|
4301
5105
|
const mode = body.mode ?? "replace";
|
|
@@ -4312,7 +5116,7 @@ var adminRoutes = (runtime) => ({
|
|
|
4312
5116
|
},
|
|
4313
5117
|
"GET /settings": ({ namespace }) => json5(200, runtime.instance(namespace).state.current()),
|
|
4314
5118
|
"PUT /settings": ({ body, namespace }) => {
|
|
4315
|
-
if (!
|
|
5119
|
+
if (!isRecord7(body)) return adminError4(400, "expected a JSON object");
|
|
4316
5120
|
const patch = {};
|
|
4317
5121
|
if (body.tokenTtlSeconds !== void 0) {
|
|
4318
5122
|
if (typeof body.tokenTtlSeconds !== "number")
|
|
@@ -4326,7 +5130,7 @@ var adminRoutes = (runtime) => ({
|
|
|
4326
5130
|
if (body.clients !== void 0) {
|
|
4327
5131
|
if (!Array.isArray(body.clients))
|
|
4328
5132
|
return adminError4(400, "clients: [{client_id, client_secret}]");
|
|
4329
|
-
patch.clients = body.clients.filter(
|
|
5133
|
+
patch.clients = body.clients.filter(isRecord7).map((c) => ({
|
|
4330
5134
|
client_id: String(c.client_id),
|
|
4331
5135
|
client_secret: String(c.client_secret)
|
|
4332
5136
|
}));
|
|
@@ -4356,7 +5160,7 @@ var createRuntime2 = (options = {}) => {
|
|
|
4356
5160
|
});
|
|
4357
5161
|
const runtime = createRuntime({
|
|
4358
5162
|
name: RXVORTEX_NAMESPACE,
|
|
4359
|
-
document,
|
|
5163
|
+
document: document2,
|
|
4360
5164
|
...options.sqlite ? { sqlite: options.sqlite } : {},
|
|
4361
5165
|
...options.clock ? { clock: options.clock } : {},
|
|
4362
5166
|
...options.seed !== void 0 ? { seed: options.seed } : {},
|
|
@@ -4488,7 +5292,7 @@ var RxVortexAPI = class {
|
|
|
4488
5292
|
ListPresetCatalogItems: () => jsonRes(200, { data: this.state.catalogRows() })
|
|
4489
5293
|
});
|
|
4490
5294
|
this.service = createService({
|
|
4491
|
-
document,
|
|
5295
|
+
document: document2,
|
|
4492
5296
|
handlers,
|
|
4493
5297
|
sqlite,
|
|
4494
5298
|
namespace,
|
|
@@ -4755,7 +5559,7 @@ var RxVortexAPI = class {
|
|
|
4755
5559
|
};
|
|
4756
5560
|
|
|
4757
5561
|
export {
|
|
4758
|
-
document,
|
|
5562
|
+
document2 as document,
|
|
4759
5563
|
operationIds,
|
|
4760
5564
|
supportedOperationIds,
|
|
4761
5565
|
CUSTOM_CREAM_ANCHOR_PRESET_ID,
|
|
@@ -4768,4 +5572,4 @@ export {
|
|
|
4768
5572
|
tokenCredential,
|
|
4769
5573
|
RxVortexAPI
|
|
4770
5574
|
};
|
|
4771
|
-
//# sourceMappingURL=chunk-
|
|
5575
|
+
//# sourceMappingURL=chunk-7IB3GSYU.js.map
|