@react-native-firebase/app 26.3.3 → 26.4.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/CHANGELOG.md +27 -0
- package/RNFBApp.podspec +1 -1
- package/android/src/main/java/io/invertase/firebase/common/RNFBHandleCollisionException.java +31 -0
- package/android/src/main/java/io/invertase/firebase/common/RNFBHandleMap.java +150 -0
- package/android/src/main/java/io/invertase/firebase/common/TaskExecutorService.java +27 -3
- package/android/src/reactnative/java/io/invertase/firebase/app/NativeRNFBTurboApp.java +3 -1
- package/android/src/reactnative/java/io/invertase/firebase/app/ReactNativeFirebaseVersion.java +1 -1
- package/android/src/reactnative/java/io/invertase/firebase/common/RCTConvertFirebase.java +3 -2
- package/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebaseJSON.java +5 -3
- package/android/src/test/java/io/invertase/firebase/app/NativeRNFBTurboAppTest.java +26 -1
- package/android/src/test/java/io/invertase/firebase/common/RNFBHandleMapTest.java +275 -0
- package/android/src/test/java/io/invertase/firebase/common/ReactNativeFirebaseEventEmitterTest.java +1 -1
- package/android/src/test/java/io/invertase/firebase/common/ReactNativeFirebaseJSONTest.java +108 -0
- package/android/src/test/java/io/invertase/firebase/common/TaskExecutorServiceTest.java +97 -0
- package/android/src/test/java/io/invertase/firebase/utils/NativeRNFBTurboUtilsTest.java +0 -2
- package/dist/module/common/Base64.js +13 -21
- package/dist/module/common/Base64.js.map +1 -1
- package/dist/module/common/index.js +17 -9
- package/dist/module/common/index.js.map +1 -1
- package/dist/module/internal/asyncStorage.js +7 -11
- package/dist/module/internal/asyncStorage.js.map +1 -1
- package/dist/module/internal/logger.js +44 -40
- package/dist/module/internal/logger.js.map +1 -1
- package/dist/module/internal/nativeModuleAndroidIos.js +2 -29
- package/dist/module/internal/nativeModuleAndroidIos.js.map +1 -1
- package/dist/module/internal/nativeModuleDebug.js +74 -0
- package/dist/module/internal/nativeModuleDebug.js.map +1 -0
- package/dist/module/internal/nativeModuleWeb.js +9 -29
- package/dist/module/internal/nativeModuleWeb.js.map +1 -1
- package/dist/module/internal/registry/app.js +1 -1
- package/dist/module/internal/registry/app.js.map +1 -1
- package/dist/module/internal/registry/modular.js +2 -2
- package/dist/module/internal/registry/modular.js.map +1 -1
- package/dist/module/internal/registry/nativeModule.js +3 -3
- package/dist/module/internal/registry/nativeModule.js.map +1 -1
- package/dist/module/internal/web/RNFBAppModule.js +16 -15
- package/dist/module/internal/web/RNFBAppModule.js.map +1 -1
- package/dist/module/internal/web/memidb/lib/FakeEventTarget.js +3 -1
- package/dist/module/internal/web/memidb/lib/FakeEventTarget.js.map +1 -1
- package/dist/module/version.js +1 -1
- package/dist/typescript/lib/common/Base64.d.ts.map +1 -1
- package/dist/typescript/lib/common/index.d.ts.map +1 -1
- package/dist/typescript/lib/internal/asyncStorage.d.ts.map +1 -1
- package/dist/typescript/lib/internal/logger.d.ts.map +1 -1
- package/dist/typescript/lib/internal/nativeModuleAndroidIos.d.ts.map +1 -1
- package/dist/typescript/lib/internal/nativeModuleDebug.d.ts +2 -0
- package/dist/typescript/lib/internal/nativeModuleDebug.d.ts.map +1 -0
- package/dist/typescript/lib/internal/nativeModuleWeb.d.ts.map +1 -1
- package/dist/typescript/lib/internal/web/RNFBAppModule.d.ts +7 -7
- package/dist/typescript/lib/internal/web/RNFBAppModule.d.ts.map +1 -1
- package/dist/typescript/lib/version.d.ts +1 -1
- package/firebase-schema.json +1 -1
- package/ios/RNFBApp/RNFBAppModule.mm +40 -14
- package/ios/RNFBApp/RNFBHandleMap.h +73 -0
- package/ios/RNFBApp/RNFBHandleMap.m +116 -0
- package/ios/RNFBApp/RNFBSharedUtils.m +3 -2
- package/ios/RNFBApp/RNFBVersion.m +1 -1
- package/ios/RNFBApp.xcodeproj/project.pbxproj +6 -0
- package/ios/RNFBAppUnitTests/RNFBAppUnitTests.xcodeproj/project.pbxproj +237 -0
- package/ios/RNFBAppUnitTests/RNFBAppUnitTests.xcodeproj/xcshareddata/xcschemes/RNFBAppUnitTests.xcscheme +69 -0
- package/ios/RNFBAppUnitTests/RNFBHandleMapTests.m +238 -0
- package/lib/common/Base64.ts +10 -16
- package/lib/common/index.ts +20 -9
- package/lib/internal/asyncStorage.ts +7 -11
- package/lib/internal/logger.ts +53 -43
- package/lib/internal/nativeModuleAndroidIos.ts +2 -40
- package/lib/internal/nativeModuleDebug.ts +97 -0
- package/lib/internal/nativeModuleWeb.ts +10 -40
- package/lib/internal/registry/app.ts +1 -1
- package/lib/internal/registry/modular.ts +2 -2
- package/lib/internal/registry/nativeModule.ts +3 -3
- package/lib/internal/web/RNFBAppModule.ts +16 -15
- package/lib/internal/web/memidb/lib/FakeEventTarget.js +3 -1
- package/lib/version.ts +1 -1
- package/package.json +3 -2
- package/plugin/build/ios/appDelegate.js +18 -8
- package/plugin/src/ios/appDelegate.ts +44 -14
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2016-present Invertase Limited & Contributors
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this library except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
#import <XCTest/XCTest.h>
|
|
19
|
+
|
|
20
|
+
#import "RNFBHandleMap.h"
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* XCTest coverage for RNFBHandleMap. Tests must not invoke methods on stored values — the map only
|
|
24
|
+
* moves pointers.
|
|
25
|
+
*/
|
|
26
|
+
@interface RNFBHandleMapTests : XCTestCase
|
|
27
|
+
@property(nonatomic, strong) RNFBHandleMap *map;
|
|
28
|
+
@end
|
|
29
|
+
|
|
30
|
+
@implementation RNFBHandleMapTests
|
|
31
|
+
|
|
32
|
+
- (void)setUp {
|
|
33
|
+
[super setUp];
|
|
34
|
+
self.map = [[RNFBHandleMap alloc] init];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
- (void)testPutGetTake_happyPath {
|
|
38
|
+
NSObject *handle = [[NSObject alloc] init];
|
|
39
|
+
NSError *error = nil;
|
|
40
|
+
XCTAssertTrue([self.map put:@1 value:handle error:&error]);
|
|
41
|
+
XCTAssertNil(error);
|
|
42
|
+
|
|
43
|
+
XCTAssertEqual(handle, [self.map get:@1]);
|
|
44
|
+
XCTAssertEqual(handle, [self.map take:@1]);
|
|
45
|
+
XCTAssertNil([self.map get:@1]);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
- (void)testTake_missingKey_returnsNil {
|
|
49
|
+
XCTAssertNil([self.map take:@99]);
|
|
50
|
+
XCTAssertNil([self.map get:@99]);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
- (void)testTakeAll_returnsSnapshotAndLeavesMapEmpty {
|
|
54
|
+
NSObject *a = [[NSObject alloc] init];
|
|
55
|
+
NSObject *b = [[NSObject alloc] init];
|
|
56
|
+
XCTAssertTrue([self.map put:@1 value:a error:nil]);
|
|
57
|
+
XCTAssertTrue([self.map put:@2 value:b error:nil]);
|
|
58
|
+
|
|
59
|
+
NSArray *snapshot = [self.map takeAll];
|
|
60
|
+
|
|
61
|
+
XCTAssertEqual(snapshot.count, 2u);
|
|
62
|
+
XCTAssertTrue([snapshot containsObject:a]);
|
|
63
|
+
XCTAssertTrue([snapshot containsObject:b]);
|
|
64
|
+
XCTAssertNil([self.map get:@1]);
|
|
65
|
+
XCTAssertNil([self.map get:@2]);
|
|
66
|
+
XCTAssertEqual([self.map takeAll].count, 0u);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
- (void)testPut_occupiedId_returnsCollision {
|
|
70
|
+
NSObject *first = [[NSObject alloc] init];
|
|
71
|
+
XCTAssertTrue([self.map put:@1 value:first error:nil]);
|
|
72
|
+
|
|
73
|
+
NSError *error = nil;
|
|
74
|
+
XCTAssertFalse([self.map put:@1 value:[[NSObject alloc] init] error:&error]);
|
|
75
|
+
XCTAssertNotNil(error);
|
|
76
|
+
XCTAssertEqualObjects(error.domain, RNFBHandleMapErrorDomain);
|
|
77
|
+
XCTAssertEqual(error.code, RNFBHandleMapErrorCollision);
|
|
78
|
+
XCTAssertTrue([error.localizedDescription containsString:@"1"]);
|
|
79
|
+
XCTAssertEqual(first, [self.map get:@1]);
|
|
80
|
+
|
|
81
|
+
XCTAssertFalse([self.map put:@1 value:[[NSObject alloc] init] error:nil]);
|
|
82
|
+
XCTAssertEqual(first, [self.map get:@1]);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
- (void)testPutIfAbsent_whenFree_storesAndReturnsYes {
|
|
86
|
+
NSObject *handle = [[NSObject alloc] init];
|
|
87
|
+
XCTAssertTrue([self.map putIfAbsent:@1 value:handle]);
|
|
88
|
+
XCTAssertEqual(handle, [self.map get:@1]);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
- (void)testPutIfAbsent_whenOccupied_keepsExistingAndReturnsNo {
|
|
92
|
+
NSObject *first = [[NSObject alloc] init];
|
|
93
|
+
NSObject *second = [[NSObject alloc] init];
|
|
94
|
+
XCTAssertTrue([self.map put:@1 value:first error:nil]);
|
|
95
|
+
XCTAssertFalse([self.map putIfAbsent:@1 value:second]);
|
|
96
|
+
XCTAssertEqual(first, [self.map get:@1]);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
- (void)testPutIfAbsentOrSame_whenAbsent_storesAndReturnsYes {
|
|
100
|
+
NSObject *handle = [[NSObject alloc] init];
|
|
101
|
+
XCTAssertTrue([self.map putIfAbsentOrSame:@1 value:handle]);
|
|
102
|
+
XCTAssertEqual(handle, [self.map get:@1]);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
- (void)testPutIfAbsentOrSame_whenSameInstance_returnsYes {
|
|
106
|
+
NSObject *handle = [[NSObject alloc] init];
|
|
107
|
+
XCTAssertTrue([self.map put:@1 value:handle error:nil]);
|
|
108
|
+
XCTAssertTrue([self.map putIfAbsentOrSame:@1 value:handle]);
|
|
109
|
+
XCTAssertEqual(handle, [self.map get:@1]);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
- (void)testPutIfAbsentOrSame_whenDifferent_returnsNo {
|
|
113
|
+
NSObject *first = [[NSObject alloc] init];
|
|
114
|
+
NSObject *second = [[NSObject alloc] init];
|
|
115
|
+
XCTAssertTrue([self.map put:@1 value:first error:nil]);
|
|
116
|
+
XCTAssertFalse([self.map putIfAbsentOrSame:@1 value:second]);
|
|
117
|
+
XCTAssertEqual(first, [self.map get:@1]);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
- (void)testPutReplacing_whenFree_storesAndReturnsNil {
|
|
121
|
+
NSObject *handle = [[NSObject alloc] init];
|
|
122
|
+
XCTAssertNil([self.map putReplacing:@1 value:handle]);
|
|
123
|
+
XCTAssertEqual(handle, [self.map get:@1]);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
- (void)testPutReplacing_whenOccupied_replacesLastWins {
|
|
127
|
+
NSObject *first = [[NSObject alloc] init];
|
|
128
|
+
NSObject *second = [[NSObject alloc] init];
|
|
129
|
+
XCTAssertTrue([self.map put:@1 value:first error:nil]);
|
|
130
|
+
XCTAssertEqual(first, [self.map putReplacing:@1 value:second]);
|
|
131
|
+
XCTAssertEqual(second, [self.map get:@1]);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
- (void)testPut_afterTake_allowsReuse {
|
|
135
|
+
NSObject *first = [[NSObject alloc] init];
|
|
136
|
+
NSObject *second = [[NSObject alloc] init];
|
|
137
|
+
XCTAssertTrue([self.map put:@1 value:first error:nil]);
|
|
138
|
+
XCTAssertEqual(first, [self.map take:@1]);
|
|
139
|
+
XCTAssertTrue([self.map put:@1 value:second error:nil]);
|
|
140
|
+
XCTAssertEqual(second, [self.map get:@1]);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
- (void)testTakeIf_whenConditionYes_removesAndReturns {
|
|
144
|
+
NSObject *handle = [[NSObject alloc] init];
|
|
145
|
+
XCTAssertTrue([self.map put:@1 value:handle error:nil]);
|
|
146
|
+
XCTAssertEqual(handle, [self.map takeIf:@1
|
|
147
|
+
when:^BOOL(id value) {
|
|
148
|
+
return value == handle;
|
|
149
|
+
}]);
|
|
150
|
+
XCTAssertNil([self.map get:@1]);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
- (void)testTakeIf_whenConditionNo_leavesMapping {
|
|
154
|
+
NSObject *handle = [[NSObject alloc] init];
|
|
155
|
+
XCTAssertTrue([self.map put:@1 value:handle error:nil]);
|
|
156
|
+
XCTAssertNil([self.map takeIf:@1
|
|
157
|
+
when:^BOOL(id value) {
|
|
158
|
+
return NO;
|
|
159
|
+
}]);
|
|
160
|
+
XCTAssertEqual(handle, [self.map get:@1]);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
- (void)testTakeIf_whenMissing_returnsNil {
|
|
164
|
+
XCTAssertNil([self.map takeIf:@99
|
|
165
|
+
when:^BOOL(id value) {
|
|
166
|
+
return YES;
|
|
167
|
+
}]);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
- (void)testTake_sameIdFromTwoThreads_onlyOneReturnsNonNull {
|
|
171
|
+
NSObject *handle = [[NSObject alloc] init];
|
|
172
|
+
XCTAssertTrue([self.map put:@1 value:handle error:nil]);
|
|
173
|
+
|
|
174
|
+
dispatch_semaphore_t start = dispatch_semaphore_create(0);
|
|
175
|
+
dispatch_semaphore_t done = dispatch_semaphore_create(0);
|
|
176
|
+
__block id first = [NSNull null];
|
|
177
|
+
__block id second = [NSNull null];
|
|
178
|
+
|
|
179
|
+
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{
|
|
180
|
+
dispatch_semaphore_wait(start, DISPATCH_TIME_FOREVER);
|
|
181
|
+
first = [self.map take:@1];
|
|
182
|
+
dispatch_semaphore_signal(done);
|
|
183
|
+
});
|
|
184
|
+
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{
|
|
185
|
+
dispatch_semaphore_wait(start, DISPATCH_TIME_FOREVER);
|
|
186
|
+
second = [self.map take:@1];
|
|
187
|
+
dispatch_semaphore_signal(done);
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
dispatch_semaphore_signal(start);
|
|
191
|
+
dispatch_semaphore_signal(start);
|
|
192
|
+
XCTAssertEqual(dispatch_semaphore_wait(done, dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC)),
|
|
193
|
+
0);
|
|
194
|
+
XCTAssertEqual(dispatch_semaphore_wait(done, dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC)),
|
|
195
|
+
0);
|
|
196
|
+
|
|
197
|
+
BOOL onlyFirst = first == handle && second == nil;
|
|
198
|
+
BOOL onlySecond = second == handle && first == nil;
|
|
199
|
+
XCTAssertTrue(onlyFirst || onlySecond);
|
|
200
|
+
XCTAssertNil([self.map get:@1]);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
- (void)testPutReplacing_sameIdFromTwoThreads_lastWins {
|
|
204
|
+
NSObject *first = [[NSObject alloc] init];
|
|
205
|
+
NSObject *second = [[NSObject alloc] init];
|
|
206
|
+
NSObject *third = [[NSObject alloc] init];
|
|
207
|
+
XCTAssertTrue([self.map put:@1 value:first error:nil]);
|
|
208
|
+
|
|
209
|
+
dispatch_semaphore_t start = dispatch_semaphore_create(0);
|
|
210
|
+
dispatch_semaphore_t done = dispatch_semaphore_create(0);
|
|
211
|
+
__block id prevA = [NSNull null];
|
|
212
|
+
__block id prevB = [NSNull null];
|
|
213
|
+
|
|
214
|
+
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{
|
|
215
|
+
dispatch_semaphore_wait(start, DISPATCH_TIME_FOREVER);
|
|
216
|
+
prevA = [self.map putReplacing:@1 value:second];
|
|
217
|
+
dispatch_semaphore_signal(done);
|
|
218
|
+
});
|
|
219
|
+
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{
|
|
220
|
+
dispatch_semaphore_wait(start, DISPATCH_TIME_FOREVER);
|
|
221
|
+
prevB = [self.map putReplacing:@1 value:third];
|
|
222
|
+
dispatch_semaphore_signal(done);
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
dispatch_semaphore_signal(start);
|
|
226
|
+
dispatch_semaphore_signal(start);
|
|
227
|
+
XCTAssertEqual(dispatch_semaphore_wait(done, dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC)),
|
|
228
|
+
0);
|
|
229
|
+
XCTAssertEqual(dispatch_semaphore_wait(done, dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC)),
|
|
230
|
+
0);
|
|
231
|
+
|
|
232
|
+
id winner = [self.map get:@1];
|
|
233
|
+
XCTAssertTrue(winner == second || winner == third);
|
|
234
|
+
id returnedFirst = prevA == first ? prevA : prevB;
|
|
235
|
+
XCTAssertEqual(first, returnedFirst);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
@end
|
package/lib/common/Base64.ts
CHANGED
|
@@ -17,7 +17,6 @@
|
|
|
17
17
|
|
|
18
18
|
// @ts-expect-error - No type declarations available
|
|
19
19
|
import binaryToBase64 from 'react-native/Libraries/Utilities/binaryToBase64';
|
|
20
|
-
import { promiseDefer } from './promise';
|
|
21
20
|
|
|
22
21
|
const CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
|
|
23
22
|
|
|
@@ -90,23 +89,18 @@ export interface Base64Result {
|
|
|
90
89
|
*/
|
|
91
90
|
function fromData(data: Blob | ArrayBuffer | Uint8Array): Promise<Base64Result> {
|
|
92
91
|
if (data instanceof Blob) {
|
|
93
|
-
|
|
94
|
-
|
|
92
|
+
return new Promise<Base64Result>((resolve, reject) => {
|
|
93
|
+
const fileReader = new FileReader();
|
|
95
94
|
|
|
96
|
-
|
|
95
|
+
fileReader.onloadend = () => {
|
|
96
|
+
if (fileReader.result) {
|
|
97
|
+
resolve({ string: fileReader.result, format: 'data_url' });
|
|
98
|
+
}
|
|
99
|
+
};
|
|
97
100
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
}
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
fileReader.onerror = event => {
|
|
105
|
-
fileReader?.abort();
|
|
106
|
-
reject?.(event);
|
|
107
|
-
};
|
|
108
|
-
|
|
109
|
-
return promise;
|
|
101
|
+
fileReader.onerror = event => reject(event);
|
|
102
|
+
fileReader.readAsDataURL(data);
|
|
103
|
+
});
|
|
110
104
|
}
|
|
111
105
|
|
|
112
106
|
if (data instanceof ArrayBuffer || data instanceof Uint8Array) {
|
package/lib/common/index.ts
CHANGED
|
@@ -30,18 +30,29 @@ export { default as ReferenceBase } from './ReferenceBase';
|
|
|
30
30
|
export type { DataUrlParts, Observer };
|
|
31
31
|
|
|
32
32
|
export function getDataUrlParts(dataUrlString: string): DataUrlParts {
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
if (!mediaType || !base64String) {
|
|
33
|
+
const match = /^data:([^,]*?)(;base64)?,([\s\S]*)$/.exec(dataUrlString);
|
|
34
|
+
if (!match) {
|
|
36
35
|
return { base64String: undefined, mediaType: undefined };
|
|
37
36
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
base64String
|
|
37
|
+
|
|
38
|
+
const mediaType = match[1] || undefined;
|
|
39
|
+
let base64String = match[3] ?? '';
|
|
40
|
+
|
|
41
|
+
try {
|
|
42
|
+
if (base64String.includes('%')) {
|
|
43
|
+
base64String = decodeURIComponent(base64String);
|
|
44
|
+
}
|
|
45
|
+
if (!match[2]) {
|
|
46
|
+
const binaryString = encodeURIComponent(base64String).replace(
|
|
47
|
+
/%([0-9A-F]{2})/g,
|
|
48
|
+
(_, hex: string) => String.fromCharCode(Number.parseInt(hex, 16)),
|
|
49
|
+
);
|
|
50
|
+
base64String = Base64.btoa(binaryString);
|
|
51
|
+
}
|
|
52
|
+
} catch (_) {
|
|
53
|
+
return { base64String: undefined, mediaType: undefined };
|
|
44
54
|
}
|
|
55
|
+
|
|
45
56
|
return { base64String, mediaType };
|
|
46
57
|
}
|
|
47
58
|
|
|
@@ -29,11 +29,7 @@ const asyncStorageMemory: AsyncStorageStatic = {
|
|
|
29
29
|
return Promise.resolve();
|
|
30
30
|
},
|
|
31
31
|
getItem(key: string): Promise<string | null> {
|
|
32
|
-
|
|
33
|
-
if (hasValue) {
|
|
34
|
-
return Promise.resolve(memoryStorage.get(key) || null);
|
|
35
|
-
}
|
|
36
|
-
return Promise.resolve(null);
|
|
32
|
+
return Promise.resolve(memoryStorage.get(key) ?? null);
|
|
37
33
|
},
|
|
38
34
|
removeItem(key: string): Promise<void> {
|
|
39
35
|
memoryStorage.delete(key);
|
|
@@ -61,16 +57,16 @@ export function isMemoryStorage(): boolean {
|
|
|
61
57
|
}
|
|
62
58
|
|
|
63
59
|
// Set an item in storage with the React Native Firebase prefix
|
|
64
|
-
export
|
|
65
|
-
return
|
|
60
|
+
export function setItem(key: string, value: string): Promise<void> {
|
|
61
|
+
return asyncStorage.setItem(prefix + key, value);
|
|
66
62
|
}
|
|
67
63
|
|
|
68
64
|
// Get an item from storage with the React Native Firebase prefix
|
|
69
|
-
export
|
|
70
|
-
return
|
|
65
|
+
export function getItem(key: string): Promise<string | null> {
|
|
66
|
+
return asyncStorage.getItem(prefix + key);
|
|
71
67
|
}
|
|
72
68
|
|
|
73
69
|
// Remove an item from storage with the React Native Firebase prefix
|
|
74
|
-
export
|
|
75
|
-
return
|
|
70
|
+
export function removeItem(key: string): Promise<void> {
|
|
71
|
+
return asyncStorage.removeItem(prefix + key);
|
|
76
72
|
}
|
package/lib/internal/logger.ts
CHANGED
|
@@ -108,6 +108,50 @@ const defaultLogHandler: LogHandler = (instance, logType, ...args) => {
|
|
|
108
108
|
|
|
109
109
|
const defaultLogLevel = LogLevel.INFO;
|
|
110
110
|
|
|
111
|
+
let currentLogLevel = defaultLogLevel;
|
|
112
|
+
let currentUserLogHandler: LogHandler | null = null;
|
|
113
|
+
|
|
114
|
+
function createUserLogHandler(
|
|
115
|
+
logCallback: LogCallback | null,
|
|
116
|
+
options?: LogOptions,
|
|
117
|
+
): LogHandler | null {
|
|
118
|
+
if (logCallback === null) {
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const customLogLevel = options?.level ? levelStringToEnum[options.level] : null;
|
|
123
|
+
return (instance, level, ...args) => {
|
|
124
|
+
const message = args
|
|
125
|
+
.map(arg => {
|
|
126
|
+
if (arg == null) {
|
|
127
|
+
return null;
|
|
128
|
+
} else if (typeof arg === 'string') {
|
|
129
|
+
return arg;
|
|
130
|
+
} else if (typeof arg === 'number' || typeof arg === 'boolean') {
|
|
131
|
+
return arg.toString();
|
|
132
|
+
} else if (arg instanceof Error) {
|
|
133
|
+
return arg.message;
|
|
134
|
+
} else {
|
|
135
|
+
try {
|
|
136
|
+
return JSON.stringify(arg);
|
|
137
|
+
} catch (_ignored) {
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
})
|
|
142
|
+
.filter(arg => arg)
|
|
143
|
+
.join(' ');
|
|
144
|
+
if (level >= (customLogLevel ?? instance.logLevel)) {
|
|
145
|
+
logCallback({
|
|
146
|
+
level: LogLevelReversed[level]!.toLowerCase() as LogLevelString,
|
|
147
|
+
message,
|
|
148
|
+
args,
|
|
149
|
+
type: instance.name,
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
111
155
|
/**
|
|
112
156
|
* A container for all of the Logger instances
|
|
113
157
|
*/
|
|
@@ -118,9 +162,9 @@ export const instances: Logger[] = [];
|
|
|
118
162
|
*/
|
|
119
163
|
export class Logger {
|
|
120
164
|
name: string;
|
|
121
|
-
private _logLevel: LogLevel =
|
|
165
|
+
private _logLevel: LogLevel = currentLogLevel;
|
|
122
166
|
private _logHandler: LogHandler = defaultLogHandler;
|
|
123
|
-
private _userLogHandler: LogHandler | null =
|
|
167
|
+
private _userLogHandler: LogHandler | null = currentUserLogHandler;
|
|
124
168
|
|
|
125
169
|
/**
|
|
126
170
|
* Gives you an instance of a Logger to capture messages according to
|
|
@@ -215,9 +259,11 @@ export class Logger {
|
|
|
215
259
|
* Sets the log level for all Logger instances
|
|
216
260
|
*/
|
|
217
261
|
export function setLogLevel(level: LogLevelString | LogLevel): void {
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
262
|
+
const normalizedLevel = typeof level === 'string' ? levelStringToEnum[level] : level;
|
|
263
|
+
currentLogLevel = normalizedLevel;
|
|
264
|
+
for (const instance of instances) {
|
|
265
|
+
instance.setLogLevel(normalizedLevel);
|
|
266
|
+
}
|
|
221
267
|
}
|
|
222
268
|
|
|
223
269
|
// Alias for compatibility
|
|
@@ -227,44 +273,8 @@ export const setLogLevelInternal = setLogLevel;
|
|
|
227
273
|
* Sets a custom user log handler for all Logger instances
|
|
228
274
|
*/
|
|
229
275
|
export function setUserLogHandler(logCallback: LogCallback | null, options?: LogOptions): void {
|
|
276
|
+
currentUserLogHandler = createUserLogHandler(logCallback, options);
|
|
230
277
|
for (const instance of instances) {
|
|
231
|
-
|
|
232
|
-
if (options?.level) {
|
|
233
|
-
customLogLevel = levelStringToEnum[options.level];
|
|
234
|
-
}
|
|
235
|
-
if (logCallback === null) {
|
|
236
|
-
instance.userLogHandler = null;
|
|
237
|
-
} else {
|
|
238
|
-
instance.userLogHandler = (_instance, level, ...args) => {
|
|
239
|
-
const message = args
|
|
240
|
-
.map(arg => {
|
|
241
|
-
if (arg == null) {
|
|
242
|
-
return null;
|
|
243
|
-
} else if (typeof arg === 'string') {
|
|
244
|
-
return arg;
|
|
245
|
-
} else if (typeof arg === 'number' || typeof arg === 'boolean') {
|
|
246
|
-
return arg.toString();
|
|
247
|
-
} else if (arg instanceof Error) {
|
|
248
|
-
return arg.message;
|
|
249
|
-
} else {
|
|
250
|
-
try {
|
|
251
|
-
return JSON.stringify(arg);
|
|
252
|
-
} catch (_ignored) {
|
|
253
|
-
return null;
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
})
|
|
257
|
-
.filter(arg => arg)
|
|
258
|
-
.join(' ');
|
|
259
|
-
if (level >= (customLogLevel ?? _instance.logLevel)) {
|
|
260
|
-
logCallback({
|
|
261
|
-
level: LogLevelReversed[level]!.toLowerCase() as LogLevelString,
|
|
262
|
-
message,
|
|
263
|
-
args,
|
|
264
|
-
type: _instance.name,
|
|
265
|
-
});
|
|
266
|
-
}
|
|
267
|
-
};
|
|
268
|
-
}
|
|
278
|
+
instance.userLogHandler = currentUserLogHandler;
|
|
269
279
|
}
|
|
270
280
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
/* eslint-disable no-console */
|
|
2
1
|
import { TurboModuleRegistry } from 'react-native';
|
|
2
|
+
import { createNativeModuleDebugProxy } from './nativeModuleDebug';
|
|
3
3
|
|
|
4
4
|
const DYNAMIC_CONSTANT_KEYS = new Set(['androidPlayServices']);
|
|
5
5
|
|
|
@@ -64,45 +64,7 @@ export function getReactNativeModule(moduleName: string): Record<string, unknown
|
|
|
64
64
|
return debugProxy;
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
debugProxy =
|
|
68
|
-
ownKeys(target) {
|
|
69
|
-
const keys: string[] = [];
|
|
70
|
-
for (const key in target) {
|
|
71
|
-
keys.push(key);
|
|
72
|
-
}
|
|
73
|
-
return keys;
|
|
74
|
-
},
|
|
75
|
-
get: (_, name) => {
|
|
76
|
-
const prop = (nativeModule as Record<string, unknown>)[name as string];
|
|
77
|
-
if (typeof prop !== 'function') return prop;
|
|
78
|
-
return (...args: unknown[]) => {
|
|
79
|
-
console.debug(
|
|
80
|
-
`[RNFB->Native][🔵] ${moduleName}.${String(name)} -> ${JSON.stringify(args)}`,
|
|
81
|
-
);
|
|
82
|
-
const result: unknown = (prop as (...args: unknown[]) => unknown).apply(nativeModule, args);
|
|
83
|
-
if (result && typeof result === 'object' && 'then' in result) {
|
|
84
|
-
return (result as Promise<unknown>).then(
|
|
85
|
-
(res: unknown) => {
|
|
86
|
-
console.debug(
|
|
87
|
-
`[RNFB<-Native][🟢] ${moduleName}.${String(name)} <- ${JSON.stringify(res)}`,
|
|
88
|
-
);
|
|
89
|
-
return res;
|
|
90
|
-
},
|
|
91
|
-
(err: unknown) => {
|
|
92
|
-
console.debug(
|
|
93
|
-
`[RNFB<-Native][🔴] ${moduleName}.${String(name)} <- ${JSON.stringify(err)}`,
|
|
94
|
-
);
|
|
95
|
-
throw err;
|
|
96
|
-
},
|
|
97
|
-
);
|
|
98
|
-
}
|
|
99
|
-
console.debug(
|
|
100
|
-
`[RNFB<-Native][🟢] ${moduleName}.${String(name)} <- ${JSON.stringify(result)}`,
|
|
101
|
-
);
|
|
102
|
-
return result;
|
|
103
|
-
};
|
|
104
|
-
},
|
|
105
|
-
});
|
|
67
|
+
debugProxy = createNativeModuleDebugProxy(moduleName, nativeModule);
|
|
106
68
|
memoizedDebugProxies.set(moduleName, debugProxy);
|
|
107
69
|
return debugProxy;
|
|
108
70
|
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
|
|
3
|
+
function serializeDebugValue(value: unknown): string {
|
|
4
|
+
const seen = new WeakSet<object>();
|
|
5
|
+
|
|
6
|
+
try {
|
|
7
|
+
const serialized = JSON.stringify(value, (_key, currentValue: unknown) => {
|
|
8
|
+
if (typeof currentValue === 'bigint') {
|
|
9
|
+
return `${currentValue}n`;
|
|
10
|
+
}
|
|
11
|
+
if (currentValue instanceof Error) {
|
|
12
|
+
return { name: currentValue.name, message: currentValue.message };
|
|
13
|
+
}
|
|
14
|
+
if (currentValue && typeof currentValue === 'object') {
|
|
15
|
+
if (seen.has(currentValue)) {
|
|
16
|
+
return '[Circular]';
|
|
17
|
+
}
|
|
18
|
+
seen.add(currentValue);
|
|
19
|
+
}
|
|
20
|
+
return currentValue;
|
|
21
|
+
});
|
|
22
|
+
return serialized ?? String(value);
|
|
23
|
+
} catch (_error) {
|
|
24
|
+
return '[Unserializable]';
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function logDebug(message: string): void {
|
|
29
|
+
try {
|
|
30
|
+
console.debug(message);
|
|
31
|
+
} catch (_error) {
|
|
32
|
+
// Debug logging must not change native module behavior.
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function createNativeModuleDebugProxy(
|
|
37
|
+
moduleName: string,
|
|
38
|
+
nativeModule: Record<string, unknown>,
|
|
39
|
+
): Record<string, unknown> {
|
|
40
|
+
return new Proxy(nativeModule, {
|
|
41
|
+
ownKeys(target) {
|
|
42
|
+
const keys: string[] = [];
|
|
43
|
+
for (const key in target) {
|
|
44
|
+
keys.push(key);
|
|
45
|
+
}
|
|
46
|
+
return keys;
|
|
47
|
+
},
|
|
48
|
+
get: (_, name) => {
|
|
49
|
+
const prop = nativeModule[name as string];
|
|
50
|
+
if (typeof prop !== 'function') return prop;
|
|
51
|
+
|
|
52
|
+
return (...args: unknown[]) => {
|
|
53
|
+
logDebug(
|
|
54
|
+
`[RNFB->Native][🔵] ${moduleName}.${String(name)} -> ${serializeDebugValue(args)}`,
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
let result: unknown;
|
|
58
|
+
try {
|
|
59
|
+
result = Reflect.apply(prop, nativeModule, args);
|
|
60
|
+
} catch (error) {
|
|
61
|
+
logDebug(
|
|
62
|
+
`[RNFB<-Native][🔴] ${moduleName}.${String(name)} <- ${serializeDebugValue(error)}`,
|
|
63
|
+
);
|
|
64
|
+
throw error;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (
|
|
68
|
+
result &&
|
|
69
|
+
typeof result === 'object' &&
|
|
70
|
+
typeof (result as Promise<unknown>).then === 'function'
|
|
71
|
+
) {
|
|
72
|
+
return (result as Promise<unknown>).then(
|
|
73
|
+
resolvedValue => {
|
|
74
|
+
logDebug(
|
|
75
|
+
`[RNFB<-Native][🟢] ${moduleName}.${String(name)} <- ${serializeDebugValue(
|
|
76
|
+
resolvedValue,
|
|
77
|
+
)}`,
|
|
78
|
+
);
|
|
79
|
+
return resolvedValue;
|
|
80
|
+
},
|
|
81
|
+
error => {
|
|
82
|
+
logDebug(
|
|
83
|
+
`[RNFB<-Native][🔴] ${moduleName}.${String(name)} <- ${serializeDebugValue(error)}`,
|
|
84
|
+
);
|
|
85
|
+
throw error;
|
|
86
|
+
},
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
logDebug(
|
|
91
|
+
`[RNFB<-Native][🟢] ${moduleName}.${String(name)} <- ${serializeDebugValue(result)}`,
|
|
92
|
+
);
|
|
93
|
+
return result;
|
|
94
|
+
};
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
}
|