@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 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 Dependencies
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@1.0.0-beta.40
74
- npm install @spacesops/pear-wrk-wdk@1.1.1-beta.40 @spacesops/react-native-bare-kit@0.11.0-beta.40
75
- npm install @tetherto/wdk-react-native-secure-storage
76
- npm install react@">=18.0.0" react-native@">=0.70.0"
77
- npm install react-native-nitro-modules@">=0.35.0"
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
- `react-native-mmkv` (4.3.x) is bundled as a dependency; the host app must install **`react-native-nitro-modules` 0.35** so Android/iOS Nitro codegen (e.g. `HybridObject.CxxPart`) matches MMKV.
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
- **Host app notes (beta.40):**
93
+ ```bash
94
+ npx wdk-verify-addons
95
+ ```
83
96
 
84
- - You do **not** need `wire-worklet.js`, `relink-bare-addons.js`, or a direct `@spacesops/wdk-wallet-btc` dependency—bitcoin runs inside the pear worklet bundle.
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
- ### Step 2: Install from GitHub (if using source)
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
- npm install https://github.com/spacesops/wdk-react-native-core.git
102
+ rm -rf node_modules package-lock.json && npm install
92
103
  ```
93
104
 
94
- Or add to your `package.json`:
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
- "dependencies": {
99
- "@spacesops/wdk-react-native-core": "1.0.0-beta.40",
100
- "@spacesops/pear-wrk-wdk": "1.1.1-beta.40",
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.53",
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
- "@tetherto/wdk-react-native-secure-storage": "github:tetherto/wdk-react-native-secure-storage",
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
  }