arcy.js 0.1.0 → 0.1.2
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/arcy.chat.39921845e233.js +1271 -0
- package/dist/arcy.legacy.7037dec7a67f.js +568 -0
- package/dist/arcy.loader.js +1 -1
- package/dist/arcy.modern.0ffea3108a8d.js +568 -0
- package/dist/arcy.picker.73f47221b73c.js +59 -0
- package/dist/chat-IU4IJMGO.js +3860 -0
- package/dist/chunk-7VVKR3AO.js +122 -0
- package/dist/chunk-CQ2DESAJ.js +263 -0
- package/dist/chunk-DKYMIAYF.js +7 -0
- package/dist/chunk-DW3GUQ6J.js +234 -0
- package/dist/chunk-FR6SJSDU.js +220 -0
- package/dist/chunk-LGWYJSYX.js +271 -0
- package/dist/chunk-NIHUDSWK.js +155 -0
- package/dist/chunk-NY3NXM2V.js +213 -0
- package/dist/design-mode.js +2 -127
- package/dist/flow-M7W3H3C5.js +1422 -0
- package/dist/index.cjs +9915 -1158
- package/dist/index.d.ts +44 -8
- package/dist/index.js +799 -1157
- package/dist/picker-JPESE6JJ.js +2134 -0
- package/package.json +1 -1
- package/dist/arcy.chat.ab181cf5a84b.js +0 -1258
- package/dist/arcy.legacy.ab4e9266b657.js +0 -567
- package/dist/arcy.modern.60faeffc62a2.js +0 -567
- package/dist/arcy.picker.c598a581d964.js +0 -59
- package/dist/design-mode.d.cts +0 -197
- package/dist/index.d.cts +0 -133
- package/dist/snippet.d.cts +0 -39
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
/* arcy.js — https://arcyai.com */
|
|
2
|
+
|
|
3
|
+
// src/flow/flow-contract.ts
|
|
4
|
+
var FLOW_GLOBAL = "__arcyFlow";
|
|
5
|
+
var FLOW_CONTRACT = 2;
|
|
6
|
+
|
|
7
|
+
// src/telemetry/storage.ts
|
|
8
|
+
var NULL_STORE = {
|
|
9
|
+
getItem: () => null,
|
|
10
|
+
setItem: () => {
|
|
11
|
+
},
|
|
12
|
+
removeItem: () => {
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
function safeStore(raw) {
|
|
16
|
+
if (!raw) return NULL_STORE;
|
|
17
|
+
return {
|
|
18
|
+
getItem(key) {
|
|
19
|
+
try {
|
|
20
|
+
return raw.getItem(key);
|
|
21
|
+
} catch {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
setItem(key, value) {
|
|
26
|
+
try {
|
|
27
|
+
raw.setItem(key, value);
|
|
28
|
+
} catch {
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
removeItem(key) {
|
|
32
|
+
try {
|
|
33
|
+
raw.removeItem(key);
|
|
34
|
+
} catch {
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function browserLocalStorage() {
|
|
40
|
+
try {
|
|
41
|
+
const candidate = globalThis.localStorage;
|
|
42
|
+
return safeStore(candidate);
|
|
43
|
+
} catch {
|
|
44
|
+
return NULL_STORE;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function browserSessionStorage() {
|
|
48
|
+
try {
|
|
49
|
+
const candidate = globalThis.sessionStorage;
|
|
50
|
+
return safeStore(candidate);
|
|
51
|
+
} catch {
|
|
52
|
+
return NULL_STORE;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function readJson(store, key) {
|
|
56
|
+
const raw = store.getItem(key);
|
|
57
|
+
if (!raw) return null;
|
|
58
|
+
try {
|
|
59
|
+
const parsed = JSON.parse(raw);
|
|
60
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
return parsed;
|
|
64
|
+
} catch {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
function writeJson(store, key, value) {
|
|
69
|
+
try {
|
|
70
|
+
store.setItem(key, JSON.stringify(value));
|
|
71
|
+
} catch {
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// src/flow/preview-state.ts
|
|
76
|
+
var PREVIEW_KEY_PREFIX = "arcy.preview.";
|
|
77
|
+
function isValid(stored) {
|
|
78
|
+
return typeof stored.flowId === "string" && stored.flowId.length > 0 && typeof stored.currentStepCvid === "string" && stored.currentStepCvid.length > 0 && typeof stored.flowSessionId === "string" && stored.flowSessionId.length > 0 && typeof stored.startedAt === "number" && stored.flow !== void 0 && stored.flow !== null;
|
|
79
|
+
}
|
|
80
|
+
function createPreviewRunState(token, { store: raw = browserSessionStorage() } = {}) {
|
|
81
|
+
const store = safeStore(raw);
|
|
82
|
+
const key = PREVIEW_KEY_PREFIX + token;
|
|
83
|
+
return {
|
|
84
|
+
read() {
|
|
85
|
+
const stored = readJson(store, key);
|
|
86
|
+
if (!stored || !isValid(stored)) return null;
|
|
87
|
+
return stored;
|
|
88
|
+
},
|
|
89
|
+
save(run) {
|
|
90
|
+
writeJson(store, key, run);
|
|
91
|
+
},
|
|
92
|
+
clear() {
|
|
93
|
+
store.removeItem(key);
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
function hasPendingPreview(token, store = browserSessionStorage()) {
|
|
98
|
+
try {
|
|
99
|
+
return safeStore(store).getItem(PREVIEW_KEY_PREFIX + token) !== null;
|
|
100
|
+
} catch {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// src/flow/state.ts
|
|
106
|
+
var FLOW_KEY_PREFIX = "arcy.flow.";
|
|
107
|
+
function isValid2(stored) {
|
|
108
|
+
return typeof stored.flowId === "string" && stored.flowId.length > 0 && typeof stored.currentStepCvid === "string" && stored.currentStepCvid.length > 0 && typeof stored.flowSessionId === "string" && stored.flowSessionId.length > 0 && typeof stored.startedAt === "number";
|
|
109
|
+
}
|
|
110
|
+
function createFlowRunState(token, { store: raw = browserLocalStorage() } = {}) {
|
|
111
|
+
const store = safeStore(raw);
|
|
112
|
+
const key = FLOW_KEY_PREFIX + token;
|
|
113
|
+
return {
|
|
114
|
+
read() {
|
|
115
|
+
const stored = readJson(store, key);
|
|
116
|
+
if (!stored || !isValid2(stored)) return null;
|
|
117
|
+
return {
|
|
118
|
+
flowId: stored.flowId,
|
|
119
|
+
currentStepCvid: stored.currentStepCvid,
|
|
120
|
+
flowSessionId: stored.flowSessionId,
|
|
121
|
+
startedAt: stored.startedAt
|
|
122
|
+
};
|
|
123
|
+
},
|
|
124
|
+
save(run) {
|
|
125
|
+
writeJson(store, key, run);
|
|
126
|
+
},
|
|
127
|
+
clear() {
|
|
128
|
+
store.removeItem(key);
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// src/shell/state.ts
|
|
134
|
+
var SHELL_KEY_PREFIX = "arcy.shell.";
|
|
135
|
+
var UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
|
|
136
|
+
var LOCALE_PATTERN = /^[a-z]{2}-[A-Z]{2}$/;
|
|
137
|
+
function readPosition(value) {
|
|
138
|
+
if (!value || typeof value !== "object") return null;
|
|
139
|
+
const { x, y } = value;
|
|
140
|
+
if (typeof x !== "number" || typeof y !== "number") return null;
|
|
141
|
+
if (!Number.isFinite(x) || !Number.isFinite(y)) return null;
|
|
142
|
+
return { x, y };
|
|
143
|
+
}
|
|
144
|
+
function uuidV4(fill) {
|
|
145
|
+
const source = typeof globalThis !== "undefined" ? globalThis.crypto : void 0;
|
|
146
|
+
if (!fill && source && typeof source.randomUUID === "function") {
|
|
147
|
+
try {
|
|
148
|
+
const native = source.randomUUID();
|
|
149
|
+
if (UUID_PATTERN.test(native)) return native;
|
|
150
|
+
} catch {
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
const bytes = new Uint8Array(16);
|
|
154
|
+
if (fill) {
|
|
155
|
+
fill(bytes);
|
|
156
|
+
} else if (source && typeof source.getRandomValues === "function") {
|
|
157
|
+
source.getRandomValues(bytes);
|
|
158
|
+
} else {
|
|
159
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
160
|
+
bytes[i] = Math.floor(Math.random() * 256);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
bytes[6] = (bytes[6] ?? 0) & 15 | 64;
|
|
164
|
+
bytes[8] = (bytes[8] ?? 0) & 63 | 128;
|
|
165
|
+
let hex = "";
|
|
166
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
167
|
+
hex += (bytes[i] ?? 0).toString(16).padStart(2, "0");
|
|
168
|
+
}
|
|
169
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
170
|
+
}
|
|
171
|
+
function createShellState(token, { store: raw = browserLocalStorage(), fill } = {}) {
|
|
172
|
+
const store = safeStore(raw);
|
|
173
|
+
const key = SHELL_KEY_PREFIX + token;
|
|
174
|
+
function readRaw() {
|
|
175
|
+
return readJson(store, key) ?? {};
|
|
176
|
+
}
|
|
177
|
+
function patch(next) {
|
|
178
|
+
writeJson(store, key, { ...readRaw(), ...next });
|
|
179
|
+
}
|
|
180
|
+
function read() {
|
|
181
|
+
const stored = readRaw();
|
|
182
|
+
const id = stored.anonymousId;
|
|
183
|
+
return {
|
|
184
|
+
anonymousId: typeof id === "string" && UUID_PATTERN.test(id) ? id : null,
|
|
185
|
+
position: readPosition(stored.position),
|
|
186
|
+
// Anything but a literal `true` means not docked. The value is in the
|
|
187
|
+
// visitor's own storage, so a truthy string written by an older build
|
|
188
|
+
// must not be able to hide the widget.
|
|
189
|
+
docked: stored.docked === true,
|
|
190
|
+
locale: typeof stored.locale === "string" && LOCALE_PATTERN.test(stored.locale) ? stored.locale : null
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
return {
|
|
194
|
+
read,
|
|
195
|
+
anonymousId() {
|
|
196
|
+
const existing = read().anonymousId;
|
|
197
|
+
if (existing) return existing;
|
|
198
|
+
patch({ anonymousId: uuidV4(fill) });
|
|
199
|
+
return read().anonymousId;
|
|
200
|
+
},
|
|
201
|
+
rememberPosition(position) {
|
|
202
|
+
patch({ position });
|
|
203
|
+
},
|
|
204
|
+
rememberDocked(docked) {
|
|
205
|
+
patch({ docked });
|
|
206
|
+
},
|
|
207
|
+
rememberLocale(locale) {
|
|
208
|
+
patch({ locale });
|
|
209
|
+
},
|
|
210
|
+
forgetAnonymousId() {
|
|
211
|
+
const current = readRaw();
|
|
212
|
+
if (current.anonymousId === void 0) return;
|
|
213
|
+
delete current.anonymousId;
|
|
214
|
+
if (Object.keys(current).length === 0) store.removeItem(key);
|
|
215
|
+
else writeJson(store, key, current);
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export { FLOW_CONTRACT, FLOW_GLOBAL, browserLocalStorage, browserSessionStorage, createFlowRunState, createPreviewRunState, createShellState, hasPendingPreview, readJson, safeStore, uuidV4, writeJson };
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
/* arcy.js — https://arcyai.com */
|
|
2
|
+
|
|
3
|
+
// src/warn.ts
|
|
4
|
+
function warn(message) {
|
|
5
|
+
if (typeof console !== "undefined" && console.warn) {
|
|
6
|
+
console.warn(`[arcy] ${message}`);
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// src/csp.ts
|
|
11
|
+
function readCurrentScriptNonce(doc) {
|
|
12
|
+
try {
|
|
13
|
+
const current = doc.currentScript;
|
|
14
|
+
const own = current?.nonce;
|
|
15
|
+
if (typeof own === "string" && own.length > 0) return own;
|
|
16
|
+
const scripts = doc.getElementsByTagName("script");
|
|
17
|
+
for (let i = 0; i < scripts.length; i++) {
|
|
18
|
+
const value = scripts[i].nonce;
|
|
19
|
+
if (typeof value === "string" && value.length > 0) return value;
|
|
20
|
+
}
|
|
21
|
+
return null;
|
|
22
|
+
} catch {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
var captured = typeof document !== "undefined" ? readCurrentScriptNonce(document) : null;
|
|
27
|
+
function currentNonce() {
|
|
28
|
+
return captured;
|
|
29
|
+
}
|
|
30
|
+
function applyNonce(element) {
|
|
31
|
+
const nonce = currentNonce();
|
|
32
|
+
if (!nonce) return;
|
|
33
|
+
try {
|
|
34
|
+
;
|
|
35
|
+
element.nonce = nonce;
|
|
36
|
+
element.setAttribute("nonce", nonce);
|
|
37
|
+
} catch {
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
var CSP_DOCS_URL = "https://docs.arcyai.com/troubleshooting#content-security-policy";
|
|
41
|
+
function originOf(value) {
|
|
42
|
+
try {
|
|
43
|
+
const { origin } = new URL(value);
|
|
44
|
+
return origin && origin !== "null" ? origin : null;
|
|
45
|
+
} catch {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function cspAdvice(options) {
|
|
50
|
+
const api = originOf(options.apiBase);
|
|
51
|
+
const cdn = options.cdnBase ? originOf(options.cdnBase) : null;
|
|
52
|
+
const lines = [
|
|
53
|
+
"This page's Content-Security-Policy is blocking part of the ARCY widget.",
|
|
54
|
+
"Add these to the policy your app sends:",
|
|
55
|
+
""
|
|
56
|
+
];
|
|
57
|
+
if (cdn) lines.push(` script-src ${cdn}`);
|
|
58
|
+
if (api) {
|
|
59
|
+
lines.push(` connect-src ${api}`);
|
|
60
|
+
lines.push(` font-src ${api}`);
|
|
61
|
+
lines.push(` img-src ${api} blob:`);
|
|
62
|
+
}
|
|
63
|
+
lines.push(" style-src 'unsafe-inline', or a nonce on the script that loads ARCY");
|
|
64
|
+
lines.push("");
|
|
65
|
+
lines.push(
|
|
66
|
+
"To allow none of them, proxy a path on your own domain to the API and pass it as arcy.init(token, { apiBase })."
|
|
67
|
+
);
|
|
68
|
+
lines.push(options.docsUrl ?? CSP_DOCS_URL);
|
|
69
|
+
return lines.join("\n");
|
|
70
|
+
}
|
|
71
|
+
function reportCspViolations(win, options) {
|
|
72
|
+
const ours = [options.apiBase, options.cdnBase].filter((value) => typeof value === "string" && value.length > 0).map(originOf).filter((value) => value !== null);
|
|
73
|
+
let stop = () => {
|
|
74
|
+
};
|
|
75
|
+
try {
|
|
76
|
+
const onViolation = (event) => {
|
|
77
|
+
const blocked = event.blockedURI;
|
|
78
|
+
const isOurs = blocked === "inline" || ours.some((origin) => blocked?.startsWith(origin));
|
|
79
|
+
if (!isOurs) return;
|
|
80
|
+
stop();
|
|
81
|
+
warn(cspAdvice(options));
|
|
82
|
+
};
|
|
83
|
+
win.document.addEventListener("securitypolicyviolation", onViolation);
|
|
84
|
+
stop = () => {
|
|
85
|
+
try {
|
|
86
|
+
win.document.removeEventListener("securitypolicyviolation", onViolation);
|
|
87
|
+
} catch {
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
} catch {
|
|
91
|
+
}
|
|
92
|
+
return () => stop();
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// src/shell/host.ts
|
|
96
|
+
var HOST_ID = "arcy-widget";
|
|
97
|
+
var HOST_ATTRIBUTE = "data-arcy";
|
|
98
|
+
var Z_INDEX = "2147483000";
|
|
99
|
+
var CRITICAL_STYLE = [
|
|
100
|
+
["position", "fixed"],
|
|
101
|
+
["inset", "0"],
|
|
102
|
+
["z-index", Z_INDEX],
|
|
103
|
+
// The layer spans the viewport so the launcher (1.4) and the chat panel (1.5)
|
|
104
|
+
// share one coordinate space. It must therefore be transparent to the
|
|
105
|
+
// pointer; each surface inside re-enables its own.
|
|
106
|
+
["pointer-events", "none"],
|
|
107
|
+
// The layer must also be invisible, and that is not the shadow root's job
|
|
108
|
+
// (D512). The host is in the customer's light DOM whatever happens inside
|
|
109
|
+
// it, so their `div { background: ... !important }` paints it, and a painted
|
|
110
|
+
// full-viewport layer is their whole site replaced by a coloured sheet with
|
|
111
|
+
// no error anywhere. These four are every property that fills or edges a
|
|
112
|
+
// box, and none of them is something a customer wants on an invisible layer.
|
|
113
|
+
["background", "transparent"],
|
|
114
|
+
["border", "0"],
|
|
115
|
+
["box-shadow", "none"],
|
|
116
|
+
["outline", "0"]
|
|
117
|
+
];
|
|
118
|
+
var RESET_STYLE = [
|
|
119
|
+
["margin", "0"],
|
|
120
|
+
["padding", "0"]
|
|
121
|
+
];
|
|
122
|
+
var STYLE_MARKER = "data-arcy-style";
|
|
123
|
+
var shells = /* @__PURE__ */ new Map();
|
|
124
|
+
function isMounted(id = HOST_ID) {
|
|
125
|
+
const shell = shells.get(id);
|
|
126
|
+
return shell !== void 0 && shell.host.isConnected;
|
|
127
|
+
}
|
|
128
|
+
function createShell(options = {}) {
|
|
129
|
+
if (typeof document === "undefined") return null;
|
|
130
|
+
const id = options.id ?? HOST_ID;
|
|
131
|
+
const container = options.container;
|
|
132
|
+
if (isMounted(id)) {
|
|
133
|
+
warn("This ARCY surface is already on this page. Ignoring the second mount.");
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
shells.delete(id);
|
|
137
|
+
const parent = container ?? document.body ?? document.documentElement;
|
|
138
|
+
if (!parent) {
|
|
139
|
+
warn("The page has no <body> to mount into yet. The widget will not render.");
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
if (!container) {
|
|
143
|
+
const hosts = document.querySelectorAll(`[${HOST_ATTRIBUTE}]`);
|
|
144
|
+
for (let i = 0; i < hosts.length; i += 1) {
|
|
145
|
+
if (hosts[i]?.id === id) {
|
|
146
|
+
warn("Another copy of ARCY is already on this page. Ignoring this one.");
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
try {
|
|
152
|
+
const host = document.createElement("div");
|
|
153
|
+
host.id = id;
|
|
154
|
+
host.setAttribute(HOST_ATTRIBUTE, "");
|
|
155
|
+
for (const [property, value] of CRITICAL_STYLE) {
|
|
156
|
+
host.style.setProperty(property, value, "important");
|
|
157
|
+
}
|
|
158
|
+
for (const [property, value] of RESET_STYLE) {
|
|
159
|
+
host.style.setProperty(property, value);
|
|
160
|
+
}
|
|
161
|
+
let root = host;
|
|
162
|
+
let isolated = false;
|
|
163
|
+
try {
|
|
164
|
+
if (host.attachShadow) {
|
|
165
|
+
root = host.attachShadow({ mode: "closed" });
|
|
166
|
+
isolated = true;
|
|
167
|
+
}
|
|
168
|
+
} catch {
|
|
169
|
+
}
|
|
170
|
+
parent.appendChild(host);
|
|
171
|
+
isolateInteractions(host);
|
|
172
|
+
const shell = {
|
|
173
|
+
host,
|
|
174
|
+
root,
|
|
175
|
+
isolated,
|
|
176
|
+
destroy() {
|
|
177
|
+
try {
|
|
178
|
+
if (host.parentNode) host.parentNode.removeChild(host);
|
|
179
|
+
} catch {
|
|
180
|
+
}
|
|
181
|
+
if (shells.get(id) === shell) shells.delete(id);
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
shells.set(id, shell);
|
|
185
|
+
return shell;
|
|
186
|
+
} catch (error) {
|
|
187
|
+
warn(`The widget could not mount. ${String(error)}`);
|
|
188
|
+
return null;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
var ISOLATED_EVENTS = [
|
|
192
|
+
"pointerdown",
|
|
193
|
+
"pointerup",
|
|
194
|
+
"pointercancel",
|
|
195
|
+
"mousedown",
|
|
196
|
+
"mouseup",
|
|
197
|
+
"click",
|
|
198
|
+
"dblclick",
|
|
199
|
+
"auxclick",
|
|
200
|
+
"contextmenu",
|
|
201
|
+
"touchstart",
|
|
202
|
+
"touchend",
|
|
203
|
+
"touchcancel",
|
|
204
|
+
"focusin",
|
|
205
|
+
"focusout",
|
|
206
|
+
"keydown",
|
|
207
|
+
"keyup",
|
|
208
|
+
"keypress",
|
|
209
|
+
"beforeinput",
|
|
210
|
+
"input",
|
|
211
|
+
"change",
|
|
212
|
+
"compositionstart",
|
|
213
|
+
"compositionupdate",
|
|
214
|
+
"compositionend",
|
|
215
|
+
"cut",
|
|
216
|
+
"copy",
|
|
217
|
+
"paste"
|
|
218
|
+
];
|
|
219
|
+
function isolateInteractions(host) {
|
|
220
|
+
const stop = (event) => {
|
|
221
|
+
try {
|
|
222
|
+
event.stopPropagation();
|
|
223
|
+
} catch {
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
for (const type of ISOLATED_EVENTS) {
|
|
227
|
+
try {
|
|
228
|
+
host.addEventListener(type, stop);
|
|
229
|
+
} catch {
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
try {
|
|
233
|
+
host.addEventListener("mousedown", (event) => {
|
|
234
|
+
if (event.defaultPrevented) return;
|
|
235
|
+
releaseHostFocus(host.ownerDocument, host);
|
|
236
|
+
});
|
|
237
|
+
} catch {
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
function releaseHostFocus(doc, host) {
|
|
241
|
+
try {
|
|
242
|
+
const active = doc.activeElement;
|
|
243
|
+
if (!active || active === host || host.contains(active)) return;
|
|
244
|
+
if (active === doc.body || active === doc.documentElement) return;
|
|
245
|
+
const blur = active.blur;
|
|
246
|
+
if (typeof blur === "function") blur.call(active);
|
|
247
|
+
} catch {
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
function injectStyles(shell, css) {
|
|
251
|
+
if (!shell.isolated) return false;
|
|
252
|
+
try {
|
|
253
|
+
const existing = shell.root.querySelector(`[${STYLE_MARKER}]`);
|
|
254
|
+
if (existing) {
|
|
255
|
+
existing.textContent = `${existing.textContent ?? ""}
|
|
256
|
+
${css}`;
|
|
257
|
+
return true;
|
|
258
|
+
}
|
|
259
|
+
const style = shell.host.ownerDocument.createElement("style");
|
|
260
|
+
applyNonce(style);
|
|
261
|
+
style.setAttribute(STYLE_MARKER, "");
|
|
262
|
+
style.textContent = css;
|
|
263
|
+
shell.root.appendChild(style);
|
|
264
|
+
return true;
|
|
265
|
+
} catch (error) {
|
|
266
|
+
warn(`The widget could not apply its styles. ${String(error)}`);
|
|
267
|
+
return false;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
export { HOST_ATTRIBUTE, Z_INDEX, applyNonce, createShell, injectStyles, releaseHostFocus, reportCspViolations, warn };
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { querySelectorAllDeep, captureEventFingerprint, containsAcrossShadow, collectElementsDeep, isInteractive, MASK } from './chunk-NY3NXM2V.js';
|
|
2
|
+
|
|
3
|
+
/* arcy.js — https://arcyai.com */
|
|
4
|
+
|
|
5
|
+
// src/fingerprint/matcher.ts
|
|
6
|
+
function isVisible(el) {
|
|
7
|
+
return el.getClientRects().length > 0;
|
|
8
|
+
}
|
|
9
|
+
var WEIGHT_TEST_ID = 50;
|
|
10
|
+
var WEIGHT_TEXT = 40;
|
|
11
|
+
var WEIGHT_TEXT_DYNAMIC = 10;
|
|
12
|
+
var WEIGHT_ARIA_LABEL = 15;
|
|
13
|
+
var WEIGHT_ID = 15;
|
|
14
|
+
var WEIGHT_TAG = 10;
|
|
15
|
+
var WEIGHT_PLACEHOLDER = 10;
|
|
16
|
+
var WEIGHT_ANCESTOR = 10;
|
|
17
|
+
var WEIGHT_ROLE = 5;
|
|
18
|
+
var WEIGHT_SIBLING_INDEX = 5;
|
|
19
|
+
var TEXT_CONTAINMENT_CREDIT = 0.6;
|
|
20
|
+
var ANCESTOR_PARTIAL_CREDIT = 0.7;
|
|
21
|
+
var PRECISION_THRESHOLDS = {
|
|
22
|
+
1: 0.3,
|
|
23
|
+
2: 0.45,
|
|
24
|
+
3: 0.6,
|
|
25
|
+
4: 0.75,
|
|
26
|
+
5: 0.9
|
|
27
|
+
};
|
|
28
|
+
function demask(value) {
|
|
29
|
+
return value.split(MASK).join(" ").replace(/\s+/g, " ").trim();
|
|
30
|
+
}
|
|
31
|
+
function textSignal(stored, live) {
|
|
32
|
+
if (live === void 0) return 0;
|
|
33
|
+
const storedText = demask(stored);
|
|
34
|
+
const liveText = demask(live);
|
|
35
|
+
if (storedText === "" || liveText === "") return 0;
|
|
36
|
+
if (liveText === storedText) return 1;
|
|
37
|
+
if (liveText.includes(storedText) || storedText.includes(liveText))
|
|
38
|
+
return TEXT_CONTAINMENT_CREDIT;
|
|
39
|
+
return 0;
|
|
40
|
+
}
|
|
41
|
+
function exactSignal(stored, live) {
|
|
42
|
+
return live === stored ? 1 : 0;
|
|
43
|
+
}
|
|
44
|
+
function isMaskOnly(value) {
|
|
45
|
+
return value.includes(MASK) && demask(value) === "";
|
|
46
|
+
}
|
|
47
|
+
function scoreFingerprint(stored, live, dynamicText) {
|
|
48
|
+
let weightSum = 0;
|
|
49
|
+
let scoreSum = 0;
|
|
50
|
+
const add = (weight, signal) => {
|
|
51
|
+
weightSum += weight;
|
|
52
|
+
scoreSum += weight * signal;
|
|
53
|
+
};
|
|
54
|
+
if (stored.testId !== void 0)
|
|
55
|
+
add(WEIGHT_TEST_ID, exactSignal(stored.testId, live.testId));
|
|
56
|
+
if (stored.text !== void 0 && demask(stored.text) !== "") {
|
|
57
|
+
add(
|
|
58
|
+
dynamicText ? WEIGHT_TEXT_DYNAMIC : WEIGHT_TEXT,
|
|
59
|
+
textSignal(stored.text, live.text)
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
if (stored.ariaLabel !== void 0 && !isMaskOnly(stored.ariaLabel))
|
|
63
|
+
add(WEIGHT_ARIA_LABEL, exactSignal(stored.ariaLabel, live.ariaLabel));
|
|
64
|
+
if (stored.placeholder !== void 0 && !isMaskOnly(stored.placeholder)) {
|
|
65
|
+
add(WEIGHT_PLACEHOLDER, exactSignal(stored.placeholder, live.placeholder));
|
|
66
|
+
}
|
|
67
|
+
if (stored.id !== void 0) add(WEIGHT_ID, exactSignal(stored.id, live.id));
|
|
68
|
+
if (stored.role !== void 0)
|
|
69
|
+
add(WEIGHT_ROLE, exactSignal(stored.role, live.role));
|
|
70
|
+
add(WEIGHT_TAG, live.tag === stored.tag ? 1 : 0);
|
|
71
|
+
add(WEIGHT_SIBLING_INDEX, live.siblingIndex === stored.siblingIndex ? 1 : 0);
|
|
72
|
+
if (stored.ancestorAttr !== void 0 && stored.ancestorValue !== void 0 && !isMaskOnly(stored.ancestorValue)) {
|
|
73
|
+
let signal = 0;
|
|
74
|
+
if (live.ancestorAttr === stored.ancestorAttr && live.ancestorValue === stored.ancestorValue) {
|
|
75
|
+
signal = live.ancestorTag === stored.ancestorTag ? 1 : ANCESTOR_PARTIAL_CREDIT;
|
|
76
|
+
}
|
|
77
|
+
add(WEIGHT_ANCESTOR, signal);
|
|
78
|
+
}
|
|
79
|
+
return weightSum === 0 ? 0 : scoreSum / weightSum;
|
|
80
|
+
}
|
|
81
|
+
function selectorPool(target, doc) {
|
|
82
|
+
if (target.selector === null) return null;
|
|
83
|
+
try {
|
|
84
|
+
return querySelectorAllDeep(doc, target.selector);
|
|
85
|
+
} catch {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function scanPool(target, doc) {
|
|
90
|
+
const storedTag = target.core.tag;
|
|
91
|
+
const pool = [];
|
|
92
|
+
for (const el of collectElementsDeep(doc)) {
|
|
93
|
+
if (el.tagName.toLowerCase() === storedTag || isInteractive(el))
|
|
94
|
+
pool.push(el);
|
|
95
|
+
}
|
|
96
|
+
return pool;
|
|
97
|
+
}
|
|
98
|
+
function evaluatePool(target, pool) {
|
|
99
|
+
if (pool.length === 0)
|
|
100
|
+
return { status: "not_found", reason: "no_candidates", matchCount: 0 };
|
|
101
|
+
const threshold = PRECISION_THRESHOLDS[target.precision];
|
|
102
|
+
let sawVisible = false;
|
|
103
|
+
let passing = [];
|
|
104
|
+
for (const el of pool) {
|
|
105
|
+
if (!isVisible(el)) continue;
|
|
106
|
+
sawVisible = true;
|
|
107
|
+
const score = scoreFingerprint(
|
|
108
|
+
target.core,
|
|
109
|
+
captureEventFingerprint(el),
|
|
110
|
+
target.dynamicText
|
|
111
|
+
);
|
|
112
|
+
if (score >= threshold) passing.push({ element: el, score });
|
|
113
|
+
}
|
|
114
|
+
passing = passing.filter((p, i) => {
|
|
115
|
+
const next = passing[i + 1];
|
|
116
|
+
if (next === void 0) return true;
|
|
117
|
+
return !(containsAcrossShadow(p.element, next.element) && p.score <= next.score);
|
|
118
|
+
});
|
|
119
|
+
if (passing.length === 0) {
|
|
120
|
+
return {
|
|
121
|
+
status: "not_found",
|
|
122
|
+
reason: sawVisible ? "below_threshold" : "not_visible",
|
|
123
|
+
matchCount: 0
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
const picked = target.ordinal >= 1 ? passing[target.ordinal - 1] : void 0;
|
|
127
|
+
if (picked === void 0) {
|
|
128
|
+
return {
|
|
129
|
+
status: "not_found",
|
|
130
|
+
reason: "ordinal_out_of_range",
|
|
131
|
+
matchCount: passing.length
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
return {
|
|
135
|
+
status: "matched",
|
|
136
|
+
element: picked.element,
|
|
137
|
+
score: picked.score,
|
|
138
|
+
matchCount: passing.length
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
function matchTarget(target, doc) {
|
|
142
|
+
try {
|
|
143
|
+
const fromSelector = selectorPool(target, doc);
|
|
144
|
+
if (fromSelector !== null && fromSelector.length > 0) {
|
|
145
|
+
const result = evaluatePool(target, fromSelector);
|
|
146
|
+
if (result.status === "matched" || result.reason === "ordinal_out_of_range")
|
|
147
|
+
return result;
|
|
148
|
+
}
|
|
149
|
+
return evaluatePool(target, scanPool(target, doc));
|
|
150
|
+
} catch {
|
|
151
|
+
return { status: "not_found", reason: "error", matchCount: 0 };
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export { matchTarget };
|