@spacesops/wdk-react-native-core 1.0.0-beta.53 → 1.0.0-beta.55
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/AGENTS.md +23 -0
- package/README.md +46 -21
- package/bin/verify-addons.mjs +127 -0
- package/package.json +11 -7
package/AGENTS.md
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# AGENTS.md — @spacesops/wdk-react-native-core
|
|
2
|
+
|
|
3
|
+
The single package consuming apps install. It owns provider/hook/lifecycle code and, just as importantly, **pins the versions of `@spacesops/pear-wrk-wdk` and `@spacesops/react-native-bare-kit` that are known to work together**. Consumers must not install those directly.
|
|
4
|
+
|
|
5
|
+
## Release order
|
|
6
|
+
|
|
7
|
+
Publish upstream first, then bump here, then the app: `wdk-wallet-btc` → `pear-wrk-wdk` → `react-native-bare-kit` → **this** → app. Both `@spacesops/*` dependencies are exact pins, so **publishing this package before an upstream version exists makes it uninstallable** (`ETARGET / notarget`) — verify each pin resolves on npm before publishing.
|
|
8
|
+
|
|
9
|
+
Regenerate `package-lock.json` from scratch when bumping either pin. npm preserves existing lock entries instead of re-resolving, so an incremental install keeps an old addon version hoisted and nests the correct one beneath it — two copies, and two `.so` files in every consumer APK.
|
|
10
|
+
|
|
11
|
+
## Non-obvious invariants
|
|
12
|
+
|
|
13
|
+
**No Expo config plugin can live here.** This package is `"type": "module"` and Expo does a plain `require()` of `app.plugin.js`, so a CJS plugin throws `ERR_REQUIRE_ESM` at prebuild. The Android packaging plugin lives in `react-native-bare-kit`, which is CJS; consumers reference `"@spacesops/react-native-bare-kit"` in `plugins`. `bin/verify-addons.mjs` is fine as ESM.
|
|
14
|
+
|
|
15
|
+
**Do not add `@expo/*` packages as dependencies.** They use Expo-SDK-aligned versions (`@expo/config-plugins` is `54.x`, not `10.x`), so a guessed range installs a second incompatible copy. Use optional peers.
|
|
16
|
+
|
|
17
|
+
**Some things genuinely cannot be fixed from here.** npm `overrides` only work from the root manifest, so a bad transitive range in a dependency is the *app's* problem to override — document it in the README rather than attempting a fix. `@tetherto/wdk-react-native-secure-storage` targets Expo SDK 55 while apps on SDK 54 must override `expo-crypto` and `expo-local-authentication`.
|
|
18
|
+
|
|
19
|
+
**Never add a `postinstall` that builds.** It runs in every consumer install, needs TypeScript present, and `dist` already ships.
|
|
20
|
+
|
|
21
|
+
## Triage
|
|
22
|
+
|
|
23
|
+
Native addon failures (`ADDON_NOT_FOUND`, release-only crashes) belong to `react-native-bare-kit` — see its `TROUBLESHOOTING.md`. Failures scoped to one chain (a wrong or `undefined` address, `MODULE_NOT_FOUND` for a wallet file) come from the versions packed inside pear's bundle and need a pear release; the copies in an app's `node_modules` never execute.
|
package/README.md
CHANGED
|
@@ -65,45 +65,60 @@ function WalletScreen() {
|
|
|
65
65
|
|
|
66
66
|
## Installation
|
|
67
67
|
|
|
68
|
-
### Step 1: Install
|
|
69
|
-
|
|
70
|
-
This package is part of the **Spacesops beta.40 pipeline**. Install matching peer/runtime versions in your host app:
|
|
68
|
+
### Step 1: Install
|
|
71
69
|
|
|
72
70
|
```bash
|
|
73
|
-
npm install @spacesops/wdk-react-native-core
|
|
74
|
-
npm install
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
71
|
+
npm install @spacesops/wdk-react-native-core
|
|
72
|
+
npm install react-native-nitro-modules # >=0.35, required by react-native-mmkv
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
**Install this package only.** `@spacesops/pear-wrk-wdk` (the worklet bundle) and `@spacesops/react-native-bare-kit` (the native runtime) arrive as dependencies at versions known to work together. Adding them to your own `dependencies` lets your ranges drift from the set this package was tested against, which fails at runtime rather than at install.
|
|
76
|
+
|
|
77
|
+
For the same reason, **never add a bare addon package** (`bare-crypto`, `@buildonspark/spark-frost-bare-addon`, anything with `"addon": true`) to your `dependencies`. The worklet bundle hardcodes the exact addon versions it links, pear pins all of them, and a direct dependency at a different version gets linked into your APK as a second unused `.so`.
|
|
78
|
+
|
|
79
|
+
### Step 2: Add the Expo config plugin
|
|
80
|
+
|
|
81
|
+
```json
|
|
82
|
+
{ "expo": { "plugins": ["@spacesops/react-native-bare-kit"] } }
|
|
78
83
|
```
|
|
79
84
|
|
|
80
|
-
|
|
85
|
+
This is not optional on Android. Bare addons are `dlopen`ed by filename and the loader reads their symbol tables directly, so stripping their debug symbols corrupts them — and only some builds strip, so **without the plugin your debug builds work and your release builds crash**. The plugin keeps symbols for every addon it finds in the tree, discovering them the same way the linker does rather than matching a fixed list of names.
|
|
86
|
+
|
|
87
|
+
The plugin lives in `@spacesops/react-native-bare-kit` because the constraint is a property of the native runtime. It installs as a dependency of this package, so you do not need to add it to your own `dependencies` — only to `plugins` above.
|
|
88
|
+
|
|
89
|
+
Non-Expo apps must do the equivalent in `android/app/build.gradle`; see the `@spacesops/react-native-bare-kit` README.
|
|
90
|
+
|
|
91
|
+
### Step 3: Verify the native addons line up
|
|
81
92
|
|
|
82
|
-
|
|
93
|
+
```bash
|
|
94
|
+
npx wdk-verify-addons
|
|
95
|
+
```
|
|
83
96
|
|
|
84
|
-
|
|
85
|
-
- On **Android**, configure **`keepDebugSymbols`** for `libbare*.so` in your Expo app or config plugin (see `@spacesops/react-native-bare-kit` README).
|
|
86
|
-
- Include **`bitcoin`** in `networkConfigs` (electrum URLs, etc.) so it matches pear’s `networks: ["bitcoin"]`.
|
|
97
|
+
Every addon the worklet bundle links must exist on disk at that exact version. Addon linking itself needs no setup — bare-kit's Gradle `preBuild` runs it on every Android build.
|
|
87
98
|
|
|
88
|
-
|
|
99
|
+
If it reports a mismatch, the usual cause is a **stale lockfile**. npm preserves existing lock entries instead of re-resolving, so an incremental install can keep an older addon version hoisted and nest the correct one beneath it, putting two copies in your APK. Re-resolve from scratch:
|
|
89
100
|
|
|
90
101
|
```bash
|
|
91
|
-
|
|
102
|
+
rm -rf node_modules package-lock.json && npm install
|
|
92
103
|
```
|
|
93
104
|
|
|
94
|
-
|
|
105
|
+
`bare-posix` is reported as having no Android prebuild. That is expected — its `exports` map `android` to `unsupported.js`, so nothing can ever link it.
|
|
106
|
+
|
|
107
|
+
### Expo SDK compatibility
|
|
108
|
+
|
|
109
|
+
`@tetherto/wdk-react-native-secure-storage` targets **Expo SDK 55** (`expo-crypto@^55`, `expo-local-authentication@^55`). On **SDK 54** those ranges resolve to packages your app cannot use, and npm `overrides` are the only mechanism that can correct a transitive range — no library can do it for you. Add to your app's `package.json`:
|
|
95
110
|
|
|
96
111
|
```json
|
|
97
112
|
{
|
|
98
|
-
"
|
|
99
|
-
"
|
|
100
|
-
"
|
|
101
|
-
"@spacesops/react-native-bare-kit": "0.11.0-beta.40",
|
|
102
|
-
"@tetherto/wdk-react-native-secure-storage": "github:tetherto/wdk-react-native-secure-storage"
|
|
113
|
+
"overrides": {
|
|
114
|
+
"expo-crypto": "~15.0.9",
|
|
115
|
+
"expo-local-authentication": "~17.0.8"
|
|
103
116
|
}
|
|
104
117
|
}
|
|
105
118
|
```
|
|
106
119
|
|
|
120
|
+
Drop these once you move to SDK 55 or later.
|
|
121
|
+
|
|
107
122
|
## Core Concepts
|
|
108
123
|
|
|
109
124
|
### WdkAppProvider
|
|
@@ -491,6 +506,16 @@ The `WdkAppProvider` uses a **consolidated effect** for wallet state synchroniza
|
|
|
491
506
|
|
|
492
507
|
## Troubleshooting
|
|
493
508
|
|
|
509
|
+
### Native addons fail to load (`ADDON_NOT_FOUND`, `dlopen` failures, release-only crashes)
|
|
510
|
+
|
|
511
|
+
Start with `npx wdk-verify-addons`, then see **`node_modules/@spacesops/react-native-bare-kit/TROUBLESHOOTING.md`**. A listed candidate in an `ADDON_NOT_FOUND` message means the file *was* found and `dlopen` failed, so it is a symbol problem rather than a missing or misnamed library — and bare truncates the real cause out of the log.
|
|
512
|
+
|
|
513
|
+
If it only reproduces in release builds, you are almost certainly missing the Expo config plugin from Step 2.
|
|
514
|
+
|
|
515
|
+
### Errors from a specific network after the worklet starts
|
|
516
|
+
|
|
517
|
+
Failures scoped to one chain (a wrong or `undefined` address, `MODULE_NOT_FOUND` for a wallet file) come from the versions packed inside the worklet bundle, not from your app. The same packages in your `node_modules` are used for types and never execute, so they can look correct while the device fails. These need a `@spacesops/pear-wrk-wdk` release; report them rather than patching locally.
|
|
518
|
+
|
|
494
519
|
### Wallet Initialization Fails
|
|
495
520
|
|
|
496
521
|
**Symptoms**: `status` is `ERROR`, `error` is set
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Verifies that every native addon the pear worklet bundle links is actually
|
|
4
|
+
* present on disk, at the exact version the bundle expects.
|
|
5
|
+
*
|
|
6
|
+
* Deliberately checks files rather than `addons-lock.json`: the lock records
|
|
7
|
+
* what link intended to write, so comparing against it can pass while a `.so`
|
|
8
|
+
* is missing.
|
|
9
|
+
*/
|
|
10
|
+
import fs from 'node:fs'
|
|
11
|
+
import path from 'node:path'
|
|
12
|
+
|
|
13
|
+
const ABIS = ['arm64-v8a', 'armeabi-v7a', 'x86', 'x86_64']
|
|
14
|
+
|
|
15
|
+
const root = process.cwd()
|
|
16
|
+
const req = (p) => path.join(root, 'node_modules', p)
|
|
17
|
+
|
|
18
|
+
const pearDir = req('@spacesops/pear-wrk-wdk')
|
|
19
|
+
const addonsDir = req('@spacesops/react-native-bare-kit/android/src/main/addons')
|
|
20
|
+
|
|
21
|
+
function fail(message, hint) {
|
|
22
|
+
console.error(`[verify-addons] ${message}`)
|
|
23
|
+
if (hint) console.error(`\n${hint}`)
|
|
24
|
+
process.exit(1)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function expectedAddons() {
|
|
28
|
+
const manifest = path.join(pearDir, 'generated/pear-linked-addons.json')
|
|
29
|
+
if (fs.existsSync(manifest)) {
|
|
30
|
+
return JSON.parse(fs.readFileSync(manifest, 'utf8')).linkedAddons
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Older pear releases did not ship the manifest.
|
|
34
|
+
const bundle = path.join(pearDir, 'generated/bundle/wdk-worklet.mobile.bundle.js')
|
|
35
|
+
if (!fs.existsSync(bundle)) {
|
|
36
|
+
fail(
|
|
37
|
+
'could not find the pear worklet bundle',
|
|
38
|
+
'Is @spacesops/pear-wrk-wdk installed? Expected:\n ' + bundle
|
|
39
|
+
)
|
|
40
|
+
}
|
|
41
|
+
const text = fs.readFileSync(bundle, 'utf8')
|
|
42
|
+
const names = [...text.matchAll(/linked\\?:lib[^"'\\]+/g)].map((m) =>
|
|
43
|
+
m[0].replace(/^linked\\?:/, '')
|
|
44
|
+
)
|
|
45
|
+
return [...new Set(names)].sort()
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* A missing `.so` is expected when the package ships no Android prebuild at
|
|
50
|
+
* all — `bare-posix` maps `android` to `unsupported.js` in its exports, so the
|
|
51
|
+
* bundle references it but nothing can ever link it.
|
|
52
|
+
*/
|
|
53
|
+
function shipsAndroidPrebuild(addonFile) {
|
|
54
|
+
const name = addonFile.replace(/^lib/, '').replace(/\.\d+\.\d+\.\d+\.so$/, '')
|
|
55
|
+
const pkg = name.includes('__') ? '@' + name.replace('__', '/') : name
|
|
56
|
+
const prebuilds = req(path.join(pkg, 'prebuilds'))
|
|
57
|
+
if (!fs.existsSync(prebuilds)) return false
|
|
58
|
+
return fs.readdirSync(prebuilds).some((d) => d.startsWith('android-'))
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const expected = expectedAddons()
|
|
62
|
+
if (expected.length === 0) fail('the pear bundle links no native addons, which cannot be right')
|
|
63
|
+
|
|
64
|
+
if (!fs.existsSync(addonsDir)) {
|
|
65
|
+
fail(
|
|
66
|
+
'no linked addons directory',
|
|
67
|
+
'bare-kit link has not run yet. It runs automatically on Android preBuild;\n' +
|
|
68
|
+
'to run it now:\n node node_modules/@spacesops/react-native-bare-kit/android/link.mjs'
|
|
69
|
+
)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
let failed = false
|
|
73
|
+
|
|
74
|
+
for (const abi of ABIS) {
|
|
75
|
+
const dir = path.join(addonsDir, abi)
|
|
76
|
+
if (!fs.existsSync(dir)) {
|
|
77
|
+
console.error(`[verify-addons] ${abi}: missing entirely`)
|
|
78
|
+
failed = true
|
|
79
|
+
continue
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const present = new Set(fs.readdirSync(dir).filter((f) => f.endsWith('.so')))
|
|
83
|
+
const missing = expected.filter((n) => !present.has(n))
|
|
84
|
+
|
|
85
|
+
const hard = missing.filter(shipsAndroidPrebuild)
|
|
86
|
+
const expectedAbsent = missing.filter((n) => !shipsAndroidPrebuild(n))
|
|
87
|
+
|
|
88
|
+
// Two versions of one addon means a duplicate package in the tree: the
|
|
89
|
+
// bundle only ever loads one, so the other is dead weight in the APK.
|
|
90
|
+
const byName = new Map()
|
|
91
|
+
for (const f of present) {
|
|
92
|
+
const base = f.replace(/\.\d+\.\d+\.\d+\.so$/, '')
|
|
93
|
+
byName.set(base, (byName.get(base) ?? 0) + 1)
|
|
94
|
+
}
|
|
95
|
+
const duplicated = [...byName].filter(([, n]) => n > 1).map(([b]) => b)
|
|
96
|
+
|
|
97
|
+
if (hard.length) {
|
|
98
|
+
console.error(`[verify-addons] ${abi}: ${hard.length} addon(s) the bundle needs are missing:`)
|
|
99
|
+
hard.forEach((n) => console.error(` - ${n}`))
|
|
100
|
+
failed = true
|
|
101
|
+
} else {
|
|
102
|
+
const notes = []
|
|
103
|
+
if (expectedAbsent.length) notes.push(`${expectedAbsent.length} with no Android prebuild`)
|
|
104
|
+
if (duplicated.length) notes.push(`${duplicated.length} duplicated`)
|
|
105
|
+
console.log(
|
|
106
|
+
`[verify-addons] ${abi}: ${expected.length - missing.length}/${expected.length} present` +
|
|
107
|
+
(notes.length ? ` (${notes.join(', ')})` : '')
|
|
108
|
+
)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
for (const n of duplicated) {
|
|
112
|
+
console.warn(`[verify-addons] ${abi}: ${n} is linked at more than one version`)
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (failed) {
|
|
117
|
+
fail(
|
|
118
|
+
'the installed tree does not match the pear bundle',
|
|
119
|
+
'pear pins every addon it links as an exact dependency, so a mismatch usually\n' +
|
|
120
|
+
'means a stale lockfile kept an older resolution. Re-resolve from scratch:\n' +
|
|
121
|
+
' rm -rf node_modules package-lock.json && npm install\n\n' +
|
|
122
|
+
'If it persists, the pear release is inconsistent — see\n' +
|
|
123
|
+
'node_modules/@spacesops/react-native-bare-kit/TROUBLESHOOTING.md'
|
|
124
|
+
)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
console.log('[verify-addons] OK')
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@spacesops/wdk-react-native-core",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.55",
|
|
4
4
|
"description": "Core functionality for React Native wallets - wallet management, balance fetching, and worklet operations",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -10,7 +10,9 @@
|
|
|
10
10
|
"dist",
|
|
11
11
|
"src",
|
|
12
12
|
"README.md",
|
|
13
|
-
"LICENSE"
|
|
13
|
+
"LICENSE",
|
|
14
|
+
"bin",
|
|
15
|
+
"AGENTS.md"
|
|
14
16
|
],
|
|
15
17
|
"exports": {
|
|
16
18
|
".": {
|
|
@@ -38,8 +40,7 @@
|
|
|
38
40
|
"test": "jest",
|
|
39
41
|
"test:watch": "jest --watch",
|
|
40
42
|
"test:coverage": "jest --coverage",
|
|
41
|
-
"prepack": "npm run build"
|
|
42
|
-
"postinstall": "npm run build"
|
|
43
|
+
"prepack": "npm run build"
|
|
43
44
|
},
|
|
44
45
|
"keywords": [
|
|
45
46
|
"wallet",
|
|
@@ -52,11 +53,11 @@
|
|
|
52
53
|
"author": "Spacesops",
|
|
53
54
|
"license": "Apache-2.0",
|
|
54
55
|
"dependencies": {
|
|
55
|
-
"@
|
|
56
|
+
"@spacesops/pear-wrk-wdk": "1.1.1-beta.44",
|
|
57
|
+
"@spacesops/react-native-bare-kit": "0.11.0-beta.49",
|
|
56
58
|
"@tanstack/react-query": "^5.0.0",
|
|
59
|
+
"@tetherto/wdk-react-native-secure-storage": "1.0.0-beta.5",
|
|
57
60
|
"expo-crypto": "^15.0.8",
|
|
58
|
-
"@spacesops/pear-wrk-wdk": "1.1.1-beta.43",
|
|
59
|
-
"@spacesops/react-native-bare-kit": "0.11.0-beta.48",
|
|
60
61
|
"react-native-mmkv": "^4.3.0",
|
|
61
62
|
"zod": "^3.22.0",
|
|
62
63
|
"zustand": "^5.0.9"
|
|
@@ -77,5 +78,8 @@
|
|
|
77
78
|
"react": "*",
|
|
78
79
|
"react-native": ">=0.70.0",
|
|
79
80
|
"react-native-nitro-modules": ">=0.35.0"
|
|
81
|
+
},
|
|
82
|
+
"bin": {
|
|
83
|
+
"wdk-verify-addons": "./bin/verify-addons.mjs"
|
|
80
84
|
}
|
|
81
85
|
}
|