@auto-wiz/core 1.0.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.
@@ -0,0 +1,4 @@
1
+ export * from "./types";
2
+ export * from "./steps/stepValidation";
3
+ export * from "./storage/flowStorage";
4
+ export * from "./runner";
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export * from "./types";
2
+ export * from "./steps/stepValidation";
3
+ export * from "./storage/flowStorage";
4
+ export * from "./runner";
@@ -0,0 +1,28 @@
1
+ import type { Flow, Step } from "./types";
2
+ export interface ExecutionResult {
3
+ success: boolean;
4
+ error?: string;
5
+ extractedData?: any;
6
+ usedSelector?: string;
7
+ }
8
+ export interface RunResult {
9
+ success: boolean;
10
+ error?: string;
11
+ failedStepIndex?: number;
12
+ extractedData?: Record<string, any>;
13
+ }
14
+ export interface RunnerOptions {
15
+ timeout?: number;
16
+ stopOnError?: boolean;
17
+ }
18
+ /**
19
+ * Abstract Flow Runner Interface
20
+ *
21
+ * TContext: The environment-specific context required to run the flow.
22
+ * - DOM: void (runs in global window)
23
+ * - Playwright: Page (runs on a specific page instance)
24
+ */
25
+ export interface FlowRunner<TContext = any> {
26
+ run(flow: Flow, context: TContext, options?: RunnerOptions): Promise<RunResult>;
27
+ runStep(step: Step, context: TContext, options?: RunnerOptions): Promise<ExecutionResult>;
28
+ }
package/dist/runner.js ADDED
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,25 @@
1
+ import type { Step } from "../types";
2
+ /**
3
+ * Step validation 유틸리티
4
+ * Step의 유효성 검증 및 오류 메시지 생성
5
+ */
6
+ export interface ValidationResult {
7
+ valid: boolean;
8
+ error?: string;
9
+ }
10
+ /**
11
+ * Step의 기본 구조 검증
12
+ */
13
+ export declare function validateStep(step: Step): ValidationResult;
14
+ /**
15
+ * Step 배열의 모든 Step 검증
16
+ */
17
+ export declare function validateSteps(steps: Step[]): ValidationResult;
18
+ /**
19
+ * Step이 실행 가능한지 확인
20
+ */
21
+ export declare function isExecutableStep(step: Step): boolean;
22
+ /**
23
+ * Step 타입별 필수 필드 확인
24
+ */
25
+ export declare function hasRequiredFields(step: Step): boolean;
@@ -0,0 +1,134 @@
1
+ /**
2
+ * Step의 기본 구조 검증
3
+ */
4
+ export function validateStep(step) {
5
+ if (!step || typeof step !== "object") {
6
+ return { valid: false, error: "Step must be an object" };
7
+ }
8
+ if (!step.type) {
9
+ return { valid: false, error: "Step type is required" };
10
+ }
11
+ // 타입별 검증
12
+ switch (step.type) {
13
+ case "click":
14
+ return validateClickStep(step);
15
+ case "type":
16
+ return validateTypeStep(step);
17
+ case "select":
18
+ return validateSelectStep(step);
19
+ case "extract":
20
+ return validateExtractStep(step);
21
+ case "navigate":
22
+ return validateNavigateStep(step);
23
+ case "waitFor":
24
+ return validateWaitForStep(step);
25
+ default:
26
+ return { valid: false, error: `Unknown step type: ${step.type}` };
27
+ }
28
+ }
29
+ function validateClickStep(step) {
30
+ if (step.type !== "click") {
31
+ return { valid: false, error: "Invalid step type for click validation" };
32
+ }
33
+ if (!step.selector) {
34
+ return { valid: false, error: "Click step requires selector" };
35
+ }
36
+ return { valid: true };
37
+ }
38
+ function validateTypeStep(step) {
39
+ if (step.type !== "type") {
40
+ return { valid: false, error: "Invalid step type for type validation" };
41
+ }
42
+ if (!step.selector) {
43
+ return { valid: false, error: "Type step requires selector" };
44
+ }
45
+ if (step.text === undefined && step.originalText === undefined) {
46
+ return { valid: false, error: "Type step requires text or originalText" };
47
+ }
48
+ return { valid: true };
49
+ }
50
+ function validateSelectStep(step) {
51
+ if (step.type !== "select") {
52
+ return { valid: false, error: "Invalid step type for select validation" };
53
+ }
54
+ if (!step.selector) {
55
+ return { valid: false, error: "Select step requires selector" };
56
+ }
57
+ if (step.value === undefined) {
58
+ return { valid: false, error: "Select step requires value" };
59
+ }
60
+ return { valid: true };
61
+ }
62
+ function validateExtractStep(step) {
63
+ if (step.type !== "extract") {
64
+ return { valid: false, error: "Invalid step type for extract validation" };
65
+ }
66
+ if (!step.selector) {
67
+ return { valid: false, error: "Extract step requires selector" };
68
+ }
69
+ return { valid: true };
70
+ }
71
+ function validateNavigateStep(step) {
72
+ if (step.type !== "navigate") {
73
+ return { valid: false, error: "Invalid step type for navigate validation" };
74
+ }
75
+ if (!step.url) {
76
+ return { valid: false, error: "Navigate step requires URL" };
77
+ }
78
+ // URL 형식 검증
79
+ try {
80
+ new URL(step.url);
81
+ }
82
+ catch {
83
+ return { valid: false, error: `Invalid URL: ${step.url}` };
84
+ }
85
+ return { valid: true };
86
+ }
87
+ function validateWaitForStep(step) {
88
+ if (step.type !== "waitFor") {
89
+ return { valid: false, error: "Invalid step type for waitFor validation" };
90
+ }
91
+ if (!step.selector && step.timeoutMs === undefined) {
92
+ return {
93
+ valid: false,
94
+ error: "WaitFor step requires selector or timeoutMs",
95
+ };
96
+ }
97
+ if (step.timeoutMs !== undefined) {
98
+ if (typeof step.timeoutMs !== "number" || step.timeoutMs < 0) {
99
+ return { valid: false, error: "Timeout must be a positive number" };
100
+ }
101
+ }
102
+ return { valid: true };
103
+ }
104
+ /**
105
+ * Step 배열의 모든 Step 검증
106
+ */
107
+ export function validateSteps(steps) {
108
+ if (!Array.isArray(steps)) {
109
+ return { valid: false, error: "Steps must be an array" };
110
+ }
111
+ for (let i = 0; i < steps.length; i++) {
112
+ const result = validateStep(steps[i]);
113
+ if (!result.valid) {
114
+ return {
115
+ valid: false,
116
+ error: `Step ${i + 1}: ${result.error}`,
117
+ };
118
+ }
119
+ }
120
+ return { valid: true };
121
+ }
122
+ /**
123
+ * Step이 실행 가능한지 확인
124
+ */
125
+ export function isExecutableStep(step) {
126
+ return validateStep(step).valid;
127
+ }
128
+ /**
129
+ * Step 타입별 필수 필드 확인
130
+ */
131
+ export function hasRequiredFields(step) {
132
+ const validation = validateStep(step);
133
+ return validation.valid;
134
+ }
@@ -0,0 +1,51 @@
1
+ import type { Flow, Step } from "../types";
2
+ export interface StorageAdapter {
3
+ get(key: string): Promise<any>;
4
+ set(key: string, value: any): Promise<void>;
5
+ }
6
+ declare class ExtensionStorageAdapter implements StorageAdapter {
7
+ get(key: string): Promise<any>;
8
+ set(key: string, value: any): Promise<void>;
9
+ }
10
+ declare class MemoryStorageAdapter implements StorageAdapter {
11
+ private storage;
12
+ get(key: string): Promise<any>;
13
+ set(key: string, value: any): Promise<void>;
14
+ }
15
+ /**
16
+ * 스토리지 어댑터 설정 (라이브러리 사용 시 필수)
17
+ */
18
+ export declare function setStorageAdapter(adapter: StorageAdapter): void;
19
+ /**
20
+ * Flow 가져오기
21
+ */
22
+ export declare function getFlow(): Promise<Flow | null>;
23
+ /**
24
+ * Flow 저장하기
25
+ */
26
+ export declare function saveFlow(flow: Flow): Promise<void>;
27
+ /**
28
+ * Flow 초기화 (비우기)
29
+ */
30
+ export declare function clearFlow(): Promise<void>;
31
+ /**
32
+ * Flow에 Step 추가
33
+ */
34
+ export declare function addStep(step: Step): Promise<Flow>;
35
+ /**
36
+ * Flow에서 마지막 Step 제거
37
+ */
38
+ export declare function removeLastStep(): Promise<Flow>;
39
+ /**
40
+ * Flow에서 특정 Step 제거
41
+ */
42
+ export declare function removeStep(index: number): Promise<Flow>;
43
+ /**
44
+ * Flow의 특정 Step 업데이트
45
+ */
46
+ export declare function updateStep(index: number, step: Step): Promise<Flow>;
47
+ /**
48
+ * Flow 업데이트 (전체 교체)
49
+ */
50
+ export declare function updateFlow(updates: Partial<Flow>): Promise<Flow>;
51
+ export { ExtensionStorageAdapter, MemoryStorageAdapter };
@@ -0,0 +1,157 @@
1
+ const FLOW_STORAGE_KEY = "flow";
2
+ // 기본 브라우저 확장 프로그램 스토리지 어댑터
3
+ class ExtensionStorageAdapter {
4
+ async get(key) {
5
+ if (typeof browser !== "undefined" && browser.storage) {
6
+ const result = await browser.storage.local.get(key);
7
+ return result[key];
8
+ }
9
+ return null;
10
+ }
11
+ async set(key, value) {
12
+ if (typeof browser !== "undefined" && browser.storage) {
13
+ await browser.storage.local.set({ [key]: value });
14
+ }
15
+ }
16
+ }
17
+ // 메모리 스토리지 어댑터 (테스트 또는 비-확장 프로그램 환경용)
18
+ class MemoryStorageAdapter {
19
+ constructor() {
20
+ Object.defineProperty(this, "storage", {
21
+ enumerable: true,
22
+ configurable: true,
23
+ writable: true,
24
+ value: {}
25
+ });
26
+ }
27
+ async get(key) {
28
+ return this.storage[key] || null;
29
+ }
30
+ async set(key, value) {
31
+ this.storage[key] = value;
32
+ }
33
+ }
34
+ let storageAdapter = new ExtensionStorageAdapter();
35
+ /**
36
+ * 스토리지 어댑터 설정 (라이브러리 사용 시 필수)
37
+ */
38
+ export function setStorageAdapter(adapter) {
39
+ storageAdapter = adapter;
40
+ }
41
+ /**
42
+ * Flow 가져오기
43
+ */
44
+ export async function getFlow() {
45
+ try {
46
+ const flow = await storageAdapter.get(FLOW_STORAGE_KEY);
47
+ return flow || null;
48
+ }
49
+ catch (error) {
50
+ console.error("Failed to get flow from storage:", error);
51
+ return null;
52
+ }
53
+ }
54
+ /**
55
+ * Flow 저장하기
56
+ */
57
+ export async function saveFlow(flow) {
58
+ try {
59
+ await storageAdapter.set(FLOW_STORAGE_KEY, flow);
60
+ }
61
+ catch (error) {
62
+ console.error("Failed to save flow to storage:", error);
63
+ throw error;
64
+ }
65
+ }
66
+ /**
67
+ * Flow 초기화 (비우기)
68
+ */
69
+ export async function clearFlow() {
70
+ try {
71
+ const emptyFlow = {
72
+ id: crypto.randomUUID(),
73
+ title: "New Flow",
74
+ steps: [],
75
+ createdAt: Date.now(),
76
+ };
77
+ await saveFlow(emptyFlow);
78
+ }
79
+ catch (error) {
80
+ console.error("Failed to clear flow:", error);
81
+ throw error;
82
+ }
83
+ }
84
+ /**
85
+ * Flow에 Step 추가
86
+ */
87
+ export async function addStep(step) {
88
+ const flow = (await getFlow()) || {
89
+ id: crypto.randomUUID(),
90
+ title: "New Flow",
91
+ steps: [],
92
+ createdAt: Date.now(),
93
+ };
94
+ flow.steps.push(step);
95
+ if (!flow.startUrl && step.url) {
96
+ flow.startUrl = step.url;
97
+ }
98
+ await saveFlow(flow);
99
+ return flow;
100
+ }
101
+ /**
102
+ * Flow에서 마지막 Step 제거
103
+ */
104
+ export async function removeLastStep() {
105
+ const flow = await getFlow();
106
+ if (!flow || flow.steps.length === 0) {
107
+ return {
108
+ id: crypto.randomUUID(),
109
+ title: "New Flow",
110
+ steps: [],
111
+ createdAt: Date.now(),
112
+ };
113
+ }
114
+ flow.steps.pop();
115
+ await saveFlow(flow);
116
+ return flow;
117
+ }
118
+ /**
119
+ * Flow에서 특정 Step 제거
120
+ */
121
+ export async function removeStep(index) {
122
+ const flow = await getFlow();
123
+ if (!flow || index < 0 || index >= flow.steps.length) {
124
+ throw new Error(`Invalid step index: ${index}`);
125
+ }
126
+ flow.steps.splice(index, 1);
127
+ await saveFlow(flow);
128
+ return flow;
129
+ }
130
+ /**
131
+ * Flow의 특정 Step 업데이트
132
+ */
133
+ export async function updateStep(index, step) {
134
+ const flow = await getFlow();
135
+ if (!flow || index < 0 || index >= flow.steps.length) {
136
+ throw new Error(`Invalid step index: ${index}`);
137
+ }
138
+ flow.steps[index] = step;
139
+ await saveFlow(flow);
140
+ return flow;
141
+ }
142
+ /**
143
+ * Flow 업데이트 (전체 교체)
144
+ */
145
+ export async function updateFlow(updates) {
146
+ const flow = (await getFlow()) || {
147
+ id: crypto.randomUUID(),
148
+ title: "New Flow",
149
+ steps: [],
150
+ createdAt: Date.now(),
151
+ };
152
+ const updatedFlow = { ...flow, ...updates };
153
+ await saveFlow(updatedFlow);
154
+ return updatedFlow;
155
+ }
156
+ // Export adapters for external use/testing
157
+ export { ExtensionStorageAdapter, MemoryStorageAdapter };
@@ -0,0 +1,163 @@
1
+ /**
2
+ * ElementLocator: 다중 selector 전략 (Playwright/Maestro 스타일)
3
+ * 우선순위 기반으로 여러 selector를 시도하여 안정성 향상
4
+ */
5
+ export interface ElementLocator {
6
+ /** Primary selector (가장 신뢰할 수 있는) */
7
+ primary: string;
8
+ /** Fallback selectors (우선순위 순서대로) */
9
+ fallbacks: string[];
10
+ /** 요소 메타데이터 (fuzzy matching용) */
11
+ metadata?: {
12
+ text?: string;
13
+ role?: string;
14
+ tagName?: string;
15
+ testId?: string;
16
+ ariaLabel?: string;
17
+ placeholder?: string;
18
+ title?: string;
19
+ };
20
+ }
21
+ type CoreStep = {
22
+ type: "click";
23
+ selector: string;
24
+ locator?: ElementLocator;
25
+ url?: string;
26
+ screenshot?: string;
27
+ timeoutMs?: number;
28
+ } | {
29
+ type: "type";
30
+ selector: string;
31
+ locator?: ElementLocator;
32
+ text: string;
33
+ originalText?: string;
34
+ submit?: boolean;
35
+ url?: string;
36
+ screenshot?: string;
37
+ timeoutMs?: number;
38
+ } | {
39
+ type: "select";
40
+ selector: string;
41
+ locator?: ElementLocator;
42
+ value: string;
43
+ url?: string;
44
+ screenshot?: string;
45
+ timeoutMs?: number;
46
+ } | {
47
+ type: "extract";
48
+ selector: string;
49
+ locator?: ElementLocator;
50
+ prop?: "innerText" | "value";
51
+ url?: string;
52
+ screenshot?: string;
53
+ timeoutMs?: number;
54
+ } | {
55
+ type: "waitFor";
56
+ selector?: string;
57
+ locator?: ElementLocator;
58
+ timeoutMs?: number;
59
+ url?: string;
60
+ screenshot?: string;
61
+ } | {
62
+ type: "screenshot";
63
+ selector: string;
64
+ locator?: ElementLocator;
65
+ url?: string;
66
+ screenshot: string;
67
+ timeoutMs?: number;
68
+ } | {
69
+ type: "navigate";
70
+ url: string;
71
+ } | {
72
+ type: "waitForNavigation";
73
+ timeoutMs?: number;
74
+ };
75
+ export type Step = CoreStep & {
76
+ _frameId?: number;
77
+ _frameUrl?: string;
78
+ };
79
+ export interface Flow {
80
+ id: string;
81
+ title: string;
82
+ steps: Step[];
83
+ createdAt: number;
84
+ startUrl?: string;
85
+ }
86
+ export type RecordStepMessage = {
87
+ type: "REC_STEP";
88
+ step: Step;
89
+ };
90
+ export type TogglePickerMessage = {
91
+ type: "TOGGLE_PICKER";
92
+ on: boolean;
93
+ };
94
+ export type RunFlowMessage = {
95
+ type: "RUN_FLOW";
96
+ };
97
+ export type SendToBackendMessage = {
98
+ type: "SEND_TO_BACKEND";
99
+ endpoint: string;
100
+ };
101
+ export type FlowUpdatedMessage = {
102
+ type: "FLOW_UPDATED";
103
+ flow: Flow;
104
+ };
105
+ export type SentOkMessage = {
106
+ type: "SENT_OK";
107
+ };
108
+ export type StepExecutingMessage = {
109
+ type: "STEP_EXECUTING";
110
+ step: Step;
111
+ stepIndex: number;
112
+ totalSteps: number;
113
+ currentUrl?: string;
114
+ };
115
+ export type StepCompletedMessage = {
116
+ type: "STEP_COMPLETED";
117
+ step: Step;
118
+ stepIndex: number;
119
+ success: boolean;
120
+ error?: string;
121
+ extractedData?: any;
122
+ };
123
+ export type FlowFailedMessage = {
124
+ type: "FLOW_FAILED";
125
+ error: string;
126
+ failedStepIndex: number;
127
+ failedStep: Step;
128
+ };
129
+ export type ElementScreenshotMessage = {
130
+ type: "ELEMENT_SCREENSHOT";
131
+ stepIndex: number;
132
+ screenshot: string;
133
+ elementInfo: {
134
+ tagName: string;
135
+ selector: string;
136
+ text?: string;
137
+ };
138
+ };
139
+ export type StartRecordMessage = {
140
+ type: "START_RECORD";
141
+ };
142
+ export type StopRecordMessage = {
143
+ type: "STOP_RECORD";
144
+ };
145
+ export type StopRunMessage = {
146
+ type: "STOP_RUN";
147
+ };
148
+ export type PlayEventsToContentMessage = {
149
+ type: "PLAY_EVENTS";
150
+ events: any[];
151
+ };
152
+ export type RecordStateUpdatedMessage = {
153
+ type: "RECORD_STATE";
154
+ recording: boolean;
155
+ };
156
+ export type GetRecordStateMessage = {
157
+ type: "GET_RECORD_STATE";
158
+ };
159
+ export type UndoLastStepMessage = {
160
+ type: "UNDO_LAST_STEP";
161
+ };
162
+ export type Message = RecordStepMessage | TogglePickerMessage | RunFlowMessage | SendToBackendMessage | FlowUpdatedMessage | SentOkMessage | StepExecutingMessage | StepCompletedMessage | FlowFailedMessage | ElementScreenshotMessage | StartRecordMessage | StopRecordMessage | StopRunMessage | PlayEventsToContentMessage | RecordStateUpdatedMessage | GetRecordStateMessage | UndoLastStepMessage;
163
+ export {};
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,49 @@
1
+ /**
2
+ * URL 유틸리티 함수들
3
+ * URL 비교, 정규화, 파싱 등의 기능 제공
4
+ */
5
+ /**
6
+ * URL 정규화
7
+ * - 트레일링 슬래시 제거
8
+ * - 해시(fragment) 제거
9
+ * - 쿼리 파라미터 정렬
10
+ */
11
+ export declare function normalizeUrl(url: string): string;
12
+ /**
13
+ * 두 URL이 같은 페이지를 가리키는지 확인
14
+ * (해시와 쿼리 파라미터 순서 무시)
15
+ */
16
+ export declare function isSameUrl(url1: string, url2: string): boolean;
17
+ /**
18
+ * 두 URL의 origin이 같은지 확인
19
+ */
20
+ export declare function isSameOrigin(url1: string, url2: string): boolean;
21
+ /**
22
+ * 상대 URL을 절대 URL로 변환
23
+ */
24
+ export declare function resolveUrl(relativeUrl: string, baseUrl: string): string;
25
+ /**
26
+ * URL에서 도메인 추출
27
+ */
28
+ export declare function getDomain(url: string): string;
29
+ /**
30
+ * URL에서 경로 추출
31
+ */
32
+ export declare function getPath(url: string): string;
33
+ /**
34
+ * URL에서 쿼리 파라미터 추출
35
+ */
36
+ export declare function getQueryParams(url: string): Record<string, string>;
37
+ /**
38
+ * URL이 유효한지 검증
39
+ */
40
+ export declare function isValidUrl(url: string): boolean;
41
+ /**
42
+ * HTTP/HTTPS URL인지 확인
43
+ */
44
+ export declare function isHttpUrl(url: string): boolean;
45
+ /**
46
+ * URL이 현재 페이지와 같은 페이지인지 확인
47
+ * (해시만 다른 경우 같은 페이지로 간주)
48
+ */
49
+ export declare function isSamePage(url1: string, url2: string): boolean;
@@ -0,0 +1,144 @@
1
+ /**
2
+ * URL 유틸리티 함수들
3
+ * URL 비교, 정규화, 파싱 등의 기능 제공
4
+ */
5
+ /**
6
+ * URL 정규화
7
+ * - 트레일링 슬래시 제거
8
+ * - 해시(fragment) 제거
9
+ * - 쿼리 파라미터 정렬
10
+ */
11
+ export function normalizeUrl(url) {
12
+ try {
13
+ const urlObj = new URL(url);
14
+ // 트레일링 슬래시 제거 (루트 경로 제외)
15
+ if (urlObj.pathname !== "/" && urlObj.pathname.endsWith("/")) {
16
+ urlObj.pathname = urlObj.pathname.slice(0, -1);
17
+ }
18
+ // 쿼리 파라미터 정렬
19
+ const params = Array.from(urlObj.searchParams.entries()).sort(([a], [b]) => a.localeCompare(b));
20
+ urlObj.search = new URLSearchParams(params).toString();
21
+ // 해시 제거 (fragment는 페이지 내 이동만 하므로)
22
+ urlObj.hash = "";
23
+ return urlObj.toString();
24
+ }
25
+ catch (error) {
26
+ console.error("Invalid URL:", url, error);
27
+ return url;
28
+ }
29
+ }
30
+ /**
31
+ * 두 URL이 같은 페이지를 가리키는지 확인
32
+ * (해시와 쿼리 파라미터 순서 무시)
33
+ */
34
+ export function isSameUrl(url1, url2) {
35
+ try {
36
+ return normalizeUrl(url1) === normalizeUrl(url2);
37
+ }
38
+ catch (error) {
39
+ return url1 === url2;
40
+ }
41
+ }
42
+ /**
43
+ * 두 URL의 origin이 같은지 확인
44
+ */
45
+ export function isSameOrigin(url1, url2) {
46
+ try {
47
+ const origin1 = new URL(url1).origin;
48
+ const origin2 = new URL(url2).origin;
49
+ return origin1 === origin2;
50
+ }
51
+ catch (error) {
52
+ return false;
53
+ }
54
+ }
55
+ /**
56
+ * 상대 URL을 절대 URL로 변환
57
+ */
58
+ export function resolveUrl(relativeUrl, baseUrl) {
59
+ try {
60
+ return new URL(relativeUrl, baseUrl).toString();
61
+ }
62
+ catch (error) {
63
+ console.error("Failed to resolve URL:", relativeUrl, baseUrl, error);
64
+ return relativeUrl;
65
+ }
66
+ }
67
+ /**
68
+ * URL에서 도메인 추출
69
+ */
70
+ export function getDomain(url) {
71
+ try {
72
+ return new URL(url).hostname;
73
+ }
74
+ catch (error) {
75
+ return "";
76
+ }
77
+ }
78
+ /**
79
+ * URL에서 경로 추출
80
+ */
81
+ export function getPath(url) {
82
+ try {
83
+ return new URL(url).pathname;
84
+ }
85
+ catch (error) {
86
+ return "";
87
+ }
88
+ }
89
+ /**
90
+ * URL에서 쿼리 파라미터 추출
91
+ */
92
+ export function getQueryParams(url) {
93
+ try {
94
+ const urlObj = new URL(url);
95
+ const params = {};
96
+ urlObj.searchParams.forEach((value, key) => {
97
+ params[key] = value;
98
+ });
99
+ return params;
100
+ }
101
+ catch (error) {
102
+ return {};
103
+ }
104
+ }
105
+ /**
106
+ * URL이 유효한지 검증
107
+ */
108
+ export function isValidUrl(url) {
109
+ try {
110
+ new URL(url);
111
+ return true;
112
+ }
113
+ catch {
114
+ return false;
115
+ }
116
+ }
117
+ /**
118
+ * HTTP/HTTPS URL인지 확인
119
+ */
120
+ export function isHttpUrl(url) {
121
+ try {
122
+ const protocol = new URL(url).protocol;
123
+ return protocol === "http:" || protocol === "https:";
124
+ }
125
+ catch {
126
+ return false;
127
+ }
128
+ }
129
+ /**
130
+ * URL이 현재 페이지와 같은 페이지인지 확인
131
+ * (해시만 다른 경우 같은 페이지로 간주)
132
+ */
133
+ export function isSamePage(url1, url2) {
134
+ try {
135
+ const obj1 = new URL(url1);
136
+ const obj2 = new URL(url2);
137
+ return (obj1.origin === obj2.origin &&
138
+ obj1.pathname === obj2.pathname &&
139
+ obj1.search === obj2.search);
140
+ }
141
+ catch {
142
+ return false;
143
+ }
144
+ }
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@auto-wiz/core",
3
+ "version": "1.0.0",
4
+ "license": "MIT",
5
+ "author": "JaeSang",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/JaeSang1998/automation-wizard.git"
9
+ },
10
+ "publishConfig": {
11
+ "access": "public"
12
+ },
13
+ "files": [
14
+ "dist"
15
+ ],
16
+ "main": "./dist/index.js",
17
+ "types": "./dist/index.d.ts",
18
+ "exports": {
19
+ ".": {
20
+ "types": "./dist/index.d.ts",
21
+ "import": "./dist/index.js",
22
+ "require": "./dist/index.js"
23
+ }
24
+ },
25
+ "dependencies": {},
26
+ "devDependencies": {
27
+ "typescript": "^5.0.0",
28
+ "wxt": "^0.19.0"
29
+ },
30
+ "scripts": {
31
+ "build": "tsc"
32
+ }
33
+ }