@mpgd/cli 0.53.0 → 0.54.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.js +9 -1
- package/dist/deploy-planning.js +2 -2
- package/dist/index.js +148 -52
- package/dist/kit-upgrade.js +105 -6
- package/dist/legal-site-output.d.ts +12 -0
- package/dist/legal-site-output.js +226 -0
- package/dist/native-build-info.json +2 -2
- package/dist/native-build-target.js +166 -14
- package/dist/notifications-starter.d.ts +6 -0
- package/dist/notifications-starter.js +9 -0
- package/dist/offline-playtest.js +1 -1
- package/dist/tooling-migration.js +1 -1
- package/package.json +17 -17
- package/templates/notifications-overlay/common/docs/NOTIFICATIONS.md +36 -7
- package/templates/notifications-overlay/common/src/platform/notificationBackend.ts +157 -0
- package/templates/notifications-overlay/common/src/platform/notifications.ts +67 -35
- package/templates/notifications-overlay/phaser/src/scenes/NotificationSettingsScene.ts +26 -7
- package/templates/phaser-game/src/platform/gameServices.ts +3 -0
- package/templates/phaser-game/src/platform/serverSession.ts +66 -0
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { lstatSync, mkdirSync, realpathSync, renameSync, rmSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
/**
|
|
5
|
+
* Legal-site paths are contained only after the nearest existing ancestor is
|
|
6
|
+
* canonicalized. A lexical `path.relative` check treats a symlinked `artifacts`
|
|
7
|
+
* directory as inside the project while later writes follow the link.
|
|
8
|
+
*/
|
|
9
|
+
export function assertLegalProjectPath(label, resolved) {
|
|
10
|
+
const lexicalRoot = process.cwd();
|
|
11
|
+
const lexical = path.relative(lexicalRoot, resolved);
|
|
12
|
+
if (lexical.startsWith('..') || path.isAbsolute(lexical)) {
|
|
13
|
+
throw new Error(`${label} must be inside the project root: ${resolved}`);
|
|
14
|
+
}
|
|
15
|
+
assertNoLegalSymlink(lexicalRoot, resolved, label);
|
|
16
|
+
const canonicalRoot = realpathSync(lexicalRoot);
|
|
17
|
+
const canonical = realpathBestEffort(resolved);
|
|
18
|
+
const canonicalRelative = path.relative(canonicalRoot, canonical);
|
|
19
|
+
if (canonicalRelative.startsWith('..') || path.isAbsolute(canonicalRelative)) {
|
|
20
|
+
throw new Error(`${label} must be inside the project root: ${resolved}`);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/** Stage the site beside the destination, then publish with rename. */
|
|
24
|
+
export function publishLegalSiteFiles(outDir, outputs) {
|
|
25
|
+
assertLegalProjectPath('out-dir', outDir);
|
|
26
|
+
// A temp dir on the project root is a different filesystem when out-dir is a
|
|
27
|
+
// mount or sits under a bind-mounted artifacts directory. Rename then fails
|
|
28
|
+
// with EXDEV. Stage on the destination filesystem instead.
|
|
29
|
+
const staging = path.join(legalStagingParent(outDir), `.mpgd-legal-${randomUUID()}`);
|
|
30
|
+
let publishedDirectory = false;
|
|
31
|
+
try {
|
|
32
|
+
for (const output of outputs) {
|
|
33
|
+
const relative = path.relative(outDir, output.file);
|
|
34
|
+
if (relative.startsWith('..') || path.isAbsolute(relative)) {
|
|
35
|
+
throw new Error(`Legal site output escapes the output directory: ${output.file}`);
|
|
36
|
+
}
|
|
37
|
+
const staged = path.join(staging, relative);
|
|
38
|
+
mkdirSync(path.dirname(staged), { recursive: true });
|
|
39
|
+
writeFileSync(staged, output.content, { flag: 'wx' });
|
|
40
|
+
}
|
|
41
|
+
assertLegalProjectPath('out-dir', outDir);
|
|
42
|
+
let existing;
|
|
43
|
+
try {
|
|
44
|
+
existing = lstatSync(outDir);
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
if (!isErrno(error, 'ENOENT')) {
|
|
48
|
+
throw error;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
if (existing?.isSymbolicLink() === true) {
|
|
52
|
+
throw new Error(`Refusing to write legal site output through a symbolic link: ${outDir}`);
|
|
53
|
+
}
|
|
54
|
+
if (existing === undefined) {
|
|
55
|
+
ensureLegalDirectory(path.dirname(outDir));
|
|
56
|
+
assertLegalProjectPath('out-dir', outDir);
|
|
57
|
+
renameSync(staging, outDir);
|
|
58
|
+
publishedDirectory = true;
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
if (!existing.isDirectory()) {
|
|
62
|
+
throw new Error(`Legal site output path is not a directory: ${outDir}`);
|
|
63
|
+
}
|
|
64
|
+
for (const output of outputs) {
|
|
65
|
+
assertNoLegalSymlink(process.cwd(), output.file, 'out-dir');
|
|
66
|
+
ensureLegalDirectory(path.dirname(output.file));
|
|
67
|
+
assertNoLegalSymlink(process.cwd(), output.file, 'out-dir');
|
|
68
|
+
renameSync(path.join(staging, path.relative(outDir, output.file)), output.file);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
finally {
|
|
72
|
+
if (!publishedDirectory) {
|
|
73
|
+
rmSync(staging, { recursive: true, force: true });
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Parent for the staging directory. An existing out-dir may itself be a mount,
|
|
79
|
+
* so stage inside it. A missing out-dir is created on its nearest existing
|
|
80
|
+
* ancestor, which is the filesystem the later rename must stay on.
|
|
81
|
+
*/
|
|
82
|
+
function legalStagingParent(outDir) {
|
|
83
|
+
const existing = lstatIfPresent(outDir);
|
|
84
|
+
if (existing !== undefined) {
|
|
85
|
+
if (existing.isSymbolicLink()) {
|
|
86
|
+
throw new Error(`Refusing to write legal site output through a symbolic link: ${outDir}`);
|
|
87
|
+
}
|
|
88
|
+
if (!existing.isDirectory()) {
|
|
89
|
+
throw new Error(`Legal site output path is not a directory: ${outDir}`);
|
|
90
|
+
}
|
|
91
|
+
return outDir;
|
|
92
|
+
}
|
|
93
|
+
let current = path.dirname(outDir);
|
|
94
|
+
for (;;) {
|
|
95
|
+
const stat = lstatIfPresent(current);
|
|
96
|
+
if (stat !== undefined) {
|
|
97
|
+
if (stat.isSymbolicLink()) {
|
|
98
|
+
throw new Error(`Refusing to write legal site output through a symbolic link: ${current}`);
|
|
99
|
+
}
|
|
100
|
+
if (!stat.isDirectory()) {
|
|
101
|
+
throw new Error(`Legal site output path is not a directory: ${current}`);
|
|
102
|
+
}
|
|
103
|
+
return current;
|
|
104
|
+
}
|
|
105
|
+
const parent = path.dirname(current);
|
|
106
|
+
if (parent === current) {
|
|
107
|
+
throw new Error(`out-dir must be inside the project root: ${outDir}`);
|
|
108
|
+
}
|
|
109
|
+
current = parent;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
function lstatIfPresent(target) {
|
|
113
|
+
try {
|
|
114
|
+
return lstatSync(target);
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
if (isErrno(error, 'ENOENT')) {
|
|
118
|
+
return undefined;
|
|
119
|
+
}
|
|
120
|
+
throw error;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
/** Resolve symlinks for the longest existing ancestor, keeping the remainder. */
|
|
124
|
+
function realpathBestEffort(target) {
|
|
125
|
+
// lstat-based existence stops the walk at dangling symlinks, so realpath
|
|
126
|
+
// fails closed on them instead of lexically skipping the link.
|
|
127
|
+
const exists = (candidate) => {
|
|
128
|
+
try {
|
|
129
|
+
lstatSync(candidate);
|
|
130
|
+
return true;
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
let existing = target;
|
|
137
|
+
while (!exists(existing)) {
|
|
138
|
+
const parent = path.dirname(existing);
|
|
139
|
+
if (parent === existing) {
|
|
140
|
+
return existing;
|
|
141
|
+
}
|
|
142
|
+
existing = parent;
|
|
143
|
+
}
|
|
144
|
+
let real;
|
|
145
|
+
try {
|
|
146
|
+
real = realpathSync(existing);
|
|
147
|
+
}
|
|
148
|
+
catch (error) {
|
|
149
|
+
throw new Error(`Legal site path resolves through a broken symbolic link: ${target}`, {
|
|
150
|
+
cause: error,
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
return existing === target ? real : path.resolve(real, path.relative(existing, target));
|
|
154
|
+
}
|
|
155
|
+
function assertNoLegalSymlink(root, target, label) {
|
|
156
|
+
const relative = path.relative(root, target);
|
|
157
|
+
if (relative.startsWith('..') || path.isAbsolute(relative)) {
|
|
158
|
+
throw new Error(`${label} must be inside the project root: ${target}`);
|
|
159
|
+
}
|
|
160
|
+
const verb = label === 'legal-dir' ? 'read legal sources' : 'write legal site output';
|
|
161
|
+
let current = root;
|
|
162
|
+
for (const part of relative.split(path.sep)) {
|
|
163
|
+
if (part === '' || part === '.') {
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
current = path.join(current, part);
|
|
167
|
+
let stat;
|
|
168
|
+
try {
|
|
169
|
+
stat = lstatSync(current);
|
|
170
|
+
}
|
|
171
|
+
catch (error) {
|
|
172
|
+
if (isErrno(error, 'ENOENT')) {
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
throw error;
|
|
176
|
+
}
|
|
177
|
+
if (stat.isSymbolicLink()) {
|
|
178
|
+
throw new Error(`Refusing to ${verb} through a symbolic link: ${current}`);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
function ensureLegalDirectory(directory) {
|
|
183
|
+
const root = process.cwd();
|
|
184
|
+
const relative = path.relative(root, directory);
|
|
185
|
+
if (relative.startsWith('..') || path.isAbsolute(relative)) {
|
|
186
|
+
throw new Error(`out-dir must be inside the project root: ${directory}`);
|
|
187
|
+
}
|
|
188
|
+
if (relative === '') {
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
let current = root;
|
|
192
|
+
for (const part of relative.split(path.sep)) {
|
|
193
|
+
if (part === '' || part === '.') {
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
current = path.join(current, part);
|
|
197
|
+
let stat;
|
|
198
|
+
try {
|
|
199
|
+
stat = lstatSync(current);
|
|
200
|
+
}
|
|
201
|
+
catch (error) {
|
|
202
|
+
if (!isErrno(error, 'ENOENT')) {
|
|
203
|
+
throw error;
|
|
204
|
+
}
|
|
205
|
+
try {
|
|
206
|
+
mkdirSync(current);
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
catch (mkdirError) {
|
|
210
|
+
if (!isErrno(mkdirError, 'EEXIST')) {
|
|
211
|
+
throw mkdirError;
|
|
212
|
+
}
|
|
213
|
+
stat = lstatSync(current);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
if (stat.isSymbolicLink()) {
|
|
217
|
+
throw new Error(`Refusing to write legal site output through a symbolic link: ${current}`);
|
|
218
|
+
}
|
|
219
|
+
if (!stat.isDirectory()) {
|
|
220
|
+
throw new Error(`Legal site output path is not a directory: ${current}`);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
function isErrno(error, code) {
|
|
225
|
+
return error !== null && typeof error === 'object' && 'code' in error && error.code === code;
|
|
226
|
+
}
|
|
@@ -6899,6 +6899,72 @@ var timestampPattern = new RegExp("^(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2})
|
|
|
6899
6899
|
var fcmAddressKinds = Object.freeze(["fcm-token"]);
|
|
6900
6900
|
var fcmReservedDataKeys = Object.freeze(["topic", "route"]);
|
|
6901
6901
|
var tokenRefreshMarginMs = 5 * 60 * 1e3;
|
|
6902
|
+
var sha256K = new Uint32Array([
|
|
6903
|
+
1116352408,
|
|
6904
|
+
1899447441,
|
|
6905
|
+
3049323471,
|
|
6906
|
+
3921009573,
|
|
6907
|
+
961987163,
|
|
6908
|
+
1508970993,
|
|
6909
|
+
2453635748,
|
|
6910
|
+
2870763221,
|
|
6911
|
+
3624381080,
|
|
6912
|
+
310598401,
|
|
6913
|
+
607225278,
|
|
6914
|
+
1426881987,
|
|
6915
|
+
1925078388,
|
|
6916
|
+
2162078206,
|
|
6917
|
+
2614888103,
|
|
6918
|
+
3248222580,
|
|
6919
|
+
3835390401,
|
|
6920
|
+
4022224774,
|
|
6921
|
+
264347078,
|
|
6922
|
+
604807628,
|
|
6923
|
+
770255983,
|
|
6924
|
+
1249150122,
|
|
6925
|
+
1555081692,
|
|
6926
|
+
1996064986,
|
|
6927
|
+
2554220882,
|
|
6928
|
+
2821834349,
|
|
6929
|
+
2952996808,
|
|
6930
|
+
3210313671,
|
|
6931
|
+
3336571891,
|
|
6932
|
+
3584528711,
|
|
6933
|
+
113926993,
|
|
6934
|
+
338241895,
|
|
6935
|
+
666307205,
|
|
6936
|
+
773529912,
|
|
6937
|
+
1294757372,
|
|
6938
|
+
1396182291,
|
|
6939
|
+
1695183700,
|
|
6940
|
+
1986661051,
|
|
6941
|
+
2177026350,
|
|
6942
|
+
2456956037,
|
|
6943
|
+
2730485921,
|
|
6944
|
+
2820302411,
|
|
6945
|
+
3259730800,
|
|
6946
|
+
3345764771,
|
|
6947
|
+
3516065817,
|
|
6948
|
+
3600352804,
|
|
6949
|
+
4094571909,
|
|
6950
|
+
275423344,
|
|
6951
|
+
430227734,
|
|
6952
|
+
506948616,
|
|
6953
|
+
659060556,
|
|
6954
|
+
883997877,
|
|
6955
|
+
958139571,
|
|
6956
|
+
1322822218,
|
|
6957
|
+
1537002063,
|
|
6958
|
+
1747873779,
|
|
6959
|
+
1955562222,
|
|
6960
|
+
2024104815,
|
|
6961
|
+
2227730452,
|
|
6962
|
+
2361852424,
|
|
6963
|
+
2428436474,
|
|
6964
|
+
2756734187,
|
|
6965
|
+
3204031479,
|
|
6966
|
+
3329325298
|
|
6967
|
+
]);
|
|
6902
6968
|
|
|
6903
6969
|
// node_modules/.cache/mpgd-native-build/packages/game-services/src/notifications/registry.js
|
|
6904
6970
|
var notificationHistoryRetentionDays = 30;
|
|
@@ -9436,9 +9502,21 @@ var assertReleaseManifestStructure = /* @__PURE__ */ (() => {
|
|
|
9436
9502
|
};
|
|
9437
9503
|
})();
|
|
9438
9504
|
var fullGitShaPattern = /^[0-9a-f]{40}$/u;
|
|
9505
|
+
var sha256DigestPattern = /^[0-9a-f]{64}$/u;
|
|
9439
9506
|
function assertReleaseManifest(input) {
|
|
9440
9507
|
const manifest = assertReleaseManifestStructure(input);
|
|
9508
|
+
if (!fullGitShaPattern.test(manifest.gitSha)) {
|
|
9509
|
+
throw new TypeError("Release manifest gitSha must be a lowercase 40-character SHA.");
|
|
9510
|
+
}
|
|
9511
|
+
if (!fullGitShaPattern.test(manifest.kitGitSha)) {
|
|
9512
|
+
throw new TypeError("Release manifest kitGitSha must be a lowercase 40-character SHA.");
|
|
9513
|
+
}
|
|
9441
9514
|
for (const [target2, entry] of Object.entries(manifest.targets)) {
|
|
9515
|
+
assertSha256Digest(entry.effectiveConfig.digest, `Release manifest ${target2} effectiveConfig.digest`);
|
|
9516
|
+
assertSha256Digest(entry.iconManifest.digest, `Release manifest ${target2} iconManifest.digest`);
|
|
9517
|
+
assertSha256Digest(entry.iconManifest.sourceSha256, `Release manifest ${target2} iconManifest.sourceSha256`);
|
|
9518
|
+
assertSha256Digest(entry.iconManifest.sharedConfigSha256, `Release manifest ${target2} iconManifest.sharedConfigSha256`);
|
|
9519
|
+
assertSha256Digest(entry.iconManifest.renderConfigSha256, `Release manifest ${target2} iconManifest.renderConfigSha256`);
|
|
9442
9520
|
const delivery = entry.nativeDelivery;
|
|
9443
9521
|
if (delivery === void 0) {
|
|
9444
9522
|
continue;
|
|
@@ -9453,9 +9531,6 @@ function assertReleaseManifest(input) {
|
|
|
9453
9531
|
throw new TypeError(`Release manifest production native delivery is unsigned: ${target2}.`);
|
|
9454
9532
|
}
|
|
9455
9533
|
}
|
|
9456
|
-
if (!fullGitShaPattern.test(manifest.kitGitSha)) {
|
|
9457
|
-
throw new TypeError("Release manifest kitGitSha must be a lowercase 40-character SHA.");
|
|
9458
|
-
}
|
|
9459
9534
|
if (manifest.releaseIdentity !== void 0) {
|
|
9460
9535
|
const releaseIdentity = createMpgdReleaseIdentity({
|
|
9461
9536
|
gameVersion: manifest.releaseIdentity.gameVersion,
|
|
@@ -9477,6 +9552,11 @@ function assertReleaseManifest(input) {
|
|
|
9477
9552
|
}
|
|
9478
9553
|
return manifest;
|
|
9479
9554
|
}
|
|
9555
|
+
function assertSha256Digest(value, label) {
|
|
9556
|
+
if (!sha256DigestPattern.test(value)) {
|
|
9557
|
+
throw new TypeError(`${label} must be a lowercase 64-character SHA-256 digest.`);
|
|
9558
|
+
}
|
|
9559
|
+
}
|
|
9480
9560
|
|
|
9481
9561
|
// node_modules/.cache/mpgd-native-build/tools/target/release-manifest-lock.js
|
|
9482
9562
|
import { randomUUID } from "node:crypto";
|
|
@@ -9575,7 +9655,7 @@ function canRecoverLock(lockFile) {
|
|
|
9575
9655
|
// node_modules/.cache/mpgd-native-build/tools/target/generate-release-manifest.js
|
|
9576
9656
|
var __typia_transform__accessExpressionAsString6 = _accessExpressionAsString_110._accessExpressionAsString;
|
|
9577
9657
|
var assertProductCatalog = /* @__PURE__ */ (() => {
|
|
9578
|
-
const _ae0 = '({ readonly type: "currency"; readonly currency: string & MinLength<1>; readonly amount:
|
|
9658
|
+
const _ae0 = '({ readonly type: "currency"; readonly currency: string & MinLength<1>; readonly amount: PositiveLedgerAmount; } | { readonly type: "entitlement"; readonly entitlement: string; } | { readonly type: "resource"; readonly resource: string & MinLength<1>; readonly amount: number & ExclusiveMinimum<0> & Maximum<1.7976931348623157e+308>; })';
|
|
9579
9659
|
const _ao0 = (input, _path, _exceptionable = true) => ("string" === typeof input.version || _assertGuard_19._assertGuard(_exceptionable, {
|
|
9580
9660
|
method: "typia.createAssert",
|
|
9581
9661
|
path: _path + ".version",
|
|
@@ -9673,10 +9753,25 @@ var assertProductCatalog = /* @__PURE__ */ (() => {
|
|
|
9673
9753
|
path: _path + ".currency",
|
|
9674
9754
|
expected: "(string & MinLength<1>)",
|
|
9675
9755
|
value: input.currency
|
|
9676
|
-
}, _errorFactory)) && ("number" === typeof input.amount || _assertGuard_19._assertGuard(_exceptionable, {
|
|
9756
|
+
}, _errorFactory)) && ("number" === typeof input.amount && (1 <= input.amount || _assertGuard_19._assertGuard(_exceptionable, {
|
|
9677
9757
|
method: "typia.createAssert",
|
|
9678
9758
|
path: _path + ".amount",
|
|
9679
|
-
expected: "number",
|
|
9759
|
+
expected: "number & Minimum<1>",
|
|
9760
|
+
value: input.amount
|
|
9761
|
+
}, _errorFactory)) && (input.amount <= 9007199254740991 || _assertGuard_19._assertGuard(_exceptionable, {
|
|
9762
|
+
method: "typia.createAssert",
|
|
9763
|
+
path: _path + ".amount",
|
|
9764
|
+
expected: "number & Maximum<9007199254740991>",
|
|
9765
|
+
value: input.amount
|
|
9766
|
+
}, _errorFactory)) && (input.amount % 1 === 0 || _assertGuard_19._assertGuard(_exceptionable, {
|
|
9767
|
+
method: "typia.createAssert",
|
|
9768
|
+
path: _path + ".amount",
|
|
9769
|
+
expected: "number & MultipleOf<1>",
|
|
9770
|
+
value: input.amount
|
|
9771
|
+
}, _errorFactory)) || _assertGuard_19._assertGuard(_exceptionable, {
|
|
9772
|
+
method: "typia.createAssert",
|
|
9773
|
+
path: _path + ".amount",
|
|
9774
|
+
expected: "(number & Minimum<1> & Maximum<9007199254740991> & MultipleOf<1>)",
|
|
9680
9775
|
value: input.amount
|
|
9681
9776
|
}, _errorFactory));
|
|
9682
9777
|
const _ao3 = (input, _path, _exceptionable = true) => ("entitlement" === input.type || _assertGuard_19._assertGuard(_exceptionable, {
|
|
@@ -9749,7 +9844,7 @@ var assertProductCatalog = /* @__PURE__ */ (() => {
|
|
|
9749
9844
|
})();
|
|
9750
9845
|
const _io0 = (input) => "string" === typeof input.version && (void 0 === input.currencies || Array.isArray(input.currencies) && (1 <= input.currencies.length && input.currencies.every((elem) => "string" === typeof elem && 1 <= elem.length))) && (Array.isArray(input.products) && input.products.every((elem) => "object" === typeof elem && null !== elem && _io1(elem)));
|
|
9751
9846
|
const _io1 = (input) => "string" === typeof input.id && ("consumable" === input.type || "non_consumable" === input.type || "subscription" === input.type) && ("object" === typeof input.grant && null !== input.grant && _iu0(input.grant)) && ("object" === typeof input.platformProductIds && null !== input.platformProductIds && false === Array.isArray(input.platformProductIds) && _io5(input.platformProductIds));
|
|
9752
|
-
const _io2 = (input) => "currency" === input.type && ("string" === typeof input.currency && 1 <= input.currency.length) && "number" === typeof input.amount;
|
|
9847
|
+
const _io2 = (input) => "currency" === input.type && ("string" === typeof input.currency && 1 <= input.currency.length) && ("number" === typeof input.amount && (1 <= input.amount && input.amount <= 9007199254740991 && input.amount % 1 === 0));
|
|
9753
9848
|
const _io3 = (input) => "entitlement" === input.type && "string" === typeof input.entitlement;
|
|
9754
9849
|
const _io4 = (input) => "resource" === input.type && ("string" === typeof input.resource && 1 <= input.resource.length) && ("number" === typeof input.amount && (0 < input.amount && input.amount <= 17976931348623157e292));
|
|
9755
9850
|
const _io5 = (input) => Object.keys(input).every((key) => {
|
|
@@ -9789,16 +9884,31 @@ var assertProductCatalog = /* @__PURE__ */ (() => {
|
|
|
9789
9884
|
};
|
|
9790
9885
|
})();
|
|
9791
9886
|
var assertAdPlacements = /* @__PURE__ */ (() => {
|
|
9792
|
-
const _ip0 = (input) => "number" === typeof input["amount"];
|
|
9887
|
+
const _ip0 = (input) => "number" === typeof input["amount"] && (1 <= input["amount"] && input["amount"] <= 9007199254740991 && input["amount"] % 1 === 0);
|
|
9793
9888
|
const _ae0 = '(AdPlacementEntryBase & { readonly type: "banner" | "interstitial"; readonly reward?: never; } | AdPlacementEntryBase & { readonly type: "rewarded"; readonly reward?: AdReward; })';
|
|
9794
|
-
const _ae1 = '(undefined | { readonly type: "continue"; readonly amount:
|
|
9795
|
-
const _ap0 = (input, _path, _exceptionable = true) => "number" === typeof input["amount"] || _assertGuard_19._assertGuard(_exceptionable, {
|
|
9889
|
+
const _ae1 = '(undefined | { readonly type: "continue"; readonly amount: PositiveLedgerAmount; } | { readonly type: "currency"; readonly amount: PositiveLedgerAmount; readonly currency: string & MinLength<1>; })';
|
|
9890
|
+
const _ap0 = (input, _path, _exceptionable = true) => "number" === typeof input["amount"] && (1 <= input["amount"] || _assertGuard_19._assertGuard(_exceptionable, {
|
|
9796
9891
|
method: "typia.createAssert",
|
|
9797
9892
|
path: _path + ".amount",
|
|
9798
|
-
expected: "number",
|
|
9893
|
+
expected: "number & Minimum<1>",
|
|
9894
|
+
value: input["amount"]
|
|
9895
|
+
}, _errorFactory)) && (input["amount"] <= 9007199254740991 || _assertGuard_19._assertGuard(_exceptionable, {
|
|
9896
|
+
method: "typia.createAssert",
|
|
9897
|
+
path: _path + ".amount",
|
|
9898
|
+
expected: "number & Maximum<9007199254740991>",
|
|
9899
|
+
value: input["amount"]
|
|
9900
|
+
}, _errorFactory)) && (input["amount"] % 1 === 0 || _assertGuard_19._assertGuard(_exceptionable, {
|
|
9901
|
+
method: "typia.createAssert",
|
|
9902
|
+
path: _path + ".amount",
|
|
9903
|
+
expected: "number & MultipleOf<1>",
|
|
9904
|
+
value: input["amount"]
|
|
9905
|
+
}, _errorFactory)) || _assertGuard_19._assertGuard(_exceptionable, {
|
|
9906
|
+
method: "typia.createAssert",
|
|
9907
|
+
path: _path + ".amount",
|
|
9908
|
+
expected: "(number & Minimum<1> & Maximum<9007199254740991> & MultipleOf<1>)",
|
|
9799
9909
|
value: input["amount"]
|
|
9800
9910
|
}, _errorFactory);
|
|
9801
|
-
const _ae2 = '({ readonly type: "continue"; readonly amount:
|
|
9911
|
+
const _ae2 = '({ readonly type: "continue"; readonly amount: PositiveLedgerAmount; } | { readonly type: "currency"; readonly amount: PositiveLedgerAmount; readonly currency: string & MinLength<1>; })';
|
|
9802
9912
|
const _ao0 = (input, _path, _exceptionable = true) => ("string" === typeof input.version || _assertGuard_19._assertGuard(_exceptionable, {
|
|
9803
9913
|
method: "typia.createAssert",
|
|
9804
9914
|
path: _path + ".version",
|
|
@@ -10307,9 +10417,47 @@ function readSdkMajor(envValue, metadataValue) {
|
|
|
10307
10417
|
function getSourceGitSha(kitGitSha) {
|
|
10308
10418
|
const configuredGitSha = readOptionalString2(process.env.MPGD_SOURCE_GIT_SHA);
|
|
10309
10419
|
if (configuredGitSha !== void 0) {
|
|
10310
|
-
return configuredGitSha;
|
|
10420
|
+
return requireFullGitSha(configuredGitSha, "MPGD_SOURCE_GIT_SHA");
|
|
10311
10421
|
}
|
|
10312
|
-
|
|
10422
|
+
if (isInRepoKitBuild()) {
|
|
10423
|
+
return inRepoKitSourceGitSha(kitGitSha);
|
|
10424
|
+
}
|
|
10425
|
+
throw new Error("MPGD_SOURCE_GIT_SHA is required for a downstream game build and must be a lowercase 40-character SHA. Refusing to record the kit revision as the game revision.");
|
|
10426
|
+
}
|
|
10427
|
+
function inRepoKitSourceGitSha(kitGitSha) {
|
|
10428
|
+
return requireFullGitSha(kitGitSha, "in-repo kit source Git SHA");
|
|
10429
|
+
}
|
|
10430
|
+
function isInRepoKitBuild() {
|
|
10431
|
+
if (process.env.MPGD_NATIVE_PACKAGED_BUILD === "1") {
|
|
10432
|
+
return false;
|
|
10433
|
+
}
|
|
10434
|
+
const targetsFile = readOptionalString2(process.env.MPGD_PLATFORM_TARGETS_FILE);
|
|
10435
|
+
if (targetsFile === void 0) {
|
|
10436
|
+
return true;
|
|
10437
|
+
}
|
|
10438
|
+
return isInsideDirectory(resolveKitRoot(), targetsFile);
|
|
10439
|
+
}
|
|
10440
|
+
function isInsideDirectory(root, candidate) {
|
|
10441
|
+
let resolvedRoot;
|
|
10442
|
+
try {
|
|
10443
|
+
resolvedRoot = realpathSync6(root);
|
|
10444
|
+
} catch {
|
|
10445
|
+
return false;
|
|
10446
|
+
}
|
|
10447
|
+
const resolvedCandidate = resolve12(candidate);
|
|
10448
|
+
let comparable = resolvedCandidate;
|
|
10449
|
+
try {
|
|
10450
|
+
comparable = realpathSync6(resolvedCandidate);
|
|
10451
|
+
} catch {
|
|
10452
|
+
}
|
|
10453
|
+
const rel = relative9(resolvedRoot, comparable);
|
|
10454
|
+
return rel !== "" && !rel.startsWith("..") && !isAbsolute8(rel);
|
|
10455
|
+
}
|
|
10456
|
+
function requireFullGitSha(value, label) {
|
|
10457
|
+
if (!/^[0-9a-f]{40}$/u.test(value)) {
|
|
10458
|
+
throw new Error(`${label} must be a lowercase 40-character SHA.`);
|
|
10459
|
+
}
|
|
10460
|
+
return value;
|
|
10313
10461
|
}
|
|
10314
10462
|
function resolveKitGitSha() {
|
|
10315
10463
|
if (process.env.MPGD_NATIVE_PACKAGED_BUILD === "1") {
|
|
@@ -14739,6 +14887,7 @@ var notificationsCapacitorGateways = Object.freeze({
|
|
|
14739
14887
|
var notificationsCapacitorGatewayModules = Object.freeze([
|
|
14740
14888
|
"src/platform/capacitorNotifications.ts",
|
|
14741
14889
|
"src/platform/notifications.ts",
|
|
14890
|
+
"src/platform/notificationBackend.ts",
|
|
14742
14891
|
"src/platform/notificationSettings.ts",
|
|
14743
14892
|
"src/platform/notificationOpens.ts"
|
|
14744
14893
|
]);
|
|
@@ -14749,6 +14898,9 @@ var notificationsCapacitorSupportFiles = Object.freeze([
|
|
|
14749
14898
|
"tools/validate-notifications.ts",
|
|
14750
14899
|
"docs/NOTIFICATIONS.md"
|
|
14751
14900
|
]);
|
|
14901
|
+
var notificationsCapacitorTemplateSupportFiles = Object.freeze([
|
|
14902
|
+
"src/platform/serverSession.ts"
|
|
14903
|
+
]);
|
|
14752
14904
|
|
|
14753
14905
|
// node_modules/.cache/mpgd-native-build/packages/cli/src/native-shell-identity.js
|
|
14754
14906
|
function assertAndroidSettingsAppProject(source) {
|
|
@@ -58,6 +58,12 @@ export declare const notificationsCapacitorGatewayModules: readonly string[];
|
|
|
58
58
|
* catalog files they read, the validator, and the game's notifications doc.
|
|
59
59
|
*/
|
|
60
60
|
export declare const notificationsCapacitorSupportFiles: readonly string[];
|
|
61
|
+
/**
|
|
62
|
+
* Files of the base game template the notifications modules import, which a
|
|
63
|
+
* game created by an earlier CLI release may not have: written from the
|
|
64
|
+
* template when missing, kept when the game has one.
|
|
65
|
+
*/
|
|
66
|
+
export declare const notificationsCapacitorTemplateSupportFiles: readonly string[];
|
|
61
67
|
/** Whether a Capacitor gateway already registers a notifications provider. */
|
|
62
68
|
export declare function capacitorGatewayRegistersNotifications(source: string): boolean;
|
|
63
69
|
/**
|
|
@@ -460,6 +460,7 @@ export const notificationsCapacitorGateways = Object.freeze({
|
|
|
460
460
|
export const notificationsCapacitorGatewayModules = Object.freeze([
|
|
461
461
|
'src/platform/capacitorNotifications.ts',
|
|
462
462
|
'src/platform/notifications.ts',
|
|
463
|
+
'src/platform/notificationBackend.ts',
|
|
463
464
|
'src/platform/notificationSettings.ts',
|
|
464
465
|
'src/platform/notificationOpens.ts',
|
|
465
466
|
]);
|
|
@@ -475,6 +476,14 @@ export const notificationsCapacitorSupportFiles = Object.freeze([
|
|
|
475
476
|
'tools/validate-notifications.ts',
|
|
476
477
|
'docs/NOTIFICATIONS.md',
|
|
477
478
|
]);
|
|
479
|
+
/**
|
|
480
|
+
* Files of the base game template the notifications modules import, which a
|
|
481
|
+
* game created by an earlier CLI release may not have: written from the
|
|
482
|
+
* template when missing, kept when the game has one.
|
|
483
|
+
*/
|
|
484
|
+
export const notificationsCapacitorTemplateSupportFiles = Object.freeze([
|
|
485
|
+
'src/platform/serverSession.ts',
|
|
486
|
+
]);
|
|
478
487
|
/** Whether a Capacitor gateway already registers a notifications provider. */
|
|
479
488
|
export function capacitorGatewayRegistersNotifications(source) {
|
|
480
489
|
// One pass over strings and comments: neither registers anything, and a
|
package/dist/offline-playtest.js
CHANGED
|
@@ -3125,7 +3125,7 @@ function assembleOfflineHtml(html, bundledEntry, entryAttributes) {
|
|
|
3125
3125
|
|| output.indexOf(offlineEntryPlaceholder) !== output.lastIndexOf(offlineEntryPlaceholder)) {
|
|
3126
3126
|
throw new Error('Offline playtest could not preserve the module entry position.');
|
|
3127
3127
|
}
|
|
3128
|
-
output = output.replace(offlineEntryPlaceholder, inlineScript);
|
|
3128
|
+
output = output.replace(offlineEntryPlaceholder, () => inlineScript);
|
|
3129
3129
|
if (findActiveHtmlStartTag(output, 'body') !== undefined) {
|
|
3130
3130
|
return `${output.trim()}\n`;
|
|
3131
3131
|
}
|
|
@@ -399,7 +399,7 @@ function migrateKnownRunner(source, template) {
|
|
|
399
399
|
if (defaults.length !== 1) {
|
|
400
400
|
return undefined;
|
|
401
401
|
}
|
|
402
|
-
const rendered = template.replace("'__DEFAULT_KIT_PATH__'", JSON.stringify(defaults[0]));
|
|
402
|
+
const rendered = template.replace("'__DEFAULT_KIT_PATH__'", () => JSON.stringify(defaults[0]));
|
|
403
403
|
const canonical = (value) => JSON.stringify(value, (key, item) => ['start', 'end', 'raw'].includes(key) ? undefined : item);
|
|
404
404
|
return canonical(ast) === canonical(parse(rendered, { ecmaVersion: 'latest', sourceType: 'module' }))
|
|
405
405
|
? rendered
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mpgd/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.54.1",
|
|
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.10.
|
|
74
|
-
"@mpgd/platform": "0.
|
|
75
|
-
"@mpgd/
|
|
76
|
-
"@mpgd/
|
|
73
|
+
"@mpgd/catalog": "0.10.6",
|
|
74
|
+
"@mpgd/platform": "0.27.1",
|
|
75
|
+
"@mpgd/phaser-assets": "0.9.1",
|
|
76
|
+
"@mpgd/target-config": "0.23.6"
|
|
77
77
|
},
|
|
78
78
|
"devDependencies": {
|
|
79
79
|
"@types/node": "^24.0.0",
|
|
@@ -81,18 +81,18 @@
|
|
|
81
81
|
"@types/yauzl": "3.4.0",
|
|
82
82
|
"ttsc": "0.32.1",
|
|
83
83
|
"typescript": "7.0.2",
|
|
84
|
-
"@mpgd/adapter-
|
|
85
|
-
"@mpgd/adapter-
|
|
86
|
-
"@mpgd/adapter-
|
|
87
|
-
"@mpgd/adapter-
|
|
88
|
-
"@mpgd/
|
|
89
|
-
"@mpgd/analytics": "0.4.
|
|
90
|
-
"@mpgd/
|
|
91
|
-
"@mpgd/capacitor-
|
|
92
|
-
"@mpgd/
|
|
93
|
-
"@mpgd/
|
|
94
|
-
"@mpgd/
|
|
95
|
-
"@mpgd/
|
|
84
|
+
"@mpgd/adapter-browser": "0.11.6",
|
|
85
|
+
"@mpgd/adapter-capacitor": "0.12.2",
|
|
86
|
+
"@mpgd/adapter-ait": "0.20.2",
|
|
87
|
+
"@mpgd/adapter-verse8": "0.7.6",
|
|
88
|
+
"@mpgd/bridge": "0.14.3",
|
|
89
|
+
"@mpgd/analytics": "0.4.15",
|
|
90
|
+
"@mpgd/adapter-devvit": "0.13.4",
|
|
91
|
+
"@mpgd/capacitor-play-billing": "0.3.6",
|
|
92
|
+
"@mpgd/capacitor-storekit": "0.3.6",
|
|
93
|
+
"@mpgd/game-runtime": "0.7.6",
|
|
94
|
+
"@mpgd/game-services": "0.27.1",
|
|
95
|
+
"@mpgd/i18n": "0.6.20",
|
|
96
96
|
"@mpgd/lite-shell": "0.1.0"
|
|
97
97
|
},
|
|
98
98
|
"main": "./dist/index.js",
|
|
@@ -57,13 +57,42 @@ left for you.
|
|
|
57
57
|
|
|
58
58
|
Consent records (reminders, 광고성 정보 수신동의, night consent), endpoint
|
|
59
59
|
registration, server reminders, and consent notices go through the game
|
|
60
|
-
backend.
|
|
61
|
-
`
|
|
62
|
-
`
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
game-services
|
|
60
|
+
backend. `createStarterNotificationBackend` in
|
|
61
|
+
`src/platform/notifications.ts` builds the notifications runtime
|
|
62
|
+
(`@mpgd/platform/notifications-runtime`) over the game-services Worker's
|
|
63
|
+
notification routes (`@mpgd/game-services/notifications/client`) when the
|
|
64
|
+
game has both:
|
|
65
|
+
|
|
66
|
+
- a game-services URL, `VITE_MPGD_GAME_SERVICES_URL`, the one the commerce
|
|
67
|
+
client uses (a public https URL in production); and
|
|
68
|
+
- a server-backed session, which the game sets with
|
|
69
|
+
`setStarterServerSession` in `src/platform/serverSession.ts` at the top of
|
|
70
|
+
`src/main.ts`, before `bootstrap()`: the signed-in `{ playerId, sessionId }`
|
|
71
|
+
(`undefined` until the player signs in), a sign-in subscription, and the
|
|
72
|
+
session's `Authorization` header. The commerce client then sends the same
|
|
73
|
+
header. `sessionId` must be the stable session-family id: the `sessionId` your
|
|
74
|
+
identity service answers to the Worker's `authenticateNotificationSession`
|
|
75
|
+
for this session's header, the same across token refreshes (see the kit's
|
|
76
|
+
`docs/CLOUDFLARE_WORKER_DEPLOY.md`, "Sessions"). Do not pass a per-token
|
|
77
|
+
or rotating id: a `GuestSessionCoordinator` view's `sessionId` may rotate
|
|
78
|
+
on refresh, so it is not that id.
|
|
79
|
+
|
|
80
|
+
Without either, it returns `undefined` and the settings screen asks only
|
|
81
|
+
for the OS or host permission (and withdraws a Devvit opt-in the game
|
|
82
|
+
rendered). With them, the runtime registers the device for the signed-in
|
|
83
|
+
player, keeps its registration state in the platform storage, and
|
|
84
|
+
`listConsent` reads the player's consent records (the Worker's
|
|
85
|
+
`GET /consent`). The runtime also loads the Worker's registry entry for
|
|
86
|
+
its target (`GET /providers`) and turns notifications into a configuration
|
|
87
|
+
error when the provider disagrees with it; a Worker it cannot reach is
|
|
88
|
+
logged and the runtime runs without that check.
|
|
89
|
+
|
|
90
|
+
Reddit never gets this backend, even with a URL and a session: a Reddit
|
|
91
|
+
game's notifications backend is its Devvit server, which keeps the opt-in,
|
|
92
|
+
the records, and the sends, and the Worker serves no `reddit` target. The
|
|
93
|
+
Worker serves the Android, iOS, and Apps in Toss providers.
|
|
94
|
+
|
|
95
|
+
The settings screen shows each
|
|
67
96
|
switch from the latest record, read every time it opens and after every
|
|
68
97
|
change, so a choice made on another device shows. A tap reads the records
|
|
69
98
|
again and writes only what changes. It keeps nothing on the device, and
|