@npaw/forge-react-native 9.6.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 +10 -0
- package/NPAWForgeReactNative.podspec +44 -0
- package/README.md +51 -0
- package/android/build.gradle +77 -0
- package/android/src/main/AndroidManifest.xml +14 -0
- package/android/src/main/java/com/npaw/forge/reactnative/ForgeDtoMapper.java +295 -0
- package/android/src/main/java/com/npaw/forge/reactnative/ForgeInstanceRegistry.java +61 -0
- package/android/src/main/java/com/npaw/forge/reactnative/NPAWForgeModule.java +735 -0
- package/android/src/main/java/com/npaw/forge/reactnative/NPAWForgePackage.java +53 -0
- package/android/src/test/java/com/npaw/forge/reactnative/ForgeDtoMapperTest.java +133 -0
- package/ios/ForgeApple/ForgeKernelFFI.xcframework/Info.plist +82 -0
- package/ios/ForgeApple/ForgeKernelFFI.xcframework/ios-arm64/Headers/ForgeKernelFFI.h +17 -0
- package/ios/ForgeApple/ForgeKernelFFI.xcframework/ios-arm64/Headers/forge_kernelFFI.h +873 -0
- package/ios/ForgeApple/ForgeKernelFFI.xcframework/ios-arm64/Headers/module.modulemap +7 -0
- package/ios/ForgeApple/ForgeKernelFFI.xcframework/ios-arm64/libForgeKernelFFI.a +0 -0
- package/ios/ForgeApple/ForgeKernelFFI.xcframework/ios-arm64_x86_64-simulator/Headers/ForgeKernelFFI.h +17 -0
- package/ios/ForgeApple/ForgeKernelFFI.xcframework/ios-arm64_x86_64-simulator/Headers/forge_kernelFFI.h +873 -0
- package/ios/ForgeApple/ForgeKernelFFI.xcframework/ios-arm64_x86_64-simulator/Headers/module.modulemap +7 -0
- package/ios/ForgeApple/ForgeKernelFFI.xcframework/ios-arm64_x86_64-simulator/libForgeKernelFFI.a +0 -0
- package/ios/ForgeApple/ForgeKernelFFI.xcframework/tvos-arm64/Headers/ForgeKernelFFI.h +17 -0
- package/ios/ForgeApple/ForgeKernelFFI.xcframework/tvos-arm64/Headers/forge_kernelFFI.h +873 -0
- package/ios/ForgeApple/ForgeKernelFFI.xcframework/tvos-arm64/Headers/module.modulemap +7 -0
- package/ios/ForgeApple/ForgeKernelFFI.xcframework/tvos-arm64/libForgeKernelFFI.a +0 -0
- package/ios/ForgeApple/ForgeKernelFFI.xcframework/tvos-arm64-simulator/Headers/ForgeKernelFFI.h +17 -0
- package/ios/ForgeApple/ForgeKernelFFI.xcframework/tvos-arm64-simulator/Headers/forge_kernelFFI.h +873 -0
- package/ios/ForgeApple/ForgeKernelFFI.xcframework/tvos-arm64-simulator/Headers/module.modulemap +7 -0
- package/ios/ForgeApple/ForgeKernelFFI.xcframework/tvos-arm64-simulator/libForgeKernelFFI.a +0 -0
- package/ios/ForgeApple/Sources/ForgeKernelUniFFI/forge_kernel.swift +3931 -0
- package/ios/ForgeApple/Sources/ForgeKernelUniFFI/forge_kernelFFI.h +873 -0
- package/ios/ForgeApple/Sources/ForgeKernelUniFFI/forge_kernelFFI.modulemap +7 -0
- package/ios/ForgeApple/Sources/NPAWForgeAVPlayerAdapter/AVPlayerAccessLogFacts.swift +152 -0
- package/ios/ForgeApple/Sources/NPAWForgeAVPlayerAdapter/AVPlayerAdapter.swift +877 -0
- package/ios/ForgeApple/Sources/NPAWForgeCapture/AttributionCapture.swift +158 -0
- package/ios/ForgeApple/Sources/NPAWForgeCapture/CapturePlanGate.swift +189 -0
- package/ios/ForgeApple/Sources/NPAWForgeCapture/CrashCapture.swift +120 -0
- package/ios/ForgeApple/Sources/NPAWForgeCapture/ForgeAutomaticCapture.swift +639 -0
- package/ios/ForgeApple/Sources/NPAWForgeCapture/SectionsCapture.swift +1176 -0
- package/ios/ForgeApple/Sources/NPAWForgeCapture/SectionsEngine.swift +674 -0
- package/ios/ForgeApple/Sources/NPAWForgeCapture/SectionsMarkers.swift +105 -0
- package/ios/ForgeApple/Sources/NPAWForgeCapture/SectionsSwiftUI.swift +284 -0
- package/ios/ForgeApple/Sources/NPAWForgeCapture/SwiftUICapture.swift +44 -0
- package/ios/ForgeApple/Sources/NPAWForgeCore/AdAdapter.swift +199 -0
- package/ios/ForgeApple/Sources/NPAWForgeCore/AdSemantics.swift +136 -0
- package/ios/ForgeApple/Sources/NPAWForgeCore/ForgeApple.swift +596 -0
- package/ios/ForgeApple/Sources/NPAWForgeCore/ForgeCapturePlan.swift +297 -0
- package/ios/ForgeApple/Sources/NPAWForgeCore/ForgeClock.swift +29 -0
- package/ios/ForgeApple/Sources/NPAWForgeCore/ForgeLogging.swift +109 -0
- package/ios/ForgeApple/Sources/NPAWForgeCore/ForgeOutcome.swift +89 -0
- package/ios/ForgeApple/Sources/NPAWForgeCore/ForgeRuntime.swift +291 -0
- package/ios/ForgeApple/Sources/NPAWForgeCore/ForgeStorage.swift +70 -0
- package/ios/ForgeApple/Sources/NPAWForgeCore/ManualAdReporter.swift +180 -0
- package/ios/ForgeApple/Sources/NPAWForgeCore/NPAWForge.swift +453 -0
- package/ios/ForgeApple/Sources/NPAWForgeCore/NativeUniFFIRuntime.swift +594 -0
- package/ios/ForgeApple/Sources/NPAWForgeCore/NetworkRequestRecord.swift +191 -0
- package/ios/ForgeApple/Sources/NPAWForgeCore/ObjectiveCWrappers.swift +191 -0
- package/ios/ForgeApple/Sources/NPAWForgeCore/PlayerAdapter.swift +299 -0
- package/ios/ForgeApple/Sources/NPAWForgeCore/PlayerSemantics.swift +266 -0
- package/ios/ForgeApple/Sources/NPAWForgeCore/PrivacySanitizer.swift +59 -0
- package/ios/ForgeApple/Sources/NPAWForgeCore/VideoAdapter.swift +478 -0
- package/ios/ForgeApple/Sources/NPAWForgeCore/VideoOptions.swift +431 -0
- package/ios/ForgeApple/Sources/NPAWForgeNetworkCapture/ForgeNetworkCapture.swift +116 -0
- package/ios/ForgeApple/Sources/NPAWForgeNetworkCaptureObjC/NPAWForgeNetworkSwizzler.m +293 -0
- package/ios/ForgeApple/Sources/NPAWForgeNetworkCaptureObjC/include/NPAWForgeNetworkSwizzler.h +33 -0
- package/ios/ForgeModuleImpl.swift +1131 -0
- package/ios/NPAWForgeTurboModule.mm +399 -0
- package/package.json +33 -0
- package/src/ForgeAdReporter.ts +101 -0
- package/src/ForgeVideo.ts +169 -0
- package/src/NPAWForge.ts +177 -0
- package/src/capture.ts +192 -0
- package/src/events.ts +40 -0
- package/src/index.ts +37 -0
- package/src/specs/NativeNPAWForge.ts +168 -0
- package/src/types.ts +114 -0
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (C) NPAW - All Rights Reserved
|
|
3
|
+
*
|
|
4
|
+
* This source code is protected under international copyright law. All rights
|
|
5
|
+
* reserved and protected by the copyright holders.
|
|
6
|
+
* This file is confidential and only available to authorized individuals with the
|
|
7
|
+
* permission of the copyright holders. If you encounter this file and do not have
|
|
8
|
+
* permission, please contact the copyright holders and delete this file.
|
|
9
|
+
*
|
|
10
|
+
*
|
|
11
|
+
* @author: Victor Soto <victor.soto@nicepeopleatwork.com>
|
|
12
|
+
* @description: Per-(instance, identifier) video facade; mirrors the native VideoAdapter manual fire* surface and creates ad reporters.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import NativeNPAWForge from './specs/NativeNPAWForge';
|
|
16
|
+
import { ForgeAdReporter } from './ForgeAdReporter';
|
|
17
|
+
import type { ForgeOutcome, ForgeParams } from './types';
|
|
18
|
+
|
|
19
|
+
function asOutcome(value: Promise<unknown>): Promise<ForgeOutcome> {
|
|
20
|
+
return value as Promise<ForgeOutcome>;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export class ForgeVideo {
|
|
24
|
+
constructor(
|
|
25
|
+
readonly instanceId: string,
|
|
26
|
+
readonly identifier: string
|
|
27
|
+
) {}
|
|
28
|
+
|
|
29
|
+
fireInit(params: ForgeParams = {}): Promise<ForgeOutcome> {
|
|
30
|
+
return asOutcome(NativeNPAWForge.videoFireInit(this.instanceId, this.identifier, params));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
fireStart(params: ForgeParams = {}, options: { forceInit?: boolean } = {}): Promise<ForgeOutcome> {
|
|
34
|
+
return asOutcome(
|
|
35
|
+
NativeNPAWForge.videoFireStart(this.instanceId, this.identifier, params, options.forceInit ?? false)
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
fireJoin(params: ForgeParams = {}): Promise<ForgeOutcome> {
|
|
40
|
+
return asOutcome(NativeNPAWForge.videoFireJoin(this.instanceId, this.identifier, params));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
firePause(params: ForgeParams = {}): Promise<ForgeOutcome> {
|
|
44
|
+
return asOutcome(NativeNPAWForge.videoFirePause(this.instanceId, this.identifier, params));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
fireResume(params: ForgeParams = {}): Promise<ForgeOutcome> {
|
|
48
|
+
return asOutcome(NativeNPAWForge.videoFireResume(this.instanceId, this.identifier, params));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
fireSeekBegin(params: ForgeParams = {}, options: { convertFromBuffer?: boolean } = {}): Promise<ForgeOutcome> {
|
|
52
|
+
return asOutcome(
|
|
53
|
+
NativeNPAWForge.videoFireSeekBegin(
|
|
54
|
+
this.instanceId,
|
|
55
|
+
this.identifier,
|
|
56
|
+
params,
|
|
57
|
+
options.convertFromBuffer ?? false
|
|
58
|
+
)
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
fireSeekEnd(params: ForgeParams = {}): Promise<ForgeOutcome> {
|
|
63
|
+
return asOutcome(NativeNPAWForge.videoFireSeekEnd(this.instanceId, this.identifier, params));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
fireBufferBegin(params: ForgeParams = {}, options: { convertFromSeek?: boolean } = {}): Promise<ForgeOutcome> {
|
|
67
|
+
return asOutcome(
|
|
68
|
+
NativeNPAWForge.videoFireBufferBegin(
|
|
69
|
+
this.instanceId,
|
|
70
|
+
this.identifier,
|
|
71
|
+
params,
|
|
72
|
+
options.convertFromSeek ?? false
|
|
73
|
+
)
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
fireBufferEnd(params: ForgeParams = {}): Promise<ForgeOutcome> {
|
|
78
|
+
return asOutcome(NativeNPAWForge.videoFireBufferEnd(this.instanceId, this.identifier, params));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
fireStop(params: ForgeParams = {}): Promise<ForgeOutcome> {
|
|
82
|
+
return asOutcome(NativeNPAWForge.videoFireStop(this.instanceId, this.identifier, params));
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
fireError(errorCode?: string, errorMessage?: string, errorMetadata?: string): Promise<ForgeOutcome> {
|
|
86
|
+
return asOutcome(
|
|
87
|
+
NativeNPAWForge.videoFireError(
|
|
88
|
+
this.instanceId,
|
|
89
|
+
this.identifier,
|
|
90
|
+
errorCode ?? '',
|
|
91
|
+
errorMessage ?? '',
|
|
92
|
+
errorMetadata ?? ''
|
|
93
|
+
)
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
fireFatalError(errorCode?: string, errorMessage?: string, errorMetadata?: string): Promise<ForgeOutcome> {
|
|
98
|
+
return asOutcome(
|
|
99
|
+
NativeNPAWForge.videoFireFatalError(
|
|
100
|
+
this.instanceId,
|
|
101
|
+
this.identifier,
|
|
102
|
+
errorCode ?? '',
|
|
103
|
+
errorMessage ?? '',
|
|
104
|
+
errorMetadata ?? ''
|
|
105
|
+
)
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
fireDestroy(): Promise<ForgeOutcome> {
|
|
110
|
+
return asOutcome(NativeNPAWForge.videoFireDestroy(this.instanceId, this.identifier));
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
fireEvent(
|
|
114
|
+
eventName: string,
|
|
115
|
+
dimensions: Record<string, string> = {},
|
|
116
|
+
values: Record<string, number> = {},
|
|
117
|
+
topLevelDimensions: Record<string, string> = {},
|
|
118
|
+
options: { hasEndDatetime?: boolean } = {}
|
|
119
|
+
): Promise<ForgeOutcome> {
|
|
120
|
+
return asOutcome(
|
|
121
|
+
NativeNPAWForge.videoFireEvent(
|
|
122
|
+
this.instanceId,
|
|
123
|
+
this.identifier,
|
|
124
|
+
eventName,
|
|
125
|
+
dimensions,
|
|
126
|
+
values,
|
|
127
|
+
topLevelDimensions,
|
|
128
|
+
options.hasEndDatetime ?? false
|
|
129
|
+
)
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
fireEventEnd(
|
|
134
|
+
eventName: string,
|
|
135
|
+
dimensions: Record<string, string> = {},
|
|
136
|
+
values: Record<string, number> = {},
|
|
137
|
+
topLevelDimensions: Record<string, string> = {}
|
|
138
|
+
): Promise<ForgeOutcome> {
|
|
139
|
+
return asOutcome(
|
|
140
|
+
NativeNPAWForge.videoFireEventEnd(
|
|
141
|
+
this.instanceId,
|
|
142
|
+
this.identifier,
|
|
143
|
+
eventName,
|
|
144
|
+
dimensions,
|
|
145
|
+
values,
|
|
146
|
+
topLevelDimensions
|
|
147
|
+
)
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** Manual metadata refresh (the native facade owns the snapshot semantics). */
|
|
152
|
+
fireMetadataUpdate(): Promise<ForgeOutcome> {
|
|
153
|
+
return asOutcome(NativeNPAWForge.videoFireMetadataUpdate(this.instanceId, this.identifier));
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
async createAdReporter(adapterVersion?: string): Promise<ForgeAdReporter> {
|
|
157
|
+
const reporterId = await NativeNPAWForge.createAdReporter(
|
|
158
|
+
this.instanceId,
|
|
159
|
+
this.identifier,
|
|
160
|
+
adapterVersion ?? ''
|
|
161
|
+
);
|
|
162
|
+
return new ForgeAdReporter(reporterId);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/** Destroys the kernel video view and every native resource behind it. */
|
|
166
|
+
destroy(): Promise<ForgeOutcome> {
|
|
167
|
+
return asOutcome(NativeNPAWForge.videoDestroy(this.instanceId, this.identifier));
|
|
168
|
+
}
|
|
169
|
+
}
|
package/src/NPAWForge.ts
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (C) NPAW - All Rights Reserved
|
|
3
|
+
*
|
|
4
|
+
* This source code is protected under international copyright law. All rights
|
|
5
|
+
* reserved and protected by the copyright holders.
|
|
6
|
+
* This file is confidential and only available to authorized individuals with the
|
|
7
|
+
* permission of the copyright holders. If you encounter this file and do not have
|
|
8
|
+
* permission, please contact the copyright holders and delete this file.
|
|
9
|
+
*
|
|
10
|
+
*
|
|
11
|
+
* @author: Victor Soto <victor.soto@nicepeopleatwork.com>
|
|
12
|
+
* @description: Main @npaw/forge-react-native facade class - sessions, identity, raw/business events, network records, diagnostics, video handles, kernel event feed.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import NativeNPAWForge from './specs/NativeNPAWForge';
|
|
16
|
+
import { ForgeVideo } from './ForgeVideo';
|
|
17
|
+
import { subscribeInstanceEvents } from './events';
|
|
18
|
+
import type {
|
|
19
|
+
ForgeConfig,
|
|
20
|
+
ForgeEventSubscription,
|
|
21
|
+
ForgeNetworkRequestRecord,
|
|
22
|
+
ForgeOutcome,
|
|
23
|
+
ForgeParams,
|
|
24
|
+
ForgeRecordedEvent,
|
|
25
|
+
ForgeVideoOptions,
|
|
26
|
+
} from './types';
|
|
27
|
+
|
|
28
|
+
export const DEFAULT_VIDEO_IDENTIFIER = 'default';
|
|
29
|
+
|
|
30
|
+
function asOutcome(value: Promise<unknown>): Promise<ForgeOutcome> {
|
|
31
|
+
return value as Promise<ForgeOutcome>;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export class NPAWForge {
|
|
35
|
+
private constructor(readonly instanceId: string) {}
|
|
36
|
+
|
|
37
|
+
/** Builds a Forge instance; resolves once the native runtime is live. */
|
|
38
|
+
static async create(accountCode: string, config: ForgeConfig = {}): Promise<NPAWForge> {
|
|
39
|
+
const instanceId = await NativeNPAWForge.create(accountCode, config);
|
|
40
|
+
return new NPAWForge(instanceId);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// ---- lifecycle ----
|
|
44
|
+
|
|
45
|
+
destroy(): Promise<ForgeOutcome> {
|
|
46
|
+
return asOutcome(NativeNPAWForge.destroy(this.instanceId));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
setLogLevel(level: string): Promise<ForgeOutcome> {
|
|
50
|
+
return asOutcome(NativeNPAWForge.setLogLevel(this.instanceId, level));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// ---- app session ----
|
|
54
|
+
|
|
55
|
+
startSession(metadata: ForgeParams = {}): Promise<ForgeOutcome> {
|
|
56
|
+
return asOutcome(NativeNPAWForge.startSession(this.instanceId, metadata));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
endSession(metadata: ForgeParams = {}): Promise<ForgeOutcome> {
|
|
60
|
+
return asOutcome(NativeNPAWForge.endSession(this.instanceId, metadata));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
trackSessionNavigation(metadata: ForgeParams): Promise<ForgeOutcome> {
|
|
64
|
+
return asOutcome(NativeNPAWForge.trackSessionNavigation(this.instanceId, metadata));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
trackSessionEvent(name: string, metadata: ForgeParams = {}): Promise<ForgeOutcome> {
|
|
68
|
+
return asOutcome(NativeNPAWForge.trackSessionEvent(this.instanceId, name, metadata));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
trackSessionEventStop(name: string, metadata: ForgeParams = {}): Promise<ForgeOutcome> {
|
|
72
|
+
return asOutcome(NativeNPAWForge.trackSessionEventStop(this.instanceId, name, metadata));
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
trackSessionError(errorCode: string, errorMessage: string, metadata: ForgeParams = {}): Promise<ForgeOutcome> {
|
|
76
|
+
return asOutcome(NativeNPAWForge.trackSessionError(this.instanceId, errorCode, errorMessage, metadata));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// ---- identity ----
|
|
80
|
+
|
|
81
|
+
loginSuccess(userId: string, options: ForgeParams = {}): Promise<ForgeOutcome> {
|
|
82
|
+
return asOutcome(NativeNPAWForge.loginSuccess(this.instanceId, userId, options));
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
loginFailed(options: ForgeParams = {}): Promise<ForgeOutcome> {
|
|
86
|
+
return asOutcome(NativeNPAWForge.loginFailed(this.instanceId, options));
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
logout(options: ForgeParams = {}): Promise<ForgeOutcome> {
|
|
90
|
+
return asOutcome(NativeNPAWForge.logout(this.instanceId, options));
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
profileCreated(profileId: string, options: ForgeParams = {}): Promise<ForgeOutcome> {
|
|
94
|
+
return asOutcome(NativeNPAWForge.profileCreated(this.instanceId, profileId, options));
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
profileSelected(profileId: string, options: ForgeParams = {}): Promise<ForgeOutcome> {
|
|
98
|
+
return asOutcome(NativeNPAWForge.profileSelected(this.instanceId, profileId, options));
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
profileDeleted(profileId: string): Promise<ForgeOutcome> {
|
|
102
|
+
return asOutcome(NativeNPAWForge.profileDeleted(this.instanceId, profileId));
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// ---- raw / business ----
|
|
106
|
+
|
|
107
|
+
trackRawEvent(name: string, params: ForgeParams = {}): Promise<ForgeOutcome> {
|
|
108
|
+
return asOutcome(NativeNPAWForge.trackRawEvent(this.instanceId, name, params));
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
trackSearch(query: string, resultCount?: number): Promise<ForgeOutcome> {
|
|
112
|
+
return asOutcome(NativeNPAWForge.trackSearch(this.instanceId, query, resultCount ?? -1));
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
trackSearchResultClick(query: string, options: ForgeParams = {}): Promise<ForgeOutcome> {
|
|
116
|
+
return asOutcome(NativeNPAWForge.trackSearchResultClick(this.instanceId, query, options));
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
trackBusinessEvent(name: string, attributes: ForgeParams = {}): Promise<ForgeOutcome> {
|
|
120
|
+
return asOutcome(NativeNPAWForge.trackBusinessEvent(this.instanceId, name, attributes));
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
trackAttribution(options: ForgeParams = {}): Promise<ForgeOutcome> {
|
|
124
|
+
return asOutcome(NativeNPAWForge.trackAttribution(this.instanceId, options));
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// ---- network ----
|
|
128
|
+
|
|
129
|
+
trackNetworkRequest(record: ForgeNetworkRequestRecord): Promise<ForgeOutcome> {
|
|
130
|
+
return asOutcome(NativeNPAWForge.trackNetworkRequest(this.instanceId, record));
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Installs automatic network capture (Android: RN OkHttp factory
|
|
135
|
+
* instrumentation, call before the app's first fetch; iOS: URLSession
|
|
136
|
+
* swizzling). The kernel capture plan still gates what propagates.
|
|
137
|
+
*/
|
|
138
|
+
installNetworkCapture(): Promise<ForgeOutcome> {
|
|
139
|
+
return asOutcome(NativeNPAWForge.installNetworkCapture(this.instanceId, {}));
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// ---- diagnostics / events ----
|
|
143
|
+
|
|
144
|
+
async snapshotEvents(): Promise<ForgeRecordedEvent[]> {
|
|
145
|
+
const result = (await NativeNPAWForge.snapshotEvents(this.instanceId)) as {
|
|
146
|
+
items: ForgeRecordedEvent[];
|
|
147
|
+
};
|
|
148
|
+
return result.items;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** Live feed of every event the kernel records for this instance. */
|
|
152
|
+
onEvent(callback: (event: ForgeRecordedEvent) => void): ForgeEventSubscription {
|
|
153
|
+
return subscribeInstanceEvents(this.instanceId, callback);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// ---- video ----
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Creates (or reuses, per native same-identifier replacement semantics) a
|
|
160
|
+
* video handle. Options flow through the native VideoOptions metadata
|
|
161
|
+
* pipeline (web-parity key naming).
|
|
162
|
+
*/
|
|
163
|
+
async createVideo(
|
|
164
|
+
options: ForgeVideoOptions = {},
|
|
165
|
+
identifier: string = DEFAULT_VIDEO_IDENTIFIER
|
|
166
|
+
): Promise<ForgeVideo> {
|
|
167
|
+
await NativeNPAWForge.createVideo(this.instanceId, identifier, options);
|
|
168
|
+
return new ForgeVideo(this.instanceId, identifier);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/** Returns a handle to an existing video without creating it natively. */
|
|
172
|
+
video(identifier: string = DEFAULT_VIDEO_IDENTIFIER): ForgeVideo {
|
|
173
|
+
return new ForgeVideo(this.instanceId, identifier);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export default NPAWForge;
|
package/src/capture.ts
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (C) NPAW - All Rights Reserved
|
|
3
|
+
*
|
|
4
|
+
* This source code is protected under international copyright law. All rights
|
|
5
|
+
* reserved and protected by the copyright holders.
|
|
6
|
+
* This file is confidential and only available to authorized individuals with the
|
|
7
|
+
* permission of the copyright holders. If you encounter this file and do not have
|
|
8
|
+
* permission, please contact the copyright holders and delete this file.
|
|
9
|
+
*
|
|
10
|
+
*
|
|
11
|
+
* @author: Victor Soto <victor.soto@nicepeopleatwork.com>
|
|
12
|
+
* @description: RN-side capture helpers - JS error/unhandled-rejection observer (native crashes stay native) and screen-view tracking with Web-parity payload keys, plus a react-navigation onStateChange helper.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import type { NPAWForge } from './NPAWForge';
|
|
16
|
+
import type { ForgeEventSubscription } from './types';
|
|
17
|
+
|
|
18
|
+
type GlobalErrorHandler = (error: unknown, isFatal?: boolean) => void;
|
|
19
|
+
|
|
20
|
+
type ErrorUtilsLike = {
|
|
21
|
+
getGlobalHandler: () => GlobalErrorHandler;
|
|
22
|
+
setGlobalHandler: (handler: GlobalErrorHandler) => void;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
type RejectionTrackingModule = {
|
|
26
|
+
enable: (options: {
|
|
27
|
+
allRejections: boolean;
|
|
28
|
+
onUnhandled: (id: number, rejection: unknown) => void;
|
|
29
|
+
onHandled?: (id: number) => void;
|
|
30
|
+
}) => void;
|
|
31
|
+
disable: () => void;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export type ErrorCaptureOptions = {
|
|
35
|
+
/** Also observe unhandled promise rejections. Default true. */
|
|
36
|
+
trackUnhandledRejections?: boolean;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const STACK_LIMIT_CHARS = 8 * 1024; // kernel caps at 8 KiB; pre-trim the JS copy
|
|
40
|
+
|
|
41
|
+
function describeError(error: unknown): { code: string; message: string; stack?: string } {
|
|
42
|
+
if (error instanceof Error) {
|
|
43
|
+
return {
|
|
44
|
+
code: error.name || 'Error',
|
|
45
|
+
message: error.message || String(error),
|
|
46
|
+
stack: error.stack ? error.stack.slice(0, STACK_LIMIT_CHARS) : undefined,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
return { code: 'Error', message: String(error) };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* W1: JS error visibility. Native crashes are already captured by the native
|
|
54
|
+
* SDKs; JS exceptions and unhandled rejections are invisible natively, so
|
|
55
|
+
* this observer forwards them through the typed session-error path. The
|
|
56
|
+
* previous global handler keeps running (RedBox/LogBox in dev, crash
|
|
57
|
+
* semantics in release are unchanged).
|
|
58
|
+
*/
|
|
59
|
+
export function installErrorCapture(
|
|
60
|
+
forge: NPAWForge,
|
|
61
|
+
options: ErrorCaptureOptions = {}
|
|
62
|
+
): ForgeEventSubscription {
|
|
63
|
+
const globalObject = globalThis as {
|
|
64
|
+
ErrorUtils?: ErrorUtilsLike;
|
|
65
|
+
HermesInternal?: unknown;
|
|
66
|
+
};
|
|
67
|
+
const errorUtils = globalObject.ErrorUtils;
|
|
68
|
+
let removed = false;
|
|
69
|
+
let previousHandler: GlobalErrorHandler | undefined;
|
|
70
|
+
|
|
71
|
+
if (errorUtils) {
|
|
72
|
+
previousHandler = errorUtils.getGlobalHandler();
|
|
73
|
+
errorUtils.setGlobalHandler((error, isFatal) => {
|
|
74
|
+
if (!removed) {
|
|
75
|
+
const described = describeError(error);
|
|
76
|
+
void forge
|
|
77
|
+
.trackSessionError(described.code, described.message, {
|
|
78
|
+
source: 'react_native_capture',
|
|
79
|
+
fatal: isFatal ? 'true' : 'false',
|
|
80
|
+
...(described.stack ? { stack: described.stack } : {}),
|
|
81
|
+
})
|
|
82
|
+
.catch(() => {});
|
|
83
|
+
}
|
|
84
|
+
previousHandler?.(error, isFatal);
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
let rejectionTrackingEnabled = false;
|
|
89
|
+
if (options.trackUnhandledRejections !== false) {
|
|
90
|
+
try {
|
|
91
|
+
// The promise polyfill RN installs ships rejection tracking here.
|
|
92
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
93
|
+
const tracking: RejectionTrackingModule = require('promise/setimmediate/rejection-tracking');
|
|
94
|
+
tracking.enable({
|
|
95
|
+
allRejections: true,
|
|
96
|
+
onUnhandled: (_id, rejection) => {
|
|
97
|
+
if (removed) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
const described = describeError(rejection);
|
|
101
|
+
void forge
|
|
102
|
+
.trackSessionError('UnhandledPromiseRejection', described.message, {
|
|
103
|
+
source: 'react_native_capture',
|
|
104
|
+
fatal: 'false',
|
|
105
|
+
...(described.stack ? { stack: described.stack } : {}),
|
|
106
|
+
})
|
|
107
|
+
.catch(() => {});
|
|
108
|
+
},
|
|
109
|
+
});
|
|
110
|
+
rejectionTrackingEnabled = true;
|
|
111
|
+
} catch {
|
|
112
|
+
rejectionTrackingEnabled = false;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return {
|
|
117
|
+
remove: () => {
|
|
118
|
+
removed = true;
|
|
119
|
+
if (errorUtils && previousHandler) {
|
|
120
|
+
errorUtils.setGlobalHandler(previousHandler);
|
|
121
|
+
}
|
|
122
|
+
// Rejection tracking stays enabled (it delegates to the default
|
|
123
|
+
// logger); the removed flag stops Forge forwarding.
|
|
124
|
+
void rejectionTrackingEnabled;
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export type ScreenViewOptions = {
|
|
130
|
+
/** Parameterized route shape, e.g. '/movie/:id'. Defaults to the name. */
|
|
131
|
+
routeTemplate?: string;
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
const lastScreenByInstance = new Map<string, { route: string; page: string }>();
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* W2: manual screen views. Mirrors the Web navigation capture pair: a typed
|
|
138
|
+
* trackSessionNavigation plus the raw_screen_view raw event with the same
|
|
139
|
+
* payload keys (route/page/previousRoute/previousPage/routeTemplate/
|
|
140
|
+
* launchSource), so RN screens land in the same dashboards.
|
|
141
|
+
*/
|
|
142
|
+
export async function trackScreenView(
|
|
143
|
+
forge: NPAWForge,
|
|
144
|
+
screenName: string,
|
|
145
|
+
options: ScreenViewOptions = {}
|
|
146
|
+
): Promise<void> {
|
|
147
|
+
const route = screenName.startsWith('/') ? screenName : `/${screenName}`;
|
|
148
|
+
const previous = lastScreenByInstance.get(forge.instanceId);
|
|
149
|
+
const launchSource = previous ? 'normal' : 'launch';
|
|
150
|
+
lastScreenByInstance.set(forge.instanceId, { route, page: screenName });
|
|
151
|
+
|
|
152
|
+
await forge.trackSessionNavigation({
|
|
153
|
+
route,
|
|
154
|
+
page: screenName,
|
|
155
|
+
source: 'react_native_capture',
|
|
156
|
+
});
|
|
157
|
+
await forge.trackRawEvent('raw_screen_view', {
|
|
158
|
+
route,
|
|
159
|
+
page: screenName,
|
|
160
|
+
...(previous ? { previousRoute: previous.route, previousPage: previous.page } : {}),
|
|
161
|
+
routeTemplate: options.routeTemplate ?? route,
|
|
162
|
+
launchSource,
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
type NavigationStateLike = {
|
|
167
|
+
routes?: Array<{ name?: string }>;
|
|
168
|
+
index?: number;
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* react-navigation glue: pass the returned function to
|
|
173
|
+
* NavigationContainer's onStateChange (and call it once with the initial
|
|
174
|
+
* state from onReady).
|
|
175
|
+
*/
|
|
176
|
+
export function createNavigationObserver(
|
|
177
|
+
forge: NPAWForge
|
|
178
|
+
): (state: NavigationStateLike | undefined) => void {
|
|
179
|
+
let lastName: string | undefined;
|
|
180
|
+
return state => {
|
|
181
|
+
const route =
|
|
182
|
+
state && Array.isArray(state.routes) && typeof state.index === 'number'
|
|
183
|
+
? state.routes[state.index]
|
|
184
|
+
: undefined;
|
|
185
|
+
const name = route?.name;
|
|
186
|
+
if (!name || name === lastName) {
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
lastName = name;
|
|
190
|
+
void trackScreenView(forge, name).catch(() => {});
|
|
191
|
+
};
|
|
192
|
+
}
|
package/src/events.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (C) NPAW - All Rights Reserved
|
|
3
|
+
*
|
|
4
|
+
* This source code is protected under international copyright law. All rights
|
|
5
|
+
* reserved and protected by the copyright holders.
|
|
6
|
+
* This file is confidential and only available to authorized individuals with the
|
|
7
|
+
* permission of the copyright holders. If you encounter this file and do not have
|
|
8
|
+
* permission, please contact the copyright holders and delete this file.
|
|
9
|
+
*
|
|
10
|
+
*
|
|
11
|
+
* @author: Victor Soto <victor.soto@nicepeopleatwork.com>
|
|
12
|
+
* @description: Kernel event feed wiring - single NativeEventEmitter channel shared by every Forge instance, filtered per instance id.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { NativeEventEmitter, NativeModules } from 'react-native';
|
|
16
|
+
import NativeNPAWForge from './specs/NativeNPAWForge';
|
|
17
|
+
import type { ForgeEventSubscription, ForgeRecordedEvent } from './types';
|
|
18
|
+
|
|
19
|
+
export const FORGE_EVENT_CHANNEL = 'NPAWForgeEvent';
|
|
20
|
+
|
|
21
|
+
const emitter = new NativeEventEmitter(
|
|
22
|
+
// The spec module implements addListener/removeListeners; iOS needs the
|
|
23
|
+
// module object itself for the classic emitter contract.
|
|
24
|
+
NativeModules.NPAWForgeRN ?? (NativeNPAWForge as never)
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
export function subscribeInstanceEvents(
|
|
28
|
+
instanceId: string,
|
|
29
|
+
callback: (event: ForgeRecordedEvent) => void
|
|
30
|
+
): ForgeEventSubscription {
|
|
31
|
+
const subscription = emitter.addListener(
|
|
32
|
+
FORGE_EVENT_CHANNEL,
|
|
33
|
+
(event: ForgeRecordedEvent) => {
|
|
34
|
+
if (event.instanceId === instanceId) {
|
|
35
|
+
callback(event);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
);
|
|
39
|
+
return { remove: () => subscription.remove() };
|
|
40
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (C) NPAW - All Rights Reserved
|
|
3
|
+
*
|
|
4
|
+
* This source code is protected under international copyright law. All rights
|
|
5
|
+
* reserved and protected by the copyright holders.
|
|
6
|
+
* This file is confidential and only available to authorized individuals with the
|
|
7
|
+
* permission of the copyright holders. If you encounter this file and do not have
|
|
8
|
+
* permission, please contact the copyright holders and delete this file.
|
|
9
|
+
*
|
|
10
|
+
*
|
|
11
|
+
* @author: Victor Soto <victor.soto@nicepeopleatwork.com>
|
|
12
|
+
* @description: Public exports for @npaw/forge-react-native.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
export { NPAWForge, DEFAULT_VIDEO_IDENTIFIER } from './NPAWForge';
|
|
16
|
+
export { ForgeVideo } from './ForgeVideo';
|
|
17
|
+
export { ForgeAdReporter } from './ForgeAdReporter';
|
|
18
|
+
export { FORGE_EVENT_CHANNEL } from './events';
|
|
19
|
+
export {
|
|
20
|
+
installErrorCapture,
|
|
21
|
+
trackScreenView,
|
|
22
|
+
createNavigationObserver,
|
|
23
|
+
} from './capture';
|
|
24
|
+
export type { ErrorCaptureOptions, ScreenViewOptions } from './capture';
|
|
25
|
+
export type {
|
|
26
|
+
ForgeConfig,
|
|
27
|
+
ForgeEventSubscription,
|
|
28
|
+
ForgeNetworkOutcome,
|
|
29
|
+
ForgeNetworkRequestRecord,
|
|
30
|
+
ForgeOutcome,
|
|
31
|
+
ForgeOutcomeCode,
|
|
32
|
+
ForgeParams,
|
|
33
|
+
ForgeRecordedEvent,
|
|
34
|
+
ForgeVideoOptions,
|
|
35
|
+
} from './types';
|
|
36
|
+
|
|
37
|
+
export { NPAWForge as default } from './NPAWForge';
|