@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,228 @@
1
+ // TODO lazyload chokidar and trash (but doesn't work currently because of bug with jest)
2
+ import * as fs from 'node:fs/promises'
3
+ import * as os from 'node:os'
4
+ import chokidar from 'chokidar'
5
+ import * as Error from '../Error/Error.js'
6
+ import * as Path from '../Path/Path.js'
7
+ import * as Trash from '../Trash/Trash.js'
8
+ import * as Platform from '../Platform/Platform.js'
9
+
10
+ export const state = {
11
+ watcherMap: Object.create(null),
12
+ }
13
+
14
+ export const copy = async (source, target) => {
15
+ const { copy } = await import('fs-extra')
16
+ try {
17
+ await copy(source, target, { recursive: true })
18
+ } catch (error) {
19
+ throw new Error.OperationalError({
20
+ cause: error,
21
+ code: 'E_FILE_SYSTEM_ERROR',
22
+ message: `Failed to copy "${source}" to "${target}"`,
23
+ category: 'File System Error',
24
+ })
25
+ }
26
+ }
27
+
28
+ export const readFile = async (path) => {
29
+ try {
30
+ const content = await fs.readFile(path, 'utf-8')
31
+ return content
32
+ } catch (error) {
33
+ throw new Error.OperationalError({
34
+ cause: error,
35
+ code: 'E_FILE_SYSTEM_ERROR',
36
+ message: `Failed to read file "${path}"`,
37
+ category: 'File System Error',
38
+ })
39
+ }
40
+ }
41
+
42
+ export const writeFile = async (path, content) => {
43
+ try {
44
+ // queue would be more correct for concurrent writes but also slower
45
+ // Queue.add(`writeFile/${path}`, () =>
46
+ await fs.writeFile(path, content)
47
+ } catch (error) {
48
+ throw new Error.OperationalError({
49
+ cause: error,
50
+ code: 'E_FILE_SYSTEM_ERROR',
51
+ message: `Failed to write to file "${path}"`,
52
+ category: 'File System Error',
53
+ })
54
+ }
55
+ }
56
+
57
+ export const ensureFile = async (path, content) => {
58
+ try {
59
+ await fs.mkdir(Path.dirname(path), { recursive: true })
60
+ await fs.writeFile(path, content)
61
+ } catch (error) {
62
+ throw new Error.OperationalError({
63
+ cause: error,
64
+ code: 'E_FILE_SYSTEM_ERROR',
65
+ message: `Failed to write to file "${path}"`,
66
+ category: 'File System Error',
67
+ })
68
+ }
69
+ }
70
+
71
+ export const createFile = async (path) => {
72
+ try {
73
+ await fs.writeFile(path, '', { flag: 'wx' })
74
+ } catch (error) {
75
+ throw new Error.OperationalError({
76
+ cause: error,
77
+ code: 'E_FILE_SYSTEM_ERROR',
78
+ message: `Failed to create file "${path}"`,
79
+ category: 'File System Error',
80
+ })
81
+ }
82
+ }
83
+
84
+ export const createFolder = async (path, options) => {
85
+ try {
86
+ await fs.mkdir(path, options)
87
+ } catch (error) {
88
+ throw new Error.OperationalError({
89
+ cause: error,
90
+ code: 'E_FILE_SYSTEM_ERROR',
91
+ message: `Failed to create folder "${path}"`,
92
+ category: 'File System Error',
93
+ })
94
+ }
95
+ }
96
+
97
+ const isOkayToRemove = (path) => {
98
+ if (path === '/') {
99
+ return false
100
+ }
101
+ if (path === '~') {
102
+ return false
103
+ }
104
+ if (path === os.homedir()) {
105
+ return false
106
+ }
107
+ return true
108
+ }
109
+
110
+ export const remove = async (path) => {
111
+ if (!isOkayToRemove(path)) {
112
+ console.warn('not removing path')
113
+ return
114
+ }
115
+
116
+ // TODO lazyload trash (doesn't work currently because of bug with jest)
117
+ // const { trash } = await import('../../wrap/trash.js')
118
+ try {
119
+ await Trash.trash(path)
120
+ } catch (error) {
121
+ throw new Error.OperationalError({
122
+ cause: error,
123
+ code: 'E_FILE_REMOVE_SYSTEM_ERROR',
124
+ message: `Failed to remove "${path}"`,
125
+ category: 'File System Error',
126
+ })
127
+ }
128
+ }
129
+
130
+ export const exists = async (path) => {
131
+ try {
132
+ await fs.access(path)
133
+ return true
134
+ } catch {
135
+ return false
136
+ }
137
+ }
138
+
139
+ const getType = (dirent) => {
140
+ if (dirent.isFile()) {
141
+ return 'file'
142
+ }
143
+ if (dirent.isDirectory()) {
144
+ return 'directory'
145
+ }
146
+ return 'unknown'
147
+ }
148
+
149
+ const toPrettyDirent = (dirent) => {
150
+ return {
151
+ name: dirent.name,
152
+ type: getType(dirent),
153
+ }
154
+ }
155
+
156
+ export const readDirWithFileTypes = async (path) => {
157
+ try {
158
+ const dirents = await fs.readdir(path, { withFileTypes: true })
159
+ const prettyDirents = dirents.map(toPrettyDirent)
160
+ return prettyDirents
161
+ } catch (error) {
162
+ throw new Error.OperationalError({
163
+ cause: error,
164
+ code: 'E_FILE_READ_DIR_SYSTEM_ERROR',
165
+ message: `Failed to read directory "${path}"`,
166
+ category: 'File System Error',
167
+ })
168
+ }
169
+ }
170
+
171
+ export const mkdir = async (path) => {
172
+ try {
173
+ await fs.mkdir(path)
174
+ } catch (error) {
175
+ throw new Error.OperationalError({
176
+ cause: error,
177
+ code: 'E_CREATE_DIRECTORY_FAILED',
178
+ message: `Failed to create directory "${path}"`,
179
+ category: 'File System Error',
180
+ })
181
+ }
182
+ }
183
+
184
+ export const rename = async (oldPath, newPath) => {
185
+ try {
186
+ await fs.rename(oldPath, newPath)
187
+ } catch (error) {
188
+ throw new Error.OperationalError({
189
+ cause: error,
190
+ code: 'E_RENAME_FAILED',
191
+ message: `Failed to rename "${oldPath}" to "${newPath}"`,
192
+ category: 'File System Error',
193
+ })
194
+ }
195
+ }
196
+
197
+ export const watch = (path, options) => {
198
+ // let state = 'loading'
199
+
200
+ const watcher = chokidar.watch(`${path}`, {
201
+ ignoreInitial: true,
202
+ })
203
+
204
+ if (options.onAll) {
205
+ watcher.on('all', options.onAll)
206
+ }
207
+ return watcher
208
+
209
+ // const { default: chokidar } = await import('chokidar')
210
+ }
211
+
212
+ export const getPathSeparator = () => {
213
+ return Platform.getPathSeparator()
214
+ }
215
+
216
+ // export const unwatch = (id) => {
217
+ // state.watchers[id].close()
218
+ // delete state.watchers[id]
219
+ // }
220
+
221
+ // export const unwatchAll = () => {
222
+ // for (const key in state.watchers) {
223
+ // unwatch(key)
224
+ // }
225
+ // }
226
+
227
+ // TODO ui should show useful error message when file cannot be saved
228
+ // and for permission denied error it should use that npm module that allows root
@@ -0,0 +1,58 @@
1
+ export const id = 'git'
2
+
3
+ export const label = 'Git'
4
+
5
+ export const acceptInputCommand = 'git.commit'
6
+
7
+ const RE_ONLY_WHITESPACE = /^\s+$/
8
+
9
+ export const validateInput = (text) => {
10
+ if (RE_ONLY_WHITESPACE.test(text)) {
11
+ return {
12
+ message: 'Current commit message only contains whitespace characters',
13
+ type: 'warning',
14
+ }
15
+ }
16
+ return undefined
17
+ }
18
+
19
+ const vscode = {}
20
+
21
+ export const activate = () => {
22
+ const sourceControlProvider = vscode.createSourceControlProvider({
23
+ providerId: 'git',
24
+ label: 'Git',
25
+ acceptInput(text) {},
26
+ validateInput,
27
+ })
28
+
29
+ const mergeGroup = vscode.createSourceControlResourceGroup({
30
+ providerId: 'git',
31
+ groupId: 'merge',
32
+ label: 'Merge Changes',
33
+ })
34
+
35
+ const indexGroup = vscode.createSourceControlResourceGroup({
36
+ providerId: 'git',
37
+ groupId: 'index',
38
+ label: 'Staged Changes',
39
+ })
40
+
41
+ const workingTreeGroup = vscode.createSourceControlResourceGroup({
42
+ providerId: 'git',
43
+ groupId: 'workingTree',
44
+ label: 'Untracked Changes',
45
+ })
46
+
47
+ const untrackedGroup = vscode.createSourceControlResourceGroup({
48
+ providerId: 'git',
49
+ groupId: 'untracked',
50
+ label: 'Untracked Changes',
51
+ })
52
+
53
+ sourceControlProvider.setCount(111)
54
+ }
55
+
56
+ export const deactivate = () => {
57
+ vscode.unregisterSourceControlProvider('git')
58
+ }
@@ -0,0 +1,67 @@
1
+ import fallback from 'json-parse-even-better-errors'
2
+ import { codeFrameColumns } from '@babel/code-frame'
3
+ import { LinesAndColumns } from 'lines-and-columns'
4
+
5
+ // parsing error handling based on https://github.com/sindresorhus/parse-json/blob/main/index.js
6
+
7
+ export const parse = async (string, filePath) => {
8
+ try {
9
+ try {
10
+ return JSON.parse(string)
11
+ } catch (error) {
12
+ fallback(string)
13
+ throw error
14
+ }
15
+ } catch (error) {
16
+ error.message = error.message.replace(/\n/g, '')
17
+ const indexMatch = error.message.match(
18
+ /in JSON at position (\d+) while parsing/
19
+ )
20
+ let topMessage = error.message
21
+ let bottomMessage = error.message
22
+ if (topMessage.startsWith('Unexpected token')) {
23
+ topMessage = 'JSON parsing error:'
24
+ }
25
+ if (bottomMessage.includes('while parsing "{')) {
26
+ bottomMessage = bottomMessage.slice(
27
+ 0,
28
+ bottomMessage.indexOf('while parsing "{')
29
+ )
30
+ }
31
+ if (bottomMessage.includes(' (0x7D) ')) {
32
+ bottomMessage = bottomMessage.replace(' (0x7D) ', ' ')
33
+ }
34
+ bottomMessage = bottomMessage.trim()
35
+ const jsonError = new Error(topMessage)
36
+ if (indexMatch && indexMatch.length > 0) {
37
+ const lines = new LinesAndColumns(string)
38
+ const index = Number(indexMatch[1])
39
+ const location = lines.locationForIndex(index)
40
+ const codeFrame = codeFrameColumns(
41
+ string,
42
+ { start: { line: location.line + 1, column: location.column + 1 } },
43
+ { highlightCode: true }
44
+ )
45
+ jsonError.codeFrame = codeFrame
46
+ }
47
+ // console.log({ bottomMessage, message: error.message })
48
+ jsonError.stack = `${bottomMessage}\n at ${filePath}`
49
+ throw jsonError
50
+ }
51
+
52
+ // try {
53
+ // return JSON.parse(json)
54
+ // } catch (error) {
55
+ // const parseJsonBetterErrors = await import('parse-json')
56
+ // try {
57
+ // return parseJsonBetterErrors.default(json, filePath)
58
+ // } catch (error) {
59
+ // console.log({ frame: error.codeFrame })
60
+ // throw error
61
+ // }
62
+ // }
63
+ }
64
+
65
+ export const stringify = (value) => {
66
+ return JSON.stringify(value, null, 2) + '\n'
67
+ }
@@ -0,0 +1,14 @@
1
+ import { mkdir, readFile, writeFile } from 'node:fs/promises'
2
+ import * as Path from '../Path/Path.js'
3
+ import * as Json from '../Json/Json.js'
4
+
5
+ export const readJson = async (absolutePath) => {
6
+ const content = await readFile(absolutePath, 'utf-8')
7
+ const json = await Json.parse(content, absolutePath)
8
+ return json
9
+ }
10
+
11
+ export const writeJson = async (absolutePath, value) => {
12
+ await mkdir(Path.dirname(absolutePath), { recursive: true })
13
+ await writeFile(absolutePath, Json.stringify(value))
14
+ }
@@ -0,0 +1,22 @@
1
+ import * as Callback from '../Callback/Callback.js'
2
+
3
+ export const send = (transport, method, ...params) => {
4
+ transport.send({
5
+ jsonrpc: '2.0',
6
+ method,
7
+ params,
8
+ })
9
+ }
10
+
11
+ export const invoke = (transport, method, ...params) => {
12
+ return new Promise((resolve, reject) => {
13
+ // TODO use one map instead of two
14
+ const callbackId = Callback.register(resolve, reject)
15
+ transport.send({
16
+ jsonrpc: '2.0',
17
+ method,
18
+ params,
19
+ id: callbackId,
20
+ })
21
+ })
22
+ }
@@ -0,0 +1,6 @@
1
+ import * as Command from '../Command/Command.js'
2
+ import * as Native from './Native.js'
3
+
4
+ export const __initialize__ = () => {
5
+ Command.register(221, Native.openFolder)
6
+ }
@@ -0,0 +1,14 @@
1
+ import open from 'open'
2
+ import * as Error from '../Error/Error.js'
3
+
4
+ export const openFolder = async (path) => {
5
+ try {
6
+ await open(path)
7
+ } catch (error) {
8
+ throw new Error.OperationalError({
9
+ cause: error,
10
+ code: 'E_OPEN_SYSTEM_ERROR',
11
+ message: `Failed to open ${path}`,
12
+ })
13
+ }
14
+ }
@@ -0,0 +1,51 @@
1
+ import * as Assert from '../Assert/Assert.js'
2
+ import * as Command from '../Command/Command.js'
3
+ import * as OutputChannel from './OutputChannel.js'
4
+
5
+ export const state = {
6
+ outputChannels: Object.create(null)
7
+ }
8
+
9
+ const open = (socket, id, path) => {
10
+ console.log({path})
11
+ Assert.object(socket)
12
+ // Assert.string(id)
13
+ Assert.string(path )
14
+ if (!socket) {
15
+ console.warn('socket not available')
16
+ return
17
+ }
18
+ if (state.outputChannels[id]) {
19
+ OutputChannel.dispose(state.outputChannels[id])
20
+ }
21
+ const onData = (data) => {
22
+ console.log('send data', data)
23
+ socket.send(
24
+ {
25
+ jsonrpc: '2.0',
26
+ method: 2133,
27
+ params: ['Output', 'handleData', data],
28
+ }
29
+ )
30
+ }
31
+ const onError = (error) => {
32
+ console.info(`[shared process] output channel error: ${error}`)
33
+ socket.send(
34
+ {
35
+ jsonrpc: '2.0',
36
+ method: 2133,
37
+ params: ['Output', 'handleError', error],
38
+ })
39
+ }
40
+ state.outputChannels[id] = OutputChannel.open(path, onData, onError)
41
+ }
42
+
43
+ const close = (socket, id) => {
44
+ OutputChannel.dispose(state.outputChannels[id])
45
+ delete state.outputChannels[id]
46
+ }
47
+
48
+ export const __initialize__ = () => {
49
+ Command.register('OutputChannel.open', open)
50
+ Command.register('OutputChannel.close', close)
51
+ }
@@ -0,0 +1,37 @@
1
+ import { Tail } from 'tail'
2
+
3
+ // TODO
4
+ // option 1: use tail child process
5
+ // option 2: https://www.npmjs.com/package/@logdna/tail-file
6
+ // option 3: node tail
7
+
8
+ export const open = (path, onData, onError) => {
9
+ let tail
10
+ try {
11
+ tail = new Tail(path, {
12
+ separator: null,
13
+ fromBeginning: true,
14
+ })
15
+ } catch (error) {
16
+ // @ts-ignore
17
+ onError(error.toString())
18
+ }
19
+
20
+ if (tail) {
21
+ tail.on('line', onData)
22
+
23
+ tail.on('error', (error) => {
24
+ onError(error.toString())
25
+ })
26
+ }
27
+ const state = {
28
+ tail,
29
+ }
30
+ return state
31
+ }
32
+
33
+ export const dispose = (state) => {
34
+ if (state.tail) {
35
+ state.tail.unwatch()
36
+ }
37
+ }
@@ -0,0 +1,203 @@
1
+ import { MessagePort, parentPort } from 'node:worker_threads'
2
+ import * as Callback from '../Callback/Callback.js'
3
+ import * as Command from '../Command/Command.js'
4
+ import * as Debug from '../Debug/Debug.js'
5
+ import * as Error from '../Error/Error.js'
6
+ import { requiresSocket } from '../RequiresSocket/RequiresSocket.js'
7
+
8
+ // TODO add tests for this
9
+
10
+ // TODO handle structure: one shared process multiple extension hosts
11
+
12
+ // TODO pass socket / port handle also in electron
13
+
14
+ export const state = {
15
+ electronPortMap: new Map(),
16
+ }
17
+
18
+ export const electronSend = (message) => {
19
+ // @ts-ignore
20
+ // process.send(message)
21
+ parentPort.postMessage(message)
22
+ }
23
+
24
+ const handleMessageFromParentProcess = async (message, handle) => {
25
+ if (handle) {
26
+ handle.on('error', (error) => {
27
+ if (error && error.code === 'ECONNRESET') {
28
+ return
29
+ }
30
+ console.info('[info shared process: handle error]', error)
31
+ })
32
+ Command.execute(
33
+ /* WebSocketServer.handleUpgrade */ 5621,
34
+ /* message */ message,
35
+ /* handle */ handle
36
+ )
37
+ } else if (message.result) {
38
+ Callback.resolve(message.id, message.result)
39
+ } else if (message.method) {
40
+ console.log({ message })
41
+ if (message.id) {
42
+ try {
43
+ console.log('INISDE HERE')
44
+ const result = requiresSocket(message.method)
45
+ ? await Command.invoke(message.method, handle, ...message.params)
46
+ : await Command.invoke(message.method, ...message.params)
47
+ electronSend({
48
+ jsonrpc: '2.0',
49
+ id: message.id,
50
+ result: result ?? null,
51
+ })
52
+ } catch (error) {
53
+ console.error('[shared process] command failed to execute')
54
+ console.error(error)
55
+ electronSend({
56
+ jsonrpc: '2.0',
57
+ code: /* ExpectedError */ -32000,
58
+ id: message.id,
59
+ error: 'ExpectedError',
60
+ // @ts-ignore
61
+ data: error.toString(),
62
+ })
63
+ }
64
+ } else {
65
+ // TODO handle error
66
+ Command.execute(message.method, null, ...message.params)
67
+ }
68
+ } else {
69
+ console.warn('unknown message', message)
70
+ }
71
+ }
72
+
73
+ /**
74
+ *
75
+ * @param {{initialize:{port: MessagePort, folder:string}}} initializeMessage
76
+ */
77
+ const electronInitialize = (initializeMessage) => {
78
+ const port = initializeMessage.initialize.port
79
+ // TODO handle error
80
+ const fakeSocket = {
81
+ send: port.postMessage.bind(port),
82
+ on(event, listener) {
83
+ switch (event) {
84
+ case 'close':
85
+ port.on('close', listener)
86
+ break
87
+ case 'message':
88
+ port.on('message', listener)
89
+ break
90
+ default:
91
+ console.warn('socket event not implemented', event, listener)
92
+ break
93
+ }
94
+ },
95
+ }
96
+ const handleOtherMessagesFromMessagePort = async (message) => {
97
+ // console.log('got port message', message)
98
+ if (message.result) {
99
+ Callback.resolve(message.id, message.result)
100
+ } else if (message.method) {
101
+ if (message.id) {
102
+ try {
103
+ const result = requiresSocket(message.method)
104
+ ? await Command.invoke(
105
+ message.method,
106
+ fakeSocket,
107
+ ...message.params
108
+ )
109
+ : await Command.invoke(message.method, ...message.params)
110
+
111
+ port.postMessage({
112
+ jsonrpc: '2.0',
113
+ id: message.id,
114
+ result: result ?? null,
115
+ })
116
+ } catch (error) {
117
+ // TODO duplicate code with Socket.js, clean this up
118
+ if (error instanceof Error.OperationalError) {
119
+ // console.log({ error })
120
+ const rawCause = error.cause()
121
+ const errorCause = rawCause ? rawCause.message : ''
122
+ const errorMessage = rawCause
123
+ ? error.message.slice(0, error.message.indexOf(errorCause))
124
+ : ''
125
+
126
+ // console.info('expected error', error)
127
+ port.postMessage({
128
+ jsonrpc: '2.0',
129
+ id: message.id,
130
+ error: {
131
+ code: /* ExpectedError */ -32000,
132
+ message: error.message,
133
+ data: {
134
+ stack: error.originalStack,
135
+ codeFrame: error.originalCodeFrame,
136
+ category: error.category,
137
+ // @ts-ignore
138
+ stderr: error.stderr,
139
+ },
140
+ },
141
+ })
142
+ } else {
143
+ console.error('[shared process] command failed to execute')
144
+ console.error(error)
145
+ // TODO check if socket is active
146
+ port.postMessage({
147
+ jsonrpc: '2.0',
148
+ id: message.id,
149
+ error: {
150
+ code: /* UnexpectedError */ -32001,
151
+ // @ts-ignore
152
+ message: error.toString(),
153
+ },
154
+ })
155
+ }
156
+ return
157
+ }
158
+ } else {
159
+ Command.execute(message.method, fakeSocket, ...message.params)
160
+ }
161
+ } else {
162
+ console.warn('unknown message', message)
163
+ }
164
+ }
165
+ port.on('message', handleOtherMessagesFromMessagePort)
166
+ }
167
+
168
+ const handleMessageFromParentProcessElectron = async (message) => {
169
+ if (message.initialize) {
170
+ electronInitialize(message)
171
+ return
172
+ }
173
+ console.log({ message })
174
+ if ('result' in message) {
175
+ Callback.resolve(message.id, message.result)
176
+ return
177
+ }
178
+
179
+ console.log('unknown message from electron', message)
180
+ console.log({ message })
181
+ }
182
+
183
+ // TODO maybe rename to hydrate
184
+ export const listen = () => {
185
+ // TODO tree-shake out if-else
186
+ // console.log({ ...process.env })
187
+ if (process.env.ELECTRON_RUN_AS_NODE && parentPort) {
188
+ // electron process listens to main process ipc
189
+ Debug.debug('is electron')
190
+ // @ts-ignore
191
+ parentPort.on('message', handleMessageFromParentProcessElectron)
192
+ electronSend('ready')
193
+ } else {
194
+ Debug.debug('is not electron')
195
+ // otherwise listen to web process ipc
196
+ // and when a socket is transferred,
197
+ // listen to socket messages also
198
+ process.on('message', handleMessageFromParentProcess)
199
+ if (process.send) {
200
+ process.send('ready')
201
+ }
202
+ }
203
+ }