pear-electron 1.5.2 → 1.7.0
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 +7 -0
- package/api.js +359 -0
- package/package.json +15 -4
- package/pre.js +3 -0
- package/runtime.js +7 -57
package/README.md
CHANGED
|
@@ -814,6 +814,13 @@ Use `<script type="module" src="path/to/my-file.js">` to load a JavaScript Modul
|
|
|
814
814
|
|
|
815
815
|
Use `<script src="path/to/my-file.js">` to load a JavaScript Script.
|
|
816
816
|
|
|
817
|
+
|
|
818
|
+
## API Extension
|
|
819
|
+
|
|
820
|
+
### `pear-electron/api`
|
|
821
|
+
|
|
822
|
+
Function that takes an API class and extends it with pear-electron APIs
|
|
823
|
+
|
|
817
824
|
## Graphical User Interface Options
|
|
818
825
|
|
|
819
826
|
GUI options for an application are set in the application `package.json` `pear.gui` field.
|
package/api.js
ADDED
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
/* globals Pear */
|
|
2
|
+
'use strict'
|
|
3
|
+
const streamx = require('streamx')
|
|
4
|
+
const { EventEmitter } = require('events')
|
|
5
|
+
|
|
6
|
+
module.exports = (api) => {
|
|
7
|
+
class API extends api {
|
|
8
|
+
static UI = Symbol('ui')
|
|
9
|
+
#ipc = null
|
|
10
|
+
#pipe = null
|
|
11
|
+
|
|
12
|
+
constructor (ipc, state, teardown, id) {
|
|
13
|
+
super(ipc, state, { teardown })
|
|
14
|
+
this.#ipc = ipc
|
|
15
|
+
const kGuiCtrl = Symbol('gui:ctrl')
|
|
16
|
+
const media = {
|
|
17
|
+
status: {
|
|
18
|
+
microphone: () => ipc.getMediaAccessStatus({ id, media: 'microphone' }),
|
|
19
|
+
camera: () => ipc.getMediaAccessStatus({ id, media: 'camera' }),
|
|
20
|
+
screen: () => ipc.getMediaAccessStatus({ id, media: 'screen' })
|
|
21
|
+
},
|
|
22
|
+
access: {
|
|
23
|
+
microphone: () => ipc.askForMediaAccess({ id, media: 'microphone' }),
|
|
24
|
+
camera: () => ipc.askForMediaAccess({ id, media: 'camera' }),
|
|
25
|
+
screen: () => ipc.askForMediaAccess({ id, media: 'screen' })
|
|
26
|
+
},
|
|
27
|
+
desktopSources: (options = {}) => ipc.desktopSources(options)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
class Found extends streamx.Readable {
|
|
31
|
+
#id = null
|
|
32
|
+
#stream = null
|
|
33
|
+
#listener = (data) => {
|
|
34
|
+
this.push(data.result)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
constructor (id) {
|
|
38
|
+
super()
|
|
39
|
+
this.#id = id
|
|
40
|
+
this.#stream = ipc.found(this.#id)
|
|
41
|
+
this.#stream.on('data', this.#listener)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
proceed () {
|
|
45
|
+
return ipc.find({ id: this.#id, next: true })
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
clear () {
|
|
49
|
+
if (this.destroyed) throw Error('Nothing to clear, already destroyed')
|
|
50
|
+
return ipc.find({ id: this.#id, stop: 'clear' }).finally(() => this.destroy())
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
keep () {
|
|
54
|
+
if (this.destroyed) throw Error('Nothing to keep, already destroyed')
|
|
55
|
+
return ipc.find({ id: this.#id, stop: 'keep' }).finally(() => this.destroy())
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
activate () {
|
|
59
|
+
if (this.destroyed) throw Error('Nothing to activate, already destroyed')
|
|
60
|
+
return ipc.find({ id: this.#id, stop: 'activate' }).finally(() => this.destroy())
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
_destroy () {
|
|
64
|
+
this.#stream.destroy()
|
|
65
|
+
return this.clear()
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
class Parent extends EventEmitter {
|
|
70
|
+
constructor (id) {
|
|
71
|
+
super()
|
|
72
|
+
this.id = id
|
|
73
|
+
ipc.receiveFrom(id, (...args) => {
|
|
74
|
+
this.emit('message', ...args)
|
|
75
|
+
})
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async find (options) {
|
|
79
|
+
const found = new Found(this.id)
|
|
80
|
+
await ipc.find({ id: this.id, options })
|
|
81
|
+
return found
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
send (...args) { return ipc.sendTo(this.id, ...args) }
|
|
85
|
+
focus (options = null) { return ipc.parent({ act: 'focus', id: this.id, options }) }
|
|
86
|
+
blur () { return ipc.parent({ act: 'blur', id: this.id }) }
|
|
87
|
+
show () { return ipc.parent({ act: 'show', id: this.id }) }
|
|
88
|
+
hide () { return ipc.parent({ act: 'hide', id: this.id }) }
|
|
89
|
+
minimize () { return ipc.parent({ act: 'minimize', id: this.id }) }
|
|
90
|
+
maximize () { return ipc.parent({ act: 'maximize', id: this.id }) }
|
|
91
|
+
fullscreen () { return ipc.parent({ act: 'fullscreen', id: this.id }) }
|
|
92
|
+
restore () { return ipc.parent({ act: 'restore', id: this.id }) }
|
|
93
|
+
dimensions (options = null) { return ipc.parent({ act: 'dimensions', id: this.id, options }) }
|
|
94
|
+
isVisible () { return ipc.parent({ act: 'isVisible', id: this.id }) }
|
|
95
|
+
isMinimized () { return ipc.parent({ act: 'isMinimized', id: this.id }) }
|
|
96
|
+
isMaximized () { return ipc.parent({ act: 'isMaximized', id: this.id }) }
|
|
97
|
+
isFullscreen () { return ipc.parent({ act: 'isFullscreen', id: this.id }) }
|
|
98
|
+
isClosed () { return ipc.parent({ act: 'isClosed', id: this.id }) }
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
class App {
|
|
102
|
+
id = null
|
|
103
|
+
#untray = null
|
|
104
|
+
|
|
105
|
+
get parent () {
|
|
106
|
+
const parentId = ipc.getParentId()
|
|
107
|
+
Object.defineProperty(this, 'parent', { value: new Parent(parentId) })
|
|
108
|
+
return this.parent
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
constructor (id) {
|
|
112
|
+
this.id = id
|
|
113
|
+
this.tray.scaleFactor = state.tray?.scaleFactor
|
|
114
|
+
this.tray.darkMode = state.tray?.darkMode
|
|
115
|
+
|
|
116
|
+
ipc.systemTheme().on('data', ({ mode }) => {
|
|
117
|
+
this.tray.darkMode = mode === 'dark'
|
|
118
|
+
})
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
find = (options) => {
|
|
122
|
+
const found = new Found(this.id)
|
|
123
|
+
ipc.find({ id: this.id, options })
|
|
124
|
+
return found
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
badge = (count) => {
|
|
128
|
+
if (!Number.isInteger(+count)) throw new Error('argument must be an integer')
|
|
129
|
+
return ipc.badge({ id: this.id, count })
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
tray = async (opts = {}, listener) => {
|
|
133
|
+
opts = {
|
|
134
|
+
...opts,
|
|
135
|
+
menu: opts.menu ?? {
|
|
136
|
+
show: `Show ${state.name}`,
|
|
137
|
+
quit: 'Quit'
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
listener = listener ?? ((key) => {
|
|
141
|
+
if (key === 'click' || key === 'show') {
|
|
142
|
+
this.show()
|
|
143
|
+
this.focus({ steal: true })
|
|
144
|
+
return
|
|
145
|
+
}
|
|
146
|
+
if (key === 'quit') {
|
|
147
|
+
this.quit()
|
|
148
|
+
}
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
const untray = async () => {
|
|
152
|
+
if (this.#untray) {
|
|
153
|
+
await this.#untray()
|
|
154
|
+
this.#untray = null
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
await untray()
|
|
159
|
+
this.#untray = ipc.tray(opts, listener)
|
|
160
|
+
return untray
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
focus = (options = null) => { return ipc.focus({ id: this.id, options }) }
|
|
164
|
+
blur = () => { return ipc.blur({ id: this.id }) }
|
|
165
|
+
show = () => { return ipc.show({ id: this.id }) }
|
|
166
|
+
hide = () => { return ipc.hide({ id: this.id }) }
|
|
167
|
+
minimize = () => { return ipc.minimize({ id: this.id }) }
|
|
168
|
+
maximize = () => { return ipc.maximize({ id: this.id }) }
|
|
169
|
+
fullscreen = () => { return ipc.fullscreen({ id: this.id }) }
|
|
170
|
+
restore = () => { return ipc.restore({ id: this.id }) }
|
|
171
|
+
close = () => { return ipc.close({ id: this.id }) }
|
|
172
|
+
quit = () => { return ipc.quit({ id: this.id }) }
|
|
173
|
+
dimensions (options = null) { return ipc.dimensions({ id: this.id, options }) }
|
|
174
|
+
isVisible = () => { return ipc.isVisible({ id: this.id }) }
|
|
175
|
+
isMinimized = () => { return ipc.isMinimized({ id: this.id }) }
|
|
176
|
+
isMaximized = () => { return ipc.isMaximized({ id: this.id }) }
|
|
177
|
+
isFullscreen = () => { return ipc.isFullscreen({ id: this.id }) }
|
|
178
|
+
report = (rpt) => { return ipc.report(rpt) }
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
class GuiCtrl extends EventEmitter {
|
|
182
|
+
#listener = null
|
|
183
|
+
#unlisten = null
|
|
184
|
+
|
|
185
|
+
static get parent () {
|
|
186
|
+
console.warn('Pear.Window.parent & Pear.View.parent are deprecated use ui.app.parent')
|
|
187
|
+
return Pear[Pear.constructor.UI].app.parent
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
static get self () {
|
|
191
|
+
console.warn('Pear.Window.self & Pear.View.self are deprecated use ui.app')
|
|
192
|
+
return Pear[Pear.constructor.UI].app
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
constructor (entry, at, options = at) {
|
|
196
|
+
super()
|
|
197
|
+
if (options === at) {
|
|
198
|
+
if (typeof at === 'string') options = { at }
|
|
199
|
+
}
|
|
200
|
+
if (!entry) throw new Error(`No path provided, cannot open ${this.constructor[kGuiCtrl]}`)
|
|
201
|
+
this.entry = entry
|
|
202
|
+
this.options = options
|
|
203
|
+
this.id = null
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
#rxtx () {
|
|
207
|
+
this.#listener = (e, ...args) => this.emit('message', ...args)
|
|
208
|
+
this.#unlisten = ipc.receiveFrom(this.#listener)
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
#unrxtx () {
|
|
212
|
+
if (this.#unlisten === null) return
|
|
213
|
+
this.#unlisten()
|
|
214
|
+
this.#unlisten = null
|
|
215
|
+
this.#listener = null
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
find = async (options) => {
|
|
219
|
+
const found = new Found(this.id)
|
|
220
|
+
await ipc.find({ id: this.id, options })
|
|
221
|
+
return found
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
send (...args) { return ipc.sendTo(this.id, ...args) }
|
|
225
|
+
|
|
226
|
+
async open (opts) {
|
|
227
|
+
if (this.id === null) {
|
|
228
|
+
await new Promise(setImmediate) // needed for windows/views opening on app load
|
|
229
|
+
this.#rxtx()
|
|
230
|
+
this.id = await ipc.ctrl({
|
|
231
|
+
parentId: Pear[Pear.constructor.UI].app.id,
|
|
232
|
+
type: this.constructor[kGuiCtrl],
|
|
233
|
+
entry: this.entry,
|
|
234
|
+
options: this.options,
|
|
235
|
+
state,
|
|
236
|
+
openOptions: opts
|
|
237
|
+
})
|
|
238
|
+
return true
|
|
239
|
+
}
|
|
240
|
+
return await ipc.open({ id: this.id })
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
async close () {
|
|
244
|
+
const result = await ipc.close({ id: this.id })
|
|
245
|
+
this.#unrxtx()
|
|
246
|
+
this.id = null
|
|
247
|
+
return result
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
show () { return ipc.show({ id: this.id }) }
|
|
251
|
+
hide () { return ipc.hide({ id: this.id }) }
|
|
252
|
+
focus (options = null) { return ipc.focus({ id: this.id, options }) }
|
|
253
|
+
blur () { return ipc.blur({ id: this.id }) }
|
|
254
|
+
|
|
255
|
+
dimensions (options = null) { return ipc.dimensions({ id: this.id, options }) }
|
|
256
|
+
minimize () {
|
|
257
|
+
if (this.constructor[kGuiCtrl] === 'view') throw new Error('A View cannot be minimized')
|
|
258
|
+
return ipc.minimize({ id: this.id })
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
maximize () {
|
|
262
|
+
if (this.constructor[kGuiCtrl] === 'view') throw new Error('A View cannot be maximized')
|
|
263
|
+
return ipc.maximize({ id: this.id })
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
fullscreen () {
|
|
267
|
+
if (this.constructor[kGuiCtrl] === 'view') throw new Error('A View cannot be fullscreened')
|
|
268
|
+
return ipc.fullscreen({ id: this.id })
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
restore () { return ipc.restore({ id: this.id }) }
|
|
272
|
+
|
|
273
|
+
isVisible () { return ipc.isVisible({ id: this.id }) }
|
|
274
|
+
|
|
275
|
+
isMinimized () {
|
|
276
|
+
if (this.constructor[kGuiCtrl] === 'view') throw new Error('A View cannot be minimized')
|
|
277
|
+
return ipc.isMinimized({ id: this.id })
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
isMaximized () {
|
|
281
|
+
if (this.constructor[kGuiCtrl] === 'view') throw new Error('A View cannot be maximized')
|
|
282
|
+
return ipc.isMaximized({ id: this.id })
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
isFullscreen () {
|
|
286
|
+
if (this.constructor[kGuiCtrl] === 'view') throw new Error('A View cannot be maximized')
|
|
287
|
+
return ipc.isFullscreen({ id: this.id })
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
isClosed () { return ipc.isClosed({ id: this.id }) }
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
class Window extends GuiCtrl {
|
|
294
|
+
static [kGuiCtrl] = 'window'
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
class View extends GuiCtrl { static [kGuiCtrl] = 'view' }
|
|
298
|
+
|
|
299
|
+
class PearElectron {
|
|
300
|
+
Window = Window
|
|
301
|
+
View = View
|
|
302
|
+
media = media
|
|
303
|
+
#app = null
|
|
304
|
+
get app () {
|
|
305
|
+
if (this.#app) return this.#app
|
|
306
|
+
this.#app = new App(ipc.getId())
|
|
307
|
+
return this.#app
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
warming () { return ipc.warming() }
|
|
311
|
+
|
|
312
|
+
async get (key) {
|
|
313
|
+
return Buffer.from(await ipc.get(key)).toString('utf-8')
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
constructor () {
|
|
317
|
+
if (state.isDecal) {
|
|
318
|
+
this.constructor.DECAL = {
|
|
319
|
+
ipc,
|
|
320
|
+
'hypercore-id-encoding': require('hypercore-id-encoding'),
|
|
321
|
+
'pear-api/constants': require('pear-api/constants')
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
this[this.constructor.UI] = new PearElectron()
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
get media () {
|
|
331
|
+
console.warn('Pear.media is deprecated use require(\'pear-electron\').media')
|
|
332
|
+
return this[this.constructor.UI].media
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
get Window () {
|
|
336
|
+
console.warn('Pear.Window is deprecated use require(\'pear-electron\').Window')
|
|
337
|
+
return this[this.constructor.UI].Window
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
get View () {
|
|
341
|
+
console.warn('Pear.View is deprecated use require(\'pear-electron\').View')
|
|
342
|
+
return this[this.constructor.UI].View
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
run (link, args = []) { return this.#ipc.run(link, args) }
|
|
346
|
+
|
|
347
|
+
get pipe () {
|
|
348
|
+
if (this.#pipe !== null) return this.#pipe
|
|
349
|
+
this.#pipe = this.#ipc.pipe()
|
|
350
|
+
return this.#pipe
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
exit = (code) => {
|
|
354
|
+
process.exitCode = code
|
|
355
|
+
this.ipc.processExit(code)
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
return API
|
|
359
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pear-electron",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.0",
|
|
4
4
|
"description": "Pear User-Interface Library for Electron",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -14,10 +14,21 @@
|
|
|
14
14
|
"bootstrap.js",
|
|
15
15
|
"index.js",
|
|
16
16
|
"runtime.js",
|
|
17
|
-
"pre.js"
|
|
17
|
+
"pre.js",
|
|
18
|
+
"api.js"
|
|
18
19
|
],
|
|
19
20
|
"pear": {
|
|
20
|
-
"
|
|
21
|
+
"assets": {
|
|
22
|
+
"ui": {
|
|
23
|
+
"link": "pear://0.2731.goowesg5dga9j1ryx47rsk9o4zms4541me4zerxsnbu8u99duh4o",
|
|
24
|
+
"name": "Pear Runtime",
|
|
25
|
+
"only": [
|
|
26
|
+
"/boot.bundle",
|
|
27
|
+
"/by-arch/%%HOST%%",
|
|
28
|
+
"/prebuilds/%%HOST%%"
|
|
29
|
+
]
|
|
30
|
+
}
|
|
31
|
+
},
|
|
21
32
|
"stage": {
|
|
22
33
|
"skipWarmup": "true",
|
|
23
34
|
"only": [
|
|
@@ -62,7 +73,7 @@
|
|
|
62
73
|
"iambus": "^1.0.3",
|
|
63
74
|
"localdrive": "^1.12.1",
|
|
64
75
|
"paparam": "^1.6.1",
|
|
65
|
-
"pear-api": "^1.17.
|
|
76
|
+
"pear-api": "^1.17.7",
|
|
66
77
|
"pear-ipc": "^6.3.0",
|
|
67
78
|
"script-linker": "^2.5.3",
|
|
68
79
|
"streamx": "^2.20.2",
|
package/pre.js
CHANGED
|
@@ -16,6 +16,9 @@ async function configure (options) {
|
|
|
16
16
|
const entrypoints = srcs(html)
|
|
17
17
|
stage.entrypoints = Array.isArray(stage.entrypoints) ? [...stage.entrypoints, ...entrypoints] : entrypoints
|
|
18
18
|
options.stage = stage
|
|
19
|
+
const pkg = (options.assets?.ui && !options.assets.ui.only) ? null : JSON.parse(await drive.get('node_modules/pear-electron/package.json'))
|
|
20
|
+
options.assets = options.assets ?? pkg?.pear?.assets
|
|
21
|
+
options.assets.ui.only = options.assets?.ui?.only ?? pkg?.pear?.assets?.ui?.only
|
|
19
22
|
return options
|
|
20
23
|
}
|
|
21
24
|
|
package/runtime.js
CHANGED
|
@@ -12,24 +12,19 @@ const constants = require('pear-api/constants')
|
|
|
12
12
|
const plink = require('pear-api/link')
|
|
13
13
|
const Logger = require('pear-api/logger')
|
|
14
14
|
const { ERR_INVALID_APPLING, ERR_INVALID_PROJECT_DIR } = require('pear-api/errors')
|
|
15
|
-
|
|
15
|
+
|
|
16
16
|
const run = require('pear-api/cmd/run')
|
|
17
17
|
const pear = require('pear-api/cmd')
|
|
18
18
|
const pkg = require('./package.json')
|
|
19
19
|
|
|
20
|
-
const bin = (name
|
|
21
|
-
const
|
|
22
|
-
const
|
|
23
|
-
const
|
|
20
|
+
const bin = (name) => {
|
|
21
|
+
const kebab = name.toLowerCase().split(' ').join('-')
|
|
22
|
+
const cased = kebab.split('-').map((w) => w[0].toUpperCase() + w.slice(1)).join(' ')
|
|
23
|
+
const app = isMac ? cased + '.app' : kebab + '-app'
|
|
24
|
+
const exe = isWindows ? cased + '.exe' : (isMac ? 'Contents/MacOS/' + cased : kebab)
|
|
24
25
|
return isWindows ? 'bin\\' + app + '\\' + exe : (isMac ? 'bin/' + app + '/' + exe : 'bin/' + app + '/' + exe)
|
|
25
26
|
}
|
|
26
27
|
|
|
27
|
-
const BIN = isWindows
|
|
28
|
-
? 'bin\\pear-runtime-app\\Pear Runtime.exe'
|
|
29
|
-
: isMac
|
|
30
|
-
? 'bin/Pear Runtime.app/Contents/MacOS/Pear Runtime'
|
|
31
|
-
: 'bin/pear-runtime-app/pear-runtime'
|
|
32
|
-
|
|
33
28
|
class PearElectron {
|
|
34
29
|
constructor () {
|
|
35
30
|
this.ipc = Pear[Pear.constructor.IPC]
|
|
@@ -38,54 +33,9 @@ class PearElectron {
|
|
|
38
33
|
Pear.teardown(() => this.ipc.close())
|
|
39
34
|
}
|
|
40
35
|
|
|
41
|
-
#outs () {
|
|
42
|
-
if (this.LOG.INF === false) {
|
|
43
|
-
return {
|
|
44
|
-
stats ({ upload, download, peers }) {
|
|
45
|
-
const dl = download.total + download.speed === 0 ? '' : `[${ansi.down} ${byteSize(download.total)} - ${byteSize(download.speed)}/s ] `
|
|
46
|
-
const ul = upload.total + upload.speed === 0 ? '' : `[${ansi.up} ${byteSize(upload.total)} - ${byteSize(upload.speed)}/s ] `
|
|
47
|
-
return {
|
|
48
|
-
output: 'status',
|
|
49
|
-
message: `Syncing Runtime [ Peers: ${peers} ] ${dl}${ul}`
|
|
50
|
-
}
|
|
51
|
-
},
|
|
52
|
-
final (asset) {
|
|
53
|
-
if (asset.forced === false && asset.inserted === false) return {}
|
|
54
|
-
return 'Synced\x1b[K'
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
return {
|
|
59
|
-
dumping: ({ link, dir }) => this.LOG.format('INF', 'Syncing runtime from peers\nfrom: ' + link + '\ninto: ' + dir + '\n'),
|
|
60
|
-
byteDiff: ({ type, sizes, message }) => {
|
|
61
|
-
sizes = sizes.map((size) => (size > 0 ? '+' : '') + byteSize(size)).join(', ')
|
|
62
|
-
return this.LOG.format('INF', indicator(type, 'diff') + ' ' + message + ' (' + sizes + ')')
|
|
63
|
-
},
|
|
64
|
-
file: ({ key }) => this.LOG.format('INF', key),
|
|
65
|
-
complete: () => this.LOG.format('INF', 'Asset fetch complete'),
|
|
66
|
-
error: (err) => this.LOG.format('INF', `Asset fetch Failure (code: ${err.code || 'none'}) ${err.stack}`)
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
async #asset (opts, force = false) {
|
|
71
|
-
const output = outputter('asset', this.#outs())
|
|
72
|
-
const json = false
|
|
73
|
-
const bootstrap = opts.bootstrap?.link ?? opts.bootstrap ?? pkg.pear.bootstrap
|
|
74
|
-
const executable = opts.bootstrap ? bin(opts.bootstrap.name) : BIN
|
|
75
|
-
const stream = Pear.asset(bootstrap, {
|
|
76
|
-
only: ['/boot.bundle', '/by-arch/' + require.addon.host, '/prebuilds/' + require.addon.host],
|
|
77
|
-
force
|
|
78
|
-
})
|
|
79
|
-
const asset = await output(json, stream)
|
|
80
|
-
if (asset === null || !asset?.path) throw new Error('Failed to determine runtime asset from sidecar')
|
|
81
|
-
return path.join(asset.path, 'by-arch', require.addon.host, executable)
|
|
82
|
-
}
|
|
83
|
-
|
|
84
36
|
async start (opts = {}) {
|
|
85
37
|
this.LOG.info('Fetching asset & determining bin path')
|
|
86
|
-
this.bin =
|
|
87
|
-
// if disk tampered then resync:
|
|
88
|
-
if (fs.existsSync(this.bin) === false) this.bin = await this.#asset(opts, true)
|
|
38
|
+
this.bin = path.join(Pear.config.assets.ui.path, 'by-arch', require.addon.host, bin(Pear.config.assets.ui.name))
|
|
89
39
|
const parsed = pear(Pear.argv.slice(1))
|
|
90
40
|
const cmd = command('run', ...run)
|
|
91
41
|
let argv = parsed.rest
|