@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.
- package/.agents/skills/meet-sdk-regression/SKILL.md +93 -0
- package/.cursor/mcp.example.json +16 -0
- package/.cursor/mcp.json +11 -0
- package/.cursor/skills/meetgames-mcp/SKILL.md +48 -0
- package/.vite/vitest/results.json +1 -0
- package/README.md +31 -8
- package/dist/aab-converter/aab-entry.d.ts +3 -0
- package/dist/aab-converter/aab-entry.d.ts.map +1 -0
- package/dist/aab-converter/aab-entry.js +49 -0
- package/dist/aab-converter/aab-entry.js.map +1 -0
- package/dist/aab-converter/apksExtractor.d.ts +2 -0
- package/dist/aab-converter/apksExtractor.d.ts.map +1 -0
- package/dist/aab-converter/apksExtractor.js +108 -0
- package/dist/aab-converter/apksExtractor.js.map +1 -0
- package/dist/aab-converter/bundletoolRunner.d.ts +15 -0
- package/dist/aab-converter/bundletoolRunner.d.ts.map +1 -0
- package/dist/aab-converter/bundletoolRunner.js +46 -0
- package/dist/aab-converter/bundletoolRunner.js.map +1 -0
- package/dist/aab-converter/cliArgs.d.ts +27 -0
- package/dist/aab-converter/cliArgs.d.ts.map +1 -0
- package/dist/aab-converter/cliArgs.js +170 -0
- package/dist/aab-converter/cliArgs.js.map +1 -0
- package/dist/aab-converter/convertAabToApk.d.ts +7 -0
- package/dist/aab-converter/convertAabToApk.d.ts.map +1 -0
- package/dist/aab-converter/convertAabToApk.js +69 -0
- package/dist/aab-converter/convertAabToApk.js.map +1 -0
- package/dist/aab-converter/resourcePaths.d.ts +4 -0
- package/dist/aab-converter/resourcePaths.d.ts.map +1 -0
- package/dist/aab-converter/resourcePaths.js +42 -0
- package/dist/aab-converter/resourcePaths.js.map +1 -0
- package/dist/aab-converter/signingOptions.d.ts +9 -0
- package/dist/aab-converter/signingOptions.d.ts.map +1 -0
- package/dist/aab-converter/signingOptions.js +21 -0
- package/dist/aab-converter/signingOptions.js.map +1 -0
- package/dist/aab-converter/types.d.ts +24 -0
- package/dist/aab-converter/types.d.ts.map +1 -0
- package/dist/aab-converter/types.js +2 -0
- package/dist/aab-converter/types.js.map +1 -0
- package/dist/android/adapter.d.ts.map +1 -1
- package/dist/android/adapter.js +2 -2
- package/dist/android/adapter.js.map +1 -1
- package/dist/android/detect.d.ts +2 -2
- package/dist/android/detect.d.ts.map +1 -1
- package/dist/android/detect.js +36 -8
- package/dist/android/detect.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +157 -31
- package/dist/cli.js.map +1 -1
- package/dist/config/meetSdkDefaultConfig.d.ts +19 -2
- package/dist/config/meetSdkDefaultConfig.d.ts.map +1 -1
- package/dist/config/meetSdkDefaultConfig.js +67 -5
- package/dist/config/meetSdkDefaultConfig.js.map +1 -1
- package/dist/config/meetSdkIosConfig.d.ts +21 -0
- package/dist/config/meetSdkIosConfig.d.ts.map +1 -0
- package/dist/config/meetSdkIosConfig.js +66 -0
- package/dist/config/meetSdkIosConfig.js.map +1 -0
- package/dist/config/meetSdkRemoteConfig.d.ts +18 -1
- package/dist/config/meetSdkRemoteConfig.d.ts.map +1 -1
- package/dist/config/meetSdkRemoteConfig.js +61 -25
- package/dist/config/meetSdkRemoteConfig.js.map +1 -1
- package/dist/contracts/types.d.ts +19 -6
- package/dist/contracts/types.d.ts.map +1 -1
- package/dist/core/doctor.d.ts +17 -0
- package/dist/core/doctor.d.ts.map +1 -0
- package/dist/core/doctor.js +444 -0
- package/dist/core/doctor.js.map +1 -0
- package/dist/core/pipeline.d.ts.map +1 -1
- package/dist/core/pipeline.js +0 -15
- package/dist/core/pipeline.js.map +1 -1
- package/dist/core/platform.d.ts +12 -0
- package/dist/core/platform.d.ts.map +1 -0
- package/dist/core/platform.js +40 -0
- package/dist/core/platform.js.map +1 -0
- package/dist/core/reporter.js +1 -1
- package/dist/core/reporter.js.map +1 -1
- package/dist/core/workspace.d.ts +2 -2
- package/dist/core/workspace.d.ts.map +1 -1
- package/dist/core/workspace.js +4 -5
- package/dist/core/workspace.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/ios/channelConfig.d.ts +1 -0
- package/dist/ios/channelConfig.d.ts.map +1 -1
- package/dist/ios/channelConfig.js +82 -0
- package/dist/ios/channelConfig.js.map +1 -1
- package/dist/ios/codeUtils.d.ts +1 -0
- package/dist/ios/codeUtils.d.ts.map +1 -1
- package/dist/ios/codeUtils.js +11 -2
- package/dist/ios/codeUtils.js.map +1 -1
- package/dist/ios/detect.d.ts +2 -2
- package/dist/ios/detect.d.ts.map +1 -1
- package/dist/ios/detect.js +49 -10
- package/dist/ios/detect.js.map +1 -1
- package/dist/ios/entitlements.d.ts +4 -0
- package/dist/ios/entitlements.d.ts.map +1 -0
- package/dist/ios/entitlements.js +53 -0
- package/dist/ios/entitlements.js.map +1 -0
- package/dist/ios/fileManager.d.ts.map +1 -1
- package/dist/ios/fileManager.js +3 -2
- package/dist/ios/fileManager.js.map +1 -1
- package/dist/ios/infoPlist.d.ts +1 -1
- package/dist/ios/infoPlist.d.ts.map +1 -1
- package/dist/ios/infoPlist.js.map +1 -1
- package/dist/ios/integrate.d.ts.map +1 -1
- package/dist/ios/integrate.js +211 -36
- package/dist/ios/integrate.js.map +1 -1
- package/dist/ios/pbxprojEditor.d.ts +2 -0
- package/dist/ios/pbxprojEditor.d.ts.map +1 -1
- package/dist/ios/pbxprojEditor.js +179 -1
- package/dist/ios/pbxprojEditor.js.map +1 -1
- package/dist/ios/pluginConfig.d.ts +1 -0
- package/dist/ios/pluginConfig.d.ts.map +1 -1
- package/dist/ios/pluginConfig.js +36 -4
- package/dist/ios/pluginConfig.js.map +1 -1
- package/dist/ios/sdkBundle.d.ts +1 -1
- package/dist/ios/sdkBundle.d.ts.map +1 -1
- package/dist/ios/sdkBundle.js +7 -5
- package/dist/ios/sdkBundle.js.map +1 -1
- package/dist/ios/template.d.ts +1 -0
- package/dist/ios/template.d.ts.map +1 -1
- package/dist/ios/template.js +14 -1
- package/dist/ios/template.js.map +1 -1
- package/dist/ios/types.d.ts +2 -2
- package/dist/ios/types.d.ts.map +1 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +14 -13
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/service.d.ts +8 -6
- package/dist/mcp/service.d.ts.map +1 -1
- package/dist/mcp/service.js +34 -14
- package/dist/mcp/service.js.map +1 -1
- package/dist/ops/handlers.d.ts.map +1 -1
- package/dist/ops/handlers.js +10 -4
- package/dist/ops/handlers.js.map +1 -1
- package/dist/remote/sdkHomeDownload.d.ts +65 -0
- package/dist/remote/sdkHomeDownload.d.ts.map +1 -0
- package/dist/remote/sdkHomeDownload.js +208 -0
- package/dist/remote/sdkHomeDownload.js.map +1 -0
- package/dist/remote/topsdkDownloadSdkConfig.d.ts.map +1 -1
- package/dist/remote/topsdkDownloadSdkConfig.js +11 -1
- package/dist/remote/topsdkDownloadSdkConfig.js.map +1 -1
- package/dist/shared/errors.d.ts +7 -0
- package/dist/shared/errors.d.ts.map +1 -0
- package/dist/shared/errors.js +16 -0
- package/dist/shared/errors.js.map +1 -0
- package/dist/shared/fileUtils.d.ts +5 -0
- package/dist/shared/fileUtils.d.ts.map +1 -0
- package/dist/shared/fileUtils.js +35 -0
- package/dist/shared/fileUtils.js.map +1 -0
- package/dist/shared/logger.d.ts +10 -0
- package/dist/shared/logger.d.ts.map +1 -0
- package/dist/shared/logger.js +37 -0
- package/dist/shared/logger.js.map +1 -0
- package/dist/shared/pathUtils.d.ts +4 -0
- package/dist/shared/pathUtils.d.ts.map +1 -0
- package/dist/shared/pathUtils.js +22 -0
- package/dist/shared/pathUtils.js.map +1 -0
- package/dist/shared/processRunner.d.ts +12 -0
- package/dist/shared/processRunner.d.ts.map +1 -0
- package/dist/shared/processRunner.js +31 -0
- package/dist/shared/processRunner.js.map +1 -0
- package/docs/AAB_CONVERTER_CLI_PLAN.md +392 -0
- package/docs/API.md +246 -32
- package/docs/CLI.md +292 -0
- package/docs/INTEGRATION.md +116 -0
- package/docs/MCP.md +86 -0
- package/docs/README.md +19 -10
- package/docs/{api → archive/api}/downloadSDKConfig.md +8 -6
- package/docs/{api → archive/api}/getChannelConfig-meetgames.md +1 -1
- package/docs/archive/ios-migration.md +2139 -0
- 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
- package/docs/{ → archive/product/}/351/234/200/346/261/202/346/226/207/346/241/243.md +15 -14
- package/logs/convert-20260622-155037.log +5 -0
- package/logs/convert-20260622-155226.log +6 -0
- package/meetsdk-android.json +2 -1
- package/meetsdk-ios.json +15 -0
- package/package.json +10 -36
- package/scripts/package-aab-cli-win.mjs +193 -0
- package/src/aab-converter/aab-entry.ts +48 -0
- package/src/aab-converter/apksExtractor.ts +119 -0
- package/src/aab-converter/bundletoolRunner.ts +63 -0
- package/src/aab-converter/cliArgs.ts +194 -0
- package/src/aab-converter/convertAabToApk.ts +81 -0
- package/src/aab-converter/resourcePaths.ts +43 -0
- package/src/aab-converter/signingOptions.ts +29 -0
- package/src/aab-converter/types.ts +26 -0
- package/src/android/adapter.ts +9 -0
- package/src/android/assembleIntegrationJson.ts +33 -0
- package/src/android/detect.ts +132 -0
- package/src/android/downloadGoogleServicesJson.ts +56 -0
- package/src/android/gradle.ts +116 -0
- package/src/android/manifest.ts +50 -0
- package/src/android/meetSdkRemoteGradle.ts +837 -0
- package/src/cli.ts +488 -0
- package/src/config/fetchConfigWrite.ts +30 -0
- package/src/config/loadAndroidIntegration.ts +41 -0
- package/src/config/loadManifest.ts +40 -0
- package/src/config/meetSdkDefaultConfig.ts +99 -0
- package/src/config/meetSdkIosConfig.ts +87 -0
- package/src/config/meetSdkRemoteConfig.ts +1211 -0
- package/src/config/topsdkFeatureModules.ts +92 -0
- package/src/contracts/types.ts +121 -0
- package/src/core/doctor.ts +485 -0
- package/src/core/patch.ts +64 -0
- package/src/core/pipeline.ts +107 -0
- package/src/core/platform.ts +47 -0
- package/src/core/previewPatches.ts +23 -0
- package/src/core/reporter.ts +24 -0
- package/src/core/workspace.ts +29 -0
- package/src/entry.ts +7 -0
- package/src/index.ts +133 -0
- package/src/ios/channelConfig.ts +128 -0
- package/src/ios/codeUtils.ts +160 -0
- package/src/ios/detect.ts +105 -0
- package/src/ios/entitlements.ts +61 -0
- package/src/ios/fileManager.ts +48 -0
- package/src/ios/infoPlist.ts +55 -0
- package/src/ios/integrate.ts +516 -0
- package/src/ios/pbxprojEditor.ts +383 -0
- package/src/ios/pluginConfig.ts +97 -0
- package/src/ios/reserved.ts +8 -0
- package/src/ios/sdkBundle.ts +36 -0
- package/src/ios/template.ts +36 -0
- package/src/ios/types.ts +65 -0
- package/src/mcp/server.ts +170 -0
- package/src/mcp/service.ts +222 -0
- package/src/mcp-entry.ts +7 -0
- package/src/ops/fileStore.ts +56 -0
- package/src/ops/handlers.ts +304 -0
- package/src/remote/fetchJson.ts +22 -0
- package/src/remote/sdkHomeDownload.ts +274 -0
- package/src/remote/topsdkDownloadSdkConfig.ts +93 -0
- package/src/remote/topsdkGetSdkConfig.ts +122 -0
- package/src/remote/topsdkSign.ts +10 -0
- package/src/shared/errors.ts +16 -0
- package/src/shared/fileUtils.ts +41 -0
- package/src/shared/logger.ts +49 -0
- package/src/shared/pathUtils.ts +24 -0
- package/src/shared/processRunner.ts +43 -0
- package/test-projects/README.md +51 -0
- package/test-projects/_preview/pipeline.patch +281 -0
- package/tests/aab-converter.test.ts +213 -0
- package/tests/assemble.test.ts +12 -0
- package/tests/doctor.test.ts +89 -0
- package/tests/downloadGoogleServicesJson.test.ts +47 -0
- package/tests/fetch-remote.test.ts +23 -0
- package/tests/fetchConfigOverrides.test.ts +28 -0
- package/tests/fetchConfigWrite.test.ts +54 -0
- package/tests/gradle.test.ts +33 -0
- package/tests/integration-json.test.ts +29 -0
- package/tests/ios.codeUtils.test.ts +23 -0
- package/tests/ios.sdkBundle.test.ts +16 -0
- package/tests/loadManifest.test.ts +15 -0
- package/tests/manifest-xml.test.ts +30 -0
- package/tests/mcp.e2e.ts +217 -0
- package/tests/mcp.service.test.ts +53 -0
- package/tests/meetSdkRemoteConfig.test.ts +456 -0
- package/tests/meetSdkRemoteGradle.test.ts +414 -0
- package/tests/pipeline.android.test.ts +96 -0
- package/tests/pipeline.integration-json.test.ts +58 -0
- package/tests/pipeline.ios.test.ts +385 -0
- package/tests/pipeline.preview.patch.test.ts +85 -0
- package/tests/platformSelection.test.ts +77 -0
- package/tests/sdkHomeDownload.test.ts +124 -0
- package/tests/sdkVersionConfig.test.ts +130 -0
- package/tests/test-projects-hosts.test.ts +78 -0
- package/tests/topsdk.test.ts +53 -0
- package/tests/topsdkDownloadSdkConfig.test.ts +81 -0
- package/tests/topsdkFeatureModules.test.ts +116 -0
- package/tsconfig.json +19 -0
- package/vitest.config.ts +9 -0
- package/vitest.mcp.config.ts +11 -0
- package/bundled/android/sample.txt +0 -1
- package/docs/ANDROID.md +0 -133
- package/docs/CURSOR-MCP-SETUP.md +0 -72
- package/docs/MCP-SKILL.md +0 -63
- package/fixtures/api-samples/getChannelConfig-meetgames.sample.json +0 -123
- package/fixtures/meetsdk-remote-config.download-shape.json +0 -20
- package/fixtures/meetsdk-remote-config.mock.json +0 -69
- package/fixtures/recipes/android-default.fixture.yaml +0 -15
- package/fixtures/recipes/android-integration.fixture.json +0 -29
- package/fixtures/topsdk-config-reference.json +0 -39
- /package/docs/{api → archive/api}/getSDKConfig.md +0 -0
package/src/cli.ts
ADDED
|
@@ -0,0 +1,488 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { loadManifestFile } from "./config/loadManifest.js";
|
|
4
|
+
import { buildWorkspaceContext, resolvePackageRoot } from "./core/workspace.js";
|
|
5
|
+
import { runPipeline } from "./core/pipeline.js";
|
|
6
|
+
import {
|
|
7
|
+
detectSinglePlatform,
|
|
8
|
+
manifestForPlatform,
|
|
9
|
+
platformContext,
|
|
10
|
+
} from "./core/platform.js";
|
|
11
|
+
import { runDoctor } from "./core/doctor.js";
|
|
12
|
+
import { clearPreviewPatchFilesIfTargetInside } from "./core/previewPatches.js";
|
|
13
|
+
import { printReport } from "./core/reporter.js";
|
|
14
|
+
import { defaultTopSdkBaseUrl, fetchTopSdkDownloadSdkConfig, type TopSdkApiEnv } from "./remote/topsdkDownloadSdkConfig.js";
|
|
15
|
+
import {
|
|
16
|
+
defaultSdkHomeApiBaseUrl,
|
|
17
|
+
downloadIosSdkToBundled,
|
|
18
|
+
DEFAULT_IOS_SDK_PACKAGE_TYPE,
|
|
19
|
+
} from "./remote/sdkHomeDownload.js";
|
|
20
|
+
import type { Manifest } from "./contracts/types.js";
|
|
21
|
+
import {
|
|
22
|
+
validateDownloadSdkConfigForWrite,
|
|
23
|
+
writeDownloadSdkConfigRaw,
|
|
24
|
+
} from "./config/fetchConfigWrite.js";
|
|
25
|
+
import {
|
|
26
|
+
MEETSDK_REMOTE_CONFIG_FILENAME,
|
|
27
|
+
readMeetSdkRemoteChannel,
|
|
28
|
+
tryParseAsMeetSdkRemoteConfig,
|
|
29
|
+
type MeetSdkRemoteConfig,
|
|
30
|
+
} from "./config/meetSdkRemoteConfig.js";
|
|
31
|
+
|
|
32
|
+
const EXIT = {
|
|
33
|
+
OK: 0,
|
|
34
|
+
INVALID_ARGS: 2,
|
|
35
|
+
PROJECT_ERROR: 3,
|
|
36
|
+
CONFIG_ERROR: 4,
|
|
37
|
+
PIPELINE_ERROR: 5,
|
|
38
|
+
NETWORK_ERROR: 6,
|
|
39
|
+
} as const;
|
|
40
|
+
|
|
41
|
+
function fail(message: string, code: number): never {
|
|
42
|
+
console.error(`[meetgames] ERROR: ${message}`);
|
|
43
|
+
process.exit(code);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
interface Parsed {
|
|
47
|
+
command: string;
|
|
48
|
+
projectRoot: string;
|
|
49
|
+
projectRootProvided: boolean;
|
|
50
|
+
dryRun: boolean;
|
|
51
|
+
verbose: boolean;
|
|
52
|
+
reportFile: string;
|
|
53
|
+
patchFile: string;
|
|
54
|
+
help: boolean;
|
|
55
|
+
topsdkEnv: TopSdkApiEnv;
|
|
56
|
+
topsdkAppId: string;
|
|
57
|
+
topsdkAppSecret: string;
|
|
58
|
+
topsdkChannelType: string;
|
|
59
|
+
topsdkPackageName: string;
|
|
60
|
+
appTarget: string;
|
|
61
|
+
sdkHomeApiBaseUrl: string;
|
|
62
|
+
iosSdkVersion: string;
|
|
63
|
+
iosSdkPlugins: string;
|
|
64
|
+
iosSdkPackageType: string;
|
|
65
|
+
iosSdkOutputDir: string;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function printHelp(): void {
|
|
69
|
+
console.log(`
|
|
70
|
+
meetgames — Android-first integration CLI (TypeScript)
|
|
71
|
+
|
|
72
|
+
Usage:
|
|
73
|
+
meetgames integrate [--project-root <path>] [--app-target <target>] [--dry-run] [--verbose] [--patch-file <path>]
|
|
74
|
+
meetgames fetch-config [--app-id ID] [--app-secret SECRET] [--channel-type TYPE] [--env prod|pre|test] [--project-root <path>] [--verbose]
|
|
75
|
+
meetgames download-ios-sdk [--version VERSION] [--plugins csv] [--package-type native|unity|cocos] [--sdk-api-base-url URL] [--output-dir <path>] [--verbose]
|
|
76
|
+
meetgames setup [--app-id ID] [--app-secret SECRET] [--channel-type TYPE] [--env prod|pre|test] [--project-root <path>] [--app-target <target>] [--dry-run] [--verbose] [--patch-file <path>]
|
|
77
|
+
meetgames doctor [--app-id ID] [--app-secret SECRET] [--channel-type TYPE] [--env prod|pre|test] --project-root <path> [--app-target <target>] [--verbose]
|
|
78
|
+
|
|
79
|
+
Notes:
|
|
80
|
+
- Source code is TypeScript only; published bin runs compiled output under dist/.
|
|
81
|
+
- iOS: copies frameworks to topSDK/, edits project.pbxproj & Info.plist, injects AppDelegate (.m/.mm) when present (topsdk-tool-ios parity).
|
|
82
|
+
- fetch-config calls TOPSDK downloadSDKConfig and writes the response body unchanged to <project-root>/meetsdk-remote-config.json.
|
|
83
|
+
- setup runs fetch-config then integrate in one process (same flags as both subcommands).
|
|
84
|
+
- doctor / setup / integrate require --project-root and auto-select exactly one platform from that root.
|
|
85
|
+
- doctor first fetches downloadSDKConfig to <project-root>/meetsdk-remote-config.json, then validates the detected platform integration.
|
|
86
|
+
- integrate uses recipes/integrate-default.yaml, filtered to the detected platform before execution.
|
|
87
|
+
- iOS SDK is read from bundled/ios-sdk/ in the tool package (native iOS only).
|
|
88
|
+
- Android integrate/doctor reads the latest SDK version from sdk-home and stores it in meetsdk-android.json.
|
|
89
|
+
- download-ios-sdk calls sdk-home getDownLoadUrl, saves the iOS SDK zip under bundled/ios-sdk/, extracts it there, and stores the resolved version in meetsdk-ios.json.
|
|
90
|
+
- fetch-config requires --app-secret (or TOPSDK_APP_SECRET) to sign downloadSDKConfig; appSecret is never read from meetsdk-remote-config.json.
|
|
91
|
+
- When analytics.firebase is enabled, Android downloads google-services.json to the detected/selected app module; iOS downloads GoogleService-Info.plist next to Info.plist.
|
|
92
|
+
- fetch-config writes to <project-root>/meetsdk-remote-config.json.
|
|
93
|
+
- fetch-config requires --channel-type (or channel in local meetsdk-remote-config.json / TOPSDK_CHANNEL_TYPE); not inferred from Android/iOS detection.
|
|
94
|
+
- Recipe op gradle.applyMeetSdkRemoteConfig reads meetsdk-remote-config.json and writes TOPSDK marker blocks to root + app build.gradle (same idea as sdk-integration-agent gradleEditor).
|
|
95
|
+
|
|
96
|
+
Options (global where noted):
|
|
97
|
+
--project-root Host project directory (default: cwd); for iOS it may point to the project root or directly to a .xcodeproj directory
|
|
98
|
+
--app-target App target to integrate/check: Android application Gradle module (e.g. :app or launcher) or iOS App target name
|
|
99
|
+
--app-id fetch-config override; default reads topsdk.appId from meetsdk-remote-config.json
|
|
100
|
+
--app-secret fetch-config: required appSecret for downloadSDKConfig sign (or env TOPSDK_APP_SECRET)
|
|
101
|
+
--channel-type fetch-config: channelType query param for downloadSDKConfig (required unless channel in local file or TOPSDK_CHANNEL_TYPE)
|
|
102
|
+
--env fetch-config: prod(正式服,默认)| pre | test(开发调试)
|
|
103
|
+
TOPSDK_API_BASE_URL fetch-config: 覆盖 API 根地址(如 http://localhost:18080/ 联调本机 console)
|
|
104
|
+
MEETGAMES_SDK_HOME_API_BASE_URL 覆盖 sdk-home API 根地址(默认 https://business-api.meetgames.com);用于 Android 最新版本查询和 iOS SDK 下载
|
|
105
|
+
--sdk-api-base-url sdk-home API 根地址;用于 Android 最新版本查询和 iOS SDK 下载
|
|
106
|
+
--version download-ios-sdk: 指定 iOS SDK 版本;不传时从 /sdk/home/version 读取最新 iOS 版本
|
|
107
|
+
--plugins download-ios-sdk: 逗号分隔插件列表;默认与下载页 iOS 默认选择一致
|
|
108
|
+
--package-type download-ios-sdk: native(默认)| unity | cocos
|
|
109
|
+
--output-dir download-ios-sdk: 解压目标目录;默认 <package-root>/bundled/ios-sdk
|
|
110
|
+
--dry-run integrate: compute changes and patch preview without writing host project files (default is write to disk)
|
|
111
|
+
--report-file Write JSON report to path
|
|
112
|
+
--patch-file integrate: write unified diff to this path (works with --dry-run). If under <pkg>/test-projects/_preview/, all existing files in that dir are removed first so only the latest patch remains.
|
|
113
|
+
--verbose Verbose logs and full patch preview
|
|
114
|
+
|
|
115
|
+
Subcommand notes:
|
|
116
|
+
- integrate defaults to writing files; use --dry-run for CI preview / diff-only.
|
|
117
|
+
- setup: step 1 always writes meetsdk-remote-config.json; step 2 honors --dry-run / --patch-file / --report-file.
|
|
118
|
+
- fetch-config ignores --dry-run, --report-file, --patch-file (shared parser only).
|
|
119
|
+
`);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function parseArgv(argv: string[]): Parsed {
|
|
123
|
+
const out: Parsed = {
|
|
124
|
+
command: "",
|
|
125
|
+
projectRoot: process.cwd(),
|
|
126
|
+
projectRootProvided: false,
|
|
127
|
+
dryRun: false,
|
|
128
|
+
verbose: false,
|
|
129
|
+
reportFile: "",
|
|
130
|
+
patchFile: "",
|
|
131
|
+
help: false,
|
|
132
|
+
topsdkEnv: "prod",
|
|
133
|
+
topsdkAppId: "",
|
|
134
|
+
topsdkAppSecret: "",
|
|
135
|
+
topsdkChannelType: "",
|
|
136
|
+
topsdkPackageName: "",
|
|
137
|
+
appTarget: "",
|
|
138
|
+
sdkHomeApiBaseUrl: "",
|
|
139
|
+
iosSdkVersion: "",
|
|
140
|
+
iosSdkPlugins: "",
|
|
141
|
+
iosSdkPackageType: "",
|
|
142
|
+
iosSdkOutputDir: "",
|
|
143
|
+
};
|
|
144
|
+
const rest = argv.slice(2);
|
|
145
|
+
if (!rest.length || (rest[0]?.startsWith("-") ?? false)) {
|
|
146
|
+
out.help = true;
|
|
147
|
+
return out;
|
|
148
|
+
}
|
|
149
|
+
out.command = rest[0] ?? "";
|
|
150
|
+
for (let i = 1; i < rest.length; i += 1) {
|
|
151
|
+
const t = rest[i];
|
|
152
|
+
if (t === "--help" || t === "-h") {
|
|
153
|
+
out.help = true;
|
|
154
|
+
return out;
|
|
155
|
+
}
|
|
156
|
+
if (t === "--verbose") {
|
|
157
|
+
out.verbose = true;
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
if (t === "--dry-run") {
|
|
161
|
+
out.dryRun = true;
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
if (t === "--project-root") {
|
|
165
|
+
out.projectRoot = path.resolve(rest[i + 1] ?? "");
|
|
166
|
+
out.projectRootProvided = true;
|
|
167
|
+
i += 1;
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
if (t === "--app-target") {
|
|
171
|
+
out.appTarget = rest[i + 1] ?? "";
|
|
172
|
+
if (!out.appTarget || out.appTarget.startsWith("-")) fail("--app-target requires a target name", EXIT.INVALID_ARGS);
|
|
173
|
+
i += 1;
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
if (t === "--sdk-api-base-url") {
|
|
177
|
+
out.sdkHomeApiBaseUrl = rest[i + 1] ?? "";
|
|
178
|
+
if (!out.sdkHomeApiBaseUrl || out.sdkHomeApiBaseUrl.startsWith("-")) {
|
|
179
|
+
fail("--sdk-api-base-url requires a URL", EXIT.INVALID_ARGS);
|
|
180
|
+
}
|
|
181
|
+
i += 1;
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
if (t === "--version") {
|
|
185
|
+
out.iosSdkVersion = rest[i + 1] ?? "";
|
|
186
|
+
if (!out.iosSdkVersion || out.iosSdkVersion.startsWith("-")) fail("--version requires a version", EXIT.INVALID_ARGS);
|
|
187
|
+
i += 1;
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
if (t === "--plugins") {
|
|
191
|
+
out.iosSdkPlugins = rest[i + 1] ?? "";
|
|
192
|
+
if (!out.iosSdkPlugins || out.iosSdkPlugins.startsWith("-")) fail("--plugins requires a comma-separated list", EXIT.INVALID_ARGS);
|
|
193
|
+
i += 1;
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
if (t === "--package-type") {
|
|
197
|
+
out.iosSdkPackageType = rest[i + 1] ?? "";
|
|
198
|
+
if (!out.iosSdkPackageType || out.iosSdkPackageType.startsWith("-")) {
|
|
199
|
+
fail("--package-type requires native, unity, or cocos", EXIT.INVALID_ARGS);
|
|
200
|
+
}
|
|
201
|
+
i += 1;
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
if (t === "--output-dir") {
|
|
205
|
+
out.iosSdkOutputDir = path.resolve(rest[i + 1] ?? "");
|
|
206
|
+
if (!out.iosSdkOutputDir || rest[i + 1]?.startsWith("-")) fail("--output-dir requires a directory path", EXIT.INVALID_ARGS);
|
|
207
|
+
i += 1;
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
if (t === "--env") {
|
|
211
|
+
const v = (rest[i + 1] ?? "").toLowerCase();
|
|
212
|
+
i += 1;
|
|
213
|
+
if (v === "prod" || v === "pre" || v === "test") out.topsdkEnv = v;
|
|
214
|
+
else fail(`invalid --env (use prod, pre, or test): ${v}`, EXIT.INVALID_ARGS);
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
if (t === "--app-id") {
|
|
218
|
+
out.topsdkAppId = rest[i + 1] ?? "";
|
|
219
|
+
i += 1;
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
if (t === "--app-secret") {
|
|
223
|
+
out.topsdkAppSecret = rest[i + 1] ?? "";
|
|
224
|
+
i += 1;
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
if (t === "--channel-type") {
|
|
228
|
+
out.topsdkChannelType = rest[i + 1] ?? "";
|
|
229
|
+
i += 1;
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
if (t === "--report-file") {
|
|
233
|
+
out.reportFile = path.resolve(rest[i + 1] ?? "");
|
|
234
|
+
i += 1;
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
if (t === "--patch-file") {
|
|
238
|
+
out.patchFile = path.resolve(rest[i + 1] ?? "");
|
|
239
|
+
i += 1;
|
|
240
|
+
continue;
|
|
241
|
+
}
|
|
242
|
+
fail(`unsupported argument: ${t}`, EXIT.INVALID_ARGS);
|
|
243
|
+
}
|
|
244
|
+
return out;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const DEFAULT_MANIFEST = "recipes/integrate-default.yaml";
|
|
248
|
+
|
|
249
|
+
function requiresProjectRoot(command: string): boolean {
|
|
250
|
+
return command === "doctor" || command === "setup" || command === "integrate";
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function resolvedAppTarget(parsed: Parsed): string {
|
|
254
|
+
return parsed.appTarget;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function selectedPlatformContext(parsed: Parsed): {
|
|
258
|
+
platform: "android" | "ios";
|
|
259
|
+
ctx: ReturnType<typeof buildWorkspaceContext>;
|
|
260
|
+
selectedCtx: ReturnType<typeof buildWorkspaceContext>;
|
|
261
|
+
} {
|
|
262
|
+
const ctx = buildWorkspaceContext(parsed.projectRoot, resolvePackageRoot(), {
|
|
263
|
+
appTarget: resolvedAppTarget(parsed),
|
|
264
|
+
sdkHomeApiBaseUrl: parsed.sdkHomeApiBaseUrl || undefined,
|
|
265
|
+
});
|
|
266
|
+
const detected = detectSinglePlatform(ctx);
|
|
267
|
+
if (!detected.ok) fail(detected.error, EXIT.PROJECT_ERROR);
|
|
268
|
+
return {
|
|
269
|
+
platform: detected.platform,
|
|
270
|
+
ctx,
|
|
271
|
+
selectedCtx: platformContext(ctx, detected.platform),
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
async function cmdDoctor(parsed: Parsed): Promise<void> {
|
|
276
|
+
const { platform, selectedCtx } = selectedPlatformContext(parsed);
|
|
277
|
+
console.log("[meetgames] doctor: fetching remote config…");
|
|
278
|
+
await cmdFetchConfig(parsed);
|
|
279
|
+
console.log("[meetgames] doctor: checking project integration…");
|
|
280
|
+
const report = await runDoctor(selectedCtx, platform);
|
|
281
|
+
console.log(JSON.stringify(report, null, 2));
|
|
282
|
+
if (!report.ok) process.exit(EXIT.CONFIG_ERROR);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/** 固定缓存路径:宿主工程根目录下的 meetsdk-remote-config.json */
|
|
286
|
+
function meetSdkRemoteConfigCachePath(projectRoot: string): string {
|
|
287
|
+
return path.join(projectRoot, MEETSDK_REMOTE_CONFIG_FILENAME);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function readLocalMeetSdkRemoteConfig(projectRoot: string): MeetSdkRemoteConfig | null {
|
|
291
|
+
const configPath = meetSdkRemoteConfigCachePath(projectRoot);
|
|
292
|
+
if (!fs.existsSync(configPath)) return null;
|
|
293
|
+
try {
|
|
294
|
+
return tryParseAsMeetSdkRemoteConfig(JSON.parse(fs.readFileSync(configPath, "utf8")) as unknown);
|
|
295
|
+
} catch {
|
|
296
|
+
return null;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
async function cmdFetchConfig(parsed: Parsed): Promise<void> {
|
|
301
|
+
const out = meetSdkRemoteConfigCachePath(parsed.projectRoot);
|
|
302
|
+
const localConfig = readLocalMeetSdkRemoteConfig(parsed.projectRoot);
|
|
303
|
+
|
|
304
|
+
const appId = parsed.topsdkAppId || localConfig?.topsdk.appId || process.env.TOPSDK_APP_ID || "";
|
|
305
|
+
const appSecret = parsed.topsdkAppSecret || process.env.TOPSDK_APP_SECRET || "";
|
|
306
|
+
const channelType =
|
|
307
|
+
parsed.topsdkChannelType ||
|
|
308
|
+
(localConfig ? readMeetSdkRemoteChannel(localConfig) : "") ||
|
|
309
|
+
process.env.TOPSDK_CHANNEL_TYPE ||
|
|
310
|
+
"";
|
|
311
|
+
|
|
312
|
+
if (!appId || !channelType || !appSecret) {
|
|
313
|
+
fail(
|
|
314
|
+
`fetch-config requires --app-id, --app-secret, and --channel-type (or TOPSDK_APP_ID / TOPSDK_APP_SECRET / TOPSDK_CHANNEL_TYPE; appSecret is not read from ${MEETSDK_REMOTE_CONFIG_FILENAME}).`,
|
|
315
|
+
EXIT.INVALID_ARGS
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
const baseUrl = process.env.TOPSDK_API_BASE_URL?.trim() || defaultTopSdkBaseUrl(parsed.topsdkEnv);
|
|
320
|
+
|
|
321
|
+
let body: unknown;
|
|
322
|
+
let requestUrl: string;
|
|
323
|
+
let rawText: string;
|
|
324
|
+
try {
|
|
325
|
+
const res = await fetchTopSdkDownloadSdkConfig({
|
|
326
|
+
baseUrl,
|
|
327
|
+
appId,
|
|
328
|
+
appSecret,
|
|
329
|
+
channelType,
|
|
330
|
+
});
|
|
331
|
+
body = res.body;
|
|
332
|
+
requestUrl = res.url;
|
|
333
|
+
rawText = res.text;
|
|
334
|
+
} catch (e) {
|
|
335
|
+
fail(e instanceof Error ? e.message : String(e), EXIT.NETWORK_ERROR);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
const validation = validateDownloadSdkConfigForWrite(body);
|
|
339
|
+
if (!validation) {
|
|
340
|
+
fail(
|
|
341
|
+
`downloadSDKConfig response is not a valid ${MEETSDK_REMOTE_CONFIG_FILENAME} document (GET ${requestUrl})`,
|
|
342
|
+
EXIT.CONFIG_ERROR
|
|
343
|
+
);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
for (const warning of validation.warnings) {
|
|
347
|
+
console.warn(`[meetgames] warning: ${warning}`);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
writeDownloadSdkConfigRaw(out, rawText);
|
|
351
|
+
console.log(`[meetgames] wrote ${MEETSDK_REMOTE_CONFIG_FILENAME} (downloadSDKConfig response, unmodified): ${out}`);
|
|
352
|
+
if (parsed.verbose) {
|
|
353
|
+
console.log(`[meetgames] GET ${requestUrl}`);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
async function cmdIntegrate(parsed: Parsed): Promise<void> {
|
|
358
|
+
const manifestPath = path.join(resolvePackageRoot(), DEFAULT_MANIFEST);
|
|
359
|
+
if (!fs.existsSync(manifestPath)) {
|
|
360
|
+
fail(`config file not found: ${manifestPath}`, EXIT.PROJECT_ERROR);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
let manifest: Manifest;
|
|
364
|
+
try {
|
|
365
|
+
manifest = loadManifestFile(manifestPath);
|
|
366
|
+
} catch (e) {
|
|
367
|
+
fail(e instanceof Error ? e.message : String(e), EXIT.CONFIG_ERROR);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
const { platform, ctx, selectedCtx } = selectedPlatformContext(parsed);
|
|
371
|
+
const selectedManifest = manifestForPlatform(manifest, platform);
|
|
372
|
+
const { report, patch, binaryCopies } = await runPipeline(selectedCtx, selectedManifest, { dryRun: parsed.dryRun });
|
|
373
|
+
printReport(report, patch, parsed.verbose);
|
|
374
|
+
if (binaryCopies.length) {
|
|
375
|
+
console.log("[meetgames] planned binary copies:");
|
|
376
|
+
for (const c of binaryCopies) {
|
|
377
|
+
console.log(` ${c.fromAbs} -> ${path.join(ctx.projectRoot, c.relTo)}`);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
if (parsed.reportFile) {
|
|
382
|
+
fs.mkdirSync(path.dirname(parsed.reportFile), { recursive: true });
|
|
383
|
+
fs.writeFileSync(parsed.reportFile, JSON.stringify({ ...report, binaryCopies }, null, 2), "utf8");
|
|
384
|
+
}
|
|
385
|
+
if (parsed.patchFile) {
|
|
386
|
+
const patchAbs = path.resolve(parsed.patchFile);
|
|
387
|
+
clearPreviewPatchFilesIfTargetInside(resolvePackageRoot(), patchAbs);
|
|
388
|
+
fs.mkdirSync(path.dirname(patchAbs), { recursive: true });
|
|
389
|
+
fs.writeFileSync(patchAbs, patch || "# no text changes\n", "utf8");
|
|
390
|
+
console.log(`[meetgames] wrote patch file: ${patchAbs}`);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
if (report.errors.length) {
|
|
394
|
+
process.exit(EXIT.PIPELINE_ERROR);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
async function cmdSetup(parsed: Parsed): Promise<void> {
|
|
399
|
+
selectedPlatformContext(parsed);
|
|
400
|
+
console.log("[meetgames] setup: fetching remote config…");
|
|
401
|
+
await cmdFetchConfig(parsed);
|
|
402
|
+
console.log("[meetgames] setup: integrating project…");
|
|
403
|
+
await cmdIntegrate(parsed);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
async function cmdDownloadIosSdk(parsed: Parsed): Promise<void> {
|
|
407
|
+
const baseUrl =
|
|
408
|
+
parsed.sdkHomeApiBaseUrl || process.env.MEETGAMES_SDK_HOME_API_BASE_URL?.trim() || defaultSdkHomeApiBaseUrl;
|
|
409
|
+
const packageType = parsed.iosSdkPackageType || DEFAULT_IOS_SDK_PACKAGE_TYPE;
|
|
410
|
+
if (!["native", "unity", "cocos"].includes(packageType)) {
|
|
411
|
+
fail(`invalid --package-type (use native, unity, or cocos): ${packageType}`, EXIT.INVALID_ARGS);
|
|
412
|
+
}
|
|
413
|
+
const plugins = parsed.iosSdkPlugins
|
|
414
|
+
? parsed.iosSdkPlugins
|
|
415
|
+
.split(",")
|
|
416
|
+
.map((s) => s.trim())
|
|
417
|
+
.filter(Boolean)
|
|
418
|
+
: undefined;
|
|
419
|
+
if (parsed.iosSdkPlugins && !plugins?.length) {
|
|
420
|
+
fail("--plugins must contain at least one plugin", EXIT.INVALID_ARGS);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
console.log("[meetgames] download-ios-sdk: resolving sdk-home iOS SDK…");
|
|
424
|
+
let result;
|
|
425
|
+
try {
|
|
426
|
+
result = await downloadIosSdkToBundled(resolvePackageRoot(), {
|
|
427
|
+
baseUrl,
|
|
428
|
+
version: parsed.iosSdkVersion || undefined,
|
|
429
|
+
plugins,
|
|
430
|
+
packageType,
|
|
431
|
+
outputDir: parsed.iosSdkOutputDir || undefined,
|
|
432
|
+
});
|
|
433
|
+
} catch (e) {
|
|
434
|
+
fail(e instanceof Error ? e.message : String(e), EXIT.NETWORK_ERROR);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
console.log(`[meetgames] downloaded iOS SDK ${result.version} zip: ${result.zipPath}`);
|
|
438
|
+
console.log(`[meetgames] extracted iOS SDK to: ${result.extractDir}`);
|
|
439
|
+
console.log(`[meetgames] resolved iOS SDK root: ${result.resolvedSdkRoot}`);
|
|
440
|
+
console.log(`[meetgames] updated iOS SDK config: ${result.iosConfigPath}`);
|
|
441
|
+
if (parsed.verbose) {
|
|
442
|
+
console.log(`[meetgames] GET ${result.versionUrl}`);
|
|
443
|
+
console.log(`[meetgames] GET ${result.downloadApiUrl}`);
|
|
444
|
+
console.log(`[meetgames] SDK zip URL: ${result.sdkZipUrl}`);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
export async function main(): Promise<void> {
|
|
449
|
+
const parsed = parseArgv(process.argv);
|
|
450
|
+
if (parsed.help) {
|
|
451
|
+
printHelp();
|
|
452
|
+
process.exit(EXIT.OK);
|
|
453
|
+
}
|
|
454
|
+
if (!parsed.command) {
|
|
455
|
+
printHelp();
|
|
456
|
+
process.exit(EXIT.INVALID_ARGS);
|
|
457
|
+
}
|
|
458
|
+
if (requiresProjectRoot(parsed.command) && !parsed.projectRootProvided) {
|
|
459
|
+
fail(`${parsed.command} requires --project-root`, EXIT.INVALID_ARGS);
|
|
460
|
+
}
|
|
461
|
+
if (!fs.existsSync(parsed.projectRoot) || !fs.statSync(parsed.projectRoot).isDirectory()) {
|
|
462
|
+
fail("project root does not exist or is not a directory", EXIT.INVALID_ARGS);
|
|
463
|
+
}
|
|
464
|
+
if (parsed.appTarget && !["doctor", "setup", "integrate"].includes(parsed.command)) {
|
|
465
|
+
fail("--app-target is only supported by doctor, setup, and integrate", EXIT.INVALID_ARGS);
|
|
466
|
+
}
|
|
467
|
+
resolvedAppTarget(parsed);
|
|
468
|
+
|
|
469
|
+
switch (parsed.command) {
|
|
470
|
+
case "doctor":
|
|
471
|
+
await cmdDoctor(parsed);
|
|
472
|
+
break;
|
|
473
|
+
case "integrate":
|
|
474
|
+
await cmdIntegrate(parsed);
|
|
475
|
+
break;
|
|
476
|
+
case "fetch-config":
|
|
477
|
+
await cmdFetchConfig(parsed);
|
|
478
|
+
break;
|
|
479
|
+
case "download-ios-sdk":
|
|
480
|
+
await cmdDownloadIosSdk(parsed);
|
|
481
|
+
break;
|
|
482
|
+
case "setup":
|
|
483
|
+
await cmdSetup(parsed);
|
|
484
|
+
break;
|
|
485
|
+
default:
|
|
486
|
+
fail(`unknown command: ${parsed.command}`, EXIT.INVALID_ARGS);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import {
|
|
4
|
+
tryParseAsMeetSdkRemoteConfig,
|
|
5
|
+
validateMeetSdkRemoteConfig,
|
|
6
|
+
type MeetSdkRemoteConfig,
|
|
7
|
+
} from "./meetSdkRemoteConfig.js";
|
|
8
|
+
|
|
9
|
+
export interface FetchConfigWriteValidation {
|
|
10
|
+
parsed: MeetSdkRemoteConfig;
|
|
11
|
+
warnings: string[];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** Shape check only; does not normalize or merge meetsdk-android.json. */
|
|
15
|
+
export function validateDownloadSdkConfigForWrite(body: unknown): FetchConfigWriteValidation | null {
|
|
16
|
+
const parsed = tryParseAsMeetSdkRemoteConfig(body);
|
|
17
|
+
if (!parsed) return null;
|
|
18
|
+
const warnings: string[] = [];
|
|
19
|
+
const validation = validateMeetSdkRemoteConfig(parsed);
|
|
20
|
+
if (!validation.ok) {
|
|
21
|
+
warnings.push(`remote-config missing or empty fields: ${validation.missing.join(", ")}`);
|
|
22
|
+
}
|
|
23
|
+
return { parsed, warnings };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Write gp-sdk response bytes as-is (no normalize / no meetsdk-android merge). */
|
|
27
|
+
export function writeDownloadSdkConfigRaw(outPath: string, rawText: string): void {
|
|
28
|
+
fs.mkdirSync(path.dirname(outPath), { recursive: true });
|
|
29
|
+
fs.writeFileSync(outPath, rawText, "utf8");
|
|
30
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { createRequire } from "node:module";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import type { ErrorObject } from "ajv";
|
|
6
|
+
import type { AndroidIntegrationDocument } from "../contracts/types.js";
|
|
7
|
+
|
|
8
|
+
const require = createRequire(import.meta.url);
|
|
9
|
+
const Ajv: typeof import("ajv").default = require("ajv") as typeof import("ajv").default;
|
|
10
|
+
const addFormats = require("ajv-formats") as (ajv: import("ajv").default) => import("ajv").default;
|
|
11
|
+
|
|
12
|
+
const ajv = new Ajv({ allErrors: true, strict: true });
|
|
13
|
+
addFormats(ajv);
|
|
14
|
+
|
|
15
|
+
let validateCached: ReturnType<typeof ajv.compile<AndroidIntegrationDocument>> | undefined;
|
|
16
|
+
|
|
17
|
+
function getValidator() {
|
|
18
|
+
if (validateCached) return validateCached;
|
|
19
|
+
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
20
|
+
const schemaPath = path.join(here, "..", "..", "schema", "android-integration.schema.json");
|
|
21
|
+
const raw = JSON.parse(fs.readFileSync(schemaPath, "utf8")) as object;
|
|
22
|
+
validateCached = ajv.compile<AndroidIntegrationDocument>(raw);
|
|
23
|
+
return validateCached;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function parseAndroidIntegrationJson(data: unknown): AndroidIntegrationDocument {
|
|
27
|
+
const validate = getValidator();
|
|
28
|
+
if (!validate(data)) {
|
|
29
|
+
const msg =
|
|
30
|
+
validate.errors?.map((e: ErrorObject) => `${e.instancePath} ${e.message}`).join("; ") ||
|
|
31
|
+
"invalid android integration json";
|
|
32
|
+
throw new Error(`Android integration validation failed: ${msg}`);
|
|
33
|
+
}
|
|
34
|
+
return data as AndroidIntegrationDocument;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function loadAndroidIntegrationFile(filePath: string): AndroidIntegrationDocument {
|
|
38
|
+
const text = fs.readFileSync(filePath, "utf8");
|
|
39
|
+
const data: unknown = JSON.parse(text);
|
|
40
|
+
return parseAndroidIntegrationJson(data);
|
|
41
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { createRequire } from "node:module";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import type { ErrorObject } from "ajv";
|
|
6
|
+
import yaml from "js-yaml";
|
|
7
|
+
import type { Manifest } from "../contracts/types.js";
|
|
8
|
+
|
|
9
|
+
const require = createRequire(import.meta.url);
|
|
10
|
+
const Ajv: typeof import("ajv").default = require("ajv") as typeof import("ajv").default;
|
|
11
|
+
const addFormats = require("ajv-formats") as (ajv: import("ajv").default) => import("ajv").default;
|
|
12
|
+
|
|
13
|
+
const ajv = new Ajv({ allErrors: true, strict: true });
|
|
14
|
+
addFormats(ajv);
|
|
15
|
+
|
|
16
|
+
let validateCached: ReturnType<typeof ajv.compile<Manifest>> | undefined;
|
|
17
|
+
|
|
18
|
+
function getValidator() {
|
|
19
|
+
if (validateCached) return validateCached;
|
|
20
|
+
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
21
|
+
const schemaPath = path.join(here, "..", "..", "schema", "manifest.schema.json");
|
|
22
|
+
const raw = JSON.parse(fs.readFileSync(schemaPath, "utf8")) as object;
|
|
23
|
+
validateCached = ajv.compile<Manifest>(raw);
|
|
24
|
+
return validateCached;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function loadManifestFile(filePath: string): Manifest {
|
|
28
|
+
const text = fs.readFileSync(filePath, "utf8");
|
|
29
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
30
|
+
const data: unknown =
|
|
31
|
+
ext === ".yaml" || ext === ".yml" ? yaml.load(text) : JSON.parse(text);
|
|
32
|
+
const validate = getValidator();
|
|
33
|
+
if (!validate(data)) {
|
|
34
|
+
const msg =
|
|
35
|
+
validate.errors?.map((e: ErrorObject) => `${e.instancePath} ${e.message}`).join("; ") ||
|
|
36
|
+
"invalid manifest";
|
|
37
|
+
throw new Error(`Manifest validation failed: ${msg}`);
|
|
38
|
+
}
|
|
39
|
+
return data as Manifest;
|
|
40
|
+
}
|