@hostwebhook/platform-contracts 0.1.0 → 0.2.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
@@ -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,4 @@
10
10
  * Stripe, límites por plan).
11
11
  */
12
12
  export * from './addons';
13
+ export * from './operadores';
package/dist/index.js CHANGED
@@ -26,3 +26,4 @@ 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);
@@ -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
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hostwebhook/platform-contracts",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Contratos compartidos entre los servicios de HostWebhook: addons del plan, identidad interna, y las formas que cruzan una frontera de red",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",