@ait-co/devtools 0.1.7 → 0.1.9
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 +37 -2
- package/dist/mock/index.d.ts +86 -1
- package/dist/mock/index.d.ts.map +1 -1
- package/dist/mock/index.js +317 -2
- package/dist/mock/index.js.map +1 -1
- package/dist/panel/index.js +597 -3
- package/dist/panel/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
- **60+ SDK API mock** — 인증, 결제, IAP, 위치, 카메라, 스토리지 등
|
|
8
8
|
- **Device API 모드 시스템** — mock / web / prompt 세 가지 모드로 디바이스 API 동작 전환
|
|
9
9
|
- **Device simulation** — iPhone/Galaxy 프리셋 + orientation 토글로 데스크탑 브라우저에서 모바일 뷰포트 시뮬레이션
|
|
10
|
-
- **Floating DevTools Panel** — 브라우저에서 SDK 상태를 실시간으로 제어 (
|
|
10
|
+
- **Floating DevTools Panel** — 브라우저에서 SDK 상태를 실시간으로 제어 (10개 탭, mock state preset library 포함)
|
|
11
11
|
- **모든 번들러 지원** — [unplugin](https://github.com/unjs/unplugin) 기반 Vite, Webpack, Rspack, esbuild, Rollup 통합
|
|
12
12
|
|
|
13
13
|
## Reference consumer
|
|
@@ -247,11 +247,12 @@ mock 모드에서 카메라/앨범 API는 더미 이미지를 반환합니다.
|
|
|
247
247
|
|
|
248
248
|
플러그인 사용 시 진입점 파일에 패널이 자동 주입됩니다. 화면 우하단의 **'AIT' 버튼**을 클릭하면 토글됩니다.
|
|
249
249
|
|
|
250
|
-
###
|
|
250
|
+
### 10개 탭
|
|
251
251
|
|
|
252
252
|
| 탭 | 설명 |
|
|
253
253
|
|---|---|
|
|
254
254
|
| **Environment** | 플랫폼 OS (ios/android), 앱 버전, 환경 (toss/sandbox), 로케일, 네트워크 상태, Safe Area Insets |
|
|
255
|
+
| **Presets** | 자주 쓰는 QA 시나리오(권한 거부, offline, 미로그인 등)를 한 클릭으로 적용/해제. 사용자 preset 저장/삭제 가능 |
|
|
255
256
|
| **Viewport** | 디바이스 프리셋(iPhone/Galaxy) + orientation 토글로 모바일 뷰포트 시뮬레이션 |
|
|
256
257
|
| **Permissions** | camera, photos, geolocation, clipboard, contacts, microphone 권한 상태 제어 (allowed/denied/notDetermined) |
|
|
257
258
|
| **Location** | 위도, 경도, 정확도 설정 |
|
|
@@ -263,6 +264,40 @@ mock 모드에서 카메라/앨범 API는 더미 이미지를 반환합니다.
|
|
|
263
264
|
|
|
264
265
|
> **prompt 모드 자동 열림**: prompt 모드로 설정된 API가 호출되면, Panel이 자동으로 Device 탭을 열고 사용자 입력 UI를 표시합니다.
|
|
265
266
|
|
|
267
|
+
### Mock state preset library (Presets 탭)
|
|
268
|
+
|
|
269
|
+
한 시나리오에 여러 mock 키가 동시에 일정 상태여야 하는 경우(예: "offline일 때 IAP `NETWORK_ERROR` + 결제 fail")를 매번 손으로 맞추지 않고 한 클릭으로 적용합니다. 적용된 preset은 ✓ 표시되며, 정의된 키 중 하나라도 변경되면 자동으로 indicator가 풀립니다 (preset이 정의하지 않은 키는 비교 대상이 아님).
|
|
270
|
+
|
|
271
|
+
내장 preset:
|
|
272
|
+
|
|
273
|
+
| ID | 의미 |
|
|
274
|
+
|---|---|
|
|
275
|
+
| `all-allowed` | 모든 권한 허용, WIFI, 로그인됨, IAP success — 기본 시나리오 복귀 |
|
|
276
|
+
| `permission-denied` | camera / photos / geolocation / contacts 거부 |
|
|
277
|
+
| `offline` | `getNetworkStatus` → OFFLINE, IAP `NETWORK_ERROR`, 결제 fail |
|
|
278
|
+
| `logged-out` | `auth.isLoggedIn=false`. 로그인 플로우 검증 |
|
|
279
|
+
| `iap-pending` | IAP `nextResult` → `PAYMENT_PENDING` |
|
|
280
|
+
| `ads-no-fill` | 광고 fill 실패 분기 |
|
|
281
|
+
|
|
282
|
+
사용자가 토글로 만든 임의 상태는 "Save current as preset" 버튼으로 저장됩니다 (`localStorage` 영속, `__ait_preset:<id>` prefix). 저장된 preset은 새로고침/탭 재진입 후에도 유지됩니다. Preset 적용 범위는 `networkStatus / permissions / auth / iap / ads / payment` 슬라이스로 제한 — viewport나 brand 같은 무관한 상태가 흔들리지 않습니다.
|
|
283
|
+
|
|
284
|
+
코드에서도 export됩니다:
|
|
285
|
+
|
|
286
|
+
```ts
|
|
287
|
+
import { applyPreset, builtInPresets, saveUserPreset } from '@ait-co/devtools';
|
|
288
|
+
|
|
289
|
+
// 내장 preset 적용
|
|
290
|
+
const offline = builtInPresets.find((p) => p.id === 'offline')!;
|
|
291
|
+
applyPreset(offline.state);
|
|
292
|
+
|
|
293
|
+
// 커스텀 preset 저장
|
|
294
|
+
saveUserPreset('My QA scenario', {
|
|
295
|
+
networkStatus: 'OFFLINE',
|
|
296
|
+
permissions: { camera: 'denied' },
|
|
297
|
+
auth: { isLoggedIn: false },
|
|
298
|
+
});
|
|
299
|
+
```
|
|
300
|
+
|
|
266
301
|
## Device simulation (Viewport 탭)
|
|
267
302
|
|
|
268
303
|
데스크탑 브라우저에서 모바일 미니앱을 개발할 때, 실제 디바이스 해상도/safe area/노치/홈 인디케이터/앱인토스 nav bar를 반영해 레이아웃을 검증할 수 있습니다.
|
package/dist/mock/index.d.ts
CHANGED
|
@@ -669,6 +669,11 @@ interface AitDevtoolsState {
|
|
|
669
669
|
ads: {
|
|
670
670
|
isLoaded: boolean;
|
|
671
671
|
nextEvent: 'loaded' | 'clicked' | 'dismissed' | 'failedToShow' | 'impression' | 'userEarnedReward';
|
|
672
|
+
forceNoFill: boolean;
|
|
673
|
+
lastEvent: {
|
|
674
|
+
type: string;
|
|
675
|
+
timestamp: number;
|
|
676
|
+
} | null;
|
|
672
677
|
};
|
|
673
678
|
game: {
|
|
674
679
|
profile: {
|
|
@@ -689,12 +694,29 @@ interface AitDevtoolsState {
|
|
|
689
694
|
declare class AitStateManager {
|
|
690
695
|
private _state;
|
|
691
696
|
private _listeners;
|
|
697
|
+
private _inTransaction;
|
|
692
698
|
constructor();
|
|
693
699
|
get state(): AitDevtoolsState;
|
|
694
700
|
update(partial: Partial<AitDevtoolsState>): void;
|
|
695
701
|
/** 중첩 객체 업데이트용 */
|
|
696
702
|
patch<K extends keyof AitDevtoolsState>(key: K, partial: Partial<AitDevtoolsState[K]>): void;
|
|
697
703
|
subscribe(listener: Listener): () => void;
|
|
704
|
+
/**
|
|
705
|
+
* 한 묶음의 update/patch 호출을 묶어 listener notify 1회로 만든다.
|
|
706
|
+
* preset 적용처럼 여러 슬라이스를 동시에 바꿀 때 panel re-render 폭주를
|
|
707
|
+
* 방지한다. 중첩 호출은 outermost transaction이 끝날 때 한 번만 notify
|
|
708
|
+
* (inner도 throw해도 outer finally가 flag를 복구한다).
|
|
709
|
+
*
|
|
710
|
+
* Rollback은 없다 — `fn`이 throw해도 그때까지의 state 변경은 유지된다.
|
|
711
|
+
* 구독자가 partial state를 영원히 못 보는 사고를 막기 위해, throw 여부와
|
|
712
|
+
* 무관하게 항상 한 번 notify한 뒤 throw를 propagate한다. DB transaction이
|
|
713
|
+
* 아니라 "여러 mutation을 한 notify로 묶는 batch"라고 생각하면 된다.
|
|
714
|
+
*
|
|
715
|
+
* Listener는 throw해선 안 된다 — finally 안의 `_notify()`가 throw하면 원래
|
|
716
|
+
* `fn`의 throw를 덮어버린다. 우리 구독자는 panel re-render뿐이라 실제
|
|
717
|
+
* 발생 사례는 없지만, 외부에서 listener를 등록할 때 주의.
|
|
718
|
+
*/
|
|
719
|
+
transaction(fn: () => void): void;
|
|
698
720
|
/** 분석 로그 추가 */
|
|
699
721
|
logAnalytics(entry: Omit<AnalyticsLogEntry, 'timestamp'>): void;
|
|
700
722
|
/** 이벤트 트리거 (backEvent, homeEvent 등) */
|
|
@@ -704,5 +726,68 @@ declare class AitStateManager {
|
|
|
704
726
|
}
|
|
705
727
|
declare const aitState: AitStateManager;
|
|
706
728
|
//#endregion
|
|
707
|
-
|
|
729
|
+
//#region src/mock/presets.d.ts
|
|
730
|
+
/**
|
|
731
|
+
* Preset이 덮어쓸 수 있는 mock state slice. 모든 키는 optional —
|
|
732
|
+
* 한 preset이 모든 분야를 정의할 필요 없다.
|
|
733
|
+
*
|
|
734
|
+
* 일부러 좁게 잡았다: viewport / brand / mockData / analyticsLog 등 QA 시나리오와
|
|
735
|
+
* 직접 관련 없는 영역은 preset 대상에서 제외한다 (preset 적용으로 unrelated state가
|
|
736
|
+
* 흔들리는 사고 방지). 필요해지면 추가.
|
|
737
|
+
*
|
|
738
|
+
* `iap` slice는 일부러 `nextResult`만 노출한다 — products / pendingOrders /
|
|
739
|
+
* completedOrders는 array/object 비교가 까다롭고 QA 시나리오에서 강제할 일이 거의
|
|
740
|
+
* 없다. `captureCurrentState` / `matchesPreset` 의 iap 처리 범위와 동기화.
|
|
741
|
+
*/
|
|
742
|
+
interface MockPresetState {
|
|
743
|
+
networkStatus?: AitDevtoolsState['networkStatus'];
|
|
744
|
+
permissions?: Partial<AitDevtoolsState['permissions']>;
|
|
745
|
+
auth?: Partial<AitDevtoolsState['auth']>;
|
|
746
|
+
iap?: {
|
|
747
|
+
nextResult?: AitDevtoolsState['iap']['nextResult'];
|
|
748
|
+
};
|
|
749
|
+
ads?: Partial<AitDevtoolsState['ads']>;
|
|
750
|
+
payment?: Partial<AitDevtoolsState['payment']>;
|
|
751
|
+
}
|
|
752
|
+
interface MockPreset {
|
|
753
|
+
id: string;
|
|
754
|
+
label: string;
|
|
755
|
+
description?: string;
|
|
756
|
+
state: MockPresetState;
|
|
757
|
+
}
|
|
758
|
+
declare const builtInPresets: readonly MockPreset[];
|
|
759
|
+
/**
|
|
760
|
+
* Preset state를 현재 `aitState`에 적용한다. 정의된 키만 덮어쓰고, 알지 못하는 키는
|
|
761
|
+
* 조용히 drop한다 (한 번 warn). 여러 슬라이스를 적용해도 listener notify는 한 번이다
|
|
762
|
+
* (`aitState.transaction` 사용 — panel re-render 폭주 방지).
|
|
763
|
+
*/
|
|
764
|
+
declare function applyPreset(state: MockPresetState): void;
|
|
765
|
+
/**
|
|
766
|
+
* Preset의 모든 정의된 슬라이스가 현재 state와 일치하는지 검사. UI에서 dirty
|
|
767
|
+
* indicator를 그릴 때 쓴다.
|
|
768
|
+
*
|
|
769
|
+
* 일치한다 = preset이 정의한 키 전부가 그대로다. preset이 정의하지 않은 키는
|
|
770
|
+
* 비교 대상이 아니다 — preset은 partial이므로 다른 토글이 바뀌어도 dirty가 아니다.
|
|
771
|
+
*/
|
|
772
|
+
declare function matchesPreset(snapshot: AitDevtoolsState, preset: MockPresetState): boolean;
|
|
773
|
+
/**
|
|
774
|
+
* 현재 state에서 preset에 저장할 만한 슬라이스를 추출. "save current as preset"에서 쓴다.
|
|
775
|
+
*/
|
|
776
|
+
declare function captureCurrentState(snapshot: AitDevtoolsState): MockPresetState;
|
|
777
|
+
//#endregion
|
|
778
|
+
//#region src/mock/preset-store.d.ts
|
|
779
|
+
declare function listUserPresets(): MockPreset[];
|
|
780
|
+
/**
|
|
781
|
+
* Preset을 저장한다. label에서 slug를 derive — 같은 slug가 이미 있으면 `-2`, `-3`
|
|
782
|
+
* suffix를 붙여 새 entry를 만든다 (기존 entry 덮어쓰기 아님). UI는 label만 받으면 된다.
|
|
783
|
+
*
|
|
784
|
+
* Throws:
|
|
785
|
+
* - label trim한 뒤 빈 문자열일 때
|
|
786
|
+
* - localStorage 미가용 환경일 때 (SSR 등)
|
|
787
|
+
* - `setItem` 실패 (`QuotaExceededError` 등) — caller가 처리해야 함
|
|
788
|
+
*/
|
|
789
|
+
declare function saveUserPreset(label: string, state: MockPresetState, description?: string): MockPreset;
|
|
790
|
+
declare function deleteUserPreset(id: string): void;
|
|
791
|
+
//#endregion
|
|
792
|
+
export { Accuracy, type AitDevtoolsState, Analytics, type AnalyticsLogEntry, type DeviceApiMode, type DeviceModes, GoogleAdMob, type HapticFeedbackType, IAP, type IapNextResult, type LocationCoords, type MockContact, type MockData, type MockIapProduct, type MockLocation, type MockPreset, type MockPresetState, type NetworkStatus, type OperationalEnvironment, type PermissionName, type PermissionStatus, type PlatformOS, type Primitive, type SafeAreaInsets, type SafeAreaInsets$1 as SafeAreaInsetsType, Storage, TossAds, aitState, appLogin, applyPreset, appsInTossEvent, appsInTossSignTossCert, builtInPresets, captureCurrentState, checkoutPayment, closeView, contactsViral, deleteUserPreset, 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, listUserPresets, loadFullScreenAd, matchesPreset, onVisibilityChangedByTransparentServiceWeb, openCamera, openGameCenterLeaderboard, openPermissionDialog, openURL, partner, requestPermission, requestReview, saveBase64Data, saveUserPreset, setClipboardText, setDeviceOrientation, setIosSwipeGestureEnabled, setScreenAwakeMode, setSecureScreen, share, showFullScreenAd, startUpdateLocation, submitGameCenterLeaderBoardScore, tdsEvent };
|
|
708
793
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/mock/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/mock/ads/index.ts","../../src/mock/analytics/index.ts","../../src/mock/auth/index.ts","../../src/mock/device/_helpers.ts","../../src/mock/types.ts","../../src/mock/device/camera.ts","../../src/mock/device/clipboard.ts","../../src/mock/device/contacts.ts","../../src/mock/device/haptic.ts","../../src/mock/device/location.ts","../../src/mock/device/storage.ts","../../src/mock/game/index.ts","../../src/mock/iap/index.ts","../../src/mock/navigation/index.ts","../../src/mock/partner/index.ts","../../src/mock/permissions.ts","../../src/mock/state.ts"],"mappings":";;AAgBA;;cAAa,WAAA;;IAGP,OAAA,GAAU,IAAA;MAAQ,IAAA;MAAc,IAAA;IAAA;IAChC,OAAA,GAAU,KAAA,EAAO,KAAA;IACjB,OAAA;MAAY,SAAA;IAAA;EAAA;;;;
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/mock/ads/index.ts","../../src/mock/analytics/index.ts","../../src/mock/auth/index.ts","../../src/mock/device/_helpers.ts","../../src/mock/types.ts","../../src/mock/device/camera.ts","../../src/mock/device/clipboard.ts","../../src/mock/device/contacts.ts","../../src/mock/device/haptic.ts","../../src/mock/device/location.ts","../../src/mock/device/storage.ts","../../src/mock/game/index.ts","../../src/mock/iap/index.ts","../../src/mock/navigation/index.ts","../../src/mock/partner/index.ts","../../src/mock/permissions.ts","../../src/mock/state.ts","../../src/mock/presets.ts","../../src/mock/preset-store.ts"],"mappings":"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"}
|
package/dist/mock/index.js
CHANGED
|
@@ -90,7 +90,9 @@ const DEFAULT_STATE = {
|
|
|
90
90
|
},
|
|
91
91
|
ads: {
|
|
92
92
|
isLoaded: false,
|
|
93
|
-
nextEvent: "loaded"
|
|
93
|
+
nextEvent: "loaded",
|
|
94
|
+
forceNoFill: false,
|
|
95
|
+
lastEvent: null
|
|
94
96
|
},
|
|
95
97
|
game: {
|
|
96
98
|
profile: {
|
|
@@ -134,6 +136,7 @@ function generateDeviceId() {
|
|
|
134
136
|
var AitStateManager = class {
|
|
135
137
|
_state;
|
|
136
138
|
_listeners = /* @__PURE__ */ new Set();
|
|
139
|
+
_inTransaction = false;
|
|
137
140
|
constructor() {
|
|
138
141
|
this._state = structuredClone(DEFAULT_STATE);
|
|
139
142
|
try {
|
|
@@ -172,6 +175,34 @@ var AitStateManager = class {
|
|
|
172
175
|
this._listeners.add(listener);
|
|
173
176
|
return () => this._listeners.delete(listener);
|
|
174
177
|
}
|
|
178
|
+
/**
|
|
179
|
+
* 한 묶음의 update/patch 호출을 묶어 listener notify 1회로 만든다.
|
|
180
|
+
* preset 적용처럼 여러 슬라이스를 동시에 바꿀 때 panel re-render 폭주를
|
|
181
|
+
* 방지한다. 중첩 호출은 outermost transaction이 끝날 때 한 번만 notify
|
|
182
|
+
* (inner도 throw해도 outer finally가 flag를 복구한다).
|
|
183
|
+
*
|
|
184
|
+
* Rollback은 없다 — `fn`이 throw해도 그때까지의 state 변경은 유지된다.
|
|
185
|
+
* 구독자가 partial state를 영원히 못 보는 사고를 막기 위해, throw 여부와
|
|
186
|
+
* 무관하게 항상 한 번 notify한 뒤 throw를 propagate한다. DB transaction이
|
|
187
|
+
* 아니라 "여러 mutation을 한 notify로 묶는 batch"라고 생각하면 된다.
|
|
188
|
+
*
|
|
189
|
+
* Listener는 throw해선 안 된다 — finally 안의 `_notify()`가 throw하면 원래
|
|
190
|
+
* `fn`의 throw를 덮어버린다. 우리 구독자는 panel re-render뿐이라 실제
|
|
191
|
+
* 발생 사례는 없지만, 외부에서 listener를 등록할 때 주의.
|
|
192
|
+
*/
|
|
193
|
+
transaction(fn) {
|
|
194
|
+
if (this._inTransaction) {
|
|
195
|
+
fn();
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
this._inTransaction = true;
|
|
199
|
+
try {
|
|
200
|
+
fn();
|
|
201
|
+
} finally {
|
|
202
|
+
this._inTransaction = false;
|
|
203
|
+
this._notify();
|
|
204
|
+
}
|
|
205
|
+
}
|
|
175
206
|
/** 분석 로그 추가 */
|
|
176
207
|
logAnalytics(entry) {
|
|
177
208
|
this._state = {
|
|
@@ -196,6 +227,7 @@ var AitStateManager = class {
|
|
|
196
227
|
this._notify();
|
|
197
228
|
}
|
|
198
229
|
_notify() {
|
|
230
|
+
if (this._inTransaction) return;
|
|
199
231
|
for (const listener of this._listeners) listener();
|
|
200
232
|
}
|
|
201
233
|
};
|
|
@@ -213,6 +245,10 @@ function withIsSupported(fn) {
|
|
|
213
245
|
const GoogleAdMob = createMockProxy("GoogleAdMob", {
|
|
214
246
|
loadAppsInTossAdMob: withIsSupported((args) => {
|
|
215
247
|
setTimeout(() => {
|
|
248
|
+
if (aitState.state.ads.forceNoFill) {
|
|
249
|
+
args.onError(/* @__PURE__ */ new Error("No fill"));
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
216
252
|
aitState.patch("ads", { isLoaded: true });
|
|
217
253
|
args.onEvent({
|
|
218
254
|
type: "loaded",
|
|
@@ -274,6 +310,10 @@ const TossAds = createMockProxy("TossAds", {
|
|
|
274
310
|
});
|
|
275
311
|
const loadFullScreenAd = withIsSupported((args) => {
|
|
276
312
|
setTimeout(() => {
|
|
313
|
+
if (aitState.state.ads.forceNoFill) {
|
|
314
|
+
args.onError(/* @__PURE__ */ new Error("No fill"));
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
277
317
|
aitState.patch("ads", { isLoaded: true });
|
|
278
318
|
args.onEvent({
|
|
279
319
|
type: "loaded",
|
|
@@ -1099,6 +1139,281 @@ const partner = {
|
|
|
1099
1139
|
}
|
|
1100
1140
|
};
|
|
1101
1141
|
//#endregion
|
|
1102
|
-
|
|
1142
|
+
//#region src/mock/preset-store.ts
|
|
1143
|
+
const PREFIX = "__ait_preset:";
|
|
1144
|
+
function safeLocalStorage() {
|
|
1145
|
+
try {
|
|
1146
|
+
if (typeof localStorage === "undefined") return null;
|
|
1147
|
+
return localStorage;
|
|
1148
|
+
} catch {
|
|
1149
|
+
return null;
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
function isObject(v) {
|
|
1153
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
1154
|
+
}
|
|
1155
|
+
/**
|
|
1156
|
+
* Storage에서 읽은 임의 JSON을 MockPreset으로 검증. id/label 필수, state는
|
|
1157
|
+
* object여야 함. 실패하면 null — caller가 storage entry를 무시하거나 정리하면 된다.
|
|
1158
|
+
*
|
|
1159
|
+
* `state`의 내부 키/값은 검증하지 않는다. `applyPreset`이 `pickKnownKeys`로
|
|
1160
|
+
* 키만 거른 뒤 그대로 state에 패치하므로 잘못된 enum 값이 통과될 수 있지만,
|
|
1161
|
+
* mock state라 보안 위협은 없다 — 새 enum 값이 추가됐을 때 저장된 preset을
|
|
1162
|
+
* reject하지 않으려는 의도.
|
|
1163
|
+
*/
|
|
1164
|
+
function parsePreset(raw) {
|
|
1165
|
+
try {
|
|
1166
|
+
const parsed = JSON.parse(raw);
|
|
1167
|
+
if (!isObject(parsed)) return null;
|
|
1168
|
+
const { id, label, description, state } = parsed;
|
|
1169
|
+
if (typeof id !== "string" || id.length === 0) return null;
|
|
1170
|
+
if (typeof label !== "string" || label.length === 0) return null;
|
|
1171
|
+
if (!isObject(state)) return null;
|
|
1172
|
+
return {
|
|
1173
|
+
id,
|
|
1174
|
+
label,
|
|
1175
|
+
description: typeof description === "string" ? description : void 0,
|
|
1176
|
+
state
|
|
1177
|
+
};
|
|
1178
|
+
} catch {
|
|
1179
|
+
return null;
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
1182
|
+
function listUserPresets() {
|
|
1183
|
+
const ls = safeLocalStorage();
|
|
1184
|
+
if (!ls) return [];
|
|
1185
|
+
const out = [];
|
|
1186
|
+
for (let i = 0; i < ls.length; i++) {
|
|
1187
|
+
const key = ls.key(i);
|
|
1188
|
+
if (!key?.startsWith(PREFIX)) continue;
|
|
1189
|
+
const raw = ls.getItem(key);
|
|
1190
|
+
if (!raw) continue;
|
|
1191
|
+
const preset = parsePreset(raw);
|
|
1192
|
+
if (preset) out.push(preset);
|
|
1193
|
+
}
|
|
1194
|
+
return out.sort((a, b) => a.label.localeCompare(b.label));
|
|
1195
|
+
}
|
|
1196
|
+
/**
|
|
1197
|
+
* Preset을 저장한다. label에서 slug를 derive — 같은 slug가 이미 있으면 `-2`, `-3`
|
|
1198
|
+
* suffix를 붙여 새 entry를 만든다 (기존 entry 덮어쓰기 아님). UI는 label만 받으면 된다.
|
|
1199
|
+
*
|
|
1200
|
+
* Throws:
|
|
1201
|
+
* - label trim한 뒤 빈 문자열일 때
|
|
1202
|
+
* - localStorage 미가용 환경일 때 (SSR 등)
|
|
1203
|
+
* - `setItem` 실패 (`QuotaExceededError` 등) — caller가 처리해야 함
|
|
1204
|
+
*/
|
|
1205
|
+
function saveUserPreset(label, state, description) {
|
|
1206
|
+
const trimmed = label.trim();
|
|
1207
|
+
if (trimmed.length === 0) throw new Error("Preset label cannot be empty");
|
|
1208
|
+
const ls = safeLocalStorage();
|
|
1209
|
+
if (!ls) throw new Error("localStorage not available");
|
|
1210
|
+
const id = generateId(trimmed, ls);
|
|
1211
|
+
const preset = {
|
|
1212
|
+
id,
|
|
1213
|
+
label: trimmed,
|
|
1214
|
+
state,
|
|
1215
|
+
...description !== void 0 && description.length > 0 ? { description } : {}
|
|
1216
|
+
};
|
|
1217
|
+
ls.setItem(PREFIX + id, JSON.stringify(preset));
|
|
1218
|
+
return preset;
|
|
1219
|
+
}
|
|
1220
|
+
function deleteUserPreset(id) {
|
|
1221
|
+
const ls = safeLocalStorage();
|
|
1222
|
+
if (!ls) return;
|
|
1223
|
+
ls.removeItem(PREFIX + id);
|
|
1224
|
+
}
|
|
1225
|
+
/** 충돌 시 `-2`, `-3` 등 suffix를 붙여 unique한 id 만든다. */
|
|
1226
|
+
function generateId(label, ls) {
|
|
1227
|
+
const base = label.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40) || "preset";
|
|
1228
|
+
let candidate = base;
|
|
1229
|
+
let n = 2;
|
|
1230
|
+
while (ls.getItem(PREFIX + candidate) !== null) {
|
|
1231
|
+
candidate = `${base}-${n}`;
|
|
1232
|
+
n += 1;
|
|
1233
|
+
}
|
|
1234
|
+
return candidate;
|
|
1235
|
+
}
|
|
1236
|
+
//#endregion
|
|
1237
|
+
//#region src/mock/presets.ts
|
|
1238
|
+
const builtInPresets = [
|
|
1239
|
+
{
|
|
1240
|
+
id: "all-allowed",
|
|
1241
|
+
label: "All allowed (default-ish)",
|
|
1242
|
+
description: "모든 권한 허용, WIFI, 로그인됨, IAP success",
|
|
1243
|
+
state: {
|
|
1244
|
+
networkStatus: "WIFI",
|
|
1245
|
+
permissions: {
|
|
1246
|
+
camera: "allowed",
|
|
1247
|
+
photos: "allowed",
|
|
1248
|
+
geolocation: "allowed",
|
|
1249
|
+
clipboard: "allowed",
|
|
1250
|
+
contacts: "allowed",
|
|
1251
|
+
microphone: "allowed"
|
|
1252
|
+
},
|
|
1253
|
+
auth: { isLoggedIn: true },
|
|
1254
|
+
iap: { nextResult: "success" },
|
|
1255
|
+
ads: { forceNoFill: false },
|
|
1256
|
+
payment: {
|
|
1257
|
+
nextResult: "success",
|
|
1258
|
+
failReason: ""
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1261
|
+
},
|
|
1262
|
+
{
|
|
1263
|
+
id: "permission-denied",
|
|
1264
|
+
label: "Permissions denied",
|
|
1265
|
+
description: "camera / photos / geolocation / contacts 거부",
|
|
1266
|
+
state: { permissions: {
|
|
1267
|
+
camera: "denied",
|
|
1268
|
+
photos: "denied",
|
|
1269
|
+
geolocation: "denied",
|
|
1270
|
+
contacts: "denied"
|
|
1271
|
+
} }
|
|
1272
|
+
},
|
|
1273
|
+
{
|
|
1274
|
+
id: "offline",
|
|
1275
|
+
label: "Offline",
|
|
1276
|
+
description: "getNetworkStatus → OFFLINE, IAP NETWORK_ERROR",
|
|
1277
|
+
state: {
|
|
1278
|
+
networkStatus: "OFFLINE",
|
|
1279
|
+
iap: { nextResult: "NETWORK_ERROR" },
|
|
1280
|
+
payment: {
|
|
1281
|
+
nextResult: "fail",
|
|
1282
|
+
failReason: "NETWORK_ERROR"
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1285
|
+
},
|
|
1286
|
+
{
|
|
1287
|
+
id: "logged-out",
|
|
1288
|
+
label: "Logged out",
|
|
1289
|
+
description: "auth.isLoggedIn=false. login flow 검증용",
|
|
1290
|
+
state: { auth: { isLoggedIn: false } }
|
|
1291
|
+
},
|
|
1292
|
+
{
|
|
1293
|
+
id: "iap-pending",
|
|
1294
|
+
label: "IAP payment pending",
|
|
1295
|
+
description: "결제 진행 중 분기 검증",
|
|
1296
|
+
state: { iap: { nextResult: "PAYMENT_PENDING" } }
|
|
1297
|
+
},
|
|
1298
|
+
{
|
|
1299
|
+
id: "ads-no-fill",
|
|
1300
|
+
label: "Ads — no fill",
|
|
1301
|
+
description: "광고 fill 실패 분기 검증",
|
|
1302
|
+
state: {
|
|
1303
|
+
networkStatus: "WIFI",
|
|
1304
|
+
ads: { forceNoFill: true }
|
|
1305
|
+
}
|
|
1306
|
+
}
|
|
1307
|
+
];
|
|
1308
|
+
/**
|
|
1309
|
+
* Preset의 nested slice를 검증된 키만 골라서 풀어낸다. Forward-compat 차원에서
|
|
1310
|
+
* 알지 못하는 키는 drop, drop된 키 전부를 모아 한 번에 warn한다.
|
|
1311
|
+
*
|
|
1312
|
+
* Value 단위 검증은 하지 않는다 — `permissions.camera`에 enum 외 값이 들어와도
|
|
1313
|
+
* 그대로 통과한다. mock state라 잘못된 값은 mock 함수 분기 결과만 흔든다.
|
|
1314
|
+
* 새 enum 값이 추가됐을 때 저장된 preset을 reject하지 않으려는 의도.
|
|
1315
|
+
*/
|
|
1316
|
+
function pickKnownKeys(input, allowed) {
|
|
1317
|
+
if (typeof input !== "object" || input === null) return {};
|
|
1318
|
+
const out = {};
|
|
1319
|
+
const dropped = [];
|
|
1320
|
+
for (const [key, value] of Object.entries(input)) if (allowed.includes(key)) out[key] = value;
|
|
1321
|
+
else dropped.push(key);
|
|
1322
|
+
if (dropped.length > 0) console.warn(`[@ait-co/devtools] Preset dropped unknown keys: ${dropped.join(", ")}`);
|
|
1323
|
+
return out;
|
|
1324
|
+
}
|
|
1325
|
+
const PERMISSION_KEYS = [
|
|
1326
|
+
"camera",
|
|
1327
|
+
"photos",
|
|
1328
|
+
"geolocation",
|
|
1329
|
+
"clipboard",
|
|
1330
|
+
"contacts",
|
|
1331
|
+
"microphone"
|
|
1332
|
+
];
|
|
1333
|
+
const AUTH_KEYS = [
|
|
1334
|
+
"isLoggedIn",
|
|
1335
|
+
"isTossLoginIntegrated",
|
|
1336
|
+
"userKeyHash"
|
|
1337
|
+
];
|
|
1338
|
+
const IAP_KEYS = ["nextResult"];
|
|
1339
|
+
const ADS_KEYS = [
|
|
1340
|
+
"isLoaded",
|
|
1341
|
+
"nextEvent",
|
|
1342
|
+
"forceNoFill",
|
|
1343
|
+
"lastEvent"
|
|
1344
|
+
];
|
|
1345
|
+
const PAYMENT_KEYS = ["nextResult", "failReason"];
|
|
1346
|
+
/**
|
|
1347
|
+
* Preset state를 현재 `aitState`에 적용한다. 정의된 키만 덮어쓰고, 알지 못하는 키는
|
|
1348
|
+
* 조용히 drop한다 (한 번 warn). 여러 슬라이스를 적용해도 listener notify는 한 번이다
|
|
1349
|
+
* (`aitState.transaction` 사용 — panel re-render 폭주 방지).
|
|
1350
|
+
*/
|
|
1351
|
+
function applyPreset(state) {
|
|
1352
|
+
aitState.transaction(() => {
|
|
1353
|
+
if (state.networkStatus !== void 0) aitState.update({ networkStatus: state.networkStatus });
|
|
1354
|
+
if (state.permissions !== void 0) aitState.patch("permissions", pickKnownKeys(state.permissions, PERMISSION_KEYS));
|
|
1355
|
+
if (state.auth !== void 0) aitState.patch("auth", pickKnownKeys(state.auth, AUTH_KEYS));
|
|
1356
|
+
if (state.iap !== void 0) {
|
|
1357
|
+
const picked = pickKnownKeys(state.iap, IAP_KEYS);
|
|
1358
|
+
aitState.patch("iap", picked);
|
|
1359
|
+
}
|
|
1360
|
+
if (state.ads !== void 0) aitState.patch("ads", pickKnownKeys(state.ads, ADS_KEYS));
|
|
1361
|
+
if (state.payment !== void 0) aitState.patch("payment", pickKnownKeys(state.payment, PAYMENT_KEYS));
|
|
1362
|
+
});
|
|
1363
|
+
}
|
|
1364
|
+
/**
|
|
1365
|
+
* Preset의 모든 정의된 슬라이스가 현재 state와 일치하는지 검사. UI에서 dirty
|
|
1366
|
+
* indicator를 그릴 때 쓴다.
|
|
1367
|
+
*
|
|
1368
|
+
* 일치한다 = preset이 정의한 키 전부가 그대로다. preset이 정의하지 않은 키는
|
|
1369
|
+
* 비교 대상이 아니다 — preset은 partial이므로 다른 토글이 바뀌어도 dirty가 아니다.
|
|
1370
|
+
*/
|
|
1371
|
+
function matchesPreset(snapshot, preset) {
|
|
1372
|
+
if (preset.networkStatus !== void 0 && snapshot.networkStatus !== preset.networkStatus) return false;
|
|
1373
|
+
if (preset.permissions !== void 0) for (const k of PERMISSION_KEYS) {
|
|
1374
|
+
const want = preset.permissions[k];
|
|
1375
|
+
if (want !== void 0 && snapshot.permissions[k] !== want) return false;
|
|
1376
|
+
}
|
|
1377
|
+
if (preset.auth !== void 0) for (const k of AUTH_KEYS) {
|
|
1378
|
+
const want = preset.auth[k];
|
|
1379
|
+
if (want !== void 0 && snapshot.auth[k] !== want) return false;
|
|
1380
|
+
}
|
|
1381
|
+
if (preset.iap !== void 0) {
|
|
1382
|
+
if (preset.iap.nextResult !== void 0 && snapshot.iap.nextResult !== preset.iap.nextResult) return false;
|
|
1383
|
+
}
|
|
1384
|
+
if (preset.ads !== void 0) {
|
|
1385
|
+
if (preset.ads.forceNoFill !== void 0 && snapshot.ads.forceNoFill !== preset.ads.forceNoFill) return false;
|
|
1386
|
+
if (preset.ads.isLoaded !== void 0 && snapshot.ads.isLoaded !== preset.ads.isLoaded) return false;
|
|
1387
|
+
if (preset.ads.nextEvent !== void 0 && snapshot.ads.nextEvent !== preset.ads.nextEvent) return false;
|
|
1388
|
+
}
|
|
1389
|
+
if (preset.payment !== void 0) for (const k of PAYMENT_KEYS) {
|
|
1390
|
+
const want = preset.payment[k];
|
|
1391
|
+
if (want !== void 0 && snapshot.payment[k] !== want) return false;
|
|
1392
|
+
}
|
|
1393
|
+
return true;
|
|
1394
|
+
}
|
|
1395
|
+
/**
|
|
1396
|
+
* 현재 state에서 preset에 저장할 만한 슬라이스를 추출. "save current as preset"에서 쓴다.
|
|
1397
|
+
*/
|
|
1398
|
+
function captureCurrentState(snapshot) {
|
|
1399
|
+
return {
|
|
1400
|
+
networkStatus: snapshot.networkStatus,
|
|
1401
|
+
permissions: { ...snapshot.permissions },
|
|
1402
|
+
auth: {
|
|
1403
|
+
isLoggedIn: snapshot.auth.isLoggedIn,
|
|
1404
|
+
isTossLoginIntegrated: snapshot.auth.isTossLoginIntegrated,
|
|
1405
|
+
userKeyHash: snapshot.auth.userKeyHash
|
|
1406
|
+
},
|
|
1407
|
+
iap: { nextResult: snapshot.iap.nextResult },
|
|
1408
|
+
ads: {
|
|
1409
|
+
forceNoFill: snapshot.ads.forceNoFill,
|
|
1410
|
+
isLoaded: snapshot.ads.isLoaded,
|
|
1411
|
+
nextEvent: snapshot.ads.nextEvent
|
|
1412
|
+
},
|
|
1413
|
+
payment: { ...snapshot.payment }
|
|
1414
|
+
};
|
|
1415
|
+
}
|
|
1416
|
+
//#endregion
|
|
1417
|
+
export { Accuracy, Analytics, GoogleAdMob, IAP, SafeAreaInsets, Storage, TossAds, aitState, appLogin, applyPreset, appsInTossEvent, appsInTossSignTossCert, builtInPresets, captureCurrentState, checkoutPayment, closeView, contactsViral, deleteUserPreset, 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, listUserPresets, loadFullScreenAd, matchesPreset, onVisibilityChangedByTransparentServiceWeb, openCamera, openGameCenterLeaderboard, openPermissionDialog, openURL, partner, requestPermission, requestReview, saveBase64Data, saveUserPreset, setClipboardText, setDeviceOrientation, setIosSwipeGestureEnabled, setScreenAwakeMode, setSecureScreen, share, showFullScreenAd, startUpdateLocation, submitGameCenterLeaderBoardScore, tdsEvent };
|
|
1103
1418
|
|
|
1104
1419
|
//# sourceMappingURL=index.js.map
|