Split sium step factories

master
dev 5 months ago
parent 4fa2e7e600
commit 164e6fb9db

@ -31,6 +31,7 @@ Estado al cierre:
- `arts/conn/connection.ts` delega la programacion de reconnect y exhaustion en `connection-reconnect-runtime.ts`.
- `libs/perm/evaluator.ts` delega helpers puros de resultado, dependencias y comparacion en `evaluator-helpers.ts`.
- `arts/http/engine-http.ts` delega la validacion preflight de `bodySchema` en `request-validation.ts`.
- `arts/sium/core/pipe.ts` queda centrado en composicion; factories `refine/transform/codec/meta` viven en `steps.ts`.
- `arts/conn/connection.ts` delega decode/routing de frames entrantes en `connection-message-router.ts`.
- `arts/conn/connection.ts` delega el intento open/auth/flush/join en `connection-connect.ts`.
- `arts/conn/connection.ts` delega current transport, attach/detach y close en `connection-transport-runtime.ts`.

@ -1,11 +1,7 @@
import { issue } from './issue';
import { createSchema } from './schema';
import {
SiumAsyncSchemaError,
SiumValidationError,
type Ctx,
type MetaAnnotations,
type RefineFailure,
type Schema,
type SchemaEffect,
type Step
@ -13,35 +9,6 @@ import {
type UnknownSchema = Schema<unknown, unknown>;
type UnknownStep = Step<unknown, unknown>;
type StepOptions = {
/**
* Mark callbacks as async when they are regular functions that return a
* Promise. JavaScript cannot detect that shape before executing the callback.
*/
async?: boolean;
};
import { isPromiseLike } from './internals';
function isAsyncFunction(fn: Function): boolean {
return fn.constructor.name === 'AsyncFunction';
}
function createAsyncStepError() {
return new SiumAsyncSchemaError(
undefined,
'Step contains async logic — use decode() instead of decodeSync().'
);
}
function createRefineIssue(path: ReadonlyArray<string | number>, fail: RefineFailure) {
return issue({
path,
code: fail.code ?? 'custom',
message: fail.message ?? '#?sium.errors.custom|Invalid value',
...(fail.params === undefined ? {} : { params: fail.params })
});
}
function mergeMeta(
baseMeta: Readonly<MetaAnnotations>,
@ -191,146 +158,4 @@ export function pipe<I, O>(
});
}
/**
* Creates a refinement step that validates the current value without changing it.
*
* Failed checks emit a single `Issue` built from `fail`, defaulting to
* `code='custom'` and `message='#?sium.errors.custom|Invalid value'`.
*/
export function refine<T>(
check: (value: T, ctx: Ctx) => boolean | Promise<boolean>,
fail: RefineFailure,
opts?: StepOptions
): Step<T, T> {
const async = opts?.async ?? isAsyncFunction(check);
return {
kind: 'refine',
async,
meta: {},
decode: async (input, ctx) => {
try {
const result = await check(input, ctx);
if (result) {
return input;
}
throw new SiumValidationError([createRefineIssue(ctx.path, fail)]);
} catch (error) {
if (error instanceof SiumValidationError) {
throw error;
}
throw error;
}
},
decodeSync: (input, ctx) => {
if (async) {
throw createAsyncStepError();
}
try {
const result = check(input, ctx);
if (isPromiseLike(result)) {
throw createAsyncStepError();
}
if (result) {
return input;
}
throw new SiumValidationError([createRefineIssue(ctx.path, fail)]);
} catch (error) {
if (error instanceof SiumValidationError) {
throw error;
}
throw error;
}
}
};
}
/**
* Creates a one-way transformation step.
*
* `transform` affects decode only. It intentionally does not expose `encode`,
* so `pipe(...).encode(...)` treats it as identity in reverse composition.
*/
export function transform<I, O>(
fn: (value: I, ctx: Ctx) => O | Promise<O>,
opts?: StepOptions
): Step<I, O> {
const async = opts?.async ?? isAsyncFunction(fn);
return {
kind: 'transform',
async,
meta: {},
decode: async (input, ctx) => await fn(input, ctx),
decodeSync: (input, ctx) => {
if (async) {
throw createAsyncStepError();
}
const result = fn(input, ctx);
if (isPromiseLike(result)) {
throw createAsyncStepError();
}
return result;
}
};
}
/**
* Creates a bidirectional step with explicit decode and encode sides.
*
* Unlike `transform`, `codec` participates in reverse composition during
* `pipe(...).encode(...)` by applying the provided `encode` function.
*/
export function codec<I, O>(
decode: (value: I, ctx: Ctx) => O | Promise<O>,
encode: (value: O) => I,
opts?: StepOptions
): Step<I, O> {
const async = opts?.async ?? isAsyncFunction(decode);
return {
kind: 'codec',
async,
meta: {},
decode: async (input, ctx) => await decode(input, ctx),
decodeSync: (input, ctx) => {
if (async) {
throw createAsyncStepError();
}
const result = decode(input, ctx);
if (isPromiseLike(result)) {
throw createAsyncStepError();
}
return result;
},
encode
};
}
/**
* Creates a pure annotation step that leaves the value untouched.
*
* `meta` never changes runtime data and never participates in encode; it only
* contributes shallow-copied annotations to the final schema introspection.
*/
export function meta<T>(annotations: MetaAnnotations): Step<T, T> {
const copiedMeta = { ...annotations };
return {
kind: 'meta',
async: false,
meta: copiedMeta,
decode: async (input) => input,
decodeSync: (input) => input
};
}
export { refine, transform, codec, meta } from './steps';

@ -0,0 +1,182 @@
import { issue } from './issue';
import {
SiumAsyncSchemaError,
SiumValidationError,
type Ctx,
type MetaAnnotations,
type RefineFailure,
type Step
} from './types';
import { isPromiseLike } from './internals';
type StepOptions = {
/**
* Mark callbacks as async when they are regular functions that return a
* Promise. JavaScript cannot detect that shape before executing the callback.
*/
async?: boolean;
};
function isAsyncFunction(fn: Function): boolean {
return fn.constructor.name === 'AsyncFunction';
}
function createAsyncStepError() {
return new SiumAsyncSchemaError(
undefined,
'Step contains async logic — use decode() instead of decodeSync().'
);
}
function createRefineIssue(path: ReadonlyArray<string | number>, fail: RefineFailure) {
return issue({
path,
code: fail.code ?? 'custom',
message: fail.message ?? '#?sium.errors.custom|Invalid value',
...(fail.params === undefined ? {} : { params: fail.params })
});
}
/**
* Creates a refinement step that validates the current value without changing it.
*
* Failed checks emit a single `Issue` built from `fail`, defaulting to
* `code='custom'` and `message='#?sium.errors.custom|Invalid value'`.
*/
export function refine<T>(
check: (value: T, ctx: Ctx) => boolean | Promise<boolean>,
fail: RefineFailure,
opts?: StepOptions
): Step<T, T> {
const async = opts?.async ?? isAsyncFunction(check);
return {
kind: 'refine',
async,
meta: {},
decode: async (input, ctx) => {
try {
const result = await check(input, ctx);
if (result) {
return input;
}
throw new SiumValidationError([createRefineIssue(ctx.path, fail)]);
} catch (error) {
if (error instanceof SiumValidationError) {
throw error;
}
throw error;
}
},
decodeSync: (input, ctx) => {
if (async) {
throw createAsyncStepError();
}
try {
const result = check(input, ctx);
if (isPromiseLike(result)) {
throw createAsyncStepError();
}
if (result) {
return input;
}
throw new SiumValidationError([createRefineIssue(ctx.path, fail)]);
} catch (error) {
if (error instanceof SiumValidationError) {
throw error;
}
throw error;
}
}
};
}
/**
* Creates a one-way transformation step.
*
* `transform` affects decode only. It intentionally does not expose `encode`,
* so `pipe(...).encode(...)` treats it as identity in reverse composition.
*/
export function transform<I, O>(
fn: (value: I, ctx: Ctx) => O | Promise<O>,
opts?: StepOptions
): Step<I, O> {
const async = opts?.async ?? isAsyncFunction(fn);
return {
kind: 'transform',
async,
meta: {},
decode: async (input, ctx) => await fn(input, ctx),
decodeSync: (input, ctx) => {
if (async) {
throw createAsyncStepError();
}
const result = fn(input, ctx);
if (isPromiseLike(result)) {
throw createAsyncStepError();
}
return result;
}
};
}
/**
* Creates a bidirectional step with explicit decode and encode sides.
*
* Unlike `transform`, `codec` participates in reverse composition during
* `pipe(...).encode(...)` by applying the provided `encode` function.
*/
export function codec<I, O>(
decode: (value: I, ctx: Ctx) => O | Promise<O>,
encode: (value: O) => I,
opts?: StepOptions
): Step<I, O> {
const async = opts?.async ?? isAsyncFunction(decode);
return {
kind: 'codec',
async,
meta: {},
decode: async (input, ctx) => await decode(input, ctx),
decodeSync: (input, ctx) => {
if (async) {
throw createAsyncStepError();
}
const result = decode(input, ctx);
if (isPromiseLike(result)) {
throw createAsyncStepError();
}
return result;
},
encode
};
}
/**
* Creates a pure annotation step that leaves the value untouched.
*
* `meta` never changes runtime data and never participates in encode; it only
* contributes shallow-copied annotations to the final schema introspection.
*/
export function meta<T>(annotations: MetaAnnotations): Step<T, T> {
const copiedMeta = { ...annotations };
return {
kind: 'meta',
async: false,
meta: copiedMeta,
decode: async (input) => input,
decodeSync: (input) => input
};
}
Loading…
Cancel
Save

Powered by TurnKey Linux.