@mpgd/cli 0.62.0 → 0.63.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/app-store-iap-asc.d.ts +23 -0
- package/dist/app-store-iap-asc.js +326 -0
- package/dist/app-store-iap-port.d.ts +125 -0
- package/dist/app-store-iap-port.js +14 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +95 -1
- package/dist/native-build-info.json +2 -2
- package/dist/native-build-target.js +344 -339
- package/dist/play-product-port.d.ts +101 -0
- package/dist/play-product-port.js +7 -0
- package/dist/play-sdk-adapter.js +67 -0
- package/dist/store-products-app-store.d.ts +144 -0
- package/dist/store-products-app-store.js +632 -0
- package/dist/store-products-command.d.ts +50 -0
- package/dist/store-products-command.js +182 -0
- package/dist/store-products-play.d.ts +112 -0
- package/dist/store-products-play.js +622 -0
- package/dist/store-products.d.ts +46 -0
- package/dist/store-products.js +124 -0
- package/dist/testflight-submission.d.ts +1 -1
- package/package.json +14 -14
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { assertProductCatalog, assertStoreListingCatalog, } from '@mpgd/catalog';
|
|
5
|
+
export const defaultStoreListingFileName = 'mpgd.store-listings.json';
|
|
6
|
+
export const defaultProductCatalogFileName = 'mpgd.catalog.json';
|
|
7
|
+
/**
|
|
8
|
+
* Read and cross-check the game's product catalog and store listing file.
|
|
9
|
+
* Given paths resolve against the current directory, like every other mpgd
|
|
10
|
+
* file flag (`deploy --plan`, `--release-notes`); the defaults are the
|
|
11
|
+
* game's own files.
|
|
12
|
+
*/
|
|
13
|
+
export function readStoreProductSources(input) {
|
|
14
|
+
const catalogFile = input.catalogFile === undefined
|
|
15
|
+
? path.join(path.resolve(input.gameRoot), defaultProductCatalogFileName)
|
|
16
|
+
: path.resolve(input.catalogFile);
|
|
17
|
+
const listingsFile = input.listingsFile === undefined
|
|
18
|
+
? path.join(path.resolve(input.gameRoot), defaultStoreListingFileName)
|
|
19
|
+
: path.resolve(input.listingsFile);
|
|
20
|
+
for (const file of [catalogFile, listingsFile]) {
|
|
21
|
+
if (!existsSync(file)) {
|
|
22
|
+
throw new Error(`Store product sync needs ${file}.`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
const catalog = assertProductCatalog(readJson(catalogFile));
|
|
26
|
+
const listings = assertStoreListingCatalog(readJson(listingsFile), catalog);
|
|
27
|
+
return { catalogFile, listingsFile, catalog, listings };
|
|
28
|
+
}
|
|
29
|
+
/** JSON with object keys sorted, so equal values always hash the same. */
|
|
30
|
+
export function stableJson(value) {
|
|
31
|
+
return JSON.stringify(sortKeys(value));
|
|
32
|
+
}
|
|
33
|
+
export function stableSha256(value) {
|
|
34
|
+
return createHash('sha256').update(stableJson(value)).digest('hex');
|
|
35
|
+
}
|
|
36
|
+
/** A new plan file, never overwriting one (the same rule as `deploy plan --out`). */
|
|
37
|
+
export function writeStoreProductPlan(file, plan) {
|
|
38
|
+
const resolved = path.resolve(file);
|
|
39
|
+
writeFileSync(resolved, `${JSON.stringify(plan, null, 2)}\n`, { flag: 'wx', mode: 0o600 });
|
|
40
|
+
return resolved;
|
|
41
|
+
}
|
|
42
|
+
export function readStoreProductPlanFile(file) {
|
|
43
|
+
const raw = readJson(path.resolve(file));
|
|
44
|
+
if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) {
|
|
45
|
+
throw new Error('Saved store product plan is malformed.');
|
|
46
|
+
}
|
|
47
|
+
return raw;
|
|
48
|
+
}
|
|
49
|
+
export class StoreProductPlanStaleError extends Error {
|
|
50
|
+
constructor(detail) {
|
|
51
|
+
super(`${detail} Run "mpgd store products plan" again and review the new plan.`);
|
|
52
|
+
this.name = 'StoreProductPlanStaleError';
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/** Decimal string (`"0.99"`) as whole units and nanos, without floating point. */
|
|
56
|
+
export function decimalToUnitsAndNanos(amount) {
|
|
57
|
+
const match = /^(0|[1-9]\d{0,11})(?:\.(\d{1,9}))?$/u.exec(amount);
|
|
58
|
+
if (match === null) {
|
|
59
|
+
throw new Error(`Price ${amount} is not a plain decimal amount.`);
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
units: match[1] ?? '0',
|
|
63
|
+
nanos: Number((match[2] ?? '').padEnd(9, '0')),
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/** Decimal strings compared by value: `0.99` equals `0.990`. */
|
|
67
|
+
export function sameDecimal(left, right) {
|
|
68
|
+
const normal = (value) => {
|
|
69
|
+
const [whole = '0', fraction = ''] = value.trim().split('.');
|
|
70
|
+
const trimmedFraction = fraction.replace(/0+$/u, '');
|
|
71
|
+
const trimmedWhole = whole.replace(/^0+(?=\d)/u, '');
|
|
72
|
+
return trimmedFraction === '' ? trimmedWhole : `${trimmedWhole}.${trimmedFraction}`;
|
|
73
|
+
};
|
|
74
|
+
return normal(left) === normal(right);
|
|
75
|
+
}
|
|
76
|
+
/** BCP-47 casing: language lowercase, script title case, region uppercase. */
|
|
77
|
+
export function canonicalLanguageTag(tag) {
|
|
78
|
+
return tag.split('-').map((part, index) => {
|
|
79
|
+
if (index === 0) {
|
|
80
|
+
return part.toLowerCase();
|
|
81
|
+
}
|
|
82
|
+
if (/^[A-Za-z]{4}$/u.test(part)) {
|
|
83
|
+
return `${part.slice(0, 1).toUpperCase()}${part.slice(1).toLowerCase()}`;
|
|
84
|
+
}
|
|
85
|
+
if (/^[A-Za-z]{2}$/u.test(part)) {
|
|
86
|
+
return part.toUpperCase();
|
|
87
|
+
}
|
|
88
|
+
return part.toLowerCase();
|
|
89
|
+
}).join('-');
|
|
90
|
+
}
|
|
91
|
+
/** `map` with at most `limit` calls in flight; results keep input order. */
|
|
92
|
+
export async function mapWithConcurrency(items, limit, map) {
|
|
93
|
+
const results = new Array(items.length);
|
|
94
|
+
let next = 0;
|
|
95
|
+
const worker = async () => {
|
|
96
|
+
while (next < items.length) {
|
|
97
|
+
const index = next;
|
|
98
|
+
next += 1;
|
|
99
|
+
results[index] = await map(items[index]);
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
await Promise.all(Array.from({ length: Math.min(limit, items.length) }, worker));
|
|
103
|
+
return results;
|
|
104
|
+
}
|
|
105
|
+
function readJson(file) {
|
|
106
|
+
try {
|
|
107
|
+
return JSON.parse(readFileSync(file, 'utf8'));
|
|
108
|
+
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
throw new Error(`${file} is not valid JSON: ${error.message}`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
function sortKeys(value) {
|
|
114
|
+
if (Array.isArray(value)) {
|
|
115
|
+
return value.map(sortKeys);
|
|
116
|
+
}
|
|
117
|
+
if (typeof value === 'object' && value !== null) {
|
|
118
|
+
return Object.fromEntries(Object.keys(value)
|
|
119
|
+
.sort()
|
|
120
|
+
.filter((key) => value[key] !== undefined)
|
|
121
|
+
.map((key) => [key, sortKeys(value[key])]));
|
|
122
|
+
}
|
|
123
|
+
return value;
|
|
124
|
+
}
|
|
@@ -53,6 +53,6 @@ export declare function classifyInternalBuildState(state: string): {
|
|
|
53
53
|
readonly status: Extract<IosTestFlightStatus, 'processing' | 'action-required' | 'failed' | 'unknown'>;
|
|
54
54
|
readonly detail?: string;
|
|
55
55
|
} | undefined;
|
|
56
|
-
export declare function isolatedAscEnvironment(input: IosTestFlightSubmissionInput, directory: string, source?: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
|
|
56
|
+
export declare function isolatedAscEnvironment(input: Pick<IosTestFlightSubmissionInput, 'apiKeyId' | 'apiIssuerId' | 'apiPrivateKeyBase64'>, directory: string, source?: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
|
|
57
57
|
/** The package consumes one reviewed asc pin; it does not trust PATH or version text. */
|
|
58
58
|
export declare function verifyPinnedAscBinary(binary: string, pinFile?: string): Promise<void>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mpgd/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.63.0",
|
|
4
4
|
"description": "Gunshi CLI for mpgd-kit starter generation and target workflow orchestration.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -70,10 +70,10 @@
|
|
|
70
70
|
"parse5": "7.3.0",
|
|
71
71
|
"postcss": "8.5.28",
|
|
72
72
|
"postcss-value-parser": "4.2.0",
|
|
73
|
-
"@mpgd/catalog": "0.
|
|
74
|
-
"@mpgd/
|
|
75
|
-
"@mpgd/
|
|
76
|
-
"@mpgd/
|
|
73
|
+
"@mpgd/catalog": "0.12.0",
|
|
74
|
+
"@mpgd/target-config": "0.24.2",
|
|
75
|
+
"@mpgd/platform": "0.32.0",
|
|
76
|
+
"@mpgd/phaser-assets": "0.9.1"
|
|
77
77
|
},
|
|
78
78
|
"devDependencies": {
|
|
79
79
|
"@types/node": "^24.0.0",
|
|
@@ -81,17 +81,17 @@
|
|
|
81
81
|
"@types/yauzl": "3.4.0",
|
|
82
82
|
"ttsc": "0.32.1",
|
|
83
83
|
"typescript": "7.0.2",
|
|
84
|
-
"@mpgd/adapter-ait": "0.21.
|
|
84
|
+
"@mpgd/adapter-ait": "0.21.4",
|
|
85
85
|
"@mpgd/adapter-browser": "0.12.1",
|
|
86
|
-
"@mpgd/adapter-capacitor": "0.16.
|
|
87
|
-
"@mpgd/adapter-
|
|
88
|
-
"@mpgd/bridge": "0.16.4",
|
|
89
|
-
"@mpgd/capacitor-play-billing": "0.4.3",
|
|
90
|
-
"@mpgd/adapter-devvit": "0.14.5",
|
|
91
|
-
"@mpgd/capacitor-storekit": "0.4.3",
|
|
86
|
+
"@mpgd/adapter-capacitor": "0.16.2",
|
|
87
|
+
"@mpgd/adapter-devvit": "0.14.6",
|
|
92
88
|
"@mpgd/analytics": "0.5.5",
|
|
93
|
-
"@mpgd/
|
|
94
|
-
"@mpgd/
|
|
89
|
+
"@mpgd/adapter-verse8": "0.7.15",
|
|
90
|
+
"@mpgd/capacitor-play-billing": "0.4.4",
|
|
91
|
+
"@mpgd/bridge": "0.16.4",
|
|
92
|
+
"@mpgd/capacitor-storekit": "0.4.4",
|
|
93
|
+
"@mpgd/game-runtime": "0.8.8",
|
|
94
|
+
"@mpgd/game-services": "0.30.4",
|
|
95
95
|
"@mpgd/i18n": "0.6.26",
|
|
96
96
|
"@mpgd/lite-shell": "0.1.0"
|
|
97
97
|
},
|