autokap 1.0.7 → 1.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/cursors/macos.svg +4 -0
- package/assets/cursors/windows.svg +15 -0
- package/assets/skill/OPCODE-REFERENCE.md +607 -0
- package/assets/skill/README.md +39 -0
- package/assets/skill/SKILL.md +453 -468
- package/assets/skill/STUDIO-SKILL.md +476 -0
- package/assets/skill/references/examples.md +104 -0
- package/assets/skill/references/interactive-demo.md +225 -0
- package/assets/skill/references/mock-data.md +178 -0
- package/dist/action-verifier.d.ts +29 -0
- package/dist/action-verifier.js +133 -0
- package/dist/agent-action-recovery.d.ts +45 -0
- package/dist/agent-action-recovery.js +370 -0
- package/dist/agent-message-utils.d.ts +21 -0
- package/dist/agent-message-utils.js +77 -0
- package/dist/agent-url-utils.d.ts +30 -0
- package/dist/agent-url-utils.js +138 -0
- package/dist/agent.d.ts +92 -8
- package/dist/agent.js +2936 -781
- package/dist/ak-tree.d.ts +39 -0
- package/dist/ak-tree.js +368 -0
- package/dist/alt-text.d.ts +26 -0
- package/dist/alt-text.js +55 -0
- package/dist/auth-capture.d.ts +17 -0
- package/dist/auth-capture.js +164 -0
- package/dist/benchmark.d.ts +59 -0
- package/dist/benchmark.js +135 -0
- package/dist/browser-bar.d.ts +14 -6
- package/dist/browser-bar.js +145 -8
- package/dist/browser-pool.d.ts +7 -0
- package/dist/browser-pool.js +15 -5
- package/dist/browser-utils.d.ts +31 -0
- package/dist/browser-utils.js +97 -0
- package/dist/browser.d.ts +51 -1
- package/dist/browser.js +1481 -31
- package/dist/capture-alt-text.js +2 -1
- package/dist/capture-language-preflight.js +14 -0
- package/dist/capture-llm-page-identity.js +22 -10
- package/dist/capture-page-identity.d.ts +5 -7
- package/dist/capture-page-identity.js +211 -78
- package/dist/capture-preset-credentials.d.ts +50 -0
- package/dist/capture-preset-credentials.js +127 -0
- package/dist/capture-request-plan.d.ts +2 -2
- package/dist/capture-request-plan.js +64 -16
- package/dist/capture-run-optimizer.js +48 -33
- package/dist/capture-selector-memory.d.ts +5 -0
- package/dist/capture-selector-memory.js +18 -0
- package/dist/capture-strategy.d.ts +36 -0
- package/dist/capture-strategy.js +95 -0
- package/dist/capture-studio-sync.d.ts +1 -0
- package/dist/capture-studio-sync.js +9 -3
- package/dist/capture-surface-contract.d.ts +36 -0
- package/dist/capture-surface-contract.js +299 -0
- package/dist/capture-transition-engine.d.ts +28 -0
- package/dist/capture-transition-engine.js +292 -0
- package/dist/capture-variant-state.d.ts +2 -0
- package/dist/capture-variant-state.js +26 -0
- package/dist/capture-verification.d.ts +35 -0
- package/dist/capture-verification.js +95 -0
- package/dist/capture-viewport-lock.d.ts +48 -0
- package/dist/capture-viewport-lock.js +74 -0
- package/dist/circuit-breaker.d.ts +42 -0
- package/dist/circuit-breaker.js +119 -0
- package/dist/cli-config.d.ts +8 -1
- package/dist/cli-config.js +62 -6
- package/dist/cli-contract.d.ts +15 -0
- package/dist/cli-contract.js +167 -0
- package/dist/cli-runner-local.d.ts +12 -0
- package/dist/cli-runner-local.js +102 -0
- package/dist/cli-runner.d.ts +34 -0
- package/dist/cli-runner.js +433 -0
- package/dist/cli-utils.d.ts +0 -1
- package/dist/cli-utils.js +2 -5
- package/dist/cli.js +1005 -267
- package/dist/clip-orchestrator.js +9 -2
- package/dist/clip-postprocess.js +25 -16
- package/dist/cookie-dismiss.d.ts +2 -0
- package/dist/cookie-dismiss.js +48 -13
- package/dist/cost-logging.d.ts +8 -0
- package/dist/cost-logging.js +160 -46
- package/dist/cost-resolution-monitor.d.ts +16 -0
- package/dist/cost-resolution-monitor.js +34 -0
- package/dist/credential-templates.js +2 -2
- package/dist/cursor-overlay-script.d.ts +6 -0
- package/dist/cursor-overlay-script.js +169 -0
- package/dist/dom-css-purger.d.ts +65 -0
- package/dist/dom-css-purger.js +333 -0
- package/dist/dom-font-inliner.d.ts +45 -0
- package/dist/dom-font-inliner.js +148 -0
- package/dist/dom-patch-resolver.d.ts +52 -0
- package/dist/dom-patch-resolver.js +242 -0
- package/dist/dom-serializer.d.ts +82 -0
- package/dist/dom-serializer.js +378 -0
- package/dist/element-capture.d.ts +1 -41
- package/dist/element-capture.js +202 -446
- package/dist/env-validation.d.ts +5 -0
- package/dist/env-validation.js +29 -0
- package/dist/execution-schema.d.ts +4423 -0
- package/dist/execution-schema.js +507 -0
- package/dist/execution-types.d.ts +886 -0
- package/dist/execution-types.js +65 -0
- package/dist/fonts-loader.d.ts +14 -0
- package/dist/fonts-loader.js +55 -0
- package/dist/hybrid-navigator.js +12 -12
- package/dist/index.d.ts +9 -6
- package/dist/index.js +10 -4
- package/dist/legacy/agent-action-recovery.d.ts +45 -0
- package/dist/legacy/agent-action-recovery.js +370 -0
- package/dist/legacy/agent-message-utils.d.ts +21 -0
- package/dist/legacy/agent-message-utils.js +77 -0
- package/dist/legacy/agent-url-utils.d.ts +30 -0
- package/dist/legacy/agent-url-utils.js +138 -0
- package/dist/legacy/agent.d.ts +226 -0
- package/dist/legacy/agent.js +6666 -0
- package/dist/legacy/clip-orchestrator.d.ts +148 -0
- package/dist/legacy/clip-orchestrator.js +957 -0
- package/dist/legacy/credential-templates.d.ts +5 -0
- package/dist/legacy/credential-templates.js +60 -0
- package/dist/legacy/hybrid-navigator.d.ts +138 -0
- package/dist/legacy/hybrid-navigator.js +468 -0
- package/dist/legacy/llm-usage.d.ts +17 -0
- package/dist/legacy/llm-usage.js +45 -0
- package/dist/legacy/prompt-cache.d.ts +10 -0
- package/dist/legacy/prompt-cache.js +24 -0
- package/dist/legacy/prompts.d.ts +175 -0
- package/dist/legacy/prompts.js +1038 -0
- package/dist/legacy/tools.d.ts +4 -0
- package/dist/legacy/tools.js +216 -0
- package/dist/legacy/video-agent.d.ts +143 -0
- package/dist/legacy/video-agent.js +4788 -0
- package/dist/legacy/video-observation.d.ts +36 -0
- package/dist/legacy/video-observation.js +192 -0
- package/dist/legacy/video-planner.d.ts +12 -0
- package/dist/legacy/video-planner.js +501 -0
- package/dist/legacy/video-prompts.d.ts +37 -0
- package/dist/legacy/video-prompts.js +569 -0
- package/dist/legacy/video-tools.d.ts +3 -0
- package/dist/legacy/video-tools.js +59 -0
- package/dist/legacy/video-variant-state.d.ts +29 -0
- package/dist/legacy/video-variant-state.js +80 -0
- package/dist/legacy/vision-model.d.ts +17 -0
- package/dist/legacy/vision-model.js +74 -0
- package/dist/llm-healer.d.ts +63 -0
- package/dist/llm-healer.js +166 -0
- package/dist/llm-provider.d.ts +29 -0
- package/dist/llm-provider.js +80 -0
- package/dist/logger.d.ts +6 -2
- package/dist/logger.js +15 -1
- package/dist/mockup-html.js +35 -25
- package/dist/mockup.d.ts +95 -2
- package/dist/mockup.js +427 -166
- package/dist/mouse-animation.d.ts +2 -2
- package/dist/mouse-animation.js +34 -20
- package/dist/opcode-actions.d.ts +42 -0
- package/dist/opcode-actions.js +511 -0
- package/dist/opcode-runner.d.ts +51 -0
- package/dist/opcode-runner.js +770 -0
- package/dist/openrouter-client.d.ts +40 -0
- package/dist/openrouter-client.js +16 -0
- package/dist/overlay-engine.d.ts +24 -0
- package/dist/overlay-engine.js +176 -0
- package/dist/postcondition.d.ts +16 -0
- package/dist/postcondition.js +269 -0
- package/dist/program-patcher.d.ts +25 -0
- package/dist/program-patcher.js +44 -0
- package/dist/prompts.d.ts +13 -5
- package/dist/prompts.js +224 -351
- package/dist/provider-config.d.ts +12 -0
- package/dist/provider-config.js +15 -0
- package/dist/recovery-chain.d.ts +37 -0
- package/dist/recovery-chain.js +350 -0
- package/dist/remote-browser.d.ts +28 -4
- package/dist/remote-browser.js +60 -5
- package/dist/safari-browser-bar.d.ts +15 -0
- package/dist/safari-browser-bar.js +95 -0
- package/dist/safari-toolbar-asset.d.ts +15 -0
- package/dist/safari-toolbar-asset.js +12 -0
- package/dist/security.d.ts +2 -1
- package/dist/security.js +49 -10
- package/dist/selector-resolver.d.ts +34 -0
- package/dist/selector-resolver.js +181 -0
- package/dist/semantic-resolver.d.ts +35 -0
- package/dist/semantic-resolver.js +161 -0
- package/dist/server-capture-runtime.d.ts +5 -3
- package/dist/server-capture-runtime.js +42 -95
- package/dist/server-credit-usage.d.ts +2 -2
- package/dist/server-project-webhooks.d.ts +15 -1
- package/dist/server-project-webhooks.js +34 -8
- package/dist/server-screenshot-watermark.js +27 -5
- package/dist/session-profile.js +164 -1
- package/dist/sf-pro-symbols.d.ts +1 -0
- package/dist/sf-pro-symbols.js +55 -0
- package/dist/skill-packaging.d.ts +28 -0
- package/dist/skill-packaging.js +169 -0
- package/dist/smart-wait.d.ts +27 -0
- package/dist/smart-wait.js +81 -0
- package/dist/status-bar-render.d.ts +20 -0
- package/dist/status-bar-render.js +410 -0
- package/dist/status-bar.d.ts +9 -0
- package/dist/status-bar.js +298 -14
- package/dist/svg-browser-bar.d.ts +33 -0
- package/dist/svg-browser-bar.js +206 -0
- package/dist/svg-status-bar.d.ts +36 -0
- package/dist/svg-status-bar.js +597 -0
- package/dist/svg-text.d.ts +61 -0
- package/dist/svg-text.js +118 -0
- package/dist/tools.js +89 -451
- package/dist/types.d.ts +240 -5
- package/dist/types.js +23 -1
- package/dist/v2/action-verifier.d.ts +29 -0
- package/dist/v2/action-verifier.js +133 -0
- package/dist/v2/alt-text.d.ts +26 -0
- package/dist/v2/alt-text.js +55 -0
- package/dist/v2/benchmark.d.ts +59 -0
- package/dist/v2/benchmark.js +135 -0
- package/dist/v2/capture-strategy.d.ts +30 -0
- package/dist/v2/capture-strategy.js +67 -0
- package/dist/v2/capture-verification.d.ts +35 -0
- package/dist/v2/capture-verification.js +95 -0
- package/dist/v2/circuit-breaker.d.ts +42 -0
- package/dist/v2/circuit-breaker.js +119 -0
- package/dist/v2/cli-runner-local.d.ts +11 -0
- package/dist/v2/cli-runner-local.js +91 -0
- package/dist/v2/cli-runner.d.ts +34 -0
- package/dist/v2/cli-runner.js +300 -0
- package/dist/v2/compiler-prompts.d.ts +27 -0
- package/dist/v2/compiler-prompts.js +123 -0
- package/dist/v2/compiler.d.ts +37 -0
- package/dist/v2/compiler.js +147 -0
- package/dist/v2/explorer.d.ts +41 -0
- package/dist/v2/explorer.js +56 -0
- package/dist/v2/index.d.ts +37 -0
- package/dist/v2/index.js +31 -0
- package/dist/v2/llm-healer.d.ts +62 -0
- package/dist/v2/llm-healer.js +166 -0
- package/dist/v2/llm-provider.d.ts +29 -0
- package/dist/v2/llm-provider.js +80 -0
- package/dist/v2/opcode-runner.d.ts +47 -0
- package/dist/v2/opcode-runner.js +634 -0
- package/dist/v2/overlay-engine.d.ts +24 -0
- package/dist/v2/overlay-engine.js +150 -0
- package/dist/v2/postcondition.d.ts +16 -0
- package/dist/v2/postcondition.js +249 -0
- package/dist/v2/program-patcher.d.ts +25 -0
- package/dist/v2/program-patcher.js +44 -0
- package/dist/v2/recovery-chain.d.ts +30 -0
- package/dist/v2/recovery-chain.js +368 -0
- package/dist/v2/schema.d.ts +2580 -0
- package/dist/v2/schema.js +295 -0
- package/dist/v2/selector-resolver.d.ts +34 -0
- package/dist/v2/selector-resolver.js +181 -0
- package/dist/v2/semantic-resolver.d.ts +35 -0
- package/dist/v2/semantic-resolver.js +161 -0
- package/dist/v2/smart-wait.d.ts +27 -0
- package/dist/v2/smart-wait.js +81 -0
- package/dist/v2/types.d.ts +444 -0
- package/dist/v2/types.js +19 -0
- package/dist/v2/web-playwright-local.d.ts +69 -0
- package/dist/v2/web-playwright-local.js +392 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +5 -0
- package/dist/video-agent.js +18 -13
- package/dist/video-planner.js +2 -1
- package/dist/video-prompts.js +3 -3
- package/dist/web-playwright-local.d.ts +126 -0
- package/dist/web-playwright-local.js +819 -0
- package/dist/ws-auth.js +4 -1
- package/dist/ws-broadcast.d.ts +34 -0
- package/dist/ws-broadcast.js +85 -0
- package/dist/ws-connection-limits.d.ts +12 -0
- package/dist/ws-connection-limits.js +44 -0
- package/dist/ws-handler-utils.d.ts +32 -0
- package/dist/ws-handler-utils.js +139 -0
- package/dist/ws-handler.js +294 -164
- package/dist/ws-metrics-server.d.ts +9 -0
- package/dist/ws-metrics-server.js +31 -0
- package/dist/ws-server.js +41 -1
- package/package.json +51 -34
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DOM Font Inliner — AUT-121 Interactive Demos, Phase 3.4
|
|
3
|
+
*
|
|
4
|
+
* Walks captured HTML, finds `@font-face { src: url(...) }` references inside
|
|
5
|
+
* inline `<style>` blocks, fetches each font, and base64-inlines fonts whose
|
|
6
|
+
* size is below a configurable threshold (default 100kb). Larger fonts are
|
|
7
|
+
* left untouched — the Linear issue mentions "Base64 OU stockage dédié" as
|
|
8
|
+
* alternatives, so the threshold lets the caller pick whichever fits.
|
|
9
|
+
*
|
|
10
|
+
* Pure module: the actual fetching is delegated via a `fetcher` callback so
|
|
11
|
+
* this file stays dependency-free and unit-testable. Server-side wiring
|
|
12
|
+
* passes a Supabase-aware fetcher.
|
|
13
|
+
*/
|
|
14
|
+
import { parse, serialize } from 'parse5';
|
|
15
|
+
/** Maximum bytes a font can have to be inlined as base64. */
|
|
16
|
+
export const DEFAULT_FONT_INLINE_THRESHOLD = 100 * 1024;
|
|
17
|
+
/**
|
|
18
|
+
* Walk an HTML document, scan every inline `<style>` block for `@font-face`
|
|
19
|
+
* `url(...)` references, fetch and base64-inline each one whose payload size
|
|
20
|
+
* is below the threshold.
|
|
21
|
+
*/
|
|
22
|
+
export async function inlineFontFaceUrls(html, options) {
|
|
23
|
+
const stats = {
|
|
24
|
+
scanned: 0,
|
|
25
|
+
inlined: 0,
|
|
26
|
+
skipped: 0,
|
|
27
|
+
inlinedBytes: 0,
|
|
28
|
+
};
|
|
29
|
+
const document = parse(html);
|
|
30
|
+
const styleNodes = [];
|
|
31
|
+
walk(document, (node) => {
|
|
32
|
+
if (isElement(node) && node.tagName === 'style') {
|
|
33
|
+
styleNodes.push(node);
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
for (const styleNode of styleNodes) {
|
|
37
|
+
const text = readText(styleNode);
|
|
38
|
+
if (!text || text.indexOf('@font-face') === -1)
|
|
39
|
+
continue;
|
|
40
|
+
const urls = extractFontFaceUrls(text);
|
|
41
|
+
if (urls.length === 0)
|
|
42
|
+
continue;
|
|
43
|
+
stats.scanned += urls.length;
|
|
44
|
+
const replacements = new Map();
|
|
45
|
+
for (const url of urls) {
|
|
46
|
+
try {
|
|
47
|
+
const fetched = await options.fetcher(url);
|
|
48
|
+
if (!fetched) {
|
|
49
|
+
stats.skipped += 1;
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
const limit = options.thresholdBytes ?? DEFAULT_FONT_INLINE_THRESHOLD;
|
|
53
|
+
if (fetched.buffer.length > limit) {
|
|
54
|
+
stats.skipped += 1;
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
const base64 = fetched.buffer.toString('base64');
|
|
58
|
+
replacements.set(url, `data:${fetched.mimeType};base64,${base64}`);
|
|
59
|
+
stats.inlined += 1;
|
|
60
|
+
stats.inlinedBytes += fetched.buffer.length;
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
stats.skipped += 1;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (replacements.size > 0) {
|
|
67
|
+
const rewritten = rewriteCssUrls(text, replacements);
|
|
68
|
+
setText(styleNode, rewritten);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return {
|
|
72
|
+
html: serialize(document),
|
|
73
|
+
stats,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Extract every `url(...)` token that appears inside `@font-face { src: ... }`
|
|
78
|
+
* declarations. Returns the URLs in the order they appear.
|
|
79
|
+
*/
|
|
80
|
+
export function extractFontFaceUrls(css) {
|
|
81
|
+
const out = [];
|
|
82
|
+
// Match @font-face { ... } blocks (non-greedy)
|
|
83
|
+
const blockRe = /@font-face\s*\{([^}]+)\}/gi;
|
|
84
|
+
let match;
|
|
85
|
+
while ((match = blockRe.exec(css)) !== null) {
|
|
86
|
+
const body = match[1];
|
|
87
|
+
const urlRe = /url\(\s*(?:"([^"]+)"|'([^']+)'|([^\s)]+))\s*\)/gi;
|
|
88
|
+
let urlMatch;
|
|
89
|
+
while ((urlMatch = urlRe.exec(body)) !== null) {
|
|
90
|
+
const url = urlMatch[1] ?? urlMatch[2] ?? urlMatch[3] ?? '';
|
|
91
|
+
if (url && !url.startsWith('data:') && out.indexOf(url) === -1) {
|
|
92
|
+
out.push(url);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return out;
|
|
97
|
+
}
|
|
98
|
+
function rewriteCssUrls(css, replacements) {
|
|
99
|
+
return css.replace(/url\(\s*(?:"([^"]+)"|'([^']+)'|([^\s)]+))\s*\)/g, (match, quoted, quotedSingle, bare) => {
|
|
100
|
+
const original = quoted ?? quotedSingle ?? bare;
|
|
101
|
+
const replacement = replacements.get(original);
|
|
102
|
+
if (!replacement)
|
|
103
|
+
return match;
|
|
104
|
+
return `url("${replacement}")`;
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
// ── parse5 helpers (mirrors dom-serializer.ts / dom-css-purger.ts) ──
|
|
108
|
+
function isElement(node) {
|
|
109
|
+
return node.tagName !== undefined && node.attrs !== undefined;
|
|
110
|
+
}
|
|
111
|
+
function isParentNode(node) {
|
|
112
|
+
return Array.isArray(node.childNodes);
|
|
113
|
+
}
|
|
114
|
+
function walk(root, visit) {
|
|
115
|
+
const stack = [root];
|
|
116
|
+
while (stack.length > 0) {
|
|
117
|
+
const node = stack.pop();
|
|
118
|
+
visit(node);
|
|
119
|
+
if (!isParentNode(node))
|
|
120
|
+
continue;
|
|
121
|
+
for (let i = node.childNodes.length - 1; i >= 0; i -= 1) {
|
|
122
|
+
stack.push(node.childNodes[i]);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
function readText(element) {
|
|
127
|
+
let text = '';
|
|
128
|
+
for (const child of element.childNodes) {
|
|
129
|
+
const node = child;
|
|
130
|
+
if (node.nodeName === '#text' && typeof node.value === 'string') {
|
|
131
|
+
text += node.value;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return text;
|
|
135
|
+
}
|
|
136
|
+
function setText(element, value) {
|
|
137
|
+
for (let i = element.childNodes.length - 1; i >= 0; i -= 1) {
|
|
138
|
+
const child = element.childNodes[i];
|
|
139
|
+
child.parentNode = null;
|
|
140
|
+
}
|
|
141
|
+
element.childNodes = [];
|
|
142
|
+
element.childNodes.push({
|
|
143
|
+
nodeName: '#text',
|
|
144
|
+
value,
|
|
145
|
+
parentNode: element,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
//# sourceMappingURL=dom-font-inliner.js.map
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Photoshop DOM patch resolver (AUT-120, Phase 1).
|
|
3
|
+
*
|
|
4
|
+
* Resolves a DomPatchSpec.target to a live element by walking the live AKTree.
|
|
5
|
+
* Returns the AKNode's sourceRef (CSS selector) for the runner to use, or
|
|
6
|
+
* `null` if the cascade exhausts all fallbacks. Resolution is non-fatal —
|
|
7
|
+
* callers convert null into a soft-skip with a warning.
|
|
8
|
+
*
|
|
9
|
+
* Cascade order:
|
|
10
|
+
* 1. fingerprint (bounds-stripped) — best, robust to layout shifts
|
|
11
|
+
* 2. sourceRef — recorded selector, cheap and direct
|
|
12
|
+
* 3. structuralPath — CSS path from root, used when sourceRef rotted
|
|
13
|
+
* 4. exactText — last-resort match against AKNode.label
|
|
14
|
+
*
|
|
15
|
+
* On multi-match at any step, picks the closest by bounds proximity using
|
|
16
|
+
* the patch's stored `target.bounds` as the anchor.
|
|
17
|
+
*/
|
|
18
|
+
import type { DomPatchSpec, ExecutionOpcode, RuntimeAdapter } from './execution-types.js';
|
|
19
|
+
import type { AKTree } from './types.js';
|
|
20
|
+
/**
|
|
21
|
+
* Walks the live AKTree and returns the sourceRef of the best match for the
|
|
22
|
+
* patch's target, or `null` if nothing matches.
|
|
23
|
+
*/
|
|
24
|
+
export declare function resolvePatchTarget(patch: DomPatchSpec, tree: AKTree): string | null;
|
|
25
|
+
/**
|
|
26
|
+
* Builds a transient ExecutionOpcode that the runner can dispatch via
|
|
27
|
+
* `executeOpcodeCoreAction`. The opcode is marked as soft and uses the
|
|
28
|
+
* lightest postcondition so resolution failures don't propagate as errors.
|
|
29
|
+
*/
|
|
30
|
+
export declare function expandPatchToOpcode(patch: DomPatchSpec, selector: string): ExecutionOpcode | null;
|
|
31
|
+
/**
|
|
32
|
+
* Filter patches relevant to the current variant. A NULL filter ("any value")
|
|
33
|
+
* matches everything; a specific value matches exactly. The patches are
|
|
34
|
+
* returned in `ordering` order so collisions resolve deterministically.
|
|
35
|
+
*/
|
|
36
|
+
export declare function filterPatchesForVariant(patches: DomPatchSpec[] | undefined, variant: {
|
|
37
|
+
locale?: string;
|
|
38
|
+
theme?: 'light' | 'dark';
|
|
39
|
+
targetId?: string;
|
|
40
|
+
}): DomPatchSpec[];
|
|
41
|
+
/**
|
|
42
|
+
* Resolves and applies all DOM patches matching the current variant. Each
|
|
43
|
+
* patch is independent — a resolution failure logs a warning and the next
|
|
44
|
+
* patch still runs. Returns a tally of applied / skipped counts for telemetry.
|
|
45
|
+
*/
|
|
46
|
+
export declare function applyDomPatches(patches: DomPatchSpec[], adapter: RuntimeAdapter, executor: (opcode: ExecutionOpcode) => Promise<{
|
|
47
|
+
success: boolean;
|
|
48
|
+
error?: string;
|
|
49
|
+
}>, logger?: (level: 'warn' | 'info', msg: string) => void): Promise<{
|
|
50
|
+
applied: number;
|
|
51
|
+
skipped: number;
|
|
52
|
+
}>;
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Photoshop DOM patch resolver (AUT-120, Phase 1).
|
|
3
|
+
*
|
|
4
|
+
* Resolves a DomPatchSpec.target to a live element by walking the live AKTree.
|
|
5
|
+
* Returns the AKNode's sourceRef (CSS selector) for the runner to use, or
|
|
6
|
+
* `null` if the cascade exhausts all fallbacks. Resolution is non-fatal —
|
|
7
|
+
* callers convert null into a soft-skip with a warning.
|
|
8
|
+
*
|
|
9
|
+
* Cascade order:
|
|
10
|
+
* 1. fingerprint (bounds-stripped) — best, robust to layout shifts
|
|
11
|
+
* 2. sourceRef — recorded selector, cheap and direct
|
|
12
|
+
* 3. structuralPath — CSS path from root, used when sourceRef rotted
|
|
13
|
+
* 4. exactText — last-resort match against AKNode.label
|
|
14
|
+
*
|
|
15
|
+
* On multi-match at any step, picks the closest by bounds proximity using
|
|
16
|
+
* the patch's stored `target.bounds` as the anchor.
|
|
17
|
+
*/
|
|
18
|
+
import { fingerprintAKNodeForPatch, flattenAKNodes } from './ak-tree.js';
|
|
19
|
+
/**
|
|
20
|
+
* Walks the live AKTree and returns the sourceRef of the best match for the
|
|
21
|
+
* patch's target, or `null` if nothing matches.
|
|
22
|
+
*/
|
|
23
|
+
export function resolvePatchTarget(patch, tree) {
|
|
24
|
+
const nodes = flattenAKNodes(tree.root);
|
|
25
|
+
// 1. Fingerprint match (bounds-stripped) — most resilient.
|
|
26
|
+
if (patch.target.fingerprint) {
|
|
27
|
+
const matches = nodes.filter((n) => fingerprintAKNodeForPatch(toFingerprintInput(n)) === patch.target.fingerprint);
|
|
28
|
+
const picked = pickByProximity(matches, patch.target.bounds);
|
|
29
|
+
if (picked)
|
|
30
|
+
return picked.sourceRef;
|
|
31
|
+
}
|
|
32
|
+
// 2. sourceRef match against the live AKTree.
|
|
33
|
+
if (patch.target.sourceRef) {
|
|
34
|
+
const matches = nodes.filter((n) => n.sourceRef === patch.target.sourceRef);
|
|
35
|
+
const picked = pickByProximity(matches, patch.target.bounds);
|
|
36
|
+
if (picked)
|
|
37
|
+
return picked.sourceRef;
|
|
38
|
+
}
|
|
39
|
+
// 3. Structural path match against the live AKTree.
|
|
40
|
+
if (patch.target.structuralPath && patch.target.structuralPath !== patch.target.sourceRef) {
|
|
41
|
+
const matches = nodes.filter((n) => n.sourceRef === patch.target.structuralPath);
|
|
42
|
+
const picked = pickByProximity(matches, patch.target.bounds);
|
|
43
|
+
if (picked)
|
|
44
|
+
return picked.sourceRef;
|
|
45
|
+
}
|
|
46
|
+
// 4. Exact text — find the AKNode whose label matches verbatim, or
|
|
47
|
+
// contains the wanted text as a fallback (handles whitespace drift).
|
|
48
|
+
if (patch.target.exactText) {
|
|
49
|
+
const wanted = patch.target.exactText.trim();
|
|
50
|
+
if (wanted.length > 0) {
|
|
51
|
+
const exactMatches = nodes.filter((n) => n.label.trim() === wanted);
|
|
52
|
+
const exactPicked = pickByProximity(exactMatches, patch.target.bounds);
|
|
53
|
+
if (exactPicked)
|
|
54
|
+
return exactPicked.sourceRef;
|
|
55
|
+
const partialMatches = nodes.filter((n) => n.label && n.label.includes(wanted));
|
|
56
|
+
const partialPicked = pickByProximity(partialMatches, patch.target.bounds);
|
|
57
|
+
if (partialPicked)
|
|
58
|
+
return partialPicked.sourceRef;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
// LAST RESORT: trust the recorded selectors even if no AKTree node carries
|
|
62
|
+
// them. The runtime will try `document.querySelector` directly. If the page
|
|
63
|
+
// has evolved structurally this may resolve to the wrong element — the
|
|
64
|
+
// calling opcode (e.g. replaceText) will then fail loudly with the live
|
|
65
|
+
// textContent so the user can see what's actually there.
|
|
66
|
+
if (patch.target.sourceRef)
|
|
67
|
+
return patch.target.sourceRef;
|
|
68
|
+
if (patch.target.structuralPath)
|
|
69
|
+
return patch.target.structuralPath;
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Builds a transient ExecutionOpcode that the runner can dispatch via
|
|
74
|
+
* `executeOpcodeCoreAction`. The opcode is marked as soft and uses the
|
|
75
|
+
* lightest postcondition so resolution failures don't propagate as errors.
|
|
76
|
+
*/
|
|
77
|
+
export function expandPatchToOpcode(patch, selector) {
|
|
78
|
+
const base = {
|
|
79
|
+
description: `dom patch ${patch.id}`,
|
|
80
|
+
postcondition: { type: 'always' },
|
|
81
|
+
recovery: {
|
|
82
|
+
retries: 0,
|
|
83
|
+
useSelectorMemory: false,
|
|
84
|
+
useAltInteraction: false,
|
|
85
|
+
allowReload: false,
|
|
86
|
+
allowHealer: false,
|
|
87
|
+
},
|
|
88
|
+
timeoutMs: 5000,
|
|
89
|
+
maxFailures: 1,
|
|
90
|
+
};
|
|
91
|
+
switch (patch.kind) {
|
|
92
|
+
case 'REPLACE_TEXT':
|
|
93
|
+
if (patch.from === undefined || patch.to === undefined)
|
|
94
|
+
return null;
|
|
95
|
+
return {
|
|
96
|
+
kind: 'REPLACE_TEXT',
|
|
97
|
+
...base,
|
|
98
|
+
selector,
|
|
99
|
+
fromText: patch.from,
|
|
100
|
+
toText: patch.to,
|
|
101
|
+
};
|
|
102
|
+
case 'SET_TEXT':
|
|
103
|
+
if (patch.to === undefined)
|
|
104
|
+
return null;
|
|
105
|
+
return {
|
|
106
|
+
kind: 'SET_TEXT',
|
|
107
|
+
...base,
|
|
108
|
+
selector,
|
|
109
|
+
text: patch.to,
|
|
110
|
+
};
|
|
111
|
+
case 'HIDE':
|
|
112
|
+
return {
|
|
113
|
+
kind: 'HIDE',
|
|
114
|
+
...base,
|
|
115
|
+
selector,
|
|
116
|
+
};
|
|
117
|
+
case 'SET_STYLE':
|
|
118
|
+
if (!patch.property || patch.styleValue === undefined)
|
|
119
|
+
return null;
|
|
120
|
+
return {
|
|
121
|
+
kind: 'SET_STYLE',
|
|
122
|
+
...base,
|
|
123
|
+
selector,
|
|
124
|
+
property: patch.property,
|
|
125
|
+
value: patch.styleValue,
|
|
126
|
+
};
|
|
127
|
+
case 'SET_ATTRIBUTE':
|
|
128
|
+
if (!patch.attribute || patch.value === undefined)
|
|
129
|
+
return null;
|
|
130
|
+
return {
|
|
131
|
+
kind: 'SET_ATTRIBUTE',
|
|
132
|
+
...base,
|
|
133
|
+
selector,
|
|
134
|
+
attribute: patch.attribute,
|
|
135
|
+
value: patch.value,
|
|
136
|
+
};
|
|
137
|
+
case 'REMOVE_ELEMENT':
|
|
138
|
+
return {
|
|
139
|
+
kind: 'REMOVE_ELEMENT',
|
|
140
|
+
...base,
|
|
141
|
+
selector,
|
|
142
|
+
};
|
|
143
|
+
default: {
|
|
144
|
+
// Exhaustive check
|
|
145
|
+
const _exhaustive = patch.kind;
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Filter patches relevant to the current variant. A NULL filter ("any value")
|
|
152
|
+
* matches everything; a specific value matches exactly. The patches are
|
|
153
|
+
* returned in `ordering` order so collisions resolve deterministically.
|
|
154
|
+
*/
|
|
155
|
+
export function filterPatchesForVariant(patches, variant) {
|
|
156
|
+
if (!patches || patches.length === 0)
|
|
157
|
+
return [];
|
|
158
|
+
return patches
|
|
159
|
+
.filter((p) => {
|
|
160
|
+
if (p.variantLang && variant.locale && p.variantLang !== variant.locale)
|
|
161
|
+
return false;
|
|
162
|
+
if (p.variantTheme && variant.theme && p.variantTheme !== variant.theme)
|
|
163
|
+
return false;
|
|
164
|
+
if (p.variantTargetId && variant.targetId && p.variantTargetId !== variant.targetId)
|
|
165
|
+
return false;
|
|
166
|
+
return true;
|
|
167
|
+
})
|
|
168
|
+
.slice()
|
|
169
|
+
.sort((a, b) => a.ordering - b.ordering);
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Resolves and applies all DOM patches matching the current variant. Each
|
|
173
|
+
* patch is independent — a resolution failure logs a warning and the next
|
|
174
|
+
* patch still runs. Returns a tally of applied / skipped counts for telemetry.
|
|
175
|
+
*/
|
|
176
|
+
export async function applyDomPatches(patches, adapter, executor, logger) {
|
|
177
|
+
if (patches.length === 0)
|
|
178
|
+
return { applied: 0, skipped: 0 };
|
|
179
|
+
let tree = null;
|
|
180
|
+
try {
|
|
181
|
+
tree = await adapter.getAKTree();
|
|
182
|
+
}
|
|
183
|
+
catch (err) {
|
|
184
|
+
logger?.('warn', `dom-patches: failed to fetch AKTree, skipping ${patches.length} patch(es): ${err instanceof Error ? err.message : String(err)}`);
|
|
185
|
+
return { applied: 0, skipped: patches.length };
|
|
186
|
+
}
|
|
187
|
+
let applied = 0;
|
|
188
|
+
let skipped = 0;
|
|
189
|
+
for (const patch of patches) {
|
|
190
|
+
const selector = resolvePatchTarget(patch, tree);
|
|
191
|
+
if (!selector) {
|
|
192
|
+
skipped++;
|
|
193
|
+
logger?.('warn', `dom-patches: patch ${patch.id} (${patch.kind}) could not be resolved (no match)`);
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
const opcode = expandPatchToOpcode(patch, selector);
|
|
197
|
+
if (!opcode) {
|
|
198
|
+
skipped++;
|
|
199
|
+
logger?.('warn', `dom-patches: patch ${patch.id} (${patch.kind}) has incomplete payload, skipped`);
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
const result = await executor(opcode);
|
|
203
|
+
if (result.success) {
|
|
204
|
+
applied++;
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
skipped++;
|
|
208
|
+
logger?.('warn', `dom-patches: patch ${patch.id} (${patch.kind}) failed: ${result.error ?? 'unknown error'}`);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return { applied, skipped };
|
|
212
|
+
}
|
|
213
|
+
// ── Helpers ─────────────────────────────────────────────────────────
|
|
214
|
+
function toFingerprintInput(node) {
|
|
215
|
+
return {
|
|
216
|
+
tagName: node.attributes.tagName?.toLowerCase() || node.type,
|
|
217
|
+
role: node.attributes.role,
|
|
218
|
+
textContent: node.label,
|
|
219
|
+
ariaLabel: node.attributes['aria-label'],
|
|
220
|
+
htmlId: node.attributes.id,
|
|
221
|
+
name: node.attributes.name,
|
|
222
|
+
inputType: node.attributes.type,
|
|
223
|
+
href: node.attributes.href,
|
|
224
|
+
placeholder: node.attributes.placeholder,
|
|
225
|
+
bounds: node.bounds,
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
function pickByProximity(candidates, anchor) {
|
|
229
|
+
if (candidates.length === 0)
|
|
230
|
+
return null;
|
|
231
|
+
if (candidates.length === 1)
|
|
232
|
+
return candidates[0];
|
|
233
|
+
if (!anchor)
|
|
234
|
+
return candidates[0];
|
|
235
|
+
const score = (n) => {
|
|
236
|
+
const dx = n.bounds.x - anchor.x;
|
|
237
|
+
const dy = n.bounds.y - anchor.y;
|
|
238
|
+
return dx * dx + dy * dy;
|
|
239
|
+
};
|
|
240
|
+
return candidates.slice().sort((a, b) => score(a) - score(b))[0];
|
|
241
|
+
}
|
|
242
|
+
//# sourceMappingURL=dom-patch-resolver.js.map
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DOM Serializer — AUT-121 Interactive Demos
|
|
3
|
+
*
|
|
4
|
+
* Pure functions that take a raw HTML snapshot from Playwright
|
|
5
|
+
* (`document.documentElement.outerHTML`) and produce a sanitized HTML string
|
|
6
|
+
* suitable for client-side replay by the demo player.
|
|
7
|
+
*
|
|
8
|
+
* Phase 1 responsibilities:
|
|
9
|
+
* - Strip every `<script>` element (security + we never replay JS)
|
|
10
|
+
* - Strip every `on*` event handler attribute
|
|
11
|
+
* - Strip cross-origin `<iframe>` elements (XSS surface)
|
|
12
|
+
* - Strip `<link rel="modulepreload">` and `<link rel="preload" as="script">`
|
|
13
|
+
* - Strip framework dev overlays (Next.js, Vite, Astro, Nuxt) — same set as
|
|
14
|
+
* `cookie-dismiss.ts`'s `DEV_TOOL_SELECTORS`, just removed instead of hidden
|
|
15
|
+
* - Collect every asset URL referenced from `<img src|srcset>`,
|
|
16
|
+
* `<source src|srcset>`, `<link rel="stylesheet" href>`. Phase 3 will
|
|
17
|
+
* resolve these via CAS — Phase 1 only records them for telemetry.
|
|
18
|
+
*
|
|
19
|
+
* Out of scope for Phase 1: PurgeCSS, font inlining, CSS `url(...)` extraction,
|
|
20
|
+
* Brotli. Those land in Phase 3.
|
|
21
|
+
*/
|
|
22
|
+
export interface RawDomCapture {
|
|
23
|
+
/** Raw HTML, typically `document.documentElement.outerHTML` from Playwright */
|
|
24
|
+
html: string;
|
|
25
|
+
/** Page URL at the moment of capture, used to resolve relative asset URLs and same-origin iframes */
|
|
26
|
+
baseUrl: string;
|
|
27
|
+
/** Viewport dimensions of the captured page */
|
|
28
|
+
viewport: {
|
|
29
|
+
width: number;
|
|
30
|
+
height: number;
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
export interface SerializedDom {
|
|
34
|
+
/** Post-sanitization HTML, ready to be replayed by the demo player */
|
|
35
|
+
html: string;
|
|
36
|
+
/** Absolute URLs of assets referenced by the captured DOM */
|
|
37
|
+
assetUrls: string[];
|
|
38
|
+
/** Byte length of the sanitized HTML */
|
|
39
|
+
htmlBytes: number;
|
|
40
|
+
/** Stats counters for telemetry */
|
|
41
|
+
removed: {
|
|
42
|
+
scripts: number;
|
|
43
|
+
eventHandlers: number;
|
|
44
|
+
iframes: number;
|
|
45
|
+
devOverlays: number;
|
|
46
|
+
preloads: number;
|
|
47
|
+
};
|
|
48
|
+
/** How many `<a>` / `<form>` navigation targets were neutralized */
|
|
49
|
+
neutralized: {
|
|
50
|
+
links: number;
|
|
51
|
+
forms: number;
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Rewrite asset URLs (img/source/srcset, link rel=stylesheet) inside an HTML
|
|
56
|
+
* string using a `originalUrl → replacementUrl` map. URLs not in the map are
|
|
57
|
+
* left untouched. Used by Phase 3 to point captured DOMs at CAS-backed
|
|
58
|
+
* permanent URLs after asset extraction.
|
|
59
|
+
*/
|
|
60
|
+
export declare function rewriteAssetUrls(html: string, replacements: Map<string, string>): string;
|
|
61
|
+
/** Sanitize a raw DOM snapshot and return the cleaned HTML + asset manifest. */
|
|
62
|
+
export declare function sanitizeDom(raw: RawDomCapture): SerializedDom;
|
|
63
|
+
export interface RawFragmentCapture {
|
|
64
|
+
/** Raw `outerHTML` of the captured subtree (a single root element). */
|
|
65
|
+
html: string;
|
|
66
|
+
/** Page URL at the moment of capture, used to resolve relative asset URLs. */
|
|
67
|
+
baseUrl: string;
|
|
68
|
+
}
|
|
69
|
+
export interface SerializedFragment {
|
|
70
|
+
html: string;
|
|
71
|
+
assetUrls: string[];
|
|
72
|
+
htmlBytes: number;
|
|
73
|
+
removed: SerializedDom['removed'];
|
|
74
|
+
neutralized: SerializedDom['neutralized'];
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Sanitize a single subtree captured via `CAPTURE_FRAGMENT`. Same passes as
|
|
78
|
+
* {@link sanitizeDom} (strip scripts, on*, dev overlays, third-party iframes,
|
|
79
|
+
* neutralize anchors / forms, collect asset URLs) but operates on a fragment
|
|
80
|
+
* — no `<html>` / `<head>` / `<body>` shell. Phase 5 of AUT-121.
|
|
81
|
+
*/
|
|
82
|
+
export declare function sanitizeFragment(raw: RawFragmentCapture): SerializedFragment;
|