@mpgd/game-services 0.16.0 → 0.17.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/admob-client-reward.d.ts +27 -0
- package/dist/admob-client-reward.js +84 -0
- package/dist/admob-ssv.d.ts +2 -0
- package/dist/admob-ssv.js +5 -0
- package/dist/app-store-recovery.d.ts +38 -0
- package/dist/app-store-recovery.js +271 -0
- package/dist/app-store-verifier.d.ts +7 -0
- package/dist/app-store-verifier.js +41 -3
- package/dist/authoritative-purchase.d.ts +9 -0
- package/dist/authoritative-purchase.js +10 -0
- package/dist/client.d.ts +2 -0
- package/dist/client.js +17 -18
- package/dist/google-play-publisher-client.d.ts +11 -0
- package/dist/google-play-publisher-client.js +113 -0
- package/dist/google-play-purchase.d.ts +2 -0
- package/dist/google-play-purchase.js +21 -7
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/monetization-recovery.d.ts +63 -0
- package/dist/monetization-recovery.js +632 -0
- package/dist/operation-progress.d.ts +7 -1
- package/dist/operation-progress.js +23 -0
- package/dist/operation-results.d.ts +4 -0
- package/dist/operations.d.ts +1 -1
- package/dist/runtime.d.ts +4 -0
- package/dist/runtime.js +29 -14
- package/dist/server.d.ts +20 -2
- package/dist/server.js +246 -86
- package/dist/types.d.ts +12 -0
- package/dist/types.js +81 -27
- package/dist/validation.d.ts +18 -0
- package/dist/validation.js +26 -0
- package/package.json +19 -6
package/dist/types.js
CHANGED
|
@@ -1,18 +1,26 @@
|
|
|
1
|
+
import { assertOwnEnumerablePropertyLimit, gameServicesRequestLimits, toGameServicesRequestValidationError, } from './validation.js';
|
|
2
|
+
/**
|
|
3
|
+
* Validates an untrusted purchase verification request. Failures throw
|
|
4
|
+
* `GameServicesRequestValidationError` so transports can answer with a stable 400.
|
|
5
|
+
*/
|
|
1
6
|
export function assertVerifyPurchaseRequest(input) {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
return validateRequest(() => {
|
|
8
|
+
assertRecord(input, 'VerifyPurchaseRequest');
|
|
9
|
+
assertStoreTarget(input.target);
|
|
10
|
+
assertOptionalGameServicesDeploymentTarget(input.deploymentTarget);
|
|
11
|
+
assertRequestString(input.playerId, 'playerId');
|
|
12
|
+
assertRequestString(input.productId, 'productId');
|
|
13
|
+
assertRequestString(input.platformTransactionId, 'platformTransactionId');
|
|
14
|
+
assertRequestString(input.idempotencyKey, 'idempotencyKey');
|
|
15
|
+
assertRequestString(input.purchasedAt, 'purchasedAt');
|
|
16
|
+
assertOptionalEvidenceEnvelope(input.evidence);
|
|
17
|
+
return input;
|
|
18
|
+
});
|
|
12
19
|
}
|
|
13
20
|
export function assertVerifyPurchaseResponse(input) {
|
|
14
21
|
assertRecord(input, 'VerifyPurchaseResponse');
|
|
15
22
|
assertBoolean(input.verified, 'verified');
|
|
23
|
+
assertOptionalNonGrantDisposition(input.disposition, input.verified);
|
|
16
24
|
assertOptionalNonEmptyString(input.ledgerEntryId, 'ledgerEntryId');
|
|
17
25
|
assertBoolean(input.alreadyProcessed, 'alreadyProcessed');
|
|
18
26
|
assertOptionalNonEmptyString(input.reason, 'reason');
|
|
@@ -42,35 +50,49 @@ export function assertPurchaseGrantFinalization(input) {
|
|
|
42
50
|
}
|
|
43
51
|
return input;
|
|
44
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* Validates an untrusted ad reward claim. Failures throw
|
|
55
|
+
* `GameServicesRequestValidationError` so transports can answer with a stable 400.
|
|
56
|
+
*/
|
|
45
57
|
export function assertClaimAdRewardRequest(input) {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
58
|
+
return validateRequest(() => {
|
|
59
|
+
assertRecord(input, 'ClaimAdRewardRequest');
|
|
60
|
+
assertAdRewardTarget(input.target);
|
|
61
|
+
assertOptionalGameServicesDeploymentTarget(input.deploymentTarget);
|
|
62
|
+
assertRequestString(input.playerId, 'playerId');
|
|
63
|
+
assertRequestString(input.placementId, 'placementId');
|
|
64
|
+
assertOptionalRequestString(input.platformImpressionId, 'platformImpressionId');
|
|
65
|
+
assertRequestString(input.idempotencyKey, 'idempotencyKey');
|
|
66
|
+
assertRequestString(input.completedAt, 'completedAt');
|
|
67
|
+
assertOptionalEvidenceEnvelope(input.evidence);
|
|
68
|
+
return input;
|
|
69
|
+
});
|
|
56
70
|
}
|
|
57
71
|
export function assertClaimAdRewardResponse(input) {
|
|
58
72
|
assertRecord(input, 'ClaimAdRewardResponse');
|
|
59
73
|
assertBoolean(input.granted, 'granted');
|
|
74
|
+
assertOptionalNonGrantDisposition(input.disposition, input.granted);
|
|
60
75
|
assertOptionalNonEmptyString(input.ledgerEntryId, 'ledgerEntryId');
|
|
61
76
|
assertBoolean(input.alreadyProcessed, 'alreadyProcessed');
|
|
62
77
|
assertOptionalNonEmptyString(input.reason, 'reason');
|
|
63
78
|
return input;
|
|
64
79
|
}
|
|
80
|
+
/**
|
|
81
|
+
* Validates an untrusted leaderboard score submission. Failures throw
|
|
82
|
+
* `GameServicesRequestValidationError` so transports can answer with a stable 400.
|
|
83
|
+
*/
|
|
65
84
|
export function assertRecordLeaderboardScoreRequest(input) {
|
|
85
|
+
return validateRequest(() => validateRecordLeaderboardScoreRequest(input));
|
|
86
|
+
}
|
|
87
|
+
function validateRecordLeaderboardScoreRequest(input) {
|
|
66
88
|
assertRecord(input, 'RecordLeaderboardScoreRequest');
|
|
67
89
|
assertLeaderboardTarget(input.target);
|
|
68
|
-
|
|
69
|
-
|
|
90
|
+
assertRequestString(input.playerId, 'playerId');
|
|
91
|
+
assertRequestString(input.leaderboardId, 'leaderboardId');
|
|
70
92
|
assertFiniteNumber(input.score, 'score');
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
93
|
+
assertRequestString(input.runId, 'runId');
|
|
94
|
+
assertRequestString(input.submittedAt, 'submittedAt');
|
|
95
|
+
assertOptionalRequestString(input.platformSubmissionId, 'platformSubmissionId');
|
|
74
96
|
return input;
|
|
75
97
|
}
|
|
76
98
|
export function assertRecordLeaderboardScoreResponse(input) {
|
|
@@ -117,7 +139,7 @@ export function assertProductGrantTransaction(input) {
|
|
|
117
139
|
return input;
|
|
118
140
|
}
|
|
119
141
|
export function assertLeaderboardScoreTransaction(input) {
|
|
120
|
-
|
|
142
|
+
validateRecordLeaderboardScoreRequest(input);
|
|
121
143
|
assertNonEmptyString(input.ledgerEntryId, 'ledgerEntryId');
|
|
122
144
|
assertNonEmptyString(input.recordedAt, 'recordedAt');
|
|
123
145
|
return input;
|
|
@@ -178,11 +200,31 @@ function assertLeaderboardTarget(input) {
|
|
|
178
200
|
throw new Error('target must be browser, microsoft-store, android, ios, ait, or reddit.');
|
|
179
201
|
}
|
|
180
202
|
}
|
|
203
|
+
function validateRequest(validate) {
|
|
204
|
+
try {
|
|
205
|
+
return validate();
|
|
206
|
+
}
|
|
207
|
+
catch (error) {
|
|
208
|
+
throw toGameServicesRequestValidationError(error);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
181
211
|
function assertNonEmptyString(input, label) {
|
|
182
212
|
if (typeof input !== 'string' || input.length === 0) {
|
|
183
213
|
throw new Error(`${label} must be a non-empty string.`);
|
|
184
214
|
}
|
|
185
215
|
}
|
|
216
|
+
/** Client-supplied identifiers feed ledger keys, so they are bounded as well as non-empty. */
|
|
217
|
+
function assertRequestString(input, label) {
|
|
218
|
+
assertNonEmptyString(input, label);
|
|
219
|
+
if (input.length > gameServicesRequestLimits.maxStringLength) {
|
|
220
|
+
throw new Error(`${label} must not exceed ${String(gameServicesRequestLimits.maxStringLength)} characters.`);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
function assertOptionalRequestString(input, label) {
|
|
224
|
+
if (input !== undefined) {
|
|
225
|
+
assertRequestString(input, label);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
186
228
|
function assertOptionalNonEmptyString(input, label) {
|
|
187
229
|
if (input !== undefined) {
|
|
188
230
|
assertNonEmptyString(input, label);
|
|
@@ -198,6 +240,17 @@ function assertBoolean(input, label) {
|
|
|
198
240
|
throw new Error(`${label} must be a boolean.`);
|
|
199
241
|
}
|
|
200
242
|
}
|
|
243
|
+
function assertOptionalNonGrantDisposition(input, granted) {
|
|
244
|
+
if (input === undefined) {
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
if (input !== 'pending' && input !== 'rejected') {
|
|
248
|
+
throw new Error('disposition must be pending or rejected.');
|
|
249
|
+
}
|
|
250
|
+
if (granted) {
|
|
251
|
+
throw new Error('A granted result cannot have a non-grant disposition.');
|
|
252
|
+
}
|
|
253
|
+
}
|
|
201
254
|
function assertPurchaseGrantFinalizationAction(input) {
|
|
202
255
|
if (input !== 'acknowledge'
|
|
203
256
|
&& input !== 'consume'
|
|
@@ -248,6 +301,7 @@ function assertOptionalEvidenceEnvelope(input) {
|
|
|
248
301
|
return;
|
|
249
302
|
}
|
|
250
303
|
assertRecord(input, 'evidence');
|
|
251
|
-
|
|
304
|
+
assertRequestString(input.schema, 'evidence.schema');
|
|
252
305
|
assertPayload(input.payload);
|
|
306
|
+
assertOwnEnumerablePropertyLimit(input.payload, gameServicesRequestLimits.maxEvidencePayloadEntries, 'evidence.payload');
|
|
253
307
|
}
|
package/dist/validation.d.ts
CHANGED
|
@@ -1 +1,19 @@
|
|
|
1
|
+
/** Hard limits applied to untrusted client requests before they reach ledger keys. */
|
|
2
|
+
export declare const gameServicesRequestLimits: {
|
|
3
|
+
/** Maximum length of any identifier or timestamp string in a client request. */
|
|
4
|
+
readonly maxStringLength: 256;
|
|
5
|
+
/** Maximum number of entries accepted inside a platform evidence payload. */
|
|
6
|
+
readonly maxEvidencePayloadEntries: 64;
|
|
7
|
+
/** Maximum request body size accepted by the HTTP and oRPC fetch handlers. */
|
|
8
|
+
readonly maxBodyBytes: number;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* A client-supplied request failed validation. Handlers map this error to a 400 response
|
|
12
|
+
* with the stable `INVALID_REQUEST` code; every other error is treated as internal.
|
|
13
|
+
*/
|
|
14
|
+
export declare class GameServicesRequestValidationError extends Error {
|
|
15
|
+
readonly code = "INVALID_REQUEST";
|
|
16
|
+
constructor(message: string);
|
|
17
|
+
}
|
|
18
|
+
export declare function toGameServicesRequestValidationError(error: unknown): GameServicesRequestValidationError;
|
|
1
19
|
export declare function assertOwnEnumerablePropertyLimit(input: Record<string, unknown>, maximum: number, label: string): void;
|
package/dist/validation.js
CHANGED
|
@@ -1,3 +1,29 @@
|
|
|
1
|
+
/** Hard limits applied to untrusted client requests before they reach ledger keys. */
|
|
2
|
+
export const gameServicesRequestLimits = {
|
|
3
|
+
/** Maximum length of any identifier or timestamp string in a client request. */
|
|
4
|
+
maxStringLength: 256,
|
|
5
|
+
/** Maximum number of entries accepted inside a platform evidence payload. */
|
|
6
|
+
maxEvidencePayloadEntries: 64,
|
|
7
|
+
/** Maximum request body size accepted by the HTTP and oRPC fetch handlers. */
|
|
8
|
+
maxBodyBytes: 64 * 1024,
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* A client-supplied request failed validation. Handlers map this error to a 400 response
|
|
12
|
+
* with the stable `INVALID_REQUEST` code; every other error is treated as internal.
|
|
13
|
+
*/
|
|
14
|
+
export class GameServicesRequestValidationError extends Error {
|
|
15
|
+
code = 'INVALID_REQUEST';
|
|
16
|
+
constructor(message) {
|
|
17
|
+
super(message);
|
|
18
|
+
this.name = 'GameServicesRequestValidationError';
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
export function toGameServicesRequestValidationError(error) {
|
|
22
|
+
if (error instanceof GameServicesRequestValidationError) {
|
|
23
|
+
return error;
|
|
24
|
+
}
|
|
25
|
+
return new GameServicesRequestValidationError(error instanceof Error ? error.message : 'Request validation failed.');
|
|
26
|
+
}
|
|
1
27
|
export function assertOwnEnumerablePropertyLimit(input, maximum, label) {
|
|
2
28
|
let propertyCount = 0;
|
|
3
29
|
for (const key in input) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mpgd/game-services",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.1",
|
|
4
4
|
"description": "Client, contract, server, store, and test helpers for authoritative mpgd game services.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -29,6 +29,10 @@
|
|
|
29
29
|
"types": "./dist/index.d.ts",
|
|
30
30
|
"default": "./dist/index.js"
|
|
31
31
|
},
|
|
32
|
+
"./admob-client-reward": {
|
|
33
|
+
"types": "./dist/admob-client-reward.d.ts",
|
|
34
|
+
"default": "./dist/admob-client-reward.js"
|
|
35
|
+
},
|
|
32
36
|
"./admob-ssv": {
|
|
33
37
|
"types": "./dist/admob-ssv.d.ts",
|
|
34
38
|
"default": "./dist/admob-ssv.js"
|
|
@@ -41,6 +45,10 @@
|
|
|
41
45
|
"types": "./dist/app-store-verifier.d.ts",
|
|
42
46
|
"default": "./dist/app-store-verifier.js"
|
|
43
47
|
},
|
|
48
|
+
"./app-store-recovery": {
|
|
49
|
+
"types": "./dist/app-store-recovery.d.ts",
|
|
50
|
+
"default": "./dist/app-store-recovery.js"
|
|
51
|
+
},
|
|
44
52
|
"./app-store-verifier-conformance": {
|
|
45
53
|
"types": "./dist/app-store-verifier-conformance.d.ts",
|
|
46
54
|
"default": "./dist/app-store-verifier-conformance.js"
|
|
@@ -73,6 +81,10 @@
|
|
|
73
81
|
"types": "./dist/google-play-purchase.d.ts",
|
|
74
82
|
"default": "./dist/google-play-purchase.js"
|
|
75
83
|
},
|
|
84
|
+
"./google-play-publisher-client": {
|
|
85
|
+
"types": "./dist/google-play-publisher-client.d.ts",
|
|
86
|
+
"default": "./dist/google-play-publisher-client.js"
|
|
87
|
+
},
|
|
76
88
|
"./guest-session": {
|
|
77
89
|
"types": "./dist/guest-session.d.ts",
|
|
78
90
|
"default": "./dist/guest-session.js"
|
|
@@ -142,9 +154,9 @@
|
|
|
142
154
|
"@orpc/client": "2.0.0-beta.14",
|
|
143
155
|
"@orpc/contract": "2.0.0-beta.14",
|
|
144
156
|
"@orpc/server": "2.0.0-beta.14",
|
|
145
|
-
"@mpgd/analytics": "0.
|
|
146
|
-
"@mpgd/
|
|
147
|
-
"@mpgd/
|
|
157
|
+
"@mpgd/analytics": "0.4.0",
|
|
158
|
+
"@mpgd/catalog": "0.7.6",
|
|
159
|
+
"@mpgd/platform": "0.14.0"
|
|
148
160
|
},
|
|
149
161
|
"devDependencies": {
|
|
150
162
|
"@opentelemetry/api": "1.9.0",
|
|
@@ -163,7 +175,8 @@
|
|
|
163
175
|
"fix": "ttsc fix",
|
|
164
176
|
"build": "cd ../.. && node tools/run-ttsx.mjs tools/package/build-packages.ts @mpgd/game-services",
|
|
165
177
|
"test:packed-transport": "pnpm build && node test/runtime-transport-package-import.mjs",
|
|
166
|
-
"test:unit": "cd ../.. && node tools/run-ttsx.mjs packages/game-services/src/client.test.ts && node tools/run-ttsx.mjs packages/game-services/src/client-progress.test.ts && node tools/run-ttsx.mjs packages/game-services/src/runtime.test.ts && node tools/run-ttsx.mjs packages/game-services/src/types.test.ts && node tools/run-ttsx.mjs packages/game-services/src/server.test.ts && node tools/run-ttsx.mjs packages/game-services/src/google-play-purchase.test.ts && node tools/run-ttsx.mjs packages/game-services/src/microsoft-store-purchase.test.ts && node tools/run-ttsx.mjs packages/game-services/src/microsoft-store-identity.test.ts && node tools/run-ttsx.mjs packages/game-services/src/platform-order.test.ts && node tools/run-ttsx.mjs packages/game-services/src/progress-link.test.ts && node tools/run-ttsx.mjs packages/game-services/src/notification-delivery.test.ts && node tools/run-ttsx.mjs packages/game-services/src/admob-ssv.test.ts && node tools/run-ttsx.mjs packages/game-services/src/app-store-verifier.test.ts && node tools/run-ttsx.mjs packages/game-services/src/apps-in-toss-evidence-verification-conformance.test.ts && node tools/run-ttsx.mjs packages/game-services/src/apps-in-toss-partner-api.test.ts && node tools/run-ttsx.mjs packages/game-services/src/verified-leaderboard.test.ts && node tools/run-ttsx.mjs packages/game-services/src/verified-leaderboard-conformance.test.ts && node tools/run-ttsx.mjs packages/game-services/src/verified-leaderboard-durability-conformance.test.ts && node tools/run-ttsx.mjs packages/game-services/src/verified-leaderboard-transport.test.ts && node tools/run-ttsx.mjs packages/game-services/src/guest-session.test.ts",
|
|
167
|
-
"test": "
|
|
178
|
+
"test:unit": "cd ../.. && node tools/run-ttsx.mjs packages/game-services/src/client.test.ts && node tools/run-ttsx.mjs packages/game-services/src/client-progress.test.ts && node tools/run-ttsx.mjs packages/game-services/src/runtime.test.ts && node tools/run-ttsx.mjs packages/game-services/src/types.test.ts && node tools/run-ttsx.mjs packages/game-services/src/server.test.ts && node tools/run-ttsx.mjs packages/game-services/src/google-play-purchase.test.ts && node tools/run-ttsx.mjs packages/game-services/src/google-play-publisher-client.test.ts && node tools/run-ttsx.mjs packages/game-services/src/microsoft-store-purchase.test.ts && node tools/run-ttsx.mjs packages/game-services/src/microsoft-store-identity.test.ts && node tools/run-ttsx.mjs packages/game-services/src/platform-order.test.ts && node tools/run-ttsx.mjs packages/game-services/src/progress-link.test.ts && node tools/run-ttsx.mjs packages/game-services/src/notification-delivery.test.ts && node tools/run-ttsx.mjs packages/game-services/src/admob-ssv.test.ts && node tools/run-ttsx.mjs packages/game-services/src/app-store-verifier.test.ts && node tools/run-ttsx.mjs packages/game-services/src/apps-in-toss-evidence-verification-conformance.test.ts && node tools/run-ttsx.mjs packages/game-services/src/apps-in-toss-partner-api.test.ts && node tools/run-ttsx.mjs packages/game-services/src/verified-leaderboard.test.ts && node tools/run-ttsx.mjs packages/game-services/src/verified-leaderboard-conformance.test.ts && node tools/run-ttsx.mjs packages/game-services/src/verified-leaderboard-durability-conformance.test.ts && node tools/run-ttsx.mjs packages/game-services/src/verified-leaderboard-transport.test.ts && node tools/run-ttsx.mjs packages/game-services/src/guest-session.test.ts",
|
|
179
|
+
"test:recovery": "cd ../.. && node tools/run-ttsx.mjs packages/game-services/src/monetization-recovery.test.ts && node tools/run-ttsx.mjs packages/game-services/src/app-store-recovery.test.ts",
|
|
180
|
+
"test": "pnpm test:unit && pnpm test:recovery && pnpm test:packed-transport"
|
|
168
181
|
}
|
|
169
182
|
}
|