@nmtjs/client 0.13.0 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/common.d.ts CHANGED
@@ -1,11 +1,19 @@
1
+ import type { TypeProvider } from '@nmtjs/common';
1
2
  import type { TAnyAPIContract } from '@nmtjs/contract';
2
- import { EventEmitter, type ProtocolBaseClientCallOptions, type ProtocolBaseTransformer, ProtocolError, type ProtocolTransport } from '@nmtjs/protocol/client';
3
- import type { ClientCallers, ResolveAPIContract, ResolveClientEvents, RuntimeInputContractTypeProvider, RuntimeOutputContractTypeProvider } from './types.ts';
3
+ import type { ProtocolBaseClientCallOptions, ProtocolBaseTransformer, ProtocolTransport } from '@nmtjs/protocol/client';
4
+ import { ProtocolError } from '@nmtjs/protocol/client';
5
+ import type { ClientCallers, ResolveAPIRouterRoutes } from './types.ts';
4
6
  export { ErrorCode, ProtocolBlob, type ProtocolBlobMetadata, TransportType, } from '@nmtjs/protocol';
5
7
  export * from './types.ts';
6
8
  export declare class ClientError extends ProtocolError {
7
9
  }
8
- export declare abstract class BaseClient<APIContract extends TAnyAPIContract = TAnyAPIContract, SafeCall extends boolean = false, API extends ResolveAPIContract<APIContract, RuntimeInputContractTypeProvider, RuntimeOutputContractTypeProvider> = ResolveAPIContract<APIContract, RuntimeInputContractTypeProvider, RuntimeOutputContractTypeProvider>> extends EventEmitter<ResolveClientEvents<API>> {
10
+ export declare abstract class BaseClient<APIContract extends TAnyAPIContract = TAnyAPIContract, SafeCall extends boolean = false, InputTypeProvider extends TypeProvider = TypeProvider, OutputTypeProvider extends TypeProvider = TypeProvider, Routes extends {
11
+ contract: APIContract['router'];
12
+ routes: ResolveAPIRouterRoutes<APIContract['router'], InputTypeProvider, OutputTypeProvider>;
13
+ } = {
14
+ contract: APIContract['router'];
15
+ routes: ResolveAPIRouterRoutes<APIContract['router'], InputTypeProvider, OutputTypeProvider>;
16
+ }> {
9
17
  readonly transport: ProtocolTransport;
10
18
  readonly options: {
11
19
  timeout: number;
@@ -13,20 +21,20 @@ export declare abstract class BaseClient<APIContract extends TAnyAPIContract = T
13
21
  safe?: SafeCall;
14
22
  };
15
23
  _: {
16
- api: API;
24
+ api: Routes;
17
25
  safe: SafeCall;
18
26
  };
19
27
  protected abstract transformer: ProtocolBaseTransformer;
20
- protected callers: ClientCallers<API, SafeCall>;
21
- protected auth: any;
28
+ protected callers: ClientCallers<Routes, SafeCall>;
29
+ auth: any;
22
30
  protected reconnectTimeout: number;
23
31
  constructor(transport: ProtocolTransport, options: {
24
32
  timeout: number;
25
33
  autoreconnect?: boolean;
26
34
  safe?: SafeCall;
27
35
  });
28
- protected _call(namespace: string, procedure: string, payload: any, options: ProtocolBaseClientCallOptions): Promise<any>;
29
- get call(): ClientCallers<API, SafeCall>;
36
+ protected _call(procedure: string, payload: any, options: ProtocolBaseClientCallOptions): Promise<any>;
37
+ get call(): ClientCallers<Routes, SafeCall>;
30
38
  setAuth(auth: any): void;
31
39
  connect(): Promise<void>;
32
40
  disconnect(): Promise<void>;
package/dist/common.js CHANGED
@@ -1,11 +1,11 @@
1
1
  import { noopFn } from '@nmtjs/common';
2
- import { EventEmitter, ProtocolError, } from '@nmtjs/protocol/client';
2
+ import { ProtocolError } from '@nmtjs/protocol/client';
3
3
  export { ErrorCode, ProtocolBlob, TransportType, } from '@nmtjs/protocol';
4
4
  export * from "./types.js";
5
5
  export class ClientError extends ProtocolError {
6
6
  }
7
7
  const DEFAULT_RECONNECT_TIMEOUT = 1000;
8
- export class BaseClient extends EventEmitter {
8
+ export class BaseClient {
9
9
  transport;
10
10
  options;
11
11
  _;
@@ -13,7 +13,6 @@ export class BaseClient extends EventEmitter {
13
13
  auth;
14
14
  reconnectTimeout = DEFAULT_RECONNECT_TIMEOUT;
15
15
  constructor(transport, options) {
16
- super();
17
16
  this.transport = transport;
18
17
  this.options = options;
19
18
  if (this.options.autoreconnect) {
@@ -33,8 +32,8 @@ export class BaseClient extends EventEmitter {
33
32
  });
34
33
  }
35
34
  }
36
- async _call(namespace, procedure, payload, options) {
37
- const call = await this.transport.call(namespace, procedure, payload, options, this.transformer);
35
+ async _call(procedure, payload, options) {
36
+ const call = await this.transport.call(procedure, payload, options, this.transformer);
38
37
  if (this.options.safe) {
39
38
  return await call.promise
40
39
  .then((result) => ({ result }))
package/dist/runtime.d.ts CHANGED
@@ -1,16 +1,21 @@
1
- import type { TAnyAPIContract } from '@nmtjs/contract';
1
+ import type { TAnyAPIContract, TAnyProcedureContract, TAnyRouterContract } from '@nmtjs/contract';
2
2
  import { ProtocolBaseTransformer } from '@nmtjs/protocol/client';
3
+ import type { RuntimeInputContractTypeProvider, RuntimeOutputContractTypeProvider } from './common.ts';
3
4
  import { BaseClient } from './common.ts';
4
5
  export declare class RuntimeContractTransformer extends ProtocolBaseTransformer {
5
- protected contract: TAnyAPIContract;
6
- constructor(contract: TAnyAPIContract);
7
- decodeEvent(namespace: string, event: string, payload: any): unknown;
8
- decodeRPC(namespace: string, procedure: string, payload: any): unknown;
9
- decodeRPCChunk(namespace: string, procedure: string, payload: any): unknown;
10
- encodeRPC(namespace: string, procedure: string, payload: any): unknown;
6
+ protected procedures: Map<string, TAnyProcedureContract>;
7
+ constructor(procedures: Map<string, TAnyProcedureContract>);
8
+ decodeRPC(procedure: string, payload: any): any;
9
+ decodeRPCChunk(procedure: string, payload: any): unknown;
10
+ encodeRPC(procedure: string, payload: any): unknown;
11
+ protected getProcedureContract(procedure: string): TAnyProcedureContract;
12
+ protected build(router: TAnyRouterContract): void;
11
13
  }
12
- export declare class RuntimeClient<APIContract extends TAnyAPIContract, SafeCall extends boolean> extends BaseClient<APIContract, SafeCall> {
14
+ export declare class RuntimeClient<APIContract extends TAnyAPIContract, SafeCall extends boolean = false> extends BaseClient<APIContract, SafeCall, RuntimeInputContractTypeProvider, RuntimeOutputContractTypeProvider> {
13
15
  contract: APIContract;
14
16
  protected transformer: RuntimeContractTransformer;
17
+ protected procedures: Map<string, TAnyProcedureContract>;
15
18
  constructor(contract: APIContract, ...args: ConstructorParameters<typeof BaseClient<APIContract, SafeCall>>);
19
+ protected resolveProcedures(router: TAnyRouterContract): void;
20
+ protected buildCallers(): any;
16
21
  }
package/dist/runtime.js CHANGED
@@ -1,31 +1,31 @@
1
+ import { IsProcedureContract, IsRouterContract } from '@nmtjs/contract';
1
2
  import { ErrorCode } from '@nmtjs/protocol';
2
3
  import { ProtocolBaseTransformer } from '@nmtjs/protocol/client';
3
4
  import { NeemataTypeError, t } from '@nmtjs/type';
4
5
  import { BaseClient, ClientError } from "./common.js";
5
6
  export class RuntimeContractTransformer extends ProtocolBaseTransformer {
6
- contract;
7
- constructor(contract) {
7
+ procedures;
8
+ constructor(procedures) {
8
9
  super();
9
- this.contract = contract;
10
- }
11
- decodeEvent(namespace, event, payload) {
12
- const type = this.contract.namespaces[namespace].events[event].payload;
13
- return type.decode(payload);
10
+ this.procedures = procedures;
14
11
  }
15
- decodeRPC(namespace, procedure, payload) {
16
- const type = this.contract.namespaces[namespace].procedures[procedure].output;
12
+ decodeRPC(procedure, payload) {
13
+ const contract = this.getProcedureContract(procedure);
14
+ const type = contract.output;
17
15
  if (type instanceof t.NeverType)
18
16
  return undefined;
19
- return type.decode(payload);
17
+ return payload;
20
18
  }
21
- decodeRPCChunk(namespace, procedure, payload) {
22
- const type = this.contract.namespaces[namespace].procedures[procedure].stream;
19
+ decodeRPCChunk(procedure, payload) {
20
+ const contract = this.getProcedureContract(procedure);
21
+ const type = contract.stream;
23
22
  if (!type || type instanceof t.NeverType)
24
23
  return undefined;
25
24
  return type.decode(payload);
26
25
  }
27
- encodeRPC(namespace, procedure, payload) {
28
- const type = this.contract.namespaces[namespace].procedures[procedure].input;
26
+ encodeRPC(procedure, payload) {
27
+ const contract = this.getProcedureContract(procedure);
28
+ const type = contract.input;
29
29
  if (type instanceof t.NeverType)
30
30
  return undefined;
31
31
  try {
@@ -33,31 +33,73 @@ export class RuntimeContractTransformer extends ProtocolBaseTransformer {
33
33
  }
34
34
  catch (error) {
35
35
  if (error instanceof NeemataTypeError) {
36
- throw new ClientError(ErrorCode.ValidationError, `Invalid payload for ${namespace}.${procedure}: ${error.message}`, error.issues);
36
+ throw new ClientError(ErrorCode.ValidationError, `Invalid payload for ${procedure}: ${error.message}`, error.issues);
37
37
  }
38
38
  throw error;
39
39
  }
40
40
  }
41
+ getProcedureContract(procedure) {
42
+ const proc = this.procedures.get(procedure);
43
+ if (!proc) {
44
+ throw new ClientError(ErrorCode.NotFound, `Procedure contract not found for procedure: ${procedure}`);
45
+ }
46
+ return proc;
47
+ }
48
+ build(router) {
49
+ const routes = Object.values(router.routes);
50
+ for (const route of routes) {
51
+ if (IsRouterContract(route)) {
52
+ this.build(route);
53
+ }
54
+ else if (IsProcedureContract(route)) {
55
+ this.procedures.set(route.name, route);
56
+ }
57
+ }
58
+ }
41
59
  }
42
60
  export class RuntimeClient extends BaseClient {
43
61
  contract;
44
62
  transformer;
63
+ procedures = new Map();
45
64
  constructor(contract, ...args) {
46
65
  super(...args);
47
66
  this.contract = contract;
48
- this.transformer = new RuntimeContractTransformer(this.contract);
49
- const callers = {};
50
- const namespaces = Object.entries(this.contract.namespaces);
51
- for (const [namespaceKey, namespace] of namespaces) {
52
- callers[namespaceKey] = {};
53
- const procedures = Object.entries(namespace.procedures);
54
- for (const [procedureKey, procedure] of procedures) {
55
- callers[namespaceKey][procedureKey] = (payload, options) => this._call(namespace.name, procedure.name, payload, {
56
- timeout: procedure.timeout || namespace.timeout || options.timeout,
57
- ...options,
58
- });
67
+ this.resolveProcedures(this.contract.router);
68
+ this.transformer = new RuntimeContractTransformer(this.procedures);
69
+ this.callers = this.buildCallers();
70
+ }
71
+ resolveProcedures(router) {
72
+ const routes = Object.values(router.routes);
73
+ for (const route of routes) {
74
+ if (IsRouterContract(route)) {
75
+ this.resolveProcedures(route);
76
+ }
77
+ else if (IsProcedureContract(route)) {
78
+ this.procedures.set(route.name, route);
79
+ }
80
+ }
81
+ }
82
+ buildCallers() {
83
+ const callers = Object.create(null);
84
+ for (const [name, procedure] of this.procedures) {
85
+ const parts = name.split('/');
86
+ let current = callers;
87
+ for (let i = 0; i < parts.length; i++) {
88
+ const part = parts[i];
89
+ if (i === parts.length - 1) {
90
+ current[part] = (payload, options = {}) => this._call(name, payload, {
91
+ timeout: procedure.timeout || options.timeout || this.options.timeout,
92
+ ...options,
93
+ });
94
+ }
95
+ else {
96
+ if (!current[part]) {
97
+ current[part] = {};
98
+ }
99
+ current = current[part];
100
+ }
59
101
  }
60
102
  }
61
- this.callers = callers;
103
+ return callers;
62
104
  }
63
105
  }
package/dist/static.d.ts CHANGED
@@ -1,7 +1,9 @@
1
1
  import type { TAnyAPIContract } from '@nmtjs/contract';
2
2
  import { ProtocolBaseTransformer } from '@nmtjs/protocol/client';
3
+ import type { StaticInputContractTypeProvider, StaticOutputContractTypeProvider } from './common.ts';
3
4
  import { BaseClient } from './common.ts';
4
- export declare class StaticClient<APIContract extends TAnyAPIContract, SafeCall extends boolean = false> extends BaseClient<APIContract, SafeCall> {
5
+ export declare class StaticClient<APIContract extends TAnyAPIContract, SafeCall extends boolean = false> extends BaseClient<APIContract, SafeCall, StaticInputContractTypeProvider, StaticOutputContractTypeProvider> {
5
6
  protected transformer: ProtocolBaseTransformer;
6
7
  constructor(...args: ConstructorParameters<typeof BaseClient<APIContract, SafeCall>>);
8
+ protected createProxy(target: any, path?: string[]): any;
7
9
  }
package/dist/static.js CHANGED
@@ -5,26 +5,22 @@ export class StaticClient extends BaseClient {
5
5
  constructor(...args) {
6
6
  super(...args);
7
7
  this.transformer = new ProtocolBaseTransformer();
8
- this.callers = new Proxy(Object(), {
9
- get: (target, namespace) => {
10
- // `await client.call.namespaceName` or `await client.call.namespaceName.procedureName`
11
- // without explicitly calling a function implicitly calls .then() on target
12
- // FIXME: this basically makes "then" a reserved word
13
- if (namespace === 'then')
14
- return target;
15
- return new Proxy(Object(), {
16
- get: (target, procedure) => {
17
- // `await client.call.namespaceName` or `await client.call.namespaceName.procedureName`
18
- // without explicitly calling a function implicitly calls .then() on target
19
- // FIXME: this basically makes "then" a reserved word
20
- if (procedure === 'then')
21
- return target;
22
- return (payload, options) => this._call(namespace, procedure, payload, {
23
- ...options,
24
- timeout: options?.timeout ?? this.options.timeout,
25
- });
26
- },
8
+ this.callers = this.createProxy(Object.create(null));
9
+ }
10
+ createProxy(target, path = []) {
11
+ return new Proxy(target, {
12
+ get: (obj, prop) => {
13
+ // `await client.call.something` or `await client.call.something.nested`
14
+ // without explicitly calling a function implicitly calls .then() on a target
15
+ // FIXME: this basically makes "then" a reserved word for static Client
16
+ if (prop === 'then')
17
+ return obj;
18
+ const newPath = [...path, String(prop)];
19
+ const caller = (payload, options) => this._call(newPath.join('/'), payload, {
20
+ ...options,
21
+ timeout: options?.timeout ?? this.options.timeout,
27
22
  });
23
+ return this.createProxy(caller, newPath);
28
24
  },
29
25
  });
30
26
  }
package/dist/types.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { CallTypeProvider, OneOf, TypeProvider } from '@nmtjs/common';
2
- import type { TAnyAPIContract, TAnyProcedureContract } from '@nmtjs/contract';
2
+ import type { TAnyAPIContract, TAnyProcedureContract, TAnyRouterContract } from '@nmtjs/contract';
3
3
  import type { InputType, OutputType, ProtocolBaseClientCallOptions, ProtocolError, ProtocolServerStreamInterface } from '@nmtjs/protocol/client';
4
4
  import type { BaseTypeAny, t } from '@nmtjs/type';
5
5
  export interface StaticInputContractTypeProvider extends TypeProvider {
@@ -14,57 +14,41 @@ export interface StaticOutputContractTypeProvider extends TypeProvider {
14
14
  export interface RuntimeOutputContractTypeProvider extends TypeProvider {
15
15
  output: this['input'] extends BaseTypeAny ? t.infer.decoded.output<this['input']> : never;
16
16
  }
17
- export type AnyResolvedAPIContract = Record<string, {
18
- procedures: Record<string, {
19
- contract: TAnyProcedureContract;
20
- input: any;
21
- output: any;
22
- }>;
23
- events: Record<string, {
24
- payload: any;
25
- }>;
26
- }>;
27
- export type ResolveAPIContract<C extends TAnyAPIContract = TAnyAPIContract, InputTypeProvider extends TypeProvider = TypeProvider, OutputTypeProvider extends TypeProvider = TypeProvider> = {
28
- [N in keyof C['namespaces']]: {
29
- procedures: {
30
- [P in keyof C['namespaces'][N]['procedures']]: {
31
- contract: C['namespaces'][N]['procedures'][P];
32
- input: InputType<CallTypeProvider<InputTypeProvider, C['namespaces'][N]['procedures'][P]['input']>>;
33
- output: C['namespaces'][N]['procedures'][P]['stream'] extends undefined | t.NeverType ? OutputType<CallTypeProvider<OutputTypeProvider, C['namespaces'][N]['procedures'][P]['output']>> : {
34
- result: OutputType<CallTypeProvider<OutputTypeProvider, C['namespaces'][N]['procedures'][P]['output']>>;
35
- stream: ProtocolServerStreamInterface<CallTypeProvider<OutputTypeProvider, C['namespaces'][N]['procedures'][P]['stream']>>;
36
- };
37
- };
38
- };
39
- events: {
40
- [KE in keyof C['namespaces'][N]['events']]: {
41
- payload: OutputType<CallTypeProvider<OutputTypeProvider, C['namespaces'][N]['events'][KE]['payload']>>;
42
- };
17
+ export type AnyResolvedAPIContractProcedure = {
18
+ contract: TAnyProcedureContract;
19
+ input: any;
20
+ output: any;
21
+ };
22
+ export type AnyResolvedAPIContractRouter = {
23
+ contract: TAnyRouterContract;
24
+ routes: Record<string, AnyResolvedAPIContractRouter | AnyResolvedAPIContractProcedure>;
25
+ };
26
+ export type AnyResolvedAPIContract = Record<string, Record<string, AnyResolvedAPIContractProcedure | AnyResolvedAPIContractRouter>>;
27
+ export type ResolveAPIRouterRoutes<T extends TAnyRouterContract, InputTypeProvider extends TypeProvider = TypeProvider, OutputTypeProvider extends TypeProvider = TypeProvider> = {
28
+ [K in keyof T['routes']]: T['routes'][K] extends TAnyRouterContract ? {
29
+ contract: T['routes'][K];
30
+ routes: ResolveAPIRouterRoutes<T['routes'][K], InputTypeProvider, OutputTypeProvider>;
31
+ } : T['routes'][K] extends TAnyProcedureContract ? {
32
+ contract: T['routes'][K];
33
+ input: InputType<CallTypeProvider<InputTypeProvider, T['routes'][K]['input']>>;
34
+ output: T['routes'][K]['stream'] extends undefined | t.NeverType ? OutputType<CallTypeProvider<OutputTypeProvider, T['routes'][K]['output']>> : {
35
+ result: OutputType<CallTypeProvider<OutputTypeProvider, T['routes'][K]['output']>>;
36
+ stream: ProtocolServerStreamInterface<CallTypeProvider<OutputTypeProvider, T['routes'][K]['stream']>>;
43
37
  };
44
- };
38
+ } : never;
45
39
  };
46
- export type ResolveClientEvents<C extends AnyResolvedAPIContract = AnyResolvedAPIContract> = {
47
- [N in keyof C]: {
48
- [E in keyof C[N]['events'] as `${Extract<N, string>}/${Extract<E, string>}`]: [
49
- C[N]['events'][E]['payload']
50
- ];
51
- };
52
- }[keyof C];
53
- export type ClientCallers<Resolved extends AnyResolvedAPIContract, SafeCall extends boolean> = {
54
- [N in keyof Resolved]: {
55
- [P in keyof Resolved[N]['procedures']]: (...args: Resolved[N]['procedures'][P]['input'] extends t.NeverType ? [data?: undefined, options?: Partial<ProtocolBaseClientCallOptions>] : undefined extends t.infer.encoded.input<Resolved[N]['procedures'][P]['contract']['input']> ? [
56
- data?: Resolved[N]['procedures'][P]['input'],
57
- options?: Partial<ProtocolBaseClientCallOptions>
58
- ] : [
59
- data: Resolved[N]['procedures'][P]['input'],
60
- options?: Partial<ProtocolBaseClientCallOptions>
61
- ]) => SafeCall extends true ? Promise<OneOf<[
62
- {
63
- output: Resolved[N]['procedures'][P]['output'];
64
- },
65
- {
66
- error: ProtocolError;
67
- }
68
- ]>> : Promise<Resolved[N]['procedures'][P]['output']>;
69
- };
40
+ export type ResolveAPIContract<C extends TAnyAPIContract = TAnyAPIContract, InputTypeProvider extends TypeProvider = TypeProvider, OutputTypeProvider extends TypeProvider = TypeProvider> = ResolveAPIRouterRoutes<C['router'], InputTypeProvider, OutputTypeProvider>;
41
+ export type ClientCaller<Procedure extends AnyResolvedAPIContractProcedure, SafeCall extends boolean> = (...args: Procedure['input'] extends t.NeverType ? [data?: undefined, options?: Partial<ProtocolBaseClientCallOptions>] : undefined extends t.infer.encoded.input<Procedure['contract']['input']> ? [
42
+ data?: Procedure['input'],
43
+ options?: Partial<ProtocolBaseClientCallOptions>
44
+ ] : [
45
+ data: Procedure['input'],
46
+ options?: Partial<ProtocolBaseClientCallOptions>
47
+ ]) => SafeCall extends true ? Promise<OneOf<[{
48
+ output: Procedure['output'];
49
+ }, {
50
+ error: ProtocolError;
51
+ }]>> : Promise<Procedure['output']>;
52
+ export type ClientCallers<Resolved extends AnyResolvedAPIContractRouter, SafeCall extends boolean> = {
53
+ [K in keyof Resolved['routes']]: Resolved['routes'][K] extends AnyResolvedAPIContractProcedure ? ClientCaller<Resolved['routes'][K], SafeCall> : Resolved['routes'][K] extends AnyResolvedAPIContractRouter ? ClientCallers<Resolved['routes'][K], SafeCall> : never;
70
54
  };
package/package.json CHANGED
@@ -19,23 +19,23 @@
19
19
  }
20
20
  },
21
21
  "peerDependencies": {
22
- "@nmtjs/type": "0.13.0",
23
- "@nmtjs/contract": "0.13.0",
24
- "@nmtjs/common": "0.13.0",
25
- "@nmtjs/protocol": "0.13.0"
22
+ "@nmtjs/type": "0.14.0",
23
+ "@nmtjs/contract": "0.14.0",
24
+ "@nmtjs/common": "0.14.0",
25
+ "@nmtjs/protocol": "0.14.0"
26
26
  },
27
27
  "devDependencies": {
28
- "@nmtjs/type": "0.13.0",
29
- "@nmtjs/common": "0.13.0",
30
- "@nmtjs/contract": "0.13.0",
31
- "@nmtjs/protocol": "0.13.0"
28
+ "@nmtjs/type": "0.14.0",
29
+ "@nmtjs/contract": "0.14.0",
30
+ "@nmtjs/common": "0.14.0",
31
+ "@nmtjs/protocol": "0.14.0"
32
32
  },
33
33
  "files": [
34
34
  "dist",
35
35
  "LICENSE.md",
36
36
  "README.md"
37
37
  ],
38
- "version": "0.13.0",
38
+ "version": "0.14.0",
39
39
  "scripts": {
40
40
  "build": "tsc",
41
41
  "type-check": "tsc --noEmit"