parche-cli 0.1.1 → 0.5.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/CHANGELOG.md +97 -0
- package/README.md +83 -13
- package/bin/parche.js +144 -42
- package/data/known-sdks.json +125 -0
- package/package.json +7 -3
- package/packs/ai.model-retirements.json +67 -15
- package/packs/arca.wsfe.condicion-iva-receptor.json +7 -5
- package/src/ast.js +21 -1
- package/src/codemods/arca-rules.js +109 -0
- package/src/codemods/arca-wsfe-condicion-iva-php.js +195 -0
- package/src/codemods/arca-wsfe-condicion-iva.js +105 -66
- package/src/context.js +93 -0
- package/src/detectors/ai-model-retirements.js +34 -19
- package/src/engine.js +26 -22
- package/src/git.js +42 -6
- package/src/inventory/index.js +133 -0
- package/src/inventory/js-scan.js +235 -0
- package/src/inventory/known.js +60 -0
- package/src/packs.js +21 -5
- package/src/paths.js +37 -0
- package/src/php.js +52 -0
- package/src/report.js +70 -17
- package/src/sdk-diff/compare.js +121 -0
- package/src/sdk-diff/registry.js +142 -0
- package/src/sdk-diff/surface.js +159 -0
- package/src/sdk-diff/tar.js +47 -0
- package/src/sdk-diff/upgrades.js +107 -0
- package/src/verify.js +64 -8
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# Cambios
|
|
2
|
+
|
|
3
|
+
Todas las versiones de `parche-cli` (npm) y de la Action `Pepo08/parche@v0`.
|
|
4
|
+
|
|
5
|
+
## 0.5.1 — 2026-10-17
|
|
6
|
+
|
|
7
|
+
Lo que apareció en el primer PR real (lunamarie11/obraya#1).
|
|
8
|
+
|
|
9
|
+
### Arreglado
|
|
10
|
+
- **Los commits ya no se firman con `bot@parche.dev`**, un dominio que no es nuestro. Ahora firma `PARCHE_GIT_NAME`/`PARCHE_GIT_EMAIL` si están; si no, la identidad de git de quien lo corre; y en GitHub Actions sin identidad, `github-actions[bot]`. Sin ninguna, `parche pr` frena antes de tocar el repo.
|
|
11
|
+
- **`parche pr` chequea la sintaxis siempre**, también sin `--verify` (no ejecuta nada del repo). El PR ya no dice "Sin verificar": dice que Parche no corrió los tests y nombra los workflows del repo que los corren en cada pull request (`pull_request`; `pull_request_target` no cuenta).
|
|
12
|
+
- El texto para un issue (sin cambios de código) ya no lleva la sección de verificación.
|
|
13
|
+
|
|
14
|
+
## 0.5.0 — 2026-10-16
|
|
15
|
+
|
|
16
|
+
Lo que encontró el frente de tracción al correr Parche sobre 175 repos públicos reales.
|
|
17
|
+
|
|
18
|
+
### Nuevo
|
|
19
|
+
- **La versión de la librería de ARCA importa:** con `afip.js`/`afip.php` 0.x (o `eesnaola/afip.php`) el campo no llega a ARCA (WSDL de 2021). Parche lee la versión instalada y el arreglo pasa a "revisar" con el aviso de actualizar a 1.x.
|
|
20
|
+
- Detección de **`.wsdl` de WSFEv1 sin el campo** en el repo.
|
|
21
|
+
- **Python, C#, Java, Go y VB.NET:** detección de integraciones SOAP propias y de clientes SOAP generados sin el campo.
|
|
22
|
+
- `parche pr --push-remote <remote>`: abre PRs **desde un fork**.
|
|
23
|
+
- **Pack de IA al día con las páginas oficiales:** `claude-sonnet-4-5-20250929` se retira el 30/11/2026; OpenAI retira `gpt-3.5-turbo`, `gpt-4`, `gpt-4-turbo`, `o1`, `o3-mini`, `o4-mini`, `gpt-4.1-nano`, `gpt-image-1` y otros el **23/10/2026**, y los snapshots de `gpt-5-*-2025-08-07` y `o3` el 11/12/2026; suma los ya retirados (DALL·E, `gpt-3.5-turbo-instruct`, `chatgpt-4o-latest`, Claude 2/3.5…). Cada aviso trae el reemplazo recomendado.
|
|
24
|
+
|
|
25
|
+
### Arreglado
|
|
26
|
+
- `parche pr` dejaba el archivo modificado en la rama base cuando fallaba la verificación; ahora el repo queda como estaba y la rama se borra. Tampoco quedan ramas vacías.
|
|
27
|
+
- No se abre un PR cuyo único cambio sea un test o un mock.
|
|
28
|
+
- ARCA en JS: se reconocen `(afip.ElectronicBilling as any).createVoucher(...)`, `afip.ElectronicBilling!` y alias (`const eb = afip.ElectronicBilling`). Si un archivo de ARCA no parsea, se avisa (antes se descartaba en silencio).
|
|
29
|
+
- Menos ruido: la detección de SOAP propio ya no marca tests, mocks, tipos, constantes ni copias de la librería; si el repo ya usa el campo en otro archivo, lo que no se pudo leer pasa a "revisar".
|
|
30
|
+
- Pack de IA: un modelo en una lista o catálogo se avisa como mención, no como uso; en Azure OpenAI no se da por cerrada la Assistants API.
|
|
31
|
+
- Mucho más rápido en repos grandes: los codemods de ARCA no parsean archivos que no mencionan ARCA.
|
|
32
|
+
- El texto para un issue (sin cambios de código) ya no habla de "este PR".
|
|
33
|
+
- `detect.packages` de ARCA: fuera `afip.js` (no existe en npm); suma `@arcasdk/core`, `afip.ts`, `ts-afip-ws` y `facturajs`.
|
|
34
|
+
|
|
35
|
+
### Cambiado
|
|
36
|
+
- `docs/arquitectura.md` sale del repo: era documentación interna del equipo. El formato del inventario sigue en `docs/inventario.md`.
|
|
37
|
+
|
|
38
|
+
## 0.4.1 — 2026-10-16
|
|
39
|
+
|
|
40
|
+
Arreglos encontrados al correr Parche sobre los primeros repos públicos reales con ARCA y Mercado Pago.
|
|
41
|
+
|
|
42
|
+
### Arreglado
|
|
43
|
+
- **ARCA:** si el mismo objeto de comprobante se pasaba a dos llamadas, el campo se insertaba dos veces (clave duplicada: TypeScript no compila). Ahora se inserta una sola vez, en JS y en PHP.
|
|
44
|
+
- **Decoradores** (NestJS, Angular, TypeORM): el chequeo de sintaxis fallaba y `--verify`/`--dry-run` frenaban el PR; el inventario no veía nada en esos archivos.
|
|
45
|
+
- **Inventario en monorepos:** se leen todos los `package.json`, `composer.json` y `requirements.txt` del repo, no solo los de la raíz (`manifestFiles` dice cuáles).
|
|
46
|
+
- **Inventario:** se siguen los clientes guardados en clases (`private x = new X()`, `this.x = new X()`).
|
|
47
|
+
- `http://localhost:${PORT}` ya no aparece como un host externo.
|
|
48
|
+
- `socket.io-client`, `@prisma/client`, `@trpc/client`, `pg`, `ws`, `@aws-sdk/types` y similares ya no se listan como APIs; toda la familia `@aws-sdk/` cuenta como AWS.
|
|
49
|
+
|
|
50
|
+
### Mejorado
|
|
51
|
+
- **ARCA:** cuando el comprobante lo arma otra función o llega como parámetro, el hallazgo dice dónde agregar el dato (por ejemplo: "lo arma `buildVoucherPayload()` (de `@/lib/afip/voucher-builder`)").
|
|
52
|
+
|
|
53
|
+
## 0.4.0 — 2026-10-16
|
|
54
|
+
|
|
55
|
+
### Nuevo
|
|
56
|
+
- **Qué cambia en las versiones nuevas de tus SDKs** (`parche scan --upgrades`): para cada SDK de npm que el repo usa, compara la versión instalada con la última publicada leyendo sus tipos (`.d.ts`) y dice qué operaciones **del repo** desaparecen, piden parámetros obligatorios nuevos, pierden parámetros, cambian de tipo o pasan a deprecadas, con archivo y línea. Solo avisa (detect-only).
|
|
57
|
+
- **`parche diff <paquete> --from <v> --to <v>`**: el mismo análisis para cualquier paquete, sin repo (acepta `latest` y versiones parciales; `--all`, `--json`).
|
|
58
|
+
- Tarballs bajados del registry con verificación de integridad y cacheados en `~/.parche/cache` (`PARCHE_CACHE`, `PARCHE_OFFLINE=1`). Si el paquete no trae tipos, usa `@types/<paquete>`.
|
|
59
|
+
- Ejemplo nuevo: `examples/legacy-ia` (SDK de OpenAI v3: `OpenAIApi` desaparece en las versiones nuevas, como dice la guía oficial de migración).
|
|
60
|
+
- Dependencia nueva: `typescript ~6.0.3` (la última con el compiler API de JavaScript), cargada solo para el diff.
|
|
61
|
+
|
|
62
|
+
## 0.3.0 — 2026-10-16
|
|
63
|
+
|
|
64
|
+
### Nuevo
|
|
65
|
+
- **Inventario de cualquier API** (nivel 1): `parche scan` lista todas las APIs de terceros del repo, tengan pack o no.
|
|
66
|
+
- **SDKs** de `package.json`, `composer.json` y `requirements.txt`, clasificados con una lista curada (`data/known-sdks.json`: pagos, facturación, IA, mensajería, email, nube, datos y más) y una heurística por nombre, con nivel de confianza.
|
|
67
|
+
- En JS/TS, **qué operaciones** se usan de cada SDK (`stripe.paymentIntents.create`, `client.messages.create`…), siguiendo imports, `require` e instancias.
|
|
68
|
+
- **Llamadas HTTP directas** con `fetch`, `axios` (incluido `axios.create({ baseURL })`), `got`, `undici` y `node-fetch`: método, host y ruta. Las URLs dinámicas se reportan aparte; lo local y lo relativo se descarta.
|
|
69
|
+
- **Modelos de IA** nombrados en el código (OpenAI, Anthropic, Gemini, Mistral, Cohere).
|
|
70
|
+
- `.parche/inventory.json` con `schemaVersion: 1`, documentado en `docs/inventario.md` (los campos anteriores se mantienen).
|
|
71
|
+
- La salida de `scan` y `.parche/report.md` muestran el inventario completo.
|
|
72
|
+
- Ejemplo nuevo: `examples/saas-demo` (Stripe, Twilio, OpenAI, GitHub, Mercado Libre y HTTP directo).
|
|
73
|
+
- `docs/arquitectura.md`: qué hace cada parte del código y por qué.
|
|
74
|
+
|
|
75
|
+
## 0.2.0 — 2026-10-16
|
|
76
|
+
|
|
77
|
+
### Nuevo
|
|
78
|
+
- **Pack ARCA para PHP**: `afipsdk/afip.php` y sus forks (`fertandil87/afip.php`, `ivanalemunioz/afip.php`, `eesnaola/afip.php`). Agrega `CondicionIVAReceptorId` a `$afip->ElectronicBilling->CreateVoucher($data)` y `CreateNextVoucher($data)` con las mismas reglas que en JS: DocTipo 99 → automático; DNI con comprobante B o C → para revisar; factura A o CUIT → pide el dato y muestra los códigos válidos.
|
|
79
|
+
- Lee el array en la llamada (`array()` o `[]`) o en una variable definida una vez en la misma función; si no puede leerlo con certeza, lo reporta y no toca nada.
|
|
80
|
+
- Copia el estilo del cliente: comillas, indentación (espacios o tabs), `=>` alineados, coma final y fin de línea.
|
|
81
|
+
- El motor escanea archivos `.php`, lee `composer.json` y saltea `vendor/`.
|
|
82
|
+
- `--verify` corre `composer test` o `vendor/bin/phpunit` cuando cambian archivos PHP (y `npm test` cuando cambian archivos JS/TS).
|
|
83
|
+
- Ejemplo nuevo: `examples/tienda-php`.
|
|
84
|
+
|
|
85
|
+
### Arreglado
|
|
86
|
+
- El cuerpo del PR decía "✅ pasan" cuando el repo no tenía tests; ahora dice que no hubo tests y que se validó la sintaxis.
|
|
87
|
+
- Mercado Pago: el `import { randomUUID }` nuevo queda junto a los otros imports.
|
|
88
|
+
- ARCA (JS): si la última propiedad no tenía coma final, la propiedad nueva tampoco la lleva.
|
|
89
|
+
|
|
90
|
+
## 0.1.1 — 2026-10-15
|
|
91
|
+
|
|
92
|
+
- Publicado en npm como `parche-cli` (npm no acepta `parche`). El comando instalado sigue siendo `parche`.
|
|
93
|
+
|
|
94
|
+
## 0.1.0 — 2026-10-15
|
|
95
|
+
|
|
96
|
+
- Primera versión: CLI (`packs`, `scan`, `fix`, `pr`, `feedback`), GitHub Action y packs de ARCA (afip.js), Mercado Pago Payments → Orders y retiros de modelos de IA.
|
|
97
|
+
- CI en Linux y Windows. Arreglados 4 bugs que solo aparecían en Windows (offsets de recast, CRLF, rutas con `\`, `npm test` sin shell).
|
package/README.md
CHANGED
|
@@ -2,24 +2,31 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://github.com/Pepo08/parche/actions/workflows/ci.yml)
|
|
4
4
|
|
|
5
|
-
Parche lee tu código
|
|
5
|
+
Parche lee tu código y arma el inventario de **todas** las APIs de terceros que usás: SDKs (Stripe, Twilio, OpenAI, AWS, Mercado Pago, ARCA…), llamadas HTTP directas y modelos de IA. Lo cruza contra una base de **cambios de API verificados** y, cuando un proveedor cambia algo que te afecta, te avisa o abre un PR con el arreglo, explica qué cambió y por qué, y marca lo que necesita una persona. Ningún PR sale sin pasar la verificación. Las APIs de pagos y facturación de Latinoamérica son el fuerte, pero el inventario cubre cualquier API.
|
|
6
6
|
|
|
7
7
|
```
|
|
8
8
|
$ parche scan .
|
|
9
|
-
APIs
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
APIs del repo (6):
|
|
10
|
+
SDK Anthropic · @anthropic-ai/sdk alta messages.create · con pack
|
|
11
|
+
SDK ARCA (ex AFIP) · @afipsdk/afip.js alta ElectronicBilling.createNextVoucher ×3 · con pack
|
|
12
|
+
SDK Mercado Pago · mercadopago alta Payment.create ×3, Payment.get · con pack
|
|
13
|
+
SDK OpenAI · openai alta beta.assistants.create · con pack
|
|
14
|
+
IA Anthropic · modelos alta claude-3-5-haiku-20241022 · con pack
|
|
15
|
+
IA OpenAI · modelos alta gpt-4o · con pack
|
|
13
16
|
|
|
14
17
|
■ Condición frente al IVA del receptor obligatoria en WSFEv1
|
|
15
|
-
ARCA (ex AFIP) · severidad critical · vence 2026-12-01 (
|
|
18
|
+
ARCA (ex AFIP) · severidad critical · vence 2026-12-01 (46 días)
|
|
16
19
|
✅ automático src/facturacion.js:14 Agregado CondicionIVAReceptorId: 5 (Consumidor Final)…
|
|
17
20
|
👀 revisar src/facturacion.js:56 Agregado CondicionIVAReceptorId: 5 por inferencia (DNI + comprobante B/C)…
|
|
18
21
|
✋ falta un dato src/facturacion.js:62 Falta CondicionIVAReceptorId y no se puede inferir… (comprobante clase A).
|
|
22
|
+
|
|
23
|
+
■ Modelos y endpoints de IA retirados o por retirarse
|
|
24
|
+
🔎 detectado src/ia.js:9 El modelo `claude-3-5-haiku-20241022` fue retirado el 2026-02-19: las llamadas ya fallan…
|
|
19
25
|
…
|
|
20
|
-
✅ automático: 5 👀 revisar: 8 ✋ falta un dato: 1 ⛔ bloqueado: 1 🔎 detectado: 2
|
|
21
26
|
```
|
|
22
27
|
|
|
28
|
+
El inventario completo (con operaciones, ubicaciones y nivel de confianza) queda en `.parche/inventory.json`; el formato está en [`docs/inventario.md`](docs/inventario.md). Para ver un repo con APIs de todo tipo, desde este repo: `node bin/parche.js scan examples/saas-demo`.
|
|
29
|
+
|
|
23
30
|
## Por qué packs y no "un LLM que arregla todo"
|
|
24
31
|
|
|
25
32
|
Ya hay varios prototipos que le piden a un modelo que lea un changelog y escriba el fix. El problema es que el modelo solo sabe lo que aprendió en su entrenamiento, y una migración de pagos o de facturación no admite "casi bien". Parche separa las dos cosas:
|
|
@@ -36,6 +43,8 @@ Requiere Node 22 o más nuevo. El paquete de npm se llama `parche-cli`; si lo in
|
|
|
36
43
|
```bash
|
|
37
44
|
npx parche-cli packs # cambios que Parche conoce
|
|
38
45
|
npx parche-cli scan . # inventario + qué te afecta (.parche/report.md)
|
|
46
|
+
npx parche-cli scan . --upgrades # + qué cambia en las versiones nuevas de tus SDKs
|
|
47
|
+
npx parche-cli diff <paquete> --from <v> --to <v> # qué cambia en la API de un paquete
|
|
39
48
|
npx parche-cli fix . # aplica los arreglos en tu working copy
|
|
40
49
|
npx parche-cli pr . --verify # una rama + commit + PR por cambio
|
|
41
50
|
npx parche-cli feedback . --pack <id> --outcome merged|edited|rejected
|
|
@@ -43,6 +52,27 @@ npx parche-cli feedback . --pack <id> --outcome merged|edited|rejected
|
|
|
43
52
|
|
|
44
53
|
`parche pr` abre el PR por la API de GitHub si hay `GITHUB_TOKEN` y un remote `origin` en github.com; si no (o con `--dry-run`), deja la rama, el commit y el cuerpo del PR en `.parche/pr-<pack>.md`. Si hay ítems para revisar, el PR se abre como borrador.
|
|
45
54
|
|
|
55
|
+
- **Desde un fork:** `parche pr . --push-remote fork` pushea la rama al remote `fork` (tu fork, con tus credenciales de git) y abre el PR contra `origin`.
|
|
56
|
+
- **Nunca deja el repo sucio:** si la verificación falla, descarta lo que escribió y borra su rama. Si no hay cambios de código, o solo cambiarían tests o mocks, no abre PR: deja el texto como issue en `.parche/`.
|
|
57
|
+
|
|
58
|
+
### Qué cambia en las versiones nuevas de tus SDKs
|
|
59
|
+
|
|
60
|
+
`--upgrades` compara, para cada SDK de npm que usás, la versión instalada con la última publicada. Lee los tipos (`.d.ts`) de las dos versiones y te dice qué operaciones **de tu código** desaparecen, piden parámetros nuevos, cambian de tipo o pasan a deprecadas. Solo avisa: no toca código.
|
|
61
|
+
|
|
62
|
+
```
|
|
63
|
+
$ npx parche-cli scan . --upgrades
|
|
64
|
+
…
|
|
65
|
+
Versiones nuevas de tus SDKs de npm:
|
|
66
|
+
openai 3.3.0 → 7.34.0 ⚠ 2 de tus operaciones cambian o desaparecen
|
|
67
|
+
OpenAIApi.createChatCompletion (src/asistente.js:8)
|
|
68
|
+
`OpenAIApi` ya no existe.
|
|
69
|
+
OpenAIApi.createEmbedding (src/asistente.js:16)
|
|
70
|
+
`OpenAIApi` ya no existe.
|
|
71
|
+
stripe 17.0.0 → 23.0.0 ✓ lo que usás no cambia (22 cambios en el SDK)
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Y para un paquete cualquiera, sin repo: `npx parche-cli diff stripe --from 17 --to latest` (acepta versiones parciales y `latest`; `--all` para ver todo, `--json` para procesarlo). Los tarballs quedan en `~/.parche/cache` (cambiable con `PARCHE_CACHE`); con `PARCHE_OFFLINE=1` usa solo el caché.
|
|
75
|
+
|
|
46
76
|
### En GitHub Actions
|
|
47
77
|
|
|
48
78
|
Para que corra todas las semanas, copiá [`templates/github-workflow.yml`](templates/github-workflow.yml) a `.github/workflows/parche.yml`. El paso clave es:
|
|
@@ -61,10 +91,30 @@ El workflow necesita `permissions: contents: write` y `pull-requests: write`.
|
|
|
61
91
|
|
|
62
92
|
```bash
|
|
63
93
|
npm install
|
|
64
|
-
npm test #
|
|
65
|
-
bash demo.sh # demo completa sobre examples/tienda-demo
|
|
94
|
+
npm test # el CI los corre en Linux y Windows
|
|
95
|
+
bash demo.sh # demo completa sobre examples/tienda-demo (JS)
|
|
96
|
+
node bin/parche.js scan examples/tienda-php # ejemplo PHP con afip.php
|
|
97
|
+
node bin/parche.js scan examples/saas-demo # SaaS con Stripe, Twilio, OpenAI y HTTP directo
|
|
98
|
+
node bin/parche.js scan examples/legacy-ia --upgrades # OpenAI v3: qué se rompe al actualizar (usa la red)
|
|
66
99
|
```
|
|
67
100
|
|
|
101
|
+
#### Sacar una versión
|
|
102
|
+
|
|
103
|
+
Las versiones se publican solas desde GitHub Actions ([`release.yml`](.github/workflows/release.yml)) con [trusted publishing](https://docs.npmjs.com/trusted-publishers): npm confía en este repo y en ese workflow, sin tokens ni 2FA a mano. Desde `main` actualizado:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
npm version 0.1.2 --no-git-tag-version # cambia package.json y package-lock.json
|
|
107
|
+
git commit -am "Parche 0.1.2"
|
|
108
|
+
git tag -a v0.1.2 -m "Parche 0.1.2: qué cambió" # el mensaje del tag es el texto del GitHub Release
|
|
109
|
+
git push origin main v0.1.2
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Con el tag, el workflow corre los tests, controla que la versión de `package.json` coincida con el tag y que el commit esté en `main`, publica `parche-cli` en npm (con provenance), mueve `v0` a esta versión (es la que usan los workflows con `Pepo08/parche@v0`) y crea el GitHub Release. Para notas largas, `git tag -a v0.1.2 -F notas.md`.
|
|
113
|
+
|
|
114
|
+
- Solo dispara con versiones estables (`vX.Y.Z`); un tag como `v0.2.0-beta.1` no hace nada.
|
|
115
|
+
- Si falla un paso después de publicar, "Re-run failed jobs" retoma: lo que ya está hecho se saltea.
|
|
116
|
+
- Requiere, una sola vez, el Trusted Publisher en npmjs.com: `parche-cli` → Settings → Trusted publishing → GitHub Actions, con `Pepo08` / `parche` / `release.yml` y permiso para `npm publish`.
|
|
117
|
+
|
|
68
118
|
## Estados de cada hallazgo
|
|
69
119
|
|
|
70
120
|
| Estado | Qué significa |
|
|
@@ -79,18 +129,38 @@ bash demo.sh # demo completa sobre examples/tienda-demo
|
|
|
79
129
|
|
|
80
130
|
| Pack | Proveedor | Tipo | Fecha límite | Arreglo |
|
|
81
131
|
|---|---|---|---|---|
|
|
82
|
-
| `arca.wsfe.condicion-iva-receptor` | ARCA | rompe | 2026-12-01 |
|
|
132
|
+
| `arca.wsfe.condicion-iva-receptor` | ARCA | rompe | 2026-12-01 | sí, en JS (`afip.js`) y PHP (`afipsdk/afip.php` y forks): consumidor final y DNI B/C |
|
|
83
133
|
| `mercadopago.payments-to-orders` | Mercado Pago | migración | — | sí (creación de pagos en SDK Node v2) |
|
|
84
134
|
| `ai.model-retirements` | Anthropic, OpenAI | retiro | varias | solo detección |
|
|
85
135
|
|
|
86
136
|
## Verificación
|
|
87
137
|
|
|
88
|
-
Antes de commitear,
|
|
138
|
+
Antes de commitear, Parche parsea **siempre** cada archivo modificado (JS/TS o PHP): eso no ejecuta nada del repo. Con `--verify`, además corre los tests del repo que correspondan a lo que cambió: `npm test` para JS/TS; para PHP, `composer test` o `vendor/bin/phpunit`. Si no hay tests, queda el chequeo de sintaxis y el PR lo dice. Sin `--verify` (por ejemplo, en un PR a un repo de terceros, donde no se ejecuta su código), el PR dice que Parche no corrió los tests y nombra los workflows del repo que los corren en cada pull request. Si algo falla, no hay PR. El commit lleva la versión del pack que lo generó, para poder rastrear qué regla produjo cada cambio.
|
|
139
|
+
|
|
140
|
+
**Quién firma el commit:** `PARCHE_GIT_NAME` y `PARCHE_GIT_EMAIL` si están; si no, tu identidad de git (`user.name` y `user.email`, también la que configure tu workflow); en GitHub Actions sin identidad, `github-actions[bot]`, el mismo que abre el PR. Si no hay ninguna, `parche pr` frena antes de tocar el repo.
|
|
141
|
+
|
|
142
|
+
## ARCA en PHP
|
|
143
|
+
|
|
144
|
+
Para `afipsdk/afip.php` y sus forks (`fertandil87/afip.php`, `ivanalemunioz/afip.php`, `eesnaola/afip.php`), Parche busca `$afip->ElectronicBilling->CreateVoucher($data)` y `CreateNextVoucher($data)` (también en minúsculas: PHP no distingue) y agrega una línea por factura:
|
|
145
|
+
|
|
146
|
+
```php
|
|
147
|
+
'DocNro' => 0,
|
|
148
|
+
// parche: 5 = Consumidor Final (DocTipo 99, sin identificar)
|
|
149
|
+
'CondicionIVAReceptorId' => 5,
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
- Lee el array cuando está en la llamada (`array(...)` o `[...]`) o en una variable definida una sola vez, antes de la llamada y en la misma función. Si los datos llegan de otro lado, se reasignan o se completan en varios pasos (`$data['DocTipo'] = ...`), lo reporta como "falta un dato" y no toca nada.
|
|
153
|
+
- Copia el estilo del array: comillas de las claves, indentación (espacios o tabs), alineación de los `=>`, coma final y fin de línea (LF o CRLF).
|
|
154
|
+
- Saltea `vendor/`.
|
|
155
|
+
|
|
156
|
+
**La versión de la librería importa.** Con `afip.js`/`afip.php` 0.x (y `eesnaola/afip.php`) el pedido se arma con un WSDL de 2021 que no tiene `CondicionIVAReceptorId`: aunque el campo esté en el código, no llega a ARCA. Parche lee la versión instalada (`composer.lock`, `package-lock.json`, `node_modules`) y, en esos casos, el arreglo sale como **"revisar"** con el aviso de actualizar a 1.x. También avisa si el repo tiene un `.wsdl` de WSFEv1 sin el campo.
|
|
157
|
+
|
|
158
|
+
**Otros lenguajes:** en Python, C#, Java, Go y VB.NET, Parche detecta integraciones SOAP propias y clientes SOAP generados sin el campo (no los reescribe).
|
|
89
159
|
|
|
90
160
|
## Límites del MVP
|
|
91
161
|
|
|
92
|
-
-
|
|
93
|
-
- ARCA: cubre `@afipsdk/afip.js
|
|
162
|
+
- JS/TS (ESM y CommonJS) y, para ARCA, PHP. Siguen Python y Java.
|
|
163
|
+
- ARCA: cubre `@afipsdk/afip.js` y `afipsdk/afip.php` (con sus forks). Las integraciones SOAP propias (en cualquier lenguaje) se detectan pero no se reescriben.
|
|
94
164
|
- Mercado Pago: migra la creación de pagos con el SDK v2. Las consultas, capturas y reembolsos de pagos existentes se dejan a propósito en Payments API, como indica la guía oficial.
|
|
95
165
|
- Se distribuye como CLI (`npx parche-cli`) + GitHub Action (`Pepo08/parche@v0`). La GitHub App (instalación en un click, PRs automáticos al publicarse un pack nuevo) es el próximo paso.
|
|
96
166
|
- La capa de LLM (redactar packs desde changelogs y resolver casos "falta un dato" leyendo el modelo de datos del cliente) todavía no está incluida; cuando entre, va a pasar por la misma verificación.
|
package/bin/parche.js
CHANGED
|
@@ -9,15 +9,22 @@ import { verify } from '../src/verify.js';
|
|
|
9
9
|
import * as git from '../src/git.js';
|
|
10
10
|
import { parseGithubRemote, openPullRequest } from '../src/github.js';
|
|
11
11
|
import { record } from '../src/ledger.js';
|
|
12
|
+
import { apiLabel } from '../src/inventory/index.js';
|
|
13
|
+
import { isTestFile } from '../src/paths.js';
|
|
12
14
|
|
|
13
15
|
const HELP = `parche — Dependabot para APIs
|
|
14
16
|
|
|
15
17
|
Uso:
|
|
16
18
|
parche packs Lista los cambios de API que Parche conoce
|
|
17
|
-
parche scan <dir> [--json]
|
|
19
|
+
parche scan <dir> [--json] [--upgrades]
|
|
20
|
+
Inventario de APIs + qué te afecta; con --upgrades,
|
|
21
|
+
qué cambia en las versiones nuevas de tus SDKs de npm
|
|
22
|
+
parche diff <paquete> --from <v> --to <v> [--json] [--all]
|
|
23
|
+
Qué cambia en la API de un paquete de npm entre dos versiones
|
|
18
24
|
parche fix <dir> [--pack <id>] Aplica los arreglos en el código
|
|
19
|
-
parche pr <dir> [--pack <id>] [--dry-run] [--verify]
|
|
20
|
-
Rama + commit + PR por cada cambio
|
|
25
|
+
parche pr <dir> [--pack <id>] [--dry-run] [--verify] [--push-remote <remote>]
|
|
26
|
+
Rama + commit + PR por cada cambio. Con --push-remote,
|
|
27
|
+
pushea a ese remote (tu fork) y abre el PR contra origin
|
|
21
28
|
parche feedback <dir> --pack <id> --outcome merged|edited|rejected [--note "..."]
|
|
22
29
|
`;
|
|
23
30
|
|
|
@@ -49,6 +56,65 @@ function ensureLocalDir(root) {
|
|
|
49
56
|
}
|
|
50
57
|
}
|
|
51
58
|
|
|
59
|
+
// Inventario de cualquier API (nivel 1): todo lo que el repo usa, tenga pack o no.
|
|
60
|
+
function printInventory(inventory) {
|
|
61
|
+
const KIND = { sdk: 'SDK ', http: 'HTTP', 'ai-model': 'IA ' };
|
|
62
|
+
console.log(`APIs del repo (${inventory.apis.length}):`);
|
|
63
|
+
if (!inventory.apis.length) console.log(' (no se encontraron APIs de terceros)');
|
|
64
|
+
for (const api of inventory.apis) {
|
|
65
|
+
const ops = api.operations.map((o) => (o.count > 1 ? `${o.op} ×${o.count}` : o.op));
|
|
66
|
+
const shown = ops.length > 4 ? [...ops.slice(0, 4), `+${ops.length - 4} más`] : ops;
|
|
67
|
+
const packs = api.packs.length ? ' · con pack' : '';
|
|
68
|
+
console.log(` ${KIND[api.kind]} ${apiLabel(api).padEnd(38)} ${api.confidence.padEnd(5)} ${shown.join(', ') || '(declarada, sin usos encontrados)'}${packs}`);
|
|
69
|
+
}
|
|
70
|
+
if (inventory.unresolved.length) {
|
|
71
|
+
console.log(` + ${inventory.unresolved.length} llamada(s) HTTP con URL dinámica (no se sabe a qué host van; detalle en el reporte)`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Versiones nuevas de los SDKs de npm (scan --upgrades): qué operaciones del repo cambian o desaparecen.
|
|
76
|
+
function printUpgrades(results) {
|
|
77
|
+
console.log('\nVersiones nuevas de tus SDKs de npm:');
|
|
78
|
+
if (!results.length) console.log(' (no hay SDKs de npm con operaciones para comparar)');
|
|
79
|
+
for (const r of results) {
|
|
80
|
+
const head = ` ${r.package.padEnd(22)} ${r.installed ? `${r.installed} → ${r.latest}`.padEnd(20) : ''.padEnd(20)}`;
|
|
81
|
+
if (r.status === 'afectado') {
|
|
82
|
+
console.log(`${head} ⚠ ${r.affected.length} de tus operaciones cambian o desaparecen`);
|
|
83
|
+
for (const a of r.affected) {
|
|
84
|
+
const where = a.locations.map((l) => `${l.file}:${l.line}`).join(', ');
|
|
85
|
+
console.log(` ${a.op} (${where})`);
|
|
86
|
+
for (const c of a.changes) console.log(` ${c.message}`);
|
|
87
|
+
}
|
|
88
|
+
} else if (r.status === 'sin-impacto') {
|
|
89
|
+
console.log(`${head} ✓ lo que usás no cambia (${r.totalChanges} cambios en el SDK)${r.unverified.length ? ` · ${r.unverified.length} operación(es) sin verificar` : ''}`);
|
|
90
|
+
} else if (r.status === 'al-dia') {
|
|
91
|
+
console.log(`${head} ✓ al día`);
|
|
92
|
+
} else if (r.status === 'sin-verificar') {
|
|
93
|
+
console.log(`${head} ? tus operaciones no aparecen en los tipos de la versión instalada: no se puede verificar`);
|
|
94
|
+
} else {
|
|
95
|
+
console.log(`${head} — ${r.error || 'no se pudo determinar la versión instalada'}`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Salida de `parche diff`.
|
|
101
|
+
function printDiff(d, { all = false } = {}) {
|
|
102
|
+
const groups = [
|
|
103
|
+
['Desaparecen', d.changes.filter((c) => c.type === 'removed')],
|
|
104
|
+
['Cambian los parámetros', d.changes.filter((c) => ['new-required', 'param-removed', 'type-changed'].includes(c.type))],
|
|
105
|
+
['Pasan a deprecados', d.changes.filter((c) => c.type === 'deprecated')],
|
|
106
|
+
];
|
|
107
|
+
console.log(`${d.pkg} ${d.from} → ${d.to} (tipos: ${d.typesFrom} → ${d.typesTo})`);
|
|
108
|
+
if (!d.changes.length) console.log(' No hay cambios incompatibles en la superficie pública.');
|
|
109
|
+
for (const [title, list] of groups) {
|
|
110
|
+
if (!list.length) continue;
|
|
111
|
+
console.log(`\n${title} (${list.length}):`);
|
|
112
|
+
const shown = all ? list : list.slice(0, 25);
|
|
113
|
+
for (const c of shown) console.log(` - ${c.message}`);
|
|
114
|
+
if (shown.length < list.length) console.log(` … y ${list.length - shown.length} más (--all para verlos todos)`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
52
118
|
function printFindings(result) {
|
|
53
119
|
for (const pack of result.packs) {
|
|
54
120
|
const fs2 = result.findings.filter((f) => f.pack === pack.id);
|
|
@@ -71,24 +137,44 @@ async function main() {
|
|
|
71
137
|
return 0;
|
|
72
138
|
}
|
|
73
139
|
|
|
140
|
+
if (cmd === 'diff') {
|
|
141
|
+
const pkg = dirArg;
|
|
142
|
+
if (!pkg || !args.from || !args.to) throw new Error('Uso: parche diff <paquete> --from <versión> --to <versión>');
|
|
143
|
+
// TypeScript se carga recién acá: el scan común no lo necesita.
|
|
144
|
+
const { createRegistry } = await import('../src/sdk-diff/registry.js');
|
|
145
|
+
const { diffVersions } = await import('../src/sdk-diff/upgrades.js');
|
|
146
|
+
const d = await diffVersions(createRegistry(), pkg, String(args.from), String(args.to));
|
|
147
|
+
if (d.error) throw new Error(d.error);
|
|
148
|
+
if (args.json) { console.log(JSON.stringify({ pkg, from: d.from, to: d.to, changes: d.changes }, null, 2)); return 0; }
|
|
149
|
+
printDiff(d, { all: !!args.all });
|
|
150
|
+
return 0;
|
|
151
|
+
}
|
|
152
|
+
|
|
74
153
|
const root = path.resolve(dirArg || '.');
|
|
75
154
|
if (!fs.existsSync(root)) throw new Error(`No existe ${root}`);
|
|
76
155
|
const packIds = args.pack ? [args.pack] : null;
|
|
77
156
|
|
|
78
157
|
if (cmd === 'scan') {
|
|
79
158
|
const result = run(root, { write: false, packIds });
|
|
159
|
+
let upgrades = null;
|
|
160
|
+
if (args.upgrades) {
|
|
161
|
+
if (!args.json) console.error('Buscando versiones nuevas de tus SDKs en npm...');
|
|
162
|
+
const { createRegistry } = await import('../src/sdk-diff/registry.js');
|
|
163
|
+
const { checkUpgrades } = await import('../src/sdk-diff/upgrades.js');
|
|
164
|
+
upgrades = await checkUpgrades(result.inventory, root, createRegistry());
|
|
165
|
+
result.inventory.upgrades = upgrades;
|
|
166
|
+
}
|
|
80
167
|
ensureLocalDir(root);
|
|
81
168
|
fs.writeFileSync(path.join(root, '.parche', 'inventory.json'), JSON.stringify(result.inventory, null, 2));
|
|
82
|
-
fs.writeFileSync(path.join(root, '.parche', 'report.md'), scanReport({ ...result, changed: [] }));
|
|
169
|
+
fs.writeFileSync(path.join(root, '.parche', 'report.md'), scanReport({ ...result, changed: [], upgrades }));
|
|
83
170
|
record(root, { type: 'scan', findings: counts(result.findings) });
|
|
84
171
|
if (args.json) { console.log(JSON.stringify({ inventory: result.inventory, findings: result.findings }, null, 2)); return 0; }
|
|
85
172
|
console.log(`Parche · scan de ${path.basename(root)}\n`);
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
console.log(` ${prov.padEnd(12)} ${String(p.callSites).padStart(2)} usos en ${p.files.length} archivo(s) [${Object.keys(p.ops).join(', ')}]`);
|
|
89
|
-
}
|
|
173
|
+
printInventory(result.inventory);
|
|
174
|
+
if (upgrades) printUpgrades(upgrades);
|
|
90
175
|
printFindings(result);
|
|
91
|
-
|
|
176
|
+
const s = summary(result.findings);
|
|
177
|
+
console.log(s ? `\n${s}` : '\nNinguno de los packs verificados tiene cambios que te afecten hoy.');
|
|
92
178
|
console.log(`Reporte: ${path.join(root, '.parche', 'report.md')}`);
|
|
93
179
|
return 0;
|
|
94
180
|
}
|
|
@@ -107,49 +193,65 @@ async function main() {
|
|
|
107
193
|
if (cmd === 'pr') {
|
|
108
194
|
if (!git.isRepo(root)) throw new Error('`parche pr` necesita un repositorio git.');
|
|
109
195
|
if (!git.isClean(root)) throw new Error('Hay cambios sin commitear. Commiteá o guardá (stash) antes de correr `parche pr`.');
|
|
196
|
+
// Quién firma el commit se resuelve antes de escribir nada: si falla al commitear, el repo quedaría sucio.
|
|
197
|
+
git.commitIdentity(root);
|
|
110
198
|
ensureLocalDir(root);
|
|
111
199
|
const base = git.currentBranch(root);
|
|
112
200
|
const packs = loadPacks().filter((p) => p.codemod && (!packIds || packIds.includes(p.id)));
|
|
201
|
+
// A dónde se pushea: origin, o el fork de quien abre el PR (--push-remote fork). El PR se abre contra origin.
|
|
202
|
+
const pushRemote = typeof args['push-remote'] === 'string' ? args['push-remote'] : 'origin';
|
|
113
203
|
for (const pack of packs) {
|
|
114
204
|
const preview = run(root, { write: false, packIds: [pack.id] });
|
|
115
205
|
if (!preview.findings.length) continue;
|
|
116
206
|
const branch = `parche/${pack.id}`;
|
|
117
|
-
git.checkoutNewBranch(root, branch, base);
|
|
118
|
-
const result = run(root, { write: true, packIds: [pack.id] });
|
|
119
|
-
const changedFiles = result.changed.map((c) => c.file);
|
|
120
|
-
const verification = args.verify || args['dry-run'] ? verify(root, changedFiles, { runTests: !!args.verify }) : null;
|
|
121
|
-
if (verification && !verification.ok) {
|
|
122
|
-
git.checkout(root, base);
|
|
123
|
-
throw new Error(`La verificación falló para ${pack.id}; no se abre el PR.\n${JSON.stringify(verification, null, 2)}`);
|
|
124
|
-
}
|
|
125
|
-
const body = prBody({ pack, findings: result.findings, changed: result.changed, verification });
|
|
126
|
-
const bodyPath = path.join(root, '.parche', `pr-${pack.id}.md`);
|
|
127
|
-
fs.writeFileSync(bodyPath, body);
|
|
128
|
-
const c = counts(result.findings);
|
|
129
|
-
const draft = !!(c['needs-review'] || c['needs-input'] || c.blocked);
|
|
130
207
|
const title = `Parche: ${pack.title}`;
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
208
|
+
git.checkoutNewBranch(root, branch, base);
|
|
209
|
+
let keepBranch = false;
|
|
210
|
+
try {
|
|
211
|
+
const result = run(root, { write: true, packIds: [pack.id] });
|
|
212
|
+
const changedFiles = result.changed.map((c) => c.file);
|
|
213
|
+
const c = counts(result.findings);
|
|
214
|
+
const bodyPath = path.join(root, '.parche', `pr-${pack.id}.md`);
|
|
215
|
+
console.log(`\n■ ${title}`);
|
|
216
|
+
// La sintaxis se chequea siempre (no ejecuta nada del repo); los tests, solo con --verify.
|
|
217
|
+
const verification = changedFiles.length ? verify(root, changedFiles, { runTests: !!args.verify }) : null;
|
|
218
|
+
if (verification && !verification.ok) {
|
|
219
|
+
// Nada de lo que escribió Parche queda en el repo: se descarta y se borra la rama.
|
|
220
|
+
git.discardChanges(root);
|
|
221
|
+
throw new Error(`La verificación falló para ${pack.id}; no se abre el PR y el repo queda como estaba.\n${JSON.stringify(verification, null, 2)}`);
|
|
222
|
+
}
|
|
223
|
+
fs.writeFileSync(bodyPath, prBody({ pack, findings: result.findings, changed: result.changed, verification }));
|
|
224
|
+
if (!changedFiles.length || changedFiles.every(isTestFile)) {
|
|
225
|
+
const why = changedFiles.length ? 'solo cambiaría tests o mocks: no se abre un PR' : 'sin cambios de código';
|
|
226
|
+
git.discardChanges(root);
|
|
227
|
+
console.log(` ${summary(result.findings)}`);
|
|
228
|
+
console.log(` ${why[0].toUpperCase()}${why.slice(1)}; queda como issue o reporte: ${bodyPath}`);
|
|
229
|
+
record(root, { type: 'report-only', pack: pack.id, findings: c, reason: why });
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
const draft = !!(c['needs-review'] || c['needs-input'] || c.blocked);
|
|
233
|
+
const sha = git.commitAll(root, `${title}\n\n${pack.summary_es}\nPack ${pack.id} v${pack.version}.`);
|
|
234
|
+
keepBranch = true;
|
|
235
|
+
console.log(` Rama ${branch} · commit ${sha} · ${changedFiles.length} archivo(s)${verification ? ` · verificación ${verification.ok ? 'OK' : 'FALLÓ'}` : ''}`);
|
|
236
|
+
console.log(` ${summary(result.findings)}`);
|
|
237
|
+
console.log(` Cuerpo del PR: ${bodyPath}`);
|
|
238
|
+
const target = parseGithubRemote(git.originUrl(root));
|
|
239
|
+
const pushTo = parseGithubRemote(git.remoteUrl(root, pushRemote));
|
|
240
|
+
let url = null;
|
|
241
|
+
if (!args['dry-run'] && process.env.GITHUB_TOKEN && target && pushTo) {
|
|
242
|
+
git.push(root, branch, pushRemote);
|
|
243
|
+
const head = pushTo.owner !== target.owner ? `${pushTo.owner}:${branch}` : branch;
|
|
244
|
+
url = await openPullRequest({ token: process.env.GITHUB_TOKEN, ...target, head, base, title, body: fs.readFileSync(bodyPath, 'utf8'), draft });
|
|
245
|
+
console.log(` PR abierto${draft ? ' (borrador)' : ''}: ${url}`);
|
|
246
|
+
} else {
|
|
247
|
+
console.log(` Modo prueba: no se abrió el PR${draft ? ' (iría como borrador por tener ítems para revisar)' : ''}.`);
|
|
248
|
+
}
|
|
249
|
+
record(root, { type: 'pr', pack: pack.id, packVersion: pack.version, branch, sha, url, draft, files: changedFiles, findings: c });
|
|
250
|
+
} finally {
|
|
251
|
+
// Pase lo que pase, se vuelve a la rama base; la rama de Parche solo queda si tiene un commit.
|
|
134
252
|
git.checkout(root, base);
|
|
135
|
-
|
|
136
|
-
continue;
|
|
137
|
-
}
|
|
138
|
-
const sha = git.commitAll(root, `${title}\n\n${pack.summary_es}\nPack ${pack.id} v${pack.version}.`);
|
|
139
|
-
console.log(` Rama ${branch} · commit ${sha} · ${changedFiles.length} archivo(s)${verification ? ` · verificación ${verification.ok ? 'OK' : 'FALLÓ'}` : ''}`);
|
|
140
|
-
console.log(` ${summary(result.findings)}`);
|
|
141
|
-
console.log(` Cuerpo del PR: ${bodyPath}`);
|
|
142
|
-
const remote = parseGithubRemote(git.originUrl(root));
|
|
143
|
-
let url = null;
|
|
144
|
-
if (!args['dry-run'] && process.env.GITHUB_TOKEN && remote) {
|
|
145
|
-
git.push(root, branch);
|
|
146
|
-
url = await openPullRequest({ token: process.env.GITHUB_TOKEN, ...remote, head: branch, base, title, body, draft });
|
|
147
|
-
console.log(` PR abierto${draft ? ' (borrador)' : ''}: ${url}`);
|
|
148
|
-
} else {
|
|
149
|
-
console.log(` Modo prueba: no se abrió el PR${draft ? ' (iría como borrador por tener ítems para revisar)' : ''}.`);
|
|
253
|
+
if (!keepBranch) git.deleteBranch(root, branch);
|
|
150
254
|
}
|
|
151
|
-
record(root, { type: 'pr', pack: pack.id, packVersion: pack.version, branch, sha, url, draft, files: changedFiles, findings: c });
|
|
152
|
-
git.checkout(root, base);
|
|
153
255
|
}
|
|
154
256
|
return 0;
|
|
155
257
|
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"about": "Lista curada de SDKs de APIs de terceros (paquete → proveedor y rubro), de clientes HTTP y de hosts de APIs conocidas. La usa el inventario de `parche scan`. Agregar entradas es la forma de subir la confianza de una detección de 'baja' a 'alta'.",
|
|
4
|
+
"npm": {
|
|
5
|
+
"stripe": { "provider": "Stripe", "category": "pagos" },
|
|
6
|
+
"@stripe/stripe-js": { "provider": "Stripe", "category": "pagos" },
|
|
7
|
+
"mercadopago": { "provider": "Mercado Pago", "category": "pagos" },
|
|
8
|
+
"@paypal/checkout-server-sdk": { "provider": "PayPal", "category": "pagos" },
|
|
9
|
+
"@paypal/paypal-server-sdk": { "provider": "PayPal", "category": "pagos" },
|
|
10
|
+
"braintree": { "provider": "Braintree", "category": "pagos" },
|
|
11
|
+
"plaid": { "provider": "Plaid", "category": "pagos" },
|
|
12
|
+
"@adyen/api-library": { "provider": "Adyen", "category": "pagos" },
|
|
13
|
+
"@afipsdk/afip.js": { "provider": "ARCA (ex AFIP)", "category": "facturación" },
|
|
14
|
+
"@arcasdk/core": { "provider": "ARCA (ex AFIP)", "category": "facturación" },
|
|
15
|
+
"afip.ts": { "provider": "ARCA (ex AFIP)", "category": "facturación" },
|
|
16
|
+
"ts-afip-ws": { "provider": "ARCA (ex AFIP)", "category": "facturación" },
|
|
17
|
+
"facturajs": { "provider": "ARCA (ex AFIP)", "category": "facturación" },
|
|
18
|
+
"openai": { "provider": "OpenAI", "category": "ia" },
|
|
19
|
+
"@anthropic-ai/sdk": { "provider": "Anthropic", "category": "ia" },
|
|
20
|
+
"@google/generative-ai": { "provider": "Google (Gemini)", "category": "ia" },
|
|
21
|
+
"@google/genai": { "provider": "Google (Gemini)", "category": "ia" },
|
|
22
|
+
"@mistralai/mistralai": { "provider": "Mistral", "category": "ia" },
|
|
23
|
+
"cohere-ai": { "provider": "Cohere", "category": "ia" },
|
|
24
|
+
"groq-sdk": { "provider": "Groq", "category": "ia" },
|
|
25
|
+
"replicate": { "provider": "Replicate", "category": "ia" },
|
|
26
|
+
"@huggingface/inference": { "provider": "Hugging Face", "category": "ia" },
|
|
27
|
+
"twilio": { "provider": "Twilio", "category": "mensajería" },
|
|
28
|
+
"@slack/web-api": { "provider": "Slack", "category": "mensajería" },
|
|
29
|
+
"@slack/bolt": { "provider": "Slack", "category": "mensajería" },
|
|
30
|
+
"discord.js": { "provider": "Discord", "category": "mensajería" },
|
|
31
|
+
"telegraf": { "provider": "Telegram", "category": "mensajería" },
|
|
32
|
+
"node-telegram-bot-api": { "provider": "Telegram", "category": "mensajería" },
|
|
33
|
+
"@sendgrid/mail": { "provider": "SendGrid", "category": "email" },
|
|
34
|
+
"resend": { "provider": "Resend", "category": "email" },
|
|
35
|
+
"postmark": { "provider": "Postmark", "category": "email" },
|
|
36
|
+
"mailgun.js": { "provider": "Mailgun", "category": "email" },
|
|
37
|
+
"@mailchimp/mailchimp_marketing": { "provider": "Mailchimp", "category": "email" },
|
|
38
|
+
"aws-sdk": { "provider": "AWS", "category": "nube" },
|
|
39
|
+
"firebase-admin": { "provider": "Firebase (Google)", "category": "nube" },
|
|
40
|
+
"firebase": { "provider": "Firebase (Google)", "category": "nube" },
|
|
41
|
+
"googleapis": { "provider": "Google APIs", "category": "nube" },
|
|
42
|
+
"@supabase/supabase-js": { "provider": "Supabase", "category": "datos" },
|
|
43
|
+
"algoliasearch": { "provider": "Algolia", "category": "datos" },
|
|
44
|
+
"@notionhq/client": { "provider": "Notion", "category": "productividad" },
|
|
45
|
+
"airtable": { "provider": "Airtable", "category": "productividad" },
|
|
46
|
+
"@octokit/rest": { "provider": "GitHub", "category": "devtools" },
|
|
47
|
+
"@octokit/core": { "provider": "GitHub", "category": "devtools" },
|
|
48
|
+
"@sentry/node": { "provider": "Sentry", "category": "observabilidad" },
|
|
49
|
+
"@shopify/shopify-api": { "provider": "Shopify", "category": "ecommerce" },
|
|
50
|
+
"@woocommerce/woocommerce-rest-api": { "provider": "WooCommerce", "category": "ecommerce" },
|
|
51
|
+
"@hubspot/api-client": { "provider": "HubSpot", "category": "crm" },
|
|
52
|
+
"contentful": { "provider": "Contentful", "category": "contenido" },
|
|
53
|
+
"@googlemaps/google-maps-services-js": { "provider": "Google Maps", "category": "mapas" },
|
|
54
|
+
"auth0": { "provider": "Auth0", "category": "auth" },
|
|
55
|
+
"@clerk/clerk-sdk-node": { "provider": "Clerk", "category": "auth" }
|
|
56
|
+
},
|
|
57
|
+
"npmPrefixes": {
|
|
58
|
+
"@aws-sdk/": { "provider": "AWS", "category": "nube" },
|
|
59
|
+
"@google-cloud/": { "provider": "Google Cloud", "category": "nube" },
|
|
60
|
+
"@azure/": { "provider": "Azure", "category": "nube" }
|
|
61
|
+
},
|
|
62
|
+
"composer": {
|
|
63
|
+
"afipsdk/afip.php": { "provider": "ARCA (ex AFIP)", "category": "facturación" },
|
|
64
|
+
"fertandil87/afip.php": { "provider": "ARCA (ex AFIP)", "category": "facturación" },
|
|
65
|
+
"ivanalemunioz/afip.php": { "provider": "ARCA (ex AFIP)", "category": "facturación" },
|
|
66
|
+
"eesnaola/afip.php": { "provider": "ARCA (ex AFIP)", "category": "facturación" },
|
|
67
|
+
"multinexo/php-afip-ws": { "provider": "ARCA (ex AFIP)", "category": "facturación" },
|
|
68
|
+
"mercadopago/dx-php": { "provider": "Mercado Pago", "category": "pagos" },
|
|
69
|
+
"stripe/stripe-php": { "provider": "Stripe", "category": "pagos" },
|
|
70
|
+
"twilio/sdk": { "provider": "Twilio", "category": "mensajería" },
|
|
71
|
+
"aws/aws-sdk-php": { "provider": "AWS", "category": "nube" },
|
|
72
|
+
"openai-php/client": { "provider": "OpenAI", "category": "ia" },
|
|
73
|
+
"sendgrid/sendgrid": { "provider": "SendGrid", "category": "email" },
|
|
74
|
+
"google/apiclient": { "provider": "Google APIs", "category": "nube" }
|
|
75
|
+
},
|
|
76
|
+
"pypi": {
|
|
77
|
+
"stripe": { "provider": "Stripe", "category": "pagos" },
|
|
78
|
+
"mercadopago": { "provider": "Mercado Pago", "category": "pagos" },
|
|
79
|
+
"openai": { "provider": "OpenAI", "category": "ia" },
|
|
80
|
+
"anthropic": { "provider": "Anthropic", "category": "ia" },
|
|
81
|
+
"google-generativeai": { "provider": "Google (Gemini)", "category": "ia" },
|
|
82
|
+
"twilio": { "provider": "Twilio", "category": "mensajería" },
|
|
83
|
+
"boto3": { "provider": "AWS", "category": "nube" },
|
|
84
|
+
"sendgrid": { "provider": "SendGrid", "category": "email" },
|
|
85
|
+
"slack-sdk": { "provider": "Slack", "category": "mensajería" },
|
|
86
|
+
"pyafipws": { "provider": "ARCA (ex AFIP)", "category": "facturación" }
|
|
87
|
+
},
|
|
88
|
+
"httpClients": {
|
|
89
|
+
"npm": ["axios", "got", "undici", "node-fetch", "ky", "superagent", "cross-fetch", "isomorphic-fetch"],
|
|
90
|
+
"composer": ["guzzlehttp/guzzle", "symfony/http-client"],
|
|
91
|
+
"pypi": ["requests", "httpx", "aiohttp", "urllib3"]
|
|
92
|
+
},
|
|
93
|
+
"notApis": {
|
|
94
|
+
"prefixes": ["@types/", "eslint", "@eslint/", "prettier", "jest", "@jest/", "vitest", "mocha", "typescript", "ts-node", "@babel/", "babel-", "webpack", "vite", "rollup", "esbuild", "@vitejs/", "nodemon", "husky", "lint-staged", "@testing-library/", "@apollo/", "graphql", "react", "vue", "svelte", "next", "nuxt", "express", "fastify", "koa", "@nestjs/", "@angular/", "socket.io", "@prisma/", "prisma", "@trpc/", "@tanstack/", "@redis/", "redis", "ioredis", "ws", "pg", "mysql", "mongodb", "mongoose", "sequelize", "typeorm", "knex", "drizzle-orm", "@aws-sdk/types", "@smithy/"],
|
|
95
|
+
"why": "Librerías de desarrollo, frameworks, bases de datos y transporte (socket.io, ws): no son la API de un tercero aunque el nombre diga 'client'."
|
|
96
|
+
},
|
|
97
|
+
"hosts": {
|
|
98
|
+
"api.stripe.com": { "provider": "Stripe", "category": "pagos" },
|
|
99
|
+
"api.mercadopago.com": { "provider": "Mercado Pago", "category": "pagos" },
|
|
100
|
+
"api.mercadolibre.com": { "provider": "Mercado Libre", "category": "ecommerce" },
|
|
101
|
+
"api.paypal.com": { "provider": "PayPal", "category": "pagos" },
|
|
102
|
+
"api-m.paypal.com": { "provider": "PayPal", "category": "pagos" },
|
|
103
|
+
"afip.gov.ar": { "provider": "ARCA (ex AFIP)", "category": "facturación" },
|
|
104
|
+
"arca.gob.ar": { "provider": "ARCA (ex AFIP)", "category": "facturación" },
|
|
105
|
+
"api.openai.com": { "provider": "OpenAI", "category": "ia" },
|
|
106
|
+
"api.anthropic.com": { "provider": "Anthropic", "category": "ia" },
|
|
107
|
+
"generativelanguage.googleapis.com": { "provider": "Google (Gemini)", "category": "ia" },
|
|
108
|
+
"api.mistral.ai": { "provider": "Mistral", "category": "ia" },
|
|
109
|
+
"api.twilio.com": { "provider": "Twilio", "category": "mensajería" },
|
|
110
|
+
"api.telegram.org": { "provider": "Telegram", "category": "mensajería" },
|
|
111
|
+
"graph.facebook.com": { "provider": "Meta (WhatsApp/Facebook)", "category": "mensajería" },
|
|
112
|
+
"slack.com": { "provider": "Slack", "category": "mensajería" },
|
|
113
|
+
"hooks.slack.com": { "provider": "Slack", "category": "mensajería" },
|
|
114
|
+
"discord.com": { "provider": "Discord", "category": "mensajería" },
|
|
115
|
+
"api.sendgrid.com": { "provider": "SendGrid", "category": "email" },
|
|
116
|
+
"api.resend.com": { "provider": "Resend", "category": "email" },
|
|
117
|
+
"api.github.com": { "provider": "GitHub", "category": "devtools" },
|
|
118
|
+
"googleapis.com": { "provider": "Google APIs", "category": "nube" },
|
|
119
|
+
"amazonaws.com": { "provider": "AWS", "category": "nube" },
|
|
120
|
+
"api.notion.com": { "provider": "Notion", "category": "productividad" },
|
|
121
|
+
"api.airtable.com": { "provider": "Airtable", "category": "productividad" },
|
|
122
|
+
"myshopify.com": { "provider": "Shopify", "category": "ecommerce" },
|
|
123
|
+
"api.hubapi.com": { "provider": "HubSpot", "category": "crm" }
|
|
124
|
+
}
|
|
125
|
+
}
|