@mpgd/cli 0.59.0 → 0.61.1
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 +24 -0
- package/dist/capacitor-shell-starter.js +100 -8
- package/dist/crash-reporting-starter.d.ts +99 -0
- package/dist/crash-reporting-starter.js +271 -0
- package/dist/index.d.ts +5 -2
- package/dist/index.js +221 -14
- package/dist/ios-ipa-inspection.js +120 -1
- package/dist/native-build-info.json +2 -2
- package/dist/native-build-target.js +1617 -1185
- package/dist/native-deploy-run.d.ts +6 -0
- package/dist/native-deploy-run.js +19 -1
- package/dist/native-deploy-submission.d.ts +20 -1
- package/dist/native-deploy-submission.js +94 -7
- package/dist/native-shell-ios-push-files.d.ts +9 -0
- package/dist/native-shell-ios-push-files.js +65 -0
- package/dist/native-shell-store-policy.d.ts +109 -0
- package/dist/native-shell-store-policy.js +553 -7
- package/dist/play-internal-submission.d.ts +40 -4
- package/dist/play-internal-submission.js +215 -48
- package/dist/play-publisher-port.d.ts +26 -1
- package/dist/play-release-options.d.ts +97 -0
- package/dist/play-release-options.js +307 -0
- package/dist/play-release-promote.d.ts +59 -0
- package/dist/play-release-promote.js +235 -0
- package/dist/play-release-rollout.d.ts +57 -0
- package/dist/play-release-rollout.js +237 -0
- package/dist/play-sdk-adapter.js +26 -3
- package/dist/release-state.d.ts +6 -0
- package/dist/release-state.js +8 -0
- package/package.json +17 -17
- package/templates/phaser-game/apps/target-ait/package.json +2 -2
- package/templates/phaser-game/package.json +1 -1
- package/templates/phaser-game/vite.runtime-target-config.ts +4 -0
|
@@ -76,6 +76,14 @@ export interface CapacitorShellStarterPlan {
|
|
|
76
76
|
readonly version: string;
|
|
77
77
|
}[];
|
|
78
78
|
/**
|
|
79
|
+
* The Firebase Crashlytics plugin (`--providers crash-reporting`) the plan
|
|
80
|
+
* installs into the shell; empty when crash reporting is not requested.
|
|
81
|
+
*/
|
|
82
|
+
readonly crashReportingPlugins: readonly {
|
|
83
|
+
readonly packageName: string;
|
|
84
|
+
readonly version: string;
|
|
85
|
+
}[];
|
|
86
|
+
/**
|
|
79
87
|
* Store wiring the plan could not write or confirm: a missing or game-owned
|
|
80
88
|
* gateway that does not register the plugin, or a store plugin the shell
|
|
81
89
|
* keeps although it is no longer requested. Printed; game source is never
|
|
@@ -117,6 +125,22 @@ export declare const androidManifestBackupAttributes: readonly (readonly [string
|
|
|
117
125
|
* the result is stable across repeated runs.
|
|
118
126
|
*/
|
|
119
127
|
export declare function applyAndroidManifestBackupPolicy(manifest: string): string;
|
|
128
|
+
/**
|
|
129
|
+
* iOS push wiring of a freshly generated project (`--providers notifications`):
|
|
130
|
+
* the `aps-environment` entitlement on the App target, the AppDelegate's
|
|
131
|
+
* APNs registration forwarding, and the reference shell's cold-start tap
|
|
132
|
+
* forwarding in SceneDelegate (#407). An existing game-owned project is never
|
|
133
|
+
* rewritten; the initializer and the production build report what it lacks.
|
|
134
|
+
* Whatever this cannot place in an unexpected layout is printed as the
|
|
135
|
+
* manual step. Exported for the CLI's tests.
|
|
136
|
+
*/
|
|
137
|
+
export declare function materializeIosPushWiring(gameRoot: string): void;
|
|
138
|
+
/**
|
|
139
|
+
* The initializer's warnings for an existing, game-owned iOS project that
|
|
140
|
+
* lacks the push wiring: each names the manual step. Empty when the shell has
|
|
141
|
+
* no iOS project yet (the run generates it with the wiring) or lacks nothing.
|
|
142
|
+
*/
|
|
143
|
+
export declare function iosPushWiringWarnings(gameRoot: string): readonly string[];
|
|
120
144
|
/** Fail closed when a Release plist hardcodes values independently of Xcode build settings. */
|
|
121
145
|
export declare function assertIosReleasePlistIdentity(source: string): void;
|
|
122
146
|
export declare function assertAndroidReleaseDisplayName(shellApp: string, expectedDisplayName: string): void;
|
|
@@ -6,7 +6,9 @@ import { fileURLToPath } from 'node:url';
|
|
|
6
6
|
import { DOMParser } from '@xmldom/xmldom';
|
|
7
7
|
import { satisfies, valid, validRange } from 'semver';
|
|
8
8
|
import { capacitorGatewayRegistersNotifications, conflictingPushPlugin, notificationsCapacitorGatewayModules, notificationsCapacitorGateways, notificationsCapacitorSupportFiles, notificationsCapacitorTemplateSupportFiles, notificationsCapacitorTemplateSupportImporters, notificationsGameDependencies, notificationsMessagingFirebaseRange, notificationsMessagingPlugin, notificationsProviderId, notificationsShellPlugins, sourceReferencesRelativeModule, wireCapacitorGatewayNotifications, } from './notifications-starter.js';
|
|
9
|
-
import {
|
|
9
|
+
import { crashReportingPlugin, crashReportingProviderId, crashReportingRemovalWarning, materializeAndroidCrashlytics, planCrashReportingProvider, } from './crash-reporting-starter.js';
|
|
10
|
+
import { addIosAppResource, applyAndroidManifestAdId, applyAndroidManifestOrientation, applyIosAppDelegatePushForwarding, applyIosInfoPlistOrientation, applyIosInfoPlistStorePolicy, applyIosProjectDeviceFamily, applyIosProjectPushEntitlements, applyIosSceneDelegateColdStartForwarding, inspectIosPushWiring, iosAppPrivacyManifestFile, iosAppPrivacyManifestXml, iosPushEntitlementsFile, iosPushEntitlementsXml, isRecord, readGameAdvertisingIdPolicy, readGameNativeOrientation, readIosDeviceFamily, shellUsesAdMob, } from './native-shell-store-policy.js';
|
|
11
|
+
import { readIosPushWiringInput } from './native-shell-ios-push-files.js';
|
|
10
12
|
import { isNonPublicServiceHostname } from './production-target-readiness.js';
|
|
11
13
|
import { assertAndroidSettingsAppProject, assertAndroidSettingsNoAppRemap, assertIosReleaseInfoPlistExpansion, assertIosReleasePackagingSettings, assertIosReleaseProductName, assertNativeShellIdentity, hasAndroidDisplayNameResourceOverride, hasAndroidManifestSourceSetOverride, hasAndroidResourceSourceSetOverride, hasGradleIdentityMutation, hasGradleTaskAction, maskGradleStrings, readIosAppTargetId, readIosReleaseInfoPlist, stripGradleComments, } from './native-shell-identity.js';
|
|
12
14
|
const shellPath = 'apps/mobile-capacitor';
|
|
@@ -262,6 +264,19 @@ export function planCapacitorShellStarter(input) {
|
|
|
262
264
|
...(notifications?.files ?? []),
|
|
263
265
|
];
|
|
264
266
|
const notificationPlugins = notifications?.shellPlugins ?? [];
|
|
267
|
+
const existingShellDependencies = packageContent === undefined
|
|
268
|
+
? undefined
|
|
269
|
+
: requireObject(parseJsonObject(packageContent, 'existing shell package').dependencies, 'existing shell dependencies');
|
|
270
|
+
const crashReporting = requestedProviderIds.includes(crashReportingProviderId)
|
|
271
|
+
? planCrashReportingProvider({ gameRoot, shellPath, existingShellDependencies, warnings })
|
|
272
|
+
: undefined;
|
|
273
|
+
const crashReportingPlugins = crashReporting?.shellPlugins ?? [];
|
|
274
|
+
const crashReportingWarning = crashReporting === undefined && existingShellDependencies !== undefined
|
|
275
|
+
? crashReportingRemovalWarning(existingShellDependencies)
|
|
276
|
+
: undefined;
|
|
277
|
+
if (crashReportingWarning !== undefined) {
|
|
278
|
+
warnings.push(crashReportingWarning);
|
|
279
|
+
}
|
|
265
280
|
if (packageContent !== undefined) {
|
|
266
281
|
const existingShellDependencies = requireObject(parseJsonObject(packageContent, 'existing shell package').dependencies, 'existing shell dependencies');
|
|
267
282
|
for (const plugin of capacitorShellProviderPlugins) {
|
|
@@ -310,6 +325,7 @@ export function planCapacitorShellStarter(input) {
|
|
|
310
325
|
'@mpgd/capacitor-game-services': pluginVersion,
|
|
311
326
|
...Object.fromEntries(providerPlugins.map((plugin) => [plugin.packageName, plugin.version])),
|
|
312
327
|
...Object.fromEntries(notificationPlugins.map((plugin) => [plugin.packageName, plugin.version])),
|
|
328
|
+
...Object.fromEntries(crashReportingPlugins.map((plugin) => [plugin.packageName, plugin.version])),
|
|
313
329
|
};
|
|
314
330
|
if (packageContent !== undefined) {
|
|
315
331
|
assertCompatibleExistingPackage(packageContent, shellDependencies, capacitorVersion, typescriptVersion);
|
|
@@ -356,7 +372,7 @@ export function planCapacitorShellStarter(input) {
|
|
|
356
372
|
typescript: typescriptVersion,
|
|
357
373
|
},
|
|
358
374
|
})
|
|
359
|
-
: withPackageDependencies(packageContent, [...providerPlugins, ...notificationPlugins]),
|
|
375
|
+
: withPackageDependencies(packageContent, [...providerPlugins, ...notificationPlugins, ...crashReportingPlugins]),
|
|
360
376
|
},
|
|
361
377
|
{
|
|
362
378
|
path: `${shellPath}/capacitor.config.ts`,
|
|
@@ -384,6 +400,7 @@ export function planCapacitorShellStarter(input) {
|
|
|
384
400
|
displayName: input.displayName,
|
|
385
401
|
requestedProviderIds,
|
|
386
402
|
...(providerPlugins.length === 0 && notificationPlugins.length === 0
|
|
403
|
+
&& crashReporting === undefined
|
|
387
404
|
? {}
|
|
388
405
|
: {
|
|
389
406
|
providers: {
|
|
@@ -403,6 +420,9 @@ export function planCapacitorShellStarter(input) {
|
|
|
403
420
|
platforms: ['android', 'ios'],
|
|
404
421
|
},
|
|
405
422
|
}),
|
|
423
|
+
...(crashReporting === undefined
|
|
424
|
+
? {}
|
|
425
|
+
: { [crashReportingProviderId]: crashReporting.manifestEntry }),
|
|
406
426
|
},
|
|
407
427
|
}),
|
|
408
428
|
...(requestedBackendUrl === undefined ? {} : { backendUrl: requestedBackendUrl }),
|
|
@@ -427,8 +447,9 @@ export function planCapacitorShellStarter(input) {
|
|
|
427
447
|
'@capacitor-firebase/messaging and @capacitor/local-notifications (never',
|
|
428
448
|
`${conflictingPushPlugin}, which claims the same iOS push handler).`,
|
|
429
449
|
'Notifications also need the Firebase files (google-services.json,',
|
|
430
|
-
'GoogleService-Info.plist) and
|
|
431
|
-
'
|
|
450
|
+
'GoogleService-Info.plist) and an APNs key in Firebase; an iOS project',
|
|
451
|
+
'generated with them gets the push entitlement and the AppDelegate and',
|
|
452
|
+
'SceneDelegate forwarding. See the mpgd-kit Firebase guide. A plugin never reports a feature as',
|
|
432
453
|
'available on its own; the gateway registers the store provider, and the',
|
|
433
454
|
'store answers configuration-required until the plugin and products exist.',
|
|
434
455
|
'Production builds require a real game-owned backend when grants are enabled.',
|
|
@@ -539,6 +560,7 @@ export function planCapacitorShellStarter(input) {
|
|
|
539
560
|
nativePlatformsToAdd,
|
|
540
561
|
providerPlugins,
|
|
541
562
|
notificationPlugins,
|
|
563
|
+
crashReportingPlugins,
|
|
542
564
|
warnings,
|
|
543
565
|
};
|
|
544
566
|
}
|
|
@@ -660,9 +682,15 @@ export function materializeCapacitorShellStarter(plan, runner = { run: runComman
|
|
|
660
682
|
materializeAndroidAdvertisingRenderer(plan.gameRoot);
|
|
661
683
|
}
|
|
662
684
|
materializeAndroidStorePolicy(plan.gameRoot);
|
|
685
|
+
if (plan.crashReportingPlugins.length > 0) {
|
|
686
|
+
materializeAndroidCrashlytics(path.join(plan.gameRoot, shellPath));
|
|
687
|
+
}
|
|
663
688
|
}
|
|
664
689
|
if (platform === 'ios' && generatedNow) {
|
|
665
690
|
materializeIosStorePolicy(plan.gameRoot);
|
|
691
|
+
if (plan.notificationPlugins.length > 0) {
|
|
692
|
+
materializeIosPushWiring(plan.gameRoot);
|
|
693
|
+
}
|
|
666
694
|
}
|
|
667
695
|
if (platform === 'ios') {
|
|
668
696
|
materializeIosArchiveScheme(plan.gameRoot);
|
|
@@ -682,7 +710,7 @@ export function materializeCapacitorShellStarter(plan, runner = { run: runComman
|
|
|
682
710
|
// sync so the store plugin's native code joins the shell.
|
|
683
711
|
for (const platform of ['android', 'ios']) {
|
|
684
712
|
if ((plan.providerPlugins.some((plugin) => plugin.platform === platform)
|
|
685
|
-
|| plan.notificationPlugins.length > 0)
|
|
713
|
+
|| plan.notificationPlugins.length > 0 || plan.crashReportingPlugins.length > 0)
|
|
686
714
|
&& !plan.nativePlatformsToAdd.includes(platform)
|
|
687
715
|
&& existsSync(path.join(plan.gameRoot, shellPath, platform))) {
|
|
688
716
|
runner.run('pnpm', ['--dir', shellPath, 'cap', 'sync', platform], plan.gameRoot);
|
|
@@ -851,6 +879,67 @@ function materializeIosStorePolicy(gameRoot) {
|
|
|
851
879
|
writeFileSync(projectFile, next);
|
|
852
880
|
}
|
|
853
881
|
}
|
|
882
|
+
/**
|
|
883
|
+
* iOS push wiring of a freshly generated project (`--providers notifications`):
|
|
884
|
+
* the `aps-environment` entitlement on the App target, the AppDelegate's
|
|
885
|
+
* APNs registration forwarding, and the reference shell's cold-start tap
|
|
886
|
+
* forwarding in SceneDelegate (#407). An existing game-owned project is never
|
|
887
|
+
* rewritten; the initializer and the production build report what it lacks.
|
|
888
|
+
* Whatever this cannot place in an unexpected layout is printed as the
|
|
889
|
+
* manual step. Exported for the CLI's tests.
|
|
890
|
+
*/
|
|
891
|
+
export function materializeIosPushWiring(gameRoot) {
|
|
892
|
+
const projectFile = safeDestination(gameRoot, `${shellPath}/ios/App/App.xcodeproj/project.pbxproj`);
|
|
893
|
+
const project = readFileSync(projectFile, 'utf8');
|
|
894
|
+
const withEntitlements = applyIosProjectPushEntitlements(project);
|
|
895
|
+
if (withEntitlements !== undefined) {
|
|
896
|
+
const entitlementsFile = safeDestination(gameRoot, `${shellPath}/ios/App/App/${iosPushEntitlementsFile}`);
|
|
897
|
+
if (!existsSync(entitlementsFile)) {
|
|
898
|
+
writeFileSync(entitlementsFile, iosPushEntitlementsXml, { flag: 'wx' });
|
|
899
|
+
}
|
|
900
|
+
if (withEntitlements !== project) {
|
|
901
|
+
writeFileSync(projectFile, withEntitlements);
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
for (const [name, apply] of [
|
|
905
|
+
['AppDelegate.swift', applyIosAppDelegatePushForwarding],
|
|
906
|
+
['SceneDelegate.swift', applyIosSceneDelegateColdStartForwarding],
|
|
907
|
+
]) {
|
|
908
|
+
const file = safeDestination(gameRoot, `${shellPath}/ios/App/App/${name}`);
|
|
909
|
+
if (!existsSync(file) || !lstatSync(file).isFile()) {
|
|
910
|
+
continue;
|
|
911
|
+
}
|
|
912
|
+
const source = readFileSync(file, 'utf8');
|
|
913
|
+
let next;
|
|
914
|
+
try {
|
|
915
|
+
next = apply(source);
|
|
916
|
+
}
|
|
917
|
+
catch {
|
|
918
|
+
// Left to the game; the inspection below prints the step.
|
|
919
|
+
next = undefined;
|
|
920
|
+
}
|
|
921
|
+
if (next !== undefined && next !== source) {
|
|
922
|
+
writeFileSync(file, next);
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
for (const issue of readIosPushWiringIssues(gameRoot) ?? []) {
|
|
926
|
+
console.warn(`[mpgd] The generated iOS project has an unexpected layout: ${issue.message}`);
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
/**
|
|
930
|
+
* The initializer's warnings for an existing, game-owned iOS project that
|
|
931
|
+
* lacks the push wiring: each names the manual step. Empty when the shell has
|
|
932
|
+
* no iOS project yet (the run generates it with the wiring) or lacks nothing.
|
|
933
|
+
*/
|
|
934
|
+
export function iosPushWiringWarnings(gameRoot) {
|
|
935
|
+
return (readIosPushWiringIssues(gameRoot) ?? []).map((issue) => `${shellPath}/ios is game-owned and not rewritten: ${issue.message}`);
|
|
936
|
+
}
|
|
937
|
+
/** What the shell's iOS project lacks for push, or undefined when it has no project yet. */
|
|
938
|
+
function readIosPushWiringIssues(gameRoot) {
|
|
939
|
+
safeDestination(gameRoot, `${shellPath}/ios/App`);
|
|
940
|
+
const input = readIosPushWiringInput(path.join(gameRoot, shellPath, 'ios/App'));
|
|
941
|
+
return input === undefined ? undefined : inspectIosPushWiring(input);
|
|
942
|
+
}
|
|
854
943
|
/**
|
|
855
944
|
* Orientation of a freshly generated Android project's launcher activity,
|
|
856
945
|
* `android:appCategory="game"`, and the advertising ID permission (declared
|
|
@@ -2223,6 +2312,7 @@ function assertCompatibleExistingPackage(source, shellDependencies, capacitorVer
|
|
|
2223
2312
|
const providerPackages = new Set([
|
|
2224
2313
|
...capacitorShellProviderPlugins.map((plugin) => plugin.packageName),
|
|
2225
2314
|
...notificationsShellPlugins,
|
|
2315
|
+
crashReportingPlugin,
|
|
2226
2316
|
]);
|
|
2227
2317
|
for (const [name, version] of Object.entries(shellDependencies)) {
|
|
2228
2318
|
// A store plugin the shell does not list yet is added by the plan. One it
|
|
@@ -3069,10 +3159,12 @@ function planNotificationsProvider(input) {
|
|
|
3069
3159
|
}
|
|
3070
3160
|
if (readExisting(safeDestination(gameRoot, `${shellPath}/ios/App/App/GoogleService-Info.plist`))
|
|
3071
3161
|
=== undefined) {
|
|
3072
|
-
warnings.push(`iOS push needs ${shellPath}/ios/App/App/GoogleService-Info.plist
|
|
3073
|
-
+ '
|
|
3074
|
-
+ 'messaging plugin documents (see the mpgd-kit Firebase guide).');
|
|
3162
|
+
warnings.push(`iOS push needs ${shellPath}/ios/App/App/GoogleService-Info.plist in the App target and an `
|
|
3163
|
+
+ 'APNs key uploaded to Firebase (see the mpgd-kit Firebase guide).');
|
|
3075
3164
|
}
|
|
3165
|
+
// A project this run generates gets the entitlement and the forwarding; an
|
|
3166
|
+
// existing one is game-owned, so only the steps it still needs are printed.
|
|
3167
|
+
warnings.push(...iosPushWiringWarnings(gameRoot));
|
|
3076
3168
|
return {
|
|
3077
3169
|
shellPlugins: shellPlugins.map(({ packageName, version }) => ({ packageName, version })),
|
|
3078
3170
|
gameDependencies: gamePackages,
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { type NativeShellPolicyIssue } from './native-shell-store-policy.js';
|
|
2
|
+
export declare const crashReportingProviderId = "crash-reporting";
|
|
3
|
+
/** The native plugin installed into the shell. */
|
|
4
|
+
export declare const crashReportingPlugin = "@capacitor-firebase/crashlytics";
|
|
5
|
+
/** The plugin version this CLI installs when the shell does not list one yet. */
|
|
6
|
+
export declare const crashReportingPluginVersion = "8.5.2";
|
|
7
|
+
/**
|
|
8
|
+
* The Crashlytics Gradle plugin, which writes the build ID the Crashlytics
|
|
9
|
+
* Android SDK requires at startup and uploads R8 mapping files. 3.0.8 needs
|
|
10
|
+
* AGP 8.1+, Gradle 8.0+, and the Google services plugin 4.4.1+
|
|
11
|
+
* (https://firebase.google.com/docs/crashlytics/android/get-started).
|
|
12
|
+
*/
|
|
13
|
+
export declare const crashlyticsGradlePluginVersion = "3.0.8";
|
|
14
|
+
/** The manual Android steps for a shell whose Gradle files the CLI does not rewrite. */
|
|
15
|
+
export declare const crashReportingAndroidSteps: readonly string[];
|
|
16
|
+
/**
|
|
17
|
+
* The iOS steps the CLI leaves to the game: the plugin captures crashes once
|
|
18
|
+
* `GoogleService-Info.plist` is in the App target, but readable native stack
|
|
19
|
+
* traces need the dSYMs uploaded (docs/CRASH_REPORTING.md#symbols).
|
|
20
|
+
*/
|
|
21
|
+
export declare const crashReportingIosSteps: readonly string[];
|
|
22
|
+
export interface PlannedCrashReporting {
|
|
23
|
+
/** The plugin the plan installs into the shell, at the shell's version or this CLI's. */
|
|
24
|
+
readonly shellPlugins: readonly {
|
|
25
|
+
readonly packageName: string;
|
|
26
|
+
readonly version: string;
|
|
27
|
+
}[];
|
|
28
|
+
/** `mpgd.native-shell.json` `providers['crash-reporting']`. */
|
|
29
|
+
readonly manifestEntry: Readonly<Record<string, unknown>>;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Plan the crash-reporting provider before anything is written. Refused when
|
|
33
|
+
* the game's adapter has no crash reporter, when the plugin version is outside
|
|
34
|
+
* the adapter's peer range, or when the shell already has an Android project
|
|
35
|
+
* without the Crashlytics Gradle plugin: the Crashlytics SDK throws at startup
|
|
36
|
+
* without the build ID that plugin writes, so the plugin is never installed
|
|
37
|
+
* into such a project.
|
|
38
|
+
*/
|
|
39
|
+
export declare function planCrashReportingProvider(input: {
|
|
40
|
+
readonly gameRoot: string;
|
|
41
|
+
readonly shellPath: string;
|
|
42
|
+
readonly existingShellDependencies: Readonly<Record<string, unknown>> | undefined;
|
|
43
|
+
readonly warnings: string[];
|
|
44
|
+
}): PlannedCrashReporting;
|
|
45
|
+
/** Whether an Xcode project runs the Crashlytics dSYM upload script or `upload-symbols`. */
|
|
46
|
+
export declare function iosProjectUploadsCrashlyticsSymbols(project: string): boolean;
|
|
47
|
+
/** A warning for a shell that still lists the plugin although crash reporting is not requested. */
|
|
48
|
+
export declare function crashReportingRemovalWarning(existingShellDependencies: Readonly<Record<string, unknown>>): string | undefined;
|
|
49
|
+
/** Whether both Android Gradle files apply the Crashlytics Gradle plugin (comments ignored). */
|
|
50
|
+
export declare function androidShellHasCrashlytics(shellRoot: string): boolean;
|
|
51
|
+
export declare function androidRootGradleHasCrashlytics(source: string): boolean;
|
|
52
|
+
/**
|
|
53
|
+
* Whether the app module applies the Crashlytics Gradle plugin: `apply
|
|
54
|
+
* plugin:`, or a plugins-block `id` that is not declared `apply false` (that
|
|
55
|
+
* only puts the plugin on the classpath, as the root project does).
|
|
56
|
+
*/
|
|
57
|
+
export declare function androidAppGradleHasCrashlytics(source: string): boolean;
|
|
58
|
+
/**
|
|
59
|
+
* Add the Crashlytics Gradle plugin classpath next to the Google services
|
|
60
|
+
* classpath of the Capacitor template's root `build.gradle`. Returns the
|
|
61
|
+
* source unchanged when it is already there, and `undefined` for a layout the
|
|
62
|
+
* template does not produce.
|
|
63
|
+
*/
|
|
64
|
+
export declare function applyAndroidRootGradleCrashlytics(source: string): string | undefined;
|
|
65
|
+
/**
|
|
66
|
+
* Apply the Crashlytics Gradle plugin right after the Google services plugin
|
|
67
|
+
* in the Capacitor template's app `build.gradle`, inside its
|
|
68
|
+
* `google-services.json` check: without that file Firebase does not start, so
|
|
69
|
+
* the Crashlytics SDK and its build ID check stay off too. Returns the source
|
|
70
|
+
* unchanged when it is already applied, and `undefined` for another layout.
|
|
71
|
+
*/
|
|
72
|
+
export declare function applyAndroidAppGradleCrashlytics(source: string): string | undefined;
|
|
73
|
+
/**
|
|
74
|
+
* Wire the Crashlytics Gradle plugin into a freshly generated Android project.
|
|
75
|
+
* Both files are checked before either is written; a layout the template does
|
|
76
|
+
* not produce is refused with the manual steps.
|
|
77
|
+
*/
|
|
78
|
+
export declare function materializeAndroidCrashlytics(shellRoot: string): void;
|
|
79
|
+
/** Whether the shell's package.json installs the Crashlytics plugin (`--providers crash-reporting`). */
|
|
80
|
+
export declare function shellUsesCrashlytics(shellPackage: unknown): boolean;
|
|
81
|
+
/**
|
|
82
|
+
* Store-policy preflight for a shell that installs the Crashlytics plugin.
|
|
83
|
+
* Errors fail a production build (`reportNativeStorePolicy`); other profiles
|
|
84
|
+
* print them.
|
|
85
|
+
*
|
|
86
|
+
* - Android: `android/app/google-services.json` must be present (without it
|
|
87
|
+
* Firebase does not start and the release reports nothing), and the app
|
|
88
|
+
* module must apply the Crashlytics Gradle plugin (without its build ID the
|
|
89
|
+
* Crashlytics SDK stops the app at startup once Firebase starts).
|
|
90
|
+
* - iOS: the App target must bundle `GoogleService-Info.plist`; without it
|
|
91
|
+
* Firebase does not start and the plugin still calls Crashlytics.
|
|
92
|
+
*/
|
|
93
|
+
export declare function inspectCrashReportingShell(input: {
|
|
94
|
+
readonly platform: 'android' | 'ios';
|
|
95
|
+
readonly shellApp: string;
|
|
96
|
+
readonly shellPackage: unknown;
|
|
97
|
+
/** The App target's `project.pbxproj` source, for iOS. */
|
|
98
|
+
readonly iosProject?: string;
|
|
99
|
+
}): readonly NativeShellPolicyIssue[];
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
import { existsSync, lstatSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { satisfies, valid, validRange } from 'semver';
|
|
4
|
+
import { stripGradleComments } from './native-shell-identity.js';
|
|
5
|
+
import { iosAppBundlesResource } from './native-shell-store-policy.js';
|
|
6
|
+
// `mpgd target init capacitor --providers crash-reporting`: Firebase
|
|
7
|
+
// Crashlytics in the game-owned Capacitor shell. The shell gets the native
|
|
8
|
+
// plugin `@capacitor-firebase/crashlytics`, and a freshly generated Android
|
|
9
|
+
// project gets the Crashlytics Gradle plugin. The game itself is unchanged:
|
|
10
|
+
// `@mpgd/adapter-capacitor` reaches the native plugin by its registered name
|
|
11
|
+
// and exposes it as `gateway.crashReporter` (docs/CRASH_REPORTING.md).
|
|
12
|
+
export const crashReportingProviderId = 'crash-reporting';
|
|
13
|
+
/** The native plugin installed into the shell. */
|
|
14
|
+
export const crashReportingPlugin = '@capacitor-firebase/crashlytics';
|
|
15
|
+
/** The plugin version this CLI installs when the shell does not list one yet. */
|
|
16
|
+
export const crashReportingPluginVersion = '8.5.2';
|
|
17
|
+
/**
|
|
18
|
+
* The Crashlytics Gradle plugin, which writes the build ID the Crashlytics
|
|
19
|
+
* Android SDK requires at startup and uploads R8 mapping files. 3.0.8 needs
|
|
20
|
+
* AGP 8.1+, Gradle 8.0+, and the Google services plugin 4.4.1+
|
|
21
|
+
* (https://firebase.google.com/docs/crashlytics/android/get-started).
|
|
22
|
+
*/
|
|
23
|
+
export const crashlyticsGradlePluginVersion = '3.0.8';
|
|
24
|
+
const crashlyticsGradleClasspath = `com.google.firebase:firebase-crashlytics-gradle:${crashlyticsGradlePluginVersion}`;
|
|
25
|
+
const crashlyticsGradlePluginId = 'com.google.firebase.crashlytics';
|
|
26
|
+
const androidRootGradle = 'android/build.gradle';
|
|
27
|
+
const androidAppGradle = 'android/app/build.gradle';
|
|
28
|
+
/** The manual Android steps for a shell whose Gradle files the CLI does not rewrite. */
|
|
29
|
+
export const crashReportingAndroidSteps = Object.freeze([
|
|
30
|
+
`In android/build.gradle, add classpath '${crashlyticsGradleClasspath}' to buildscript.dependencies, next to the google-services classpath (4.4.1 or later).`,
|
|
31
|
+
`In android/app/build.gradle, add apply plugin: '${crashlyticsGradlePluginId}' right after apply plugin: 'com.google.gms.google-services', inside the same google-services.json check.`,
|
|
32
|
+
]);
|
|
33
|
+
/**
|
|
34
|
+
* The iOS steps the CLI leaves to the game: the plugin captures crashes once
|
|
35
|
+
* `GoogleService-Info.plist` is in the App target, but readable native stack
|
|
36
|
+
* traces need the dSYMs uploaded (docs/CRASH_REPORTING.md#symbols).
|
|
37
|
+
*/
|
|
38
|
+
export const crashReportingIosSteps = Object.freeze([
|
|
39
|
+
'Upload dSYMs for every App Store build: add the Crashlytics run script build phase '
|
|
40
|
+
+ '("${BUILD_DIR%/Build/*}/SourcePackages/checkouts/firebase-ios-sdk/Crashlytics/run") as the '
|
|
41
|
+
+ 'last phase of the App target, or run upload-symbols after each archive; see '
|
|
42
|
+
+ 'docs/CRASH_REPORTING.md in mpgd-kit.',
|
|
43
|
+
]);
|
|
44
|
+
/**
|
|
45
|
+
* Plan the crash-reporting provider before anything is written. Refused when
|
|
46
|
+
* the game's adapter has no crash reporter, when the plugin version is outside
|
|
47
|
+
* the adapter's peer range, or when the shell already has an Android project
|
|
48
|
+
* without the Crashlytics Gradle plugin: the Crashlytics SDK throws at startup
|
|
49
|
+
* without the build ID that plugin writes, so the plugin is never installed
|
|
50
|
+
* into such a project.
|
|
51
|
+
*/
|
|
52
|
+
export function planCrashReportingProvider(input) {
|
|
53
|
+
const adapterFile = path.join(input.gameRoot, 'node_modules/@mpgd/adapter-capacitor/package.json');
|
|
54
|
+
const adapter = JSON.parse(readFileSync(adapterFile, 'utf8'));
|
|
55
|
+
const adapterExports = adapter.exports;
|
|
56
|
+
if (typeof adapterExports !== 'object' || adapterExports === null
|
|
57
|
+
|| adapterExports['./crash-reporting'] === undefined) {
|
|
58
|
+
throw new Error(`--providers ${crashReportingProviderId} needs the crash reporter of @mpgd/adapter-capacitor, `
|
|
59
|
+
+ `which the game's ${String(adapter.version)} does not have; upgrade the game's kit `
|
|
60
|
+
+ 'packages (mpgd kit upgrade) first.');
|
|
61
|
+
}
|
|
62
|
+
const listed = input.existingShellDependencies?.[crashReportingPlugin];
|
|
63
|
+
if (listed !== undefined && (typeof listed !== 'string' || valid(listed) === null)) {
|
|
64
|
+
throw new Error(`The shell lists ${crashReportingPlugin} as ${String(listed)}; pin an exact version before rerunning.`);
|
|
65
|
+
}
|
|
66
|
+
const version = typeof listed === 'string' ? listed : crashReportingPluginVersion;
|
|
67
|
+
const peers = typeof adapter.peerDependencies === 'object' && adapter.peerDependencies !== null
|
|
68
|
+
? adapter.peerDependencies
|
|
69
|
+
: {};
|
|
70
|
+
const range = peers[crashReportingPlugin];
|
|
71
|
+
if (typeof range === 'string' && validRange(range) !== null
|
|
72
|
+
&& !satisfies(version, range, { includePrerelease: true })) {
|
|
73
|
+
throw new Error(`@mpgd/adapter-capacitor ${String(adapter.version)} needs ${crashReportingPlugin} ${range}, `
|
|
74
|
+
+ `but the shell would get ${version}; align the shell's version before rerunning.`);
|
|
75
|
+
}
|
|
76
|
+
const shellRoot = path.join(input.gameRoot, input.shellPath);
|
|
77
|
+
if (existsSync(path.join(shellRoot, 'android'))
|
|
78
|
+
&& !androidShellHasCrashlytics(shellRoot)) {
|
|
79
|
+
throw new Error(`--providers ${crashReportingProviderId} installs Firebase Crashlytics, whose Android SDK `
|
|
80
|
+
+ 'stops the app at startup unless the Crashlytics Gradle plugin is applied. The existing '
|
|
81
|
+
+ 'Android project is game-owned and is not rewritten; first:\n'
|
|
82
|
+
+ crashReportingAndroidSteps.map((step) => `- ${step}`).join('\n')
|
|
83
|
+
+ '\nthen rerun.');
|
|
84
|
+
}
|
|
85
|
+
// Without the Firebase files Firebase does not start. Android then reports
|
|
86
|
+
// not-configured; the iOS plugin only logs it and still calls Crashlytics,
|
|
87
|
+
// so the plist must ship whenever the plugin does.
|
|
88
|
+
for (const [file, platform] of [
|
|
89
|
+
['android/app/google-services.json', 'Android'],
|
|
90
|
+
['ios/App/App/GoogleService-Info.plist', 'iOS'],
|
|
91
|
+
]) {
|
|
92
|
+
if (!existsSync(path.join(shellRoot, file))) {
|
|
93
|
+
input.warnings.push(`${crashReportingProviderId}: ${platform} crash reports need ${input.shellPath}/${file} from the `
|
|
94
|
+
+ 'Firebase console (the same file notifications use); add it before a release build.');
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
const iosProject = path.join(shellRoot, 'ios/App/App.xcodeproj/project.pbxproj');
|
|
98
|
+
if (!existsSync(iosProject) || !iosProjectUploadsCrashlyticsSymbols(readFileSync(iosProject, 'utf8'))) {
|
|
99
|
+
input.warnings.push(`${crashReportingProviderId}: on iOS, ${crashReportingIosSteps.join(' ')}`);
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
shellPlugins: [{ packageName: crashReportingPlugin, version }],
|
|
103
|
+
manifestEntry: {
|
|
104
|
+
packages: { [crashReportingPlugin]: version },
|
|
105
|
+
platforms: ['android', 'ios'],
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
/** Whether an Xcode project runs the Crashlytics dSYM upload script or `upload-symbols`. */
|
|
110
|
+
export function iosProjectUploadsCrashlyticsSymbols(project) {
|
|
111
|
+
return /Crashlytics\/run\b|\bupload-symbols\b/u.test(project);
|
|
112
|
+
}
|
|
113
|
+
/** A warning for a shell that still lists the plugin although crash reporting is not requested. */
|
|
114
|
+
export function crashReportingRemovalWarning(existingShellDependencies) {
|
|
115
|
+
return existingShellDependencies[crashReportingPlugin] === undefined
|
|
116
|
+
? undefined
|
|
117
|
+
: `The shell still lists ${crashReportingPlugin} although ${crashReportingProviderId} is no `
|
|
118
|
+
+ 'longer requested; the initializer never removes a plugin, so remove it from the shell '
|
|
119
|
+
+ 'package (and the Crashlytics Gradle plugin) and resync if the game stopped reporting crashes.';
|
|
120
|
+
}
|
|
121
|
+
/** Whether both Android Gradle files apply the Crashlytics Gradle plugin (comments ignored). */
|
|
122
|
+
export function androidShellHasCrashlytics(shellRoot) {
|
|
123
|
+
const read = (relative) => {
|
|
124
|
+
const file = path.join(shellRoot, relative);
|
|
125
|
+
return existsSync(file) && lstatSync(file).isFile() ? readFileSync(file, 'utf8') : undefined;
|
|
126
|
+
};
|
|
127
|
+
const root = read(androidRootGradle);
|
|
128
|
+
const app = read(androidAppGradle);
|
|
129
|
+
return root !== undefined && app !== undefined
|
|
130
|
+
&& androidRootGradleHasCrashlytics(root) && androidAppGradleHasCrashlytics(app);
|
|
131
|
+
}
|
|
132
|
+
export function androidRootGradleHasCrashlytics(source) {
|
|
133
|
+
return /['"]com\.google\.firebase:firebase-crashlytics-gradle:[^'"]+['"]|\bid\s*\(?\s*['"]com\.google\.firebase\.crashlytics['"]/u
|
|
134
|
+
.test(stripGradleComments(source));
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Whether the app module applies the Crashlytics Gradle plugin: `apply
|
|
138
|
+
* plugin:`, or a plugins-block `id` that is not declared `apply false` (that
|
|
139
|
+
* only puts the plugin on the classpath, as the root project does).
|
|
140
|
+
*/
|
|
141
|
+
export function androidAppGradleHasCrashlytics(source) {
|
|
142
|
+
const clean = stripGradleComments(source);
|
|
143
|
+
if (/\bapply\s+plugin\s*:\s*['"]com\.google\.firebase\.crashlytics['"]/u.test(clean)) {
|
|
144
|
+
return true;
|
|
145
|
+
}
|
|
146
|
+
return clean.split('\n').some((line) => /\bid\s*\(?\s*['"]com\.google\.firebase\.crashlytics['"]/u.test(line)
|
|
147
|
+
&& !/\bapply\s*\(?\s*false\b/u.test(line));
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Add the Crashlytics Gradle plugin classpath next to the Google services
|
|
151
|
+
* classpath of the Capacitor template's root `build.gradle`. Returns the
|
|
152
|
+
* source unchanged when it is already there, and `undefined` for a layout the
|
|
153
|
+
* template does not produce.
|
|
154
|
+
*/
|
|
155
|
+
export function applyAndroidRootGradleCrashlytics(source) {
|
|
156
|
+
if (androidRootGradleHasCrashlytics(source)) {
|
|
157
|
+
return source;
|
|
158
|
+
}
|
|
159
|
+
const anchor = /^([ \t]*)classpath[ \t]+(['"])com\.google\.gms:google-services:[^'"\n]+\2[ \t]*$/mu.exec(source);
|
|
160
|
+
if (anchor === null) {
|
|
161
|
+
return undefined;
|
|
162
|
+
}
|
|
163
|
+
const end = anchor.index + anchor[0].length;
|
|
164
|
+
const newline = source.includes('\r\n') ? '\r\n' : '\n';
|
|
165
|
+
return `${source.slice(0, end)}${newline}${anchor[1] ?? ''}classpath '${crashlyticsGradleClasspath}'${source.slice(end)}`;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Apply the Crashlytics Gradle plugin right after the Google services plugin
|
|
169
|
+
* in the Capacitor template's app `build.gradle`, inside its
|
|
170
|
+
* `google-services.json` check: without that file Firebase does not start, so
|
|
171
|
+
* the Crashlytics SDK and its build ID check stay off too. Returns the source
|
|
172
|
+
* unchanged when it is already applied, and `undefined` for another layout.
|
|
173
|
+
*/
|
|
174
|
+
export function applyAndroidAppGradleCrashlytics(source) {
|
|
175
|
+
if (androidAppGradleHasCrashlytics(source)) {
|
|
176
|
+
return source;
|
|
177
|
+
}
|
|
178
|
+
const anchor = /^([ \t]*)apply[ \t]+plugin[ \t]*:[ \t]*(['"])com\.google\.gms\.google-services\2[ \t]*$/mu
|
|
179
|
+
.exec(source);
|
|
180
|
+
if (anchor === null) {
|
|
181
|
+
return undefined;
|
|
182
|
+
}
|
|
183
|
+
const end = anchor.index + anchor[0].length;
|
|
184
|
+
const newline = source.includes('\r\n') ? '\r\n' : '\n';
|
|
185
|
+
return `${source.slice(0, end)}${newline}${anchor[1] ?? ''}apply plugin: '${crashlyticsGradlePluginId}'${source.slice(end)}`;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Wire the Crashlytics Gradle plugin into a freshly generated Android project.
|
|
189
|
+
* Both files are checked before either is written; a layout the template does
|
|
190
|
+
* not produce is refused with the manual steps.
|
|
191
|
+
*/
|
|
192
|
+
export function materializeAndroidCrashlytics(shellRoot) {
|
|
193
|
+
const rootFile = path.join(shellRoot, androidRootGradle);
|
|
194
|
+
const appFile = path.join(shellRoot, androidAppGradle);
|
|
195
|
+
for (const file of [rootFile, appFile]) {
|
|
196
|
+
if (!existsSync(file) || !lstatSync(file).isFile()) {
|
|
197
|
+
throw new Error(`The generated Android project has no ${path.relative(shellRoot, file)}.`);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
const rootSource = readFileSync(rootFile, 'utf8');
|
|
201
|
+
const appSource = readFileSync(appFile, 'utf8');
|
|
202
|
+
const nextRoot = applyAndroidRootGradleCrashlytics(rootSource);
|
|
203
|
+
const nextApp = applyAndroidAppGradleCrashlytics(appSource);
|
|
204
|
+
if (nextRoot === undefined || nextApp === undefined) {
|
|
205
|
+
throw new Error('The generated Android project has an unexpected Gradle layout; apply the Crashlytics '
|
|
206
|
+
+ 'Gradle plugin yourself:\n'
|
|
207
|
+
+ crashReportingAndroidSteps.map((step) => `- ${step}`).join('\n'));
|
|
208
|
+
}
|
|
209
|
+
if (nextRoot !== rootSource) {
|
|
210
|
+
writeFileSync(rootFile, nextRoot);
|
|
211
|
+
}
|
|
212
|
+
if (nextApp !== appSource) {
|
|
213
|
+
writeFileSync(appFile, nextApp);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
/** Whether the shell's package.json installs the Crashlytics plugin (`--providers crash-reporting`). */
|
|
217
|
+
export function shellUsesCrashlytics(shellPackage) {
|
|
218
|
+
if (typeof shellPackage !== 'object' || shellPackage === null) {
|
|
219
|
+
return false;
|
|
220
|
+
}
|
|
221
|
+
const { dependencies, devDependencies } = shellPackage;
|
|
222
|
+
return [dependencies, devDependencies].some((entries) => typeof entries === 'object' && entries !== null
|
|
223
|
+
&& entries[crashReportingPlugin] !== undefined);
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Store-policy preflight for a shell that installs the Crashlytics plugin.
|
|
227
|
+
* Errors fail a production build (`reportNativeStorePolicy`); other profiles
|
|
228
|
+
* print them.
|
|
229
|
+
*
|
|
230
|
+
* - Android: `android/app/google-services.json` must be present (without it
|
|
231
|
+
* Firebase does not start and the release reports nothing), and the app
|
|
232
|
+
* module must apply the Crashlytics Gradle plugin (without its build ID the
|
|
233
|
+
* Crashlytics SDK stops the app at startup once Firebase starts).
|
|
234
|
+
* - iOS: the App target must bundle `GoogleService-Info.plist`; without it
|
|
235
|
+
* Firebase does not start and the plugin still calls Crashlytics.
|
|
236
|
+
*/
|
|
237
|
+
export function inspectCrashReportingShell(input) {
|
|
238
|
+
if (!shellUsesCrashlytics(input.shellPackage)) {
|
|
239
|
+
return [];
|
|
240
|
+
}
|
|
241
|
+
const issues = [];
|
|
242
|
+
if (input.platform === 'android') {
|
|
243
|
+
const config = path.join(input.shellApp, 'android/app/google-services.json');
|
|
244
|
+
if (!existsSync(config) || !lstatSync(config).isFile() || readFileSync(config, 'utf8').trim() === '') {
|
|
245
|
+
issues.push({
|
|
246
|
+
level: 'error',
|
|
247
|
+
message: `The shell installs ${crashReportingPlugin}, but android/app/google-services.json is `
|
|
248
|
+
+ 'missing; Firebase does not start and the release reports no crashes. Download it from the '
|
|
249
|
+
+ 'Firebase console (the same file notifications use).',
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
if (!androidShellHasCrashlytics(input.shellApp)) {
|
|
253
|
+
issues.push({
|
|
254
|
+
level: 'error',
|
|
255
|
+
message: `The shell installs ${crashReportingPlugin}, but the Android project does not apply the `
|
|
256
|
+
+ 'Crashlytics Gradle plugin; the Crashlytics SDK stops the app at startup without the build ID '
|
|
257
|
+
+ `it writes. ${crashReportingAndroidSteps.join(' ')}`,
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
return issues;
|
|
261
|
+
}
|
|
262
|
+
if (input.iosProject !== undefined && !iosAppBundlesResource(input.iosProject, 'GoogleService-Info.plist')) {
|
|
263
|
+
issues.push({
|
|
264
|
+
level: 'error',
|
|
265
|
+
message: `The shell installs ${crashReportingPlugin}, but the iOS App target does not bundle `
|
|
266
|
+
+ 'GoogleService-Info.plist; Firebase does not start and the plugin still calls Crashlytics. '
|
|
267
|
+
+ 'Download it from the Firebase console and add it to the App target in Xcode.',
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
return issues;
|
|
271
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -6,8 +6,11 @@ export { installPinnedReleaseDependencies, pinNativeDeploymentPlan, preparePinne
|
|
|
6
6
|
export { withAndroidUploadSigningSession, type AndroidUploadSigningInput, } from './android-signing-session.js';
|
|
7
7
|
export { checkpointNativeSubmission, readNativeReleaseStatus, reclaimNativeSubmission, releaseNativeSubmissionLease, recordNativeReleaseBuild, reserveNativeRelease, type ImmutableNativeBuildRecord, type NativeReleaseStatus, type NativeReleaseReservation, type NativeReleaseReservationInput, type NativeSubmissionCheckpoint, type NativeSubmissionStatus, type RecordNativeBuildInput, } from './release-state.js';
|
|
8
8
|
export { runNativeDeployment, readStoreCredential, type RunNativeDeploymentInput, } from './native-deploy-run.js';
|
|
9
|
-
export { submitRecordedNativeTarget, type NativeStoreCredential, type SubmitRecordedNativeTargetInput, } from './native-deploy-submission.js';
|
|
10
|
-
export { PlaySubmissionUncertainError, submitVerifiedAndroidBundle, type PlayInternalSubmissionInput, type PlayInternalSubmissionResult, } from './play-internal-submission.js';
|
|
9
|
+
export { planRecordedAndroidSubmission, submitRecordedNativeTarget, type NativeStoreCredential, type SubmitRecordedNativeTargetInput, } from './native-deploy-submission.js';
|
|
10
|
+
export { PlaySubmissionUncertainError, planVerifiedAndroidBundleSubmission, submitVerifiedAndroidBundle, type PlayInternalSubmissionInput, type PlayInternalSubmissionResult, type PlaySubmissionPlan, } from './play-internal-submission.js';
|
|
11
|
+
export { createPlayReleaseRequest, defaultPlayReleaseRequest, maximumPlayReleaseNoteCharacters, parsePlayReleaseNotes, type CreatePlayReleaseRequestInput, type PlayNewReleaseStatus, type PlayReleaseNote, type PlayReleaseRequest, type PlayReleaseStatus, } from './play-release-options.js';
|
|
12
|
+
export { PlayPromoteUncertainError, promotePlayRelease, type PlayPromoteInput, type PlayPromoteResult, } from './play-release-promote.js';
|
|
13
|
+
export { PlayRolloutUncertainError, changePlayRollout, type PlayRolloutAction, type PlayRolloutInput, type PlayRolloutResult, } from './play-release-rollout.js';
|
|
11
14
|
export { IosSubmissionUncertainError, submitVerifiedIosBuild, type IosTestFlightStatus, type IosTestFlightSubmissionInput, type IosTestFlightSubmissionResult, } from './testflight-submission.js';
|
|
12
15
|
export { createBrowserGameplayE2EDriver, type BrowserGameplayE2EInspectInput, type BrowserGameplayE2EPage, type BrowserGameplayE2EScreenshotOptions, type BrowserGameplayE2EViewport, type CreateBrowserGameplayE2EDriverInput, } from './browser-gameplay-e2e.js';
|
|
13
16
|
export { collectGameplayE2EPathEvidence, defaultGameplayE2EReportFile, maximumGameplayE2EStates, parseGameplayE2EPlan, readGameplayE2EPlan, renderGameplayE2EMarkdown, resolveGameplayE2EReportFile, runGameplayE2E, type GameplayE2EAction, type GameplayE2EActionResult, type GameplayE2EDriver, type GameplayE2EHashLimits, type GameplayE2EInputAction, type GameplayE2EObservation, type GameplayE2EPauseResumeAction, type GameplayE2EPathEvidence, type GameplayE2EPlan, type GameplayE2EReport, type GameplayE2EResultStatus, type GameplayE2EState, type GameplayE2EStateResult, type GameplayE2EStatus, type RunGameplayE2EInput, type RunGameplayE2EResult, } from './gameplay-e2e.js';
|