parche-cli 0.7.3 → 0.7.5
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 +20 -1
- package/README.md +4 -3
- package/bin/parche.js +3 -0
- package/package.json +1 -1
- package/packs/arca.wsfe.condicion-iva-receptor.json +2 -0
- package/packs/mercadopago.payments-to-orders.json +6 -3
- package/src/codemods/mercadopago-payments-to-orders.js +45 -29
- package/src/git.js +20 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,25 @@
|
|
|
1
1
|
# Cambios
|
|
2
2
|
|
|
3
|
-
Los cambios de cada versión de `parche-cli` y de la Action `Pepo08/parche@v0`. En npm están la 0.1.1, la 0.5.1, de la 0.6.0 a la 0.7.0 y de la 0.7.
|
|
3
|
+
Los cambios de cada versión de `parche-cli` y de la Action `Pepo08/parche@v0`. En npm están la 0.1.1, la 0.5.1, de la 0.6.0 a la 0.7.0, la 0.7.2, la 0.7.3 y de la 0.7.5 en adelante. La 0.1.0 y de la 0.2.0 a la 0.5.0 no se publicaron en npm; lo que traían está incluido en la 0.5.1. La 0.7.1 y la 0.7.4 tampoco: salieron junto con la 0.7.2 y la 0.7.5. La fecha de cada versión es la del cambio; la de publicación figura en npm y en los [Releases](https://github.com/Pepo08/parche/releases).
|
|
4
|
+
|
|
5
|
+
## 0.7.5 — 2026-10-24
|
|
6
|
+
|
|
7
|
+
Mercado Pago: dos cosas del SDK oficial que el pack no tenía en cuenta, verificadas en su código (github.com/mercadopago/sdk-nodejs, 24/10/2026).
|
|
8
|
+
|
|
9
|
+
### Cambios
|
|
10
|
+
- **Ya no se agrega `idempotencyKey` al migrar a `Order.create`.** El SDK manda una `X-Idempotency-Key` nueva en cada escritura cuando no recibe una, así que agregarla no aportaba nada. Y por un bug abierto del SDK (mercadopago/sdk-nodejs#481), las `requestOptions` de `create` quedan guardadas en la configuración del `client`: la clave que agregaba Parche salía también en los reembolsos y las capturas siguientes de ese `client`. Si el código ya traía su propia clave, se deja como está y el PR avisa del #481. Tampoco se agrega más el import de `randomUUID`.
|
|
11
|
+
- **Versión del SDK:** `Order` existe en `mercadopago` desde la 2.4.0 (1/4/2025). Con una versión anterior instalada (según el lockfile o `node_modules`), la migración queda **bloqueada** y pide actualizar el SDK, porque migrada dejaría `Order` en undefined. Si solo se conoce el rango de `package.json` y permite una versión anterior, se migra para revisar y pide fijar `^2.4.0`. Si no se ve ninguna versión, pide confirmarla.
|
|
12
|
+
- **Pack `mercadopago.payments-to-orders` 2026.10.24:** el checklist ya no pide mandar `X-Idempotency-Key` a mano con el SDK (sí por HTTP directo) y pide `mercadopago` 2.4.0 o posterior. Suma como fuentes el release 2.4.0 del SDK y el issue #481.
|
|
13
|
+
|
|
14
|
+
## 0.7.4 — 2026-10-24
|
|
15
|
+
|
|
16
|
+
Un repo con `.parche/` commiteado, y los códigos de error de ARCA según su manual oficial.
|
|
17
|
+
|
|
18
|
+
### Cambios
|
|
19
|
+
- **`.parche/` commiteado:** si alguien commiteó `.parche/` (pasó copiando una carpeta ya escaneada a un repo nuevo), `scan` lo modificaba y después `pr` frenaba con "Hay cambios sin commitear"; y si hubiera seguido, `git add -A` metía `.parche/` en el PR. Ahora `pr` no cuenta `.parche/` al ver si el repo está limpio, nunca lo incluye en el commit, y avisa con una línea cómo dejar de versionarlo. Si la verificación falla, se descarta el arreglo pero no el registro de esa corrida.
|
|
20
|
+
- **Pack `arca.wsfe.condicion-iva-receptor`:** cita como fuentes los dos manuales para el desarrollador de WSFEv1 de ARCA: el de producción (revisión del 1/9/2026), cuyo anexo confirma la tabla de códigos válidos por clase de comprobante que ya usaba el pack, y el de homologación (v4.8, revisión del 1/12/2026), que fija el 01/12/2026 como la fecha en que el campo pasa a ser obligatorio. Hasta ahora esa fecha salía solo de fuentes de terceros.
|
|
21
|
+
- **Guía de `CondicionIVAReceptorId`:** los códigos son los de los manuales. Cuando falta el campo, el código es la observación 10245 (que queda en desuso el 1/12) y, cuando pasa a ser obligatorio, el error 10246; el 10242 es un código que no existe y el 10243, uno que no corresponde a la clase del comprobante, y los dos ya rechazan hoy. Antes decía que el campo faltante era el 10242. Con afip.js 0.x y `ts-afip-ws` dice "tratalo como si no viajara", porque no está confirmado que no viaje (PR #26). El README dice lo mismo.
|
|
22
|
+
- README e inventario: `.parche/` es salida local y no va en git.
|
|
4
23
|
|
|
5
24
|
## 0.7.3 — 2026-10-24
|
|
6
25
|
|
package/README.md
CHANGED
|
@@ -82,6 +82,7 @@ npx parche-cli feedback . --pack <id> --outcome merged|edited|rejected
|
|
|
82
82
|
|
|
83
83
|
- **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`.
|
|
84
84
|
- **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/`.
|
|
85
|
+
- **`.parche/` no va en git:** es lo que Parche escribe en cada corrida (el reporte, el inventario, el registro y los cuerpos de los PRs), y lo agrega a `.git/info/exclude`. Si ya lo commiteaste (por ejemplo, copiando una carpeta escaneada a un repo nuevo), Parche funciona igual: no lo cuenta como un cambio sin commitear, nunca lo incluye en sus PRs y te avisa. Para dejar de versionarlo: `git rm -r --cached .parche` y sumá `.parche/` al `.gitignore`.
|
|
85
86
|
|
|
86
87
|
### Qué cambia en las versiones nuevas de tus SDKs
|
|
87
88
|
|
|
@@ -155,7 +156,7 @@ Cómo contribuir, las reglas para el código y cómo se saca una versión: [`CON
|
|
|
155
156
|
| Pack | Proveedor | Tipo | Fecha límite | Arreglo |
|
|
156
157
|
|---|---|---|---|---|
|
|
157
158
|
| `arca.wsfe.condicion-iva-receptor` | ARCA | rompe | 2026-12-01 | sí, en JS (`afip.js` y `@arcasdk/core`) y PHP (`afipsdk/afip.php` y forks): consumidor final y DNI B/C; con `DocTipo` en una variable, consumidor final al ejecutarse (PR parcial) |
|
|
158
|
-
| `mercadopago.payments-to-orders` | Mercado Pago | migración | — | sí (creación de pagos
|
|
159
|
+
| `mercadopago.payments-to-orders` | Mercado Pago | migración | — | sí (creación de pagos con el SDK de Node, 2.4.0 o posterior) |
|
|
159
160
|
| `ai.model-retirements` | Anthropic, OpenAI | retiro | varias | solo detección (también modelos fine-tuned de OpenAI, `ft:…`). De OpenAI, los modelos que su página de deprecaciones apaga desde 2026: texto, código, imagen, video, voz, transcripción y realtime; y los productos que cierran: Assistants API, prompts reutilizables (`pmpt_…`, `v1/prompts`), la plataforma de Evals, ChatKit con un workflow de Agent Builder y la Videos API (cerrada el 24/9/2026). De Anthropic, los modelos retirados o con fecha, también por su alias (`claude-sonnet-4-5`, que apunta a `claude-sonnet-4-5-20250929` y se retira el 30/11/2026), `temperature`, `top_p` y `top_k` con un valor que no es el de por defecto en Claude 4.7 y posteriores, que dan error 400 (SDK de JS/TS y de Python, y HTTP directo), y los mismos tres parámetros con cualquier modelo en la API version `2026-10-21`, que usan el SDK de TypeScript 1.0.0 y el de Python 2.0.0 (del 21/10/2026): dice si el pedido ya falla, si falla en una instalación nueva o si va a fallar cuando suban el SDK |
|
|
160
161
|
|
|
161
162
|
## Verificación
|
|
@@ -197,7 +198,7 @@ Para `afipsdk/afip.php` y sus forks (`fertandil87/afip.php`, `ivanalemunioz/afip
|
|
|
197
198
|
- 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).
|
|
198
199
|
- Saltea `vendor/`.
|
|
199
200
|
|
|
200
|
-
**La versión de la librería importa.** Con `afip.
|
|
201
|
+
**La versión de la librería importa.** Con `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. Con `afip.js` 0.x y `ts-afip-ws` (un fork de afip.js 0.x; su última versión, la 1.3.2, trae el WSDL sin el campo), el WSDL que traen tampoco lo tiene; no está confirmado que el campo no viaje, así que conviene tratarlo como si no viajara. Parche lee la versión instalada (`composer.lock`, `package-lock.json`, `node_modules`) y, en esos casos, el arreglo sale como **"revisar"** y lo que pide un dato lleva el mismo aviso: qué actualizar para que el campo viaje. También avisa si el repo tiene un `.wsdl` de WSFEv1 sin el campo.
|
|
201
202
|
|
|
202
203
|
**Clave casi bien escrita.** Si el detalle del comprobante lleva `CondicionIVAReceptor` (sin `Id`), `CondicionIvaReceptorId` o `condicionIVAReceptorId`, ese elemento no existe en el WSDL: el dato nunca llega y el repo parece migrado. Parche renombra la clave a `CondicionIVAReceptorId` sin tocar el valor, en JS y en PHP, y el PR sale **borrador** para revisar que el valor sea un código válido. Solo lo hace dentro de objetos o arrays con al menos dos claves de WSFEv1 (`DocTipo`, `CbteDesde`, `ImpTotal`…), para no tocar modelos propios con un nombre parecido, y nunca en tests. También renombra una asignación aparte (`det.CondicionIVAReceptor = x`, `det['CondicionIVAReceptor'] = x` o `$det['CondicionIVAReceptor'] = $x`) cuando `det` es, en la misma función, un objeto o array con esas claves; si no puede confirmarlo, lo avisa sin tocar. Si un `interface` o un `type` del repo declara la clave mal escrita, no renombra, porque TypeScript dejaría de compilar, y el aviso dice dónde está ese tipo.
|
|
203
204
|
|
|
@@ -235,6 +236,6 @@ Cuando `DocTipo` no es un número fijo (`DocTipo: docTipo`), Parche no puede sab
|
|
|
235
236
|
- IA, parámetros de Claude 4.7 y posteriores: avisa solo si el modelo del pedido se lee en el mismo archivo (un literal, una constante asignada una vez o el valor por defecto de una variable de entorno) y el parámetro es un número escrito en el código. Si el modelo llega de otro archivo o de la configuración, no avisa: no se puede saber si el pedido va a fallar.
|
|
236
237
|
- IA, API version `2026-10-21` de Anthropic: la versión del SDK sale de `package-lock.json`, `node_modules` o el rango de `package.json`, y en Python solo de `requirements.txt` (todavía no de `pyproject.toml`, `poetry.lock` ni `uv.lock`). Un rango sin número (`latest`, `*`) cuenta como versión desconocida. Por HTTP directo avisa solo si el archivo manda `anthropic-version: 2026-10-21`. Los SDKs de Go, Java, .NET, Ruby y PHP no se leen.
|
|
237
238
|
- IA, alias de Anthropic: avisa los de Claude Sonnet 4.5, Opus 4.5 y Haiku 4.5 (`claude-sonnet-4-5`, `claude-opus-4-5`, `claude-haiku-4-5`), escritos enteros entre comillas. Los alias de modelos ya retirados (`claude-3-5-sonnet-latest`, `claude-3-7-sonnet-latest` y otros) todavía no: la documentación actual de Anthropic ya no los nombra.
|
|
238
|
-
- Mercado Pago: migra la creación de pagos con el SDK
|
|
239
|
+
- Mercado Pago: migra la creación de pagos con el SDK de Node 2.4.0 o posterior, la primera versión con `Order` (con una anterior instalada, no migra y pide actualizar). No agrega una `idempotencyKey`: el SDK genera una en cada escritura, y una fija quedaría en el client por un bug abierto del SDK (mercadopago/sdk-nodejs#481). Las consultas, capturas y reembolsos de pagos existentes se dejan a propósito en Payments API, como indica la guía oficial.
|
|
239
240
|
- 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.
|
|
240
241
|
- 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
|
@@ -215,6 +215,9 @@ async function main() {
|
|
|
215
215
|
if (cmd === 'pr') {
|
|
216
216
|
if (!git.isRepo(root)) throw new Error('`parche pr` necesita un repositorio git.');
|
|
217
217
|
if (!git.isClean(root)) throw new Error('Hay cambios sin commitear. Commiteá o guardá (stash) antes de correr `parche pr`.');
|
|
218
|
+
if (git.tracksParcheDir(root)) {
|
|
219
|
+
console.warn('Ojo: `.parche/` está commiteado en el repo. Parche lo usa para sus reportes y no lo incluye en sus PRs. Si no lo querés versionar: `git rm -r --cached .parche` y sumalo al .gitignore.');
|
|
220
|
+
}
|
|
218
221
|
// Quién firma el commit se resuelve antes de escribir nada: si falla al commitear, el repo quedaría sucio.
|
|
219
222
|
const env = args.author !== undefined ? { ...process.env, ...git.parseAuthor(args.author) } : process.env;
|
|
220
223
|
const identity = git.commitIdentity(root, env);
|
package/package.json
CHANGED
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
"summary_es": "Desde el 1/12/2026 ARCA rechaza automáticamente las solicitudes de CAE que no informen CondicionIVAReceptorId.",
|
|
11
11
|
"why_es": "La RG 5616/2024 hizo obligatorio informar la condición frente al IVA del receptor en cada comprobante electrónico. El manual del desarrollador de WSFEv1 fija el 1 de diciembre de 2026 como fecha desde la cual el campo es obligatorio: si viaja vacío, nulo o no viaja, el pedido de CAE se rechaza y no se puede facturar.",
|
|
12
12
|
"sources": [
|
|
13
|
+
{ "title": "ARCA: Manual para el desarrollador WSFEv1 (RG 4291, revisión del 1/9/2026): el campo, los códigos 10242, 10243, 10245 y 10246 y el anexo de condiciones por clase de comprobante", "url": "https://www.afip.gob.ar/ws/documentacion/manuales/manual-desarrollador-ARCA-COMPG.pdf" },
|
|
14
|
+
{ "title": "ARCA: Manual para el desarrollador WSFEv1 de homologación (v4.8, revisión del 1/12/2026): el campo pasa a ser obligatorio el 01/12/2026 y la observación 10245 queda en desuso", "url": "https://www.afip.gob.ar/fe/ayuda/documentos/wsfev1-RG-4291.pdf" },
|
|
13
15
|
{ "title": "VATupdate: Argentina makes recipient VAT status mandatory in electronic invoices", "url": "https://www.vatupdate.com/2026/09/09/argentina-makes-recipient-vat-status-mandatory-in-electronic-invoices/" },
|
|
14
16
|
{ "title": "iProfesional: ARCA rechazará facturas sin la condición del cliente frente al IVA", "url": "https://www.iprofesional.com/impuestos/464104-nuevo-requisito-arca-puede-trabar-emision-facturas" },
|
|
15
17
|
{ "title": "Tabla de códigos de FEParamGetCondicionIvaReceptor", "url": "https://app.spomsolutions.com/blog/condicion-iva-receptor-rg-5616" },
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "mercadopago.payments-to-orders",
|
|
3
|
-
"version": "2026.10.
|
|
3
|
+
"version": "2026.10.24",
|
|
4
4
|
"provider": "Mercado Pago",
|
|
5
5
|
"title": "Migración de creación de pagos: Payments API → Orders API",
|
|
6
6
|
"kind": "migration",
|
|
@@ -11,7 +11,9 @@
|
|
|
11
11
|
"why_es": "Payments API sigue funcionando pero solo recibe correcciones de seguridad y estabilidad. Orders API unifica el procesamiento online, trae varias transacciones por orden, captura dedicada, 3DS 2.0 nativo y errores consolidados. Los pagos ya creados se siguen consultando, capturando, cancelando y reembolsando por la API con la que se crearon, así que Parche migra solo la creación y deja el resto marcado para revisión.",
|
|
12
12
|
"sources": [
|
|
13
13
|
{ "title": "Mercado Pago: Cómo migrar de Payments API a Orders API", "url": "https://www.mercadopago.com.ar/developers/es/docs/checkout-api-orders/resources/migrate-payments-to-orders" },
|
|
14
|
-
{ "title": "SDK Node.js de Mercado Pago (clase Order)", "url": "https://www.npmjs.com/package/mercadopago" }
|
|
14
|
+
{ "title": "SDK Node.js de Mercado Pago (clase Order)", "url": "https://www.npmjs.com/package/mercadopago" },
|
|
15
|
+
{ "title": "SDK Node.js de Mercado Pago 2.4.0 (1/4/2025): \"Feature: Order\", la primera versión con Order", "url": "https://github.com/mercadopago/sdk-nodejs/releases/tag/2.4.0" },
|
|
16
|
+
{ "title": "mercadopago/sdk-nodejs#481 (abierto, 18/9/2026): las requestOptions de create quedan en la configuración compartida y su idempotencyKey sale en las llamadas siguientes", "url": "https://github.com/mercadopago/sdk-nodejs/issues/481" }
|
|
15
17
|
],
|
|
16
18
|
"detect": {
|
|
17
19
|
"packages": ["mercadopago"],
|
|
@@ -47,7 +49,8 @@
|
|
|
47
49
|
"codemod": "mercadopago-payments-to-orders",
|
|
48
50
|
"review_checklist_es": [
|
|
49
51
|
"Configurar el webhook con el tópico `orders` en Tus integraciones y mantener `payment` en paralelo durante la transición.",
|
|
50
|
-
"
|
|
52
|
+
"Con el SDK, no le pases una `idempotencyKey` fija en `requestOptions`: el SDK manda una `X-Idempotency-Key` nueva en cada escritura, y una fija queda en el client (mercadopago/sdk-nodejs#481) y sale también en los reembolsos y capturas siguientes. Por HTTP directo, mandá `X-Idempotency-Key` en todas las escrituras (crear, capturar, cancelar, reembolsar).",
|
|
53
|
+
"Tener `mercadopago` 2.4.0 o posterior: `Order` existe desde esa versión.",
|
|
51
54
|
"Manejar `status` y `status_detail` a nivel orden y a nivel transacción.",
|
|
52
55
|
"Persistir también `transactions.payments[].id` (los reembolsos parciales lo necesitan).",
|
|
53
56
|
"En sandbox, el email del pagador tiene que ser test@testuser.com.",
|
|
@@ -6,8 +6,31 @@ import {
|
|
|
6
6
|
jFor, detectQuote, eolOf, getProp, hasSpread, keyName, literalValue, isStringLit, resolveBinding,
|
|
7
7
|
memberChain, buildMember, isReference, cloneNode, finding, tidy, snippet,
|
|
8
8
|
} from '../ast.js';
|
|
9
|
+
import { emptyContext } from '../context.js';
|
|
9
10
|
|
|
10
11
|
const PACK = 'mercadopago.payments-to-orders';
|
|
12
|
+
// `Order` existe en el SDK desde la 2.4.0 (release del 1/4/2025, "Feature: Order"; no está en el index de la 2.3.0):
|
|
13
|
+
// con una versión anterior, `import { Order }` queda undefined. Verificado en github.com/mercadopago/sdk-nodejs el 24/10.
|
|
14
|
+
const ORDER_SINCE = [2, 4, 0];
|
|
15
|
+
const isOlder = (version, min) => {
|
|
16
|
+
const v = String(version).split('.').map((n) => Number.parseInt(n, 10) || 0);
|
|
17
|
+
for (let i = 0; i < min.length; i++) if (v[i] !== min[i]) return v[i] < min[i];
|
|
18
|
+
return false;
|
|
19
|
+
};
|
|
20
|
+
// Lo que se sabe de la versión instalada (src/context.js): la del lockfile o node_modules es la que corre; la
|
|
21
|
+
// "declarada" sale del rango de package.json y puede instalar otra.
|
|
22
|
+
function sdkVersion(context) {
|
|
23
|
+
const sdk = context?.versions?.npm?.mercadopago;
|
|
24
|
+
if (!sdk) return { unknown: true };
|
|
25
|
+
if (!isOlder(sdk.version, ORDER_SINCE)) return { ok: true };
|
|
26
|
+
return sdk.source === 'declarada' ? { declaredOld: sdk } : { installedOld: sdk };
|
|
27
|
+
}
|
|
28
|
+
// mercadopago/sdk-nodejs#481 (abierto desde el 18/9/2026): `create` guarda sus requestOptions en la configuración del
|
|
29
|
+
// client (`this.config.options = { ...this.config.options, ...requestOptions }`, de la 2.4.0 a la 3.6.1, también en
|
|
30
|
+
// Payment), y el RestClient solo genera una X-Idempotency-Key nueva cuando no recibe una.
|
|
31
|
+
const IDEMPOTENCY_LEAK = 'Por un bug abierto del SDK (mercadopago/sdk-nodejs#481), las `requestOptions` de `create` quedan guardadas '
|
|
32
|
+
+ 'en la configuración del `client`: esta `idempotencyKey` va a salir también en las escrituras siguientes de ese `client` que no traigan '
|
|
33
|
+
+ 'la suya (reembolsos, capturas). Ya pasaba con `Payment.create`. Si no necesitás una clave fija, sacala: el SDK genera una nueva en cada escritura.';
|
|
11
34
|
const DEBIT = new Set(['debvisa', 'debmaster', 'debcabal', 'maestro']);
|
|
12
35
|
const CREDIT = new Set(['visa', 'master', 'amex', 'naranja', 'cabal', 'tarshop', 'cencosud', 'diners', 'argencard', 'cmr']);
|
|
13
36
|
const TICKET = new Set(['rapipago', 'pagofacil']);
|
|
@@ -28,7 +51,7 @@ const RESPONSE_RENAMES = {
|
|
|
28
51
|
};
|
|
29
52
|
const READ_METHODS = new Set(['get', 'search', 'cancel', 'capture', 'update']);
|
|
30
53
|
|
|
31
|
-
export default function transform({ source, file, pack }) {
|
|
54
|
+
export default function transform({ source, file, pack, context = emptyContext() }) {
|
|
32
55
|
const findings = [];
|
|
33
56
|
const usages = [];
|
|
34
57
|
if (!/mercadopago/.test(source)) return { output: null, findings, usages };
|
|
@@ -50,7 +73,7 @@ export default function transform({ source, file, pack }) {
|
|
|
50
73
|
return node;
|
|
51
74
|
};
|
|
52
75
|
let changed = false;
|
|
53
|
-
|
|
76
|
+
const sdk = sdkVersion(context);
|
|
54
77
|
|
|
55
78
|
textDetections(source, file, findings, usages);
|
|
56
79
|
|
|
@@ -121,7 +144,14 @@ export default function transform({ source, file, pack }) {
|
|
|
121
144
|
}
|
|
122
145
|
const paymentRefs = root.find(j.Identifier, { name: paymentLocal }).filter(isReference).size();
|
|
123
146
|
if (paymentRefs === 0) mp.removeSpecifier('Payment');
|
|
124
|
-
|
|
147
|
+
// Sin la versión instalada a la vista no se puede saber si `Order` existe: se migra, pero para revisar.
|
|
148
|
+
if (sdk.declaredOld) {
|
|
149
|
+
findings.push({ pack: PACK, file, line: mp.line, status: 'needs-review',
|
|
150
|
+
message: `\`package.json\` declara mercadopago \`${sdk.declaredOld.range}\`, que permite instalar una versión sin \`Order\` (existe desde la 2.4.0, del 1/4/2025): fijá \`"mercadopago": "^2.4.0"\` o posterior.` });
|
|
151
|
+
} else if (sdk.unknown) {
|
|
152
|
+
findings.push({ pack: PACK, file, line: mp.line, status: 'needs-review',
|
|
153
|
+
message: 'No pude leer qué versión de mercadopago tenés instalada: `Order` existe desde la 2.4.0 (1/4/2025). Confirmá que tenés esa o una posterior.' });
|
|
154
|
+
}
|
|
125
155
|
}
|
|
126
156
|
|
|
127
157
|
webhookDetections(j, root, file, findings);
|
|
@@ -162,6 +192,12 @@ export default function transform({ source, file, pack }) {
|
|
|
162
192
|
'Usa `application_fee` (Marketplace / Split 1:1). Orders API todavía no tiene un equivalente documentado: NO migrar este flujo hasta que Mercado Pago lo soporte.'));
|
|
163
193
|
return false;
|
|
164
194
|
}
|
|
195
|
+
if (sdk.installedOld) {
|
|
196
|
+
const { version, source: from } = sdk.installedOld;
|
|
197
|
+
findings.push(finding(PACK, file, call, 'blocked',
|
|
198
|
+
`Tenés mercadopago ${version} (según \`${from}\`) y \`Order\` existe desde la 2.4.0 (1/4/2025): migrado ahora, \`Order\` quedaría undefined. Subí el SDK a 2.4.0 o posterior y volvé a correr Parche.`));
|
|
199
|
+
return false;
|
|
200
|
+
}
|
|
165
201
|
const amount = getProp(body, 'transaction_amount');
|
|
166
202
|
if (!amount) {
|
|
167
203
|
findings.push(finding(PACK, file, call, 'needs-input', 'El pago no tiene `transaction_amount` literal en el body. Migrar a mano.'));
|
|
@@ -271,16 +307,13 @@ export default function transform({ source, file, pack }) {
|
|
|
271
307
|
findings.push(finding(PACK, file, call, 'needs-review', 'No hay `external_reference`: Orders lo exige para algunos medios de pago y sirve para conciliar.'));
|
|
272
308
|
}
|
|
273
309
|
|
|
274
|
-
//
|
|
310
|
+
// Orders exige X-Idempotency-Key en cada escritura, y el SDK ya manda una nueva en cada una si no recibe ninguna.
|
|
311
|
+
// Hasta la 0.7.4 Parche agregaba `requestOptions: { idempotencyKey: randomUUID() }`: no aportaba nada y, por el
|
|
312
|
+
// #481, esa clave quedaba en el client y salía también en las escrituras siguientes. Si el código ya traía una, se
|
|
313
|
+
// deja como está y se avisa.
|
|
275
314
|
const ro = getProp(arg, 'requestOptions');
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
arg.properties.push(prop('requestOptions', snippet(j, 'x = { idempotencyKey: randomUUID() };', 'ObjectExpression')));
|
|
279
|
-
needUUID = true;
|
|
280
|
-
findings.push(finding(PACK, file, call, 'auto', 'Se agregó `idempotencyKey` (X-Idempotency-Key es obligatorio en Orders).'));
|
|
281
|
-
} else if (ro.value.type === 'ObjectExpression' && !getProp(ro.value, 'idempotencyKey')) {
|
|
282
|
-
ro.value.properties.push(idem);
|
|
283
|
-
needUUID = true;
|
|
315
|
+
if (ro && ro.value.type === 'ObjectExpression' && getProp(ro.value, 'idempotencyKey')) {
|
|
316
|
+
findings.push(finding(PACK, file, call, 'needs-review', IDEMPOTENCY_LEAK));
|
|
284
317
|
}
|
|
285
318
|
|
|
286
319
|
rewriteResponse(p);
|
|
@@ -415,23 +448,6 @@ function findMpImport(j, root) {
|
|
|
415
448
|
return null;
|
|
416
449
|
}
|
|
417
450
|
|
|
418
|
-
function ensureRandomUUID(j, root, mp, quoteChar) {
|
|
419
|
-
const already = root.find(j.Identifier, { name: 'randomUUID' }).filter((p) => {
|
|
420
|
-
const parent = p.parent.node;
|
|
421
|
-
return parent.type === 'ImportSpecifier' || parent.type === 'ObjectProperty' || parent.type === 'Property';
|
|
422
|
-
}).size();
|
|
423
|
-
if (already) return;
|
|
424
|
-
const q = quoteChar;
|
|
425
|
-
if (mp.kind === 'esm') {
|
|
426
|
-
const decl = snippet(j, `import { randomUUID } from ${q}node:crypto${q};`, 'ImportDeclaration');
|
|
427
|
-
const imports = root.find(j.ImportDeclaration);
|
|
428
|
-
j(imports.paths()[imports.size() - 1]).insertAfter(decl);
|
|
429
|
-
} else {
|
|
430
|
-
const decl = snippet(j, `const { randomUUID } = require(${q}node:crypto${q});`, 'VariableDeclaration');
|
|
431
|
-
j(mp.statement).insertAfter(decl);
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
|
|
435
451
|
function textDetections(source, file, findings, usages) {
|
|
436
452
|
const lines = source.split('\n');
|
|
437
453
|
lines.forEach((l, i) => {
|
package/src/git.js
CHANGED
|
@@ -17,8 +17,19 @@ export function currentBranch(cwd) {
|
|
|
17
17
|
return git(cwd, 'rev-parse', '--abbrev-ref', 'HEAD');
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
// `.parche/` es lo que Parche escribe en cada corrida (reportes, inventario, registro, cuerpos de PR). No cuenta para
|
|
21
|
+
// "repo limpio" y nunca entra en un PR, aunque alguien lo haya commiteado: pasó copiando una carpeta ya escaneada a un
|
|
22
|
+
// repo nuevo, y `scan` lo modificaba y `pr` frenaba con "Hay cambios sin commitear". Todo el repo (`:/`) menos
|
|
23
|
+
// `.parche/` de la carpeta que se revisa (las rutas de exclusión son relativas a `cwd`, que es donde vive `.parche/`).
|
|
24
|
+
const OUTSIDE_PARCHE = ['--', ':/', ':(exclude).parche'];
|
|
25
|
+
|
|
20
26
|
export function isClean(cwd) {
|
|
21
|
-
return git(cwd, 'status', '--porcelain') === '';
|
|
27
|
+
return git(cwd, 'status', '--porcelain', ...OUTSIDE_PARCHE) === '';
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// ¿`.parche/` está commiteado? Solo para avisarlo: Parche funciona igual.
|
|
31
|
+
export function tracksParcheDir(cwd) {
|
|
32
|
+
return git(cwd, 'ls-files', '--', '.parche') !== '';
|
|
22
33
|
}
|
|
23
34
|
|
|
24
35
|
export function checkoutNewBranch(cwd, name, base) {
|
|
@@ -65,6 +76,10 @@ export const isNoreply = (email) => /@users\.noreply\.github\.com$/i.test(email
|
|
|
65
76
|
export function commitAll(cwd, message, env = process.env) {
|
|
66
77
|
const id = commitIdentity(cwd, env);
|
|
67
78
|
git(cwd, 'add', '-A');
|
|
79
|
+
// `.parche/` nunca entra en el commit. Si no está commiteado, `add` ya no lo toma (está en .git/info/exclude); si lo
|
|
80
|
+
// está, se saca del índice lo que `add` tomó de ahí. No va como pathspec de exclusión en `add`: git sale con error
|
|
81
|
+
// cuando una pathspec nombra una carpeta ignorada, aunque sea para excluirla.
|
|
82
|
+
if (tracksParcheDir(cwd)) git(cwd, 'reset', '-q', 'HEAD', '--', '.parche');
|
|
68
83
|
git(cwd, '-c', `user.name=${id.name}`, '-c', `user.email=${id.email}`, 'commit', '-q', '-m', message);
|
|
69
84
|
return git(cwd, 'rev-parse', '--short', 'HEAD');
|
|
70
85
|
}
|
|
@@ -87,9 +102,11 @@ export function push(cwd, branch, remote = 'origin') {
|
|
|
87
102
|
git(cwd, 'push', '-u', '--force', remote, branch);
|
|
88
103
|
}
|
|
89
104
|
|
|
90
|
-
// Descarta lo que Parche escribió en su rama (el árbol estaba limpio antes de empezar
|
|
105
|
+
// Descarta lo que Parche escribió en su rama (el árbol estaba limpio antes de empezar). Antes era `reset --hard`, que
|
|
106
|
+
// con un `.parche/` commiteado también borraba el registro de esta corrida; los codemods solo modifican archivos que
|
|
107
|
+
// ya existen, así que alcanza con volver todo, menos `.parche/`, a lo que hay en HEAD.
|
|
91
108
|
export function discardChanges(cwd) {
|
|
92
|
-
git(cwd, '
|
|
109
|
+
git(cwd, 'checkout', '-q', 'HEAD', ...OUTSIDE_PARCHE);
|
|
93
110
|
}
|
|
94
111
|
|
|
95
112
|
export function deleteBranch(cwd, name) {
|