@omnivoice/page-agent 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,203 @@
1
+ import { c as controlsOf, r as refOf, i as isRequired, t as typeOfInput, a as attrOf, s as signatureOf, b as accessibleNameWithSourceOf, h as hash64 } from "./brain-extension-DDuKTYrJ.js";
2
+ class NoSignatureError extends Error {
3
+ constructor() {
4
+ super(...arguments);
5
+ this.name = "NoSignatureError";
6
+ }
7
+ }
8
+ const TIPOS_QUE_SAO_ACAO = /* @__PURE__ */ new Set(["submit", "button", "reset", "image"]);
9
+ const LANDMARKS_POR_TAG = { nav: "navigation", main: "main", aside: "complementary", dialog: "dialog" };
10
+ const LANDMARKS_POR_PAPEL = /* @__PURE__ */ new Set(["navigation", "main", "complementary", "banner", "contentinfo", "search", "region", "dialog"]);
11
+ const PAPEIS_AO_VIVO = /* @__PURE__ */ new Set(["status", "alert", "log"]);
12
+ function textoCru(tree, id) {
13
+ const n = tree.map[id];
14
+ if (!n) return "";
15
+ if (n.type === "TEXT_NODE") return n.text ?? "";
16
+ return (n.children ?? []).map((f) => textoCru(tree, f)).join("");
17
+ }
18
+ function formaDasOpcoes(tree, c) {
19
+ const tag = c.node.tagName.toLowerCase();
20
+ let raiz;
21
+ if (tag === "select") raiz = c.node;
22
+ else if (tag === "input" && c.node.attributes?.list) {
23
+ raiz = Object.values(tree.map).find((n) => n.tagName?.toLowerCase() === "datalist" && attrOf(n, "id") === c.node.attributes.list);
24
+ }
25
+ if (!raiz) return void 0;
26
+ let contagem = 0;
27
+ let primeira;
28
+ const coletar = (id) => {
29
+ const n = tree.map[id];
30
+ if (!n) return;
31
+ if (n.tagName?.toLowerCase() === "option") {
32
+ if (contagem === 0) primeira = textoCru(tree, id).trim();
33
+ contagem++;
34
+ return;
35
+ }
36
+ for (const filho of n.children ?? []) coletar(filho);
37
+ };
38
+ for (const filho of raiz.children ?? []) coletar(filho);
39
+ if (contagem === 0) return void 0;
40
+ return { optionCount: contagem, hasPlaceholderOption: primeira === "" || /^(escolh|selecion|select|choose|-{2}|—|\.{3})/i.test(primeira ?? "") };
41
+ }
42
+ function declaredOf(tree, c) {
43
+ const a = c.node.attributes ?? {};
44
+ const declared = {};
45
+ const inteiro = (valor) => {
46
+ const n = valor === void 0 || valor.trim() === "" ? NaN : Number(valor);
47
+ return Number.isInteger(n) && n >= 0 ? n : void 0;
48
+ };
49
+ if (a.pattern !== void 0) declared.pattern = a.pattern;
50
+ const max = inteiro(a.maxlength);
51
+ if (max !== void 0) declared.maxLength = max;
52
+ const min = inteiro(a.minlength);
53
+ if (min !== void 0) declared.minLength = min;
54
+ if (a.inputmode) declared.inputMode = a.inputmode;
55
+ if (a.autocomplete) declared.autocomplete = a.autocomplete;
56
+ if (a.step !== void 0) declared.step = a.step;
57
+ if (a.min !== void 0) declared.min = a.min;
58
+ if (a.max !== void 0) declared.max = a.max;
59
+ const opcoes = formaDasOpcoes(tree, c);
60
+ if (opcoes) Object.assign(declared, opcoes);
61
+ return declared;
62
+ }
63
+ function isField(c) {
64
+ const tag = c.node.tagName.toLowerCase();
65
+ if (tag === "select" || tag === "textarea") return true;
66
+ return tag === "input" && !TIPOS_QUE_SAO_ACAO.has(typeOfInput(c.node)) && typeOfInput(c.node) !== "hidden";
67
+ }
68
+ function actionKind(c) {
69
+ const tag = c.node.tagName.toLowerCase();
70
+ const tipo = c.node.attributes?.type?.toLowerCase();
71
+ const dentroDeForm = c.form !== void 0 || !!c.node.ref?.closest?.("form");
72
+ if (tag === "input" && TIPOS_QUE_SAO_ACAO.has(typeOfInput(c.node))) {
73
+ return typeOfInput(c.node) === "submit" || typeOfInput(c.node) === "image" ? "submit" : "button";
74
+ }
75
+ if (tag === "button") return tipo === "submit" || tipo === void 0 && dentroDeForm ? "submit" : "button";
76
+ if (tag === "a" && c.node.attributes?.href !== void 0) return "link";
77
+ if (c.node.attributes?.role === "button") return "button";
78
+ return null;
79
+ }
80
+ function descrever(nodo, tag, papel, ordinal) {
81
+ const d = { frame: "main", tag, accessibleName: "", ordinal };
82
+ if (nodo.ref) {
83
+ const { name, nameFromContent } = accessibleNameWithSourceOf(nodo.ref);
84
+ if (nameFromContent) {
85
+ d.nameFromContent = true;
86
+ d.accessibleNameHash = hash64(name);
87
+ } else d.accessibleName = name;
88
+ }
89
+ if (papel) d.role = papel;
90
+ return d;
91
+ }
92
+ function percorrer(tree) {
93
+ const vistos = /* @__PURE__ */ new Set();
94
+ const ordem = [];
95
+ const visitar = (id) => {
96
+ const n = tree.map[id];
97
+ if (!n || vistos.has(id)) return;
98
+ vistos.add(id);
99
+ if (n.type !== "TEXT_NODE") {
100
+ ordem.push(n);
101
+ for (const filho of n.children ?? []) visitar(filho);
102
+ }
103
+ };
104
+ visitar(tree.rootId);
105
+ return ordem;
106
+ }
107
+ function rolesOf(nodo) {
108
+ return { papel: attrOf(nodo, "role"), tag: (nodo.tagName ?? "").toLowerCase() };
109
+ }
110
+ function rolaSozinho(el) {
111
+ if (el === el.ownerDocument.body || el === el.ownerDocument.documentElement) return false;
112
+ const excedeY = el.scrollHeight > el.clientHeight + 1;
113
+ const excedeX = el.scrollWidth > el.clientWidth + 1;
114
+ if (!excedeY && !excedeX) return false;
115
+ const estilo = el.ownerDocument.defaultView?.getComputedStyle(el);
116
+ if (!estilo) return false;
117
+ const rola = (v) => v === "auto" || v === "scroll";
118
+ return excedeY && rola(estilo.overflowY) || excedeX && rola(estilo.overflowX);
119
+ }
120
+ function profileLevel1(tree, url, signal, assinatura) {
121
+ signal?.throwIfAborted();
122
+ const signature = assinatura ?? assinar(tree, url);
123
+ const controles = controlsOf(tree);
124
+ const refs = controles.map((c) => refOf(c));
125
+ const candidatos = controles.map((c, i) => ({ c, i })).filter(({ c }) => c.node.tagName.toLowerCase() !== "label");
126
+ const duplicatasDe = (indice) => {
127
+ const nome = controles[indice].accessibleName;
128
+ if (!nome) return [];
129
+ return candidatos.filter((o) => o.i !== indice && o.c.accessibleName === nome).map((o) => refs[o.i]);
130
+ };
131
+ const fields = [];
132
+ const actions = [];
133
+ controles.forEach((c, i) => {
134
+ if (isField(c)) {
135
+ const tag = c.node.tagName.toLowerCase();
136
+ const campo = { ref: refs[i], tagName: tag, required: isRequired(c.node), declared: declaredOf(tree, c) };
137
+ if (tag === "input") campo.type = typeOfInput(c.node);
138
+ const duplicatas = duplicatasDe(i);
139
+ if (duplicatas.length) campo.duplicates = duplicatas;
140
+ fields.push(campo);
141
+ return;
142
+ }
143
+ const kind = actionKind(c);
144
+ if (!kind) return;
145
+ const acao = {
146
+ ref: refs[i],
147
+ kind,
148
+ declaredDisabled: c.node.attributes?.disabled !== void 0 || c.node.attributes?.["aria-disabled"] === "true"
149
+ };
150
+ if (c.form) acao.form = c.form;
151
+ actions.push(acao);
152
+ });
153
+ const landmarks = /* @__PURE__ */ new Set();
154
+ const scrollContainers = [];
155
+ const liveRegions = [];
156
+ const limits = /* @__PURE__ */ new Set();
157
+ let temCanvas = false;
158
+ for (const nodo of percorrer(tree)) {
159
+ const { papel, tag } = rolesOf(nodo);
160
+ const aoVivo = attrOf(nodo, "aria-live");
161
+ if (aoVivo && aoVivo !== "off" || papel && PAPEIS_AO_VIVO.has(papel)) {
162
+ liveRegions.push(descrever(nodo, tag, papel, liveRegions.length));
163
+ }
164
+ if (nodo.isVisible === false) continue;
165
+ if (papel && LANDMARKS_POR_PAPEL.has(papel)) landmarks.add(papel);
166
+ else if (LANDMARKS_POR_TAG[tag]) landmarks.add(LANDMARKS_POR_TAG[tag]);
167
+ if (nodo.ref && rolaSozinho(nodo.ref)) scrollContainers.push(descrever(nodo, tag, papel, scrollContainers.length));
168
+ if (tag === "canvas") temCanvas = true;
169
+ if (tag === "iframe") {
170
+ let alcancavel = false;
171
+ try {
172
+ alcancavel = !!nodo.ref?.contentDocument;
173
+ } catch {
174
+ }
175
+ if (nodo.ref && !alcancavel) limits.add("cross_origin_iframe");
176
+ }
177
+ }
178
+ if (temCanvas && fields.length === 0) limits.add("canvas_only");
179
+ return {
180
+ signature,
181
+ capturedAt: Date.now(),
182
+ level: 1,
183
+ fields,
184
+ actions,
185
+ landmarks: [...landmarks].sort(),
186
+ scrollContainers,
187
+ liveRegions,
188
+ limits: [...limits]
189
+ };
190
+ }
191
+ function assinar(tree, url) {
192
+ try {
193
+ return signatureOf(tree, url);
194
+ } catch (erro) {
195
+ if (controlsOf(tree).length === 0) throw new NoSignatureError("superfície sem controles interativos; tentar de novo depois");
196
+ throw erro;
197
+ }
198
+ }
199
+ export {
200
+ NoSignatureError,
201
+ profileLevel1
202
+ };
203
+ //# sourceMappingURL=profiler-CcdRV96d.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"profiler-CcdRV96d.js","sources":["../../src/brain/profiler.ts"],"sourcesContent":["// Perfilador da superfície, em DOIS níveis com contratos opostos. Onde um termina e o outro começa:\n// a seção \"Nível 2: eco reversível\" mais abaixo.\n//\n// NÍVEL 1 (`profileLevel1`, `scheduleProfile`): leitura ESTÁTICA do que a página declara sobre cada\n// controle. É função da árvore achatada que o agente já vê (PageController.readFlatTree) e, onde a árvore\n// não alcança (overflow computado, nome de região), de atributos do elemento real. Não escreve, não\n// foca, não clica, não submete, e nenhum valor digitado por pessoa é lido ou gravado.\n//\n// NÍVEL 2 (`probeField`): o ÚNICO ponto que escreve num campo real da página. Lê o valor atual\n// (possivelmente digitado por pessoa) só em variável local, escreve uma amostra, relê, e restaura em\n// `try/finally`. A recusa (senha, cartão, [data-page-agent-ignore]) é decidida ANTES da primeira\n// escrita. Dispara apenas `input` e `change`; nunca submete. Nada do que ele lê entra num perfil.\n//\n// A árvore do fork só copia atributos de candidatos a interação; `<form>`, `<option>` e rótulos chegam\n// vazios, mas carregam `ref`. Ver `attrOf` em surface.ts.\nimport type { FlatDomTree, InteractiveElementDomNode } from \"../../vendor/page-agent/packages/page-controller/src/dom/dom_tree/type\";\nimport type { BrainStore, ActionProfile, DescriptiveRef, FieldProfile, ObservedEcho, PageProfile } from \"./store\";\nimport {\n accessibleNameWithSourceOf, attrOf, closestAtravessando, controlsOf, hash64, isRequired, refOf, resolverRef, signatureOf, typeOfInput,\n type Control, type StableRef, type SurfaceSignature,\n} from \"./surface\";\n\n/** A superfície não tem controles interativos: ainda não há assinatura. Tentar de novo mais tarde. */\nexport class NoSignatureError extends Error {\n override name = \"NoSignatureError\";\n}\n\n/** O perfil foi descartado (sinal de aborto ou página re-renderizada). `name` é \"AbortError\". */\nexport class ProfileAbortedError extends Error {\n override name = \"AbortError\";\n}\n\ntype Nodo = {\n tagName?: string; type?: string; text?: string; attributes?: Record<string, string>;\n children?: string[]; isVisible?: boolean; ref?: HTMLElement;\n};\n\nconst TIPOS_QUE_SAO_ACAO = new Set([\"submit\", \"button\", \"reset\", \"image\"]);\nconst LANDMARKS_POR_TAG: Record<string, string> = { nav: \"navigation\", main: \"main\", aside: \"complementary\", dialog: \"dialog\" };\nconst LANDMARKS_POR_PAPEL = new Set([\"navigation\", \"main\", \"complementary\", \"banner\", \"contentinfo\", \"search\", \"region\", \"dialog\"]);\nconst PAPEIS_AO_VIVO = new Set([\"status\", \"alert\", \"log\"]);\n\nfunction textoCru(tree: FlatDomTree, id: string): string {\n const n = tree.map[id] as Nodo | undefined;\n if (!n) return \"\";\n if (n.type === \"TEXT_NODE\") return n.text ?? \"\";\n return (n.children ?? []).map(f => textoCru(tree, f)).join(\"\");\n}\n\n// ---------- opções de select/datalist ----------\n// Só CONTAGEM e presença de placeholder; o TEXTO das opções nunca é lido para o perfil. Um `<select>`\n// preenchido pelo servidor pode listar endereços salvos ou titulares de conta, e não há como provar\n// pelo DOM que um select é estático. A seleção acontece lendo o DOM vivo na hora de executar.\nfunction formaDasOpcoes(tree: FlatDomTree, c: Control): { optionCount: number; hasPlaceholderOption: boolean } | undefined {\n const tag = c.node.tagName.toLowerCase();\n let raiz: Nodo | undefined;\n if (tag === \"select\") raiz = c.node as Nodo;\n else if (tag === \"input\" && c.node.attributes?.list) {\n raiz = Object.values(tree.map).find(n => (n as Nodo).tagName?.toLowerCase() === \"datalist\" && attrOf(n as Nodo, \"id\") === c.node.attributes!.list) as Nodo | undefined;\n }\n if (!raiz) return undefined;\n let contagem = 0;\n let primeira: string | undefined;\n const coletar = (id: string) => {\n const n = tree.map[id] as Nodo | undefined;\n if (!n) return;\n if (n.tagName?.toLowerCase() === \"option\") {\n // Texto consultado só para testar \"vazio\"; não sai desta função.\n if (contagem === 0) primeira = textoCru(tree, id).trim();\n contagem++;\n return;\n }\n for (const filho of n.children ?? []) coletar(filho);\n };\n for (const filho of raiz.children ?? []) coletar(filho);\n if (contagem === 0) return undefined;\n // Placeholder: primeira opção vazia ou do tipo \"Escolha...\". O fork não traz `value` de <option>.\n return { optionCount: contagem, hasPlaceholderOption: primeira === \"\" || /^(escolh|selecion|select|choose|-{2}|—|\\.{3})/i.test(primeira ?? \"\") };\n}\n\n// ---------- campos e ações ----------\nfunction declaredOf(tree: FlatDomTree, c: Control): FieldProfile[\"declared\"] {\n const a = c.node.attributes ?? {};\n const declared: FieldProfile[\"declared\"] = {};\n const inteiro = (valor: string | undefined) => {\n const n = valor === undefined || valor.trim() === \"\" ? NaN : Number(valor);\n return Number.isInteger(n) && n >= 0 ? n : undefined;\n };\n if (a.pattern !== undefined) declared.pattern = a.pattern;\n const max = inteiro(a.maxlength); if (max !== undefined) declared.maxLength = max;\n const min = inteiro(a.minlength); if (min !== undefined) declared.minLength = min;\n if (a.inputmode) declared.inputMode = a.inputmode;\n if (a.autocomplete) declared.autocomplete = a.autocomplete;\n if (a.step !== undefined) declared.step = a.step;\n if (a.min !== undefined) declared.min = a.min;\n if (a.max !== undefined) declared.max = a.max;\n const opcoes = formaDasOpcoes(tree, c);\n if (opcoes) Object.assign(declared, opcoes);\n return declared;\n}\n\nfunction isField(c: Control): boolean {\n const tag = c.node.tagName.toLowerCase();\n if (tag === \"select\" || tag === \"textarea\") return true;\n return tag === \"input\" && !TIPOS_QUE_SAO_ACAO.has(typeOfInput(c.node)) && typeOfInput(c.node) !== \"hidden\";\n}\n\nfunction actionKind(c: Control): ActionProfile[\"kind\"] | null {\n const tag = c.node.tagName.toLowerCase();\n const tipo = c.node.attributes?.type?.toLowerCase();\n const dentroDeForm = c.form !== undefined || !!c.node.ref?.closest?.(\"form\");\n if (tag === \"input\" && TIPOS_QUE_SAO_ACAO.has(typeOfInput(c.node))) {\n return typeOfInput(c.node) === \"submit\" || typeOfInput(c.node) === \"image\" ? \"submit\" : \"button\";\n }\n if (tag === \"button\") return tipo === \"submit\" || (tipo === undefined && dentroDeForm) ? \"submit\" : \"button\";\n if (tag === \"a\" && c.node.attributes?.href !== undefined) return \"link\";\n if (c.node.attributes?.role === \"button\") return \"button\";\n return null;\n}\n\n// ---------- referências auxiliares ----------\n// Descritivas, não resolvíveis (ver DescriptiveRef em store.ts): papel só se declarado, ordinal na lista.\nfunction descrever(nodo: Nodo, tag: string, papel: string | undefined, ordinal: number): DescriptiveRef {\n const d: DescriptiveRef = { frame: \"main\", tag, accessibleName: \"\", ordinal };\n if (nodo.ref) {\n // Mesma regra de refOf: texto de conteúdo (região que seja button/link) persiste só como hash.\n const { name, nameFromContent } = accessibleNameWithSourceOf(nodo.ref);\n if (nameFromContent) { d.nameFromContent = true; d.accessibleNameHash = hash64(name); }\n else d.accessibleName = name;\n }\n if (papel) d.role = papel;\n return d;\n}\n\nfunction percorrer(tree: FlatDomTree): Nodo[] {\n const vistos = new Set<string>();\n const ordem: Nodo[] = [];\n const visitar = (id: string) => {\n const n = tree.map[id] as Nodo | undefined;\n if (!n || vistos.has(id)) return;\n vistos.add(id);\n if (n.type !== \"TEXT_NODE\") {\n ordem.push(n);\n for (const filho of n.children ?? []) visitar(filho);\n }\n };\n visitar(tree.rootId);\n return ordem;\n}\n\nfunction rolesOf(nodo: Nodo): { papel: string | undefined; tag: string } {\n return { papel: attrOf(nodo, \"role\"), tag: (nodo.tagName ?? \"\").toLowerCase() };\n}\n\nfunction rolaSozinho(el: HTMLElement): boolean {\n if (el === el.ownerDocument.body || el === el.ownerDocument.documentElement) return false;\n const excedeY = el.scrollHeight > el.clientHeight + 1;\n const excedeX = el.scrollWidth > el.clientWidth + 1;\n if (!excedeY && !excedeX) return false;\n const estilo = el.ownerDocument.defaultView?.getComputedStyle(el);\n if (!estilo) return false;\n const rola = (v: string) => v === \"auto\" || v === \"scroll\";\n return (excedeY && rola(estilo.overflowY)) || (excedeX && rola(estilo.overflowX));\n}\n\n/**\n * Perfil nível 1 de uma árvore já capturada. Síncrono: descreve UMA árvore, nunca duas.\n * Lança NoSignatureError se a superfície não tem controles (ainda não há identidade a perfilar).\n */\nexport function profileLevel1(tree: FlatDomTree, url: string, signal?: AbortSignal, assinatura?: SurfaceSignature): PageProfile {\n signal?.throwIfAborted();\n // `scheduleProfile` passa a assinatura que já calculou: a chave de cache e o perfil gravado são a MESMA.\n const signature = assinatura ?? assinar(tree, url);\n const controles = controlsOf(tree);\n const refs = controles.map(c => refOf(c));\n\n // Nunca compara um controle com `<label>` (a árvore marca rótulos como interativos) nem consigo mesmo.\n const candidatos = controles.map((c, i) => ({ c, i })).filter(({ c }) => c.node.tagName.toLowerCase() !== \"label\");\n const duplicatasDe = (indice: number): StableRef[] => {\n const nome = controles[indice].accessibleName;\n if (!nome) return [];\n return candidatos.filter(o => o.i !== indice && o.c.accessibleName === nome).map(o => refs[o.i]);\n };\n\n const fields: FieldProfile[] = [];\n const actions: ActionProfile[] = [];\n controles.forEach((c, i) => {\n if (isField(c)) {\n const tag = c.node.tagName.toLowerCase();\n const campo: FieldProfile = { ref: refs[i], tagName: tag, required: isRequired(c.node), declared: declaredOf(tree, c) };\n if (tag === \"input\") campo.type = typeOfInput(c.node);\n const duplicatas = duplicatasDe(i);\n if (duplicatas.length) campo.duplicates = duplicatas;\n fields.push(campo);\n return;\n }\n const kind = actionKind(c);\n if (!kind) return;\n const acao: ActionProfile = {\n ref: refs[i], kind,\n declaredDisabled: c.node.attributes?.disabled !== undefined || c.node.attributes?.[\"aria-disabled\"] === \"true\",\n };\n if (c.form) acao.form = c.form;\n actions.push(acao);\n });\n\n const landmarks = new Set<string>();\n const scrollContainers: DescriptiveRef[] = [];\n const liveRegions: DescriptiveRef[] = [];\n const limits = new Set<PageProfile[\"limits\"][number]>();\n let temCanvas = false;\n for (const nodo of percorrer(tree)) {\n const { papel, tag } = rolesOf(nodo);\n // Regiões ao vivo existem mesmo ocultas (um `role=status` oculto até receber a mensagem).\n const aoVivo = attrOf(nodo, \"aria-live\");\n if ((aoVivo && aoVivo !== \"off\") || (papel && PAPEIS_AO_VIVO.has(papel))) {\n liveRegions.push(descrever(nodo, tag, papel, liveRegions.length));\n }\n if (nodo.isVisible === false) continue;\n if (papel && LANDMARKS_POR_PAPEL.has(papel)) landmarks.add(papel);\n else if (LANDMARKS_POR_TAG[tag]) landmarks.add(LANDMARKS_POR_TAG[tag]);\n if (nodo.ref && rolaSozinho(nodo.ref)) scrollContainers.push(descrever(nodo, tag, papel, scrollContainers.length));\n if (tag === \"canvas\") temCanvas = true;\n if (tag === \"iframe\") {\n let alcancavel = false;\n try { alcancavel = !!(nodo.ref as HTMLIFrameElement | undefined)?.contentDocument; } catch { /* cross-origin */ }\n if (nodo.ref && !alcancavel) limits.add(\"cross_origin_iframe\");\n }\n }\n if (temCanvas && fields.length === 0) limits.add(\"canvas_only\");\n\n return {\n signature, capturedAt: Date.now(), level: 1, fields, actions,\n landmarks: [...landmarks].sort(), scrollContainers, liveRegions, limits: [...limits],\n };\n}\n\n// `signatureOf` lança quando não há controles. Isso é \"ainda sem assinatura\", um erro TIPADO que o\n// chamador distingue de \"store indisponível\" (BrainStore.open() === null) e de bugs reais (repassados).\nfunction assinar(tree: FlatDomTree, url: string): SurfaceSignature {\n try {\n return signatureOf(tree, url);\n } catch (erro) {\n if (controlsOf(tree).length === 0) throw new NoSignatureError(\"superfície sem controles interativos; tentar de novo depois\");\n throw erro;\n }\n}\n\n// ---------- agendamento ----------\n// Cache da sessão. A chave é a tupla inteira porque os hashes não carregam origin/rota/frame.\n// Guarda o PERFIL, não a decisão de gravar: a primeira chamada da sessão pode ter vindo sem store\n// (`BrainStore.open()` devolveu null naquele instante, ou o chamador ainda não tinha um), e isso não\n// pode decidir pelo resto da sessão. `persistido` vira true quando uma chamada COM store TENTA\n// gravar — a marca é posta antes do await. Como `putProfile` degrada em silêncio (contrato do store),\n// cota esgotada deixa `persistido: true` sem nada gravado e sem nova tentativa nesta sessão.\ntype Agendado = { tarefa: Promise<PageProfile>; persistido: boolean };\nconst jaPerfilados = new Map<string, Agendado>();\n\n// O AbortSignal é de QUEM CHAMOU, nunca da tarefa: a tarefa é compartilhada por assinatura, e\n// abortá-la cancelaria o perfil para todos os outros chamadores. Cada chamador corre o seu próprio\n// sinal contra a tarefa; perdendo, ele rejeita e a tarefa segue, deixando o perfil no cache para quem\n// mais o esperava.\nfunction corridaComSinal<T>(tarefa: Promise<T>, signal?: AbortSignal): Promise<T> {\n if (!signal) return tarefa;\n return new Promise<T>((resolve, reject) => {\n const abortar = () => reject(new ProfileAbortedError(\"perfil abortado\"));\n if (signal.aborted) return abortar();\n signal.addEventListener(\"abort\", abortar, { once: true });\n tarefa.then(resolve, reject).finally(() => signal.removeEventListener(\"abort\", abortar));\n });\n}\n\nfunction chaveDe(s: SurfaceSignature): string {\n return JSON.stringify([s.origin, s.routePattern, s.frame, s.structuralHash]);\n}\n\nfunction quandoOcioso(): Promise<void> {\n return new Promise(resolve => {\n const ric = (globalThis as { requestIdleCallback?: (cb: () => void, opts?: { timeout: number }) => void }).requestIdleCallback;\n if (ric) ric(() => resolve(), { timeout: 2000 });\n else setTimeout(resolve, 0);\n });\n}\n\n// Uma árvore capturada antes do ocioso pode já estar velha. Dois testes, do mais barato ao mais forte:\n// (1) algum `ref` de controle saiu do documento (o nó foi substituído); (2) se o chamador forneceu\n// `rereadRenumberingAgentIndexes`, o structuralHash recalculado difere do inicial (mudou no lugar: rótulo, tipo, required...).\n// Isso NÃO impede toda corrida: um re-render depois da última checagem e antes do `put` ainda passa. Mas\n// o perfil sempre descreve uma só árvore (profileLevel1 é síncrono) e vai sob a assinatura dela, então o\n// pior caso é um perfil coerente de uma estrutura que acabou de mudar, nunca um perfil misturado.\nasync function aindaVale(tree: FlatDomTree, assinatura: SurfaceSignature, rereadRenumberingAgentIndexes?: () => FlatDomTree | null | Promise<FlatDomTree | null>): Promise<boolean> {\n if (controlsOf(tree).some(c => c.node.ref && !c.node.ref.isConnected)) return false;\n if (!rereadRenumberingAgentIndexes) return true;\n const fresca = await rereadRenumberingAgentIndexes();\n if (!fresca) return false;\n try {\n return signatureOf(fresca, \"about:blank\").structuralHash === assinatura.structuralHash;\n } catch {\n return false; // a superfície esvaziou\n }\n}\n\n/**\n * Perfila em `requestIdleCallback`, uma vez por assinatura de superfície por sessão, e grava em\n * `store` quando ele existe (e não foi fechado). Com `store === null` o perfil só é devolvido.\n *\n * O CACHE é do perfil, não da gravação: uma chamada posterior que traga um store grava o perfil que\n * uma chamada anterior calculou sem store — uma vez só. Quem chama de pontos diferentes (`bridge.ts`)\n * não precisa garantir que a primeira chamada da sessão já tivesse store.\n *\n * O SINAL é do chamador: ele rejeita a SUA chamada e não cancela a tarefa compartilhada, então o\n * aborto de um chamador não tira o perfil dos outros. Quem aborta não grava nada.\n *\n * Rejeita com NoSignatureError (sem controles; chame de novo depois) ou ProfileAbortedError (sinal do\n * próprio chamador, ou re-render durante a varredura; no re-render nada foi gravado por ninguém).\n * Chamadores \"dispare e esqueça\" devem encadear `.catch`.\n * `rereadRenumberingAgentIndexes` é opcional e re-extrai a árvore para comparar o hash antes de gravar; quem o fornece\n * (tipicamente `updateTree()`) aceita que isso renumera os índices do agente.\n * `store.putProfile` nunca rejeita (degrada; ver store.ts), então uma falha do IndexedDB não vira\n * erro desta função.\n */\nexport async function scheduleProfile(\n tree: FlatDomTree,\n url: string,\n store: BrainStore | null,\n signal?: AbortSignal,\n rereadRenumberingAgentIndexes?: () => FlatDomTree | null | Promise<FlatDomTree | null>,\n): Promise<PageProfile> {\n signal?.throwIfAborted();\n const assinatura = assinar(tree, url);\n const chave = chaveDe(assinatura);\n let agendado = jaPerfilados.get(chave);\n if (!agendado) {\n // A tarefa não observa sinal nenhum: ela é compartilhada (ver `corridaComSinal`).\n const tarefa = (async () => {\n await quandoOcioso();\n if (!(await aindaVale(tree, assinatura, rereadRenumberingAgentIndexes))) throw new ProfileAbortedError(\"a página mudou durante a varredura\");\n return profileLevel1(tree, url, undefined, assinatura);\n })();\n const entrada: Agendado = { tarefa, persistido: false };\n jaPerfilados.set(chave, entrada);\n // Falhou ou re-renderizou: não fica em cache, uma chamada posterior tenta de novo.\n tarefa.catch(() => { if (jaPerfilados.get(chave) === entrada) jaPerfilados.delete(chave); });\n agendado = entrada;\n }\n const perfil = await corridaComSinal(agendado.tarefa, signal);\n // A gravação é por CHAMADOR, e por isso quem abortou não grava: o await acima já rejeitou por ele.\n if (store && !store.closed && !agendado.persistido) {\n agendado.persistido = true; // marcado antes do await: dois chamadores simultâneos gravam uma vez\n await store.putProfile(perfil);\n }\n return perfil;\n}\n\n// ======================================================================================\n// Nível 2: eco reversível\n// ======================================================================================\n// Único ponto do perfilador que ESCREVE num campo real. Por que no campo real e não num clone: um\n// `cloneNode()` não copia listeners, e a máscara de uma aplicação quase sempre vem de um handler JS.\n//\n// Garantias, em ordem de importância:\n// 1. Restauração em TODOS os caminhos (`finally`): sucesso, erro, aborto e listener que lança.\n// 2. A recusa é decidida ANTES da primeira escrita. \"Escreveu e desfez\" não equivale a \"nunca\n// escreveu\": listeners rodaram no meio.\n// 3. Nunca submete: só `input` e `change`. Nada de click, form.submit() ou tecla Enter.\n// O valor anterior do campo (pode ser digitado por pessoa) vive só numa variável local, durante a\n// chamada. Não entra na observação nem em nenhum perfil.\n\n// Uma só definição, em store.ts, onde vive a forma PERSISTÍVEL (`FieldProfile.observed`): o que a\n// sonda devolve e o que a P2 vai gravar têm de ser a mesma coisa, inclusive quando mudarem.\n// Nada persiste isto ainda; o docblock de `FieldProfile.observed` diz o que falta antes de gravar.\nexport type EchoObservation = ObservedEcho;\n// `echo: \"equals\"` significa \"nenhum SCRIPT da página reformatou o valor\", NÃO \"nenhum truncamento\n// ocorreria para uma pessoa\": escrita programática não aciona o `maxlength` nativo. O limite declarado\n// vive em `FieldProfile.declared.maxLength` (nível 1); os dois níveis só valem juntos.\nexport type EchoRefusal = { refused: \"password\" | \"payment\" | \"ignored\" };\n// Não sondável: \"a capacidade não existe aqui\" (estado `unsupported` da spec), distinto de recusa por política.\nexport type EchoUnsupported = { unsupported: \"missing\" | \"disabled\" | \"unsupported_type\" };\n\n// Tokens de autocomplete de cartão (`cc-*`) e termos de dado bancário/segurança de cartão.\nconst AUTOCOMPLETE_PAGAMENTO = /(^|\\s)cc-|\\b(cvv|cvc|csc|card|iban|bank|routing|account-number)\\b/i;\nconst AUTOCOMPLETE_SENHA = /(^|\\s)(current|new)-password(\\s|$)/i;\n\n/** Decide a recusa lendo SÓ atributos e posição; não escreve, não foca, não dispara evento. */\nfunction recusaDe(el: HTMLElement): EchoRefusal | null {\n const tipo = el instanceof HTMLInputElement ? el.type.toLowerCase() : \"\";\n const auto = el.getAttribute(\"autocomplete\") ?? \"\";\n if (tipo === \"password\" || AUTOCOMPLETE_SENHA.test(auto)) return { refused: \"password\" };\n if (AUTOCOMPLETE_PAGAMENTO.test(auto)) return { refused: \"payment\" };\n if (closestAtravessando(el, \"[data-page-agent-ignore]\")) return { refused: \"ignored\" };\n return null;\n}\n\n// Escrita pelo setter NATIVO do protótipo, não pelo da instância: frameworks (React) instalam um\n// rastreador de valor na instância, e escrever por ele faz o evento seguinte ser descartado como \"sem\n// mudança\". Pelo protótipo, o framework enxerga a mudança como se viesse do usuário.\nfunction escrever(el: HTMLInputElement | HTMLTextAreaElement, valor: string): void {\n const proto = el instanceof HTMLTextAreaElement ? HTMLTextAreaElement.prototype : HTMLInputElement.prototype;\n Object.getOwnPropertyDescriptor(proto, \"value\")!.set!.call(el, valor);\n}\n\nfunction disparar(el: HTMLElement, tipo: \"input\" | \"change\"): void {\n // `dispatchEvent` não propaga exceções de listeners (o navegador as reporta e segue), mas o try\n // garante que um ambiente que propague não interrompa a restauração.\n try {\n el.dispatchEvent(new Event(tipo, { bubbles: true }));\n } catch { /* listener quebrado não pode impedir o resto */ }\n}\n\nfunction normalizacaoDe(escrito: string, lido: string): EchoObservation[\"normalizedAs\"] | undefined {\n if (/^\\d{4}-\\d{2}-\\d{2}$/.test(lido) && !/^\\d{4}-\\d{2}-\\d{2}$/.test(escrito)) return \"iso_date\";\n if (/^\\d{3}\\.\\d{3}\\.\\d{3}-\\d{2}$/.test(lido)) return \"cpf\";\n if (/^\\(\\d{2}\\) \\d{4,5}-\\d{4}$/.test(lido)) return \"phone\";\n if (/^\\d+$/.test(lido) && !/^\\d+$/.test(escrito)) return \"digits\";\n return undefined;\n}\n\nfunction classificar(escrito: string, lido: string): EchoObservation {\n if (lido === escrito) return { echo: \"equals\" };\n const amostra = { wrote: escrito, readBack: lido };\n // Vazio, ou nenhum caractere escrito sobreviveu: o campo rejeitou a amostra.\n if (lido === \"\" || ![...escrito].some(ch => lido.includes(ch))) return { echo: \"rejected\", maskSample: amostra };\n // Mais curto e é prefixo do que foi escrito: cortado. (Escrita programática não aciona o `maxlength`\n // nativo; o que se observa aqui é corte feito por script da página.)\n if (lido.length < escrito.length && escrito.startsWith(lido)) return { echo: \"truncated\", maskSample: amostra };\n const obs: EchoObservation = { echo: \"masked\", maskSample: amostra };\n const norm = normalizacaoDe(escrito, lido);\n if (norm) obs.normalizedAs = norm;\n return obs;\n}\n\n/**\n * Escreve `sample` no campo real que `ref` descreve, relê o valor e RESTAURA o anterior.\n * Devolve `{ refused }` (sem escrever nada) para senha, cartão/banco e elementos ignorados.\n * Devolve `{ unsupported }` (sem escrever) se o campo não existe, não é de texto ou está desabilitado/somente leitura.\n * Só aborto e erros inesperados lançam.\n * Em qualquer lançamento DEPOIS da primeira escrita (erro, aborto) o `finally` restaura o valor.\n */\nexport async function probeField(ref: StableRef, sample: string, signal?: AbortSignal): Promise<EchoObservation | EchoRefusal | EchoUnsupported> {\n signal?.throwIfAborted();\n const el = resolverRef(ref, document);\n if (!el) return { unsupported: \"missing\" };\n // 1) RECUSA, antes de qualquer escrita (vale também para um campo que é senha E está ignorado).\n const recusa = recusaDe(el);\n if (recusa) return recusa;\n if (!(el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement)) return { unsupported: \"unsupported_type\" };\n const tipo = el instanceof HTMLInputElement ? el.type.toLowerCase() : \"text\";\n if ([\"checkbox\", \"radio\", \"file\", \"button\", \"submit\", \"reset\", \"image\", \"range\", \"color\", \"hidden\"].includes(tipo)) {\n return { unsupported: \"unsupported_type\" };\n }\n if (el.disabled || el.readOnly) return { unsupported: \"disabled\" };\n\n const anterior = el.value; // só em memória local; nunca devolvido nem persistido\n let escreveu = false;\n try {\n escreveu = true; // marcado ANTES: se a escrita lançar no meio, o finally ainda restaura\n escrever(el, sample);\n disparar(el, \"input\");\n signal?.throwIfAborted();\n disparar(el, \"change\");\n signal?.throwIfAborted();\n return classificar(sample, el.value);\n } finally {\n if (escreveu) {\n escrever(el, anterior);\n disparar(el, \"input\");\n disparar(el, \"change\");\n // Um handler de máscara pode ter reformatado o valor restaurado: reescreve sem evento.\n if (Object.getOwnPropertyDescriptor(Object.getPrototypeOf(el), \"value\")!.get!.call(el) !== anterior) escrever(el, anterior);\n }\n }\n}\n"],"names":[],"mappings":"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"}