Accept Standard Schema in defineTrait#205
Accept Standard Schema in defineTrait#205JohannesKlauss wants to merge 9 commits intopmndrs:mainfrom
Conversation
|
An exciting Christmas present. 👀 I’m sure it has opened up a lot of questions and I will start reviewing it before the New Year. |
|
Okay, I took a closer look. As you say, the standard schema does not define a representation of the schema, only validation and metadata, making it impossible for us to generically create an AoS store. I think this is a blocker, since cache locality is motivating factor for data-oriented design 😅. So this appears to leave us with two options:
(2) might not require packaging anything from the library. The standard schema requires that the vendor adds metadata identifying itself and then the types can be copied in or a dev dependency. |
|
I don't have a good feeling about the second approach, since this creates a lot of maintenance overhead. Supporting every library that implements the Standard schema is a lot of work. |
|
We could support generic SS libraries with AoS storage and then provide an SoA path for particular libraries like Zod. But this also makes it complex to understand from reading the code how the data will be represented. I really wish there was a standard schema representation. |
|
Hmh... this sounds quite complex and as you said, it complicates the user experience with some being SoA others AoS. My recommendation would be to confine standard schema usage to SoAs. Using a standard schema library gives you a performance penalty anyway because of the validation when setting values, so users won't use use this on performance critical traits. But it's your library and your call :) |
|
I am going to think about this. It is not quite what I expected which it only being a validator with no schema introspection. |
This closes #195
Every validation library that implements Standard Schema works (https://standardschema.dev/schema#what-schema-libraries-implement-the-spec).
Caveats
No nested arrays/objects
As far as I can see, enforcing the "no nested objects/arrays rule" is only possible when initializing a defined trait. I'd love to have this check at defineTrait, but Standard Schema does not specify how an implementation should represent the schema during runtime. So different validation libraries do it differently. Valibot for example has a
entrieskey on the validator object that holds the shape, but zod does not. This makes it difficult to come up with a general approach to enforce this rule during trait definition. So I moved that to the initialization phase.This will introduce a runtime penalty when initializing traits that have been defined with a schema.
Validation occurs on .set and direct field mutation
I modified the accessor functions so that they check for a validator. If present, the resulting mutation will be validated and throw if the updated values don't pass the schema.
I'm not sure if throwing here is a good or a bad thing, maybe @krispya can chime in here. Instead od throwing, we could just log a warning with the failed validation and and values and discard the changes.
No async validation
To keep everything synchronous I left out async validation. In theory Standard Schema allows for
.validateto return a Promise. I haven't really seen this being used in the wild a lot and it would quite complicate a lot of things in koota, so I explicitly forbid async validation.In compliance with the standard schema FAQ and to not introduce depencendies I copied the interface of the Standard Schema into the code base.
If something is off or should be improved, please let me know 🙂. Happy Holidays.