dreaction-react 1.0.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/README.md +255 -0
- package/lib/components/ConfigPanel.d.ts +12 -0
- package/lib/components/ConfigPanel.d.ts.map +1 -0
- package/lib/components/ConfigPanel.js +151 -0
- package/lib/dreaction.d.ts +32 -0
- package/lib/dreaction.d.ts.map +1 -0
- package/lib/dreaction.js +110 -0
- package/lib/hooks.d.ts +17 -0
- package/lib/hooks.d.ts.map +1 -0
- package/lib/hooks.js +111 -0
- package/lib/index.d.ts +13 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +23 -0
- package/lib/plugins/localStorage.d.ts +14 -0
- package/lib/plugins/localStorage.d.ts.map +1 -0
- package/lib/plugins/localStorage.js +85 -0
- package/lib/plugins/networking.d.ts +11 -0
- package/lib/plugins/networking.d.ts.map +1 -0
- package/lib/plugins/networking.js +263 -0
- package/lib/plugins/trackGlobalErrors.d.ts +25 -0
- package/lib/plugins/trackGlobalErrors.d.ts.map +1 -0
- package/lib/plugins/trackGlobalErrors.js +149 -0
- package/lib/plugins/trackGlobalLogs.d.ts +10 -0
- package/lib/plugins/trackGlobalLogs.d.ts.map +1 -0
- package/lib/plugins/trackGlobalLogs.js +42 -0
- package/package.json +35 -0
- package/src/components/ConfigPanel.tsx +247 -0
- package/src/dreaction.ts +181 -0
- package/src/hooks.ts +129 -0
- package/src/index.ts +17 -0
- package/src/plugins/localStorage.ts +100 -0
- package/src/plugins/networking.ts +333 -0
- package/src/plugins/trackGlobalErrors.ts +203 -0
- package/src/plugins/trackGlobalLogs.ts +53 -0
package/src/hooks.ts
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { useState, useEffect, useRef } from 'react';
|
|
2
|
+
import { dreaction } from './dreaction';
|
|
3
|
+
|
|
4
|
+
const STORAGE_KEY_HOST = 'DREACTION_host';
|
|
5
|
+
const STORAGE_KEY_PORT = 'DREACTION_port';
|
|
6
|
+
const STORAGE_KEY_AUTO_CONNECT = 'DREACTION_autoConnect';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Hook to manage DReaction connection status
|
|
10
|
+
*/
|
|
11
|
+
export function useConnectionStatus() {
|
|
12
|
+
const [isConnected, setIsConnected] = useState(dreaction.connected);
|
|
13
|
+
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
const checkConnection = () => {
|
|
16
|
+
setIsConnected(dreaction.connected);
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
// Check connection status periodically
|
|
20
|
+
const interval = setInterval(checkConnection, 1000);
|
|
21
|
+
|
|
22
|
+
return () => clearInterval(interval);
|
|
23
|
+
}, []);
|
|
24
|
+
|
|
25
|
+
return isConnected;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Hook to manage DReaction configuration
|
|
30
|
+
*/
|
|
31
|
+
export function useDReactionConfig() {
|
|
32
|
+
const [host, setHost] = useState(() => {
|
|
33
|
+
if (typeof window !== 'undefined' && window.localStorage) {
|
|
34
|
+
return window.localStorage.getItem(STORAGE_KEY_HOST) || 'localhost';
|
|
35
|
+
}
|
|
36
|
+
return 'localhost';
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const [port, setPort] = useState(() => {
|
|
40
|
+
if (typeof window !== 'undefined' && window.localStorage) {
|
|
41
|
+
const stored = window.localStorage.getItem(STORAGE_KEY_PORT);
|
|
42
|
+
return stored ? parseInt(stored, 10) : 9600;
|
|
43
|
+
}
|
|
44
|
+
return 9600;
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const [isConnected, setIsConnected] = useState(dreaction.connected);
|
|
48
|
+
const hasAutoConnected = useRef(false);
|
|
49
|
+
|
|
50
|
+
// Auto-connect on mount if previously connected
|
|
51
|
+
useEffect(() => {
|
|
52
|
+
if (hasAutoConnected.current) return;
|
|
53
|
+
|
|
54
|
+
if (typeof window !== 'undefined' && window.localStorage) {
|
|
55
|
+
const shouldAutoConnect = window.localStorage.getItem(
|
|
56
|
+
STORAGE_KEY_AUTO_CONNECT
|
|
57
|
+
);
|
|
58
|
+
if (shouldAutoConnect === 'true' && !dreaction.connected) {
|
|
59
|
+
hasAutoConnected.current = true;
|
|
60
|
+
// Auto-reconnect with saved settings
|
|
61
|
+
dreaction.configure({
|
|
62
|
+
host,
|
|
63
|
+
port,
|
|
64
|
+
});
|
|
65
|
+
dreaction.connect();
|
|
66
|
+
setIsConnected(true);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}, [host, port]);
|
|
70
|
+
|
|
71
|
+
useEffect(() => {
|
|
72
|
+
const checkConnection = () => {
|
|
73
|
+
setIsConnected(dreaction.connected);
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
// Check connection status periodically
|
|
77
|
+
const interval = setInterval(checkConnection, 1000);
|
|
78
|
+
|
|
79
|
+
return () => clearInterval(interval);
|
|
80
|
+
}, []);
|
|
81
|
+
|
|
82
|
+
const updateHost = (newHost: string) => {
|
|
83
|
+
setHost(newHost);
|
|
84
|
+
if (typeof window !== 'undefined' && window.localStorage) {
|
|
85
|
+
window.localStorage.setItem(STORAGE_KEY_HOST, newHost);
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const updatePort = (newPort: number) => {
|
|
90
|
+
setPort(newPort);
|
|
91
|
+
if (typeof window !== 'undefined' && window.localStorage) {
|
|
92
|
+
window.localStorage.setItem(STORAGE_KEY_PORT, newPort.toString());
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const connect = () => {
|
|
97
|
+
dreaction.configure({
|
|
98
|
+
host,
|
|
99
|
+
port,
|
|
100
|
+
});
|
|
101
|
+
dreaction.connect();
|
|
102
|
+
setIsConnected(true);
|
|
103
|
+
|
|
104
|
+
// Save auto-connect preference
|
|
105
|
+
if (typeof window !== 'undefined' && window.localStorage) {
|
|
106
|
+
window.localStorage.setItem(STORAGE_KEY_AUTO_CONNECT, 'true');
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const disconnect = () => {
|
|
111
|
+
dreaction.close();
|
|
112
|
+
setIsConnected(false);
|
|
113
|
+
|
|
114
|
+
// Clear auto-connect preference
|
|
115
|
+
if (typeof window !== 'undefined' && window.localStorage) {
|
|
116
|
+
window.localStorage.setItem(STORAGE_KEY_AUTO_CONNECT, 'false');
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
return {
|
|
121
|
+
host,
|
|
122
|
+
port,
|
|
123
|
+
isConnected,
|
|
124
|
+
updateHost,
|
|
125
|
+
updatePort,
|
|
126
|
+
connect,
|
|
127
|
+
disconnect,
|
|
128
|
+
};
|
|
129
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export { dreaction, reactCorePlugins } from './dreaction';
|
|
2
|
+
export type { DReactionReact, UseReactOptions } from './dreaction';
|
|
3
|
+
export { ConfigPanel } from './components/ConfigPanel';
|
|
4
|
+
export type { ConfigPanelProps } from './components/ConfigPanel';
|
|
5
|
+
export { useConnectionStatus, useDReactionConfig } from './hooks';
|
|
6
|
+
|
|
7
|
+
// Plugin exports
|
|
8
|
+
export { default as networking } from './plugins/networking';
|
|
9
|
+
export type { NetworkingOptions } from './plugins/networking';
|
|
10
|
+
export { default as localStorage } from './plugins/localStorage';
|
|
11
|
+
export type { LocalStorageOptions } from './plugins/localStorage';
|
|
12
|
+
export { default as trackGlobalLogs } from './plugins/trackGlobalLogs';
|
|
13
|
+
export { default as trackGlobalErrors } from './plugins/trackGlobalErrors';
|
|
14
|
+
export type {
|
|
15
|
+
TrackGlobalErrorsOptions,
|
|
16
|
+
ErrorStackFrame,
|
|
17
|
+
} from './plugins/trackGlobalErrors';
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import type { DReactionCore, Plugin } from 'dreaction-client-core';
|
|
2
|
+
|
|
3
|
+
export interface LocalStorageOptions {
|
|
4
|
+
ignore?: string[];
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
const PLUGIN_DEFAULTS: LocalStorageOptions = {
|
|
8
|
+
ignore: [],
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const localStorage =
|
|
12
|
+
(options?: LocalStorageOptions) => (dreaction: DReactionCore) => {
|
|
13
|
+
// setup configuration
|
|
14
|
+
const config = Object.assign({}, PLUGIN_DEFAULTS, options || {});
|
|
15
|
+
const ignore = config.ignore || PLUGIN_DEFAULTS.ignore;
|
|
16
|
+
|
|
17
|
+
let originalSetItem: typeof Storage.prototype.setItem;
|
|
18
|
+
let originalRemoveItem: typeof Storage.prototype.removeItem;
|
|
19
|
+
let originalClear: typeof Storage.prototype.clear;
|
|
20
|
+
let isIntercepted = false;
|
|
21
|
+
|
|
22
|
+
const sendToDReaction = (action: string, data?: any) => {
|
|
23
|
+
dreaction.send('asyncStorage.mutation', { action: action as any, data });
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const setItem = function (this: Storage, key: string, value: string): void {
|
|
27
|
+
try {
|
|
28
|
+
if (ignore!.indexOf(key) < 0) {
|
|
29
|
+
sendToDReaction('setItem', { key, value });
|
|
30
|
+
}
|
|
31
|
+
} catch (e) {
|
|
32
|
+
// ignore errors
|
|
33
|
+
}
|
|
34
|
+
return originalSetItem.call(this, key, value);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const removeItem = function (this: Storage, key: string): void {
|
|
38
|
+
try {
|
|
39
|
+
if (ignore!.indexOf(key) < 0) {
|
|
40
|
+
sendToDReaction('removeItem', { key });
|
|
41
|
+
}
|
|
42
|
+
} catch (e) {
|
|
43
|
+
// ignore errors
|
|
44
|
+
}
|
|
45
|
+
return originalRemoveItem.call(this, key);
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const clear = function (this: Storage): void {
|
|
49
|
+
try {
|
|
50
|
+
sendToDReaction('clear');
|
|
51
|
+
} catch (e) {
|
|
52
|
+
// ignore errors
|
|
53
|
+
}
|
|
54
|
+
return originalClear.call(this);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Hijacks the localStorage API.
|
|
59
|
+
*/
|
|
60
|
+
const trackLocalStorage = () => {
|
|
61
|
+
if (isIntercepted) return;
|
|
62
|
+
if (typeof window === 'undefined' || !window.localStorage) return;
|
|
63
|
+
|
|
64
|
+
originalSetItem = Storage.prototype.setItem;
|
|
65
|
+
Storage.prototype.setItem = setItem;
|
|
66
|
+
|
|
67
|
+
originalRemoveItem = Storage.prototype.removeItem;
|
|
68
|
+
Storage.prototype.removeItem = removeItem;
|
|
69
|
+
|
|
70
|
+
originalClear = Storage.prototype.clear;
|
|
71
|
+
Storage.prototype.clear = clear;
|
|
72
|
+
|
|
73
|
+
isIntercepted = true;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const untrackLocalStorage = () => {
|
|
77
|
+
if (!isIntercepted) return;
|
|
78
|
+
|
|
79
|
+
Storage.prototype.setItem = originalSetItem;
|
|
80
|
+
Storage.prototype.removeItem = originalRemoveItem;
|
|
81
|
+
Storage.prototype.clear = originalClear;
|
|
82
|
+
|
|
83
|
+
isIntercepted = false;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
onConnect: () => {
|
|
88
|
+
trackLocalStorage();
|
|
89
|
+
},
|
|
90
|
+
onDisconnect: () => {
|
|
91
|
+
untrackLocalStorage();
|
|
92
|
+
},
|
|
93
|
+
features: {
|
|
94
|
+
trackLocalStorage,
|
|
95
|
+
untrackLocalStorage,
|
|
96
|
+
},
|
|
97
|
+
} satisfies Plugin<DReactionCore>;
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
export default localStorage;
|
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
import type { DReactionCore, Plugin } from 'dreaction-client-core';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Don't include the response bodies for images by default.
|
|
5
|
+
*/
|
|
6
|
+
const DEFAULT_CONTENT_TYPES_RX = /^(image)\/.*$/i;
|
|
7
|
+
|
|
8
|
+
export interface NetworkingOptions {
|
|
9
|
+
ignoreContentTypes?: RegExp;
|
|
10
|
+
ignoreUrls?: RegExp;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const DEFAULTS: NetworkingOptions = {
|
|
14
|
+
ignoreUrls: /symbolicate/,
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const networking =
|
|
18
|
+
(pluginConfig: NetworkingOptions = {}) =>
|
|
19
|
+
(dreaction: DReactionCore) => {
|
|
20
|
+
const options = Object.assign({}, DEFAULTS, pluginConfig);
|
|
21
|
+
|
|
22
|
+
// a RegExp to suppress adding the body cuz it costs a lot to serialize
|
|
23
|
+
const ignoreContentTypes =
|
|
24
|
+
options.ignoreContentTypes || DEFAULT_CONTENT_TYPES_RX;
|
|
25
|
+
|
|
26
|
+
// a XHR call tracker
|
|
27
|
+
let dreactionCounter = 1000;
|
|
28
|
+
|
|
29
|
+
// a temporary cache to hold requests so we can match up the data
|
|
30
|
+
const requestCache: Record<number, any> = {};
|
|
31
|
+
|
|
32
|
+
// Store original functions
|
|
33
|
+
let originalXHROpen: typeof XMLHttpRequest.prototype.open;
|
|
34
|
+
let originalXHRSend: typeof XMLHttpRequest.prototype.send;
|
|
35
|
+
let originalFetch: typeof window.fetch;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Intercept XMLHttpRequest
|
|
39
|
+
*/
|
|
40
|
+
const interceptXHR = () => {
|
|
41
|
+
originalXHROpen = XMLHttpRequest.prototype.open;
|
|
42
|
+
originalXHRSend = XMLHttpRequest.prototype.send;
|
|
43
|
+
|
|
44
|
+
XMLHttpRequest.prototype.open = function (
|
|
45
|
+
method: string,
|
|
46
|
+
url: string | URL,
|
|
47
|
+
...rest: any[]
|
|
48
|
+
) {
|
|
49
|
+
(this as any)._method = method;
|
|
50
|
+
(this as any)._url = url.toString();
|
|
51
|
+
return originalXHROpen.apply(this, [method, url, ...rest] as any);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
XMLHttpRequest.prototype.send = function (data?: any) {
|
|
55
|
+
const xhr = this as any;
|
|
56
|
+
|
|
57
|
+
if (options.ignoreUrls && options.ignoreUrls.test(xhr._url)) {
|
|
58
|
+
xhr._skipDReaction = true;
|
|
59
|
+
return originalXHRSend.apply(this, [data]);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// bump the counter
|
|
63
|
+
dreactionCounter++;
|
|
64
|
+
|
|
65
|
+
// tag
|
|
66
|
+
xhr._trackingName = dreactionCounter;
|
|
67
|
+
|
|
68
|
+
// cache
|
|
69
|
+
requestCache[dreactionCounter] = {
|
|
70
|
+
data,
|
|
71
|
+
xhr,
|
|
72
|
+
stopTimer: dreaction.startTimer(),
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
// Setup listener for response
|
|
76
|
+
const originalOnReadyStateChange = xhr.onreadystatechange;
|
|
77
|
+
xhr.onreadystatechange = function () {
|
|
78
|
+
if (originalOnReadyStateChange) {
|
|
79
|
+
originalOnReadyStateChange.apply(this, arguments as any);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (xhr.readyState === 4) {
|
|
83
|
+
handleXHRResponse(xhr);
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
return originalXHRSend.apply(this, [data]);
|
|
88
|
+
};
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Handle XHR response
|
|
93
|
+
*/
|
|
94
|
+
const handleXHRResponse = (xhr: any) => {
|
|
95
|
+
if (xhr._skipDReaction) {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const url = xhr._url;
|
|
100
|
+
let params = null;
|
|
101
|
+
const queryParamIdx = url ? url.indexOf('?') : -1;
|
|
102
|
+
if (queryParamIdx > -1) {
|
|
103
|
+
params = {} as Record<string, string>;
|
|
104
|
+
url
|
|
105
|
+
.substr(queryParamIdx + 1)
|
|
106
|
+
.split('&')
|
|
107
|
+
.forEach((pair: string) => {
|
|
108
|
+
const [key, value] = pair.split('=');
|
|
109
|
+
if (key && value !== undefined) {
|
|
110
|
+
params![key] = decodeURIComponent(value.replace(/\+/g, ' '));
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// fetch and clear the request data from the cache
|
|
116
|
+
const rid = xhr._trackingName;
|
|
117
|
+
const cachedRequest = requestCache[rid] || { xhr };
|
|
118
|
+
requestCache[rid] = null;
|
|
119
|
+
|
|
120
|
+
// assemble the request object
|
|
121
|
+
const { data, stopTimer } = cachedRequest;
|
|
122
|
+
|
|
123
|
+
// Parse request headers
|
|
124
|
+
let requestHeaders: Record<string, string> = {};
|
|
125
|
+
try {
|
|
126
|
+
const requestHeaderString = xhr._requestHeaders;
|
|
127
|
+
if (requestHeaderString) {
|
|
128
|
+
requestHeaders = requestHeaderString;
|
|
129
|
+
}
|
|
130
|
+
} catch (e) {
|
|
131
|
+
// ignore
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const tronRequest = {
|
|
135
|
+
url: url || cachedRequest.xhr._url,
|
|
136
|
+
method: xhr._method || null,
|
|
137
|
+
data,
|
|
138
|
+
headers: requestHeaders || null,
|
|
139
|
+
params,
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
// Parse response headers
|
|
143
|
+
let responseHeaders: Record<string, string> = {};
|
|
144
|
+
try {
|
|
145
|
+
const headersString = xhr.getAllResponseHeaders();
|
|
146
|
+
if (headersString) {
|
|
147
|
+
headersString.split('\r\n').forEach((line: string) => {
|
|
148
|
+
const parts = line.split(': ');
|
|
149
|
+
if (parts.length === 2) {
|
|
150
|
+
responseHeaders[parts[0]] = parts[1];
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
} catch (e) {
|
|
155
|
+
// ignore
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// what type of content is this?
|
|
159
|
+
const contentType =
|
|
160
|
+
responseHeaders['content-type'] ||
|
|
161
|
+
responseHeaders['Content-Type'] ||
|
|
162
|
+
'';
|
|
163
|
+
|
|
164
|
+
let body = `~~~ skipped ~~~`;
|
|
165
|
+
const response = xhr.response || xhr.responseText;
|
|
166
|
+
|
|
167
|
+
// can we use the real response?
|
|
168
|
+
const useRealResponse =
|
|
169
|
+
(typeof response === 'string' || typeof response === 'object') &&
|
|
170
|
+
!ignoreContentTypes.test(contentType || '');
|
|
171
|
+
|
|
172
|
+
if (useRealResponse && response) {
|
|
173
|
+
try {
|
|
174
|
+
// Try to parse JSON
|
|
175
|
+
if (typeof response === 'string') {
|
|
176
|
+
body = JSON.parse(response);
|
|
177
|
+
} else {
|
|
178
|
+
body = response;
|
|
179
|
+
}
|
|
180
|
+
} catch (e) {
|
|
181
|
+
body = response;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const tronResponse = {
|
|
186
|
+
body,
|
|
187
|
+
status: xhr.status,
|
|
188
|
+
headers: responseHeaders || null,
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
(dreaction as any).apiResponse(
|
|
192
|
+
tronRequest,
|
|
193
|
+
tronResponse,
|
|
194
|
+
stopTimer ? stopTimer() : null
|
|
195
|
+
);
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Intercept fetch API
|
|
200
|
+
*/
|
|
201
|
+
const interceptFetch = () => {
|
|
202
|
+
originalFetch = window.fetch;
|
|
203
|
+
|
|
204
|
+
window.fetch = function (
|
|
205
|
+
input: RequestInfo | URL,
|
|
206
|
+
init?: RequestInit
|
|
207
|
+
): Promise<Response> {
|
|
208
|
+
const url =
|
|
209
|
+
typeof input === 'string'
|
|
210
|
+
? input
|
|
211
|
+
: input instanceof Request
|
|
212
|
+
? input.url
|
|
213
|
+
: input.toString();
|
|
214
|
+
|
|
215
|
+
if (options.ignoreUrls && options.ignoreUrls.test(url)) {
|
|
216
|
+
return originalFetch.call(this, input as RequestInfo, init);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// bump the counter
|
|
220
|
+
dreactionCounter++;
|
|
221
|
+
const requestId = dreactionCounter;
|
|
222
|
+
|
|
223
|
+
const stopTimer = dreaction.startTimer();
|
|
224
|
+
|
|
225
|
+
// Parse URL and params
|
|
226
|
+
let params = null;
|
|
227
|
+
const queryParamIdx = url.indexOf('?');
|
|
228
|
+
if (queryParamIdx > -1) {
|
|
229
|
+
params = {} as Record<string, string>;
|
|
230
|
+
url
|
|
231
|
+
.substr(queryParamIdx + 1)
|
|
232
|
+
.split('&')
|
|
233
|
+
.forEach((pair: string) => {
|
|
234
|
+
const [key, value] = pair.split('=');
|
|
235
|
+
if (key && value !== undefined) {
|
|
236
|
+
params![key] = decodeURIComponent(value.replace(/\+/g, ' '));
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const tronRequest = {
|
|
242
|
+
url,
|
|
243
|
+
method: init?.method || 'GET',
|
|
244
|
+
data: init?.body || null,
|
|
245
|
+
headers: init?.headers || {},
|
|
246
|
+
params,
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
return originalFetch
|
|
250
|
+
.call(this, input as RequestInfo, init)
|
|
251
|
+
.then(async (response) => {
|
|
252
|
+
const contentType = response.headers.get('content-type') || '';
|
|
253
|
+
|
|
254
|
+
// Clone the response so we can read it
|
|
255
|
+
const clonedResponse = response.clone();
|
|
256
|
+
|
|
257
|
+
let body = `~~~ skipped ~~~`;
|
|
258
|
+
const useRealResponse = !ignoreContentTypes.test(contentType);
|
|
259
|
+
|
|
260
|
+
if (useRealResponse) {
|
|
261
|
+
try {
|
|
262
|
+
if (contentType.includes('application/json')) {
|
|
263
|
+
body = await clonedResponse.json();
|
|
264
|
+
} else {
|
|
265
|
+
body = await clonedResponse.text();
|
|
266
|
+
}
|
|
267
|
+
} catch (e) {
|
|
268
|
+
// ignore parsing errors
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// Parse response headers
|
|
273
|
+
const responseHeaders: Record<string, string> = {};
|
|
274
|
+
response.headers.forEach((value: string, key: string) => {
|
|
275
|
+
responseHeaders[key] = value;
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
const tronResponse = {
|
|
279
|
+
body,
|
|
280
|
+
status: response.status,
|
|
281
|
+
headers: responseHeaders,
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
(dreaction as any).apiResponse(
|
|
285
|
+
tronRequest,
|
|
286
|
+
tronResponse,
|
|
287
|
+
stopTimer()
|
|
288
|
+
);
|
|
289
|
+
|
|
290
|
+
return response;
|
|
291
|
+
});
|
|
292
|
+
};
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Restore original functions
|
|
297
|
+
*/
|
|
298
|
+
const restoreXHR = () => {
|
|
299
|
+
if (originalXHROpen) {
|
|
300
|
+
XMLHttpRequest.prototype.open = originalXHROpen;
|
|
301
|
+
}
|
|
302
|
+
if (originalXHRSend) {
|
|
303
|
+
XMLHttpRequest.prototype.send = originalXHRSend;
|
|
304
|
+
}
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
const restoreFetch = () => {
|
|
308
|
+
if (originalFetch) {
|
|
309
|
+
window.fetch = originalFetch;
|
|
310
|
+
}
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
return {
|
|
314
|
+
onConnect: () => {
|
|
315
|
+
// register our interceptors
|
|
316
|
+
if (typeof XMLHttpRequest !== 'undefined') {
|
|
317
|
+
interceptXHR();
|
|
318
|
+
}
|
|
319
|
+
if (
|
|
320
|
+
typeof window !== 'undefined' &&
|
|
321
|
+
typeof window.fetch === 'function'
|
|
322
|
+
) {
|
|
323
|
+
interceptFetch();
|
|
324
|
+
}
|
|
325
|
+
},
|
|
326
|
+
onDisconnect: () => {
|
|
327
|
+
restoreXHR();
|
|
328
|
+
restoreFetch();
|
|
329
|
+
},
|
|
330
|
+
} satisfies Plugin<DReactionCore>;
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
export default networking;
|