@grest-ts/http 0.0.5 → 0.0.7

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.
@@ -1,107 +1,107 @@
1
- import {ANY_ERROR, ERROR, GGContractApiDefinition, GGContractClient, GGContractExecutor, GGContractImplementation, GGPromise, OK, SERVER_ERROR} from "@grest-ts/schema"
2
- import {ClientHttpRouteToRpcTransformClientCodec, GGHttpCodec, GGHttpSchema} from "../schema/GGHttpSchema";
3
- import {isBrowser} from "@grest-ts/common";
4
-
5
-
6
- declare module "../schema/GGHttpSchema" {
7
- interface GGHttpSchema<TContract extends GGContractApiDefinition, TContext = {}> {
8
- createClient(config?: GGHttpClientConfig): GGContractClient<TContract>
9
- }
10
- }
11
-
12
- export interface GGHttpClientConfig {
13
- url?: string;
14
- timeout?: number;
15
- noValidation?: boolean
16
- }
17
-
18
- GGHttpSchema.prototype.createClient = function <TContract extends GGContractApiDefinition, TContext>(
19
- this: GGHttpSchema<TContract, TContext>,
20
- config?: GGHttpClientConfig
21
- ): GGContractClient<TContract> {
22
- return createClient(this, config);
23
- }
24
-
25
- export function createClient<TContract extends GGContractApiDefinition, TContext>(
26
- httpSchema: GGHttpSchema<TContract, TContext>,
27
- config?: GGHttpClientConfig
28
- ): GGContractClient<TContract> {
29
- config ??= {};
30
- config.timeout ??= 15000;
31
-
32
- if (config.url === undefined && isBrowser()) {
33
- throw new Error("Must define URL for GGHttpClient when running in browser! Use empty string for same-origin requests.");
34
- }
35
-
36
- const pathPrefix = "/" + httpSchema.pathPrefix + "/";
37
-
38
- const transportImplementation: any = {}
39
- for (const methodName of Object.keys(httpSchema.codec)) {
40
-
41
- const contractFunction = httpSchema.contract.methods[methodName];
42
- const noValidation = config?.noValidation === true;
43
-
44
- const codec: GGHttpCodec = httpSchema.codec[methodName];
45
- const wireFormat: ClientHttpRouteToRpcTransformClientCodec = codec.createForClient({
46
- pathPrefix: pathPrefix,
47
- contract: contractFunction,
48
- middlewares: httpSchema.apiMiddlewares
49
- })
50
-
51
- const implementation = async (data?: unknown): Promise<OK<unknown> | ANY_ERROR> => {
52
- try {
53
- let baseUrl: string = config.url;
54
- if (baseUrl === undefined) {
55
- try {
56
- const {GG_DISCOVERY} = await import(/* @vite-ignore */ '@grest-ts/discovery');
57
- baseUrl = await GG_DISCOVERY.get().discoverApi(httpSchema.name);
58
- } catch (err) {
59
- throw new SERVER_ERROR({displayMessage: "Service discovery failed", originalError: err});
60
- }
61
- }
62
-
63
- // ---------------------------------------------
64
- // Input validation
65
- const validatedInput = noValidation ? data : GGContractExecutor.parseInput(contractFunction.input, data)
66
-
67
- // ---------------------------------------------
68
- // Execution
69
- const fetchRequest = await wireFormat.createRequest(validatedInput);
70
- const controller = new AbortController();
71
- const timeoutId = setTimeout(() => controller.abort(), config.timeout);
72
- const wireResponse = await fetch(baseUrl + fetchRequest.url, {
73
- method: fetchRequest.method,
74
- signal: controller.signal,
75
- headers: fetchRequest.headers,
76
- body: fetchRequest.body
77
- }).finally(() => clearTimeout(timeoutId));
78
- const resData = await wireFormat.parseResponse(wireResponse);
79
-
80
- // ---------------------------------------------
81
- // Response handling
82
- const schema = GGContractExecutor.getResponseSchema(contractFunction, resData);
83
- if (schema) {
84
- resData.data = noValidation ? resData.data : GGContractExecutor.parseOutputData(schema, resData.data);
85
- } else if (resData.data !== undefined) {
86
- resData.data = undefined
87
- }
88
- if (resData.success === true) {
89
- return resData as OK<unknown>;
90
- } else {
91
- return GGContractExecutor.createErrorObj(resData, contractFunction.errors);
92
- }
93
- // ---------------------------------------------
94
- } catch (error) {
95
- return ERROR.fromUnknown(error);
96
- }
97
- };
98
-
99
- transportImplementation[methodName] = (data?: unknown) => {
100
- return new GGPromise(implementation(data))
101
- }
102
-
103
- }
104
- // @TODO Next line is just so test would register it correctly.
105
- httpSchema.contract.implement(transportImplementation as GGContractImplementation<TContract>)
106
- return transportImplementation;
1
+ import {ANY_ERROR, ERROR, GGContractApiDefinition, GGContractClient, GGContractExecutor, GGContractImplementation, GGPromise, OK, SERVER_ERROR} from "@grest-ts/schema"
2
+ import {ClientHttpRouteToRpcTransformClientCodec, GGHttpCodec, GGHttpSchema} from "../schema/GGHttpSchema";
3
+ import {isBrowser} from "@grest-ts/common";
4
+
5
+
6
+ declare module "../schema/GGHttpSchema" {
7
+ interface GGHttpSchema<TContract extends GGContractApiDefinition, TContext = {}> {
8
+ createClient(config?: GGHttpClientConfig): GGContractClient<TContract>
9
+ }
10
+ }
11
+
12
+ export interface GGHttpClientConfig {
13
+ url?: string;
14
+ timeout?: number;
15
+ noValidation?: boolean
16
+ }
17
+
18
+ GGHttpSchema.prototype.createClient = function <TContract extends GGContractApiDefinition, TContext>(
19
+ this: GGHttpSchema<TContract, TContext>,
20
+ config?: GGHttpClientConfig
21
+ ): GGContractClient<TContract> {
22
+ return createClient(this, config);
23
+ }
24
+
25
+ export function createClient<TContract extends GGContractApiDefinition, TContext>(
26
+ httpSchema: GGHttpSchema<TContract, TContext>,
27
+ config?: GGHttpClientConfig
28
+ ): GGContractClient<TContract> {
29
+ config ??= {};
30
+ config.timeout ??= 15000;
31
+
32
+ if (config.url === undefined && isBrowser()) {
33
+ throw new Error("Must define URL for GGHttpClient when running in browser! Use empty string for same-origin requests.");
34
+ }
35
+
36
+ const pathPrefix = "/" + httpSchema.pathPrefix + "/";
37
+
38
+ const transportImplementation: any = {}
39
+ for (const methodName of Object.keys(httpSchema.codec)) {
40
+
41
+ const contractFunction = httpSchema.contract.methods[methodName];
42
+ const noValidation = config?.noValidation === true;
43
+
44
+ const codec: GGHttpCodec = httpSchema.codec[methodName];
45
+ const wireFormat: ClientHttpRouteToRpcTransformClientCodec = codec.createForClient({
46
+ pathPrefix: pathPrefix,
47
+ contract: contractFunction,
48
+ middlewares: httpSchema.apiMiddlewares
49
+ })
50
+
51
+ const implementation = async (data?: unknown): Promise<OK<unknown> | ANY_ERROR> => {
52
+ try {
53
+ let baseUrl: string = config.url;
54
+ if (baseUrl === undefined) {
55
+ try {
56
+ const {GG_DISCOVERY} = await import(/* @vite-ignore */ '@grest-ts/discovery');
57
+ baseUrl = await GG_DISCOVERY.get().discoverApi(httpSchema.name);
58
+ } catch (err) {
59
+ throw new SERVER_ERROR({displayMessage: "Service discovery failed", originalError: err});
60
+ }
61
+ }
62
+
63
+ // ---------------------------------------------
64
+ // Input validation
65
+ const validatedInput = noValidation ? data : GGContractExecutor.parseInput(contractFunction.input, data)
66
+
67
+ // ---------------------------------------------
68
+ // Execution
69
+ const fetchRequest = await wireFormat.createRequest(validatedInput);
70
+ const controller = new AbortController();
71
+ const timeoutId = setTimeout(() => controller.abort(), config.timeout);
72
+ const wireResponse = await fetch(baseUrl + fetchRequest.url, {
73
+ method: fetchRequest.method,
74
+ signal: controller.signal,
75
+ headers: fetchRequest.headers,
76
+ body: fetchRequest.body
77
+ }).finally(() => clearTimeout(timeoutId));
78
+ const resData = await wireFormat.parseResponse(wireResponse);
79
+
80
+ // ---------------------------------------------
81
+ // Response handling
82
+ const schema = GGContractExecutor.getResponseSchema(contractFunction, resData);
83
+ if (schema) {
84
+ resData.data = noValidation ? resData.data : GGContractExecutor.parseOutputData(schema, resData.data);
85
+ } else if (resData.data !== undefined) {
86
+ resData.data = undefined
87
+ }
88
+ if (resData.success === true) {
89
+ return resData as OK<unknown>;
90
+ } else {
91
+ return GGContractExecutor.createErrorObj(resData, contractFunction.errors);
92
+ }
93
+ // ---------------------------------------------
94
+ } catch (error) {
95
+ return ERROR.fromUnknown(error);
96
+ }
97
+ };
98
+
99
+ transportImplementation[methodName] = (data?: unknown) => {
100
+ return new GGPromise(implementation(data))
101
+ }
102
+
103
+ }
104
+ // @TODO Next line is just so test would register it correctly.
105
+ httpSchema.contract.implement(transportImplementation as GGContractImplementation<TContract>)
106
+ return transportImplementation;
107
107
  }
@@ -1,12 +1,12 @@
1
- // API Schema
2
- export * from "./schema/GGHttpSchema";
3
- export * from "./schema/httpSchema";
4
- export * from "./rpc/GGHttpRouteRPC";
5
- export * from "./rpc/RpcRequest/GGRpcRequestBuilder";
6
- export * from "./rpc/RpcResponse/GGRpcResponseParser";
7
-
8
- // Client
9
- export * from "./client/GGHttpSchema.createClient";
10
-
11
- // Extensions
12
- import "./client/GGHttpSchema.createClient";
1
+ // API Schema
2
+ export * from "./schema/GGHttpSchema";
3
+ export * from "./schema/httpSchema";
4
+ export * from "./rpc/GGHttpRouteRPC";
5
+ export * from "./rpc/RpcRequest/GGRpcRequestBuilder";
6
+ export * from "./rpc/RpcResponse/GGRpcResponseParser";
7
+
8
+ // Client
9
+ export * from "./client/GGHttpSchema.createClient";
10
+
11
+ // Extensions
12
+ import "./client/GGHttpSchema.createClient";
package/src/index-node.ts CHANGED
@@ -1,38 +1,38 @@
1
- // Register server RPC codec factory (must be before other exports that use it)
2
- import {_registerRpcServerCodecFactory} from "./rpc/GGHttpRouteRPC";
3
- import {GGRpcResponseBuilder} from "./rpc/RpcResponse/GGRpcResponseBuilder";
4
- import {GGRpcRequestParser} from "./rpc/RpcRequest/GGRpcRequestParser";
5
- _registerRpcServerCodecFactory((method, path, config) => ({
6
- parseRequest: new GGRpcRequestParser(method, path, config).parseRequest,
7
- sendResponse: new GGRpcResponseBuilder(config).sendResponse
8
- }));
9
-
10
- // Metrics
11
- export * from "./server/GGHttpMetrics";
12
-
13
- // Context
14
- export * from "./server/GG_HTTP_REQUEST";
15
-
16
- // API Schema
17
- export * from "./schema/GGHttpSchema";
18
- export * from "./schema/httpSchema";
19
- export * from "./rpc/GGHttpRouteRPC";
20
- export * from "./rpc/RpcRequest/GGRpcRequestBuilder";
21
- export * from "./rpc/RpcRequest/GGRpcRequestParser";
22
- export * from "./rpc/RpcResponse/GGRpcResponseBuilder";
23
- export * from "./rpc/RpcResponse/GGRpcResponseParser";
24
-
25
- // Server (convenience builder)
26
- export * from "./server/GGHttp";
27
- export * from "./server/GGHttp";
28
- export * from "./server/GGHttpServer";
29
- export * from "./server/GG_HTTP_SERVER";
30
-
31
- // Client
32
- export * from "./client/GGHttpSchema.createClient";
33
- export * from "./server/GGHttpSchema.startServer";
34
-
35
-
36
- // Extensions
37
- import "./client/GGHttpSchema.createClient";
38
- import "./server/GGHttpSchema.startServer";
1
+ // Register server RPC codec factory (must be before other exports that use it)
2
+ import {_registerRpcServerCodecFactory} from "./rpc/GGHttpRouteRPC";
3
+ import {GGRpcResponseBuilder} from "./rpc/RpcResponse/GGRpcResponseBuilder";
4
+ import {GGRpcRequestParser} from "./rpc/RpcRequest/GGRpcRequestParser";
5
+ _registerRpcServerCodecFactory((method, path, config) => ({
6
+ parseRequest: new GGRpcRequestParser(method, path, config).parseRequest,
7
+ sendResponse: new GGRpcResponseBuilder(config).sendResponse
8
+ }));
9
+
10
+ // Metrics
11
+ export * from "./server/GGHttpMetrics";
12
+
13
+ // Context
14
+ export * from "./server/GG_HTTP_REQUEST";
15
+
16
+ // API Schema
17
+ export * from "./schema/GGHttpSchema";
18
+ export * from "./schema/httpSchema";
19
+ export * from "./rpc/GGHttpRouteRPC";
20
+ export * from "./rpc/RpcRequest/GGRpcRequestBuilder";
21
+ export * from "./rpc/RpcRequest/GGRpcRequestParser";
22
+ export * from "./rpc/RpcResponse/GGRpcResponseBuilder";
23
+ export * from "./rpc/RpcResponse/GGRpcResponseParser";
24
+
25
+ // Server (convenience builder)
26
+ export * from "./server/GGHttp";
27
+ export * from "./server/GGHttp";
28
+ export * from "./server/GGHttpServer";
29
+ export * from "./server/GG_HTTP_SERVER";
30
+
31
+ // Client
32
+ export * from "./client/GGHttpSchema.createClient";
33
+ export * from "./server/GGHttpSchema.startServer";
34
+
35
+
36
+ // Extensions
37
+ import "./client/GGHttpSchema.createClient";
38
+ import "./server/GGHttpSchema.startServer";
@@ -1,42 +1,42 @@
1
- import {HttpMethod} from "@grest-ts/common"
2
- import {ClientHttpRouteToRpcTransformClientCodec, ClientHttpRouteToRpcTransformClientConfig, ClientHttpRouteToRpcTransformServerCodec, ClientHttpRouteToRpcTransformServerConfig, GGHttpCodec} from "../schema/GGHttpSchema"
3
- import {GGRpcRequestBuilder} from "./RpcRequest/GGRpcRequestBuilder";
4
- import {GGRpcResponseParser} from "./RpcResponse/GGRpcResponseParser";
5
-
6
- export type GGRpcServerCodecFactory = (method: HttpMethod, path: string, config: ClientHttpRouteToRpcTransformServerConfig) => ClientHttpRouteToRpcTransformServerCodec;
7
-
8
- let _serverCodecFactory: GGRpcServerCodecFactory | undefined;
9
-
10
- export function _registerRpcServerCodecFactory(factory: GGRpcServerCodecFactory): void {
11
- _serverCodecFactory = factory;
12
- }
13
-
14
- export const GGRpc = {
15
- GET: (path: string) => new GGHttpRpcCodec("GET", path),
16
- DELETE: (path: string) => new GGHttpRpcCodec("DELETE", path),
17
- POST: (path: string) => new GGHttpRpcCodec("POST", path),
18
- PUT: (path: string) => new GGHttpRpcCodec("PUT", path),
19
- }
20
-
21
- class GGHttpRpcCodec implements GGHttpCodec {
22
-
23
- public readonly method: HttpMethod
24
- public readonly path: string
25
-
26
- constructor(method: HttpMethod, path: string) {
27
- this.method = method
28
- this.path = path
29
- }
30
-
31
- public createForClient(config: ClientHttpRouteToRpcTransformClientConfig): ClientHttpRouteToRpcTransformClientCodec {
32
- return {
33
- createRequest: new GGRpcRequestBuilder(this.method, this.path, config).createRequest,
34
- parseResponse: new GGRpcResponseParser(config).parseResponse
35
- }
36
- }
37
-
38
- public createForServer(config: ClientHttpRouteToRpcTransformServerConfig): ClientHttpRouteToRpcTransformServerCodec {
39
- if (!_serverCodecFactory) throw new Error("Server RPC codec not available. Ensure @grest-ts/http server entry is imported.");
40
- return _serverCodecFactory(this.method, this.path, config);
41
- }
42
- }
1
+ import {HttpMethod} from "@grest-ts/common"
2
+ import {ClientHttpRouteToRpcTransformClientCodec, ClientHttpRouteToRpcTransformClientConfig, ClientHttpRouteToRpcTransformServerCodec, ClientHttpRouteToRpcTransformServerConfig, GGHttpCodec} from "../schema/GGHttpSchema"
3
+ import {GGRpcRequestBuilder} from "./RpcRequest/GGRpcRequestBuilder";
4
+ import {GGRpcResponseParser} from "./RpcResponse/GGRpcResponseParser";
5
+
6
+ export type GGRpcServerCodecFactory = (method: HttpMethod, path: string, config: ClientHttpRouteToRpcTransformServerConfig) => ClientHttpRouteToRpcTransformServerCodec;
7
+
8
+ let _serverCodecFactory: GGRpcServerCodecFactory | undefined;
9
+
10
+ export function _registerRpcServerCodecFactory(factory: GGRpcServerCodecFactory): void {
11
+ _serverCodecFactory = factory;
12
+ }
13
+
14
+ export const GGRpc = {
15
+ GET: (path: string) => new GGHttpRpcCodec("GET", path),
16
+ DELETE: (path: string) => new GGHttpRpcCodec("DELETE", path),
17
+ POST: (path: string) => new GGHttpRpcCodec("POST", path),
18
+ PUT: (path: string) => new GGHttpRpcCodec("PUT", path),
19
+ }
20
+
21
+ class GGHttpRpcCodec implements GGHttpCodec {
22
+
23
+ public readonly method: HttpMethod
24
+ public readonly path: string
25
+
26
+ constructor(method: HttpMethod, path: string) {
27
+ this.method = method
28
+ this.path = path
29
+ }
30
+
31
+ public createForClient(config: ClientHttpRouteToRpcTransformClientConfig): ClientHttpRouteToRpcTransformClientCodec {
32
+ return {
33
+ createRequest: new GGRpcRequestBuilder(this.method, this.path, config).createRequest,
34
+ parseResponse: new GGRpcResponseParser(config).parseResponse
35
+ }
36
+ }
37
+
38
+ public createForServer(config: ClientHttpRouteToRpcTransformServerConfig): ClientHttpRouteToRpcTransformServerCodec {
39
+ if (!_serverCodecFactory) throw new Error("Server RPC codec not available. Ensure @grest-ts/http server entry is imported.");
40
+ return _serverCodecFactory(this.method, this.path, config);
41
+ }
42
+ }
@@ -1,91 +1,91 @@
1
- import type {HttpMethod} from "@grest-ts/common";
2
- import {GGContractMethod} from "@grest-ts/schema";
3
- import {ClientHttpRouteToRpcTransformClientConfig, GGHttpFetchRequest, GGHttpTransportMiddleware} from "../../schema/GGHttpSchema";
4
-
5
- export class GGRpcRequestBuilder {
6
-
7
- public readonly contract: GGContractMethod
8
- public readonly middlewares: readonly GGHttpTransportMiddleware[]
9
- public readonly method: HttpMethod;
10
- public readonly pathTemplate: string;
11
- public readonly pathParams: string[];
12
- public readonly pathPrefix: string;
13
- public readonly hasBody: boolean;
14
-
15
- constructor(
16
- method: HttpMethod,
17
- pathTemplate: string,
18
- config: ClientHttpRouteToRpcTransformClientConfig
19
- ) {
20
- this.method = method
21
- this.pathTemplate = pathTemplate
22
- this.pathPrefix = config.pathPrefix
23
- this.contract = config.contract
24
- this.middlewares = config.middlewares
25
- this.pathParams = (pathTemplate.match(/:(\w+)/g) || []).map(m => m.slice(1))
26
- this.hasBody = method === "POST" || method === "PUT" || method === "PATCH"
27
- }
28
-
29
- public createRequest = (data: unknown): GGHttpFetchRequest => {
30
- let result: GGHttpFetchRequest;
31
- if (this.hasBody) {
32
- result = {
33
- url: this.pathPrefix + this.buildPath(data),
34
- method: this.method,
35
- headers: {'Content-Type': 'application/json'},
36
- body: this.buildBody(data)
37
- }
38
- } else {
39
- result = {
40
- url: this.pathPrefix + this.buildPath(data) + this.buildQueryString(data as Record<string, unknown>),
41
- method: this.method,
42
- headers: {},
43
- body: undefined
44
- }
45
- }
46
- this.middlewares?.forEach(mw => mw.updateRequest?.(result))
47
- return result
48
- }
49
-
50
- private buildPath(data: unknown) {
51
- let path: string = this.pathTemplate;
52
- if (this.pathParams.length > 0 && typeof data === "object" && data) {
53
- for (let i = 0; i < this.pathParams.length; i++) {
54
- const p = this.pathParams[i]
55
- const val = (data as Record<string, unknown>)[p]
56
- path = path.replace(':' + p, encodeURIComponent(val === undefined || val === null ? "" : String(val)))
57
- }
58
- }
59
- return path;
60
- }
61
-
62
- private buildBody(data: unknown) {
63
- if (data === undefined || data === null) {
64
- return undefined;
65
- }
66
- if (this.contract.input?.def.hasNonJsonData) {
67
- throw new Error("Schema contains non-JSON data (e.g. files). Use GGRpc.MULTIPART_POST instead of GGRpc.POST for this route.")
68
- } else {
69
- return JSON.stringify(data)
70
- }
71
- }
72
-
73
- private buildQueryString(data: unknown): string {
74
- if (data && typeof data === "object") {
75
- const params = new URLSearchParams()
76
- for (const [key, value] of Object.entries(data)) {
77
- if (value !== undefined && value !== null) {
78
- if (this.pathParams.length > 0 && this.pathParams.includes(key)) {
79
- continue;
80
- }
81
- params.append(key, String(value))
82
- }
83
- }
84
- const query = params.toString()
85
- if (query) {
86
- return "?" + query
87
- }
88
- }
89
- return ""
90
- }
91
- }
1
+ import type {HttpMethod} from "@grest-ts/common";
2
+ import {GGContractMethod} from "@grest-ts/schema";
3
+ import {ClientHttpRouteToRpcTransformClientConfig, GGHttpFetchRequest, GGHttpTransportMiddleware} from "../../schema/GGHttpSchema";
4
+
5
+ export class GGRpcRequestBuilder {
6
+
7
+ public readonly contract: GGContractMethod
8
+ public readonly middlewares: readonly GGHttpTransportMiddleware[]
9
+ public readonly method: HttpMethod;
10
+ public readonly pathTemplate: string;
11
+ public readonly pathParams: string[];
12
+ public readonly pathPrefix: string;
13
+ public readonly hasBody: boolean;
14
+
15
+ constructor(
16
+ method: HttpMethod,
17
+ pathTemplate: string,
18
+ config: ClientHttpRouteToRpcTransformClientConfig
19
+ ) {
20
+ this.method = method
21
+ this.pathTemplate = pathTemplate
22
+ this.pathPrefix = config.pathPrefix
23
+ this.contract = config.contract
24
+ this.middlewares = config.middlewares
25
+ this.pathParams = (pathTemplate.match(/:(\w+)/g) || []).map(m => m.slice(1))
26
+ this.hasBody = method === "POST" || method === "PUT" || method === "PATCH"
27
+ }
28
+
29
+ public createRequest = (data: unknown): GGHttpFetchRequest => {
30
+ let result: GGHttpFetchRequest;
31
+ if (this.hasBody) {
32
+ result = {
33
+ url: this.pathPrefix + this.buildPath(data),
34
+ method: this.method,
35
+ headers: {'Content-Type': 'application/json'},
36
+ body: this.buildBody(data)
37
+ }
38
+ } else {
39
+ result = {
40
+ url: this.pathPrefix + this.buildPath(data) + this.buildQueryString(data as Record<string, unknown>),
41
+ method: this.method,
42
+ headers: {},
43
+ body: undefined
44
+ }
45
+ }
46
+ this.middlewares?.forEach(mw => mw.updateRequest?.(result))
47
+ return result
48
+ }
49
+
50
+ private buildPath(data: unknown) {
51
+ let path: string = this.pathTemplate;
52
+ if (this.pathParams.length > 0 && typeof data === "object" && data) {
53
+ for (let i = 0; i < this.pathParams.length; i++) {
54
+ const p = this.pathParams[i]
55
+ const val = (data as Record<string, unknown>)[p]
56
+ path = path.replace(':' + p, encodeURIComponent(val === undefined || val === null ? "" : String(val)))
57
+ }
58
+ }
59
+ return path;
60
+ }
61
+
62
+ private buildBody(data: unknown) {
63
+ if (data === undefined || data === null) {
64
+ return undefined;
65
+ }
66
+ if (this.contract.input?.def.hasNonJsonData) {
67
+ throw new Error("Schema contains non-JSON data (e.g. files). Use GGRpc.MULTIPART_POST instead of GGRpc.POST for this route.")
68
+ } else {
69
+ return JSON.stringify(data)
70
+ }
71
+ }
72
+
73
+ private buildQueryString(data: unknown): string {
74
+ if (data && typeof data === "object") {
75
+ const params = new URLSearchParams()
76
+ for (const [key, value] of Object.entries(data)) {
77
+ if (value !== undefined && value !== null) {
78
+ if (this.pathParams.length > 0 && this.pathParams.includes(key)) {
79
+ continue;
80
+ }
81
+ params.append(key, String(value))
82
+ }
83
+ }
84
+ const query = params.toString()
85
+ if (query) {
86
+ return "?" + query
87
+ }
88
+ }
89
+ return ""
90
+ }
91
+ }