![Oracle Drags Its Feet in the JavaScript Trademark Dispute](https://cdn.sanity.io/images/cgdhsj6q/production/919c3b22c24f93884c548d60cbb338e819ff2435-1024x1024.webp?w=400&fit=max&auto=format)
Security News
Oracle Drags Its Feet in the JavaScript Trademark Dispute
Oracle seeks to dismiss fraud claims in the JavaScript trademark dispute, delaying the case and avoiding questions about its right to the name.
@travetto/schema
Advanced tools
Install: @travetto/schema
npm install @travetto/schema
# or
yarn add @travetto/schema
This module's purpose is to allow for proper declaration and validation of data types, in the course of running a program. The framework defined here, is leveraged in the Configuration, Application, RESTful API, OpenAPI Specification and Data Modeling Support modules. The schema is the backbone of all data transfer, as it helps to provide validation on correctness of input, whether it is a rest request, command line inputs, or a configuration file.
This module provides a mechanism for registering classes and field level information as well the ability to apply that information at runtime.
The registry's schema information is defined by Typescript AST and only applies to classes registered with the @Schema decoration.
The module utilizes AST transformations to collect schema information, and facilitate the registration process without user intervention. The class can also be described using providing a:
title
- definition of the schemadescription
- detailed description of the schemaexamples
- A set of examples as JSON or YAML
The title
will be picked up from the JSDoc comments, and additionally all fields can be set using the @Describe decorator.Code: Sample User Schema
import { Schema } from '@travetto/schema';
@Schema()
export class User {
name: string;
age: number;
favoriteFood?: 'pizza' | 'burrito' | 'salad';
}
From this schema, the registry would have the following information:
Config: User schemas as a YAML file
User:
fields:
-
name: name
type": string
required: true
-
name: age
type: number
required: true
-
name: favoriteFood
type: string
required: false
allowedValues: ["pizza", "burrito", "salad" ]
This schema provides a powerful base for data binding and validation at runtime. Additionally there may be types that cannot be detected, or some information that the programmer would like to override. Below are the supported field decorators:
Just like the class, all fields can be defined with
description
- detailed description of the schemaexamples
- A set of examples as JSON or YAML
And similarly, the description
will be picked up from the JSDoc comments, and additionally all fields can be set using the @Describe decorator.At runtime, once a schema is registered, a programmer can utilize this structure to perform specific operations. Specifically binding and validation.
Binding is a very simple operation, as it takes in a class registered as as @Schema and a JS object that will be the source of the binding. Given the schema:
Code: Sub Schemas via Address
import { Schema, Integer } from '@travetto/schema';
@Schema()
export class Address {
street1: string;
street2: string;
}
@Schema()
export class Person {
name: string;
@Integer() age: number;
address: Address;
}
A binding operation could look like:
Code: Binding from JSON to Schema
import { Person } from './person';
export function Test(): Person {
return Person.from({
name: 'Test',
age: 19.999978,
address: {
street1: '1234 Fun',
street2: 'Unit 20'
}
});
}
and the output would be a Person
instance with the following structure
Terminal: Sample data output after binding
$ trv main doc/person-output.ts
Person {
name: 'Test',
age: 19,
address: Address { street1: '1234 Fun', street2: 'Unit 20' }
}
Note: Binding will attempt to convert/coerce types as much as possible to honor the pattern of Javascript and it's dynamic nature.
Validation is very similar to binding, but instead of attempting to assign values, any mismatch or violation of the schema will result in errors. All errors will be collected and returned. Given the same schema as above,
Code: Sub Schemas via Address
import { Schema, Integer } from '@travetto/schema';
@Schema()
export class Address {
street1: string;
street2: string;
}
@Schema()
export class Person {
name: string;
@Integer() age: number;
address: Address;
}
But now with an invalid json object
Code: Read Person, and validate
import { SchemaValidator } from '@travetto/schema';
import { Person } from './person';
export async function validate(): Promise<void> {
const person = Person.from({
name: 'Test',
// @ts-expect-error
age: 'abc',
address: {
street1: '1234 Fun'
}
});
await SchemaValidator.validate(Person, person);
}
would produce an exception similar to following structure
Terminal: Sample error output
$ trv main doc/person-invalid-output.ts
Validation Failed {
"message": "Validation errors have occurred",
"category": "data",
"type": "ValidationResultError",
"at": "2029-03-14T04:00:00.618Z",
"errors": [
{
"kind": "type",
"message": "age is not a valid number",
"path": "age",
"type": "number"
},
{
"kind": "required",
"message": "address.street2 is required",
"path": "address.street2"
}
]
}
Within the schema framework, it is possible to add custom validators class level. This allows for more flexibility when dealing with specific situations (e.g. password requirements or ensuring two fields match)
Code: Password Validator
import { Schema, Validator, ValidationError } from '@travetto/schema';
const passwordValidator = (user: User): ValidationError | undefined => {
const p = user.password;
const hasNum = /\d/.test(p);
const hasSpecial = /[!@#$%%^&*()<>?/,.;':"']/.test(p);
const noRepeat = !/(.)(\1)/.test(p);
if (!hasNum || !hasSpecial || !noRepeat) {
return {
kind: 'password-rules',
path: 'password',
message: 'A password must include at least one number, one special char, and have no repeating characters'
};
}
};
@Schema()
@Validator(passwordValidator)
class User {
password: string;
}
When the validator is executed, it has access to the entire object, and you can check any of the values. The validator expects an object of a specific structure if you are looking to indicate an error has occurred.
Code: Validation Error Structure
export interface ValidationError {
/**
* The error message
*/
message: string;
/**
* The object path of the error
*/
path: string;
/**
* The kind of validation
*/
kind: ValidationKind;
/**
* The value provided
*/
value?: unknown;
/**
* Regular expression to match
*/
re?: string;
/**
* The type of the field
*/
type?: string;
}
When working with the schema, the basic types are easily understood, but some of Typescript's more complex constructs are too complex to automate cleanly.
To that end, the module supports two concepts:
This feature is meant to allow for simple Typescript types to be able to be backed by a proper class. This is because all of the typescript type information disappears at runtime, and so only concrete types (like classes) remain. An example of this, can be found with how the Data Model Querying module handles geo data.
Code: Simple Custom Type
import { DataUtil } from '@travetto/base';
/**
* @concrete .:PointImpl
*/
export type Point = [number, number];
const INVALID = Symbol.for('invalid-point');
export class PointImpl {
static validateSchema(input: unknown): 'type' | undefined {
const ret = this.bindSchema(input);
return ret !== INVALID && ret && !isNaN(ret[0]) && !isNaN(ret[1]) ? undefined : 'type';
}
static bindSchema(input: unknown): [number, number] | typeof INVALID | undefined {
if (Array.isArray(input) && input.length === 2) {
return input.map(x => DataUtil.coerceType(x, Number, false)) as [number, number];
} else {
return INVALID;
}
}
}
What you can see here is that the Point
type is now backed by a class that supports:
validateSchema
- Will run during validation for this specific type.bindSchema
- Will run during binding to ensure correct behavior.Code: Simple Custom Type Usage
import { Schema } from '@travetto/schema';
import { Point } from './custom-type';
@Schema()
export class LocationAware {
name: string;
point: Point;
}
All that happens now, is the type is exported, and the class above is able to properly handle point as an [x, y]
tuple. All standard binding and validation patterns are supported, and type enforcement will work as expected.
Terminal: Custom Type Validation
$ trv main doc/custom-type-output.ts
Validation Failed {
"message": "Validation errors have occurred",
"category": "data",
"type": "ValidationResultError",
"at": "2029-03-14T04:00:00.837Z",
"errors": [
{
"kind": "type",
"message": "point is not a valid PointImpl",
"path": "point",
"type": "PointImpl"
}
]
}
FAQs
Data type registry for runtime validation, reflection and binding.
The npm package @travetto/schema receives a total of 0 weekly downloads. As such, @travetto/schema popularity was classified as not popular.
We found that @travetto/schema demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Oracle seeks to dismiss fraud claims in the JavaScript trademark dispute, delaying the case and avoiding questions about its right to the name.
Security News
The Linux Foundation is warning open source developers that compliance with global sanctions is mandatory, highlighting legal risks and restrictions on contributions.
Security News
Maven Central now validates Sigstore signatures, making it easier for developers to verify the provenance of Java packages.