react-native-pirate-wallet 0.2.1
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-MIT +21 -0
- package/README.md +537 -0
- package/android/build.gradle +82 -0
- package/android/consumer-rules.pro +1 -0
- package/android/src/main/AndroidManifest.xml +1 -0
- package/android/src/main/java/com/pirate/wallet/reactnative/PirateWalletReactNativeModule.kt +96 -0
- package/android/src/main/java/com/pirate/wallet/reactnative/PirateWalletReactNativePackage.kt +14 -0
- package/ios/PirateWalletReactNative.m +172 -0
- package/ios/PirateWalletReactNative.swift +40 -0
- package/package.json +64 -0
- package/react-native-pirate-wallet.podspec +19 -0
- package/react-native.config.js +10 -0
- package/scripts/assemble-ios-framework.js +124 -0
- package/scripts/resolve-android-packages.js +75 -0
- package/scripts/verify-package.js +128 -0
- package/src/index.d.ts +245 -0
- package/src/index.js +876 -0
- package/test/smoke.js +220 -0
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
package com.pirate.wallet.reactnative
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.Promise
|
|
4
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
5
|
+
import com.facebook.react.bridge.ReactContextBaseJavaModule
|
|
6
|
+
import com.facebook.react.bridge.ReactMethod
|
|
7
|
+
import java.io.File
|
|
8
|
+
import org.json.JSONObject
|
|
9
|
+
|
|
10
|
+
internal object NativeBridge {
|
|
11
|
+
init {
|
|
12
|
+
System.loadLibrary("pirate_ffi_native")
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
external fun invokeJson(requestJson: String, pretty: Boolean = false): String
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
class PirateWalletReactNativeModule(
|
|
19
|
+
reactContext: ReactApplicationContext,
|
|
20
|
+
) : ReactContextBaseJavaModule(reactContext) {
|
|
21
|
+
override fun getName(): String = "PirateWalletReactNative"
|
|
22
|
+
|
|
23
|
+
@ReactMethod
|
|
24
|
+
fun invoke(requestJson: String, pretty: Boolean, promise: Promise) {
|
|
25
|
+
try {
|
|
26
|
+
promise.resolve(NativeBridge.invokeJson(requestJson, pretty))
|
|
27
|
+
} catch (t: Throwable) {
|
|
28
|
+
promise.reject("PIRATE_WALLET_INVOKE_ERROR", t.message, t)
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
@ReactMethod
|
|
33
|
+
fun configureAccountStorage(
|
|
34
|
+
accountId: String,
|
|
35
|
+
passphrase: String,
|
|
36
|
+
storagePath: String?,
|
|
37
|
+
promise: Promise,
|
|
38
|
+
) {
|
|
39
|
+
try {
|
|
40
|
+
require(accountId.trim().isNotEmpty()) { "accountId must not be empty" }
|
|
41
|
+
require(passphrase.isNotEmpty()) { "passphrase must not be empty" }
|
|
42
|
+
|
|
43
|
+
val walletDbDir = accountStorageDirectory(reactApplicationContext, accountId, storagePath)
|
|
44
|
+
ensureDirectory(walletDbDir)
|
|
45
|
+
|
|
46
|
+
val requestJson = JSONObject()
|
|
47
|
+
.put("method", "configure_wallet_storage")
|
|
48
|
+
.put("base_dir", walletDbDir.absolutePath)
|
|
49
|
+
.put("passphrase", passphrase)
|
|
50
|
+
.toString()
|
|
51
|
+
|
|
52
|
+
promise.resolve(NativeBridge.invokeJson(requestJson, false))
|
|
53
|
+
} catch (t: Throwable) {
|
|
54
|
+
promise.reject("PIRATE_WALLET_CONFIGURE_STORAGE_ERROR", t.message, t)
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
private fun accountStorageDirectory(
|
|
59
|
+
context: ReactApplicationContext,
|
|
60
|
+
accountId: String,
|
|
61
|
+
storagePath: String?,
|
|
62
|
+
): File {
|
|
63
|
+
if (!storagePath.isNullOrBlank()) {
|
|
64
|
+
return File(storagePath)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
val accountsDir = File(File(context.filesDir, "pirate_wallet"), "accounts")
|
|
68
|
+
return File(accountsDir, sanitizeAccountId(accountId))
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
private fun ensureDirectory(walletDbDir: File) {
|
|
72
|
+
if (!walletDbDir.exists() && !walletDbDir.mkdirs()) {
|
|
73
|
+
throw IllegalStateException(
|
|
74
|
+
"Failed to create wallet database directory: ${walletDbDir.absolutePath}"
|
|
75
|
+
)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
private fun sanitizeAccountId(accountId: String): String {
|
|
80
|
+
val trimmed = accountId.trim()
|
|
81
|
+
require(trimmed.isNotEmpty()) { "accountId must not be empty" }
|
|
82
|
+
|
|
83
|
+
val sanitized = buildString {
|
|
84
|
+
for (char in trimmed) {
|
|
85
|
+
append(
|
|
86
|
+
if (char.isLetterOrDigit() || char == '_' || char == '-' || char == '.') {
|
|
87
|
+
char
|
|
88
|
+
} else {
|
|
89
|
+
'_'
|
|
90
|
+
}
|
|
91
|
+
)
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return sanitized.ifEmpty { "account" }
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
package com.pirate.wallet.reactnative
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.ReactPackage
|
|
4
|
+
import com.facebook.react.bridge.NativeModule
|
|
5
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
6
|
+
import com.facebook.react.uimanager.ViewManager
|
|
7
|
+
|
|
8
|
+
class PirateWalletReactNativePackage : ReactPackage {
|
|
9
|
+
override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> =
|
|
10
|
+
listOf(PirateWalletReactNativeModule(reactContext))
|
|
11
|
+
|
|
12
|
+
override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> =
|
|
13
|
+
emptyList()
|
|
14
|
+
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
#import <Foundation/Foundation.h>
|
|
2
|
+
#import <React/RCTBridgeModule.h>
|
|
3
|
+
@import PirateWalletNative;
|
|
4
|
+
|
|
5
|
+
@interface PirateWalletReactNative : NSObject <RCTBridgeModule>
|
|
6
|
+
@end
|
|
7
|
+
|
|
8
|
+
@implementation PirateWalletReactNative
|
|
9
|
+
|
|
10
|
+
RCT_EXPORT_MODULE();
|
|
11
|
+
|
|
12
|
+
+ (BOOL)requiresMainQueueSetup
|
|
13
|
+
{
|
|
14
|
+
return NO;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
RCT_REMAP_METHOD(invoke,
|
|
18
|
+
invoke:(NSString *)requestJson
|
|
19
|
+
pretty:(BOOL)pretty
|
|
20
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
21
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
22
|
+
{
|
|
23
|
+
const char *requestCString = [requestJson UTF8String];
|
|
24
|
+
if (requestCString == NULL) {
|
|
25
|
+
reject(@"PIRATE_WALLET_INVOKE_ERROR", @"Request string was not valid UTF-8.", nil);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
char *responsePtr = pirate_wallet_service_invoke_json(requestCString, pretty);
|
|
30
|
+
if (responsePtr == NULL) {
|
|
31
|
+
reject(@"PIRATE_WALLET_INVOKE_ERROR", @"Wallet service returned a null response.", nil);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
NSString *response = [NSString stringWithUTF8String:responsePtr];
|
|
36
|
+
pirate_wallet_service_free_string(responsePtr);
|
|
37
|
+
|
|
38
|
+
if (response == nil) {
|
|
39
|
+
reject(@"PIRATE_WALLET_INVOKE_ERROR", @"Wallet service returned invalid UTF-8.", nil);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
resolve(response);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
RCT_REMAP_METHOD(configureAccountStorage,
|
|
47
|
+
configureAccountStorage:(NSString *)accountId
|
|
48
|
+
passphrase:(NSString *)passphrase
|
|
49
|
+
storagePath:(NSString *)storagePath
|
|
50
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
51
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
52
|
+
{
|
|
53
|
+
if ((id)storagePath == [NSNull null]) {
|
|
54
|
+
storagePath = nil;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (accountId == nil || accountId.length == 0) {
|
|
58
|
+
reject(@"PIRATE_WALLET_CONFIGURE_STORAGE_ERROR", @"accountId must not be empty", nil);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
if (passphrase == nil || passphrase.length == 0) {
|
|
62
|
+
reject(@"PIRATE_WALLET_CONFIGURE_STORAGE_ERROR", @"passphrase must not be empty", nil);
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
NSError *error = nil;
|
|
67
|
+
NSString *baseDir = [self storagePathForAccountId:accountId storagePath:storagePath error:&error];
|
|
68
|
+
if (baseDir == nil) {
|
|
69
|
+
reject(@"PIRATE_WALLET_CONFIGURE_STORAGE_ERROR", error.localizedDescription, error);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
NSDictionary *request = @{
|
|
74
|
+
@"method": @"configure_wallet_storage",
|
|
75
|
+
@"base_dir": baseDir,
|
|
76
|
+
@"passphrase": passphrase
|
|
77
|
+
};
|
|
78
|
+
NSData *requestData = [NSJSONSerialization dataWithJSONObject:request options:0 error:&error];
|
|
79
|
+
if (requestData == nil) {
|
|
80
|
+
reject(@"PIRATE_WALLET_CONFIGURE_STORAGE_ERROR", error.localizedDescription, error);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
NSString *requestJson = [[NSString alloc] initWithData:requestData encoding:NSUTF8StringEncoding];
|
|
85
|
+
if (requestJson == nil) {
|
|
86
|
+
reject(@"PIRATE_WALLET_CONFIGURE_STORAGE_ERROR", @"Storage configuration request was not valid UTF-8.", nil);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const char *requestCString = [requestJson UTF8String];
|
|
91
|
+
char *responsePtr = pirate_wallet_service_invoke_json(requestCString, NO);
|
|
92
|
+
if (responsePtr == NULL) {
|
|
93
|
+
reject(@"PIRATE_WALLET_CONFIGURE_STORAGE_ERROR", @"Wallet service returned a null response.", nil);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
NSString *response = [NSString stringWithUTF8String:responsePtr];
|
|
98
|
+
pirate_wallet_service_free_string(responsePtr);
|
|
99
|
+
|
|
100
|
+
if (response == nil) {
|
|
101
|
+
reject(@"PIRATE_WALLET_CONFIGURE_STORAGE_ERROR", @"Wallet service returned invalid UTF-8.", nil);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
resolve(response);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
- (NSString *)storagePathForAccountId:(NSString *)accountId
|
|
109
|
+
storagePath:(NSString *)storagePath
|
|
110
|
+
error:(NSError **)error
|
|
111
|
+
{
|
|
112
|
+
if (storagePath != nil && storagePath.length > 0) {
|
|
113
|
+
return [self ensureStorageDirectory:storagePath error:error] ? storagePath : nil;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
NSString *sanitized = [self sanitizedAccountId:accountId];
|
|
117
|
+
if (sanitized.length == 0) {
|
|
118
|
+
if (error != nil) {
|
|
119
|
+
*error = [NSError errorWithDomain:@"PirateWalletReactNative"
|
|
120
|
+
code:1
|
|
121
|
+
userInfo:@{NSLocalizedDescriptionKey: @"accountId must not be empty"}];
|
|
122
|
+
}
|
|
123
|
+
return nil;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
NSArray<NSURL *> *urls = [[NSFileManager defaultManager] URLsForDirectory:NSApplicationSupportDirectory
|
|
127
|
+
inDomains:NSUserDomainMask];
|
|
128
|
+
NSURL *applicationSupport = urls.firstObject;
|
|
129
|
+
if (applicationSupport == nil) {
|
|
130
|
+
if (error != nil) {
|
|
131
|
+
*error = [NSError errorWithDomain:@"PirateWalletReactNative"
|
|
132
|
+
code:2
|
|
133
|
+
userInfo:@{NSLocalizedDescriptionKey: @"Application Support directory is unavailable"}];
|
|
134
|
+
}
|
|
135
|
+
return nil;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
NSURL *base = [[applicationSupport URLByAppendingPathComponent:@"PirateWallet" isDirectory:YES]
|
|
139
|
+
URLByAppendingPathComponent:@"accounts" isDirectory:YES];
|
|
140
|
+
NSString *path = [[base URLByAppendingPathComponent:sanitized isDirectory:YES] path];
|
|
141
|
+
return [self ensureStorageDirectory:path error:error] ? path : nil;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
- (BOOL)ensureStorageDirectory:(NSString *)path error:(NSError **)error
|
|
145
|
+
{
|
|
146
|
+
return [[NSFileManager defaultManager] createDirectoryAtPath:path
|
|
147
|
+
withIntermediateDirectories:YES
|
|
148
|
+
attributes:nil
|
|
149
|
+
error:error];
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
- (NSString *)sanitizedAccountId:(NSString *)accountId
|
|
153
|
+
{
|
|
154
|
+
NSString *trimmed = [accountId stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
|
|
155
|
+
if (trimmed.length == 0) {
|
|
156
|
+
return @"";
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
NSMutableString *result = [NSMutableString stringWithCapacity:trimmed.length];
|
|
160
|
+
NSCharacterSet *allowed = [NSCharacterSet characterSetWithCharactersInString:@"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789._-"];
|
|
161
|
+
for (NSUInteger index = 0; index < trimmed.length; index++) {
|
|
162
|
+
unichar character = [trimmed characterAtIndex:index];
|
|
163
|
+
if ([allowed characterIsMember:character]) {
|
|
164
|
+
[result appendFormat:@"%C", character];
|
|
165
|
+
} else {
|
|
166
|
+
[result appendString:@"_"];
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return result;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
@end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import PirateWalletNative
|
|
3
|
+
import React
|
|
4
|
+
|
|
5
|
+
@objc(PirateWalletReactNative)
|
|
6
|
+
final class PirateWalletReactNative: NSObject {
|
|
7
|
+
@objc
|
|
8
|
+
static func requiresMainQueueSetup() -> Bool {
|
|
9
|
+
false
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
@objc
|
|
13
|
+
func invoke(
|
|
14
|
+
_ requestJson: String,
|
|
15
|
+
pretty: Bool,
|
|
16
|
+
resolver resolve: @escaping RCTPromiseResolveBlock,
|
|
17
|
+
rejecter reject: @escaping RCTPromiseRejectBlock
|
|
18
|
+
) {
|
|
19
|
+
guard let request = requestJson.cString(using: .utf8) else {
|
|
20
|
+
reject("PIRATE_WALLET_INVOKE_ERROR", "Request string was not valid UTF-8.", nil)
|
|
21
|
+
return
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
let pointer = request.withUnsafeBufferPointer { buffer in
|
|
25
|
+
pirate_wallet_service_invoke_json(buffer.baseAddress, pretty)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
guard let pointer else {
|
|
29
|
+
reject("PIRATE_WALLET_INVOKE_ERROR", "Wallet service returned a null response.", nil)
|
|
30
|
+
return
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
defer {
|
|
34
|
+
pirate_wallet_service_free_string(pointer)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
let response = String(cString: pointer)
|
|
38
|
+
resolve(response)
|
|
39
|
+
}
|
|
40
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "react-native-pirate-wallet",
|
|
3
|
+
"version": "0.2.1",
|
|
4
|
+
"description": "React Native wrapper for the Pirate Unified Wallet native SDK surfaces",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"react-native",
|
|
7
|
+
"pirate-chain",
|
|
8
|
+
"cryptocurrency",
|
|
9
|
+
"wallet",
|
|
10
|
+
"privacy",
|
|
11
|
+
"shielded"
|
|
12
|
+
],
|
|
13
|
+
"homepage": "https://github.com/PirateNetwork/Pirate-Unified-Light-Wallet/tree/main/bindings/react-native-pirate-wallet#readme",
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/PirateNetwork/Pirate-Unified-Light-Wallet/issues"
|
|
16
|
+
},
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "https://github.com/PirateNetwork/Pirate-Unified-Light-Wallet.git",
|
|
20
|
+
"directory": "bindings/react-native-pirate-wallet"
|
|
21
|
+
},
|
|
22
|
+
"author": "Pirate Chain Contributors",
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"main": "src/index.js",
|
|
25
|
+
"types": "src/index.d.ts",
|
|
26
|
+
"files": [
|
|
27
|
+
"android/build.gradle",
|
|
28
|
+
"android/consumer-rules.pro",
|
|
29
|
+
"android/src/main/AndroidManifest.xml",
|
|
30
|
+
"android/src/main/java/",
|
|
31
|
+
"ios/PirateWalletReactNative.m",
|
|
32
|
+
"ios/PirateWalletReactNative.swift",
|
|
33
|
+
"src/",
|
|
34
|
+
"scripts/",
|
|
35
|
+
"test/",
|
|
36
|
+
"LICENSE-MIT",
|
|
37
|
+
"README.md",
|
|
38
|
+
"package.json",
|
|
39
|
+
"react-native.config.js",
|
|
40
|
+
"react-native-pirate-wallet.podspec"
|
|
41
|
+
],
|
|
42
|
+
"scripts": {
|
|
43
|
+
"test": "node test/smoke.js",
|
|
44
|
+
"verify:package": "node scripts/verify-package.js",
|
|
45
|
+
"prepare:native": "node scripts/assemble-ios-framework.js",
|
|
46
|
+
"postinstall": "npm run prepare:native",
|
|
47
|
+
"prepack": "node scripts/verify-package.js --publish-layout"
|
|
48
|
+
},
|
|
49
|
+
"engines": {
|
|
50
|
+
"node": ">=18"
|
|
51
|
+
},
|
|
52
|
+
"peerDependencies": {
|
|
53
|
+
"react-native": ">=0.71.0 <1.0.0"
|
|
54
|
+
},
|
|
55
|
+
"optionalDependencies": {
|
|
56
|
+
"react-native-pirate-wallet-android": "0.2.1",
|
|
57
|
+
"react-native-pirate-wallet-android-x86_64": "0.2.1",
|
|
58
|
+
"react-native-pirate-wallet-ios-device": "0.2.1",
|
|
59
|
+
"react-native-pirate-wallet-ios-simulator": "0.2.1"
|
|
60
|
+
},
|
|
61
|
+
"publishConfig": {
|
|
62
|
+
"access": "public"
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
require "json"
|
|
2
|
+
|
|
3
|
+
package = JSON.parse(File.read(File.join(__dir__, "package.json")))
|
|
4
|
+
|
|
5
|
+
Pod::Spec.new do |s|
|
|
6
|
+
s.name = package["name"]
|
|
7
|
+
s.version = package["version"]
|
|
8
|
+
s.summary = package["description"]
|
|
9
|
+
s.license = package["license"]
|
|
10
|
+
s.homepage = "https://github.com/piratenetwork/Pirate-Unified-Light-Wallet"
|
|
11
|
+
s.authors = "Pirate Chain Contributors"
|
|
12
|
+
|
|
13
|
+
s.platform = :ios, "15.0"
|
|
14
|
+
s.source = { :path => "." }
|
|
15
|
+
s.source_files = "ios/PirateWalletReactNative.m"
|
|
16
|
+
s.vendored_frameworks = "ios/Frameworks/PirateWalletNative.xcframework"
|
|
17
|
+
|
|
18
|
+
s.dependency "React-Core"
|
|
19
|
+
end
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
|
|
6
|
+
const packageRoot = path.resolve(__dirname, '..');
|
|
7
|
+
const frameworkName = 'PirateWalletNative.xcframework';
|
|
8
|
+
const iosPackages = [
|
|
9
|
+
{
|
|
10
|
+
name: 'react-native-pirate-wallet-ios-device',
|
|
11
|
+
slice: 'ios-arm64',
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
name: 'react-native-pirate-wallet-ios-simulator',
|
|
15
|
+
slice: 'ios-arm64_x86_64-simulator',
|
|
16
|
+
},
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
function candidatePackageJsonPaths(packageName) {
|
|
20
|
+
const candidates = [];
|
|
21
|
+
try {
|
|
22
|
+
candidates.push(
|
|
23
|
+
require.resolve(`${packageName}/package.json`, {
|
|
24
|
+
paths: [process.cwd(), packageRoot],
|
|
25
|
+
}),
|
|
26
|
+
);
|
|
27
|
+
} catch (_) {
|
|
28
|
+
// The monorepo package is resolved below before it has been published.
|
|
29
|
+
}
|
|
30
|
+
candidates.push(
|
|
31
|
+
path.resolve(packageRoot, '..', packageName, 'package.json'),
|
|
32
|
+
);
|
|
33
|
+
return [...new Set(candidates)];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function resolvePackage(packageName, expectedVersion) {
|
|
37
|
+
for (const packageJsonPath of candidatePackageJsonPaths(packageName)) {
|
|
38
|
+
if (!fs.statSync(packageJsonPath, {throwIfNoEntry: false})?.isFile()) {
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
42
|
+
if (packageJson.name !== packageName) {
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
if (packageJson.version !== expectedVersion) {
|
|
46
|
+
throw new Error(
|
|
47
|
+
`${packageName}@${packageJson.version} does not match ` +
|
|
48
|
+
`react-native-pirate-wallet@${expectedVersion}`,
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
return path.dirname(packageJsonPath);
|
|
52
|
+
}
|
|
53
|
+
throw new Error(
|
|
54
|
+
`${packageName}@${expectedVersion} is required to build ` +
|
|
55
|
+
'react-native-pirate-wallet for iOS',
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function linkTree(source, destination) {
|
|
60
|
+
fs.mkdirSync(destination, {recursive: true});
|
|
61
|
+
for (const entry of fs.readdirSync(source, {withFileTypes: true})) {
|
|
62
|
+
const sourcePath = path.join(source, entry.name);
|
|
63
|
+
const destinationPath = path.join(destination, entry.name);
|
|
64
|
+
if (entry.isDirectory()) {
|
|
65
|
+
linkTree(sourcePath, destinationPath);
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
if (!entry.isFile()) {
|
|
69
|
+
throw new Error(`Unsupported iOS package entry: ${sourcePath}`);
|
|
70
|
+
}
|
|
71
|
+
try {
|
|
72
|
+
fs.linkSync(sourcePath, destinationPath);
|
|
73
|
+
} catch (error) {
|
|
74
|
+
if (!['EXDEV', 'EPERM', 'EACCES', 'EMLINK'].includes(error.code)) {
|
|
75
|
+
throw error;
|
|
76
|
+
}
|
|
77
|
+
fs.copyFileSync(sourcePath, destinationPath);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function assemble() {
|
|
83
|
+
if (process.platform !== 'darwin' && !process.argv.includes('--force')) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const wrapperPackage = JSON.parse(
|
|
88
|
+
fs.readFileSync(path.join(packageRoot, 'package.json'), 'utf8'),
|
|
89
|
+
);
|
|
90
|
+
const resolved = iosPackages.map(({name, slice}) => {
|
|
91
|
+
const expectedVersion = wrapperPackage.optionalDependencies?.[name];
|
|
92
|
+
if (expectedVersion !== wrapperPackage.version) {
|
|
93
|
+
throw new Error(`${name} must use the exact wrapper version`);
|
|
94
|
+
}
|
|
95
|
+
const root = resolvePackage(name, expectedVersion);
|
|
96
|
+
return {
|
|
97
|
+
name,
|
|
98
|
+
slice,
|
|
99
|
+
framework: path.join(root, 'ios', 'Frameworks', frameworkName),
|
|
100
|
+
};
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
const infoPlists = resolved.map(({framework}) =>
|
|
104
|
+
fs.readFileSync(path.join(framework, 'Info.plist')),
|
|
105
|
+
);
|
|
106
|
+
if (!infoPlists.slice(1).every(data => data.equals(infoPlists[0]))) {
|
|
107
|
+
throw new Error('iOS binary packages contain different XCFramework metadata');
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const output = path.join(packageRoot, 'ios', 'Frameworks', frameworkName);
|
|
111
|
+
fs.rmSync(output, {recursive: true, force: true});
|
|
112
|
+
fs.mkdirSync(output, {recursive: true});
|
|
113
|
+
fs.writeFileSync(path.join(output, 'Info.plist'), infoPlists[0]);
|
|
114
|
+
|
|
115
|
+
for (const {name, slice, framework} of resolved) {
|
|
116
|
+
const source = path.join(framework, slice);
|
|
117
|
+
if (!fs.statSync(source, {throwIfNoEntry: false})?.isDirectory()) {
|
|
118
|
+
throw new Error(`${name} is missing XCFramework slice ${slice}`);
|
|
119
|
+
}
|
|
120
|
+
linkTree(source, path.join(output, slice));
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
assemble();
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
|
|
6
|
+
const androidPackageNames = [
|
|
7
|
+
'react-native-pirate-wallet-android',
|
|
8
|
+
'react-native-pirate-wallet-android-x86_64',
|
|
9
|
+
];
|
|
10
|
+
const packageRoot = path.resolve(__dirname, '..');
|
|
11
|
+
|
|
12
|
+
function candidatePackageJsonPaths(packageName) {
|
|
13
|
+
const candidates = [];
|
|
14
|
+
try {
|
|
15
|
+
candidates.push(
|
|
16
|
+
require.resolve(`${packageName}/package.json`, {
|
|
17
|
+
paths: [process.cwd(), packageRoot],
|
|
18
|
+
}),
|
|
19
|
+
);
|
|
20
|
+
} catch (_) {
|
|
21
|
+
// The monorepo package is resolved below before it has been published.
|
|
22
|
+
}
|
|
23
|
+
candidates.push(
|
|
24
|
+
path.resolve(packageRoot, '..', packageName, 'package.json'),
|
|
25
|
+
);
|
|
26
|
+
return [...new Set(candidates)];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function resolveAndroidJniLibsPaths() {
|
|
30
|
+
const wrapperPackage = JSON.parse(
|
|
31
|
+
fs.readFileSync(path.join(packageRoot, 'package.json'), 'utf8'),
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
return androidPackageNames.map(packageName => {
|
|
35
|
+
const expectedVersion =
|
|
36
|
+
wrapperPackage.optionalDependencies?.[packageName];
|
|
37
|
+
|
|
38
|
+
for (const packageJsonPath of candidatePackageJsonPaths(packageName)) {
|
|
39
|
+
if (!fs.statSync(packageJsonPath, {throwIfNoEntry: false})?.isFile()) {
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const androidPackage = JSON.parse(
|
|
44
|
+
fs.readFileSync(packageJsonPath, 'utf8'),
|
|
45
|
+
);
|
|
46
|
+
if (androidPackage.name !== packageName) {
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
if (androidPackage.version !== expectedVersion) {
|
|
50
|
+
throw new Error(
|
|
51
|
+
`${packageName}@${androidPackage.version} does not match ` +
|
|
52
|
+
`react-native-pirate-wallet@${wrapperPackage.version}`,
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const jniLibsPath = path.join(
|
|
57
|
+
path.dirname(packageJsonPath),
|
|
58
|
+
'android',
|
|
59
|
+
'src',
|
|
60
|
+
'main',
|
|
61
|
+
'jniLibs',
|
|
62
|
+
);
|
|
63
|
+
if (fs.statSync(jniLibsPath, {throwIfNoEntry: false})?.isDirectory()) {
|
|
64
|
+
return jniLibsPath;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
throw new Error(
|
|
69
|
+
`${packageName}@${expectedVersion} is required to build ` +
|
|
70
|
+
'react-native-pirate-wallet for Android',
|
|
71
|
+
);
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
module.exports = {resolveAndroidJniLibsPaths};
|