pear-mobile 1.0.0-rc → 1.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/README.md +66 -38
- package/index.js +18 -113
- package/package.json +5 -13
package/README.md
CHANGED
|
@@ -6,75 +6,103 @@ Embeddable Pear runtime for mobile applications. Provides storage path and bare
|
|
|
6
6
|
npm install pear-mobile
|
|
7
7
|
```
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
npm install react-native-bare-kit --save
|
|
11
|
-
```
|
|
9
|
+
This module integrates Pear into React-Native-based Mobile applications.
|
|
12
10
|
|
|
13
|
-
|
|
11
|
+
See [pear-runtime](https://github.com/holepunchto/pear-runtime) for Pear's embeddable runtime module for Desktop Devices.
|
|
14
12
|
|
|
15
|
-
##
|
|
13
|
+
## MVP - EXPERIMENTAL
|
|
16
14
|
|
|
17
|
-
|
|
18
|
-
/* React Native */
|
|
19
|
-
import PearRuntime from 'pear-mobile'
|
|
20
|
-
import bundle from './worker.bundle.js'
|
|
21
|
-
import { version, upgrade } from './package.json'
|
|
15
|
+
This boilerplate is MVP and Experimental.
|
|
22
16
|
|
|
23
|
-
|
|
24
|
-
const IPC = runtime.run('/worker.bundle', bundle, [runtime.dir])
|
|
17
|
+
## Usage
|
|
25
18
|
|
|
26
|
-
|
|
27
|
-
const PearRuntime = require('pear-
|
|
19
|
+
```js
|
|
20
|
+
const PearRuntime = require('pear-mobile')
|
|
28
21
|
const { version, upgrade } = require('./package.json')
|
|
29
22
|
|
|
30
|
-
const dir = Bare.argv[0]
|
|
23
|
+
const dir = Bare.argv[0] // pass the /Documents storage dir
|
|
31
24
|
|
|
32
|
-
const runtime = new PearRuntime({version, upgrade, dir})
|
|
33
|
-
runtime.on('updated', () => {
|
|
34
|
-
|
|
35
|
-
|
|
25
|
+
const runtime = new PearRuntime({ version, upgrade, dir })
|
|
26
|
+
runtime.updater.on('updated', async () => {
|
|
27
|
+
await runtime.updater.applyUpdate()
|
|
28
|
+
conosle.log('restart for update')
|
|
36
29
|
})
|
|
37
30
|
```
|
|
38
31
|
|
|
32
|
+
## Quick Starts
|
|
33
|
+
|
|
34
|
+
### Expo
|
|
35
|
+
|
|
36
|
+
```sh
|
|
37
|
+
git clone https://github.com/holepunchto/hello-pear-react-native
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
For end-to-end instructions from building to deploying with [Pear](https://docs.pears.com) see [hello-pear-react-native](https://github.com/holepunchto/hello-pear-react-native) `README.md`.
|
|
41
|
+
|
|
42
|
+
## Features
|
|
43
|
+
|
|
44
|
+
- Peer-to-Peer Over-the-Air (P2P OTA) updates (via [pear-runtime-updater](https://www.github.com/holepunchto/pear-runtime-updater))
|
|
45
|
+
- Application storage management
|
|
46
|
+
|
|
39
47
|
## API
|
|
40
48
|
|
|
41
|
-
#### `const
|
|
49
|
+
#### `const pear = new PearRuntime(opts)`
|
|
50
|
+
|
|
51
|
+
Create a runtime. `opts` may include:
|
|
42
52
|
|
|
43
|
-
|
|
53
|
+
- **`dir`** (required) Directory to store data (e.g. app data dir).
|
|
54
|
+
- **`upgrade`** – (required) Pear link for OTA updates (e.g. from `package.json` `upgrade` field).
|
|
55
|
+
- **`version`** – Current app version (e.g. from `package.json`); used for update checks.
|
|
56
|
+
- **`app`** – Path to the native boot bundle override; required for `applyUpdate()` to swap in the new build. Defaults to `path/to/Documents/pear-runtime/upgrades`
|
|
57
|
+
- **`updates`** – Set to `false` to disable P2P OTA updates.
|
|
58
|
+
- **`storage`** – Saves the app storage path.
|
|
44
59
|
|
|
45
|
-
#### `
|
|
60
|
+
#### `pear.storage`
|
|
46
61
|
|
|
47
|
-
|
|
62
|
+
Suggested storage folder for app storage.
|
|
48
63
|
|
|
49
|
-
#### `
|
|
64
|
+
#### `pear.updater`
|
|
50
65
|
|
|
51
|
-
|
|
66
|
+
Instance of [pear-runtime-updater](https://www.github.com/holepunchto/pear-runtime-updater)
|
|
52
67
|
|
|
53
|
-
#### `
|
|
68
|
+
#### `await updater.ready()`
|
|
54
69
|
|
|
55
|
-
|
|
70
|
+
Awaits the open of the updater (p2p connections, drive open ...)
|
|
56
71
|
|
|
57
|
-
#### `
|
|
72
|
+
#### `await pear.close()`
|
|
58
73
|
|
|
59
|
-
|
|
74
|
+
Shut it down. You should do this when closing your app for best performance.
|
|
60
75
|
|
|
61
|
-
|
|
76
|
+
## Making updates
|
|
62
77
|
|
|
63
|
-
|
|
78
|
+
VERY EXPERIMENTAL, MOST DEFINITELY WILL CHANGE.
|
|
64
79
|
|
|
65
|
-
|
|
80
|
+
Update listening and apply logic lives in [pear-runtime-updater](https://www.github.com/holepunchto/pear-runtime-updater).
|
|
66
81
|
|
|
67
|
-
|
|
82
|
+
First allocate a pear link if you haven't using [`pear`](https://github.com/holepunchto/pear):
|
|
68
83
|
|
|
69
|
-
|
|
84
|
+
```sh
|
|
85
|
+
pear touch
|
|
86
|
+
```
|
|
70
87
|
|
|
71
|
-
|
|
88
|
+
Store this link in the `package.json` `upgrade` field of a project.
|
|
72
89
|
|
|
73
|
-
|
|
90
|
+
bundle your JS frontend. Take the distributable (e.g react-native bundle and assets) produced and make a deployment folder with the following structure:
|
|
74
91
|
|
|
75
|
-
|
|
92
|
+
```
|
|
93
|
+
/package.json
|
|
94
|
+
/by-arch
|
|
95
|
+
/[...platform-arch]
|
|
96
|
+
/app
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Now go to this folder and stage this onto the link with `pear stage`
|
|
100
|
+
|
|
101
|
+
```sh
|
|
102
|
+
pear stage {link-from-touch}
|
|
103
|
+
```
|
|
76
104
|
|
|
77
|
-
|
|
105
|
+
Now seed it. Any build out there on a lower version will trigger the update flow.
|
|
78
106
|
|
|
79
107
|
## LICENSE
|
|
80
108
|
|
package/index.js
CHANGED
|
@@ -1,126 +1,31 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
const Hyperswarm = require('hyperswarm')
|
|
4
|
-
const Hyperdrive = require('hyperdrive')
|
|
5
|
-
const Corestore = require('corestore')
|
|
1
|
+
const PearRuntimeUpdater = require('pear-runtime-updater')
|
|
2
|
+
const ReadyResouce = require('ready-resource')
|
|
6
3
|
const path = require('bare-path')
|
|
7
4
|
const fs = require('bare-fs')
|
|
8
|
-
const
|
|
9
|
-
const ReadyResource = require('ready-resource')
|
|
10
|
-
const plink = require('pear-link')
|
|
11
|
-
const hid = require('hypercore-id-encoding')
|
|
12
|
-
const Localdrive = require('localdrive')
|
|
13
|
-
const { isIOS } = require('which-runtime')
|
|
5
|
+
const dir = require('bare-storage')
|
|
14
6
|
|
|
15
|
-
module.exports = class PearRuntime extends
|
|
16
|
-
constructor(
|
|
7
|
+
module.exports = class PearRuntime extends ReadyResouce {
|
|
8
|
+
constructor(opts = {}) {
|
|
17
9
|
super()
|
|
18
|
-
if (!
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
this.
|
|
27
|
-
this.
|
|
28
|
-
this.store = config.store || new Corestore(path.join(this.dir, 'pear-runtime/corestore'))
|
|
29
|
-
this.drive = new Hyperdrive(this.store, this.key)
|
|
30
|
-
this.swarm = config.swarm || null
|
|
31
|
-
this.next = null
|
|
32
|
-
this.checkout = null
|
|
33
|
-
|
|
34
|
-
this.updating = false
|
|
35
|
-
this.updated = false
|
|
10
|
+
if (!opts.dir) opts.dir = dir.persistent()
|
|
11
|
+
const appPath = opts.app || path.join(opts.dir, 'pear-runtime', 'upgrade')
|
|
12
|
+
if (!fs.existsSync(appPath)) fs.mkdirSync(appPath, { recursive: true, force: true })
|
|
13
|
+
if (fs.existsSync(path.join(appPath, 'package.json'))) {
|
|
14
|
+
const manifest = fs.readFileSync(path.join(appPath, 'package.json'))
|
|
15
|
+
opts.version = JSON.parse(manifest).version
|
|
16
|
+
}
|
|
17
|
+
opts = { app: appPath, ...opts }
|
|
18
|
+
this.dir = opts.dir
|
|
19
|
+
this.storage = opts.storage || path.join(this.dir, 'app-storage')
|
|
36
20
|
|
|
37
|
-
this.
|
|
21
|
+
this.updater = new PearRuntimeUpdater(opts)
|
|
38
22
|
}
|
|
39
23
|
|
|
40
24
|
async _open() {
|
|
41
|
-
await this.
|
|
42
|
-
await fs.rm(path.join(this.dir, 'pear-runtime/next'), {
|
|
43
|
-
recursive: true,
|
|
44
|
-
force: true
|
|
45
|
-
})
|
|
46
|
-
await fs.mkdir(path.join(this.dir, 'pear-runtime/next'), { recursive: true, force: true })
|
|
47
|
-
if (!this.swarm) {
|
|
48
|
-
const keyPair = await this.store.createKeyPair('pear-container')
|
|
49
|
-
this.swarm = new Hyperswarm({ keyPair })
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
this.swarm.on('connection', (connection) => this.store.replicate(connection))
|
|
53
|
-
this.swarm.join(this.drive.core.discoveryKey, { client: true, server: false })
|
|
54
|
-
|
|
55
|
-
this._updateBackground()
|
|
56
|
-
this.drive.core.on('append', () => this._updateBackground())
|
|
25
|
+
await this.updater.ready()
|
|
57
26
|
}
|
|
58
27
|
|
|
59
28
|
async _close() {
|
|
60
|
-
await this.
|
|
61
|
-
if (this.checkout) await this.checkout.close()
|
|
62
|
-
await this.store.destroy()
|
|
63
|
-
await this.swarm.destroy()
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
async applyUpdate() {
|
|
67
|
-
// we apply the update the same way bc react-native will always try to boot (e.g) upgrade/runtime.ios.bundle first
|
|
68
|
-
if (!this.updated || this.applied || !this.dir) return
|
|
69
|
-
this.applied = true
|
|
70
|
-
if (!await fs.exists(this.app)) {
|
|
71
|
-
await fs.mkdir(path.join(this.dir, 'pear-runtime/upgrade'), { recursive: true, force: true })
|
|
72
|
-
await fs.writeFile(this.app, '')
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
await fsx.swap(path.join(this.next, this.name), this.app)
|
|
76
|
-
await fs.promises.rm(this.next, { recursive: true, force: true })
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
_updateBackground() {
|
|
80
|
-
this._update().catch(noop)
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
async _update() {
|
|
84
|
-
if (this.updating) return
|
|
85
|
-
this.updating = true
|
|
86
|
-
|
|
87
|
-
const length = this.drive.core.length
|
|
88
|
-
const id = length + '.' + this.drive.core.fork
|
|
89
|
-
const next = path.join(this.dir, 'pear-runtime/next', id)
|
|
90
|
-
const co = this.drive.checkout(length)
|
|
91
|
-
|
|
92
|
-
this.checkout = co
|
|
93
|
-
|
|
94
|
-
const manifest = await co.get('/package.json')
|
|
95
|
-
const version = manifest && JSON.parse(manifest).version
|
|
96
|
-
if (!manifest || version === this.version) {
|
|
97
|
-
this.updating = false
|
|
98
|
-
this.checkout = null
|
|
99
|
-
await co.close()
|
|
100
|
-
return
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
const local = new Localdrive(next)
|
|
104
|
-
|
|
105
|
-
this.emit('updating')
|
|
106
|
-
|
|
107
|
-
for await (const data of co.mirror(local)) {
|
|
108
|
-
this.emit('updating-delta', data)
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
await co.close()
|
|
112
|
-
await local.close()
|
|
113
|
-
|
|
114
|
-
this.checkout = null
|
|
115
|
-
this.length = length
|
|
116
|
-
this.next = next
|
|
117
|
-
|
|
118
|
-
this.updating = false
|
|
119
|
-
this.updated = true
|
|
120
|
-
this.emit('updated', version)
|
|
121
|
-
|
|
122
|
-
if (this.drive.core.length > length) this._updateBackground()
|
|
29
|
+
await this.updater.close()
|
|
123
30
|
}
|
|
124
31
|
}
|
|
125
|
-
|
|
126
|
-
function noop() {}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pear-mobile",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "Embeddable Pear runtime for mobile applications",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"exports": {
|
|
@@ -38,18 +38,10 @@
|
|
|
38
38
|
"prettier-config-holepunch": "^2.0.0"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
+
"bare-fs": "^4.5.4",
|
|
41
42
|
"bare-path": "^3.0.0",
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"hyperdrive": "^13.2.1",
|
|
46
|
-
"hyperswarm": "^4.16.0",
|
|
47
|
-
"localdrive": "^2.2.0",
|
|
48
|
-
"pear-link": "^4.2.1",
|
|
49
|
-
"ready-resource": "^1.2.0",
|
|
50
|
-
"which-runtime": "^1.3.2"
|
|
51
|
-
},
|
|
52
|
-
"peerDependencies": {
|
|
53
|
-
"react-native-bare-kit": "*"
|
|
43
|
+
"bare-storage": "^1.0.1",
|
|
44
|
+
"pear-runtime-updater": "^0.0.13",
|
|
45
|
+
"ready-resource": "^1.2.0"
|
|
54
46
|
}
|
|
55
47
|
}
|