unjs / untyped

Generate types and markdown from a config object.
https://untyped.unjs.io/
MIT License
386 stars 19 forks source link

rewamp untyped into a runtime-friendly lib #101

Open pi0 opened 11 months ago

pi0 commented 11 months ago

Background

Back in time I was developing the untyped library, the idea was to make a unified (build-time) solution to merge type and runtime validation from one source with little or no extra code and use JSON Schema as a unified schema format mainly for handling nuxt config schema.

Currently untyped:

However, it has some limitations:

Future Vision

Untyped should:

At this point, I am not sure how to make the perfect solution to meet the above criteria. I might also end-up with making a new library if not fitting for one lib. Share the main vision and ideas publicly to gather ideas and myself taking notes.

Similar efforts

Recently I have found out about the (amazing) typeschema project which tries to almost do the same of unifying schema validation libraries. I love it but it has some implementation drawbacks that are not fully tree-shakable (it is fixable with some major changes) but also is not itself trying to make a standard schema nor provide build-time utils like currently untyped does.

There is also JsonTypeDef Standard (RFC) which could be a nice replacement for JSON Schema as a current source of trust.

Schema Validation Libs

Zod

Joi

Yup

io-ts

Runtypes

Valibot

AJV

Superstruct

OW

Typebox

Typia

Deepkit/type

Arktype

pi0 commented 11 months ago

Notes from @decs (author of typeschema): (https://github.com/unjs/h3/pull/431#issuecomment-1650221199)

we started with a simple logic, inspired by tRPC, but quickly noticed that it wouldn't support other libraries where the validation function lives outside of the schema object (like io-ts, ajv). so we pivoted to the adapter + optional peer dependencies model, and now we're on track to support all major libs this week. and indeed there's a tradeoff: the architecture change increased the LOC.

I can think of 2 alternatives to adding typeschema as a direct dependency, if the LOC is a concern:

  1. making it an optional peer dependency. try dynamically importing typeschema (falling back to no schema validation if not installed) and instructing users to install it on their own
  2. or supporting custom validators with this format: <T>(data: unknown): Promise<T> | T, then asking users to independently install typeschema and use createAssert(schema) (which generates a function on this format)
stafyniaksacha commented 11 months ago

Another option from typeschema (which I realy like) is to take inspiration from vee-validate vue validation library which support yup and zod out of the box by using abstraction and adapters package like @vee-validate/yup and @vee-validate/zod.

https://vee-validate.logaretm.com/v4/guide/composition-api/getting-started/#form-schema

The benefit would be to keep minimum dependencies, but would need to create and maintain each package.

The caveat I see with typeschema is that it has a lot of peerDependencies which can grow dependencies (recently pnpm has toggled auto-install-peers to true by default https://pnpm.io/next/npmrc#auto-install-peers)

decs commented 11 months ago

The peer dependencies on typeschema are all optional, just to ensure that you're using a version that's compatible with the adapter. Installing typeschema shouldn't install any peer dependencies.

I looked at the pnpm option you mentioned and it should skip optional peer dependencies as expected.

auto-install-peers

Default: true Type: Boolean When true, any missing non-optional peer dependencies are automatically installed.

I still haven't set my mind on one package vs multiple packages (one per adapter). But I currently lean towards one package to avoid users from having to manually install every adapter they care about (hopefully all). Though it's growing on me the idea of having both: scoped packages for each adapter and one main package will all automatically installed.