Skip to content

Commit

Permalink
Merge pull request #479 from streamich/json-type-value-2
Browse files Browse the repository at this point in the history
Json type value 2
  • Loading branch information
streamich authored Dec 7, 2023
2 parents bc48f26 + 63fb36b commit 548a10c
Show file tree
Hide file tree
Showing 33 changed files with 270 additions and 145 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@
"json-stable",
"json-text",
"json-type",
"json-type-value",
"reactive-rpc",
"util"
]
Expand Down
4 changes: 2 additions & 2 deletions src/json-type-cli/Cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ import {formatError} from './util';
import {defineBuiltinRoutes} from './methods';
import {defaultParams} from './defaultParams';
import type {CliCodecs} from './CliCodecs';
import type {Value} from '../reactive-rpc/common/messages/Value';
import type {TypeBuilder} from '../json-type/type/TypeBuilder';
import type {WriteStream, ReadStream} from 'tty';
import type {CliCodec, CliContext, CliParam, CliParamInstance} from './types';
import type {Value} from '../json-type-value/Value';

export interface CliOptions<Router extends TypeRouter<any>> {
codecs: CliCodecs;
Expand Down Expand Up @@ -109,7 +109,7 @@ export class Cli<Router extends TypeRouter<RoutesBase> = TypeRouter<RoutesBase>>
const ctx: CliContext<Router> = {cli: this};
for (const instance of this.paramInstances) if (instance.onBeforeCall) await instance.onBeforeCall(method, ctx);
const value = await this.caller.call(method, this.request as any, ctx);
this.response = (value as Value).data;
this.response = (value as Value<any>).data;
for (const instance of this.paramInstances) if (instance.onResponse) await instance.onResponse();
const buf = this.responseCodec.encode(this.response);
this.stdout.write(buf);
Expand Down
2 changes: 1 addition & 1 deletion src/json-type-cli/util.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {Value} from '../reactive-rpc/common/messages/Value';
import {Value} from '../json-type-value/Value';
import {RpcError} from '../reactive-rpc/common/rpc/caller';

export const formatError = (err: unknown): unknown => {
Expand Down
96 changes: 96 additions & 0 deletions src/json-type-value/ObjectValue.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import {Value} from './Value';
import {toText} from '../json-type/typescript/toText';
import type {ResolveType} from '../json-type';
import type * as classes from '../json-type/type';
import type * as ts from '../json-type/typescript/types';

type UnObjectType<T> = T extends classes.ObjectType<infer U> ? U : never;
type UnObjectFieldTypeVal<T> = T extends classes.ObjectFieldType<any, infer U> ? U : never;

// export type MergeObjectsTypes<A, B> =
// A extends classes.ObjectType<infer A2>
// ? B extends classes.ObjectType<infer B2>
// ? classes.ObjectType<[...A2, ...B2]> :
// never :
// never;

// export type MergeObjectValues<A, B> =
// A extends ObjectValue<infer A2>
// ? B extends ObjectValue<infer B2>
// ? ObjectValue<MergeObjectsTypes<A2, B2>> :
// never :
// never;

export class ObjectValue<T extends classes.ObjectType<any>> extends Value<T> {
public field<F extends classes.ObjectFieldType<any, any>>(
field: F,
data: ResolveType<UnObjectFieldTypeVal<F>>,
): ObjectValue<classes.ObjectType<[...UnObjectType<T>, F]>> {
const extendedData = {...this.data, [field.key]: data};
const type = this.type;
const system = type.system;
if (!system) throw new Error('NO_SYSTEM');
const extendedType = system.t.Object(...type.fields, field);
return new ObjectValue(extendedType, extendedData) as any;
}

public prop<K extends string, V extends classes.Type>(key: K, type: V, data: ResolveType<V>) {
const system = type.system;
if (!system) throw new Error('NO_SYSTEM');
return this.field(system.t.prop(key, type), data);
}

public merge<O extends ObjectValue<any>>(
obj: O,
): ObjectValue<classes.ObjectType<[...UnObjectType<T>, ...UnObjectType<O['type']>]>> {
const extendedData = {...this.data, ...obj.data};
const type = this.type;
const system = type.system;
if (!system) throw new Error('NO_SYSTEM');
const extendedType = system.t.Object(...type.fields, ...obj.type.fields);
return new ObjectValue(extendedType, extendedData) as any;
}

public toTypeScriptAst(): ts.TsTypeLiteral {
const node: ts.TsTypeLiteral = {
node: 'TypeLiteral',
members: [],
};
const data = this.data as Record<string, classes.Type>;
for (const [name, type] of Object.entries(data)) {
const schema = type.getSchema();
const property: ts.TsPropertySignature = {
node: 'PropertySignature',
name,
type: type.toTypeScriptAst(),
};
if (schema.title) property.comment = schema.title;
node.members.push(property);
}
return node;
}

public toTypeScriptModuleAst(): ts.TsModuleDeclaration {
const node: ts.TsModuleDeclaration = {
node: 'ModuleDeclaration',
name: 'Router',
export: true,
statements: [
{
node: 'TypeAliasDeclaration',
name: 'Routes',
type: this.toTypeScriptAst(),
export: true,
},
],
};
const system = this.type.system;
if (!system) throw new Error('system is undefined');
for (const alias of system.aliases.values()) node.statements.push({...alias.toTypeScriptAst(), export: true});
return node;
}

public toTypeScript(): string {
return toText(this.toTypeScriptModuleAst());
}
}
5 changes: 5 additions & 0 deletions src/json-type-value/Value.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import type {ResolveType, Type} from '../json-type';

export class Value<T extends Type> {
constructor(public type: T, public data: ResolveType<T>) {}
}
11 changes: 11 additions & 0 deletions src/json-type-value/util.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import {Value} from './Value';
import {ObjectValue} from './ObjectValue';
import * as classes from '../json-type/type';

export const value: {
<T extends classes.ObjectType>(type: T, data: unknown): ObjectValue<T>;
<T extends classes.Type>(type: T, data: unknown): Value<T>;
} = (type: any, data: any): any => {
if (type instanceof classes.ObjectType) return new ObjectValue(type as classes.ObjectType, <any>data);
return new Value(type as classes.Type, <any>data);
};
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {Codegen, CodegenStepExecJs} from '../../../util/codegen';
import {WriteBlobStep} from '../WriteBlobStep';
import {concat} from '../../../util/buffers/concat';
import {Value} from '../../../reactive-rpc/common/messages/Value';
import {Value} from '../../../json-type-value/Value';
import type {TypeSystem} from '../../system';
import type {Type} from '../../type';
import type {CompiledBinaryEncoder} from '../types';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {Codegen, CodegenStepExecJs} from '../../../util/codegen';
import {maxEncodingCapacity} from '../../../json-size';
import {Value} from '../../../reactive-rpc/common/messages/Value';
import {Value} from '../../../json-type-value/Value';
import type {TypeSystem} from '../../system';
import type {Type} from '../../type';

Expand Down
1 change: 0 additions & 1 deletion src/json-type/system/TypeRouter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,6 @@ export class TypeRouter<Routes extends RoutesBase> {
}

public toTypeScript(): string {
this.system.exportTypes;
return toText(this.toTypeScriptModuleAst());
}
}
Expand Down
2 changes: 2 additions & 0 deletions src/json-type/type/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import type * as schema from '../schema';
import type * as classes from './classes';

export type * from './classes';

export interface BaseType<S extends schema.TType> {
getSchema(): S;
}
Expand Down
4 changes: 2 additions & 2 deletions src/reactive-rpc/common/codec/binary/__tests__/decode.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
ResponseErrorMessage,
ResponseUnsubscribeMessage,
} from '../../../messages';
import {Value} from '../../../messages/Value';
import {RpcValue} from '../../../messages/Value';
import {decode} from '../decode';
import {Reader} from '../../../../../util/buffers/Reader';
import {Uint8ArrayCut} from '../../../../../util/buffers/Uint8ArrayCut';
Expand All @@ -19,7 +19,7 @@ import {Writer} from '../../../../../util/buffers/Writer';
const codec = new CborJsonValueCodec(new Writer(64));
const encoder = codec.encoder;
const decoder = codec.decoder;
const val = <T>(v: T) => new Value<T>(v, undefined);
const val = <T>(v: T) => new RpcValue<T>(v, undefined);
const assertMessage = (msg: ReactiveRpcMessage) => {
encoder.writer.reset();
msg.encodeBinary(codec);
Expand Down
4 changes: 2 additions & 2 deletions src/reactive-rpc/common/codec/binary/__tests__/encode.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ import {
RequestUnsubscribeMessage,
ResponseDataMessage,
} from '../../../messages';
import {Value} from '../../../messages/Value';
import {RpcValue} from '../../../messages/Value';
import {CborJsonValueCodec} from '../../../../../json-pack/codecs/cbor';
import {Writer} from '../../../../../util/buffers/Writer';

const cborCodec = new CborJsonValueCodec(new Writer(64));
const encoder = cborCodec.encoder;
const val = <T>(v: T) => new Value<T>(v, undefined);
const val = <T>(v: T) => new RpcValue<T>(v, undefined);
const encode = (msg: ReactiveRpcMessage) => {
msg.encodeBinary(cborCodec);
return encoder.writer.flush();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
ResponseErrorMessage,
ResponseUnsubscribeMessage,
} from '../../../messages';
import {Value} from '../../../messages/Value';
import {RpcValue} from '../../../messages/Value';
import {messages} from '../../../messages/__tests__/fixtures';

const writer = new Writer(8 * Math.round(Math.random() * 100));
Expand All @@ -31,25 +31,25 @@ for (const jsonCodec of codecList) {

describe(jsonCodec.id, () => {
test('Notification message', () => {
const value = new Value({foo: 'bar'}, undefined);
const value = new RpcValue({foo: 'bar'}, undefined);
const message = new NotificationMessage('abc', value);
assertMessage(message);
});

test('Request Data message', () => {
const value = new Value([1, 2, 3], undefined);
const value = new RpcValue([1, 2, 3], undefined);
const message = new RequestDataMessage(9999, 'a', value);
assertMessage(message);
});

test('Request Complete message', () => {
const value = new Value(true, undefined);
const value = new RpcValue(true, undefined);
const message = new RequestCompleteMessage(3, 'abc', value);
assertMessage(message);
});

test('Request Error message', () => {
const value = new Value({message: 'Error!', errno: 123, code: 'ERROR'}, undefined);
const value = new RpcValue({message: 'Error!', errno: 123, code: 'ERROR'}, undefined);
const message = new RequestErrorMessage(0, 'wtf', value);
assertMessage(message);
});
Expand All @@ -60,19 +60,19 @@ for (const jsonCodec of codecList) {
});

test('Response Data message', () => {
const value = new Value([1, 2, 3], undefined);
const value = new RpcValue([1, 2, 3], undefined);
const message = new ResponseDataMessage(30000, value);
assertMessage(message);
});

test('Response Complete message', () => {
const value = new Value(true, undefined);
const value = new RpcValue(true, undefined);
const message = new ResponseCompleteMessage(3, value);
assertMessage(message);
});

test('Response Error message', () => {
const value = new Value({message: 'Error!', errno: 123, code: 'ERROR'}, undefined);
const value = new RpcValue({message: 'Error!', errno: 123, code: 'ERROR'}, undefined);
const message = new ResponseErrorMessage(0, value);
assertMessage(message);
});
Expand All @@ -85,7 +85,7 @@ for (const jsonCodec of codecList) {
}

describe('batch of messages', () => {
const value = new Value({foo: 'bar'}, undefined);
const value = new RpcValue({foo: 'bar'}, undefined);
const message1 = new NotificationMessage('abc', value);
const message2 = new RequestDataMessage(888, 'a', value);
const message3 = new ResponseCompleteMessage(3, value);
Expand Down
8 changes: 4 additions & 4 deletions src/reactive-rpc/common/codec/binary/decode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
ResponseErrorMessage,
ResponseUnsubscribeMessage,
} from '../../messages';
import {Value} from '../../messages/Value';
import {RpcValue} from '../../messages/Value';
import {BinaryMessageType} from './constants';
import type {Reader} from '../../../../util/buffers/Reader';

Expand All @@ -24,7 +24,7 @@ export const decode = (reader: Reader): ReactiveRpcMessage => {
const x = word >>> 8;
const name = reader.ascii(z);
const cut = new Uint8ArrayCut(reader.uint8, reader.x, x);
const value = new Value(cut, undefined);
const value = new RpcValue(cut, undefined);
reader.skip(x);
return new NotificationMessage(name, value);
}
Expand Down Expand Up @@ -52,7 +52,7 @@ export const decode = (reader: Reader): ReactiveRpcMessage => {
reader.skip(x);
}
const cut = new Uint8ArrayCut(reader.uint8, cutStart, x);
const value = new Value(cut, undefined);
const value = new RpcValue(cut, undefined);
switch (type) {
case BinaryMessageType.RequestData:
return new RequestDataMessage(y, name, value);
Expand Down Expand Up @@ -85,7 +85,7 @@ export const decode = (reader: Reader): ReactiveRpcMessage => {
reader.skip(x);
}
const cut = new Uint8ArrayCut(reader.uint8, cutStart, x);
const value = new Value(cut, undefined);
const value = new RpcValue(cut, undefined);
switch (type) {
case BinaryMessageType.ResponseData:
return new ResponseDataMessage(y, value);
Expand Down
16 changes: 8 additions & 8 deletions src/reactive-rpc/common/codec/compact/CompactRpcMessageCodec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {RpcMessageFormat} from '../constants';
import {RpcError, RpcErrorCodes} from '../../rpc/caller/error';
import * as msg from '../../messages';
import {CompactMessageType} from './constants';
import {Value} from '../../messages/Value';
import {RpcValue} from '../../messages/Value';
import {CborEncoder} from '../../../../json-pack/cbor/CborEncoder';
import {MsgPackEncoder} from '../../../../json-pack/msgpack';
import {JsonEncoder} from '../../../../json-pack/json/JsonEncoder';
Expand All @@ -16,36 +16,36 @@ const fromJson = (arr: unknown | unknown[] | types.CompactMessage): msg.Reactive
switch (type) {
case CompactMessageType.RequestComplete: {
const data = arr[3];
const value = data === undefined ? data : new Value(data, undefined);
const value = data === undefined ? data : new RpcValue(data, undefined);
return new msg.RequestCompleteMessage(arr[1], arr[2], value);
}
case CompactMessageType.RequestData: {
const data = arr[3];
const value = data === undefined ? data : new Value(data, undefined);
const value = data === undefined ? data : new RpcValue(data, undefined);
return new msg.RequestDataMessage(arr[1], arr[2], value);
}
case CompactMessageType.RequestError: {
return new msg.RequestErrorMessage(arr[1], arr[2], new Value(arr[3], undefined));
return new msg.RequestErrorMessage(arr[1], arr[2], new RpcValue(arr[3], undefined));
}
case CompactMessageType.RequestUnsubscribe: {
return new msg.RequestUnsubscribeMessage(arr[1]);
}
case CompactMessageType.ResponseComplete: {
const data = arr[2];
const value = data === undefined ? data : new Value(data, undefined);
const value = data === undefined ? data : new RpcValue(data, undefined);
return new msg.ResponseCompleteMessage(arr[1], value);
}
case CompactMessageType.ResponseData: {
return new msg.ResponseDataMessage(arr[1], new Value(arr[2], undefined));
return new msg.ResponseDataMessage(arr[1], new RpcValue(arr[2], undefined));
}
case CompactMessageType.ResponseError: {
return new msg.ResponseErrorMessage(arr[1], new Value(arr[2], undefined));
return new msg.ResponseErrorMessage(arr[1], new RpcValue(arr[2], undefined));
}
case CompactMessageType.ResponseUnsubscribe: {
return new msg.ResponseUnsubscribeMessage(arr[1]);
}
case CompactMessageType.Notification: {
return new msg.NotificationMessage(arr[1], new Value(arr[2], undefined));
return new msg.NotificationMessage(arr[1], new RpcValue(arr[2], undefined));
}
}
throw RpcError.value(RpcError.validation('Unknown message type'));
Expand Down
Loading

0 comments on commit 548a10c

Please sign in to comment.