@crvouga/mockingbird-service-easypost 1.3.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -38,30 +38,30 @@ var createClock = (source = Date.now) => {
38
38
  var Collection = class {
39
39
  sqlite;
40
40
  namespace;
41
- name;
42
- constructor(sqlite, namespace, name) {
41
+ collectionName;
42
+ constructor(sqlite, namespace, collectionName) {
43
43
  this.sqlite = sqlite;
44
44
  this.namespace = namespace;
45
- this.name = name;
45
+ this.collectionName = collectionName;
46
46
  }
47
47
  bumpCollectionSeq() {
48
- const row = this.sqlite.prepare("SELECT value FROM mockingbird_sequences WHERE namespace = ? AND name = ? AND kind = 'collection'").get(this.namespace, this.name);
48
+ const row = this.sqlite.prepare("SELECT value FROM mockingbird_sequences WHERE namespace = ? AND name = ? AND kind = 'collection'").get(this.namespace, this.collectionName);
49
49
  const next = (row?.value ?? 0) + 1;
50
50
  this.sqlite.prepare(`INSERT INTO mockingbird_sequences (namespace, name, kind, value) VALUES (?, ?, 'collection', ?)
51
- ON CONFLICT(namespace, name, kind) DO UPDATE SET value = excluded.value`).run(this.namespace, this.name, next);
51
+ ON CONFLICT(namespace, name, kind) DO UPDATE SET value = excluded.value`).run(this.namespace, this.collectionName, next);
52
52
  return next;
53
53
  }
54
54
  nextSequence() {
55
55
  return this.sqlite.transaction(() => this.bumpCollectionSeq());
56
56
  }
57
57
  get(id) {
58
- const row = this.sqlite.prepare("SELECT value FROM mockingbird_records WHERE namespace = ? AND collection = ? AND id = ?").get(this.namespace, this.name, id);
58
+ const row = this.sqlite.prepare("SELECT value FROM mockingbird_records WHERE namespace = ? AND collection = ? AND id = ?").get(this.namespace, this.collectionName, id);
59
59
  if (!row)
60
60
  return void 0;
61
61
  return JSON.parse(row.value).value;
62
62
  }
63
63
  has(id) {
64
- const row = this.sqlite.prepare("SELECT 1 AS ok FROM mockingbird_records WHERE namespace = ? AND collection = ? AND id = ?").get(this.namespace, this.name, id);
64
+ const row = this.sqlite.prepare("SELECT 1 AS ok FROM mockingbird_records WHERE namespace = ? AND collection = ? AND id = ?").get(this.namespace, this.collectionName, id);
65
65
  return row !== void 0;
66
66
  }
67
67
  /** Insert a new record, assigning it the next sequence number. */
@@ -71,32 +71,32 @@ var Collection = class {
71
71
  const stored = { seq, value };
72
72
  this.sqlite.prepare(`INSERT INTO mockingbird_records (namespace, collection, id, seq, value)
73
73
  VALUES (?, ?, ?, ?, ?)
74
- ON CONFLICT(namespace, collection, id) DO UPDATE SET seq = excluded.seq, value = excluded.value`).run(this.namespace, this.name, id, seq, JSON.stringify(stored));
74
+ ON CONFLICT(namespace, collection, id) DO UPDATE SET seq = excluded.seq, value = excluded.value`).run(this.namespace, this.collectionName, id, seq, JSON.stringify(stored));
75
75
  return stored;
76
76
  });
77
77
  }
78
78
  /** Replace an existing record's value, keeping its position. */
79
79
  update(id, value) {
80
80
  return this.sqlite.transaction(() => {
81
- const row = this.sqlite.prepare("SELECT seq, value FROM mockingbird_records WHERE namespace = ? AND collection = ? AND id = ?").get(this.namespace, this.name, id);
81
+ const row = this.sqlite.prepare("SELECT seq, value FROM mockingbird_records WHERE namespace = ? AND collection = ? AND id = ?").get(this.namespace, this.collectionName, id);
82
82
  if (!row)
83
83
  return void 0;
84
84
  const stored = { seq: row.seq, value };
85
- this.sqlite.prepare("UPDATE mockingbird_records SET value = ? WHERE namespace = ? AND collection = ? AND id = ?").run(JSON.stringify(stored), this.namespace, this.name, id);
85
+ this.sqlite.prepare("UPDATE mockingbird_records SET value = ? WHERE namespace = ? AND collection = ? AND id = ?").run(JSON.stringify(stored), this.namespace, this.collectionName, id);
86
86
  return stored;
87
87
  });
88
88
  }
89
89
  delete(id) {
90
- const result = this.sqlite.prepare("DELETE FROM mockingbird_records WHERE namespace = ? AND collection = ? AND id = ?").run(this.namespace, this.name, id);
90
+ const result = this.sqlite.prepare("DELETE FROM mockingbird_records WHERE namespace = ? AND collection = ? AND id = ?").run(this.namespace, this.collectionName, id);
91
91
  return result.changes > 0;
92
92
  }
93
93
  /** How many records the collection holds, without reading them. */
94
94
  count() {
95
- const row = this.sqlite.prepare("SELECT COUNT(*) AS n FROM mockingbird_records WHERE namespace = ? AND collection = ?").get(this.namespace, this.name);
95
+ const row = this.sqlite.prepare("SELECT COUNT(*) AS n FROM mockingbird_records WHERE namespace = ? AND collection = ?").get(this.namespace, this.collectionName);
96
96
  return Number(row?.n ?? 0);
97
97
  }
98
98
  list(options = {}) {
99
- const rows = this.sqlite.prepare("SELECT id, seq, value FROM mockingbird_records WHERE namespace = ? AND collection = ?").all(this.namespace, this.name);
99
+ const rows = this.sqlite.prepare("SELECT id, seq, value FROM mockingbird_records WHERE namespace = ? AND collection = ?").all(this.namespace, this.collectionName);
100
100
  const out = [];
101
101
  for (const row of rows) {
102
102
  const stored = JSON.parse(row.value);
@@ -353,16 +353,18 @@ var createControlPlane = (context) => {
353
353
  uptimeMs: context.wallNow() - context.startedAt,
354
354
  clock: context.clock.state(),
355
355
  namespaces: context.namespaces().length,
356
+ adminUi: `${ADMIN_PREFIX}/ui`,
356
357
  ...context.describe()
357
358
  });
358
359
  }
359
360
  if (url.pathname !== ADMIN_PREFIX && !url.pathname.startsWith(`${ADMIN_PREFIX}/`)) {
360
361
  return void 0;
361
362
  }
362
- if (context.adminKey !== void 0 && request.headers.get(ADMIN_KEY_HEADER) !== context.adminKey) {
363
+ const path = url.pathname.slice(ADMIN_PREFIX.length) || "/";
364
+ const shell = request.method === "GET" && (path === "/ui" || path === "/ui/");
365
+ if (!shell && context.adminKey !== void 0 && request.headers.get(ADMIN_KEY_HEADER) !== context.adminKey) {
363
366
  return adminError(401, `missing or wrong ${ADMIN_KEY_HEADER}`);
364
367
  }
365
- const path = url.pathname.slice(ADMIN_PREFIX.length) || "/";
366
368
  for (const route of routes) {
367
369
  if (route.method !== request.method)
368
370
  continue;
@@ -549,10 +551,10 @@ var OpenAPIReferenceError = class extends Error {
549
551
  };
550
552
  var unescapePointer = (segment) => segment.replace(/~1/g, "/").replace(/~0/g, "~");
551
553
  var isReference = (value) => typeof value === "object" && value !== null && typeof value.$ref === "string";
552
- var resolveRef = (document2, ref) => {
554
+ var resolveRef = (document3, ref) => {
553
555
  if (!ref.startsWith("#/"))
554
556
  throw new OpenAPIReferenceError(ref);
555
- let cursor = document2;
557
+ let cursor = document3;
556
558
  for (const raw of ref.slice(2).split("/")) {
557
559
  const segment = unescapePointer(raw);
558
560
  if (typeof cursor !== "object" || cursor === null || !(segment in cursor)) {
@@ -564,14 +566,14 @@ var resolveRef = (document2, ref) => {
564
566
  throw new OpenAPIReferenceError(ref);
565
567
  return cursor;
566
568
  };
567
- var deref = (document2, value) => {
569
+ var deref = (document3, value) => {
568
570
  let current = value;
569
571
  const seen = /* @__PURE__ */ new Set();
570
572
  while (isReference(current)) {
571
573
  if (seen.has(current.$ref))
572
574
  throw new OpenAPIReferenceError(`${current.$ref} (cycle)`);
573
575
  seen.add(current.$ref);
574
- current = resolveRef(document2, current.$ref);
576
+ current = resolveRef(document3, current.$ref);
575
577
  }
576
578
  return current;
577
579
  };
@@ -589,36 +591,36 @@ var HTTP_METHODS = [
589
591
  ];
590
592
 
591
593
  // ../../openapi/core/dist/document.js
592
- var mergeParameters = (document2, item, own) => {
594
+ var mergeParameters = (document3, item, own) => {
593
595
  const merged = /* @__PURE__ */ new Map();
594
596
  for (const raw of item.parameters ?? []) {
595
- const parameter = deref(document2, raw);
597
+ const parameter = deref(document3, raw);
596
598
  merged.set(`${parameter.in}:${parameter.name}`, parameter);
597
599
  }
598
600
  for (const raw of own ?? []) {
599
- const parameter = deref(document2, raw);
601
+ const parameter = deref(document3, raw);
600
602
  merged.set(`${parameter.in}:${parameter.name}`, parameter);
601
603
  }
602
604
  return [...merged.values()];
603
605
  };
604
- var listOperations = (document2) => {
606
+ var listOperations = (document3) => {
605
607
  const operations = [];
606
- for (const [path, item] of Object.entries(document2.paths)) {
608
+ for (const [path, item] of Object.entries(document3.paths)) {
607
609
  for (const method of HTTP_METHODS) {
608
610
  const operation = item[method];
609
611
  if (operation?.operationId === void 0)
610
612
  continue;
611
613
  const responses = {};
612
614
  for (const [status, response] of Object.entries(operation.responses)) {
613
- responses[status] = deref(document2, response);
615
+ responses[status] = deref(document3, response);
614
616
  }
615
617
  operations.push({
616
618
  operationId: operation.operationId,
617
619
  method,
618
620
  path,
619
621
  operation,
620
- parameters: mergeParameters(document2, item, operation.parameters),
621
- requestBody: operation.requestBody === void 0 ? void 0 : deref(document2, operation.requestBody),
622
+ parameters: mergeParameters(document3, item, operation.parameters),
623
+ requestBody: operation.requestBody === void 0 ? void 0 : deref(document3, operation.requestBody),
622
624
  responses
623
625
  });
624
626
  }
@@ -627,7 +629,7 @@ var listOperations = (document2) => {
627
629
  };
628
630
 
629
631
  // ../../openapi/core/dist/schema.js
630
- var resolveSchema = (document2, schema) => {
632
+ var resolveSchema = (document3, schema) => {
631
633
  let current = schema;
632
634
  const seen = /* @__PURE__ */ new Set();
633
635
  while (typeof current.$ref === "string") {
@@ -636,7 +638,7 @@ var resolveSchema = (document2, schema) => {
636
638
  break;
637
639
  seen.add(ref);
638
640
  const { $ref: _ignored, ...siblings } = current;
639
- const target = resolveRef(document2, ref);
641
+ const target = resolveRef(document3, ref);
640
642
  current = { ...target, ...siblings };
641
643
  }
642
644
  if (current.nullable === true) {
@@ -707,71 +709,71 @@ var FORMAT_PATTERNS = {
707
709
  ipv4: /^(25[0-5]|2[0-4]\d|1?\d?\d)(\.(25[0-5]|2[0-4]\d|1?\d?\d)){3}$/
708
710
  };
709
711
  var graphemeLength = (value) => [...value].length;
710
- var validateValue = (document2, schema, value, path = []) => {
712
+ var validateValue = (document3, schema, value, path = []) => {
711
713
  const errors = [];
712
- const s = resolveSchema(document2, schema);
713
- const fail2 = (message) => errors.push({ path, message });
714
+ const s = resolveSchema(document3, schema);
715
+ const fail3 = (message) => errors.push({ path, message });
714
716
  const actual = jsonTypeOf(value);
715
717
  if (actual === "undefined") {
716
- fail2("value is undefined");
718
+ fail3("value is undefined");
717
719
  return errors;
718
720
  }
719
721
  const types = schemaTypes(s);
720
722
  if (types.length > 0) {
721
723
  const ok2 = types.some((t) => t === actual || t === "number" && actual === "integer");
722
724
  if (!ok2) {
723
- fail2(`expected type ${types.join("|")}, got ${actual}`);
725
+ fail3(`expected type ${types.join("|")}, got ${actual}`);
724
726
  return errors;
725
727
  }
726
728
  }
727
729
  if (s.enum && !(value === null && types.includes("null")) && !s.enum.some((candidate) => deepEqual(candidate, value))) {
728
- fail2("value not in enum");
730
+ fail3("value not in enum");
729
731
  }
730
732
  if (s.const !== void 0 && !deepEqual(s.const, value))
731
- fail2("value does not equal const");
733
+ fail3("value does not equal const");
732
734
  if (typeof value === "string") {
733
735
  const length = graphemeLength(value);
734
736
  if (s.minLength !== void 0 && length < s.minLength)
735
- fail2(`length ${length} < minLength ${s.minLength}`);
737
+ fail3(`length ${length} < minLength ${s.minLength}`);
736
738
  if (s.maxLength !== void 0 && length > s.maxLength)
737
- fail2(`length ${length} > maxLength ${s.maxLength}`);
739
+ fail3(`length ${length} > maxLength ${s.maxLength}`);
738
740
  if (s.pattern !== void 0) {
739
741
  try {
740
742
  if (!new RegExp(s.pattern, "u").test(value))
741
- fail2(`does not match pattern ${s.pattern}`);
743
+ fail3(`does not match pattern ${s.pattern}`);
742
744
  } catch {
743
745
  }
744
746
  }
745
747
  if (s.format !== void 0) {
746
748
  const pattern = FORMAT_PATTERNS[s.format];
747
749
  if (pattern && !pattern.test(value))
748
- fail2(`does not match format ${s.format}`);
750
+ fail3(`does not match format ${s.format}`);
749
751
  }
750
752
  }
751
753
  if (typeof value === "number") {
752
754
  if (s.minimum !== void 0 && value < s.minimum)
753
- fail2(`${value} < minimum ${s.minimum}`);
755
+ fail3(`${value} < minimum ${s.minimum}`);
754
756
  if (s.maximum !== void 0 && value > s.maximum)
755
- fail2(`${value} > maximum ${s.maximum}`);
757
+ fail3(`${value} > maximum ${s.maximum}`);
756
758
  if (s.exclusiveMinimum !== void 0 && value <= s.exclusiveMinimum)
757
- fail2(`${value} <= exclusiveMinimum ${s.exclusiveMinimum}`);
759
+ fail3(`${value} <= exclusiveMinimum ${s.exclusiveMinimum}`);
758
760
  if (s.exclusiveMaximum !== void 0 && value >= s.exclusiveMaximum)
759
- fail2(`${value} >= exclusiveMaximum ${s.exclusiveMaximum}`);
761
+ fail3(`${value} >= exclusiveMaximum ${s.exclusiveMaximum}`);
760
762
  if (s.multipleOf !== void 0 && Math.abs(value / s.multipleOf - Math.round(value / s.multipleOf)) > 1e-9) {
761
- fail2(`${value} is not a multiple of ${s.multipleOf}`);
763
+ fail3(`${value} is not a multiple of ${s.multipleOf}`);
762
764
  }
763
765
  }
764
766
  if (Array.isArray(value)) {
765
767
  if (s.minItems !== void 0 && value.length < s.minItems)
766
- fail2(`${value.length} items < minItems ${s.minItems}`);
768
+ fail3(`${value.length} items < minItems ${s.minItems}`);
767
769
  if (s.maxItems !== void 0 && value.length > s.maxItems)
768
- fail2(`${value.length} items > maxItems ${s.maxItems}`);
770
+ fail3(`${value.length} items > maxItems ${s.maxItems}`);
769
771
  if (s.uniqueItems && value.some((item, i) => value.slice(0, i).some((prev) => deepEqual(prev, item))))
770
- fail2("items are not unique");
772
+ fail3("items are not unique");
771
773
  value.forEach((item, i) => {
772
774
  const itemSchema = s.prefixItems?.[i] ?? s.items;
773
775
  if (itemSchema)
774
- errors.push(...validateValue(document2, itemSchema, item, [...path, i]));
776
+ errors.push(...validateValue(document3, itemSchema, item, [...path, i]));
775
777
  });
776
778
  }
777
779
  if (actual === "object") {
@@ -779,41 +781,41 @@ var validateValue = (document2, schema, value, path = []) => {
779
781
  const keys = Object.keys(record);
780
782
  for (const name of s.required ?? [])
781
783
  if (!(name in record))
782
- fail2(`missing required property ${name}`);
784
+ fail3(`missing required property ${name}`);
783
785
  if (s.minProperties !== void 0 && keys.length < s.minProperties)
784
- fail2(`${keys.length} properties < minProperties ${s.minProperties}`);
786
+ fail3(`${keys.length} properties < minProperties ${s.minProperties}`);
785
787
  if (s.maxProperties !== void 0 && keys.length > s.maxProperties)
786
- fail2(`${keys.length} properties > maxProperties ${s.maxProperties}`);
788
+ fail3(`${keys.length} properties > maxProperties ${s.maxProperties}`);
787
789
  for (const key of keys) {
788
790
  const property = s.properties?.[key];
789
791
  if (property) {
790
- errors.push(...validateValue(document2, property, record[key], [...path, key]));
792
+ errors.push(...validateValue(document3, property, record[key], [...path, key]));
791
793
  continue;
792
794
  }
793
795
  if (s.additionalProperties === false)
794
- fail2(`unexpected property ${key}`);
796
+ fail3(`unexpected property ${key}`);
795
797
  else if (typeof s.additionalProperties === "object") {
796
- errors.push(...validateValue(document2, s.additionalProperties, record[key], [...path, key]));
798
+ errors.push(...validateValue(document3, s.additionalProperties, record[key], [...path, key]));
797
799
  }
798
800
  if (s.propertyNames) {
799
- const nameErrors = validateValue(document2, s.propertyNames, key, [...path, key]);
801
+ const nameErrors = validateValue(document3, s.propertyNames, key, [...path, key]);
800
802
  if (nameErrors.length > 0)
801
- fail2(`property name ${key} is invalid: ${nameErrors[0]?.message}`);
803
+ fail3(`property name ${key} is invalid: ${nameErrors[0]?.message}`);
802
804
  }
803
805
  }
804
806
  }
805
807
  if (s.allOf)
806
808
  for (const branch of s.allOf)
807
- errors.push(...validateValue(document2, branch, value, path));
808
- if (s.anyOf && !s.anyOf.some((branch) => validateValue(document2, branch, value).length === 0))
809
- fail2("matches no anyOf branch");
809
+ errors.push(...validateValue(document3, branch, value, path));
810
+ if (s.anyOf && !s.anyOf.some((branch) => validateValue(document3, branch, value).length === 0))
811
+ fail3("matches no anyOf branch");
810
812
  if (s.oneOf) {
811
- const matches2 = s.oneOf.filter((branch) => validateValue(document2, branch, value).length === 0).length;
813
+ const matches2 = s.oneOf.filter((branch) => validateValue(document3, branch, value).length === 0).length;
812
814
  if (matches2 !== 1)
813
- fail2(`matches ${matches2} oneOf branches, expected exactly 1`);
815
+ fail3(`matches ${matches2} oneOf branches, expected exactly 1`);
814
816
  }
815
- if (s.not && validateValue(document2, s.not, value).length === 0)
816
- fail2("matches forbidden `not` schema");
817
+ if (s.not && validateValue(document3, s.not, value).length === 0)
818
+ fail3("matches forbidden `not` schema");
817
819
  return errors;
818
820
  };
819
821
 
@@ -1386,6 +1388,1737 @@ var clearNamespace = (sqlite, namespace) => {
1386
1388
  });
1387
1389
  };
1388
1390
 
1391
+ // ../../ui/dist/reset.js
1392
+ var CSS_RESET = `*,
1393
+ *::before,
1394
+ *::after {
1395
+ box-sizing: border-box;
1396
+ margin: 0;
1397
+ padding: 0;
1398
+ }
1399
+
1400
+ * {
1401
+ scrollbar-width: thin;
1402
+ scrollbar-color: color-mix(in srgb, CanvasText 35%, Canvas) Canvas;
1403
+ }
1404
+
1405
+ *::-webkit-scrollbar {
1406
+ width: 12px;
1407
+ height: 12px;
1408
+ }
1409
+
1410
+ *::-webkit-scrollbar-track,
1411
+ *::-webkit-scrollbar-corner {
1412
+ background: Canvas;
1413
+ }
1414
+
1415
+ *::-webkit-scrollbar-thumb {
1416
+ background: color-mix(in srgb, CanvasText 35%, Canvas);
1417
+ border: 3px solid Canvas;
1418
+ border-radius: 999px;
1419
+ }
1420
+
1421
+ html {
1422
+ hanging-punctuation: first allow-end last;
1423
+ -webkit-text-size-adjust: none;
1424
+ text-size-adjust: none;
1425
+ tab-size: 4;
1426
+ font-family:
1427
+ ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI Variable", "Segoe UI",
1428
+ "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
1429
+ font-size: 16px;
1430
+ line-height: 1.5;
1431
+ -webkit-font-smoothing: antialiased;
1432
+ -moz-osx-font-smoothing: grayscale;
1433
+ text-rendering: optimizeLegibility;
1434
+ -webkit-tap-highlight-color: transparent;
1435
+ scroll-behavior: smooth;
1436
+ background: Canvas;
1437
+ color: CanvasText;
1438
+ }
1439
+
1440
+ body {
1441
+ min-height: 100svh;
1442
+ line-height: 1.5;
1443
+ background: Canvas;
1444
+ color: CanvasText;
1445
+ }
1446
+
1447
+ h1 {
1448
+ font-size: 2em;
1449
+ }
1450
+
1451
+ h2 {
1452
+ font-size: 1.5em;
1453
+ }
1454
+
1455
+ h3 {
1456
+ font-size: 1.17em;
1457
+ }
1458
+
1459
+ h4 {
1460
+ font-size: 1em;
1461
+ }
1462
+
1463
+ h5 {
1464
+ font-size: 0.83em;
1465
+ }
1466
+
1467
+ h6 {
1468
+ font-size: 0.67em;
1469
+ }
1470
+
1471
+ h1,
1472
+ h2,
1473
+ h3,
1474
+ h4,
1475
+ h5,
1476
+ h6 {
1477
+ font-weight: 700;
1478
+ line-height: 1.2;
1479
+ text-wrap: balance;
1480
+ overflow-wrap: break-word;
1481
+ }
1482
+
1483
+ p,
1484
+ li,
1485
+ figcaption,
1486
+ dd,
1487
+ dt,
1488
+ blockquote {
1489
+ overflow-wrap: break-word;
1490
+ }
1491
+
1492
+ p,
1493
+ li,
1494
+ figcaption,
1495
+ blockquote {
1496
+ text-wrap: pretty;
1497
+ }
1498
+
1499
+ ul,
1500
+ ol,
1501
+ menu {
1502
+ list-style: none;
1503
+ }
1504
+
1505
+ a {
1506
+ color: inherit;
1507
+ text-decoration: underline;
1508
+ text-decoration-thickness: 1px;
1509
+ text-underline-offset: 0.15em;
1510
+ text-decoration-skip-ink: auto;
1511
+ }
1512
+
1513
+ b,
1514
+ strong {
1515
+ font-weight: 700;
1516
+ }
1517
+
1518
+ small {
1519
+ font-size: 0.875em;
1520
+ }
1521
+
1522
+ code,
1523
+ kbd,
1524
+ samp,
1525
+ pre {
1526
+ font-family: ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, monospace;
1527
+ font-size: 1em;
1528
+ }
1529
+
1530
+ sub,
1531
+ sup {
1532
+ font-size: 75%;
1533
+ line-height: 0;
1534
+ position: relative;
1535
+ vertical-align: baseline;
1536
+ }
1537
+
1538
+ sub {
1539
+ bottom: -0.25em;
1540
+ }
1541
+
1542
+ sup {
1543
+ top: -0.5em;
1544
+ }
1545
+
1546
+ abbr[title] {
1547
+ text-decoration: underline dotted;
1548
+ text-underline-offset: 0.15em;
1549
+ }
1550
+
1551
+ mark {
1552
+ background: Mark;
1553
+ color: MarkText;
1554
+ }
1555
+
1556
+ hr {
1557
+ height: 0;
1558
+ border: 0;
1559
+ border-top: 1px solid color-mix(in srgb, CanvasText 20%, transparent);
1560
+ color: inherit;
1561
+ }
1562
+
1563
+ img,
1564
+ picture,
1565
+ video,
1566
+ canvas {
1567
+ display: block;
1568
+ max-width: 100%;
1569
+ }
1570
+
1571
+ img,
1572
+ video {
1573
+ height: auto;
1574
+ }
1575
+
1576
+ svg {
1577
+ overflow: hidden;
1578
+ }
1579
+
1580
+ iframe {
1581
+ border: 0;
1582
+ max-width: 100%;
1583
+ }
1584
+
1585
+ table {
1586
+ border-collapse: collapse;
1587
+ border-spacing: 0;
1588
+ }
1589
+
1590
+ fieldset {
1591
+ border: 0;
1592
+ min-inline-size: 0;
1593
+ }
1594
+
1595
+ legend {
1596
+ padding: 0;
1597
+ }
1598
+
1599
+ button,
1600
+ input,
1601
+ select,
1602
+ textarea,
1603
+ optgroup {
1604
+ font: inherit;
1605
+ letter-spacing: inherit;
1606
+ color: inherit;
1607
+ margin: 0;
1608
+ vertical-align: middle;
1609
+ }
1610
+
1611
+ button,
1612
+ input[type="button"],
1613
+ input[type="submit"],
1614
+ input[type="reset"] {
1615
+ appearance: none;
1616
+ background: none;
1617
+ border: 0;
1618
+ padding: 0;
1619
+ cursor: pointer;
1620
+ text-align: center;
1621
+ }
1622
+
1623
+ input,
1624
+ textarea,
1625
+ select {
1626
+ background-color: Canvas;
1627
+ border: 1px solid color-mix(in srgb, CanvasText 28%, transparent);
1628
+ border-radius: 0;
1629
+ padding: 0.5em 0.65em;
1630
+ }
1631
+
1632
+ input[type="checkbox"],
1633
+ input[type="radio"],
1634
+ input[type="range"],
1635
+ input[type="file"],
1636
+ input[type="color"],
1637
+ input[type="button"],
1638
+ input[type="submit"],
1639
+ input[type="reset"],
1640
+ input[type="image"],
1641
+ input[type="hidden"] {
1642
+ background: none;
1643
+ border: 0;
1644
+ padding: 0;
1645
+ border-radius: 0;
1646
+ }
1647
+
1648
+ input[type="checkbox"],
1649
+ input[type="radio"] {
1650
+ accent-color: CanvasText;
1651
+ }
1652
+
1653
+ input[type="search"] {
1654
+ appearance: none;
1655
+ background-color: Canvas;
1656
+ border: 1px solid color-mix(in srgb, CanvasText 28%, transparent);
1657
+ }
1658
+
1659
+ input[type="search"]::-webkit-search-decoration,
1660
+ input[type="search"]::-webkit-search-cancel-button,
1661
+ input[type="search"]::-webkit-search-results-button,
1662
+ input[type="search"]::-webkit-search-results-decoration {
1663
+ -webkit-appearance: none;
1664
+ display: none;
1665
+ }
1666
+
1667
+ input[type="number"] {
1668
+ appearance: textfield;
1669
+ background-color: Canvas;
1670
+ border: 1px solid color-mix(in srgb, CanvasText 28%, transparent);
1671
+ }
1672
+
1673
+ input[type="number"]::-webkit-inner-spin-button,
1674
+ input[type="number"]::-webkit-outer-spin-button {
1675
+ -webkit-appearance: none;
1676
+ margin: 0;
1677
+ }
1678
+
1679
+ textarea {
1680
+ resize: vertical;
1681
+ }
1682
+
1683
+ ::placeholder {
1684
+ opacity: 1;
1685
+ color: color-mix(in srgb, CanvasText 45%, transparent);
1686
+ }
1687
+
1688
+ :focus-visible {
1689
+ outline: 2px solid CanvasText;
1690
+ outline-offset: 2px;
1691
+ }
1692
+
1693
+ :focus:not(:focus-visible) {
1694
+ outline: none;
1695
+ }
1696
+
1697
+ :disabled,
1698
+ [disabled] {
1699
+ cursor: default;
1700
+ opacity: 0.6;
1701
+ }
1702
+
1703
+ label,
1704
+ summary,
1705
+ select,
1706
+ [role="button"] {
1707
+ cursor: pointer;
1708
+ }
1709
+
1710
+ summary {
1711
+ display: list-item;
1712
+ }
1713
+
1714
+ details {
1715
+ display: block;
1716
+ }
1717
+
1718
+ dialog {
1719
+ margin: auto;
1720
+ padding: 1rem;
1721
+ border: 1px solid color-mix(in srgb, CanvasText 20%, transparent);
1722
+ background: Canvas;
1723
+ color: CanvasText;
1724
+ max-width: calc(100vw - 2rem);
1725
+ max-height: calc(100svh - 2rem);
1726
+ }
1727
+
1728
+ dialog:not([open]) {
1729
+ display: none;
1730
+ }
1731
+
1732
+ ::backdrop {
1733
+ background: color-mix(in srgb, CanvasText 40%, transparent);
1734
+ }
1735
+
1736
+ [hidden] {
1737
+ display: none !important;
1738
+ }
1739
+
1740
+ ::selection {
1741
+ background: Highlight;
1742
+ color: HighlightText;
1743
+ }
1744
+
1745
+ @media (prefers-reduced-motion: reduce) {
1746
+ *,
1747
+ *::before,
1748
+ *::after {
1749
+ animation-duration: 0.01ms !important;
1750
+ animation-iteration-count: 1 !important;
1751
+ transition-duration: 0.01ms !important;
1752
+ scroll-behavior: auto !important;
1753
+ }
1754
+ }
1755
+
1756
+ @media (pointer: coarse) {
1757
+ input,
1758
+ select,
1759
+ textarea {
1760
+ font-size: max(16px, 1em);
1761
+ }
1762
+ }`;
1763
+
1764
+ // ../core/dist/admin-client.js
1765
+ function isRecord2(value) {
1766
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1767
+ }
1768
+ function mountAdminBrand(doc, host, service, catalog) {
1769
+ const clip = (value, max) => {
1770
+ if (typeof value !== "string")
1771
+ return null;
1772
+ const trimmed = value.trim();
1773
+ if (trimmed === "")
1774
+ return null;
1775
+ return trimmed.length > max ? trimmed.slice(0, max) : trimmed;
1776
+ };
1777
+ const httpUrl = (value) => {
1778
+ if (typeof value !== "string" || value.length > 2e3)
1779
+ return null;
1780
+ try {
1781
+ const url = new URL(value);
1782
+ if (url.protocol !== "https:" && url.protocol !== "http:")
1783
+ return null;
1784
+ return url.href;
1785
+ } catch {
1786
+ return null;
1787
+ }
1788
+ };
1789
+ const hexColor = (value) => {
1790
+ if (typeof value !== "string")
1791
+ return null;
1792
+ const raw = value.trim();
1793
+ return /^#[0-9a-fA-F]{3,8}$/.test(raw) ? raw : null;
1794
+ };
1795
+ const hostname = (value) => {
1796
+ try {
1797
+ return new URL(value).hostname.replace(/^www\./, "");
1798
+ } catch {
1799
+ return value;
1800
+ }
1801
+ };
1802
+ host.replaceChildren();
1803
+ host.hidden = true;
1804
+ if (!isRecord2(catalog) || !Object.hasOwn(catalog, service))
1805
+ return;
1806
+ const row = catalog[service];
1807
+ if (!isRecord2(row))
1808
+ return;
1809
+ const vendor = clip(row.vendor, 80);
1810
+ const website = httpUrl(row.website);
1811
+ const guide = httpUrl(row.guide);
1812
+ const docs = httpUrl(row.docs);
1813
+ const logo = httpUrl(row.logo);
1814
+ const color = hexColor(row.color);
1815
+ const description = clip(row.description, 280);
1816
+ if (vendor === null || website === null && guide === null && docs === null)
1817
+ return;
1818
+ const add = (node) => {
1819
+ host.append(node);
1820
+ return node;
1821
+ };
1822
+ if (logo !== null) {
1823
+ const img = doc.createElement("img");
1824
+ img.alt = "";
1825
+ img.src = logo;
1826
+ img.width = 16;
1827
+ img.height = 16;
1828
+ img.decoding = "async";
1829
+ img.referrerPolicy = "no-referrer";
1830
+ if (color !== null)
1831
+ img.style.borderColor = color;
1832
+ add(img);
1833
+ }
1834
+ const name = doc.createElement("span");
1835
+ name.className = "vendor-name";
1836
+ name.textContent = vendor;
1837
+ if (description !== null)
1838
+ name.title = description;
1839
+ add(name);
1840
+ const sep = () => {
1841
+ const node = doc.createElement("span");
1842
+ node.className = "vendor-sep";
1843
+ node.setAttribute("aria-hidden", "true");
1844
+ node.textContent = "\xB7";
1845
+ return node;
1846
+ };
1847
+ const link = (href, label, title) => {
1848
+ const node = doc.createElement("a");
1849
+ node.className = "vendor-link";
1850
+ node.href = href;
1851
+ node.target = "_blank";
1852
+ node.rel = "noopener noreferrer";
1853
+ node.referrerPolicy = "no-referrer";
1854
+ node.textContent = label;
1855
+ node.title = title;
1856
+ return node;
1857
+ };
1858
+ if (website !== null) {
1859
+ add(sep());
1860
+ add(link(website, hostname(website), `${vendor} website`));
1861
+ }
1862
+ if (guide !== null) {
1863
+ add(sep());
1864
+ add(link(guide, "Docs", `Docs for ${vendor}`));
1865
+ }
1866
+ if (docs !== null) {
1867
+ add(sep());
1868
+ add(link(docs, "API", `${vendor} API reference`));
1869
+ }
1870
+ host.removeAttribute("hidden");
1871
+ host.hidden = false;
1872
+ }
1873
+ function startAdminBrand(env) {
1874
+ const requested = env.params.get("brands");
1875
+ let brandsUrl = env.brandsUrl;
1876
+ if (requested) {
1877
+ try {
1878
+ const url = new URL(requested, env.location.href);
1879
+ const local = url.hostname === "localhost" || url.hostname === "127.0.0.1" || url.hostname === "::1" || url.hostname === "[::1]" || url.origin === env.location.origin;
1880
+ if ((url.protocol === "https:" || url.protocol === "http:") && local)
1881
+ brandsUrl = url.href;
1882
+ } catch {
1883
+ brandsUrl = env.brandsUrl;
1884
+ }
1885
+ }
1886
+ const vendorHost = env.$("vendor");
1887
+ const brandRoot = env.document.querySelector("[data-mockingbird-admin]");
1888
+ const brandService = brandRoot?.getAttribute("data-service") || "";
1889
+ if (vendorHost === null)
1890
+ return;
1891
+ env.fetch(brandsUrl, {
1892
+ headers: { accept: "application/json" },
1893
+ signal: AbortSignal.timeout(5e3)
1894
+ }).then(async (response) => {
1895
+ if (!response.ok)
1896
+ return null;
1897
+ const catalog = await response.json();
1898
+ return catalog;
1899
+ }).then((catalog) => {
1900
+ mountAdminBrand(env.document, vendorHost, brandService, catalog);
1901
+ }).catch(() => {
1902
+ });
1903
+ }
1904
+ function faultPresetList(value) {
1905
+ if (!isRecord2(value))
1906
+ throw new Error("Fault presets response was not an object");
1907
+ if (!Object.hasOwn(value, "presets") || value.presets === void 0 || value.presets === null) {
1908
+ return { presets: [] };
1909
+ }
1910
+ if (!Array.isArray(value.presets))
1911
+ throw new Error("Fault presets response was not a list");
1912
+ return {
1913
+ presets: value.presets.flatMap((item) => {
1914
+ if (!isRecord2(item) || typeof item.name !== "string")
1915
+ return [];
1916
+ return [{ name: item.name }];
1917
+ })
1918
+ };
1919
+ }
1920
+ function bootAdmin(config) {
1921
+ const fieldKinds = ["string", "number", "boolean", "null", "object", "array", "unknown"];
1922
+ const sources = ["declared", "inferred", "mixed"];
1923
+ const isFieldKind = (value) => typeof value === "string" && fieldKinds.some((kind) => kind === value);
1924
+ const isSource = (value) => typeof value === "string" && sources.some((source) => source === value);
1925
+ const readJson2 = (text) => {
1926
+ const value = JSON.parse(text);
1927
+ return value;
1928
+ };
1929
+ const esc = (value) => String(value ?? "").replace(/[&<>"']/g, (char) => {
1930
+ switch (char) {
1931
+ case "&":
1932
+ return "&amp;";
1933
+ case "<":
1934
+ return "&lt;";
1935
+ case ">":
1936
+ return "&gt;";
1937
+ case '"':
1938
+ return "&quot;";
1939
+ default:
1940
+ return "&#39;";
1941
+ }
1942
+ });
1943
+ const el = (id, kind) => {
1944
+ const node = document.getElementById(id);
1945
+ if (!(node instanceof kind))
1946
+ throw new Error(`admin shell is missing #${id}`);
1947
+ return node;
1948
+ };
1949
+ const errorMessage = (data, fallback) => {
1950
+ if (!isRecord2(data))
1951
+ return fallback;
1952
+ const error = data.error;
1953
+ if (!isRecord2(error) || typeof error.message !== "string" || error.message === "") {
1954
+ return fallback;
1955
+ }
1956
+ return error.message;
1957
+ };
1958
+ const requireList = (value, label) => {
1959
+ if (value === void 0 || value === null)
1960
+ return [];
1961
+ if (!Array.isArray(value))
1962
+ throw new Error(`${label} was not a list`);
1963
+ return value;
1964
+ };
1965
+ const state = {
1966
+ namespace: "default",
1967
+ key: sessionStorage.getItem("mockingbird-admin-key") || "",
1968
+ view: "overview",
1969
+ collection: "",
1970
+ shape: null,
1971
+ route: null,
1972
+ editing: null
1973
+ };
1974
+ const standard = new Set(config.standardRoutes);
1975
+ const banner = el("banner", HTMLElement);
1976
+ const keyInput = el("admin-key", HTMLInputElement);
1977
+ keyInput.value = state.key;
1978
+ const params = new URLSearchParams(location.search);
1979
+ const queryKey = params.get("key");
1980
+ if (queryKey) {
1981
+ state.key = queryKey;
1982
+ keyInput.value = state.key;
1983
+ sessionStorage.setItem("mockingbird-admin-key", state.key);
1984
+ params.delete("key");
1985
+ const next = params.toString();
1986
+ history.replaceState(null, "", location.pathname + (next ? `?${next}` : ""));
1987
+ }
1988
+ const queryNamespace = params.get("namespace");
1989
+ if (queryNamespace)
1990
+ state.namespace = queryNamespace;
1991
+ const showError = (error) => {
1992
+ banner.textContent = error instanceof Error ? error.message : String(error);
1993
+ banner.classList.add("show");
1994
+ };
1995
+ const clearError = () => {
1996
+ banner.classList.remove("show");
1997
+ };
1998
+ const api = async (method, path, body) => {
1999
+ const headers = { accept: "application/json" };
2000
+ if (state.key !== "")
2001
+ headers[config.adminKeyHeader] = state.key;
2002
+ if (state.namespace !== "")
2003
+ headers["x-mockingbird-namespace"] = state.namespace;
2004
+ if (body !== void 0)
2005
+ headers["content-type"] = "application/json";
2006
+ const init = { method, headers };
2007
+ if (body !== void 0)
2008
+ init.body = JSON.stringify(body);
2009
+ const response = await fetch(config.adminPrefix + path, init);
2010
+ const text = await response.text();
2011
+ let data = null;
2012
+ if (text !== "") {
2013
+ try {
2014
+ data = readJson2(text);
2015
+ } catch {
2016
+ data = { raw: text };
2017
+ }
2018
+ }
2019
+ if (response.status === 401) {
2020
+ keyInput.focus();
2021
+ throw new Error(errorMessage(data, "Admin key required"));
2022
+ }
2023
+ if (!response.ok) {
2024
+ throw new Error(errorMessage(data, `${response.status} ${method} ${path}`));
2025
+ }
2026
+ return data;
2027
+ };
2028
+ const originGet = async (path) => {
2029
+ const response = await fetch(path, { headers: { accept: "application/json" } });
2030
+ if (!response.ok)
2031
+ throw new Error(`${response.status} ${path}`);
2032
+ const value = await response.json();
2033
+ return value;
2034
+ };
2035
+ const panelApi = {
2036
+ get: (path) => api("GET", path),
2037
+ send: (method, path, body) => api(method, path, body),
2038
+ namespace: () => state.namespace
2039
+ };
2040
+ const isPanelScript = (value) => typeof value === "function";
2041
+ const setTheme = (theme) => {
2042
+ if (theme)
2043
+ document.documentElement.dataset.theme = theme;
2044
+ else
2045
+ delete document.documentElement.dataset.theme;
2046
+ };
2047
+ const storedTheme = localStorage.getItem("mockingbird-admin-theme");
2048
+ if (storedTheme)
2049
+ setTheme(storedTheme);
2050
+ el("theme", HTMLButtonElement).addEventListener("click", () => {
2051
+ const current = document.documentElement.dataset.theme;
2052
+ const dark = current ? current === "dark" : matchMedia("(prefers-color-scheme: dark)").matches;
2053
+ const next = dark ? "light" : "dark";
2054
+ localStorage.setItem("mockingbird-admin-theme", next);
2055
+ setTheme(next);
2056
+ });
2057
+ const show = (view) => {
2058
+ state.view = view;
2059
+ for (const button of document.querySelectorAll("#nav button")) {
2060
+ if (!(button instanceof HTMLButtonElement))
2061
+ continue;
2062
+ button.setAttribute("aria-current", button.dataset.view === view ? "true" : "false");
2063
+ }
2064
+ for (const section of document.querySelectorAll("main .view")) {
2065
+ if (!(section instanceof HTMLElement))
2066
+ continue;
2067
+ section.hidden = section.id !== `view-${view}`;
2068
+ }
2069
+ };
2070
+ el("nav", HTMLElement).addEventListener("click", (event) => {
2071
+ const target = event.target;
2072
+ if (!(target instanceof Element))
2073
+ return;
2074
+ const button = target.closest("button[data-view]");
2075
+ if (!(button instanceof HTMLButtonElement))
2076
+ return;
2077
+ const view = button.dataset.view;
2078
+ if (view === void 0)
2079
+ return;
2080
+ show(view);
2081
+ refresh().catch(showError);
2082
+ });
2083
+ 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>`;
2084
+ const table = (columns, rows) => {
2085
+ if (rows.length === 0)
2086
+ return '<p class="empty">Nothing here yet.</p>';
2087
+ const head = `<tr>${columns.map((column) => `<th>${column.label}</th>`).join("")}</tr>`;
2088
+ const body = rows.map((row) => {
2089
+ const cells = columns.map((column) => `<td data-label="${column.label}">${column.cell(row)}</td>`).join("");
2090
+ const attrs = "attrs" in row && typeof row.attrs === "string" ? row.attrs : "";
2091
+ return `<tr${attrs}>${cells}</tr>`;
2092
+ }).join("");
2093
+ return `<table><thead>${head}</thead><tbody>${body}</tbody></table>`;
2094
+ };
2095
+ const preview = (value) => {
2096
+ const text = JSON.stringify(value) ?? "null";
2097
+ return esc(text.length > 180 ? `${text.slice(0, 180)}\u2026` : text);
2098
+ };
2099
+ const when = (epoch) => {
2100
+ const date = new Date(epoch);
2101
+ return Number.isNaN(date.getTime()) ? String(epoch) : date.toISOString();
2102
+ };
2103
+ const clockCard = (epoch) => {
2104
+ const date = new Date(epoch);
2105
+ if (Number.isNaN(date.getTime()))
2106
+ return esc(String(epoch));
2107
+ const iso = date.toISOString();
2108
+ return `${esc(iso.slice(0, 10))}<span class="clock-time">${esc(iso.slice(11, 19))} UTC</span>`;
2109
+ };
2110
+ const decodeClock = (value) => {
2111
+ if (!isRecord2(value) || typeof value.now !== "number" || typeof value.offsetMs !== "number" || typeof value.frozen !== "boolean") {
2112
+ throw new Error("Clock response was not a clock");
2113
+ }
2114
+ return { now: value.now, offsetMs: value.offsetMs, frozen: value.frozen };
2115
+ };
2116
+ const decodeField = (value) => {
2117
+ if (!isRecord2(value) || typeof value.name !== "string" || !isFieldKind(value.kind)) {
2118
+ return void 0;
2119
+ }
2120
+ const field = {
2121
+ name: value.name,
2122
+ kind: value.kind,
2123
+ optional: value.optional === true
2124
+ };
2125
+ if (typeof value.description === "string")
2126
+ field.description = value.description;
2127
+ if (Array.isArray(value.fields)) {
2128
+ field.fields = value.fields.flatMap((item) => {
2129
+ const child = decodeField(item);
2130
+ return child === void 0 ? [] : [child];
2131
+ });
2132
+ }
2133
+ return field;
2134
+ };
2135
+ const decodeCollection = (value) => {
2136
+ if (!isRecord2(value) || typeof value.name !== "string")
2137
+ return void 0;
2138
+ const fields = Array.isArray(value.fields) ? value.fields.flatMap((item) => {
2139
+ const field = decodeField(item);
2140
+ return field === void 0 ? [] : [field];
2141
+ }) : [];
2142
+ const collection = {
2143
+ name: value.name,
2144
+ label: typeof value.label === "string" ? value.label : value.name,
2145
+ count: typeof value.count === "number" ? value.count : 0,
2146
+ source: isSource(value.source) ? value.source : "inferred",
2147
+ fields
2148
+ };
2149
+ if (typeof value.description === "string")
2150
+ collection.description = value.description;
2151
+ return collection;
2152
+ };
2153
+ const decodeState = (value) => {
2154
+ if (!isRecord2(value) || !Array.isArray(value.collections)) {
2155
+ throw new Error("State response has no collection list");
2156
+ }
2157
+ return {
2158
+ namespace: typeof value.namespace === "string" ? value.namespace : state.namespace,
2159
+ storageNamespace: typeof value.storageNamespace === "string" ? value.storageNamespace : "",
2160
+ collections: value.collections.flatMap((item) => {
2161
+ const collection = decodeCollection(item);
2162
+ return collection === void 0 ? [] : [collection];
2163
+ })
2164
+ };
2165
+ };
2166
+ const decodeRecords = (value) => requireList(isRecord2(value) ? value.records : void 0, "Records").flatMap((item) => {
2167
+ if (!isRecord2(item) || typeof item.id !== "string")
2168
+ return [];
2169
+ return [
2170
+ {
2171
+ id: item.id,
2172
+ seq: typeof item.seq === "number" ? item.seq : 0,
2173
+ value: item.value,
2174
+ attrs: ` class="clickable" data-id="${esc(item.id)}"`
2175
+ }
2176
+ ];
2177
+ });
2178
+ const textOf = (value) => typeof value === "string" || typeof value === "number" || typeof value === "boolean" ? String(value) : "";
2179
+ const decodeJournal = (value) => requireList(isRecord2(value) ? value.requests : void 0, "Journal").flatMap((item) => {
2180
+ if (!isRecord2(item))
2181
+ return [];
2182
+ return [
2183
+ {
2184
+ at: textOf(item.at),
2185
+ method: textOf(item.method),
2186
+ path: textOf(item.path),
2187
+ status: textOf(item.status),
2188
+ durationMs: textOf(item.durationMs),
2189
+ operationId: textOf(item.operationId)
2190
+ }
2191
+ ];
2192
+ });
2193
+ const decodeFaults = (value) => requireList(isRecord2(value) ? value.faults : void 0, "Faults").flatMap((item) => {
2194
+ if (!isRecord2(item))
2195
+ return [];
2196
+ return [
2197
+ {
2198
+ id: textOf(item.id),
2199
+ method: typeof item.method === "string" ? item.method : void 0,
2200
+ pathPrefix: typeof item.pathPrefix === "string" ? item.pathPrefix : void 0,
2201
+ operationId: typeof item.operationId === "string" ? item.operationId : void 0,
2202
+ status: typeof item.status === "number" ? item.status : void 0,
2203
+ effect: typeof item.effect === "string" ? item.effect : void 0,
2204
+ drop: typeof item.drop === "boolean" ? item.drop : void 0,
2205
+ latencyMs: typeof item.latencyMs === "number" ? item.latencyMs : void 0
2206
+ }
2207
+ ];
2208
+ });
2209
+ const loadNamespaces = async () => {
2210
+ const data = await api("GET", "/namespaces");
2211
+ const select = el("namespace", HTMLSelectElement);
2212
+ const listed = requireList(isRecord2(data) ? data.namespaces : void 0, "Namespaces");
2213
+ const fallback = isRecord2(data) && typeof data.default === "string" && data.default !== "" ? data.default : "default";
2214
+ const names = (listed.length > 0 ? listed.map((name) => String(name)) : [fallback]).slice();
2215
+ if (!names.includes(state.namespace))
2216
+ names.unshift(state.namespace);
2217
+ select.innerHTML = names.map((name) => `<option value="${esc(name)}">${esc(name)}</option>`).join("");
2218
+ select.value = state.namespace;
2219
+ };
2220
+ const renderOverview = async () => {
2221
+ const [health, shape, clock, journal] = await Promise.all([
2222
+ originGet("/health"),
2223
+ api("GET", "/state"),
2224
+ api("GET", "/clock"),
2225
+ api("GET", "/requests?limit=8")
2226
+ ]);
2227
+ if (!isRecord2(health))
2228
+ throw new Error("Health response was not an object");
2229
+ const decoded = decodeState(shape);
2230
+ state.shape = decoded;
2231
+ const time = decodeClock(clock);
2232
+ el("overview-cards", HTMLElement).innerHTML = cards([
2233
+ {
2234
+ k: "Service",
2235
+ v: esc(typeof health.service === "string" && health.service !== "" ? health.service : config.service)
2236
+ },
2237
+ {
2238
+ k: "Status",
2239
+ v: esc(typeof health.status === "string" && health.status !== "" ? health.status : "ok")
2240
+ },
2241
+ { k: "Namespace", v: esc(state.namespace) },
2242
+ { k: "Collections", v: String(decoded.collections.length) },
2243
+ {
2244
+ k: "Records",
2245
+ v: String(decoded.collections.reduce((sum, item) => sum + item.count, 0))
2246
+ },
2247
+ { k: "Clock", v: clockCard(time.now) }
2248
+ ]);
2249
+ el("overview-requests", HTMLElement).innerHTML = table([
2250
+ { label: "Method", cell: (row) => esc(row.method) },
2251
+ { label: "Path", cell: (row) => esc(row.path) },
2252
+ {
2253
+ label: "Status",
2254
+ cell: (row) => `<span class="status-${row.status.charAt(0)}">${esc(row.status)}</span>`
2255
+ },
2256
+ { label: "Operation", cell: (row) => esc(row.operationId) }
2257
+ ], decodeJournal(journal));
2258
+ };
2259
+ const schemaChips = (collection) => {
2260
+ if (collection.fields.length === 0) {
2261
+ return '<p class="empty">No fields yet. Add a record or declare the collection on the service.</p>';
2262
+ }
2263
+ return `<div class="chips">${collection.fields.map((field) => `<span class="chip">${esc(field.name)} <i>${esc(field.kind)}${field.optional ? "?" : ""}</i></span>`).join("")}</div>`;
2264
+ };
2265
+ const renderState = async () => {
2266
+ const shape = decodeState(await api("GET", "/state"));
2267
+ state.shape = shape;
2268
+ const host = el("collections", HTMLElement);
2269
+ if (shape.collections.length === 0) {
2270
+ host.innerHTML = '<p class="empty">This namespace has no collections yet.</p>';
2271
+ el("schema", HTMLElement).innerHTML = "";
2272
+ el("records", HTMLElement).innerHTML = "";
2273
+ return;
2274
+ }
2275
+ if (!shape.collections.some((item) => item.name === state.collection)) {
2276
+ const first = shape.collections[0];
2277
+ if (first === void 0)
2278
+ return;
2279
+ state.collection = first.name;
2280
+ }
2281
+ 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("");
2282
+ const selected = shape.collections.find((item) => item.name === state.collection);
2283
+ if (selected === void 0)
2284
+ return;
2285
+ const description = selected.description ? `<p class="lede">${esc(selected.description)}</p>` : "";
2286
+ el("schema", HTMLElement).innerHTML = `<h3>${esc(selected.label)}</h3>${description}${schemaChips(selected)}`;
2287
+ const page = await api("GET", `/state/${encodeURIComponent(state.collection)}?limit=50`);
2288
+ el("records", HTMLElement).innerHTML = table([
2289
+ { label: "Id", cell: (row) => `<code>${esc(row.id)}</code>` },
2290
+ { label: "Seq", cell: (row) => esc(row.seq) },
2291
+ { label: "Value", cell: (row) => preview(row.value) }
2292
+ ], decodeRecords(page));
2293
+ };
2294
+ const renderClock = async () => {
2295
+ const clock = decodeClock(await api("GET", "/clock"));
2296
+ 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>`;
2297
+ const freeze = el("freeze", HTMLButtonElement);
2298
+ freeze.textContent = clock.frozen ? "Unfreeze" : "Freeze";
2299
+ freeze.dataset.frozen = clock.frozen ? "1" : "0";
2300
+ };
2301
+ const renderFaults = async () => {
2302
+ const [faults, presets] = await Promise.all([
2303
+ api("GET", "/faults"),
2304
+ api("GET", "/faults/presets").catch(() => ({ presets: [] }))
2305
+ ]);
2306
+ const select = el("fault-preset", HTMLSelectElement);
2307
+ const current = select.value;
2308
+ const list = faultPresetList(presets).presets;
2309
+ select.innerHTML = `<option value="">Custom rule</option>${list.map((preset) => `<option value="${esc(preset.name)}">${esc(preset.name)}</option>`).join("")}`;
2310
+ select.value = current;
2311
+ el("fault-list", HTMLElement).innerHTML = table([
2312
+ { label: "Id", cell: (row) => esc(row.id) },
2313
+ {
2314
+ label: "Match",
2315
+ cell: (row) => esc([row.method, row.pathPrefix, row.operationId].filter((part) => Boolean(part)).join(" ") || "all")
2316
+ },
2317
+ {
2318
+ label: "Effect",
2319
+ cell: (row) => esc(row.status ?? row.effect ?? (row.drop ? "drop" : row.latencyMs || ""))
2320
+ }
2321
+ ], decodeFaults(faults));
2322
+ };
2323
+ const renderJournal = async () => {
2324
+ const journal = await api("GET", "/requests?limit=100");
2325
+ el("journal", HTMLElement).innerHTML = table([
2326
+ { label: "When", cell: (row) => esc(row.at) },
2327
+ { label: "Method", cell: (row) => esc(row.method) },
2328
+ { label: "Path", cell: (row) => esc(row.path) },
2329
+ { label: "Status", cell: (row) => esc(row.status) },
2330
+ { label: "ms", cell: (row) => esc(row.durationMs) }
2331
+ ], decodeJournal(journal));
2332
+ };
2333
+ const renderRoutes = async () => {
2334
+ const data = await api("GET", "/");
2335
+ if (!isRecord2(data))
2336
+ throw new Error("Route list was not an object");
2337
+ const routes = requireList(data.routes, "Routes").map((route) => String(route));
2338
+ el("route-list", HTMLElement).innerHTML = routes.map((route) => {
2339
+ const extra = standard.has(route) ? "" : " <span class='badge'>extension</span>";
2340
+ const parts = route.split(" ");
2341
+ const method = parts[0] ?? "";
2342
+ const path = parts.slice(1).join(" ");
2343
+ 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>`;
2344
+ }).join("");
2345
+ };
2346
+ const renderers = {
2347
+ overview: renderOverview,
2348
+ state: renderState,
2349
+ clock: renderClock,
2350
+ faults: renderFaults,
2351
+ journal: renderJournal,
2352
+ routes: renderRoutes
2353
+ };
2354
+ async function refresh() {
2355
+ clearError();
2356
+ const render = renderers[state.view];
2357
+ if (render)
2358
+ await render();
2359
+ }
2360
+ el("namespace", HTMLSelectElement).addEventListener("change", () => {
2361
+ state.namespace = el("namespace", HTMLSelectElement).value;
2362
+ refresh().catch(showError);
2363
+ });
2364
+ keyInput.addEventListener("change", () => {
2365
+ state.key = keyInput.value.trim();
2366
+ sessionStorage.setItem("mockingbird-admin-key", state.key);
2367
+ refresh().catch(showError);
2368
+ });
2369
+ el("refresh", HTMLButtonElement).addEventListener("click", () => {
2370
+ refresh().catch(showError);
2371
+ });
2372
+ el("collections", HTMLElement).addEventListener("click", (event) => {
2373
+ const target = event.target;
2374
+ if (!(target instanceof Element))
2375
+ return;
2376
+ const button = target.closest("[data-collection]");
2377
+ if (!(button instanceof HTMLElement))
2378
+ return;
2379
+ const collection = button.dataset.collection;
2380
+ if (collection === void 0)
2381
+ return;
2382
+ state.collection = collection;
2383
+ renderState().catch(showError);
2384
+ });
2385
+ el("records", HTMLElement).addEventListener("click", (event) => {
2386
+ const target = event.target;
2387
+ if (!(target instanceof Element))
2388
+ return;
2389
+ const row = target.closest("[data-id]");
2390
+ if (!(row instanceof HTMLElement))
2391
+ return;
2392
+ const id = row.dataset.id;
2393
+ if (id === void 0)
2394
+ return;
2395
+ openEditor(id).catch(showError);
2396
+ });
2397
+ const editor = el("editor", HTMLDialogElement);
2398
+ const editorId = el("editor-id", HTMLInputElement);
2399
+ const editorValue = el("editor-value", HTMLTextAreaElement);
2400
+ const editorDelete = el("editor-delete", HTMLButtonElement);
2401
+ async function openEditor(id) {
2402
+ state.editing = id;
2403
+ el("editor-title", HTMLElement).textContent = id ? "Edit record" : "New record";
2404
+ editorId.value = id ?? "";
2405
+ editorId.readOnly = id !== null;
2406
+ editorDelete.hidden = id === null;
2407
+ if (id) {
2408
+ const record = await api("GET", `/state/${encodeURIComponent(state.collection)}/${encodeURIComponent(id)}`);
2409
+ if (!isRecord2(record))
2410
+ throw new Error("Record response was not an object");
2411
+ editorValue.value = JSON.stringify(record.value, null, 2) ?? "";
2412
+ } else {
2413
+ editorValue.value = "{\n \n}";
2414
+ }
2415
+ editor.showModal();
2416
+ }
2417
+ el("new-record", HTMLButtonElement).addEventListener("click", () => {
2418
+ openEditor(null).catch(showError);
2419
+ });
2420
+ el("editor-form", HTMLFormElement).addEventListener("submit", (event) => {
2421
+ const submitter = event.submitter;
2422
+ if (!(submitter instanceof HTMLButtonElement) || submitter.value !== "save")
2423
+ return;
2424
+ event.preventDefault();
2425
+ let value;
2426
+ try {
2427
+ value = readJson2(editorValue.value);
2428
+ } catch {
2429
+ showError(new Error("Value is not JSON"));
2430
+ return;
2431
+ }
2432
+ const id = editorId.value.trim();
2433
+ const path = `/state/${encodeURIComponent(state.collection)}`;
2434
+ const request = state.editing ? api("PUT", `${path}/${encodeURIComponent(id)}`, { value }) : api("POST", path, { id: id || void 0, value });
2435
+ request.then(() => {
2436
+ editor.close();
2437
+ return renderState();
2438
+ }).catch(showError);
2439
+ });
2440
+ editorDelete.addEventListener("click", () => {
2441
+ const id = state.editing;
2442
+ if (!id)
2443
+ return;
2444
+ api("DELETE", `/state/${encodeURIComponent(state.collection)}/${encodeURIComponent(id)}`).then(() => {
2445
+ editor.close();
2446
+ return renderState();
2447
+ }).catch(showError);
2448
+ });
2449
+ for (const button of document.querySelectorAll("[data-advance]")) {
2450
+ if (!(button instanceof HTMLButtonElement))
2451
+ continue;
2452
+ button.addEventListener("click", () => {
2453
+ const raw = button.dataset.advance;
2454
+ if (raw === void 0)
2455
+ return;
2456
+ api("POST", "/clock", { advance: Number(raw) }).then(() => renderClock()).catch(showError);
2457
+ });
2458
+ }
2459
+ el("freeze", HTMLButtonElement).addEventListener("click", () => {
2460
+ const frozen = el("freeze", HTMLButtonElement).dataset.frozen === "1";
2461
+ api("POST", "/clock", { freeze: !frozen }).then(() => renderClock()).catch(showError);
2462
+ });
2463
+ el("clock-reset", HTMLButtonElement).addEventListener("click", () => {
2464
+ api("POST", "/clock", { reset: true }).then(() => renderClock()).catch(showError);
2465
+ });
2466
+ el("clock-set", HTMLFormElement).addEventListener("submit", (event) => {
2467
+ event.preventDefault();
2468
+ const raw = el("clock-instant", HTMLInputElement).value.trim();
2469
+ const set = /^-?\d+$/.test(raw) ? Number(raw) : raw;
2470
+ api("POST", "/clock", { set }).then(() => renderClock()).catch(showError);
2471
+ });
2472
+ el("fault-form", HTMLFormElement).addEventListener("submit", (event) => {
2473
+ event.preventDefault();
2474
+ const preset = el("fault-preset", HTMLSelectElement).value;
2475
+ if (preset !== "") {
2476
+ api("POST", "/faults", { preset }).then(() => renderFaults()).catch(showError);
2477
+ return;
2478
+ }
2479
+ const status = Number(el("fault-status", HTMLInputElement).value);
2480
+ if (!Number.isFinite(status)) {
2481
+ showError(new Error("Status must be a number, or choose a preset"));
2482
+ return;
2483
+ }
2484
+ const path = el("fault-path", HTMLInputElement).value;
2485
+ const method = el("fault-method", HTMLInputElement).value;
2486
+ api("POST", "/faults", {
2487
+ status,
2488
+ ...path !== "" ? { pathPrefix: path } : {},
2489
+ ...method !== "" ? { method } : {}
2490
+ }).then(() => renderFaults()).catch(showError);
2491
+ });
2492
+ el("fault-clear", HTMLButtonElement).addEventListener("click", () => {
2493
+ api("DELETE", "/faults").then(() => renderFaults()).catch(showError);
2494
+ });
2495
+ el("journal-clear", HTMLButtonElement).addEventListener("click", () => {
2496
+ api("DELETE", "/requests").then(() => renderJournal()).catch(showError);
2497
+ });
2498
+ el("route-list", HTMLElement).addEventListener("click", (event) => {
2499
+ const target = event.target;
2500
+ if (!(target instanceof Element))
2501
+ return;
2502
+ const button = target.closest("[data-route]");
2503
+ if (!(button instanceof HTMLElement))
2504
+ return;
2505
+ const route = button.dataset.route;
2506
+ if (route === void 0)
2507
+ return;
2508
+ state.route = route;
2509
+ el("route-title", HTMLElement).textContent = route;
2510
+ const method = route.split(" ")[0] ?? "";
2511
+ el("route-body", HTMLTextAreaElement).disabled = method === "GET" || method === "DELETE";
2512
+ });
2513
+ el("route-form", HTMLFormElement).addEventListener("submit", (event) => {
2514
+ event.preventDefault();
2515
+ const send = async () => {
2516
+ if (state.route === null)
2517
+ throw new Error("Choose a route first");
2518
+ const parts = state.route.split(" ");
2519
+ const method = parts[0];
2520
+ const path = parts.slice(1).join(" ");
2521
+ if (method === void 0)
2522
+ throw new Error("Choose a route first");
2523
+ const concrete = path.replace(/:([A-Za-z0-9_]+)/g, (_match, key) => prompt(`Value for ${key}`) || "");
2524
+ let body;
2525
+ const routeBody = el("route-body", HTMLTextAreaElement);
2526
+ if (method !== "GET" && method !== "DELETE" && routeBody.value.trim() !== "") {
2527
+ try {
2528
+ body = readJson2(routeBody.value);
2529
+ } catch {
2530
+ throw new Error("Body is not JSON");
2531
+ }
2532
+ }
2533
+ const data = await api(method, concrete, body);
2534
+ el("route-result", HTMLElement).textContent = JSON.stringify(data, null, 2);
2535
+ };
2536
+ send().catch(showError);
2537
+ });
2538
+ const decodePanel = (value) => {
2539
+ if (!isRecord2(value) || typeof value.id !== "string" || typeof value.title !== "string") {
2540
+ return void 0;
2541
+ }
2542
+ const panel = {
2543
+ id: value.id,
2544
+ title: value.title,
2545
+ description: typeof value.description === "string" ? value.description : "",
2546
+ html: typeof value.html === "string" ? value.html : ""
2547
+ };
2548
+ if (typeof value.script === "string")
2549
+ panel.script = value.script;
2550
+ return panel;
2551
+ };
2552
+ const mountPanels = (panels) => {
2553
+ const nav = el("nav", HTMLElement);
2554
+ const views = el("views", HTMLElement);
2555
+ for (const panel of panels) {
2556
+ const button = document.createElement("button");
2557
+ button.type = "button";
2558
+ button.dataset.view = panel.id;
2559
+ button.textContent = panel.title;
2560
+ nav.append(button);
2561
+ const section = document.createElement("section");
2562
+ section.className = "view";
2563
+ section.id = `view-${panel.id}`;
2564
+ section.hidden = true;
2565
+ const title = document.createElement("h2");
2566
+ title.textContent = panel.title;
2567
+ const lede = document.createElement("p");
2568
+ lede.className = "lede";
2569
+ lede.textContent = panel.description;
2570
+ const body = document.createElement("div");
2571
+ body.className = "panel-body";
2572
+ body.id = `panel-${panel.id}`;
2573
+ body.innerHTML = panel.html;
2574
+ section.append(title, lede, body);
2575
+ views.append(section);
2576
+ if (panel.script === void 0)
2577
+ continue;
2578
+ try {
2579
+ const compiled = new Function("root", "api", panel.script);
2580
+ if (!isPanelScript(compiled))
2581
+ throw new Error("panel script did not compile");
2582
+ compiled(body, panelApi);
2583
+ } catch (error) {
2584
+ const note = document.createElement("p");
2585
+ note.className = "banner show";
2586
+ note.textContent = error instanceof Error ? error.message : String(error);
2587
+ body.append(note);
2588
+ }
2589
+ }
2590
+ };
2591
+ startAdminBrand({
2592
+ document,
2593
+ $: (id) => document.getElementById(id),
2594
+ params,
2595
+ location: { href: location.href, origin: location.origin },
2596
+ fetch,
2597
+ brandsUrl: config.brandsUrl
2598
+ });
2599
+ loadNamespaces().then(() => api("GET", "/ui/manifest")).then((manifest) => {
2600
+ const panels = requireList(isRecord2(manifest) ? manifest.panels : void 0, "Panels").flatMap((panel) => {
2601
+ const decoded = decodePanel(panel);
2602
+ return decoded === void 0 ? [] : [decoded];
2603
+ });
2604
+ mountPanels(panels);
2605
+ return refresh();
2606
+ }).catch(showError);
2607
+ }
2608
+ var clientPrelude = [isRecord2, mountAdminBrand, startAdminBrand, faultPresetList, bootAdmin].map((fn) => fn.toString()).join("\n");
2609
+ var adminClientSource = (config) => {
2610
+ const payload = JSON.stringify(config).replace(/</g, "\\u003c");
2611
+ return `<script>
2612
+ ${clientPrelude}
2613
+ bootAdmin(${payload});
2614
+ </script>`;
2615
+ };
2616
+
2617
+ // ../core/dist/surface.js
2618
+ var defineMock = (runtime) => runtime;
2619
+ var STANDARD_ADMIN_ROUTES = [
2620
+ "GET /",
2621
+ "POST /reset",
2622
+ "GET /namespaces",
2623
+ "GET /clock",
2624
+ "POST /clock",
2625
+ "GET /faults",
2626
+ "POST /faults",
2627
+ "DELETE /faults",
2628
+ "POST /snapshots",
2629
+ "POST /snapshots/:id/restore",
2630
+ "DELETE /snapshots/:id",
2631
+ "GET /timeline",
2632
+ "POST /checkpoints",
2633
+ "POST /branches/:name",
2634
+ "POST /branches/:name/checkout",
2635
+ "GET /requests",
2636
+ "DELETE /requests",
2637
+ "GET /metrics",
2638
+ "DELETE /metrics",
2639
+ "GET /credentials",
2640
+ "PUT /credentials",
2641
+ "DELETE /credentials",
2642
+ "GET /state",
2643
+ "GET /state/:collection",
2644
+ "GET /state/:collection/:id",
2645
+ "POST /state/:collection",
2646
+ "PUT /state/:collection/:id",
2647
+ "PATCH /state/:collection/:id",
2648
+ "DELETE /state/:collection/:id",
2649
+ "GET /ui",
2650
+ "GET /ui/",
2651
+ "GET /ui/manifest"
2652
+ ];
2653
+ var PANEL_ID = /^[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$/;
2654
+ var assertAdminUi = (ui) => {
2655
+ if (!ui?.panels)
2656
+ return;
2657
+ const seen = /* @__PURE__ */ new Set();
2658
+ for (const panel of ui.panels) {
2659
+ if (!PANEL_ID.test(panel.id)) {
2660
+ throw new RangeError(`admin panel id must match ${PANEL_ID}: ${JSON.stringify(panel.id)}`);
2661
+ }
2662
+ if (seen.has(panel.id))
2663
+ throw new RangeError(`duplicate admin panel id ${panel.id}`);
2664
+ seen.add(panel.id);
2665
+ if (panel.title.trim() === "")
2666
+ throw new RangeError(`admin panel ${panel.id} needs a title`);
2667
+ }
2668
+ };
2669
+
2670
+ // ../core/dist/admin-ui.js
2671
+ var escapeHtml = (value) => value.replace(/[&<>"']/g, (char) => {
2672
+ switch (char) {
2673
+ case "&":
2674
+ return "&amp;";
2675
+ case "<":
2676
+ return "&lt;";
2677
+ case ">":
2678
+ return "&gt;";
2679
+ case '"':
2680
+ return "&quot;";
2681
+ default:
2682
+ return "&#39;";
2683
+ }
2684
+ });
2685
+ var ADMIN_BRANDS_URL = "https://mockingbird.chrisvouga.dev/brands.json";
2686
+ var renderAdminDocument = (service) => {
2687
+ const name = escapeHtml(service);
2688
+ const script = adminClientSource({
2689
+ standardRoutes: STANDARD_ADMIN_ROUTES,
2690
+ adminPrefix: ADMIN_PREFIX,
2691
+ adminKeyHeader: ADMIN_KEY_HEADER,
2692
+ brandsUrl: ADMIN_BRANDS_URL,
2693
+ service
2694
+ });
2695
+ return `<!DOCTYPE html>
2696
+ <html lang="en">
2697
+ <head>
2698
+ <meta charset="utf-8">
2699
+ <meta name="viewport" content="width=device-width, initial-scale=1">
2700
+ <meta name="color-scheme" content="light dark">
2701
+ <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">
2702
+ <title>${name} admin</title>
2703
+ <style>
2704
+ ${CSS_RESET}
2705
+ :root {
2706
+ color-scheme: light;
2707
+ --bg: #fafafa;
2708
+ --bg-elev: #ffffff;
2709
+ --bg-muted: #f4f4f5;
2710
+ --bg-inset: #f4f4f5;
2711
+ --line: #dedee3;
2712
+ --line-strong: #c4c4cc;
2713
+ --ink: #18181b;
2714
+ --muted: #62626b;
2715
+ --faint: #71717a;
2716
+ --accent: #27272a;
2717
+ --accent-ink: #ffffff;
2718
+ --accent-soft: #f4f4f5;
2719
+ --ok: #166534;
2720
+ --ok-soft: #f0fdf4;
2721
+ --warn: #a16207;
2722
+ --warn-soft: #fefce8;
2723
+ --err: #a82d32;
2724
+ --err-soft: #fef2f2;
2725
+ --shadow: none;
2726
+ --sans: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
2727
+ --mono: ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, monospace;
2728
+ --radius: 10px;
2729
+ --header: 56px;
2730
+ --nav: 200px;
2731
+ }
2732
+ :root[data-theme="dark"] {
2733
+ color-scheme: dark;
2734
+ --bg: #111113;
2735
+ --bg-elev: #19191c;
2736
+ --bg-muted: #242428;
2737
+ --bg-inset: #111113;
2738
+ --line: #36363c;
2739
+ --line-strong: #52525b;
2740
+ --ink: #f4f4f5;
2741
+ --muted: #a9a9b2;
2742
+ --faint: #71717a;
2743
+ --accent: #e4e4e7;
2744
+ --accent-ink: #18181b;
2745
+ --accent-soft: #242428;
2746
+ --ok: #86efac;
2747
+ --ok-soft: #14241b;
2748
+ --warn: #fde68a;
2749
+ --warn-soft: #2a2410;
2750
+ --err: #ffaaaa;
2751
+ --err-soft: #2c1516;
2752
+ --shadow: none;
2753
+ }
2754
+ @media (prefers-color-scheme: dark) {
2755
+ :root:not([data-theme="light"]) {
2756
+ color-scheme: dark;
2757
+ --bg: #111113;
2758
+ --bg-elev: #19191c;
2759
+ --bg-muted: #242428;
2760
+ --bg-inset: #111113;
2761
+ --line: #36363c;
2762
+ --line-strong: #52525b;
2763
+ --ink: #f4f4f5;
2764
+ --muted: #a9a9b2;
2765
+ --faint: #71717a;
2766
+ --accent: #e4e4e7;
2767
+ --accent-ink: #18181b;
2768
+ --accent-soft: #242428;
2769
+ --ok: #86efac;
2770
+ --ok-soft: #14241b;
2771
+ --warn: #fde68a;
2772
+ --warn-soft: #2a2410;
2773
+ --err: #ffaaaa;
2774
+ --err-soft: #2c1516;
2775
+ --shadow: none;
2776
+ }
2777
+ }
2778
+ /* Fill the viewport, then grow with the content so the background covers the scroll. */
2779
+ html { height: 100%; }
2780
+ body {
2781
+ margin: 0;
2782
+ min-height: 100%;
2783
+ display: flex;
2784
+ flex-direction: column;
2785
+ font-family: var(--sans);
2786
+ background: var(--bg);
2787
+ color: var(--ink);
2788
+ line-height: 1.45;
2789
+ padding-left: env(safe-area-inset-left);
2790
+ padding-right: env(safe-area-inset-right);
2791
+ -webkit-text-size-adjust: 100%;
2792
+ }
2793
+ :focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
2794
+ .app { flex: 1 0 auto; display: flex; flex-direction: column; min-width: 0; width: 100%; }
2795
+ header.top {
2796
+ position: sticky; top: 0; z-index: 5;
2797
+ display: flex; align-items: center; gap: 12px;
2798
+ min-height: var(--header); padding: 10px 16px;
2799
+ background: var(--bg-elev);
2800
+ border-bottom: 1px solid var(--line);
2801
+ min-width: 0; max-width: 100%;
2802
+ }
2803
+ .brand {
2804
+ display: flex; flex-flow: row wrap; align-items: center; gap: 14px;
2805
+ min-width: 0; max-width: 100%; margin-right: auto;
2806
+ }
2807
+ .brand-id { display: flex; flex-direction: column; min-width: 0; flex: none; }
2808
+ .brand-id strong { font-size: 15px; letter-spacing: -0.01em; }
2809
+ .brand-id span { color: var(--faint); font-size: 12px; }
2810
+ .vendor {
2811
+ display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
2812
+ min-width: 0; max-width: 100%; font-size: 12px; color: var(--muted);
2813
+ }
2814
+ .vendor[hidden] { display: none; }
2815
+ /* Logos are drawn for a light tile, same as the docs site, so the plate stays white. */
2816
+ .vendor img {
2817
+ width: 16px; height: 16px; padding: 2px; box-sizing: content-box;
2818
+ border-radius: 4px; border: 1px solid var(--line); background: #fff; object-fit: contain;
2819
+ }
2820
+ .vendor-name { color: var(--ink); font-weight: 600; }
2821
+ .vendor-sep { color: var(--faint); }
2822
+ .vendor-link { color: inherit; text-decoration: none; }
2823
+ .vendor-link:hover { color: var(--ink); text-decoration: underline; }
2824
+ .controls { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
2825
+ label.field { display: flex; flex-direction: column; gap: 2px; font-size: 11px; color: var(--faint); }
2826
+ label.field input, label.field select, .controls input[type="password"] {
2827
+ background: var(--bg-inset); border: 1px solid var(--line); border-radius: 8px;
2828
+ padding: 7px 8px; min-height: 36px;
2829
+ }
2830
+ .icon-btn, .btn, .btn-primary, .btn-quiet, .btn-danger {
2831
+ border-radius: 9px; min-height: 36px; padding: 0 12px; border: 1px solid var(--line);
2832
+ background: var(--bg-elev);
2833
+ }
2834
+ .btn-primary { background: var(--accent); color: var(--accent-ink); border-color: transparent; }
2835
+ .btn-quiet { background: transparent; }
2836
+ .btn-danger { color: var(--err); }
2837
+ /* minmax(0, 1fr): a 1fr column will not shrink below its content, so a long
2838
+ record or the full nav row would widen the page past the viewport. */
2839
+ .layout {
2840
+ display: grid; grid-template-columns: var(--nav) minmax(0, 1fr);
2841
+ flex: 1 0 auto; min-width: 0;
2842
+ }
2843
+ nav.side {
2844
+ border-right: 1px solid var(--line); padding: 12px;
2845
+ display: flex; flex-direction: column; gap: 4px;
2846
+ background: var(--bg-elev);
2847
+ }
2848
+ nav.side button {
2849
+ text-align: left; border: 0; background: transparent; border-radius: 9px;
2850
+ padding: 9px 10px; min-height: 40px; color: var(--muted);
2851
+ }
2852
+ nav.side button[aria-current="true"] { background: var(--accent-soft); color: var(--ink); font-weight: 600; }
2853
+ main { padding: 20px; max-width: 1100px; width: 100%; min-width: 0; }
2854
+ .view h2 { margin: 0 0 4px; font-size: 22px; letter-spacing: -0.02em; }
2855
+ .lede { margin: 0 0 16px; color: var(--muted); }
2856
+ .grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 12px; }
2857
+ .card {
2858
+ background: var(--bg-elev); border: 1px solid var(--line); border-radius: var(--radius);
2859
+ padding: 14px 16px; box-shadow: var(--shadow);
2860
+ }
2861
+ .card .k { color: var(--faint); font-size: 12px; text-transform: uppercase; letter-spacing: 0.04em; }
2862
+ .card .v { display: flex; flex-direction: column; gap: 2px; font-size: 22px; margin-top: 4px; font-variant-numeric: tabular-nums; overflow-wrap: anywhere; }
2863
+ .card .v .clock-time { font-size: 14px; color: var(--muted); }
2864
+ .split { display: grid; grid-template-columns: minmax(0, 240px) minmax(0, 1fr); gap: 16px; align-items: start; }
2865
+ .collection {
2866
+ width: 100%; text-align: left; border: 1px solid transparent; background: transparent;
2867
+ border-radius: 10px; padding: 8px 10px; min-height: 44px;
2868
+ }
2869
+ .collection[aria-current="true"] { background: var(--bg-elev); border-color: var(--line); }
2870
+ .collection small { display: block; color: var(--faint); }
2871
+ .chips { display: flex; flex-wrap: wrap; gap: 6px; margin: 8px 0 14px; }
2872
+ .chip {
2873
+ font-family: var(--mono); font-size: 12px; background: var(--bg-muted);
2874
+ border-radius: 999px; padding: 4px 8px;
2875
+ }
2876
+ .chip i { color: var(--faint); font-style: normal; }
2877
+ table { width: 100%; border-collapse: collapse; font-size: 14px; }
2878
+ th, td { text-align: left; padding: 8px 10px; border-bottom: 1px solid var(--line); vertical-align: top; }
2879
+ th { color: var(--faint); font-weight: 600; font-size: 12px; }
2880
+ tr.clickable { cursor: pointer; }
2881
+ tr.clickable:hover { background: var(--bg-muted); }
2882
+ pre, code, .mono { font-family: var(--mono); overflow-wrap: anywhere; }
2883
+ pre {
2884
+ margin: 0; white-space: pre-wrap; word-break: break-word;
2885
+ background: var(--bg-inset); border-radius: 10px; padding: 12px; font-size: 12px;
2886
+ }
2887
+ .row-actions { display: flex; flex-wrap: wrap; gap: 8px; margin: 12px 0; }
2888
+ .banner {
2889
+ display: none; margin-bottom: 12px; padding: 10px 12px; border-radius: 10px;
2890
+ background: var(--err-soft); color: var(--err);
2891
+ }
2892
+ .banner.show { display: block; }
2893
+ .empty { color: var(--muted); padding: 24px 8px; }
2894
+ dialog {
2895
+ border: 1px solid var(--line); border-radius: var(--radius); padding: 0; background: var(--bg-elev);
2896
+ color: var(--ink); width: min(640px, calc(100vw - 24px)); box-shadow: none;
2897
+ }
2898
+ dialog::backdrop { background: rgb(20 19 17 / 0.45); }
2899
+ dialog form, .dialog-body { padding: 16px; display: flex; flex-direction: column; gap: 10px; }
2900
+ textarea {
2901
+ width: 100%; min-height: 180px; resize: vertical; border-radius: 10px;
2902
+ border: 1px solid var(--line); background: var(--bg-inset); padding: 10px;
2903
+ font-family: var(--mono); font-size: 12px;
2904
+ }
2905
+ .dialog-actions { display: flex; justify-content: flex-end; gap: 8px; }
2906
+ .badge { font-size: 11px; border-radius: 999px; padding: 2px 7px; background: var(--bg-muted); color: var(--muted); }
2907
+ .badge.ok { background: var(--ok-soft); color: var(--ok); }
2908
+ .badge.warn { background: var(--warn-soft); color: var(--warn); }
2909
+ .status-2 { color: var(--ok); }
2910
+ .status-4, .status-5, .status-0 { color: var(--err); }
2911
+ .stack { display: flex; flex-direction: column; gap: 12px; }
2912
+ .route {
2913
+ display: grid; grid-template-columns: 88px 1fr auto; gap: 8px; align-items: center;
2914
+ padding: 8px 0; border-bottom: 1px solid var(--line);
2915
+ }
2916
+ .method { font-family: var(--mono); font-size: 12px; font-weight: 700; }
2917
+ @media (max-width: 800px) {
2918
+ header.top {
2919
+ align-items: stretch; flex-direction: column;
2920
+ padding-top: max(10px, env(safe-area-inset-top));
2921
+ }
2922
+ input, select, textarea { font-size: 16px; }
2923
+ .brand, .vendor, .controls { width: 100%; }
2924
+ label.field, label.field select, label.field input { flex: 1; min-width: 0; }
2925
+ .layout { grid-template-columns: minmax(0, 1fr); }
2926
+ nav.side {
2927
+ flex-direction: row; align-items: center; overflow-x: auto; min-width: 0;
2928
+ border-right: 0; border-bottom: 1px solid var(--line);
2929
+ padding: 8px;
2930
+ }
2931
+ nav.side button { white-space: nowrap; flex: none; }
2932
+ .split { grid-template-columns: minmax(0, 1fr); }
2933
+ main { padding: 14px; }
2934
+ table, thead, tbody, tr, th, td { display: block; }
2935
+ thead { display: none; }
2936
+ tr {
2937
+ border: 1px solid var(--line); border-radius: 12px; margin-bottom: 8px; padding: 6px;
2938
+ background: var(--bg-elev); min-width: 0;
2939
+ }
2940
+ td {
2941
+ border: 0; display: flex; flex-wrap: wrap; justify-content: space-between; gap: 4px 12px;
2942
+ min-width: 0; overflow-wrap: anywhere;
2943
+ }
2944
+ td::before { content: attr(data-label); color: var(--faint); font-size: 12px; flex: none; }
2945
+ .route { grid-template-columns: 72px 1fr; }
2946
+ .route button { grid-column: 1 / -1; }
2947
+ }
2948
+ </style>
2949
+ </head>
2950
+ <body>
2951
+ <div class="app" data-service="${name}" data-mockingbird-admin>
2952
+ <header class="top">
2953
+ <div class="brand">
2954
+ <div class="brand-id">
2955
+ <strong>${name}</strong>
2956
+ <span>Admin</span>
2957
+ </div>
2958
+ <div class="vendor" id="vendor" hidden></div>
2959
+ </div>
2960
+ <div class="controls">
2961
+ <label class="field">Namespace
2962
+ <select id="namespace" aria-label="Namespace"></select>
2963
+ </label>
2964
+ <label class="field">Admin key
2965
+ <input id="admin-key" type="password" autocomplete="off" spellcheck="false" aria-label="Admin key">
2966
+ </label>
2967
+ <button class="icon-btn" id="theme" type="button" aria-label="Toggle color theme">Theme</button>
2968
+ <button class="btn" id="refresh" type="button">Refresh</button>
2969
+ </div>
2970
+ </header>
2971
+ <div class="layout">
2972
+ <nav class="side" id="nav" aria-label="Admin sections">
2973
+ <button type="button" data-view="overview" aria-current="true">Overview</button>
2974
+ <button type="button" data-view="state">State</button>
2975
+ <button type="button" data-view="clock">Clock</button>
2976
+ <button type="button" data-view="faults">Faults</button>
2977
+ <button type="button" data-view="journal">Journal</button>
2978
+ <button type="button" data-view="routes">Routes</button>
2979
+ </nav>
2980
+ <main id="views">
2981
+ <p class="banner" id="banner" role="alert"></p>
2982
+ <section class="view" id="view-overview">
2983
+ <h2>Overview</h2>
2984
+ <p class="lede">Health, clock, and the collections this namespace is holding.</p>
2985
+ <div class="grid" id="overview-cards"></div>
2986
+ <h3>Recent requests</h3>
2987
+ <div id="overview-requests"></div>
2988
+ </section>
2989
+ <section class="view" id="view-state" hidden>
2990
+ <h2>State</h2>
2991
+ <p class="lede">Every collection in this namespace. Declared fields stay visible before any row exists; stored rows add whatever else they contain.</p>
2992
+ <div class="split">
2993
+ <div id="collections"></div>
2994
+ <div>
2995
+ <div id="schema"></div>
2996
+ <div class="row-actions">
2997
+ <button class="btn-primary" id="new-record" type="button">New record</button>
2998
+ </div>
2999
+ <div id="records"></div>
3000
+ </div>
3001
+ </div>
3002
+ </section>
3003
+ <section class="view" id="view-clock" hidden>
3004
+ <h2>Clock</h2>
3005
+ <p class="lede">The clock every timestamp in this mock reads.</p>
3006
+ <div class="card" id="clock-readout"></div>
3007
+ <div class="row-actions">
3008
+ <button class="btn" type="button" data-advance="60000">+1m</button>
3009
+ <button class="btn" type="button" data-advance="900000">+15m</button>
3010
+ <button class="btn" type="button" data-advance="3600000">+1h</button>
3011
+ <button class="btn" type="button" data-advance="86400000">+1d</button>
3012
+ <button class="btn" type="button" data-advance="-3600000">\u22121h</button>
3013
+ <button class="btn" id="freeze" type="button">Freeze</button>
3014
+ <button class="btn-quiet" id="clock-reset" type="button">Reset</button>
3015
+ </div>
3016
+ <form id="clock-set" class="row-actions">
3017
+ <label class="field">Set instant
3018
+ <input id="clock-instant" type="text" placeholder="2026-01-01T00:00:00Z or epoch ms" size="32">
3019
+ </label>
3020
+ <button class="btn-primary" type="submit">Set</button>
3021
+ </form>
3022
+ </section>
3023
+ <section class="view" id="view-faults" hidden>
3024
+ <h2>Faults</h2>
3025
+ <p class="lede">Inject a response, a delay, or a named preset. Rules apply to this namespace.</p>
3026
+ <form id="fault-form" class="card stack">
3027
+ <label class="field">Preset
3028
+ <select id="fault-preset"><option value="">Custom rule</option></select>
3029
+ </label>
3030
+ <label class="field">Status
3031
+ <input id="fault-status" inputmode="numeric" placeholder="503">
3032
+ </label>
3033
+ <label class="field">Path prefix
3034
+ <input id="fault-path" placeholder="/v1">
3035
+ </label>
3036
+ <label class="field">Method
3037
+ <input id="fault-method" placeholder="GET">
3038
+ </label>
3039
+ <div class="dialog-actions">
3040
+ <button class="btn-primary" type="submit">Add fault</button>
3041
+ <button class="btn-danger" id="fault-clear" type="button">Clear all</button>
3042
+ </div>
3043
+ </form>
3044
+ <div id="fault-list"></div>
3045
+ </section>
3046
+ <section class="view" id="view-journal" hidden>
3047
+ <h2>Journal</h2>
3048
+ <p class="lede">Requests this namespace has seen. Bodies are not stored.</p>
3049
+ <div class="row-actions">
3050
+ <button class="btn-danger" id="journal-clear" type="button">Clear journal</button>
3051
+ </div>
3052
+ <div id="journal"></div>
3053
+ </section>
3054
+ <section class="view" id="view-routes" hidden>
3055
+ <h2>Routes</h2>
3056
+ <p class="lede">Shared controls, plus any routes this mock adds. Extra routes are how a bespoke workflow shows up beside the default UI.</p>
3057
+ <div id="route-list"></div>
3058
+ <form id="route-form" class="card stack">
3059
+ <strong id="route-title">Select a route</strong>
3060
+ <label class="field">JSON body
3061
+ <textarea id="route-body" placeholder="{ }"></textarea>
3062
+ </label>
3063
+ <div class="dialog-actions">
3064
+ <button class="btn-primary" type="submit">Send</button>
3065
+ </div>
3066
+ <pre id="route-result">Response will show here.</pre>
3067
+ </form>
3068
+ </section>
3069
+ </main>
3070
+ </div>
3071
+ </div>
3072
+ <dialog id="editor">
3073
+ <form method="dialog" id="editor-form">
3074
+ <strong id="editor-title">Record</strong>
3075
+ <label class="field">Id
3076
+ <input id="editor-id" required>
3077
+ </label>
3078
+ <label class="field">JSON value
3079
+ <textarea id="editor-value"></textarea>
3080
+ </label>
3081
+ <div class="dialog-actions">
3082
+ <button class="btn-danger" id="editor-delete" type="button" value="delete">Delete</button>
3083
+ <button class="btn-quiet" type="submit" value="cancel">Cancel</button>
3084
+ <button class="btn-primary" type="submit" value="save">Save</button>
3085
+ </div>
3086
+ </form>
3087
+ </dialog>
3088
+ ${script}
3089
+ </body>
3090
+ </html>`;
3091
+ };
3092
+ var htmlResponse = (body) => new Response(body, {
3093
+ status: 200,
3094
+ headers: {
3095
+ "content-type": "text/html; charset=utf-8",
3096
+ "cache-control": "no-store"
3097
+ }
3098
+ });
3099
+ var json2 = (body) => new Response(JSON.stringify(body), {
3100
+ status: 200,
3101
+ headers: { "content-type": "application/json; charset=utf-8" }
3102
+ });
3103
+ var adminUiRoutes = (service, ui) => {
3104
+ const shell = () => renderAdminDocument(service);
3105
+ const document3 = () => ui?.render ? ui.render({ service, defaultHtml: shell }) : shell();
3106
+ return {
3107
+ "GET /ui": () => htmlResponse(document3()),
3108
+ "GET /ui/": () => htmlResponse(document3()),
3109
+ "GET /ui/manifest": () => json2({
3110
+ service,
3111
+ panels: (ui?.panels ?? []).map((panel) => ({
3112
+ id: panel.id,
3113
+ title: panel.title,
3114
+ ...panel.description !== void 0 ? { description: panel.description } : {},
3115
+ html: panel.html,
3116
+ ...panel.script !== void 0 ? { script: panel.script } : {}
3117
+ }))
3118
+ })
3119
+ };
3120
+ };
3121
+
1389
3122
  // ../../openapi/metadata/dist/types.js
1390
3123
  var EXTENSION_KEYS = {
1391
3124
  operation: "x-mockingbird",
@@ -1398,11 +3131,11 @@ var EXTENSION_KEYS = {
1398
3131
  };
1399
3132
 
1400
3133
  // ../../openapi/metadata/dist/read.js
1401
- var isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
3134
+ var isRecord3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
1402
3135
  var extensionOf = (holder, key) => holder[key];
1403
3136
  var operationMetadata = (operation) => {
1404
3137
  const raw = extensionOf(operation, EXTENSION_KEYS.operation);
1405
- const ext = isRecord2(raw) ? raw : {};
3138
+ const ext = isRecord3(raw) ? raw : {};
1406
3139
  const supported = ext.supported ?? true;
1407
3140
  const parity = ext.parity ?? {};
1408
3141
  return {
@@ -1512,9 +3245,9 @@ ${problems.map((p) => ` - ${p}`).join("\n")}`);
1512
3245
  this.name = "OperationRegistryError";
1513
3246
  }
1514
3247
  };
1515
- var verifyOperations = (document2, handlers) => {
3248
+ var verifyOperations = (document3, handlers) => {
1516
3249
  const problems = [];
1517
- const operations = listOperations(document2);
3250
+ const operations = listOperations(document3);
1518
3251
  const seen = /* @__PURE__ */ new Set();
1519
3252
  for (const operation of operations) {
1520
3253
  if (seen.has(operation.operationId))
@@ -1623,15 +3356,449 @@ var restoreNamespace = (sqlite, namespace, snapshot) => {
1623
3356
  });
1624
3357
  };
1625
3358
 
3359
+ // ../core/dist/state-view.js
3360
+ var COLLECTION_NAME = /^[A-Za-z0-9_.@:-]{1,200}$/;
3361
+ var RECORD_ID = /^[^\s/]{1,256}$/;
3362
+ var SAMPLE = 25;
3363
+ var WALK_LIMIT = 1e4;
3364
+ var SKIP_CTORS = /* @__PURE__ */ new Set(["Hono", "HonoRequest", "Router"]);
3365
+ var fail2 = (status, message) => ({ ok: false, status, message });
3366
+ var isRecord4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
3367
+ var kindOf = (value) => {
3368
+ if (value === null)
3369
+ return "null";
3370
+ if (Array.isArray(value))
3371
+ return "array";
3372
+ switch (typeof value) {
3373
+ case "string":
3374
+ return "string";
3375
+ case "number":
3376
+ return Number.isFinite(value) ? "number" : "unknown";
3377
+ case "boolean":
3378
+ return "boolean";
3379
+ case "object":
3380
+ return "object";
3381
+ default:
3382
+ return "unknown";
3383
+ }
3384
+ };
3385
+ var humanize = (name) => {
3386
+ const words = name.replace(/[@:_-]+/g, " ").trim();
3387
+ if (words === "")
3388
+ return name;
3389
+ return words.charAt(0).toUpperCase() + words.slice(1);
3390
+ };
3391
+ var jsonValue = (value) => {
3392
+ if (value === void 0)
3393
+ return fail2(400, "value is required");
3394
+ try {
3395
+ return { ok: true, value: JSON.parse(JSON.stringify(value)) };
3396
+ } catch {
3397
+ return fail2(400, "value must be JSON");
3398
+ }
3399
+ };
3400
+ var ctorName = (value) => {
3401
+ const ctor = value.constructor;
3402
+ return typeof ctor?.name === "string" ? ctor.name : "";
3403
+ };
3404
+ var isSqlite = (value) => "prepare" in value && typeof value.prepare === "function" && "transaction" in value;
3405
+ var discoverCollections = (root) => {
3406
+ const found = [];
3407
+ const seen = /* @__PURE__ */ new Set();
3408
+ const visit = (value, depth) => {
3409
+ if (found.length + seen.size > WALK_LIMIT)
3410
+ return;
3411
+ if (value === null || typeof value !== "object")
3412
+ return;
3413
+ if (seen.has(value))
3414
+ return;
3415
+ seen.add(value);
3416
+ if (value instanceof Collection) {
3417
+ found.push(value);
3418
+ return;
3419
+ }
3420
+ if (value instanceof Map) {
3421
+ for (const child of value.values())
3422
+ visit(child, depth + 1);
3423
+ return;
3424
+ }
3425
+ if (value instanceof Set) {
3426
+ for (const child of value.values())
3427
+ visit(child, depth + 1);
3428
+ return;
3429
+ }
3430
+ if (depth > 6 || isSqlite(value) || SKIP_CTORS.has(ctorName(value)))
3431
+ return;
3432
+ if (Array.isArray(value)) {
3433
+ const cap = Math.min(value.length, 32);
3434
+ for (let i = 0; i < cap; i++)
3435
+ visit(value[i], depth + 1);
3436
+ return;
3437
+ }
3438
+ let children;
3439
+ try {
3440
+ children = Object.values(value);
3441
+ } catch {
3442
+ return;
3443
+ }
3444
+ for (const child of children)
3445
+ visit(child, depth + 1);
3446
+ };
3447
+ visit(root, 0);
3448
+ return found;
3449
+ };
3450
+ var storedNames = (sqlite, namespace) => {
3451
+ const records = sqlite.prepare("SELECT DISTINCT collection AS name FROM mockingbird_records WHERE namespace = ?").all(namespace);
3452
+ const sequences = sqlite.prepare("SELECT DISTINCT name FROM mockingbird_sequences WHERE namespace = ? AND kind = 'collection'").all(namespace);
3453
+ return [...records, ...sequences].map((row) => row.name);
3454
+ };
3455
+ var blankAccum = (name) => ({
3456
+ name,
3457
+ kinds: /* @__PURE__ */ new Set(),
3458
+ present: 0,
3459
+ nested: /* @__PURE__ */ new Map()
3460
+ });
3461
+ var absorb = (accum, value) => {
3462
+ const kind = kindOf(value);
3463
+ accum.kinds.add(kind);
3464
+ accum.present++;
3465
+ if (!isRecord4(value))
3466
+ return;
3467
+ for (const [key, child] of Object.entries(value)) {
3468
+ const nested = accum.nested.get(key) ?? blankAccum(key);
3469
+ absorb(nested, child);
3470
+ accum.nested.set(key, nested);
3471
+ }
3472
+ };
3473
+ var finishField = (accum, samples) => {
3474
+ const kinds = [...accum.kinds];
3475
+ const kind = kinds.length === 1 ? kinds[0] : "unknown";
3476
+ const field = {
3477
+ name: accum.name,
3478
+ kind,
3479
+ optional: accum.present < samples || kinds.includes("null")
3480
+ };
3481
+ if (accum.nested.size > 0) {
3482
+ field.fields = [...accum.nested.values()].sort((a, b) => a.name.localeCompare(b.name)).map((child) => finishField(child, accum.present));
3483
+ }
3484
+ return field;
3485
+ };
3486
+ var inferFields = (records) => {
3487
+ if (records.length === 0)
3488
+ return [];
3489
+ const sample = records.slice(0, SAMPLE);
3490
+ const objects = sample.filter((record) => isRecord4(record.value));
3491
+ if (objects.length === 0) {
3492
+ const kinds = new Set(sample.map((record) => kindOf(record.value)));
3493
+ const list = [...kinds];
3494
+ return [
3495
+ {
3496
+ name: "(value)",
3497
+ kind: list.length === 1 ? list[0] : "unknown",
3498
+ optional: false
3499
+ }
3500
+ ];
3501
+ }
3502
+ const root = /* @__PURE__ */ new Map();
3503
+ for (const record of objects) {
3504
+ for (const [key, value] of Object.entries(record.value)) {
3505
+ const accum = root.get(key) ?? blankAccum(key);
3506
+ absorb(accum, value);
3507
+ root.set(key, accum);
3508
+ }
3509
+ }
3510
+ return [...root.values()].sort((a, b) => a.name.localeCompare(b.name)).map((accum) => finishField(accum, objects.length));
3511
+ };
3512
+ var mergeFields = (declared, inferred) => {
3513
+ const byName = /* @__PURE__ */ new Map();
3514
+ for (const field of inferred)
3515
+ byName.set(field.name, field);
3516
+ const ordered = [];
3517
+ const seen = /* @__PURE__ */ new Set();
3518
+ for (const field of declared ?? []) {
3519
+ const found = byName.get(field.name);
3520
+ seen.add(field.name);
3521
+ if (!found) {
3522
+ ordered.push({ ...field, optional: field.optional });
3523
+ continue;
3524
+ }
3525
+ ordered.push({
3526
+ ...found,
3527
+ kind: field.kind === "unknown" ? found.kind : field.kind,
3528
+ optional: field.optional || found.optional,
3529
+ ...field.description !== void 0 ? { description: field.description } : {},
3530
+ ...field.meta !== void 0 ? { meta: field.meta } : {},
3531
+ ...field.fields !== void 0 ? { fields: mergeFields(field.fields, found.fields ?? []) } : {}
3532
+ });
3533
+ }
3534
+ for (const field of inferred) {
3535
+ if (!seen.has(field.name))
3536
+ ordered.push(field);
3537
+ }
3538
+ return ordered;
3539
+ };
3540
+ var collectionOf = (sqlite, namespace, name) => new Collection(sqlite, namespace, name);
3541
+ var readRecord = (collection, id) => {
3542
+ const rows = collection.list({ where: (_value, _seq) => true, order: "oldest" });
3543
+ const row = rows.find((entry) => entry.id === id);
3544
+ return row ? { id: row.id, seq: row.seq, value: row.value } : void 0;
3545
+ };
3546
+ var inspectState = (scope) => {
3547
+ const declared = /* @__PURE__ */ new Map();
3548
+ for (const declaration of scope.declarations ?? []) {
3549
+ if (COLLECTION_NAME.test(declaration.name))
3550
+ declared.set(declaration.name, declaration);
3551
+ }
3552
+ const names = new Set(declared.keys());
3553
+ for (const name of storedNames(scope.sqlite, scope.storageNamespace))
3554
+ names.add(name);
3555
+ for (const collection of discoverCollections(scope.root)) {
3556
+ if (collection.namespace === scope.storageNamespace)
3557
+ names.add(collection.collectionName);
3558
+ }
3559
+ const collections = [];
3560
+ for (const name of names) {
3561
+ if (!COLLECTION_NAME.test(name))
3562
+ continue;
3563
+ const declaration = declared.get(name);
3564
+ const collection = collectionOf(scope.sqlite, scope.storageNamespace, name);
3565
+ const records = collection.list({ order: "newest" }).slice(0, SAMPLE).map((row) => ({ id: row.id, seq: row.seq, value: row.value }));
3566
+ const inferred = inferFields(records);
3567
+ const fields = mergeFields(declaration?.fields, inferred);
3568
+ const source = declaration && records.length > 0 ? "mixed" : declaration ? "declared" : "inferred";
3569
+ collections.push({
3570
+ name,
3571
+ label: declaration?.label ?? humanize(name),
3572
+ ...declaration?.description !== void 0 ? { description: declaration.description } : {},
3573
+ count: collection.count(),
3574
+ source,
3575
+ fields,
3576
+ ...declaration?.meta !== void 0 ? { meta: declaration.meta } : {}
3577
+ });
3578
+ }
3579
+ const order = /* @__PURE__ */ new Map();
3580
+ for (const [index, item] of (scope.declarations ?? []).entries())
3581
+ order.set(item.name, index);
3582
+ collections.sort((a, b) => {
3583
+ const left = order.get(a.name);
3584
+ const right = order.get(b.name);
3585
+ if (left !== void 0 && right !== void 0)
3586
+ return left - right;
3587
+ if (left !== void 0)
3588
+ return -1;
3589
+ if (right !== void 0)
3590
+ return 1;
3591
+ return a.name.localeCompare(b.name);
3592
+ });
3593
+ return {
3594
+ namespace: scope.namespace,
3595
+ storageNamespace: scope.storageNamespace,
3596
+ collections
3597
+ };
3598
+ };
3599
+ var pageOf = (sqlite, storageNamespace, name, query) => {
3600
+ if (!COLLECTION_NAME.test(name))
3601
+ return fail2(400, "collection name is not allowed");
3602
+ const limit = query.limit ?? 50;
3603
+ if (!Number.isInteger(limit) || limit < 1 || limit > 200) {
3604
+ return fail2(400, "limit must be an integer from 1 to 200");
3605
+ }
3606
+ const order = query.order ?? "newest";
3607
+ if (order !== "newest" && order !== "oldest")
3608
+ return fail2(400, 'order must be "newest" or "oldest"');
3609
+ const collection = collectionOf(sqlite, storageNamespace, name);
3610
+ const rows = collection.list({ order }).filter((row) => {
3611
+ if (query.after === void 0)
3612
+ return true;
3613
+ return order === "newest" ? row.seq < query.after : row.seq > query.after;
3614
+ });
3615
+ const slice = rows.slice(0, limit);
3616
+ const last = slice.at(-1);
3617
+ return {
3618
+ ok: true,
3619
+ value: {
3620
+ collection: name,
3621
+ count: collection.count(),
3622
+ records: slice.map((row) => ({ id: row.id, seq: row.seq, value: row.value })),
3623
+ next: rows.length > limit && last ? last.seq : null
3624
+ }
3625
+ };
3626
+ };
3627
+ var readStatePage = (scope, name, query = {}) => pageOf(scope.sqlite, scope.storageNamespace, name, query);
3628
+ var readStateRecord = (scope, name, id) => {
3629
+ if (!COLLECTION_NAME.test(name))
3630
+ return fail2(400, "collection name is not allowed");
3631
+ if (!RECORD_ID.test(id))
3632
+ return fail2(400, "record id is not allowed");
3633
+ const record = readRecord(collectionOf(scope.sqlite, scope.storageNamespace, name), id);
3634
+ return record ? { ok: true, value: record } : fail2(404, `no record ${id} in ${name}`);
3635
+ };
3636
+ var requireId = (id) => RECORD_ID.test(id) ? { ok: true, value: id } : fail2(400, "id must be a single path segment");
3637
+ var createStateRecord = (scope, name, input) => {
3638
+ if (!COLLECTION_NAME.test(name))
3639
+ return fail2(400, "collection name is not allowed");
3640
+ const parsed = jsonValue(input.value);
3641
+ if (!parsed.ok)
3642
+ return parsed;
3643
+ const collection = collectionOf(scope.sqlite, scope.storageNamespace, name);
3644
+ const id = input.id ?? `${name}_${collection.nextSequence()}`;
3645
+ const checked = requireId(id);
3646
+ if (!checked.ok)
3647
+ return checked;
3648
+ if (collection.has(checked.value))
3649
+ return fail2(409, `record ${checked.value} already exists`);
3650
+ const stored = collection.insert(checked.value, parsed.value);
3651
+ return { ok: true, value: { id: checked.value, seq: stored.seq, value: stored.value } };
3652
+ };
3653
+ var replaceStateRecord = (scope, name, id, value) => {
3654
+ if (!COLLECTION_NAME.test(name))
3655
+ return fail2(400, "collection name is not allowed");
3656
+ const checked = requireId(id);
3657
+ if (!checked.ok)
3658
+ return checked;
3659
+ const parsed = jsonValue(value);
3660
+ if (!parsed.ok)
3661
+ return parsed;
3662
+ const collection = collectionOf(scope.sqlite, scope.storageNamespace, name);
3663
+ if (!collection.has(checked.value))
3664
+ return fail2(404, `no record ${checked.value} in ${name}`);
3665
+ const stored = collection.update(checked.value, parsed.value);
3666
+ if (!stored)
3667
+ return fail2(404, `no record ${checked.value} in ${name}`);
3668
+ return { ok: true, value: { id: checked.value, seq: stored.seq, value: stored.value } };
3669
+ };
3670
+ var patchStateRecord = (scope, name, id, value) => {
3671
+ const current = readStateRecord(scope, name, id);
3672
+ if (!current.ok)
3673
+ return current;
3674
+ const parsed = jsonValue(value);
3675
+ if (!parsed.ok)
3676
+ return parsed;
3677
+ const next = isRecord4(current.value.value) && isRecord4(parsed.value) ? { ...current.value.value, ...parsed.value } : parsed.value;
3678
+ return replaceStateRecord(scope, name, id, next);
3679
+ };
3680
+ var deleteStateRecord = (scope, name, id) => {
3681
+ if (!COLLECTION_NAME.test(name))
3682
+ return fail2(400, "collection name is not allowed");
3683
+ const checked = requireId(id);
3684
+ if (!checked.ok)
3685
+ return checked;
3686
+ const collection = collectionOf(scope.sqlite, scope.storageNamespace, name);
3687
+ return collection.delete(checked.value) ? { ok: true, value: { id: checked.value } } : fail2(404, `no record ${checked.value} in ${name}`);
3688
+ };
3689
+ var json3 = (status, body) => new Response(JSON.stringify(body), {
3690
+ status,
3691
+ headers: { "content-type": "application/json; charset=utf-8" }
3692
+ });
3693
+ var adminError2 = (status, message) => json3(status, { error: { type: "mockingbird_admin", message } });
3694
+ var fromResult = (result, status) => result.ok ? json3(status, result.value) : adminError2(result.status, result.message);
3695
+ var integerParam = (value) => {
3696
+ if (value === null)
3697
+ return void 0;
3698
+ if (!/^-?\d+$/.test(value))
3699
+ return "bad";
3700
+ return Number(value);
3701
+ };
3702
+ var stateAdminRoutes = (routes) => {
3703
+ const open = (namespace) => {
3704
+ try {
3705
+ return { ok: true, value: routes.open(namespace) };
3706
+ } catch (error) {
3707
+ return fail2(400, error instanceof Error ? error.message : String(error));
3708
+ }
3709
+ };
3710
+ const wrote = (namespace) => {
3711
+ routes.afterWrite?.(namespace);
3712
+ };
3713
+ return {
3714
+ "GET /state": ({ namespace }) => {
3715
+ const scope = open(namespace);
3716
+ return fromResult(scope.ok ? { ok: true, value: inspectState(scope.value) } : scope, 200);
3717
+ },
3718
+ "GET /state/:collection": ({ namespace, params, url }) => {
3719
+ const scope = open(namespace);
3720
+ if (!scope.ok)
3721
+ return fromResult(scope, 200);
3722
+ const limit = integerParam(url.searchParams.get("limit"));
3723
+ const after = integerParam(url.searchParams.get("after"));
3724
+ if (limit === "bad")
3725
+ return adminError2(400, "limit: expected an integer");
3726
+ if (after === "bad")
3727
+ return adminError2(400, "after: expected a sequence number");
3728
+ const order = url.searchParams.get("order");
3729
+ return fromResult(readStatePage(scope.value, params.collection ?? "", {
3730
+ ...limit !== void 0 ? { limit } : {},
3731
+ ...after !== void 0 ? { after } : {},
3732
+ ...order === "newest" || order === "oldest" ? { order } : {}
3733
+ }), 200);
3734
+ },
3735
+ "GET /state/:collection/:id": ({ namespace, params }) => {
3736
+ const scope = open(namespace);
3737
+ if (!scope.ok)
3738
+ return fromResult(scope, 200);
3739
+ return fromResult(readStateRecord(scope.value, params.collection ?? "", params.id ?? ""), 200);
3740
+ },
3741
+ "POST /state/:collection": ({ namespace, params, body }) => {
3742
+ const scope = open(namespace);
3743
+ if (!scope.ok)
3744
+ return fromResult(scope, 201);
3745
+ if (!isRecord4(body) || !("value" in body)) {
3746
+ return adminError2(400, 'expected {"id?": "<id>", "value": <json>}');
3747
+ }
3748
+ const id = body.id;
3749
+ if (id !== void 0 && typeof id !== "string")
3750
+ return adminError2(400, "id must be a string");
3751
+ const created = createStateRecord(scope.value, params.collection ?? "", {
3752
+ ...typeof id === "string" ? { id } : {},
3753
+ value: body.value
3754
+ });
3755
+ if (created.ok)
3756
+ wrote(namespace);
3757
+ return fromResult(created, 201);
3758
+ },
3759
+ "PUT /state/:collection/:id": ({ namespace, params, body }) => {
3760
+ const scope = open(namespace);
3761
+ if (!scope.ok)
3762
+ return fromResult(scope, 200);
3763
+ if (!isRecord4(body) || !("value" in body))
3764
+ return adminError2(400, 'expected {"value": <json>}');
3765
+ const replaced = replaceStateRecord(scope.value, params.collection ?? "", params.id ?? "", body.value);
3766
+ if (replaced.ok)
3767
+ wrote(namespace);
3768
+ return fromResult(replaced, 200);
3769
+ },
3770
+ "PATCH /state/:collection/:id": ({ namespace, params, body }) => {
3771
+ const scope = open(namespace);
3772
+ if (!scope.ok)
3773
+ return fromResult(scope, 200);
3774
+ if (!isRecord4(body) || !("value" in body))
3775
+ return adminError2(400, 'expected {"value": <json>}');
3776
+ const patched = patchStateRecord(scope.value, params.collection ?? "", params.id ?? "", body.value);
3777
+ if (patched.ok)
3778
+ wrote(namespace);
3779
+ return fromResult(patched, 200);
3780
+ },
3781
+ "DELETE /state/:collection/:id": ({ namespace, params }) => {
3782
+ const scope = open(namespace);
3783
+ if (!scope.ok)
3784
+ return fromResult(scope, 200);
3785
+ const removed = deleteStateRecord(scope.value, params.collection ?? "", params.id ?? "");
3786
+ if (removed.ok)
3787
+ wrote(namespace);
3788
+ return fromResult(removed, 200);
3789
+ }
3790
+ };
3791
+ };
3792
+
1626
3793
  // ../core/dist/version.js
1627
- var PACKAGE_VERSION = true ? "1.3.0" : UNRELEASED_VERSION;
3794
+ var PACKAGE_VERSION = true ? "1.5.0" : UNRELEASED_VERSION;
1628
3795
 
1629
3796
  // ../core/dist/webhooks.js
1630
- var json2 = (status, body) => new Response(JSON.stringify(body), { status, headers: { "content-type": "application/json" } });
1631
- var adminError2 = (status, message) => json2(status, { error: { type: "mockingbird_admin", message } });
1632
- var isRecord3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
3797
+ var json4 = (status, body) => new Response(JSON.stringify(body), { status, headers: { "content-type": "application/json" } });
3798
+ var adminError3 = (status, message) => json4(status, { error: { type: "mockingbird_admin", message } });
3799
+ var isRecord5 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
1633
3800
  var parseEndpoint = (value) => {
1634
- if (!isRecord3(value) || typeof value.url !== "string")
3801
+ if (!isRecord5(value) || typeof value.url !== "string")
1635
3802
  return "each endpoint needs a url";
1636
3803
  try {
1637
3804
  new URL(value.url);
@@ -1648,18 +3815,18 @@ var parseEndpoint = (value) => {
1648
3815
  const events = value.events ?? value.enabledEvents;
1649
3816
  if (Array.isArray(events))
1650
3817
  endpoint.events = events.map(String);
1651
- if (isRecord3(value.tags)) {
3818
+ if (isRecord5(value.tags)) {
1652
3819
  endpoint.tags = Object.fromEntries(Object.entries(value.tags).map(([k, v]) => [k, String(v)]));
1653
3820
  }
1654
3821
  if (typeof value.account === "string")
1655
3822
  endpoint.tags = { ...endpoint.tags, account: value.account };
1656
- if (isRecord3(value.headers)) {
3823
+ if (isRecord5(value.headers)) {
1657
3824
  endpoint.headers = Object.fromEntries(Object.entries(value.headers).map(([k, v]) => [k, String(v)]));
1658
3825
  }
1659
3826
  return endpoint;
1660
3827
  };
1661
3828
  var webhookAdminRoutes = (hub) => ({
1662
- "GET /webhooks": ({ url, namespace }) => json2(200, {
3829
+ "GET /webhooks": ({ url, namespace }) => json4(200, {
1663
3830
  deliveries: hub.deliveries(url.searchParams.get("all") === "1" ? void 0 : namespace).filter((d) => {
1664
3831
  const type = url.searchParams.get("type");
1665
3832
  return type === null || d.type === type;
@@ -1667,51 +3834,51 @@ var webhookAdminRoutes = (hub) => ({
1667
3834
  }),
1668
3835
  "GET /webhooks/events": ({ url, namespace }) => {
1669
3836
  const type = url.searchParams.get("type");
1670
- return json2(200, {
3837
+ return json4(200, {
1671
3838
  events: hub.messages(url.searchParams.get("all") === "1" ? void 0 : namespace).filter((m) => type === null || m.type === type).map((m) => ({ ...m, payload: parsePayload(m) }))
1672
3839
  });
1673
3840
  },
1674
3841
  "POST /webhooks/:id/replay": async ({ params }) => {
1675
3842
  const replayed = await hub.replay(params.id);
1676
- return replayed ? json2(200, replayed) : adminError2(404, `no delivery ${params.id}`);
3843
+ return replayed ? json4(200, replayed) : adminError3(404, `no delivery ${params.id}`);
1677
3844
  },
1678
3845
  "POST /webhooks/flush": async () => {
1679
3846
  await hub.flush();
1680
- return json2(200, { status: "ok" });
3847
+ return json4(200, { status: "ok" });
1681
3848
  },
1682
3849
  "POST /webhooks/faults": ({ body, namespace }) => {
1683
- if (!isRecord3(body) || !["duplicate", "reorder", "drop"].includes(String(body.mode))) {
1684
- return adminError2(400, "mode must be duplicate, reorder or drop");
3850
+ if (!isRecord5(body) || !["duplicate", "reorder", "drop"].includes(String(body.mode))) {
3851
+ return adminError3(400, "mode must be duplicate, reorder or drop");
1685
3852
  }
1686
3853
  const fault = { mode: body.mode };
1687
3854
  if (typeof body.count === "number")
1688
3855
  fault.count = body.count;
1689
3856
  hub.fault(namespace, fault);
1690
- return json2(201, { namespace, ...fault });
3857
+ return json4(201, { namespace, ...fault });
1691
3858
  },
1692
- "GET /webhook-endpoints": ({ namespace }) => json2(200, {
3859
+ "GET /webhook-endpoints": ({ namespace }) => json4(200, {
1693
3860
  endpoints: hub.endpoints(namespace).map(({ secret, ...rest }) => ({
1694
3861
  ...rest,
1695
3862
  secret: secret ? "(set)" : null
1696
3863
  }))
1697
3864
  }),
1698
3865
  "PUT /webhook-endpoints": ({ body, namespace }) => {
1699
- const list = Array.isArray(body) ? body : isRecord3(body) ? body.endpoints : void 0;
3866
+ const list = Array.isArray(body) ? body : isRecord5(body) ? body.endpoints : void 0;
1700
3867
  if (!Array.isArray(list))
1701
- return adminError2(400, "expected [{url, secret?, events?}]");
3868
+ return adminError3(400, "expected [{url, secret?, events?}]");
1702
3869
  const parsed = [];
1703
3870
  for (const each of list) {
1704
3871
  const endpoint = parseEndpoint(each);
1705
3872
  if (typeof endpoint === "string")
1706
- return adminError2(400, endpoint);
3873
+ return adminError3(400, endpoint);
1707
3874
  parsed.push(endpoint);
1708
3875
  }
1709
3876
  const set = hub.setEndpoints(namespace, parsed);
1710
- return json2(200, { endpoints: set.map((e) => ({ ...e, secret: e.secret ? "(set)" : null })) });
3877
+ return json4(200, { endpoints: set.map((e) => ({ ...e, secret: e.secret ? "(set)" : null })) });
1711
3878
  },
1712
3879
  "DELETE /webhook-endpoints": ({ namespace }) => {
1713
3880
  hub.setEndpoints(namespace, []);
1714
- return json2(200, { status: "ok" });
3881
+ return json4(200, { status: "ok" });
1715
3882
  }
1716
3883
  });
1717
3884
  var parsePayload = (message) => {
@@ -1764,8 +3931,8 @@ var DroppedConnectionError = class extends TypeError {
1764
3931
  this.name = "TypeError";
1765
3932
  }
1766
3933
  };
1767
- var operationMatcher = (document2) => {
1768
- const matchers = listOperations(document2).map((operation) => ({
3934
+ var operationMatcher = (document3) => {
3935
+ const matchers = listOperations(document3).map((operation) => ({
1769
3936
  operationId: operation.operationId,
1770
3937
  method: operation.method.toUpperCase(),
1771
3938
  pattern: new RegExp(`^${operation.path.split("/").map((segment) => segment.startsWith("{") ? "[^/]+" : segment.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("/")}/?$`),
@@ -1774,6 +3941,7 @@ var operationMatcher = (document2) => {
1774
3941
  return (request, path) => matchers.find((m) => m.method === request.method && m.pattern.test(path))?.operationId;
1775
3942
  };
1776
3943
  var createRuntime = (options) => {
3944
+ assertAdminUi(options.adminUi);
1777
3945
  const sqlite = bootSqlite(options.sqlite);
1778
3946
  const clock = options.clock ?? createClock();
1779
3947
  const rng = createRng(options.seed ?? 0);
@@ -1859,10 +4027,10 @@ var createRuntime = (options) => {
1859
4027
  const ensureBranch = (namespace, branch2, at) => {
1860
4028
  if (!BRANCH_PATTERN2.test(branch2))
1861
4029
  throw new RangeError(`branch must match ${BRANCH_PATTERN2}`);
1862
- const history = timeline(namespace);
4030
+ const history2 = timeline(namespace);
1863
4031
  if (branch2 === "main") {
1864
4032
  if (at !== void 0) {
1865
- const point = history.checkout("main", at);
4033
+ const point = history2.checkout("main", at);
1866
4034
  restoreNamespace(sqlite, storageNamespace(namespace), point.value.snapshot);
1867
4035
  captured.set(namespace, point.value.snapshot);
1868
4036
  rng.setState(point.value.rngState);
@@ -1875,10 +4043,10 @@ var createRuntime = (options) => {
1875
4043
  return namespace;
1876
4044
  }
1877
4045
  const storage = physicalBranch(namespace, branch2);
1878
- if (!history.hasBranch(branch2)) {
4046
+ if (!history2.hasBranch(branch2)) {
1879
4047
  if (at === void 0)
1880
- history.commit(capture(namespace));
1881
- const point = history.fork(branch2, at === void 0 ? {} : { from: at });
4048
+ history2.commit(capture(namespace));
4049
+ const point = history2.fork(branch2, at === void 0 ? {} : { from: at });
1882
4050
  const branchRng = createRng(options.seed ?? 0);
1883
4051
  if (point)
1884
4052
  branchRng.setState(point.value.rngState);
@@ -1887,8 +4055,8 @@ var createRuntime = (options) => {
1887
4055
  restoreNamespace(sqlite, storageNamespace(storage), point.value.snapshot);
1888
4056
  if (point)
1889
4057
  captured.set(storage, point.value.snapshot);
1890
- } else if (at !== void 0 && history.head(branch2)?.id !== at) {
1891
- const point = history.checkout(branch2, at);
4058
+ } else if (at !== void 0 && history2.head(branch2)?.id !== at) {
4059
+ const point = history2.checkout(branch2, at);
1892
4060
  if (!instances.has(storage)) {
1893
4061
  const branchRng = createRng(options.seed ?? 0);
1894
4062
  branchRng.setState(point.value.rngState);
@@ -1899,7 +4067,7 @@ var createRuntime = (options) => {
1899
4067
  captured.set(storage, point.value.snapshot);
1900
4068
  } else {
1901
4069
  if (!instances.has(storage)) {
1902
- const point = history.head(branch2);
4070
+ const point = history2.head(branch2);
1903
4071
  const branchRng = createRng(options.seed ?? 0);
1904
4072
  if (point)
1905
4073
  branchRng.setState(point.value.rngState);
@@ -1923,8 +4091,8 @@ var createRuntime = (options) => {
1923
4091
  const checkout = (checkpointId, checkoutOptions = {}) => {
1924
4092
  const namespace = checkoutOptions.namespace ?? DEFAULT_NAMESPACE;
1925
4093
  const branchName = checkoutOptions.branch ?? "main";
1926
- const history = timeline(namespace);
1927
- const point = history.checkout(branchName, checkpointId);
4094
+ const history2 = timeline(namespace);
4095
+ const point = history2.checkout(branchName, checkpointId);
1928
4096
  const storage = ensureBranch(namespace, branchName);
1929
4097
  restoreNamespace(sqlite, storageNamespace(storage), point.value.snapshot);
1930
4098
  captured.set(storage, point.value.snapshot);
@@ -1974,12 +4142,25 @@ var createRuntime = (options) => {
1974
4142
  instance(name);
1975
4143
  restoreNamespace(sqlite, storageNamespace(name), from);
1976
4144
  captured.set(name, from);
1977
- const history = timelines.get(name);
1978
- if (history)
1979
- history.commit(capture(name), { branch: "main" });
4145
+ const history2 = timelines.get(name);
4146
+ if (history2)
4147
+ history2.commit(capture(name), { branch: "main" });
1980
4148
  else
1981
4149
  timeline(name);
1982
4150
  };
4151
+ const declarationsFor = (namespace) => {
4152
+ const declared = options.state;
4153
+ if (!declared)
4154
+ return [];
4155
+ return typeof declared === "function" ? declared(instance(namespace), namespace) : declared;
4156
+ };
4157
+ const stateScope = (namespace) => ({
4158
+ sqlite,
4159
+ namespace,
4160
+ storageNamespace: storageNamespace(namespace),
4161
+ root: instance(namespace),
4162
+ declarations: declarationsFor(namespace)
4163
+ });
1983
4164
  const runtime = {
1984
4165
  name: options.name,
1985
4166
  sqlite,
@@ -2018,6 +4199,7 @@ var createRuntime = (options) => {
2018
4199
  branch,
2019
4200
  checkout,
2020
4201
  timeline,
4202
+ state: (namespace = DEFAULT_NAMESPACE) => inspectState(stateScope(namespace)),
2021
4203
  fetch: async (incoming) => {
2022
4204
  let request = incoming;
2023
4205
  const prefixed = PATH_PREFIX.exec(new URL(request.url).pathname);
@@ -2191,10 +4373,10 @@ var createRuntime = (options) => {
2191
4373
  },
2192
4374
  release: (name, checkpointId) => timeline(name).release(checkpointId),
2193
4375
  inspect: (name) => {
2194
- const history = timeline(name);
4376
+ const history2 = timeline(name);
2195
4377
  return {
2196
- branches: history.branches(),
2197
- checkpoints: history.checkpoints().map(({ id, branch: branchName, parent, at }) => ({
4378
+ branches: history2.branches(),
4379
+ checkpoints: history2.checkpoints().map(({ id, branch: branchName, parent, at }) => ({
2198
4380
  id,
2199
4381
  branch: branchName,
2200
4382
  parent,
@@ -2209,13 +4391,22 @@ var createRuntime = (options) => {
2209
4391
  } : {},
2210
4392
  routes: {
2211
4393
  ...credentialRoutes(credentials),
2212
- ...options.presets ? presetRoutes(options.presets, runtime) : {},
2213
4394
  ...options.webhooks ? webhookAdminRoutes(options.webhooks) : {},
2214
- ...options.admin?.(runtime) ?? {}
4395
+ ...options.admin?.(runtime) ?? {},
4396
+ // Shell routes win a colliding key. Fault presets are one of them: the admin page
4397
+ // maps `presets`, and a service route must not replace that list with a record.
4398
+ ...options.presets ? presetRoutes(options.presets, runtime) : {},
4399
+ ...stateAdminRoutes({
4400
+ open: stateScope,
4401
+ afterWrite: (namespace) => {
4402
+ checkpoint(namespace, "main");
4403
+ }
4404
+ }),
4405
+ ...adminUiRoutes(options.name, options.adminUi)
2215
4406
  },
2216
4407
  adminKey: options.adminKey
2217
4408
  });
2218
- return runtime;
4409
+ return defineMock(runtime);
2219
4410
  };
2220
4411
  var mutableResponse = (response) => {
2221
4412
  try {
@@ -2279,9 +4470,12 @@ var credentialRoutes = (registry) => ({
2279
4470
  }
2280
4471
  });
2281
4472
  var presetRoutes = (presets, runtime) => ({
2282
- "GET /faults/presets": () => adminJson(200, {
2283
- presets: Object.entries(presets).map(([name, preset]) => ({ name, ...preset }))
2284
- }),
4473
+ "GET /faults/presets": () => {
4474
+ const body = {
4475
+ presets: Object.entries(presets).map(([name, preset]) => ({ name, ...preset }))
4476
+ };
4477
+ return adminJson(200, body);
4478
+ },
2285
4479
  "POST /faults/presets/:name": ({ params, body, namespace }) => {
2286
4480
  const name = params.name;
2287
4481
  if (!presets[name])
@@ -2292,7 +4486,7 @@ var presetRoutes = (presets, runtime) => ({
2292
4486
  });
2293
4487
 
2294
4488
  // src/generated/openapi.ts
2295
- var document = JSON.parse(`{"openapi":"3.1.0","info":{"title":"EasyPost Trackers API (Mockingbird subset)","description":"Stateful mock subset of the EasyPost v2 API: create (or re-use) a tracker for a tracking\\ncode, retrieve it, and list trackers. Trimmed from EasyPost's published reference to what\\nour genomics tracking lookup calls, plus the two reads that make parity walks meaningful.\\n","version":"2","x-mockingbird-upstream":{"note":"Shapes from https://docs.easypost.com/docs/trackers; request/response fields our consumer reads come from packages/lib/src/shipment-tracking-status/easypost-client.ts."}},"servers":[{"url":"https://api.easypost.com"}],"security":[{"basicAuth":[]}],"paths":{"/v2/trackers":{"post":{"operationId":"CreateTracker","description":"Creates a tracker, or answers the existing one when the same tracking code (and carrier) was already tracked. EasyPost's test codes (EZ1000000001 \u2026 EZ7000000007) answer their documented fixed statuses.","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/CreateTrackerBody"}},"application/json":{"schema":{"$ref":"#/components/schemas/CreateTrackerBody"}}}},"responses":{"201":{"description":"The tracker (new or re-used)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Tracker"}}}},"401":{"description":"Missing or unknown API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Missing tracking code or invalid carrier","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"get":{"operationId":"ListTrackers","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"parameters":[{"name":"tracking_code","in":"query","required":false,"schema":{"type":"string","maxLength":40}},{"name":"carrier","in":"query","required":false,"schema":{"type":"string","enum":["USPS","UPS","FedEx","DHLExpress"]}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100}}],"responses":{"200":{"description":"Newest trackers first","content":{"application/json":{"schema":{"type":"object","required":["trackers","has_more"],"properties":{"trackers":{"type":"array","items":{"$ref":"#/components/schemas/Tracker"}},"has_more":{"type":"boolean"}}}}}},"401":{"description":"Missing or unknown API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Invalid query parameter","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v2/trackers/{id}":{"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","x-mockingbird-resource-ref":{"type":"tracker","missing":"trk_00000000000000000000000000000000"}}}],"get":{"operationId":"RetrieveTracker","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"responses":{"200":{"description":"The tracker","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Tracker"}}}},"401":{"description":"Missing or unknown API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Unknown tracker","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"components":{"securitySchemes":{"basicAuth":{"type":"http","scheme":"basic","description":"The API key as the username, an empty password (\`Basic base64(key:)\`)."}},"schemas":{"CreateTrackerBody":{"type":"object","required":["tracker"],"properties":{"tracker":{"type":"object","required":["tracking_code"],"properties":{"tracking_code":{"type":"string","minLength":1,"maxLength":40,"examples":["EZ1000000001","EZ4000000004","1Z999AA10123456784"]},"carrier":{"type":"string","enum":["USPS","UPS","FedEx","DHL","DHLExpress"]}}}}},"Tracker":{"type":"object","required":["id","object","mode","tracking_code","status","status_detail","carrier","tracking_details","public_url","created_at","updated_at"],"properties":{"id":{"type":"string","x-mockingbird-resource":{"type":"tracker","identity":true},"x-mockingbird-volatile":{"kind":"id"}},"object":{"type":"string","enum":["Tracker"]},"mode":{"type":"string","enum":["test","production"]},"tracking_code":{"type":"string"},"status":{"type":"string","enum":["unknown","pre_transit","in_transit","out_for_delivery","delivered","available_for_pickup","return_to_sender","failure","cancelled","error"]},"status_detail":{"type":"string"},"carrier":{"type":"string"},"signed_by":{"type":["string","null"]},"weight":{"type":["number","null"]},"est_delivery_date":{"type":["string","null"],"x-mockingbird-volatile":{"kind":"timestamp"}},"shipment_id":{"type":["string","null"]},"tracking_details":{"type":"array","items":{"$ref":"#/components/schemas/TrackingDetail"}},"carrier_detail":{"type":["object","null"]},"public_url":{"type":"string","x-mockingbird-volatile":{"kind":"url"}},"fees":{"type":"array","items":{"type":"object"}},"created_at":{"type":"string","x-mockingbird-volatile":{"kind":"timestamp"}},"updated_at":{"type":"string","x-mockingbird-volatile":{"kind":"timestamp"}}}},"TrackingDetail":{"type":"object","required":["object","message","status","status_detail","datetime","source"],"properties":{"object":{"type":"string","enum":["TrackingDetail"]},"message":{"type":"string"},"status":{"type":"string"},"status_detail":{"type":"string"},"datetime":{"type":"string","x-mockingbird-volatile":{"kind":"timestamp"}},"source":{"type":"string"},"tracking_location":{"type":"object"}}},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"errors":{"type":"array","items":{"type":"object"}}}}}}}}}`);
4489
+ var document2 = JSON.parse(`{"openapi":"3.1.0","info":{"title":"EasyPost Trackers API (Mockingbird subset)","description":"Stateful mock subset of the EasyPost v2 API: create (or re-use) a tracker for a tracking\\ncode, retrieve it, and list trackers. Trimmed from EasyPost's published reference to what\\nour genomics tracking lookup calls, plus the two reads that make parity walks meaningful.\\n","version":"2","x-mockingbird-upstream":{"note":"Shapes from https://docs.easypost.com/docs/trackers; request/response fields our consumer reads come from packages/lib/src/shipment-tracking-status/easypost-client.ts."}},"servers":[{"url":"https://api.easypost.com"}],"security":[{"basicAuth":[]}],"paths":{"/v2/trackers":{"post":{"operationId":"CreateTracker","description":"Creates a tracker, or answers the existing one when the same tracking code (and carrier) was already tracked. EasyPost's test codes (EZ1000000001 \u2026 EZ7000000007) answer their documented fixed statuses.","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/CreateTrackerBody"}},"application/json":{"schema":{"$ref":"#/components/schemas/CreateTrackerBody"}}}},"responses":{"201":{"description":"The tracker (new or re-used)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Tracker"}}}},"401":{"description":"Missing or unknown API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Missing tracking code or invalid carrier","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"get":{"operationId":"ListTrackers","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"parameters":[{"name":"tracking_code","in":"query","required":false,"schema":{"type":"string","maxLength":40}},{"name":"carrier","in":"query","required":false,"schema":{"type":"string","enum":["USPS","UPS","FedEx","DHLExpress"]}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100}}],"responses":{"200":{"description":"Newest trackers first","content":{"application/json":{"schema":{"type":"object","required":["trackers","has_more"],"properties":{"trackers":{"type":"array","items":{"$ref":"#/components/schemas/Tracker"}},"has_more":{"type":"boolean"}}}}}},"401":{"description":"Missing or unknown API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Invalid query parameter","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v2/trackers/{id}":{"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","x-mockingbird-resource-ref":{"type":"tracker","missing":"trk_00000000000000000000000000000000"}}}],"get":{"operationId":"RetrieveTracker","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"responses":{"200":{"description":"The tracker","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Tracker"}}}},"401":{"description":"Missing or unknown API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Unknown tracker","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"components":{"securitySchemes":{"basicAuth":{"type":"http","scheme":"basic","description":"The API key as the username, an empty password (\`Basic base64(key:)\`)."}},"schemas":{"CreateTrackerBody":{"type":"object","required":["tracker"],"properties":{"tracker":{"type":"object","required":["tracking_code"],"properties":{"tracking_code":{"type":"string","minLength":1,"maxLength":40,"examples":["EZ1000000001","EZ4000000004","1Z999AA10123456784"]},"carrier":{"type":"string","enum":["USPS","UPS","FedEx","DHL","DHLExpress"]}}}}},"Tracker":{"type":"object","required":["id","object","mode","tracking_code","status","status_detail","carrier","tracking_details","public_url","created_at","updated_at"],"properties":{"id":{"type":"string","x-mockingbird-resource":{"type":"tracker","identity":true},"x-mockingbird-volatile":{"kind":"id"}},"object":{"type":"string","enum":["Tracker"]},"mode":{"type":"string","enum":["test","production"]},"tracking_code":{"type":"string"},"status":{"type":"string","enum":["unknown","pre_transit","in_transit","out_for_delivery","delivered","available_for_pickup","return_to_sender","failure","cancelled","error"]},"status_detail":{"type":"string"},"carrier":{"type":"string"},"signed_by":{"type":["string","null"]},"weight":{"type":["number","null"]},"est_delivery_date":{"type":["string","null"],"x-mockingbird-volatile":{"kind":"timestamp"}},"shipment_id":{"type":["string","null"]},"tracking_details":{"type":"array","items":{"$ref":"#/components/schemas/TrackingDetail"}},"carrier_detail":{"type":["object","null"]},"public_url":{"type":"string","x-mockingbird-volatile":{"kind":"url"}},"fees":{"type":"array","items":{"type":"object"}},"created_at":{"type":"string","x-mockingbird-volatile":{"kind":"timestamp"}},"updated_at":{"type":"string","x-mockingbird-volatile":{"kind":"timestamp"}}}},"TrackingDetail":{"type":"object","required":["object","message","status","status_detail","datetime","source"],"properties":{"object":{"type":"string","enum":["TrackingDetail"]},"message":{"type":"string"},"status":{"type":"string"},"status_detail":{"type":"string"},"datetime":{"type":"string","x-mockingbird-volatile":{"kind":"timestamp"}},"source":{"type":"string"},"tracking_location":{"type":"object"}}},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"errors":{"type":"array","items":{"type":"object"}}}}}}}}}`);
2296
4490
  var operationIds = ["ListTrackers", "CreateTracker", "RetrieveTracker"];
2297
4491
  var supportedOperationIds = ["ListTrackers", "CreateTracker", "RetrieveTracker"];
2298
4492
 
@@ -2413,14 +4607,14 @@ var EASYPOST_PRESETS = {
2413
4607
  rules: [{ pathPrefix: "/v2/trackers", latencyMs: 5e3 }]
2414
4608
  }
2415
4609
  };
2416
- var json3 = (status, body) => new Response(JSON.stringify(body), { status, headers: { "content-type": "application/json" } });
2417
- var adminError3 = (status, message) => json3(status, { error: { type: "mockingbird_admin", message } });
2418
- var isRecord4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
4610
+ var json5 = (status, body) => new Response(JSON.stringify(body), { status, headers: { "content-type": "application/json" } });
4611
+ var adminError4 = (status, message) => json5(status, { error: { type: "mockingbird_admin", message } });
4612
+ var isRecord6 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
2419
4613
  var adminRoutes = (runtime) => ({
2420
- "GET /trackers": ({ namespace }) => json3(200, { trackers: runtime.instance(namespace).trackers() }),
4614
+ "GET /trackers": ({ namespace }) => json5(200, { trackers: runtime.instance(namespace).trackers() }),
2421
4615
  "POST /trackers/:code/transition": ({ params, body, namespace }) => {
2422
- if (!isRecord4(body) || !isTrackerStatus(body.status)) {
2423
- return adminError3(
4616
+ if (!isRecord6(body) || !isTrackerStatus(body.status)) {
4617
+ return adminError4(
2424
4618
  400,
2425
4619
  'expected {"status": "<unknown|pre_transit|in_transit|out_for_delivery|delivered|available_for_pickup|return_to_sender|failure|cancelled|error>", "status_detail"?, "message"?, "carrier"?}'
2426
4620
  );
@@ -2431,22 +4625,22 @@ var adminRoutes = (runtime) => ({
2431
4625
  { status: body.status, ...text("status_detail"), ...text("message"), ...text("signed_by") },
2432
4626
  typeof body.carrier === "string" ? body.carrier : void 0
2433
4627
  );
2434
- return json3(200, tracker);
4628
+ return json5(200, tracker);
2435
4629
  },
2436
- "GET /settings": ({ namespace }) => json3(200, runtime.instance(namespace).state.current()),
4630
+ "GET /settings": ({ namespace }) => json5(200, runtime.instance(namespace).state.current()),
2437
4631
  "PUT /settings": ({ body, namespace }) => {
2438
- if (!isRecord4(body)) return adminError3(400, "expected a JSON object");
4632
+ if (!isRecord6(body)) return adminError4(400, "expected a JSON object");
2439
4633
  const patch = {};
2440
4634
  if (body.apiKeys !== void 0) {
2441
- if (!Array.isArray(body.apiKeys)) return adminError3(400, "apiKeys: string[]");
4635
+ if (!Array.isArray(body.apiKeys)) return adminError4(400, "apiKeys: string[]");
2442
4636
  patch.apiKeys = body.apiKeys.map(String);
2443
4637
  }
2444
- return json3(200, runtime.instance(namespace).state.update(patch));
4638
+ return json5(200, runtime.instance(namespace).state.update(patch));
2445
4639
  }
2446
4640
  });
2447
4641
  var createRuntime2 = (options = {}) => createRuntime({
2448
4642
  name: EASYPOST_NAMESPACE,
2449
- document,
4643
+ document: document2,
2450
4644
  ...options.sqlite ? { sqlite: options.sqlite } : {},
2451
4645
  ...options.clock ? { clock: options.clock } : {},
2452
4646
  ...options.seed !== void 0 ? { seed: options.seed } : {},
@@ -2530,7 +4724,7 @@ var EasyPostAPI = class {
2530
4724
  }
2531
4725
  });
2532
4726
  this.service = createService({
2533
- document,
4727
+ document: document2,
2534
4728
  handlers,
2535
4729
  sqlite,
2536
4730
  namespace,
@@ -2688,7 +4882,7 @@ var EasyPostAPI = class {
2688
4882
  };
2689
4883
 
2690
4884
  export {
2691
- document,
4885
+ document2 as document,
2692
4886
  operationIds,
2693
4887
  supportedOperationIds,
2694
4888
  TRACKER_STATUSES,
@@ -2702,4 +4896,4 @@ export {
2702
4896
  isTrackerStatus,
2703
4897
  EasyPostAPI
2704
4898
  };
2705
- //# sourceMappingURL=chunk-AW2SKHTR.js.map
4899
+ //# sourceMappingURL=chunk-DLVQBVAS.js.map