react-native-vconsole 0.0.1 → 0.2.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/LICENSE +20 -0
- package/README.md +39 -9
- package/Vconsole.podspec +25 -0
- package/android/build.gradle +61 -129
- package/android/gradle.properties +5 -0
- package/android/src/main/AndroidManifest.xml +1 -2
- package/android/src/main/AndroidManifestNew.xml +2 -0
- package/android/src/main/java/com/vconsole/VconsoleModule.kt +80 -0
- package/android/src/main/java/com/vconsole/VconsolePackage.kt +17 -0
- package/ios/Vconsole.h +6 -0
- package/ios/Vconsole.mm +64 -0
- package/lib/module/VConsole.js +769 -0
- package/lib/module/VConsole.js.map +1 -0
- package/lib/module/core/consoleProxy.js +86 -0
- package/lib/module/core/consoleProxy.js.map +1 -0
- package/lib/module/core/xhrProxy.js +247 -0
- package/lib/module/core/xhrProxy.js.map +1 -0
- package/lib/module/index.js +24 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/types.js +2 -0
- package/lib/module/types.js.map +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/VConsole.d.ts +6 -0
- package/lib/typescript/src/VConsole.d.ts.map +1 -0
- package/lib/typescript/src/core/consoleProxy.d.ts +9 -0
- package/lib/typescript/src/core/consoleProxy.d.ts.map +1 -0
- package/lib/typescript/src/core/xhrProxy.d.ts +12 -0
- package/lib/typescript/src/core/xhrProxy.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +9 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/types.d.ts +37 -0
- package/lib/typescript/src/types.d.ts.map +1 -0
- package/package.json +138 -25
- package/src/VConsole.tsx +871 -0
- package/src/core/consoleProxy.ts +108 -0
- package/src/core/xhrProxy.ts +319 -0
- package/src/index.tsx +36 -0
- package/src/types.ts +42 -0
- package/android/README.md +0 -14
- package/android/src/main/java/wiki/qdc/rn/vconsole/ReactNativeVconsoleModule.java +0 -27
- package/android/src/main/java/wiki/qdc/rn/vconsole/ReactNativeVconsolePackage.java +0 -23
- package/index.js +0 -5
- package/ios/.DS_Store +0 -0
- package/ios/ReactNativeVconsole.h +0 -5
- package/ios/ReactNativeVconsole.m +0 -13
- package/ios/ReactNativeVconsole.xcodeproj/project.pbxproj +0 -281
- package/ios/ReactNativeVconsole.xcworkspace/contents.xcworkspacedata +0 -7
- package/react-native-vconsole.podspec +0 -28
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import type { LogEntry, LogLevel } from '../types';
|
|
2
|
+
|
|
3
|
+
type LogListener = (entries: LogEntry[]) => void;
|
|
4
|
+
type ConsoleMethod = (...args: unknown[]) => void;
|
|
5
|
+
|
|
6
|
+
const MAX_LOG_COUNT = 1000;
|
|
7
|
+
|
|
8
|
+
const listeners = new Set<LogListener>();
|
|
9
|
+
const entries: LogEntry[] = [];
|
|
10
|
+
const originalConsole: Partial<Record<LogLevel, ConsoleMethod>> = {};
|
|
11
|
+
|
|
12
|
+
let isInstalled = false;
|
|
13
|
+
let logId = 1;
|
|
14
|
+
|
|
15
|
+
function safeSerialize(value: unknown): string {
|
|
16
|
+
if (typeof value === 'string') {
|
|
17
|
+
return value;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (value instanceof Error) {
|
|
21
|
+
return `${value.name}: ${value.message}`;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
return JSON.stringify(value);
|
|
26
|
+
} catch {
|
|
27
|
+
return String(value);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function buildText(args: unknown[]): string {
|
|
32
|
+
return args.map((item) => safeSerialize(item)).join(' ');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function notify() {
|
|
36
|
+
const next = [...entries];
|
|
37
|
+
listeners.forEach((listener) => {
|
|
38
|
+
listener(next);
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function pushEntry(level: LogLevel, args: unknown[]) {
|
|
43
|
+
entries.push({
|
|
44
|
+
id: logId++,
|
|
45
|
+
level,
|
|
46
|
+
args,
|
|
47
|
+
text: buildText(args),
|
|
48
|
+
timestamp: Date.now(),
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
if (entries.length > MAX_LOG_COUNT) {
|
|
52
|
+
entries.splice(0, entries.length - MAX_LOG_COUNT);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function installConsoleProxy() {
|
|
57
|
+
if (isInstalled) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const levels: LogLevel[] = ['log', 'info', 'warn', 'error'];
|
|
62
|
+
levels.forEach((level) => {
|
|
63
|
+
const original = console[level] as ConsoleMethod;
|
|
64
|
+
originalConsole[level] = original;
|
|
65
|
+
|
|
66
|
+
console[level] = (...args: unknown[]) => {
|
|
67
|
+
pushEntry(level, args);
|
|
68
|
+
notify();
|
|
69
|
+
original(...args);
|
|
70
|
+
};
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
isInstalled = true;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function uninstallConsoleProxy() {
|
|
77
|
+
if (!isInstalled) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const levels: LogLevel[] = ['log', 'info', 'warn', 'error'];
|
|
82
|
+
levels.forEach((level) => {
|
|
83
|
+
const original = originalConsole[level];
|
|
84
|
+
if (original) {
|
|
85
|
+
console[level] = original as ConsoleMethod;
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
isInstalled = false;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function getLogEntries(): LogEntry[] {
|
|
93
|
+
return [...entries];
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function clearLogEntries() {
|
|
97
|
+
entries.length = 0;
|
|
98
|
+
notify();
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function subscribeLogEntries(listener: LogListener) {
|
|
102
|
+
listeners.add(listener);
|
|
103
|
+
listener(getLogEntries());
|
|
104
|
+
|
|
105
|
+
return () => {
|
|
106
|
+
listeners.delete(listener);
|
|
107
|
+
};
|
|
108
|
+
}
|
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
import type { NetworkEntry } from '../types';
|
|
2
|
+
|
|
3
|
+
type NetworkListener = (entries: NetworkEntry[]) => void;
|
|
4
|
+
type InstallXhrProxyOptions = {
|
|
5
|
+
filterHosts?: string[];
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
const MAX_NETWORK_COUNT = 500;
|
|
9
|
+
|
|
10
|
+
const listeners = new Set<NetworkListener>();
|
|
11
|
+
const entries: NetworkEntry[] = [];
|
|
12
|
+
|
|
13
|
+
let isInstalled = false;
|
|
14
|
+
let networkId = 1;
|
|
15
|
+
let OriginalXHR: typeof XMLHttpRequest | undefined;
|
|
16
|
+
let ignoredHosts = new Set<string>();
|
|
17
|
+
|
|
18
|
+
function normalizeHosts(hosts?: string[]): Set<string> {
|
|
19
|
+
return new Set(
|
|
20
|
+
(hosts ?? []).map((item) => item.trim().toLowerCase()).filter(Boolean)
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function getHostFromUrl(rawUrl: string): string | undefined {
|
|
25
|
+
if (!rawUrl) {
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const isAbsoluteHttp = /^https?:\/\//i.test(rawUrl);
|
|
30
|
+
const isProtocolRelative = /^\/\//.test(rawUrl);
|
|
31
|
+
if (!isAbsoluteHttp && !isProtocolRelative) {
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
const normalizedUrl = isProtocolRelative ? `https:${rawUrl}` : rawUrl;
|
|
37
|
+
return new URL(normalizedUrl).host.toLowerCase();
|
|
38
|
+
} catch {
|
|
39
|
+
const normalizedUrl = isProtocolRelative ? `https:${rawUrl}` : rawUrl;
|
|
40
|
+
const matched = normalizedUrl.match(/^https?:\/\/([^/]+)/i);
|
|
41
|
+
return matched?.[1]?.toLowerCase();
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function shouldSkipNetworkCapture(rawUrl: string): boolean {
|
|
46
|
+
const host = getHostFromUrl(rawUrl);
|
|
47
|
+
if (!host) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
return ignoredHosts.has(host);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function notify() {
|
|
54
|
+
const next = [...entries];
|
|
55
|
+
listeners.forEach((listener) => {
|
|
56
|
+
listener(next);
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function trimEntries() {
|
|
61
|
+
if (entries.length > MAX_NETWORK_COUNT) {
|
|
62
|
+
entries.splice(0, entries.length - MAX_NETWORK_COUNT);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function isPromiseLike(value: unknown): value is Promise<unknown> {
|
|
67
|
+
return (
|
|
68
|
+
typeof value === 'object' &&
|
|
69
|
+
value !== null &&
|
|
70
|
+
'then' in value &&
|
|
71
|
+
typeof (value as { then?: unknown }).then === 'function'
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function getHeaderCaseInsensitive(
|
|
76
|
+
headers: Record<string, string>,
|
|
77
|
+
key: string
|
|
78
|
+
): string | undefined {
|
|
79
|
+
const target = key.toLowerCase();
|
|
80
|
+
const foundKey = Object.keys(headers).find(
|
|
81
|
+
(item) => item.toLowerCase() === target
|
|
82
|
+
);
|
|
83
|
+
return foundKey ? headers[foundKey] : undefined;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function parseBlobLikeJson(blobLike: unknown): Promise<unknown> | undefined {
|
|
87
|
+
if (
|
|
88
|
+
typeof blobLike === 'object' &&
|
|
89
|
+
blobLike !== null &&
|
|
90
|
+
'text' in blobLike &&
|
|
91
|
+
typeof (blobLike as { text?: unknown }).text === 'function'
|
|
92
|
+
) {
|
|
93
|
+
const textFn = (blobLike as { text: () => Promise<string> }).text;
|
|
94
|
+
return textFn()
|
|
95
|
+
.then((text) => {
|
|
96
|
+
try {
|
|
97
|
+
return JSON.parse(text);
|
|
98
|
+
} catch {
|
|
99
|
+
return text;
|
|
100
|
+
}
|
|
101
|
+
})
|
|
102
|
+
.catch(() => '[Blob response]');
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (typeof Response !== 'undefined') {
|
|
106
|
+
try {
|
|
107
|
+
const response = new Response(blobLike as never);
|
|
108
|
+
return response
|
|
109
|
+
.text()
|
|
110
|
+
.then((text) => {
|
|
111
|
+
try {
|
|
112
|
+
return JSON.parse(text);
|
|
113
|
+
} catch {
|
|
114
|
+
return text;
|
|
115
|
+
}
|
|
116
|
+
})
|
|
117
|
+
.catch(() => '[Blob response]');
|
|
118
|
+
} catch {
|
|
119
|
+
return undefined;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return undefined;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function parseResponseData(
|
|
127
|
+
xhr: XMLHttpRequest,
|
|
128
|
+
responseHeaders: Record<string, string>
|
|
129
|
+
): unknown | Promise<unknown> {
|
|
130
|
+
const responseType = xhr.responseType ?? '';
|
|
131
|
+
const canReadResponseText = responseType === '' || responseType === 'text';
|
|
132
|
+
|
|
133
|
+
if (canReadResponseText) {
|
|
134
|
+
try {
|
|
135
|
+
const responseText = xhr.responseText;
|
|
136
|
+
if (!responseText) {
|
|
137
|
+
return '';
|
|
138
|
+
}
|
|
139
|
+
try {
|
|
140
|
+
return JSON.parse(responseText);
|
|
141
|
+
} catch {
|
|
142
|
+
return responseText;
|
|
143
|
+
}
|
|
144
|
+
} catch {
|
|
145
|
+
// Some RN runtimes still throw for responseText access.
|
|
146
|
+
return xhr.response ?? '';
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (responseType === 'json') {
|
|
151
|
+
return xhr.response ?? null;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (responseType === 'blob') {
|
|
155
|
+
const contentType =
|
|
156
|
+
getHeaderCaseInsensitive(responseHeaders, 'content-type') ?? '';
|
|
157
|
+
if (contentType.toLowerCase().includes('application/json')) {
|
|
158
|
+
const parsed = parseBlobLikeJson(xhr.response);
|
|
159
|
+
if (parsed) {
|
|
160
|
+
return parsed;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return '[Blob response]';
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (responseType === 'arraybuffer') {
|
|
167
|
+
return '[ArrayBuffer response]';
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return xhr.response ?? '';
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function parseHeaders(rawHeaders: string): Record<string, string> {
|
|
174
|
+
const result: Record<string, string> = {};
|
|
175
|
+
rawHeaders
|
|
176
|
+
.split('\r\n')
|
|
177
|
+
.filter(Boolean)
|
|
178
|
+
.forEach((line) => {
|
|
179
|
+
const splitIndex = line.indexOf(':');
|
|
180
|
+
if (splitIndex > 0) {
|
|
181
|
+
const key = line.slice(0, splitIndex).trim();
|
|
182
|
+
const value = line.slice(splitIndex + 1).trim();
|
|
183
|
+
result[key] = value;
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
return result;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export function installXhrProxy(options?: InstallXhrProxyOptions) {
|
|
190
|
+
ignoredHosts = normalizeHosts(options?.filterHosts);
|
|
191
|
+
|
|
192
|
+
if (isInstalled || typeof XMLHttpRequest === 'undefined') {
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
OriginalXHR = global.XMLHttpRequest;
|
|
197
|
+
if (!OriginalXHR) {
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
class PatchedXMLHttpRequest extends OriginalXHR {
|
|
202
|
+
private _entryId = 0;
|
|
203
|
+
private _requestHeaders: Record<string, string> = {};
|
|
204
|
+
private _method = 'GET';
|
|
205
|
+
private _url = '';
|
|
206
|
+
|
|
207
|
+
open(method: string, url: string, ...rest: unknown[]) {
|
|
208
|
+
this._method = (method || 'GET').toUpperCase();
|
|
209
|
+
this._url = url;
|
|
210
|
+
return super.open(method, url, ...(rest as []));
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
setRequestHeader(header: string, value: string) {
|
|
214
|
+
this._requestHeaders[header] = value;
|
|
215
|
+
return super.setRequestHeader(header, value);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
send(body?: unknown) {
|
|
219
|
+
if (shouldSkipNetworkCapture(this._url)) {
|
|
220
|
+
return super.send(body as never);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const entry: NetworkEntry = {
|
|
224
|
+
id: networkId++,
|
|
225
|
+
method: this._method,
|
|
226
|
+
url: this._url,
|
|
227
|
+
startedAt: Date.now(),
|
|
228
|
+
requestHeaders: { ...this._requestHeaders },
|
|
229
|
+
requestBody: body ?? undefined,
|
|
230
|
+
responseHeaders: {},
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
this._entryId = entry.id;
|
|
234
|
+
entries.push(entry);
|
|
235
|
+
trimEntries();
|
|
236
|
+
notify();
|
|
237
|
+
|
|
238
|
+
this.addEventListener('readystatechange', () => {
|
|
239
|
+
if (this.readyState !== 4) {
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const current = entries.find((item) => item.id === this._entryId);
|
|
244
|
+
if (!current) {
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Some internal RN/Metro requests start as relative paths (e.g. /symbolicate).
|
|
249
|
+
// Re-check with responseURL at completion to apply host filters correctly.
|
|
250
|
+
const finalUrl = this.responseURL || this._url;
|
|
251
|
+
if (shouldSkipNetworkCapture(finalUrl)) {
|
|
252
|
+
const index = entries.findIndex((item) => item.id === this._entryId);
|
|
253
|
+
if (index >= 0) {
|
|
254
|
+
entries.splice(index, 1);
|
|
255
|
+
notify();
|
|
256
|
+
}
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const finishedAt = Date.now();
|
|
261
|
+
current.status = this.status;
|
|
262
|
+
current.finishedAt = finishedAt;
|
|
263
|
+
current.durationMs = finishedAt - current.startedAt;
|
|
264
|
+
current.responseHeaders = parseHeaders(this.getAllResponseHeaders());
|
|
265
|
+
try {
|
|
266
|
+
const parsedData = parseResponseData(this, current.responseHeaders);
|
|
267
|
+
if (isPromiseLike(parsedData)) {
|
|
268
|
+
current.responseData = '[Parsing blob response...]';
|
|
269
|
+
notify();
|
|
270
|
+
parsedData
|
|
271
|
+
.then((resolved) => {
|
|
272
|
+
current.responseData = resolved;
|
|
273
|
+
notify();
|
|
274
|
+
})
|
|
275
|
+
.catch(() => {
|
|
276
|
+
current.responseData = '[Blob response]';
|
|
277
|
+
notify();
|
|
278
|
+
});
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
current.responseData = parsedData;
|
|
282
|
+
} catch {
|
|
283
|
+
current.responseData = '[Unreadable response]';
|
|
284
|
+
}
|
|
285
|
+
notify();
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
return super.send(body as never);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
global.XMLHttpRequest = PatchedXMLHttpRequest as typeof XMLHttpRequest;
|
|
293
|
+
isInstalled = true;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
export function uninstallXhrProxy() {
|
|
297
|
+
if (!isInstalled || !OriginalXHR) {
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
global.XMLHttpRequest = OriginalXHR;
|
|
301
|
+
isInstalled = false;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
export function getNetworkEntries(): NetworkEntry[] {
|
|
305
|
+
return [...entries];
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
export function clearNetworkEntries() {
|
|
309
|
+
entries.length = 0;
|
|
310
|
+
notify();
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
export function subscribeNetworkEntries(listener: NetworkListener) {
|
|
314
|
+
listeners.add(listener);
|
|
315
|
+
listener(getNetworkEntries());
|
|
316
|
+
return () => {
|
|
317
|
+
listeners.delete(listener);
|
|
318
|
+
};
|
|
319
|
+
}
|
package/src/index.tsx
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { NativeModules, Platform } from 'react-native';
|
|
2
|
+
import { VConsole } from './VConsole';
|
|
3
|
+
import type { AppInfo, SystemInfo } from './types';
|
|
4
|
+
import type { VConsoleProps } from './VConsole';
|
|
5
|
+
|
|
6
|
+
const LINKING_ERROR =
|
|
7
|
+
`The package 'react-native-vconsole' doesn't seem to be linked. Make sure: \n\n` +
|
|
8
|
+
Platform.select({ ios: "- You have run 'pod install'\n", default: '' }) +
|
|
9
|
+
'- You rebuilt the app after installing the package\n' +
|
|
10
|
+
'- You are not using Expo Go\n';
|
|
11
|
+
|
|
12
|
+
const Vconsole = NativeModules.Vconsole
|
|
13
|
+
? NativeModules.Vconsole
|
|
14
|
+
: new Proxy(
|
|
15
|
+
{},
|
|
16
|
+
{
|
|
17
|
+
get() {
|
|
18
|
+
throw new Error(LINKING_ERROR);
|
|
19
|
+
},
|
|
20
|
+
}
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
export function multiply(a: number, b: number): Promise<number> {
|
|
24
|
+
return Vconsole.multiply(a, b);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function getSystemInfo(): Promise<SystemInfo> {
|
|
28
|
+
return Vconsole.getSystemInfo();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function getAppInfo(): Promise<AppInfo> {
|
|
32
|
+
return Vconsole.getAppInfo();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export { VConsole };
|
|
36
|
+
export type { AppInfo, SystemInfo, VConsoleProps };
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export type VConsoleTab = 'Log' | 'Network' | 'System' | 'App';
|
|
2
|
+
|
|
3
|
+
export type LogLevel = 'log' | 'info' | 'warn' | 'error';
|
|
4
|
+
|
|
5
|
+
export type LogFilterTab = 'All' | LogLevel;
|
|
6
|
+
|
|
7
|
+
export interface LogEntry {
|
|
8
|
+
id: number;
|
|
9
|
+
level: LogLevel;
|
|
10
|
+
args: unknown[];
|
|
11
|
+
text: string;
|
|
12
|
+
timestamp: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface NetworkEntry {
|
|
16
|
+
id: number;
|
|
17
|
+
method: string;
|
|
18
|
+
url: string;
|
|
19
|
+
status?: number;
|
|
20
|
+
startedAt: number;
|
|
21
|
+
finishedAt?: number;
|
|
22
|
+
durationMs?: number;
|
|
23
|
+
requestHeaders: Record<string, string>;
|
|
24
|
+
requestBody?: unknown;
|
|
25
|
+
responseHeaders: Record<string, string>;
|
|
26
|
+
responseData?: unknown;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface SystemInfo {
|
|
30
|
+
manufacturer: string;
|
|
31
|
+
model: string;
|
|
32
|
+
osVersion: string;
|
|
33
|
+
networkType: string;
|
|
34
|
+
isNetworkReachable: boolean;
|
|
35
|
+
totalMemory: number;
|
|
36
|
+
availableMemory: number;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface AppInfo {
|
|
40
|
+
appVersion: string;
|
|
41
|
+
buildNumber: string;
|
|
42
|
+
}
|
package/android/README.md
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
README
|
|
2
|
-
======
|
|
3
|
-
|
|
4
|
-
If you want to publish the lib as a maven dependency, follow these steps before publishing a new version to npm:
|
|
5
|
-
|
|
6
|
-
1. Be sure to have the Android [SDK](https://developer.android.com/studio/index.html) and [NDK](https://developer.android.com/ndk/guides/index.html) installed
|
|
7
|
-
2. Be sure to have a `local.properties` file in this folder that points to the Android SDK and NDK
|
|
8
|
-
```
|
|
9
|
-
ndk.dir=/Users/{username}/Library/Android/sdk/ndk-bundle
|
|
10
|
-
sdk.dir=/Users/{username}/Library/Android/sdk
|
|
11
|
-
```
|
|
12
|
-
3. Delete the `maven` folder
|
|
13
|
-
4. Run `./gradlew installArchives`
|
|
14
|
-
5. Verify that latest set of generated files is in the maven folder with the correct version number
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
package wiki.qdc.rn.vconsole;
|
|
2
|
-
|
|
3
|
-
import com.facebook.react.bridge.ReactApplicationContext;
|
|
4
|
-
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
|
5
|
-
import com.facebook.react.bridge.ReactMethod;
|
|
6
|
-
import com.facebook.react.bridge.Callback;
|
|
7
|
-
|
|
8
|
-
public class ReactNativeVconsoleModule extends ReactContextBaseJavaModule {
|
|
9
|
-
|
|
10
|
-
private final ReactApplicationContext reactContext;
|
|
11
|
-
|
|
12
|
-
public ReactNativeVconsoleModule(ReactApplicationContext reactContext) {
|
|
13
|
-
super(reactContext);
|
|
14
|
-
this.reactContext = reactContext;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
@Override
|
|
18
|
-
public String getName() {
|
|
19
|
-
return "ReactNativeVconsole";
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
@ReactMethod
|
|
23
|
-
public void sampleMethod(String stringArgument, int numberArgument, Callback callback) {
|
|
24
|
-
// TODO: Implement some actually useful functionality
|
|
25
|
-
callback.invoke("Received numberArgument: " + numberArgument + " stringArgument: " + stringArgument);
|
|
26
|
-
}
|
|
27
|
-
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
package wiki.qdc.rn.vconsole;
|
|
2
|
-
|
|
3
|
-
import java.util.Arrays;
|
|
4
|
-
import java.util.Collections;
|
|
5
|
-
import java.util.List;
|
|
6
|
-
|
|
7
|
-
import com.facebook.react.ReactPackage;
|
|
8
|
-
import com.facebook.react.bridge.NativeModule;
|
|
9
|
-
import com.facebook.react.bridge.ReactApplicationContext;
|
|
10
|
-
import com.facebook.react.uimanager.ViewManager;
|
|
11
|
-
import com.facebook.react.bridge.JavaScriptModule;
|
|
12
|
-
|
|
13
|
-
public class ReactNativeVconsolePackage implements ReactPackage {
|
|
14
|
-
@Override
|
|
15
|
-
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
|
|
16
|
-
return Arrays.<NativeModule>asList(new ReactNativeVconsoleModule(reactContext));
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
@Override
|
|
20
|
-
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
|
|
21
|
-
return Collections.emptyList();
|
|
22
|
-
}
|
|
23
|
-
}
|
package/index.js
DELETED
package/ios/.DS_Store
DELETED
|
Binary file
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
#import "ReactNativeVconsole.h"
|
|
2
|
-
|
|
3
|
-
@implementation ReactNativeVconsole
|
|
4
|
-
|
|
5
|
-
RCT_EXPORT_MODULE()
|
|
6
|
-
|
|
7
|
-
RCT_EXPORT_METHOD(sampleMethod:(NSString *)stringArgument numberParameter:(nonnull NSNumber *)numberArgument callback:(RCTResponseSenderBlock)callback)
|
|
8
|
-
{
|
|
9
|
-
// TODO: Implement some actually useful functionality
|
|
10
|
-
callback(@[[NSString stringWithFormat: @"numberArgument: %@ stringArgument: %@", numberArgument, stringArgument]]);
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
@end
|