@hostwebhook/node-types 1.76.0 → 1.78.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/README.md CHANGED
Binary file
package/dist/dispatch.js CHANGED
@@ -39,31 +39,36 @@ exports.NODE_DISPATCH = {
39
39
  // ── Routing — custom dispatch ──
40
40
  router: { service: 'routersService', collection: 'routers', hasFilters: false, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'custom', downstreamPayload: 'result' },
41
41
  // ── Action nodes — fired in onDeliveryResult, excluded from webhook dispatch ──
42
- emailAction: { service: 'emailActionsService', collection: 'emailactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'original' },
43
- gmailAction: { service: 'gmailActionsService', collection: 'gmailactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'original' },
42
+ emailAction: { service: 'emailActionsService', collection: 'emailactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'result' },
43
+ gmailAction: { service: 'gmailActionsService', collection: 'gmailactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'result' },
44
44
  httpAction: { service: 'httpActionsService', collection: 'httpactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'result' },
45
45
  mongoAction: { service: 'mongoActionsService', collection: 'mongoactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'result' },
46
46
  postgresAction: { service: 'postgresActionsService', collection: 'postgresactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'result' },
47
- notificationAction: { service: 'notificationActionsService', collection: 'notificationactions', hasFilters: true, outputFields: [], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'original' },
47
+ notificationAction: { service: 'notificationActionsService', collection: 'notificationactions', hasFilters: true, outputFields: [], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'result' },
48
48
  sheetsAction: { service: 'sheetsActionsService', collection: 'sheetsactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'result' },
49
49
  calendarAction: { service: 'calendarActionsService', collection: 'calendaractions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'result' },
50
50
  docsAction: { service: 'docsActionsService', collection: 'docsactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'result' },
51
51
  driveAction: { service: 'driveActionsService', collection: 'driveactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'result' },
52
52
  firecrawlAction: { service: 'firecrawlActionsService', collection: 'firecrawlactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'result' },
53
- telegramAction: { service: 'telegramActionsService', collection: 'telegramactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'original' },
54
- whatsappAction: { service: 'whatsappActionsService', collection: 'whatsappactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'original' },
55
- discordAction: { service: 'discordActionsService', collection: 'discordactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'original' },
53
+ telegramAction: { service: 'telegramActionsService', collection: 'telegramactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'result' },
54
+ whatsappAction: { service: 'whatsappActionsService', collection: 'whatsappactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'result' },
55
+ /* `'result'` y no `'original'` por decisión del dueño: lo que tiene que bajar
56
+ de un Discord es SU acuse —`messageId`, `channelId`, `content`— y no el
57
+ evento que le entró. Ver `discord-actions.service.ts`, donde está el coste
58
+ escrito: un flujo que leyera `{{payload.<campo del evento>}}` después de un
59
+ Discord ahora lee el acuse. */
60
+ discordAction: { service: 'discordActionsService', collection: 'discordactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'result' },
56
61
  mailchimpAction: { service: 'mailchimpActionsService', collection: 'mailchimpactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'result' },
57
62
  shopifyAction: { service: 'shopifyActionsService', collection: 'shopifyactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'result' },
58
63
  githubAction: { service: 'githubActionsService', collection: 'githubactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'result' },
59
64
  jiraAction: { service: 'jiraActionsService', collection: 'jiraactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'result' },
60
65
  bucketAction: { service: 'bucketActionsService', collection: 'bucketactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'result' },
61
- slackAction: { service: 'slackActionsService', collection: 'slackactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'original' },
66
+ slackAction: { service: 'slackActionsService', collection: 'slackactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'result' },
62
67
  googleContactsAction: { service: 'googleContactsActionsService', collection: 'googlecontactsactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'result' },
63
68
  googleAnalyticsAction: { service: 'googleAnalyticsActionsService', collection: 'googleanalyticsactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'result' },
64
69
  notionAction: { service: 'notionActionsService', collection: 'notionactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'result' },
65
70
  rssAction: { service: 'rssActionsService', collection: 'rssactions', hasFilters: false, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'result' },
66
- socialMediaAction: { service: 'socialMediaActionsService', collection: 'socialmediaactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'original' },
71
+ socialMediaAction: { service: 'socialMediaActionsService', collection: 'socialmediaactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'result' },
67
72
  // ── Sources — start the flow themselves, not dispatched ──
68
73
  webhook: { service: 'webhooksService', collection: 'webhooks', hasFilters: false, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'result' },
69
74
  scheduledWorkflow: { service: 'scheduledWorkflowsService', collection: 'scheduledworkflows', hasFilters: false, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'result' },
@@ -33,31 +33,36 @@ export const NODE_DISPATCH = {
33
33
  // ── Routing — custom dispatch ──
34
34
  router: { service: 'routersService', collection: 'routers', hasFilters: false, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'custom', downstreamPayload: 'result' },
35
35
  // ── Action nodes — fired in onDeliveryResult, excluded from webhook dispatch ──
36
- emailAction: { service: 'emailActionsService', collection: 'emailactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'original' },
37
- gmailAction: { service: 'gmailActionsService', collection: 'gmailactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'original' },
36
+ emailAction: { service: 'emailActionsService', collection: 'emailactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'result' },
37
+ gmailAction: { service: 'gmailActionsService', collection: 'gmailactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'result' },
38
38
  httpAction: { service: 'httpActionsService', collection: 'httpactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'result' },
39
39
  mongoAction: { service: 'mongoActionsService', collection: 'mongoactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'result' },
40
40
  postgresAction: { service: 'postgresActionsService', collection: 'postgresactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'result' },
41
- notificationAction: { service: 'notificationActionsService', collection: 'notificationactions', hasFilters: true, outputFields: [], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'original' },
41
+ notificationAction: { service: 'notificationActionsService', collection: 'notificationactions', hasFilters: true, outputFields: [], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'result' },
42
42
  sheetsAction: { service: 'sheetsActionsService', collection: 'sheetsactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'result' },
43
43
  calendarAction: { service: 'calendarActionsService', collection: 'calendaractions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'result' },
44
44
  docsAction: { service: 'docsActionsService', collection: 'docsactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'result' },
45
45
  driveAction: { service: 'driveActionsService', collection: 'driveactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'result' },
46
46
  firecrawlAction: { service: 'firecrawlActionsService', collection: 'firecrawlactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'result' },
47
- telegramAction: { service: 'telegramActionsService', collection: 'telegramactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'original' },
48
- whatsappAction: { service: 'whatsappActionsService', collection: 'whatsappactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'original' },
49
- discordAction: { service: 'discordActionsService', collection: 'discordactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'original' },
47
+ telegramAction: { service: 'telegramActionsService', collection: 'telegramactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'result' },
48
+ whatsappAction: { service: 'whatsappActionsService', collection: 'whatsappactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'result' },
49
+ /* `'result'` y no `'original'` por decisión del dueño: lo que tiene que bajar
50
+ de un Discord es SU acuse —`messageId`, `channelId`, `content`— y no el
51
+ evento que le entró. Ver `discord-actions.service.ts`, donde está el coste
52
+ escrito: un flujo que leyera `{{payload.<campo del evento>}}` después de un
53
+ Discord ahora lee el acuse. */
54
+ discordAction: { service: 'discordActionsService', collection: 'discordactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'result' },
50
55
  mailchimpAction: { service: 'mailchimpActionsService', collection: 'mailchimpactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'result' },
51
56
  shopifyAction: { service: 'shopifyActionsService', collection: 'shopifyactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'result' },
52
57
  githubAction: { service: 'githubActionsService', collection: 'githubactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'result' },
53
58
  jiraAction: { service: 'jiraActionsService', collection: 'jiraactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'result' },
54
59
  bucketAction: { service: 'bucketActionsService', collection: 'bucketactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'result' },
55
- slackAction: { service: 'slackActionsService', collection: 'slackactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'original' },
60
+ slackAction: { service: 'slackActionsService', collection: 'slackactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'result' },
56
61
  googleContactsAction: { service: 'googleContactsActionsService', collection: 'googlecontactsactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'result' },
57
62
  googleAnalyticsAction: { service: 'googleAnalyticsActionsService', collection: 'googleanalyticsactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'result' },
58
63
  notionAction: { service: 'notionActionsService', collection: 'notionactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'result' },
59
64
  rssAction: { service: 'rssActionsService', collection: 'rssactions', hasFilters: false, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'result' },
60
- socialMediaAction: { service: 'socialMediaActionsService', collection: 'socialmediaactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'original' },
65
+ socialMediaAction: { service: 'socialMediaActionsService', collection: 'socialmediaactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'result' },
61
66
  // ── Sources — start the flow themselves, not dispatched ──
62
67
  webhook: { service: 'webhooksService', collection: 'webhooks', hasFilters: false, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'result' },
63
68
  scheduledWorkflow: { service: 'scheduledWorkflowsService', collection: 'scheduledworkflows', hasFilters: false, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'result' },
@@ -66,3 +66,5 @@ export type { ModeloDeOpenRouter, PrecioDeOpenRouter, RespuestaDeModelosDeOpenRo
66
66
  export { URL_DE_MODELOS_DE_OPENROUTER, opcionesDeModelosDeOpenRouter, ventanaDeContextoDeOpenRouter, } from './openrouter.js';
67
67
  export type { CredentialTypeRegistration, CredentialType } from './credentials.js';
68
68
  export { CREDENTIAL_TYPES, CREDENTIAL_TYPE_VALUES, credentialTypeValues, getCredentialType, isCredentialType, } from './credentials.js';
69
+ export type { NodoQueContestaEnSync, ResultadoDelValidador, FalloDeValidacion, VeredictoDelValidador, NivelDeDetalle, ModoDePayload, SobreDeRespuesta, TopeDeNumero, RespuestaDelSync, RespuestaHttp, } from './respuesta-del-sync.js';
70
+ export { NODOS_QUE_CONTESTAN_EN_SYNC, contestaEnSync, RESULTADOS_DEL_VALIDADOR, NIVELES_DE_DETALLE, MODOS_DE_PAYLOAD, SOBRES_DE_RESPUESTA, TOPES_DE_ESTADO, TOPE_DE_ESPERA_DEL_SYNC, RESPUESTA_DEL_SYNC_POR_DEFECTO, armarRespuestaDelSync, } from './respuesta-del-sync.js';
package/dist/esm/index.js CHANGED
@@ -51,3 +51,4 @@ export { DOCS_TOOLKIT_SPECS, DOCS_TOOLKIT_BY_TOOL_NAME, DOCS_TOOLKIT_DEFAULTABLE
51
51
  export { LLM_PROVIDERS, LLM_MODELS, MODEL_CONTEXT_WINDOWS, getModelsFor, getDefaultModel, getModelLabel, } from './llm-models.js';
52
52
  export { URL_DE_MODELOS_DE_OPENROUTER, opcionesDeModelosDeOpenRouter, ventanaDeContextoDeOpenRouter, } from './openrouter.js';
53
53
  export { CREDENTIAL_TYPES, CREDENTIAL_TYPE_VALUES, credentialTypeValues, getCredentialType, isCredentialType, } from './credentials.js';
54
+ export { NODOS_QUE_CONTESTAN_EN_SYNC, contestaEnSync, RESULTADOS_DEL_VALIDADOR, NIVELES_DE_DETALLE, MODOS_DE_PAYLOAD, SOBRES_DE_RESPUESTA, TOPES_DE_ESTADO, TOPE_DE_ESPERA_DEL_SYNC, RESPUESTA_DEL_SYNC_POR_DEFECTO, armarRespuestaDelSync, } from './respuesta-del-sync.js';
@@ -0,0 +1,353 @@
1
+ /**
2
+ * La respuesta que recibe quien llama a un webhook en modo `sync`, armada en
3
+ * UN solo sitio.
4
+ *
5
+ * ## Qué problema resuelve
6
+ *
7
+ * El dueño quiere unas pestañas para configurar qué recibe el cliente y una
8
+ * VISTA PREVIA que enseñe los bytes exactos. Una vista previa sólo vale si no
9
+ * puede desincronizarse del servidor: si son dos implementaciones, el día que
10
+ * difieran la pantalla miente con toda la confianza del mundo, y miente sobre
11
+ * lo único que el usuario no puede comprobar desde ahí.
12
+ *
13
+ * Ya hay un caso así en el dashboard —`FORMAT_OPTIONS`, cuatro formatos
14
+ * copiados a mano en `schema-validators/[id]/page.tsx` cuando el paquete
15
+ * publica dieciocho— y ahí se asume porque desincronizarse AVISA: el campo
16
+ * deja de ofrecer un formato que sí existe y alguien lo pide. Aquí no avisaría
17
+ * nada. La previa diría 422 con tres errores, el servidor mandaría otra cosa,
18
+ * y nadie se enteraría hasta que un cliente se quejara.
19
+ *
20
+ * Así que la función de abajo es la ÚNICA que sabe montar estos bytes. La
21
+ * llaman los tres: el handler del validador en hw-nodes, el endpoint de previa
22
+ * de la api, y el dashboard para pintarla.
23
+ *
24
+ * ## Por qué este paquete y no otro — medido el 2026-09-10
25
+ *
26
+ * Los tres candidatos, con los números delante:
27
+ *
28
+ * | | `platform-contracts` | `node-sdk` | **`node-types`** |
29
+ * |---|---|---|---|
30
+ * | lo pinea el dashboard | `^0.2.0` (0.2.0 instalada) | no es dependencia suya | `^1.74.0` (1.74.0 instalada) |
31
+ * | ¿el caret llega a lo nuevo? | **NO** — en `0.x` no cruza el minor: habría que subir a `^0.16` a mano | — | **SÍ** — en `1.x` sí lo cruza; basta refrescar el lock |
32
+ * | dependencias de ejecución | `dependencies: {}` **pero** su barril hace `require('@nestjs/common')` de verdad | mongoose, mongodb, express, re2 (binario nativo) | **ninguna: ni deps ni peers** |
33
+ * | ¿sirve en el navegador? | **NO** hoy | no | **sí** |
34
+ * | ficheros del dashboard que ya lo importan | 6 | 0 | **92** |
35
+ *
36
+ * ⚠️ Lo de `platform-contracts` conviene leerlo dos veces, porque la primera
37
+ * medición decía lo contrario. `dependencies: {}` es cierto, y por eso parecía
38
+ * servible en el navegador. Pero un PEER también se ejecuta: desde 0.3.0 el
39
+ * paquete trae `servicios/credenciales-del-gateway`, que importa
40
+ * `BadRequestException` como VALOR, y eso sale en el `dist` como un
41
+ * `require("@nestjs/common")` al que se llega desde `dist/index.js` —barril de
42
+ * CommonJS, sin mapa `exports` y sin `sideEffects`, o sea que no se sacude—.
43
+ * El dashboard entra por ese barril (`lib/store/hooks.ts` pide `tieneAddon`
44
+ * como valor, no como tipo), así que subirlo metería ~9,5 MB de framework de
45
+ * servidor —@nestjs/common 1,1 MB, rxjs 8,1 MB, reflect-metadata 265 kB— en un
46
+ * Next. Lo que NO es el problema, y también se midió: entre 0.2.0 y 0.15.0
47
+ * `addons.ts` y `operadores.ts` están byte a byte idénticos, así que de los 13
48
+ * minors de salto NO sale ni un breaking para lo que el dashboard usa hoy.
49
+ *
50
+ * Y aquí, además, cae en el sitio correcto por significado: la constante del
51
+ * gate es una lista de `NodeType`, y `NodeType` vive en este fichero de al
52
+ * lado. En cualquier otro paquete sería una lista de cadenas sueltas que nadie
53
+ * comprueba contra el catálogo de nodos.
54
+ */
55
+ /**
56
+ * Los tipos de nodo que SABEN contestar en `sync`.
57
+ *
58
+ * Lo lee la api al guardar —para no dejar activar `responseMode: 'sync'` en un
59
+ * flujo que no lleva ninguno— y el dashboard para avisar antes de guardar.
60
+ *
61
+ * 🔥 Es una CONSTANTE y no el literal `'schemaValidator'` repartido por los dos
62
+ * sitios, y la razón no es estética: el día que un segundo nodo implemente
63
+ * `getSyncResult`, con literales el gate sigue compilando, sigue pasando los
64
+ * tests y **se queda mintiendo en silencio** —le dice al usuario que su flujo
65
+ * no puede contestar cuando sí puede—. Un fallo que no rompe nada es el que
66
+ * sobrevive años.
67
+ *
68
+ * ⚠️ Y lo que esta lista NO puede comprobar desde aquí: que sea la misma que
69
+ * la de los handlers que de verdad implementan `getSyncResult`. Esos viven en
70
+ * hw-nodes, que este paquete no ve ni debe ver. El guardián de esa atadura va
71
+ * ALLÍ, que es donde están los handlers — el mismo reparto que ya usa
72
+ * `pasaLoQueRecibe`: la tabla se fija aquí y
73
+ * `el-panel-lee-lo-mismo-que-el-handler.spec.ts` la ata en hw-nodes.
74
+ *
75
+ * ⚠️⚠️ Presencia no es paso. Que el flujo LLEVE un validador no garantiza que
76
+ * el evento pase por él: un conditional o un filter puede rutear alrededor. Por
77
+ * eso el gate es de producto y no de corrección, y por eso `'no-verdict'`
78
+ * existe ahí abajo — sin él, el flujo con el gate puesto se colgaría igual.
79
+ */
80
+ export declare const NODOS_QUE_CONTESTAN_EN_SYNC: readonly ["schemaValidator"];
81
+ /** Un tipo de nodo de los que saben contestar en `sync`. */
82
+ export type NodoQueContestaEnSync = (typeof NODOS_QUE_CONTESTAN_EN_SYNC)[number];
83
+ /**
84
+ * ¿Este tipo de nodo sabe contestar en `sync`?
85
+ *
86
+ * Acepta `string` y no `NodeType` a propósito: quien pregunta es la api con lo
87
+ * que trae un documento de mongo y el dashboard con lo que hay en el lienzo, y
88
+ * los dos manejan cadenas que todavía no han pasado por ningún tipo.
89
+ */
90
+ export declare function contestaEnSync(tipo: string): boolean;
91
+ /**
92
+ * Los cuatro finales posibles de una espera en `sync`. Son cuatro y no dos, y
93
+ * los dos de en medio son los que hoy faltan:
94
+ *
95
+ * - `valid` — el payload pasó. **Esto es lo que hoy no contesta nadie**: el
96
+ * `getSyncResult` del validador devuelve `null` cuando todo está bien y el
97
+ * paso 8.5 de `node-lifecycle.ts` sólo resuelve `if (syncResult)`. O sea que
98
+ * el camino FELIZ es el que se cuelga 120 s y sale con un 408. Pasar la
99
+ * validación era indistinguible de que no contestara nadie.
100
+ * - `invalid` — el payload no pasó. El único que funciona hoy.
101
+ * - `no-verdict` — la corrida TERMINÓ y ningún validador se pronunció (nadie
102
+ * pasó por él, o el flujo no lleva ninguno). Hay que contestar igual, ahí
103
+ * mismo, en vez de esperar el tope entero.
104
+ * - `timeout` — se acabó la espera de verdad.
105
+ */
106
+ export declare const RESULTADOS_DEL_VALIDADOR: readonly ["valid", "invalid", "no-verdict", "timeout"];
107
+ export type ResultadoDelValidador = (typeof RESULTADOS_DEL_VALIDADOR)[number];
108
+ /**
109
+ * Un campo que no pasó.
110
+ *
111
+ * `path` y `message` son lo que `validateSchema` produce hoy
112
+ * (`@hostwebhook/node-sdk`, `schema-validator-utils.ts`). `rule` y `expected`
113
+ * son OPCIONALES porque hoy ese validador todavía no los emite: cuando no
114
+ * vienen, el nivel `full` sale exactamente igual que el cuerpo que ya se envía,
115
+ * o sea que nada empeora mientras tanto, y el día que los emita esta función no
116
+ * cambia una línea.
117
+ */
118
+ export interface FalloDeValidacion {
119
+ /**
120
+ * La ruta del campo dentro del payload: `user.email`, `items[0].sku`.
121
+ *
122
+ * ⚠️ Se llama `path` y no `field` a propósito, aunque el hueco de la
123
+ * plantilla se llame `{{field}}`. `path` es lo que YA sale por el cable en el
124
+ * 422 de hoy, y renombrarlo rompería a todo el que lo esté parseando ahí
125
+ * fuera. El hueco se llama como lo pidió el dueño, que es quien lo escribe en
126
+ * una caja de texto; la clave del JSON se llama como siempre se llamó.
127
+ */
128
+ path: string;
129
+ /** El texto que produjo el validador: `must be a valid email`. */
130
+ message: string;
131
+ /** Qué regla falló: `format`, `minLength`, `required`, `type`… */
132
+ rule?: string;
133
+ /** Qué esperaba esa regla: `email`, `3`, `string`… */
134
+ expected?: string;
135
+ }
136
+ /**
137
+ * Lo que dictaminó el validador. Es la ENTRADA de la función pura, y va como
138
+ * unión porque los cuatro finales no llevan los mismos datos: un `valid` no
139
+ * tiene errores y un `timeout` tampoco tiene payload que devolver.
140
+ */
141
+ export type VeredictoDelValidador = {
142
+ outcome: 'valid';
143
+ eventId?: string;
144
+ webhookName?: string;
145
+ nodeName?: string;
146
+ } | {
147
+ outcome: 'invalid';
148
+ errors: FalloDeValidacion[];
149
+ /** Lo que ENTRÓ. Hace falta para `includePayload`. */
150
+ payload?: unknown;
151
+ eventId?: string;
152
+ webhookName?: string;
153
+ nodeName?: string;
154
+ } | {
155
+ outcome: 'no-verdict';
156
+ eventId?: string;
157
+ webhookName?: string;
158
+ nodeName?: string;
159
+ } | {
160
+ outcome: 'timeout';
161
+ eventId?: string;
162
+ webhookName?: string;
163
+ nodeName?: string;
164
+ };
165
+ /**
166
+ * Cuánto se le cuenta al cliente de POR QUÉ falló.
167
+ *
168
+ * Los valores van en inglés aunque el tipo se llame en español, por lo mismo
169
+ * que `FORMATOS_DEL_ESQUEMA`: acaban en un `enum` de mongoose, en un `@IsIn`
170
+ * de un DTO y en un `<select>`, y ninguno de esos tres sitios quiere una
171
+ * cadena en español dentro de un JSON que un cliente puede exportar.
172
+ *
173
+ * - `full` — ruta, regla y esperado. Lo que quiere quien integra con su propio
174
+ * equipo detrás.
175
+ * - `fields-only` — QUÉ campos fallaron, sin el porqué. El término medio para
176
+ * un formulario público: basta para señalar la casilla en rojo y no publica
177
+ * las reglas.
178
+ * - `opaque` — «Invalid payload» y ya. **Ni una ruta de campo sale de aquí.**
179
+ * Un esquema es un mapa de tu modelo de datos; repartirlo por un endpoint
180
+ * público es regalar el trabajo de enumeración a quien lo quiera.
181
+ */
182
+ export declare const NIVELES_DE_DETALLE: readonly ["full", "fields-only", "opaque"];
183
+ export type NivelDeDetalle = (typeof NIVELES_DE_DETALLE)[number];
184
+ /**
185
+ * Si se le devuelve al cliente lo que mandó, y cuánto.
186
+ *
187
+ * - `none` — no.
188
+ * - `full` — tal cual llegó. No revela nada: es SU payload.
189
+ * - `failed-fields` — sólo los campos que fallaron, **en plano**, con la ruta
190
+ * completa como clave: `{ "user.email": "nope", "items[0].sku": 12 }`.
191
+ * Plano y no reconstruido con su anidamiento por dos razones: reconstruirlo
192
+ * añade una segunda lectura de rutas que puede discrepar de la del
193
+ * validador, y porque quien está depurando quiere la lista de lo que falló
194
+ * junto a lo que mandó, no un recorte del original con la misma forma.
195
+ */
196
+ export declare const MODOS_DE_PAYLOAD: readonly ["none", "full", "failed-fields"];
197
+ export type ModoDePayload = (typeof MODOS_DE_PAYLOAD)[number];
198
+ /**
199
+ * La forma del sobre.
200
+ *
201
+ * - `hostwebhook` — el nuestro, el que ya sale hoy por el cable.
202
+ * - `problem+json` — RFC 7807, con su `content-type: application/problem+json`.
203
+ * Lo piden los que meten esto detrás de un cliente generado.
204
+ */
205
+ export declare const SOBRES_DE_RESPUESTA: readonly ["hostwebhook", "problem+json"];
206
+ export type SobreDeRespuesta = (typeof SOBRES_DE_RESPUESTA)[number];
207
+ /** Un tope: el mínimo, el máximo y con qué se rellena si se sale. */
208
+ export interface TopeDeNumero {
209
+ min: number;
210
+ max: number;
211
+ defecto: number;
212
+ }
213
+ /**
214
+ * Los topes de los códigos de estado, exportados para que el dashboard ponga
215
+ * el `min`/`max` de cada caja desde AQUÍ y no a ojo.
216
+ *
217
+ * El del éxito acaba en 299 por lo que pidió el dueño: nada de un 500 en el
218
+ * éxito. Y no es una manía — un 5xx en la respuesta de «tu payload es válido»
219
+ * hace que el cliente reintente, y reintentar un webhook aceptado duplica el
220
+ * evento.
221
+ *
222
+ * El del error se queda en 4xx: la culpa del payload es de quien lo manda, y un
223
+ * 5xx ahí también invita al reintento de lo que nunca va a pasar.
224
+ *
225
+ * El del timeout llega hasta 599 porque ahí sí caben los dos: 408 (lo que sale
226
+ * hoy) y 504, que es lo que muchos proxies esperan.
227
+ */
228
+ export declare const TOPES_DE_ESTADO: {
229
+ readonly success: {
230
+ readonly min: 200;
231
+ readonly max: 299;
232
+ readonly defecto: 200;
233
+ };
234
+ readonly error: {
235
+ readonly min: 400;
236
+ readonly max: 499;
237
+ readonly defecto: 422;
238
+ };
239
+ readonly timeout: {
240
+ readonly min: 400;
241
+ readonly max: 599;
242
+ readonly defecto: 408;
243
+ };
244
+ };
245
+ /**
246
+ * Cuánto se puede tener la conexión viva.
247
+ *
248
+ * ⚠️ El `max` es `SYNC_TIMEOUT_SECONDS` de `@hostwebhook/node-sdk`
249
+ * (`topes/plan.constants.ts`), que es el `setTimeout` real del `waitForSync`
250
+ * del gateway. Está copiado y no importado porque este paquete **no tiene ni
251
+ * una dependencia** —es justo lo que lo hace servible en el navegador— y
252
+ * traerse node-sdk arrastraría mongoose, express y un binario nativo.
253
+ *
254
+ * Que la copia no se despiste lo ata un test EN node-sdk, que sí puede ver los
255
+ * dos: `el-tope-de-la-espera-es-uno.test.ts`. Sin esa atadura, configurar 200 s
256
+ * aquí daría una pantalla que promete 200 y un gateway que corta a los 120.
257
+ */
258
+ export declare const TOPE_DE_ESPERA_DEL_SYNC: {
259
+ readonly min: 1;
260
+ readonly max: 120;
261
+ readonly defecto: 120;
262
+ };
263
+ /**
264
+ * Cómo se quiere la respuesta del `sync`. Todo opcional: lo que no venga sale
265
+ * de `RESPUESTA_DEL_SYNC_POR_DEFECTO`, y así un webhook que nunca tocó estas
266
+ * pestañas se comporta exactamente como hoy.
267
+ */
268
+ export interface RespuestaDelSync {
269
+ /** Estado del ÉXITO. 200–299; fuera de rango vuelve a 200. */
270
+ successStatus?: number;
271
+ /** Estado del ERROR de validación. 400–499; fuera de rango vuelve a 422. */
272
+ errorStatus?: number;
273
+ /** Cuánto se cuenta del porqué. */
274
+ detail?: NivelDeDetalle;
275
+ /**
276
+ * Plantilla del mensaje de cada fallo, con huecos `{{field}}`, `{{rule}}` y
277
+ * `{{expected}}`. Sin ella se usa el texto del validador tal cual.
278
+ */
279
+ messageTemplate?: string;
280
+ /**
281
+ * Plantilla propia para un campo concreto, por su ruta exacta. Gana sobre
282
+ * `messageTemplate`.
283
+ */
284
+ fieldTemplates?: Record<string, string>;
285
+ /** Si se le devuelve al cliente lo que mandó. */
286
+ includePayload?: ModoDePayload;
287
+ /** La forma del sobre. */
288
+ envelope?: SobreDeRespuesta;
289
+ /**
290
+ * El `type` de RFC 7807: un URI que describe el problema. Sólo se usa con el
291
+ * sobre `problem+json`.
292
+ *
293
+ * Por defecto `about:blank`, que es lo que manda el propio RFC cuando no hay
294
+ * un URI específico. Inventarse una URL de hostwebhook.com que da 404 es peor
295
+ * que no dar ninguna: el 7807 existe para que ese enlace se pueda seguir.
296
+ */
297
+ problemType?: string;
298
+ /** Segundos de conexión viva. 1–120; fuera de rango vuelve a 120. */
299
+ timeoutSeconds?: number;
300
+ /** Estado del timeout. 400–599; fuera de rango vuelve a 408. */
301
+ timeoutStatus?: number;
302
+ /** Cuerpo del timeout. Sin él, el de hoy: `{ error: 'Pipeline timeout' }`. */
303
+ timeoutBody?: Record<string, unknown>;
304
+ }
305
+ /**
306
+ * Lo que se usa cuando no se configuró nada.
307
+ *
308
+ * Están elegidos para que un webhook que no toque nada se comporte IGUAL que
309
+ * antes de todo esto: 422 al fallar, 120 s de espera, 408 al agotarse, el sobre
310
+ * de siempre y sin devolver el payload. Lo único que cambia respecto a hoy es
311
+ * que el camino feliz ahora contesta en vez de colgarse, que era el bug.
312
+ *
313
+ * `detail: 'full'` por lo mismo: es lo que sale hoy por el cable.
314
+ */
315
+ export declare const RESPUESTA_DEL_SYNC_POR_DEFECTO: {
316
+ readonly successStatus: 200;
317
+ readonly errorStatus: 422;
318
+ readonly detail: "full";
319
+ readonly includePayload: "none";
320
+ readonly envelope: "hostwebhook";
321
+ readonly problemType: "about:blank";
322
+ readonly timeoutSeconds: 120;
323
+ readonly timeoutStatus: 408;
324
+ };
325
+ /**
326
+ * La respuesta HTTP, entera.
327
+ *
328
+ * Lleva `headers` y no sólo `{status, body}` porque el sobre RFC 7807 SON las
329
+ * cabeceras tanto como el cuerpo: mandar ese JSON con `application/json` no es
330
+ * un 7807, es un JSON que se le parece, y el cliente generado que lo espera no
331
+ * lo reconoce. `{status, body}` sigue siendo un subconjunto de esto, así que
332
+ * el `getSyncResult` de hoy encaja sin tocar nada.
333
+ */
334
+ export interface RespuestaHttp {
335
+ status: number;
336
+ headers: Record<string, string>;
337
+ /**
338
+ * ⚠️ El orden de inserción de las claves ES el orden en el que salen por el
339
+ * cable: `JSON.stringify` respeta el orden de inserción de las claves de
340
+ * texto. La vista previa enseña esto mismo, así que el orden de abajo está
341
+ * escrito a mano y no por casualidad.
342
+ */
343
+ body: Record<string, unknown>;
344
+ }
345
+ /**
346
+ * Arma la respuesta que recibe quien llamó en `sync`.
347
+ *
348
+ * PURA: sin nestjs, sin mongoose, sin `Date.now()`, sin nada de servidor. Entra
349
+ * un veredicto y una configuración, sale la respuesta. Por eso puede correr
350
+ * igual en el handler de hw-nodes y en el navegador del dashboard, y por eso la
351
+ * vista previa no puede desincronizarse: es literalmente la misma llamada.
352
+ */
353
+ export declare function armarRespuestaDelSync(veredicto: VeredictoDelValidador, config?: RespuestaDelSync): RespuestaHttp;