@exodus/react-native-bundle-loader 0.2.0-exodus.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 +21 -0
- package/README.md +107 -0
- package/SECURITY.md +57 -0
- package/android/build.gradle +119 -0
- package/android/gradle.properties +3 -0
- package/android/src/main/AndroidManifest.xml +4 -0
- package/android/src/main/java/com/reactnativebundleloader/BundleLoaderModule.java +152 -0
- package/android/src/main/java/com/reactnativebundleloader/BundleLoaderPackage.java +31 -0
- package/ios/BundleLoader.h +6 -0
- package/ios/BundleLoader.m +61 -0
- package/ios/BundleLoader.xcodeproj/project.pbxproj +285 -0
- package/lib/commonjs/index.js +165 -0
- package/lib/module/index.js +144 -0
- package/lib/typescript/index.d.ts +12 -0
- package/package.json +149 -0
- package/react-native-bundle-loader.podspec +19 -0
- package/src/index.tsx +171 -0
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
// !$*UTF8*$!
|
|
2
|
+
{
|
|
3
|
+
archiveVersion = 1;
|
|
4
|
+
classes = {
|
|
5
|
+
};
|
|
6
|
+
objectVersion = 46;
|
|
7
|
+
objects = {
|
|
8
|
+
|
|
9
|
+
/* Begin PBXBuildFile section */
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
5E555C0D2413F4C50049A1A2 /* BundleLoader.mm in Sources */ = {isa = PBXBuildFile; fileRef = B3E7B5891CC2AC0600A0062D /* BundleLoader.mm */; };
|
|
13
|
+
/* End PBXBuildFile section */
|
|
14
|
+
|
|
15
|
+
/* Begin PBXCopyFilesBuildPhase section */
|
|
16
|
+
58B511D91A9E6C8500147676 /* CopyFiles */ = {
|
|
17
|
+
isa = PBXCopyFilesBuildPhase;
|
|
18
|
+
buildActionMask = 2147483647;
|
|
19
|
+
dstPath = "include/$(PRODUCT_NAME)";
|
|
20
|
+
dstSubfolderSpec = 16;
|
|
21
|
+
files = (
|
|
22
|
+
);
|
|
23
|
+
runOnlyForDeploymentPostprocessing = 0;
|
|
24
|
+
};
|
|
25
|
+
/* End PBXCopyFilesBuildPhase section */
|
|
26
|
+
|
|
27
|
+
/* Begin PBXFileReference section */
|
|
28
|
+
134814201AA4EA6300B7C361 /* libBundleLoader.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libBundleLoader.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
B3E7B5881CC2AC0600A0062D /* BundleLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BundleLoader.h; sourceTree = "<group>"; };
|
|
32
|
+
B3E7B5891CC2AC0600A0062D /* BundleLoader.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = BundleLoader.mm; sourceTree = "<group>"; };
|
|
33
|
+
|
|
34
|
+
/* End PBXFileReference section */
|
|
35
|
+
|
|
36
|
+
/* Begin PBXFrameworksBuildPhase section */
|
|
37
|
+
58B511D81A9E6C8500147676 /* Frameworks */ = {
|
|
38
|
+
isa = PBXFrameworksBuildPhase;
|
|
39
|
+
buildActionMask = 2147483647;
|
|
40
|
+
files = (
|
|
41
|
+
);
|
|
42
|
+
runOnlyForDeploymentPostprocessing = 0;
|
|
43
|
+
};
|
|
44
|
+
/* End PBXFrameworksBuildPhase section */
|
|
45
|
+
|
|
46
|
+
/* Begin PBXGroup section */
|
|
47
|
+
134814211AA4EA7D00B7C361 /* Products */ = {
|
|
48
|
+
isa = PBXGroup;
|
|
49
|
+
children = (
|
|
50
|
+
134814201AA4EA6300B7C361 /* libBundleLoader.a */,
|
|
51
|
+
);
|
|
52
|
+
name = Products;
|
|
53
|
+
sourceTree = "<group>";
|
|
54
|
+
};
|
|
55
|
+
58B511D21A9E6C8500147676 = {
|
|
56
|
+
isa = PBXGroup;
|
|
57
|
+
children = (
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
B3E7B5881CC2AC0600A0062D /* BundleLoader.h */,
|
|
61
|
+
B3E7B5891CC2AC0600A0062D /* BundleLoader.mm */,
|
|
62
|
+
|
|
63
|
+
134814211AA4EA7D00B7C361 /* Products */,
|
|
64
|
+
);
|
|
65
|
+
sourceTree = "<group>";
|
|
66
|
+
};
|
|
67
|
+
/* End PBXGroup section */
|
|
68
|
+
|
|
69
|
+
/* Begin PBXNativeTarget section */
|
|
70
|
+
58B511DA1A9E6C8500147676 /* BundleLoader */ = {
|
|
71
|
+
isa = PBXNativeTarget;
|
|
72
|
+
buildConfigurationList = 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "BundleLoader" */;
|
|
73
|
+
buildPhases = (
|
|
74
|
+
58B511D71A9E6C8500147676 /* Sources */,
|
|
75
|
+
58B511D81A9E6C8500147676 /* Frameworks */,
|
|
76
|
+
58B511D91A9E6C8500147676 /* CopyFiles */,
|
|
77
|
+
);
|
|
78
|
+
buildRules = (
|
|
79
|
+
);
|
|
80
|
+
dependencies = (
|
|
81
|
+
);
|
|
82
|
+
name = BundleLoader;
|
|
83
|
+
productName = RCTDataManager;
|
|
84
|
+
productReference = 134814201AA4EA6300B7C361 /* libBundleLoader.a */;
|
|
85
|
+
productType = "com.apple.product-type.library.static";
|
|
86
|
+
};
|
|
87
|
+
/* End PBXNativeTarget section */
|
|
88
|
+
|
|
89
|
+
/* Begin PBXProject section */
|
|
90
|
+
58B511D31A9E6C8500147676 /* Project object */ = {
|
|
91
|
+
isa = PBXProject;
|
|
92
|
+
attributes = {
|
|
93
|
+
LastUpgradeCheck = 0920;
|
|
94
|
+
ORGANIZATIONNAME = Facebook;
|
|
95
|
+
TargetAttributes = {
|
|
96
|
+
58B511DA1A9E6C8500147676 = {
|
|
97
|
+
CreatedOnToolsVersion = 6.1.1;
|
|
98
|
+
};
|
|
99
|
+
};
|
|
100
|
+
};
|
|
101
|
+
buildConfigurationList = 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "BundleLoader" */;
|
|
102
|
+
compatibilityVersion = "Xcode 3.2";
|
|
103
|
+
developmentRegion = English;
|
|
104
|
+
hasScannedForEncodings = 0;
|
|
105
|
+
knownRegions = (
|
|
106
|
+
English,
|
|
107
|
+
en,
|
|
108
|
+
);
|
|
109
|
+
mainGroup = 58B511D21A9E6C8500147676;
|
|
110
|
+
productRefGroup = 58B511D21A9E6C8500147676;
|
|
111
|
+
projectDirPath = "";
|
|
112
|
+
projectRoot = "";
|
|
113
|
+
targets = (
|
|
114
|
+
58B511DA1A9E6C8500147676 /* BundleLoader */,
|
|
115
|
+
);
|
|
116
|
+
};
|
|
117
|
+
/* End PBXProject section */
|
|
118
|
+
|
|
119
|
+
/* Begin PBXSourcesBuildPhase section */
|
|
120
|
+
58B511D71A9E6C8500147676 /* Sources */ = {
|
|
121
|
+
isa = PBXSourcesBuildPhase;
|
|
122
|
+
buildActionMask = 2147483647;
|
|
123
|
+
files = (
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
5E555C0D2413F4C50049A1A2 /* BundleLoader.mm in Sources */,
|
|
127
|
+
|
|
128
|
+
);
|
|
129
|
+
runOnlyForDeploymentPostprocessing = 0;
|
|
130
|
+
};
|
|
131
|
+
/* End PBXSourcesBuildPhase section */
|
|
132
|
+
|
|
133
|
+
/* Begin XCBuildConfiguration section */
|
|
134
|
+
58B511ED1A9E6C8500147676 /* Debug */ = {
|
|
135
|
+
isa = XCBuildConfiguration;
|
|
136
|
+
buildSettings = {
|
|
137
|
+
ALWAYS_SEARCH_USER_PATHS = NO;
|
|
138
|
+
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
|
139
|
+
CLANG_CXX_LIBRARY = "libc++";
|
|
140
|
+
CLANG_ENABLE_MODULES = YES;
|
|
141
|
+
CLANG_ENABLE_OBJC_ARC = YES;
|
|
142
|
+
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
|
143
|
+
CLANG_WARN_BOOL_CONVERSION = YES;
|
|
144
|
+
CLANG_WARN_COMMA = YES;
|
|
145
|
+
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
|
146
|
+
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
|
147
|
+
CLANG_WARN_EMPTY_BODY = YES;
|
|
148
|
+
CLANG_WARN_ENUM_CONVERSION = YES;
|
|
149
|
+
CLANG_WARN_INFINITE_RECURSION = YES;
|
|
150
|
+
CLANG_WARN_INT_CONVERSION = YES;
|
|
151
|
+
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
|
152
|
+
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
|
153
|
+
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
|
154
|
+
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
|
155
|
+
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
|
156
|
+
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
|
157
|
+
CLANG_WARN_UNREACHABLE_CODE = YES;
|
|
158
|
+
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
|
159
|
+
COPY_PHASE_STRIP = NO;
|
|
160
|
+
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
|
161
|
+
ENABLE_TESTABILITY = YES;
|
|
162
|
+
GCC_C_LANGUAGE_STANDARD = gnu99;
|
|
163
|
+
GCC_DYNAMIC_NO_PIC = NO;
|
|
164
|
+
GCC_NO_COMMON_BLOCKS = YES;
|
|
165
|
+
GCC_OPTIMIZATION_LEVEL = 0;
|
|
166
|
+
GCC_PREPROCESSOR_DEFINITIONS = (
|
|
167
|
+
"DEBUG=1",
|
|
168
|
+
"$(inherited)",
|
|
169
|
+
);
|
|
170
|
+
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
|
|
171
|
+
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
|
172
|
+
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
|
173
|
+
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
|
174
|
+
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
|
175
|
+
GCC_WARN_UNUSED_FUNCTION = YES;
|
|
176
|
+
GCC_WARN_UNUSED_VARIABLE = YES;
|
|
177
|
+
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
|
178
|
+
MTL_ENABLE_DEBUG_INFO = YES;
|
|
179
|
+
ONLY_ACTIVE_ARCH = YES;
|
|
180
|
+
SDKROOT = iphoneos;
|
|
181
|
+
};
|
|
182
|
+
name = Debug;
|
|
183
|
+
};
|
|
184
|
+
58B511EE1A9E6C8500147676 /* Release */ = {
|
|
185
|
+
isa = XCBuildConfiguration;
|
|
186
|
+
buildSettings = {
|
|
187
|
+
ALWAYS_SEARCH_USER_PATHS = NO;
|
|
188
|
+
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
|
189
|
+
CLANG_CXX_LIBRARY = "libc++";
|
|
190
|
+
CLANG_ENABLE_MODULES = YES;
|
|
191
|
+
CLANG_ENABLE_OBJC_ARC = YES;
|
|
192
|
+
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
|
193
|
+
CLANG_WARN_BOOL_CONVERSION = YES;
|
|
194
|
+
CLANG_WARN_COMMA = YES;
|
|
195
|
+
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
|
196
|
+
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
|
197
|
+
CLANG_WARN_EMPTY_BODY = YES;
|
|
198
|
+
CLANG_WARN_ENUM_CONVERSION = YES;
|
|
199
|
+
CLANG_WARN_INFINITE_RECURSION = YES;
|
|
200
|
+
CLANG_WARN_INT_CONVERSION = YES;
|
|
201
|
+
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
|
202
|
+
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
|
203
|
+
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
|
204
|
+
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
|
205
|
+
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
|
206
|
+
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
|
207
|
+
CLANG_WARN_UNREACHABLE_CODE = YES;
|
|
208
|
+
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
|
209
|
+
COPY_PHASE_STRIP = YES;
|
|
210
|
+
ENABLE_NS_ASSERTIONS = NO;
|
|
211
|
+
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
|
212
|
+
GCC_C_LANGUAGE_STANDARD = gnu99;
|
|
213
|
+
GCC_NO_COMMON_BLOCKS = YES;
|
|
214
|
+
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
|
215
|
+
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
|
216
|
+
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
|
217
|
+
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
|
218
|
+
GCC_WARN_UNUSED_FUNCTION = YES;
|
|
219
|
+
GCC_WARN_UNUSED_VARIABLE = YES;
|
|
220
|
+
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
|
221
|
+
MTL_ENABLE_DEBUG_INFO = NO;
|
|
222
|
+
SDKROOT = iphoneos;
|
|
223
|
+
VALIDATE_PRODUCT = YES;
|
|
224
|
+
};
|
|
225
|
+
name = Release;
|
|
226
|
+
};
|
|
227
|
+
58B511F01A9E6C8500147676 /* Debug */ = {
|
|
228
|
+
isa = XCBuildConfiguration;
|
|
229
|
+
buildSettings = {
|
|
230
|
+
HEADER_SEARCH_PATHS = (
|
|
231
|
+
"$(inherited)",
|
|
232
|
+
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
|
233
|
+
"$(SRCROOT)/../../../React/**",
|
|
234
|
+
"$(SRCROOT)/../../react-native/React/**",
|
|
235
|
+
);
|
|
236
|
+
LIBRARY_SEARCH_PATHS = "$(inherited)";
|
|
237
|
+
OTHER_LDFLAGS = "-ObjC";
|
|
238
|
+
PRODUCT_NAME = BundleLoader;
|
|
239
|
+
SKIP_INSTALL = YES;
|
|
240
|
+
|
|
241
|
+
};
|
|
242
|
+
name = Debug;
|
|
243
|
+
};
|
|
244
|
+
58B511F11A9E6C8500147676 /* Release */ = {
|
|
245
|
+
isa = XCBuildConfiguration;
|
|
246
|
+
buildSettings = {
|
|
247
|
+
HEADER_SEARCH_PATHS = (
|
|
248
|
+
"$(inherited)",
|
|
249
|
+
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
|
250
|
+
"$(SRCROOT)/../../../React/**",
|
|
251
|
+
"$(SRCROOT)/../../react-native/React/**",
|
|
252
|
+
);
|
|
253
|
+
LIBRARY_SEARCH_PATHS = "$(inherited)";
|
|
254
|
+
OTHER_LDFLAGS = "-ObjC";
|
|
255
|
+
PRODUCT_NAME = BundleLoader;
|
|
256
|
+
SKIP_INSTALL = YES;
|
|
257
|
+
|
|
258
|
+
};
|
|
259
|
+
name = Release;
|
|
260
|
+
};
|
|
261
|
+
/* End XCBuildConfiguration section */
|
|
262
|
+
|
|
263
|
+
/* Begin XCConfigurationList section */
|
|
264
|
+
58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "BundleLoader" */ = {
|
|
265
|
+
isa = XCConfigurationList;
|
|
266
|
+
buildConfigurations = (
|
|
267
|
+
58B511ED1A9E6C8500147676 /* Debug */,
|
|
268
|
+
58B511EE1A9E6C8500147676 /* Release */,
|
|
269
|
+
);
|
|
270
|
+
defaultConfigurationIsVisible = 0;
|
|
271
|
+
defaultConfigurationName = Release;
|
|
272
|
+
};
|
|
273
|
+
58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "BundleLoader" */ = {
|
|
274
|
+
isa = XCConfigurationList;
|
|
275
|
+
buildConfigurations = (
|
|
276
|
+
58B511F01A9E6C8500147676 /* Debug */,
|
|
277
|
+
58B511F11A9E6C8500147676 /* Release */,
|
|
278
|
+
);
|
|
279
|
+
defaultConfigurationIsVisible = 0;
|
|
280
|
+
defaultConfigurationName = Release;
|
|
281
|
+
};
|
|
282
|
+
/* End XCConfigurationList section */
|
|
283
|
+
};
|
|
284
|
+
rootObject = 58B511D31A9E6C8500147676 /* Project object */;
|
|
285
|
+
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.loadVerified = loadVerified;
|
|
7
|
+
exports.BundlePrompt = BundlePrompt;
|
|
8
|
+
exports.default = void 0;
|
|
9
|
+
|
|
10
|
+
var _react = _interopRequireWildcard(require("react"));
|
|
11
|
+
|
|
12
|
+
var _reactNative = require("react-native");
|
|
13
|
+
|
|
14
|
+
var _hash = require("@exodus/crypto/hash");
|
|
15
|
+
|
|
16
|
+
var _hex = require("@exodus/bytes/hex.js");
|
|
17
|
+
|
|
18
|
+
var _base = require("@exodus/bytes/base64.js");
|
|
19
|
+
|
|
20
|
+
function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; }
|
|
21
|
+
|
|
22
|
+
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
|
23
|
+
|
|
24
|
+
/** @format */
|
|
25
|
+
function getNative() {
|
|
26
|
+
const m = _reactNative.NativeModules.BundleLoader;
|
|
27
|
+
|
|
28
|
+
if (!m) {
|
|
29
|
+
throw new Error("@exodus/react-native-bundle-loader: native module 'BundleLoader' is not linked. " + 'On iOS, run `pod install` and rebuild the host app. ' + 'On Android, ensure BundleLoaderPackage is registered.');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return m;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function assertSafeUrl(url) {
|
|
36
|
+
if (typeof url !== 'string' || url.length === 0) {
|
|
37
|
+
throw new Error('Bundle URL must be a non-empty string');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (!/^https:\/\//.test(url)) {
|
|
41
|
+
throw new Error('Bundle URL must use the https scheme');
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function timingSafeEqualBytes(a, b) {
|
|
46
|
+
if (a.length !== b.length) return false;
|
|
47
|
+
let diff = 0;
|
|
48
|
+
/* eslint-disable no-bitwise */
|
|
49
|
+
|
|
50
|
+
for (let i = 0; i < a.length; i++) diff |= a[i] ^ b[i];
|
|
51
|
+
/* eslint-enable no-bitwise */
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
return diff === 0;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async function loadVerified(url, expectedSha256Hex) {
|
|
58
|
+
assertSafeUrl(url);
|
|
59
|
+
|
|
60
|
+
if (typeof expectedSha256Hex !== 'string' || expectedSha256Hex.length !== 64) {
|
|
61
|
+
throw new Error('Expected sha256 must be a 64-character hex string');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const expected = (0, _hex.fromHex)(expectedSha256Hex);
|
|
65
|
+
const response = await fetch(url);
|
|
66
|
+
|
|
67
|
+
if (!response.ok) {
|
|
68
|
+
throw new Error("Bundle fetch failed: HTTP ".concat(response.status));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const buffer = await response.arrayBuffer();
|
|
72
|
+
const bytes = new Uint8Array(buffer);
|
|
73
|
+
const actual = await (0, _hash.hash)('sha256', bytes, 'uint8');
|
|
74
|
+
|
|
75
|
+
if (!timingSafeEqualBytes(actual, expected)) {
|
|
76
|
+
throw new Error('Bundle hash mismatch — refusing to load');
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const native = getNative();
|
|
80
|
+
|
|
81
|
+
if (typeof native.loadFromBase64 !== 'function') {
|
|
82
|
+
throw new Error('Native loadFromBase64 not available. Rebuild the host app after upgrading.');
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
await native.loadFromBase64((0, _base.toBase64)(bytes));
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function loadUnverified(url) {
|
|
89
|
+
assertSafeUrl(url);
|
|
90
|
+
getNative().load(url);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async function runningMode() {
|
|
94
|
+
return getNative().runningMode();
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const BundleLoader = {
|
|
98
|
+
load: loadUnverified,
|
|
99
|
+
loadVerified,
|
|
100
|
+
runningMode
|
|
101
|
+
};
|
|
102
|
+
var _default = BundleLoader;
|
|
103
|
+
exports.default = _default;
|
|
104
|
+
|
|
105
|
+
const styles = _reactNative.StyleSheet.create({
|
|
106
|
+
container: {
|
|
107
|
+
marginTop: 48,
|
|
108
|
+
padding: 16,
|
|
109
|
+
flex: 1
|
|
110
|
+
},
|
|
111
|
+
input: {
|
|
112
|
+
height: 48,
|
|
113
|
+
marginTop: 8,
|
|
114
|
+
paddingHorizontal: 8,
|
|
115
|
+
borderColor: 'gray',
|
|
116
|
+
borderRadius: 4,
|
|
117
|
+
borderWidth: 1
|
|
118
|
+
},
|
|
119
|
+
button: {
|
|
120
|
+
backgroundColor: '#007AFF',
|
|
121
|
+
marginTop: 16,
|
|
122
|
+
height: 48,
|
|
123
|
+
justifyContent: 'center'
|
|
124
|
+
},
|
|
125
|
+
buttonText: {
|
|
126
|
+
color: 'white',
|
|
127
|
+
alignSelf: 'center',
|
|
128
|
+
fontSize: 18,
|
|
129
|
+
alignContent: 'center'
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
function BundlePrompt() {
|
|
134
|
+
const [url, setUrl] = (0, _react.useState)('');
|
|
135
|
+
const reload = (0, _react.useCallback)(() => {
|
|
136
|
+
if (!url) {
|
|
137
|
+
_reactNative.Alert.alert('Oops…', 'You need to provide a URL');
|
|
138
|
+
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
try {
|
|
143
|
+
loadUnverified(url);
|
|
144
|
+
} catch (e) {
|
|
145
|
+
_reactNative.Alert.alert('Invalid URL', e.message);
|
|
146
|
+
}
|
|
147
|
+
}, [url]);
|
|
148
|
+
return /*#__PURE__*/_react.default.createElement(_reactNative.Modal, null, /*#__PURE__*/_react.default.createElement(_reactNative.View, {
|
|
149
|
+
style: styles.container
|
|
150
|
+
}, /*#__PURE__*/_react.default.createElement(_reactNative.TextInput, {
|
|
151
|
+
keyboardType: "url",
|
|
152
|
+
onChange: e => setUrl(e.nativeEvent.text.trim()),
|
|
153
|
+
style: styles.input,
|
|
154
|
+
clearButtonMode: "always",
|
|
155
|
+
autoFocus: true,
|
|
156
|
+
placeholder: "https://\u2026"
|
|
157
|
+
}), /*#__PURE__*/_react.default.createElement(_reactNative.TouchableOpacity, {
|
|
158
|
+
style: styles.button,
|
|
159
|
+
onPress: reload,
|
|
160
|
+
accessibilityLabel: "Reload entire app"
|
|
161
|
+
}, /*#__PURE__*/_react.default.createElement(_reactNative.Text, {
|
|
162
|
+
style: styles.buttonText
|
|
163
|
+
}, "Reload"))));
|
|
164
|
+
}
|
|
165
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/** @format */
|
|
2
|
+
import React, { useCallback, useState } from 'react';
|
|
3
|
+
import { Alert, Modal, NativeModules, StyleSheet, Text, TextInput, TouchableOpacity, View } from 'react-native';
|
|
4
|
+
import { hash } from '@exodus/crypto/hash';
|
|
5
|
+
import { fromHex } from '@exodus/bytes/hex.js';
|
|
6
|
+
import { toBase64 } from '@exodus/bytes/base64.js';
|
|
7
|
+
|
|
8
|
+
function getNative() {
|
|
9
|
+
const m = NativeModules.BundleLoader;
|
|
10
|
+
|
|
11
|
+
if (!m) {
|
|
12
|
+
throw new Error("@exodus/react-native-bundle-loader: native module 'BundleLoader' is not linked. " + 'On iOS, run `pod install` and rebuild the host app. ' + 'On Android, ensure BundleLoaderPackage is registered.');
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return m;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function assertSafeUrl(url) {
|
|
19
|
+
if (typeof url !== 'string' || url.length === 0) {
|
|
20
|
+
throw new Error('Bundle URL must be a non-empty string');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (!/^https:\/\//.test(url)) {
|
|
24
|
+
throw new Error('Bundle URL must use the https scheme');
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function timingSafeEqualBytes(a, b) {
|
|
29
|
+
if (a.length !== b.length) return false;
|
|
30
|
+
let diff = 0;
|
|
31
|
+
/* eslint-disable no-bitwise */
|
|
32
|
+
|
|
33
|
+
for (let i = 0; i < a.length; i++) diff |= a[i] ^ b[i];
|
|
34
|
+
/* eslint-enable no-bitwise */
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
return diff === 0;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export async function loadVerified(url, expectedSha256Hex) {
|
|
41
|
+
assertSafeUrl(url);
|
|
42
|
+
|
|
43
|
+
if (typeof expectedSha256Hex !== 'string' || expectedSha256Hex.length !== 64) {
|
|
44
|
+
throw new Error('Expected sha256 must be a 64-character hex string');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const expected = fromHex(expectedSha256Hex);
|
|
48
|
+
const response = await fetch(url);
|
|
49
|
+
|
|
50
|
+
if (!response.ok) {
|
|
51
|
+
throw new Error("Bundle fetch failed: HTTP ".concat(response.status));
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const buffer = await response.arrayBuffer();
|
|
55
|
+
const bytes = new Uint8Array(buffer);
|
|
56
|
+
const actual = await hash('sha256', bytes, 'uint8');
|
|
57
|
+
|
|
58
|
+
if (!timingSafeEqualBytes(actual, expected)) {
|
|
59
|
+
throw new Error('Bundle hash mismatch — refusing to load');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const native = getNative();
|
|
63
|
+
|
|
64
|
+
if (typeof native.loadFromBase64 !== 'function') {
|
|
65
|
+
throw new Error('Native loadFromBase64 not available. Rebuild the host app after upgrading.');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
await native.loadFromBase64(toBase64(bytes));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function loadUnverified(url) {
|
|
72
|
+
assertSafeUrl(url);
|
|
73
|
+
getNative().load(url);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async function runningMode() {
|
|
77
|
+
return getNative().runningMode();
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const BundleLoader = {
|
|
81
|
+
load: loadUnverified,
|
|
82
|
+
loadVerified,
|
|
83
|
+
runningMode
|
|
84
|
+
};
|
|
85
|
+
export default BundleLoader;
|
|
86
|
+
const styles = StyleSheet.create({
|
|
87
|
+
container: {
|
|
88
|
+
marginTop: 48,
|
|
89
|
+
padding: 16,
|
|
90
|
+
flex: 1
|
|
91
|
+
},
|
|
92
|
+
input: {
|
|
93
|
+
height: 48,
|
|
94
|
+
marginTop: 8,
|
|
95
|
+
paddingHorizontal: 8,
|
|
96
|
+
borderColor: 'gray',
|
|
97
|
+
borderRadius: 4,
|
|
98
|
+
borderWidth: 1
|
|
99
|
+
},
|
|
100
|
+
button: {
|
|
101
|
+
backgroundColor: '#007AFF',
|
|
102
|
+
marginTop: 16,
|
|
103
|
+
height: 48,
|
|
104
|
+
justifyContent: 'center'
|
|
105
|
+
},
|
|
106
|
+
buttonText: {
|
|
107
|
+
color: 'white',
|
|
108
|
+
alignSelf: 'center',
|
|
109
|
+
fontSize: 18,
|
|
110
|
+
alignContent: 'center'
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
export function BundlePrompt() {
|
|
114
|
+
const [url, setUrl] = useState('');
|
|
115
|
+
const reload = useCallback(() => {
|
|
116
|
+
if (!url) {
|
|
117
|
+
Alert.alert('Oops…', 'You need to provide a URL');
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
try {
|
|
122
|
+
loadUnverified(url);
|
|
123
|
+
} catch (e) {
|
|
124
|
+
Alert.alert('Invalid URL', e.message);
|
|
125
|
+
}
|
|
126
|
+
}, [url]);
|
|
127
|
+
return /*#__PURE__*/React.createElement(Modal, null, /*#__PURE__*/React.createElement(View, {
|
|
128
|
+
style: styles.container
|
|
129
|
+
}, /*#__PURE__*/React.createElement(TextInput, {
|
|
130
|
+
keyboardType: "url",
|
|
131
|
+
onChange: e => setUrl(e.nativeEvent.text.trim()),
|
|
132
|
+
style: styles.input,
|
|
133
|
+
clearButtonMode: "always",
|
|
134
|
+
autoFocus: true,
|
|
135
|
+
placeholder: "https://\u2026"
|
|
136
|
+
}), /*#__PURE__*/React.createElement(TouchableOpacity, {
|
|
137
|
+
style: styles.button,
|
|
138
|
+
onPress: reload,
|
|
139
|
+
accessibilityLabel: "Reload entire app"
|
|
140
|
+
}, /*#__PURE__*/React.createElement(Text, {
|
|
141
|
+
style: styles.buttonText
|
|
142
|
+
}, "Reload"))));
|
|
143
|
+
}
|
|
144
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
export declare type RunningMode = 'LOCAL' | 'REMOTE';
|
|
3
|
+
export declare function loadVerified(url: string, expectedSha256Hex: string): Promise<void>;
|
|
4
|
+
declare function loadUnverified(url: string): void;
|
|
5
|
+
declare function runningMode(): Promise<RunningMode>;
|
|
6
|
+
declare const BundleLoader: {
|
|
7
|
+
load: typeof loadUnverified;
|
|
8
|
+
loadVerified: typeof loadVerified;
|
|
9
|
+
runningMode: typeof runningMode;
|
|
10
|
+
};
|
|
11
|
+
export default BundleLoader;
|
|
12
|
+
export declare function BundlePrompt(): JSX.Element;
|