@mpgd/cli 0.57.0 → 0.59.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 +30 -0
- package/dist/capacitor-shell-starter.js +243 -11
- package/dist/index.js +170 -11
- package/dist/legal-site-output.d.ts +18 -0
- package/dist/legal-site-output.js +46 -1
- package/dist/native-build-info.json +2 -2
- package/dist/native-build-target.js +1663 -899
- package/dist/native-deploy-run.js +4 -0
- package/dist/native-shell-store-policy.d.ts +123 -0
- package/dist/native-shell-store-policy.js +816 -0
- package/dist/release-state.d.ts +2 -0
- package/dist/release-state.js +4 -2
- package/dist/testflight-submission.d.ts +9 -0
- package/dist/testflight-submission.js +69 -0
- package/package.json +17 -17
- package/templates/lite-overlay/agent/acceptance.md +1 -1
- package/templates/lite-overlay/agent/brief.md +1 -1
- package/templates/lite-overlay/src/lite/deleteAccountButton.ts +173 -0
- package/templates/lite-overlay/src/lite/restorePurchasesButton.ts +63 -0
- package/templates/lite-overlay/src/runtime/createGame.ts +8 -0
- package/templates/notifications-overlay/common/docs/NOTIFICATIONS.md +12 -6
- package/templates/notifications-overlay/common/notifications/deployment.json +2 -1
- package/templates/notifications-overlay/common/notifications/topics.json +3 -0
- package/templates/notifications-overlay/common/src/platform/capacitorNotifications.ts +8 -3
- package/templates/notifications-overlay/common/src/platform/notifications.ts +17 -3
- package/templates/phaser-game/README.md +72 -0
- package/templates/phaser-game/agent/acceptance.md +4 -1
- package/templates/phaser-game/agent/brief.md +14 -4
- package/templates/phaser-game/gitignore +11 -0
- package/templates/phaser-game/legal/delete-account.html +133 -0
- package/templates/phaser-game/legal/privacy.html +111 -12
- package/templates/phaser-game/legal/support.html +1 -0
- package/templates/phaser-game/legal/terms.html +1 -0
- package/templates/phaser-game/mpgd.game.json +3 -0
- package/templates/phaser-game/src/env.d.ts +6 -0
- package/templates/phaser-game/src/i18n/messages.ts +86 -1
- package/templates/phaser-game/src/main.ts +69 -8
- package/templates/phaser-game/src/platform/commerceAccountBinding.ts +39 -10
- package/templates/phaser-game/src/platform/deleteAccount.ts +334 -0
- package/templates/phaser-game/src/platform/gameServices.ts +55 -10
- package/templates/phaser-game/src/platform/guestIdentity.ts +301 -0
- package/templates/phaser-game/src/platform/restorePurchases.ts +205 -0
- package/templates/phaser-game/src/platform/serverSession.ts +8 -0
- package/templates/phaser-game/src/platform/sessionGatedServices.ts +217 -0
- package/templates/phaser-game/src/scenes/LobbyScene.ts +254 -1
- package/templates/phaser-game/src/scenes/PlayScene.ts +16 -6
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// node_modules/.cache/mpgd-native-build/tools/target/build-target.js
|
|
2
2
|
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
3
|
-
import { cpSync as cpSync4, existsSync as
|
|
3
|
+
import { cpSync as cpSync4, existsSync as existsSync25, lstatSync as lstatSync13, mkdirSync as mkdirSync13, mkdtempSync as mkdtempSync5, readdirSync as readdirSync8, readFileSync as readFileSync24, realpathSync as realpathSync10, rmSync as rmSync12, statSync as statSync7, writeFileSync as writeFileSync17 } from "node:fs";
|
|
4
4
|
import { tmpdir as tmpdir5 } from "node:os";
|
|
5
|
-
import { basename as basename5, dirname as
|
|
5
|
+
import { basename as basename5, dirname as dirname12, join as join16, relative as relative13, resolve as resolve18 } from "node:path";
|
|
6
6
|
import { loadEnv } from "vite";
|
|
7
7
|
|
|
8
8
|
// node_modules/.cache/mpgd-native-build/packages/cli/src/asset-pack-native-audit.js
|
|
@@ -4469,6 +4469,9 @@ var recordableNotificationPermissionStates = Object.freeze([
|
|
|
4469
4469
|
"denied",
|
|
4470
4470
|
"blocked"
|
|
4471
4471
|
]);
|
|
4472
|
+
var notificationShellRenderedAdvertisingTargets = Object.freeze([
|
|
4473
|
+
"android"
|
|
4474
|
+
]);
|
|
4472
4475
|
var referenceNotificationProviders = deepFreeze({
|
|
4473
4476
|
fcm: {
|
|
4474
4477
|
provider: "fcm",
|
|
@@ -8728,6 +8731,15 @@ var timestampPattern = new RegExp("^(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2})
|
|
|
8728
8731
|
// node_modules/.cache/mpgd-native-build/packages/game-services/src/notifications/fcm.js
|
|
8729
8732
|
var fcmAddressKinds = Object.freeze(["fcm-token"]);
|
|
8730
8733
|
var fcmReservedDataKeys = Object.freeze(["topic", "route"]);
|
|
8734
|
+
var fcmShellRenderedDataKeys = Object.freeze({
|
|
8735
|
+
render: "mpgd.render",
|
|
8736
|
+
title: "mpgd.title",
|
|
8737
|
+
body: "mpgd.body",
|
|
8738
|
+
channel: "mpgd.channel",
|
|
8739
|
+
actionTitle: "mpgd.actionTitle",
|
|
8740
|
+
optOutRoute: "mpgd.optOutRoute",
|
|
8741
|
+
expiresAt: "mpgd.expiresAt"
|
|
8742
|
+
});
|
|
8731
8743
|
var tokenRefreshMarginMs = 5 * 60 * 1e3;
|
|
8732
8744
|
var sha256K = new Uint32Array([
|
|
8733
8745
|
1116352408,
|
|
@@ -8800,6 +8812,10 @@ var sha256K = new Uint32Array([
|
|
|
8800
8812
|
var notificationHistoryRetentionDays = 30;
|
|
8801
8813
|
var maxHistoryWindowSeconds = notificationHistoryRetentionDays * 86400;
|
|
8802
8814
|
|
|
8815
|
+
// node_modules/.cache/mpgd-native-build/packages/game-services/src/account-deletion.js
|
|
8816
|
+
var staleErasingPendingMs = 60 * 60 * 1e3;
|
|
8817
|
+
var finalErasureDelayMs = 15 * 60 * 1e3;
|
|
8818
|
+
|
|
8803
8819
|
// node_modules/.cache/mpgd-native-build/packages/game-services/src/notifications/store.js
|
|
8804
8820
|
var notificationAmbiguousReplayHours = 24;
|
|
8805
8821
|
var ambiguousReplayMs = notificationAmbiguousReplayHours * 36e5;
|
|
@@ -9620,6 +9636,11 @@ var assertPlatformTargetsConfig = /* @__PURE__ */ (() => {
|
|
|
9620
9636
|
path: _path + ".sdkMajor",
|
|
9621
9637
|
expected: "(number | undefined)",
|
|
9622
9638
|
value: input.sdkMajor
|
|
9639
|
+
}, _errorFactory)) && (void 0 === input.iosDeviceFamily || "iphone" === input.iosDeviceFamily || "universal" === input.iosDeviceFamily || _assertGuard_16._assertGuard(_exceptionable, {
|
|
9640
|
+
method: "typia.createAssert",
|
|
9641
|
+
path: _path + ".iosDeviceFamily",
|
|
9642
|
+
expected: '("iphone" | "universal" | undefined)',
|
|
9643
|
+
value: input.iosDeviceFamily
|
|
9623
9644
|
}, _errorFactory));
|
|
9624
9645
|
const _ao6 = (input, _path, _exceptionable = true) => (void 0 === input.identityUpgrade || "action-required" === input.identityUpgrade || "approval-required" === input.identityUpgrade || "available" === input.identityUpgrade || "configuration-required" === input.identityUpgrade || "disabled" === input.identityUpgrade || "temporarily-unavailable" === input.identityUpgrade || "unsupported" === input.identityUpgrade || _assertGuard_16._assertGuard(_exceptionable, {
|
|
9625
9646
|
method: "typia.createAssert",
|
|
@@ -9770,7 +9791,7 @@ var assertPlatformTargetsConfig = /* @__PURE__ */ (() => {
|
|
|
9770
9791
|
return true;
|
|
9771
9792
|
return "packaged" === value || "remote" === value;
|
|
9772
9793
|
});
|
|
9773
|
-
const _io5 = (input) => (void 0 === input.appName || "string" === typeof input.appName) && (void 0 === input.displayName || "string" === typeof input.displayName) && (void 0 === input.primaryColor || "string" === typeof input.primaryColor) && (void 0 === input.packageId || "string" === typeof input.packageId) && (void 0 === input.bundleId || "string" === typeof input.bundleId) && (void 0 === input.sdkMajor || "number" === typeof input.sdkMajor);
|
|
9794
|
+
const _io5 = (input) => (void 0 === input.appName || "string" === typeof input.appName) && (void 0 === input.displayName || "string" === typeof input.displayName) && (void 0 === input.primaryColor || "string" === typeof input.primaryColor) && (void 0 === input.packageId || "string" === typeof input.packageId) && (void 0 === input.bundleId || "string" === typeof input.bundleId) && (void 0 === input.sdkMajor || "number" === typeof input.sdkMajor) && (void 0 === input.iosDeviceFamily || "iphone" === input.iosDeviceFamily || "universal" === input.iosDeviceFamily);
|
|
9774
9795
|
const _io6 = (input) => (void 0 === input.identityUpgrade || "action-required" === input.identityUpgrade || "approval-required" === input.identityUpgrade || "available" === input.identityUpgrade || "configuration-required" === input.identityUpgrade || "disabled" === input.identityUpgrade || "temporarily-unavailable" === input.identityUpgrade || "unsupported" === input.identityUpgrade) && (void 0 === input.presentation || "action-required" === input.presentation || "approval-required" === input.presentation || "available" === input.presentation || "configuration-required" === input.presentation || "disabled" === input.presentation || "temporarily-unavailable" === input.presentation || "unsupported" === input.presentation) && (void 0 === input.sharing || "action-required" === input.sharing || "approval-required" === input.sharing || "available" === input.sharing || "configuration-required" === input.sharing || "disabled" === input.sharing || "temporarily-unavailable" === input.sharing || "unsupported" === input.sharing) && (void 0 === input.inboundShare || "action-required" === input.inboundShare || "approval-required" === input.inboundShare || "available" === input.inboundShare || "configuration-required" === input.inboundShare || "disabled" === input.inboundShare || "temporarily-unavailable" === input.inboundShare || "unsupported" === input.inboundShare) && (void 0 === input.notifications || "action-required" === input.notifications || "approval-required" === input.notifications || "available" === input.notifications || "configuration-required" === input.notifications || "disabled" === input.notifications || "temporarily-unavailable" === input.notifications || "unsupported" === input.notifications) && (void 0 === input.presentationMode || "fullscreen" === input.presentationMode || "inline-expanded" === input.presentationMode);
|
|
9775
9796
|
const _io7 = (input) => (void 0 === input.profile || "string" === typeof input.profile) && (void 0 === input.source || "string" === typeof input.source) && (void 0 === input.backgroundColor || "string" === typeof input.backgroundColor) && (void 0 === input.externalUrl || "string" === typeof input.externalUrl) && (void 0 === input.variants || "object" === typeof input.variants && null !== input.variants && false === Array.isArray(input.variants) && _io8(input.variants));
|
|
9776
9797
|
const _io8 = (input) => (void 0 === input.maskable || "string" === typeof input.maskable) && (void 0 === input.androidForeground || "string" === typeof input.androidForeground) && (void 0 === input.monochrome || "string" === typeof input.monochrome) && (void 0 === input.background || "string" === typeof input.background);
|
|
@@ -15423,9 +15444,9 @@ function isProcessAlive(pid) {
|
|
|
15423
15444
|
}
|
|
15424
15445
|
|
|
15425
15446
|
// node_modules/.cache/mpgd-native-build/tools/target/native-build-execution.js
|
|
15426
|
-
import { existsSync as
|
|
15447
|
+
import { existsSync as existsSync24, mkdirSync as mkdirSync12, mkdtempSync as mkdtempSync4, readdirSync as readdirSync7, rmSync as rmSync11, statSync as statSync6 } from "node:fs";
|
|
15427
15448
|
import { tmpdir as tmpdir4 } from "node:os";
|
|
15428
|
-
import { dirname as
|
|
15449
|
+
import { dirname as dirname11, isAbsolute as isAbsolute12, join as join15 } from "node:path";
|
|
15429
15450
|
|
|
15430
15451
|
// node_modules/.cache/mpgd-native-build/packages/cli/src/ios-signing-session.js
|
|
15431
15452
|
import { createHash as createHash12, randomBytes } from "node:crypto";
|
|
@@ -16226,922 +16247,1457 @@ function applyCapacitorShellCsp(webDir) {
|
|
|
16226
16247
|
}
|
|
16227
16248
|
}
|
|
16228
16249
|
|
|
16229
|
-
// node_modules/.cache/mpgd-native-build/tools/target/native-
|
|
16230
|
-
import {
|
|
16231
|
-
import {
|
|
16232
|
-
|
|
16233
|
-
|
|
16234
|
-
function
|
|
16235
|
-
|
|
16236
|
-
|
|
16237
|
-
|
|
16238
|
-
|
|
16239
|
-
|
|
16240
|
-
|
|
16241
|
-
const runner = input.runner ?? { run: runCommand2 };
|
|
16242
|
-
const listing = runner.run("unzip", ["-Z1", ipa]);
|
|
16243
|
-
if (listing.status !== 0) {
|
|
16244
|
-
throw new Error("iOS store IPA could not be listed.");
|
|
16245
|
-
}
|
|
16246
|
-
const entries = listing.stdout.split(/\r?\n/u).filter(Boolean);
|
|
16247
|
-
if (entries.some((entry) => entry.startsWith("/") || entry.includes("\\") || entry.split("/").some((segment) => segment === ".." || segment === "."))) {
|
|
16248
|
-
throw new Error("iOS store IPA has an unsafe path.");
|
|
16250
|
+
// node_modules/.cache/mpgd-native-build/tools/target/native-store-policy.js
|
|
16251
|
+
import { existsSync as existsSync19, readFileSync as readFileSync20 } from "node:fs";
|
|
16252
|
+
import { dirname as dirname9, join as join13 } from "node:path";
|
|
16253
|
+
|
|
16254
|
+
// node_modules/.cache/mpgd-native-build/packages/cli/src/native-shell-identity.js
|
|
16255
|
+
function assertAndroidSettingsAppProject(source) {
|
|
16256
|
+
const clean = stripGradleComments(source);
|
|
16257
|
+
const code = maskGradleStrings(clean);
|
|
16258
|
+
const includes = [...clean.matchAll(/\binclude\s*(?:\(([^)]*)\)|([^\r\n;]+))/gu)];
|
|
16259
|
+
const includesApp = includes.some((match) => code.slice(match.index, match.index + 7) === "include" && /(?:^|,)\s*["']:app["']\s*(?:,|$)/u.test(match[1] ?? match[2] ?? ""));
|
|
16260
|
+
if (!includesApp) {
|
|
16261
|
+
throw new Error("Android settings must include :app at android/app without remapping it.");
|
|
16249
16262
|
}
|
|
16250
|
-
|
|
16251
|
-
|
|
16252
|
-
|
|
16263
|
+
assertAndroidSettingsNoAppRemap(clean);
|
|
16264
|
+
}
|
|
16265
|
+
function assertAndroidSettingsNoAppRemap(source) {
|
|
16266
|
+
const clean = stripGradleComments(source);
|
|
16267
|
+
const code = maskGradleStrings(clean);
|
|
16268
|
+
const appRemap = [...clean.matchAll(/\b(project|findProject)\s*\(\s*["']:app["']\s*\)/gu)].some((match) => code.slice(match.index, match.index + (match[1]?.length ?? 0)) === match[1]);
|
|
16269
|
+
if (appRemap) {
|
|
16270
|
+
throw new Error("Android settings must include :app at android/app without remapping it.");
|
|
16253
16271
|
}
|
|
16254
|
-
|
|
16255
|
-
|
|
16256
|
-
|
|
16257
|
-
|
|
16258
|
-
|
|
16259
|
-
|
|
16272
|
+
}
|
|
16273
|
+
function stripGradleComments(source) {
|
|
16274
|
+
let output = "";
|
|
16275
|
+
let quote;
|
|
16276
|
+
for (let index = 0; index < source.length; index += 1) {
|
|
16277
|
+
const character = source[index] ?? "";
|
|
16278
|
+
const next = source[index + 1];
|
|
16279
|
+
if (quote !== void 0) {
|
|
16280
|
+
output += character;
|
|
16281
|
+
if (character === "\\" && next !== void 0) {
|
|
16282
|
+
output += next;
|
|
16283
|
+
index += 1;
|
|
16284
|
+
} else if (character === quote) {
|
|
16285
|
+
quote = void 0;
|
|
16286
|
+
}
|
|
16287
|
+
continue;
|
|
16260
16288
|
}
|
|
16261
|
-
|
|
16262
|
-
|
|
16263
|
-
|
|
16289
|
+
if (character === '"' || character === "'") {
|
|
16290
|
+
quote = character;
|
|
16291
|
+
output += character;
|
|
16292
|
+
continue;
|
|
16293
|
+
}
|
|
16294
|
+
if (character === "/" && next === "/") {
|
|
16295
|
+
index += 2;
|
|
16296
|
+
while (index < source.length && source[index] !== "\n") {
|
|
16297
|
+
index += 1;
|
|
16298
|
+
}
|
|
16299
|
+
output += "\n";
|
|
16300
|
+
continue;
|
|
16301
|
+
}
|
|
16302
|
+
if (character === "/" && next === "*") {
|
|
16303
|
+
index += 2;
|
|
16304
|
+
while (index < source.length && !(source[index] === "*" && source[index + 1] === "/")) {
|
|
16305
|
+
output += source[index] === "\n" ? "\n" : " ";
|
|
16306
|
+
index += 1;
|
|
16307
|
+
}
|
|
16308
|
+
index += 1;
|
|
16309
|
+
continue;
|
|
16310
|
+
}
|
|
16311
|
+
output += character;
|
|
16264
16312
|
}
|
|
16313
|
+
return output;
|
|
16265
16314
|
}
|
|
16266
|
-
function
|
|
16267
|
-
const
|
|
16268
|
-
|
|
16269
|
-
|
|
16270
|
-
|
|
16271
|
-
if (!existsSync19(path7.join(appPath, "Assets.car")) || existsSync19(path7.join(appPath, "Info-Smoke.plist"))) {
|
|
16272
|
-
throw new Error("Signed iOS app contains smoke assets or omits release icons.");
|
|
16273
|
-
}
|
|
16274
|
-
const runner = input.runner ?? { run: runCommand2 };
|
|
16275
|
-
const signature = runner.run("codesign", ["--verify", "--deep", "--strict", appPath]);
|
|
16276
|
-
if (signature.status !== 0) {
|
|
16277
|
-
throw new Error("iOS app code signature could not be verified.");
|
|
16278
|
-
}
|
|
16279
|
-
const signingInfo = runner.run("codesign", ["-dv", "--verbose=4", appPath]);
|
|
16280
|
-
const signingText = `${signingInfo.stdout}
|
|
16281
|
-
${signingInfo.stderr}`;
|
|
16282
|
-
const expectedTeamLine = `TeamIdentifier=${input.expectedTeamId}`;
|
|
16283
|
-
if (signingInfo.status !== 0 || /Signature=adhoc/iu.test(signingText) || !signingText.split(/\r?\n/u).some((line) => line.trim() === expectedTeamLine)) {
|
|
16284
|
-
throw new Error("iOS app is not signed for the expected development team.");
|
|
16315
|
+
function hasGradleIdentityMutation(source) {
|
|
16316
|
+
const clean = stripGradleComments(source);
|
|
16317
|
+
const masked = maskGradleStrings(clean);
|
|
16318
|
+
if (hasGradlePropertySetter(clean) || hasGradleBracketIdentityWrite(clean)) {
|
|
16319
|
+
return true;
|
|
16285
16320
|
}
|
|
16286
|
-
const
|
|
16287
|
-
|
|
16288
|
-
|
|
16289
|
-
|
|
16290
|
-
|
|
16291
|
-
|
|
16292
|
-
|
|
16293
|
-
|
|
16294
|
-
|
|
16321
|
+
const setter = /\bset(?:ApplicationId|ApplicationIdSuffix|VersionCode|VersionName|VersionNameSuffix)\s*\(/u;
|
|
16322
|
+
const field = "(?:applicationId|applicationIdSuffix|versionCode|versionName|versionNameSuffix)";
|
|
16323
|
+
const assignment = new RegExp(`\\b${field}(?:\\s*(?:\\+?=|\\(|["'\\d]|\\.set\\s*\\()|[ \\t]+[A-Za-z_$])`, "u");
|
|
16324
|
+
return setter.test(masked) || assignment.test(masked);
|
|
16325
|
+
}
|
|
16326
|
+
function hasGradleBracketIdentityWrite(source) {
|
|
16327
|
+
const clean = stripGradleComments(source);
|
|
16328
|
+
const code = maskGradleStrings(clean);
|
|
16329
|
+
const key = "(?:applicationId|applicationIdSuffix|versionCode|versionName|versionNameSuffix)";
|
|
16330
|
+
const bracketWrite = `\\[\\s*["']${key}["']\\s*\\]\\s*(?:\\+?=|\\.\\s*set\\s*\\(|\\()`;
|
|
16331
|
+
const expression = new RegExp(bracketWrite, "gu");
|
|
16332
|
+
return [...clean.matchAll(expression)].some((match) => code[match.index] === "[");
|
|
16333
|
+
}
|
|
16334
|
+
function hasAndroidDisplayNameResourceOverride(source) {
|
|
16335
|
+
const clean = stripGradleComments(source);
|
|
16336
|
+
const code = maskGradleStrings(clean);
|
|
16337
|
+
return [...clean.matchAll(/\bresValue\b/gu)].some((match) => {
|
|
16338
|
+
if (code.slice(match.index, match.index + 8) !== "resValue") {
|
|
16339
|
+
return false;
|
|
16340
|
+
}
|
|
16341
|
+
const tail = clean.slice(match.index);
|
|
16342
|
+
const literal = /^resValue\s*(?:\(\s*)?["'][^"']+["']\s*,\s*["']([^"']+)["']/u.exec(tail);
|
|
16343
|
+
return literal === null || literal[1] === "app_name";
|
|
16344
|
+
});
|
|
16345
|
+
}
|
|
16346
|
+
function hasAndroidResourceSourceSetOverride(source) {
|
|
16347
|
+
const code = maskGradleStrings(stripGradleComments(source));
|
|
16348
|
+
return /\bsourceSets\b/u.test(code) && /\bres\b/u.test(code);
|
|
16349
|
+
}
|
|
16350
|
+
function hasAndroidManifestSourceSetOverride(source) {
|
|
16351
|
+
const code = maskGradleStrings(stripGradleComments(source));
|
|
16352
|
+
return /\bsourceSets\b/u.test(code) && /\bmanifest\b/u.test(code);
|
|
16353
|
+
}
|
|
16354
|
+
function hasGradleTaskAction(source) {
|
|
16355
|
+
const code = maskGradleStrings(stripGradleComments(source));
|
|
16356
|
+
const withoutStandardClean = code.replace(/\btask\s+clean\s*\(\s*type\s*:\s*Delete\s*\)\s*\{\s*delete\s+rootProject\.buildDir\s*;?\s*\}/gu, "");
|
|
16357
|
+
return /\b(?:doFirst|doLast|whenTaskAdded|whenReady)\s*(?:\(|\{)/u.test(withoutStandardClean) || /\btasks\s*(?:\.|\[|\{)/u.test(withoutStandardClean) || /\btask\s*(?:\([^)]*\)|[A-Za-z_]\w*(?:\s*\([^)]*\))?)\s*\{/u.test(withoutStandardClean);
|
|
16358
|
+
}
|
|
16359
|
+
function hasGradlePropertySetter(source) {
|
|
16360
|
+
const clean = stripGradleComments(source);
|
|
16361
|
+
const masked = maskGradleStrings(clean);
|
|
16362
|
+
return /\bsetProperty\s*\(/u.test(masked);
|
|
16363
|
+
}
|
|
16364
|
+
function countGradleIdentityWrites(source, key) {
|
|
16365
|
+
const masked = maskGradleStrings(stripGradleComments(source));
|
|
16366
|
+
const expression = new RegExp(`\\b${key}\\b`, "gu");
|
|
16367
|
+
return [...masked.matchAll(expression)].filter((match) => {
|
|
16368
|
+
const index = match.index ?? 0;
|
|
16369
|
+
const rest = masked.slice(index + key.length);
|
|
16370
|
+
if (masked[index - 1] === ".") {
|
|
16371
|
+
return /^\s*(?:\+?=|\(|\.set\s*\()/u.test(rest);
|
|
16372
|
+
}
|
|
16373
|
+
return /^\s*(?:\+?=|\(|["'\d]|[A-Za-z_$])/u.test(rest);
|
|
16374
|
+
}).length;
|
|
16375
|
+
}
|
|
16376
|
+
function maskGradleStrings(clean) {
|
|
16377
|
+
let masked = "";
|
|
16378
|
+
let quote;
|
|
16379
|
+
for (let index = 0; index < clean.length; index += 1) {
|
|
16380
|
+
const character = clean[index] ?? "";
|
|
16381
|
+
if (quote !== void 0) {
|
|
16382
|
+
if (character === "\\" && index + 1 < clean.length) {
|
|
16383
|
+
masked += " ";
|
|
16384
|
+
index += 1;
|
|
16385
|
+
} else if (character === quote) {
|
|
16386
|
+
masked += character;
|
|
16387
|
+
quote = void 0;
|
|
16388
|
+
} else {
|
|
16389
|
+
masked += character === "\n" ? "\n" : " ";
|
|
16390
|
+
}
|
|
16391
|
+
} else if (character === '"' || character === "'") {
|
|
16392
|
+
quote = character;
|
|
16393
|
+
masked += character;
|
|
16394
|
+
} else {
|
|
16395
|
+
masked += character;
|
|
16295
16396
|
}
|
|
16296
|
-
return [name, expected];
|
|
16297
|
-
}));
|
|
16298
|
-
const configFiles = [
|
|
16299
|
-
path7.join(appPath, "capacitor.config.json"),
|
|
16300
|
-
path7.join(appPath, "public/capacitor.config.json")
|
|
16301
|
-
];
|
|
16302
|
-
if (!configFiles.some((file) => existsSync19(file))) {
|
|
16303
|
-
throw new Error("Signed iOS app Capacitor configuration is missing.");
|
|
16304
16397
|
}
|
|
16305
|
-
|
|
16306
|
-
|
|
16307
|
-
|
|
16398
|
+
return masked;
|
|
16399
|
+
}
|
|
16400
|
+
function readIosReleaseInfoPlist(source) {
|
|
16401
|
+
const target2 = readIosReleaseConfiguration(source, readIosAppConfigurationList(source));
|
|
16402
|
+
let value = readIosInfoPlistSetting(target2);
|
|
16403
|
+
if (value === void 0) {
|
|
16404
|
+
if (/\bbaseConfigurationReference\s*=/u.test(target2)) {
|
|
16405
|
+
throw new Error("Existing ios project Release Info.plist xcconfig cannot be read safely.");
|
|
16406
|
+
}
|
|
16407
|
+
const projectListId = readIosProjectConfigurationList(source);
|
|
16408
|
+
if (projectListId === void 0) {
|
|
16409
|
+
throw new Error("Existing ios project Release Info.plist has no project configuration.");
|
|
16410
|
+
}
|
|
16411
|
+
const project = readIosReleaseConfiguration(source, projectListId);
|
|
16412
|
+
value = readIosInfoPlistSetting(project);
|
|
16413
|
+
if (value === void 0 && /\bbaseConfigurationReference\s*=/u.test(project)) {
|
|
16414
|
+
throw new Error("Existing ios project Release Info.plist xcconfig cannot be read safely.");
|
|
16308
16415
|
}
|
|
16309
16416
|
}
|
|
16310
|
-
|
|
16417
|
+
if (value === void 0) {
|
|
16418
|
+
throw new Error("Existing ios project Release Info.plist cannot be read safely.");
|
|
16419
|
+
}
|
|
16420
|
+
return value;
|
|
16311
16421
|
}
|
|
16312
|
-
function
|
|
16313
|
-
const
|
|
16314
|
-
|
|
16315
|
-
|
|
16422
|
+
function assertIosReleaseProductName(source) {
|
|
16423
|
+
const target2 = readIosReleaseConfiguration(source, readIosAppConfigurationList(source));
|
|
16424
|
+
let name = readIosProductName(target2);
|
|
16425
|
+
if (name === void 0 || name === "$(inherited)") {
|
|
16426
|
+
if (/\bbaseConfigurationReference\s*=/u.test(target2)) {
|
|
16427
|
+
throw new Error("Existing ios project Release product name xcconfig is unsupported.");
|
|
16428
|
+
}
|
|
16429
|
+
const projectList = readIosProjectConfigurationList(source);
|
|
16430
|
+
if (projectList !== void 0) {
|
|
16431
|
+
const project = readIosReleaseConfiguration(source, projectList);
|
|
16432
|
+
name = readIosProductName(project);
|
|
16433
|
+
if ((name === void 0 || name === "$(inherited)") && /\bbaseConfigurationReference\s*=/u.test(project)) {
|
|
16434
|
+
throw new Error("Existing ios project Release product name xcconfig is unsupported.");
|
|
16435
|
+
}
|
|
16436
|
+
}
|
|
16316
16437
|
}
|
|
16317
|
-
|
|
16318
|
-
|
|
16319
|
-
const ios = config2.ios;
|
|
16320
|
-
if (typeof server === "object" && server !== null && "url" in server || typeof ios === "object" && ios !== null && "webContentsDebuggingEnabled" in ios && ios.webContentsDebuggingEnabled === true) {
|
|
16321
|
-
throw new Error("Signed iOS app retains a live-reload or debug bridge configuration.");
|
|
16438
|
+
if (name !== void 0 && name !== "$(inherited)" && name !== "App" && name !== "$(TARGET_NAME)") {
|
|
16439
|
+
throw new Error("Existing ios project Release PRODUCT_NAME must build App.app.");
|
|
16322
16440
|
}
|
|
16323
16441
|
}
|
|
16324
|
-
function
|
|
16325
|
-
const
|
|
16326
|
-
|
|
16327
|
-
|
|
16328
|
-
|
|
16329
|
-
|
|
16330
|
-
if (result.error !== void 0) {
|
|
16331
|
-
throw new Error(`Required iOS inspection command is unavailable: ${command}.`);
|
|
16442
|
+
function assertIosReleaseInfoPlistExpansion(source) {
|
|
16443
|
+
const target2 = readIosReleaseConfiguration(source, readIosAppConfigurationList(source));
|
|
16444
|
+
assertIosInfoPlistExpansionSetting(target2);
|
|
16445
|
+
const projectList = readIosProjectConfigurationList(source);
|
|
16446
|
+
if (projectList !== void 0) {
|
|
16447
|
+
assertIosInfoPlistExpansionSetting(readIosReleaseConfiguration(source, projectList));
|
|
16332
16448
|
}
|
|
16333
|
-
|
|
16334
|
-
|
|
16335
|
-
|
|
16336
|
-
|
|
16449
|
+
}
|
|
16450
|
+
function assertIosReleasePackagingSettings(source) {
|
|
16451
|
+
const target2 = readIosReleaseConfiguration(source, readIosAppConfigurationList(source));
|
|
16452
|
+
const projectList = readIosProjectConfigurationList(source);
|
|
16453
|
+
const project = projectList === void 0 ? void 0 : readIosReleaseConfiguration(source, projectList);
|
|
16454
|
+
const setting = (configuration, key) => {
|
|
16455
|
+
if (configuration === void 0) {
|
|
16456
|
+
return void 0;
|
|
16457
|
+
}
|
|
16458
|
+
const conditional = new RegExp(`\\b${key}\\[[^\\]\\r\\n]+\\]`, "u");
|
|
16459
|
+
if (conditional.test(configuration)) {
|
|
16460
|
+
throw new Error(`Existing ios project conditional Release ${key} is unsupported.`);
|
|
16461
|
+
}
|
|
16462
|
+
const expression = `(?:^|[\\s{;])["']?${key}["']?\\s*=\\s*([^;]+);`;
|
|
16463
|
+
const pattern = new RegExp(expression, "gu");
|
|
16464
|
+
const values = [...configuration.matchAll(pattern)].map((match) => match[1]?.trim().replace(/^["']|["']$/gu, ""));
|
|
16465
|
+
if (values.length > 1 || /\bbaseConfigurationReference\s*=/u.test(configuration)) {
|
|
16466
|
+
throw new Error(`Existing ios project Release ${key} cannot be read safely.`);
|
|
16467
|
+
}
|
|
16468
|
+
return values[0];
|
|
16469
|
+
};
|
|
16470
|
+
const effective = (key) => {
|
|
16471
|
+
const targetValue = setting(target2, key);
|
|
16472
|
+
return targetValue === void 0 || targetValue === "$(inherited)" ? setting(project, key) : targetValue;
|
|
16337
16473
|
};
|
|
16474
|
+
const preprocessing = effective("INFOPLIST_PREPROCESS");
|
|
16475
|
+
if (preprocessing !== void 0 && preprocessing !== "NO") {
|
|
16476
|
+
throw new Error("Existing ios project Release Info.plist preprocessing is unsupported.");
|
|
16477
|
+
}
|
|
16478
|
+
for (const key of ["INFOPLIST_PREPROCESSOR_DEFINITIONS", "INFOPLIST_PREFIX_HEADER"]) {
|
|
16479
|
+
if (effective(key) !== void 0) {
|
|
16480
|
+
throw new Error(`Existing ios project Release ${key} is unsupported.`);
|
|
16481
|
+
}
|
|
16482
|
+
}
|
|
16483
|
+
const skipInstall = effective("SKIP_INSTALL");
|
|
16484
|
+
if (skipInstall !== void 0 && skipInstall !== "NO") {
|
|
16485
|
+
throw new Error("Existing ios project Release SKIP_INSTALL must be NO.");
|
|
16486
|
+
}
|
|
16338
16487
|
}
|
|
16339
|
-
|
|
16340
|
-
|
|
16341
|
-
|
|
16342
|
-
|
|
16343
|
-
|
|
16344
|
-
function resolveIosSigningPlan(environment, mode) {
|
|
16345
|
-
const teamId = environment.MPGD_IOS_TEAM_ID?.trim();
|
|
16346
|
-
if (teamId === void 0 || !/^[A-Z0-9]{10}$/u.test(teamId)) {
|
|
16347
|
-
throw new Error("Signed iOS builds require a 10-character MPGD_IOS_TEAM_ID.");
|
|
16488
|
+
function assertIosInfoPlistExpansionSetting(configuration) {
|
|
16489
|
+
const key = "INFOPLIST_EXPAND_BUILD_SETTINGS";
|
|
16490
|
+
const conditional = /(?:^|[\s{;])["']?INFOPLIST_EXPAND_BUILD_SETTINGS(?:\[[^\]\r\n]+\])+["']?\s*=/u;
|
|
16491
|
+
if (conditional.test(configuration)) {
|
|
16492
|
+
throw new Error("Existing ios project conditional Release Info.plist expansion is unsupported.");
|
|
16348
16493
|
}
|
|
16349
|
-
const
|
|
16350
|
-
if (
|
|
16351
|
-
throw new Error("
|
|
16494
|
+
const values = [...configuration.matchAll(/(?:^|[\s{;])["']?INFOPLIST_EXPAND_BUILD_SETTINGS["']?\s*=\s*([^;]+);/gu)].map((match) => match[1]?.trim().replace(/^["']|["']$/gu, ""));
|
|
16495
|
+
if (values.length === 0 && /\bbaseConfigurationReference\s*=/u.test(configuration)) {
|
|
16496
|
+
throw new Error("Existing ios project Release Info.plist expansion xcconfig is unsupported.");
|
|
16352
16497
|
}
|
|
16353
|
-
|
|
16354
|
-
|
|
16355
|
-
if (style === "Manual" && (identity === void 0 || identity === "" || profile2 === void 0 || profile2 === "")) {
|
|
16356
|
-
throw new Error("Manual iOS signing requires host identity and provisioning profile names.");
|
|
16498
|
+
if (values.length > 1 || values.some((value) => value !== "YES")) {
|
|
16499
|
+
throw new Error(`Existing ios project Release ${key} must be enabled.`);
|
|
16357
16500
|
}
|
|
16358
|
-
|
|
16359
|
-
|
|
16501
|
+
}
|
|
16502
|
+
function readIosProductName(configuration) {
|
|
16503
|
+
if (/(?:^|[\s{;])["']?PRODUCT_NAME(?:\[[^\]\r\n]+\])+["']?\s*=/u.test(configuration)) {
|
|
16504
|
+
throw new Error("Existing ios project conditional Release PRODUCT_NAME is unsupported.");
|
|
16360
16505
|
}
|
|
16361
|
-
const
|
|
16362
|
-
|
|
16363
|
-
|
|
16364
|
-
`DEVELOPMENT_TEAM=${teamId}`,
|
|
16365
|
-
...identity === void 0 || identity === "" ? [] : [`CODE_SIGN_IDENTITY=${identity}`],
|
|
16366
|
-
...profile2 === void 0 || profile2 === "" ? [] : [`PROVISIONING_PROFILE_SPECIFIER=${profile2}`],
|
|
16367
|
-
...environment.MPGD_IOS_SIGNING_KEYCHAIN === void 0 ? [] : [`OTHER_CODE_SIGN_FLAGS=--keychain ${JSON.stringify(environment.MPGD_IOS_SIGNING_KEYCHAIN)}`]
|
|
16368
|
-
];
|
|
16369
|
-
if (mode !== "store-export") {
|
|
16370
|
-
return { teamId, archiveBuildSettings };
|
|
16506
|
+
const values = [...configuration.matchAll(/(?:^|[\s{;])["']?PRODUCT_NAME["']?\s*=\s*([^;]+);/gu)].map((match) => match[1]?.trim().replace(/^["']|["']$/gu, ""));
|
|
16507
|
+
if (values.length > 1) {
|
|
16508
|
+
throw new Error("Existing ios project Release PRODUCT_NAME is ambiguous.");
|
|
16371
16509
|
}
|
|
16372
|
-
|
|
16373
|
-
|
|
16374
|
-
|
|
16510
|
+
return values[0];
|
|
16511
|
+
}
|
|
16512
|
+
function readIosInfoPlistSetting(configuration) {
|
|
16513
|
+
if (/(?:^|[\s{;])["']?INFOPLIST_FILE(?:\[[^\]\r\n]+\])+["']?\s*=/u.test(configuration)) {
|
|
16514
|
+
throw new Error("Existing ios project conditional Release Info.plist is unsupported.");
|
|
16375
16515
|
}
|
|
16376
|
-
const
|
|
16377
|
-
if (
|
|
16378
|
-
throw new Error("
|
|
16516
|
+
const values = [...configuration.matchAll(/(?:^|[\s{;])["']?INFOPLIST_FILE["']?\s*=\s*([^;]+);/gu)].map((match) => match[1]?.trim().replace(/^["']|["']$/gu, ""));
|
|
16517
|
+
if (values.length > 1) {
|
|
16518
|
+
throw new Error("Existing ios project Release Info.plist is ambiguous.");
|
|
16379
16519
|
}
|
|
16380
|
-
const
|
|
16381
|
-
|
|
16382
|
-
|
|
16520
|
+
const value = values[0];
|
|
16521
|
+
if (value === void 0 || value === "$(inherited)") {
|
|
16522
|
+
return void 0;
|
|
16523
|
+
}
|
|
16524
|
+
if (value.includes("$") || value.includes("\\") || pathIsUnsafe(value)) {
|
|
16525
|
+
throw new Error("Existing ios project Release Info.plist cannot be read safely.");
|
|
16526
|
+
}
|
|
16527
|
+
return value;
|
|
16528
|
+
}
|
|
16529
|
+
function pathIsUnsafe(value) {
|
|
16530
|
+
return value.startsWith("/") || value.split("/").some((part) => part === ".." || part === "");
|
|
16531
|
+
}
|
|
16532
|
+
function readIosAppConfigurationList(source) {
|
|
16533
|
+
const target2 = readPbxObject(source, readIosAppTargetId(source));
|
|
16534
|
+
const listId = /\bbuildConfigurationList\s*=\s*([A-F0-9]+)\b/u.exec(target2)?.[1];
|
|
16535
|
+
if (listId === void 0) {
|
|
16536
|
+
throw new Error("Existing ios project App target configuration is missing.");
|
|
16537
|
+
}
|
|
16538
|
+
return listId;
|
|
16539
|
+
}
|
|
16540
|
+
function readIosAppTargetId(source) {
|
|
16541
|
+
const appTargets = [...source.matchAll(/\b([A-F0-9]+)\s*\/\*\s*App\s*\*\/\s*=\s*\{/gu)].map((match) => match[1] ?? "").filter((id) => {
|
|
16542
|
+
const block = readPbxObject(source, id);
|
|
16543
|
+
return /\bisa\s*=\s*PBXNativeTarget;/u.test(block) && /\bname\s*=\s*"?App"?\s*;/u.test(block);
|
|
16383
16544
|
});
|
|
16384
|
-
if (
|
|
16385
|
-
throw new Error("
|
|
16545
|
+
if (appTargets.length !== 1) {
|
|
16546
|
+
throw new Error("Existing ios project App target could not be read safely.");
|
|
16386
16547
|
}
|
|
16387
|
-
|
|
16548
|
+
const targetId = appTargets[0] ?? "";
|
|
16549
|
+
const target2 = readPbxObject(source, targetId);
|
|
16550
|
+
const productId = /\bproductReference\s*=\s*([A-F0-9]+)\b/u.exec(target2)?.[1];
|
|
16551
|
+
if (!/\bproductType\s*=\s*"?com\.apple\.product-type\.application"?\s*;/u.test(target2) || productId === void 0) {
|
|
16552
|
+
throw new Error("Existing ios App target must produce an application.");
|
|
16553
|
+
}
|
|
16554
|
+
const product = readPbxObject(source, productId);
|
|
16555
|
+
if (!/\bisa\s*=\s*PBXFileReference\s*;/u.test(product) || !/\b(?:explicitFileType|lastKnownFileType)\s*=\s*wrapper\.application\s*;/u.test(product) || !/\bpath\s*=\s*"?App\.app"?\s*;/u.test(product) || !/\bsourceTree\s*=\s*BUILT_PRODUCTS_DIR\s*;/u.test(product)) {
|
|
16556
|
+
throw new Error("Existing ios App target product must be App.app.");
|
|
16557
|
+
}
|
|
16558
|
+
return targetId;
|
|
16559
|
+
}
|
|
16560
|
+
function readIosProjectConfigurationList(source) {
|
|
16561
|
+
const projects = [...source.matchAll(/\b([A-F0-9]+)\s*\/\*[^*]+\*\/\s*=\s*\{/gu)].map((match) => readPbxObject(source, match[1] ?? "")).filter((block) => /\bisa\s*=\s*PBXProject;/u.test(block));
|
|
16562
|
+
return projects.length === 1 ? /\bbuildConfigurationList\s*=\s*([A-F0-9]+)\b/u.exec(projects[0] ?? "")?.[1] : void 0;
|
|
16563
|
+
}
|
|
16564
|
+
function readIosReleaseConfiguration(source, listId) {
|
|
16565
|
+
const list = readPbxObject(source, listId);
|
|
16566
|
+
const configurations = /\bbuildConfigurations\s*=\s*\(([^)]*)\)/u.exec(list)?.[1];
|
|
16567
|
+
const releaseIds = configurations === void 0 ? [] : [...configurations.matchAll(/\b([A-F0-9]+)\s*\/\*\s*Release\s*\*\//gu)].map((match) => match[1]);
|
|
16568
|
+
if (releaseIds.length !== 1) {
|
|
16569
|
+
throw new Error("Existing ios project App Release configuration is missing or ambiguous.");
|
|
16570
|
+
}
|
|
16571
|
+
return stripGradleComments(readPbxObject(source, releaseIds[0] ?? ""));
|
|
16572
|
+
}
|
|
16573
|
+
function readPbxObject(source, id) {
|
|
16574
|
+
const marker = new RegExp(`\\b${id}\\s*\\/\\*[^*]+\\*\\/\\s*=\\s*\\{`, "u").exec(source);
|
|
16575
|
+
if (marker?.index === void 0) {
|
|
16576
|
+
throw new Error(`Existing ios project object ${id} could not be read safely.`);
|
|
16577
|
+
}
|
|
16578
|
+
const opening = source.indexOf("{", marker.index);
|
|
16579
|
+
return readBracedText(source, opening);
|
|
16580
|
+
}
|
|
16581
|
+
function readBracedText(source, opening) {
|
|
16582
|
+
if (opening < 0) {
|
|
16583
|
+
throw new Error("Existing native project block is missing.");
|
|
16584
|
+
}
|
|
16585
|
+
let depth = 0;
|
|
16586
|
+
let quote = false;
|
|
16587
|
+
let blockComment = false;
|
|
16588
|
+
for (let index = opening; index < source.length; index += 1) {
|
|
16589
|
+
const character = source[index];
|
|
16590
|
+
const next = source[index + 1];
|
|
16591
|
+
if (blockComment) {
|
|
16592
|
+
if (character === "*" && next === "/") {
|
|
16593
|
+
blockComment = false;
|
|
16594
|
+
index += 1;
|
|
16595
|
+
}
|
|
16596
|
+
continue;
|
|
16597
|
+
}
|
|
16598
|
+
if (quote) {
|
|
16599
|
+
if (character === "\\") {
|
|
16600
|
+
index += 1;
|
|
16601
|
+
} else if (character === '"') {
|
|
16602
|
+
quote = false;
|
|
16603
|
+
}
|
|
16604
|
+
continue;
|
|
16605
|
+
}
|
|
16606
|
+
if (character === "/" && next === "*") {
|
|
16607
|
+
blockComment = true;
|
|
16608
|
+
index += 1;
|
|
16609
|
+
} else if (character === '"') {
|
|
16610
|
+
quote = true;
|
|
16611
|
+
} else if (character === "{") {
|
|
16612
|
+
depth += 1;
|
|
16613
|
+
} else if (character === "}") {
|
|
16614
|
+
depth -= 1;
|
|
16615
|
+
if (depth === 0) {
|
|
16616
|
+
return source.slice(opening, index + 1);
|
|
16617
|
+
}
|
|
16618
|
+
}
|
|
16619
|
+
}
|
|
16620
|
+
throw new Error("Existing native project block is incomplete.");
|
|
16388
16621
|
}
|
|
16389
16622
|
|
|
16390
|
-
// node_modules/.cache/mpgd-native-build/
|
|
16391
|
-
|
|
16392
|
-
|
|
16393
|
-
|
|
16394
|
-
|
|
16395
|
-
|
|
16396
|
-
|
|
16397
|
-
|
|
16398
|
-
|
|
16399
|
-
|
|
16400
|
-
|
|
16401
|
-
|
|
16402
|
-
|
|
16403
|
-
|
|
16404
|
-
|
|
16405
|
-
|
|
16406
|
-
|
|
16407
|
-
|
|
16408
|
-
|
|
16409
|
-
"
|
|
16410
|
-
|
|
16411
|
-
|
|
16412
|
-
|
|
16413
|
-
|
|
16414
|
-
|
|
16415
|
-
|
|
16416
|
-
|
|
16417
|
-
|
|
16418
|
-
|
|
16419
|
-
|
|
16420
|
-
|
|
16421
|
-
|
|
16422
|
-
|
|
16423
|
-
|
|
16424
|
-
|
|
16425
|
-
|
|
16426
|
-
|
|
16427
|
-
|
|
16428
|
-
|
|
16429
|
-
|
|
16430
|
-
|
|
16431
|
-
});
|
|
16432
|
-
|
|
16433
|
-
|
|
16434
|
-
|
|
16435
|
-
|
|
16436
|
-
{
|
|
16437
|
-
anchor: " createDevvitCommerceProducts,\n createDevvitPlatformGateway,\n",
|
|
16438
|
-
replacement: [
|
|
16439
|
-
" createDevvitCommerceProducts,",
|
|
16440
|
-
" createDevvitNotificationsBackend,",
|
|
16441
|
-
" createDevvitPlatformGateway,",
|
|
16442
|
-
""
|
|
16443
|
-
].join("\n")
|
|
16444
|
-
},
|
|
16445
|
-
{
|
|
16446
|
-
anchor: "import { createDevvitWebPaymentsClient } from '@mpgd/adapter-devvit/web';\n",
|
|
16447
|
-
replacement: [
|
|
16448
|
-
"import {",
|
|
16449
|
-
" createDevvitWebLoginPrompt,",
|
|
16450
|
-
" createDevvitWebPaymentsClient,",
|
|
16451
|
-
"} from '@mpgd/adapter-devvit/web';",
|
|
16452
|
-
""
|
|
16453
|
-
].join("\n")
|
|
16454
|
-
},
|
|
16455
|
-
{
|
|
16456
|
-
anchor: "import type { RuntimeConfig } from '../runtimeDetector';\n",
|
|
16457
|
-
replacement: [
|
|
16458
|
-
"import { setStarterHostedNotificationBackend } from '../notificationBackend';",
|
|
16459
|
-
"import { starterNotificationTopics } from '../notifications';",
|
|
16460
|
-
"import type { RuntimeConfig } from '../runtimeDetector';",
|
|
16461
|
-
""
|
|
16462
|
-
].join("\n")
|
|
16463
|
-
},
|
|
16464
|
-
{
|
|
16465
|
-
anchor: " ...(commerce === undefined ? {} : { commerce }),\n",
|
|
16466
|
-
replacement: [
|
|
16467
|
-
" ...(commerce === undefined ? {} : { commerce }),",
|
|
16468
|
-
" // Notifications v2: the game-rendered opt-in the server bridge serves",
|
|
16469
|
-
" // (a gated beta: `integrations.notifications` stays approval-required",
|
|
16470
|
-
" // until Reddit approves the app), and Reddit login for a logged-out",
|
|
16471
|
-
" // player, who must log in before opting in.",
|
|
16472
|
-
" ...notificationOptions(runtime),",
|
|
16473
|
-
" loginPrompt: createDevvitWebLoginPrompt(),",
|
|
16474
|
-
""
|
|
16475
|
-
].join("\n")
|
|
16476
|
-
},
|
|
16477
|
-
{
|
|
16478
|
-
anchor: "function createCommerceOptions(): DevvitGatewayCommerceOptions | undefined {\n",
|
|
16479
|
-
replacement: [
|
|
16480
|
-
"function notificationOptions(",
|
|
16481
|
-
" runtime: RuntimeConfig,",
|
|
16482
|
-
"): Pick<Parameters<typeof createDevvitPlatformGateway>[0], 'notifications'> {",
|
|
16483
|
-
" const topics = starterNotificationTopics(runtime.configTarget).map((topic) => topic.id);",
|
|
16484
|
-
" if (topics.length === 0) {",
|
|
16485
|
-
" return {};",
|
|
16486
|
-
" }",
|
|
16487
|
-
" // The runtime's backend is the Devvit server (consent records, reminders, notices for the",
|
|
16488
|
-
" // logged-in Reddit user), over the same server bridge as the gateway; created on first use.",
|
|
16489
|
-
" setStarterHostedNotificationBackend(runtime.configTarget, () =>",
|
|
16490
|
-
" createDevvitNotificationsBackend({ appVersion: runtime.appVersion, buildId: runtime.buildId }));",
|
|
16491
|
-
" return { notifications: { topics } };",
|
|
16492
|
-
"}",
|
|
16493
|
-
"",
|
|
16494
|
-
"function createCommerceOptions(): DevvitGatewayCommerceOptions | undefined {",
|
|
16495
|
-
""
|
|
16496
|
-
].join("\n")
|
|
16623
|
+
// node_modules/.cache/mpgd-native-build/packages/cli/src/native-shell-store-policy.js
|
|
16624
|
+
function inspectIosInfoPlistStorePolicy(plist) {
|
|
16625
|
+
const issues = [];
|
|
16626
|
+
if (lacksExportComplianceKey(plist)) {
|
|
16627
|
+
issues.push({
|
|
16628
|
+
level: "warning",
|
|
16629
|
+
message: "iOS Info.plist has no ITSAppUsesNonExemptEncryption: App Store Connect will ask the export-compliance question for every build and TestFlight holds the build until it is answered. Add <key>ITSAppUsesNonExemptEncryption</key><false/> when the game uses only HTTPS/OS encryption (see docs/TESTFLIGHT_INTERNAL_SUBMISSION.md)."
|
|
16630
|
+
});
|
|
16631
|
+
}
|
|
16632
|
+
const capabilities = readPlistTopLevelValue(plist, "UIRequiredDeviceCapabilities");
|
|
16633
|
+
if (capabilities !== void 0 && requires32BitArm(capabilities.xml)) {
|
|
16634
|
+
issues.push({
|
|
16635
|
+
level: "warning",
|
|
16636
|
+
message: "iOS Info.plist UIRequiredDeviceCapabilities lists the 32-bit armv7; use arm64."
|
|
16637
|
+
});
|
|
16638
|
+
}
|
|
16639
|
+
return issues;
|
|
16640
|
+
}
|
|
16641
|
+
function lacksExportComplianceKey(plist) {
|
|
16642
|
+
return readPlistTopLevelValue(plist, "ITSAppUsesNonExemptEncryption") === void 0;
|
|
16643
|
+
}
|
|
16644
|
+
function requires32BitArm(capabilitiesXml) {
|
|
16645
|
+
return /<string>\s*armv7\s*<\/string>/u.test(capabilitiesXml);
|
|
16646
|
+
}
|
|
16647
|
+
function readPlistTopLevelValue(plist, key) {
|
|
16648
|
+
const masked = plist.replace(/<!--[\s\S]*?-->/gu, (comment) => comment.replace(/[^\n]/gu, " "));
|
|
16649
|
+
const marker = new RegExp(`<key>\\s*${escapeRegExp(key)}\\s*</key>\\s*`, "u").exec(masked);
|
|
16650
|
+
if (marker === null) {
|
|
16651
|
+
return void 0;
|
|
16652
|
+
}
|
|
16653
|
+
const start = marker.index + marker[0].length;
|
|
16654
|
+
const open2 = /^<([A-Za-z]+)\s*(\/?)>/u.exec(masked.slice(start));
|
|
16655
|
+
if (open2 === null) {
|
|
16656
|
+
throw new Error(`Info.plist key ${key} has no readable value.`);
|
|
16657
|
+
}
|
|
16658
|
+
const tag = open2[1] ?? "";
|
|
16659
|
+
if (open2[2] === "/") {
|
|
16660
|
+
const end = start + open2[0].length;
|
|
16661
|
+
return { start, end, xml: plist.slice(start, end) };
|
|
16662
|
+
}
|
|
16663
|
+
let depth = 0;
|
|
16664
|
+
const tags = new RegExp(`<(/?)${tag}\\b[^>]*?(/?)>`, "gu");
|
|
16665
|
+
tags.lastIndex = start;
|
|
16666
|
+
for (let match = tags.exec(masked); match !== null; match = tags.exec(masked)) {
|
|
16667
|
+
if (match[2] === "/") {
|
|
16668
|
+
continue;
|
|
16497
16669
|
}
|
|
16498
|
-
|
|
16499
|
-
|
|
16500
|
-
|
|
16501
|
-
|
|
16502
|
-
replacement: [
|
|
16503
|
-
"import {",
|
|
16504
|
-
" context,",
|
|
16505
|
-
" createServer,",
|
|
16506
|
-
" getServerPort,",
|
|
16507
|
-
" notifications,",
|
|
16508
|
-
" payments,",
|
|
16509
|
-
" reddit,",
|
|
16510
|
-
" redis,",
|
|
16511
|
-
"} from '@devvit/web/server';",
|
|
16512
|
-
""
|
|
16513
|
-
].join("\n")
|
|
16514
|
-
},
|
|
16515
|
-
{
|
|
16516
|
-
anchor: [
|
|
16517
|
-
"import {",
|
|
16518
|
-
" createDevvitCommerceBridgeProducts,",
|
|
16519
|
-
" createDevvitCommerceV2BridgeHandler,",
|
|
16520
|
-
"} from '@mpgd/adapter-devvit/server';",
|
|
16521
|
-
""
|
|
16522
|
-
].join("\n"),
|
|
16523
|
-
replacement: [
|
|
16524
|
-
"import {",
|
|
16525
|
-
" createDevvitCommerceBridgeProducts,",
|
|
16526
|
-
" createDevvitCommerceV2BridgeHandler,",
|
|
16527
|
-
" createDevvitNotificationService,",
|
|
16528
|
-
" createDevvitNotificationsBackendBridgeHandler,",
|
|
16529
|
-
" createDevvitNotificationsV2BridgeHandler,",
|
|
16530
|
-
"} from '@mpgd/adapter-devvit/server';",
|
|
16531
|
-
""
|
|
16532
|
-
].join("\n")
|
|
16533
|
-
},
|
|
16534
|
-
{
|
|
16535
|
-
anchor: "import type { ProductCatalog } from '@mpgd/catalog';\n",
|
|
16536
|
-
replacement: [
|
|
16537
|
-
"import { resolveNotificationTopicsForTarget, type NotificationTopicCatalog, type ProductCatalog } from '@mpgd/catalog';",
|
|
16538
|
-
"import { assertNotificationDeployment } from '@mpgd/platform/notifications';",
|
|
16539
|
-
""
|
|
16540
|
-
].join("\n")
|
|
16541
|
-
},
|
|
16542
|
-
{
|
|
16543
|
-
anchor: "import devvitManifest from '../../devvit.json';\n",
|
|
16544
|
-
replacement: [
|
|
16545
|
-
"import notificationDeploymentJson from '../../../../notifications/deployment.json';",
|
|
16546
|
-
"import notificationTopicsJson from '../../../../notifications/topics.json';",
|
|
16547
|
-
"import devvitManifest from '../../devvit.json';",
|
|
16548
|
-
""
|
|
16549
|
-
].join("\n")
|
|
16550
|
-
},
|
|
16551
|
-
{
|
|
16552
|
-
anchor: [
|
|
16553
|
-
"const handleBridgeRequest = createDevvitBridgeHandler({",
|
|
16554
|
-
" redis,",
|
|
16555
|
-
" currentPlayerId,",
|
|
16556
|
-
" currentDisplayName,",
|
|
16557
|
-
" storageKeyNamespace: gameName,",
|
|
16558
|
-
" ...(commerce === undefined ? {} : { commerce }),",
|
|
16559
|
-
"});",
|
|
16560
|
-
""
|
|
16561
|
-
].join("\n"),
|
|
16562
|
-
replacement: [
|
|
16563
|
-
"/**",
|
|
16564
|
-
" * Notifications v2 (`notifications/topics.json`, mapped for the `reddit`",
|
|
16565
|
-
" * target): the game-rendered opt-in for the logged-in requester, and the",
|
|
16566
|
-
" * service that records consent and sends server reminders over the Devvit",
|
|
16567
|
-
" * notifications plugin. Devvit notifications are a gated beta: the game",
|
|
16568
|
-
" * shows them only once `integrations.notifications` is `available`.",
|
|
16569
|
-
" */",
|
|
16570
|
-
"const notificationTopicCatalog = notificationTopicsJson as NotificationTopicCatalog;",
|
|
16571
|
-
"const redditNotificationTopics = readRedditNotificationTopics();",
|
|
16572
|
-
"const notificationService: ReturnType<typeof createDevvitNotificationService>['service'] | undefined =",
|
|
16573
|
-
" redditNotificationTopics.length === 0 ? undefined : createNotificationService();",
|
|
16574
|
-
"// The opt-in is served only with the service that sends; a refused catalog turns both off.",
|
|
16575
|
-
"const notificationsBridge = notificationService === undefined",
|
|
16576
|
-
" ? undefined",
|
|
16577
|
-
" : createDevvitNotificationsV2BridgeHandler({",
|
|
16578
|
-
" notifications,",
|
|
16579
|
-
" currentUserId: currentPlayerId,",
|
|
16580
|
-
" topics: redditNotificationTopics,",
|
|
16581
|
-
" });",
|
|
16582
|
-
"// The webview runtime's backend: consent records, reminders, and notices for the request's user.",
|
|
16583
|
-
"const notificationsBackend = notificationService === undefined",
|
|
16584
|
-
" ? undefined",
|
|
16585
|
-
" : createDevvitNotificationsBackendBridgeHandler({ service: notificationService, context: () => context });",
|
|
16586
|
-
"const handleBridgeRequest = createDevvitBridgeHandler({",
|
|
16587
|
-
" redis,",
|
|
16588
|
-
" currentPlayerId,",
|
|
16589
|
-
" currentDisplayName,",
|
|
16590
|
-
" storageKeyNamespace: gameName,",
|
|
16591
|
-
" ...(commerce === undefined ? {} : { commerce }),",
|
|
16592
|
-
" ...(notificationsBridge === undefined ? {} : { notifications: notificationsBridge }),",
|
|
16593
|
-
" ...(notificationsBackend === undefined ? {} : { notificationsBackend }),",
|
|
16594
|
-
"});",
|
|
16595
|
-
""
|
|
16596
|
-
].join("\n")
|
|
16597
|
-
},
|
|
16598
|
-
{
|
|
16599
|
-
anchor: [
|
|
16600
|
-
"/**",
|
|
16601
|
-
" * The notifications v2 service (`createDevvitNotificationService` from",
|
|
16602
|
-
" * `@mpgd/adapter-devvit/server`). None until the game declares a topic",
|
|
16603
|
-
" * catalog, so the scheduler tasks below do no work.",
|
|
16604
|
-
" */",
|
|
16605
|
-
"const notificationService: Parameters<typeof handleNotificationTask>[0]['notifications'] = undefined;",
|
|
16606
|
-
""
|
|
16607
|
-
].join("\n"),
|
|
16608
|
-
replacement: [
|
|
16609
|
-
"/** The topic ids the catalog maps for Reddit; none when the catalog cannot be read. */",
|
|
16610
|
-
"function readRedditNotificationTopics(): readonly string[] {",
|
|
16611
|
-
" try {",
|
|
16612
|
-
" return resolveNotificationTopicsForTarget(notificationTopicCatalog, 'reddit').map((topic) => topic.id);",
|
|
16613
|
-
" } catch (error) {",
|
|
16614
|
-
" console.error(`devvit notifications disabled: ${errorMessage(error)}`);",
|
|
16615
|
-
" return [];",
|
|
16616
|
-
" }",
|
|
16617
|
-
"}",
|
|
16618
|
-
"",
|
|
16619
|
-
"/**",
|
|
16620
|
-
" * The notifications v2 service over Redis and the Devvit plugin; the",
|
|
16621
|
-
" * scheduler tasks below run its reminders and purges. A catalog or",
|
|
16622
|
-
" * deployment the service refuses turns notifications off, never the game",
|
|
16623
|
-
" * server.",
|
|
16624
|
-
" */",
|
|
16625
|
-
"function createNotificationService(): ReturnType<typeof createDevvitNotificationService>['service'] | undefined {",
|
|
16626
|
-
" try {",
|
|
16627
|
-
" return createDevvitNotificationService({",
|
|
16628
|
-
" redis,",
|
|
16629
|
-
" notifications,",
|
|
16630
|
-
" catalog: notificationTopicCatalog,",
|
|
16631
|
-
" deployment: assertNotificationDeployment(notificationDeploymentJson),",
|
|
16632
|
-
" }).service;",
|
|
16633
|
-
" } catch (error) {",
|
|
16634
|
-
" console.error(`devvit notifications disabled: ${errorMessage(error)}`);",
|
|
16635
|
-
" return undefined;",
|
|
16636
|
-
" }",
|
|
16637
|
-
"}",
|
|
16638
|
-
""
|
|
16639
|
-
].join("\n")
|
|
16670
|
+
depth += match[1] === "/" ? -1 : 1;
|
|
16671
|
+
if (depth === 0) {
|
|
16672
|
+
const end = match.index + match[0].length;
|
|
16673
|
+
return { start, end, xml: plist.slice(start, end) };
|
|
16640
16674
|
}
|
|
16641
|
-
|
|
16642
|
-
};
|
|
16643
|
-
|
|
16644
|
-
|
|
16645
|
-
|
|
16646
|
-
|
|
16647
|
-
|
|
16648
|
-
|
|
16649
|
-
|
|
16650
|
-
|
|
16651
|
-
|
|
16652
|
-
|
|
16653
|
-
|
|
16654
|
-
|
|
16655
|
-
|
|
16656
|
-
{
|
|
16657
|
-
|
|
16658
|
-
replacement: " scene: [BootScene, LobbyScene, PlayScene, NotificationSettingsScene],\n"
|
|
16659
|
-
},
|
|
16660
|
-
{
|
|
16661
|
-
anchor: " ownGameAudio(game, input.context);\n",
|
|
16662
|
-
replacement: [
|
|
16663
|
-
" ownGameAudio(game, input.context);",
|
|
16664
|
-
" // The advertising opt-out action and consent notices open the settings screen.",
|
|
16665
|
-
" routeNotificationSettingsOpens(game, input.context);",
|
|
16666
|
-
""
|
|
16667
|
-
].join("\n")
|
|
16675
|
+
}
|
|
16676
|
+
throw new Error(`Info.plist key ${key} has an unclosed value.`);
|
|
16677
|
+
}
|
|
16678
|
+
function escapeRegExp(value) {
|
|
16679
|
+
return value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
|
|
16680
|
+
}
|
|
16681
|
+
var nativeShellOrientations = ["portrait", "landscape", "any"];
|
|
16682
|
+
function readGameNativeOrientation(gameConfig) {
|
|
16683
|
+
if (!isRecord5(gameConfig) || gameConfig.native === void 0) {
|
|
16684
|
+
return void 0;
|
|
16685
|
+
}
|
|
16686
|
+
if (!isRecord5(gameConfig.native)) {
|
|
16687
|
+
throw new Error("mpgd.game.json native must be an object when present.");
|
|
16688
|
+
}
|
|
16689
|
+
for (const key of Object.keys(gameConfig.native)) {
|
|
16690
|
+
if (key !== "orientation") {
|
|
16691
|
+
throw new Error(`mpgd.game.json native.${key} is not supported.`);
|
|
16668
16692
|
}
|
|
16669
|
-
|
|
16670
|
-
|
|
16671
|
-
|
|
16672
|
-
|
|
16673
|
-
|
|
16674
|
-
|
|
16675
|
-
|
|
16676
|
-
|
|
16677
|
-
|
|
16678
|
-
|
|
16679
|
-
|
|
16680
|
-
|
|
16681
|
-
|
|
16682
|
-
|
|
16683
|
-
|
|
16684
|
-
|
|
16685
|
-
|
|
16686
|
-
|
|
16687
|
-
|
|
16688
|
-
|
|
16689
|
-
|
|
16690
|
-
|
|
16691
|
-
|
|
16692
|
-
|
|
16693
|
-
|
|
16694
|
-
|
|
16695
|
-
|
|
16696
|
-
|
|
16697
|
-
|
|
16698
|
-
|
|
16699
|
-
|
|
16700
|
-
|
|
16701
|
-
|
|
16702
|
-
|
|
16693
|
+
}
|
|
16694
|
+
const orientation = gameConfig.native.orientation;
|
|
16695
|
+
if (orientation === void 0) {
|
|
16696
|
+
return void 0;
|
|
16697
|
+
}
|
|
16698
|
+
if (!nativeShellOrientations.includes(orientation)) {
|
|
16699
|
+
throw new Error("mpgd.game.json native.orientation must be 'portrait', 'landscape' or 'any'.");
|
|
16700
|
+
}
|
|
16701
|
+
return orientation;
|
|
16702
|
+
}
|
|
16703
|
+
function readIosDeviceFamily(metadata) {
|
|
16704
|
+
if (!isRecord5(metadata) || metadata.iosDeviceFamily === void 0) {
|
|
16705
|
+
return void 0;
|
|
16706
|
+
}
|
|
16707
|
+
if (metadata.iosDeviceFamily !== "universal" && metadata.iosDeviceFamily !== "iphone") {
|
|
16708
|
+
throw new Error("ios metadata.iosDeviceFamily must be 'universal' or 'iphone'.");
|
|
16709
|
+
}
|
|
16710
|
+
return metadata.iosDeviceFamily;
|
|
16711
|
+
}
|
|
16712
|
+
var iosPortrait = "UIInterfaceOrientationPortrait";
|
|
16713
|
+
var iosPortraitUpsideDown = "UIInterfaceOrientationPortraitUpsideDown";
|
|
16714
|
+
var iosLandscape = [
|
|
16715
|
+
"UIInterfaceOrientationLandscapeLeft",
|
|
16716
|
+
"UIInterfaceOrientationLandscapeRight"
|
|
16717
|
+
];
|
|
16718
|
+
var iosIpadOrientations = [
|
|
16719
|
+
iosPortrait,
|
|
16720
|
+
iosPortraitUpsideDown,
|
|
16721
|
+
...iosLandscape
|
|
16722
|
+
];
|
|
16723
|
+
function readPlistStrings(plist, key) {
|
|
16724
|
+
const value = readPlistTopLevelValue(plist, key);
|
|
16725
|
+
if (value === void 0) {
|
|
16726
|
+
return void 0;
|
|
16727
|
+
}
|
|
16728
|
+
return [...value.xml.matchAll(/<string>\s*([^<]*?)\s*<\/string>/gu)].map((match) => match[1] ?? "");
|
|
16729
|
+
}
|
|
16730
|
+
function inspectIosInfoPlistOrientation(plist, orientation, deviceFamily) {
|
|
16731
|
+
const issues = [];
|
|
16732
|
+
if (orientation !== void 0) {
|
|
16733
|
+
const listed = readPlistStrings(plist, "UISupportedInterfaceOrientations");
|
|
16734
|
+
const iphone = listed ?? iosIpadOrientations;
|
|
16735
|
+
const portrait = iphone.some((value) => value === iosPortrait || value === iosPortraitUpsideDown);
|
|
16736
|
+
const landscape = iphone.some((value) => iosLandscape.includes(value));
|
|
16737
|
+
const matchesByOrientation = {
|
|
16738
|
+
portrait: portrait && !landscape,
|
|
16739
|
+
landscape: landscape && !portrait,
|
|
16740
|
+
any: portrait && landscape
|
|
16741
|
+
};
|
|
16742
|
+
const matches = matchesByOrientation[orientation];
|
|
16743
|
+
if (!matches) {
|
|
16744
|
+
issues.push({
|
|
16745
|
+
level: "error",
|
|
16746
|
+
message: `iOS Info.plist UISupportedInterfaceOrientations (${listed === void 0 ? "key missing, so every orientation" : iphone.join(", ") || "none"}) does not match mpgd.game.json native.orientation "${orientation}".`
|
|
16747
|
+
});
|
|
16703
16748
|
}
|
|
16704
|
-
|
|
16705
|
-
|
|
16706
|
-
|
|
16707
|
-
|
|
16708
|
-
|
|
16709
|
-
|
|
16710
|
-
|
|
16711
|
-
|
|
16712
|
-
"import { createTapGame } from '../lite/tapGame';",
|
|
16713
|
-
""
|
|
16714
|
-
].join("\n")
|
|
16715
|
-
},
|
|
16716
|
-
{
|
|
16717
|
-
anchor: " const shell = createTapGame({ mount, title: '__GAME_TITLE__' });\n",
|
|
16718
|
-
replacement: [
|
|
16719
|
-
" const shell = createTapGame({ mount, title: '__GAME_TITLE__' });",
|
|
16720
|
-
" const disposeNotificationSettings = mountNotificationSettingsPanel(mount, input.context);",
|
|
16721
|
-
""
|
|
16722
|
-
].join("\n")
|
|
16723
|
-
},
|
|
16724
|
-
{
|
|
16725
|
-
anchor: " audio.dispose();\n",
|
|
16726
|
-
replacement: [" audio.dispose();", " disposeNotificationSettings();", ""].join("\n")
|
|
16749
|
+
}
|
|
16750
|
+
if (deviceFamily === "universal") {
|
|
16751
|
+
const ipad = readPlistStrings(plist, "UISupportedInterfaceOrientations~ipad") ?? readPlistStrings(plist, "UISupportedInterfaceOrientations") ?? iosIpadOrientations;
|
|
16752
|
+
if (iosIpadOrientations.some((value) => !ipad.includes(value))) {
|
|
16753
|
+
issues.push({
|
|
16754
|
+
level: "warning",
|
|
16755
|
+
message: "iOS Info.plist iPad orientations (UISupportedInterfaceOrientations~ipad, or UISupportedInterfaceOrientations when that key is absent) do not list all four; Apple asks iPad apps to support every orientation (TN3192) and deprecated UIRequiresFullScreen in iPadOS 26."
|
|
16756
|
+
});
|
|
16727
16757
|
}
|
|
16728
|
-
|
|
16758
|
+
}
|
|
16759
|
+
return issues;
|
|
16760
|
+
}
|
|
16761
|
+
function iosTargetedDeviceFamily(family) {
|
|
16762
|
+
return family === "iphone" ? "1" : "1,2";
|
|
16763
|
+
}
|
|
16764
|
+
function readIosAppReleaseDeviceFamily(project) {
|
|
16765
|
+
for (const id of readAppTargetConfigurationIds(project)) {
|
|
16766
|
+
const settings = locatePbxBuildSettings(project, id);
|
|
16767
|
+
if (settings.name !== "Release") {
|
|
16768
|
+
continue;
|
|
16769
|
+
}
|
|
16770
|
+
const value = /\bTARGETED_DEVICE_FAMILY\s*=\s*([^;]+);/u.exec(project.slice(settings.start, settings.end))?.[1];
|
|
16771
|
+
return value?.trim().replace(/^"(.*)"$/u, "$1").replace(/\s+/gu, "");
|
|
16772
|
+
}
|
|
16773
|
+
return void 0;
|
|
16774
|
+
}
|
|
16775
|
+
function inspectIosProjectDeviceFamily(project, family) {
|
|
16776
|
+
if (family === void 0) {
|
|
16777
|
+
return [];
|
|
16778
|
+
}
|
|
16779
|
+
const actual = readIosAppReleaseDeviceFamily(project);
|
|
16780
|
+
const expected = iosTargetedDeviceFamily(family);
|
|
16781
|
+
if (actual === expected) {
|
|
16782
|
+
return [];
|
|
16783
|
+
}
|
|
16784
|
+
return [{
|
|
16785
|
+
level: "error",
|
|
16786
|
+
message: `iOS App target Release TARGETED_DEVICE_FAMILY is ${actual ?? "unset"}, but mpgd.targets.json ios metadata.iosDeviceFamily "${family}" needs "${expected}".`
|
|
16787
|
+
}];
|
|
16788
|
+
}
|
|
16789
|
+
var androidOrientationValues = {
|
|
16790
|
+
portrait: ["portrait", "sensorPortrait", "userPortrait", "reversePortrait"],
|
|
16791
|
+
landscape: ["landscape", "sensorLandscape", "userLandscape", "reverseLandscape"],
|
|
16792
|
+
any: ["unspecified", "user", "sensor", "fullSensor", "fullUser"]
|
|
16729
16793
|
};
|
|
16730
|
-
|
|
16731
|
-
|
|
16732
|
-
|
|
16733
|
-
"",
|
|
16734
|
-
"This game was created with `--providers notifications`. Its topics live in",
|
|
16735
|
-
"`notifications/topics.json` and the backend settings (`advertisingRules`,",
|
|
16736
|
-
"the advertising sender) in `notifications/deployment.json`; run",
|
|
16737
|
-
"`pnpm validate:notifications` after every change and before every release.",
|
|
16738
|
-
"Replace the sender contact placeholder in `notifications/deployment.json`",
|
|
16739
|
-
"before any advertising is sent. The notification settings screen asks for the",
|
|
16740
|
-
"OS or host permission and records reminder, advertising (\uAD11\uACE0\uC131 \uC815\uBCF4",
|
|
16741
|
-
"\uC218\uC2E0\uB3D9\uC758), and night consent through the notifications runtime. See",
|
|
16742
|
-
"`docs/NOTIFICATIONS.md` and, for Android and iOS, the mpgd-kit Firebase setup",
|
|
16743
|
-
"guide; `mpgd target init capacitor --providers notifications` installs the two",
|
|
16744
|
-
"Capacitor plugins into a game-owned shell.",
|
|
16745
|
-
""
|
|
16746
|
-
].join("\n");
|
|
16747
|
-
var notificationsCapacitorGateways = Object.freeze({
|
|
16748
|
-
android: "src/platform/buildGateways/capacitorAndroid.ts",
|
|
16749
|
-
ios: "src/platform/buildGateways/capacitorIos.ts"
|
|
16750
|
-
});
|
|
16751
|
-
var notificationsCapacitorGatewayModules = Object.freeze([
|
|
16752
|
-
"src/platform/capacitorNotifications.ts",
|
|
16753
|
-
"src/platform/notifications.ts",
|
|
16754
|
-
"src/platform/notificationBackend.ts",
|
|
16755
|
-
"src/platform/notificationSettings.ts",
|
|
16756
|
-
"src/platform/notificationOpens.ts"
|
|
16757
|
-
]);
|
|
16758
|
-
var notificationsCapacitorSupportFiles = Object.freeze([
|
|
16759
|
-
...notificationsCapacitorGatewayModules,
|
|
16760
|
-
"notifications/topics.json",
|
|
16761
|
-
"notifications/deployment.json",
|
|
16762
|
-
"tools/validate-notifications.ts",
|
|
16763
|
-
"docs/NOTIFICATIONS.md"
|
|
16764
|
-
]);
|
|
16765
|
-
var notificationsCapacitorTemplateSupportFiles = Object.freeze([
|
|
16766
|
-
"src/platform/serverSession.ts",
|
|
16767
|
-
"src/platform/developmentServerSession.ts"
|
|
16768
|
-
]);
|
|
16769
|
-
var notificationsCapacitorTemplateSupportImporters = Object.freeze({
|
|
16770
|
-
"src/platform/developmentServerSession.ts": {
|
|
16771
|
-
importer: "src/platform/serverSession.ts",
|
|
16772
|
-
requires: { packageName: "@mpgd/game-services", exportName: "createDevelopmentServerSession" },
|
|
16773
|
-
warning: "src/platform/serverSession.ts predates development notification sessions (VITE_MPGD_DEV_NOTIFICATION_SESSION); to try notifications locally, copy serverSession.ts and developmentServerSession.ts from the starter template."
|
|
16794
|
+
function inspectAndroidManifestOrientation(manifest, orientation) {
|
|
16795
|
+
if (orientation === void 0) {
|
|
16796
|
+
return [];
|
|
16774
16797
|
}
|
|
16775
|
-
|
|
16776
|
-
|
|
16777
|
-
|
|
16778
|
-
function assertAndroidSettingsAppProject(source) {
|
|
16779
|
-
const clean = stripGradleComments(source);
|
|
16780
|
-
const code = maskGradleStrings(clean);
|
|
16781
|
-
const includes = [...clean.matchAll(/\binclude\s*(?:\(([^)]*)\)|([^\r\n;]+))/gu)];
|
|
16782
|
-
const includesApp = includes.some((match) => code.slice(match.index, match.index + 7) === "include" && /(?:^|,)\s*["']:app["']\s*(?:,|$)/u.test(match[1] ?? match[2] ?? ""));
|
|
16783
|
-
if (!includesApp) {
|
|
16784
|
-
throw new Error("Android settings must include :app at android/app without remapping it.");
|
|
16798
|
+
const activity = findAndroidLauncherActivity(manifest);
|
|
16799
|
+
if (activity === void 0) {
|
|
16800
|
+
return [{ level: "error", message: "Android manifest has no launcher activity." }];
|
|
16785
16801
|
}
|
|
16786
|
-
|
|
16802
|
+
const value = readXmlAttribute(activity.tag, "android:screenOrientation") ?? "unspecified";
|
|
16803
|
+
const issues = [];
|
|
16804
|
+
if (!androidOrientationValues[orientation].includes(value)) {
|
|
16805
|
+
issues.push({
|
|
16806
|
+
level: "error",
|
|
16807
|
+
message: `Android launcher activity android:screenOrientation="${value}" does not match mpgd.game.json native.orientation "${orientation}".`
|
|
16808
|
+
});
|
|
16809
|
+
}
|
|
16810
|
+
const application = /<application\b[^>]*?>/u.exec(maskXmlComments(manifest))?.[0] ?? "";
|
|
16811
|
+
if (orientation !== "any" && readXmlAttribute(application, "android:appCategory") !== "game") {
|
|
16812
|
+
issues.push({
|
|
16813
|
+
level: "warning",
|
|
16814
|
+
message: 'Android <application> has no android:appCategory="game": Android 16 ignores the orientation lock on tablets and foldables for apps targeting API 36 unless they are games.'
|
|
16815
|
+
});
|
|
16816
|
+
}
|
|
16817
|
+
return issues;
|
|
16787
16818
|
}
|
|
16788
|
-
function
|
|
16789
|
-
const
|
|
16790
|
-
const
|
|
16791
|
-
|
|
16792
|
-
|
|
16793
|
-
|
|
16819
|
+
function findAndroidLauncherActivity(manifest) {
|
|
16820
|
+
const elements2 = readAndroidActivityElements(manifest, "activity");
|
|
16821
|
+
const direct = elements2.find((element) => element.launcher);
|
|
16822
|
+
if (direct !== void 0) {
|
|
16823
|
+
return { start: direct.start, tag: direct.tag };
|
|
16824
|
+
}
|
|
16825
|
+
const alias = readAndroidActivityElements(manifest, "activity-alias").find((element) => element.launcher);
|
|
16826
|
+
const target2 = alias === void 0 ? void 0 : readXmlAttribute(alias.tag, "android:targetActivity");
|
|
16827
|
+
if (target2 === void 0) {
|
|
16828
|
+
return void 0;
|
|
16829
|
+
}
|
|
16830
|
+
const sameClass = (name) => name === target2 || target2.startsWith(".") && name.endsWith(target2) || name.startsWith(".") && target2.endsWith(name) || !name.includes(".") && name !== "" && target2.endsWith(`.${name}`) || !target2.includes(".") && name.endsWith(`.${target2}`);
|
|
16831
|
+
const match = elements2.filter((element) => sameClass(readXmlAttribute(element.tag, "android:name") ?? ""));
|
|
16832
|
+
return match.length === 1 && match[0] !== void 0 ? { start: match[0].start, tag: match[0].tag } : void 0;
|
|
16833
|
+
}
|
|
16834
|
+
function readAndroidActivityElements(manifest, element) {
|
|
16835
|
+
const masked = maskXmlComments(manifest);
|
|
16836
|
+
const openings = new RegExp(`<${element}(?=[\\s/>])(?:"[^"]*"|'[^']*'|[^>"'])*?(\\/?)>`, "gu");
|
|
16837
|
+
const found = [];
|
|
16838
|
+
for (const opening of masked.matchAll(openings)) {
|
|
16839
|
+
const start = opening.index ?? 0;
|
|
16840
|
+
const tagEnd = start + opening[0].length;
|
|
16841
|
+
const tag = manifest.slice(start, tagEnd);
|
|
16842
|
+
if (opening[1] === "/") {
|
|
16843
|
+
found.push({ start, tag, launcher: false });
|
|
16844
|
+
continue;
|
|
16845
|
+
}
|
|
16846
|
+
const close = masked.indexOf(`</${element}>`, tagEnd);
|
|
16847
|
+
const body = close === -1 ? "" : masked.slice(tagEnd, close);
|
|
16848
|
+
found.push({ start, tag, launcher: /android\.intent\.category\.LAUNCHER/u.test(body) });
|
|
16794
16849
|
}
|
|
16850
|
+
return found;
|
|
16795
16851
|
}
|
|
16796
|
-
function
|
|
16797
|
-
|
|
16798
|
-
|
|
16799
|
-
|
|
16800
|
-
|
|
16801
|
-
|
|
16802
|
-
|
|
16803
|
-
|
|
16804
|
-
|
|
16805
|
-
|
|
16852
|
+
function maskXmlComments(xml) {
|
|
16853
|
+
return xml.replace(/<!--[\s\S]*?-->/gu, (comment) => comment.replace(/[^\n]/gu, " "));
|
|
16854
|
+
}
|
|
16855
|
+
function xmlAttribute(name) {
|
|
16856
|
+
return new RegExp(`(\\s+)${escapeRegExp(name)}\\s*=\\s*(?:"([^"]*)"|'([^']*)')`, "u");
|
|
16857
|
+
}
|
|
16858
|
+
function readXmlAttribute(tag, name) {
|
|
16859
|
+
const match = xmlAttribute(name).exec(tag);
|
|
16860
|
+
return match === null ? void 0 : match[2] ?? match[3];
|
|
16861
|
+
}
|
|
16862
|
+
function readAppTargetConfigurationIds(project) {
|
|
16863
|
+
const target2 = findPbxObject(project, (body) => /\bisa\s*=\s*PBXNativeTarget;/u.test(body) && /\bname\s*=\s*"?App"?\s*;/u.test(body));
|
|
16864
|
+
const listId = /\bbuildConfigurationList\s*=\s*([A-F0-9]+)\b/u.exec(target2?.text ?? "")?.[1];
|
|
16865
|
+
const list = listId === void 0 ? void 0 : findPbxObjectById(project, listId);
|
|
16866
|
+
const ids = list === void 0 ? void 0 : readPbxList(project, list, "buildConfigurations")?.ids;
|
|
16867
|
+
if (ids === void 0) {
|
|
16868
|
+
throw new Error("Xcode project has no App target build configuration list.");
|
|
16869
|
+
}
|
|
16870
|
+
return ids;
|
|
16871
|
+
}
|
|
16872
|
+
function locatePbxBuildSettings(project, id) {
|
|
16873
|
+
const marker = new RegExp(`\\b${id}\\s*(?:\\/\\*[^*]*\\*\\/)?\\s*=\\s*\\{`, "u").exec(project);
|
|
16874
|
+
if (marker === null) {
|
|
16875
|
+
throw new Error(`Xcode build configuration ${id} is missing.`);
|
|
16876
|
+
}
|
|
16877
|
+
const object = readBraced(project, project.indexOf("{", marker.index));
|
|
16878
|
+
const name = /\bname\s*=\s*"?([^";]+)"?\s*;/u.exec(object.text.replace(/\bbuildSettings\s*=\s*\{[\s\S]*\}\s*;/u, ""))?.[1] ?? "";
|
|
16879
|
+
const settingsMarker = /\bbuildSettings\s*=\s*\{/u.exec(object.text);
|
|
16880
|
+
if (settingsMarker === null) {
|
|
16881
|
+
throw new Error(`Xcode build configuration ${id} has no build settings.`);
|
|
16882
|
+
}
|
|
16883
|
+
const settings = readBraced(project, object.start + settingsMarker.index + settingsMarker[0].length - 1);
|
|
16884
|
+
return { name: name.trim(), start: settings.start, end: settings.end };
|
|
16885
|
+
}
|
|
16886
|
+
function readBraced(source, open2) {
|
|
16887
|
+
let depth = 0;
|
|
16888
|
+
let quote = false;
|
|
16889
|
+
for (let index = open2; index < source.length; index += 1) {
|
|
16890
|
+
const character = source[index];
|
|
16891
|
+
if (quote) {
|
|
16892
|
+
if (character === "\\") {
|
|
16806
16893
|
index += 1;
|
|
16807
|
-
} else if (character ===
|
|
16808
|
-
quote =
|
|
16894
|
+
} else if (character === '"') {
|
|
16895
|
+
quote = false;
|
|
16809
16896
|
}
|
|
16810
16897
|
continue;
|
|
16811
16898
|
}
|
|
16812
|
-
if (character ===
|
|
16813
|
-
|
|
16814
|
-
|
|
16899
|
+
if (character === "/" && source[index + 1] === "*") {
|
|
16900
|
+
const close = source.indexOf("*/", index + 2);
|
|
16901
|
+
index = close === -1 ? source.length : close + 1;
|
|
16815
16902
|
continue;
|
|
16816
16903
|
}
|
|
16817
|
-
if (character === "
|
|
16818
|
-
|
|
16819
|
-
|
|
16820
|
-
|
|
16904
|
+
if (character === '"') {
|
|
16905
|
+
quote = true;
|
|
16906
|
+
} else if (character === "{") {
|
|
16907
|
+
depth += 1;
|
|
16908
|
+
} else if (character === "}") {
|
|
16909
|
+
depth -= 1;
|
|
16910
|
+
if (depth === 0) {
|
|
16911
|
+
return { text: source.slice(open2 + 1, index), start: open2 + 1, end: index };
|
|
16821
16912
|
}
|
|
16822
|
-
|
|
16913
|
+
}
|
|
16914
|
+
}
|
|
16915
|
+
throw new Error("Xcode project has an unclosed object.");
|
|
16916
|
+
}
|
|
16917
|
+
function isRecord5(value) {
|
|
16918
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
16919
|
+
}
|
|
16920
|
+
var iosAppPrivacyManifestFile = "PrivacyInfo.xcprivacy";
|
|
16921
|
+
var iosAppPrivacyManifestXml = [
|
|
16922
|
+
'<?xml version="1.0" encoding="UTF-8"?>',
|
|
16923
|
+
'<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">',
|
|
16924
|
+
'<plist version="1.0">',
|
|
16925
|
+
"<dict>",
|
|
16926
|
+
" <key>NSPrivacyTracking</key>",
|
|
16927
|
+
" <false/>",
|
|
16928
|
+
" <key>NSPrivacyTrackingDomains</key>",
|
|
16929
|
+
" <array/>",
|
|
16930
|
+
" <key>NSPrivacyCollectedDataTypes</key>",
|
|
16931
|
+
" <array/>",
|
|
16932
|
+
" <key>NSPrivacyAccessedAPITypes</key>",
|
|
16933
|
+
" <array/>",
|
|
16934
|
+
"</dict>",
|
|
16935
|
+
"</plist>",
|
|
16936
|
+
""
|
|
16937
|
+
].join("\n");
|
|
16938
|
+
function iosAppBundlesResource(project, fileName) {
|
|
16939
|
+
return findAppResourcesPhase(project)?.text.includes(`/* ${fileName} in Resources */`) === true;
|
|
16940
|
+
}
|
|
16941
|
+
function inspectIosAppPrivacyManifest(project) {
|
|
16942
|
+
if (iosAppBundlesResource(project, iosAppPrivacyManifestFile)) {
|
|
16943
|
+
return [];
|
|
16944
|
+
}
|
|
16945
|
+
return [{
|
|
16946
|
+
level: "warning",
|
|
16947
|
+
message: `iOS App target does not bundle an app-level ${iosAppPrivacyManifestFile}; add one to the App group and its Resources phase (the kit reference shell has one) and declare any required-reason API the game's own native code uses.`
|
|
16948
|
+
}];
|
|
16949
|
+
}
|
|
16950
|
+
function findAppResourcesPhase(project) {
|
|
16951
|
+
const target2 = findPbxObject(project, (body) => /\bisa\s*=\s*PBXNativeTarget;/u.test(body) && /\bname\s*=\s*"?App"?\s*;/u.test(body));
|
|
16952
|
+
if (target2 === void 0) {
|
|
16953
|
+
return void 0;
|
|
16954
|
+
}
|
|
16955
|
+
for (const id of readPbxList(project, target2, "buildPhases")?.ids ?? []) {
|
|
16956
|
+
const phase = findPbxObjectById(project, id);
|
|
16957
|
+
if (phase === void 0 || !/\bisa\s*=\s*PBXResourcesBuildPhase\s*;/u.test(phase.text)) {
|
|
16823
16958
|
continue;
|
|
16824
16959
|
}
|
|
16825
|
-
|
|
16826
|
-
|
|
16827
|
-
|
|
16828
|
-
|
|
16960
|
+
const files = readPbxList(project, phase, "files");
|
|
16961
|
+
return files === void 0 ? void 0 : { text: files.text, filesEnd: files.end };
|
|
16962
|
+
}
|
|
16963
|
+
return void 0;
|
|
16964
|
+
}
|
|
16965
|
+
function findPbxObject(project, accept) {
|
|
16966
|
+
for (const match of project.matchAll(/\b([A-F0-9]+)\s*(?:\/\*[^*]*\*\/)?\s*=\s*\{/gu)) {
|
|
16967
|
+
const body = readBraced(project, project.indexOf("{", match.index ?? 0));
|
|
16968
|
+
if (accept(body.text)) {
|
|
16969
|
+
return { text: body.text, start: body.start };
|
|
16970
|
+
}
|
|
16971
|
+
}
|
|
16972
|
+
return void 0;
|
|
16973
|
+
}
|
|
16974
|
+
function findPbxObjectById(project, id) {
|
|
16975
|
+
const marker = new RegExp(`\\b${id}\\s*(?:\\/\\*[^*]*\\*\\/)?\\s*=\\s*\\{`, "u").exec(project);
|
|
16976
|
+
if (marker === null) {
|
|
16977
|
+
return void 0;
|
|
16978
|
+
}
|
|
16979
|
+
const body = readBraced(project, project.indexOf("{", marker.index));
|
|
16980
|
+
return { text: body.text, start: body.start };
|
|
16981
|
+
}
|
|
16982
|
+
function readPbxList(project, object, key) {
|
|
16983
|
+
const marker = new RegExp(`\\b${key}\\s*=\\s*\\(`, "u").exec(object.text);
|
|
16984
|
+
if (marker === null) {
|
|
16985
|
+
return void 0;
|
|
16986
|
+
}
|
|
16987
|
+
const start = object.start + marker.index + marker[0].length;
|
|
16988
|
+
let quote = false;
|
|
16989
|
+
for (let index = start; index < project.length; index += 1) {
|
|
16990
|
+
const character = project[index];
|
|
16991
|
+
if (quote) {
|
|
16992
|
+
if (character === "\\") {
|
|
16829
16993
|
index += 1;
|
|
16994
|
+
} else if (character === '"') {
|
|
16995
|
+
quote = false;
|
|
16830
16996
|
}
|
|
16831
|
-
index += 1;
|
|
16832
16997
|
continue;
|
|
16833
16998
|
}
|
|
16834
|
-
|
|
16999
|
+
if (character === "/" && project[index + 1] === "*") {
|
|
17000
|
+
const close = project.indexOf("*/", index + 2);
|
|
17001
|
+
index = close === -1 ? project.length : close + 1;
|
|
17002
|
+
continue;
|
|
17003
|
+
}
|
|
17004
|
+
if (character === '"') {
|
|
17005
|
+
quote = true;
|
|
17006
|
+
} else if (character === ")") {
|
|
17007
|
+
const text = project.slice(start, index);
|
|
17008
|
+
const ids = text.replace(/\/\*[^]*?\*\//gu, " ").split(",").map((entry) => /^\s*([A-F0-9]+)\b/u.exec(entry)?.[1]).filter((id) => id !== void 0);
|
|
17009
|
+
return { text, ids, end: index };
|
|
17010
|
+
}
|
|
16835
17011
|
}
|
|
16836
|
-
return
|
|
17012
|
+
return void 0;
|
|
16837
17013
|
}
|
|
16838
|
-
|
|
16839
|
-
|
|
16840
|
-
|
|
16841
|
-
if (
|
|
16842
|
-
return
|
|
17014
|
+
var admobCapacitorPlugin = "@capacitor-community/admob";
|
|
17015
|
+
var androidAdIdPermission = "com.google.android.gms.permission.AD_ID";
|
|
17016
|
+
function shellUsesAdMob(shellPackage) {
|
|
17017
|
+
if (!isRecord5(shellPackage)) {
|
|
17018
|
+
return false;
|
|
16843
17019
|
}
|
|
16844
|
-
|
|
16845
|
-
const field = "(?:applicationId|applicationIdSuffix|versionCode|versionName|versionNameSuffix)";
|
|
16846
|
-
const assignment = new RegExp(`\\b${field}(?:\\s*(?:\\+?=|\\(|["'\\d]|\\.set\\s*\\()|[ \\t]+[A-Za-z_$])`, "u");
|
|
16847
|
-
return setter.test(masked) || assignment.test(masked);
|
|
17020
|
+
return [shellPackage.dependencies, shellPackage.devDependencies].some((dependencies) => isRecord5(dependencies) && dependencies[admobCapacitorPlugin] !== void 0);
|
|
16848
17021
|
}
|
|
16849
|
-
function
|
|
16850
|
-
const
|
|
16851
|
-
|
|
16852
|
-
const key = "(?:applicationId|applicationIdSuffix|versionCode|versionName|versionNameSuffix)";
|
|
16853
|
-
const bracketWrite = `\\[\\s*["']${key}["']\\s*\\]\\s*(?:\\+?=|\\.\\s*set\\s*\\(|\\()`;
|
|
16854
|
-
const expression = new RegExp(bracketWrite, "gu");
|
|
16855
|
-
return [...clean.matchAll(expression)].some((match) => code[match.index] === "[");
|
|
17022
|
+
function adIdPermissions(manifest) {
|
|
17023
|
+
const masked = maskXmlComments(manifest);
|
|
17024
|
+
return [...masked.matchAll(/<uses-permission(?=[\s/>])[^>]*?\/?>(?:\s*<\/uses-permission>)?/gu)].filter((match) => readXmlAttribute(match[0], "android:name") === androidAdIdPermission);
|
|
16856
17025
|
}
|
|
16857
|
-
function
|
|
16858
|
-
const
|
|
16859
|
-
|
|
16860
|
-
|
|
16861
|
-
|
|
16862
|
-
|
|
16863
|
-
}
|
|
16864
|
-
const tail = clean.slice(match.index);
|
|
16865
|
-
const literal = /^resValue\s*(?:\(\s*)?["'][^"']+["']\s*,\s*["']([^"']+)["']/u.exec(tail);
|
|
16866
|
-
return literal === null || literal[1] === "app_name";
|
|
16867
|
-
});
|
|
16868
|
-
}
|
|
16869
|
-
function hasAndroidResourceSourceSetOverride(source) {
|
|
16870
|
-
const code = maskGradleStrings(stripGradleComments(source));
|
|
16871
|
-
return /\bsourceSets\b/u.test(code) && /\bres\b/u.test(code);
|
|
16872
|
-
}
|
|
16873
|
-
function hasAndroidManifestSourceSetOverride(source) {
|
|
16874
|
-
const code = maskGradleStrings(stripGradleComments(source));
|
|
16875
|
-
return /\bsourceSets\b/u.test(code) && /\bmanifest\b/u.test(code);
|
|
16876
|
-
}
|
|
16877
|
-
function hasGradleTaskAction(source) {
|
|
16878
|
-
const code = maskGradleStrings(stripGradleComments(source));
|
|
16879
|
-
const withoutStandardClean = code.replace(/\btask\s+clean\s*\(\s*type\s*:\s*Delete\s*\)\s*\{\s*delete\s+rootProject\.buildDir\s*;?\s*\}/gu, "");
|
|
16880
|
-
return /\b(?:doFirst|doLast|whenTaskAdded|whenReady)\s*(?:\(|\{)/u.test(withoutStandardClean) || /\btasks\s*(?:\.|\[|\{)/u.test(withoutStandardClean) || /\btask\s*(?:\([^)]*\)|[A-Za-z_]\w*(?:\s*\([^)]*\))?)\s*\{/u.test(withoutStandardClean);
|
|
16881
|
-
}
|
|
16882
|
-
function hasGradlePropertySetter(source) {
|
|
16883
|
-
const clean = stripGradleComments(source);
|
|
16884
|
-
const masked = maskGradleStrings(clean);
|
|
16885
|
-
return /\bsetProperty\s*\(/u.test(masked);
|
|
16886
|
-
}
|
|
16887
|
-
function countGradleIdentityWrites(source, key) {
|
|
16888
|
-
const masked = maskGradleStrings(stripGradleComments(source));
|
|
16889
|
-
const expression = new RegExp(`\\b${key}\\b`, "gu");
|
|
16890
|
-
return [...masked.matchAll(expression)].filter((match) => {
|
|
16891
|
-
const index = match.index ?? 0;
|
|
16892
|
-
const rest = masked.slice(index + key.length);
|
|
16893
|
-
if (masked[index - 1] === ".") {
|
|
16894
|
-
return /^\s*(?:\+?=|\(|\.set\s*\()/u.test(rest);
|
|
16895
|
-
}
|
|
16896
|
-
return /^\s*(?:\+?=|\(|["'\d]|[A-Za-z_$])/u.test(rest);
|
|
16897
|
-
}).length;
|
|
17026
|
+
function readAndroidAdIdDeclaration(manifest) {
|
|
17027
|
+
const permissions = adIdPermissions(manifest);
|
|
17028
|
+
if (permissions.length === 0) {
|
|
17029
|
+
return "absent";
|
|
17030
|
+
}
|
|
17031
|
+
return permissions.some((match) => readXmlAttribute(match[0], "tools:node") === "remove") ? "removed" : "declared";
|
|
16898
17032
|
}
|
|
16899
|
-
function
|
|
16900
|
-
|
|
16901
|
-
|
|
16902
|
-
|
|
16903
|
-
|
|
16904
|
-
|
|
16905
|
-
if (character === "\\" && index + 1 < clean.length) {
|
|
16906
|
-
masked += " ";
|
|
16907
|
-
index += 1;
|
|
16908
|
-
} else if (character === quote) {
|
|
16909
|
-
masked += character;
|
|
16910
|
-
quote = void 0;
|
|
16911
|
-
} else {
|
|
16912
|
-
masked += character === "\n" ? "\n" : " ";
|
|
16913
|
-
}
|
|
16914
|
-
} else if (character === '"' || character === "'") {
|
|
16915
|
-
quote = character;
|
|
16916
|
-
masked += character;
|
|
16917
|
-
} else {
|
|
16918
|
-
masked += character;
|
|
16919
|
-
}
|
|
17033
|
+
function readGameAdvertisingIdPolicy(gameConfig) {
|
|
17034
|
+
const ads = isRecord5(gameConfig) ? gameConfig.ads : void 0;
|
|
17035
|
+
const admob = isRecord5(ads) ? ads.admob : void 0;
|
|
17036
|
+
const value = isRecord5(admob) ? admob.advertisingId : void 0;
|
|
17037
|
+
if (value === void 0 || value === "declared") {
|
|
17038
|
+
return "declared";
|
|
16920
17039
|
}
|
|
16921
|
-
|
|
17040
|
+
if (value === "removed") {
|
|
17041
|
+
return "removed";
|
|
17042
|
+
}
|
|
17043
|
+
throw new Error("mpgd.game.json ads.admob.advertisingId must be 'declared' or 'removed'.");
|
|
16922
17044
|
}
|
|
16923
|
-
function
|
|
16924
|
-
const
|
|
16925
|
-
|
|
16926
|
-
if (
|
|
16927
|
-
|
|
16928
|
-
|
|
17045
|
+
function inspectAndroidManifestAds(manifest, usesAds, advertisingIdPolicy = "declared") {
|
|
17046
|
+
const issues = [];
|
|
17047
|
+
const declaration = readAndroidAdIdDeclaration(manifest);
|
|
17048
|
+
if (usesAds) {
|
|
17049
|
+
const hasAppId = [...maskXmlComments(manifest).matchAll(/<meta-data\b[^>]*?\/?>/gu)].some((match) => readXmlAttribute(match[0], "android:name") === "com.google.android.gms.ads.APPLICATION_ID");
|
|
17050
|
+
if (!hasAppId) {
|
|
17051
|
+
issues.push({
|
|
17052
|
+
level: "error",
|
|
17053
|
+
message: "The shell installs AdMob but neither android/app/src/main/AndroidManifest.xml nor the release source set's manifest has com.google.android.gms.ads.APPLICATION_ID meta-data; the app crashes at launch."
|
|
17054
|
+
});
|
|
16929
17055
|
}
|
|
16930
|
-
|
|
16931
|
-
|
|
16932
|
-
|
|
17056
|
+
if (declaration === "removed" && advertisingIdPolicy !== "removed") {
|
|
17057
|
+
issues.push({
|
|
17058
|
+
level: "error",
|
|
17059
|
+
message: `AndroidManifest.xml removes ${androidAdIdPermission} although the shell installs AdMob: ads get a zeroed advertising ID on Android 13+ and the Play Console answer no longer matches. Replace the tools:node="remove" line with <uses-permission android:name="${androidAdIdPermission}" />, or, for a deliberate no-advertising-ID policy (for example a child-directed game), set ads.admob.advertisingId to "removed" in mpgd.game.json.`
|
|
17060
|
+
});
|
|
16933
17061
|
}
|
|
16934
|
-
|
|
16935
|
-
|
|
16936
|
-
|
|
16937
|
-
|
|
17062
|
+
if (declaration !== "removed" && advertisingIdPolicy === "removed") {
|
|
17063
|
+
issues.push({
|
|
17064
|
+
level: "error",
|
|
17065
|
+
message: `mpgd.game.json ads.admob.advertisingId is "removed", but AndroidManifest.xml does not remove ${androidAdIdPermission}; add <uses-permission android:name="${androidAdIdPermission}" tools:node="remove" />.`
|
|
17066
|
+
});
|
|
16938
17067
|
}
|
|
17068
|
+
} else if (declaration !== "removed") {
|
|
17069
|
+
issues.push({
|
|
17070
|
+
level: "warning",
|
|
17071
|
+
message: `The shell shows no AdMob ads, but AndroidManifest.xml does not remove ${androidAdIdPermission}; a dependency can still merge it. Add <uses-permission android:name="${androidAdIdPermission}" tools:node="remove" />.`
|
|
17072
|
+
});
|
|
16939
17073
|
}
|
|
16940
|
-
|
|
16941
|
-
throw new Error("Existing ios project Release Info.plist cannot be read safely.");
|
|
16942
|
-
}
|
|
16943
|
-
return value;
|
|
17074
|
+
return issues;
|
|
16944
17075
|
}
|
|
16945
|
-
function
|
|
16946
|
-
|
|
16947
|
-
|
|
16948
|
-
if (name === void 0 || name === "$(inherited)") {
|
|
16949
|
-
if (/\bbaseConfigurationReference\s*=/u.test(target2)) {
|
|
16950
|
-
throw new Error("Existing ios project Release product name xcconfig is unsupported.");
|
|
16951
|
-
}
|
|
16952
|
-
const projectList = readIosProjectConfigurationList(source);
|
|
16953
|
-
if (projectList !== void 0) {
|
|
16954
|
-
const project = readIosReleaseConfiguration(source, projectList);
|
|
16955
|
-
name = readIosProductName(project);
|
|
16956
|
-
if ((name === void 0 || name === "$(inherited)") && /\bbaseConfigurationReference\s*=/u.test(project)) {
|
|
16957
|
-
throw new Error("Existing ios project Release product name xcconfig is unsupported.");
|
|
16958
|
-
}
|
|
16959
|
-
}
|
|
17076
|
+
function inspectIosInfoPlistAds(plist, usesAds) {
|
|
17077
|
+
if (!usesAds) {
|
|
17078
|
+
return [];
|
|
16960
17079
|
}
|
|
16961
|
-
|
|
16962
|
-
|
|
17080
|
+
const issues = [];
|
|
17081
|
+
if (readPlistTopLevelValue(plist, "GADApplicationIdentifier") === void 0) {
|
|
17082
|
+
issues.push({
|
|
17083
|
+
level: "error",
|
|
17084
|
+
message: "The shell installs AdMob but Info.plist has no GADApplicationIdentifier; the Google Mobile Ads SDK stops the app at launch."
|
|
17085
|
+
});
|
|
16963
17086
|
}
|
|
16964
|
-
|
|
16965
|
-
|
|
16966
|
-
|
|
16967
|
-
|
|
16968
|
-
|
|
16969
|
-
|
|
16970
|
-
assertIosInfoPlistExpansionSetting(readIosReleaseConfiguration(source, projectList));
|
|
17087
|
+
const networks = readPlistTopLevelValue(plist, "SKAdNetworkItems");
|
|
17088
|
+
if (networks === void 0 || !/cstr6suwn9\.skadnetwork/u.test(networks.xml)) {
|
|
17089
|
+
issues.push({
|
|
17090
|
+
level: "warning",
|
|
17091
|
+
message: "Info.plist SKAdNetworkItems does not list Google's cstr6suwn9.skadnetwork; copy the current list from https://developers.google.com/admob/ios/3p-skadnetworks."
|
|
17092
|
+
});
|
|
16971
17093
|
}
|
|
17094
|
+
return issues;
|
|
16972
17095
|
}
|
|
16973
|
-
|
|
16974
|
-
|
|
16975
|
-
|
|
16976
|
-
const
|
|
16977
|
-
const
|
|
16978
|
-
|
|
16979
|
-
|
|
16980
|
-
|
|
16981
|
-
|
|
16982
|
-
|
|
16983
|
-
|
|
16984
|
-
|
|
16985
|
-
const expression = `(?:^|[\\s{;])["']?${key}["']?\\s*=\\s*([^;]+);`;
|
|
16986
|
-
const pattern = new RegExp(expression, "gu");
|
|
16987
|
-
const values = [...configuration.matchAll(pattern)].map((match) => match[1]?.trim().replace(/^["']|["']$/gu, ""));
|
|
16988
|
-
if (values.length > 1 || /\bbaseConfigurationReference\s*=/u.test(configuration)) {
|
|
16989
|
-
throw new Error(`Existing ios project Release ${key} cannot be read safely.`);
|
|
17096
|
+
|
|
17097
|
+
// node_modules/.cache/mpgd-native-build/tools/target/native-store-policy.js
|
|
17098
|
+
function inspectNativeStorePolicy(input) {
|
|
17099
|
+
const gameConfig = input.gameRoot === void 0 ? void 0 : join13(input.gameRoot, "mpgd.game.json");
|
|
17100
|
+
const gameConfigJson = gameConfig !== void 0 && existsSync19(gameConfig) ? readJson2(gameConfig) : void 0;
|
|
17101
|
+
const orientation = readGameNativeOrientation(gameConfigJson);
|
|
17102
|
+
const shellPackage = join13(input.shellApp, "package.json");
|
|
17103
|
+
const usesAds = existsSync19(shellPackage) && shellUsesAdMob(readJson2(shellPackage));
|
|
17104
|
+
if (input.platform === "ios") {
|
|
17105
|
+
const project = join13(input.shellApp, "ios/App/App.xcodeproj/project.pbxproj");
|
|
17106
|
+
if (!existsSync19(project)) {
|
|
17107
|
+
return [];
|
|
16990
17108
|
}
|
|
16991
|
-
|
|
16992
|
-
|
|
16993
|
-
|
|
16994
|
-
|
|
16995
|
-
|
|
16996
|
-
|
|
16997
|
-
|
|
16998
|
-
|
|
16999
|
-
|
|
17000
|
-
|
|
17001
|
-
for (const key of ["INFOPLIST_PREPROCESSOR_DEFINITIONS", "INFOPLIST_PREFIX_HEADER"]) {
|
|
17002
|
-
if (effective(key) !== void 0) {
|
|
17003
|
-
throw new Error(`Existing ios project Release ${key} is unsupported.`);
|
|
17109
|
+
const projectSource = readFileSync20(project, "utf8");
|
|
17110
|
+
const deviceFamily = readIosDeviceFamily(input.metadata);
|
|
17111
|
+
const issues = [
|
|
17112
|
+
...inspectIosProjectDeviceFamily(projectSource, deviceFamily),
|
|
17113
|
+
...inspectIosAppPrivacyManifest(projectSource)
|
|
17114
|
+
];
|
|
17115
|
+
const plist = join13(dirname9(dirname9(project)), readIosReleaseInfoPlist(projectSource));
|
|
17116
|
+
if (existsSync19(plist)) {
|
|
17117
|
+
const plistSource = readFileSync20(plist, "utf8");
|
|
17118
|
+
issues.push(...inspectIosInfoPlistStorePolicy(plistSource), ...inspectIosInfoPlistOrientation(plistSource, orientation, deviceFamily ?? "universal"), ...inspectIosInfoPlistAds(plistSource, usesAds));
|
|
17004
17119
|
}
|
|
17120
|
+
return issues;
|
|
17005
17121
|
}
|
|
17006
|
-
const
|
|
17007
|
-
if (
|
|
17008
|
-
|
|
17122
|
+
const manifest = join13(input.shellApp, "android/app/src/main/AndroidManifest.xml");
|
|
17123
|
+
if (!existsSync19(manifest)) {
|
|
17124
|
+
return [];
|
|
17009
17125
|
}
|
|
17126
|
+
const manifestSource = readFileSync20(manifest, "utf8");
|
|
17127
|
+
const releaseManifest = join13(input.shellApp, "android/app/src/release/AndroidManifest.xml");
|
|
17128
|
+
const releaseSource = existsSync19(releaseManifest) ? readFileSync20(releaseManifest, "utf8") : "";
|
|
17129
|
+
return [
|
|
17130
|
+
...inspectAndroidManifestOrientation(manifestSource, orientation),
|
|
17131
|
+
...inspectAndroidManifestAds(`${manifestSource}
|
|
17132
|
+
${releaseSource}`, usesAds, usesAds ? readGameAdvertisingIdPolicy(gameConfigJson) : "declared")
|
|
17133
|
+
];
|
|
17010
17134
|
}
|
|
17011
|
-
function
|
|
17012
|
-
const
|
|
17013
|
-
|
|
17014
|
-
|
|
17015
|
-
|
|
17016
|
-
|
|
17017
|
-
const values = [...configuration.matchAll(/(?:^|[\s{;])["']?INFOPLIST_EXPAND_BUILD_SETTINGS["']?\s*=\s*([^;]+);/gu)].map((match) => match[1]?.trim().replace(/^["']|["']$/gu, ""));
|
|
17018
|
-
if (values.length === 0 && /\bbaseConfigurationReference\s*=/u.test(configuration)) {
|
|
17019
|
-
throw new Error("Existing ios project Release Info.plist expansion xcconfig is unsupported.");
|
|
17020
|
-
}
|
|
17021
|
-
if (values.length > 1 || values.some((value) => value !== "YES")) {
|
|
17022
|
-
throw new Error(`Existing ios project Release ${key} must be enabled.`);
|
|
17135
|
+
function readJson2(file) {
|
|
17136
|
+
const source = readFileSync20(file, "utf8");
|
|
17137
|
+
try {
|
|
17138
|
+
return JSON.parse(source);
|
|
17139
|
+
} catch (error) {
|
|
17140
|
+
throw new Error(`${file} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`);
|
|
17023
17141
|
}
|
|
17024
17142
|
}
|
|
17025
|
-
function
|
|
17026
|
-
|
|
17027
|
-
|
|
17143
|
+
function reportNativeStorePolicy(issues, production, log = (message) => console.warn(message)) {
|
|
17144
|
+
for (const issue of issues) {
|
|
17145
|
+
log(`[native-store-policy] ${issue.level}: ${issue.message}`);
|
|
17028
17146
|
}
|
|
17029
|
-
const
|
|
17030
|
-
if (
|
|
17031
|
-
throw new Error(
|
|
17147
|
+
const errors = issues.filter((issue) => issue.level === "error");
|
|
17148
|
+
if (production && errors.length > 0) {
|
|
17149
|
+
throw new Error(`Native shell is not ready for store release:
|
|
17150
|
+
${errors.map((issue) => `- ${issue.message}`).join("\n")}`);
|
|
17032
17151
|
}
|
|
17033
|
-
return values[0];
|
|
17034
17152
|
}
|
|
17035
|
-
|
|
17036
|
-
|
|
17037
|
-
|
|
17153
|
+
|
|
17154
|
+
// node_modules/.cache/mpgd-native-build/tools/target/native-ios-inspection.js
|
|
17155
|
+
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
17156
|
+
import { existsSync as existsSync20, mkdtempSync as mkdtempSync3, readFileSync as readFileSync21, rmSync as rmSync10, statSync as statSync4 } from "node:fs";
|
|
17157
|
+
import { tmpdir as tmpdir3 } from "node:os";
|
|
17158
|
+
import path7 from "node:path";
|
|
17159
|
+
function inspectSignedIosArchive(archive, input) {
|
|
17160
|
+
return inspectSignedIosApplication(path7.join(archive, "Products/Applications/App.app"), input);
|
|
17161
|
+
}
|
|
17162
|
+
function inspectSignedIosIpa(ipa, input) {
|
|
17163
|
+
if (!existsSync20(ipa) || !statSync4(ipa).isFile()) {
|
|
17164
|
+
throw new Error("iOS store IPA is missing.");
|
|
17038
17165
|
}
|
|
17039
|
-
const
|
|
17040
|
-
|
|
17041
|
-
|
|
17166
|
+
const runner = input.runner ?? { run: runCommand2 };
|
|
17167
|
+
const listing = runner.run("unzip", ["-Z1", ipa]);
|
|
17168
|
+
if (listing.status !== 0) {
|
|
17169
|
+
throw new Error("iOS store IPA could not be listed.");
|
|
17042
17170
|
}
|
|
17043
|
-
const
|
|
17044
|
-
if (
|
|
17045
|
-
|
|
17171
|
+
const entries = listing.stdout.split(/\r?\n/u).filter(Boolean);
|
|
17172
|
+
if (entries.some((entry) => entry.startsWith("/") || entry.includes("\\") || entry.split("/").some((segment) => segment === ".." || segment === "."))) {
|
|
17173
|
+
throw new Error("iOS store IPA has an unsafe path.");
|
|
17046
17174
|
}
|
|
17047
|
-
|
|
17048
|
-
|
|
17175
|
+
const appManifests = entries.filter((entry) => /^Payload\/[^/]+\.app\/Info\.plist$/u.test(entry));
|
|
17176
|
+
if (appManifests.length !== 1) {
|
|
17177
|
+
throw new Error("iOS store IPA must contain exactly one app manifest.");
|
|
17049
17178
|
}
|
|
17050
|
-
|
|
17051
|
-
|
|
17052
|
-
|
|
17053
|
-
|
|
17054
|
-
|
|
17055
|
-
|
|
17056
|
-
|
|
17057
|
-
|
|
17058
|
-
|
|
17059
|
-
|
|
17179
|
+
const appRelative = path7.dirname(appManifests[0] ?? "");
|
|
17180
|
+
const extracted = mkdtempSync3(path7.join(tmpdir3(), "mpgd-ios-inspection-"));
|
|
17181
|
+
try {
|
|
17182
|
+
const result = runner.run("unzip", ["-q", ipa, "-d", extracted]);
|
|
17183
|
+
if (result.status !== 0) {
|
|
17184
|
+
throw new Error("iOS store IPA could not be extracted.");
|
|
17185
|
+
}
|
|
17186
|
+
return inspectSignedIosApplication(path7.join(extracted, appRelative), input);
|
|
17187
|
+
} finally {
|
|
17188
|
+
rmSync10(extracted, { recursive: true, force: true });
|
|
17060
17189
|
}
|
|
17061
|
-
return listId;
|
|
17062
17190
|
}
|
|
17063
|
-
function
|
|
17064
|
-
const
|
|
17065
|
-
|
|
17066
|
-
|
|
17067
|
-
});
|
|
17068
|
-
if (appTargets.length !== 1) {
|
|
17069
|
-
throw new Error("Existing ios project App target could not be read safely.");
|
|
17191
|
+
function inspectSignedIosApplication(appPath, input) {
|
|
17192
|
+
const plist = path7.join(appPath, "Info.plist");
|
|
17193
|
+
if (!existsSync20(plist) || !statSync4(plist).isFile()) {
|
|
17194
|
+
throw new Error("Signed iOS app manifest is missing.");
|
|
17070
17195
|
}
|
|
17071
|
-
|
|
17072
|
-
|
|
17073
|
-
const productId = /\bproductReference\s*=\s*([A-F0-9]+)\b/u.exec(target2)?.[1];
|
|
17074
|
-
if (!/\bproductType\s*=\s*"?com\.apple\.product-type\.application"?\s*;/u.test(target2) || productId === void 0) {
|
|
17075
|
-
throw new Error("Existing ios App target must produce an application.");
|
|
17196
|
+
if (!existsSync20(path7.join(appPath, "Assets.car")) || existsSync20(path7.join(appPath, "Info-Smoke.plist"))) {
|
|
17197
|
+
throw new Error("Signed iOS app contains smoke assets or omits release icons.");
|
|
17076
17198
|
}
|
|
17077
|
-
const
|
|
17078
|
-
|
|
17079
|
-
|
|
17199
|
+
const runner = input.runner ?? { run: runCommand2 };
|
|
17200
|
+
const signature = runner.run("codesign", ["--verify", "--deep", "--strict", appPath]);
|
|
17201
|
+
if (signature.status !== 0) {
|
|
17202
|
+
throw new Error("iOS app code signature could not be verified.");
|
|
17080
17203
|
}
|
|
17081
|
-
|
|
17082
|
-
}
|
|
17083
|
-
|
|
17084
|
-
const
|
|
17085
|
-
|
|
17086
|
-
|
|
17087
|
-
function readIosReleaseConfiguration(source, listId) {
|
|
17088
|
-
const list = readPbxObject(source, listId);
|
|
17089
|
-
const configurations = /\bbuildConfigurations\s*=\s*\(([^)]*)\)/u.exec(list)?.[1];
|
|
17090
|
-
const releaseIds = configurations === void 0 ? [] : [...configurations.matchAll(/\b([A-F0-9]+)\s*\/\*\s*Release\s*\*\//gu)].map((match) => match[1]);
|
|
17091
|
-
if (releaseIds.length !== 1) {
|
|
17092
|
-
throw new Error("Existing ios project App Release configuration is missing or ambiguous.");
|
|
17204
|
+
const signingInfo = runner.run("codesign", ["-dv", "--verbose=4", appPath]);
|
|
17205
|
+
const signingText = `${signingInfo.stdout}
|
|
17206
|
+
${signingInfo.stderr}`;
|
|
17207
|
+
const expectedTeamLine = `TeamIdentifier=${input.expectedTeamId}`;
|
|
17208
|
+
if (signingInfo.status !== 0 || /Signature=adhoc/iu.test(signingText) || !signingText.split(/\r?\n/u).some((line) => line.trim() === expectedTeamLine)) {
|
|
17209
|
+
throw new Error("iOS app is not signed for the expected development team.");
|
|
17093
17210
|
}
|
|
17094
|
-
|
|
17211
|
+
const fields = [
|
|
17212
|
+
["bundleId", "CFBundleIdentifier", input.expectedBundleId],
|
|
17213
|
+
["marketingVersion", "CFBundleShortVersionString", input.expectedMarketingVersion],
|
|
17214
|
+
["buildNumber", "CFBundleVersion", input.expectedBuildNumber]
|
|
17215
|
+
];
|
|
17216
|
+
const observed = Object.fromEntries(fields.map(([name, key, expected]) => {
|
|
17217
|
+
const result = runner.run("/usr/libexec/PlistBuddy", ["-c", `Print :${key}`, plist]);
|
|
17218
|
+
if (result.status !== 0 || result.stdout.trim() !== expected) {
|
|
17219
|
+
throw new Error(`iOS app ${name} does not match release identity.`);
|
|
17220
|
+
}
|
|
17221
|
+
return [name, expected];
|
|
17222
|
+
}));
|
|
17223
|
+
const configFiles = [
|
|
17224
|
+
path7.join(appPath, "capacitor.config.json"),
|
|
17225
|
+
path7.join(appPath, "public/capacitor.config.json")
|
|
17226
|
+
];
|
|
17227
|
+
if (!configFiles.some((file) => existsSync20(file))) {
|
|
17228
|
+
throw new Error("Signed iOS app Capacitor configuration is missing.");
|
|
17229
|
+
}
|
|
17230
|
+
for (const configFile of configFiles) {
|
|
17231
|
+
if (existsSync20(configFile)) {
|
|
17232
|
+
assertNoDebugServer(configFile);
|
|
17233
|
+
}
|
|
17234
|
+
}
|
|
17235
|
+
return { ...observed, teamId: input.expectedTeamId, signed: true };
|
|
17095
17236
|
}
|
|
17096
|
-
function
|
|
17097
|
-
const
|
|
17098
|
-
if (
|
|
17099
|
-
throw new Error(
|
|
17237
|
+
function assertNoDebugServer(file) {
|
|
17238
|
+
const value = JSON.parse(readFileSync21(file, "utf8"));
|
|
17239
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
17240
|
+
throw new Error("Signed iOS app has malformed Capacitor configuration.");
|
|
17241
|
+
}
|
|
17242
|
+
const config2 = value;
|
|
17243
|
+
const server = config2.server;
|
|
17244
|
+
const ios = config2.ios;
|
|
17245
|
+
if (typeof server === "object" && server !== null && "url" in server || typeof ios === "object" && ios !== null && "webContentsDebuggingEnabled" in ios && ios.webContentsDebuggingEnabled === true) {
|
|
17246
|
+
throw new Error("Signed iOS app retains a live-reload or debug bridge configuration.");
|
|
17100
17247
|
}
|
|
17101
|
-
const opening = source.indexOf("{", marker.index);
|
|
17102
|
-
return readBracedText(source, opening);
|
|
17103
17248
|
}
|
|
17104
|
-
function
|
|
17105
|
-
|
|
17106
|
-
|
|
17249
|
+
function runCommand2(command, args) {
|
|
17250
|
+
const result = spawnSync3(command, [...args], {
|
|
17251
|
+
encoding: "utf8",
|
|
17252
|
+
env: nativeInspectionEnvironment(),
|
|
17253
|
+
maxBuffer: 4 * 1024 * 1024
|
|
17254
|
+
});
|
|
17255
|
+
if (result.error !== void 0) {
|
|
17256
|
+
throw new Error(`Required iOS inspection command is unavailable: ${command}.`);
|
|
17107
17257
|
}
|
|
17108
|
-
|
|
17109
|
-
|
|
17110
|
-
|
|
17111
|
-
|
|
17112
|
-
|
|
17113
|
-
|
|
17114
|
-
|
|
17115
|
-
|
|
17116
|
-
|
|
17117
|
-
|
|
17118
|
-
|
|
17119
|
-
|
|
17258
|
+
return {
|
|
17259
|
+
status: result.status,
|
|
17260
|
+
stdout: result.stdout,
|
|
17261
|
+
stderr: result.stderr
|
|
17262
|
+
};
|
|
17263
|
+
}
|
|
17264
|
+
|
|
17265
|
+
// node_modules/.cache/mpgd-native-build/tools/target/native-ios-signing.js
|
|
17266
|
+
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
17267
|
+
import { existsSync as existsSync21, statSync as statSync5 } from "node:fs";
|
|
17268
|
+
import path8 from "node:path";
|
|
17269
|
+
function resolveIosSigningPlan(environment, mode) {
|
|
17270
|
+
const teamId = environment.MPGD_IOS_TEAM_ID?.trim();
|
|
17271
|
+
if (teamId === void 0 || !/^[A-Z0-9]{10}$/u.test(teamId)) {
|
|
17272
|
+
throw new Error("Signed iOS builds require a 10-character MPGD_IOS_TEAM_ID.");
|
|
17273
|
+
}
|
|
17274
|
+
const style = environment.MPGD_IOS_SIGNING_STYLE ?? "Automatic";
|
|
17275
|
+
if (style !== "Automatic" && style !== "Manual") {
|
|
17276
|
+
throw new Error("MPGD_IOS_SIGNING_STYLE must be Automatic or Manual.");
|
|
17277
|
+
}
|
|
17278
|
+
const identity = environment.MPGD_IOS_SIGNING_IDENTITY?.trim();
|
|
17279
|
+
const profile2 = environment.MPGD_IOS_PROVISIONING_PROFILE_SPECIFIER?.trim();
|
|
17280
|
+
if (style === "Manual" && (identity === void 0 || identity === "" || profile2 === void 0 || profile2 === "")) {
|
|
17281
|
+
throw new Error("Manual iOS signing requires host identity and provisioning profile names.");
|
|
17282
|
+
}
|
|
17283
|
+
if (style === "Automatic" && profile2 !== void 0 && profile2 !== "") {
|
|
17284
|
+
throw new Error("Automatic iOS signing must not set a manual provisioning profile.");
|
|
17285
|
+
}
|
|
17286
|
+
const archiveBuildSettings = [
|
|
17287
|
+
"CODE_SIGNING_ALLOWED=YES",
|
|
17288
|
+
`CODE_SIGN_STYLE=${style}`,
|
|
17289
|
+
`DEVELOPMENT_TEAM=${teamId}`,
|
|
17290
|
+
...identity === void 0 || identity === "" ? [] : [`CODE_SIGN_IDENTITY=${identity}`],
|
|
17291
|
+
...profile2 === void 0 || profile2 === "" ? [] : [`PROVISIONING_PROFILE_SPECIFIER=${profile2}`],
|
|
17292
|
+
...environment.MPGD_IOS_SIGNING_KEYCHAIN === void 0 ? [] : [`OTHER_CODE_SIGN_FLAGS=--keychain ${JSON.stringify(environment.MPGD_IOS_SIGNING_KEYCHAIN)}`]
|
|
17293
|
+
];
|
|
17294
|
+
if (mode !== "store-export") {
|
|
17295
|
+
return { teamId, archiveBuildSettings };
|
|
17296
|
+
}
|
|
17297
|
+
const configuredExportFile = environment.MPGD_IOS_EXPORT_OPTIONS_PLIST;
|
|
17298
|
+
if (configuredExportFile === void 0 || configuredExportFile.trim() === "") {
|
|
17299
|
+
throw new Error("iOS store export requires MPGD_IOS_EXPORT_OPTIONS_PLIST.");
|
|
17300
|
+
}
|
|
17301
|
+
const exportOptionsPlist = path8.resolve(configuredExportFile);
|
|
17302
|
+
if (!existsSync21(exportOptionsPlist) || !statSync5(exportOptionsPlist).isFile()) {
|
|
17303
|
+
throw new Error("iOS export options plist must be an existing host file.");
|
|
17304
|
+
}
|
|
17305
|
+
const method = spawnSync4("/usr/libexec/PlistBuddy", ["-c", "Print :method", exportOptionsPlist], {
|
|
17306
|
+
encoding: "utf8",
|
|
17307
|
+
maxBuffer: 1024 * 1024
|
|
17308
|
+
});
|
|
17309
|
+
if (method.error !== void 0 || method.status !== 0 || !["app-store", "app-store-connect"].includes(method.stdout.trim())) {
|
|
17310
|
+
throw new Error("iOS export options must select an App Store distribution method.");
|
|
17311
|
+
}
|
|
17312
|
+
return { teamId, archiveBuildSettings, exportOptionsPlist };
|
|
17313
|
+
}
|
|
17314
|
+
|
|
17315
|
+
// node_modules/.cache/mpgd-native-build/tools/target/native-release-identity.js
|
|
17316
|
+
import { existsSync as existsSync23, readFileSync as readFileSync23, writeFileSync as writeFileSync16 } from "node:fs";
|
|
17317
|
+
import { dirname as dirname10, isAbsolute as isAbsolute11, join as join14, relative as relative12, resolve as resolve17 } from "node:path";
|
|
17318
|
+
|
|
17319
|
+
// node_modules/.cache/mpgd-native-build/packages/cli/src/capacitor-shell-starter.js
|
|
17320
|
+
import { chmodSync as chmodSync2, existsSync as existsSync22, lstatSync as lstatSync12, mkdirSync as mkdirSync11, readdirSync as readdirSync6, readFileSync as readFileSync22, realpathSync as realpathSync9, renameSync as renameSync5, unlinkSync as unlinkSync2, writeFileSync as writeFileSync15 } from "node:fs";
|
|
17321
|
+
import path9 from "node:path";
|
|
17322
|
+
import { DOMParser as DOMParser2 } from "@xmldom/xmldom";
|
|
17323
|
+
import { satisfies, valid, validRange } from "semver";
|
|
17324
|
+
|
|
17325
|
+
// node_modules/.cache/mpgd-native-build/packages/cli/src/notifications-starter.js
|
|
17326
|
+
var notificationsMessagingPlugin = "@capacitor-firebase/messaging";
|
|
17327
|
+
var notificationsGameDependencies = Object.freeze({
|
|
17328
|
+
[notificationsMessagingPlugin]: "8.5.2",
|
|
17329
|
+
"@capacitor/local-notifications": "8.3.1",
|
|
17330
|
+
firebase: "12.19.0"
|
|
17331
|
+
});
|
|
17332
|
+
var notificationsShellPlugins = Object.freeze([
|
|
17333
|
+
notificationsMessagingPlugin,
|
|
17334
|
+
"@capacitor/local-notifications"
|
|
17335
|
+
]);
|
|
17336
|
+
var gatewayImport = {
|
|
17337
|
+
anchor: "import type { RuntimeConfig } from '../runtimeDetector';\n",
|
|
17338
|
+
replacement: [
|
|
17339
|
+
"import { createStarterCapacitorNotifications } from '../capacitorNotifications';",
|
|
17340
|
+
"import type { RuntimeConfig } from '../runtimeDetector';",
|
|
17341
|
+
""
|
|
17342
|
+
].join("\n")
|
|
17343
|
+
};
|
|
17344
|
+
var gatewayProviders = (platform) => ({
|
|
17345
|
+
anchor: " const providers = createStoreProviders(runtime, () => gateway);\n",
|
|
17346
|
+
replacement: [
|
|
17347
|
+
" const providers = [",
|
|
17348
|
+
" ...createStoreProviders(runtime, () => gateway),",
|
|
17349
|
+
" // Notifications v2 (FCM and local reminders); `notifications/topics.json`",
|
|
17350
|
+
" // maps the topics, and the shell needs the two plugins",
|
|
17351
|
+
" // (`mpgd target init capacitor --providers notifications`).",
|
|
17352
|
+
` ...(await createStarterCapacitorNotifications('${platform}', runtime.configTarget)),`,
|
|
17353
|
+
" ];",
|
|
17354
|
+
""
|
|
17355
|
+
].join("\n")
|
|
17356
|
+
});
|
|
17357
|
+
var commonEdits = {
|
|
17358
|
+
"src/platform/buildGateways/capacitorAndroid.ts": [gatewayImport, gatewayProviders("android")],
|
|
17359
|
+
"src/platform/buildGateways/capacitorIos.ts": [gatewayImport, gatewayProviders("ios")],
|
|
17360
|
+
"src/platform/buildGateways/reddit.ts": [
|
|
17361
|
+
{
|
|
17362
|
+
anchor: " createDevvitCommerceProducts,\n createDevvitPlatformGateway,\n",
|
|
17363
|
+
replacement: [
|
|
17364
|
+
" createDevvitCommerceProducts,",
|
|
17365
|
+
" createDevvitNotificationsBackend,",
|
|
17366
|
+
" createDevvitPlatformGateway,",
|
|
17367
|
+
""
|
|
17368
|
+
].join("\n")
|
|
17369
|
+
},
|
|
17370
|
+
{
|
|
17371
|
+
anchor: "import { createDevvitWebPaymentsClient } from '@mpgd/adapter-devvit/web';\n",
|
|
17372
|
+
replacement: [
|
|
17373
|
+
"import {",
|
|
17374
|
+
" createDevvitWebLoginPrompt,",
|
|
17375
|
+
" createDevvitWebPaymentsClient,",
|
|
17376
|
+
"} from '@mpgd/adapter-devvit/web';",
|
|
17377
|
+
""
|
|
17378
|
+
].join("\n")
|
|
17379
|
+
},
|
|
17380
|
+
{
|
|
17381
|
+
anchor: "import type { RuntimeConfig } from '../runtimeDetector';\n",
|
|
17382
|
+
replacement: [
|
|
17383
|
+
"import { setStarterHostedNotificationBackend } from '../notificationBackend';",
|
|
17384
|
+
"import { starterNotificationTopics } from '../notifications';",
|
|
17385
|
+
"import type { RuntimeConfig } from '../runtimeDetector';",
|
|
17386
|
+
""
|
|
17387
|
+
].join("\n")
|
|
17388
|
+
},
|
|
17389
|
+
{
|
|
17390
|
+
anchor: " ...(commerce === undefined ? {} : { commerce }),\n",
|
|
17391
|
+
replacement: [
|
|
17392
|
+
" ...(commerce === undefined ? {} : { commerce }),",
|
|
17393
|
+
" // Notifications v2: the game-rendered opt-in the server bridge serves",
|
|
17394
|
+
" // (a gated beta: `integrations.notifications` stays approval-required",
|
|
17395
|
+
" // until Reddit approves the app), and Reddit login for a logged-out",
|
|
17396
|
+
" // player, who must log in before opting in.",
|
|
17397
|
+
" ...notificationOptions(runtime),",
|
|
17398
|
+
" loginPrompt: createDevvitWebLoginPrompt(),",
|
|
17399
|
+
""
|
|
17400
|
+
].join("\n")
|
|
17401
|
+
},
|
|
17402
|
+
{
|
|
17403
|
+
anchor: "function createCommerceOptions(): DevvitGatewayCommerceOptions | undefined {\n",
|
|
17404
|
+
replacement: [
|
|
17405
|
+
"function notificationOptions(",
|
|
17406
|
+
" runtime: RuntimeConfig,",
|
|
17407
|
+
"): Pick<Parameters<typeof createDevvitPlatformGateway>[0], 'notifications'> {",
|
|
17408
|
+
" const topics = starterNotificationTopics(runtime.configTarget).map((topic) => topic.id);",
|
|
17409
|
+
" if (topics.length === 0) {",
|
|
17410
|
+
" return {};",
|
|
17411
|
+
" }",
|
|
17412
|
+
" // The runtime's backend is the Devvit server (consent records, reminders, notices for the",
|
|
17413
|
+
" // logged-in Reddit user), over the same server bridge as the gateway; created on first use.",
|
|
17414
|
+
" setStarterHostedNotificationBackend(runtime.configTarget, () =>",
|
|
17415
|
+
" createDevvitNotificationsBackend({ appVersion: runtime.appVersion, buildId: runtime.buildId }));",
|
|
17416
|
+
" return { notifications: { topics } };",
|
|
17417
|
+
"}",
|
|
17418
|
+
"",
|
|
17419
|
+
"function createCommerceOptions(): DevvitGatewayCommerceOptions | undefined {",
|
|
17420
|
+
""
|
|
17421
|
+
].join("\n")
|
|
17422
|
+
}
|
|
17423
|
+
],
|
|
17424
|
+
"apps/target-devvit/src/server/index.ts": [
|
|
17425
|
+
{
|
|
17426
|
+
anchor: "import { context, createServer, getServerPort, payments, reddit, redis } from '@devvit/web/server';\n",
|
|
17427
|
+
replacement: [
|
|
17428
|
+
"import {",
|
|
17429
|
+
" context,",
|
|
17430
|
+
" createServer,",
|
|
17431
|
+
" getServerPort,",
|
|
17432
|
+
" notifications,",
|
|
17433
|
+
" payments,",
|
|
17434
|
+
" reddit,",
|
|
17435
|
+
" redis,",
|
|
17436
|
+
"} from '@devvit/web/server';",
|
|
17437
|
+
""
|
|
17438
|
+
].join("\n")
|
|
17439
|
+
},
|
|
17440
|
+
{
|
|
17441
|
+
anchor: [
|
|
17442
|
+
"import {",
|
|
17443
|
+
" createDevvitCommerceBridgeProducts,",
|
|
17444
|
+
" createDevvitCommerceV2BridgeHandler,",
|
|
17445
|
+
"} from '@mpgd/adapter-devvit/server';",
|
|
17446
|
+
""
|
|
17447
|
+
].join("\n"),
|
|
17448
|
+
replacement: [
|
|
17449
|
+
"import {",
|
|
17450
|
+
" createDevvitCommerceBridgeProducts,",
|
|
17451
|
+
" createDevvitCommerceV2BridgeHandler,",
|
|
17452
|
+
" createDevvitNotificationService,",
|
|
17453
|
+
" createDevvitNotificationsBackendBridgeHandler,",
|
|
17454
|
+
" createDevvitNotificationsV2BridgeHandler,",
|
|
17455
|
+
"} from '@mpgd/adapter-devvit/server';",
|
|
17456
|
+
""
|
|
17457
|
+
].join("\n")
|
|
17458
|
+
},
|
|
17459
|
+
{
|
|
17460
|
+
anchor: "import type { ProductCatalog } from '@mpgd/catalog';\n",
|
|
17461
|
+
replacement: [
|
|
17462
|
+
"import { resolveNotificationTopicsForTarget, type NotificationTopicCatalog, type ProductCatalog } from '@mpgd/catalog';",
|
|
17463
|
+
"import { assertNotificationDeployment } from '@mpgd/platform/notifications';",
|
|
17464
|
+
""
|
|
17465
|
+
].join("\n")
|
|
17466
|
+
},
|
|
17467
|
+
{
|
|
17468
|
+
anchor: "import devvitManifest from '../../devvit.json';\n",
|
|
17469
|
+
replacement: [
|
|
17470
|
+
"import notificationDeploymentJson from '../../../../notifications/deployment.json';",
|
|
17471
|
+
"import notificationTopicsJson from '../../../../notifications/topics.json';",
|
|
17472
|
+
"import devvitManifest from '../../devvit.json';",
|
|
17473
|
+
""
|
|
17474
|
+
].join("\n")
|
|
17475
|
+
},
|
|
17476
|
+
{
|
|
17477
|
+
anchor: [
|
|
17478
|
+
"const handleBridgeRequest = createDevvitBridgeHandler({",
|
|
17479
|
+
" redis,",
|
|
17480
|
+
" currentPlayerId,",
|
|
17481
|
+
" currentDisplayName,",
|
|
17482
|
+
" storageKeyNamespace: gameName,",
|
|
17483
|
+
" ...(commerce === undefined ? {} : { commerce }),",
|
|
17484
|
+
"});",
|
|
17485
|
+
""
|
|
17486
|
+
].join("\n"),
|
|
17487
|
+
replacement: [
|
|
17488
|
+
"/**",
|
|
17489
|
+
" * Notifications v2 (`notifications/topics.json`, mapped for the `reddit`",
|
|
17490
|
+
" * target): the game-rendered opt-in for the logged-in requester, and the",
|
|
17491
|
+
" * service that records consent and sends server reminders over the Devvit",
|
|
17492
|
+
" * notifications plugin. Devvit notifications are a gated beta: the game",
|
|
17493
|
+
" * shows them only once `integrations.notifications` is `available`.",
|
|
17494
|
+
" */",
|
|
17495
|
+
"const notificationTopicCatalog = notificationTopicsJson as NotificationTopicCatalog;",
|
|
17496
|
+
"const redditNotificationTopics = readRedditNotificationTopics();",
|
|
17497
|
+
"const notificationService: ReturnType<typeof createDevvitNotificationService>['service'] | undefined =",
|
|
17498
|
+
" redditNotificationTopics.length === 0 ? undefined : createNotificationService();",
|
|
17499
|
+
"// The opt-in is served only with the service that sends; a refused catalog turns both off.",
|
|
17500
|
+
"const notificationsBridge = notificationService === undefined",
|
|
17501
|
+
" ? undefined",
|
|
17502
|
+
" : createDevvitNotificationsV2BridgeHandler({",
|
|
17503
|
+
" notifications,",
|
|
17504
|
+
" currentUserId: currentPlayerId,",
|
|
17505
|
+
" topics: redditNotificationTopics,",
|
|
17506
|
+
" });",
|
|
17507
|
+
"// The webview runtime's backend: consent records, reminders, and notices for the request's user.",
|
|
17508
|
+
"const notificationsBackend = notificationService === undefined",
|
|
17509
|
+
" ? undefined",
|
|
17510
|
+
" : createDevvitNotificationsBackendBridgeHandler({ service: notificationService, context: () => context });",
|
|
17511
|
+
"const handleBridgeRequest = createDevvitBridgeHandler({",
|
|
17512
|
+
" redis,",
|
|
17513
|
+
" currentPlayerId,",
|
|
17514
|
+
" currentDisplayName,",
|
|
17515
|
+
" storageKeyNamespace: gameName,",
|
|
17516
|
+
" ...(commerce === undefined ? {} : { commerce }),",
|
|
17517
|
+
" ...(notificationsBridge === undefined ? {} : { notifications: notificationsBridge }),",
|
|
17518
|
+
" ...(notificationsBackend === undefined ? {} : { notificationsBackend }),",
|
|
17519
|
+
"});",
|
|
17520
|
+
""
|
|
17521
|
+
].join("\n")
|
|
17522
|
+
},
|
|
17523
|
+
{
|
|
17524
|
+
anchor: [
|
|
17525
|
+
"/**",
|
|
17526
|
+
" * The notifications v2 service (`createDevvitNotificationService` from",
|
|
17527
|
+
" * `@mpgd/adapter-devvit/server`). None until the game declares a topic",
|
|
17528
|
+
" * catalog, so the scheduler tasks below do no work.",
|
|
17529
|
+
" */",
|
|
17530
|
+
"const notificationService: Parameters<typeof handleNotificationTask>[0]['notifications'] = undefined;",
|
|
17531
|
+
""
|
|
17532
|
+
].join("\n"),
|
|
17533
|
+
replacement: [
|
|
17534
|
+
"/** The topic ids the catalog maps for Reddit; none when the catalog cannot be read. */",
|
|
17535
|
+
"function readRedditNotificationTopics(): readonly string[] {",
|
|
17536
|
+
" try {",
|
|
17537
|
+
" return resolveNotificationTopicsForTarget(notificationTopicCatalog, 'reddit').map((topic) => topic.id);",
|
|
17538
|
+
" } catch (error) {",
|
|
17539
|
+
" console.error(`devvit notifications disabled: ${errorMessage(error)}`);",
|
|
17540
|
+
" return [];",
|
|
17541
|
+
" }",
|
|
17542
|
+
"}",
|
|
17543
|
+
"",
|
|
17544
|
+
"/**",
|
|
17545
|
+
" * The notifications v2 service over Redis and the Devvit plugin; the",
|
|
17546
|
+
" * scheduler tasks below run its reminders and purges. A catalog or",
|
|
17547
|
+
" * deployment the service refuses turns notifications off, never the game",
|
|
17548
|
+
" * server.",
|
|
17549
|
+
" */",
|
|
17550
|
+
"function createNotificationService(): ReturnType<typeof createDevvitNotificationService>['service'] | undefined {",
|
|
17551
|
+
" try {",
|
|
17552
|
+
" return createDevvitNotificationService({",
|
|
17553
|
+
" redis,",
|
|
17554
|
+
" notifications,",
|
|
17555
|
+
" catalog: notificationTopicCatalog,",
|
|
17556
|
+
" deployment: assertNotificationDeployment(notificationDeploymentJson),",
|
|
17557
|
+
" }).service;",
|
|
17558
|
+
" } catch (error) {",
|
|
17559
|
+
" console.error(`devvit notifications disabled: ${errorMessage(error)}`);",
|
|
17560
|
+
" return undefined;",
|
|
17561
|
+
" }",
|
|
17562
|
+
"}",
|
|
17563
|
+
""
|
|
17564
|
+
].join("\n")
|
|
17120
17565
|
}
|
|
17121
|
-
|
|
17122
|
-
|
|
17123
|
-
|
|
17124
|
-
|
|
17125
|
-
|
|
17126
|
-
}
|
|
17127
|
-
|
|
17566
|
+
]
|
|
17567
|
+
};
|
|
17568
|
+
var phaserEdits = {
|
|
17569
|
+
"src/runtime/createGame.ts": [
|
|
17570
|
+
{
|
|
17571
|
+
anchor: "import { LobbyScene } from '../scenes/LobbyScene';\n",
|
|
17572
|
+
replacement: [
|
|
17573
|
+
"import { LobbyScene } from '../scenes/LobbyScene';",
|
|
17574
|
+
"import {",
|
|
17575
|
+
" NotificationSettingsScene,",
|
|
17576
|
+
" routeNotificationSettingsOpens,",
|
|
17577
|
+
"} from '../scenes/NotificationSettingsScene';",
|
|
17578
|
+
""
|
|
17579
|
+
].join("\n")
|
|
17580
|
+
},
|
|
17581
|
+
{
|
|
17582
|
+
anchor: " scene: [BootScene, LobbyScene, PlayScene],\n",
|
|
17583
|
+
replacement: " scene: [BootScene, LobbyScene, PlayScene, NotificationSettingsScene],\n"
|
|
17584
|
+
},
|
|
17585
|
+
{
|
|
17586
|
+
anchor: " ownGameAudio(game, input.context);\n",
|
|
17587
|
+
replacement: [
|
|
17588
|
+
" ownGameAudio(game, input.context);",
|
|
17589
|
+
" // The advertising opt-out action and consent notices open the settings screen.",
|
|
17590
|
+
" routeNotificationSettingsOpens(game, input.context);",
|
|
17591
|
+
""
|
|
17592
|
+
].join("\n")
|
|
17128
17593
|
}
|
|
17129
|
-
|
|
17130
|
-
|
|
17131
|
-
|
|
17132
|
-
|
|
17133
|
-
|
|
17134
|
-
|
|
17135
|
-
|
|
17136
|
-
|
|
17137
|
-
|
|
17138
|
-
|
|
17139
|
-
|
|
17140
|
-
|
|
17594
|
+
],
|
|
17595
|
+
"src/scenes/LobbyScene.ts": [
|
|
17596
|
+
{
|
|
17597
|
+
anchor: "import { starterContextKey, type StarterContext } from '../runtime/gameContext';\n",
|
|
17598
|
+
replacement: [
|
|
17599
|
+
"import { starterContextKey, type StarterContext } from '../runtime/gameContext';",
|
|
17600
|
+
"import { notificationSettingsSceneKey } from './NotificationSettingsScene';",
|
|
17601
|
+
""
|
|
17602
|
+
].join("\n")
|
|
17603
|
+
},
|
|
17604
|
+
{
|
|
17605
|
+
anchor: " this.input.once('pointerdown', () => this.scene.start('PlayScene'));\n",
|
|
17606
|
+
replacement: [
|
|
17607
|
+
" // The settings button shows only where this target has notifications.",
|
|
17608
|
+
" const notificationsButton = context.platform.notificationsV2 === undefined",
|
|
17609
|
+
" ? undefined",
|
|
17610
|
+
" : this.add",
|
|
17611
|
+
" .text(940, 16, locale === 'ko' ? '\uC54C\uB9BC \uC124\uC815' : 'Notifications', {",
|
|
17612
|
+
" color: '#5eead4',",
|
|
17613
|
+
" fontFamily: 'Arial, sans-serif',",
|
|
17614
|
+
" fontSize: '22px',",
|
|
17615
|
+
" })",
|
|
17616
|
+
" .setOrigin(1, 0)",
|
|
17617
|
+
" .setPadding(16, 12, 16, 12)",
|
|
17618
|
+
" .setInteractive({ useHandCursor: true });",
|
|
17619
|
+
" notificationsButton?.on('pointerup', () => this.scene.start(notificationSettingsSceneKey));",
|
|
17620
|
+
" // A tap anywhere else starts the game; the scene drops this listener on shutdown.",
|
|
17621
|
+
" this.input.on('pointerdown', (_pointer: Phaser.Input.Pointer, over: Phaser.GameObjects.GameObject[]) => {",
|
|
17622
|
+
" if (notificationsButton === undefined || !over.includes(notificationsButton)) {",
|
|
17623
|
+
" this.scene.start('PlayScene');",
|
|
17624
|
+
" }",
|
|
17625
|
+
" });",
|
|
17626
|
+
""
|
|
17627
|
+
].join("\n")
|
|
17628
|
+
}
|
|
17629
|
+
]
|
|
17630
|
+
};
|
|
17631
|
+
var liteEdits = {
|
|
17632
|
+
"src/runtime/createGame.ts": [
|
|
17633
|
+
{
|
|
17634
|
+
anchor: "import { createTapGame } from '../lite/tapGame';\n",
|
|
17635
|
+
replacement: [
|
|
17636
|
+
"import { mountNotificationSettingsPanel } from '../lite/notificationSettingsPanel';",
|
|
17637
|
+
"import { createTapGame } from '../lite/tapGame';",
|
|
17638
|
+
""
|
|
17639
|
+
].join("\n")
|
|
17640
|
+
},
|
|
17641
|
+
{
|
|
17642
|
+
anchor: " const shell = createTapGame({ mount, title: '__GAME_TITLE__' });\n",
|
|
17643
|
+
replacement: [
|
|
17644
|
+
" const shell = createTapGame({ mount, title: '__GAME_TITLE__' });",
|
|
17645
|
+
" const disposeNotificationSettings = mountNotificationSettingsPanel(mount, input.context);",
|
|
17646
|
+
""
|
|
17647
|
+
].join("\n")
|
|
17648
|
+
},
|
|
17649
|
+
{
|
|
17650
|
+
anchor: " audio.dispose();\n",
|
|
17651
|
+
replacement: [" audio.dispose();", " disposeNotificationSettings();", ""].join("\n")
|
|
17141
17652
|
}
|
|
17653
|
+
]
|
|
17654
|
+
};
|
|
17655
|
+
var readmeSection = [
|
|
17656
|
+
"",
|
|
17657
|
+
"## Notifications",
|
|
17658
|
+
"",
|
|
17659
|
+
"This game was created with `--providers notifications`. Its topics live in",
|
|
17660
|
+
"`notifications/topics.json` and the backend settings (`advertisingRules`,",
|
|
17661
|
+
"the advertising sender) in `notifications/deployment.json`; run",
|
|
17662
|
+
"`pnpm validate:notifications` after every change and before every release.",
|
|
17663
|
+
"Replace the sender contact placeholder in `notifications/deployment.json`",
|
|
17664
|
+
"before any advertising is sent. The notification settings screen asks for the",
|
|
17665
|
+
"OS or host permission and records reminder, advertising (\uAD11\uACE0\uC131 \uC815\uBCF4",
|
|
17666
|
+
"\uC218\uC2E0\uB3D9\uC758), and night consent through the notifications runtime. See",
|
|
17667
|
+
"`docs/NOTIFICATIONS.md` and, for Android and iOS, the mpgd-kit Firebase setup",
|
|
17668
|
+
"guide; `mpgd target init capacitor --providers notifications` installs the two",
|
|
17669
|
+
"Capacitor plugins into a game-owned shell.",
|
|
17670
|
+
""
|
|
17671
|
+
].join("\n");
|
|
17672
|
+
var notificationsCapacitorGateways = Object.freeze({
|
|
17673
|
+
android: "src/platform/buildGateways/capacitorAndroid.ts",
|
|
17674
|
+
ios: "src/platform/buildGateways/capacitorIos.ts"
|
|
17675
|
+
});
|
|
17676
|
+
var notificationsCapacitorGatewayModules = Object.freeze([
|
|
17677
|
+
"src/platform/capacitorNotifications.ts",
|
|
17678
|
+
"src/platform/notifications.ts",
|
|
17679
|
+
"src/platform/notificationBackend.ts",
|
|
17680
|
+
"src/platform/notificationSettings.ts",
|
|
17681
|
+
"src/platform/notificationOpens.ts"
|
|
17682
|
+
]);
|
|
17683
|
+
var notificationsCapacitorSupportFiles = Object.freeze([
|
|
17684
|
+
...notificationsCapacitorGatewayModules,
|
|
17685
|
+
"notifications/topics.json",
|
|
17686
|
+
"notifications/deployment.json",
|
|
17687
|
+
"tools/validate-notifications.ts",
|
|
17688
|
+
"docs/NOTIFICATIONS.md"
|
|
17689
|
+
]);
|
|
17690
|
+
var notificationsCapacitorTemplateSupportFiles = Object.freeze([
|
|
17691
|
+
"src/platform/serverSession.ts",
|
|
17692
|
+
"src/platform/developmentServerSession.ts"
|
|
17693
|
+
]);
|
|
17694
|
+
var notificationsCapacitorTemplateSupportImporters = Object.freeze({
|
|
17695
|
+
"src/platform/developmentServerSession.ts": {
|
|
17696
|
+
importer: "src/platform/serverSession.ts",
|
|
17697
|
+
requires: { packageName: "@mpgd/game-services", exportName: "createDevelopmentServerSession" },
|
|
17698
|
+
warning: "src/platform/serverSession.ts predates development notification sessions (VITE_MPGD_DEV_NOTIFICATION_SESSION); to try notifications locally, copy serverSession.ts and developmentServerSession.ts from the starter template."
|
|
17142
17699
|
}
|
|
17143
|
-
|
|
17144
|
-
}
|
|
17700
|
+
});
|
|
17145
17701
|
|
|
17146
17702
|
// node_modules/.cache/mpgd-native-build/packages/cli/src/capacitor-shell-starter.js
|
|
17147
17703
|
var shellPath = "apps/mobile-capacitor";
|
|
@@ -17233,7 +17789,7 @@ function assertAndroidLauncherClasses(nativeDirectory, gradle) {
|
|
|
17233
17789
|
const manifestPackage = manifestRoot?.getAttribute("package");
|
|
17234
17790
|
const namespace = /\bnamespace\s*(?:=\s*)?["']([A-Za-z_][\w]*(?:\.[A-Za-z_][\w]*)+)["']/u.exec(stripGradleComments(gradle))?.[1] ?? (manifestPackage === null || manifestPackage === "" ? void 0 : manifestPackage);
|
|
17235
17791
|
const releaseFile = path9.join(nativeDirectory, "app/src/release/AndroidManifest.xml");
|
|
17236
|
-
const release =
|
|
17792
|
+
const release = existsSync22(releaseFile) ? readAndroidApplication(releaseFile, false) : void 0;
|
|
17237
17793
|
if (release !== void 0) {
|
|
17238
17794
|
assertAndroidLauncherEnabled(release, "Release application");
|
|
17239
17795
|
}
|
|
@@ -17343,7 +17899,7 @@ function resolvesAndroidClass(nativeDirectory, qualified, sourceFiles, visited,
|
|
|
17343
17899
|
if (!isNativeFile(nativeDirectory, relative14)) {
|
|
17344
17900
|
continue;
|
|
17345
17901
|
}
|
|
17346
|
-
const source = stripSourceCommentsAndStrings(
|
|
17902
|
+
const source = stripSourceCommentsAndStrings(readFileSync22(path9.join(nativeDirectory, relative14), "utf8"));
|
|
17347
17903
|
const declaredPackage = /\bpackage\s+([A-Za-z_][\w]*(?:\.[A-Za-z_][\w]*)+)\s*;?/u.exec(source)?.[1];
|
|
17348
17904
|
if (declaredPackage !== packageName) {
|
|
17349
17905
|
continue;
|
|
@@ -17400,7 +17956,7 @@ function listNativeFiles(nativeDirectory, relativeRoot, extension) {
|
|
|
17400
17956
|
while (pending.length > 0) {
|
|
17401
17957
|
const relative14 = pending.pop() ?? "";
|
|
17402
17958
|
const full = path9.join(nativeDirectory, relative14);
|
|
17403
|
-
if (!
|
|
17959
|
+
if (!existsSync22(full) || lstatSync12(full).isSymbolicLink()) {
|
|
17404
17960
|
continue;
|
|
17405
17961
|
}
|
|
17406
17962
|
for (const entry of readdirSync6(full, { withFileTypes: true })) {
|
|
@@ -17613,7 +18169,7 @@ function assertReferencedIosFiles(nativeDirectory, project, infoRelative) {
|
|
|
17613
18169
|
throw new Error(`Existing ios project is incomplete; referenced app file ${relative14} is missing.`);
|
|
17614
18170
|
}
|
|
17615
18171
|
}
|
|
17616
|
-
const info =
|
|
18172
|
+
const info = readFileSync22(path9.join(nativeDirectory, infoRelative), "utf8");
|
|
17617
18173
|
assertSceneDelegateFiles(nativeDirectory, info, "Release", compiledSources);
|
|
17618
18174
|
return compiledSources;
|
|
17619
18175
|
}
|
|
@@ -17801,7 +18357,7 @@ function isNativeEntry(directory, relative14) {
|
|
|
17801
18357
|
let current = directory;
|
|
17802
18358
|
for (const segment of within.split(path9.sep)) {
|
|
17803
18359
|
current = path9.join(current, segment);
|
|
17804
|
-
if (!
|
|
18360
|
+
if (!existsSync22(current) || lstatSync12(current).isSymbolicLink()) {
|
|
17805
18361
|
return false;
|
|
17806
18362
|
}
|
|
17807
18363
|
}
|
|
@@ -17834,7 +18390,7 @@ function assertSceneDelegateFiles(nativeDirectory, source, label, compiledSource
|
|
|
17834
18390
|
if (!compiledSources.has(relative14)) {
|
|
17835
18391
|
return false;
|
|
17836
18392
|
}
|
|
17837
|
-
const text = stripSourceCommentsAndStrings(
|
|
18393
|
+
const text = stripSourceCommentsAndStrings(readFileSync22(path9.join(nativeDirectory, relative14), "utf8"));
|
|
17838
18394
|
return hasUsableTopLevelSwiftSceneDelegate(text, className);
|
|
17839
18395
|
})) {
|
|
17840
18396
|
throw new Error(`Existing ios project ${label} scene delegate ${delegate} is missing from App Sources.`);
|
|
@@ -17859,17 +18415,17 @@ function hasUsableTopLevelSwiftSceneDelegate(source, className) {
|
|
|
17859
18415
|
return false;
|
|
17860
18416
|
}
|
|
17861
18417
|
function assertAndroidManifestResources(nativeDirectory) {
|
|
17862
|
-
const manifestFiles = ["main", "release"].map((sourceSet) => path9.join(nativeDirectory, `app/src/${sourceSet}/AndroidManifest.xml`)).filter((file) =>
|
|
18418
|
+
const manifestFiles = ["main", "release"].map((sourceSet) => path9.join(nativeDirectory, `app/src/${sourceSet}/AndroidManifest.xml`)).filter((file) => existsSync22(file));
|
|
17863
18419
|
const resourceRoots = ["main", "release"].map((sourceSet) => path9.join(nativeDirectory, `app/src/${sourceSet}/res`));
|
|
17864
18420
|
const references = manifestFiles.flatMap((file) => {
|
|
17865
|
-
const manifest =
|
|
18421
|
+
const manifest = readFileSync22(file, "utf8").replace(/<!--[\s\S]*?-->/gu, "");
|
|
17866
18422
|
return [...manifest.matchAll(/(?<![\w+])@([a-z]+)\/([A-Za-z_][A-Za-z0-9_.]*)/gu)];
|
|
17867
18423
|
});
|
|
17868
18424
|
for (const reference of references) {
|
|
17869
18425
|
const type = reference[1] ?? "";
|
|
17870
18426
|
const name = reference[2] ?? "";
|
|
17871
18427
|
const found = resourceRoots.some((resourceRoot) => {
|
|
17872
|
-
if (!
|
|
18428
|
+
if (!existsSync22(resourceRoot)) {
|
|
17873
18429
|
return false;
|
|
17874
18430
|
}
|
|
17875
18431
|
const folders = readdirSync6(resourceRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
|
|
@@ -17894,7 +18450,7 @@ function assertNativeDisplayName(nativeDirectory, platform, expectedDisplayName,
|
|
|
17894
18450
|
if (platform === "ios" && !isNativeFile(nativeDirectory, relative14)) {
|
|
17895
18451
|
throw new Error(`Existing ${platform} project is incomplete; native display name resource is missing.`);
|
|
17896
18452
|
}
|
|
17897
|
-
const source = platform === "ios" ?
|
|
18453
|
+
const source = platform === "ios" ? readFileSync22(path9.join(nativeDirectory, relative14), "utf8") : "";
|
|
17898
18454
|
const labels = platform === "ios" ? [parsePlistDictionary(source, "Release").get("CFBundleDisplayName")].filter((value) => value?.tagName === "string").map((value) => value?.textContent ?? "") : [];
|
|
17899
18455
|
if (platform === "ios" && (labels.length !== 1 || labels[0] !== expectedDisplayName)) {
|
|
17900
18456
|
throw new Error(`Existing ${platform} project display name differs or cannot be read safely.`);
|
|
@@ -17920,7 +18476,7 @@ function assertNativeDisplayName(nativeDirectory, platform, expectedDisplayName,
|
|
|
17920
18476
|
}
|
|
17921
18477
|
}
|
|
17922
18478
|
const overlayFile = path9.join(nativeDirectory, "app/src/release/AndroidManifest.xml");
|
|
17923
|
-
if (
|
|
18479
|
+
if (existsSync22(overlayFile)) {
|
|
17924
18480
|
const release = readAndroidApplication(overlayFile, false);
|
|
17925
18481
|
if (release !== void 0) {
|
|
17926
18482
|
if (hasAndroidMergerDirective(release)) {
|
|
@@ -17930,8 +18486,8 @@ function assertNativeDisplayName(nativeDirectory, platform, expectedDisplayName,
|
|
|
17930
18486
|
if (releaseLabel !== null && resolveAndroidLabel(nativeDirectory, releaseLabel) !== expectedDisplayName) {
|
|
17931
18487
|
throw new Error("Existing android Release application label differs from the requested name.");
|
|
17932
18488
|
}
|
|
17933
|
-
const appBuildFile = ["app/build.gradle", "app/build.gradle.kts"].map((relative15) => path9.join(nativeDirectory, relative15)).find((file) =>
|
|
17934
|
-
const appGradle = appBuildFile === void 0 ? "" :
|
|
18489
|
+
const appBuildFile = ["app/build.gradle", "app/build.gradle.kts"].map((relative15) => path9.join(nativeDirectory, relative15)).find((file) => existsSync22(file));
|
|
18490
|
+
const appGradle = appBuildFile === void 0 ? "" : readFileSync22(appBuildFile, "utf8");
|
|
17935
18491
|
const manifestPackage = main.parentNode?.getAttribute("package");
|
|
17936
18492
|
const namespace = /\bnamespace\s*(?:=\s*)?["']([A-Za-z_][\w]*(?:\.[A-Za-z_][\w]*)+)["']/u.exec(stripGradleComments(appGradle))?.[1] ?? (manifestPackage === null || manifestPackage === "" ? void 0 : manifestPackage);
|
|
17937
18493
|
const launcherNames = /* @__PURE__ */ new Set();
|
|
@@ -17974,13 +18530,13 @@ function assertAndroidReleaseDisplayName(shellApp, expectedDisplayName) {
|
|
|
17974
18530
|
function assertAndroidReleaseLauncherIntegrity(shellApp) {
|
|
17975
18531
|
const nativeDirectory = path9.join(shellApp, "android");
|
|
17976
18532
|
const gradleRelative = requireOneNativeFile(nativeDirectory, ["app/build.gradle", "app/build.gradle.kts"], "android");
|
|
17977
|
-
const gradle =
|
|
18533
|
+
const gradle = readFileSync22(path9.join(nativeDirectory, gradleRelative), "utf8");
|
|
17978
18534
|
assertAndroidLauncherClasses(nativeDirectory, gradle);
|
|
17979
18535
|
}
|
|
17980
18536
|
function assertIosReleaseDisplayName(shellApp, expectedDisplayName) {
|
|
17981
18537
|
const nativeDirectory = path9.join(shellApp, "ios");
|
|
17982
18538
|
const projectFile = path9.join(nativeDirectory, "App/App.xcodeproj/project.pbxproj");
|
|
17983
|
-
const project =
|
|
18539
|
+
const project = readFileSync22(projectFile, "utf8");
|
|
17984
18540
|
const infoRelative = path9.join("App", readIosReleaseInfoPlist(project));
|
|
17985
18541
|
if (!isNativeFile(nativeDirectory, infoRelative)) {
|
|
17986
18542
|
throw new Error("Existing ios project Release Info.plist is missing or unsafe.");
|
|
@@ -18002,9 +18558,9 @@ function assertIosArchiveScheme(shellApp) {
|
|
|
18002
18558
|
if (!isNativeFile(nativeDirectory, projectRelative) || !isNativeFile(nativeDirectory, schemeRelative)) {
|
|
18003
18559
|
throw new Error("Existing ios project shared App archive scheme is missing or unsafe.");
|
|
18004
18560
|
}
|
|
18005
|
-
const project =
|
|
18561
|
+
const project = readFileSync22(path9.join(nativeDirectory, projectRelative), "utf8");
|
|
18006
18562
|
const appTargetId = readIosAppTargetId(project);
|
|
18007
|
-
const schemeSource =
|
|
18563
|
+
const schemeSource = readFileSync22(path9.join(nativeDirectory, schemeRelative), "utf8");
|
|
18008
18564
|
const scheme = parseXml(schemeSource, "ios project App archive scheme").documentElement;
|
|
18009
18565
|
if (scheme?.tagName !== "Scheme") {
|
|
18010
18566
|
throw new Error("Existing ios project App archive scheme is malformed.");
|
|
@@ -18031,7 +18587,7 @@ function hasAndroidMergerDirectiveDeep(element) {
|
|
|
18031
18587
|
return hasAndroidMergerDirective(element) || childElements(element).some(hasAndroidMergerDirectiveDeep);
|
|
18032
18588
|
}
|
|
18033
18589
|
function readAndroidApplication(file, required = true) {
|
|
18034
|
-
const root = parseXml(
|
|
18590
|
+
const root = parseXml(readFileSync22(file, "utf8"), "android manifest").documentElement;
|
|
18035
18591
|
const application = root === null ? void 0 : childElements(root).find((element) => element.tagName === "application");
|
|
18036
18592
|
if (root?.tagName !== "manifest" || required && application === void 0) {
|
|
18037
18593
|
throw new Error("Existing android project manifest application is missing.");
|
|
@@ -18060,9 +18616,9 @@ function readAndroidString(nativeDirectory, key) {
|
|
|
18060
18616
|
throw new Error("Existing android project application label is unsupported.");
|
|
18061
18617
|
}
|
|
18062
18618
|
const overlay = path9.join(nativeDirectory, "app/src/release/res/values");
|
|
18063
|
-
const overlayValues =
|
|
18619
|
+
const overlayValues = existsSync22(overlay) && lstatSync12(overlay).isDirectory() ? readdirSync6(overlay).filter((file) => file.endsWith(".xml")).flatMap((file) => readAndroidValueResource(path9.join(overlay, file), "string", key)) : [];
|
|
18064
18620
|
const main = path9.join(nativeDirectory, "app/src/main/res/values");
|
|
18065
|
-
const mainValues =
|
|
18621
|
+
const mainValues = existsSync22(main) && lstatSync12(main).isDirectory() ? readdirSync6(main).filter((file) => file.endsWith(".xml")).flatMap((file) => readAndroidValueResource(path9.join(main, file), "string", key)) : [];
|
|
18066
18622
|
const values = overlayValues.length > 0 ? overlayValues : mainValues;
|
|
18067
18623
|
if (values.length !== 1) {
|
|
18068
18624
|
throw new Error("Existing android project application label resource is missing or ambiguous.");
|
|
@@ -18074,7 +18630,7 @@ function readAndroidString(nativeDirectory, key) {
|
|
|
18074
18630
|
return values[0] ?? "";
|
|
18075
18631
|
}
|
|
18076
18632
|
function readAndroidValueResource(file, type, name) {
|
|
18077
|
-
const root = parseXml(
|
|
18633
|
+
const root = parseXml(readFileSync22(file, "utf8"), "android values resource").documentElement;
|
|
18078
18634
|
if (root?.tagName !== "resources") {
|
|
18079
18635
|
throw new Error("Existing android values resource is malformed.");
|
|
18080
18636
|
}
|
|
@@ -18142,30 +18698,61 @@ function isNativeFile(directory, relative14) {
|
|
|
18142
18698
|
let current = directory;
|
|
18143
18699
|
for (const segment of within.split(path9.sep)) {
|
|
18144
18700
|
current = path9.join(current, segment);
|
|
18145
|
-
if (!
|
|
18701
|
+
if (!existsSync22(current) || lstatSync12(current).isSymbolicLink()) {
|
|
18146
18702
|
return false;
|
|
18147
18703
|
}
|
|
18148
18704
|
}
|
|
18149
18705
|
return lstatSync12(file).isFile();
|
|
18150
18706
|
}
|
|
18707
|
+
var androidAdvertisingRendererService = "dev.mpgd.capacitor.AdvertisingMessagingService";
|
|
18708
|
+
var androidMessagingPluginService = "io.capawesome.capacitorjs.plugins.firebase.messaging.MessagingService";
|
|
18709
|
+
var androidAdvertisingRendererMetaData = "dev.mpgd.notifications.advertising_renderer";
|
|
18710
|
+
var androidAdvertisingRendererServiceEntry = [
|
|
18711
|
+
`<service android:name="${androidAdvertisingRendererService}" android:exported="false">`,
|
|
18712
|
+
" <intent-filter>",
|
|
18713
|
+
' <action android:name="com.google.firebase.MESSAGING_EVENT" />',
|
|
18714
|
+
" </intent-filter>",
|
|
18715
|
+
` <meta-data android:name="${androidAdvertisingRendererMetaData}" android:value="true" />`,
|
|
18716
|
+
"</service>"
|
|
18717
|
+
];
|
|
18718
|
+
var androidMessagingPluginServiceRemoval = `<service android:name="${androidMessagingPluginService}" tools:node="remove" />`;
|
|
18719
|
+
var androidAdvertisingRendererEntries = [
|
|
18720
|
+
...androidAdvertisingRendererServiceEntry,
|
|
18721
|
+
androidMessagingPluginServiceRemoval
|
|
18722
|
+
];
|
|
18151
18723
|
|
|
18152
18724
|
// node_modules/.cache/mpgd-native-build/tools/target/native-release-identity.js
|
|
18725
|
+
var nativeVersionStampEnvironmentName = "MPGD_NATIVE_VERSION_STAMP";
|
|
18726
|
+
function resolveNativeVersionSource(environment) {
|
|
18727
|
+
const value = environment[nativeVersionStampEnvironmentName]?.trim();
|
|
18728
|
+
if (value === void 0 || value === "" || value === "0") {
|
|
18729
|
+
return "source";
|
|
18730
|
+
}
|
|
18731
|
+
if (value === "1") {
|
|
18732
|
+
return "stamped";
|
|
18733
|
+
}
|
|
18734
|
+
throw new Error(`${nativeVersionStampEnvironmentName} must be 1 or 0, received ${value}.`);
|
|
18735
|
+
}
|
|
18153
18736
|
function assertNativeReleaseIdentity(input) {
|
|
18154
18737
|
const expected = resolveExpectedNativeIdentity(input.platform, input.metadata, input.environment, input.required);
|
|
18155
18738
|
if (expected === void 0) {
|
|
18739
|
+
if (input.versionSource === "stamped") {
|
|
18740
|
+
throw new Error(`${nativeVersionStampEnvironmentName}=1 requires the reserved native version numbers.`);
|
|
18741
|
+
}
|
|
18156
18742
|
return;
|
|
18157
18743
|
}
|
|
18158
18744
|
assertNativeVersionMatchesGameVersion(expected, input.environment, input.required);
|
|
18745
|
+
const checkVersions = input.versionSource !== "stamped";
|
|
18159
18746
|
if (expected.kind === "android") {
|
|
18160
18747
|
const candidates = [
|
|
18161
|
-
|
|
18162
|
-
|
|
18163
|
-
].filter((file) =>
|
|
18748
|
+
join14(input.shellApp, "android/app/build.gradle"),
|
|
18749
|
+
join14(input.shellApp, "android/app/build.gradle.kts")
|
|
18750
|
+
].filter((file) => existsSync23(file));
|
|
18164
18751
|
if (candidates.length !== 1) {
|
|
18165
18752
|
throw new Error("Android native release requires exactly one Gradle app build file.");
|
|
18166
18753
|
}
|
|
18167
|
-
assertAndroidIdentity(candidates[0] ?? "", expected);
|
|
18168
|
-
assertAndroidAppliedScripts(candidates[0] ?? "",
|
|
18754
|
+
assertAndroidIdentity(candidates[0] ?? "", expected, checkVersions);
|
|
18755
|
+
assertAndroidAppliedScripts(candidates[0] ?? "", join14(input.shellApp, "android"));
|
|
18169
18756
|
if (input.required) {
|
|
18170
18757
|
const name = requireValue(input.metadata?.displayName, "Android target metadata displayName");
|
|
18171
18758
|
assertAndroidReleaseDisplayName(input.shellApp, name);
|
|
@@ -18173,7 +18760,7 @@ function assertNativeReleaseIdentity(input) {
|
|
|
18173
18760
|
}
|
|
18174
18761
|
return;
|
|
18175
18762
|
}
|
|
18176
|
-
assertIosIdentity(
|
|
18763
|
+
assertIosIdentity(join14(input.shellApp, "ios/App/App.xcodeproj/project.pbxproj"), expected, checkVersions);
|
|
18177
18764
|
if (input.required) {
|
|
18178
18765
|
const name = requireValue(input.metadata?.displayName, "iOS target metadata displayName");
|
|
18179
18766
|
assertIosReleaseDisplayName(input.shellApp, name);
|
|
@@ -18184,6 +18771,161 @@ function runNativeSyncWithIdentityCheck(input, synchronize) {
|
|
|
18184
18771
|
synchronize();
|
|
18185
18772
|
assertNativeReleaseIdentity(input);
|
|
18186
18773
|
}
|
|
18774
|
+
function stampNativeReleaseVersions(input) {
|
|
18775
|
+
if (input.platform === "android") {
|
|
18776
|
+
const versionCode = requirePositiveInteger2(optional(input.environment.MPGD_TARGET_VERSION_CODE), "MPGD_TARGET_VERSION_CODE");
|
|
18777
|
+
const versionName = requireFinalSemVer2(optional(input.environment.MPGD_TARGET_VERSION_NAME), "MPGD_TARGET_VERSION_NAME");
|
|
18778
|
+
const candidates = [
|
|
18779
|
+
join14(input.stagedShellApp, "android/app/build.gradle"),
|
|
18780
|
+
join14(input.stagedShellApp, "android/app/build.gradle.kts")
|
|
18781
|
+
].filter((file3) => existsSync23(file3));
|
|
18782
|
+
if (candidates.length !== 1) {
|
|
18783
|
+
throw new Error("Android native release requires exactly one Gradle app build file.");
|
|
18784
|
+
}
|
|
18785
|
+
const file2 = candidates[0] ?? "";
|
|
18786
|
+
writeFileSync16(file2, stampAndroidGradleVersions(readFileSync23(file2, "utf8"), {
|
|
18787
|
+
versionCode,
|
|
18788
|
+
versionName
|
|
18789
|
+
}, file2));
|
|
18790
|
+
return;
|
|
18791
|
+
}
|
|
18792
|
+
const marketingVersion = requireFinalSemVer2(optional(input.environment.MPGD_TARGET_MARKETING_VERSION), "MPGD_TARGET_MARKETING_VERSION");
|
|
18793
|
+
const buildNumber = requirePositiveInteger2(optional(input.environment.MPGD_TARGET_BUILD_NUMBER), "MPGD_TARGET_BUILD_NUMBER");
|
|
18794
|
+
const file = join14(input.stagedShellApp, "ios/App/App.xcodeproj/project.pbxproj");
|
|
18795
|
+
writeFileSync16(file, stampIosProjectVersions(readRequiredFile(file, "iOS Xcode project configuration"), { marketingVersion, buildNumber }, file));
|
|
18796
|
+
}
|
|
18797
|
+
function stampAndroidGradleVersions(source, versions, file) {
|
|
18798
|
+
const end = "(?=\\s*(?:;|\\r?\\n|\\}|$))";
|
|
18799
|
+
const code = new RegExp(`(\\bversionCode\\s*(?:=\\s*)?)\\d+${end}`, "gu");
|
|
18800
|
+
const name = new RegExp(`(\\bversionName\\s*(?:=\\s*)?)(?:"[^"']+"|'[^"']+')${end}`, "gu");
|
|
18801
|
+
const masked = blankComments(source);
|
|
18802
|
+
const codeEdits = [...masked.matchAll(code)].map((match) => ({
|
|
18803
|
+
start: (match.index ?? 0) + (match[1]?.length ?? 0),
|
|
18804
|
+
end: (match.index ?? 0) + match[0].length,
|
|
18805
|
+
text: versions.versionCode
|
|
18806
|
+
}));
|
|
18807
|
+
const nameEdits = [...masked.matchAll(name)].map((match) => ({
|
|
18808
|
+
start: (match.index ?? 0) + (match[1]?.length ?? 0) + 1,
|
|
18809
|
+
end: (match.index ?? 0) + match[0].length - 1,
|
|
18810
|
+
text: versions.versionName
|
|
18811
|
+
}));
|
|
18812
|
+
if (codeEdits.length === 0 || nameEdits.length === 0) {
|
|
18813
|
+
throw new Error(`Native version stamping found no literal versionCode and versionName in ${file}.`);
|
|
18814
|
+
}
|
|
18815
|
+
const edits = [...codeEdits, ...nameEdits];
|
|
18816
|
+
return applyTextEdits(source, edits);
|
|
18817
|
+
}
|
|
18818
|
+
function stampIosProjectVersions(source, versions, file) {
|
|
18819
|
+
const { appTarget: appTarget2 } = findIosBlocks(source, file);
|
|
18820
|
+
if (appTarget2 === void 0) {
|
|
18821
|
+
throw new Error(`Native release preflight could not find the App target in ${file}.`);
|
|
18822
|
+
}
|
|
18823
|
+
const listId = readPbxReference(appTarget2, /\bbuildConfigurationList\s*=\s*([A-F0-9]+)\b/u, "the App target build configuration list", file);
|
|
18824
|
+
const releaseId = readPbxReference(readPbxObject2(source, listId, file), /\b([A-F0-9]+)\s*\/\*\s*Release\s*\*\//u, "App target Release build configuration", file);
|
|
18825
|
+
const marker = source.match(new RegExp(`\\b${releaseId}\\s*\\/\\*\\s*[^*]+\\s*\\*\\/\\s*=\\s*\\{`, "u"));
|
|
18826
|
+
if (marker?.index === void 0) {
|
|
18827
|
+
throw new Error(`Native release preflight could not find Xcode object ${releaseId} in ${file}.`);
|
|
18828
|
+
}
|
|
18829
|
+
const objectOpen = source.indexOf("{", marker.index);
|
|
18830
|
+
const objectBody = readBracedBlock(source, objectOpen, `Xcode object ${releaseId}`, file);
|
|
18831
|
+
const settingsIndex = objectBody.search(/\bbuildSettings\s*=\s*\{/u);
|
|
18832
|
+
if (settingsIndex === -1) {
|
|
18833
|
+
throw new Error(`Native release preflight could not find App target Release build settings in ${file}.`);
|
|
18834
|
+
}
|
|
18835
|
+
const settingsOpen = objectOpen + 1 + objectBody.indexOf("{", settingsIndex);
|
|
18836
|
+
const settings = readBracedBlock(source, settingsOpen, "App target Release build settings", file);
|
|
18837
|
+
const masked = blankComments(settings);
|
|
18838
|
+
const edits = [];
|
|
18839
|
+
const additions = [];
|
|
18840
|
+
for (const [key, value] of [
|
|
18841
|
+
["MARKETING_VERSION", versions.marketingVersion],
|
|
18842
|
+
["CURRENT_PROJECT_VERSION", versions.buildNumber]
|
|
18843
|
+
]) {
|
|
18844
|
+
const assignment = new RegExp(`((?:^|[\\s{;])["']?${key}["']?\\s*=\\s*)[^;]+;`, "gu");
|
|
18845
|
+
const matches = [...masked.matchAll(assignment)];
|
|
18846
|
+
for (const match of matches) {
|
|
18847
|
+
edits.push({
|
|
18848
|
+
start: (match.index ?? 0) + (match[1]?.length ?? 0),
|
|
18849
|
+
end: (match.index ?? 0) + match[0].length - 1,
|
|
18850
|
+
text: value
|
|
18851
|
+
});
|
|
18852
|
+
}
|
|
18853
|
+
if (matches.length === 0) {
|
|
18854
|
+
additions.push(`${key} = ${value};`);
|
|
18855
|
+
}
|
|
18856
|
+
}
|
|
18857
|
+
const indentation = /\n([ \t]*)\S/u.exec(settings)?.[1] ?? " ";
|
|
18858
|
+
const stamped = `${additions.map((line) => `
|
|
18859
|
+
${indentation}${line}`).join("")}${applyTextEdits(settings, edits)}`;
|
|
18860
|
+
return `${source.slice(0, settingsOpen + 1)}${stamped}${source.slice(settingsOpen + 1 + settings.length)}`;
|
|
18861
|
+
}
|
|
18862
|
+
function applyTextEdits(source, edits) {
|
|
18863
|
+
let result = source;
|
|
18864
|
+
for (const edit of [...edits].sort((left, right) => right.start - left.start)) {
|
|
18865
|
+
result = `${result.slice(0, edit.start)}${edit.text}${result.slice(edit.end)}`;
|
|
18866
|
+
}
|
|
18867
|
+
return result;
|
|
18868
|
+
}
|
|
18869
|
+
function blankComments(source) {
|
|
18870
|
+
const stripped = stripComments(source);
|
|
18871
|
+
if (stripped.length === source.length) {
|
|
18872
|
+
return source;
|
|
18873
|
+
}
|
|
18874
|
+
let result = "";
|
|
18875
|
+
let quote;
|
|
18876
|
+
let lineComment = false;
|
|
18877
|
+
let blockComment = false;
|
|
18878
|
+
for (let index = 0; index < source.length; index += 1) {
|
|
18879
|
+
const character = source[index] ?? "";
|
|
18880
|
+
const next = source[index + 1];
|
|
18881
|
+
if (lineComment) {
|
|
18882
|
+
if (character === "\n") {
|
|
18883
|
+
lineComment = false;
|
|
18884
|
+
result += character;
|
|
18885
|
+
} else {
|
|
18886
|
+
result += character === "\r" ? character : " ";
|
|
18887
|
+
}
|
|
18888
|
+
continue;
|
|
18889
|
+
}
|
|
18890
|
+
if (blockComment) {
|
|
18891
|
+
if (character === "*" && next === "/") {
|
|
18892
|
+
blockComment = false;
|
|
18893
|
+
result += " ";
|
|
18894
|
+
index += 1;
|
|
18895
|
+
} else {
|
|
18896
|
+
result += character === "\n" || character === "\r" ? character : " ";
|
|
18897
|
+
}
|
|
18898
|
+
continue;
|
|
18899
|
+
}
|
|
18900
|
+
if (quote !== void 0) {
|
|
18901
|
+
result += character;
|
|
18902
|
+
if (character === "\\") {
|
|
18903
|
+
result += next ?? "";
|
|
18904
|
+
index += 1;
|
|
18905
|
+
} else if (character === quote) {
|
|
18906
|
+
quote = void 0;
|
|
18907
|
+
}
|
|
18908
|
+
continue;
|
|
18909
|
+
}
|
|
18910
|
+
if (character === "/" && next === "/") {
|
|
18911
|
+
lineComment = true;
|
|
18912
|
+
result += " ";
|
|
18913
|
+
index += 1;
|
|
18914
|
+
continue;
|
|
18915
|
+
}
|
|
18916
|
+
if (character === "/" && next === "*") {
|
|
18917
|
+
blockComment = true;
|
|
18918
|
+
result += " ";
|
|
18919
|
+
index += 1;
|
|
18920
|
+
continue;
|
|
18921
|
+
}
|
|
18922
|
+
if (character === '"' || character === "'") {
|
|
18923
|
+
quote = character;
|
|
18924
|
+
}
|
|
18925
|
+
result += character;
|
|
18926
|
+
}
|
|
18927
|
+
return result;
|
|
18928
|
+
}
|
|
18187
18929
|
function resolveExpectedNativeIdentity(platform, metadata, environment, required) {
|
|
18188
18930
|
if (platform === "android") {
|
|
18189
18931
|
const versionName = optional(environment.MPGD_TARGET_VERSION_NAME);
|
|
@@ -18210,7 +18952,7 @@ function resolveExpectedNativeIdentity(platform, metadata, environment, required
|
|
|
18210
18952
|
marketingVersion: requireFinalSemVer2(marketingVersion, "MPGD_TARGET_MARKETING_VERSION")
|
|
18211
18953
|
};
|
|
18212
18954
|
}
|
|
18213
|
-
function assertAndroidIdentity(file, expected) {
|
|
18955
|
+
function assertAndroidIdentity(file, expected, checkVersions) {
|
|
18214
18956
|
const source = stripComments(readRequiredFile(file, "Android Gradle configuration"));
|
|
18215
18957
|
const code = maskGradleStrings(source);
|
|
18216
18958
|
if (hasGradleBracketIdentityWrite(source)) {
|
|
@@ -18240,8 +18982,8 @@ function assertAndroidIdentity(file, expected) {
|
|
|
18240
18982
|
const codePattern = new RegExp(`\\bversionCode\\s*(?:=\\s*)?(\\d+)${end}`, "u");
|
|
18241
18983
|
const namePattern = new RegExp(`\\bversionName\\s*(?:=\\s*)?["']([^"']+)["']${end}`, "u");
|
|
18242
18984
|
assertAndroidSetting(source, "applicationId", appIdPattern, expected.packageId, file);
|
|
18243
|
-
assertAndroidSetting(source, "versionCode", codePattern, expected.versionCode, file);
|
|
18244
|
-
assertAndroidSetting(source, "versionName", namePattern, expected.versionName, file);
|
|
18985
|
+
assertAndroidSetting(source, "versionCode", codePattern, checkVersions ? expected.versionCode : void 0, file);
|
|
18986
|
+
assertAndroidSetting(source, "versionName", namePattern, checkVersions ? expected.versionName : void 0, file);
|
|
18245
18987
|
}
|
|
18246
18988
|
function assertAndroidSetting(source, key, expression, expected, file) {
|
|
18247
18989
|
const mentions = countGradleIdentityWrites(source, key);
|
|
@@ -18312,7 +19054,7 @@ function assertAndroidAppliedScripts(appBuild, androidRoot) {
|
|
|
18312
19054
|
throw new Error("Native release preflight cannot resolve an applied Gradle script.");
|
|
18313
19055
|
}
|
|
18314
19056
|
};
|
|
18315
|
-
const rootFiles = ["build.gradle", "build.gradle.kts"].map((name) =>
|
|
19057
|
+
const rootFiles = ["build.gradle", "build.gradle.kts"].map((name) => join14(androidRoot, name)).filter((file) => existsSync23(file));
|
|
18316
19058
|
if (rootFiles.length !== 1) {
|
|
18317
19059
|
throw new Error("Native release preflight requires one Android root Gradle build file.");
|
|
18318
19060
|
}
|
|
@@ -18322,7 +19064,7 @@ function assertAndroidAppliedScripts(appBuild, androidRoot) {
|
|
|
18322
19064
|
if (/\b(?:afterEvaluate|projectsEvaluated)\b/u.test(rootCode) && /\b(?:project|subprojects|allprojects|android)\b/u.test(rootCode)) {
|
|
18323
19065
|
throw new Error("Native release preflight cannot resolve root Gradle app callbacks.");
|
|
18324
19066
|
}
|
|
18325
|
-
const settingsFiles = ["settings.gradle", "settings.gradle.kts"].map((name) =>
|
|
19067
|
+
const settingsFiles = ["settings.gradle", "settings.gradle.kts"].map((name) => join14(androidRoot, name)).filter((file) => existsSync23(file));
|
|
18326
19068
|
if (settingsFiles.length !== 1) {
|
|
18327
19069
|
throw new Error("Native release preflight requires one Android settings Gradle file.");
|
|
18328
19070
|
}
|
|
@@ -18334,19 +19076,19 @@ function assertAndroidAppliedScripts(appBuild, androidRoot) {
|
|
|
18334
19076
|
}
|
|
18335
19077
|
inspect(settingsFile, true, androidRoot);
|
|
18336
19078
|
inspect(rootFile, false, androidRoot);
|
|
18337
|
-
inspect(appBuild, false,
|
|
19079
|
+
inspect(appBuild, false, dirname10(appBuild));
|
|
18338
19080
|
}
|
|
18339
|
-
function assertIosIdentity(file, expected) {
|
|
19081
|
+
function assertIosIdentity(file, expected, checkVersions) {
|
|
18340
19082
|
const source = readRequiredFile(file, "iOS Xcode project configuration");
|
|
18341
19083
|
assertIosReleaseProductName(source);
|
|
18342
19084
|
assertIosReleaseInfoPlistExpansion(source);
|
|
18343
19085
|
assertIosReleasePackagingSettings(source);
|
|
18344
19086
|
const releaseSettings = readIosAppReleaseSettings(source, file);
|
|
18345
19087
|
assertIosSetting(releaseSettings, "PRODUCT_BUNDLE_IDENTIFIER", expected.bundleId, file);
|
|
18346
|
-
assertIosSetting(releaseSettings, "MARKETING_VERSION", expected.marketingVersion, file);
|
|
18347
|
-
assertIosSetting(releaseSettings, "CURRENT_PROJECT_VERSION", expected.buildNumber, file);
|
|
19088
|
+
assertIosSetting(releaseSettings, "MARKETING_VERSION", checkVersions ? expected.marketingVersion : void 0, file);
|
|
19089
|
+
assertIosSetting(releaseSettings, "CURRENT_PROJECT_VERSION", checkVersions ? expected.buildNumber : void 0, file);
|
|
18348
19090
|
const plistRelative = readIosReleaseInfoPlist(source);
|
|
18349
|
-
const plist =
|
|
19091
|
+
const plist = join14(dirname10(dirname10(file)), plistRelative);
|
|
18350
19092
|
assertIosReleasePlistIdentity(readRequiredFile(plist, "iOS Release Info.plist"));
|
|
18351
19093
|
}
|
|
18352
19094
|
function assertIosSetting(settings, key, expected, file) {
|
|
@@ -18355,6 +19097,15 @@ function assertIosSetting(settings, key, expected, file) {
|
|
|
18355
19097
|
const conditional = new RegExp(conditionalPattern, "u");
|
|
18356
19098
|
const target2 = stripComments(settings.target.values);
|
|
18357
19099
|
const project = stripComments(settings.project.values);
|
|
19100
|
+
if (expected === void 0 && key !== "PRODUCT_BUNDLE_IDENTIFIER") {
|
|
19101
|
+
if (conditional.test(target2) || conditional.test(project)) {
|
|
19102
|
+
throw new Error(`Native release preflight does not support conditional ${key} in ${file}.`);
|
|
19103
|
+
}
|
|
19104
|
+
if (readSettingValues(target2, expression).length > 1) {
|
|
19105
|
+
throw new Error(`Native release preflight has ambiguous App Release ${key} in ${file}.`);
|
|
19106
|
+
}
|
|
19107
|
+
return;
|
|
19108
|
+
}
|
|
18358
19109
|
const targetRaw = readSettingValues(target2, expression);
|
|
18359
19110
|
if (targetRaw.length > 1) {
|
|
18360
19111
|
throw new Error(`Native release preflight has ambiguous App Release ${key} in ${file}.`);
|
|
@@ -18381,6 +19132,9 @@ function assertSettingValues(values, expression, expected, file) {
|
|
|
18381
19132
|
if (values.length === 0) {
|
|
18382
19133
|
throw new Error(`Native release preflight could not find ${expression.source} in ${file}.`);
|
|
18383
19134
|
}
|
|
19135
|
+
if (expected === void 0) {
|
|
19136
|
+
return;
|
|
19137
|
+
}
|
|
18384
19138
|
if (values.some((value) => value !== expected)) {
|
|
18385
19139
|
throw new Error(`Native release identity mismatch in ${file}: expected ${expected}, received ${values.join(", ")}.`);
|
|
18386
19140
|
}
|
|
@@ -18646,10 +19400,10 @@ function stripOptionalQuotes(value) {
|
|
|
18646
19400
|
return value;
|
|
18647
19401
|
}
|
|
18648
19402
|
function readRequiredFile(file, label) {
|
|
18649
|
-
if (!
|
|
19403
|
+
if (!existsSync23(file)) {
|
|
18650
19404
|
throw new Error(`Missing ${label}: ${file}`);
|
|
18651
19405
|
}
|
|
18652
|
-
return
|
|
19406
|
+
return readFileSync23(file, "utf8");
|
|
18653
19407
|
}
|
|
18654
19408
|
function requireFinalSemVer2(value, label) {
|
|
18655
19409
|
const normalized = requireValue(value, label);
|
|
@@ -18697,12 +19451,22 @@ async function executeNativeTargetBuild(input) {
|
|
|
18697
19451
|
required: profile2 === "production",
|
|
18698
19452
|
shellApp
|
|
18699
19453
|
};
|
|
18700
|
-
|
|
19454
|
+
const versionSource = resolveNativeVersionSource(environment);
|
|
19455
|
+
assertNativeReleaseIdentity({ ...identityInput, versionSource });
|
|
19456
|
+
reportNativeStorePolicy(inspectNativeStorePolicy({
|
|
19457
|
+
platform,
|
|
19458
|
+
shellApp,
|
|
19459
|
+
gameRoot: input.targetPath(target2.gameApp),
|
|
19460
|
+
metadata: target2.metadata
|
|
19461
|
+
}), profile2 === "production");
|
|
18701
19462
|
const stage = createNativeShellStage({ shellApp, webDir });
|
|
18702
19463
|
try {
|
|
18703
19464
|
input.replaceDirectory(webBundle, stage.webDir);
|
|
18704
19465
|
applyCapacitorShellCsp(stage.webDir);
|
|
18705
19466
|
input.ensureCapacitorPlatform(stage.shellApp, platform, environment);
|
|
19467
|
+
if (versionSource === "stamped") {
|
|
19468
|
+
stampNativeReleaseVersions({ platform, stagedShellApp: stage.shellApp, environment });
|
|
19469
|
+
}
|
|
18706
19470
|
await stageNativeIconResources(input.generatedIcons, stage.shellApp);
|
|
18707
19471
|
runNativeSyncWithIdentityCheck({ ...identityInput, shellApp: stage.shellApp }, () => {
|
|
18708
19472
|
input.run("pnpm", ["--dir", stage.shellApp, "cap", "sync", platform], environment);
|
|
@@ -18750,7 +19514,7 @@ function androidSignedReleaseGradleArgs(environment) {
|
|
|
18750
19514
|
if (initScript === void 0 || initScript === "") {
|
|
18751
19515
|
return ["bundleRelease", "--no-daemon"];
|
|
18752
19516
|
}
|
|
18753
|
-
if (!isAbsolute12(initScript) || !
|
|
19517
|
+
if (!isAbsolute12(initScript) || !existsSync24(initScript) || !statSync6(initScript).isFile()) {
|
|
18754
19518
|
throw new Error("Android upload signing init script must be an existing absolute file.");
|
|
18755
19519
|
}
|
|
18756
19520
|
return ["bundleRelease", "--no-daemon", "--init-script", initScript];
|
|
@@ -18775,7 +19539,7 @@ function executeIosBuildWithEnvironment(input, stagedShell, environment) {
|
|
|
18775
19539
|
let releaseArtifact;
|
|
18776
19540
|
if (plan.mode === "unsigned-archive") {
|
|
18777
19541
|
releaseArtifact = `${artifactRoot}/App.xcarchive`;
|
|
18778
|
-
mkdirSync12(
|
|
19542
|
+
mkdirSync12(dirname11(input.targetPath(releaseArtifact)), { recursive: true });
|
|
18779
19543
|
input.run("xcodebuild", [
|
|
18780
19544
|
"archive",
|
|
18781
19545
|
"-project",
|
|
@@ -18791,7 +19555,7 @@ function executeIosBuildWithEnvironment(input, stagedShell, environment) {
|
|
|
18791
19555
|
"CODE_SIGNING_ALLOWED=NO"
|
|
18792
19556
|
], environment, `${stagedShell}/ios`);
|
|
18793
19557
|
} else if (plan.mode === "simulator") {
|
|
18794
|
-
const buildRoot = mkdtempSync4(
|
|
19558
|
+
const buildRoot = mkdtempSync4(join15(tmpdir4(), "mpgd-ios-simulator-"));
|
|
18795
19559
|
releaseArtifact = `${artifactRoot}/App.app`;
|
|
18796
19560
|
try {
|
|
18797
19561
|
input.run("xcodebuild", [
|
|
@@ -18805,7 +19569,7 @@ function executeIosBuildWithEnvironment(input, stagedShell, environment) {
|
|
|
18805
19569
|
"-sdk",
|
|
18806
19570
|
"iphonesimulator",
|
|
18807
19571
|
`SYMROOT=${buildRoot}`,
|
|
18808
|
-
`OBJROOT=${
|
|
19572
|
+
`OBJROOT=${join15(buildRoot, "Intermediates.noindex")}`,
|
|
18809
19573
|
"INFOPLIST_FILE=App/Info-Smoke.plist",
|
|
18810
19574
|
"EXCLUDED_SOURCE_FILE_NAMES=Main.storyboard LaunchScreen.storyboard Assets.xcassets",
|
|
18811
19575
|
"ASSETCATALOG_COMPILER_APPICON_NAME=",
|
|
@@ -18825,7 +19589,7 @@ function executeIosBuildWithEnvironment(input, stagedShell, environment) {
|
|
|
18825
19589
|
} else if (plan.mode === "signed-archive" || plan.mode === "store-export") {
|
|
18826
19590
|
const signing = resolveIosSigningPlan(environment, plan.mode);
|
|
18827
19591
|
const archiveArtifact = `${artifactRoot}/App.xcarchive`;
|
|
18828
|
-
mkdirSync12(
|
|
19592
|
+
mkdirSync12(dirname11(input.targetPath(archiveArtifact)), { recursive: true });
|
|
18829
19593
|
input.run("xcodebuild", [
|
|
18830
19594
|
"archive",
|
|
18831
19595
|
"-project",
|
|
@@ -18849,7 +19613,7 @@ function executeIosBuildWithEnvironment(input, stagedShell, environment) {
|
|
|
18849
19613
|
inspectSignedIosArchive(input.targetPath(archiveArtifact), expectedIosIdentity);
|
|
18850
19614
|
releaseArtifact = archiveArtifact;
|
|
18851
19615
|
if (plan.mode === "store-export") {
|
|
18852
|
-
const exportRoot = mkdtempSync4(
|
|
19616
|
+
const exportRoot = mkdtempSync4(join15(tmpdir4(), "mpgd-ios-export-"));
|
|
18853
19617
|
try {
|
|
18854
19618
|
input.run("xcodebuild", [
|
|
18855
19619
|
"-exportArchive",
|
|
@@ -18865,7 +19629,7 @@ function executeIosBuildWithEnvironment(input, stagedShell, environment) {
|
|
|
18865
19629
|
throw new Error("iOS store export must produce exactly one IPA.");
|
|
18866
19630
|
}
|
|
18867
19631
|
releaseArtifact = `${artifactRoot}/App.ipa`;
|
|
18868
|
-
input.copyFile(
|
|
19632
|
+
input.copyFile(join15(exportRoot, exportedIpas[0] ?? ""), input.targetPath(releaseArtifact));
|
|
18869
19633
|
inspectSignedIosIpa(input.targetPath(releaseArtifact), expectedIosIdentity);
|
|
18870
19634
|
} finally {
|
|
18871
19635
|
rmSync11(exportRoot, { recursive: true, force: true });
|
|
@@ -18978,7 +19742,7 @@ var releaseManifestEnvKeys = [
|
|
|
18978
19742
|
var platformTargets = loadPlatformTargetsConfig();
|
|
18979
19743
|
var configBaseDir = platformTargets.baseDir;
|
|
18980
19744
|
var kitLinkCheck = resolve18("tools/workspace/relink-kit-workspace.mjs");
|
|
18981
|
-
if (
|
|
19745
|
+
if (existsSync25(kitLinkCheck)) {
|
|
18982
19746
|
spawnSync5(process.execPath, [kitLinkCheck, "--warn-unless-owner", configBaseDir], {
|
|
18983
19747
|
stdio: "inherit"
|
|
18984
19748
|
});
|
|
@@ -19060,11 +19824,11 @@ assertProductionTargetReadiness({
|
|
|
19060
19824
|
...runtimeTargetConfigMatrix.targets[targetName] === void 0 ? {} : { targetPolicy: runtimeTargetConfigMatrix.targets[targetName] },
|
|
19061
19825
|
...gameServicesUrl === void 0 ? {} : { gameServicesUrl }
|
|
19062
19826
|
});
|
|
19063
|
-
var nativeWebBundle = nativePlan === void 0 ? void 0 : mkdtempSync5(
|
|
19827
|
+
var nativeWebBundle = nativePlan === void 0 ? void 0 : mkdtempSync5(join16(tmpdir5(), "mpgd-native-web-"));
|
|
19064
19828
|
var nativeArtifactRoot = nativeAttempt === void 0 ? void 0 : `release-output/native/${targetName}/${safeArtifactFileStem(profile)}/${nativeAttempt.runId}`;
|
|
19065
19829
|
var nativeCompleted = false;
|
|
19066
19830
|
var runtimeTargetConfigMatrixFile = createRuntimeTargetConfigMatrixFile(runtimeTargetConfigMatrix);
|
|
19067
|
-
var assetTargetTemporary = target.assetPacks === void 0 ? void 0 : mkdtempSync5(
|
|
19831
|
+
var assetTargetTemporary = target.assetPacks === void 0 ? void 0 : mkdtempSync5(join16(tmpdir5(), "mpgd-target-assets-"));
|
|
19068
19832
|
var assetTargetReport;
|
|
19069
19833
|
var env = {
|
|
19070
19834
|
...process.env,
|
|
@@ -19093,7 +19857,7 @@ try {
|
|
|
19093
19857
|
assetTargetReport = await buildAssetPackTarget({
|
|
19094
19858
|
policy: target.assetPacks,
|
|
19095
19859
|
baseDir: configBaseDir,
|
|
19096
|
-
outDir:
|
|
19860
|
+
outDir: join16(assetTargetTemporary, "deployment")
|
|
19097
19861
|
});
|
|
19098
19862
|
}
|
|
19099
19863
|
if (targetName === "microsoft-store" && target.kind === "web" && profile === "production") {
|
|
@@ -19126,10 +19890,10 @@ try {
|
|
|
19126
19890
|
case "wechat-minigame": {
|
|
19127
19891
|
const outputConfigPath = requireString2(target.output, `${targetName}.output`);
|
|
19128
19892
|
const output = targetPath(outputConfigPath);
|
|
19129
|
-
const bundleRoot = mkdtempSync5(
|
|
19893
|
+
const bundleRoot = mkdtempSync5(join16(tmpdir5(), "mpgd-wechat-bundles-"));
|
|
19130
19894
|
try {
|
|
19131
|
-
const runtimeBundleRoot =
|
|
19132
|
-
const gameBundleRoot =
|
|
19895
|
+
const runtimeBundleRoot = join16(bundleRoot, "runtime");
|
|
19896
|
+
const gameBundleRoot = join16(bundleRoot, "game");
|
|
19133
19897
|
buildMiniGameBundle("runtime", runtimeBundleRoot, bundleRoot, gameApp, profile, env);
|
|
19134
19898
|
buildMiniGameBundle("game", gameBundleRoot, bundleRoot, gameApp, profile, env);
|
|
19135
19899
|
const effectiveTargetConfig = generateEffectiveTargetConfigArtifact(targetName, env);
|
|
@@ -19222,7 +19986,7 @@ try {
|
|
|
19222
19986
|
mirrorAitRuntimeAssets(gameApp, wrapperApp);
|
|
19223
19987
|
run("pnpm", ["--dir", wrapperApp, "exec", "vite", "build", "--mode", profile], env);
|
|
19224
19988
|
if (assetTargetReport !== void 0) {
|
|
19225
|
-
await assertAssetPackTargetArtifact(assetTargetReport, `${wrapperApp}/dist`,
|
|
19989
|
+
await assertAssetPackTargetArtifact(assetTargetReport, `${wrapperApp}/dist`, join16(relative13(join16(wrapperApp, "public"), webDir), assetPackTargetNamespace));
|
|
19226
19990
|
}
|
|
19227
19991
|
let releaseArtifact = webDirConfigPath;
|
|
19228
19992
|
if (process.env.MPGD_AIT_PACKAGE_MODE !== "skip") {
|
|
@@ -19256,7 +20020,7 @@ try {
|
|
|
19256
20020
|
await stageTargetAssetPacks(webDir);
|
|
19257
20021
|
stageWebIconEvidence(generatedIcons, webDir);
|
|
19258
20022
|
if (assetTargetReport !== void 0) {
|
|
19259
|
-
await assertAssetPackTargetArtifact(assetTargetReport, `${wrapperApp}/dist`,
|
|
20023
|
+
await assertAssetPackTargetArtifact(assetTargetReport, `${wrapperApp}/dist`, join16(relative13(join16(wrapperApp, "dist"), webDir), assetPackTargetNamespace));
|
|
19260
20024
|
}
|
|
19261
20025
|
await writeRemoteAssetPacks();
|
|
19262
20026
|
writeManifest(targetName, profile, `${wrapperAppConfigPath}/dist`, env);
|
|
@@ -19303,12 +20067,12 @@ try {
|
|
|
19303
20067
|
if (nativeWebBundle !== void 0) {
|
|
19304
20068
|
rmSync12(nativeWebBundle, { force: true, recursive: true });
|
|
19305
20069
|
}
|
|
19306
|
-
rmSync12(
|
|
20070
|
+
rmSync12(dirname12(runtimeTargetConfigMatrixFile), { force: true, recursive: true });
|
|
19307
20071
|
}
|
|
19308
20072
|
function createRuntimeTargetConfigMatrixFile(matrix) {
|
|
19309
|
-
const directory = mkdtempSync5(
|
|
19310
|
-
const file =
|
|
19311
|
-
|
|
20073
|
+
const directory = mkdtempSync5(join16(tmpdir5(), "mpgd-target-config-matrix-"));
|
|
20074
|
+
const file = join16(directory, "matrix.json");
|
|
20075
|
+
writeFileSync17(file, `${JSON.stringify(matrix)}
|
|
19312
20076
|
`);
|
|
19313
20077
|
return file;
|
|
19314
20078
|
}
|
|
@@ -19328,7 +20092,7 @@ function replaceDirectory(source, destination) {
|
|
|
19328
20092
|
function mirrorAitRuntimeAssets(gameApp2, wrapperApp) {
|
|
19329
20093
|
const sourceAssets = `${gameApp2}/dist/assets`;
|
|
19330
20094
|
const destinationAssets = `${wrapperApp}/public/assets`;
|
|
19331
|
-
if (
|
|
20095
|
+
if (existsSync25(sourceAssets)) {
|
|
19332
20096
|
replaceDirectory(sourceAssets, destinationAssets);
|
|
19333
20097
|
} else {
|
|
19334
20098
|
rmSync12(destinationAssets, { recursive: true, force: true });
|
|
@@ -19338,8 +20102,8 @@ async function stageTargetAssetPacks(webOutput) {
|
|
|
19338
20102
|
if (assetTargetReport === void 0) {
|
|
19339
20103
|
return;
|
|
19340
20104
|
}
|
|
19341
|
-
const destination =
|
|
19342
|
-
if (
|
|
20105
|
+
const destination = join16(webOutput, assetPackTargetNamespace);
|
|
20106
|
+
if (existsSync25(destination)) {
|
|
19343
20107
|
throw new Error("mpgd-asset-packs is owned by the target policy; remove source/public copies from the game");
|
|
19344
20108
|
}
|
|
19345
20109
|
cpSync4(assetTargetReport.packagedDir, destination, {
|
|
@@ -19362,7 +20126,7 @@ async function writeRemoteAssetPacks() {
|
|
|
19362
20126
|
});
|
|
19363
20127
|
replaceDirectory(assetTargetReport.remoteDir, `${remoteOutput}/remote`);
|
|
19364
20128
|
mkdirSync13(remoteOutput, { recursive: true });
|
|
19365
|
-
|
|
20129
|
+
writeFileSync17(`${remoteOutput}/report.json`, JSON.stringify({
|
|
19366
20130
|
...assetTargetReport,
|
|
19367
20131
|
outDir: remoteOutput,
|
|
19368
20132
|
packagedDir: assetPackTargetNamespace,
|
|
@@ -19433,7 +20197,7 @@ function copyIosSyncSwiftPackage(shellApp, releaseArtifact, packageName, swiftPa
|
|
|
19433
20197
|
const shellReference = readSwiftPackagePath(shellPackageFile, swiftPackageName);
|
|
19434
20198
|
let referencedPackage;
|
|
19435
20199
|
try {
|
|
19436
|
-
referencedPackage = realpathSync10(resolve18(
|
|
20200
|
+
referencedPackage = realpathSync10(resolve18(dirname12(shellPackageFile), shellReference));
|
|
19437
20201
|
} catch {
|
|
19438
20202
|
throw new Error(`Missing iOS Swift package reference: ${swiftPackageName}`);
|
|
19439
20203
|
}
|
|
@@ -19445,14 +20209,14 @@ function copyIosSyncSwiftPackage(shellApp, releaseArtifact, packageName, swiftPa
|
|
|
19445
20209
|
}
|
|
19446
20210
|
function rewriteIosSyncSwiftPackagePath(releaseArtifact, packageName, swiftPackageName) {
|
|
19447
20211
|
const packageFile = targetPath(`${releaseArtifact}/App/CapApp-SPM/Package.swift`);
|
|
19448
|
-
const contents =
|
|
20212
|
+
const contents = readFileSync24(packageFile, "utf8");
|
|
19449
20213
|
const originalPath = readSwiftPackagePath(packageFile, swiftPackageName);
|
|
19450
20214
|
const rewrittenPath = `../../node_modules/${packageName}`;
|
|
19451
20215
|
const marker = `.package(name: "${swiftPackageName}", path: "`;
|
|
19452
|
-
|
|
20216
|
+
writeFileSync17(packageFile, contents.replace(`${marker}${originalPath}"`, `${marker}${rewrittenPath}"`));
|
|
19453
20217
|
}
|
|
19454
20218
|
function readSwiftPackagePath(packageFile, swiftPackageName) {
|
|
19455
|
-
const contents =
|
|
20219
|
+
const contents = readFileSync24(packageFile, "utf8");
|
|
19456
20220
|
const marker = `.package(name: "${swiftPackageName}", path: "`;
|
|
19457
20221
|
const index = contents.indexOf(marker);
|
|
19458
20222
|
if (index < 0 || contents.indexOf(marker, index + marker.length) >= 0) {
|
|
@@ -19466,11 +20230,11 @@ function readSwiftPackagePath(packageFile, swiftPackageName) {
|
|
|
19466
20230
|
return contents.slice(pathStart, pathEnd);
|
|
19467
20231
|
}
|
|
19468
20232
|
function copyFile2(source, destination) {
|
|
19469
|
-
mkdirSync13(
|
|
20233
|
+
mkdirSync13(dirname12(destination), { recursive: true });
|
|
19470
20234
|
cpSync4(source, destination);
|
|
19471
20235
|
}
|
|
19472
20236
|
function removeFilesByExtension(directory, extension) {
|
|
19473
|
-
if (!
|
|
20237
|
+
if (!existsSync25(directory)) {
|
|
19474
20238
|
return;
|
|
19475
20239
|
}
|
|
19476
20240
|
for (const file of readdirSync8(directory)) {
|
|
@@ -19485,7 +20249,7 @@ function safeArtifactFileStem(value) {
|
|
|
19485
20249
|
return stem.length === 0 ? "mpgd-kit" : stem;
|
|
19486
20250
|
}
|
|
19487
20251
|
function ensureCapacitorPlatform(shellApp, platform, commandEnv) {
|
|
19488
|
-
if (!
|
|
20252
|
+
if (!existsSync25(`${shellApp}/${platform}`)) {
|
|
19489
20253
|
run("pnpm", ["--dir", shellApp, "cap", "add", platform], commandEnv);
|
|
19490
20254
|
}
|
|
19491
20255
|
}
|
|
@@ -19522,7 +20286,7 @@ function findEmbeddedIconManifestArtifactPath(artifact) {
|
|
|
19522
20286
|
function findNamedArtifactFiles(root, fileName) {
|
|
19523
20287
|
const matches = [];
|
|
19524
20288
|
for (const entry of readdirSync8(root)) {
|
|
19525
|
-
const path10 =
|
|
20289
|
+
const path10 = join16(root, entry);
|
|
19526
20290
|
const stat2 = lstatSync13(path10);
|
|
19527
20291
|
if (stat2.isSymbolicLink()) {
|
|
19528
20292
|
continue;
|
|
@@ -19572,8 +20336,8 @@ function isMiniGameTarget(target2) {
|
|
|
19572
20336
|
return target2.kind === "wechat-minigame" || target2.kind === "tiktok-minigame";
|
|
19573
20337
|
}
|
|
19574
20338
|
function buildMiniGameBundle(bundleKind, outputDir, stagingRoot, gameApp2, profile2, commandEnv) {
|
|
19575
|
-
const viteConfig =
|
|
19576
|
-
if (!
|
|
20339
|
+
const viteConfig = join16(gameApp2, "vite.minigame.config.ts");
|
|
20340
|
+
if (!existsSync25(viteConfig)) {
|
|
19577
20341
|
throw new Error(`Mini-game target requires a game-owned vite.minigame.config.ts: ${viteConfig}`);
|
|
19578
20342
|
}
|
|
19579
20343
|
run("pnpm", ["exec", "vite", "build", "--config", "vite.minigame.config.ts", "--mode", profile2], {
|