lopata 0.8.4 → 0.10.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.
@@ -930,6 +930,39 @@ var ICONS = {
930
930
  }, undefined, false, undefined, this)
931
931
  ]
932
932
  }, undefined, true, undefined, this),
933
+ routes: () => /* @__PURE__ */ u3("svg", {
934
+ width: "16",
935
+ height: "16",
936
+ viewBox: "0 0 16 16",
937
+ fill: "none",
938
+ stroke: "currentColor",
939
+ "stroke-width": "1.5",
940
+ "stroke-linecap": "round",
941
+ "stroke-linejoin": "round",
942
+ children: [
943
+ /* @__PURE__ */ u3("circle", {
944
+ cx: "4",
945
+ cy: "4",
946
+ r: "1.5"
947
+ }, undefined, false, undefined, this),
948
+ /* @__PURE__ */ u3("circle", {
949
+ cx: "12",
950
+ cy: "4",
951
+ r: "1.5"
952
+ }, undefined, false, undefined, this),
953
+ /* @__PURE__ */ u3("circle", {
954
+ cx: "12",
955
+ cy: "12",
956
+ r: "1.5"
957
+ }, undefined, false, undefined, this),
958
+ /* @__PURE__ */ u3("path", {
959
+ d: "M4 5.5v2c0 1.5 1 2.5 2.5 2.5h4"
960
+ }, undefined, false, undefined, this),
961
+ /* @__PURE__ */ u3("path", {
962
+ d: "M4 5.5v5c0 1 .5 1.5 1.5 1.5h5"
963
+ }, undefined, false, undefined, this)
964
+ ]
965
+ }, undefined, true, undefined, this),
933
966
  overview: () => /* @__PURE__ */ u3("svg", {
934
967
  width: "16",
935
968
  height: "16",
@@ -1109,6 +1142,50 @@ function toError(err) {
1109
1142
  return err instanceof Error ? err : new Error(String(err));
1110
1143
  }
1111
1144
 
1145
+ // src/dashboard/components/copy-markdown-button.tsx
1146
+ function CopyMarkdownButton({ getMarkdown, title }) {
1147
+ const [copied, setCopied] = d2(false);
1148
+ const timerRef = A2(null);
1149
+ y2(() => {
1150
+ return () => {
1151
+ if (timerRef.current)
1152
+ clearTimeout(timerRef.current);
1153
+ };
1154
+ }, []);
1155
+ const handleCopy = (e3) => {
1156
+ e3.stopPropagation();
1157
+ navigator.clipboard.writeText(getMarkdown()).then(() => {
1158
+ setCopied(true);
1159
+ if (timerRef.current)
1160
+ clearTimeout(timerRef.current);
1161
+ timerRef.current = setTimeout(() => setCopied(false), 1500);
1162
+ });
1163
+ };
1164
+ return /* @__PURE__ */ u3("button", {
1165
+ onClick: handleCopy,
1166
+ class: "rounded-md px-2 py-1 text-xs font-medium text-text-muted hover:text-text-data hover:bg-panel-hover border border-transparent hover:border-border transition-all",
1167
+ title: title ?? "Copy as Markdown",
1168
+ children: copied ? "Copied!" : "Copy MD"
1169
+ }, undefined, false, undefined, this);
1170
+ }
1171
+ function tableToMarkdown(headers, rows) {
1172
+ const escape = (v3) => String(v3 ?? "").replace(/\|/g, "\\|").replace(/\n/g, " ");
1173
+ const headerRow = `| ${headers.map(escape).join(" | ")} |`;
1174
+ const separator = `| ${headers.map(() => "---").join(" | ")} |`;
1175
+ const dataRows = rows.map((row) => `| ${row.map(escape).join(" | ")} |`);
1176
+ return [headerRow, separator, ...dataRows].join(`
1177
+ `);
1178
+ }
1179
+ function recordsToMarkdown(headers, rows) {
1180
+ return tableToMarkdown(headers, rows.map((row) => headers.map((h3) => row[h3])));
1181
+ }
1182
+ function keyValueToMarkdown(data) {
1183
+ const entries = Object.entries(data);
1184
+ if (entries.length === 0)
1185
+ return "";
1186
+ return tableToMarkdown(["Key", "Value"], entries);
1187
+ }
1188
+
1112
1189
  // src/dashboard/sql-browser/editable-cell.tsx
1113
1190
  function EditableCell({ value, onSave, foreignKey, onNavigateFK, onInspect, alignRight }) {
1114
1191
  const [editing, setEditing] = d2(false);
@@ -2321,6 +2398,9 @@ function TableDataView({ table, execQuery, onOpenInConsole, history: history2, b
2321
2398
  }, undefined, true, undefined, this)
2322
2399
  ]
2323
2400
  }, undefined, true, undefined, this),
2401
+ /* @__PURE__ */ u3(CopyMarkdownButton, {
2402
+ getMarkdown: () => recordsToMarkdown(displayCols, rows)
2403
+ }, undefined, false, undefined, this),
2324
2404
  /* @__PURE__ */ u3("button", {
2325
2405
  onClick: () => setShowInsert(!showInsert),
2326
2406
  class: `rounded-md px-3 py-1.5 text-sm font-medium transition-all ${showInsert ? "bg-panel-active text-text-data" : "bg-ink text-surface hover:opacity-80"}`,
@@ -2984,33 +3064,41 @@ function SqlConsoleTab({ execQuery, generateSql, initialSql, history: history2 }
2984
3064
  function ResultTable({ columns, rows }) {
2985
3065
  return /* @__PURE__ */ u3("div", {
2986
3066
  class: "bg-panel rounded-lg border border-border overflow-x-auto",
2987
- children: /* @__PURE__ */ u3("table", {
2988
- class: "w-full text-sm",
2989
- children: [
2990
- /* @__PURE__ */ u3("thead", {
2991
- children: /* @__PURE__ */ u3("tr", {
2992
- class: "border-b border-border-subtle",
2993
- children: columns.map((col) => /* @__PURE__ */ u3("th", {
2994
- class: "text-left px-4 py-2.5 font-medium text-xs text-text-muted uppercase tracking-wider font-mono",
2995
- children: col
2996
- }, col, false, undefined, this))
2997
- }, undefined, false, undefined, this)
2998
- }, undefined, false, undefined, this),
2999
- /* @__PURE__ */ u3("tbody", {
3000
- children: rows.map((row, i3) => /* @__PURE__ */ u3("tr", {
3001
- class: "group border-b border-border-row last:border-0 hover:bg-panel-hover/50 transition-colors",
3002
- children: columns.map((col) => /* @__PURE__ */ u3("td", {
3003
- class: "px-4 py-2.5 font-mono text-xs",
3004
- children: row[col] === null ? /* @__PURE__ */ u3("span", {
3005
- class: "text-text-dim italic",
3006
- children: "NULL"
3007
- }, undefined, false, undefined, this) : String(row[col])
3008
- }, col, false, undefined, this))
3009
- }, i3, false, undefined, this))
3067
+ children: [
3068
+ /* @__PURE__ */ u3("div", {
3069
+ class: "flex justify-end px-2 pt-2",
3070
+ children: /* @__PURE__ */ u3(CopyMarkdownButton, {
3071
+ getMarkdown: () => recordsToMarkdown(columns, rows)
3010
3072
  }, undefined, false, undefined, this)
3011
- ]
3012
- }, undefined, true, undefined, this)
3013
- }, undefined, false, undefined, this);
3073
+ }, undefined, false, undefined, this),
3074
+ /* @__PURE__ */ u3("table", {
3075
+ class: "w-full text-sm",
3076
+ children: [
3077
+ /* @__PURE__ */ u3("thead", {
3078
+ children: /* @__PURE__ */ u3("tr", {
3079
+ class: "border-b border-border-subtle",
3080
+ children: columns.map((col) => /* @__PURE__ */ u3("th", {
3081
+ class: "text-left px-4 py-2.5 font-medium text-xs text-text-muted uppercase tracking-wider font-mono",
3082
+ children: col
3083
+ }, col, false, undefined, this))
3084
+ }, undefined, false, undefined, this)
3085
+ }, undefined, false, undefined, this),
3086
+ /* @__PURE__ */ u3("tbody", {
3087
+ children: rows.map((row, i3) => /* @__PURE__ */ u3("tr", {
3088
+ class: "group border-b border-border-row last:border-0 hover:bg-panel-hover/50 transition-colors",
3089
+ children: columns.map((col) => /* @__PURE__ */ u3("td", {
3090
+ class: "px-4 py-2.5 font-mono text-xs",
3091
+ children: row[col] === null ? /* @__PURE__ */ u3("span", {
3092
+ class: "text-text-dim italic",
3093
+ children: "NULL"
3094
+ }, undefined, false, undefined, this) : String(row[col])
3095
+ }, col, false, undefined, this))
3096
+ }, i3, false, undefined, this))
3097
+ }, undefined, false, undefined, this)
3098
+ ]
3099
+ }, undefined, true, undefined, this)
3100
+ ]
3101
+ }, undefined, true, undefined, this);
3014
3102
  }
3015
3103
 
3016
3104
  // src/dashboard/sql-browser/sql-browser.tsx
@@ -3162,25 +3250,35 @@ function KeyValueTable({ data }) {
3162
3250
  children: "No entries"
3163
3251
  }, undefined, false, undefined, this);
3164
3252
  }
3165
- return /* @__PURE__ */ u3("table", {
3166
- class: "w-full text-sm",
3167
- children: /* @__PURE__ */ u3("tbody", {
3168
- children: entries.map(([key, value]) => /* @__PURE__ */ u3("tr", {
3169
- class: "border-b border-border-subtle last:border-0 hover:bg-panel-hover/50 transition-colors",
3170
- children: [
3171
- /* @__PURE__ */ u3("td", {
3172
- class: "px-4 py-2 font-medium text-text-secondary whitespace-nowrap align-top font-mono",
3173
- style: "width: 1%;",
3174
- children: key
3175
- }, undefined, false, undefined, this),
3176
- /* @__PURE__ */ u3("td", {
3177
- class: "px-4 py-2 text-ink break-all font-mono",
3178
- children: value
3179
- }, undefined, false, undefined, this)
3180
- ]
3181
- }, key, true, undefined, this))
3182
- }, undefined, false, undefined, this)
3183
- }, undefined, false, undefined, this);
3253
+ return /* @__PURE__ */ u3("div", {
3254
+ children: [
3255
+ /* @__PURE__ */ u3("div", {
3256
+ class: "flex justify-end px-2 pt-1",
3257
+ children: /* @__PURE__ */ u3(CopyMarkdownButton, {
3258
+ getMarkdown: () => keyValueToMarkdown(data)
3259
+ }, undefined, false, undefined, this)
3260
+ }, undefined, false, undefined, this),
3261
+ /* @__PURE__ */ u3("table", {
3262
+ class: "w-full text-sm",
3263
+ children: /* @__PURE__ */ u3("tbody", {
3264
+ children: entries.map(([key, value]) => /* @__PURE__ */ u3("tr", {
3265
+ class: "border-b border-border-subtle last:border-0 hover:bg-panel-hover/50 transition-colors",
3266
+ children: [
3267
+ /* @__PURE__ */ u3("td", {
3268
+ class: "px-4 py-2 font-medium text-text-secondary whitespace-nowrap align-top font-mono",
3269
+ style: "width: 1%;",
3270
+ children: key
3271
+ }, undefined, false, undefined, this),
3272
+ /* @__PURE__ */ u3("td", {
3273
+ class: "px-4 py-2 text-ink break-all font-mono",
3274
+ children: value
3275
+ }, undefined, false, undefined, this)
3276
+ ]
3277
+ }, key, true, undefined, this))
3278
+ }, undefined, false, undefined, this)
3279
+ }, undefined, false, undefined, this)
3280
+ ]
3281
+ }, undefined, true, undefined, this);
3184
3282
  }
3185
3283
  // src/dashboard/components/page-header.tsx
3186
3284
  function PageHeader({ title, subtitle, actions }) {
@@ -3308,30 +3406,38 @@ function StatusBadge({ status, colorMap }) {
3308
3406
  function Table({ headers, rows }) {
3309
3407
  return /* @__PURE__ */ u3("div", {
3310
3408
  class: "bg-panel rounded-lg border border-border overflow-x-auto",
3311
- children: /* @__PURE__ */ u3("table", {
3312
- class: "w-full text-sm",
3313
- children: [
3314
- /* @__PURE__ */ u3("thead", {
3315
- children: /* @__PURE__ */ u3("tr", {
3316
- class: "border-b border-border-subtle",
3317
- children: headers.map((h3) => /* @__PURE__ */ u3("th", {
3318
- class: "text-left px-4 py-3 font-mono font-medium text-xs text-text-muted uppercase tracking-wider",
3319
- children: h3
3320
- }, h3, false, undefined, this))
3321
- }, undefined, false, undefined, this)
3322
- }, undefined, false, undefined, this),
3323
- /* @__PURE__ */ u3("tbody", {
3324
- children: rows.map((row, i3) => /* @__PURE__ */ u3("tr", {
3325
- class: "group border-b border-border-row last:border-0 hover:bg-panel-hover/50 transition-colors",
3326
- children: row.map((cell, j3) => /* @__PURE__ */ u3("td", {
3327
- class: "px-4 py-3",
3328
- children: cell
3329
- }, j3, false, undefined, this))
3330
- }, i3, false, undefined, this))
3409
+ children: [
3410
+ /* @__PURE__ */ u3("div", {
3411
+ class: "flex justify-end px-2 pt-2",
3412
+ children: /* @__PURE__ */ u3(CopyMarkdownButton, {
3413
+ getMarkdown: () => tableToMarkdown(headers, rows)
3331
3414
  }, undefined, false, undefined, this)
3332
- ]
3333
- }, undefined, true, undefined, this)
3334
- }, undefined, false, undefined, this);
3415
+ }, undefined, false, undefined, this),
3416
+ /* @__PURE__ */ u3("table", {
3417
+ class: "w-full text-sm",
3418
+ children: [
3419
+ /* @__PURE__ */ u3("thead", {
3420
+ children: /* @__PURE__ */ u3("tr", {
3421
+ class: "border-b border-border-subtle",
3422
+ children: headers.map((h3) => /* @__PURE__ */ u3("th", {
3423
+ class: "text-left px-4 py-3 font-mono font-medium text-xs text-text-muted uppercase tracking-wider",
3424
+ children: h3
3425
+ }, h3, false, undefined, this))
3426
+ }, undefined, false, undefined, this)
3427
+ }, undefined, false, undefined, this),
3428
+ /* @__PURE__ */ u3("tbody", {
3429
+ children: rows.map((row, i3) => /* @__PURE__ */ u3("tr", {
3430
+ class: "group border-b border-border-row last:border-0 hover:bg-panel-hover/50 transition-colors",
3431
+ children: row.map((cell, j3) => /* @__PURE__ */ u3("td", {
3432
+ class: "px-4 py-3",
3433
+ children: cell
3434
+ }, j3, false, undefined, this))
3435
+ }, i3, false, undefined, this))
3436
+ }, undefined, false, undefined, this)
3437
+ ]
3438
+ }, undefined, true, undefined, this)
3439
+ ]
3440
+ }, undefined, true, undefined, this);
3335
3441
  }
3336
3442
  // src/dashboard/components/table-link.tsx
3337
3443
  function TableLink({ href, children, mono }) {
@@ -5642,11 +5748,25 @@ function ErrorList() {
5642
5748
  /* @__PURE__ */ u3(RefreshButton, {
5643
5749
  onClick: () => loadErrors()
5644
5750
  }, undefined, false, undefined, this),
5645
- errors.length > 0 && /* @__PURE__ */ u3("button", {
5646
- onClick: handleClear,
5647
- class: "rounded-md px-3 py-1.5 text-sm font-medium bg-panel border border-border text-text-secondary btn-danger transition-all",
5648
- children: "Clear all"
5649
- }, undefined, false, undefined, this)
5751
+ errors.length > 0 && /* @__PURE__ */ u3(k, {
5752
+ children: [
5753
+ /* @__PURE__ */ u3(CopyMarkdownButton, {
5754
+ getMarkdown: () => tableToMarkdown(["Source", "Error", "Message", "Context", "Worker", "Time"], errors.map((err) => [
5755
+ err.source ?? "-",
5756
+ err.errorName,
5757
+ err.errorMessage,
5758
+ err.requestMethod && err.requestUrl ? `${err.requestMethod} ${err.requestUrl}` : "-",
5759
+ err.workerName ?? "-",
5760
+ formatTimestamp2(err.timestamp)
5761
+ ]))
5762
+ }, undefined, false, undefined, this),
5763
+ /* @__PURE__ */ u3("button", {
5764
+ onClick: handleClear,
5765
+ class: "rounded-md px-3 py-1.5 text-sm font-medium bg-panel border border-border text-text-secondary btn-danger transition-all",
5766
+ children: "Clear all"
5767
+ }, undefined, false, undefined, this)
5768
+ ]
5769
+ }, undefined, true, undefined, this)
5650
5770
  ]
5651
5771
  }, undefined, true, undefined, this)
5652
5772
  ]
@@ -5856,11 +5976,20 @@ function ErrorDetailPage({ errorId }) {
5856
5976
  }, undefined, true, undefined, this)
5857
5977
  ]
5858
5978
  }, undefined, true, undefined, this),
5859
- /* @__PURE__ */ u3("button", {
5860
- onClick: handleDelete,
5861
- class: "rounded-md px-3 py-1.5 text-sm font-medium bg-panel border border-border text-text-secondary btn-danger transition-all",
5862
- children: "Delete"
5863
- }, undefined, false, undefined, this)
5979
+ /* @__PURE__ */ u3("div", {
5980
+ class: "flex items-center gap-2",
5981
+ children: [
5982
+ /* @__PURE__ */ u3(CopyMarkdownButton, {
5983
+ getMarkdown: () => errorToMarkdown(detail),
5984
+ title: "Copy error as Markdown for LLM"
5985
+ }, undefined, false, undefined, this),
5986
+ /* @__PURE__ */ u3("button", {
5987
+ onClick: handleDelete,
5988
+ class: "rounded-md px-3 py-1.5 text-sm font-medium bg-panel border border-border text-text-secondary btn-danger transition-all",
5989
+ children: "Delete"
5990
+ }, undefined, false, undefined, this)
5991
+ ]
5992
+ }, undefined, true, undefined, this)
5864
5993
  ]
5865
5994
  }, undefined, true, undefined, this),
5866
5995
  /* @__PURE__ */ u3("div", {
@@ -6263,6 +6392,60 @@ function truncateUrl(url) {
6263
6392
  return url.length > 50 ? url.slice(0, 50) + "..." : url;
6264
6393
  }
6265
6394
  }
6395
+ function errorToMarkdown(detail) {
6396
+ const { data } = detail;
6397
+ const lines = [];
6398
+ lines.push(`## ${data.error.name}: ${data.error.message}`);
6399
+ if (detail.source)
6400
+ lines.push(`**Source:** ${detail.source}`);
6401
+ if (data.runtime.workerName)
6402
+ lines.push(`**Worker:** ${data.runtime.workerName}`);
6403
+ lines.push("");
6404
+ if (data.error.stack) {
6405
+ lines.push("### Stack Trace");
6406
+ lines.push("```");
6407
+ lines.push(data.error.stack);
6408
+ lines.push("```");
6409
+ lines.push("");
6410
+ }
6411
+ if (data.error.frames.length > 0) {
6412
+ lines.push("### Source Code");
6413
+ for (const frame of data.error.frames) {
6414
+ lines.push(`#### ${frame.file}:${frame.line}:${frame.column}${frame.function ? ` in ${frame.function}` : ""}`);
6415
+ if (frame.source && frame.source.length > 0) {
6416
+ const startLine = frame.line - (frame.sourceLine ?? 0);
6417
+ lines.push("```");
6418
+ frame.source.forEach((line, i3) => {
6419
+ const lineNum = startLine + i3;
6420
+ const marker = i3 === frame.sourceLine ? ">" : " ";
6421
+ lines.push(`${marker} ${lineNum} | ${line}`);
6422
+ });
6423
+ lines.push("```");
6424
+ }
6425
+ lines.push("");
6426
+ }
6427
+ }
6428
+ if (data.request.method && data.request.url) {
6429
+ lines.push("### Request");
6430
+ lines.push(`${data.request.method} ${data.request.url}`);
6431
+ const headers = Object.entries(data.request.headers);
6432
+ if (headers.length > 0) {
6433
+ lines.push("");
6434
+ lines.push(keyValueToMarkdown(data.request.headers));
6435
+ }
6436
+ lines.push("");
6437
+ }
6438
+ if (data.bindings.length > 0) {
6439
+ lines.push("### Bindings");
6440
+ lines.push(tableToMarkdown(["Name", "Type"], data.bindings.map((b) => [b.name, b.type])));
6441
+ lines.push("");
6442
+ }
6443
+ lines.push("### Runtime");
6444
+ lines.push(`- **Bun:** ${data.runtime.bunVersion}`);
6445
+ lines.push(`- **Platform:** ${data.runtime.platform} / ${data.runtime.arch}`);
6446
+ return lines.join(`
6447
+ `);
6448
+ }
6266
6449
 
6267
6450
  // src/dashboard/views/generations.tsx
6268
6451
  var STATE_COLORS = {
@@ -7825,6 +8008,44 @@ function R2ObjectList({ bucket }) {
7825
8008
  }, undefined, true, undefined, this);
7826
8009
  }
7827
8010
 
8011
+ // src/dashboard/views/routes.tsx
8012
+ var TYPE_COLORS = {
8013
+ route: "bg-emerald-500/15 text-emerald-500",
8014
+ host: "bg-blue-500/15 text-blue-500",
8015
+ fallback: "bg-panel-active text-text-data"
8016
+ };
8017
+ function RoutesView() {
8018
+ const { data: routes } = useQuery("routes.list");
8019
+ return /* @__PURE__ */ u3("div", {
8020
+ class: "p-4 sm:p-8",
8021
+ children: [
8022
+ /* @__PURE__ */ u3(PageHeader, {
8023
+ title: "Routes",
8024
+ subtitle: `${routes?.length ?? 0} route(s)`
8025
+ }, undefined, false, undefined, this),
8026
+ !routes?.length ? /* @__PURE__ */ u3(EmptyState, {
8027
+ message: "No routes configured"
8028
+ }, undefined, false, undefined, this) : /* @__PURE__ */ u3(Table, {
8029
+ headers: ["Pattern", "Worker", "Type"],
8030
+ rows: routes.map((r3) => [
8031
+ /* @__PURE__ */ u3("span", {
8032
+ class: "font-mono text-xs font-medium",
8033
+ children: r3.pattern
8034
+ }, undefined, false, undefined, this),
8035
+ /* @__PURE__ */ u3("span", {
8036
+ class: "text-text-secondary",
8037
+ children: r3.workerName
8038
+ }, undefined, false, undefined, this),
8039
+ /* @__PURE__ */ u3(StatusBadge, {
8040
+ status: r3.isFallback ? "fallback" : r3.type === "host" ? "host" : "route",
8041
+ colorMap: TYPE_COLORS
8042
+ }, undefined, false, undefined, this)
8043
+ ])
8044
+ }, undefined, false, undefined, this)
8045
+ ]
8046
+ }, undefined, true, undefined, this);
8047
+ }
8048
+
7828
8049
  // src/dashboard/views/scheduled.tsx
7829
8050
  function ScheduledView({ route }) {
7830
8051
  return /* @__PURE__ */ u3(ScheduledList, {}, undefined, false, undefined, this);
@@ -8958,7 +9179,7 @@ function DurationBar({ durationMs, maxDuration }) {
8958
9179
  }
8959
9180
 
8960
9181
  // src/dashboard/views/workers.tsx
8961
- var TYPE_COLORS = {
9182
+ var TYPE_COLORS2 = {
8962
9183
  kv: "bg-emerald-500/15 text-emerald-500",
8963
9184
  r2: "bg-blue-500/15 text-blue-400",
8964
9185
  d1: "bg-violet-500/15 text-violet-400",
@@ -9014,7 +9235,7 @@ function WorkersView() {
9014
9235
  rows: w3.bindings.map((b) => [
9015
9236
  /* @__PURE__ */ u3(StatusBadge, {
9016
9237
  status: b.type,
9017
- colorMap: TYPE_COLORS
9238
+ colorMap: TYPE_COLORS2
9018
9239
  }, undefined, false, undefined, this),
9019
9240
  /* @__PURE__ */ u3("span", {
9020
9241
  class: "font-mono text-xs font-medium",
@@ -9744,6 +9965,7 @@ var NAV_GROUPS = [
9744
9965
  label: "Compute",
9745
9966
  items: [
9746
9967
  { path: "/workers", label: "Workers", icon: "workers" },
9968
+ { path: "/routes", label: "Routes", icon: "routes" },
9747
9969
  { path: "/do", label: "Durable Objects", icon: "do" },
9748
9970
  { path: "/containers", label: "Containers", icon: "containers" },
9749
9971
  { path: "/workflows", label: "Workflows", icon: "workflows" },
@@ -9880,6 +10102,8 @@ function App() {
9880
10102
  }, undefined, false, undefined, this);
9881
10103
  if (route.startsWith("/workers"))
9882
10104
  return /* @__PURE__ */ u3(WorkersView, {}, undefined, false, undefined, this);
10105
+ if (route.startsWith("/routes"))
10106
+ return /* @__PURE__ */ u3(RoutesView, {}, undefined, false, undefined, this);
9883
10107
  if (route.startsWith("/generations"))
9884
10108
  return /* @__PURE__ */ u3(GenerationsView, {}, undefined, false, undefined, this);
9885
10109
  if (route.startsWith("/kv"))
@@ -1756,6 +1756,14 @@
1756
1756
  padding-top: calc(var(--spacing) * .5);
1757
1757
  }
1758
1758
 
1759
+ .pt-1 {
1760
+ padding-top: calc(var(--spacing) * 1);
1761
+ }
1762
+
1763
+ .pt-2 {
1764
+ padding-top: calc(var(--spacing) * 2);
1765
+ }
1766
+
1759
1767
  .pt-3 {
1760
1768
  padding-top: calc(var(--spacing) * 3);
1761
1769
  }
@@ -8,7 +8,7 @@
8
8
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
9
9
  <link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet" />
10
10
 
11
- <link rel="stylesheet" crossorigin href="/__dashboard/assets/chunk-csyd2tq2.css"><script type="module" crossorigin src="/__dashboard/assets/chunk-yxzrcvyh.js"></script></head>
11
+ <link rel="stylesheet" crossorigin href="/__dashboard/assets/chunk-jzyhpjad.css"><script type="module" crossorigin src="/__dashboard/assets/chunk-hnsny9g7.js"></script></head>
12
12
  <body class="h-full bg-surface text-ink" style="font-family: system-ui, -apple-system, sans-serif;">
13
13
  <script>
14
14
  // Apply saved theme before first paint to prevent flash
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lopata",
3
- "version": "0.8.4",
3
+ "version": "0.10.0",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -13,6 +13,7 @@ import { handlers as kv } from './handlers/kv'
13
13
  import { handlers as overview } from './handlers/overview'
14
14
  import { handlers as queue } from './handlers/queue'
15
15
  import { handlers as r2 } from './handlers/r2'
16
+ import { handlers as routes } from './handlers/routes'
16
17
  import { handlers as scheduled } from './handlers/scheduled'
17
18
  import { handlers as traces } from './handlers/traces'
18
19
  import { handlers as warnings } from './handlers/warnings'
@@ -40,6 +41,7 @@ const allHandlers = {
40
41
  ...ai,
41
42
  ...analyticsEngine,
42
43
  ...warnings,
44
+ ...routes,
43
45
  }
44
46
 
45
47
  export type Procedures = {
@@ -0,0 +1,36 @@
1
+ import { extractPathPattern } from '../../route-matcher'
2
+ import type { HandlerContext, RouteInfo } from '../types'
3
+
4
+ export const handlers = {
5
+ 'routes.list'(_input: {}, ctx: HandlerContext): RouteInfo[] {
6
+ const routes: RouteInfo[] = []
7
+
8
+ // Show host-based routes
9
+ for (const hr of ctx.hostRoutes) {
10
+ routes.push({ pattern: hr.pattern, workerName: hr.workerName, isFallback: false, type: 'host' })
11
+ }
12
+
13
+ if (ctx.routeDispatcher) {
14
+ for (const r of ctx.routeDispatcher.getRegisteredRoutes()) {
15
+ routes.push({ pattern: r.pattern, workerName: r.workerName, isFallback: false })
16
+ }
17
+ }
18
+
19
+ // In single-worker mode, show routes from config
20
+ if (!ctx.routeDispatcher && ctx.config?.routes) {
21
+ const workerName = ctx.config.name || 'main'
22
+ for (const route of ctx.config.routes) {
23
+ if (typeof route === 'object' && route.custom_domain) continue
24
+ routes.push({ pattern: extractPathPattern(route), workerName, isFallback: false })
25
+ }
26
+ }
27
+
28
+ // Add main/fallback worker entry
29
+ const mainName = ctx.registry
30
+ ? Array.from(ctx.registry.listManagers().keys())[0] ?? 'main'
31
+ : ctx.config?.name || 'main'
32
+ routes.push({ pattern: '/*', workerName: mainName, isFallback: true })
33
+
34
+ return routes
35
+ },
36
+ }
package/src/api/index.ts CHANGED
@@ -1,13 +1,14 @@
1
1
  import type { WranglerConfig } from '../config'
2
2
  import type { GenerationManager } from '../generation-manager'
3
3
  import type { LopataConfig } from '../lopata-config'
4
+ import type { RouteDispatcher } from '../route-matcher'
4
5
  import type { WorkerRegistry } from '../worker-registry'
5
6
  import { handlePreflight, withCors } from './cors'
6
7
  import { dispatch } from './dispatch'
7
8
  import { handleR2Download, handleR2Upload } from './r2'
8
- import type { HandlerContext } from './types'
9
+ import type { HandlerContext, HostRouteInfo } from './types'
9
10
 
10
- const ctx: HandlerContext = { config: null, manager: null, registry: null, lopataConfig: null }
11
+ const ctx: HandlerContext = { config: null, manager: null, registry: null, lopataConfig: null, routeDispatcher: null, hostRoutes: [] }
11
12
 
12
13
  export function setDashboardConfig(config: WranglerConfig): void {
13
14
  ctx.config = config
@@ -25,6 +26,14 @@ export function setLopataConfig(config: LopataConfig): void {
25
26
  ctx.lopataConfig = config
26
27
  }
27
28
 
29
+ export function setRouteDispatcher(dispatcher: RouteDispatcher): void {
30
+ ctx.routeDispatcher = dispatcher
31
+ }
32
+
33
+ export function setHostRoutes(routes: HostRouteInfo[]): void {
34
+ ctx.hostRoutes = routes
35
+ }
36
+
28
37
  export function handleApiRequest(request: Request): Response | Promise<Response> {
29
38
  const url = new URL(request.url)
30
39
 
package/src/api/types.ts CHANGED
@@ -379,13 +379,28 @@ export type { SpanData, SpanEventData, TraceDetail, TraceEvent, TraceSummary } f
379
379
  import type { WranglerConfig } from '../config'
380
380
  import type { GenerationManager } from '../generation-manager'
381
381
  import type { LopataConfig } from '../lopata-config'
382
+ import type { RouteDispatcher } from '../route-matcher'
382
383
  import type { WorkerRegistry } from '../worker-registry'
383
384
 
385
+ export interface HostRouteInfo {
386
+ pattern: string
387
+ workerName: string
388
+ }
389
+
384
390
  export interface HandlerContext {
385
391
  config: WranglerConfig | null
386
392
  manager: GenerationManager | null
387
393
  registry: WorkerRegistry | null
388
394
  lopataConfig: LopataConfig | null
395
+ routeDispatcher: RouteDispatcher | null
396
+ hostRoutes: HostRouteInfo[]
397
+ }
398
+
399
+ export interface RouteInfo {
400
+ pattern: string
401
+ workerName: string
402
+ isFallback: boolean
403
+ type?: 'path' | 'host'
389
404
  }
390
405
 
391
406
  /** Collect configs from all workers (registry) or fall back to single config. */