@mpgd/adapter-ait 0.10.0 → 0.12.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.
- package/dist/host.d.ts +6 -0
- package/dist/host.js +29 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +7 -2
- package/dist/local-mock.d.ts +17 -0
- package/dist/local-mock.js +19 -1
- package/dist/safe-area.d.ts +54 -0
- package/dist/safe-area.js +132 -0
- package/package.json +6 -2
package/dist/host.d.ts
CHANGED
|
@@ -4,6 +4,12 @@ import type { AitAdPlacementType } from './ad-config.js';
|
|
|
4
4
|
import type { GamePlatformBridge } from './index.js';
|
|
5
5
|
export type AitIdentityProvider = () => Promise<unknown>;
|
|
6
6
|
/**
|
|
7
|
+
* Shares one game identity hash read across a wrapper session.
|
|
8
|
+
* Rejected reads are evicted so a resumed mobile host can retry after login or
|
|
9
|
+
* WebView state has recovered.
|
|
10
|
+
*/
|
|
11
|
+
export declare function createAitSessionIdentityProvider(provider?: AitIdentityProvider): AitIdentityProvider;
|
|
12
|
+
/**
|
|
7
13
|
* The AIT SDK adds support-version metadata to native functions over time.
|
|
8
14
|
* The host only depends on the callable surface and an optional support probe
|
|
9
15
|
* so adapter tests remain compatible with older wrappers and newer SDKs.
|
package/dist/host.js
CHANGED
|
@@ -44,8 +44,31 @@ const launchEntries = new Set([
|
|
|
44
44
|
'leaderboard',
|
|
45
45
|
'friend-challenge',
|
|
46
46
|
]);
|
|
47
|
+
const defaultAitIdentityProvider = () => getUserKeyForGame();
|
|
48
|
+
/**
|
|
49
|
+
* Shares one game identity hash read across a wrapper session.
|
|
50
|
+
* Rejected reads are evicted so a resumed mobile host can retry after login or
|
|
51
|
+
* WebView state has recovered.
|
|
52
|
+
*/
|
|
53
|
+
export function createAitSessionIdentityProvider(provider = defaultAitIdentityProvider) {
|
|
54
|
+
let pending;
|
|
55
|
+
return () => {
|
|
56
|
+
const current = pending;
|
|
57
|
+
if (current !== undefined) {
|
|
58
|
+
return current;
|
|
59
|
+
}
|
|
60
|
+
const attempt = Promise.resolve().then(provider);
|
|
61
|
+
void (pending = attempt);
|
|
62
|
+
void attempt.catch(() => {
|
|
63
|
+
if (pending === attempt) {
|
|
64
|
+
pending = undefined;
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
return attempt;
|
|
68
|
+
};
|
|
69
|
+
}
|
|
47
70
|
const defaultDependencies = {
|
|
48
|
-
identityProvider:
|
|
71
|
+
identityProvider: defaultAitIdentityProvider,
|
|
49
72
|
storage: Storage,
|
|
50
73
|
getTossShareLink,
|
|
51
74
|
share,
|
|
@@ -65,7 +88,11 @@ export function installAitHostBridge(options = {}) {
|
|
|
65
88
|
return bridge;
|
|
66
89
|
}
|
|
67
90
|
export function createAitHostBridge(options = {}) {
|
|
68
|
-
const
|
|
91
|
+
const configuredDependencies = { ...defaultDependencies, ...options.dependencies };
|
|
92
|
+
const dependencies = {
|
|
93
|
+
...configuredDependencies,
|
|
94
|
+
identityProvider: createAitSessionIdentityProvider(configuredDependencies.identityProvider),
|
|
95
|
+
};
|
|
69
96
|
const appName = normalizeAppName(options.appName ?? 'mpgd-kit');
|
|
70
97
|
const adGroupIds = normalizeAdGroupIds(options.adGroupIds);
|
|
71
98
|
const adPlacementTypes = normalizeAdPlacementTypes(options.adPlacementTypes);
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type BridgeRequest, type BridgeResponse } from '@mpgd/bridge';
|
|
2
|
-
import type
|
|
2
|
+
import { type PlatformGateway } from '@mpgd/platform';
|
|
3
3
|
export interface GamePlatformBridge {
|
|
4
4
|
request(input: BridgeRequest): Promise<BridgeResponse>;
|
|
5
5
|
}
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import { bridgeStorageLoadProtocol, decodeBridgeStorageLoadData, } from '@mpgd/bridge';
|
|
2
|
+
import { PlatformOperationError, } from '@mpgd/platform';
|
|
2
3
|
import { createAitLifecycleAdapter } from './lifecycle.js';
|
|
3
4
|
export function createAitPlatformGateway(input) {
|
|
4
5
|
async function request(method, payload) {
|
|
5
6
|
const bridge = input.bridge ?? getBridge() ?? input.fallbackBridge;
|
|
6
7
|
if (bridge === undefined) {
|
|
7
|
-
throw new
|
|
8
|
+
throw new PlatformOperationError({
|
|
9
|
+
code: 'AIT_BRIDGE_NOT_INSTALLED',
|
|
10
|
+
message: 'AIT bridge is not installed.',
|
|
11
|
+
retryable: false,
|
|
12
|
+
});
|
|
8
13
|
}
|
|
9
14
|
const response = await bridge.request({
|
|
10
15
|
id: crypto.randomUUID(),
|
|
@@ -18,7 +23,7 @@ export function createAitPlatformGateway(input) {
|
|
|
18
23
|
},
|
|
19
24
|
});
|
|
20
25
|
if (!response.ok) {
|
|
21
|
-
throw new
|
|
26
|
+
throw new PlatformOperationError(response.error);
|
|
22
27
|
}
|
|
23
28
|
return response.data;
|
|
24
29
|
}
|
package/dist/local-mock.d.ts
CHANGED
|
@@ -6,11 +6,28 @@
|
|
|
6
6
|
* commerce: those paths must be verified in the Toss app sandbox/console.
|
|
7
7
|
*/
|
|
8
8
|
declare function unsupported(): boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Local browsers have no native safe-area constant. Throwing preserves the
|
|
11
|
+
* wrapper's CSS `env(safe-area-inset-*)` fallback while keeping ESM exports in
|
|
12
|
+
* parity with the SDK surface consumed by the adapter.
|
|
13
|
+
*/
|
|
14
|
+
export declare const SafeArea: {
|
|
15
|
+
get(): never;
|
|
16
|
+
subscribe(_input: {
|
|
17
|
+
readonly onEvent: (insets: unknown) => void;
|
|
18
|
+
}): () => void;
|
|
19
|
+
};
|
|
9
20
|
export declare const Storage: {
|
|
10
21
|
getItem(key: string): Promise<string | null>;
|
|
11
22
|
setItem(key: string, value: string): Promise<void>;
|
|
12
23
|
removeItem(key: string): Promise<void>;
|
|
13
24
|
};
|
|
25
|
+
export declare const User: {
|
|
26
|
+
getAnonymousKey(): Promise<{
|
|
27
|
+
readonly type: 'HASH';
|
|
28
|
+
readonly hash: string;
|
|
29
|
+
}>;
|
|
30
|
+
};
|
|
14
31
|
export declare function getUserKeyForGame(): Promise<{
|
|
15
32
|
readonly type: 'HASH';
|
|
16
33
|
readonly hash: string;
|
package/dist/local-mock.js
CHANGED
|
@@ -12,6 +12,19 @@ function unsupported() {
|
|
|
12
12
|
function noOpSubscription() {
|
|
13
13
|
return () => { };
|
|
14
14
|
}
|
|
15
|
+
/**
|
|
16
|
+
* Local browsers have no native safe-area constant. Throwing preserves the
|
|
17
|
+
* wrapper's CSS `env(safe-area-inset-*)` fallback while keeping ESM exports in
|
|
18
|
+
* parity with the SDK surface consumed by the adapter.
|
|
19
|
+
*/
|
|
20
|
+
export const SafeArea = {
|
|
21
|
+
get() {
|
|
22
|
+
throw new Error('SafeArea is unavailable in the Apps in Toss local mock.');
|
|
23
|
+
},
|
|
24
|
+
subscribe(_input) {
|
|
25
|
+
return noOpSubscription();
|
|
26
|
+
},
|
|
27
|
+
};
|
|
15
28
|
export const Storage = {
|
|
16
29
|
async getItem(key) {
|
|
17
30
|
return localStorageValues.get(key) ?? null;
|
|
@@ -23,8 +36,13 @@ export const Storage = {
|
|
|
23
36
|
localStorageValues.delete(key);
|
|
24
37
|
},
|
|
25
38
|
};
|
|
39
|
+
export const User = {
|
|
40
|
+
async getAnonymousKey() {
|
|
41
|
+
return { type: 'HASH', hash: 'ait-local-player' };
|
|
42
|
+
},
|
|
43
|
+
};
|
|
26
44
|
export async function getUserKeyForGame() {
|
|
27
|
-
return
|
|
45
|
+
return User.getAnonymousKey();
|
|
28
46
|
}
|
|
29
47
|
export async function getTossShareLink() {
|
|
30
48
|
return globalThis.location?.href ?? 'https://localhost/';
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/** Raw Apps in Toss safe-area values expressed in CSS pixels. */
|
|
2
|
+
export interface AitSafeAreaInsets {
|
|
3
|
+
readonly top: number;
|
|
4
|
+
readonly right: number;
|
|
5
|
+
readonly bottom: number;
|
|
6
|
+
readonly left: number;
|
|
7
|
+
}
|
|
8
|
+
/** Minimal SDK surface kept injectable for deterministic wrapper tests. */
|
|
9
|
+
export interface AitSafeAreaSource {
|
|
10
|
+
get(): unknown;
|
|
11
|
+
subscribe(input: {
|
|
12
|
+
readonly onEvent: (insets: unknown) => void;
|
|
13
|
+
}): () => void;
|
|
14
|
+
}
|
|
15
|
+
/** Minimal CSS declaration surface required by the bridge. */
|
|
16
|
+
export interface AitSafeAreaCssStyle {
|
|
17
|
+
getPropertyValue(property: string): string;
|
|
18
|
+
getPropertyPriority?(property: string): string;
|
|
19
|
+
setProperty(property: string, value: string, priority?: string): void;
|
|
20
|
+
removeProperty(property: string): string;
|
|
21
|
+
}
|
|
22
|
+
export interface InstallAitSafeAreaCssVariablesOptions {
|
|
23
|
+
readonly source?: AitSafeAreaSource;
|
|
24
|
+
readonly style?: AitSafeAreaCssStyle;
|
|
25
|
+
/** Override only when a host version or custom navigation treatment has a measured band. */
|
|
26
|
+
readonly navigationControlBandHeightPx?: number;
|
|
27
|
+
/** Override only when native navigation uses a measured right-edge gap. */
|
|
28
|
+
readonly navigationControlEdgeGapPx?: number;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Apps in Toss positions the game navigation controls immediately below the
|
|
32
|
+
* native top safe area. Keep full-width app chrome below this additional band.
|
|
33
|
+
*/
|
|
34
|
+
export declare const aitNavigationControlBandHeightPx = 52;
|
|
35
|
+
/** Apps in Toss navigation controls retain this edge gap from the safe right inset. */
|
|
36
|
+
export declare const aitNavigationControlEdgeGapPx = 10;
|
|
37
|
+
/** CSS contract shared by generated AIT wrappers and full-bleed games. */
|
|
38
|
+
export declare const aitSafeAreaCssVariables: Readonly<{
|
|
39
|
+
readonly top: '--mpgd-safe-area-top';
|
|
40
|
+
readonly right: '--mpgd-safe-area-right';
|
|
41
|
+
readonly bottom: '--mpgd-safe-area-bottom';
|
|
42
|
+
readonly left: '--mpgd-safe-area-left';
|
|
43
|
+
readonly navigationControlTop: '--mpgd-ait-navigation-control-top';
|
|
44
|
+
readonly navigationControlRight: '--mpgd-ait-navigation-control-right';
|
|
45
|
+
readonly navigationContentTop: '--mpgd-ait-navigation-content-top';
|
|
46
|
+
}>;
|
|
47
|
+
/**
|
|
48
|
+
* Mirror the official AIT `SafeArea` snapshot into CSS custom properties and
|
|
49
|
+
* keep them current when the WebView changes presentation or orientation.
|
|
50
|
+
*
|
|
51
|
+
* CSS `env(safe-area-inset-*)` remains the wrapper's pre-SDK fallback. Invalid
|
|
52
|
+
* or unavailable native values never overwrite that fallback.
|
|
53
|
+
*/
|
|
54
|
+
export declare function installAitSafeAreaCssVariables(options?: InstallAitSafeAreaCssVariablesOptions): () => void;
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { SafeArea } from '@apps-in-toss/web-framework';
|
|
2
|
+
/**
|
|
3
|
+
* Apps in Toss positions the game navigation controls immediately below the
|
|
4
|
+
* native top safe area. Keep full-width app chrome below this additional band.
|
|
5
|
+
*/
|
|
6
|
+
export const aitNavigationControlBandHeightPx = 52;
|
|
7
|
+
/** Apps in Toss navigation controls retain this edge gap from the safe right inset. */
|
|
8
|
+
export const aitNavigationControlEdgeGapPx = 10;
|
|
9
|
+
/** CSS contract shared by generated AIT wrappers and full-bleed games. */
|
|
10
|
+
export const aitSafeAreaCssVariables = Object.freeze({
|
|
11
|
+
top: '--mpgd-safe-area-top',
|
|
12
|
+
right: '--mpgd-safe-area-right',
|
|
13
|
+
bottom: '--mpgd-safe-area-bottom',
|
|
14
|
+
left: '--mpgd-safe-area-left',
|
|
15
|
+
navigationControlTop: '--mpgd-ait-navigation-control-top',
|
|
16
|
+
navigationControlRight: '--mpgd-ait-navigation-control-right',
|
|
17
|
+
navigationContentTop: '--mpgd-ait-navigation-content-top',
|
|
18
|
+
});
|
|
19
|
+
const maximumSafeAreaInsetPx = 4096;
|
|
20
|
+
const maximumCssPixelOptionPx = 4096;
|
|
21
|
+
/**
|
|
22
|
+
* Mirror the official AIT `SafeArea` snapshot into CSS custom properties and
|
|
23
|
+
* keep them current when the WebView changes presentation or orientation.
|
|
24
|
+
*
|
|
25
|
+
* CSS `env(safe-area-inset-*)` remains the wrapper's pre-SDK fallback. Invalid
|
|
26
|
+
* or unavailable native values never overwrite that fallback.
|
|
27
|
+
*/
|
|
28
|
+
export function installAitSafeAreaCssVariables(options = {}) {
|
|
29
|
+
const navigationControlBandHeightPx = normalizeCssPixelOption(options.navigationControlBandHeightPx, aitNavigationControlBandHeightPx, 'navigationControlBandHeightPx');
|
|
30
|
+
const navigationControlEdgeGapPx = normalizeCssPixelOption(options.navigationControlEdgeGapPx, aitNavigationControlEdgeGapPx, 'navigationControlEdgeGapPx');
|
|
31
|
+
const style = options.style ?? resolveDocumentRootStyle();
|
|
32
|
+
if (style === undefined) {
|
|
33
|
+
return () => { };
|
|
34
|
+
}
|
|
35
|
+
const source = options.source ?? SafeArea;
|
|
36
|
+
const previous = rememberCssVariables(style);
|
|
37
|
+
let active = true;
|
|
38
|
+
let unsubscribe = () => { };
|
|
39
|
+
const apply = (input) => {
|
|
40
|
+
if (!active) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
const insets = normalizeAitSafeAreaInsets(input);
|
|
44
|
+
if (insets === null) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
style.setProperty(aitSafeAreaCssVariables.top, formatCssPixels(insets.top));
|
|
48
|
+
style.setProperty(aitSafeAreaCssVariables.right, formatCssPixels(insets.right));
|
|
49
|
+
style.setProperty(aitSafeAreaCssVariables.bottom, formatCssPixels(insets.bottom));
|
|
50
|
+
style.setProperty(aitSafeAreaCssVariables.left, formatCssPixels(insets.left));
|
|
51
|
+
style.setProperty(aitSafeAreaCssVariables.navigationControlTop, formatCssPixels(insets.top));
|
|
52
|
+
style.setProperty(aitSafeAreaCssVariables.navigationControlRight, formatCssPixels(insets.right + navigationControlEdgeGapPx));
|
|
53
|
+
style.setProperty(aitSafeAreaCssVariables.navigationContentTop, formatCssPixels(insets.top + navigationControlBandHeightPx));
|
|
54
|
+
};
|
|
55
|
+
try {
|
|
56
|
+
apply(source.get());
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
// Local browsers and older Toss hosts keep the CSS env() fallback.
|
|
60
|
+
}
|
|
61
|
+
try {
|
|
62
|
+
const subscribed = source.subscribe({ onEvent: apply });
|
|
63
|
+
unsubscribe = typeof subscribed === 'function' ? subscribed : () => { };
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
// A missing native event bridge must not prevent the game from booting.
|
|
67
|
+
}
|
|
68
|
+
return () => {
|
|
69
|
+
if (!active) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
active = false;
|
|
73
|
+
try {
|
|
74
|
+
unsubscribe();
|
|
75
|
+
}
|
|
76
|
+
finally {
|
|
77
|
+
restoreCssVariables(style, previous);
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
function normalizeAitSafeAreaInsets(input) {
|
|
82
|
+
if (typeof input !== 'object' || input === null) {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
const record = input;
|
|
86
|
+
const top = normalizeInset(record.top);
|
|
87
|
+
const right = normalizeInset(record.right);
|
|
88
|
+
const bottom = normalizeInset(record.bottom);
|
|
89
|
+
const left = normalizeInset(record.left);
|
|
90
|
+
if (top === null || right === null || bottom === null || left === null) {
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
return { top, right, bottom, left };
|
|
94
|
+
}
|
|
95
|
+
function normalizeInset(input) {
|
|
96
|
+
return typeof input === 'number'
|
|
97
|
+
&& Number.isFinite(input)
|
|
98
|
+
&& input >= 0
|
|
99
|
+
&& input <= maximumSafeAreaInsetPx
|
|
100
|
+
? input
|
|
101
|
+
: null;
|
|
102
|
+
}
|
|
103
|
+
function normalizeCssPixelOption(input, fallback, name) {
|
|
104
|
+
const value = input ?? fallback;
|
|
105
|
+
if (!Number.isFinite(value) || value < 0 || value > maximumCssPixelOptionPx) {
|
|
106
|
+
throw new TypeError(`${name} must be a non-negative finite CSS pixel value.`);
|
|
107
|
+
}
|
|
108
|
+
return value;
|
|
109
|
+
}
|
|
110
|
+
function formatCssPixels(value) {
|
|
111
|
+
return `${Object.is(value, -0) ? 0 : value}px`;
|
|
112
|
+
}
|
|
113
|
+
function resolveDocumentRootStyle() {
|
|
114
|
+
return typeof document === 'undefined' ? undefined : document.documentElement.style;
|
|
115
|
+
}
|
|
116
|
+
function rememberCssVariables(style) {
|
|
117
|
+
return Object.values(aitSafeAreaCssVariables).map((property) => ({
|
|
118
|
+
property,
|
|
119
|
+
value: style.getPropertyValue(property),
|
|
120
|
+
priority: style.getPropertyPriority?.(property) ?? '',
|
|
121
|
+
}));
|
|
122
|
+
}
|
|
123
|
+
function restoreCssVariables(style, previous) {
|
|
124
|
+
for (const entry of previous) {
|
|
125
|
+
if (entry.value.length === 0) {
|
|
126
|
+
style.removeProperty(entry.property);
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
style.setProperty(entry.property, entry.value, entry.priority);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mpgd/adapter-ait",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"description": "Apps in Toss WebView platform adapter for mpgd games.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -45,6 +45,10 @@
|
|
|
45
45
|
"types": "./dist/local-mock.d.ts",
|
|
46
46
|
"default": "./dist/local-mock.js"
|
|
47
47
|
},
|
|
48
|
+
"./safe-area": {
|
|
49
|
+
"types": "./dist/safe-area.d.ts",
|
|
50
|
+
"default": "./dist/safe-area.js"
|
|
51
|
+
},
|
|
48
52
|
"./package.json": "./package.json"
|
|
49
53
|
},
|
|
50
54
|
"files": [
|
|
@@ -52,7 +56,7 @@
|
|
|
52
56
|
],
|
|
53
57
|
"dependencies": {
|
|
54
58
|
"@mpgd/bridge": "0.9.0",
|
|
55
|
-
"@mpgd/platform": "0.
|
|
59
|
+
"@mpgd/platform": "0.11.0"
|
|
56
60
|
},
|
|
57
61
|
"peerDependencies": {
|
|
58
62
|
"@apps-in-toss/web-framework": ">=3.0.0 <4"
|