diff --git a/src/modules/__tests__/json-rx.spec.ts b/src/modules/__tests__/json-rx.spec.ts new file mode 100644 index 00000000..bd09100b --- /dev/null +++ b/src/modules/__tests__/json-rx.spec.ts @@ -0,0 +1,9 @@ +import {SchemaOf, t} from '../../type'; +import {Message, NotificationMessage} from '../json-rx'; + +test('...', () => { + console.log(NotificationMessage + ''); + type adasfsad = SchemaOf; + type asdf = t.infer; + // console.log(Message + ''); +}); diff --git a/src/modules/json-rx.ts b/src/modules/json-rx.ts new file mode 100644 index 00000000..504430d2 --- /dev/null +++ b/src/modules/json-rx.ts @@ -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('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('MethodName'), + ), + t.tuple( + t.con(CompactMessageType.RequestData), + t.num.format('u32'), + t.Ref('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('MethodName'), + ), + t.tuple( + t.con(CompactMessageType.RequestComplete), + t.num.format('u32'), + t.Ref('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('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'); diff --git a/src/schema/schema.ts b/src/schema/schema.ts index 950d6330..82a508dc 100644 --- a/src/schema/schema.ts +++ b/src/schema/schema.ts @@ -489,19 +489,21 @@ export type TypeOfValue = T extends BoolSchema ] : T extends ConSchema ? U - : T extends KeySchema - ? TypeOf - : T extends ObjSchema - ? NoEmptyInterface>> - : T extends MapSchema - ? Record> - : T extends BinSchema - ? Uint8Array - : T extends FnSchema - ? (req: TypeOf, ctx: Ctx) => UndefToVoid> | Promise>> - : T extends FnRxSchema - ? (req$: Observable>, ctx: Ctx) => Observable>> - : never; + : T extends OptKeySchema + ? (TypeOf | undefined) + : T extends KeySchema + ? TypeOf + : T extends ObjSchema + ? NoEmptyInterface>> + : T extends MapSchema + ? Record> + : T extends BinSchema + ? Uint8Array + : T extends FnSchema + ? (req: TypeOf, ctx: Ctx) => UndefToVoid> | Promise>> + : T extends FnRxSchema + ? (req$: Observable>, ctx: Ctx) => Observable>> + : never; export type TypeOfMap> = { [K in keyof M]: TypeOf; diff --git a/src/type/TypeBuilder.ts b/src/type/TypeBuilder.ts index 9034acb7..80397e25 100644 --- a/src/type/TypeBuilder.ts +++ b/src/type/TypeBuilder.ts @@ -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; @@ -76,7 +76,7 @@ export class TypeBuilder { // --------------------------------------------------------------- shorthands - public readonly or = (...types: F) => this.Or(...types); + public readonly or = []>(...types: F) => this.Or(...types); public readonly undefined = () => this.undef; public readonly null = () => this.nil; public readonly boolean = () => this.bool; @@ -94,7 +94,11 @@ export class TypeBuilder { options, ); - public readonly tuple = (...types: F) => this.Tuple(types); + public readonly tuple = []>(...types: F) => this.Tuple(types); + + protected typeRefToType = (type: TypeRef): TypeRefToType => { + return typeof type === 'function' ? (type() as TypeRefToType) : (type as TypeRefToType); + }; /** * Creates an object type with the specified properties. This is a shorthand for @@ -117,10 +121,10 @@ export class TypeBuilder { * @param record A mapping of property names to types. * @returns An object type. */ - public readonly object = >(record: R): classes.ObjType> => { + public readonly object = >>(record: R): classes.ObjType}>> => { const keys: classes.KeyType[] = []; - for (const [key, value] of Object.entries(record)) keys.push(this.Key(key, value)); - return new classes.ObjType>(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}>>(keys as any).sys(this.system); }; /** diff --git a/src/type/__tests__/type-ref.spec.ts b/src/type/__tests__/type-ref.spec.ts new file mode 100644 index 00000000..3920ab37 --- /dev/null +++ b/src/type/__tests__/type-ref.spec.ts @@ -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; + t.Array(Str); + + const Node = t.object({ + value: t.any, + key: t.str, + left: () => Node, + right: () => Node, + }); + + +}); diff --git a/src/type/classes/ObjType.ts b/src/type/classes/ObjType.ts index a9a687af..eb62589b 100644 --- a/src/type/classes/ObjType.ts +++ b/src/type/classes/ObjType.ts @@ -35,20 +35,35 @@ export class KeyType extends AbsType extends KeyType { +export class KeyOptType extends AbsType>> { 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> { + return { + ...this.schema, + value: this.val.getSchema() as any, + }; + } + + public getOptions(): schema.Optional>> { + 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< @@ -113,7 +128,7 @@ export class ObjType< public getField>>>( key: K, - ): KeyType | undefined { + ): KeyType | KeyOptType | undefined { return this.keys.find((f) => f.key === key); } diff --git a/src/type/classes/RefType.ts b/src/type/classes/RefType.ts index 5e735586..67a95abc 100644 --- a/src/type/classes/RefType.ts +++ b/src/type/classes/RefType.ts @@ -3,12 +3,16 @@ import type {SchemaOf, Type} from '../types'; import {AbsType} from './AbsType'; export class RefType extends AbsType>> { - constructor(ref: string) { - super(schema.s.Ref>(ref)); + protected _ref: string | (() => string); + + constructor(ref: string | (() => string)) { + super(schema.s.Ref>(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>> { @@ -20,8 +24,14 @@ export class RefType extends AbsType | classes.FnRxType; +export type TypeRef = T | (() => AliasType) +export type TypeRefToType> = T extends () => infer U ? U : T; + export type SchemaOf = T extends BaseType ? U : never; export type SchemaOfMap> = { [K in keyof M]: SchemaOf; diff --git a/src/value/ObjValue.ts b/src/value/ObjValue.ts index 47807733..835e4810 100644 --- a/src/value/ObjValue.ts +++ b/src/value/ObjValue.ts @@ -56,12 +56,11 @@ export class ObjValue> extends Value implement public add( key: K, - type: V | ((t: TypeBuilder) => V), + type: V, data: classes.ResolveType, ) { const system = (this.type as classes.ObjType).getSystem(); const t = system.t; - type = typeof type === 'function' ? type(t) : type; return this.field(t.Key(key, type), data); }