@lvce-editor/shared-process 0.16.17 → 0.16.19

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 (50) hide show
  1. package/extensions/builtin.language-basics-javascript/src/tokenizeJavaScript.js +5 -0
  2. package/extensions/builtin.language-basics-less/src/tokenizeLess.js +17 -1
  3. package/package.json +4 -4
  4. package/src/parts/ElectronContextMenu/ElectronContextMenu.ipc.js +7 -0
  5. package/src/parts/ElectronContextMenu/ElectronContextMenu.js +5 -0
  6. package/src/parts/ElectronProcess/ElectronProcess.ipc.js +8 -0
  7. package/src/parts/ElectronProcess/ElectronProcess.js +9 -0
  8. package/src/parts/ElectronSafeStorage/ElectronSafeStorage.ipc.js +9 -0
  9. package/src/parts/ElectronSafeStorage/ElectronSafeStorage.js +13 -0
  10. package/src/parts/ExtensionManifest/ExtensionManifest.js +2 -1
  11. package/src/parts/ExtensionManifests/ExtensionManifestsFromFolder.js +2 -4
  12. package/src/parts/ExtensionManifests/ExtensionManifestsFromLinkedExtensionsFolder.js +2 -4
  13. package/src/parts/FileSystem/FileSystem.js +9 -4
  14. package/src/parts/GetAccurateMemoryUsage/GetAccurateMemoryUsage.js +30 -23
  15. package/src/parts/GetElectronRebuildPath/GetElectronRebuildPath.js +4 -0
  16. package/src/parts/GetFirstEvent/GetFirstEvent.js +22 -0
  17. package/src/parts/GetFirstNodeChildProcessEvent/GetFirstNodeChildProcessEvent.js +8 -4
  18. package/src/parts/GetFirstNodeWorkerEvent/GetFirstNodeWorkerEvent.js +5 -16
  19. package/src/parts/GetFirstWebSocketEvent/GetFirstWebSocketEvent.js +3 -3
  20. package/src/parts/GetHttpErrorMessage/GetHttpErrorMessage.js +28 -0
  21. package/src/parts/GetLatestReleaseVersion/GetLatestReleaseVersion.js +2 -28
  22. package/src/parts/GetTerminalProcessPath/GetTerminalProcessPath.js +6 -0
  23. package/src/parts/IpcParentType/IpcParentType.js +1 -0
  24. package/src/parts/IpcParentWithElectronUtilityProcess/IpcParentWithElectronUtilityProcess.js +12 -0
  25. package/src/parts/IsDlOpenError/IsDlOpenError.js +5 -0
  26. package/src/parts/IsEsrchError/IsEsrchError.js +5 -0
  27. package/src/parts/IsWindows/IsWindows.js +1 -0
  28. package/src/parts/ListProcessGetName/ListProcessGetName.js +3 -0
  29. package/src/parts/LoadWindowsProcessTree/LoadWindowsProcessTree.js +2 -2
  30. package/src/parts/Module/Module.js +10 -2
  31. package/src/parts/ModuleId/ModuleId.js +5 -0
  32. package/src/parts/ModuleMap/ModuleMap.js +18 -0
  33. package/src/parts/PassThroughElectronMessageForTerminalProcess/PassThroughElectronMessageForTerminalProcess.js +29 -0
  34. package/src/parts/PassThroughElectronMessagePort/PassThroughElectronMessagePort.ipc.js +7 -0
  35. package/src/parts/PassThroughElectronMessagePort/PassThroughElectronMessagePort.js +4 -0
  36. package/src/parts/PassThroughElectronMessagePortModule/PassThroughElectronMessagePortModule.js +8 -0
  37. package/src/parts/Platform/Platform.ipc.js +1 -1
  38. package/src/parts/Platform/Platform.js +3 -3
  39. package/src/parts/Preferences/Preferences.js +2 -1
  40. package/src/parts/Process/Process.js +4 -0
  41. package/src/parts/PtyHost/PtyHost.js +17 -12
  42. package/src/parts/RebuildForElectron/RebuildForElectron.js +19 -0
  43. package/src/parts/RebuildForNode/RebuildForNode.js +17 -0
  44. package/src/parts/RebuildNodePty/RebuildNodePty.js +5 -29
  45. package/src/parts/RecentlyOpened/RecentlyOpened.js +3 -2
  46. package/src/parts/Terminal/Terminal.js +5 -3
  47. package/src/parts/WaitForProcessToExit/WaitForProcessToExit.js +4 -24
  48. package/src/parts/WebSocketReadyState/WebSocketReadyState.js +5 -0
  49. package/src/parts/Window/ElectronWindow.ipc.js +12 -0
  50. package/src/parts/Window/ElectronWindow.js +32 -0
@@ -143,6 +143,8 @@ const RE_REGEX =
143
143
 
144
144
  const RE_VARIABLE_NAME_SPECIAL = /\p{L}/u
145
145
  const RE_VARIABLE_NAME_SPECIAL_2 = /./u
146
+ const RE_BUILTIN_CLASS =
147
+ /^(?:Array|Object|Promise|ArrayBuffer|URL|URLSearchParams|WebSocket|FileSystemHandle|Function|StorageEvent|MessageEvent|MessageChannel|Int32Array|Boolean|String|Error|Set|RegExp|Map|WeakMap|RangeError|Date|Headers|Response|Request|Buffer|MessagePort|FileHandle|X509Certificate|Blob|HTMLElement)\b/
146
148
 
147
149
  export const initialLineState = {
148
150
  state: State.TopLevelContent,
@@ -251,6 +253,9 @@ export const tokenizeLine = (line, lineState) => {
251
253
  } else if ((next = part.match(RE_FUNCTION_CALL_NAME))) {
252
254
  token = TokenType.FunctionName
253
255
  state = State.TopLevelContent
256
+ } else if ((next = part.match(RE_BUILTIN_CLASS))) {
257
+ token = TokenType.Class
258
+ state = State.TopLevelContent
254
259
  } else if ((next = part.match(RE_VARIABLE_NAME))) {
255
260
  token = TokenType.VariableName
256
261
  state = State.TopLevelContent
@@ -125,6 +125,8 @@ const RE_STRING_SINGLE_QUOTE_CONTENT = /^[^']+/
125
125
  const RE_SINGLE_QUOTE = /^'/
126
126
  const RE_ANYTHING_BUT_CURLY = /^[^\{\}]+/s
127
127
  const RE_LINE_COMMENT = /\/\/.*/
128
+ const RE_NESTED_SELECTOR = /^[\w\.\-\s]+(?=\s*(?:\{|,))/
129
+ const RE_AMPERSAND = /^\&/
128
130
 
129
131
  /**
130
132
  * @param {string} line
@@ -205,9 +207,13 @@ export const tokenizeLine = (line, lineState) => {
205
207
  if ((next = part.match(RE_WHITESPACE))) {
206
208
  token = TokenType.Whitespace
207
209
  state = State.AfterSelector
210
+ } else if ((next = part.match(RE_AMPERSAND))) {
211
+ token = TokenType.Punctuation
212
+ state = State.AfterSelector
208
213
  } else if ((next = part.match(RE_CURLY_OPEN))) {
209
214
  token = TokenType.CurlyOpen
210
215
  state = State.InsideSelector
216
+ stack.push(State.InsideSelector)
211
217
  } else if ((next = part.match(RE_SELECTOR_CLASS))) {
212
218
  token = TokenType.CssSelectorClass
213
219
  state = State.AfterSelector
@@ -297,6 +303,16 @@ export const tokenizeLine = (line, lineState) => {
297
303
  if ((next = part.match(RE_WHITESPACE))) {
298
304
  token = TokenType.Whitespace
299
305
  state = State.InsideSelector
306
+ } else if ((next = part.match(RE_AMPERSAND))) {
307
+ token = TokenType.Punctuation
308
+ state = State.InsideSelector
309
+ } else if ((next = part.match(RE_NESTED_SELECTOR))) {
310
+ if (next[0].startsWith('.')) {
311
+ token = TokenType.CssSelectorClass
312
+ } else {
313
+ token = TokenType.CssSelector
314
+ }
315
+ state = State.AfterSelector
300
316
  } else if ((next = part.match(RE_PROPERTY_NAME))) {
301
317
  token = TokenType.CssPropertyName
302
318
  state = State.AfterPropertyName
@@ -318,7 +334,7 @@ export const tokenizeLine = (line, lineState) => {
318
334
  state = State.InsideSelector
319
335
  } else if ((next = part.match(RE_CURLY_CLOSE))) {
320
336
  token = TokenType.CurlyClose
321
- state = State.TopLevelContent
337
+ state = stack.pop() || State.TopLevelContent
322
338
  } else {
323
339
  throw new Error('no')
324
340
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lvce-editor/shared-process",
3
- "version": "0.16.17",
3
+ "version": "0.16.19",
4
4
  "description": "Utility package for @lvce-editor/server",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -17,9 +17,9 @@
17
17
  },
18
18
  "dependencies": {
19
19
  "@babel/code-frame": "^7.22.5",
20
- "@lvce-editor/extension-host": "0.16.17",
21
- "@lvce-editor/extension-host-helper-process": "0.16.17",
22
- "@lvce-editor/pty-host": "0.16.17",
20
+ "@lvce-editor/extension-host": "0.16.19",
21
+ "@lvce-editor/extension-host-helper-process": "0.16.19",
22
+ "@lvce-editor/pty-host": "0.16.19",
23
23
  "debug": "^4.3.4",
24
24
  "execa": "^7.1.1",
25
25
  "exit-hook": "^3.2.0",
@@ -0,0 +1,7 @@
1
+ import * as ElectronContextMenu from './ElectronContextMenu.js'
2
+
3
+ export const name = 'ElectronContextMenu'
4
+
5
+ export const Commands = {
6
+ openContextMenu: ElectronContextMenu.openContextMenu,
7
+ }
@@ -0,0 +1,5 @@
1
+ import * as ParentIpc from '../ParentIpc/ParentIpc.js'
2
+
3
+ export const openContextMenu = (menuItems, x, y, customData) => {
4
+ return ParentIpc.invoke('ElectronContextMenu.openContextMenu', menuItems, x, y, customData)
5
+ }
@@ -0,0 +1,8 @@
1
+ import * as ElectronProcess from './ElectronProcess.js'
2
+
3
+ export const name = 'ElectronProcess'
4
+
5
+ export const Commands = {
6
+ getChromeVersion: ElectronProcess.getChromeVersion,
7
+ getElectronVersion: ElectronProcess.getElectronVersion,
8
+ }
@@ -0,0 +1,9 @@
1
+ import * as ParentIpc from '../ParentIpc/ParentIpc.js'
2
+
3
+ export const getElectronVersion = () => {
4
+ return ParentIpc.invoke('Process.getElectronVersion')
5
+ }
6
+
7
+ export const getChromeVersion = () => {
8
+ return ParentIpc.invoke('Process.getChromeVersion')
9
+ }
@@ -0,0 +1,9 @@
1
+ import * as ElectronSafeStorage from './ElectronSafeStorage.js'
2
+
3
+ export const name = 'ElectronSafeStorage'
4
+
5
+ export const Commands = {
6
+ decryptString: ElectronSafeStorage.decryptString,
7
+ encryptString: ElectronSafeStorage.encryptString,
8
+ isEncryptionAvailable: ElectronSafeStorage.isEncryptionAvailable,
9
+ }
@@ -0,0 +1,13 @@
1
+ import * as ParentIpc from '../ParentIpc/ParentIpc.js'
2
+
3
+ export const isEncryptionAvailable = () => {
4
+ return ParentIpc.invoke('ElectronSafeStorage.isEncryptionAvailable')
5
+ }
6
+
7
+ export const encryptString = (plainText) => {
8
+ return ParentIpc.invoke('ElectronSafeStorage.encryptString', plainText)
9
+ }
10
+
11
+ export const decryptString = (encrypted) => {
12
+ return ParentIpc.invoke('ElectronSafeStorage.decryptString', encrypted)
13
+ }
@@ -3,6 +3,7 @@ import * as ErrorCodes from '../ErrorCodes/ErrorCodes.js'
3
3
  import * as ExtensionManifestStatus from '../ExtensionManifestStatus/ExtensionManifestStatus.js'
4
4
  import * as ReadJson from '../JsonFile/JsonFile.js'
5
5
  import * as Path from '../Path/Path.js'
6
+ import * as IsEnoentError from '../IsEnoentError/IsEnoentError.js'
6
7
  import { VError } from '../VError/VError.js'
7
8
 
8
9
  const RE_EXTENSION_FRAGMENT = /.+(\/|\\)(.+)$/
@@ -32,7 +33,7 @@ export const get = async (path) => {
32
33
  } catch (error) {
33
34
  const id = inferExtensionId(path)
34
35
  const enhancedError = new VError(error, `Failed to load extension manifest for ${id}`)
35
- if (error.code === ErrorCodes.ENOENT) {
36
+ if (IsEnoentError.isEnoentError(error)) {
36
37
  try {
37
38
  const monoRepoPath = Path.join(path, `packages`, 'extension', 'extension.json')
38
39
  const json = await ReadJson.readJson(monoRepoPath)
@@ -1,6 +1,6 @@
1
- import * as ErrorCodes from '../ErrorCodes/ErrorCodes.js'
2
1
  import * as ExtensionManifest from '../ExtensionManifest/ExtensionManifest.js'
3
2
  import * as FileSystem from '../FileSystem/FileSystem.js'
3
+ import * as IsEnoentError from '../IsEnoentError/IsEnoentError.js'
4
4
  import * as ToAbsolutePaths from '../ToAbsolutePaths/ToAbsolutePaths.js'
5
5
  import { VError } from '../VError/VError.js'
6
6
 
@@ -14,9 +14,7 @@ export const getExtensionManifests = async (path) => {
14
14
  const manifests = await Promise.all(absolutePaths.map(ExtensionManifest.get))
15
15
  return manifests
16
16
  } catch (error) {
17
- // TODO how to make typescript happy?
18
- // @ts-ignore
19
- if (error.code === ErrorCodes.ENOENT) {
17
+ if (IsEnoentError.isEnoentError(error)) {
20
18
  return []
21
19
  }
22
20
  throw new VError(error, 'Failed to get extension manifests')
@@ -1,8 +1,8 @@
1
1
  import { readlink } from 'fs/promises'
2
- import * as ErrorCodes from '../ErrorCodes/ErrorCodes.js'
3
2
  import * as ExtensionManifest from '../ExtensionManifest/ExtensionManifest.js'
4
3
  import * as ExtensionManifestStatus from '../ExtensionManifestStatus/ExtensionManifestStatus.js'
5
4
  import * as FileSystem from '../FileSystem/FileSystem.js'
5
+ import * as IsEnoentError from '../IsEnoentError/IsEnoentError.js'
6
6
  import * as ToAbsolutePaths from '../ToAbsolutePaths/ToAbsolutePaths.js'
7
7
  import { VError } from '../VError/VError.js'
8
8
 
@@ -46,9 +46,7 @@ export const getExtensionManifests = async (path) => {
46
46
  const merged = mergeWithSymLinks(manifests, symlinks)
47
47
  return merged
48
48
  } catch (error) {
49
- // TODO how to make typescript happy?
50
- // @ts-ignore
51
- if (error.code === ErrorCodes.ENOENT) {
49
+ if (IsEnoentError.isEnoentError(error)) {
52
50
  return []
53
51
  }
54
52
  throw new VError(error, 'Failed to get extension manifests')
@@ -1,10 +1,12 @@
1
1
  // TODO lazyload chokidar and trash (but doesn't work currently because of bug with jest)
2
2
  import * as fs from 'node:fs/promises'
3
3
  import * as os from 'node:os'
4
+ import * as Assert from '../Assert/Assert.js'
4
5
  import * as EncodingType from '../EncodingType/EncodingType.js'
5
- import { FileNotFoundError } from '../FileNotFoundError/FileNotFoundError.js'
6
6
  import * as ErrorCodes from '../ErrorCodes/ErrorCodes.js'
7
+ import { FileNotFoundError } from '../FileNotFoundError/FileNotFoundError.js'
7
8
  import * as GetDirentType from '../GetDirentType/GetDirentType.js'
9
+ import * as IsEnoentError from '../IsEnoentError/IsEnoentError.js'
8
10
  import * as Path from '../Path/Path.js'
9
11
  import * as Platform from '../Platform/Platform.js'
10
12
  import * as Trash from '../Trash/Trash.js'
@@ -36,6 +38,7 @@ export const copy = async (source, target) => {
36
38
  export const readFile = async (path, encoding = EncodingType.Utf8) => {
37
39
  // console.info('[shared-process] read file', path)
38
40
  try {
41
+ Assert.string(path)
39
42
  // const start = performance.now()
40
43
  // console.time(`read ${path}`)
41
44
  const content = await fs.readFile(path, encoding)
@@ -44,7 +47,7 @@ export const readFile = async (path, encoding = EncodingType.Utf8) => {
44
47
  // console.timeEnd(`read ${path}`)
45
48
  return content
46
49
  } catch (error) {
47
- if (error && error.code === ErrorCodes.ENOENT) {
50
+ if (IsEnoentError.isEnoentError(error)) {
48
51
  throw new FileNotFoundError(path)
49
52
  }
50
53
  throw new VError(error, `Failed to read file "${path}"`)
@@ -59,11 +62,13 @@ export const readFile = async (path, encoding = EncodingType.Utf8) => {
59
62
  */
60
63
  export const writeFile = async (path, content, encoding = EncodingType.Utf8) => {
61
64
  try {
65
+ Assert.string(path)
66
+ Assert.string(content)
62
67
  // queue would be more correct for concurrent writes but also slower
63
68
  // Queue.add(`writeFile/${path}`, () =>
64
69
  await fs.writeFile(path, content, encoding)
65
70
  } catch (error) {
66
- if (error && error.code === ErrorCodes.ENOENT) {
71
+ if (IsEnoentError.isEnoentError(error)) {
67
72
  throw new FileNotFoundError(path)
68
73
  }
69
74
  throw new VError(error, `Failed to write to file "${path}"`)
@@ -169,7 +174,7 @@ export const readDir = async (path) => {
169
174
  const dirents = await fs.readdir(path)
170
175
  return dirents
171
176
  } catch (error) {
172
- if (error && error.code === ErrorCodes.ENOENT) {
177
+ if (IsEnoentError.isEnoentError(error)) {
173
178
  throw new FileNotFoundError(path)
174
179
  }
175
180
  throw new VError(error, `Failed to read directory "${path}"`)
@@ -3,34 +3,41 @@ import { join } from 'node:path'
3
3
  import * as Assert from '../Assert/Assert.js'
4
4
  import * as Character from '../Character/Character.js'
5
5
  import * as EncodingType from '../EncodingType/EncodingType.js'
6
- import * as ErrorCodes from '../ErrorCodes/ErrorCodes.js'
6
+ import * as IsEnoentError from '../IsEnoentError/IsEnoentError.js'
7
+ import * as IsEsrchError from '../IsEsrchError/IsEsrchError.js'
7
8
  import { VError } from '../VError/VError.js'
8
9
 
9
- export const getAccurateMemoryUsage = async (pid) => {
10
- Assert.number(pid)
10
+ const getContent = async (pid) => {
11
11
  try {
12
12
  const filePath = join('/proc', `${pid}`, 'statm')
13
- let content
14
- try {
15
- content = await readFile(filePath, EncodingType.Utf8)
16
- } catch (error) {
17
- if (
18
- error &&
19
- // @ts-ignore
20
- (error.code === ErrorCodes.ENOENT ||
21
- // @ts-ignore
22
- error.code === ErrorCodes.ESRCH)
23
- ) {
24
- return -1
25
- }
26
- throw error
13
+ const content = await readFile(filePath, EncodingType.Utf8)
14
+ return content
15
+ } catch (error) {
16
+ if (IsEnoentError.isEnoentError(error) || IsEsrchError.isEsrchError(error)) {
17
+ return ''
18
+ }
19
+ throw error
20
+ }
21
+ }
22
+
23
+ const parseMemory = (content) => {
24
+ const trimmedContent = content.trim()
25
+ const numberBlocks = trimmedContent.split(Character.Space)
26
+ const pageSize = 4096
27
+ const rss = Number.parseInt(numberBlocks[1]) * pageSize
28
+ const shared = Number.parseInt(numberBlocks[2]) * pageSize
29
+ const memory = rss - shared
30
+ return memory
31
+ }
32
+
33
+ export const getAccurateMemoryUsage = async (pid) => {
34
+ try {
35
+ Assert.number(pid)
36
+ const content = await getContent(pid)
37
+ if (!content) {
38
+ return -1
27
39
  }
28
- const trimmedContent = content.trim()
29
- const numberBlocks = trimmedContent.split(Character.Space)
30
- const pageSize = 4096
31
- const rss = Number.parseInt(numberBlocks[1]) * pageSize
32
- const shared = Number.parseInt(numberBlocks[2]) * pageSize
33
- const memory = rss - shared
40
+ const memory = parseMemory(content)
34
41
  return memory
35
42
  } catch (error) {
36
43
  throw new VError(error, 'Failed to get accurate memory usage')
@@ -1,6 +1,10 @@
1
+ import * as IsWindows from '../IsWindows/IsWindows.js'
1
2
  import * as Path from '../Path/Path.js'
2
3
  import * as Root from '../Root/Root.js'
3
4
 
4
5
  export const getElectronRebuildPath = () => {
6
+ if (IsWindows.isWindows) {
7
+ return Path.join(Root.root, 'packages', 'main-process', 'node_modules', '.bin', 'electron-rebuild.cmd')
8
+ }
5
9
  return Path.join(Root.root, 'packages', 'main-process', 'node_modules', '.bin', 'electron-rebuild')
6
10
  }
@@ -0,0 +1,22 @@
1
+ export const getFirstEvent = async (eventEmitter, eventMap) => {
2
+ const { type, event } = await new Promise((resolve, reject) => {
3
+ const listenerMap = Object.create(null)
4
+ const cleanup = (value) => {
5
+ for (const event of Object.keys(eventMap)) {
6
+ eventEmitter.off(event, listenerMap[event])
7
+ }
8
+ resolve(value)
9
+ }
10
+ for (const [event, type] of Object.entries(eventMap)) {
11
+ const listener = (event) => {
12
+ cleanup({
13
+ type,
14
+ event,
15
+ })
16
+ }
17
+ eventEmitter.on(event, listener)
18
+ listenerMap[event] = listener
19
+ }
20
+ })
21
+ return { type, event }
22
+ }
@@ -5,8 +5,10 @@ export const getFirstNodeChildProcessEvent = async (childProcess) => {
5
5
  let stderr = ''
6
6
  let stdout = ''
7
7
  const cleanup = (value) => {
8
- childProcess.stderr.off('data', handleStdErrData)
9
- childProcess.stdout.off('data', handleStdoutData)
8
+ if (childProcess.stdout && childProcess.stderr) {
9
+ childProcess.stderr.off('data', handleStdErrData)
10
+ childProcess.stdout.off('data', handleStdoutData)
11
+ }
10
12
  childProcess.off('message', handleMessage)
11
13
  childProcess.off('exit', handleExit)
12
14
  childProcess.off('error', handleError)
@@ -27,8 +29,10 @@ export const getFirstNodeChildProcessEvent = async (childProcess) => {
27
29
  const handleError = (event) => {
28
30
  cleanup({ type: FirstNodeWorkerEventType.Error, event, stdout, stderr })
29
31
  }
30
- childProcess.stderr.on('data', handleStdErrData)
31
- childProcess.stdout.on('data', handleStdoutData)
32
+ if (childProcess.stdout && childProcess.stderr) {
33
+ childProcess.stderr.on('data', handleStdErrData)
34
+ childProcess.stdout.on('data', handleStdoutData)
35
+ }
32
36
  childProcess.on('message', handleMessage)
33
37
  childProcess.on('exit', handleExit)
34
38
  childProcess.on('error', handleError)
@@ -1,20 +1,9 @@
1
1
  import * as FirstNodeWorkerEventType from '../FirstNodeWorkerEventType/FirstNodeWorkerEventType.js'
2
+ import * as GetFirstEvent from '../GetFirstEvent/GetFirstEvent.js'
2
3
 
3
- export const getFirstNodeWorkerEvent = async (worker) => {
4
- const { type, event } = await new Promise((resolve, reject) => {
5
- const cleanup = (value) => {
6
- worker.off('exit', handleExit)
7
- worker.off('error', handleError)
8
- resolve(value)
9
- }
10
- const handleExit = (event) => {
11
- cleanup({ type: FirstNodeWorkerEventType.Exit, event })
12
- }
13
- const handleError = (event) => {
14
- cleanup({ type: FirstNodeWorkerEventType.Error, event })
15
- }
16
- worker.on('exit', handleExit)
17
- worker.on('error', handleError)
4
+ export const getFirstNodeWorkerEvent = (worker) => {
5
+ return GetFirstEvent.getFirstEvent(worker, {
6
+ exit: FirstNodeWorkerEventType.Exit,
7
+ error: FirstNodeWorkerEventType.Error,
18
8
  })
19
- return { type, event }
20
9
  }
@@ -1,14 +1,14 @@
1
1
  import * as FirstWebSocketEventType from '../FirstWebSocketEventType/FirstWebSocketEventType.js'
2
- import { WebSocket } from 'ws'
2
+ import * as WebSocketReadyState from '../WebSocketReadyState/WebSocketReadyState.js'
3
3
 
4
4
  export const getFirstWebSocketEvent = async (webSocket) => {
5
5
  switch (webSocket.readyState) {
6
- case WebSocket.OPEN:
6
+ case WebSocketReadyState.Open:
7
7
  return {
8
8
  type: FirstWebSocketEventType.Open,
9
9
  event: undefined,
10
10
  }
11
- case WebSocket.CLOSED:
11
+ case WebSocketReadyState.Closed:
12
12
  return {
13
13
  type: FirstWebSocketEventType.Close,
14
14
  event: undefined,
@@ -0,0 +1,28 @@
1
+ import { HTTPError } from 'got'
2
+
3
+ /**
4
+ *
5
+ * @param {HTTPError} error
6
+ */
7
+ export const getHttpErrorMessage = (error) => {
8
+ try {
9
+ const body = error.response.body
10
+ if (error.response.url.includes('api.github.com') && typeof body === 'string') {
11
+ const json = JSON.parse(body)
12
+ if (json.message) {
13
+ const message = json.message
14
+ if (message.includes('rate limit exceeded')) {
15
+ const reset = error.response.headers['x-ratelimit-reset']
16
+ const limit = error.response.headers['x-ratelimit-limit']
17
+ if (reset && typeof reset === 'string' && typeof limit === 'string') {
18
+ const resetDate = new Date(parseInt(reset) * 1000)
19
+ const limitAmount = parseInt(limit)
20
+ return `GitHub rate limit of ${limitAmount} requests per hour execeeded, resets at ${resetDate}`
21
+ }
22
+ }
23
+ return json.message
24
+ }
25
+ }
26
+ } catch {}
27
+ return `${error.message}`
28
+ }
@@ -1,33 +1,7 @@
1
1
  import got, { HTTPError } from 'got'
2
+ import * as GetHttpErrorMessage from '../GetHttpErrorMessage/GetHttpErrorMessage.js'
2
3
  import { VError } from '../VError/VError.js'
3
4
 
4
- /**
5
- *
6
- * @param {HTTPError} error
7
- */
8
- const getHttpErrorMessage = (error) => {
9
- try {
10
- const body = error.response.body
11
- if (error.response.url.includes('api.github.com') && typeof body === 'string') {
12
- const json = JSON.parse(body)
13
- if (json.message) {
14
- const message = json.message
15
- if (message.includes('rate limit exceeded')) {
16
- const reset = error.response.headers['x-ratelimit-reset']
17
- const limit = error.response.headers['x-ratelimit-limit']
18
- if (reset && typeof reset === 'string' && typeof limit === 'string') {
19
- const resetDate = new Date(parseInt(reset) * 1000)
20
- const limitAmount = parseInt(limit)
21
- return `GitHub rate limit of ${limitAmount} requests per hour execeeded, resets at ${resetDate}`
22
- }
23
- }
24
- return json.message
25
- }
26
- }
27
- } catch {}
28
- return `${error.message}`
29
- }
30
-
31
5
  const parseVersionFromUrl = (url, repository) => {
32
6
  if (!url.includes('releases/tag')) {
33
7
  if (url.endsWith('/releases')) {
@@ -51,7 +25,7 @@ export const getLatestReleaseVersion = async (repository) => {
51
25
  return version
52
26
  } catch (error) {
53
27
  if (error instanceof HTTPError) {
54
- const httpErrorMessage = getHttpErrorMessage(error)
28
+ const httpErrorMessage = GetHttpErrorMessage.getHttpErrorMessage(error)
55
29
  throw new VError(`Failed to get latest release for ${repository}: ${httpErrorMessage}`)
56
30
  }
57
31
  // @ts-ignore
@@ -0,0 +1,6 @@
1
+ import * as Path from '../Path/Path.js'
2
+ import * as Root from '../Root/Root.js'
3
+
4
+ export const getTerminalProcessPath = () => {
5
+ return Path.join(Root.root, 'packages', 'pty-host', 'src', 'ptyHostMain.js')
6
+ }
@@ -1,2 +1,3 @@
1
1
  export const NodeWorker = 1
2
2
  export const NodeForkedProcess = 2
3
+ export const ElectronUtilityProcess = 3
@@ -0,0 +1,12 @@
1
+ import * as ParentIpc from '../ParentIpc/ParentIpc.js'
2
+
3
+ export const create = async (options) => {
4
+ // TODO how to launch process without race condition?
5
+ // when promise resolves, process might have already exited
6
+ const messagePort = await ParentIpc.invoke('IpcParent.create', { ...options })
7
+ return messagePort
8
+ }
9
+
10
+ export const wrap = (messagePort) => {
11
+ return {}
12
+ }
@@ -0,0 +1,5 @@
1
+ import * as ErrorCodes from '../ErrorCodes/ErrorCodes.js'
2
+
3
+ export const isDlOpenError = (error) => {
4
+ return error && error instanceof Error && 'code' in error && error.code === ErrorCodes.ERR_DLOPEN_FAILED
5
+ }
@@ -0,0 +1,5 @@
1
+ import * as ErrorCodes from '../ErrorCodes/ErrorCodes.js'
2
+
3
+ export const isEsrchError = (error) => {
4
+ return error && error.code === ErrorCodes.ESRCH
5
+ }
@@ -0,0 +1 @@
1
+ export const isWindows = process.platform === 'win32'
@@ -50,5 +50,8 @@ export const getName = (pid, cmd, rootPid, pidMap) => {
50
50
  if (cmd.startsWith(`/opt/sublime_text/sublime_text `)) {
51
51
  return 'sublime-text'
52
52
  }
53
+ if(cmd.includes('\\conhost.exe')){
54
+ return 'conhost.exe'
55
+ }
53
56
  return `${cmd}`
54
57
  }
@@ -1,12 +1,12 @@
1
+ import * as IsDlOpenError from '../IsDlOpenError/IsDlOpenError.js'
1
2
  import { VError } from '../VError/VError.js'
2
- import * as ErrorCodes from '../ErrorCodes/ErrorCodes.js'
3
3
 
4
4
  export const loadWindowProcessTree = async () => {
5
5
  try {
6
6
  // @ts-ignore
7
7
  return await import('@vscode/windows-process-tree')
8
8
  } catch (error) {
9
- if (error && error instanceof Error && 'code' in error && error.code === ErrorCodes.ERR_DLOPEN_FAILED) {
9
+ if (IsDlOpenError.isDlOpenError(error)) {
10
10
  throw new VError(
11
11
  `Failed to load windows process tree: The native module "@vscode/windows-process-tree" is not compatible with this node version and must be compiled against a matching electron version using electron-rebuild`
12
12
  )
@@ -20,8 +20,14 @@ export const load = (moduleId) => {
20
20
  return import('../Developer/Developer.ipc.js')
21
21
  case ModuleId.Download:
22
22
  return import('../Download/Download.ipc.js')
23
+ case ModuleId.ElectronContextMenu:
24
+ return import('../ElectronContextMenu/ElectronContextMenu.ipc.js')
23
25
  case ModuleId.ElectronInitialize:
24
26
  return import('../ElectronInitialize/ElectronInitialize.ipc.js')
27
+ case ModuleId.ElectronProcess:
28
+ return import('../ElectronProcess/ElectronProcess.ipc.js')
29
+ case ModuleId.ElectronSafeStorage:
30
+ return import('../ElectronSafeStorage/ElectronSafeStorage.ipc.js')
25
31
  case ModuleId.ExtensionHost:
26
32
  return import('../ExtensionHost/ExtensionHost.ipc.js')
27
33
  case ModuleId.ExtensionManagement:
@@ -52,6 +58,8 @@ export const load = (moduleId) => {
52
58
  return import('../OpenNativeFolder/OpenNativeFolder.ipc.js')
53
59
  case ModuleId.OutputChannel:
54
60
  return import('../OutputChannel/OutputChannel.ipc.js')
61
+ case ModuleId.Performance:
62
+ return import('../Performance/Performance.ipc.js')
55
63
  case ModuleId.Platform:
56
64
  return import('../Platform/Platform.ipc.js')
57
65
  case ModuleId.Preferences:
@@ -72,10 +80,10 @@ export const load = (moduleId) => {
72
80
  return import('../TextSearch/TextSearch.ipc.js')
73
81
  case ModuleId.WebSocketServer:
74
82
  return import('../WebSocketServer/WebSocketServer.ipc.js')
83
+ case ModuleId.Window:
84
+ return import('../Window/ElectronWindow.ipc.js')
75
85
  case ModuleId.Workspace:
76
86
  return import('../Workspace/Workspace.ipc.js')
77
- case ModuleId.Performance:
78
- return import('../Performance/Performance.ipc.js')
79
87
  default:
80
88
  throw new Error(`module ${moduleId} not found`)
81
89
  }
@@ -24,14 +24,19 @@ export const IsAutoUpdateSupported = 23
24
24
  export const AutoUpdaterAppImage = 24
25
25
  export const AutoUpdaterWindowsNsis = 25
26
26
  export const RebuildNodePty = 26
27
+ export const ElectronSafeStorage = 80
27
28
  export const ElectronInitialize = 27
28
29
  export const HandleWebSocket = 28
29
30
  export const Process = 29
30
31
  export const AttachDebugger = 30
31
32
  export const HandleElectronMessagePort = 31
33
+ export const ElectronProcess = 70
32
34
  export const HandleNodeMessagePort = 32
33
35
  export const GetTerminalSpawnOptions = 33
34
36
  export const HandleCliArgs = 34
35
37
  export const ListProcessesWithMemoryUsage = 35
38
+ export const ElectronContextMenu = 40
36
39
  export const IncrementalTextSearch = 36
37
40
  export const Performance = 37
41
+ export const Window = 38
42
+ export const PassThroughElectronMessagePort = 39
@@ -3,6 +3,8 @@ import * as ModuleId from '../ModuleId/ModuleId.js'
3
3
 
4
4
  export const getModuleId = (commandId) => {
5
5
  switch (commandId) {
6
+ case 'PassthroughElectronMessagePort.passThroughElectronMessagePort':
7
+ return ModuleId.PassThroughElectronMessagePort
6
8
  case 'AttachDebugger.attachDebugger':
7
9
  return ModuleId.AttachDebugger
8
10
  case 'AutoUpdater.getAutoUpdateType':
@@ -22,6 +24,10 @@ export const getModuleId = (commandId) => {
22
24
  case 'ClipBoard.readFiles':
23
25
  case 'ClipBoard.writeFiles':
24
26
  return ModuleId.ClipBoard
27
+ case 'ElectronSafeStorage.encryptString':
28
+ case 'ElectronSafeStorage.decryptString':
29
+ case 'ElectronSafeStorage.isEncryptionAvailable':
30
+ return ModuleId.ElectronSafeStorage
25
31
  case 'Developer.allocateMemory':
26
32
  case 'Developer.crashSharedProcess':
27
33
  case 'Developer.createHeapSnapshot':
@@ -177,13 +183,25 @@ export const getModuleId = (commandId) => {
177
183
  case 'Terminal.resize':
178
184
  case 'Terminal.write':
179
185
  return ModuleId.Terminal
186
+ case 'ElectronProcess.getChromeVersion':
187
+ case 'ElectronProcess.getElectronVersion':
188
+ return ModuleId.ElectronProcess
180
189
  case 4820:
181
190
  return ModuleId.TextDocument
191
+ case 'ElectronContextMenu.openContextMenu':
192
+ return ModuleId.ElectronContextMenu
182
193
  case 'WebSocketServer.handleUpgrade':
183
194
  return ModuleId.WebSocketServer
184
195
  case 'Workspace.getHomeDir':
185
196
  case 'Workspace.resolveRoot':
186
197
  return ModuleId.Workspace
198
+ case 'ElectronWindow.minimize':
199
+ case 'ElectronWindow.maximize':
200
+ case 'ElectronWindow.toggleDevtools':
201
+ case 'ElectronWindow.unmaximize':
202
+ case 'ElectronWindow.close':
203
+ case 'ElectronWindow.reload':
204
+ return ModuleId.Window
187
205
  default:
188
206
  throw new CommandNotFoundError(commandId)
189
207
  }
@@ -0,0 +1,29 @@
1
+ import * as Assert from '../Assert/Assert.js'
2
+ import * as GetTerminalProcessPath from '../GetTerminalProcessPath/GetTerminalProcessPath.js'
3
+ import * as IpcParent from '../IpcParent/IpcParent.js'
4
+ import * as IpcParentType from '../IpcParentType/IpcParentType.js'
5
+
6
+ /**
7
+ *
8
+ * @returns
9
+ */
10
+ export const handlePort = async (browserWindowPort, type, name) => {
11
+ Assert.object(browserWindowPort)
12
+ Assert.string(type)
13
+ Assert.string(name)
14
+ const ptyHostPath = GetTerminalProcessPath.getTerminalProcessPath()
15
+ const ipc = await IpcParent.create({
16
+ method: IpcParentType.ElectronUtilityProcess,
17
+ path: ptyHostPath,
18
+ name,
19
+ })
20
+ // TODO use connectIpc for better error handling
21
+ ipc.sendAndTransfer(
22
+ {
23
+ jsonrpc: '2.0',
24
+ method: 'HandleElectronMessagePort.handleElectronMessagePort',
25
+ params: [],
26
+ },
27
+ [browserWindowPort]
28
+ )
29
+ }
@@ -0,0 +1,7 @@
1
+ import * as PassThroughElectronMessagePort from './PassThroughElectronMessagePort.js'
2
+
3
+ export const name = 'PassThroughElectronMessagePort'
4
+
5
+ export const Commands = {
6
+ passThroughElectronMessagePort: PassThroughElectronMessagePort.passThroughElectronMessagePort,
7
+ }
@@ -0,0 +1,4 @@
1
+ import * as Path from '../Path/Path.js'
2
+ import * as Root from '../Root/Root.js'
3
+
4
+ export const passThroughElectronMessagePort = () => {}
@@ -0,0 +1,8 @@
1
+ export const getModule = (type) => {
2
+ switch (type) {
3
+ case 'terminal-process':
4
+ return import('../PassThroughElectronMessageForTerminalProcess/PassThroughElectronMessageForTerminalProcess.js')
5
+ default:
6
+ return undefined
7
+ }
8
+ }
@@ -21,8 +21,8 @@ export const Commands = {
21
21
  getRecentlyOpenedPath: Platform.getRecentlyOpenedPath,
22
22
  getTestPath: Platform.getTestPath,
23
23
  getTmpDir: Platform.getTmpDir,
24
+ getUserKeyBindingsPath: Platform.getUserKeyBindingsPath,
24
25
  getUserSettingsPath: Platform.getUserSettingsPath,
25
26
  getVersion: Platform.getVersion,
26
27
  setEnvironmentVariables: Platform.setEnvironmentVariables,
27
- getUserKeyBindingsPath: Platform.getUserKeyBindingsPath,
28
28
  }
@@ -169,11 +169,11 @@ export const getSetupName = () => {
169
169
  return 'Lvce-Setup'
170
170
  }
171
171
 
172
- export const version = '0.16.17'
172
+ export const version = '0.16.19'
173
173
 
174
- export const commit = '6f015ba'
174
+ export const commit = '6594144'
175
175
 
176
- export const date = '2023-07-19T21:49:49.000Z'
176
+ export const date = '2023-07-27T00:13:37.000Z'
177
177
 
178
178
  export const getVersion = () => {
179
179
  return version
@@ -2,6 +2,7 @@ import * as ErrorCodes from '../ErrorCodes/ErrorCodes.js'
2
2
  import * as JsoncFile from '../JsoncFile/JsoncFile.js'
3
3
  import * as Platform from '../Platform/Platform.js'
4
4
  import * as Process from '../Process/Process.js'
5
+ import * as IsEnoentError from '../IsEnoentError/IsEnoentError.js'
5
6
  import { VError } from '../VError/VError.js'
6
7
  // TODO need jsonc parser for settings with comments
7
8
 
@@ -12,7 +13,7 @@ export const getUserPreferences = async () => {
12
13
  try {
13
14
  json = await JsoncFile.readJsonc(userSettingsPath)
14
15
  } catch (error) {
15
- if (error && error.code === ErrorCodes.ENOENT) {
16
+ if (IsEnoentError.isEnoentError(error)) {
16
17
  return {}
17
18
  }
18
19
  throw error
@@ -1,4 +1,5 @@
1
1
  import { VError } from '../VError/VError.js'
2
+ import * as IsEsrchError from '../IsEsrchError/IsEsrchError.js'
2
3
 
3
4
  export const crash = (message) => {
4
5
  throw new Error(message)
@@ -34,6 +35,9 @@ export const kill = (pid, signal) => {
34
35
  try {
35
36
  process.kill(pid, signal)
36
37
  } catch (error) {
38
+ if (IsEsrchError.isEsrchError(error)) {
39
+ return
40
+ }
37
41
  throw new VError(error, `Failed to kill process ${pid} with signal ${signal}`)
38
42
  }
39
43
  }
@@ -3,12 +3,13 @@ import * as Debug from '../Debug/Debug.js'
3
3
  import * as IpcParent from '../IpcParent/IpcParent.js'
4
4
  import * as IpcParentType from '../IpcParentType/IpcParentType.js'
5
5
  import * as PtyHostPath from '../PtyHostPath/PtyHostPath.js'
6
+ import * as JsonRpc from '../JsonRpc/JsonRpc.js'
6
7
 
7
8
  export const state = {
8
9
  /**
9
10
  * @type {any}
10
11
  */
11
- ptyHost: undefined,
12
+ ipc: undefined,
12
13
  /**
13
14
  * @type {any}
14
15
  */
@@ -23,9 +24,9 @@ export const state = {
23
24
  }
24
25
 
25
26
  const cleanUpAll = () => {
26
- if (state.ptyHost) {
27
- state.ptyHost.dispose()
28
- state.ptyHost = undefined
27
+ if (state.ipc) {
28
+ state.ipc.dispose()
29
+ state.ipc = undefined
29
30
  }
30
31
  }
31
32
 
@@ -46,11 +47,11 @@ const createPtyHost = async () => {
46
47
  })
47
48
  const handleClose = () => {
48
49
  ptyHost.off('close', handleClose)
49
- state.ptyHost = undefined
50
+ state.ipc = undefined
50
51
  state.ptyHostPromise = undefined
51
52
  }
52
53
  ptyHost.on('close', handleClose)
53
- state.ptyHost = ptyHost
54
+ state.ipc = ptyHost
54
55
  return ptyHost
55
56
  }
56
57
 
@@ -63,15 +64,15 @@ export const getOrCreate = () => {
63
64
  }
64
65
 
65
66
  export const getCurrentInstance = () => {
66
- return state.ptyHost
67
+ return state.ipc
67
68
  }
68
69
 
69
70
  export const disposeAll = () => {
70
- if (state.ptyHost) {
71
- state.ptyHost.removeAllListeners()
72
- if (state.ptyHost) {
73
- state.ptyHost.dispose()
74
- state.ptyHost = undefined
71
+ if (state.ipc) {
72
+ state.ipc.removeAllListeners()
73
+ if (state.ipc) {
74
+ state.ipc.dispose()
75
+ state.ipc = undefined
75
76
  }
76
77
  state.ptyHostState = /* None */ 0
77
78
  state.send = (message) => {
@@ -79,3 +80,7 @@ export const disposeAll = () => {
79
80
  }
80
81
  }
81
82
  }
83
+
84
+ export const invoke = (method, ...params) => {
85
+ return JsonRpc.invoke(state.ipc, method, ...params)
86
+ }
@@ -0,0 +1,19 @@
1
+ import { spawn } from 'node:child_process'
2
+ import * as FirstNodeWorkerEventType from '../FirstNodeWorkerEventType/FirstNodeWorkerEventType.js'
3
+ import * as GetElectronRebuildPath from '../GetElectronRebuildPath/GetElectronRebuildPath.js'
4
+ import * as GetFirstNodeChildProcessEvent from '../GetFirstNodeChildProcessEvent/GetFirstNodeChildProcessEvent.js'
5
+
6
+ /**
7
+ * @param {string} cwd
8
+ */
9
+ export const rebuild = async (cwd) => {
10
+ const electronRebuildPath = GetElectronRebuildPath.getElectronRebuildPath()
11
+ const childProcess = spawn(electronRebuildPath, [], {
12
+ cwd,
13
+ stdio: 'inherit',
14
+ })
15
+ const { type, event } = await GetFirstNodeChildProcessEvent.getFirstNodeChildProcessEvent(childProcess)
16
+ if (type === FirstNodeWorkerEventType.Error) {
17
+ throw new Error(`Failed to rebuild native module: ${event}`)
18
+ }
19
+ }
@@ -0,0 +1,17 @@
1
+ import { spawn } from 'node:child_process'
2
+ import * as FirstNodeWorkerEventType from '../FirstNodeWorkerEventType/FirstNodeWorkerEventType.js'
3
+ import * as GetFirstNodeChildProcessEvent from '../GetFirstNodeChildProcessEvent/GetFirstNodeChildProcessEvent.js'
4
+
5
+ /**
6
+ * @param {string} cwd
7
+ */
8
+ export const rebuild = async (cwd) => {
9
+ const childProcess = spawn('npm', ['rebuild'], {
10
+ cwd,
11
+ stdio: 'inherit',
12
+ })
13
+ const { type, event } = await GetFirstNodeChildProcessEvent.getFirstNodeChildProcessEvent(childProcess)
14
+ if (type === FirstNodeWorkerEventType.Error) {
15
+ throw new Error(`Failed to rebuild native module: ${event}`)
16
+ }
17
+ }
@@ -1,5 +1,3 @@
1
- import { spawn } from 'node:child_process'
2
- import * as GetElectronRebuildPath from '../GetElectronRebuildPath/GetElectronRebuildPath.js'
3
1
  import * as IsElectron from '../IsElectron/IsElectron.js'
4
2
  import * as Path from '../Path/Path.js'
5
3
  import * as Root from '../Root/Root.js'
@@ -9,40 +7,18 @@ const getPtyHostPath = () => {
9
7
  return Path.join(Root.root, 'packages', 'pty-host')
10
8
  }
11
9
 
12
- /**
13
- * @param {string} cwd
14
- */
15
- const rebuildNodePtyElectron = async (cwd) => {
16
- const electronRebuildPath = GetElectronRebuildPath.getElectronRebuildPath()
17
- const childProcess = spawn(electronRebuildPath, [], {
18
- cwd,
19
- stdio: 'inherit',
20
- })
21
- // TODO wait for child process to be finished
22
- }
23
-
24
- /**
25
- * @param {string} cwd
26
- */
27
- const rebuildNodePtyNode = async (cwd) => {
28
- const childProcess = spawn('npm', ['rebuild'], {
29
- cwd,
30
- stdio: 'inherit',
31
- })
32
- }
33
-
34
- const getFn = () => {
10
+ const getModule = () => {
35
11
  if (IsElectron.isElectron()) {
36
- return rebuildNodePtyElectron
12
+ return import('../RebuildForElectron/RebuildForElectron.js')
37
13
  }
38
- return rebuildNodePtyNode
14
+ return import('../RebuildForNode/RebuildForNode.js')
39
15
  }
40
16
 
41
17
  export const rebuildNodePty = async () => {
42
18
  try {
43
19
  const ptyHostPath = getPtyHostPath()
44
- const rebuild = getFn()
45
- await rebuild(ptyHostPath)
20
+ const module = await getModule()
21
+ await module.rebuild(ptyHostPath)
46
22
  } catch (error) {
47
23
  throw new VError(error, `Failed to rebuild node-pty`)
48
24
  }
@@ -5,6 +5,7 @@ import * as FileSystem from '../FileSystem/FileSystem.js'
5
5
  import * as Json from '../Json/Json.js'
6
6
  import * as JsonFile from '../JsonFile/JsonFile.js'
7
7
  import * as Platform from '../Platform/Platform.js'
8
+ import * as IsEnoentError from '../IsEnoentError/IsEnoentError.js'
8
9
  import { VError } from '../VError/VError.js'
9
10
 
10
11
  const addToArrayUnique = (recentlyOpened, path) => {
@@ -20,7 +21,7 @@ const getRecentlyOpened = async (recentlyOpenedPath) => {
20
21
  const parsed = await JsonFile.readJson(recentlyOpenedPath)
21
22
  return parsed
22
23
  } catch (error) {
23
- if (error && error.code === ErrorCodes.ENOENT) {
24
+ if (IsEnoentError.isEnoentError(error)) {
24
25
  // ignore
25
26
  } else if (error && error.code === ErrorCodes.E_JSON_PARSE) {
26
27
  // ignore
@@ -36,7 +37,7 @@ const setRecentlyOpened = async (recentlyOpenedPath, newRecentlyOpened) => {
36
37
  try {
37
38
  await FileSystem.writeFile(recentlyOpenedPath, stringified)
38
39
  } catch (error) {
39
- if (error && error.code === ErrorCodes.ENOENT) {
40
+ if (IsEnoentError.isEnoentError(error)) {
40
41
  await FileSystem.mkdir(dirname(recentlyOpenedPath))
41
42
  await FileSystem.writeFile(recentlyOpenedPath, stringified)
42
43
  return
@@ -37,9 +37,11 @@ export const create = async (ipc, id, cwd, command, args) => {
37
37
  const ptyHost = await PtyHost.getOrCreate()
38
38
  const terminal = createTerminal(ptyHost, ipc)
39
39
  TerminalState.add(id, terminal)
40
- // TODO use invoke
41
- const message = JsonRpcEvent.create('Terminal.create', [id, cwd, command, args])
42
- ptyHost.send(message)
40
+ // TODO improve ipc error handling and await promise
41
+ // current can't await promise because when process exits
42
+ // because of native module error, promise is not resolved
43
+ // causing application which waits on this promise to hang
44
+ PtyHost.invoke('Terminal.create', id, cwd, command, args)
43
45
  } catch (error) {
44
46
  throw new VError(error, `Failed to create terminal`)
45
47
  }
@@ -1,29 +1,9 @@
1
1
  import * as ProcessExitEventType from '../ProcessExitEventType/ProcessExitEventType.js'
2
+ import * as GetFirstEvent from '../GetFirstEvent/GetFirstEvent.js'
2
3
 
3
4
  export const waitForProcessToExit = async (childProcess) => {
4
- const { type, event } = await new Promise((resolve, reject) => {
5
- const cleanup = (value) => {
6
- childProcess.off('close', handleClose)
7
- childProcess.off('error', handleError)
8
- resolve(value)
9
- }
10
- const handleClose = (event) => {
11
- cleanup({
12
- type: ProcessExitEventType.Exit,
13
- event,
14
- })
15
- }
16
- const handleError = (event) => {
17
- cleanup({
18
- type: ProcessExitEventType.Error,
19
- event,
20
- })
21
- }
22
- childProcess.on('close', handleClose)
23
- childProcess.on('error', handleError)
5
+ return GetFirstEvent.getFirstEvent(childProcess, {
6
+ error: ProcessExitEventType.Error,
7
+ close: ProcessExitEventType.Exit,
24
8
  })
25
- return {
26
- type,
27
- event,
28
- }
29
9
  }
@@ -0,0 +1,5 @@
1
+ import { WebSocket } from 'ws'
2
+
3
+ export const Open = WebSocket.OPEN
4
+
5
+ export const Closed = WebSocket.CLOSED
@@ -0,0 +1,12 @@
1
+ import * as ElectronWindow from './ElectronWindow.js'
2
+
3
+ export const name = 'ElectronWindow'
4
+
5
+ export const Commands = {
6
+ close: ElectronWindow.close,
7
+ maximize: ElectronWindow.maximize,
8
+ minimize: ElectronWindow.minimize,
9
+ reload: ElectronWindow.reload,
10
+ toggleDevtools: ElectronWindow.toggleDevtools,
11
+ unmaximize: ElectronWindow.unmaximize,
12
+ }
@@ -0,0 +1,32 @@
1
+ import * as Assert from '../Assert/Assert.js'
2
+ import * as ParentIpc from '../ParentIpc/ParentIpc.js'
3
+
4
+ export const minimize = (windowId) => {
5
+ Assert.number(windowId)
6
+ return ParentIpc.invoke('ElectronWindow.minimize', windowId)
7
+ }
8
+
9
+ export const toggleDevtools = (windowId) => {
10
+ Assert.number(windowId)
11
+ return ParentIpc.invoke('ElectronWindow.toggleDevtools', windowId)
12
+ }
13
+
14
+ export const maximize = (windowId) => {
15
+ Assert.number(windowId)
16
+ return ParentIpc.invoke('ElectronWindow.maximize', windowId)
17
+ }
18
+
19
+ export const unmaximize = (windowId) => {
20
+ Assert.number(windowId)
21
+ return ParentIpc.invoke('ElectronWindow.unmaximize')
22
+ }
23
+
24
+ export const close = (windowId) => {
25
+ Assert.number(windowId)
26
+ return ParentIpc.invoke('ElectronWindow.close', windowId)
27
+ }
28
+
29
+ export const reload = (windowId) => {
30
+ Assert.number(windowId)
31
+ return ParentIpc.invoke('ElectronWindow.reload', windowId)
32
+ }