@lvce-editor/shared-process 0.22.1 → 0.22.2

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 (20) hide show
  1. package/package.json +6 -8
  2. package/src/parts/ExtensionHostHelperProcessIpc/ExtensionHostHelperProcessIpc.js +3 -0
  3. package/src/parts/ExtensionManagement/ExtensionManagement.js +48 -36
  4. package/src/parts/ExtensionManifests/ExtensionManifests.js +6 -3
  5. package/src/parts/GetUtilityProcessPortData/GetUtilityProcessPortData.js +10 -0
  6. package/src/parts/HandleElectronMessagePort/HandleElectronMessagePort.js +0 -1
  7. package/src/parts/HandleIpc/HandleIpc.js +3 -0
  8. package/src/parts/HandleMessagePortForExtensionHostHelperProcess/HandleMessagePortForExtensionHostHelperProcess.js +20 -4
  9. package/src/parts/HandleNodeMessagePort/HandleNodeMessagePort.js +0 -1
  10. package/src/parts/HandleWebSocket/HandleWebSocket.js +0 -1
  11. package/src/parts/HandleWebSocketForSharedProcess/HandleWebSocketForSharedProcess.js +6 -4
  12. package/src/parts/IpcChildWithElectronMessagePort/IpcChildWithElectronMessagePort.js +3 -0
  13. package/src/parts/IpcChildWithElectronUtilityProcess/IpcChildWithElectronUtilityProcess.js +3 -12
  14. package/src/parts/IpcChildWithNodeMessagePort/IpcChildWithNodeMessagePort.js +3 -0
  15. package/src/parts/IpcChildWithWebSocket/IpcChildWithWebSocket.js +17 -11
  16. package/src/parts/IpcParentWithElectronUtilityProcess/IpcParentWithElectronUtilityProcess.js +2 -0
  17. package/src/parts/Platform/Platform.js +3 -3
  18. package/src/parts/RequiresSocket/RequiresSocket.js +1 -0
  19. package/src/parts/WebSocketSerialization/WebSocketSerialization.js +7 -0
  20. package/src/parts/WebSocketServer/WebSocketServer.js +1 -30
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lvce-editor/shared-process",
3
- "version": "0.22.1",
3
+ "version": "0.22.2",
4
4
  "main": "index.js",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -17,23 +17,21 @@
17
17
  "node": ">=18"
18
18
  },
19
19
  "dependencies": {
20
- "@lvce-editor/assert": "^1.0.1",
21
- "@lvce-editor/extension-host": "0.22.1",
22
- "@lvce-editor/extension-host-helper-process": "0.22.1",
20
+ "@lvce-editor/assert": "^1.1.0",
21
+ "@lvce-editor/extension-host-helper-process": "0.22.2",
23
22
  "@lvce-editor/json-rpc": "^1.0.0",
24
23
  "@lvce-editor/jsonc-parser": "^1.1.0",
25
- "@lvce-editor/pretty-error": "^1.3.0",
26
- "@lvce-editor/pty-host": "0.22.1",
24
+ "@lvce-editor/pretty-error": "^1.4.0",
25
+ "@lvce-editor/pty-host": "0.22.2",
27
26
  "@lvce-editor/verror": "^1.1.2",
27
+ "@lvce-editor/web-socket-server": "^1.1.0",
28
28
  "debug": "^4.3.4",
29
29
  "execa": "^8.0.1",
30
30
  "exit-hook": "^4.0.0",
31
31
  "got": "^13.0.0",
32
32
  "is-object": "^1.0.2",
33
33
  "p-queue": "^8.0.1",
34
- "parse-json": "^8.1.0",
35
34
  "tar-fs": "^3.0.4",
36
- "ws": "^8.16.0",
37
35
  "xdg-basedir": "^5.1.0"
38
36
  },
39
37
  "optionalDependencies": {
@@ -1,12 +1,15 @@
1
1
  import * as IpcParent from '../IpcParent/IpcParent.js'
2
2
  import * as PlatformPaths from '../PlatformPaths/PlatformPaths.js'
3
+ import * as Id from '../Id/Id.js'
3
4
 
4
5
  export const create = async ({ method }) => {
5
6
  const extensionHostHelperProcessPath = await PlatformPaths.getExtensionHostHelperProcessPath()
7
+ const id = Id.create()
6
8
  const ipc = await IpcParent.create({
7
9
  method,
8
10
  path: extensionHostHelperProcessPath,
9
11
  execArgv: ['--max-old-space-size=60', '--enable-source-maps'],
12
+ name: `Extension Host Helper Process ${id}`,
10
13
  })
11
14
  return ipc
12
15
  }
@@ -31,51 +31,63 @@ export const disable = async (id) => {
31
31
  }
32
32
 
33
33
  export const getBuiltinExtensions = () => {
34
- return ExtensionManifests.getAll([
35
- {
36
- type: ExtensionManifestInputType.Folder,
37
- path: PlatformPaths.getBuiltinExtensionsPath(),
38
- },
39
- ])
34
+ return ExtensionManifests.getAll(
35
+ [
36
+ {
37
+ type: ExtensionManifestInputType.Folder,
38
+ path: PlatformPaths.getBuiltinExtensionsPath(),
39
+ },
40
+ ],
41
+ PlatformPaths.getBuiltinExtensionsPath(),
42
+ )
40
43
  }
41
44
 
42
45
  export const getInstalledExtensions = () => {
43
- return ExtensionManifests.getAll([
44
- {
45
- type: ExtensionManifestInputType.Folder,
46
- path: PlatformPaths.getExtensionsPath(),
47
- },
48
- ])
46
+ return ExtensionManifests.getAll(
47
+ [
48
+ {
49
+ type: ExtensionManifestInputType.Folder,
50
+ path: PlatformPaths.getExtensionsPath(),
51
+ },
52
+ ],
53
+ PlatformPaths.getBuiltinExtensionsPath(),
54
+ )
49
55
  }
50
56
 
51
57
  export const getExtensions = () => {
52
- return ExtensionManifests.getAll([
53
- {
54
- type: ExtensionManifestInputType.OnlyExtension,
55
- path: PlatformPaths.getOnlyExtensionPath(),
56
- },
57
- {
58
- type: ExtensionManifestInputType.Folder,
59
- path: PlatformPaths.getLinkedExtensionsPath(),
60
- },
61
- {
62
- type: ExtensionManifestInputType.Folder,
63
- path: PlatformPaths.getExtensionsPath(),
64
- },
65
- {
66
- type: ExtensionManifestInputType.Folder,
67
- path: PlatformPaths.getBuiltinExtensionsPath(),
68
- },
69
- ])
58
+ return ExtensionManifests.getAll(
59
+ [
60
+ {
61
+ type: ExtensionManifestInputType.OnlyExtension,
62
+ path: PlatformPaths.getOnlyExtensionPath(),
63
+ },
64
+ {
65
+ type: ExtensionManifestInputType.Folder,
66
+ path: PlatformPaths.getLinkedExtensionsPath(),
67
+ },
68
+ {
69
+ type: ExtensionManifestInputType.Folder,
70
+ path: PlatformPaths.getExtensionsPath(),
71
+ },
72
+ {
73
+ type: ExtensionManifestInputType.Folder,
74
+ path: PlatformPaths.getBuiltinExtensionsPath(),
75
+ },
76
+ ],
77
+ PlatformPaths.getBuiltinExtensionsPath(),
78
+ )
70
79
  }
71
80
 
72
81
  export const getDisabledExtensions = () => {
73
- return ExtensionManifests.getAll([
74
- {
75
- type: ExtensionManifestInputType.Folder,
76
- path: PlatformPaths.getDisabledExtensionsPath(),
77
- },
78
- ])
82
+ return ExtensionManifests.getAll(
83
+ [
84
+ {
85
+ type: ExtensionManifestInputType.Folder,
86
+ path: PlatformPaths.getDisabledExtensionsPath(),
87
+ },
88
+ ],
89
+ PlatformPaths.getBuiltinExtensionsPath(),
90
+ )
79
91
  }
80
92
 
81
93
  export * from '../ExtensionUninstall/ExtensionUninstall.js'
@@ -21,7 +21,7 @@ const get = (input) => {
21
21
  return module.getExtensionManifests(input.path)
22
22
  }
23
23
 
24
- const deduplicateExtensions = (extensions) => {
24
+ const deduplicateExtensions = (extensions, builtinExtensionsPath) => {
25
25
  const seen = Object.create(null)
26
26
  const uniqueExtensions = []
27
27
  for (const extension of extensions) {
@@ -29,14 +29,17 @@ const deduplicateExtensions = (extensions) => {
29
29
  continue
30
30
  }
31
31
  seen[extension.id] = true
32
+ if (extension.path.startsWith(builtinExtensionsPath)) {
33
+ extension.builtin = true
34
+ }
32
35
  uniqueExtensions.push(extension)
33
36
  }
34
37
  return uniqueExtensions
35
38
  }
36
39
 
37
- export const getAll = async (inputs) => {
40
+ export const getAll = async (inputs, builtinExtensionsPath) => {
38
41
  const manifests = await Promise.all(inputs.map(get))
39
42
  const flatManifests = manifests.flat(1)
40
- const uniqueExtensions = deduplicateExtensions(flatManifests)
43
+ const uniqueExtensions = deduplicateExtensions(flatManifests, builtinExtensionsPath)
41
44
  return uniqueExtensions
42
45
  }
@@ -0,0 +1,10 @@
1
+ export const getUtilityProcessPortData = (event) => {
2
+ const { data, ports } = event
3
+ if (ports.length === 0) {
4
+ return data
5
+ }
6
+ return {
7
+ ...data,
8
+ params: [...ports, ...data.params],
9
+ }
10
+ }
@@ -10,7 +10,6 @@ export const handleElectronMessagePort = async (messagePort, ...params) => {
10
10
  messagePort,
11
11
  })
12
12
  HandleIpc.handleIpc(ipc)
13
- messagePort.start()
14
13
  // TODO find better way to associate configuration with ipc
15
14
  ipc.windowId = params[1]
16
15
  }
@@ -19,4 +19,7 @@ export const handleIpc = (ipc) => {
19
19
  return JsonRpc.handleJsonRpcMessage(ipc, message, Command.execute, Callback.resolve, preparePrettyError, logError, RequiresSocket.requiresSocket)
20
20
  }
21
21
  ipc.on('message', handleMessage)
22
+ if (ipc.start) {
23
+ ipc.start()
24
+ }
22
25
  }
@@ -1,8 +1,24 @@
1
1
  import * as Assert from '../Assert/Assert.js'
2
+ import * as ExtensionHostHelperProcessIpc from '../ExtensionHostHelperProcessIpc/ExtensionHostHelperProcessIpc.js'
3
+ import * as HandleIpc from '../HandleIpc/HandleIpc.js'
4
+ import * as IpcParentType from '../IpcParentType/IpcParentType.js'
5
+ import * as JsonRpc from '../JsonRpc/JsonRpc.js'
2
6
 
3
- export const handleMessagePortForExtensionHostHelperProcess = async (port) => {
7
+ // TODO connect this ipc to the renderer worker ipc
8
+ // when renderer worker ipc closes, dispose all
9
+ // matching extension host helper processes
10
+ // unless the app is exiting, in which case the child processes
11
+ // would get cleaned up automatically
12
+ export const handleMessagePortForExtensionHostHelperProcess = async (rendererWorkerIpc, port) => {
4
13
  Assert.object(port)
5
- // TODO
6
- // 1. create extension host helper utility process
7
- // 2. send message port to utility process
14
+ const ipc = await ExtensionHostHelperProcessIpc.create({
15
+ method: IpcParentType.ElectronUtilityProcess,
16
+ })
17
+ HandleIpc.handleIpc(ipc)
18
+ await JsonRpc.invokeAndTransfer(ipc, [port], 'HandleElectronMessagePort.handleElectronMessagePort')
19
+ const cleanup = () => {
20
+ ipc.dispose()
21
+ rendererWorkerIpc.off('close', cleanup)
22
+ }
23
+ rendererWorkerIpc.on('close', cleanup)
8
24
  }
@@ -8,5 +8,4 @@ export const handleNodeMessagePort = async (messagePort) => {
8
8
  messagePort,
9
9
  })
10
10
  HandleIpc.handleIpc(ipc)
11
- messagePort.start()
12
11
  }
@@ -18,7 +18,6 @@ export const handleWebSocket = async (message, handle) => {
18
18
  handle.pause()
19
19
  const module = await HandleWebSocketModule.load(protocol)
20
20
  await module.handleWebSocket(message, handle, protocol)
21
- handle.resume()
22
21
  } catch (error) {
23
22
  DestroyWebSocket.destroySocket(handle)
24
23
  throw new VError(error, `Failed to connect to websocket`)
@@ -1,15 +1,17 @@
1
+ import * as Assert from '../Assert/Assert.js'
1
2
  import * as HandleIpc from '../HandleIpc/HandleIpc.js'
2
3
  import * as HandleSocketError from '../HandleSocketError/HandleSocketError.js'
3
4
  import * as IpcChild from '../IpcChild/IpcChild.js'
4
5
  import * as IpcChildType from '../IpcChildType/IpcChildType.js'
5
- import * as WebSocketServer from '../WebSocketServer/WebSocketServer.js'
6
6
 
7
- export const handleWebSocket = async (message, handle) => {
7
+ export const handleWebSocket = async (request, handle) => {
8
+ Assert.object(request)
9
+ Assert.object(handle)
8
10
  handle.on('error', HandleSocketError.handleSocketError)
9
- const webSocket = await WebSocketServer.handleUpgrade(message, handle)
10
11
  const ipc = await IpcChild.listen({
11
12
  method: IpcChildType.WebSocket,
12
- webSocket,
13
+ request,
14
+ handle,
13
15
  })
14
16
  HandleIpc.handleIpc(ipc)
15
17
  }
@@ -44,5 +44,8 @@ export const wrap = (messagePort) => {
44
44
  dispose() {
45
45
  this.messagePort.close()
46
46
  },
47
+ start() {
48
+ this.messagePort.start()
49
+ },
47
50
  }
48
51
  }
@@ -1,3 +1,5 @@
1
+ import * as GetUtilityProcessPortData from '../GetUtilityProcessPortData/GetUtilityProcessPortData.js'
2
+
1
3
  export const listen = () => {
2
4
  // @ts-ignore
3
5
  const { parentPort } = process
@@ -11,24 +13,13 @@ export const signal = (parentPort) => {
11
13
  parentPort.postMessage('ready')
12
14
  }
13
15
 
14
- const getActualData = (event) => {
15
- const { data, ports } = event
16
- if (ports.length === 0) {
17
- return data
18
- }
19
- return {
20
- ...data,
21
- params: [...ports, ...data.params],
22
- }
23
- }
24
-
25
16
  export const wrap = (parentPort) => {
26
17
  return {
27
18
  parentPort,
28
19
  on(event, listener) {
29
20
  if (event === 'message') {
30
21
  const wrappedListener = (event) => {
31
- const actualData = getActualData(event)
22
+ const actualData = GetUtilityProcessPortData.getUtilityProcessPortData(event)
32
23
  listener(actualData)
33
24
  }
34
25
  this.parentPort.on(event, wrappedListener)
@@ -37,5 +37,8 @@ export const wrap = (messagePort) => {
37
37
  dispose() {
38
38
  this.messagePort.close()
39
39
  },
40
+ start() {
41
+ this.messagePort.start()
42
+ },
40
43
  }
41
44
  }
@@ -1,7 +1,18 @@
1
1
  import * as GetFirstWebSocketEvent from '../GetFirstWebSocketEvent/GetFirstWebSocketEvent.js'
2
+ import { IpcError } from '../IpcError/IpcError.js'
2
3
  import * as IsWebSocketOpen from '../IsWebSocketOpen/IsWebSocketOpen.js'
4
+ import * as WebSocketSerialization from '../WebSocketSerialization/WebSocketSerialization.js'
5
+ import * as WebSocketServer from '../WebSocketServer/WebSocketServer.js'
3
6
 
4
- export const listen = async ({ webSocket }) => {
7
+ export const listen = async ({ request, handle }) => {
8
+ if (!request) {
9
+ throw new IpcError('request must be defined')
10
+ }
11
+ if (!handle) {
12
+ throw new IpcError('handle must be defined')
13
+ }
14
+ const webSocket = await WebSocketServer.handleUpgrade(request, handle)
15
+ webSocket.pause()
5
16
  if (!IsWebSocketOpen.isWebSocketOpen(webSocket)) {
6
17
  const { type, event } = await GetFirstWebSocketEvent.getFirstWebSocketEvent(webSocket)
7
18
  console.log({ type, event })
@@ -9,11 +20,6 @@ export const listen = async ({ webSocket }) => {
9
20
  return webSocket
10
21
  }
11
22
 
12
- const getActualData = (message) => {
13
- const data = JSON.parse(message.toString())
14
- return data
15
- }
16
-
17
23
  export const wrap = (webSocket) => {
18
24
  return {
19
25
  webSocket,
@@ -25,14 +31,11 @@ export const wrap = (webSocket) => {
25
31
  switch (event) {
26
32
  case 'message':
27
33
  const wrappedListener = (message) => {
28
- const data = getActualData(message)
34
+ const data = WebSocketSerialization.deserialize(message)
29
35
  listener(data)
30
36
  }
31
37
  webSocket.on('message', wrappedListener)
32
38
  break
33
- case 'close':
34
- webSocket.on('close', listener)
35
- break
36
39
  default:
37
40
  throw new Error('unknown event listener type')
38
41
  }
@@ -41,11 +44,14 @@ export const wrap = (webSocket) => {
41
44
  this.webSocket.off(event, listener)
42
45
  },
43
46
  send(message) {
44
- const stringifiedMessage = JSON.stringify(message)
47
+ const stringifiedMessage = WebSocketSerialization.serialize(message)
45
48
  this.webSocket.send(stringifiedMessage)
46
49
  },
47
50
  dispose() {
48
51
  this.webSocket.close()
49
52
  },
53
+ start() {
54
+ this.webSocket.resume()
55
+ },
50
56
  }
51
57
  }
@@ -13,6 +13,7 @@ export const create = async (options) => {
13
13
  })
14
14
  // TODO use uuid instead of name
15
15
  const port = await TemporaryMessagePort.create(options.name)
16
+ port.name = options.name
16
17
  return port
17
18
  }
18
19
 
@@ -43,6 +44,7 @@ export const wrap = (port) => {
43
44
  off(event, listener) {},
44
45
  dispose() {
45
46
  this.port.close()
47
+ ParentIpc.invoke('TemporaryMessagePort.dispose', this.port.name)
46
48
  },
47
49
  }
48
50
  }
@@ -61,11 +61,11 @@ export const getSetupName = () => {
61
61
  return 'Lvce-Setup'
62
62
  }
63
63
 
64
- export const version = '0.22.1'
64
+ export const version = '0.22.2'
65
65
 
66
- export const commit = 'a466fc1'
66
+ export const commit = 'c239d9c'
67
67
 
68
- export const date = '2024-01-04T17:28:33.000Z'
68
+ export const date = '2024-01-06T23:18:20.000Z'
69
69
 
70
70
  export const getVersion = () => {
71
71
  return version
@@ -10,6 +10,7 @@ const METHODS_THAT_REQUIRE_SOCKET = new Set([
10
10
  'IncrementalTextSearch.start',
11
11
  'ElectronApplicationMenu.setItems',
12
12
  'GetWindowId.getWindowId',
13
+ 'HandleMessagePortForExtensionHostHelperProcess.handleMessagePortForExtensionHostHelperProcess',
13
14
  ])
14
15
 
15
16
  export const requiresSocket = (method) => {
@@ -0,0 +1,7 @@
1
+ export const serialize = (message) => {
2
+ return JSON.stringify(message)
3
+ }
4
+
5
+ export const deserialize = (message) => {
6
+ return JSON.parse(message.toString())
7
+ }
@@ -1,30 +1 @@
1
- import { Buffer } from 'node:buffer'
2
- import * as _ws from 'ws'
3
- import * as IsSocket from '../IsSocket/IsSocket.js'
4
- import * as Promises from '../Promises/Promises.js'
5
-
6
- // workaround for jest or node bug
7
- const WebSocketServer = _ws.WebSocketServer
8
- ? _ws.WebSocketServer
9
- : // @ts-ignore
10
- _ws.default.WebSocketServer
11
-
12
- const webSocketServer = new WebSocketServer({
13
- noServer: true,
14
-
15
- // TODO not sure if ws compress is working at all
16
- // perMessageDeflate: true
17
- })
18
-
19
- export const handleUpgrade = async (request, socket) => {
20
- if (!IsSocket.isSocket(socket)) {
21
- throw new TypeError(`socket must be of type Socket`)
22
- }
23
- const { resolve, promise } = Promises.withResolvers()
24
- const upgradeCallback = (ws) => {
25
- resolve(ws)
26
- }
27
- webSocketServer.handleUpgrade(request, socket, Buffer.alloc(0), upgradeCallback)
28
- const webSocket = await promise
29
- return webSocket
30
- }
1
+ export * from '@lvce-editor/web-socket-server'