diff --git a/CHANGELOG.md b/CHANGELOG.md
index f88bb55a..918e17c1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,15 @@
English | [简体中文](./CHANGELOG_CN.md)
+## 3.14.0 (2022-03-23)
+
+- `Feat(Core)` Add new option `pluginOrder` to adjust the order of built-in and custom plugins, see [Public Properties & Methods](./doc/public_properties_methods.md).
+- `Feat(Core)` Panel will auto scroll to previous position when switching plugin panel.
+- `Feat(Network)` Add response size.
+- `Feat(Network)` Add support for `transfer-encoding: chunked`, now streaming response can be recorded.
+- `Feat(Network)` Improve rendering performance of large Response data by cropping the displayed response content.
+- `Refactor(Network)` Now network records will be more accurate by using Proxy to prevent `XMLHttpRequest | fetch` overwriting by other request libraries (like Axios).
+
+
## 3.13.0 (2022-03-15)
- `Feat(Log)` Add new option `log.showTimestames`, see [Public Properties & Methods](./doc/public_properties_methods.md).
diff --git a/CHANGELOG_CN.md b/CHANGELOG_CN.md
index 61108d8a..7569ed31 100644
--- a/CHANGELOG_CN.md
+++ b/CHANGELOG_CN.md
@@ -1,5 +1,15 @@
[English](./CHANGELOG.md) | 简体中文
+## 3.14.0 (2022-03-23)
+
+- `Feat(Core)` 新增配置项 `pluginOrder` 来调整插件面板的排序,见 [公共属性及方法](./doc/public_properties_methods_CN.md)。
+- `Feat(Core)` 切换插件面板时,面板会自动滚动到上次的位置。
+- `Feat(Network)` 新增显示 Response 的体积。
+- `Feat(Network)` 新增对 `transfer-encoding: chunked` 的支持,现在可记录流式回包(stream response)。
+- `Feat(Network)` 展示时裁剪过大的 Response 回包以提高渲染性能。
+- `Refactor(Network)` 提高网络记录的准确性,以避免被外部库(如 Axios)覆盖;方法是对 `XMLHttpRequest | fetch` 使用 Proxy。
+
+
## 3.13.0 (2022-03-15)
- `Feat(Log)` 新增配置项 `log.showTimestames`,见 [公共属性及方法](./doc/public_properties_methods_CN.md)。
diff --git a/dev/network.html b/dev/network.html
index 09ff2056..67382d43 100644
--- a/dev/network.html
+++ b/dev/network.html
@@ -7,8 +7,8 @@
-
+
@@ -131,6 +133,7 @@
}
function getFetch() {
+ vConsole.show();
window.fetch('./data/success.json?method=fetchGet&id=' + Math.random(), {
method: 'GET',
headers: {
@@ -138,9 +141,16 @@
'content-type': 'application/json'
},
}).then((data) => {
- return data.json();
+ console.log('getFetch() response:', data);
+ setTimeout(() => {
+ data.json().then((res) => {
+ console.log(res);
+ });
+ }, 3000);
+ // return data;
+ // return data.json();
}).then((data) => {
- console.log('get Fetch Response:', data);
+ // console.log('getFetch() json:', data);
});
}
@@ -161,7 +171,6 @@
});
}
-
function getFetchSimple() {
window.fetch('./data/large.json?type=fetchGet&id=' + Math.random()).then((data) => {
return data.json();
@@ -256,6 +265,74 @@
});
}
+function fetchStream() {
+ vConsole.show();
+ window.fetch('./data/stream.flv?id=' + Math.random()).then((response) => {
+ console.log('then response', 'bodyUsed:', response.bodyUsed, 'locked:', response.body.locked);
+ // console.log(response.text())
+ // return;
+ const reader = response.body.getReader();
+ console.log('then response', 'bodyUsed:', response.bodyUsed, 'locked:', response.body.locked);
+ let bytesReceived = 0;
+
+ return reader.read().then(function process(result) {
+ console.log('reader.read', 'bodyUsed:', response.bodyUsed, 'locked:', response.body.locked);
+ if (result.done) {
+ console.log('Failed to find match');
+ return;
+ }
+
+ bytesReceived += result.value.length;
+ console.log(`Received ${bytesReceived} bytes.`);
+
+ if (bytesReceived > 3000000) {
+ reader.cancel();
+ console.log('Cancel.', response.status);
+ return;
+ }
+
+ return reader.read().then(process);
+ });
+ });
+}
+
+function xhrStream() {
+ vConsole.show();
+ const url = './data/stream.flv?id=' + Math.random();
+ const xhr = new XMLHttpRequest();
+ xhr.timeout = 11000;
+ console.log('xhr type:', typeof xhr, xhr instanceof XMLHttpRequest);
+ xhr.open('GET', url);
+ xhr.send();
+ xhr.onreadystatechange = () => {
+ console.log('XHR onreadystatechange:', 'readyState:', xhr.readyState, 'responseType:', xhr.responseType);
+ };
+ xhr.onprogress = (e) => {
+ // console.log('XHR onprogress:', 'readyState:', xhr.readyState, 'status:', xhr.status, 'loaded:', e.loaded, 'timeStamp:', e.timeStamp);
+ if (e.loaded > 3000000) {
+ xhr.abort();
+ }
+ };
+ xhr.onloadstart = (e) => {
+ // console.log('XHR onloadstart:', e);
+ };
+ xhr.onloadend = (e) => {
+ // console.log('XHR onloadend:', 'readyState:', xhr.readyState, xhr.status, e);
+ };
+ xhr.onload = (e) => {
+ console.log('XHR onload:', 'readyState:', xhr.readyState, xhr.status, xhr.responseType);
+ };
+ xhr.onerror = (e) => {
+ console.log('XHR onerror:', e);
+ };
+ xhr.onabort = (e) => {
+ console.log('XHR onabort:', xhr.readyState, xhr.status, e);
+ };
+ xhr.ontimeout = (e) => {
+ console.log('XHR ontimeout:', e);
+ }
+}
+
function postImage() {
console.info('postImage() Start, response should be logged after End');
const xhr = new XMLHttpRequest();
@@ -269,6 +346,7 @@
}
function sendBeacon() {
+ vConsole.show();
console.info('sendBeacon() Start, response should be logged after End');
window.navigator.sendBeacon('./data/success.json?method=beacon', JSON.stringify({
foo: 'bar',
@@ -297,16 +375,6 @@
console.info('axiosRequest() End');
}
-function sendBeacon() {
- console.info('sendBeacon() Start, response should be logged after End');
- window.navigator.sendBeacon('./data/success.json?method=beacon', JSON.stringify({
- foo: 'bar',
- id: Math.random(),
- type: 'sendBeacon'
- }));
- console.info('sendBeacon() End');
-}
-
function axiosRequest(method) {
console.info('axiosRequest() Start');
axios({
diff --git a/dev/plugin.html b/dev/plugin.html
index 83873eee..b9b7e760 100644
--- a/dev/plugin.html
+++ b/dev/plugin.html
@@ -25,18 +25,13 @@