@hostwebhook/platform-contracts 0.1.0 → 0.3.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.
Files changed (66) hide show
  1. package/README.md +64 -4
  2. package/dist/index.d.ts +2 -0
  3. package/dist/index.js +2 -0
  4. package/dist/operadores.d.ts +60 -0
  5. package/dist/operadores.js +123 -0
  6. package/dist/servicios/almacenes-vectoriales.d.ts +105 -0
  7. package/dist/servicios/almacenes-vectoriales.js +26 -0
  8. package/dist/servicios/aprobaciones-pendientes.d.ts +207 -0
  9. package/dist/servicios/aprobaciones-pendientes.js +51 -0
  10. package/dist/servicios/avisos-en-vivo.d.ts +106 -0
  11. package/dist/servicios/avisos-en-vivo.js +60 -0
  12. package/dist/servicios/canal-de-stream.d.ts +78 -0
  13. package/dist/servicios/canal-de-stream.js +5 -0
  14. package/dist/servicios/conversaciones-del-chat.d.ts +139 -0
  15. package/dist/servicios/conversaciones-del-chat.js +51 -0
  16. package/dist/servicios/corridas-programadas.d.ts +29 -0
  17. package/dist/servicios/corridas-programadas.js +5 -0
  18. package/dist/servicios/credencial/permiso-sobre-credencial.d.ts +61 -0
  19. package/dist/servicios/credencial/permiso-sobre-credencial.js +42 -0
  20. package/dist/servicios/credenciales-del-gateway.d.ts +722 -0
  21. package/dist/servicios/credenciales-del-gateway.js +114 -0
  22. package/dist/servicios/cuentas-de-atlassian.d.ts +22 -0
  23. package/dist/servicios/cuentas-de-atlassian.js +5 -0
  24. package/dist/servicios/descarga-de-drive.d.ts +74 -0
  25. package/dist/servicios/descarga-de-drive.js +41 -0
  26. package/dist/servicios/ejecucion-de-ia.d.ts +81 -0
  27. package/dist/servicios/ejecucion-de-ia.js +39 -0
  28. package/dist/servicios/enlaces-de-flujo.d.ts +59 -0
  29. package/dist/servicios/enlaces-de-flujo.js +27 -0
  30. package/dist/servicios/ficheros-del-gateway.d.ts +210 -0
  31. package/dist/servicios/ficheros-del-gateway.js +8 -0
  32. package/dist/servicios/formas-compartidas.d.ts +57 -0
  33. package/dist/servicios/formas-compartidas.js +22 -0
  34. package/dist/servicios/historial-de-corridas.d.ts +75 -0
  35. package/dist/servicios/historial-de-corridas.js +7 -0
  36. package/dist/servicios/index.d.ts +57 -0
  37. package/dist/servicios/index.js +44 -0
  38. package/dist/servicios/limites-del-plan.d.ts +82 -0
  39. package/dist/servicios/limites-del-plan.js +41 -0
  40. package/dist/servicios/motor-de-ejecucion.d.ts +197 -0
  41. package/dist/servicios/motor-de-ejecucion.js +11 -0
  42. package/dist/servicios/plantillas-de-origen.d.ts +25 -0
  43. package/dist/servicios/plantillas-de-origen.js +5 -0
  44. package/dist/servicios/posts-sociales.d.ts +69 -0
  45. package/dist/servicios/posts-sociales.js +23 -0
  46. package/dist/servicios/registro-de-entregas.d.ts +93 -0
  47. package/dist/servicios/registro-de-entregas.js +46 -0
  48. package/dist/servicios/registro-de-eventos.d.ts +135 -0
  49. package/dist/servicios/registro-de-eventos.js +62 -0
  50. package/dist/servicios/restauracion-de-nodos.d.ts +53 -0
  51. package/dist/servicios/restauracion-de-nodos.js +4 -0
  52. package/dist/servicios/salud-del-webhook.d.ts +26 -0
  53. package/dist/servicios/salud-del-webhook.js +6 -0
  54. package/dist/servicios/secretos-de-firma.d.ts +26 -0
  55. package/dist/servicios/secretos-de-firma.js +5 -0
  56. package/dist/servicios/telemetria.d.ts +114 -0
  57. package/dist/servicios/telemetria.js +60 -0
  58. package/dist/servicios/trazas-de-llm.d.ts +79 -0
  59. package/dist/servicios/trazas-de-llm.js +23 -0
  60. package/dist/servicios/tuneles.d.ts +106 -0
  61. package/dist/servicios/tuneles.js +87 -0
  62. package/dist/servicios/workspace/acceso-al-recurso.d.ts +39 -0
  63. package/dist/servicios/workspace/acceso-al-recurso.js +5 -0
  64. package/dist/servicios/zona-horaria-del-usuario.d.ts +40 -0
  65. package/dist/servicios/zona-horaria-del-usuario.js +7 -0
  66. package/package.json +9 -3
package/README.md CHANGED
@@ -3,20 +3,80 @@
3
3
  Contratos compartidos entre los servicios de HostWebhook: lo que cruza una
4
4
  frontera y no puede estar escrito dos veces.
5
5
 
6
- Hoy contiene el registro de **addons**los complementos que se venden aparte
7
- del plan. La api decide si una petición pasa; el dashboard decide si pinta la
8
- pantalla o el candado. Los dos leen de aquí.
6
+ Cero dependencias de runtime, a propósitoel dashboard lo carga en el
7
+ navegador.
8
+
9
+ ## Addons
10
+
11
+ Los complementos que se venden aparte del plan. La api decide si una petición
12
+ pasa; el dashboard decide si pinta la pantalla o nada en absoluto. Los dos leen
13
+ de aquí.
9
14
 
10
15
  ```ts
11
16
  import { ADDONS, tieneAddon, normalizarAddons } from '@hostwebhook/platform-contracts';
12
17
 
13
- tieneAddon(user.plan.addons, 'social'); // → boolean, falla cerrado
18
+ tieneAddon(user.plan.addons, 'social'); // → boolean, falla CERRADO
14
19
  normalizarAddons(['social', 'social']); // → ['social']
15
20
  ```
16
21
 
22
+ ⚠️ `tieneAddon` falla **cerrado**: ante una entrada que no entiende, dice que
23
+ no. Es lo contrario que `llevaValor` de abajo, y la diferencia es deliberada —
24
+ lo seguro al conceder acceso es negar; lo seguro al pintar un formulario es
25
+ enseñar el campo.
26
+
27
+ ## Operadores
28
+
29
+ Las dos listas de operadores, con los tipos derivados de ellas.
30
+
31
+ ```ts
32
+ import {
33
+ FILTER_OPERATORS, // 30 — los que evalúa `filter-utils` del node-sdk
34
+ ROUTER_OPERATORS, // 9 — los que evalúa `RoutersService` de la api
35
+ llevaValor,
36
+ type FilterOperator,
37
+ type RouterOperator,
38
+ } from '@hostwebhook/platform-contracts';
39
+
40
+ llevaValor('exists'); // → false: el formulario esconde el campo del valor
41
+ llevaValor('eq'); // → true
42
+ ```
43
+
44
+ ⚠️ **`ROUTER_OPERATORS` no es `FILTER_OPERATORS` recortada.** Tiene `in` y
45
+ `not_in`, que `filter-utils` no sabe resolver, y le faltan los otros 23. Son
46
+ dos evaluadores distintos. Darle los 30 al router permitiría guardar reglas que
47
+ su `switch` no resuelve: caerían en el `default: false` y la regla nunca
48
+ casaría — sin dar error, que es lo que lo hace difícil de ver.
49
+
50
+ ### Por qué las listas están aquí y no junto a sus evaluadores
51
+
52
+ La regla natural sería «la lista vive pegada al `switch` que la resuelve». Pero
53
+ el dashboard también las necesita, para pintar los desplegables, y el evaluador
54
+ de filtros vive en `@hostwebhook/node-sdk`, que arrastra `re2` y medio runtime
55
+ de nodos. Importarlo desde el navegador para leer treinta cadenas sería pagar
56
+ un bundle entero por una constante.
57
+
58
+ Así que la regla se afina:
59
+
60
+ > la **definición** vive aquí, donde no hay dependencias de runtime; el **test**
61
+ > que la ata a su evaluador vive con el evaluador.
62
+
63
+ Quién ata cada una:
64
+
65
+ | lista | quién la ata | contra qué |
66
+ |---|---|---|
67
+ | `FILTER_OPERATORS` | `node-sdk/__tests__/una-sola-lista-de-operadores` | los `case` de `filter-utils.ts` |
68
+ | `ROUTER_OPERATORS` | `api/src/nodes/una-sola-lista-de-operadores.spec` | los `case` de `routers.service.ts` |
69
+
70
+ ⚠️ Si un evaluador se muda, **su test se muda con él**. Una lista aquí sin
71
+ nadie que la ate al otro lado es una lista que vuelve a divergir — que es
72
+ exactamente de donde se venía: había seis copias de los operadores de filtro y
73
+ ninguna igual a otra.
74
+
17
75
  ## Qué no va aquí
18
76
 
19
77
  - Precios e ids de Stripe: viven en la configuración de la api, que es la única
20
78
  que habla con Stripe. Cambiarlos no puede obligar a publicar un paquete.
21
79
  - Límites por plan: `plan.constants.ts` en la api.
80
+ - Etiquetas de pantalla: cómo se llama un operador para el usuario es cosa del
81
+ dashboard (`lib/operadores.ts`). Aquí van las claves, no los textos.
22
82
  - Cualquier cosa que sólo lea un servicio.
package/dist/index.d.ts CHANGED
@@ -10,3 +10,5 @@
10
10
  * Stripe, límites por plan).
11
11
  */
12
12
  export * from './addons';
13
+ export * from './operadores';
14
+ export * from './servicios';
package/dist/index.js CHANGED
@@ -26,3 +26,5 @@ Object.defineProperty(exports, "__esModule", { value: true });
26
26
  * Stripe, límites por plan).
27
27
  */
28
28
  __exportStar(require("./addons"), exports);
29
+ __exportStar(require("./operadores"), exports);
30
+ __exportStar(require("./servicios"), exports);
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Los operadores de filtro y de router.
3
+ *
4
+ * ## Por qué están AQUÍ y no donde vive el evaluador
5
+ *
6
+ * La regla natural sería «la lista vive pegada al `switch` que la resuelve»,
7
+ * y durante un rato lo estuvo. Pero el dashboard también necesita las listas
8
+ * —para pintar los desplegables— y el evaluador de filtros vive en
9
+ * `@hostwebhook/node-sdk`, que arrastra `re2` y medio runtime de nodos.
10
+ * Importarlo desde el navegador para leer treinta cadenas sería pagar un
11
+ * bundle entero por una constante.
12
+ *
13
+ * Así que la regla se afina: **la definición vive aquí, en el paquete que no
14
+ * tiene dependencias de runtime; el test que la ata a su evaluador vive con
15
+ * el evaluador.** La garantía es la misma —la lista no puede separarse de lo
16
+ * que la resuelve— y quien sólo necesita leerla no se trae el motor.
17
+ *
18
+ * Quién ata cada una:
19
+ *
20
+ * `FILTER_OPERATORS` → `node-sdk/__tests__/una-sola-lista-de-operadores`
21
+ * contra los `case` de `filter-utils.ts`
22
+ * `ROUTER_OPERATORS` → `api/src/nodes/una-sola-lista-de-operadores.spec`
23
+ * contra los `case` de `routers.service.ts`
24
+ *
25
+ * ⚠️ Si alguna de esas dos casas se muda, el test se muda con ella. Una lista
26
+ * aquí sin nadie que la ate al otro lado es una lista que vuelve a divergir.
27
+ */
28
+ /**
29
+ * Lo que `filter-utils` sabe evaluar. Treinta.
30
+ *
31
+ * ⚠️ No incluye `in` ni `not_in`: esos son del router, que tiene otro
32
+ * evaluador. Ver `ROUTER_OPERATORS` abajo.
33
+ */
34
+ export declare const FILTER_OPERATORS: readonly ["eq", "neq", "gt", "gte", "lt", "lte", "contains", "not_contains", "starts_with", "not_starts_with", "ends_with", "not_ends_with", "matches_regex", "is_empty", "is_not_empty", "exists", "not_exists", "has_key", "is_true", "is_false", "array_contains", "array_not_contains", "array_empty", "array_not_empty", "array_length_eq", "array_length_gt", "array_length_lt", "date_eq", "date_before", "date_after"];
35
+ export type FilterOperator = (typeof FILTER_OPERATORS)[number];
36
+ /**
37
+ * Lo que `RoutersService.evaluateRule` sabe resolver. Nueve.
38
+ *
39
+ * ⚠️ **Parece la lista de arriba recortada y no lo es.** Tiene dos que allí no
40
+ * existen —`in` y `not_in`, que parten el valor por comas y miran si el del
41
+ * campo está dentro— y le faltan los otros veintitrés.
42
+ *
43
+ * Por eso no se unifican. Si el router heredara los treinta, se podrían
44
+ * guardar reglas que su `switch` no resuelve: caerían en el `default: false`
45
+ * y la regla nunca casaría. No daría error — sencillamente no enrutaría, que
46
+ * es más difícil de ver que un fallo.
47
+ *
48
+ * Quien quiera unificarlas de verdad tiene que escribir los `case` primero.
49
+ */
50
+ export declare const ROUTER_OPERATORS: readonly ["eq", "neq", "contains", "gt", "lt", "exists", "not_exists", "in", "not_in"];
51
+ export type RouterOperator = (typeof ROUTER_OPERATORS)[number];
52
+ /**
53
+ * Los operadores que no llevan valor.
54
+ *
55
+ * Sirve a los dos lados: el formulario esconde el campo del valor y el
56
+ * resumen de un vistazo no escribe «existe undefined». Es una propiedad del
57
+ * operador, no de la pantalla, así que se declara una vez.
58
+ */
59
+ export declare const OPERADORES_SIN_VALOR: readonly ["exists", "not_exists", "is_empty", "is_not_empty", "is_true", "is_false", "array_empty", "array_not_empty"];
60
+ export declare function llevaValor(operador: string): boolean;
@@ -0,0 +1,123 @@
1
+ "use strict";
2
+ /**
3
+ * Los operadores de filtro y de router.
4
+ *
5
+ * ## Por qué están AQUÍ y no donde vive el evaluador
6
+ *
7
+ * La regla natural sería «la lista vive pegada al `switch` que la resuelve»,
8
+ * y durante un rato lo estuvo. Pero el dashboard también necesita las listas
9
+ * —para pintar los desplegables— y el evaluador de filtros vive en
10
+ * `@hostwebhook/node-sdk`, que arrastra `re2` y medio runtime de nodos.
11
+ * Importarlo desde el navegador para leer treinta cadenas sería pagar un
12
+ * bundle entero por una constante.
13
+ *
14
+ * Así que la regla se afina: **la definición vive aquí, en el paquete que no
15
+ * tiene dependencias de runtime; el test que la ata a su evaluador vive con
16
+ * el evaluador.** La garantía es la misma —la lista no puede separarse de lo
17
+ * que la resuelve— y quien sólo necesita leerla no se trae el motor.
18
+ *
19
+ * Quién ata cada una:
20
+ *
21
+ * `FILTER_OPERATORS` → `node-sdk/__tests__/una-sola-lista-de-operadores`
22
+ * contra los `case` de `filter-utils.ts`
23
+ * `ROUTER_OPERATORS` → `api/src/nodes/una-sola-lista-de-operadores.spec`
24
+ * contra los `case` de `routers.service.ts`
25
+ *
26
+ * ⚠️ Si alguna de esas dos casas se muda, el test se muda con ella. Una lista
27
+ * aquí sin nadie que la ate al otro lado es una lista que vuelve a divergir.
28
+ */
29
+ Object.defineProperty(exports, "__esModule", { value: true });
30
+ exports.OPERADORES_SIN_VALOR = exports.ROUTER_OPERATORS = exports.FILTER_OPERATORS = void 0;
31
+ exports.llevaValor = llevaValor;
32
+ /* ── Filtros ─────────────────────────────────────────────────────── */
33
+ /**
34
+ * Lo que `filter-utils` sabe evaluar. Treinta.
35
+ *
36
+ * ⚠️ No incluye `in` ni `not_in`: esos son del router, que tiene otro
37
+ * evaluador. Ver `ROUTER_OPERATORS` abajo.
38
+ */
39
+ exports.FILTER_OPERATORS = [
40
+ /* Comparación */
41
+ 'eq',
42
+ 'neq',
43
+ 'gt',
44
+ 'gte',
45
+ 'lt',
46
+ 'lte',
47
+ /* Texto */
48
+ 'contains',
49
+ 'not_contains',
50
+ 'starts_with',
51
+ 'not_starts_with',
52
+ 'ends_with',
53
+ 'not_ends_with',
54
+ 'matches_regex',
55
+ 'is_empty',
56
+ 'is_not_empty',
57
+ /* Presencia */
58
+ 'exists',
59
+ 'not_exists',
60
+ 'has_key',
61
+ /* Booleanos */
62
+ 'is_true',
63
+ 'is_false',
64
+ /* Listas */
65
+ 'array_contains',
66
+ 'array_not_contains',
67
+ 'array_empty',
68
+ 'array_not_empty',
69
+ 'array_length_eq',
70
+ 'array_length_gt',
71
+ 'array_length_lt',
72
+ /* Fechas */
73
+ 'date_eq',
74
+ 'date_before',
75
+ 'date_after',
76
+ ];
77
+ /* ── Router ──────────────────────────────────────────────────────── */
78
+ /**
79
+ * Lo que `RoutersService.evaluateRule` sabe resolver. Nueve.
80
+ *
81
+ * ⚠️ **Parece la lista de arriba recortada y no lo es.** Tiene dos que allí no
82
+ * existen —`in` y `not_in`, que parten el valor por comas y miran si el del
83
+ * campo está dentro— y le faltan los otros veintitrés.
84
+ *
85
+ * Por eso no se unifican. Si el router heredara los treinta, se podrían
86
+ * guardar reglas que su `switch` no resuelve: caerían en el `default: false`
87
+ * y la regla nunca casaría. No daría error — sencillamente no enrutaría, que
88
+ * es más difícil de ver que un fallo.
89
+ *
90
+ * Quien quiera unificarlas de verdad tiene que escribir los `case` primero.
91
+ */
92
+ exports.ROUTER_OPERATORS = [
93
+ 'eq',
94
+ 'neq',
95
+ 'contains',
96
+ 'gt',
97
+ 'lt',
98
+ 'exists',
99
+ 'not_exists',
100
+ 'in',
101
+ 'not_in',
102
+ ];
103
+ /* ── Lo que tienen en común ──────────────────────────────────────── */
104
+ /**
105
+ * Los operadores que no llevan valor.
106
+ *
107
+ * Sirve a los dos lados: el formulario esconde el campo del valor y el
108
+ * resumen de un vistazo no escribe «existe undefined». Es una propiedad del
109
+ * operador, no de la pantalla, así que se declara una vez.
110
+ */
111
+ exports.OPERADORES_SIN_VALOR = [
112
+ 'exists',
113
+ 'not_exists',
114
+ 'is_empty',
115
+ 'is_not_empty',
116
+ 'is_true',
117
+ 'is_false',
118
+ 'array_empty',
119
+ 'array_not_empty',
120
+ ];
121
+ function llevaValor(operador) {
122
+ return !exports.OPERADORES_SIN_VALOR.includes(operador);
123
+ }
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Lo que un nodo le pide a un almacén vectorial.
3
+ *
4
+ * Costura de la Fase 2, del lote de las pequeñas. `VectorStoresService`
5
+ * arrastra tres colecciones, los backends de vectores —Atlas, pgvector,
6
+ * Pinecone—, el cifrado de credenciales y el registro de accesos. El nodo
7
+ * sólo consulta, inserta y borra.
8
+ *
9
+ * ## ⚠️ Lo que NO puede perderse: la marca de quién
10
+ *
11
+ * Los tres métodos que TOCAN datos llevan `userId`, `sourceIp` y
12
+ * `relatedEntity`, y no son telemetría de adorno: son lo que el registro de
13
+ * accesos del almacén guarda para poder decir quién consultó qué. Hay un
14
+ * guardián dedicado a eso (`el-nodo-se-firma-en-el-registro.spec.ts`) porque
15
+ * un nodo que se cuela sin firmar deja el registro mintiendo.
16
+ *
17
+ * ⚠️ Y `relatedEntity` es lo que distingue a un nodo de su DUEÑO: un nodo
18
+ * corre con el `userId` del usuario que lo creó, así que sin la entidad
19
+ * relacionada la fila del registro dice «lo consultó Ariel» cuando lo
20
+ * consultó un flujo. Ver `project_el_registro_dice_quien`.
21
+ */
22
+ /** Quién —o qué— está detrás de una operación sobre el almacén. */
23
+ export interface EntidadRelacionada {
24
+ type: string;
25
+ id: string;
26
+ name?: string;
27
+ }
28
+ /** Un trozo devuelto por una consulta. */
29
+ export interface TrozoEncontrado {
30
+ chunkId: string;
31
+ content: string;
32
+ score: number;
33
+ sourceDocId?: string;
34
+ sourceDocName?: string;
35
+ sourceDocType?: string;
36
+ metadata?: Record<string, unknown>;
37
+ createdAt?: Date;
38
+ }
39
+ import type { ChunkingStrategy } from './formas-compartidas';
40
+ /**
41
+ * El troceo que el almacén trae de fábrica.
42
+ *
43
+ * ⚠️ `chunkSize` y `chunkOverlap` son en CARACTERES, no en tokens. Está en el
44
+ * contrato porque es justo lo que se malinterpreta al leerlo de fuera; ver
45
+ * `project_vector_store_troceo`.
46
+ */
47
+ export interface TroceoPorDefecto {
48
+ strategy: ChunkingStrategy;
49
+ chunkSize: number;
50
+ chunkOverlap: number;
51
+ separators?: string[] | null;
52
+ }
53
+ export interface ConsultaAlAlmacen {
54
+ query: string;
55
+ topK?: number;
56
+ filter?: Record<string, unknown>;
57
+ minScore?: number;
58
+ }
59
+ export interface BorradoDeTrozos {
60
+ chunkIds?: string[];
61
+ sourceDocId?: string;
62
+ metadata?: Record<string, unknown>;
63
+ }
64
+ export interface AlmacenesVectoriales {
65
+ query(storeId: string, orgId: string, dto: ConsultaAlAlmacen, userId: string, sourceIp: string, relatedEntity?: EntidadRelacionada): Promise<{
66
+ matches: TrozoEncontrado[];
67
+ tookMs: number;
68
+ storeName: string;
69
+ storeId: string;
70
+ }>;
71
+ /**
72
+ * El almacén, para leerle la configuración de troceo por defecto.
73
+ *
74
+ * ⚠️ Devuelve SÓLO `chunking`, que es lo único que su llamante lee — se
75
+ * comprobó campo por campo. El documento entero trae además el backend, sus
76
+ * credenciales cifradas y el recuento de vectores; nada de eso tiene por
77
+ * qué cruzar.
78
+ *
79
+ * ⚠️ Y lanza si el almacén no es de esa organización. Eso hay que
80
+ * conservarlo: es la comprobación de propiedad, no un detalle.
81
+ */
82
+ getStore(id: string, orgId: string): Promise<{
83
+ chunking: TroceoPorDefecto;
84
+ }>;
85
+ findExistingHashes(storeId: string, orgId: string, hashes: string[]): Promise<string[]>;
86
+ deleteChunks(storeId: string, orgId: string, dto: BorradoDeTrozos, userId: string, sourceIp: string, relatedEntity?: EntidadRelacionada): Promise<{
87
+ deletedCount: number;
88
+ }>;
89
+ ingestPreChunked(storeId: string, orgId: string, opts: {
90
+ chunks: Array<{
91
+ content: string;
92
+ metadata?: Record<string, unknown>;
93
+ }>;
94
+ sourceDocId?: string;
95
+ sourceDocName?: string;
96
+ sourceDocType?: string;
97
+ baseMetadata?: Record<string, unknown>;
98
+ }, userId: string, sourceIp: string, relatedEntity?: EntidadRelacionada): Promise<{
99
+ chunksInserted: number;
100
+ skipped: number;
101
+ bytes: number;
102
+ }>;
103
+ }
104
+ /** ⚠️ Cadena y no `Symbol`, igual que el resto de tokens de la Fase 2. */
105
+ export declare const ALMACENES_VECTORIALES = "ALMACENES_VECTORIALES";
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ /**
3
+ * Lo que un nodo le pide a un almacén vectorial.
4
+ *
5
+ * Costura de la Fase 2, del lote de las pequeñas. `VectorStoresService`
6
+ * arrastra tres colecciones, los backends de vectores —Atlas, pgvector,
7
+ * Pinecone—, el cifrado de credenciales y el registro de accesos. El nodo
8
+ * sólo consulta, inserta y borra.
9
+ *
10
+ * ## ⚠️ Lo que NO puede perderse: la marca de quién
11
+ *
12
+ * Los tres métodos que TOCAN datos llevan `userId`, `sourceIp` y
13
+ * `relatedEntity`, y no son telemetría de adorno: son lo que el registro de
14
+ * accesos del almacén guarda para poder decir quién consultó qué. Hay un
15
+ * guardián dedicado a eso (`el-nodo-se-firma-en-el-registro.spec.ts`) porque
16
+ * un nodo que se cuela sin firmar deja el registro mintiendo.
17
+ *
18
+ * ⚠️ Y `relatedEntity` es lo que distingue a un nodo de su DUEÑO: un nodo
19
+ * corre con el `userId` del usuario que lo creó, así que sin la entidad
20
+ * relacionada la fila del registro dice «lo consultó Ariel» cuando lo
21
+ * consultó un flujo. Ver `project_el_registro_dice_quien`.
22
+ */
23
+ Object.defineProperty(exports, "__esModule", { value: true });
24
+ exports.ALMACENES_VECTORIALES = void 0;
25
+ /** ⚠️ Cadena y no `Symbol`, igual que el resto de tokens de la Fase 2. */
26
+ exports.ALMACENES_VECTORIALES = 'ALMACENES_VECTORIALES';
@@ -0,0 +1,207 @@
1
+ /**
2
+ * Lo que una PUERTA del gateway necesita de las aprobaciones pendientes.
3
+ *
4
+ * ## Por qué existe
5
+ *
6
+ * `source-nodes/triggers` —el trigger de Telegram— y `social-posts` viven en el
7
+ * gateway; `approval-nodes` se muda a hw-nodes. Entre los dos había cuatro
8
+ * llamadas directas a `ApprovalNodesService`, y una de ellas pasaba un
9
+ * **documento de Mongoose vivo** al motor.
10
+ *
11
+ * ## 🔥 Por qué el contrato NO es «los mismos cuatro métodos»
12
+ *
13
+ * El código de hoy hace esto:
14
+ *
15
+ * const fila = await pendingService.resolvePendingByToken({…});
16
+ * await eventPipeline.resumeAfterSendAndWait(fila, {…});
17
+ *
18
+ * O sea: la puerta resuelve la fila y le pasa el DOCUMENTO al motor, que le lee
19
+ * seis campos más (`eventId`, `organizationId`, `webhookId`, `payload`,
20
+ * `eventHeaders`, `id`). Un documento de Mongoose no cruza una red, así que
21
+ * copiar esos métodos tal cual obligaría a serializar la fila entera y a
22
+ * mantener las dos formas en paralelo.
23
+ *
24
+ * Pero es que no hace falta: **el motor y las aprobaciones están del MISMO
25
+ * lado**. Los dos se van a hw-nodes. Lo único que cruza es la decisión —quién
26
+ * pulsó, cuándo, qué escribió—, y el documento se queda donde vive.
27
+ *
28
+ * Por eso hay un solo método para las dos cosas, `resolverYReanudar`.
29
+ *
30
+ * ## ⚠️ La distinción que hay que conservar
31
+ *
32
+ * Hoy los dos fallos NO se tratan igual, y eso es visible para el usuario:
33
+ *
34
+ * - si no se puede RESOLVER la fila, a Telegram le vuelve «No pude registrar
35
+ * tu respuesta. Inténtalo de nuevo»;
36
+ * - si falla el REANUDADO, se registra y ya: la fila está resuelta, el
37
+ * usuario ve su confirmación, y volver a lanzar aguas abajo necesitaría a
38
+ * un operador de todas formas.
39
+ *
40
+ * Por eso `resolverYReanudar` devuelve un booleano que habla SÓLO de la
41
+ * resolución. Fundir los dos fallos en uno cambiaría lo que ve el usuario sin
42
+ * tocar una línea de la UI.
43
+ */
44
+ /** La fila pendiente, en lo que la puerta lee de ella. Seis campos, medidos. */
45
+ export interface AprobacionPendiente {
46
+ /** El `id` virtual de Mongoose, ya en cadena. */
47
+ id: string;
48
+ /** `pending` | `approved` | `rejected` | `expired`. */
49
+ status: string;
50
+ /** De dónde salió: `telegramSendAndWait`, `approval`, `socialMedia`… */
51
+ source: string;
52
+ /** El contexto con el que se creó. La puerta lo lee entero y opaco. */
53
+ pipelineContext: Record<string, unknown>;
54
+ /** El nodo que la pidió, o `null` si no hubo uno. */
55
+ sourceNodeId: string | null;
56
+ /** El token con el que se resuelve. */
57
+ resolveToken: string;
58
+ /** De quién es. Las puertas públicas lo necesitan para acotar sus lecturas. */
59
+ organizationId: string;
60
+ /** El evento que la creó, si lo hubo. */
61
+ eventId: string | null;
62
+ /** El webhook que lo trajo, si lo hubo. */
63
+ webhookId: string | null;
64
+ /** Cuándo se pidió y cuándo caduca — la página se lo dice a quien llega
65
+ * tarde. */
66
+ requestedAt: Date | null;
67
+ expiresAt: Date | null;
68
+ }
69
+ /** Quién decidió, cuándo y qué dijo. Es lo único que cruza. */
70
+ export interface DecisionSobrePendiente {
71
+ token: string;
72
+ aprobada: boolean;
73
+ decidedAt: Date;
74
+ /** Etiqueta legible: `telegram:ariel`, `telegram-callback`… */
75
+ decidedBy: string;
76
+ /** De dónde vino la decisión, para la auditoría. */
77
+ ipAddress: string;
78
+ comment?: string;
79
+ /**
80
+ * La respuesta escrita a mano, cuando la hay.
81
+ *
82
+ * ⚠️ Sólo el camino de texto libre de Telegram la trae; un botón no. Sale en
83
+ * `_waitResponse.responseText` para que un Conditional o un nodo de IA de
84
+ * abajo puedan leerla.
85
+ */
86
+ responseText?: string;
87
+ }
88
+ export interface AprobacionesPendientes {
89
+ /** La fila de ese token, o `null` si no existe. */
90
+ porToken(token: string): Promise<AprobacionPendiente | null>;
91
+ /**
92
+ * La pendiente MÁS ANTIGUA de ese chat de Telegram que admita texto libre.
93
+ *
94
+ * ⚠️ Primero en llegar, primero en resolverse: si un chat tiene dos
95
+ * esperando, la respuesta escrita resuelve la de arriba. Y la comparación va
96
+ * por tipo exacto —`telegramChatId` se guarda como NÚMERO y `allowFreeText`
97
+ * como BOOLEANO—, o una coerción de cadena a número deja de encontrar la
98
+ * fila sin decir nada.
99
+ */
100
+ deTelegram(orgId: string, chatId: number | string): Promise<AprobacionPendiente | null>;
101
+ /**
102
+ * Resuelve la fila y reanuda el flujo, del otro lado y en proceso.
103
+ *
104
+ * ⚠️ El booleano habla SÓLO de la resolución. Un fallo al reanudar se
105
+ * registra y devuelve `true`, porque la fila ya está resuelta y el usuario ya
106
+ * tiene su confirmación. Ver la cabecera.
107
+ */
108
+ resolverYReanudar(decision: DecisionSobrePendiente): Promise<boolean>;
109
+ /**
110
+ * Resuelve por ID y reanuda. Hermana de `resolverYReanudar`.
111
+ *
112
+ * ⚠️ Son DOS y no una porque abajo hay dos métodos distintos, no dos
113
+ * nombres del mismo: `resolvePendingByToken` empareja por token, y `resolve`
114
+ * empareja por id Y comprueba `allowedApproverIds` cuando hay un usuario
115
+ * detrás. Fundirlas aquí escondería esa comprobación —una aprobación
116
+ * restringida al director la resolvía cualquier empleado hasta que se
117
+ * arregló—, así que cada puerta llama a la suya.
118
+ *
119
+ * El enlace de un clic no trae identidad: resuelve con una etiqueta, así que
120
+ * la comprobación de aprobador no le aplica. Pero el método sí es el otro.
121
+ */
122
+ resolverPorIdYReanudar(decision: {
123
+ pendingId: string;
124
+ orgId: string;
125
+ aprobada: boolean;
126
+ decidedAt: Date;
127
+ /** Etiqueta, no identidad: en un enlace de un clic sólo hay una IP. */
128
+ decidedBy: string;
129
+ ipAddress?: string;
130
+ comment?: string;
131
+ }): Promise<boolean>;
132
+ /**
133
+ * Resuelve y NO encadena nada. Devuelve la fila resuelta, aplanada.
134
+ *
135
+ * ⚠️ Es la tercera resolución del contrato, y las tres existen porque lo que
136
+ * SIGUE a una resolución es distinto en cada puerta:
137
+ *
138
+ * `resolverYReanudar` resuelve por token → reanuda el flujo
139
+ * `resolverPorIdYReanudar` resuelve por id → reanuda el flujo
140
+ * `resolverAprobacionDeRedes` resuelve por token → nada, el que llama decide
141
+ *
142
+ * La de redes se bifurca DESPUÉS: un post del calendario se publica por su
143
+ * camino, que es del gateway, y el plan de un nodo por el suyo, que es del
144
+ * ejecutor. Fundir eso aquí obligaría al contrato a saber de calendarios.
145
+ *
146
+ * ⚠️ Devuelve la fila porque quien llama necesita su `pipelineContext` para
147
+ * elegir la rama — y `null` cuando no ganó la reclamación, que es lo que
148
+ * distingue «resuelta por mí» de «alguien se me adelantó».
149
+ */
150
+ resolverAprobacionDeRedes(decision: {
151
+ token: string;
152
+ aprobada: boolean;
153
+ decidedBy: string;
154
+ comment?: string | null;
155
+ }): Promise<AprobacionPendiente | null>;
156
+ /**
157
+ * Publica el plan de una fila que QUIEN LLAMA acaba de reclamar.
158
+ *
159
+ * ⚠️ El nombre lo dice a propósito: la garantía de una sola publicación no la
160
+ * da el estado de la fila, la da haber ganado la reclamación. Llamar a esto
161
+ * sin haberla ganado es un error, y del otro lado se rechaza.
162
+ */
163
+ publicarPlanReclamado(pendingId: string, orgId: string): Promise<{
164
+ statusCode: number;
165
+ responseBody: string;
166
+ }>;
167
+ /**
168
+ * ¿La página de rechazo tiene que pedir un comentario?
169
+ *
170
+ * ⚠️ Sale de `operationConfig.requireDisapproveComment` del nodo que creó la
171
+ * pendiente —Gmail primero, Email de respaldo, porque `sendAndWaitForResponse`
172
+ * pasó al nodo de Gmail al repartirse y los pendientes de antes apuntan a la
173
+ * fila vieja—. Es UN booleano, así que se pide en vez de traerse el nodo: el
174
+ * gateway sólo tiene que pintar la página.
175
+ *
176
+ * ⚠️ Y va en su propio método, no en `AprobacionPendiente`: sólo hace falta
177
+ * en el camino de rechazo, y meterlo en la fila obligaría a dos búsquedas
178
+ * extra en CADA lectura.
179
+ */
180
+ pideComentarioAlRechazar(pendingId: string): Promise<{
181
+ /** Si es `false`, el rechazo sigue siendo de un clic. */
182
+ pide: boolean;
183
+ /** Los dos textos de la página, tal y como los configuró quien monta el
184
+ * flujo. `undefined` deja el de por defecto. */
185
+ label?: string;
186
+ placeholder?: string;
187
+ }>;
188
+ /** Da de alta la aprobación de una publicación en redes. */
189
+ crearParaAprobacionDeRedes(params: {
190
+ sourceNodeId: string;
191
+ eventId: string;
192
+ webhookId: string;
193
+ orgId: string;
194
+ payload: Record<string, unknown>;
195
+ headers: Record<string, unknown>;
196
+ plan: Record<string, unknown>;
197
+ timeoutMinutes: number;
198
+ }): Promise<{
199
+ id: string;
200
+ resolveToken: string;
201
+ }>;
202
+ }
203
+ /**
204
+ * ⚠️ Cadena y no `Symbol`, igual que los otros tokens de esta fase: un
205
+ * `Symbol` no sobrevive a una frontera de módulo duplicada.
206
+ */
207
+ export declare const APROBACIONES_PENDIENTES = "APROBACIONES_PENDIENTES";
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ /**
3
+ * Lo que una PUERTA del gateway necesita de las aprobaciones pendientes.
4
+ *
5
+ * ## Por qué existe
6
+ *
7
+ * `source-nodes/triggers` —el trigger de Telegram— y `social-posts` viven en el
8
+ * gateway; `approval-nodes` se muda a hw-nodes. Entre los dos había cuatro
9
+ * llamadas directas a `ApprovalNodesService`, y una de ellas pasaba un
10
+ * **documento de Mongoose vivo** al motor.
11
+ *
12
+ * ## 🔥 Por qué el contrato NO es «los mismos cuatro métodos»
13
+ *
14
+ * El código de hoy hace esto:
15
+ *
16
+ * const fila = await pendingService.resolvePendingByToken({…});
17
+ * await eventPipeline.resumeAfterSendAndWait(fila, {…});
18
+ *
19
+ * O sea: la puerta resuelve la fila y le pasa el DOCUMENTO al motor, que le lee
20
+ * seis campos más (`eventId`, `organizationId`, `webhookId`, `payload`,
21
+ * `eventHeaders`, `id`). Un documento de Mongoose no cruza una red, así que
22
+ * copiar esos métodos tal cual obligaría a serializar la fila entera y a
23
+ * mantener las dos formas en paralelo.
24
+ *
25
+ * Pero es que no hace falta: **el motor y las aprobaciones están del MISMO
26
+ * lado**. Los dos se van a hw-nodes. Lo único que cruza es la decisión —quién
27
+ * pulsó, cuándo, qué escribió—, y el documento se queda donde vive.
28
+ *
29
+ * Por eso hay un solo método para las dos cosas, `resolverYReanudar`.
30
+ *
31
+ * ## ⚠️ La distinción que hay que conservar
32
+ *
33
+ * Hoy los dos fallos NO se tratan igual, y eso es visible para el usuario:
34
+ *
35
+ * - si no se puede RESOLVER la fila, a Telegram le vuelve «No pude registrar
36
+ * tu respuesta. Inténtalo de nuevo»;
37
+ * - si falla el REANUDADO, se registra y ya: la fila está resuelta, el
38
+ * usuario ve su confirmación, y volver a lanzar aguas abajo necesitaría a
39
+ * un operador de todas formas.
40
+ *
41
+ * Por eso `resolverYReanudar` devuelve un booleano que habla SÓLO de la
42
+ * resolución. Fundir los dos fallos en uno cambiaría lo que ve el usuario sin
43
+ * tocar una línea de la UI.
44
+ */
45
+ Object.defineProperty(exports, "__esModule", { value: true });
46
+ exports.APROBACIONES_PENDIENTES = void 0;
47
+ /**
48
+ * ⚠️ Cadena y no `Symbol`, igual que los otros tokens de esta fase: un
49
+ * `Symbol` no sobrevive a una frontera de módulo duplicada.
50
+ */
51
+ exports.APROBACIONES_PENDIENTES = 'APROBACIONES_PENDIENTES';