capacitor-gleap-plugin 18.1.0 → 19.0.1
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/CapacitorGleapPlugin.podspec +7 -2
- package/Package.swift +31 -0
- package/README.md +298 -759
- package/android/build.gradle +1 -1
- package/android/src/main/java/io/gleapplugins/capacitor/GleapPlugin.java +202 -37
- package/dist/docs.json +288 -3893
- package/dist/esm/definitions.d.ts +162 -3
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/index.js +9 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/logs/body.d.ts +58 -0
- package/dist/esm/logs/body.js +303 -0
- package/dist/esm/logs/body.js.map +1 -0
- package/dist/esm/logs/consoleCapture.d.ts +29 -0
- package/dist/esm/logs/consoleCapture.js +189 -0
- package/dist/esm/logs/consoleCapture.js.map +1 -0
- package/dist/esm/logs/format.d.ts +16 -0
- package/dist/esm/logs/format.js +394 -0
- package/dist/esm/logs/format.js.map +1 -0
- package/dist/esm/logs/networkCapture.d.ts +65 -0
- package/dist/esm/logs/networkCapture.js +654 -0
- package/dist/esm/logs/networkCapture.js.map +1 -0
- package/dist/esm/logs/redaction.d.ts +54 -0
- package/dist/esm/logs/redaction.js +388 -0
- package/dist/esm/logs/redaction.js.map +1 -0
- package/dist/esm/logs/webViewLogs.d.ts +70 -0
- package/dist/esm/logs/webViewLogs.js +297 -0
- package/dist/esm/logs/webViewLogs.js.map +1 -0
- package/dist/esm/logs/zone.d.ts +5 -0
- package/dist/esm/logs/zone.js +15 -0
- package/dist/esm/logs/zone.js.map +1 -0
- package/dist/esm/web.d.ts +42 -2
- package/dist/esm/web.js +38 -0
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +2258 -1
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +2258 -1
- package/dist/plugin.js.map +1 -1
- package/ios/{Plugin → Sources/GleapPlugin}/GleapPlugin.swift +219 -12
- package/package.json +18 -4
- package/ios/Plugin/GleapPlugin.h +0 -10
- package/ios/Plugin/GleapPlugin.m +0 -68
- package/ios/Plugin/Info.plist +0 -24
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { GleapConsoleLogEntry, GleapLogConfig, GleapNetworkLogEntry } from '../definitions';
|
|
2
|
+
/** Pushes are batched: at most one per channel every PUSH_DELAY_MS. */
|
|
3
|
+
export declare const PUSH_DELAY_MS = 500;
|
|
4
|
+
export interface LogTarget {
|
|
5
|
+
attachConsoleLogs(options: {
|
|
6
|
+
logs: GleapConsoleLogEntry[];
|
|
7
|
+
}): Promise<unknown>;
|
|
8
|
+
attachNetworkLogs(options: {
|
|
9
|
+
logs: GleapNetworkLogEntry[];
|
|
10
|
+
}): Promise<unknown>;
|
|
11
|
+
addListener(eventName: 'logConfigLoaded', listenerFunc: (config: GleapLogConfig) => void): Promise<unknown>;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Captures console output and fetch/XHR traffic inside the WebView of a native (iOS / Android) app
|
|
15
|
+
* and hands them to the native SDK, which attaches them to tickets. The native SDKs cannot see
|
|
16
|
+
* either: WebView requests do not go through URLSession / OkHttp, and console output only reaches
|
|
17
|
+
* the native log in debug builds.
|
|
18
|
+
*/
|
|
19
|
+
export declare class WebViewLogCapture {
|
|
20
|
+
private readonly win;
|
|
21
|
+
private readonly target;
|
|
22
|
+
private readonly platform;
|
|
23
|
+
private consoleCapture;
|
|
24
|
+
private readonly networkCapture;
|
|
25
|
+
private readonly consoleChannel;
|
|
26
|
+
private readonly networkChannel;
|
|
27
|
+
private isNetworkEnabled;
|
|
28
|
+
private remoteProps;
|
|
29
|
+
private localProps;
|
|
30
|
+
private remoteBlacklist;
|
|
31
|
+
private localBlacklist;
|
|
32
|
+
private rules;
|
|
33
|
+
private rulesVersion;
|
|
34
|
+
private redactedEntries;
|
|
35
|
+
constructor(win: Window, target: LogTarget, platform: string);
|
|
36
|
+
start(): void;
|
|
37
|
+
/**
|
|
38
|
+
* Android debug builds (Capacitor's loggingBehavior) write the WebView console to logcat, which the
|
|
39
|
+
* Android SDK already reads: capturing it here too would log every line twice. On iOS, Capacitor
|
|
40
|
+
* prints it to stdout as "⚡️ [level] - message"; the iOS SDK drops those lines for Capacitor apps
|
|
41
|
+
* (application type CAPACITOR, set in initialize), so iOS always captures here.
|
|
42
|
+
*/
|
|
43
|
+
private nativeLogsConsole;
|
|
44
|
+
private listenForConfig;
|
|
45
|
+
applyRemoteConfig(config: GleapLogConfig | null | undefined): void;
|
|
46
|
+
setLocalPropsToIgnore(props: unknown): void;
|
|
47
|
+
setLocalBlacklist(blacklist: unknown): void;
|
|
48
|
+
/** disableConsoleLogOverwrite(): restore the console and drop what was recorded (also natively). */
|
|
49
|
+
disableConsole(): void;
|
|
50
|
+
onInitialize(): void;
|
|
51
|
+
/** Pushes pending changes now (before a plugin call that can produce a report). */
|
|
52
|
+
flushNow(): void;
|
|
53
|
+
private updateRules;
|
|
54
|
+
private canPush;
|
|
55
|
+
private markDirty;
|
|
56
|
+
private flushChannel;
|
|
57
|
+
private onPushFailed;
|
|
58
|
+
/** Redacted copies of the buffered entries; unchanged entries reuse their previous copy. */
|
|
59
|
+
private buildNetworkLogs;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Starts the WebView log capture on iOS and Android (not on web, where the JavaScript SDK records
|
|
63
|
+
* the console and network itself). Safe to call more than once.
|
|
64
|
+
*/
|
|
65
|
+
export declare function installWebViewLogCapture(target: LogTarget): WebViewLogCapture | null;
|
|
66
|
+
/**
|
|
67
|
+
* Wraps the registered plugin so the calls that affect the WebView capture reach it before they go
|
|
68
|
+
* to native. Every other call passes through unchanged.
|
|
69
|
+
*/
|
|
70
|
+
export declare function wrapPluginWithLogCapture<T extends object>(plugin: T, capture: WebViewLogCapture): T;
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
import { Capacitor } from '@capacitor/core';
|
|
2
|
+
import { ConsoleCapture } from './consoleCapture';
|
|
3
|
+
import { NetworkCapture } from './networkCapture';
|
|
4
|
+
import { createRedactionRules, isBlacklistedUrl, redactNetworkLogEntryOrStrip } from './redaction';
|
|
5
|
+
import { runOutsideZone } from './zone';
|
|
6
|
+
/** Pushes are batched: at most one per channel every PUSH_DELAY_MS. */
|
|
7
|
+
export const PUSH_DELAY_MS = 500;
|
|
8
|
+
/** A channel stops pushing after this many failed pushes in a row (e.g. an outdated native plugin). */
|
|
9
|
+
const MAX_FAILED_PUSHES = 3;
|
|
10
|
+
const GLOBAL_KEY = '__gleapWebViewLogCapture';
|
|
11
|
+
/** Plugin calls that can lead to a report: the latest logs are pushed right before them. */
|
|
12
|
+
const FLUSH_BEFORE_METHODS = [
|
|
13
|
+
'sendSilentCrashReport',
|
|
14
|
+
'open',
|
|
15
|
+
'startFeedbackFlow',
|
|
16
|
+
'startClassicForm',
|
|
17
|
+
'startConversation',
|
|
18
|
+
'openConversation',
|
|
19
|
+
'openConversations',
|
|
20
|
+
'startBot',
|
|
21
|
+
];
|
|
22
|
+
const createChannel = (name) => ({
|
|
23
|
+
name,
|
|
24
|
+
isDirty: false,
|
|
25
|
+
timer: null,
|
|
26
|
+
failures: 0,
|
|
27
|
+
isStopped: false,
|
|
28
|
+
hasPushedLogs: false,
|
|
29
|
+
});
|
|
30
|
+
const toStringArray = (value) => Array.isArray(value) ? value.filter((item) => typeof item === 'string') : [];
|
|
31
|
+
/**
|
|
32
|
+
* Captures console output and fetch/XHR traffic inside the WebView of a native (iOS / Android) app
|
|
33
|
+
* and hands them to the native SDK, which attaches them to tickets. The native SDKs cannot see
|
|
34
|
+
* either: WebView requests do not go through URLSession / OkHttp, and console output only reaches
|
|
35
|
+
* the native log in debug builds.
|
|
36
|
+
*/
|
|
37
|
+
export class WebViewLogCapture {
|
|
38
|
+
constructor(win, target, platform) {
|
|
39
|
+
this.win = win;
|
|
40
|
+
this.target = target;
|
|
41
|
+
this.platform = platform;
|
|
42
|
+
this.consoleCapture = null;
|
|
43
|
+
this.consoleChannel = createChannel('console');
|
|
44
|
+
this.networkChannel = createChannel('network');
|
|
45
|
+
this.isNetworkEnabled = false;
|
|
46
|
+
this.remoteProps = [];
|
|
47
|
+
this.localProps = [];
|
|
48
|
+
this.remoteBlacklist = [];
|
|
49
|
+
this.localBlacklist = [];
|
|
50
|
+
this.rules = createRedactionRules([], []);
|
|
51
|
+
this.rulesVersion = 0;
|
|
52
|
+
this.redactedEntries = new Map();
|
|
53
|
+
this.networkCapture = new NetworkCapture(win, () => this.markDirty(this.networkChannel));
|
|
54
|
+
}
|
|
55
|
+
start() {
|
|
56
|
+
if (!this.nativeLogsConsole()) {
|
|
57
|
+
this.consoleCapture = new ConsoleCapture(this.win, () => this.markDirty(this.consoleChannel));
|
|
58
|
+
this.consoleCapture.install();
|
|
59
|
+
}
|
|
60
|
+
// The hooks go in now, but requests are only recorded once the project config says network
|
|
61
|
+
// logs are enabled (like the JavaScript SDK), so nothing is held in memory before that.
|
|
62
|
+
this.networkCapture.install();
|
|
63
|
+
this.listenForConfig();
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Android debug builds (Capacitor's loggingBehavior) write the WebView console to logcat, which the
|
|
67
|
+
* Android SDK already reads: capturing it here too would log every line twice. On iOS, Capacitor
|
|
68
|
+
* prints it to stdout as "⚡️ [level] - message"; the iOS SDK drops those lines for Capacitor apps
|
|
69
|
+
* (application type CAPACITOR, set in initialize), so iOS always captures here.
|
|
70
|
+
*/
|
|
71
|
+
nativeLogsConsole() {
|
|
72
|
+
const capacitor = this.win.Capacitor;
|
|
73
|
+
return this.platform === 'android' && !!capacitor && capacitor.isLoggingEnabled === true;
|
|
74
|
+
}
|
|
75
|
+
listenForConfig() {
|
|
76
|
+
try {
|
|
77
|
+
const handle = this.target.addListener('logConfigLoaded', (config) => this.applyRemoteConfig(config));
|
|
78
|
+
if (handle && typeof handle.catch === 'function') {
|
|
79
|
+
handle.catch(() => undefined);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
catch (e) {
|
|
83
|
+
// The native side predates the event: network logs stay off.
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
applyRemoteConfig(config) {
|
|
87
|
+
try {
|
|
88
|
+
if (!config || typeof config !== 'object') {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
this.remoteProps = toStringArray(config.networkLogPropsToIgnore);
|
|
92
|
+
this.remoteBlacklist = toStringArray(config.networkLogBlacklist);
|
|
93
|
+
this.updateRules();
|
|
94
|
+
this.isNetworkEnabled = config.enableNetworkLogs === true;
|
|
95
|
+
this.networkCapture.setActive(this.isNetworkEnabled);
|
|
96
|
+
this.markDirty(this.networkChannel);
|
|
97
|
+
}
|
|
98
|
+
catch (e) {
|
|
99
|
+
// Ignore.
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
setLocalPropsToIgnore(props) {
|
|
103
|
+
this.localProps = toStringArray(props);
|
|
104
|
+
this.updateRules();
|
|
105
|
+
}
|
|
106
|
+
setLocalBlacklist(blacklist) {
|
|
107
|
+
this.localBlacklist = toStringArray(blacklist);
|
|
108
|
+
this.updateRules();
|
|
109
|
+
}
|
|
110
|
+
/** disableConsoleLogOverwrite(): restore the console and drop what was recorded (also natively). */
|
|
111
|
+
disableConsole() {
|
|
112
|
+
try {
|
|
113
|
+
if (this.consoleCapture) {
|
|
114
|
+
this.consoleCapture.uninstall();
|
|
115
|
+
this.consoleCapture = null;
|
|
116
|
+
}
|
|
117
|
+
this.markDirty(this.consoleChannel);
|
|
118
|
+
this.flushChannel(this.consoleChannel);
|
|
119
|
+
}
|
|
120
|
+
catch (e) {
|
|
121
|
+
// Ignore.
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
onInitialize() {
|
|
125
|
+
this.markDirty(this.consoleChannel);
|
|
126
|
+
this.markDirty(this.networkChannel);
|
|
127
|
+
}
|
|
128
|
+
/** Pushes pending changes now (before a plugin call that can produce a report). */
|
|
129
|
+
flushNow() {
|
|
130
|
+
try {
|
|
131
|
+
this.flushChannel(this.consoleChannel);
|
|
132
|
+
this.flushChannel(this.networkChannel);
|
|
133
|
+
}
|
|
134
|
+
catch (e) {
|
|
135
|
+
// Ignore.
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
updateRules() {
|
|
139
|
+
this.rules = createRedactionRules([...this.remoteProps, ...this.localProps], [...this.remoteBlacklist, ...this.localBlacklist]);
|
|
140
|
+
this.rulesVersion++;
|
|
141
|
+
this.markDirty(this.networkChannel);
|
|
142
|
+
}
|
|
143
|
+
canPush(channel) {
|
|
144
|
+
if (channel.isStopped) {
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
if (channel.name === 'network') {
|
|
148
|
+
return this.isNetworkEnabled || channel.hasPushedLogs;
|
|
149
|
+
}
|
|
150
|
+
return true;
|
|
151
|
+
}
|
|
152
|
+
markDirty(channel) {
|
|
153
|
+
channel.isDirty = true;
|
|
154
|
+
if (channel.timer !== null || !this.canPush(channel)) {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
try {
|
|
158
|
+
channel.timer = runOutsideZone(() => setTimeout(() => {
|
|
159
|
+
channel.timer = null;
|
|
160
|
+
this.flushChannel(channel);
|
|
161
|
+
}, PUSH_DELAY_MS));
|
|
162
|
+
}
|
|
163
|
+
catch (e) {
|
|
164
|
+
channel.timer = null;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
flushChannel(channel) {
|
|
168
|
+
if (channel.timer !== null) {
|
|
169
|
+
clearTimeout(channel.timer);
|
|
170
|
+
channel.timer = null;
|
|
171
|
+
}
|
|
172
|
+
if (!channel.isDirty || !this.canPush(channel)) {
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
channel.isDirty = false;
|
|
176
|
+
let push;
|
|
177
|
+
if (channel.name === 'console') {
|
|
178
|
+
const logs = this.consoleCapture ? this.consoleCapture.getEntries() : [];
|
|
179
|
+
if (logs.length === 0 && !channel.hasPushedLogs) {
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
channel.hasPushedLogs = logs.length > 0;
|
|
183
|
+
push = runOutsideZone(() => this.target.attachConsoleLogs({ logs }));
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
const logs = this.isNetworkEnabled ? this.buildNetworkLogs() : [];
|
|
187
|
+
if (logs.length === 0 && !channel.hasPushedLogs) {
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
channel.hasPushedLogs = logs.length > 0;
|
|
191
|
+
push = runOutsideZone(() => this.target.attachNetworkLogs({ logs }));
|
|
192
|
+
}
|
|
193
|
+
try {
|
|
194
|
+
Promise.resolve(push).then(() => {
|
|
195
|
+
channel.failures = 0;
|
|
196
|
+
}, () => this.onPushFailed(channel));
|
|
197
|
+
}
|
|
198
|
+
catch (e) {
|
|
199
|
+
this.onPushFailed(channel);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
onPushFailed(channel) {
|
|
203
|
+
channel.failures++;
|
|
204
|
+
if (channel.failures >= MAX_FAILED_PUSHES) {
|
|
205
|
+
channel.isStopped = true;
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
this.markDirty(channel);
|
|
209
|
+
}
|
|
210
|
+
/** Redacted copies of the buffered entries; unchanged entries reuse their previous copy. */
|
|
211
|
+
buildNetworkLogs() {
|
|
212
|
+
const logs = [];
|
|
213
|
+
const cache = new Map();
|
|
214
|
+
for (const snapshot of this.networkCapture.getSnapshots()) {
|
|
215
|
+
let cached = this.redactedEntries.get(snapshot.id);
|
|
216
|
+
if (!cached || cached.version !== snapshot.version || cached.rulesVersion !== this.rulesVersion) {
|
|
217
|
+
cached = {
|
|
218
|
+
version: snapshot.version,
|
|
219
|
+
rulesVersion: this.rulesVersion,
|
|
220
|
+
entry: isBlacklistedUrl(snapshot.entry.url, this.rules)
|
|
221
|
+
? null
|
|
222
|
+
: redactNetworkLogEntryOrStrip(snapshot.entry, this.rules),
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
cache.set(snapshot.id, cached);
|
|
226
|
+
if (cached.entry) {
|
|
227
|
+
logs.push(cached.entry);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
this.redactedEntries = cache;
|
|
231
|
+
return logs;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Starts the WebView log capture on iOS and Android (not on web, where the JavaScript SDK records
|
|
236
|
+
* the console and network itself). Safe to call more than once.
|
|
237
|
+
*/
|
|
238
|
+
export function installWebViewLogCapture(target) {
|
|
239
|
+
try {
|
|
240
|
+
if (typeof window === 'undefined' || !Capacitor.isNativePlatform()) {
|
|
241
|
+
return null;
|
|
242
|
+
}
|
|
243
|
+
const win = window;
|
|
244
|
+
const existing = win[GLOBAL_KEY];
|
|
245
|
+
if (existing) {
|
|
246
|
+
return existing;
|
|
247
|
+
}
|
|
248
|
+
const capture = new WebViewLogCapture(window, target, Capacitor.getPlatform());
|
|
249
|
+
win[GLOBAL_KEY] = capture;
|
|
250
|
+
capture.start();
|
|
251
|
+
return capture;
|
|
252
|
+
}
|
|
253
|
+
catch (e) {
|
|
254
|
+
return null;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Wraps the registered plugin so the calls that affect the WebView capture reach it before they go
|
|
259
|
+
* to native. Every other call passes through unchanged.
|
|
260
|
+
*/
|
|
261
|
+
export function wrapPluginWithLogCapture(plugin, capture) {
|
|
262
|
+
return new Proxy(plugin, {
|
|
263
|
+
get(target, property, receiver) {
|
|
264
|
+
const value = Reflect.get(target, property, receiver);
|
|
265
|
+
if (typeof property !== 'string' || typeof value !== 'function') {
|
|
266
|
+
return value;
|
|
267
|
+
}
|
|
268
|
+
const method = value;
|
|
269
|
+
const before = (hook) => (...args) => {
|
|
270
|
+
try {
|
|
271
|
+
const options = args[0];
|
|
272
|
+
hook(options && typeof options === 'object' ? options : {});
|
|
273
|
+
}
|
|
274
|
+
catch (e) {
|
|
275
|
+
// Never break the plugin call.
|
|
276
|
+
}
|
|
277
|
+
return method(...args);
|
|
278
|
+
};
|
|
279
|
+
switch (property) {
|
|
280
|
+
case 'initialize':
|
|
281
|
+
return before(() => capture.onInitialize());
|
|
282
|
+
case 'setNetworkLogPropsToIgnore':
|
|
283
|
+
return before((options) => capture.setLocalPropsToIgnore(options.propsToIgnore));
|
|
284
|
+
case 'setNetworkLogsBlacklist':
|
|
285
|
+
return before((options) => capture.setLocalBlacklist(options.blacklist));
|
|
286
|
+
case 'disableConsoleLogOverwrite':
|
|
287
|
+
return before(() => capture.disableConsole());
|
|
288
|
+
default:
|
|
289
|
+
if (FLUSH_BEFORE_METHODS.indexOf(property) > -1) {
|
|
290
|
+
return before(() => capture.flushNow());
|
|
291
|
+
}
|
|
292
|
+
return value;
|
|
293
|
+
}
|
|
294
|
+
},
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
//# sourceMappingURL=webViewLogs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webViewLogs.js","sourceRoot":"","sources":["../../../src/logs/webViewLogs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAI5C,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAElD,OAAO,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,4BAA4B,EAAE,MAAM,aAAa,CAAC;AACnG,OAAO,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AAExC,uEAAuE;AACvE,MAAM,CAAC,MAAM,aAAa,GAAG,GAAG,CAAC;AACjC,uGAAuG;AACvG,MAAM,iBAAiB,GAAG,CAAC,CAAC;AAC5B,MAAM,UAAU,GAAG,0BAA0B,CAAC;AAE9C,4FAA4F;AAC5F,MAAM,oBAAoB,GAAG;IAC3B,uBAAuB;IACvB,MAAM;IACN,mBAAmB;IACnB,kBAAkB;IAClB,mBAAmB;IACnB,kBAAkB;IAClB,mBAAmB;IACnB,UAAU;CACX,CAAC;AAwBF,MAAM,aAAa,GAAG,CAAC,IAAqB,EAAW,EAAE,CAAC,CAAC;IACzD,IAAI;IACJ,OAAO,EAAE,KAAK;IACd,KAAK,EAAE,IAAI;IACX,QAAQ,EAAE,CAAC;IACX,SAAS,EAAE,KAAK;IAChB,aAAa,EAAE,KAAK;CACrB,CAAC,CAAC;AAEH,MAAM,aAAa,GAAG,CAAC,KAAc,EAAY,EAAE,CACjD,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAkB,EAAE,CAAC,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AAE/F;;;;;GAKG;AACH,MAAM,OAAO,iBAAiB;IAc5B,YACmB,GAAW,EACX,MAAiB,EACjB,QAAgB;QAFhB,QAAG,GAAH,GAAG,CAAQ;QACX,WAAM,GAAN,MAAM,CAAW;QACjB,aAAQ,GAAR,QAAQ,CAAQ;QAhB3B,mBAAc,GAA0B,IAAI,CAAC;QAEpC,mBAAc,GAAG,aAAa,CAAC,SAAS,CAAC,CAAC;QAC1C,mBAAc,GAAG,aAAa,CAAC,SAAS,CAAC,CAAC;QACnD,qBAAgB,GAAG,KAAK,CAAC;QACzB,gBAAW,GAAa,EAAE,CAAC;QAC3B,eAAU,GAAa,EAAE,CAAC;QAC1B,oBAAe,GAAa,EAAE,CAAC;QAC/B,mBAAc,GAAa,EAAE,CAAC;QAC9B,UAAK,GAAmB,oBAAoB,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;QACrD,iBAAY,GAAG,CAAC,CAAC;QACjB,oBAAe,GAAG,IAAI,GAAG,EAAuB,CAAC;QAOvD,IAAI,CAAC,cAAc,GAAG,IAAI,cAAc,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC;IAC3F,CAAC;IAED,KAAK;QACH,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,EAAE;YAC7B,IAAI,CAAC,cAAc,GAAG,IAAI,cAAc,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC;YAC9F,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE,CAAC;SAC/B;QACD,2FAA2F;QAC3F,wFAAwF;QACxF,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE,CAAC;QAC9B,IAAI,CAAC,eAAe,EAAE,CAAC;IACzB,CAAC;IAED;;;;;OAKG;IACK,iBAAiB;QACvB,MAAM,SAAS,GACb,IAAI,CAAC,GAGN,CAAC,SAAS,CAAC;QACZ,OAAO,IAAI,CAAC,QAAQ,KAAK,SAAS,IAAI,CAAC,CAAC,SAAS,IAAI,SAAS,CAAC,gBAAgB,KAAK,IAAI,CAAC;IAC3F,CAAC;IAEO,eAAe;QACrB,IAAI;YACF,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,iBAAiB,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC,CAAC;YACtG,IAAI,MAAM,IAAI,OAAO,MAAM,CAAC,KAAK,KAAK,UAAU,EAAE;gBAChD,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;aAC/B;SACF;QAAC,OAAO,CAAC,EAAE;YACV,6DAA6D;SAC9D;IACH,CAAC;IAED,iBAAiB,CAAC,MAAyC;QACzD,IAAI;YACF,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE;gBACzC,OAAO;aACR;YACD,IAAI,CAAC,WAAW,GAAG,aAAa,CAAC,MAAM,CAAC,uBAAuB,CAAC,CAAC;YACjE,IAAI,CAAC,eAAe,GAAG,aAAa,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAC;YACjE,IAAI,CAAC,WAAW,EAAE,CAAC;YACnB,IAAI,CAAC,gBAAgB,GAAG,MAAM,CAAC,iBAAiB,KAAK,IAAI,CAAC;YAC1D,IAAI,CAAC,cAAc,CAAC,SAAS,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;YACrD,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;SACrC;QAAC,OAAO,CAAC,EAAE;YACV,UAAU;SACX;IACH,CAAC;IAED,qBAAqB,CAAC,KAAc;QAClC,IAAI,CAAC,UAAU,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;QACvC,IAAI,CAAC,WAAW,EAAE,CAAC;IACrB,CAAC;IAED,iBAAiB,CAAC,SAAkB;QAClC,IAAI,CAAC,cAAc,GAAG,aAAa,CAAC,SAAS,CAAC,CAAC;QAC/C,IAAI,CAAC,WAAW,EAAE,CAAC;IACrB,CAAC;IAED,oGAAoG;IACpG,cAAc;QACZ,IAAI;YACF,IAAI,IAAI,CAAC,cAAc,EAAE;gBACvB,IAAI,CAAC,cAAc,CAAC,SAAS,EAAE,CAAC;gBAChC,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;aAC5B;YACD,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;YACpC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;SACxC;QAAC,OAAO,CAAC,EAAE;YACV,UAAU;SACX;IACH,CAAC;IAED,YAAY;QACV,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QACpC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IACtC,CAAC;IAED,mFAAmF;IACnF,QAAQ;QACN,IAAI;YACF,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;YACvC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;SACxC;QAAC,OAAO,CAAC,EAAE;YACV,UAAU;SACX;IACH,CAAC;IAEO,WAAW;QACjB,IAAI,CAAC,KAAK,GAAG,oBAAoB,CAC/B,CAAC,GAAG,IAAI,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,UAAU,CAAC,EACzC,CAAC,GAAG,IAAI,CAAC,eAAe,EAAE,GAAG,IAAI,CAAC,cAAc,CAAC,CAClD,CAAC;QACF,IAAI,CAAC,YAAY,EAAE,CAAC;QACpB,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IACtC,CAAC;IAEO,OAAO,CAAC,OAAgB;QAC9B,IAAI,OAAO,CAAC,SAAS,EAAE;YACrB,OAAO,KAAK,CAAC;SACd;QACD,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS,EAAE;YAC9B,OAAO,IAAI,CAAC,gBAAgB,IAAI,OAAO,CAAC,aAAa,CAAC;SACvD;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAEO,SAAS,CAAC,OAAgB;QAChC,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;QACvB,IAAI,OAAO,CAAC,KAAK,KAAK,IAAI,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE;YACpD,OAAO;SACR;QACD,IAAI;YACF,OAAO,CAAC,KAAK,GAAG,cAAc,CAAC,GAAG,EAAE,CAClC,UAAU,CAAC,GAAG,EAAE;gBACd,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC;gBACrB,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;YAC7B,CAAC,EAAE,aAAa,CAAC,CAClB,CAAC;SACH;QAAC,OAAO,CAAC,EAAE;YACV,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC;SACtB;IACH,CAAC;IAEO,YAAY,CAAC,OAAgB;QACnC,IAAI,OAAO,CAAC,KAAK,KAAK,IAAI,EAAE;YAC1B,YAAY,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;YAC5B,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC;SACtB;QACD,IAAI,CAAC,OAAO,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE;YAC9C,OAAO;SACR;QACD,OAAO,CAAC,OAAO,GAAG,KAAK,CAAC;QAExB,IAAI,IAAsB,CAAC;QAC3B,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS,EAAE;YAC9B,MAAM,IAAI,GAAG,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACzE,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE;gBAC/C,OAAO;aACR;YACD,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;YACxC,IAAI,GAAG,cAAc,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,iBAAiB,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;SACtE;aAAM;YACL,MAAM,IAAI,GAAG,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC,IAAI,CAAC,gBAAgB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAClE,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE;gBAC/C,OAAO;aACR;YACD,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;YACxC,IAAI,GAAG,cAAc,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,iBAAiB,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;SACtE;QAED,IAAI;YACF,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,CACxB,GAAG,EAAE;gBACH,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;YACvB,CAAC,EACD,GAAG,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,CACjC,CAAC;SACH;QAAC,OAAO,CAAC,EAAE;YACV,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;SAC5B;IACH,CAAC;IAEO,YAAY,CAAC,OAAgB;QACnC,OAAO,CAAC,QAAQ,EAAE,CAAC;QACnB,IAAI,OAAO,CAAC,QAAQ,IAAI,iBAAiB,EAAE;YACzC,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;YACzB,OAAO;SACR;QACD,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IAC1B,CAAC;IAED,4FAA4F;IACpF,gBAAgB;QACtB,MAAM,IAAI,GAA2B,EAAE,CAAC;QACxC,MAAM,KAAK,GAAG,IAAI,GAAG,EAAuB,CAAC;QAC7C,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC,cAAc,CAAC,YAAY,EAAE,EAAE;YACzD,IAAI,MAAM,GAAG,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;YACnD,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,OAAO,KAAK,QAAQ,CAAC,OAAO,IAAI,MAAM,CAAC,YAAY,KAAK,IAAI,CAAC,YAAY,EAAE;gBAC/F,MAAM,GAAG;oBACP,OAAO,EAAE,QAAQ,CAAC,OAAO;oBACzB,YAAY,EAAE,IAAI,CAAC,YAAY;oBAC/B,KAAK,EAAE,gBAAgB,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC;wBACrD,CAAC,CAAC,IAAI;wBACN,CAAC,CAAC,4BAA4B,CAAC,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC;iBAC7D,CAAC;aACH;YACD,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;YAC/B,IAAI,MAAM,CAAC,KAAK,EAAE;gBAChB,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;aACzB;SACF;QACD,IAAI,CAAC,eAAe,GAAG,KAAK,CAAC;QAC7B,OAAO,IAAI,CAAC;IACd,CAAC;CACF;AAED;;;GAGG;AACH,MAAM,UAAU,wBAAwB,CAAC,MAAiB;IACxD,IAAI;QACF,IAAI,OAAO,MAAM,KAAK,WAAW,IAAI,CAAC,SAAS,CAAC,gBAAgB,EAAE,EAAE;YAClE,OAAO,IAAI,CAAC;SACb;QACD,MAAM,GAAG,GAAG,MAAyD,CAAC;QACtE,MAAM,QAAQ,GAAG,GAAG,CAAC,UAAU,CAAC,CAAC;QACjC,IAAI,QAAQ,EAAE;YACZ,OAAO,QAAQ,CAAC;SACjB;QACD,MAAM,OAAO,GAAG,IAAI,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC,WAAW,EAAE,CAAC,CAAC;QAC/E,GAAG,CAAC,UAAU,CAAC,GAAG,OAAO,CAAC;QAC1B,OAAO,CAAC,KAAK,EAAE,CAAC;QAChB,OAAO,OAAO,CAAC;KAChB;IAAC,OAAO,CAAC,EAAE;QACV,OAAO,IAAI,CAAC;KACb;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,wBAAwB,CAAmB,MAAS,EAAE,OAA0B;IAC9F,OAAO,IAAI,KAAK,CAAC,MAAM,EAAE;QACvB,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ;YAC5B,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;YACtD,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,OAAO,KAAK,KAAK,UAAU,EAAE;gBAC/D,OAAO,KAAK,CAAC;aACd;YACD,MAAM,MAAM,GAAG,KAAwC,CAAC;YACxD,MAAM,MAAM,GACV,CAAC,IAAmD,EAAE,EAAE,CACxD,CAAC,GAAG,IAAe,EAAE,EAAE;gBACrB,IAAI;oBACF,MAAM,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;oBACxB,IAAI,CAAC,OAAO,IAAI,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAE,OAAsC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;iBAC7F;gBAAC,OAAO,CAAC,EAAE;oBACV,+BAA+B;iBAChC;gBACD,OAAO,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC;YACzB,CAAC,CAAC;YAEJ,QAAQ,QAAQ,EAAE;gBAChB,KAAK,YAAY;oBACf,OAAO,MAAM,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC;gBAC9C,KAAK,4BAA4B;oBAC/B,OAAO,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,qBAAqB,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC;gBACnF,KAAK,yBAAyB;oBAC5B,OAAO,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,iBAAiB,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC;gBAC3E,KAAK,4BAA4B;oBAC/B,OAAO,MAAM,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,cAAc,EAAE,CAAC,CAAC;gBAChD;oBACE,IAAI,oBAAoB,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE;wBAC/C,OAAO,MAAM,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;qBACzC;oBACD,OAAO,KAAK,CAAC;aAChB;QACH,CAAC;KACF,CAAC,CAAC;AACL,CAAC","sourcesContent":["import { Capacitor } from '@capacitor/core';\n\nimport type { GleapConsoleLogEntry, GleapLogConfig, GleapNetworkLogEntry } from '../definitions';\n\nimport { ConsoleCapture } from './consoleCapture';\nimport { NetworkCapture } from './networkCapture';\nimport type { RedactionRules } from './redaction';\nimport { createRedactionRules, isBlacklistedUrl, redactNetworkLogEntryOrStrip } from './redaction';\nimport { runOutsideZone } from './zone';\n\n/** Pushes are batched: at most one per channel every PUSH_DELAY_MS. */\nexport const PUSH_DELAY_MS = 500;\n/** A channel stops pushing after this many failed pushes in a row (e.g. an outdated native plugin). */\nconst MAX_FAILED_PUSHES = 3;\nconst GLOBAL_KEY = '__gleapWebViewLogCapture';\n\n/** Plugin calls that can lead to a report: the latest logs are pushed right before them. */\nconst FLUSH_BEFORE_METHODS = [\n 'sendSilentCrashReport',\n 'open',\n 'startFeedbackFlow',\n 'startClassicForm',\n 'startConversation',\n 'openConversation',\n 'openConversations',\n 'startBot',\n];\n\nexport interface LogTarget {\n attachConsoleLogs(options: { logs: GleapConsoleLogEntry[] }): Promise<unknown>;\n attachNetworkLogs(options: { logs: GleapNetworkLogEntry[] }): Promise<unknown>;\n addListener(eventName: 'logConfigLoaded', listenerFunc: (config: GleapLogConfig) => void): Promise<unknown>;\n}\n\ninterface Channel {\n name: 'console' | 'network';\n isDirty: boolean;\n timer: ReturnType<typeof setTimeout> | null;\n failures: number;\n isStopped: boolean;\n /** Whether native currently holds a non-empty list (so an empty list must be pushed to clear it). */\n hasPushedLogs: boolean;\n}\n\ninterface CachedEntry {\n version: number;\n rulesVersion: number;\n entry: GleapNetworkLogEntry | null;\n}\n\nconst createChannel = (name: Channel['name']): Channel => ({\n name,\n isDirty: false,\n timer: null,\n failures: 0,\n isStopped: false,\n hasPushedLogs: false,\n});\n\nconst toStringArray = (value: unknown): string[] =>\n Array.isArray(value) ? value.filter((item): item is string => typeof item === 'string') : [];\n\n/**\n * Captures console output and fetch/XHR traffic inside the WebView of a native (iOS / Android) app\n * and hands them to the native SDK, which attaches them to tickets. The native SDKs cannot see\n * either: WebView requests do not go through URLSession / OkHttp, and console output only reaches\n * the native log in debug builds.\n */\nexport class WebViewLogCapture {\n private consoleCapture: ConsoleCapture | null = null;\n private readonly networkCapture: NetworkCapture;\n private readonly consoleChannel = createChannel('console');\n private readonly networkChannel = createChannel('network');\n private isNetworkEnabled = false;\n private remoteProps: string[] = [];\n private localProps: string[] = [];\n private remoteBlacklist: string[] = [];\n private localBlacklist: string[] = [];\n private rules: RedactionRules = createRedactionRules([], []);\n private rulesVersion = 0;\n private redactedEntries = new Map<number, CachedEntry>();\n\n constructor(\n private readonly win: Window,\n private readonly target: LogTarget,\n private readonly platform: string,\n ) {\n this.networkCapture = new NetworkCapture(win, () => this.markDirty(this.networkChannel));\n }\n\n start(): void {\n if (!this.nativeLogsConsole()) {\n this.consoleCapture = new ConsoleCapture(this.win, () => this.markDirty(this.consoleChannel));\n this.consoleCapture.install();\n }\n // The hooks go in now, but requests are only recorded once the project config says network\n // logs are enabled (like the JavaScript SDK), so nothing is held in memory before that.\n this.networkCapture.install();\n this.listenForConfig();\n }\n\n /**\n * Android debug builds (Capacitor's loggingBehavior) write the WebView console to logcat, which the\n * Android SDK already reads: capturing it here too would log every line twice. On iOS, Capacitor\n * prints it to stdout as \"⚡️ [level] - message\"; the iOS SDK drops those lines for Capacitor apps\n * (application type CAPACITOR, set in initialize), so iOS always captures here.\n */\n private nativeLogsConsole(): boolean {\n const capacitor = (\n this.win as unknown as {\n Capacitor?: { isLoggingEnabled?: unknown };\n }\n ).Capacitor;\n return this.platform === 'android' && !!capacitor && capacitor.isLoggingEnabled === true;\n }\n\n private listenForConfig(): void {\n try {\n const handle = this.target.addListener('logConfigLoaded', (config) => this.applyRemoteConfig(config));\n if (handle && typeof handle.catch === 'function') {\n handle.catch(() => undefined);\n }\n } catch (e) {\n // The native side predates the event: network logs stay off.\n }\n }\n\n applyRemoteConfig(config: GleapLogConfig | null | undefined): void {\n try {\n if (!config || typeof config !== 'object') {\n return;\n }\n this.remoteProps = toStringArray(config.networkLogPropsToIgnore);\n this.remoteBlacklist = toStringArray(config.networkLogBlacklist);\n this.updateRules();\n this.isNetworkEnabled = config.enableNetworkLogs === true;\n this.networkCapture.setActive(this.isNetworkEnabled);\n this.markDirty(this.networkChannel);\n } catch (e) {\n // Ignore.\n }\n }\n\n setLocalPropsToIgnore(props: unknown): void {\n this.localProps = toStringArray(props);\n this.updateRules();\n }\n\n setLocalBlacklist(blacklist: unknown): void {\n this.localBlacklist = toStringArray(blacklist);\n this.updateRules();\n }\n\n /** disableConsoleLogOverwrite(): restore the console and drop what was recorded (also natively). */\n disableConsole(): void {\n try {\n if (this.consoleCapture) {\n this.consoleCapture.uninstall();\n this.consoleCapture = null;\n }\n this.markDirty(this.consoleChannel);\n this.flushChannel(this.consoleChannel);\n } catch (e) {\n // Ignore.\n }\n }\n\n onInitialize(): void {\n this.markDirty(this.consoleChannel);\n this.markDirty(this.networkChannel);\n }\n\n /** Pushes pending changes now (before a plugin call that can produce a report). */\n flushNow(): void {\n try {\n this.flushChannel(this.consoleChannel);\n this.flushChannel(this.networkChannel);\n } catch (e) {\n // Ignore.\n }\n }\n\n private updateRules(): void {\n this.rules = createRedactionRules(\n [...this.remoteProps, ...this.localProps],\n [...this.remoteBlacklist, ...this.localBlacklist],\n );\n this.rulesVersion++;\n this.markDirty(this.networkChannel);\n }\n\n private canPush(channel: Channel): boolean {\n if (channel.isStopped) {\n return false;\n }\n if (channel.name === 'network') {\n return this.isNetworkEnabled || channel.hasPushedLogs;\n }\n return true;\n }\n\n private markDirty(channel: Channel): void {\n channel.isDirty = true;\n if (channel.timer !== null || !this.canPush(channel)) {\n return;\n }\n try {\n channel.timer = runOutsideZone(() =>\n setTimeout(() => {\n channel.timer = null;\n this.flushChannel(channel);\n }, PUSH_DELAY_MS),\n );\n } catch (e) {\n channel.timer = null;\n }\n }\n\n private flushChannel(channel: Channel): void {\n if (channel.timer !== null) {\n clearTimeout(channel.timer);\n channel.timer = null;\n }\n if (!channel.isDirty || !this.canPush(channel)) {\n return;\n }\n channel.isDirty = false;\n\n let push: Promise<unknown>;\n if (channel.name === 'console') {\n const logs = this.consoleCapture ? this.consoleCapture.getEntries() : [];\n if (logs.length === 0 && !channel.hasPushedLogs) {\n return;\n }\n channel.hasPushedLogs = logs.length > 0;\n push = runOutsideZone(() => this.target.attachConsoleLogs({ logs }));\n } else {\n const logs = this.isNetworkEnabled ? this.buildNetworkLogs() : [];\n if (logs.length === 0 && !channel.hasPushedLogs) {\n return;\n }\n channel.hasPushedLogs = logs.length > 0;\n push = runOutsideZone(() => this.target.attachNetworkLogs({ logs }));\n }\n\n try {\n Promise.resolve(push).then(\n () => {\n channel.failures = 0;\n },\n () => this.onPushFailed(channel),\n );\n } catch (e) {\n this.onPushFailed(channel);\n }\n }\n\n private onPushFailed(channel: Channel): void {\n channel.failures++;\n if (channel.failures >= MAX_FAILED_PUSHES) {\n channel.isStopped = true;\n return;\n }\n this.markDirty(channel);\n }\n\n /** Redacted copies of the buffered entries; unchanged entries reuse their previous copy. */\n private buildNetworkLogs(): GleapNetworkLogEntry[] {\n const logs: GleapNetworkLogEntry[] = [];\n const cache = new Map<number, CachedEntry>();\n for (const snapshot of this.networkCapture.getSnapshots()) {\n let cached = this.redactedEntries.get(snapshot.id);\n if (!cached || cached.version !== snapshot.version || cached.rulesVersion !== this.rulesVersion) {\n cached = {\n version: snapshot.version,\n rulesVersion: this.rulesVersion,\n entry: isBlacklistedUrl(snapshot.entry.url, this.rules)\n ? null\n : redactNetworkLogEntryOrStrip(snapshot.entry, this.rules),\n };\n }\n cache.set(snapshot.id, cached);\n if (cached.entry) {\n logs.push(cached.entry);\n }\n }\n this.redactedEntries = cache;\n return logs;\n }\n}\n\n/**\n * Starts the WebView log capture on iOS and Android (not on web, where the JavaScript SDK records\n * the console and network itself). Safe to call more than once.\n */\nexport function installWebViewLogCapture(target: LogTarget): WebViewLogCapture | null {\n try {\n if (typeof window === 'undefined' || !Capacitor.isNativePlatform()) {\n return null;\n }\n const win = window as unknown as { [GLOBAL_KEY]?: WebViewLogCapture };\n const existing = win[GLOBAL_KEY];\n if (existing) {\n return existing;\n }\n const capture = new WebViewLogCapture(window, target, Capacitor.getPlatform());\n win[GLOBAL_KEY] = capture;\n capture.start();\n return capture;\n } catch (e) {\n return null;\n }\n}\n\n/**\n * Wraps the registered plugin so the calls that affect the WebView capture reach it before they go\n * to native. Every other call passes through unchanged.\n */\nexport function wrapPluginWithLogCapture<T extends object>(plugin: T, capture: WebViewLogCapture): T {\n return new Proxy(plugin, {\n get(target, property, receiver) {\n const value = Reflect.get(target, property, receiver);\n if (typeof property !== 'string' || typeof value !== 'function') {\n return value;\n }\n const method = value as (...args: unknown[]) => unknown;\n const before =\n (hook: (options: { [key: string]: unknown }) => void) =>\n (...args: unknown[]) => {\n try {\n const options = args[0];\n hook(options && typeof options === 'object' ? (options as { [key: string]: unknown }) : {});\n } catch (e) {\n // Never break the plugin call.\n }\n return method(...args);\n };\n\n switch (property) {\n case 'initialize':\n return before(() => capture.onInitialize());\n case 'setNetworkLogPropsToIgnore':\n return before((options) => capture.setLocalPropsToIgnore(options.propsToIgnore));\n case 'setNetworkLogsBlacklist':\n return before((options) => capture.setLocalBlacklist(options.blacklist));\n case 'disableConsoleLogOverwrite':\n return before(() => capture.disableConsole());\n default:\n if (FLUSH_BEFORE_METHODS.indexOf(property) > -1) {\n return before(() => capture.flushNow());\n }\n return value;\n }\n },\n });\n}\n"]}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runs fn in zone.js' root zone when zone.js is loaded (Angular / Ionic apps), so the plugin's
|
|
3
|
+
* timers and body reads do not trigger Angular change detection. Without zone.js it just runs fn.
|
|
4
|
+
*/
|
|
5
|
+
export function runOutsideZone(fn) {
|
|
6
|
+
const zone = typeof globalThis !== 'undefined'
|
|
7
|
+
? globalThis.Zone
|
|
8
|
+
: undefined;
|
|
9
|
+
const root = zone === null || zone === void 0 ? void 0 : zone.root;
|
|
10
|
+
if (root && typeof root.run === 'function') {
|
|
11
|
+
return root.run(fn);
|
|
12
|
+
}
|
|
13
|
+
return fn();
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=zone.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"zone.js","sourceRoot":"","sources":["../../../src/logs/zone.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAI,EAAW;IAC3C,MAAM,IAAI,GACR,OAAO,UAAU,KAAK,WAAW;QAC/B,CAAC,CAAE,UAAiE,CAAC,IAAI;QACzE,CAAC,CAAC,SAAS,CAAC;IAChB,MAAM,IAAI,GAAG,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,IAAI,CAAC;IACxB,IAAI,IAAI,IAAI,OAAO,IAAI,CAAC,GAAG,KAAK,UAAU,EAAE;QAC1C,OAAQ,IAA0C,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;KAC5D;IACD,OAAO,EAAE,EAAE,CAAC;AACd,CAAC","sourcesContent":["/**\n * Runs fn in zone.js' root zone when zone.js is loaded (Angular / Ionic apps), so the plugin's\n * timers and body reads do not trigger Angular change detection. Without zone.js it just runs fn.\n */\nexport function runOutsideZone<T>(fn: () => T): T {\n const zone =\n typeof globalThis !== 'undefined'\n ? (globalThis as unknown as { Zone?: { root?: { run?: unknown } } }).Zone\n : undefined;\n const root = zone?.root;\n if (root && typeof root.run === 'function') {\n return (root as { run: (callback: () => T) => T }).run(fn);\n }\n return fn();\n}\n"]}
|
package/dist/esm/web.d.ts
CHANGED
|
@@ -86,6 +86,16 @@ export declare class GleapWeb extends WebPlugin implements GleapPlugin {
|
|
|
86
86
|
}): Promise<{
|
|
87
87
|
conversationsOpened: boolean;
|
|
88
88
|
}>;
|
|
89
|
+
openConversations(options: {
|
|
90
|
+
showBackButton?: boolean | undefined;
|
|
91
|
+
}): Promise<{
|
|
92
|
+
conversationsOpened: boolean;
|
|
93
|
+
}>;
|
|
94
|
+
openProtectedFileFromUrl(_options: {
|
|
95
|
+
url: string;
|
|
96
|
+
}): Promise<{
|
|
97
|
+
opened: boolean;
|
|
98
|
+
}>;
|
|
89
99
|
showSurvey(options: {
|
|
90
100
|
surveyId: string;
|
|
91
101
|
format?: 'survey' | 'survey_full' | undefined;
|
|
@@ -107,10 +117,17 @@ export declare class GleapWeb extends WebPlugin implements GleapPlugin {
|
|
|
107
117
|
}): Promise<{
|
|
108
118
|
envDataDisabled: boolean;
|
|
109
119
|
}>;
|
|
120
|
+
setColorScheme(options: {
|
|
121
|
+
colorScheme: 'auto' | 'light' | 'dark';
|
|
122
|
+
lightBackgroundColor?: string;
|
|
123
|
+
darkBackgroundColor?: string;
|
|
124
|
+
}): Promise<{
|
|
125
|
+
colorScheme: string;
|
|
126
|
+
}>;
|
|
110
127
|
identify(options: {
|
|
111
128
|
userId: string;
|
|
112
129
|
userHash?: string | undefined;
|
|
113
|
-
customData?:
|
|
130
|
+
customData?: Record<string, any> | undefined;
|
|
114
131
|
name?: string | undefined;
|
|
115
132
|
email?: string | undefined;
|
|
116
133
|
phone?: string | undefined;
|
|
@@ -133,7 +150,7 @@ export declare class GleapWeb extends WebPlugin implements GleapPlugin {
|
|
|
133
150
|
sla?: number | undefined;
|
|
134
151
|
plan?: string | undefined;
|
|
135
152
|
value?: number | undefined;
|
|
136
|
-
customData?:
|
|
153
|
+
customData?: Record<string, any> | undefined;
|
|
137
154
|
}): Promise<{
|
|
138
155
|
identify: boolean;
|
|
139
156
|
}>;
|
|
@@ -147,6 +164,12 @@ export declare class GleapWeb extends WebPlugin implements GleapPlugin {
|
|
|
147
164
|
}): Promise<{
|
|
148
165
|
propsToIgnoreSet: boolean;
|
|
149
166
|
}>;
|
|
167
|
+
attachNetworkLogs(): Promise<{
|
|
168
|
+
networkLogsAttached: boolean;
|
|
169
|
+
}>;
|
|
170
|
+
attachConsoleLogs(): Promise<{
|
|
171
|
+
consoleLogsAttached: boolean;
|
|
172
|
+
}>;
|
|
150
173
|
setEnvDataPropsToIgnore(options: {
|
|
151
174
|
propsToIgnore: string[];
|
|
152
175
|
}): Promise<{
|
|
@@ -250,6 +273,23 @@ export declare class GleapWeb extends WebPlugin implements GleapPlugin {
|
|
|
250
273
|
open(): Promise<{
|
|
251
274
|
openedWidget: boolean;
|
|
252
275
|
}>;
|
|
276
|
+
openChecklists(options: {
|
|
277
|
+
showBackButton?: boolean | undefined;
|
|
278
|
+
}): Promise<{
|
|
279
|
+
opened: boolean;
|
|
280
|
+
}>;
|
|
281
|
+
openChecklist(options: {
|
|
282
|
+
checklistId: string;
|
|
283
|
+
showBackButton?: boolean | undefined;
|
|
284
|
+
}): Promise<{
|
|
285
|
+
opened: boolean;
|
|
286
|
+
}>;
|
|
287
|
+
startChecklist(options: {
|
|
288
|
+
outboundId: string;
|
|
289
|
+
showBackButton?: boolean | undefined;
|
|
290
|
+
}): Promise<{
|
|
291
|
+
opened: boolean;
|
|
292
|
+
}>;
|
|
253
293
|
openFeatureRequests(options: {
|
|
254
294
|
showBackButton?: boolean | undefined;
|
|
255
295
|
}): Promise<{
|
package/dist/esm/web.js
CHANGED
|
@@ -160,6 +160,14 @@ export class GleapWeb extends WebPlugin {
|
|
|
160
160
|
Gleap.openConversations(options.showBackButton);
|
|
161
161
|
return { conversationsOpened: true };
|
|
162
162
|
}
|
|
163
|
+
async openConversations(options) {
|
|
164
|
+
Gleap.openConversations(options.showBackButton);
|
|
165
|
+
return { conversationsOpened: true };
|
|
166
|
+
}
|
|
167
|
+
// The JavaScript SDK opens `?gleapFile=` links itself on page load.
|
|
168
|
+
async openProtectedFileFromUrl(_options) {
|
|
169
|
+
return { opened: false };
|
|
170
|
+
}
|
|
163
171
|
async showSurvey(options) {
|
|
164
172
|
Gleap.showSurvey(options.surveyId, options.format);
|
|
165
173
|
return { opened: true };
|
|
@@ -178,6 +186,16 @@ export class GleapWeb extends WebPlugin {
|
|
|
178
186
|
Gleap.setDisableEnvData((_a = options.disableEnvData) !== null && _a !== void 0 ? _a : false);
|
|
179
187
|
return { envDataDisabled: true };
|
|
180
188
|
}
|
|
189
|
+
async setColorScheme(options) {
|
|
190
|
+
if (!(options === null || options === void 0 ? void 0 : options.colorScheme)) {
|
|
191
|
+
throw new Error('No colorScheme provided');
|
|
192
|
+
}
|
|
193
|
+
Gleap.setColorScheme(options.colorScheme, {
|
|
194
|
+
lightBackgroundColor: options.lightBackgroundColor,
|
|
195
|
+
darkBackgroundColor: options.darkBackgroundColor,
|
|
196
|
+
});
|
|
197
|
+
return { colorScheme: options.colorScheme };
|
|
198
|
+
}
|
|
181
199
|
async identify(options) {
|
|
182
200
|
var userData = {
|
|
183
201
|
name: options.name,
|
|
@@ -211,6 +229,14 @@ export class GleapWeb extends WebPlugin {
|
|
|
211
229
|
Gleap.setNetworkLogPropsToIgnore(options.propsToIgnore);
|
|
212
230
|
return { propsToIgnoreSet: true };
|
|
213
231
|
}
|
|
232
|
+
async attachNetworkLogs() {
|
|
233
|
+
// The JavaScript SDK records the page's requests itself on web.
|
|
234
|
+
return { networkLogsAttached: false };
|
|
235
|
+
}
|
|
236
|
+
async attachConsoleLogs() {
|
|
237
|
+
// The JavaScript SDK records the page's console itself on web.
|
|
238
|
+
return { consoleLogsAttached: false };
|
|
239
|
+
}
|
|
214
240
|
async setEnvDataPropsToIgnore(options) {
|
|
215
241
|
Gleap.setEnvDataPropsToIgnore(options.propsToIgnore);
|
|
216
242
|
return { envDataPropsToIgnoreSet: true };
|
|
@@ -290,6 +316,18 @@ export class GleapWeb extends WebPlugin {
|
|
|
290
316
|
Gleap.open();
|
|
291
317
|
return { openedWidget: true };
|
|
292
318
|
}
|
|
319
|
+
async openChecklists(options) {
|
|
320
|
+
Gleap.openChecklists(options.showBackButton);
|
|
321
|
+
return { opened: true };
|
|
322
|
+
}
|
|
323
|
+
async openChecklist(options) {
|
|
324
|
+
Gleap.openChecklist(options.checklistId, options.showBackButton);
|
|
325
|
+
return { opened: true };
|
|
326
|
+
}
|
|
327
|
+
async startChecklist(options) {
|
|
328
|
+
Gleap.startChecklist(options.outboundId, options.showBackButton);
|
|
329
|
+
return { opened: true };
|
|
330
|
+
}
|
|
293
331
|
async openFeatureRequests(options) {
|
|
294
332
|
Gleap.openFeatureRequests(options.showBackButton);
|
|
295
333
|
return { openedFeatureRequests: true };
|