noibu-react-native 0.2.33-rc.1 → 0.2.33-rc.2
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/android/build.gradle +3 -11
- package/android/src/newarch/java/com/noibu/sessionreplay/reactnative/NoibuSessionRecorderModule.kt +4 -6
- package/android/src/newarch/java/com/noibu/sessionreplay/reactnative/NoibuSessionReplayPackage.kt +10 -11
- package/dist/constants.js +1 -1
- package/dist/sessionRecorder/nativeSessionRecorderSubscription.js +79 -62
- package/ios/IOSPocEmitter.swift +2 -2
- package/ios/RCTNoibuSessionRecorder.h +5 -0
- package/ios/RCTNoibuSessionRecorder.mm +112 -0
- package/noibu-react-native.podspec +14 -3
- package/package.json +8 -4
- package/specs/NativeSessionRecorder.ts +10 -0
- package/src/native/NativeNoibuSessionRecorder.ts +0 -52
package/android/build.gradle
CHANGED
|
@@ -64,15 +64,7 @@ repositories {
|
|
|
64
64
|
def kotlin_version = getExtOrDefault("kotlinVersion")
|
|
65
65
|
|
|
66
66
|
dependencies {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
if (isNewArchitectureEnabled()) {
|
|
73
|
-
react {
|
|
74
|
-
jsRootDir = file("../src/")
|
|
75
|
-
libraryName = "NoibuSessionRecorder"
|
|
76
|
-
codegenJavaPackageName = "com.noibu.sessionreplay.reactnative"
|
|
77
|
-
}
|
|
67
|
+
implementation "com.facebook.react:react-native:+"
|
|
68
|
+
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
|
69
|
+
implementation "com.noibu:sessionreplay-recorder:1.0.5-rc.1"
|
|
78
70
|
}
|
package/android/src/newarch/java/com/noibu/sessionreplay/reactnative/NoibuSessionRecorderModule.kt
CHANGED
|
@@ -11,7 +11,7 @@ import com.noibu.mobile.android.sessionreplay.Noibu
|
|
|
11
11
|
import com.noibu.mobile.android.sessionreplay.NoibuConfig
|
|
12
12
|
import java.util.concurrent.ConcurrentLinkedQueue
|
|
13
13
|
|
|
14
|
-
class NoibuSessionRecorderModule(val reactContext: ReactApplicationContext) :
|
|
14
|
+
class NoibuSessionRecorderModule(val reactContext: ReactApplicationContext) : NativeSessionRecorderSpec(reactContext) {
|
|
15
15
|
companion object {
|
|
16
16
|
private const val TAG = "NoibuSessionRecorder"
|
|
17
17
|
private const val MAX_BATCH = 100
|
|
@@ -25,11 +25,10 @@ class NoibuSessionRecorderModule(val reactContext: ReactApplicationContext) : Re
|
|
|
25
25
|
|
|
26
26
|
override fun getName(): String = "NoibuSessionRecorder"
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
fun initialize(promise: Promise) {
|
|
28
|
+
override fun initialize(promise: Promise) {
|
|
30
29
|
Log.i(TAG, "[new-arch] initialize() called")
|
|
31
30
|
try {
|
|
32
|
-
val context = currentActivity?.applicationContext ?: reactApplicationContext.applicationContext
|
|
31
|
+
val context = reactContext.currentActivity?.applicationContext ?: reactApplicationContext.applicationContext
|
|
33
32
|
val config = NoibuConfig(
|
|
34
33
|
sessionReplayEnabled = true,
|
|
35
34
|
maskAllTextInputs = false,
|
|
@@ -46,8 +45,7 @@ class NoibuSessionRecorderModule(val reactContext: ReactApplicationContext) : Re
|
|
|
46
45
|
}
|
|
47
46
|
}
|
|
48
47
|
|
|
49
|
-
|
|
50
|
-
fun consumeEvents(promise: Promise) {
|
|
48
|
+
override fun consumeEvents(promise: Promise) {
|
|
51
49
|
try {
|
|
52
50
|
val array = Arguments.createArray()
|
|
53
51
|
var count = 0
|
package/android/src/newarch/java/com/noibu/sessionreplay/reactnative/NoibuSessionReplayPackage.kt
CHANGED
|
@@ -1,28 +1,27 @@
|
|
|
1
1
|
package com.noibu.sessionreplay.reactnative
|
|
2
2
|
|
|
3
|
-
import com.facebook.react.
|
|
3
|
+
import com.facebook.react.BaseReactPackage
|
|
4
4
|
import com.facebook.react.bridge.NativeModule
|
|
5
5
|
import com.facebook.react.bridge.ReactApplicationContext
|
|
6
6
|
import com.facebook.react.module.model.ReactModuleInfo
|
|
7
7
|
import com.facebook.react.module.model.ReactModuleInfoProvider
|
|
8
8
|
|
|
9
|
-
class NoibuSessionReplayPackage :
|
|
9
|
+
class NoibuSessionReplayPackage : BaseReactPackage() {
|
|
10
10
|
override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? {
|
|
11
|
-
return if (name ==
|
|
11
|
+
return if (name == NativeSessionRecorderSpec.NAME) {
|
|
12
12
|
NoibuSessionRecorderModule(reactContext)
|
|
13
13
|
} else null
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
override fun getReactModuleInfoProvider(): ReactModuleInfoProvider = ReactModuleInfoProvider {
|
|
17
17
|
mapOf(
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
/* isTurboModule */ true
|
|
18
|
+
NativeSessionRecorderSpec.NAME to ReactModuleInfo(/* name */
|
|
19
|
+
name = NativeSessionRecorderSpec.NAME,
|
|
20
|
+
className = NativeSessionRecorderSpec.NAME,
|
|
21
|
+
canOverrideExistingModule = false,
|
|
22
|
+
needsEagerInit = false,
|
|
23
|
+
isCxxModule = false,
|
|
24
|
+
isTurboModule = true
|
|
26
25
|
)
|
|
27
26
|
)
|
|
28
27
|
}
|
package/dist/constants.js
CHANGED
|
@@ -24,7 +24,7 @@ const CONTENT_TYPE = 'content-type';
|
|
|
24
24
|
* Gets the script id from the cookie object, returns default if cannot be found
|
|
25
25
|
*/
|
|
26
26
|
function GET_SCRIPT_ID() {
|
|
27
|
-
return "1.0.104-rn-sdk-0.2.33-rc.
|
|
27
|
+
return "1.0.104-rn-sdk-0.2.33-rc.2" ;
|
|
28
28
|
}
|
|
29
29
|
/**
|
|
30
30
|
* Gets the max metro recon number
|
|
@@ -11,7 +11,9 @@ const LINKING_ERROR = `The package 'noibu-session-replay' doesn't seem to be lin
|
|
|
11
11
|
const RNModule = (_a = NativeModules.NoibuSessionRecorder) !== null && _a !== void 0 ? _a : NativeModules.NativeSessionRecorder;
|
|
12
12
|
const NativeSessionRecorder = TurboNativeSessionRecorder !== null && TurboNativeSessionRecorder !== void 0 ? TurboNativeSessionRecorder : RNModule;
|
|
13
13
|
// Consider new-arch if the resolved module exposes the polling method
|
|
14
|
-
const
|
|
14
|
+
const isNewArch = typeof (NativeSessionRecorder === null || NativeSessionRecorder === void 0 ? void 0 : NativeSessionRecorder.consumeEvents) === 'function';
|
|
15
|
+
const isNewArchAndroid = Platform.OS === 'android' && isNewArch;
|
|
16
|
+
const isNewArchIOS = Platform.OS === 'ios' && isNewArch;
|
|
15
17
|
let nativeModuleEmitter;
|
|
16
18
|
const SupportedPlatforms = ['android', 'ios'];
|
|
17
19
|
/** The level of logging to show in the device logcat stream. */
|
|
@@ -30,6 +32,10 @@ var LogLevel;
|
|
|
30
32
|
*/
|
|
31
33
|
function initialize() {
|
|
32
34
|
if (Platform.OS === 'ios') {
|
|
35
|
+
// In new architecture, initialize native module to start capturing
|
|
36
|
+
if (isNewArchIOS) {
|
|
37
|
+
NativeSessionRecorder.initialize();
|
|
38
|
+
}
|
|
33
39
|
return;
|
|
34
40
|
}
|
|
35
41
|
if (!SupportedPlatforms.includes(Platform.OS)) {
|
|
@@ -69,84 +75,95 @@ let isIOSInitialized = false;
|
|
|
69
75
|
* @throws {Error} If the Noibu Session Recorder is not initialized before calling this function.
|
|
70
76
|
*/
|
|
71
77
|
function subscribeToNativeEvent(callback) {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
(function
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
noibuLog("
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
callback({ message: JSON.stringify(e) });
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
catch (e) {
|
|
94
|
-
// Skip malformed items
|
|
78
|
+
noibuLog('will subscribe to NativeEvent');
|
|
79
|
+
// Shared new-architecture polling (Android/iOS)
|
|
80
|
+
const startNewArchPump = () => {
|
|
81
|
+
let cancelled = false;
|
|
82
|
+
const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
|
|
83
|
+
(function pump() {
|
|
84
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
85
|
+
while (!cancelled) {
|
|
86
|
+
try {
|
|
87
|
+
const batch = yield NativeSessionRecorder.consumeEvents();
|
|
88
|
+
if (Array.isArray(batch) && batch.length > 0) {
|
|
89
|
+
noibuLog("Actual batch: ", batch);
|
|
90
|
+
for (const message of batch) {
|
|
91
|
+
try {
|
|
92
|
+
const _a = JSON.parse(message), { data } = _a, rest = __rest(_a, ["data"]);
|
|
93
|
+
const transformedEvents = transformToWeb([Object.assign(Object.assign({}, rest), { data })]);
|
|
94
|
+
for (const e of transformedEvents) {
|
|
95
|
+
callback({ message: JSON.stringify(e) });
|
|
95
96
|
}
|
|
96
97
|
}
|
|
98
|
+
catch (_e) {
|
|
99
|
+
// Skip malformed items
|
|
100
|
+
}
|
|
97
101
|
}
|
|
98
102
|
}
|
|
99
|
-
catch (err) {
|
|
100
|
-
// Swallow errors and continue polling
|
|
101
|
-
}
|
|
102
|
-
yield sleep(50);
|
|
103
103
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
const transformedEvents = transformToWeb([Object.assign(Object.assign({}, rest), { data })]);
|
|
119
|
-
// Emit pre-serialized JSON strings to minimize JS heap during batching
|
|
120
|
-
transformedEvents.forEach(e => callback({ message: JSON.stringify(e) }));
|
|
121
|
-
});
|
|
122
|
-
// return () => subscription.remove();
|
|
123
|
-
}
|
|
124
|
-
if (Platform.OS === 'ios') {
|
|
104
|
+
catch (_err) {
|
|
105
|
+
// Swallow errors and continue polling
|
|
106
|
+
}
|
|
107
|
+
yield sleep(50);
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
})();
|
|
111
|
+
return () => {
|
|
112
|
+
cancelled = true;
|
|
113
|
+
};
|
|
114
|
+
};
|
|
115
|
+
// Shared legacy-architecture event listener (Android/iOS)
|
|
116
|
+
const addLegacyListener = () => {
|
|
117
|
+
var _a;
|
|
125
118
|
if (!nativeModuleEmitter) {
|
|
126
|
-
|
|
127
|
-
|
|
119
|
+
const moduleForEmitter = RNModule !== null && RNModule !== void 0 ? RNModule : NativeSessionRecorder;
|
|
120
|
+
if (!moduleForEmitter) {
|
|
121
|
+
throw new Error('You have to initialize Noibu Session Recorder first');
|
|
122
|
+
}
|
|
123
|
+
nativeModuleEmitter = new NativeEventEmitter(moduleForEmitter);
|
|
128
124
|
}
|
|
129
|
-
|
|
125
|
+
const handleLegacyPayload = (event) => {
|
|
130
126
|
var _a;
|
|
127
|
+
noibuLog('handleLegacyPayload', event);
|
|
131
128
|
try {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
129
|
+
let mobileEvents = [];
|
|
130
|
+
if (event && Array.isArray(event.mobileEvents)) {
|
|
131
|
+
// iOS legacy format
|
|
132
|
+
mobileEvents = event.mobileEvents;
|
|
133
|
+
}
|
|
134
|
+
else if (event && typeof event.message === 'string') {
|
|
135
|
+
// Android legacy format: single JSON string containing one mobile event
|
|
136
|
+
const _b = JSON.parse(event.message), { data } = _b, rest = __rest(_b, ["data"]);
|
|
137
|
+
mobileEvents = [Object.assign(Object.assign({}, rest), { data })];
|
|
138
|
+
}
|
|
139
|
+
else if (typeof event === 'string') {
|
|
140
|
+
// Fallback: raw string message
|
|
141
|
+
const _c = JSON.parse(event), { data } = _c, rest = __rest(_c, ["data"]);
|
|
142
|
+
mobileEvents = [Object.assign(Object.assign({}, rest), { data })];
|
|
143
|
+
}
|
|
144
|
+
if (mobileEvents.length > 0) {
|
|
145
|
+
const webEvents = transformToWeb(mobileEvents);
|
|
146
|
+
for (const webEvent of webEvents) {
|
|
147
|
+
callback({ message: JSON.stringify(webEvent) });
|
|
148
|
+
}
|
|
137
149
|
}
|
|
138
150
|
}
|
|
139
151
|
catch (err) {
|
|
140
|
-
noibuLog(`[Error]
|
|
152
|
+
noibuLog(`[Error] Legacy transformToWeb failed: ${(_a = err === null || err === void 0 ? void 0 : err.message) !== null && _a !== void 0 ? _a : err}`);
|
|
141
153
|
}
|
|
142
|
-
}
|
|
143
|
-
|
|
154
|
+
};
|
|
155
|
+
nativeModuleEmitter.addListener('noibuRecordingEvent', handleLegacyPayload);
|
|
156
|
+
if (Platform.OS === 'ios' && !isIOSInitialized) {
|
|
144
157
|
(_a = RNModule === null || RNModule === void 0 ? void 0 : RNModule.startIOS) === null || _a === void 0 ? void 0 : _a.call(RNModule);
|
|
145
158
|
isIOSInitialized = true;
|
|
146
159
|
}
|
|
147
|
-
// return () => subscription
|
|
160
|
+
// return () => subscription?.remove();
|
|
161
|
+
return () => { };
|
|
162
|
+
};
|
|
163
|
+
if (isNewArch) {
|
|
164
|
+
return startNewArchPump();
|
|
148
165
|
}
|
|
149
|
-
return ()
|
|
166
|
+
return addLegacyListener();
|
|
150
167
|
}
|
|
151
168
|
|
|
152
169
|
export { LogLevel, initialize, subscribeToNativeEvent };
|
package/ios/IOSPocEmitter.swift
CHANGED
|
@@ -16,7 +16,7 @@ class NoibuSessionRecorder: RCTEventEmitter {
|
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
override func supportedEvents() -> [String]! {
|
|
19
|
-
return ["
|
|
19
|
+
return ["noibuRecordingEvent"]
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
override func startObserving() {
|
|
@@ -36,7 +36,7 @@ class NoibuSessionRecorder: RCTEventEmitter {
|
|
|
36
36
|
|
|
37
37
|
config.onReactNativeCallback = { [weak self] mobileEvents in
|
|
38
38
|
guard let self = self, self.bridge != nil else { return }
|
|
39
|
-
self.sendEvent(withName: "
|
|
39
|
+
self.sendEvent(withName: "noibuRecordingEvent", body: ["mobileEvents": mobileEvents])
|
|
40
40
|
}
|
|
41
41
|
NoibuSDKManager.shared.setup(config)
|
|
42
42
|
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
// Must be Obj-C++
|
|
2
|
+
#import "RCTNoibuSessionRecorder.h"
|
|
3
|
+
|
|
4
|
+
#import <ReactCommon/RCTTurboModule.h>
|
|
5
|
+
// Import the generated header (TurboModule spec)
|
|
6
|
+
#import <NoibuSessionRecorder/NoibuSessionRecorder.h>
|
|
7
|
+
// Import Noibu SDK Swift interfaces exposed to ObjC
|
|
8
|
+
#if __has_include(<NoibuSDK/NoibuSDK-Swift.h>)
|
|
9
|
+
#import <NoibuSDK/NoibuSDK-Swift.h>
|
|
10
|
+
#elif __has_include("NoibuSDK-Swift.h")
|
|
11
|
+
#import "NoibuSDK-Swift.h"
|
|
12
|
+
#endif
|
|
13
|
+
|
|
14
|
+
@interface RCTNoibuSessionRecorder () <NativeSessionRecorderSpec>
|
|
15
|
+
@end
|
|
16
|
+
|
|
17
|
+
@implementation RCTNoibuSessionRecorder
|
|
18
|
+
|
|
19
|
+
RCT_EXPORT_MODULE(NoibuSessionRecorder)
|
|
20
|
+
|
|
21
|
+
+ (BOOL)requiresMainQueueSetup { return NO; }
|
|
22
|
+
|
|
23
|
+
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
|
|
24
|
+
(const facebook::react::ObjCTurboModule::InitParams &)params
|
|
25
|
+
{
|
|
26
|
+
return std::make_shared<facebook::react::NativeSessionRecorderSpecJSI>(params);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
static const NSInteger kMaxBatch = 100;
|
|
30
|
+
|
|
31
|
+
// Simple thread-safe queue of JSON strings
|
|
32
|
+
static dispatch_queue_t noibu_queue_lock;
|
|
33
|
+
static NSMutableArray<NSString *> *noibu_queue_storage;
|
|
34
|
+
|
|
35
|
+
static void ensure_queue_initialized(void) {
|
|
36
|
+
static dispatch_once_t onceToken;
|
|
37
|
+
dispatch_once(&onceToken, ^{
|
|
38
|
+
noibu_queue_lock = dispatch_queue_create("com.noibu.sessionreplay.queue", DISPATCH_QUEUE_SERIAL);
|
|
39
|
+
noibu_queue_storage = [NSMutableArray array];
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
static void enqueue_json_string(NSString *item) {
|
|
44
|
+
ensure_queue_initialized();
|
|
45
|
+
if (item.length == 0) { return; }
|
|
46
|
+
dispatch_sync(noibu_queue_lock, ^{
|
|
47
|
+
[noibu_queue_storage addObject:item];
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
static NSArray<NSString *> *drain_queue(NSUInteger maxCount) {
|
|
52
|
+
ensure_queue_initialized();
|
|
53
|
+
__block NSArray<NSString *> *result = @[];
|
|
54
|
+
dispatch_sync(noibu_queue_lock, ^{
|
|
55
|
+
if (noibu_queue_storage.count == 0) { return; }
|
|
56
|
+
NSUInteger count = MIN(MAX(1, maxCount), (NSUInteger)noibu_queue_storage.count);
|
|
57
|
+
result = [noibu_queue_storage subarrayWithRange:NSMakeRange(0, count)];
|
|
58
|
+
[noibu_queue_storage removeObjectsInRange:NSMakeRange(0, count)];
|
|
59
|
+
});
|
|
60
|
+
return result ?: @[];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
- (void)initialize:(RCTPromiseResolveBlock)resolve
|
|
64
|
+
reject:(RCTPromiseRejectBlock)reject
|
|
65
|
+
{
|
|
66
|
+
@try {
|
|
67
|
+
ensure_queue_initialized();
|
|
68
|
+
|
|
69
|
+
// Construct Noibu SDK configuration (placeholder values)
|
|
70
|
+
NSString *apiKey = @"NOIBU_API_KEY";
|
|
71
|
+
NSString *host = @"https://noibu.com/";
|
|
72
|
+
|
|
73
|
+
#if __has_include(<NoibuSDK/NoibuSDK-Swift.h>) || __has_include("NoibuSDK-Swift.h")
|
|
74
|
+
NoibuConfig *config = [[NoibuConfig alloc] apiKey:apiKey host:host];
|
|
75
|
+
// Enable session replay by default, leave other options as defaults
|
|
76
|
+
// Bridge callback to enqueue raw mobile events for JS polling
|
|
77
|
+
|
|
78
|
+
config.onReactNativeCallback = ^(NSArray<NSDictionary *> *mobileEvents) {
|
|
79
|
+
for (id item in mobileEvents) {
|
|
80
|
+
if ([item isKindOfClass:[NSString class]]) {
|
|
81
|
+
enqueue_json_string((NSString *)item);
|
|
82
|
+
} else if ([NSJSONSerialization isValidJSONObject:item]) {
|
|
83
|
+
NSError *error = nil;
|
|
84
|
+
NSData *data = [NSJSONSerialization dataWithJSONObject:item options:0 error:&error];
|
|
85
|
+
if (data && !error) {
|
|
86
|
+
NSString *json = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
|
87
|
+
if (json.length > 0) { enqueue_json_string(json); }
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
[[NoibuSDKManager shared] setup:config];
|
|
94
|
+
#endif
|
|
95
|
+
resolve(@(YES));
|
|
96
|
+
} @catch (NSException *exception) {
|
|
97
|
+
reject(@"E_INIT", exception.reason ?: @"initialize() failed", nil);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
- (void)consumeEvents:(RCTPromiseResolveBlock)resolve
|
|
102
|
+
reject:(RCTPromiseRejectBlock)reject
|
|
103
|
+
{
|
|
104
|
+
@try {
|
|
105
|
+
NSArray<NSString *> *batch = drain_queue((NSUInteger)kMaxBatch);
|
|
106
|
+
resolve(batch ?: @[]);
|
|
107
|
+
} @catch (NSException *exception) {
|
|
108
|
+
reject(@"E_CONSUME", exception.reason ?: @"consumeEvents() failed", nil);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
@end
|
|
@@ -15,9 +15,20 @@ Pod::Spec.new do |s|
|
|
|
15
15
|
#s.source = { :git => "https://www.github.com/aleguia.git", :tag => "#{s.version}" }
|
|
16
16
|
s.source = { :path => "." }
|
|
17
17
|
|
|
18
|
-
# Include source files
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
# Include source files per-architecture (legacy vs new-arch)
|
|
19
|
+
if ENV['RCT_NEW_ARCH_ENABLED'] == '1'
|
|
20
|
+
# New Architecture: TurboModule implementation only (Objective-C++)
|
|
21
|
+
s.source_files = [
|
|
22
|
+
"ios/RCTNoibuSessionRecorder.h",
|
|
23
|
+
"ios/RCTNoibuSessionRecorder.mm",
|
|
24
|
+
]
|
|
25
|
+
else
|
|
26
|
+
# Legacy Architecture: RCTEventEmitter implementation only
|
|
27
|
+
s.source_files = [
|
|
28
|
+
"ios/IOSPocEmitter.m",
|
|
29
|
+
"ios/IOSPocEmitter.swift",
|
|
30
|
+
]
|
|
31
|
+
end
|
|
21
32
|
s.swift_version = '5.0'
|
|
22
33
|
|
|
23
34
|
# Build as static framework to avoid new-arch linking issues
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "noibu-react-native",
|
|
3
|
-
"version": "0.2.33-rc.
|
|
3
|
+
"version": "0.2.33-rc.2",
|
|
4
4
|
"targetNjsVersion": "1.0.104",
|
|
5
5
|
"description": "React-Native SDK for NoibuJS to collect errors in React-Native applications",
|
|
6
6
|
"main": "dist/entry/index.js",
|
|
@@ -8,16 +8,20 @@
|
|
|
8
8
|
"codegenConfig": {
|
|
9
9
|
"name": "NoibuSessionRecorder",
|
|
10
10
|
"type": "modules",
|
|
11
|
-
"jsSrcsDir": "
|
|
11
|
+
"jsSrcsDir": "specs",
|
|
12
12
|
"android": {
|
|
13
13
|
"javaPackageName": "com.noibu.sessionreplay.reactnative"
|
|
14
|
+
},
|
|
15
|
+
"ios": {
|
|
16
|
+
"modulesProvider": {
|
|
17
|
+
"NoibuSessionRecorder": "RCTNoibuSessionRecorder"
|
|
18
|
+
}
|
|
14
19
|
}
|
|
15
20
|
},
|
|
16
21
|
"files": [
|
|
17
22
|
"android",
|
|
18
|
-
"android/settings.gradle",
|
|
19
23
|
"dist/*",
|
|
20
|
-
"
|
|
24
|
+
"specs/*",
|
|
21
25
|
"README.md",
|
|
22
26
|
"CHANGELOG.md",
|
|
23
27
|
"ios",
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type {TurboModule} from 'react-native';
|
|
2
|
+
import {TurboModuleRegistry} from 'react-native';
|
|
3
|
+
|
|
4
|
+
export interface Spec extends TurboModule {
|
|
5
|
+
initialize(): Promise<boolean>;
|
|
6
|
+
// Returns an array of raw mobile event JSON strings and clears native queue
|
|
7
|
+
consumeEvents?(): Promise<string[]>;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export default TurboModuleRegistry.get<Spec>('NoibuSessionRecorder');
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
// @ts-ignore - Using type from newer React Native versions when building locally
|
|
2
|
-
import type { TurboModule } from 'react-native';
|
|
3
|
-
// Provide a type-only ambient declaration so TS doesn't require a real export
|
|
4
|
-
// This is used only for RN Codegen parsing inside a dead code branch below
|
|
5
|
-
declare const TurboModuleRegistry: {
|
|
6
|
-
get?<T>(name: string): T | null | undefined;
|
|
7
|
-
getEnforcing?<T>(name: string): T;
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
export interface Spec extends TurboModule {
|
|
11
|
-
initialize(): Promise<boolean>;
|
|
12
|
-
// Returns an array of raw mobile event JSON strings and clears native queue
|
|
13
|
-
consumeEvents?(): Promise<string[]>;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
// Ensure the Codegen parser sees Spec usage with TurboModuleRegistry.get<Spec>(..)
|
|
17
|
-
// This block is never executed at runtime but allows codegen to infer the module name.
|
|
18
|
-
// eslint-disable-next-line no-constant-condition, @typescript-eslint/no-unused-expressions
|
|
19
|
-
if (false) {
|
|
20
|
-
// The string must match the native module name
|
|
21
|
-
// @ts-ignore
|
|
22
|
-
TurboModuleRegistry.get<Spec>('NoibuSessionRecorder');
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
// Safely access TurboModuleRegistry without throwing on legacy arch
|
|
26
|
-
type TurboModuleRegistryType = {
|
|
27
|
-
get?<T>(name: string): T | null | undefined;
|
|
28
|
-
getEnforcing?<T>(name: string): T;
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
32
|
-
const { TurboModuleRegistry: TurboModuleRegistryUntyped } = require('react-native') as {
|
|
33
|
-
TurboModuleRegistry?: TurboModuleRegistryType;
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
let moduleRef: Spec | null = null;
|
|
37
|
-
try {
|
|
38
|
-
// Note: We've already destructured the TurboModuleRegistry object into TurboModuleRegistryUntyped
|
|
39
|
-
const tmr = TurboModuleRegistryUntyped as TurboModuleRegistryType | undefined;
|
|
40
|
-
// Prefer non-throwing get() if available
|
|
41
|
-
const maybe = tmr?.get ? tmr.get<Spec>('NoibuSessionRecorder') : undefined;
|
|
42
|
-
if (maybe) {
|
|
43
|
-
moduleRef = maybe;
|
|
44
|
-
} else if (tmr?.getEnforcing) {
|
|
45
|
-
// getEnforcing may throw on legacy; catch and fall back to null
|
|
46
|
-
moduleRef = tmr.getEnforcing<Spec>('NoibuSessionRecorder');
|
|
47
|
-
}
|
|
48
|
-
} catch (_e) {
|
|
49
|
-
moduleRef = null;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export default moduleRef;
|