@mpgd/cli 0.12.0 → 0.14.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.
Files changed (27) hide show
  1. package/dist/index.d.ts +3 -0
  2. package/dist/index.js +363 -3
  3. package/dist/microsoft-store-package-acceptance.d.ts +54 -0
  4. package/dist/microsoft-store-package-acceptance.js +787 -0
  5. package/dist/microsoft-store-package-generation-contract.d.ts +129 -0
  6. package/dist/microsoft-store-package-generation-contract.js +3 -0
  7. package/dist/microsoft-store-package-generation-download.d.ts +22 -0
  8. package/dist/microsoft-store-package-generation-download.js +453 -0
  9. package/dist/microsoft-store-package-generation-input.d.ts +3 -0
  10. package/dist/microsoft-store-package-generation-input.js +384 -0
  11. package/dist/microsoft-store-package-generation-integrity.d.ts +5 -0
  12. package/dist/microsoft-store-package-generation-integrity.js +100 -0
  13. package/dist/microsoft-store-package-generation-manifest.d.ts +3 -0
  14. package/dist/microsoft-store-package-generation-manifest.js +67 -0
  15. package/dist/microsoft-store-package-generation-zip.d.ts +1 -0
  16. package/dist/microsoft-store-package-generation-zip.js +180 -0
  17. package/dist/microsoft-store-package-generation.d.ts +24 -0
  18. package/dist/microsoft-store-package-generation.js +430 -0
  19. package/dist/microsoft-store-submission.d.ts +79 -0
  20. package/dist/microsoft-store-submission.js +952 -0
  21. package/dist/microsoft-store-supported-locales.d.ts +1 -0
  22. package/dist/microsoft-store-supported-locales.js +33 -0
  23. package/package.json +10 -9
  24. package/templates/phaser-game/README.md +38 -0
  25. package/templates/phaser-game/gitignore +1 -0
  26. package/templates/phaser-game/mpgd.microsoft-store.json +29 -0
  27. package/templates/phaser-game/package.json +2 -0
package/dist/index.d.ts CHANGED
@@ -2,6 +2,9 @@ export { renderGameAcceptanceMarkdown, resolveGameAcceptanceReleaseManifestFile,
2
2
  export { createBrowserGameplayE2EDriver, type BrowserGameplayE2EInspectInput, type BrowserGameplayE2EPage, type BrowserGameplayE2EScreenshotOptions, type BrowserGameplayE2EViewport, type CreateBrowserGameplayE2EDriverInput, } from './browser-gameplay-e2e.js';
3
3
  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';
4
4
  export { assertMicrosoftStorePwaCacheTransition, assertMicrosoftStorePwaPrecacheUrl, assertMicrosoftStorePwaReleaseEvidence, inspectMicrosoftStorePwaBrowserCacheTransition, microsoftStorePwaCacheSchema, readMicrosoftStorePwaBrowserReleaseEvidence, requestMicrosoftStorePwaBrowserUpdate, resolveMicrosoftStorePwaScopedCacheName, type AssertMicrosoftStorePwaCacheTransitionInput, type InspectMicrosoftStorePwaCacheTransitionInput, type MicrosoftStorePwaBrowserPage, type MicrosoftStorePwaCachedIndexObservation, type MicrosoftStorePwaCacheTransitionEvidence, type MicrosoftStorePwaCacheTransitionObservation, type MicrosoftStorePwaReleaseEvidence, } from './microsoft-store-pwa-e2e.js';
5
+ export { createMicrosoftStorePackageAcceptanceRuntime, microsoftStorePackageAcceptanceSchemaVersion, parseMicrosoftStorePackageIdentity, parseWindowsAppCertificationResult, renderMicrosoftStorePackageAcceptanceMarkdown, runMicrosoftStorePackageAcceptance, type CreateMicrosoftStorePackageAcceptanceRuntimeInput, type MicrosoftStorePackageAcceptanceEvidence, type MicrosoftStorePackageAcceptanceRuntime, type MicrosoftStorePackageIdentity, type RunMicrosoftStorePackageAcceptanceInput, } from './microsoft-store-package-acceptance.js';
6
+ export { createMicrosoftStorePackageGenerationRuntime, microsoftStorePackageGeneratorEndpoint, microsoftStorePackageGeneratorSourceRevision, microsoftStorePackageGenerationSchemaVersion, renderMicrosoftStorePackageGenerationMarkdown, runMicrosoftStorePackageGeneration, type CreateMicrosoftStorePackageGenerationRuntimeInput, type MicrosoftStoreAddressResolver, type MicrosoftStorePackageGenerationEvidence, type MicrosoftStorePackageGenerationRuntime, type MicrosoftStoreResolvedAddress, type RunMicrosoftStorePackageGenerationInput, } from './microsoft-store-package-generation.js';
7
+ export { microsoftStoreSubmissionSchemaVersion, parseMicrosoftStoreSubmissionConfig, renderMicrosoftStoreSubmissionMarkdown, runMicrosoftStoreSubmissionPreflight, type MicrosoftStoreSubmissionConfig, type MicrosoftStoreSubmissionEvidence, type RunMicrosoftStoreSubmissionPreflightInput, } from './microsoft-store-submission.js';
5
8
  export { assertProductionTargetReadiness, type ProductionTargetReadinessInput, } from './production-target-readiness.js';
6
9
  export declare function runMpgdCli(args: readonly string[]): Promise<void>;
7
10
  export declare function runCreateGameCli(args: readonly string[]): Promise<void>;
package/dist/index.js CHANGED
@@ -8,10 +8,16 @@ import resources from '@gunshi/resources';
8
8
  import { cli } from 'gunshi';
9
9
  import { defaultGameAcceptanceCommandTimeoutMs, resolveGameAcceptanceReleaseManifestFile, runGameAcceptance, } from './game-acceptance.js';
10
10
  import { resolveGameplayE2EReportFile } from './gameplay-e2e.js';
11
+ import { createMicrosoftStorePackageAcceptanceRuntime, runMicrosoftStorePackageAcceptance, } from './microsoft-store-package-acceptance.js';
12
+ import { runMicrosoftStorePackageGeneration } from './microsoft-store-package-generation.js';
13
+ import { runMicrosoftStoreSubmissionPreflight } from './microsoft-store-submission.js';
11
14
  export { renderGameAcceptanceMarkdown, resolveGameAcceptanceReleaseManifestFile, runGameAcceptance, defaultGameAcceptanceCommandTimeoutMs, maximumGameplayE2EReportBytes, } from './game-acceptance.js';
12
15
  export { createBrowserGameplayE2EDriver, } from './browser-gameplay-e2e.js';
13
16
  export { collectGameplayE2EPathEvidence, defaultGameplayE2EReportFile, maximumGameplayE2EStates, parseGameplayE2EPlan, readGameplayE2EPlan, renderGameplayE2EMarkdown, resolveGameplayE2EReportFile, runGameplayE2E, } from './gameplay-e2e.js';
14
17
  export { assertMicrosoftStorePwaCacheTransition, assertMicrosoftStorePwaPrecacheUrl, assertMicrosoftStorePwaReleaseEvidence, inspectMicrosoftStorePwaBrowserCacheTransition, microsoftStorePwaCacheSchema, readMicrosoftStorePwaBrowserReleaseEvidence, requestMicrosoftStorePwaBrowserUpdate, resolveMicrosoftStorePwaScopedCacheName, } from './microsoft-store-pwa-e2e.js';
18
+ export { createMicrosoftStorePackageAcceptanceRuntime, microsoftStorePackageAcceptanceSchemaVersion, parseMicrosoftStorePackageIdentity, parseWindowsAppCertificationResult, renderMicrosoftStorePackageAcceptanceMarkdown, runMicrosoftStorePackageAcceptance, } from './microsoft-store-package-acceptance.js';
19
+ export { createMicrosoftStorePackageGenerationRuntime, microsoftStorePackageGeneratorEndpoint, microsoftStorePackageGeneratorSourceRevision, microsoftStorePackageGenerationSchemaVersion, renderMicrosoftStorePackageGenerationMarkdown, runMicrosoftStorePackageGeneration, } from './microsoft-store-package-generation.js';
20
+ export { microsoftStoreSubmissionSchemaVersion, parseMicrosoftStoreSubmissionConfig, renderMicrosoftStoreSubmissionMarkdown, runMicrosoftStoreSubmissionPreflight, } from './microsoft-store-submission.js';
15
21
  export { assertProductionTargetReadiness, } from './production-target-readiness.js';
16
22
  const sourceDir = path.dirname(fileURLToPath(import.meta.url));
17
23
  const packageRoot = resolvePackageRoot(sourceDir);
@@ -727,10 +733,10 @@ const legalCommand = defineI18n({
727
733
  });
728
734
  const targetCommand = defineI18n({
729
735
  name: 'target',
730
- description: 'Build and smoke target artifacts with generated game target config.',
736
+ description: 'Build, smoke, generate, preflight, and accept target release artifacts.',
731
737
  resource: commandResource({
732
- en: 'Build and smoke target artifacts with generated game target config.',
733
- ko: '게임 타깃 설정으로 타깃 산출물을 빌드하고 스모크 검증합니다.',
738
+ en: 'Build, smoke, generate, preflight, and accept target release artifacts.',
739
+ ko: '타깃 출시 산출물을 빌드하고 스모크, 패키지 생성, 제출 사전 검증 및 인수 검증을 수행합니다.',
734
740
  }),
735
741
  subCommands: {
736
742
  build: defineI18n({
@@ -833,6 +839,319 @@ const targetCommand = defineI18n({
833
839
  });
834
840
  },
835
841
  }),
842
+ 'generate-package': defineI18n({
843
+ name: 'generate-package',
844
+ description: 'Generate a packaged target artifact with an external platform service.',
845
+ resource: commandResource({
846
+ en: 'Generate a packaged target artifact with an external platform service.',
847
+ ko: '외부 플랫폼 서비스로 패키징된 타깃 산출물을 생성합니다.',
848
+ }, {
849
+ target: {
850
+ en: 'Package target. Currently supports microsoft-store.',
851
+ ko: '패키지 타깃. 현재 microsoft-store를 지원합니다.',
852
+ },
853
+ 'pwa-url': {
854
+ en: 'Public HTTPS URL of the deployed PWA.',
855
+ ko: '배포된 PWA의 공개 HTTPS URL.',
856
+ },
857
+ 'manifest-url': {
858
+ en: 'Public HTTPS URL of the deployed web app manifest.',
859
+ ko: '배포된 웹 앱 매니페스트의 공개 HTTPS URL.',
860
+ },
861
+ 'package-version': {
862
+ en: 'Modern Microsoft Store package version.',
863
+ ko: '최신 Microsoft Store 패키지 버전.',
864
+ },
865
+ 'classic-version': {
866
+ en: 'Lower classic Microsoft Store package version.',
867
+ ko: '최신 버전보다 낮은 클래식 Microsoft Store 패키지 버전.',
868
+ },
869
+ 'submission-evidence': {
870
+ en: 'Submission preflight evidence file.',
871
+ ko: '제출 사전 검증 증적 파일.',
872
+ },
873
+ 'output-file': {
874
+ en: 'New game-owned ZIP file for the package generator response.',
875
+ ko: '패키지 생성기 응답을 기록할 새 게임 소유 ZIP 파일.',
876
+ },
877
+ 'output-dir': {
878
+ en: 'Directory for package generation evidence.',
879
+ ko: '패키지 생성 증적 디렉터리.',
880
+ },
881
+ 'targets-file': {
882
+ en: 'Game target config file.',
883
+ ko: '게임 타깃 설정 파일.',
884
+ },
885
+ }),
886
+ args: {
887
+ target: {
888
+ type: 'positional',
889
+ required: true,
890
+ description: 'Package target. Currently supports microsoft-store.',
891
+ },
892
+ 'pwa-url': {
893
+ type: 'string',
894
+ required: true,
895
+ description: 'Public HTTPS URL of the deployed PWA.',
896
+ },
897
+ 'manifest-url': {
898
+ type: 'string',
899
+ required: true,
900
+ description: 'Public HTTPS URL of the deployed web app manifest.',
901
+ },
902
+ 'package-version': {
903
+ type: 'string',
904
+ required: true,
905
+ description: 'Modern Microsoft Store package version.',
906
+ },
907
+ 'classic-version': {
908
+ type: 'string',
909
+ required: true,
910
+ description: 'Lower classic Microsoft Store package version.',
911
+ },
912
+ 'submission-evidence': {
913
+ type: 'string',
914
+ required: false,
915
+ default: 'release-output/microsoft-store/submission-preflight.json',
916
+ description: 'Submission preflight evidence file.',
917
+ },
918
+ 'output-file': {
919
+ type: 'string',
920
+ required: false,
921
+ default: 'release-input/microsoft-store/pwabuilder-package.zip',
922
+ description: 'New game-owned ZIP file for the package generator response.',
923
+ },
924
+ 'output-dir': {
925
+ type: 'string',
926
+ required: false,
927
+ default: 'release-output/microsoft-store',
928
+ description: 'Directory for package generation evidence.',
929
+ },
930
+ 'targets-file': {
931
+ type: 'string',
932
+ required: false,
933
+ default: 'mpgd.targets.json',
934
+ description: 'Game target config file.',
935
+ },
936
+ },
937
+ run: async (ctx) => {
938
+ const positionals = readLocalPositionals(ctx.positionals, ['target', 'generate-package']);
939
+ const target = normalizeBuildTarget(readRequiredPositional(positionals, 0, 'target'));
940
+ if (target !== 'microsoft-store') {
941
+ throw new Error(`Package generation is not available for target: ${target}`);
942
+ }
943
+ const targetsFile = path.resolve(readOptionalString(ctx.values['targets-file']) ?? 'mpgd.targets.json');
944
+ const gameRoot = path.dirname(targetsFile);
945
+ const outputDir = resolveGameRelativePath(gameRoot, readOptionalString(ctx.values['output-dir']) ?? 'release-output/microsoft-store');
946
+ assertGameOwnedOutputDirectory(gameRoot, outputDir);
947
+ resolveMicrosoftStoreArtifactRoot(targetsFile);
948
+ const evidence = await runMicrosoftStorePackageGeneration({
949
+ gameRoot,
950
+ submissionEvidenceFile: resolveGameRelativePath(gameRoot, readOptionalString(ctx.values['submission-evidence'])
951
+ ?? 'release-output/microsoft-store/submission-preflight.json'),
952
+ pwaUrl: readOptionalString(ctx.values['pwa-url']) ?? '',
953
+ manifestUrl: readOptionalString(ctx.values['manifest-url']) ?? '',
954
+ modernVersion: readOptionalString(ctx.values['package-version']) ?? '',
955
+ classicVersion: readOptionalString(ctx.values['classic-version']) ?? '',
956
+ outputFile: resolveGameRelativePath(gameRoot, readOptionalString(ctx.values['output-file'])
957
+ ?? 'release-input/microsoft-store/pwabuilder-package.zip'),
958
+ jsonFile: path.join(outputDir, 'package-generation.json'),
959
+ markdownFile: path.join(outputDir, 'package-generation.md'),
960
+ });
961
+ },
962
+ }),
963
+ preflight: defineI18n({
964
+ name: 'preflight',
965
+ description: 'Validate game-owned target submission inputs and write release evidence.',
966
+ resource: commandResource({
967
+ en: 'Validate game-owned target submission inputs and write release evidence.',
968
+ ko: '게임 소유 타깃 제출 입력을 검증하고 출시 증적을 작성합니다.',
969
+ }, {
970
+ target: {
971
+ en: 'Submission target. Currently supports microsoft-store.',
972
+ ko: '제출 타깃. 현재 microsoft-store를 지원합니다.',
973
+ },
974
+ 'targets-file': {
975
+ en: 'Game target config file.',
976
+ ko: '게임 타깃 설정 파일.',
977
+ },
978
+ 'kit-path': {
979
+ en: 'Path to the mpgd-kit checkout used to resolve target config tokens.',
980
+ ko: '타깃 설정 토큰 해석에 사용할 mpgd-kit 체크아웃 경로.',
981
+ },
982
+ 'resolved-targets-file': {
983
+ en: 'Output path for the resolved target config file.',
984
+ ko: '해석된 타깃 설정 파일의 출력 경로.',
985
+ },
986
+ 'submission-file': {
987
+ en: 'Game-owned Microsoft Store submission config file.',
988
+ ko: '게임 소유 Microsoft Store 제출 설정 파일.',
989
+ },
990
+ 'output-dir': {
991
+ en: 'Directory for deterministic submission evidence.',
992
+ ko: '결정적 제출 증적을 기록할 디렉터리.',
993
+ },
994
+ }),
995
+ args: {
996
+ target: {
997
+ type: 'positional',
998
+ required: true,
999
+ description: 'Submission target. Currently supports microsoft-store.',
1000
+ },
1001
+ ...targetConfigArgs(),
1002
+ 'submission-file': {
1003
+ type: 'string',
1004
+ required: false,
1005
+ default: 'mpgd.microsoft-store.json',
1006
+ description: 'Game-owned Microsoft Store submission config file.',
1007
+ },
1008
+ 'output-dir': {
1009
+ type: 'string',
1010
+ required: false,
1011
+ default: 'release-output/microsoft-store',
1012
+ description: 'Directory for deterministic submission evidence.',
1013
+ },
1014
+ },
1015
+ run: (ctx) => {
1016
+ const positionals = readLocalPositionals(ctx.positionals, ['target', 'preflight']);
1017
+ const target = normalizeBuildTarget(readRequiredPositional(positionals, 0, 'target'));
1018
+ if (target !== 'microsoft-store') {
1019
+ throw new Error(`Submission preflight is not available for target: ${target}`);
1020
+ }
1021
+ const targetsFile = path.resolve(readOptionalString(ctx.values['targets-file']) ?? 'mpgd.targets.json');
1022
+ const gameRoot = path.dirname(targetsFile);
1023
+ const commandEnv = createTargetCommandEnv(ctx.values);
1024
+ const preparedTargetsFile = commandEnv.MPGD_PLATFORM_TARGETS_FILE;
1025
+ if (preparedTargetsFile === undefined || preparedTargetsFile.length === 0) {
1026
+ throw new Error('Failed to prepare the game target config for submission preflight.');
1027
+ }
1028
+ const configFile = resolveGameRelativePath(gameRoot, readOptionalString(ctx.values['submission-file']) ?? 'mpgd.microsoft-store.json');
1029
+ const outputDir = resolveGameRelativePath(gameRoot, readOptionalString(ctx.values['output-dir']) ?? 'release-output/microsoft-store');
1030
+ assertGameOwnedOutputDirectory(gameRoot, outputDir);
1031
+ mkdirSync(outputDir, { recursive: true });
1032
+ const evidence = runMicrosoftStoreSubmissionPreflight({
1033
+ gameRoot,
1034
+ artifactRoot: resolveMicrosoftStoreArtifactRoot(preparedTargetsFile),
1035
+ configFile,
1036
+ jsonFile: path.join(outputDir, 'submission-preflight.json'),
1037
+ markdownFile: path.join(outputDir, 'submission-preflight.md'),
1038
+ });
1039
+ },
1040
+ }),
1041
+ 'accept-package': defineI18n({
1042
+ name: 'accept-package',
1043
+ description: 'Inspect packaged target artifacts and optionally run WACK.',
1044
+ resource: commandResource({
1045
+ en: 'Inspect packaged target artifacts and optionally run WACK.',
1046
+ ko: '패키징된 타깃 산출물을 검사하고 선택적으로 WACK를 실행합니다.',
1047
+ }, {
1048
+ target: {
1049
+ en: 'Packaged target. Currently supports microsoft-store.',
1050
+ ko: '패키징 타깃. 현재 microsoft-store를 지원합니다.',
1051
+ },
1052
+ packages: {
1053
+ en: 'Comma-separated game-relative MSIX or AppX package paths.',
1054
+ ko: '쉼표로 구분한 게임 상대 MSIX 또는 AppX 패키지 경로.',
1055
+ },
1056
+ 'targets-file': {
1057
+ en: 'Game target config file.',
1058
+ ko: '게임 타깃 설정 파일.',
1059
+ },
1060
+ 'submission-evidence': {
1061
+ en: 'Submission preflight evidence file.',
1062
+ ko: '제출 사전 검증 증적 파일.',
1063
+ },
1064
+ 'output-dir': {
1065
+ en: 'Directory for optional WACK reports and acceptance evidence.',
1066
+ ko: '선택적 WACK 리포트와 인수 증적을 기록할 디렉터리.',
1067
+ },
1068
+ appcert: {
1069
+ en: 'Optional appcert.exe path to opt into the recommended WACK check.',
1070
+ ko: '권장 WACK 검사를 선택 실행하기 위한 appcert.exe 경로.',
1071
+ },
1072
+ makeappx: {
1073
+ en: 'Optional path to makeappx.exe.',
1074
+ ko: '선택적 makeappx.exe 경로.',
1075
+ },
1076
+ }),
1077
+ args: {
1078
+ target: {
1079
+ type: 'positional',
1080
+ required: true,
1081
+ description: 'Packaged target. Currently supports microsoft-store.',
1082
+ },
1083
+ packages: {
1084
+ type: 'string',
1085
+ required: true,
1086
+ description: 'Comma-separated game-relative MSIX or AppX package paths.',
1087
+ },
1088
+ 'targets-file': {
1089
+ type: 'string',
1090
+ required: false,
1091
+ default: 'mpgd.targets.json',
1092
+ description: 'Game target config file.',
1093
+ },
1094
+ 'submission-evidence': {
1095
+ type: 'string',
1096
+ required: false,
1097
+ default: 'release-output/microsoft-store/submission-preflight.json',
1098
+ description: 'Submission preflight evidence file.',
1099
+ },
1100
+ 'output-dir': {
1101
+ type: 'string',
1102
+ required: false,
1103
+ default: 'release-output/microsoft-store',
1104
+ description: 'Directory for optional WACK reports and acceptance evidence.',
1105
+ },
1106
+ appcert: {
1107
+ type: 'string',
1108
+ required: false,
1109
+ description: 'Optional appcert.exe path to opt into the recommended WACK check.',
1110
+ },
1111
+ makeappx: {
1112
+ type: 'string',
1113
+ required: false,
1114
+ description: 'Optional path to makeappx.exe.',
1115
+ },
1116
+ },
1117
+ run: (ctx) => {
1118
+ const positionals = readLocalPositionals(ctx.positionals, ['target', 'accept-package']);
1119
+ const target = normalizeBuildTarget(readRequiredPositional(positionals, 0, 'target'));
1120
+ if (target !== 'microsoft-store') {
1121
+ throw new Error(`Package acceptance is not available for target: ${target}`);
1122
+ }
1123
+ const targetsFile = path.resolve(readOptionalString(ctx.values['targets-file']) ?? 'mpgd.targets.json');
1124
+ const gameRoot = path.dirname(targetsFile);
1125
+ const packageList = readOptionalString(ctx.values.packages);
1126
+ if (packageList === undefined) {
1127
+ throw new Error('--packages must be a non-empty comma-separated list.');
1128
+ }
1129
+ const packages = packageList
1130
+ .split(',')
1131
+ .map((file) => file.trim())
1132
+ .filter((file) => file.length > 0)
1133
+ .map((file) => resolveGameRelativePath(gameRoot, file));
1134
+ if (packages.length === 0) {
1135
+ throw new Error('--packages must be a non-empty comma-separated list.');
1136
+ }
1137
+ const outputDir = resolveGameRelativePath(gameRoot, readOptionalString(ctx.values['output-dir']) ?? 'release-output/microsoft-store');
1138
+ const appCertExecutable = readOptionalString(ctx.values.appcert);
1139
+ const makeAppxExecutable = readOptionalString(ctx.values.makeappx);
1140
+ resolveMicrosoftStoreArtifactRoot(targetsFile);
1141
+ assertGameOwnedOutputDirectory(gameRoot, outputDir);
1142
+ mkdirSync(outputDir, { recursive: true });
1143
+ const evidence = runMicrosoftStorePackageAcceptance({
1144
+ gameRoot,
1145
+ submissionEvidenceFile: resolveGameRelativePath(gameRoot, readOptionalString(ctx.values['submission-evidence'])
1146
+ ?? 'release-output/microsoft-store/submission-preflight.json'),
1147
+ packageFiles: packages,
1148
+ outputDir,
1149
+ }, createMicrosoftStorePackageAcceptanceRuntime({
1150
+ ...(appCertExecutable === undefined ? {} : { appCertExecutable }),
1151
+ ...(makeAppxExecutable === undefined ? {} : { makeAppxExecutable }),
1152
+ }));
1153
+ },
1154
+ }),
836
1155
  'build-all': defineI18n({
837
1156
  name: 'build-all',
838
1157
  description: 'Build multiple target artifacts.',
@@ -1620,6 +1939,47 @@ function readConfiguredTargetsFile(env) {
1620
1939
  }
1621
1940
  return file;
1622
1941
  }
1942
+ function resolveMicrosoftStoreArtifactRoot(targetsFile) {
1943
+ const parsed = readJsonForCli(targetsFile);
1944
+ assertJsonObject(parsed, `targets file ${targetsFile}`);
1945
+ assertJsonObject(parsed.targets, `targets in ${targetsFile}`);
1946
+ const target = parsed.targets['microsoft-store'];
1947
+ assertJsonObject(target, `microsoft-store target in ${targetsFile}`);
1948
+ if (target.kind !== 'web') {
1949
+ throw new Error(`Microsoft Store target in ${targetsFile} must use kind web.`);
1950
+ }
1951
+ const output = readOptionalString(target.output);
1952
+ if (output === undefined) {
1953
+ throw new Error(`Microsoft Store target in ${targetsFile} must declare output.`);
1954
+ }
1955
+ return resolveGameRelativePath(path.dirname(targetsFile), output);
1956
+ }
1957
+ function resolveGameRelativePath(gameRoot, candidate) {
1958
+ return path.resolve(gameRoot, candidate);
1959
+ }
1960
+ function assertGameOwnedOutputDirectory(gameRoot, outputDir) {
1961
+ const canonicalGameRoot = realpathSync(gameRoot);
1962
+ const canonicalOutputDir = resolvePotentiallyMissingPath(outputDir);
1963
+ const relative = path.relative(canonicalGameRoot, canonicalOutputDir);
1964
+ if (relative.length === 0
1965
+ || relative === '..'
1966
+ || relative.startsWith(`..${path.sep}`)
1967
+ || path.isAbsolute(relative)) {
1968
+ throw new Error(`Microsoft Store submission output directory must stay inside the game root: ${canonicalOutputDir} is not inside ${canonicalGameRoot}.`);
1969
+ }
1970
+ }
1971
+ function resolvePotentiallyMissingPath(input) {
1972
+ const resolved = path.resolve(input);
1973
+ let existingAncestor = resolved;
1974
+ while (!existsSync(existingAncestor)) {
1975
+ const parent = path.dirname(existingAncestor);
1976
+ if (parent === existingAncestor) {
1977
+ throw new Error(`Could not find an existing ancestor for output path: ${resolved}`);
1978
+ }
1979
+ existingAncestor = parent;
1980
+ }
1981
+ return path.resolve(realpathSync(existingAncestor), path.relative(existingAncestor, resolved));
1982
+ }
1623
1983
  function normalizeConfiguredBuildTarget(target) {
1624
1984
  try {
1625
1985
  return normalizeBuildTarget(target);
@@ -0,0 +1,54 @@
1
+ export declare const microsoftStorePackageAcceptanceSchemaVersion: 1;
2
+ export interface MicrosoftStorePackageIdentity {
3
+ readonly name: string;
4
+ readonly publisher: string;
5
+ readonly version: string;
6
+ }
7
+ export interface MicrosoftStorePackageAcceptanceEvidence {
8
+ readonly schemaVersion: 1;
9
+ readonly target: 'microsoft-store';
10
+ readonly submissionEvidenceFile: string;
11
+ readonly submissionEvidenceSha256: string;
12
+ readonly productIdentity: {
13
+ readonly packageId: string;
14
+ readonly publisherId: string;
15
+ readonly publisherDisplayName: string;
16
+ readonly reservedName: string;
17
+ };
18
+ readonly packages: readonly {
19
+ readonly file: string;
20
+ readonly sizeBytes: number;
21
+ readonly sha256: string;
22
+ readonly identity: MicrosoftStorePackageIdentity;
23
+ readonly payloadIdentities: readonly MicrosoftStorePackageIdentity[];
24
+ readonly certification: {
25
+ readonly result: 'NOT_RUN';
26
+ } | {
27
+ readonly result: 'PASS';
28
+ readonly reportFile: string;
29
+ readonly reportSha256: string;
30
+ };
31
+ }[];
32
+ }
33
+ export interface RunMicrosoftStorePackageAcceptanceInput {
34
+ readonly gameRoot: string;
35
+ readonly submissionEvidenceFile: string;
36
+ readonly packageFiles: readonly string[];
37
+ readonly outputDir: string;
38
+ }
39
+ export interface MicrosoftStorePackageAcceptanceRuntime {
40
+ readonly platform: NodeJS.Platform;
41
+ readonly appCertExecutable?: string;
42
+ readonly makeAppxExecutable: string;
43
+ readonly runCommand: (command: string, args: readonly string[]) => void;
44
+ }
45
+ export interface CreateMicrosoftStorePackageAcceptanceRuntimeInput {
46
+ readonly appCertExecutable?: string;
47
+ readonly makeAppxExecutable?: string;
48
+ }
49
+ export declare function createMicrosoftStorePackageAcceptanceRuntime(input?: CreateMicrosoftStorePackageAcceptanceRuntimeInput): MicrosoftStorePackageAcceptanceRuntime;
50
+ export declare function findMicrosoftStoreMakeAppxExecutable(windowsKitsDir: string, architecture: NodeJS.Architecture): string;
51
+ export declare function runMicrosoftStorePackageAcceptance(input: RunMicrosoftStorePackageAcceptanceInput, runtime?: MicrosoftStorePackageAcceptanceRuntime): MicrosoftStorePackageAcceptanceEvidence;
52
+ export declare function parseMicrosoftStorePackageIdentity(xml: string): MicrosoftStorePackageIdentity;
53
+ export declare function parseWindowsAppCertificationResult(xml: string): 'PASS' | 'FAIL';
54
+ export declare function renderMicrosoftStorePackageAcceptanceMarkdown(evidence: MicrosoftStorePackageAcceptanceEvidence): string;