@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 +64 -4
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/operadores.d.ts +60 -0
- package/dist/operadores.js +123 -0
- package/package.json +1 -1
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
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
Cero dependencias de runtime, a propósito — el 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
|
|
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
package/dist/index.js
CHANGED
|
@@ -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.
|
|
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",
|