@mpgd/target-config 0.1.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.
@@ -0,0 +1,106 @@
1
+ import type { PlatformCapabilities, PlatformGateway } from '@mpgd/platform';
2
+ import type { EffectiveTargetConfig } from './effective';
3
+ export type PlatformFeature = 'iap' | 'rewardedAds' | 'interstitialAds' | 'leaderboard' | 'localization';
4
+ export type AdPlacementType = 'rewarded' | 'interstitial';
5
+ type PlatformConfigTarget = PlatformGateway['target'];
6
+ export type TargetRuntimeKind = 'web-preview' | 'capacitor-android' | 'capacitor-ios' | 'apps-in-toss';
7
+ export type ReleaseProfile = 'web-preview' | 'google-play' | 'app-store' | 'apps-in-toss';
8
+ export type StorageSupport = 'local' | 'native' | 'none';
9
+ export interface TargetFeatureConfig {
10
+ readonly iap: boolean;
11
+ readonly rewardedAds: boolean;
12
+ readonly interstitialAds: boolean;
13
+ readonly leaderboard: boolean;
14
+ readonly localization: boolean;
15
+ }
16
+ export interface TargetCapabilityConfig {
17
+ readonly storage: StorageSupport;
18
+ readonly localization: boolean;
19
+ }
20
+ export interface TargetMonetizationConfig {
21
+ readonly iap: boolean;
22
+ readonly rewardedAds: boolean;
23
+ readonly interstitialAds: boolean;
24
+ }
25
+ export interface TargetLeaderboardConfig {
26
+ readonly native: boolean;
27
+ }
28
+ export interface TargetReleaseConfig {
29
+ readonly profile: ReleaseProfile;
30
+ }
31
+ export interface TargetPolicyRestrictions {
32
+ readonly externalPaymentAllowed: boolean;
33
+ readonly remoteExecutableCodeAllowed: boolean;
34
+ readonly installOtherAppCTAAllowed: boolean;
35
+ readonly requiresStoreReview: boolean;
36
+ readonly requiresAitReview: boolean;
37
+ }
38
+ export interface TargetConfig {
39
+ readonly runtime: TargetRuntimeKind;
40
+ readonly features: TargetFeatureConfig;
41
+ readonly capabilities: TargetCapabilityConfig;
42
+ readonly monetization: TargetMonetizationConfig;
43
+ readonly leaderboard: TargetLeaderboardConfig;
44
+ readonly release: TargetReleaseConfig;
45
+ readonly policy: TargetPolicyRestrictions;
46
+ }
47
+ export interface TargetConfigMatrix {
48
+ readonly version: string;
49
+ readonly targets: Record<string, TargetConfig>;
50
+ }
51
+ export type FeatureAvailabilityReason = 'available' | 'target-disabled' | 'capability-unsupported';
52
+ export interface FeatureAvailability {
53
+ readonly feature: PlatformFeature;
54
+ readonly enabled: boolean;
55
+ readonly targetEnabled: boolean;
56
+ readonly capabilitySupported: boolean;
57
+ readonly reason: FeatureAvailabilityReason;
58
+ }
59
+ export interface AdPlacementDefinition {
60
+ readonly id: string;
61
+ readonly type: AdPlacementType;
62
+ }
63
+ export interface AdPlacementAvailability {
64
+ readonly id: string;
65
+ readonly type: AdPlacementType;
66
+ readonly enabled: boolean;
67
+ readonly reason: FeatureAvailabilityReason;
68
+ }
69
+ export interface TargetRuntimeSnapshot {
70
+ readonly target: PlatformConfigTarget;
71
+ readonly configTarget: string;
72
+ readonly config: TargetConfig;
73
+ readonly effectiveConfig?: EffectiveTargetConfig;
74
+ readonly capabilities: PlatformCapabilities;
75
+ readonly features: Record<PlatformFeature, FeatureAvailability>;
76
+ readonly adPlacements: readonly AdPlacementAvailability[];
77
+ }
78
+ export interface TargetAvailabilityOptions {
79
+ readonly configTarget?: string;
80
+ readonly effectiveConfig?: EffectiveTargetConfig;
81
+ readonly adPlacements?: readonly AdPlacementDefinition[];
82
+ readonly resolveAdPlacementType?: (placementId: string) => AdPlacementType | undefined;
83
+ }
84
+ export interface TargetConfiguredGateway extends PlatformGateway {
85
+ readonly configTarget: string;
86
+ readonly targetConfig: TargetConfig;
87
+ readonly effectiveConfig?: EffectiveTargetConfig;
88
+ getTargetRuntime(): Promise<TargetRuntimeSnapshot>;
89
+ }
90
+ export declare const platformFeatures: readonly ["iap", "rewardedAds", "interstitialAds", "leaderboard", "localization"];
91
+ export declare function targetConfigKeyForPlatform(target: PlatformConfigTarget): string;
92
+ export declare function getTargetConfig(matrix: TargetConfigMatrix, target: PlatformConfigTarget | string): TargetConfig;
93
+ export declare function isPlatformFeatureEnabled(config: TargetConfig, feature: PlatformFeature): boolean;
94
+ export declare function applyTargetConfigToCapabilities(capabilities: PlatformCapabilities, config: TargetConfig): PlatformCapabilities;
95
+ export declare function getFeatureAvailability(feature: PlatformFeature, config: TargetConfig, capabilities: PlatformCapabilities): FeatureAvailability;
96
+ export declare function createTargetRuntimeSnapshot(input: {
97
+ readonly target: PlatformConfigTarget;
98
+ readonly configTarget?: string;
99
+ readonly config: TargetConfig;
100
+ readonly effectiveConfig?: EffectiveTargetConfig;
101
+ readonly capabilities: PlatformCapabilities;
102
+ readonly adPlacements?: readonly AdPlacementDefinition[];
103
+ }): TargetRuntimeSnapshot;
104
+ export declare function withTargetAvailability(gateway: PlatformGateway, config: TargetConfig, options?: TargetAvailabilityOptions): TargetConfiguredGateway;
105
+ export declare function isTargetConfiguredGateway(gateway: PlatformGateway): gateway is TargetConfiguredGateway;
106
+ export {};
@@ -0,0 +1,194 @@
1
+ export const platformFeatures = [
2
+ 'iap',
3
+ 'rewardedAds',
4
+ 'interstitialAds',
5
+ 'leaderboard',
6
+ 'localization',
7
+ ];
8
+ export function targetConfigKeyForPlatform(target) {
9
+ return target === 'browser' ? 'web-preview' : target;
10
+ }
11
+ export function getTargetConfig(matrix, target) {
12
+ const config = matrix.targets[target];
13
+ if (config === undefined) {
14
+ throw new Error(`Missing target config for target: ${target}`);
15
+ }
16
+ return config;
17
+ }
18
+ export function isPlatformFeatureEnabled(config, feature) {
19
+ return config.features[feature];
20
+ }
21
+ export function applyTargetConfigToCapabilities(capabilities, config) {
22
+ return {
23
+ ...capabilities,
24
+ nativeIap: capabilities.nativeIap && config.features.iap,
25
+ nativeAds: capabilities.nativeAds &&
26
+ (config.features.rewardedAds || config.features.interstitialAds),
27
+ rewardedAds: capabilities.rewardedAds && config.features.rewardedAds,
28
+ interstitialAds: capabilities.interstitialAds && config.features.interstitialAds,
29
+ nativeLeaderboard: capabilities.nativeLeaderboard && config.features.leaderboard,
30
+ localizedContent: capabilities.localizedContent && config.features.localization,
31
+ };
32
+ }
33
+ export function getFeatureAvailability(feature, config, capabilities) {
34
+ const targetEnabled = config.features[feature];
35
+ const capabilitySupported = isFeatureCapabilitySupported(feature, capabilities);
36
+ const enabled = targetEnabled && capabilitySupported;
37
+ return {
38
+ feature,
39
+ enabled,
40
+ targetEnabled,
41
+ capabilitySupported,
42
+ reason: enabled
43
+ ? 'available'
44
+ : targetEnabled
45
+ ? 'capability-unsupported'
46
+ : 'target-disabled',
47
+ };
48
+ }
49
+ export function createTargetRuntimeSnapshot(input) {
50
+ const configTarget = input.configTarget ?? targetConfigKeyForPlatform(input.target);
51
+ const features = {
52
+ iap: getFeatureAvailability('iap', input.config, input.capabilities),
53
+ rewardedAds: getFeatureAvailability('rewardedAds', input.config, input.capabilities),
54
+ interstitialAds: getFeatureAvailability('interstitialAds', input.config, input.capabilities),
55
+ leaderboard: getFeatureAvailability('leaderboard', input.config, input.capabilities),
56
+ localization: getFeatureAvailability('localization', input.config, input.capabilities),
57
+ };
58
+ return {
59
+ target: input.target,
60
+ configTarget,
61
+ config: input.config,
62
+ ...(input.effectiveConfig === undefined ? {} : { effectiveConfig: input.effectiveConfig }),
63
+ capabilities: input.capabilities,
64
+ features,
65
+ adPlacements: (input.adPlacements ?? []).map((placement) => {
66
+ const feature = placement.type === 'rewarded' ? 'rewardedAds' : 'interstitialAds';
67
+ const availability = features[feature];
68
+ return {
69
+ id: placement.id,
70
+ type: placement.type,
71
+ enabled: availability.enabled,
72
+ reason: availability.reason,
73
+ };
74
+ }),
75
+ };
76
+ }
77
+ export function withTargetAvailability(gateway, config, options = {}) {
78
+ const configTarget = options.configTarget ?? targetConfigKeyForPlatform(gateway.target);
79
+ const isAdPlacementAllowed = (placementId, expectedType) => {
80
+ const actualType = options.resolveAdPlacementType?.(placementId);
81
+ if (actualType !== undefined && actualType !== expectedType) {
82
+ return false;
83
+ }
84
+ return expectedType === 'rewarded'
85
+ ? config.features.rewardedAds
86
+ : config.features.interstitialAds;
87
+ };
88
+ const canPreloadAdPlacement = (placementId) => {
89
+ const actualType = options.resolveAdPlacementType?.(placementId);
90
+ if (actualType === 'rewarded') {
91
+ return config.features.rewardedAds;
92
+ }
93
+ if (actualType === 'interstitial') {
94
+ return config.features.interstitialAds;
95
+ }
96
+ return config.features.rewardedAds || config.features.interstitialAds;
97
+ };
98
+ return {
99
+ ...gateway,
100
+ configTarget,
101
+ targetConfig: config,
102
+ ...(options.effectiveConfig === undefined
103
+ ? {}
104
+ : { effectiveConfig: options.effectiveConfig }),
105
+ async getTargetRuntime() {
106
+ return createTargetRuntimeSnapshot({
107
+ target: gateway.target,
108
+ configTarget,
109
+ config,
110
+ ...(options.effectiveConfig === undefined
111
+ ? {}
112
+ : { effectiveConfig: options.effectiveConfig }),
113
+ capabilities: applyTargetConfigToCapabilities(await gateway.getCapabilities(), config),
114
+ adPlacements: options.adPlacements ?? [],
115
+ });
116
+ },
117
+ async getCapabilities() {
118
+ return applyTargetConfigToCapabilities(await gateway.getCapabilities(), config);
119
+ },
120
+ commerce: config.features.iap
121
+ ? gateway.commerce
122
+ : {
123
+ async getProducts() {
124
+ return [];
125
+ },
126
+ async purchase() {
127
+ return {
128
+ status: 'cancelled',
129
+ entitlementIds: [],
130
+ };
131
+ },
132
+ async restore() {
133
+ return {
134
+ restoredEntitlements: [],
135
+ };
136
+ },
137
+ async getEntitlements() {
138
+ return [];
139
+ },
140
+ },
141
+ ads: {
142
+ async preload(input) {
143
+ if (canPreloadAdPlacement(input.placementId)) {
144
+ await gateway.ads.preload(input);
145
+ }
146
+ },
147
+ async showRewarded(input) {
148
+ if (!isAdPlacementAllowed(input.placementId, 'rewarded')) {
149
+ return {
150
+ status: 'unavailable',
151
+ rewardGranted: false,
152
+ };
153
+ }
154
+ return gateway.ads.showRewarded(input);
155
+ },
156
+ async showInterstitial(input) {
157
+ if (!isAdPlacementAllowed(input.placementId, 'interstitial') ||
158
+ gateway.ads.showInterstitial === undefined) {
159
+ return {
160
+ status: 'unavailable',
161
+ };
162
+ }
163
+ return gateway.ads.showInterstitial(input);
164
+ },
165
+ },
166
+ leaderboard: config.features.leaderboard
167
+ ? gateway.leaderboard
168
+ : {
169
+ async submitScore() {
170
+ return {
171
+ submitted: false,
172
+ };
173
+ },
174
+ async open() { },
175
+ },
176
+ };
177
+ }
178
+ export function isTargetConfiguredGateway(gateway) {
179
+ return typeof gateway.getTargetRuntime === 'function';
180
+ }
181
+ function isFeatureCapabilitySupported(feature, capabilities) {
182
+ switch (feature) {
183
+ case 'iap':
184
+ return capabilities.nativeIap;
185
+ case 'rewardedAds':
186
+ return capabilities.rewardedAds;
187
+ case 'interstitialAds':
188
+ return capabilities.interstitialAds;
189
+ case 'leaderboard':
190
+ return capabilities.nativeLeaderboard;
191
+ case 'localization':
192
+ return capabilities.localizedContent;
193
+ }
194
+ }
package/package.json ADDED
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "@mpgd/target-config",
3
+ "version": "0.1.0",
4
+ "description": "Target-specific platform feature availability and effective config helpers for mpgd.",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/imjlk/mpgd-kit.git",
9
+ "directory": "packages/target-config"
10
+ },
11
+ "bugs": {
12
+ "url": "https://github.com/imjlk/mpgd-kit/issues"
13
+ },
14
+ "homepage": "https://github.com/imjlk/mpgd-kit#readme",
15
+ "keywords": [
16
+ "mpgd",
17
+ "phaser",
18
+ "vite",
19
+ "typescript",
20
+ "capacitor",
21
+ "apps-in-toss",
22
+ "game-development",
23
+ "game-distribution"
24
+ ],
25
+ "type": "module",
26
+ "sideEffects": false,
27
+ "exports": {
28
+ ".": {
29
+ "types": "./dist/index.d.ts",
30
+ "default": "./dist/index.js"
31
+ },
32
+ "./targets.json": "./targets.json"
33
+ },
34
+ "files": [
35
+ "dist",
36
+ "targets.json"
37
+ ],
38
+ "dependencies": {
39
+ "typia": "rc",
40
+ "@mpgd/platform": "0.1.0",
41
+ "@mpgd/catalog": "0.1.0"
42
+ },
43
+ "devDependencies": {
44
+ "ttsc": "0.16.9",
45
+ "typescript": "7.0.1-rc"
46
+ },
47
+ "main": "./dist/index.js",
48
+ "types": "./dist/index.d.ts",
49
+ "publishConfig": {
50
+ "access": "public"
51
+ },
52
+ "scripts": {
53
+ "check": "ttsc --noEmit",
54
+ "lint": "ttsc --noEmit",
55
+ "format": "ttsc format",
56
+ "fix": "ttsc fix",
57
+ "test": "cd ../.. && node tools/run-ttsx.mjs packages/target-config/test/target-config-smoke.ts"
58
+ }
59
+ }
package/targets.json ADDED
@@ -0,0 +1,133 @@
1
+ {
2
+ "version": "2026-07-03",
3
+ "targets": {
4
+ "web-preview": {
5
+ "runtime": "web-preview",
6
+ "features": {
7
+ "iap": false,
8
+ "rewardedAds": false,
9
+ "interstitialAds": false,
10
+ "leaderboard": false,
11
+ "localization": true
12
+ },
13
+ "capabilities": {
14
+ "storage": "local",
15
+ "localization": true
16
+ },
17
+ "monetization": {
18
+ "iap": false,
19
+ "rewardedAds": false,
20
+ "interstitialAds": false
21
+ },
22
+ "leaderboard": {
23
+ "native": false
24
+ },
25
+ "release": {
26
+ "profile": "web-preview"
27
+ },
28
+ "policy": {
29
+ "externalPaymentAllowed": false,
30
+ "remoteExecutableCodeAllowed": false,
31
+ "installOtherAppCTAAllowed": false,
32
+ "requiresStoreReview": false,
33
+ "requiresAitReview": false
34
+ }
35
+ },
36
+ "android": {
37
+ "runtime": "capacitor-android",
38
+ "features": {
39
+ "iap": true,
40
+ "rewardedAds": true,
41
+ "interstitialAds": true,
42
+ "leaderboard": true,
43
+ "localization": true
44
+ },
45
+ "capabilities": {
46
+ "storage": "native",
47
+ "localization": true
48
+ },
49
+ "monetization": {
50
+ "iap": true,
51
+ "rewardedAds": true,
52
+ "interstitialAds": true
53
+ },
54
+ "leaderboard": {
55
+ "native": true
56
+ },
57
+ "release": {
58
+ "profile": "google-play"
59
+ },
60
+ "policy": {
61
+ "externalPaymentAllowed": false,
62
+ "remoteExecutableCodeAllowed": false,
63
+ "installOtherAppCTAAllowed": false,
64
+ "requiresStoreReview": true,
65
+ "requiresAitReview": false
66
+ }
67
+ },
68
+ "ios": {
69
+ "runtime": "capacitor-ios",
70
+ "features": {
71
+ "iap": true,
72
+ "rewardedAds": true,
73
+ "interstitialAds": true,
74
+ "leaderboard": true,
75
+ "localization": true
76
+ },
77
+ "capabilities": {
78
+ "storage": "native",
79
+ "localization": true
80
+ },
81
+ "monetization": {
82
+ "iap": true,
83
+ "rewardedAds": true,
84
+ "interstitialAds": true
85
+ },
86
+ "leaderboard": {
87
+ "native": true
88
+ },
89
+ "release": {
90
+ "profile": "app-store"
91
+ },
92
+ "policy": {
93
+ "externalPaymentAllowed": false,
94
+ "remoteExecutableCodeAllowed": false,
95
+ "installOtherAppCTAAllowed": false,
96
+ "requiresStoreReview": true,
97
+ "requiresAitReview": false
98
+ }
99
+ },
100
+ "ait": {
101
+ "runtime": "apps-in-toss",
102
+ "features": {
103
+ "iap": true,
104
+ "rewardedAds": true,
105
+ "interstitialAds": true,
106
+ "leaderboard": true,
107
+ "localization": true
108
+ },
109
+ "capabilities": {
110
+ "storage": "none",
111
+ "localization": true
112
+ },
113
+ "monetization": {
114
+ "iap": true,
115
+ "rewardedAds": true,
116
+ "interstitialAds": true
117
+ },
118
+ "leaderboard": {
119
+ "native": true
120
+ },
121
+ "release": {
122
+ "profile": "apps-in-toss"
123
+ },
124
+ "policy": {
125
+ "externalPaymentAllowed": false,
126
+ "remoteExecutableCodeAllowed": false,
127
+ "installOtherAppCTAAllowed": false,
128
+ "requiresStoreReview": false,
129
+ "requiresAitReview": true
130
+ }
131
+ }
132
+ }
133
+ }