service-sync.mjs 1.9 KB

12345678910111213141516171819202122232425262728293031
  1. import { spawn } from 'node:child_process'
  2. import { join } from 'node:path'
  3. /** A native helper owns the update until completion, even if the window closes.
  4. * The renderer never receives its configuration or a process-control bridge. */
  5. export const synchronizeLocalService = options => serviceCommand({...options,action:'sync'})
  6. export const stopLocalService = options => serviceCommand({...options,action:'stop'})
  7. function serviceCommand({ home, port, root, fixture, environment, onProgress = () => {}, action }) {
  8. return new Promise((done, reject) => {
  9. const runtimeRoot = join(root, 'web-service')
  10. const helper = spawn(join(runtimeRoot, 'node'), [join(runtimeRoot, 'bin', 'desktop-service-sync.mjs')], {
  11. cwd: runtimeRoot, detached: true, stdio: ['pipe', 'pipe', 'ignore'],
  12. env: { HOME: process.env.HOME, PATH: process.env.PATH || '/usr/bin:/bin:/usr/sbin:/sbin', NODE_USE_ENV_PROXY: '0' },
  13. })
  14. let buffer = '', result, failure, failureCode
  15. helper.stdout.on('data', bytes => {
  16. buffer += bytes
  17. if (buffer.length > 256 * 1024) { failure = '同步进程输出异常'; return }
  18. for (;;) {
  19. const index = buffer.indexOf('\n'); if (index < 0) break
  20. const line = buffer.slice(0, index); buffer = buffer.slice(index + 1)
  21. try { const value = JSON.parse(line); if (value.message) onProgress(value.message); if (value.result) result = value.result; if (value.error) { failure = value.error; failureCode = value.code } }
  22. catch { failure = '同步进程响应无效' }
  23. }
  24. })
  25. helper.once('error', reject)
  26. helper.once('exit', code => code === 0 && result ? done(result) : reject(Object.assign(new Error(failure || (action==='stop'?'后台服务停止未完成':'本机 Web 同步未完成,请重试')), { code: failureCode })))
  27. helper.stdin.on('error', () => {})
  28. helper.stdin.end(JSON.stringify({ home, port, runtimeRoot, fixture, environment, action }))
  29. })
  30. }