@leonxin/meetgames 0.1.8 → 0.1.12

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 (228) hide show
  1. package/.agents/skills/meet-sdk-regression/SKILL.md +93 -0
  2. package/.cursor/mcp.example.json +16 -0
  3. package/.cursor/mcp.json +11 -0
  4. package/.cursor/skills/meetgames-mcp/SKILL.md +48 -0
  5. package/.vite/vitest/results.json +1 -0
  6. package/README.md +36 -13
  7. package/{meetsdk-android.json → config/meetsdk-android.json} +2 -1
  8. package/config/meetsdk-ios.json +15 -0
  9. package/dist/android/adapter.d.ts.map +1 -1
  10. package/dist/android/adapter.js +2 -2
  11. package/dist/android/adapter.js.map +1 -1
  12. package/dist/android/detect.d.ts +2 -2
  13. package/dist/android/detect.d.ts.map +1 -1
  14. package/dist/android/detect.js +36 -8
  15. package/dist/android/detect.js.map +1 -1
  16. package/dist/cache.d.ts +44 -0
  17. package/dist/cache.d.ts.map +1 -0
  18. package/dist/cache.js +101 -0
  19. package/dist/cache.js.map +1 -0
  20. package/dist/cli.d.ts.map +1 -1
  21. package/dist/cli.js +181 -49
  22. package/dist/cli.js.map +1 -1
  23. package/dist/config/meetSdkDefaultConfig.d.ts +19 -2
  24. package/dist/config/meetSdkDefaultConfig.d.ts.map +1 -1
  25. package/dist/config/meetSdkDefaultConfig.js +69 -6
  26. package/dist/config/meetSdkDefaultConfig.js.map +1 -1
  27. package/dist/config/meetSdkIosConfig.d.ts +21 -0
  28. package/dist/config/meetSdkIosConfig.d.ts.map +1 -0
  29. package/dist/config/meetSdkIosConfig.js +68 -0
  30. package/dist/config/meetSdkIosConfig.js.map +1 -0
  31. package/dist/config/meetSdkRemoteConfig.d.ts +19 -1
  32. package/dist/config/meetSdkRemoteConfig.d.ts.map +1 -1
  33. package/dist/config/meetSdkRemoteConfig.js +64 -25
  34. package/dist/config/meetSdkRemoteConfig.js.map +1 -1
  35. package/dist/contracts/types.d.ts +27 -6
  36. package/dist/contracts/types.d.ts.map +1 -1
  37. package/dist/core/doctor.d.ts +17 -0
  38. package/dist/core/doctor.d.ts.map +1 -0
  39. package/dist/core/doctor.js +444 -0
  40. package/dist/core/doctor.js.map +1 -0
  41. package/dist/core/pipeline.d.ts.map +1 -1
  42. package/dist/core/pipeline.js +0 -15
  43. package/dist/core/pipeline.js.map +1 -1
  44. package/dist/core/platform.d.ts +12 -0
  45. package/dist/core/platform.d.ts.map +1 -0
  46. package/dist/core/platform.js +40 -0
  47. package/dist/core/platform.js.map +1 -0
  48. package/dist/core/previewPatches.d.ts +1 -1
  49. package/dist/core/previewPatches.js +2 -2
  50. package/dist/core/previewPatches.js.map +1 -1
  51. package/dist/core/reporter.js +1 -1
  52. package/dist/core/reporter.js.map +1 -1
  53. package/dist/core/workspace.d.ts +2 -2
  54. package/dist/core/workspace.d.ts.map +1 -1
  55. package/dist/core/workspace.js +6 -5
  56. package/dist/core/workspace.js.map +1 -1
  57. package/dist/index.d.ts +4 -1
  58. package/dist/index.d.ts.map +1 -1
  59. package/dist/index.js +4 -1
  60. package/dist/index.js.map +1 -1
  61. package/dist/ios/channelConfig.d.ts +1 -0
  62. package/dist/ios/channelConfig.d.ts.map +1 -1
  63. package/dist/ios/channelConfig.js +82 -0
  64. package/dist/ios/channelConfig.js.map +1 -1
  65. package/dist/ios/codeUtils.d.ts +1 -0
  66. package/dist/ios/codeUtils.d.ts.map +1 -1
  67. package/dist/ios/codeUtils.js +11 -2
  68. package/dist/ios/codeUtils.js.map +1 -1
  69. package/dist/ios/detect.d.ts +2 -2
  70. package/dist/ios/detect.d.ts.map +1 -1
  71. package/dist/ios/detect.js +49 -10
  72. package/dist/ios/detect.js.map +1 -1
  73. package/dist/ios/entitlements.d.ts +4 -0
  74. package/dist/ios/entitlements.d.ts.map +1 -0
  75. package/dist/ios/entitlements.js +53 -0
  76. package/dist/ios/entitlements.js.map +1 -0
  77. package/dist/ios/fileManager.d.ts.map +1 -1
  78. package/dist/ios/fileManager.js +3 -2
  79. package/dist/ios/fileManager.js.map +1 -1
  80. package/dist/ios/infoPlist.d.ts +1 -1
  81. package/dist/ios/infoPlist.d.ts.map +1 -1
  82. package/dist/ios/infoPlist.js.map +1 -1
  83. package/dist/ios/integrate.d.ts.map +1 -1
  84. package/dist/ios/integrate.js +214 -39
  85. package/dist/ios/integrate.js.map +1 -1
  86. package/dist/ios/pbxprojEditor.d.ts +2 -0
  87. package/dist/ios/pbxprojEditor.d.ts.map +1 -1
  88. package/dist/ios/pbxprojEditor.js +250 -6
  89. package/dist/ios/pbxprojEditor.js.map +1 -1
  90. package/dist/ios/pluginConfig.d.ts +1 -0
  91. package/dist/ios/pluginConfig.d.ts.map +1 -1
  92. package/dist/ios/pluginConfig.js +36 -4
  93. package/dist/ios/pluginConfig.js.map +1 -1
  94. package/dist/ios/sdkBundle.d.ts +1 -6
  95. package/dist/ios/sdkBundle.d.ts.map +1 -1
  96. package/dist/ios/sdkBundle.js +47 -17
  97. package/dist/ios/sdkBundle.js.map +1 -1
  98. package/dist/ios/template.d.ts +1 -0
  99. package/dist/ios/template.d.ts.map +1 -1
  100. package/dist/ios/template.js +14 -1
  101. package/dist/ios/template.js.map +1 -1
  102. package/dist/ios/types.d.ts +2 -2
  103. package/dist/ios/types.d.ts.map +1 -1
  104. package/dist/mcp/server.d.ts.map +1 -1
  105. package/dist/mcp/server.js +22 -15
  106. package/dist/mcp/server.js.map +1 -1
  107. package/dist/mcp/service.d.ts +11 -6
  108. package/dist/mcp/service.d.ts.map +1 -1
  109. package/dist/mcp/service.js +61 -18
  110. package/dist/mcp/service.js.map +1 -1
  111. package/dist/ops/handlers.d.ts.map +1 -1
  112. package/dist/ops/handlers.js +34 -23
  113. package/dist/ops/handlers.js.map +1 -1
  114. package/dist/remote/sdkHomeDownload.d.ts +65 -0
  115. package/dist/remote/sdkHomeDownload.d.ts.map +1 -0
  116. package/dist/remote/sdkHomeDownload.js +229 -0
  117. package/dist/remote/sdkHomeDownload.js.map +1 -0
  118. package/dist/remote/topsdkDownloadSdkConfig.d.ts.map +1 -1
  119. package/dist/remote/topsdkDownloadSdkConfig.js +11 -1
  120. package/dist/remote/topsdkDownloadSdkConfig.js.map +1 -1
  121. package/dist/shared/errors.d.ts +7 -0
  122. package/dist/shared/errors.d.ts.map +1 -0
  123. package/dist/shared/errors.js +16 -0
  124. package/dist/shared/errors.js.map +1 -0
  125. package/docs/API.md +246 -32
  126. package/docs/CLI.md +291 -0
  127. package/docs/INTEGRATION.md +116 -0
  128. package/docs/MCP.md +86 -0
  129. package/docs/README.md +18 -10
  130. package/docs/{api → archive/api}/downloadSDKConfig.md +9 -7
  131. package/docs/{api → archive/api}/getChannelConfig-meetgames.md +2 -2
  132. package/docs/archive/ios-migration.md +2139 -0
  133. package/docs/{ → archive/product/}/346/212/200/346/234/257/346/226/271/346/241/210/350/260/203/347/240/224.md +7 -7
  134. package/docs/{ → archive/product/}/351/234/200/346/261/202/346/226/207/346/241/243.md +16 -15
  135. package/package.json +7 -36
  136. package/recipes/android-default.yaml +0 -5
  137. package/recipes/integrate-default.yaml +0 -5
  138. package/src/android/adapter.ts +9 -0
  139. package/src/android/assembleIntegrationJson.ts +33 -0
  140. package/src/android/detect.ts +132 -0
  141. package/src/android/downloadGoogleServicesJson.ts +56 -0
  142. package/src/android/gradle.ts +116 -0
  143. package/src/android/manifest.ts +50 -0
  144. package/src/android/meetSdkRemoteGradle.ts +837 -0
  145. package/src/cache.ts +164 -0
  146. package/src/cli.ts +496 -0
  147. package/src/config/fetchConfigWrite.ts +30 -0
  148. package/src/config/loadAndroidIntegration.ts +41 -0
  149. package/src/config/loadManifest.ts +40 -0
  150. package/src/config/meetSdkDefaultConfig.ts +100 -0
  151. package/src/config/meetSdkIosConfig.ts +89 -0
  152. package/src/config/meetSdkRemoteConfig.ts +1215 -0
  153. package/src/config/topsdkFeatureModules.ts +92 -0
  154. package/src/contracts/types.ts +129 -0
  155. package/src/core/doctor.ts +485 -0
  156. package/src/core/patch.ts +64 -0
  157. package/src/core/pipeline.ts +107 -0
  158. package/src/core/platform.ts +47 -0
  159. package/src/core/previewPatches.ts +23 -0
  160. package/src/core/reporter.ts +24 -0
  161. package/src/core/workspace.ts +31 -0
  162. package/src/entry.ts +7 -0
  163. package/src/index.ts +140 -0
  164. package/src/ios/channelConfig.ts +128 -0
  165. package/src/ios/codeUtils.ts +160 -0
  166. package/src/ios/detect.ts +105 -0
  167. package/src/ios/entitlements.ts +61 -0
  168. package/src/ios/fileManager.ts +48 -0
  169. package/src/ios/infoPlist.ts +55 -0
  170. package/src/ios/integrate.ts +517 -0
  171. package/src/ios/pbxprojEditor.ts +450 -0
  172. package/src/ios/pluginConfig.ts +97 -0
  173. package/src/ios/reserved.ts +8 -0
  174. package/src/ios/sdkBundle.ts +59 -0
  175. package/src/ios/template.ts +36 -0
  176. package/src/ios/types.ts +65 -0
  177. package/src/mcp/server.ts +176 -0
  178. package/src/mcp/service.ts +253 -0
  179. package/src/mcp-entry.ts +7 -0
  180. package/src/ops/fileStore.ts +56 -0
  181. package/src/ops/handlers.ts +308 -0
  182. package/src/remote/fetchJson.ts +22 -0
  183. package/src/remote/sdkHomeDownload.ts +295 -0
  184. package/src/remote/topsdkDownloadSdkConfig.ts +93 -0
  185. package/src/remote/topsdkGetSdkConfig.ts +122 -0
  186. package/src/remote/topsdkSign.ts +10 -0
  187. package/src/shared/errors.ts +16 -0
  188. package/tests/assemble.test.ts +12 -0
  189. package/tests/doctor.test.ts +91 -0
  190. package/tests/downloadGoogleServicesJson.test.ts +47 -0
  191. package/tests/fetch-remote.test.ts +23 -0
  192. package/tests/fetchConfigOverrides.test.ts +28 -0
  193. package/tests/fetchConfigWrite.test.ts +54 -0
  194. package/tests/fixtures-hosts.test.ts +78 -0
  195. package/tests/gradle.test.ts +33 -0
  196. package/tests/integration-json.test.ts +29 -0
  197. package/tests/ios.codeUtils.test.ts +23 -0
  198. package/tests/ios.sdkBundle.test.ts +21 -0
  199. package/tests/loadManifest.test.ts +15 -0
  200. package/tests/manifest-xml.test.ts +30 -0
  201. package/tests/mcp.e2e.ts +214 -0
  202. package/tests/mcp.service.test.ts +53 -0
  203. package/tests/meetSdkRemoteConfig.test.ts +481 -0
  204. package/tests/meetSdkRemoteGradle.test.ts +414 -0
  205. package/tests/pipeline.android.test.ts +95 -0
  206. package/tests/pipeline.integration-json.test.ts +58 -0
  207. package/tests/pipeline.ios.test.ts +392 -0
  208. package/tests/pipeline.preview.patch.test.ts +85 -0
  209. package/tests/platformSelection.test.ts +77 -0
  210. package/tests/sdkHomeDownload.test.ts +124 -0
  211. package/tests/sdkVersionConfig.test.ts +131 -0
  212. package/tests/topsdk.test.ts +53 -0
  213. package/tests/topsdkDownloadSdkConfig.test.ts +81 -0
  214. package/tests/topsdkFeatureModules.test.ts +116 -0
  215. package/tsconfig.json +19 -0
  216. package/vitest.config.ts +9 -0
  217. package/vitest.mcp.config.ts +11 -0
  218. package/bundled/android/sample.txt +0 -1
  219. package/docs/ANDROID.md +0 -133
  220. package/docs/CURSOR-MCP-SETUP.md +0 -72
  221. package/docs/MCP-SKILL.md +0 -63
  222. package/fixtures/api-samples/getChannelConfig-meetgames.sample.json +0 -123
  223. package/fixtures/meetsdk-remote-config.download-shape.json +0 -20
  224. package/fixtures/meetsdk-remote-config.mock.json +0 -69
  225. package/fixtures/recipes/android-default.fixture.yaml +0 -15
  226. package/fixtures/recipes/android-integration.fixture.json +0 -29
  227. package/fixtures/topsdk-config-reference.json +0 -39
  228. /package/docs/{api → archive/api}/getSDKConfig.md +0 -0
@@ -0,0 +1,1215 @@
1
+ /**
2
+ * TOPSDK 能力:开关与参数均在 `sdkModules` 下。
3
+ * **integrate 是否接入某插件**:只看 JSON 是否写了对应子键(如 `kakao`、`appsflyer`、`facebookdata`);有键则 value 必为 `{…}` / `{}`,无配置则不写该键。
4
+ * **登录 Facebook**(`login.facebook`)与 **埋点 Facebook Data**(`analytics.facebookdata`)子键名可区分。
5
+ * 不再使用顶层 `facebook` / `google` / `appsflyer` / `parameterConfig`,也不再用扁平 `features` / `featuresByCategory`。
6
+ *
7
+ * `console/openSDK/getSDKConfig` 服务端:gp-sdk `SDKDownloadController`(`VOSDKConfig` 仅含 `channelAuthConfig`);
8
+ * `VOChannelAuthConfig` 使用 `clientId` / `scheme` / `secret` 等。`openSDK/downloadSDKConfig`(gp-sdk)从 DB 组装 `meetsdk-remote-config.json`;
9
+ * `sdk-home` 负责 SDK 最新版本发现和 iOS SDK 包下载。
10
+ */
11
+ import { collectTopSdkFeatureRepositories } from "./topsdkFeatureModules.js";
12
+
13
+ /** integrate / Gradle 依赖:写了子键且 value 为对象(gp-sdk 有键则必为 `{…}` / `{}`)。 */
14
+ export function hasSdkModuleKey(scope: Record<string, unknown>, subKey: string): boolean {
15
+ if (!Object.prototype.hasOwnProperty.call(scope, subKey)) return false;
16
+ const v = scope[subKey];
17
+ return typeof v === "object" && v !== null && !Array.isArray(v);
18
+ }
19
+
20
+ /** 子模块带凭证对象(用于 default 合并、校验等);`false` / 缺失视为无凭证。 */
21
+ export function isSdkModuleToggleOn(value: unknown): boolean {
22
+ if (value === true) return true;
23
+ if (typeof value === "object" && value !== null && !Array.isArray(value)) return true;
24
+ return false;
25
+ }
26
+
27
+ /** `login.facebook`:为 `false` / 未写表示关闭;对象表示开启并写入 Gradle resValue。 */
28
+ export interface MeetSdkLoginFacebookCredentials {
29
+ facebookAppId: string;
30
+ fbLoginProtocolScheme: string;
31
+ facebookClientToken: string;
32
+ name?: string;
33
+ openMessenger?: string;
34
+ dependencies?: MeetSdkGradleDependency[];
35
+ }
36
+
37
+ export type MeetSdkLoginFacebookModule = false | MeetSdkLoginFacebookCredentials;
38
+
39
+ /** `login.google` */
40
+ export interface MeetSdkLoginGoogleCredentials {
41
+ googleClientId: string;
42
+ scheme?: string;
43
+ reversedClientId?: string;
44
+ dependencies?: MeetSdkGradleDependency[];
45
+ }
46
+
47
+ export type MeetSdkLoginGoogleModule = false | MeetSdkLoginGoogleCredentials;
48
+
49
+ /** `login.twitter`(与控制台 `authType: TWITTER` 字段对齐:`clientId` / `secret` / `redirect`) */
50
+ export interface MeetSdkLoginTwitterCredentials {
51
+ clientId: string;
52
+ secret: string;
53
+ redirect: string;
54
+ dependencies?: MeetSdkGradleDependency[];
55
+ }
56
+ export type MeetSdkLoginTwitterModule = false | MeetSdkLoginTwitterCredentials;
57
+
58
+ /** `login.snapchat` */
59
+ export interface MeetSdkLoginSnapchatCredentials {
60
+ clientId: string;
61
+ redirect: string;
62
+ scheme?: string;
63
+ /** 控制台可能无此项 */
64
+ secret?: string;
65
+ dependencies?: MeetSdkGradleDependency[];
66
+ }
67
+ export type MeetSdkLoginSnapchatModule = false | MeetSdkLoginSnapchatCredentials;
68
+
69
+ /** `login.line` */
70
+ export interface MeetSdkLoginLineCredentials {
71
+ clientId: string;
72
+ scheme?: string;
73
+ redirect?: string;
74
+ dependencies?: MeetSdkGradleDependency[];
75
+ }
76
+ export type MeetSdkLoginLineModule = false | MeetSdkLoginLineCredentials;
77
+
78
+ /** `login.naver` */
79
+ export interface MeetSdkLoginNaverCredentials {
80
+ clientId: string;
81
+ secret: string;
82
+ name: string;
83
+ scheme?: string;
84
+ redirect?: string;
85
+ dependencies?: MeetSdkGradleDependency[];
86
+ }
87
+ export type MeetSdkLoginNaverModule = false | MeetSdkLoginNaverCredentials;
88
+
89
+ /** `login.kakao`(`scheme` 对应 Kakao 自定义 URL scheme) */
90
+ export interface MeetSdkLoginKakaoCredentials {
91
+ clientId: string;
92
+ scheme: string;
93
+ repositories?: string[];
94
+ dependencies?: MeetSdkGradleDependency[];
95
+ }
96
+ export type MeetSdkLoginKakaoModule = false | MeetSdkLoginKakaoCredentials;
97
+
98
+ /** `login.tiktok` */
99
+ export interface MeetSdkLoginTiktokCredentials {
100
+ clientId: string;
101
+ secret: string;
102
+ redirect: string;
103
+ repositories?: string[];
104
+ dependencies?: MeetSdkGradleDependency[];
105
+ }
106
+ export type MeetSdkLoginTiktokModule = false | MeetSdkLoginTiktokCredentials;
107
+
108
+ /** `login.discord` */
109
+ export interface MeetSdkLoginDiscordCredentials {
110
+ clientId: string;
111
+ secret: string;
112
+ redirect: string;
113
+ dependencies?: MeetSdkGradleDependency[];
114
+ }
115
+ export type MeetSdkLoginDiscordModule = false | MeetSdkLoginDiscordCredentials;
116
+
117
+ export interface MeetSdkGradleDependency {
118
+ implementation: string;
119
+ }
120
+
121
+ export interface MeetSdkSimplePluginConfig {
122
+ repositories?: string[];
123
+ dependencies?: MeetSdkGradleDependency[];
124
+ }
125
+
126
+ export type MeetSdkSimpleModule = false | MeetSdkSimplePluginConfig;
127
+
128
+ /** `analytics.appsflyer` */
129
+ export interface MeetSdkAnalyticsAppsflyerCredentials {
130
+ devKey: string;
131
+ clientId?: string;
132
+ appleAppId?: string;
133
+ enableDebugLog: boolean;
134
+ repositories?: string[];
135
+ dependencies?: MeetSdkGradleDependency[];
136
+ }
137
+
138
+ export type MeetSdkAnalyticsAppsflyerModule = false | MeetSdkAnalyticsAppsflyerCredentials;
139
+
140
+ /** `analytics.firebase`:配置文件远程地址;落盘目录由 meet-sdk-tool 检测 app module,文件名为 `firebase_file_name`(DB `firebase_name`)。 */
141
+ export interface MeetSdkAnalyticsFirebaseCredentials {
142
+ firebase_file_url: string;
143
+ /** 落盘文件名(含后缀),来自 DB `firebase_name`;缺省为 `google-services.json` */
144
+ firebase_file_name?: string;
145
+ repositories?: string[];
146
+ classpath?: string;
147
+ applyplugin?: string;
148
+ dependencies?: MeetSdkGradleDependency[];
149
+ }
150
+
151
+ export type MeetSdkAnalyticsFirebaseModule = false | MeetSdkAnalyticsFirebaseCredentials;
152
+
153
+ /** `analytics.adjust` */
154
+ export interface MeetSdkAnalyticsAdjustCredentials {
155
+ appId: string;
156
+ clientId?: string;
157
+ eventUrl?: string;
158
+ enableSandbox: boolean;
159
+ dependencies?: MeetSdkGradleDependency[];
160
+ }
161
+
162
+ export type MeetSdkAnalyticsAdjustModule = false | MeetSdkAnalyticsAdjustCredentials;
163
+
164
+ /** `analytics.facebookdata` */
165
+ export interface MeetSdkAnalyticsFacebookDataCredentials {
166
+ facebookAppId?: string;
167
+ facebookClientToken?: string;
168
+ fbLoginProtocolScheme?: string;
169
+ name?: string;
170
+ dependencies?: MeetSdkGradleDependency[];
171
+ }
172
+
173
+ export type MeetSdkAnalyticsFacebookDataModule = false | MeetSdkAnalyticsFacebookDataCredentials;
174
+
175
+ export interface MeetSdkLoginModules {
176
+ guest: MeetSdkSimpleModule;
177
+ email: MeetSdkSimpleModule;
178
+ apple: MeetSdkSimpleModule;
179
+ facebook: MeetSdkLoginFacebookModule;
180
+ google: MeetSdkLoginGoogleModule;
181
+ twitter: MeetSdkLoginTwitterModule;
182
+ snapchat: MeetSdkLoginSnapchatModule;
183
+ line: MeetSdkLoginLineModule;
184
+ naver: MeetSdkLoginNaverModule;
185
+ kakao: MeetSdkLoginKakaoModule;
186
+ tiktok: MeetSdkLoginTiktokModule;
187
+ discord: MeetSdkLoginDiscordModule;
188
+ }
189
+
190
+ export interface MeetSdkPaymentModules {
191
+ googleIap: MeetSdkSimpleModule;
192
+ onestoreIap: MeetSdkSimpleModule;
193
+ huaweiIap: MeetSdkSimpleModule;
194
+ xiaomiIap: MeetSdkSimpleModule;
195
+ }
196
+
197
+ export interface MeetSdkAnalyticsModules {
198
+ appsflyer: MeetSdkAnalyticsAppsflyerModule;
199
+ facebookdata: MeetSdkAnalyticsFacebookDataModule;
200
+ firebase: MeetSdkAnalyticsFirebaseModule;
201
+ adjust: MeetSdkAnalyticsAdjustModule;
202
+ }
203
+
204
+ export interface MeetSdkModuleToggles {
205
+ login: MeetSdkLoginModules;
206
+ payment: MeetSdkPaymentModules;
207
+ analytics: MeetSdkAnalyticsModules;
208
+ }
209
+
210
+ export type MeetSdkFeatureScope = keyof MeetSdkModuleToggles;
211
+
212
+ /**
213
+ * TOPSDK 宿主与控制台侧标识(与 `sdkModules` 内的登录/埋点凭证分离)。
214
+ */
215
+ export interface MeetSdkRemoteTopsdkBlock {
216
+ appId: string;
217
+ /** 用于 `getSDKConfig` 签名等;落盘于本地缓存,请勿提交到公开仓库。 */
218
+ appSecret: string;
219
+ version: string;
220
+ groupId: string;
221
+ repositories: string[];
222
+ }
223
+
224
+ /**
225
+ * Normalized remote config (local file `meetsdk-remote-config.json`).
226
+ * 凭证均在 `sdkModules` 内;商店渠道在根级 `channel`,app 凭证在 `topsdk`。
227
+ */
228
+ export interface MeetSdkRemoteConfig {
229
+ /** Android `applicationId`(或与 manifest package 对齐的标识),供 resValue 等使用。 */
230
+ packageName: string;
231
+ /** 商店渠道类型,如 `GOOGLE` / `APPLE`(与 fetch-config 的 `--channel-type` 一致,大写落盘)。 */
232
+ channel: string;
233
+ /** 设备平台:`android` / `ios`(小写,来自 `channel.device_platform`)。 */
234
+ devicePlatform?: string;
235
+ topsdk: MeetSdkRemoteTopsdkBlock;
236
+ sdkModules: MeetSdkModuleToggles;
237
+ }
238
+
239
+ /** 读取根级渠道字段。 */
240
+ export function readMeetSdkRemoteChannel(config: MeetSdkRemoteConfig): string {
241
+ const root = config.channel?.trim();
242
+ return root || "";
243
+ }
244
+
245
+ export interface MeetSdkDefaultConfig {
246
+ topsdk: {
247
+ version?: string;
248
+ date?: string;
249
+ groupId?: string;
250
+ repositories: string[];
251
+ };
252
+ sdkModules: MeetSdkModuleToggles;
253
+ }
254
+
255
+ /** 缓存文件名;`fetch-config` 固定写入 `<project-root>/` + 此文件名(不可通过 --out 修改)。 */
256
+ export const MEETSDK_REMOTE_CONFIG_FILENAME = "meetsdk-remote-config.json";
257
+
258
+ export function defaultSdkModules(): MeetSdkModuleToggles {
259
+ return {
260
+ login: {
261
+ guest: false,
262
+ email: false,
263
+ apple: false,
264
+ facebook: false,
265
+ google: false,
266
+ twitter: false,
267
+ snapchat: false,
268
+ line: false,
269
+ naver: false,
270
+ kakao: false,
271
+ tiktok: false,
272
+ discord: false,
273
+ },
274
+ payment: { googleIap: false, onestoreIap: false, huaweiIap: false, xiaomiIap: false },
275
+ analytics: { appsflyer: false, facebookdata: false, firebase: false, adjust: false },
276
+ };
277
+ }
278
+
279
+ /** 解析 downloadSDKConfig / 本地 JSON:仅保留 JSON 里出现的子键(不写则视为未配置)。 */
280
+ export function emptySdkModules(): MeetSdkModuleToggles {
281
+ return {
282
+ login: {} as MeetSdkLoginModules,
283
+ payment: {} as MeetSdkPaymentModules,
284
+ analytics: {} as MeetSdkAnalyticsModules,
285
+ };
286
+ }
287
+
288
+ function isRecord(v: unknown): v is Record<string, unknown> {
289
+ return typeof v === "object" && v !== null && !Array.isArray(v);
290
+ }
291
+
292
+ function str(v: unknown): string {
293
+ if (v === undefined || v === null) return "";
294
+ return String(v);
295
+ }
296
+
297
+ function optionalStr(v: unknown): string | undefined {
298
+ if (v === undefined || v === null) return undefined;
299
+ const s = String(v).trim();
300
+ return s.length ? s : undefined;
301
+ }
302
+
303
+ function bool(v: unknown): boolean {
304
+ if (typeof v === "boolean") return v;
305
+ if (typeof v === "string") return v === "true" || v === "1";
306
+ return Boolean(v);
307
+ }
308
+
309
+ function dependency(artifact: string): MeetSdkGradleDependency[] {
310
+ return [{ implementation: `$groupId:${artifact}:$topsdk_version` }];
311
+ }
312
+
313
+ export const DEFAULT_TOPSDK_PLUGIN_DEPENDENCIES = {
314
+ login: {
315
+ guest: dependency("guest"),
316
+ email: dependency("email"),
317
+ apple: dependency("apple"),
318
+ facebook: dependency("facebook"),
319
+ google: dependency("google"),
320
+ twitter: dependency("twitter"),
321
+ snapchat: dependency("snapchat"),
322
+ line: dependency("line"),
323
+ naver: dependency("naver"),
324
+ kakao: dependency("kakao"),
325
+ tiktok: dependency("tiktok"),
326
+ discord: dependency("discord"),
327
+ },
328
+ analytics: {
329
+ appsflyer: dependency("appsflyer"),
330
+ firebase: dependency("firebase"),
331
+ adjust: dependency("adjust"),
332
+ facebookdata: dependency("facebook-data"),
333
+ },
334
+ payment: {
335
+ googleIap: dependency("google-iap"),
336
+ onestoreIap: dependency("onestore-iap"),
337
+ huaweiIap: dependency("huawei-iap"),
338
+ xiaomiIap: dependency("xiaomi-iap"),
339
+ },
340
+ } as const;
341
+
342
+ function parseDependencies(v: unknown, fallback: readonly MeetSdkGradleDependency[] = []): MeetSdkGradleDependency[] | undefined {
343
+ if (!Array.isArray(v)) return fallback.length > 0 ? [...fallback] : undefined;
344
+ const out: MeetSdkGradleDependency[] = [];
345
+ for (const raw of v) {
346
+ if (typeof raw === "string") {
347
+ out.push({ implementation: raw });
348
+ } else if (isRecord(raw)) {
349
+ out.push({ implementation: str(raw.implementation ?? raw.value ?? raw.artifact) });
350
+ }
351
+ }
352
+ if (out.length > 0) return out;
353
+ return fallback.length > 0 ? [...fallback] : undefined;
354
+ }
355
+
356
+ function parseSimpleModule(v: unknown, fallback: readonly MeetSdkGradleDependency[]): MeetSdkSimpleModule | null {
357
+ if (v === undefined || v === false || v === null) return false;
358
+ if (v === true) return {};
359
+ if (!isRecord(v)) return null;
360
+ const dependencies = parseDependencies(v.dependencies);
361
+ const repositories = Array.isArray(v.repositories) ? v.repositories.map((x) => String(x)) : undefined;
362
+ if (!dependencies && !repositories) return {};
363
+ return { ...(repositories ? { repositories } : {}), ...(dependencies ? { dependencies } : {}) };
364
+ }
365
+
366
+ /**
367
+ * gp-sdk `getSDKConfig` 的 `data` 往往只有 `channelAuthConfig`;版本 / groupId 可能由网关合并或嵌在 `topsdk` 中。
368
+ */
369
+ function readTopSdkVersionFromData(data: Record<string, unknown> | null): string {
370
+ if (!data) return "0.0.0";
371
+ if (isRecord(data.topsdk)) {
372
+ const nested = str((data.topsdk as Record<string, unknown>).version);
373
+ if (nested) return nested;
374
+ }
375
+ const flat = str(
376
+ data.topsdkVersion ??
377
+ data.sdkVersion ??
378
+ data.version ??
379
+ data.sdkFullVersion ??
380
+ (data as { topSdkVersion?: unknown }).topSdkVersion
381
+ );
382
+ return flat || "0.0.0";
383
+ }
384
+
385
+ function readGroupIdFromData(data: Record<string, unknown> | null): string {
386
+ if (!data) return "com.sino.topsdk";
387
+ if (isRecord(data.topsdk)) {
388
+ const nested = str((data.topsdk as Record<string, unknown>).groupId);
389
+ if (nested) return nested;
390
+ }
391
+ const flat = str(data.groupId ?? (data as { mavenGroupId?: unknown }).mavenGroupId ?? (data as { group_id?: unknown }).group_id);
392
+ return flat || "com.sino.topsdk";
393
+ }
394
+
395
+ function readRepositoriesFromData(data: Record<string, unknown> | null): string[] {
396
+ if (!data) return [];
397
+ const raw = data.repositories ?? (data as { mavenRepositories?: unknown }).mavenRepositories;
398
+ if (Array.isArray(raw)) return raw.map((x) => String(x));
399
+ return [];
400
+ }
401
+
402
+ function parseLoginFacebookModule(v: unknown): MeetSdkLoginFacebookModule | null {
403
+ if (v === undefined || v === false || v === null) return false;
404
+ if (v === true) return null;
405
+ if (!isRecord(v)) return null;
406
+ return {
407
+ facebookAppId: str(v.clientId),
408
+ fbLoginProtocolScheme: str(v.scheme),
409
+ facebookClientToken: str(v.secret),
410
+ name: optionalStr(v.name),
411
+ dependencies: parseDependencies(v.dependencies),
412
+ };
413
+ }
414
+
415
+ function parseLoginGoogleModule(v: unknown): MeetSdkLoginGoogleModule | null {
416
+ if (v === undefined || v === false || v === null) return false;
417
+ if (v === true) return null;
418
+ if (!isRecord(v)) return null;
419
+ return {
420
+ googleClientId: str(v.clientId),
421
+ scheme: optionalStr(v.scheme),
422
+ dependencies: parseDependencies(v.dependencies),
423
+ };
424
+ }
425
+
426
+ function parseLoginTwitterModule(v: unknown): MeetSdkLoginTwitterModule | null {
427
+ if (v === undefined || v === false || v === null) return false;
428
+ if (v === true) return null;
429
+ if (!isRecord(v)) return null;
430
+ return {
431
+ clientId: str(v.clientId),
432
+ secret: str(v.secret),
433
+ redirect: str(v.redirect),
434
+ dependencies: parseDependencies(v.dependencies),
435
+ };
436
+ }
437
+
438
+ function parseLoginSnapchatModule(v: unknown): MeetSdkLoginSnapchatModule | null {
439
+ if (v === undefined || v === false || v === null) return false;
440
+ if (v === true) return null;
441
+ if (!isRecord(v)) return null;
442
+ const secret = str(v.secret);
443
+ const base: MeetSdkLoginSnapchatCredentials = {
444
+ clientId: str(v.clientId),
445
+ redirect: str(v.redirect),
446
+ scheme: optionalStr(v.scheme),
447
+ dependencies: parseDependencies(v.dependencies),
448
+ };
449
+ if (secret) base.secret = secret;
450
+ return base;
451
+ }
452
+
453
+ function parseLoginLineModule(v: unknown): MeetSdkLoginLineModule | null {
454
+ if (v === undefined || v === false || v === null) return false;
455
+ if (v === true) return null;
456
+ if (!isRecord(v)) return null;
457
+ return {
458
+ clientId: str(v.clientId),
459
+ scheme: optionalStr(v.scheme),
460
+ redirect: optionalStr(v.redirect),
461
+ dependencies: parseDependencies(v.dependencies),
462
+ };
463
+ }
464
+
465
+ function parseLoginNaverModule(v: unknown): MeetSdkLoginNaverModule | null {
466
+ if (v === undefined || v === false || v === null) return false;
467
+ if (v === true) return null;
468
+ if (!isRecord(v)) return null;
469
+ return {
470
+ clientId: str(v.clientId),
471
+ secret: str(v.secret),
472
+ name: str(v.name),
473
+ scheme: optionalStr(v.scheme),
474
+ redirect: optionalStr(v.redirect),
475
+ dependencies: parseDependencies(v.dependencies),
476
+ };
477
+ }
478
+
479
+ function parseLoginKakaoModule(v: unknown): MeetSdkLoginKakaoModule | null {
480
+ if (v === undefined || v === false || v === null) return false;
481
+ if (v === true) return null;
482
+ if (!isRecord(v)) return null;
483
+ return {
484
+ clientId: str(v.clientId),
485
+ scheme: str(v.scheme),
486
+ repositories: Array.isArray(v.repositories) ? v.repositories.map((x) => String(x)) : undefined,
487
+ dependencies: parseDependencies(v.dependencies),
488
+ };
489
+ }
490
+
491
+ function parseLoginTiktokModule(v: unknown): MeetSdkLoginTiktokModule | null {
492
+ if (v === undefined || v === false || v === null) return false;
493
+ if (v === true) return null;
494
+ if (!isRecord(v)) return null;
495
+ return {
496
+ clientId: str(v.clientId),
497
+ secret: str(v.secret),
498
+ redirect: str(v.redirect),
499
+ repositories: Array.isArray(v.repositories) ? v.repositories.map((x) => String(x)) : undefined,
500
+ dependencies: parseDependencies(v.dependencies),
501
+ };
502
+ }
503
+
504
+ function parseLoginDiscordModule(v: unknown): MeetSdkLoginDiscordModule | null {
505
+ if (v === undefined || v === false || v === null) return false;
506
+ if (v === true) return null;
507
+ if (!isRecord(v)) return null;
508
+ return {
509
+ clientId: str(v.clientId),
510
+ secret: str(v.secret),
511
+ redirect: str(v.redirect),
512
+ dependencies: parseDependencies(v.dependencies),
513
+ };
514
+ }
515
+
516
+ function parseAnalyticsFirebaseModule(v: unknown): MeetSdkAnalyticsFirebaseModule | null {
517
+ if (v === undefined || v === false || v === null) return false;
518
+ if (v === true) return null;
519
+ if (!isRecord(v)) return null;
520
+ return {
521
+ firebase_file_url: str(v.firebaseUrl),
522
+ firebase_file_name: optionalStr(v.firebaseName),
523
+ repositories: Array.isArray(v.repositories) ? v.repositories.map((x) => String(x)) : undefined,
524
+ classpath: str(v.classpath),
525
+ applyplugin: str(v.applyplugin ?? v.applyPlugin),
526
+ dependencies: parseDependencies(v.dependencies),
527
+ };
528
+ }
529
+
530
+ function parseAnalyticsAdjustModule(v: unknown): MeetSdkAnalyticsAdjustModule | null {
531
+ if (v === undefined || v === false || v === null) return false;
532
+ if (v === true) return null;
533
+ if (!isRecord(v)) return null;
534
+ return {
535
+ appId: str(v.appCode),
536
+ eventUrl: optionalStr(v.eventUrl),
537
+ enableSandbox: "enableSandbox" in v ? bool(v.enableSandbox) : false,
538
+ dependencies: parseDependencies(v.dependencies),
539
+ };
540
+ }
541
+
542
+ function parseAnalyticsFacebookDataModule(v: unknown): MeetSdkAnalyticsFacebookDataModule | null {
543
+ if (v === undefined || v === false || v === null) return false;
544
+ if (v === true) return null;
545
+ if (!isRecord(v)) return null;
546
+ return {
547
+ facebookAppId: optionalStr(v.clientId),
548
+ facebookClientToken: optionalStr(v.secret),
549
+ fbLoginProtocolScheme: optionalStr(v.scheme),
550
+ name: optionalStr(v.name),
551
+ dependencies: parseDependencies(v.dependencies),
552
+ };
553
+ }
554
+
555
+ function parseAnalyticsAppsflyerModule(v: unknown): MeetSdkAnalyticsAppsflyerModule | null {
556
+ if (v === undefined || v === false || v === null) return false;
557
+ if (v === true) return null;
558
+ if (!isRecord(v)) return null;
559
+ return {
560
+ devKey: str(v.devKey),
561
+ clientId: optionalStr(v.clientId),
562
+ appleAppId: optionalStr(v.appleAppId ?? v.appId),
563
+ enableDebugLog: "enableDebugLog" in v ? bool(v.enableDebugLog) : false,
564
+ repositories: Array.isArray(v.repositories) ? v.repositories.map((x) => String(x)) : undefined,
565
+ dependencies: parseDependencies(v.dependencies),
566
+ };
567
+ }
568
+
569
+ /** downloadSDKConfig:子键存在时 value 必须为 JSON 对象(含 `{}`)。 */
570
+ function isSdkModuleJsonObject(v: unknown): v is Record<string, unknown> {
571
+ return typeof v === "object" && v !== null && !Array.isArray(v);
572
+ }
573
+
574
+ /**
575
+ * 从 JSON 提取 `sdkModules`:仅保留出现的子键;有键则 value 须为对象。
576
+ * @returns `null` 表示形状非法(例如 `"kakao": false` 或 `true`)。
577
+ */
578
+ export function normalizeSdkModulesFromUnknown(raw: unknown): MeetSdkModuleToggles | null {
579
+ const d = emptySdkModules();
580
+ if (!isRecord(raw)) return d;
581
+
582
+ if (isRecord(raw.login)) {
583
+ const L = raw.login;
584
+ if ("parameterConfig" in L || "parameter" in L) return null;
585
+ if ("guest" in L) {
586
+ if (!isSdkModuleJsonObject(L.guest)) return null;
587
+ const x = parseSimpleModule(L.guest, DEFAULT_TOPSDK_PLUGIN_DEPENDENCIES.login.guest);
588
+ if (x === null || x === false) return null;
589
+ d.login.guest = x;
590
+ }
591
+ if ("email" in L) {
592
+ if (!isSdkModuleJsonObject(L.email)) return null;
593
+ const x = parseSimpleModule(L.email, DEFAULT_TOPSDK_PLUGIN_DEPENDENCIES.login.email);
594
+ if (x === null || x === false) return null;
595
+ d.login.email = x;
596
+ }
597
+ if ("apple" in L) {
598
+ if (!isSdkModuleJsonObject(L.apple)) return null;
599
+ const x = parseSimpleModule(L.apple, DEFAULT_TOPSDK_PLUGIN_DEPENDENCIES.login.apple);
600
+ if (x === null || x === false) return null;
601
+ d.login.apple = x;
602
+ }
603
+ if ("facebook" in L) {
604
+ if (!isSdkModuleJsonObject(L.facebook)) return null;
605
+ const fb = parseLoginFacebookModule(L.facebook);
606
+ if (fb === null || fb === false) return null;
607
+ d.login.facebook = fb;
608
+ }
609
+ if ("google" in L) {
610
+ if (!isSdkModuleJsonObject(L.google)) return null;
611
+ const g = parseLoginGoogleModule(L.google);
612
+ if (g === null || g === false) return null;
613
+ d.login.google = g;
614
+ }
615
+ if ("twitter" in L) {
616
+ if (!isSdkModuleJsonObject(L.twitter)) return null;
617
+ const x = parseLoginTwitterModule(L.twitter);
618
+ if (x === null || x === false) return null;
619
+ d.login.twitter = x;
620
+ }
621
+ if ("snapchat" in L) {
622
+ if (!isSdkModuleJsonObject(L.snapchat)) return null;
623
+ const x = parseLoginSnapchatModule(L.snapchat);
624
+ if (x === null || x === false) return null;
625
+ d.login.snapchat = x;
626
+ }
627
+ if ("line" in L) {
628
+ if (!isSdkModuleJsonObject(L.line)) return null;
629
+ const x = parseLoginLineModule(L.line);
630
+ if (x === null || x === false) return null;
631
+ d.login.line = x;
632
+ }
633
+ if ("naver" in L) {
634
+ if (!isSdkModuleJsonObject(L.naver)) return null;
635
+ const x = parseLoginNaverModule(L.naver);
636
+ if (x === null || x === false) return null;
637
+ d.login.naver = x;
638
+ }
639
+ if ("kakao" in L) {
640
+ if (!isSdkModuleJsonObject(L.kakao)) return null;
641
+ const x = parseLoginKakaoModule(L.kakao);
642
+ if (x === null || x === false) return null;
643
+ d.login.kakao = x;
644
+ }
645
+ if ("tiktok" in L) {
646
+ if (!isSdkModuleJsonObject(L.tiktok)) return null;
647
+ const x = parseLoginTiktokModule(L.tiktok);
648
+ if (x === null || x === false) return null;
649
+ d.login.tiktok = x;
650
+ }
651
+ if ("discord" in L) {
652
+ if (!isSdkModuleJsonObject(L.discord)) return null;
653
+ const x = parseLoginDiscordModule(L.discord);
654
+ if (x === null || x === false) return null;
655
+ d.login.discord = x;
656
+ }
657
+ }
658
+ if (isRecord(raw.payment)) {
659
+ const P = raw.payment;
660
+ if ("googleIap" in P) {
661
+ if (!isSdkModuleJsonObject(P.googleIap)) return null;
662
+ const x = parseSimpleModule(P.googleIap, DEFAULT_TOPSDK_PLUGIN_DEPENDENCIES.payment.googleIap);
663
+ if (x === null || x === false) return null;
664
+ d.payment.googleIap = x;
665
+ }
666
+ if ("onestoreIap" in P) {
667
+ if (!isSdkModuleJsonObject(P.onestoreIap)) return null;
668
+ const x = parseSimpleModule(P.onestoreIap, DEFAULT_TOPSDK_PLUGIN_DEPENDENCIES.payment.onestoreIap);
669
+ if (x === null || x === false) return null;
670
+ d.payment.onestoreIap = x;
671
+ }
672
+ if ("huaweiIap" in P) {
673
+ if (!isSdkModuleJsonObject(P.huaweiIap)) return null;
674
+ const x = parseSimpleModule(P.huaweiIap, DEFAULT_TOPSDK_PLUGIN_DEPENDENCIES.payment.huaweiIap);
675
+ if (x === null || x === false) return null;
676
+ d.payment.huaweiIap = x;
677
+ }
678
+ if ("xiaomiIap" in P) {
679
+ if (!isSdkModuleJsonObject(P.xiaomiIap)) return null;
680
+ const x = parseSimpleModule(P.xiaomiIap, DEFAULT_TOPSDK_PLUGIN_DEPENDENCIES.payment.xiaomiIap);
681
+ if (x === null || x === false) return null;
682
+ d.payment.xiaomiIap = x;
683
+ }
684
+ }
685
+ if (isRecord(raw.analytics)) {
686
+ const A = raw.analytics;
687
+ if ("appsflyer" in A) {
688
+ if (!isSdkModuleJsonObject(A.appsflyer)) return null;
689
+ const af = parseAnalyticsAppsflyerModule(A.appsflyer);
690
+ if (af === null || af === false) return null;
691
+ d.analytics.appsflyer = af;
692
+ }
693
+ if ("facebookdata" in A) {
694
+ if (!isSdkModuleJsonObject(A.facebookdata)) return null;
695
+ const x = parseAnalyticsFacebookDataModule(A.facebookdata);
696
+ if (x === null || x === false) return null;
697
+ d.analytics.facebookdata = x;
698
+ } else if ("facebook" in A) {
699
+ if (!isSdkModuleJsonObject(A.facebook)) return null;
700
+ const x = parseAnalyticsFacebookDataModule(A.facebook);
701
+ if (x === null || x === false) return null;
702
+ d.analytics.facebookdata = x;
703
+ }
704
+ if ("firebase" in A) {
705
+ if (!isSdkModuleJsonObject(A.firebase)) return null;
706
+ const f = parseAnalyticsFirebaseModule(A.firebase);
707
+ if (f === null || f === false) return null;
708
+ d.analytics.firebase = f;
709
+ }
710
+ if ("adjust" in A) {
711
+ if (!isSdkModuleJsonObject(A.adjust)) return null;
712
+ const adj = parseAnalyticsAdjustModule(A.adjust);
713
+ if (adj === null || adj === false) return null;
714
+ d.analytics.adjust = adj;
715
+ }
716
+ }
717
+ return d;
718
+ }
719
+
720
+ /**
721
+ * Maps TOPSDK `getSDKConfig` JSON (`code` / `data` / `data.channelAuthConfig`) into MeetSdkRemoteConfig.
722
+ * Applies the same MESSENGER→FACEBOOK naming tweak as topsdk-tool-ios `ChannelConfigManager.getSdkConfig`.
723
+ */
724
+ export function mapTopSdkGetSdkConfigToMeetSdkRemoteConfig(
725
+ body: unknown,
726
+ ctx: { appId: string; appSecret: string; channelType: string; packageName: string }
727
+ ): MeetSdkRemoteConfig {
728
+ const channel = ctx.channelType.trim().toUpperCase() || "UNKNOWN";
729
+
730
+ const base = (data: Record<string, unknown> | null): MeetSdkRemoteConfig => ({
731
+ packageName: str(isRecord(data) ? data.packageName : undefined) || ctx.packageName || "",
732
+ channel,
733
+ topsdk: {
734
+ appId: str(data?.appId ?? ctx.appId),
735
+ appSecret: ctx.appSecret,
736
+ version: readTopSdkVersionFromData(data),
737
+ groupId: readGroupIdFromData(data),
738
+ repositories: readRepositoriesFromData(data),
739
+ },
740
+ sdkModules: emptySdkModules(),
741
+ });
742
+
743
+ if (!isRecord(body)) {
744
+ return base(null);
745
+ }
746
+
747
+ const code = body.code;
748
+ if (code !== 200 && code !== "200") {
749
+ throw new Error(`TOPSDK getSDKConfig returned code=${String(code)}`);
750
+ }
751
+
752
+ const data = isRecord(body.data) ? body.data : null;
753
+ if (!data) {
754
+ return base(null);
755
+ }
756
+
757
+ const out = base(data);
758
+ out.topsdk.appId = str(data.appId ?? ctx.appId);
759
+ out.topsdk.appSecret = ctx.appSecret;
760
+
761
+ const list = data.channelAuthConfig;
762
+ if (!Array.isArray(list)) {
763
+ return out;
764
+ }
765
+
766
+ for (const raw of list) {
767
+ if (!isRecord(raw)) continue;
768
+ const item: Record<string, unknown> = { ...raw };
769
+ let name = str(item.authType).toUpperCase();
770
+ if (name === "MESSENGER") {
771
+ name = "FACEBOOK";
772
+ item.openMessenger = "1";
773
+ }
774
+ if (name === "FACEBOOK") {
775
+ if (item.openMessenger === undefined || item.openMessenger === null) {
776
+ item.openMessenger = "0";
777
+ }
778
+ }
779
+
780
+ switch (name) {
781
+ case "FACEBOOK":
782
+ out.sdkModules.login.facebook = {
783
+ facebookAppId: str(item.clientId),
784
+ fbLoginProtocolScheme: str(item.scheme),
785
+ facebookClientToken: str(item.clientToken ?? item.facebookClientToken ?? item.secret),
786
+ name: optionalStr(item.name ?? item.facebookDisplayName ?? item.displayName),
787
+ openMessenger: optionalStr(item.openMessenger),
788
+ };
789
+ break;
790
+ case "GOOGLE":
791
+ out.sdkModules.login.google = {
792
+ googleClientId: str(item.clientId),
793
+ scheme: optionalStr(item.scheme ?? item.reversedClientId ?? item.reversed_client_id),
794
+ reversedClientId: optionalStr(item.reversedClientId ?? item.reversed_client_id),
795
+ };
796
+ break;
797
+ case "APPSFLYER":
798
+ out.sdkModules.analytics.appsflyer = {
799
+ enableDebugLog: bool(item.enableDebugLog ?? item.appsflyerEnableDebugLog),
800
+ devKey: str(item.devKey ?? item.secret ?? item.clientId),
801
+ clientId: optionalStr(item.clientId),
802
+ appleAppId: optionalStr(item.appleAppId ?? item.appId),
803
+ };
804
+ break;
805
+ case "FIREBASE":
806
+ out.sdkModules.analytics.firebase = {
807
+ firebase_file_url: str(
808
+ item.firebase_file_url ?? item.googleServicesUrl ?? item.configUrl ?? item.clientId ?? item.url
809
+ ),
810
+ };
811
+ break;
812
+ case "ADJUST":
813
+ out.sdkModules.analytics.adjust = {
814
+ appId: str(item.appId ?? item.appToken ?? item.clientId),
815
+ clientId: optionalStr(item.clientId),
816
+ eventUrl: optionalStr(item.eventUrl ?? item.event_url),
817
+ enableSandbox: bool(item.enableSandbox ?? item.adjustEnableSandbox),
818
+ };
819
+ break;
820
+ case "GUEST":
821
+ out.sdkModules.login.guest = {};
822
+ break;
823
+ case "EMAIL":
824
+ out.sdkModules.login.email = {};
825
+ break;
826
+ case "APPLE":
827
+ case "APPLESIGNIN":
828
+ case "APPLE_SIGNIN":
829
+ out.sdkModules.login.apple = {};
830
+ break;
831
+ case "TWITTER":
832
+ out.sdkModules.login.twitter = {
833
+ clientId: str(item.clientId),
834
+ secret: str(item.secret),
835
+ redirect: str(item.redirect),
836
+ };
837
+ break;
838
+ case "SNAPCHAT":
839
+ out.sdkModules.login.snapchat = {
840
+ clientId: str(item.clientId),
841
+ redirect: str(item.redirect),
842
+ scheme: optionalStr(item.scheme),
843
+ ...(str(item.secret) ? { secret: str(item.secret) } : {}),
844
+ };
845
+ break;
846
+ case "LINE":
847
+ out.sdkModules.login.line = {
848
+ clientId: str(item.clientId),
849
+ scheme: optionalStr(item.scheme),
850
+ redirect: optionalStr(item.redirect),
851
+ };
852
+ break;
853
+ case "NAVER":
854
+ out.sdkModules.login.naver = {
855
+ clientId: str(item.clientId),
856
+ secret: str(item.secret),
857
+ name: str(item.name),
858
+ scheme: optionalStr(item.scheme),
859
+ redirect: optionalStr(item.redirect),
860
+ };
861
+ break;
862
+ case "KAKAO":
863
+ out.sdkModules.login.kakao = {
864
+ clientId: str(item.clientId),
865
+ scheme: str(item.scheme),
866
+ };
867
+ break;
868
+ case "TIKTOK":
869
+ out.sdkModules.login.tiktok = {
870
+ clientId: str(item.clientId),
871
+ secret: str(item.secret),
872
+ redirect: str(item.redirect),
873
+ };
874
+ break;
875
+ case "DISCORD":
876
+ out.sdkModules.login.discord = {
877
+ clientId: str(item.clientId),
878
+ secret: str(item.secret),
879
+ redirect: str(item.redirect),
880
+ };
881
+ break;
882
+ case "GOOGLEIAP":
883
+ case "GOOGLE_IAP":
884
+ out.sdkModules.payment.googleIap = {};
885
+ break;
886
+ case "ONESTOREIAP":
887
+ case "ONESTORE_IAP":
888
+ out.sdkModules.payment.onestoreIap = {};
889
+ break;
890
+ case "HUAWEIIAP":
891
+ case "HUAWEI_IAP":
892
+ out.sdkModules.payment.huaweiIap = {};
893
+ break;
894
+ case "XIAOMIIAP":
895
+ case "XIAOMI_IAP":
896
+ out.sdkModules.payment.xiaomiIap = {};
897
+ break;
898
+ default:
899
+ break;
900
+ }
901
+ }
902
+
903
+ return out;
904
+ }
905
+
906
+ export function getValueByPath(obj: unknown, dotPath: string): unknown {
907
+ if (!isRecord(obj)) return undefined;
908
+ const keys = dotPath.split(".");
909
+ let current: unknown = obj;
910
+ for (const key of keys) {
911
+ if (!isRecord(current)) return undefined;
912
+ current = current[key];
913
+ }
914
+ return current;
915
+ }
916
+
917
+ function isMissingValue(value: unknown): boolean {
918
+ if (value === undefined || value === null) return true;
919
+ if (typeof value === "string" && value.trim() === "") return true;
920
+ return false;
921
+ }
922
+
923
+ export function tryParseAsMeetSdkRemoteConfig(raw: unknown): MeetSdkRemoteConfig | null {
924
+ if (!isRecord(raw)) return null;
925
+ if (typeof raw.packageName !== "string" || !isRecord(raw.topsdk)) return null;
926
+ const topsdk = raw.topsdk as Record<string, unknown>;
927
+ const channel = str(raw.channel);
928
+ if (!channel || typeof topsdk.appId !== "string" || typeof topsdk.appSecret !== "string") {
929
+ return null;
930
+ }
931
+ if ("features" in raw || "featuresByCategory" in raw) return null;
932
+ if ("facebook" in raw || "google" in raw || "appsflyer" in raw) return null;
933
+ if ("parameterConfig" in raw) return null;
934
+ if ("repositories" in raw) return null;
935
+ if (!("sdkModules" in raw) || !isRecord(raw.sdkModules)) return null;
936
+ const sdkModules = normalizeSdkModulesFromUnknown(raw.sdkModules);
937
+ if (sdkModules === null) return null;
938
+ const normalizedTopsdk: MeetSdkRemoteTopsdkBlock = {
939
+ appId: topsdk.appId,
940
+ appSecret: topsdk.appSecret,
941
+ version: str(topsdk.version),
942
+ groupId: str(topsdk.groupId),
943
+ repositories: Array.isArray(topsdk.repositories) ? topsdk.repositories.map((x) => String(x)) : [],
944
+ };
945
+ const devicePlatform = str(raw.devicePlatform).toLowerCase();
946
+ return {
947
+ packageName: String(raw.packageName),
948
+ channel,
949
+ devicePlatform: devicePlatform || undefined,
950
+ topsdk: normalizedTopsdk,
951
+ sdkModules,
952
+ };
953
+ }
954
+
955
+ export function tryParseAsMeetSdkDefaultConfig(raw: unknown): MeetSdkDefaultConfig | null {
956
+ if (!isRecord(raw)) return null;
957
+ const topsdkRaw = isRecord(raw.topsdk) ? raw.topsdk : {};
958
+ const sdkModules = normalizeSdkModulesFromUnknown(isRecord(raw.sdkModules) ? raw.sdkModules : {});
959
+ if (sdkModules === null) return null;
960
+ return {
961
+ topsdk: {
962
+ version: str(topsdkRaw.version) || undefined,
963
+ date: str(topsdkRaw.date) || undefined,
964
+ groupId: str(topsdkRaw.groupId) || undefined,
965
+ repositories: Array.isArray(topsdkRaw.repositories) ? topsdkRaw.repositories.map((x) => String(x)) : [],
966
+ },
967
+ sdkModules,
968
+ };
969
+ }
970
+
971
+ function cloneConfig<T>(value: T): T {
972
+ if (value === undefined) return value;
973
+ return JSON.parse(JSON.stringify(value, (_k, v) => (v === undefined ? null : v))) as T;
974
+ }
975
+
976
+ function mergeSdkModuleValue(remoteValue: unknown, defaultValue: unknown): unknown {
977
+ if (!isSdkModuleToggleOn(remoteValue)) return remoteValue;
978
+ if (!isRecord(defaultValue)) return remoteValue;
979
+ if (!isRecord(remoteValue)) return cloneConfig(defaultValue);
980
+
981
+ const merged: Record<string, unknown> = { ...cloneConfig(defaultValue), ...cloneConfig(remoteValue) };
982
+ for (const [key, value] of Object.entries(defaultValue)) {
983
+ if (value === undefined) continue;
984
+ if (merged[key] === undefined || merged[key] === null || merged[key] === "") {
985
+ merged[key] = cloneConfig(value);
986
+ }
987
+ }
988
+ return merged;
989
+ }
990
+
991
+ function mergeSdkModuleScope(
992
+ remoteScope: Record<string, unknown>,
993
+ defaultScope: Record<string, unknown>
994
+ ): Record<string, unknown> {
995
+ const out: Record<string, unknown> = { ...remoteScope };
996
+ for (const key of Object.keys(remoteScope)) {
997
+ out[key] = mergeSdkModuleValue(remoteScope[key], defaultScope[key]);
998
+ }
999
+ return out;
1000
+ }
1001
+
1002
+ export function applyMeetSdkDefaultConfig(
1003
+ remote: MeetSdkRemoteConfig,
1004
+ defaults: MeetSdkDefaultConfig
1005
+ ): MeetSdkRemoteConfig {
1006
+ const out = cloneConfig(remote);
1007
+ out.topsdk = {
1008
+ ...out.topsdk,
1009
+ version: out.topsdk.version || defaults.topsdk.version || "",
1010
+ groupId: out.topsdk.groupId || defaults.topsdk.groupId || "",
1011
+ repositories: out.topsdk.repositories.length > 0 ? out.topsdk.repositories : defaults.topsdk.repositories,
1012
+ };
1013
+
1014
+ out.sdkModules = {
1015
+ login: mergeSdkModuleScope(
1016
+ out.sdkModules.login as unknown as Record<string, unknown>,
1017
+ defaults.sdkModules.login as unknown as Record<string, unknown>
1018
+ ) as unknown as MeetSdkLoginModules,
1019
+ payment: mergeSdkModuleScope(
1020
+ out.sdkModules.payment as unknown as Record<string, unknown>,
1021
+ defaults.sdkModules.payment as unknown as Record<string, unknown>
1022
+ ) as unknown as MeetSdkPaymentModules,
1023
+ analytics: mergeSdkModuleScope(
1024
+ out.sdkModules.analytics as unknown as Record<string, unknown>,
1025
+ defaults.sdkModules.analytics as unknown as Record<string, unknown>
1026
+ ) as unknown as MeetSdkAnalyticsModules,
1027
+ };
1028
+
1029
+ return out;
1030
+ }
1031
+
1032
+ function appendRepositories(out: string[], repositories: readonly string[] | undefined): void {
1033
+ if (!repositories) return;
1034
+ for (const repo of repositories) {
1035
+ if (!out.includes(repo)) out.push(repo);
1036
+ }
1037
+ }
1038
+
1039
+ /** Repositories for enabled `sdkModules` keys (from remote JSON) plus TopSdk host repos. */
1040
+ export function collectMeetSdkRemoteRepositories(data: MeetSdkRemoteConfig): string[] {
1041
+ const out = collectTopSdkFeatureRepositories(data);
1042
+ appendRepositories(out, data.topsdk.repositories);
1043
+ return out;
1044
+ }
1045
+
1046
+ export function collectMeetSdkRemoteBuildscriptRepositories(data: MeetSdkRemoteConfig): string[] {
1047
+ const analytics = data.sdkModules.analytics as unknown as Record<string, unknown>;
1048
+ if (!hasSdkModuleKey(analytics, "firebase")) return [];
1049
+ const firebase = data.sdkModules.analytics.firebase;
1050
+ if (typeof firebase !== "object" || firebase === null || !firebase.classpath) return [];
1051
+ const out: string[] = [];
1052
+ appendRepositories(out, firebase.repositories);
1053
+ return out;
1054
+ }
1055
+
1056
+ export function collectMeetSdkRemoteBuildscriptClasspaths(data: MeetSdkRemoteConfig): string[] {
1057
+ const analytics = data.sdkModules.analytics as unknown as Record<string, unknown>;
1058
+ if (!hasSdkModuleKey(analytics, "firebase")) return [];
1059
+ const firebase = data.sdkModules.analytics.firebase;
1060
+ if (typeof firebase !== "object" || firebase === null || !firebase.classpath) return [];
1061
+ return [firebase.classpath];
1062
+ }
1063
+
1064
+ export function collectMeetSdkRemoteApplyPlugins(data: MeetSdkRemoteConfig): string[] {
1065
+ const analytics = data.sdkModules.analytics as unknown as Record<string, unknown>;
1066
+ if (!hasSdkModuleKey(analytics, "firebase")) return [];
1067
+ const firebase = data.sdkModules.analytics.firebase;
1068
+ if (typeof firebase !== "object" || firebase === null || !firebase.applyplugin) return [];
1069
+ return [firebase.applyplugin];
1070
+ }
1071
+
1072
+ /** Gradle `plugins { id '…' }` entry (Firebase google-services). */
1073
+ export interface MeetSdkGradlePluginDslSpec {
1074
+ id: string;
1075
+ version?: string;
1076
+ applyFalse?: boolean;
1077
+ }
1078
+
1079
+ function firebaseGradlePluginVersion(classpath: string | undefined): string | undefined {
1080
+ if (!classpath) return undefined;
1081
+ const parts = classpath.split(":");
1082
+ return parts.length >= 3 ? parts[2] : undefined;
1083
+ }
1084
+
1085
+ /** Module-level `plugins { id 'com.google.gms.google-services' }` (version declared on root). */
1086
+ export function collectMeetSdkRemotePluginsDslForModule(data: MeetSdkRemoteConfig): MeetSdkGradlePluginDslSpec[] {
1087
+ const analytics = data.sdkModules.analytics as unknown as Record<string, unknown>;
1088
+ if (!hasSdkModuleKey(analytics, "firebase")) return [];
1089
+ const firebase = data.sdkModules.analytics.firebase;
1090
+ if (typeof firebase !== "object" || firebase === null || !firebase.applyplugin) return [];
1091
+ return [{ id: firebase.applyplugin }];
1092
+ }
1093
+
1094
+ /** Root-level `plugins { id '…' version '…' apply false }` for Firebase. */
1095
+ export function collectMeetSdkRemotePluginsDslForRoot(data: MeetSdkRemoteConfig): MeetSdkGradlePluginDslSpec[] {
1096
+ const analytics = data.sdkModules.analytics as unknown as Record<string, unknown>;
1097
+ if (!hasSdkModuleKey(analytics, "firebase")) return [];
1098
+ const firebase = data.sdkModules.analytics.firebase;
1099
+ if (typeof firebase !== "object" || firebase === null || !firebase.applyplugin) return [];
1100
+ const version = firebaseGradlePluginVersion(firebase.classpath);
1101
+ if (!version) return [];
1102
+ return [{ id: firebase.applyplugin, version, applyFalse: true }];
1103
+ }
1104
+
1105
+ /**
1106
+ * Validates populated sections (same spirit as sdk-integration-agent `validateRequiredConfig`):
1107
+ * 仅当子模块为参数对象时校验其字段;缺失键或 `false` 表示关闭。
1108
+ */
1109
+ /** CLI overrides applied after `downloadSDKConfig` (appId/channelType only; secret & packageName come from server JSON). */
1110
+ export function applyFetchConfigCliOverrides(
1111
+ config: MeetSdkRemoteConfig,
1112
+ overrides: {
1113
+ appId?: string;
1114
+ channelType?: string;
1115
+ }
1116
+ ): MeetSdkRemoteConfig {
1117
+ const out = cloneConfig(config);
1118
+ if (overrides.appId?.trim()) {
1119
+ out.topsdk.appId = overrides.appId.trim();
1120
+ }
1121
+ if (overrides.channelType?.trim()) {
1122
+ out.channel = overrides.channelType.trim().toUpperCase();
1123
+ }
1124
+ return out;
1125
+ }
1126
+
1127
+ export function validateMeetSdkRemoteConfig(data: MeetSdkRemoteConfig): { ok: boolean; missing: string[] } {
1128
+ const missing: string[] = [];
1129
+ const need = (path: string, reportPath = path) => {
1130
+ if (isMissingValue(getValueByPath(data, path))) missing.push(reportPath);
1131
+ };
1132
+
1133
+ need("packageName");
1134
+ need("channel");
1135
+ need("topsdk.appId");
1136
+ need("topsdk.appSecret");
1137
+
1138
+ const login = data.sdkModules.login as unknown as Record<string, unknown>;
1139
+ const payment = data.sdkModules.payment as unknown as Record<string, unknown>;
1140
+ const analytics = data.sdkModules.analytics as unknown as Record<string, unknown>;
1141
+
1142
+ if (hasSdkModuleKey(login, "facebook")) {
1143
+ need("sdkModules.login.facebook.facebookAppId", "sdkModules.login.facebook.clientId");
1144
+ need("sdkModules.login.facebook.fbLoginProtocolScheme", "sdkModules.login.facebook.scheme");
1145
+ need("sdkModules.login.facebook.facebookClientToken", "sdkModules.login.facebook.secret");
1146
+ }
1147
+
1148
+ if (hasSdkModuleKey(login, "google")) {
1149
+ need("sdkModules.login.google.googleClientId", "sdkModules.login.google.clientId");
1150
+ }
1151
+
1152
+ if (hasSdkModuleKey(analytics, "appsflyer")) {
1153
+ const af = data.sdkModules.analytics.appsflyer;
1154
+ if (typeof af === "object" && af !== null) {
1155
+ need("sdkModules.analytics.appsflyer.devKey");
1156
+ if (af.enableDebugLog === undefined || af.enableDebugLog === null) {
1157
+ missing.push("sdkModules.analytics.appsflyer.enableDebugLog");
1158
+ }
1159
+ }
1160
+ }
1161
+
1162
+ if (hasSdkModuleKey(login, "twitter")) {
1163
+ need("sdkModules.login.twitter.clientId");
1164
+ need("sdkModules.login.twitter.secret");
1165
+ need("sdkModules.login.twitter.redirect");
1166
+ }
1167
+
1168
+ if (hasSdkModuleKey(login, "snapchat")) {
1169
+ need("sdkModules.login.snapchat.clientId");
1170
+ need("sdkModules.login.snapchat.redirect");
1171
+ }
1172
+
1173
+ if (hasSdkModuleKey(login, "line")) {
1174
+ need("sdkModules.login.line.clientId");
1175
+ }
1176
+
1177
+ if (hasSdkModuleKey(login, "naver")) {
1178
+ need("sdkModules.login.naver.clientId");
1179
+ need("sdkModules.login.naver.secret");
1180
+ need("sdkModules.login.naver.name");
1181
+ }
1182
+
1183
+ if (hasSdkModuleKey(login, "kakao")) {
1184
+ need("sdkModules.login.kakao.clientId");
1185
+ need("sdkModules.login.kakao.scheme");
1186
+ }
1187
+
1188
+ if (hasSdkModuleKey(login, "tiktok")) {
1189
+ need("sdkModules.login.tiktok.clientId");
1190
+ need("sdkModules.login.tiktok.secret");
1191
+ need("sdkModules.login.tiktok.redirect");
1192
+ }
1193
+
1194
+ if (hasSdkModuleKey(login, "discord")) {
1195
+ need("sdkModules.login.discord.clientId");
1196
+ need("sdkModules.login.discord.secret");
1197
+ need("sdkModules.login.discord.redirect");
1198
+ }
1199
+
1200
+ if (hasSdkModuleKey(analytics, "firebase")) {
1201
+ need("sdkModules.analytics.firebase.firebase_file_url", "sdkModules.analytics.firebase.firebaseUrl");
1202
+ }
1203
+
1204
+ if (hasSdkModuleKey(analytics, "adjust")) {
1205
+ const adj = data.sdkModules.analytics.adjust;
1206
+ if (typeof adj === "object" && adj !== null) {
1207
+ need("sdkModules.analytics.adjust.appId", "sdkModules.analytics.adjust.appCode");
1208
+ if (adj.enableSandbox === undefined || adj.enableSandbox === null) {
1209
+ missing.push("sdkModules.analytics.adjust.enableSandbox");
1210
+ }
1211
+ }
1212
+ }
1213
+
1214
+ return { ok: missing.length === 0, missing };
1215
+ }