datool 0.0.6 → 0.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/client-dist/assets/index-BJjc4NKE.css +1 -0
- package/client-dist/assets/{index-CU3ksvkv.js → index-C6-ONVzY.js} +43 -43
- package/client-dist/index.html +2 -2
- package/package.json +1 -1
- package/src/client/components/data-table.tsx +130 -117
- package/src/node/server.ts +96 -56
- package/client-dist/assets/index-Rn9jSaAz.css +0 -1
package/client-dist/index.html
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>datool</title>
|
|
7
|
-
<script type="module" crossorigin src="/assets/index-
|
|
8
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
7
|
+
<script type="module" crossorigin src="/assets/index-C6-ONVzY.js"></script>
|
|
8
|
+
<link rel="stylesheet" crossorigin href="/assets/index-BJjc4NKE.css">
|
|
9
9
|
</head>
|
|
10
10
|
<body>
|
|
11
11
|
<div id="root"></div>
|
package/package.json
CHANGED
|
@@ -8,7 +8,6 @@ import {
|
|
|
8
8
|
getSortedRowModel,
|
|
9
9
|
useReactTable,
|
|
10
10
|
type AggregationFnOption,
|
|
11
|
-
type Column,
|
|
12
11
|
type ColumnDef,
|
|
13
12
|
type ColumnFiltersState,
|
|
14
13
|
type ExpandedState,
|
|
@@ -604,14 +603,6 @@ function formatDuration(durationMs: number) {
|
|
|
604
603
|
return parts.join(" ")
|
|
605
604
|
}
|
|
606
605
|
|
|
607
|
-
function getColumnHeaderLabel<TData extends DataTableRow>(
|
|
608
|
-
column: Column<TData, unknown>
|
|
609
|
-
) {
|
|
610
|
-
return typeof column.columnDef.header === "string"
|
|
611
|
-
? column.columnDef.header
|
|
612
|
-
: formatHeaderLabel(column.id)
|
|
613
|
-
}
|
|
614
|
-
|
|
615
606
|
function resolveGroupedPadding(
|
|
616
607
|
padding: React.CSSProperties["paddingLeft"],
|
|
617
608
|
depth: number
|
|
@@ -1294,21 +1285,6 @@ function RowActionButtonGroup<TData extends DataTableRow>({
|
|
|
1294
1285
|
)
|
|
1295
1286
|
}
|
|
1296
1287
|
|
|
1297
|
-
function GroupSummaryBadge({
|
|
1298
|
-
label,
|
|
1299
|
-
value,
|
|
1300
|
-
}: {
|
|
1301
|
-
label: string
|
|
1302
|
-
value: string
|
|
1303
|
-
}) {
|
|
1304
|
-
return (
|
|
1305
|
-
<span className="inline-flex items-center gap-1 rounded-full border border-border/70 bg-background/80 px-2 py-0.5 text-[11px] font-medium text-muted-foreground">
|
|
1306
|
-
<span className="text-foreground">{value}</span>
|
|
1307
|
-
<span>{label}</span>
|
|
1308
|
-
</span>
|
|
1309
|
-
)
|
|
1310
|
-
}
|
|
1311
|
-
|
|
1312
1288
|
function DataTableView<TData extends DataTableRow>({
|
|
1313
1289
|
autoScrollToBottom = false,
|
|
1314
1290
|
autoScrollToBottomThreshold = 96,
|
|
@@ -1359,6 +1335,9 @@ function DataTableView<TData extends DataTableRow>({
|
|
|
1359
1335
|
const [grouping, setGrouping] = React.useState<GroupingState>(
|
|
1360
1336
|
() => persistedState.grouping ?? []
|
|
1361
1337
|
)
|
|
1338
|
+
const [expanded, setExpanded] = React.useState<ExpandedState>(() =>
|
|
1339
|
+
(controlledGrouping ?? persistedState.grouping ?? []).length > 0 ? true : {}
|
|
1340
|
+
)
|
|
1362
1341
|
const [rowSelection, setRowSelection] = React.useState<RowSelectionState>({})
|
|
1363
1342
|
const [rowActionStatuses, setRowActionStatuses] = React.useState<
|
|
1364
1343
|
Record<string, RowActionStatus>
|
|
@@ -1386,6 +1365,10 @@ function DataTableView<TData extends DataTableRow>({
|
|
|
1386
1365
|
const resolvedColumnVisibility =
|
|
1387
1366
|
controlledColumnVisibility ?? columnVisibility
|
|
1388
1367
|
const resolvedGrouping = controlledGrouping ?? grouping
|
|
1368
|
+
const groupingKey = React.useMemo(
|
|
1369
|
+
() => resolvedGrouping.join("\u001f"),
|
|
1370
|
+
[resolvedGrouping]
|
|
1371
|
+
)
|
|
1389
1372
|
const hasSelectionActions = rowActions
|
|
1390
1373
|
? hasSelectionScopedAction(rowActions)
|
|
1391
1374
|
: false
|
|
@@ -1526,12 +1509,9 @@ function DataTableView<TData extends DataTableRow>({
|
|
|
1526
1509
|
},
|
|
1527
1510
|
[isGroupingControlled, onGroupingChange, resolvedGrouping]
|
|
1528
1511
|
)
|
|
1529
|
-
const expandedState = React.useMemo<ExpandedState>(
|
|
1530
|
-
() => (resolvedGrouping.length > 0 ? true : {}),
|
|
1531
|
-
[resolvedGrouping.length]
|
|
1532
|
-
)
|
|
1533
1512
|
|
|
1534
1513
|
const table = useReactTable({
|
|
1514
|
+
autoResetExpanded: false,
|
|
1535
1515
|
columnResizeMode: "onChange",
|
|
1536
1516
|
columns: tableColumns,
|
|
1537
1517
|
data,
|
|
@@ -1550,6 +1530,7 @@ function DataTableView<TData extends DataTableRow>({
|
|
|
1550
1530
|
onColumnFiltersChange: handleColumnFiltersChange,
|
|
1551
1531
|
onColumnSizingChange: handleColumnSizingChange,
|
|
1552
1532
|
onColumnVisibilityChange: handleColumnVisibilityChange,
|
|
1533
|
+
onExpandedChange: setExpanded,
|
|
1553
1534
|
onGroupingChange: handleGroupingChange,
|
|
1554
1535
|
onRowSelectionChange: setRowSelection,
|
|
1555
1536
|
onSortingChange: setSorting,
|
|
@@ -1557,7 +1538,7 @@ function DataTableView<TData extends DataTableRow>({
|
|
|
1557
1538
|
columnFilters: resolvedColumnFilters,
|
|
1558
1539
|
columnSizing,
|
|
1559
1540
|
columnVisibility: resolvedColumnVisibility,
|
|
1560
|
-
expanded
|
|
1541
|
+
expanded,
|
|
1561
1542
|
globalFilter: deferredSearch.trim(),
|
|
1562
1543
|
grouping: resolvedGrouping,
|
|
1563
1544
|
rowSelection,
|
|
@@ -1618,6 +1599,12 @@ function DataTableView<TData extends DataTableRow>({
|
|
|
1618
1599
|
if (!isGroupingControlled) {
|
|
1619
1600
|
setGrouping(nextState.grouping ?? [])
|
|
1620
1601
|
}
|
|
1602
|
+
setExpanded(
|
|
1603
|
+
(isGroupingControlled ? controlledGrouping : nextState.grouping ?? [])
|
|
1604
|
+
.length > 0
|
|
1605
|
+
? true
|
|
1606
|
+
: {}
|
|
1607
|
+
)
|
|
1621
1608
|
setSearchDraft(nextState.globalFilter ?? "")
|
|
1622
1609
|
setRowSelection({})
|
|
1623
1610
|
dragSelectionRef.current = null
|
|
@@ -1630,6 +1617,7 @@ function DataTableView<TData extends DataTableRow>({
|
|
|
1630
1617
|
rowActionStatusTimersRef.current = {}
|
|
1631
1618
|
setRowActionStatuses({})
|
|
1632
1619
|
}, [
|
|
1620
|
+
controlledGrouping,
|
|
1633
1621
|
id,
|
|
1634
1622
|
isColumnFiltersControlled,
|
|
1635
1623
|
isColumnVisibilityControlled,
|
|
@@ -1687,6 +1675,12 @@ function DataTableView<TData extends DataTableRow>({
|
|
|
1687
1675
|
if (!isGroupingControlled) {
|
|
1688
1676
|
setGrouping(nextState.grouping ?? [])
|
|
1689
1677
|
}
|
|
1678
|
+
setExpanded(
|
|
1679
|
+
(isGroupingControlled ? controlledGrouping : nextState.grouping ?? [])
|
|
1680
|
+
.length > 0
|
|
1681
|
+
? true
|
|
1682
|
+
: {}
|
|
1683
|
+
)
|
|
1690
1684
|
setSearchDraft(nextState.globalFilter ?? "")
|
|
1691
1685
|
setRowSelection({})
|
|
1692
1686
|
dragSelectionRef.current = null
|
|
@@ -1698,7 +1692,13 @@ function DataTableView<TData extends DataTableRow>({
|
|
|
1698
1692
|
window.addEventListener("popstate", syncFromUrl)
|
|
1699
1693
|
|
|
1700
1694
|
return () => window.removeEventListener("popstate", syncFromUrl)
|
|
1701
|
-
}, [
|
|
1695
|
+
}, [
|
|
1696
|
+
controlledGrouping,
|
|
1697
|
+
id,
|
|
1698
|
+
isColumnVisibilityControlled,
|
|
1699
|
+
isGroupingControlled,
|
|
1700
|
+
statePersistence,
|
|
1701
|
+
])
|
|
1702
1702
|
|
|
1703
1703
|
React.useEffect(() => {
|
|
1704
1704
|
if (isGlobalFilterControlled || isColumnFiltersControlled) {
|
|
@@ -1729,6 +1729,10 @@ function DataTableView<TData extends DataTableRow>({
|
|
|
1729
1729
|
statePersistence,
|
|
1730
1730
|
])
|
|
1731
1731
|
|
|
1732
|
+
React.useEffect(() => {
|
|
1733
|
+
setExpanded(resolvedGrouping.length > 0 ? true : {})
|
|
1734
|
+
}, [groupingKey, resolvedGrouping.length])
|
|
1735
|
+
|
|
1732
1736
|
React.useEffect(() => {
|
|
1733
1737
|
const container = containerRef.current
|
|
1734
1738
|
|
|
@@ -2200,34 +2204,13 @@ function DataTableView<TData extends DataTableRow>({
|
|
|
2200
2204
|
enumOptions: groupingMeta?.enumOptions,
|
|
2201
2205
|
})
|
|
2202
2206
|
: null
|
|
2203
|
-
const
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
if (meta.kind === "date" && isDateRangeAggregate(value)) {
|
|
2211
|
-
return [
|
|
2212
|
-
{
|
|
2213
|
-
label: `${label} span`,
|
|
2214
|
-
value: formatDuration(value.durationMs),
|
|
2215
|
-
},
|
|
2216
|
-
]
|
|
2217
|
-
}
|
|
2218
|
-
|
|
2219
|
-
if (meta.kind === "number" && typeof value === "number") {
|
|
2220
|
-
return [
|
|
2221
|
-
{
|
|
2222
|
-
label: `${label} sum`,
|
|
2223
|
-
value: formatSummaryNumber(value),
|
|
2224
|
-
},
|
|
2225
|
-
]
|
|
2226
|
-
}
|
|
2227
|
-
|
|
2228
|
-
return []
|
|
2229
|
-
})
|
|
2230
|
-
: []
|
|
2207
|
+
const hasVisibleGroupedCell = groupVisibleCells.some((cell) =>
|
|
2208
|
+
cell.getIsGrouped()
|
|
2209
|
+
)
|
|
2210
|
+
const primaryGroupCellId = groupVisibleCells.find(
|
|
2211
|
+
(cell) =>
|
|
2212
|
+
cell.column.id !== "__select" && cell.column.id !== "__actions"
|
|
2213
|
+
)?.id
|
|
2231
2214
|
const rowContent = (
|
|
2232
2215
|
<tr
|
|
2233
2216
|
aria-selected={isSelected}
|
|
@@ -2274,65 +2257,95 @@ function DataTableView<TData extends DataTableRow>({
|
|
|
2274
2257
|
}}
|
|
2275
2258
|
>
|
|
2276
2259
|
{isGroupRow ? (
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
</
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2260
|
+
groupVisibleCells.map((cell, index, visibleCells) => {
|
|
2261
|
+
const meta = (cell.column.columnDef.meta ??
|
|
2262
|
+
{}) as DataTableColumnMeta
|
|
2263
|
+
const isActionsCell = cell.column.id === "__actions"
|
|
2264
|
+
const isSelectionCell = meta.kind === "selection"
|
|
2265
|
+
const shouldRenderGroupLabel =
|
|
2266
|
+
cell.getIsGrouped() ||
|
|
2267
|
+
(!hasVisibleGroupedCell && cell.id === primaryGroupCellId)
|
|
2268
|
+
const value = cell.getValue()
|
|
2269
|
+
let content: React.ReactNode = null
|
|
2270
|
+
|
|
2271
|
+
if (!isActionsCell && !isSelectionCell) {
|
|
2272
|
+
if (shouldRenderGroupLabel) {
|
|
2273
|
+
content = (
|
|
2274
|
+
<div className="flex min-w-0 items-center gap-0.5">
|
|
2275
|
+
{row.getCanExpand() ? (
|
|
2276
|
+
<button
|
|
2277
|
+
aria-label={
|
|
2278
|
+
row.getIsExpanded()
|
|
2279
|
+
? "Collapse group"
|
|
2280
|
+
: "Expand group"
|
|
2281
|
+
}
|
|
2282
|
+
className="-ml-4"
|
|
2283
|
+
onClick={() => row.toggleExpanded()}
|
|
2284
|
+
type="button"
|
|
2285
|
+
>
|
|
2286
|
+
{row.getIsExpanded() ? (
|
|
2287
|
+
<ChevronDown className="size-3.5" />
|
|
2288
|
+
) : (
|
|
2289
|
+
<ChevronRight className="size-3.5" />
|
|
2290
|
+
)}
|
|
2291
|
+
</button>
|
|
2292
|
+
) : null}
|
|
2293
|
+
<div className="min-w-0 truncate font-medium text-foreground">
|
|
2294
|
+
{groupingColumn ? groupingValue : "Group"}
|
|
2295
|
+
</div>
|
|
2296
|
+
</div>
|
|
2297
|
+
)
|
|
2298
|
+
} else if (meta.kind === "date" && isDateRangeAggregate(value)) {
|
|
2299
|
+
content = (
|
|
2300
|
+
<div className="min-w-0 truncate">
|
|
2301
|
+
<span className="font-medium text-foreground">
|
|
2302
|
+
{formatDuration(value.durationMs)}
|
|
2303
|
+
</span>
|
|
2304
|
+
<span className="ml-2 text-[11px] text-muted-foreground">
|
|
2305
|
+
span
|
|
2306
|
+
</span>
|
|
2307
|
+
</div>
|
|
2308
|
+
)
|
|
2309
|
+
} else if (meta.kind === "number" && typeof value === "number") {
|
|
2310
|
+
content = (
|
|
2311
|
+
<div className="min-w-0 truncate font-medium text-foreground">
|
|
2312
|
+
{formatSummaryNumber(value)}
|
|
2313
|
+
</div>
|
|
2314
|
+
)
|
|
2315
|
+
}
|
|
2316
|
+
}
|
|
2317
|
+
|
|
2318
|
+
return (
|
|
2319
|
+
<td
|
|
2320
|
+
className={cn(
|
|
2321
|
+
"flex shrink-0 items-center border-y border-border/70 px-2 py-2 align-middle text-xs text-muted-foreground",
|
|
2322
|
+
meta.align === "center" && "justify-center text-center",
|
|
2323
|
+
meta.align === "right" && "justify-end text-right",
|
|
2324
|
+
meta.sticky === "left" &&
|
|
2325
|
+
"sticky left-0 z-10 border-r border-r-border"
|
|
2326
|
+
)}
|
|
2327
|
+
key={cell.id}
|
|
2328
|
+
style={{
|
|
2329
|
+
background:
|
|
2330
|
+
"var(--color-table-gap, color-mix(in oklab, var(--color-muted) 84%, transparent))",
|
|
2331
|
+
paddingLeft:
|
|
2332
|
+
index === 0
|
|
2333
|
+
? resolveGroupedPadding(
|
|
2334
|
+
edgeHorizontalPadding,
|
|
2335
|
+
row.depth
|
|
2336
|
+
)
|
|
2337
|
+
: undefined,
|
|
2338
|
+
paddingRight:
|
|
2339
|
+
index === visibleCells.length - 1
|
|
2340
|
+
? edgeHorizontalPadding
|
|
2341
|
+
: undefined,
|
|
2342
|
+
width: cell.column.getSize(),
|
|
2343
|
+
}}
|
|
2344
|
+
>
|
|
2345
|
+
<div className="w-full min-w-0">{content}</div>
|
|
2346
|
+
</td>
|
|
2347
|
+
)
|
|
2348
|
+
})
|
|
2336
2349
|
) : (
|
|
2337
2350
|
row.getVisibleCells().map((cell, index, visibleCells) => {
|
|
2338
2351
|
const meta = (cell.column.columnDef.meta ??
|
package/src/node/server.ts
CHANGED
|
@@ -36,6 +36,8 @@ const SSE_HEADERS = {
|
|
|
36
36
|
"Content-Type": "text/event-stream; charset=utf-8",
|
|
37
37
|
} as const
|
|
38
38
|
|
|
39
|
+
const MAX_PORT_ATTEMPTS = 10
|
|
40
|
+
|
|
39
41
|
function packageRootFromImportMeta() {
|
|
40
42
|
return path.resolve(import.meta.dir, "..", "..")
|
|
41
43
|
}
|
|
@@ -85,6 +87,14 @@ function encodeSseEvent(event: string, data: unknown) {
|
|
|
85
87
|
return `event: ${event}\ndata: ${JSON.stringify(data)}\n\n`
|
|
86
88
|
}
|
|
87
89
|
|
|
90
|
+
function isAddressInUseError(error: unknown) {
|
|
91
|
+
return (
|
|
92
|
+
error instanceof Error &&
|
|
93
|
+
"code" in error &&
|
|
94
|
+
error.code === "EADDRINUSE"
|
|
95
|
+
)
|
|
96
|
+
}
|
|
97
|
+
|
|
88
98
|
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
89
99
|
return Boolean(value) && typeof value === "object"
|
|
90
100
|
}
|
|
@@ -334,78 +344,108 @@ export async function startDatoolServer(
|
|
|
334
344
|
cwd,
|
|
335
345
|
})
|
|
336
346
|
const host = options.host ?? config.server?.host ?? "127.0.0.1"
|
|
337
|
-
const
|
|
347
|
+
const requestedPort = options.port ?? config.server?.port ?? 3210
|
|
338
348
|
const indexHtml = await loadClientIndexHtml(packageRoot)
|
|
339
|
-
const
|
|
340
|
-
|
|
341
|
-
const url = new URL(request.url)
|
|
349
|
+
const fetch = async (request: Request) => {
|
|
350
|
+
const url = new URL(request.url)
|
|
342
351
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
352
|
+
if (url.pathname === "/api/config") {
|
|
353
|
+
return jsonResponse(toClientConfig(config))
|
|
354
|
+
}
|
|
346
355
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
356
|
+
if (
|
|
357
|
+
url.pathname.startsWith("/api/streams/") &&
|
|
358
|
+
url.pathname.endsWith("/events")
|
|
359
|
+
) {
|
|
360
|
+
const streamId = decodeURIComponent(
|
|
361
|
+
url.pathname
|
|
362
|
+
.slice("/api/streams/".length, -"/events".length)
|
|
363
|
+
.replace(/\/+$/, "")
|
|
364
|
+
)
|
|
365
|
+
const query = new URLSearchParams(url.searchParams)
|
|
366
|
+
|
|
367
|
+
query.delete("stream")
|
|
357
368
|
|
|
358
|
-
|
|
369
|
+
return createSseResponse(config, streamId, query, request.signal)
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
if (
|
|
373
|
+
request.method === "POST" &&
|
|
374
|
+
url.pathname.startsWith("/api/streams/") &&
|
|
375
|
+
url.pathname.includes("/actions/")
|
|
376
|
+
) {
|
|
377
|
+
const pathMatch = url.pathname.match(
|
|
378
|
+
/^\/api\/streams\/(.+?)\/actions\/(.+?)\/?$/
|
|
379
|
+
)
|
|
359
380
|
|
|
360
|
-
|
|
381
|
+
if (!pathMatch) {
|
|
382
|
+
return new Response("Not found", {
|
|
383
|
+
status: 404,
|
|
384
|
+
})
|
|
361
385
|
}
|
|
362
386
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
url.pathname.includes("/actions/")
|
|
367
|
-
) {
|
|
368
|
-
const pathMatch = url.pathname.match(
|
|
369
|
-
/^\/api\/streams\/(.+?)\/actions\/(.+?)\/?$/
|
|
370
|
-
)
|
|
387
|
+
const streamId = decodeURIComponent(pathMatch[1] ?? "")
|
|
388
|
+
const actionId = decodeURIComponent(pathMatch[2] ?? "")
|
|
389
|
+
const query = new URLSearchParams(url.searchParams)
|
|
371
390
|
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
391
|
+
query.delete("stream")
|
|
392
|
+
|
|
393
|
+
return createActionResponse(config, streamId, actionId, query, request)
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
if (url.pathname === "/" || url.pathname === "/index.html") {
|
|
397
|
+
return new Response(indexHtml, {
|
|
398
|
+
headers: {
|
|
399
|
+
"Content-Type": "text/html; charset=utf-8",
|
|
400
|
+
},
|
|
401
|
+
})
|
|
402
|
+
}
|
|
377
403
|
|
|
378
|
-
|
|
379
|
-
const actionId = decodeURIComponent(pathMatch[2] ?? "")
|
|
380
|
-
const query = new URLSearchParams(url.searchParams)
|
|
404
|
+
const assetPath = getClientAssetPath(packageRoot, url.pathname)
|
|
381
405
|
|
|
382
|
-
|
|
406
|
+
if (assetPath && fs.existsSync(assetPath)) {
|
|
407
|
+
return new Response(Bun.file(assetPath))
|
|
408
|
+
}
|
|
383
409
|
|
|
384
|
-
|
|
410
|
+
return new Response("Not found", {
|
|
411
|
+
status: 404,
|
|
412
|
+
})
|
|
413
|
+
}
|
|
414
|
+
const maxPort = requestedPort === 0
|
|
415
|
+
? 0
|
|
416
|
+
: Math.min(requestedPort + MAX_PORT_ATTEMPTS - 1, 65_535)
|
|
417
|
+
let server: ReturnType<typeof Bun.serve> | null = null
|
|
418
|
+
|
|
419
|
+
for (
|
|
420
|
+
let port = requestedPort;
|
|
421
|
+
port <= maxPort && server === null;
|
|
422
|
+
port += 1
|
|
423
|
+
) {
|
|
424
|
+
try {
|
|
425
|
+
server = Bun.serve({
|
|
426
|
+
fetch,
|
|
427
|
+
hostname: host,
|
|
428
|
+
port,
|
|
429
|
+
})
|
|
430
|
+
} catch (error) {
|
|
431
|
+
if (isAddressInUseError(error) && port < maxPort) {
|
|
432
|
+
continue
|
|
385
433
|
}
|
|
386
434
|
|
|
387
|
-
if (
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
},
|
|
392
|
-
})
|
|
435
|
+
if (isAddressInUseError(error)) {
|
|
436
|
+
throw new Error(
|
|
437
|
+
`No available ports from ${requestedPort} to ${maxPort}.`
|
|
438
|
+
)
|
|
393
439
|
}
|
|
394
440
|
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
return new Response(Bun.file(assetPath))
|
|
399
|
-
}
|
|
441
|
+
throw error
|
|
442
|
+
}
|
|
443
|
+
}
|
|
400
444
|
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
hostname: host,
|
|
406
|
-
port,
|
|
407
|
-
})
|
|
408
|
-
const resolvedPort = server.port ?? port
|
|
445
|
+
if (!server) {
|
|
446
|
+
throw new Error(`No available ports from ${requestedPort} to ${maxPort}.`)
|
|
447
|
+
}
|
|
448
|
+
const resolvedPort = server.port ?? requestedPort
|
|
409
449
|
|
|
410
450
|
return {
|
|
411
451
|
config,
|