| name | new-detector |
| description | Skill para criar novos detectores e classificadores de campo seguindo as convenções do projeto Fill All. |
| license | MIT |
| compatibility | Node.js 18+, TypeScript 5.x, Chrome Extension Manifest V3 |
| metadata | {"author":"marcusp","version":"1.0","project":"fill-all","category":"scaffolding"} |
Skill: Criar Novo Detector / Classificador
Objetivo
Guia passo a passo para criar um novo detector ou classificador de campos no projeto Fill All, seguindo a interface FieldClassifier, pipeline imutável e convenções de confiança.
Conceitos
| Conceito | Descrição |
|---|
Detector | Interface genérica Detector<TInput, TResult> com .name + .detect() |
FieldClassifier | Subtipo que classifica FormField → ClassifierResult | null |
PageDetector | Subtipo que detecta contexto da página |
DetectionPipeline | Pipeline imutável que roda classificadores em ordem |
confidence | Valor 0–1 indicando certeza (1 = absoluta) |
Passo a Passo
1. Criar o Classificador
Nota: Nos exemplos abaixo, <nome> é um placeholder. Substitua pelo nome real do classificador (ex: keyword, tensorflow, chromeAi, pattern).
src/lib/form/detectors/strategies/<nome>-classifier.ts
import type { FieldClassifier, ClassifierResult } from "@/lib/form/detectors/detector.interface";
import type { FormField } from "@/types";
export const <nome>Classifier: FieldClassifier = {
name: "<nome>-classifier",
detect(field: FormField): ClassifierResult | null {
const signals = field.signals ?? "";
if (!match) {
return null;
}
return {
type: "<field-type>",
confidence: 0.8,
method: "<nome>-classifier",
};
},
};
Regras Obrigatórias
| Regra | Detalhe |
|---|
Objeto const | Nunca classe — usar objeto com .name + .detect() |
| Nome único | .name é usado pela pipeline para reordenação |
Retornar null | Quando não tiver confiança — nunca forçar resultado |
| Confidence 0–1 | 0 = nenhuma, 0.5 = moderada, 0.8 = alta, 1.0 = certeza absoluta |
| Sem side effects | .detect() é puro — sem I/O, sem mutação |
| Sem throw | Retornar null em caso de erro |
| Named export | Nunca export default |
2. Registrar no Registry de Classificadores
Arquivo: src/lib/form/detectors/classifiers.ts
import { <nome>Classifier } from "./strategies/<nome>-classifier";
export const ALL_CLASSIFIERS: FieldClassifier[] = [
<nome>Classifier,
];
3. (Opcional) Adicionar à Pipeline Padrão
Se o classificador deve ser parte do pipeline padrão:
export const DEFAULT_PIPELINE = new DetectionPipeline([
htmlTypeDetector,
<nome>Classifier,
]);
⚠️ A posição na pipeline define prioridade. Classificadores mais específicos devem vir antes dos genéricos.
4. (Opcional) Suporte Async
Se o classificador precisa de operações assíncronas (ex: Chrome AI, API):
export const <nome>Classifier: FieldClassifier = {
name: "<nome>-classifier",
detect(field: FormField): ClassifierResult | null {
return null;
},
async detectAsync(field: FormField): Promise<ClassifierResult | null> {
try {
const result = await someAsyncOperation(field);
return result ? { type: result.type, confidence: result.score, method: "<nome>-classifier" } : null;
} catch {
return null;
}
},
};
5. Criar Testes Unitários
src/lib/form/detectors/__tests__/<nome>-classifier.test.ts
import { describe, it, expect } from "vitest";
import { <nome>Classifier } from "@/lib/form/detectors/strategies/<nome>-classifier";
import type { FormField } from "@/types";
const makeField = (overrides: Partial<FormField> = {}): FormField => ({
selector: "input[name='test']",
tagName: "INPUT",
type: "text",
signals: "",
label: "",
...overrides,
});
describe("<nome>Classifier", () => {
it("has a unique name", () => {
expect(<nome>Classifier.name).toBe("<nome>-classifier");
});
it("returns null for unrecognized fields", () => {
const result = <nome>Classifier.detect(makeField());
expect(result).toBeNull();
});
it("detects <tipo esperado> with correct confidence", () => {
const field = makeField({
signals: "<sinais relevantes>",
label: "<label relevante>",
});
const result = <nome>Classifier.detect(field);
expect(result).not.toBeNull();
expect(result!.type).toBe("<field-type>");
expect(result!.confidence).toBeGreaterThanOrEqual(0);
expect(result!.confidence).toBeLessThanOrEqual(1);
});
it("returns confidence in valid range", () => {
const field = makeField({ signals: "<sinais>" });
const result = <nome>Classifier.detect(field);
if (result) {
expect(result.confidence).toBeGreaterThanOrEqual(0);
expect(result.confidence).toBeLessThanOrEqual(1);
}
});
it("never throws", () => {
const fields = [
makeField(),
makeField({ signals: undefined as unknown as string }),
makeField({ type: undefined as unknown as string }),
];
for (const field of fields) {
expect(() => <nome>Classifier.detect(field)).not.toThrow();
}
});
});
6. Validar
./scripts/validate-step.sh types unit
npm run type-check
npx vitest run src/lib/form/detectors/__tests__/<nome>-classifier.test.ts
npx vitest run src/lib/form/detectors
npx vitest run src/lib/dataset
Pipeline — Como Funciona
Campo HTML → Pipeline.detect(field)
│
├─ Classificador 1 → resultado ou null
├─ Classificador 2 → resultado ou null
├─ Classificador 3 → resultado ou null
└─ ... (roda em ordem até encontrar confiante)
│
└─ Retorna melhor resultado (maior confidence)
- Pipeline é imutável —
.with(), .without(), .withOrder() retornam NOVA instância
- Classificadores são executados em ordem de registro
- O resultado com maior confidence é selecionado
- Se nenhum classificador tiver confiança, retorna
null
Checklist Final