Skip to content

Commit 632ee46

Browse files
committedAug 15, 2024·
chore: add comparisons
·
v0.2.3v0.2.0
1 parent ba82e53 commit 632ee46

File tree

11 files changed

+1012
-90
lines changed

11 files changed

+1012
-90
lines changed
 

‎README.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,30 @@ Next-gen React Native library for Bcrypt hashing using pure C++ with Turbo Modul
44

55
**_NOTE:_** This library can be used only with New Architecture (more information about New Architecture [here](https://github.com/reactwg/react-native-new-architecture))
66

7+
## Features
8+
9+
- **50x faster than JS implementation** 🚀
10+
- **Multithreaded for high performance without blocking the JS thread** 🧵
11+
- **Seamless integration with Turbo Modules** 🔌
12+
- **Native C++ hashing for maximum security** 🔒
13+
- **Supports both asynchronous and synchronous operations** ⚡️
14+
- **Optimized for React Native's New Architecture** 📱
15+
16+
## Performance
17+
18+
The C++ implementation of Bcrypt hashing is significantly faster than the JavaScript implementation, especially for high-cost factors. Here are some benchmarks comparing the two implementations:
19+
20+
![Comparisons](./assets/comparisons)
21+
22+
## Demo
23+
24+
After running "Generate Hash" function on JS side, it blocks JS Thread while the function runs (approximately 14 seconds). On the other hand, the C++ implementation runs the same function in a separate thread, allowing the JS thread to continue executing other tasks without blocking (approximately 0.3 seconds). This demonstrates the superior performance of the C++ implementation over the JavaScript implementation.
25+
26+
| JavaScript Demo | C++ Demo |
27+
| :---------------------------------------: | :-----------------------------------------: |
28+
| ![JS Demo](./assets/JS_GENERATE_HASH.gif) | ![C++ Demo](./assets/C++_GENERATE_HASH.gif) |
29+
| **JavaScript Hashing** | **C++ Hashing** |
30+
731
## Installation
832

933
```sh
@@ -16,6 +40,10 @@ or
1640
yarn add react-native-bcrypt-cpp
1741
```
1842

43+
### Linking
44+
45+
TBD
46+
1947
## Usage
2048

2149
### Asynchronous Hashing (Multithreaded)
@@ -101,6 +129,11 @@ Synchronously validates the given password against the Bcrypt hash.
101129

102130
- A `boolean` indicating whether the password is valid.
103131

132+
## Bcrypt Algorithm Source
133+
134+
This library implements the Bcrypt hashing algorithm in C++, adapted from the [Bcrypt.cpp project](https://github.com/hilch/Bcrypt.cpp?tab=License-1-ov-file) by Hilko Bengen.
135+
This product includes software developed by Niels Provos.
136+
104137
## Contributing
105138

106139
See the [contributing guide](CONTRIBUTING.md) to learn how to contribute to the repository and the development workflow.

‎assets/C++_GENERATE_HASH.gif

10.8 MB
Loading

‎assets/JS_GENERATE_HASH.gif

10.2 MB
Loading

‎assets/comparisons

102 KB
Binary file not shown.

‎example/ios/BcryptCppExample.xcodeproj/project.pbxproj

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -592,7 +592,10 @@
592592
"-DFOLLY_CFG_NO_COROUTINES=1",
593593
"-DFOLLY_HAVE_CLOCK_GETTIME=1",
594594
);
595-
OTHER_LDFLAGS = "$(inherited) ";
595+
OTHER_LDFLAGS = (
596+
"$(inherited)",
597+
" ",
598+
);
596599
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
597600
SDKROOT = iphoneos;
598601
USE_HERMES = true;
@@ -664,7 +667,10 @@
664667
"-DFOLLY_CFG_NO_COROUTINES=1",
665668
"-DFOLLY_HAVE_CLOCK_GETTIME=1",
666669
);
667-
OTHER_LDFLAGS = "$(inherited) ";
670+
OTHER_LDFLAGS = (
671+
"$(inherited)",
672+
" ",
673+
);
668674
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
669675
SDKROOT = iphoneos;
670676
USE_HERMES = true;

‎example/ios/Podfile.lock

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -956,6 +956,8 @@ PODS:
956956
- ReactCommon/turbomodule/bridging
957957
- ReactCommon/turbomodule/core
958958
- Yoga
959+
- react-native-randombytes (3.6.1):
960+
- React-Core
959961
- React-nativeconfig (0.74.5)
960962
- React-NativeModulesApple (0.74.5):
961963
- glog
@@ -1222,6 +1224,7 @@ DEPENDENCIES:
12221224
- React-logger (from `../node_modules/react-native/ReactCommon/logger`)
12231225
- React-Mapbuffer (from `../node_modules/react-native/ReactCommon`)
12241226
- react-native-bcrypt-cpp (from `../..`)
1227+
- react-native-randombytes (from `../node_modules/react-native-randombytes`)
12251228
- React-nativeconfig (from `../node_modules/react-native/ReactCommon`)
12261229
- React-NativeModulesApple (from `../node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios`)
12271230
- React-perflogger (from `../node_modules/react-native/ReactCommon/reactperflogger`)
@@ -1315,6 +1318,8 @@ EXTERNAL SOURCES:
13151318
:path: "../node_modules/react-native/ReactCommon"
13161319
react-native-bcrypt-cpp:
13171320
:path: "../.."
1321+
react-native-randombytes:
1322+
:path: "../node_modules/react-native-randombytes"
13181323
React-nativeconfig:
13191324
:path: "../node_modules/react-native/ReactCommon"
13201325
React-NativeModulesApple:
@@ -1395,7 +1400,8 @@ SPEC CHECKSUMS:
13951400
React-jsitracing: 3b6060bbf5317663667e1dd93560c7943ab86ccc
13961401
React-logger: 257858bd55f3a4e1bc0cf07ddc8fb9faba6f8c7c
13971402
React-Mapbuffer: 6c1cacdbf40b531f549eba249e531a7d0bfd8e7f
1398-
react-native-bcrypt-cpp: 4e7cb1f3c64413bd84b3e8b13db0ce7bcb52b581
1403+
react-native-bcrypt-cpp: 3a7a9427b6aa068c1fa462ee0465eb05d3cd198c
1404+
react-native-randombytes: 421f1c7d48c0af8dbcd471b0324393ebf8fe7846
13991405
React-nativeconfig: ba9a2e54e2f0882cf7882698825052793ed4c851
14001406
React-NativeModulesApple: 8d11ff8955181540585c944cf48e9e7236952697
14011407
React-perflogger: ed4e0c65781521e0424f2e5e40b40cc7879d737e

‎example/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
},
1212
"dependencies": {
1313
"react": "18.2.0",
14-
"react-native": "0.74.5"
14+
"react-native": "0.74.5",
15+
"react-native-randombytes": "^3.6.1"
1516
},
1617
"devDependencies": {
1718
"@babel/core": "^7.20.0",

‎example/src/App.tsx

Lines changed: 197 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,79 +1,217 @@
1-
import { Button, StyleSheet, View } from 'react-native';
1+
import { useCallback, useState } from 'react';
22
import {
3-
generateHash,
4-
generateHashSync,
5-
validatePassword,
6-
validatePasswordSync,
7-
} from 'react-native-bcrypt-cpp';
3+
ActivityIndicator,
4+
SafeAreaView,
5+
StyleSheet,
6+
Text,
7+
TouchableOpacity,
8+
View,
9+
} from 'react-native';
10+
import { generateHash, validatePassword } from 'react-native-bcrypt-cpp';
811
import { MovingRectangle } from './MovingRectangle';
12+
import {
13+
genSaltSync,
14+
hashSync as generateHashJS,
15+
compareSync as validatePasswordJS,
16+
} from './bcryptjs';
917

10-
const workload = 15;
18+
const workload = 12;
1119
const password = 'asdcds-sdjakl12313841skdnanczdeioaj';
12-
const hash = '$2b$15$wX0mKtpwGwzdfbz099nUnu5R.NN/huUK9XBra0sS4xj4XfjfPOkzO';
13-
14-
async function measureTime<T>(fn: () => T): Promise<T> {
15-
const start = performance.now();
16-
const res = await fn();
17-
const end = performance.now();
18-
const timeTaken = end - start;
19-
console.log('Time taken :', timeTaken, 'ms');
20-
return res;
21-
}
2220

2321
export default function App() {
22+
const [hash, setHash] = useState<string>(
23+
'$2a$12$a7aUL27hsWw0x2V8pJfK4eUNeANOCtAEOxJA6V4N3FIOfgoZuJz2W'
24+
);
25+
const [timeTaken, setTimeTaken] = useState<number>(0);
26+
const [isValid, setIsValid] = useState<boolean | undefined>();
27+
const [loading, setLoading] = useState<boolean>(false);
28+
29+
const measureTime = useCallback(async function <T>(fn: () => T): Promise<T> {
30+
setLoading(true);
31+
const start = performance.now();
32+
const res = await fn();
33+
const end = performance.now();
34+
const _timeTaken = end - start;
35+
console.log('Time taken :', _timeTaken, 'ms');
36+
setTimeTaken(_timeTaken);
37+
setLoading(false);
38+
return res;
39+
}, []);
40+
2441
return (
25-
<View style={styles.container}>
42+
<SafeAreaView style={styles.container}>
43+
<View>
44+
<Text style={styles.header}>Bcrypt Performance Test</Text>
45+
<View style={styles.resultContainer}>
46+
<Text style={styles.resultText}>Password:</Text>
47+
<Text style={styles.hashText}>{password}</Text>
48+
<Text style={styles.resultText}>Generated Hash:</Text>
49+
<Text style={styles.hashText}>{hash}</Text>
50+
<Text style={styles.resultText}>Time taken:</Text>
51+
<Text style={styles.timeText}>
52+
{Math.round((timeTaken / 1000) * 100) / 100} s
53+
</Text>
54+
<Text style={styles.resultText}>Is Valid:</Text>
55+
<Text style={styles.validText}>
56+
{isValid === undefined
57+
? 'Not checked'
58+
: isValid
59+
? '✅ Yes'
60+
: '❌ No'}
61+
</Text>
62+
</View>
63+
</View>
64+
2665
<MovingRectangle />
27-
<Button
28-
title="Generate Hash"
29-
onPress={async () => {
30-
const generatedHash = await measureTime(() =>
31-
generateHash(password, workload)
32-
);
33-
console.log('Generated hash:', generatedHash);
34-
}}
35-
/>
36-
<Button
37-
title="Validate Password"
38-
onPress={async () => {
39-
const isValid = await measureTime(() =>
40-
validatePassword(password, hash)
41-
);
42-
console.log('isValid', isValid);
43-
}}
44-
/>
45-
<Button
46-
title="Generate Hash Sync"
47-
onPress={async () => {
48-
const generatedHash = await measureTime(() =>
49-
generateHashSync(password, workload)
50-
);
51-
console.log('Generated hash:', generatedHash);
52-
}}
53-
/>
5466

55-
<Button
56-
title="Validate Password Sync"
57-
onPress={async () => {
58-
const isValid = await measureTime(() =>
59-
validatePasswordSync(password, hash)
60-
);
61-
console.log('isValid', isValid);
62-
}}
63-
/>
64-
</View>
67+
{loading && (
68+
<ActivityIndicator size="large" color="#6200ee" style={styles.loader} />
69+
)}
70+
71+
<View style={styles.buttonContainer}>
72+
<View style={styles.column}>
73+
<Text style={styles.columnHeader}>JavaScript</Text>
74+
<TouchableOpacity
75+
style={styles.button}
76+
onPress={async () => {
77+
const generatedHash = await measureTime(() =>
78+
generateHashJS(password, genSaltSync(workload))
79+
);
80+
console.log('Generated hash JS:', generatedHash);
81+
setHash(generatedHash);
82+
}}
83+
>
84+
<Text style={styles.buttonText}>Generate Hash</Text>
85+
</TouchableOpacity>
86+
<TouchableOpacity
87+
style={styles.button}
88+
onPress={async () => {
89+
const _isValid = await measureTime(() =>
90+
validatePasswordJS(password, hash)
91+
);
92+
console.log('is Valid JS:', _isValid);
93+
setIsValid(_isValid);
94+
}}
95+
>
96+
<Text style={styles.buttonText}>Validate Password</Text>
97+
</TouchableOpacity>
98+
</View>
99+
100+
<View style={styles.column}>
101+
<Text style={styles.columnHeader}>C++</Text>
102+
<TouchableOpacity
103+
style={styles.button}
104+
onPress={async () => {
105+
const generatedHash = await measureTime(() =>
106+
generateHash(password, workload)
107+
);
108+
console.log('Generated hash:', generatedHash);
109+
setHash(generatedHash);
110+
}}
111+
>
112+
<Text style={styles.buttonText}>Generate Hash</Text>
113+
</TouchableOpacity>
114+
<TouchableOpacity
115+
style={styles.button}
116+
onPress={async () => {
117+
const _isValid = await measureTime(() =>
118+
validatePassword(password, hash)
119+
);
120+
console.log('isValid', _isValid);
121+
setIsValid(_isValid);
122+
}}
123+
>
124+
<Text style={styles.buttonText}>Validate Password</Text>
125+
</TouchableOpacity>
126+
</View>
127+
</View>
128+
</SafeAreaView>
65129
);
66130
}
67131

68132
const styles = StyleSheet.create({
69133
container: {
70134
flex: 1,
135+
padding: 20,
136+
justifyContent: 'space-around',
137+
backgroundColor: '#f0f4f7',
138+
},
139+
header: {
140+
fontSize: 28,
141+
fontWeight: 'bold',
142+
textAlign: 'center',
143+
marginBottom: 30,
144+
color: '#333',
145+
},
146+
resultContainer: {
147+
marginBottom: 20,
148+
padding: 15,
149+
borderRadius: 10,
150+
backgroundColor: '#fff',
151+
shadowColor: '#000',
152+
shadowOpacity: 0.1,
153+
shadowRadius: 10,
154+
shadowOffset: { width: 0, height: 4 },
155+
elevation: 5,
156+
},
157+
resultText: {
158+
fontSize: 18,
159+
fontWeight: '600',
160+
marginVertical: 5,
161+
color: '#555',
162+
},
163+
hashText: {
164+
fontSize: 16,
165+
color: '#6200ee',
166+
fontWeight: '500',
167+
marginVertical: 5,
168+
},
169+
timeText: {
170+
fontSize: 20,
171+
fontWeight: '700',
172+
color: '#6200ee',
173+
marginVertical: 5,
174+
},
175+
validText: {
176+
fontSize: 20,
177+
fontWeight: '700',
178+
marginVertical: 5,
179+
},
180+
buttonContainer: {
181+
flexDirection: 'row',
182+
justifyContent: 'space-between',
183+
},
184+
column: {
185+
flex: 1,
186+
marginHorizontal: 10,
187+
},
188+
columnHeader: {
189+
fontSize: 20,
190+
fontWeight: 'bold',
191+
textAlign: 'center',
192+
marginBottom: 15,
193+
color: '#6200ee',
194+
},
195+
button: {
196+
backgroundColor: '#6200ee',
197+
paddingVertical: 12,
198+
paddingHorizontal: 20,
199+
borderRadius: 30,
200+
marginBottom: 10,
71201
alignItems: 'center',
72-
justifyContent: 'center',
202+
shadowColor: '#6200ee',
203+
shadowOpacity: 0.3,
204+
shadowRadius: 10,
205+
shadowOffset: { width: 0, height: 4 },
206+
elevation: 5,
207+
},
208+
buttonText: {
209+
color: '#fff',
210+
fontSize: 16,
211+
fontWeight: '600',
212+
textAlign: 'center',
73213
},
74-
box: {
75-
width: 60,
76-
height: 60,
214+
loader: {
77215
marginVertical: 20,
78216
},
79217
});

‎example/src/MovingRectangle.tsx

Lines changed: 65 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,74 @@
11
import { useEffect, useRef } from 'react';
2-
import { Animated, StyleSheet, View } from 'react-native';
2+
import {
3+
Animated,
4+
Dimensions,
5+
Easing,
6+
StyleSheet,
7+
Text,
8+
View,
9+
} from 'react-native';
310

411
export const MovingRectangle = () => {
512
const animationValue = useRef(new Animated.Value(0)).current;
13+
const colorAnimation = useRef(new Animated.Value(0)).current;
14+
15+
// Get the full width of the screen
16+
const screenWidth = Dimensions.get('window').width;
617

718
useEffect(() => {
819
const animate = () => {
920
Animated.loop(
10-
Animated.sequence([
11-
Animated.timing(animationValue, {
12-
toValue: 100,
13-
duration: 300,
14-
useNativeDriver: false, // This runs the animation on the JS thread
15-
}),
16-
Animated.timing(animationValue, {
17-
toValue: 0,
18-
duration: 300,
19-
useNativeDriver: false, // This runs the animation on the JS thread
20-
}),
21+
Animated.parallel([
22+
Animated.sequence([
23+
Animated.timing(animationValue, {
24+
toValue: -200,
25+
duration: 600,
26+
easing: Easing.inOut(Easing.quad),
27+
useNativeDriver: false,
28+
}),
29+
Animated.timing(animationValue, {
30+
toValue: 0,
31+
duration: 600,
32+
easing: Easing.inOut(Easing.quad),
33+
useNativeDriver: false,
34+
}),
35+
]),
36+
Animated.sequence([
37+
Animated.timing(colorAnimation, {
38+
toValue: 1,
39+
duration: 600,
40+
easing: Easing.inOut(Easing.quad),
41+
useNativeDriver: false,
42+
}),
43+
Animated.timing(colorAnimation, {
44+
toValue: 0,
45+
duration: 600,
46+
easing: Easing.inOut(Easing.quad),
47+
useNativeDriver: false,
48+
}),
49+
]),
2150
])
2251
).start();
2352
};
2453

2554
animate();
26-
}, [animationValue]);
55+
}, [animationValue, colorAnimation, screenWidth]);
56+
57+
const backgroundColor = colorAnimation.interpolate({
58+
inputRange: [0, 1],
59+
outputRange: ['blue', 'green'],
60+
});
2761

2862
return (
2963
<View style={styles.container}>
3064
<Animated.View
3165
style={[
3266
styles.rectangle,
33-
{ transform: [{ translateX: animationValue }] },
67+
{ transform: [{ translateX: animationValue }], backgroundColor },
3468
]}
35-
/>
69+
>
70+
<Text style={styles.text}>It stops when JS Thread blocked</Text>
71+
</Animated.View>
3672
</View>
3773
);
3874
};
@@ -41,11 +77,22 @@ const styles = StyleSheet.create({
4177
container: {
4278
justifyContent: 'center',
4379
alignItems: 'center',
44-
marginBottom: 50,
80+
marginVertical: 20,
81+
width: '100%',
4582
},
4683
rectangle: {
47-
width: 100,
48-
height: 100,
84+
width: 150,
85+
height: 150,
4986
backgroundColor: 'blue',
87+
borderRadius: 10,
88+
marginLeft: 200,
89+
justifyContent: 'center',
90+
alignItems: 'center',
91+
},
92+
text: {
93+
color: 'white',
94+
fontSize: 16,
95+
fontWeight: 'bold',
96+
textAlign: 'center',
5097
},
5198
});

‎example/src/bcryptjs.ts

Lines changed: 662 additions & 0 deletions
Large diffs are not rendered by default.

‎yarn.lock

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4070,7 +4070,7 @@ __metadata:
40704070
languageName: node
40714071
linkType: hard
40724072

4073-
"base64-js@npm:^1.3.1, base64-js@npm:^1.5.1":
4073+
"base64-js@npm:^1.0.2, base64-js@npm:^1.3.1, base64-js@npm:^1.5.1":
40744074
version: 1.5.1
40754075
resolution: "base64-js@npm:1.5.1"
40764076
checksum: 669632eb3745404c2f822a18fc3a0122d2f9a7a13f7fb8b5823ee19d1d2ff9ee5b52c53367176ea4ad093c332fd5ab4bd0ebae5a8e27917a4105a4cfc86b1005
@@ -4203,6 +4203,17 @@ __metadata:
42034203
languageName: node
42044204
linkType: hard
42054205

4206+
"buffer@npm:^4.9.1":
4207+
version: 4.9.2
4208+
resolution: "buffer@npm:4.9.2"
4209+
dependencies:
4210+
base64-js: ^1.0.2
4211+
ieee754: ^1.1.4
4212+
isarray: ^1.0.0
4213+
checksum: 8801bc1ba08539f3be70eee307a8b9db3d40f6afbfd3cf623ab7ef41dffff1d0a31de0addbe1e66e0ca5f7193eeb667bfb1ecad3647f8f1b0750de07c13295c3
4214+
languageName: node
4215+
linkType: hard
4216+
42064217
"buffer@npm:^5.5.0":
42074218
version: 5.7.1
42084219
resolution: "buffer@npm:5.7.1"
@@ -7236,7 +7247,7 @@ __metadata:
72367247
languageName: node
72377248
linkType: hard
72387249

7239-
"ieee754@npm:^1.1.13, ieee754@npm:^1.2.1":
7250+
"ieee754@npm:^1.1.13, ieee754@npm:^1.1.4, ieee754@npm:^1.2.1":
72407251
version: 1.2.1
72417252
resolution: "ieee754@npm:1.2.1"
72427253
checksum: 5144c0c9815e54ada181d80a0b810221a253562422e7c6c3a60b1901154184f49326ec239d618c416c1c5945a2e197107aee8d986a3dd836b53dffefd99b5e7e
@@ -7946,20 +7957,20 @@ __metadata:
79467957
languageName: node
79477958
linkType: hard
79487959

7960+
"isarray@npm:^1.0.0, isarray@npm:~1.0.0":
7961+
version: 1.0.0
7962+
resolution: "isarray@npm:1.0.0"
7963+
checksum: f032df8e02dce8ec565cf2eb605ea939bdccea528dbcf565cdf92bfa2da9110461159d86a537388ef1acef8815a330642d7885b29010e8f7eac967c9993b65ab
7964+
languageName: node
7965+
linkType: hard
7966+
79497967
"isarray@npm:^2.0.5":
79507968
version: 2.0.5
79517969
resolution: "isarray@npm:2.0.5"
79527970
checksum: bd5bbe4104438c4196ba58a54650116007fa0262eccef13a4c55b2e09a5b36b59f1e75b9fcc49883dd9d4953892e6fc007eef9e9155648ceea036e184b0f930a
79537971
languageName: node
79547972
linkType: hard
79557973

7956-
"isarray@npm:~1.0.0":
7957-
version: 1.0.0
7958-
resolution: "isarray@npm:1.0.0"
7959-
checksum: f032df8e02dce8ec565cf2eb605ea939bdccea528dbcf565cdf92bfa2da9110461159d86a537388ef1acef8815a330642d7885b29010e8f7eac967c9993b65ab
7960-
languageName: node
7961-
linkType: hard
7962-
79637974
"isexe@npm:^2.0.0":
79647975
version: 2.0.0
79657976
resolution: "isexe@npm:2.0.0"
@@ -10851,6 +10862,7 @@ __metadata:
1085110862
react: 18.2.0
1085210863
react-native: 0.74.5
1085310864
react-native-builder-bob: ^0.29.0
10865+
react-native-randombytes: ^3.6.1
1085410866
languageName: unknown
1085510867
linkType: soft
1085610868

@@ -10919,6 +10931,16 @@ __metadata:
1091910931
languageName: node
1092010932
linkType: hard
1092110933

10934+
"react-native-randombytes@npm:^3.6.1":
10935+
version: 3.6.1
10936+
resolution: "react-native-randombytes@npm:3.6.1"
10937+
dependencies:
10938+
buffer: ^4.9.1
10939+
sjcl: ^1.0.3
10940+
checksum: a0d8ec80e1a4573d8c4924cb52b01007fe2ae1d3698bb5e1e1330246f0ae307af4e7df236e176f438a7f69151e321a36a633f53dbb61f1c965a960f572bd3b1e
10941+
languageName: node
10942+
linkType: hard
10943+
1092210944
"react-native@npm:0.74.5":
1092310945
version: 0.74.5
1092410946
resolution: "react-native@npm:0.74.5"
@@ -11816,6 +11838,13 @@ __metadata:
1181611838
languageName: node
1181711839
linkType: hard
1181811840

11841+
"sjcl@npm:^1.0.3":
11842+
version: 1.0.8
11843+
resolution: "sjcl@npm:1.0.8"
11844+
checksum: ae5bc8172dec88376fbb1c8508f2be24c1dd064be2af25368e6267824b6a84b52b515546b48c8f72460c715a39e934b077f0bf2adefc36c22527bff57ec06e1d
11845+
languageName: node
11846+
linkType: hard
11847+
1181911848
"slash@npm:^3.0.0":
1182011849
version: 3.0.0
1182111850
resolution: "slash@npm:3.0.0"

0 commit comments

Comments
 (0)
Please sign in to comment.