@mpgd/platform 0.22.0 → 0.23.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/capability-conformance.d.ts +6 -5
- package/dist/capability-conformance.js +178 -42
- package/dist/commerce-capabilities.d.ts +0 -6
- package/dist/commerce-capabilities.js +2 -8
- package/dist/commerce-contract.d.ts +12 -2
- package/dist/commerce.d.ts +8 -17
- package/dist/commerce.js +10 -94
- package/dist/index.d.ts +80 -43
- package/dist/index.js +3 -36
- package/dist/notifications-conformance.d.ts +529 -0
- package/dist/notifications-conformance.js +3537 -0
- package/dist/notifications-contract.d.ts +204 -0
- package/dist/notifications-contract.js +1 -0
- package/dist/notifications-policy.d.ts +885 -0
- package/dist/notifications-policy.js +1658 -0
- package/dist/notifications-vectors.d.ts +427 -0
- package/dist/notifications-vectors.js +4949 -0
- package/dist/notifications.d.ts +265 -0
- package/dist/notifications.js +4958 -0
- package/dist/route-vectors.d.ts +23 -0
- package/dist/route-vectors.js +96 -0
- package/dist/routes.d.ts +60 -0
- package/dist/routes.js +209 -0
- package/dist/share-link-conformance.d.ts +47 -0
- package/dist/share-link-conformance.js +100 -0
- package/package.json +28 -4
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import type { AdProvider } from './ads.js';
|
|
2
2
|
import type { CommerceProvider } from './commerce-contract.js';
|
|
3
|
+
import { createUnsupportedCommerceCapabilities } from './commerce-capabilities.js';
|
|
4
|
+
import type { NotificationCapabilities, NotificationProvider } from './notifications-contract.js';
|
|
5
|
+
export { createUnsupportedCommerceCapabilities };
|
|
3
6
|
export type PlatformTarget = 'browser' | 'microsoft-store' | 'android' | 'ios' | 'ait' | 'reddit' | 'verse8' | 'crazygames' | 'oneplay' | 'telegram' | 'tauri' | 'wechat' | 'tiktok';
|
|
4
7
|
export type StarterLogicalProductId = 'COINS_100' | 'COINS_500' | 'REMOVE_ADS';
|
|
5
8
|
export type LogicalProductId = StarterLogicalProductId | (string & Record<never, never>);
|
|
@@ -57,12 +60,23 @@ export interface CommerceDiagnostic extends PlatformOperationFailure {
|
|
|
57
60
|
/** Provider SDK error code, passed through only when it is a safe identifier. */
|
|
58
61
|
readonly providerCode?: string;
|
|
59
62
|
}
|
|
63
|
+
/**
|
|
64
|
+
* Purchase result vocabulary of the adapter engines and of
|
|
65
|
+
* `GameServicesPurchaseResult.purchase`. It is no gateway surface: a gateway
|
|
66
|
+
* sells through `PlatformGateway.commerce`, and nothing here is ever an input
|
|
67
|
+
* to a grant.
|
|
68
|
+
*/
|
|
60
69
|
export interface PurchaseResult {
|
|
61
70
|
readonly status: 'completed' | 'cancelled' | 'pending' | 'failed';
|
|
62
71
|
readonly transactionId?: string;
|
|
63
72
|
readonly entitlementIds: readonly string[];
|
|
64
73
|
readonly evidence?: PlatformEvidenceEnvelope;
|
|
65
|
-
/**
|
|
74
|
+
/**
|
|
75
|
+
* An adapter engine's record that its own authority already wrote a ledger
|
|
76
|
+
* entry. Informational only: no client code reads it to grant, and a
|
|
77
|
+
* provider's `settlement` pointer is verified through the backend by the
|
|
78
|
+
* reconciler like any other evidence.
|
|
79
|
+
*/
|
|
66
80
|
readonly authoritativeGrant?: Readonly<{
|
|
67
81
|
readonly ledgerEntryId: string;
|
|
68
82
|
readonly alreadyProcessed?: boolean;
|
|
@@ -86,19 +100,6 @@ export interface PurchaseSettlement {
|
|
|
86
100
|
/** Client purchase idempotency key the order was started with, when the adapter can link it. */
|
|
87
101
|
readonly idempotencyKey?: string;
|
|
88
102
|
}
|
|
89
|
-
/**
|
|
90
|
-
* Resolve a specific purchase from authoritative checkout or restore evidence.
|
|
91
|
-
* A completed native checkout without authoritativeGrant is still unconfirmed.
|
|
92
|
-
* This is UI status evidence, never permission to credit a wallet locally.
|
|
93
|
-
* The caller must pair `purchase` with its requested `productId` because
|
|
94
|
-
* PurchaseResult itself does not carry a product identifier.
|
|
95
|
-
*/
|
|
96
|
-
export declare function findAuthoritativePurchaseSettlement(input: {
|
|
97
|
-
readonly productId: LogicalProductId;
|
|
98
|
-
readonly transactionId?: string;
|
|
99
|
-
readonly purchase?: PurchaseResult;
|
|
100
|
-
readonly restore?: PurchaseRestoreResult;
|
|
101
|
-
}): PurchaseSettlement | null;
|
|
102
103
|
export interface RewardedAdResult {
|
|
103
104
|
readonly status: 'completed' | 'pending' | 'skipped' | 'unavailable' | 'failed';
|
|
104
105
|
readonly rewardGranted: boolean;
|
|
@@ -123,20 +124,6 @@ export interface PlatformPurchasePresentationEvent {
|
|
|
123
124
|
readonly sequence: number;
|
|
124
125
|
readonly state: 'open' | 'closed' | 'not-started' | 'unknown';
|
|
125
126
|
}
|
|
126
|
-
export interface CommerceAdapter {
|
|
127
|
-
/** Trusted native presentation facts, independent of the purchase grant result. */
|
|
128
|
-
readonly presentation?: {
|
|
129
|
-
subscribe(listener: (event: PlatformPurchasePresentationEvent) => void): () => void;
|
|
130
|
-
};
|
|
131
|
-
getProducts(): Promise<readonly ProductInfo[]>;
|
|
132
|
-
purchase(input: {
|
|
133
|
-
readonly productId: LogicalProductId;
|
|
134
|
-
readonly source: 'shop' | 'stage_fail' | 'result' | 'event';
|
|
135
|
-
readonly idempotencyKey: string;
|
|
136
|
-
}): Promise<PurchaseResult>;
|
|
137
|
-
restore?(): Promise<PurchaseRestoreResult>;
|
|
138
|
-
getEntitlements(): Promise<readonly Entitlement[]>;
|
|
139
|
-
}
|
|
140
127
|
export interface AdAdapter {
|
|
141
128
|
/** Optional versioned provider surface; v1 methods remain compatible. */
|
|
142
129
|
readonly provider?: AdProvider;
|
|
@@ -208,7 +195,7 @@ export interface LeaderboardAdapter {
|
|
|
208
195
|
/** Readiness of an installed platform provider, independent of target policy. */
|
|
209
196
|
export type PlatformProviderAvailability = 'unsupported' | 'configuration-required' | 'action-required' | 'temporarily-unavailable' | 'available';
|
|
210
197
|
/** Existing capabilities and integrations annotated by optional providers. */
|
|
211
|
-
export type PlatformProviderFeature = '
|
|
198
|
+
export type PlatformProviderFeature = 'rewardedAds' | 'interstitialAds' | 'bannerAds' | 'nativeLeaderboard' | 'identityUpgrade' | 'pushNotifications';
|
|
212
199
|
export type CommerceFinishOwner = 'client' | 'server' | 'host' | 'none';
|
|
213
200
|
export type CommerceEvidenceAuthority = 'client-token' | 'host-callback' | 'adapter-settled' | 'server-push';
|
|
214
201
|
export type CommerceAccountBindingPolicy = 'required' | 'optional' | 'host';
|
|
@@ -249,9 +236,6 @@ export interface CommerceCapabilities {
|
|
|
249
236
|
readonly availability: PlatformProviderAvailability;
|
|
250
237
|
}
|
|
251
238
|
export interface PlatformCapabilities {
|
|
252
|
-
readonly nativeIap: boolean;
|
|
253
|
-
/** Optional for adapters published before subscription-specific support. */
|
|
254
|
-
readonly subscriptionIap?: boolean;
|
|
255
239
|
readonly nativeAds: boolean;
|
|
256
240
|
readonly rewardedAds: boolean;
|
|
257
241
|
readonly interstitialAds: boolean;
|
|
@@ -268,11 +252,23 @@ export interface PlatformCapabilities {
|
|
|
268
252
|
/** Explains a false capability without claiming an unavailable SDK works. */
|
|
269
253
|
readonly providerAvailability?: Readonly<Partial<Record<PlatformProviderFeature, PlatformProviderAvailability>>>;
|
|
270
254
|
/**
|
|
271
|
-
*
|
|
272
|
-
*
|
|
273
|
-
*
|
|
255
|
+
* The one source of commerce capabilities: what the gateway's commerce
|
|
256
|
+
* provider sells, who finishes a purchase, and what evidence authorizes a
|
|
257
|
+
* grant. A gateway without a commerce provider reports
|
|
258
|
+
* `createUnsupportedCommerceCapabilities()`. The block must be a fresh object
|
|
259
|
+
* on every read with no unknown keys.
|
|
274
260
|
*/
|
|
275
|
-
readonly commerce
|
|
261
|
+
readonly commerce: CommerceCapabilities;
|
|
262
|
+
/**
|
|
263
|
+
* Optional notifications v2 capability block of the `notificationsV2`
|
|
264
|
+
* provider the gateway exposes: present exactly when the gateway exposes
|
|
265
|
+
* one, raw adapters included. `withTargetAvailability` in
|
|
266
|
+
* `@mpgd/target-config` adds it from that provider and leaves it out while
|
|
267
|
+
* the provider is hidden. When present it must be a fresh object on every
|
|
268
|
+
* read with no unknown keys, and `providerAvailability.pushNotifications`
|
|
269
|
+
* must equal its `availability`.
|
|
270
|
+
*/
|
|
271
|
+
readonly notifications?: NotificationCapabilities;
|
|
276
272
|
}
|
|
277
273
|
export interface PlayerIdentity {
|
|
278
274
|
readonly playerId: string;
|
|
@@ -317,6 +313,18 @@ export interface ShareIntent {
|
|
|
317
313
|
readonly kind: 'daily-result' | 'friend-challenge' | 'invite';
|
|
318
314
|
readonly title: string;
|
|
319
315
|
readonly text: string;
|
|
316
|
+
/**
|
|
317
|
+
* A root-relative link on the game's origin (`/daily?challengeToken=…`) or an
|
|
318
|
+
* absolute `https://` (or `http://`) link. Every adapter whose host builds or
|
|
319
|
+
* receives a share URL checks it with `normalizeShareLink` from
|
|
320
|
+
* `@mpgd/platform/routes`, most through that module's `withShareIntentCheck`,
|
|
321
|
+
* hands the host the normalized link, and answers `unavailable` when the rule
|
|
322
|
+
* refuses it: a link that could name another host, contains a control
|
|
323
|
+
* character, or has another form, such as a page-relative link. Apps in Toss
|
|
324
|
+
* also accepts its own `intoss://<app>` links. An adapter whose host shares
|
|
325
|
+
* no link ignores it: WeChat's card reopens the game with `query` built from
|
|
326
|
+
* `kind` and `payload`.
|
|
327
|
+
*/
|
|
320
328
|
readonly deepLink: string;
|
|
321
329
|
readonly payload?: SharePayload;
|
|
322
330
|
readonly previewImageUrl?: string;
|
|
@@ -357,6 +365,18 @@ export interface NotificationSubscriptionAdapter {
|
|
|
357
365
|
export interface NativeOpenUrlEvent {
|
|
358
366
|
readonly url: string;
|
|
359
367
|
readonly source: 'cold' | 'warm';
|
|
368
|
+
/**
|
|
369
|
+
* For a game link, the URL's path, query, and fragment, normalized with
|
|
370
|
+
* `normalizeRootRelativeRoute` from `@mpgd/platform/routes`. The host is not
|
|
371
|
+
* part of it: `mygame://game/daily` and `https://game.example/daily` give
|
|
372
|
+
* `/daily`, and `mygame://level/3` gives `/3`. Adapters do not deliver a game
|
|
373
|
+
* link whose path that rule refuses, so a router that routes by path opens
|
|
374
|
+
* this route as is. It is absent when the URL has no path to check, such as
|
|
375
|
+
* `mygame:daily` or `mygame://daily`. A router that builds its route from
|
|
376
|
+
* anything else in the URL, such as a custom-scheme host, checks that route
|
|
377
|
+
* with `normalizeRootRelativeRoute` and opens only what it returns.
|
|
378
|
+
*/
|
|
379
|
+
readonly route?: string;
|
|
360
380
|
}
|
|
361
381
|
export interface NativeBackButtonEvent {
|
|
362
382
|
/** Whether the WebView history can navigate back, not the game's own scene stack. */
|
|
@@ -413,7 +433,11 @@ export interface LifecycleAdapter {
|
|
|
413
433
|
* are not guaranteed to run before process termination.
|
|
414
434
|
*/
|
|
415
435
|
onBackButton?(handler: (event: NativeBackButtonEvent) => boolean | Promise<boolean>): () => void;
|
|
416
|
-
/**
|
|
436
|
+
/**
|
|
437
|
+
* Game links and OAuth redirects use separate callbacks. A game link is
|
|
438
|
+
* untrusted input: open its checked `route`, or check any route built from
|
|
439
|
+
* `url` with `normalizeRootRelativeRoute` (see `NativeOpenUrlEvent.route`).
|
|
440
|
+
*/
|
|
417
441
|
onGameUrlOpen?(callback: (event: NativeOpenUrlEvent) => void): () => void;
|
|
418
442
|
onOAuthRedirect?(callback: (event: NativeOpenUrlEvent) => void): () => void;
|
|
419
443
|
getInitialGameUrl?(): Promise<NativeOpenUrlEvent | null>;
|
|
@@ -481,14 +505,14 @@ export interface PlatformGateway {
|
|
|
481
505
|
readonly gameLoading?: GameLoadingAdapter;
|
|
482
506
|
getCapabilities(): Promise<PlatformCapabilities>;
|
|
483
507
|
readonly identity: IdentityAdapter;
|
|
484
|
-
readonly commerce: CommerceAdapter;
|
|
485
508
|
/**
|
|
486
|
-
*
|
|
487
|
-
*
|
|
488
|
-
*
|
|
489
|
-
*
|
|
509
|
+
* The commerce provider (`@mpgd/platform/commerce`), the only commerce
|
|
510
|
+
* surface of a gateway. Games purchase through `@mpgd/game-services`, whose
|
|
511
|
+
* reconciler drives it; a scene never calls it directly. Absent on gateways
|
|
512
|
+
* whose target sells nothing, which then report
|
|
513
|
+
* `createUnsupportedCommerceCapabilities()`.
|
|
490
514
|
*/
|
|
491
|
-
readonly
|
|
515
|
+
readonly commerce?: CommerceProvider;
|
|
492
516
|
readonly ads: AdAdapter;
|
|
493
517
|
readonly leaderboard: LeaderboardAdapter;
|
|
494
518
|
readonly lifecycle: LifecycleAdapter;
|
|
@@ -499,6 +523,19 @@ export interface PlatformGateway {
|
|
|
499
523
|
readonly presentation?: PresentationAdapter;
|
|
500
524
|
readonly sharing?: ShareAdapter;
|
|
501
525
|
readonly notifications?: NotificationSubscriptionAdapter;
|
|
526
|
+
/**
|
|
527
|
+
* Optional notifications v2 provider (`@mpgd/platform/notifications`).
|
|
528
|
+
* Transitional: adapters expose it next to the v1 `notifications` adapter
|
|
529
|
+
* until the major release that removes v1. Games do not call it directly:
|
|
530
|
+
* the game-services client composes it with the backend into the
|
|
531
|
+
* notifications runtime, and `toNotificationSubscriptionAdapter` serves v1
|
|
532
|
+
* callers from that runtime with the topics resolved for the target. Absent
|
|
533
|
+
* on gateways whose target has no v2 provider. `withTargetAvailability` in
|
|
534
|
+
* `@mpgd/target-config` exposes it only while `integrations.notifications`
|
|
535
|
+
* lets the game call notifications, as it does the v1 adapter, and then
|
|
536
|
+
* adds `PlatformCapabilities.notifications` from it.
|
|
537
|
+
*/
|
|
538
|
+
readonly notificationsV2?: NotificationProvider;
|
|
502
539
|
readonly promotions?: PromotionRewardAdapter;
|
|
503
540
|
readonly gameActivity?: GameActivityAdapter;
|
|
504
541
|
readonly gameSettings?: GameSettingsAdapter;
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { createUnsupportedCommerceCapabilities } from './commerce-capabilities.js';
|
|
2
|
+
export { createUnsupportedCommerceCapabilities };
|
|
1
3
|
/**
|
|
2
4
|
* Provider-neutral error raised when a platform bridge rejects an operation.
|
|
3
5
|
*
|
|
@@ -41,41 +43,6 @@ function isPlatformOperationCode(code) {
|
|
|
41
43
|
&& code.length <= 128
|
|
42
44
|
&& /^[A-Z][A-Z0-9_:-]*$/u.test(code);
|
|
43
45
|
}
|
|
44
|
-
/**
|
|
45
|
-
* Resolve a specific purchase from authoritative checkout or restore evidence.
|
|
46
|
-
* A completed native checkout without authoritativeGrant is still unconfirmed.
|
|
47
|
-
* This is UI status evidence, never permission to credit a wallet locally.
|
|
48
|
-
* The caller must pair `purchase` with its requested `productId` because
|
|
49
|
-
* PurchaseResult itself does not carry a product identifier.
|
|
50
|
-
*/
|
|
51
|
-
export function findAuthoritativePurchaseSettlement(input) {
|
|
52
|
-
const effectiveTransactionId = input.transactionId ?? input.purchase?.transactionId;
|
|
53
|
-
const matchingRestored = input.restore?.settledPurchases?.filter((settlement) => settlement.productId === input.productId
|
|
54
|
-
&& (effectiveTransactionId === undefined || settlement.transactionId === effectiveTransactionId));
|
|
55
|
-
// Without an order ID, multiple historical outcomes are ambiguous.
|
|
56
|
-
if (matchingRestored !== undefined && matchingRestored.length > 1) {
|
|
57
|
-
return null;
|
|
58
|
-
}
|
|
59
|
-
if (matchingRestored?.[0] !== undefined) {
|
|
60
|
-
return matchingRestored[0];
|
|
61
|
-
}
|
|
62
|
-
const purchase = input.purchase;
|
|
63
|
-
if (purchase?.status !== 'completed'
|
|
64
|
-
|| purchase.transactionId === undefined
|
|
65
|
-
|| purchase.authoritativeGrant === undefined
|
|
66
|
-
|| (effectiveTransactionId !== undefined && effectiveTransactionId !== purchase.transactionId)) {
|
|
67
|
-
return null;
|
|
68
|
-
}
|
|
69
|
-
return {
|
|
70
|
-
transactionId: purchase.transactionId,
|
|
71
|
-
productId: input.productId,
|
|
72
|
-
status: 'granted',
|
|
73
|
-
ledgerEntryId: purchase.authoritativeGrant.ledgerEntryId,
|
|
74
|
-
...(purchase.authoritativeGrant.alreadyProcessed === undefined
|
|
75
|
-
? {}
|
|
76
|
-
: { alreadyProcessed: purchase.authoritativeGrant.alreadyProcessed }),
|
|
77
|
-
};
|
|
78
|
-
}
|
|
79
46
|
export function resolveShareCompletion(result) {
|
|
80
47
|
if (result.status !== 'shared') {
|
|
81
48
|
return undefined;
|
|
@@ -87,7 +54,6 @@ export function isShareCompleted(result) {
|
|
|
87
54
|
}
|
|
88
55
|
export function createUnsupportedCapabilities() {
|
|
89
56
|
return {
|
|
90
|
-
nativeIap: false,
|
|
91
57
|
nativeAds: false,
|
|
92
58
|
rewardedAds: false,
|
|
93
59
|
interstitialAds: false,
|
|
@@ -98,5 +64,6 @@ export function createUnsupportedCapabilities() {
|
|
|
98
64
|
socialShare: false,
|
|
99
65
|
haptics: false,
|
|
100
66
|
localizedContent: false,
|
|
67
|
+
commerce: createUnsupportedCommerceCapabilities(),
|
|
101
68
|
};
|
|
102
69
|
}
|