@leonxin/meetgames 0.1.11 → 0.1.13

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 (179) hide show
  1. package/README.md +9 -9
  2. package/dist/cache.d.ts +44 -0
  3. package/dist/cache.d.ts.map +1 -0
  4. package/dist/cache.js +101 -0
  5. package/dist/cache.js.map +1 -0
  6. package/dist/cli.d.ts.map +1 -1
  7. package/dist/cli.js +43 -37
  8. package/dist/cli.js.map +1 -1
  9. package/dist/config/meetSdkDefaultConfig.d.ts +1 -1
  10. package/dist/config/meetSdkDefaultConfig.d.ts.map +1 -1
  11. package/dist/config/meetSdkDefaultConfig.js +4 -3
  12. package/dist/config/meetSdkDefaultConfig.js.map +1 -1
  13. package/dist/config/meetSdkIosConfig.d.ts.map +1 -1
  14. package/dist/config/meetSdkIosConfig.js +3 -1
  15. package/dist/config/meetSdkIosConfig.js.map +1 -1
  16. package/dist/config/meetSdkRemoteConfig.d.ts +1 -0
  17. package/dist/config/meetSdkRemoteConfig.d.ts.map +1 -1
  18. package/dist/config/meetSdkRemoteConfig.js +4 -1
  19. package/dist/config/meetSdkRemoteConfig.js.map +1 -1
  20. package/dist/contracts/types.d.ts +11 -0
  21. package/dist/contracts/types.d.ts.map +1 -1
  22. package/dist/core/doctor.js +7 -7
  23. package/dist/core/doctor.js.map +1 -1
  24. package/dist/core/pipeline.d.ts.map +1 -1
  25. package/dist/core/pipeline.js +3 -0
  26. package/dist/core/pipeline.js.map +1 -1
  27. package/dist/core/previewPatches.d.ts +1 -1
  28. package/dist/core/previewPatches.js +2 -2
  29. package/dist/core/previewPatches.js.map +1 -1
  30. package/dist/core/reporter.d.ts.map +1 -1
  31. package/dist/core/reporter.js +4 -0
  32. package/dist/core/reporter.js.map +1 -1
  33. package/dist/core/workspace.d.ts.map +1 -1
  34. package/dist/core/workspace.js +2 -0
  35. package/dist/core/workspace.js.map +1 -1
  36. package/dist/index.d.ts +1 -0
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +1 -0
  39. package/dist/index.js.map +1 -1
  40. package/dist/ios/channelConfig.js +1 -1
  41. package/dist/ios/channelConfig.js.map +1 -1
  42. package/dist/ios/codeUtils.d.ts +1 -0
  43. package/dist/ios/codeUtils.d.ts.map +1 -1
  44. package/dist/ios/codeUtils.js +3 -0
  45. package/dist/ios/codeUtils.js.map +1 -1
  46. package/dist/ios/integrate.d.ts.map +1 -1
  47. package/dist/ios/integrate.js +97 -23
  48. package/dist/ios/integrate.js.map +1 -1
  49. package/dist/ios/pbxprojEditor.d.ts.map +1 -1
  50. package/dist/ios/pbxprojEditor.js +123 -6
  51. package/dist/ios/pbxprojEditor.js.map +1 -1
  52. package/dist/ios/sdkBundle.d.ts +1 -6
  53. package/dist/ios/sdkBundle.d.ts.map +1 -1
  54. package/dist/ios/sdkBundle.js +44 -16
  55. package/dist/ios/sdkBundle.js.map +1 -1
  56. package/dist/mcp/server.d.ts.map +1 -1
  57. package/dist/mcp/server.js +12 -6
  58. package/dist/mcp/server.js.map +1 -1
  59. package/dist/mcp/service.d.ts +3 -0
  60. package/dist/mcp/service.d.ts.map +1 -1
  61. package/dist/mcp/service.js +31 -8
  62. package/dist/mcp/service.js.map +1 -1
  63. package/dist/ops/handlers.d.ts.map +1 -1
  64. package/dist/ops/handlers.js +24 -19
  65. package/dist/ops/handlers.js.map +1 -1
  66. package/dist/remote/sdkHomeDownload.d.ts +1 -1
  67. package/dist/remote/sdkHomeDownload.d.ts.map +1 -1
  68. package/dist/remote/sdkHomeDownload.js +27 -6
  69. package/dist/remote/sdkHomeDownload.js.map +1 -1
  70. package/docs/API.md +16 -16
  71. package/docs/CLI.md +21 -22
  72. package/docs/INTEGRATION.md +30 -11
  73. package/docs/MCP.md +1 -1
  74. package/docs/README.md +0 -1
  75. package/docs/archive/api/downloadSDKConfig.md +2 -2
  76. package/docs/archive/api/getChannelConfig-meetgames.md +1 -1
  77. package/docs/archive/product//351/234/200/346/261/202/346/226/207/346/241/243.md +1 -1
  78. package/package.json +2 -5
  79. package/recipes/android-default.yaml +0 -5
  80. package/recipes/integrate-default.yaml +0 -5
  81. package/src/cache.ts +164 -0
  82. package/src/cli.ts +46 -38
  83. package/src/config/meetSdkDefaultConfig.ts +4 -3
  84. package/src/config/meetSdkIosConfig.ts +3 -1
  85. package/src/config/meetSdkRemoteConfig.ts +5 -1
  86. package/src/contracts/types.ts +11 -0
  87. package/src/core/doctor.ts +7 -7
  88. package/src/core/pipeline.ts +3 -0
  89. package/src/core/previewPatches.ts +2 -2
  90. package/src/core/reporter.ts +3 -0
  91. package/src/core/workspace.ts +2 -0
  92. package/src/index.ts +7 -0
  93. package/src/ios/channelConfig.ts +1 -1
  94. package/src/ios/codeUtils.ts +4 -0
  95. package/src/ios/integrate.ts +110 -27
  96. package/src/ios/pbxprojEditor.ts +121 -4
  97. package/src/ios/sdkBundle.ts +41 -18
  98. package/src/mcp/server.ts +12 -6
  99. package/src/mcp/service.ts +39 -8
  100. package/src/ops/handlers.ts +23 -19
  101. package/src/remote/sdkHomeDownload.ts +28 -7
  102. package/tests/doctor.test.ts +4 -2
  103. package/tests/{test-projects-hosts.test.ts → fixtures-hosts.test.ts} +2 -2
  104. package/tests/ios.sdkBundle.test.ts +10 -5
  105. package/tests/mcp.e2e.ts +2 -5
  106. package/tests/meetSdkRemoteConfig.test.ts +25 -0
  107. package/tests/pipeline.android.test.ts +1 -2
  108. package/tests/pipeline.ios.test.ts +133 -20
  109. package/tests/pipeline.preview.patch.test.ts +2 -2
  110. package/tests/sdkVersionConfig.test.ts +3 -2
  111. package/dist/aab-converter/aab-entry.d.ts +0 -3
  112. package/dist/aab-converter/aab-entry.d.ts.map +0 -1
  113. package/dist/aab-converter/aab-entry.js +0 -49
  114. package/dist/aab-converter/aab-entry.js.map +0 -1
  115. package/dist/aab-converter/apksExtractor.d.ts +0 -2
  116. package/dist/aab-converter/apksExtractor.d.ts.map +0 -1
  117. package/dist/aab-converter/apksExtractor.js +0 -108
  118. package/dist/aab-converter/apksExtractor.js.map +0 -1
  119. package/dist/aab-converter/bundletoolRunner.d.ts +0 -15
  120. package/dist/aab-converter/bundletoolRunner.d.ts.map +0 -1
  121. package/dist/aab-converter/bundletoolRunner.js +0 -46
  122. package/dist/aab-converter/bundletoolRunner.js.map +0 -1
  123. package/dist/aab-converter/cliArgs.d.ts +0 -27
  124. package/dist/aab-converter/cliArgs.d.ts.map +0 -1
  125. package/dist/aab-converter/cliArgs.js +0 -170
  126. package/dist/aab-converter/cliArgs.js.map +0 -1
  127. package/dist/aab-converter/convertAabToApk.d.ts +0 -7
  128. package/dist/aab-converter/convertAabToApk.d.ts.map +0 -1
  129. package/dist/aab-converter/convertAabToApk.js +0 -69
  130. package/dist/aab-converter/convertAabToApk.js.map +0 -1
  131. package/dist/aab-converter/resourcePaths.d.ts +0 -4
  132. package/dist/aab-converter/resourcePaths.d.ts.map +0 -1
  133. package/dist/aab-converter/resourcePaths.js +0 -42
  134. package/dist/aab-converter/resourcePaths.js.map +0 -1
  135. package/dist/aab-converter/signingOptions.d.ts +0 -9
  136. package/dist/aab-converter/signingOptions.d.ts.map +0 -1
  137. package/dist/aab-converter/signingOptions.js +0 -21
  138. package/dist/aab-converter/signingOptions.js.map +0 -1
  139. package/dist/aab-converter/types.d.ts +0 -24
  140. package/dist/aab-converter/types.d.ts.map +0 -1
  141. package/dist/aab-converter/types.js +0 -2
  142. package/dist/aab-converter/types.js.map +0 -1
  143. package/dist/shared/fileUtils.d.ts +0 -5
  144. package/dist/shared/fileUtils.d.ts.map +0 -1
  145. package/dist/shared/fileUtils.js +0 -35
  146. package/dist/shared/fileUtils.js.map +0 -1
  147. package/dist/shared/logger.d.ts +0 -10
  148. package/dist/shared/logger.d.ts.map +0 -1
  149. package/dist/shared/logger.js +0 -37
  150. package/dist/shared/logger.js.map +0 -1
  151. package/dist/shared/pathUtils.d.ts +0 -4
  152. package/dist/shared/pathUtils.d.ts.map +0 -1
  153. package/dist/shared/pathUtils.js +0 -22
  154. package/dist/shared/pathUtils.js.map +0 -1
  155. package/dist/shared/processRunner.d.ts +0 -12
  156. package/dist/shared/processRunner.d.ts.map +0 -1
  157. package/dist/shared/processRunner.js +0 -31
  158. package/dist/shared/processRunner.js.map +0 -1
  159. package/docs/AAB_CONVERTER_CLI_PLAN.md +0 -392
  160. package/logs/convert-20260622-155037.log +0 -5
  161. package/logs/convert-20260622-155226.log +0 -6
  162. package/scripts/package-aab-cli-win.mjs +0 -193
  163. package/src/aab-converter/aab-entry.ts +0 -48
  164. package/src/aab-converter/apksExtractor.ts +0 -119
  165. package/src/aab-converter/bundletoolRunner.ts +0 -63
  166. package/src/aab-converter/cliArgs.ts +0 -194
  167. package/src/aab-converter/convertAabToApk.ts +0 -81
  168. package/src/aab-converter/resourcePaths.ts +0 -43
  169. package/src/aab-converter/signingOptions.ts +0 -29
  170. package/src/aab-converter/types.ts +0 -26
  171. package/src/shared/fileUtils.ts +0 -41
  172. package/src/shared/logger.ts +0 -49
  173. package/src/shared/pathUtils.ts +0 -24
  174. package/src/shared/processRunner.ts +0 -43
  175. package/test-projects/README.md +0 -51
  176. package/test-projects/_preview/pipeline.patch +0 -281
  177. package/tests/aab-converter.test.ts +0 -213
  178. /package/{meetsdk-android.json → config/meetsdk-android.json} +0 -0
  179. /package/{meetsdk-ios.json → config/meetsdk-ios.json} +0 -0
@@ -22,6 +22,8 @@ export function buildWorkspaceContext(
22
22
  projectRoot: root,
23
23
  packageRoot: pkg,
24
24
  sdkHomeApiBaseUrl: options.sdkHomeApiBaseUrl,
25
+ remoteConfigPath: options.remoteConfigPath,
26
+ iosSdkRoot: options.iosSdkRoot,
25
27
  android: detectAndroid(root, { appTarget: options.appTarget }),
26
28
  ios: detectIOS(root, { appTarget: options.appTarget }),
27
29
  iosReserved: iosToolingReserved,
package/src/index.ts CHANGED
@@ -80,6 +80,13 @@ export {
80
80
  isTopSdkFeatureModuleEnabled,
81
81
  } from "./config/topsdkFeatureModules.js";
82
82
  export { clearPreviewPatchFilesIfTargetInside, resolveDefaultPreviewPatchDir } from "./core/previewPatches.js";
83
+ export {
84
+ MEET_SDK_TOOL_CACHE_ROOT,
85
+ ensureCacheRoot,
86
+ resolveIosSdkCacheLayout,
87
+ resolveRemoteConfigCachePath,
88
+ writeRemoteConfigCache,
89
+ } from "./cache.js";
83
90
  export { buildWorkspaceContext, resolvePackageRoot } from "./core/workspace.js";
84
91
  export { runPipeline } from "./core/pipeline.js";
85
92
  export { generatePatchForFile } from "./core/patch.js";
@@ -101,7 +101,7 @@ export const IOS_PLUGIN_FOLDER_BY_SUBKEY: Record<string, string> = {
101
101
  };
102
102
 
103
103
  export function enabledIosPluginFolders(config: MeetSdkRemoteConfig): string[] {
104
- const folders: string[] = [];
104
+ const folders: string[] = ["UI"];
105
105
  for (const [scope, bucket] of Object.entries(config.sdkModules)) {
106
106
  if (!bucket || typeof bucket !== "object") continue;
107
107
  const scopeRecord = bucket as Record<string, unknown>;
@@ -19,6 +19,10 @@ export class CodeUtils {
19
19
  return norm(this.content).includes(norm(code));
20
20
  }
21
21
 
22
+ hasCode(code: string): boolean {
23
+ return this.checkExisted(code);
24
+ }
25
+
22
26
  addHeader(code: string): boolean {
23
27
  if (this.checkExisted(code)) return true;
24
28
  this.content = `${code}\n${this.content}`;
@@ -50,12 +50,12 @@ export interface IosIntegrateOptions {
50
50
 
51
51
  const IOS_FIREBASE_CONFIG_FILE = "GoogleService-Info.plist";
52
52
 
53
- function ok(changed: string[], warnings: string[] = []): StepResult {
54
- return { ok: true, changedFiles: changed, warnings, errors: [] };
53
+ function ok(changed: string[], warnings: string[] = [], logs: string[] = []): StepResult {
54
+ return { ok: true, changedFiles: changed, logs, warnings, errors: [] };
55
55
  }
56
56
 
57
- function fail(errors: string[], warnings: string[] = []): StepResult {
58
- return { ok: false, changedFiles: [], warnings, errors };
57
+ function fail(errors: string[], warnings: string[] = [], logs: string[] = []): StepResult {
58
+ return { ok: false, changedFiles: [], logs, warnings, errors };
59
59
  }
60
60
 
61
61
  function applyPlugin(
@@ -66,15 +66,19 @@ function applyPlugin(
66
66
  perform: readonly PerformSetting[],
67
67
  binaryCopies: BinaryCopy[],
68
68
  dryRun: boolean,
69
+ logs: string[],
69
70
  remoteSources: Map<string, string> = new Map()
70
71
  ): void {
71
72
  const { config, sourceDir } = loaded;
72
73
  const srcRoot = pbx.srcRoot;
73
74
 
75
+ logs.push(`- 插件:${config.name}, 类型:${config.type},版本:${config.pluginVersion}`);
76
+
74
77
  if (perform.includes("frameworks") && config.frameworks?.length) {
75
78
  for (const fw of config.frameworks) {
76
79
  if (fw.system) {
77
80
  addSystemFramework(pbx, fw.name);
81
+ logs.push(`SUCCESS: 导入系统依赖framework ${fw.name} 完成`);
78
82
  continue;
79
83
  }
80
84
  const src = path.join(sourceDir, fw.name);
@@ -86,6 +90,7 @@ function applyPlugin(
86
90
  fm.copyFramework(src);
87
91
  }
88
92
  addThirdPartyFramework(pbx, relTo, Boolean(fw.embed));
93
+ logs.push(`SUCCESS: 导入三方依赖framework ${fw.name} 完成`);
89
94
  if (fw.copyFile) addCopyFile(pbx, relTo);
90
95
  }
91
96
  }
@@ -94,6 +99,7 @@ function applyPlugin(
94
99
  for (const lib of config.libs) {
95
100
  if (lib.system) {
96
101
  addSystemLib(pbx, lib.name);
102
+ logs.push(`SUCCESS: 导入系统依赖lib ${lib.name} 完成`);
97
103
  continue;
98
104
  }
99
105
  const src = path.join(sourceDir, lib.name);
@@ -102,6 +108,7 @@ function applyPlugin(
102
108
  if (dryRun) binaryCopies.push({ fromAbs: src, relTo });
103
109
  else fm.copyFile(src);
104
110
  addSourceOrResourceFile(pbx, relTo);
111
+ logs.push(`SUCCESS: 导入三方依赖lib ${lib.name} 完成`);
105
112
  }
106
113
  }
107
114
 
@@ -110,6 +117,7 @@ function applyPlugin(
110
117
  const remoteRel = remoteSources.get(`${config.name}:${source}`);
111
118
  if (remoteRel) {
112
119
  addSourceOrResourceFile(pbx, remoteRel);
120
+ logs.push(`SUCCESS: 引入资源 ${source} 完成`);
113
121
  continue;
114
122
  }
115
123
  const src = path.join(sourceDir, source);
@@ -126,15 +134,17 @@ function applyPlugin(
126
134
  for (const name of fs.readdirSync(dir)) {
127
135
  const full = path.join(dir, name);
128
136
  if (fs.statSync(full).isDirectory()) {
129
- if (name.endsWith(".bundle")) {
130
- addSourceOrResourceFile(pbx, fm.relFromSrc(full));
131
- } else {
132
- walk(full);
133
- }
137
+ if (name.endsWith(".bundle")) {
138
+ addSourceOrResourceFile(pbx, fm.relFromSrc(full));
139
+ logs.push(`SUCCESS: 引入资源 ${name} 完成`);
140
+ } else {
141
+ walk(full);
142
+ }
134
143
  continue;
135
144
  }
136
145
  if (/\.(h|m|mm|plist|a)$/.test(name)) {
137
146
  addSourceOrResourceFile(pbx, fm.relFromSrc(full));
147
+ logs.push(`SUCCESS: 引入资源 ${name} 完成`);
138
148
  }
139
149
  }
140
150
  };
@@ -146,6 +156,7 @@ function applyPlugin(
146
156
  else if (fs.statSync(src).isDirectory()) fm.copyDir(src);
147
157
  else fm.copyFile(src);
148
158
  addSourceOrResourceFile(pbx, relTo);
159
+ logs.push(`SUCCESS: 引入资源 ${source} 完成`);
149
160
  }
150
161
  }
151
162
  }
@@ -153,15 +164,18 @@ function applyPlugin(
153
164
  if (perform.includes("buildSettings") && config.buildSettings) {
154
165
  for (const [k, v] of Object.entries(config.buildSettings)) {
155
166
  setBuildSetting(pbx, k, v);
167
+ logs.push(`SUCCESS: BuildSetting设置${k}为${v} 完成`);
156
168
  }
157
169
  }
158
170
 
159
171
  if (perform.includes("OtherLinkerFlags") && config.OtherLinkerFlags?.length) {
160
172
  for (const flag of config.OtherLinkerFlags) {
161
173
  addOtherLinkerFlag(pbx, flag);
174
+ logs.push(`SUCCESS: 添加other link flag:${flag} 完成`);
162
175
  }
163
176
  }
164
177
 
178
+ logs.push(`SUCCESS: 插件${config.name}接入完成`);
165
179
  }
166
180
 
167
181
  function validateLoadedPluginResourcesForIos(loaded: LoadedPluginConfig): string[] {
@@ -224,6 +238,42 @@ function iosFirebaseConfigRelPath(plistDocs: PlistDocument[]): string {
224
238
  return dir === "." ? IOS_FIREBASE_CONFIG_FILE : path.posix.join(dir, IOS_FIREBASE_CONFIG_FILE);
225
239
  }
226
240
 
241
+ function hasSwiftSource(srcRoot: string): boolean {
242
+ const walk = (dir: string): boolean => {
243
+ let entries: fs.Dirent[];
244
+ try {
245
+ entries = fs.readdirSync(dir, { withFileTypes: true });
246
+ } catch {
247
+ return false;
248
+ }
249
+ for (const entry of entries) {
250
+ if (entry.name.startsWith(".")) continue;
251
+ const full = path.join(dir, entry.name);
252
+ if (entry.isDirectory()) {
253
+ if (!["Pods", "build", "DerivedData", "topSDK"].includes(entry.name) && walk(full)) return true;
254
+ continue;
255
+ }
256
+ if (entry.name.endsWith(".swift")) return true;
257
+ }
258
+ return false;
259
+ };
260
+ return walk(srcRoot);
261
+ }
262
+
263
+ function ensureTopSdkInstallSwift(
264
+ store: TextFileStore,
265
+ ctx: WorkspaceContext,
266
+ pbx: PbxContext
267
+ ): string | null {
268
+ if (hasSwiftSource(pbx.srcRoot)) return null;
269
+ const relFromSrc = path.join(pbx.targetName, "TopSDKInstall.swift").split(path.sep).join("/");
270
+ const abs = path.join(pbx.srcRoot, relFromSrc);
271
+ const relFromProject = path.relative(ctx.projectRoot, abs).split(path.sep).join("/");
272
+ store.write(relFromProject, "import Foundation\n\n");
273
+ addSourceOrResourceFile(pbx, relFromSrc);
274
+ return relFromProject;
275
+ }
276
+
227
277
  function appDelegateCodeShouldMoveToSceneDelegate(code: CodeConfig): boolean {
228
278
  if (code.type === "header") return false;
229
279
  const method = code.method ?? "";
@@ -274,7 +324,8 @@ function mergePlistParams(
274
324
  docs: PlistDocument[],
275
325
  loaded: LoadedPluginConfig,
276
326
  channelConfig: Record<string, unknown>,
277
- perform: readonly PerformSetting[]
327
+ perform: readonly PerformSetting[],
328
+ logs: string[]
278
329
  ): void {
279
330
  if (!perform.includes("infoParams") && !perform.includes("urlScheme") && !perform.includes("queriesSchemes")) {
280
331
  return;
@@ -282,18 +333,22 @@ function mergePlistParams(
282
333
  for (const doc of docs) {
283
334
  if (perform.includes("infoParams") && loaded.config.infoParams) {
284
335
  for (const [key, raw] of Object.entries(loaded.config.infoParams)) {
285
- addPlistParam(doc.data, key, applyChannelTemplateValue(raw, channelConfig));
336
+ const value = applyChannelTemplateValue(raw, channelConfig);
337
+ addPlistParam(doc.data, key, value);
338
+ logs.push(`SUCCESS: info.plist添加配置key:${key} value:${String(value)} 完成`);
286
339
  }
287
340
  }
288
341
  if (perform.includes("queriesSchemes") && loaded.config.queriesSchemes?.length) {
289
342
  for (const scheme of loaded.config.queriesSchemes) {
290
343
  addQueriesScheme(doc.data, scheme);
344
+ logs.push(`SUCCESS: 添加queriesSchemes:${scheme} 完成`);
291
345
  }
292
346
  }
293
347
  if (perform.includes("urlScheme") && loaded.config.urlScheme) {
294
348
  let scheme = applyChannelTemplate(loaded.config.urlScheme, channelConfig);
295
349
  if (scheme.endsWith("://")) scheme = scheme.replace("://", "");
296
350
  addUrlScheme(doc.data, scheme);
351
+ logs.push(`SUCCESS: 设置urlScheme:${scheme}完成`);
297
352
  }
298
353
  }
299
354
  }
@@ -331,20 +386,21 @@ export async function runIosIntegrateTopSdk(
331
386
  ): Promise<StepResult> {
332
387
  if (!ctx.ios?.ok) return fail(["iOS project not detected"]);
333
388
  const warnings: string[] = [];
389
+ const logs: string[] = [];
334
390
  const changed = new Set<string>();
335
391
 
336
- const configPath = path.join(ctx.projectRoot, MEETSDK_REMOTE_CONFIG_FILENAME);
392
+ const configPath = ctx.remoteConfigPath ?? path.join(ctx.projectRoot, MEETSDK_REMOTE_CONFIG_FILENAME);
337
393
  if (!fs.existsSync(configPath)) {
338
- return fail([`${MEETSDK_REMOTE_CONFIG_FILENAME} not found; run fetch-config or setup first`]);
394
+ return fail([`${MEETSDK_REMOTE_CONFIG_FILENAME} not found at ${configPath}; run fetch-config or setup first`], warnings, logs);
339
395
  }
340
396
  let remote: ReturnType<typeof tryParseAsMeetSdkRemoteConfig>;
341
397
  try {
342
398
  remote = tryParseAsMeetSdkRemoteConfig(JSON.parse(fs.readFileSync(configPath, "utf8")) as unknown);
343
399
  } catch (e) {
344
- return fail([e instanceof Error ? e.message : String(e)]);
400
+ return fail([e instanceof Error ? e.message : String(e)], warnings, logs);
345
401
  }
346
402
  if (!remote) {
347
- return fail([`invalid ${MEETSDK_REMOTE_CONFIG_FILENAME}`]);
403
+ return fail([`invalid ${MEETSDK_REMOTE_CONFIG_FILENAME}`], warnings, logs);
348
404
  }
349
405
  const validation = validateMeetSdkRemoteConfig(remote);
350
406
  if (!validation.ok) {
@@ -353,15 +409,17 @@ export async function runIosIntegrateTopSdk(
353
409
 
354
410
  let sdkRoot: string;
355
411
  try {
356
- sdkRoot = resolveIosSdkRoot(ctx.packageRoot);
412
+ sdkRoot = ctx.iosSdkRoot ?? resolveIosSdkRoot(ctx.packageRoot);
357
413
  } catch (e) {
358
- return fail([e instanceof Error ? e.message : String(e)]);
414
+ return fail([e instanceof Error ? e.message : String(e)], warnings, logs);
359
415
  }
360
416
 
361
417
  const targetName = options.targetName ?? ctx.ios.targetName ?? path.basename(ctx.ios.xcodeprojPath!, ".xcodeproj");
362
418
  const perform = options.performSettings ?? DEFAULT_PERFORM_SETTINGS;
363
419
  const executeAppDelegate = options.executeAppDelegate !== false;
364
420
  const dryRun = Boolean(options.dryRun);
421
+ logs.push("6、执行 SDK 接入");
422
+ logs.push(`*** 项目根目录:${ctx.projectRoot} ***`);
365
423
 
366
424
  const channelConfig = buildChannelConfigMap(remote);
367
425
  const coreConfigs = listSdkCoreConfigs(sdkRoot);
@@ -375,6 +433,7 @@ export async function runIosIntegrateTopSdk(
375
433
  if (p) pluginConfigs.push(p);
376
434
  else warnings.push(`iOS plugin folder not in SDK package: plugins/${folder}`);
377
435
  }
436
+
378
437
  const xcodeprojPath = ctx.ios.xcodeprojPath!;
379
438
  const pbx = await loadPbxFromStore(store, ctx.projectRoot, xcodeprojPath, targetName);
380
439
  const fm = new TopSdkFileManager(pbx.srcRoot);
@@ -383,30 +442,37 @@ export async function runIosIntegrateTopSdk(
383
442
  try {
384
443
  plistDocs = await ensureInfoPlists(store, ctx, pbx, targetName);
385
444
  } catch (e) {
386
- return fail([e instanceof Error ? e.message : String(e)], warnings);
445
+ return fail([e instanceof Error ? e.message : String(e)], warnings, logs);
387
446
  }
388
447
 
389
448
  const firebaseDownload = await downloadIosFirebaseConfig(remote, pbx, dryRun, iosFirebaseConfigRelPath(plistDocs));
390
449
  warnings.push(...firebaseDownload.warnings);
391
- if (firebaseDownload.errors.length) return fail(firebaseDownload.errors, warnings);
450
+ if (firebaseDownload.errors.length) return fail(firebaseDownload.errors, warnings, logs);
392
451
 
393
452
  const resourceErrors = [...coreConfigs, ...pluginConfigs].flatMap(validateLoadedPluginResourcesForIos);
394
- if (resourceErrors.length) return fail(resourceErrors, warnings);
453
+ if (resourceErrors.length) return fail(resourceErrors, warnings, logs);
395
454
 
396
455
  const missingRequiredConfigs = validateRequiredChannelConfigs(pluginConfigs, channelConfig);
397
456
  if (missingRequiredConfigs.length) {
398
- return fail([`iOS remote config missing required channel params: ${missingRequiredConfigs.join(", ")}`], warnings);
457
+ return fail([`iOS remote config missing required channel params: ${missingRequiredConfigs.join(", ")}`], warnings, logs);
399
458
  }
400
459
 
401
- for (const loaded of coreConfigs) {
402
- applyPlugin(loaded, pbx, fm, channelConfig, perform, binaryCopies, dryRun, firebaseDownload.remoteSources);
403
- }
404
460
  for (const loaded of pluginConfigs) {
405
- applyPlugin(loaded, pbx, fm, channelConfig, perform, binaryCopies, dryRun, firebaseDownload.remoteSources);
461
+ applyPlugin(loaded, pbx, fm, channelConfig, perform, binaryCopies, dryRun, logs, firebaseDownload.remoteSources);
462
+ if (loaded.config.name === "FacebookSignin" && perform.includes("sources")) {
463
+ const swiftRel = ensureTopSdkInstallSwift(store, ctx, pbx);
464
+ if (swiftRel) {
465
+ changed.add(swiftRel);
466
+ logs.push("SUCCESS: 未检测到Swift代码文件,自动添加TopSDKInstall.swift完成");
467
+ }
468
+ }
469
+ }
470
+ for (const loaded of coreConfigs) {
471
+ applyPlugin(loaded, pbx, fm, channelConfig, perform, binaryCopies, dryRun, logs, firebaseDownload.remoteSources);
406
472
  }
407
473
 
408
474
  for (const loaded of [...coreConfigs, ...pluginConfigs]) {
409
- mergePlistParams(plistDocs, loaded, channelConfig, perform);
475
+ mergePlistParams(plistDocs, loaded, channelConfig, perform, logs);
410
476
  if (perform.includes("infoParams")) {
411
477
  setAppTransportSecurity(plistDocs[0]?.data ?? {}, true);
412
478
  }
@@ -429,6 +495,7 @@ export async function runIosIntegrateTopSdk(
429
495
  for (const rel of ensureAppleSignInEntitlement(store, ctx.projectRoot, pbx)) {
430
496
  changed.add(rel);
431
497
  }
498
+ logs.push("SUCCESS: 启用SigninWithApple完成");
432
499
  }
433
500
 
434
501
  savePbxToStore(store, pbx);
@@ -453,14 +520,21 @@ export async function runIosIntegrateTopSdk(
453
520
  }
454
521
  for (const rel of delegateRelPaths.length === 1 ? delegateRelPaths : []) {
455
522
  const cu = CodeUtils.fromFile(path.join(ctx.projectRoot, rel));
523
+ logs.push("检测到工程UIApplicationDelegate实现类,将自动接入对应接口");
456
524
  let okInject = true;
457
525
  for (const loaded of [...coreConfigs, ...pluginConfigs]) {
458
526
  for (const code of appDelegateCodesForLifecycle(loaded, hasUniqueSceneDelegate)) {
527
+ const existed = cu.hasCode(code.content);
459
528
  if (code.type === "header") {
460
529
  if (!cu.addHeader(code.content)) okInject = false;
461
530
  } else if ((code.type === "method" || code.type === "code") && code.method) {
462
531
  if (!cu.addCodeToMethod(code.method, code.content, Boolean(code.addToReturn))) okInject = false;
463
532
  }
533
+ if (okInject) {
534
+ logs.push(
535
+ `SUCCESS: 代码文件${path.join(ctx.projectRoot, rel)},${existed ? "已存在代码" : "添加代码"}${code.content}${existed ? "" : "完成"}`
536
+ );
537
+ }
464
538
  }
465
539
  }
466
540
  if (!okInject) warnings.push(`AppDelegate injection incomplete for ${rel}`);
@@ -474,14 +548,21 @@ export async function runIosIntegrateTopSdk(
474
548
  }
475
549
  for (const rel of sceneDelegateRelPaths.length === 1 ? sceneDelegateRelPaths : []) {
476
550
  const cu = CodeUtils.fromFile(path.join(ctx.projectRoot, rel));
551
+ logs.push("检测到工程存在UIWindowSceneDelegate实现类,将自动接入对应接口");
477
552
  let okInject = true;
478
553
  for (const loaded of [...coreConfigs, ...pluginConfigs]) {
479
554
  for (const code of loaded.config.sceneDelegateCodes ?? []) {
555
+ const existed = cu.hasCode(code.content);
480
556
  if (code.type === "header") {
481
557
  if (!cu.addHeader(code.content)) okInject = false;
482
558
  } else if ((code.type === "method" || code.type === "code") && code.method) {
483
559
  if (!cu.addCodeToMethod(code.method, code.content, Boolean(code.addToReturn))) okInject = false;
484
560
  }
561
+ if (okInject) {
562
+ logs.push(
563
+ `SUCCESS: 代码文件${path.join(ctx.projectRoot, rel)},${existed ? "已存在代码" : "添加代码"}${code.content}${existed ? "" : "完成"}`
564
+ );
565
+ }
485
566
  }
486
567
  }
487
568
  if (!okInject) warnings.push(`SceneDelegate injection incomplete for ${rel}`);
@@ -491,7 +572,9 @@ export async function runIosIntegrateTopSdk(
491
572
  }
492
573
  }
493
574
 
494
- return ok([...changed], warnings);
575
+ logs.push("SUCCESS: info.plist配置写入完成");
576
+ logs.push("!! 接入流程已全部结束 !!");
577
+ return ok([...changed], warnings, logs);
495
578
  }
496
579
 
497
580
  async function ensureInfoPlists(
@@ -69,9 +69,70 @@ export async function loadPbxFromStore(
69
69
  }
70
70
 
71
71
  export function savePbxToStore(store: TextFileStore, ctx: PbxContext): void {
72
+ stripUndefinedValues(ctx.proj.hash?.project?.objects);
73
+ sanitizePbxBuildSettings(ctx.proj);
72
74
  store.write(ctx.rel, ctx.proj.writeSync());
73
75
  }
74
76
 
77
+ function stripUndefinedValues(value: unknown): void {
78
+ if (Array.isArray(value)) {
79
+ for (const item of value) stripUndefinedValues(item);
80
+ return;
81
+ }
82
+ if (!value || typeof value !== "object") return;
83
+
84
+ const record = value as Record<string, unknown>;
85
+ for (const [key, nested] of Object.entries(record)) {
86
+ if (nested === undefined || nested === "undefined") {
87
+ delete record[key];
88
+ continue;
89
+ }
90
+ stripUndefinedValues(nested);
91
+ }
92
+ }
93
+
94
+ function sanitizePbxBuildSettings(proj: XcodeProject): void {
95
+ const buildConfigs = proj.pbxXCBuildConfigurationSection?.() ?? {};
96
+ for (const [uuid, cfg] of Object.entries(buildConfigs)) {
97
+ if (uuid.endsWith("_comment") || !cfg || typeof cfg !== "object") continue;
98
+ const settings = (cfg as { buildSettings?: Record<string, unknown> }).buildSettings;
99
+ if (!settings) continue;
100
+ for (const [key, raw] of Object.entries(settings)) {
101
+ if (typeof raw === "string") {
102
+ settings[key] = quotePbxStringIfNeeded(raw);
103
+ continue;
104
+ }
105
+ if (Array.isArray(raw)) {
106
+ settings[key] = raw.map((value) => (typeof value === "string" ? quotePbxStringIfNeeded(value) : value));
107
+ }
108
+ }
109
+ }
110
+ }
111
+
112
+ function isPbxQuotedString(value: string): boolean {
113
+ return value.length >= 2 && value.startsWith('"') && value.endsWith('"');
114
+ }
115
+
116
+ function unquotePbxString(value: string): string {
117
+ if (!isPbxQuotedString(value)) return value;
118
+ return value.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, "\\");
119
+ }
120
+
121
+ function quotePbxStringIfNeeded(value: string): string {
122
+ if (isPbxQuotedString(value)) return value;
123
+ if (!pbxStringNeedsQuotes(value)) return value;
124
+ return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
125
+ }
126
+
127
+ function pbxStringNeedsQuotes(value: string): boolean {
128
+ if (value.length === 0 || value.startsWith("-")) return true;
129
+ return /[^A-Za-z0-9_.$/{}()+-]/.test(value);
130
+ }
131
+
132
+ function samePbxStringValue(a: unknown, b: string): boolean {
133
+ return typeof a === "string" && unquotePbxString(a) === unquotePbxString(b);
134
+ }
135
+
75
136
  function targetUuid(proj: XcodeProject, targetName: string): string | undefined {
76
137
  const targets = proj.pbxNativeTargetSection?.() ?? {};
77
138
  for (const [uuid, t] of Object.entries(targets)) {
@@ -229,7 +290,12 @@ export function addSourceOrResourceFile(ctx: PbxContext, relPathFromSrcRoot: str
229
290
  if (file.endsWith(".h")) {
230
291
  ctx.proj.addHeaderFile(file, { target });
231
292
  } else if (file.endsWith(".m") || file.endsWith(".mm") || file.endsWith(".swift")) {
232
- ctx.proj.addSourceFile(file, { target });
293
+ try {
294
+ ctx.proj.addSourceFile(file, { target });
295
+ } catch (e) {
296
+ if (!String(e instanceof Error ? e.message : e).includes("path")) throw e;
297
+ addSourceFileManually(ctx, file);
298
+ }
233
299
  } else {
234
300
  ensureResourcesBuildPhase(ctx.proj, ctx.targetName);
235
301
  const opt: Record<string, unknown> = { target };
@@ -245,6 +311,51 @@ export function addSourceOrResourceFile(ctx: PbxContext, relPathFromSrcRoot: str
245
311
  }
246
312
  }
247
313
 
314
+ function ensureSourcesBuildPhase(proj: XcodeProject, targetName: string): void {
315
+ const target = targetKey(proj, targetName);
316
+ try {
317
+ proj.pbxSourcesBuildPhaseObj(target);
318
+ return;
319
+ } catch {
320
+ proj.addBuildPhase([], "PBXSourcesBuildPhase", "Sources", target);
321
+ }
322
+ }
323
+
324
+ function sourceFileType(file: string): string {
325
+ if (file.endsWith(".swift")) return "sourcecode.swift";
326
+ if (file.endsWith(".mm")) return "sourcecode.cpp.objcpp";
327
+ return "sourcecode.c.objc";
328
+ }
329
+
330
+ function addSourceFileManually(ctx: PbxContext, file: string): void {
331
+ const basename = path.basename(file);
332
+ ensureSourcesBuildPhase(ctx.proj, ctx.targetName);
333
+ const sources = ctx.proj.pbxSourcesBuildPhaseObj(targetKey(ctx.proj, ctx.targetName));
334
+ const files = (sources.files ??= []) as Array<{ value?: string; comment?: string }>;
335
+ if (JSON.stringify(files).includes(basename)) return;
336
+
337
+ const fileRefUuid = ctx.proj.generateUuid();
338
+ const buildUuid = ctx.proj.generateUuid();
339
+ const fileRefSection = objectSection(ctx.proj, "PBXFileReference");
340
+ const buildFileSection = objectSection(ctx.proj, "PBXBuildFile");
341
+
342
+ fileRefSection[fileRefUuid] = {
343
+ isa: "PBXFileReference",
344
+ name: `"${basename}"`,
345
+ path: `"${file}"`,
346
+ sourceTree: '"<group>"',
347
+ lastKnownFileType: sourceFileType(file),
348
+ };
349
+ fileRefSection[`${fileRefUuid}_comment`] = basename;
350
+ buildFileSection[buildUuid] = {
351
+ isa: "PBXBuildFile",
352
+ fileRef: fileRefUuid,
353
+ fileRef_comment: basename,
354
+ };
355
+ buildFileSection[`${buildUuid}_comment`] = `${basename} in Sources`;
356
+ files.push({ value: buildUuid, comment: `${basename} in Sources` });
357
+ }
358
+
248
359
  function resourceFileType(file: string, lastKnownFileType?: string): string {
249
360
  if (lastKnownFileType) return lastKnownFileType;
250
361
  if (file.endsWith(".bundle")) return "wrapper.plug-in";
@@ -317,12 +428,18 @@ export function addOtherLinkerFlag(ctx: PbxContext, flag: string): void {
317
428
  if (!cfg || typeof cfg !== "object") continue;
318
429
  const settings = (cfg as { buildSettings?: Record<string, unknown> }).buildSettings ?? {};
319
430
  const cur = settings.OTHER_LDFLAGS;
431
+ const normalizedFlag = unquotePbxString(flag);
432
+ const nextFlag = quotePbxStringIfNeeded(normalizedFlag);
320
433
  if (typeof cur === "string") {
321
- if (!cur.includes(flag)) settings.OTHER_LDFLAGS = `${cur} ${flag}`.trim();
434
+ if (!unquotePbxString(cur).split(/\s+/).includes(normalizedFlag)) {
435
+ settings.OTHER_LDFLAGS = [quotePbxStringIfNeeded(cur), nextFlag];
436
+ }
322
437
  } else if (Array.isArray(cur)) {
323
- if (!cur.includes(flag)) settings.OTHER_LDFLAGS = [...cur, flag];
438
+ const values = cur.map((value) => (typeof value === "string" ? quotePbxStringIfNeeded(value) : value));
439
+ if (!values.some((value) => samePbxStringValue(value, normalizedFlag))) values.push(nextFlag);
440
+ settings.OTHER_LDFLAGS = values;
324
441
  } else {
325
- settings.OTHER_LDFLAGS = [flag];
442
+ settings.OTHER_LDFLAGS = [nextFlag];
326
443
  }
327
444
  (cfg as { buildSettings: Record<string, unknown> }).buildSettings = settings;
328
445
  }
@@ -1,25 +1,17 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
-
4
- const BUNDLED_IOS_REL = path.join("bundled", "ios-sdk");
5
-
6
- export function defaultBundledIosSdkRoot(packageRoot: string): string {
7
- return path.join(packageRoot, BUNDLED_IOS_REL);
8
- }
3
+ import { MEET_SDK_TOOL_CACHE_ROOT } from "../cache.js";
9
4
 
10
5
  /**
11
- * TopSDK iOS package root: `bundled/ios-sdk` or a child version dir (e.g. `ios--V1.6.0.4/`).
12
- * Native iOS only; SDK is vendored in the tool package.
6
+ * TopSDK iOS package root from cache.
7
+ * Native iOS only.
13
8
  */
14
- export function resolveIosSdkRoot(packageRoot: string): string {
15
- const bundled = defaultBundledIosSdkRoot(packageRoot);
16
- if (!fs.existsSync(bundled)) {
17
- throw new Error(`iOS SDK not found at ${BUNDLED_IOS_REL} (expected sdk/ and plugins/ inside).`);
18
- }
9
+ function findIosSdkRoot(container: string): string | null {
10
+ if (!fs.existsSync(container)) return null;
19
11
  const children = fs
20
- .readdirSync(bundled, { withFileTypes: true })
12
+ .readdirSync(container, { withFileTypes: true })
21
13
  .filter((d) => d.isDirectory() && !d.name.startsWith("."))
22
- .map((d) => path.join(bundled, d.name))
14
+ .map((d) => path.join(container, d.name))
23
15
  .sort()
24
16
  .reverse();
25
17
  for (const dir of children) {
@@ -27,10 +19,41 @@ export function resolveIosSdkRoot(packageRoot: string): string {
27
19
  return dir;
28
20
  }
29
21
  }
30
- if (fs.existsSync(path.join(bundled, "sdk")) && fs.existsSync(path.join(bundled, "plugins"))) {
31
- return bundled;
22
+ if (fs.existsSync(path.join(container, "sdk")) && fs.existsSync(path.join(container, "plugins"))) {
23
+ return container;
32
24
  }
25
+ return null;
26
+ }
27
+
28
+ function findLatestCachedIosSdkRoot(): string | null {
29
+ const iosCache = path.join(MEET_SDK_TOOL_CACHE_ROOT, "sdk", "ios");
30
+ if (!fs.existsSync(iosCache)) return null;
31
+ const candidates: string[] = [];
32
+ const walk = (dir: string): void => {
33
+ for (const ent of fs.readdirSync(dir, { withFileTypes: true })) {
34
+ if (!ent.isDirectory() || ent.name.startsWith(".")) continue;
35
+ const abs = path.join(dir, ent.name);
36
+ if (ent.name === "extracted") {
37
+ candidates.push(abs);
38
+ } else {
39
+ walk(abs);
40
+ }
41
+ }
42
+ };
43
+ walk(iosCache);
44
+ candidates.sort((a, b) => fs.statSync(b).mtimeMs - fs.statSync(a).mtimeMs);
45
+ for (const candidate of candidates) {
46
+ const found = findIosSdkRoot(candidate);
47
+ if (found) return found;
48
+ }
49
+ return null;
50
+ }
51
+
52
+ export function resolveIosSdkRoot(_packageRoot: string): string {
53
+ const cached = findLatestCachedIosSdkRoot();
54
+ if (cached) return cached;
55
+
33
56
  throw new Error(
34
- `iOS SDK layout invalid under ${BUNDLED_IOS_REL}: need sdk/ + plugins/ at top level or under one version directory.`
57
+ `iOS SDK not found in ${path.join(MEET_SDK_TOOL_CACHE_ROOT, "sdk", "ios")}; run download-ios-sdk or setup first.`
35
58
  );
36
59
  }