parche-cli 0.0.0-stage → 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.
@@ -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
+ }
package/package.json CHANGED
@@ -1,6 +1,49 @@
1
1
  {
2
2
  "name": "parche-cli",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.5.1",
4
+ "description": "Dependabot para APIs: detecta cambios de APIs de terceros en tu código y abre el PR con el arreglo.",
5
+ "keywords": [
6
+ "api",
7
+ "migration",
8
+ "codemod",
9
+ "dependabot",
10
+ "arca",
11
+ "afip",
12
+ "wsfe",
13
+ "mercadopago",
14
+ "github-action"
15
+ ],
16
+ "homepage": "https://github.com/Pepo08/parche#readme",
17
+ "bugs": "https://github.com/Pepo08/parche/issues",
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/Pepo08/parche.git"
21
+ },
22
+ "author": "Joaquín González",
23
+ "license": "MIT",
24
+ "type": "module",
25
+ "bin": {
26
+ "parche": "bin/parche.js"
27
+ },
28
+ "files": [
29
+ "bin",
30
+ "src",
31
+ "packs",
32
+ "data",
33
+ "templates",
34
+ "CHANGELOG.md"
35
+ ],
36
+ "scripts": {
37
+ "test": "node --test test/*.test.js",
38
+ "demo": "bash demo.sh"
39
+ },
40
+ "engines": {
41
+ "node": ">=22"
42
+ },
43
+ "dependencies": {
44
+ "@babel/parser": "^7.28.0",
45
+ "jscodeshift": "^17.3.0",
46
+ "php-parser": "^3.7.0",
47
+ "typescript": "~6.0.3"
48
+ }
49
+ }
@@ -0,0 +1,96 @@
1
+ {
2
+ "id": "ai.model-retirements",
3
+ "version": "2026.10.16",
4
+ "provider": "Modelos de IA",
5
+ "title": "Modelos y endpoints de IA retirados o por retirarse",
6
+ "kind": "retirement",
7
+ "severity": "high",
8
+ "deadline": null,
9
+ "last_verified": "2026-10-16",
10
+ "summary_es": "Tu código usa modelos o APIs de IA que ya no responden o tienen fecha de retiro.",
11
+ "why_es": "Los proveedores de modelos retiran IDs de modelo y endpoints con algunas semanas o meses de aviso. Después de la fecha, las llamadas fallan. Parche no reemplaza el modelo automáticamente porque cambiar de modelo cambia el comportamiento: te avisa, te dice la fecha y el reemplazo que recomienda el proveedor.",
12
+ "sources": [
13
+ { "title": "Anthropic: Model deprecations", "url": "https://platform.claude.com/docs/en/about-claude/model-deprecations" },
14
+ { "title": "OpenAI: Deprecations", "url": "https://developers.openai.com/api/docs/deprecations" }
15
+ ],
16
+ "detect": {
17
+ "packages": ["@anthropic-ai/sdk", "openai"],
18
+ "retired_models": {
19
+ "claude-sonnet-4-5-20250929": { "date": "2026-11-30", "replacement": "claude-sonnet-5-5" },
20
+ "claude-opus-4-1-20250805": { "date": "2026-08-05", "replacement": "claude-opus-4-8" },
21
+ "claude-sonnet-4-20250514": { "date": "2026-06-15", "replacement": "claude-sonnet-5-5" },
22
+ "claude-opus-4-20250514": { "date": "2026-06-15", "replacement": "claude-opus-4-8" },
23
+ "claude-3-haiku-20240307": { "date": "2026-04-20", "replacement": "claude-haiku-4-5-20251001" },
24
+ "claude-3-7-sonnet-20250219": { "date": "2026-02-19", "replacement": "claude-sonnet-5-5" },
25
+ "claude-3-5-haiku-20241022": { "date": "2026-02-19", "replacement": "claude-haiku-4-5-20251001" },
26
+ "claude-3-opus-20240229": { "date": "2026-01-05", "replacement": "claude-opus-4-8" },
27
+ "claude-3-5-sonnet-20241022": { "date": "2025-10-28", "replacement": "claude-sonnet-5-5" },
28
+ "claude-3-5-sonnet-20240620": { "date": "2025-10-28", "replacement": "claude-sonnet-5-5" },
29
+ "claude-3-sonnet-20240229": { "date": "2025-07-21", "replacement": "claude-sonnet-5-5" },
30
+ "claude-2.1": { "date": "2025-07-21", "replacement": "claude-opus-4-8" },
31
+ "claude-2.0": { "date": "2025-07-21", "replacement": "claude-opus-4-8" },
32
+ "claude-instant-1.2": { "date": "2024-11-06", "replacement": "claude-haiku-4-5-20251001" },
33
+ "claude-instant-1.1": { "date": "2024-11-06", "replacement": "claude-haiku-4-5-20251001" },
34
+ "claude-instant-1.0": { "date": "2024-11-06", "replacement": "claude-haiku-4-5-20251001" },
35
+ "claude-1.3": { "date": "2024-11-06", "replacement": "claude-haiku-4-5-20251001" },
36
+ "claude-1.2": { "date": "2024-11-06", "replacement": "claude-haiku-4-5-20251001" },
37
+ "claude-1.1": { "date": "2024-11-06", "replacement": "claude-haiku-4-5-20251001" },
38
+ "claude-1.0": { "date": "2024-11-06", "replacement": "claude-haiku-4-5-20251001" },
39
+
40
+ "gpt-5-2025-08-07": { "date": "2026-12-11", "replacement": "gpt-5.6-sol" },
41
+ "gpt-5-mini-2025-08-07": { "date": "2026-12-11", "replacement": "gpt-5.6-terra" },
42
+ "gpt-5-nano-2025-08-07": { "date": "2026-12-11", "replacement": "gpt-5.6-luna" },
43
+ "gpt-5-pro-2025-10-06": { "date": "2026-12-11", "replacement": "gpt-5.6-sol (reasoning.mode: pro)" },
44
+ "o3-2025-04-16": { "date": "2026-12-11", "replacement": "gpt-5.6-sol" },
45
+ "o3-pro-2025-06-10": { "date": "2026-12-11", "replacement": "gpt-5.6-sol (reasoning.mode: pro)" },
46
+ "gpt-3.5-turbo": { "date": "2026-10-23", "replacement": "gpt-5.6-terra" },
47
+ "gpt-3.5-turbo-0125": { "date": "2026-10-23", "replacement": "gpt-5.6-terra" },
48
+ "gpt-4": { "date": "2026-10-23", "replacement": "gpt-5.6-sol" },
49
+ "gpt-4-0613": { "date": "2026-10-23", "replacement": "gpt-5.6-sol" },
50
+ "gpt-4-1106-preview": { "date": "2026-10-23", "replacement": "gpt-5.6-sol" },
51
+ "gpt-4-turbo": { "date": "2026-10-23", "replacement": "gpt-5.6-sol" },
52
+ "gpt-4-turbo-2024-04-09": { "date": "2026-10-23", "replacement": "gpt-5.6-sol" },
53
+ "gpt-4.1-nano": { "date": "2026-10-23", "replacement": "gpt-5.6-luna" },
54
+ "gpt-4.1-nano-2025-04-14": { "date": "2026-10-23", "replacement": "gpt-5.6-luna" },
55
+ "gpt-4o-2024-05-13": { "date": "2026-10-23", "replacement": "gpt-5.6-sol" },
56
+ "gpt-image-1": { "date": "2026-10-23", "replacement": "gpt-image-2.5-sunburst o gpt-image-2.5-flare" },
57
+ "o1": { "date": "2026-10-23", "replacement": "gpt-5.6-sol" },
58
+ "o1-2024-12-17": { "date": "2026-10-23", "replacement": "gpt-5.6-sol" },
59
+ "o1-pro": { "date": "2026-10-23", "replacement": "gpt-5.6-sol (reasoning.mode: pro)" },
60
+ "o1-pro-2025-03-19": { "date": "2026-10-23", "replacement": "gpt-5.6-sol (reasoning.mode: pro)" },
61
+ "o3-mini": { "date": "2026-10-23", "replacement": "gpt-5.6-sol" },
62
+ "o3-mini-2025-01-31": { "date": "2026-10-23", "replacement": "gpt-5.6-sol" },
63
+ "o4-mini": { "date": "2026-10-23", "replacement": "gpt-5.6-terra" },
64
+ "o4-mini-2025-04-16": { "date": "2026-10-23", "replacement": "gpt-5.6-terra" },
65
+ "gpt-3.5-turbo-instruct": { "date": "2026-09-28", "replacement": "gpt-5.6-terra" },
66
+ "gpt-3.5-turbo-1106": { "date": "2026-09-28", "replacement": "gpt-5.6-terra" },
67
+ "babbage-002": { "date": "2026-09-28", "replacement": "gpt-5.6-terra" },
68
+ "davinci-002": { "date": "2026-09-28", "replacement": "gpt-5.6-terra" },
69
+ "dall-e-3": { "date": "2026-05-12", "replacement": "gpt-image-2, gpt-image-1 o gpt-image-1-mini" },
70
+ "dall-e-2": { "date": "2026-05-12", "replacement": "gpt-image-2, gpt-image-1 o gpt-image-1-mini" },
71
+ "gpt-4o-realtime-preview": { "date": "2026-05-07", "replacement": "gpt-realtime-1.5" },
72
+ "gpt-4o-realtime-preview-2025-06-03": { "date": "2026-05-07", "replacement": "gpt-realtime-1.5" },
73
+ "gpt-4o-realtime-preview-2024-12-17": { "date": "2026-05-07", "replacement": "gpt-realtime-1.5" },
74
+ "gpt-4-0314": { "date": "2026-03-26", "replacement": "gpt-5 o gpt-4.1" },
75
+ "gpt-4-0125-preview": { "date": "2026-03-26", "replacement": "gpt-5 o gpt-4.1" },
76
+ "gpt-4-turbo-preview": { "date": "2026-03-26", "replacement": "gpt-5 o gpt-4.1" },
77
+ "chatgpt-4o-latest": { "date": "2026-02-17", "replacement": "gpt-5.1-chat-latest" },
78
+ "codex-mini-latest": { "date": "2026-02-12", "replacement": "gpt-5-codex-mini" }
79
+ },
80
+ "retirement_floors": {
81
+ "claude-haiku-4-5-20251001": "2026-10-15",
82
+ "claude-opus-4-5-20251101": "2026-11-24"
83
+ },
84
+ "retired_endpoints": [
85
+ { "pattern": ".beta.assistants", "retired": "2026-08-26", "replacement_es": "Responses API + Conversations API", "provider": "openai" },
86
+ { "pattern": ".beta.threads", "retired": "2026-08-26", "replacement_es": "Responses API + Conversations API", "provider": "openai" },
87
+ { "pattern": "/v1/assistants", "retired": "2026-08-26", "replacement_es": "Responses API + Conversations API", "provider": "openai" }
88
+ ]
89
+ },
90
+ "codemod": null,
91
+ "review_checklist_es": [
92
+ "Elegir el modelo de reemplazo según la página de deprecaciones del proveedor.",
93
+ "Correr tus evals o pruebas de regresión de prompts antes de cambiar de modelo.",
94
+ "Revisar parámetros que cambian entre generaciones de modelos."
95
+ ]
96
+ }
@@ -0,0 +1,51 @@
1
+ {
2
+ "id": "arca.wsfe.condicion-iva-receptor",
3
+ "version": "2026.10.16",
4
+ "provider": "ARCA (ex AFIP)",
5
+ "title": "Condición frente al IVA del receptor obligatoria en WSFEv1",
6
+ "kind": "breaking",
7
+ "severity": "critical",
8
+ "deadline": "2026-12-01",
9
+ "last_verified": "2026-10-16",
10
+ "summary_es": "Desde el 1/12/2026 ARCA rechaza automáticamente las solicitudes de CAE que no informen CondicionIVAReceptorId.",
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 (v4.8) 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
+ "sources": [
13
+ { "title": "VATupdate: Argentina makes recipient VAT status mandatory (WSFEv1 v4.8)", "url": "https://www.vatupdate.com/2026/09/09/argentina-makes-recipient-vat-status-mandatory-in-electronic-invoices/" },
14
+ { "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
+ { "title": "Tabla de códigos de FEParamGetCondicionIvaReceptor", "url": "https://app.spomsolutions.com/blog/condicion-iva-receptor-rg-5616" },
16
+ { "title": "Afip SDK: factura electrónica (Node y PHP, CreateVoucher / CreateNextVoucher)", "url": "https://docs.afipsdk.com/siguientes-pasos/web-services/factura-electronica" },
17
+ { "title": "afipsdk/afip.php en Packagist", "url": "https://packagist.org/packages/afipsdk/afip.php" }
18
+ ],
19
+ "detect": {
20
+ "packages": ["@afipsdk/afip.js", "@arcasdk/core", "afip.ts", "ts-afip-ws", "facturajs", "afipsdk/afip.php", "fertandil87/afip.php", "ivanalemunioz/afip.php", "eesnaola/afip.php"],
21
+ "calls": ["ElectronicBilling.createVoucher", "ElectronicBilling.createNextVoucher", "ElectronicBilling->CreateVoucher", "ElectronicBilling->CreateNextVoucher"],
22
+ "raw_markers": ["FECAESolicitar", "FECAEDetRequest"],
23
+ "field": "CondicionIVAReceptorId"
24
+ },
25
+ "codes": {
26
+ "1": "IVA Responsable Inscripto",
27
+ "4": "IVA Sujeto Exento",
28
+ "5": "Consumidor Final",
29
+ "6": "Responsable Monotributo",
30
+ "7": "Sujeto No Categorizado",
31
+ "8": "Proveedor del Exterior",
32
+ "9": "Cliente del Exterior",
33
+ "10": "IVA Liberado – Ley N° 19.640",
34
+ "13": "Monotributista Social",
35
+ "15": "IVA No Alcanzado",
36
+ "16": "Monotributo Trabajador Independiente Promovido"
37
+ },
38
+ "codes_by_class": {
39
+ "A": [1, 6, 13, 16],
40
+ "B": [4, 5, 7, 8, 9, 10, 15],
41
+ "C": [1, 4, 5, 6, 7, 8, 9, 10, 13, 15, 16]
42
+ },
43
+ "codes_note_es": "Validá los códigos contra FEParamGetCondicionIvaReceptor en homologación antes de pasar a producción.",
44
+ "codemod": "arca-wsfe-condicion-iva",
45
+ "review_checklist_es": [
46
+ "Probar la emisión en homologación (wsfehomo) con cada tipo de comprobante que emitís.",
47
+ "Confirmar que cada cliente con CUIT tiene guardada su condición frente al IVA y que se envía en cada CAE.",
48
+ "Pedir la condición frente al IVA en el alta de clientes nuevos.",
49
+ "Validar los códigos con FEParamGetCondicionIvaReceptor (la tabla puede cambiar)."
50
+ ]
51
+ }
@@ -0,0 +1,57 @@
1
+ {
2
+ "id": "mercadopago.payments-to-orders",
3
+ "version": "2026.10.15",
4
+ "provider": "Mercado Pago",
5
+ "title": "Migración de creación de pagos: Payments API → Orders API",
6
+ "kind": "migration",
7
+ "severity": "medium",
8
+ "deadline": null,
9
+ "last_verified": "2026-10-15",
10
+ "summary_es": "Mercado Pago dejó de sumar features a Payments API; Orders API es la integración nueva para Checkout API.",
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
+ "sources": [
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" }
15
+ ],
16
+ "detect": {
17
+ "packages": ["mercadopago"],
18
+ "calls": ["Payment.create", "Payment.get", "Payment.search", "Payment.cancel", "Payment.capture", "PaymentRefund"],
19
+ "raw_markers": ["api.mercadopago.com/v1/payments"],
20
+ "legacy_v1": ["mercadopago.configure(", "mercadopago.payment.create(", "mercadopago.payment.save("]
21
+ },
22
+ "status_map": {
23
+ "approved": "processed",
24
+ "rejected": "failed",
25
+ "cancelled": "canceled",
26
+ "pending": "action_required",
27
+ "in_process": "processing",
28
+ "authorized": "action_required",
29
+ "in_mediation": "charged_back",
30
+ "refunded": "refunded",
31
+ "charged_back": "charged_back"
32
+ },
33
+ "status_detail_map": {
34
+ "cc_rejected_bad_filled_card_number": "bad_filled_card_data",
35
+ "cc_rejected_bad_filled_date": "bad_filled_card_data",
36
+ "cc_rejected_bad_filled_security_code": "bad_filled_card_data",
37
+ "cc_rejected_call_for_authorize": "required_call_for_authorize",
38
+ "cc_rejected_card_disabled": "card_disabled",
39
+ "cc_rejected_duplicated_payment": "processing_error",
40
+ "cc_rejected_high_risk": "high_risk",
41
+ "cc_rejected_insufficient_amount": "insufficient_amount",
42
+ "cc_rejected_invalid_installments": "invalid_installments",
43
+ "cc_rejected_max_attempts": "max_attempts_exceeded",
44
+ "cc_rejected_other_reason": "rejected_by_issuer",
45
+ "pending_waiting_transfer": "waiting_transfer"
46
+ },
47
+ "codemod": "mercadopago-payments-to-orders",
48
+ "review_checklist_es": [
49
+ "Configurar el webhook con el tópico `orders` en Tus integraciones y mantener `payment` en paralelo durante la transición.",
50
+ "Enviar `X-Idempotency-Key` en todas las escrituras (crear, capturar, cancelar, reembolsar).",
51
+ "Manejar `status` y `status_detail` a nivel orden y a nivel transacción.",
52
+ "Persistir también `transactions.payments[].id` (los reembolsos parciales lo necesitan).",
53
+ "En sandbox, el email del pagador tiene que ser test@testuser.com.",
54
+ "Si usás captura manual: la ventana de captura es de 5 días y Orders solo soporta captura total.",
55
+ "Pagos creados con Payments API se siguen consultando y reembolsando por Payments API."
56
+ ]
57
+ }
package/src/ast.js ADDED
@@ -0,0 +1,204 @@
1
+ // Utilidades de AST compartidas por todos los codemods.
2
+ import jscodeshift from 'jscodeshift';
3
+ import { parse as babelParse } from '@babel/parser';
4
+ import { parsePhp, isPhp } from './php.js';
5
+
6
+ export function jFor(file) {
7
+ const parser = /\.ts$/.test(file) ? 'ts' : 'tsx';
8
+ return jscodeshift.withParser(parser);
9
+ }
10
+
11
+ // Estilo de comillas predominante en el archivo, para que el código nuevo combine.
12
+ export function detectQuote(source) {
13
+ const single = (source.match(/'[^'\n]*'/g) || []).length;
14
+ const double = (source.match(/"[^"\n]*"/g) || []).length;
15
+ return double > single ? 'double' : 'single';
16
+ }
17
+
18
+ // Fin de línea predominante en el archivo. recast imprime con os.EOL por defecto (\r\n en
19
+ // Windows), así que hay que pasárselo siempre para no reescribir el archivo entero.
20
+ export function eolOf(source) {
21
+ const crlf = (source.match(/\r\n/g) || []).length;
22
+ const lf = (source.match(/\n/g) || []).length - crlf;
23
+ return crlf > lf ? '\r\n' : '\n';
24
+ }
25
+
26
+ // Aplica inserciones de texto { offset, text } de atrás para adelante, así no se corren los offsets.
27
+ export function applyEdits(source, edits) {
28
+ let out = source;
29
+ for (const e of [...edits].sort((a, b) => b.offset - a.offset)) {
30
+ out = out.slice(0, e.offset) + e.text + out.slice(e.offset);
31
+ }
32
+ return out;
33
+ }
34
+
35
+ // Offset en el texto original de una posición { line, column } de recast.
36
+ // No usar node.start/node.end para editar texto: recast parsea una copia con las líneas unidas
37
+ // por os.EOL y las tabs de la indentación expandidas, así que esos offsets no coinciden con el
38
+ // archivo del cliente (en Windows quedan corridos un carácter por línea).
39
+ export function offsetAt(source, { line, column }, tabWidth = 4) {
40
+ let k = 0;
41
+ for (let i = 1; i < line; i++) k = source.indexOf('\n', k) + 1;
42
+ // La indentación se mide como en recast: tab hasta el próximo múltiplo, \v \f y BOM no ocupan.
43
+ let width = 0;
44
+ while (width < column && /[^\S\r\n]/.test(source[k] ?? '')) {
45
+ if (source[k] === '\t') width = (Math.floor(width / tabWidth) + 1) * tabWidth;
46
+ else if (!'\v\f'.includes(source[k])) width += 1;
47
+ k++;
48
+ }
49
+ return k + (column - width);
50
+ }
51
+
52
+ export function keyName(prop) {
53
+ if (!prop || !prop.key) return null;
54
+ if (prop.computed) return null;
55
+ const k = prop.key;
56
+ if (k.type === 'Identifier') return k.name;
57
+ if (k.type === 'StringLiteral' || (k.type === 'Literal' && typeof k.value === 'string')) return k.value;
58
+ return null;
59
+ }
60
+
61
+ export function isPlainProp(prop) {
62
+ return prop && (prop.type === 'ObjectProperty' || prop.type === 'Property');
63
+ }
64
+
65
+ export function hasSpread(obj) {
66
+ return obj.properties.some((p) => p.type === 'SpreadElement' || p.type === 'SpreadProperty' || p.type === 'ObjectMethod');
67
+ }
68
+
69
+ export function getProp(obj, name) {
70
+ return obj.properties.find((p) => isPlainProp(p) && keyName(p) === name) || null;
71
+ }
72
+
73
+ export function isQuotedKey(prop) {
74
+ return prop.key && (prop.key.type === 'StringLiteral' || prop.key.type === 'Literal');
75
+ }
76
+
77
+ export function literalValue(node) {
78
+ if (!node) return undefined;
79
+ if (['StringLiteral', 'NumericLiteral', 'BooleanLiteral'].includes(node.type)) return node.value;
80
+ if (node.type === 'Literal') return node.value;
81
+ if (node.type === 'TemplateLiteral' && node.expressions.length === 0) return node.quasis[0].value.cooked;
82
+ return undefined;
83
+ }
84
+
85
+ export function isStringLit(node) {
86
+ return node && (node.type === 'StringLiteral' || (node.type === 'Literal' && typeof node.value === 'string'));
87
+ }
88
+
89
+ export function lineOf(node) {
90
+ return node && node.loc ? node.loc.start.line : null;
91
+ }
92
+
93
+ // Copia profunda de un nodo sin metadatos de posición, para poder usarlo dos veces.
94
+ export function cloneNode(node) {
95
+ if (Array.isArray(node)) return node.map(cloneNode);
96
+ if (!node || typeof node !== 'object') return node;
97
+ const out = {};
98
+ for (const [k, v] of Object.entries(node)) {
99
+ if (['loc', 'start', 'end', 'range', 'comments', 'leadingComments', 'trailingComments', 'innerComments', 'original', 'tokens'].includes(k)) continue;
100
+ out[k] = cloneNode(v);
101
+ }
102
+ return out;
103
+ }
104
+
105
+ // Cadena de propiedades de un MemberExpression no computado: result.a.b → { root: 'result', path: ['a','b'] }
106
+ export function memberChain(node) {
107
+ const path = [];
108
+ let cur = node;
109
+ while (cur && (cur.type === 'MemberExpression' || cur.type === 'OptionalMemberExpression')) {
110
+ if (cur.computed || cur.property.type !== 'Identifier') return null;
111
+ path.unshift(cur.property.name);
112
+ cur = cur.object;
113
+ }
114
+ if (!cur || cur.type !== 'Identifier') return null;
115
+ return { root: cur.name, path };
116
+ }
117
+
118
+ export function buildMember(j, rootName, parts) {
119
+ let expr = j.identifier(rootName);
120
+ for (const part of parts) {
121
+ if (typeof part === 'number') expr = j.memberExpression(expr, j.numericLiteral(part), true);
122
+ else expr = j.memberExpression(expr, j.identifier(part));
123
+ }
124
+ return expr;
125
+ }
126
+
127
+ // ¿Este Identifier es una referencia real (no una clave de objeto ni una propiedad de member)?
128
+ export function isReference(path) {
129
+ const parent = path.parent && path.parent.node;
130
+ if (!parent) return true;
131
+ if ((parent.type === 'MemberExpression' || parent.type === 'OptionalMemberExpression') && parent.property === path.node && !parent.computed) return false;
132
+ if ((parent.type === 'ObjectProperty' || parent.type === 'Property') && parent.key === path.node && !parent.computed && !parent.shorthand) return false;
133
+ if (parent.type === 'ImportSpecifier' || parent.type === 'ImportDefaultSpecifier') return false;
134
+ if (parent.type === 'VariableDeclarator' && parent.id === path.node) return false;
135
+ if (parent.type === 'TSPropertySignature' || parent.type === 'TSQualifiedName') return false;
136
+ const grand = path.parent.parent && path.parent.parent.node;
137
+ if ((parent.type === 'ObjectProperty' || parent.type === 'Property') && grand && grand.type === 'ObjectPattern') return false;
138
+ return true;
139
+ }
140
+
141
+ // Nodo armado desde texto: recast lo imprime tal cual, sin reformatear.
142
+ export function snippet(j, code, type) {
143
+ return j(code).find(j[type]).paths()[0].node;
144
+ }
145
+
146
+ // Resuelve un Identifier a la inicialización de su declaración (const x = {...}) usando el scope.
147
+ export function resolveBinding(path, name) {
148
+ const scope = path.scope && path.scope.lookup(name);
149
+ if (!scope) return null;
150
+ const bindings = scope.getBindings()[name];
151
+ if (!bindings || !bindings.length) return null;
152
+ const declPath = bindings[0].parent;
153
+ if (declPath && declPath.node.type === 'VariableDeclarator' && declPath.node.id.type === 'Identifier') return declPath;
154
+ return null;
155
+ }
156
+
157
+ // Chequeo de sintaxis del resultado, usado por verify y por los tests.
158
+ export function parseCheck(source, file) {
159
+ if (isPhp(file)) {
160
+ parsePhp(source, file);
161
+ return true;
162
+ }
163
+ // decorators-legacy: NestJS, Angular y TypeORM usan decoradores (@Injectable()); sin el plugin, el chequeo
164
+ // de sintaxis fallaba y --verify frenaba el PR en esos repos.
165
+ const plugins = ['jsx', 'decorators-legacy'];
166
+ if (/\.(ts|tsx)$/.test(file)) plugins.push('typescript');
167
+ if (/\.ts$/.test(file)) plugins.splice(plugins.indexOf('jsx'), 1);
168
+ babelParse(source, { sourceType: 'unambiguous', plugins, allowAwaitOutsideFunction: true });
169
+ return true;
170
+ }
171
+
172
+ export function finding(pack, file, node, status, message, extra = {}) {
173
+ return { pack, file, line: lineOf(node), status, message, ...extra };
174
+ }
175
+
176
+ // Recast agrega líneas en blanco alrededor de propiedades multilínea que reimprime.
177
+ // Esto saca SOLO las líneas en blanco nuevas (las que no estaban en el original),
178
+ // usando un diff de líneas, así el PR muestra el cambio y nada más.
179
+ export function tidy(original, output) {
180
+ const a = original.split('\n');
181
+ const b = output.split('\n');
182
+ const n = a.length;
183
+ const m = b.length;
184
+ const dp = Array.from({ length: n + 1 }, () => new Uint32Array(m + 1));
185
+ for (let i = n - 1; i >= 0; i--) {
186
+ for (let k = m - 1; k >= 0; k--) {
187
+ dp[i][k] = a[i] === b[k] ? dp[i + 1][k + 1] + 1 : Math.max(dp[i + 1][k], dp[i][k + 1]);
188
+ }
189
+ }
190
+ const keep = [];
191
+ let i = 0;
192
+ let k = 0;
193
+ while (k < m) {
194
+ // Una línea en blanco que también puede emparejarse más adelante se toma como nueva: así lo
195
+ // insertado con insertAfter queda pegado a la línea anterior (el import nuevo junto a los otros
196
+ // imports) y la línea en blanco original queda después.
197
+ if (i < n && a[i] === b[k] && b[k].trim() === '' && dp[i][k + 1] === dp[i][k]) { k++; continue; }
198
+ if (i < n && a[i] === b[k]) { keep.push(b[k]); i++; k++; continue; }
199
+ if (i < n && dp[i + 1][k] >= dp[i][k + 1]) { i++; continue; }
200
+ if (b[k].trim() !== '') keep.push(b[k]); // línea nueva con contenido: se queda
201
+ k++;
202
+ }
203
+ return keep.join('\n');
204
+ }