Skip to content

Commit 9f85a18

Browse files
authored
Merge pull request #442 from streamich/rx-rpc
JSON Type and JSON Reactive RPC improvements
2 parents e001168 + c9e186c commit 9f85a18

37 files changed

+602
-233
lines changed

.github/workflows/ci.yml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ jobs:
4646
- run: yarn test:cli:pack
4747
- run: yarn demo:json-patch
4848
- run: yarn demo:json-pointer
49-
rx-rpc:
49+
e2e-rx-rpc:
5050
runs-on: ubuntu-latest
5151
strategy:
5252
matrix:
@@ -59,5 +59,4 @@ jobs:
5959
node-version: ${{ matrix.node-version }}
6060
cache: yarn
6161
- run: yarn install --frozen-lockfile
62-
- run: yarn build:all
63-
- run: PORT=9999 yarn test:reactive-rpc
62+
- run: yarn test:reactive-rpc

package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,14 +47,14 @@
4747
"build": "yarn build:es2020",
4848
"jest": "node -r ts-node/register ./node_modules/.bin/jest",
4949
"test": "jest --maxWorkers 7",
50-
"test:all": "yarn lint && yarn test && yarn test:cli:pointer && yarn test:cli:patch && yarn test:cli:pack && yarn test:reactive-rpc && yarn demo:json-patch && yarn demo:json-pointer",
50+
"test:all": "yarn lint && yarn test && yarn build:all && yarn test:cli:pointer && yarn test:cli:patch && yarn test:cli:pack && yarn test:reactive-rpc && yarn demo:json-patch && yarn demo:json-pointer",
5151
"test:ci": "yarn jest --maxWorkers 3 --no-cache",
5252
"test:cli": "yarn test:cli:pointer && yarn test:cli:patch && yarn test:cli:pack",
5353
"test:cli:pointer": "./bin/json-pointer-test.js ./bin/json-pointer.js",
5454
"test:cli:patch": "./bin/json-patch-test.js ./bin/json-patch.js",
5555
"test:cli:pack": "./bin/json-pack-test.js ./bin/json-pack.js",
56-
"test:reactive-rpc": "node -r ts-node/register/transpile-only src/__tests__/reactive-rpc/run.ts",
57-
"test:reactive-rpc:jest": "TEST_E2E=1 jest --maxWorkers 1 --no-cache src/__tests__/reactive-rpc/",
56+
"test:reactive-rpc": "node -r ts-node/register/transpile-only src/reactive-rpc/__tests__/e2e/run.ts",
57+
"test:reactive-rpc:jest": "TEST_E2E=1 jest --maxWorkers 1 --no-cache src/reactive-rpc/__tests__/e2e/",
5858
"demo:json-patch": "ts-node src/json-patch/__demos__/json-patch.ts",
5959
"demo:json-pointer": "ts-node src/json-pointer/__demos__/json-pointer.ts",
6060
"demo:reactive-rpc:server": "ts-node src/reactive-rpc/__demos__/server.ts",
@@ -158,7 +158,7 @@
158158
"webpack": "^5.84.1",
159159
"webpack-cli": "^5.1.1",
160160
"webpack-dev-server": "^4.15.0",
161-
"ws": "^8.6.0",
161+
"ws": "^8.14.2",
162162
"yjs": "13.6.8",
163163
"ywasm": "0.16.10"
164164
},

src/json-type/system/TypeRouter.ts

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import * as classes from '../type/classes';
22
import {TypeSystem} from './TypeSystem';
3+
import {toText} from '../typescript/toText';
4+
import type * as ts from '../typescript/types';
35
import type {TypeBuilder} from '../type/TypeBuilder';
46

57
export interface TypeRouterOptions<R extends RoutesBase> {
@@ -27,7 +29,7 @@ export class TypeRouter<Routes extends RoutesBase> {
2729
this.system = options.system;
2830
this.t = this.system.t;
2931
this.routes = options.routes;
30-
this.system.importTypes(this.routes);
32+
// this.system.importTypes(this.routes);
3133
}
3234

3335
protected merge<Router extends TypeRouter<any>>(router: Router): TypeRouter<Routes & TypeRouterRoutes<Router>> {
@@ -60,6 +62,47 @@ export class TypeRouter<Routes extends RoutesBase> {
6062
this.routes[name] = <any>type;
6163
return <any>this;
6264
}
65+
66+
public toTypeScriptAst(): ts.TsTypeLiteral {
67+
const node: ts.TsTypeLiteral = {
68+
node: 'TypeLiteral',
69+
members: [],
70+
};
71+
for (const [name, type] of Object.entries(this.routes)) {
72+
const schema = type.getSchema();
73+
const property: ts.TsPropertySignature = {
74+
node: 'PropertySignature',
75+
name,
76+
type: type.toTypeScriptAst(),
77+
};
78+
if (schema.title) property.comment = schema.title;
79+
node.members.push(property);
80+
}
81+
return node;
82+
}
83+
84+
public toTypeScriptModuleAst(): ts.TsModuleDeclaration {
85+
const node: ts.TsModuleDeclaration = {
86+
node: 'ModuleDeclaration',
87+
name: 'Router',
88+
export: true,
89+
statements: [
90+
{
91+
node: 'TypeAliasDeclaration',
92+
name: 'Routes',
93+
type: this.toTypeScriptAst(),
94+
export: true,
95+
},
96+
],
97+
};
98+
for (const alias of this.system.aliases.values()) node.statements.push({...alias.toTypeScriptAst(), export: true});
99+
return node;
100+
}
101+
102+
public toTypeScript(): string {
103+
this.system.exportTypes;
104+
return toText(this.toTypeScriptModuleAst());
105+
}
63106
}
64107

65108
export type RoutesBase = Record<string, classes.FunctionType<any, any> | classes.FunctionStreamingType<any, any>>;

src/json-type/system/TypeSystem.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import type {Printable} from '../../util/print/types';
99
export class TypeSystem implements Printable {
1010
public readonly t = new TypeBuilder(this);
1111

12-
protected readonly aliases: Map<string, TypeAlias<string, any>> = new Map();
12+
public readonly aliases: Map<string, TypeAlias<string, any>> = new Map();
1313

1414
/**
1515
* @todo Add ability fetch object of given type by its ID, analogous to

src/json-type/system/__tests__/toTypeScript.spec.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {TypeSystem} from '..';
2+
import {TypeRouter} from '../TypeRouter';
23

34
test('generates TypeScript source for simple string type', () => {
45
const system = new TypeSystem();
@@ -91,3 +92,49 @@ test('type interface inside a tuple', () => {
9192
"
9293
`);
9394
});
95+
96+
test('can export whole router', () => {
97+
const system = new TypeSystem();
98+
const {t} = system;
99+
const router = new TypeRouter({system, routes: {}}).extend(() => ({
100+
callMe: t.Function(t.str, t.num),
101+
'block.subscribe': t.Function$(t.Object(t.prop('id', t.str)), t.obj),
102+
}));
103+
expect(router.toTypeScript()).toMatchInlineSnapshot(`
104+
"export namespace Router {
105+
export type Routes = {
106+
callMe: (request: string) => Promise<number>;
107+
"block.subscribe": (request$: Observable<{
108+
id: string;
109+
}>) => Observable<{}>;
110+
};
111+
}
112+
"
113+
`);
114+
});
115+
116+
test('can export whole router and aliases', () => {
117+
const system = new TypeSystem();
118+
const {t} = system;
119+
system.alias('Document', t.Object(t.prop('id', t.str), t.prop('title', t.str)).options({title: 'The document'}));
120+
const router = new TypeRouter({system, routes: {}}).extend(() => ({
121+
callMe: t.Function(t.str, t.num),
122+
'block.subscribe': t.Function$(t.Object(t.prop('id', t.str)), t.Ref('Document')),
123+
}));
124+
expect(router.toTypeScript()).toMatchInlineSnapshot(`
125+
"export namespace Router {
126+
export type Routes = {
127+
callMe: (request: string) => Promise<number>;
128+
"block.subscribe": (request$: Observable<{
129+
id: string;
130+
}>) => Observable<Document>;
131+
};
132+
133+
export interface Document {
134+
id: string;
135+
title: string;
136+
}
137+
}
138+
"
139+
`);
140+
});

src/json-type/type/__tests__/toTypeScriptAst.spec.ts

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,3 +244,86 @@ describe('or', () => {
244244
`);
245245
});
246246
});
247+
248+
describe('fn', () => {
249+
test('can emit reference AST', () => {
250+
const system = new TypeSystem();
251+
const {t} = system;
252+
const type = system.t.Function(t.str, t.num);
253+
expect(type.toTypeScriptAst()).toMatchInlineSnapshot(`
254+
{
255+
"node": "FunctionType",
256+
"parameters": [
257+
{
258+
"name": {
259+
"name": "request",
260+
"node": "Identifier",
261+
},
262+
"node": "Parameter",
263+
"type": {
264+
"node": "StringKeyword",
265+
},
266+
},
267+
],
268+
"type": {
269+
"node": "TypeReference",
270+
"typeArguments": [
271+
{
272+
"node": "NumberKeyword",
273+
},
274+
],
275+
"typeName": {
276+
"name": "Promise",
277+
"node": "Identifier",
278+
},
279+
},
280+
}
281+
`);
282+
});
283+
});
284+
285+
describe('fn$', () => {
286+
test('can emit reference AST', () => {
287+
const system = new TypeSystem();
288+
const {t} = system;
289+
const type = system.t.Function$(t.str, t.num);
290+
expect(type.toTypeScriptAst()).toMatchInlineSnapshot(`
291+
{
292+
"node": "FunctionType",
293+
"parameters": [
294+
{
295+
"name": {
296+
"name": "request$",
297+
"node": "Identifier",
298+
},
299+
"node": "Parameter",
300+
"type": {
301+
"node": "TypeReference",
302+
"typeArguments": [
303+
{
304+
"node": "StringKeyword",
305+
},
306+
],
307+
"typeName": {
308+
"name": "Observable",
309+
"node": "Identifier",
310+
},
311+
},
312+
},
313+
],
314+
"type": {
315+
"node": "TypeReference",
316+
"typeArguments": [
317+
{
318+
"node": "NumberKeyword",
319+
},
320+
],
321+
"typeName": {
322+
"name": "Observable",
323+
"node": "Identifier",
324+
},
325+
},
326+
}
327+
`);
328+
});
329+
});

src/json-type/type/classes.ts

Lines changed: 53 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2228,8 +2228,29 @@ export class FunctionType<Req extends Type, Res extends Type> extends AbstractTy
22282228
return this;
22292229
}
22302230

2231-
public toTypeScriptAst(): ts.TsUnionType {
2232-
throw new Error('Method not implemented.');
2231+
public toTypeScriptAst(): ts.TsFunctionType {
2232+
const node: ts.TsFunctionType = {
2233+
node: 'FunctionType',
2234+
parameters: [
2235+
{
2236+
node: 'Parameter',
2237+
name: {
2238+
node: 'Identifier',
2239+
name: 'request',
2240+
},
2241+
type: this.req.toTypeScriptAst(),
2242+
},
2243+
],
2244+
type: {
2245+
node: 'TypeReference',
2246+
typeName: {
2247+
node: 'Identifier',
2248+
name: 'Promise',
2249+
},
2250+
typeArguments: [this.res.toTypeScriptAst()],
2251+
},
2252+
};
2253+
return node;
22332254
}
22342255

22352256
public toString(tab: string = ''): string {
@@ -2289,8 +2310,36 @@ export class FunctionStreamingType<Req extends Type, Res extends Type> extends A
22892310
return this;
22902311
}
22912312

2292-
public toTypeScriptAst(): ts.TsUnionType {
2293-
throw new Error('Method not implemented.');
2313+
public toTypeScriptAst(): ts.TsFunctionType {
2314+
const node: ts.TsFunctionType = {
2315+
node: 'FunctionType',
2316+
parameters: [
2317+
{
2318+
node: 'Parameter',
2319+
name: {
2320+
node: 'Identifier',
2321+
name: 'request$',
2322+
},
2323+
type: {
2324+
node: 'TypeReference',
2325+
typeName: {
2326+
node: 'Identifier',
2327+
name: 'Observable',
2328+
},
2329+
typeArguments: [this.req.toTypeScriptAst()],
2330+
},
2331+
},
2332+
],
2333+
type: {
2334+
node: 'TypeReference',
2335+
typeName: {
2336+
node: 'Identifier',
2337+
name: 'Observable',
2338+
},
2339+
typeArguments: [this.res.toTypeScriptAst()],
2340+
},
2341+
};
2342+
return node;
22942343
}
22952344

22962345
public toString(tab: string = ''): string {

0 commit comments

Comments
 (0)