Expresiones CEL
El lenguaje de expresiones usado para fórmulas, predicados, programaciones y cadenas con plantilla — expuesto mediante cinco plantillas etiquetadas.
Expresiones CEL
ObjectOS usa CEL (Common Expression Language) para cada lugar donde necesitas una expresión pequeña, segura y aislada: campos de fórmula, reglas de validación, predicados de visibilidad, condiciones de compartición, guardas de flujo, programaciones y cadenas con plantilla.
La autoría se realiza a través de cinco plantillas etiquetadas
importadas desde @objectstack/spec. Todas producen un pequeño objeto JSON
que el runtime analiza:
{ dialect: 'cel' | 'template' | 'cron', source: string }Fuente del esquema:
packages/spec/src/shared/expression.zod.ts.
Las cinco plantillas etiquetadas
| Plantilla | Dialecto | Úsala para | Ejemplo |
|---|---|---|---|
F`...` | cel | Campos de fórmula — valores derivados almacenados junto al registro | F`record.amount * 0.1` |
P`...` | cel | Predicados — booleanos para validación / compartición / visibilidad / condiciones | P`record.status == "open"` |
cel`...` | cel | CEL general — cuando ni F ni P encajan (p. ej. el valor de un parámetro) | cel`now() + duration("P30D")` |
tmpl`...` | template | Plantillas de cadenas con interpolación {{var}} | tmpl`Order from {{record.customer.name}}` |
cron`...` | cron | Programaciones — sintaxis cron estándar de 5 campos | cron`0 9 * * 1-5` |
No hay diferencia funcional entre F, P y cel en el momento de la
evaluación — todas ejecutan CEL. La división existe para que los esquemas
(y los agentes de IA) sepan qué rol desempeña la expresión y para que el
editor pueda comprobar tipos (las fórmulas deben devolver un valor, los
predicados deben devolver un bool).
Importación
import { F, P, cel, tmpl, cron } from '@objectstack/spec'Dónde se usa cada una
| Campo en una spec | Etiqueta | Ubicación de ejemplo |
|---|---|---|
Field.expression (tipo fórmula) | F | campos de fórmula en *.object.ts |
Field.conditionalRequired | P | campos de objeto |
Validation.predicate | P | validaciones de objeto |
SharingRule.condition | P | reglas de compartición |
View.conditionalFormatting[].condition | P | vistas |
Flow.step.when / Flow.transition.when | P | flujos |
Action.guard | P | acciones |
| Asuntos de notificaciones / cuerpos de mensaje | tmpl | notificaciones |
Schedule.cron | cron | flujos / informes programados |
| Cualquier parámetro de valor | cel | entradas de pasos de flujo |
Ámbito de variables
Las expresiones CEL se evalúan en un contexto con estas variables de nivel superior:
| Variable | Disponible cuando | Contenido |
|---|---|---|
record | casi siempre | El registro actual que se está evaluando |
previous | en hooks de actualización / detección de cambios | El estado del registro previo al cambio (o null) |
input | acciones, pasos de flujo | La carga útil de entrada proporcionada por el usuario |
os.user | siempre | { id, roles: string[], permissions: string[] } |
os.org | siempre | Contexto de organización / tenant |
os.env | siempre | Variables de entorno expuestas a las expresiones |
Las variables heredadas
OLD/NEWse eliminaron en M9.5. Usapreviousyrecord.
Biblioteca estándar
Registrada en
packages/formula/src/stdlib.ts.
Las funciones integradas más usadas:
Tiempo
| Función | Devuelve | Notas |
|---|---|---|
now() | Timestamp | Fijada al contexto de evaluación — estable dentro de una consulta |
today() | Timestamp | Inicio del día UTC |
daysFromNow(int) | Timestamp | Fecha futura |
daysAgo(int) | Timestamp | Fecha pasada |
CEL también incluye los nativos timestamp(...), duration(...),
date.getDayOfWeek(), etc. — consulta la
especificación de CEL.
Utilidad
| Función | Propósito |
|---|---|
isBlank(x) | true si es null, undefined, "" o una lista vacía |
coalesce(a, b) | Primer valor no nulo |
trim(s) | Elimina espacios en blanco |
joinNonEmpty(list, sep) | Concatena las entradas no vacías |
Los ayudantes de cadenas nativos de CEL (.contains(...), .startsWith(...),
.matches(...), .size()) siempre están disponibles.
Ejemplos
Campo de fórmula — total de línea de artículo:
{ name: 'subtotal', type: 'formula', expression: F`record.quantity * record.unit_price` }Validación — la fecha de cierre debe ser posterior a hoy:
{ message: 'Close date must be in the future', predicate: P`record.close_date > today()` }Visibilidad — mostrar el campo solo a los gerentes:
{ visibleIf: P`'manager' in os.user.roles` }Guarda de flujo — omitir el paso cuando el importe es pequeño:
{ when: P`record.amount >= 1000` }Programación — días laborables a las 9 a. m.:
{ schedule: cron`0 9 * * 1-5` }Plantilla — asunto de notificación:
{ subject: tmpl`[{{record.priority}}] {{record.subject}}` }Errores
Las expresiones se compilan en el momento de la carga. Los fallos aparecen como
VALIDATION_ERROR con la ubicación en la fuente:
{ "code": "VALIDATION_ERROR", "message": "CEL: unknown field 'amout' on Record", "details": { "field": "subtotal", "expression": "record.amout * 0.1" } }Las expresiones inválidas no fallan en silencio. Una expresión mal formada o
con un campo desconocido hace fallar os compile con el mensaje localizado de
arriba (incluida una sugerencia «quizás quisiste decir» para un campo mal
escrito). En tiempo de ejecución, una expresión incorrecta lanza un error
atribuido en lugar de evaluarse silenciosamente a null o false, de modo que
el fallo es visible en los logs y en el registro de auditoría en lugar de
corromper de forma silenciosa el valor de una fórmula o la decisión de una
guarda.
Véase también
- Tipos de campo — campos de fórmula y de obligatoriedad condicional
- Build → Modelo de datos — validaciones y predicados
- Build → Flujos — guardas y programaciones
@objectstack/spec/shared/expression.zod.ts— esquema@objectstack/formula/stdlib.ts— funciones integradas