@mpgd/cli 0.52.0 → 0.54.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.
- package/dist/capacitor-shell-starter.d.ts +22 -0
- package/dist/capacitor-shell-starter.js +395 -18
- package/dist/deploy-planning.js +2 -2
- package/dist/index.js +122 -34
- package/dist/kit-upgrade.js +2 -2
- package/dist/native-build-info.json +2 -2
- package/dist/native-build-target.js +433 -2
- package/dist/notifications-starter.d.ts +75 -0
- package/dist/notifications-starter.js +522 -0
- package/dist/offline-playtest.js +1 -1
- package/dist/tooling-migration.js +1 -1
- package/package.json +16 -16
- package/templates/notifications-overlay/common/docs/NOTIFICATIONS.md +104 -0
- package/templates/notifications-overlay/common/notifications/deployment.json +8 -0
- package/templates/notifications-overlay/common/notifications/topics.json +222 -0
- package/templates/notifications-overlay/common/src/platform/capacitorNotifications.ts +59 -0
- package/templates/notifications-overlay/common/src/platform/notificationBackend.ts +157 -0
- package/templates/notifications-overlay/common/src/platform/notificationOpens.ts +60 -0
- package/templates/notifications-overlay/common/src/platform/notificationSettings.ts +761 -0
- package/templates/notifications-overlay/common/src/platform/notifications.ts +163 -0
- package/templates/notifications-overlay/common/tools/validate-notifications.ts +75 -0
- package/templates/notifications-overlay/lite/src/lite/notificationSettingsPanel.ts +200 -0
- package/templates/notifications-overlay/phaser/src/scenes/NotificationSettingsScene.ts +301 -0
- package/templates/phaser-game/apps/target-ait/src/main.ts +15 -0
- package/templates/phaser-game/apps/target-ait/src/runtime-config.types.ts +5 -0
- package/templates/phaser-game/apps/target-ait/vite.config.ts +22 -1
- package/templates/phaser-game/docs/MPGD_KIT_WORKFLOWS.md +14 -0
- package/templates/phaser-game/src/platform/gameServices.ts +3 -0
- package/templates/phaser-game/src/platform/serverSession.ts +66 -0
package/dist/index.js
CHANGED
|
@@ -24,6 +24,7 @@ import { cloudflarePagesDeploymentProfiles, hostedPwaDeploymentHosts, verifyHost
|
|
|
24
24
|
import { createMicrosoftStorePackageAcceptanceRuntime, runMicrosoftStorePackageAcceptance, } from './microsoft-store-package-acceptance.js';
|
|
25
25
|
import { runMicrosoftStorePackageGeneration } from './microsoft-store-package-generation.js';
|
|
26
26
|
import { defaultMpgdKitPath, initializeMicrosoftStoreStarter, listMicrosoftStoreInitializerTemplateFiles, prepareBaseGameTemplateFile, resolveMicrosoftStoreAdapterDependencyVersion, } from './microsoft-store-starter.js';
|
|
27
|
+
import { applyNotificationsStarter, notificationsProviderId, readGameCreateProviders, } from './notifications-starter.js';
|
|
27
28
|
import { applyKitUpgrade, planKitUpgrade } from './kit-upgrade.js';
|
|
28
29
|
import { applyToolingMigration, planToolingMigration } from './tooling-migration.js';
|
|
29
30
|
import { runMicrosoftStoreSubmissionPreflight } from './microsoft-store-submission.js';
|
|
@@ -58,6 +59,7 @@ const packageRoot = resolvePackageRoot(sourceDir);
|
|
|
58
59
|
const detectedKitRoot = resolveDefaultKitRoot();
|
|
59
60
|
const gameTemplateDir = path.resolve(packageRoot, 'templates/phaser-game');
|
|
60
61
|
const liteTemplateOverlayDir = path.resolve(packageRoot, 'templates/lite-overlay');
|
|
62
|
+
const notificationsTemplateDir = path.resolve(packageRoot, 'templates/notifications-overlay');
|
|
61
63
|
const supportedGameTemplates = ['phaser', 'lite'];
|
|
62
64
|
/** Lite renderers implemented today. dom/svg are reserved extension points in @mpgd/lite-shell. */
|
|
63
65
|
const supportedLiteRenderers = ['canvas'];
|
|
@@ -575,6 +577,10 @@ const gameCommand = defineI18n({
|
|
|
575
577
|
en: 'Lite template renderer. Only canvas is implemented today.',
|
|
576
578
|
ko: 'lite 템플릿 렌더러. 현재는 canvas만 지원합니다.',
|
|
577
579
|
},
|
|
580
|
+
providers: {
|
|
581
|
+
en: 'Comma-separated game providers. notifications adds the notifications v2 starter: the topic catalog, the backend settings, the Android, iOS, Apps in Toss, and Devvit wiring, and a notification settings screen.',
|
|
582
|
+
ko: '쉼표로 구분한 게임 provider. notifications는 알림 v2 스타터(토픽 카탈로그, 백엔드 설정, Android·iOS·앱인토스·Devvit 연결, 알림 설정 화면)를 추가합니다.',
|
|
583
|
+
},
|
|
578
584
|
}),
|
|
579
585
|
args: {
|
|
580
586
|
template: {
|
|
@@ -627,6 +633,11 @@ const gameCommand = defineI18n({
|
|
|
627
633
|
required: false,
|
|
628
634
|
description: 'Include the Microsoft Store PWA target and release workflow.',
|
|
629
635
|
},
|
|
636
|
+
providers: {
|
|
637
|
+
type: 'string',
|
|
638
|
+
required: false,
|
|
639
|
+
description: 'Comma-separated game providers. notifications adds the notifications v2 starter.',
|
|
640
|
+
},
|
|
630
641
|
},
|
|
631
642
|
run: (ctx) => {
|
|
632
643
|
const positionals = readLocalPositionals(ctx.positionals, ['game', 'create']);
|
|
@@ -649,6 +660,7 @@ const gameCommand = defineI18n({
|
|
|
649
660
|
...(kitPath === undefined ? {} : { kitPath }),
|
|
650
661
|
dryRun: ctx.values['dry-run'] === true,
|
|
651
662
|
microsoftStore: ctx.values['microsoft-store'] === true,
|
|
663
|
+
providers: readGameCreateProviders(readOptionalString(ctx.values.providers)),
|
|
652
664
|
});
|
|
653
665
|
},
|
|
654
666
|
}),
|
|
@@ -1597,8 +1609,8 @@ const targetCommand = defineI18n({
|
|
|
1597
1609
|
ko: '프로덕션 HTTPS Game Services 기본 URL.',
|
|
1598
1610
|
},
|
|
1599
1611
|
providers: {
|
|
1600
|
-
en: 'Comma-separated provider IDs. play-billing (Android) and storekit (iOS) install the kit store plugin into the shell and the game, register it with cap sync, and wire an unwired template gateway; other IDs are recorded only.',
|
|
1601
|
-
ko: '쉼표로 구분한 provider ID. play-billing(Android)과 storekit(iOS)은 키트 스토어 플러그인을 셸과 게임에 설치하고 cap sync로 등록하며, 연결되지 않은 템플릿 게이트웨이를 연결합니다. 다른 ID는 기록만 합니다.',
|
|
1612
|
+
en: 'Comma-separated provider IDs. play-billing (Android) and storekit (iOS) install the kit store plugin into the shell and the game, register it with cap sync, and wire an unwired template gateway; notifications installs the Firebase messaging and local notifications plugins for Android and iOS and wires a template gateway; other IDs are recorded only.',
|
|
1613
|
+
ko: '쉼표로 구분한 provider ID. play-billing(Android)과 storekit(iOS)은 키트 스토어 플러그인을 셸과 게임에 설치하고 cap sync로 등록하며, 연결되지 않은 템플릿 게이트웨이를 연결합니다. notifications는 Android·iOS용 Firebase 메시징과 로컬 알림 플러그인을 설치하고 템플릿 게이트웨이를 연결합니다. 다른 ID는 기록만 합니다.',
|
|
1602
1614
|
},
|
|
1603
1615
|
}),
|
|
1604
1616
|
args: {
|
|
@@ -1646,7 +1658,7 @@ const targetCommand = defineI18n({
|
|
|
1646
1658
|
providers: {
|
|
1647
1659
|
type: 'string',
|
|
1648
1660
|
required: false,
|
|
1649
|
-
description: 'Comma-separated provider IDs. play-billing (Android) and storekit (iOS) install the kit store plugin into the shell and the game, register it with cap sync, and wire an unwired template gateway; other IDs are recorded only.',
|
|
1661
|
+
description: 'Comma-separated provider IDs. play-billing (Android) and storekit (iOS) install the kit store plugin into the shell and the game, register it with cap sync, and wire an unwired template gateway; notifications installs the Firebase messaging and local notifications plugins for Android and iOS and wires a template gateway; other IDs are recorded only.',
|
|
1650
1662
|
},
|
|
1651
1663
|
},
|
|
1652
1664
|
run: (ctx) => {
|
|
@@ -1693,6 +1705,11 @@ const targetCommand = defineI18n({
|
|
|
1693
1705
|
+ `${plugin.versionSource === 'kit' ? ' and the game' : ''}`
|
|
1694
1706
|
+ `${dryRun ? '; cap add or cap sync registers it' : ''}.`);
|
|
1695
1707
|
}
|
|
1708
|
+
if (plan.notificationPlugins.length > 0) {
|
|
1709
|
+
console.info(`${dryRun ? 'Would install' : 'Installed'} notifications plugins `
|
|
1710
|
+
+ plan.notificationPlugins.map((plugin) => `${plugin.packageName}@${plugin.version}`).join(', ')
|
|
1711
|
+
+ ` into the shell for android and ios${dryRun ? '; cap add or cap sync registers them' : ''}.`);
|
|
1712
|
+
}
|
|
1696
1713
|
for (const warning of plan.warnings) {
|
|
1697
1714
|
console.warn(`Warning: ${warning}`);
|
|
1698
1715
|
}
|
|
@@ -2943,15 +2960,30 @@ function createGameApp(input) {
|
|
|
2943
2960
|
microsoftStore: input.microsoftStore,
|
|
2944
2961
|
});
|
|
2945
2962
|
const baseTemplateFiles = collectTemplateFiles(gameTemplateDir);
|
|
2946
|
-
const
|
|
2963
|
+
const templateFiles = input.template === 'lite'
|
|
2947
2964
|
? applyLiteTemplateOverlay(baseTemplateFiles, collectTemplateFiles(liteTemplateOverlayDir))
|
|
2948
2965
|
: baseTemplateFiles;
|
|
2966
|
+
const selectedTemplateFiles = input.providers?.includes(notificationsProviderId) === true
|
|
2967
|
+
? applyNotificationsStarter(templateFiles, {
|
|
2968
|
+
template: input.template === 'lite' ? 'lite' : 'phaser',
|
|
2969
|
+
common: collectTemplateFiles(path.join(notificationsTemplateDir, 'common')),
|
|
2970
|
+
phaser: collectTemplateFiles(path.join(notificationsTemplateDir, 'phaser')),
|
|
2971
|
+
lite: collectTemplateFiles(path.join(notificationsTemplateDir, 'lite')),
|
|
2972
|
+
})
|
|
2973
|
+
: templateFiles;
|
|
2949
2974
|
const files = selectedTemplateFiles.flatMap((file) => {
|
|
2950
2975
|
const prepared = prepareBaseGameTemplateFile({
|
|
2951
2976
|
relativePath: file.relativePath,
|
|
2952
2977
|
content: file.content,
|
|
2953
2978
|
});
|
|
2954
|
-
return prepared === undefined
|
|
2979
|
+
return prepared === undefined
|
|
2980
|
+
? []
|
|
2981
|
+
: [
|
|
2982
|
+
{
|
|
2983
|
+
...file,
|
|
2984
|
+
content: renderTemplate(prepared, context, file.relativePath),
|
|
2985
|
+
},
|
|
2986
|
+
];
|
|
2955
2987
|
});
|
|
2956
2988
|
if (input.dryRun) {
|
|
2957
2989
|
console.info(`Would create ${path.relative(process.cwd(), appDir) || appDir}:`);
|
|
@@ -3162,8 +3194,10 @@ function assertValidGameTitle(title) {
|
|
|
3162
3194
|
if (title.trim().length === 0) {
|
|
3163
3195
|
throw new Error('Game title cannot be empty.');
|
|
3164
3196
|
}
|
|
3165
|
-
|
|
3166
|
-
|
|
3197
|
+
// Quotes, backslashes, and `$` are allowed: JSON files and JS string literals
|
|
3198
|
+
// escape them. `<` would break the generated HTML title.
|
|
3199
|
+
if (/[\u0000-\u001f<>]/u.test(title)) {
|
|
3200
|
+
throw new Error('Game title cannot contain control characters or angle brackets.');
|
|
3167
3201
|
}
|
|
3168
3202
|
}
|
|
3169
3203
|
function collectTemplateFiles(templateDir) {
|
|
@@ -3185,28 +3219,79 @@ function walkTemplateDir(templateDir, relativeDir, files) {
|
|
|
3185
3219
|
});
|
|
3186
3220
|
}
|
|
3187
3221
|
}
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
|
|
3194
|
-
|
|
3195
|
-
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
|
|
3201
|
-
|
|
3202
|
-
|
|
3203
|
-
|
|
3204
|
-
|
|
3205
|
-
|
|
3206
|
-
|
|
3207
|
-
|
|
3222
|
+
/**
|
|
3223
|
+
* String replacements treat `$&`, `$'`, `` $` ``, and `$$` as patterns.
|
|
3224
|
+
* A function replacer inserts `value` literally.
|
|
3225
|
+
*/
|
|
3226
|
+
function replaceLiteral(content, placeholder, value) {
|
|
3227
|
+
return content.replaceAll(placeholder, () => value);
|
|
3228
|
+
}
|
|
3229
|
+
/** Interior of a JSON string, without the surrounding quotes. */
|
|
3230
|
+
function jsonStringContents(value) {
|
|
3231
|
+
return JSON.stringify(value).slice(1, -1);
|
|
3232
|
+
}
|
|
3233
|
+
/**
|
|
3234
|
+
* Placeholders substituted inside an existing JSON string. Structural
|
|
3235
|
+
* fragments (`__TSCONFIG_EXTENDS_LINE__` and the workspace include/exclude
|
|
3236
|
+
* lists) stay raw so their quotes remain JSON syntax.
|
|
3237
|
+
*/
|
|
3238
|
+
function jsonEscapedPlaceholder(placeholder) {
|
|
3239
|
+
return placeholder === '__GAME_TITLE__'
|
|
3240
|
+
|| placeholder === '__GAME_NAME__'
|
|
3241
|
+
|| placeholder === '__PACKAGE_NAME__'
|
|
3242
|
+
|| placeholder === '__DEVVIT_APP_NAME__'
|
|
3243
|
+
|| placeholder === '__DEFAULT_KIT_PATH__'
|
|
3244
|
+
|| placeholder === '__WORKSPACE_I18N_BUILD_PREFIX__'
|
|
3245
|
+
|| placeholder === '__TSCONFIG_ROOT_DIR__'
|
|
3246
|
+
|| placeholder === '__LEGAL_LAST_UPDATED__'
|
|
3247
|
+
|| placeholder === '__MPGD_DEPENDENCY_VERSION__'
|
|
3248
|
+
|| placeholder.startsWith('__MPGD_DEPENDENCY_VERSION_');
|
|
3249
|
+
}
|
|
3250
|
+
/** Interior of a single-quoted JS string. Backslash first, then quote. */
|
|
3251
|
+
function singleQuotedContents(value) {
|
|
3252
|
+
return value.replaceAll('\\', () => '\\\\').replaceAll("'", () => "\\'");
|
|
3253
|
+
}
|
|
3254
|
+
function renderTemplate(content, context, relativePath) {
|
|
3255
|
+
// `.webmanifest` is JSON. Hosted PWA deploy and Microsoft Store submission parse it.
|
|
3256
|
+
const jsonFile = relativePath.endsWith('.json') || relativePath.endsWith('.webmanifest');
|
|
3257
|
+
const typescriptFile = relativePath.endsWith('.ts');
|
|
3258
|
+
const insert = (placeholder, value) => {
|
|
3259
|
+
if (jsonFile && jsonEscapedPlaceholder(placeholder)) {
|
|
3260
|
+
return jsonStringContents(value);
|
|
3261
|
+
}
|
|
3262
|
+
// Template titles sit in single quotes (`title: '__GAME_TITLE__'`).
|
|
3263
|
+
if (typescriptFile && placeholder === '__GAME_TITLE__') {
|
|
3264
|
+
return singleQuotedContents(value);
|
|
3265
|
+
}
|
|
3266
|
+
return value;
|
|
3267
|
+
};
|
|
3268
|
+
const pairs = [
|
|
3269
|
+
[`from '@mpgd/cli/devvit'`, `from ${context.devvitOutputPluginImport}`],
|
|
3270
|
+
['__GAME_NAME__', context.gameName],
|
|
3271
|
+
['__DEVVIT_APP_NAME__', context.devvitAppName],
|
|
3272
|
+
// Longer than `__GAME_TITLE__`, so a title is not written into the literal name.
|
|
3273
|
+
['__GAME_TITLE_TS_LITERAL__', context.gameTitleTsLiteral],
|
|
3274
|
+
['__GAME_TITLE__', context.gameTitle],
|
|
3275
|
+
['__LEGAL_LAST_UPDATED__', context.legalLastUpdated],
|
|
3276
|
+
['__PACKAGE_NAME__', context.packageName],
|
|
3277
|
+
['__MPGD_DEPENDENCY_VERSION__', context.defaultDependencyVersion],
|
|
3278
|
+
['__TSCONFIG_EXTENDS_LINE__', context.tsconfigExtendsLine],
|
|
3279
|
+
['__TSCONFIG_ROOT_DIR__', context.tsconfigRootDir],
|
|
3280
|
+
['__TSCONFIG_WORKSPACE_INCLUDES__', context.tsconfigWorkspaceIncludes],
|
|
3281
|
+
['__TSCONFIG_WORKSPACE_EXCLUDES__', context.tsconfigWorkspaceExcludes],
|
|
3282
|
+
['__WORKSPACE_I18N_BUILD_PREFIX__', context.workspaceI18nBuildPrefix],
|
|
3283
|
+
['__DEFAULT_KIT_PATH__', context.defaultKitPath],
|
|
3284
|
+
['__RECOMMENDED_MATRIX_TARGETS__', context.recommendedMatrixTargets],
|
|
3285
|
+
['__PNPM_WORKSPACE_KIT_PACKAGES__', context.pnpmWorkspaceKitPackages],
|
|
3286
|
+
['__PASCAL_NAME__', context.pascalName],
|
|
3287
|
+
['__CAMEL_NAME__', context.camelName],
|
|
3288
|
+
];
|
|
3289
|
+
let rendered = content;
|
|
3290
|
+
for (const [placeholder, value] of pairs) {
|
|
3291
|
+
rendered = replaceLiteral(rendered, placeholder, insert(placeholder, value));
|
|
3292
|
+
}
|
|
3208
3293
|
for (const replacement of context.mpgdDependencyVersionReplacements) {
|
|
3209
|
-
rendered = rendered.
|
|
3294
|
+
rendered = replaceLiteral(rendered, replacement.placeholder, insert(replacement.placeholder, replacement.version));
|
|
3210
3295
|
}
|
|
3211
3296
|
return rendered;
|
|
3212
3297
|
}
|
|
@@ -3311,8 +3396,8 @@ function resolveStandaloneNativeBuild(targetsFile, requestedTarget, values) {
|
|
|
3311
3396
|
return undefined;
|
|
3312
3397
|
}
|
|
3313
3398
|
const candidate = path.resolve(gameRoot, configuredPath
|
|
3314
|
-
.replaceAll('${MPGD_GAME_ROOT}', gameRoot)
|
|
3315
|
-
.replaceAll('${MPGD_GAME_APP_ROOT}', gameRoot));
|
|
3399
|
+
.replaceAll('${MPGD_GAME_ROOT}', () => gameRoot)
|
|
3400
|
+
.replaceAll('${MPGD_GAME_APP_ROOT}', () => gameRoot));
|
|
3316
3401
|
let existingAncestor = candidate;
|
|
3317
3402
|
while (!existsSync(existingAncestor)) {
|
|
3318
3403
|
const parent = path.dirname(existingAncestor);
|
|
@@ -3371,8 +3456,14 @@ function createGameOwnedReleaseEnv(gameRoot, baseEnv) {
|
|
|
3371
3456
|
?? readSourceGitSha(gameRoot);
|
|
3372
3457
|
const appVersion = readOptionalString(baseEnv.APP_VERSION)
|
|
3373
3458
|
?? readGamePackageVersion(gameRoot);
|
|
3459
|
+
const notificationTopicsFile = readConfiguredPath(baseEnv.MPGD_NOTIFICATION_TOPICS_FILE);
|
|
3374
3460
|
return {
|
|
3375
3461
|
...monetizationFiles,
|
|
3462
|
+
// The Apps in Toss wrapper builds in its own directory; a relative path
|
|
3463
|
+
// names a file of the game, as `pnpm validate:notifications` reads it.
|
|
3464
|
+
...(notificationTopicsFile === undefined
|
|
3465
|
+
? {}
|
|
3466
|
+
: { MPGD_NOTIFICATION_TOPICS_FILE: path.resolve(gameRoot, notificationTopicsFile) }),
|
|
3376
3467
|
MPGD_SOURCE_GIT_SHA: sourceGitSha,
|
|
3377
3468
|
...(appVersion === undefined ? {} : { APP_VERSION: appVersion }),
|
|
3378
3469
|
};
|
|
@@ -3464,10 +3555,7 @@ function formatError(error) {
|
|
|
3464
3555
|
}
|
|
3465
3556
|
function replaceTargetTokens(value, context) {
|
|
3466
3557
|
if (typeof value === 'string') {
|
|
3467
|
-
return value
|
|
3468
|
-
.replaceAll('${MPGD_KIT_PATH}', context.kitPath)
|
|
3469
|
-
.replaceAll('${MPGD_GAME_ROOT}', context.gameRoot)
|
|
3470
|
-
.replaceAll('${MPGD_GAME_APP_ROOT}', context.gameRoot);
|
|
3558
|
+
return replaceLiteral(replaceLiteral(replaceLiteral(value, '${MPGD_KIT_PATH}', context.kitPath), '${MPGD_GAME_ROOT}', context.gameRoot), '${MPGD_GAME_APP_ROOT}', context.gameRoot);
|
|
3471
3559
|
}
|
|
3472
3560
|
if (Array.isArray(value)) {
|
|
3473
3561
|
return value.map((entry) => replaceTargetTokens(entry, context));
|
package/dist/kit-upgrade.js
CHANGED
|
@@ -278,8 +278,8 @@ function discoverGameManifests(gameRoot, notes, blockers, targetsFileName) {
|
|
|
278
278
|
continue;
|
|
279
279
|
}
|
|
280
280
|
const resolvedLocation = location
|
|
281
|
-
.replaceAll('${MPGD_GAME_ROOT}', targetsDirectory)
|
|
282
|
-
.replaceAll('${MPGD_GAME_APP_ROOT}', targetsDirectory);
|
|
281
|
+
.replaceAll('${MPGD_GAME_ROOT}', () => targetsDirectory)
|
|
282
|
+
.replaceAll('${MPGD_GAME_APP_ROOT}', () => targetsDirectory);
|
|
283
283
|
if (resolvedLocation.includes('$' + '{')) {
|
|
284
284
|
notes.push('Skipped target with unresolved path token: ' + location);
|
|
285
285
|
continue;
|