docs(arts): A2 ES->EN — sium (full translation)

Full Spanish -> English translation of sium/README.md (862 lines; faithful).
Code blocks, schema examples, issue codes and `#?sium.errors.*` idlangrefs kept
verbatim. Also fixed a stale ref: `/test/sium` -> `/active/docs/sium`. Carries
the A1 additions (cssLength/cssValue in Included Refines).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
menubar-v4-safe
dev 3 months ago
parent 52c0631f97
commit 5a81ecd923

@ -1,53 +1,52 @@
# Sium
Sium es la capa de contratos, validacion e introspeccion de Active.
Sium is Active's contract, validation and introspection layer.
Su objetivo no es competir con Zod, Valibot o ArkType como libreria generalista. Sium existe para que Active tenga un contrato nativo entre datos, validacion, errores traducibles, tipos de dominio y UI auto-generada.
Its goal is not to compete with Zod, Valibot or ArkType as a general-purpose library. Sium exists so Active has a native contract between data, validation, translatable errors, domain types and auto-generated UI.
Este archivo es la unica documentacion Markdown del modulo. Si cambia el comportamiento de Sium, actualiza este README en lugar de crear documentos paralelos.
This file is the module's only Markdown documentation. If Sium's behavior changes, update this README instead of creating parallel documents.
## Sium no forma parte de active-app core
## Sium is not part of active-app core
`createActiveApp(...)` deliberadamente no construye Sium. La validacion es
**page-scoped**: solo las paginas con formularios la necesitan. Cargar todo
el core de schemas/types/issues en paginas sin formularios serìa coste
innecesario.
`createActiveApp(...)` deliberately does not build Sium. Validation is
**page-scoped**: only pages with forms need it. Loading the whole
schemas/types/issues core on pages without forms would be unnecessary cost.
El patron estandar es una linea al inicio del modulo de la pagina:
The standard pattern is a single line at the top of the page module:
```ts
import { createEngineSium } from '$sium';
const sium = createEngineSium({ langs: App.langs, logger: App.logger });
```
Asi cada formulario obtiene un engine afinado a sus necesidades (logger
category, contexto de validacion, etc.) y comparte la traduccion de issues +
el bus de logs con la app.
This way each form gets an engine tuned to its needs (logger category,
validation context, etc.) and shares the issue translation + the log bus with
the app.
## Estado
## Status
Core, tipos de dominio, lenguajes, Standard Schema e introspeccion estan activos.
Core, domain types, languages, Standard Schema and introspection are active.
Sium no tiene adapter Svelte propio, por diseno: la integracion con UI es agnostica al validador. Los formularios (`createForm` / `Form.Provider`) consumen cualquier schema via `~standard`, `Form.AutoFields` auto-renderiza via la introspeccion `~sium`, y langs/logger se inyectan por `createEngineSium({ langs, logger })`. El core sigue siendo TypeScript puro.
Sium has no Svelte adapter of its own, by design: the UI integration is validator-agnostic. Forms (`createForm` / `Form.Provider`) consume any schema via `~standard`, `Form.AutoFields` auto-renders via the `~sium` introspection, and langs/logger are injected through `createEngineSium({ langs, logger })`. The core stays pure TypeScript.
## Novedades 2.0
## What's New in 2.0
Ampliacion alineada a lo que el ecosistema Active necesita (formularios, storage, http). NO busca paridad con Zod/Valibot.
An extension aligned with what the Active ecosystem needs (forms, storage, http). It does NOT aim for parity with Zod/Valibot.
- **Refines de formato (string):** `uuid()`, `slug()`, `datetime()` (ISO 8601), `ipv4()`.
- **Refines numericos:** `finite()` (rechaza `Infinity`/`-Infinity`/`NaN`), `positive()` (> 0), `nonnegative()` (>= 0), `multipleOf(n)`.
- **Coercion:** `coerceNumber()`, `coerceBoolean()`, `coerceString()` — hermanos generales de `coerceDate()` para entradas sueltas (inputs HTML, query strings).
- **Standard Schema sincrono:** `schema['~standard'].validate(x)` resuelve de forma SINCRONA cuando el schema no tiene pasos async (devuelve el `Result` directo en vez de `Promise`). SS v1 permite `Result | Promise<Result>`; esto elimina el branch async + el workaround `untrack` en los consumidores (`form-core`, `storage`) para schemas sincronos.
- **Transforms de string:** `trim()`, `toLowerCase()`, `toUpperCase()` para normalizar formularios.
- **Modificador `nullish()`:** acepta `undefined` y `null` (= `optional` + `nullable`).
- **Combinadores `tuple()` y `record()`:** tupla heterogenea de longitud fija + diccionario de claves arbitrarias. Amplian el vocabulario `SchemaKind` (`'tuple'`/`'record'`); `Form.AutoFields` los degrada con un aviso "not supported yet".
- **Utilidades de object:** `pick` / `omit` / `partial` / `extend` / `merge` — derivan un `object()` reusando los campos del original, con tipos precisos via `Pick`/`Omit`/`Partial`.
- **API depurada:** `dateValue()` / `timeValue()` ya no aceptan `opts` (eran ignorados). Para validar granularidad/calendario, compon un `refine` sobre el valor.
- 8 nuevos issue codes (`uuid`, `slug`, `datetime`, `ipv4`, `finite`, `positive`, `nonnegative`, `multiple_of`) con traduccion es/en.
- **Format refines (string):** `uuid()`, `slug()`, `datetime()` (ISO 8601), `ipv4()`.
- **Numeric refines:** `finite()` (rejects `Infinity`/`-Infinity`/`NaN`), `positive()` (> 0), `nonnegative()` (>= 0), `multipleOf(n)`.
- **Coercion:** `coerceNumber()`, `coerceBoolean()`, `coerceString()` — general siblings of `coerceDate()` for loose inputs (HTML inputs, query strings).
- **Synchronous Standard Schema:** `schema['~standard'].validate(x)` resolves SYNCHRONOUSLY when the schema has no async steps (it returns the `Result` directly instead of a `Promise`). SS v1 allows `Result | Promise<Result>`; this removes the async branch + the `untrack` workaround in consumers (`form-core`, `storage`) for synchronous schemas.
- **String transforms:** `trim()`, `toLowerCase()`, `toUpperCase()` to normalize forms.
- **`nullish()` modifier:** accepts `undefined` and `null` (= `optional` + `nullable`).
- **`tuple()` and `record()` combinators:** a fixed-length heterogeneous tuple + a dictionary of arbitrary keys. They extend the `SchemaKind` vocabulary (`'tuple'`/`'record'`); `Form.AutoFields` degrades them with a "not supported yet" notice.
- **Object utilities:** `pick` / `omit` / `partial` / `extend` / `merge` — derive an `object()` reusing the original's fields, with precise types via `Pick`/`Omit`/`Partial`.
- **Cleaned-up API:** `dateValue()` / `timeValue()` no longer accept `opts` (they were ignored). To validate granularity/calendar, compose a `refine` over the value.
- 8 new issue codes (`uuid`, `slug`, `datetime`, `ipv4`, `finite`, `positive`, `nonnegative`, `multiple_of`) with es/en translation.
## Importaciones
Sium expone una fachada publica y barrels tematicos.
Sium exposes a public facade and thematic barrels.
```ts
import { createEngineSium } from '$sium';
@ -55,7 +54,7 @@ import { createEngineSium } from '$sium';
const sium = createEngineSium();
```
Si quieres traducciones activas, inyecta el engine de traducciones:
If you want active translations, inject the translation engine:
```ts
import { createEngineSium, siumLangs } from '$sium';
@ -71,19 +70,19 @@ import { colorValue, dateValue, timeValue } from '$sium/types';
import { siumLangs, ISSUE_CODES, resolve } from '$sium/langs';
```
`createSchema()` es interno. La API publica debe entrar por `createEngineSium()` o por los barrels `core`, `types` y `langs`.
`createSchema()` is internal. The public API must enter through `createEngineSium()` or through the `core`, `types` and `langs` barrels.
El directorio `_examples/` contiene esquemas de referencia (formularios de
login, signup, settings, etc.) usados por `/test/sium` y citados en este
README. **No es API publica** — el guion bajo inicial es la convencion que
señala material interno/dev. No esta re-exportado por `sium/index.ts` y
ningun consumidor lo importa. Si encuentras un caso real para uno de esos
esquemas, copialo al consumidor; promoverlo a `sium` publico significaria
soporte indefinido para una decision de UX que no pertenece al runtime.
The `_examples/` directory contains reference schemas (login, signup, settings
forms, etc.) used by `/active/docs/sium` and cited in this README. **It is not
public API** — the leading underscore is the convention that signals
internal/dev material. It is not re-exported by `sium/index.ts` and no consumer
imports it. If you find a real use case for one of those schemas, copy it into
the consumer; promoting it to public `sium` would mean indefinite support for a
UX decision that does not belong to the runtime.
## Idea Central
## Core Idea
Un schema Sium es un codec bidireccional con validacion, introspeccion y compatibilidad Standard Schema.
A Sium schema is a bidirectional codec with validation, introspection and Standard Schema compatibility.
```ts
interface Schema<I = unknown, O = I> {
@ -99,54 +98,54 @@ interface Schema<I = unknown, O = I> {
}
```
`decode` valida y transforma. Si falla, lanza `SiumValidationError`.
`decode` validates and transforms. If it fails, it throws `SiumValidationError`.
`validate` envuelve `decode` y devuelve `{ ok: true, value }` o `{ ok: false, issues }`.
`validate` wraps `decode` and returns `{ ok: true, value }` or `{ ok: false, issues }`.
`createEngineSium({ logger }).validate(schema, input)` hace lo mismo y, si falla, emite un diagnóstico `SIUM_DIAGNOSTIC_EVENTS.VALIDATION_FAILED` a nivel `debug` (categoría `SIUM_MODULE`), con `issueCount` e `issues`.
`createEngineSium({ logger }).validate(schema, input)` does the same and, on failure, emits a `SIUM_DIAGNOSTIC_EVENTS.VALIDATION_FAILED` diagnostic at `debug` level (category `SIUM_MODULE`), with `issueCount` and `issues`.
`encode` invierte la salida hacia la forma de entrada. En transforms de una sola direccion actua como identidad para esa parte; usa `codec()` cuando necesites reversibilidad real.
`encode` inverts the output back toward the input shape. In one-way transforms it acts as identity for that part; use `codec()` when you need real reversibility.
`~standard` permite usar schemas Sium en cualquier consumidor de Standard Schema v1. Los schemas sincronos resuelven de forma sincrona (`validate` devuelve el `Result` directo); solo los que tienen pasos async devuelven `Promise`.
`~standard` lets you use Sium schemas in any Standard Schema v1 consumer. Synchronous schemas resolve synchronously (`validate` returns the `Result` directly); only those with async steps return a `Promise`.
`~sium` contiene metadata para UI, debug, docs y herramientas.
`~sium` contains metadata for UI, debug, docs and tooling.
## Por Que Existe
## Why It Exists
Sium cubre cuatro huecos que las librerias externas no resuelven de forma nativa dentro de Active.
Sium covers four gaps that external libraries do not solve natively within Active.
- Tipos de dominio: `ColorValue`, `DateValue`, `TimeValue` y sus segmentos ya existen en Active.
- UI metadata: `meta.widget`, `meta.channel`, labels y opciones viajan dentro del schema.
- Errores traducibles: los issues llevan `code`, `message` idlangref, `params` y `path`.
- Interop: cada schema tambien habla Standard Schema v1.
- Domain types: `ColorValue`, `DateValue`, `TimeValue` and their segments already exist in Active.
- UI metadata: `meta.widget`, `meta.channel`, labels and options travel inside the schema.
- Translatable errors: issues carry `code`, an idlangref `message`, `params` and `path`.
- Interop: every schema also speaks Standard Schema v1.
## Arquitectura
```txt
src/arts/sium/
index.ts fachada publica
index.ts public facade
engine-sium.ts createEngineSium()
engine-resolver.ts, engine-validation.ts
resolucion de mensajes idlangref + runtime de validate/validateSync
idlangref message resolution + validate/validateSync runtime
consts.ts, errors.ts, diagnostics.ts
SIUM_MODULE + errores tipados (CodeError) + catalogo de logs
core/ kernel puro TypeScript (schemas, combinators, pipe)
types/ tipos de dominio sobre $libs/color y $libs/days
langs/ catalogo de mensajes traducibles + fallback de interpolacion
_examples/ schemas de ejemplo (dev-only, fuera del barril publico)
test/ suite Vitest
SIUM_MODULE + typed errors (CodeError) + log catalog
core/ pure TypeScript kernel (schemas, combinators, pipe)
types/ domain types over $libs/color and $libs/days
langs/ catalog of translatable messages + interpolation fallback
_examples/ example schemas (dev-only, outside the public barrel)
test/ Vitest suite
```
Reglas de dependencia:
Dependency rules:
- `core/` no importa Svelte, UI, `days`, `color` ni `langs`.
- `types/` puede importar `core/`, `$libs/color` y `$libs/days`.
- `langs/` define el catalogo `siumLangs` y un fallback minimo de interpolacion.
- `engine-sium.ts` compone la fachada publica.
- `core/` imports no Svelte, UI, `days`, `color` or `langs`.
- `types/` may import `core/`, `$libs/color` and `$libs/days`.
- `langs/` defines the `siumLangs` catalog and a minimal interpolation fallback.
- `engine-sium.ts` composes the public facade.
## Guia Rapida
## Quick Guide
### Usar La Fachada
### Using the Facade
```ts
import { createEngineSium } from '$sium';
@ -166,7 +165,7 @@ const result = await User.validate({
});
```
Con integracion:
With integration:
```ts
const s = createEngineSium({ langs, logger });
@ -179,18 +178,18 @@ s.resolveIssue({
});
```
Si `langs` existe, `resolveIssue()` delega en `EngineLangs`. Si no existe, usa el fallback local de `$sium/langs.resolve`.
If `langs` exists, `resolveIssue()` delegates to `EngineLangs`. If not, it uses the local `$sium/langs.resolve` fallback.
En desarrollo tambien puedes validar a traves del engine para obtener logs `debug` cuando hay issues:
In development you can also validate through the engine to get `debug` logs when there are issues:
```ts
const result = await s.validate(User, input);
const syncResult = s.validateSync(User, input);
```
Estos wrappers no cambian el resultado de `schema.validate()`. Solo emiten un log `debug` en la categoria exportada `LOGGER_CATEGORY` cuando la validacion falla y has inyectado `logger`.
These wrappers do not change the result of `schema.validate()`. They only emit a `debug` log in the exported `LOGGER_CATEGORY` category when validation fails and you have injected `logger`.
### Usar Imports Granulares
### Using Granular Imports
```ts
import { object, optional, pipe, string, number, integer, min, email } from '$sium/core';
@ -202,7 +201,7 @@ export const User = object({
});
```
### Validar
### Validating
```ts
const result = await User.validate(input);
@ -214,7 +213,7 @@ if (result.ok) {
}
```
### Decodificar Con Excepcion
### Decoding With an Exception
```ts
import { SiumValidationError } from '$sium/core';
@ -228,7 +227,7 @@ try {
}
```
### Codificar
### Encoding
```ts
const QueryNumber = s.pipe(
@ -243,9 +242,9 @@ await QueryNumber.decode('42'); // 42
QueryNumber.encode(42); // '42'
```
## API De Core
## Core API
### Primitivos
### Primitives
```ts
string();
@ -255,7 +254,7 @@ literal('admin');
enumOf(['admin', 'user', 'guest'] as const);
```
### Modificadores
### Modifiers
```ts
optional(string());
@ -264,20 +263,22 @@ nullish(string());
defaulted(number(), () => 18);
```
`optional` permite `undefined`.
`optional` allows `undefined`.
`nullable` permite `null`.
`nullable` allows `null`.
`nullish` permite `undefined` y `null` (combina `optional` + `nullable`).
`nullish` allows `undefined` and `null` (combines `optional` + `nullable`).
`defaulted` aplica el valor por defecto en decode cuando el input es `undefined`.
`defaulted` applies the default value on decode when the input is `undefined`.
Si el schema interno es async, los wrappers conservan el short-circuit sincronico del sentinel:
`optional(asyncSchema).decodeSync(undefined)`, `nullable(asyncSchema).decodeSync(null)` y
`defaulted(asyncSchema).decodeSync(undefined)` no entran al schema interno. Cualquier valor que
si necesite validar el inner async lanza `SiumAsyncSchemaError` en APIs sync.
If the inner schema is async, the wrappers keep the sentinel's synchronous
short-circuit: `optional(asyncSchema).decodeSync(undefined)`,
`nullable(asyncSchema).decodeSync(null)` and
`defaulted(asyncSchema).decodeSync(undefined)` do not enter the inner schema. Any
value that does need to validate the inner async throws `SiumAsyncSchemaError` in
sync APIs.
### Combinadores
### Combinators
```ts
object({ name: string() });
@ -292,43 +293,43 @@ discriminated('kind', [
]);
```
`object()` usa `unknownKeys: 'strip'` por defecto.
`object()` uses `unknownKeys: 'strip'` by default.
```ts
object({ name: string() }, { unknownKeys: 'strict' });
object({ name: string() }, { unknownKeys: 'passthrough' });
```
`tuple(...)` valida posicionalmente y exige la longitud exacta (issue `tuple_length`). `record(schema)` valida un objeto plano de claves arbitrarias cuyos valores comparten `schema`.
`tuple(...)` validates positionally and requires the exact length (issue `tuple_length`). `record(schema)` validates a flat object of arbitrary keys whose values share `schema`.
### Utilidades de object
### Object utilities
Derivan un nuevo `object()` a partir de otro, reusando sus campos:
They derive a new `object()` from another, reusing its fields:
```ts
pick(User, ['id', 'name']); // solo esos campos
omit(User, ['password']); // todos menos esos
partial(User); // todos los campos opcionales
extend(User, { role: string() }); // anade / sobrescribe campos
merge(Base, Overrides); // combina dos object (gana el segundo)
pick(User, ['id', 'name']); // only those fields
omit(User, ['password']); // all but those
partial(User); // all fields optional
extend(User, { role: string() }); // adds / overrides fields
merge(Base, Overrides); // combines two objects (the second wins)
```
Los tipos resultantes se derivan con `Pick` / `Omit` / `Partial` sobre el schema fuente, asi que siguen siendo precisos. Pasar un schema que no sea `object()` lanza `SiumObjectUtilError`.
The resulting types are derived with `Pick` / `Omit` / `Partial` over the source schema, so they stay precise. Passing a schema that is not an `object()` throws `SiumObjectUtilError`.
### Pipe Y Steps
### Pipe and Steps
```ts
pipe(string(), min(3), max(40), meta({ label: 'Name', widget: 'text-field' }));
```
Steps disponibles:
Available steps:
- `refine(check, fail, opts?)`
- `transform(fn, opts?)`
- `codec(decode, encode, opts?)`
- `meta(annotations)`
Si una funcion normal devuelve `Promise`, declara el step como async.
If a normal function returns a `Promise`, declare the step as async.
```ts
const UniqueEmail = pipe(
@ -344,7 +345,7 @@ const UniqueEmail = pipe(
);
```
### Refines Incluidos
### Included Refines
```ts
min(2);
@ -355,53 +356,53 @@ email();
url();
integer();
// formato (string)
// format (string)
uuid();
slug();
datetime(); // ISO 8601
ipv4();
cssLength(); // px/rem/em/%/vw/vh/vmin/vmax/ch no negativo (code 'custom'; regex CSS_LENGTH_REGEX)
cssValue(); // valor CSS más rico — hermano de cssLength, code 'css_value'
cssLength(); // non-negative px/rem/em/%/vw/vh/vmin/vmax/ch (code 'custom'; regex CSS_LENGTH_REGEX)
cssValue(); // richer CSS value — sibling of cssLength, code 'css_value'
// numericos
finite(); // rechaza Infinity / -Infinity / NaN
// numeric
finite(); // rejects Infinity / -Infinity / NaN
positive(); // > 0
nonnegative(); // >= 0
multipleOf(0.5);
```
`min` y `max` funcionan con numeros, strings y arrays. Emiten codigos distintos para valor numerico y longitud.
`min` and `max` work with numbers, strings and arrays. They emit distinct codes for numeric value and length.
`finite()` cubre el hueco de `number()`, que acepta `Infinity` por diseno. `positive()` no implica `finite()`: compon ambos para rechazar tambien `Infinity`.
`finite()` covers the gap in `number()`, which accepts `Infinity` by design. `positive()` does not imply `finite()`: compose both to also reject `Infinity`.
### Coercion
Para entradas sueltas (inputs HTML, query strings, FormData) que llegan como string y necesitan convertirse a un primitivo:
For loose inputs (HTML inputs, query strings, FormData) that arrive as a string and need converting to a primitive:
```ts
coerceNumber(); // string | number -> number (rechaza '' y NaN; Infinity pasa, compon con finite())
coerceNumber(); // string | number -> number (rejects '' and NaN; Infinity passes, compose with finite())
coerceBoolean(); // string | number | boolean -> boolean ('true/1/yes/on' vs 'false/0/no/off')
coerceString(); // string | number | boolean -> string
```
Son los hermanos generales de `coerceDate()`. Cada uno reporta el `kind` de SALIDA (`coerceNumber()` -> `number`), asi que `Form.AutoFields` renderiza el widget correcto.
They are the general siblings of `coerceDate()`. Each reports the OUTPUT `kind` (`coerceNumber()` -> `number`), so `Form.AutoFields` renders the right widget.
### Transforms De String
### String Transforms
Normalizadores que cambian el valor en decode (son `transform`, sin `encode`). Aplica despues de `string()`:
Normalizers that change the value on decode (they are `transform`, with no `encode`). Apply them after `string()`:
```ts
pipe(string(), trim()); // recorta espacios
pipe(string(), toLowerCase()); // minusculas
pipe(string(), toUpperCase()); // mayusculas
pipe(string(), trim(), toLowerCase()); // email normalizado
pipe(string(), trim()); // trims spaces
pipe(string(), toLowerCase()); // lowercase
pipe(string(), toUpperCase()); // uppercase
pipe(string(), trim(), toLowerCase()); // normalized email
```
El `kind` sigue siendo `string`, asi que son transparentes para `Form.AutoFields`.
The `kind` stays `string`, so they are transparent to `Form.AutoFields`.
## Tipos De Dominio
## Domain Types
Los tipos de dominio viven en `$sium/types` y envuelven las librerias canonicas `$libs/color` y `$libs/days`.
Domain types live in `$sium/types` and wrap the canonical `$libs/color` and `$libs/days` libraries.
### Color
@ -422,15 +423,15 @@ const Color = colorValue();
const Red = red();
```
`colorValue()` valida un `ColorValue` normalizado con `hex`, `rgb`, `hsl` y `hsv`.
`colorValue()` validates a normalized `ColorValue` with `hex`, `rgb`, `hsl` and `hsv`.
Los segmentos RGB son enteros `0..255`.
RGB segments are integers `0..255`.
Los segmentos HSL/HSV son numeros en sus rangos naturales.
HSL/HSV segments are numbers in their natural ranges.
`alpha()` valida `0..1`.
`alpha()` validates `0..1`.
### Fecha
### Date
```ts
import { dateValue, dateRange, coerceDate, year, month, day } from '$sium/types';
@ -440,13 +441,13 @@ const Range = dateRange();
const FromNativeDate = coerceDate();
```
`dateValue()` acepta `DateValue` de `$libs/days`.
`dateValue()` accepts `DateValue` from `$libs/days`.
`coerceDate()` acepta `Date | DateValue` y devuelve `DateValue`.
`coerceDate()` accepts `Date | DateValue` and returns `DateValue`.
`dateRange()` valida `{ start, end }` y rechaza rangos donde `start > end`.
`dateRange()` validates `{ start, end }` and rejects ranges where `start > end`.
### Hora
### Time
```ts
import { timeValue, timeRange, hour, minute, second, dayPeriod } from '$sium/types';
@ -455,15 +456,15 @@ const TimeOnly = timeValue();
const Range = timeRange();
```
`timeRange()` valida `{ start, end }` y rechaza rangos donde `start > end`.
`timeRange()` validates `{ start, end }` and rejects ranges where `start > end`.
`hour({ cycle: 12 })` usa rango `1..12`.
`hour({ cycle: 12 })` uses the range `1..12`.
`hour()` usa rango `0..23`.
`hour()` uses the range `0..23`.
## Introspeccion
## Introspection
Cada schema expone `schema['~sium']`.
Every schema exposes `schema['~sium']`.
```ts
const schema = pipe(string(), email(), meta({ label: 'Email', widget: 'email-field' }));
@ -471,7 +472,7 @@ const schema = pipe(string(), email(), meta({ label: 'Email', widget: 'email-fie
schema['~sium'];
```
Forma simplificada:
Simplified shape:
```ts
{
@ -484,7 +485,7 @@ Forma simplificada:
}
```
Herramientas incluidas:
Included tools:
```ts
serializeSchema(schema);
@ -492,11 +493,11 @@ countLeafFields(schema);
walkSchema(schema, (node, path) => {});
```
`walkSchema()` recorre objects, arrays y wrappers. Las variantes de union/discriminated no se expanden automaticamente para evitar UI ambigua.
`walkSchema()` traverses objects, arrays and wrappers. Union/discriminated variants are not expanded automatically, to avoid ambiguous UI.
## Errores E i18n
## Errors and i18n
Un issue tiene esta forma:
An issue has this shape:
```ts
type Issue = {
@ -507,13 +508,13 @@ type Issue = {
};
```
`message` usa idlangref:
`message` uses an idlangref:
```txt
#?sium.errors.type|Expected {{expected}} but received {{actual}}
```
Los mensajes base viven en `siumLangs`.
The base messages live in `siumLangs`.
```ts
import { siumLangs, resolve } from '$sium/langs';
@ -524,23 +525,23 @@ resolve('#?sium.errors.type|Invalid', {
});
```
`resolve()` de `$sium/langs` no traduce por locale. Es solo el fallback local de interpolacion cuando no hay `EngineLangs`.
`resolve()` from `$sium/langs` does not translate by locale. It is only the local interpolation fallback when there is no `EngineLangs`.
Para traduccion real:
For real translation:
```ts
const sium = createEngineSium({ langs });
const text = sium.resolveIssue(issue, 'es');
```
El engine expone `langSchema` como nombre historico para acceder al catalogo
estatico si lo necesitas:
The engine exposes `langSchema` as a historical name to access the static catalog
if you need it:
```ts
sium.langSchema === siumLangs;
```
Codigos estables actuales:
Current stable codes:
```ts
type
@ -563,9 +564,9 @@ discriminated_missing_key
discriminated_unknown_value
```
## Contexto De Validacion
## Validation Context
Los callbacks de `refine`, `transform` y `codec` reciben `ctx`.
The `refine`, `transform` and `codec` callbacks receive `ctx`.
```ts
type Ctx = {
@ -577,18 +578,19 @@ type Ctx = {
};
```
`ctx.path` es absoluto. En `object({ user: object({ name }) })`, un refine de `name` recibe `['user', 'name']`.
`ctx.path` is absolute. In `object({ user: object({ name }) })`, a refine on `name` receives `['user', 'name']`.
`ctx.rootInput` es el input original.
`ctx.rootInput` is the original input.
`ctx.rootValue` es passthrough opcional. Sium lo conserva si el caller o un adapter lo
pasan en el contexto, pero el core no lo sintetiza automaticamente.
`ctx.rootValue` is an optional passthrough. Sium keeps it if the caller or an
adapter passes it in the context, but the core does not synthesize it
automatically.
Para validaciones cross-field sobre input de usuario, usa `ctx.rootInput`. Para validaciones
cross-field sobre datos ya decodificados, coloca un `refine()` sobre el schema `object()` y usa
el objeto decodificado que recibe ese refine.
For cross-field validations over user input, use `ctx.rootInput`. For cross-field
validations over already-decoded data, place a `refine()` over the `object()`
schema and use the decoded object that refine receives.
## Ejemplos
## Examples
### Login
@ -601,7 +603,7 @@ export const Login = object({
});
```
### Signup Con Confirmacion
### Signup With Confirmation
```ts
import { boolean, object, optional, pipe, refine, string, meta } from '$sium/core';
@ -628,7 +630,7 @@ export const Signup = pipe(
);
```
### Perfil Con Fecha Y Color
### Profile With Date and Color
```ts
import { object, pipe, string, email, min, max, meta } from '$sium/core';
@ -642,7 +644,7 @@ export const Profile = object({
});
```
### Reserva Con Rango
### Booking With a Range
```ts
import { integer, max, meta, min, number, object, optional, pipe, string } from '$sium/core';
@ -673,7 +675,7 @@ export const Event = discriminated('kind', [
]);
```
### Recursion Con Lazy
### Recursion With Lazy
```ts
import { array, lazy, object, optional, string } from '$sium/core';
@ -692,13 +694,14 @@ export const Tree: Schema<TreeNode> = lazy(() =>
);
```
`lazy()` protege errores de construccion del schema: si el thunk intenta usar el propio proxy
mientras resuelve, o devuelve ese mismo proxy como schema final, lanza `SiumLazyResolvingError`.
Esto no es deteccion de ciclos en datos de usuario. Si pasas un objeto runtime autociclico a un
schema recursivo, el decode/encode puede seguir desbordando la pila; valida esos datos antes o
anade una capa de normalizacion especifica del dominio.
`lazy()` guards schema-construction errors: if the thunk tries to use the proxy
itself while resolving, or returns that same proxy as the final schema, it throws
`SiumLazyResolvingError`. This is not cycle detection in user data. If you pass a
self-cyclic runtime object to a recursive schema, decode/encode can still overflow
the stack; validate that data beforehand or add a domain-specific normalization
layer.
### Transform Y Codec
### Transform and Codec
```ts
import { codec, pipe, string, transform } from '$sium/core';
@ -717,7 +720,7 @@ export const NumberFromString = pipe(
);
```
### Validacion Async
### Async Validation
```ts
import { pipe, refine, string } from '$sium/core';
@ -735,17 +738,17 @@ export const UniqueUsername = pipe(
);
```
## Integracion Svelte
## Svelte Integration
Sium no tiene adapter Svelte propio: la integracion es agnostica al validador y vive en la capa de formularios, no en sium.
Sium has no Svelte adapter of its own: the integration is validator-agnostic and lives in the forms layer, not in sium.
- El core es TypeScript puro y se usa sin Svelte.
- Los schemas exponen `~standard` (Standard Schema v1), asi que `createForm` / `Form.Provider` los validan reactivamente igual que a Zod/Valibot/ArkType. Los schemas sincronos resuelven de forma sincrona.
- `Form.AutoFields` auto-renderiza los campos leyendo la introspeccion `~sium` (`kind`, `wrappers`, `meta.widget`, …).
- Los issues idlangref (`#?sium.errors.*`) los traduce el servicio `langs` compartido, reactivo al locale activo.
- langs/logger se inyectan en sium por `createEngineSium({ langs, logger })`. sium es page-scoped: lo declara la app, no el runtime UIX.
- The core is pure TypeScript and is used without Svelte.
- Schemas expose `~standard` (Standard Schema v1), so `createForm` / `Form.Provider` validate them reactively just like Zod/Valibot/ArkType. Synchronous schemas resolve synchronously.
- `Form.AutoFields` auto-renders the fields by reading the `~sium` introspection (`kind`, `wrappers`, `meta.widget`, …).
- The idlangref issues (`#?sium.errors.*`) are translated by the shared `langs` service, reactive to the active locale.
- langs/logger are injected into sium via `createEngineSium({ langs, logger })`. sium is page-scoped: the app declares it, not the UIX runtime.
## Recetas Rapidas
## Quick Recipes
### UUID v4
@ -762,7 +765,7 @@ const UUID = pipe(
const Slug = pipe(string(), min(1), max(80), regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/));
```
### Password Fuerte
### Strong Password
```ts
const StrongPassword = pipe(
@ -809,7 +812,7 @@ const ImageFile = refine<File>((file) => file.size <= 2_000_000 && file.type.sta
### `SiumValidationError`
Lanzado por `decode` cuando hay errores de validacion.
Thrown by `decode` when there are validation errors.
```ts
if (error instanceof SiumValidationError) {
@ -819,28 +822,28 @@ if (error instanceof SiumValidationError) {
### `SiumAsyncSchemaError`
Lanzado por `decodeSync` o `validateSync` cuando la ruta de validacion necesita steps async.
Los wrappers de sentinel pueden devolver sincronamente si no consultan el inner async
(`optional(undefined)`, `nullable(null)`, `defaulted(undefined)`).
Los combinadores siguen la misma regla por ruta: por ejemplo, `object({ a:
optional(asyncSchema) }).decodeSync({ a: undefined })` puede completarse; `{ a: 'x' }`
lanza porque ya necesita validar el inner async.
Thrown by `decodeSync` or `validateSync` when the validation path needs async
steps. The sentinel wrappers can return synchronously if they do not consult the
inner async (`optional(undefined)`, `nullable(null)`, `defaulted(undefined)`). The
combinators follow the same per-path rule: for example, `object({ a:
optional(asyncSchema) }).decodeSync({ a: undefined })` can complete; `{ a: 'x' }`
throws because it already needs to validate the inner async.
Solucion: usa `await schema.decode(...)` o `await schema.validate(...)`.
Fix: use `await schema.decode(...)` or `await schema.validate(...)`.
### Un callback devuelve Promise pero `~sium.async` es false
### A callback returns a Promise but `~sium.async` is false
JavaScript no permite detectar una funcion normal que devuelve `Promise` sin ejecutarla. Declara el step con `{ async: true }`.
JavaScript does not allow detecting a normal function that returns a `Promise` without executing it. Declare the step with `{ async: true }`.
```ts
refine((value) => Promise.resolve(Boolean(value)), { code: 'custom' }, { async: true });
```
### El path de un error no coincide
### An error's path does not match
Los errores emitidos dentro de schemas hijos deben usar `ctx.path`. Sium pasa paths absolutos a hijos de object y array.
Errors emitted inside child schemas must use `ctx.path`. Sium passes absolute paths to object and array children.
## Verificacion Recomendada
## Recommended Verification
```bash
npx vitest run src/arts/sium/test/barrel.test.ts src/arts/sium/test/engine-sium.test.ts src/arts/sium/test/combinators.test.ts src/arts/sium/test/date.test.ts src/arts/sium/test/time.test.ts src/arts/sium/test/color.test.ts src/arts/sium/test/pipe.test.ts src/arts/sium/test/primitives.test.ts src/arts/sium/test/refines.test.ts src/arts/sium/test/schema.test.ts src/arts/sium/test/standard-schema.test.ts src/arts/sium/test/modifiers.test.ts src/arts/sium/test/issue.test.ts src/arts/sium/test/lazy.test.ts src/arts/sium/test/introspect.test.ts src/arts/sium/test/langs.test.ts src/arts/sium/test/resolver.test.ts src/arts/sium/test/coerce.test.ts src/arts/sium/test/refines-2.test.ts src/arts/sium/test/transforms.test.ts src/arts/sium/test/tuple.test.ts src/arts/sium/test/record.test.ts src/arts/sium/test/object-utils.test.ts
@ -850,13 +853,13 @@ npx vitest run src/arts/sium/test/barrel.test.ts src/arts/sium/test/engine-sium.
npx vitest run src/libs/days/test/parse.test.ts src/libs/days/test/queries.test.ts src/libs/color/test/guards.test.ts
```
## Criterio Para Cambios Futuros
## Criteria for Future Changes
Antes de ampliar Sium, comprueba que el cambio cumple estas reglas.
Before extending Sium, check that the change meets these rules.
- El core sigue siendo TypeScript puro.
- La API publica no expone `createSchema()`.
- Cada schema mantiene `decode`, `encode`, `validate`, `~standard` y `~sium`.
- Los errores son datos estructurados, no strings finales.
- Los tipos de dominio reutilizan `$libs/color` y `$libs/days`.
- La documentacion se actualiza en este unico README.
- The core stays pure TypeScript.
- The public API does not expose `createSchema()`.
- Every schema keeps `decode`, `encode`, `validate`, `~standard` and `~sium`.
- Errors are structured data, not final strings.
- Domain types reuse `$libs/color` and `$libs/days`.
- Documentation is updated in this single README.

Loading…
Cancel
Save

Powered by TurnKey Linux.