@lvce-editor/shared-process 0.0.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 (143) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +41 -0
  3. package/bin/sharedProcess.js +3 -0
  4. package/index.js +6 -0
  5. package/package.json +96 -0
  6. package/src/parts/Assert/Assert.js +57 -0
  7. package/src/parts/Callback/Callback.js +48 -0
  8. package/src/parts/ChildProcess/ChildProcess.js +98 -0
  9. package/src/parts/ClipBoard/ClipBoard.ipc.js +7 -0
  10. package/src/parts/ClipBoard/ClipBoard.js +23 -0
  11. package/src/parts/ClipBoard/ClipBoardGnome.js +78 -0
  12. package/src/parts/ClipBoard/ClipBoardNoop.js +14 -0
  13. package/src/parts/ClipBoard/ClipBoardWindows.js +22 -0
  14. package/src/parts/Command/Command.js +264 -0
  15. package/src/parts/Configuration/Configuration.ipc.js +7 -0
  16. package/src/parts/Configuration/Configuration.js +39 -0
  17. package/src/parts/Credentials/Credentials.ipc.js +10 -0
  18. package/src/parts/Credentials/Credentials.js +69 -0
  19. package/src/parts/Debug/Debug.js +3 -0
  20. package/src/parts/Developer/Developer.ipc.js +20 -0
  21. package/src/parts/Developer/Developer.js +132 -0
  22. package/src/parts/Download/Download.js +33 -0
  23. package/src/parts/Electron/Electron.ipc.js +19 -0
  24. package/src/parts/Electron/Electron.js +99 -0
  25. package/src/parts/Env/Env.js +3 -0
  26. package/src/parts/Error/Error.js +30 -0
  27. package/src/parts/ErrorHandling/ErrorHandling.js +40 -0
  28. package/src/parts/Exec/Exec.js +16 -0
  29. package/src/parts/Extension/Extension.js +7 -0
  30. package/src/parts/ExtensionHost/ExtensionHost.ipc.js +80 -0
  31. package/src/parts/ExtensionHost/ExtensionHost.js +163 -0
  32. package/src/parts/ExtensionHost/ExtensionHostBraceCompletion.js +14 -0
  33. package/src/parts/ExtensionHost/ExtensionHostClosingTag.js +14 -0
  34. package/src/parts/ExtensionHost/ExtensionHostColorTheme.js +60 -0
  35. package/src/parts/ExtensionHost/ExtensionHostCommand.js +19 -0
  36. package/src/parts/ExtensionHost/ExtensionHostCompletion.js +12 -0
  37. package/src/parts/ExtensionHost/ExtensionHostDefinition.js +12 -0
  38. package/src/parts/ExtensionHost/ExtensionHostDiagnostic.js +7 -0
  39. package/src/parts/ExtensionHost/ExtensionHostFileSystem.js +23 -0
  40. package/src/parts/ExtensionHost/ExtensionHostFormatting.js +4 -0
  41. package/src/parts/ExtensionHost/ExtensionHostHover.js +12 -0
  42. package/src/parts/ExtensionHost/ExtensionHostIconTheme.js +35 -0
  43. package/src/parts/ExtensionHost/ExtensionHostImplementation.js +12 -0
  44. package/src/parts/ExtensionHost/ExtensionHostKeyBindings.js +23 -0
  45. package/src/parts/ExtensionHost/ExtensionHostLanguages.js +84 -0
  46. package/src/parts/ExtensionHost/ExtensionHostManagement.js +26 -0
  47. package/src/parts/ExtensionHost/ExtensionHostOutput.js +3 -0
  48. package/src/parts/ExtensionHost/ExtensionHostQuickPick.js +6 -0
  49. package/src/parts/ExtensionHost/ExtensionHostReference.js +12 -0
  50. package/src/parts/ExtensionHost/ExtensionHostRename.js +27 -0
  51. package/src/parts/ExtensionHost/ExtensionHostSemanticTokens.js +10 -0
  52. package/src/parts/ExtensionHost/ExtensionHostSourceControl.js +15 -0
  53. package/src/parts/ExtensionHost/ExtensionHostStats.js +3 -0
  54. package/src/parts/ExtensionHost/ExtensionHostStatusBar.js +7 -0
  55. package/src/parts/ExtensionHost/ExtensionHostTabCompletion.js +12 -0
  56. package/src/parts/ExtensionHost/ExtensionHostTextDocument.js +35 -0
  57. package/src/parts/ExtensionHost/ExtensionHostTypeDefinition.js +12 -0
  58. package/src/parts/ExtensionHost/ExtensionHostWorkspace.js +3 -0
  59. package/src/parts/ExtensionManagement/ExtensionManagement.ipc.js +11 -0
  60. package/src/parts/ExtensionManagement/ExtensionManagement.js +319 -0
  61. package/src/parts/FileSystem/FileSystem.ipc.js +32 -0
  62. package/src/parts/FileSystem/FileSystem.js +228 -0
  63. package/src/parts/Git/Git.js +58 -0
  64. package/src/parts/Json/Json.js +67 -0
  65. package/src/parts/JsonFile/JsonFile.js +14 -0
  66. package/src/parts/JsonRpc/JsonRpc.js +22 -0
  67. package/src/parts/Native/Native.ipc.js +6 -0
  68. package/src/parts/Native/Native.js +14 -0
  69. package/src/parts/OutputChannel/OutputChannel.ipc.js +51 -0
  70. package/src/parts/OutputChannel/OutputChannel.js +37 -0
  71. package/src/parts/ParentIpc/ParentIpc.js +203 -0
  72. package/src/parts/Path/Path.js +17 -0
  73. package/src/parts/Platform/Platform.ipc.js +18 -0
  74. package/src/parts/Platform/Platform.js +165 -0
  75. package/src/parts/Preferences/Preferences.ipc.js +6 -0
  76. package/src/parts/Preferences/Preferences.js +75 -0
  77. package/src/parts/PrettyError/PrettyError.js +86 -0
  78. package/src/parts/Process/Process.ipc.js +7 -0
  79. package/src/parts/Process/Process.js +7 -0
  80. package/src/parts/Queue/Queue.js +35 -0
  81. package/src/parts/RequiresSocket/RequiresSocket.js +11 -0
  82. package/src/parts/RgPath/RgPath.js +1 -0
  83. package/src/parts/Root/Root.js +6 -0
  84. package/src/parts/Search/Search.ipc.js +6 -0
  85. package/src/parts/Search/Search.js +101 -0
  86. package/src/parts/SearchFile/SearchFile.ipc.js +6 -0
  87. package/src/parts/SearchFile/SearchFile.js +51 -0
  88. package/src/parts/Socket/Socket.js +132 -0
  89. package/src/parts/Stats/Stats.js +45 -0
  90. package/src/parts/Terminal/Terminal.ipc.js +45 -0
  91. package/src/parts/Terminal/Terminal.js +161 -0
  92. package/src/parts/TextDocument/TextDocument.ipc.js +6 -0
  93. package/src/parts/TextDocument/TextDocument.js +37 -0
  94. package/src/parts/Trace/Trace.js +15 -0
  95. package/src/parts/Trash/Trash.js +11 -0
  96. package/src/parts/WebSocketServer/WebSocketServer.ipc.js +6 -0
  97. package/src/parts/WebSocketServer/WebSocketServer.js +43 -0
  98. package/src/parts/Workspace/Workspace.ipc.js +7 -0
  99. package/src/parts/Workspace/Workspace.js +107 -0
  100. package/src/sharedProcessMain.js +61 -0
  101. package/test/Callback.test.js +42 -0
  102. package/test/ClipBoard.test.js +105 -0
  103. package/test/Credentials.test.js +147 -0
  104. package/test/Developer.test.js +34 -0
  105. package/test/Electron.test.js +93 -0
  106. package/test/Error.test.js +16 -0
  107. package/test/Exec.test.js +25 -0
  108. package/test/ExtensionHost.test.js +127 -0
  109. package/test/ExtensionHostColorTheme.test.js +141 -0
  110. package/test/ExtensionHostCommand.test.js +27 -0
  111. package/test/ExtensionHostCompletion.test.js +45 -0
  112. package/test/ExtensionHostDefinition.test.js +35 -0
  113. package/test/ExtensionHostDiagnostic.test.js +23 -0
  114. package/test/ExtensionHostFileSystem.test.js +91 -0
  115. package/test/ExtensionHostFormatting.test.js +27 -0
  116. package/test/ExtensionHostIconTheme.test.js +73 -0
  117. package/test/ExtensionHostLanguages.test.js +212 -0
  118. package/test/ExtensionHostOutput.test.js +23 -0
  119. package/test/ExtensionHostRename.test.js +59 -0
  120. package/test/ExtensionHostSemanticTokens.test.js +26 -0
  121. package/test/ExtensionHostTabCompletion.test.js +35 -0
  122. package/test/ExtensionHostTextDocument.test.js +61 -0
  123. package/test/ExtensionHostTypeDefinition.test.js +35 -0
  124. package/test/ExtensionHostWorkspace.test.js +21 -0
  125. package/test/ExtensionManagement.test.js +408 -0
  126. package/test/FileSystem.test.js +294 -0
  127. package/test/Json.test.js +25 -0
  128. package/test/Native.test.js +31 -0
  129. package/test/OutputChannel.test.js +68 -0
  130. package/test/Path.test.js +17 -0
  131. package/test/Platform.test.js +61 -0
  132. package/test/Preferences.test.js +140 -0
  133. package/test/Process.test.js +9 -0
  134. package/test/RgPath.test.js +5 -0
  135. package/test/Search.test.js +45 -0
  136. package/test/SearchFile.test.js +80 -0
  137. package/test/Terminal.test.js +53 -0
  138. package/test/Trash.test.js +34 -0
  139. package/test/WebSocketServer.test.js +98 -0
  140. package/test/fixture-search-1/index.html +10 -0
  141. package/test/fixture-search-file-1/fileA +0 -0
  142. package/test/fixture-search-file-1/fileB +0 -0
  143. package/test/fixture-search-file-1/nested/fileC +0 -0
@@ -0,0 +1,132 @@
1
+ import VError from 'verror'
2
+ import * as Command from '../Command/Command.js'
3
+ import * as Error from '../Error/Error.js'
4
+ import { requiresSocket } from '../RequiresSocket/RequiresSocket.js'
5
+
6
+ export const state = {
7
+ /**
8
+ * @type {any}
9
+ */
10
+ socket: undefined,
11
+ }
12
+
13
+ export const send = (message) => {
14
+ if (!state.socket) {
15
+ console.warn('socket not yet available')
16
+ return
17
+ }
18
+ state.socket.send(message)
19
+ }
20
+
21
+ const handleMessage = async (event) => {
22
+ const object = JSON.parse(event.data)
23
+ if (object.id) {
24
+ if (!object.params) {
25
+ console.warn(
26
+ '[shared-process] invalid message 1',
27
+ typeof object,
28
+ object.params,
29
+ object
30
+ )
31
+ return
32
+ }
33
+ let result
34
+ try {
35
+ result = requiresSocket(object.method)
36
+ ? await Command.invoke(object.method, state.socket, ...object.params)
37
+ : await Command.invoke(object.method, ...object.params)
38
+ } catch (error) {
39
+ if (error instanceof Error.OperationalError) {
40
+ // console.log({ error })
41
+ const rawCause = error.cause()
42
+ const errorCause = rawCause ? rawCause.message : ''
43
+ const errorMessage = rawCause
44
+ ? error.message.slice(0, error.message.indexOf(errorCause))
45
+ : ''
46
+
47
+ // console.info('expected error', error)
48
+ send({
49
+ jsonrpc: '2.0',
50
+ id: object.id,
51
+ error: {
52
+ code: /* ExpectedError */ -32000,
53
+ message: error.message,
54
+ data: {
55
+ stack: error.originalStack,
56
+ codeFrame: error.originalCodeFrame,
57
+ category: error.category,
58
+ // @ts-ignore
59
+ stderr: error.stderr,
60
+ },
61
+ },
62
+ })
63
+ } else {
64
+ console.error(error)
65
+ // TODO check if socket is active
66
+ send({
67
+ jsonrpc: '2.0',
68
+ id: object.id,
69
+ error: {
70
+ code: /* UnexpectedError */ -32001,
71
+ // @ts-ignore
72
+ message: error.toString(),
73
+ },
74
+ })
75
+ }
76
+ return
77
+ }
78
+ send({
79
+ jsonrpc: '2.0',
80
+ result,
81
+ id: object.id,
82
+ })
83
+ } else {
84
+ if (!object.params) {
85
+ console.warn('invalid message', typeof object, object.params, object)
86
+ return
87
+ }
88
+ Command.execute(object.method, state.socket, ...object.params)
89
+ }
90
+ }
91
+
92
+ const handleError = (error) => {
93
+ console.log('EEERRR')
94
+ // console.error(error)
95
+ throw new VError(error, 'Shared Process Socket Error')
96
+
97
+ // console.error('[Shared Process: Socket Error]', event)
98
+ }
99
+
100
+ // TODO
101
+ const createAbstractSocket = (socket) => {
102
+ return {
103
+ _socket: socket,
104
+ send(message) {
105
+ socket.send(JSON.stringify(message))
106
+ },
107
+ on(event, listener) {
108
+ switch (event) {
109
+ case 'message':
110
+ socket.on('message', listener)
111
+ break
112
+ case 'close':
113
+ socket.on('close', listener)
114
+ break
115
+ default:
116
+ console.warn('socket event not implemented', event, listener)
117
+ break
118
+ }
119
+ },
120
+ }
121
+ }
122
+
123
+ export const set = (socket) => {
124
+ socket.on('close', () => {
125
+ console.log('[shared-process] socket closed')
126
+ })
127
+ socket.onmessage = handleMessage
128
+ // socket.onerror = handleError
129
+ socket.on('error', handleError)
130
+ const abstractSocket = createAbstractSocket(socket)
131
+ state.socket = abstractSocket
132
+ }
@@ -0,0 +1,45 @@
1
+ import * as Exec from '../Exec/Exec.js'
2
+
3
+ // parse ps output based on vscode https://github.com/microsoft/vscode/blob/c0769274fa136b45799edeccc0d0a2f645b75caf/src/vs/base/node/ps.ts (License MIT)
4
+
5
+ const parsePsOutputLine = (line) => {
6
+ const PID_CMD =
7
+ /^\s*([0-9]+)\s+([0-9]+)\s+([0-9]+\.[0-9]+)\s+([0-9]+\.[0-9]+)\s+(.+)$/
8
+ const matches = PID_CMD.exec(line.trim())
9
+ if (matches && matches.length === 6) {
10
+ return {
11
+ pid: parseInt(matches[1]),
12
+ ppid: parseInt(matches[2]),
13
+ cwd: matches[5],
14
+ load: parseInt(matches[3]),
15
+ mem: parseInt(matches[4]),
16
+ }
17
+ }
18
+ }
19
+ const parsePsOutput = (stdout, rootPid) => {
20
+ const lines = stdout.split('\n')
21
+ return lines.map(parsePsOutputLine)
22
+ }
23
+
24
+ const getPsOutput = async (rootPid) => {
25
+ const { stdout } = await Exec.exec(
26
+ 'ps',
27
+ ['-g', `${rootPid}`, '-a', '-o', 'pid=,ppid=,pcpu=,pmem=,command='],
28
+ {
29
+ shell: true,
30
+ }
31
+ )
32
+ return stdout
33
+ }
34
+
35
+ export const listProcessesWithMemoryUsage = async (rootPid) => {
36
+ const stdout = await getPsOutput(rootPid)
37
+ const parsed = parsePsOutput(stdout, rootPid)
38
+ console.log(parsed)
39
+ }
40
+
41
+ const main = async () => {
42
+ await listProcessesWithMemoryUsage(process.pid)
43
+ }
44
+
45
+ main()
@@ -0,0 +1,45 @@
1
+ import * as Command from '../Command/Command.js'
2
+ import * as Terminal from './Terminal.js'
3
+ import * as Assert from '../Assert/Assert.js'
4
+
5
+ const create = (socket, id, cwd) => {
6
+ Assert.object(socket)
7
+ if (!socket) {
8
+ console.log({socket, id, cwd})
9
+ console.warn('socket not available')
10
+ return
11
+ }
12
+ // terminalMap[id] = Terminal.create({
13
+ // handleData(data) {
14
+ // socket.send(
15
+ // JSON.stringify({
16
+ // jsonrpc: '2.0',
17
+ // method: 2133,
18
+ // params: ['Terminal', 'handleData', data],
19
+ // })
20
+ // )
21
+ // },
22
+ // cwd,
23
+ // })
24
+ Terminal.create(socket, id, cwd)
25
+ }
26
+
27
+ const write = ( id, input) => {
28
+ console.log({ id, input})
29
+ Terminal.write(id, input)
30
+ }
31
+
32
+ const resize = (socket, id, columns, rows) => {
33
+ Terminal.resize(id, columns, rows)
34
+ }
35
+
36
+ const dispose = (socket, id) => {
37
+ Terminal.dispose(id)
38
+ }
39
+
40
+ export const __initialize__ = () => {
41
+ Command.register('Terminal.create', create)
42
+ Command.register('Terminal.dispose', dispose)
43
+ Command.register('Terminal.write', write)
44
+ Command.register('Terminal.resize', resize)
45
+ }
@@ -0,0 +1,161 @@
1
+ import { ChildProcess, fork } from 'node:child_process'
2
+ import exitHook from 'exit-hook'
3
+ import * as Debug from '../Debug/Debug.js'
4
+ import * as Path from '../Path/Path.js'
5
+ import * as Root from '../Root/Root.js'
6
+ import * as Assert from '../Assert/Assert.js'
7
+ import { ptyHostPath } from '@lvce-editor/pty-host'
8
+
9
+ export const state = {
10
+ /**
11
+ * @type {ChildProcess|undefined}
12
+ */
13
+ ptyHost: undefined,
14
+ ptyHostState: /* None */ 0,
15
+ /**
16
+ * @type {any[]}
17
+ */
18
+ pendingMessages: [],
19
+ send(message) {
20
+ state.pendingMessages.push(message)
21
+ },
22
+ }
23
+
24
+ // const getSpawnOptions = () => {
25
+ // if (Platform.isWindows) {
26
+ // return {
27
+ // command: 'powershell.exe',
28
+ // args: [],
29
+ // }
30
+ // }
31
+ // return {
32
+ // command: 'bash',
33
+ // args: ['-i'],
34
+ // }
35
+ // }
36
+
37
+ const cleanUpAll = () => {
38
+ if (state.ptyHost) {
39
+ state.ptyHost.kill()
40
+ }
41
+ }
42
+
43
+ const handleProcessExit = () => {
44
+ Debug.debug('shared process exit, clean terminals')
45
+ cleanUpAll()
46
+ }
47
+
48
+ const createPtyHost = () => {
49
+ exitHook(handleProcessExit)
50
+ const ptyHost = fork(ptyHostPath, { stdio: 'inherit' })
51
+ return ptyHost
52
+ }
53
+
54
+ // const send = (method, ...params) => {
55
+
56
+ // state.ptyHost.send({
57
+ // jsonrpc: '2.0',
58
+ // method,
59
+ // params,
60
+ // })
61
+ // }
62
+
63
+ export const create = (socket, id, cwd) => {
64
+ Assert.object(socket)
65
+ Assert.number(id)
66
+ Assert.string(cwd)
67
+
68
+ console.log('create terminal')
69
+ Debug.debug('creating pty host')
70
+ switch (state.ptyHostState) {
71
+ case /* None */ 0: {
72
+ state.ptyHostState = /* Creating */ 1
73
+ const ptyHost = createPtyHost()
74
+ const handleFirstMessage = () => {
75
+ Debug.debug('pty host ready')
76
+ state.ptyHostState = /* Ready */ 2
77
+ const handleMessage = (message) => {
78
+ const data = message.params[1]
79
+ socket.send({
80
+ jsonrpc: '2.0',
81
+ method: 2133,
82
+ params: ['Terminal', 'handleData', data],
83
+ })
84
+ }
85
+ ptyHost.on('message', handleMessage)
86
+ state.send = (message) => {
87
+ ptyHost.send(message)
88
+ }
89
+ while (state.pendingMessages.length > 0) {
90
+ state.send(state.pendingMessages.shift())
91
+ }
92
+ }
93
+ ptyHost.once('message', handleFirstMessage)
94
+ state.ptyHost = ptyHost
95
+ break
96
+ }
97
+ case /* Creating */ 1: {
98
+ break
99
+ }
100
+ case /* Ready */ 2: {
101
+ break
102
+ }
103
+ default:
104
+ break
105
+ }
106
+ state.send({
107
+ jsonrpc: '2.0',
108
+ method: 'Terminal.create',
109
+ params: [id, cwd],
110
+ })
111
+ const handleClose = () => {
112
+ if (state.ptyHost) {
113
+ state.ptyHost.removeAllListeners()
114
+ state.ptyHost.kill()
115
+ state.ptyHost = undefined
116
+ state.ptyHostState = /* None */ 0
117
+ state.send = (message) => {
118
+ state.pendingMessages.push(message)
119
+ }
120
+ }
121
+ }
122
+ socket.on('close', handleClose)
123
+ }
124
+
125
+ export const write = (id, data) => {
126
+ Assert.number(id)
127
+ Assert.string(data)
128
+ if (!state.ptyHost) {
129
+ console.log('[shared-process] pty host not ready')
130
+ return
131
+ }
132
+ state.send({
133
+ jsonrpc: '2.0',
134
+ method: 'Terminal.write',
135
+ params: [id, data],
136
+ })
137
+ }
138
+
139
+ export const resize = (state, columns, rows) => {
140
+ // state.pty.resize(columns, rows)
141
+ }
142
+
143
+ export const dispose = (id) => {
144
+ state.send({
145
+ jsonrpc: '2.0',
146
+ method: 'Terminal.dispose',
147
+ params: [id],
148
+ })
149
+ }
150
+
151
+ export const disposeAll = () => {
152
+ if (state.ptyHost) {
153
+ state.ptyHost.removeAllListeners()
154
+ state.ptyHost.kill()
155
+ state.ptyHost = undefined
156
+ state.ptyHostState = /* None */ 0
157
+ state.send = (message) => {
158
+ state.pendingMessages.push(message)
159
+ }
160
+ }
161
+ }
@@ -0,0 +1,6 @@
1
+ import * as Command from '../Command/Command.js'
2
+ import * as TextDocument from './TextDocument.js'
3
+
4
+ export const __initialize__ = () => {
5
+ Command.register(4820, TextDocument.applyEdit)
6
+ }
@@ -0,0 +1,37 @@
1
+ export const state = {
2
+ textDocuments: Object.create(null),
3
+ /** @type{any[]} */
4
+ listeners: [],
5
+ }
6
+
7
+ // const toOffsetBasedEdit = (textDocument, documentEdit) => {
8
+ // let offset = 0
9
+ // let rowIndex = 0
10
+ // while (rowIndex++ < documentEdit.rowIndex) {
11
+ // offset += textDocument.lines[rowIndex].length
12
+ // }
13
+ // offset += documentEdit.columnIndex
14
+ // return {
15
+ // offset,
16
+ // inserted: documentEdit.inserted,
17
+ // deleted: documentEdit.deleted,
18
+ // }
19
+ // }
20
+
21
+ export const applyEdit = (socket, textDocument, documentEdits) => {
22
+ // TODO avoid extra object allocation and anonymous function
23
+ // const offsetBasedEdits = documentEdits.map((e) =>
24
+ // toOffsetBasedEdit(textDocument, e)
25
+ // )
26
+ for (const listener of state.listeners) {
27
+ listener(textDocument, documentEdits)
28
+ }
29
+ }
30
+
31
+ export const getText = (textDocument) => {
32
+ return textDocument.lines.join('\n')
33
+ }
34
+
35
+ export const onChange = (listener) => {
36
+ state.listeners.push(listener)
37
+ }
@@ -0,0 +1,15 @@
1
+ export const LEVEL = {
2
+ DEFAULT: 1,
3
+ VERBOSE: 2,
4
+ }
5
+
6
+ export const state = {
7
+ level: LEVEL.VERBOSE,
8
+ }
9
+
10
+ export const trace =
11
+ process.env.NODE_ENV === 'test'
12
+ ? () => {}
13
+ : (...args) => {
14
+ console.info('[Trace]', ...args)
15
+ }
@@ -0,0 +1,11 @@
1
+ import { default as _trash } from 'trash'
2
+ import VError from 'verror'
3
+
4
+ export const trash = async (path) => {
5
+ try {
6
+ await _trash(path)
7
+ } catch (error) {
8
+ // @ts-ignore
9
+ throw new VError(error, 'Failed to move item to trash')
10
+ }
11
+ }
@@ -0,0 +1,6 @@
1
+ import * as Command from '../Command/Command.js'
2
+ import * as WebSocketServer from './WebSocketServer.js'
3
+
4
+ export const __initialize__ = () => {
5
+ Command.register(5621, WebSocketServer.handleUpgrade)
6
+ }
@@ -0,0 +1,43 @@
1
+ import { Buffer } from 'node:buffer'
2
+ import * as _ws from 'ws'
3
+ import * as Socket from '../Socket/Socket.js'
4
+
5
+ // workaround for jest or node bug
6
+ const WebSocketServer = _ws.WebSocketServer
7
+ ? _ws.WebSocketServer
8
+ : // @ts-ignore
9
+ _ws.default.WebSocketServer
10
+
11
+ const encode = (commandId, callbackId) => (commandId << 16) | callbackId
12
+ const decodeCommand = (encodedCommand) => encodedCommand >> 16
13
+ const decodeCallback = (encodedCommand) => encodedCommand & 0x0000FFFF
14
+
15
+ // console.log('ws', _ws)
16
+ // console.log('server', ws.WebSocketServer)
17
+
18
+ const webSocketServer = new WebSocketServer({
19
+ noServer: true,
20
+
21
+ // TODO not sure if ws compress is working at all
22
+ // perMessageDeflate: true
23
+ })
24
+
25
+ // TODO should not import command directly -> Websocket server should be independent of business logic
26
+
27
+ const handleSocket = (socket) => {
28
+ Socket.set(socket)
29
+ }
30
+
31
+ webSocketServer.on('connection', handleSocket)
32
+
33
+ export const handleUpgrade = (request, socket) => {
34
+ const upgradeCallback = (ws) => {
35
+ webSocketServer.emit('connection', ws, request)
36
+ }
37
+ webSocketServer.handleUpgrade(
38
+ request,
39
+ socket,
40
+ Buffer.alloc(0),
41
+ upgradeCallback
42
+ )
43
+ }
@@ -0,0 +1,7 @@
1
+ import * as Command from '../Command/Command.js'
2
+ import * as Workspace from './Workspace.js'
3
+
4
+ export const __initialize__ = () => {
5
+ Command.register('Workspace.resolveRoot', Workspace.resolveRoot)
6
+ Command.register('Workspace.getHomeDir', Workspace.getHomeDir)
7
+ }
@@ -0,0 +1,107 @@
1
+ import { createHash } from 'node:crypto'
2
+ import { readFile } from 'node:fs/promises'
3
+ import { homedir } from 'node:os'
4
+ import { join, resolve } from 'node:path'
5
+ import { fileURLToPath, pathToFileURL } from 'node:url'
6
+ import * as Env from '../Env/Env.js'
7
+ import * as Platform from '../Platform/Platform.js'
8
+ import * as Root from '../Root/Root.js'
9
+
10
+ const RE_ABSOLUTE_URI = /^[a-z]+:\/\//
11
+
12
+ const isAbsoluteUri = (path) => {
13
+ return RE_ABSOLUTE_URI.test(path)
14
+ }
15
+
16
+ const getAbsolutePath = (path) => {
17
+ if (isAbsoluteUri(path)) {
18
+ return path
19
+ }
20
+ if (path.startsWith('${cwd}')) {
21
+ path = Root.root + path.slice(5)
22
+ } else if (path.startsWith('~')) {
23
+ path = `${homedir()}${path.slice(1)}`
24
+ }
25
+ path = resolve(path)
26
+ return path
27
+ }
28
+
29
+ const getWorkspaceStorage = async (workspaceId) => {
30
+ try {
31
+ const workspaceStorage = JSON.parse(
32
+ await readFile(`/tmp/config/${workspaceId}`, 'utf-8')
33
+ )
34
+ return workspaceStorage
35
+ } catch {
36
+ return {}
37
+ }
38
+ }
39
+
40
+ /**
41
+ * @deprecated use platform instead
42
+ */
43
+ export const getHomeDir = () => {
44
+ if (Platform.isWindows) {
45
+ return ''
46
+ }
47
+ const homeDir = homedir()
48
+ return homeDir
49
+ }
50
+
51
+ const getWorkspaceId = (absolutePath) => {
52
+ return createHash('sha256').update(absolutePath).digest('hex').slice(0, 16)
53
+ }
54
+
55
+ const configs = {
56
+ messagePort1: {
57
+ folder: '/tmp',
58
+ },
59
+ messagePort2: {
60
+ folder: '~',
61
+ },
62
+ }
63
+
64
+ const toUri = (path) => {
65
+ return pathToFileURL(path).toString()
66
+ }
67
+
68
+ export const resolveRoot = async () => {
69
+ // TODO ask electron, electron can do mapping
70
+ // await
71
+
72
+ // TODO shared process should have no logic, this should probably be somewhere else
73
+ const folder = Env.getFolder()
74
+ if (!folder) {
75
+ const path = join(Root.root, 'playground')
76
+ return {
77
+ path,
78
+ uri: toUri(path),
79
+ workspaceId: getWorkspaceId(path),
80
+ homeDir: Platform.getHomeDir(),
81
+ pathSeparator: Platform.getPathSeparator(),
82
+ source: 'shared-process-env',
83
+ }
84
+ }
85
+ const absolutePath = getAbsolutePath(folder)
86
+ const workspaceId = getWorkspaceId(absolutePath)
87
+ // TODO this slows down startup a lot (~30-50ms)
88
+ // const workspaceStorage = await getWorkspaceStorage(workspaceId)
89
+ return {
90
+ path: absolutePath,
91
+ uri: toUri(absolutePath),
92
+ workspaceId,
93
+ homeDir: Platform.getHomeDir(),
94
+ pathSeparator: Platform.getPathSeparator(),
95
+ source: 'shared-process-default',
96
+ }
97
+ }
98
+
99
+ export const resolveUri = (uri) => {
100
+ const path = fileURLToPath(uri)
101
+ return {
102
+ path,
103
+ uri,
104
+ homeDir: Platform.getHomeDir(),
105
+ pathSeparator: Platform.getPathSeparator(),
106
+ }
107
+ }
@@ -0,0 +1,61 @@
1
+ import * as ParentIpc from './parts/ParentIpc/ParentIpc.js'
2
+ import * as PrettyError from './parts/PrettyError/PrettyError.js'
3
+
4
+ // TODO handle structure: one shared process multiple extension hosts
5
+
6
+ // TODO use named functions here
7
+
8
+ const handleUncaughtExceptionMonitor = (error, origin) => {
9
+ console.info(`[shared process] uncaught exception: ${error.stack}`)
10
+ console.info('code', error.code)
11
+ if (error && error.code === 'EPIPE' && !process.connected) {
12
+ // parent process is disposed, ignore
13
+ return
14
+ }
15
+ if (error && error.code === 'ERR_IPC_CHANNEL_CLOSED' && !process.connected) {
16
+ // parent process is disposed, ignore
17
+ return
18
+ }
19
+ console.log(error)
20
+ const prettyError = PrettyError.prepare(error)
21
+ console.error(prettyError.message)
22
+ console.error(prettyError.codeFrame)
23
+ console.error(prettyError.stack)
24
+ process.exit(1)
25
+ }
26
+
27
+ const handleSigInt = () => {
28
+ console.info('[shared process] sigint')
29
+ }
30
+
31
+ const handleDisconnect = () => {
32
+ console.info('[shared process] disconnected')
33
+ }
34
+
35
+ const handleBeforeExit = () => {
36
+ console.info('[shared process] will exit now')
37
+ }
38
+
39
+ const handleSigTerm = () => {
40
+ console.info('[shared-process] sigterm')
41
+ }
42
+
43
+ const main = () => {
44
+ console.log('[shared process] started')
45
+ process.on('beforeExit', handleBeforeExit)
46
+ process.on('disconnect', handleDisconnect)
47
+ process.on('SIGINT', handleSigInt)
48
+ process.on('SIGTERM', handleSigTerm)
49
+
50
+ process.on('uncaughtExceptionMonitor', handleUncaughtExceptionMonitor)
51
+ ParentIpc.listen()
52
+ // ExtensionHost.start() // TODO start on demand, e.g. not when extensions should be disabled
53
+ }
54
+
55
+ main()
56
+
57
+ // TODO when browser reloads or opens in new tab how does the window know which folder to open?
58
+
59
+ // setTimeout(() => {
60
+ // throw new Error('oops')
61
+ // }, 210)