pear-electron 0.0.1
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/LICENSE +202 -0
- package/README.md +808 -0
- package/bin.js +36 -0
- package/boot.js +31 -0
- package/decal.html +733 -0
- package/electron-main.js +112 -0
- package/fonts.css +82 -0
- package/gui/decal.js +2 -0
- package/gui/gui.js +1814 -0
- package/gui/index.js +2 -0
- package/gui/preload.js +366 -0
- package/index.js +3 -0
- package/lib/bootstrap.js +155 -0
- package/lib/bundle.js +33 -0
- package/package.json +101 -0
- package/preload.js +348 -0
- package/runtime.js +123 -0
- package/scripts/bootstrap.js +20 -0
- package/scripts/bundle.js +3 -0
- package/scripts/decal.js +28 -0
package/gui/index.js
ADDED
package/gui/preload.js
ADDED
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
/* eslint-env browser */
|
|
2
|
+
'use strict'
|
|
3
|
+
const streamx = require('streamx')
|
|
4
|
+
const { EventEmitter } = require('events')
|
|
5
|
+
const Iambus = require('iambus')
|
|
6
|
+
const electron = require('electron')
|
|
7
|
+
const noop = () => {}
|
|
8
|
+
class Worker extends require('pear-api/worker') {
|
|
9
|
+
#ipc = null
|
|
10
|
+
constructor ({ ref = noop, unref = noop, ipc } = {}) {
|
|
11
|
+
super({ ref, unref })
|
|
12
|
+
this.#ipc = ipc
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
run (link, args = []) { return this.#ipc.workerRun(link, args) }
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
module.exports = class PearGUI {
|
|
19
|
+
constructor ({ API, state }) {
|
|
20
|
+
const id = this.id = electron.ipcRenderer.sendSync('id')
|
|
21
|
+
|
|
22
|
+
this.ipc = new IPC()
|
|
23
|
+
electron.ipcRenderer.on('ipc', (e, data) => {
|
|
24
|
+
this.ipc.stream.push(Buffer.from(data))
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
const teardown = async (fn) => {
|
|
28
|
+
if (state.isDecal) return
|
|
29
|
+
const action = await this.ipc.unloading({ id }) // only resolves when unloading occurs
|
|
30
|
+
await fn()
|
|
31
|
+
await this.ipc.completeUnload({ id, action })
|
|
32
|
+
if (action.type === 'reload') location.reload()
|
|
33
|
+
else if (action.type === 'nav') location.href = action.url
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
API = class extends API {
|
|
37
|
+
static UI = Symbol('ui')
|
|
38
|
+
constructor (ipc, state) {
|
|
39
|
+
const worker = new Worker({ ipc })
|
|
40
|
+
super(ipc, state, { teardown, worker })
|
|
41
|
+
this[Symbol.for('pear.ipc')] = ipc
|
|
42
|
+
const media = {
|
|
43
|
+
status: {
|
|
44
|
+
microphone: () => ipc.getMediaAccessStatus({ id, media: 'microphone' }),
|
|
45
|
+
camera: () => ipc.getMediaAccessStatus({ id, media: 'camera' }),
|
|
46
|
+
screen: () => ipc.getMediaAccessStatus({ id, media: 'screen' })
|
|
47
|
+
},
|
|
48
|
+
access: {
|
|
49
|
+
microphone: () => ipc.askForMediaAccess({ id, media: 'microphone' }),
|
|
50
|
+
camera: () => ipc.askForMediaAccess({ id, media: 'camera' }),
|
|
51
|
+
screen: () => ipc.askForMediaAccess({ id, media: 'screen' })
|
|
52
|
+
},
|
|
53
|
+
desktopSources: (options = {}) => ipc.desktopSources(options)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const kGuiCtrl = Symbol('gui:ctrl')
|
|
57
|
+
|
|
58
|
+
class Parent extends EventEmitter {
|
|
59
|
+
#id
|
|
60
|
+
constructor (id) {
|
|
61
|
+
super()
|
|
62
|
+
this.#id = id
|
|
63
|
+
electron.ipcRenderer.on('send', (e, ...args) => {
|
|
64
|
+
this.emit('message', ...args)
|
|
65
|
+
})
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
send (...args) { return electron.ipcRenderer.send('send-to', this.#id, ...args) }
|
|
69
|
+
focus (options = null) { return ipc.parent({ act: 'focus', id: this.#id, options }) }
|
|
70
|
+
blur () { return ipc.parent({ act: 'blur', id: this.#id }) }
|
|
71
|
+
show () { return ipc.parent({ act: 'show', id: this.#id }) }
|
|
72
|
+
hide () { return ipc.parent({ act: 'hide', id: this.#id }) }
|
|
73
|
+
minimize () { return ipc.parent({ act: 'minimize', id: this.#id }) }
|
|
74
|
+
maximize () { return ipc.parent({ act: 'maximize', id: this.#id }) }
|
|
75
|
+
fullscreen () { return ipc.parent({ act: 'fullscreen', id: this.#id }) }
|
|
76
|
+
restore () { return ipc.parent({ act: 'restore', id: this.#id }) }
|
|
77
|
+
dimensions (options = null) { return ipc.parent({ act: 'dimensions', id: this.#id, options }) }
|
|
78
|
+
isVisible () { return ipc.parent({ act: 'isVisible', id: this.#id }) }
|
|
79
|
+
isMinimized () { return ipc.parent({ act: 'isMinimized', id: this.#id }) }
|
|
80
|
+
isMaximized () { return ipc.parent({ act: 'isMaximized', id: this.#id }) }
|
|
81
|
+
isFullscreen () { return ipc.parent({ act: 'isFullscreen', id: this.#id }) }
|
|
82
|
+
isClosed () { return ipc.parent({ act: 'isClosed', id: this.#id }) }
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
class Self {
|
|
86
|
+
constructor (id) { this.id = id }
|
|
87
|
+
focus (options = null) { return ipc.focus({ id: this.id, options }) }
|
|
88
|
+
blur () { return ipc.blur({ id: this.id }) }
|
|
89
|
+
show () { return ipc.show({ id: this.id }) }
|
|
90
|
+
hide () { return ipc.hide({ id: this.id }) }
|
|
91
|
+
minimize () { return ipc.minimize({ id: this.id }) }
|
|
92
|
+
maximize () { return ipc.maximize({ id: this.id }) }
|
|
93
|
+
fullscreen () { return ipc.fullscreen({ id: this.id }) }
|
|
94
|
+
restore () { return ipc.restore({ id: this.id }) }
|
|
95
|
+
close () { return ipc.close({ id: this.id }) }
|
|
96
|
+
dimensions (options = null) { return ipc.dimensions({ id: this.id, options }) }
|
|
97
|
+
isVisible () { return ipc.isVisible({ id: this.id }) }
|
|
98
|
+
isMinimized () { return ipc.isMinimized({ id: this.id }) }
|
|
99
|
+
isMaximized () { return ipc.isMaximized({ id: this.id }) }
|
|
100
|
+
isFullscreen () { return ipc.isFullscreen({ id: this.id }) }
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
class GuiCtrl extends EventEmitter {
|
|
104
|
+
#listener = null
|
|
105
|
+
|
|
106
|
+
static get parent () {
|
|
107
|
+
Object.defineProperty(this, 'parent', {
|
|
108
|
+
value: new Parent(electron.ipcRenderer.sendSync('parentId'))
|
|
109
|
+
})
|
|
110
|
+
return this.parent
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
static get self () {
|
|
114
|
+
Object.defineProperty(this, 'self', {
|
|
115
|
+
value: new Self(electron.ipcRenderer.sendSync('id'))
|
|
116
|
+
})
|
|
117
|
+
return this.self
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
constructor (entry, at, options = at) {
|
|
121
|
+
super()
|
|
122
|
+
if (options === at) {
|
|
123
|
+
if (typeof at === 'string') options = { at }
|
|
124
|
+
}
|
|
125
|
+
if (!entry) throw new Error(`No path provided, cannot open ${this.constructor[kGuiCtrl]}`)
|
|
126
|
+
this.entry = entry
|
|
127
|
+
this.options = options
|
|
128
|
+
this.id = null
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
#rxtx () {
|
|
132
|
+
this.#listener = (e, ...args) => this.emit('message', ...args)
|
|
133
|
+
electron.ipcRenderer.on('send', this.#listener)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
#unrxtx () {
|
|
137
|
+
if (this.#listener === null) return
|
|
138
|
+
electron.ipcRenderer.removeListener('send', this.#listener)
|
|
139
|
+
this.#listener = null
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
async open (opts) {
|
|
143
|
+
if (this.id === null) {
|
|
144
|
+
await new Promise(setImmediate) // needed for windows/views opening on app load
|
|
145
|
+
this.#rxtx()
|
|
146
|
+
this.id = await ipc.ctrl({
|
|
147
|
+
parentId: this.self.id,
|
|
148
|
+
type: this.constructor[kGuiCtrl],
|
|
149
|
+
entry: this.entry,
|
|
150
|
+
options: this.options,
|
|
151
|
+
state: this.state,
|
|
152
|
+
openOptions: opts
|
|
153
|
+
})
|
|
154
|
+
return true
|
|
155
|
+
}
|
|
156
|
+
return await ipc.open({ id: this.id })
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
async close () {
|
|
160
|
+
const result = await ipc.close({ id: this.id })
|
|
161
|
+
this.#unrxtx()
|
|
162
|
+
this.id = null
|
|
163
|
+
return result
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
show () { return ipc.show({ id: this.id }) }
|
|
167
|
+
hide () { return ipc.hide({ id: this.id }) }
|
|
168
|
+
focus (options = null) { return ipc.focus({ id: this.id, options }) }
|
|
169
|
+
blur () { return ipc.blur({ id: this.id }) }
|
|
170
|
+
|
|
171
|
+
dimensions (options = null) { return ipc.dimensions({ id: this.id, options }) }
|
|
172
|
+
minimize () {
|
|
173
|
+
if (this.constructor[kGuiCtrl] === 'view') throw new Error('A View cannot be minimized')
|
|
174
|
+
return ipc.minimize({ id: this.id })
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
maximize () {
|
|
178
|
+
if (this.constructor[kGuiCtrl] === 'view') throw new Error('A View cannot be maximized')
|
|
179
|
+
return ipc.maximize({ id: this.id })
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
fullscreen () {
|
|
183
|
+
if (this.constructor[kGuiCtrl] === 'view') throw new Error('A View cannot be fullscreened')
|
|
184
|
+
return ipc.fullscreen({ id: this.id })
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
restore () { return ipc.restore({ id: this.id }) }
|
|
188
|
+
|
|
189
|
+
isVisible () { return ipc.isVisible({ id: this.id }) }
|
|
190
|
+
|
|
191
|
+
isMinimized () {
|
|
192
|
+
if (this.constructor[kGuiCtrl] === 'view') throw new Error('A View cannot be minimized')
|
|
193
|
+
return ipc.isMinimized({ id: this.id })
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
isMaximized () {
|
|
197
|
+
if (this.constructor[kGuiCtrl] === 'view') throw new Error('A View cannot be maximized')
|
|
198
|
+
return ipc.isMaximized({ id: this.id })
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
isFullscreen () {
|
|
202
|
+
if (this.constructor[kGuiCtrl] === 'view') throw new Error('A View cannot be maximized')
|
|
203
|
+
return ipc.isFullscreen({ id: this.id })
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
isClosed () { return ipc.isClosed({ id: this.id }) }
|
|
207
|
+
|
|
208
|
+
send (...args) { return electron.ipcRenderer.send('send-to', this.id, ...args) }
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
class Window extends GuiCtrl {
|
|
212
|
+
static [kGuiCtrl] = 'window'
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
class View extends GuiCtrl { static [kGuiCtrl] = 'view' }
|
|
216
|
+
|
|
217
|
+
class PearElectron {
|
|
218
|
+
Window = Window
|
|
219
|
+
View = View
|
|
220
|
+
media = media
|
|
221
|
+
static DECAL = Symbol('decal')
|
|
222
|
+
warming () {
|
|
223
|
+
electron.ipcRenderer.send('warming')
|
|
224
|
+
const stream = new streamx.Readable()
|
|
225
|
+
electron.ipcRenderer.on('warming', (e, data) => { stream.push(data) })
|
|
226
|
+
return stream
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
constructor () {
|
|
230
|
+
if (state.isDecal) {
|
|
231
|
+
this[this.constructor.DECAL] = {
|
|
232
|
+
ipc,
|
|
233
|
+
'hypercore-id-encoding': require('hypercore-id-encoding'),
|
|
234
|
+
'pear-api/constants': require('pear-api/constants')
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
this[this.constructor.UI] = new PearElectron()
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
get media () {
|
|
244
|
+
console.warn('Pear.media is deprecated use require(\'pear-electron\').media')
|
|
245
|
+
return this[this.constructor.UI].media
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
get Window () {
|
|
249
|
+
console.warn('Pear.Window is deprecated use require(\'pear-electron\').Window')
|
|
250
|
+
return this[this.constructor.UI].Window
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
get View () {
|
|
254
|
+
console.warn('Pear.View is deprecated use require(\'pear-electron\').View')
|
|
255
|
+
return this[this.constructor.UI].View
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
exit = (code) => {
|
|
259
|
+
process.exitCode = code
|
|
260
|
+
electron.ipcRenderer.sendSync('exit', code)
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
this.api = new API(this.ipc, state, teardown)
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
class IPC {
|
|
268
|
+
getMediaAccessStatus (...args) { return electron.ipcRenderer.invoke('getMediaAccessStatus', ...args) }
|
|
269
|
+
askForMediaAccess (...args) { return electron.ipcRenderer.invoke('askForMediaAccess', ...args) }
|
|
270
|
+
desktopSources (...args) { return electron.ipcRenderer.invoke('desktopSources', ...args) }
|
|
271
|
+
chrome (...args) { return electron.ipcRenderer.invoke('chrome', ...args) }
|
|
272
|
+
ctrl (...args) { return electron.ipcRenderer.invoke('ctrl', ...args) }
|
|
273
|
+
parent (...args) { return electron.ipcRenderer.invoke('parent', ...args) }
|
|
274
|
+
open (...args) { return electron.ipcRenderer.invoke('open', ...args) }
|
|
275
|
+
close (...args) { return electron.ipcRenderer.invoke('close', ...args) }
|
|
276
|
+
show (...args) { return electron.ipcRenderer.invoke('show', ...args) }
|
|
277
|
+
hide (...args) { return electron.ipcRenderer.invoke('hide', ...args) }
|
|
278
|
+
minimize (...args) { return electron.ipcRenderer.invoke('minimize', ...args) }
|
|
279
|
+
maximize (...args) { return electron.ipcRenderer.invoke('maximize', ...args) }
|
|
280
|
+
setMaximizable (...args) { return electron.ipcRenderer.invoke('setMaximizable', ...args) }
|
|
281
|
+
setMinimizable (...args) { return electron.ipcRenderer.invoke('setMinimizable', ...args) }
|
|
282
|
+
fullscreen (...args) { return electron.ipcRenderer.invoke('fullscreen', ...args) }
|
|
283
|
+
restore (...args) { return electron.ipcRenderer.invoke('restore', ...args) }
|
|
284
|
+
focus (...args) { return electron.ipcRenderer.invoke('focus', ...args) }
|
|
285
|
+
blur (...args) { return electron.ipcRenderer.invoke('blur', ...args) }
|
|
286
|
+
dimensions (...args) { return electron.ipcRenderer.invoke('dimensions', ...args) }
|
|
287
|
+
isVisible (...args) { return electron.ipcRenderer.invoke('isVisible', ...args) }
|
|
288
|
+
isClosed (...args) { return electron.ipcRenderer.invoke('isClosed', ...args) }
|
|
289
|
+
isMinimized (...args) { return electron.ipcRenderer.invoke('isMinimized', ...args) }
|
|
290
|
+
isMaximized (...args) { return electron.ipcRenderer.invoke('isMaximized', ...args) }
|
|
291
|
+
isFullscreen (...args) { return electron.ipcRenderer.invoke('isFullscreen', ...args) }
|
|
292
|
+
setSize (...args) { return electron.ipcRenderer.invoke('setSize', ...args) }
|
|
293
|
+
permit (...args) { return electron.ipcRenderer.invoke('permit', ...args) }
|
|
294
|
+
unloading (...args) { return electron.ipcRenderer.invoke('unloading', ...args) }
|
|
295
|
+
completeUnload (...args) { return electron.ipcRenderer.invoke('completeUnload', ...args) }
|
|
296
|
+
attachMainView (...args) { return electron.ipcRenderer.invoke('attachMainView', ...args) }
|
|
297
|
+
detachMainView (...args) { return electron.ipcRenderer.invoke('detachMainView', ...args) }
|
|
298
|
+
afterViewLoaded (...args) { return electron.ipcRenderer.invoke('afterViewLoaded', ...args) }
|
|
299
|
+
setWindowButtonPosition (...args) { return electron.ipcRenderer.invoke('setWindowButtonPosition', ...args) }
|
|
300
|
+
setWindowButtonVisibility (...args) { return electron.ipcRenderer.invoke('setWindowButtonVisibility', ...args) }
|
|
301
|
+
async requestIdentity (...args) {
|
|
302
|
+
const publicKey = await electron.ipcRenderer.invoke('requestIdentity', ...args)
|
|
303
|
+
return Buffer.from(publicKey)
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
shareIdentity (...args) { return electron.ipcRenderer.invoke('shareIdentity', ...args) }
|
|
307
|
+
clearIdentity (...args) { return electron.ipcRenderer.invoke('clearIdentity', ...args) }
|
|
308
|
+
message (...args) { return electron.ipcRenderer.invoke('message', ...args) }
|
|
309
|
+
checkpoint (...args) { return electron.ipcRenderer.invoke('checkpoint', ...args) }
|
|
310
|
+
versions (...args) { return electron.ipcRenderer.invoke('versions', ...args) }
|
|
311
|
+
restart (...args) { return electron.ipcRenderer.invoke('restart', ...args) }
|
|
312
|
+
|
|
313
|
+
messages (pattern) {
|
|
314
|
+
electron.ipcRenderer.send('messages', pattern)
|
|
315
|
+
const bus = new Iambus()
|
|
316
|
+
electron.ipcRenderer.on('messages', (e, msg) => {
|
|
317
|
+
if (msg === null) bus.end()
|
|
318
|
+
else bus.pub(msg)
|
|
319
|
+
})
|
|
320
|
+
const stream = bus.sub(pattern)
|
|
321
|
+
return stream
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
warming () {
|
|
325
|
+
electron.ipcRenderer.send('warming')
|
|
326
|
+
const stream = new streamx.Readable()
|
|
327
|
+
electron.ipcRenderer.on('warming', (e, data) => { stream.push(data) })
|
|
328
|
+
return stream
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
reports () {
|
|
332
|
+
electron.ipcRenderer.send('reports')
|
|
333
|
+
const stream = new streamx.Readable()
|
|
334
|
+
electron.ipcRenderer.on('reports', (e, data) => { stream.push(data) })
|
|
335
|
+
return stream
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
workerRun (link, args) {
|
|
339
|
+
const id = electron.ipcRenderer.sendSync('workerPipeId')
|
|
340
|
+
electron.ipcRenderer.send('workerRun', link, args)
|
|
341
|
+
const stream = new streamx.Duplex({
|
|
342
|
+
write (data, cb) {
|
|
343
|
+
electron.ipcRenderer.send('workerPipeWrite', id, data)
|
|
344
|
+
cb()
|
|
345
|
+
},
|
|
346
|
+
final (cb) {
|
|
347
|
+
electron.ipcRenderer.send('workerPipeEnd', id)
|
|
348
|
+
cb()
|
|
349
|
+
}
|
|
350
|
+
})
|
|
351
|
+
electron.ipcRenderer.on('workerPipeError', (e, stack) => {
|
|
352
|
+
stream.emit('error', new Error('Worker PipeError (from electron-main): ' + stack))
|
|
353
|
+
})
|
|
354
|
+
electron.ipcRenderer.on('workerPipeClose', () => { stream.destroy() })
|
|
355
|
+
electron.ipcRenderer.on('workerPipeEnd', () => { stream.end() })
|
|
356
|
+
stream.once('close', () => {
|
|
357
|
+
electron.ipcRenderer.send('workerPipeClose', id)
|
|
358
|
+
})
|
|
359
|
+
|
|
360
|
+
electron.ipcRenderer.on('workerPipeData', (e, data) => { stream.push(data) })
|
|
361
|
+
return stream
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
ref () {}
|
|
365
|
+
unref () {}
|
|
366
|
+
}
|
package/index.js
ADDED
package/lib/bootstrap.js
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
const { platform, arch, isWindows } = require('which-runtime')
|
|
3
|
+
const path = require('path')
|
|
4
|
+
const { command, flag, rest } = require('paparam')
|
|
5
|
+
const Corestore = require('corestore')
|
|
6
|
+
const Localdrive = require('localdrive')
|
|
7
|
+
const Hyperdrive = require('hyperdrive')
|
|
8
|
+
const Hyperswarm = require('hyperswarm')
|
|
9
|
+
const goodbye = global.Pear?.teardown || require('graceful-goodbye')
|
|
10
|
+
const byteSize = require('tiny-byte-size')
|
|
11
|
+
const { decode } = require('hypercore-id-encoding')
|
|
12
|
+
const Rache = require('rache')
|
|
13
|
+
const speedometer = require('speedometer')
|
|
14
|
+
const isTTY = global.Pear ? false : process.stdout.isTTY // TODO: support Pear
|
|
15
|
+
const argv = global.Pear?.config.args || global.Bare?.argv || global.process.argv
|
|
16
|
+
const parser = command('bootstrap',
|
|
17
|
+
flag('--archdump'),
|
|
18
|
+
flag('--dlruntime'),
|
|
19
|
+
flag('--external-corestore'),
|
|
20
|
+
rest('rest')
|
|
21
|
+
)
|
|
22
|
+
const cmd = parser.parse(argv.slice(2), { sync: true })
|
|
23
|
+
|
|
24
|
+
const ARCHDUMP = cmd.flags.archdump === true
|
|
25
|
+
const DLRUNTIME = cmd.flags.dlruntime === true
|
|
26
|
+
const RUNTIMES_DRIVE_KEY = cmd.rest?.[0] || 'gd4n8itmfs6x7tzioj6jtxexiu4x4ijiu3grxdjwkbtkczw5dwho'
|
|
27
|
+
const CORESTORE = `/tmp/pear-archdump/${RUNTIMES_DRIVE_KEY}`
|
|
28
|
+
const ROOT = global.Pear ? path.join(new URL(global.Pear.config.applink).pathname, __dirname) : __dirname
|
|
29
|
+
const ADDON_HOST = require.addon?.host || platform + '-' + arch
|
|
30
|
+
const SWAP = path.join(ROOT, '..')
|
|
31
|
+
const HOST = path.join(SWAP, 'by-arch', ADDON_HOST)
|
|
32
|
+
|
|
33
|
+
module.exports = (all = true) => download(RUNTIMES_DRIVE_KEY, all)
|
|
34
|
+
module.exports.ARCHDUMP = ARCHDUMP
|
|
35
|
+
module.exports.DLRUNTIME = DLRUNTIME
|
|
36
|
+
module.exports.HOST = HOST
|
|
37
|
+
|
|
38
|
+
async function download (key, all = false) {
|
|
39
|
+
if (all) console.log('🍐 Fetching all runtimes from: \n ' + key)
|
|
40
|
+
else console.log('🍐 [ localdev ] - no local runtime: fetching runtime')
|
|
41
|
+
|
|
42
|
+
const store = CORESTORE
|
|
43
|
+
|
|
44
|
+
const maxCacheSize = 65536
|
|
45
|
+
const globalCache = new Rache({ maxSize: maxCacheSize })
|
|
46
|
+
|
|
47
|
+
const corestore = new Corestore(store, { globalCache })
|
|
48
|
+
let runtimes = new Hyperdrive(corestore, decode(key))
|
|
49
|
+
|
|
50
|
+
const swarm = new Hyperswarm()
|
|
51
|
+
goodbye(() => swarm.destroy())
|
|
52
|
+
|
|
53
|
+
swarm.on('connection', (socket) => { runtimes.corestore.replicate(socket) })
|
|
54
|
+
|
|
55
|
+
await runtimes.ready()
|
|
56
|
+
|
|
57
|
+
swarm.join(runtimes.discoveryKey, { server: false, client: true })
|
|
58
|
+
const done = runtimes.corestore.findingPeers()
|
|
59
|
+
swarm.flush().then(done, done)
|
|
60
|
+
|
|
61
|
+
await runtimes.core.update() // make sure we have latest version
|
|
62
|
+
|
|
63
|
+
runtimes = runtimes.checkout(runtimes.version)
|
|
64
|
+
goodbye(() => runtimes.close())
|
|
65
|
+
|
|
66
|
+
console.log(`\n Extracting platform runtime${all ? 's' : ''} to disk`)
|
|
67
|
+
|
|
68
|
+
const runtime = runtimes.mirror(new Localdrive(SWAP), {
|
|
69
|
+
prefix: '/by-arch' + (all ? '' : '/' + ADDON_HOST)
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
const monitor = monitorDrive(runtimes)
|
|
73
|
+
goodbye(() => monitor.stop())
|
|
74
|
+
|
|
75
|
+
for await (const { op, key, bytesAdded } of runtime) {
|
|
76
|
+
if (isTTY) monitor.clear()
|
|
77
|
+
if (op === 'add') {
|
|
78
|
+
console.log('\x1B[32m+\x1B[39m ' + key + ' [' + byteSize(bytesAdded) + ']')
|
|
79
|
+
} else if (op === 'change') {
|
|
80
|
+
console.log('\x1B[33m~\x1B[39m ' + key + ' [' + byteSize(bytesAdded) + ']')
|
|
81
|
+
} else if (op === 'remove') {
|
|
82
|
+
console.log('\x1B[31m-\x1B[39m ' + key + ' [' + byteSize(bytesAdded) + ']')
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
monitor.stop()
|
|
87
|
+
|
|
88
|
+
console.log('\x1B[2K\x1B[200D Runtime extraction complete\x1b[K\n')
|
|
89
|
+
|
|
90
|
+
await runtimes.close()
|
|
91
|
+
await swarm.destroy()
|
|
92
|
+
await corestore.close()
|
|
93
|
+
|
|
94
|
+
const tick = isWindows ? '^' : '✔'
|
|
95
|
+
|
|
96
|
+
if (all) console.log('\x1B[32m' + tick + '\x1B[39m Download complete\n')
|
|
97
|
+
else console.log('\x1B[32m' + tick + '\x1B[39m Download complete, initalizing...\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n')
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* @param {Hyperdrive} drive
|
|
102
|
+
*/
|
|
103
|
+
function monitorDrive (drive) {
|
|
104
|
+
if (!isTTY) {
|
|
105
|
+
return {
|
|
106
|
+
clear: () => null,
|
|
107
|
+
stop: () => null
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const downloadSpeedometer = speedometer()
|
|
112
|
+
const uploadSpeedometer = speedometer()
|
|
113
|
+
let peers = 0
|
|
114
|
+
let downloadedBytes = 0
|
|
115
|
+
let uploadedBytes = 0
|
|
116
|
+
|
|
117
|
+
drive.getBlobs().then(blobs => {
|
|
118
|
+
blobs.core.on('download', (_index, bytes) => {
|
|
119
|
+
downloadedBytes += bytes
|
|
120
|
+
downloadSpeedometer(bytes)
|
|
121
|
+
})
|
|
122
|
+
blobs.core.on('upload', (_index, bytes) => {
|
|
123
|
+
uploadedBytes += bytes
|
|
124
|
+
uploadSpeedometer(bytes)
|
|
125
|
+
})
|
|
126
|
+
blobs.core.on('peer-add', () => {
|
|
127
|
+
peers = blobs.core.peers.length
|
|
128
|
+
})
|
|
129
|
+
blobs.core.on('peer-remove', () => {
|
|
130
|
+
peers = blobs.core.peers.length
|
|
131
|
+
})
|
|
132
|
+
}).catch(() => {
|
|
133
|
+
// ignore
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
const clear = () => {
|
|
137
|
+
process.stdout.clearLine()
|
|
138
|
+
process.stdout.cursorTo(0)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const interval = setInterval(() => {
|
|
142
|
+
clear()
|
|
143
|
+
process.stdout.write(`[⬇ ${byteSize(downloadedBytes)} - ${byteSize(downloadSpeedometer())}/s - ${peers} peers] [⬆ ${byteSize(uploadedBytes)} - ${byteSize(uploadSpeedometer())}/s - ${peers} peers]`)
|
|
144
|
+
}, 500)
|
|
145
|
+
|
|
146
|
+
const stop = () => {
|
|
147
|
+
clearInterval(interval)
|
|
148
|
+
clear()
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return {
|
|
152
|
+
clear,
|
|
153
|
+
stop
|
|
154
|
+
}
|
|
155
|
+
}
|
package/lib/bundle.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
const Bundle = require('bare-bundle')
|
|
3
|
+
const Localdrive = require('localdrive')
|
|
4
|
+
const DriveBundler = require('drive-bundler')
|
|
5
|
+
const path = require('path')
|
|
6
|
+
const fs = require('fs')
|
|
7
|
+
const dirname = global.Pear?.config.applink ? new URL(global.Pear.config.applink + '/').pathname : path.join(__dirname, '/..')
|
|
8
|
+
async function bundle () {
|
|
9
|
+
const drive = new Localdrive(dirname)
|
|
10
|
+
const b = new Bundle()
|
|
11
|
+
|
|
12
|
+
const cache = {}
|
|
13
|
+
const res = {}
|
|
14
|
+
|
|
15
|
+
const { entrypoint, resolutions, sources } = await DriveBundler.bundle(drive, { cache, cwd: dirname, entrypoint: '/boot.js', absoluteFiles: false })
|
|
16
|
+
|
|
17
|
+
for (const [key, map] of Object.entries(resolutions)) {
|
|
18
|
+
res[key] = map
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
for (const [key, source] of Object.entries(sources)) {
|
|
22
|
+
cache[key] = true
|
|
23
|
+
b.write(key, source)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
b.main = entrypoint
|
|
27
|
+
b.resolutions = res
|
|
28
|
+
|
|
29
|
+
await fs.promises.writeFile(dirname + 'boot.bundle', b.toBuffer())
|
|
30
|
+
console.log('boot.bundle generated')
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
module.exports = bundle
|
package/package.json
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "pear-electron",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Pear User-Interface Library for Electron",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"bin": "bin.js",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"archdump": "node scripts/bootstrap.js --archdump",
|
|
9
|
+
"bootstrap": "node scripts/bootstrap.js",
|
|
10
|
+
"decal": "node scripts/decal.js",
|
|
11
|
+
"bundle": "node scripts/bundle.js",
|
|
12
|
+
"generate": "npm run decal && npm run bundle",
|
|
13
|
+
"prestage": "npm run archdump && npm run generate"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"bin.js",
|
|
17
|
+
"scripts/",
|
|
18
|
+
"lib/",
|
|
19
|
+
"boot.js",
|
|
20
|
+
"decal.html",
|
|
21
|
+
"electron-main.js",
|
|
22
|
+
"fonts.css",
|
|
23
|
+
"gui/",
|
|
24
|
+
"index.js",
|
|
25
|
+
"preload.js",
|
|
26
|
+
"runtime.js"
|
|
27
|
+
],
|
|
28
|
+
"pear": {
|
|
29
|
+
"runtimes": "pear://0.2356.pqbzjhqyonxprx8hghxexnmctw75mr91ewqw5dxe1zmntfyaddqy",
|
|
30
|
+
"name": "pear-electron",
|
|
31
|
+
"stage": {
|
|
32
|
+
"ignore": [
|
|
33
|
+
".git",
|
|
34
|
+
".gitignore",
|
|
35
|
+
".github",
|
|
36
|
+
".DS_Store",
|
|
37
|
+
".vscode",
|
|
38
|
+
".npmrc",
|
|
39
|
+
"scripts",
|
|
40
|
+
"package-lock.json",
|
|
41
|
+
"node_modules",
|
|
42
|
+
"node_modules",
|
|
43
|
+
"gui",
|
|
44
|
+
"boot.js",
|
|
45
|
+
"decal.html",
|
|
46
|
+
"electron-main.js",
|
|
47
|
+
"fonts.css",
|
|
48
|
+
"index.js",
|
|
49
|
+
"preload.js",
|
|
50
|
+
"README.md",
|
|
51
|
+
"runtime.js"
|
|
52
|
+
]
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
"keywords": [
|
|
56
|
+
"pear",
|
|
57
|
+
"runtime",
|
|
58
|
+
"electron",
|
|
59
|
+
"Pear Runtime",
|
|
60
|
+
"peer-to-peer",
|
|
61
|
+
"user-interface",
|
|
62
|
+
"UI",
|
|
63
|
+
"interface"
|
|
64
|
+
],
|
|
65
|
+
"author": "Holepunch",
|
|
66
|
+
"license": "Apache-2.0",
|
|
67
|
+
"dependencies": {
|
|
68
|
+
"bare-env": "^3.0.0",
|
|
69
|
+
"bare-fs": "^4.0.1",
|
|
70
|
+
"bare-os": "^3.3.0",
|
|
71
|
+
"bare-path": "^3.0.0",
|
|
72
|
+
"bare-subprocess": "^4.0.3",
|
|
73
|
+
"bare-tty": "^4.0.2",
|
|
74
|
+
"corestore": "^6.18.4",
|
|
75
|
+
"fs": "npm:bare-node-fs@^1.0.2",
|
|
76
|
+
"hypercore-id-encoding": "^1.3.0",
|
|
77
|
+
"hyperdrive": "^11.13.3",
|
|
78
|
+
"hyperswarm": "^4.8.4",
|
|
79
|
+
"iambus": "^1.0.3",
|
|
80
|
+
"localdrive": "^1.12.1",
|
|
81
|
+
"paparam": "^1.6.1",
|
|
82
|
+
"path": "npm:bare-node-path@^1.0.1",
|
|
83
|
+
"pear-api": "^0.0.17",
|
|
84
|
+
"pear-interface": "^1.0.3",
|
|
85
|
+
"pear-ipc": "^3.0.3",
|
|
86
|
+
"pear-link": "^2.0.6",
|
|
87
|
+
"pear-updater-bootstrap": "github:holepunchto/pear-updater-bootstrap#corestore-option",
|
|
88
|
+
"rache": "^1.0.0",
|
|
89
|
+
"safety-catch": "^1.0.2",
|
|
90
|
+
"script-linker": "^2.5.3",
|
|
91
|
+
"streamx": "^2.20.2",
|
|
92
|
+
"tiny-byte-size": "^1.1.0",
|
|
93
|
+
"url-file-url": "^1.0.4",
|
|
94
|
+
"which-runtime": "^1.2.1"
|
|
95
|
+
},
|
|
96
|
+
"devDependencies": {
|
|
97
|
+
"@fontsource/open-sans": "^5.1.0",
|
|
98
|
+
"graceful-goodbye": "^1.3.2",
|
|
99
|
+
"redhat-overpass-font": "^1.0.0"
|
|
100
|
+
}
|
|
101
|
+
}
|