@hostwebhook/node-types 1.88.0 → 1.90.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.
@@ -80,6 +80,8 @@ export { TOOLKITS_POR_TIPO, operacionDeToolkit, esOperacionDestructiva, TIPOS_CO
80
80
  export { GOOGLE_CONTACTS_TOOLKIT_SPECS, GOOGLE_CONTACTS_TOOLKIT_BY_TOOL_NAME, } from './google-contacts-toolkit.js';
81
81
  export type { GoogleContactsToolkitSpec, GoogleContactsToolkitParameter, } from './google-contacts-toolkit.js';
82
82
  export type { OperacionDeToolkit } from './toolkits.js';
83
+ export { leerSslPostgres, ponerSslPostgres, pistaDeSslPostgres, conPistaDeSslPostgres, CASOS_DE_SSL_POSTGRES, FICHEROS_DE_POSTGRES, parametrosDeCadenaPostgres, ficherosEnCadenaPostgres, CASOS_DE_FICHEROS_POSTGRES, } from './postgres-ssl.js';
84
+ export type { ModoSslPostgres, SslDeLaCadenaPostgres } from './postgres-ssl.js';
83
85
  export type { LlmProvider, LlmProviderOption, LlmModelOption, } from './llm-models.js';
84
86
  export { LLM_PROVIDERS, LLM_MODELS, MODEL_CONTEXT_WINDOWS, getModelsFor, getDefaultModel, getModelLabel, } from './llm-models.js';
85
87
  export type { ModeloDeOpenRouter, PrecioDeOpenRouter, RespuestaDeModelosDeOpenRouter, } from './openrouter.js';
package/dist/esm/index.js CHANGED
@@ -62,6 +62,11 @@ export { TOOLKITS_POR_TIPO, operacionDeToolkit, esOperacionDestructiva, TIPOS_CO
62
62
  llama `…_TOOLKIT_SPECS` porque `GOOGLE_CONTACTS_OPERATION_SPECS` ya es el
63
63
  formulario del nodo — la misma pareja que Discord y Slack. */
64
64
  export { GOOGLE_CONTACTS_TOOLKIT_SPECS, GOOGLE_CONTACTS_TOOLKIT_BY_TOOL_NAME, } from './google-contacts-toolkit.js';
65
+ // ── El SSL de una cadena de Postgres (dashboard, api y hw-nodes) ──
66
+ /* Lo que hará `pg` con el SSL de una cadena, escrito una vez: el panel lo usa
67
+ para fijar el modo cuando la cadena lo dice, y los servicios para decir qué
68
+ cambiar cuando falla. El api lo compara con el driver de verdad. */
69
+ export { leerSslPostgres, ponerSslPostgres, pistaDeSslPostgres, conPistaDeSslPostgres, CASOS_DE_SSL_POSTGRES, FICHEROS_DE_POSTGRES, parametrosDeCadenaPostgres, ficherosEnCadenaPostgres, CASOS_DE_FICHEROS_POSTGRES, } from './postgres-ssl.js';
65
70
  export { LLM_PROVIDERS, LLM_MODELS, MODEL_CONTEXT_WINDOWS, getModelsFor, getDefaultModel, getModelLabel, } from './llm-models.js';
66
71
  export { URL_DE_MODELOS_DE_OPENROUTER, opcionesDeModelosDeOpenRouter, ventanaDeContextoDeOpenRouter, } from './openrouter.js';
67
72
  export { CREDENTIAL_TYPES, CREDENTIAL_TYPE_VALUES, credentialTypeValues, getCredentialType, isCredentialType, } from './credentials.js';
@@ -0,0 +1,163 @@
1
+ /**
2
+ * El SSL de una cadena de conexión de Postgres: qué hará el driver con ella,
3
+ * y cómo escribir el modo que se elija.
4
+ *
5
+ * ## Por qué vive aquí (2026-09-24)
6
+ *
7
+ * Ariel conectó un Postgres de Railway eligiendo «Require» y el panel del nodo
8
+ * dijo `self-signed certificate in certificate chain`. Medido: el servidor
9
+ * enseña un certificado `CN=localhost` firmado por su propio `root-ca`, y
10
+ * «Require» escribía `sslmode=require`, que para `pg` 8 es VERIFICAR el
11
+ * certificado. Y pidió que, si la cadena ya trae el SSL, el panel lo lea y lo
12
+ * deje fijo; y si no lo trae, que se elija a mano.
13
+ *
14
+ * Para fijarlo, el panel tiene que saber lo que hará EL DRIVER, no lo que
15
+ * parece que dice la cadena — y el driver tiene reglas que no se adivinan:
16
+ * `sslmode=prefer` también verifica, `ssl=false` ENCIENDE el SSL (queda como
17
+ * el texto `"false"`, que es verdadero), y `uselibpqcompat=true` le da la
18
+ * vuelta a `require`. El panel sólo conocía `require` y `no-verify`; lo demás
19
+ * lo pintaba como «Off».
20
+ *
21
+ * Una sola función para el dashboard y para los servicios, y el api la compara
22
+ * caso a caso con el `pg` que de verdad corre (`el-ssl-de-postgres-es-el-del-
23
+ * driver.spec.ts`): si una actualización de `pg` cambia sus reglas, ese spec
24
+ * se pone rojo antes de que el panel enseñe una cosa y el driver haga otra.
25
+ *
26
+ * ## Las reglas, copiadas de `pg-connection-string` 2.12 y `pg` 8.20
27
+ *
28
+ * 1. Los parámetros salen de `new URL(cadena, 'postgres://base')` (con el
29
+ * mismo arreglo de espacios y el mismo host de relleno para `@/`); si una
30
+ * clave se repite, gana la ÚLTIMA. Las claves distinguen mayúsculas:
31
+ * `SSLMODE` no existe para el driver.
32
+ * 2. `ssl=true`/`ssl=1` → sí; `ssl=0` → no; cualquier otro texto se queda
33
+ * como texto (`ssl=false` → `"false"` → ENCENDIDO).
34
+ * 3. Si hay `sslmode` (no vacío) o un fichero (`sslcert`, `sslkey`,
35
+ * `sslrootcert`), el SSL pasa a ser un objeto y `ssl=` deja de contar.
36
+ * 4. `sslmode`: `disable` → no; `no-verify` → cifrar sin comprobar; todo lo
37
+ * demás (`prefer`, `require`, `verify-ca`, `verify-full`, y cualquier
38
+ * valor raro) → cifrar Y comprobar el certificado.
39
+ * 5. Con `uselibpqcompat=true`, las de libpq: `prefer` y `require` → sin
40
+ * comprobar; `verify-full` → comprobando; `verify-ca` sin `sslrootcert`
41
+ * → el driver se NIEGA; `no-verify` no existe en libpq → comprobando.
42
+ * 6. Sin nada de eso, `pg` mira `PGSSLMODE` del entorno; en los servicios no
43
+ * está puesta (medido el 2026-09-24), así que es SIN SSL.
44
+ *
45
+ * ## Los ficheros (`sslcert`, `sslkey`, `sslrootcert`)
46
+ *
47
+ * El driver los LEE DEL DISCO de quien conecta — nuestro servidor, no la
48
+ * máquina de quien pegó la cadena. Nunca pueden valer: aquí salen como
49
+ * `problema` y el panel no deja guardarlos.
50
+ */
51
+ /** Los tres modos del selector. `require` es «cifrar y comprobar el
52
+ * certificado» — lo que `pg` 8 hace con `sslmode=require`. */
53
+ export type ModoSslPostgres = 'off' | 'require' | 'no-verify';
54
+ export type SslDeLaCadenaPostgres =
55
+ /** La cadena no dice nada de SSL: se elige a mano. */
56
+ {
57
+ dice: false;
58
+ }
59
+ /** La cadena lo decide. `por` es el parámetro que manda (`sslmode=require`). */
60
+ | {
61
+ dice: true;
62
+ modo: ModoSslPostgres;
63
+ por: string;
64
+ }
65
+ /** La cadena dice algo que no puede funcionar desde nuestros servidores. */
66
+ | {
67
+ dice: true;
68
+ modo: null;
69
+ por: string;
70
+ problema: string;
71
+ };
72
+ /**
73
+ * Los parámetros que `pg-connection-string` LEE DEL DISCO con
74
+ * `fs.readFileSync(<valor>)` al construir el cliente (2.12 y 2.14, medido
75
+ * el 2026-09-24). El api y hw-nodes comprueban contra el fuente del driver
76
+ * que no lee ninguno más (`ninguna-conexion-postgres-lee-ficheros.spec.ts`).
77
+ */
78
+ export declare const FICHEROS_DE_POSTGRES: readonly ["sslcert", "sslkey", "sslrootcert"];
79
+ /**
80
+ * Los parámetros de la cadena EXACTAMENTE como los lee `pg-connection-string`
81
+ * —mismo arreglo de espacios, mismo host de relleno para `@/`, claves
82
+ * decodificadas, la última repetida gana—. `null` si el driver no llega a
83
+ * leer parámetros: un socket (`/ruta` o `socket:`) o una cadena que ni él
84
+ * puede leer.
85
+ *
86
+ * ⚠️ SIN recortar: la usa una guarda de seguridad
87
+ * (`asegurarPostgresSinFicheros` en node-sdk). Con `" /x?sslrootcert=…"`, un
88
+ * recorte vería un socket y dejaría pasar lo que el driver sí lee.
89
+ */
90
+ export declare function parametrosDeCadenaPostgres(cadena: string): Record<string, string> | null;
91
+ /**
92
+ * Qué hará el driver con el SSL de esta cadena.
93
+ *
94
+ * `{ dice: false }` cuando la cadena no dice nada: ni `ssl`, ni `sslmode`, ni
95
+ * ficheros (un `sslmode=` o `ssl=` vacío tampoco dicen nada: el driver los
96
+ * ignora).
97
+ */
98
+ export declare function leerSslPostgres(cadena: string): SslDeLaCadenaPostgres;
99
+ /**
100
+ * Escribe el modo elegido en la cadena, quitando antes `ssl` y `sslmode` (en
101
+ * una cadena que «no dice nada», los que hubiera están vacíos). No toca el
102
+ * resto de parámetros, ni los recodifica.
103
+ *
104
+ * Lo que escribe, y por qué:
105
+ * - `off` → `sslmode=disable`, explícito: la cadena guardada dice lo
106
+ * que hace sin depender de `PGSSLMODE`.
107
+ * - `require` → `sslmode=verify-full`, no `require`: para `pg` 8 es lo
108
+ * mismo, pero `require` avisa en cada conexión de que en la
109
+ * versión 9 pasará a NO comprobar. `verify-full` significa
110
+ * lo mismo en las dos.
111
+ * - `no-verify` → `sslmode=no-verify`; con `uselibpqcompat=true`, que no lo
112
+ * conoce, `sslmode=require` (lo mismo en libpq).
113
+ *
114
+ * Una cadena vacía se devuelve tal cual.
115
+ */
116
+ export declare function ponerSslPostgres(cadena: string, modo: ModoSslPostgres): string;
117
+ /**
118
+ * La frase que le falta a un error de conexión de SSL: qué cambiar. `null` si
119
+ * el error no es de SSL.
120
+ *
121
+ * Los tres casos que se ven al conectar:
122
+ * - el certificado no se puede comprobar (autofirmado, CA privada, nombre
123
+ * que no casa): Railway (`CN=localhost` de su `root-ca`) y Supabase (su
124
+ * `Supabase Root 2021 CA`) —medidos los dos el 2026-09-24—, Heroku y
125
+ * RDS. Neon sí se comprueba (Let's Encrypt);
126
+ * - el servidor no acepta SSL;
127
+ * - el servidor exige SSL y se conectó sin él.
128
+ */
129
+ export declare function pistaDeSslPostgres(mensaje: string): string | null;
130
+ /**
131
+ * Los casos con los que se comprueba `ficherosEnCadenaPostgres` contra el
132
+ * driver de verdad (el api y hw-nodes espían `fs.readFileSync` mientras
133
+ * `pg-connection-string` lee cada cadena): `lee` es si el DRIVER abre un
134
+ * fichero. Incluye los dos rodeos medidos el 2026-09-24 —un espacio delante
135
+ * de `/` (el driver no lo toma por socket y lee) y una clave codificada
136
+ * (`ssl%72ootcert`)— y lo que no lee (mayúsculas, sockets, valor vacío).
137
+ * Las rutas empiezan por `/nope` para que el espía las reconozca.
138
+ */
139
+ export declare const CASOS_DE_FICHEROS_POSTGRES: ReadonlyArray<{
140
+ cadena: string;
141
+ lee: boolean;
142
+ }>;
143
+ /**
144
+ * Los parámetros de la cadena que el driver leería como FICHEROS del disco de
145
+ * quien conecta —nuestros servidores—. Vacío si no hay ninguno. Devuelve los
146
+ * NOMBRES, nunca los valores: una ruta no se repite en un mensaje.
147
+ */
148
+ export declare function ficherosEnCadenaPostgres(cadena: string): string[];
149
+ /** El mensaje con su pista, si la tiene. Lo que devuelven los servicios. Si el
150
+ * error ya pasó por aquí (dos capas que lo envuelven), no la repite. */
151
+ export declare function conPistaDeSslPostgres(mensaje: string): string;
152
+ /**
153
+ * Los casos con los que se comprueba la regla — aquí y contra el driver de
154
+ * verdad en el api (`el-ssl-de-postgres-es-el-del-driver.spec.ts`). Uno nuevo
155
+ * se añade AQUÍ y lo miran los dos.
156
+ *
157
+ * `esperado`: el modo, `'nada'` si la cadena no dice nada, `'problema'` si no
158
+ * puede funcionar.
159
+ */
160
+ export declare const CASOS_DE_SSL_POSTGRES: ReadonlyArray<{
161
+ cadena: string;
162
+ esperado: ModoSslPostgres | 'nada' | 'problema';
163
+ }>;
@@ -0,0 +1,327 @@
1
+ /**
2
+ * El SSL de una cadena de conexión de Postgres: qué hará el driver con ella,
3
+ * y cómo escribir el modo que se elija.
4
+ *
5
+ * ## Por qué vive aquí (2026-09-24)
6
+ *
7
+ * Ariel conectó un Postgres de Railway eligiendo «Require» y el panel del nodo
8
+ * dijo `self-signed certificate in certificate chain`. Medido: el servidor
9
+ * enseña un certificado `CN=localhost` firmado por su propio `root-ca`, y
10
+ * «Require» escribía `sslmode=require`, que para `pg` 8 es VERIFICAR el
11
+ * certificado. Y pidió que, si la cadena ya trae el SSL, el panel lo lea y lo
12
+ * deje fijo; y si no lo trae, que se elija a mano.
13
+ *
14
+ * Para fijarlo, el panel tiene que saber lo que hará EL DRIVER, no lo que
15
+ * parece que dice la cadena — y el driver tiene reglas que no se adivinan:
16
+ * `sslmode=prefer` también verifica, `ssl=false` ENCIENDE el SSL (queda como
17
+ * el texto `"false"`, que es verdadero), y `uselibpqcompat=true` le da la
18
+ * vuelta a `require`. El panel sólo conocía `require` y `no-verify`; lo demás
19
+ * lo pintaba como «Off».
20
+ *
21
+ * Una sola función para el dashboard y para los servicios, y el api la compara
22
+ * caso a caso con el `pg` que de verdad corre (`el-ssl-de-postgres-es-el-del-
23
+ * driver.spec.ts`): si una actualización de `pg` cambia sus reglas, ese spec
24
+ * se pone rojo antes de que el panel enseñe una cosa y el driver haga otra.
25
+ *
26
+ * ## Las reglas, copiadas de `pg-connection-string` 2.12 y `pg` 8.20
27
+ *
28
+ * 1. Los parámetros salen de `new URL(cadena, 'postgres://base')` (con el
29
+ * mismo arreglo de espacios y el mismo host de relleno para `@/`); si una
30
+ * clave se repite, gana la ÚLTIMA. Las claves distinguen mayúsculas:
31
+ * `SSLMODE` no existe para el driver.
32
+ * 2. `ssl=true`/`ssl=1` → sí; `ssl=0` → no; cualquier otro texto se queda
33
+ * como texto (`ssl=false` → `"false"` → ENCENDIDO).
34
+ * 3. Si hay `sslmode` (no vacío) o un fichero (`sslcert`, `sslkey`,
35
+ * `sslrootcert`), el SSL pasa a ser un objeto y `ssl=` deja de contar.
36
+ * 4. `sslmode`: `disable` → no; `no-verify` → cifrar sin comprobar; todo lo
37
+ * demás (`prefer`, `require`, `verify-ca`, `verify-full`, y cualquier
38
+ * valor raro) → cifrar Y comprobar el certificado.
39
+ * 5. Con `uselibpqcompat=true`, las de libpq: `prefer` y `require` → sin
40
+ * comprobar; `verify-full` → comprobando; `verify-ca` sin `sslrootcert`
41
+ * → el driver se NIEGA; `no-verify` no existe en libpq → comprobando.
42
+ * 6. Sin nada de eso, `pg` mira `PGSSLMODE` del entorno; en los servicios no
43
+ * está puesta (medido el 2026-09-24), así que es SIN SSL.
44
+ *
45
+ * ## Los ficheros (`sslcert`, `sslkey`, `sslrootcert`)
46
+ *
47
+ * El driver los LEE DEL DISCO de quien conecta — nuestro servidor, no la
48
+ * máquina de quien pegó la cadena. Nunca pueden valer: aquí salen como
49
+ * `problema` y el panel no deja guardarlos.
50
+ */
51
+ /**
52
+ * Los parámetros que `pg-connection-string` LEE DEL DISCO con
53
+ * `fs.readFileSync(<valor>)` al construir el cliente (2.12 y 2.14, medido
54
+ * el 2026-09-24). El api y hw-nodes comprueban contra el fuente del driver
55
+ * que no lee ninguno más (`ninguna-conexion-postgres-lee-ficheros.spec.ts`).
56
+ */
57
+ export const FICHEROS_DE_POSTGRES = ['sslcert', 'sslkey', 'sslrootcert'];
58
+ const FICHEROS = FICHEROS_DE_POSTGRES;
59
+ /**
60
+ * Los parámetros de la cadena EXACTAMENTE como los lee `pg-connection-string`
61
+ * —mismo arreglo de espacios, mismo host de relleno para `@/`, claves
62
+ * decodificadas, la última repetida gana—. `null` si el driver no llega a
63
+ * leer parámetros: un socket (`/ruta` o `socket:`) o una cadena que ni él
64
+ * puede leer.
65
+ *
66
+ * ⚠️ SIN recortar: la usa una guarda de seguridad
67
+ * (`asegurarPostgresSinFicheros` en node-sdk). Con `" /x?sslrootcert=…"`, un
68
+ * recorte vería un socket y dejaría pasar lo que el driver sí lee.
69
+ */
70
+ export function parametrosDeCadenaPostgres(cadena) {
71
+ let str = cadena ?? '';
72
+ if (!str || str.charAt(0) === '/')
73
+ return null;
74
+ if (/ |%[^a-f0-9]|%[a-f0-9][^a-f0-9]/i.test(str)) {
75
+ str = encodeURI(str).replace(/%25(\d\d)/g, '%$1');
76
+ }
77
+ let url;
78
+ try {
79
+ try {
80
+ url = new URL(str, 'postgres://base');
81
+ }
82
+ catch {
83
+ url = new URL(str.replace('@/', '@___DUMMY___/'), 'postgres://base');
84
+ }
85
+ }
86
+ catch {
87
+ return null;
88
+ }
89
+ /* Con `socket:` el driver devuelve antes de mirar el SSL. */
90
+ if (url.protocol === 'socket:')
91
+ return null;
92
+ const p = {};
93
+ url.searchParams.forEach((valor, clave) => {
94
+ p[clave] = valor;
95
+ });
96
+ return p;
97
+ }
98
+ /**
99
+ * Qué hará el driver con el SSL de esta cadena.
100
+ *
101
+ * `{ dice: false }` cuando la cadena no dice nada: ni `ssl`, ni `sslmode`, ni
102
+ * ficheros (un `sslmode=` o `ssl=` vacío tampoco dicen nada: el driver los
103
+ * ignora).
104
+ */
105
+ export function leerSslPostgres(cadena) {
106
+ /* Recortada: es lo que se guarda (los formularios guardan `trim()`). */
107
+ const p = parametrosDeCadenaPostgres((cadena ?? '').trim());
108
+ if (!p)
109
+ return { dice: false };
110
+ const ficheros = FICHEROS.filter((k) => p[k]);
111
+ if (ficheros.length > 0) {
112
+ return {
113
+ dice: true,
114
+ modo: null,
115
+ por: `${ficheros[0]}=…`,
116
+ problema: `\`${ficheros[0]}\` points to a file, and the driver reads it from the disk of the ` +
117
+ `machine that connects — our servers, not yours. Remove it from the connection string ` +
118
+ `and pick the SSL mode here instead.`,
119
+ };
120
+ }
121
+ const libpq = p.uselibpqcompat === 'true';
122
+ const conLibpq = libpq ? ' with uselibpqcompat=true' : '';
123
+ if (p.sslmode) {
124
+ const por = `sslmode=${p.sslmode}${conLibpq}`;
125
+ if (libpq) {
126
+ switch (p.sslmode) {
127
+ case 'disable':
128
+ return { dice: true, modo: 'off', por };
129
+ case 'prefer':
130
+ case 'require':
131
+ return { dice: true, modo: 'no-verify', por };
132
+ case 'verify-ca':
133
+ return {
134
+ dice: true,
135
+ modo: null,
136
+ por,
137
+ problema: 'With uselibpqcompat=true, sslmode=verify-ca needs sslrootcert, and a certificate file ' +
138
+ "can't be read from our servers. Use sslmode=verify-full, or remove sslmode and pick " +
139
+ 'the SSL mode here.',
140
+ };
141
+ default:
142
+ /* `verify-full`, `no-verify` (que libpq no conoce) o un valor raro:
143
+ el objeto se queda vacío → comprobar. */
144
+ return { dice: true, modo: 'require', por };
145
+ }
146
+ }
147
+ switch (p.sslmode) {
148
+ case 'disable':
149
+ return { dice: true, modo: 'off', por };
150
+ case 'no-verify':
151
+ return { dice: true, modo: 'no-verify', por };
152
+ default:
153
+ return { dice: true, modo: 'require', por };
154
+ }
155
+ }
156
+ if (p.ssl) {
157
+ const por = `ssl=${p.ssl}`;
158
+ if (p.ssl === '0')
159
+ return { dice: true, modo: 'off', por };
160
+ /* `pg` convierte `ssl=no-verify` en «sin comprobar». */
161
+ if (p.ssl === 'no-verify')
162
+ return { dice: true, modo: 'no-verify', por };
163
+ /* `true`, `1`… y también `false`: queda como el texto `"false"`, que es
164
+ verdadero, y el driver cifra y comprueba. */
165
+ return { dice: true, modo: 'require', por };
166
+ }
167
+ return { dice: false };
168
+ }
169
+ /**
170
+ * Escribe el modo elegido en la cadena, quitando antes `ssl` y `sslmode` (en
171
+ * una cadena que «no dice nada», los que hubiera están vacíos). No toca el
172
+ * resto de parámetros, ni los recodifica.
173
+ *
174
+ * Lo que escribe, y por qué:
175
+ * - `off` → `sslmode=disable`, explícito: la cadena guardada dice lo
176
+ * que hace sin depender de `PGSSLMODE`.
177
+ * - `require` → `sslmode=verify-full`, no `require`: para `pg` 8 es lo
178
+ * mismo, pero `require` avisa en cada conexión de que en la
179
+ * versión 9 pasará a NO comprobar. `verify-full` significa
180
+ * lo mismo en las dos.
181
+ * - `no-verify` → `sslmode=no-verify`; con `uselibpqcompat=true`, que no lo
182
+ * conoce, `sslmode=require` (lo mismo en libpq).
183
+ *
184
+ * Una cadena vacía se devuelve tal cual.
185
+ */
186
+ export function ponerSslPostgres(cadena, modo) {
187
+ if (!cadena)
188
+ return cadena;
189
+ const i = cadena.indexOf('?');
190
+ const base = i >= 0 ? cadena.slice(0, i) : cadena;
191
+ const partes = i >= 0 ? cadena.slice(i + 1).split('&') : [];
192
+ const clave = (parte) => {
193
+ const k = parte.split('=')[0];
194
+ try {
195
+ return decodeURIComponent(k);
196
+ }
197
+ catch {
198
+ return k;
199
+ }
200
+ };
201
+ const libpq = partes.some((parte) => clave(parte) === 'uselibpqcompat' && /=true$/.test(parte));
202
+ const quedan = partes.filter((parte) => parte !== '' && clave(parte) !== 'ssl' && clave(parte) !== 'sslmode');
203
+ const valor = modo === 'off' ? 'disable' : modo === 'require' ? 'verify-full' : libpq ? 'require' : 'no-verify';
204
+ quedan.push(`sslmode=${valor}`);
205
+ return `${base}?${quedan.join('&')}`;
206
+ }
207
+ /**
208
+ * La frase que le falta a un error de conexión de SSL: qué cambiar. `null` si
209
+ * el error no es de SSL.
210
+ *
211
+ * Los tres casos que se ven al conectar:
212
+ * - el certificado no se puede comprobar (autofirmado, CA privada, nombre
213
+ * que no casa): Railway (`CN=localhost` de su `root-ca`) y Supabase (su
214
+ * `Supabase Root 2021 CA`) —medidos los dos el 2026-09-24—, Heroku y
215
+ * RDS. Neon sí se comprueba (Let's Encrypt);
216
+ * - el servidor no acepta SSL;
217
+ * - el servidor exige SSL y se conectó sin él.
218
+ */
219
+ export function pistaDeSslPostgres(mensaje) {
220
+ const m = mensaje ?? '';
221
+ if (/self[- ]signed certificate|unable to verify the first certificate|unable to get local issuer certificate|certificate has expired|Hostname\/IP does not match certificate|SELF_SIGNED_CERT_IN_CHAIN|DEPTH_ZERO_SELF_SIGNED_CERT|UNABLE_TO_VERIFY_LEAF_SIGNATURE|ERR_TLS_CERT_ALTNAME_INVALID|CERT_HAS_EXPIRED/i.test(m)) {
222
+ return ("The server's SSL certificate can't be verified (self-signed or from a private CA — Railway, " +
223
+ 'Supabase, Heroku and RDS do this). Set the credential\'s SSL mode to "Require, skip cert" ' +
224
+ '(sslmode=no-verify): the connection stays encrypted, only the certificate check is skipped.');
225
+ }
226
+ if (/server does not support SSL connections/i.test(m)) {
227
+ return ('This server does not accept SSL. Set the credential\'s SSL mode to "Off" (sslmode=disable) — ' +
228
+ 'only for a private network or a tunnel.');
229
+ }
230
+ if (/no pg_hba\.conf entry.*(no encryption|SSL off)|connection is insecure|SSL (connection )?is required|requires? SSL/i.test(m)) {
231
+ return ('This server requires SSL. Set the credential\'s SSL mode to "Require" (sslmode=verify-full), ' +
232
+ 'or "Require, skip cert" (sslmode=no-verify) if its certificate is self-signed.');
233
+ }
234
+ return null;
235
+ }
236
+ /**
237
+ * Los casos con los que se comprueba `ficherosEnCadenaPostgres` contra el
238
+ * driver de verdad (el api y hw-nodes espían `fs.readFileSync` mientras
239
+ * `pg-connection-string` lee cada cadena): `lee` es si el DRIVER abre un
240
+ * fichero. Incluye los dos rodeos medidos el 2026-09-24 —un espacio delante
241
+ * de `/` (el driver no lo toma por socket y lee) y una clave codificada
242
+ * (`ssl%72ootcert`)— y lo que no lee (mayúsculas, sockets, valor vacío).
243
+ * Las rutas empiezan por `/nope` para que el espía las reconozca.
244
+ */
245
+ export const CASOS_DE_FICHEROS_POSTGRES = [
246
+ { cadena: 'postgres://u:p@h/db?sslrootcert=/nope1', lee: true },
247
+ { cadena: ' /x?sslrootcert=/nope2', lee: true },
248
+ { cadena: 'postgres://u:p@h/db?ssl%72ootcert=/nope3', lee: true },
249
+ { cadena: 'postgres://u:p@h/db?SSLROOTCERT=/nope4', lee: false },
250
+ { cadena: 'socket://u:p@/var/run?sslrootcert=/nope5', lee: false },
251
+ { cadena: '/var/run/postgresql db?sslrootcert=/nope6', lee: false },
252
+ { cadena: 'postgres://u:p@/db?sslkey=/nope7', lee: true },
253
+ { cadena: 'postgres://u:p w@h/db?sslcert=/nope8', lee: true },
254
+ { cadena: 'postgres://u:p@h/db?sslrootcert=/nope9&sslrootcert=', lee: false },
255
+ { cadena: 'postgres://u:p@h/db?sslrootcert=&sslrootcert=/nope10', lee: true },
256
+ { cadena: 'postgres://u:p@h/db?sslmode=require&sslcert=/nope11&sslkey=/nope12', lee: true },
257
+ { cadena: 'postgres://u:p@h/db?sslmode=no-verify&sslpassword=x', lee: false },
258
+ ];
259
+ /**
260
+ * Los parámetros de la cadena que el driver leería como FICHEROS del disco de
261
+ * quien conecta —nuestros servidores—. Vacío si no hay ninguno. Devuelve los
262
+ * NOMBRES, nunca los valores: una ruta no se repite en un mensaje.
263
+ */
264
+ export function ficherosEnCadenaPostgres(cadena) {
265
+ const p = parametrosDeCadenaPostgres(cadena);
266
+ if (!p)
267
+ return [];
268
+ return FICHEROS_DE_POSTGRES.filter((k) => p[k]);
269
+ }
270
+ /** El mensaje con su pista, si la tiene. Lo que devuelven los servicios. Si el
271
+ * error ya pasó por aquí (dos capas que lo envuelven), no la repite. */
272
+ export function conPistaDeSslPostgres(mensaje) {
273
+ const pista = pistaDeSslPostgres(mensaje);
274
+ if (!pista || mensaje.includes(pista))
275
+ return mensaje;
276
+ return `${mensaje} — ${pista}`;
277
+ }
278
+ /**
279
+ * Los casos con los que se comprueba la regla — aquí y contra el driver de
280
+ * verdad en el api (`el-ssl-de-postgres-es-el-del-driver.spec.ts`). Uno nuevo
281
+ * se añade AQUÍ y lo miran los dos.
282
+ *
283
+ * `esperado`: el modo, `'nada'` si la cadena no dice nada, `'problema'` si no
284
+ * puede funcionar.
285
+ */
286
+ export const CASOS_DE_SSL_POSTGRES = [
287
+ { cadena: 'postgresql://u:p@h.example:5432/db', esperado: 'nada' },
288
+ { cadena: 'postgresql://u:p@h.example:5432/db?application_name=x', esperado: 'nada' },
289
+ { cadena: 'postgresql://u:p@h.example:5432/db?sslmode=', esperado: 'nada' },
290
+ { cadena: 'postgresql://u:p@h.example:5432/db?ssl=', esperado: 'nada' },
291
+ { cadena: 'postgresql://u:p@h.example:5432/db?SSLMODE=require', esperado: 'nada' },
292
+ { cadena: 'postgresql://u:p@h.example:5432/db?uselibpqcompat=true', esperado: 'nada' },
293
+ { cadena: 'postgresql://u:p@h.example:5432/db?sslmode=disable', esperado: 'off' },
294
+ { cadena: 'postgresql://u:p@h.example:5432/db?sslmode=allow', esperado: 'require' },
295
+ { cadena: 'postgresql://u:p@h.example:5432/db?sslmode=prefer', esperado: 'require' },
296
+ { cadena: 'postgresql://u:p@h.example:5432/db?sslmode=require', esperado: 'require' },
297
+ { cadena: 'postgresql://u:p@h.example:5432/db?sslmode=verify-ca', esperado: 'require' },
298
+ { cadena: 'postgresql://u:p@h.example:5432/db?sslmode=verify-full', esperado: 'require' },
299
+ { cadena: 'postgresql://u:p@h.example:5432/db?sslmode=no-verify', esperado: 'no-verify' },
300
+ { cadena: 'postgresql://u:p@h.example:5432/db?sslmode=REQUIRE', esperado: 'require' },
301
+ { cadena: 'postgresql://u:p@h.example:5432/db?ssl=true', esperado: 'require' },
302
+ { cadena: 'postgresql://u:p@h.example:5432/db?ssl=1', esperado: 'require' },
303
+ { cadena: 'postgresql://u:p@h.example:5432/db?ssl=false', esperado: 'require' },
304
+ { cadena: 'postgresql://u:p@h.example:5432/db?ssl=0', esperado: 'off' },
305
+ { cadena: 'postgresql://u:p@h.example:5432/db?ssl=no-verify', esperado: 'no-verify' },
306
+ { cadena: 'postgresql://u:p@h.example:5432/db?ssl=true&sslmode=disable', esperado: 'off' },
307
+ { cadena: 'postgresql://u:p@h.example:5432/db?sslmode=disable&ssl=true', esperado: 'off' },
308
+ { cadena: 'postgresql://u:p@h.example:5432/db?sslmode=no-verify&ssl=true', esperado: 'no-verify' },
309
+ { cadena: 'postgresql://u:p@h.example:5432/db?sslmode=require&sslmode=disable', esperado: 'off' },
310
+ { cadena: 'postgresql://u:p@h.example:5432/db?uselibpqcompat=true&sslmode=disable', esperado: 'off' },
311
+ { cadena: 'postgresql://u:p@h.example:5432/db?uselibpqcompat=true&sslmode=prefer', esperado: 'no-verify' },
312
+ { cadena: 'postgresql://u:p@h.example:5432/db?uselibpqcompat=true&sslmode=require', esperado: 'no-verify' },
313
+ { cadena: 'postgresql://u:p@h.example:5432/db?uselibpqcompat=true&sslmode=verify-full', esperado: 'require' },
314
+ { cadena: 'postgresql://u:p@h.example:5432/db?uselibpqcompat=true&sslmode=no-verify', esperado: 'require' },
315
+ { cadena: 'postgresql://u:p@h.example:5432/db?uselibpqcompat=true&sslmode=verify-ca', esperado: 'problema' },
316
+ { cadena: 'postgresql://u:p@h.example:5432/db?sslrootcert=/etc/ssl/ca.pem', esperado: 'problema' },
317
+ { cadena: 'postgresql://u:p@h.example:5432/db?sslmode=require&sslkey=key.pem', esperado: 'problema' },
318
+ { cadena: 'postgres://u:p@h.example/db?sslmode=require', esperado: 'require' },
319
+ { cadena: 'postgres://u:p%40x@h.example/db?sslmode=no-verify', esperado: 'no-verify' },
320
+ { cadena: 'postgres://u:p@/db?host=/var/run/postgresql&sslmode=disable', esperado: 'off' },
321
+ /* Varios hosts: `pg` ni la lee (`ERR_INVALID_URL`), así que no hay SSL que
322
+ fijar — falla al conectar con su propio error. */
323
+ { cadena: 'postgres://u:p@h1.example:5432,h2.example:5433/db?sslmode=require', esperado: 'nada' },
324
+ { cadena: 'postgres://u:p w@h.example/db?sslmode=no-verify', esperado: 'no-verify' },
325
+ { cadena: '/var/run/postgresql mydb', esperado: 'nada' },
326
+ { cadena: '', esperado: 'nada' },
327
+ ];
package/dist/index.d.ts CHANGED
@@ -80,6 +80,8 @@ export { TOOLKITS_POR_TIPO, operacionDeToolkit, esOperacionDestructiva, TIPOS_CO
80
80
  export { GOOGLE_CONTACTS_TOOLKIT_SPECS, GOOGLE_CONTACTS_TOOLKIT_BY_TOOL_NAME, } from './google-contacts-toolkit.js';
81
81
  export type { GoogleContactsToolkitSpec, GoogleContactsToolkitParameter, } from './google-contacts-toolkit.js';
82
82
  export type { OperacionDeToolkit } from './toolkits.js';
83
+ export { leerSslPostgres, ponerSslPostgres, pistaDeSslPostgres, conPistaDeSslPostgres, CASOS_DE_SSL_POSTGRES, FICHEROS_DE_POSTGRES, parametrosDeCadenaPostgres, ficherosEnCadenaPostgres, CASOS_DE_FICHEROS_POSTGRES, } from './postgres-ssl.js';
84
+ export type { ModoSslPostgres, SslDeLaCadenaPostgres } from './postgres-ssl.js';
83
85
  export type { LlmProvider, LlmProviderOption, LlmModelOption, } from './llm-models.js';
84
86
  export { LLM_PROVIDERS, LLM_MODELS, MODEL_CONTEXT_WINDOWS, getModelsFor, getDefaultModel, getModelLabel, } from './llm-models.js';
85
87
  export type { ModeloDeOpenRouter, PrecioDeOpenRouter, RespuestaDeModelosDeOpenRouter, } from './openrouter.js';
package/dist/index.js CHANGED
@@ -3,8 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.GMAIL_SEND_LEGACY_FIELDS = exports.NATIVE_EMAIL_TOOLKIT_BY_TOOL_NAME = exports.GMAIL_TOOLKIT_BY_TOOL_NAME = exports.NATIVE_EMAIL_TOOLKIT_SPECS = exports.GMAIL_SEND_AND_WAIT_TOOL_SPEC = exports.GMAIL_ALL_TOOLKIT_SPECS = exports.GMAIL_TOOLKIT_SPECS = exports.GMAIL_TOOLKIT_OPERATIONS = exports.GMAIL_DROPDOWN_OPERATIONS = exports.GMAIL_OPERATION_GROUPS = exports.GMAIL_OPERATION_SPECS = exports.GMAIL_OPERATIONS = exports.versionCatalogErrors = exports.fieldsLost = exports.fieldsLostBetween = exports.currentVersion = exports.versionSpec = exports.versionsOf = exports.isVersioned = exports.NODE_TYPE_TO_PREFIX = exports.PREFIX_TO_NODE_TYPE = exports.NODE_STATE_KEYS = exports.NODE_COLORS = exports.NODE_DETAIL_PATHS = exports.getNodeRegistryEntry = exports.NODE_REGISTRY = exports.pasaLoQueRecibe = exports.getNodeDispatchConfig = exports.getAllNodeCollections = exports.NODE_DISPATCH = exports.todosLosOutputGroups = exports.outputGroupsFor = exports.esEtiquetaDeSentimiento = exports.SENTIMENT_PORT_SUMMARY = exports.SENTIMENT_PORT_LABEL = exports.SENTIMENT_PORTS = exports.SENTIMENT_UNCLEAR = exports.SENTIMENT_LABELS = exports.resolveNodeId = exports.PREFIX_TO_TYPE = exports.NODE_UI = exports.ALL_NODE_TYPES = exports.isNodeType = exports.isTerminal = exports.canSendToNodes = exports.canReceiveFromNodes = exports.canReceiveFrom = exports.NODE_CONNECTIONS = exports.iterableMeta = exports.singleMeta = void 0;
4
4
  exports.isGithubOperation = exports.GITHUB_ITERABLE_OPERATIONS = exports.GITHUB_DROPDOWN_OPERATIONS = exports.GITHUB_OPERATION_SPECS = exports.GITHUB_OPERATIONS = exports.scopesQueFaltanEnShopify = exports.isShopifyOperation = exports.SHOPIFY_PRODUCT_STATUSES = exports.SHOPIFY_CANCEL_REASONS = exports.SHOPIFY_SEARCHABLE_RESOURCES = exports.SHOPIFY_TAGGABLE_RESOURCES = exports.SHOPIFY_OPERATION_SCOPES = exports.SHOPIFY_OPERATION_SPECS = exports.SHOPIFY_OPERATIONS = exports.isMailchimpOperation = exports.MAILCHIMP_CONTACT_STATUSES = exports.MAILCHIMP_OPERATION_SPECS = exports.MAILCHIMP_OPERATIONS = exports.herramientasDeDiscordPara = exports.DISCORD_TOOLKIT_BY_TOOL_NAME = exports.DISCORD_TOOLKIT_SPECS = exports.isDiscordOperation = exports.camposDeDiscordNoDisponibles = exports.discordPuedeEjecutar = exports.operacionesDeDiscordPara = exports.DISCORD_CAPACIDADES_POR_CREDENCIAL = exports.DISCORD_OPERATION_SPECS = exports.DISCORD_OPERATIONS = exports.camposNoDisponiblesPara = exports.puedeEjecutar = exports.operacionesPara = exports.isWhatsAppOperation = exports.WHATSAPP_OPERATIONS = exports.TELEGRAM_TOOLKIT_BY_TOOL_NAME = exports.TELEGRAM_TOOLKIT_SPECS = exports.isTelegramOperation = exports.TELEGRAM_OPERATION_SPECS = exports.TELEGRAM_OPERATIONS = exports.DRIVE_TOOLKIT_BY_TOOL_NAME = exports.DRIVE_TOOLKIT_SPECS = exports.isDriveOperation = exports.DRIVE_OPERATION_SPECS = exports.DRIVE_OPERATIONS = exports.GOOGLE_CALENDAR_TOOLKIT_BY_TOOL_NAME = exports.GOOGLE_CALENDAR_TOOLKIT_SPECS = exports.isGoogleCalendarOperation = exports.GOOGLE_CALENDAR_OPERATION_SPECS = exports.GOOGLE_CALENDAR_OPERATIONS = exports.isGmailOperation = exports.resolveGmailSendFields = void 0;
5
5
  exports.NOTION_OPERATION_SPECS = exports.NOTION_OPERATIONS = exports.isGoogleAnalyticsOperation = exports.GOOGLE_ANALYTICS_DROPDOWN_OPERATIONS = exports.GOOGLE_ANALYTICS_OPERATION_SPECS = exports.GOOGLE_ANALYTICS_OPERATIONS = exports.isGoogleContactsOperation = exports.GOOGLE_CONTACTS_DEFAULT_PERSON_FIELDS = exports.GOOGLE_CONTACTS_OPERATION_GROUPS = exports.GOOGLE_CONTACTS_OPERATION_SPECS = exports.GOOGLE_CONTACTS_OPERATIONS_V2 = exports.GOOGLE_CONTACTS_OPERATIONS_V1 = exports.GOOGLE_CONTACTS_OPERATIONS = exports.SHEETS_TOOLKIT_DEFAULTABLE = exports.SHEETS_TOOLKIT_BY_TOOL_NAME = exports.SHEETS_TOOLKIT_SPECS = exports.isSheetsOperation = exports.SHEETS_OPERATION_SPECS = exports.SHEETS_OPERATIONS = exports.herramientasDeSlackPara = exports.SLACK_TOOLKIT_BY_TOOL_NAME = exports.SLACK_TOOLKIT_SPECS = exports.isSlackOperation = exports.camposDeSlackNoDisponibles = exports.slackPuedeEjecutar = exports.operacionesDeSlackPara = exports.SLACK_CAPACIDADES_POR_CREDENCIAL = exports.SLACK_OPERATION_SPECS = exports.SLACK_OPERATIONS = exports.isAirtableOperation = exports.AIRTABLE_ITERABLE_OPERATIONS = exports.AIRTABLE_OPERATION_SPECS = exports.AIRTABLE_OPERATIONS = exports.isCalendlyOperation = exports.CALENDLY_ITERABLE_OPERATIONS = exports.CALENDLY_OPERATION_SPECS = exports.CALENDLY_OPERATIONS = exports.isApifyOperation = exports.APIFY_ITERABLE_OPERATIONS = exports.APIFY_OPERATION_SPECS = exports.APIFY_OPERATIONS = exports.isBucketOperation = exports.BUCKET_ITERABLE_OPERATIONS = exports.BUCKET_OPERATION_SPECS = exports.BUCKET_OPERATIONS = exports.isJiraOperation = exports.JIRA_ITERABLE_OPERATIONS = exports.JIRA_DROPDOWN_OPERATIONS = exports.JIRA_OPERATION_SPECS = exports.JIRA_OPERATIONS = void 0;
6
- exports.isCredentialType = exports.getCredentialType = exports.credentialTypeValues = exports.CREDENTIAL_TYPE_VALUES = exports.CREDENTIAL_TYPES = exports.ventanaDeContextoDeOpenRouter = exports.opcionesDeModelosDeOpenRouter = exports.URL_DE_MODELOS_DE_OPENROUTER = exports.getModelLabel = exports.getDefaultModel = exports.getModelsFor = exports.MODEL_CONTEXT_WINDOWS = exports.LLM_MODELS = exports.LLM_PROVIDERS = exports.GOOGLE_CONTACTS_TOOLKIT_BY_TOOL_NAME = exports.GOOGLE_CONTACTS_TOOLKIT_SPECS = exports.motivoParaNoCorrer = exports.mensajeDeHerramientaBloqueada = exports.MENSAJE_SIN_HERRAMIENTA = exports.herramientaBloqueadaEn = exports.bloqueadasDe = exports.nombresDeHerramientas = exports.admiteBloqueo = exports.tieneModoToolkit = exports.TIPOS_CON_TOOLKIT = exports.esOperacionDestructiva = exports.operacionDeToolkit = exports.TOOLKITS_POR_TIPO = exports.marcarDestructivas = exports.esHerramientaDestructiva = exports.claseDeHerramienta = exports.POSTGRES_TOOLKIT_BY_TOOL_NAME = exports.POSTGRES_TOOLKIT_SPECS = exports.MONGO_TOOLKIT_BY_TOOL_NAME = exports.MONGO_TOOLKIT_SPECS = exports.DOCS_TOOLKIT_DEFAULTABLE = exports.DOCS_TOOLKIT_BY_TOOL_NAME = exports.DOCS_TOOLKIT_SPECS = exports.isDocsOperation = exports.DOCS_OPERATION_SPECS = exports.DOCS_OPERATIONS = exports.isMongoOperation = exports.MONGO_OPERATION_SPECS = exports.MONGO_OPERATIONS = exports.isPostgresOperation = exports.POSTGRES_OPERATION_SPECS = exports.POSTGRES_MODES = exports.POSTGRES_OPERATIONS = exports.isNotionOperation = exports.NOTION_DROPDOWN_OPERATIONS = void 0;
7
- exports.armarRespuestaDelSync = exports.RESPUESTA_DEL_SYNC_POR_DEFECTO = exports.TOPE_DE_ESPERA_DEL_SYNC = exports.TOPES_DE_ESTADO = exports.SOBRES_DE_RESPUESTA = exports.MODOS_DE_PAYLOAD = exports.NIVELES_DE_DETALLE = exports.RESULTADOS_DEL_VALIDADOR = exports.contestaEnSync = exports.NODOS_QUE_CONTESTAN_EN_SYNC = void 0;
6
+ exports.getDefaultModel = exports.getModelsFor = exports.MODEL_CONTEXT_WINDOWS = exports.LLM_MODELS = exports.LLM_PROVIDERS = exports.CASOS_DE_FICHEROS_POSTGRES = exports.ficherosEnCadenaPostgres = exports.parametrosDeCadenaPostgres = exports.FICHEROS_DE_POSTGRES = exports.CASOS_DE_SSL_POSTGRES = exports.conPistaDeSslPostgres = exports.pistaDeSslPostgres = exports.ponerSslPostgres = exports.leerSslPostgres = exports.GOOGLE_CONTACTS_TOOLKIT_BY_TOOL_NAME = exports.GOOGLE_CONTACTS_TOOLKIT_SPECS = exports.motivoParaNoCorrer = exports.mensajeDeHerramientaBloqueada = exports.MENSAJE_SIN_HERRAMIENTA = exports.herramientaBloqueadaEn = exports.bloqueadasDe = exports.nombresDeHerramientas = exports.admiteBloqueo = exports.tieneModoToolkit = exports.TIPOS_CON_TOOLKIT = exports.esOperacionDestructiva = exports.operacionDeToolkit = exports.TOOLKITS_POR_TIPO = exports.marcarDestructivas = exports.esHerramientaDestructiva = exports.claseDeHerramienta = exports.POSTGRES_TOOLKIT_BY_TOOL_NAME = exports.POSTGRES_TOOLKIT_SPECS = exports.MONGO_TOOLKIT_BY_TOOL_NAME = exports.MONGO_TOOLKIT_SPECS = exports.DOCS_TOOLKIT_DEFAULTABLE = exports.DOCS_TOOLKIT_BY_TOOL_NAME = exports.DOCS_TOOLKIT_SPECS = exports.isDocsOperation = exports.DOCS_OPERATION_SPECS = exports.DOCS_OPERATIONS = exports.isMongoOperation = exports.MONGO_OPERATION_SPECS = exports.MONGO_OPERATIONS = exports.isPostgresOperation = exports.POSTGRES_OPERATION_SPECS = exports.POSTGRES_MODES = exports.POSTGRES_OPERATIONS = exports.isNotionOperation = exports.NOTION_DROPDOWN_OPERATIONS = void 0;
7
+ exports.armarRespuestaDelSync = exports.RESPUESTA_DEL_SYNC_POR_DEFECTO = exports.TOPE_DE_ESPERA_DEL_SYNC = exports.TOPES_DE_ESTADO = exports.SOBRES_DE_RESPUESTA = exports.MODOS_DE_PAYLOAD = exports.NIVELES_DE_DETALLE = exports.RESULTADOS_DEL_VALIDADOR = exports.contestaEnSync = exports.NODOS_QUE_CONTESTAN_EN_SYNC = exports.isCredentialType = exports.getCredentialType = exports.credentialTypeValues = exports.CREDENTIAL_TYPE_VALUES = exports.CREDENTIAL_TYPES = exports.ventanaDeContextoDeOpenRouter = exports.opcionesDeModelosDeOpenRouter = exports.URL_DE_MODELOS_DE_OPENROUTER = exports.getModelLabel = void 0;
8
8
  var types_js_1 = require("./types.js");
9
9
  Object.defineProperty(exports, "singleMeta", { enumerable: true, get: function () { return types_js_1.singleMeta; } });
10
10
  Object.defineProperty(exports, "iterableMeta", { enumerable: true, get: function () { return types_js_1.iterableMeta; } });
@@ -255,6 +255,20 @@ Object.defineProperty(exports, "motivoParaNoCorrer", { enumerable: true, get: fu
255
255
  var google_contacts_toolkit_js_1 = require("./google-contacts-toolkit.js");
256
256
  Object.defineProperty(exports, "GOOGLE_CONTACTS_TOOLKIT_SPECS", { enumerable: true, get: function () { return google_contacts_toolkit_js_1.GOOGLE_CONTACTS_TOOLKIT_SPECS; } });
257
257
  Object.defineProperty(exports, "GOOGLE_CONTACTS_TOOLKIT_BY_TOOL_NAME", { enumerable: true, get: function () { return google_contacts_toolkit_js_1.GOOGLE_CONTACTS_TOOLKIT_BY_TOOL_NAME; } });
258
+ // ── El SSL de una cadena de Postgres (dashboard, api y hw-nodes) ──
259
+ /* Lo que hará `pg` con el SSL de una cadena, escrito una vez: el panel lo usa
260
+ para fijar el modo cuando la cadena lo dice, y los servicios para decir qué
261
+ cambiar cuando falla. El api lo compara con el driver de verdad. */
262
+ var postgres_ssl_js_1 = require("./postgres-ssl.js");
263
+ Object.defineProperty(exports, "leerSslPostgres", { enumerable: true, get: function () { return postgres_ssl_js_1.leerSslPostgres; } });
264
+ Object.defineProperty(exports, "ponerSslPostgres", { enumerable: true, get: function () { return postgres_ssl_js_1.ponerSslPostgres; } });
265
+ Object.defineProperty(exports, "pistaDeSslPostgres", { enumerable: true, get: function () { return postgres_ssl_js_1.pistaDeSslPostgres; } });
266
+ Object.defineProperty(exports, "conPistaDeSslPostgres", { enumerable: true, get: function () { return postgres_ssl_js_1.conPistaDeSslPostgres; } });
267
+ Object.defineProperty(exports, "CASOS_DE_SSL_POSTGRES", { enumerable: true, get: function () { return postgres_ssl_js_1.CASOS_DE_SSL_POSTGRES; } });
268
+ Object.defineProperty(exports, "FICHEROS_DE_POSTGRES", { enumerable: true, get: function () { return postgres_ssl_js_1.FICHEROS_DE_POSTGRES; } });
269
+ Object.defineProperty(exports, "parametrosDeCadenaPostgres", { enumerable: true, get: function () { return postgres_ssl_js_1.parametrosDeCadenaPostgres; } });
270
+ Object.defineProperty(exports, "ficherosEnCadenaPostgres", { enumerable: true, get: function () { return postgres_ssl_js_1.ficherosEnCadenaPostgres; } });
271
+ Object.defineProperty(exports, "CASOS_DE_FICHEROS_POSTGRES", { enumerable: true, get: function () { return postgres_ssl_js_1.CASOS_DE_FICHEROS_POSTGRES; } });
258
272
  var llm_models_js_1 = require("./llm-models.js");
259
273
  Object.defineProperty(exports, "LLM_PROVIDERS", { enumerable: true, get: function () { return llm_models_js_1.LLM_PROVIDERS; } });
260
274
  Object.defineProperty(exports, "LLM_MODELS", { enumerable: true, get: function () { return llm_models_js_1.LLM_MODELS; } });
@@ -0,0 +1,163 @@
1
+ /**
2
+ * El SSL de una cadena de conexión de Postgres: qué hará el driver con ella,
3
+ * y cómo escribir el modo que se elija.
4
+ *
5
+ * ## Por qué vive aquí (2026-09-24)
6
+ *
7
+ * Ariel conectó un Postgres de Railway eligiendo «Require» y el panel del nodo
8
+ * dijo `self-signed certificate in certificate chain`. Medido: el servidor
9
+ * enseña un certificado `CN=localhost` firmado por su propio `root-ca`, y
10
+ * «Require» escribía `sslmode=require`, que para `pg` 8 es VERIFICAR el
11
+ * certificado. Y pidió que, si la cadena ya trae el SSL, el panel lo lea y lo
12
+ * deje fijo; y si no lo trae, que se elija a mano.
13
+ *
14
+ * Para fijarlo, el panel tiene que saber lo que hará EL DRIVER, no lo que
15
+ * parece que dice la cadena — y el driver tiene reglas que no se adivinan:
16
+ * `sslmode=prefer` también verifica, `ssl=false` ENCIENDE el SSL (queda como
17
+ * el texto `"false"`, que es verdadero), y `uselibpqcompat=true` le da la
18
+ * vuelta a `require`. El panel sólo conocía `require` y `no-verify`; lo demás
19
+ * lo pintaba como «Off».
20
+ *
21
+ * Una sola función para el dashboard y para los servicios, y el api la compara
22
+ * caso a caso con el `pg` que de verdad corre (`el-ssl-de-postgres-es-el-del-
23
+ * driver.spec.ts`): si una actualización de `pg` cambia sus reglas, ese spec
24
+ * se pone rojo antes de que el panel enseñe una cosa y el driver haga otra.
25
+ *
26
+ * ## Las reglas, copiadas de `pg-connection-string` 2.12 y `pg` 8.20
27
+ *
28
+ * 1. Los parámetros salen de `new URL(cadena, 'postgres://base')` (con el
29
+ * mismo arreglo de espacios y el mismo host de relleno para `@/`); si una
30
+ * clave se repite, gana la ÚLTIMA. Las claves distinguen mayúsculas:
31
+ * `SSLMODE` no existe para el driver.
32
+ * 2. `ssl=true`/`ssl=1` → sí; `ssl=0` → no; cualquier otro texto se queda
33
+ * como texto (`ssl=false` → `"false"` → ENCENDIDO).
34
+ * 3. Si hay `sslmode` (no vacío) o un fichero (`sslcert`, `sslkey`,
35
+ * `sslrootcert`), el SSL pasa a ser un objeto y `ssl=` deja de contar.
36
+ * 4. `sslmode`: `disable` → no; `no-verify` → cifrar sin comprobar; todo lo
37
+ * demás (`prefer`, `require`, `verify-ca`, `verify-full`, y cualquier
38
+ * valor raro) → cifrar Y comprobar el certificado.
39
+ * 5. Con `uselibpqcompat=true`, las de libpq: `prefer` y `require` → sin
40
+ * comprobar; `verify-full` → comprobando; `verify-ca` sin `sslrootcert`
41
+ * → el driver se NIEGA; `no-verify` no existe en libpq → comprobando.
42
+ * 6. Sin nada de eso, `pg` mira `PGSSLMODE` del entorno; en los servicios no
43
+ * está puesta (medido el 2026-09-24), así que es SIN SSL.
44
+ *
45
+ * ## Los ficheros (`sslcert`, `sslkey`, `sslrootcert`)
46
+ *
47
+ * El driver los LEE DEL DISCO de quien conecta — nuestro servidor, no la
48
+ * máquina de quien pegó la cadena. Nunca pueden valer: aquí salen como
49
+ * `problema` y el panel no deja guardarlos.
50
+ */
51
+ /** Los tres modos del selector. `require` es «cifrar y comprobar el
52
+ * certificado» — lo que `pg` 8 hace con `sslmode=require`. */
53
+ export type ModoSslPostgres = 'off' | 'require' | 'no-verify';
54
+ export type SslDeLaCadenaPostgres =
55
+ /** La cadena no dice nada de SSL: se elige a mano. */
56
+ {
57
+ dice: false;
58
+ }
59
+ /** La cadena lo decide. `por` es el parámetro que manda (`sslmode=require`). */
60
+ | {
61
+ dice: true;
62
+ modo: ModoSslPostgres;
63
+ por: string;
64
+ }
65
+ /** La cadena dice algo que no puede funcionar desde nuestros servidores. */
66
+ | {
67
+ dice: true;
68
+ modo: null;
69
+ por: string;
70
+ problema: string;
71
+ };
72
+ /**
73
+ * Los parámetros que `pg-connection-string` LEE DEL DISCO con
74
+ * `fs.readFileSync(<valor>)` al construir el cliente (2.12 y 2.14, medido
75
+ * el 2026-09-24). El api y hw-nodes comprueban contra el fuente del driver
76
+ * que no lee ninguno más (`ninguna-conexion-postgres-lee-ficheros.spec.ts`).
77
+ */
78
+ export declare const FICHEROS_DE_POSTGRES: readonly ["sslcert", "sslkey", "sslrootcert"];
79
+ /**
80
+ * Los parámetros de la cadena EXACTAMENTE como los lee `pg-connection-string`
81
+ * —mismo arreglo de espacios, mismo host de relleno para `@/`, claves
82
+ * decodificadas, la última repetida gana—. `null` si el driver no llega a
83
+ * leer parámetros: un socket (`/ruta` o `socket:`) o una cadena que ni él
84
+ * puede leer.
85
+ *
86
+ * ⚠️ SIN recortar: la usa una guarda de seguridad
87
+ * (`asegurarPostgresSinFicheros` en node-sdk). Con `" /x?sslrootcert=…"`, un
88
+ * recorte vería un socket y dejaría pasar lo que el driver sí lee.
89
+ */
90
+ export declare function parametrosDeCadenaPostgres(cadena: string): Record<string, string> | null;
91
+ /**
92
+ * Qué hará el driver con el SSL de esta cadena.
93
+ *
94
+ * `{ dice: false }` cuando la cadena no dice nada: ni `ssl`, ni `sslmode`, ni
95
+ * ficheros (un `sslmode=` o `ssl=` vacío tampoco dicen nada: el driver los
96
+ * ignora).
97
+ */
98
+ export declare function leerSslPostgres(cadena: string): SslDeLaCadenaPostgres;
99
+ /**
100
+ * Escribe el modo elegido en la cadena, quitando antes `ssl` y `sslmode` (en
101
+ * una cadena que «no dice nada», los que hubiera están vacíos). No toca el
102
+ * resto de parámetros, ni los recodifica.
103
+ *
104
+ * Lo que escribe, y por qué:
105
+ * - `off` → `sslmode=disable`, explícito: la cadena guardada dice lo
106
+ * que hace sin depender de `PGSSLMODE`.
107
+ * - `require` → `sslmode=verify-full`, no `require`: para `pg` 8 es lo
108
+ * mismo, pero `require` avisa en cada conexión de que en la
109
+ * versión 9 pasará a NO comprobar. `verify-full` significa
110
+ * lo mismo en las dos.
111
+ * - `no-verify` → `sslmode=no-verify`; con `uselibpqcompat=true`, que no lo
112
+ * conoce, `sslmode=require` (lo mismo en libpq).
113
+ *
114
+ * Una cadena vacía se devuelve tal cual.
115
+ */
116
+ export declare function ponerSslPostgres(cadena: string, modo: ModoSslPostgres): string;
117
+ /**
118
+ * La frase que le falta a un error de conexión de SSL: qué cambiar. `null` si
119
+ * el error no es de SSL.
120
+ *
121
+ * Los tres casos que se ven al conectar:
122
+ * - el certificado no se puede comprobar (autofirmado, CA privada, nombre
123
+ * que no casa): Railway (`CN=localhost` de su `root-ca`) y Supabase (su
124
+ * `Supabase Root 2021 CA`) —medidos los dos el 2026-09-24—, Heroku y
125
+ * RDS. Neon sí se comprueba (Let's Encrypt);
126
+ * - el servidor no acepta SSL;
127
+ * - el servidor exige SSL y se conectó sin él.
128
+ */
129
+ export declare function pistaDeSslPostgres(mensaje: string): string | null;
130
+ /**
131
+ * Los casos con los que se comprueba `ficherosEnCadenaPostgres` contra el
132
+ * driver de verdad (el api y hw-nodes espían `fs.readFileSync` mientras
133
+ * `pg-connection-string` lee cada cadena): `lee` es si el DRIVER abre un
134
+ * fichero. Incluye los dos rodeos medidos el 2026-09-24 —un espacio delante
135
+ * de `/` (el driver no lo toma por socket y lee) y una clave codificada
136
+ * (`ssl%72ootcert`)— y lo que no lee (mayúsculas, sockets, valor vacío).
137
+ * Las rutas empiezan por `/nope` para que el espía las reconozca.
138
+ */
139
+ export declare const CASOS_DE_FICHEROS_POSTGRES: ReadonlyArray<{
140
+ cadena: string;
141
+ lee: boolean;
142
+ }>;
143
+ /**
144
+ * Los parámetros de la cadena que el driver leería como FICHEROS del disco de
145
+ * quien conecta —nuestros servidores—. Vacío si no hay ninguno. Devuelve los
146
+ * NOMBRES, nunca los valores: una ruta no se repite en un mensaje.
147
+ */
148
+ export declare function ficherosEnCadenaPostgres(cadena: string): string[];
149
+ /** El mensaje con su pista, si la tiene. Lo que devuelven los servicios. Si el
150
+ * error ya pasó por aquí (dos capas que lo envuelven), no la repite. */
151
+ export declare function conPistaDeSslPostgres(mensaje: string): string;
152
+ /**
153
+ * Los casos con los que se comprueba la regla — aquí y contra el driver de
154
+ * verdad en el api (`el-ssl-de-postgres-es-el-del-driver.spec.ts`). Uno nuevo
155
+ * se añade AQUÍ y lo miran los dos.
156
+ *
157
+ * `esperado`: el modo, `'nada'` si la cadena no dice nada, `'problema'` si no
158
+ * puede funcionar.
159
+ */
160
+ export declare const CASOS_DE_SSL_POSTGRES: ReadonlyArray<{
161
+ cadena: string;
162
+ esperado: ModoSslPostgres | 'nada' | 'problema';
163
+ }>;
@@ -0,0 +1,336 @@
1
+ "use strict";
2
+ /**
3
+ * El SSL de una cadena de conexión de Postgres: qué hará el driver con ella,
4
+ * y cómo escribir el modo que se elija.
5
+ *
6
+ * ## Por qué vive aquí (2026-09-24)
7
+ *
8
+ * Ariel conectó un Postgres de Railway eligiendo «Require» y el panel del nodo
9
+ * dijo `self-signed certificate in certificate chain`. Medido: el servidor
10
+ * enseña un certificado `CN=localhost` firmado por su propio `root-ca`, y
11
+ * «Require» escribía `sslmode=require`, que para `pg` 8 es VERIFICAR el
12
+ * certificado. Y pidió que, si la cadena ya trae el SSL, el panel lo lea y lo
13
+ * deje fijo; y si no lo trae, que se elija a mano.
14
+ *
15
+ * Para fijarlo, el panel tiene que saber lo que hará EL DRIVER, no lo que
16
+ * parece que dice la cadena — y el driver tiene reglas que no se adivinan:
17
+ * `sslmode=prefer` también verifica, `ssl=false` ENCIENDE el SSL (queda como
18
+ * el texto `"false"`, que es verdadero), y `uselibpqcompat=true` le da la
19
+ * vuelta a `require`. El panel sólo conocía `require` y `no-verify`; lo demás
20
+ * lo pintaba como «Off».
21
+ *
22
+ * Una sola función para el dashboard y para los servicios, y el api la compara
23
+ * caso a caso con el `pg` que de verdad corre (`el-ssl-de-postgres-es-el-del-
24
+ * driver.spec.ts`): si una actualización de `pg` cambia sus reglas, ese spec
25
+ * se pone rojo antes de que el panel enseñe una cosa y el driver haga otra.
26
+ *
27
+ * ## Las reglas, copiadas de `pg-connection-string` 2.12 y `pg` 8.20
28
+ *
29
+ * 1. Los parámetros salen de `new URL(cadena, 'postgres://base')` (con el
30
+ * mismo arreglo de espacios y el mismo host de relleno para `@/`); si una
31
+ * clave se repite, gana la ÚLTIMA. Las claves distinguen mayúsculas:
32
+ * `SSLMODE` no existe para el driver.
33
+ * 2. `ssl=true`/`ssl=1` → sí; `ssl=0` → no; cualquier otro texto se queda
34
+ * como texto (`ssl=false` → `"false"` → ENCENDIDO).
35
+ * 3. Si hay `sslmode` (no vacío) o un fichero (`sslcert`, `sslkey`,
36
+ * `sslrootcert`), el SSL pasa a ser un objeto y `ssl=` deja de contar.
37
+ * 4. `sslmode`: `disable` → no; `no-verify` → cifrar sin comprobar; todo lo
38
+ * demás (`prefer`, `require`, `verify-ca`, `verify-full`, y cualquier
39
+ * valor raro) → cifrar Y comprobar el certificado.
40
+ * 5. Con `uselibpqcompat=true`, las de libpq: `prefer` y `require` → sin
41
+ * comprobar; `verify-full` → comprobando; `verify-ca` sin `sslrootcert`
42
+ * → el driver se NIEGA; `no-verify` no existe en libpq → comprobando.
43
+ * 6. Sin nada de eso, `pg` mira `PGSSLMODE` del entorno; en los servicios no
44
+ * está puesta (medido el 2026-09-24), así que es SIN SSL.
45
+ *
46
+ * ## Los ficheros (`sslcert`, `sslkey`, `sslrootcert`)
47
+ *
48
+ * El driver los LEE DEL DISCO de quien conecta — nuestro servidor, no la
49
+ * máquina de quien pegó la cadena. Nunca pueden valer: aquí salen como
50
+ * `problema` y el panel no deja guardarlos.
51
+ */
52
+ Object.defineProperty(exports, "__esModule", { value: true });
53
+ exports.CASOS_DE_SSL_POSTGRES = exports.CASOS_DE_FICHEROS_POSTGRES = exports.FICHEROS_DE_POSTGRES = void 0;
54
+ exports.parametrosDeCadenaPostgres = parametrosDeCadenaPostgres;
55
+ exports.leerSslPostgres = leerSslPostgres;
56
+ exports.ponerSslPostgres = ponerSslPostgres;
57
+ exports.pistaDeSslPostgres = pistaDeSslPostgres;
58
+ exports.ficherosEnCadenaPostgres = ficherosEnCadenaPostgres;
59
+ exports.conPistaDeSslPostgres = conPistaDeSslPostgres;
60
+ /**
61
+ * Los parámetros que `pg-connection-string` LEE DEL DISCO con
62
+ * `fs.readFileSync(<valor>)` al construir el cliente (2.12 y 2.14, medido
63
+ * el 2026-09-24). El api y hw-nodes comprueban contra el fuente del driver
64
+ * que no lee ninguno más (`ninguna-conexion-postgres-lee-ficheros.spec.ts`).
65
+ */
66
+ exports.FICHEROS_DE_POSTGRES = ['sslcert', 'sslkey', 'sslrootcert'];
67
+ const FICHEROS = exports.FICHEROS_DE_POSTGRES;
68
+ /**
69
+ * Los parámetros de la cadena EXACTAMENTE como los lee `pg-connection-string`
70
+ * —mismo arreglo de espacios, mismo host de relleno para `@/`, claves
71
+ * decodificadas, la última repetida gana—. `null` si el driver no llega a
72
+ * leer parámetros: un socket (`/ruta` o `socket:`) o una cadena que ni él
73
+ * puede leer.
74
+ *
75
+ * ⚠️ SIN recortar: la usa una guarda de seguridad
76
+ * (`asegurarPostgresSinFicheros` en node-sdk). Con `" /x?sslrootcert=…"`, un
77
+ * recorte vería un socket y dejaría pasar lo que el driver sí lee.
78
+ */
79
+ function parametrosDeCadenaPostgres(cadena) {
80
+ let str = cadena ?? '';
81
+ if (!str || str.charAt(0) === '/')
82
+ return null;
83
+ if (/ |%[^a-f0-9]|%[a-f0-9][^a-f0-9]/i.test(str)) {
84
+ str = encodeURI(str).replace(/%25(\d\d)/g, '%$1');
85
+ }
86
+ let url;
87
+ try {
88
+ try {
89
+ url = new URL(str, 'postgres://base');
90
+ }
91
+ catch {
92
+ url = new URL(str.replace('@/', '@___DUMMY___/'), 'postgres://base');
93
+ }
94
+ }
95
+ catch {
96
+ return null;
97
+ }
98
+ /* Con `socket:` el driver devuelve antes de mirar el SSL. */
99
+ if (url.protocol === 'socket:')
100
+ return null;
101
+ const p = {};
102
+ url.searchParams.forEach((valor, clave) => {
103
+ p[clave] = valor;
104
+ });
105
+ return p;
106
+ }
107
+ /**
108
+ * Qué hará el driver con el SSL de esta cadena.
109
+ *
110
+ * `{ dice: false }` cuando la cadena no dice nada: ni `ssl`, ni `sslmode`, ni
111
+ * ficheros (un `sslmode=` o `ssl=` vacío tampoco dicen nada: el driver los
112
+ * ignora).
113
+ */
114
+ function leerSslPostgres(cadena) {
115
+ /* Recortada: es lo que se guarda (los formularios guardan `trim()`). */
116
+ const p = parametrosDeCadenaPostgres((cadena ?? '').trim());
117
+ if (!p)
118
+ return { dice: false };
119
+ const ficheros = FICHEROS.filter((k) => p[k]);
120
+ if (ficheros.length > 0) {
121
+ return {
122
+ dice: true,
123
+ modo: null,
124
+ por: `${ficheros[0]}=…`,
125
+ problema: `\`${ficheros[0]}\` points to a file, and the driver reads it from the disk of the ` +
126
+ `machine that connects — our servers, not yours. Remove it from the connection string ` +
127
+ `and pick the SSL mode here instead.`,
128
+ };
129
+ }
130
+ const libpq = p.uselibpqcompat === 'true';
131
+ const conLibpq = libpq ? ' with uselibpqcompat=true' : '';
132
+ if (p.sslmode) {
133
+ const por = `sslmode=${p.sslmode}${conLibpq}`;
134
+ if (libpq) {
135
+ switch (p.sslmode) {
136
+ case 'disable':
137
+ return { dice: true, modo: 'off', por };
138
+ case 'prefer':
139
+ case 'require':
140
+ return { dice: true, modo: 'no-verify', por };
141
+ case 'verify-ca':
142
+ return {
143
+ dice: true,
144
+ modo: null,
145
+ por,
146
+ problema: 'With uselibpqcompat=true, sslmode=verify-ca needs sslrootcert, and a certificate file ' +
147
+ "can't be read from our servers. Use sslmode=verify-full, or remove sslmode and pick " +
148
+ 'the SSL mode here.',
149
+ };
150
+ default:
151
+ /* `verify-full`, `no-verify` (que libpq no conoce) o un valor raro:
152
+ el objeto se queda vacío → comprobar. */
153
+ return { dice: true, modo: 'require', por };
154
+ }
155
+ }
156
+ switch (p.sslmode) {
157
+ case 'disable':
158
+ return { dice: true, modo: 'off', por };
159
+ case 'no-verify':
160
+ return { dice: true, modo: 'no-verify', por };
161
+ default:
162
+ return { dice: true, modo: 'require', por };
163
+ }
164
+ }
165
+ if (p.ssl) {
166
+ const por = `ssl=${p.ssl}`;
167
+ if (p.ssl === '0')
168
+ return { dice: true, modo: 'off', por };
169
+ /* `pg` convierte `ssl=no-verify` en «sin comprobar». */
170
+ if (p.ssl === 'no-verify')
171
+ return { dice: true, modo: 'no-verify', por };
172
+ /* `true`, `1`… y también `false`: queda como el texto `"false"`, que es
173
+ verdadero, y el driver cifra y comprueba. */
174
+ return { dice: true, modo: 'require', por };
175
+ }
176
+ return { dice: false };
177
+ }
178
+ /**
179
+ * Escribe el modo elegido en la cadena, quitando antes `ssl` y `sslmode` (en
180
+ * una cadena que «no dice nada», los que hubiera están vacíos). No toca el
181
+ * resto de parámetros, ni los recodifica.
182
+ *
183
+ * Lo que escribe, y por qué:
184
+ * - `off` → `sslmode=disable`, explícito: la cadena guardada dice lo
185
+ * que hace sin depender de `PGSSLMODE`.
186
+ * - `require` → `sslmode=verify-full`, no `require`: para `pg` 8 es lo
187
+ * mismo, pero `require` avisa en cada conexión de que en la
188
+ * versión 9 pasará a NO comprobar. `verify-full` significa
189
+ * lo mismo en las dos.
190
+ * - `no-verify` → `sslmode=no-verify`; con `uselibpqcompat=true`, que no lo
191
+ * conoce, `sslmode=require` (lo mismo en libpq).
192
+ *
193
+ * Una cadena vacía se devuelve tal cual.
194
+ */
195
+ function ponerSslPostgres(cadena, modo) {
196
+ if (!cadena)
197
+ return cadena;
198
+ const i = cadena.indexOf('?');
199
+ const base = i >= 0 ? cadena.slice(0, i) : cadena;
200
+ const partes = i >= 0 ? cadena.slice(i + 1).split('&') : [];
201
+ const clave = (parte) => {
202
+ const k = parte.split('=')[0];
203
+ try {
204
+ return decodeURIComponent(k);
205
+ }
206
+ catch {
207
+ return k;
208
+ }
209
+ };
210
+ const libpq = partes.some((parte) => clave(parte) === 'uselibpqcompat' && /=true$/.test(parte));
211
+ const quedan = partes.filter((parte) => parte !== '' && clave(parte) !== 'ssl' && clave(parte) !== 'sslmode');
212
+ const valor = modo === 'off' ? 'disable' : modo === 'require' ? 'verify-full' : libpq ? 'require' : 'no-verify';
213
+ quedan.push(`sslmode=${valor}`);
214
+ return `${base}?${quedan.join('&')}`;
215
+ }
216
+ /**
217
+ * La frase que le falta a un error de conexión de SSL: qué cambiar. `null` si
218
+ * el error no es de SSL.
219
+ *
220
+ * Los tres casos que se ven al conectar:
221
+ * - el certificado no se puede comprobar (autofirmado, CA privada, nombre
222
+ * que no casa): Railway (`CN=localhost` de su `root-ca`) y Supabase (su
223
+ * `Supabase Root 2021 CA`) —medidos los dos el 2026-09-24—, Heroku y
224
+ * RDS. Neon sí se comprueba (Let's Encrypt);
225
+ * - el servidor no acepta SSL;
226
+ * - el servidor exige SSL y se conectó sin él.
227
+ */
228
+ function pistaDeSslPostgres(mensaje) {
229
+ const m = mensaje ?? '';
230
+ if (/self[- ]signed certificate|unable to verify the first certificate|unable to get local issuer certificate|certificate has expired|Hostname\/IP does not match certificate|SELF_SIGNED_CERT_IN_CHAIN|DEPTH_ZERO_SELF_SIGNED_CERT|UNABLE_TO_VERIFY_LEAF_SIGNATURE|ERR_TLS_CERT_ALTNAME_INVALID|CERT_HAS_EXPIRED/i.test(m)) {
231
+ return ("The server's SSL certificate can't be verified (self-signed or from a private CA — Railway, " +
232
+ 'Supabase, Heroku and RDS do this). Set the credential\'s SSL mode to "Require, skip cert" ' +
233
+ '(sslmode=no-verify): the connection stays encrypted, only the certificate check is skipped.');
234
+ }
235
+ if (/server does not support SSL connections/i.test(m)) {
236
+ return ('This server does not accept SSL. Set the credential\'s SSL mode to "Off" (sslmode=disable) — ' +
237
+ 'only for a private network or a tunnel.');
238
+ }
239
+ if (/no pg_hba\.conf entry.*(no encryption|SSL off)|connection is insecure|SSL (connection )?is required|requires? SSL/i.test(m)) {
240
+ return ('This server requires SSL. Set the credential\'s SSL mode to "Require" (sslmode=verify-full), ' +
241
+ 'or "Require, skip cert" (sslmode=no-verify) if its certificate is self-signed.');
242
+ }
243
+ return null;
244
+ }
245
+ /**
246
+ * Los casos con los que se comprueba `ficherosEnCadenaPostgres` contra el
247
+ * driver de verdad (el api y hw-nodes espían `fs.readFileSync` mientras
248
+ * `pg-connection-string` lee cada cadena): `lee` es si el DRIVER abre un
249
+ * fichero. Incluye los dos rodeos medidos el 2026-09-24 —un espacio delante
250
+ * de `/` (el driver no lo toma por socket y lee) y una clave codificada
251
+ * (`ssl%72ootcert`)— y lo que no lee (mayúsculas, sockets, valor vacío).
252
+ * Las rutas empiezan por `/nope` para que el espía las reconozca.
253
+ */
254
+ exports.CASOS_DE_FICHEROS_POSTGRES = [
255
+ { cadena: 'postgres://u:p@h/db?sslrootcert=/nope1', lee: true },
256
+ { cadena: ' /x?sslrootcert=/nope2', lee: true },
257
+ { cadena: 'postgres://u:p@h/db?ssl%72ootcert=/nope3', lee: true },
258
+ { cadena: 'postgres://u:p@h/db?SSLROOTCERT=/nope4', lee: false },
259
+ { cadena: 'socket://u:p@/var/run?sslrootcert=/nope5', lee: false },
260
+ { cadena: '/var/run/postgresql db?sslrootcert=/nope6', lee: false },
261
+ { cadena: 'postgres://u:p@/db?sslkey=/nope7', lee: true },
262
+ { cadena: 'postgres://u:p w@h/db?sslcert=/nope8', lee: true },
263
+ { cadena: 'postgres://u:p@h/db?sslrootcert=/nope9&sslrootcert=', lee: false },
264
+ { cadena: 'postgres://u:p@h/db?sslrootcert=&sslrootcert=/nope10', lee: true },
265
+ { cadena: 'postgres://u:p@h/db?sslmode=require&sslcert=/nope11&sslkey=/nope12', lee: true },
266
+ { cadena: 'postgres://u:p@h/db?sslmode=no-verify&sslpassword=x', lee: false },
267
+ ];
268
+ /**
269
+ * Los parámetros de la cadena que el driver leería como FICHEROS del disco de
270
+ * quien conecta —nuestros servidores—. Vacío si no hay ninguno. Devuelve los
271
+ * NOMBRES, nunca los valores: una ruta no se repite en un mensaje.
272
+ */
273
+ function ficherosEnCadenaPostgres(cadena) {
274
+ const p = parametrosDeCadenaPostgres(cadena);
275
+ if (!p)
276
+ return [];
277
+ return exports.FICHEROS_DE_POSTGRES.filter((k) => p[k]);
278
+ }
279
+ /** El mensaje con su pista, si la tiene. Lo que devuelven los servicios. Si el
280
+ * error ya pasó por aquí (dos capas que lo envuelven), no la repite. */
281
+ function conPistaDeSslPostgres(mensaje) {
282
+ const pista = pistaDeSslPostgres(mensaje);
283
+ if (!pista || mensaje.includes(pista))
284
+ return mensaje;
285
+ return `${mensaje} — ${pista}`;
286
+ }
287
+ /**
288
+ * Los casos con los que se comprueba la regla — aquí y contra el driver de
289
+ * verdad en el api (`el-ssl-de-postgres-es-el-del-driver.spec.ts`). Uno nuevo
290
+ * se añade AQUÍ y lo miran los dos.
291
+ *
292
+ * `esperado`: el modo, `'nada'` si la cadena no dice nada, `'problema'` si no
293
+ * puede funcionar.
294
+ */
295
+ exports.CASOS_DE_SSL_POSTGRES = [
296
+ { cadena: 'postgresql://u:p@h.example:5432/db', esperado: 'nada' },
297
+ { cadena: 'postgresql://u:p@h.example:5432/db?application_name=x', esperado: 'nada' },
298
+ { cadena: 'postgresql://u:p@h.example:5432/db?sslmode=', esperado: 'nada' },
299
+ { cadena: 'postgresql://u:p@h.example:5432/db?ssl=', esperado: 'nada' },
300
+ { cadena: 'postgresql://u:p@h.example:5432/db?SSLMODE=require', esperado: 'nada' },
301
+ { cadena: 'postgresql://u:p@h.example:5432/db?uselibpqcompat=true', esperado: 'nada' },
302
+ { cadena: 'postgresql://u:p@h.example:5432/db?sslmode=disable', esperado: 'off' },
303
+ { cadena: 'postgresql://u:p@h.example:5432/db?sslmode=allow', esperado: 'require' },
304
+ { cadena: 'postgresql://u:p@h.example:5432/db?sslmode=prefer', esperado: 'require' },
305
+ { cadena: 'postgresql://u:p@h.example:5432/db?sslmode=require', esperado: 'require' },
306
+ { cadena: 'postgresql://u:p@h.example:5432/db?sslmode=verify-ca', esperado: 'require' },
307
+ { cadena: 'postgresql://u:p@h.example:5432/db?sslmode=verify-full', esperado: 'require' },
308
+ { cadena: 'postgresql://u:p@h.example:5432/db?sslmode=no-verify', esperado: 'no-verify' },
309
+ { cadena: 'postgresql://u:p@h.example:5432/db?sslmode=REQUIRE', esperado: 'require' },
310
+ { cadena: 'postgresql://u:p@h.example:5432/db?ssl=true', esperado: 'require' },
311
+ { cadena: 'postgresql://u:p@h.example:5432/db?ssl=1', esperado: 'require' },
312
+ { cadena: 'postgresql://u:p@h.example:5432/db?ssl=false', esperado: 'require' },
313
+ { cadena: 'postgresql://u:p@h.example:5432/db?ssl=0', esperado: 'off' },
314
+ { cadena: 'postgresql://u:p@h.example:5432/db?ssl=no-verify', esperado: 'no-verify' },
315
+ { cadena: 'postgresql://u:p@h.example:5432/db?ssl=true&sslmode=disable', esperado: 'off' },
316
+ { cadena: 'postgresql://u:p@h.example:5432/db?sslmode=disable&ssl=true', esperado: 'off' },
317
+ { cadena: 'postgresql://u:p@h.example:5432/db?sslmode=no-verify&ssl=true', esperado: 'no-verify' },
318
+ { cadena: 'postgresql://u:p@h.example:5432/db?sslmode=require&sslmode=disable', esperado: 'off' },
319
+ { cadena: 'postgresql://u:p@h.example:5432/db?uselibpqcompat=true&sslmode=disable', esperado: 'off' },
320
+ { cadena: 'postgresql://u:p@h.example:5432/db?uselibpqcompat=true&sslmode=prefer', esperado: 'no-verify' },
321
+ { cadena: 'postgresql://u:p@h.example:5432/db?uselibpqcompat=true&sslmode=require', esperado: 'no-verify' },
322
+ { cadena: 'postgresql://u:p@h.example:5432/db?uselibpqcompat=true&sslmode=verify-full', esperado: 'require' },
323
+ { cadena: 'postgresql://u:p@h.example:5432/db?uselibpqcompat=true&sslmode=no-verify', esperado: 'require' },
324
+ { cadena: 'postgresql://u:p@h.example:5432/db?uselibpqcompat=true&sslmode=verify-ca', esperado: 'problema' },
325
+ { cadena: 'postgresql://u:p@h.example:5432/db?sslrootcert=/etc/ssl/ca.pem', esperado: 'problema' },
326
+ { cadena: 'postgresql://u:p@h.example:5432/db?sslmode=require&sslkey=key.pem', esperado: 'problema' },
327
+ { cadena: 'postgres://u:p@h.example/db?sslmode=require', esperado: 'require' },
328
+ { cadena: 'postgres://u:p%40x@h.example/db?sslmode=no-verify', esperado: 'no-verify' },
329
+ { cadena: 'postgres://u:p@/db?host=/var/run/postgresql&sslmode=disable', esperado: 'off' },
330
+ /* Varios hosts: `pg` ni la lee (`ERR_INVALID_URL`), así que no hay SSL que
331
+ fijar — falla al conectar con su propio error. */
332
+ { cadena: 'postgres://u:p@h1.example:5432,h2.example:5433/db?sslmode=require', esperado: 'nada' },
333
+ { cadena: 'postgres://u:p w@h.example/db?sslmode=no-verify', esperado: 'no-verify' },
334
+ { cadena: '/var/run/postgresql mydb', esperado: 'nada' },
335
+ { cadena: '', esperado: 'nada' },
336
+ ];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hostwebhook/node-types",
3
- "version": "1.88.0",
3
+ "version": "1.90.0",
4
4
  "description": "Shared node type definitions, connection rules, and dispatch config for HostWebhook",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/esm/index.js",