pear-mobile 1.0.0-rc → 1.0.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.
Files changed (3) hide show
  1. package/README.md +66 -38
  2. package/index.js +17 -113
  3. 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
- ```sh
10
- npm install react-native-bare-kit --save
11
- ```
9
+ This module integrates Pear into React-Native-based Mobile applications.
12
10
 
13
- Requires `react-native-bare-kit` to be listed in project dependencies.
11
+ See [pear-runtime](https://github.com/holepunchto/pear-runtime) for Pear's embeddable runtime module for Desktop Devices.
14
12
 
15
- ## Usage
13
+ ## MVP - EXPERIMENTAL
16
14
 
17
- ```js
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
- const runtime = new PearRuntime()
24
- const IPC = runtime.run('/worker.bundle', bundle, [runtime.dir])
17
+ ## Usage
25
18
 
26
- /* Bare Worklet */
27
- const PearRuntime = require('pear-runtime')
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
- runtime.applyUpdate()
35
- conosle.log('restart for update')
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 runtime = new PearRuntime(...)`
49
+ #### `const pear = new PearRuntime(opts)`
50
+
51
+ Create a runtime. `opts` may include:
42
52
 
43
- Create a runtime.
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
- #### `runtime.storage`
60
+ #### `pear.storage`
46
61
 
47
- Absolute path to the runtime app storage directory.
62
+ Suggested storage folder for app storage.
48
63
 
49
- #### `runtime.on(event, callback)`
64
+ #### `pear.updater`
50
65
 
51
- Subscribe to an event. Returns `runtime` for chaining.
66
+ Instance of [pear-runtime-updater](https://www.github.com/holepunchto/pear-runtime-updater)
52
67
 
53
- #### `runtime.off(event, callback?)`
68
+ #### `await updater.ready()`
54
69
 
55
- Unsubscribe: remove `callback` for `event`, or remove all listeners for `event` if `callback` is omitted. Returns `runtime`.
70
+ Awaits the open of the updater (p2p connections, drive open ...)
56
71
 
57
- #### `runtime.once(event, callback)`
72
+ #### `await pear.close()`
58
73
 
59
- Subscribe to an event once; listener is removed after the first emit. Returns `runtime`.
74
+ Shut it down. You should do this when closing your app for best performance.
60
75
 
61
- #### `IPC = runtime.run(filename, bundle, argv)`
76
+ ## Making updates
62
77
 
63
- Start a bare worker (worklet). Returns an IPC duplex stream. `filename` is a virtual path, `bundle` is the worklet bundle, `argv` is an array of string arguments.
78
+ VERY EXPERIMENTAL, MOST DEFINITELY WILL CHANGE.
64
79
 
65
- `Bare.argv` in worker to access `argv`.
80
+ Update listening and apply logic lives in [pear-runtime-updater](https://www.github.com/holepunchto/pear-runtime-updater).
66
81
 
67
- #### `runtime.ready()`
82
+ First allocate a pear link if you haven't using [`pear`](https://github.com/holepunchto/pear):
68
83
 
69
- Returns a Promise. No-op on mobile; for API compatibility.
84
+ ```sh
85
+ pear touch
86
+ ```
70
87
 
71
- #### `runtime.close()`
88
+ Store this link in the `package.json` `upgrade` field of a project.
72
89
 
73
- Returns a Promise. No-op on mobile; for API compatibility.
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
- #### `runtime.applyUpdate()`
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
- Returns a Promise. On mobile this is not supported and only logs a warning; for API compatibility.
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,30 @@
1
- /* global BareKit */
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 fsx = require('fs-native-extensions')
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 ReadyResource {
16
- constructor(config) {
7
+ module.exports = class PearRuntime extends ReadyResouce {
8
+ constructor(opts = {}) {
17
9
  super()
18
- if (!config.dir) throw new Error('PearRuntime needs "dir" to /Documents Sandbox')
19
- const { drive: upgrade } = plink.parse(config.upgrade)
20
- this.dir = config.dir
21
- this.version = config.version || 0
22
- this.app = path.join(this.dir, 'pear-runtime/upgrade', isIOS ? 'runtime.ios.bundle' : 'runtime.android.bundle')
23
- this.name = isIOS ? 'runtime.ios.bundle' : 'runtime.android.bundle'
24
- this.key = hid.decode(upgrade.key)
25
- this.length = upgrade.length || 0
26
- this.fork = upgrade.fork || 0
27
- this.link = plink.serialize({ drive: { fork: this.fork, length: this.length, key: this.key }})
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.storage = opts.storage || path.join(this.dir, 'app-storage')
36
19
 
37
- this.ready().catch(noop)
20
+ this.updater = new PearRuntimeUpdater(opts)
38
21
  }
39
22
 
40
23
  async _open() {
41
- await this.drive.ready()
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())
24
+ await this.updater.ready()
57
25
  }
58
26
 
59
27
  async _close() {
60
- await this.drive.close()
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()
28
+ await this.updater.close()
123
29
  }
124
30
  }
125
-
126
- function noop() {}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pear-mobile",
3
- "version": "1.0.0-rc",
3
+ "version": "1.0.0",
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
- "corestore": "^7.8.0",
43
- "fs-native-extensions": "^1.4.5",
44
- "hypercore-id-encoding": "^1.3.0",
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
  }