@leonxin/meetgames 0.1.8 → 0.1.11

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 (285) 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 +31 -8
  7. package/dist/aab-converter/aab-entry.d.ts +3 -0
  8. package/dist/aab-converter/aab-entry.d.ts.map +1 -0
  9. package/dist/aab-converter/aab-entry.js +49 -0
  10. package/dist/aab-converter/aab-entry.js.map +1 -0
  11. package/dist/aab-converter/apksExtractor.d.ts +2 -0
  12. package/dist/aab-converter/apksExtractor.d.ts.map +1 -0
  13. package/dist/aab-converter/apksExtractor.js +108 -0
  14. package/dist/aab-converter/apksExtractor.js.map +1 -0
  15. package/dist/aab-converter/bundletoolRunner.d.ts +15 -0
  16. package/dist/aab-converter/bundletoolRunner.d.ts.map +1 -0
  17. package/dist/aab-converter/bundletoolRunner.js +46 -0
  18. package/dist/aab-converter/bundletoolRunner.js.map +1 -0
  19. package/dist/aab-converter/cliArgs.d.ts +27 -0
  20. package/dist/aab-converter/cliArgs.d.ts.map +1 -0
  21. package/dist/aab-converter/cliArgs.js +170 -0
  22. package/dist/aab-converter/cliArgs.js.map +1 -0
  23. package/dist/aab-converter/convertAabToApk.d.ts +7 -0
  24. package/dist/aab-converter/convertAabToApk.d.ts.map +1 -0
  25. package/dist/aab-converter/convertAabToApk.js +69 -0
  26. package/dist/aab-converter/convertAabToApk.js.map +1 -0
  27. package/dist/aab-converter/resourcePaths.d.ts +4 -0
  28. package/dist/aab-converter/resourcePaths.d.ts.map +1 -0
  29. package/dist/aab-converter/resourcePaths.js +42 -0
  30. package/dist/aab-converter/resourcePaths.js.map +1 -0
  31. package/dist/aab-converter/signingOptions.d.ts +9 -0
  32. package/dist/aab-converter/signingOptions.d.ts.map +1 -0
  33. package/dist/aab-converter/signingOptions.js +21 -0
  34. package/dist/aab-converter/signingOptions.js.map +1 -0
  35. package/dist/aab-converter/types.d.ts +24 -0
  36. package/dist/aab-converter/types.d.ts.map +1 -0
  37. package/dist/aab-converter/types.js +2 -0
  38. package/dist/aab-converter/types.js.map +1 -0
  39. package/dist/android/adapter.d.ts.map +1 -1
  40. package/dist/android/adapter.js +2 -2
  41. package/dist/android/adapter.js.map +1 -1
  42. package/dist/android/detect.d.ts +2 -2
  43. package/dist/android/detect.d.ts.map +1 -1
  44. package/dist/android/detect.js +36 -8
  45. package/dist/android/detect.js.map +1 -1
  46. package/dist/cli.d.ts.map +1 -1
  47. package/dist/cli.js +157 -31
  48. package/dist/cli.js.map +1 -1
  49. package/dist/config/meetSdkDefaultConfig.d.ts +19 -2
  50. package/dist/config/meetSdkDefaultConfig.d.ts.map +1 -1
  51. package/dist/config/meetSdkDefaultConfig.js +67 -5
  52. package/dist/config/meetSdkDefaultConfig.js.map +1 -1
  53. package/dist/config/meetSdkIosConfig.d.ts +21 -0
  54. package/dist/config/meetSdkIosConfig.d.ts.map +1 -0
  55. package/dist/config/meetSdkIosConfig.js +66 -0
  56. package/dist/config/meetSdkIosConfig.js.map +1 -0
  57. package/dist/config/meetSdkRemoteConfig.d.ts +18 -1
  58. package/dist/config/meetSdkRemoteConfig.d.ts.map +1 -1
  59. package/dist/config/meetSdkRemoteConfig.js +61 -25
  60. package/dist/config/meetSdkRemoteConfig.js.map +1 -1
  61. package/dist/contracts/types.d.ts +19 -6
  62. package/dist/contracts/types.d.ts.map +1 -1
  63. package/dist/core/doctor.d.ts +17 -0
  64. package/dist/core/doctor.d.ts.map +1 -0
  65. package/dist/core/doctor.js +444 -0
  66. package/dist/core/doctor.js.map +1 -0
  67. package/dist/core/pipeline.d.ts.map +1 -1
  68. package/dist/core/pipeline.js +0 -15
  69. package/dist/core/pipeline.js.map +1 -1
  70. package/dist/core/platform.d.ts +12 -0
  71. package/dist/core/platform.d.ts.map +1 -0
  72. package/dist/core/platform.js +40 -0
  73. package/dist/core/platform.js.map +1 -0
  74. package/dist/core/reporter.js +1 -1
  75. package/dist/core/reporter.js.map +1 -1
  76. package/dist/core/workspace.d.ts +2 -2
  77. package/dist/core/workspace.d.ts.map +1 -1
  78. package/dist/core/workspace.js +4 -5
  79. package/dist/core/workspace.js.map +1 -1
  80. package/dist/index.d.ts +3 -1
  81. package/dist/index.d.ts.map +1 -1
  82. package/dist/index.js +3 -1
  83. package/dist/index.js.map +1 -1
  84. package/dist/ios/channelConfig.d.ts +1 -0
  85. package/dist/ios/channelConfig.d.ts.map +1 -1
  86. package/dist/ios/channelConfig.js +82 -0
  87. package/dist/ios/channelConfig.js.map +1 -1
  88. package/dist/ios/codeUtils.d.ts +1 -0
  89. package/dist/ios/codeUtils.d.ts.map +1 -1
  90. package/dist/ios/codeUtils.js +11 -2
  91. package/dist/ios/codeUtils.js.map +1 -1
  92. package/dist/ios/detect.d.ts +2 -2
  93. package/dist/ios/detect.d.ts.map +1 -1
  94. package/dist/ios/detect.js +49 -10
  95. package/dist/ios/detect.js.map +1 -1
  96. package/dist/ios/entitlements.d.ts +4 -0
  97. package/dist/ios/entitlements.d.ts.map +1 -0
  98. package/dist/ios/entitlements.js +53 -0
  99. package/dist/ios/entitlements.js.map +1 -0
  100. package/dist/ios/fileManager.d.ts.map +1 -1
  101. package/dist/ios/fileManager.js +3 -2
  102. package/dist/ios/fileManager.js.map +1 -1
  103. package/dist/ios/infoPlist.d.ts +1 -1
  104. package/dist/ios/infoPlist.d.ts.map +1 -1
  105. package/dist/ios/infoPlist.js.map +1 -1
  106. package/dist/ios/integrate.d.ts.map +1 -1
  107. package/dist/ios/integrate.js +211 -36
  108. package/dist/ios/integrate.js.map +1 -1
  109. package/dist/ios/pbxprojEditor.d.ts +2 -0
  110. package/dist/ios/pbxprojEditor.d.ts.map +1 -1
  111. package/dist/ios/pbxprojEditor.js +179 -1
  112. package/dist/ios/pbxprojEditor.js.map +1 -1
  113. package/dist/ios/pluginConfig.d.ts +1 -0
  114. package/dist/ios/pluginConfig.d.ts.map +1 -1
  115. package/dist/ios/pluginConfig.js +36 -4
  116. package/dist/ios/pluginConfig.js.map +1 -1
  117. package/dist/ios/sdkBundle.d.ts +1 -1
  118. package/dist/ios/sdkBundle.d.ts.map +1 -1
  119. package/dist/ios/sdkBundle.js +7 -5
  120. package/dist/ios/sdkBundle.js.map +1 -1
  121. package/dist/ios/template.d.ts +1 -0
  122. package/dist/ios/template.d.ts.map +1 -1
  123. package/dist/ios/template.js +14 -1
  124. package/dist/ios/template.js.map +1 -1
  125. package/dist/ios/types.d.ts +2 -2
  126. package/dist/ios/types.d.ts.map +1 -1
  127. package/dist/mcp/server.d.ts.map +1 -1
  128. package/dist/mcp/server.js +14 -13
  129. package/dist/mcp/server.js.map +1 -1
  130. package/dist/mcp/service.d.ts +8 -6
  131. package/dist/mcp/service.d.ts.map +1 -1
  132. package/dist/mcp/service.js +34 -14
  133. package/dist/mcp/service.js.map +1 -1
  134. package/dist/ops/handlers.d.ts.map +1 -1
  135. package/dist/ops/handlers.js +10 -4
  136. package/dist/ops/handlers.js.map +1 -1
  137. package/dist/remote/sdkHomeDownload.d.ts +65 -0
  138. package/dist/remote/sdkHomeDownload.d.ts.map +1 -0
  139. package/dist/remote/sdkHomeDownload.js +208 -0
  140. package/dist/remote/sdkHomeDownload.js.map +1 -0
  141. package/dist/remote/topsdkDownloadSdkConfig.d.ts.map +1 -1
  142. package/dist/remote/topsdkDownloadSdkConfig.js +11 -1
  143. package/dist/remote/topsdkDownloadSdkConfig.js.map +1 -1
  144. package/dist/shared/errors.d.ts +7 -0
  145. package/dist/shared/errors.d.ts.map +1 -0
  146. package/dist/shared/errors.js +16 -0
  147. package/dist/shared/errors.js.map +1 -0
  148. package/dist/shared/fileUtils.d.ts +5 -0
  149. package/dist/shared/fileUtils.d.ts.map +1 -0
  150. package/dist/shared/fileUtils.js +35 -0
  151. package/dist/shared/fileUtils.js.map +1 -0
  152. package/dist/shared/logger.d.ts +10 -0
  153. package/dist/shared/logger.d.ts.map +1 -0
  154. package/dist/shared/logger.js +37 -0
  155. package/dist/shared/logger.js.map +1 -0
  156. package/dist/shared/pathUtils.d.ts +4 -0
  157. package/dist/shared/pathUtils.d.ts.map +1 -0
  158. package/dist/shared/pathUtils.js +22 -0
  159. package/dist/shared/pathUtils.js.map +1 -0
  160. package/dist/shared/processRunner.d.ts +12 -0
  161. package/dist/shared/processRunner.d.ts.map +1 -0
  162. package/dist/shared/processRunner.js +31 -0
  163. package/dist/shared/processRunner.js.map +1 -0
  164. package/docs/AAB_CONVERTER_CLI_PLAN.md +392 -0
  165. package/docs/API.md +246 -32
  166. package/docs/CLI.md +292 -0
  167. package/docs/INTEGRATION.md +116 -0
  168. package/docs/MCP.md +86 -0
  169. package/docs/README.md +19 -10
  170. package/docs/{api → archive/api}/downloadSDKConfig.md +8 -6
  171. package/docs/{api → archive/api}/getChannelConfig-meetgames.md +1 -1
  172. package/docs/archive/ios-migration.md +2139 -0
  173. 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
  174. package/docs/{ → archive/product/}/351/234/200/346/261/202/346/226/207/346/241/243.md +15 -14
  175. package/logs/convert-20260622-155037.log +5 -0
  176. package/logs/convert-20260622-155226.log +6 -0
  177. package/meetsdk-android.json +2 -1
  178. package/meetsdk-ios.json +15 -0
  179. package/package.json +10 -36
  180. package/scripts/package-aab-cli-win.mjs +193 -0
  181. package/src/aab-converter/aab-entry.ts +48 -0
  182. package/src/aab-converter/apksExtractor.ts +119 -0
  183. package/src/aab-converter/bundletoolRunner.ts +63 -0
  184. package/src/aab-converter/cliArgs.ts +194 -0
  185. package/src/aab-converter/convertAabToApk.ts +81 -0
  186. package/src/aab-converter/resourcePaths.ts +43 -0
  187. package/src/aab-converter/signingOptions.ts +29 -0
  188. package/src/aab-converter/types.ts +26 -0
  189. package/src/android/adapter.ts +9 -0
  190. package/src/android/assembleIntegrationJson.ts +33 -0
  191. package/src/android/detect.ts +132 -0
  192. package/src/android/downloadGoogleServicesJson.ts +56 -0
  193. package/src/android/gradle.ts +116 -0
  194. package/src/android/manifest.ts +50 -0
  195. package/src/android/meetSdkRemoteGradle.ts +837 -0
  196. package/src/cli.ts +488 -0
  197. package/src/config/fetchConfigWrite.ts +30 -0
  198. package/src/config/loadAndroidIntegration.ts +41 -0
  199. package/src/config/loadManifest.ts +40 -0
  200. package/src/config/meetSdkDefaultConfig.ts +99 -0
  201. package/src/config/meetSdkIosConfig.ts +87 -0
  202. package/src/config/meetSdkRemoteConfig.ts +1211 -0
  203. package/src/config/topsdkFeatureModules.ts +92 -0
  204. package/src/contracts/types.ts +121 -0
  205. package/src/core/doctor.ts +485 -0
  206. package/src/core/patch.ts +64 -0
  207. package/src/core/pipeline.ts +107 -0
  208. package/src/core/platform.ts +47 -0
  209. package/src/core/previewPatches.ts +23 -0
  210. package/src/core/reporter.ts +24 -0
  211. package/src/core/workspace.ts +29 -0
  212. package/src/entry.ts +7 -0
  213. package/src/index.ts +133 -0
  214. package/src/ios/channelConfig.ts +128 -0
  215. package/src/ios/codeUtils.ts +160 -0
  216. package/src/ios/detect.ts +105 -0
  217. package/src/ios/entitlements.ts +61 -0
  218. package/src/ios/fileManager.ts +48 -0
  219. package/src/ios/infoPlist.ts +55 -0
  220. package/src/ios/integrate.ts +516 -0
  221. package/src/ios/pbxprojEditor.ts +383 -0
  222. package/src/ios/pluginConfig.ts +97 -0
  223. package/src/ios/reserved.ts +8 -0
  224. package/src/ios/sdkBundle.ts +36 -0
  225. package/src/ios/template.ts +36 -0
  226. package/src/ios/types.ts +65 -0
  227. package/src/mcp/server.ts +170 -0
  228. package/src/mcp/service.ts +222 -0
  229. package/src/mcp-entry.ts +7 -0
  230. package/src/ops/fileStore.ts +56 -0
  231. package/src/ops/handlers.ts +304 -0
  232. package/src/remote/fetchJson.ts +22 -0
  233. package/src/remote/sdkHomeDownload.ts +274 -0
  234. package/src/remote/topsdkDownloadSdkConfig.ts +93 -0
  235. package/src/remote/topsdkGetSdkConfig.ts +122 -0
  236. package/src/remote/topsdkSign.ts +10 -0
  237. package/src/shared/errors.ts +16 -0
  238. package/src/shared/fileUtils.ts +41 -0
  239. package/src/shared/logger.ts +49 -0
  240. package/src/shared/pathUtils.ts +24 -0
  241. package/src/shared/processRunner.ts +43 -0
  242. package/test-projects/README.md +51 -0
  243. package/test-projects/_preview/pipeline.patch +281 -0
  244. package/tests/aab-converter.test.ts +213 -0
  245. package/tests/assemble.test.ts +12 -0
  246. package/tests/doctor.test.ts +89 -0
  247. package/tests/downloadGoogleServicesJson.test.ts +47 -0
  248. package/tests/fetch-remote.test.ts +23 -0
  249. package/tests/fetchConfigOverrides.test.ts +28 -0
  250. package/tests/fetchConfigWrite.test.ts +54 -0
  251. package/tests/gradle.test.ts +33 -0
  252. package/tests/integration-json.test.ts +29 -0
  253. package/tests/ios.codeUtils.test.ts +23 -0
  254. package/tests/ios.sdkBundle.test.ts +16 -0
  255. package/tests/loadManifest.test.ts +15 -0
  256. package/tests/manifest-xml.test.ts +30 -0
  257. package/tests/mcp.e2e.ts +217 -0
  258. package/tests/mcp.service.test.ts +53 -0
  259. package/tests/meetSdkRemoteConfig.test.ts +456 -0
  260. package/tests/meetSdkRemoteGradle.test.ts +414 -0
  261. package/tests/pipeline.android.test.ts +96 -0
  262. package/tests/pipeline.integration-json.test.ts +58 -0
  263. package/tests/pipeline.ios.test.ts +385 -0
  264. package/tests/pipeline.preview.patch.test.ts +85 -0
  265. package/tests/platformSelection.test.ts +77 -0
  266. package/tests/sdkHomeDownload.test.ts +124 -0
  267. package/tests/sdkVersionConfig.test.ts +130 -0
  268. package/tests/test-projects-hosts.test.ts +78 -0
  269. package/tests/topsdk.test.ts +53 -0
  270. package/tests/topsdkDownloadSdkConfig.test.ts +81 -0
  271. package/tests/topsdkFeatureModules.test.ts +116 -0
  272. package/tsconfig.json +19 -0
  273. package/vitest.config.ts +9 -0
  274. package/vitest.mcp.config.ts +11 -0
  275. package/bundled/android/sample.txt +0 -1
  276. package/docs/ANDROID.md +0 -133
  277. package/docs/CURSOR-MCP-SETUP.md +0 -72
  278. package/docs/MCP-SKILL.md +0 -63
  279. package/fixtures/api-samples/getChannelConfig-meetgames.sample.json +0 -123
  280. package/fixtures/meetsdk-remote-config.download-shape.json +0 -20
  281. package/fixtures/meetsdk-remote-config.mock.json +0 -69
  282. package/fixtures/recipes/android-default.fixture.yaml +0 -15
  283. package/fixtures/recipes/android-integration.fixture.json +0 -29
  284. package/fixtures/topsdk-config-reference.json +0 -39
  285. /package/docs/{api → archive/api}/getSDKConfig.md +0 -0
@@ -0,0 +1,414 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import { describe, expect, it } from "vitest";
5
+ import {
6
+ collectMeetSdkRemoteApplyPlugins,
7
+ collectMeetSdkRemoteBuildscriptClasspaths,
8
+ collectMeetSdkRemoteBuildscriptRepositories,
9
+ collectMeetSdkRemotePluginsDslForModule,
10
+ collectMeetSdkRemotePluginsDslForRoot,
11
+ collectMeetSdkRemoteRepositories,
12
+ applyMeetSdkDefaultConfig,
13
+ tryParseAsMeetSdkRemoteConfig,
14
+ validateMeetSdkRemoteConfig,
15
+ } from "../src/config/meetSdkRemoteConfig.js";
16
+ import { loadBuiltInMeetSdkDefaultConfig } from "../src/config/meetSdkDefaultConfig.js";
17
+ import {
18
+ TOPSDK_AUTO_START,
19
+ TOPSDK_AUTO_END,
20
+ TOPSDK_PLUGIN_AUTO_START,
21
+ TOPSDK_REPO_AUTO_START,
22
+ detectModuleGradlePluginStyle,
23
+ extractApplyPluginId,
24
+ extractClasspathCoordinate,
25
+ extractImplementationKey,
26
+ extractPluginsDslId,
27
+ extractResValueKey,
28
+ mavenGroupArtifactKey,
29
+ mergeApplyPluginsInContent,
30
+ mergeClasspathsInBlock,
31
+ mergePluginsDslInContent,
32
+ mergeRepositoriesInBlock,
33
+ mergeResValuesInDefaultConfigBlock,
34
+ removeImplementationLinesByGroupArtifact,
35
+ updateModuleBuildGradleMeetSdkRemote,
36
+ updateRootBuildGradleMeetSdkRemote,
37
+ updateSettingsGradleMeetSdkRemote,
38
+ } from "../src/android/meetSdkRemoteGradle.js";
39
+
40
+ const here = path.dirname(fileURLToPath(import.meta.url));
41
+ const sampleConfigPath = path.join(here, "..", "fixtures", "meetsdk-remote-config.mock.json");
42
+
43
+ describe("meetSdkRemoteGradle", () => {
44
+ it("writes repositories and module blocks from meetsdk-remote-config mock shape", () => {
45
+ const raw = JSON.parse(fs.readFileSync(sampleConfigPath, "utf8")) as unknown;
46
+ const cfg = tryParseAsMeetSdkRemoteConfig(raw);
47
+ expect(cfg).not.toBeNull();
48
+ expect(validateMeetSdkRemoteConfig(cfg!).ok).toBe(true);
49
+ const resolved = applyMeetSdkDefaultConfig(cfg!, loadBuiltInMeetSdkDefaultConfig());
50
+
51
+ const rootIn = `
52
+ allprojects {
53
+ repositories {
54
+ google()
55
+ }
56
+ }
57
+ `;
58
+ const rootOut = updateRootBuildGradleMeetSdkRemote(rootIn, {
59
+ repositories: collectMeetSdkRemoteRepositories(resolved),
60
+ buildscriptRepositories: collectMeetSdkRemoteBuildscriptRepositories(resolved),
61
+ buildscriptClasspaths: collectMeetSdkRemoteBuildscriptClasspaths(resolved),
62
+ });
63
+ expect(rootOut.ok).toBe(true);
64
+ if (!rootOut.ok) return;
65
+ expect(rootOut.content).toContain(TOPSDK_REPO_AUTO_START);
66
+ expect(rootOut.content).toContain("storage-sdk-gameplus");
67
+ expect(rootOut.content).toContain("google()");
68
+ expect(rootOut.content).toContain("mavenCentral()");
69
+ expect(rootOut.content).toContain("storage-sdk-gameplus");
70
+ expect(rootOut.content).not.toContain("//Firebase");
71
+ expect(rootOut.content).not.toContain("//Appsflyer");
72
+ expect(rootOut.content).not.toContain("//TOPSdk");
73
+ expect(rootOut.content).toContain("buildscript");
74
+ expect(rootOut.content).toContain("com.google.gms:google-services:4.4.4");
75
+
76
+ const modIn = `
77
+ apply plugin: 'com.android.application'
78
+
79
+ android {
80
+ defaultConfig {
81
+ applicationId "com.example.app"
82
+ }
83
+ }
84
+
85
+ dependencies {
86
+ }
87
+ `;
88
+ const modOut = updateModuleBuildGradleMeetSdkRemote(modIn, resolved, collectMeetSdkRemoteApplyPlugins(resolved));
89
+ expect(modOut.ok).toBe(true);
90
+ if (!modOut.ok) return;
91
+ expect(modOut.content).toContain(TOPSDK_AUTO_START);
92
+ expect(modOut.content).toContain("top_channel_id");
93
+ expect(modOut.content).toContain("top_app_id");
94
+ expect(modOut.content).toContain(resolved.topsdk.appId);
95
+ expect(modOut.content).toContain(resolved.topsdk.version);
96
+ expect(modOut.content).toContain('implementation "$groupId:ui:$topsdk_version"');
97
+ expect(modOut.content).toContain(':google:');
98
+ expect(modOut.content).not.toContain("top_package_name");
99
+ expect(modOut.content).toContain(`applicationId "${resolved.packageName}"`);
100
+ expect(modOut.content).toContain("facebook_app_id");
101
+ expect(modOut.content).toContain("apply plugin: 'com.google.gms.google-services'");
102
+ });
103
+
104
+ it("updates existing resValue by key instead of duplicating", () => {
105
+ const blockIn = `
106
+ applicationId 'com.feishu.topsdk'
107
+ resValue('string', 'top_channel_id', 'GOOGLE')
108
+ resValue('string', 'facebook_app_id', 'old-id')
109
+ `;
110
+ const snippet = `${TOPSDK_AUTO_START}
111
+ resValue('string', 'top_channel_id', 'GOOGLE')
112
+ resValue('string', 'top_app_id', '791251136341225472')
113
+ resValue('string', 'facebook_app_id', 'new-id')
114
+ resValue('string', 'kakao_app_id', 'kakao-key')
115
+ ${TOPSDK_AUTO_END}`;
116
+ const out = mergeResValuesInDefaultConfigBlock(blockIn, snippet);
117
+ expect(extractResValueKey("resValue('string', 'facebook_app_id', 'old-id')")).toBe("facebook_app_id");
118
+ expect((out.match(/facebook_app_id/g) ?? []).length).toBe(1);
119
+ expect(out).toContain("'new-id'");
120
+ expect(out).not.toContain("'old-id'");
121
+ expect(out).toContain("kakao_app_id");
122
+ expect(out).not.toContain("kakao_native_app_key");
123
+ expect(out).toContain("top_app_id");
124
+ });
125
+
126
+ it("inserts apply plugin block directly after com.android.application", () => {
127
+ const modIn = `
128
+ apply plugin: 'com.android.application'
129
+
130
+ dependencies {
131
+ }
132
+
133
+ android {
134
+ defaultConfig { applicationId "com.example.app" }
135
+ }
136
+ `;
137
+ const raw = JSON.parse(fs.readFileSync(sampleConfigPath, "utf8")) as unknown;
138
+ const cfg = tryParseAsMeetSdkRemoteConfig(raw);
139
+ const resolved = applyMeetSdkDefaultConfig(cfg!, loadBuiltInMeetSdkDefaultConfig());
140
+ const modOut = updateModuleBuildGradleMeetSdkRemote(modIn, resolved, collectMeetSdkRemoteApplyPlugins(resolved));
141
+ expect(modOut.ok).toBe(true);
142
+ if (!modOut.ok) return;
143
+ const lines = modOut.content.split("\n").map((l) => l.trimEnd());
144
+ const appPluginIdx = lines.findIndex((l) => l.includes("com.android.application"));
145
+ const googleServicesIdx = lines.findIndex((l) => l.includes("com.google.gms.google-services"));
146
+ const depsIdx = lines.findIndex((l) => l.trim() === "dependencies {");
147
+ expect(appPluginIdx).toBeGreaterThanOrEqual(0);
148
+ expect(googleServicesIdx).toBeGreaterThan(appPluginIdx);
149
+ expect(depsIdx).toBeGreaterThan(googleServicesIdx);
150
+ expect(modOut.content.indexOf(TOPSDK_PLUGIN_AUTO_START)).toBeLessThan(modOut.content.indexOf("dependencies"));
151
+ });
152
+
153
+ it("updates apply plugin by id instead of duplicating", () => {
154
+ const modIn = `
155
+ apply plugin: 'com.android.application'
156
+ apply plugin: 'com.google.gms.google-services'
157
+
158
+ android {
159
+ defaultConfig { applicationId "com.example.app" }
160
+ }
161
+ dependencies { }
162
+ `;
163
+ const raw = JSON.parse(fs.readFileSync(sampleConfigPath, "utf8")) as unknown;
164
+ const cfg = tryParseAsMeetSdkRemoteConfig(raw);
165
+ const resolved = applyMeetSdkDefaultConfig(cfg!, loadBuiltInMeetSdkDefaultConfig());
166
+ const modOut = updateModuleBuildGradleMeetSdkRemote(modIn, resolved, collectMeetSdkRemoteApplyPlugins(resolved));
167
+ expect(modOut.ok).toBe(true);
168
+ if (!modOut.ok) return;
169
+ expect(extractApplyPluginId("apply plugin: 'com.google.gms.google-services'")).toBe(
170
+ "com.google.gms.google-services"
171
+ );
172
+ const pluginLines = modOut.content
173
+ .split("\n")
174
+ .filter((l) => l.includes("com.google.gms.google-services"));
175
+ expect(pluginLines.length).toBe(1);
176
+ expect(modOut.content).toContain(TOPSDK_PLUGIN_AUTO_START);
177
+ });
178
+
179
+ it("updates classpath by group:artifact instead of duplicating", () => {
180
+ const blockIn = `
181
+ classpath 'com.google.gms:google-services:4.3.0'
182
+ `;
183
+ const out = mergeClasspathsInBlock(blockIn, ["com.google.gms:google-services:4.4.4"]);
184
+ expect(mavenGroupArtifactKey("com.google.gms:google-services:4.3.0")).toBe("com.google.gms:google-services");
185
+ const classpathLines = out.split("\n").filter((l) => extractClasspathCoordinate(l));
186
+ expect(classpathLines.length).toBe(1);
187
+ expect(classpathLines[0]).toContain("4.4.4");
188
+ expect(out).not.toContain("4.3.0");
189
+ });
190
+
191
+ it("updates implementation by group:artifact instead of duplicating", () => {
192
+ const blockIn = `
193
+ def groupId = "com.sino.topsdk"
194
+ implementation "com.sino.topsdk:facebook:1.0.0"
195
+ implementation "com.sino.topsdk:google:9.9.9"
196
+ `;
197
+ const cleaned = removeImplementationLinesByGroupArtifact(
198
+ blockIn,
199
+ new Set(["com.sino.topsdk:facebook"]),
200
+ "com.sino.topsdk"
201
+ );
202
+ expect(
203
+ extractImplementationKey('implementation "com.sino.topsdk:facebook:1.0.0"', blockIn, "com.sino.topsdk")
204
+ ).toBe("com.sino.topsdk:facebook");
205
+ expect(cleaned).not.toContain("1.0.0");
206
+ expect(cleaned).toContain("com.sino.topsdk:google:9.9.9");
207
+ const fbLines = cleaned.split("\n").filter(
208
+ (l) => extractImplementationKey(l, cleaned, "com.sino.topsdk") === "com.sino.topsdk:facebook"
209
+ );
210
+ expect(fbLines.length).toBe(0);
211
+ });
212
+
213
+ it("updates repositories by key instead of skipping or duplicating", () => {
214
+ const blockIn = `
215
+ google()
216
+ maven { url 'https://storage-sdk-gameplus.meetsocial.com/repository/TopSdk/' }
217
+ `;
218
+ const out = mergeRepositoriesInBlock(blockIn, [
219
+ "google",
220
+ "mavenCentral",
221
+ "https://storage-sdk-gameplus.meetsocial.com/repository/TopSdk/",
222
+ ]);
223
+ expect((out.match(/\bgoogle\s*\(\s*\)/g) ?? []).length).toBe(1);
224
+ expect(out).toContain("mavenCentral()");
225
+ expect((out.match(/storage-sdk-gameplus/g) ?? []).length).toBe(1);
226
+ expect(out).toContain(TOPSDK_REPO_AUTO_START);
227
+ });
228
+
229
+ it("merges power-raid style pre-existing resValue and dependencies without duplicates", () => {
230
+ const modIn = `
231
+ apply plugin: 'com.android.application'
232
+
233
+ dependencies {
234
+ implementation 'androidx.multidex:multidex:2.0.1'
235
+ def topsdk_version = "1.6.1.1"
236
+ def groupId = "com.sino.topsdk"
237
+ implementation "$groupId:facebook:$topsdk_version"
238
+ }
239
+
240
+ android {
241
+ defaultConfig {
242
+ applicationId 'com.feishu.topsdk'
243
+ resValue('string', 'facebook_app_id', 'old-fb-id')
244
+ resValue('string', 'af_dev_key', 'old-af-key')
245
+ }
246
+ }
247
+ `;
248
+ const raw = JSON.parse(fs.readFileSync(sampleConfigPath, "utf8")) as unknown;
249
+ const cfg = tryParseAsMeetSdkRemoteConfig(raw);
250
+ const resolved = applyMeetSdkDefaultConfig(cfg!, loadBuiltInMeetSdkDefaultConfig());
251
+ const modOut = updateModuleBuildGradleMeetSdkRemote(modIn, resolved, collectMeetSdkRemoteApplyPlugins(resolved));
252
+ expect(modOut.ok).toBe(true);
253
+ if (!modOut.ok) return;
254
+ expect((modOut.content.match(/facebook_app_id/g) ?? []).length).toBe(1);
255
+ expect((modOut.content.match(/af_dev_key/g) ?? []).length).toBe(1);
256
+ expect(modOut.content).not.toContain("old-fb-id");
257
+ const fbImpl = modOut.content
258
+ .split("\n")
259
+ .filter(
260
+ (l) => extractImplementationKey(l, modOut.content, resolved.topsdk.groupId) === "com.sino.topsdk:facebook"
261
+ );
262
+ expect(fbImpl.length).toBe(1);
263
+ });
264
+
265
+ it("detects plugins-dsl vs apply-plugin module style", () => {
266
+ expect(
267
+ detectModuleGradlePluginStyle(`
268
+ plugins {
269
+ alias(libs.plugins.android.application)
270
+ }
271
+ android { defaultConfig { applicationId "com.example.app" } }
272
+ dependencies { }
273
+ `)
274
+ ).toBe("plugins-dsl");
275
+ expect(
276
+ detectModuleGradlePluginStyle(`
277
+ apply plugin: 'com.android.application'
278
+ android { defaultConfig { applicationId "com.example.app" } }
279
+ dependencies { }
280
+ `)
281
+ ).toBe("apply-plugin");
282
+ });
283
+
284
+ it("writes Firebase google-services via plugins DSL for modern templates", () => {
285
+ const raw = JSON.parse(fs.readFileSync(sampleConfigPath, "utf8")) as unknown;
286
+ const cfg = tryParseAsMeetSdkRemoteConfig(raw);
287
+ const resolved = applyMeetSdkDefaultConfig(cfg!, loadBuiltInMeetSdkDefaultConfig());
288
+
289
+ const rootIn = `
290
+ plugins {
291
+ alias(libs.plugins.android.application) apply false
292
+ alias(libs.plugins.kotlin.compose) apply false
293
+ }
294
+ `;
295
+ const rootOut = updateRootBuildGradleMeetSdkRemote(rootIn, {
296
+ repositories: [],
297
+ rootPluginsDsl: collectMeetSdkRemotePluginsDslForRoot(resolved),
298
+ });
299
+ expect(rootOut.ok).toBe(true);
300
+ if (!rootOut.ok) return;
301
+ expect(rootOut.content).toContain(TOPSDK_PLUGIN_AUTO_START);
302
+ expect(rootOut.content).toContain("id 'com.google.gms.google-services' version '4.4.4' apply false");
303
+ expect(rootOut.content).not.toContain("buildscript");
304
+
305
+ const modIn = `
306
+ plugins {
307
+ alias(libs.plugins.android.application)
308
+ alias(libs.plugins.kotlin.compose)
309
+ }
310
+
311
+ android {
312
+ defaultConfig {
313
+ applicationId "com.example.app"
314
+ }
315
+ }
316
+
317
+ dependencies {
318
+ }
319
+ `;
320
+ const modOut = updateModuleBuildGradleMeetSdkRemote(modIn, resolved, {
321
+ style: "plugins-dsl",
322
+ pluginsDsl: collectMeetSdkRemotePluginsDslForModule(resolved),
323
+ });
324
+ expect(modOut.ok).toBe(true);
325
+ if (!modOut.ok) return;
326
+ expect(extractPluginsDslId(" id 'com.google.gms.google-services'")).toBe("com.google.gms.google-services");
327
+ expect(modOut.content).toContain("id 'com.google.gms.google-services'");
328
+ expect(modOut.content).not.toContain("apply plugin: 'com.google.gms.google-services'");
329
+ const pluginLines = modOut.content
330
+ .split("\n")
331
+ .filter((l) => l.includes("com.google.gms.google-services"));
332
+ expect(pluginLines.length).toBe(1);
333
+ const pluginsClose = modOut.content.indexOf("}", modOut.content.indexOf("plugins {"));
334
+ const androidIdx = modOut.content.indexOf("android {");
335
+ const googleIdx = modOut.content.indexOf("com.google.gms.google-services");
336
+ expect(googleIdx).toBeGreaterThan(modOut.content.indexOf("plugins {"));
337
+ expect(googleIdx).toBeLessThan(pluginsClose);
338
+ expect(androidIdx).toBeGreaterThan(pluginsClose);
339
+ });
340
+
341
+ it("migrates mixed Firebase config from buildscript/apply plugin to plugins DSL", () => {
342
+ const raw = JSON.parse(fs.readFileSync(sampleConfigPath, "utf8")) as unknown;
343
+ const cfg = tryParseAsMeetSdkRemoteConfig(raw);
344
+ const resolved = applyMeetSdkDefaultConfig(cfg!, loadBuiltInMeetSdkDefaultConfig());
345
+
346
+ const rootIn = `
347
+ buildscript {
348
+ repositories { google() }
349
+ dependencies {
350
+ classpath 'com.google.gms:google-services:4.3.0'
351
+ }
352
+ }
353
+ plugins {
354
+ alias(libs.plugins.android.application) apply false
355
+ }
356
+ `;
357
+ const rootOut = updateRootBuildGradleMeetSdkRemote(rootIn, {
358
+ repositories: [],
359
+ rootPluginsDsl: collectMeetSdkRemotePluginsDslForRoot(resolved),
360
+ stripBuildscriptClasspaths: collectMeetSdkRemoteBuildscriptClasspaths(resolved),
361
+ });
362
+ expect(rootOut.ok).toBe(true);
363
+ if (!rootOut.ok) return;
364
+ expect(rootOut.content).toContain("id 'com.google.gms.google-services' version '4.4.4' apply false");
365
+ expect(rootOut.content).not.toContain("4.3.0");
366
+
367
+ const modIn = `
368
+ plugins {
369
+ alias(libs.plugins.android.application)
370
+ }
371
+ apply plugin: 'com.google.gms.google-services'
372
+
373
+ android {
374
+ defaultConfig { applicationId "com.example.app" }
375
+ }
376
+ dependencies { }
377
+ `;
378
+ const modOut = updateModuleBuildGradleMeetSdkRemote(modIn, resolved, {
379
+ style: "plugins-dsl",
380
+ pluginsDsl: collectMeetSdkRemotePluginsDslForModule(resolved),
381
+ });
382
+ expect(modOut.ok).toBe(true);
383
+ if (!modOut.ok) return;
384
+ expect(modOut.content).not.toContain("apply plugin: 'com.google.gms.google-services'");
385
+ expect(modOut.content).toContain("id 'com.google.gms.google-services'");
386
+ });
387
+
388
+ it("writes repositories into dependencyResolutionManagement in settings.gradle", () => {
389
+ const settingsIn = `
390
+ pluginManagement {
391
+ repositories {
392
+ google()
393
+ mavenCentral()
394
+ }
395
+ }
396
+ dependencyResolutionManagement {
397
+ repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
398
+ repositories {
399
+ google()
400
+ mavenCentral()
401
+ }
402
+ }
403
+ `;
404
+ const settingsOut = updateSettingsGradleMeetSdkRemote(settingsIn, [
405
+ "google",
406
+ "mavenCentral",
407
+ "https://storage-sdk-gameplus.meetsocial.com/repository/TopSdk/",
408
+ ]);
409
+ expect(settingsOut.ok).toBe(true);
410
+ if (!settingsOut.ok) return;
411
+ expect(settingsOut.content).toContain(TOPSDK_REPO_AUTO_START);
412
+ expect(settingsOut.content).toContain("storage-sdk-gameplus");
413
+ });
414
+ });
@@ -0,0 +1,96 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
5
+ import { loadManifestFile } from "../src/config/loadManifest.js";
6
+ import { buildWorkspaceContext } from "../src/core/workspace.js";
7
+ import { runPipeline } from "../src/core/pipeline.js";
8
+
9
+ const here = path.dirname(fileURLToPath(import.meta.url));
10
+ const pkgRoot = path.resolve(here, "..");
11
+ const androidFixtureRoot = path.join(pkgRoot, "fixtures", "android-test-project");
12
+ const androidLatestRoot = path.join(androidFixtureRoot, "android-latest-project");
13
+ const recipeFixtureRoot = path.join(pkgRoot, "fixtures", "recipes");
14
+
15
+ function stubSdkHomeVersion(): void {
16
+ vi.stubGlobal(
17
+ "fetch",
18
+ vi.fn(async () => ({
19
+ ok: true,
20
+ status: 200,
21
+ text: async () =>
22
+ JSON.stringify({
23
+ code: 200,
24
+ data: {
25
+ result: {
26
+ android: { ver: "1.6.1.3", date: "2026-05-25" },
27
+ ios: { ver: "1.6.0.5", date: "2026-06-09" },
28
+ },
29
+ },
30
+ }),
31
+ })) as unknown as typeof fetch
32
+ );
33
+ }
34
+
35
+ function testProjectRoot(...names: string[]): string {
36
+ for (const name of names) {
37
+ const candidate = path.join(androidFixtureRoot, name);
38
+ if (fs.existsSync(candidate)) return candidate;
39
+ }
40
+ return path.join(androidFixtureRoot, names[0] ?? "");
41
+ }
42
+
43
+ async function runAndroidPipelineDryRun(projectRoot: string, opts?: { requirePackageNameInPatch?: boolean }) {
44
+ const manifest = loadManifestFile(path.join(recipeFixtureRoot, "android-default.fixture.yaml"));
45
+ const cfg = JSON.parse(fs.readFileSync(path.join(projectRoot, "meetsdk-remote-config.json"), "utf8")) as {
46
+ packageName: string;
47
+ channel: string;
48
+ topsdk: { appId: string };
49
+ };
50
+ const ctx = buildWorkspaceContext(projectRoot, pkgRoot);
51
+ expect(ctx.android?.ok).toBe(true);
52
+ const { report, patch, binaryCopies } = await runPipeline(ctx, manifest, { dryRun: true });
53
+ expect(report.errors).toEqual([]);
54
+ expect(patch.length).toBeGreaterThan(0);
55
+ expect(binaryCopies.length).toBe(1);
56
+ expect(binaryCopies[0].relTo).toBe("vendor/meet-integrate/sample.txt");
57
+ expect(patch).toContain("TOPSDK REPO AUTO");
58
+ expect(patch).toContain("TOPSDK AUTO");
59
+ if (opts?.requirePackageNameInPatch !== false) {
60
+ expect(patch).toContain(cfg.packageName);
61
+ }
62
+ expect(patch).toContain(cfg.topsdk.appId);
63
+ expect(patch).toContain(cfg.channel);
64
+ expect(patch).toContain("facebook_app_id");
65
+ expect(patch).toContain("top_app_id");
66
+ return { ctx, patch };
67
+ }
68
+
69
+ describe("pipeline android fixture", () => {
70
+ beforeEach(() => {
71
+ stubSdkHomeVersion();
72
+ });
73
+
74
+ afterEach(() => {
75
+ vi.unstubAllGlobals();
76
+ });
77
+
78
+ it("dry-run on android-latest-project (plugins DSL / :app)", async () => {
79
+ const { patch } = await runAndroidPipelineDryRun(androidLatestRoot);
80
+ expect(patch).toContain("twitter_redirect_url");
81
+ expect(patch).toContain("kakao_app_id");
82
+ expect(patch).toContain("id 'com.google.gms.google-services'");
83
+ });
84
+
85
+ const powerRaidRoot = testProjectRoot("power-raid");
86
+ const hasPowerRaidConfig = fs.existsSync(path.join(powerRaidRoot, "meetsdk-remote-config.json"));
87
+
88
+ it.skipIf(!hasPowerRaidConfig)("dry-run on power-raid (:launcher)", async () => {
89
+ const { ctx, patch } = await runAndroidPipelineDryRun(powerRaidRoot, { requirePackageNameInPatch: false });
90
+ expect(ctx.android?.ok && ctx.android.moduleName).toBe(":launcher");
91
+ expect(patch).toContain("launcher/build.gradle");
92
+ const addedLines = patch.split("\n").filter((line) => line.startsWith("+")).join("\n");
93
+ expect(addedLines).not.toContain("//Firebase");
94
+ expect(addedLines).not.toContain("//Appsflyer");
95
+ });
96
+ });
@@ -0,0 +1,58 @@
1
+ import path from "node:path";
2
+ import { fileURLToPath } from "node:url";
3
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
4
+ import { loadAndroidIntegrationFile } from "../src/config/loadAndroidIntegration.js";
5
+ import { buildWorkspaceContext } from "../src/core/workspace.js";
6
+ import { runPipeline } from "../src/core/pipeline.js";
7
+ import type { AndroidIntegrationDocument, Manifest } from "../src/contracts/types.js";
8
+
9
+ const here = path.dirname(fileURLToPath(import.meta.url));
10
+ const pkgRoot = path.resolve(here, "..");
11
+ const recipeFixtureRoot = path.join(pkgRoot, "fixtures", "recipes");
12
+
13
+ function stubSdkHomeVersion(): void {
14
+ vi.stubGlobal(
15
+ "fetch",
16
+ vi.fn(async () => ({
17
+ ok: true,
18
+ status: 200,
19
+ text: async () =>
20
+ JSON.stringify({
21
+ code: 200,
22
+ data: {
23
+ result: {
24
+ android: { ver: "1.6.1.3", date: "2026-05-25" },
25
+ ios: { ver: "1.6.0.5", date: "2026-06-09" },
26
+ },
27
+ },
28
+ }),
29
+ })) as unknown as typeof fetch
30
+ );
31
+ }
32
+
33
+ function toManifest(doc: AndroidIntegrationDocument): Manifest {
34
+ const { meta: _m, remote: _r, ...rest } = doc;
35
+ void _m;
36
+ void _r;
37
+ return rest;
38
+ }
39
+
40
+ describe("pipeline integration json", () => {
41
+ beforeEach(() => {
42
+ stubSdkHomeVersion();
43
+ });
44
+
45
+ afterEach(() => {
46
+ vi.unstubAllGlobals();
47
+ });
48
+
49
+ it("dry-run accepts assembled android integration file", async () => {
50
+ const doc = loadAndroidIntegrationFile(path.join(recipeFixtureRoot, "android-integration.fixture.json"));
51
+ const manifest = toManifest(doc);
52
+ const ctx = buildWorkspaceContext(path.join(pkgRoot, "fixtures", "android-test-project", "android-latest-project"), pkgRoot);
53
+ const { report, patch } = await runPipeline(ctx, manifest, { dryRun: true });
54
+ expect(report.errors).toEqual([]);
55
+ expect(patch.length).toBeGreaterThan(0);
56
+ expect(patch).toContain("TOPSDK AUTO");
57
+ });
58
+ });