Initial commit
This commit is contained in:
20
.editorconfig
Normal file
20
.editorconfig
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
end_of_line = lf
|
||||||
|
indent_size = 4
|
||||||
|
indent_style = tab
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
|
[*.md]
|
||||||
|
trim_trailing_whitespace = false
|
||||||
|
|
||||||
|
[*.{yml,yaml}]
|
||||||
|
indent_size = 2
|
||||||
|
indent_style = space
|
||||||
|
|
||||||
|
[bun.lock]
|
||||||
|
indent_size = 2
|
||||||
|
indent_style = space
|
||||||
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
/dist
|
||||||
|
/node_modules
|
||||||
1
.tsbuildinfo
Normal file
1
.tsbuildinfo
Normal file
File diff suppressed because one or more lines are too long
20
package.json
Normal file
20
package.json
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"name": "apios",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"license": "UNLICENSED",
|
||||||
|
"imports": {
|
||||||
|
"#/*": {
|
||||||
|
"types": "./dist/*.d.ts",
|
||||||
|
"import": "./dist/*.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"watch": "pnpm exec tsc --build --watch"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@effect/language-service": "^0.86.2",
|
||||||
|
"@types/node": "^24",
|
||||||
|
"typescript": "^6.0.3"
|
||||||
|
}
|
||||||
|
}
|
||||||
78
pnpm-lock.yaml
generated
Normal file
78
pnpm-lock.yaml
generated
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
lockfileVersion: '9.0'
|
||||||
|
|
||||||
|
settings:
|
||||||
|
autoInstallPeers: true
|
||||||
|
excludeLinksFromLockfile: false
|
||||||
|
|
||||||
|
importers:
|
||||||
|
|
||||||
|
.:
|
||||||
|
dependencies:
|
||||||
|
effect:
|
||||||
|
specifier: ^3.21.2
|
||||||
|
version: 3.21.2
|
||||||
|
devDependencies:
|
||||||
|
'@effect/language-service':
|
||||||
|
specifier: ^0.86.2
|
||||||
|
version: 0.86.2
|
||||||
|
'@types/node':
|
||||||
|
specifier: ^24
|
||||||
|
version: 24.12.4
|
||||||
|
typescript:
|
||||||
|
specifier: ^6.0.3
|
||||||
|
version: 6.0.3
|
||||||
|
|
||||||
|
packages:
|
||||||
|
|
||||||
|
'@effect/language-service@0.86.2':
|
||||||
|
resolution: {integrity: sha512-SaPln+8srOqDJDUwNTDmP5e+IYpEDr9+1epGznnsLqu8xvo6VnxyWARdeLpqvZJlb0Pgy9ca7ppqvvdWbHPXAg==}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
|
'@standard-schema/spec@1.1.0':
|
||||||
|
resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==}
|
||||||
|
|
||||||
|
'@types/node@24.12.4':
|
||||||
|
resolution: {integrity: sha512-GUUEShf+PBCGW2KaXwcIt3Yk+e3pkKwWKb9GSyM9WQVE+ep2jzmHdGsHzu4wgcZy5fN9FBdVzjpBQsYlpfpgLA==}
|
||||||
|
|
||||||
|
effect@3.21.2:
|
||||||
|
resolution: {integrity: sha512-rXd2FGDM8KdjSIrc+mqEELo7ScW7xTVxEf1iInmPSpIde9/nyGuFM710cjTo7/EreGXiUX2MOonPpprbz2XHCg==}
|
||||||
|
|
||||||
|
fast-check@3.23.2:
|
||||||
|
resolution: {integrity: sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==}
|
||||||
|
engines: {node: '>=8.0.0'}
|
||||||
|
|
||||||
|
pure-rand@6.1.0:
|
||||||
|
resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==}
|
||||||
|
|
||||||
|
typescript@6.0.3:
|
||||||
|
resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==}
|
||||||
|
engines: {node: '>=14.17'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
|
undici-types@7.16.0:
|
||||||
|
resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==}
|
||||||
|
|
||||||
|
snapshots:
|
||||||
|
|
||||||
|
'@effect/language-service@0.86.2': {}
|
||||||
|
|
||||||
|
'@standard-schema/spec@1.1.0': {}
|
||||||
|
|
||||||
|
'@types/node@24.12.4':
|
||||||
|
dependencies:
|
||||||
|
undici-types: 7.16.0
|
||||||
|
|
||||||
|
effect@3.21.2:
|
||||||
|
dependencies:
|
||||||
|
'@standard-schema/spec': 1.1.0
|
||||||
|
fast-check: 3.23.2
|
||||||
|
|
||||||
|
fast-check@3.23.2:
|
||||||
|
dependencies:
|
||||||
|
pure-rand: 6.1.0
|
||||||
|
|
||||||
|
pure-rand@6.1.0: {}
|
||||||
|
|
||||||
|
typescript@6.0.3: {}
|
||||||
|
|
||||||
|
undici-types@7.16.0: {}
|
||||||
145
src/Result.ts
Normal file
145
src/Result.ts
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
type Covariant<A> = (_: never) => A;
|
||||||
|
|
||||||
|
type Destruct<A, E> =
|
||||||
|
| { success: A, failure: null }
|
||||||
|
| { success: null, failure: E }
|
||||||
|
;
|
||||||
|
|
||||||
|
const identity = <A>(_: A): A => _;
|
||||||
|
const throwUnwrapError = <E>(_: E): never => { throw new UnwrapError(_); }
|
||||||
|
|
||||||
|
const TypeId = Symbol("Result");
|
||||||
|
type TypeId = typeof TypeId;
|
||||||
|
|
||||||
|
interface ResultBase<out A, out E> {
|
||||||
|
readonly [TypeId]: {
|
||||||
|
readonly _A: Covariant<A>,
|
||||||
|
readonly _E: Covariant<E>,
|
||||||
|
};
|
||||||
|
|
||||||
|
isFailure<A, E>(): this is Failure<A, E>;
|
||||||
|
isSuccess<A, E>(): this is Success<A, E>;
|
||||||
|
|
||||||
|
unwrap<E1 = never>(onFailure?: (failure: E) => E1): A | E1;
|
||||||
|
destruct(): Destruct<A, E>;
|
||||||
|
|
||||||
|
map<A1, E1 = E>(
|
||||||
|
onSuccess: (success: A) => A1,
|
||||||
|
onFailure?: (failure: E) => E1,
|
||||||
|
): Result<A1, E1>;
|
||||||
|
|
||||||
|
flatMap<A1, E1, A2 = never, E2 = E>(
|
||||||
|
onSuccess: (success: A) => Result<A1, E1>,
|
||||||
|
onFailure?: (failure: E) => Result<A2, E2>,
|
||||||
|
): Result<A1 | A2, E1 | E2>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CommonProto = Object.freeze({
|
||||||
|
[TypeId]: Object.freeze({
|
||||||
|
_A: (_: never) => _,
|
||||||
|
_E: (_: never) => _,
|
||||||
|
}),
|
||||||
|
|
||||||
|
isFailure<A, E>(this: Result<A, E>): this is Failure<A, E> { return this._tag == "Failure"; },
|
||||||
|
isSuccess<A, E>(this: Result<A, E>): this is Success<A, E> { return this._tag == "Success"; },
|
||||||
|
|
||||||
|
unwrap<A, E, E1 = never>(
|
||||||
|
this: Result<A, E>,
|
||||||
|
onFailure: (failure: E) => E1 = throwUnwrapError,
|
||||||
|
): A | E1 {
|
||||||
|
switch (this._tag) {
|
||||||
|
case "Failure":
|
||||||
|
return onFailure(this.failure);
|
||||||
|
case "Success":
|
||||||
|
return this.success;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
destruct<A, E>(this: Result<A, E>): Destruct<A, E> {
|
||||||
|
switch (this._tag) {
|
||||||
|
case "Failure":
|
||||||
|
return { failure: this.failure, success: null };
|
||||||
|
case "Success":
|
||||||
|
return { failure: null, success: this.success };
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
map<A, E, A1, E1 = E>(
|
||||||
|
this: Result<A, E>,
|
||||||
|
onSuccess: (success: A) => A1,
|
||||||
|
onFailure: (failure: E) => E1 = identity as (_: E) => E1,
|
||||||
|
): Result<A1, E1> {
|
||||||
|
switch (this._tag) {
|
||||||
|
case "Failure":
|
||||||
|
return Result.failure(onFailure(this.failure));
|
||||||
|
case "Success":
|
||||||
|
return Result.success(onSuccess(this.success));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
flatMap<A, E, A1, E1, A2 = never, E2 = E>(
|
||||||
|
this: Result<A, E>,
|
||||||
|
onSuccess: (success: A) => Result<A1, E1>,
|
||||||
|
onFailure: (failure: E) => Result<A2, E2> = Result.failure as (_: E) => Result<A2, E2>,
|
||||||
|
): Result<A1 | A2, E1 | E2> {
|
||||||
|
switch (this._tag) {
|
||||||
|
case "Failure":
|
||||||
|
return onFailure(this.failure);
|
||||||
|
case "Success":
|
||||||
|
return onSuccess(this.success);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export interface Failure<out A, out E> extends ResultBase<A, E> {
|
||||||
|
readonly _tag: "Failure";
|
||||||
|
readonly failure: E;
|
||||||
|
}
|
||||||
|
|
||||||
|
const FailureProto = Object.freeze(Object.assign(
|
||||||
|
Object.create(CommonProto),
|
||||||
|
{
|
||||||
|
_tag: "Failure",
|
||||||
|
},
|
||||||
|
));
|
||||||
|
|
||||||
|
export interface Success<out A, out E> extends ResultBase<A, E> {
|
||||||
|
readonly _tag: "Success";
|
||||||
|
readonly success: A;
|
||||||
|
}
|
||||||
|
|
||||||
|
const SuccessProto = Object.freeze(Object.assign(
|
||||||
|
Object.create(CommonProto),
|
||||||
|
{
|
||||||
|
_tag: "Success",
|
||||||
|
},
|
||||||
|
));
|
||||||
|
|
||||||
|
export type Result<A, E = never> = Failure<A, E> | Success<A, E>;
|
||||||
|
|
||||||
|
export const Result = Object.freeze({
|
||||||
|
isResult(u: unknown): u is Result<unknown, unknown> {
|
||||||
|
return typeof u === "object" && u !== null && TypeId in u;
|
||||||
|
},
|
||||||
|
|
||||||
|
failure<E>(failure: E): Failure<never, E> {
|
||||||
|
return Object.freeze(Object.create(FailureProto, {
|
||||||
|
failure: { value: failure },
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
success<A>(success: A): Success<A, never> {
|
||||||
|
return Object.freeze(Object.create(SuccessProto, {
|
||||||
|
success: { value: success },
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export class UnwrapError<out E> extends Error {
|
||||||
|
|
||||||
|
readonly failure: E;
|
||||||
|
|
||||||
|
constructor(failure: E) {
|
||||||
|
super();
|
||||||
|
this.failure = failure;
|
||||||
|
}
|
||||||
|
}
|
||||||
76
src/StandardSchemaV1.ts
Normal file
76
src/StandardSchemaV1.ts
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
/** The Standard Schema interface. */
|
||||||
|
export interface StandardSchemaV1<Input = unknown, Output = Input> {
|
||||||
|
/** The Standard Schema properties. */
|
||||||
|
readonly '~standard': StandardSchemaV1.Props<Input, Output>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export declare namespace StandardSchemaV1 {
|
||||||
|
/** The Standard Schema properties interface. */
|
||||||
|
export interface Props<Input = unknown, Output = Input> {
|
||||||
|
/** The version number of the standard. */
|
||||||
|
readonly version: 1;
|
||||||
|
/** The vendor name of the schema library. */
|
||||||
|
readonly vendor: string;
|
||||||
|
/** Validates unknown input values. */
|
||||||
|
readonly validate: (
|
||||||
|
value: unknown,
|
||||||
|
options?: StandardSchemaV1.Options | undefined
|
||||||
|
) => Result<Output> | Promise<Result<Output>>;
|
||||||
|
/** Inferred types associated with the schema. */
|
||||||
|
readonly types?: Types<Input, Output> | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The result interface of the validate function. */
|
||||||
|
export type Result<Output> = SuccessResult<Output> | FailureResult;
|
||||||
|
|
||||||
|
/** The result interface if validation succeeds. */
|
||||||
|
export interface SuccessResult<Output> {
|
||||||
|
/** The typed output value. */
|
||||||
|
readonly value: Output;
|
||||||
|
/** A falsy value for `issues` indicates success. */
|
||||||
|
readonly issues?: undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Options {
|
||||||
|
/** Explicit support for additional vendor-specific parameters, if needed. */
|
||||||
|
readonly libraryOptions?: Record<string, unknown> | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The result interface if validation fails. */
|
||||||
|
export interface FailureResult {
|
||||||
|
/** The issues of failed validation. */
|
||||||
|
readonly issues: ReadonlyArray<Issue>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The issue interface of the failure output. */
|
||||||
|
export interface Issue {
|
||||||
|
/** The error message of the issue. */
|
||||||
|
readonly message: string;
|
||||||
|
/** The path of the issue, if any. */
|
||||||
|
readonly path?: ReadonlyArray<PropertyKey | PathSegment> | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The path segment interface of the issue. */
|
||||||
|
export interface PathSegment {
|
||||||
|
/** The key representing a path segment. */
|
||||||
|
readonly key: PropertyKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The Standard Schema types interface. */
|
||||||
|
export interface Types<Input = unknown, Output = Input> {
|
||||||
|
/** The input type of the schema. */
|
||||||
|
readonly input: Input;
|
||||||
|
/** The output type of the schema. */
|
||||||
|
readonly output: Output;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Infers the input type of a Standard Schema. */
|
||||||
|
export type InferInput<Schema extends StandardSchemaV1> = NonNullable<
|
||||||
|
Schema['~standard']['types']
|
||||||
|
>['input'];
|
||||||
|
|
||||||
|
/** Infers the output type of a Standard Schema. */
|
||||||
|
export type InferOutput<Schema extends StandardSchemaV1> = NonNullable<
|
||||||
|
Schema['~standard']['types']
|
||||||
|
>['output'];
|
||||||
|
}
|
||||||
53
src/index.test.ts
Normal file
53
src/index.test.ts
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
import { defineApi } from "./index.js";
|
||||||
|
import type { StandardSchemaV1 } from "./StandardSchemaV1.js";
|
||||||
|
|
||||||
|
interface User {
|
||||||
|
readonly userId: string;
|
||||||
|
readonly username: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UserCreateInfo {
|
||||||
|
readonly username: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare const UserSchema: StandardSchemaV1<User>;
|
||||||
|
declare const UsersSchema: StandardSchemaV1<readonly User[]>;
|
||||||
|
declare const UserCreateInfoSchema: StandardSchemaV1<UserCreateInfo>;
|
||||||
|
|
||||||
|
const fetchUsers = defineApi({
|
||||||
|
url: "https://apios.invalid/api/v1/users/",
|
||||||
|
method: "GET",
|
||||||
|
|
||||||
|
responseBody: UsersSchema,
|
||||||
|
});
|
||||||
|
|
||||||
|
const createUser = defineApi({
|
||||||
|
url: "https://apios.invalid/api/v1/users/",
|
||||||
|
method: "POST",
|
||||||
|
|
||||||
|
requestBody: UserCreateInfoSchema,
|
||||||
|
responseBody: UserSchema,
|
||||||
|
|
||||||
|
credentials: "include",
|
||||||
|
});
|
||||||
|
|
||||||
|
const resultA = (await createUser({ username: "admin" })).unwrap();
|
||||||
|
const resultB = await fetchUsers();
|
||||||
|
|
||||||
|
{
|
||||||
|
const { success: users, failure } = resultB.destruct();
|
||||||
|
|
||||||
|
if (failure !== null) {
|
||||||
|
console.error("Failed to fetch users");
|
||||||
|
} else {
|
||||||
|
console.log("Users:", users);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- OR ---
|
||||||
|
|
||||||
|
if (resultB.isFailure()) {
|
||||||
|
console.error("Failed to fetch users");
|
||||||
|
} else {
|
||||||
|
console.log("Users:", resultB.success);
|
||||||
|
}
|
||||||
116
src/index.ts
Normal file
116
src/index.ts
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
import { Result } from "./Result.js";
|
||||||
|
import type { StandardSchemaV1 } from "./StandardSchemaV1.js";
|
||||||
|
|
||||||
|
type RequestBody<Args extends ArgsBase> = Args["requestBody"] extends StandardSchemaV1<any> ? StandardSchemaV1.InferOutput<Args["requestBody"]> : void;
|
||||||
|
type ResponseBody<Args extends ArgsBase> = Args["responseBody"] extends StandardSchemaV1<any> ? StandardSchemaV1.InferOutput<Args["responseBody"]> : void;
|
||||||
|
type ErrorBody<Args extends ArgsBase> = Args["errorBody"] extends StandardSchemaV1<any> ? StandardSchemaV1.InferOutput<Args["errorBody"]> : void;
|
||||||
|
|
||||||
|
type Api<Args extends ArgsBase> = (requestBody: RequestBody<Args>, signal?: AbortSignal) => Promise<Result<ResponseBody<Args>, ErrorBody<Args>>>;
|
||||||
|
|
||||||
|
interface ArgsBase {
|
||||||
|
readonly url: string;
|
||||||
|
readonly method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | (string & {});
|
||||||
|
|
||||||
|
readonly requestBody?: StandardSchemaV1<any> | undefined;
|
||||||
|
readonly responseBody?: StandardSchemaV1<any> | undefined;
|
||||||
|
readonly errorBody?: StandardSchemaV1<any> | undefined;
|
||||||
|
|
||||||
|
readonly cache?: RequestInit["cache"];
|
||||||
|
readonly credentials?: RequestInit["credentials"];
|
||||||
|
readonly headers?: RequestInit["headers"];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const defineApi = <const Args extends ArgsBase>({
|
||||||
|
url,
|
||||||
|
method,
|
||||||
|
|
||||||
|
requestBody: requestBodySchema,
|
||||||
|
responseBody: responseBodySchema,
|
||||||
|
errorBody: errorBodySchema,
|
||||||
|
|
||||||
|
cache,
|
||||||
|
credentials,
|
||||||
|
headers: headersInit,
|
||||||
|
}: Args): Api<Args> => {
|
||||||
|
|
||||||
|
const headers = new Headers(headersInit);
|
||||||
|
if (requestBodySchema !== undefined) {
|
||||||
|
headers.set("Content-Type", "application/json");
|
||||||
|
}
|
||||||
|
|
||||||
|
const init: RequestInit = {
|
||||||
|
method,
|
||||||
|
headers,
|
||||||
|
...(cache !== undefined ? { cache } : null),
|
||||||
|
...(credentials !== undefined ? { credentials } : null),
|
||||||
|
};
|
||||||
|
|
||||||
|
return async (requestBody, signal) => {
|
||||||
|
const body = requestBodySchema !== undefined ? JSON.stringify(requestBody) : null;
|
||||||
|
const response = await fetch(url, {
|
||||||
|
...init,
|
||||||
|
body,
|
||||||
|
...(signal !== undefined ? { signal } : null),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
if (responseBodySchema !== undefined) {
|
||||||
|
const json = await response.json();
|
||||||
|
const validateResult = await responseBodySchema["~standard"].validate(json);
|
||||||
|
if (validateResult.issues !== undefined) {
|
||||||
|
throw new ValidationError({
|
||||||
|
schema: responseBodySchema,
|
||||||
|
input: json,
|
||||||
|
issues: validateResult.issues,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return Result.success(validateResult.value);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Result.success(undefined);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (errorBodySchema !== undefined) {
|
||||||
|
const json = await response.json();
|
||||||
|
const validateResult = await errorBodySchema["~standard"].validate(json);
|
||||||
|
if (validateResult.issues !== undefined) {
|
||||||
|
throw new ValidationError({
|
||||||
|
schema: errorBodySchema,
|
||||||
|
input: json,
|
||||||
|
issues: validateResult.issues,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return Result.failure(validateResult.value);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Result.failure(undefined);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export namespace ValidationError {
|
||||||
|
export interface Props {
|
||||||
|
readonly schema: StandardSchemaV1<any>;
|
||||||
|
readonly input: unknown;
|
||||||
|
readonly issues: readonly StandardSchemaV1.Issue[];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ValidationError extends Error {
|
||||||
|
|
||||||
|
readonly schema: StandardSchemaV1;
|
||||||
|
readonly input: unknown;
|
||||||
|
readonly issues: readonly StandardSchemaV1.Issue[];
|
||||||
|
|
||||||
|
constructor({
|
||||||
|
schema,
|
||||||
|
input,
|
||||||
|
issues,
|
||||||
|
}: ValidationError.Props) {
|
||||||
|
super();
|
||||||
|
this.schema = schema;
|
||||||
|
this.input = input;
|
||||||
|
this.issues = issues;
|
||||||
|
}
|
||||||
|
}
|
||||||
44
tsconfig.json
Normal file
44
tsconfig.json
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"incremental": true,
|
||||||
|
"composite": true,
|
||||||
|
|
||||||
|
"rootDir": "./src",
|
||||||
|
"outDir": "./dist",
|
||||||
|
"tsBuildInfoFile": ".tsbuildinfo",
|
||||||
|
|
||||||
|
"module": "NodeNext",
|
||||||
|
"moduleResolution": "NodeNext",
|
||||||
|
"moduleDetection": "force",
|
||||||
|
|
||||||
|
"types": ["node"],
|
||||||
|
|
||||||
|
"target": "ESNext",
|
||||||
|
"lib": ["ESNext"],
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
|
||||||
|
"sourceMap": true,
|
||||||
|
"declaration": true,
|
||||||
|
"declarationMap": true,
|
||||||
|
"removeComments": true,
|
||||||
|
"newLine": "lf",
|
||||||
|
|
||||||
|
"strict": true,
|
||||||
|
|
||||||
|
"noImplicitOverride": true,
|
||||||
|
"noImplicitReturns": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"noPropertyAccessFromIndexSignature": true,
|
||||||
|
"exactOptionalPropertyTypes": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"verbatimModuleSyntax": true,
|
||||||
|
|
||||||
|
"noErrorTruncation": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
|
||||||
|
"plugins": [
|
||||||
|
{ "name": "@effect/language-service" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"include": ["./src"],
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user