decs / typeschema Goto Github PK
View Code? Open in Web Editor NEW๐ต Universal adapter for TypeScript schema validation.
Home Page: https://typeschema.com
License: MIT License
๐ต Universal adapter for TypeScript schema validation.
Home Page: https://typeschema.com
License: MIT License
Hi, Thank you for this lib.
It would be nice if we can abstract a common set of schema apis (e.g. merge schemas) available for lib authors.
I am happy to help implementing this if you think this is a valid requirement.
It is very important for lib builders, to have access to the typescript type Schema
.
Can you pls add it to the exports?
I had to downgrade to 0.11.0
because the library was asking me to install all the peer dependencies.
Tried with pnpm and npm. Same error.
./node_modules/.pnpm/@[email protected][email protected]/node_modules/@decs/typeschema/dist/deepkit-KEFZXADV.mjs:2:0
Module not found: Can't resolve '@deepkit/type'
https://nextjs.org/docs/messages/module-not-found
Import trace for requested module:
./node_modules/.pnpm/@[email protected][email protected]/node_modules/@decs/typeschema/dist/index.mjs
./src/server/routers/auth/login.ts
./src/server/routers/auth.ts
./src/server/index.ts
./src/app/api/trpc/[trpc]/route.ts
It seems there is an error where the _valibot
variant is calling for the other deps.
Can you add support for vinejs :
Thanks in advance
New design makes registry no more extensible by users reverting the design to what it was before (ie: adapter are not self contained)
Making users dependant on typeschema needing to add support for niche libraries.
Making it not trully agnostic library.
is this temporary or a design decision ?
At the moment the the validate function is returning a promise, is there a way to support synchronous validation and rename the current validate function to asyncValidate
?
Hello,
I did some tests on some more advanced schemas.
There are some issues with current approach to infer the type for libraries supporting inputs that are different than outputs.
For example, in arktype, adding transformations, the type T of the schema is not equal the typescript type to output :
There is the same kind of issue with io-ts
where the schema inferred is wrong and can't even be passed to validate
function when transforming the input data to a different one (example transforming a string
to a Date
object)
One solution is to remove the utility type Schema<T>
and instead change the signature of validate
and assert
functions.
export async function validate<Schema>(
schema: Schema,
data: unknown,
): Promise<{data: InferOutput<Schema>} | {issues: Array<ValidationIssue>}> {
const wrappedSchema = wrapCached(schema) ?? (await wrap(schema));
return wrappedSchema.validate(data);
}
and:
export async function assert<Schema>(schema: Schema, data: unknown): Promise<InferOutput<Schema>> {
const result = await validate(schema, data);
if ('issues' in result) {
throw result.issues[0];
}
return result.data;
}
I'm creating a PR to solve this in this way.
It will be a BREAKING CHANGE. Since you'll no more have Schema<T>
type.
But i don't thing it's that bad, since nobody should use this utility type directly, because you don't want to write the ts type youself but always want to infer it.
Now that its decided that TypeSchema will be used in Superforms v2, I'm seeing the need for a Validation Schema to JSON Schema feature for the validation libraries.
I'm planning to do this with similar kind adapters as in TypeSchema, but then I was thinking "why shouldn't that be a part of TypeSchema, or its own library?" So I wonder if it would be feasible to build this into TypeSchema? Of course, I'll be contributing to that feature.
I see that typeschema doesn't have Infer capabilities for ajv. But there's a typescript version, could it be used for that?
This is just a hint that we will change the safeParseAsync
API. You can write result.issues
in the next version instead of result.error.issues
. .error
will still work then but is deprecated.
https://github.com/decs/typeschema/blob/main/src/adapters/valibot.ts#L35
Hi, thank you for a nice initiative to standardize a very fragmented area of the ecosystem. I'd like to get some ideas about how to build upon and improve the simple and useful API you have right now.
I'd love to use typeschema with Superforms, and in the future v2.0 be able to hook into any validation library. Right now only Zod is supported, which wasn't a random pick, it's a great library but also pretty much the only one I could find that goes beyond validation and has some important features for a comprehensive form library like Superforms.
You can read the details here: ciscoheat/sveltekit-superforms#120, but in essence I'm looking for a couple of additional things for a validation library:
A. Being able to introspect the schema types, to figure out the following about a schema field:
ZodEffect
)This allows Superforms to create default values for a schema, so you can do something like:
const form = await superValidate(schema);
Which will amongst other return an object with default values for the schema, so type-safety is ensured.
Another piece of data returned from superValidate
is constraints
, which takes us into the next level of introspection:
B. Introspect the specific validators on a schema field.
Given a Zod schema like this:
z.string().min(3).regex(/^\w+$/)
You can look into all validators and generate html validation constraints based upon them, so in this case:
{
min: 3,
pattern: '^\w+$'
required: true
}
This object can then be spread on an html input field, and we have browser validation without requiring javascript.
I'm not really up to speed with the other libraries, so my question is, do you know if some of them have the possibility to handle A and B? A feature matrix would be awesome, then I can make a decision if it's worth pursuing this for v2.0.
It depends on how many libraries can handle case A basically. B is nice to have, but A is pretty much required, unless the library supports default values and the consumer want to supply their own for every field, which is a bit tedious. So maybe the matrix would be:
Project | Default values | A. Type introspection | B. Validator introspection |
---|---|---|---|
zod | โ | โ | โ |
yup | |||
joi | |||
ajv | |||
superstruct | |||
io-ts | |||
ow | |||
typebox | |||
typia | |||
deepkit | |||
runtypes | |||
arktype |
Let me know what you think, thanks for any help you can give. :)
This is the error:
Running locally is fine but production. Please fix this asap! Thank you so much!
Hello,
Someone showed me your project. it's really interresting.
I did something a little different with my library Typematic
: https://github.com/ts-api-spec/typematic
it's using some Higher Kinded Types trick to not tie the OpenApi Spec to any validation library at compile time/runtime.
The advantages over the tRPC approach :
@effect/schema
, json-schema or json-types)validate()
to not throw while another lib do)Some disadvantages :
Maybe typematic can give you some ideas on how to have your cake and it up too.
Keep it up!
The more, the merrier? https://github.com/badrap/valita
Hi, I'm using TypeSchema in next-safe-action, which is a Next.js type safe wrapper for React Sever Actions.
Some users found out that since version 6 of the library, Next.js dev server using Turbopack (next dev --turbo
) and deployments on Vercel using edge runtime are broken, due to TypeSchema internal errors. Additional information about these errors can be found in this issue.
I'm not sure if these problems can be fixed easily, but I'm happy to help in any way I can. Thank you!
I am using valibot in a Svelte app. Using "@decs/typeschema": "^0.7.0"
and "valibot": "^0.8.0"
Not getting a type error, but I am getting this message as a runtime error.
Error: Missing adapters for schema: [object Object]
Example:
// valibot schema
export const newCharacterSchema = object({
name: string([minLength(1, "Required")]),
campaign: string([minLength(1, "Required")]),
race: useDefault(string(), ""),
class: useDefault(string(), ""),
character_sheet_url: useDefault(string([url()]), ""),
image_url: useDefault(string([url()]), "")
});
<!-- +page.svelte -->
<SchemaForm ... schema={newCharacterSchema} ...>
...
</SchemaForm>
<!-- SchemaForm.svelte -->
<script lang="ts" generics="TSchema extends Schema">
import type { Infer, InferIn, Schema } from "@decs/typeschema";
import { validate } from "@decs/typeschema";
export let schema: TSchema;
export let data: InferIn<TSchema>;
export let validatedData: Infer<TSchema> | undefined = undefined;
export let errors = new SvelteMap<string, string>();
$: checkErrors(data);
function checkErrors(data: InferIn<TSchema>) {
errors = errors.clear();
validate(schema, data).then((result) => {
if ("issues" in result) {
result.issues.forEach((issue) => {
if (issue.path) errors = errors.set(issue.path.join("."), issue.message);
});
}
if ("data" in result) {
validatedData = result.data;
}
});
}
</script>
What am I doing wrong?
For example, you cannot use this library with Zod 3.21.4, only 3.22.2. It would be great if the README table of supported validation libraries included the supported versions.
@decs Hi, cool project :)
Hey, had just noticed TB got added to the supported list (which is awesome, thanks!). Was just looking over the implementation of the TB compiler though, and note there might be a couple of optimizations you could implement there to help ensure good validation performance. Was looking specifically at the following lines https://github.com/decs/typeschema/blob/main/src/adapters/typebox.ts#L36-L48
validate: async data => {
const {TypeCompiler} = await import('@sinclair/typebox/compiler');
const result = TypeCompiler.Compile(schema); // quite slow per validation pass
if (result.Check(data)) {
return {data};
}
return {
issues: [...result.Errors(data)].map(
({message, path}) => new ValidationIssue(message, [path]),
),
};
},
Two options for optimization below.
You can use the Value
module to perform dynamic type checking against a value. This isn't as fast as the compiled version, but would be faster than compiling the schematics each time .validate(data)
is run. The Value.Check
performance is roughly inline with Zod.
validate: async data => {
const {Value} = await import('@sinclair/typebox/value');
if (Value.Check(schema, data)) {
return {data};
}
return {
issues: [...Value.Errors(schema, data)].map(
({message, path}) => new ValidationIssue(message, [path]),
),
};
},
Another approach you can maybe try is to cache the compiled result
by schema reference. This caching pattern is an alternative to closure caching (where you have the outer closure compile, and return a validate function), but can be used to retain previously compiled compilation and only incurs a cost of a single Map
lookup.
const cache = new Map<TSchema, TypeCheck<any>>() // caches by reference
// ...
validate: async data => {
const {TypeCompiler} = await import('@sinclair/typebox/compiler');
const result = cache.has(schema) ? cache.get(schema)! : (function() {
const typecheck = TypeCompiler.Compile(schema);
cache.set(schema, typecheck) // cache for next time.
return typecheck
}())
if (result.Check(data)) {
return {data};
}
return {
issues: [...result.Errors(data)].map(
({message, path}) => new ValidationIssue(message, [path]),
),
};
},
TypeBox supports both dynamic and compiled type checking. The decision to use one over the other mostly comes down to whether or not the environment is supportive of JIT compilation. For validation that runs everywhere, use Value.Check
, for high performance where you have control over the environment, use the TypeCompiler
.
Hope this helps, and great work. This project looks cool
Cheers
S
Assert is only throwing the first issue atm.
I propose either target ES2021 to have AggregateError
available. Or create your own to allow all errors to be reported :
export async function assert<TSchema extends RegistryBaseSchema>(
schema: TSchema,
data: unknown,
): Promise<InferOutput<TSchema>> {
const result = await validate(schema, data);
if ('issues' in result) {
throw new AggregateError(result.issues);
}
return result.data;
}
Valibot seems like a good addition to typeschema. It has a simple adapter, mentioned here: fabian-hiller/valibot#8
Hi, a user of next-safe-action reported a bug with the edge runtime, where the action execution breaks with this error when client input(s) get validated and parsed:
TypeError [ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING]: A dynamic import callback was not specified.
I created a repo with a minimal reproduction of the issue, which appears to be directly related to TypeSchema validate
function.
Now, apparently Vercel is abandoning edge rendering and I don't know if this "bug" is easily fixable, so please let me know if I can do something to help solving this one, if possible.
Here's the original issue opened in next-safe-action repo:
Thanks!
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.