pear-electron 0.2.4 → 0.2.6
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.
- package/README.md +6 -5
- package/boot.js +4 -6
- package/electron-main.js +1 -0
- package/gui/gui.js +7 -5
- package/package.json +1 -1
- package/preload.js +72 -16
- package/runtime.js +23 -14
package/README.md
CHANGED
|
@@ -19,12 +19,11 @@ import Runtime from 'pear-electron'
|
|
|
19
19
|
import Bridge from 'pear-bridge'
|
|
20
20
|
|
|
21
21
|
const runtime = new Runtime()
|
|
22
|
-
await runtime.ready()
|
|
23
22
|
|
|
24
23
|
const bridge = new Bridge()
|
|
25
24
|
await bridge.ready()
|
|
26
25
|
|
|
27
|
-
const pipe = runtime.start(bridge
|
|
26
|
+
const pipe = runtime.start({ bridge })
|
|
28
27
|
Pear.teardown(() => pipe.end())
|
|
29
28
|
```
|
|
30
29
|
|
|
@@ -40,11 +39,13 @@ Create the runtime instances with `new Runtime()`.
|
|
|
40
39
|
|
|
41
40
|
Prepare the runtime, runtime binaries for the runtime version may be bootstrapped peer-to-peer at this point. This only runs once per version and any prior bootstraps can be reused for subsequent versions where state hasn't changed. In a production scenario any bootstrapping would be performed in advance by the application distributable.
|
|
42
41
|
|
|
43
|
-
### `runtime.start(
|
|
42
|
+
### `runtime.start(opts)`
|
|
44
43
|
|
|
45
|
-
Opens the UI.
|
|
44
|
+
Opens the UI.
|
|
46
45
|
|
|
47
|
-
|
|
46
|
+
#### Options
|
|
47
|
+
|
|
48
|
+
* `bridge` - An instance of `pear-bridge`.
|
|
48
49
|
|
|
49
50
|
## User-Interface API
|
|
50
51
|
|
package/boot.js
CHANGED
|
@@ -3,14 +3,12 @@
|
|
|
3
3
|
const { isElectron, isElectronRenderer, isElectronWorker, isWindows } = require('which-runtime')
|
|
4
4
|
const BOOT_ELECTRON_MAIN = 1
|
|
5
5
|
const BOOT_ELECTRON_PRELOAD = 2
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
const state =
|
|
9
|
-
const RUNTIME_INFO = rti ? JSON.parse(rti) : state.runtimeInfo
|
|
6
|
+
const rtiFlagIx = process.argv.indexOf('--rti')
|
|
7
|
+
const RTI = rtiFlagIx > -1 && process.argv[rtiFlagIx + 1]
|
|
8
|
+
const state = RTI ? null : JSON.parse(process.argv.slice(isWindows ? -2 : -1)[0])
|
|
10
9
|
|
|
11
10
|
class API {
|
|
12
|
-
static
|
|
13
|
-
static MOUNT = RUNTIME_INFO.mount
|
|
11
|
+
static RTI = RTI ? JSON.parse(RTI) : state.rti
|
|
14
12
|
static get CONSTANTS () { return require('pear-api/constants') }
|
|
15
13
|
config = {}
|
|
16
14
|
}
|
package/electron-main.js
CHANGED
package/gui/gui.js
CHANGED
|
@@ -806,8 +806,8 @@ class GuiCtrl {
|
|
|
806
806
|
this.parentId = parentId
|
|
807
807
|
this.closed = true
|
|
808
808
|
this.id = null
|
|
809
|
-
this.
|
|
810
|
-
this.bridge = this.
|
|
809
|
+
this.rti = this.state.rti
|
|
810
|
+
this.bridge = this.rti?.bridge ?? null
|
|
811
811
|
this.entry = this.bridge === null ? entry : `${this.bridge}${entry}`
|
|
812
812
|
this.sessname = sessname
|
|
813
813
|
this.appkin = appkin
|
|
@@ -1020,7 +1020,7 @@ class Window extends GuiCtrl {
|
|
|
1020
1020
|
preload: require.main.filename,
|
|
1021
1021
|
...(decal === false ? { session } : {}),
|
|
1022
1022
|
partition: 'persist:pear',
|
|
1023
|
-
additionalArguments: [JSON.stringify({ ...this.state.config,
|
|
1023
|
+
additionalArguments: [JSON.stringify({ ...this.state.config, rti: this.rti, isDecal: true })],
|
|
1024
1024
|
autoHideMenuBar: true,
|
|
1025
1025
|
experimentalFeatures: true,
|
|
1026
1026
|
nodeIntegration: true,
|
|
@@ -1119,7 +1119,7 @@ class Window extends GuiCtrl {
|
|
|
1119
1119
|
webPreferences: {
|
|
1120
1120
|
preload: require.main.filename,
|
|
1121
1121
|
session,
|
|
1122
|
-
additionalArguments: [JSON.stringify({ ...this.state.config,
|
|
1122
|
+
additionalArguments: [JSON.stringify({ ...this.state.config, rti: this.rti, parentWcId: this.win.webContents.id, decalled: true })],
|
|
1123
1123
|
autoHideMenuBar: true,
|
|
1124
1124
|
experimentalFeatures: true,
|
|
1125
1125
|
nodeIntegration: true,
|
|
@@ -1134,6 +1134,7 @@ class Window extends GuiCtrl {
|
|
|
1134
1134
|
|
|
1135
1135
|
if (options.afterNativeViewCreated) options.afterNativeViewCreated(this)
|
|
1136
1136
|
this.view.setBounds({ x: 0, y: 0, width, height })
|
|
1137
|
+
|
|
1137
1138
|
const viewLoading = this.view.webContents.loadURL(this.entry)
|
|
1138
1139
|
viewInitialized()
|
|
1139
1140
|
this.view.webContents.once('did-finish-load', () => { viewLoaded() })
|
|
@@ -1322,7 +1323,7 @@ class View extends GuiCtrl {
|
|
|
1322
1323
|
webPreferences: {
|
|
1323
1324
|
preload: require.main.filename,
|
|
1324
1325
|
session,
|
|
1325
|
-
additionalArguments: [JSON.stringify({ ...this.state.config, ...(options?.view?.config || options.config || {}),
|
|
1326
|
+
additionalArguments: [JSON.stringify({ ...this.state.config, ...(options?.view?.config || options.config || {}), rti: this.rti, parentWcId: this.win.webContents.id })],
|
|
1326
1327
|
autoHideMenuBar: true,
|
|
1327
1328
|
experimentalFeatures: true,
|
|
1328
1329
|
nodeIntegration: true,
|
|
@@ -1574,6 +1575,7 @@ class PearGUI extends ReadyResource {
|
|
|
1574
1575
|
}
|
|
1575
1576
|
|
|
1576
1577
|
static async ctrl (type, entry, { state, parentId = 0, ua, sessname = null, appkin }, options = {}, openOptions = {}) {
|
|
1578
|
+
entry = entry || '/'
|
|
1577
1579
|
;[entry] = entry.split('+')
|
|
1578
1580
|
if (entry.slice(0, 2) === './') entry = entry.slice(1)
|
|
1579
1581
|
if (entry[0] !== '/') entry = `/~${entry}`
|
package/package.json
CHANGED
package/preload.js
CHANGED
|
@@ -9,7 +9,7 @@ module.exports = (state) => {
|
|
|
9
9
|
const { isMac, isWindows, platform } = require('which-runtime')
|
|
10
10
|
const API = require('pear-api')
|
|
11
11
|
const GUI = require('./gui')
|
|
12
|
-
const { parentWcId, env, id,
|
|
12
|
+
const { parentWcId, env, id, rti, ...config } = state
|
|
13
13
|
const isDecal = state.isDecal || false
|
|
14
14
|
if (config.key?.type === 'Buffer') config.key = Buffer.from(config.key.data)
|
|
15
15
|
const dir = config.dir
|
|
@@ -43,6 +43,76 @@ module.exports = (state) => {
|
|
|
43
43
|
})
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
const { warn } = console
|
|
47
|
+
console.warn = (msg, ...args) => {
|
|
48
|
+
// if (/Insecure Content-Security-Policy/.test(msg)) return.
|
|
49
|
+
warn.call(console, msg, ...args)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (location.hostname === 'localhost' && location.port) {
|
|
53
|
+
const gunk = require('pear-api/gunk')
|
|
54
|
+
const runtime = require('script-linker/runtime')
|
|
55
|
+
|
|
56
|
+
// platform runtime:
|
|
57
|
+
const pltsl = runtime({
|
|
58
|
+
builtins: gunk.builtins,
|
|
59
|
+
map: gunk.platform.map,
|
|
60
|
+
mapImport: gunk.platform.mapImport,
|
|
61
|
+
symbol: gunk.platform.symbol,
|
|
62
|
+
protocol: gunk.platform.protocol,
|
|
63
|
+
getSync (url) {
|
|
64
|
+
const xhr = new XMLHttpRequest()
|
|
65
|
+
xhr.open('GET', url, false)
|
|
66
|
+
xhr.send(null)
|
|
67
|
+
return xhr.responseText
|
|
68
|
+
},
|
|
69
|
+
resolveSync (req, dirname, { isImport }) {
|
|
70
|
+
const xhr = new XMLHttpRequest()
|
|
71
|
+
const type = isImport ? 'esm' : 'cjs'
|
|
72
|
+
const url = `${dirname}/~${req}+platform-resolve+${type}`
|
|
73
|
+
xhr.open('GET', url, false)
|
|
74
|
+
xhr.send(null)
|
|
75
|
+
return xhr.responseText
|
|
76
|
+
}
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
// app runtime:
|
|
80
|
+
const appsl = runtime({
|
|
81
|
+
builtins: gunk.builtins,
|
|
82
|
+
map: gunk.app.map,
|
|
83
|
+
mapImport: gunk.app.mapImport,
|
|
84
|
+
symbol: gunk.app.symbol,
|
|
85
|
+
protocol: gunk.app.protocol,
|
|
86
|
+
getSync (url) {
|
|
87
|
+
const xhr = new XMLHttpRequest()
|
|
88
|
+
xhr.open('GET', url, false)
|
|
89
|
+
xhr.send(null)
|
|
90
|
+
return xhr.responseText
|
|
91
|
+
},
|
|
92
|
+
resolveSync (req, dirname, { isImport }) {
|
|
93
|
+
const xhr = new XMLHttpRequest()
|
|
94
|
+
const type = isImport ? 'esm' : 'cjs'
|
|
95
|
+
const url = `${dirname}/~${req}+resolve+${type}`
|
|
96
|
+
xhr.open('GET', url, false)
|
|
97
|
+
xhr.send(null)
|
|
98
|
+
if (xhr.status !== 200) throw new Error(`${xhr.status} ${xhr.responseText}`)
|
|
99
|
+
return xhr.responseText
|
|
100
|
+
}
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
async function warm () {
|
|
104
|
+
for await (const { batch, protocol } of Pear[Pear.constructor.UI].warming()) {
|
|
105
|
+
let sl = null
|
|
106
|
+
if (protocol === 'pear' || protocol === 'holepunch') sl = pltsl
|
|
107
|
+
if (protocol === 'app') sl = appsl
|
|
108
|
+
if (sl === null) continue
|
|
109
|
+
for (const { filename, source } of batch) sl.sources.set(filename, source)
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (Pear.config.isDecal === false) warm().catch(console.error)
|
|
114
|
+
}
|
|
115
|
+
|
|
46
116
|
function descopeGlobals () {
|
|
47
117
|
delete global.require
|
|
48
118
|
delete global.module
|
|
@@ -50,21 +120,7 @@ module.exports = (state) => {
|
|
|
50
120
|
delete global.__filename
|
|
51
121
|
}
|
|
52
122
|
|
|
53
|
-
|
|
54
|
-
console.warn = (msg, ...args) => {
|
|
55
|
-
if (/Insecure Content-Security-Policy/.test(msg)) return
|
|
56
|
-
warn.call(console, msg, ...args)
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
if (Pear.config.options.gui?.preload) {
|
|
60
|
-
const run = require('node-bare-bundle')
|
|
61
|
-
const key = Pear.config.options.gui.preload
|
|
62
|
-
Pear.get(key, { bundle: true }).then((preload) => {
|
|
63
|
-
run(preload, { mount: '/', entrypoint: key })
|
|
64
|
-
}, console.error).finally(descopeGlobals)
|
|
65
|
-
} else {
|
|
66
|
-
descopeGlobals()
|
|
67
|
-
}
|
|
123
|
+
descopeGlobals()
|
|
68
124
|
|
|
69
125
|
customElements.define('pear-ctrl', class extends HTMLElement {
|
|
70
126
|
#onfocus = null
|
package/runtime.js
CHANGED
|
@@ -8,11 +8,11 @@ const Pipe = require('bare-pipe')
|
|
|
8
8
|
const { spawn } = require('bare-subprocess')
|
|
9
9
|
const env = require('bare-env')
|
|
10
10
|
const { command } = require('paparam')
|
|
11
|
-
const { isLinux, isWindows } = require('which-runtime')
|
|
11
|
+
const { isLinux, isWindows, isMac } = require('which-runtime')
|
|
12
12
|
const { pathToFileURL, fileURLToPath } = require('url-file-url')
|
|
13
13
|
const constants = require('pear-api/constants')
|
|
14
14
|
const parseLink = require('pear-api/parse-link')
|
|
15
|
-
const { ERR_INVALID_INPUT } = require('pear-api/errors')
|
|
15
|
+
const { ERR_INVALID_INPUT, ERR_INVALID_APPLING } = require('pear-api/errors')
|
|
16
16
|
const run = require('pear-api/cmd/run')
|
|
17
17
|
const pear = require('pear-api/cmd')
|
|
18
18
|
const EXEC = isWindows
|
|
@@ -43,8 +43,7 @@ class PearElectron {
|
|
|
43
43
|
const isFile = link.startsWith('file://')
|
|
44
44
|
const isPath = isPear === false && isFile === false
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
const originalCwd = cwd
|
|
46
|
+
const cwd = os.cwd()
|
|
48
47
|
let dir = cwd
|
|
49
48
|
let base = null
|
|
50
49
|
if (key === null) {
|
|
@@ -53,12 +52,7 @@ class PearElectron {
|
|
|
53
52
|
} catch { /* ignore */ }
|
|
54
53
|
base = project(dir, pathname, cwd)
|
|
55
54
|
dir = base.dir
|
|
56
|
-
if (dir
|
|
57
|
-
global.Bare.on('exit', () => os.chdir(originalCwd)) // TODO: remove this once Pear.shutdown is used to close
|
|
58
|
-
Pear.teardown(() => os.chdir(originalCwd))
|
|
59
|
-
os.chdir(dir)
|
|
60
|
-
cwd = dir
|
|
61
|
-
}
|
|
55
|
+
if (dir.length > 1 && dir.endsWith('/')) dir = dir.slice(0, -1)
|
|
62
56
|
if (isPath) {
|
|
63
57
|
link = pathToFileURL(path.join(dir, base.entrypoint || '/')).pathname
|
|
64
58
|
}
|
|
@@ -66,20 +60,35 @@ class PearElectron {
|
|
|
66
60
|
|
|
67
61
|
if (isPath) argv[indices.args.link] = 'file://' + (base.entrypoint || '/')
|
|
68
62
|
argv[indices.args.link] = argv[indices.args.link].replace('://', '_||') // for Windows
|
|
63
|
+
|
|
69
64
|
if ((isLinux || isWindows) && !flags.sandbox) argv.splice(indices.args.link, 0, '--no-sandbox')
|
|
70
65
|
const info = JSON.stringify({
|
|
71
66
|
checkout: constants.CHECKOUT,
|
|
72
67
|
mount: constants.MOUNT,
|
|
73
|
-
bridge: opts.bridge?.addr ?? undefined
|
|
68
|
+
bridge: opts.bridge?.addr ?? undefined,
|
|
69
|
+
dir
|
|
74
70
|
})
|
|
75
|
-
argv = [BOOT, '--
|
|
71
|
+
argv = [BOOT, '--rti', info, ...argv]
|
|
76
72
|
const stdio = args.detach ? 'ignore' : ['ignore', 'inherit', 'pipe', 'pipe']
|
|
77
|
-
const
|
|
73
|
+
const options = {
|
|
78
74
|
stdio,
|
|
79
75
|
cwd,
|
|
80
76
|
windowsHide: true,
|
|
81
77
|
...{ env: { ...env, NODE_PRESERVE_SYMLINKS: 1 } }
|
|
82
|
-
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const sp = spawn(this.bin, argv, options)
|
|
81
|
+
if (args.appling) {
|
|
82
|
+
const { appling } = args
|
|
83
|
+
const applingApp = isMac ? appling.split('.app')[0] + '.app' : appling
|
|
84
|
+
try {
|
|
85
|
+
fs.statSync(applingApp)
|
|
86
|
+
} catch {
|
|
87
|
+
throw ERR_INVALID_APPLING('Appling does not exist')
|
|
88
|
+
}
|
|
89
|
+
if (isMac) spawn('open', [applingApp, '--args', ...argv], options).unref()
|
|
90
|
+
else spawn(applingApp, argv, options).unref()
|
|
91
|
+
}
|
|
83
92
|
if (args.detach) return null
|
|
84
93
|
const pipe = sp.stdio[3]
|
|
85
94
|
sp.on('exit', (code) => { Pear.exit(code) })
|