@rich-automation/lotto 2.0.5 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/README.md +1 -0
  2. package/lib/cjs/controllers/factory.js +7 -0
  3. package/lib/cjs/controllers/playwright/playwright.page.js +3 -2
  4. package/lib/cjs/controllers/puppeteer/puppeteer.page.js +3 -2
  5. package/lib/cjs/controllers/webview/bridgeScript.js +125 -0
  6. package/lib/cjs/controllers/webview/index.js +35 -0
  7. package/lib/cjs/controllers/webview/types.js +2 -0
  8. package/lib/cjs/controllers/webview/webview.page.js +106 -0
  9. package/lib/cjs/index.js +1 -1
  10. package/lib/cjs/{lottoService.js → services/lottoService.core.js} +20 -22
  11. package/lib/cjs/services/lottoService.js +17 -0
  12. package/lib/esm/controllers/factory.js +7 -0
  13. package/lib/esm/controllers/playwright/playwright.page.js +3 -2
  14. package/lib/esm/controllers/puppeteer/puppeteer.page.js +3 -2
  15. package/lib/esm/controllers/webview/bridgeScript.js +122 -0
  16. package/lib/esm/controllers/webview/index.js +31 -0
  17. package/lib/esm/controllers/webview/types.js +1 -0
  18. package/lib/esm/controllers/webview/webview.page.js +102 -0
  19. package/lib/esm/index.js +1 -1
  20. package/lib/esm/index.native.js +163 -0
  21. package/lib/esm/{lottoService.js → services/lottoService.core.js} +18 -20
  22. package/lib/esm/services/lottoService.js +10 -0
  23. package/lib/tsconfig.native.tsbuildinfo +1 -0
  24. package/lib/typescript/controllers/playwright/playwright.page.d.ts +3 -1
  25. package/lib/typescript/controllers/puppeteer/puppeteer.page.d.ts +3 -1
  26. package/lib/typescript/controllers/webview/bridgeScript.d.ts +1 -0
  27. package/lib/typescript/controllers/webview/index.d.ts +12 -0
  28. package/lib/typescript/controllers/webview/types.d.ts +11 -0
  29. package/lib/typescript/controllers/webview/webview.page.d.ts +22 -0
  30. package/lib/typescript/index.d.ts +1 -1
  31. package/lib/typescript/index.native.d.ts +19 -0
  32. package/lib/typescript/{lottoService.d.ts → services/lottoService.core.d.ts} +4 -4
  33. package/lib/typescript/services/lottoService.d.ts +5 -0
  34. package/lib/typescript/types.d.ts +5 -2
  35. package/package.json +32 -6
package/README.md CHANGED
@@ -20,6 +20,7 @@ yarn add @rich-automation/lotto
20
20
 
21
21
  1. 내부적으로 Playwright 또는 Puppeteer를 사용하므로, Chromium이 사전에 설치되어 있어야 합니다. ([링크](https://github.com/rich-automation/lotto-module/blob/main/package.json#L38-L39))
22
22
  2. 구매는 [동행복권](https://dhlottery.co.kr/common.do?method=main) 사이트에서 진행되며, 예치금 충전 기능은 없으므로 미리 계정에 예치금을 충전해두어야 합니다.
23
+ 3. 건전 구매 서약서에 동의하세요 (https://www.dhlottery.co.kr/hpns/sdnsCamPainView), 1년 주기로 동의가 필요합니다.
23
24
 
24
25
  ## 사용법
25
26
 
@@ -3,11 +3,15 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createBrowserController = void 0;
4
4
  const puppeteer_1 = require("./puppeteer");
5
5
  const playwright_1 = require("./playwright");
6
+ const webview_1 = require("./webview");
6
7
  const api_1 = require("./api");
7
8
  function createBrowserController(configs, logger) {
8
9
  if (isAPIMode(configs)) {
9
10
  return new api_1.APIModeController(configs, logger);
10
11
  }
12
+ if (isWebView(configs)) {
13
+ return new webview_1.WebViewController(configs, logger);
14
+ }
11
15
  if (isPlaywright(configs)) {
12
16
  return new playwright_1.PlaywrightController(configs, logger);
13
17
  }
@@ -20,6 +24,9 @@ exports.createBrowserController = createBrowserController;
20
24
  function isAPIMode(configs) {
21
25
  return configs.controller === 'api';
22
26
  }
27
+ function isWebView(configs) {
28
+ return (typeof configs.controller === 'object' && '__type' in configs.controller && configs.controller.__type === 'webview');
29
+ }
23
30
  function isPlaywright(configs) {
24
31
  return typeof configs.controller !== 'string' && 'connectOverCDP' in configs.controller;
25
32
  }
@@ -23,9 +23,10 @@ class PlaywrightPage {
23
23
  return this.page.url();
24
24
  });
25
25
  }
26
- goto(url) {
26
+ goto(url, options) {
27
27
  return __awaiter(this, void 0, void 0, function* () {
28
- yield this.page.goto(url, { waitUntil: 'load' });
28
+ const waitUntil = (options === null || options === void 0 ? void 0 : options.waitUntil) === 'idle' ? 'networkidle' : 'load';
29
+ yield this.page.goto(url, { waitUntil });
29
30
  });
30
31
  }
31
32
  fill(selector, value) {
@@ -22,9 +22,10 @@ class PuppeteerPage {
22
22
  return this.page.url();
23
23
  });
24
24
  }
25
- goto(url) {
25
+ goto(url, options) {
26
26
  return __awaiter(this, void 0, void 0, function* () {
27
- yield this.page.goto(url, { waitUntil: 'load' });
27
+ const waitUntil = (options === null || options === void 0 ? void 0 : options.waitUntil) === 'idle' ? 'networkidle0' : 'load';
28
+ yield this.page.goto(url, { waitUntil });
28
29
  });
29
30
  }
30
31
  fill(selector, value) {
@@ -0,0 +1,125 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BRIDGE_SCRIPT = void 0;
4
+ // WebView에 주입되는 브릿지 스크립트
5
+ // 모든 DOM 조작은 이 스크립트 내부에서 실행되며, postMessage로 결과를 반환함
6
+ exports.BRIDGE_SCRIPT = `
7
+ (function() {
8
+ if (window.__LOTTO_BRIDGE__) return;
9
+
10
+ window.__LOTTO_BRIDGE__ = {
11
+ fill: function(selector, value) {
12
+ var el = document.querySelector(selector);
13
+ if (!el) return { ok: false, error: 'Element not found: ' + selector };
14
+ el.focus();
15
+ el.value = value;
16
+ el.dispatchEvent(new Event('input', { bubbles: true }));
17
+ el.dispatchEvent(new Event('change', { bubbles: true }));
18
+ return { ok: true };
19
+ },
20
+
21
+ click: function(selector, domDirect) {
22
+ var el = document.querySelector(selector);
23
+ if (!el) return { ok: false, error: 'Element not found: ' + selector };
24
+ el.click();
25
+ return { ok: true };
26
+ },
27
+
28
+ select: function(selector, value) {
29
+ var el = document.querySelector(selector);
30
+ if (!el) return { ok: false, error: 'Element not found: ' + selector };
31
+ el.value = value;
32
+ el.dispatchEvent(new Event('change', { bubbles: true }));
33
+ return { ok: true };
34
+ },
35
+
36
+ exists: function(selector, containsText) {
37
+ var el = document.querySelector(selector);
38
+ if (!el) return false;
39
+ if (containsText) return (el.textContent || '').indexOf(containsText) !== -1;
40
+ return true;
41
+ },
42
+
43
+ querySelectorAll: function(selector) {
44
+ var MAX_DEPTH = 5;
45
+ function toFakeDOM(el, depth) {
46
+ return {
47
+ className: el.className || '',
48
+ innerHTML: el.innerHTML || '',
49
+ children: depth < MAX_DEPTH
50
+ ? Array.from(el.children).map(function(c) { return toFakeDOM(c, depth + 1); })
51
+ : []
52
+ };
53
+ }
54
+ return Array.from(document.querySelectorAll(selector)).map(function(el) { return toFakeDOM(el, 0); });
55
+ },
56
+
57
+ getUrl: function() {
58
+ return window.location.href;
59
+ },
60
+
61
+ getCookies: function() {
62
+ return document.cookie;
63
+ },
64
+
65
+ setCookies: function(cookieString) {
66
+ cookieString.split('; ').forEach(function(cookie) {
67
+ document.cookie = cookie;
68
+ });
69
+ return { ok: true };
70
+ },
71
+
72
+ waitForSelector: function(selector, timeout) {
73
+ return new Promise(function(resolve) {
74
+ var el = document.querySelector(selector);
75
+ if (el) { resolve({ ok: true }); return; }
76
+
77
+ var observer = new MutationObserver(function() {
78
+ var found = document.querySelector(selector);
79
+ if (found) {
80
+ observer.disconnect();
81
+ clearTimeout(timer);
82
+ resolve({ ok: true });
83
+ }
84
+ });
85
+ observer.observe(document.body || document.documentElement, { childList: true, subtree: true });
86
+
87
+ var timer = setTimeout(function() {
88
+ observer.disconnect();
89
+ resolve({ ok: false, error: 'Timeout waiting for: ' + selector });
90
+ }, timeout || 10000);
91
+ });
92
+ }
93
+ };
94
+
95
+ var ALLOWED_METHODS = ['fill','click','select','exists','querySelectorAll','getUrl','getCookies','setCookies','waitForSelector'];
96
+
97
+ // 커맨드 수신 리스너
98
+ window.addEventListener('message', function(e) {
99
+ try {
100
+ var msg = typeof e.data === 'string' ? JSON.parse(e.data) : e.data;
101
+ if (!msg || !msg.__lotto_call_id__ || !msg.method) return;
102
+ if (ALLOWED_METHODS.indexOf(msg.method) === -1) return;
103
+
104
+ var fn = window.__LOTTO_BRIDGE__[msg.method];
105
+ if (!fn) return;
106
+
107
+ Promise.resolve(fn.apply(null, msg.args || [])).then(function(result) {
108
+ window.ReactNativeWebView.postMessage(JSON.stringify({
109
+ __lotto_call_id__: msg.__lotto_call_id__,
110
+ result: result
111
+ }));
112
+ });
113
+ } catch (err) {
114
+ if (msg && msg.__lotto_call_id__) {
115
+ window.ReactNativeWebView.postMessage(JSON.stringify({
116
+ __lotto_call_id__: msg.__lotto_call_id__,
117
+ result: { ok: false, error: String(err) }
118
+ }));
119
+ }
120
+ }
121
+ });
122
+
123
+ true;
124
+ })();
125
+ `;
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.WebViewController = void 0;
13
+ const webview_page_1 = require("./webview.page");
14
+ class WebViewController {
15
+ constructor(configs, logger) {
16
+ this.page = null;
17
+ this.focus = () => __awaiter(this, void 0, void 0, function* () {
18
+ if (!this.page) {
19
+ this.page = new webview_page_1.WebViewPage(this.configs.controller, this.logger);
20
+ }
21
+ return this.page;
22
+ });
23
+ this.close = () => __awaiter(this, void 0, void 0, function* () {
24
+ this.configs.controller.destroy();
25
+ this.page = null;
26
+ });
27
+ this.cleanPages = () => __awaiter(this, void 0, void 0, function* () {
28
+ // WebView는 단일 페이지이므로 no-op
29
+ this.logger.debug('[WebViewController]', 'cleanPages is no-op for WebView');
30
+ });
31
+ this.configs = configs;
32
+ this.logger = logger;
33
+ }
34
+ }
35
+ exports.WebViewController = WebViewController;
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,106 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.WebViewPage = void 0;
13
+ const lazyRun_1 = require("../../utils/lazyRun");
14
+ const deferred_1 = require("../../utils/deferred");
15
+ class WebViewPage {
16
+ constructor(bridge, logger) {
17
+ this.bridge = bridge;
18
+ this.logger = logger;
19
+ }
20
+ url() {
21
+ return __awaiter(this, void 0, void 0, function* () {
22
+ return this.bridge.getCurrentUrl();
23
+ });
24
+ }
25
+ goto(url, options) {
26
+ return __awaiter(this, void 0, void 0, function* () {
27
+ yield this.bridge.navigateTo(url, options === null || options === void 0 ? void 0 : options.waitUntil);
28
+ });
29
+ }
30
+ fill(selector, value) {
31
+ var _a;
32
+ return __awaiter(this, void 0, void 0, function* () {
33
+ const result = (yield this.bridge.call('fill', [selector, value.toString()]));
34
+ if (!result.ok) {
35
+ (_a = this.logger) === null || _a === void 0 ? void 0 : _a.warn('[WebViewPage]', 'fill failed', result.error);
36
+ }
37
+ });
38
+ }
39
+ click(selector, _domDirect = false) {
40
+ var _a;
41
+ return __awaiter(this, void 0, void 0, function* () {
42
+ const result = (yield this.bridge.call('click', [selector]));
43
+ if (!result.ok) {
44
+ (_a = this.logger) === null || _a === void 0 ? void 0 : _a.warn('[WebViewPage]', 'click failed', result.error);
45
+ }
46
+ });
47
+ }
48
+ select(selector, value) {
49
+ var _a;
50
+ return __awaiter(this, void 0, void 0, function* () {
51
+ const result = (yield this.bridge.call('select', [selector, value]));
52
+ if (!result.ok) {
53
+ (_a = this.logger) === null || _a === void 0 ? void 0 : _a.warn('[WebViewPage]', 'select failed', result.error);
54
+ }
55
+ });
56
+ }
57
+ querySelectorAll(selector, callback) {
58
+ return __awaiter(this, void 0, void 0, function* () {
59
+ const elems = (yield this.bridge.call('querySelectorAll', [selector]));
60
+ return callback(elems);
61
+ });
62
+ }
63
+ exists(selector, containsText) {
64
+ return __awaiter(this, void 0, void 0, function* () {
65
+ return (yield this.bridge.call('exists', [selector, containsText]));
66
+ });
67
+ }
68
+ wait(param) {
69
+ return __awaiter(this, void 0, void 0, function* () {
70
+ const p = (0, deferred_1.deferred)();
71
+ if (param === 'idle' || param === 'load') {
72
+ // WebView에서는 네비게이션 대기를 직접 지원하지 않으므로 짧은 대기로 대체
73
+ yield (0, lazyRun_1.lazyRun)(() => __awaiter(this, void 0, void 0, function* () { return p.resolve(); }), 1000);
74
+ }
75
+ if (typeof param === 'number') {
76
+ yield (0, lazyRun_1.lazyRun)(() => __awaiter(this, void 0, void 0, function* () { return p.resolve(); }), param);
77
+ }
78
+ return p.promise;
79
+ });
80
+ }
81
+ waitForSelector(selector, timeout = 10000) {
82
+ var _a;
83
+ return __awaiter(this, void 0, void 0, function* () {
84
+ const result = (yield this.bridge.call('waitForSelector', [selector, timeout]));
85
+ if (!result.ok) {
86
+ throw new Error((_a = result.error) !== null && _a !== void 0 ? _a : `waitForSelector timeout: ${selector}`);
87
+ }
88
+ });
89
+ }
90
+ getCookies() {
91
+ return __awaiter(this, void 0, void 0, function* () {
92
+ return (yield this.bridge.call('getCookies', []));
93
+ });
94
+ }
95
+ setCookies(cookies) {
96
+ return __awaiter(this, void 0, void 0, function* () {
97
+ yield this.bridge.call('setCookies', [cookies]);
98
+ });
99
+ }
100
+ on(_event, _callback) {
101
+ var _a;
102
+ (_a = this.logger) === null || _a === void 0 ? void 0 : _a.warn('[WebViewPage]', 'on() is not supported in WebView controller');
103
+ return () => { };
104
+ }
105
+ }
106
+ exports.WebViewPage = WebViewPage;
package/lib/cjs/index.js CHANGED
@@ -15,7 +15,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  exports.checkWinning = exports.getCheckWinningLink = exports.getNextLottoRound = exports.getLastLottoRound = exports.LogLevel = exports.LottoService = void 0;
18
- var lottoService_1 = require("./lottoService");
18
+ var lottoService_1 = require("./services/lottoService");
19
19
  Object.defineProperty(exports, "LottoService", { enumerable: true, get: function () { return lottoService_1.LottoService; } });
20
20
  var logger_1 = require("./logger");
21
21
  Object.defineProperty(exports, "LogLevel", { enumerable: true, get: function () { return logger_1.LogLevel; } });
@@ -12,23 +12,21 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
12
12
  return (mod && mod.__esModule) ? mod : { "default": mod };
13
13
  };
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
- exports.LottoService = void 0;
16
- const lottoError_1 = __importDefault(require("./lottoError"));
17
- const selectors_1 = require("./constants/selectors");
18
- const factory_1 = require("./controllers/factory");
19
- const urls_1 = require("./constants/urls");
20
- const constants_1 = require("./constants");
21
- const lazyRun_1 = require("./utils/lazyRun");
22
- const logger_1 = __importDefault(require("./logger"));
23
- const getLastLottoRound_1 = require("./utils/getLastLottoRound");
24
- const validateLottoNumber_1 = require("./utils/validateLottoNumber");
25
- const getWinningNumbers_1 = require("./apis/dhlottery/getWinningNumbers");
26
- const checkWinning_1 = require("./utils/checkWinning");
27
- const validatePurchaseAvailability_1 = require("./utils/validatePurchaseAvailability");
28
- const getCheckWinningLink_1 = require("./utils/getCheckWinningLink");
29
- const getNextLottoRound_1 = require("./utils/getNextLottoRound");
30
- class LottoService {
31
- constructor(configs) {
15
+ exports.LottoServiceCore = void 0;
16
+ const lottoError_1 = __importDefault(require("../lottoError"));
17
+ const selectors_1 = require("../constants/selectors");
18
+ const urls_1 = require("../constants/urls");
19
+ const constants_1 = require("../constants");
20
+ const lazyRun_1 = require("../utils/lazyRun");
21
+ const getLastLottoRound_1 = require("../utils/getLastLottoRound");
22
+ const validateLottoNumber_1 = require("../utils/validateLottoNumber");
23
+ const getWinningNumbers_1 = require("../apis/dhlottery/getWinningNumbers");
24
+ const checkWinning_1 = require("../utils/checkWinning");
25
+ const validatePurchaseAvailability_1 = require("../utils/validatePurchaseAvailability");
26
+ const getCheckWinningLink_1 = require("../utils/getCheckWinningLink");
27
+ const getNextLottoRound_1 = require("../utils/getNextLottoRound");
28
+ class LottoServiceCore {
29
+ constructor(controller, logger) {
32
30
  this.context = {
33
31
  authenticated: false
34
32
  };
@@ -66,10 +64,10 @@ class LottoService {
66
64
  if (this.browserController.configs.controller === 'api') {
67
65
  throw lottoError_1.default.NotSupported('API mode does not support signIn.');
68
66
  }
69
- // 페이지 이동
67
+ // 페이지 이동 (RSA 키 로딩 등 비동기 스크립트 완료까지 대기)
70
68
  const page = yield this.browserController.focus(0);
71
69
  this.logger.debug('[signIn]', 'goto', 'login page');
72
- yield page.goto(urls_1.URLS.LOGIN);
70
+ yield page.goto(urls_1.URLS.LOGIN, { waitUntil: 'idle' });
73
71
  this.logger.debug('[signIn]', 'page url', yield page.url());
74
72
  // 로그인 시도
75
73
  this.logger.debug('[signIn]', 'try login');
@@ -179,8 +177,8 @@ class LottoService {
179
177
  this.logger.debug('[getCheckWinningLink]', 'getCheckWinningLink');
180
178
  return (0, getCheckWinningLink_1.getCheckWinningLink)(numbers, round);
181
179
  };
182
- this.logger = new logger_1.default(configs.logLevel, '[LottoService]');
183
- this.browserController = (0, factory_1.createBrowserController)(Object.assign({ defaultViewport: { width: 1080, height: 1024 } }, configs), this.logger);
180
+ this.browserController = controller;
181
+ this.logger = logger;
184
182
  }
185
183
  }
186
- exports.LottoService = LottoService;
184
+ exports.LottoServiceCore = LottoServiceCore;
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.LottoService = void 0;
7
+ const factory_1 = require("../controllers/factory");
8
+ const logger_1 = __importDefault(require("../logger"));
9
+ const lottoService_core_1 = require("./lottoService.core");
10
+ class LottoService extends lottoService_core_1.LottoServiceCore {
11
+ constructor(configs) {
12
+ const logger = new logger_1.default(configs.logLevel, '[LottoService]');
13
+ const controller = (0, factory_1.createBrowserController)(Object.assign({ defaultViewport: { width: 1080, height: 1024 } }, configs), logger);
14
+ super(controller, logger);
15
+ }
16
+ }
17
+ exports.LottoService = LottoService;
@@ -1,10 +1,14 @@
1
1
  import { PuppeteerController } from './puppeteer';
2
2
  import { PlaywrightController } from './playwright';
3
+ import { WebViewController } from './webview';
3
4
  import { APIModeController } from './api';
4
5
  export function createBrowserController(configs, logger) {
5
6
  if (isAPIMode(configs)) {
6
7
  return new APIModeController(configs, logger);
7
8
  }
9
+ if (isWebView(configs)) {
10
+ return new WebViewController(configs, logger);
11
+ }
8
12
  if (isPlaywright(configs)) {
9
13
  return new PlaywrightController(configs, logger);
10
14
  }
@@ -16,6 +20,9 @@ export function createBrowserController(configs, logger) {
16
20
  function isAPIMode(configs) {
17
21
  return configs.controller === 'api';
18
22
  }
23
+ function isWebView(configs) {
24
+ return (typeof configs.controller === 'object' && '__type' in configs.controller && configs.controller.__type === 'webview');
25
+ }
19
26
  function isPlaywright(configs) {
20
27
  return typeof configs.controller !== 'string' && 'connectOverCDP' in configs.controller;
21
28
  }
@@ -20,9 +20,10 @@ export class PlaywrightPage {
20
20
  return this.page.url();
21
21
  });
22
22
  }
23
- goto(url) {
23
+ goto(url, options) {
24
24
  return __awaiter(this, void 0, void 0, function* () {
25
- yield this.page.goto(url, { waitUntil: 'load' });
25
+ const waitUntil = (options === null || options === void 0 ? void 0 : options.waitUntil) === 'idle' ? 'networkidle' : 'load';
26
+ yield this.page.goto(url, { waitUntil });
26
27
  });
27
28
  }
28
29
  fill(selector, value) {
@@ -19,9 +19,10 @@ export class PuppeteerPage {
19
19
  return this.page.url();
20
20
  });
21
21
  }
22
- goto(url) {
22
+ goto(url, options) {
23
23
  return __awaiter(this, void 0, void 0, function* () {
24
- yield this.page.goto(url, { waitUntil: 'load' });
24
+ const waitUntil = (options === null || options === void 0 ? void 0 : options.waitUntil) === 'idle' ? 'networkidle0' : 'load';
25
+ yield this.page.goto(url, { waitUntil });
25
26
  });
26
27
  }
27
28
  fill(selector, value) {
@@ -0,0 +1,122 @@
1
+ // WebView에 주입되는 브릿지 스크립트
2
+ // 모든 DOM 조작은 이 스크립트 내부에서 실행되며, postMessage로 결과를 반환함
3
+ export const BRIDGE_SCRIPT = `
4
+ (function() {
5
+ if (window.__LOTTO_BRIDGE__) return;
6
+
7
+ window.__LOTTO_BRIDGE__ = {
8
+ fill: function(selector, value) {
9
+ var el = document.querySelector(selector);
10
+ if (!el) return { ok: false, error: 'Element not found: ' + selector };
11
+ el.focus();
12
+ el.value = value;
13
+ el.dispatchEvent(new Event('input', { bubbles: true }));
14
+ el.dispatchEvent(new Event('change', { bubbles: true }));
15
+ return { ok: true };
16
+ },
17
+
18
+ click: function(selector, domDirect) {
19
+ var el = document.querySelector(selector);
20
+ if (!el) return { ok: false, error: 'Element not found: ' + selector };
21
+ el.click();
22
+ return { ok: true };
23
+ },
24
+
25
+ select: function(selector, value) {
26
+ var el = document.querySelector(selector);
27
+ if (!el) return { ok: false, error: 'Element not found: ' + selector };
28
+ el.value = value;
29
+ el.dispatchEvent(new Event('change', { bubbles: true }));
30
+ return { ok: true };
31
+ },
32
+
33
+ exists: function(selector, containsText) {
34
+ var el = document.querySelector(selector);
35
+ if (!el) return false;
36
+ if (containsText) return (el.textContent || '').indexOf(containsText) !== -1;
37
+ return true;
38
+ },
39
+
40
+ querySelectorAll: function(selector) {
41
+ var MAX_DEPTH = 5;
42
+ function toFakeDOM(el, depth) {
43
+ return {
44
+ className: el.className || '',
45
+ innerHTML: el.innerHTML || '',
46
+ children: depth < MAX_DEPTH
47
+ ? Array.from(el.children).map(function(c) { return toFakeDOM(c, depth + 1); })
48
+ : []
49
+ };
50
+ }
51
+ return Array.from(document.querySelectorAll(selector)).map(function(el) { return toFakeDOM(el, 0); });
52
+ },
53
+
54
+ getUrl: function() {
55
+ return window.location.href;
56
+ },
57
+
58
+ getCookies: function() {
59
+ return document.cookie;
60
+ },
61
+
62
+ setCookies: function(cookieString) {
63
+ cookieString.split('; ').forEach(function(cookie) {
64
+ document.cookie = cookie;
65
+ });
66
+ return { ok: true };
67
+ },
68
+
69
+ waitForSelector: function(selector, timeout) {
70
+ return new Promise(function(resolve) {
71
+ var el = document.querySelector(selector);
72
+ if (el) { resolve({ ok: true }); return; }
73
+
74
+ var observer = new MutationObserver(function() {
75
+ var found = document.querySelector(selector);
76
+ if (found) {
77
+ observer.disconnect();
78
+ clearTimeout(timer);
79
+ resolve({ ok: true });
80
+ }
81
+ });
82
+ observer.observe(document.body || document.documentElement, { childList: true, subtree: true });
83
+
84
+ var timer = setTimeout(function() {
85
+ observer.disconnect();
86
+ resolve({ ok: false, error: 'Timeout waiting for: ' + selector });
87
+ }, timeout || 10000);
88
+ });
89
+ }
90
+ };
91
+
92
+ var ALLOWED_METHODS = ['fill','click','select','exists','querySelectorAll','getUrl','getCookies','setCookies','waitForSelector'];
93
+
94
+ // 커맨드 수신 리스너
95
+ window.addEventListener('message', function(e) {
96
+ try {
97
+ var msg = typeof e.data === 'string' ? JSON.parse(e.data) : e.data;
98
+ if (!msg || !msg.__lotto_call_id__ || !msg.method) return;
99
+ if (ALLOWED_METHODS.indexOf(msg.method) === -1) return;
100
+
101
+ var fn = window.__LOTTO_BRIDGE__[msg.method];
102
+ if (!fn) return;
103
+
104
+ Promise.resolve(fn.apply(null, msg.args || [])).then(function(result) {
105
+ window.ReactNativeWebView.postMessage(JSON.stringify({
106
+ __lotto_call_id__: msg.__lotto_call_id__,
107
+ result: result
108
+ }));
109
+ });
110
+ } catch (err) {
111
+ if (msg && msg.__lotto_call_id__) {
112
+ window.ReactNativeWebView.postMessage(JSON.stringify({
113
+ __lotto_call_id__: msg.__lotto_call_id__,
114
+ result: { ok: false, error: String(err) }
115
+ }));
116
+ }
117
+ }
118
+ });
119
+
120
+ true;
121
+ })();
122
+ `;
@@ -0,0 +1,31 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { WebViewPage } from './webview.page';
11
+ export class WebViewController {
12
+ constructor(configs, logger) {
13
+ this.page = null;
14
+ this.focus = () => __awaiter(this, void 0, void 0, function* () {
15
+ if (!this.page) {
16
+ this.page = new WebViewPage(this.configs.controller, this.logger);
17
+ }
18
+ return this.page;
19
+ });
20
+ this.close = () => __awaiter(this, void 0, void 0, function* () {
21
+ this.configs.controller.destroy();
22
+ this.page = null;
23
+ });
24
+ this.cleanPages = () => __awaiter(this, void 0, void 0, function* () {
25
+ // WebView는 단일 페이지이므로 no-op
26
+ this.logger.debug('[WebViewController]', 'cleanPages is no-op for WebView');
27
+ });
28
+ this.configs = configs;
29
+ this.logger = logger;
30
+ }
31
+ }
@@ -0,0 +1 @@
1
+ export {};