@leonxin/meetgames 0.1.18 → 0.1.19

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 (88) hide show
  1. package/README.md +5 -4
  2. package/dist/cache.d.ts +16 -0
  3. package/dist/cache.d.ts.map +1 -1
  4. package/dist/cache.js +21 -0
  5. package/dist/cache.js.map +1 -1
  6. package/dist/cli.d.ts.map +1 -1
  7. package/dist/cli.js +20 -16
  8. package/dist/cli.js.map +1 -1
  9. package/dist/config/meetSdkDefaultConfig.d.ts +7 -11
  10. package/dist/config/meetSdkDefaultConfig.d.ts.map +1 -1
  11. package/dist/config/meetSdkDefaultConfig.js +216 -53
  12. package/dist/config/meetSdkDefaultConfig.js.map +1 -1
  13. package/dist/config/meetSdkIosConfig.d.ts +4 -5
  14. package/dist/config/meetSdkIosConfig.d.ts.map +1 -1
  15. package/dist/config/meetSdkIosConfig.js +19 -19
  16. package/dist/config/meetSdkIosConfig.js.map +1 -1
  17. package/dist/contracts/types.d.ts +4 -0
  18. package/dist/contracts/types.d.ts.map +1 -1
  19. package/dist/core/doctor.d.ts.map +1 -1
  20. package/dist/core/doctor.js +2 -0
  21. package/dist/core/doctor.js.map +1 -1
  22. package/dist/core/previewPatches.d.ts +2 -2
  23. package/dist/core/previewPatches.d.ts.map +1 -1
  24. package/dist/core/previewPatches.js +3 -3
  25. package/dist/core/previewPatches.js.map +1 -1
  26. package/dist/core/workspace.d.ts.map +1 -1
  27. package/dist/core/workspace.js +1 -0
  28. package/dist/core/workspace.js.map +1 -1
  29. package/dist/index.d.ts +4 -4
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/index.js +4 -4
  32. package/dist/index.js.map +1 -1
  33. package/dist/mcp/service.d.ts +2 -0
  34. package/dist/mcp/service.d.ts.map +1 -1
  35. package/dist/mcp/service.js +8 -7
  36. package/dist/mcp/service.js.map +1 -1
  37. package/dist/ops/handlers.d.ts.map +1 -1
  38. package/dist/ops/handlers.js +4 -0
  39. package/dist/ops/handlers.js.map +1 -1
  40. package/dist/remote/sdkHomeDownload.d.ts +4 -5
  41. package/dist/remote/sdkHomeDownload.d.ts.map +1 -1
  42. package/dist/remote/sdkHomeDownload.js +38 -12
  43. package/dist/remote/sdkHomeDownload.js.map +1 -1
  44. package/docs/API.md +13 -13
  45. package/docs/CLI.md +27 -13
  46. package/docs/INTEGRATION.md +7 -6
  47. package/package.json +1 -1
  48. package/src/cache.ts +37 -0
  49. package/src/cli.ts +21 -14
  50. package/src/config/meetSdkDefaultConfig.ts +243 -59
  51. package/src/config/meetSdkIosConfig.ts +17 -21
  52. package/src/contracts/types.ts +4 -0
  53. package/src/core/doctor.ts +2 -0
  54. package/src/core/previewPatches.ts +3 -3
  55. package/src/core/workspace.ts +1 -0
  56. package/src/index.ts +5 -3
  57. package/src/mcp/service.ts +11 -7
  58. package/src/ops/handlers.ts +4 -0
  59. package/src/remote/sdkHomeDownload.ts +48 -16
  60. package/config/meetsdk-android.json +0 -171
  61. package/config/meetsdk-ios.json +0 -15
  62. package/tests/assemble.test.ts +0 -12
  63. package/tests/doctor.test.ts +0 -131
  64. package/tests/downloadGoogleServicesJson.test.ts +0 -47
  65. package/tests/fetch-remote.test.ts +0 -23
  66. package/tests/fetchConfigOverrides.test.ts +0 -28
  67. package/tests/fetchConfigWrite.test.ts +0 -54
  68. package/tests/fixtures-hosts.test.ts +0 -78
  69. package/tests/gradle.test.ts +0 -33
  70. package/tests/integration-json.test.ts +0 -29
  71. package/tests/ios.codeUtils.test.ts +0 -23
  72. package/tests/ios.sdkBundle.test.ts +0 -21
  73. package/tests/loadManifest.test.ts +0 -15
  74. package/tests/manifest-xml.test.ts +0 -30
  75. package/tests/mcp.e2e.ts +0 -214
  76. package/tests/mcp.service.test.ts +0 -53
  77. package/tests/meetSdkRemoteConfig.test.ts +0 -482
  78. package/tests/meetSdkRemoteGradle.test.ts +0 -414
  79. package/tests/pipeline.android.test.ts +0 -149
  80. package/tests/pipeline.integration-json.test.ts +0 -58
  81. package/tests/pipeline.ios.test.ts +0 -609
  82. package/tests/pipeline.preview.patch.test.ts +0 -85
  83. package/tests/platformSelection.test.ts +0 -77
  84. package/tests/sdkHomeDownload.test.ts +0 -275
  85. package/tests/sdkVersionConfig.test.ts +0 -131
  86. package/tests/topsdk.test.ts +0 -53
  87. package/tests/topsdkDownloadSdkConfig.test.ts +0 -81
  88. package/tests/topsdkFeatureModules.test.ts +0 -116
@@ -1,42 +1,159 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
- import { fileURLToPath } from "node:url";
4
- import { tryParseAsMeetSdkDefaultConfig, type MeetSdkDefaultConfig } from "./meetSdkRemoteConfig.js";
5
- import { defaultSdkHomeApiBaseUrl, fetchSdkHomeVersions, type SdkHomePlatformVersion } from "../remote/sdkHomeDownload.js";
3
+ import {
4
+ MEETSDK_ANDROID_CONFIG_FILENAME,
5
+ resolveAndroidSdkCacheLayout,
6
+ resolveMeetSdkAndroidConfigCachePath,
7
+ withCacheLock,
8
+ } from "../cache.js";
9
+ import {
10
+ buildSdkHomeDownloadUrl,
11
+ defaultSdkHomeApiBaseUrl,
12
+ downloadBinaryFile,
13
+ extractZip,
14
+ fetchSdkHomeVersions,
15
+ type SdkHomePlatformVersion,
16
+ } from "../remote/sdkHomeDownload.js";
17
+ import {
18
+ DEFAULT_TOPSDK_PLUGIN_DEPENDENCIES,
19
+ tryParseAsMeetSdkDefaultConfig,
20
+ type MeetSdkDefaultConfig,
21
+ } from "./meetSdkRemoteConfig.js";
6
22
 
7
- /** Built-in Android SDK defaults (repos and per-plugin dependencies). */
8
- export const MEETSDK_ANDROID_DEFAULTS_FILENAME = "meetsdk-android.json";
9
- const MEETSDK_CONFIG_DIR = "config";
23
+ const DEFAULT_GROUP_ID = "com.sino.topsdk";
24
+ const DEFAULT_ANDROID_SDK_PACKAGE_TYPE = "native";
10
25
 
11
- const defaultConfigPath = fileURLToPath(
12
- new URL(`../../${MEETSDK_CONFIG_DIR}/${MEETSDK_ANDROID_DEFAULTS_FILENAME}`, import.meta.url)
13
- );
26
+ type JsonRecord = Record<string, unknown>;
14
27
 
15
- export function resolveMeetSdkAndroidConfigPath(packageRoot?: string): string {
16
- return packageRoot ? path.join(packageRoot, MEETSDK_CONFIG_DIR, MEETSDK_ANDROID_DEFAULTS_FILENAME) : defaultConfigPath;
28
+ function isRecord(value: unknown): value is JsonRecord {
29
+ return typeof value === "object" && value !== null && !Array.isArray(value);
17
30
  }
18
31
 
19
- export function loadBuiltInMeetSdkDefaultConfig(options: { packageRoot?: string } = {}): MeetSdkDefaultConfig {
20
- const configPath = resolveMeetSdkAndroidConfigPath(options.packageRoot);
21
- if (!fs.existsSync(configPath)) {
22
- throw new Error(
23
- `built-in ${MEETSDK_ANDROID_DEFAULTS_FILENAME} not found at ${configPath}; reinstall @leonxin/meetgames or run from a complete package install`
24
- );
32
+ function cloneDefaultConfig(value: MeetSdkDefaultConfig): MeetSdkDefaultConfig {
33
+ return JSON.parse(JSON.stringify(value)) as MeetSdkDefaultConfig;
34
+ }
35
+
36
+ function resolveSdkHomeApiBaseUrl(baseUrl?: string): string {
37
+ return baseUrl || process.env.MEETGAMES_SDK_HOME_API_BASE_URL?.trim() || defaultSdkHomeApiBaseUrl;
38
+ }
39
+
40
+ function parseJsonResponseEnvelope(body: unknown, label: string): unknown {
41
+ if (!isRecord(body)) throw new Error(`${label} response is not an object`);
42
+ const code = body.code;
43
+ if (code !== undefined && Number(code) !== 200) {
44
+ const message = typeof body.message === "string" ? body.message : JSON.stringify(body);
45
+ throw new Error(`${label} API error (code=${String(code)}): ${message}`);
25
46
  }
26
- const raw = JSON.parse(fs.readFileSync(configPath, "utf8")) as unknown;
27
- const parsed = tryParseAsMeetSdkDefaultConfig(raw);
47
+ return "data" in body ? body.data : body;
48
+ }
49
+
50
+ async function fetchJson(url: string, signal?: AbortSignal): Promise<unknown> {
51
+ let res: Response;
52
+ try {
53
+ res = await fetch(url, { redirect: "follow", signal, headers: { Accept: "application/json" } });
54
+ } catch (e) {
55
+ const cause = e instanceof Error && "cause" in e && e.cause instanceof Error ? e.cause.message : null;
56
+ const detail = cause && cause !== (e instanceof Error ? e.message : String(e)) ? ` (${cause})` : "";
57
+ throw new Error(`GET ${url} failed: ${e instanceof Error ? e.message : String(e)}${detail}`);
58
+ }
59
+ if (!res.ok) {
60
+ const text = await res.text().catch(() => "");
61
+ const detail = text.trim() ? `: ${text.trim().slice(0, 1000)}` : "";
62
+ throw new Error(`GET ${url} failed: HTTP ${res.status}${detail}`);
63
+ }
64
+ const text = await res.text();
65
+ try {
66
+ return JSON.parse(text) as unknown;
67
+ } catch {
68
+ throw new Error(`GET ${url} did not return valid JSON`);
69
+ }
70
+ }
71
+
72
+ export function resolveMeetSdkAndroidConfigPath(cacheRoot?: string): string {
73
+ return resolveMeetSdkAndroidConfigCachePath(cacheRoot);
74
+ }
75
+
76
+ function readMeetSdkDefaultConfigFromPath(configPath: string): MeetSdkDefaultConfig {
77
+ const parsed = tryParseAsMeetSdkDefaultConfig(JSON.parse(fs.readFileSync(configPath, "utf8")) as unknown);
28
78
  if (!parsed) {
29
- throw new Error(`built-in ${MEETSDK_ANDROID_DEFAULTS_FILENAME} is not a valid MeetSdk default config`);
79
+ throw new Error(`${MEETSDK_ANDROID_CONFIG_FILENAME} is not a valid MeetSdk default config: ${configPath}`);
30
80
  }
31
81
  return parsed;
32
82
  }
33
83
 
34
- function cloneDefaultConfig(value: MeetSdkDefaultConfig): MeetSdkDefaultConfig {
35
- return JSON.parse(JSON.stringify(value)) as MeetSdkDefaultConfig;
84
+ export function loadBuiltInMeetSdkDefaultConfig(options: { cacheRoot?: string; packageRoot?: string } = {}): MeetSdkDefaultConfig {
85
+ const cachePath = resolveMeetSdkAndroidConfigPath(options.cacheRoot);
86
+ if (fs.existsSync(cachePath)) return readMeetSdkDefaultConfigFromPath(cachePath);
87
+ return compatibilityDefaultConfig();
36
88
  }
37
89
 
38
- function resolveSdkHomeApiBaseUrl(baseUrl?: string): string {
39
- return baseUrl || process.env.MEETGAMES_SDK_HOME_API_BASE_URL?.trim() || defaultSdkHomeApiBaseUrl;
90
+ function compatibilityDefaultConfig(): MeetSdkDefaultConfig {
91
+ const raw = {
92
+ topsdk: {
93
+ version: "",
94
+ groupId: DEFAULT_GROUP_ID,
95
+ repositories: ["https://storage-sdk-gameplus.meetsocial.com/repository/TopSdk/"],
96
+ },
97
+ sdkModules: {
98
+ login: {
99
+ guest: { dependencies: [...DEFAULT_TOPSDK_PLUGIN_DEPENDENCIES.login.guest] },
100
+ email: { dependencies: [...DEFAULT_TOPSDK_PLUGIN_DEPENDENCIES.login.email] },
101
+ apple: { dependencies: [...DEFAULT_TOPSDK_PLUGIN_DEPENDENCIES.login.apple] },
102
+ facebook: { dependencies: [...DEFAULT_TOPSDK_PLUGIN_DEPENDENCIES.login.facebook] },
103
+ google: { dependencies: [...DEFAULT_TOPSDK_PLUGIN_DEPENDENCIES.login.google] },
104
+ twitter: { dependencies: [...DEFAULT_TOPSDK_PLUGIN_DEPENDENCIES.login.twitter] },
105
+ snapchat: { dependencies: [...DEFAULT_TOPSDK_PLUGIN_DEPENDENCIES.login.snapchat] },
106
+ line: { dependencies: [...DEFAULT_TOPSDK_PLUGIN_DEPENDENCIES.login.line] },
107
+ naver: { dependencies: [...DEFAULT_TOPSDK_PLUGIN_DEPENDENCIES.login.naver] },
108
+ kakao: {
109
+ repositories: ["https://devrepo.kakao.com/nexus/content/groups/public/"],
110
+ dependencies: [...DEFAULT_TOPSDK_PLUGIN_DEPENDENCIES.login.kakao],
111
+ },
112
+ tiktok: {
113
+ repositories: ["https://artifact.bytedance.com/repository/AwemeOpenSDK"],
114
+ dependencies: [...DEFAULT_TOPSDK_PLUGIN_DEPENDENCIES.login.tiktok],
115
+ },
116
+ discord: { dependencies: [...DEFAULT_TOPSDK_PLUGIN_DEPENDENCIES.login.discord] },
117
+ },
118
+ payment: {
119
+ googleIap: { dependencies: [...DEFAULT_TOPSDK_PLUGIN_DEPENDENCIES.payment.googleIap] },
120
+ appleIap: { dependencies: [...DEFAULT_TOPSDK_PLUGIN_DEPENDENCIES.payment.appleIap] },
121
+ onestoreIap: {
122
+ repositories: ["https://repo.onestore.co.kr/repository/onestore-sdk-public/"],
123
+ dependencies: [...DEFAULT_TOPSDK_PLUGIN_DEPENDENCIES.payment.onestoreIap],
124
+ },
125
+ huaweiIap: {
126
+ repositories: ["https://developer.huawei.com/repo/"],
127
+ dependencies: [...DEFAULT_TOPSDK_PLUGIN_DEPENDENCIES.payment.huaweiIap],
128
+ },
129
+ xiaomiIap: { dependencies: [...DEFAULT_TOPSDK_PLUGIN_DEPENDENCIES.payment.xiaomiIap] },
130
+ },
131
+ analytics: {
132
+ appsflyer: {
133
+ repositories: ["mavenCentral"],
134
+ dependencies: [...DEFAULT_TOPSDK_PLUGIN_DEPENDENCIES.analytics.appsflyer],
135
+ },
136
+ facebookdata: { dependencies: [...DEFAULT_TOPSDK_PLUGIN_DEPENDENCIES.analytics.facebookdata] },
137
+ firebase: {
138
+ repositories: ["google"],
139
+ classpath: "com.google.gms:google-services:4.4.4",
140
+ applyplugin: "com.google.gms.google-services",
141
+ dependencies: [...DEFAULT_TOPSDK_PLUGIN_DEPENDENCIES.analytics.firebase],
142
+ },
143
+ adjust: { dependencies: [...DEFAULT_TOPSDK_PLUGIN_DEPENDENCIES.analytics.adjust] },
144
+ },
145
+ },
146
+ };
147
+ const parsed = tryParseAsMeetSdkDefaultConfig(raw);
148
+ if (!parsed) throw new Error("embedded compatibility meetsdk-android defaults are invalid");
149
+ return parsed;
150
+ }
151
+
152
+ function writeMeetSdkAndroidConfigCache(config: MeetSdkDefaultConfig, cacheRoot?: string): string {
153
+ const configPath = resolveMeetSdkAndroidConfigPath(cacheRoot);
154
+ fs.mkdirSync(path.dirname(configPath), { recursive: true });
155
+ fs.writeFileSync(configPath, `${JSON.stringify(config, null, 2)}\n`, "utf8");
156
+ return configPath;
40
157
  }
41
158
 
42
159
  const latestAndroidVersionCache = new Map<string, Promise<SdkHomePlatformVersion>>();
@@ -51,50 +168,117 @@ async function fetchLatestAndroidSdkVersion(baseUrl?: string): Promise<SdkHomePl
51
168
  return cached;
52
169
  }
53
170
 
54
- export function syncMeetSdkAndroidVersionToConfig(params: {
55
- version: string;
56
- date?: string;
57
- packageRoot?: string;
58
- }): string {
59
- const version = params.version.trim();
60
- if (!version) throw new Error("Android SDK version is empty");
171
+ function androidChannelPlugin(channelType?: string): string {
172
+ const normalized = (channelType ?? "").trim().toUpperCase();
173
+ const map: Record<string, string> = {
174
+ GOOGLE: "google_channel",
175
+ ONESTORE: "onestore_channel",
176
+ XIAOMI: "xiaomi_channel",
177
+ HUAWEI: "huawei_channel",
178
+ };
179
+ return map[normalized] ?? "google_channel";
180
+ }
61
181
 
62
- const configPath = resolveMeetSdkAndroidConfigPath(params.packageRoot);
63
- if (!fs.existsSync(configPath)) {
64
- throw new Error(`built-in ${MEETSDK_ANDROID_DEFAULTS_FILENAME} not found at ${configPath}`);
65
- }
66
- const raw = JSON.parse(fs.readFileSync(configPath, "utf8")) as unknown;
67
- if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
68
- throw new Error(`built-in ${MEETSDK_ANDROID_DEFAULTS_FILENAME} is not a JSON object`);
182
+ function findMeetSdkAndroidConfigFile(root: string): string | null {
183
+ if (!fs.existsSync(root)) return null;
184
+ const stack = [root];
185
+ while (stack.length) {
186
+ const dir = stack.pop()!;
187
+ for (const ent of fs.readdirSync(dir, { withFileTypes: true })) {
188
+ if (ent.name.startsWith("__MACOSX") || ent.name.startsWith("._")) continue;
189
+ const abs = path.join(dir, ent.name);
190
+ if (ent.isDirectory()) {
191
+ stack.push(abs);
192
+ } else if (ent.isFile() && ent.name === MEETSDK_ANDROID_CONFIG_FILENAME) {
193
+ return abs;
194
+ }
195
+ }
69
196
  }
70
- const record = raw as Record<string, unknown>;
71
- if (typeof record.topsdk !== "object" || record.topsdk === null || Array.isArray(record.topsdk)) {
72
- throw new Error(`built-in ${MEETSDK_ANDROID_DEFAULTS_FILENAME} does not contain topsdk object`);
73
- }
74
- const topsdk = record.topsdk as Record<string, unknown>;
75
- topsdk.version = version;
76
- if (params.date?.trim()) topsdk.date = params.date.trim();
77
- fs.writeFileSync(configPath, `${JSON.stringify(record, null, 2)}\n`, "utf8");
78
- return configPath;
197
+ return null;
79
198
  }
80
199
 
200
+ function loadMeetSdkAndroidConfigFromExtracted(root: string, version: string, date?: string): MeetSdkDefaultConfig {
201
+ const configPath = findMeetSdkAndroidConfigFile(root);
202
+ if (!configPath) throw new Error(`${MEETSDK_ANDROID_CONFIG_FILENAME} not found under downloaded Android SDK: ${root}`);
203
+ const config = readMeetSdkDefaultConfigFromPath(configPath);
204
+ config.topsdk.version = config.topsdk.version || version;
205
+ config.topsdk.date = config.topsdk.date || date;
206
+ return config;
207
+ }
208
+
209
+ async function fetchSdkHomeAndroidConfig(params: {
210
+ baseUrl?: string;
211
+ version: string;
212
+ date?: string;
213
+ channelType?: string;
214
+ packageType?: string;
215
+ cacheRoot?: string;
216
+ signal?: AbortSignal;
217
+ }): Promise<{ url?: string; config: MeetSdkDefaultConfig }> {
218
+ const baseUrl = resolveSdkHomeApiBaseUrl(params.baseUrl);
219
+ const packageType = params.packageType || DEFAULT_ANDROID_SDK_PACKAGE_TYPE;
220
+ const plugins = [androidChannelPlugin(params.channelType)];
221
+ const layout = resolveAndroidSdkCacheLayout({
222
+ version: params.version,
223
+ packageType,
224
+ plugins,
225
+ cacheRoot: params.cacheRoot,
226
+ });
227
+ let downloadApiUrl: string | undefined;
228
+ const downloadAndExtract = async (): Promise<void> => {
229
+ if (findMeetSdkAndroidConfigFile(layout.extractDir)) return;
230
+ downloadApiUrl = buildSdkHomeDownloadUrl({
231
+ baseUrl,
232
+ version: params.version,
233
+ platform: "android",
234
+ packageType,
235
+ plugins,
236
+ });
237
+ const body = parseJsonResponseEnvelope(await fetchJson(downloadApiUrl, params.signal), "sdk-home Android download");
238
+ if (typeof body !== "string" || !body.trim()) {
239
+ throw new Error("sdk-home Android getDownLoadUrl response does not contain a download URL in data");
240
+ }
241
+ await downloadBinaryFile(body, layout.zipPath, params.signal);
242
+ fs.rmSync(layout.extractDir, { recursive: true, force: true });
243
+ await extractZip(layout.zipPath, layout.extractDir);
244
+ };
245
+ await withCacheLock(layout.lockDir, downloadAndExtract);
246
+ return {
247
+ url: downloadApiUrl,
248
+ config: loadMeetSdkAndroidConfigFromExtracted(layout.extractDir, params.version, params.date),
249
+ };
250
+ }
251
+
252
+ const androidConfigCache = new Map<string, Promise<MeetSdkDefaultConfig>>();
253
+
81
254
  /**
82
- * Built-in Android SDK defaults with the SDK version resolved at runtime from sdk-home.
83
- * The version is also persisted to `config/meetsdk-android.json` so the package config
84
- * reflects the latest SDK version discovered from the download center.
255
+ * Android SDK defaults resolved from sdk-home and persisted under ~/.cache/meet-sdk-tool.
256
+ * The package config/ directory is no longer written at runtime.
85
257
  */
86
258
  export async function loadMeetSdkDefaultConfigWithLatestAndroidVersion(options: {
87
259
  sdkHomeApiBaseUrl?: string;
260
+ cacheRoot?: string;
88
261
  packageRoot?: string;
262
+ channelType?: string;
263
+ packageType?: string;
89
264
  } = {}): Promise<MeetSdkDefaultConfig> {
90
- const defaults = cloneDefaultConfig(loadBuiltInMeetSdkDefaultConfig({ packageRoot: options.packageRoot }));
91
265
  const latest = await fetchLatestAndroidSdkVersion(options.sdkHomeApiBaseUrl);
92
- syncMeetSdkAndroidVersionToConfig({
93
- version: latest.ver,
94
- date: latest.date,
95
- packageRoot: options.packageRoot,
96
- });
97
- defaults.topsdk.version = latest.ver;
98
- defaults.topsdk.date = latest.date;
99
- return defaults;
266
+ const baseUrl = resolveSdkHomeApiBaseUrl(options.sdkHomeApiBaseUrl);
267
+ const key = `${baseUrl}\n${latest.ver}\n${options.cacheRoot ?? ""}\n${options.channelType ?? ""}\n${options.packageType ?? ""}`;
268
+ let cached = androidConfigCache.get(key);
269
+ if (!cached) {
270
+ cached = fetchSdkHomeAndroidConfig({
271
+ baseUrl,
272
+ version: latest.ver,
273
+ date: latest.date,
274
+ channelType: options.channelType,
275
+ packageType: options.packageType,
276
+ cacheRoot: options.cacheRoot,
277
+ }).then(({ config }) => {
278
+ writeMeetSdkAndroidConfigCache(config, options.cacheRoot);
279
+ return cloneDefaultConfig(config);
280
+ });
281
+ androidConfigCache.set(key, cached);
282
+ }
283
+ return cloneDefaultConfig(await cached);
100
284
  }
@@ -1,8 +1,6 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
-
4
- export const MEETSDK_IOS_CONFIG_FILENAME = "meetsdk-ios.json";
5
- const MEETSDK_CONFIG_DIR = "config";
3
+ import { MEETSDK_IOS_CONFIG_FILENAME, resolveMeetSdkIosConfigCachePath } from "../cache.js";
6
4
 
7
5
  export interface MeetSdkIosConfig {
8
6
  topsdk: {
@@ -27,8 +25,8 @@ function normalizePlugins(value: unknown): string[] | undefined {
27
25
  return plugins.length ? plugins : undefined;
28
26
  }
29
27
 
30
- export function resolveMeetSdkIosConfigPath(packageRoot: string): string {
31
- return path.join(packageRoot, MEETSDK_CONFIG_DIR, MEETSDK_IOS_CONFIG_FILENAME);
28
+ export function resolveMeetSdkIosConfigPath(cacheRoot?: string): string {
29
+ return resolveMeetSdkIosConfigCachePath(cacheRoot);
32
30
  }
33
31
 
34
32
  export function tryParseAsMeetSdkIosConfig(raw: unknown): MeetSdkIosConfig | null {
@@ -44,8 +42,8 @@ export function tryParseAsMeetSdkIosConfig(raw: unknown): MeetSdkIosConfig | nul
44
42
  };
45
43
  }
46
44
 
47
- export function loadMeetSdkIosConfig(packageRoot: string): MeetSdkIosConfig {
48
- const configPath = resolveMeetSdkIosConfigPath(packageRoot);
45
+ export function loadMeetSdkIosConfig(cacheRoot?: string): MeetSdkIosConfig {
46
+ const configPath = resolveMeetSdkIosConfigPath(cacheRoot);
49
47
  if (!fs.existsSync(configPath)) {
50
48
  return { topsdk: { version: "" } };
51
49
  }
@@ -56,15 +54,15 @@ export function loadMeetSdkIosConfig(packageRoot: string): MeetSdkIosConfig {
56
54
  return parsed;
57
55
  }
58
56
 
59
- export function writeMeetSdkIosConfig(packageRoot: string, config: MeetSdkIosConfig): string {
60
- const configPath = resolveMeetSdkIosConfigPath(packageRoot);
57
+ export function writeMeetSdkIosConfig(config: MeetSdkIosConfig, cacheRoot?: string): string {
58
+ const configPath = resolveMeetSdkIosConfigPath(cacheRoot);
61
59
  fs.mkdirSync(path.dirname(configPath), { recursive: true });
62
60
  fs.writeFileSync(configPath, `${JSON.stringify(config, null, 2)}\n`, "utf8");
63
61
  return configPath;
64
62
  }
65
63
 
66
64
  export function syncMeetSdkIosVersionToConfig(params: {
67
- packageRoot: string;
65
+ cacheRoot?: string;
68
66
  version: string;
69
67
  date?: string;
70
68
  packageType?: string;
@@ -73,17 +71,15 @@ export function syncMeetSdkIosVersionToConfig(params: {
73
71
  const version = params.version.trim();
74
72
  if (!version) throw new Error("iOS SDK version is empty");
75
73
 
76
- const current = loadMeetSdkIosConfig(params.packageRoot);
77
- const nextDate = params.date === undefined ? current.topsdk.date : params.date.trim() || undefined;
74
+ const topsdk: MeetSdkIosConfig["topsdk"] = { version };
75
+ const date = params.date?.trim();
76
+ const packageType = params.packageType?.trim();
77
+ const plugins = params.plugins?.map((x) => x.trim()).filter(Boolean);
78
+ if (date) topsdk.date = date;
79
+ if (packageType) topsdk.packageType = packageType;
80
+ if (plugins?.length) topsdk.plugins = plugins;
78
81
  const next: MeetSdkIosConfig = {
79
- ...current,
80
- topsdk: {
81
- ...current.topsdk,
82
- version,
83
- date: nextDate,
84
- packageType: params.packageType?.trim() || current.topsdk.packageType,
85
- plugins: params.plugins?.map((x) => x.trim()).filter(Boolean) || current.topsdk.plugins,
86
- },
82
+ topsdk,
87
83
  };
88
- return writeMeetSdkIosConfig(params.packageRoot, next);
84
+ return writeMeetSdkIosConfig(next, params.cacheRoot);
89
85
  }
@@ -57,6 +57,8 @@ export interface WorkspaceContext {
57
57
  remoteConfigPath?: string;
58
58
  /** Cached iOS SDK root used as the source for integration resources. */
59
59
  iosSdkRoot?: string;
60
+ /** Optional cache root override, primarily used by tests. */
61
+ cacheRoot?: string;
60
62
  android?: AndroidDetectResult;
61
63
  /** Reserved: populated via placeholder until iOS tooling ships. */
62
64
  ios?: IOSDetectResult;
@@ -72,6 +74,8 @@ export interface WorkspaceContextOptions {
72
74
  remoteConfigPath?: string;
73
75
  /** Override the iOS SDK source root. */
74
76
  iosSdkRoot?: string;
77
+ /** Override meet-sdk-tool cache root. */
78
+ cacheRoot?: string;
75
79
  }
76
80
 
77
81
  export interface ManifestStep {
@@ -198,6 +198,8 @@ async function checkAndroid(ctx: WorkspaceContext, report: DoctorReport, config:
198
198
  await loadMeetSdkDefaultConfigWithLatestAndroidVersion({
199
199
  sdkHomeApiBaseUrl: ctx.sdkHomeApiBaseUrl,
200
200
  packageRoot: ctx.packageRoot,
201
+ cacheRoot: ctx.cacheRoot,
202
+ channelType: config.channel,
201
203
  })
202
204
  );
203
205
  const moduleGradleAbs = moduleBuildGradlePath(ctx);
@@ -1,13 +1,13 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
3
 
4
- /** Default directory for `pipeline.patch` / `cli-preview.patch` (gitignored). */
4
+ /** Default directory for expected dry-run patch artifacts. */
5
5
  export function resolveDefaultPreviewPatchDir(packageRoot: string): string {
6
- return path.join(packageRoot, "preview-patches");
6
+ return path.join(packageRoot, "fixtures", "expected-patches");
7
7
  }
8
8
 
9
9
  /**
10
- * Before writing a new patch under `preview-patches/`, remove all existing files there
10
+ * Before writing a new patch under `fixtures/expected-patches/`, remove all existing files there
11
11
  * so only the latest command output remains.
12
12
  */
13
13
  export function clearPreviewPatchFilesIfTargetInside(packageRoot: string, patchFileAbs: string): void {
@@ -24,6 +24,7 @@ export function buildWorkspaceContext(
24
24
  sdkHomeApiBaseUrl: options.sdkHomeApiBaseUrl,
25
25
  remoteConfigPath: options.remoteConfigPath,
26
26
  iosSdkRoot: options.iosSdkRoot,
27
+ cacheRoot: options.cacheRoot,
27
28
  android: detectAndroid(root, { appTarget: options.appTarget }),
28
29
  ios: detectIOS(root, { appTarget: options.appTarget }),
29
30
  iosReserved: iosToolingReserved,
package/src/index.ts CHANGED
@@ -61,10 +61,8 @@ export {
61
61
  loadBuiltInMeetSdkDefaultConfig,
62
62
  loadMeetSdkDefaultConfigWithLatestAndroidVersion,
63
63
  resolveMeetSdkAndroidConfigPath,
64
- syncMeetSdkAndroidVersionToConfig,
65
64
  } from "./config/meetSdkDefaultConfig.js";
66
65
  export {
67
- MEETSDK_IOS_CONFIG_FILENAME,
68
66
  loadMeetSdkIosConfig,
69
67
  resolveMeetSdkIosConfigPath,
70
68
  syncMeetSdkIosVersionToConfig,
@@ -81,9 +79,13 @@ export {
81
79
  } from "./config/topsdkFeatureModules.js";
82
80
  export { clearPreviewPatchFilesIfTargetInside, resolveDefaultPreviewPatchDir } from "./core/previewPatches.js";
83
81
  export {
82
+ MEETSDK_ANDROID_CONFIG_FILENAME,
83
+ MEETSDK_IOS_CONFIG_FILENAME,
84
84
  MEET_SDK_TOOL_CACHE_ROOT,
85
85
  ensureCacheRoot,
86
86
  resolveIosSdkCacheLayout,
87
+ resolveMeetSdkAndroidConfigCachePath,
88
+ resolveMeetSdkIosConfigCachePath,
87
89
  resolveRemoteConfigCachePath,
88
90
  writeRemoteConfigCache,
89
91
  } from "./cache.js";
@@ -108,7 +110,6 @@ export {
108
110
  } from "./remote/topsdkDownloadSdkConfig.js";
109
111
  export {
110
112
  DEFAULT_IOS_SDK_PACKAGE_TYPE,
111
- DEFAULT_IOS_SDK_PLUGINS,
112
113
  buildSdkHomeDownloadUrl,
113
114
  buildSdkHomeVersionUrl,
114
115
  defaultSdkHomeApiBaseUrl,
@@ -118,6 +119,7 @@ export {
118
119
  fetchSdkHomeIosDownloadUrl,
119
120
  fetchSdkHomeIosVersion,
120
121
  fetchSdkHomeVersions,
122
+ resolveIosSdkDownloadPluginsFromRemoteConfig,
121
123
  resolveIosSdkRootFromDirectory,
122
124
  resolveIosSdkZipFileName,
123
125
  type DownloadIosSdkOptions,
@@ -24,7 +24,6 @@ import { defaultTopSdkBaseUrl, fetchTopSdkDownloadSdkConfig, type TopSdkApiEnv }
24
24
  import {
25
25
  downloadIosSdkToBundled,
26
26
  defaultSdkHomeApiBaseUrl,
27
- resolveIosSdkDownloadPluginsFromRemoteConfig,
28
27
  } from "../remote/sdkHomeDownload.js";
29
28
  import { resolveRemoteConfigCachePath, writeRemoteConfigCache } from "../cache.js";
30
29
 
@@ -71,12 +70,13 @@ function ensureSinglePlatformContext(
71
70
  projectRoot: string,
72
71
  packageRoot: string,
73
72
  appTarget?: string,
74
- remoteConfigPath?: string
73
+ remoteConfigPath?: string,
74
+ cacheRoot?: string
75
75
  ): {
76
76
  platform: DetectedPlatform;
77
77
  ctx: ReturnType<typeof buildWorkspaceContext>;
78
78
  } {
79
- const detectedCtx = buildWorkspaceContext(projectRoot, packageRoot, { appTarget, remoteConfigPath });
79
+ const detectedCtx = buildWorkspaceContext(projectRoot, packageRoot, { appTarget, remoteConfigPath, cacheRoot });
80
80
  const detected = detectSinglePlatform(detectedCtx);
81
81
  if (!detected.ok) throw new Error(detected.error);
82
82
  const platformCtx = platformContext(detectedCtx, detected.platform);
@@ -186,6 +186,7 @@ export async function meetgamesIntegrate(params: {
186
186
  env?: TopSdkApiEnv;
187
187
  appId?: string;
188
188
  channelType?: string;
189
+ cacheRoot?: string;
189
190
  dryRun: boolean;
190
191
  }): Promise<{
191
192
  dryRun: boolean;
@@ -203,7 +204,8 @@ export async function meetgamesIntegrate(params: {
203
204
  projectRoot,
204
205
  packageRoot,
205
206
  resolveAppTarget(params),
206
- resolveCachedRemoteConfigPath(params)
207
+ resolveCachedRemoteConfigPath(params),
208
+ params.cacheRoot
207
209
  );
208
210
  const { report, patch, binaryCopies } = await runPipeline(ctx, manifestForPlatform(manifest, platform), { dryRun: params.dryRun });
209
211
  return {
@@ -224,6 +226,7 @@ export async function meetgamesSetup(params: {
224
226
  appId?: string;
225
227
  appSecret?: string;
226
228
  channelType?: string;
229
+ cacheRoot?: string;
227
230
  dryRun: boolean;
228
231
  }): Promise<{
229
232
  fetch: Awaited<ReturnType<typeof meetgamesFetchConfig>>;
@@ -232,7 +235,7 @@ export async function meetgamesSetup(params: {
232
235
  const projectRoot = ensureProjectRoot(params.projectRoot);
233
236
  const packageRoot = resolvePackageRoot();
234
237
  const appTarget = resolveAppTarget(params);
235
- const { platform } = ensureSinglePlatformContext(projectRoot, packageRoot, appTarget);
238
+ const { platform } = ensureSinglePlatformContext(projectRoot, packageRoot, appTarget, undefined, params.cacheRoot);
236
239
  const fetch = await meetgamesFetchConfig({
237
240
  projectRoot,
238
241
  env: params.env,
@@ -241,10 +244,10 @@ export async function meetgamesSetup(params: {
241
244
  channelType: params.channelType,
242
245
  });
243
246
  if (platform === "ios") {
244
- const remote = tryParseAsMeetSdkRemoteConfig(JSON.parse(fs.readFileSync(fetch.outputPath, "utf8")) as unknown);
245
247
  await downloadIosSdkToBundled(packageRoot, {
246
248
  baseUrl: process.env.MEETGAMES_SDK_HOME_API_BASE_URL?.trim() || defaultSdkHomeApiBaseUrl,
247
- plugins: remote ? resolveIosSdkDownloadPluginsFromRemoteConfig(remote) : undefined,
249
+ remoteConfigPath: fetch.outputPath,
250
+ cacheRoot: params.cacheRoot,
248
251
  });
249
252
  }
250
253
  const integrate = await meetgamesIntegrate({
@@ -253,6 +256,7 @@ export async function meetgamesSetup(params: {
253
256
  env: params.env,
254
257
  appId: params.appId,
255
258
  channelType: params.channelType,
259
+ cacheRoot: params.cacheRoot,
256
260
  dryRun: params.dryRun,
257
261
  });
258
262
  return { fetch, integrate };
@@ -146,6 +146,8 @@ export const opHandlers: Record<string, OpHandler> = {
146
146
  await loadMeetSdkDefaultConfigWithLatestAndroidVersion({
147
147
  sdkHomeApiBaseUrl: ctx.sdkHomeApiBaseUrl,
148
148
  packageRoot: ctx.packageRoot,
149
+ cacheRoot: ctx.cacheRoot,
150
+ channelType: parsed.channel,
149
151
  })
150
152
  );
151
153
 
@@ -302,6 +304,8 @@ export const opHandlers: Record<string, OpHandler> = {
302
304
  await loadMeetSdkDefaultConfigWithLatestAndroidVersion({
303
305
  sdkHomeApiBaseUrl: ctx.sdkHomeApiBaseUrl,
304
306
  packageRoot: ctx.packageRoot,
307
+ cacheRoot: ctx.cacheRoot,
308
+ channelType: parsed.channel,
305
309
  })
306
310
  );
307
311
  const firebase = resolved.sdkModules.analytics.firebase;