@blazium/ton-connect-mobile 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.
- package/LICENSE +22 -0
- package/README.md +271 -0
- package/dist/adapters/expo.d.ts +25 -0
- package/dist/adapters/expo.js +145 -0
- package/dist/adapters/index.d.ts +6 -0
- package/dist/adapters/index.js +12 -0
- package/dist/adapters/react-native.d.ts +25 -0
- package/dist/adapters/react-native.js +133 -0
- package/dist/adapters/web.d.ts +27 -0
- package/dist/adapters/web.js +147 -0
- package/dist/core/crypto.d.ts +28 -0
- package/dist/core/crypto.js +183 -0
- package/dist/core/index.d.ts +5 -0
- package/dist/core/index.js +21 -0
- package/dist/core/protocol.d.ts +50 -0
- package/dist/core/protocol.js +260 -0
- package/dist/index.d.ts +112 -0
- package/dist/index.js +502 -0
- package/dist/types/index.d.ts +192 -0
- package/dist/types/index.js +6 -0
- package/package.json +57 -0
- package/src/adapters/expo.ts +160 -0
- package/src/adapters/index.ts +8 -0
- package/src/adapters/react-native.ts +148 -0
- package/src/adapters/web.ts +176 -0
- package/src/core/crypto.ts +238 -0
- package/src/core/index.ts +7 -0
- package/src/core/protocol.ts +330 -0
- package/src/index.d.ts +19 -0
- package/src/index.ts +578 -0
- package/src/types/index.ts +206 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
package/README.md
ADDED
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
# TON Connect Mobile SDK
|
|
2
|
+
|
|
3
|
+
Production-ready TON Connect Mobile SDK for React Native and Expo. This SDK implements the real TonConnect protocol for mobile applications using deep links and callbacks.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- ✅ **Real TonConnect Protocol** - Implements the actual protocol, not a mock
|
|
8
|
+
- ✅ **Deep Link Wallet Connection** - Connect to wallets via deep links
|
|
9
|
+
- ✅ **Session Persistence** - Maintains connection across app restarts
|
|
10
|
+
- ✅ **Transaction Signing** - Send and sign transactions
|
|
11
|
+
- ✅ **Signature Verification** - Verifies wallet signatures
|
|
12
|
+
- ✅ **Cross-Platform** - Works with Expo Managed, Expo Bare, and React Native CLI
|
|
13
|
+
- ✅ **TypeScript** - Fully typed with TypeScript
|
|
14
|
+
- ✅ **Production Ready** - No placeholders, no mocks, ready for production use
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install @blazium/ton-connect-mobile
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### Required Dependencies
|
|
23
|
+
|
|
24
|
+
**IMPORTANT**: You must install `react-native-get-random-values` for secure random number generation:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npm install react-native-get-random-values
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Then import it at the very top of your entry file (before any other imports):
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
import 'react-native-get-random-values';
|
|
34
|
+
// ... other imports
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Peer Dependencies
|
|
38
|
+
|
|
39
|
+
The SDK requires one of the following storage solutions:
|
|
40
|
+
|
|
41
|
+
- `@react-native-async-storage/async-storage` (for React Native CLI)
|
|
42
|
+
- Expo's built-in AsyncStorage (for Expo)
|
|
43
|
+
|
|
44
|
+
For Expo projects, also install:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
npx expo install expo-linking expo-crypto @react-native-async-storage/async-storage
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Usage
|
|
51
|
+
|
|
52
|
+
### Basic Setup
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
import { TonConnectMobile } from '@blazium/ton-connect-mobile';
|
|
56
|
+
|
|
57
|
+
const ton = new TonConnectMobile({
|
|
58
|
+
manifestUrl: 'https://example.com/tonconnect-manifest.json',
|
|
59
|
+
scheme: 'myapp', // Your app's deep link scheme
|
|
60
|
+
});
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Connect to Wallet
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
try {
|
|
67
|
+
const wallet = await ton.connect();
|
|
68
|
+
console.log('Connected to:', wallet.name);
|
|
69
|
+
console.log('Address:', wallet.address);
|
|
70
|
+
console.log('Public Key:', wallet.publicKey);
|
|
71
|
+
} catch (error) {
|
|
72
|
+
if (error instanceof UserRejectedError) {
|
|
73
|
+
console.log('User rejected the connection');
|
|
74
|
+
} else if (error instanceof ConnectionTimeoutError) {
|
|
75
|
+
console.log('Connection timed out');
|
|
76
|
+
} else {
|
|
77
|
+
console.error('Connection failed:', error.message);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Listen to Status Changes
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
ton.onStatusChange((status) => {
|
|
86
|
+
if (status.connected) {
|
|
87
|
+
console.log('Wallet:', status.wallet?.name);
|
|
88
|
+
console.log('Address:', status.wallet?.address);
|
|
89
|
+
} else {
|
|
90
|
+
console.log('Disconnected');
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Send Transaction
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
try {
|
|
99
|
+
const response = await ton.sendTransaction({
|
|
100
|
+
validUntil: Date.now() + 5 * 60 * 1000, // 5 minutes
|
|
101
|
+
messages: [
|
|
102
|
+
{
|
|
103
|
+
address: 'EQD0vdSA_NedR9uvbgN9EikRX-suesDxGeFg69XQMavfLqIo',
|
|
104
|
+
amount: '10000000', // 0.01 TON in nanotons
|
|
105
|
+
},
|
|
106
|
+
],
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
console.log('Transaction BOC:', response.boc);
|
|
110
|
+
console.log('Signature:', response.signature);
|
|
111
|
+
|
|
112
|
+
// IMPORTANT: Transaction signatures must be verified server-side
|
|
113
|
+
// The SDK does not perform cryptographic signature verification
|
|
114
|
+
// Use @ton/core or @ton/crypto on your backend to verify signatures
|
|
115
|
+
} catch (error) {
|
|
116
|
+
if (error instanceof UserRejectedError) {
|
|
117
|
+
console.log('User rejected the transaction');
|
|
118
|
+
} else if (error instanceof TransactionTimeoutError) {
|
|
119
|
+
console.log('Transaction timed out');
|
|
120
|
+
} else if (error instanceof TransactionInProgressError) {
|
|
121
|
+
console.log('Transaction already in progress');
|
|
122
|
+
} else {
|
|
123
|
+
console.error('Transaction failed:', error.message);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Disconnect
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
await ton.disconnect();
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Configuration
|
|
135
|
+
|
|
136
|
+
### Expo Setup
|
|
137
|
+
|
|
138
|
+
In your `app.json` or `app.config.js`, configure the deep link scheme:
|
|
139
|
+
|
|
140
|
+
```json
|
|
141
|
+
{
|
|
142
|
+
"expo": {
|
|
143
|
+
"scheme": "myapp"
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### React Native CLI Setup
|
|
149
|
+
|
|
150
|
+
For iOS, add to `ios/YourApp/Info.plist`:
|
|
151
|
+
|
|
152
|
+
```xml
|
|
153
|
+
<key>CFBundleURLTypes</key>
|
|
154
|
+
<array>
|
|
155
|
+
<dict>
|
|
156
|
+
<key>CFBundleURLSchemes</key>
|
|
157
|
+
<array>
|
|
158
|
+
<string>myapp</string>
|
|
159
|
+
</array>
|
|
160
|
+
</dict>
|
|
161
|
+
</array>
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
For Android, add to `android/app/src/main/AndroidManifest.xml`:
|
|
165
|
+
|
|
166
|
+
```xml
|
|
167
|
+
<activity>
|
|
168
|
+
<intent-filter>
|
|
169
|
+
<action android:name="android.intent.action.VIEW" />
|
|
170
|
+
<category android:name="android.intent.category.DEFAULT" />
|
|
171
|
+
<category android:name="android.intent.category.BROWSABLE" />
|
|
172
|
+
<data android:scheme="myapp" />
|
|
173
|
+
</intent-filter>
|
|
174
|
+
</activity>
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## Manifest File
|
|
178
|
+
|
|
179
|
+
Create a `tonconnect-manifest.json` file on your server with the following structure:
|
|
180
|
+
|
|
181
|
+
```json
|
|
182
|
+
{
|
|
183
|
+
"url": "https://yourdomain.com",
|
|
184
|
+
"name": "Your App Name",
|
|
185
|
+
"iconUrl": "https://yourdomain.com/icon.png"
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
The manifest URL must be accessible via HTTPS.
|
|
190
|
+
|
|
191
|
+
## API Reference
|
|
192
|
+
|
|
193
|
+
### `TonConnectMobile`
|
|
194
|
+
|
|
195
|
+
Main SDK class.
|
|
196
|
+
|
|
197
|
+
#### Constructor
|
|
198
|
+
|
|
199
|
+
```typescript
|
|
200
|
+
new TonConnectMobile(config: TonConnectMobileConfig)
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
**Config Options:**
|
|
204
|
+
|
|
205
|
+
- `manifestUrl` (required): URL to your TonConnect manifest file
|
|
206
|
+
- `scheme` (required): Your app's deep link scheme
|
|
207
|
+
- `storageKeyPrefix` (optional): Prefix for storage keys (default: `'tonconnect_'`)
|
|
208
|
+
- `connectionTimeout` (optional): Connection timeout in ms (default: `300000`)
|
|
209
|
+
- `transactionTimeout` (optional): Transaction timeout in ms (default: `300000`)
|
|
210
|
+
|
|
211
|
+
#### Methods
|
|
212
|
+
|
|
213
|
+
- `connect(): Promise<WalletInfo>` - Connect to a wallet
|
|
214
|
+
- `disconnect(): Promise<void>` - Disconnect from wallet
|
|
215
|
+
- `sendTransaction(request: SendTransactionRequest): Promise<TransactionResponse>` - Send transaction
|
|
216
|
+
- `getStatus(): ConnectionStatus` - Get current connection status
|
|
217
|
+
- `onStatusChange(callback: StatusChangeCallback): () => void` - Subscribe to status changes
|
|
218
|
+
- `destroy(): void` - Cleanup resources
|
|
219
|
+
|
|
220
|
+
### Error Classes
|
|
221
|
+
|
|
222
|
+
- `TonConnectError` - Base error class
|
|
223
|
+
- `ConnectionTimeoutError` - Connection request timed out
|
|
224
|
+
- `ConnectionInProgressError` - Connection request already in progress
|
|
225
|
+
- `TransactionTimeoutError` - Transaction request timed out
|
|
226
|
+
- `TransactionInProgressError` - Transaction request already in progress
|
|
227
|
+
- `UserRejectedError` - User rejected the request
|
|
228
|
+
|
|
229
|
+
## Architecture
|
|
230
|
+
|
|
231
|
+
The SDK uses an adapter-based architecture:
|
|
232
|
+
|
|
233
|
+
- **Core** - Pure TypeScript protocol implementation (no platform dependencies)
|
|
234
|
+
- **Expo Adapter** - Expo-specific implementation using `expo-linking` and `expo-crypto`
|
|
235
|
+
- **React Native Adapter** - React Native CLI implementation using `react-native` Linking
|
|
236
|
+
|
|
237
|
+
The core module is platform-agnostic and can be used in any JavaScript environment.
|
|
238
|
+
|
|
239
|
+
## Example
|
|
240
|
+
|
|
241
|
+
See the `example/` directory for a complete Expo example app demonstrating:
|
|
242
|
+
|
|
243
|
+
- Connecting to a wallet
|
|
244
|
+
- Receiving wallet information
|
|
245
|
+
- Sending transactions
|
|
246
|
+
- Handling errors
|
|
247
|
+
|
|
248
|
+
## Security Notes
|
|
249
|
+
|
|
250
|
+
### Transaction Signature Verification
|
|
251
|
+
|
|
252
|
+
⚠️ **IMPORTANT**: The SDK does NOT perform cryptographic verification of transaction signatures. The `verifyTransactionSignature()` function only validates format, not cryptographic correctness.
|
|
253
|
+
|
|
254
|
+
**You MUST verify transaction signatures server-side** using proper TON libraries:
|
|
255
|
+
- `@ton/core` - For BOC parsing and transaction verification
|
|
256
|
+
- `@ton/crypto` - For cryptographic operations
|
|
257
|
+
|
|
258
|
+
### Random Number Generation
|
|
259
|
+
|
|
260
|
+
The SDK requires `react-native-get-random-values` for cryptographically secure random number generation. Make sure to import it at the top of your entry file.
|
|
261
|
+
|
|
262
|
+
### Session Storage
|
|
263
|
+
|
|
264
|
+
Sessions are stored in AsyncStorage (unencrypted). For production apps handling sensitive data, consider:
|
|
265
|
+
- Using encrypted storage (iOS Keychain, Android EncryptedSharedPreferences)
|
|
266
|
+
- Implementing additional security measures for sensitive wallet data
|
|
267
|
+
|
|
268
|
+
## License
|
|
269
|
+
|
|
270
|
+
MIT
|
|
271
|
+
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Expo platform adapter
|
|
3
|
+
* Handles deep linking, storage, and crypto for Expo environments
|
|
4
|
+
*/
|
|
5
|
+
import { PlatformAdapter } from '../types';
|
|
6
|
+
/**
|
|
7
|
+
* Expo platform adapter implementation
|
|
8
|
+
*/
|
|
9
|
+
export declare class ExpoAdapter implements PlatformAdapter {
|
|
10
|
+
private urlListeners;
|
|
11
|
+
private subscription;
|
|
12
|
+
constructor();
|
|
13
|
+
private setupURLListener;
|
|
14
|
+
openURL(url: string): Promise<boolean>;
|
|
15
|
+
getInitialURL(): Promise<string | null>;
|
|
16
|
+
addURLListener(callback: (url: string) => void): () => void;
|
|
17
|
+
setItem(key: string, value: string): Promise<void>;
|
|
18
|
+
getItem(key: string): Promise<string | null>;
|
|
19
|
+
removeItem(key: string): Promise<void>;
|
|
20
|
+
randomBytes(length: number): Promise<Uint8Array>;
|
|
21
|
+
/**
|
|
22
|
+
* Cleanup resources
|
|
23
|
+
*/
|
|
24
|
+
destroy(): void;
|
|
25
|
+
}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Expo platform adapter
|
|
4
|
+
* Handles deep linking, storage, and crypto for Expo environments
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.ExpoAdapter = void 0;
|
|
8
|
+
// Dynamic imports to handle optional dependencies
|
|
9
|
+
let Linking;
|
|
10
|
+
let Crypto;
|
|
11
|
+
let AsyncStorage;
|
|
12
|
+
try {
|
|
13
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
14
|
+
Linking = require('expo-linking');
|
|
15
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
16
|
+
Crypto = require('expo-crypto');
|
|
17
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
18
|
+
AsyncStorage = require('@react-native-async-storage/async-storage').default;
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
// Dependencies not available
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Expo platform adapter implementation
|
|
25
|
+
*/
|
|
26
|
+
class ExpoAdapter {
|
|
27
|
+
constructor() {
|
|
28
|
+
this.urlListeners = [];
|
|
29
|
+
this.subscription = null;
|
|
30
|
+
// Set up URL listener
|
|
31
|
+
this.setupURLListener();
|
|
32
|
+
}
|
|
33
|
+
setupURLListener() {
|
|
34
|
+
if (!Linking) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
// Listen for deep links when app is already open
|
|
38
|
+
this.subscription = Linking.addEventListener('url', (event) => {
|
|
39
|
+
this.urlListeners.forEach((listener) => listener(event.url));
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
async openURL(url) {
|
|
43
|
+
if (!Linking) {
|
|
44
|
+
throw new Error('expo-linking is not available');
|
|
45
|
+
}
|
|
46
|
+
try {
|
|
47
|
+
const canOpen = await Linking.canOpenURL(url);
|
|
48
|
+
if (!canOpen) {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
await Linking.openURL(url);
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
async getInitialURL() {
|
|
59
|
+
if (!Linking) {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
try {
|
|
63
|
+
return await Linking.getInitialURL();
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
addURLListener(callback) {
|
|
70
|
+
this.urlListeners.push(callback);
|
|
71
|
+
// Return unsubscribe function
|
|
72
|
+
return () => {
|
|
73
|
+
const index = this.urlListeners.indexOf(callback);
|
|
74
|
+
if (index > -1) {
|
|
75
|
+
this.urlListeners.splice(index, 1);
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
async setItem(key, value) {
|
|
80
|
+
if (!AsyncStorage) {
|
|
81
|
+
throw new Error('@react-native-async-storage/async-storage is not available');
|
|
82
|
+
}
|
|
83
|
+
try {
|
|
84
|
+
await AsyncStorage.setItem(key, value);
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
throw new Error(`Failed to set storage item: ${error?.message || error}`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
async getItem(key) {
|
|
91
|
+
if (!AsyncStorage) {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
try {
|
|
95
|
+
return await AsyncStorage.getItem(key);
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
async removeItem(key) {
|
|
102
|
+
if (!AsyncStorage) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
try {
|
|
106
|
+
await AsyncStorage.removeItem(key);
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
// Ignore errors on remove
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
async randomBytes(length) {
|
|
113
|
+
if (Crypto) {
|
|
114
|
+
try {
|
|
115
|
+
// Use expo-crypto for random bytes
|
|
116
|
+
const randomString = await Crypto.getRandomBytesAsync(length);
|
|
117
|
+
return randomString;
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
// Fall through to crypto.getRandomValues check
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
// HIGH FIX: Try crypto.getRandomValues as fallback
|
|
124
|
+
// eslint-disable-next-line no-undef
|
|
125
|
+
if (typeof globalThis !== 'undefined' && globalThis.crypto && globalThis.crypto.getRandomValues) {
|
|
126
|
+
const bytes = new Uint8Array(length);
|
|
127
|
+
globalThis.crypto.getRandomValues(bytes);
|
|
128
|
+
return bytes;
|
|
129
|
+
}
|
|
130
|
+
// HIGH FIX: Throw error instead of using insecure Math.random()
|
|
131
|
+
throw new Error('Cryptographically secure random number generation not available. ' +
|
|
132
|
+
'Please ensure expo-crypto is installed or use react-native-get-random-values');
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Cleanup resources
|
|
136
|
+
*/
|
|
137
|
+
destroy() {
|
|
138
|
+
if (this.subscription) {
|
|
139
|
+
this.subscription.remove();
|
|
140
|
+
this.subscription = null;
|
|
141
|
+
}
|
|
142
|
+
this.urlListeners = [];
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
exports.ExpoAdapter = ExpoAdapter;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Platform adapter exports
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.WebAdapter = exports.ReactNativeAdapter = exports.ExpoAdapter = void 0;
|
|
7
|
+
var expo_1 = require("./expo");
|
|
8
|
+
Object.defineProperty(exports, "ExpoAdapter", { enumerable: true, get: function () { return expo_1.ExpoAdapter; } });
|
|
9
|
+
var react_native_1 = require("./react-native");
|
|
10
|
+
Object.defineProperty(exports, "ReactNativeAdapter", { enumerable: true, get: function () { return react_native_1.ReactNativeAdapter; } });
|
|
11
|
+
var web_1 = require("./web");
|
|
12
|
+
Object.defineProperty(exports, "WebAdapter", { enumerable: true, get: function () { return web_1.WebAdapter; } });
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* React Native platform adapter
|
|
3
|
+
* Handles deep linking and storage for React Native CLI environments
|
|
4
|
+
*/
|
|
5
|
+
import { PlatformAdapter } from '../types';
|
|
6
|
+
/**
|
|
7
|
+
* React Native platform adapter implementation
|
|
8
|
+
*/
|
|
9
|
+
export declare class ReactNativeAdapter implements PlatformAdapter {
|
|
10
|
+
private urlListeners;
|
|
11
|
+
private subscription;
|
|
12
|
+
constructor();
|
|
13
|
+
private setupURLListener;
|
|
14
|
+
openURL(url: string): Promise<boolean>;
|
|
15
|
+
getInitialURL(): Promise<string | null>;
|
|
16
|
+
addURLListener(callback: (url: string) => void): () => void;
|
|
17
|
+
setItem(key: string, value: string): Promise<void>;
|
|
18
|
+
getItem(key: string): Promise<string | null>;
|
|
19
|
+
removeItem(key: string): Promise<void>;
|
|
20
|
+
randomBytes(length: number): Promise<Uint8Array>;
|
|
21
|
+
/**
|
|
22
|
+
* Cleanup resources
|
|
23
|
+
*/
|
|
24
|
+
destroy(): void;
|
|
25
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* React Native platform adapter
|
|
4
|
+
* Handles deep linking and storage for React Native CLI environments
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.ReactNativeAdapter = void 0;
|
|
8
|
+
// Dynamic imports to handle optional dependencies
|
|
9
|
+
let Linking;
|
|
10
|
+
let AsyncStorage;
|
|
11
|
+
try {
|
|
12
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
13
|
+
const RN = require('react-native');
|
|
14
|
+
Linking = RN.Linking;
|
|
15
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
16
|
+
AsyncStorage = require('@react-native-async-storage/async-storage').default;
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
// Dependencies not available
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* React Native platform adapter implementation
|
|
23
|
+
*/
|
|
24
|
+
class ReactNativeAdapter {
|
|
25
|
+
constructor() {
|
|
26
|
+
this.urlListeners = [];
|
|
27
|
+
this.subscription = null;
|
|
28
|
+
// Set up URL listener
|
|
29
|
+
this.setupURLListener();
|
|
30
|
+
}
|
|
31
|
+
setupURLListener() {
|
|
32
|
+
if (!Linking) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
// Listen for deep links when app is already open
|
|
36
|
+
this.subscription = Linking.addEventListener('url', (event) => {
|
|
37
|
+
this.urlListeners.forEach((listener) => listener(event.url));
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
async openURL(url) {
|
|
41
|
+
if (!Linking) {
|
|
42
|
+
throw new Error('react-native Linking is not available');
|
|
43
|
+
}
|
|
44
|
+
try {
|
|
45
|
+
const canOpen = await Linking.canOpenURL(url);
|
|
46
|
+
if (!canOpen) {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
await Linking.openURL(url);
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
async getInitialURL() {
|
|
57
|
+
if (!Linking) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
try {
|
|
61
|
+
return await Linking.getInitialURL();
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
addURLListener(callback) {
|
|
68
|
+
this.urlListeners.push(callback);
|
|
69
|
+
// Return unsubscribe function
|
|
70
|
+
return () => {
|
|
71
|
+
const index = this.urlListeners.indexOf(callback);
|
|
72
|
+
if (index > -1) {
|
|
73
|
+
this.urlListeners.splice(index, 1);
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
async setItem(key, value) {
|
|
78
|
+
if (!AsyncStorage) {
|
|
79
|
+
throw new Error('@react-native-async-storage/async-storage is not available');
|
|
80
|
+
}
|
|
81
|
+
try {
|
|
82
|
+
await AsyncStorage.setItem(key, value);
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
throw new Error(`Failed to set storage item: ${error?.message || error}`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
async getItem(key) {
|
|
89
|
+
if (!AsyncStorage) {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
try {
|
|
93
|
+
return await AsyncStorage.getItem(key);
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
async removeItem(key) {
|
|
100
|
+
if (!AsyncStorage) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
try {
|
|
104
|
+
await AsyncStorage.removeItem(key);
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
// Ignore errors on remove
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
async randomBytes(length) {
|
|
111
|
+
// HIGH FIX: Use crypto.getRandomValues if available (with polyfill)
|
|
112
|
+
// eslint-disable-next-line no-undef
|
|
113
|
+
if (typeof globalThis !== 'undefined' && globalThis.crypto && globalThis.crypto.getRandomValues) {
|
|
114
|
+
const bytes = new Uint8Array(length);
|
|
115
|
+
globalThis.crypto.getRandomValues(bytes);
|
|
116
|
+
return bytes;
|
|
117
|
+
}
|
|
118
|
+
// HIGH FIX: Throw error instead of using insecure Math.random()
|
|
119
|
+
throw new Error('Cryptographically secure random number generation not available. ' +
|
|
120
|
+
'Please install react-native-get-random-values: npm install react-native-get-random-values');
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Cleanup resources
|
|
124
|
+
*/
|
|
125
|
+
destroy() {
|
|
126
|
+
if (this.subscription) {
|
|
127
|
+
this.subscription.remove();
|
|
128
|
+
this.subscription = null;
|
|
129
|
+
}
|
|
130
|
+
this.urlListeners = [];
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
exports.ReactNativeAdapter = ReactNativeAdapter;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Web platform adapter
|
|
3
|
+
* Handles deep linking and storage for web environments
|
|
4
|
+
*/
|
|
5
|
+
import { PlatformAdapter } from '../types';
|
|
6
|
+
/**
|
|
7
|
+
* Web platform adapter implementation
|
|
8
|
+
* Uses browser APIs for deep linking and localStorage for storage
|
|
9
|
+
*/
|
|
10
|
+
export declare class WebAdapter implements PlatformAdapter {
|
|
11
|
+
private urlListeners;
|
|
12
|
+
private isListening;
|
|
13
|
+
constructor();
|
|
14
|
+
private setupURLListener;
|
|
15
|
+
private handleURLChange;
|
|
16
|
+
openURL(url: string): Promise<boolean>;
|
|
17
|
+
getInitialURL(): Promise<string | null>;
|
|
18
|
+
addURLListener(callback: (url: string) => void): () => void;
|
|
19
|
+
setItem(key: string, value: string): Promise<void>;
|
|
20
|
+
getItem(key: string): Promise<string | null>;
|
|
21
|
+
removeItem(key: string): Promise<void>;
|
|
22
|
+
randomBytes(length: number): Promise<Uint8Array>;
|
|
23
|
+
/**
|
|
24
|
+
* Cleanup resources
|
|
25
|
+
*/
|
|
26
|
+
destroy(): void;
|
|
27
|
+
}
|