@skapxd/eslint-opinionated 0.4.0 → 0.5.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.
package/README.md CHANGED
@@ -13,8 +13,9 @@ Cursor o Copilot.
13
13
 
14
14
  - **Una función por archivo:** un archivo con cinco helpers escondidos no pasa;
15
15
  la regla hasta te dibuja la carpeta sugerida con formato `tree`.
16
- - **Errores con `Result`:** una función `async` que puede fallar lo dice en su
17
- tipo de retorno (`Promise<Result<...>>`), no en una convención oral.
16
+ - **Errores con `Result`:** ningún `await` queda fuera del sistema de errores:
17
+ o llamas una función que retorna `Promise<Result<...>>` o envuelves la
18
+ operación en `trySafe`. Nada lanza sin que el tipo lo diga.
18
19
  - **Causa preservada:** al transformar un error de dominio, el `cause` original
19
20
  no puede desaparecer — type-aware, vía el checker de TypeScript.
20
21
  - **Hooks acotados:** un hook con demasiado estado deja de pasar como "solo un
@@ -251,6 +252,70 @@ hay errores, sale con código `1` (apto para CI). Como acota por **archivo
251
252
  completo**, también dispara las reglas estructurales (p. ej.
252
253
  `one-root-function-per-file`), que un filtrado por línea se perdería.
253
254
 
255
+ ## Cómo encaja todo: `@skapxd/result` + `ts-pattern`
256
+
257
+ Este plugin no es una colección de reglas sueltas: es el guardián de un
258
+ pipeline de errores donde cada pieza cierra un hueco que las otras dejan.
259
+
260
+ ```text
261
+ excepción ──trySafe──▶ Result ──map con cause──▶ error de dominio ──match()──▶ UI/respuesta
262
+ ```
263
+
264
+ | Pieza | Qué aporta | Regla que lo vigila |
265
+ | --- | --- | --- |
266
+ | `try/catch` prohibido | Los errores no viajan invisibles al tipo. | `skapxd/no-try-catch` |
267
+ | `.then/.catch` prohibido | Una sola forma de asincronía: `await`. | `skapxd/no-promise-chain` |
268
+ | `trySafe` (`@skapxd/result`) | La única puerta: lo que lanza se vuelve `Result`. | `skapxd/await-requires-result` |
269
+ | Errores de dominio con `cause` | Al traducir un error técnico, la causa sobrevive. | `skapxd/result-error-requires-cause` |
270
+ | Un solo contrato `Result` | Nada de `{ ok: ... }` caseros que fragmenten el sistema. | `skapxd/no-ad-hoc-ok-result` |
271
+ | `match()` (`ts-pattern`) | Consumo exhaustivo: el compilador exige manejar cada error. | `skapxd/prefer-ts-pattern` |
272
+
273
+ De punta a punta:
274
+
275
+ ```ts
276
+ import { Result, trySafe } from "@skapxd/result";
277
+ import { match } from "ts-pattern";
278
+
279
+ type UserError =
280
+ | { type: "NETWORK"; message: string; cause: unknown }
281
+ | { type: "NOT_FOUND"; message: string };
282
+
283
+ // 1. La frontera con el mundo que lanza: trySafe + errores de dominio.
284
+ async function getUser(id: string): Promise<Result<User, UserError>> {
285
+ const response = await trySafe(() => fetch(`/users/${id}`));
286
+
287
+ if (!response.ok) {
288
+ return Result.err({
289
+ cause: response.error, // result-error-requires-cause vigila esto
290
+ message: "No pude cargar el usuario.",
291
+ type: "NETWORK",
292
+ });
293
+ }
294
+
295
+ if (response.value.status === 404) {
296
+ return Result.err({ message: "El usuario no existe.", type: "NOT_FOUND" });
297
+ }
298
+
299
+ return trySafe(() => response.value.json());
300
+ }
301
+
302
+ // 2. El consumo: el await ya resuelve en Result (await-requires-result pasa)
303
+ // y match() obliga a manejar cada variante (prefer-ts-pattern).
304
+ const user = await getUser(id);
305
+
306
+ const label = match(user)
307
+ .with({ ok: true }, ({ value }) => value.name)
308
+ .with({ ok: false, error: { type: "NOT_FOUND" } }, () => "No existe")
309
+ .with({ ok: false, error: { type: "NETWORK" } }, () => "Reintenta")
310
+ .exhaustive();
311
+ ```
312
+
313
+ El resultado: ningún error puede escaparse (sin `try/catch` ni `.catch`, todo
314
+ pasa por `trySafe`), ningún error pierde su origen (siempre hay `cause` hasta
315
+ la excepción original), y ningún error queda sin manejar (el `.exhaustive()`
316
+ de ts-pattern no compila si falta una variante). Legibilidad y manejo de
317
+ errores dejan de depender de la disciplina del autor — humano o agente.
318
+
254
319
  ## Estructura del paquete
255
320
 
256
321
  ```text
@@ -302,6 +367,29 @@ export default [
302
367
  ];
303
368
  ```
304
369
 
370
+ El contrato del back es el mismo que el del front: todo `await` debe resolver
371
+ en un `Result` (`skapxd/await-requires-result`). Exigir además la firma
372
+ `Promise<Result<...>>` en cada función async
373
+ (`skapxd/async-functions-return-result`) está **apagado por defecto** — los
374
+ motivos están documentados en la sección de esa regla. Si quieres el contrato
375
+ duro, actívala encima del preset:
376
+
377
+ ```js
378
+ export default [
379
+ {
380
+ files: ["src/server/**/*.{ts,tsx}"],
381
+ ...skapxd.configs.shared.backend,
382
+ rules: {
383
+ ...skapxd.configs.shared.backend.rules,
384
+ "skapxd/async-functions-return-result": [
385
+ "error",
386
+ { checkMissingReturnType: true },
387
+ ],
388
+ },
389
+ },
390
+ ];
391
+ ```
392
+
305
393
  ### Frontend
306
394
 
307
395
  ```js
@@ -427,9 +515,9 @@ bloque con `linterOptions: { noInlineConfig: false }` para esos globs.
427
515
  | Regla | Qué protege |
428
516
  | --- | --- |
429
517
  | `skapxd/one-root-function-per-file` | Un archivo, una función top-level semántica. |
430
- | `skapxd/async-functions-return-result` | Funciones async de dominio deben retornar `Promise<Result<...>>`. |
518
+ | `skapxd/async-functions-return-result` | Funciones async de dominio deben retornar `Promise<Result<...>>`. **Apagada por defecto; opt-in** (ver motivos en su sección). |
431
519
  | `skapxd/result-error-requires-cause` | Un `Result.err` derivado debe preservar `cause: result.error`. |
432
- | `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`. La activa `shared.frontend`. |
520
+ | `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.** |
433
521
  | `skapxd/no-ad-hoc-ok-result` | Evita contratos `{ ok: ... }` hechos a mano en async exports. |
434
522
  | `skapxd/max-hook-size` | Marca hooks grandes o con demasiados `useState`. |
435
523
  | `skapxd/jsx-return-name-pascal-case` | Funciones que retornan JSX deben nombrarse como componentes. |
@@ -465,6 +553,39 @@ sugiere helpers al lado.
465
553
 
466
554
  ### `skapxd/async-functions-return-result`
467
555
 
556
+ > **Apagada por defecto desde v0.5.0** — ningún preset la activa. La regla
557
+ > obligatoria del sistema de errores es `skapxd/await-requires-result`.
558
+ >
559
+ > **Por qué se tomó esta decisión:**
560
+ >
561
+ > 1. **`await-requires-result` produce el mismo estado final con mejor
562
+ > ergonomía.** Si ningún `await` puede quedar sin `Result`, envolver con
563
+ > `trySafe` inline una y otra vez se vuelve incómodo rápido — la presión
564
+ > natural es extraer funciones que retornen `Promise<Result<...>>` con
565
+ > errores de dominio. Se llega a las mismas firmas que esta regla imponía,
566
+ > pero por gravedad, no por decreto.
567
+ > 2. **Imponer la firma choca con los bordes del framework.** Los handlers
568
+ > `GET/POST` de Next, `page.tsx`, los callbacks de librerías: sus firmas no
569
+ > son tuyas. Esta regla necesitaba listas de excepciones
570
+ > (`allowFilePatterns`, `allowNamePatterns`) para convivir con eso;
571
+ > `await-requires-result` no necesita ninguna, porque envolver un `await`
572
+ > es compatible con cualquier firma.
573
+ > 3. **Adopción incremental.** En un codebase existente, exigir la firma en
574
+ > cada función async lo rompe todo de golpe. Exigir `Result` en los `await`
575
+ > permite migrar llamada por llamada.
576
+ >
577
+ > Sigue disponible para quien quiera endurecer el contrato (p. ej. un backend
578
+ > nuevo donde todas las firmas son tuyas):
579
+ >
580
+ > ```js
581
+ > rules: {
582
+ > "skapxd/async-functions-return-result": ["error", {
583
+ > checkMissingReturnType: true,
584
+ > resultTypeNames: ["Result", "ResultValue", "SafeResult"],
585
+ > }],
586
+ > }
587
+ > ```
588
+
468
589
  Obliga a que funciones async en dominios configurados declaren un retorno como:
469
590
 
470
591
  ```ts
@@ -484,7 +605,8 @@ type Result<T, E> = ...; // ❌ Result ajeno
484
605
  async function no(): Promise<Result<number, Error>> {} // se reporta
485
606
  ```
486
607
 
487
- > Requiere `projectService` (los presets `backend` y `next/server` ya lo activan).
608
+ > Requiere `projectService` (actívalo en `languageOptions.parserOptions` o
609
+ > apóyate en un preset tipado del plugin, que ya lo trae).
488
610
  > Sin información de tipos cae a una comprobación por nombre (`resultTypeNames`),
489
611
  > menos estricta.
490
612
 
@@ -509,10 +631,12 @@ el archivo.
509
631
 
510
632
  ### `skapxd/await-requires-result`
511
633
 
512
- > La activa el preset `shared.frontend` en todo el front: componentes, hooks,
513
- > handlers y servicios. El contrato queda así: ninguna función está obligada
514
- > a retornar `Result`, pero todo `await` debe **resolver** en uno. Para
515
- > activarla en otros globs, añádela mismo:
634
+ > **Es la regla obligatoria del sistema de errores**: la activan todos los
635
+ > presets tipados (`shared.frontend`, `shared.backend`, `next/server`,
636
+ > `astro/typescript`). El contrato queda así: ninguna función está obligada
637
+ > a retornar `Result` (eso es `async-functions-return-result`, apagada por
638
+ > defecto), pero todo `await` debe **resolver** en uno. Para activarla en
639
+ > otros globs, añádela tú mismo:
516
640
  >
517
641
  > ```js
518
642
  > rules: {
@@ -650,8 +774,9 @@ const result = await trySafe(() => client.execute(query)); // ✅
650
774
  if (!result.ok) return Result.err({ cause: result.error, type: "DB_FAILED" });
651
775
  ```
652
776
 
653
- Se complementa con `result-error-requires-cause` (preservar la causa) y, si la
654
- activas, con `await-requires-result` (que además exige que cada `await` resuelva en un `Result`).
777
+ Se complementa con `result-error-requires-cause` (preservar la causa) y con
778
+ `await-requires-result` (obligatoria en los presets tipados: cada `await`
779
+ resuelve en un `Result`).
655
780
 
656
781
  ### `skapxd/no-promise-chain`
657
782
 
@@ -47,6 +47,7 @@ declare function createAstroConfigs(pluginReference: unknown): ({
47
47
  skapxd: unknown;
48
48
  };
49
49
  rules: {
50
+ "skapxd/await-requires-result": string;
50
51
  "skapxd/result-error-requires-cause": string;
51
52
  };
52
53
  })[];
@@ -47,6 +47,7 @@ declare function createAstroConfigs(pluginReference: unknown): ({
47
47
  skapxd: unknown;
48
48
  };
49
49
  rules: {
50
+ "skapxd/await-requires-result": string;
50
51
  "skapxd/result-error-requires-cause": string;
51
52
  };
52
53
  })[];
@@ -92,6 +92,7 @@ function createAstroConfigs(pluginReference) {
92
92
  name: "skapxd/astro/typescript",
93
93
  plugins: { skapxd: pluginReference },
94
94
  rules: {
95
+ "skapxd/await-requires-result": "error",
95
96
  "skapxd/result-error-requires-cause": "error"
96
97
  }
97
98
  }
@@ -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-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/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,2BAA2B;AAAA,EAC3B,uBAAuB;AAAA,EACvB,qCAAqC;AAAA,EACrC,4BAA4B;AAAA,EAC5B,sCAAsC;AACxC;;;ACRA,+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,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-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,2BAA2B;AAAA,EAC3B,uBAAuB;AAAA,EACvB,qCAAqC;AAAA,EACrC,4BAA4B;AAAA,EAC5B,sCAAsC;AACxC;;;ACRA,+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,7 +1,7 @@
1
1
  import {
2
2
  createAstroConfigs
3
- } from "../chunk-3XDQN6ZH.mjs";
4
- import "../chunk-NYLQRBBK.mjs";
3
+ } from "../chunk-7H7DRTP2.mjs";
4
+ import "../chunk-RWQGOTVZ.mjs";
5
5
  export {
6
6
  createAstroConfigs
7
7
  };
@@ -2,7 +2,7 @@ import {
2
2
  baseRules,
3
3
  createBaseLanguageOptions,
4
4
  createTypedLanguageOptions
5
- } from "./chunk-NYLQRBBK.mjs";
5
+ } from "./chunk-RWQGOTVZ.mjs";
6
6
 
7
7
  // src/astro/configs.ts
8
8
  function createAstroConfigs(pluginReference) {
@@ -30,6 +30,7 @@ function createAstroConfigs(pluginReference) {
30
30
  name: "skapxd/astro/typescript",
31
31
  plugins: { skapxd: pluginReference },
32
32
  rules: {
33
+ "skapxd/await-requires-result": "error",
33
34
  "skapxd/result-error-requires-cause": "error"
34
35
  }
35
36
  }
@@ -39,4 +40,4 @@ function createAstroConfigs(pluginReference) {
39
40
  export {
40
41
  createAstroConfigs
41
42
  };
42
- //# sourceMappingURL=chunk-3XDQN6ZH.mjs.map
43
+ //# sourceMappingURL=chunk-7H7DRTP2.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/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,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 },\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":[]}
@@ -2,7 +2,7 @@ import {
2
2
  baseRules,
3
3
  createBaseLanguageOptions,
4
4
  createTypedLanguageOptions
5
- } from "./chunk-NYLQRBBK.mjs";
5
+ } from "./chunk-RWQGOTVZ.mjs";
6
6
 
7
7
  // src/next/configs.ts
8
8
  function createNextConfigs(pluginReference) {
@@ -22,21 +22,11 @@ function createNextConfigs(pluginReference) {
22
22
  plugins: { skapxd: pluginReference },
23
23
  rules: {
24
24
  ...baseRules,
25
- "skapxd/async-functions-return-result": [
26
- "error",
27
- {
28
- allowFilePatterns: [
29
- "/(route|page|layout|template|loading|error|not-found)\\.tsx?$"
30
- ],
31
- allowNamePatterns: [
32
- "^(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)$",
33
- "^handle(Get|Post|Put|Patch|Delete|Head|Options)$",
34
- "^start$"
35
- ],
36
- checkMissingReturnType: true,
37
- resultTypeNames: ["Result", "ResultValue", "SafeResult"]
38
- }
39
- ]
25
+ // Obligatoria: todo await resuelve en Result. A diferencia de
26
+ // async-functions-return-result (apagada por defecto), no necesita
27
+ // excepciones para los entrypoints de Next: envolver un await en
28
+ // trySafe es compatible con cualquier firma que imponga el framework.
29
+ "skapxd/await-requires-result": "error"
40
30
  }
41
31
  },
42
32
  {
@@ -63,4 +53,4 @@ function createNextConfigs(pluginReference) {
63
53
  export {
64
54
  createNextConfigs
65
55
  };
66
- //# sourceMappingURL=chunk-OQLKVDGZ.mjs.map
56
+ //# sourceMappingURL=chunk-QJQ2E2NG.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/next/configs.ts"],"sourcesContent":["import {\n baseRules,\n createBaseLanguageOptions,\n createTypedLanguageOptions,\n} from \"#/shared/configs\";\n\nexport function createNextConfigs(pluginReference: unknown) {\n const baseLanguageOptions = createBaseLanguageOptions();\n const typedLanguageOptions = createTypedLanguageOptions();\n\n return [\n {\n languageOptions: baseLanguageOptions,\n name: \"skapxd/next/base\",\n plugins: { skapxd: pluginReference },\n rules: baseRules,\n },\n {\n files: [\"src/app/api/**/*.{ts,tsx}\", \"src/server/**/*.{ts,tsx}\"],\n languageOptions: typedLanguageOptions,\n name: \"skapxd/next/server\",\n plugins: { skapxd: pluginReference },\n rules: {\n ...baseRules,\n // Obligatoria: todo await resuelve en Result. A diferencia de\n // async-functions-return-result (apagada por defecto), no necesita\n // excepciones para los entrypoints de Next: envolver un await en\n // trySafe es compatible con cualquier firma que imponga el framework.\n \"skapxd/await-requires-result\": \"error\",\n },\n },\n {\n files: [\"**/*.tsx\"],\n languageOptions: baseLanguageOptions,\n name: \"skapxd/next/react\",\n plugins: { skapxd: pluginReference },\n rules: {\n \"skapxd/jsx-return-name-pascal-case\": \"error\",\n \"skapxd/no-functions-inside-components\": \"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 ];\n}\n"],"mappings":";;;;;;;AAMO,SAAS,kBAAkB,iBAA0B;AAC1D,QAAM,sBAAsB,0BAA0B;AACtD,QAAM,uBAAuB,2BAA2B;AAExD,SAAO;AAAA,IACL;AAAA,MACE,iBAAiB;AAAA,MACjB,MAAM;AAAA,MACN,SAAS,EAAE,QAAQ,gBAAgB;AAAA,MACnC,OAAO;AAAA,IACT;AAAA,IACA;AAAA,MACE,OAAO,CAAC,6BAA6B,0BAA0B;AAAA,MAC/D,iBAAiB;AAAA,MACjB,MAAM;AAAA,MACN,SAAS,EAAE,QAAQ,gBAAgB;AAAA,MACnC,OAAO;AAAA,QACL,GAAG;AAAA;AAAA;AAAA;AAAA;AAAA,QAKH,gCAAgC;AAAA,MAClC;AAAA,IACF;AAAA,IACA;AAAA,MACE,OAAO,CAAC,UAAU;AAAA,MAClB,iBAAiB;AAAA,MACjB,MAAM;AAAA,MACN,SAAS,EAAE,QAAQ,gBAAgB;AAAA,MACnC,OAAO;AAAA,QACL,sCAAsC;AAAA,QACtC,yCAAyC;AAAA,QACzC,8BAA8B;AAAA,QAC9B,wBAAwB;AAAA,UACtB;AAAA,UACA;AAAA,YACE,UAAU;AAAA,YACV,aAAa;AAAA,UACf;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
@@ -593,8 +593,8 @@ var asyncFunctionsReturnResult = {
593
593
  description: "Exige Promise<Result<...>> en funciones async de dominio."
594
594
  },
595
595
  messages: {
596
- missingReturnType: "La funcion async `{{name}}` debe declarar Promise<Result<...>> como tipo de retorno.",
597
- invalidReturnType: "La funcion async `{{name}}` debe retornar Promise<Result<...>> para modelar errores de forma explicita."
596
+ missingReturnType: "La funcion async `{{name}}` debe declarar Promise<Result<...>> como tipo de retorno: trySafe en la frontera, errores de dominio con `cause`, y el consumidor decide con `match()` de ts-pattern.",
597
+ invalidReturnType: "La funcion async `{{name}}` debe retornar Promise<Result<...>> para modelar errores de forma explicita: trySafe en la frontera, errores de dominio con `cause`, y el consumidor decide con `match()` de ts-pattern."
598
598
  },
599
599
  schema: [
600
600
  {
@@ -780,7 +780,7 @@ var noAdHocOkResult = {
780
780
  description: "Prohibe retornar contratos ad hoc con ok en funciones async exportadas."
781
781
  },
782
782
  messages: {
783
- adHocOkResult: "No retornes objetos ad hoc con `ok` desde la funcion async `{{name}}`. Usa Result.ok(...) / Result.err(...) con un error discriminado `{ type: ... }`."
783
+ adHocOkResult: "No retornes objetos ad hoc con `ok` desde la funcion async `{{name}}`. Usa Result.ok(...) / Result.err(...) de @skapxd/result con un error discriminado `{ type: ... }`: un unico contrato Result permite consumir cada variante con `match()` de ts-pattern y `.exhaustive()`."
784
784
  },
785
785
  schema: []
786
786
  },
@@ -903,7 +903,7 @@ var awaitRequiresResult = {
903
903
  description: "Exige que todo await resuelva en un Result: una funcion que retorne Promise<Result<...>> o trySafe en el sitio."
904
904
  },
905
905
  messages: {
906
- awaitWithoutResult: "El await dentro de `{{name}}` no resuelve en un Result. Mejor opcion: extrae la operacion a una funcion que retorne Promise<Result<...>> y modela ahi los errores de dominio (el trySafe vive dentro de esa funcion). Alternativa: envuelvela aqui mismo: `{{suggestion}}`."
906
+ awaitWithoutResult: "El await dentro de `{{name}}` no resuelve en un Result. Mejor opcion: extrae la operacion a una funcion que retorne Promise<Result<...>> y modela ahi los errores de dominio con `{ type, message, cause }` (el trySafe vive dentro de esa funcion). Alternativa: envuelvela aqui mismo: `{{suggestion}}`. En ambos casos, consume el Result con `match()` de ts-pattern."
907
907
  },
908
908
  schema: [
909
909
  {
@@ -1115,7 +1115,7 @@ var resultErrorRequiresCause = {
1115
1115
  description: "Exige preservar result.error como cause cuando una funcion que retorna Result transforma un Result fallido en Result.err."
1116
1116
  },
1117
1117
  messages: {
1118
- missingCause: "El error de `{{name}}` ya existe como `{{name}}.error`. Preservalo en Result.err con `cause: {{name}}.error`."
1118
+ missingCause: "El error de `{{name}}` ya existe como `{{name}}.error`. Preservalo en Result.err con `cause: {{name}}.error`: la cadena de causas conecta el error de dominio con la excepcion original que capturo `trySafe`; sin ella el debugging pierde el contexto."
1119
1119
  },
1120
1120
  schema: []
1121
1121
  },
@@ -1382,7 +1382,7 @@ var noTryCatch = {
1382
1382
  description: "Prohibe try/catch; usa trySafe de @skapxd/result para modelar el error como Result."
1383
1383
  },
1384
1384
  messages: {
1385
- noTryCatch: "Usa `trySafe` de @skapxd/result en lugar de try/catch. El error queda modelado como Result en vez de saltar como excepcion."
1385
+ noTryCatch: "Usa `trySafe` de @skapxd/result en lugar de try/catch: el error queda modelado como Result en vez de saltar como excepcion. Luego mapealo a un error de dominio `{ type, message, cause }` y consumelo con `match()` de ts-pattern."
1386
1386
  },
1387
1387
  schema: []
1388
1388
  },
@@ -1406,8 +1406,8 @@ var preferTsPattern = {
1406
1406
  description: "Prefiere match() de ts-pattern sobre switch/case y ternarios anidados."
1407
1407
  },
1408
1408
  messages: {
1409
- noSwitch: "Usa `match()` de ts-pattern en lugar de switch/case para un control de flujo exhaustivo y tipado.",
1410
- noNestedTernary: "Usa `match()` de ts-pattern en lugar de ternarios anidados; mejora la legibilidad y la exhaustividad."
1409
+ noSwitch: "Usa `match()` de ts-pattern en lugar de switch/case para un control de flujo exhaustivo y tipado. Es la pieza que cierra el sistema de errores: un Result con errores `{ type: ... }` se consume con una rama `.with()` por variante y `.exhaustive()` garantiza que ninguna quede sin manejar.",
1410
+ noNestedTernary: "Usa `match()` de ts-pattern en lugar de ternarios anidados; mejora la legibilidad y `.exhaustive()` obliga a cubrir todos los casos."
1411
1411
  },
1412
1412
  schema: []
1413
1413
  },
@@ -1483,7 +1483,7 @@ var noPromiseChain = {
1483
1483
  description: "Prohibe encadenar .then/.catch/.finally en promesas; usa await (envuelto en trySafe)."
1484
1484
  },
1485
1485
  messages: {
1486
- noPromiseChain: "No encadenes `.{{method}}()` en una promesa. La unica forma de tratar funciones asincronas es `await` (envuelto en `trySafe`)."
1486
+ noPromiseChain: "No encadenes `.{{method}}()` en una promesa. La unica forma de tratar funciones asincronas es `await`: o la funcion llamada ya retorna Promise<Result<...>> o envuelve la llamada en `trySafe` de @skapxd/result."
1487
1487
  },
1488
1488
  schema: [
1489
1489
  {
@@ -1556,4 +1556,4 @@ var rules = {
1556
1556
  export {
1557
1557
  rules
1558
1558
  };
1559
- //# sourceMappingURL=chunk-OYVXAPDZ.mjs.map
1559
+ //# sourceMappingURL=chunk-QNSHGNVQ.mjs.map