scordi-extension 1.14.6 → 1.14.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/loading-page-1924caaa.js +51 -0
- package/dist/blocks/ClearValueFormBlock.d.ts +8 -8
- package/dist/blocks/DataExtractBlock.d.ts +2 -2
- package/dist/blocks/ElementExistsBlock.d.ts +8 -8
- package/dist/blocks/EventClickBlock.d.ts +12 -12
- package/dist/blocks/FetchApiBlock.d.ts +2 -2
- package/dist/blocks/GetAttributeValueBlock.d.ts +8 -8
- package/dist/blocks/GetElementDataBlock.d.ts +14 -14
- package/dist/blocks/GetTextBlock.d.ts +10 -10
- package/dist/blocks/GetValueFormBlock.d.ts +8 -8
- package/dist/blocks/SaveAssetsBlock.d.ts +8 -8
- package/dist/blocks/ScrollBlock.d.ts +8 -8
- package/dist/blocks/SetValueFormBlock.d.ts +8 -8
- package/dist/blocks/WaitForConditionBlock.d.ts +176 -0
- package/dist/blocks/WaitForConditionBlock.d.ts.map +1 -0
- package/dist/blocks/index.d.ts +232 -96
- package/dist/blocks/index.d.ts.map +1 -1
- package/dist/blocks/types.d.ts +25 -8
- package/dist/blocks/types.d.ts.map +1 -1
- package/dist/logo.png +0 -0
- package/dist/manifest.json +55 -0
- package/dist/public/logo.png +0 -0
- package/dist/sdk/index.cjs +4 -1
- package/dist/sdk/index.d.ts +1 -0
- package/dist/sdk/index.d.ts.map +1 -1
- package/dist/sdk/index.js +5569 -1437
- package/dist/sdk/types.d.ts +3 -3
- package/dist/sdk/types.d.ts.map +1 -1
- package/dist/service-worker-loader.js +3 -0
- package/dist/src/blocks/AiParseDataBlock.ts.js +101 -0
- package/dist/src/blocks/ClearValueFormBlock.ts.js +55 -0
- package/dist/src/blocks/DataExtractBlock.ts.js +28 -0
- package/dist/src/blocks/ElementExistsBlock.ts.js +26 -0
- package/dist/src/blocks/EventClickBlock.ts.js +143 -0
- package/dist/src/blocks/FetchApiBlock.ts.js +50 -0
- package/dist/src/blocks/GetAttributeValueBlock.ts.js +33 -0
- package/dist/src/blocks/GetElementDataBlock.ts.js +114 -0
- package/dist/src/blocks/GetTextBlock.ts.js +152 -0
- package/dist/src/blocks/GetValueFormBlock.ts.js +52 -0
- package/dist/src/blocks/KeypressBlock.ts.js +89 -0
- package/dist/src/blocks/SaveAssetsBlock.ts.js +35 -0
- package/dist/src/blocks/ScrollBlock.ts.js +111 -0
- package/dist/src/blocks/SetValueFormBlock.ts.js +56 -0
- package/dist/src/blocks/WaitBlock.ts.js +24 -0
- package/dist/src/blocks/WaitForConditionBlock.ts.js +187 -0
- package/dist/src/blocks/index.ts.js +174 -0
- package/dist/src/blocks/types.ts.js +12 -0
- package/dist/src/content/components/ConfirmationUI.tsx.js +236 -0
- package/dist/src/content/elements/finders/CssSelector.ts.js +51 -0
- package/dist/src/content/elements/finders/ElementSelector.ts.js +20 -0
- package/dist/src/content/elements/finders/IframeSelector.ts.js +32 -0
- package/dist/src/content/elements/finders/ShadowDOMSelector.ts.js +38 -0
- package/dist/src/content/elements/finders/XPathFinder.ts.js +32 -0
- package/dist/src/content/elements/index.ts.js +26 -0
- package/dist/src/content/elements/utils/CSSSelectorGenerator.ts.js +72 -0
- package/dist/src/content/elements/utils/XPathGenerator.ts.js +62 -0
- package/dist/src/content/handler/ExternalMessageHandler.ts.js +78 -0
- package/dist/src/content/handler/InternalMessageHandler.ts.js +18 -0
- package/dist/src/content/kernel/MessageKernel.ts.js +83 -0
- package/dist/src/content/main.tsx-loader.js +22 -0
- package/dist/src/content/main.tsx.js +27 -0
- package/dist/src/content/utils/index.ts.js +1 -0
- package/dist/src/content/utils/synchronizedLock.ts.js +35 -0
- package/dist/src/popup/index.html +12 -0
- package/dist/src/types/internal-messages.ts.js +15 -0
- package/dist/vendor/.vite-deps-chunk-2TUXWMP5.js__v--9054997c.js +45 -0
- package/dist/vendor/.vite-deps-chunk-2TUXWMP5.js__v--e2a1f584.js +45 -0
- package/dist/vendor/.vite-deps-chunk-EL3BNLGW.js__v--e2a1f584.js +975 -0
- package/dist/vendor/.vite-deps-chunk-QIBDMRD4.js__v--9054997c.js +4158 -0
- package/dist/vendor/.vite-deps-chunk-QIBDMRD4.js__v--e2a1f584.js +4158 -0
- package/dist/vendor/.vite-deps-chunk-XHY3JSIG.js__v--e2a1f584.js +280 -0
- package/dist/vendor/.vite-deps-jsonata.js__v--d602c657.js +5761 -0
- package/dist/vendor/.vite-deps-jsonata.js__v--e2a1f584.js +5761 -0
- package/dist/vendor/.vite-deps-react-dom.js__v--e2a1f584.js +6 -0
- package/dist/vendor/.vite-deps-react-dom_client.js__v--e2a1f584.js +18108 -0
- package/dist/vendor/.vite-deps-react.js__v--e2a1f584.js +5 -0
- package/dist/vendor/.vite-deps-react_jsx-dev-runtime.js__v--e2a1f584.js +281 -0
- package/dist/vendor/.vite-deps-zod.js__v--9e0f4cc1.js +219 -0
- package/dist/vendor/.vite-deps-zod.js__v--e2a1f584.js +219 -0
- package/dist/vendor/crx-client-port.js +66 -0
- package/dist/vendor/crx-client-preamble.js +4 -0
- package/dist/vendor/react-refresh.js +670 -0
- package/dist/vendor/vite-client.js +1134 -0
- package/dist/vendor/vite-dist-client-env.mjs.js +24 -0
- package/dist/vendor/webcomponents-custom-elements.js +47 -0
- package/package.json +1 -1
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { ElementSelector } from "/src/content/elements/finders/ElementSelector.ts.js";
|
|
2
|
+
export class IframSelector extends ElementSelector {
|
|
3
|
+
async find(data, documentCtx = document) {
|
|
4
|
+
const { selector, option } = data;
|
|
5
|
+
const { multiple = false } = option || {};
|
|
6
|
+
if (!selector || selector.trim() === "") {
|
|
7
|
+
return null;
|
|
8
|
+
}
|
|
9
|
+
const parts = selector.split("|>").map((part) => part.trim());
|
|
10
|
+
if (parts.length !== 2) {
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
const iframeSelector = parts[0];
|
|
14
|
+
const targetSelector = parts[1];
|
|
15
|
+
if (!iframeSelector || !targetSelector) {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
try {
|
|
19
|
+
const iframe = documentCtx.querySelector(iframeSelector);
|
|
20
|
+
if (!iframe || !iframe.contentDocument) return null;
|
|
21
|
+
const iframeDoc = iframe.contentDocument;
|
|
22
|
+
if (multiple) {
|
|
23
|
+
return Array.from(iframeDoc.querySelectorAll(targetSelector));
|
|
24
|
+
} else {
|
|
25
|
+
return iframeDoc.querySelector(targetSelector);
|
|
26
|
+
}
|
|
27
|
+
} catch (error) {
|
|
28
|
+
console.error("Iframe Selector error:", error);
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { ElementSelector } from "/src/content/elements/finders/ElementSelector.ts.js";
|
|
2
|
+
export class ShadowDOMSelector extends ElementSelector {
|
|
3
|
+
async find(data, documentCtx = document) {
|
|
4
|
+
const { selector, option } = data;
|
|
5
|
+
const { multiple = false } = option || {};
|
|
6
|
+
if (!selector || selector.trim() === "") {
|
|
7
|
+
return null;
|
|
8
|
+
}
|
|
9
|
+
const parts = selector.split(">>").map((part) => part.trim());
|
|
10
|
+
let currentContext = documentCtx;
|
|
11
|
+
try {
|
|
12
|
+
for (let i = 0; i < parts.length; i++) {
|
|
13
|
+
const part = parts[i];
|
|
14
|
+
const isLast = i === parts.length - 1;
|
|
15
|
+
if (!part) {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
if (isLast && multiple) {
|
|
19
|
+
return Array.from(currentContext.querySelectorAll(part));
|
|
20
|
+
}
|
|
21
|
+
const element = currentContext.querySelector(part);
|
|
22
|
+
if (!element) return null;
|
|
23
|
+
if (isLast) {
|
|
24
|
+
return element;
|
|
25
|
+
}
|
|
26
|
+
if (element.shadowRoot) {
|
|
27
|
+
currentContext = element.shadowRoot;
|
|
28
|
+
} else {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
} catch (error) {
|
|
33
|
+
console.error("Shadow DOM Selector error:", error);
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { ElementSelector } from "/src/content/elements/finders/ElementSelector.ts.js";
|
|
2
|
+
export class XPathSelector extends ElementSelector {
|
|
3
|
+
async find(data, documentCtx = document) {
|
|
4
|
+
const { selector, option } = data;
|
|
5
|
+
const { multiple = false } = option || {};
|
|
6
|
+
try {
|
|
7
|
+
const result = documentCtx.evaluate(
|
|
8
|
+
selector,
|
|
9
|
+
documentCtx,
|
|
10
|
+
null,
|
|
11
|
+
multiple ? XPathResult.ORDERED_NODE_SNAPSHOT_TYPE : XPathResult.FIRST_ORDERED_NODE_TYPE,
|
|
12
|
+
null
|
|
13
|
+
);
|
|
14
|
+
if (multiple) {
|
|
15
|
+
const nodes = [];
|
|
16
|
+
for (let i = 0; i < result.snapshotLength; i++) {
|
|
17
|
+
const node = result.snapshotItem(i);
|
|
18
|
+
if (node && node.nodeType === Node.ELEMENT_NODE) {
|
|
19
|
+
nodes.push(node);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return Promise.resolve(nodes.length > 0 ? nodes : null);
|
|
23
|
+
} else {
|
|
24
|
+
const node = result.singleNodeValue;
|
|
25
|
+
return node && node.nodeType === Node.ELEMENT_NODE ? node : null;
|
|
26
|
+
}
|
|
27
|
+
} catch (error) {
|
|
28
|
+
console.error("XPath error:", error);
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { CssSelector } from "/src/content/elements/finders/CssSelector.ts.js";
|
|
2
|
+
import { IframSelector } from "/src/content/elements/finders/IframeSelector.ts.js";
|
|
3
|
+
import { ShadowDOMSelector } from "/src/content/elements/finders/ShadowDOMSelector.ts.js";
|
|
4
|
+
import { XPathSelector } from "/src/content/elements/finders/XPathFinder.ts.js";
|
|
5
|
+
export async function findElement(data, documentCtx = document) {
|
|
6
|
+
const { selector, findBy = "cssSelector", option } = data;
|
|
7
|
+
const { waitForSelector = false, waitSelectorTimeout = 5e3 } = option || {};
|
|
8
|
+
const selectorInstance = buildSelector(selector, findBy);
|
|
9
|
+
if (waitForSelector) {
|
|
10
|
+
return selectorInstance.waitForElement(data, documentCtx, waitSelectorTimeout);
|
|
11
|
+
}
|
|
12
|
+
return selectorInstance.find(data, documentCtx);
|
|
13
|
+
}
|
|
14
|
+
function buildSelector(selector, findBy) {
|
|
15
|
+
if (findBy === "xpath") {
|
|
16
|
+
return new XPathSelector();
|
|
17
|
+
}
|
|
18
|
+
if (selector.includes(">>")) {
|
|
19
|
+
return new ShadowDOMSelector();
|
|
20
|
+
}
|
|
21
|
+
if (selector.includes("|>")) {
|
|
22
|
+
return new IframSelector();
|
|
23
|
+
}
|
|
24
|
+
return new CssSelector();
|
|
25
|
+
}
|
|
26
|
+
export { CSSSelectorGenerator } from "/src/content/elements/utils/CSSSelectorGenerator.ts.js";
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
export class CSSSelectorGenerator {
|
|
2
|
+
/**
|
|
3
|
+
* 요소의 안정적인 CSS 셀렉터 생성
|
|
4
|
+
* @param element 대상 요소
|
|
5
|
+
* @returns 생성된 CSS 셀렉터
|
|
6
|
+
*/
|
|
7
|
+
static generate(element) {
|
|
8
|
+
const staticAttributes = ["data-testid", "aria-label", "title", "alt", "role"];
|
|
9
|
+
for (const attr of staticAttributes) {
|
|
10
|
+
const value = element.getAttribute(attr);
|
|
11
|
+
if (value && !this.isDynamicValue(value)) {
|
|
12
|
+
return `[${attr}="${CSS.escape(value)}"]`;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
const tagName = element.tagName.toLowerCase();
|
|
16
|
+
const parent = element.parentElement;
|
|
17
|
+
if (parent) {
|
|
18
|
+
const siblings = Array.from(parent.children);
|
|
19
|
+
const index = siblings.indexOf(element) + 1;
|
|
20
|
+
if (siblings.length > 1) {
|
|
21
|
+
return `${tagName}:nth-child(${index})`;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
if (parent) {
|
|
25
|
+
const parentSelector = this.generateParentSelector(parent);
|
|
26
|
+
if (parentSelector) {
|
|
27
|
+
return `${parentSelector} > ${tagName}:nth-child(${Array.from(parent.children).indexOf(element) + 1})`;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return tagName;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* 부모 요소의 셀렉터 생성
|
|
34
|
+
*/
|
|
35
|
+
static generateParentSelector(parent) {
|
|
36
|
+
const tagName = parent.tagName.toLowerCase();
|
|
37
|
+
const staticAttributes = ["data-testid", "aria-label", "title", "alt", "role"];
|
|
38
|
+
for (const attr of staticAttributes) {
|
|
39
|
+
const value = parent.getAttribute(attr);
|
|
40
|
+
if (value && !this.isDynamicValue(value)) {
|
|
41
|
+
return `[${attr}="${CSS.escape(value)}"]`;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
const grandParent = parent.parentElement;
|
|
45
|
+
if (grandParent) {
|
|
46
|
+
const siblings = Array.from(grandParent.children);
|
|
47
|
+
const index = siblings.indexOf(parent) + 1;
|
|
48
|
+
if (siblings.length > 1) {
|
|
49
|
+
return `${tagName}:nth-child(${index})`;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return tagName;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* 동적 값 패턴 감지
|
|
56
|
+
* @param value 확인할 값
|
|
57
|
+
* @returns 동적 값 여부
|
|
58
|
+
*/
|
|
59
|
+
static isDynamicValue(value) {
|
|
60
|
+
const dynamicPatterns = [
|
|
61
|
+
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,
|
|
62
|
+
// UUID
|
|
63
|
+
/^\d{13}$/,
|
|
64
|
+
// timestamp
|
|
65
|
+
/^[a-zA-Z0-9]{16,}$/,
|
|
66
|
+
// long random string
|
|
67
|
+
/^[a-zA-Z0-9]+-\d+$/
|
|
68
|
+
// name-123 pattern
|
|
69
|
+
];
|
|
70
|
+
return dynamicPatterns.some((pattern) => pattern.test(value));
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
export class XPathGenerator {
|
|
2
|
+
/**
|
|
3
|
+
* 요소의 XPath 생성
|
|
4
|
+
* @param element 대상 요소
|
|
5
|
+
* @returns 생성된 XPath
|
|
6
|
+
*/
|
|
7
|
+
static generate(element) {
|
|
8
|
+
const path = [];
|
|
9
|
+
let current = element;
|
|
10
|
+
while (current && current.nodeType === Node.ELEMENT_NODE) {
|
|
11
|
+
let selector = current.tagName.toLowerCase();
|
|
12
|
+
const siblings = Array.from(current.parentElement?.children || []).filter((sibling) => sibling.tagName === current.tagName);
|
|
13
|
+
if (siblings.length > 1) {
|
|
14
|
+
const index = siblings.indexOf(current) + 1;
|
|
15
|
+
selector += `[${index}]`;
|
|
16
|
+
}
|
|
17
|
+
const attributes = this.getStableAttributes(current);
|
|
18
|
+
if (attributes.length > 0) {
|
|
19
|
+
selector += `[@${attributes.join(" and @")}]`;
|
|
20
|
+
}
|
|
21
|
+
path.unshift(selector);
|
|
22
|
+
current = current.parentElement;
|
|
23
|
+
}
|
|
24
|
+
return "/" + path.join("/");
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* 요소의 안정적인 속성들을 반환
|
|
28
|
+
* @param element 대상 요소
|
|
29
|
+
* @returns 안정적인 속성 배열
|
|
30
|
+
*/
|
|
31
|
+
static getStableAttributes(element) {
|
|
32
|
+
const stableAttributes = [];
|
|
33
|
+
const stableAttrs = ["data-testid", "aria-label", "title", "alt", "role"];
|
|
34
|
+
for (const attr of stableAttrs) {
|
|
35
|
+
const value = element.getAttribute(attr);
|
|
36
|
+
if (value && !this.isDynamicValue(value)) {
|
|
37
|
+
stableAttributes.push(`${attr}="${value}"`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return stableAttributes;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* 동적 값 패턴 감지
|
|
44
|
+
* @param value 확인할 값
|
|
45
|
+
* @returns 동적 값 여부
|
|
46
|
+
*/
|
|
47
|
+
static isDynamicValue(value) {
|
|
48
|
+
const dynamicPatterns = [
|
|
49
|
+
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,
|
|
50
|
+
// UUID
|
|
51
|
+
/^\d{13}$/,
|
|
52
|
+
// timestamp
|
|
53
|
+
/^[a-zA-Z0-9]{16,}$/,
|
|
54
|
+
// long random string
|
|
55
|
+
/^[a-zA-Z0-9]+-\d+$/,
|
|
56
|
+
// name-123 pattern
|
|
57
|
+
/^[a-zA-Z0-9]+_[a-zA-Z0-9]+$/
|
|
58
|
+
// underscore pattern
|
|
59
|
+
];
|
|
60
|
+
return dynamicPatterns.some((pattern) => pattern.test(value));
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
export class ExternalMessageHandler {
|
|
2
|
+
constructor(kernel) {
|
|
3
|
+
this.kernel = kernel;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* 웹페이지로부터의 window message 리스너 초기화
|
|
7
|
+
*/
|
|
8
|
+
initializeMessageListener() {
|
|
9
|
+
window.addEventListener("message", this.handleWindowMessage.bind(this));
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* 웹페이지로부터의 window message 처리
|
|
13
|
+
*/
|
|
14
|
+
async handleWindowMessage(event) {
|
|
15
|
+
if (event.source !== window) return;
|
|
16
|
+
const message = event.data;
|
|
17
|
+
switch (message.type) {
|
|
18
|
+
case "8G_EXTENSION_CHECK":
|
|
19
|
+
this.handleExtensionCheck();
|
|
20
|
+
break;
|
|
21
|
+
case "8G_COLLECT_WORKFLOW":
|
|
22
|
+
await this.handleCollectWorkflow(message);
|
|
23
|
+
break;
|
|
24
|
+
default:
|
|
25
|
+
break;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* 확장 프로그램 설치 확인 요청 처리
|
|
30
|
+
*/
|
|
31
|
+
handleExtensionCheck() {
|
|
32
|
+
const response = {
|
|
33
|
+
type: "8G_EXTENSION_RESPONSE",
|
|
34
|
+
installed: true,
|
|
35
|
+
version: "1.0.0"
|
|
36
|
+
};
|
|
37
|
+
this.kernel.sendToWebpage(response);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* 워크플로우 수집 요청 처리
|
|
41
|
+
*/
|
|
42
|
+
async handleCollectWorkflow(message) {
|
|
43
|
+
try {
|
|
44
|
+
this.validateCollectWorkflowMessage(message);
|
|
45
|
+
const backgroundMessage = {
|
|
46
|
+
type: "COLLECT_WORKFLOW_NEW_TAB",
|
|
47
|
+
data: {
|
|
48
|
+
targetUrl: message.targetUrl,
|
|
49
|
+
workflow: message.workflow,
|
|
50
|
+
closeTabAfterCollection: message.closeTabAfterCollection !== false,
|
|
51
|
+
activateTab: message.activateTab === true
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
try {
|
|
55
|
+
const response = await this.kernel.sendToBackground(backgroundMessage);
|
|
56
|
+
const successResponse = this.kernel.createSuccessResponse(message.requestId, response);
|
|
57
|
+
this.kernel.sendToWebpage(successResponse);
|
|
58
|
+
} catch (error) {
|
|
59
|
+
const errorResponse = this.kernel.createErrorResponse(message.requestId, error);
|
|
60
|
+
this.kernel.sendToWebpage(errorResponse);
|
|
61
|
+
}
|
|
62
|
+
} catch (error) {
|
|
63
|
+
const errorResponse = this.kernel.createErrorResponse(message.requestId, error);
|
|
64
|
+
this.kernel.sendToWebpage(errorResponse);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
validateCollectWorkflowMessage(message) {
|
|
68
|
+
if (!message.targetUrl) {
|
|
69
|
+
throw new Error("Target URL is required");
|
|
70
|
+
}
|
|
71
|
+
if (!message.workflow) {
|
|
72
|
+
throw new Error("Workflow is required");
|
|
73
|
+
}
|
|
74
|
+
if (!message.workflow.start || !Array.isArray(message.workflow.steps)) {
|
|
75
|
+
throw new Error("Workflow must have start and steps");
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { isExecuteBlockMessage } from "/src/types/internal-messages.ts.js";
|
|
2
|
+
export class InternalMessageHandler {
|
|
3
|
+
constructor(kernel) {
|
|
4
|
+
this.kernel = kernel;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Background script로부터의 메시지 리스너 초기화
|
|
8
|
+
*/
|
|
9
|
+
initializeMessageListener() {
|
|
10
|
+
chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
|
|
11
|
+
if (isExecuteBlockMessage(message)) {
|
|
12
|
+
this.kernel.handleRuntimeMessage(message).then((result) => sendResponse(result)).catch((error) => sendResponse(this.kernel.createErrorResponse("", error)));
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
return false;
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isErrorResponse
|
|
3
|
+
} from "/src/types/internal-messages.ts.js";
|
|
4
|
+
export class MessageKernel {
|
|
5
|
+
/**
|
|
6
|
+
* Background script로 메시지 전송
|
|
7
|
+
*/
|
|
8
|
+
async sendToBackground(message) {
|
|
9
|
+
return new Promise((resolve, reject) => {
|
|
10
|
+
chrome.runtime.sendMessage(message, (response) => {
|
|
11
|
+
if (chrome.runtime.lastError) {
|
|
12
|
+
reject(new Error(chrome.runtime.lastError.message || "Communication error"));
|
|
13
|
+
} else if (isErrorResponse(response)) {
|
|
14
|
+
reject(new Error(response.message));
|
|
15
|
+
} else {
|
|
16
|
+
resolve(response);
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Content script에서 Block 실행
|
|
23
|
+
*/
|
|
24
|
+
async executeBlock(block) {
|
|
25
|
+
const { BlockHandler } = await import("/src/blocks/index.ts.js");
|
|
26
|
+
const { synchronizedLock } = await import("/src/content/utils/index.ts.js");
|
|
27
|
+
await synchronizedLock.getLock();
|
|
28
|
+
try {
|
|
29
|
+
return await BlockHandler.executeBlock(block);
|
|
30
|
+
} finally {
|
|
31
|
+
synchronizedLock.releaseLock();
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Chrome runtime 메시지 처리 (Background -> Content)
|
|
36
|
+
*/
|
|
37
|
+
async handleRuntimeMessage(message) {
|
|
38
|
+
if (message?.isBlock && message?.type === "EXECUTE_BLOCK") {
|
|
39
|
+
try {
|
|
40
|
+
const result = await this.executeBlock(message.data);
|
|
41
|
+
return result;
|
|
42
|
+
} catch (error) {
|
|
43
|
+
return {
|
|
44
|
+
$isError: true,
|
|
45
|
+
message: error instanceof Error ? error.message : "Unknown error",
|
|
46
|
+
data: {}
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
throw new Error("Invalid message type");
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Window message를 웹페이지로 전송
|
|
54
|
+
*/
|
|
55
|
+
sendToWebpage(message) {
|
|
56
|
+
window.postMessage(message, "*");
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* 에러 응답 생성 헬퍼
|
|
60
|
+
*/
|
|
61
|
+
createErrorResponse(requestId, error) {
|
|
62
|
+
return {
|
|
63
|
+
type: "8G_COLLECT_RESPONSE",
|
|
64
|
+
requestId,
|
|
65
|
+
success: false,
|
|
66
|
+
result: {
|
|
67
|
+
error: error instanceof Error ? error.message : "Unknown error",
|
|
68
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* 성공 응답 생성 헬퍼
|
|
74
|
+
*/
|
|
75
|
+
createSuccessResponse(requestId, result) {
|
|
76
|
+
return {
|
|
77
|
+
type: "8G_COLLECT_RESPONSE",
|
|
78
|
+
requestId,
|
|
79
|
+
success: true,
|
|
80
|
+
result
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
(function () {
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const injectTime = performance.now();
|
|
5
|
+
(async () => {
|
|
6
|
+
if ("vendor/crx-client-preamble.js")
|
|
7
|
+
await import(
|
|
8
|
+
/* @vite-ignore */
|
|
9
|
+
chrome.runtime.getURL("vendor/crx-client-preamble.js")
|
|
10
|
+
);
|
|
11
|
+
await import(
|
|
12
|
+
/* @vite-ignore */
|
|
13
|
+
chrome.runtime.getURL("vendor/vite-client.js")
|
|
14
|
+
);
|
|
15
|
+
const { onExecute } = await import(
|
|
16
|
+
/* @vite-ignore */
|
|
17
|
+
chrome.runtime.getURL("src/content/main.tsx.js")
|
|
18
|
+
);
|
|
19
|
+
onExecute?.({ perf: { injectTime, loadTime: performance.now() - injectTime } });
|
|
20
|
+
})().catch(console.error);
|
|
21
|
+
|
|
22
|
+
})();
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import __vite__cjsImport0_react_jsxDevRuntime from "/vendor/.vite-deps-react_jsx-dev-runtime.js__v--e2a1f584.js"; const jsxDEV = __vite__cjsImport0_react_jsxDevRuntime["jsxDEV"];
|
|
2
|
+
import __vite__cjsImport1_reactDom_client from "/vendor/.vite-deps-react-dom_client.js__v--e2a1f584.js"; const createRoot = __vite__cjsImport1_reactDom_client["createRoot"];
|
|
3
|
+
import { MessageKernel } from "/src/content/kernel/MessageKernel.ts.js";
|
|
4
|
+
import { InternalMessageHandler } from "/src/content/handler/InternalMessageHandler.ts.js";
|
|
5
|
+
import { ExternalMessageHandler } from "/src/content/handler/ExternalMessageHandler.ts.js";
|
|
6
|
+
import { ConfirmationUIContainer } from "/src/content/components/ConfirmationUI.tsx.js";
|
|
7
|
+
(() => {
|
|
8
|
+
if (window.is8gExtensionInjected) return;
|
|
9
|
+
window.is8gExtensionInjected = true;
|
|
10
|
+
console.log("[8G Extension] Content script initialized on:", window.location.href);
|
|
11
|
+
const messageKernel = new MessageKernel();
|
|
12
|
+
const internalHandler = new InternalMessageHandler(messageKernel);
|
|
13
|
+
const externalHandler = new ExternalMessageHandler(messageKernel);
|
|
14
|
+
internalHandler.initializeMessageListener();
|
|
15
|
+
externalHandler.initializeMessageListener();
|
|
16
|
+
const uiRoot = document.createElement("div");
|
|
17
|
+
uiRoot.id = "8g-confirmation-ui-root";
|
|
18
|
+
uiRoot.style.cssText = "all: initial; position: fixed; z-index: 2147483647;";
|
|
19
|
+
document.body.appendChild(uiRoot);
|
|
20
|
+
const root = createRoot(uiRoot);
|
|
21
|
+
root.render(/* @__PURE__ */ jsxDEV(ConfirmationUIContainer, {}, void 0, false, {
|
|
22
|
+
fileName: "/Users/kerry/Documents/GitHub/8g-extension/src/content/main.tsx",
|
|
23
|
+
lineNumber: 34,
|
|
24
|
+
columnNumber: 15
|
|
25
|
+
}, this));
|
|
26
|
+
console.log("[8G Extension] Confirmation UI mounted");
|
|
27
|
+
})();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { synchronizedLock } from "/src/content/utils/synchronizedLock.ts.js";
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
class SynchronizedLock {
|
|
2
|
+
isLocked = false;
|
|
3
|
+
waitQueue = [];
|
|
4
|
+
async getLock() {
|
|
5
|
+
return new Promise((resolve) => {
|
|
6
|
+
if (!this.isLocked) {
|
|
7
|
+
this.isLocked = true;
|
|
8
|
+
resolve();
|
|
9
|
+
} else {
|
|
10
|
+
this.waitQueue.push(resolve);
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
releaseLock() {
|
|
15
|
+
if (!this.isLocked) {
|
|
16
|
+
console.warn("[SynchronizedLock] Attempting to release an unlocked lock");
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
this.isLocked = false;
|
|
20
|
+
if (this.waitQueue.length > 0) {
|
|
21
|
+
const nextResolve = this.waitQueue.shift();
|
|
22
|
+
if (nextResolve) {
|
|
23
|
+
this.isLocked = true;
|
|
24
|
+
nextResolve();
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
isCurrentlyLocked() {
|
|
29
|
+
return this.isLocked;
|
|
30
|
+
}
|
|
31
|
+
getQueueLength() {
|
|
32
|
+
return this.waitQueue.length;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
export const synchronizedLock = new SynchronizedLock();
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<title>Vite Dev Mode</title>
|
|
5
|
+
<script src="/assets/loading-page-1924caaa.js" type="module"></script>
|
|
6
|
+
</head>
|
|
7
|
+
<body
|
|
8
|
+
style="font-family: Arial, sans-serif; padding: 20px; text-align: center"
|
|
9
|
+
>
|
|
10
|
+
<h1>Vite Dev Mode</h1>
|
|
11
|
+
</body>
|
|
12
|
+
</html>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export function isExecuteBlockMessage(message) {
|
|
2
|
+
return message && message.isBlock === true && message.type === "EXECUTE_BLOCK";
|
|
3
|
+
}
|
|
4
|
+
export function isCdpClickMessage(message) {
|
|
5
|
+
return message && message.type === "CDP_CLICK";
|
|
6
|
+
}
|
|
7
|
+
export function isCdpKeypressMessage(message) {
|
|
8
|
+
return message && message.type === "CDP_KEYPRESS";
|
|
9
|
+
}
|
|
10
|
+
export function isFetchApiMessage(message) {
|
|
11
|
+
return message && message.type === "FETCH_API";
|
|
12
|
+
}
|
|
13
|
+
export function isErrorResponse(response) {
|
|
14
|
+
return response && response.$isError === true;
|
|
15
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
var __create = Object.create;
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
8
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
9
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
10
|
+
}) : x)(function(x) {
|
|
11
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
12
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
13
|
+
});
|
|
14
|
+
var __commonJS = (cb, mod) => function __require2() {
|
|
15
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
16
|
+
};
|
|
17
|
+
var __export = (target, all) => {
|
|
18
|
+
for (var name in all)
|
|
19
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
20
|
+
};
|
|
21
|
+
var __copyProps = (to, from, except, desc) => {
|
|
22
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
23
|
+
for (let key of __getOwnPropNames(from))
|
|
24
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
25
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
26
|
+
}
|
|
27
|
+
return to;
|
|
28
|
+
};
|
|
29
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
30
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
31
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
32
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
33
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
34
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
35
|
+
mod
|
|
36
|
+
));
|
|
37
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
38
|
+
|
|
39
|
+
export {
|
|
40
|
+
__require,
|
|
41
|
+
__commonJS,
|
|
42
|
+
__export,
|
|
43
|
+
__toESM,
|
|
44
|
+
__publicField
|
|
45
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
var __create = Object.create;
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
8
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
9
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
10
|
+
}) : x)(function(x) {
|
|
11
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
12
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
13
|
+
});
|
|
14
|
+
var __commonJS = (cb, mod) => function __require2() {
|
|
15
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
16
|
+
};
|
|
17
|
+
var __export = (target, all) => {
|
|
18
|
+
for (var name in all)
|
|
19
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
20
|
+
};
|
|
21
|
+
var __copyProps = (to, from, except, desc) => {
|
|
22
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
23
|
+
for (let key of __getOwnPropNames(from))
|
|
24
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
25
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
26
|
+
}
|
|
27
|
+
return to;
|
|
28
|
+
};
|
|
29
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
30
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
31
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
32
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
33
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
34
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
35
|
+
mod
|
|
36
|
+
));
|
|
37
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
38
|
+
|
|
39
|
+
export {
|
|
40
|
+
__require,
|
|
41
|
+
__commonJS,
|
|
42
|
+
__export,
|
|
43
|
+
__toESM,
|
|
44
|
+
__publicField
|
|
45
|
+
};
|