@reticlehq/browser 3.7.0 → 3.8.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/actions/action-report.d.ts +10 -0
- package/dist/actions/action-report.js +59 -14
- package/dist/actions/actions-dom.d.ts +4 -0
- package/dist/actions/actions-dom.js +20 -4
- package/dist/actions/actions.d.ts +1 -0
- package/dist/actions/actions.js +94 -46
- package/dist/actions/appeared-text.js +2 -2
- package/dist/actions/click-target.d.ts +1 -0
- package/dist/actions/click-target.js +14 -0
- package/dist/actions/danger-context.d.ts +2 -1
- package/dist/actions/danger-context.js +43 -17
- package/dist/actions/scroll.d.ts +2 -0
- package/dist/actions/scroll.js +10 -2
- package/dist/activate.d.ts +14 -0
- package/dist/activate.js +43 -0
- package/dist/busy.d.ts +3 -0
- package/dist/busy.js +42 -0
- package/dist/commands/commands.js +45 -17
- package/dist/connect-options.d.ts +12 -1
- package/dist/connect-options.js +18 -1
- package/dist/dom/a11y.d.ts +0 -2
- package/dist/dom/a11y.js +103 -363
- package/dist/dom/chart.d.ts +3 -2
- package/dist/dom/chart.js +3 -2
- package/dist/dom/dom-ignore.d.ts +6 -0
- package/dist/dom/dom-ignore.js +60 -5
- package/dist/dom/layout-audit.d.ts +15 -0
- package/dist/dom/layout-audit.js +208 -0
- package/dist/dom/paint-context.d.ts +15 -0
- package/dist/dom/paint-context.js +78 -0
- package/dist/dom/query-finders.d.ts +6 -0
- package/dist/dom/query-finders.js +216 -0
- package/dist/dom/query.d.ts +1 -1
- package/dist/dom/query.js +154 -227
- package/dist/dom/shadow-registry.d.ts +1 -0
- package/dist/dom/shadow-registry.js +27 -0
- package/dist/dom/snapshot.d.ts +5 -0
- package/dist/dom/snapshot.js +87 -35
- package/dist/dom/stable-selector.d.ts +1 -0
- package/dist/dom/stable-selector.js +19 -0
- package/dist/dom/text-match.d.ts +12 -0
- package/dist/dom/text-match.js +115 -0
- package/dist/dom/theme.d.ts +1 -0
- package/dist/dom/theme.js +21 -9
- package/dist/dom/virtualized.js +5 -1
- package/dist/dom/visibility.d.ts +7 -0
- package/dist/dom/visibility.js +322 -0
- package/dist/index.d.ts +4 -2
- package/dist/index.js +7 -1
- package/dist/observers/blind-spots.js +3 -2
- package/dist/observers/canvas.d.ts +18 -0
- package/dist/observers/canvas.js +76 -0
- package/dist/observers/dom.js +68 -19
- package/dist/observers/field.d.ts +11 -0
- package/dist/observers/field.js +117 -13
- package/dist/observers/install-all.js +4 -0
- package/dist/observers/media.d.ts +2 -0
- package/dist/observers/media.js +32 -0
- package/dist/observers/net-detail/network-body.d.ts +2 -0
- package/dist/observers/net-detail/network-body.js +36 -9
- package/dist/observers/net-detail/network-event-stream.d.ts +5 -0
- package/dist/observers/net-detail/network-event-stream.js +55 -0
- package/dist/observers/net-detail/network-frames.d.ts +2 -0
- package/dist/observers/net-detail/network-frames.js +39 -0
- package/dist/observers/net-detail/network-response-body.d.ts +3 -0
- package/dist/observers/net-detail/network-response-body.js +97 -0
- package/dist/observers/network.js +111 -101
- package/dist/observers/route.d.ts +2 -0
- package/dist/observers/route.js +33 -4
- package/dist/presenter/chrome/presenter-report-copy.d.ts +0 -5
- package/dist/presenter/chrome/presenter-report-copy.js +0 -14
- package/dist/presenter/hud-telemetry.js +1 -1
- package/dist/presenter/presenter-chat-views.d.ts +3 -4
- package/dist/presenter/presenter-chat-views.js +47 -20
- package/dist/presenter/presenter-config.d.ts +1 -0
- package/dist/presenter/presenter-config.js +1 -0
- package/dist/presenter/presenter-frame.d.ts +3 -2
- package/dist/presenter/presenter-frame.js +24 -15
- package/dist/presenter/presenter-harness-row.d.ts +4 -15
- package/dist/presenter/presenter-harness-row.js +11 -50
- package/dist/presenter/presenter-report-styles.js +0 -17
- package/dist/presenter/presenter-report.d.ts +1 -2
- package/dist/presenter/presenter-report.js +4 -54
- package/dist/presenter/presenter-run-state.js +2 -1
- package/dist/presenter/presenter-settings.d.ts +2 -3
- package/dist/presenter/presenter-settings.js +14 -17
- package/dist/presenter/presenter-shell.d.ts +6 -4
- package/dist/presenter/presenter-shell.js +37 -7
- package/dist/presenter/presenter.js +32 -11
- package/dist/registry/store-adapters.d.ts +5 -2
- package/dist/registry/store-adapters.js +40 -10
- package/dist/reticle-inject.js +700 -645
- package/dist/reticle.d.ts +4 -2
- package/dist/reticle.js +36 -6
- package/dist/security/page-location.d.ts +2 -0
- package/dist/security/page-location.js +7 -0
- package/dist/security/serialization.js +1 -1
- package/dist/storage-keys.d.ts +1 -0
- package/dist/storage-keys.js +1 -0
- package/dist/timers/native/native-timers.d.ts +1 -0
- package/dist/timers/native/native-timers.js +5 -0
- package/dist/transport/transport.d.ts +1 -0
- package/dist/transport/transport.js +18 -0
- package/package.json +6 -2
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isDangerousActionText, outwardActionOf, outwardRefusal } from '@reticlehq/core';
|
|
1
|
+
import { OutwardAction, isDangerousActionText, isDangerousNavigation, isInertHref, isPaymentField, outwardActionOf, outwardRefusal, withoutFilenameTokens, } from '@reticlehq/core';
|
|
2
2
|
import { getAccessibleName, getRole } from '../dom/a11y.js';
|
|
3
3
|
import { isHtmlElement } from '../dom/realm.js';
|
|
4
4
|
const LABEL_VALUED_INPUT_TYPES = new Set([
|
|
@@ -8,35 +8,39 @@ const LABEL_VALUED_INPUT_TYPES = new Set([
|
|
|
8
8
|
'image',
|
|
9
9
|
]);
|
|
10
10
|
function holdsUserText(el) {
|
|
11
|
-
if (isHtmlElement(el) && el.isContentEditable)
|
|
11
|
+
if (isHtmlElement(el) && el.isContentEditable) {
|
|
12
12
|
return true;
|
|
13
|
-
|
|
13
|
+
}
|
|
14
|
+
if (el instanceof HTMLTextAreaElement) {
|
|
14
15
|
return true;
|
|
15
|
-
|
|
16
|
+
}
|
|
17
|
+
if (el instanceof HTMLInputElement) {
|
|
16
18
|
return !LABEL_VALUED_INPUT_TYPES.has(el.type.toLowerCase());
|
|
19
|
+
}
|
|
17
20
|
return false;
|
|
18
21
|
}
|
|
19
22
|
const ANCHOR_TAG = 'A';
|
|
20
|
-
const INERT_HREF = /^\s*(#.*|javascript:.*)?$/i;
|
|
21
23
|
function navigatingHref(el) {
|
|
22
|
-
if (ANCHOR_TAG !== el.tagName)
|
|
24
|
+
if (ANCHOR_TAG !== el.tagName) {
|
|
23
25
|
return undefined;
|
|
26
|
+
}
|
|
24
27
|
const href = el.getAttribute('href');
|
|
25
|
-
return null === href ||
|
|
28
|
+
return null === href || isInertHref(href) ? undefined : href;
|
|
26
29
|
}
|
|
27
30
|
export function dangerousActionContext(el) {
|
|
28
31
|
const href = navigatingHref(el);
|
|
29
|
-
if (href !== undefined)
|
|
32
|
+
if (href !== undefined) {
|
|
30
33
|
return href;
|
|
34
|
+
}
|
|
31
35
|
const form = el.closest('form');
|
|
32
36
|
const ownText = holdsUserText(el)
|
|
33
37
|
? ['', '']
|
|
34
38
|
: [el.textContent ?? '', el.getAttribute('value') ?? ''];
|
|
35
39
|
return [
|
|
36
|
-
getAccessibleName(el),
|
|
37
|
-
|
|
38
|
-
el.getAttribute('title') ?? '',
|
|
39
|
-
el.getAttribute('aria-label') ?? '',
|
|
40
|
+
withoutFilenameTokens(getAccessibleName(el)),
|
|
41
|
+
withoutFilenameTokens(ownText.join(' ')),
|
|
42
|
+
withoutFilenameTokens(el.getAttribute('title') ?? ''),
|
|
43
|
+
withoutFilenameTokens(el.getAttribute('aria-label') ?? ''),
|
|
40
44
|
el.getAttribute('href') ?? '',
|
|
41
45
|
form?.getAttribute('action') ?? '',
|
|
42
46
|
].join(' ');
|
|
@@ -46,21 +50,43 @@ export function submitControlFor(el) {
|
|
|
46
50
|
const found = el.closest('form')?.querySelector(SUBMIT_CONTROL_SELECTOR);
|
|
47
51
|
return isHtmlElement(found) ? found : null;
|
|
48
52
|
}
|
|
49
|
-
export function requiresDangerousConfirmation(
|
|
50
|
-
|
|
53
|
+
export function requiresDangerousConfirmation(el) {
|
|
54
|
+
const href = navigatingHref(el);
|
|
55
|
+
if (href !== undefined) {
|
|
56
|
+
return isDangerousNavigation(href);
|
|
57
|
+
}
|
|
58
|
+
return isDangerousActionText(dangerousActionContext(el), getRole(el));
|
|
51
59
|
}
|
|
52
60
|
let outwardAllowed;
|
|
53
61
|
export function setOutwardPolicy(allowed) {
|
|
54
62
|
outwardAllowed = allowed;
|
|
55
63
|
}
|
|
56
64
|
export function assertOutwardAllowed(targets) {
|
|
57
|
-
if (outwardAllowed === undefined)
|
|
65
|
+
if (outwardAllowed === undefined) {
|
|
58
66
|
return;
|
|
67
|
+
}
|
|
59
68
|
for (const el of targets) {
|
|
60
|
-
if (null === el)
|
|
69
|
+
if (null === el) {
|
|
61
70
|
continue;
|
|
71
|
+
}
|
|
62
72
|
const kind = outwardActionOf(dangerousActionContext(el), getRole(el));
|
|
63
|
-
if (kind !== undefined && !outwardAllowed.includes(kind))
|
|
73
|
+
if (kind !== undefined && !outwardAllowed.includes(kind)) {
|
|
64
74
|
throw new Error(outwardRefusal(kind));
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
export function assertPaymentEntryAllowed(el) {
|
|
79
|
+
if (outwardAllowed === undefined || outwardAllowed.includes(OutwardAction.PAY)) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
const facts = {
|
|
83
|
+
autocomplete: el.getAttribute('autocomplete') ?? undefined,
|
|
84
|
+
name: el.getAttribute('name') ?? undefined,
|
|
85
|
+
id: el.getAttribute('id') ?? undefined,
|
|
86
|
+
inputMode: el.getAttribute('inputmode') ?? undefined,
|
|
87
|
+
label: `${getAccessibleName(el)} ${el.getAttribute('placeholder') ?? ''}`,
|
|
88
|
+
};
|
|
89
|
+
if (isPaymentField(facts)) {
|
|
90
|
+
throw new Error(outwardRefusal(OutwardAction.PAY));
|
|
65
91
|
}
|
|
66
92
|
}
|
package/dist/actions/scroll.d.ts
CHANGED
|
@@ -11,6 +11,8 @@ interface ScrollResult {
|
|
|
11
11
|
atLeftEnd: boolean;
|
|
12
12
|
atRightEnd: boolean;
|
|
13
13
|
}
|
|
14
|
+
export declare const SCROLL_FRACTION_RANGE_ERROR_PREFIX = "scroll fraction must be between 0 and 1, got ";
|
|
15
|
+
export declare function fractionOutOfRangeMessage(fraction: number): string;
|
|
14
16
|
export declare function scrollContainer(ref: string | undefined, dy: number | undefined, fraction?: number, dx?: number): ScrollResult;
|
|
15
17
|
export declare function scrollFrom(base: Element | undefined, dy: number | undefined, fraction?: number, dx?: number): ScrollResult;
|
|
16
18
|
export {};
|
package/dist/actions/scroll.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { refs } from '../dom/addressing/refs.js';
|
|
2
2
|
const FALLBACK_STEP_PX = 400;
|
|
3
3
|
const VIEWPORT_FRACTION = 0.8;
|
|
4
|
+
export const SCROLL_FRACTION_RANGE_ERROR_PREFIX = 'scroll fraction must be between 0 and 1, got ';
|
|
5
|
+
export function fractionOutOfRangeMessage(fraction) {
|
|
6
|
+
return `${SCROLL_FRACTION_RANGE_ERROR_PREFIX}${fraction}`;
|
|
7
|
+
}
|
|
4
8
|
function nearestScrollable(el, axis) {
|
|
5
9
|
let cur = el;
|
|
6
10
|
while (cur !== null && cur !== document.body) {
|
|
@@ -18,18 +22,22 @@ export function scrollContainer(ref, dy, fraction, dx) {
|
|
|
18
22
|
return scrollFrom(base instanceof Element ? base : undefined, dy, fraction, dx);
|
|
19
23
|
}
|
|
20
24
|
export function scrollFrom(base, dy, fraction, dx) {
|
|
25
|
+
if (fraction !== undefined && (fraction < 0 || fraction > 1 || Number.isNaN(fraction))) {
|
|
26
|
+
throw new RangeError(fractionOutOfRangeMessage(fraction));
|
|
27
|
+
}
|
|
21
28
|
const axis = dx !== undefined && dx !== 0 && (dy === undefined || 0 === dy) ? 'x' : 'y';
|
|
22
29
|
const target = base !== undefined
|
|
23
30
|
? nearestScrollable(base, axis)
|
|
24
31
|
: (document.scrollingElement ?? document.documentElement);
|
|
25
32
|
const before = target.scrollTop;
|
|
26
33
|
const beforeLeft = target.scrollLeft;
|
|
27
|
-
if (fraction !== undefined
|
|
34
|
+
if (fraction !== undefined) {
|
|
28
35
|
target.scrollTop = Math.round(target.scrollHeight * fraction);
|
|
29
36
|
}
|
|
30
37
|
else {
|
|
31
|
-
if (dx !== undefined && dx !== 0)
|
|
38
|
+
if (dx !== undefined && dx !== 0) {
|
|
32
39
|
target.scrollLeft = beforeLeft + dx;
|
|
40
|
+
}
|
|
33
41
|
if (dx === undefined || (dy !== undefined && 0 !== dy)) {
|
|
34
42
|
const step = dy ?? (Math.round(target.clientHeight * VIEWPORT_FRACTION) || FALLBACK_STEP_PX);
|
|
35
43
|
target.scrollTop = before + step;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ReticleConnectOptions } from './connect-options.js';
|
|
2
|
+
export interface ActivateOptions {
|
|
3
|
+
publicKey: string;
|
|
4
|
+
}
|
|
5
|
+
export interface ActivateHost {
|
|
6
|
+
location: Pick<Location, 'href' | 'origin'>;
|
|
7
|
+
history: Pick<History, 'replaceState' | 'state'>;
|
|
8
|
+
now: () => number;
|
|
9
|
+
connect: () => Promise<{
|
|
10
|
+
connect: (options: ReticleConnectOptions) => void;
|
|
11
|
+
}>;
|
|
12
|
+
warn: (message: string) => void;
|
|
13
|
+
}
|
|
14
|
+
export declare function activate(options: ActivateOptions, host?: ActivateHost | undefined): Promise<boolean>;
|
package/dist/activate.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { HelloEnv, PRODUCTION_ACTIVATE_PARAM, verifyActivationTicket, } from '@reticlehq/core/activation';
|
|
2
|
+
const REFUSED_PREFIX = '[Reticle] activation refused: ';
|
|
3
|
+
function pageHost() {
|
|
4
|
+
if ('undefined' === typeof location || 'undefined' === typeof history)
|
|
5
|
+
return undefined;
|
|
6
|
+
return {
|
|
7
|
+
location,
|
|
8
|
+
history,
|
|
9
|
+
now: () => Date.now(),
|
|
10
|
+
connect: () => import('./reticle-singleton.js').then((sdk) => sdk.reticle),
|
|
11
|
+
warn: (message) => console.warn(message),
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
export async function activate(options, host = pageHost()) {
|
|
15
|
+
if (host === undefined)
|
|
16
|
+
return false;
|
|
17
|
+
const url = new URL(host.location.href);
|
|
18
|
+
const ticket = url.searchParams.get(PRODUCTION_ACTIVATE_PARAM);
|
|
19
|
+
if (null === ticket)
|
|
20
|
+
return false;
|
|
21
|
+
url.searchParams.delete(PRODUCTION_ACTIVATE_PARAM);
|
|
22
|
+
host.history.replaceState(host.history.state, '', url.toString());
|
|
23
|
+
const verdict = await verifyActivationTicket(ticket, {
|
|
24
|
+
publicKey: options.publicKey,
|
|
25
|
+
origin: host.location.origin,
|
|
26
|
+
now: host.now(),
|
|
27
|
+
});
|
|
28
|
+
if (!verdict.ok) {
|
|
29
|
+
host.warn(`${REFUSED_PREFIX}${verdict.reason}`);
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
const { payload } = verdict;
|
|
33
|
+
const sdk = await host.connect();
|
|
34
|
+
sdk.connect({
|
|
35
|
+
url: payload.bridge,
|
|
36
|
+
token: payload.nonce,
|
|
37
|
+
env: HelloEnv.PRODUCTION,
|
|
38
|
+
scope: payload.scope,
|
|
39
|
+
allowInProduction: true,
|
|
40
|
+
allowNonLocalhost: true,
|
|
41
|
+
});
|
|
42
|
+
return true;
|
|
43
|
+
}
|
package/dist/busy.d.ts
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export declare function trackBusy<T>(work: Promise<T>, signal: (name: string, data: Record<string, unknown>) => void): Promise<T>;
|
|
2
|
+
export declare const SHORT_TIMER_MS = 1000;
|
|
3
|
+
export declare function trackShortTimers(signal: (name: string, data: Record<string, unknown>) => void, maxMs?: number): () => void;
|
package/dist/busy.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { RETICLE_BUSY_SIGNAL } from '@reticlehq/core';
|
|
2
|
+
import { captureMethod } from './patching/capture-method.js';
|
|
3
|
+
export function trackBusy(work, signal) {
|
|
4
|
+
signal(RETICLE_BUSY_SIGNAL, { delta: 1 });
|
|
5
|
+
const done = () => {
|
|
6
|
+
signal(RETICLE_BUSY_SIGNAL, { delta: -1 });
|
|
7
|
+
};
|
|
8
|
+
work.then(done, done);
|
|
9
|
+
return work;
|
|
10
|
+
}
|
|
11
|
+
export const SHORT_TIMER_MS = 1000;
|
|
12
|
+
export function trackShortTimers(signal, maxMs = SHORT_TIMER_MS) {
|
|
13
|
+
const realSet = captureMethod(window, 'setTimeout');
|
|
14
|
+
const realClear = captureMethod(window, 'clearTimeout');
|
|
15
|
+
const callSet = realSet.bind(window);
|
|
16
|
+
const callClear = realClear.bind(window);
|
|
17
|
+
const pending = new Set();
|
|
18
|
+
const end = (id) => {
|
|
19
|
+
if (pending.delete(id))
|
|
20
|
+
signal(RETICLE_BUSY_SIGNAL, { delta: -1 });
|
|
21
|
+
};
|
|
22
|
+
window.setTimeout = ((cb, delay = 0, ...args) => {
|
|
23
|
+
if ('function' !== typeof cb || delay > maxMs)
|
|
24
|
+
return Reflect.apply(realSet, window, [cb, delay, ...args]);
|
|
25
|
+
signal(RETICLE_BUSY_SIGNAL, { delta: 1 });
|
|
26
|
+
const id = callSet(() => {
|
|
27
|
+
end(id);
|
|
28
|
+
cb(...args);
|
|
29
|
+
}, delay);
|
|
30
|
+
pending.add(id);
|
|
31
|
+
return id;
|
|
32
|
+
});
|
|
33
|
+
window.clearTimeout = ((id) => {
|
|
34
|
+
if (id !== undefined)
|
|
35
|
+
end(id);
|
|
36
|
+
callClear(id);
|
|
37
|
+
});
|
|
38
|
+
return () => {
|
|
39
|
+
window.setTimeout = realSet;
|
|
40
|
+
window.clearTimeout = realClear;
|
|
41
|
+
};
|
|
42
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ComponentStateReason, ElementQuerySchema, ReticleCommand, SnapshotMode, TRANSPORT_LIMITS, selectPath, capDepth, } from '@reticlehq/core';
|
|
1
|
+
import { ComponentStateReason, ElementQuerySchema, INSPECT_IN_VIEWPORT_FIELD, INSPECT_REVEAL_ARG, ReticleCommand, SnapshotMode, TRANSPORT_LIMITS, selectPath, capDepth, } from '@reticlehq/core';
|
|
2
2
|
import { buildSnapshot } from '../dom/snapshot.js';
|
|
3
3
|
import { paintContextOf } from '../dom/paint-context.js';
|
|
4
4
|
import { matchQuery, runQuery } from '../dom/query.js';
|
|
@@ -11,6 +11,7 @@ import { editEpoch } from '../edit-epoch.js';
|
|
|
11
11
|
import { isButton, isInput } from '../dom/realm.js';
|
|
12
12
|
import { hitTestOccluder } from '../dom/occlusion.js';
|
|
13
13
|
import { readStorage } from '../observers/storage.js';
|
|
14
|
+
import { canvasAfterAct } from '../observers/canvas.js';
|
|
14
15
|
import { captureDesktopWindow } from '../dom/desktop-capture.js';
|
|
15
16
|
import { identifyComponent, readComponentState } from '../registry/stores/adapters.js';
|
|
16
17
|
import { readStoresWithTruncation, readStoresRaw, storeNames } from '../registry/stores.js';
|
|
@@ -18,6 +19,7 @@ import { sanitizeWithReport } from '../security/serialization.js';
|
|
|
18
19
|
import { getCapabilities } from '../registry/capabilities.js';
|
|
19
20
|
import { freezeClock, advanceClock, resetClock, isClockFrozen } from '../timers/clock.js';
|
|
20
21
|
import { scrollContainer } from '../actions/scroll.js';
|
|
22
|
+
import { isOffViewport } from '../actions/actions-dom.js';
|
|
21
23
|
export const RELOAD_CACHE_BUST_PARAM = '_reticle_reload';
|
|
22
24
|
function str(value) {
|
|
23
25
|
return 'string' === typeof value ? value : undefined;
|
|
@@ -29,11 +31,13 @@ function record(value) {
|
|
|
29
31
|
return 'object' === typeof value && value !== null ? value : {};
|
|
30
32
|
}
|
|
31
33
|
function sourceLocation(value) {
|
|
32
|
-
if (typeof value !== 'object' || null === value)
|
|
34
|
+
if (typeof value !== 'object' || null === value) {
|
|
33
35
|
return undefined;
|
|
36
|
+
}
|
|
34
37
|
const obj = value;
|
|
35
|
-
if (typeof obj['file'] !== 'string' || typeof obj['line'] !== 'number')
|
|
38
|
+
if (typeof obj['file'] !== 'string' || typeof obj['line'] !== 'number') {
|
|
36
39
|
return undefined;
|
|
40
|
+
}
|
|
37
41
|
return { file: obj['file'], line: obj['line'], column: num(obj['column']) };
|
|
38
42
|
}
|
|
39
43
|
function queryFromArgs(args) {
|
|
@@ -56,10 +60,17 @@ function queryFromArgs(args) {
|
|
|
56
60
|
source: sourceLocation(args['source']),
|
|
57
61
|
};
|
|
58
62
|
}
|
|
59
|
-
function inspect(ref) {
|
|
63
|
+
function inspect(ref, reveal = false) {
|
|
60
64
|
const el = refs.resolve(ref);
|
|
61
|
-
if (null === el)
|
|
65
|
+
if (null === el) {
|
|
62
66
|
throw new Error(editEpoch.staleRefMessage(ref));
|
|
67
|
+
}
|
|
68
|
+
const scrolledIntoView = reveal &&
|
|
69
|
+
'function' === typeof el.scrollIntoView &&
|
|
70
|
+
isOffViewport(el, el.getBoundingClientRect());
|
|
71
|
+
if (scrolledIntoView) {
|
|
72
|
+
el.scrollIntoView({ block: 'center', inline: 'center' });
|
|
73
|
+
}
|
|
63
74
|
const rect = el.getBoundingClientRect();
|
|
64
75
|
const component = identifyComponent(el);
|
|
65
76
|
const view = el.ownerDocument.defaultView;
|
|
@@ -101,6 +112,8 @@ function inspect(ref) {
|
|
|
101
112
|
formAction: isButton(el) || isInput(el) ? (el.form?.getAttribute('action') ?? undefined) : undefined,
|
|
102
113
|
formText: isButton(el) || isInput(el) ? (el.form?.textContent ?? undefined) : undefined,
|
|
103
114
|
box: { x: rect.x, y: rect.y, width: rect.width, height: rect.height },
|
|
115
|
+
...(scrolledIntoView ? { scrolledIntoView: true } : {}),
|
|
116
|
+
...(reveal ? { [INSPECT_IN_VIEWPORT_FIELD]: !isOffViewport(el, rect) } : {}),
|
|
104
117
|
occluded: isOccluded(el, rect),
|
|
105
118
|
styles,
|
|
106
119
|
scroll,
|
|
@@ -145,8 +158,9 @@ function readState(ref, store, path, depth) {
|
|
|
145
158
|
stores,
|
|
146
159
|
storeNames: names,
|
|
147
160
|
};
|
|
148
|
-
if (truncation !== undefined)
|
|
161
|
+
if (truncation !== undefined) {
|
|
149
162
|
result.truncation = truncation;
|
|
163
|
+
}
|
|
150
164
|
if (ref !== undefined && ref.length > 0) {
|
|
151
165
|
const el = refs.resolve(ref);
|
|
152
166
|
if (null === el) {
|
|
@@ -161,8 +175,9 @@ function readState(ref, store, path, depth) {
|
|
|
161
175
|
}
|
|
162
176
|
function listAnimations() {
|
|
163
177
|
const doc = document;
|
|
164
|
-
if (typeof doc.getAnimations !== 'function')
|
|
178
|
+
if (typeof doc.getAnimations !== 'function') {
|
|
165
179
|
return { animations: [] };
|
|
180
|
+
}
|
|
166
181
|
const animations = doc.getAnimations().map((a) => {
|
|
167
182
|
const effect = a.effect;
|
|
168
183
|
const timing = effect?.getTiming();
|
|
@@ -177,8 +192,9 @@ function listAnimations() {
|
|
|
177
192
|
return { animations };
|
|
178
193
|
}
|
|
179
194
|
export function resolveNavigationUrl(rawUrl, baseUrl) {
|
|
180
|
-
if (0 === rawUrl.length || rawUrl.length > TRANSPORT_LIMITS.MAX_URL_LENGTH)
|
|
195
|
+
if (0 === rawUrl.length || rawUrl.length > TRANSPORT_LIMITS.MAX_URL_LENGTH) {
|
|
181
196
|
return null;
|
|
197
|
+
}
|
|
182
198
|
try {
|
|
183
199
|
const url = new URL(rawUrl, baseUrl);
|
|
184
200
|
return 'http:' === url.protocol || 'https:' === url.protocol ? url.toString() : null;
|
|
@@ -193,29 +209,39 @@ export function createCommandRegistry() {
|
|
|
193
209
|
scope: str(args['scope']),
|
|
194
210
|
mode: str(args['mode']) ?? SnapshotMode.FULL,
|
|
195
211
|
includeRoot: true === args['includeRoot'],
|
|
212
|
+
ids: true === args['ids'],
|
|
213
|
+
audit: true === args['audit'],
|
|
196
214
|
}));
|
|
197
215
|
reg.set(ReticleCommand.QUERY, (args) => {
|
|
198
216
|
const limit = args['limit'];
|
|
199
217
|
return runQuery(queryFromArgs(args), 'number' === typeof limit ? limit : undefined);
|
|
200
218
|
});
|
|
201
|
-
reg.set(ReticleCommand.MATCH, (args) => matchQuery(ElementQuerySchema.parse(record(args['query'])), str(args['state'])));
|
|
202
|
-
reg.set(ReticleCommand.ACT, (args) => {
|
|
219
|
+
reg.set(ReticleCommand.MATCH, (args) => matchQuery(ElementQuerySchema.parse(record(args['query'])), str(args['state']), undefined, undefined, true === args['diagnose']));
|
|
220
|
+
reg.set(ReticleCommand.ACT, async (args) => {
|
|
203
221
|
const action = str(args['action']) ?? '';
|
|
204
|
-
|
|
222
|
+
const result = await executeAction(str(args['ref']) ?? '', action, record(args['args']));
|
|
223
|
+
canvasAfterAct();
|
|
224
|
+
return result;
|
|
225
|
+
});
|
|
226
|
+
reg.set(ReticleCommand.ACT_SEQUENCE, async (args) => {
|
|
227
|
+
const result = await executeSequence((Array.isArray(args['steps']) ? args['steps'] : []));
|
|
228
|
+
canvasAfterAct();
|
|
229
|
+
return result;
|
|
205
230
|
});
|
|
206
|
-
reg.set(ReticleCommand.
|
|
207
|
-
reg.set(ReticleCommand.INSPECT, (args) => inspect(str(args['ref']) ?? ''));
|
|
231
|
+
reg.set(ReticleCommand.INSPECT, (args) => inspect(str(args['ref']) ?? '', true === args[INSPECT_REVEAL_ARG]));
|
|
208
232
|
reg.set(ReticleCommand.ANIMATIONS, () => listAnimations());
|
|
209
233
|
reg.set(ReticleCommand.CLOCK, (args) => {
|
|
210
234
|
if (true === args['reset']) {
|
|
211
235
|
resetClock();
|
|
212
236
|
}
|
|
213
237
|
else {
|
|
214
|
-
if (true === args['freeze'])
|
|
238
|
+
if (true === args['freeze']) {
|
|
215
239
|
freezeClock();
|
|
240
|
+
}
|
|
216
241
|
const adv = args['advanceMs'];
|
|
217
|
-
if ('number' === typeof adv)
|
|
242
|
+
if ('number' === typeof adv) {
|
|
218
243
|
advanceClock(adv);
|
|
244
|
+
}
|
|
219
245
|
}
|
|
220
246
|
return { frozen: isClockFrozen() };
|
|
221
247
|
});
|
|
@@ -231,11 +257,13 @@ export function createCommandRegistry() {
|
|
|
231
257
|
});
|
|
232
258
|
reg.set(ReticleCommand.NAVIGATE, (args) => {
|
|
233
259
|
const rawUrl = str(args['url']);
|
|
234
|
-
if (rawUrl === undefined || 0 === rawUrl.length)
|
|
260
|
+
if (rawUrl === undefined || 0 === rawUrl.length) {
|
|
235
261
|
return { ok: false, reason: 'url required' };
|
|
262
|
+
}
|
|
236
263
|
const url = resolveNavigationUrl(rawUrl, window.location.href);
|
|
237
|
-
if (null === url)
|
|
264
|
+
if (null === url) {
|
|
238
265
|
return { ok: false, reason: 'only http(s) navigation is allowed' };
|
|
266
|
+
}
|
|
239
267
|
window.location.assign(url);
|
|
240
268
|
return { ok: true, url };
|
|
241
269
|
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { HelloInstrumentation, type HelloEnv, type HelloScope, type RedactionConfig } from '@reticlehq/core';
|
|
2
2
|
export interface ReticleConnectOptions {
|
|
3
3
|
url?: string;
|
|
4
4
|
session?: string;
|
|
@@ -6,6 +6,10 @@ export interface ReticleConnectOptions {
|
|
|
6
6
|
token?: string;
|
|
7
7
|
allowNonLocalhost?: boolean;
|
|
8
8
|
allowInProduction?: boolean;
|
|
9
|
+
env?: HelloEnv;
|
|
10
|
+
scope?: HelloScope;
|
|
11
|
+
injected?: boolean;
|
|
12
|
+
lease?: boolean;
|
|
9
13
|
testIdAttribute?: string;
|
|
10
14
|
overlay?: boolean;
|
|
11
15
|
captureNetworkBodies?: boolean;
|
|
@@ -26,3 +30,10 @@ export interface ReticleConnectOptions {
|
|
|
26
30
|
idleEndMs?: number;
|
|
27
31
|
redact?: RedactionConfig;
|
|
28
32
|
}
|
|
33
|
+
export declare function helloMode(options: Pick<ReticleConnectOptions, 'env' | 'scope' | 'injected'>): {
|
|
34
|
+
env?: HelloEnv;
|
|
35
|
+
scope?: HelloScope;
|
|
36
|
+
instrumentation?: HelloInstrumentation;
|
|
37
|
+
};
|
|
38
|
+
export declare function shouldBlockProduction(nodeEnv: string | undefined, allowInProduction: boolean): boolean;
|
|
39
|
+
export declare function leaseToken(options: ReticleConnectOptions, current: string | undefined): string | undefined;
|
package/dist/connect-options.js
CHANGED
|
@@ -1 +1,18 @@
|
|
|
1
|
-
|
|
1
|
+
import { HelloInstrumentation, } from '@reticlehq/core';
|
|
2
|
+
export function helloMode(options) {
|
|
3
|
+
return {
|
|
4
|
+
...(options.env === undefined ? {} : { env: options.env }),
|
|
5
|
+
...(options.scope === undefined ? {} : { scope: options.scope }),
|
|
6
|
+
...(true === options.injected ? { instrumentation: HelloInstrumentation.INJECTED } : {}),
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
export function shouldBlockProduction(nodeEnv, allowInProduction) {
|
|
10
|
+
return 'production' === nodeEnv && !allowInProduction;
|
|
11
|
+
}
|
|
12
|
+
export function leaseToken(options, current) {
|
|
13
|
+
const token = options.token;
|
|
14
|
+
if (true !== options.lease || token === undefined || 0 === token.length || token === current) {
|
|
15
|
+
return undefined;
|
|
16
|
+
}
|
|
17
|
+
return token;
|
|
18
|
+
}
|
package/dist/dom/a11y.d.ts
CHANGED
|
@@ -4,6 +4,4 @@ export declare function getAccessibleName(el: Element): string;
|
|
|
4
4
|
export declare function getStates(el: Element, visible?: boolean): ElementState[];
|
|
5
5
|
export declare function isSensitiveField(el: Element): boolean;
|
|
6
6
|
export declare function getValue(el: Element): string | undefined;
|
|
7
|
-
export declare function isInViewport(el: Element, memo?: Map<Element, boolean>): boolean;
|
|
8
|
-
export declare function isVisible(el: Element, memo?: Map<Element, boolean>): boolean;
|
|
9
7
|
export declare function describe(el: Element, memo?: Map<Element, boolean>): ElementDescriptor;
|