Guard Sium lazy self resolution

active-uix
dev 5 months ago
parent c4eafd02f2
commit ac5fdca4e7

@ -129,8 +129,14 @@ Actualizacion 2026-05-16:
guard + widget + range order sin mover esa logica a `core`. guard + widget + range order sin mover esa logica a `core`.
2. Unificar paths sync/async de combinadores solo con tests amplios; es zona 2. Unificar paths sync/async de combinadores solo con tests amplios; es zona
sensible por `SiumAsyncSchemaError`. sensible por `SiumAsyncSchemaError`.
3. Documentar limites de `lazy()` con inputs ciclicos o anadir proteccion 3. Cerrado: `lazy()` documenta limites de datos runtime autociclicos y lanza
`WeakSet` si empieza a recibir datos no confiables. `SiumLazyResolvingError` cuando el thunk usa el proxy mientras resuelve o
devuelve el mismo proxy como schema final. No se anade `WeakSet` global
porque la deteccion de ciclos de datos debe ser una capa de normalizacion
del dominio si entran datos no confiables.
4. Validado tras `lazy()`: `npx vitest run src/arts/sium/test` -> 18
archivos, 370 tests OK; `npm run check` -> 0 errores, 0 warnings;
`npm run test` -> 233 archivos, 2266 tests OK.
- Validacion Sium domain factory: - Validacion Sium domain factory:
- `npx vitest run src/arts/sium/test/color.test.ts src/arts/sium/test/date.test.ts src/arts/sium/test/time.test.ts src/arts/sium/test/standard-schema.test.ts` -> 4 archivos, 122 tests OK. - `npx vitest run src/arts/sium/test/color.test.ts src/arts/sium/test/date.test.ts src/arts/sium/test/time.test.ts src/arts/sium/test/standard-schema.test.ts` -> 4 archivos, 122 tests OK.
- `npm run check` -> 0 errores, 0 warnings. - `npm run check` -> 0 errores, 0 warnings.

@ -613,6 +613,12 @@ 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.
### Transform Y Codec ### Transform Y Codec
```ts ```ts

@ -20,6 +20,7 @@ function createResolvingAccessError(): SiumLazyResolvingError {
export function lazy<I, O>(getSchema: () => Schema<I, O>): Schema<I, O> { export function lazy<I, O>(getSchema: () => Schema<I, O>): Schema<I, O> {
let cached: Schema<I, O> | undefined; let cached: Schema<I, O> | undefined;
let resolving = false; let resolving = false;
let proxy: Schema<I, O>;
const unresolvedStandard = { const unresolvedStandard = {
version: 1, version: 1,
@ -54,14 +55,19 @@ export function lazy<I, O>(getSchema: () => Schema<I, O>): Schema<I, O> {
resolving = true; resolving = true;
try { try {
cached = getSchema(); const resolved = getSchema();
if (resolved === proxy) {
throw createResolvingAccessError();
}
cached = resolved;
return cached; return cached;
} finally { } finally {
resolving = false; resolving = false;
} }
}; };
const schema: Schema<I, O> = { proxy = {
get '~standard'() { get '~standard'() {
if (cached !== undefined) { if (cached !== undefined) {
return cached['~standard']; return cached['~standard'];
@ -91,5 +97,5 @@ export function lazy<I, O>(getSchema: () => Schema<I, O>): Schema<I, O> {
validateSync: (input, ctx) => getResolvedSchema().validateSync(input, ctx) validateSync: (input, ctx) => getResolvedSchema().validateSync(input, ctx)
}; };
return schema; return proxy;
} }

@ -2,7 +2,7 @@ import { describe, expect, it } from 'vitest';
import { array, lazy, object, optional, string } from '../core'; import { array, lazy, object, optional, string } from '../core';
import { createSchema } from '../core/schema'; import { createSchema } from '../core/schema';
import type { Schema } from '../core/types'; import type { Schema } from '../core/types';
import { SiumAsyncSchemaError } from '../errors'; import { SiumAsyncSchemaError, SiumLazyResolvingError } from '../errors';
type TreeNode = { type TreeNode = {
value: string; value: string;
@ -219,8 +219,22 @@ describe('lazy()', () => {
expect(() => schema.decodeSync('x')).toThrow(SiumAsyncSchemaError); expect(() => schema.decodeSync('x')).toThrow(SiumAsyncSchemaError);
}); });
it('throws SiumLazyResolvingError when the thunk uses the proxy while resolving', () => {
const schema: Schema<string, string> = lazy(() => {
schema.decodeSync('x');
return string();
});
expect(() => schema.decodeSync('x')).toThrow(SiumLazyResolvingError);
});
it('throws SiumLazyResolvingError when the thunk returns the same lazy proxy', () => {
const schema: Schema<string, string> = lazy(() => schema);
expect(() => schema.decodeSync('x')).toThrow(SiumLazyResolvingError);
});
it('is re-exported by the core barrel', () => { it('is re-exported by the core barrel', () => {
expect(lazy).toBeTypeOf('function'); expect(lazy).toBeTypeOf('function');
}); });
}); });

Loading…
Cancel
Save

Powered by TurnKey Linux.