pear-mobile 0.1.1-rc → 1.0.0-rc

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 CHANGED
@@ -15,18 +15,25 @@ Requires `react-native-bare-kit` to be listed in project dependencies.
15
15
  ## Usage
16
16
 
17
17
  ```js
18
+ /* React Native */
18
19
  import PearRuntime from 'pear-mobile'
19
20
  import bundle from './worker.bundle.js'
20
21
  import { version, upgrade } from './package.json'
21
22
 
22
- const runtime = new PearRuntime({ version, upgrade })
23
+ const runtime = new PearRuntime()
24
+ const IPC = runtime.run('/worker.bundle', bundle, [runtime.dir])
25
+
26
+ /* Bare Worklet */
27
+ const PearRuntime = require('pear-runtime')
28
+ const { version, upgrade } = require('./package.json')
29
+
30
+ const dir = Bare.argv[0]
31
+
32
+ const runtime = new PearRuntime({version, upgrade, dir})
23
33
  runtime.on('updated', () => {
24
34
  runtime.applyUpdate()
35
+ conosle.log('restart for update')
25
36
  })
26
-
27
- const appStorage = runtime.storage
28
-
29
- const IPC = runtime.run('/worker.bundle', bundle, [appStorage])
30
37
  ```
31
38
 
32
39
  ## API
package/index.js CHANGED
@@ -1,109 +1,126 @@
1
- const { Worklet } = require('react-native-bare-kit') // NEED TO INSTALL IN ROOT PRJECT (version match)
2
- const bundle = require('./lib/pear.bundle.js')
3
- const RPC = require('bare-rpc')
4
- const RNFS = require('react-native-fs')
5
- const AsyncStorage = require('@react-native-async-storage/async-storage') // NEED TO INSTALL IN ROOT PRJECT (version match)
6
- const { DevSettings } = require('react-native') // NEED TO INSTALL IN ROOT PRJECT (version match)
7
- const b4a = require('b4a')
8
-
9
- module.exports = class PearRuntime {
10
- constructor(config = {}) {
11
- if (!config.upgrade) throw new Error('upgrade link required')
12
- this.config = {
13
- dir: `${RNFS.DocumentDirectoryPath}/pear-runtime`,
14
- isDev: __DEV__,
15
- ...config
16
- }
17
-
18
- this._listeners = Object.create(null)
19
- this._calledReady = null
20
-
21
- this.IPCPromise = this.ready().catch(noop)
1
+ /* global BareKit */
2
+
3
+ const Hyperswarm = require('hyperswarm')
4
+ const Hyperdrive = require('hyperdrive')
5
+ const Corestore = require('corestore')
6
+ const path = require('bare-path')
7
+ 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')
14
+
15
+ module.exports = class PearRuntime extends ReadyResource {
16
+ constructor(config) {
17
+ 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
36
+
37
+ this.ready().catch(noop)
22
38
  }
23
39
 
24
- ready() {
25
- if (this._calledReady) return this._calledReady
26
- this._calledReady = this._open()
27
- return this._calledReady
40
+ 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())
28
57
  }
29
58
 
30
- async _open() {
31
- const runtimeDir = `${RNFS.DocumentDirectoryPath}/pear-runtime`
32
- const storageDir = `${runtimeDir}/storage`
33
-
34
- await RNFS.mkdir(runtimeDir)
35
- await RNFS.mkdir(storageDir)
36
-
37
- const argv = [JSON.stringify(this.config)]
38
- const worklet = new Worklet()
39
- worklet.start('/pear.bundle', bundle, argv)
40
-
41
- return new RPC(worklet.IPC, async (req) => {
42
- if (req.command === 0) {
43
- const version = b4a.toString(req.data)
44
- console.log('received version form pearend:', version)
45
- this.emit('updated')
46
- }
47
- if (req.command === 1){
48
- const updateData = b4a.toString(req.data)
49
- console.log('[Update Diff]', updateData)
50
- }
51
- if (req.command === 2){
52
- const logString = b4a.toString(req.data)
53
- console.log('[pear-runtime]:', logString)
54
- }
55
- })
59
+ 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()
56
64
  }
57
65
 
58
- async applyUpdate(version) {
59
- await AsyncStorage.multiSet([
60
- ['updatePending', 'true'],
61
- ['updateConfirmed', 'false'],
62
- ['runtimeVersion', String(version ?? this.version)]
63
- ])
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
+ }
64
74
 
65
- DevSettings.reload()
75
+ await fsx.swap(path.join(this.next, this.name), this.app)
76
+ await fs.promises.rm(this.next, { recursive: true, force: true })
66
77
  }
67
78
 
68
- async close() {}
69
-
70
- on(event, callback) {
71
- if (!this._listeners[event]) this._listeners[event] = []
72
- this._listeners[event].push(callback)
73
- return this
79
+ _updateBackground() {
80
+ this._update().catch(noop)
74
81
  }
75
82
 
76
- off(event, callback) {
77
- if (!this._listeners[event]) return this
78
- if (callback) {
79
- this._listeners[event] =
80
- this._listeners[event].filter(fn => fn !== callback)
81
- } else {
82
- this._listeners[event] = []
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
83
101
  }
84
- return this
85
- }
86
102
 
87
- once(event, fn) {
88
- const wrap = (...args) => {
89
- this.off(event, wrap)
90
- fn(...args)
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)
91
109
  }
92
- return this.on(event, wrap)
93
- }
94
110
 
95
- emit(event, ...args) {
96
- const list = this._listeners[event]
97
- if (!list) return this
98
- for (const fn of list) fn(...args)
99
- return this
100
- }
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)
101
121
 
102
- run(filename, bundle, argv) {
103
- const worklet = new Worklet()
104
- worklet.start(filename, bundle, argv)
105
- return worklet.IPC
122
+ if (this.drive.core.length > length) this._updateBackground()
106
123
  }
107
124
  }
108
125
 
109
- function noop() {}
126
+ function noop() {}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pear-mobile",
3
- "version": "0.1.1-rc",
3
+ "version": "1.0.0-rc",
4
4
  "description": "Embeddable Pear runtime for mobile applications",
5
5
  "main": "index.js",
6
6
  "exports": {
@@ -14,10 +14,9 @@
14
14
  "package.json",
15
15
  "index.js",
16
16
  "index.d.ts",
17
- "lib/pear.bundle.js"
17
+ "index.rn.js"
18
18
  ],
19
19
  "scripts": {
20
- "build": "npx bare-pack --host ios --host android --linked --out ./lib/pear.bundle.js ./lib/pear.js",
21
20
  "format": "prettier . --write",
22
21
  "test": "prettier . --check && lunte && brittle-bare test/index.js",
23
22
  "lint": "lunte"
@@ -36,25 +35,21 @@
36
35
  "brittle": "^3.19.0",
37
36
  "lunte": "^1.2.0",
38
37
  "prettier": "^3.6.2",
39
- "prettier-config-holepunch": "^2.0.0",
40
- "bare-pack": "^2.0.0"
38
+ "prettier-config-holepunch": "^2.0.0"
41
39
  },
42
40
  "dependencies": {
43
- "b4a": "^1.7.4",
44
- "bare-rpc": "^1.1.0",
45
- "react-native-fs": "^2.20.0",
41
+ "bare-path": "^3.0.0",
42
+ "corestore": "^7.8.0",
43
+ "fs-native-extensions": "^1.4.5",
44
+ "hypercore-id-encoding": "^1.3.0",
46
45
  "hyperdrive": "^13.2.1",
47
46
  "hyperswarm": "^4.16.0",
48
47
  "localdrive": "^2.2.0",
49
48
  "pear-link": "^4.2.1",
50
- "bare-path": "^3.0.0",
51
- "corestore": "^7.8.0",
52
- "hypercore-id-encoding": "^1.3.0",
53
- "ready-resource": "^1.2.0"
49
+ "ready-resource": "^1.2.0",
50
+ "which-runtime": "^1.3.2"
54
51
  },
55
52
  "peerDependencies": {
56
- "react-native-bare-kit": "*",
57
- "@react-native-async-storage/async-storage": "*",
58
- "react-native": "*"
53
+ "react-native-bare-kit": "*"
59
54
  }
60
55
  }