@skapxd/eslint-opinionated 0.11.0 → 0.12.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/README.md CHANGED
@@ -552,6 +552,7 @@ de cada regla):
552
552
  | `no-promise-chain` | `methods` |
553
553
  | `no-tunnel-props` | `allowFilePatterns` (globs), `allowPropPatterns` (regex) |
554
554
  | `prefer-abort-signal` | `allowFilePatterns` (globs), `effectNames` (default `["useEffect", "useLayoutEffect"]`) |
555
+ | `result-error-requires-handling` | `allowFilePatterns` (globs) |
555
556
 
556
557
  Los `allowFilePatterns` de todas las reglas son **globs** (`*` un segmento,
557
558
  `**` cualquier profundidad, `{a,b}` alternativas; un patrón sin prefijo
@@ -565,6 +566,7 @@ matchea en cualquier carpeta). Las 7 reglas restantes no tienen opciones: su
565
566
  | `skapxd/one-root-function-per-file` | Un archivo, una función top-level semántica. |
566
567
  | `skapxd/async-functions-return-result` | Funciones async de dominio deben retornar `Promise<Result<...>>`. **Apagada por defecto; opt-in** (ver motivos en su sección). |
567
568
  | `skapxd/result-error-requires-cause` | Un `Result.err` derivado debe preservar `cause: result.error`. |
569
+ | `skapxd/result-error-requires-handling` | Prohíbe descartar en silencio un Result fallido: el error se transforma o se entrega, nunca se ignora. |
568
570
  | `skapxd/await-requires-result` | Todo `await` debe resolver en un `Result`: o la función llamada retorna `Promise<Result<...>>` (preferido) o se envuelve en `trySafe`. **Obligatoria en todos los presets tipados.** |
569
571
  | `skapxd/no-ad-hoc-ok-result` | Evita contratos `{ ok: ... }` hechos a mano en async exports. |
570
572
  | `skapxd/max-hook-size` | Marca hooks grandes o con demasiados `useState`. |
@@ -705,6 +707,61 @@ aliases, re-exports y tipos inferidos, sin depender solo del nombre importado en
705
707
  el archivo. Su punto ciego histórico —el `Result.err` escondido en un `if`
706
708
  anidado— lo elimina `skapxd/no-nested-if` de raíz.
707
709
 
710
+ ### `skapxd/result-error-requires-handling`
711
+
712
+ La hermana de la anterior cierra la última puerta de evasión: el **descarte
713
+ silencioso**. Detectar el fallo y botarlo sin tocarlo es legal para
714
+ `result-error-requires-cause` (no hay transformación que vigilar), pero deja
715
+ morir información valiosa sin que nadie lo decidiera conscientemente:
716
+
717
+ ```ts
718
+ const result = await copyTextToClipboard(text);
719
+ if (!result.ok) return; // ❌ el error muere aquí, en silencio
720
+ ```
721
+
722
+ El contrato: dentro de un guard de Result fallido, `result.error` (o el
723
+ result completo) debe **fluir a alguna parte**. Dos salidas:
724
+
725
+ ```ts
726
+ // 1. Transformarlo (y result-error-requires-cause vigila el cause)
727
+ if (!result.ok) {
728
+ return Result.err({ cause: result.error, message: "...", type: "COPY_FAILED" });
729
+ }
730
+
731
+ // 2. Entregárselo a alguien: telemetría, estado de error, log de dominio
732
+ if (!result.ok) {
733
+ trackClipboardFailure(result.error);
734
+ return;
735
+ }
736
+
737
+ // (propagar el result completo también vale: `if (!result.ok) return result;`)
738
+ ```
739
+
740
+ **No hay tercera salida.** `void result.error` no cuenta como manejo, y
741
+ manejar sin tocar el error (`setFailed(true)`) tampoco — el detalle se perdió
742
+ igual. Esto es deliberado: si darle seguimiento a un error es crítico o no,
743
+ no puede depender de la interpretación de quien escribe; el camino por
744
+ defecto nunca es ignorarlo.
745
+
746
+ **El alias tampoco es escape.** Asignar no es consumir: la regla sigue los
747
+ alias (y los encadenados, y el destructuring) hasta verificar que alguno se
748
+ consume de verdad:
749
+
750
+ ```ts
751
+ if (!result.ok) {
752
+ const e = result.error; // ❌ transferencia sin destino: se reporta
753
+ return;
754
+ }
755
+
756
+ if (!result.ok) {
757
+ const cause = result.error;
758
+ return Result.err({ cause, message: "..." }); // ✅ el alias se consumió
759
+ }
760
+ ```
761
+
762
+ Type-aware como su hermana: solo aplica a Results reales de `@skapxd/result`,
763
+ con las mismas cinco formas de guard.
764
+
708
765
  ### `skapxd/await-requires-result`
709
766
 
710
767
  > **Es la regla obligatoria del sistema de errores**: la activan todos los
@@ -20,6 +20,7 @@ declare function createAstroConfigs(pluginReference: unknown): ({
20
20
  "skapxd/one-root-function-per-file": string;
21
21
  "skapxd/prefer-ts-pattern": string;
22
22
  "skapxd/result-error-requires-cause": string;
23
+ "skapxd/result-error-requires-handling": string;
23
24
  };
24
25
  } | {
25
26
  files: string[];
@@ -38,6 +39,7 @@ declare function createAstroConfigs(pluginReference: unknown): ({
38
39
  "skapxd/one-root-function-per-file": string;
39
40
  "skapxd/prefer-ts-pattern": string;
40
41
  "skapxd/result-error-requires-cause": string;
42
+ "skapxd/result-error-requires-handling": string;
41
43
  };
42
44
  languageOptions?: undefined;
43
45
  } | {
@@ -55,6 +57,7 @@ declare function createAstroConfigs(pluginReference: unknown): ({
55
57
  rules: {
56
58
  "skapxd/await-requires-result": string;
57
59
  "skapxd/result-error-requires-cause": string;
60
+ "skapxd/result-error-requires-handling": string;
58
61
  };
59
62
  })[];
60
63
 
@@ -20,6 +20,7 @@ declare function createAstroConfigs(pluginReference: unknown): ({
20
20
  "skapxd/one-root-function-per-file": string;
21
21
  "skapxd/prefer-ts-pattern": string;
22
22
  "skapxd/result-error-requires-cause": string;
23
+ "skapxd/result-error-requires-handling": string;
23
24
  };
24
25
  } | {
25
26
  files: string[];
@@ -38,6 +39,7 @@ declare function createAstroConfigs(pluginReference: unknown): ({
38
39
  "skapxd/one-root-function-per-file": string;
39
40
  "skapxd/prefer-ts-pattern": string;
40
41
  "skapxd/result-error-requires-cause": string;
42
+ "skapxd/result-error-requires-handling": string;
41
43
  };
42
44
  languageOptions?: undefined;
43
45
  } | {
@@ -55,6 +57,7 @@ declare function createAstroConfigs(pluginReference: unknown): ({
55
57
  rules: {
56
58
  "skapxd/await-requires-result": string;
57
59
  "skapxd/result-error-requires-cause": string;
60
+ "skapxd/result-error-requires-handling": string;
58
61
  };
59
62
  })[];
60
63
 
@@ -45,7 +45,8 @@ var baseRules = {
45
45
  "skapxd/no-try-catch": "error",
46
46
  "skapxd/one-root-function-per-file": "error",
47
47
  "skapxd/prefer-ts-pattern": "error",
48
- "skapxd/result-error-requires-cause": "error"
48
+ "skapxd/result-error-requires-cause": "error",
49
+ "skapxd/result-error-requires-handling": "error"
49
50
  };
50
51
 
51
52
  // src/shared/configs/create-base-language-options.ts
@@ -96,7 +97,8 @@ function createAstroConfigs(pluginReference) {
96
97
  plugins: { skapxd: pluginReference },
97
98
  rules: {
98
99
  "skapxd/await-requires-result": "error",
99
- "skapxd/result-error-requires-cause": "error"
100
+ "skapxd/result-error-requires-cause": "error",
101
+ "skapxd/result-error-requires-handling": "error"
100
102
  }
101
103
  }
102
104
  ];
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/astro/index.ts","../../src/shared/configs/base-rules.ts","../../src/shared/configs/create-base-language-options.ts","../../src/shared/configs/create-typed-language-options.ts","../../src/astro/configs.ts"],"sourcesContent":["export { createAstroConfigs } from \"./configs\";\n","export const baseRules = {\n \"skapxd/no-ad-hoc-ok-result\": \"error\",\n \"skapxd/no-deep-relative-imports\": \"error\",\n \"skapxd/no-default-export\": \"error\",\n \"skapxd/no-emoji\": \"error\",\n \"skapxd/no-nested-if\": \"error\",\n \"skapxd/no-promise-chain\": \"error\",\n \"skapxd/no-try-catch\": \"error\",\n \"skapxd/one-root-function-per-file\": \"error\",\n \"skapxd/prefer-ts-pattern\": \"error\",\n \"skapxd/result-error-requires-cause\": \"error\",\n};\n","import tseslint from \"typescript-eslint\";\n\n// Variante sin type-checking: solo el parser, para presets que aplican a TS\n// pero no necesitan projectService (base, package, next/base, next/react).\nexport function createBaseLanguageOptions() {\n return {\n parser: tseslint.parser,\n };\n}\n","import tseslint from \"typescript-eslint\";\n\nexport function createTypedLanguageOptions() {\n return {\n // Sin el parser explícito, un consumidor que use solo estos presets\n // obtiene \"Parsing error\" en cada archivo TS (espree no parsea TS).\n parser: tseslint.parser,\n parserOptions: {\n projectService: true,\n },\n };\n}\n","import {\n baseRules,\n createBaseLanguageOptions,\n createTypedLanguageOptions,\n} from \"#/shared/configs\";\n\nexport function createAstroConfigs(pluginReference: unknown) {\n const baseLanguageOptions = createBaseLanguageOptions();\n const typedLanguageOptions = createTypedLanguageOptions();\n\n return [\n {\n files: [\"src/**/*.{ts,tsx}\"],\n languageOptions: baseLanguageOptions,\n name: \"skapxd/astro/base\",\n plugins: { skapxd: pluginReference },\n rules: baseRules,\n },\n // Los .astro no llevan parser propio: lo aporta eslint-plugin-astro,\n // que el consumidor debe tener configurado.\n {\n files: [\"src/**/*.astro\"],\n name: \"skapxd/astro/astro-files\",\n plugins: { skapxd: pluginReference },\n rules: baseRules,\n },\n {\n files: [\"src/**/*.{ts,tsx}\"],\n languageOptions: typedLanguageOptions,\n name: \"skapxd/astro/typescript\",\n plugins: { skapxd: pluginReference },\n rules: {\n \"skapxd/await-requires-result\": \"error\",\n \"skapxd/result-error-requires-cause\": \"error\",\n },\n },\n ];\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAO,IAAM,YAAY;AAAA,EACvB,8BAA8B;AAAA,EAC9B,mCAAmC;AAAA,EACnC,4BAA4B;AAAA,EAC5B,mBAAmB;AAAA,EACnB,uBAAuB;AAAA,EACvB,2BAA2B;AAAA,EAC3B,uBAAuB;AAAA,EACvB,qCAAqC;AAAA,EACrC,4BAA4B;AAAA,EAC5B,sCAAsC;AACxC;;;ACXA,+BAAqB;AAId,SAAS,4BAA4B;AAC1C,SAAO;AAAA,IACL,QAAQ,yBAAAA,QAAS;AAAA,EACnB;AACF;;;ACRA,IAAAC,4BAAqB;AAEd,SAAS,6BAA6B;AAC3C,SAAO;AAAA;AAAA;AAAA,IAGL,QAAQ,0BAAAC,QAAS;AAAA,IACjB,eAAe;AAAA,MACb,gBAAgB;AAAA,IAClB;AAAA,EACF;AACF;;;ACLO,SAAS,mBAAmB,iBAA0B;AAC3D,QAAM,sBAAsB,0BAA0B;AACtD,QAAM,uBAAuB,2BAA2B;AAExD,SAAO;AAAA,IACL;AAAA,MACE,OAAO,CAAC,mBAAmB;AAAA,MAC3B,iBAAiB;AAAA,MACjB,MAAM;AAAA,MACN,SAAS,EAAE,QAAQ,gBAAgB;AAAA,MACnC,OAAO;AAAA,IACT;AAAA;AAAA;AAAA,IAGA;AAAA,MACE,OAAO,CAAC,gBAAgB;AAAA,MACxB,MAAM;AAAA,MACN,SAAS,EAAE,QAAQ,gBAAgB;AAAA,MACnC,OAAO;AAAA,IACT;AAAA,IACA;AAAA,MACE,OAAO,CAAC,mBAAmB;AAAA,MAC3B,iBAAiB;AAAA,MACjB,MAAM;AAAA,MACN,SAAS,EAAE,QAAQ,gBAAgB;AAAA,MACnC,OAAO;AAAA,QACL,gCAAgC;AAAA,QAChC,sCAAsC;AAAA,MACxC;AAAA,IACF;AAAA,EACF;AACF;","names":["tseslint","import_typescript_eslint","tseslint"]}
1
+ {"version":3,"sources":["../../src/astro/index.ts","../../src/shared/configs/base-rules.ts","../../src/shared/configs/create-base-language-options.ts","../../src/shared/configs/create-typed-language-options.ts","../../src/astro/configs.ts"],"sourcesContent":["export { createAstroConfigs } from \"./configs\";\n","export const baseRules = {\n \"skapxd/no-ad-hoc-ok-result\": \"error\",\n \"skapxd/no-deep-relative-imports\": \"error\",\n \"skapxd/no-default-export\": \"error\",\n \"skapxd/no-emoji\": \"error\",\n \"skapxd/no-nested-if\": \"error\",\n \"skapxd/no-promise-chain\": \"error\",\n \"skapxd/no-try-catch\": \"error\",\n \"skapxd/one-root-function-per-file\": \"error\",\n \"skapxd/prefer-ts-pattern\": \"error\",\n \"skapxd/result-error-requires-cause\": \"error\",\n \"skapxd/result-error-requires-handling\": \"error\",\n};\n","import tseslint from \"typescript-eslint\";\n\n// Variante sin type-checking: solo el parser, para presets que aplican a TS\n// pero no necesitan projectService (base, package, next/base, next/react).\nexport function createBaseLanguageOptions() {\n return {\n parser: tseslint.parser,\n };\n}\n","import tseslint from \"typescript-eslint\";\n\nexport function createTypedLanguageOptions() {\n return {\n // Sin el parser explícito, un consumidor que use solo estos presets\n // obtiene \"Parsing error\" en cada archivo TS (espree no parsea TS).\n parser: tseslint.parser,\n parserOptions: {\n projectService: true,\n },\n };\n}\n","import {\n baseRules,\n createBaseLanguageOptions,\n createTypedLanguageOptions,\n} from \"#/shared/configs\";\n\nexport function createAstroConfigs(pluginReference: unknown) {\n const baseLanguageOptions = createBaseLanguageOptions();\n const typedLanguageOptions = createTypedLanguageOptions();\n\n return [\n {\n files: [\"src/**/*.{ts,tsx}\"],\n languageOptions: baseLanguageOptions,\n name: \"skapxd/astro/base\",\n plugins: { skapxd: pluginReference },\n rules: baseRules,\n },\n // Los .astro no llevan parser propio: lo aporta eslint-plugin-astro,\n // que el consumidor debe tener configurado.\n {\n files: [\"src/**/*.astro\"],\n name: \"skapxd/astro/astro-files\",\n plugins: { skapxd: pluginReference },\n rules: baseRules,\n },\n {\n files: [\"src/**/*.{ts,tsx}\"],\n languageOptions: typedLanguageOptions,\n name: \"skapxd/astro/typescript\",\n plugins: { skapxd: pluginReference },\n rules: {\n \"skapxd/await-requires-result\": \"error\",\n \"skapxd/result-error-requires-cause\": \"error\",\n \"skapxd/result-error-requires-handling\": \"error\",\n },\n },\n ];\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAO,IAAM,YAAY;AAAA,EACvB,8BAA8B;AAAA,EAC9B,mCAAmC;AAAA,EACnC,4BAA4B;AAAA,EAC5B,mBAAmB;AAAA,EACnB,uBAAuB;AAAA,EACvB,2BAA2B;AAAA,EAC3B,uBAAuB;AAAA,EACvB,qCAAqC;AAAA,EACrC,4BAA4B;AAAA,EAC5B,sCAAsC;AAAA,EACtC,yCAAyC;AAC3C;;;ACZA,+BAAqB;AAId,SAAS,4BAA4B;AAC1C,SAAO;AAAA,IACL,QAAQ,yBAAAA,QAAS;AAAA,EACnB;AACF;;;ACRA,IAAAC,4BAAqB;AAEd,SAAS,6BAA6B;AAC3C,SAAO;AAAA;AAAA;AAAA,IAGL,QAAQ,0BAAAC,QAAS;AAAA,IACjB,eAAe;AAAA,MACb,gBAAgB;AAAA,IAClB;AAAA,EACF;AACF;;;ACLO,SAAS,mBAAmB,iBAA0B;AAC3D,QAAM,sBAAsB,0BAA0B;AACtD,QAAM,uBAAuB,2BAA2B;AAExD,SAAO;AAAA,IACL;AAAA,MACE,OAAO,CAAC,mBAAmB;AAAA,MAC3B,iBAAiB;AAAA,MACjB,MAAM;AAAA,MACN,SAAS,EAAE,QAAQ,gBAAgB;AAAA,MACnC,OAAO;AAAA,IACT;AAAA;AAAA;AAAA,IAGA;AAAA,MACE,OAAO,CAAC,gBAAgB;AAAA,MACxB,MAAM;AAAA,MACN,SAAS,EAAE,QAAQ,gBAAgB;AAAA,MACnC,OAAO;AAAA,IACT;AAAA,IACA;AAAA,MACE,OAAO,CAAC,mBAAmB;AAAA,MAC3B,iBAAiB;AAAA,MACjB,MAAM;AAAA,MACN,SAAS,EAAE,QAAQ,gBAAgB;AAAA,MACnC,OAAO;AAAA,QACL,gCAAgC;AAAA,QAChC,sCAAsC;AAAA,QACtC,yCAAyC;AAAA,MAC3C;AAAA,IACF;AAAA,EACF;AACF;","names":["tseslint","import_typescript_eslint","tseslint"]}
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  createAstroConfigs
3
- } from "../chunk-3NM7FEIN.mjs";
4
- import "../chunk-J472YWOD.mjs";
3
+ } from "../chunk-JA2ZO3KQ.mjs";
4
+ import "../chunk-QADXO5IL.mjs";
5
5
  export {
6
6
  createAstroConfigs
7
7
  };
@@ -6,7 +6,7 @@ import {
6
6
  baseRules,
7
7
  createBaseLanguageOptions,
8
8
  createTypedLanguageOptions
9
- } from "./chunk-J472YWOD.mjs";
9
+ } from "./chunk-QADXO5IL.mjs";
10
10
 
11
11
  // src/next/configs.ts
12
12
  var nextDefaultExportFileGlob = `{${[
@@ -71,4 +71,4 @@ function createNextConfigs(pluginReference) {
71
71
  export {
72
72
  createNextConfigs
73
73
  };
74
- //# sourceMappingURL=chunk-Z2MYYOIS.mjs.map
74
+ //# sourceMappingURL=chunk-DJXM5PMG.mjs.map
@@ -2,7 +2,7 @@ import {
2
2
  baseRules,
3
3
  createBaseLanguageOptions,
4
4
  createTypedLanguageOptions
5
- } from "./chunk-J472YWOD.mjs";
5
+ } from "./chunk-QADXO5IL.mjs";
6
6
 
7
7
  // src/astro/configs.ts
8
8
  function createAstroConfigs(pluginReference) {
@@ -31,7 +31,8 @@ function createAstroConfigs(pluginReference) {
31
31
  plugins: { skapxd: pluginReference },
32
32
  rules: {
33
33
  "skapxd/await-requires-result": "error",
34
- "skapxd/result-error-requires-cause": "error"
34
+ "skapxd/result-error-requires-cause": "error",
35
+ "skapxd/result-error-requires-handling": "error"
35
36
  }
36
37
  }
37
38
  ];
@@ -40,4 +41,4 @@ function createAstroConfigs(pluginReference) {
40
41
  export {
41
42
  createAstroConfigs
42
43
  };
43
- //# sourceMappingURL=chunk-3NM7FEIN.mjs.map
44
+ //# sourceMappingURL=chunk-JA2ZO3KQ.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/astro/configs.ts"],"sourcesContent":["import {\n baseRules,\n createBaseLanguageOptions,\n createTypedLanguageOptions,\n} from \"#/shared/configs\";\n\nexport function createAstroConfigs(pluginReference: unknown) {\n const baseLanguageOptions = createBaseLanguageOptions();\n const typedLanguageOptions = createTypedLanguageOptions();\n\n return [\n {\n files: [\"src/**/*.{ts,tsx}\"],\n languageOptions: baseLanguageOptions,\n name: \"skapxd/astro/base\",\n plugins: { skapxd: pluginReference },\n rules: baseRules,\n },\n // Los .astro no llevan parser propio: lo aporta eslint-plugin-astro,\n // que el consumidor debe tener configurado.\n {\n files: [\"src/**/*.astro\"],\n name: \"skapxd/astro/astro-files\",\n plugins: { skapxd: pluginReference },\n rules: baseRules,\n },\n {\n files: [\"src/**/*.{ts,tsx}\"],\n languageOptions: typedLanguageOptions,\n name: \"skapxd/astro/typescript\",\n plugins: { skapxd: pluginReference },\n rules: {\n \"skapxd/await-requires-result\": \"error\",\n \"skapxd/result-error-requires-cause\": \"error\",\n },\n },\n ];\n}\n"],"mappings":";;;;;;;AAMO,SAAS,mBAAmB,iBAA0B;AAC3D,QAAM,sBAAsB,0BAA0B;AACtD,QAAM,uBAAuB,2BAA2B;AAExD,SAAO;AAAA,IACL;AAAA,MACE,OAAO,CAAC,mBAAmB;AAAA,MAC3B,iBAAiB;AAAA,MACjB,MAAM;AAAA,MACN,SAAS,EAAE,QAAQ,gBAAgB;AAAA,MACnC,OAAO;AAAA,IACT;AAAA;AAAA;AAAA,IAGA;AAAA,MACE,OAAO,CAAC,gBAAgB;AAAA,MACxB,MAAM;AAAA,MACN,SAAS,EAAE,QAAQ,gBAAgB;AAAA,MACnC,OAAO;AAAA,IACT;AAAA,IACA;AAAA,MACE,OAAO,CAAC,mBAAmB;AAAA,MAC3B,iBAAiB;AAAA,MACjB,MAAM;AAAA,MACN,SAAS,EAAE,QAAQ,gBAAgB;AAAA,MACnC,OAAO;AAAA,QACL,gCAAgC;AAAA,QAChC,sCAAsC;AAAA,MACxC;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
1
+ {"version":3,"sources":["../src/astro/configs.ts"],"sourcesContent":["import {\n baseRules,\n createBaseLanguageOptions,\n createTypedLanguageOptions,\n} from \"#/shared/configs\";\n\nexport function createAstroConfigs(pluginReference: unknown) {\n const baseLanguageOptions = createBaseLanguageOptions();\n const typedLanguageOptions = createTypedLanguageOptions();\n\n return [\n {\n files: [\"src/**/*.{ts,tsx}\"],\n languageOptions: baseLanguageOptions,\n name: \"skapxd/astro/base\",\n plugins: { skapxd: pluginReference },\n rules: baseRules,\n },\n // Los .astro no llevan parser propio: lo aporta eslint-plugin-astro,\n // que el consumidor debe tener configurado.\n {\n files: [\"src/**/*.astro\"],\n name: \"skapxd/astro/astro-files\",\n plugins: { skapxd: pluginReference },\n rules: baseRules,\n },\n {\n files: [\"src/**/*.{ts,tsx}\"],\n languageOptions: typedLanguageOptions,\n name: \"skapxd/astro/typescript\",\n plugins: { skapxd: pluginReference },\n rules: {\n \"skapxd/await-requires-result\": \"error\",\n \"skapxd/result-error-requires-cause\": \"error\",\n \"skapxd/result-error-requires-handling\": \"error\",\n },\n },\n ];\n}\n"],"mappings":";;;;;;;AAMO,SAAS,mBAAmB,iBAA0B;AAC3D,QAAM,sBAAsB,0BAA0B;AACtD,QAAM,uBAAuB,2BAA2B;AAExD,SAAO;AAAA,IACL;AAAA,MACE,OAAO,CAAC,mBAAmB;AAAA,MAC3B,iBAAiB;AAAA,MACjB,MAAM;AAAA,MACN,SAAS,EAAE,QAAQ,gBAAgB;AAAA,MACnC,OAAO;AAAA,IACT;AAAA;AAAA;AAAA,IAGA;AAAA,MACE,OAAO,CAAC,gBAAgB;AAAA,MACxB,MAAM;AAAA,MACN,SAAS,EAAE,QAAQ,gBAAgB;AAAA,MACnC,OAAO;AAAA,IACT;AAAA,IACA;AAAA,MACE,OAAO,CAAC,mBAAmB;AAAA,MAC3B,iBAAiB;AAAA,MACjB,MAAM;AAAA,MACN,SAAS,EAAE,QAAQ,gBAAgB;AAAA,MACnC,OAAO;AAAA,QACL,gCAAgC;AAAA,QAChC,sCAAsC;AAAA,QACtC,yCAAyC;AAAA,MAC3C;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
@@ -22,7 +22,8 @@ var baseRules = {
22
22
  "skapxd/no-try-catch": "error",
23
23
  "skapxd/one-root-function-per-file": "error",
24
24
  "skapxd/prefer-ts-pattern": "error",
25
- "skapxd/result-error-requires-cause": "error"
25
+ "skapxd/result-error-requires-cause": "error",
26
+ "skapxd/result-error-requires-handling": "error"
26
27
  };
27
28
 
28
29
  // src/shared/configs/create-base-language-options.ts
@@ -115,4 +116,4 @@ export {
115
116
  createSharedConfigs,
116
117
  strictConfig
117
118
  };
118
- //# sourceMappingURL=chunk-J472YWOD.mjs.map
119
+ //# sourceMappingURL=chunk-QADXO5IL.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/shared/configs/create-typed-language-options.ts","../src/shared/configs/base-rules.ts","../src/shared/configs/create-base-language-options.ts","../src/shared/configs/create-shared-configs.ts","../src/shared/configs/strict-config.ts"],"sourcesContent":["import tseslint from \"typescript-eslint\";\n\nexport function createTypedLanguageOptions() {\n return {\n // Sin el parser explícito, un consumidor que use solo estos presets\n // obtiene \"Parsing error\" en cada archivo TS (espree no parsea TS).\n parser: tseslint.parser,\n parserOptions: {\n projectService: true,\n },\n };\n}\n","export const baseRules = {\n \"skapxd/no-ad-hoc-ok-result\": \"error\",\n \"skapxd/no-deep-relative-imports\": \"error\",\n \"skapxd/no-default-export\": \"error\",\n \"skapxd/no-emoji\": \"error\",\n \"skapxd/no-nested-if\": \"error\",\n \"skapxd/no-promise-chain\": \"error\",\n \"skapxd/no-try-catch\": \"error\",\n \"skapxd/one-root-function-per-file\": \"error\",\n \"skapxd/prefer-ts-pattern\": \"error\",\n \"skapxd/result-error-requires-cause\": \"error\",\n \"skapxd/result-error-requires-handling\": \"error\",\n};\n","import tseslint from \"typescript-eslint\";\n\n// Variante sin type-checking: solo el parser, para presets que aplican a TS\n// pero no necesitan projectService (base, package, next/base, next/react).\nexport function createBaseLanguageOptions() {\n return {\n parser: tseslint.parser,\n };\n}\n","import { baseRules } from \"./base-rules\";\nimport { createBaseLanguageOptions } from \"./create-base-language-options\";\nimport { createTypedLanguageOptions } from \"./create-typed-language-options\";\n\nexport function createSharedConfigs(pluginReference: unknown) {\n const baseLanguageOptions = createBaseLanguageOptions();\n const typedLanguageOptions = createTypedLanguageOptions();\n\n return {\n backend: {\n languageOptions: typedLanguageOptions,\n name: \"skapxd/shared/backend\",\n plugins: { skapxd: pluginReference },\n rules: {\n ...baseRules,\n // La regla obligatoria del sistema de errores es await-requires-result\n // (todo await resuelve en Result). async-functions-return-result queda\n // apagada por defecto: exigir la firma por decreto choca con los bordes\n // del framework y bloquea la adopción incremental; la presión sobre los\n // awaits produce el mismo estado final. Ver \"¿Por qué está apagada por\n // defecto?\" en el README.\n \"skapxd/await-requires-result\": \"error\",\n },\n },\n base: {\n languageOptions: baseLanguageOptions,\n name: \"skapxd/shared/base\",\n plugins: { skapxd: pluginReference },\n rules: baseRules,\n },\n frontend: {\n languageOptions: typedLanguageOptions,\n name: \"skapxd/shared/frontend\",\n plugins: { skapxd: pluginReference },\n rules: {\n ...baseRules,\n // En el front no se obliga a retornar Result, pero todo await debe\n // resolver en uno: o la función llamada ya retorna Promise<Result>\n // (camino preferido: errores modelados en el dominio) o se envuelve\n // en trySafe en el sitio.\n \"skapxd/await-requires-result\": \"error\",\n \"skapxd/jsx-return-name-pascal-case\": \"error\",\n // Anti prop-drilling: ninguna prop viaja más de un nivel. Quien la\n // crea puede pasarla a UN hijo; quien la recibe no la reenvía —\n // estado y acciones a un store global o custom hook.\n \"skapxd/no-functions-inside-components\": \"error\",\n \"skapxd/no-tunnel-props\": \"error\",\n // Listeners en efectos: un AbortController por efecto, cleanup con\n // un solo abort() en vez de removeEventListener por listener.\n \"skapxd/prefer-abort-signal\": \"error\",\n \"skapxd/no-jsx-ternary-null\": \"error\",\n \"skapxd/max-hook-size\": [\n \"error\",\n {\n maxLines: 120,\n maxUseState: 1,\n },\n ],\n },\n },\n package: {\n languageOptions: baseLanguageOptions,\n name: \"skapxd/shared/package\",\n plugins: { skapxd: pluginReference },\n rules: {\n \"skapxd/one-root-function-per-file\": \"error\",\n },\n },\n };\n}\n","// Config endurecida: ignora TODOS los comentarios `eslint-disable` (y demás\n// directivas inline) en los archivos que cubre. Así ni una persona ni un agente\n// pueden saltarse una regla con `// eslint-disable-next-line`.\nexport const strictConfig = {\n linterOptions: {\n noInlineConfig: true,\n },\n name: \"skapxd/strict\",\n};\n"],"mappings":";AAAA,OAAO,cAAc;AAEd,SAAS,6BAA6B;AAC3C,SAAO;AAAA;AAAA;AAAA,IAGL,QAAQ,SAAS;AAAA,IACjB,eAAe;AAAA,MACb,gBAAgB;AAAA,IAClB;AAAA,EACF;AACF;;;ACXO,IAAM,YAAY;AAAA,EACvB,8BAA8B;AAAA,EAC9B,mCAAmC;AAAA,EACnC,4BAA4B;AAAA,EAC5B,mBAAmB;AAAA,EACnB,uBAAuB;AAAA,EACvB,2BAA2B;AAAA,EAC3B,uBAAuB;AAAA,EACvB,qCAAqC;AAAA,EACrC,4BAA4B;AAAA,EAC5B,sCAAsC;AAAA,EACtC,yCAAyC;AAC3C;;;ACZA,OAAOA,eAAc;AAId,SAAS,4BAA4B;AAC1C,SAAO;AAAA,IACL,QAAQA,UAAS;AAAA,EACnB;AACF;;;ACJO,SAAS,oBAAoB,iBAA0B;AAC5D,QAAM,sBAAsB,0BAA0B;AACtD,QAAM,uBAAuB,2BAA2B;AAExD,SAAO;AAAA,IACL,SAAS;AAAA,MACP,iBAAiB;AAAA,MACjB,MAAM;AAAA,MACN,SAAS,EAAE,QAAQ,gBAAgB;AAAA,MACnC,OAAO;AAAA,QACL,GAAG;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAOH,gCAAgC;AAAA,MAClC;AAAA,IACF;AAAA,IACA,MAAM;AAAA,MACJ,iBAAiB;AAAA,MACjB,MAAM;AAAA,MACN,SAAS,EAAE,QAAQ,gBAAgB;AAAA,MACnC,OAAO;AAAA,IACT;AAAA,IACA,UAAU;AAAA,MACR,iBAAiB;AAAA,MACjB,MAAM;AAAA,MACN,SAAS,EAAE,QAAQ,gBAAgB;AAAA,MACnC,OAAO;AAAA,QACL,GAAG;AAAA;AAAA;AAAA;AAAA;AAAA,QAKH,gCAAgC;AAAA,QAChC,sCAAsC;AAAA;AAAA;AAAA;AAAA,QAItC,yCAAyC;AAAA,QACzC,0BAA0B;AAAA;AAAA;AAAA,QAG1B,8BAA8B;AAAA,QAC9B,8BAA8B;AAAA,QAC9B,wBAAwB;AAAA,UACtB;AAAA,UACA;AAAA,YACE,UAAU;AAAA,YACV,aAAa;AAAA,UACf;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,IACA,SAAS;AAAA,MACP,iBAAiB;AAAA,MACjB,MAAM;AAAA,MACN,SAAS,EAAE,QAAQ,gBAAgB;AAAA,MACnC,OAAO;AAAA,QACL,qCAAqC;AAAA,MACvC;AAAA,IACF;AAAA,EACF;AACF;;;AClEO,IAAM,eAAe;AAAA,EAC1B,eAAe;AAAA,IACb,gBAAgB;AAAA,EAClB;AAAA,EACA,MAAM;AACR;","names":["tseslint"]}
@@ -1151,6 +1151,135 @@ var resultErrorRequiresCause = {
1151
1151
  }
1152
1152
  };
1153
1153
 
1154
+ // src/utils/collect-identifiers-named.ts
1155
+ function collectIdentifiersNamed(node, name, results = []) {
1156
+ if (node?.type === "Identifier" && node.name === name) {
1157
+ results.push(node);
1158
+ }
1159
+ for (const child of getNodeChildren(node)) {
1160
+ collectIdentifiersNamed(child, name, results);
1161
+ }
1162
+ return results;
1163
+ }
1164
+
1165
+ // src/utils/get-result-error-requires-handling-options.ts
1166
+ function getResultErrorRequiresHandlingOptions(options = {}) {
1167
+ return {
1168
+ allowFilePatterns: options.allowFilePatterns ?? []
1169
+ };
1170
+ }
1171
+
1172
+ // src/utils/get-declared-alias-names.ts
1173
+ function getDeclaredAliasNames(id) {
1174
+ if (id.type === "Identifier") {
1175
+ return [id.name];
1176
+ }
1177
+ if (id.type !== "ObjectPattern") {
1178
+ return [];
1179
+ }
1180
+ return id.properties.flatMap((property) => {
1181
+ if (property.type === "RestElement" && property.argument.type === "Identifier") {
1182
+ return [property.argument.name];
1183
+ }
1184
+ if (property.type === "Property" && property.value.type === "Identifier") {
1185
+ return [property.value.name];
1186
+ }
1187
+ return [];
1188
+ });
1189
+ }
1190
+
1191
+ // src/utils/is-inside-node.ts
1192
+ function isInsideNode(node, ancestor) {
1193
+ let current = node;
1194
+ while (current) {
1195
+ if (current === ancestor) {
1196
+ return true;
1197
+ }
1198
+ current = current.parent;
1199
+ }
1200
+ return false;
1201
+ }
1202
+
1203
+ // src/utils/is-consumed-result-reference.ts
1204
+ function isConsumedResultReference(identifier, searchRoot, visited = /* @__PURE__ */ new Set()) {
1205
+ const member = identifier.parent?.type === "MemberExpression" && identifier.parent.object === identifier ? identifier.parent : null;
1206
+ const reference = member ?? identifier;
1207
+ const parent = reference.parent;
1208
+ if (parent?.type === "UnaryExpression" && parent.operator === "void") {
1209
+ return false;
1210
+ }
1211
+ if (parent?.type === "ExpressionStatement") {
1212
+ return false;
1213
+ }
1214
+ if (parent?.type !== "VariableDeclarator" || parent.init !== reference) {
1215
+ return true;
1216
+ }
1217
+ const aliasNames = getDeclaredAliasNames(parent.id).filter(
1218
+ (name) => !visited.has(name)
1219
+ );
1220
+ return aliasNames.some((aliasName) => {
1221
+ visited.add(aliasName);
1222
+ return collectIdentifiersNamed(searchRoot, aliasName).filter((aliasReference) => !isInsideNode(aliasReference, parent.id)).some(
1223
+ (aliasReference) => isConsumedResultReference(aliasReference, searchRoot, visited)
1224
+ );
1225
+ });
1226
+ }
1227
+
1228
+ // src/rules/result-error-requires-handling.ts
1229
+ var resultErrorRequiresHandling = {
1230
+ meta: {
1231
+ type: "problem",
1232
+ docs: {
1233
+ description: "Prohibe descartar en silencio un Result fallido: el error se transforma o se entrega, nunca se ignora."
1234
+ },
1235
+ messages: {
1236
+ unhandledResultError: "El guard detecta que `{{name}}` fallo, pero `{{name}}.error` muere aqui sin seguimiento. Transformalo (`Result.err({ cause: {{name}}.error, ... })`), entregaselo a alguien (telemetria, estado de error, log de dominio) o propaga el result completo. Si darle seguimiento es critico o no, no es una interpretacion: todo error fluye a alguna parte."
1237
+ },
1238
+ schema: [
1239
+ {
1240
+ additionalProperties: false,
1241
+ properties: {
1242
+ allowFilePatterns: {
1243
+ items: { type: "string" },
1244
+ type: "array"
1245
+ }
1246
+ },
1247
+ type: "object"
1248
+ }
1249
+ ]
1250
+ },
1251
+ create(context) {
1252
+ const options = getResultErrorRequiresHandlingOptions(context.options[0]);
1253
+ const filename = context.filename ?? context.getFilename();
1254
+ const typeContext = getTypeContext(context);
1255
+ if (matchesAnyGlob(filename, options.allowFilePatterns)) {
1256
+ return {};
1257
+ }
1258
+ return {
1259
+ IfStatement(node) {
1260
+ const resultGuard = getFailedResultGuard(node.test);
1261
+ if (!typeContext || !resultGuard || !isSkapxdResultExpression(resultGuard.node, typeContext)) {
1262
+ return;
1263
+ }
1264
+ const references = collectIdentifiersNamed(
1265
+ node.consequent,
1266
+ resultGuard.name
1267
+ );
1268
+ if (references.some(
1269
+ (reference) => isConsumedResultReference(reference, node.consequent)
1270
+ )) {
1271
+ return;
1272
+ }
1273
+ context.report({
1274
+ data: { name: resultGuard.name },
1275
+ messageId: "unhandledResultError",
1276
+ node: node.test
1277
+ });
1278
+ }
1279
+ };
1280
+ }
1281
+ };
1282
+
1154
1283
  // src/utils/count-own-use-state-calls-in-node.ts
1155
1284
  function countOwnUseStateCallsInNode(node) {
1156
1285
  if (!isAstNode(node)) {
@@ -1461,17 +1590,6 @@ var noEmoji = {
1461
1590
  }
1462
1591
  };
1463
1592
 
1464
- // src/utils/collect-identifiers-named.ts
1465
- function collectIdentifiersNamed(node, name, results = []) {
1466
- if (node?.type === "Identifier" && node.name === name) {
1467
- results.push(node);
1468
- }
1469
- for (const child of getNodeChildren(node)) {
1470
- collectIdentifiersNamed(child, name, results);
1471
- }
1472
- return results;
1473
- }
1474
-
1475
1593
  // src/utils/get-no-tunnel-props-options.ts
1476
1594
  function getNoTunnelPropsOptions(options = {}) {
1477
1595
  return {
@@ -2045,6 +2163,7 @@ var rules = {
2045
2163
  }
2046
2164
  },
2047
2165
  "result-error-requires-cause": resultErrorRequiresCause,
2166
+ "result-error-requires-handling": resultErrorRequiresHandling,
2048
2167
  "max-hook-size": maxHookSize,
2049
2168
  "no-deep-relative-imports": noDeepRelativeImports,
2050
2169
  "no-default-export": noDefaultExport,
@@ -2062,4 +2181,4 @@ var rules = {
2062
2181
  export {
2063
2182
  rules
2064
2183
  };
2065
- //# sourceMappingURL=chunk-EXMF54EM.mjs.map
2184
+ //# sourceMappingURL=chunk-WZERKXPJ.mjs.map