@mpgd/cli 0.26.0 → 0.28.0

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.
@@ -40,12 +40,16 @@ export function normalizeConfiguredBuildTargets(configuredTargets) {
40
40
  }
41
41
  function resolveBuildTarget(target, configuredTargets) {
42
42
  const configuredWebTarget = isConfiguredWebTarget(target, configuredTargets);
43
- if (configuredWebTarget) {
43
+ const configuredMiniGameTarget = isConfiguredMiniGameTarget(target, configuredTargets);
44
+ if (configuredWebTarget || configuredMiniGameTarget) {
44
45
  assertDeploymentTargetName(target);
45
46
  }
46
47
  if (target === 'web' && configuredWebTarget) {
47
48
  return target;
48
49
  }
50
+ if (configuredMiniGameTarget) {
51
+ return target;
52
+ }
49
53
  const builtInTarget = normalizeBuiltInBuildTarget(target);
50
54
  if (builtInTarget !== undefined) {
51
55
  return builtInTarget;
@@ -55,6 +59,17 @@ function resolveBuildTarget(target, configuredTargets) {
55
59
  }
56
60
  return undefined;
57
61
  }
62
+ function isConfiguredMiniGameTarget(target, configuredTargets) {
63
+ if (!Object.prototype.hasOwnProperty.call(configuredTargets, target)) {
64
+ return false;
65
+ }
66
+ const configuredTarget = configuredTargets[target];
67
+ return typeof configuredTarget === 'object'
68
+ && configuredTarget !== null
69
+ && !Array.isArray(configuredTarget)
70
+ && 'kind' in configuredTarget
71
+ && configuredTarget.kind === 'wechat-minigame';
72
+ }
58
73
  function normalizeBuiltInBuildTarget(target) {
59
74
  if (target === 'browser' || target === 'web') {
60
75
  return 'web-preview';
@@ -5,6 +5,7 @@ export interface ProductionTargetReadinessInput {
5
5
  readonly gameRoot: string;
6
6
  readonly gameServicesUrl?: string;
7
7
  readonly targetPolicy?: ProductionTargetPolicy;
8
+ readonly environment?: NodeJS.ProcessEnv;
8
9
  }
9
10
  export interface ProductionTargetPolicy {
10
11
  readonly runtime: string;
@@ -1,6 +1,7 @@
1
1
  import { existsSync, readFileSync, realpathSync } from 'node:fs';
2
2
  import { isIP } from 'node:net';
3
3
  import path from 'node:path';
4
+ const wechatStagingAppId = 'wx0000000000000000';
4
5
  const ownerPathByTarget = {
5
6
  ait: 'wrapperApp',
6
7
  android: 'shellApp',
@@ -23,6 +24,7 @@ export function assertProductionTargetReadiness(input) {
23
24
  if (!isRecord(targetConfig)) {
24
25
  throw new Error(`Missing target configuration for ${input.target}.`);
25
26
  }
27
+ assertExperimentalTargetReadiness(input, targetConfig);
26
28
  const targetPolicy = targetConfig.authoritativeGameServices === false
27
29
  ? disableMonetization(input.targetPolicy)
28
30
  : input.targetPolicy;
@@ -83,6 +85,24 @@ export function assertProductionTargetReadiness(input) {
83
85
  assertAuthoritativeGameServicesUrl(input.gameServicesUrl, input.target);
84
86
  }
85
87
  }
88
+ function assertExperimentalTargetReadiness(input, targetConfig) {
89
+ if (targetConfig.experimental !== true) {
90
+ return;
91
+ }
92
+ const environment = input.environment ?? process.env;
93
+ if (environment.MPGD_ALLOW_EXPERIMENTAL_TARGET !== '1') {
94
+ throw new Error(`Production build for experimental target ${input.target} is blocked.\n`
95
+ + 'Set MPGD_ALLOW_EXPERIMENTAL_TARGET=1 only for platform validation builds.');
96
+ }
97
+ if (targetConfig.kind === 'wechat-minigame') {
98
+ const appId = environment.MPGD_WECHAT_APP_ID?.trim();
99
+ if (appId === undefined
100
+ || appId === wechatStagingAppId
101
+ || !/^wx[0-9a-f]{16}$/u.test(appId)) {
102
+ throw new Error('Production WeChat Mini Game builds require MPGD_WECHAT_APP_ID.');
103
+ }
104
+ }
105
+ }
86
106
  function disableMonetization(policy) {
87
107
  if (policy === undefined) {
88
108
  return undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mpgd/cli",
3
- "version": "0.26.0",
3
+ "version": "0.28.0",
4
4
  "description": "Gunshi CLI for mpgd-kit starter generation and target workflow orchestration.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -52,19 +52,19 @@
52
52
  "@types/node": "^24.0.0",
53
53
  "ttsc": "0.18.4",
54
54
  "typescript": "7.0.2",
55
- "@mpgd/adapter-ait": "0.11.0",
56
- "@mpgd/adapter-browser": "0.7.1",
57
- "@mpgd/adapter-capacitor": "0.4.7",
58
- "@mpgd/adapter-devvit": "0.9.4",
59
- "@mpgd/adapter-verse8": "0.3.2",
60
- "@mpgd/analytics": "0.3.10",
55
+ "@mpgd/adapter-ait": "0.12.1",
56
+ "@mpgd/adapter-browser": "0.7.3",
57
+ "@mpgd/adapter-capacitor": "0.4.9",
58
+ "@mpgd/adapter-devvit": "0.9.6",
59
+ "@mpgd/adapter-verse8": "0.3.4",
60
+ "@mpgd/analytics": "0.3.12",
61
+ "@mpgd/catalog": "0.7.2",
62
+ "@mpgd/i18n": "0.6.0",
63
+ "@mpgd/game-services": "0.14.0",
61
64
  "@mpgd/bridge": "0.9.0",
62
- "@mpgd/game-services": "0.13.0",
63
- "@mpgd/i18n": "0.5.6",
64
- "@mpgd/catalog": "0.7.0",
65
65
  "@mpgd/phaser-assets": "0.4.1",
66
- "@mpgd/target-config": "0.13.0",
67
- "@mpgd/platform": "0.10.0"
66
+ "@mpgd/platform": "0.12.0",
67
+ "@mpgd/target-config": "0.14.0"
68
68
  },
69
69
  "main": "./dist/index.js",
70
70
  "types": "./dist/index.d.ts",
@@ -277,13 +277,22 @@ and non-native flow checks:
277
277
  ```sh
278
278
  pnpm ait:wrapper:dev
279
279
  pnpm ait:wrapper:dev:plain
280
+ pnpm ait:wrapper:dev:sandbox
280
281
  ```
281
282
 
282
283
  `ait:wrapper:dev` loads the last game bundle copied by `pnpm build:ait` from the
283
284
  wrapper's `public/game` directory, so run `pnpm build:ait` again after game
284
285
  changes before opening it. The local mock deliberately keeps ads, promotions,
285
- and IAP unavailable; test those flows from the Apps in Toss console QR sandbox
286
- or a real device after uploading the `.ait` bundle.
286
+ and IAP unavailable.
287
+
288
+ `ait:wrapper:dev:sandbox` disables every local bridge and identity mock so the
289
+ official Apps in Toss Sandbox host can inject its native SDK. Supply any
290
+ game-owned staging endpoints, product SKUs, and server-authority configuration
291
+ as environment variables before starting it. Use this route for native identity,
292
+ one-time IAP, and Game Center tests; it is not a normal browser preview. The
293
+ Sandbox does not support ads, so upload a staging `.ait` bundle and use the
294
+ console `intoss-private://` QR in the Toss app for test-ad and final integration
295
+ checks.
287
296
 
288
297
  The starter includes `@ait-co/console-cli`, the Apps in Toss community console
289
298
  CLI, as a dev dependency for project-local console automation:
@@ -6,6 +6,7 @@
6
6
  "scripts": {
7
7
  "dev": "MPGD_AIT_LOCAL_MOCK=1 VITE_MPGD_AIT_MOCK_IDENTITY=1 vite --host 0.0.0.0",
8
8
  "dev:plain": "pnpm dev",
9
+ "dev:sandbox": "MPGD_AIT_LOCAL_MOCK=0 VITE_MPGD_AIT_MOCK_IDENTITY=0 vite --host 0.0.0.0",
9
10
  "build": "vite build && ait build",
10
11
  "ait:build": "ait build",
11
12
  "ait:deploy": "ait deploy --api-key \"$AIT_API_KEY\"",
@@ -1,21 +1,16 @@
1
1
  import './styles.css';
2
2
 
3
- import type { AitAdPlacementType } from '@mpgd/adapter-ait/ad-config';
4
3
  import { installAitHostBridge, type InstallAitHostBridgeOptions } from '@mpgd/adapter-ait/host';
5
4
  import { installAitSafeAreaCssVariables } from '@mpgd/adapter-ait/safe-area';
6
5
  import { mountAitGameBundle } from '@mpgd/adapter-ait/wrapper';
7
6
 
8
- declare const __MPGD_AIT_APP_NAME__: string;
9
- declare const __MPGD_AIT_AD_GROUP_IDS__: Readonly<Record<string, string>>;
10
- declare const __MPGD_AIT_AD_PLACEMENT_TYPES__: Readonly<
11
- Record<string, AitAdPlacementType>
12
- >;
7
+ import runtimeConfig from 'virtual:mpgd-ait-runtime-config';
13
8
 
14
9
  installAitSafeAreaCssVariables();
15
10
  installAitHostBridge({
16
- appName: __MPGD_AIT_APP_NAME__,
17
- adGroupIds: __MPGD_AIT_AD_GROUP_IDS__,
18
- adPlacementTypes: __MPGD_AIT_AD_PLACEMENT_TYPES__,
11
+ appName: runtimeConfig.appName,
12
+ adGroupIds: runtimeConfig.adGroupIds,
13
+ adPlacementTypes: runtimeConfig.adPlacementTypes,
19
14
  ...identityBridgeOptions(),
20
15
  });
21
16
 
@@ -0,0 +1,12 @@
1
+ declare module 'virtual:mpgd-ait-runtime-config' {
2
+ const runtimeConfig: Readonly<{
3
+ readonly appName: string;
4
+ readonly adGroupIds: Readonly<Record<string, string>>;
5
+ readonly adPlacementTypes: Readonly<Record<
6
+ string,
7
+ import('@mpgd/adapter-ait/ad-config').AitAdPlacementType
8
+ >>;
9
+ }>;
10
+
11
+ export default runtimeConfig;
12
+ }
@@ -5,7 +5,7 @@ import {
5
5
  type AitAdBridgeConfig,
6
6
  } from '@mpgd/adapter-ait/ad-config';
7
7
  import ttsc from '@ttsc/unplugin/vite';
8
- import { defineConfig } from 'vite';
8
+ import { defineConfig, type Plugin } from 'vite';
9
9
 
10
10
  const aitAppName = process.env.MPGD_AIT_APP_NAME?.trim() || '__GAME_NAME__';
11
11
  const aitAdConfig = readAitAdConfig(process.env.MPGD_AD_PLACEMENTS_FILE);
@@ -16,11 +16,6 @@ export default defineConfig(({ command, isPreview }) => {
16
16
  && process.env.MPGD_AIT_LOCAL_MOCK !== '0';
17
17
 
18
18
  return {
19
- define: {
20
- __MPGD_AIT_APP_NAME__: JSON.stringify(aitAppName),
21
- __MPGD_AIT_AD_GROUP_IDS__: JSON.stringify(aitAdConfig.adGroupIds),
22
- __MPGD_AIT_AD_PLACEMENT_TYPES__: JSON.stringify(aitAdConfig.adPlacementTypes),
23
- },
24
19
  // Browser-only local development must not pretend that native rewards or
25
20
  // purchases are available. Production and console builds use the real SDK.
26
21
  ...(enableLocalAitMock
@@ -33,7 +28,14 @@ export default defineConfig(({ command, isPreview }) => {
33
28
  }
34
29
  : {}),
35
30
  plugins: [
36
- ttsc({ project: 'tsconfig.json', plugins: false }),
31
+ createAitRuntimeConfigPlugin({
32
+ appName: aitAppName,
33
+ adGroupIds: aitAdConfig.adGroupIds,
34
+ adPlacementTypes: aitAdConfig.adPlacementTypes,
35
+ }),
36
+ ...(command === 'build'
37
+ ? [ttsc({ project: 'tsconfig.json', plugins: false })]
38
+ : []),
37
39
  ],
38
40
  build: {
39
41
  target: 'es2022',
@@ -43,6 +45,26 @@ export default defineConfig(({ command, isPreview }) => {
43
45
  };
44
46
  });
45
47
 
48
+ function createAitRuntimeConfigPlugin(input: Readonly<{
49
+ readonly appName: string;
50
+ readonly adGroupIds: Readonly<Record<string, string>>;
51
+ readonly adPlacementTypes: AitAdBridgeConfig['adPlacementTypes'];
52
+ }>): Plugin {
53
+ const publicId = 'virtual:mpgd-ait-runtime-config';
54
+ const resolvedId = `\0${publicId}`;
55
+ return {
56
+ name: 'mpgd-ait-runtime-config',
57
+ resolveId(id) {
58
+ return id === publicId ? resolvedId : null;
59
+ },
60
+ load(id) {
61
+ return id === resolvedId
62
+ ? `export default Object.freeze(${JSON.stringify(input)});\n`
63
+ : null;
64
+ },
65
+ };
66
+ }
67
+
46
68
  function readAitAdConfig(path: string | undefined): AitAdBridgeConfig {
47
69
  if (path === undefined || path.trim().length === 0) {
48
70
  return { adGroupIds: {}, adPlacementTypes: {} };
@@ -19,6 +19,7 @@
19
19
  "smoke:ait": "pnpm exec mpgd target smoke ait --targets-file ./mpgd.targets.json --kit-path ${MPGD_KIT_PATH:-__DEFAULT_KIT_PATH__}",
20
20
  "ait:wrapper:dev": "pnpm --dir apps/target-ait run dev",
21
21
  "ait:wrapper:dev:plain": "pnpm --dir apps/target-ait run dev:plain",
22
+ "ait:wrapper:dev:sandbox": "pnpm --dir apps/target-ait run dev:sandbox",
22
23
  "ait:console:login": "aitcc login",
23
24
  "ait:console:whoami": "aitcc whoami",
24
25
  "ait:console:init": "aitcc app init",
@@ -9,6 +9,8 @@ const validTargets = new Set<string>([
9
9
  'verse8',
10
10
  'telegram',
11
11
  'tauri',
12
+ 'wechat',
13
+ 'tiktok',
12
14
  ] satisfies readonly PlatformTarget[]);
13
15
 
14
16
  export interface RuntimeConfig {
@@ -20,6 +20,8 @@ const runtimeKinds = valueSet({
20
20
  'apps-in-toss': true,
21
21
  'devvit-web': true,
22
22
  'verse8-web': true,
23
+ 'wechat-minigame': true,
24
+ 'tiktok-minigame': true,
23
25
  } satisfies Record<TargetRuntimeKind, true>);
24
26
  const releaseProfiles = valueSet({
25
27
  web: true,
@@ -30,6 +32,8 @@ const releaseProfiles = valueSet({
30
32
  'apps-in-toss': true,
31
33
  'devvit': true,
32
34
  'verse8': true,
35
+ 'wechat-minigame': true,
36
+ 'tiktok-minigame': true,
33
37
  } satisfies Record<ReleaseProfile, true>);
34
38
  const storageSupportValues = valueSet({
35
39
  local: true,
@@ -138,6 +138,16 @@ export function resolveBuildGatewayModule(input: {
138
138
  return 'src/platform/buildGateways/verse8.ts';
139
139
  case 'microsoft-store':
140
140
  return 'src/platform/buildGateways/microsoftStore.ts';
141
+ case 'telegram':
142
+ case 'tauri':
143
+ throw new Error(
144
+ `Direct APP_TARGET=${input.target} builds are unavailable until its native platform gateway is installed.`,
145
+ );
146
+ case 'wechat':
147
+ case 'tiktok':
148
+ throw new Error(
149
+ `Direct APP_TARGET=${input.target} builds are unavailable until the native mini-game gateway and artifact emitter are installed.`,
150
+ );
141
151
  default:
142
152
  return 'src/platform/buildGateways/browser.ts';
143
153
  }