positron.js 1.0.6 → 1.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/README.md +7 -8
- package/builder.js +148 -1
- package/core/linux/main.cpp +1123 -0
- package/core/mac/main.swift +45 -20
- package/core/win/main.cs +46 -12
- package/index.js +75 -34
- package/package.json +3 -2
- package/packager.js +101 -7
- package/screen.js +7 -5
- package/tray.js +9 -1
package/core/mac/main.swift
CHANGED
|
@@ -127,6 +127,22 @@ func printError(_ message: String) {
|
|
|
127
127
|
let tag = isWarning ? "WARNING" : (isInfo ? "INFO" : "ERROR")
|
|
128
128
|
|
|
129
129
|
print("\(red)[SWIFT \(tag)] \(msg)\(reset)")
|
|
130
|
+
|
|
131
|
+
if(tag == "INFO") {
|
|
132
|
+
return
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if msg.contains("IPC response") || msg.contains("WebSocket error") || msg.contains("Reconnecting to") || msg.contains("IPC message") {
|
|
136
|
+
return
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if let ipcClient = AppDelegate.shared?.ipcClient {
|
|
140
|
+
ipcClient.send(
|
|
141
|
+
IPCResponse(windowId: -1, event: "nativeError", data: ["message": message, "type": tag])
|
|
142
|
+
)
|
|
143
|
+
} else {
|
|
144
|
+
print("\(red)[SWIFT ERROR] No IPC client available to send error message over. \(reset)")
|
|
145
|
+
}
|
|
130
146
|
}
|
|
131
147
|
|
|
132
148
|
public protocol PositronExtension {
|
|
@@ -370,6 +386,10 @@ case "forceCloseWindow":
|
|
|
370
386
|
printError("loadURL — invalid or missing URL")
|
|
371
387
|
return
|
|
372
388
|
}
|
|
389
|
+
if let scheme = url.scheme?.lowercased(), !["http", "https", "file"].contains(scheme) {
|
|
390
|
+
printError("loadURL — blocked unauthorized URL scheme: \(scheme)")
|
|
391
|
+
return
|
|
392
|
+
}
|
|
373
393
|
(window.contentView as? WKWebView)?.load(URLRequest(url: url))
|
|
374
394
|
|
|
375
395
|
case "hide":
|
|
@@ -585,19 +605,6 @@ UNUserNotificationCenter.current().requestAuthorization(
|
|
|
585
605
|
IPCResponse(windowId: windowId, event: args.last ?? "getTitle-reply-\(windowId)", data: ["title": title])
|
|
586
606
|
)
|
|
587
607
|
|
|
588
|
-
case "executeAppleScript":
|
|
589
|
-
guard let scriptSource = args.first else {
|
|
590
|
-
printError("executeAppleScript — missing script argument")
|
|
591
|
-
return
|
|
592
|
-
}
|
|
593
|
-
let script = NSAppleScript(source: scriptSource)
|
|
594
|
-
var errorInfo: NSDictionary?
|
|
595
|
-
script?.executeAndReturnError(&errorInfo)
|
|
596
|
-
if let errorInfo {
|
|
597
|
-
let errorMessage = errorInfo[NSAppleScript.errorMessage] as? String ?? "Unknown error"
|
|
598
|
-
printError("executeAppleScript failed: \(errorMessage)")
|
|
599
|
-
}
|
|
600
|
-
|
|
601
608
|
case "isVisible":
|
|
602
609
|
guard let window = windows[windowId] else { return }
|
|
603
610
|
let isVisible = window.isVisible
|
|
@@ -819,7 +826,7 @@ case "addToContentBlocker":
|
|
|
819
826
|
alert.addButton(withTitle: "Cancel")
|
|
820
827
|
|
|
821
828
|
alert.beginSheetModal(for: window) { response in
|
|
822
|
-
let confirmed = (response == .
|
|
829
|
+
let confirmed = (response == .alertSecondButtonReturn) ? false : true
|
|
823
830
|
AppDelegate.shared?.ipcClient.send(
|
|
824
831
|
IPCResponse(windowId: windowId, event: args.last ?? "confirm-reply-\(windowId)", data: ["confirmed": confirmed ? "true" : "false"])
|
|
825
832
|
)
|
|
@@ -926,6 +933,14 @@ final class WebViewNavigationDelegate: NSObject, WKNavigationDelegate {
|
|
|
926
933
|
IPCResponse(windowId: windowId, event: eventName, data: ["url": webView.url?.absoluteString ?? "", "title": webView.title ?? "", "canGoBack": (webView.canGoBack ? "true" : "false"), "canGoForward": (webView.canGoForward ? "true" : "false")])
|
|
927
934
|
)
|
|
928
935
|
}
|
|
936
|
+
|
|
937
|
+
func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
|
|
938
|
+
printError("Navigation failed: \(error.localizedDescription)")
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) {
|
|
942
|
+
printError("Provisional navigation failed: \(error.localizedDescription)")
|
|
943
|
+
}
|
|
929
944
|
}
|
|
930
945
|
|
|
931
946
|
// MARK: - WebView → Swift IPC Handler
|
|
@@ -1035,9 +1050,9 @@ final class IPCClient {
|
|
|
1035
1050
|
private let maxReconnectAttempts = 10
|
|
1036
1051
|
private let reconnectDelay: TimeInterval = 2.0
|
|
1037
1052
|
|
|
1038
|
-
init(serverURL: URL = URL(string: "ws://
|
|
1053
|
+
init(serverURL: URL = URL(string: "ws://127.0.0.1:9000")!) {
|
|
1039
1054
|
let POSITRON_IPC_PORT = port ?? 9000
|
|
1040
|
-
self.serverURL = URL(string: "ws://
|
|
1055
|
+
self.serverURL = URL(string: "ws://127.0.0.1:\(POSITRON_IPC_PORT)")!
|
|
1041
1056
|
self.authToken = AUTH_TOKEN
|
|
1042
1057
|
}
|
|
1043
1058
|
|
|
@@ -1052,7 +1067,6 @@ final class IPCClient {
|
|
|
1052
1067
|
webSocketTask = session.webSocketTask(with: request)
|
|
1053
1068
|
webSocketTask?.resume()
|
|
1054
1069
|
printError("INFO: Connecting to IPC server (attempt \(reconnectAttempts + 1))…")
|
|
1055
|
-
reconnectAttempts = 0 // reset on successful connect
|
|
1056
1070
|
receiveMessage()
|
|
1057
1071
|
}
|
|
1058
1072
|
|
|
@@ -1064,7 +1078,12 @@ final class IPCClient {
|
|
|
1064
1078
|
}
|
|
1065
1079
|
webSocketTask?.send(.string(text)) { error in
|
|
1066
1080
|
if let error {
|
|
1067
|
-
|
|
1081
|
+
let errorMsg = error.localizedDescription
|
|
1082
|
+
printError("Failed to send IPC response: \(errorMsg)")
|
|
1083
|
+
if errorMsg.lowercased().contains("bad response from the server") {
|
|
1084
|
+
printError("Fatal connection error (Unauthorized or Port Hijacked). Exiting immediately.")
|
|
1085
|
+
exit(1)
|
|
1086
|
+
}
|
|
1068
1087
|
}
|
|
1069
1088
|
}
|
|
1070
1089
|
}
|
|
@@ -1074,9 +1093,15 @@ final class IPCClient {
|
|
|
1074
1093
|
guard let self else { return }
|
|
1075
1094
|
switch result {
|
|
1076
1095
|
case .failure(let error):
|
|
1077
|
-
|
|
1096
|
+
let errorMsg = error.localizedDescription
|
|
1097
|
+
printError("WebSocket error: \(errorMsg).")
|
|
1098
|
+
if errorMsg.contains("503") || errorMsg.contains("401") || errorMsg.contains("403") || errorMsg.lowercased().contains("bad response from the server") {
|
|
1099
|
+
printError("Fatal connection error (Unauthorized or Port Hijacked). Exiting immediately.")
|
|
1100
|
+
exit(1)
|
|
1101
|
+
}
|
|
1078
1102
|
self.scheduleReconnect()
|
|
1079
1103
|
case .success(let message):
|
|
1104
|
+
self.reconnectAttempts = 0
|
|
1080
1105
|
switch message {
|
|
1081
1106
|
case .string(let text):
|
|
1082
1107
|
self.parseAndDispatch(text)
|
|
@@ -1108,7 +1133,7 @@ final class IPCClient {
|
|
|
1108
1133
|
reconnectAttempts += 1
|
|
1109
1134
|
guard reconnectAttempts < maxReconnectAttempts else {
|
|
1110
1135
|
printError("Exceeded maximum reconnect attempts (\(maxReconnectAttempts)). Giving up.")
|
|
1111
|
-
|
|
1136
|
+
exit(1)
|
|
1112
1137
|
}
|
|
1113
1138
|
printError("Reconnecting to \(serverURL) in \(reconnectDelay)s… (attempt \(reconnectAttempts)/\(maxReconnectAttempts))")
|
|
1114
1139
|
DispatchQueue.global().asyncAfter(deadline: .now() + reconnectDelay) { [weak self] in
|
package/core/win/main.cs
CHANGED
|
@@ -131,6 +131,20 @@ namespace PositronWindows
|
|
|
131
131
|
|
|
132
132
|
string reset = "\u001b[0m";
|
|
133
133
|
Console.WriteLine($"{red}[C# {tag}] {message}{reset}");
|
|
134
|
+
|
|
135
|
+
if (_ipcClient != null) {
|
|
136
|
+
if (tag != "INFO") {
|
|
137
|
+
_ipcClient.Send(new IPCResponse
|
|
138
|
+
{
|
|
139
|
+
windowId = -1,
|
|
140
|
+
@event = "nativeError",
|
|
141
|
+
data = new() { { "message", message }, { "type", tag } }
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
} else
|
|
145
|
+
{
|
|
146
|
+
Console.WriteLine($"{red}[C# ERROR] No IPC client available to send error message over. {reset}");
|
|
147
|
+
}
|
|
134
148
|
}
|
|
135
149
|
|
|
136
150
|
private static readonly string AuthToken =
|
|
@@ -222,7 +236,7 @@ namespace PositronWindows
|
|
|
222
236
|
};
|
|
223
237
|
|
|
224
238
|
await Task.Delay(500);
|
|
225
|
-
_ipcClient = new IPCClient(new Uri($"ws://
|
|
239
|
+
_ipcClient = new IPCClient(new Uri($"ws://127.0.0.1:{_ipcPort}"));
|
|
226
240
|
_ = _ipcClient.ConnectAsync(AuthToken);
|
|
227
241
|
}
|
|
228
242
|
|
|
@@ -377,12 +391,16 @@ private void StartNodeProcess(string workingDirectory, string backendExeName)
|
|
|
377
391
|
{
|
|
378
392
|
bool isFile = webView.Source != null && webView.Source.IsFile;
|
|
379
393
|
string eventName = isFile ? $"loadFile-reply-{windowId}" : $"loadURL-reply-{windowId}";
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
394
|
+
if (e.IsSuccess) {
|
|
395
|
+
_ipcClient.Send(new IPCResponse
|
|
396
|
+
{
|
|
397
|
+
windowId = windowId,
|
|
398
|
+
@event = eventName,
|
|
399
|
+
data = new() { { "url", webView.Source?.ToString() ?? "" }, { "title", webView.CoreWebView2.DocumentTitle }, { "canGoBack", webView.CoreWebView2.CanGoBack.ToString().ToLower() }, { "canGoForward", webView.CoreWebView2.CanGoForward.ToString().ToLower() } }
|
|
400
|
+
});
|
|
401
|
+
} else {
|
|
402
|
+
error($"Navigation failed: {e.WebErrorStatus}");
|
|
403
|
+
}
|
|
386
404
|
};
|
|
387
405
|
|
|
388
406
|
webView.CoreWebView2.ContextMenuRequested += (s, e) =>
|
|
@@ -624,14 +642,19 @@ case "forceCloseWindow":
|
|
|
624
642
|
|
|
625
643
|
case "loadURL":
|
|
626
644
|
if (!WindowsMap.TryGetValue(windowId, out _)) break;
|
|
627
|
-
if (args.Count == 0)
|
|
645
|
+
if (args.Count == 0 || !Uri.TryCreate(args[0], UriKind.Absolute, out var loadUrlUri))
|
|
628
646
|
{
|
|
629
647
|
error("loadURL — invalid or missing URL");
|
|
630
648
|
break;
|
|
631
649
|
}
|
|
650
|
+
if (loadUrlUri.Scheme != Uri.UriSchemeHttp && loadUrlUri.Scheme != Uri.UriSchemeHttps && loadUrlUri.Scheme != Uri.UriSchemeFile)
|
|
651
|
+
{
|
|
652
|
+
error($"loadURL — blocked unauthorized URL scheme: {loadUrlUri.Scheme}");
|
|
653
|
+
break;
|
|
654
|
+
}
|
|
632
655
|
{
|
|
633
656
|
var wv = GetWebView(windowId);
|
|
634
|
-
if (wv != null) wv.Source =
|
|
657
|
+
if (wv != null) wv.Source = loadUrlUri;
|
|
635
658
|
}
|
|
636
659
|
break;
|
|
637
660
|
|
|
@@ -1269,11 +1292,12 @@ case "setBounds":
|
|
|
1269
1292
|
if (_reconnectAttempts >= MaxReconnectAttempts)
|
|
1270
1293
|
{
|
|
1271
1294
|
error($"Exceeded maximum reconnect attempts ({MaxReconnectAttempts}). Giving up.");
|
|
1272
|
-
|
|
1295
|
+
Environment.Exit(1);
|
|
1273
1296
|
}
|
|
1274
1297
|
|
|
1275
1298
|
try
|
|
1276
1299
|
{
|
|
1300
|
+
if (_ws != null) _ws.Dispose();
|
|
1277
1301
|
_ws = new ClientWebSocket();
|
|
1278
1302
|
_ws.Options.SetRequestHeader("x-positron-auth-token", authToken);
|
|
1279
1303
|
error($"INFO: Connecting to IPC server (attempt {_reconnectAttempts + 1})…");
|
|
@@ -1285,8 +1309,18 @@ case "setBounds":
|
|
|
1285
1309
|
catch (Exception ex)
|
|
1286
1310
|
{
|
|
1287
1311
|
_reconnectAttempts++;
|
|
1288
|
-
|
|
1289
|
-
|
|
1312
|
+
string errorMsg = ex.Message;
|
|
1313
|
+
if (ex.InnerException != null) errorMsg += " | " + ex.InnerException.Message;
|
|
1314
|
+
|
|
1315
|
+
error($"WebSocket error: {errorMsg}. Reconnecting in {ReconnectDelayMs / 1000}s… (attempt {_reconnectAttempts}/{MaxReconnectAttempts})");
|
|
1316
|
+
|
|
1317
|
+
if (errorMsg.Contains("503") || errorMsg.Contains("401") || errorMsg.Contains("403"))
|
|
1318
|
+
{
|
|
1319
|
+
error("Fatal connection error (Unauthorized or Port Hijacked). Exiting immediately.");
|
|
1320
|
+
Environment.Exit(1);
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1323
|
+
await Task.Delay(ReconnectDelayMs, _cts.Token);
|
|
1290
1324
|
}
|
|
1291
1325
|
}
|
|
1292
1326
|
}
|
package/index.js
CHANGED
|
@@ -37,12 +37,11 @@ const binaryPath = path.join(appRoot, "bin", binaryName);
|
|
|
37
37
|
|
|
38
38
|
const appEvents = new Events.EventEmitter();
|
|
39
39
|
|
|
40
|
-
|
|
41
40
|
const isPackaged = process.env.POSITRON_PACKAGED === "true";
|
|
42
41
|
|
|
43
42
|
if(isPackaged) {
|
|
44
43
|
if (typeof process.pkg !== 'undefined') {
|
|
45
|
-
if (process.platform === 'darwin') {
|
|
44
|
+
if (process.platform === 'darwin' || process.platform === 'linux') {
|
|
46
45
|
__dirname = path.join(path.dirname(process.execPath), '.');
|
|
47
46
|
} else {
|
|
48
47
|
__dirname = path.dirname(process.execPath);
|
|
@@ -52,11 +51,6 @@ if (typeof process.pkg !== 'undefined') {
|
|
|
52
51
|
|
|
53
52
|
const EXPECTED_TOKEN = process.env.POSITRON_AUTH_TOKEN;
|
|
54
53
|
|
|
55
|
-
const parseRes = (obj) => {
|
|
56
|
-
if (Object.keys(obj) > 1) return obj;
|
|
57
|
-
|
|
58
|
-
return Object.values(obj)[0];
|
|
59
|
-
}
|
|
60
54
|
|
|
61
55
|
if (!isPackaged) {
|
|
62
56
|
// DEV MODE
|
|
@@ -69,6 +63,8 @@ if (!isPackaged) {
|
|
|
69
63
|
}
|
|
70
64
|
}
|
|
71
65
|
|
|
66
|
+
// setTimeout(() => { // FOR HIJACK TESTING
|
|
67
|
+
|
|
72
68
|
info("Starting Positron render process...");
|
|
73
69
|
const renderProcess = cp.spawn(binaryPath, {
|
|
74
70
|
env: {
|
|
@@ -102,12 +98,20 @@ process.on("uncaughtException", (err) => {
|
|
|
102
98
|
info(`[Positron] Render process exited with code ${code}`);
|
|
103
99
|
process.exit(code);
|
|
104
100
|
});
|
|
101
|
+
// }, 60000);
|
|
105
102
|
} else {
|
|
106
103
|
// PRODUCTION MODE
|
|
107
104
|
info("[Positron] Packaged mode detected. Skipping native binary spawn.");
|
|
108
105
|
}
|
|
109
106
|
|
|
110
107
|
const httpServer = http.createServer((req, res) => {
|
|
108
|
+
const clientToken = req.headers["x-positron-auth-token"];
|
|
109
|
+
if (clientToken !== EXPECTED_TOKEN) {
|
|
110
|
+
res.writeHead(401, { 'Content-Type': 'text/plain' });
|
|
111
|
+
res.end('Unauthorized');
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
111
115
|
if (req.method === 'GET' && req.url === '/running') {
|
|
112
116
|
res.writeHead(200, { 'Content-Type': 'text/plain' });
|
|
113
117
|
res.end('true');
|
|
@@ -117,7 +121,25 @@ const httpServer = http.createServer((req, res) => {
|
|
|
117
121
|
}
|
|
118
122
|
});
|
|
119
123
|
|
|
120
|
-
const
|
|
124
|
+
const MAX_CONNECTIONS = 1;
|
|
125
|
+
|
|
126
|
+
const _ipcWS = new WebSocket.Server({ server: httpServer, verifyClient: (info, cb) => {
|
|
127
|
+
|
|
128
|
+
const clientToken = info.req.headers["x-positron-auth-token"];
|
|
129
|
+
if (clientToken !== EXPECTED_TOKEN) {
|
|
130
|
+
warn("[Security] Unauthorized local connection attempt rejected.");
|
|
131
|
+
cb(false, 401, "Unauthorized token match failure.");
|
|
132
|
+
return
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (_ipcWS.clients.size >= MAX_CONNECTIONS) {
|
|
136
|
+
return cb(false, 503, 'IPC client already connected. Only one client allowed at a time.');
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
cb(true);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
});
|
|
121
143
|
let activeSocket = null;
|
|
122
144
|
const pendingWindows = new Set();
|
|
123
145
|
|
|
@@ -127,14 +149,6 @@ const commandQueue = [];
|
|
|
127
149
|
let activeWindows = new Set();
|
|
128
150
|
|
|
129
151
|
_ipcWS.on("connection", (ws, req) => {
|
|
130
|
-
const clientToken = req.headers["x-positron-auth-token"];
|
|
131
|
-
|
|
132
|
-
if (clientToken !== EXPECTED_TOKEN) {
|
|
133
|
-
warn("[Security] Unauthorized local connection attempt rejected. Token:", clientToken, "Expected:", EXPECTED_TOKEN);
|
|
134
|
-
ws.close(4001, "Unauthorized token match failure.");
|
|
135
|
-
return;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
152
|
activeSocket = ws;
|
|
139
153
|
success("Client connected to IPC");
|
|
140
154
|
|
|
@@ -154,7 +168,7 @@ ws.on("message", raw => {
|
|
|
154
168
|
|
|
155
169
|
if(process.env.POSITRON_LOG_IPC) console.log("Received IPC message:", msg);
|
|
156
170
|
|
|
157
|
-
if (msg.event === "ipcMessage" || msg.event.includes("-reply-") || msg.event.includes("-result-")) {
|
|
171
|
+
if (msg.event === "ipcMessage" || msg.event.includes("-reply-") || msg.event.includes("-result-") || msg.event === "nativeError") {
|
|
158
172
|
|
|
159
173
|
const simulatedMsg = msg.event === "ipcMessage" ? msg : {
|
|
160
174
|
event: "ipcMessage",
|
|
@@ -216,7 +230,7 @@ ws.on("message", raw => {
|
|
|
216
230
|
appEvents.emit(msg.event, msg.data);
|
|
217
231
|
}
|
|
218
232
|
} catch (err) {
|
|
219
|
-
error("Failed to process incoming IPC network frame:", err);
|
|
233
|
+
error("Failed to process incoming IPC network frame:", err, err.stack.split('\n').slice(1).join('\n'));
|
|
220
234
|
}
|
|
221
235
|
});
|
|
222
236
|
|
|
@@ -243,12 +257,20 @@ class Window extends Events.EventEmitter {
|
|
|
243
257
|
minimizable: true,
|
|
244
258
|
titlebarTransparent: false,
|
|
245
259
|
titlebarVisible: true
|
|
246
|
-
}
|
|
260
|
+
},
|
|
261
|
+
linuxOptions: {
|
|
262
|
+
closable: true,
|
|
263
|
+
resizable: true,
|
|
264
|
+
minimizable: true,
|
|
265
|
+
titlebarTransparent: false,
|
|
266
|
+
titlebarVisible: true
|
|
267
|
+
},
|
|
268
|
+
allowEvaluateJS: false
|
|
247
269
|
|
|
248
270
|
}) {
|
|
249
271
|
super();
|
|
250
272
|
this.id = ++_windowCounter;
|
|
251
|
-
this.options = options;
|
|
273
|
+
this.options = { allowEvaluateJS: false, ...options };
|
|
252
274
|
activeWindows.add(this);
|
|
253
275
|
|
|
254
276
|
if (activeSocket && activeSocket.readyState === WebSocket.OPEN) {
|
|
@@ -261,7 +283,11 @@ class Window extends Events.EventEmitter {
|
|
|
261
283
|
const height = options.height ? String(options.height) : "600";
|
|
262
284
|
|
|
263
285
|
if(!this.options.skipCreate) {
|
|
264
|
-
|
|
286
|
+
if (process.platform === "linux") {
|
|
287
|
+
this.create(width, height, options.linuxOptions || options.darwinOptions);
|
|
288
|
+
} else {
|
|
289
|
+
this.create(width, height, options.darwinOptions);
|
|
290
|
+
}
|
|
265
291
|
}
|
|
266
292
|
|
|
267
293
|
}
|
|
@@ -698,7 +724,8 @@ async request(command, ...args) {
|
|
|
698
724
|
}
|
|
699
725
|
});
|
|
700
726
|
|
|
701
|
-
|
|
727
|
+
let timeout;
|
|
728
|
+
|
|
702
729
|
|
|
703
730
|
if(!options.noTimeout) {
|
|
704
731
|
let timeoutDuration = 7000;
|
|
@@ -712,7 +739,8 @@ if (options.timeout) {
|
|
|
712
739
|
if (!settled) {
|
|
713
740
|
settled = true;
|
|
714
741
|
unsubscribe();
|
|
715
|
-
|
|
742
|
+
// reject(new Error(`Request timed out waiting for reply on channel "${replyChannel}"`));
|
|
743
|
+
resolve({ error: `Request timed out waiting for reply on channel "${replyChannel}"` });
|
|
716
744
|
}
|
|
717
745
|
}, timeoutDuration);
|
|
718
746
|
} else {
|
|
@@ -882,8 +910,16 @@ setTitlebarTransparent(isTransparent) {
|
|
|
882
910
|
* @returns {Promise<*>} A Promise that resolves to the result of the evaluation.
|
|
883
911
|
*/
|
|
884
912
|
async evaluateJavaScript(script) {
|
|
885
|
-
|
|
886
|
-
|
|
913
|
+
if (!this.options.allowEvaluateJS) {
|
|
914
|
+
throw new Error("evaluateJavaScript is disabled by default for security. Set allowEvaluateJS: true in window options to enable it.");
|
|
915
|
+
}
|
|
916
|
+
return await this.#evaluateJavaScriptInternal(script);
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
|
|
920
|
+
async #evaluateJavaScriptInternal(script) {
|
|
921
|
+
const res = await this.request("evaluateJS", script);
|
|
922
|
+
return res.result;
|
|
887
923
|
}
|
|
888
924
|
|
|
889
925
|
/**
|
|
@@ -891,7 +927,7 @@ return res.result;
|
|
|
891
927
|
* @returns {Promise<string>} The user agent string of the window.
|
|
892
928
|
*/
|
|
893
929
|
async getUserAgent() {
|
|
894
|
-
return await this
|
|
930
|
+
return await this.#evaluateJavaScriptInternal("navigator.userAgent");
|
|
895
931
|
}
|
|
896
932
|
|
|
897
933
|
/**
|
|
@@ -912,7 +948,7 @@ async setStyleOf(selector, style) {
|
|
|
912
948
|
});
|
|
913
949
|
})();
|
|
914
950
|
`;
|
|
915
|
-
await this
|
|
951
|
+
await this.#evaluateJavaScriptInternal(script);
|
|
916
952
|
this.emit("style-updated", { selector, style });
|
|
917
953
|
}
|
|
918
954
|
|
|
@@ -932,7 +968,7 @@ async setAttributeOf(selector, attribute, value) {
|
|
|
932
968
|
});
|
|
933
969
|
})();
|
|
934
970
|
`;
|
|
935
|
-
await this
|
|
971
|
+
await this.#evaluateJavaScriptInternal(script);
|
|
936
972
|
this.emit("attribute-updated", { selector, attribute, value });
|
|
937
973
|
}
|
|
938
974
|
|
|
@@ -951,7 +987,7 @@ async removeAttributeOf(selector, attribute) {
|
|
|
951
987
|
});
|
|
952
988
|
})();
|
|
953
989
|
`;
|
|
954
|
-
await this
|
|
990
|
+
await this.#evaluateJavaScriptInternal(script);
|
|
955
991
|
this.emit("attribute-removed", { selector, attribute });
|
|
956
992
|
}
|
|
957
993
|
|
|
@@ -973,7 +1009,7 @@ async removeStyleOf(selector, styleProperties) {
|
|
|
973
1009
|
});
|
|
974
1010
|
})();
|
|
975
1011
|
`;
|
|
976
|
-
await this
|
|
1012
|
+
await this.#evaluateJavaScriptInternal(script);
|
|
977
1013
|
this.emit("style-removed", { selector, styleProperties });
|
|
978
1014
|
}
|
|
979
1015
|
|
|
@@ -1008,7 +1044,7 @@ async onClick(selector, channel, { replace = true } = {}) {
|
|
|
1008
1044
|
})();
|
|
1009
1045
|
`;
|
|
1010
1046
|
|
|
1011
|
-
await this
|
|
1047
|
+
await this.#evaluateJavaScriptInternal(script);
|
|
1012
1048
|
}
|
|
1013
1049
|
|
|
1014
1050
|
/**
|
|
@@ -1023,7 +1059,7 @@ async removeOnClick(selector) {
|
|
|
1023
1059
|
el.onclick = null;
|
|
1024
1060
|
});
|
|
1025
1061
|
`;
|
|
1026
|
-
await this
|
|
1062
|
+
await this.#evaluateJavaScriptInternal(script);
|
|
1027
1063
|
}
|
|
1028
1064
|
|
|
1029
1065
|
/**
|
|
@@ -1033,8 +1069,8 @@ async removeOnClick(selector) {
|
|
|
1033
1069
|
*/
|
|
1034
1070
|
async confirm(message) {
|
|
1035
1071
|
const res = await this.request("confirm", message);
|
|
1036
|
-
this.emit("confirm",
|
|
1037
|
-
return res?.confirmed === "true";
|
|
1072
|
+
this.emit("confirm", res?.confirmed);
|
|
1073
|
+
return res?.confirmed == true || res?.confirmed === "true";
|
|
1038
1074
|
}
|
|
1039
1075
|
|
|
1040
1076
|
/**
|
|
@@ -1217,6 +1253,11 @@ userData: {
|
|
|
1217
1253
|
"Application Support",
|
|
1218
1254
|
process.env.POSITRON_APP_NAME
|
|
1219
1255
|
);
|
|
1256
|
+
} else {
|
|
1257
|
+
// Linux / other POSIX — follow XDG Base Directory spec
|
|
1258
|
+
const xdgDataHome = process.env.XDG_DATA_HOME
|
|
1259
|
+
|| path.join(process.env.HOME, ".local", "share");
|
|
1260
|
+
userPath = path.join(xdgDataHome, process.env.POSITRON_APP_NAME);
|
|
1220
1261
|
}
|
|
1221
1262
|
|
|
1222
1263
|
if(!fs.existsSync(userPath)) {
|
package/package.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"url": "https://github.com/systemsoftware/positron.js"
|
|
6
6
|
},
|
|
7
7
|
"homepage": "https://positronjs.gitbook.io",
|
|
8
|
-
"version": "1.0
|
|
8
|
+
"version": "1.1.0",
|
|
9
9
|
"main": "index.js",
|
|
10
10
|
"scripts": {
|
|
11
11
|
"test": "node --test"
|
|
@@ -19,7 +19,8 @@
|
|
|
19
19
|
"javascript",
|
|
20
20
|
"node",
|
|
21
21
|
"macos",
|
|
22
|
-
"windows"
|
|
22
|
+
"windows",
|
|
23
|
+
"linux"
|
|
23
24
|
],
|
|
24
25
|
"author": "Bryce",
|
|
25
26
|
"license": "MIT",
|