@ait-co/devtools 0.0.2 → 0.0.3
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/README.md +14 -6
- package/dist/mock/index.d.ts +471 -434
- package/dist/mock/index.d.ts.map +1 -0
- package/dist/mock/index.js +1002 -427
- package/dist/mock/index.js.map +1 -1
- package/dist/panel/index.d.ts +3 -1
- package/dist/panel/index.d.ts.map +1 -0
- package/dist/panel/index.js +1221 -679
- package/dist/panel/index.js.map +1 -1
- package/dist/unplugin/index.cjs +68 -73
- package/dist/unplugin/index.cjs.map +1 -1
- package/dist/unplugin/index.d.cts +1367 -23
- package/dist/unplugin/index.d.cts.map +1 -0
- package/dist/unplugin/index.d.ts +1367 -23
- package/dist/unplugin/index.d.ts.map +1 -0
- package/dist/unplugin/index.js +59 -44
- package/dist/unplugin/index.js.map +1 -1
- package/package.json +16 -14
- package/dist/chunk-6PPZTREF.js +0 -569
- package/dist/chunk-6PPZTREF.js.map +0 -1
package/dist/mock/index.js
CHANGED
|
@@ -1,511 +1,1086 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
1
|
+
//#region src/mock/state.ts
|
|
2
|
+
const DEFAULT_STATE = {
|
|
3
|
+
platform: "ios",
|
|
4
|
+
environment: "sandbox",
|
|
5
|
+
appVersion: "5.240.0",
|
|
6
|
+
locale: "ko-KR",
|
|
7
|
+
schemeUri: "/",
|
|
8
|
+
groupId: "mock-group-id",
|
|
9
|
+
deploymentId: "mock-deployment-id",
|
|
10
|
+
deviceId: "",
|
|
11
|
+
brand: {
|
|
12
|
+
displayName: "Mock App",
|
|
13
|
+
icon: "",
|
|
14
|
+
primaryColor: "#3182F6"
|
|
15
|
+
},
|
|
16
|
+
networkStatus: "WIFI",
|
|
17
|
+
permissions: {
|
|
18
|
+
clipboard: "allowed",
|
|
19
|
+
contacts: "allowed",
|
|
20
|
+
photos: "allowed",
|
|
21
|
+
geolocation: "allowed",
|
|
22
|
+
camera: "allowed",
|
|
23
|
+
microphone: "notDetermined"
|
|
24
|
+
},
|
|
25
|
+
location: {
|
|
26
|
+
coords: {
|
|
27
|
+
latitude: 37.5665,
|
|
28
|
+
longitude: 126.978,
|
|
29
|
+
altitude: 0,
|
|
30
|
+
accuracy: 10,
|
|
31
|
+
altitudeAccuracy: 0,
|
|
32
|
+
heading: 0
|
|
33
|
+
},
|
|
34
|
+
timestamp: Date.now(),
|
|
35
|
+
accessLocation: "FINE"
|
|
36
|
+
},
|
|
37
|
+
safeAreaInsets: {
|
|
38
|
+
top: 47,
|
|
39
|
+
bottom: 34,
|
|
40
|
+
left: 0,
|
|
41
|
+
right: 0
|
|
42
|
+
},
|
|
43
|
+
contacts: [{
|
|
44
|
+
name: "홍길동",
|
|
45
|
+
phoneNumber: "010-1234-5678"
|
|
46
|
+
}, {
|
|
47
|
+
name: "김토스",
|
|
48
|
+
phoneNumber: "010-9876-5432"
|
|
49
|
+
}],
|
|
50
|
+
iap: {
|
|
51
|
+
products: [{
|
|
52
|
+
sku: "mock-gem-100",
|
|
53
|
+
type: "CONSUMABLE",
|
|
54
|
+
displayName: "보석 100개",
|
|
55
|
+
displayAmount: "1,000원",
|
|
56
|
+
iconUrl: "",
|
|
57
|
+
description: "게임에서 사용할 수 있는 보석 100개"
|
|
58
|
+
}],
|
|
59
|
+
nextResult: "success",
|
|
60
|
+
pendingOrders: [],
|
|
61
|
+
completedOrders: []
|
|
62
|
+
},
|
|
63
|
+
payment: {
|
|
64
|
+
nextResult: "success",
|
|
65
|
+
failReason: ""
|
|
66
|
+
},
|
|
67
|
+
auth: {
|
|
68
|
+
isLoggedIn: true,
|
|
69
|
+
isTossLoginIntegrated: true,
|
|
70
|
+
userKeyHash: "mock-user-hash-abc123"
|
|
71
|
+
},
|
|
72
|
+
ads: {
|
|
73
|
+
isLoaded: false,
|
|
74
|
+
nextEvent: "loaded"
|
|
75
|
+
},
|
|
76
|
+
game: {
|
|
77
|
+
profile: {
|
|
78
|
+
nickname: "MockPlayer",
|
|
79
|
+
profileImageUri: ""
|
|
80
|
+
},
|
|
81
|
+
leaderboardScores: []
|
|
82
|
+
},
|
|
83
|
+
analyticsLog: [],
|
|
84
|
+
deviceModes: {
|
|
85
|
+
camera: "mock",
|
|
86
|
+
photos: "mock",
|
|
87
|
+
location: "mock",
|
|
88
|
+
network: "mock",
|
|
89
|
+
clipboard: "web"
|
|
90
|
+
},
|
|
91
|
+
mockData: {
|
|
92
|
+
images: [],
|
|
93
|
+
clipboardText: ""
|
|
94
|
+
},
|
|
95
|
+
panelEditable: true
|
|
96
|
+
};
|
|
97
|
+
function generateDeviceId() {
|
|
98
|
+
const stored = localStorage.getItem("__ait_device_id");
|
|
99
|
+
if (stored) return stored;
|
|
100
|
+
const id = crypto.randomUUID();
|
|
101
|
+
localStorage.setItem("__ait_device_id", id);
|
|
102
|
+
return id;
|
|
103
|
+
}
|
|
104
|
+
var AitStateManager = class {
|
|
105
|
+
_state;
|
|
106
|
+
_listeners = /* @__PURE__ */ new Set();
|
|
107
|
+
constructor() {
|
|
108
|
+
this._state = structuredClone(DEFAULT_STATE);
|
|
109
|
+
try {
|
|
110
|
+
this._state.deviceId = generateDeviceId();
|
|
111
|
+
} catch {
|
|
112
|
+
this._state.deviceId = "mock-device-" + Math.random().toString(36).slice(2);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
get state() {
|
|
116
|
+
return this._state;
|
|
117
|
+
}
|
|
118
|
+
update(partial) {
|
|
119
|
+
this._state = {
|
|
120
|
+
...this._state,
|
|
121
|
+
...partial
|
|
122
|
+
};
|
|
123
|
+
this._notify();
|
|
124
|
+
}
|
|
125
|
+
/** 중첩 객체 업데이트용 */
|
|
126
|
+
patch(key, partial) {
|
|
127
|
+
const current = this._state[key];
|
|
128
|
+
if (typeof current === "object" && current !== null && !Array.isArray(current)) this._state = {
|
|
129
|
+
...this._state,
|
|
130
|
+
[key]: {
|
|
131
|
+
...current,
|
|
132
|
+
...partial
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
else this._state = {
|
|
136
|
+
...this._state,
|
|
137
|
+
[key]: partial
|
|
138
|
+
};
|
|
139
|
+
this._notify();
|
|
140
|
+
}
|
|
141
|
+
subscribe(listener) {
|
|
142
|
+
this._listeners.add(listener);
|
|
143
|
+
return () => this._listeners.delete(listener);
|
|
144
|
+
}
|
|
145
|
+
/** 분석 로그 추가 */
|
|
146
|
+
logAnalytics(entry) {
|
|
147
|
+
this._state = {
|
|
148
|
+
...this._state,
|
|
149
|
+
analyticsLog: [...this._state.analyticsLog, {
|
|
150
|
+
...entry,
|
|
151
|
+
timestamp: Date.now()
|
|
152
|
+
}]
|
|
153
|
+
};
|
|
154
|
+
this._notify();
|
|
155
|
+
}
|
|
156
|
+
/** 이벤트 트리거 (backEvent, homeEvent 등) */
|
|
157
|
+
trigger(event) {
|
|
158
|
+
window.dispatchEvent(new CustomEvent(`__ait:${event}`));
|
|
159
|
+
}
|
|
160
|
+
reset() {
|
|
161
|
+
const deviceId = this._state.deviceId;
|
|
162
|
+
this._state = {
|
|
163
|
+
...structuredClone(DEFAULT_STATE),
|
|
164
|
+
deviceId
|
|
165
|
+
};
|
|
166
|
+
this._notify();
|
|
167
|
+
}
|
|
168
|
+
_notify() {
|
|
169
|
+
for (const listener of this._listeners) listener();
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
const aitState = new AitStateManager();
|
|
173
|
+
if (typeof window !== "undefined") window.__ait = aitState;
|
|
174
|
+
//#endregion
|
|
175
|
+
//#region src/mock/auth/index.ts
|
|
176
|
+
/**
|
|
177
|
+
* 인증/로그인 mock
|
|
178
|
+
*/
|
|
23
179
|
async function appLogin() {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
180
|
+
return {
|
|
181
|
+
authorizationCode: `mock-auth-${crypto.randomUUID()}`,
|
|
182
|
+
referrer: aitState.state.environment === "toss" ? "DEFAULT" : "SANDBOX"
|
|
183
|
+
};
|
|
28
184
|
}
|
|
29
185
|
async function getIsTossLoginIntegratedService() {
|
|
30
|
-
|
|
186
|
+
return aitState.state.auth.isTossLoginIntegrated;
|
|
31
187
|
}
|
|
32
188
|
async function getUserKeyForGame() {
|
|
33
|
-
|
|
34
|
-
|
|
189
|
+
if (!aitState.state.auth.userKeyHash) return void 0;
|
|
190
|
+
return {
|
|
191
|
+
hash: aitState.state.auth.userKeyHash,
|
|
192
|
+
type: "HASH"
|
|
193
|
+
};
|
|
35
194
|
}
|
|
36
195
|
async function appsInTossSignTossCert(_params) {
|
|
37
|
-
|
|
196
|
+
console.log("[@ait-co/devtools] appsInTossSignTossCert called (no-op in mock)");
|
|
38
197
|
}
|
|
39
|
-
|
|
40
|
-
|
|
198
|
+
//#endregion
|
|
199
|
+
//#region src/mock/device/_helpers.ts
|
|
200
|
+
/**
|
|
201
|
+
* 디바이스 모듈 내부 공유 헬퍼
|
|
202
|
+
*/
|
|
203
|
+
function generatePlaceholderImage(width, height, text, color) {
|
|
204
|
+
const canvas = document.createElement("canvas");
|
|
205
|
+
canvas.width = width;
|
|
206
|
+
canvas.height = height;
|
|
207
|
+
const ctx = canvas.getContext("2d");
|
|
208
|
+
if (!ctx) {
|
|
209
|
+
const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}"><rect fill="${color}" width="${width}" height="${height}"/><text x="50%" y="50%" fill="white" font-size="16" text-anchor="middle" dominant-baseline="middle">${text}</text></svg>`;
|
|
210
|
+
return "data:image/svg+xml;base64," + btoa(svg);
|
|
211
|
+
}
|
|
212
|
+
ctx.fillStyle = color;
|
|
213
|
+
ctx.fillRect(0, 0, width, height);
|
|
214
|
+
ctx.fillStyle = "white";
|
|
215
|
+
ctx.font = "16px sans-serif";
|
|
216
|
+
ctx.textAlign = "center";
|
|
217
|
+
ctx.textBaseline = "middle";
|
|
218
|
+
ctx.fillText(text, width / 2, height / 2);
|
|
219
|
+
return canvas.toDataURL("image/png");
|
|
220
|
+
}
|
|
221
|
+
const DEFAULT_PLACEHOLDERS = [
|
|
222
|
+
{
|
|
223
|
+
text: "Mock Photo 1",
|
|
224
|
+
color: "#3182F6"
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
text: "Mock Photo 2",
|
|
228
|
+
color: "#27ae60"
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
text: "Mock Photo 3",
|
|
232
|
+
color: "#e67e22"
|
|
233
|
+
}
|
|
234
|
+
];
|
|
235
|
+
let cachedPlaceholders = null;
|
|
236
|
+
function getDefaultPlaceholderImages() {
|
|
237
|
+
if (!cachedPlaceholders) cachedPlaceholders = DEFAULT_PLACEHOLDERS.map((p) => generatePlaceholderImage(320, 240, p.text, p.color));
|
|
238
|
+
return [...cachedPlaceholders];
|
|
239
|
+
}
|
|
240
|
+
/** @internal device 모듈 내부 전용 */
|
|
241
|
+
function getMockImages() {
|
|
242
|
+
const images = aitState.state.mockData.images;
|
|
243
|
+
if (images.length > 0) return images;
|
|
244
|
+
return getDefaultPlaceholderImages();
|
|
245
|
+
}
|
|
246
|
+
const PROMPT_TIMEOUT_MS = 3e4;
|
|
247
|
+
/** @internal device 모듈 내부 전용 */
|
|
248
|
+
function waitForPromptResponse(type) {
|
|
249
|
+
return new Promise((resolve, reject) => {
|
|
250
|
+
const eventName = "__ait:prompt-response:" + type;
|
|
251
|
+
const cancelName = "__ait:prompt-cancel";
|
|
252
|
+
function cleanup() {
|
|
253
|
+
clearTimeout(timer);
|
|
254
|
+
window.removeEventListener(eventName, handler);
|
|
255
|
+
window.removeEventListener(cancelName, cancelHandler);
|
|
256
|
+
}
|
|
257
|
+
const timer = setTimeout(() => {
|
|
258
|
+
cleanup();
|
|
259
|
+
const hint = !!document.querySelector(".ait-panel") ? "Please provide input via the DevTools panel." : "Is @ait-co/devtools/panel imported?";
|
|
260
|
+
reject(/* @__PURE__ */ new Error(`[@ait-co/devtools] Prompt timeout for "${type}" after ${PROMPT_TIMEOUT_MS / 1e3}s. ${hint}`));
|
|
261
|
+
}, PROMPT_TIMEOUT_MS);
|
|
262
|
+
const handler = (e) => {
|
|
263
|
+
cleanup();
|
|
264
|
+
resolve(e.detail);
|
|
265
|
+
};
|
|
266
|
+
const cancelHandler = () => {
|
|
267
|
+
cleanup();
|
|
268
|
+
reject(/* @__PURE__ */ new Error(`[@ait-co/devtools] Prompt cancelled for "${type}"`));
|
|
269
|
+
};
|
|
270
|
+
window.addEventListener(eventName, handler);
|
|
271
|
+
window.addEventListener(cancelName, cancelHandler);
|
|
272
|
+
window.dispatchEvent(new CustomEvent("__ait:prompt-request", { detail: { type } }));
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
//#endregion
|
|
276
|
+
//#region src/mock/proxy.ts
|
|
277
|
+
/**
|
|
278
|
+
* 미구현 API용 Proxy 트립와이어.
|
|
279
|
+
*
|
|
280
|
+
* 미구현 프로퍼티에 접근하면 throw한다. 이는 "devtools에서는 멀쩡히 돌지만
|
|
281
|
+
* 실 SDK에선 실제로 동작하는" 시나리오를 차단하기 위한 의도적 선택이다.
|
|
282
|
+
* mock이 미구현인 API는 실 SDK에서는 존재할 수 있고, 사용자가 이를 인지하지
|
|
283
|
+
* 못한 채 개발을 이어가면 배포 시점에 놀라게 된다. 에러 메시지에 이슈 URL을
|
|
284
|
+
* 포함해 사용자가 mock 누락을 제보할 수 있게 한다.
|
|
285
|
+
*/
|
|
286
|
+
const ISSUES_URL = "https://github.com/apps-in-toss-community/devtools/issues";
|
|
287
|
+
function createMockProxy(moduleName, implementations) {
|
|
288
|
+
return new Proxy(implementations, { get(target, prop) {
|
|
289
|
+
if (typeof prop === "symbol") return void 0;
|
|
290
|
+
if (prop in target) return target[prop];
|
|
291
|
+
throw new Error(`[@ait-co/devtools] ${moduleName}.${prop} is not mocked. This API may exist in @apps-in-toss/web-framework, but devtools' mock does not cover it yet. Please file an issue: ${ISSUES_URL}`);
|
|
292
|
+
} });
|
|
293
|
+
}
|
|
294
|
+
//#endregion
|
|
295
|
+
//#region src/mock/device/storage.ts
|
|
296
|
+
/**
|
|
297
|
+
* Storage mock
|
|
298
|
+
* localStorage에 `__ait_storage:` prefix로 저장하여 앱 자체 localStorage와 분리
|
|
299
|
+
*/
|
|
300
|
+
const Storage = createMockProxy("Storage", {
|
|
301
|
+
getItem: async (key) => {
|
|
302
|
+
return localStorage.getItem(`__ait_storage:${key}`);
|
|
303
|
+
},
|
|
304
|
+
setItem: async (key, value) => {
|
|
305
|
+
localStorage.setItem(`__ait_storage:${key}`, value);
|
|
306
|
+
},
|
|
307
|
+
removeItem: async (key) => {
|
|
308
|
+
localStorage.removeItem(`__ait_storage:${key}`);
|
|
309
|
+
},
|
|
310
|
+
clearItems: async () => {
|
|
311
|
+
Object.keys(localStorage).filter((k) => k.startsWith("__ait_storage:")).forEach((k) => localStorage.removeItem(k));
|
|
312
|
+
}
|
|
313
|
+
});
|
|
314
|
+
//#endregion
|
|
315
|
+
//#region src/mock/permissions.ts
|
|
316
|
+
/**
|
|
317
|
+
* 권한 시스템 mock
|
|
318
|
+
* 각 디바이스 API (.getPermission, .openPermissionDialog)에 부착된다.
|
|
319
|
+
*/
|
|
320
|
+
async function getPermission(name) {
|
|
321
|
+
return aitState.state.permissions[name];
|
|
322
|
+
}
|
|
323
|
+
async function openPermissionDialog(name) {
|
|
324
|
+
if (aitState.state.permissions[name] === "allowed") return "allowed";
|
|
325
|
+
aitState.patch("permissions", { [name]: "allowed" });
|
|
326
|
+
return "allowed";
|
|
327
|
+
}
|
|
328
|
+
async function requestPermission(permission) {
|
|
329
|
+
return openPermissionDialog(permission.name);
|
|
330
|
+
}
|
|
331
|
+
/** 권한이 필요한 함수에 .getPermission(), .openPermissionDialog()를 부착 */
|
|
332
|
+
function withPermission(fn, permissionName) {
|
|
333
|
+
const enhanced = fn;
|
|
334
|
+
enhanced.getPermission = () => getPermission(permissionName);
|
|
335
|
+
enhanced.openPermissionDialog = () => openPermissionDialog(permissionName);
|
|
336
|
+
return enhanced;
|
|
337
|
+
}
|
|
338
|
+
/** 권한 체크 후 denied면 에러 throw */
|
|
339
|
+
function checkPermission(name, fnName) {
|
|
340
|
+
if (aitState.state.permissions[name] === "denied") throw new Error(`[@ait-co/devtools] ${fnName}: Permission "${name}" is denied. Change it in the DevTools panel.`);
|
|
341
|
+
}
|
|
342
|
+
//#endregion
|
|
343
|
+
//#region src/mock/device/location.ts
|
|
344
|
+
/**
|
|
345
|
+
* Location mock (getCurrentLocation, startUpdateLocation)
|
|
346
|
+
* mock/web/prompt 모드 지원
|
|
347
|
+
*/
|
|
348
|
+
var Accuracy = /* @__PURE__ */ function(Accuracy) {
|
|
349
|
+
Accuracy[Accuracy["Lowest"] = 1] = "Lowest";
|
|
350
|
+
Accuracy[Accuracy["Low"] = 2] = "Low";
|
|
351
|
+
Accuracy[Accuracy["Balanced"] = 3] = "Balanced";
|
|
352
|
+
Accuracy[Accuracy["High"] = 4] = "High";
|
|
353
|
+
Accuracy[Accuracy["Highest"] = 5] = "Highest";
|
|
354
|
+
Accuracy[Accuracy["BestForNavigation"] = 6] = "BestForNavigation";
|
|
355
|
+
return Accuracy;
|
|
356
|
+
}(Accuracy || {});
|
|
357
|
+
function buildLocation() {
|
|
358
|
+
return {
|
|
359
|
+
coords: { ...aitState.state.location.coords },
|
|
360
|
+
timestamp: Date.now(),
|
|
361
|
+
accessLocation: aitState.state.location.accessLocation
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
async function getCurrentLocationMock() {
|
|
365
|
+
return buildLocation();
|
|
366
|
+
}
|
|
367
|
+
async function getCurrentLocationWeb() {
|
|
368
|
+
return new Promise((resolve) => {
|
|
369
|
+
if (!navigator.geolocation) {
|
|
370
|
+
console.warn("[@ait-co/devtools] Geolocation API not available, falling back to mock");
|
|
371
|
+
resolve(buildLocation());
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
navigator.geolocation.getCurrentPosition((pos) => {
|
|
375
|
+
resolve({
|
|
376
|
+
coords: {
|
|
377
|
+
latitude: pos.coords.latitude,
|
|
378
|
+
longitude: pos.coords.longitude,
|
|
379
|
+
altitude: pos.coords.altitude ?? 0,
|
|
380
|
+
accuracy: pos.coords.accuracy,
|
|
381
|
+
altitudeAccuracy: pos.coords.altitudeAccuracy ?? 0,
|
|
382
|
+
heading: pos.coords.heading ?? 0
|
|
383
|
+
},
|
|
384
|
+
timestamp: pos.timestamp,
|
|
385
|
+
accessLocation: "FINE"
|
|
386
|
+
});
|
|
387
|
+
}, () => {
|
|
388
|
+
console.warn("[@ait-co/devtools] Geolocation failed, falling back to mock");
|
|
389
|
+
resolve(buildLocation());
|
|
390
|
+
});
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
async function getCurrentLocationPrompt() {
|
|
394
|
+
return waitForPromptResponse("location");
|
|
395
|
+
}
|
|
396
|
+
const _getCurrentLocation = async (_options) => {
|
|
397
|
+
checkPermission("geolocation", "getCurrentLocation");
|
|
398
|
+
const mode = aitState.state.deviceModes.location;
|
|
399
|
+
if (mode === "web") return getCurrentLocationWeb();
|
|
400
|
+
if (mode === "prompt") return getCurrentLocationPrompt();
|
|
401
|
+
return getCurrentLocationMock();
|
|
402
|
+
};
|
|
403
|
+
const getCurrentLocation = withPermission(_getCurrentLocation, "geolocation");
|
|
404
|
+
function startUpdateLocationMock(eventParams) {
|
|
405
|
+
const { onEvent, options } = eventParams;
|
|
406
|
+
const interval = Math.max(options.timeInterval, 500);
|
|
407
|
+
const id = setInterval(() => {
|
|
408
|
+
const loc = buildLocation();
|
|
409
|
+
loc.coords.latitude += (Math.random() - .5) * 1e-4;
|
|
410
|
+
loc.coords.longitude += (Math.random() - .5) * 1e-4;
|
|
411
|
+
onEvent(loc);
|
|
412
|
+
}, interval);
|
|
413
|
+
return () => clearInterval(id);
|
|
414
|
+
}
|
|
415
|
+
function startUpdateLocationWeb(eventParams) {
|
|
416
|
+
const { onEvent, onError } = eventParams;
|
|
417
|
+
if (!navigator.geolocation) {
|
|
418
|
+
console.warn("[@ait-co/devtools] Geolocation API not available, falling back to mock");
|
|
419
|
+
return startUpdateLocationMock(eventParams);
|
|
420
|
+
}
|
|
421
|
+
const watchId = navigator.geolocation.watchPosition((pos) => {
|
|
422
|
+
onEvent({
|
|
423
|
+
coords: {
|
|
424
|
+
latitude: pos.coords.latitude,
|
|
425
|
+
longitude: pos.coords.longitude,
|
|
426
|
+
altitude: pos.coords.altitude ?? 0,
|
|
427
|
+
accuracy: pos.coords.accuracy,
|
|
428
|
+
altitudeAccuracy: pos.coords.altitudeAccuracy ?? 0,
|
|
429
|
+
heading: pos.coords.heading ?? 0
|
|
430
|
+
},
|
|
431
|
+
timestamp: pos.timestamp,
|
|
432
|
+
accessLocation: "FINE"
|
|
433
|
+
});
|
|
434
|
+
}, (err) => onError(err));
|
|
435
|
+
return () => navigator.geolocation.clearWatch(watchId);
|
|
436
|
+
}
|
|
437
|
+
function startUpdateLocationPrompt(eventParams) {
|
|
438
|
+
const { onEvent } = eventParams;
|
|
439
|
+
const handler = (e) => {
|
|
440
|
+
onEvent(e.detail);
|
|
441
|
+
};
|
|
442
|
+
window.addEventListener("__ait:prompt-response:location-update", handler);
|
|
443
|
+
window.dispatchEvent(new CustomEvent("__ait:prompt-request", { detail: { type: "location-update" } }));
|
|
444
|
+
return () => window.removeEventListener("__ait:prompt-response:location-update", handler);
|
|
445
|
+
}
|
|
446
|
+
const _startUpdateLocation = (eventParams) => {
|
|
447
|
+
const mode = aitState.state.deviceModes.location;
|
|
448
|
+
if (mode === "web") return startUpdateLocationWeb(eventParams);
|
|
449
|
+
if (mode === "prompt") return startUpdateLocationPrompt(eventParams);
|
|
450
|
+
return startUpdateLocationMock(eventParams);
|
|
451
|
+
};
|
|
452
|
+
const startUpdateLocation = withPermission(_startUpdateLocation, "geolocation");
|
|
453
|
+
//#endregion
|
|
454
|
+
//#region src/mock/device/camera.ts
|
|
455
|
+
/**
|
|
456
|
+
* Camera & Album Photos mock
|
|
457
|
+
* mock/web/prompt 모드 지원
|
|
458
|
+
*/
|
|
459
|
+
async function openCameraMock() {
|
|
460
|
+
const images = getMockImages();
|
|
461
|
+
return {
|
|
462
|
+
id: crypto.randomUUID(),
|
|
463
|
+
dataUri: images[0]
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
async function openCameraWeb() {
|
|
467
|
+
return new Promise((resolve, reject) => {
|
|
468
|
+
const input = document.createElement("input");
|
|
469
|
+
input.type = "file";
|
|
470
|
+
input.accept = "image/*";
|
|
471
|
+
input.capture = "environment";
|
|
472
|
+
let settled = false;
|
|
473
|
+
input.onchange = () => {
|
|
474
|
+
settled = true;
|
|
475
|
+
const file = input.files?.[0];
|
|
476
|
+
if (!file) {
|
|
477
|
+
reject(/* @__PURE__ */ new Error("No file selected"));
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
480
|
+
const reader = new FileReader();
|
|
481
|
+
reader.onload = () => resolve({
|
|
482
|
+
id: crypto.randomUUID(),
|
|
483
|
+
dataUri: reader.result
|
|
484
|
+
});
|
|
485
|
+
reader.onerror = () => reject(/* @__PURE__ */ new Error("Failed to read file"));
|
|
486
|
+
reader.readAsDataURL(file);
|
|
487
|
+
};
|
|
488
|
+
const onFocus = () => {
|
|
489
|
+
setTimeout(() => {
|
|
490
|
+
if (!settled) reject(/* @__PURE__ */ new Error("File picker cancelled"));
|
|
491
|
+
window.removeEventListener("focus", onFocus);
|
|
492
|
+
}, 300);
|
|
493
|
+
};
|
|
494
|
+
window.addEventListener("focus", onFocus);
|
|
495
|
+
input.click();
|
|
496
|
+
});
|
|
497
|
+
}
|
|
498
|
+
async function openCameraPrompt() {
|
|
499
|
+
const dataUri = await waitForPromptResponse("camera");
|
|
500
|
+
return {
|
|
501
|
+
id: crypto.randomUUID(),
|
|
502
|
+
dataUri
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
const _openCamera = async (_options) => {
|
|
506
|
+
checkPermission("camera", "openCamera");
|
|
507
|
+
const mode = aitState.state.deviceModes.camera;
|
|
508
|
+
if (mode === "web") return openCameraWeb();
|
|
509
|
+
if (mode === "prompt") return openCameraPrompt();
|
|
510
|
+
return openCameraMock();
|
|
511
|
+
};
|
|
512
|
+
const openCamera = withPermission(_openCamera, "camera");
|
|
513
|
+
async function fetchAlbumPhotosMock(maxCount) {
|
|
514
|
+
return getMockImages().slice(0, maxCount).map((dataUri) => ({
|
|
515
|
+
id: crypto.randomUUID(),
|
|
516
|
+
dataUri
|
|
517
|
+
}));
|
|
518
|
+
}
|
|
519
|
+
async function fetchAlbumPhotosWeb(maxCount) {
|
|
520
|
+
return new Promise((resolve, reject) => {
|
|
521
|
+
const input = document.createElement("input");
|
|
522
|
+
input.type = "file";
|
|
523
|
+
input.accept = "image/*";
|
|
524
|
+
input.multiple = true;
|
|
525
|
+
let settled = false;
|
|
526
|
+
input.onchange = async () => {
|
|
527
|
+
settled = true;
|
|
528
|
+
const files = Array.from(input.files ?? []).slice(0, maxCount);
|
|
529
|
+
if (files.length === 0) {
|
|
530
|
+
reject(/* @__PURE__ */ new Error("No files selected"));
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
533
|
+
resolve(await Promise.all(files.map((file) => new Promise((res, rej) => {
|
|
534
|
+
const reader = new FileReader();
|
|
535
|
+
reader.onload = () => res({
|
|
536
|
+
id: crypto.randomUUID(),
|
|
537
|
+
dataUri: reader.result
|
|
538
|
+
});
|
|
539
|
+
reader.onerror = () => rej(/* @__PURE__ */ new Error("Failed to read file"));
|
|
540
|
+
reader.readAsDataURL(file);
|
|
541
|
+
}))));
|
|
542
|
+
};
|
|
543
|
+
const onFocus = () => {
|
|
544
|
+
setTimeout(() => {
|
|
545
|
+
if (!settled) reject(/* @__PURE__ */ new Error("File picker cancelled"));
|
|
546
|
+
window.removeEventListener("focus", onFocus);
|
|
547
|
+
}, 300);
|
|
548
|
+
};
|
|
549
|
+
window.addEventListener("focus", onFocus);
|
|
550
|
+
input.click();
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
async function fetchAlbumPhotosPrompt(maxCount) {
|
|
554
|
+
return (await waitForPromptResponse("photos")).slice(0, maxCount).map((dataUri) => ({
|
|
555
|
+
id: crypto.randomUUID(),
|
|
556
|
+
dataUri
|
|
557
|
+
}));
|
|
558
|
+
}
|
|
559
|
+
const _fetchAlbumPhotos = async (options) => {
|
|
560
|
+
checkPermission("photos", "fetchAlbumPhotos");
|
|
561
|
+
const maxCount = options?.maxCount ?? 10;
|
|
562
|
+
const mode = aitState.state.deviceModes.photos;
|
|
563
|
+
if (mode === "web") return fetchAlbumPhotosWeb(maxCount);
|
|
564
|
+
if (mode === "prompt") return fetchAlbumPhotosPrompt(maxCount);
|
|
565
|
+
return fetchAlbumPhotosMock(maxCount);
|
|
566
|
+
};
|
|
567
|
+
const fetchAlbumPhotos = withPermission(_fetchAlbumPhotos, "photos");
|
|
568
|
+
//#endregion
|
|
569
|
+
//#region src/mock/device/clipboard.ts
|
|
570
|
+
/**
|
|
571
|
+
* Clipboard mock
|
|
572
|
+
* mock/web 모드 지원
|
|
573
|
+
*/
|
|
574
|
+
const _getClipboardText = async () => {
|
|
575
|
+
checkPermission("clipboard", "getClipboardText");
|
|
576
|
+
if (aitState.state.deviceModes.clipboard === "mock") return aitState.state.mockData.clipboardText;
|
|
577
|
+
try {
|
|
578
|
+
return await navigator.clipboard.readText();
|
|
579
|
+
} catch {
|
|
580
|
+
return "";
|
|
581
|
+
}
|
|
582
|
+
};
|
|
583
|
+
const getClipboardText = withPermission(_getClipboardText, "clipboard");
|
|
584
|
+
const _setClipboardText = async (text) => {
|
|
585
|
+
checkPermission("clipboard", "setClipboardText");
|
|
586
|
+
if (aitState.state.deviceModes.clipboard === "mock") {
|
|
587
|
+
aitState.patch("mockData", { clipboardText: text });
|
|
588
|
+
return;
|
|
589
|
+
}
|
|
590
|
+
await navigator.clipboard.writeText(text);
|
|
591
|
+
};
|
|
592
|
+
const setClipboardText = withPermission(_setClipboardText, "clipboard");
|
|
593
|
+
//#endregion
|
|
594
|
+
//#region src/mock/device/contacts.ts
|
|
595
|
+
/**
|
|
596
|
+
* Contacts mock
|
|
597
|
+
*/
|
|
598
|
+
const _fetchContacts = async (options) => {
|
|
599
|
+
checkPermission("contacts", "fetchContacts");
|
|
600
|
+
let contacts = aitState.state.contacts;
|
|
601
|
+
if (options.query?.contains) {
|
|
602
|
+
const q = options.query.contains.toLowerCase();
|
|
603
|
+
contacts = contacts.filter((c) => c.name.toLowerCase().includes(q) || c.phoneNumber.includes(q));
|
|
604
|
+
}
|
|
605
|
+
const sliced = contacts.slice(options.offset, options.offset + options.size);
|
|
606
|
+
const nextOffset = options.offset + options.size;
|
|
607
|
+
return {
|
|
608
|
+
result: sliced,
|
|
609
|
+
nextOffset: nextOffset < contacts.length ? nextOffset : null,
|
|
610
|
+
done: nextOffset >= contacts.length
|
|
611
|
+
};
|
|
612
|
+
};
|
|
613
|
+
const fetchContacts = withPermission(_fetchContacts, "contacts");
|
|
614
|
+
//#endregion
|
|
615
|
+
//#region src/mock/device/haptic.ts
|
|
616
|
+
/**
|
|
617
|
+
* Haptic Feedback & saveBase64Data mock
|
|
618
|
+
*/
|
|
619
|
+
async function generateHapticFeedback(options) {
|
|
620
|
+
console.log(`[@ait-co/devtools] haptic: ${options.type}`);
|
|
621
|
+
aitState.logAnalytics({
|
|
622
|
+
type: "haptic",
|
|
623
|
+
params: { hapticType: options.type }
|
|
624
|
+
});
|
|
625
|
+
}
|
|
626
|
+
async function saveBase64Data(params) {
|
|
627
|
+
const a = document.createElement("a");
|
|
628
|
+
a.href = `data:${params.mimeType};base64,${params.data}`;
|
|
629
|
+
a.download = params.fileName;
|
|
630
|
+
a.click();
|
|
631
|
+
}
|
|
632
|
+
//#endregion
|
|
633
|
+
//#region src/mock/device/network.ts
|
|
634
|
+
/**
|
|
635
|
+
* Network Status mock (mode-aware helper)
|
|
636
|
+
* navigation 모듈에서 사용. circular dep 방지를 위해 device에 위치.
|
|
637
|
+
*/
|
|
638
|
+
/**
|
|
639
|
+
* Web mode: uses navigator.connection.effectiveType (4g/3g/2g) and navigator.onLine.
|
|
640
|
+
* Limitations: WIFI, 5G, WWAN cannot be detected via the Network Information API.
|
|
641
|
+
* Falls back to state-based value when effectiveType is unavailable.
|
|
642
|
+
*/
|
|
643
|
+
function getNetworkStatusByMode() {
|
|
644
|
+
const mode = aitState.state.deviceModes.network;
|
|
645
|
+
if (mode === "mock") return null;
|
|
646
|
+
if (mode === "web") {
|
|
647
|
+
if (!navigator.onLine) return "OFFLINE";
|
|
648
|
+
const conn = navigator.connection;
|
|
649
|
+
if (conn?.effectiveType) return {
|
|
650
|
+
"4g": "4G",
|
|
651
|
+
"3g": "3G",
|
|
652
|
+
"2g": "2G",
|
|
653
|
+
"slow-2g": "2G"
|
|
654
|
+
}[conn.effectiveType] ?? "UNKNOWN";
|
|
655
|
+
return aitState.state.networkStatus;
|
|
656
|
+
}
|
|
657
|
+
return null;
|
|
658
|
+
}
|
|
659
|
+
//#endregion
|
|
660
|
+
//#region src/mock/navigation/index.ts
|
|
661
|
+
/**
|
|
662
|
+
* 화면/네비게이션/이벤트 mock
|
|
663
|
+
*/
|
|
41
664
|
async function closeView() {
|
|
42
|
-
|
|
43
|
-
|
|
665
|
+
console.log("[@ait-co/devtools] closeView called");
|
|
666
|
+
window.history.back();
|
|
44
667
|
}
|
|
45
668
|
async function openURL(url) {
|
|
46
|
-
|
|
47
|
-
|
|
669
|
+
console.log("[@ait-co/devtools] openURL:", url);
|
|
670
|
+
window.open(url, "_blank");
|
|
48
671
|
}
|
|
49
672
|
async function share(message) {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
673
|
+
if (navigator.share) {
|
|
674
|
+
await navigator.share({ text: message.message });
|
|
675
|
+
return;
|
|
676
|
+
}
|
|
677
|
+
console.log("[@ait-co/devtools] share:", message.message);
|
|
55
678
|
}
|
|
56
679
|
async function getTossShareLink(path, _ogImageUrl) {
|
|
57
|
-
|
|
680
|
+
return `https://toss.im/share/mock${path}`;
|
|
58
681
|
}
|
|
59
682
|
async function setIosSwipeGestureEnabled(_options) {
|
|
60
|
-
|
|
683
|
+
console.log("[@ait-co/devtools] setIosSwipeGestureEnabled:", _options.isEnabled);
|
|
61
684
|
}
|
|
62
685
|
async function setDeviceOrientation(_options) {
|
|
63
|
-
|
|
686
|
+
console.log("[@ait-co/devtools] setDeviceOrientation:", _options.type);
|
|
64
687
|
}
|
|
65
688
|
async function setScreenAwakeMode(options) {
|
|
66
|
-
|
|
67
|
-
|
|
689
|
+
console.log("[@ait-co/devtools] setScreenAwakeMode:", options.enabled);
|
|
690
|
+
return { enabled: options.enabled };
|
|
68
691
|
}
|
|
69
692
|
async function setSecureScreen(options) {
|
|
70
|
-
|
|
71
|
-
|
|
693
|
+
console.log("[@ait-co/devtools] setSecureScreen:", options.enabled);
|
|
694
|
+
return { enabled: options.enabled };
|
|
72
695
|
}
|
|
73
696
|
async function requestReview() {
|
|
74
|
-
|
|
697
|
+
console.log("[@ait-co/devtools] requestReview called");
|
|
75
698
|
}
|
|
76
699
|
requestReview.isSupported = () => true;
|
|
77
700
|
function getPlatformOS() {
|
|
78
|
-
|
|
701
|
+
return aitState.state.platform;
|
|
79
702
|
}
|
|
80
703
|
function getOperationalEnvironment() {
|
|
81
|
-
|
|
704
|
+
return aitState.state.environment;
|
|
82
705
|
}
|
|
83
706
|
function getTossAppVersion() {
|
|
84
|
-
|
|
707
|
+
return aitState.state.appVersion;
|
|
85
708
|
}
|
|
86
709
|
function isMinVersionSupported(minVersions) {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
return true;
|
|
710
|
+
const required = aitState.state.platform === "ios" ? minVersions.ios : minVersions.android;
|
|
711
|
+
if (required === "always") return true;
|
|
712
|
+
if (required === "never") return false;
|
|
713
|
+
const current = aitState.state.appVersion.split(".").map(Number);
|
|
714
|
+
const min = required.split(".").map(Number);
|
|
715
|
+
for (let i = 0; i < 3; i++) {
|
|
716
|
+
if ((current[i] ?? 0) > (min[i] ?? 0)) return true;
|
|
717
|
+
if ((current[i] ?? 0) < (min[i] ?? 0)) return false;
|
|
718
|
+
}
|
|
719
|
+
return true;
|
|
98
720
|
}
|
|
99
721
|
function getSchemeUri() {
|
|
100
|
-
|
|
722
|
+
return aitState.state.schemeUri || window.location.pathname;
|
|
101
723
|
}
|
|
102
724
|
function getLocale() {
|
|
103
|
-
|
|
725
|
+
return aitState.state.locale;
|
|
104
726
|
}
|
|
105
727
|
function getDeviceId() {
|
|
106
|
-
|
|
728
|
+
return aitState.state.deviceId;
|
|
107
729
|
}
|
|
108
730
|
function getGroupId() {
|
|
109
|
-
|
|
731
|
+
return aitState.state.groupId;
|
|
110
732
|
}
|
|
111
733
|
async function getNetworkStatus() {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
734
|
+
const modeResult = getNetworkStatusByMode();
|
|
735
|
+
if (modeResult) return modeResult;
|
|
736
|
+
return aitState.state.networkStatus;
|
|
115
737
|
}
|
|
116
738
|
async function getServerTime() {
|
|
117
|
-
|
|
739
|
+
return Date.now();
|
|
118
740
|
}
|
|
119
741
|
getServerTime.isSupported = () => true;
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
};
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
};
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
const detail = e.detail;
|
|
143
|
-
onEvent(detail);
|
|
144
|
-
};
|
|
145
|
-
window.addEventListener(`__ait:${event}`, handler);
|
|
146
|
-
return () => window.removeEventListener(`__ait:${event}`, handler);
|
|
147
|
-
}
|
|
148
|
-
};
|
|
742
|
+
const graniteEvent = { addEventListener(event, { onEvent, onError }) {
|
|
743
|
+
const handler = () => {
|
|
744
|
+
try {
|
|
745
|
+
onEvent();
|
|
746
|
+
} catch (e) {
|
|
747
|
+
onError?.(e instanceof Error ? e : new Error(String(e)));
|
|
748
|
+
}
|
|
749
|
+
};
|
|
750
|
+
window.addEventListener(`__ait:${event}`, handler);
|
|
751
|
+
return () => window.removeEventListener(`__ait:${event}`, handler);
|
|
752
|
+
} };
|
|
753
|
+
const appsInTossEvent = { addEventListener(_event, _handlers) {
|
|
754
|
+
return () => {};
|
|
755
|
+
} };
|
|
756
|
+
const tdsEvent = { addEventListener(event, { onEvent }) {
|
|
757
|
+
const handler = (e) => {
|
|
758
|
+
const detail = e.detail;
|
|
759
|
+
onEvent(detail);
|
|
760
|
+
};
|
|
761
|
+
window.addEventListener(`__ait:${event}`, handler);
|
|
762
|
+
return () => window.removeEventListener(`__ait:${event}`, handler);
|
|
763
|
+
} };
|
|
149
764
|
function onVisibilityChangedByTransparentServiceWeb(eventParams) {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
765
|
+
const handler = () => eventParams.onEvent(!document.hidden);
|
|
766
|
+
document.addEventListener("visibilitychange", handler);
|
|
767
|
+
return () => document.removeEventListener("visibilitychange", handler);
|
|
153
768
|
}
|
|
154
|
-
|
|
155
|
-
getDeploymentId: () => aitState.state.deploymentId
|
|
156
|
-
};
|
|
769
|
+
const env = { getDeploymentId: () => aitState.state.deploymentId };
|
|
157
770
|
function getAppsInTossGlobals() {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
return aitState.subscribe(() => onEvent({ ...aitState.state.safeAreaInsets }));
|
|
171
|
-
}
|
|
771
|
+
return {
|
|
772
|
+
deploymentId: aitState.state.deploymentId,
|
|
773
|
+
brandDisplayName: aitState.state.brand.displayName,
|
|
774
|
+
brandIcon: aitState.state.brand.icon,
|
|
775
|
+
brandPrimaryColor: aitState.state.brand.primaryColor
|
|
776
|
+
};
|
|
777
|
+
}
|
|
778
|
+
const SafeAreaInsets = {
|
|
779
|
+
get: () => ({ ...aitState.state.safeAreaInsets }),
|
|
780
|
+
subscribe: ({ onEvent }) => {
|
|
781
|
+
return aitState.subscribe(() => onEvent({ ...aitState.state.safeAreaInsets }));
|
|
782
|
+
}
|
|
172
783
|
};
|
|
784
|
+
/** @deprecated */
|
|
173
785
|
function getSafeAreaInsets() {
|
|
174
|
-
|
|
786
|
+
return aitState.state.safeAreaInsets.top;
|
|
175
787
|
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
788
|
+
//#endregion
|
|
789
|
+
//#region src/mock/iap/index.ts
|
|
790
|
+
/**
|
|
791
|
+
* IAP (인앱결제) mock
|
|
792
|
+
*/
|
|
793
|
+
let orderCounter = 0;
|
|
179
794
|
function generateOrderId() {
|
|
180
|
-
|
|
795
|
+
return `mock-order-${++orderCounter}-${Date.now()}`;
|
|
181
796
|
}
|
|
182
797
|
function buildOrderResult(sku) {
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
798
|
+
const product = aitState.state.iap.products.find((p) => p.sku === sku);
|
|
799
|
+
const amountStr = product?.displayAmount?.replace(/[^0-9]/g, "") ?? "1000";
|
|
800
|
+
return {
|
|
801
|
+
orderId: generateOrderId(),
|
|
802
|
+
displayName: product?.displayName ?? "Mock Product",
|
|
803
|
+
displayAmount: product?.displayAmount ?? "1,000원",
|
|
804
|
+
amount: parseInt(amountStr, 10) || 1e3,
|
|
805
|
+
currency: "KRW",
|
|
806
|
+
fraction: 0,
|
|
807
|
+
miniAppIconUrl: product?.iconUrl || null
|
|
808
|
+
};
|
|
194
809
|
}
|
|
195
810
|
async function handlePurchase(sku, processProductGrant, onEvent, onError) {
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
gracePeriodExpiresAt: null,
|
|
277
|
-
isAccessible: true
|
|
278
|
-
}
|
|
279
|
-
};
|
|
280
|
-
}
|
|
811
|
+
const nextResult = aitState.state.iap.nextResult;
|
|
812
|
+
await new Promise((r) => setTimeout(r, 300));
|
|
813
|
+
if (nextResult !== "success") {
|
|
814
|
+
onError({ code: nextResult });
|
|
815
|
+
return;
|
|
816
|
+
}
|
|
817
|
+
const result = buildOrderResult(sku);
|
|
818
|
+
try {
|
|
819
|
+
if (!await processProductGrant({ orderId: result.orderId })) {
|
|
820
|
+
onError({ code: "PRODUCT_NOT_GRANTED_BY_PARTNER" });
|
|
821
|
+
return;
|
|
822
|
+
}
|
|
823
|
+
} catch (e) {
|
|
824
|
+
onError(e);
|
|
825
|
+
return;
|
|
826
|
+
}
|
|
827
|
+
aitState.patch("iap", { completedOrders: [...aitState.state.iap.completedOrders, {
|
|
828
|
+
orderId: result.orderId,
|
|
829
|
+
sku,
|
|
830
|
+
status: "COMPLETED",
|
|
831
|
+
date: (/* @__PURE__ */ new Date()).toISOString()
|
|
832
|
+
}] });
|
|
833
|
+
await onEvent({
|
|
834
|
+
type: "success",
|
|
835
|
+
data: result
|
|
836
|
+
});
|
|
837
|
+
}
|
|
838
|
+
const IAP = createMockProxy("IAP", {
|
|
839
|
+
createOneTimePurchaseOrder(params) {
|
|
840
|
+
handlePurchase(params.options.sku ?? params.options.productId ?? "", params.options.processProductGrant, params.onEvent, params.onError).catch((e) => console.error("[@ait-co/devtools] IAP unexpected error:", e));
|
|
841
|
+
return () => {};
|
|
842
|
+
},
|
|
843
|
+
createSubscriptionPurchaseOrder(params) {
|
|
844
|
+
handlePurchase(params.options.sku, params.options.processProductGrant, params.onEvent, params.onError).catch((e) => console.error("[@ait-co/devtools] IAP unexpected error:", e));
|
|
845
|
+
return () => {};
|
|
846
|
+
},
|
|
847
|
+
async getProductItemList() {
|
|
848
|
+
return { products: aitState.state.iap.products.map((p) => ({
|
|
849
|
+
...p,
|
|
850
|
+
...p.type === "SUBSCRIPTION" ? { renewalCycle: p.renewalCycle ?? "MONTHLY" } : {}
|
|
851
|
+
})) };
|
|
852
|
+
},
|
|
853
|
+
async getPendingOrders() {
|
|
854
|
+
return { orders: [...aitState.state.iap.pendingOrders] };
|
|
855
|
+
},
|
|
856
|
+
async getCompletedOrRefundedOrders() {
|
|
857
|
+
return {
|
|
858
|
+
hasNext: false,
|
|
859
|
+
nextKey: null,
|
|
860
|
+
orders: [...aitState.state.iap.completedOrders]
|
|
861
|
+
};
|
|
862
|
+
},
|
|
863
|
+
async completeProductGrant(args) {
|
|
864
|
+
const idx = aitState.state.iap.pendingOrders.findIndex((o) => o.orderId === args.params.orderId);
|
|
865
|
+
if (idx !== -1) {
|
|
866
|
+
const order = aitState.state.iap.pendingOrders[idx];
|
|
867
|
+
const pendingOrders = aitState.state.iap.pendingOrders.filter((_, i) => i !== idx);
|
|
868
|
+
const completedOrders = [...aitState.state.iap.completedOrders, {
|
|
869
|
+
orderId: order.orderId,
|
|
870
|
+
sku: order.sku,
|
|
871
|
+
status: "COMPLETED",
|
|
872
|
+
date: (/* @__PURE__ */ new Date()).toISOString()
|
|
873
|
+
}];
|
|
874
|
+
aitState.patch("iap", {
|
|
875
|
+
pendingOrders,
|
|
876
|
+
completedOrders
|
|
877
|
+
});
|
|
878
|
+
}
|
|
879
|
+
return true;
|
|
880
|
+
},
|
|
881
|
+
async getSubscriptionInfo(_args) {
|
|
882
|
+
return { subscription: {
|
|
883
|
+
catalogId: 1,
|
|
884
|
+
status: "ACTIVE",
|
|
885
|
+
expiresAt: new Date(Date.now() + 720 * 60 * 60 * 1e3).toISOString(),
|
|
886
|
+
isAutoRenew: true,
|
|
887
|
+
gracePeriodExpiresAt: null,
|
|
888
|
+
isAccessible: true
|
|
889
|
+
} };
|
|
890
|
+
}
|
|
281
891
|
});
|
|
282
892
|
async function checkoutPayment(options) {
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
893
|
+
const { nextResult, failReason } = aitState.state.payment;
|
|
894
|
+
console.log("[@ait-co/devtools] checkoutPayment:", options.params.payToken);
|
|
895
|
+
await new Promise((r) => setTimeout(r, 300));
|
|
896
|
+
if (nextResult === "success") return { success: true };
|
|
897
|
+
return {
|
|
898
|
+
success: false,
|
|
899
|
+
reason: failReason || "Mock payment failed"
|
|
900
|
+
};
|
|
290
901
|
}
|
|
291
|
-
|
|
292
|
-
|
|
902
|
+
//#endregion
|
|
903
|
+
//#region src/mock/ads/index.ts
|
|
904
|
+
/**
|
|
905
|
+
* 광고 mock (GoogleAdMob, TossAds, FullScreenAd)
|
|
906
|
+
*/
|
|
293
907
|
function withIsSupported(fn) {
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
908
|
+
fn.isSupported = () => true;
|
|
909
|
+
return fn;
|
|
910
|
+
}
|
|
911
|
+
const GoogleAdMob = createMockProxy("GoogleAdMob", {
|
|
912
|
+
loadAppsInTossAdMob: withIsSupported((args) => {
|
|
913
|
+
setTimeout(() => {
|
|
914
|
+
aitState.patch("ads", { isLoaded: true });
|
|
915
|
+
args.onEvent({
|
|
916
|
+
type: "loaded",
|
|
917
|
+
data: { adGroupId: args.options?.adGroupId }
|
|
918
|
+
});
|
|
919
|
+
}, 200);
|
|
920
|
+
return () => {};
|
|
921
|
+
}),
|
|
922
|
+
showAppsInTossAdMob: withIsSupported((args) => {
|
|
923
|
+
if (!aitState.state.ads.isLoaded) {
|
|
924
|
+
args.onError(/* @__PURE__ */ new Error("Ad not loaded"));
|
|
925
|
+
return () => {};
|
|
926
|
+
}
|
|
927
|
+
setTimeout(() => args.onEvent({ type: "requested" }), 50);
|
|
928
|
+
setTimeout(() => args.onEvent({ type: "show" }), 100);
|
|
929
|
+
setTimeout(() => args.onEvent({ type: "impression" }), 150);
|
|
930
|
+
setTimeout(() => {
|
|
931
|
+
args.onEvent({
|
|
932
|
+
type: "userEarnedReward",
|
|
933
|
+
data: {
|
|
934
|
+
unitType: "coins",
|
|
935
|
+
unitAmount: 10
|
|
936
|
+
}
|
|
937
|
+
});
|
|
938
|
+
}, 1e3);
|
|
939
|
+
setTimeout(() => {
|
|
940
|
+
args.onEvent({ type: "dismissed" });
|
|
941
|
+
aitState.patch("ads", { isLoaded: false });
|
|
942
|
+
}, 1500);
|
|
943
|
+
return () => {};
|
|
944
|
+
}),
|
|
945
|
+
isAppsInTossAdMobLoaded: withIsSupported(async (_options) => aitState.state.ads.isLoaded)
|
|
328
946
|
});
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
}),
|
|
355
|
-
destroyAll: withIsSupported(() => {
|
|
356
|
-
})
|
|
947
|
+
const TossAds = createMockProxy("TossAds", {
|
|
948
|
+
initialize: withIsSupported((_options) => {
|
|
949
|
+
console.log("[@ait-co/devtools] TossAds.initialize (mock)");
|
|
950
|
+
}),
|
|
951
|
+
attach: withIsSupported((_adGroupId, target, _options) => {
|
|
952
|
+
const el = typeof target === "string" ? document.querySelector(target) : target;
|
|
953
|
+
if (el) {
|
|
954
|
+
const placeholder = document.createElement("div");
|
|
955
|
+
placeholder.style.cssText = "background:#f0f0f0;border:1px dashed #999;padding:16px;text-align:center;color:#666;font-size:14px;";
|
|
956
|
+
placeholder.textContent = "[@ait-co/devtools] TossAds Placeholder";
|
|
957
|
+
el.appendChild(placeholder);
|
|
958
|
+
}
|
|
959
|
+
}),
|
|
960
|
+
attachBanner: withIsSupported((_adGroupId, target, _options) => {
|
|
961
|
+
const el = typeof target === "string" ? document.querySelector(target) : target;
|
|
962
|
+
if (el) {
|
|
963
|
+
const placeholder = document.createElement("div");
|
|
964
|
+
placeholder.style.cssText = "background:#f0f0f0;border:1px dashed #999;padding:12px;text-align:center;color:#666;font-size:12px;";
|
|
965
|
+
placeholder.textContent = "[@ait-co/devtools] Banner Ad Placeholder";
|
|
966
|
+
el.appendChild(placeholder);
|
|
967
|
+
}
|
|
968
|
+
return { destroy: () => {} };
|
|
969
|
+
}),
|
|
970
|
+
destroy: withIsSupported((_slotId) => {}),
|
|
971
|
+
destroyAll: withIsSupported(() => {})
|
|
357
972
|
});
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
973
|
+
const loadFullScreenAd = withIsSupported((args) => {
|
|
974
|
+
setTimeout(() => {
|
|
975
|
+
aitState.patch("ads", { isLoaded: true });
|
|
976
|
+
args.onEvent({
|
|
977
|
+
type: "loaded",
|
|
978
|
+
data: { adGroupId: args.options?.adGroupId }
|
|
979
|
+
});
|
|
980
|
+
}, 200);
|
|
981
|
+
return () => {};
|
|
365
982
|
});
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
return () => {
|
|
375
|
-
};
|
|
983
|
+
const showFullScreenAd = withIsSupported((args) => {
|
|
984
|
+
if (!aitState.state.ads.isLoaded) {
|
|
985
|
+
args.onError(/* @__PURE__ */ new Error("Ad not loaded"));
|
|
986
|
+
return () => {};
|
|
987
|
+
}
|
|
988
|
+
setTimeout(() => args.onEvent({ type: "show" }), 100);
|
|
989
|
+
setTimeout(() => args.onEvent({ type: "dismissed" }), 1500);
|
|
990
|
+
return () => {};
|
|
376
991
|
});
|
|
377
|
-
|
|
378
|
-
|
|
992
|
+
//#endregion
|
|
993
|
+
//#region src/mock/game/index.ts
|
|
994
|
+
/**
|
|
995
|
+
* 게임/프로모션 mock
|
|
996
|
+
*/
|
|
379
997
|
async function grantPromotionReward(params) {
|
|
380
|
-
|
|
381
|
-
|
|
998
|
+
console.log("[@ait-co/devtools] grantPromotionReward:", params.params);
|
|
999
|
+
return { key: `mock-reward-${Date.now()}` };
|
|
382
1000
|
}
|
|
383
1001
|
async function grantPromotionRewardForGame(params) {
|
|
384
|
-
|
|
385
|
-
|
|
1002
|
+
console.log("[@ait-co/devtools] grantPromotionRewardForGame:", params.params);
|
|
1003
|
+
return { key: `mock-reward-${Date.now()}` };
|
|
386
1004
|
}
|
|
387
1005
|
async function submitGameCenterLeaderBoardScore(params) {
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
1006
|
+
aitState.patch("game", { leaderboardScores: [...aitState.state.game.leaderboardScores, {
|
|
1007
|
+
score: params.score,
|
|
1008
|
+
timestamp: Date.now()
|
|
1009
|
+
}] });
|
|
1010
|
+
return { statusCode: "SUCCESS" };
|
|
392
1011
|
}
|
|
393
1012
|
async function getGameCenterGameProfile() {
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
1013
|
+
const profile = aitState.state.game.profile;
|
|
1014
|
+
if (!profile) return { statusCode: "PROFILE_NOT_FOUND" };
|
|
1015
|
+
return {
|
|
1016
|
+
statusCode: "SUCCESS",
|
|
1017
|
+
nickname: profile.nickname,
|
|
1018
|
+
profileImageUri: profile.profileImageUri
|
|
1019
|
+
};
|
|
401
1020
|
}
|
|
402
1021
|
async function openGameCenterLeaderboard() {
|
|
403
|
-
|
|
1022
|
+
console.log("[@ait-co/devtools] openGameCenterLeaderboard (no-op in browser)");
|
|
404
1023
|
}
|
|
405
1024
|
function contactsViral(params) {
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
};
|
|
1025
|
+
setTimeout(() => {
|
|
1026
|
+
params.onEvent({
|
|
1027
|
+
type: "close",
|
|
1028
|
+
data: {
|
|
1029
|
+
closeReason: "noReward",
|
|
1030
|
+
sentRewardsCount: 0
|
|
1031
|
+
}
|
|
1032
|
+
});
|
|
1033
|
+
}, 500);
|
|
1034
|
+
return () => {};
|
|
417
1035
|
}
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
1036
|
+
//#endregion
|
|
1037
|
+
//#region src/mock/analytics/index.ts
|
|
1038
|
+
/**
|
|
1039
|
+
* Analytics mock
|
|
1040
|
+
*/
|
|
1041
|
+
const Analytics = {
|
|
1042
|
+
screen: (params) => {
|
|
1043
|
+
aitState.logAnalytics({
|
|
1044
|
+
type: "screen",
|
|
1045
|
+
params: params ?? {}
|
|
1046
|
+
});
|
|
1047
|
+
return Promise.resolve();
|
|
1048
|
+
},
|
|
1049
|
+
impression: (params) => {
|
|
1050
|
+
aitState.logAnalytics({
|
|
1051
|
+
type: "impression",
|
|
1052
|
+
params: params ?? {}
|
|
1053
|
+
});
|
|
1054
|
+
return Promise.resolve();
|
|
1055
|
+
},
|
|
1056
|
+
click: (params) => {
|
|
1057
|
+
aitState.logAnalytics({
|
|
1058
|
+
type: "click",
|
|
1059
|
+
params: params ?? {}
|
|
1060
|
+
});
|
|
1061
|
+
return Promise.resolve();
|
|
1062
|
+
}
|
|
433
1063
|
};
|
|
434
1064
|
async function eventLog(params) {
|
|
435
|
-
|
|
1065
|
+
aitState.logAnalytics({
|
|
1066
|
+
type: params.log_type,
|
|
1067
|
+
params: {
|
|
1068
|
+
log_name: params.log_name,
|
|
1069
|
+
...params.params
|
|
1070
|
+
}
|
|
1071
|
+
});
|
|
436
1072
|
}
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
};
|
|
447
|
-
export {
|
|
448
|
-
Accuracy,
|
|
449
|
-
Analytics,
|
|
450
|
-
GoogleAdMob,
|
|
451
|
-
IAP,
|
|
452
|
-
SafeAreaInsets,
|
|
453
|
-
Storage,
|
|
454
|
-
TossAds,
|
|
455
|
-
aitState,
|
|
456
|
-
appLogin,
|
|
457
|
-
appsInTossEvent,
|
|
458
|
-
appsInTossSignTossCert,
|
|
459
|
-
checkoutPayment,
|
|
460
|
-
closeView,
|
|
461
|
-
contactsViral,
|
|
462
|
-
env,
|
|
463
|
-
eventLog,
|
|
464
|
-
fetchAlbumPhotos,
|
|
465
|
-
fetchContacts,
|
|
466
|
-
generateHapticFeedback,
|
|
467
|
-
getAppsInTossGlobals,
|
|
468
|
-
getClipboardText,
|
|
469
|
-
getCurrentLocation,
|
|
470
|
-
getDefaultPlaceholderImages,
|
|
471
|
-
getDeviceId,
|
|
472
|
-
getGameCenterGameProfile,
|
|
473
|
-
getGroupId,
|
|
474
|
-
getIsTossLoginIntegratedService,
|
|
475
|
-
getLocale,
|
|
476
|
-
getNetworkStatus,
|
|
477
|
-
getOperationalEnvironment,
|
|
478
|
-
getPermission,
|
|
479
|
-
getPlatformOS,
|
|
480
|
-
getSafeAreaInsets,
|
|
481
|
-
getSchemeUri,
|
|
482
|
-
getServerTime,
|
|
483
|
-
getTossAppVersion,
|
|
484
|
-
getTossShareLink,
|
|
485
|
-
getUserKeyForGame,
|
|
486
|
-
graniteEvent,
|
|
487
|
-
grantPromotionReward,
|
|
488
|
-
grantPromotionRewardForGame,
|
|
489
|
-
isMinVersionSupported,
|
|
490
|
-
loadFullScreenAd,
|
|
491
|
-
onVisibilityChangedByTransparentServiceWeb,
|
|
492
|
-
openCamera,
|
|
493
|
-
openGameCenterLeaderboard,
|
|
494
|
-
openPermissionDialog,
|
|
495
|
-
openURL,
|
|
496
|
-
partner,
|
|
497
|
-
requestPermission,
|
|
498
|
-
requestReview,
|
|
499
|
-
saveBase64Data,
|
|
500
|
-
setClipboardText,
|
|
501
|
-
setDeviceOrientation,
|
|
502
|
-
setIosSwipeGestureEnabled,
|
|
503
|
-
setScreenAwakeMode,
|
|
504
|
-
setSecureScreen,
|
|
505
|
-
share,
|
|
506
|
-
showFullScreenAd,
|
|
507
|
-
startUpdateLocation,
|
|
508
|
-
submitGameCenterLeaderBoardScore,
|
|
509
|
-
tdsEvent
|
|
1073
|
+
//#endregion
|
|
1074
|
+
//#region src/mock/partner/index.ts
|
|
1075
|
+
const partner = {
|
|
1076
|
+
async addAccessoryButton(options) {
|
|
1077
|
+
console.log("[@ait-co/devtools] partner.addAccessoryButton:", options);
|
|
1078
|
+
},
|
|
1079
|
+
async removeAccessoryButton() {
|
|
1080
|
+
console.log("[@ait-co/devtools] partner.removeAccessoryButton");
|
|
1081
|
+
}
|
|
510
1082
|
};
|
|
1083
|
+
//#endregion
|
|
1084
|
+
export { Accuracy, Analytics, GoogleAdMob, IAP, SafeAreaInsets, Storage, TossAds, aitState, appLogin, appsInTossEvent, appsInTossSignTossCert, checkoutPayment, closeView, contactsViral, env, eventLog, fetchAlbumPhotos, fetchContacts, generateHapticFeedback, getAppsInTossGlobals, getClipboardText, getCurrentLocation, getDefaultPlaceholderImages, getDeviceId, getGameCenterGameProfile, getGroupId, getIsTossLoginIntegratedService, getLocale, getNetworkStatus, getOperationalEnvironment, getPermission, getPlatformOS, getSafeAreaInsets, getSchemeUri, getServerTime, getTossAppVersion, getTossShareLink, getUserKeyForGame, graniteEvent, grantPromotionReward, grantPromotionRewardForGame, isMinVersionSupported, loadFullScreenAd, onVisibilityChangedByTransparentServiceWeb, openCamera, openGameCenterLeaderboard, openPermissionDialog, openURL, partner, requestPermission, requestReview, saveBase64Data, setClipboardText, setDeviceOrientation, setIosSwipeGestureEnabled, setScreenAwakeMode, setSecureScreen, share, showFullScreenAd, startUpdateLocation, submitGameCenterLeaderBoardScore, tdsEvent };
|
|
1085
|
+
|
|
511
1086
|
//# sourceMappingURL=index.js.map
|