@lvce-editor/pty-host 8.8.0 → 8.9.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.
Files changed (2) hide show
  1. package/dist/ptyHostMain.js +217 -109
  2. package/package.json +1 -1
@@ -1378,88 +1378,6 @@ const NodeWebSocketRpcClient = {
1378
1378
  create: create$2
1379
1379
  };
1380
1380
 
1381
- const METHODS_THAT_REQUIRE_SOCKET = new Set(['Terminal.create']);
1382
- const requiresSocket = method => {
1383
- return METHODS_THAT_REQUIRE_SOCKET.has(method);
1384
- };
1385
-
1386
- const handleElectronMessagePort = async messagePort => {
1387
- object(messagePort);
1388
- await create$5({
1389
- commandMap: {},
1390
- messagePort,
1391
- requiresSocket: requiresSocket
1392
- });
1393
- };
1394
-
1395
- const NodeWorker = 1;
1396
- const NodeForkedProcess = 2;
1397
- const WebSocket = 3;
1398
- const ElectronUtilityProcess = 4;
1399
- const ElectronMessagePort = 5;
1400
- const NodeMessagePort = 6;
1401
- const Auto = () => {
1402
- const {
1403
- argv
1404
- } = process;
1405
- if (argv.includes('--ipc-type=node-worker')) {
1406
- return NodeWorker;
1407
- }
1408
- if (argv.includes('--ipc-type=node-forked-process')) {
1409
- return NodeForkedProcess;
1410
- }
1411
- if (argv.includes('--ipc-type=websocket')) {
1412
- return WebSocket;
1413
- }
1414
- if (argv.includes('--ipc-type=electron-utility-process')) {
1415
- return ElectronUtilityProcess;
1416
- }
1417
- throw new Error(`[pty-host] unknown ipc type`);
1418
- };
1419
-
1420
- const getModule = method => {
1421
- switch (method) {
1422
- case ElectronMessagePort:
1423
- return ElectronMessagePortRpcClient;
1424
- case ElectronUtilityProcess:
1425
- return ElectronUtilityProcessRpcClient;
1426
- case NodeForkedProcess:
1427
- return NodeForkedProcessRpcClient;
1428
- case WebSocket:
1429
- return NodeWebSocketRpcClient;
1430
- default:
1431
- throw new Error('unexpected ipc type');
1432
- }
1433
- };
1434
-
1435
- const listen$1 = async ({
1436
- method,
1437
- ...params
1438
- }) => {
1439
- const module = getModule(method);
1440
- // @ts-ignore
1441
- const rpc = await module.create(params);
1442
- return rpc;
1443
- };
1444
-
1445
- const handleNodeMessagePort = async messagePort => {
1446
- await listen$1({
1447
- messagePort,
1448
- method: NodeMessagePort
1449
- });
1450
- };
1451
-
1452
- const handleWebSocket = async (handle, request) => {
1453
- object(handle);
1454
- object(request);
1455
- await create$2({
1456
- commandMap: {},
1457
- handle,
1458
- request,
1459
- requiresSocket: requiresSocket
1460
- });
1461
- };
1462
-
1463
1381
  class DataEvent extends Event {
1464
1382
  constructor(data) {
1465
1383
  super('data');
@@ -1539,63 +1457,251 @@ const create$1 = async ({
1539
1457
  }
1540
1458
  };
1541
1459
 
1542
- const state$1 = {
1543
- ptyMap: Object.create(null)
1544
- };
1545
- const set = (id, pty) => {
1546
- state$1.ptyMap[id] = pty;
1460
+ const connections$1 = new WeakMap();
1461
+ const set = (ipc, id, pty) => {
1462
+ let terminals = connections$1.get(ipc);
1463
+ if (!terminals) {
1464
+ terminals = new Map();
1465
+ connections$1.set(ipc, terminals);
1466
+ }
1467
+ terminals.set(id, pty);
1547
1468
  };
1548
- const get = id => {
1549
- return state$1.ptyMap[id];
1469
+ const get = (ipc, id) => connections$1.get(ipc)?.get(id);
1470
+ const remove = (ipc, id) => {
1471
+ connections$1.get(ipc)?.delete(id);
1550
1472
  };
1551
- const remove = id => {
1552
- delete state$1.ptyMap[id];
1473
+
1474
+ const connections = new WeakMap();
1475
+ const cleanup = new WeakMap();
1476
+ const closedConnections = new WeakSet();
1477
+ const disposeConnection = ipc => {
1478
+ closedConnections.add(ipc);
1479
+ const entries = connections.get(ipc);
1480
+ connections.delete(ipc);
1481
+ if (!entries) return;
1482
+ for (const entry of entries) {
1483
+ entry.closed = true;
1484
+ if (entry.pty) {
1485
+ cleanup.get(entry.pty)?.();
1486
+ entry.pty.dispose();
1487
+ }
1488
+ }
1553
1489
  };
1554
1490
 
1555
1491
  // TODO maybe merge pty and pty controller
1556
- const create = async (ipc, id, cwd, command, args) => {
1492
+ const createWithDependencies = async (ipc, id, cwd, command, args, createPty) => {
1557
1493
  number(id);
1558
1494
  string(cwd);
1559
1495
  string(command);
1560
1496
  array(args);
1561
1497
  object(ipc);
1562
- // @ts-ignore
1563
- const pty = await create$1({
1564
- args,
1565
- command,
1566
- cwd
1567
- });
1498
+ if (closedConnections.has(ipc)) throw new Error('Terminal connection closed');
1499
+ let entries = connections.get(ipc);
1500
+ if (!entries) {
1501
+ entries = new Set();
1502
+ connections.set(ipc, entries);
1503
+ }
1504
+ const entry = {
1505
+ closed: false
1506
+ };
1507
+ entries.add(entry);
1508
+ let pty;
1509
+ try {
1510
+ pty = await createPty({
1511
+ args,
1512
+ command,
1513
+ cwd
1514
+ });
1515
+ } catch (error) {
1516
+ entries.delete(entry);
1517
+ throw error;
1518
+ }
1519
+ entry.pty = pty;
1520
+ if (entry.closed) {
1521
+ pty.dispose();
1522
+ throw new Error('Terminal connection closed');
1523
+ }
1568
1524
  const handleData = event => {
1525
+ if (entry.closed) return;
1569
1526
  ipc.send({
1570
1527
  jsonrpc: '2.0',
1571
1528
  method: 'Viewlet.send',
1572
1529
  params: [id, 'handleData', event.data]
1573
1530
  });
1574
1531
  };
1532
+ const handleExit = event => {
1533
+ entries.delete(entry);
1534
+ if (get(ipc, id) === pty) remove(ipc, id);
1535
+ if (entry.closed) return;
1536
+ ipc.send({
1537
+ jsonrpc: '2.0',
1538
+ method: 'Viewlet.send',
1539
+ params: [id, 'handleExit', event.data]
1540
+ });
1541
+ };
1542
+ cleanup.set(pty, () => {
1543
+ entry.closed = true;
1544
+ entries.delete(entry);
1545
+ pty.removeEventListener('data', handleData);
1546
+ pty.removeEventListener('exit', handleExit);
1547
+ if (get(ipc, id) === pty) remove(ipc, id);
1548
+ cleanup.delete(pty);
1549
+ });
1575
1550
  pty.addEventListener('data', handleData);
1576
- set(id, pty);
1551
+ pty.addEventListener('exit', handleExit, {
1552
+ once: true
1553
+ });
1554
+ set(ipc, id, pty);
1555
+ };
1556
+ const create = (ipc, id, cwd, command, args) => {
1557
+ return createWithDependencies(ipc, id, cwd, command, args, create$1);
1577
1558
  };
1578
- const write = (id, data) => {
1579
- const pty = get(id);
1559
+ const write = (ipc, id, data) => {
1560
+ const pty = get(ipc, id);
1580
1561
  if (!pty) {
1581
1562
  throw new Error(`pty ${id} not found`);
1582
1563
  }
1583
1564
  pty.write(data);
1584
1565
  };
1585
- const resize = (id, columns, rows) => {
1586
- const pty = get(id);
1566
+ const resize = (ipc, id, columns, rows) => {
1567
+ const pty = get(ipc, id);
1587
1568
  if (!pty) {
1588
1569
  throw new Error(`pty ${id} not found`);
1589
1570
  }
1590
1571
  pty.resize(columns, rows);
1591
1572
  };
1592
- const dispose = id => {
1593
- const pty = get(id);
1573
+ const dispose = (ipc, id) => {
1574
+ const pty = get(ipc, id);
1594
1575
  if (!pty) {
1595
- throw new Error(`pty ${id} not found`);
1576
+ return;
1596
1577
  }
1578
+ cleanup.get(pty)?.();
1597
1579
  pty.dispose();
1598
- remove(id);
1580
+ if (get(ipc, id) === pty) remove(ipc, id);
1581
+ };
1582
+
1583
+ const ready = Promise.withResolvers();
1584
+ const state$1 = {};
1585
+ const setParent = rpc => {
1586
+ state$1.parent = rpc;
1587
+ ready.resolve();
1588
+ };
1589
+ const release = async id => {
1590
+ if (!state$1.parent) await ready.promise;
1591
+ state$1.parent.send('PtyHost.release', id);
1592
+ };
1593
+ const connect = async (handle, id, create) => {
1594
+ let closed = false;
1595
+ let rpc;
1596
+ const onClose = () => {
1597
+ if (closed) return;
1598
+ closed = true;
1599
+ handle.off('close', onClose);
1600
+ try {
1601
+ if (rpc) disposeConnection(rpc.ipc);
1602
+ } finally {
1603
+ if (id !== undefined) void release(id);
1604
+ }
1605
+ };
1606
+ handle.on('close', onClose);
1607
+ try {
1608
+ if (handle.destroyed) throw new Error('Terminal connection closed');
1609
+ rpc = await create();
1610
+ if (closed) {
1611
+ disposeConnection(rpc.ipc);
1612
+ await rpc.dispose();
1613
+ }
1614
+ } catch (error) {
1615
+ onClose();
1616
+ handle.destroy?.();
1617
+ handle.close?.();
1618
+ throw error;
1619
+ }
1620
+ };
1621
+
1622
+ const METHODS_THAT_REQUIRE_SOCKET = new Set(['Terminal.create', 'Terminal.write', 'Terminal.resize', 'Terminal.dispose']);
1623
+ const requiresSocket = method => {
1624
+ return METHODS_THAT_REQUIRE_SOCKET.has(method);
1625
+ };
1626
+
1627
+ const handleElectronMessagePort = async (messagePort, connectionId) => {
1628
+ object(messagePort);
1629
+ await connect(messagePort, connectionId, () => create$5({
1630
+ commandMap: {},
1631
+ messagePort,
1632
+ requiresSocket: requiresSocket
1633
+ }));
1634
+ };
1635
+
1636
+ const NodeWorker = 1;
1637
+ const NodeForkedProcess = 2;
1638
+ const WebSocket = 3;
1639
+ const ElectronUtilityProcess = 4;
1640
+ const ElectronMessagePort = 5;
1641
+ const NodeMessagePort = 6;
1642
+ const Auto = () => {
1643
+ const {
1644
+ argv
1645
+ } = process;
1646
+ if (argv.includes('--ipc-type=node-worker')) {
1647
+ return NodeWorker;
1648
+ }
1649
+ if (argv.includes('--ipc-type=node-forked-process')) {
1650
+ return NodeForkedProcess;
1651
+ }
1652
+ if (argv.includes('--ipc-type=websocket')) {
1653
+ return WebSocket;
1654
+ }
1655
+ if (argv.includes('--ipc-type=electron-utility-process')) {
1656
+ return ElectronUtilityProcess;
1657
+ }
1658
+ throw new Error(`[pty-host] unknown ipc type`);
1659
+ };
1660
+
1661
+ const getModule = method => {
1662
+ switch (method) {
1663
+ case ElectronMessagePort:
1664
+ return ElectronMessagePortRpcClient;
1665
+ case ElectronUtilityProcess:
1666
+ return ElectronUtilityProcessRpcClient;
1667
+ case NodeForkedProcess:
1668
+ return NodeForkedProcessRpcClient;
1669
+ case WebSocket:
1670
+ return NodeWebSocketRpcClient;
1671
+ default:
1672
+ throw new Error('unexpected ipc type');
1673
+ }
1674
+ };
1675
+
1676
+ const listen$1 = async ({
1677
+ method,
1678
+ ...params
1679
+ }) => {
1680
+ const module = getModule(method);
1681
+ // @ts-ignore
1682
+ const rpc = await module.create(params);
1683
+ if (params.requiresSocket) {
1684
+ rpc.ipc.requiresSocket = params.requiresSocket;
1685
+ }
1686
+ return rpc;
1687
+ };
1688
+
1689
+ const handleNodeMessagePort = async messagePort => {
1690
+ await listen$1({
1691
+ messagePort,
1692
+ method: NodeMessagePort
1693
+ });
1694
+ };
1695
+
1696
+ const handleWebSocket = async (handle, request, connectionId) => {
1697
+ object(handle);
1698
+ object(request);
1699
+ await connect(handle, connectionId, () => create$2({
1700
+ commandMap: {},
1701
+ handle,
1702
+ request,
1703
+ requiresSocket: requiresSocket
1704
+ }));
1599
1705
  };
1600
1706
 
1601
1707
  const HandleElectronMessagePort = 'HandleElectronMessagePort.handleElectronMessagePort';
@@ -1619,9 +1725,11 @@ const commandMap = {
1619
1725
  };
1620
1726
 
1621
1727
  const listen = async () => {
1622
- await listen$1({
1623
- method: Auto()
1728
+ const rpc = await listen$1({
1729
+ method: Auto(),
1730
+ requiresSocket: requiresSocket
1624
1731
  });
1732
+ setParent(rpc);
1625
1733
  };
1626
1734
 
1627
1735
  const NewLine$1 = '\n';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lvce-editor/pty-host",
3
- "version": "8.8.0",
3
+ "version": "8.9.0",
4
4
  "keywords": [
5
5
  "lvce-editor",
6
6
  "pty-host"