@truewatchtech/cocos-sdk 0.1.0-alpha.3

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.
Files changed (169) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +51 -0
  3. package/THIRD_PARTY_NOTICES +5 -0
  4. package/bin/cli.cjs +130 -0
  5. package/creator2/package.json +4 -0
  6. package/creator3/package.json +5 -0
  7. package/dist/creator2/core/auto-tracking.d.ts +35 -0
  8. package/dist/creator2/core/auto-tracking.d.ts.map +1 -0
  9. package/dist/creator2/core/auto-tracking.js +337 -0
  10. package/dist/creator2/core/auto-tracking.js.map +1 -0
  11. package/dist/creator2/core/client.d.ts +74 -0
  12. package/dist/creator2/core/client.d.ts.map +1 -0
  13. package/dist/creator2/core/client.js +176 -0
  14. package/dist/creator2/core/client.js.map +1 -0
  15. package/dist/creator2/core/index.d.ts +3 -0
  16. package/dist/creator2/core/index.d.ts.map +1 -0
  17. package/dist/creator2/core/index.js +6 -0
  18. package/dist/creator2/core/index.js.map +1 -0
  19. package/dist/creator2/core/modules.d.ts +159 -0
  20. package/dist/creator2/core/modules.d.ts.map +1 -0
  21. package/dist/creator2/core/modules.js +319 -0
  22. package/dist/creator2/core/modules.js.map +1 -0
  23. package/dist/creator2/core/replay-budget.d.ts +28 -0
  24. package/dist/creator2/core/replay-budget.d.ts.map +1 -0
  25. package/dist/creator2/core/replay-budget.js +113 -0
  26. package/dist/creator2/core/replay-budget.js.map +1 -0
  27. package/dist/creator2/core/replay-diagnostics.d.ts +36 -0
  28. package/dist/creator2/core/replay-diagnostics.d.ts.map +1 -0
  29. package/dist/creator2/core/replay-diagnostics.js +40 -0
  30. package/dist/creator2/core/replay-diagnostics.js.map +1 -0
  31. package/dist/creator2/core/replay-encoding.d.ts +31 -0
  32. package/dist/creator2/core/replay-encoding.d.ts.map +1 -0
  33. package/dist/creator2/core/replay-encoding.js +63 -0
  34. package/dist/creator2/core/replay-encoding.js.map +1 -0
  35. package/dist/creator2/core/replay-pixels.d.ts +3 -0
  36. package/dist/creator2/core/replay-pixels.d.ts.map +1 -0
  37. package/dist/creator2/core/replay-pixels.js +16 -0
  38. package/dist/creator2/core/replay-pixels.js.map +1 -0
  39. package/dist/creator2/core/replay-privacy.d.ts +15 -0
  40. package/dist/creator2/core/replay-privacy.d.ts.map +1 -0
  41. package/dist/creator2/core/replay-privacy.js +36 -0
  42. package/dist/creator2/core/replay-privacy.js.map +1 -0
  43. package/dist/creator2/core/replay.d.ts +97 -0
  44. package/dist/creator2/core/replay.d.ts.map +1 -0
  45. package/dist/creator2/core/replay.js +578 -0
  46. package/dist/creator2/core/replay.js.map +1 -0
  47. package/dist/creator2/core/transport.d.ts +16 -0
  48. package/dist/creator2/core/transport.d.ts.map +1 -0
  49. package/dist/creator2/core/transport.js +33 -0
  50. package/dist/creator2/core/transport.js.map +1 -0
  51. package/dist/creator2/core/types.d.ts +260 -0
  52. package/dist/creator2/core/types.d.ts.map +1 -0
  53. package/dist/creator2/core/types.js +3 -0
  54. package/dist/creator2/core/types.js.map +1 -0
  55. package/dist/creator2/core/validation.d.ts +14 -0
  56. package/dist/creator2/core/validation.d.ts.map +1 -0
  57. package/dist/creator2/core/validation.js +81 -0
  58. package/dist/creator2/core/validation.js.map +1 -0
  59. package/dist/creator2/core/version.d.ts +9 -0
  60. package/dist/creator2/core/version.d.ts.map +1 -0
  61. package/dist/creator2/core/version.js +14 -0
  62. package/dist/creator2/core/version.js.map +1 -0
  63. package/dist/creator2/creator2/auto.d.ts +9 -0
  64. package/dist/creator2/creator2/auto.d.ts.map +1 -0
  65. package/dist/creator2/creator2/auto.js +65 -0
  66. package/dist/creator2/creator2/auto.js.map +1 -0
  67. package/dist/creator2/creator2/capture.d.ts +17 -0
  68. package/dist/creator2/creator2/capture.d.ts.map +1 -0
  69. package/dist/creator2/creator2/capture.js +175 -0
  70. package/dist/creator2/creator2/capture.js.map +1 -0
  71. package/dist/creator2/creator2/index.d.ts +12 -0
  72. package/dist/creator2/creator2/index.d.ts.map +1 -0
  73. package/dist/creator2/creator2/index.js +41 -0
  74. package/dist/creator2/creator2/index.js.map +1 -0
  75. package/dist/creator2/creator2/transport.d.ts +7 -0
  76. package/dist/creator2/creator2/transport.d.ts.map +1 -0
  77. package/dist/creator2/creator2/transport.js +34 -0
  78. package/dist/creator2/creator2/transport.js.map +1 -0
  79. package/dist/creator3/core/auto-tracking.d.ts +35 -0
  80. package/dist/creator3/core/auto-tracking.d.ts.map +1 -0
  81. package/dist/creator3/core/auto-tracking.js +247 -0
  82. package/dist/creator3/core/auto-tracking.js.map +1 -0
  83. package/dist/creator3/core/client.d.ts +74 -0
  84. package/dist/creator3/core/client.d.ts.map +1 -0
  85. package/dist/creator3/core/client.js +158 -0
  86. package/dist/creator3/core/client.js.map +1 -0
  87. package/dist/creator3/core/index.d.ts +3 -0
  88. package/dist/creator3/core/index.d.ts.map +1 -0
  89. package/dist/creator3/core/index.js +2 -0
  90. package/dist/creator3/core/index.js.map +1 -0
  91. package/dist/creator3/core/modules.d.ts +159 -0
  92. package/dist/creator3/core/modules.d.ts.map +1 -0
  93. package/dist/creator3/core/modules.js +293 -0
  94. package/dist/creator3/core/modules.js.map +1 -0
  95. package/dist/creator3/core/replay-budget.d.ts +28 -0
  96. package/dist/creator3/core/replay-budget.d.ts.map +1 -0
  97. package/dist/creator3/core/replay-budget.js +105 -0
  98. package/dist/creator3/core/replay-budget.js.map +1 -0
  99. package/dist/creator3/core/replay-diagnostics.d.ts +36 -0
  100. package/dist/creator3/core/replay-diagnostics.d.ts.map +1 -0
  101. package/dist/creator3/core/replay-diagnostics.js +36 -0
  102. package/dist/creator3/core/replay-diagnostics.js.map +1 -0
  103. package/dist/creator3/core/replay-encoding.d.ts +31 -0
  104. package/dist/creator3/core/replay-encoding.d.ts.map +1 -0
  105. package/dist/creator3/core/replay-encoding.js +62 -0
  106. package/dist/creator3/core/replay-encoding.js.map +1 -0
  107. package/dist/creator3/core/replay-pixels.d.ts +3 -0
  108. package/dist/creator3/core/replay-pixels.d.ts.map +1 -0
  109. package/dist/creator3/core/replay-pixels.js +13 -0
  110. package/dist/creator3/core/replay-pixels.js.map +1 -0
  111. package/dist/creator3/core/replay-privacy.d.ts +15 -0
  112. package/dist/creator3/core/replay-privacy.d.ts.map +1 -0
  113. package/dist/creator3/core/replay-privacy.js +33 -0
  114. package/dist/creator3/core/replay-privacy.js.map +1 -0
  115. package/dist/creator3/core/replay.d.ts +97 -0
  116. package/dist/creator3/core/replay.d.ts.map +1 -0
  117. package/dist/creator3/core/replay.js +520 -0
  118. package/dist/creator3/core/replay.js.map +1 -0
  119. package/dist/creator3/core/transport.d.ts +16 -0
  120. package/dist/creator3/core/transport.d.ts.map +1 -0
  121. package/dist/creator3/core/transport.js +27 -0
  122. package/dist/creator3/core/transport.js.map +1 -0
  123. package/dist/creator3/core/types.d.ts +260 -0
  124. package/dist/creator3/core/types.d.ts.map +1 -0
  125. package/dist/creator3/core/types.js +2 -0
  126. package/dist/creator3/core/types.js.map +1 -0
  127. package/dist/creator3/core/validation.d.ts +14 -0
  128. package/dist/creator3/core/validation.d.ts.map +1 -0
  129. package/dist/creator3/core/validation.js +75 -0
  130. package/dist/creator3/core/validation.js.map +1 -0
  131. package/dist/creator3/core/version.d.ts +9 -0
  132. package/dist/creator3/core/version.d.ts.map +1 -0
  133. package/dist/creator3/core/version.js +11 -0
  134. package/dist/creator3/core/version.js.map +1 -0
  135. package/dist/creator3/creator3/auto.d.ts +9 -0
  136. package/dist/creator3/creator3/auto.d.ts.map +1 -0
  137. package/dist/creator3/creator3/auto.js +73 -0
  138. package/dist/creator3/creator3/auto.js.map +1 -0
  139. package/dist/creator3/creator3/capture.d.ts +17 -0
  140. package/dist/creator3/creator3/capture.d.ts.map +1 -0
  141. package/dist/creator3/creator3/capture.js +119 -0
  142. package/dist/creator3/creator3/capture.js.map +1 -0
  143. package/dist/creator3/creator3/index.d.ts +12 -0
  144. package/dist/creator3/creator3/index.d.ts.map +1 -0
  145. package/dist/creator3/creator3/index.js +23 -0
  146. package/dist/creator3/creator3/index.js.map +1 -0
  147. package/dist/creator3/creator3/replay-render-cycle.d.ts +9 -0
  148. package/dist/creator3/creator3/replay-render-cycle.d.ts.map +1 -0
  149. package/dist/creator3/creator3/replay-render-cycle.js +12 -0
  150. package/dist/creator3/creator3/replay-render-cycle.js.map +1 -0
  151. package/dist/creator3/creator3/transport.d.ts +8 -0
  152. package/dist/creator3/creator3/transport.d.ts.map +1 -0
  153. package/dist/creator3/creator3/transport.js +24 -0
  154. package/dist/creator3/creator3/transport.js.map +1 -0
  155. package/dist/creator3/package.json +1 -0
  156. package/extensions/creator2/install-native.cjs +333 -0
  157. package/extensions/creator2/main.js +23 -0
  158. package/extensions/creator2/package.json +6 -0
  159. package/extensions/creator3/dist/builder.js +9 -0
  160. package/extensions/creator3/dist/hooks.js +70 -0
  161. package/extensions/creator3/dist/main.js +4 -0
  162. package/extensions/creator3/install-native.cjs +333 -0
  163. package/extensions/creator3/package.json +10 -0
  164. package/native/android/src/main/java/com/ft/sdk/cocos/FTCocosBridge.java +532 -0
  165. package/native/android/src/main/java/com/ft/sdk/cocos/FTCocosBridgeProvider.java +70 -0
  166. package/native/ios/FTCocosBridge.h +10 -0
  167. package/native/ios/FTCocosBridge.m +470 -0
  168. package/native/ios/FTCocosBridge.podspec +18 -0
  169. package/package.json +60 -0
@@ -0,0 +1,470 @@
1
+ #import "FTCocosBridge.h"
2
+
3
+ #import "FTMobileSDK.h"
4
+ #import "FTSessionReplay.h"
5
+ #import <UIKit/UIKit.h>
6
+ #import <objc/message.h>
7
+
8
+ @implementation FTCocosBridge
9
+
10
+ + (NSString *)invoke:(NSString *)method payload:(NSString *)payload {
11
+ @try {
12
+ NSData *data = [payload dataUsingEncoding:NSUTF8StringEncoding];
13
+ NSDictionary *arguments = data.length > 0
14
+ ? [NSJSONSerialization JSONObjectWithData:data options:0 error:nil]
15
+ : @{};
16
+ if (![arguments isKindOfClass:NSDictionary.class]) {
17
+ return [self responseWithValue:nil error:@"Payload must be a JSON object"];
18
+ }
19
+ id value = [self dispatch:method arguments:arguments];
20
+ return [self responseWithValue:value error:nil];
21
+ } @catch (NSException *exception) {
22
+ return [self responseWithValue:nil error:[NSString stringWithFormat:@"%@: %@", exception.name, exception.reason ?: @"unknown error"]];
23
+ }
24
+ }
25
+
26
+ + (id)dispatch:(NSString *)method arguments:(NSDictionary *)arguments {
27
+ FTExternalDataManager *rum = [FTExternalDataManager sharedManager];
28
+ if ([method isEqualToString:@"hybrid.attach"]) {
29
+ [self ensureNativeHostInitialized];
30
+ NSString *sdkVersion = [self text:arguments[@"sdkVersion"]];
31
+ if (sdkVersion.length > 0) {
32
+ [FTMobileAgent appendGlobalContext:@{ @"sdk_package_cocos": sdkVersion }];
33
+ }
34
+ if ([arguments[@"requiresReplay"] boolValue]) [self ensureHybridRecorderSwitch];
35
+ return nil;
36
+ }
37
+ if ([method isEqualToString:@"hybrid.setExternalRecorderActive"]) {
38
+ [self ensureNativeHostInitialized];
39
+ [self setExternalRecorderActive:[arguments[@"active"] boolValue]];
40
+ return nil;
41
+ }
42
+ if ([method isEqualToString:@"sdk.configure"]) {
43
+ NSString *datakit = [self text:arguments[@"datakitUrl"]];
44
+ NSString *dataway = [self text:arguments[@"datawayUrl"]];
45
+ NSString *token = [self text:arguments[@"clientToken"]];
46
+ FTSDKConfig *config;
47
+ if (datakit.length > 0) {
48
+ config = [[FTSDKConfig alloc] initWithDatakitUrl:datakit];
49
+ } else if (dataway.length > 0 && token.length > 0) {
50
+ config = [[FTSDKConfig alloc] initWithDatawayUrl:dataway clientToken:token];
51
+ } else {
52
+ [self fail:@"datakitUrl or datawayUrl/clientToken is required"];
53
+ }
54
+ if (arguments[@"debug"]) config.enableSDKDebugLog = [arguments[@"debug"] boolValue];
55
+ if ([self text:arguments[@"env"]].length > 0) config.env = arguments[@"env"];
56
+ if ([self text:arguments[@"serviceName"]].length > 0) config.service = arguments[@"serviceName"];
57
+ if ([arguments[@"globalContext"] isKindOfClass:NSDictionary.class]) config.globalContext = arguments[@"globalContext"];
58
+ [FTMobileAgent startWithConfigOptions:config];
59
+ return nil;
60
+ }
61
+ if ([method isEqualToString:@"sdk.bindUser"]) {
62
+ [[FTMobileAgent sharedInstance]
63
+ bindUserWithUserID:[self requiredText:arguments key:@"userId"]
64
+ userName:[self text:arguments[@"userName"]]
65
+ userEmail:[self text:arguments[@"userEmail"]]
66
+ extra:[self dictionary:arguments[@"extra"]]];
67
+ return nil;
68
+ }
69
+ if ([method isEqualToString:@"sdk.unbindUser"]) {
70
+ [[FTMobileAgent sharedInstance] unbindUser];
71
+ return nil;
72
+ }
73
+ if ([method isEqualToString:@"sdk.shutdown"]) {
74
+ [FTMobileAgent shutDown];
75
+ return nil;
76
+ }
77
+ if ([method isEqualToString:@"rum.configure"]) {
78
+ NSString *appID = [self text:arguments[@"iosAppId"]];
79
+ if (appID.length == 0) [self fail:@"rum.iosAppId is required on iOS"];
80
+ FTRumConfig *config = [[FTRumConfig alloc] initWithAppid:appID];
81
+ if (arguments[@"sampleRate"]) config.sampleRate = [self percent:arguments[@"sampleRate"]];
82
+ if (arguments[@"sessionOnErrorSampleRate"]) config.sessionOnErrorSampleRate = [self percent:arguments[@"sessionOnErrorSampleRate"]];
83
+ if (arguments[@"enableNativeUserAction"]) config.enableTraceUserAction = [arguments[@"enableNativeUserAction"] boolValue];
84
+ if (arguments[@"enableNativeUserView"]) config.enableTraceUserView = [arguments[@"enableNativeUserView"] boolValue];
85
+ if (arguments[@"enableNativeUserResource"]) config.enableTraceUserResource = [arguments[@"enableNativeUserResource"] boolValue];
86
+ if (arguments[@"enableNativeCrash"]) config.enableTrackAppCrash = [arguments[@"enableNativeCrash"] boolValue];
87
+ if (arguments[@"enableNativeAnr"]) config.enableTrackAppANR = [arguments[@"enableNativeAnr"] boolValue];
88
+ if (arguments[@"enableNativeUiBlock"]) {
89
+ [config setEnableTrackAppFreeze:[arguments[@"enableNativeUiBlock"] boolValue]
90
+ freezeDurationMs:[arguments[@"nativeUiBlockDurationMs"] longValue] ?: 250];
91
+ }
92
+ if (arguments[@"errorMonitorType"]) config.errorMonitorType = [arguments[@"errorMonitorType"] unsignedIntegerValue];
93
+ if (arguments[@"deviceMetricsMonitorType"]) config.deviceMetricsMonitorType = [arguments[@"deviceMetricsMonitorType"] unsignedIntegerValue];
94
+ config.monitorFrequency = [self monitorFrequency:[self text:arguments[@"detectFrequency"]]];
95
+ if ([arguments[@"globalContext"] isKindOfClass:NSDictionary.class]) config.globalContext = arguments[@"globalContext"];
96
+ [[FTMobileAgent sharedInstance] startRumWithConfigOptions:config];
97
+ return nil;
98
+ }
99
+ if ([method isEqualToString:@"rum.startView"]) {
100
+ [rum startViewWithName:[self requiredText:arguments key:@"name"] property:[self dictionary:arguments[@"attributes"]]];
101
+ return nil;
102
+ }
103
+ if ([method isEqualToString:@"rum.stopView"]) {
104
+ [rum stopViewWithProperty:[self dictionary:arguments[@"attributes"]]];
105
+ return nil;
106
+ }
107
+ if ([method isEqualToString:@"rum.addAction"]) {
108
+ [rum addAction:[self requiredText:arguments key:@"name"]
109
+ actionType:[self text:arguments[@"type"]] ?: @"click"
110
+ property:[self dictionary:arguments[@"attributes"]]];
111
+ return nil;
112
+ }
113
+ if ([method isEqualToString:@"rum.startAction"]) {
114
+ [rum startAction:[self requiredText:arguments key:@"name"]
115
+ actionType:[self text:arguments[@"type"]] ?: @"click"
116
+ property:[self dictionary:arguments[@"attributes"]]];
117
+ return nil;
118
+ }
119
+ if ([method isEqualToString:@"rum.addError"]) {
120
+ [rum addErrorWithType:[self text:arguments[@"type"]] ?: @"cocos_error"
121
+ message:[self text:arguments[@"message"]] ?: @""
122
+ stack:[self text:arguments[@"stack"]] ?: @""
123
+ property:[self dictionary:arguments[@"attributes"]]];
124
+ return nil;
125
+ }
126
+ if ([method isEqualToString:@"rum.addLongTask"]) {
127
+ [rum addLongTaskWithStack:[self text:arguments[@"stack"]] ?: @""
128
+ duration:arguments[@"durationNs"] ?: @0
129
+ property:[self dictionary:arguments[@"attributes"]]];
130
+ return nil;
131
+ }
132
+ if ([method isEqualToString:@"rum.startResource"]) {
133
+ [rum startResourceWithKey:[self requiredText:arguments key:@"key"] property:[self dictionary:arguments[@"attributes"]]];
134
+ return nil;
135
+ }
136
+ if ([method isEqualToString:@"rum.stopResource"]) {
137
+ [rum stopResourceWithKey:[self requiredText:arguments key:@"key"] property:[self dictionary:arguments[@"attributes"]]];
138
+ return nil;
139
+ }
140
+ if ([method isEqualToString:@"rum.addResource"]) {
141
+ [self addResource:arguments manager:rum];
142
+ return nil;
143
+ }
144
+ if ([method isEqualToString:@"logger.configure"]) {
145
+ FTLoggerConfig *config = [[FTLoggerConfig alloc] init];
146
+ if (arguments[@"sampleRate"]) config.sampleRate = [self percent:arguments[@"sampleRate"]];
147
+ if (arguments[@"enableLinkRumData"]) config.enableLinkRumData = [arguments[@"enableLinkRumData"] boolValue];
148
+ if (arguments[@"enableCustomLog"]) config.enableCustomLog = [arguments[@"enableCustomLog"] boolValue];
149
+ if (arguments[@"printCustomLogToConsole"]) config.printCustomLogToConsole = [arguments[@"printCustomLogToConsole"] boolValue];
150
+ if (arguments[@"logCacheLimitCount"]) config.logCacheLimitCount = [arguments[@"logCacheLimitCount"] intValue];
151
+ config.discardType = [[self text:arguments[@"discardStrategy"]] isEqualToString:@"discardOldest"] ? FTDiscardOldest : FTDiscard;
152
+ if ([arguments[@"logLevelFilters"] isKindOfClass:NSArray.class]) config.logLevelFilter = [self logLevelFilters:arguments[@"logLevelFilters"]];
153
+ if ([arguments[@"globalContext"] isKindOfClass:NSDictionary.class]) config.globalContext = arguments[@"globalContext"];
154
+ [[FTMobileAgent sharedInstance] startLoggerWithConfigOptions:config];
155
+ return nil;
156
+ }
157
+ if ([method isEqualToString:@"logger.log"]) {
158
+ [[FTMobileAgent sharedInstance] logging:[self text:arguments[@"content"]] ?: @""
159
+ status:[self logStatus:[self text:arguments[@"level"]]]
160
+ property:[self dictionary:arguments[@"attributes"]]];
161
+ return nil;
162
+ }
163
+ if ([method isEqualToString:@"trace.configure"]) {
164
+ FTTraceConfig *config = [[FTTraceConfig alloc] init];
165
+ if (arguments[@"sampleRate"]) config.sampleRate = [self percent:arguments[@"sampleRate"]];
166
+ if (arguments[@"enableLinkRumData"]) config.enableLinkRumData = [arguments[@"enableLinkRumData"] boolValue];
167
+ if (arguments[@"enableNativeAutoTrace"]) config.enableAutoTrace = [arguments[@"enableNativeAutoTrace"] boolValue];
168
+ config.networkTraceType = [self traceType:[self text:arguments[@"traceType"]]];
169
+ [[FTMobileAgent sharedInstance] startTraceWithConfigOptions:config];
170
+ return nil;
171
+ }
172
+ if ([method isEqualToString:@"trace.getHeaders"]) {
173
+ NSURL *url = [NSURL URLWithString:[self requiredText:arguments key:@"url"]];
174
+ if (!url) [self fail:@"Invalid trace URL"];
175
+ NSString *key = [self text:arguments[@"resourceKey"]];
176
+ return key.length > 0 ? [rum getTraceHeaderWithKey:key url:url] : [rum getTraceHeaderWithUrl:url];
177
+ }
178
+ if ([method isEqualToString:@"replay.configure"]) {
179
+ FTSessionReplayConfig *config = [[FTSessionReplayConfig alloc] init];
180
+ [self ensureExternalReplayAPIForConfig:config];
181
+ if (arguments[@"sampleRate"]) config.sampleRate = [self percent:arguments[@"sampleRate"]];
182
+ if (arguments[@"sessionOnErrorSampleRate"]) config.sessionReplayOnErrorSampleRate = [self percent:arguments[@"sessionOnErrorSampleRate"]];
183
+ if ([[self text:arguments[@"touchPrivacy"]] isEqualToString:@"show"]) config.touchPrivacy = FTTouchPrivacyLevelShow;
184
+ SEL externalMode = NSSelectorFromString(@"setExternalRecorderMode:");
185
+ ((void (*)(id, SEL, BOOL))objc_msgSend)(config, externalMode, YES);
186
+ [[FTRumSessionReplay sharedInstance] startWithSessionReplayConfig:config];
187
+ return nil;
188
+ }
189
+ if ([method isEqualToString:@"replay.getContext"]) {
190
+ return [self invokeReplayObjectSelector:NSSelectorFromString(@"currentExternalRUMContext")];
191
+ }
192
+ if ([method isEqualToString:@"replay.saveImage"]) {
193
+ NSData *rgba = [NSData dataWithContentsOfFile:[self requiredText:arguments key:@"path"]];
194
+ if (!rgba) [self fail:@"Unable to read replay RGBA file"];
195
+ NSData *png = [self pngDataFromRGBA:rgba
196
+ width:[arguments[@"width"] unsignedIntegerValue]
197
+ height:[arguments[@"height"] unsignedIntegerValue]];
198
+ if (!png) [self fail:@"Unable to encode replay image"];
199
+ return [self invokeReplayObjectSelector:NSSelectorFromString(@"saveExternalImageResourceData:mimeType:")
200
+ first:png
201
+ second:@"image/png"];
202
+ }
203
+ if ([method isEqualToString:@"replay.saveImageV2"]) {
204
+ NSUInteger width = [arguments[@"width"] unsignedIntegerValue];
205
+ NSUInteger height = [arguments[@"height"] unsignedIntegerValue];
206
+ NSData *rgba = [NSData dataWithContentsOfFile:[self requiredText:arguments key:@"path"]];
207
+ if (!rgba) [self fail:@"Unable to read replay RGBA file"];
208
+ UIImage *image = [self imageFromRGBA:rgba width:width height:height];
209
+ if (!image) [self fail:@"Unable to decode replay RGBA image"];
210
+
211
+ CGFloat quality = MIN(1.0, MAX(0.1, [arguments[@"quality"] doubleValue] ?: 0.45));
212
+ NSUInteger maxFrameBytes = [arguments[@"maxFrameBytes"] unsignedIntegerValue] ?: 40 * 1024;
213
+ UIImage *encodedImage = [self opaqueImageFromImage:image size:CGSizeMake(width, height)];
214
+ NSData *jpeg = UIImageJPEGRepresentation(encodedImage, quality);
215
+ if (jpeg.length > maxFrameBytes) {
216
+ quality = MAX(0.2, quality * 0.7);
217
+ jpeg = UIImageJPEGRepresentation(encodedImage, quality);
218
+ }
219
+ if (jpeg.length > maxFrameBytes && width > 1 && height > 1) {
220
+ width = MAX(1, (NSUInteger)floor(width * 0.75));
221
+ height = MAX(1, (NSUInteger)floor(height * 0.75));
222
+ encodedImage = [self opaqueImageFromImage:image size:CGSizeMake(width, height)];
223
+ jpeg = UIImageJPEGRepresentation(encodedImage, quality);
224
+ }
225
+ if (!jpeg || jpeg.length > maxFrameBytes) return @{ @"accepted": @NO };
226
+
227
+ NSString *resourceID = [self invokeReplayObjectSelector:NSSelectorFromString(@"saveExternalImageResourceData:mimeType:")
228
+ first:jpeg
229
+ second:@"image/jpeg"];
230
+ if (resourceID.length == 0) [self fail:@"Unable to save replay image resource"];
231
+ return @{
232
+ @"accepted": @YES,
233
+ @"resourceId": resourceID,
234
+ @"byteSize": @(jpeg.length),
235
+ @"width": @(width),
236
+ @"height": @(height),
237
+ @"mimeType": @"image/jpeg",
238
+ };
239
+ }
240
+ if ([method isEqualToString:@"replay.writeSegment"]) {
241
+ [self invokeReplayVoidSelector:NSSelectorFromString(@"writeExternalSegment:viewID:")
242
+ first:[self requiredText:arguments key:@"segment"]
243
+ second:[self requiredText:arguments key:@"viewId"]];
244
+ return nil;
245
+ }
246
+ if ([method isEqualToString:@"replay.setRecordCount"]) {
247
+ [self invokeReplayCountSelector:NSSelectorFromString(@"setExternalRecordCountForViewID:count:")
248
+ viewID:[self requiredText:arguments key:@"viewId"]
249
+ count:[arguments[@"count"] unsignedIntegerValue]];
250
+ return nil;
251
+ }
252
+ if ([method isEqualToString:@"replay.stop"]) return nil;
253
+ [self fail:[NSString stringWithFormat:@"Unknown bridge method: %@", method]];
254
+ return nil;
255
+ }
256
+
257
+ + (void)ensureNativeHostInitialized {
258
+ @try {
259
+ if (![FTMobileAgent sharedInstance]) {
260
+ [self fail:@"The native host must initialize the native SDK before FTCocosSDK.attach()"];
261
+ }
262
+ } @catch (__unused NSException *exception) {
263
+ [self fail:@"The native host must initialize the native SDK before FTCocosSDK.attach()"];
264
+ }
265
+ }
266
+
267
+ + (void)ensureHybridRecorderSwitch {
268
+ SEL selector = NSSelectorFromString(@"setExternalRecorderActive:");
269
+ if (![[FTRumSessionReplay sharedInstance] respondsToSelector:selector]) {
270
+ [self fail:@"iOS Session Replay does not support Hybrid recorder switching; upgrade the Native SDK"];
271
+ }
272
+ }
273
+
274
+ + (void)setExternalRecorderActive:(BOOL)active {
275
+ [self ensureHybridRecorderSwitch];
276
+ SEL selector = NSSelectorFromString(@"setExternalRecorderActive:");
277
+ ((void (*)(id, SEL, BOOL))objc_msgSend)([FTRumSessionReplay sharedInstance], selector, active);
278
+ }
279
+
280
+ + (void)addResource:(NSDictionary *)arguments manager:(FTExternalDataManager *)manager {
281
+ NSDictionary *input = [self dictionary:arguments[@"content"]] ?: @{};
282
+ NSURL *url = [NSURL URLWithString:[self text:input[@"url"]] ?: @""];
283
+ if (!url) [self fail:@"Invalid resource URL"];
284
+ NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
285
+ request.HTTPMethod = [self text:input[@"httpMethod"]] ?: @"GET";
286
+ [request setAllHTTPHeaderFields:[self dictionary:input[@"requestHeaders"]]];
287
+ NSHTTPURLResponse *response = [[NSHTTPURLResponse alloc]
288
+ initWithURL:url
289
+ statusCode:[input[@"statusCode"] integerValue]
290
+ HTTPVersion:@"HTTP/1.1"
291
+ headerFields:[self dictionary:input[@"responseHeaders"]]];
292
+ NSData *body = [[self text:input[@"responseBody"]] dataUsingEncoding:NSUTF8StringEncoding];
293
+ FTResourceContentModel *content = [[FTResourceContentModel alloc] initWithRequest:request response:response data:body error:nil];
294
+
295
+ NSDictionary *timings = [self dictionary:arguments[@"metrics"]];
296
+ FTResourceMetricsModel *metrics = [[FTResourceMetricsModel alloc] init];
297
+ metrics.fetchStartNsTimeInterval = [timings[@"fetchStartTime"] longLongValue];
298
+ metrics.connectStartNsTimeInterval = [timings[@"tcpStartTime"] longLongValue];
299
+ metrics.connectEndNsTimeInterval = [timings[@"tcpEndTime"] longLongValue];
300
+ metrics.dnsStartNsTimeInterval = [timings[@"dnsStartTime"] longLongValue];
301
+ metrics.dnsEndNsTimeInterval = [timings[@"dnsEndTime"] longLongValue];
302
+ metrics.responseStartNsTimeInterval = [timings[@"responseStartTime"] longLongValue];
303
+ metrics.responseEndNsTimeInterval = [timings[@"responseEndTime"] longLongValue];
304
+ metrics.sslStartNsTimeInterval = [timings[@"sslStartTime"] longLongValue];
305
+ metrics.sslEndNsTimeInterval = [timings[@"sslEndTime"] longLongValue];
306
+ [manager addResourceWithKey:[self requiredText:arguments key:@"key"] metrics:metrics content:content];
307
+ }
308
+
309
+ + (id)invokeReplayObjectSelector:(SEL)selector {
310
+ id target = [FTRumSessionReplay sharedInstance];
311
+ if (![target respondsToSelector:selector]) [self missingReplayAPI:selector];
312
+ return ((id (*)(id, SEL))objc_msgSend)(target, selector);
313
+ }
314
+
315
+ + (id)invokeReplayObjectSelector:(SEL)selector object:(id)object {
316
+ id target = [FTRumSessionReplay sharedInstance];
317
+ if (![target respondsToSelector:selector]) [self missingReplayAPI:selector];
318
+ return ((id (*)(id, SEL, id))objc_msgSend)(target, selector, object);
319
+ }
320
+
321
+ + (id)invokeReplayObjectSelector:(SEL)selector first:(id)first second:(id)second {
322
+ id target = [FTRumSessionReplay sharedInstance];
323
+ if (![target respondsToSelector:selector]) [self missingReplayAPI:selector];
324
+ return ((id (*)(id, SEL, id, id))objc_msgSend)(target, selector, first, second);
325
+ }
326
+
327
+ + (void)invokeReplayVoidSelector:(SEL)selector first:(id)first second:(id)second {
328
+ id target = [FTRumSessionReplay sharedInstance];
329
+ if (![target respondsToSelector:selector]) [self missingReplayAPI:selector];
330
+ ((void (*)(id, SEL, id, id))objc_msgSend)(target, selector, first, second);
331
+ }
332
+
333
+ + (void)invokeReplayCountSelector:(SEL)selector viewID:(NSString *)viewID count:(NSUInteger)count {
334
+ id target = [FTRumSessionReplay sharedInstance];
335
+ if (![target respondsToSelector:selector]) [self missingReplayAPI:selector];
336
+ ((void (*)(id, SEL, id, NSUInteger))objc_msgSend)(target, selector, viewID, count);
337
+ }
338
+
339
+ + (void)missingReplayAPI:(SEL)selector {
340
+ [self fail:[NSString stringWithFormat:@"FTMobileSDK lacks external Session Replay API %@; use the Cocos-compatible iOS SDK release", NSStringFromSelector(selector)]];
341
+ }
342
+
343
+ + (void)ensureExternalReplayAPIForConfig:(FTSessionReplayConfig *)config {
344
+ SEL externalMode = NSSelectorFromString(@"setExternalRecorderMode:");
345
+ if (![config respondsToSelector:externalMode]) [self missingReplayAPI:externalMode];
346
+
347
+ id target = [FTRumSessionReplay sharedInstance];
348
+ NSArray<NSString *> *selectorNames = @[
349
+ @"currentExternalRUMContext",
350
+ @"saveExternalImageResourceData:mimeType:",
351
+ @"writeExternalSegment:viewID:",
352
+ @"setExternalRecordCountForViewID:count:",
353
+ ];
354
+ for (NSString *name in selectorNames) {
355
+ SEL selector = NSSelectorFromString(name);
356
+ if (![target respondsToSelector:selector]) [self missingReplayAPI:selector];
357
+ }
358
+ }
359
+
360
+ + (NSData *)pngDataFromRGBA:(NSData *)rgba width:(NSUInteger)width height:(NSUInteger)height {
361
+ UIImage *image = [self imageFromRGBA:rgba width:width height:height];
362
+ return image ? UIImagePNGRepresentation(image) : nil;
363
+ }
364
+
365
+ + (UIImage *)imageFromRGBA:(NSData *)rgba width:(NSUInteger)width height:(NSUInteger)height {
366
+ if (width == 0 || height == 0 || width > NSUIntegerMax / height / 4 || rgba.length < width * height * 4) {
367
+ return nil;
368
+ }
369
+ NSMutableData *pixels = [rgba mutableCopy];
370
+ CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
371
+ CGContextRef context = CGBitmapContextCreate(pixels.mutableBytes,
372
+ width,
373
+ height,
374
+ 8,
375
+ width * 4,
376
+ colorSpace,
377
+ (CGBitmapInfo)kCGBitmapByteOrderDefault |
378
+ (CGBitmapInfo)kCGImageAlphaPremultipliedLast);
379
+ CGColorSpaceRelease(colorSpace);
380
+ if (!context) return nil;
381
+ CGImageRef imageRef = CGBitmapContextCreateImage(context);
382
+ CGContextRelease(context);
383
+ if (!imageRef) return nil;
384
+ UIImage *image = [UIImage imageWithCGImage:imageRef];
385
+ CGImageRelease(imageRef);
386
+ return image;
387
+ }
388
+
389
+ + (UIImage *)opaqueImageFromImage:(UIImage *)image size:(CGSize)size {
390
+ UIGraphicsBeginImageContextWithOptions(size, YES, 1.0);
391
+ [[UIColor whiteColor] setFill];
392
+ UIRectFill(CGRectMake(0, 0, size.width, size.height));
393
+ [image drawInRect:CGRectMake(0, 0, size.width, size.height)];
394
+ UIImage *opaque = UIGraphicsGetImageFromCurrentImageContext();
395
+ UIGraphicsEndImageContext();
396
+ return opaque;
397
+ }
398
+
399
+ + (NSString *)responseWithValue:(id)value error:(NSString *)error {
400
+ NSMutableDictionary *response = [@{ @"ok": @(error == nil) } mutableCopy];
401
+ if (value) response[@"value"] = value;
402
+ if (error) response[@"error"] = error;
403
+ NSData *data = [NSJSONSerialization dataWithJSONObject:response options:0 error:nil];
404
+ return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] ?: @"{\"ok\":false}";
405
+ }
406
+
407
+ + (NSDictionary *)dictionary:(id)value {
408
+ return [value isKindOfClass:NSDictionary.class] ? value : nil;
409
+ }
410
+
411
+ + (NSString *)text:(id)value {
412
+ return [value isKindOfClass:NSString.class] && [value length] > 0 ? value : nil;
413
+ }
414
+
415
+ + (NSString *)requiredText:(NSDictionary *)arguments key:(NSString *)key {
416
+ NSString *value = [self text:arguments[key]];
417
+ if (value.length == 0) [self fail:[NSString stringWithFormat:@"%@ is required", key]];
418
+ return value;
419
+ }
420
+
421
+ + (int)percent:(NSNumber *)number {
422
+ return (int)lround(MIN(1.0, MAX(0.0, number.doubleValue)) * 100.0);
423
+ }
424
+
425
+ + (FTLogStatus)logStatus:(NSString *)value {
426
+ if ([value isEqualToString:@"warning"]) return FTStatusWarning;
427
+ if ([value isEqualToString:@"error"]) return FTStatusError;
428
+ if ([value isEqualToString:@"critical"]) return FTStatusCritical;
429
+ if ([value isEqualToString:@"ok"]) return FTStatusOk;
430
+ return FTStatusInfo;
431
+ }
432
+
433
+ + (NSArray *)logLevelFilters:(NSArray *)values {
434
+ NSMutableArray *result = [NSMutableArray arrayWithCapacity:values.count];
435
+ for (id value in values) {
436
+ if (![value isKindOfClass:NSString.class]) continue;
437
+ NSString *level = value;
438
+ if ([level isEqualToString:@"info"] ||
439
+ [level isEqualToString:@"warning"] ||
440
+ [level isEqualToString:@"error"] ||
441
+ [level isEqualToString:@"critical"] ||
442
+ [level isEqualToString:@"ok"]) {
443
+ [result addObject:@([self logStatus:level])];
444
+ } else {
445
+ [result addObject:level];
446
+ }
447
+ }
448
+ return result;
449
+ }
450
+
451
+ + (FTNetworkTraceType)traceType:(NSString *)value {
452
+ if ([value isEqualToString:@"zipkinMultiHeader"]) return FTNetworkTraceTypeZipkinMultiHeader;
453
+ if ([value isEqualToString:@"zipkinSingleHeader"]) return FTNetworkTraceTypeZipkinSingleHeader;
454
+ if ([value isEqualToString:@"traceparent"]) return FTNetworkTraceTypeTraceparent;
455
+ if ([value isEqualToString:@"skywalking"]) return FTNetworkTraceTypeSkywalking;
456
+ if ([value isEqualToString:@"jaeger"]) return FTNetworkTraceTypeJaeger;
457
+ return FTNetworkTraceTypeDDtrace;
458
+ }
459
+
460
+ + (FTMonitorFrequency)monitorFrequency:(NSString *)value {
461
+ if ([value isEqualToString:@"frequent"]) return FTMonitorFrequencyFrequent;
462
+ if ([value isEqualToString:@"rare"]) return FTMonitorFrequencyRare;
463
+ return FTMonitorFrequencyDefault;
464
+ }
465
+
466
+ + (void)fail:(NSString *)message {
467
+ @throw [NSException exceptionWithName:@"FTCocosBridgeError" reason:message userInfo:nil];
468
+ }
469
+
470
+ @end
@@ -0,0 +1,18 @@
1
+ Pod::Spec.new do |s|
2
+ s.name = 'FTCocosBridge'
3
+ s.version = '0.1.0-alpha.3'
4
+ s.summary = 'TrueWatch Mobile SDK native bridge for Cocos Creator.'
5
+ s.homepage = 'https://github.com/TrueWatchTech/datakit-cocos'
6
+ s.license = { :type => 'Apache-2.0' }
7
+ s.author = { 'TrueWatch' => 'support@truewatch.com' }
8
+ s.source = { :path => '.' }
9
+ s.platform = :ios, '12.0'
10
+ s.source_files = 'FTCocosBridge.{h,m}'
11
+ s.public_header_files = 'FTCocosBridge.h'
12
+ s.requires_arc = true
13
+ # Cocos resolves FTCocosBridge by class name at runtime, so no direct symbol
14
+ # reference would otherwise keep the Objective-C category/class in the app.
15
+ s.user_target_xcconfig = { 'OTHER_LDFLAGS' => '$(inherited) -ObjC' }
16
+ s.dependency 'TrueWatchSDK/Agent', '1.6.8-alpha.2'
17
+ s.dependency 'TrueWatchSDK/FTSessionReplay', '1.6.8-alpha.2'
18
+ end
package/package.json ADDED
@@ -0,0 +1,60 @@
1
+ {
2
+ "name": "@truewatchtech/cocos-sdk",
3
+ "version": "0.1.0-alpha.3",
4
+ "description": "Observability SDK integration for Cocos Creator 2.4 and 3.x",
5
+ "license": "Apache-2.0",
6
+ "author": "TrueWatch",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/TrueWatchTech/datakit-cocos.git",
10
+ "directory": "packages/cocos"
11
+ },
12
+ "homepage": "https://github.com/TrueWatchTech/datakit-cocos#readme",
13
+ "bugs": {
14
+ "url": "https://github.com/TrueWatchTech/datakit-cocos/issues"
15
+ },
16
+ "keywords": [
17
+ "truewatch",
18
+ "cocos-creator",
19
+ "rum",
20
+ "session-replay",
21
+ "observability"
22
+ ],
23
+ "exports": {
24
+ "./creator2": {
25
+ "types": "./dist/creator2/creator2/index.d.ts",
26
+ "require": "./dist/creator2/creator2/index.js",
27
+ "default": "./dist/creator2/creator2/index.js"
28
+ },
29
+ "./creator3": {
30
+ "types": "./dist/creator3/creator3/index.d.ts",
31
+ "import": "./dist/creator3/creator3/index.js",
32
+ "default": "./dist/creator3/creator3/index.js"
33
+ },
34
+ "./package.json": "./package.json"
35
+ },
36
+ "files": [
37
+ "creator2",
38
+ "creator3",
39
+ "dist",
40
+ "extensions",
41
+ "native",
42
+ "bin",
43
+ "README.md",
44
+ "LICENSE",
45
+ "THIRD_PARTY_NOTICES"
46
+ ],
47
+ "bin": {
48
+ "truewatch-cocos": "bin/cli.cjs"
49
+ },
50
+ "engines": {
51
+ "node": ">=14"
52
+ },
53
+ "cocosSdk": {
54
+ "creator2": "2.4.5-2.4.15",
55
+ "creator3": "3.6.3-3.8.x"
56
+ },
57
+ "publishConfig": {
58
+ "access": "public"
59
+ }
60
+ }