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 { Result } from "@shared/utils/result.ts";
|
||||||
|
import { Schema } from "@shared/utils/validator.ts";
|
||||||
|
|
||||||
class ClientApi<Req, Res> {
|
class ApiRoute<
|
||||||
constructor(path: string, method: string) {}
|
Path extends string,
|
||||||
validate(res: Response): ResultAsync<Res> {
|
ReqSchema extends Schema<any>,
|
||||||
const body = await res.json();
|
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> {
|
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> {
|
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);
|
return errAsync(this.error);
|
||||||
}
|
}
|
||||||
mapErr<U>(fn: (err: E) => U): Result<T, U> {
|
mapErr<U>(fn: (err: E) => U): Result<T, U> {
|
||||||
const mappedError = fn(this.error);
|
return new Err<T, U>(fn(this.error));
|
||||||
return new Err<T, U>(mappedError);
|
|
||||||
}
|
}
|
||||||
mapErrAsync<U>(fn: (err: E) => Promise<U>): ResultAsync<T, U> {
|
mapErrAsync<U>(fn: (err: E) => Promise<U>): ResultAsync<T, U> {
|
||||||
return ResultAsync.fromPromise(
|
return ResultAsync.fromPromise(
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { err, Result } from "@shared/utils/result.ts";
|
import { err, Result } from "@shared/utils/result.ts";
|
||||||
import { ok } from "@shared/utils/index.ts";
|
import { ok } from "@shared/utils/index.ts";
|
||||||
|
import { None, none, Option, some } from "@shared/utils/option.ts";
|
||||||
// ── Error Types ─────────────────────────────────────────────────────
|
// ── Error Types ─────────────────────────────────────────────────────
|
||||||
type ValidationErrorDetail =
|
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) ───────────────────────────────────── */
|
/* ── Helper Object for Schema Creation (z) ───────────────────────────────────── */
|
||||||
|
|
||||||
export const z = {
|
export const z = {
|
||||||
@ -1023,13 +1197,11 @@ export const z = {
|
|||||||
schema: S,
|
schema: S,
|
||||||
msg?: string,
|
msg?: string,
|
||||||
) => new NullishSchema<S>(schema, msg),
|
) => 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