arcy.js 0.1.0 → 0.1.1
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.3968644db103.js +1271 -0
- package/dist/arcy.legacy.b4005932a6cc.js +568 -0
- package/dist/arcy.loader.js +1 -1
- package/dist/arcy.modern.a51b9a32dd28.js +568 -0
- package/dist/arcy.picker.73f47221b73c.js +59 -0
- package/dist/chat-BK57WIAA.js +3867 -0
- package/dist/chunk-6HBSGVRN.js +219 -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-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 +9940 -1206
- package/dist/index.d.ts +44 -8
- package/dist/index.js +770 -1136
- 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
package/dist/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
import { DESIGN_MODE_PROTOCOL, DESIGN_MODE_EXIT, DESIGN_MODE_SCREENSHOT, DESIGN_MODE_PICK, DESIGN_MODE_READY } from './chunk-7VVKR3AO.js';
|
|
2
|
+
import { FONT_KEY_PROPERTY, FALLBACK_CHROME, readViewport, POLICY_LINK_CLASS, BAR_TRAY_CLASS, ATTACHABLE_TYPES, TOOLTIP_CSS, surfaceWidth, clampOffsetX, dragTransform, carriesFiles, droppedFiles, MOBILE_MARGIN, CHAT_CONTRACT, CHAT_GLOBAL } from './chunk-6HBSGVRN.js';
|
|
3
|
+
import { createShellState, safeStore, browserSessionStorage, writeJson, browserLocalStorage, readJson, hasPendingPreview, createFlowRunState, FLOW_CONTRACT, FLOW_GLOBAL } from './chunk-FR6SJSDU.js';
|
|
4
|
+
import { PICKER_CONTRACT, PICKER_GLOBAL } from './chunk-DKYMIAYF.js';
|
|
5
|
+
import { warn, reportCspViolations, releaseHostFocus, Z_INDEX, createShell, injectStyles } from './chunk-LGWYJSYX.js';
|
|
6
|
+
import { captureEventFingerprint, containsPii } from './chunk-NY3NXM2V.js';
|
|
2
7
|
|
|
3
|
-
|
|
4
|
-
function warn(message) {
|
|
5
|
-
if (typeof console !== "undefined" && console.warn) {
|
|
6
|
-
console.warn(`[arcy] ${message}`);
|
|
7
|
-
}
|
|
8
|
-
}
|
|
8
|
+
/* arcy.js — https://arcyai.com */
|
|
9
9
|
|
|
10
10
|
// src/emitter.ts
|
|
11
11
|
function createEmitter() {
|
|
@@ -40,149 +40,6 @@ function createEmitter() {
|
|
|
40
40
|
return { on, emit };
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
// src/fingerprint/model.ts
|
|
44
|
-
var FINGERPRINT_TEXT_MAX = 80;
|
|
45
|
-
|
|
46
|
-
// src/fingerprint/shadow.ts
|
|
47
|
-
function parentOrHost(el) {
|
|
48
|
-
if (el.parentElement !== null) return el.parentElement;
|
|
49
|
-
const parentNode = el.parentNode;
|
|
50
|
-
if (parentNode !== null && "host" in parentNode) {
|
|
51
|
-
return parentNode.host;
|
|
52
|
-
}
|
|
53
|
-
return null;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// src/fingerprint/capture.ts
|
|
57
|
-
var PII_PATTERNS = [
|
|
58
|
-
// Email
|
|
59
|
-
/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g,
|
|
60
|
-
// US SSN: 123-45-6789
|
|
61
|
-
/\b\d{3}-\d{2}-\d{4}\b/g,
|
|
62
|
-
// Card shapes: 13-19 digits, optionally grouped. A denylist, not a Luhn check.
|
|
63
|
-
/\b\d(?:[ -]?\d){12,18}\b/g
|
|
64
|
-
];
|
|
65
|
-
var MASK = "[masked]";
|
|
66
|
-
function containsPii(raw) {
|
|
67
|
-
return PII_PATTERNS.some((pattern) => {
|
|
68
|
-
pattern.lastIndex = 0;
|
|
69
|
-
return pattern.test(raw);
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
var NORMALIZE_RAW_MAX = 4096;
|
|
73
|
-
var NORMALIZE_RAW_MARGIN = 100;
|
|
74
|
-
var ROLE_MAX = 64;
|
|
75
|
-
function normalizeText(raw) {
|
|
76
|
-
if (!raw) return void 0;
|
|
77
|
-
let bounded = raw.slice(0, NORMALIZE_RAW_MAX);
|
|
78
|
-
if (raw.length > NORMALIZE_RAW_MAX) {
|
|
79
|
-
const lastSpace = bounded.search(/\s\S*$/);
|
|
80
|
-
bounded = lastSpace === -1 ? bounded.slice(0, NORMALIZE_RAW_MAX - NORMALIZE_RAW_MARGIN) : bounded.slice(0, lastSpace);
|
|
81
|
-
}
|
|
82
|
-
let text = bounded.replace(/\s+/g, " ").trim();
|
|
83
|
-
if (!text) return void 0;
|
|
84
|
-
for (const pattern of PII_PATTERNS) {
|
|
85
|
-
text = text.replace(pattern, MASK);
|
|
86
|
-
}
|
|
87
|
-
return text.slice(0, FINGERPRINT_TEXT_MAX);
|
|
88
|
-
}
|
|
89
|
-
function isStableId(id) {
|
|
90
|
-
if (id.length === 0 || id.length > 64) return false;
|
|
91
|
-
if (id.includes(":")) return false;
|
|
92
|
-
if (id.startsWith("radix-")) return false;
|
|
93
|
-
if (/^\d+$/.test(id)) return false;
|
|
94
|
-
if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(id)) return false;
|
|
95
|
-
return true;
|
|
96
|
-
}
|
|
97
|
-
var ANCESTOR_ATTRS = ["data-testid", "data-test", "data-cy", "id", "name", "aria-label"];
|
|
98
|
-
var TEST_ATTRS = ["data-testid", "data-test", "data-cy"];
|
|
99
|
-
var TEST_ID_MAX = 64;
|
|
100
|
-
function firstTestId(el) {
|
|
101
|
-
for (const attr of TEST_ATTRS) {
|
|
102
|
-
const raw = el.getAttribute(attr);
|
|
103
|
-
if (raw === null) continue;
|
|
104
|
-
const trimmed = raw.trim();
|
|
105
|
-
if (trimmed === "" || trimmed.length > TEST_ID_MAX) continue;
|
|
106
|
-
if (containsPii(trimmed)) continue;
|
|
107
|
-
return trimmed;
|
|
108
|
-
}
|
|
109
|
-
return void 0;
|
|
110
|
-
}
|
|
111
|
-
var LANDMARK_ROLES = /* @__PURE__ */ new Set([
|
|
112
|
-
"navigation",
|
|
113
|
-
"main",
|
|
114
|
-
"banner",
|
|
115
|
-
"contentinfo",
|
|
116
|
-
"search",
|
|
117
|
-
"form",
|
|
118
|
-
"region",
|
|
119
|
-
"dialog"
|
|
120
|
-
]);
|
|
121
|
-
function findSemanticAncestor(el) {
|
|
122
|
-
let current = parentOrHost(el);
|
|
123
|
-
while (current !== null) {
|
|
124
|
-
const tag = current.tagName.toLowerCase();
|
|
125
|
-
if (tag === "body" || tag === "html") return void 0;
|
|
126
|
-
for (const attr of ANCESTOR_ATTRS) {
|
|
127
|
-
const raw = current.getAttribute(attr);
|
|
128
|
-
if (raw === null) continue;
|
|
129
|
-
if (attr === "id" && !isStableId(raw)) continue;
|
|
130
|
-
const value = normalizeText(raw);
|
|
131
|
-
if (value !== void 0) return { tag, attr, value };
|
|
132
|
-
}
|
|
133
|
-
const role = current.getAttribute("role");
|
|
134
|
-
if (role !== null && LANDMARK_ROLES.has(role)) {
|
|
135
|
-
return { tag, attr: "role", value: role };
|
|
136
|
-
}
|
|
137
|
-
current = parentOrHost(current);
|
|
138
|
-
}
|
|
139
|
-
return void 0;
|
|
140
|
-
}
|
|
141
|
-
function siblingIndexOf(el) {
|
|
142
|
-
let index = 0;
|
|
143
|
-
let sibling = el.previousElementSibling;
|
|
144
|
-
while (sibling !== null) {
|
|
145
|
-
if (sibling.tagName === el.tagName) index += 1;
|
|
146
|
-
sibling = sibling.previousElementSibling;
|
|
147
|
-
}
|
|
148
|
-
return index;
|
|
149
|
-
}
|
|
150
|
-
function captureEventFingerprint(el) {
|
|
151
|
-
const fingerprint = {
|
|
152
|
-
tag: el.tagName.toLowerCase(),
|
|
153
|
-
siblingIndex: siblingIndexOf(el)
|
|
154
|
-
};
|
|
155
|
-
const text = normalizeText(el.textContent);
|
|
156
|
-
if (text !== void 0) fingerprint.text = text;
|
|
157
|
-
const ariaLabel = normalizeText(el.getAttribute("aria-label"));
|
|
158
|
-
if (ariaLabel !== void 0) fingerprint.ariaLabel = ariaLabel;
|
|
159
|
-
const placeholder = normalizeText(el.getAttribute("placeholder"));
|
|
160
|
-
if (placeholder !== void 0) fingerprint.placeholder = placeholder;
|
|
161
|
-
const id = el.getAttribute("id");
|
|
162
|
-
if (id !== null && isStableId(id) && !containsPii(id)) fingerprint.id = id;
|
|
163
|
-
const role = el.getAttribute("role");
|
|
164
|
-
if (role !== null) {
|
|
165
|
-
const trimmed = role.trim();
|
|
166
|
-
if (trimmed !== "" && trimmed.length <= ROLE_MAX && !containsPii(trimmed)) {
|
|
167
|
-
fingerprint.role = trimmed;
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
const testId = firstTestId(el);
|
|
171
|
-
if (testId !== void 0) fingerprint.testId = testId;
|
|
172
|
-
const ancestor = findSemanticAncestor(el);
|
|
173
|
-
if (ancestor !== void 0) {
|
|
174
|
-
fingerprint.ancestorTag = ancestor.tag;
|
|
175
|
-
fingerprint.ancestorAttr = ancestor.attr;
|
|
176
|
-
fingerprint.ancestorValue = ancestor.value;
|
|
177
|
-
}
|
|
178
|
-
return fingerprint;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
// src/generated/build-hashes.ts
|
|
182
|
-
var PICKER_CHUNK_FILE = "arcy.picker.c598a581d964.js";
|
|
183
|
-
var CHAT_CHUNK_FILE = "arcy.chat.ab181cf5a84b.js";
|
|
184
|
-
var FLOW_CHUNK_FILE = "arcy.flow.fef131718e42.js";
|
|
185
|
-
|
|
186
43
|
// src/telemetry/route.ts
|
|
187
44
|
var MAX_ROUTE_LENGTH = 2048;
|
|
188
45
|
var OPAQUE = /^(?:\d+|[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}|[0-9a-f]{12,})$/i;
|
|
@@ -246,192 +103,6 @@ function normalizeRoute(href, base) {
|
|
|
246
103
|
return path.length + query.length > MAX_ROUTE_LENGTH ? path : path + query;
|
|
247
104
|
}
|
|
248
105
|
|
|
249
|
-
// src/picker/contract.ts
|
|
250
|
-
var PICKER_GLOBAL = "__arcyPicker";
|
|
251
|
-
var PICKER_CONTRACT = 2;
|
|
252
|
-
|
|
253
|
-
// src/picker/message.ts
|
|
254
|
-
var DESIGN_MODE_PROTOCOL = 2;
|
|
255
|
-
var DESIGN_MODE_READY = "arcy:design-mode:ready";
|
|
256
|
-
var DESIGN_MODE_PICK = "arcy:design-mode:pick";
|
|
257
|
-
var DESIGN_MODE_EXIT = "arcy:design-mode:exit";
|
|
258
|
-
var DESIGN_MODE_SCREENSHOT = "arcy:design-mode:screenshot";
|
|
259
|
-
|
|
260
|
-
// src/shell/host.ts
|
|
261
|
-
var HOST_ID = "arcy-widget";
|
|
262
|
-
var HOST_ATTRIBUTE = "data-arcy";
|
|
263
|
-
var Z_INDEX = "2147483000";
|
|
264
|
-
var CRITICAL_STYLE = [
|
|
265
|
-
["position", "fixed"],
|
|
266
|
-
["inset", "0"],
|
|
267
|
-
["z-index", Z_INDEX],
|
|
268
|
-
// The layer spans the viewport so the launcher (1.4) and the chat panel (1.5)
|
|
269
|
-
// share one coordinate space. It must therefore be transparent to the
|
|
270
|
-
// pointer; each surface inside re-enables its own.
|
|
271
|
-
["pointer-events", "none"],
|
|
272
|
-
// The layer must also be invisible, and that is not the shadow root's job
|
|
273
|
-
// (D512). The host is in the customer's light DOM whatever happens inside
|
|
274
|
-
// it, so their `div { background: ... !important }` paints it, and a painted
|
|
275
|
-
// full-viewport layer is their whole site replaced by a coloured sheet with
|
|
276
|
-
// no error anywhere. These four are every property that fills or edges a
|
|
277
|
-
// box, and none of them is something a customer wants on an invisible layer.
|
|
278
|
-
["background", "transparent"],
|
|
279
|
-
["border", "0"],
|
|
280
|
-
["box-shadow", "none"],
|
|
281
|
-
["outline", "0"]
|
|
282
|
-
];
|
|
283
|
-
var RESET_STYLE = [
|
|
284
|
-
["margin", "0"],
|
|
285
|
-
["padding", "0"]
|
|
286
|
-
];
|
|
287
|
-
var STYLE_MARKER = "data-arcy-style";
|
|
288
|
-
var shells = /* @__PURE__ */ new Map();
|
|
289
|
-
function isMounted(id = HOST_ID) {
|
|
290
|
-
const shell = shells.get(id);
|
|
291
|
-
return shell !== void 0 && shell.host.isConnected;
|
|
292
|
-
}
|
|
293
|
-
function createShell(options = {}) {
|
|
294
|
-
if (typeof document === "undefined") return null;
|
|
295
|
-
const id = options.id ?? HOST_ID;
|
|
296
|
-
const container = options.container;
|
|
297
|
-
if (isMounted(id)) {
|
|
298
|
-
warn("This ARCY surface is already on this page. Ignoring the second mount.");
|
|
299
|
-
return null;
|
|
300
|
-
}
|
|
301
|
-
shells.delete(id);
|
|
302
|
-
const parent = container ?? document.body ?? document.documentElement;
|
|
303
|
-
if (!parent) {
|
|
304
|
-
warn("The page has no <body> to mount into yet. The widget will not render.");
|
|
305
|
-
return null;
|
|
306
|
-
}
|
|
307
|
-
if (!container) {
|
|
308
|
-
const hosts = document.querySelectorAll(`[${HOST_ATTRIBUTE}]`);
|
|
309
|
-
for (let i = 0; i < hosts.length; i += 1) {
|
|
310
|
-
if (hosts[i]?.id === id) {
|
|
311
|
-
warn("Another copy of ARCY is already on this page. Ignoring this one.");
|
|
312
|
-
return null;
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
try {
|
|
317
|
-
const host = document.createElement("div");
|
|
318
|
-
host.id = id;
|
|
319
|
-
host.setAttribute(HOST_ATTRIBUTE, "");
|
|
320
|
-
for (const [property, value] of CRITICAL_STYLE) {
|
|
321
|
-
host.style.setProperty(property, value, "important");
|
|
322
|
-
}
|
|
323
|
-
for (const [property, value] of RESET_STYLE) {
|
|
324
|
-
host.style.setProperty(property, value);
|
|
325
|
-
}
|
|
326
|
-
let root = host;
|
|
327
|
-
let isolated = false;
|
|
328
|
-
try {
|
|
329
|
-
if (host.attachShadow) {
|
|
330
|
-
root = host.attachShadow({ mode: "closed" });
|
|
331
|
-
isolated = true;
|
|
332
|
-
}
|
|
333
|
-
} catch {
|
|
334
|
-
}
|
|
335
|
-
parent.appendChild(host);
|
|
336
|
-
isolateInteractions(host);
|
|
337
|
-
const shell = {
|
|
338
|
-
host,
|
|
339
|
-
root,
|
|
340
|
-
isolated,
|
|
341
|
-
destroy() {
|
|
342
|
-
try {
|
|
343
|
-
if (host.parentNode) host.parentNode.removeChild(host);
|
|
344
|
-
} catch {
|
|
345
|
-
}
|
|
346
|
-
if (shells.get(id) === shell) shells.delete(id);
|
|
347
|
-
}
|
|
348
|
-
};
|
|
349
|
-
shells.set(id, shell);
|
|
350
|
-
return shell;
|
|
351
|
-
} catch (error) {
|
|
352
|
-
warn(`The widget could not mount. ${String(error)}`);
|
|
353
|
-
return null;
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
var ISOLATED_EVENTS = [
|
|
357
|
-
"pointerdown",
|
|
358
|
-
"pointerup",
|
|
359
|
-
"pointercancel",
|
|
360
|
-
"mousedown",
|
|
361
|
-
"mouseup",
|
|
362
|
-
"click",
|
|
363
|
-
"dblclick",
|
|
364
|
-
"auxclick",
|
|
365
|
-
"contextmenu",
|
|
366
|
-
"touchstart",
|
|
367
|
-
"touchend",
|
|
368
|
-
"touchcancel",
|
|
369
|
-
"focusin",
|
|
370
|
-
"focusout",
|
|
371
|
-
"keydown",
|
|
372
|
-
"keyup",
|
|
373
|
-
"keypress",
|
|
374
|
-
"beforeinput",
|
|
375
|
-
"input",
|
|
376
|
-
"change",
|
|
377
|
-
"compositionstart",
|
|
378
|
-
"compositionupdate",
|
|
379
|
-
"compositionend",
|
|
380
|
-
"cut",
|
|
381
|
-
"copy",
|
|
382
|
-
"paste"
|
|
383
|
-
];
|
|
384
|
-
function isolateInteractions(host) {
|
|
385
|
-
const stop = (event) => {
|
|
386
|
-
try {
|
|
387
|
-
event.stopPropagation();
|
|
388
|
-
} catch {
|
|
389
|
-
}
|
|
390
|
-
};
|
|
391
|
-
for (const type of ISOLATED_EVENTS) {
|
|
392
|
-
try {
|
|
393
|
-
host.addEventListener(type, stop);
|
|
394
|
-
} catch {
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
try {
|
|
398
|
-
host.addEventListener("mousedown", (event) => {
|
|
399
|
-
if (event.defaultPrevented) return;
|
|
400
|
-
releaseHostFocus(host.ownerDocument, host);
|
|
401
|
-
});
|
|
402
|
-
} catch {
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
function releaseHostFocus(doc, host) {
|
|
406
|
-
try {
|
|
407
|
-
const active = doc.activeElement;
|
|
408
|
-
if (!active || active === host || host.contains(active)) return;
|
|
409
|
-
if (active === doc.body || active === doc.documentElement) return;
|
|
410
|
-
const blur = active.blur;
|
|
411
|
-
if (typeof blur === "function") blur.call(active);
|
|
412
|
-
} catch {
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
function injectStyles(shell, css) {
|
|
416
|
-
if (!shell.isolated) return false;
|
|
417
|
-
try {
|
|
418
|
-
const existing = shell.root.querySelector(`[${STYLE_MARKER}]`);
|
|
419
|
-
if (existing) {
|
|
420
|
-
existing.textContent = `${existing.textContent ?? ""}
|
|
421
|
-
${css}`;
|
|
422
|
-
return true;
|
|
423
|
-
}
|
|
424
|
-
const style2 = shell.host.ownerDocument.createElement("style");
|
|
425
|
-
style2.setAttribute(STYLE_MARKER, "");
|
|
426
|
-
style2.textContent = css;
|
|
427
|
-
shell.root.appendChild(style2);
|
|
428
|
-
return true;
|
|
429
|
-
} catch (error) {
|
|
430
|
-
warn(`The widget could not apply its styles. ${String(error)}`);
|
|
431
|
-
return false;
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
|
|
435
106
|
// src/picker/notice.ts
|
|
436
107
|
var NOTICE_HOST_ID = "arcy-picker-notice";
|
|
437
108
|
var CARD_OFFSET = [
|
|
@@ -523,7 +194,6 @@ function refuse(message) {
|
|
|
523
194
|
}
|
|
524
195
|
var PICKER_PARAM = "arcy_picker";
|
|
525
196
|
var PICKER_VERIFY_PATH = "/api/v1/sdk/tagging/verify";
|
|
526
|
-
var PICKER_CDN_BASE = "https://cdn.arcyai.com";
|
|
527
197
|
var PICKER_LOAD_TIMEOUT_MS = 15e3;
|
|
528
198
|
var VERIFY_TIMEOUT_MS = 1e4;
|
|
529
199
|
function detectPickerNonce(search, hash) {
|
|
@@ -608,115 +278,6 @@ function normalizeOrigin(value) {
|
|
|
608
278
|
return null;
|
|
609
279
|
}
|
|
610
280
|
}
|
|
611
|
-
function loadPickerChunk(win, cdnBase, timeoutMs, setTimeoutImpl) {
|
|
612
|
-
return new Promise((resolve) => {
|
|
613
|
-
let settled = false;
|
|
614
|
-
let script = null;
|
|
615
|
-
const settle = (value) => {
|
|
616
|
-
if (settled) return;
|
|
617
|
-
settled = true;
|
|
618
|
-
if (script) {
|
|
619
|
-
script.onload = null;
|
|
620
|
-
script.onerror = null;
|
|
621
|
-
}
|
|
622
|
-
resolve(value);
|
|
623
|
-
};
|
|
624
|
-
let warned = false;
|
|
625
|
-
const complain = (message) => {
|
|
626
|
-
if (warned) return;
|
|
627
|
-
warned = true;
|
|
628
|
-
refuse(message);
|
|
629
|
-
};
|
|
630
|
-
const read = () => {
|
|
631
|
-
let registration;
|
|
632
|
-
try {
|
|
633
|
-
registration = win[PICKER_GLOBAL];
|
|
634
|
-
} catch {
|
|
635
|
-
complain(
|
|
636
|
-
"The element picker registration could not be read. Design mode is off."
|
|
637
|
-
);
|
|
638
|
-
return null;
|
|
639
|
-
}
|
|
640
|
-
if (!registration) return null;
|
|
641
|
-
if (typeof registration.mount !== "function") {
|
|
642
|
-
complain(
|
|
643
|
-
"The element picker registered something unusable. Design mode is off."
|
|
644
|
-
);
|
|
645
|
-
return null;
|
|
646
|
-
}
|
|
647
|
-
let contract;
|
|
648
|
-
try {
|
|
649
|
-
contract = registration.contract;
|
|
650
|
-
} catch {
|
|
651
|
-
contract = void 0;
|
|
652
|
-
}
|
|
653
|
-
if (contract !== PICKER_CONTRACT) {
|
|
654
|
-
complain(
|
|
655
|
-
`The element picker on the CDN speaks contract ${typeof contract === "number" ? contract : "unknown"}, this arcy.js speaks ${PICKER_CONTRACT}. Update arcy.js to use design mode.`
|
|
656
|
-
);
|
|
657
|
-
return null;
|
|
658
|
-
}
|
|
659
|
-
return registration;
|
|
660
|
-
};
|
|
661
|
-
const readAndSettle = (whenAbsent) => {
|
|
662
|
-
try {
|
|
663
|
-
const registration = read();
|
|
664
|
-
if (!registration && !warned) whenAbsent();
|
|
665
|
-
settle(registration);
|
|
666
|
-
} catch {
|
|
667
|
-
settle(null);
|
|
668
|
-
}
|
|
669
|
-
};
|
|
670
|
-
let present = false;
|
|
671
|
-
try {
|
|
672
|
-
present = win[PICKER_GLOBAL] !== void 0;
|
|
673
|
-
} catch {
|
|
674
|
-
present = true;
|
|
675
|
-
}
|
|
676
|
-
if (present) {
|
|
677
|
-
readAndSettle(() => {
|
|
678
|
-
});
|
|
679
|
-
return;
|
|
680
|
-
}
|
|
681
|
-
try {
|
|
682
|
-
const doc = win.document;
|
|
683
|
-
script = doc.createElement("script");
|
|
684
|
-
script.async = true;
|
|
685
|
-
script.src = `${cdnBase}/${PICKER_CHUNK_FILE}`;
|
|
686
|
-
const src = script.src;
|
|
687
|
-
script.onload = () => readAndSettle(
|
|
688
|
-
() => refuse(
|
|
689
|
-
"The element picker loaded but did not register. Design mode is off."
|
|
690
|
-
)
|
|
691
|
-
);
|
|
692
|
-
script.onerror = () => {
|
|
693
|
-
try {
|
|
694
|
-
refuse(
|
|
695
|
-
`Failed to load ${src}. Design mode is off. If this page sets a Content-Security-Policy, it must allow scripts from ${cdnBase}.`
|
|
696
|
-
);
|
|
697
|
-
} catch {
|
|
698
|
-
}
|
|
699
|
-
settle(null);
|
|
700
|
-
};
|
|
701
|
-
(doc.head || doc.documentElement).appendChild(script);
|
|
702
|
-
} catch (error) {
|
|
703
|
-
refuse(`The element picker could not load. ${String(error)}`);
|
|
704
|
-
settle(null);
|
|
705
|
-
return;
|
|
706
|
-
}
|
|
707
|
-
try {
|
|
708
|
-
setTimeoutImpl(() => {
|
|
709
|
-
if (!settled) {
|
|
710
|
-
refuse(
|
|
711
|
-
"The element picker did not load in time. Design mode is off."
|
|
712
|
-
);
|
|
713
|
-
}
|
|
714
|
-
settle(null);
|
|
715
|
-
}, timeoutMs);
|
|
716
|
-
} catch {
|
|
717
|
-
}
|
|
718
|
-
});
|
|
719
|
-
}
|
|
720
281
|
function currentPath(win) {
|
|
721
282
|
try {
|
|
722
283
|
const path = win.location.pathname;
|
|
@@ -754,7 +315,7 @@ async function maybeActivateDesignMode(options) {
|
|
|
754
315
|
apiBase,
|
|
755
316
|
fetchImpl,
|
|
756
317
|
onVerified,
|
|
757
|
-
|
|
318
|
+
loadChunk,
|
|
758
319
|
loadTimeoutMs = PICKER_LOAD_TIMEOUT_MS,
|
|
759
320
|
setTimeoutImpl = (fn, ms) => setTimeout(fn, ms)
|
|
760
321
|
} = options;
|
|
@@ -784,12 +345,7 @@ async function maybeActivateDesignMode(options) {
|
|
|
784
345
|
onVerified();
|
|
785
346
|
} catch {
|
|
786
347
|
}
|
|
787
|
-
const registration = await
|
|
788
|
-
win,
|
|
789
|
-
cdnBase,
|
|
790
|
-
loadTimeoutMs,
|
|
791
|
-
setTimeoutImpl
|
|
792
|
-
);
|
|
348
|
+
const registration = await loadChunk(win, loadTimeoutMs, setTimeoutImpl);
|
|
793
349
|
if (!registration) return "load_failed";
|
|
794
350
|
const { returnOrigin, screenshotsEnabled } = verified;
|
|
795
351
|
const handle = registration.mount({
|
|
@@ -843,84 +399,6 @@ async function maybeActivateDesignMode(options) {
|
|
|
843
399
|
}
|
|
844
400
|
}
|
|
845
401
|
|
|
846
|
-
// src/telemetry/storage.ts
|
|
847
|
-
var NULL_STORE = {
|
|
848
|
-
getItem: () => null,
|
|
849
|
-
setItem: () => {
|
|
850
|
-
},
|
|
851
|
-
removeItem: () => {
|
|
852
|
-
}
|
|
853
|
-
};
|
|
854
|
-
function safeStore(raw) {
|
|
855
|
-
if (!raw) return NULL_STORE;
|
|
856
|
-
return {
|
|
857
|
-
getItem(key) {
|
|
858
|
-
try {
|
|
859
|
-
return raw.getItem(key);
|
|
860
|
-
} catch {
|
|
861
|
-
return null;
|
|
862
|
-
}
|
|
863
|
-
},
|
|
864
|
-
setItem(key, value) {
|
|
865
|
-
try {
|
|
866
|
-
raw.setItem(key, value);
|
|
867
|
-
} catch {
|
|
868
|
-
}
|
|
869
|
-
},
|
|
870
|
-
removeItem(key) {
|
|
871
|
-
try {
|
|
872
|
-
raw.removeItem(key);
|
|
873
|
-
} catch {
|
|
874
|
-
}
|
|
875
|
-
}
|
|
876
|
-
};
|
|
877
|
-
}
|
|
878
|
-
function browserLocalStorage() {
|
|
879
|
-
try {
|
|
880
|
-
const candidate = globalThis.localStorage;
|
|
881
|
-
return safeStore(candidate);
|
|
882
|
-
} catch {
|
|
883
|
-
return NULL_STORE;
|
|
884
|
-
}
|
|
885
|
-
}
|
|
886
|
-
function browserSessionStorage() {
|
|
887
|
-
try {
|
|
888
|
-
const candidate = globalThis.sessionStorage;
|
|
889
|
-
return safeStore(candidate);
|
|
890
|
-
} catch {
|
|
891
|
-
return NULL_STORE;
|
|
892
|
-
}
|
|
893
|
-
}
|
|
894
|
-
function readJson(store, key) {
|
|
895
|
-
const raw = store.getItem(key);
|
|
896
|
-
if (!raw) return null;
|
|
897
|
-
try {
|
|
898
|
-
const parsed = JSON.parse(raw);
|
|
899
|
-
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
900
|
-
return null;
|
|
901
|
-
}
|
|
902
|
-
return parsed;
|
|
903
|
-
} catch {
|
|
904
|
-
return null;
|
|
905
|
-
}
|
|
906
|
-
}
|
|
907
|
-
function writeJson(store, key, value) {
|
|
908
|
-
try {
|
|
909
|
-
store.setItem(key, JSON.stringify(value));
|
|
910
|
-
} catch {
|
|
911
|
-
}
|
|
912
|
-
}
|
|
913
|
-
|
|
914
|
-
// src/flow/preview-state.ts
|
|
915
|
-
var PREVIEW_KEY_PREFIX = "arcy.preview.";
|
|
916
|
-
function hasPendingPreview(token, store = browserSessionStorage()) {
|
|
917
|
-
try {
|
|
918
|
-
return safeStore(store).getItem(PREVIEW_KEY_PREFIX + token) !== null;
|
|
919
|
-
} catch {
|
|
920
|
-
return false;
|
|
921
|
-
}
|
|
922
|
-
}
|
|
923
|
-
|
|
924
402
|
// src/flow/preview.ts
|
|
925
403
|
var PREVIEW_PARAM = "arcy_preview";
|
|
926
404
|
var PREVIEW_PROTOCOL = 1;
|
|
@@ -1108,6 +586,8 @@ function createSessionBootstrap(options) {
|
|
|
1108
586
|
if (payload.viewerLocale !== void 0)
|
|
1109
587
|
out.viewerLocale = payload.viewerLocale;
|
|
1110
588
|
if (payload.route !== void 0) out.route = payload.route;
|
|
589
|
+
if (payload.sessionContext !== void 0)
|
|
590
|
+
out.sessionContext = payload.sessionContext;
|
|
1111
591
|
return JSON.stringify(out);
|
|
1112
592
|
}
|
|
1113
593
|
async function attempt(json) {
|
|
@@ -1191,190 +671,14 @@ function createSessionBootstrap(options) {
|
|
|
1191
671
|
};
|
|
1192
672
|
}
|
|
1193
673
|
|
|
1194
|
-
// src/shell/chat-contract.ts
|
|
1195
|
-
var CHAT_GLOBAL = "__arcyChat";
|
|
1196
|
-
var CHAT_CONTRACT = 4;
|
|
1197
|
-
var ATTACHABLE_TYPES = [
|
|
1198
|
-
"image/png",
|
|
1199
|
-
"image/jpeg",
|
|
1200
|
-
"image/webp"
|
|
1201
|
-
];
|
|
1202
|
-
var FONT_KEY_PROPERTY = "--_arcy-font-key";
|
|
1203
|
-
var POLICY_LINK_CLASS = "arcy-chat-policy";
|
|
1204
|
-
var FALLBACK_CHROME = {
|
|
1205
|
-
historyTitle: "Recent chats",
|
|
1206
|
-
historyEmpty: "No conversations yet.",
|
|
1207
|
-
newChat: "New chat",
|
|
1208
|
-
flowsTitle: "What I can do",
|
|
1209
|
-
flowsEmpty: "Nothing to run yet.",
|
|
1210
|
-
collapse: "Collapse",
|
|
1211
|
-
back: "Back",
|
|
1212
|
-
closeSheet: "Close",
|
|
1213
|
-
settingsTitle: "Settings",
|
|
1214
|
-
dockChat: "Dock to the corner",
|
|
1215
|
-
dockChatHint: "Shrink ARCY to a button in the bottom-right corner.",
|
|
1216
|
-
language: "Language",
|
|
1217
|
-
openChat: "Open chat",
|
|
1218
|
-
moveBar: "Move the chat bar",
|
|
1219
|
-
send: "Send",
|
|
1220
|
-
flowRunning: "Running",
|
|
1221
|
-
flowStep: "Step {step} of {total}",
|
|
1222
|
-
flowStop: "Stop",
|
|
1223
|
-
flowFailed: "Sorry, I could not finish that for you this time. You can ask me here and I will help you do it.",
|
|
1224
|
-
attachMenu: "Add",
|
|
1225
|
-
attachImage: "Attach an image",
|
|
1226
|
-
removeImage: "Remove image",
|
|
1227
|
-
imageTooLarge: "That image is too large. Each image must be under 5 MB.",
|
|
1228
|
-
imageWrongType: "That file type is not supported. Use PNG, JPEG or WEBP.",
|
|
1229
|
-
imageTooMany: "You can attach up to 3 images.",
|
|
1230
|
-
imageFailed: "That image could not be uploaded. Please try again.",
|
|
1231
|
-
likeAnswer: "Good answer",
|
|
1232
|
-
dislikeAnswer: "Bad answer",
|
|
1233
|
-
answering: "Answering",
|
|
1234
|
-
aiDisclaimer: "{app} is AI and can make mistakes",
|
|
1235
|
-
consentNotice: "Your messages are processed to answer your question.",
|
|
1236
|
-
consentWithPolicy: "{policy}",
|
|
1237
|
-
disclaimerJoin: " \xB7 ",
|
|
1238
|
-
privacyPolicy: "Privacy Policy",
|
|
1239
|
-
poweredBy: "Powered by ARCY",
|
|
1240
|
-
genericError: "Something went wrong. Please try again.",
|
|
1241
|
-
userCapReached: "You've reached today's limit for questions. Please try again tomorrow.",
|
|
1242
|
-
imageConversation: "Image",
|
|
1243
|
-
searchedSourcesLabel: "Searched {count} sources",
|
|
1244
|
-
sourcesLabel: "Sources:",
|
|
1245
|
-
flowOfferHint: "You can do this in one click with the Quick Flow below.",
|
|
1246
|
-
fillConfirm: "Confirm",
|
|
1247
|
-
fillYes: "Yes",
|
|
1248
|
-
fillNo: "No",
|
|
1249
|
-
fillRequired: "This field needs an answer.",
|
|
1250
|
-
fillInvalidNumber: "Please enter a number.",
|
|
1251
|
-
fillNumberTooSmall: "Please enter {min} or more.",
|
|
1252
|
-
fillNumberTooLarge: "Please enter {max} or less.",
|
|
1253
|
-
fillInvalidEmail: "Please enter a valid email address.",
|
|
1254
|
-
fillInvalidUrl: "Please enter a valid link.",
|
|
1255
|
-
fillInvalidDate: "Please enter a valid date.",
|
|
1256
|
-
fillInvalidTime: "Please enter a valid time.",
|
|
1257
|
-
fillDateTooEarly: "Please pick {min} or later.",
|
|
1258
|
-
fillDateTooLate: "Please pick {max} or earlier.",
|
|
1259
|
-
fillTooLong: "Please keep it under {max} characters.",
|
|
1260
|
-
fillInvalidOption: "Please pick one of the options.",
|
|
1261
|
-
fillInvalid: "That does not look right. Please try again."
|
|
1262
|
-
};
|
|
1263
|
-
|
|
1264
674
|
// src/shell/chat-loader.ts
|
|
1265
|
-
var CHAT_CDN_BASE = "https://cdn.arcyai.com";
|
|
1266
675
|
var CHAT_LOAD_TIMEOUT_MS = 15e3;
|
|
1267
|
-
function loadChatChunk(win, cdnBase, timeoutMs, setTimeoutImpl) {
|
|
1268
|
-
return new Promise((resolve) => {
|
|
1269
|
-
let settled = false;
|
|
1270
|
-
let script = null;
|
|
1271
|
-
const settle = (value) => {
|
|
1272
|
-
if (settled) return;
|
|
1273
|
-
settled = true;
|
|
1274
|
-
if (script) {
|
|
1275
|
-
script.onload = null;
|
|
1276
|
-
script.onerror = null;
|
|
1277
|
-
}
|
|
1278
|
-
resolve(value);
|
|
1279
|
-
};
|
|
1280
|
-
let warned = false;
|
|
1281
|
-
const complain = (message) => {
|
|
1282
|
-
if (warned) return;
|
|
1283
|
-
warned = true;
|
|
1284
|
-
warn(message);
|
|
1285
|
-
};
|
|
1286
|
-
const read = () => {
|
|
1287
|
-
let registration;
|
|
1288
|
-
try {
|
|
1289
|
-
registration = win[CHAT_GLOBAL];
|
|
1290
|
-
} catch {
|
|
1291
|
-
complain(
|
|
1292
|
-
"The chat panel registration could not be read. The panel will not open."
|
|
1293
|
-
);
|
|
1294
|
-
return null;
|
|
1295
|
-
}
|
|
1296
|
-
if (!registration) return null;
|
|
1297
|
-
if (typeof registration.mount !== "function") {
|
|
1298
|
-
complain(
|
|
1299
|
-
"The chat panel registered something unusable. The panel will not open."
|
|
1300
|
-
);
|
|
1301
|
-
return null;
|
|
1302
|
-
}
|
|
1303
|
-
let contract;
|
|
1304
|
-
try {
|
|
1305
|
-
contract = registration.contract;
|
|
1306
|
-
} catch {
|
|
1307
|
-
contract = void 0;
|
|
1308
|
-
}
|
|
1309
|
-
if (contract !== CHAT_CONTRACT) {
|
|
1310
|
-
complain(
|
|
1311
|
-
`The chat panel on the CDN speaks contract ${typeof contract === "number" ? contract : "unknown"}, this arcy.js speaks ${CHAT_CONTRACT}. Update arcy.js to open the chat panel.`
|
|
1312
|
-
);
|
|
1313
|
-
return null;
|
|
1314
|
-
}
|
|
1315
|
-
return registration;
|
|
1316
|
-
};
|
|
1317
|
-
const readAndSettle = (whenAbsent) => {
|
|
1318
|
-
try {
|
|
1319
|
-
const registration = read();
|
|
1320
|
-
if (!registration && !warned) whenAbsent();
|
|
1321
|
-
settle(registration);
|
|
1322
|
-
} catch {
|
|
1323
|
-
settle(null);
|
|
1324
|
-
}
|
|
1325
|
-
};
|
|
1326
|
-
let present = false;
|
|
1327
|
-
try {
|
|
1328
|
-
present = win[CHAT_GLOBAL] !== void 0;
|
|
1329
|
-
} catch {
|
|
1330
|
-
present = true;
|
|
1331
|
-
}
|
|
1332
|
-
if (present) {
|
|
1333
|
-
readAndSettle(() => {
|
|
1334
|
-
});
|
|
1335
|
-
return;
|
|
1336
|
-
}
|
|
1337
|
-
try {
|
|
1338
|
-
const doc = win.document;
|
|
1339
|
-
script = doc.createElement("script");
|
|
1340
|
-
script.async = true;
|
|
1341
|
-
script.src = `${cdnBase}/${CHAT_CHUNK_FILE}`;
|
|
1342
|
-
const src = script.src;
|
|
1343
|
-
script.onload = () => readAndSettle(
|
|
1344
|
-
() => warn("The chat panel loaded but did not register. The panel will not open.")
|
|
1345
|
-
);
|
|
1346
|
-
script.onerror = () => {
|
|
1347
|
-
try {
|
|
1348
|
-
warn(
|
|
1349
|
-
`Failed to load ${src}. The chat panel will not open. If this page sets a Content-Security-Policy, it must allow scripts from ${cdnBase}.`
|
|
1350
|
-
);
|
|
1351
|
-
} catch {
|
|
1352
|
-
}
|
|
1353
|
-
settle(null);
|
|
1354
|
-
};
|
|
1355
|
-
(doc.head || doc.documentElement).appendChild(script);
|
|
1356
|
-
} catch (error) {
|
|
1357
|
-
warn(`The chat panel could not load. ${String(error)}`);
|
|
1358
|
-
settle(null);
|
|
1359
|
-
return;
|
|
1360
|
-
}
|
|
1361
|
-
try {
|
|
1362
|
-
setTimeoutImpl(() => {
|
|
1363
|
-
if (!settled) {
|
|
1364
|
-
warn("The chat panel did not load in time.");
|
|
1365
|
-
}
|
|
1366
|
-
settle(null);
|
|
1367
|
-
}, timeoutMs);
|
|
1368
|
-
} catch {
|
|
1369
|
-
}
|
|
1370
|
-
});
|
|
1371
|
-
}
|
|
1372
676
|
function createChatController(options) {
|
|
1373
677
|
const {
|
|
1374
678
|
root,
|
|
1375
679
|
host,
|
|
1376
680
|
win = typeof window !== "undefined" ? window : void 0,
|
|
1377
|
-
|
|
681
|
+
loadChunk,
|
|
1378
682
|
loadTimeoutMs = CHAT_LOAD_TIMEOUT_MS,
|
|
1379
683
|
setTimeoutImpl = (fn, ms) => setTimeout(fn, ms),
|
|
1380
684
|
onRequestClose,
|
|
@@ -1407,12 +711,7 @@ function createChatController(options) {
|
|
|
1407
711
|
loading = (async () => {
|
|
1408
712
|
if (!win) return;
|
|
1409
713
|
try {
|
|
1410
|
-
const registration = await
|
|
1411
|
-
win,
|
|
1412
|
-
cdnBase,
|
|
1413
|
-
loadTimeoutMs,
|
|
1414
|
-
setTimeoutImpl
|
|
1415
|
-
);
|
|
714
|
+
const registration = await loadChunk(win, loadTimeoutMs, setTimeoutImpl);
|
|
1416
715
|
if (!registration || destroyed) return;
|
|
1417
716
|
const context = {
|
|
1418
717
|
root,
|
|
@@ -1524,144 +823,12 @@ function createChatController(options) {
|
|
|
1524
823
|
};
|
|
1525
824
|
}
|
|
1526
825
|
|
|
1527
|
-
// src/flow/flow-contract.ts
|
|
1528
|
-
var FLOW_GLOBAL = "__arcyFlow";
|
|
1529
|
-
var FLOW_CONTRACT = 2;
|
|
1530
|
-
|
|
1531
|
-
// src/flow/state.ts
|
|
1532
|
-
var FLOW_KEY_PREFIX = "arcy.flow.";
|
|
1533
|
-
function isValid(stored) {
|
|
1534
|
-
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";
|
|
1535
|
-
}
|
|
1536
|
-
function createFlowRunState(token, { store: raw = browserLocalStorage() } = {}) {
|
|
1537
|
-
const store = safeStore(raw);
|
|
1538
|
-
const key = FLOW_KEY_PREFIX + token;
|
|
1539
|
-
return {
|
|
1540
|
-
read() {
|
|
1541
|
-
const stored = readJson(store, key);
|
|
1542
|
-
if (!stored || !isValid(stored)) return null;
|
|
1543
|
-
return {
|
|
1544
|
-
flowId: stored.flowId,
|
|
1545
|
-
currentStepCvid: stored.currentStepCvid,
|
|
1546
|
-
flowSessionId: stored.flowSessionId,
|
|
1547
|
-
startedAt: stored.startedAt
|
|
1548
|
-
};
|
|
1549
|
-
},
|
|
1550
|
-
save(run) {
|
|
1551
|
-
writeJson(store, key, run);
|
|
1552
|
-
},
|
|
1553
|
-
clear() {
|
|
1554
|
-
store.removeItem(key);
|
|
1555
|
-
}
|
|
1556
|
-
};
|
|
1557
|
-
}
|
|
1558
|
-
|
|
1559
826
|
// src/flow/flow-loader.ts
|
|
1560
|
-
var FLOW_CDN_BASE = "https://cdn.arcyai.com";
|
|
1561
827
|
var FLOW_LOAD_TIMEOUT_MS = 15e3;
|
|
1562
|
-
function loadFlowChunk(win, cdnBase, timeoutMs, setTimeoutImpl) {
|
|
1563
|
-
return new Promise((resolve) => {
|
|
1564
|
-
let settled = false;
|
|
1565
|
-
let script = null;
|
|
1566
|
-
const settle = (value) => {
|
|
1567
|
-
if (settled) return;
|
|
1568
|
-
settled = true;
|
|
1569
|
-
if (script) {
|
|
1570
|
-
script.onload = null;
|
|
1571
|
-
script.onerror = null;
|
|
1572
|
-
}
|
|
1573
|
-
resolve(value);
|
|
1574
|
-
};
|
|
1575
|
-
let warned = false;
|
|
1576
|
-
const complain = (message) => {
|
|
1577
|
-
if (warned) return;
|
|
1578
|
-
warned = true;
|
|
1579
|
-
warn(message);
|
|
1580
|
-
};
|
|
1581
|
-
const read = () => {
|
|
1582
|
-
let registration;
|
|
1583
|
-
try {
|
|
1584
|
-
registration = win[FLOW_GLOBAL];
|
|
1585
|
-
} catch {
|
|
1586
|
-
complain("The flow engine registration could not be read. The flow will not start.");
|
|
1587
|
-
return null;
|
|
1588
|
-
}
|
|
1589
|
-
if (!registration) return null;
|
|
1590
|
-
if (typeof registration.mount !== "function") {
|
|
1591
|
-
complain("The flow engine registered something unusable. The flow will not start.");
|
|
1592
|
-
return null;
|
|
1593
|
-
}
|
|
1594
|
-
let contract;
|
|
1595
|
-
try {
|
|
1596
|
-
contract = registration.contract;
|
|
1597
|
-
} catch {
|
|
1598
|
-
contract = void 0;
|
|
1599
|
-
}
|
|
1600
|
-
if (contract !== FLOW_CONTRACT) {
|
|
1601
|
-
complain(
|
|
1602
|
-
`The flow engine on the CDN speaks contract ${typeof contract === "number" ? contract : "unknown"}, this arcy.js speaks ${FLOW_CONTRACT}. Update arcy.js to run flows.`
|
|
1603
|
-
);
|
|
1604
|
-
return null;
|
|
1605
|
-
}
|
|
1606
|
-
return registration;
|
|
1607
|
-
};
|
|
1608
|
-
const readAndSettle = (whenAbsent) => {
|
|
1609
|
-
try {
|
|
1610
|
-
const registration = read();
|
|
1611
|
-
if (!registration && !warned) whenAbsent();
|
|
1612
|
-
settle(registration);
|
|
1613
|
-
} catch {
|
|
1614
|
-
settle(null);
|
|
1615
|
-
}
|
|
1616
|
-
};
|
|
1617
|
-
let present = false;
|
|
1618
|
-
try {
|
|
1619
|
-
present = win[FLOW_GLOBAL] !== void 0;
|
|
1620
|
-
} catch {
|
|
1621
|
-
present = true;
|
|
1622
|
-
}
|
|
1623
|
-
if (present) {
|
|
1624
|
-
readAndSettle(() => {
|
|
1625
|
-
});
|
|
1626
|
-
return;
|
|
1627
|
-
}
|
|
1628
|
-
try {
|
|
1629
|
-
const doc = win.document;
|
|
1630
|
-
script = doc.createElement("script");
|
|
1631
|
-
script.async = true;
|
|
1632
|
-
script.src = `${cdnBase}/${FLOW_CHUNK_FILE}`;
|
|
1633
|
-
const src = script.src;
|
|
1634
|
-
script.onload = () => readAndSettle(
|
|
1635
|
-
() => warn("The flow engine loaded but did not register. The flow will not start.")
|
|
1636
|
-
);
|
|
1637
|
-
script.onerror = () => {
|
|
1638
|
-
try {
|
|
1639
|
-
warn(
|
|
1640
|
-
`Failed to load ${src}. The flow will not start. If this page sets a Content-Security-Policy, it must allow scripts from ${cdnBase}.`
|
|
1641
|
-
);
|
|
1642
|
-
} catch {
|
|
1643
|
-
}
|
|
1644
|
-
settle(null);
|
|
1645
|
-
};
|
|
1646
|
-
(doc.head || doc.documentElement).appendChild(script);
|
|
1647
|
-
} catch (error) {
|
|
1648
|
-
warn(`The flow engine could not load. ${String(error)}`);
|
|
1649
|
-
settle(null);
|
|
1650
|
-
return;
|
|
1651
|
-
}
|
|
1652
|
-
try {
|
|
1653
|
-
setTimeoutImpl(() => {
|
|
1654
|
-
if (!settled) warn("The flow engine did not load in time.");
|
|
1655
|
-
settle(null);
|
|
1656
|
-
}, timeoutMs);
|
|
1657
|
-
} catch {
|
|
1658
|
-
}
|
|
1659
|
-
});
|
|
1660
|
-
}
|
|
1661
828
|
function createFlowController(options) {
|
|
1662
829
|
const {
|
|
1663
830
|
win = typeof window !== "undefined" ? window : void 0,
|
|
1664
|
-
|
|
831
|
+
loadChunk,
|
|
1665
832
|
loadTimeoutMs = FLOW_LOAD_TIMEOUT_MS,
|
|
1666
833
|
setTimeoutImpl = (fn, ms) => setTimeout(fn, ms),
|
|
1667
834
|
context
|
|
@@ -1675,12 +842,7 @@ function createFlowController(options) {
|
|
|
1675
842
|
loading = (async () => {
|
|
1676
843
|
if (!win) return;
|
|
1677
844
|
try {
|
|
1678
|
-
const registration = await
|
|
1679
|
-
win,
|
|
1680
|
-
cdnBase,
|
|
1681
|
-
loadTimeoutMs,
|
|
1682
|
-
setTimeoutImpl
|
|
1683
|
-
);
|
|
845
|
+
const registration = await loadChunk(win, loadTimeoutMs, setTimeoutImpl);
|
|
1684
846
|
if (!registration || destroyed) return;
|
|
1685
847
|
handle = registration.mount({ ...context, doc: win.document, win });
|
|
1686
848
|
if (!handle) warn("The flow engine could not start.");
|
|
@@ -1759,90 +921,6 @@ function createFlowController(options) {
|
|
|
1759
921
|
};
|
|
1760
922
|
}
|
|
1761
923
|
|
|
1762
|
-
// src/shell/drag.ts
|
|
1763
|
-
function carriesFiles(event) {
|
|
1764
|
-
const transfer = event.dataTransfer;
|
|
1765
|
-
if (!transfer) return false;
|
|
1766
|
-
const types = transfer.types;
|
|
1767
|
-
if (!types) return false;
|
|
1768
|
-
for (let index = 0; index < types.length; index += 1) {
|
|
1769
|
-
if (types[index] === "Files") return true;
|
|
1770
|
-
}
|
|
1771
|
-
return false;
|
|
1772
|
-
}
|
|
1773
|
-
function droppedFiles(event) {
|
|
1774
|
-
const transfer = event.dataTransfer;
|
|
1775
|
-
return Array.from(transfer?.files ?? []);
|
|
1776
|
-
}
|
|
1777
|
-
|
|
1778
|
-
// src/shell/tooltip.ts
|
|
1779
|
-
var TOOLTIP_CSS = `
|
|
1780
|
-
[data-tip] {
|
|
1781
|
-
position: relative;
|
|
1782
|
-
}
|
|
1783
|
-
|
|
1784
|
-
[data-tip]::after {
|
|
1785
|
-
content: attr(data-tip);
|
|
1786
|
-
position: absolute;
|
|
1787
|
-
left: 50%;
|
|
1788
|
-
bottom: calc(100% + 6px);
|
|
1789
|
-
transform: translateX(-50%);
|
|
1790
|
-
z-index: 2;
|
|
1791
|
-
padding: 4px 8px;
|
|
1792
|
-
border-radius: 6px;
|
|
1793
|
-
background: var(--_arcy-main-text, #101828);
|
|
1794
|
-
color: var(--_arcy-main-bg, #ffffff);
|
|
1795
|
-
font-size: 11px;
|
|
1796
|
-
line-height: 1.3;
|
|
1797
|
-
white-space: nowrap;
|
|
1798
|
-
pointer-events: none;
|
|
1799
|
-
opacity: 0;
|
|
1800
|
-
visibility: hidden;
|
|
1801
|
-
transition: opacity 120ms ease;
|
|
1802
|
-
}
|
|
1803
|
-
|
|
1804
|
-
[data-tip][data-tip-placement="below"]::after {
|
|
1805
|
-
bottom: auto;
|
|
1806
|
-
top: calc(100% + 6px);
|
|
1807
|
-
}
|
|
1808
|
-
|
|
1809
|
-
/* Alignment, for a control near an edge the panel clips (D895). Centring a
|
|
1810
|
-
bubble on the leftmost header icon puts half of it outside the panel, and
|
|
1811
|
-
the panel clips its overflow, so the half outside is simply gone. No
|
|
1812
|
-
z-index can fix that: the bubble is not behind anything, it is cut off.
|
|
1813
|
-
These pin the bubble to the control's own edge instead. */
|
|
1814
|
-
[data-tip][data-tip-align="start"]::after {
|
|
1815
|
-
left: 0;
|
|
1816
|
-
transform: none;
|
|
1817
|
-
}
|
|
1818
|
-
|
|
1819
|
-
[data-tip][data-tip-align="end"]::after {
|
|
1820
|
-
left: auto;
|
|
1821
|
-
right: 0;
|
|
1822
|
-
transform: none;
|
|
1823
|
-
}
|
|
1824
|
-
|
|
1825
|
-
[data-tip]:hover::after,
|
|
1826
|
-
[data-tip]:focus-visible::after {
|
|
1827
|
-
opacity: 1;
|
|
1828
|
-
visibility: visible;
|
|
1829
|
-
}
|
|
1830
|
-
|
|
1831
|
-
/* A coarse pointer has no hover: the tooltip would latch open on tap and
|
|
1832
|
-
sit over the thing the visitor just pressed. */
|
|
1833
|
-
@media (hover: none) {
|
|
1834
|
-
[data-tip]::after {
|
|
1835
|
-
display: none;
|
|
1836
|
-
}
|
|
1837
|
-
}
|
|
1838
|
-
|
|
1839
|
-
@media (prefers-reduced-motion: reduce) {
|
|
1840
|
-
[data-tip]::after {
|
|
1841
|
-
transition: none;
|
|
1842
|
-
}
|
|
1843
|
-
}
|
|
1844
|
-
`;
|
|
1845
|
-
|
|
1846
924
|
// src/shell/styles.ts
|
|
1847
925
|
var BAR_CSS = TOOLTIP_CSS + `
|
|
1848
926
|
.arcy-bar-wrap {
|
|
@@ -2209,20 +1287,20 @@ var BAR_CSS = TOOLTIP_CSS + `
|
|
|
2209
1287
|
outline-offset: 1px;
|
|
2210
1288
|
}
|
|
2211
1289
|
|
|
2212
|
-
/* The attachment
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
1290
|
+
/* The attachment tray. See bar.ts's renderTray(). */
|
|
1291
|
+
.arcy-bar-tray {
|
|
1292
|
+
display: none;
|
|
1293
|
+
flex-wrap: wrap;
|
|
2216
1294
|
align-items: center;
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
min-width:
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
1295
|
+
gap: 6px;
|
|
1296
|
+
padding: 10px 4px 2px 4px;
|
|
1297
|
+
min-width: 0;
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
/* A composer holding pictures is two rows, not one. */
|
|
1301
|
+
.arcy-bar[data-attachments="true"] {
|
|
1302
|
+
flex-direction: column;
|
|
1303
|
+
align-items: stretch;
|
|
2226
1304
|
}
|
|
2227
1305
|
|
|
2228
1306
|
/* \u2500\u2500 Flow-running mode \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
|
|
@@ -2344,58 +1422,6 @@ var BAR_CSS = TOOLTIP_CSS + `
|
|
|
2344
1422
|
}
|
|
2345
1423
|
`;
|
|
2346
1424
|
|
|
2347
|
-
// src/shell/viewport.ts
|
|
2348
|
-
var MOBILE_BREAKPOINT = 640;
|
|
2349
|
-
var MOBILE_MARGIN = 12;
|
|
2350
|
-
var PANEL_MARGIN = 16;
|
|
2351
|
-
var positive = (value) => typeof value === "number" && Number.isFinite(value) && value > 0 ? value : 0;
|
|
2352
|
-
function readSize(win) {
|
|
2353
|
-
const el = win?.document?.documentElement;
|
|
2354
|
-
return {
|
|
2355
|
-
width: positive(el?.clientWidth) || positive(win?.innerWidth),
|
|
2356
|
-
height: positive(el?.clientHeight) || positive(win?.innerHeight)
|
|
2357
|
-
};
|
|
2358
|
-
}
|
|
2359
|
-
function keyboardOffset(win) {
|
|
2360
|
-
const vv = win?.visualViewport;
|
|
2361
|
-
if (!vv) return 0;
|
|
2362
|
-
const layoutHeight = readSize(win).height;
|
|
2363
|
-
const visualHeight = positive(vv.height);
|
|
2364
|
-
if (!layoutHeight || !visualHeight) return 0;
|
|
2365
|
-
const top = typeof vv.offsetTop === "number" && Number.isFinite(vv.offsetTop) ? vv.offsetTop : 0;
|
|
2366
|
-
const covered = layoutHeight - (visualHeight + top);
|
|
2367
|
-
if (!Number.isFinite(covered) || covered < 24) return 0;
|
|
2368
|
-
return Math.min(covered, layoutHeight);
|
|
2369
|
-
}
|
|
2370
|
-
function readViewport(win) {
|
|
2371
|
-
const size2 = readSize(win);
|
|
2372
|
-
return {
|
|
2373
|
-
...size2,
|
|
2374
|
-
isMobile: size2.width > 0 && size2.width < MOBILE_BREAKPOINT,
|
|
2375
|
-
keyboardOffset: keyboardOffset(win)
|
|
2376
|
-
};
|
|
2377
|
-
}
|
|
2378
|
-
var FALLBACK_WIDTH_PX = 560;
|
|
2379
|
-
function surfaceWidth(widthPercent, viewport) {
|
|
2380
|
-
const available = positive(viewport.width);
|
|
2381
|
-
if (!available) return FALLBACK_WIDTH_PX;
|
|
2382
|
-
if (available < MOBILE_BREAKPOINT) {
|
|
2383
|
-
return Math.max(0, available - MOBILE_MARGIN * 2);
|
|
2384
|
-
}
|
|
2385
|
-
const target = Math.round(widthPercent / 100 * available);
|
|
2386
|
-
return Math.min(target, available - PANEL_MARGIN * 2);
|
|
2387
|
-
}
|
|
2388
|
-
function dragTransform(offset) {
|
|
2389
|
-
if (offset.x === 0 && offset.y === 0) return "none";
|
|
2390
|
-
return `translate(${offset.x}px, ${offset.y}px)`;
|
|
2391
|
-
}
|
|
2392
|
-
function clampOffsetX(x, width, viewportWidth) {
|
|
2393
|
-
const available = positive(viewportWidth);
|
|
2394
|
-
if (!available) return x;
|
|
2395
|
-
const maxX = Math.max(0, (available - positive(width)) / 2);
|
|
2396
|
-
return Math.min(maxX, Math.max(-maxX, x));
|
|
2397
|
-
}
|
|
2398
|
-
|
|
2399
1425
|
// src/shell/bar.ts
|
|
2400
1426
|
var MAX_INPUT_LENGTH = 1e4;
|
|
2401
1427
|
var BAR_HEIGHT = 52;
|
|
@@ -2508,7 +1534,7 @@ function createBar(options = {}) {
|
|
|
2508
1534
|
let statusStep = null;
|
|
2509
1535
|
let stop = null;
|
|
2510
1536
|
let disclaimer = null;
|
|
2511
|
-
let
|
|
1537
|
+
let tray = null;
|
|
2512
1538
|
let mode = "idle";
|
|
2513
1539
|
let flow = null;
|
|
2514
1540
|
let awaitingInput = false;
|
|
@@ -2631,6 +1657,15 @@ function createBar(options = {}) {
|
|
|
2631
1657
|
leading.tabIndex = hasMenu ? 0 : -1;
|
|
2632
1658
|
if (!hasMenu) closeMenu();
|
|
2633
1659
|
}
|
|
1660
|
+
function renderTray() {
|
|
1661
|
+
if (!tray || !bar) return;
|
|
1662
|
+
const visible = attachEnabled && attachmentCount > 0 && !isCollapsed();
|
|
1663
|
+
const was = bar.getAttribute("data-attachments") === "true";
|
|
1664
|
+
tray.style.setProperty("display", visible ? "flex" : "none");
|
|
1665
|
+
bar.setAttribute("data-attachments", visible ? "true" : "false");
|
|
1666
|
+
if (was === visible) return;
|
|
1667
|
+
safe("onGeometryChange", () => options.onGeometryChange?.())();
|
|
1668
|
+
}
|
|
2634
1669
|
function setMenuOpen(open) {
|
|
2635
1670
|
menuOpen = open;
|
|
2636
1671
|
if (menu) menu.style.setProperty("display", open ? "flex" : "none");
|
|
@@ -2689,6 +1724,7 @@ function createBar(options = {}) {
|
|
|
2689
1724
|
);
|
|
2690
1725
|
bar.setAttribute("data-collapsed", isCollapsed() ? "true" : "false");
|
|
2691
1726
|
renderLeading();
|
|
1727
|
+
renderTray();
|
|
2692
1728
|
render();
|
|
2693
1729
|
form.style.setProperty("display", effective === "flow" ? "none" : "flex");
|
|
2694
1730
|
status.style.setProperty(
|
|
@@ -2894,9 +1930,9 @@ function createBar(options = {}) {
|
|
|
2894
1930
|
input.maxLength = MAX_INPUT_LENGTH;
|
|
2895
1931
|
input.setAttribute("aria-label", labels.openChat);
|
|
2896
1932
|
input.autocomplete = "off";
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
1933
|
+
tray = doc.createElement("div");
|
|
1934
|
+
tray.className = BAR_TRAY_CLASS;
|
|
1935
|
+
tray.style.setProperty("display", "none");
|
|
2900
1936
|
grip = doc.createElement("button");
|
|
2901
1937
|
grip.type = "button";
|
|
2902
1938
|
grip.className = "arcy-bar-grip";
|
|
@@ -2937,7 +1973,6 @@ function createBar(options = {}) {
|
|
|
2937
1973
|
form.appendChild(grip);
|
|
2938
1974
|
form.appendChild(leading);
|
|
2939
1975
|
form.appendChild(input);
|
|
2940
|
-
form.appendChild(badge);
|
|
2941
1976
|
form.appendChild(submit);
|
|
2942
1977
|
status = doc.createElement("div");
|
|
2943
1978
|
status.className = "arcy-bar-status";
|
|
@@ -2958,6 +1993,7 @@ function createBar(options = {}) {
|
|
|
2958
1993
|
status.appendChild(statusName);
|
|
2959
1994
|
status.appendChild(statusStep);
|
|
2960
1995
|
status.appendChild(stop);
|
|
1996
|
+
bar.appendChild(tray);
|
|
2961
1997
|
bar.appendChild(form);
|
|
2962
1998
|
bar.appendChild(status);
|
|
2963
1999
|
bar.appendChild(menu);
|
|
@@ -3045,7 +2081,7 @@ function createBar(options = {}) {
|
|
|
3045
2081
|
dock = null;
|
|
3046
2082
|
dockIcon = null;
|
|
3047
2083
|
menu = null;
|
|
3048
|
-
|
|
2084
|
+
tray = null;
|
|
3049
2085
|
menuOpen = false;
|
|
3050
2086
|
flow = null;
|
|
3051
2087
|
mode = "idle";
|
|
@@ -3201,13 +2237,11 @@ function createBar(options = {}) {
|
|
|
3201
2237
|
setAttachmentCount(count) {
|
|
3202
2238
|
attachmentCount = count;
|
|
3203
2239
|
try {
|
|
3204
|
-
|
|
3205
|
-
const visible = attachEnabled && count > 0;
|
|
3206
|
-
badge.textContent = visible ? String(count) : "";
|
|
3207
|
-
badge.style.setProperty("display", visible ? "flex" : "none");
|
|
2240
|
+
renderTray();
|
|
3208
2241
|
} catch {
|
|
3209
2242
|
}
|
|
3210
2243
|
},
|
|
2244
|
+
attachmentSlot: () => tray,
|
|
3211
2245
|
focusInput() {
|
|
3212
2246
|
try {
|
|
3213
2247
|
if (shell) releaseHostFocus(shell.host.ownerDocument, shell.host);
|
|
@@ -3432,93 +2466,6 @@ function createTokenCache(token, raw = browserLocalStorage()) {
|
|
|
3432
2466
|
};
|
|
3433
2467
|
}
|
|
3434
2468
|
|
|
3435
|
-
// src/shell/state.ts
|
|
3436
|
-
var SHELL_KEY_PREFIX = "arcy.shell.";
|
|
3437
|
-
var UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
|
|
3438
|
-
var LOCALE_PATTERN = /^[a-z]{2}-[A-Z]{2}$/;
|
|
3439
|
-
function readPosition(value) {
|
|
3440
|
-
if (!value || typeof value !== "object") return null;
|
|
3441
|
-
const { x, y } = value;
|
|
3442
|
-
if (typeof x !== "number" || typeof y !== "number") return null;
|
|
3443
|
-
if (!Number.isFinite(x) || !Number.isFinite(y)) return null;
|
|
3444
|
-
return { x, y };
|
|
3445
|
-
}
|
|
3446
|
-
function uuidV4(fill) {
|
|
3447
|
-
const source = typeof globalThis !== "undefined" ? globalThis.crypto : void 0;
|
|
3448
|
-
if (!fill && source && typeof source.randomUUID === "function") {
|
|
3449
|
-
try {
|
|
3450
|
-
const native = source.randomUUID();
|
|
3451
|
-
if (UUID_PATTERN.test(native)) return native;
|
|
3452
|
-
} catch {
|
|
3453
|
-
}
|
|
3454
|
-
}
|
|
3455
|
-
const bytes = new Uint8Array(16);
|
|
3456
|
-
if (fill) {
|
|
3457
|
-
fill(bytes);
|
|
3458
|
-
} else if (source && typeof source.getRandomValues === "function") {
|
|
3459
|
-
source.getRandomValues(bytes);
|
|
3460
|
-
} else {
|
|
3461
|
-
for (let i = 0; i < bytes.length; i++) {
|
|
3462
|
-
bytes[i] = Math.floor(Math.random() * 256);
|
|
3463
|
-
}
|
|
3464
|
-
}
|
|
3465
|
-
bytes[6] = (bytes[6] ?? 0) & 15 | 64;
|
|
3466
|
-
bytes[8] = (bytes[8] ?? 0) & 63 | 128;
|
|
3467
|
-
let hex = "";
|
|
3468
|
-
for (let i = 0; i < bytes.length; i++) {
|
|
3469
|
-
hex += (bytes[i] ?? 0).toString(16).padStart(2, "0");
|
|
3470
|
-
}
|
|
3471
|
-
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
3472
|
-
}
|
|
3473
|
-
function createShellState(token, { store: raw = browserLocalStorage(), fill } = {}) {
|
|
3474
|
-
const store = safeStore(raw);
|
|
3475
|
-
const key = SHELL_KEY_PREFIX + token;
|
|
3476
|
-
function readRaw() {
|
|
3477
|
-
return readJson(store, key) ?? {};
|
|
3478
|
-
}
|
|
3479
|
-
function patch(next) {
|
|
3480
|
-
writeJson(store, key, { ...readRaw(), ...next });
|
|
3481
|
-
}
|
|
3482
|
-
function read() {
|
|
3483
|
-
const stored = readRaw();
|
|
3484
|
-
const id = stored.anonymousId;
|
|
3485
|
-
return {
|
|
3486
|
-
anonymousId: typeof id === "string" && UUID_PATTERN.test(id) ? id : null,
|
|
3487
|
-
position: readPosition(stored.position),
|
|
3488
|
-
// Anything but a literal `true` means not docked. The value is in the
|
|
3489
|
-
// visitor's own storage, so a truthy string written by an older build
|
|
3490
|
-
// must not be able to hide the widget.
|
|
3491
|
-
docked: stored.docked === true,
|
|
3492
|
-
locale: typeof stored.locale === "string" && LOCALE_PATTERN.test(stored.locale) ? stored.locale : null
|
|
3493
|
-
};
|
|
3494
|
-
}
|
|
3495
|
-
return {
|
|
3496
|
-
read,
|
|
3497
|
-
anonymousId() {
|
|
3498
|
-
const existing = read().anonymousId;
|
|
3499
|
-
if (existing) return existing;
|
|
3500
|
-
patch({ anonymousId: uuidV4(fill) });
|
|
3501
|
-
return read().anonymousId;
|
|
3502
|
-
},
|
|
3503
|
-
rememberPosition(position) {
|
|
3504
|
-
patch({ position });
|
|
3505
|
-
},
|
|
3506
|
-
rememberDocked(docked) {
|
|
3507
|
-
patch({ docked });
|
|
3508
|
-
},
|
|
3509
|
-
rememberLocale(locale) {
|
|
3510
|
-
patch({ locale });
|
|
3511
|
-
},
|
|
3512
|
-
forgetAnonymousId() {
|
|
3513
|
-
const current = readRaw();
|
|
3514
|
-
if (current.anonymousId === void 0) return;
|
|
3515
|
-
delete current.anonymousId;
|
|
3516
|
-
if (Object.keys(current).length === 0) store.removeItem(key);
|
|
3517
|
-
else writeJson(store, key, current);
|
|
3518
|
-
}
|
|
3519
|
-
};
|
|
3520
|
-
}
|
|
3521
|
-
|
|
3522
2469
|
// src/session/visit.ts
|
|
3523
2470
|
var VISIT_KEY_PREFIX = "arcy.visit.";
|
|
3524
2471
|
var FIRST_VISIT_VALUE = "first";
|
|
@@ -3610,6 +2557,97 @@ function createConversationStore(token, { store: raw = browserLocalStorage(), fi
|
|
|
3610
2557
|
};
|
|
3611
2558
|
}
|
|
3612
2559
|
|
|
2560
|
+
// src/session/context.ts
|
|
2561
|
+
var CAMPAIGN_KEYS = [
|
|
2562
|
+
["utm_source", "utmSource"],
|
|
2563
|
+
["utm_medium", "utmMedium"],
|
|
2564
|
+
["utm_campaign", "utmCampaign"],
|
|
2565
|
+
["utm_content", "utmContent"],
|
|
2566
|
+
["utm_term", "utmTerm"]
|
|
2567
|
+
];
|
|
2568
|
+
var MAX_CAMPAIGN_LENGTH = 255;
|
|
2569
|
+
var MAX_REFERRER_LENGTH = 512;
|
|
2570
|
+
var CONTROL_CHARACTERS = /[\u0000-\u001f\u007f]/g;
|
|
2571
|
+
function clean(value, max) {
|
|
2572
|
+
const trimmed = value.replace(CONTROL_CHARACTERS, "").trim();
|
|
2573
|
+
if (trimmed.length === 0) return void 0;
|
|
2574
|
+
return trimmed.slice(0, max);
|
|
2575
|
+
}
|
|
2576
|
+
function scrubReferrer(referrer) {
|
|
2577
|
+
if (typeof referrer !== "string" || referrer.length === 0) return void 0;
|
|
2578
|
+
try {
|
|
2579
|
+
const url = new URL(referrer);
|
|
2580
|
+
const host = url.host;
|
|
2581
|
+
if (!host) return void 0;
|
|
2582
|
+
const path = scrubPath(url.pathname);
|
|
2583
|
+
return clean(path === "/" ? host : host + path, MAX_REFERRER_LENGTH);
|
|
2584
|
+
} catch {
|
|
2585
|
+
return void 0;
|
|
2586
|
+
}
|
|
2587
|
+
}
|
|
2588
|
+
function readCampaign(search) {
|
|
2589
|
+
const out = {};
|
|
2590
|
+
if (typeof search !== "string" || search.length === 0) return out;
|
|
2591
|
+
const query = search.charAt(0) === "?" ? search.slice(1) : search;
|
|
2592
|
+
for (const pair of query.split("&")) {
|
|
2593
|
+
const eq = pair.indexOf("=");
|
|
2594
|
+
if (eq <= 0) continue;
|
|
2595
|
+
const rawKey = pair.slice(0, eq).toLowerCase();
|
|
2596
|
+
const match = CAMPAIGN_KEYS.find((entry) => entry[0] === rawKey);
|
|
2597
|
+
if (!match) continue;
|
|
2598
|
+
let value = pair.slice(eq + 1).replace(/\+/g, " ");
|
|
2599
|
+
try {
|
|
2600
|
+
value = decodeURIComponent(value);
|
|
2601
|
+
} catch {
|
|
2602
|
+
}
|
|
2603
|
+
const cleaned = clean(value, MAX_CAMPAIGN_LENGTH);
|
|
2604
|
+
if (cleaned) out[match[1]] = cleaned;
|
|
2605
|
+
}
|
|
2606
|
+
return out;
|
|
2607
|
+
}
|
|
2608
|
+
function readBrowser(userAgent) {
|
|
2609
|
+
const ua = typeof userAgent === "string" ? userAgent : "";
|
|
2610
|
+
if (ua.length === 0) return void 0;
|
|
2611
|
+
if (/Edg[A-Z]?\//.test(ua)) return "Edge";
|
|
2612
|
+
if (/OPR\/|Opera/.test(ua)) return "Opera";
|
|
2613
|
+
if (/SamsungBrowser\//.test(ua)) return "Samsung Internet";
|
|
2614
|
+
if (/Firefox\/|FxiOS\//.test(ua)) return "Firefox";
|
|
2615
|
+
if (/Chrome\/|CriOS\//.test(ua)) return "Chrome";
|
|
2616
|
+
if (/Safari\//.test(ua)) return "Safari";
|
|
2617
|
+
return void 0;
|
|
2618
|
+
}
|
|
2619
|
+
function readOs(userAgent) {
|
|
2620
|
+
const ua = typeof userAgent === "string" ? userAgent : "";
|
|
2621
|
+
if (ua.length === 0) return void 0;
|
|
2622
|
+
if (/iPhone|iPad|iPod/.test(ua)) return "iOS";
|
|
2623
|
+
if (/Android/.test(ua)) return "Android";
|
|
2624
|
+
if (/CrOS/.test(ua)) return "ChromeOS";
|
|
2625
|
+
if (/Windows/.test(ua)) return "Windows";
|
|
2626
|
+
if (/Mac OS X|Macintosh/.test(ua)) return "macOS";
|
|
2627
|
+
if (/Linux/.test(ua)) return "Linux";
|
|
2628
|
+
return void 0;
|
|
2629
|
+
}
|
|
2630
|
+
function viewportBucket(width) {
|
|
2631
|
+
if (typeof width !== "number" || !(width > 0)) return void 0;
|
|
2632
|
+
if (width < 480) return "xs";
|
|
2633
|
+
if (width < 768) return "sm";
|
|
2634
|
+
if (width < 1024) return "md";
|
|
2635
|
+
if (width < 1280) return "lg";
|
|
2636
|
+
return "xl";
|
|
2637
|
+
}
|
|
2638
|
+
function captureSessionContext(input) {
|
|
2639
|
+
const context = { ...readCampaign(input.search) };
|
|
2640
|
+
const referrer = scrubReferrer(input.referrer);
|
|
2641
|
+
if (referrer) context.referrer = referrer;
|
|
2642
|
+
const browser = readBrowser(input.userAgent);
|
|
2643
|
+
if (browser) context.browser = browser;
|
|
2644
|
+
const os = readOs(input.userAgent);
|
|
2645
|
+
if (os) context.os = os;
|
|
2646
|
+
const viewport = viewportBucket(input.viewportWidth);
|
|
2647
|
+
if (viewport) context.viewport = viewport;
|
|
2648
|
+
return Object.keys(context).length > 0 ? context : void 0;
|
|
2649
|
+
}
|
|
2650
|
+
|
|
3613
2651
|
// src/telemetry/consent.ts
|
|
3614
2652
|
var STATES = ["ON", "REQUIRE_CONSENT", "OFF"];
|
|
3615
2653
|
function createConsentCache(store, key) {
|
|
@@ -3666,8 +2704,14 @@ function deviceClass(nav) {
|
|
|
3666
2704
|
|
|
3667
2705
|
// src/telemetry/describe.ts
|
|
3668
2706
|
var MAX_TEXT_LENGTH = 100;
|
|
2707
|
+
var PII_SCAN_MAX = 4096;
|
|
3669
2708
|
var VALUE_BEARING = ["INPUT", "TEXTAREA", "SELECT"];
|
|
3670
|
-
function
|
|
2709
|
+
function readableCopy(raw) {
|
|
2710
|
+
if (raw.length === 0) return void 0;
|
|
2711
|
+
if (containsPii(raw.slice(0, PII_SCAN_MAX))) return void 0;
|
|
2712
|
+
return raw.slice(0, MAX_TEXT_LENGTH);
|
|
2713
|
+
}
|
|
2714
|
+
function describeElementPayload(element, describeElement) {
|
|
3671
2715
|
const tag = (element.tagName ?? "").toUpperCase();
|
|
3672
2716
|
const payload = { tag: tag.toLowerCase() };
|
|
3673
2717
|
const role = element.getAttribute("role");
|
|
@@ -3676,10 +2720,12 @@ function describeClick(element, describeElement) {
|
|
|
3676
2720
|
if (type) payload.type = type.slice(0, 32);
|
|
3677
2721
|
if (VALUE_BEARING.indexOf(tag) === -1) {
|
|
3678
2722
|
const text = (element.textContent ?? "").replace(/\s+/g, " ").trim();
|
|
3679
|
-
|
|
2723
|
+
const readable = readableCopy(text);
|
|
2724
|
+
if (readable) payload.text = readable;
|
|
3680
2725
|
} else {
|
|
3681
2726
|
const label = element.getAttribute("aria-label") ?? element.getAttribute("name");
|
|
3682
|
-
|
|
2727
|
+
const readable = readableCopy(label ?? "");
|
|
2728
|
+
if (readable) payload.label = readable;
|
|
3683
2729
|
}
|
|
3684
2730
|
if (describeElement) {
|
|
3685
2731
|
const fingerprint = describeElement(element);
|
|
@@ -3689,8 +2735,17 @@ function describeClick(element, describeElement) {
|
|
|
3689
2735
|
}
|
|
3690
2736
|
return payload;
|
|
3691
2737
|
}
|
|
3692
|
-
function
|
|
3693
|
-
|
|
2738
|
+
function describeClick(element, describeElement) {
|
|
2739
|
+
return describeElementPayload(element, describeElement);
|
|
2740
|
+
}
|
|
2741
|
+
function describeField(element, describeElement) {
|
|
2742
|
+
const payload = describeElementPayload(element, describeElement);
|
|
2743
|
+
const valid = element.validity?.valid;
|
|
2744
|
+
if (typeof valid === "boolean") payload.valid = valid;
|
|
2745
|
+
return payload;
|
|
2746
|
+
}
|
|
2747
|
+
function describeSubmit(form, describeElement) {
|
|
2748
|
+
const payload = describeElementPayload(form, describeElement);
|
|
3694
2749
|
const name = form.getAttribute("name") ?? form.getAttribute("id");
|
|
3695
2750
|
if (name) payload.form = name.slice(0, MAX_TEXT_LENGTH);
|
|
3696
2751
|
const count = form.elements?.length;
|
|
@@ -3698,6 +2753,169 @@ function describeSubmit(form) {
|
|
|
3698
2753
|
return payload;
|
|
3699
2754
|
}
|
|
3700
2755
|
|
|
2756
|
+
// src/telemetry/errors.ts
|
|
2757
|
+
var REQUEST_METHODS = [
|
|
2758
|
+
"GET",
|
|
2759
|
+
"POST",
|
|
2760
|
+
"PUT",
|
|
2761
|
+
"PATCH",
|
|
2762
|
+
"DELETE",
|
|
2763
|
+
"HEAD",
|
|
2764
|
+
"OPTIONS"
|
|
2765
|
+
];
|
|
2766
|
+
var OTHER_METHOD = "OTHER";
|
|
2767
|
+
var FAILED_STATUS_MIN = 400;
|
|
2768
|
+
var ERROR_MESSAGE_MAX = 200;
|
|
2769
|
+
var CONTROL_CHARACTERS2 = /[\u0000-\u001f\u007f]+/g;
|
|
2770
|
+
function readErrorMessage(raw) {
|
|
2771
|
+
let text = null;
|
|
2772
|
+
if (typeof raw === "string") text = raw;
|
|
2773
|
+
else if (raw && typeof raw === "object") {
|
|
2774
|
+
const message = raw.message;
|
|
2775
|
+
if (typeof message === "string") text = message;
|
|
2776
|
+
}
|
|
2777
|
+
if (text === null) return null;
|
|
2778
|
+
const flattened = text.replace(CONTROL_CHARACTERS2, " ").trim();
|
|
2779
|
+
if (flattened.length === 0) return null;
|
|
2780
|
+
return flattened.length > ERROR_MESSAGE_MAX ? flattened.slice(0, ERROR_MESSAGE_MAX) : flattened;
|
|
2781
|
+
}
|
|
2782
|
+
function normalizeMethod(raw) {
|
|
2783
|
+
if (typeof raw !== "string") return "GET";
|
|
2784
|
+
const upper = raw.toUpperCase();
|
|
2785
|
+
return REQUEST_METHODS.indexOf(upper) === -1 ? OTHER_METHOD : upper;
|
|
2786
|
+
}
|
|
2787
|
+
function isFailedStatus(status) {
|
|
2788
|
+
return typeof status === "number" && status >= FAILED_STATUS_MIN;
|
|
2789
|
+
}
|
|
2790
|
+
function installNetworkWatch(options) {
|
|
2791
|
+
const { target, report, ignore } = options;
|
|
2792
|
+
const restores = [];
|
|
2793
|
+
function emit(method, status, raw) {
|
|
2794
|
+
try {
|
|
2795
|
+
if (!isFailedStatus(status)) return;
|
|
2796
|
+
if (raw !== null && ignore?.(raw)) return;
|
|
2797
|
+
report({
|
|
2798
|
+
method,
|
|
2799
|
+
status,
|
|
2800
|
+
url: raw === null ? null : normalizeRoute(raw, target.location?.href)
|
|
2801
|
+
});
|
|
2802
|
+
} catch {
|
|
2803
|
+
}
|
|
2804
|
+
}
|
|
2805
|
+
function installFetch() {
|
|
2806
|
+
const original = target.fetch;
|
|
2807
|
+
if (typeof original !== "function") return;
|
|
2808
|
+
const wrapper = function(...args) {
|
|
2809
|
+
const result = original.apply(this, args);
|
|
2810
|
+
try {
|
|
2811
|
+
const promise = result;
|
|
2812
|
+
if (typeof promise?.then !== "function") return result;
|
|
2813
|
+
promise.then(
|
|
2814
|
+
(response) => {
|
|
2815
|
+
const status = response?.status;
|
|
2816
|
+
emit(fetchMethod(args), status, fetchUrl(args));
|
|
2817
|
+
},
|
|
2818
|
+
() => {
|
|
2819
|
+
}
|
|
2820
|
+
);
|
|
2821
|
+
} catch {
|
|
2822
|
+
}
|
|
2823
|
+
return result;
|
|
2824
|
+
};
|
|
2825
|
+
target.fetch = wrapper;
|
|
2826
|
+
restores.push(() => {
|
|
2827
|
+
if (target.fetch === wrapper) target.fetch = original;
|
|
2828
|
+
});
|
|
2829
|
+
}
|
|
2830
|
+
function installXhr() {
|
|
2831
|
+
const proto = target.XMLHttpRequest?.prototype;
|
|
2832
|
+
const originalOpen = proto?.open;
|
|
2833
|
+
const originalSend = proto?.send;
|
|
2834
|
+
if (!proto || typeof originalOpen !== "function" || typeof originalSend !== "function") {
|
|
2835
|
+
return;
|
|
2836
|
+
}
|
|
2837
|
+
if (typeof WeakMap !== "function") return;
|
|
2838
|
+
const pending = /* @__PURE__ */ new WeakMap();
|
|
2839
|
+
const open = function(...args) {
|
|
2840
|
+
const result = originalOpen.apply(this, args);
|
|
2841
|
+
try {
|
|
2842
|
+
pending.set(this, {
|
|
2843
|
+
method: normalizeMethod(args[0]),
|
|
2844
|
+
url: typeof args[1] === "string" ? args[1] : null,
|
|
2845
|
+
listening: pending.get(this)?.listening ?? false
|
|
2846
|
+
});
|
|
2847
|
+
} catch {
|
|
2848
|
+
}
|
|
2849
|
+
return result;
|
|
2850
|
+
};
|
|
2851
|
+
const send = function(...args) {
|
|
2852
|
+
try {
|
|
2853
|
+
const xhr = this;
|
|
2854
|
+
const record = pending.get(this);
|
|
2855
|
+
if (typeof xhr.addEventListener === "function" && record?.listening !== true) {
|
|
2856
|
+
if (record) record.listening = true;
|
|
2857
|
+
xhr.addEventListener("loadend", () => {
|
|
2858
|
+
const current = pending.get(this);
|
|
2859
|
+
emit(current?.method ?? "GET", xhr.status, current?.url ?? null);
|
|
2860
|
+
});
|
|
2861
|
+
}
|
|
2862
|
+
} catch {
|
|
2863
|
+
}
|
|
2864
|
+
return originalSend.apply(this, args);
|
|
2865
|
+
};
|
|
2866
|
+
proto.open = open;
|
|
2867
|
+
restores.push(() => {
|
|
2868
|
+
if (proto.open === open) proto.open = originalOpen;
|
|
2869
|
+
});
|
|
2870
|
+
proto.send = send;
|
|
2871
|
+
restores.push(() => {
|
|
2872
|
+
if (proto.send === send) proto.send = originalSend;
|
|
2873
|
+
});
|
|
2874
|
+
}
|
|
2875
|
+
try {
|
|
2876
|
+
installFetch();
|
|
2877
|
+
installXhr();
|
|
2878
|
+
} catch {
|
|
2879
|
+
for (const restore of restores) {
|
|
2880
|
+
try {
|
|
2881
|
+
restore();
|
|
2882
|
+
} catch {
|
|
2883
|
+
}
|
|
2884
|
+
}
|
|
2885
|
+
return null;
|
|
2886
|
+
}
|
|
2887
|
+
if (restores.length === 0) return null;
|
|
2888
|
+
return () => {
|
|
2889
|
+
for (const restore of restores) {
|
|
2890
|
+
try {
|
|
2891
|
+
restore();
|
|
2892
|
+
} catch {
|
|
2893
|
+
}
|
|
2894
|
+
}
|
|
2895
|
+
restores.length = 0;
|
|
2896
|
+
};
|
|
2897
|
+
}
|
|
2898
|
+
function fetchMethod(args) {
|
|
2899
|
+
const init = args[1];
|
|
2900
|
+
if (init && typeof init === "object" && typeof init.method === "string") {
|
|
2901
|
+
return normalizeMethod(init.method);
|
|
2902
|
+
}
|
|
2903
|
+
const input = args[0];
|
|
2904
|
+
if (input && typeof input === "object" && typeof input.method === "string") {
|
|
2905
|
+
return normalizeMethod(input.method);
|
|
2906
|
+
}
|
|
2907
|
+
return "GET";
|
|
2908
|
+
}
|
|
2909
|
+
function fetchUrl(args) {
|
|
2910
|
+
const input = args[0];
|
|
2911
|
+
if (typeof input === "string") return input;
|
|
2912
|
+
if (!input || typeof input !== "object") return null;
|
|
2913
|
+
const url = input.url;
|
|
2914
|
+
if (typeof url === "string") return url;
|
|
2915
|
+
const href = input.href;
|
|
2916
|
+
return typeof href === "string" ? href : null;
|
|
2917
|
+
}
|
|
2918
|
+
|
|
3701
2919
|
// src/telemetry/events.ts
|
|
3702
2920
|
function buildEvent(id, name, occurredAt, context, payload) {
|
|
3703
2921
|
const event = {
|
|
@@ -3716,6 +2934,63 @@ function buildEvent(id, name, occurredAt, context, payload) {
|
|
|
3716
2934
|
return event;
|
|
3717
2935
|
}
|
|
3718
2936
|
|
|
2937
|
+
// src/telemetry/fields.ts
|
|
2938
|
+
var NON_FIELD_TYPES = [
|
|
2939
|
+
"button",
|
|
2940
|
+
"submit",
|
|
2941
|
+
"reset",
|
|
2942
|
+
"image",
|
|
2943
|
+
"hidden",
|
|
2944
|
+
"checkbox",
|
|
2945
|
+
"radio"
|
|
2946
|
+
];
|
|
2947
|
+
var FIELD_TAGS = ["INPUT", "TEXTAREA", "SELECT"];
|
|
2948
|
+
function isFieldElement(element) {
|
|
2949
|
+
if (!element || typeof element.getAttribute !== "function") return false;
|
|
2950
|
+
const tag = (element.tagName ?? "").toUpperCase();
|
|
2951
|
+
if (FIELD_TAGS.indexOf(tag) === -1) return false;
|
|
2952
|
+
if (tag !== "INPUT") return true;
|
|
2953
|
+
const type = (element.getAttribute("type") ?? "text").toLowerCase();
|
|
2954
|
+
return NON_FIELD_TYPES.indexOf(type) === -1;
|
|
2955
|
+
}
|
|
2956
|
+
function isEmpty(element) {
|
|
2957
|
+
const value = element.value;
|
|
2958
|
+
return typeof value === "string" ? value.length === 0 : value == null;
|
|
2959
|
+
}
|
|
2960
|
+
function createFieldObserver(options) {
|
|
2961
|
+
let active = null;
|
|
2962
|
+
return {
|
|
2963
|
+
focus(element) {
|
|
2964
|
+
if (!isFieldElement(element)) return;
|
|
2965
|
+
active = {
|
|
2966
|
+
element,
|
|
2967
|
+
empty: isEmpty(element),
|
|
2968
|
+
filled: false,
|
|
2969
|
+
cleared: false
|
|
2970
|
+
};
|
|
2971
|
+
options.emit("field_focused", element);
|
|
2972
|
+
},
|
|
2973
|
+
change(element) {
|
|
2974
|
+
if (!active || active.element !== element) return;
|
|
2975
|
+
const empty = isEmpty(element);
|
|
2976
|
+
if (empty === active.empty) return;
|
|
2977
|
+
active.empty = empty;
|
|
2978
|
+
if (!empty) {
|
|
2979
|
+
if (active.filled) return;
|
|
2980
|
+
active.filled = true;
|
|
2981
|
+
options.emit("input_filled", element);
|
|
2982
|
+
return;
|
|
2983
|
+
}
|
|
2984
|
+
if (active.cleared) return;
|
|
2985
|
+
active.cleared = true;
|
|
2986
|
+
options.emit("input_cleared", element);
|
|
2987
|
+
},
|
|
2988
|
+
blur(element) {
|
|
2989
|
+
if (active && active.element === element) active = null;
|
|
2990
|
+
}
|
|
2991
|
+
};
|
|
2992
|
+
}
|
|
2993
|
+
|
|
3719
2994
|
// src/telemetry/interactive.ts
|
|
3720
2995
|
var INTERACTIVE_TAGS = [
|
|
3721
2996
|
"A",
|
|
@@ -3766,6 +3041,78 @@ function findInteractive(target, cursorOf) {
|
|
|
3766
3041
|
return null;
|
|
3767
3042
|
}
|
|
3768
3043
|
|
|
3044
|
+
// src/telemetry/scroll.ts
|
|
3045
|
+
var SCROLL_MILESTONES = [25, 50, 75, 100];
|
|
3046
|
+
var SCROLL_THROTTLE_MS = 250;
|
|
3047
|
+
var FINAL_MILESTONE = 100;
|
|
3048
|
+
function createScrollObserver(options) {
|
|
3049
|
+
const {
|
|
3050
|
+
readDepth,
|
|
3051
|
+
emit,
|
|
3052
|
+
setTimeoutImpl = (fn, ms) => setTimeout(fn, ms),
|
|
3053
|
+
clearTimeoutImpl = (handle) => clearTimeout(handle),
|
|
3054
|
+
throttleMs = SCROLL_THROTTLE_MS
|
|
3055
|
+
} = options;
|
|
3056
|
+
let reported = 0;
|
|
3057
|
+
let pending = null;
|
|
3058
|
+
function evaluate() {
|
|
3059
|
+
pending = null;
|
|
3060
|
+
let depth = null;
|
|
3061
|
+
try {
|
|
3062
|
+
depth = readDepth();
|
|
3063
|
+
} catch {
|
|
3064
|
+
return;
|
|
3065
|
+
}
|
|
3066
|
+
if (depth === null) return;
|
|
3067
|
+
let reached = 0;
|
|
3068
|
+
for (const milestone of SCROLL_MILESTONES) {
|
|
3069
|
+
if (depth >= milestone) reached = milestone;
|
|
3070
|
+
}
|
|
3071
|
+
if (reached <= reported) return;
|
|
3072
|
+
reported = reached;
|
|
3073
|
+
emit(reached);
|
|
3074
|
+
}
|
|
3075
|
+
return {
|
|
3076
|
+
scrolled() {
|
|
3077
|
+
if (pending !== null) return;
|
|
3078
|
+
if (reported >= FINAL_MILESTONE) return;
|
|
3079
|
+
try {
|
|
3080
|
+
pending = setTimeoutImpl(evaluate, throttleMs);
|
|
3081
|
+
} catch {
|
|
3082
|
+
pending = null;
|
|
3083
|
+
}
|
|
3084
|
+
},
|
|
3085
|
+
reset() {
|
|
3086
|
+
reported = 0;
|
|
3087
|
+
if (pending !== null) {
|
|
3088
|
+
try {
|
|
3089
|
+
clearTimeoutImpl(pending);
|
|
3090
|
+
} catch {
|
|
3091
|
+
}
|
|
3092
|
+
pending = null;
|
|
3093
|
+
}
|
|
3094
|
+
},
|
|
3095
|
+
dispose() {
|
|
3096
|
+
if (pending === null) return;
|
|
3097
|
+
try {
|
|
3098
|
+
clearTimeoutImpl(pending);
|
|
3099
|
+
} catch {
|
|
3100
|
+
}
|
|
3101
|
+
pending = null;
|
|
3102
|
+
}
|
|
3103
|
+
};
|
|
3104
|
+
}
|
|
3105
|
+
function scrollDepthPercent(metrics) {
|
|
3106
|
+
const viewport = metrics.innerHeight ?? 0;
|
|
3107
|
+
const total = metrics.documentHeight ?? 0;
|
|
3108
|
+
const scrollable = total - viewport;
|
|
3109
|
+
if (!(scrollable > 0) || !(viewport > 0)) return null;
|
|
3110
|
+
const scrolled = (metrics.scrollY ?? 0) + viewport;
|
|
3111
|
+
const percent = scrolled / total * 100;
|
|
3112
|
+
if (!(percent > 0)) return 0;
|
|
3113
|
+
return percent > 100 ? 100 : percent;
|
|
3114
|
+
}
|
|
3115
|
+
|
|
3769
3116
|
// src/telemetry/session.ts
|
|
3770
3117
|
var SESSION_WINDOW_MS = 30 * 60 * 1e3;
|
|
3771
3118
|
var TOUCH_WRITE_INTERVAL_MS = 60 * 1e3;
|
|
@@ -4069,7 +3416,7 @@ function createTelemetry(options) {
|
|
|
4069
3416
|
function currentRoute() {
|
|
4070
3417
|
return normalizeRoute(win?.location?.href) ?? void 0;
|
|
4071
3418
|
}
|
|
4072
|
-
function capture(name, payload,
|
|
3419
|
+
function capture(name, payload, fields2) {
|
|
4073
3420
|
if (halted) return false;
|
|
4074
3421
|
if (!consent.shouldCapture()) return false;
|
|
4075
3422
|
if (transport.stopped() !== null) return false;
|
|
@@ -4086,13 +3433,13 @@ function createTelemetry(options) {
|
|
|
4086
3433
|
route: currentRoute(),
|
|
4087
3434
|
localeCode,
|
|
4088
3435
|
deviceClass: resolvedDeviceClass,
|
|
4089
|
-
flowSessionId:
|
|
3436
|
+
flowSessionId: fields2?.flowSessionId ?? getFlowSessionId?.() ?? void 0
|
|
4090
3437
|
},
|
|
4091
3438
|
payload
|
|
4092
3439
|
);
|
|
4093
|
-
if (
|
|
4094
|
-
if (
|
|
4095
|
-
if (
|
|
3440
|
+
if (fields2?.flowId) event.flowId = fields2.flowId;
|
|
3441
|
+
if (fields2?.flowVersion) event.flowVersion = fields2.flowVersion;
|
|
3442
|
+
if (fields2?.stepCvid) event.stepCvid = fields2.stepCvid;
|
|
4096
3443
|
transport.enqueue(event);
|
|
4097
3444
|
return true;
|
|
4098
3445
|
}
|
|
@@ -4118,7 +3465,7 @@ function createTelemetry(options) {
|
|
|
4118
3465
|
const raw = event;
|
|
4119
3466
|
if (isSynthetic(raw)) return;
|
|
4120
3467
|
if (!raw.target || typeof raw.target.getAttribute !== "function") return;
|
|
4121
|
-
capture("form_submit", describeSubmit(raw.target));
|
|
3468
|
+
capture("form_submit", describeSubmit(raw.target, describeElement));
|
|
4122
3469
|
} catch {
|
|
4123
3470
|
}
|
|
4124
3471
|
};
|
|
@@ -4128,16 +3475,118 @@ function createTelemetry(options) {
|
|
|
4128
3475
|
if (route === lastRoute) return;
|
|
4129
3476
|
const from = lastRoute;
|
|
4130
3477
|
lastRoute = route;
|
|
3478
|
+
scroll.reset();
|
|
4131
3479
|
capture("navigation", from ? { from } : void 0);
|
|
4132
3480
|
} catch {
|
|
4133
3481
|
}
|
|
4134
3482
|
};
|
|
3483
|
+
const fields = createFieldObserver({
|
|
3484
|
+
emit: (name, element) => {
|
|
3485
|
+
capture(name, describeField(element, describeElement));
|
|
3486
|
+
}
|
|
3487
|
+
});
|
|
3488
|
+
const scroll = createScrollObserver({
|
|
3489
|
+
readDepth: () => scrollDepthPercent({
|
|
3490
|
+
scrollY: win?.scrollY,
|
|
3491
|
+
innerHeight: win?.innerHeight,
|
|
3492
|
+
documentHeight: doc?.documentElement?.scrollHeight
|
|
3493
|
+
}),
|
|
3494
|
+
emit: (percent) => {
|
|
3495
|
+
capture("scroll_depth", { percent });
|
|
3496
|
+
},
|
|
3497
|
+
setTimeoutImpl: options.setTimeoutImpl,
|
|
3498
|
+
clearTimeoutImpl: options.clearTimeoutImpl
|
|
3499
|
+
});
|
|
3500
|
+
let sessionEnded = false;
|
|
3501
|
+
const onFocusIn = (event) => {
|
|
3502
|
+
try {
|
|
3503
|
+
const raw = event;
|
|
3504
|
+
if (isSynthetic(raw) || !raw.target) return;
|
|
3505
|
+
fields.focus(raw.target);
|
|
3506
|
+
} catch {
|
|
3507
|
+
}
|
|
3508
|
+
};
|
|
3509
|
+
const onFieldChange = (event) => {
|
|
3510
|
+
try {
|
|
3511
|
+
const raw = event;
|
|
3512
|
+
if (isSynthetic(raw) || !raw.target) return;
|
|
3513
|
+
fields.change(raw.target);
|
|
3514
|
+
} catch {
|
|
3515
|
+
}
|
|
3516
|
+
};
|
|
3517
|
+
const onFocusOut = (event) => {
|
|
3518
|
+
try {
|
|
3519
|
+
const raw = event;
|
|
3520
|
+
if (!raw.target) return;
|
|
3521
|
+
fields.blur(raw.target);
|
|
3522
|
+
} catch {
|
|
3523
|
+
}
|
|
3524
|
+
};
|
|
3525
|
+
const onScroll = () => {
|
|
3526
|
+
try {
|
|
3527
|
+
scroll.scrolled();
|
|
3528
|
+
} catch {
|
|
3529
|
+
}
|
|
3530
|
+
};
|
|
3531
|
+
const onVisibilityChange = () => {
|
|
3532
|
+
try {
|
|
3533
|
+
if (doc?.visibilityState === "visible") {
|
|
3534
|
+
capture("page_visible");
|
|
3535
|
+
return;
|
|
3536
|
+
}
|
|
3537
|
+
capture("page_hidden");
|
|
3538
|
+
void transport.flush(true);
|
|
3539
|
+
} catch {
|
|
3540
|
+
}
|
|
3541
|
+
};
|
|
4135
3542
|
const onPageHide = () => {
|
|
4136
3543
|
try {
|
|
3544
|
+
if (!sessionEnded) {
|
|
3545
|
+
sessionEnded = true;
|
|
3546
|
+
capture("session_end");
|
|
3547
|
+
}
|
|
4137
3548
|
void transport.flush(true);
|
|
4138
3549
|
} catch {
|
|
4139
3550
|
}
|
|
4140
3551
|
};
|
|
3552
|
+
const onError = (event) => {
|
|
3553
|
+
try {
|
|
3554
|
+
const raw = event;
|
|
3555
|
+
captureError({
|
|
3556
|
+
message: readErrorMessage(raw.message) ?? "Uncaught error",
|
|
3557
|
+
kind: "error",
|
|
3558
|
+
source: scrubSource(raw.filename)
|
|
3559
|
+
});
|
|
3560
|
+
} catch {
|
|
3561
|
+
}
|
|
3562
|
+
};
|
|
3563
|
+
const onRejection = (event) => {
|
|
3564
|
+
try {
|
|
3565
|
+
const raw = event;
|
|
3566
|
+
captureError({
|
|
3567
|
+
// The kind alone when the reason is neither a string nor an `Error`.
|
|
3568
|
+
// Serializing an unknown object from the customer's application is the
|
|
3569
|
+
// one thing this event must never do.
|
|
3570
|
+
message: readErrorMessage(raw.reason) ?? "Unhandled promise rejection",
|
|
3571
|
+
kind: "unhandledrejection"
|
|
3572
|
+
});
|
|
3573
|
+
} catch {
|
|
3574
|
+
}
|
|
3575
|
+
};
|
|
3576
|
+
function captureError(encounter) {
|
|
3577
|
+
const payload = {
|
|
3578
|
+
message: encounter.message,
|
|
3579
|
+
kind: encounter.kind
|
|
3580
|
+
};
|
|
3581
|
+
if (encounter.source) payload.source = encounter.source;
|
|
3582
|
+
capture("js_error", payload);
|
|
3583
|
+
}
|
|
3584
|
+
function scrubSource(raw) {
|
|
3585
|
+
if (typeof raw !== "string") return void 0;
|
|
3586
|
+
return normalizeRoute(raw, win?.location?.href) ?? void 0;
|
|
3587
|
+
}
|
|
3588
|
+
const ownOrigin = (apiBase ?? DEFAULT_API_BASE2).replace(/\/+$/, "");
|
|
3589
|
+
let uninstallNetworkWatch = null;
|
|
4141
3590
|
return {
|
|
4142
3591
|
start() {
|
|
4143
3592
|
if (started || halted) return;
|
|
@@ -4145,10 +3594,31 @@ function createTelemetry(options) {
|
|
|
4145
3594
|
lastRoute = currentRoute() ?? null;
|
|
4146
3595
|
doc?.addEventListener("click", onClick, true);
|
|
4147
3596
|
doc?.addEventListener("submit", onSubmit, true);
|
|
3597
|
+
doc?.addEventListener("focusin", onFocusIn, true);
|
|
3598
|
+
doc?.addEventListener("focusout", onFocusOut, true);
|
|
3599
|
+
doc?.addEventListener("input", onFieldChange, true);
|
|
3600
|
+
doc?.addEventListener("change", onFieldChange, true);
|
|
4148
3601
|
win?.addEventListener(ROUTE_CHANGE_EVENT, onNavigate);
|
|
4149
3602
|
win?.addEventListener("popstate", onNavigate);
|
|
3603
|
+
win?.addEventListener("scroll", onScroll, { passive: true });
|
|
4150
3604
|
win?.addEventListener("pagehide", onPageHide);
|
|
4151
|
-
doc?.addEventListener("visibilitychange",
|
|
3605
|
+
doc?.addEventListener("visibilitychange", onVisibilityChange);
|
|
3606
|
+
win?.addEventListener("error", onError);
|
|
3607
|
+
win?.addEventListener("unhandledrejection", onRejection);
|
|
3608
|
+
if (win && uninstallNetworkWatch === null) {
|
|
3609
|
+
uninstallNetworkWatch = installNetworkWatch({
|
|
3610
|
+
target: win,
|
|
3611
|
+
report: (failure) => {
|
|
3612
|
+
const payload = {
|
|
3613
|
+
method: failure.method,
|
|
3614
|
+
status: failure.status
|
|
3615
|
+
};
|
|
3616
|
+
if (failure.url) payload.url = failure.url;
|
|
3617
|
+
capture("request_failed", payload);
|
|
3618
|
+
},
|
|
3619
|
+
ignore: (url) => url.indexOf(ownOrigin) === 0
|
|
3620
|
+
});
|
|
3621
|
+
}
|
|
4152
3622
|
loadNavigationPending = !capture("navigation") && consent.state() === null;
|
|
4153
3623
|
},
|
|
4154
3624
|
stop(options2) {
|
|
@@ -4157,10 +3627,22 @@ function createTelemetry(options) {
|
|
|
4157
3627
|
started = false;
|
|
4158
3628
|
doc?.removeEventListener("click", onClick, true);
|
|
4159
3629
|
doc?.removeEventListener("submit", onSubmit, true);
|
|
3630
|
+
doc?.removeEventListener("focusin", onFocusIn, true);
|
|
3631
|
+
doc?.removeEventListener("focusout", onFocusOut, true);
|
|
3632
|
+
doc?.removeEventListener("input", onFieldChange, true);
|
|
3633
|
+
doc?.removeEventListener("change", onFieldChange, true);
|
|
4160
3634
|
win?.removeEventListener(ROUTE_CHANGE_EVENT, onNavigate);
|
|
4161
3635
|
win?.removeEventListener("popstate", onNavigate);
|
|
3636
|
+
win?.removeEventListener("scroll", onScroll);
|
|
4162
3637
|
win?.removeEventListener("pagehide", onPageHide);
|
|
4163
|
-
doc?.removeEventListener("visibilitychange",
|
|
3638
|
+
doc?.removeEventListener("visibilitychange", onVisibilityChange);
|
|
3639
|
+
win?.removeEventListener("error", onError);
|
|
3640
|
+
win?.removeEventListener("unhandledrejection", onRejection);
|
|
3641
|
+
if (uninstallNetworkWatch) {
|
|
3642
|
+
uninstallNetworkWatch();
|
|
3643
|
+
uninstallNetworkWatch = null;
|
|
3644
|
+
}
|
|
3645
|
+
scroll.dispose();
|
|
4164
3646
|
if (options2?.discard) transport.discard();
|
|
4165
3647
|
else void transport.flush(false);
|
|
4166
3648
|
transport.dispose();
|
|
@@ -4176,8 +3658,8 @@ function createTelemetry(options) {
|
|
|
4176
3658
|
consent.setConsent(granted);
|
|
4177
3659
|
if (!granted) transport.discard();
|
|
4178
3660
|
},
|
|
4179
|
-
track(name, payload,
|
|
4180
|
-
capture(name, payload,
|
|
3661
|
+
track(name, payload, fields2) {
|
|
3662
|
+
capture(name, payload, fields2);
|
|
4181
3663
|
},
|
|
4182
3664
|
flush: () => transport.flush(false),
|
|
4183
3665
|
sessionId: () => session.touch(),
|
|
@@ -4188,14 +3670,15 @@ function createTelemetry(options) {
|
|
|
4188
3670
|
}
|
|
4189
3671
|
|
|
4190
3672
|
// src/version.ts
|
|
4191
|
-
var VERSION = "0.1.
|
|
3673
|
+
var VERSION = "0.1.1";
|
|
4192
3674
|
|
|
4193
3675
|
// src/api.ts
|
|
4194
3676
|
var OPTION_KEYS = [
|
|
4195
3677
|
"telemetry",
|
|
4196
3678
|
"locale",
|
|
4197
3679
|
"defaultOpen",
|
|
4198
|
-
"contentLocale"
|
|
3680
|
+
"contentLocale",
|
|
3681
|
+
"apiBase"
|
|
4199
3682
|
];
|
|
4200
3683
|
function emptyIdentity() {
|
|
4201
3684
|
return { userId: null, isAnonymous: false, attributes: {} };
|
|
@@ -4217,6 +3700,7 @@ function sanitizeAttributes(attributes, method) {
|
|
|
4217
3700
|
}
|
|
4218
3701
|
const out = {};
|
|
4219
3702
|
for (const [key, value] of Object.entries(attributes)) {
|
|
3703
|
+
if (value === void 0) continue;
|
|
4220
3704
|
if (value === null) {
|
|
4221
3705
|
out[key] = null;
|
|
4222
3706
|
continue;
|
|
@@ -4236,11 +3720,25 @@ function sanitizeAttributes(attributes, method) {
|
|
|
4236
3720
|
}
|
|
4237
3721
|
return out;
|
|
4238
3722
|
}
|
|
4239
|
-
|
|
4240
|
-
|
|
3723
|
+
var unavailableChunks = {
|
|
3724
|
+
cdnBase: null,
|
|
3725
|
+
loadChat: () => Promise.resolve(null),
|
|
3726
|
+
loadFlow: () => Promise.resolve(null),
|
|
3727
|
+
loadPicker: () => Promise.resolve(null)
|
|
3728
|
+
};
|
|
3729
|
+
function createArcy(chunks) {
|
|
3730
|
+
return createArcyInternals({ chunks }).api;
|
|
4241
3731
|
}
|
|
4242
3732
|
function createArcyInternals(internalOptions = {}) {
|
|
3733
|
+
const chunks = internalOptions.chunks ?? unavailableChunks;
|
|
4243
3734
|
const emitter = createEmitter();
|
|
3735
|
+
function resolveApiBase() {
|
|
3736
|
+
const fromOptions = options.apiBase;
|
|
3737
|
+
if (typeof fromOptions === "string" && fromOptions.length > 0) {
|
|
3738
|
+
return fromOptions.replace(/\/+$/, "");
|
|
3739
|
+
}
|
|
3740
|
+
return internalOptions.apiBase ?? DEFAULT_API_BASE;
|
|
3741
|
+
}
|
|
4244
3742
|
let token = null;
|
|
4245
3743
|
let options = {};
|
|
4246
3744
|
let identity = emptyIdentity();
|
|
@@ -4254,6 +3752,8 @@ function createArcyInternals(internalOptions = {}) {
|
|
|
4254
3752
|
let bootstrap = null;
|
|
4255
3753
|
let sendSessionSequence = 0;
|
|
4256
3754
|
let entryRouteValue;
|
|
3755
|
+
let sessionContextValue;
|
|
3756
|
+
let sessionContextResolved = false;
|
|
4257
3757
|
let shellState = null;
|
|
4258
3758
|
let visitState = null;
|
|
4259
3759
|
let conversationStore = null;
|
|
@@ -4292,7 +3792,7 @@ function createArcyInternals(internalOptions = {}) {
|
|
|
4292
3792
|
for (const [key, value] of Object.entries(nextOptions)) {
|
|
4293
3793
|
if (!OPTION_KEYS.includes(key)) {
|
|
4294
3794
|
warn(
|
|
4295
|
-
`init() ignored unknown option "${key}". Only telemetry, locale, defaultOpen, and
|
|
3795
|
+
`init() ignored unknown option "${key}". Only telemetry, locale, defaultOpen, contentLocale, and apiBase can be set in code; everything else is configured in the dashboard.`
|
|
4296
3796
|
);
|
|
4297
3797
|
continue;
|
|
4298
3798
|
}
|
|
@@ -4300,6 +3800,12 @@ function createArcyInternals(internalOptions = {}) {
|
|
|
4300
3800
|
}
|
|
4301
3801
|
}
|
|
4302
3802
|
}
|
|
3803
|
+
if (typeof window !== "undefined") {
|
|
3804
|
+
reportCspViolations(window, {
|
|
3805
|
+
apiBase: resolveApiBase(),
|
|
3806
|
+
cdnBase: chunks.cdnBase
|
|
3807
|
+
});
|
|
3808
|
+
}
|
|
4303
3809
|
start(nextToken);
|
|
4304
3810
|
startDesignMode(nextToken);
|
|
4305
3811
|
startPreviewMode(nextToken);
|
|
@@ -4343,7 +3849,7 @@ function createArcyInternals(internalOptions = {}) {
|
|
|
4343
3849
|
// The same seam the bootstrap gets. Without it the collector's batches
|
|
4344
3850
|
// leave through the real `fetch` even under test, so nothing in this
|
|
4345
3851
|
// package could ever assert what a captured event actually carries.
|
|
4346
|
-
apiBase:
|
|
3852
|
+
apiBase: resolveApiBase(),
|
|
4347
3853
|
fetchImpl: internalOptions.fetchImpl,
|
|
4348
3854
|
// 15.1 plugs in here. This is what makes a click on the customer's
|
|
4349
3855
|
// page resolvable to the element the dashboard picked: both sides
|
|
@@ -4368,7 +3874,7 @@ function createArcyInternals(internalOptions = {}) {
|
|
|
4368
3874
|
try {
|
|
4369
3875
|
bootstrap = createSessionBootstrap({
|
|
4370
3876
|
token: activeToken,
|
|
4371
|
-
apiBase:
|
|
3877
|
+
apiBase: resolveApiBase(),
|
|
4372
3878
|
fetchImpl: internalOptions.fetchImpl,
|
|
4373
3879
|
setTimeoutImpl: internalOptions.setTimeoutImpl
|
|
4374
3880
|
});
|
|
@@ -4383,7 +3889,8 @@ function createArcyInternals(internalOptions = {}) {
|
|
|
4383
3889
|
void maybeActivateDesignMode({
|
|
4384
3890
|
win: window,
|
|
4385
3891
|
token: activeToken,
|
|
4386
|
-
|
|
3892
|
+
loadChunk: chunks.loadPicker,
|
|
3893
|
+
apiBase: resolveApiBase(),
|
|
4387
3894
|
fetchImpl: internalOptions.fetchImpl !== void 0 ? internalOptions.fetchImpl : typeof fetch !== "undefined" ? fetch.bind(globalThis) : null,
|
|
4388
3895
|
setTimeoutImpl: internalOptions.setTimeoutImpl,
|
|
4389
3896
|
// Discard, not flush (D490, D492): the queue at this moment is the
|
|
@@ -4408,7 +3915,7 @@ function createArcyInternals(internalOptions = {}) {
|
|
|
4408
3915
|
}
|
|
4409
3916
|
function verifyPreview(nonce) {
|
|
4410
3917
|
return verifyPreviewNonce({
|
|
4411
|
-
apiBase:
|
|
3918
|
+
apiBase: resolveApiBase(),
|
|
4412
3919
|
token: token ?? "",
|
|
4413
3920
|
nonce,
|
|
4414
3921
|
sessionToken: bootstrap?.config()?.sessionToken ?? null,
|
|
@@ -4710,6 +4217,7 @@ function createArcyInternals(internalOptions = {}) {
|
|
|
4710
4217
|
chatController = createChatController({
|
|
4711
4218
|
root,
|
|
4712
4219
|
host,
|
|
4220
|
+
loadChunk: chunks.loadChat,
|
|
4713
4221
|
// The panel's collapse control reaches the public close() through
|
|
4714
4222
|
// here, so isOpen() has exactly one writer. There is no close, only
|
|
4715
4223
|
// collapse: the bar never leaves.
|
|
@@ -4742,6 +4250,10 @@ function createArcyInternals(internalOptions = {}) {
|
|
|
4742
4250
|
setDisclaimer: (text) => mountedBar.setDisclaimer(text),
|
|
4743
4251
|
setAttachEnabled: (enabled) => mountedBar.setAttachEnabled(enabled),
|
|
4744
4252
|
setAttachmentCount: (count) => mountedBar.setAttachmentCount(count),
|
|
4253
|
+
// Where the panel's image tiles render: inside the composer, above
|
|
4254
|
+
// its input. The tiles stay the panel's (it owns the upload they
|
|
4255
|
+
// stand for); only the place is the bar's.
|
|
4256
|
+
attachmentSlot: () => mountedBar.attachmentSlot(),
|
|
4745
4257
|
focusInput: () => mountedBar.focusInput(),
|
|
4746
4258
|
getMetrics: () => mountedBar.getMetrics(),
|
|
4747
4259
|
// The panel's Settings sheet asked for the widget to become a
|
|
@@ -4752,7 +4264,7 @@ function createArcyInternals(internalOptions = {}) {
|
|
|
4752
4264
|
// The flows catalog fetch inputs; read live rather than captured,
|
|
4753
4265
|
// same reasoning as everything else in this options object, since
|
|
4754
4266
|
// the panel can mount before the bootstrap resolves.
|
|
4755
|
-
apiBase:
|
|
4267
|
+
apiBase: resolveApiBase(),
|
|
4756
4268
|
getSessionToken: () => bootstrap?.config()?.sessionToken ?? null,
|
|
4757
4269
|
getSessionId: () => telemetry?.sessionId(),
|
|
4758
4270
|
getRoute: () => typeof window !== "undefined" ? window.location.pathname : "",
|
|
@@ -4806,7 +4318,7 @@ function createArcyInternals(internalOptions = {}) {
|
|
|
4806
4318
|
if (!sessionToken) return null;
|
|
4807
4319
|
const visits = visitState;
|
|
4808
4320
|
return {
|
|
4809
|
-
apiBase:
|
|
4321
|
+
apiBase: resolveApiBase(),
|
|
4810
4322
|
token,
|
|
4811
4323
|
sessionToken,
|
|
4812
4324
|
sessionId: telemetry?.sessionId(),
|
|
@@ -4882,7 +4394,7 @@ function createArcyInternals(internalOptions = {}) {
|
|
|
4882
4394
|
try {
|
|
4883
4395
|
const context = buildFlowContext();
|
|
4884
4396
|
if (!context) return;
|
|
4885
|
-
flowController = createFlowController({ context });
|
|
4397
|
+
flowController = createFlowController({ context, loadChunk: chunks.loadFlow });
|
|
4886
4398
|
} catch (error) {
|
|
4887
4399
|
warn(`The flow engine could not be prepared. ${String(error)}`);
|
|
4888
4400
|
}
|
|
@@ -4939,6 +4451,22 @@ function createArcyInternals(internalOptions = {}) {
|
|
|
4939
4451
|
return void 0;
|
|
4940
4452
|
}
|
|
4941
4453
|
}
|
|
4454
|
+
function sessionContext() {
|
|
4455
|
+
if (sessionContextResolved) return sessionContextValue;
|
|
4456
|
+
sessionContextResolved = true;
|
|
4457
|
+
try {
|
|
4458
|
+
if (typeof window === "undefined") return void 0;
|
|
4459
|
+
sessionContextValue = captureSessionContext({
|
|
4460
|
+
referrer: typeof document !== "undefined" ? document.referrer : null,
|
|
4461
|
+
search: window.location?.search ?? null,
|
|
4462
|
+
userAgent: typeof navigator !== "undefined" ? navigator.userAgent : null,
|
|
4463
|
+
viewportWidth: window.innerWidth
|
|
4464
|
+
});
|
|
4465
|
+
return sessionContextValue;
|
|
4466
|
+
} catch {
|
|
4467
|
+
return void 0;
|
|
4468
|
+
}
|
|
4469
|
+
}
|
|
4942
4470
|
async function sendSession() {
|
|
4943
4471
|
if (!bootstrap || !telemetry) return;
|
|
4944
4472
|
const mySequence = ++sendSessionSequence;
|
|
@@ -4951,6 +4479,10 @@ function createArcyInternals(internalOptions = {}) {
|
|
|
4951
4479
|
// identify() per route change from making every navigation a new
|
|
4952
4480
|
// payload, which is the re-send storm D338's skip exists to prevent.
|
|
4953
4481
|
route: entryRoute(),
|
|
4482
|
+
// Frozen with the entry route and for the same reason. Segmentation
|
|
4483
|
+
// dimensions, not behavior: they land on the session row rather than on
|
|
4484
|
+
// four hundred events that would each repeat them (#790).
|
|
4485
|
+
sessionContext: sessionContext(),
|
|
4954
4486
|
// Sent whether or not this browser has identified (D517). It is the
|
|
4955
4487
|
// Lift Subject for anyone who never will, and the anonymous half of the
|
|
4956
4488
|
// pairing ADR 0055 discovers for everyone who does.
|
|
@@ -5004,15 +4536,15 @@ function createArcyInternals(internalOptions = {}) {
|
|
|
5004
4536
|
warn("identify() needs a user id. Ignoring the call.");
|
|
5005
4537
|
return;
|
|
5006
4538
|
}
|
|
5007
|
-
const
|
|
5008
|
-
if (
|
|
4539
|
+
const clean2 = sanitizeAttributes(attributes, "identify");
|
|
4540
|
+
if (clean2 === null) return;
|
|
5009
4541
|
const hash = readUserHash((options2 ?? {}).userHash, "identify");
|
|
5010
4542
|
if (!hash.ok) return;
|
|
5011
4543
|
const sameUser = identity.userId === userId && !identity.isAnonymous;
|
|
5012
4544
|
identity = {
|
|
5013
4545
|
userId,
|
|
5014
4546
|
isAnonymous: false,
|
|
5015
|
-
attributes: sameUser ? { ...identity.attributes, ...
|
|
4547
|
+
attributes: sameUser ? { ...identity.attributes, ...clean2 } : clean2,
|
|
5016
4548
|
// **The proof carries over for the same id and never across ids.** An
|
|
5017
4549
|
// SPA re-identifying the same user on every route change passes the hash
|
|
5018
4550
|
// once at login and nothing afterwards, and dropping it there would
|
|
@@ -5026,9 +4558,9 @@ function createArcyInternals(internalOptions = {}) {
|
|
|
5026
4558
|
}
|
|
5027
4559
|
async function identifyAnonymous(attributes) {
|
|
5028
4560
|
if (!requireInit("identifyAnonymous")) return;
|
|
5029
|
-
const
|
|
5030
|
-
if (
|
|
5031
|
-
identity = { userId: null, isAnonymous: true, attributes:
|
|
4561
|
+
const clean2 = sanitizeAttributes(attributes, "identifyAnonymous");
|
|
4562
|
+
if (clean2 === null) return;
|
|
4563
|
+
identity = { userId: null, isAnonymous: true, attributes: clean2 };
|
|
5032
4564
|
await syncSession();
|
|
5033
4565
|
}
|
|
5034
4566
|
async function updateUser(attributes) {
|
|
@@ -5039,9 +4571,9 @@ function createArcyInternals(internalOptions = {}) {
|
|
|
5039
4571
|
);
|
|
5040
4572
|
return;
|
|
5041
4573
|
}
|
|
5042
|
-
const
|
|
5043
|
-
if (
|
|
5044
|
-
identity = { ...identity, attributes: { ...identity.attributes, ...
|
|
4574
|
+
const clean2 = sanitizeAttributes(attributes, "updateUser");
|
|
4575
|
+
if (clean2 === null) return;
|
|
4576
|
+
identity = { ...identity, attributes: { ...identity.attributes, ...clean2 } };
|
|
5045
4577
|
await syncSession();
|
|
5046
4578
|
}
|
|
5047
4579
|
function reset() {
|
|
@@ -5102,8 +4634,110 @@ function createArcyInternals(internalOptions = {}) {
|
|
|
5102
4634
|
};
|
|
5103
4635
|
}
|
|
5104
4636
|
|
|
4637
|
+
// src/chunks/bundled.ts
|
|
4638
|
+
async function importChunk(win, importer, globalKey, contract, surface, timeoutMs, setTimeoutImpl) {
|
|
4639
|
+
const holder = win;
|
|
4640
|
+
let warned = false;
|
|
4641
|
+
const complain = (message) => {
|
|
4642
|
+
if (warned) return;
|
|
4643
|
+
warned = true;
|
|
4644
|
+
warn(message);
|
|
4645
|
+
};
|
|
4646
|
+
const read = () => {
|
|
4647
|
+
let registration2;
|
|
4648
|
+
try {
|
|
4649
|
+
registration2 = holder[globalKey];
|
|
4650
|
+
} catch {
|
|
4651
|
+
complain(`The ${surface} registration could not be read. It will not start.`);
|
|
4652
|
+
return null;
|
|
4653
|
+
}
|
|
4654
|
+
if (!registration2) return null;
|
|
4655
|
+
if (typeof registration2.mount !== "function") {
|
|
4656
|
+
complain(`The ${surface} registered something unusable. It will not start.`);
|
|
4657
|
+
return null;
|
|
4658
|
+
}
|
|
4659
|
+
let declared;
|
|
4660
|
+
try {
|
|
4661
|
+
declared = registration2.contract;
|
|
4662
|
+
} catch {
|
|
4663
|
+
declared = void 0;
|
|
4664
|
+
}
|
|
4665
|
+
if (declared !== contract) {
|
|
4666
|
+
complain(
|
|
4667
|
+
`The bundled ${surface} speaks contract ${typeof declared === "number" ? declared : "unknown"}, this arcy.js speaks ${contract}. Reinstall arcy.js so both halves come from one version.`
|
|
4668
|
+
);
|
|
4669
|
+
return null;
|
|
4670
|
+
}
|
|
4671
|
+
return registration2;
|
|
4672
|
+
};
|
|
4673
|
+
let present = false;
|
|
4674
|
+
try {
|
|
4675
|
+
present = holder[globalKey] !== void 0;
|
|
4676
|
+
} catch {
|
|
4677
|
+
present = true;
|
|
4678
|
+
}
|
|
4679
|
+
if (present) return read();
|
|
4680
|
+
const arrived = await Promise.race([
|
|
4681
|
+
importer().then(
|
|
4682
|
+
() => "loaded",
|
|
4683
|
+
(error) => {
|
|
4684
|
+
warn(
|
|
4685
|
+
`The ${surface} chunk could not be loaded from your own build. ${String(error)}`
|
|
4686
|
+
);
|
|
4687
|
+
return "failed";
|
|
4688
|
+
}
|
|
4689
|
+
),
|
|
4690
|
+
new Promise((resolve) => {
|
|
4691
|
+
try {
|
|
4692
|
+
setTimeoutImpl(() => resolve("timeout"), timeoutMs);
|
|
4693
|
+
} catch {
|
|
4694
|
+
}
|
|
4695
|
+
})
|
|
4696
|
+
]);
|
|
4697
|
+
if (arrived === "failed") return null;
|
|
4698
|
+
if (arrived === "timeout") {
|
|
4699
|
+
warn(`The ${surface} did not load in time.`);
|
|
4700
|
+
return null;
|
|
4701
|
+
}
|
|
4702
|
+
const registration = read();
|
|
4703
|
+
if (!registration && !warned) {
|
|
4704
|
+
complain(`The ${surface} loaded but did not register. It will not start.`);
|
|
4705
|
+
}
|
|
4706
|
+
return registration;
|
|
4707
|
+
}
|
|
4708
|
+
var bundledChunks = {
|
|
4709
|
+
cdnBase: null,
|
|
4710
|
+
loadChat: (win, timeoutMs, setTimeoutImpl) => importChunk(
|
|
4711
|
+
win,
|
|
4712
|
+
() => import('./chat-BK57WIAA.js'),
|
|
4713
|
+
CHAT_GLOBAL,
|
|
4714
|
+
CHAT_CONTRACT,
|
|
4715
|
+
"chat panel",
|
|
4716
|
+
timeoutMs,
|
|
4717
|
+
setTimeoutImpl
|
|
4718
|
+
),
|
|
4719
|
+
loadFlow: (win, timeoutMs, setTimeoutImpl) => importChunk(
|
|
4720
|
+
win,
|
|
4721
|
+
() => import('./flow-M7W3H3C5.js'),
|
|
4722
|
+
FLOW_GLOBAL,
|
|
4723
|
+
FLOW_CONTRACT,
|
|
4724
|
+
"flow engine",
|
|
4725
|
+
timeoutMs,
|
|
4726
|
+
setTimeoutImpl
|
|
4727
|
+
),
|
|
4728
|
+
loadPicker: (win, timeoutMs, setTimeoutImpl) => importChunk(
|
|
4729
|
+
win,
|
|
4730
|
+
() => import('./picker-JPESE6JJ.js'),
|
|
4731
|
+
PICKER_GLOBAL,
|
|
4732
|
+
PICKER_CONTRACT,
|
|
4733
|
+
"design-mode picker",
|
|
4734
|
+
timeoutMs,
|
|
4735
|
+
setTimeoutImpl
|
|
4736
|
+
)
|
|
4737
|
+
};
|
|
4738
|
+
|
|
5105
4739
|
// src/index.ts
|
|
5106
|
-
var arcy = createArcy();
|
|
4740
|
+
var arcy = createArcy(bundledChunks);
|
|
5107
4741
|
var src_default = arcy;
|
|
5108
4742
|
|
|
5109
4743
|
export { VERSION, src_default as default };
|