mercadopublico 0.0.2 → 0.1.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 +29 -0
- package/LICENSE +133 -21
- package/README.md +156 -9
- package/dist/chunk-EZCMRQY6.js +75 -0
- package/dist/cli.js +3227 -0
- package/dist/index.cjs +2539 -0
- package/dist/index.d.cts +857 -0
- package/dist/index.d.ts +857 -0
- package/dist/index.js +2424 -0
- package/dist/modelo-D4yVym9O.d.cts +106 -0
- package/dist/modelo-D4yVym9O.d.ts +106 -0
- package/dist/radar.cjs +520 -0
- package/dist/radar.d.cts +264 -0
- package/dist/radar.d.ts +264 -0
- package/dist/radar.js +461 -0
- package/docs/assets/readme/kerokero.svg +1 -0
- package/docs/assets/readme/modelo.png +0 -0
- package/docs/assets/readme/portada.png +0 -0
- package/docs/assets/readme/radar.png +0 -0
- package/docs/assets/readme/resiliencia.png +0 -0
- package/package.json +65 -5
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# mercadopublico
|
|
2
|
+
|
|
3
|
+
## 0.1.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 1b30195: Primera versión pública (0.1).
|
|
8
|
+
|
|
9
|
+
- **Cliente tipado** para la API v1 (licitaciones, órdenes de compra, `BuscarProveedor` y `BuscarComprador`) y la v2 de Compra Ágil (listado paginado y detalle), con errores tipados (`MercadoPublicoError`) y validación de entradas antes de gastar consultas.
|
|
10
|
+
- **Un solo modelo, `Proceso`,** para v1, v2 y OCDS:
|
|
11
|
+
- fechas ISO con offset de Santiago;
|
|
12
|
+
- montos `{ valor, moneda, clp, fechaValor, fuente }` convertidos con el valor del día del documento: mindicador.cl, luego el Banco Central y al final la paridad mensual de ChileCompra.
|
|
13
|
+
- **Resiliencia:**
|
|
14
|
+
- cola serial con ritmo adaptativo ante el 10500;
|
|
15
|
+
- reintentos con presupuesto;
|
|
16
|
+
- circuit breaker por fuente (`mp.salud()`);
|
|
17
|
+
- cancelación con `AbortSignal`;
|
|
18
|
+
- eventos de observabilidad (`alEvento`);
|
|
19
|
+
- cuota diaria en `mp.cuota`.
|
|
20
|
+
- **`mp.sync.planificar` y `mp.sync.ejecutar`:**
|
|
21
|
+
- sincronización incremental con costo estimado antes de empezar;
|
|
22
|
+
- cursores que nunca adelantan a los datos y pendientes con tope;
|
|
23
|
+
- stores en memoria y en archivo, con lock y escritura atómica.
|
|
24
|
+
- **Avisos cuando la API cambia de forma** (campo nuevo o faltante, tipo distinto, valor desconocido).
|
|
25
|
+
- **Lector OCDS en streaming** (`leerOcds`) para el histórico de datos abiertos.
|
|
26
|
+
- **Tablas de códigos** con las contradicciones de la documentación oficial corregidas.
|
|
27
|
+
- **Datos personales de funcionarios** marcados en `personales`. `ocultarPersonales` los tapa, junto con correos y teléfonos en texto libre.
|
|
28
|
+
- **CLI `mercadopublico`** con salida JSON, CSV y tabla, `sync`, `radar` y `ocds`. Oculta datos personales por defecto.
|
|
29
|
+
- **`mercadopublico/radar`:** `decidir`, `priorizar`, `precioSugerido`, `rangoDePrecio`, `proximasCompras`, `competidores` y `calibrar`, con fórmulas explícitas.
|
package/LICENSE
CHANGED
|
@@ -1,21 +1,133 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
to
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
1
|
+
# PolyForm Noncommercial License 1.0.0
|
|
2
|
+
|
|
3
|
+
<https://polyformproject.org/licenses/noncommercial/1.0.0>
|
|
4
|
+
|
|
5
|
+
## Acceptance
|
|
6
|
+
|
|
7
|
+
In order to get any license under these terms, you must agree
|
|
8
|
+
to them as both strict obligations and conditions to all
|
|
9
|
+
your licenses.
|
|
10
|
+
|
|
11
|
+
## Copyright License
|
|
12
|
+
|
|
13
|
+
The licensor grants you a copyright license for the
|
|
14
|
+
software to do everything you might do with the software
|
|
15
|
+
that would otherwise infringe the licensor's copyright
|
|
16
|
+
in it for any permitted purpose. However, you may
|
|
17
|
+
only distribute the software according to [Distribution
|
|
18
|
+
License](#distribution-license) and make changes or new works
|
|
19
|
+
based on the software according to [Changes and New Works
|
|
20
|
+
License](#changes-and-new-works-license).
|
|
21
|
+
|
|
22
|
+
## Distribution License
|
|
23
|
+
|
|
24
|
+
The licensor grants you an additional copyright license
|
|
25
|
+
to distribute copies of the software. Your license
|
|
26
|
+
to distribute covers distributing the software with
|
|
27
|
+
changes and new works permitted by [Changes and New Works
|
|
28
|
+
License](#changes-and-new-works-license).
|
|
29
|
+
|
|
30
|
+
## Notices
|
|
31
|
+
|
|
32
|
+
You must ensure that anyone who gets a copy of any part of
|
|
33
|
+
the software from you also gets a copy of these terms or the
|
|
34
|
+
URL for them above, as well as copies of any plain-text lines
|
|
35
|
+
beginning with `Required Notice:` that the licensor provided
|
|
36
|
+
with the software. For example:
|
|
37
|
+
|
|
38
|
+
> Required Notice: Copyright Yoyodyne, Inc. (http://example.com)
|
|
39
|
+
|
|
40
|
+
## Changes and New Works License
|
|
41
|
+
|
|
42
|
+
The licensor grants you an additional copyright license to
|
|
43
|
+
make changes and new works based on the software for any
|
|
44
|
+
permitted purpose.
|
|
45
|
+
|
|
46
|
+
## Patent License
|
|
47
|
+
|
|
48
|
+
The licensor grants you a patent license for the software that
|
|
49
|
+
covers patent claims the licensor can license, or becomes able
|
|
50
|
+
to license, that you would infringe by using the software.
|
|
51
|
+
|
|
52
|
+
## Noncommercial Purposes
|
|
53
|
+
|
|
54
|
+
Any noncommercial purpose is a permitted purpose.
|
|
55
|
+
|
|
56
|
+
## Personal Uses
|
|
57
|
+
|
|
58
|
+
Personal use for research, experiment, and testing for
|
|
59
|
+
the benefit of public knowledge, personal study, private
|
|
60
|
+
entertainment, hobby projects, amateur pursuits, or religious
|
|
61
|
+
observance, without any anticipated commercial application,
|
|
62
|
+
is use for a permitted purpose.
|
|
63
|
+
|
|
64
|
+
## Noncommercial Organizations
|
|
65
|
+
|
|
66
|
+
Use by any charitable organization, educational institution,
|
|
67
|
+
public research organization, public safety or health
|
|
68
|
+
organization, environmental protection organization,
|
|
69
|
+
or government institution is use for a permitted purpose
|
|
70
|
+
regardless of the source of funding or obligations resulting
|
|
71
|
+
from the funding.
|
|
72
|
+
|
|
73
|
+
## Fair Use
|
|
74
|
+
|
|
75
|
+
You may have "fair use" rights for the software under the
|
|
76
|
+
law. These terms do not limit them.
|
|
77
|
+
|
|
78
|
+
## No Other Rights
|
|
79
|
+
|
|
80
|
+
These terms do not allow you to sublicense or transfer any of
|
|
81
|
+
your licenses to anyone else, or prevent the licensor from
|
|
82
|
+
granting licenses to anyone else. These terms do not imply
|
|
83
|
+
any other licenses.
|
|
84
|
+
|
|
85
|
+
## Patent Defense
|
|
86
|
+
|
|
87
|
+
If you make any written claim that the software infringes or
|
|
88
|
+
contributes to infringement of any patent, your patent license
|
|
89
|
+
for the software granted under these terms ends immediately. If
|
|
90
|
+
your company makes such a claim, your patent license ends
|
|
91
|
+
immediately for work on behalf of your company.
|
|
92
|
+
|
|
93
|
+
## Violations
|
|
94
|
+
|
|
95
|
+
The first time you are notified in writing that you have
|
|
96
|
+
violated any of these terms, or done anything with the software
|
|
97
|
+
not covered by your licenses, your licenses can nonetheless
|
|
98
|
+
continue if you come into full compliance with these terms,
|
|
99
|
+
and take practical steps to correct past violations, within
|
|
100
|
+
32 days of receiving notice. Otherwise, all your licenses
|
|
101
|
+
end immediately.
|
|
102
|
+
|
|
103
|
+
## No Liability
|
|
104
|
+
|
|
105
|
+
***As far as the law allows, the software comes as is, without
|
|
106
|
+
any warranty or condition, and the licensor will not be liable
|
|
107
|
+
to you for any damages arising out of these terms or the use
|
|
108
|
+
or nature of the software, under any kind of legal claim.***
|
|
109
|
+
|
|
110
|
+
## Definitions
|
|
111
|
+
|
|
112
|
+
The **licensor** is the individual or entity offering these
|
|
113
|
+
terms, and the **software** is the software the licensor makes
|
|
114
|
+
available under these terms.
|
|
115
|
+
|
|
116
|
+
**You** refers to the individual or entity agreeing to these
|
|
117
|
+
terms.
|
|
118
|
+
|
|
119
|
+
**Your company** is any legal entity, sole proprietorship,
|
|
120
|
+
or other kind of organization that you work for, plus all
|
|
121
|
+
organizations that have control over, are under the control of,
|
|
122
|
+
or are under common control with that organization. **Control**
|
|
123
|
+
means ownership of substantially all the assets of an entity,
|
|
124
|
+
or the power to direct its management and policies by vote,
|
|
125
|
+
contract, or otherwise. Control can be direct or indirect.
|
|
126
|
+
|
|
127
|
+
**Your licenses** are all the licenses granted to you for the
|
|
128
|
+
software under these terms.
|
|
129
|
+
|
|
130
|
+
**Use** means anything you do with the software requiring one
|
|
131
|
+
of your licenses.
|
|
132
|
+
|
|
133
|
+
Required Notice: Copyright KeroKero (https://kerokero.cl)
|
package/README.md
CHANGED
|
@@ -1,20 +1,167 @@
|
|
|
1
|
-
|
|
1
|
+

|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://github.com/keroke-ro/mercadopublico/actions/workflows/ci.yml)
|
|
4
|
+
[](https://github.com/keroke-ro/mercadopublico/actions/workflows/contrato.yml)
|
|
5
|
+
[](https://www.npmjs.com/package/mercadopublico)
|
|
6
|
+
[](package.json)
|
|
7
|
+
[](LICENSE)
|
|
4
8
|
|
|
5
|
-
|
|
9
|
+
SDK en TypeScript para la [API de Mercado Público](https://www.chilecompra.cl/api/) de ChileCompra. Junta la API v1 (licitaciones, órdenes de compra y empresas), la v2 de Compra Ágil y las descargas OCDS en un solo modelo, `Proceso`, para que tu código no dependa de cuál de las tres trajo el dato. Se hace cargo de lo que cuesta usar bien la API: manda las consultas de a una para no gatillar el error 10500, reintenta solo cuando sirve, lleva la cuenta de las 10.000 consultas diarias del ticket, convierte los montos a pesos con el valor del día del documento y oculta los datos personales de funcionarios en la CLI. No tiene dependencias en runtime.
|
|
6
10
|
|
|
7
|
-
|
|
11
|
+

|
|
8
12
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
+
> No oficial: no está afiliado a la Dirección ChileCompra.
|
|
14
|
+
|
|
15
|
+
## Instalación
|
|
16
|
+
|
|
17
|
+
```sh
|
|
18
|
+
npm install mercadopublico
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Requiere Node 20 o superior (usa `fetch` nativo). Se publica como ESM y CJS. Necesitas un ticket de la API: se pide con Clave Única en [chilecompra.cl/api](https://www.chilecompra.cl/api/) y llega por correo.
|
|
22
|
+
|
|
23
|
+
## En 30 segundos
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
import { MercadoPublico } from "mercadopublico";
|
|
27
|
+
|
|
28
|
+
const mp = new MercadoPublico({ ticket: process.env.MP_TICKET! });
|
|
29
|
+
|
|
30
|
+
const activas = await mp.licitaciones.activas(); // 1 consulta
|
|
31
|
+
console.log(activas.length, "licitaciones publicadas hoy");
|
|
32
|
+
|
|
33
|
+
const lic = await mp.licitaciones.porCodigo("1509-5-L114"); // 1 consulta; null si no existe
|
|
34
|
+
if (lic) {
|
|
35
|
+
console.log(lic.estado, lic.fechas.cierre); // "publicada" "2026-10-20T15:00:00-03:00"
|
|
36
|
+
console.log(lic.monto); // { valor: 1200, moneda: "CLF", clp: 47_000_000, fechaValor: "2026-10-01", fuente: "mindicador.cl" }
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Compra Ágil: pagina sola, 1 consulta por página de 50
|
|
40
|
+
for await (const ca of mp.compraAgil.listar({ region: [13], estado: ["publicada"], q: "papel" })) {
|
|
41
|
+
console.log(ca.codigo, ca.nombre, ca.monto?.clp);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
console.log(mp.cuota.toJSON()); // { dia, usadas, limite: 10000, restante, reinicia }
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Cuota
|
|
48
|
+
|
|
49
|
+
El ticket da 10.000 consultas por día calendario. Si mandas dos a la vez, la API responde 10500; si se te acaban, 429. El SDK:
|
|
50
|
+
|
|
51
|
+
- **Pone todas las consultas en una cola serial.** Nunca hay dos en vuelo, aunque llames en paralelo.
|
|
52
|
+
- **Reintenta solo lo que tiene arreglo:** 10500, 5xx y cortes de red, con backoff exponencial y jitter (4 reintentos, base 1 s). Nunca reintenta un ticket inválido, un 404, parámetros malos ni un 429.
|
|
53
|
+
- **Lleva la cuenta en `mp.cuota`** (`usadas`, `restante`, `limite`, `reinicia`). Cada intento cuenta, también los reintentos. La API no informa cuánto queda, así que es la cuenta de este proceso: si quieres que sobreviva entre ejecuciones, guarda `mp.cuota.toJSON()` y pásalo como `cuota` al crear el cliente. La CLI ya lo hace.
|
|
54
|
+
- **Falla antes de gastar** con `cuota_insuficiente` si una operación no cabe en lo que queda del día.
|
|
55
|
+
- **Planifica los sync.** `mp.sync.planificar()` dice cuántas consultas va a costar y si alcanzan hoy, antes de pedir un solo detalle:
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
const plan = await mp.sync.planificar({ desde: "2026-10-01", store });
|
|
59
|
+
plan.consultas; // 1240
|
|
60
|
+
plan.cabeHoy; // true
|
|
61
|
+
await mp.sync.ejecutar(plan);
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Detalle en [docs/sync.md](docs/sync.md).
|
|
65
|
+
|
|
66
|
+
## Resiliencia
|
|
67
|
+
|
|
68
|
+
La API falla seguido: rechaza con 10500 incluso consultas seriales (3 de cada 11 con 1 s de pausa) y a veces se cae entera. El SDK alarga la pausa sola ante cada 10500, pone un tope a los reintentos, deja de consultar una fuente caída por 60 s (circuit breaker) y acepta un `signal` en cada método. Un sync cortado en cualquier punto se reanuda sin huecos.
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
const mp = new MercadoPublico({
|
|
72
|
+
ticket: process.env.MP_TICKET!,
|
|
73
|
+
alEvento: (e) => console.error(JSON.stringify(e)), // consultas, reintentos, circuito, cuota, avisos de cambios en la API
|
|
74
|
+
});
|
|
75
|
+
mp.salud(); // { v1: { estado: "cerrado", fallas: 0, intervaloMs: 1000 }, v2: { … }, … }
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+

|
|
79
|
+
|
|
80
|
+
Qué pasa en cada falla, en [docs/resiliencia.md](docs/resiliencia.md).
|
|
81
|
+
|
|
82
|
+
## Datos personales
|
|
83
|
+
|
|
84
|
+
La v1 trae nombre, RUT, correo y teléfono de funcionarios (`RutUsuario`, `NombreUsuario`, `EmailResponsablePago`, `FonoResponsableContrato`, etc.). El SDK los separa en `proceso.personales`, y:
|
|
85
|
+
|
|
86
|
+
- **La CLI los oculta por defecto.** Para verlos: `--incluir-personales`.
|
|
87
|
+
- **En código los recibes tal como vienen.** Antes de exportar o publicar, pasa cada proceso por `ocultarPersonales`, que deja `personales` en `null` y tapa esos campos dentro de `raw`:
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
import { ocultarPersonales } from "mercadopublico";
|
|
91
|
+
|
|
92
|
+
const publicable = procesos.map(ocultarPersonales);
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
No se tapan el RUT ni la razón social de los proveedores: son datos de empresas, salvo cuando el proveedor es persona natural, y la API no lo indica. La Ley 21.719 de protección de datos personales empieza a regir en diciembre de 2026; si vas a publicar o cruzar estos datos, confirma el alcance con un abogado.
|
|
96
|
+
|
|
97
|
+
## Radar
|
|
98
|
+
|
|
99
|
+
`mercadopublico/radar` responde lo que se pregunta cada mañana un equipo que le vende al Estado: qué postular primero, a qué precio, contra quién compite y qué viene. Son fórmulas a la vista, sin machine learning: cada número trae su porqué en texto, y tu criterio (pesos, rubros, costos, vetos) entra en un `Perfil`.
|
|
100
|
+
|
|
101
|
+

|
|
102
|
+
|
|
103
|
+
```sh
|
|
104
|
+
npx mercadopublico radar --perfil perfil.json --decisiones decisiones.json
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Detalle en [docs/radar.md](docs/radar.md).
|
|
108
|
+
|
|
109
|
+
## CLI
|
|
110
|
+
|
|
111
|
+
```sh
|
|
112
|
+
export MP_TICKET=...
|
|
113
|
+
npx mercadopublico licitacion 1509-5-L114
|
|
114
|
+
npx mercadopublico licitaciones --dia 2026-10-01 --estado adjudicada --formato csv > adjudicadas.csv
|
|
115
|
+
npx mercadopublico compra-agil --region 13 --estado publicada --q papel --max 50
|
|
116
|
+
npx mercadopublico ocds 2026 --max 10 # no pide ticket
|
|
117
|
+
npx mercadopublico sync --desde 2026-10-01 --plan
|
|
118
|
+
npx mercadopublico cuota
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Salida en tabla si es una terminal y JSON si no; `--formato json|csv|tabla` para elegir. Referencia completa en [docs/cli.md](docs/cli.md).
|
|
122
|
+
|
|
123
|
+
## Documentación
|
|
124
|
+
|
|
125
|
+
- [Guía](docs/guia.md): cliente, licitaciones, órdenes de compra, empresas, Compra Ágil y errores.
|
|
126
|
+
- [Modelo](docs/modelo.md): `Proceso`, `Monto`, fechas y conversores.
|
|
127
|
+
- [Sync](docs/sync.md): plan, costo en consultas, cursores y stores.
|
|
128
|
+
- [CLI](docs/cli.md)
|
|
129
|
+
- [OCDS](docs/ocds.md): el histórico de datos abiertos, en streaming.
|
|
130
|
+
- [Radar](docs/radar.md): `mercadopublico/radar` y `mercadopublico radar`: qué postular primero, a qué precio, contra quién y qué viene.
|
|
131
|
+
- [Resiliencia](docs/resiliencia.md): qué hace el SDK cuando la API falla, eventos y `mp.salud()`.
|
|
132
|
+
- [Testing](docs/testing.md): qué se prueba, dónde corre y cuántas consultas reales gasta.
|
|
133
|
+
- [Contrato nocturno](docs/contrato.md)
|
|
134
|
+
- [Notas de la API](docs/api-notes.md): lo que dice cada fuente oficial y dónde se contradicen.
|
|
135
|
+
|
|
136
|
+
## Calidad
|
|
137
|
+
|
|
138
|
+
Cada PR corre, en Node 20, 22 y 24, typecheck, tests unitarios, propiedades con fast-check, un corpus de 59 respuestas reales anonimizadas con snapshots y pruebas de caos contra un servidor local; además, cobertura con umbrales y el paquete tal como se publica, importado desde Node, Bun, Deno y un entorno tipo edge. Cada semana corren la prueba de mutación y un smoke contra la API real; cada noche, el [contrato](docs/contrato.md). Detalle en [docs/testing.md](docs/testing.md).
|
|
139
|
+
|
|
140
|
+
## Estado
|
|
141
|
+
|
|
142
|
+
Versión 0.x: la API pública todavía puede cambiar entre versiones menores (ver [CONTRIBUTING.md](CONTRIBUTING.md#versiones)).
|
|
143
|
+
|
|
144
|
+
Probado con un ticket real desde el 2026-10-13: la v1 tiene fixtures grabados de la API (`test/fixtures/v1/*.real.json`) y un corpus de respuestas reales anonimizadas (ver [docs/testing.md](docs/testing.md#corpus-real-y-snapshots)). Los fixtures de la v2 y de OCDS siguen reconstruidos desde la documentación oficial y llevan la marca `"_fixture": "reconstruido…"`. Estas dudas siguen abiertas (detalle en [docs/api-notes.md](docs/api-notes.md)):
|
|
145
|
+
|
|
146
|
+
- En qué zona horaria se reinicia la cuota (el SDK supone medianoche de Santiago) y si v1 y v2 comparten contador.
|
|
147
|
+
- Si el servidor acepta la grafía correcta de dos filtros de estado de OC que la documentación escribe con errata (`recepcionaceptadacialmente`, `recepecionconformeincompleta`). El SDK manda la de la documentación.
|
|
148
|
+
- Detalles de forma: si `Publico` en las rutas de Empresas distingue mayúsculas, si el filtro `region` de la v2 va separado por comas o repetido, y con qué status HTTP llega el error 10300.
|
|
149
|
+
- Si las fechas OCDS con sufijo `Z` son UTC reales o la hora de Santiago etiquetada como UTC. El SDK las lee como hora de Santiago.
|
|
150
|
+
- En Compra Ágil, `codigo_orden_compra` viene en `null` aunque la OC exista (el SDK usa `id_orden_compra`), y `seleccion.proveedor_seleccionado` está documentado pero no confirmado.
|
|
151
|
+
- mindicador.cl no respondía al investigar; si falla, el conversor usa los valores diarios del Banco Central (sus páginas "Ver serie", no una API) y, si eso también falla, el promedio mensual oficial de ChileCompra.
|
|
13
152
|
|
|
14
153
|
## Fuente de los datos
|
|
15
154
|
|
|
16
155
|
Los datos vienen de la Dirección ChileCompra (api.mercadopublico.cl). Si publicas datos obtenidos con esta librería, cita a ChileCompra como fuente.
|
|
17
156
|
|
|
157
|
+
Esta librería no está afiliada a la Dirección ChileCompra ni cuenta con su respaldo.
|
|
158
|
+
|
|
18
159
|
## Licencia
|
|
19
160
|
|
|
20
|
-
|
|
161
|
+
[PolyForm Noncommercial 1.0.0](LICENSE): libre para uso no comercial (personal, investigación, educación, organizaciones sin fines de lucro e instituciones públicas). Para uso comercial, escríbenos a [KeroKero](https://kerokero.cl).
|
|
162
|
+
|
|
163
|
+
Required Notice: Copyright KeroKero (https://kerokero.cl)
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
<img src="https://unpkg.com/mercadopublico/docs/assets/readme/kerokero.svg" width="18" height="18" alt=""> Hecho por [KeroKero](https://kerokero.cl): sistemas de decisión para tu operación.
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __export = (target, all) => {
|
|
3
|
+
for (var name in all)
|
|
4
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
// src/fecha.ts
|
|
8
|
+
var ZONA = "America/Santiago";
|
|
9
|
+
var partes = new Intl.DateTimeFormat("en-CA", {
|
|
10
|
+
timeZone: ZONA,
|
|
11
|
+
year: "numeric",
|
|
12
|
+
month: "2-digit",
|
|
13
|
+
day: "2-digit",
|
|
14
|
+
hour: "2-digit",
|
|
15
|
+
minute: "2-digit",
|
|
16
|
+
second: "2-digit",
|
|
17
|
+
hourCycle: "h23",
|
|
18
|
+
timeZoneName: "longOffset"
|
|
19
|
+
});
|
|
20
|
+
function piezas(d) {
|
|
21
|
+
const p = {};
|
|
22
|
+
for (const { type, value } of partes.formatToParts(d)) p[type] = value;
|
|
23
|
+
const gmt = p.timeZoneName === "GMT" ? "+00:00" : p.timeZoneName.slice(3);
|
|
24
|
+
const ms = (gmt[0] === "-" ? -1 : 1) * (Number(gmt.slice(1, 3)) * 3600 + Number(gmt.slice(4, 6)) * 60 + Number(gmt.slice(7, 9) || 0)) * 1e3;
|
|
25
|
+
return { dia: `${p.year}-${p.month}-${p.day}`, hora: `${p.hour}:${p.minute}:${p.second}`, off: gmt.slice(0, 6), ms };
|
|
26
|
+
}
|
|
27
|
+
function isoSantiago(d) {
|
|
28
|
+
const { dia, hora, off } = piezas(d);
|
|
29
|
+
return `${dia}T${hora}${off}`;
|
|
30
|
+
}
|
|
31
|
+
function diaSantiago(d = /* @__PURE__ */ new Date()) {
|
|
32
|
+
return piezas(d).dia;
|
|
33
|
+
}
|
|
34
|
+
var FORMA = /^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}(:\d{2}(\.\d+)?)?(Z|[+-]\d{2}:?\d{2})?)?$/;
|
|
35
|
+
function desdeHoraSantiago(texto) {
|
|
36
|
+
if (!FORMA.test(texto)) throw new RangeError(`Fecha inv\xE1lida: ${texto.slice(0, 40)}`);
|
|
37
|
+
if (/(Z|[+-]\d{2}:?\d{2})$/.test(texto)) return new Date(texto);
|
|
38
|
+
const comoUtc = /* @__PURE__ */ new Date(`${texto.length === 10 ? `${texto}T00:00:00` : texto}Z`);
|
|
39
|
+
if (Number.isNaN(comoUtc.getTime())) throw new RangeError(`Fecha inv\xE1lida: ${texto}`);
|
|
40
|
+
const pared = comoUtc.toISOString().slice(0, 19);
|
|
41
|
+
const candidatos = [-1, 1].map((d) => comoUtc.getTime() - piezas(new Date(comoUtc.getTime() + d * 864e5)).ms);
|
|
42
|
+
const t = candidatos.find((c) => {
|
|
43
|
+
const p = piezas(new Date(c));
|
|
44
|
+
return `${p.dia}T${p.hora}` === pared;
|
|
45
|
+
});
|
|
46
|
+
return new Date(t ?? candidatos[0]);
|
|
47
|
+
}
|
|
48
|
+
function normalizarFecha(texto) {
|
|
49
|
+
if (!texto) return null;
|
|
50
|
+
if (/^\d{4}/.test(texto) && Number(texto.slice(0, 4)) < 1990) return null;
|
|
51
|
+
return isoSantiago(desdeHoraSantiago(texto));
|
|
52
|
+
}
|
|
53
|
+
function ddmmaaaa(dia) {
|
|
54
|
+
const [a, m, d] = dia.split("-");
|
|
55
|
+
return `${d}${m}${a}`;
|
|
56
|
+
}
|
|
57
|
+
function sumarDias(dia, n) {
|
|
58
|
+
const d = /* @__PURE__ */ new Date(`${dia}T12:00:00Z`);
|
|
59
|
+
d.setUTCDate(d.getUTCDate() + n);
|
|
60
|
+
return d.toISOString().slice(0, 10);
|
|
61
|
+
}
|
|
62
|
+
function proximaMedianoche(ahora = /* @__PURE__ */ new Date()) {
|
|
63
|
+
return isoSantiago(desdeHoraSantiago(sumarDias(diaSantiago(ahora), 1)));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export {
|
|
67
|
+
__export,
|
|
68
|
+
isoSantiago,
|
|
69
|
+
diaSantiago,
|
|
70
|
+
desdeHoraSantiago,
|
|
71
|
+
normalizarFecha,
|
|
72
|
+
ddmmaaaa,
|
|
73
|
+
sumarDias,
|
|
74
|
+
proximaMedianoche
|
|
75
|
+
};
|