@nextop-os/browser-node 0.0.14 → 0.0.16

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.
@@ -3,15 +3,460 @@ import {
3
3
  resolveBrowserNavigationUrl
4
4
  } from "../chunk-2ZAMKGGP.js";
5
5
  import {
6
- isBrowserSessionPartitionAllowed
6
+ isBrowserSessionPartitionAllowed,
7
+ resolveBrowserSessionPartition
7
8
  } from "../chunk-OTK5YBCK.js";
8
9
 
9
- // src/electron-main/previewRoute.ts
10
- function createNoopBrowserNodePreviewRouteResolver() {
10
+ // src/electron-main/loopbackPreviewProxy.ts
11
+ import {
12
+ createServer,
13
+ request as httpRequest
14
+ } from "http";
15
+ import { request as httpsRequest } from "https";
16
+ import { createRequire } from "module";
17
+ import { pipeline } from "stream";
18
+ var require2 = createRequire(import.meta.url);
19
+ var wsModule = require2("ws");
20
+ var WebSocket = wsModule;
21
+ var { WebSocketServer } = wsModule;
22
+ var defaultLoopbackPreviewCacheTtlMs = 3e4;
23
+ var loopbackHostPattern = /^(localhost|127(?:\.\d{1,3}){0,3})$/i;
24
+ var hopByHopHeaders = /* @__PURE__ */ new Set([
25
+ "connection",
26
+ "keep-alive",
27
+ "proxy-authenticate",
28
+ "proxy-authorization",
29
+ "proxy-connection",
30
+ "te",
31
+ "trailer",
32
+ "transfer-encoding",
33
+ "upgrade"
34
+ ]);
35
+ function createBrowserNodeLoopbackPreviewProxy({
36
+ logger,
37
+ resolveSession,
38
+ routing
39
+ }) {
40
+ const configuredSessions = /* @__PURE__ */ new WeakSet();
41
+ const targetCache = /* @__PURE__ */ new Map();
42
+ const downstreamWebSocketServer = new WebSocketServer({ noServer: true });
43
+ let server = null;
44
+ let serverStartPromise = null;
45
+ const cacheTtlMs = routing.cacheTtlMs ?? defaultLoopbackPreviewCacheTtlMs;
46
+ const fallback = routing.fallback ?? "direct";
47
+ const pruneExpiredTargets = (now) => {
48
+ for (const [cacheKey, cachedTarget] of targetCache.entries()) {
49
+ if (cachedTarget.expiresAt <= now) {
50
+ targetCache.delete(cacheKey);
51
+ }
52
+ }
53
+ };
54
+ const start = async () => {
55
+ if (server?.listening) {
56
+ return addressForTesting();
57
+ }
58
+ if (serverStartPromise) {
59
+ return serverStartPromise;
60
+ }
61
+ serverStartPromise = new Promise((resolve, reject) => {
62
+ const nextServer = createServer((request, response) => {
63
+ void handleRequest(request, response);
64
+ });
65
+ nextServer.on("upgrade", (request, socket, head) => {
66
+ void handleUpgrade(request, socket, head);
67
+ });
68
+ const cleanup = () => {
69
+ nextServer.removeListener("error", onError);
70
+ nextServer.removeListener("listening", onListening);
71
+ };
72
+ const onError = (error) => {
73
+ cleanup();
74
+ reject(error);
75
+ };
76
+ const onListening = () => {
77
+ cleanup();
78
+ server = nextServer;
79
+ resolve(addressForTesting());
80
+ };
81
+ nextServer.once("error", onError);
82
+ nextServer.once("listening", onListening);
83
+ nextServer.listen(0, "127.0.0.1");
84
+ }).finally(() => {
85
+ serverStartPromise = null;
86
+ });
87
+ return serverStartPromise;
88
+ };
89
+ const resolveLoopbackTarget = async (originalUrl) => {
90
+ const port = Number(originalUrl.port);
91
+ if (!Number.isInteger(port) || port < 1 || port > 65535) {
92
+ return null;
93
+ }
94
+ const now = Date.now();
95
+ pruneExpiredTargets(now);
96
+ const cacheKey = originalUrl.toString();
97
+ const cached = targetCache.get(cacheKey);
98
+ if (cached && cached.expiresAt > now) {
99
+ return cached.target;
100
+ }
101
+ const nextTarget = await Promise.resolve(
102
+ routing.resolver.resolveTarget({
103
+ port,
104
+ url: originalUrl.toString()
105
+ })
106
+ );
107
+ const target = normalizeLoopbackPreviewTarget(nextTarget);
108
+ targetCache.set(cacheKey, {
109
+ expiresAt: now + Math.max(0, cacheTtlMs),
110
+ target
111
+ });
112
+ return target;
113
+ };
114
+ const configureSession = async (input) => {
115
+ const nextSession = await resolveSession(input);
116
+ if (configuredSessions.has(nextSession)) {
117
+ return;
118
+ }
119
+ const address = await start();
120
+ await nextSession.setProxy({
121
+ mode: "fixed_servers",
122
+ proxyBypassRules: "<-loopback>",
123
+ proxyRules: `http=${address};https=${address};ws=${address}`
124
+ });
125
+ configuredSessions.add(nextSession);
126
+ };
127
+ const dispose = async () => {
128
+ targetCache.clear();
129
+ downstreamWebSocketServer.close();
130
+ const activeServer = server;
131
+ server = null;
132
+ if (!activeServer || !activeServer.listening) {
133
+ return;
134
+ }
135
+ await new Promise((resolve) => {
136
+ activeServer.close(() => resolve());
137
+ });
138
+ };
139
+ const handleRequest = async (request, response) => {
140
+ const originalUrl = resolveProxyRequestUrl(request);
141
+ if (!originalUrl) {
142
+ response.writeHead(400);
143
+ response.end("invalid proxy request");
144
+ return;
145
+ }
146
+ if (originalUrl.protocol !== "http:" && originalUrl.protocol !== "https:") {
147
+ response.writeHead(400);
148
+ response.end("unsupported proxy request");
149
+ return;
150
+ }
151
+ const targetContext = await resolveTargetRequestContext(originalUrl);
152
+ if (!targetContext.targetUrl) {
153
+ response.writeHead(502);
154
+ response.end("unable to resolve loopback preview target");
155
+ return;
156
+ }
157
+ const headers = filterProxyHeaders(request.headers);
158
+ headers.set("host", targetContext.targetUrl.host);
159
+ const forward = targetContext.targetUrl.protocol === "https:" ? httpsRequest : httpRequest;
160
+ const upstream = forward(
161
+ targetContext.targetUrl,
162
+ {
163
+ headers: Object.fromEntries(headers.entries()),
164
+ method: request.method
165
+ },
166
+ (upstreamResponse) => {
167
+ const responseHeaders = filterProxyHeaders(upstreamResponse.headers);
168
+ const location = responseHeaders.get("location");
169
+ if (location && targetContext.loopbackTarget) {
170
+ const rewritten = rewriteLoopbackLocation({
171
+ location,
172
+ originalUrl,
173
+ targetUrl: targetContext.loopbackTarget.targetUrl
174
+ });
175
+ if (rewritten) {
176
+ responseHeaders.set("location", rewritten);
177
+ }
178
+ }
179
+ response.writeHead(
180
+ upstreamResponse.statusCode ?? 502,
181
+ upstreamResponse.statusMessage,
182
+ Object.fromEntries(responseHeaders.entries())
183
+ );
184
+ pipeline(upstreamResponse, response, () => void 0);
185
+ }
186
+ );
187
+ upstream.on("error", (error) => {
188
+ logger?.warn?.("Browser Node loopback preview request failed", {
189
+ error: normalizeProxyError(error),
190
+ originalUrl: originalUrl.toString(),
191
+ targetUrl: targetContext.targetUrl?.toString() ?? null,
192
+ workspaceId: targetContext.loopbackTarget?.workspaceId ?? null
193
+ });
194
+ if (!response.headersSent) {
195
+ response.writeHead(502);
196
+ }
197
+ response.end(
198
+ `loopback preview upstream request failed: ${normalizeProxyError(error)}`
199
+ );
200
+ });
201
+ pipeline(request, upstream, () => void 0);
202
+ };
203
+ const handleUpgrade = async (request, socket, head) => {
204
+ const originalUrl = resolveProxyRequestUrl(request, "ws:");
205
+ if (!originalUrl) {
206
+ socket.destroy();
207
+ return;
208
+ }
209
+ if (originalUrl.protocol !== "ws:" && originalUrl.protocol !== "wss:") {
210
+ socket.destroy();
211
+ return;
212
+ }
213
+ const targetContext = await resolveTargetRequestContext(originalUrl);
214
+ const targetUrl = targetContext.targetUrl;
215
+ if (!targetUrl) {
216
+ socket.destroy();
217
+ return;
218
+ }
219
+ downstreamWebSocketServer.handleUpgrade(
220
+ request,
221
+ socket,
222
+ head,
223
+ (downstream) => {
224
+ const headers = Object.fromEntries(
225
+ filterProxyHeaders(request.headers).entries()
226
+ );
227
+ headers.host = targetUrl.host;
228
+ const upstream = new WebSocket(targetUrl, { headers });
229
+ const pendingMessages = [];
230
+ downstream.on("message", (data, isBinary) => {
231
+ if (upstream.readyState === WebSocket.OPEN) {
232
+ upstream.send(data, { binary: isBinary });
233
+ return;
234
+ }
235
+ pendingMessages.push({ data, isBinary });
236
+ });
237
+ upstream.once("open", () => {
238
+ for (const nextMessage of pendingMessages.splice(0)) {
239
+ upstream.send(nextMessage.data, { binary: nextMessage.isBinary });
240
+ }
241
+ });
242
+ upstream.on("message", (data, isBinary) => {
243
+ if (downstream.readyState === WebSocket.OPEN) {
244
+ downstream.send(data, { binary: isBinary });
245
+ }
246
+ });
247
+ upstream.once("close", (code, reason) => {
248
+ if (downstream.readyState === WebSocket.OPEN || downstream.readyState === WebSocket.CONNECTING) {
249
+ downstream.close(normalizeWebSocketCloseCode(code), reason);
250
+ }
251
+ });
252
+ downstream.once("close", (code, reason) => {
253
+ if (upstream.readyState === WebSocket.OPEN || upstream.readyState === WebSocket.CONNECTING) {
254
+ upstream.close(normalizeWebSocketCloseCode(code), reason);
255
+ }
256
+ });
257
+ upstream.once("error", (error) => {
258
+ logger?.warn?.("Browser Node loopback preview websocket failed", {
259
+ error: normalizeProxyError(error),
260
+ originalUrl: originalUrl.toString(),
261
+ targetUrl: targetContext.targetUrl?.toString() ?? null,
262
+ workspaceId: targetContext.loopbackTarget?.workspaceId ?? null
263
+ });
264
+ downstream.close(1011, "loopback preview websocket upstream failed");
265
+ });
266
+ }
267
+ );
268
+ };
269
+ const resolveTargetRequestContext = async (originalUrl) => {
270
+ if (!isLoopbackUrl(originalUrl)) {
271
+ return {
272
+ loopbackTarget: null,
273
+ targetUrl: cloneUrl(originalUrl)
274
+ };
275
+ }
276
+ const loopbackTarget = await resolveLoopbackTarget(originalUrl);
277
+ if (loopbackTarget) {
278
+ return {
279
+ loopbackTarget,
280
+ targetUrl: buildTargetRequestUrl(loopbackTarget.targetUrl, originalUrl)
281
+ };
282
+ }
283
+ if (fallback === "direct") {
284
+ return {
285
+ loopbackTarget: null,
286
+ targetUrl: cloneUrl(originalUrl)
287
+ };
288
+ }
289
+ return {
290
+ loopbackTarget: null,
291
+ targetUrl: null
292
+ };
293
+ };
294
+ const addressForTesting = () => {
295
+ const address = server?.address();
296
+ if (!address || typeof address === "string") {
297
+ throw new Error("Browser Node loopback preview proxy is not listening");
298
+ }
299
+ return `127.0.0.1:${address.port}`;
300
+ };
301
+ const serverForTesting = () => {
302
+ if (!server) {
303
+ throw new Error("Browser Node loopback preview proxy is not started");
304
+ }
305
+ return server;
306
+ };
11
307
  return {
12
- resolveRoutes: () => []
308
+ addressForTesting,
309
+ configureSession,
310
+ dispose,
311
+ serverForTesting
13
312
  };
14
313
  }
314
+ function normalizeLoopbackPreviewTarget(input) {
315
+ if (!input) {
316
+ return null;
317
+ }
318
+ const normalizedTargetUrl = input.targetUrl.trim();
319
+ if (normalizedTargetUrl.length === 0) {
320
+ return null;
321
+ }
322
+ try {
323
+ const parsed = new URL(normalizedTargetUrl);
324
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
325
+ return null;
326
+ }
327
+ return {
328
+ targetUrl: parsed.toString(),
329
+ workspaceId: input.workspaceId
330
+ };
331
+ } catch {
332
+ return null;
333
+ }
334
+ }
335
+ function resolveProxyRequestUrl(request, defaultProtocol = "http:") {
336
+ const rawUrl = request.url ?? "";
337
+ try {
338
+ if (/^[a-z][a-z\d+\-.]*:\/\//i.test(rawUrl)) {
339
+ return new URL(rawUrl);
340
+ }
341
+ const host = request.headers.host;
342
+ if (typeof host !== "string" || host.trim().length === 0) {
343
+ return null;
344
+ }
345
+ const path = rawUrl.startsWith("/") ? rawUrl : `/${rawUrl}`;
346
+ return new URL(`${defaultProtocol}//${host}${path}`);
347
+ } catch {
348
+ return null;
349
+ }
350
+ }
351
+ function filterProxyHeaders(input) {
352
+ const output = new Headers();
353
+ const appendValue = (key, value) => {
354
+ if (hopByHopHeaders.has(key.toLowerCase())) {
355
+ return;
356
+ }
357
+ output.append(key, value);
358
+ };
359
+ if (input instanceof Headers) {
360
+ input.forEach((value, key) => {
361
+ appendValue(key, value);
362
+ });
363
+ return output;
364
+ }
365
+ for (const [key, value] of Object.entries(input)) {
366
+ if (Array.isArray(value)) {
367
+ for (const nextValue of value) {
368
+ appendValue(key, nextValue);
369
+ }
370
+ continue;
371
+ }
372
+ if (typeof value === "string") {
373
+ appendValue(key, value);
374
+ }
375
+ }
376
+ return output;
377
+ }
378
+ function isLoopbackUrl(url) {
379
+ return (url.protocol === "http:" || url.protocol === "https:" || url.protocol === "ws:" || url.protocol === "wss:") && loopbackHostPattern.test(url.hostname) && url.port.length > 0;
380
+ }
381
+ function buildTargetRequestUrl(targetUrl, originalUrl) {
382
+ try {
383
+ const base = new URL(targetUrl);
384
+ const normalizedBasePath = normalizeBasePath(base.pathname);
385
+ const nextUrl = new URL(base.toString());
386
+ nextUrl.pathname = joinBasePath(normalizedBasePath, originalUrl.pathname);
387
+ nextUrl.search = originalUrl.search;
388
+ nextUrl.hash = "";
389
+ if (originalUrl.protocol === "ws:" || originalUrl.protocol === "wss:") {
390
+ nextUrl.protocol = nextUrl.protocol === "https:" ? "wss:" : "ws:";
391
+ }
392
+ return nextUrl;
393
+ } catch {
394
+ return null;
395
+ }
396
+ }
397
+ function rewriteLoopbackLocation({
398
+ location,
399
+ originalUrl,
400
+ targetUrl
401
+ }) {
402
+ try {
403
+ const targetBase = new URL(targetUrl);
404
+ const resolvedLocation = new URL(location, targetBase);
405
+ if (resolvedLocation.origin !== targetBase.origin) {
406
+ return location;
407
+ }
408
+ const basePath = normalizeBasePath(targetBase.pathname);
409
+ const strippedPath = stripBasePath(basePath, resolvedLocation.pathname) ?? resolvedLocation.pathname;
410
+ const loopbackUrl = new URL(originalUrl.origin);
411
+ resolvedLocation.protocol = loopbackUrl.protocol;
412
+ resolvedLocation.host = loopbackUrl.host;
413
+ resolvedLocation.pathname = strippedPath;
414
+ resolvedLocation.username = "";
415
+ resolvedLocation.password = "";
416
+ return resolvedLocation.toString();
417
+ } catch {
418
+ return location;
419
+ }
420
+ }
421
+ function normalizeBasePath(pathname) {
422
+ const normalized = pathname.startsWith("/") ? pathname : `/${pathname}`;
423
+ return normalized.endsWith("/") ? normalized : `${normalized}/`;
424
+ }
425
+ function joinBasePath(basePath, requestPath) {
426
+ const normalizedRequestPath = requestPath === "/" ? "" : requestPath.replace(/^\/+/, "");
427
+ return normalizedRequestPath.length > 0 ? `${basePath}${normalizedRequestPath}`.replace(/\/{2,}/g, "/") : basePath;
428
+ }
429
+ function stripBasePath(basePath, pathname) {
430
+ const normalizedPath = pathname.startsWith("/") ? pathname : `/${pathname}`;
431
+ if (normalizedPath === basePath.slice(0, -1)) {
432
+ return "/";
433
+ }
434
+ if (!normalizedPath.startsWith(basePath)) {
435
+ return null;
436
+ }
437
+ const suffix = normalizedPath.slice(basePath.length);
438
+ return suffix.length > 0 ? `/${suffix}` : "/";
439
+ }
440
+ function cloneUrl(url) {
441
+ try {
442
+ return new URL(url.toString());
443
+ } catch {
444
+ return null;
445
+ }
446
+ }
447
+ function normalizeProxyError(error) {
448
+ if (error instanceof Error) {
449
+ const code = typeof error.code === "string" ? ` ${error.code}` : "";
450
+ return `${error.message}${code}`;
451
+ }
452
+ return String(error);
453
+ }
454
+ function normalizeWebSocketCloseCode(code) {
455
+ if (code === 1e3 || code >= 3e3 && code <= 4999) {
456
+ return code;
457
+ }
458
+ return 1e3;
459
+ }
15
460
 
16
461
  // src/electron-main/guestManager.ts
17
462
  var browserPreviewMaxWidth = 260;
@@ -54,15 +499,37 @@ function resizeBrowserPreviewImage(image) {
54
499
  function isAbortedNavigationError(input) {
55
500
  return input.errorCode === abortedNavigationErrorCode || input.errorDescription === "ERR_ABORTED";
56
501
  }
502
+ async function applyPreferredColorSchemeToGuest(session, logger, syncPreferredColorScheme, scheme) {
503
+ const contents = session.contents;
504
+ if (!contents || contents.isDestroyed() || !syncPreferredColorScheme || scheme === null || session.appliedColorScheme === scheme) {
505
+ return;
506
+ }
507
+ try {
508
+ await syncPreferredColorScheme(contents, scheme);
509
+ session.appliedColorScheme = scheme;
510
+ } catch (error) {
511
+ session.appliedColorScheme = null;
512
+ logger?.warn?.("Browser Node failed to sync guest color scheme", {
513
+ error: error instanceof Error ? error.message : String(error),
514
+ nodeId: session.nodeId,
515
+ scheme,
516
+ webContentsId: session.webContentsId
517
+ });
518
+ }
519
+ }
57
520
  function createBrowserGuestManager({
58
521
  emit,
522
+ getPreferredColorScheme,
59
523
  logger,
60
524
  openExternal,
61
- previewRouteResolver = createNoopBrowserNodePreviewRouteResolver(),
62
- resolveWebContents
525
+ prepareSession,
526
+ resolveWebContents,
527
+ syncPreferredColorScheme,
528
+ subscribePreferredColorScheme
63
529
  }) {
64
530
  const sessions = /* @__PURE__ */ new Map();
65
531
  const nodeIdByWebContentsId = /* @__PURE__ */ new Map();
532
+ let preferredColorScheme = getPreferredColorScheme?.() ?? null;
66
533
  const getSession = (nodeId, input) => {
67
534
  const existing = sessions.get(nodeId);
68
535
  if (existing) {
@@ -78,6 +545,7 @@ function createBrowserGuestManager({
78
545
  return existing;
79
546
  }
80
547
  const session = {
548
+ appliedColorScheme: null,
81
549
  contents: null,
82
550
  desiredUrl: input?.url ?? "about:blank",
83
551
  lifecycle: "cold",
@@ -115,12 +583,26 @@ function createBrowserGuestManager({
115
583
  }
116
584
  session.listeners = [];
117
585
  session.contents = null;
586
+ session.appliedColorScheme = null;
118
587
  session.webContentsId = null;
119
588
  if (webContentsId !== null && nodeIdByWebContentsId.get(webContentsId) === session.nodeId) {
120
589
  nodeIdByWebContentsId.delete(webContentsId);
121
590
  }
122
591
  publishState(session);
123
592
  };
593
+ const handlePreferredColorSchemeChange = (scheme) => {
594
+ preferredColorScheme = scheme;
595
+ for (const session of sessions.values()) {
596
+ session.appliedColorScheme = null;
597
+ void applyPreferredColorSchemeToGuest(
598
+ session,
599
+ logger,
600
+ syncPreferredColorScheme,
601
+ scheme
602
+ ).catch(() => void 0);
603
+ }
604
+ };
605
+ const unsubscribePreferredColorScheme = subscribePreferredColorScheme?.(handlePreferredColorSchemeChange) ?? null;
124
606
  const attachGuestListeners = (session) => {
125
607
  const contents = session.contents;
126
608
  if (!contents) {
@@ -174,18 +656,13 @@ function createBrowserGuestManager({
174
656
  publishState(session);
175
657
  return;
176
658
  }
177
- const routes = await previewRouteResolver.resolveRoutes({
178
- nodeId: session.nodeId,
179
- url: resolved.url
180
- });
181
- const routedUrl = routes.find((route) => route.sourceUrl === resolved.url)?.targetUrl ?? resolved.url;
182
659
  const currentComparable = normalizeBrowserComparableUrl(contents.getURL());
183
- const nextComparable = normalizeBrowserComparableUrl(routedUrl);
660
+ const nextComparable = normalizeBrowserComparableUrl(resolved.url);
184
661
  if (currentComparable && currentComparable === nextComparable) {
185
662
  publishState(session);
186
663
  return;
187
664
  }
188
- await contents.loadURL(routedUrl);
665
+ await contents.loadURL(resolved.url);
189
666
  publishState(session);
190
667
  };
191
668
  const openExternalFromGuest = (url) => {
@@ -281,14 +758,19 @@ function createBrowserGuestManager({
281
758
  session.lifecycle = "active";
282
759
  await loadDesiredUrl(session);
283
760
  },
284
- prepareSession(input) {
761
+ async prepareSession(input) {
762
+ await prepareSession?.(input);
285
763
  getSession(input.nodeId, {
286
764
  profileId: input.profileId,
287
765
  sessionMode: input.sessionMode
288
766
  });
289
- return Promise.resolve();
290
767
  },
291
768
  async registerGuest(input) {
769
+ await prepareSession?.({
770
+ nodeId: input.nodeId,
771
+ profileId: input.profileId,
772
+ sessionMode: input.sessionMode
773
+ });
292
774
  const contents = resolveWebContents(input.webContentsId);
293
775
  if (!contents || contents.isDestroyed()) {
294
776
  throw new Error(
@@ -318,6 +800,12 @@ function createBrowserGuestManager({
318
800
  session.lifecycle = "active";
319
801
  contents.setWindowOpenHandler?.(({ url }) => openExternalFromGuest(url));
320
802
  attachGuestListeners(session);
803
+ await applyPreferredColorSchemeToGuest(
804
+ session,
805
+ logger,
806
+ syncPreferredColorScheme,
807
+ preferredColorScheme
808
+ );
321
809
  await loadDesiredUrl(session);
322
810
  },
323
811
  reload(input) {
@@ -335,6 +823,9 @@ function createBrowserGuestManager({
335
823
  session.lifecycle = "cold";
336
824
  detachGuest(session);
337
825
  return Promise.resolve();
826
+ },
827
+ dispose() {
828
+ unsubscribePreferredColorScheme?.();
338
829
  }
339
830
  };
340
831
  }
@@ -342,6 +833,19 @@ function createBrowserGuestManager({
342
833
  // src/electron-main/registerElectronMain.ts
343
834
  function registerBrowserNodeElectronMain(input) {
344
835
  const managersByWindow = /* @__PURE__ */ new WeakMap();
836
+ const loopbackPreviewProxy = input.loopbackPreviewRouting !== void 0 ? createBrowserNodeLoopbackPreviewProxy({
837
+ logger: input.logger,
838
+ resolveSession: async ({ profileId, sessionMode }) => {
839
+ const { session } = await import("electron");
840
+ return session.fromPartition(
841
+ resolveBrowserSessionPartition({
842
+ profileId,
843
+ sessionMode
844
+ })
845
+ );
846
+ },
847
+ routing: input.loopbackPreviewRouting
848
+ }) : null;
345
849
  const resolveManager = (event) => {
346
850
  const ownerWindow = input.getOwnerWindow(event);
347
851
  if (!ownerWindow) {
@@ -357,13 +861,21 @@ function registerBrowserNodeElectronMain(input) {
357
861
  ownerWindow.webContents.send(input.channels.event, browserEvent);
358
862
  }
359
863
  },
864
+ getPreferredColorScheme: input.getPreferredColorScheme,
360
865
  logger: input.logger,
361
866
  openExternal: input.openExternal,
867
+ prepareSession: loopbackPreviewProxy !== null ? (payload) => loopbackPreviewProxy.configureSession(payload) : void 0,
362
868
  resolveWebContents: (webContentsId) => input.resolveWebContents({
363
869
  event,
364
870
  ownerWindow,
365
871
  webContentsId
366
- })
872
+ }),
873
+ syncPreferredColorScheme: input.syncPreferredColorScheme,
874
+ subscribePreferredColorScheme: input.subscribePreferredColorScheme
875
+ });
876
+ ownerWindow.once("closed", () => {
877
+ manager.dispose();
878
+ managersByWindow.delete(ownerWindow);
367
879
  });
368
880
  managersByWindow.set(ownerWindow, manager);
369
881
  return manager;
@@ -499,8 +1011,6 @@ function installBrowserWebviewSecurity({
499
1011
  };
500
1012
  }
501
1013
  export {
502
- createBrowserGuestManager,
503
- createNoopBrowserNodePreviewRouteResolver,
504
1014
  enforceBrowserWebviewSecurity,
505
1015
  installBrowserWebviewSecurity,
506
1016
  isBrowserNodeWebviewAttach,