mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-08 00:02:20 +03:00
* feat(ci): G0 — quality rail (PR→release/**) ganha ratchets+segurança do trilho A O refactor de god-files do trilho 3.8.50→3.9.0 acontece em PRs→release/**, e esse trilho pulava o motor de ratchet, o CodeQL ratchet e todos os scanners de segurança — exatamente onde a rede era necessária (5 das 13 causas da reconciliação de 07-24 eram regressões reais shipadas por CI verde por-PR). Modo enxuto, jobs EXISTENTES (a .51 consolida lanes; nenhum job novo): - lint-guard: quality:collect + ratchet --allow-missing + require-tighten + check:codeql-ratchet. O job já escreve .artifacts/eslint-results.json, então o motor entra a custo ZERO de ESLint (um inventário, dois consumidores). Coverage ausente degrada gracioso (--allow-missing); autoridade de coverage segue no trilho A. + permissions security-events:read para o CodeQL ratchet. - fast-gates: check:cycles, check:lockfile, duplication, dead-code, type-coverage, compression-budget + install endurecido dos scanners (gh release download, zizmor PINADO 1.25.2 = mesmo auditor do ci.yml) + secrets/vuln/workflows/ openapi-breaking com --ratchet (self-skip sem binário; só regressão medida bloqueia). - Fora de propósito: bundle-size (self-skip sem build → configuração morta) e o run de coverage (fast-unit já roda a suíte cheia). Runners intocados: guard tests/unit/vps-runner-variable-scope.test.ts verde; teste novo tests/unit/quality-rail-gate-membership.test.ts pina a MEMBERSHIP dos gates no trilho B (red antes da edição, green depois). Validação no tip puro (nenhum base-red fabricado para a fila de PRs abertos): cycles OK · lockfile OK · duplication 4.26% (base 5.72%) · dead-code 226 (base 227) · type-coverage 94.13% (base 92.17%) · compression OK · secrets 0 (base 0) · vuln 5 (base 10) · codeql 0 (base 0) · oasdiff 0 (base 0) · zizmor 178 (base 190) · actionlint exit 0 no arquivo editado · quality-ratchet 56 métricas OK + require-tighten OK com --allow-missing. Refs #8084 * feat(.50): G13 golden-set, G14 import boundaries, gap34 deterministic, docs sync, R0.2 dead hooks Integra os itens restantes da 3.8.50: - G13: golden-set determinístico para combo.ts e chatCore.ts via seams públicas - G14: no-restricted-imports para localDb barrel fora de src/lib/db/ e executors em src/app/ - Gap34: teste determinístico de timeout DuckDuckGo sem rede real - Docs: golden path de contribuição + sincronização de números canônicos - R0.2: remoção dos 7 hooks mortos do BUILTIN_EVENTS + UI marketplace ajustada * fix(r0.2): remove marketplace tab remnants from plugins page — fixes dashboard typecheck regression * chore(r0.2): remove pluginWorker.ts, signing.ts, sandbox.ts — zero importers confirmed * fix(docs): remove OMNIROUTE_PLUGINS_ALLOW_EXEC reference — env var removed with pluginWorker.ts in R0.2 * fix(env): remove dead OMNIROUTE_PLUGINS_ALLOW_EXEC from .env.example — consumer removed in R0.2 * fix(test): update sidebar-visibility assertion for R0.2 marketplace removal --------- Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com>
191 lines
6.7 KiB
JavaScript
191 lines
6.7 KiB
JavaScript
import nextVitals from "eslint-config-next/core-web-vitals";
|
||
import tseslint from "typescript-eslint";
|
||
|
||
// #7879: bar NEW local `toNumber` definitions outside the canonical helper.
|
||
// Pre-existing definitions (~51 across the codebase) are frozen via
|
||
// config/quality/eslint-suppressions.json and migrated tier-by-tier; only a
|
||
// genuinely NEW `function toNumber`/`const toNumber = ...` should fail.
|
||
const TO_NUMBER_RESTRICTION = {
|
||
selector: "FunctionDeclaration[id.name='toNumber'], VariableDeclarator[id.name='toNumber']",
|
||
message:
|
||
"New local `toNumber` definitions are barred — import `toNumber` from " +
|
||
"`@/shared/utils/numeric` instead (#7879). See that module's JSDoc for the " +
|
||
"canonical coercion shape and the `toNumberOrNull`/`toNumberArray` variants.",
|
||
};
|
||
|
||
const LOCAL_DB_IMPORT_RESTRICTION = {
|
||
regex: "^(?:@/lib/localDb(?:\\.ts)?|(?:\\.\\.?/)+(?:lib/)?localDb(?:\\.ts)?)$",
|
||
message:
|
||
"The localDb compatibility barrel is restricted — import the owning domain module " +
|
||
"from `@/lib/db/` instead.",
|
||
};
|
||
|
||
const EXECUTOR_IMPORT_RESTRICTION = {
|
||
regex: "^(?:@omniroute/)?open-sse/executors(?:/|$)",
|
||
message:
|
||
"Executor implementations must stay behind an open-sse handler or service boundary.",
|
||
};
|
||
|
||
const PROP_TYPES_RESTRICTION = {
|
||
name: "prop-types",
|
||
message: "PropTypes are deprecated. Use TypeScript types/interfaces instead.",
|
||
};
|
||
|
||
const IMPORT_BOUNDARY_RESTRICTIONS = {
|
||
paths: [PROP_TYPES_RESTRICTION],
|
||
patterns: [LOCAL_DB_IMPORT_RESTRICTION],
|
||
};
|
||
|
||
/** @type {import("eslint").Linter.Config[]} */
|
||
const eslintConfig = [
|
||
...nextVitals,
|
||
// Pacote 4 (plano mestre testes+CI, 2026-07-04) — zero-warning policy: TODA regra roda
|
||
// como "error" e a dívida pré-existente vive congelada por arquivo+regra em
|
||
// config/quality/eslint-suppressions.json (ESLint bulk suppressions nativo). Violação
|
||
// NOVA = vermelho no ato (lint-staged no pre-commit + job lint-guard no fast path);
|
||
// o drift de +41/+88 warnings/ciclo que era rebaselinado às cegas na release morre no
|
||
// PR que o introduz. Aperto do baseline: npx eslint . --prune-suppressions
|
||
// --suppressions-location config/quality/eslint-suppressions.json (na release).
|
||
{
|
||
// Escopo = onde os presets do next registram estes plugins (bloco global sem `files`
|
||
// atingiria scripts/*.mjs sem o plugin react-hooks e explodiria o flat config).
|
||
files: ["src/**/*.{ts,tsx,js,jsx}"],
|
||
rules: {
|
||
"react-hooks/exhaustive-deps": "error",
|
||
"@next/next/no-img-element": "error",
|
||
"import/no-anonymous-default-export": "error",
|
||
},
|
||
},
|
||
// FASE-02: Security rules (strict everywhere)
|
||
{
|
||
rules: {
|
||
"no-eval": "error",
|
||
"no-implied-eval": "error",
|
||
"no-new-func": "error",
|
||
"no-restricted-imports": ["error", IMPORT_BOUNDARY_RESTRICTIONS],
|
||
},
|
||
},
|
||
// G14: DB internals may use the compatibility barrel while it is decomposed; all
|
||
// other source files must import the owning src/lib/db domain module directly.
|
||
{
|
||
files: ["src/lib/db/**/*.{ts,tsx,js,jsx}"],
|
||
rules: {
|
||
"no-restricted-imports": [
|
||
"error",
|
||
{
|
||
paths: [PROP_TYPES_RESTRICTION],
|
||
},
|
||
],
|
||
},
|
||
},
|
||
// G14: App routes/components must delegate provider execution through handlers or
|
||
// services instead of reaching into executor implementations.
|
||
{
|
||
files: ["src/app/**/*.{ts,tsx,js,jsx}"],
|
||
rules: {
|
||
"no-restricted-imports": [
|
||
"error",
|
||
{
|
||
...IMPORT_BOUNDARY_RESTRICTIONS,
|
||
patterns: [LOCAL_DB_IMPORT_RESTRICTION, EXECUTOR_IMPORT_RESTRICTION],
|
||
},
|
||
],
|
||
},
|
||
},
|
||
// i18n: ham toLowerCase().includes() arama pattern'ini engelle
|
||
// (Türkçe İ/ı karakterlerini bozar — matchesSearch kullanılmalı).
|
||
// "warn" (error değil): kuralın eklendiği anda kod tabanında zaten bu pattern'i
|
||
// kullanan ~19 satır var; aşamalı temizlik için uyarı seviyesinde tutuluyor
|
||
// (proje politikası: 0 error, warning'ler tolere edilir).
|
||
{
|
||
files: ["src/app/**/*.{ts,tsx}", "src/components/**/*.{ts,tsx}"],
|
||
rules: {
|
||
"no-restricted-syntax": [
|
||
"error",
|
||
{
|
||
selector:
|
||
"CallExpression[callee.property.name='includes'][callee.object.callee.property.name='toLowerCase']",
|
||
message:
|
||
"Türkçe-güvenli arama için matchesSearch() kullan (@/shared/utils/turkishText). Ham toLowerCase().includes() İ/ı karakterlerini bozar.",
|
||
},
|
||
TO_NUMBER_RESTRICTION,
|
||
],
|
||
},
|
||
},
|
||
// #7879: same toNumber restriction for the rest of src/ and open-sse/ — kept
|
||
// as a separate block (via `ignores`) so it does not clobber the
|
||
// app/components-scoped rule array above (flat config replaces a rule's
|
||
// options entirely per matching file, it does not merge arrays).
|
||
{
|
||
files: ["src/**/*.ts", "open-sse/**/*.ts"],
|
||
ignores: ["src/app/**", "src/components/**"],
|
||
rules: {
|
||
"no-restricted-syntax": ["error", TO_NUMBER_RESTRICTION],
|
||
},
|
||
},
|
||
// Canonical helper module itself is exempt from its own restriction.
|
||
{
|
||
files: ["src/shared/utils/numeric.ts"],
|
||
rules: {
|
||
"no-restricted-syntax": "off",
|
||
},
|
||
},
|
||
// Relaxed rules for open-sse and tests (incremental adoption)
|
||
{
|
||
files: ["open-sse/**/*.ts", "tests/**/*.mjs", "tests/**/*.ts"],
|
||
plugins: {
|
||
"@typescript-eslint": tseslint.plugin,
|
||
},
|
||
rules: {
|
||
"@typescript-eslint/no-explicit-any": "error",
|
||
"@next/next/no-assign-module-variable": "off",
|
||
"react-hooks/rules-of-hooks": "off",
|
||
},
|
||
},
|
||
// Global ignores — keep ESLint scoped to source files only
|
||
{
|
||
ignores: [
|
||
// Next.js build output (distDir now .build/next; keep .next for legacy)
|
||
".next/**",
|
||
".build/**",
|
||
"src/.next/**",
|
||
"out/**",
|
||
"build/**",
|
||
"dist/**",
|
||
"coverage/**",
|
||
"next-env.d.ts",
|
||
// Scripts and binaries
|
||
"scripts/**",
|
||
"bin/**",
|
||
// Dependencies
|
||
"node_modules/**",
|
||
".worktrees/**",
|
||
// Nested git worktrees created by review/resolve skills live under
|
||
// .claude/ (gitignored). They hold other sessions' in-progress work and
|
||
// their files move mid-scan, so never lint them from the main checkout.
|
||
".claude/**",
|
||
".omnivscodeagent/**",
|
||
// VS Code extension and its large test fixtures
|
||
"vscode-extension/**",
|
||
"_references/**",
|
||
"_mono_repo/**",
|
||
// Electron app
|
||
"electron/**",
|
||
// Docs
|
||
"docs/**",
|
||
// Open-SSE compiled/bundled output
|
||
"open-sse/mcp-server/dist/**",
|
||
// Playwright test output
|
||
"playwright-report/**",
|
||
"test-results/**",
|
||
// Legacy app/ and QA backup dirs (renamed to dist/ in Layer 1)
|
||
"app/**",
|
||
"app.__qa_backup/**",
|
||
// CLI package copy directory
|
||
"clipr/**",
|
||
],
|
||
},
|
||
];
|
||
|
||
export default eslintConfig;
|