@mpgd/platform 0.9.0 → 0.10.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.
@@ -1,5 +1,6 @@
1
1
  import type { PlatformCapabilities, PlatformGateway, PlatformTarget } from './index.js';
2
2
  export declare const platformCapabilityKeys: readonly ["nativeIap", "nativeAds", "rewardedAds", "interstitialAds", "nativeLeaderboard", "remoteLeaderboard", "achievements", "cloudSave", "socialShare", "haptics", "localizedContent"];
3
+ export declare const optionalPlatformCapabilityKeys: readonly ["bannerAds"];
3
4
  export interface PlatformGatewayCapabilityConformanceTransition {
4
5
  readonly update: () => Promise<void> | void;
5
6
  readonly expectedCapabilities: PlatformCapabilities;
@@ -11,6 +11,15 @@ export const platformCapabilityKeys = Object.freeze([
11
11
  'haptics',
12
12
  'localizedContent',
13
13
  ]);
14
+ export const optionalPlatformCapabilityKeys = Object.freeze([
15
+ 'bannerAds',
16
+ ]);
17
+ const allowedPlatformCapabilityKeys = Object.freeze([
18
+ ...platformCapabilityKeys,
19
+ ...optionalPlatformCapabilityKeys,
20
+ ]);
21
+ const allowedPlatformCapabilityKeySet = new Set(allowedPlatformCapabilityKeys);
22
+ const optionalPlatformCapabilityKeySet = new Set(optionalPlatformCapabilityKeys);
14
23
  /**
15
24
  * Runs provider-neutral capability checks against real adapter gateways or
16
25
  * target-configured wrappers. Every read must return a complete boolean
@@ -61,11 +70,15 @@ async function runFixture(fixture) {
61
70
  }
62
71
  function assertCapabilitySnapshot(actual, expected) {
63
72
  const actualKeys = Object.keys(actual).sort();
64
- const expectedKeys = [...platformCapabilityKeys].sort();
65
- assertEqual(JSON.stringify(actualKeys), JSON.stringify(expectedKeys), 'capability snapshots must contain exactly the public capability keys');
66
- for (const key of platformCapabilityKeys) {
67
- assertEqual(typeof actual[key], 'boolean', `capability ${key} must be a boolean`);
68
- assertEqual(actual[key], expected[key], `capability ${key} must match the expected provider state`);
73
+ const missingKeys = platformCapabilityKeys.filter((key) => !Object.hasOwn(actual, key));
74
+ const unexpectedKeys = actualKeys.filter((key) => !allowedPlatformCapabilityKeySet.has(key));
75
+ assertEqual(JSON.stringify({ missingKeys, unexpectedKeys }), JSON.stringify({ missingKeys: [], unexpectedKeys: [] }), 'capability snapshots must contain all required keys and no unknown keys');
76
+ for (const key of allowedPlatformCapabilityKeys) {
77
+ const optional = optionalPlatformCapabilityKeySet.has(key);
78
+ if (!optional || Object.hasOwn(actual, key)) {
79
+ assertEqual(typeof actual[key], 'boolean', `capability ${key} must be a boolean`);
80
+ }
81
+ assertEqual(optional ? (actual[key] ?? false) : actual[key], optional ? (expected[key] ?? false) : expected[key], `capability ${key} must match the expected provider state`);
69
82
  }
70
83
  }
71
84
  function tryMutateSnapshot(snapshot) {
@@ -77,7 +90,8 @@ function tryMutateSnapshot(snapshot) {
77
90
  }
78
91
  }
79
92
  function hasCapabilityDifference(first, second) {
80
- return platformCapabilityKeys.some((key) => first[key] !== second[key]);
93
+ return allowedPlatformCapabilityKeys
94
+ .some((key) => (first[key] ?? false) !== (second[key] ?? false));
81
95
  }
82
96
  function assert(condition, message) {
83
97
  if (!condition) {
package/dist/index.d.ts CHANGED
@@ -47,6 +47,16 @@ export interface RewardedAdResult {
47
47
  export interface InterstitialAdResult {
48
48
  readonly status: 'shown' | 'skipped' | 'unavailable';
49
49
  }
50
+ /**
51
+ * Result of mounting a platform-rendered inline banner into a game-owned surface.
52
+ * The platform host owns the native/SDK attachment; games only reserve and name
53
+ * a surface, so this contract does not leak DOM or provider types. Adapters must
54
+ * release any partial attachment before resolving `unavailable` or `failed`;
55
+ * games only need to unmount a successfully mounted banner.
56
+ */
57
+ export interface BannerAdMountResult {
58
+ readonly status: 'mounted' | 'unavailable' | 'failed';
59
+ }
50
60
  export interface CommerceAdapter {
51
61
  getProducts(): Promise<readonly ProductInfo[]>;
52
62
  purchase(input: {
@@ -68,6 +78,14 @@ export interface AdAdapter {
68
78
  showInterstitial?(input: {
69
79
  readonly placementId: LogicalAdPlacementId;
70
80
  }): Promise<InterstitialAdResult>;
81
+ mountBanner?(input: {
82
+ readonly placementId: LogicalAdPlacementId;
83
+ /** Host-resolvable identifier for an empty, game-owned inline ad surface. */
84
+ readonly surfaceId: string;
85
+ }): Promise<BannerAdMountResult>;
86
+ unmountBanner?(input: {
87
+ readonly surfaceId: string;
88
+ }): Promise<void>;
71
89
  }
72
90
  export type PromotionRewardAvailability = 'available' | 'configuration-required' | 'unsupported';
73
91
  export type PromotionRewardResult = {
@@ -115,6 +133,8 @@ export interface PlatformCapabilities {
115
133
  readonly nativeAds: boolean;
116
134
  readonly rewardedAds: boolean;
117
135
  readonly interstitialAds: boolean;
136
+ /** Optional for compatibility with adapters published before inline ads. */
137
+ readonly bannerAds?: boolean;
118
138
  readonly nativeLeaderboard: boolean;
119
139
  /** A game-owned server leaderboard is available without a native platform surface. */
120
140
  readonly remoteLeaderboard: boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mpgd/platform",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "description": "Shared platform gateway, monetization, ads, leaderboard, identity, lifecycle, and storage contracts for mpgd games.",
5
5
  "license": "MIT",
6
6
  "repository": {