@hostwebhook/node-types 1.73.0 → 1.74.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.
@@ -38,7 +38,20 @@ export interface NodeUIConfig {
38
38
  hasBranches?: boolean;
39
39
  isTerminal?: boolean;
40
40
  hasLoopBack?: boolean;
41
- connectModal?: 'branch' | 'rule' | 'output-group';
41
+ /**
42
+ * Aquí había también un `'rule'`, sólo para el router, y con él vivía en
43
+ * el lienzo un camino de conexión a medida: su propio modal, su propio
44
+ * `handleRuleSelect` y su propia puerta en `onConnect`. Dos mecanismos
45
+ * para lo mismo, y así es como uno se arregla y el otro no: el modal del
46
+ * router salía al ARRASTRAR y nunca al pulsar el `+`, porque ese segundo
47
+ * camino pasa por `outputGroups` — que el router no declaraba.
48
+ *
49
+ * El router ramifica igual que el conditional, el split y el clasificador,
50
+ * y su destino vive en el mismo sitio que el de ellos (`port: rule:<id>`),
51
+ * así que usa la misma maquinaria. Se quita del tipo para que no pueda
52
+ * volver a declararse sin que el compilador lo diga.
53
+ */
54
+ connectModal?: 'branch' | 'output-group';
42
55
  routerTargetLabel?: string;
43
56
  /**
44
57
  * Generic named output groups — used by connectModal: 'output-group'.
@@ -54,6 +67,20 @@ export interface NodeUIConfig {
54
67
  nameField?: string;
55
68
  /** Field within each group for summary/description (e.g. 'field', 'filterMode') */
56
69
  summaryField?: string;
70
+ /**
71
+ * Varios campos del grupo, unidos por espacios, cuando el resumen que
72
+ * de verdad identifica al grupo no cabe en uno solo.
73
+ *
74
+ * Existe por las reglas del router: su condición son TRES campos
75
+ * (`field`, `operator`, `value`) y el modal a medida que se retiró las
76
+ * pintaba juntas — «status eq active». Con un único `summaryField` una
77
+ * regla sin `label` quedaba en «rule 1» y «status», que no dice a qué
78
+ * se está conectando uno.
79
+ *
80
+ * Los campos vacíos se saltan, para que una regla con operador
81
+ * `exists` no salga con un espacio colgando al final.
82
+ */
83
+ summaryFields?: string[];
57
84
  /** Label shown in the modal (e.g. 'output', 'branch') */
58
85
  label: string;
59
86
  /** Entity field for the fallback/else path (e.g. 'elseOutputNodes') */
@@ -80,6 +107,29 @@ export interface NodeUIConfig {
80
107
  field: string;
81
108
  value: unknown;
82
109
  };
110
+ /**
111
+ * Qué es el puerto `main` de este nodo MIENTRAS su contenedor está
112
+ * vacío. Sin esto, conectar a un nodo que ramifica y todavía no tiene
113
+ * ninguna salida escribía una arista en `main` **en silencio**, y el
114
+ * usuario no tenía forma de saber qué había pasado con ella.
115
+ *
116
+ * No hay una respuesta única, y por eso es un dato y no una regla.
117
+ * Medido en `api/src/common/pipeline-run.service.ts`:
118
+ *
119
+ * 'passthrough' — router sin reglas: `rules.length === 0
120
+ * ? refsForPort(entity, MAIN_PORT) : []`. La
121
+ * arista SÍ dispara... hasta que se cree la
122
+ * primera regla, y entonces deja de hacerlo
123
+ * sin avisar.
124
+ * 'never-dispatched' — split: no tiene salida `main`, y el propio
125
+ * despachador registra un warning por cada
126
+ * arista que se quedó en ese puerto.
127
+ *
128
+ * Se declara sólo donde se ha medido. Sin declarar, el lienzo se
129
+ * comporta como siempre: conecta por el camino universal y no dice
130
+ * nada, que es lo que hacía antes de existir este campo.
131
+ */
132
+ emptyContainerMain?: 'passthrough' | 'never-dispatched';
83
133
  };
84
134
  /**
85
135
  * Maps output fields to alternate payload fields on the source entity.
package/dist/esm/ui.js CHANGED
@@ -50,12 +50,30 @@ export const NODE_UI = {
50
50
  fileTransform: { fromNodes: true, toNodes: true },
51
51
  limit: { fromNodes: true, toNodes: true, special: { loopBackAllowed: true } },
52
52
  // ── Flow control nodes ──
53
- split: { fromNodes: true, toNodes: true, outputHandles: ['right'], special: { connectModal: 'output-group', outputGroups: { field: 'outputs', nameField: 'name', summaryField: 'field', label: 'output' } } },
53
+ // `emptyContainerMain: 'never-dispatched'` dice lo que el despachador ya
54
+ // registra como warning: un split NO tiene salida `main`, así que una arista
55
+ // que se quede ahí —la que se guardaba al conectar un split sin salidas— no
56
+ // se despacha nunca. Ver `pipeline-run.service.ts`, el bloque `nodeType ===
57
+ // 'split'`. El lienzo lo usa para avisar en vez de callarse.
58
+ split: { fromNodes: true, toNodes: true, outputHandles: ['right'], special: { connectModal: 'output-group', outputGroups: { field: 'outputs', nameField: 'name', summaryField: 'field', label: 'output', emptyContainerMain: 'never-dispatched' } } },
54
59
  merge: { fromNodes: true, toNodes: true, inputHandles: ['left', 'top'] },
55
60
  approval: { fromNodes: true, toNodes: true, outputHandles: ['right'], special: { connectModal: 'output-group', outputGroups: { fixedGroups: [{ name: 'Approve', field: 'outputNodes' }, { name: 'Reject', field: 'rejectionOutputNodes' }], label: 'action' } } },
56
61
  loop: { fromNodes: true, toNodes: true, inputHandles: ['left'], outputHandles: ['right'], dotHandles: { input: ['left-in'], output: ['right-out-loop', 'right-in-loopback', 'right-out-done'] }, special: { hasLoopBack: true, alternatePayloads: { doneOutputNodes: 'lastDonePayload' } } },
57
62
  // ── Routing nodes ──
58
- router: { fromNodes: true, toNodes: true, outputHandles: ['right'], dotHandles: { output: ['right-out'] }, special: { connectModal: 'rule' } },
63
+ // El router ramifica igual que el conditional, el split y el clasificador:
64
+ // sus salidas son un contenedor (`rules[]`) y el destino de cada una vive en
65
+ // la arista, en `port: rule:<id>`. Lo único que le faltaba era DECIRLO aquí.
66
+ //
67
+ // Mientras no lo decía, tenía en el lienzo un camino propio —`connectModal:
68
+ // 'rule'`, con su modal, su `handleRuleSelect` y su puerta en `onConnect`— y
69
+ // eso dejaba el `+` fuera: ese camino no pasa por `onConnect`, pregunta por
70
+ // `outputGroups`, y el router no tenía. Arrastrando salía el modal; pulsando
71
+ // el `+` la arista se guardaba en `main` sin preguntar nada.
72
+ //
73
+ // `nameField: 'label'` porque el `label` de una regla es opcional; cuando
74
+ // falta, el modal numera («rule 1») y el `summaryFields` de al lado pone la
75
+ // condición, que es lo que de verdad la identifica.
76
+ router: { fromNodes: true, toNodes: true, outputHandles: ['right'], dotHandles: { output: ['right-out'] }, special: { connectModal: 'output-group', outputGroups: { field: 'rules', nameField: 'label', summaryFields: ['field', 'operator', 'value'], label: 'rule', emptyContainerMain: 'passthrough' } } },
59
77
  // ── Action nodes ──
60
78
  emailAction: { fromNodes: true, toNodes: true, inputHandles: ['left', 'top'], outputHandles: ['right'], dotHandles: { input: ['left-in', 'top-in'], output: ['right-out'] }, special: { loopBackAllowed: true, routerTargetLabel: 'email' } },
61
79
  gmailAction: { fromNodes: true, toNodes: true, inputHandles: ['left', 'top'], outputHandles: ['right'], dotHandles: { input: ['left-in', 'top-in'], output: ['right-out'] }, special: { loopBackAllowed: true, routerTargetLabel: 'gmail' } },
package/dist/types.d.ts CHANGED
@@ -38,7 +38,20 @@ export interface NodeUIConfig {
38
38
  hasBranches?: boolean;
39
39
  isTerminal?: boolean;
40
40
  hasLoopBack?: boolean;
41
- connectModal?: 'branch' | 'rule' | 'output-group';
41
+ /**
42
+ * Aquí había también un `'rule'`, sólo para el router, y con él vivía en
43
+ * el lienzo un camino de conexión a medida: su propio modal, su propio
44
+ * `handleRuleSelect` y su propia puerta en `onConnect`. Dos mecanismos
45
+ * para lo mismo, y así es como uno se arregla y el otro no: el modal del
46
+ * router salía al ARRASTRAR y nunca al pulsar el `+`, porque ese segundo
47
+ * camino pasa por `outputGroups` — que el router no declaraba.
48
+ *
49
+ * El router ramifica igual que el conditional, el split y el clasificador,
50
+ * y su destino vive en el mismo sitio que el de ellos (`port: rule:<id>`),
51
+ * así que usa la misma maquinaria. Se quita del tipo para que no pueda
52
+ * volver a declararse sin que el compilador lo diga.
53
+ */
54
+ connectModal?: 'branch' | 'output-group';
42
55
  routerTargetLabel?: string;
43
56
  /**
44
57
  * Generic named output groups — used by connectModal: 'output-group'.
@@ -54,6 +67,20 @@ export interface NodeUIConfig {
54
67
  nameField?: string;
55
68
  /** Field within each group for summary/description (e.g. 'field', 'filterMode') */
56
69
  summaryField?: string;
70
+ /**
71
+ * Varios campos del grupo, unidos por espacios, cuando el resumen que
72
+ * de verdad identifica al grupo no cabe en uno solo.
73
+ *
74
+ * Existe por las reglas del router: su condición son TRES campos
75
+ * (`field`, `operator`, `value`) y el modal a medida que se retiró las
76
+ * pintaba juntas — «status eq active». Con un único `summaryField` una
77
+ * regla sin `label` quedaba en «rule 1» y «status», que no dice a qué
78
+ * se está conectando uno.
79
+ *
80
+ * Los campos vacíos se saltan, para que una regla con operador
81
+ * `exists` no salga con un espacio colgando al final.
82
+ */
83
+ summaryFields?: string[];
57
84
  /** Label shown in the modal (e.g. 'output', 'branch') */
58
85
  label: string;
59
86
  /** Entity field for the fallback/else path (e.g. 'elseOutputNodes') */
@@ -80,6 +107,29 @@ export interface NodeUIConfig {
80
107
  field: string;
81
108
  value: unknown;
82
109
  };
110
+ /**
111
+ * Qué es el puerto `main` de este nodo MIENTRAS su contenedor está
112
+ * vacío. Sin esto, conectar a un nodo que ramifica y todavía no tiene
113
+ * ninguna salida escribía una arista en `main` **en silencio**, y el
114
+ * usuario no tenía forma de saber qué había pasado con ella.
115
+ *
116
+ * No hay una respuesta única, y por eso es un dato y no una regla.
117
+ * Medido en `api/src/common/pipeline-run.service.ts`:
118
+ *
119
+ * 'passthrough' — router sin reglas: `rules.length === 0
120
+ * ? refsForPort(entity, MAIN_PORT) : []`. La
121
+ * arista SÍ dispara... hasta que se cree la
122
+ * primera regla, y entonces deja de hacerlo
123
+ * sin avisar.
124
+ * 'never-dispatched' — split: no tiene salida `main`, y el propio
125
+ * despachador registra un warning por cada
126
+ * arista que se quedó en ese puerto.
127
+ *
128
+ * Se declara sólo donde se ha medido. Sin declarar, el lienzo se
129
+ * comporta como siempre: conecta por el camino universal y no dice
130
+ * nada, que es lo que hacía antes de existir este campo.
131
+ */
132
+ emptyContainerMain?: 'passthrough' | 'never-dispatched';
83
133
  };
84
134
  /**
85
135
  * Maps output fields to alternate payload fields on the source entity.
package/dist/ui.js CHANGED
@@ -54,12 +54,30 @@ exports.NODE_UI = {
54
54
  fileTransform: { fromNodes: true, toNodes: true },
55
55
  limit: { fromNodes: true, toNodes: true, special: { loopBackAllowed: true } },
56
56
  // ── Flow control nodes ──
57
- split: { fromNodes: true, toNodes: true, outputHandles: ['right'], special: { connectModal: 'output-group', outputGroups: { field: 'outputs', nameField: 'name', summaryField: 'field', label: 'output' } } },
57
+ // `emptyContainerMain: 'never-dispatched'` dice lo que el despachador ya
58
+ // registra como warning: un split NO tiene salida `main`, así que una arista
59
+ // que se quede ahí —la que se guardaba al conectar un split sin salidas— no
60
+ // se despacha nunca. Ver `pipeline-run.service.ts`, el bloque `nodeType ===
61
+ // 'split'`. El lienzo lo usa para avisar en vez de callarse.
62
+ split: { fromNodes: true, toNodes: true, outputHandles: ['right'], special: { connectModal: 'output-group', outputGroups: { field: 'outputs', nameField: 'name', summaryField: 'field', label: 'output', emptyContainerMain: 'never-dispatched' } } },
58
63
  merge: { fromNodes: true, toNodes: true, inputHandles: ['left', 'top'] },
59
64
  approval: { fromNodes: true, toNodes: true, outputHandles: ['right'], special: { connectModal: 'output-group', outputGroups: { fixedGroups: [{ name: 'Approve', field: 'outputNodes' }, { name: 'Reject', field: 'rejectionOutputNodes' }], label: 'action' } } },
60
65
  loop: { fromNodes: true, toNodes: true, inputHandles: ['left'], outputHandles: ['right'], dotHandles: { input: ['left-in'], output: ['right-out-loop', 'right-in-loopback', 'right-out-done'] }, special: { hasLoopBack: true, alternatePayloads: { doneOutputNodes: 'lastDonePayload' } } },
61
66
  // ── Routing nodes ──
62
- router: { fromNodes: true, toNodes: true, outputHandles: ['right'], dotHandles: { output: ['right-out'] }, special: { connectModal: 'rule' } },
67
+ // El router ramifica igual que el conditional, el split y el clasificador:
68
+ // sus salidas son un contenedor (`rules[]`) y el destino de cada una vive en
69
+ // la arista, en `port: rule:<id>`. Lo único que le faltaba era DECIRLO aquí.
70
+ //
71
+ // Mientras no lo decía, tenía en el lienzo un camino propio —`connectModal:
72
+ // 'rule'`, con su modal, su `handleRuleSelect` y su puerta en `onConnect`— y
73
+ // eso dejaba el `+` fuera: ese camino no pasa por `onConnect`, pregunta por
74
+ // `outputGroups`, y el router no tenía. Arrastrando salía el modal; pulsando
75
+ // el `+` la arista se guardaba en `main` sin preguntar nada.
76
+ //
77
+ // `nameField: 'label'` porque el `label` de una regla es opcional; cuando
78
+ // falta, el modal numera («rule 1») y el `summaryFields` de al lado pone la
79
+ // condición, que es lo que de verdad la identifica.
80
+ router: { fromNodes: true, toNodes: true, outputHandles: ['right'], dotHandles: { output: ['right-out'] }, special: { connectModal: 'output-group', outputGroups: { field: 'rules', nameField: 'label', summaryFields: ['field', 'operator', 'value'], label: 'rule', emptyContainerMain: 'passthrough' } } },
63
81
  // ── Action nodes ──
64
82
  emailAction: { fromNodes: true, toNodes: true, inputHandles: ['left', 'top'], outputHandles: ['right'], dotHandles: { input: ['left-in', 'top-in'], output: ['right-out'] }, special: { loopBackAllowed: true, routerTargetLabel: 'email' } },
65
83
  gmailAction: { fromNodes: true, toNodes: true, inputHandles: ['left', 'top'], outputHandles: ['right'], dotHandles: { input: ['left-in', 'top-in'], output: ['right-out'] }, special: { loopBackAllowed: true, routerTargetLabel: 'gmail' } },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hostwebhook/node-types",
3
- "version": "1.73.0",
3
+ "version": "1.74.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",