@hostwebhook/node-types 1.87.0 → 1.89.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/dist/esm/google-contacts-toolkit.d.ts +46 -0
- package/dist/esm/google-contacts-toolkit.js +180 -0
- package/dist/esm/index.d.ts +5 -1
- package/dist/esm/index.js +10 -1
- package/dist/esm/postgres-ssl.d.ts +125 -0
- package/dist/esm/postgres-ssl.js +276 -0
- package/dist/esm/toolkits.d.ts +69 -5
- package/dist/esm/toolkits.js +109 -5
- package/dist/google-contacts-toolkit.d.ts +46 -0
- package/dist/google-contacts-toolkit.js +183 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.js +27 -1
- package/dist/postgres-ssl.d.ts +125 -0
- package/dist/postgres-ssl.js +283 -0
- package/dist/toolkits.d.ts +69 -5
- package/dist/toolkits.js +117 -6
- package/package.json +1 -1
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Google Contacts AI Toolkit — las herramientas que un googleContactsAction
|
|
3
|
+
* expone cuando `aiEnabled` está encendido.
|
|
4
|
+
*
|
|
5
|
+
* **Se escribe una sola vez** (2026-09-24). Vivía dos veces: en
|
|
6
|
+
* `api/src/mcp-servers/toolkit-specs.ts` y en
|
|
7
|
+
* `dashboard/components/google-contacts-actions/google-contacts-operations-schemas.ts`,
|
|
8
|
+
* las dos a mano. Al mudarlas coincidían exactamente —las 15 operaciones, sus
|
|
9
|
+
* nombres, textos y parámetros—; el `group` del dashboard ya lo lleva el spec
|
|
10
|
+
* de formulario (`GOOGLE_CONTACTS_OPERATION_SPECS[op].group`).
|
|
11
|
+
*
|
|
12
|
+
* Se llama `GOOGLE_CONTACTS_TOOLKIT_SPECS` y no `…_OPERATION_SPECS` porque ese
|
|
13
|
+
* nombre ya es el del FORMULARIO del nodo en este paquete — la misma pareja que
|
|
14
|
+
* Discord y Slack.
|
|
15
|
+
*
|
|
16
|
+
* ⚠️ El `destructive` ya no se escribe a mano: lo pone `marcarDestructivas` por
|
|
17
|
+
* el verbo. Cuatro que la copia vieja marcaba dejan de serlo —crear o añadir no
|
|
18
|
+
* pisa nada—: `create_contact`, `add_contact_to_group`,
|
|
19
|
+
* `batch_create_contacts` y `create_contact_group`. El gate del AI Node ya las
|
|
20
|
+
* leía así (por el verbo del nombre); lo que cambia es la etiqueta del panel.
|
|
21
|
+
*
|
|
22
|
+
* Los textos van en inglés porque los lee el modelo.
|
|
23
|
+
*/
|
|
24
|
+
import type { GoogleContactsOperation } from './google-contacts-operations.js';
|
|
25
|
+
export interface GoogleContactsToolkitParameter {
|
|
26
|
+
name: string;
|
|
27
|
+
type: 'string' | 'number' | 'boolean';
|
|
28
|
+
description: string;
|
|
29
|
+
required: boolean;
|
|
30
|
+
}
|
|
31
|
+
export interface GoogleContactsToolkitSpec {
|
|
32
|
+
operation: GoogleContactsOperation;
|
|
33
|
+
/** Etiqueta corta de la fila en la lista de herramientas. */
|
|
34
|
+
label: string;
|
|
35
|
+
/** Nombre con el que el LLM llama a la herramienta. */
|
|
36
|
+
toolName: string;
|
|
37
|
+
/** Descripción (más reglas de uso) que ve el LLM. */
|
|
38
|
+
description: string;
|
|
39
|
+
parameters: GoogleContactsToolkitParameter[];
|
|
40
|
+
/** Pisa o borra. Lo pone `marcarDestructivas` leyendo el verbo del
|
|
41
|
+
* nombre (ver `clase-de-herramienta.ts`), no se escribe a mano. */
|
|
42
|
+
destructive?: boolean;
|
|
43
|
+
}
|
|
44
|
+
export declare const GOOGLE_CONTACTS_TOOLKIT_SPECS: GoogleContactsToolkitSpec[];
|
|
45
|
+
/** Las herramientas por nombre, para despachar una llamada del modelo. */
|
|
46
|
+
export declare const GOOGLE_CONTACTS_TOOLKIT_BY_TOOL_NAME: Record<string, GoogleContactsToolkitSpec>;
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Google Contacts AI Toolkit — las herramientas que un googleContactsAction
|
|
3
|
+
* expone cuando `aiEnabled` está encendido.
|
|
4
|
+
*
|
|
5
|
+
* **Se escribe una sola vez** (2026-09-24). Vivía dos veces: en
|
|
6
|
+
* `api/src/mcp-servers/toolkit-specs.ts` y en
|
|
7
|
+
* `dashboard/components/google-contacts-actions/google-contacts-operations-schemas.ts`,
|
|
8
|
+
* las dos a mano. Al mudarlas coincidían exactamente —las 15 operaciones, sus
|
|
9
|
+
* nombres, textos y parámetros—; el `group` del dashboard ya lo lleva el spec
|
|
10
|
+
* de formulario (`GOOGLE_CONTACTS_OPERATION_SPECS[op].group`).
|
|
11
|
+
*
|
|
12
|
+
* Se llama `GOOGLE_CONTACTS_TOOLKIT_SPECS` y no `…_OPERATION_SPECS` porque ese
|
|
13
|
+
* nombre ya es el del FORMULARIO del nodo en este paquete — la misma pareja que
|
|
14
|
+
* Discord y Slack.
|
|
15
|
+
*
|
|
16
|
+
* ⚠️ El `destructive` ya no se escribe a mano: lo pone `marcarDestructivas` por
|
|
17
|
+
* el verbo. Cuatro que la copia vieja marcaba dejan de serlo —crear o añadir no
|
|
18
|
+
* pisa nada—: `create_contact`, `add_contact_to_group`,
|
|
19
|
+
* `batch_create_contacts` y `create_contact_group`. El gate del AI Node ya las
|
|
20
|
+
* leía así (por el verbo del nombre); lo que cambia es la etiqueta del panel.
|
|
21
|
+
*
|
|
22
|
+
* Los textos van en inglés porque los lee el modelo.
|
|
23
|
+
*/
|
|
24
|
+
import { marcarDestructivas } from './clase-de-herramienta.js';
|
|
25
|
+
const p = (name, description, required = true, type = 'string') => ({ name, type, description, required });
|
|
26
|
+
export const GOOGLE_CONTACTS_TOOLKIT_SPECS = marcarDestructivas([
|
|
27
|
+
// ── Read ─────────────────────────────────────────────────────────
|
|
28
|
+
{
|
|
29
|
+
operation: 'searchContacts',
|
|
30
|
+
label: 'Search contacts',
|
|
31
|
+
toolName: 'search_contacts',
|
|
32
|
+
description: "Free-text search across the user's contacts. Matches names, emails, phones, and organization. Use this BEFORE updateContact / deleteContact / addContactToGroup when the user references a contact by name or email — never invent a resourceName.",
|
|
33
|
+
parameters: [
|
|
34
|
+
p('query', 'Free-text query (name fragment, email, phone, organization).'),
|
|
35
|
+
p('pageSize', 'Max results (default 30, hard cap 30).', false, 'number'),
|
|
36
|
+
],
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
operation: 'listContacts',
|
|
40
|
+
label: 'List contacts',
|
|
41
|
+
toolName: 'list_contacts',
|
|
42
|
+
description: 'Paginated list of every contact. Prefer searchContacts for name/email lookup — listContacts returns hundreds of rows even when the user named one.',
|
|
43
|
+
parameters: [
|
|
44
|
+
p('pageSize', 'Page size (default 100, max 1000).', false, 'number'),
|
|
45
|
+
p('pageToken', 'Token returned by previous listContacts. Empty for first page.', false),
|
|
46
|
+
],
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
operation: 'getContact',
|
|
50
|
+
label: 'Get contact',
|
|
51
|
+
toolName: 'get_contact',
|
|
52
|
+
description: 'Fetch a single contact by resourceName. Use AFTER searchContacts to read the full record before updating.',
|
|
53
|
+
parameters: [
|
|
54
|
+
p('resourceName', 'People API id, e.g. "people/c12345". Get from searchContacts results.'),
|
|
55
|
+
],
|
|
56
|
+
},
|
|
57
|
+
// ── Write ────────────────────────────────────────────────────────
|
|
58
|
+
{
|
|
59
|
+
operation: 'createContact',
|
|
60
|
+
label: 'Create contact',
|
|
61
|
+
toolName: 'create_contact',
|
|
62
|
+
description: "Add a new contact to the user's address book. NEVER call this without confirming with the user first — duplicate contacts are messy to clean up. Pass `fullName` and the helper auto-splits given/family. Pass `emailAddresses` and `phoneNumbers` as arrays of strings.",
|
|
63
|
+
parameters: [
|
|
64
|
+
p('fullName', 'Full name (e.g. "Ariel Pérez"). Auto-splits to given + family.', false),
|
|
65
|
+
p('givenName', 'First name. Overrides the fullName split.', false),
|
|
66
|
+
p('familyName', 'Last name. Overrides the fullName split.', false),
|
|
67
|
+
p('emailAddresses', 'Array of email addresses. JSON-encode if your tool layer expects a string.', false),
|
|
68
|
+
p('phoneNumbers', 'Array of phone numbers.', false),
|
|
69
|
+
p('notes', 'Free-form note attached to the contact.', false),
|
|
70
|
+
],
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
operation: 'updateContact',
|
|
74
|
+
label: 'Update contact',
|
|
75
|
+
toolName: 'update_contact',
|
|
76
|
+
description: "Change fields on an existing contact. Get the resourceName from searchContacts first. Update mask is auto-derived from the fields you pass — only include fields you want changed (passing `notes` won't blank out the email).",
|
|
77
|
+
parameters: [
|
|
78
|
+
p('resourceName', 'People API id, e.g. "people/c12345".'),
|
|
79
|
+
p('fullName', 'New full name (auto-splits).', false),
|
|
80
|
+
p('emailAddresses', 'Replace email list. Pass [] to clear.', false),
|
|
81
|
+
p('phoneNumbers', 'Replace phone list.', false),
|
|
82
|
+
p('notes', "Replace the contact's note.", false),
|
|
83
|
+
],
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
operation: 'deleteContact',
|
|
87
|
+
label: 'Delete contact',
|
|
88
|
+
toolName: 'delete_contact',
|
|
89
|
+
description: 'Permanently remove a contact. Irreversible. ALWAYS confirm with the user before calling. Use searchContacts first to verify you have the right resourceName.',
|
|
90
|
+
parameters: [p('resourceName', 'People API id of the contact to delete.')],
|
|
91
|
+
},
|
|
92
|
+
// ── Groups ───────────────────────────────────────────────────────
|
|
93
|
+
{
|
|
94
|
+
operation: 'listContactGroups',
|
|
95
|
+
label: 'List groups',
|
|
96
|
+
toolName: 'list_contact_groups',
|
|
97
|
+
description: "List the user's contact groups (labels). Call this BEFORE addContactToGroup / removeContactFromGroup so you have the right groupResourceName.",
|
|
98
|
+
parameters: [
|
|
99
|
+
p('pageSize', 'Page size (default 200, max 1000).', false, 'number'),
|
|
100
|
+
],
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
operation: 'addContactToGroup',
|
|
104
|
+
label: 'Add to group',
|
|
105
|
+
toolName: 'add_contact_to_group',
|
|
106
|
+
description: 'Add a contact to a group (label). Idempotent — no error if the contact is already in the group. Get the groupResourceName from listContactGroups.',
|
|
107
|
+
parameters: [
|
|
108
|
+
p('resourceName', 'Contact id, e.g. "people/c12345". Or array of contact ids.'),
|
|
109
|
+
p('groupResourceName', 'Group id, e.g. "contactGroups/12345".'),
|
|
110
|
+
],
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
operation: 'removeContactFromGroup',
|
|
114
|
+
label: 'Remove from group',
|
|
115
|
+
toolName: 'remove_contact_from_group',
|
|
116
|
+
description: 'Remove a contact from a group. Idempotent. Does NOT delete the contact, only its membership in that group.',
|
|
117
|
+
parameters: [
|
|
118
|
+
p('resourceName', 'Contact id, e.g. "people/c12345".'),
|
|
119
|
+
p('groupResourceName', 'Group id, e.g. "contactGroups/12345".'),
|
|
120
|
+
],
|
|
121
|
+
},
|
|
122
|
+
// ── V2 — bulk + group management ─────────────────────────────────
|
|
123
|
+
{
|
|
124
|
+
operation: 'batchCreateContacts',
|
|
125
|
+
label: 'Batch create contacts',
|
|
126
|
+
toolName: 'batch_create_contacts',
|
|
127
|
+
description: 'Bulk-create up to 200 contacts in one call. Each contact in the array uses the same shape as createContact (fullName / emailAddresses / phoneNumbers / notes / etc.).',
|
|
128
|
+
parameters: [
|
|
129
|
+
p('contacts', 'Array of contact objects. Same shape as createContact.', true),
|
|
130
|
+
],
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
operation: 'batchUpdateContacts',
|
|
134
|
+
label: 'Batch update contacts',
|
|
135
|
+
toolName: 'batch_update_contacts',
|
|
136
|
+
description: 'Bulk-update up to 200 existing contacts. Each entry must include `resourceName`. Pass `updateMask` (e.g. "names,emailAddresses") to control which fields the API replaces.',
|
|
137
|
+
parameters: [
|
|
138
|
+
p('contacts', 'Array of {resourceName, ...fields} objects.', true),
|
|
139
|
+
p('updateMask', 'Comma-separated People API field paths to replace.'),
|
|
140
|
+
],
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
operation: 'batchDeleteContacts',
|
|
144
|
+
label: 'Batch delete contacts',
|
|
145
|
+
toolName: 'batch_delete_contacts',
|
|
146
|
+
description: 'Bulk-delete up to 500 contacts by resourceName. Irreversible.',
|
|
147
|
+
parameters: [
|
|
148
|
+
p('resourceNames', 'Array of People API ids, each like "people/c12345".'),
|
|
149
|
+
],
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
operation: 'createContactGroup',
|
|
153
|
+
label: 'Create group',
|
|
154
|
+
toolName: 'create_contact_group',
|
|
155
|
+
description: 'Create a new contact group (label).',
|
|
156
|
+
parameters: [p('name', 'Group name as displayed in Contacts.')],
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
operation: 'updateContactGroup',
|
|
160
|
+
label: 'Rename group',
|
|
161
|
+
toolName: 'update_contact_group',
|
|
162
|
+
description: 'Rename an existing contact group.',
|
|
163
|
+
parameters: [
|
|
164
|
+
p('resourceName', 'Group id, e.g. "contactGroups/12345".'),
|
|
165
|
+
p('name', 'New name for the group.'),
|
|
166
|
+
],
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
operation: 'deleteContactGroup',
|
|
170
|
+
label: 'Delete group',
|
|
171
|
+
toolName: 'delete_contact_group',
|
|
172
|
+
description: 'Delete a contact group. Pass `deleteContacts: true` to also delete the contacts in the group (otherwise contacts stay, just lose this membership). Irreversible — confirm with the user before calling.',
|
|
173
|
+
parameters: [
|
|
174
|
+
p('resourceName', 'Group id, e.g. "contactGroups/12345".'),
|
|
175
|
+
p('deleteContacts', 'When true, also delete contacts in the group. Default false.', false, 'boolean'),
|
|
176
|
+
],
|
|
177
|
+
},
|
|
178
|
+
]);
|
|
179
|
+
/** Las herramientas por nombre, para despachar una llamada del modelo. */
|
|
180
|
+
export const GOOGLE_CONTACTS_TOOLKIT_BY_TOOL_NAME = Object.fromEntries(GOOGLE_CONTACTS_TOOLKIT_SPECS.map((s) => [s.toolName, s]));
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -76,8 +76,12 @@ export { POSTGRES_TOOLKIT_SPECS, POSTGRES_TOOLKIT_BY_TOOL_NAME, } from './postgr
|
|
|
76
76
|
export type { PostgresToolkitSpec, PostgresToolkitParameter, } from './postgres-toolkit.js';
|
|
77
77
|
export { claseDeHerramienta, esHerramientaDestructiva, marcarDestructivas, } from './clase-de-herramienta.js';
|
|
78
78
|
export type { ClaseDeHerramienta } from './clase-de-herramienta.js';
|
|
79
|
-
export { TOOLKITS_POR_TIPO, operacionDeToolkit, esOperacionDestructiva, } from './toolkits.js';
|
|
79
|
+
export { TOOLKITS_POR_TIPO, operacionDeToolkit, esOperacionDestructiva, TIPOS_CON_TOOLKIT, tieneModoToolkit, admiteBloqueo, nombresDeHerramientas, bloqueadasDe, herramientaBloqueadaEn, MENSAJE_SIN_HERRAMIENTA, mensajeDeHerramientaBloqueada, motivoParaNoCorrer, } from './toolkits.js';
|
|
80
|
+
export { GOOGLE_CONTACTS_TOOLKIT_SPECS, GOOGLE_CONTACTS_TOOLKIT_BY_TOOL_NAME, } from './google-contacts-toolkit.js';
|
|
81
|
+
export type { GoogleContactsToolkitSpec, GoogleContactsToolkitParameter, } from './google-contacts-toolkit.js';
|
|
80
82
|
export type { OperacionDeToolkit } from './toolkits.js';
|
|
83
|
+
export { leerSslPostgres, ponerSslPostgres, pistaDeSslPostgres, conPistaDeSslPostgres, CASOS_DE_SSL_POSTGRES, } from './postgres-ssl.js';
|
|
84
|
+
export type { ModoSslPostgres, SslDeLaCadenaPostgres } from './postgres-ssl.js';
|
|
81
85
|
export type { LlmProvider, LlmProviderOption, LlmModelOption, } from './llm-models.js';
|
|
82
86
|
export { LLM_PROVIDERS, LLM_MODELS, MODEL_CONTEXT_WINDOWS, getModelsFor, getDefaultModel, getModelLabel, } from './llm-models.js';
|
|
83
87
|
export type { ModeloDeOpenRouter, PrecioDeOpenRouter, RespuestaDeModelosDeOpenRouter, } from './openrouter.js';
|
package/dist/esm/index.js
CHANGED
|
@@ -57,7 +57,16 @@ export { DOCS_TOOLKIT_SPECS, DOCS_TOOLKIT_BY_TOOL_NAME, DOCS_TOOLKIT_DEFAULTABLE
|
|
|
57
57
|
export { MONGO_TOOLKIT_SPECS, MONGO_TOOLKIT_BY_TOOL_NAME, } from './mongo-toolkit.js';
|
|
58
58
|
export { POSTGRES_TOOLKIT_SPECS, POSTGRES_TOOLKIT_BY_TOOL_NAME, } from './postgres-toolkit.js';
|
|
59
59
|
export { claseDeHerramienta, esHerramientaDestructiva, marcarDestructivas, } from './clase-de-herramienta.js';
|
|
60
|
-
export { TOOLKITS_POR_TIPO, operacionDeToolkit, esOperacionDestructiva, } from './toolkits.js';
|
|
60
|
+
export { TOOLKITS_POR_TIPO, operacionDeToolkit, esOperacionDestructiva, TIPOS_CON_TOOLKIT, tieneModoToolkit, admiteBloqueo, nombresDeHerramientas, bloqueadasDe, herramientaBloqueadaEn, MENSAJE_SIN_HERRAMIENTA, mensajeDeHerramientaBloqueada, motivoParaNoCorrer, } from './toolkits.js';
|
|
61
|
+
/* Contacts se escribía a mano en la api y en el dashboard; ahora una vez. Se
|
|
62
|
+
llama `…_TOOLKIT_SPECS` porque `GOOGLE_CONTACTS_OPERATION_SPECS` ya es el
|
|
63
|
+
formulario del nodo — la misma pareja que Discord y Slack. */
|
|
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, } from './postgres-ssl.js';
|
|
61
70
|
export { LLM_PROVIDERS, LLM_MODELS, MODEL_CONTEXT_WINDOWS, getModelsFor, getDefaultModel, getModelLabel, } from './llm-models.js';
|
|
62
71
|
export { URL_DE_MODELOS_DE_OPENROUTER, opcionesDeModelosDeOpenRouter, ventanaDeContextoDeOpenRouter, } from './openrouter.js';
|
|
63
72
|
export { CREDENTIAL_TYPES, CREDENTIAL_TYPE_VALUES, credentialTypeValues, getCredentialType, isCredentialType, } from './credentials.js';
|
|
@@ -0,0 +1,125 @@
|
|
|
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
|
+
* Qué hará el driver con el SSL de esta cadena.
|
|
74
|
+
*
|
|
75
|
+
* `{ dice: false }` cuando la cadena no dice nada: ni `ssl`, ni `sslmode`, ni
|
|
76
|
+
* ficheros (un `sslmode=` o `ssl=` vacío tampoco dicen nada: el driver los
|
|
77
|
+
* ignora).
|
|
78
|
+
*/
|
|
79
|
+
export declare function leerSslPostgres(cadena: string): SslDeLaCadenaPostgres;
|
|
80
|
+
/**
|
|
81
|
+
* Escribe el modo elegido en la cadena, quitando antes `ssl` y `sslmode` (en
|
|
82
|
+
* una cadena que «no dice nada», los que hubiera están vacíos). No toca el
|
|
83
|
+
* resto de parámetros, ni los recodifica.
|
|
84
|
+
*
|
|
85
|
+
* Lo que escribe, y por qué:
|
|
86
|
+
* - `off` → `sslmode=disable`, explícito: la cadena guardada dice lo
|
|
87
|
+
* que hace sin depender de `PGSSLMODE`.
|
|
88
|
+
* - `require` → `sslmode=verify-full`, no `require`: para `pg` 8 es lo
|
|
89
|
+
* mismo, pero `require` avisa en cada conexión de que en la
|
|
90
|
+
* versión 9 pasará a NO comprobar. `verify-full` significa
|
|
91
|
+
* lo mismo en las dos.
|
|
92
|
+
* - `no-verify` → `sslmode=no-verify`; con `uselibpqcompat=true`, que no lo
|
|
93
|
+
* conoce, `sslmode=require` (lo mismo en libpq).
|
|
94
|
+
*
|
|
95
|
+
* Una cadena vacía se devuelve tal cual.
|
|
96
|
+
*/
|
|
97
|
+
export declare function ponerSslPostgres(cadena: string, modo: ModoSslPostgres): string;
|
|
98
|
+
/**
|
|
99
|
+
* La frase que le falta a un error de conexión de SSL: qué cambiar. `null` si
|
|
100
|
+
* el error no es de SSL.
|
|
101
|
+
*
|
|
102
|
+
* Los tres casos que se ven al conectar:
|
|
103
|
+
* - el certificado no se puede comprobar (autofirmado, CA privada, nombre
|
|
104
|
+
* que no casa): Railway (`CN=localhost` de su `root-ca`) y Supabase (su
|
|
105
|
+
* `Supabase Root 2021 CA`) —medidos los dos el 2026-09-24—, Heroku y
|
|
106
|
+
* RDS. Neon sí se comprueba (Let's Encrypt);
|
|
107
|
+
* - el servidor no acepta SSL;
|
|
108
|
+
* - el servidor exige SSL y se conectó sin él.
|
|
109
|
+
*/
|
|
110
|
+
export declare function pistaDeSslPostgres(mensaje: string): string | null;
|
|
111
|
+
/** El mensaje con su pista, si la tiene. Lo que devuelven los servicios. Si el
|
|
112
|
+
* error ya pasó por aquí (dos capas que lo envuelven), no la repite. */
|
|
113
|
+
export declare function conPistaDeSslPostgres(mensaje: string): string;
|
|
114
|
+
/**
|
|
115
|
+
* Los casos con los que se comprueba la regla — aquí y contra el driver de
|
|
116
|
+
* verdad en el api (`el-ssl-de-postgres-es-el-del-driver.spec.ts`). Uno nuevo
|
|
117
|
+
* se añade AQUÍ y lo miran los dos.
|
|
118
|
+
*
|
|
119
|
+
* `esperado`: el modo, `'nada'` si la cadena no dice nada, `'problema'` si no
|
|
120
|
+
* puede funcionar.
|
|
121
|
+
*/
|
|
122
|
+
export declare const CASOS_DE_SSL_POSTGRES: ReadonlyArray<{
|
|
123
|
+
cadena: string;
|
|
124
|
+
esperado: ModoSslPostgres | 'nada' | 'problema';
|
|
125
|
+
}>;
|