parche-cli 0.1.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +106 -0
- package/README.md +108 -13
- package/bin/parche.js +152 -44
- 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 +140 -0
- package/src/codemods/arca-wsfe-condicion-iva-php.js +276 -0
- package/src/codemods/arca-wsfe-condicion-iva.js +173 -71
- 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 +85 -18
- 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
|
@@ -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,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "parche-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Dependabot para APIs: detecta cambios de APIs de terceros en tu código y abre el PR con el arreglo.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"api",
|
|
@@ -29,7 +29,9 @@
|
|
|
29
29
|
"bin",
|
|
30
30
|
"src",
|
|
31
31
|
"packs",
|
|
32
|
-
"
|
|
32
|
+
"data",
|
|
33
|
+
"templates",
|
|
34
|
+
"CHANGELOG.md"
|
|
33
35
|
],
|
|
34
36
|
"scripts": {
|
|
35
37
|
"test": "node --test test/*.test.js",
|
|
@@ -40,6 +42,8 @@
|
|
|
40
42
|
},
|
|
41
43
|
"dependencies": {
|
|
42
44
|
"@babel/parser": "^7.28.0",
|
|
43
|
-
"jscodeshift": "^17.3.0"
|
|
45
|
+
"jscodeshift": "^17.3.0",
|
|
46
|
+
"php-parser": "^3.7.0",
|
|
47
|
+
"typescript": "~6.0.3"
|
|
44
48
|
}
|
|
45
49
|
}
|
|
@@ -1,38 +1,90 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "ai.model-retirements",
|
|
3
|
-
"version": "2026.10.
|
|
3
|
+
"version": "2026.10.16",
|
|
4
4
|
"provider": "Modelos de IA",
|
|
5
5
|
"title": "Modelos y endpoints de IA retirados o por retirarse",
|
|
6
6
|
"kind": "retirement",
|
|
7
7
|
"severity": "high",
|
|
8
8
|
"deadline": null,
|
|
9
|
-
"last_verified": "2026-10-
|
|
9
|
+
"last_verified": "2026-10-16",
|
|
10
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
|
|
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
12
|
"sources": [
|
|
13
13
|
{ "title": "Anthropic: Model deprecations", "url": "https://platform.claude.com/docs/en/about-claude/model-deprecations" },
|
|
14
|
-
{ "title": "OpenAI
|
|
14
|
+
{ "title": "OpenAI: Deprecations", "url": "https://developers.openai.com/api/docs/deprecations" }
|
|
15
15
|
],
|
|
16
16
|
"detect": {
|
|
17
17
|
"packages": ["@anthropic-ai/sdk", "openai"],
|
|
18
18
|
"retired_models": {
|
|
19
|
-
"claude-
|
|
20
|
-
"claude-
|
|
21
|
-
"claude-
|
|
22
|
-
"claude-
|
|
23
|
-
"claude-
|
|
24
|
-
"claude-
|
|
25
|
-
"claude-
|
|
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" }
|
|
26
79
|
},
|
|
27
80
|
"retirement_floors": {
|
|
28
|
-
"claude-sonnet-4-5-20250929": "2026-09-29",
|
|
29
81
|
"claude-haiku-4-5-20251001": "2026-10-15",
|
|
30
82
|
"claude-opus-4-5-20251101": "2026-11-24"
|
|
31
83
|
},
|
|
32
84
|
"retired_endpoints": [
|
|
33
|
-
{ "pattern": ".beta.assistants", "retired": "2026-08-26", "replacement_es": "Responses API + Conversations API" },
|
|
34
|
-
{ "pattern": ".beta.threads", "retired": "2026-08-26", "replacement_es": "Responses API + Conversations API" },
|
|
35
|
-
{ "pattern": "/v1/assistants", "retired": "2026-08-26", "replacement_es": "Responses API + Conversations API" }
|
|
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" }
|
|
36
88
|
]
|
|
37
89
|
},
|
|
38
90
|
"codemod": null,
|
|
@@ -1,22 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "arca.wsfe.condicion-iva-receptor",
|
|
3
|
-
"version": "2026.10.
|
|
3
|
+
"version": "2026.10.16",
|
|
4
4
|
"provider": "ARCA (ex AFIP)",
|
|
5
5
|
"title": "Condición frente al IVA del receptor obligatoria en WSFEv1",
|
|
6
6
|
"kind": "breaking",
|
|
7
7
|
"severity": "critical",
|
|
8
8
|
"deadline": "2026-12-01",
|
|
9
|
-
"last_verified": "2026-10-
|
|
9
|
+
"last_verified": "2026-10-16",
|
|
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 (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
12
|
"sources": [
|
|
13
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
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" }
|
|
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" }
|
|
16
18
|
],
|
|
17
19
|
"detect": {
|
|
18
|
-
"packages": ["@afipsdk/afip.js", "afip.
|
|
19
|
-
"calls": ["ElectronicBilling.createVoucher", "ElectronicBilling.createNextVoucher"],
|
|
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"],
|
|
20
22
|
"raw_markers": ["FECAESolicitar", "FECAEDetRequest"],
|
|
21
23
|
"field": "CondicionIVAReceptorId"
|
|
22
24
|
},
|
package/src/ast.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Utilidades de AST compartidas por todos los codemods.
|
|
2
2
|
import jscodeshift from 'jscodeshift';
|
|
3
3
|
import { parse as babelParse } from '@babel/parser';
|
|
4
|
+
import { parsePhp, isPhp } from './php.js';
|
|
4
5
|
|
|
5
6
|
export function jFor(file) {
|
|
6
7
|
const parser = /\.ts$/.test(file) ? 'ts' : 'tsx';
|
|
@@ -22,6 +23,15 @@ export function eolOf(source) {
|
|
|
22
23
|
return crlf > lf ? '\r\n' : '\n';
|
|
23
24
|
}
|
|
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
|
+
|
|
25
35
|
// Offset en el texto original de una posición { line, column } de recast.
|
|
26
36
|
// No usar node.start/node.end para editar texto: recast parsea una copia con las líneas unidas
|
|
27
37
|
// por os.EOL y las tabs de la indentación expandidas, así que esos offsets no coinciden con el
|
|
@@ -146,7 +156,13 @@ export function resolveBinding(path, name) {
|
|
|
146
156
|
|
|
147
157
|
// Chequeo de sintaxis del resultado, usado por verify y por los tests.
|
|
148
158
|
export function parseCheck(source, file) {
|
|
149
|
-
|
|
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'];
|
|
150
166
|
if (/\.(ts|tsx)$/.test(file)) plugins.push('typescript');
|
|
151
167
|
if (/\.ts$/.test(file)) plugins.splice(plugins.indexOf('jsx'), 1);
|
|
152
168
|
babelParse(source, { sourceType: 'unambiguous', plugins, allowAwaitOutsideFunction: true });
|
|
@@ -175,6 +191,10 @@ export function tidy(original, output) {
|
|
|
175
191
|
let i = 0;
|
|
176
192
|
let k = 0;
|
|
177
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; }
|
|
178
198
|
if (i < n && a[i] === b[k]) { keep.push(b[k]); i++; k++; continue; }
|
|
179
199
|
if (i < n && dp[i + 1][k] >= dp[i][k + 1]) { i++; continue; }
|
|
180
200
|
if (b[k].trim() !== '') keep.push(b[k]); // línea nueva con contenido: se queda
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
// Reglas del pack arca.wsfe.condicion-iva-receptor, compartidas por los codemods de JS y de PHP:
|
|
2
|
+
// qué se puede inferir sin datos del negocio y con qué mensajes se reporta.
|
|
3
|
+
|
|
4
|
+
import { isTestFile } from '../paths.js';
|
|
5
|
+
|
|
6
|
+
export const PACK = 'arca.wsfe.condicion-iva-receptor';
|
|
7
|
+
|
|
8
|
+
// Prefiltro de los codemods: sin estas palabras no hay nada de ARCA en el archivo y no se parsea
|
|
9
|
+
// (en repos con bundles grandes, parsear todo tardaba minutos y 1,5 GB de RAM).
|
|
10
|
+
export const ARCA_HINT = /electronicbilling|fecaesolicitar|fecaedetrequest/i;
|
|
11
|
+
|
|
12
|
+
const CLASS = {
|
|
13
|
+
A: new Set([1, 2, 3, 201, 202, 203]),
|
|
14
|
+
B: new Set([6, 7, 8, 206, 207, 208]),
|
|
15
|
+
C: new Set([11, 12, 13, 211, 212, 213]),
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export function classOf(cbteTipo) {
|
|
19
|
+
for (const [k, set] of Object.entries(CLASS)) if (set.has(cbteTipo)) return k;
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// docTipo y cbteTipo: números si el valor es un literal, undefined si no se puede saber.
|
|
24
|
+
export function decideCondition({ docTipo, cbteTipo }, pack) {
|
|
25
|
+
const clase = typeof cbteTipo === 'number' ? classOf(cbteTipo) : null;
|
|
26
|
+
if (docTipo === 99) {
|
|
27
|
+
return { status: 'auto', code: 5, comment: ' parche: 5 = Consumidor Final (DocTipo 99, sin identificar)' };
|
|
28
|
+
}
|
|
29
|
+
if (docTipo === 96 && (clase === 'B' || clase === 'C')) {
|
|
30
|
+
return { status: 'needs-review', code: 5, comment: ' parche: inferido Consumidor Final (DNI + comprobante ' + clase + ') — REVISAR' };
|
|
31
|
+
}
|
|
32
|
+
const valid = clase ? pack.codes_by_class[clase] : null;
|
|
33
|
+
return { status: 'needs-input', clase, valid };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function validCodesText(decision, pack) {
|
|
37
|
+
return (decision.valid || Object.keys(pack.codes).map(Number))
|
|
38
|
+
.map((c) => `${c} (${pack.codes[String(c)]})`).join(', ');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function missingMessage(decision) {
|
|
42
|
+
return `Falta CondicionIVAReceptorId y no se puede inferir sin saber la condición frente al IVA del cliente${decision.clase ? ` (comprobante clase ${decision.clase})` : ''}.`;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function appliedMessage(status) {
|
|
46
|
+
return status === 'auto'
|
|
47
|
+
? 'Agregado CondicionIVAReceptorId: 5 (Consumidor Final): el comprobante va a un consumidor sin identificar.'
|
|
48
|
+
: 'Agregado CondicionIVAReceptorId: 5 por inferencia (DNI + comprobante B/C). Confirmá que el cliente no sea exento ni no alcanzado.';
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Arreglo parcial "en tiempo de ejecución" (punto 7 del frente B, 16/10): si DocTipo no es un literal
|
|
52
|
+
// (DocTipo: docTipo), la regla 99 → 5 se aplica al ejecutarse y, con otro DocTipo, el pedido sale igual que hoy.
|
|
53
|
+
// No inventa nada: Consumidor Final es el único caso que no depende de datos del negocio. No se aplica si el
|
|
54
|
+
// comprobante es clase A o de otra clase conocida (99 con factura A no existe).
|
|
55
|
+
export const PARTIAL = 'consumidor final';
|
|
56
|
+
export const PARTIAL_COMMENT = ' parche: 5 = Consumidor Final solo si DocTipo es 99; con otro DocTipo falta la condición frente al IVA del cliente';
|
|
57
|
+
|
|
58
|
+
export function canApplyAtRuntime(decision, cbteTipo) {
|
|
59
|
+
return decision.status === 'needs-input' && (cbteTipo === undefined || decision.clase === 'B' || decision.clase === 'C');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const dmy = (iso) => iso.split('-').map(Number).reverse().join('/');
|
|
63
|
+
|
|
64
|
+
export function partialAppliedMessage(docTipoText) {
|
|
65
|
+
return `Arreglo parcial (${PARTIAL}): CondicionIVAReceptorId: 5 solo cuando \`${docTipoText}\` vale 99 al ejecutarse. Con otro DocTipo el pedido sale igual que hoy, sin el campo.`;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Lo que el arreglo parcial NO cubre: va en el cuerpo del PR como "lo que este PR no resuelve".
|
|
69
|
+
export function partialPending(decision, pack, suggestion) {
|
|
70
|
+
return {
|
|
71
|
+
covers: PARTIAL,
|
|
72
|
+
message: `No cubre los comprobantes con DocTipo distinto de 99 (CUIT, DNI y otros): siguen sin CondicionIVAReceptorId y desde el ${dmy(pack.deadline)} ARCA los va a rechazar. Falta la condición frente al IVA de cada cliente${decision.clase ? ` (comprobante clase ${decision.clase})` : ''}.`,
|
|
73
|
+
suggestion,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// En un scan posterior (el PR parcial ya se mergeó): lo que falta se sigue reportando.
|
|
78
|
+
export function partialLeftMessage(pending) {
|
|
79
|
+
return `Tiene el arreglo parcial de Parche (solo DocTipo 99 → 5). ${pending.message}`;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// "El repo ya usa CondicionIVAReceptorId en otro lado": prefijo para bajar a "revisar" lo que no se pudo leer
|
|
83
|
+
// (en repos ya migrados, el objeto suele salir de un builder que sí pone el campo).
|
|
84
|
+
export function alreadyMigratedPrefix(context) {
|
|
85
|
+
const f = context?.fieldFiles?.[0];
|
|
86
|
+
return f ? `El repo ya usa CondicionIVAReceptorId (en \`${f}\`): verificá que este comprobante también lo lleve. ` : null;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Si el campo puede no llegar a ARCA aunque esté en el código: librería 0.x (WSDL de 2021) o un WSDL viejo
|
|
90
|
+
// en el repo. Verificado en el código de afip.js/afip.php el 16/10/2026 (ver src/context.js).
|
|
91
|
+
export function libraryCaveat(context, ecosystem) {
|
|
92
|
+
const lib = (context?.arcaLibs || []).find((l) => l.ecosystem === ecosystem && l.travels !== 'si');
|
|
93
|
+
if (lib) {
|
|
94
|
+
return `Ojo: con ${lib.name} ${lib.version} el pedido se arma con un WSDL de 2021 que no tiene este campo`
|
|
95
|
+
+ `${lib.travels === 'no' ? '' : ' (en JS falta confirmarlo)'}: para que llegue a ARCA hay que actualizar la librería a 1.x.`;
|
|
96
|
+
}
|
|
97
|
+
const wsdl = context?.staleWsdl?.[0];
|
|
98
|
+
if (wsdl) return `Ojo: el repo tiene un WSDL de WSFEv1 sin este campo (\`${wsdl}\`): si el cliente SOAP lo usa, el campo no viaja hasta actualizarlo.`;
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Integraciones SOAP propias (cualquier lenguaje): solo detección.
|
|
103
|
+
// Con menos ruido que la primera versión (frente B, 16/10): no marca tests, mocks, tipos, copias de la librería
|
|
104
|
+
// ni archivos que solo nombran el método (constantes, reexports); solo los que arman el detalle del comprobante.
|
|
105
|
+
export function soapDetection(source, file, context) {
|
|
106
|
+
if (!/FECAESolicitar|FECAEDetRequest/.test(source) || /CondicionIVAReceptorId/.test(source)) return null;
|
|
107
|
+
if (isTestFile(file) || /\.d\.[cm]?ts$/.test(file)) return null;
|
|
108
|
+
if (/class\s+ElectronicBilling\b/.test(source)) return null; // copia de afip.php/afip.js: la cubren la versión y el WSDL
|
|
109
|
+
const generated = /(^|\/)Reference\.cs$|Stub\.java$/i.test(file) || /class\s+FECAEDetRequest\b/.test(source);
|
|
110
|
+
if (!generated && !/\b(DocTipo|CbteDesde|ImpTotal)\b/.test(source)) return null;
|
|
111
|
+
const line = source.split('\n').findIndex((l) => /FECAESolicitar|FECAEDetRequest/.test(l)) + 1;
|
|
112
|
+
const base = generated
|
|
113
|
+
? 'Cliente SOAP de WSFEv1 generado sin CondicionIVAReceptorId: hay que regenerarlo con el WSDL vigente (y después completar el campo).'
|
|
114
|
+
: 'Integración SOAP propia con WSFEv1 sin CondicionIVAReceptorId en el FECAEDetRequest. Hay que agregarlo al XML, con la condición frente al IVA del cliente.';
|
|
115
|
+
const prefix = alreadyMigratedPrefix(context);
|
|
116
|
+
return {
|
|
117
|
+
finding: { pack: PACK, file, line, status: prefix ? 'needs-review' : 'needs-input', message: (prefix || '') + base },
|
|
118
|
+
usage: { provider: 'arca', op: generated ? 'WSFEv1 SOAP (cliente generado)' : 'WSFEv1 SOAP', line },
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Archivos .wsdl de WSFEv1 sin el campo: si el cliente SOAP arma el pedido con ese esquema
|
|
123
|
+
// (SoapClient en modo WSDL, la librería soap de Node), el campo no viaja aunque esté en el código.
|
|
124
|
+
export function wsdlDetection({ source, file }) {
|
|
125
|
+
const findings = [];
|
|
126
|
+
const usages = [];
|
|
127
|
+
if (/FECAESolicitar|FECAEDetRequest/.test(source) && !source.includes('CondicionIVAReceptorId')) {
|
|
128
|
+
const line = source.split('\n').findIndex((l) => /FECAEDetRequest/.test(l)) + 1 || 1;
|
|
129
|
+
findings.push({ pack: PACK, file, line, status: 'needs-input',
|
|
130
|
+
message: 'WSDL de WSFEv1 sin CondicionIVAReceptorId: si tu cliente SOAP arma el pedido con este archivo, el campo no viaja aunque lo agregues al código. Reemplazalo por el WSDL vigente de WSFEv1.' });
|
|
131
|
+
usages.push({ provider: 'arca', op: 'WSFEv1 WSDL', line });
|
|
132
|
+
}
|
|
133
|
+
return { output: null, findings, usages };
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Lenguajes sin codemod (Python, C#, Java, Go, VB.NET): solo la detección de SOAP propio o cliente generado.
|
|
137
|
+
export function soapOnly({ source, file, context }) {
|
|
138
|
+
const soap = soapDetection(source, file, context);
|
|
139
|
+
return { output: null, findings: soap ? [soap.finding] : [], usages: soap ? [soap.usage] : [] };
|
|
140
|
+
}
|