New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@travetto/schema

Package Overview
Dependencies
Maintainers
1
Versions
326
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@travetto/schema - npm Package Compare versions

Comparing version 0.1.8 to 0.2.0

e2e/index.js

15

package.json

@@ -7,11 +7,14 @@ {

"dependencies": {
"@travetto/config": "^0.1.4",
"@travetto/registry": "^0.1.5",
"@types/faker": "^4.1.2",
"faker": "^4.1.0"
"@travetto/config": "^0.2.0",
"@travetto/registry": "^0.2.0"
},
"description": "Data type registry for runtime validation, reflection and binding. ",
"devDependencies": {
"@travetto/test": "^0.1.6"
"@travetto/test": "^0.2.0"
},
"optionalExtensionDependencies": {
"@travetto/express": "^0.1.11",
"@types/faker": "^4.1.2",
"faker": "^4.1.0"
},
"homepage": "https://travetto.io",

@@ -36,3 +39,3 @@ "keywords": [

},
"version": "0.1.8"
"version": "0.2.0"
}

59

README.md

@@ -7,4 +7,12 @@ travetto: Schema

## Registration
The registry's schema information is defined by `typescript` AST and only applies to classes registered as `@Schema`s. The module utilizes AST transformations to collect schema information, and facilitate the registration process without user intervention.
The registry's schema information is defined by `typescript` AST and only applies to classes registered with the `@Schema` decoration.
### Classes
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 schema
* `description` - detailed description of the schema
* `examples` - A set of examples as JSON or YAML
The `title` will be picked up from the [`JSDoc`](http://usejsdoc.org/about-getting-started.html) comments, and additionally all fields can be set using the [`@Describe`](./src/decorator/common) decorator.
```typescript

@@ -38,3 +46,4 @@ @Schema()

This 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:
### Fields
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:

@@ -59,2 +68,8 @@ * `@Field` defines a field that will be serialized, generally used in conjunction with ```@Schema(false)``` which disables the auto registration.

Just like the class, all fields can be defined with
* `description` - detailed description of the schema
* `examples` - A set of examples as JSON or YAML
And similarly, the `description` will be picked up from the [`JSDoc`](http://usejsdoc.org/about-getting-started.html) comments, and additionally all fields can be set using the [`@Describe`](./src/decorator/common) decorator.
## Binding/Validation

@@ -158,2 +173,40 @@ At runtime, once a schema is registered, a programmer can utilize this structure to perform specific operations. Specifically binding and validation.

message: address.street2 is a required field
```
```
## Extensions
Integration with other modules can be supported by extensions. The dependencies are `optionalExtensionDependencies` and must be installed directly if you want to use them:
### Express
The module provides high level access for [`Express`](https://github.com/travetto/express) support, via decorators, for validating and typing request bodies.
## Decorators
`@SchemaBody` provides the ability to convert the inbound request body into a schema bound object, and provide validation before the controller even receives the request.
```typescript
class User {
name: string;
age: number;
}
...
@Post('/saveUser')
@SchemaBody(User)
async save(req: TypedBody<User>) {
const user = await this.service.update(req.body);
return { success : true };
}
...
```
`@SchemaQuery` provides the ability to convert the inbound request query into a schema bound object, and provide validation before the controller even receives the request.
```typescript
class SearchParams {
page: number = 0;
pageSize: number = 100;
}
...
@Get('/search')
@SchemaQuery(SearchParams)
async search(req: TypedQuery<SearchParams>) {
return await this.service.search(req.query);
}
...
```

@@ -1,2 +0,3 @@

import { CommonRegExp, SchemaRegistry, ClassList, ValidatorFn } from '../service';
import { CommonRegExp, SchemaRegistry } from '../service';
import { ClassList, FieldConfig } from '../types';

@@ -16,3 +17,3 @@ function prop(obj: { [key: string]: any }) {

}
export function Field(type: ClassList, config?: { [key: string]: any }) {
export function Field(type: ClassList, config?: Partial<FieldConfig>) {
return (f: any, p: string) => {

@@ -26,3 +27,3 @@ SchemaRegistry.registerPendingFieldConfig(f.constructor, p, type);

export const Alias = (...aliases: string[]) => prop({ aliases });
export const Required = (message?: string) => prop({ required: { message } });
export const Required = (message?: string) => prop({ required: { active: true, message } });
export const Enum = (vals: string[] | any, message?: string) => {

@@ -29,0 +30,0 @@ const values = enumKeys(vals);

export * from './field';
export * from './schema';
export * from './schema';
export * from './common';
import { Class } from '@travetto/registry';
import { SchemaRegistry, ValidatorFn } from '../service';
import { BindUtil } from '../util';
import { SchemaRegistry } from '../service';
import { ValidatorFn } from '../types';

@@ -5,0 +5,0 @@ export interface ClassWithSchema<T> extends Class<T> {

@@ -6,1 +6,2 @@ /// <reference path="typings.d.ts" />

export * from './util';
export * from './types';
import { ChangeEvent, Class } from '@travetto/registry';
import { FieldConfig, DEFAULT_VIEW, ClassConfig } from './types';
import { FieldConfig, DEFAULT_VIEW, ClassConfig } from '../types';
import { EventEmitter } from 'events';

@@ -4,0 +4,0 @@

@@ -1,4 +0,3 @@

export * from './types';
export * from './validator';
export * from './registry';
export * from './changes';
import { MetadataRegistry, RootRegistry, Class, ChangeEvent } from '@travetto/registry';
import { Env } from '@travetto/base';
import { ClassList, FieldConfig, ClassConfig, ViewConfig, DEFAULT_VIEW } from './types';
import { ClassList, FieldConfig, ClassConfig, DEFAULT_VIEW } from '../types';
import {

@@ -128,2 +128,3 @@ SchemaChangeListener,

}
dest.title = src.title || dest.title;
dest.validators = [...src.validators, ...dest.validators];

@@ -130,0 +131,0 @@ return dest;

import { Class } from '@travetto/registry';
import { BaseError } from '@travetto/base';
import { FieldConfig, SchemaConfig } from '../types';
import { FieldConfig, SchemaConfig } from '../../types';
import { SchemaRegistry } from '../registry';

@@ -29,4 +29,4 @@ import { Messages } from './messages';

if (!hasValue) {
if (fieldSchema.required) {
errors.push(...this.prepareErrors(path, [{ kind: 'required' }]));
if (fieldSchema.required && fieldSchema.required.active) {
errors.push(...this.prepareErrors(path, [{ kind: 'required', ...fieldSchema.required }]));
}

@@ -33,0 +33,0 @@ continue;

import { Class } from '@travetto/registry';
import { SchemaRegistry, FieldConfig, DEFAULT_VIEW } from '../service';
import { SchemaRegistry } from '../service';
import { FieldConfig, DEFAULT_VIEW } from '../types';

@@ -4,0 +5,0 @@ export class BindUtil {

@@ -1,3 +0,2 @@

import * as ts from 'typescript';
import { TransformUtil, State } from '@travetto/compiler';
import { TransformUtil, TransformerState } from '@travetto/compiler';
import { ConfigLoader } from '@travetto/config';

@@ -10,64 +9,12 @@

interface AutoState extends State {
interface AutoState extends TransformerState {
inAuto: boolean;
addField: ts.Expression | undefined;
addSchema: ts.Expression | undefined;
}
function resolveType(type: ts.Node, state: State): ts.Expression {
let expr: ts.Expression | undefined;
const kind = type && type!.kind;
function computeProperty(state: AutoState, node: ts.PropertyDeclaration) {
switch (kind) {
case ts.SyntaxKind.TypeReference:
expr = TransformUtil.importIfExternal(type as ts.TypeReferenceNode, state);
break;
case ts.SyntaxKind.LiteralType: expr = resolveType((type as any as ts.LiteralTypeNode).literal, state); break;
case ts.SyntaxKind.StringLiteral:
case ts.SyntaxKind.StringKeyword: expr = ts.createIdentifier('String'); break;
case ts.SyntaxKind.NumericLiteral:
case ts.SyntaxKind.NumberKeyword: expr = ts.createIdentifier('Number'); break;
case ts.SyntaxKind.TrueKeyword:
case ts.SyntaxKind.FalseKeyword:
case ts.SyntaxKind.BooleanKeyword: expr = ts.createIdentifier('Boolean'); break;
case ts.SyntaxKind.ArrayType:
expr = ts.createArrayLiteral([resolveType((type as ts.ArrayTypeNode).elementType, state)]);
break;
case ts.SyntaxKind.TypeLiteral:
const properties: ts.PropertyAssignment[] = [];
for (const member of (type as ts.TypeLiteralNode).members) {
let subMember: ts.TypeNode = (member as any).type;
if ((subMember as any).literal) {
subMember = (subMember as any).literal;
}
properties.push(ts.createPropertyAssignment(member.name as ts.Identifier, resolveType(subMember, state)));
}
expr = ts.createObjectLiteral(properties);
break;
case ts.SyntaxKind.UnionType: {
const types = (type as ts.UnionTypeNode).types;
expr = types.slice(1).reduce((fType, stype) => {
const fTypeStr = (fType as any).text;
if (fTypeStr !== 'Object') {
const resolved = resolveType(stype, state);
if ((resolved as any).text !== fTypeStr) {
fType = ts.createIdentifier('Object');
}
}
return fType;
}, resolveType(types[0], state));
break;
}
case ts.SyntaxKind.ObjectKeyword:
default:
break;
}
return expr || ts.createIdentifier('Object');
}
function computeProperty(node: ts.PropertyDeclaration, state: AutoState) {
const typeExpr = resolveType(node.type!, state);
const typeExpr = TransformUtil.resolveType(state, node.type!);
const properties = [];
if (!node.questionToken) {
properties.push(ts.createPropertyAssignment('required', TransformUtil.fromLiteral({})));
properties.push(ts.createPropertyAssignment('required', TransformUtil.fromLiteral({ active: true })));
}

@@ -93,17 +40,14 @@

if (!state.addField) {
const ident = TransformUtil.generateUniqueId(`import_Field`, state);
state.addField = ts.createPropertyAccess(ident, 'Field');
state.newImports.push({
path: require.resolve('../src/decorator/field'),
ident
});
const dec = TransformUtil.createDecorator(state, require.resolve('../src/decorator/field'), 'Field', ...params);
const newDecs = [dec, ...(node.decorators || [])];
const comments = TransformUtil.describeByComments(state, node);
if (comments.description) {
newDecs.push(TransformUtil.createDecorator(state, require.resolve('../src/decorator/common'), 'Describe', TransformUtil.fromLiteral({
description: comments.description
})));
}
const dec = ts.createDecorator(ts.createCall(state.addField as any, undefined, ts.createNodeArray(params)));
const decls = ts.createNodeArray([
dec, ...(node.decorators || [])
]);
const res = ts.updateProperty(node,
decls,
ts.createNodeArray(newDecs),
node.modifiers,

@@ -122,7 +66,7 @@ node.name,

if (ts.isClassDeclaration(node)) {
const anySchema = TransformUtil.findAnyDecorator(node, SCHEMAS, state);
const anySchema = TransformUtil.findAnyDecorator(state, node, SCHEMAS);
const schema = TransformUtil.findAnyDecorator(node, {
const schema = TransformUtil.findAnyDecorator(state, node, {
Schema: new Set(['@travetto/schema'])
}, state);
});

@@ -150,17 +94,14 @@ let auto = !!anySchema;

const ret = node as any as ts.ClassDeclaration;
let decls = node.decorators;
const decls = [...(node.decorators || [])];
const comments = TransformUtil.describeByComments(state, node);
if (!schema) {
if (!state.addSchema) {
const ident = TransformUtil.generateUniqueId(`import_Schema`, state);
state.newImports.push({
path: require.resolve('../src/decorator/schema'),
ident
});
state.addSchema = ts.createPropertyAccess(ident, 'Schema');
}
decls.unshift(TransformUtil.createDecorator(state, require.resolve('../src/decorator/schema'), 'Schema'));
}
decls = ts.createNodeArray([
ts.createDecorator(ts.createCall(state.addSchema, undefined, ts.createNodeArray([]))),
...(decls || [])
]);
if (comments.description) {
decls.push(TransformUtil.createDecorator(state, require.resolve('../src/decorator/common'), 'Describe', TransformUtil.fromLiteral({
title: comments.description
})));
}

@@ -170,3 +111,3 @@

ret,
decls,
ts.createNodeArray(decls),
ret.modifiers,

@@ -194,5 +135,5 @@ ret.name,

if (state.inAuto) {
const ignore = TransformUtil.findAnyDecorator(node, { Ignore: new Set(['@travetto/schema']) }, state);
const ignore = TransformUtil.findAnyDecorator(state, node, { Ignore: new Set(['@travetto/schema']) });
if (!ignore) {
return computeProperty(node, state) as any as T;
return computeProperty(state, node) as any as T;
}

@@ -208,6 +149,5 @@ }

transformer: TransformUtil.importingVisitor<AutoState>(() => ({
inAuto: false,
addField: undefined
inAuto: false
}), visitNode),
phase: 'before'
};

@@ -1,2 +0,2 @@

import { Schema, Field, View, Required } from '../index';
import { Schema, Field, Required } from '../index';

@@ -7,3 +7,2 @@ @Schema()

@Field(String)
@View('test')
@Required()

@@ -10,0 +9,0 @@ street1: string;

@@ -19,3 +19,2 @@ import {

@Field(String)
@View('test')
area: string;

@@ -105,6 +104,6 @@

assert(viewPerson.address.street1 === '1234 Fun');
assert(viewPerson.address.street2 === undefined);
assert(viewPerson.address.street2 === 'Unit 20');
assert(viewPerson.counts.length === 2);
assert(viewPerson.counts[0] instanceof Count);
assert(viewPerson.counts[0].value === undefined);
assert(viewPerson.counts[0].value === 20);
}

@@ -111,0 +110,0 @@

import { Suite, Test, BeforeAll } from '@travetto/test';
import { Schema, SchemaRegistry } from '../src';
import { GenerateUtil } from '../support/util.generate';
import { GenerateUtil } from '../extension/faker';

@@ -6,0 +6,0 @@ import * as assert from 'assert';

@@ -10,3 +10,3 @@ import { Suite, Test } from '@travetto/test';

telephone() {
assert(CommonRegExp.telephone.test('555-555-5555'));
assert(CommonRegExp.telephone.test('555-555-5545'));
assert(CommonRegExp.telephone.test('5555555555'));

@@ -13,0 +13,0 @@

{
"compilerOptions": {
"module": "commonjs",
"target": "es2017",
"target": "es2018",
"strict": true,

@@ -6,0 +6,0 @@ "noStrictGenericChecks": true,

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc