Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
099b8c5
feat: 🎸 add initial JSON Type specification
streamich Jul 26, 2025
785a6df
feat: 🎸 add convenience methods for setting type fields
streamich Jul 26, 2025
aad30ed
feat: 🎸 narrow inferred constant types
streamich Jul 26, 2025
251a048
feat: 🎸 export values from the package
streamich Jul 26, 2025
fdb0c7f
feat: 🎸 add infer<T> type
streamich Jul 26, 2025
d6e1d2f
feat: 🎸 add ability to specify default value
streamich Jul 26, 2025
b97a617
feat: 🎸 add .prop() method to object types
streamich Jul 26, 2025
fffda14
feat: 🎸 add .opt() helper for optional fields
streamich Jul 26, 2025
60853b2
feat: 🎸 start lowercase node type builder implementation
streamich Jul 27, 2025
9a315be
Merge remote-tracking branch 'origin/master' into api-improvements
streamich Jul 27, 2025
b356487
chore: 🤖 fix imports after merge
streamich Jul 27, 2025
5126611
feat: 🎸 improve type builder
streamich Jul 27, 2025
1bd30cf
feat: 🎸 add infer<T> type on the default schema builder s
streamich Jul 27, 2025
4f208f8
feat: 🎸 add t.maybe() shorthand
streamich Jul 27, 2025
8ca7c1a
fix: 🐛 improve object member check in validation codegen
streamich Jul 27, 2025
c1289d5
feat: 🎸 introduce t.enum() shortcut
streamich Jul 27, 2025
d9784b8
fix: 🐛 correctly check for field existance
streamich Jul 27, 2025
14a9410
Merge remote-tracking branch 'origin/master' into api-improvements
streamich Jul 27, 2025
c7adf7a
chore: 🤖 remove unused import
streamich Jul 27, 2025
7da41db
Merge remote-tracking branch 'origin/master' into api-improvements
streamich Jul 27, 2025
c08750e
fix: 🐛 pass all tests
streamich Jul 27, 2025
9ed3743
feat: 🎸 add function req/res builder helpers
streamich Jul 27, 2025
de8439f
feat: 🎸 add .con() alias
streamich Jul 27, 2025
cca81c1
test: 💍 add funciton validation tests
streamich Jul 27, 2025
2eb08ca
style: 💄 fix linter issues
streamich Jul 27, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
718 changes: 718 additions & 0 deletions SPECIFICATION.md

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,4 @@ export * from './constants';
export * from './schema';
export * from './type';
export * from './system';
export * from './value';
12 changes: 8 additions & 4 deletions src/random/generators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,14 @@ export const num = (type: NumberType): number => {
let min = Number.MIN_SAFE_INTEGER;
let max = Number.MAX_SAFE_INTEGER;
const schema = type.getSchema();
if (schema.gt !== undefined) min = schema.gt;
if (schema.gte !== undefined) min = schema.gte + 0.000000000000001;
if (schema.lt !== undefined) max = schema.lt;
if (schema.lte !== undefined) max = schema.lte - 0.000000000000001;
const {lt, lte, gt, gte} = schema;
if (gt !== undefined) min = gt;
if (gte !== undefined)
if (gte === lte) return gte;
else min = gte + 0.000000000000001;
if (lt !== undefined) max = lt;
if (lte !== undefined) max = lte - 0.000000000000001;
if (min >= max) return max;
if (schema.format) {
switch (schema.format) {
case 'i8':
Expand Down
3 changes: 2 additions & 1 deletion src/schema/SchemaBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import type {
FunctionSchema,
FunctionStreamingSchema,
TType,
Narrow,
} from '.';

export class SchemaBuilder {
Expand Down Expand Up @@ -127,7 +128,7 @@ export class SchemaBuilder {
* ```
*/
public Const<V>(
value: V,
value: Narrow<V>,
options?: Optional<ConstSchema<V>>,
): ConstSchema<
string extends V ? never : number extends V ? never : boolean extends V ? never : any[] extends V ? never : V
Expand Down
13 changes: 12 additions & 1 deletion src/schema/__tests__/SchemaBuilder.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {s} from '..';
import {type ConstSchema, s} from '..';

describe('string', () => {
test('can create a string type', () => {
Expand Down Expand Up @@ -79,3 +79,14 @@ describe('or', () => {
});
});
});

describe('const', () => {
test('can create an "const" type', () => {
const type = s.Const('Hello');
const type2: ConstSchema<'Hello'> = type;
expect(type2).toEqual({
kind: 'const',
value: 'Hello',
});
});
});
7 changes: 6 additions & 1 deletion src/schema/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import type {TypeOf} from './schema';
import {SchemaBuilder} from './SchemaBuilder';

export * from './common';
export * from './schema';

/**
* JSON Type AST builder.
* JSON Type default AST builder.
*/
export const s = new SchemaBuilder();

export namespace s {
export type infer<T> = TypeOf<T>;
}
131 changes: 129 additions & 2 deletions src/schema/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ export interface TType<Value = unknown> extends Display, Partial<Identifiable> {
*/
meta?: Record<string, unknown>;

/**
* Default value for this type. This may be used when the value is not provided
* during validation or serialization. The default value should match the
* type of this schema node.
*/
default?: Value;

/**
* List of example usages of this type.
*/
Expand Down Expand Up @@ -52,6 +59,17 @@ export interface WithValidator {

/**
* Represents something of which type is not known.
*
* Example:
*
* ```json
* {
* "kind": "any",
* "metadata": {
* "description": "Any type"
* }
* }
* ```
*/
export interface AnySchema extends TType<unknown>, WithValidator {
kind: 'any';
Expand All @@ -65,13 +83,35 @@ export interface AnySchema extends TType<unknown>, WithValidator {

/**
* Represents a JSON boolean.
*
* Example:
*
* ```json
* {
* "kind": "bool",
* "meta": {
* "description": "A boolean value"
* }
* }
* ```
*/
export interface BooleanSchema extends TType<boolean>, WithValidator {
kind: 'bool';
}

/**
* Represents a JSON number.
*
* Example:
*
* ```json
* {
* "kind": "num",
* "format": "i32",
* "gte": 0,
* "lte": 100
* }
* ```
*/
export interface NumberSchema extends TType<number>, WithValidator {
kind: 'num';
Expand Down Expand Up @@ -112,6 +152,17 @@ export interface NumberSchema extends TType<number>, WithValidator {

/**
* Represents a JSON string.
*
* Example:
*
* ```json
* {
* "kind": "str",
* "format": "utf8",
* "min": 1,
* "max": 255
* }
* ```
*/
export interface StringSchema extends TType<string>, WithValidator {
kind: 'str';
Expand Down Expand Up @@ -150,6 +201,20 @@ export interface StringSchema extends TType<string>, WithValidator {

/**
* Represents a binary type.
*
* Example:
*
* ```json
* {
* "kind": "bin",
* "type": {
* "kind": "str"
* },
* "format": "json",
* "min": 10,
* "max": 1024
* }
* ```
*/
export interface BinarySchema<T extends TType = any> extends TType, WithValidator {
kind: 'bin';
Expand All @@ -169,6 +234,19 @@ export interface BinarySchema<T extends TType = any> extends TType, WithValidato

/**
* Represents a JSON array.
*
* Example:
*
* ```json
* {
* "kind": "arr",
* "type": {
* "kind": "num"
* },
* "min": 1,
* "max": 10
* }
* ```
*/
export interface ArraySchema<T extends TType = any> extends TType<Array<unknown>>, WithValidator {
kind: 'arr';
Expand All @@ -182,6 +260,13 @@ export interface ArraySchema<T extends TType = any> extends TType<Array<unknown>

/**
* Represents a constant value.
* Example:
* ```json
* {
* "kind": "const",
* "value": 42
* }
* ```
*/
export interface ConstSchema<V = any> extends TType, WithValidator {
/** @todo Rename to "con". */
Expand All @@ -202,6 +287,32 @@ export interface TupleSchema<T extends TType[] = any> extends TType, WithValidat
/**
* Represents a JSON object type, the "object" type excluding "null" in JavaScript,
* the "object" type in JSON Schema, and the "obj" type in MessagePack.
* Example:
* ```json
* {
* "kind": "obj",
* "fields": [
* {
* "kind": "field",
* "key": "name",
* "type": {
* "kind": "str"
* },
* "optional": false
* },
* {
* "kind": "field",
* "key": "age",
* "type": {
* "kind": "num",
* "gte": 0
* },
* "optional": true
* }
* ],
* "unknownFields": false
* }
* ```
*/
export interface ObjectSchema<
Fields extends ObjectFieldSchema<string, TType>[] | readonly ObjectFieldSchema<string, TType>[] = any,
Expand Down Expand Up @@ -246,8 +357,14 @@ export interface ObjectFieldSchema<K extends string = string, V extends TType =
kind: 'field';
/** Key name of the field. */
key: K;
/** One or more "one-of" types of the field. */

/**
* One or more "one-of" types of the field.
*
* @todo Rename to `val`.
*/
type: V;

optional?: boolean;
}

Expand All @@ -262,7 +379,11 @@ export interface ObjectOptionalFieldSchema<K extends string = string, V extends
*/
export interface MapSchema<T extends TType = any> extends TType<Record<string, unknown>>, WithValidator {
kind: 'map';
/** Type of all values in the map. */
/**
* Type of all values in the map.
*
* @todo Rename to `val`. And add `key` field for the key type. Make `key` default to `str`.
*/
type: T;
}

Expand Down Expand Up @@ -299,6 +420,7 @@ export interface FunctionSchema<Req extends TType = TType, Res extends TType = T
export type FunctionStreamingValue<Req, Res, Ctx = unknown> = (req: Observable<Req>, ctx?: Ctx) => Observable<Res>;

export interface FunctionStreamingSchema<Req extends TType = TType, Res extends TType = TType> extends TType {
/** @todo Rename to `fn`. Make it a property on the schema instead. */
kind: 'fn$';
req: Req;
res: Res;
Expand Down Expand Up @@ -396,3 +518,8 @@ export type OptionalProps<T extends object> = Exclude<

export type Optional<T extends object> = Pick<T, OptionalProps<T>>;
export type Required<T extends object> = Omit<T, OptionalProps<T>>;

export type Narrow<T> =
| (T extends infer U ? U : never)
| Extract<T, number | string | boolean | bigint | symbol | null | undefined | []>
| ([T] extends [[]] ? [] : {[K in keyof T]: Narrow<T[K]>});
2 changes: 2 additions & 0 deletions src/system/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,5 @@ export type ResolveType<T> = T extends TypeAlias<any, infer T>
: T extends Schema
? TypeOf<T>
: never;

export type infer<T> = ResolveType<T>;
Loading