@hostwebhook/node-types 1.72.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.
- package/dist/docs-operations.d.ts +39 -0
- package/dist/docs-operations.js +74 -4
- package/dist/esm/docs-operations.d.ts +39 -0
- package/dist/esm/docs-operations.js +74 -4
- package/dist/esm/types.d.ts +51 -1
- package/dist/esm/ui.js +20 -2
- package/dist/types.d.ts +51 -1
- package/dist/ui.js +20 -2
- package/package.json +1 -1
|
@@ -33,6 +33,28 @@
|
|
|
33
33
|
* Los `slot` siguen haciendo falta: separan la identidad del documento de los
|
|
34
34
|
* campos de la operación, aunque ahora las dos vayan debajo del selector. Un
|
|
35
35
|
* bucle único ordenaría por el array de params y no por ese criterio.
|
|
36
|
+
*
|
|
37
|
+
* ── Qué exige el motor (y por qué está escrito aquí) ──
|
|
38
|
+
* Este esquema declaraba CERO obligatorios en las cinco operaciones, y el
|
|
39
|
+
* ejecutor —`api/src/nodes/docs-actions/docs-actions.service.ts`— rechaza cinco
|
|
40
|
+
* cosas. Esa distancia no se veía como un error de validación sino como un
|
|
41
|
+
* nodo que no guía: sin ningún obligatorio no hay nada que esperar, ningún paso
|
|
42
|
+
* desbloquea al siguiente, y el guiado que el panel SÍ monta no se nota.
|
|
43
|
+
*
|
|
44
|
+
* Los cinco 400 del ejecutor, uno por línea, que son los cinco `required` de
|
|
45
|
+
* este fichero:
|
|
46
|
+
*
|
|
47
|
+
* :323 readDoc documentId 'Document ID is required'
|
|
48
|
+
* :440 appendText documentId 'Document ID is required'
|
|
49
|
+
* :447 appendText contentTemplate 'Content template is required'
|
|
50
|
+
* :498 replaceText documentId 'Document ID is required'
|
|
51
|
+
* :505 replaceText searchText 'Search text is required'
|
|
52
|
+
* :560 insertTable documentId 'Document ID is required'
|
|
53
|
+
*
|
|
54
|
+
* Y los cuatro que NO se marcan, con el defecto que los salva: `documentTitle`
|
|
55
|
+
* cae en `entity.name`, `contentTemplate` de `createDoc` en un documento vacío,
|
|
56
|
+
* `tableRows`/`tableCols` en `|| 3`, y `replaceWith` vacío es un BORRADO, no un
|
|
57
|
+
* hueco. Cada uno lo explica en su sitio.
|
|
36
58
|
*/
|
|
37
59
|
export declare const DOCS_OPERATIONS: readonly ["readDoc", "createDoc", "appendText", "replaceText", "insertTable"];
|
|
38
60
|
export type DocsOperation = (typeof DOCS_OPERATIONS)[number];
|
|
@@ -67,6 +89,23 @@ export interface DocsParamSpec {
|
|
|
67
89
|
label: string;
|
|
68
90
|
type: DocsParamType;
|
|
69
91
|
slot: DocsParamSlot;
|
|
92
|
+
/**
|
|
93
|
+
* Lo exige el EJECUTOR: sin él, `docs-actions.service.ts` devuelve un 400 y
|
|
94
|
+
* el nodo no corre.
|
|
95
|
+
*
|
|
96
|
+
* No es «lo que conviene rellenar», y la diferencia importa en las dos
|
|
97
|
+
* direcciones. De menos, un obligatorio que falta deja la familia con cero y
|
|
98
|
+
* `esGuiable` (`check-cuantos-faltan-por-guiar.mjs`) la echa del censo: el
|
|
99
|
+
* panel monta `useGuidedConfig`, pero sin nada que esperar ningún paso
|
|
100
|
+
* desbloquea al siguiente y el guiado se ve como si no existiera — que es el
|
|
101
|
+
* fallo que este campo arregla. De más, marcar algo que el ejecutor SÍ acepta
|
|
102
|
+
* vacío esconde tras un hueco imposible un nodo que habría funcionado, porque
|
|
103
|
+
* `frena()` sólo mira `obligatorio && !contestado`.
|
|
104
|
+
*
|
|
105
|
+
* Por eso cada `required: true` de este fichero apunta a la línea que lo
|
|
106
|
+
* exige, y los que no lo llevan dicen dónde el ejecutor pone su defecto.
|
|
107
|
+
*/
|
|
108
|
+
required?: boolean;
|
|
70
109
|
/** Texto de ayuda. Llano: el paquete no lleva React. */
|
|
71
110
|
description?: string;
|
|
72
111
|
placeholder?: string;
|
package/dist/docs-operations.js
CHANGED
|
@@ -34,6 +34,28 @@
|
|
|
34
34
|
* Los `slot` siguen haciendo falta: separan la identidad del documento de los
|
|
35
35
|
* campos de la operación, aunque ahora las dos vayan debajo del selector. Un
|
|
36
36
|
* bucle único ordenaría por el array de params y no por ese criterio.
|
|
37
|
+
*
|
|
38
|
+
* ── Qué exige el motor (y por qué está escrito aquí) ──
|
|
39
|
+
* Este esquema declaraba CERO obligatorios en las cinco operaciones, y el
|
|
40
|
+
* ejecutor —`api/src/nodes/docs-actions/docs-actions.service.ts`— rechaza cinco
|
|
41
|
+
* cosas. Esa distancia no se veía como un error de validación sino como un
|
|
42
|
+
* nodo que no guía: sin ningún obligatorio no hay nada que esperar, ningún paso
|
|
43
|
+
* desbloquea al siguiente, y el guiado que el panel SÍ monta no se nota.
|
|
44
|
+
*
|
|
45
|
+
* Los cinco 400 del ejecutor, uno por línea, que son los cinco `required` de
|
|
46
|
+
* este fichero:
|
|
47
|
+
*
|
|
48
|
+
* :323 readDoc documentId 'Document ID is required'
|
|
49
|
+
* :440 appendText documentId 'Document ID is required'
|
|
50
|
+
* :447 appendText contentTemplate 'Content template is required'
|
|
51
|
+
* :498 replaceText documentId 'Document ID is required'
|
|
52
|
+
* :505 replaceText searchText 'Search text is required'
|
|
53
|
+
* :560 insertTable documentId 'Document ID is required'
|
|
54
|
+
*
|
|
55
|
+
* Y los cuatro que NO se marcan, con el defecto que los salva: `documentTitle`
|
|
56
|
+
* cae en `entity.name`, `contentTemplate` de `createDoc` en un documento vacío,
|
|
57
|
+
* `tableRows`/`tableCols` en `|| 3`, y `replaceWith` vacío es un BORRADO, no un
|
|
58
|
+
* hueco. Cada uno lo explica en su sitio.
|
|
37
59
|
*/
|
|
38
60
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
61
|
exports.DOCS_OPERATION_SPECS = exports.DOCS_OPERATIONS = void 0;
|
|
@@ -50,19 +72,42 @@ function isDocsOperation(value) {
|
|
|
50
72
|
return typeof value === 'string' && exports.DOCS_OPERATIONS.includes(value);
|
|
51
73
|
}
|
|
52
74
|
/* Los campos que comparten varias operaciones. */
|
|
75
|
+
/**
|
|
76
|
+
* `required: true` va aquí y no operación por operación porque las CUATRO que
|
|
77
|
+
* lo usan lo exigen: `executeRead` (:323), `executeAppend` (:440),
|
|
78
|
+
* `executeReplace` (:498) y `executeInsertTable` (:560) devuelven todas el
|
|
79
|
+
* mismo 400, «Document ID is required». La quinta, `createDoc`, no llama a este
|
|
80
|
+
* ayudante — el documento aún no existe.
|
|
81
|
+
*/
|
|
53
82
|
const documentId = () => ({
|
|
54
83
|
name: 'documentId',
|
|
55
84
|
label: 'Or paste Document ID / template',
|
|
56
85
|
type: 'docPicker',
|
|
57
86
|
slot: 'document',
|
|
87
|
+
required: true,
|
|
58
88
|
placeholder: '1BxiMVs0XRA5nFMdKvBd... or {{payload.documentId}}',
|
|
59
89
|
});
|
|
60
|
-
|
|
90
|
+
/**
|
|
91
|
+
* Éste NO puede llevar `required` en el ayudante, y es la única asimetría del
|
|
92
|
+
* fichero: sus dos usuarios no piden lo mismo.
|
|
93
|
+
*
|
|
94
|
+
* `appendText` lo exige (`executeAppend` :447, «Content template is required»):
|
|
95
|
+
* añadir la cadena vacía al final de un documento no es una operación, es un
|
|
96
|
+
* viaje a Google para no hacer nada. `createDoc` no (`executeCreate` :362 lo
|
|
97
|
+
* lee con un ternario y :375 sólo inserta `if (content)`): un documento nuevo y
|
|
98
|
+
* vacío es un resultado legítimo, y bloquearlo sería inventarse un requisito
|
|
99
|
+
* que la api no tiene.
|
|
100
|
+
*
|
|
101
|
+
* De ahí el `over`, como el `columnMapping` de Sheets: el que lo exige lo dice
|
|
102
|
+
* en su sitio.
|
|
103
|
+
*/
|
|
104
|
+
const contentTemplate = (over = {}) => ({
|
|
61
105
|
name: 'contentTemplate',
|
|
62
106
|
label: 'Content',
|
|
63
107
|
type: 'htmlTemplate',
|
|
64
108
|
slot: 'config',
|
|
65
109
|
placeholder: 'Text to insert... supports {{payload.x}} templates',
|
|
110
|
+
...over,
|
|
66
111
|
});
|
|
67
112
|
exports.DOCS_OPERATION_SPECS = {
|
|
68
113
|
readDoc: {
|
|
@@ -77,13 +122,22 @@ exports.DOCS_OPERATION_SPECS = {
|
|
|
77
122
|
labelShort: 'Create',
|
|
78
123
|
description: 'Create a new Google Doc',
|
|
79
124
|
/* La única SIN `documentId`: aquí el documento no existe todavía. Eso es lo
|
|
80
|
-
que en la página eran dos compuertas negadas (`!== "createDoc"`).
|
|
125
|
+
que en la página eran dos compuertas negadas (`!== "createDoc"`).
|
|
126
|
+
|
|
127
|
+
Y la única con CERO obligatorios, porque el ejecutor no exige ninguno:
|
|
128
|
+
`executeCreate` no tiene un solo `return 400`. Un `createDoc` sin nada
|
|
129
|
+
contestado corre y crea un documento vacío con el nombre del nodo. */
|
|
81
130
|
params: [
|
|
82
131
|
{
|
|
83
132
|
name: 'documentTitle',
|
|
84
133
|
label: 'Document Title',
|
|
85
134
|
type: 'template',
|
|
86
135
|
slot: 'document',
|
|
136
|
+
/* Opcional a propósito: `executeCreate` (:361) lo lee como
|
|
137
|
+
`render(entity.documentTitle || entity.name)`. Sin título el
|
|
138
|
+
documento sale con el NOMBRE DEL NODO, que es un defecto de verdad y
|
|
139
|
+
no un descuido. Marcarlo obligatorio pararía la guía delante de un
|
|
140
|
+
campo que la api sabe rellenar sola. */
|
|
87
141
|
placeholder: 'Report — {{payload.date}} or My Document',
|
|
88
142
|
},
|
|
89
143
|
contentTemplate(),
|
|
@@ -93,7 +147,7 @@ exports.DOCS_OPERATION_SPECS = {
|
|
|
93
147
|
label: 'Append Text',
|
|
94
148
|
labelShort: 'Append',
|
|
95
149
|
description: 'Add text to end of document',
|
|
96
|
-
params: [documentId(), contentTemplate()],
|
|
150
|
+
params: [documentId(), contentTemplate({ required: true })],
|
|
97
151
|
},
|
|
98
152
|
replaceText: {
|
|
99
153
|
label: 'Replace Text',
|
|
@@ -106,6 +160,9 @@ exports.DOCS_OPERATION_SPECS = {
|
|
|
106
160
|
label: 'Search Text',
|
|
107
161
|
type: 'template',
|
|
108
162
|
slot: 'config',
|
|
163
|
+
/* `executeReplace` (:505): «Search text is required». Sin qué buscar,
|
|
164
|
+
`replaceAllText` no tiene nada que hacer. */
|
|
165
|
+
required: true,
|
|
109
166
|
placeholder: '{{PLACEHOLDER}} or text to find',
|
|
110
167
|
},
|
|
111
168
|
{
|
|
@@ -113,6 +170,11 @@ exports.DOCS_OPERATION_SPECS = {
|
|
|
113
170
|
label: 'Replace With',
|
|
114
171
|
type: 'template',
|
|
115
172
|
slot: 'config',
|
|
173
|
+
/* Opcional, y aquí el vacío SIGNIFICA algo: `executeReplace` lo pasa
|
|
174
|
+
tal cual a `replaceAllText`, así que dejarlo en blanco BORRA todas
|
|
175
|
+
las apariciones de `searchText`. Es la única forma de pedir un
|
|
176
|
+
borrado, y por eso el ejecutor no lo comprueba: hacerlo obligatorio
|
|
177
|
+
quitaría una operación que hoy funciona. */
|
|
116
178
|
placeholder: '{{payload.value}} or replacement text',
|
|
117
179
|
},
|
|
118
180
|
],
|
|
@@ -123,7 +185,15 @@ exports.DOCS_OPERATION_SPECS = {
|
|
|
123
185
|
description: 'Insert a table at end of document',
|
|
124
186
|
params: [
|
|
125
187
|
documentId(),
|
|
126
|
-
/* Los topes son distintos y no es un descuido: 50 filas, 20 columnas.
|
|
188
|
+
/* Los topes son distintos y no es un descuido: 50 filas, 20 columnas.
|
|
189
|
+
|
|
190
|
+
Y los dos siguen OPCIONALES teniendo `default: 3`, que es justo el
|
|
191
|
+
caso que el contrato vigila: `executeInsertTable` (:555) los lee con
|
|
192
|
+
`entity.tableRows || 3`, o sea que el 3×3 lo pone la api tanto si el
|
|
193
|
+
formulario lo manda como si no. Un obligatorio con `default` sería
|
|
194
|
+
además la trampa de la regla 10 — `defectoPintable` le borra el
|
|
195
|
+
`default` para no pintar como contestado algo que nadie eligió, y
|
|
196
|
+
estos dos sí quieren pintarlo. */
|
|
127
197
|
{ name: 'tableRows', label: 'Rows', type: 'number', slot: 'config', min: 1, max: 50, default: 3 },
|
|
128
198
|
{ name: 'tableCols', label: 'Columns', type: 'number', slot: 'config', min: 1, max: 20, default: 3 },
|
|
129
199
|
],
|
|
@@ -33,6 +33,28 @@
|
|
|
33
33
|
* Los `slot` siguen haciendo falta: separan la identidad del documento de los
|
|
34
34
|
* campos de la operación, aunque ahora las dos vayan debajo del selector. Un
|
|
35
35
|
* bucle único ordenaría por el array de params y no por ese criterio.
|
|
36
|
+
*
|
|
37
|
+
* ── Qué exige el motor (y por qué está escrito aquí) ──
|
|
38
|
+
* Este esquema declaraba CERO obligatorios en las cinco operaciones, y el
|
|
39
|
+
* ejecutor —`api/src/nodes/docs-actions/docs-actions.service.ts`— rechaza cinco
|
|
40
|
+
* cosas. Esa distancia no se veía como un error de validación sino como un
|
|
41
|
+
* nodo que no guía: sin ningún obligatorio no hay nada que esperar, ningún paso
|
|
42
|
+
* desbloquea al siguiente, y el guiado que el panel SÍ monta no se nota.
|
|
43
|
+
*
|
|
44
|
+
* Los cinco 400 del ejecutor, uno por línea, que son los cinco `required` de
|
|
45
|
+
* este fichero:
|
|
46
|
+
*
|
|
47
|
+
* :323 readDoc documentId 'Document ID is required'
|
|
48
|
+
* :440 appendText documentId 'Document ID is required'
|
|
49
|
+
* :447 appendText contentTemplate 'Content template is required'
|
|
50
|
+
* :498 replaceText documentId 'Document ID is required'
|
|
51
|
+
* :505 replaceText searchText 'Search text is required'
|
|
52
|
+
* :560 insertTable documentId 'Document ID is required'
|
|
53
|
+
*
|
|
54
|
+
* Y los cuatro que NO se marcan, con el defecto que los salva: `documentTitle`
|
|
55
|
+
* cae en `entity.name`, `contentTemplate` de `createDoc` en un documento vacío,
|
|
56
|
+
* `tableRows`/`tableCols` en `|| 3`, y `replaceWith` vacío es un BORRADO, no un
|
|
57
|
+
* hueco. Cada uno lo explica en su sitio.
|
|
36
58
|
*/
|
|
37
59
|
export declare const DOCS_OPERATIONS: readonly ["readDoc", "createDoc", "appendText", "replaceText", "insertTable"];
|
|
38
60
|
export type DocsOperation = (typeof DOCS_OPERATIONS)[number];
|
|
@@ -67,6 +89,23 @@ export interface DocsParamSpec {
|
|
|
67
89
|
label: string;
|
|
68
90
|
type: DocsParamType;
|
|
69
91
|
slot: DocsParamSlot;
|
|
92
|
+
/**
|
|
93
|
+
* Lo exige el EJECUTOR: sin él, `docs-actions.service.ts` devuelve un 400 y
|
|
94
|
+
* el nodo no corre.
|
|
95
|
+
*
|
|
96
|
+
* No es «lo que conviene rellenar», y la diferencia importa en las dos
|
|
97
|
+
* direcciones. De menos, un obligatorio que falta deja la familia con cero y
|
|
98
|
+
* `esGuiable` (`check-cuantos-faltan-por-guiar.mjs`) la echa del censo: el
|
|
99
|
+
* panel monta `useGuidedConfig`, pero sin nada que esperar ningún paso
|
|
100
|
+
* desbloquea al siguiente y el guiado se ve como si no existiera — que es el
|
|
101
|
+
* fallo que este campo arregla. De más, marcar algo que el ejecutor SÍ acepta
|
|
102
|
+
* vacío esconde tras un hueco imposible un nodo que habría funcionado, porque
|
|
103
|
+
* `frena()` sólo mira `obligatorio && !contestado`.
|
|
104
|
+
*
|
|
105
|
+
* Por eso cada `required: true` de este fichero apunta a la línea que lo
|
|
106
|
+
* exige, y los que no lo llevan dicen dónde el ejecutor pone su defecto.
|
|
107
|
+
*/
|
|
108
|
+
required?: boolean;
|
|
70
109
|
/** Texto de ayuda. Llano: el paquete no lleva React. */
|
|
71
110
|
description?: string;
|
|
72
111
|
placeholder?: string;
|
|
@@ -33,6 +33,28 @@
|
|
|
33
33
|
* Los `slot` siguen haciendo falta: separan la identidad del documento de los
|
|
34
34
|
* campos de la operación, aunque ahora las dos vayan debajo del selector. Un
|
|
35
35
|
* bucle único ordenaría por el array de params y no por ese criterio.
|
|
36
|
+
*
|
|
37
|
+
* ── Qué exige el motor (y por qué está escrito aquí) ──
|
|
38
|
+
* Este esquema declaraba CERO obligatorios en las cinco operaciones, y el
|
|
39
|
+
* ejecutor —`api/src/nodes/docs-actions/docs-actions.service.ts`— rechaza cinco
|
|
40
|
+
* cosas. Esa distancia no se veía como un error de validación sino como un
|
|
41
|
+
* nodo que no guía: sin ningún obligatorio no hay nada que esperar, ningún paso
|
|
42
|
+
* desbloquea al siguiente, y el guiado que el panel SÍ monta no se nota.
|
|
43
|
+
*
|
|
44
|
+
* Los cinco 400 del ejecutor, uno por línea, que son los cinco `required` de
|
|
45
|
+
* este fichero:
|
|
46
|
+
*
|
|
47
|
+
* :323 readDoc documentId 'Document ID is required'
|
|
48
|
+
* :440 appendText documentId 'Document ID is required'
|
|
49
|
+
* :447 appendText contentTemplate 'Content template is required'
|
|
50
|
+
* :498 replaceText documentId 'Document ID is required'
|
|
51
|
+
* :505 replaceText searchText 'Search text is required'
|
|
52
|
+
* :560 insertTable documentId 'Document ID is required'
|
|
53
|
+
*
|
|
54
|
+
* Y los cuatro que NO se marcan, con el defecto que los salva: `documentTitle`
|
|
55
|
+
* cae en `entity.name`, `contentTemplate` de `createDoc` en un documento vacío,
|
|
56
|
+
* `tableRows`/`tableCols` en `|| 3`, y `replaceWith` vacío es un BORRADO, no un
|
|
57
|
+
* hueco. Cada uno lo explica en su sitio.
|
|
36
58
|
*/
|
|
37
59
|
export const DOCS_OPERATIONS = [
|
|
38
60
|
'readDoc', // leer el contenido como texto plano
|
|
@@ -46,19 +68,42 @@ export function isDocsOperation(value) {
|
|
|
46
68
|
return typeof value === 'string' && DOCS_OPERATIONS.includes(value);
|
|
47
69
|
}
|
|
48
70
|
/* Los campos que comparten varias operaciones. */
|
|
71
|
+
/**
|
|
72
|
+
* `required: true` va aquí y no operación por operación porque las CUATRO que
|
|
73
|
+
* lo usan lo exigen: `executeRead` (:323), `executeAppend` (:440),
|
|
74
|
+
* `executeReplace` (:498) y `executeInsertTable` (:560) devuelven todas el
|
|
75
|
+
* mismo 400, «Document ID is required». La quinta, `createDoc`, no llama a este
|
|
76
|
+
* ayudante — el documento aún no existe.
|
|
77
|
+
*/
|
|
49
78
|
const documentId = () => ({
|
|
50
79
|
name: 'documentId',
|
|
51
80
|
label: 'Or paste Document ID / template',
|
|
52
81
|
type: 'docPicker',
|
|
53
82
|
slot: 'document',
|
|
83
|
+
required: true,
|
|
54
84
|
placeholder: '1BxiMVs0XRA5nFMdKvBd... or {{payload.documentId}}',
|
|
55
85
|
});
|
|
56
|
-
|
|
86
|
+
/**
|
|
87
|
+
* Éste NO puede llevar `required` en el ayudante, y es la única asimetría del
|
|
88
|
+
* fichero: sus dos usuarios no piden lo mismo.
|
|
89
|
+
*
|
|
90
|
+
* `appendText` lo exige (`executeAppend` :447, «Content template is required»):
|
|
91
|
+
* añadir la cadena vacía al final de un documento no es una operación, es un
|
|
92
|
+
* viaje a Google para no hacer nada. `createDoc` no (`executeCreate` :362 lo
|
|
93
|
+
* lee con un ternario y :375 sólo inserta `if (content)`): un documento nuevo y
|
|
94
|
+
* vacío es un resultado legítimo, y bloquearlo sería inventarse un requisito
|
|
95
|
+
* que la api no tiene.
|
|
96
|
+
*
|
|
97
|
+
* De ahí el `over`, como el `columnMapping` de Sheets: el que lo exige lo dice
|
|
98
|
+
* en su sitio.
|
|
99
|
+
*/
|
|
100
|
+
const contentTemplate = (over = {}) => ({
|
|
57
101
|
name: 'contentTemplate',
|
|
58
102
|
label: 'Content',
|
|
59
103
|
type: 'htmlTemplate',
|
|
60
104
|
slot: 'config',
|
|
61
105
|
placeholder: 'Text to insert... supports {{payload.x}} templates',
|
|
106
|
+
...over,
|
|
62
107
|
});
|
|
63
108
|
export const DOCS_OPERATION_SPECS = {
|
|
64
109
|
readDoc: {
|
|
@@ -73,13 +118,22 @@ export const DOCS_OPERATION_SPECS = {
|
|
|
73
118
|
labelShort: 'Create',
|
|
74
119
|
description: 'Create a new Google Doc',
|
|
75
120
|
/* La única SIN `documentId`: aquí el documento no existe todavía. Eso es lo
|
|
76
|
-
que en la página eran dos compuertas negadas (`!== "createDoc"`).
|
|
121
|
+
que en la página eran dos compuertas negadas (`!== "createDoc"`).
|
|
122
|
+
|
|
123
|
+
Y la única con CERO obligatorios, porque el ejecutor no exige ninguno:
|
|
124
|
+
`executeCreate` no tiene un solo `return 400`. Un `createDoc` sin nada
|
|
125
|
+
contestado corre y crea un documento vacío con el nombre del nodo. */
|
|
77
126
|
params: [
|
|
78
127
|
{
|
|
79
128
|
name: 'documentTitle',
|
|
80
129
|
label: 'Document Title',
|
|
81
130
|
type: 'template',
|
|
82
131
|
slot: 'document',
|
|
132
|
+
/* Opcional a propósito: `executeCreate` (:361) lo lee como
|
|
133
|
+
`render(entity.documentTitle || entity.name)`. Sin título el
|
|
134
|
+
documento sale con el NOMBRE DEL NODO, que es un defecto de verdad y
|
|
135
|
+
no un descuido. Marcarlo obligatorio pararía la guía delante de un
|
|
136
|
+
campo que la api sabe rellenar sola. */
|
|
83
137
|
placeholder: 'Report — {{payload.date}} or My Document',
|
|
84
138
|
},
|
|
85
139
|
contentTemplate(),
|
|
@@ -89,7 +143,7 @@ export const DOCS_OPERATION_SPECS = {
|
|
|
89
143
|
label: 'Append Text',
|
|
90
144
|
labelShort: 'Append',
|
|
91
145
|
description: 'Add text to end of document',
|
|
92
|
-
params: [documentId(), contentTemplate()],
|
|
146
|
+
params: [documentId(), contentTemplate({ required: true })],
|
|
93
147
|
},
|
|
94
148
|
replaceText: {
|
|
95
149
|
label: 'Replace Text',
|
|
@@ -102,6 +156,9 @@ export const DOCS_OPERATION_SPECS = {
|
|
|
102
156
|
label: 'Search Text',
|
|
103
157
|
type: 'template',
|
|
104
158
|
slot: 'config',
|
|
159
|
+
/* `executeReplace` (:505): «Search text is required». Sin qué buscar,
|
|
160
|
+
`replaceAllText` no tiene nada que hacer. */
|
|
161
|
+
required: true,
|
|
105
162
|
placeholder: '{{PLACEHOLDER}} or text to find',
|
|
106
163
|
},
|
|
107
164
|
{
|
|
@@ -109,6 +166,11 @@ export const DOCS_OPERATION_SPECS = {
|
|
|
109
166
|
label: 'Replace With',
|
|
110
167
|
type: 'template',
|
|
111
168
|
slot: 'config',
|
|
169
|
+
/* Opcional, y aquí el vacío SIGNIFICA algo: `executeReplace` lo pasa
|
|
170
|
+
tal cual a `replaceAllText`, así que dejarlo en blanco BORRA todas
|
|
171
|
+
las apariciones de `searchText`. Es la única forma de pedir un
|
|
172
|
+
borrado, y por eso el ejecutor no lo comprueba: hacerlo obligatorio
|
|
173
|
+
quitaría una operación que hoy funciona. */
|
|
112
174
|
placeholder: '{{payload.value}} or replacement text',
|
|
113
175
|
},
|
|
114
176
|
],
|
|
@@ -119,7 +181,15 @@ export const DOCS_OPERATION_SPECS = {
|
|
|
119
181
|
description: 'Insert a table at end of document',
|
|
120
182
|
params: [
|
|
121
183
|
documentId(),
|
|
122
|
-
/* Los topes son distintos y no es un descuido: 50 filas, 20 columnas.
|
|
184
|
+
/* Los topes son distintos y no es un descuido: 50 filas, 20 columnas.
|
|
185
|
+
|
|
186
|
+
Y los dos siguen OPCIONALES teniendo `default: 3`, que es justo el
|
|
187
|
+
caso que el contrato vigila: `executeInsertTable` (:555) los lee con
|
|
188
|
+
`entity.tableRows || 3`, o sea que el 3×3 lo pone la api tanto si el
|
|
189
|
+
formulario lo manda como si no. Un obligatorio con `default` sería
|
|
190
|
+
además la trampa de la regla 10 — `defectoPintable` le borra el
|
|
191
|
+
`default` para no pintar como contestado algo que nadie eligió, y
|
|
192
|
+
estos dos sí quieren pintarlo. */
|
|
123
193
|
{ name: 'tableRows', label: 'Rows', type: 'number', slot: 'config', min: 1, max: 50, default: 3 },
|
|
124
194
|
{ name: 'tableCols', label: 'Columns', type: 'number', slot: 'config', min: 1, max: 20, default: 3 },
|
|
125
195
|
],
|
package/dist/esm/types.d.ts
CHANGED
|
@@ -38,7 +38,20 @@ export interface NodeUIConfig {
|
|
|
38
38
|
hasBranches?: boolean;
|
|
39
39
|
isTerminal?: boolean;
|
|
40
40
|
hasLoopBack?: boolean;
|
|
41
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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