validator is readygit add .
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@ -1,11 +1,23 @@
|
||||
import { Result } from "@shared/utils/result.ts";
|
||||
import { Schema } from "@shared/utils/validator.ts";
|
||||
|
||||
class ClientApi<Req, Res> {
|
||||
constructor(path: string, method: string) {}
|
||||
validate(res: Response): ResultAsync<Res> {
|
||||
const body = await res.json();
|
||||
class ApiRoute<
|
||||
Path extends string,
|
||||
ReqSchema extends Schema<any>,
|
||||
ResSchema extends Schema<any>,
|
||||
> {
|
||||
constructor(
|
||||
public readonly path: Path,
|
||||
public readonly reqSchema: ReqSchema,
|
||||
public readonly resSchema: ResSchema,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
class ServerApi<Req, Res> {
|
||||
}
|
||||
export type ExtractRouteParams<T extends string> = T extends string
|
||||
? T extends `${infer _Start}:${infer Param}/${infer Rest}`
|
||||
? Param | ExtractRouteParams<Rest>
|
||||
: T extends `${infer _Start}:${infer Param}` ? Param
|
||||
: T extends `${infer _Start}*` ? "restOfThePath"
|
||||
: never
|
||||
: never;
|
||||
|
||||
@ -124,7 +124,7 @@ export class Ok<T, E> implements IResult<T, E> {
|
||||
}
|
||||
|
||||
mapErr<U>(fn: (err: E) => U): Result<T, U> {
|
||||
return new Ok<T, U>(this.value);
|
||||
return ok<T, U>(this.value);
|
||||
}
|
||||
|
||||
mapErrAsync<U>(fn: (err: E) => Promise<U>): ResultAsync<T, U> {
|
||||
@ -236,8 +236,7 @@ export class Err<T, E> implements IResult<T, E> {
|
||||
return errAsync(this.error);
|
||||
}
|
||||
mapErr<U>(fn: (err: E) => U): Result<T, U> {
|
||||
const mappedError = fn(this.error);
|
||||
return new Err<T, U>(mappedError);
|
||||
return new Err<T, U>(fn(this.error));
|
||||
}
|
||||
mapErrAsync<U>(fn: (err: E) => Promise<U>): ResultAsync<T, U> {
|
||||
return ResultAsync.fromPromise(
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { err, Result } from "@shared/utils/result.ts";
|
||||
import { ok } from "@shared/utils/index.ts";
|
||||
|
||||
import { None, none, Option, some } from "@shared/utils/option.ts";
|
||||
// ── Error Types ─────────────────────────────────────────────────────
|
||||
type ValidationErrorDetail =
|
||||
| {
|
||||
@ -973,6 +973,180 @@ class NullishSchema<S extends Schema<any>>
|
||||
}
|
||||
}
|
||||
|
||||
class ResultSchema<T extends Schema<any>, E extends Schema<any>>
|
||||
extends BaseSchema<Result<InferSchemaType<T>, InferSchemaType<E>>> {
|
||||
constructor(
|
||||
private readonly okSchema: T,
|
||||
private readonly errSchema: E,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
protected override validateInput(
|
||||
input: unknown,
|
||||
): Result<
|
||||
Result<InferSchemaType<T>, InferSchemaType<E>>,
|
||||
SchemaValidationError
|
||||
> {
|
||||
return BaseSchema.validatePrimitive<object>(input, "object").andThen(
|
||||
(
|
||||
obj,
|
||||
): Result<
|
||||
Result<InferSchemaType<T>, InferSchemaType<E>>,
|
||||
SchemaValidationError
|
||||
> => {
|
||||
if ("tag" in obj) {
|
||||
switch (obj.tag) {
|
||||
case "ok": {
|
||||
if ("value" in obj) {
|
||||
return this.okSchema.parse(
|
||||
obj.value,
|
||||
).match(
|
||||
(v) => ok(ok(v as InferSchemaType<T>)),
|
||||
(e) =>
|
||||
err(createValidationError(input, {
|
||||
kind: "propertyValidation",
|
||||
property: "value",
|
||||
detail: e.detail,
|
||||
})),
|
||||
);
|
||||
} else if (
|
||||
BaseSchema.isNullishSchema(this.okSchema)
|
||||
) {
|
||||
return ok(
|
||||
ok() as Result<
|
||||
InferSchemaType<T>,
|
||||
InferSchemaType<E>
|
||||
>,
|
||||
);
|
||||
}
|
||||
return err(createValidationError(input, {
|
||||
kind: "missingProperties",
|
||||
keys: ["value"],
|
||||
msg: "If tag is set to 'ok', than result must contain a 'value' property",
|
||||
}));
|
||||
}
|
||||
case "err": {
|
||||
if (
|
||||
"error" in obj
|
||||
) {
|
||||
return this.errSchema.parse(
|
||||
obj.error,
|
||||
).match(
|
||||
(e) => ok(err(e as InferSchemaType<E>)),
|
||||
(e) =>
|
||||
err(createValidationError(input, {
|
||||
kind: "propertyValidation",
|
||||
property: "error",
|
||||
detail: e.detail,
|
||||
})),
|
||||
);
|
||||
} else if (
|
||||
BaseSchema.isNullishSchema(this.errSchema)
|
||||
) {
|
||||
return ok(
|
||||
err() as Result<
|
||||
InferSchemaType<T>,
|
||||
InferSchemaType<E>
|
||||
>,
|
||||
);
|
||||
}
|
||||
return err(createValidationError(input, {
|
||||
kind: "missingProperties",
|
||||
keys: ["error"],
|
||||
msg: "If tag is set to 'err', than result must contain a 'error' property",
|
||||
}));
|
||||
}
|
||||
default:
|
||||
return err(createValidationError(input, {
|
||||
kind: "propertyValidation",
|
||||
property: "tag",
|
||||
detail: {
|
||||
kind: "typeMismatch",
|
||||
expected: "'ok' or 'err'",
|
||||
received: `'${obj.tag}'`,
|
||||
},
|
||||
}));
|
||||
}
|
||||
} else {
|
||||
return err(createValidationError(input, {
|
||||
kind: "missingProperties",
|
||||
keys: ["tag"],
|
||||
msg: "Result must contain a tag property",
|
||||
}));
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class OptionSchema<T extends Schema<any>>
|
||||
extends BaseSchema<Option<InferSchemaType<T>>> {
|
||||
constructor(
|
||||
private readonly schema: T,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
protected override validateInput(
|
||||
input: unknown,
|
||||
): Result<Option<InferSchemaType<T>>, SchemaValidationError> {
|
||||
return BaseSchema.validatePrimitive<object>(input, "object").andThen(
|
||||
(
|
||||
obj,
|
||||
): Result<Option<InferSchemaType<T>>, SchemaValidationError> => {
|
||||
if ("tag" in obj) {
|
||||
switch (obj.tag) {
|
||||
case "some": {
|
||||
if ("value" in obj) {
|
||||
return this.schema.parse(
|
||||
obj.value,
|
||||
).match(
|
||||
(v) => ok(some(v as InferSchemaType<T>)),
|
||||
(e) =>
|
||||
err(createValidationError(input, {
|
||||
kind: "propertyValidation",
|
||||
property: "value",
|
||||
detail: e.detail,
|
||||
})),
|
||||
);
|
||||
} else if (
|
||||
BaseSchema.isNullishSchema(this.schema)
|
||||
) {
|
||||
return ok(some() as Option<InferSchemaType<T>>);
|
||||
}
|
||||
return err(createValidationError(input, {
|
||||
kind: "missingProperties",
|
||||
keys: ["value"],
|
||||
msg: "If tag is set to 'some', than option must contain a 'value' property",
|
||||
}));
|
||||
}
|
||||
case "none": {
|
||||
return ok(none);
|
||||
}
|
||||
default:
|
||||
return err(createValidationError(input, {
|
||||
kind: "propertyValidation",
|
||||
property: "tag",
|
||||
detail: {
|
||||
kind: "typeMismatch",
|
||||
expected: "'some' or 'none'",
|
||||
received: `'${obj.tag}'`,
|
||||
},
|
||||
}));
|
||||
}
|
||||
} else {
|
||||
return err(createValidationError(input, {
|
||||
kind: "missingProperties",
|
||||
keys: ["tag"],
|
||||
msg: "Option must contain a tag property",
|
||||
}));
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/* ── Helper Object for Schema Creation (z) ───────────────────────────────────── */
|
||||
|
||||
export const z = {
|
||||
@ -1023,13 +1197,11 @@ export const z = {
|
||||
schema: S,
|
||||
msg?: string,
|
||||
) => new NullishSchema<S>(schema, msg),
|
||||
result: <T extends Schema<any>, E extends Schema<any>>(
|
||||
okSchema: T,
|
||||
errSchema: E,
|
||||
) => new ResultSchema<T, E>(okSchema, errSchema),
|
||||
option: <T extends Schema<any>>(
|
||||
schema: T,
|
||||
) => new OptionSchema<T>(schema),
|
||||
};
|
||||
|
||||
const schema = z.obj({
|
||||
string: z.string(),
|
||||
number: z.number(),
|
||||
});
|
||||
|
||||
type schemaType = typeof schema;
|
||||
|
||||
type test = InferSchemaType<typeof schema>;
|
||||
|
||||
Reference in New Issue
Block a user