Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
9 changes: 9 additions & 0 deletions src/modules/__tests__/json-rx.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import {SchemaOf, t} from '../../type';
import {Message, NotificationMessage} from '../json-rx';

test('...', () => {
console.log(NotificationMessage + '');
type adasfsad = SchemaOf<typeof NotificationMessage.type>;
type asdf = t.infer<typeof NotificationMessage.type>;
// console.log(Message + '');
});
109 changes: 109 additions & 0 deletions src/modules/json-rx.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import {ModuleType} from "../type";

export const enum CompactMessageType {
RequestData = 0,
RequestComplete = 1,
RequestError = 2,
RequestUnsubscribe = 3,

ResponseData = 4,
ResponseComplete = 5,
ResponseError = 6,
ResponseUnsubscribe = 7,

Notification = 8,
}


export const system = new ModuleType();
const t = system.t;

// export const CompactMsgId = t.num.format('u32').alias('CompactMsgId');
const MethodName = t.str.min(1).max(32).format('ascii').alias('MethodName');

export const NotificationMessage = t.tuple(
t.Key('type', t.con(8)),
t.Key('method', t.Ref<typeof MethodName>('MethodName')),
t.KeyOpt('data', t.any),
).alias('NotificationMessage');

export const RequestDataMessage = t.Or(
t.tuple(
t.con(CompactMessageType.RequestData),
t.num.format('u32'),
t.Ref<typeof MethodName>('MethodName'),
),
t.tuple(
t.con(CompactMessageType.RequestData),
t.num.format('u32'),
t.Ref<typeof MethodName>('MethodName'),
t.any,
),
).options({
discriminator: ['?', ['eq', ['len', ['$', '']], 2], 0, 1],
}).alias('RequestDataMessage');

export const RequestCompleteMessage = t.Or(
t.tuple(
t.con(CompactMessageType.RequestComplete),
t.num.format('u32'),
t.Ref<typeof MethodName>('MethodName'),
),
t.tuple(
t.con(CompactMessageType.RequestComplete),
t.num.format('u32'),
t.Ref<typeof MethodName>('MethodName'),
t.any,
),
).options({
discriminator: ['?', ['eq', ['len', ['$', '']], 2], 0, 1],
}).alias('RequestCompleteMessage');

export const RequestErrorMessage = t.tuple(
t.con(CompactMessageType.RequestError),
t.num.format('u32'),
t.Ref<typeof MethodName>('MethodName'),
t.any,
).alias('RequestErrorMessage');

export const RequestUnsubscribeMessage = t.tuple(
t.con(CompactMessageType.RequestUnsubscribe),
t.num.format('u32'),
).alias('RequestUnsubscribeMessage');

export const ResponseDataMessage = t.tuple(
t.con(CompactMessageType.ResponseData),
t.num.format('u32'),
t.any,
).alias('ResponseDataMessage');

export const ResponseCompleteMessage = t.tuple(
t.con(CompactMessageType.ResponseComplete),
t.num.format('u32'),
t.any,
).alias('ResponseCompleteMessage');

export const ResponseErrorMessage = t.tuple(
t.con(CompactMessageType.ResponseError),
t.num.format('u32'),
t.any,
).alias('ResponseErrorMessage');

export const ResponseUnsubscribeMessage = t.tuple(
t.con(CompactMessageType.ResponseUnsubscribe),
t.num.format('u32'),
).alias('ResponseUnsubscribeMessage');

export const Message = t.Or(
NotificationMessage.type,
RequestDataMessage.type,
RequestCompleteMessage.type,
RequestErrorMessage.type,
RequestUnsubscribeMessage.type,
ResponseDataMessage.type,
ResponseCompleteMessage.type,
ResponseErrorMessage.type,
ResponseUnsubscribeMessage.type,
).alias('Message');

export const messageBatch = t.array(Message).alias('MessageBatch');
28 changes: 15 additions & 13 deletions src/schema/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -489,19 +489,21 @@ export type TypeOfValue<T> = T extends BoolSchema
]
: T extends ConSchema<infer U>
? U
: T extends KeySchema<infer K, infer V>
? TypeOf<V>
: T extends ObjSchema<infer F>
? NoEmptyInterface<TypeFields<Mutable<F>>>
: T extends MapSchema<infer U>
? Record<string, TypeOf<U>>
: T extends BinSchema
? Uint8Array
: T extends FnSchema<infer Req, infer Res, infer Ctx>
? (req: TypeOf<Req>, ctx: Ctx) => UndefToVoid<TypeOf<Res>> | Promise<UndefToVoid<TypeOf<Res>>>
: T extends FnRxSchema<infer Req, infer Res, infer Ctx>
? (req$: Observable<TypeOf<Req>>, ctx: Ctx) => Observable<UndefToVoid<TypeOf<Res>>>
: never;
: T extends OptKeySchema<infer K, infer V>
? (TypeOf<V> | undefined)
: T extends KeySchema<infer K, infer V>
? TypeOf<V>
: T extends ObjSchema<infer F>
? NoEmptyInterface<TypeFields<Mutable<F>>>
: T extends MapSchema<infer U>
? Record<string, TypeOf<U>>
: T extends BinSchema
? Uint8Array
: T extends FnSchema<infer Req, infer Res, infer Ctx>
? (req: TypeOf<Req>, ctx: Ctx) => UndefToVoid<TypeOf<Res>> | Promise<UndefToVoid<TypeOf<Res>>>
: T extends FnRxSchema<infer Req, infer Res, infer Ctx>
? (req$: Observable<TypeOf<Req>>, ctx: Ctx) => Observable<UndefToVoid<TypeOf<Res>>>
: never;

export type TypeOfMap<M extends Record<string, Schema>> = {
[K in keyof M]: TypeOf<M[K]>;
Expand Down
16 changes: 10 additions & 6 deletions src/type/TypeBuilder.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as schema from '../schema';
import * as classes from './classes';
import type {Type, TypeOfAlias} from './types';
import type {TypeRef, TypeRefToType, Type, TypeOfAlias} from './types';

const {s} = schema;

Expand Down Expand Up @@ -76,7 +76,7 @@ export class TypeBuilder {

// --------------------------------------------------------------- shorthands

public readonly or = <F extends Type[]>(...types: F) => this.Or(...types);
public readonly or = <F extends TypeRef<any>[]>(...types: F) => this.Or(...types);
public readonly undefined = () => this.undef;
public readonly null = () => this.nil;
public readonly boolean = () => this.bool;
Expand All @@ -94,7 +94,11 @@ export class TypeBuilder {
options,
);

public readonly tuple = <F extends Type[]>(...types: F) => this.Tuple(types);
public readonly tuple = <F extends TypeRef<any>[]>(...types: F) => this.Tuple(types);

protected typeRefToType = <T extends Type>(type: TypeRef<T>): TypeRefToType<T> => {
return typeof type === 'function' ? (type() as TypeRefToType<T>) : (type as TypeRefToType<T>);
};

/**
* Creates an object type with the specified properties. This is a shorthand for
Expand All @@ -117,10 +121,10 @@ export class TypeBuilder {
* @param record A mapping of property names to types.
* @returns An object type.
*/
public readonly object = <R extends Record<string, Type>>(record: R): classes.ObjType<RecordToFields<R>> => {
public readonly object = <R extends Record<string, TypeRef<any>>>(record: R): classes.ObjType<RecordToFields<{[K in keyof R]: TypeRefToType<R[K]>}>> => {
const keys: classes.KeyType<any, any>[] = [];
for (const [key, value] of Object.entries(record)) keys.push(this.Key(key, value));
return new classes.ObjType<RecordToFields<R>>(keys as any).sys(this.system);
for (const [key, value] of Object.entries(record)) keys.push(this.Key(key, this.typeRefToType(value)));
return new classes.ObjType<RecordToFields<{[K in keyof R]: TypeRefToType<R[K]>}>>(keys as any).sys(this.system);
};

/**
Expand Down
24 changes: 24 additions & 0 deletions src/type/__tests__/type-ref.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import {ModuleType} from '../classes/ModuleType';
import {ResolveType} from '../types';

test('...', () => {
const mod = new ModuleType();
const t = mod.t;

const Str = t.str;
const Obj = t.object({
x: () => Str,
});

type asdf = ResolveType<typeof Obj>;
t.Array(Str);

const Node = t.object({
value: t.any,
key: t.str,
left: () => Node,
right: () => Node,
});


});
23 changes: 19 additions & 4 deletions src/type/classes/ObjType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,20 +35,35 @@ export class KeyType<K extends string, V extends Type> extends AbsType<schema.Ke
}
}

export class KeyOptType<K extends string, V extends Type> extends KeyType<K, V> {
export class KeyOptType<K extends string, V extends Type> extends AbsType<schema.OptKeySchema<K, SchemaOf<V>>> {
public readonly optional: boolean = true;

constructor(
public readonly key: K,
public readonly val: V,
) {
super(key, val);
(this as any).schema = schema.s.KeyOpt(key, schema.s.any) as any;
super(schema.s.KeyOpt(key, schema.s.any) as any);
}

public getSchema(): schema.OptKeySchema<K, SchemaOf<V>> {
return {
...this.schema,
value: this.val.getSchema() as any,
};
}

public getOptions(): schema.Optional<schema.KeySchema<K, SchemaOf<V>>> {
const {kind, key, value, optional, ...options} = this.schema;
return options as any;
}

protected toStringTitle(): string {
return JSON.stringify(this.key) + '?';
}

public toString(tab: string = ''): string {
return super.toString(tab) + printTree(tab + ' ', [(tab) => this.val.toString(tab)]);
}
}

export class ObjType<
Expand Down Expand Up @@ -113,7 +128,7 @@ export class ObjType<

public getField<K extends keyof schema.TypeOf<schema.ObjSchema<SchemaOfObjectFields<F>>>>(
key: K,
): KeyType<string, Type> | undefined {
): KeyType<string, Type> | KeyOptType<string, Type> | undefined {
return this.keys.find((f) => f.key === key);
}

Expand Down
18 changes: 14 additions & 4 deletions src/type/classes/RefType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,16 @@ import type {SchemaOf, Type} from '../types';
import {AbsType} from './AbsType';

export class RefType<T extends Type = any> extends AbsType<schema.RefSchema<SchemaOf<T>>> {
constructor(ref: string) {
super(schema.s.Ref<SchemaOf<T>>(ref));
protected _ref: string | (() => string);

constructor(ref: string | (() => string)) {
super(schema.s.Ref<SchemaOf<T>>(typeof ref === 'function' ? 'lazy' : ref));
this._ref = ref;
}

public ref(): string {
return this.schema.ref;
const _ref = this._ref;
return typeof _ref === 'function' ? _ref() : _ref;
}

public getOptions(): schema.Optional<schema.RefSchema<SchemaOf<T>>> {
Expand All @@ -20,8 +24,14 @@ export class RefType<T extends Type = any> extends AbsType<schema.RefSchema<Sche
return this.getSystem().resolve(this.ref()).type as Type;
}

public getSchema() {
const _ref = this._ref;
const ref = typeof _ref === 'function' ? _ref() : _ref;
return {...super.getSchema(), ref} as any;
}

public toStringTitle(tab: string = ''): string {
const options = this.toStringOptions();
return `${super.toStringTitle()} → [${this.schema.ref}]` + (options ? ` ${options}` : '');
return `${super.toStringTitle()} → [${this.ref()}]` + (options ? ` ${options}` : '');
}
}
3 changes: 3 additions & 0 deletions src/type/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ export type Type =
| classes.FnType<any, any, any>
| classes.FnRxType<any, any, any>;

export type TypeRef<T extends Type> = T | (() => AliasType<any, T>)
export type TypeRefToType<T extends TypeRef<Type>> = T extends () => infer U ? U : T;

export type SchemaOf<T extends Type | Type[]> = T extends BaseType<infer U> ? U : never;
export type SchemaOfMap<M extends Record<string, Type>> = {
[K in keyof M]: SchemaOf<M[K]>;
Expand Down
3 changes: 1 addition & 2 deletions src/value/ObjValue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,11 @@ export class ObjValue<T extends classes.ObjType<any>> extends Value<T> implement

public add<K extends string, V extends classes.Type>(
key: K,
type: V | ((t: TypeBuilder) => V),
type: V,
data: classes.ResolveType<V>,
) {
const system = (this.type as classes.ObjType<any>).getSystem();
const t = system.t;
type = typeof type === 'function' ? type(t) : type;
return this.field(t.Key(key, type), data);
}

Expand Down
Loading