@rozenite/network-activity-plugin 1.10.0 → 1.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +28 -0
- package/dist/devtools/App.html +2 -2
- package/dist/devtools/assets/{App-DsimzJvx.js → App-2rukIHdY.js} +1013 -264
- package/dist/devtools/assets/{App-CUXU0mup.css → App-xppYUJvX.css} +94 -0
- package/dist/rozenite.json +1 -1
- package/package.json +6 -6
- package/src/ui/components/FilterBar.tsx +13 -45
- package/src/ui/components/NetworkTimeline.tsx +422 -0
- package/src/ui/components/RequestList.tsx +6 -185
- package/src/ui/components/Toolbar.tsx +13 -1
- package/src/ui/hooks/useNetworkActivitySessionExport.ts +39 -0
- package/src/ui/state/__tests__/store.test.ts +77 -0
- package/src/ui/state/derived.ts +2 -0
- package/src/ui/state/filter.ts +49 -0
- package/src/ui/state/hooks.ts +2 -2
- package/src/ui/state/model.ts +1 -0
- package/src/ui/state/store.ts +24 -2
- package/src/ui/utils/__tests__/requestFilters.test.ts +32 -0
- package/src/ui/utils/__tests__/sessionExport.test.ts +174 -0
- package/src/ui/utils/__tests__/symbolication.test.ts +73 -0
- package/src/ui/utils/__tests__/timelineModel.test.ts +170 -0
- package/src/ui/utils/download.ts +7 -0
- package/src/ui/utils/requestFilters.ts +183 -0
- package/src/ui/utils/sessionExport.ts +185 -0
- package/src/ui/utils/symbolication.ts +37 -10
- package/src/ui/utils/timelineModel.ts +352 -0
- package/src/ui/views/InspectorView.tsx +40 -8
|
@@ -20836,12 +20836,13 @@ const getGeneratedFrameLocation$1 = (frame) => ({
|
|
|
20836
20836
|
columnNumber: frame.generatedColumnNumber ?? frame.columnNumber
|
|
20837
20837
|
});
|
|
20838
20838
|
const isGeneratedBundleUrl = (url) => /[^/]+\.bundle(?:[/?#]|$)/.test(url);
|
|
20839
|
+
const isMetroSymbolicatableUrl = (url) => url?.startsWith("http") ?? false;
|
|
20839
20840
|
const canSymbolicateStack = (stack) => stack?.some(
|
|
20840
|
-
(frame) => getGeneratedFrameLocation$1(frame).url
|
|
20841
|
+
(frame) => isMetroSymbolicatableUrl(getGeneratedFrameLocation$1(frame).url)
|
|
20841
20842
|
) ?? false;
|
|
20842
20843
|
const toReactNativeStackFrame = (frame) => {
|
|
20843
20844
|
const generatedLocation = getGeneratedFrameLocation$1(frame);
|
|
20844
|
-
if (!generatedLocation.url) {
|
|
20845
|
+
if (!isMetroSymbolicatableUrl(generatedLocation.url)) {
|
|
20845
20846
|
return null;
|
|
20846
20847
|
}
|
|
20847
20848
|
return {
|
|
@@ -20934,14 +20935,36 @@ const symbolicateInitiator = async (initiator, symbolicateStackTrace = symbolica
|
|
|
20934
20935
|
if (!canSymbolicateStack(initiator.stack)) {
|
|
20935
20936
|
return null;
|
|
20936
20937
|
}
|
|
20937
|
-
const
|
|
20938
|
+
const originalStack = initiator.stack ?? [];
|
|
20939
|
+
const generatedStackFrames = originalStack.flatMap(
|
|
20940
|
+
(originalFrame, originalIndex) => {
|
|
20941
|
+
const frame = toReactNativeStackFrame(originalFrame);
|
|
20942
|
+
return frame ? [{ frame, originalIndex }] : [];
|
|
20943
|
+
}
|
|
20944
|
+
);
|
|
20938
20945
|
if (generatedStackFrames.length === 0) {
|
|
20939
20946
|
return null;
|
|
20940
20947
|
}
|
|
20941
20948
|
try {
|
|
20942
|
-
const symbolicatedStackTrace = await symbolicateStackTrace(
|
|
20943
|
-
|
|
20944
|
-
|
|
20949
|
+
const symbolicatedStackTrace = await symbolicateStackTrace(
|
|
20950
|
+
generatedStackFrames.map((entry) => entry.frame)
|
|
20951
|
+
);
|
|
20952
|
+
const symbolicatedFramesByOriginalIndex = /* @__PURE__ */ new Map();
|
|
20953
|
+
symbolicatedStackTrace.stack.forEach((frame, index2) => {
|
|
20954
|
+
const generatedFrame = generatedStackFrames[index2];
|
|
20955
|
+
if (!generatedFrame) {
|
|
20956
|
+
return;
|
|
20957
|
+
}
|
|
20958
|
+
symbolicatedFramesByOriginalIndex.set(
|
|
20959
|
+
generatedFrame.originalIndex,
|
|
20960
|
+
fromSymbolicatedStackFrame(
|
|
20961
|
+
frame,
|
|
20962
|
+
originalStack[generatedFrame.originalIndex]
|
|
20963
|
+
)
|
|
20964
|
+
);
|
|
20965
|
+
});
|
|
20966
|
+
const symbolicatedStack = originalStack.map(
|
|
20967
|
+
(frame, index2) => symbolicatedFramesByOriginalIndex.get(index2) ?? frame
|
|
20945
20968
|
);
|
|
20946
20969
|
const sourceFrame = getPreferredSourceFrame(
|
|
20947
20970
|
symbolicatedStack,
|
|
@@ -20975,6 +20998,9 @@ const symbolicateInitiator = async (initiator, symbolicateStackTrace = symbolica
|
|
|
20975
20998
|
}
|
|
20976
20999
|
};
|
|
20977
21000
|
const STORE_VERSION = 1;
|
|
21001
|
+
const getElapsedDuration = (endTimestamp, startTimestamp) => {
|
|
21002
|
+
return Math.max(endTimestamp - startTimestamp, 0);
|
|
21003
|
+
};
|
|
20978
21004
|
const createNetworkActivityStore = () => createStore()(
|
|
20979
21005
|
persist(
|
|
20980
21006
|
(set, get) => ({
|
|
@@ -21169,6 +21195,10 @@ const createNetworkActivityStore = () => createStore()(
|
|
|
21169
21195
|
const updatedEntry = {
|
|
21170
21196
|
...httpEntry,
|
|
21171
21197
|
status: "failed",
|
|
21198
|
+
duration: getElapsedDuration(
|
|
21199
|
+
eventData.timestamp,
|
|
21200
|
+
httpEntry.timestamp
|
|
21201
|
+
),
|
|
21172
21202
|
error: eventData.error
|
|
21173
21203
|
};
|
|
21174
21204
|
const newEntries = new Map(state.networkEntries);
|
|
@@ -21259,7 +21289,10 @@ const createNetworkActivityStore = () => createStore()(
|
|
|
21259
21289
|
status: "closed",
|
|
21260
21290
|
closeCode: eventData.code,
|
|
21261
21291
|
closeReason: eventData.reason,
|
|
21262
|
-
duration:
|
|
21292
|
+
duration: getElapsedDuration(
|
|
21293
|
+
eventData.timestamp,
|
|
21294
|
+
wsEntry.timestamp
|
|
21295
|
+
)
|
|
21263
21296
|
};
|
|
21264
21297
|
const newEntries = new Map(state.networkEntries);
|
|
21265
21298
|
newEntries.set(entry.id, updatedEntry);
|
|
@@ -21324,6 +21357,10 @@ const createNetworkActivityStore = () => createStore()(
|
|
|
21324
21357
|
const updatedEntry = {
|
|
21325
21358
|
...wsEntry,
|
|
21326
21359
|
status: "error",
|
|
21360
|
+
duration: getElapsedDuration(
|
|
21361
|
+
eventData.timestamp,
|
|
21362
|
+
wsEntry.timestamp
|
|
21363
|
+
),
|
|
21327
21364
|
error: eventData.error
|
|
21328
21365
|
};
|
|
21329
21366
|
const newEntries = new Map(state.networkEntries);
|
|
@@ -21408,6 +21445,10 @@ const createNetworkActivityStore = () => createStore()(
|
|
|
21408
21445
|
const updatedEntry = {
|
|
21409
21446
|
...sseEntry,
|
|
21410
21447
|
status: "error",
|
|
21448
|
+
duration: getElapsedDuration(
|
|
21449
|
+
eventData.timestamp,
|
|
21450
|
+
sseEntry.timestamp
|
|
21451
|
+
),
|
|
21411
21452
|
error: eventData.error.message
|
|
21412
21453
|
};
|
|
21413
21454
|
const newEntries = new Map(state.networkEntries);
|
|
@@ -21425,7 +21466,10 @@ const createNetworkActivityStore = () => createStore()(
|
|
|
21425
21466
|
const updatedEntry = {
|
|
21426
21467
|
...sseEntry,
|
|
21427
21468
|
status: "closed",
|
|
21428
|
-
duration:
|
|
21469
|
+
duration: getElapsedDuration(
|
|
21470
|
+
eventData.timestamp,
|
|
21471
|
+
sseEntry.timestamp
|
|
21472
|
+
)
|
|
21429
21473
|
};
|
|
21430
21474
|
const newEntries = new Map(state.networkEntries);
|
|
21431
21475
|
newEntries.set(eventData.requestId, updatedEntry);
|
|
@@ -21862,6 +21906,7 @@ const getProcessedRequests = memoize((state) => {
|
|
|
21862
21906
|
method: httpEntry.request.method,
|
|
21863
21907
|
httpStatus: httpEntry.response?.status,
|
|
21864
21908
|
contentType: httpEntry.response?.contentType,
|
|
21909
|
+
ttfb: httpEntry.ttfb,
|
|
21865
21910
|
progress: httpEntry.progress
|
|
21866
21911
|
});
|
|
21867
21912
|
} else if (entry.type === "websocket") {
|
|
@@ -21939,9 +21984,232 @@ const useOverrides = () => {
|
|
|
21939
21984
|
const useClientUISettings = () => {
|
|
21940
21985
|
return useNetworkActivityStore((state) => state.clientUISettings);
|
|
21941
21986
|
};
|
|
21987
|
+
const base64ToBytes = (base64) => {
|
|
21988
|
+
const binary = atob(base64);
|
|
21989
|
+
const bytes = new Uint8Array(binary.length);
|
|
21990
|
+
for (let i = 0; i < binary.length; i++) {
|
|
21991
|
+
bytes[i] = binary.charCodeAt(i);
|
|
21992
|
+
}
|
|
21993
|
+
return bytes;
|
|
21994
|
+
};
|
|
21995
|
+
const base64ToBlob = (base64, contentType) => {
|
|
21996
|
+
const binary = atob(base64);
|
|
21997
|
+
const buffer = new ArrayBuffer(binary.length);
|
|
21998
|
+
const view = new Uint8Array(buffer);
|
|
21999
|
+
for (let i = 0; i < binary.length; i++) {
|
|
22000
|
+
view[i] = binary.charCodeAt(i);
|
|
22001
|
+
}
|
|
22002
|
+
return new Blob([buffer], {
|
|
22003
|
+
type: contentType || "application/octet-stream"
|
|
22004
|
+
});
|
|
22005
|
+
};
|
|
22006
|
+
const CONTENT_TYPE_EXTENSIONS = {
|
|
22007
|
+
"application/pdf": "pdf",
|
|
22008
|
+
"application/zip": "zip",
|
|
22009
|
+
"application/gzip": "gz",
|
|
22010
|
+
"application/json": "json",
|
|
22011
|
+
"application/xml": "xml",
|
|
22012
|
+
"application/javascript": "js",
|
|
22013
|
+
"application/octet-stream": "bin",
|
|
22014
|
+
"image/png": "png",
|
|
22015
|
+
"image/jpeg": "jpg",
|
|
22016
|
+
"image/gif": "gif",
|
|
22017
|
+
"image/webp": "webp",
|
|
22018
|
+
"image/svg+xml": "svg",
|
|
22019
|
+
"image/bmp": "bmp",
|
|
22020
|
+
"image/x-icon": "ico",
|
|
22021
|
+
"audio/mpeg": "mp3",
|
|
22022
|
+
"audio/ogg": "ogg",
|
|
22023
|
+
"audio/wav": "wav",
|
|
22024
|
+
"video/mp4": "mp4",
|
|
22025
|
+
"video/webm": "webm",
|
|
22026
|
+
"font/woff": "woff",
|
|
22027
|
+
"font/woff2": "woff2",
|
|
22028
|
+
"font/ttf": "ttf",
|
|
22029
|
+
"font/otf": "otf",
|
|
22030
|
+
"text/html": "html",
|
|
22031
|
+
"text/plain": "txt",
|
|
22032
|
+
"text/css": "css",
|
|
22033
|
+
"text/csv": "csv"
|
|
22034
|
+
};
|
|
22035
|
+
const extensionForContentType = (contentType) => {
|
|
22036
|
+
const bare = contentType.split(";", 1)[0]?.trim().toLowerCase() ?? "";
|
|
22037
|
+
return CONTENT_TYPE_EXTENSIONS[bare] ?? "bin";
|
|
22038
|
+
};
|
|
22039
|
+
const readHeader = (headers, name) => {
|
|
22040
|
+
if (!headers) return void 0;
|
|
22041
|
+
const lowerTarget = name.toLowerCase();
|
|
22042
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
22043
|
+
if (key.toLowerCase() === lowerTarget) {
|
|
22044
|
+
return Array.isArray(value) ? value[0] : value;
|
|
22045
|
+
}
|
|
22046
|
+
}
|
|
22047
|
+
return void 0;
|
|
22048
|
+
};
|
|
22049
|
+
const parseContentDispositionFilename = (header) => {
|
|
22050
|
+
if (!header) return void 0;
|
|
22051
|
+
const extended = /filename\*\s*=\s*[^']*''([^;]+)/i.exec(header);
|
|
22052
|
+
if (extended?.[1]) {
|
|
22053
|
+
try {
|
|
22054
|
+
return decodeURIComponent(extended[1].trim()) || void 0;
|
|
22055
|
+
} catch {
|
|
22056
|
+
}
|
|
22057
|
+
}
|
|
22058
|
+
const basic2 = /filename\s*=\s*("([^"]*)"|([^;]+))/i.exec(header);
|
|
22059
|
+
const value = basic2?.[2] ?? basic2?.[3];
|
|
22060
|
+
return value?.trim() || void 0;
|
|
22061
|
+
};
|
|
22062
|
+
const filenameFromUrl = (url) => {
|
|
22063
|
+
try {
|
|
22064
|
+
const parsed = new URL(url);
|
|
22065
|
+
const segments = parsed.pathname.split("/").filter(Boolean);
|
|
22066
|
+
const last = segments[segments.length - 1];
|
|
22067
|
+
return last && last.length > 0 ? last : void 0;
|
|
22068
|
+
} catch {
|
|
22069
|
+
return void 0;
|
|
22070
|
+
}
|
|
22071
|
+
};
|
|
22072
|
+
const deriveFilename = ({
|
|
22073
|
+
headers,
|
|
22074
|
+
url,
|
|
22075
|
+
contentType
|
|
22076
|
+
}) => {
|
|
22077
|
+
const fromDisposition = parseContentDispositionFilename(
|
|
22078
|
+
readHeader(headers, "Content-Disposition")
|
|
22079
|
+
);
|
|
22080
|
+
if (fromDisposition) return fromDisposition;
|
|
22081
|
+
const fromUrl = filenameFromUrl(url);
|
|
22082
|
+
if (fromUrl) return fromUrl;
|
|
22083
|
+
return `response.${extensionForContentType(contentType)}`;
|
|
22084
|
+
};
|
|
22085
|
+
const downloadBlob = (blob, filename) => {
|
|
22086
|
+
const objectUrl = URL.createObjectURL(blob);
|
|
22087
|
+
const anchor = document.createElement("a");
|
|
22088
|
+
anchor.href = objectUrl;
|
|
22089
|
+
anchor.download = filename;
|
|
22090
|
+
document.body.appendChild(anchor);
|
|
22091
|
+
anchor.click();
|
|
22092
|
+
document.body.removeChild(anchor);
|
|
22093
|
+
setTimeout(() => URL.revokeObjectURL(objectUrl), 0);
|
|
22094
|
+
};
|
|
22095
|
+
const downloadJson = (data, filename) => {
|
|
22096
|
+
downloadBlob(
|
|
22097
|
+
new Blob([JSON.stringify(data, null, 2)], { type: "application/json" }),
|
|
22098
|
+
filename
|
|
22099
|
+
);
|
|
22100
|
+
};
|
|
22101
|
+
const EXPORT_SCHEMA_VERSION = 1;
|
|
22102
|
+
const getDuration = (duration) => duration ?? null;
|
|
22103
|
+
const serializeHttpEntry = (entry) => ({
|
|
22104
|
+
id: entry.id,
|
|
22105
|
+
type: "http",
|
|
22106
|
+
source: entry.source,
|
|
22107
|
+
timestamp: entry.timestamp,
|
|
22108
|
+
duration: getDuration(entry.duration),
|
|
22109
|
+
status: entry.status,
|
|
22110
|
+
error: entry.error,
|
|
22111
|
+
canceled: entry.canceled,
|
|
22112
|
+
request: entry.request,
|
|
22113
|
+
response: entry.response ?? null,
|
|
22114
|
+
size: entry.size ?? null,
|
|
22115
|
+
ttfb: entry.ttfb ?? null,
|
|
22116
|
+
initiator: entry.initiator,
|
|
22117
|
+
resourceType: entry.resourceType,
|
|
22118
|
+
progress: entry.progress
|
|
22119
|
+
});
|
|
22120
|
+
const serializeWebSocketEntry = (entry, websocketMessages) => ({
|
|
22121
|
+
id: entry.id,
|
|
22122
|
+
type: "websocket",
|
|
22123
|
+
source: entry.source,
|
|
22124
|
+
timestamp: entry.timestamp,
|
|
22125
|
+
duration: getDuration(entry.duration),
|
|
22126
|
+
status: entry.status,
|
|
22127
|
+
connection: entry.connection,
|
|
22128
|
+
error: entry.error,
|
|
22129
|
+
closeCode: entry.closeCode,
|
|
22130
|
+
closeReason: entry.closeReason,
|
|
22131
|
+
messages: websocketMessages.get(entry.id) ?? []
|
|
22132
|
+
});
|
|
22133
|
+
const serializeSSEEntry = (entry) => ({
|
|
22134
|
+
id: entry.id,
|
|
22135
|
+
type: "sse",
|
|
22136
|
+
source: entry.source,
|
|
22137
|
+
timestamp: entry.timestamp,
|
|
22138
|
+
duration: getDuration(entry.duration),
|
|
22139
|
+
status: entry.status,
|
|
22140
|
+
error: entry.error,
|
|
22141
|
+
request: entry.request,
|
|
22142
|
+
response: entry.response ?? null,
|
|
22143
|
+
initiator: entry.initiator,
|
|
22144
|
+
resourceType: entry.resourceType,
|
|
22145
|
+
messages: entry.messages
|
|
22146
|
+
});
|
|
22147
|
+
const serializeEntry = (entry, websocketMessages) => {
|
|
22148
|
+
switch (entry.type) {
|
|
22149
|
+
case "http":
|
|
22150
|
+
return serializeHttpEntry(entry);
|
|
22151
|
+
case "websocket":
|
|
22152
|
+
return serializeWebSocketEntry(entry, websocketMessages);
|
|
22153
|
+
case "sse":
|
|
22154
|
+
return serializeSSEEntry(entry);
|
|
22155
|
+
}
|
|
22156
|
+
};
|
|
22157
|
+
const createNetworkActivitySessionExport = (networkEntries, websocketMessages, exportedAt = /* @__PURE__ */ new Date()) => {
|
|
22158
|
+
const entries = Array.from(networkEntries.values()).sort((a, b2) => a.timestamp - b2.timestamp).map((entry) => serializeEntry(entry, websocketMessages));
|
|
22159
|
+
return {
|
|
22160
|
+
schemaVersion: EXPORT_SCHEMA_VERSION,
|
|
22161
|
+
tool: "rozenite-network-activity",
|
|
22162
|
+
exportedAt: exportedAt.toISOString(),
|
|
22163
|
+
summary: {
|
|
22164
|
+
totalEntries: entries.length,
|
|
22165
|
+
httpRequests: entries.filter((entry) => entry.type === "http").length,
|
|
22166
|
+
webSocketConnections: entries.filter(
|
|
22167
|
+
(entry) => entry.type === "websocket"
|
|
22168
|
+
).length,
|
|
22169
|
+
sseConnections: entries.filter((entry) => entry.type === "sse").length,
|
|
22170
|
+
realtimeMessages: entries.reduce((count2, entry) => {
|
|
22171
|
+
if (entry.type === "websocket" || entry.type === "sse") {
|
|
22172
|
+
return count2 + entry.messages.length;
|
|
22173
|
+
}
|
|
22174
|
+
return count2;
|
|
22175
|
+
}, 0)
|
|
22176
|
+
},
|
|
22177
|
+
entries
|
|
22178
|
+
};
|
|
22179
|
+
};
|
|
22180
|
+
const getNetworkActivitySessionExportFileName = (exportedAt = /* @__PURE__ */ new Date()) => {
|
|
22181
|
+
const timestamp = exportedAt.toISOString().replace(/\.\d{3}Z$/, "Z").replace(/[:]/g, "-");
|
|
22182
|
+
return `rozenite-network-session-${timestamp}.json`;
|
|
22183
|
+
};
|
|
22184
|
+
const useNetworkActivitySessionExport = () => {
|
|
22185
|
+
const canExportSession = useNetworkActivityStore(
|
|
22186
|
+
(state) => state.networkEntries.size > 0
|
|
22187
|
+
);
|
|
22188
|
+
const exportSession = reactExports.useCallback(() => {
|
|
22189
|
+
const { networkEntries, websocketMessages } = store.getState();
|
|
22190
|
+
if (networkEntries.size === 0) {
|
|
22191
|
+
return;
|
|
22192
|
+
}
|
|
22193
|
+
const exportedAt = /* @__PURE__ */ new Date();
|
|
22194
|
+
const exportData = createNetworkActivitySessionExport(
|
|
22195
|
+
networkEntries,
|
|
22196
|
+
websocketMessages,
|
|
22197
|
+
exportedAt
|
|
22198
|
+
);
|
|
22199
|
+
downloadJson(
|
|
22200
|
+
exportData,
|
|
22201
|
+
getNetworkActivitySessionExportFileName(exportedAt)
|
|
22202
|
+
);
|
|
22203
|
+
}, []);
|
|
22204
|
+
return {
|
|
22205
|
+
canExportSession,
|
|
22206
|
+
exportSession
|
|
22207
|
+
};
|
|
22208
|
+
};
|
|
21942
22209
|
const Toolbar = () => {
|
|
21943
22210
|
const actions = useNetworkActivityActions();
|
|
21944
22211
|
const isRecording = useIsRecording();
|
|
22212
|
+
const { canExportSession, exportSession } = useNetworkActivitySessionExport();
|
|
21945
22213
|
const onToggleRecording = () => {
|
|
21946
22214
|
actions.setRecording(!isRecording);
|
|
21947
22215
|
};
|
|
@@ -21969,6 +22237,18 @@ const Toolbar = () => {
|
|
|
21969
22237
|
className: "h-8 w-8 p-0 text-gray-400 hover:text-blue-400",
|
|
21970
22238
|
children: /* @__PURE__ */ jsxRuntimeExports.jsx(Trash2, { className: "h-4 w-4" })
|
|
21971
22239
|
}
|
|
22240
|
+
),
|
|
22241
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
22242
|
+
Button,
|
|
22243
|
+
{
|
|
22244
|
+
variant: "ghost",
|
|
22245
|
+
size: "sm",
|
|
22246
|
+
onClick: exportSession,
|
|
22247
|
+
disabled: !canExportSession,
|
|
22248
|
+
className: "ml-auto h-8 w-8 p-0 text-gray-400 hover:text-blue-400",
|
|
22249
|
+
title: "Export session",
|
|
22250
|
+
children: /* @__PURE__ */ jsxRuntimeExports.jsx(Download, { className: "h-4 w-4" })
|
|
22251
|
+
}
|
|
21972
22252
|
)
|
|
21973
22253
|
] });
|
|
21974
22254
|
};
|
|
@@ -24755,7 +25035,7 @@ const formatStartTime = (startTime) => {
|
|
|
24755
25035
|
const milliseconds = date.getMilliseconds().toString().padStart(3, "0");
|
|
24756
25036
|
return `${timeString}.${milliseconds}`;
|
|
24757
25037
|
};
|
|
24758
|
-
const extractDomainAndPath = (url) => {
|
|
25038
|
+
const extractDomainAndPath$1 = (url) => {
|
|
24759
25039
|
try {
|
|
24760
25040
|
const { hostname, pathname, search, hash: hash2, port } = new URL(url);
|
|
24761
25041
|
return {
|
|
@@ -24806,122 +25086,9 @@ const sortTime = (rowA, rowB, columnId) => {
|
|
|
24806
25086
|
};
|
|
24807
25087
|
return getNumericValue(a) - getNumericValue(b2);
|
|
24808
25088
|
};
|
|
24809
|
-
const parseThreshold = (value) => {
|
|
24810
|
-
const normalizedValue = value.trim();
|
|
24811
|
-
if (!normalizedValue) {
|
|
24812
|
-
return null;
|
|
24813
|
-
}
|
|
24814
|
-
const parsedValue = Number(normalizedValue);
|
|
24815
|
-
return Number.isFinite(parsedValue) ? parsedValue : null;
|
|
24816
|
-
};
|
|
24817
|
-
const matchesStatusFilter = (statusCode, statusFilter) => {
|
|
24818
|
-
const normalizedFilter = statusFilter.trim().toLowerCase();
|
|
24819
|
-
if (!normalizedFilter) {
|
|
24820
|
-
return true;
|
|
24821
|
-
}
|
|
24822
|
-
if (statusCode === void 0) {
|
|
24823
|
-
return false;
|
|
24824
|
-
}
|
|
24825
|
-
const statusRangeMatch = normalizedFilter.match(/^(\d{3})\s*-\s*(\d{3})$/);
|
|
24826
|
-
if (statusRangeMatch) {
|
|
24827
|
-
const min2 = Number(statusRangeMatch[1]);
|
|
24828
|
-
const max2 = Number(statusRangeMatch[2]);
|
|
24829
|
-
return statusCode >= min2 && statusCode <= max2;
|
|
24830
|
-
}
|
|
24831
|
-
const statusClassMatch = normalizedFilter.match(/^([1-5])xx$/);
|
|
24832
|
-
if (statusClassMatch) {
|
|
24833
|
-
return Math.floor(statusCode / 100) === Number(statusClassMatch[1]);
|
|
24834
|
-
}
|
|
24835
|
-
const comparisonMatch = normalizedFilter.match(/^(>=|<=|>|<)\s*(\d{3})$/);
|
|
24836
|
-
if (comparisonMatch) {
|
|
24837
|
-
const value = Number(comparisonMatch[2]);
|
|
24838
|
-
switch (comparisonMatch[1]) {
|
|
24839
|
-
case ">=":
|
|
24840
|
-
return statusCode >= value;
|
|
24841
|
-
case "<=":
|
|
24842
|
-
return statusCode <= value;
|
|
24843
|
-
case ">":
|
|
24844
|
-
return statusCode > value;
|
|
24845
|
-
case "<":
|
|
24846
|
-
return statusCode < value;
|
|
24847
|
-
}
|
|
24848
|
-
}
|
|
24849
|
-
return statusCode === Number(normalizedFilter);
|
|
24850
|
-
};
|
|
24851
|
-
const isInFlightStatus = (status) => {
|
|
24852
|
-
return ["pending", "loading", "connecting", "open"].includes(status);
|
|
24853
|
-
};
|
|
24854
|
-
const isFailedStatus = (status) => {
|
|
24855
|
-
return ["failed", "error"].includes(status);
|
|
24856
|
-
};
|
|
24857
|
-
const isHttpMethod = (method) => method !== "WS" && method !== "SSE";
|
|
24858
|
-
const filterNetworkRequests = (requests, filter) => {
|
|
24859
|
-
const searchText = filter.text.trim().toLowerCase();
|
|
24860
|
-
const domainFilter = filter.advanced.domain.trim().toLowerCase();
|
|
24861
|
-
const contentTypeFilter = filter.advanced.contentType.trim().toLowerCase();
|
|
24862
|
-
const minSize = parseThreshold(filter.advanced.minSize);
|
|
24863
|
-
const maxSize = parseThreshold(filter.advanced.maxSize);
|
|
24864
|
-
const minDuration = parseThreshold(filter.advanced.minDuration);
|
|
24865
|
-
const maxDuration = parseThreshold(filter.advanced.maxDuration);
|
|
24866
|
-
return requests.filter((request) => {
|
|
24867
|
-
if (filter.types.size > 0 && !filter.types.has(request.type)) {
|
|
24868
|
-
return false;
|
|
24869
|
-
}
|
|
24870
|
-
if (filter.advanced.methods.size > 0 && (!isHttpMethod(request.method) || !filter.advanced.methods.has(request.method))) {
|
|
24871
|
-
return false;
|
|
24872
|
-
}
|
|
24873
|
-
if (filter.advanced.sources.size > 0 && (!request.source || !filter.advanced.sources.has(request.source))) {
|
|
24874
|
-
return false;
|
|
24875
|
-
}
|
|
24876
|
-
if (!matchesStatusFilter(request.statusCode, filter.advanced.status)) {
|
|
24877
|
-
return false;
|
|
24878
|
-
}
|
|
24879
|
-
if (domainFilter && !request.domain.toLowerCase().includes(domainFilter)) {
|
|
24880
|
-
return false;
|
|
24881
|
-
}
|
|
24882
|
-
if (contentTypeFilter && !request.contentType?.toLowerCase().includes(contentTypeFilter)) {
|
|
24883
|
-
return false;
|
|
24884
|
-
}
|
|
24885
|
-
if (filter.advanced.failedOnly && !isFailedStatus(request.statusState)) {
|
|
24886
|
-
return false;
|
|
24887
|
-
}
|
|
24888
|
-
if (filter.advanced.inFlightOnly && !isInFlightStatus(request.statusState)) {
|
|
24889
|
-
return false;
|
|
24890
|
-
}
|
|
24891
|
-
if (filter.advanced.overriddenOnly && !request.hasOverride) {
|
|
24892
|
-
return false;
|
|
24893
|
-
}
|
|
24894
|
-
if (minSize !== null && (request.sizeBytes === null || request.sizeBytes < minSize)) {
|
|
24895
|
-
return false;
|
|
24896
|
-
}
|
|
24897
|
-
if (maxSize !== null && (request.sizeBytes === null || request.sizeBytes > maxSize)) {
|
|
24898
|
-
return false;
|
|
24899
|
-
}
|
|
24900
|
-
if (minDuration !== null && request.durationMs < minDuration) {
|
|
24901
|
-
return false;
|
|
24902
|
-
}
|
|
24903
|
-
if (maxDuration !== null && request.durationMs > maxDuration) {
|
|
24904
|
-
return false;
|
|
24905
|
-
}
|
|
24906
|
-
if (searchText) {
|
|
24907
|
-
const searchableFields = [
|
|
24908
|
-
request.name,
|
|
24909
|
-
request.method,
|
|
24910
|
-
request.status,
|
|
24911
|
-
request.domain,
|
|
24912
|
-
request.path,
|
|
24913
|
-
request.source,
|
|
24914
|
-
request.type,
|
|
24915
|
-
request.contentType
|
|
24916
|
-
].join(" ").toLowerCase();
|
|
24917
|
-
return searchableFields.includes(searchText);
|
|
24918
|
-
}
|
|
24919
|
-
return true;
|
|
24920
|
-
});
|
|
24921
|
-
};
|
|
24922
25089
|
const processNetworkRequests = (processedRequests, overrides, showEntirePathAsName = false) => {
|
|
24923
25090
|
return processedRequests.map((request) => {
|
|
24924
|
-
const { domain, path } = extractDomainAndPath(request.name);
|
|
25091
|
+
const { domain, path } = extractDomainAndPath$1(request.name);
|
|
24925
25092
|
const duration = request.duration || 0;
|
|
24926
25093
|
const hasOverride = overrides.has(request.name);
|
|
24927
25094
|
let statusDisplay = request.httpStatus || request.status;
|
|
@@ -24936,7 +25103,6 @@ const processNetworkRequests = (processedRequests, overrides, showEntirePathAsNa
|
|
|
24936
25103
|
name: generateName(request.name, showEntirePathAsName),
|
|
24937
25104
|
status: statusDisplay,
|
|
24938
25105
|
statusCode: request.httpStatus || void 0,
|
|
24939
|
-
statusState: request.status,
|
|
24940
25106
|
method: request.method,
|
|
24941
25107
|
domain,
|
|
24942
25108
|
path,
|
|
@@ -25002,21 +25168,19 @@ const columns$1 = [
|
|
|
25002
25168
|
sortingFn: sortTime
|
|
25003
25169
|
})
|
|
25004
25170
|
];
|
|
25005
|
-
const RequestList = ({
|
|
25171
|
+
const RequestList = ({ requests: filteredRequests }) => {
|
|
25006
25172
|
const actions = useNetworkActivityActions();
|
|
25007
|
-
const processedRequests = useProcessedRequests();
|
|
25008
25173
|
const selectedRequestId = useSelectedRequestId();
|
|
25009
25174
|
const [sorting, setSorting] = reactExports.useState([]);
|
|
25010
25175
|
const overrides = useOverrides();
|
|
25011
25176
|
const clientUISettings = useClientUISettings();
|
|
25012
25177
|
const requests = reactExports.useMemo(() => {
|
|
25013
|
-
|
|
25014
|
-
|
|
25178
|
+
return processNetworkRequests(
|
|
25179
|
+
filteredRequests,
|
|
25015
25180
|
overrides,
|
|
25016
25181
|
clientUISettings?.showUrlAsName
|
|
25017
25182
|
);
|
|
25018
|
-
|
|
25019
|
-
}, [processedRequests, overrides, clientUISettings?.showUrlAsName, filter]);
|
|
25183
|
+
}, [filteredRequests, overrides, clientUISettings?.showUrlAsName]);
|
|
25020
25184
|
const table = useReactTable({
|
|
25021
25185
|
data: requests,
|
|
25022
25186
|
columns: columns$1,
|
|
@@ -25879,7 +26043,7 @@ const TabsContent = reactExports.forwardRef(({ className, ...props }, ref) => /*
|
|
|
25879
26043
|
}
|
|
25880
26044
|
));
|
|
25881
26045
|
TabsContent.displayName = Content$1.displayName;
|
|
25882
|
-
function clamp$
|
|
26046
|
+
function clamp$2(value, [min2, max2]) {
|
|
25883
26047
|
return Math.min(max2, Math.max(min2, value));
|
|
25884
26048
|
}
|
|
25885
26049
|
function useStateMachine(initialState, machine) {
|
|
@@ -26529,7 +26693,7 @@ function getThumbOffsetFromScroll(scrollPos, sizes, dir = "ltr") {
|
|
|
26529
26693
|
const maxScrollPos = sizes.content - sizes.viewport;
|
|
26530
26694
|
const maxThumbPos = scrollbar - thumbSizePx;
|
|
26531
26695
|
const scrollClampRange = dir === "ltr" ? [0, maxScrollPos] : [maxScrollPos * -1, 0];
|
|
26532
|
-
const scrollWithoutMomentum = clamp$
|
|
26696
|
+
const scrollWithoutMomentum = clamp$2(scrollPos, scrollClampRange);
|
|
26533
26697
|
const interpolate = linearScale([0, maxScrollPos], [0, maxThumbPos]);
|
|
26534
26698
|
return interpolate(scrollWithoutMomentum);
|
|
26535
26699
|
}
|
|
@@ -27272,7 +27436,7 @@ const oppositeAlignmentMap = {
|
|
|
27272
27436
|
start: "end",
|
|
27273
27437
|
end: "start"
|
|
27274
27438
|
};
|
|
27275
|
-
function clamp(start, value, end) {
|
|
27439
|
+
function clamp$1(start, value, end) {
|
|
27276
27440
|
return max(start, min(value, end));
|
|
27277
27441
|
}
|
|
27278
27442
|
function evaluate(value, param) {
|
|
@@ -27623,7 +27787,7 @@ const arrow$3 = (options) => ({
|
|
|
27623
27787
|
const min$12 = minPadding;
|
|
27624
27788
|
const max2 = clientSize - arrowDimensions[length] - maxPadding;
|
|
27625
27789
|
const center = clientSize / 2 - arrowDimensions[length] / 2 + centerToReference;
|
|
27626
|
-
const offset2 = clamp(min$12, center, max2);
|
|
27790
|
+
const offset2 = clamp$1(min$12, center, max2);
|
|
27627
27791
|
const shouldAddOffset = !middlewareData.arrow && getAlignment(placement) != null && center !== offset2 && rects.reference[length] / 2 - (center < min$12 ? minPadding : maxPadding) - arrowDimensions[length] / 2 < 0;
|
|
27628
27792
|
const alignmentOffset = shouldAddOffset ? center < min$12 ? center - min$12 : center - max2 : 0;
|
|
27629
27793
|
return {
|
|
@@ -27920,14 +28084,14 @@ const shift$2 = function(options) {
|
|
|
27920
28084
|
const maxSide = mainAxis === "y" ? "bottom" : "right";
|
|
27921
28085
|
const min2 = mainAxisCoord + overflow[minSide];
|
|
27922
28086
|
const max2 = mainAxisCoord - overflow[maxSide];
|
|
27923
|
-
mainAxisCoord = clamp(min2, mainAxisCoord, max2);
|
|
28087
|
+
mainAxisCoord = clamp$1(min2, mainAxisCoord, max2);
|
|
27924
28088
|
}
|
|
27925
28089
|
if (checkCrossAxis) {
|
|
27926
28090
|
const minSide = crossAxis === "y" ? "top" : "left";
|
|
27927
28091
|
const maxSide = crossAxis === "y" ? "bottom" : "right";
|
|
27928
28092
|
const min2 = crossAxisCoord + overflow[minSide];
|
|
27929
28093
|
const max2 = crossAxisCoord - overflow[maxSide];
|
|
27930
|
-
crossAxisCoord = clamp(min2, crossAxisCoord, max2);
|
|
28094
|
+
crossAxisCoord = clamp$1(min2, crossAxisCoord, max2);
|
|
27931
28095
|
}
|
|
27932
28096
|
const limitedCoords = limiter.fn({
|
|
27933
28097
|
...state,
|
|
@@ -38796,120 +38960,12 @@ const HexView = ({ bytes }) => {
|
|
|
38796
38960
|
}
|
|
38797
38961
|
) });
|
|
38798
38962
|
};
|
|
38799
|
-
const
|
|
38800
|
-
|
|
38801
|
-
|
|
38802
|
-
for (let i = 0; i < binary.length; i++) {
|
|
38803
|
-
bytes[i] = binary.charCodeAt(i);
|
|
38963
|
+
const formatBytes$1 = (bytes) => {
|
|
38964
|
+
if (bytes >= 1024 * 1024) {
|
|
38965
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
38804
38966
|
}
|
|
38805
|
-
|
|
38806
|
-
}
|
|
38807
|
-
const base64ToBlob = (base64, contentType) => {
|
|
38808
|
-
const binary = atob(base64);
|
|
38809
|
-
const buffer = new ArrayBuffer(binary.length);
|
|
38810
|
-
const view = new Uint8Array(buffer);
|
|
38811
|
-
for (let i = 0; i < binary.length; i++) {
|
|
38812
|
-
view[i] = binary.charCodeAt(i);
|
|
38813
|
-
}
|
|
38814
|
-
return new Blob([buffer], {
|
|
38815
|
-
type: contentType || "application/octet-stream"
|
|
38816
|
-
});
|
|
38817
|
-
};
|
|
38818
|
-
const CONTENT_TYPE_EXTENSIONS = {
|
|
38819
|
-
"application/pdf": "pdf",
|
|
38820
|
-
"application/zip": "zip",
|
|
38821
|
-
"application/gzip": "gz",
|
|
38822
|
-
"application/json": "json",
|
|
38823
|
-
"application/xml": "xml",
|
|
38824
|
-
"application/javascript": "js",
|
|
38825
|
-
"application/octet-stream": "bin",
|
|
38826
|
-
"image/png": "png",
|
|
38827
|
-
"image/jpeg": "jpg",
|
|
38828
|
-
"image/gif": "gif",
|
|
38829
|
-
"image/webp": "webp",
|
|
38830
|
-
"image/svg+xml": "svg",
|
|
38831
|
-
"image/bmp": "bmp",
|
|
38832
|
-
"image/x-icon": "ico",
|
|
38833
|
-
"audio/mpeg": "mp3",
|
|
38834
|
-
"audio/ogg": "ogg",
|
|
38835
|
-
"audio/wav": "wav",
|
|
38836
|
-
"video/mp4": "mp4",
|
|
38837
|
-
"video/webm": "webm",
|
|
38838
|
-
"font/woff": "woff",
|
|
38839
|
-
"font/woff2": "woff2",
|
|
38840
|
-
"font/ttf": "ttf",
|
|
38841
|
-
"font/otf": "otf",
|
|
38842
|
-
"text/html": "html",
|
|
38843
|
-
"text/plain": "txt",
|
|
38844
|
-
"text/css": "css",
|
|
38845
|
-
"text/csv": "csv"
|
|
38846
|
-
};
|
|
38847
|
-
const extensionForContentType = (contentType) => {
|
|
38848
|
-
const bare = contentType.split(";", 1)[0]?.trim().toLowerCase() ?? "";
|
|
38849
|
-
return CONTENT_TYPE_EXTENSIONS[bare] ?? "bin";
|
|
38850
|
-
};
|
|
38851
|
-
const readHeader = (headers, name) => {
|
|
38852
|
-
if (!headers) return void 0;
|
|
38853
|
-
const lowerTarget = name.toLowerCase();
|
|
38854
|
-
for (const [key, value] of Object.entries(headers)) {
|
|
38855
|
-
if (key.toLowerCase() === lowerTarget) {
|
|
38856
|
-
return Array.isArray(value) ? value[0] : value;
|
|
38857
|
-
}
|
|
38858
|
-
}
|
|
38859
|
-
return void 0;
|
|
38860
|
-
};
|
|
38861
|
-
const parseContentDispositionFilename = (header) => {
|
|
38862
|
-
if (!header) return void 0;
|
|
38863
|
-
const extended = /filename\*\s*=\s*[^']*''([^;]+)/i.exec(header);
|
|
38864
|
-
if (extended?.[1]) {
|
|
38865
|
-
try {
|
|
38866
|
-
return decodeURIComponent(extended[1].trim()) || void 0;
|
|
38867
|
-
} catch {
|
|
38868
|
-
}
|
|
38869
|
-
}
|
|
38870
|
-
const basic2 = /filename\s*=\s*("([^"]*)"|([^;]+))/i.exec(header);
|
|
38871
|
-
const value = basic2?.[2] ?? basic2?.[3];
|
|
38872
|
-
return value?.trim() || void 0;
|
|
38873
|
-
};
|
|
38874
|
-
const filenameFromUrl = (url) => {
|
|
38875
|
-
try {
|
|
38876
|
-
const parsed = new URL(url);
|
|
38877
|
-
const segments = parsed.pathname.split("/").filter(Boolean);
|
|
38878
|
-
const last = segments[segments.length - 1];
|
|
38879
|
-
return last && last.length > 0 ? last : void 0;
|
|
38880
|
-
} catch {
|
|
38881
|
-
return void 0;
|
|
38882
|
-
}
|
|
38883
|
-
};
|
|
38884
|
-
const deriveFilename = ({
|
|
38885
|
-
headers,
|
|
38886
|
-
url,
|
|
38887
|
-
contentType
|
|
38888
|
-
}) => {
|
|
38889
|
-
const fromDisposition = parseContentDispositionFilename(
|
|
38890
|
-
readHeader(headers, "Content-Disposition")
|
|
38891
|
-
);
|
|
38892
|
-
if (fromDisposition) return fromDisposition;
|
|
38893
|
-
const fromUrl = filenameFromUrl(url);
|
|
38894
|
-
if (fromUrl) return fromUrl;
|
|
38895
|
-
return `response.${extensionForContentType(contentType)}`;
|
|
38896
|
-
};
|
|
38897
|
-
const downloadBlob = (blob, filename) => {
|
|
38898
|
-
const objectUrl = URL.createObjectURL(blob);
|
|
38899
|
-
const anchor = document.createElement("a");
|
|
38900
|
-
anchor.href = objectUrl;
|
|
38901
|
-
anchor.download = filename;
|
|
38902
|
-
document.body.appendChild(anchor);
|
|
38903
|
-
anchor.click();
|
|
38904
|
-
document.body.removeChild(anchor);
|
|
38905
|
-
setTimeout(() => URL.revokeObjectURL(objectUrl), 0);
|
|
38906
|
-
};
|
|
38907
|
-
const formatBytes$1 = (bytes) => {
|
|
38908
|
-
if (bytes >= 1024 * 1024) {
|
|
38909
|
-
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
38910
|
-
}
|
|
38911
|
-
if (bytes >= 1024) {
|
|
38912
|
-
return `${(bytes / 1024).toFixed(1)} KB`;
|
|
38967
|
+
if (bytes >= 1024) {
|
|
38968
|
+
return `${(bytes / 1024).toFixed(1)} KB`;
|
|
38913
38969
|
}
|
|
38914
38970
|
return `${bytes} bytes`;
|
|
38915
38971
|
};
|
|
@@ -41730,19 +41786,14 @@ const Input = reactExports.forwardRef(
|
|
|
41730
41786
|
}
|
|
41731
41787
|
);
|
|
41732
41788
|
Input.displayName = "Input";
|
|
41733
|
-
const
|
|
41734
|
-
|
|
41735
|
-
"
|
|
41736
|
-
"
|
|
41737
|
-
"PUT",
|
|
41738
|
-
"PATCH",
|
|
41739
|
-
"DELETE",
|
|
41740
|
-
"HEAD"
|
|
41789
|
+
const DEFAULT_REQUEST_TYPES = [
|
|
41790
|
+
"http",
|
|
41791
|
+
"websocket",
|
|
41792
|
+
"sse"
|
|
41741
41793
|
];
|
|
41742
|
-
const SOURCES = ["builtin", "nitro"];
|
|
41743
41794
|
const createDefaultFilter = () => ({
|
|
41744
41795
|
text: "",
|
|
41745
|
-
types:
|
|
41796
|
+
types: new Set(DEFAULT_REQUEST_TYPES),
|
|
41746
41797
|
advanced: {
|
|
41747
41798
|
methods: /* @__PURE__ */ new Set(),
|
|
41748
41799
|
sources: /* @__PURE__ */ new Set(),
|
|
@@ -41758,6 +41809,15 @@ const createDefaultFilter = () => ({
|
|
|
41758
41809
|
maxDuration: ""
|
|
41759
41810
|
}
|
|
41760
41811
|
});
|
|
41812
|
+
const HTTP_METHODS = [
|
|
41813
|
+
"GET",
|
|
41814
|
+
"POST",
|
|
41815
|
+
"PUT",
|
|
41816
|
+
"PATCH",
|
|
41817
|
+
"DELETE",
|
|
41818
|
+
"HEAD"
|
|
41819
|
+
];
|
|
41820
|
+
const SOURCES = ["builtin", "nitro"];
|
|
41761
41821
|
const getTypeLabel = (type) => {
|
|
41762
41822
|
switch (type) {
|
|
41763
41823
|
case "http":
|
|
@@ -41793,7 +41853,7 @@ const getAdvancedFilterCount = (advanced) => {
|
|
|
41793
41853
|
].filter(Boolean).length;
|
|
41794
41854
|
};
|
|
41795
41855
|
const getActiveFilterCount = (filter) => {
|
|
41796
|
-
const typeFilterCount = filter.types.size
|
|
41856
|
+
const typeFilterCount = filter.types.size < DEFAULT_REQUEST_TYPES.length ? 1 : 0;
|
|
41797
41857
|
return typeFilterCount + getAdvancedFilterCount(filter.advanced);
|
|
41798
41858
|
};
|
|
41799
41859
|
const FilterField = ({
|
|
@@ -41904,7 +41964,7 @@ const FilterBar = ({ filter, onFilterChange }) => {
|
|
|
41904
41964
|
onFilterChange(createDefaultFilter());
|
|
41905
41965
|
};
|
|
41906
41966
|
const activeFilterCount = getActiveFilterCount(filter);
|
|
41907
|
-
const hasActiveFilters = filter.text !== "" || activeFilterCount > 0;
|
|
41967
|
+
const hasActiveFilters = filter.text.trim() !== "" || activeFilterCount > 0;
|
|
41908
41968
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 p-2 border-b border-gray-700 bg-gray-800", children: [
|
|
41909
41969
|
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
41910
41970
|
Input,
|
|
@@ -41940,7 +42000,7 @@ const FilterBar = ({ filter, onFilterChange }) => {
|
|
|
41940
42000
|
...getFloatingProps(),
|
|
41941
42001
|
children: [
|
|
41942
42002
|
/* @__PURE__ */ jsxRuntimeExports.jsx(FilterPanelLabel, { children: "Request Type" }),
|
|
41943
|
-
|
|
42003
|
+
DEFAULT_REQUEST_TYPES.map((type) => /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
41944
42004
|
FilterCheckbox,
|
|
41945
42005
|
{
|
|
41946
42006
|
checked: filter.types.has(type),
|
|
@@ -42077,14 +42137,692 @@ const FilterBar = ({ filter, onFilterChange }) => {
|
|
|
42077
42137
|
)
|
|
42078
42138
|
] });
|
|
42079
42139
|
};
|
|
42140
|
+
const TIMELINE_LAYOUT = {
|
|
42141
|
+
minVisibleBarPercent: 0.65,
|
|
42142
|
+
minRangeMs: 1e3,
|
|
42143
|
+
liveRefreshMs: 1e3,
|
|
42144
|
+
maxRenderedRequests: 1e3,
|
|
42145
|
+
laneCount: 8,
|
|
42146
|
+
laneHeightPx: 2,
|
|
42147
|
+
laneGapPx: 6,
|
|
42148
|
+
laneHitTargetHeightPx: 8,
|
|
42149
|
+
rulerHeightPx: 22,
|
|
42150
|
+
laneTopPx: 32,
|
|
42151
|
+
laneBottomPaddingPx: 18,
|
|
42152
|
+
tickTargetCount: 7,
|
|
42153
|
+
minTickLabelGapPercent: 6,
|
|
42154
|
+
rangePaddingRatio: 0.025,
|
|
42155
|
+
minRangePaddingMs: 25,
|
|
42156
|
+
streamingRequestMaxDurationMs: 5e3
|
|
42157
|
+
};
|
|
42158
|
+
const NICE_TICK_FACTORS = [1, 2, 2.5, 5, 10];
|
|
42159
|
+
const ACTIVE_HTTP_STATUSES = /* @__PURE__ */ new Set([
|
|
42160
|
+
"pending",
|
|
42161
|
+
"loading"
|
|
42162
|
+
]);
|
|
42163
|
+
const ACTIVE_WEBSOCKET_STATUSES = /* @__PURE__ */ new Set([
|
|
42164
|
+
"connecting",
|
|
42165
|
+
"open",
|
|
42166
|
+
"closing"
|
|
42167
|
+
]);
|
|
42168
|
+
const ACTIVE_SSE_STATUSES = /* @__PURE__ */ new Set([
|
|
42169
|
+
"connecting",
|
|
42170
|
+
"open"
|
|
42171
|
+
]);
|
|
42172
|
+
const clamp = (value, minimum, maximum) => {
|
|
42173
|
+
return Math.min(Math.max(value, minimum), maximum);
|
|
42174
|
+
};
|
|
42175
|
+
const getTimelineChartHeight = (layout = TIMELINE_LAYOUT) => {
|
|
42176
|
+
return layout.laneTopPx + layout.laneCount * layout.laneHeightPx + (layout.laneCount - 1) * layout.laneGapPx + layout.laneBottomPaddingPx;
|
|
42177
|
+
};
|
|
42178
|
+
const getTimelineLaneTop = (lane, layout = TIMELINE_LAYOUT) => {
|
|
42179
|
+
return lane * (layout.laneHeightPx + layout.laneGapPx) + layout.laneTopPx;
|
|
42180
|
+
};
|
|
42181
|
+
const getTimelineTrackTop = (lane, layout = TIMELINE_LAYOUT) => {
|
|
42182
|
+
const visualBarTop = getTimelineLaneTop(lane, layout);
|
|
42183
|
+
return visualBarTop - (layout.laneHitTargetHeightPx - layout.laneHeightPx) / 2;
|
|
42184
|
+
};
|
|
42185
|
+
const getTimelineBarTopOffset = (layout = TIMELINE_LAYOUT) => {
|
|
42186
|
+
return (layout.laneHitTargetHeightPx - layout.laneHeightPx) / 2;
|
|
42187
|
+
};
|
|
42188
|
+
const isRequestActive = (request) => {
|
|
42189
|
+
switch (request.type) {
|
|
42190
|
+
case "http":
|
|
42191
|
+
return ACTIVE_HTTP_STATUSES.has(request.status);
|
|
42192
|
+
case "websocket":
|
|
42193
|
+
return ACTIVE_WEBSOCKET_STATUSES.has(request.status);
|
|
42194
|
+
case "sse":
|
|
42195
|
+
return ACTIVE_SSE_STATUSES.has(request.status);
|
|
42196
|
+
}
|
|
42197
|
+
};
|
|
42198
|
+
const formatTimelineOffset = (milliseconds) => {
|
|
42199
|
+
if (milliseconds < 1e3) {
|
|
42200
|
+
return `${Math.round(milliseconds)} ms`;
|
|
42201
|
+
}
|
|
42202
|
+
if (milliseconds < 6e4) {
|
|
42203
|
+
return `${(milliseconds / 1e3).toFixed(milliseconds < 1e4 ? 1 : 0)} s`;
|
|
42204
|
+
}
|
|
42205
|
+
const totalSeconds = Math.round(milliseconds / 1e3);
|
|
42206
|
+
const minutes = Math.floor(totalSeconds / 60);
|
|
42207
|
+
const seconds = totalSeconds % 60;
|
|
42208
|
+
return `${minutes}m ${seconds.toString().padStart(2, "0")}s`;
|
|
42209
|
+
};
|
|
42210
|
+
const getRequestEndTime = (request, now) => {
|
|
42211
|
+
if (typeof request.duration === "number") {
|
|
42212
|
+
return request.timestamp + Math.max(request.duration, 0);
|
|
42213
|
+
}
|
|
42214
|
+
if (isRequestActive(request)) {
|
|
42215
|
+
return Math.max(now, request.timestamp);
|
|
42216
|
+
}
|
|
42217
|
+
return request.timestamp;
|
|
42218
|
+
};
|
|
42219
|
+
const getTimelineRequestEndTime = (request, now, layout = TIMELINE_LAYOUT) => {
|
|
42220
|
+
const endTime = getRequestEndTime(request, now);
|
|
42221
|
+
if (request.type !== "websocket" && request.type !== "sse") {
|
|
42222
|
+
return endTime;
|
|
42223
|
+
}
|
|
42224
|
+
return Math.min(
|
|
42225
|
+
endTime,
|
|
42226
|
+
request.timestamp + layout.streamingRequestMaxDurationMs
|
|
42227
|
+
);
|
|
42228
|
+
};
|
|
42229
|
+
const requestOverlapsTimelineRange = (request, range, now, layout = TIMELINE_LAYOUT) => {
|
|
42230
|
+
const rangeStart = Math.min(range.startTime, range.endTime);
|
|
42231
|
+
const rangeEnd = Math.max(range.startTime, range.endTime);
|
|
42232
|
+
const requestStart = request.timestamp;
|
|
42233
|
+
const requestEnd = getTimelineRequestEndTime(request, now, layout);
|
|
42234
|
+
return requestStart <= rangeEnd && requestEnd >= rangeStart;
|
|
42235
|
+
};
|
|
42236
|
+
const getNiceTickStep = (rangeDuration, targetTickCount) => {
|
|
42237
|
+
const targetStep = rangeDuration / targetTickCount;
|
|
42238
|
+
const exponent = Math.floor(Math.log10(targetStep));
|
|
42239
|
+
const magnitude = 10 ** exponent;
|
|
42240
|
+
const normalizedStep = targetStep / magnitude;
|
|
42241
|
+
const factor = NICE_TICK_FACTORS.find((candidate) => candidate >= normalizedStep) ?? NICE_TICK_FACTORS[NICE_TICK_FACTORS.length - 1];
|
|
42242
|
+
return factor * magnitude;
|
|
42243
|
+
};
|
|
42244
|
+
const getTimelineTicks = (rangeDuration, layout = TIMELINE_LAYOUT) => {
|
|
42245
|
+
const step = getNiceTickStep(rangeDuration, layout.tickTargetCount);
|
|
42246
|
+
const ticks = [];
|
|
42247
|
+
for (let value = 0; value <= rangeDuration; value += step) {
|
|
42248
|
+
ticks.push({
|
|
42249
|
+
label: formatTimelineOffset(value),
|
|
42250
|
+
offsetPercent: value / rangeDuration * 100
|
|
42251
|
+
});
|
|
42252
|
+
}
|
|
42253
|
+
if (ticks.length === 0 || ticks[ticks.length - 1].offsetPercent < 100 - Number.EPSILON) {
|
|
42254
|
+
const finalTick = {
|
|
42255
|
+
label: formatTimelineOffset(rangeDuration),
|
|
42256
|
+
offsetPercent: 100
|
|
42257
|
+
};
|
|
42258
|
+
const previousTick = ticks[ticks.length - 1];
|
|
42259
|
+
if (!previousTick || finalTick.label !== previousTick.label && finalTick.offsetPercent - previousTick.offsetPercent >= layout.minTickLabelGapPercent) {
|
|
42260
|
+
ticks.push(finalTick);
|
|
42261
|
+
}
|
|
42262
|
+
}
|
|
42263
|
+
return ticks;
|
|
42264
|
+
};
|
|
42265
|
+
const getTimelineBounds = (requests, now, layout) => {
|
|
42266
|
+
return requests.reduce(
|
|
42267
|
+
(result, request) => {
|
|
42268
|
+
const endTime = getTimelineRequestEndTime(request, now, layout);
|
|
42269
|
+
return {
|
|
42270
|
+
start: Math.min(result.start, request.timestamp),
|
|
42271
|
+
end: Math.max(result.end, endTime)
|
|
42272
|
+
};
|
|
42273
|
+
},
|
|
42274
|
+
{
|
|
42275
|
+
start: Number.POSITIVE_INFINITY,
|
|
42276
|
+
end: Number.NEGATIVE_INFINITY
|
|
42277
|
+
}
|
|
42278
|
+
);
|
|
42279
|
+
};
|
|
42280
|
+
const getEarliestLaneIndex = (laneEndTimes) => {
|
|
42281
|
+
return laneEndTimes.reduce((earliestIndex, laneEndTime, index2) => {
|
|
42282
|
+
return laneEndTime < laneEndTimes[earliestIndex] ? index2 : earliestIndex;
|
|
42283
|
+
}, 0);
|
|
42284
|
+
};
|
|
42285
|
+
const getRenderableRequests = (requests, layout) => {
|
|
42286
|
+
if (requests.length <= layout.maxRenderedRequests) {
|
|
42287
|
+
return requests;
|
|
42288
|
+
}
|
|
42289
|
+
return [...requests].sort((a, b2) => b2.timestamp - a.timestamp).slice(0, layout.maxRenderedRequests);
|
|
42290
|
+
};
|
|
42291
|
+
const getTimelineModel = (requests, now, layout = TIMELINE_LAYOUT) => {
|
|
42292
|
+
const renderableRequests = getRenderableRequests(requests, layout);
|
|
42293
|
+
const hiddenRequestCount = requests.length - renderableRequests.length;
|
|
42294
|
+
if (renderableRequests.length === 0) {
|
|
42295
|
+
return {
|
|
42296
|
+
rows: [],
|
|
42297
|
+
ticks: getTimelineTicks(layout.minRangeMs, layout),
|
|
42298
|
+
rangeStart: 0,
|
|
42299
|
+
rangeDuration: layout.minRangeMs,
|
|
42300
|
+
chartHeight: getTimelineChartHeight(layout),
|
|
42301
|
+
totalRequestCount: requests.length,
|
|
42302
|
+
hiddenRequestCount
|
|
42303
|
+
};
|
|
42304
|
+
}
|
|
42305
|
+
const bounds = getTimelineBounds(renderableRequests, now, layout);
|
|
42306
|
+
const rawRangeDuration = Math.max(
|
|
42307
|
+
bounds.end - bounds.start,
|
|
42308
|
+
layout.minRangeMs
|
|
42309
|
+
);
|
|
42310
|
+
const rangePadding = Math.max(
|
|
42311
|
+
rawRangeDuration * layout.rangePaddingRatio,
|
|
42312
|
+
layout.minRangePaddingMs
|
|
42313
|
+
);
|
|
42314
|
+
const rangeStart = bounds.start - rangePadding;
|
|
42315
|
+
const rangeDuration = rawRangeDuration + rangePadding * 2;
|
|
42316
|
+
const laneEndTimes = Array.from({ length: layout.laneCount }, () => 0);
|
|
42317
|
+
const rows = [...renderableRequests].sort((a, b2) => a.timestamp - b2.timestamp).map((request) => {
|
|
42318
|
+
const startTime = request.timestamp;
|
|
42319
|
+
const endTime = getTimelineRequestEndTime(request, now, layout);
|
|
42320
|
+
const duration = Math.max(endTime - startTime, 0);
|
|
42321
|
+
const offsetPercent = clamp(
|
|
42322
|
+
(startTime - rangeStart) / rangeDuration * 100,
|
|
42323
|
+
0,
|
|
42324
|
+
100 - layout.minVisibleBarPercent
|
|
42325
|
+
);
|
|
42326
|
+
const widthPercent = Math.min(
|
|
42327
|
+
Math.max(duration / rangeDuration * 100, layout.minVisibleBarPercent),
|
|
42328
|
+
100 - offsetPercent
|
|
42329
|
+
);
|
|
42330
|
+
const ttfb = clamp(request.ttfb ?? 0, 0, duration);
|
|
42331
|
+
const ttfbPercent = duration === 0 ? 0 : ttfb / duration * 100;
|
|
42332
|
+
const receivePercent = Math.max(100 - ttfbPercent, 0);
|
|
42333
|
+
const availableLane = laneEndTimes.findIndex(
|
|
42334
|
+
(laneEndTime) => laneEndTime <= startTime
|
|
42335
|
+
);
|
|
42336
|
+
const isOverflowingLane = availableLane === -1;
|
|
42337
|
+
const lane = isOverflowingLane ? getEarliestLaneIndex(laneEndTimes) : availableLane;
|
|
42338
|
+
laneEndTimes[lane] = Math.max(laneEndTimes[lane], endTime);
|
|
42339
|
+
return {
|
|
42340
|
+
request,
|
|
42341
|
+
offsetPercent,
|
|
42342
|
+
widthPercent,
|
|
42343
|
+
duration,
|
|
42344
|
+
ttfbPercent,
|
|
42345
|
+
receivePercent,
|
|
42346
|
+
isActive: isRequestActive(request),
|
|
42347
|
+
lane,
|
|
42348
|
+
isOverflowingLane
|
|
42349
|
+
};
|
|
42350
|
+
});
|
|
42351
|
+
return {
|
|
42352
|
+
rows,
|
|
42353
|
+
ticks: getTimelineTicks(rangeDuration, layout),
|
|
42354
|
+
rangeStart,
|
|
42355
|
+
rangeDuration,
|
|
42356
|
+
chartHeight: getTimelineChartHeight(layout),
|
|
42357
|
+
totalRequestCount: requests.length,
|
|
42358
|
+
hiddenRequestCount
|
|
42359
|
+
};
|
|
42360
|
+
};
|
|
42361
|
+
const REQUEST_TIMELINE_COLORS = {
|
|
42362
|
+
error: "bg-red-400",
|
|
42363
|
+
primary: "bg-gray-400",
|
|
42364
|
+
active: "bg-gray-500",
|
|
42365
|
+
httpTtfb: "bg-gray-200"
|
|
42366
|
+
};
|
|
42367
|
+
const getPrimaryBarClassName = (request) => {
|
|
42368
|
+
if (request.status === "failed" || request.status === "error") {
|
|
42369
|
+
return REQUEST_TIMELINE_COLORS.error;
|
|
42370
|
+
}
|
|
42371
|
+
return REQUEST_TIMELINE_COLORS.primary;
|
|
42372
|
+
};
|
|
42373
|
+
const getStyle = (offsetPercent, widthPercent) => ({
|
|
42374
|
+
left: `${offsetPercent}%`,
|
|
42375
|
+
width: `${widthPercent}%`
|
|
42376
|
+
});
|
|
42377
|
+
const GridLines = ({ ticks }) => {
|
|
42378
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "pointer-events-none absolute inset-0", children: ticks.map((tick) => /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
42379
|
+
"div",
|
|
42380
|
+
{
|
|
42381
|
+
className: "absolute inset-y-0 border-l border-gray-800",
|
|
42382
|
+
style: { left: `${tick.offsetPercent}%` }
|
|
42383
|
+
},
|
|
42384
|
+
`${tick.label}-${tick.offsetPercent}`
|
|
42385
|
+
)) });
|
|
42386
|
+
};
|
|
42387
|
+
const getTickLabelStyle = (tick) => {
|
|
42388
|
+
if (tick.offsetPercent === 0) {
|
|
42389
|
+
return {
|
|
42390
|
+
left: 4
|
|
42391
|
+
};
|
|
42392
|
+
}
|
|
42393
|
+
if (tick.offsetPercent === 100) {
|
|
42394
|
+
return {
|
|
42395
|
+
right: 4
|
|
42396
|
+
};
|
|
42397
|
+
}
|
|
42398
|
+
return {
|
|
42399
|
+
left: `${tick.offsetPercent}%`
|
|
42400
|
+
};
|
|
42401
|
+
};
|
|
42402
|
+
const TimelineTrack = ({
|
|
42403
|
+
row,
|
|
42404
|
+
isSelected,
|
|
42405
|
+
onSelect,
|
|
42406
|
+
shouldSuppressSelect
|
|
42407
|
+
}) => {
|
|
42408
|
+
const primaryBarClassName = row.isActive ? REQUEST_TIMELINE_COLORS.active : getPrimaryBarClassName(row.request);
|
|
42409
|
+
const isSplitHttpBar = row.request.type === "http" && row.ttfbPercent > 0 && row.receivePercent > 0;
|
|
42410
|
+
const trackTop = getTimelineTrackTop(row.lane);
|
|
42411
|
+
const barTop = getTimelineBarTopOffset();
|
|
42412
|
+
const positionStyle = {
|
|
42413
|
+
...getStyle(row.offsetPercent, row.widthPercent),
|
|
42414
|
+
top: trackTop
|
|
42415
|
+
};
|
|
42416
|
+
const durationLabel = row.isActive ? `${formatTimelineOffset(row.duration)}+` : formatTimelineOffset(row.duration);
|
|
42417
|
+
const label = `${row.request.method} ${row.request.name} - ${durationLabel}`;
|
|
42418
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
42419
|
+
"button",
|
|
42420
|
+
{
|
|
42421
|
+
type: "button",
|
|
42422
|
+
"aria-label": label,
|
|
42423
|
+
title: label,
|
|
42424
|
+
"data-timeline-track": "true",
|
|
42425
|
+
className: "absolute rounded-sm text-left transition-opacity hover:opacity-80",
|
|
42426
|
+
style: {
|
|
42427
|
+
...positionStyle,
|
|
42428
|
+
height: TIMELINE_LAYOUT.laneHitTargetHeightPx
|
|
42429
|
+
},
|
|
42430
|
+
onClick: (event) => {
|
|
42431
|
+
if (shouldSuppressSelect()) {
|
|
42432
|
+
event.preventDefault();
|
|
42433
|
+
event.stopPropagation();
|
|
42434
|
+
return;
|
|
42435
|
+
}
|
|
42436
|
+
onSelect(row.request.id);
|
|
42437
|
+
},
|
|
42438
|
+
children: isSplitHttpBar ? /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
42439
|
+
"div",
|
|
42440
|
+
{
|
|
42441
|
+
className: `absolute flex w-full overflow-hidden rounded-sm ${isSelected ? "ring-1 ring-blue-300 ring-offset-1 ring-offset-gray-950" : ""}`,
|
|
42442
|
+
style: {
|
|
42443
|
+
top: barTop,
|
|
42444
|
+
height: TIMELINE_LAYOUT.laneHeightPx
|
|
42445
|
+
},
|
|
42446
|
+
children: [
|
|
42447
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
42448
|
+
"div",
|
|
42449
|
+
{
|
|
42450
|
+
className: `h-full ${REQUEST_TIMELINE_COLORS.httpTtfb}`,
|
|
42451
|
+
style: { width: `${row.ttfbPercent}%` }
|
|
42452
|
+
}
|
|
42453
|
+
),
|
|
42454
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
42455
|
+
"div",
|
|
42456
|
+
{
|
|
42457
|
+
className: `h-full ${REQUEST_TIMELINE_COLORS.primary}`,
|
|
42458
|
+
style: { width: `${row.receivePercent}%` }
|
|
42459
|
+
}
|
|
42460
|
+
)
|
|
42461
|
+
]
|
|
42462
|
+
}
|
|
42463
|
+
) : /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
42464
|
+
"div",
|
|
42465
|
+
{
|
|
42466
|
+
className: `absolute w-full rounded-sm ${primaryBarClassName} ${isSelected ? "ring-1 ring-blue-300 ring-offset-1 ring-offset-gray-950" : ""}`,
|
|
42467
|
+
style: {
|
|
42468
|
+
top: barTop,
|
|
42469
|
+
height: TIMELINE_LAYOUT.laneHeightPx
|
|
42470
|
+
}
|
|
42471
|
+
}
|
|
42472
|
+
)
|
|
42473
|
+
}
|
|
42474
|
+
);
|
|
42475
|
+
};
|
|
42476
|
+
const clampPercent = (value) => Math.min(Math.max(value, 0), 100);
|
|
42477
|
+
const getPointerPercent = (event, element) => {
|
|
42478
|
+
const rect = element.getBoundingClientRect();
|
|
42479
|
+
if (rect.width === 0) {
|
|
42480
|
+
return 0;
|
|
42481
|
+
}
|
|
42482
|
+
return clampPercent((event.clientX - rect.left) / rect.width * 100);
|
|
42483
|
+
};
|
|
42484
|
+
const getSelectionStyle = (range, timeline) => {
|
|
42485
|
+
const startPercent = clampPercent(
|
|
42486
|
+
(range.startTime - timeline.rangeStart) / timeline.rangeDuration * 100
|
|
42487
|
+
);
|
|
42488
|
+
const endPercent = clampPercent(
|
|
42489
|
+
(range.endTime - timeline.rangeStart) / timeline.rangeDuration * 100
|
|
42490
|
+
);
|
|
42491
|
+
const left2 = Math.min(startPercent, endPercent);
|
|
42492
|
+
const width = Math.abs(endPercent - startPercent);
|
|
42493
|
+
return {
|
|
42494
|
+
left: `${left2}%`,
|
|
42495
|
+
width: `${width}%`,
|
|
42496
|
+
top: TIMELINE_LAYOUT.rulerHeightPx
|
|
42497
|
+
};
|
|
42498
|
+
};
|
|
42499
|
+
const getDraftSelectionStyle = (draft) => {
|
|
42500
|
+
const left2 = Math.min(draft.anchorPercent, draft.currentPercent);
|
|
42501
|
+
const width = Math.abs(draft.currentPercent - draft.anchorPercent);
|
|
42502
|
+
return {
|
|
42503
|
+
left: `${left2}%`,
|
|
42504
|
+
width: `${width}%`,
|
|
42505
|
+
top: TIMELINE_LAYOUT.rulerHeightPx
|
|
42506
|
+
};
|
|
42507
|
+
};
|
|
42508
|
+
const NetworkTimeline = ({
|
|
42509
|
+
requests,
|
|
42510
|
+
selection,
|
|
42511
|
+
filteredRequestCount,
|
|
42512
|
+
onSelectionChange
|
|
42513
|
+
}) => {
|
|
42514
|
+
const actions = useNetworkActivityActions();
|
|
42515
|
+
const selectedRequestId = useSelectedRequestId();
|
|
42516
|
+
const [now, setNow] = reactExports.useState(() => Date.now());
|
|
42517
|
+
const [draftSelection, setDraftSelection] = reactExports.useState(
|
|
42518
|
+
null
|
|
42519
|
+
);
|
|
42520
|
+
const chartRef = reactExports.useRef(null);
|
|
42521
|
+
const suppressTrackClickRef = reactExports.useRef(false);
|
|
42522
|
+
const hasActiveRequests = requests.some(isRequestActive);
|
|
42523
|
+
reactExports.useEffect(() => {
|
|
42524
|
+
if (!hasActiveRequests) {
|
|
42525
|
+
return;
|
|
42526
|
+
}
|
|
42527
|
+
const interval = window.setInterval(() => {
|
|
42528
|
+
setNow(Date.now());
|
|
42529
|
+
}, TIMELINE_LAYOUT.liveRefreshMs);
|
|
42530
|
+
return () => window.clearInterval(interval);
|
|
42531
|
+
}, [hasActiveRequests]);
|
|
42532
|
+
const timeline = reactExports.useMemo(() => {
|
|
42533
|
+
return getTimelineModel(requests, now);
|
|
42534
|
+
}, [requests, now]);
|
|
42535
|
+
const onRequestSelect = (requestId) => {
|
|
42536
|
+
actions.setSelectedRequest(requestId);
|
|
42537
|
+
};
|
|
42538
|
+
const onPointerDown = (event) => {
|
|
42539
|
+
if (event.button !== 0 || requests.length === 0) {
|
|
42540
|
+
return;
|
|
42541
|
+
}
|
|
42542
|
+
const chartElement = chartRef.current;
|
|
42543
|
+
if (!chartElement) {
|
|
42544
|
+
return;
|
|
42545
|
+
}
|
|
42546
|
+
const percent = getPointerPercent(event, chartElement);
|
|
42547
|
+
const target = event.target;
|
|
42548
|
+
const startedOnTrack = target instanceof Element && target.closest('[data-timeline-track="true"]') !== null;
|
|
42549
|
+
setDraftSelection({
|
|
42550
|
+
anchorPercent: percent,
|
|
42551
|
+
currentPercent: percent,
|
|
42552
|
+
startedOnTrack
|
|
42553
|
+
});
|
|
42554
|
+
chartElement.setPointerCapture(event.pointerId);
|
|
42555
|
+
};
|
|
42556
|
+
const onPointerMove = (event) => {
|
|
42557
|
+
if (!draftSelection) {
|
|
42558
|
+
return;
|
|
42559
|
+
}
|
|
42560
|
+
const chartElement = chartRef.current;
|
|
42561
|
+
if (!chartElement) {
|
|
42562
|
+
return;
|
|
42563
|
+
}
|
|
42564
|
+
event.preventDefault();
|
|
42565
|
+
const percent = getPointerPercent(event, chartElement);
|
|
42566
|
+
setDraftSelection(
|
|
42567
|
+
(current) => current ? { ...current, currentPercent: percent } : current
|
|
42568
|
+
);
|
|
42569
|
+
};
|
|
42570
|
+
const onPointerUp = (event) => {
|
|
42571
|
+
if (!draftSelection) {
|
|
42572
|
+
return;
|
|
42573
|
+
}
|
|
42574
|
+
const chartElement = chartRef.current;
|
|
42575
|
+
const currentPercent = chartElement ? getPointerPercent(event, chartElement) : draftSelection.currentPercent;
|
|
42576
|
+
const distance = Math.abs(currentPercent - draftSelection.anchorPercent);
|
|
42577
|
+
if (distance > 1) {
|
|
42578
|
+
const startOffset = Math.min(draftSelection.anchorPercent, currentPercent) / 100 * timeline.rangeDuration;
|
|
42579
|
+
const endOffset = Math.max(draftSelection.anchorPercent, currentPercent) / 100 * timeline.rangeDuration;
|
|
42580
|
+
onSelectionChange({
|
|
42581
|
+
startTime: timeline.rangeStart + startOffset,
|
|
42582
|
+
endTime: timeline.rangeStart + endOffset
|
|
42583
|
+
});
|
|
42584
|
+
suppressTrackClickRef.current = true;
|
|
42585
|
+
window.setTimeout(() => {
|
|
42586
|
+
suppressTrackClickRef.current = false;
|
|
42587
|
+
}, 0);
|
|
42588
|
+
} else if (!draftSelection.startedOnTrack) {
|
|
42589
|
+
onSelectionChange(null);
|
|
42590
|
+
}
|
|
42591
|
+
setDraftSelection(null);
|
|
42592
|
+
if (chartElement?.hasPointerCapture(event.pointerId)) {
|
|
42593
|
+
chartElement.releasePointerCapture(event.pointerId);
|
|
42594
|
+
}
|
|
42595
|
+
};
|
|
42596
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "border-b border-gray-700 bg-gray-900 p-1.5", children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
42597
|
+
"div",
|
|
42598
|
+
{
|
|
42599
|
+
ref: chartRef,
|
|
42600
|
+
className: "relative overflow-hidden border border-gray-800 bg-gray-950",
|
|
42601
|
+
style: { height: timeline.chartHeight },
|
|
42602
|
+
onPointerDown,
|
|
42603
|
+
onPointerMove,
|
|
42604
|
+
onPointerUp,
|
|
42605
|
+
children: [
|
|
42606
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(GridLines, { ticks: timeline.ticks }),
|
|
42607
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
42608
|
+
"div",
|
|
42609
|
+
{
|
|
42610
|
+
className: "pointer-events-none absolute inset-x-0 border-b border-gray-800",
|
|
42611
|
+
style: { top: TIMELINE_LAYOUT.rulerHeightPx }
|
|
42612
|
+
}
|
|
42613
|
+
),
|
|
42614
|
+
timeline.ticks.map((tick) => /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
42615
|
+
"div",
|
|
42616
|
+
{
|
|
42617
|
+
className: "absolute top-1 whitespace-nowrap tabular-nums text-xs text-gray-200",
|
|
42618
|
+
style: getTickLabelStyle(tick),
|
|
42619
|
+
children: tick.label
|
|
42620
|
+
},
|
|
42621
|
+
`${tick.label}-${tick.offsetPercent}`
|
|
42622
|
+
)),
|
|
42623
|
+
selection && /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
42624
|
+
"div",
|
|
42625
|
+
{
|
|
42626
|
+
className: "pointer-events-none absolute bottom-0 border-x border-blue-300/70 bg-blue-400/10",
|
|
42627
|
+
style: getSelectionStyle(selection, timeline)
|
|
42628
|
+
}
|
|
42629
|
+
),
|
|
42630
|
+
draftSelection && /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
42631
|
+
"div",
|
|
42632
|
+
{
|
|
42633
|
+
className: "pointer-events-none absolute bottom-0 border-x border-blue-300/70 bg-blue-400/15",
|
|
42634
|
+
style: getDraftSelectionStyle(draftSelection)
|
|
42635
|
+
}
|
|
42636
|
+
),
|
|
42637
|
+
timeline.rows.map((row) => /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
42638
|
+
TimelineTrack,
|
|
42639
|
+
{
|
|
42640
|
+
row,
|
|
42641
|
+
isSelected: selectedRequestId === row.request.id,
|
|
42642
|
+
onSelect: onRequestSelect,
|
|
42643
|
+
shouldSuppressSelect: () => suppressTrackClickRef.current
|
|
42644
|
+
},
|
|
42645
|
+
row.request.id
|
|
42646
|
+
)),
|
|
42647
|
+
selection && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "absolute bottom-1 right-1 flex items-center gap-1 rounded border border-gray-700 bg-gray-900/95 px-1.5 py-0.5 text-xs text-gray-400", children: [
|
|
42648
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
|
|
42649
|
+
filteredRequestCount,
|
|
42650
|
+
" in range"
|
|
42651
|
+
] }),
|
|
42652
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
42653
|
+
"button",
|
|
42654
|
+
{
|
|
42655
|
+
type: "button",
|
|
42656
|
+
title: "Clear timeline selection",
|
|
42657
|
+
"aria-label": "Clear timeline selection",
|
|
42658
|
+
className: "rounded p-0.5 text-gray-400 hover:bg-gray-800 hover:text-gray-100",
|
|
42659
|
+
onClick: () => onSelectionChange(null),
|
|
42660
|
+
children: /* @__PURE__ */ jsxRuntimeExports.jsx(X$1, { className: "h-3 w-3" })
|
|
42661
|
+
}
|
|
42662
|
+
)
|
|
42663
|
+
] }),
|
|
42664
|
+
timeline.hiddenRequestCount > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "absolute bottom-1 left-1 rounded border border-gray-700 bg-gray-900/95 px-1.5 py-0.5 text-xs text-gray-400", children: [
|
|
42665
|
+
"Showing latest ",
|
|
42666
|
+
timeline.rows.length,
|
|
42667
|
+
" of",
|
|
42668
|
+
" ",
|
|
42669
|
+
timeline.totalRequestCount
|
|
42670
|
+
] })
|
|
42671
|
+
]
|
|
42672
|
+
}
|
|
42673
|
+
) });
|
|
42674
|
+
};
|
|
42675
|
+
const parseThreshold = (value) => {
|
|
42676
|
+
const normalizedValue = value.trim();
|
|
42677
|
+
if (!normalizedValue) {
|
|
42678
|
+
return null;
|
|
42679
|
+
}
|
|
42680
|
+
const parsedValue = Number(normalizedValue);
|
|
42681
|
+
return Number.isFinite(parsedValue) ? parsedValue : null;
|
|
42682
|
+
};
|
|
42683
|
+
const matchesStatusFilter = (statusCode, statusFilter) => {
|
|
42684
|
+
const normalizedFilter = statusFilter.trim().toLowerCase();
|
|
42685
|
+
if (!normalizedFilter) {
|
|
42686
|
+
return true;
|
|
42687
|
+
}
|
|
42688
|
+
if (statusCode === void 0) {
|
|
42689
|
+
return false;
|
|
42690
|
+
}
|
|
42691
|
+
const statusRangeMatch = normalizedFilter.match(/^(\d{3})\s*-\s*(\d{3})$/);
|
|
42692
|
+
if (statusRangeMatch) {
|
|
42693
|
+
const min2 = Number(statusRangeMatch[1]);
|
|
42694
|
+
const max2 = Number(statusRangeMatch[2]);
|
|
42695
|
+
return statusCode >= min2 && statusCode <= max2;
|
|
42696
|
+
}
|
|
42697
|
+
const statusClassMatch = normalizedFilter.match(/^([1-5])xx$/);
|
|
42698
|
+
if (statusClassMatch) {
|
|
42699
|
+
return Math.floor(statusCode / 100) === Number(statusClassMatch[1]);
|
|
42700
|
+
}
|
|
42701
|
+
const comparisonMatch = normalizedFilter.match(/^(>=|<=|>|<)\s*(\d{3})$/);
|
|
42702
|
+
if (comparisonMatch) {
|
|
42703
|
+
const value = Number(comparisonMatch[2]);
|
|
42704
|
+
switch (comparisonMatch[1]) {
|
|
42705
|
+
case ">=":
|
|
42706
|
+
return statusCode >= value;
|
|
42707
|
+
case "<=":
|
|
42708
|
+
return statusCode <= value;
|
|
42709
|
+
case ">":
|
|
42710
|
+
return statusCode > value;
|
|
42711
|
+
case "<":
|
|
42712
|
+
return statusCode < value;
|
|
42713
|
+
}
|
|
42714
|
+
}
|
|
42715
|
+
return statusCode === Number(normalizedFilter);
|
|
42716
|
+
};
|
|
42717
|
+
const isInFlightStatus = (status) => {
|
|
42718
|
+
return ["pending", "loading", "connecting", "open"].includes(status);
|
|
42719
|
+
};
|
|
42720
|
+
const isFailedStatus = (status) => {
|
|
42721
|
+
return ["failed", "error"].includes(status);
|
|
42722
|
+
};
|
|
42723
|
+
const isHttpMethod = (method) => method !== "WS" && method !== "SSE";
|
|
42724
|
+
const extractDomainAndPath = (url) => {
|
|
42725
|
+
try {
|
|
42726
|
+
const { hostname, pathname, search, hash: hash2, port } = new URL(url);
|
|
42727
|
+
return {
|
|
42728
|
+
domain: `${hostname}${port ? `:${port}` : ""}`,
|
|
42729
|
+
path: `${pathname}${search}${hash2}`
|
|
42730
|
+
};
|
|
42731
|
+
} catch {
|
|
42732
|
+
return { domain: "unknown", path: url };
|
|
42733
|
+
}
|
|
42734
|
+
};
|
|
42735
|
+
const matchesRequestFilter = (request, filter, options = {}) => {
|
|
42736
|
+
if (filter.types.size > 0 && !filter.types.has(request.type)) {
|
|
42737
|
+
return false;
|
|
42738
|
+
}
|
|
42739
|
+
if (filter.advanced.methods.size > 0 && (!isHttpMethod(request.method) || !filter.advanced.methods.has(request.method))) {
|
|
42740
|
+
return false;
|
|
42741
|
+
}
|
|
42742
|
+
if (filter.advanced.sources.size > 0 && (!request.source || !filter.advanced.sources.has(request.source))) {
|
|
42743
|
+
return false;
|
|
42744
|
+
}
|
|
42745
|
+
if (!matchesStatusFilter(request.httpStatus, filter.advanced.status)) {
|
|
42746
|
+
return false;
|
|
42747
|
+
}
|
|
42748
|
+
const { domain, path } = extractDomainAndPath(request.name);
|
|
42749
|
+
const domainFilter = filter.advanced.domain.trim().toLowerCase();
|
|
42750
|
+
if (domainFilter && !domain.toLowerCase().includes(domainFilter)) {
|
|
42751
|
+
return false;
|
|
42752
|
+
}
|
|
42753
|
+
const contentTypeFilter = filter.advanced.contentType.trim().toLowerCase();
|
|
42754
|
+
if (contentTypeFilter && !request.contentType?.toLowerCase().includes(contentTypeFilter)) {
|
|
42755
|
+
return false;
|
|
42756
|
+
}
|
|
42757
|
+
if (filter.advanced.failedOnly && !isFailedStatus(request.status)) {
|
|
42758
|
+
return false;
|
|
42759
|
+
}
|
|
42760
|
+
if (filter.advanced.inFlightOnly && !isInFlightStatus(request.status)) {
|
|
42761
|
+
return false;
|
|
42762
|
+
}
|
|
42763
|
+
if (filter.advanced.overriddenOnly && !options.hasOverride) {
|
|
42764
|
+
return false;
|
|
42765
|
+
}
|
|
42766
|
+
const minSize = parseThreshold(filter.advanced.minSize);
|
|
42767
|
+
if (minSize !== null && (request.size === null || request.size < minSize)) {
|
|
42768
|
+
return false;
|
|
42769
|
+
}
|
|
42770
|
+
const maxSize = parseThreshold(filter.advanced.maxSize);
|
|
42771
|
+
if (maxSize !== null && (request.size === null || request.size > maxSize)) {
|
|
42772
|
+
return false;
|
|
42773
|
+
}
|
|
42774
|
+
const duration = request.duration || 0;
|
|
42775
|
+
const minDuration = parseThreshold(filter.advanced.minDuration);
|
|
42776
|
+
if (minDuration !== null && duration < minDuration) {
|
|
42777
|
+
return false;
|
|
42778
|
+
}
|
|
42779
|
+
const maxDuration = parseThreshold(filter.advanced.maxDuration);
|
|
42780
|
+
if (maxDuration !== null && duration > maxDuration) {
|
|
42781
|
+
return false;
|
|
42782
|
+
}
|
|
42783
|
+
const searchText = filter.text.trim().toLowerCase();
|
|
42784
|
+
if (!searchText) {
|
|
42785
|
+
return true;
|
|
42786
|
+
}
|
|
42787
|
+
const searchableFields = [
|
|
42788
|
+
request.name,
|
|
42789
|
+
request.method,
|
|
42790
|
+
request.status,
|
|
42791
|
+
request.httpStatus,
|
|
42792
|
+
request.source,
|
|
42793
|
+
request.type,
|
|
42794
|
+
request.contentType,
|
|
42795
|
+
domain,
|
|
42796
|
+
path
|
|
42797
|
+
].filter((value) => value !== void 0 && value !== null).join(" ").toLowerCase();
|
|
42798
|
+
return searchableFields.includes(searchText);
|
|
42799
|
+
};
|
|
42080
42800
|
const InspectorView = ({ client: client2 }) => {
|
|
42081
42801
|
const actions = useNetworkActivityActions();
|
|
42082
42802
|
const clientManagement = useNetworkActivityClientManagement();
|
|
42083
42803
|
const hasSelectedRequest = useHasSelectedRequest();
|
|
42084
42804
|
const overrides = useOverrides();
|
|
42805
|
+
const processedRequests = useProcessedRequests();
|
|
42085
42806
|
const [filter, setFilter] = reactExports.useState(
|
|
42086
42807
|
() => createDefaultFilter()
|
|
42087
42808
|
);
|
|
42809
|
+
const [timelineSelection, setTimelineSelection] = reactExports.useState(null);
|
|
42810
|
+
const filteredRequests = reactExports.useMemo(() => {
|
|
42811
|
+
return processedRequests.filter(
|
|
42812
|
+
(request) => matchesRequestFilter(request, filter, {
|
|
42813
|
+
hasOverride: overrides.has(request.name)
|
|
42814
|
+
})
|
|
42815
|
+
);
|
|
42816
|
+
}, [processedRequests, filter, overrides]);
|
|
42817
|
+
const visibleRequests = reactExports.useMemo(() => {
|
|
42818
|
+
if (!timelineSelection) {
|
|
42819
|
+
return filteredRequests;
|
|
42820
|
+
}
|
|
42821
|
+
const now = Date.now();
|
|
42822
|
+
return filteredRequests.filter(
|
|
42823
|
+
(request) => requestOverlapsTimelineRange(request, timelineSelection, now)
|
|
42824
|
+
);
|
|
42825
|
+
}, [filteredRequests, timelineSelection]);
|
|
42088
42826
|
reactExports.useEffect(() => {
|
|
42089
42827
|
if (!client2) {
|
|
42090
42828
|
return;
|
|
@@ -42103,11 +42841,22 @@ const InspectorView = ({ client: client2 }) => {
|
|
|
42103
42841
|
/* @__PURE__ */ jsxRuntimeExports.jsx(Toolbar, {}),
|
|
42104
42842
|
/* @__PURE__ */ jsxRuntimeExports.jsx(FilterBar, { filter, onFilterChange: setFilter }),
|
|
42105
42843
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-1 overflow-hidden", children: [
|
|
42106
|
-
/* @__PURE__ */ jsxRuntimeExports.
|
|
42844
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
42107
42845
|
"div",
|
|
42108
42846
|
{
|
|
42109
42847
|
className: `flex flex-col ${hasSelectedRequest ? "w-1/2" : "w-full"} border-r border-gray-700 overflow-hidden`,
|
|
42110
|
-
children:
|
|
42848
|
+
children: [
|
|
42849
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
42850
|
+
NetworkTimeline,
|
|
42851
|
+
{
|
|
42852
|
+
requests: filteredRequests,
|
|
42853
|
+
selection: timelineSelection,
|
|
42854
|
+
filteredRequestCount: visibleRequests.length,
|
|
42855
|
+
onSelectionChange: setTimelineSelection
|
|
42856
|
+
}
|
|
42857
|
+
),
|
|
42858
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(RequestList, { requests: visibleRequests })
|
|
42859
|
+
]
|
|
42111
42860
|
}
|
|
42112
42861
|
),
|
|
42113
42862
|
hasSelectedRequest && /* @__PURE__ */ jsxRuntimeExports.jsx(SidePanel, {})
|