@kabacorp/kaba-electron-rpc 7.1.2 → 8.1.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/.nvmrc +1 -0
- package/README.md +1 -1
- package/index.js +1 -1
- package/lib/export-api.js +195 -139
- package/lib/import-api.js +261 -236
- package/lib/util.js +101 -53
- package/package.json +12 -14
- package/test/renderer-runner.html +1 -1
- package/test/webview-runner.html +1 -1
package/.nvmrc
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
14.21.3
|
package/README.md
CHANGED
package/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
module.exports.exportAPI = require('./lib/export-api')
|
|
2
|
-
module.exports.importAPI = require('./lib/import-api')
|
|
2
|
+
module.exports.importAPI = require('./lib/import-api')
|
package/lib/export-api.js
CHANGED
|
@@ -6,12 +6,8 @@ const {
|
|
|
6
6
|
isRenderer,
|
|
7
7
|
} = require("./util");
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
if (ref[i] === ref) delete ref[i];
|
|
12
|
-
else if (typeof ref[i] == "Object") removeCircular(ref[i]);
|
|
13
|
-
}
|
|
14
|
-
}
|
|
9
|
+
// Persistent map to avoid leaking cleanup functions
|
|
10
|
+
const cleanupRoutines = new Map();
|
|
15
11
|
|
|
16
12
|
module.exports = function (
|
|
17
13
|
channelName,
|
|
@@ -19,54 +15,72 @@ module.exports = function (
|
|
|
19
15
|
methods,
|
|
20
16
|
globalPermissionCheck
|
|
21
17
|
) {
|
|
22
|
-
|
|
23
|
-
|
|
18
|
+
const api = new EventEmitter();
|
|
19
|
+
const webcontentsStreams = {};
|
|
20
|
+
|
|
21
|
+
// Internal Debugging helper
|
|
22
|
+
const debug = (...args) => {
|
|
23
|
+
if (process.env.RPC_DEBUG) {
|
|
24
|
+
console.log(`[RPC-EXPORT:${channelName}]`, ...args);
|
|
25
|
+
}
|
|
26
|
+
};
|
|
24
27
|
|
|
25
|
-
|
|
28
|
+
let channel;
|
|
26
29
|
if (isNodeProcess()) {
|
|
27
|
-
|
|
30
|
+
const mockedSender = new EventEmitter();
|
|
28
31
|
mockedSender.id = "main-process";
|
|
32
|
+
mockedSender.isDestroyed = () => false;
|
|
29
33
|
mockedSender.send = (channelName, msgType, requestId, ...args) => {
|
|
30
|
-
|
|
34
|
+
debug("send (node-process)", { msgType, requestId });
|
|
31
35
|
process.send({ channelName, msgType, requestId, args });
|
|
32
36
|
};
|
|
33
37
|
channel = {
|
|
34
38
|
onMessage(cb) {
|
|
35
39
|
process.on("message", (msg) => {
|
|
36
|
-
if (msg.channelName
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
+
if (msg && msg.channelName === channelName) {
|
|
41
|
+
debug("onMessage (node-process)", msg.methodName, msg.requestId);
|
|
42
|
+
const mockedEvent = { sender: mockedSender };
|
|
43
|
+
cb(mockedEvent, msg.methodName, msg.requestId, ...msg.args);
|
|
44
|
+
}
|
|
40
45
|
});
|
|
41
46
|
},
|
|
42
47
|
};
|
|
43
48
|
} else if (isRenderer()) {
|
|
44
|
-
|
|
49
|
+
const { ipcRenderer } = require("electron");
|
|
45
50
|
channel = {
|
|
46
51
|
onMessage(cb) {
|
|
47
|
-
ipcRenderer.on(channelName,
|
|
52
|
+
ipcRenderer.on(channelName, (event, methodName, requestId, ...args) => {
|
|
53
|
+
debug("onMessage (renderer)", methodName, requestId);
|
|
54
|
+
cb(event, methodName, requestId, ...args);
|
|
55
|
+
});
|
|
48
56
|
},
|
|
49
57
|
};
|
|
50
58
|
} else {
|
|
51
|
-
|
|
59
|
+
const { ipcMain } = require("electron");
|
|
52
60
|
channel = {
|
|
53
61
|
onMessage(cb) {
|
|
54
|
-
ipcMain.on(channelName,
|
|
62
|
+
ipcMain.on(channelName, (event, methodName, requestId, ...args) => {
|
|
63
|
+
debug("onMessage (main)", methodName, requestId);
|
|
64
|
+
cb(event, methodName, requestId, ...args);
|
|
65
|
+
});
|
|
55
66
|
},
|
|
56
67
|
};
|
|
57
68
|
}
|
|
58
69
|
|
|
59
70
|
// wire up handler
|
|
60
71
|
channel.onMessage(async function (event, methodName, requestId, ...args) {
|
|
61
|
-
// console.log('received', channelName, methodName, requestId, ...args)
|
|
62
72
|
args = args.map(IPCValueToValue);
|
|
63
73
|
|
|
64
74
|
// watch for a navigation event
|
|
65
|
-
|
|
66
|
-
|
|
75
|
+
let hasNavigated = false;
|
|
76
|
+
const onDidNavigate = () => {
|
|
77
|
+
debug("navigation detected", { requestId, methodName });
|
|
67
78
|
hasNavigated = true;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
if (event.sender && !event.sender.isDestroyed()) {
|
|
82
|
+
event.sender.once("did-navigate", onDidNavigate);
|
|
68
83
|
}
|
|
69
|
-
event.sender.on("did-navigate", onDidNavigate);
|
|
70
84
|
|
|
71
85
|
// helper to send
|
|
72
86
|
const send = function (
|
|
@@ -75,48 +89,84 @@ module.exports = function (
|
|
|
75
89
|
value,
|
|
76
90
|
keepListeningForDidNavigate = false
|
|
77
91
|
) {
|
|
78
|
-
if (
|
|
79
|
-
|
|
92
|
+
if (
|
|
93
|
+
!event.sender ||
|
|
94
|
+
(event.sender.isDestroyed && event.sender.isDestroyed())
|
|
95
|
+
) {
|
|
96
|
+
debug("send aborted: sender destroyed", { requestId, msgType });
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (!keepListeningForDidNavigate) {
|
|
80
101
|
event.sender.removeListener("did-navigate", onDidNavigate);
|
|
81
|
-
|
|
102
|
+
}
|
|
82
103
|
|
|
83
|
-
if (
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
} else {
|
|
87
|
-
// console.log("sending", channelName, msgType, requestId, err, value);
|
|
104
|
+
if (hasNavigated) {
|
|
105
|
+
debug("send aborted: navigated away", { requestId, msgType });
|
|
106
|
+
return;
|
|
88
107
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
108
|
+
|
|
109
|
+
const target = event.reply ? event : event.sender;
|
|
110
|
+
|
|
111
|
+
try {
|
|
112
|
+
target.send(channelName, msgType, requestId, err, value);
|
|
113
|
+
} catch (serializationError) {
|
|
114
|
+
debug("serialization failed, attempting sanitization", {
|
|
115
|
+
requestId,
|
|
116
|
+
methodName,
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
let sanitizedValue = value;
|
|
120
|
+
if (value && typeof value === "object") {
|
|
121
|
+
if (typeof value.toJSON === "function") {
|
|
122
|
+
sanitizedValue = value.toJSON();
|
|
123
|
+
} else {
|
|
124
|
+
try {
|
|
125
|
+
sanitizedValue = JSON.parse(JSON.stringify(value));
|
|
126
|
+
} catch (e) {
|
|
127
|
+
console.error(
|
|
128
|
+
`[RPC ERROR] Circular/Complex object at "${methodName}":`,
|
|
129
|
+
e
|
|
130
|
+
);
|
|
131
|
+
sanitizedValue = {
|
|
132
|
+
id: value.id,
|
|
133
|
+
error:
|
|
134
|
+
"Object too complex to serialize: " +
|
|
135
|
+
serializationError.message,
|
|
136
|
+
stack: serializationError.stack,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
target.send(channelName, msgType, requestId, err, sanitizedValue);
|
|
94
142
|
}
|
|
95
143
|
};
|
|
96
144
|
|
|
97
145
|
// handle special methods
|
|
98
|
-
if (methodName
|
|
146
|
+
if (methodName === "stream-request-write") {
|
|
99
147
|
event.returnValue = true;
|
|
100
148
|
return streamRequestWrite(event.sender.id, requestId, args);
|
|
101
149
|
}
|
|
102
|
-
if (methodName
|
|
150
|
+
if (methodName === "stream-request-end") {
|
|
103
151
|
event.returnValue = true;
|
|
104
152
|
return streamRequestEnd(event.sender.id, requestId, args);
|
|
105
153
|
}
|
|
106
|
-
if (methodName
|
|
154
|
+
if (methodName === "stream-request-close") {
|
|
107
155
|
event.returnValue = true;
|
|
108
156
|
return streamRequestClose(event.sender.id, requestId, args);
|
|
109
157
|
}
|
|
110
158
|
|
|
111
159
|
// look up the method called
|
|
112
|
-
|
|
113
|
-
|
|
160
|
+
const type = manifest[methodName];
|
|
161
|
+
const method = methods[methodName];
|
|
162
|
+
|
|
114
163
|
if (!type || !method) {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
)
|
|
164
|
+
const err = new Error(`Method not found: "${methodName}"`);
|
|
165
|
+
debug("error: method not found", methodName);
|
|
166
|
+
api.emit("error", err, { methodName, requestId, args });
|
|
167
|
+
// If async/promise, we should probably let the caller know
|
|
168
|
+
if (type === "async" || type === "promise")
|
|
169
|
+
send("async-reply", errorObject(err));
|
|
120
170
|
return;
|
|
121
171
|
}
|
|
122
172
|
|
|
@@ -125,8 +175,8 @@ module.exports = function (
|
|
|
125
175
|
globalPermissionCheck &&
|
|
126
176
|
!globalPermissionCheck(event, methodName, args)
|
|
127
177
|
) {
|
|
128
|
-
|
|
129
|
-
if (type
|
|
178
|
+
debug("permission denied", methodName);
|
|
179
|
+
if (type === "async" || type === "promise") {
|
|
130
180
|
send("async-reply", "Method Access Denied");
|
|
131
181
|
} else {
|
|
132
182
|
event.returnValue = { error: "Method Access Denied" };
|
|
@@ -135,49 +185,55 @@ module.exports = function (
|
|
|
135
185
|
}
|
|
136
186
|
|
|
137
187
|
// run method by type
|
|
138
|
-
if (type
|
|
139
|
-
// call sync
|
|
188
|
+
if (type === "sync") {
|
|
140
189
|
try {
|
|
141
190
|
event.returnValue = {
|
|
142
191
|
success: valueToIPCValue(method.apply(event, args)),
|
|
143
192
|
};
|
|
144
193
|
} catch (e) {
|
|
194
|
+
debug("sync method error", methodName, e.message);
|
|
145
195
|
event.returnValue = { error: e.message };
|
|
146
196
|
}
|
|
147
197
|
return;
|
|
148
198
|
}
|
|
149
|
-
|
|
150
|
-
|
|
199
|
+
|
|
200
|
+
if (type === "async") {
|
|
151
201
|
const replyCb = (err, value) => {
|
|
152
202
|
if (err) err = errorObject(err);
|
|
153
203
|
send("async-reply", err, valueToIPCValue(value));
|
|
154
204
|
};
|
|
155
205
|
args.push(replyCb);
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
206
|
+
try {
|
|
207
|
+
method.apply(event, args);
|
|
208
|
+
} catch (e) {
|
|
209
|
+
debug("async method throw", methodName, e.message);
|
|
210
|
+
send("async-reply", errorObject(e));
|
|
211
|
+
}
|
|
159
212
|
return;
|
|
160
213
|
}
|
|
161
|
-
|
|
162
|
-
|
|
214
|
+
|
|
215
|
+
if (type === "promise") {
|
|
163
216
|
let p;
|
|
164
217
|
try {
|
|
165
218
|
p = method.apply(event, args);
|
|
166
|
-
if (
|
|
167
|
-
|
|
219
|
+
if (!(p instanceof Promise) && (!p || typeof p.then !== "function")) {
|
|
220
|
+
p = Promise.resolve(p);
|
|
221
|
+
}
|
|
168
222
|
} catch (e) {
|
|
169
|
-
p = Promise.reject(
|
|
223
|
+
p = Promise.reject(e);
|
|
170
224
|
}
|
|
171
225
|
|
|
172
|
-
// handle response
|
|
173
226
|
p.then(
|
|
174
227
|
(value) => send("async-reply", null, valueToIPCValue(value)),
|
|
175
|
-
(error) =>
|
|
228
|
+
(error) => {
|
|
229
|
+
debug("promise rejected", methodName, error?.message);
|
|
230
|
+
send("async-reply", errorObject(error));
|
|
231
|
+
}
|
|
176
232
|
);
|
|
177
233
|
return;
|
|
178
234
|
}
|
|
179
235
|
|
|
180
|
-
|
|
236
|
+
const streamTypes = {
|
|
181
237
|
readable: createReadableEvents,
|
|
182
238
|
writable: createWritableEvents,
|
|
183
239
|
duplex: createDuplexEvents,
|
|
@@ -194,11 +250,11 @@ module.exports = function (
|
|
|
194
250
|
);
|
|
195
251
|
}
|
|
196
252
|
|
|
197
|
-
|
|
198
|
-
"
|
|
199
|
-
new Error(`Invalid method type "${type}" for "${methodName}"`),
|
|
200
|
-
arguments
|
|
253
|
+
const typeErr = new Error(
|
|
254
|
+
`Invalid method type "${type}" for "${methodName}"`
|
|
201
255
|
);
|
|
256
|
+
debug("error: invalid type", type);
|
|
257
|
+
api.emit("error", typeErr, { methodName, type, requestId });
|
|
202
258
|
});
|
|
203
259
|
|
|
204
260
|
async function handleStream(
|
|
@@ -209,45 +265,32 @@ module.exports = function (
|
|
|
209
265
|
createStreamEvents,
|
|
210
266
|
send
|
|
211
267
|
) {
|
|
212
|
-
// call duplex
|
|
213
268
|
let stream;
|
|
214
|
-
let error;
|
|
215
269
|
try {
|
|
216
270
|
stream = method.apply(event, args);
|
|
271
|
+
if (stream && typeof stream.then === "function") stream = await stream;
|
|
217
272
|
if (!stream) {
|
|
218
|
-
|
|
219
|
-
return;
|
|
273
|
+
debug("stream-error: method returned null", requestId);
|
|
274
|
+
return send("stream-error", "Empty stream response");
|
|
220
275
|
}
|
|
221
276
|
} catch (e) {
|
|
222
|
-
|
|
223
|
-
return;
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
// handle promises
|
|
227
|
-
if (stream.then) {
|
|
228
|
-
try {
|
|
229
|
-
stream = await stream; // wait for it
|
|
230
|
-
} catch (e) {
|
|
231
|
-
send("stream-error", "" + e);
|
|
232
|
-
return;
|
|
233
|
-
}
|
|
277
|
+
debug("stream instantiation error", e.message);
|
|
278
|
+
return send("stream-error", "" + e);
|
|
234
279
|
}
|
|
235
280
|
|
|
236
281
|
trackWebcontentsStreams(event.sender, requestId, stream);
|
|
237
|
-
|
|
282
|
+
const events = createStreamEvents(event, stream, requestId, send);
|
|
238
283
|
hookUpEventsAndUnregister(stream, events);
|
|
239
284
|
|
|
240
|
-
// done
|
|
241
285
|
event.returnValue = { success: true };
|
|
242
|
-
return;
|
|
243
286
|
}
|
|
244
287
|
|
|
245
288
|
function hookUpEventsAndUnregister(stream, events) {
|
|
246
289
|
Object.keys(events).forEach((key) => stream.on(key, events[key]));
|
|
247
290
|
stream.unregisterEvents = () => {
|
|
248
|
-
Object.keys(events).forEach((key) =>
|
|
249
|
-
stream.removeListener(key, events[key])
|
|
250
|
-
);
|
|
291
|
+
Object.keys(events).forEach((key) => {
|
|
292
|
+
stream.removeListener(key, events[key]);
|
|
293
|
+
});
|
|
251
294
|
};
|
|
252
295
|
}
|
|
253
296
|
|
|
@@ -255,15 +298,22 @@ module.exports = function (
|
|
|
255
298
|
return {
|
|
256
299
|
data: (chunk) =>
|
|
257
300
|
send("stream-data", valueToIPCValue(chunk), undefined, true),
|
|
258
|
-
close: () =>
|
|
301
|
+
close: () => {
|
|
302
|
+
debug("stream-close (readable)", requestId);
|
|
303
|
+
stream.unregisterEvents();
|
|
304
|
+
send("stream-close");
|
|
305
|
+
},
|
|
259
306
|
error: (err) => {
|
|
307
|
+
debug("stream-error (readable)", requestId, err?.message);
|
|
260
308
|
stream.unregisterEvents();
|
|
261
|
-
send("stream-error", err ? err.message : "");
|
|
309
|
+
send("stream-error", err ? err.message : "Unknown stream error");
|
|
262
310
|
},
|
|
263
311
|
end: () => {
|
|
264
|
-
stream
|
|
312
|
+
debug("stream-end (readable)", requestId);
|
|
265
313
|
send("stream-end");
|
|
266
|
-
webcontentsStreams[event.sender.id]
|
|
314
|
+
if (webcontentsStreams[event.sender.id]) {
|
|
315
|
+
webcontentsStreams[event.sender.id][requestId] = null;
|
|
316
|
+
}
|
|
267
317
|
},
|
|
268
318
|
};
|
|
269
319
|
}
|
|
@@ -271,87 +321,93 @@ module.exports = function (
|
|
|
271
321
|
function createWritableEvents(event, stream, requestId, send) {
|
|
272
322
|
return {
|
|
273
323
|
drain: () => send("stream-drain", undefined, undefined, true),
|
|
274
|
-
close: () =>
|
|
324
|
+
close: () => {
|
|
325
|
+
debug("stream-close (writable)", requestId);
|
|
326
|
+
stream.unregisterEvents();
|
|
327
|
+
send("stream-close");
|
|
328
|
+
},
|
|
275
329
|
error: (err) => {
|
|
330
|
+
debug("stream-error (writable)", requestId, err?.message);
|
|
276
331
|
stream.unregisterEvents();
|
|
277
|
-
send("stream-error", err ? err.message : "");
|
|
332
|
+
send("stream-error", err ? err.message : "Unknown stream error");
|
|
278
333
|
},
|
|
279
334
|
finish: () => {
|
|
280
|
-
stream
|
|
335
|
+
debug("stream-finish (writable)", requestId);
|
|
281
336
|
send("stream-finish");
|
|
282
|
-
webcontentsStreams[event.sender.id]
|
|
337
|
+
if (webcontentsStreams[event.sender.id]) {
|
|
338
|
+
webcontentsStreams[event.sender.id][requestId] = null;
|
|
339
|
+
}
|
|
283
340
|
},
|
|
284
341
|
};
|
|
285
342
|
}
|
|
286
343
|
|
|
287
344
|
function createDuplexEvents(event, stream, requestId, send) {
|
|
345
|
+
// Note: unregisterEvents will be shared, which is correct
|
|
288
346
|
return Object.assign(
|
|
289
347
|
createWritableEvents(event, stream, requestId, send),
|
|
290
348
|
createReadableEvents(event, stream, requestId, send)
|
|
291
349
|
);
|
|
292
350
|
}
|
|
293
351
|
|
|
294
|
-
// special methods
|
|
295
352
|
function trackWebcontentsStreams(webcontents, requestId, stream) {
|
|
296
|
-
|
|
297
|
-
if (!webcontentsStreams[
|
|
298
|
-
webcontentsStreams[
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
"
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
353
|
+
const wcId = webcontents.id;
|
|
354
|
+
if (!webcontentsStreams[wcId]) {
|
|
355
|
+
webcontentsStreams[wcId] = {};
|
|
356
|
+
|
|
357
|
+
const cleanup = () => {
|
|
358
|
+
debug("cleaning up all streams for webcontents", wcId);
|
|
359
|
+
if (!webcontentsStreams[wcId]) return;
|
|
360
|
+
for (let rid in webcontentsStreams[wcId]) {
|
|
361
|
+
if (webcontentsStreams[wcId][rid]) {
|
|
362
|
+
const s = webcontentsStreams[wcId][rid];
|
|
363
|
+
if (s.unregisterEvents) s.unregisterEvents();
|
|
364
|
+
streamRequestClose(wcId, rid, []);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
delete webcontentsStreams[wcId];
|
|
368
|
+
cleanupRoutines.delete(wcId);
|
|
369
|
+
};
|
|
370
|
+
|
|
371
|
+
cleanupRoutines.set(wcId, cleanup);
|
|
372
|
+
webcontents.once("did-navigate", cleanup);
|
|
373
|
+
webcontents.once("destroyed", cleanup);
|
|
305
374
|
}
|
|
306
|
-
webcontentsStreams[
|
|
375
|
+
webcontentsStreams[wcId][requestId] = stream;
|
|
307
376
|
}
|
|
308
377
|
|
|
309
378
|
function streamRequestWrite(webcontentsId, requestId, args) {
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
if (stream && typeof stream.write == "function") {
|
|
379
|
+
const stream = (webcontentsStreams[webcontentsId] || {})[requestId];
|
|
380
|
+
if (stream && typeof stream.write === "function") {
|
|
313
381
|
stream.write(...args);
|
|
382
|
+
} else {
|
|
383
|
+
debug("write failed: stream not found or not writable", requestId);
|
|
314
384
|
}
|
|
315
385
|
}
|
|
386
|
+
|
|
316
387
|
function streamRequestEnd(webcontentsId, requestId, args) {
|
|
317
|
-
|
|
318
|
-
if (stream && typeof stream.end
|
|
388
|
+
const stream = (webcontentsStreams[webcontentsId] || {})[requestId];
|
|
389
|
+
if (stream && typeof stream.end === "function") {
|
|
390
|
+
stream.end(...args);
|
|
391
|
+
}
|
|
319
392
|
}
|
|
393
|
+
|
|
320
394
|
function streamRequestClose(webcontentsId, requestId, args) {
|
|
321
|
-
|
|
395
|
+
const stream = (webcontentsStreams[webcontentsId] || {})[requestId];
|
|
322
396
|
if (!stream) return;
|
|
323
|
-
|
|
324
|
-
if (typeof stream.close
|
|
325
|
-
|
|
326
|
-
else if (typeof stream.
|
|
327
|
-
// oye, last shot: end()
|
|
328
|
-
else if (typeof stream.end == "function") stream.end(...args);
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
// helpers
|
|
332
|
-
function closeAllWebcontentsStreams(webcontentsId) {
|
|
333
|
-
return (e) => {
|
|
334
|
-
if (!webcontentsStreams[webcontentsId]) return;
|
|
335
|
-
|
|
336
|
-
// close all of the open streams
|
|
337
|
-
for (var requestId in webcontentsStreams[webcontentsId]) {
|
|
338
|
-
if (webcontentsStreams[webcontentsId][requestId]) {
|
|
339
|
-
webcontentsStreams[webcontentsId][requestId].unregisterEvents();
|
|
340
|
-
streamRequestClose(webcontentsId, requestId, []);
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
// stop tracking
|
|
345
|
-
delete webcontentsStreams[webcontentsId];
|
|
346
|
-
};
|
|
397
|
+
debug("force closing stream", requestId);
|
|
398
|
+
if (typeof stream.close === "function") stream.close(...args);
|
|
399
|
+
else if (typeof stream.destroy === "function") stream.destroy(...args);
|
|
400
|
+
else if (typeof stream.end === "function") stream.end(...args);
|
|
347
401
|
}
|
|
348
402
|
|
|
349
403
|
return api;
|
|
350
404
|
};
|
|
351
405
|
|
|
352
406
|
function errorObject(error) {
|
|
353
|
-
|
|
407
|
+
if (!error) return { message: "Unknown Error" };
|
|
408
|
+
const copy = Object.assign({}, error);
|
|
354
409
|
copy.message = error.message || error.toString();
|
|
355
410
|
if (error.name) copy.name = error.name;
|
|
411
|
+
if (error.stack) copy.stack = error.stack; // Added for better loggic
|
|
356
412
|
return copy;
|
|
357
413
|
}
|