@orpc/client 0.0.0-next.2ba0691 → 0.0.0-next.31590a1

Sign up to get free protection for your applications and to get access to all the features.
package/dist/fetch.js CHANGED
@@ -1,52 +1,103 @@
1
1
  // src/adapters/fetch/orpc-link.ts
2
- import { ORPCPayloadCodec } from "@orpc/server/fetch";
3
- import { ORPC_HANDLER_HEADER, ORPC_HANDLER_VALUE, trim } from "@orpc/shared";
4
- import { ORPCError } from "@orpc/shared/error";
5
- var ORPCLink = class {
2
+ import { ORPCError } from "@orpc/contract";
3
+ import { fetchReToStandardBody } from "@orpc/server/fetch";
4
+ import { RPCSerializer } from "@orpc/server/standard";
5
+ import { isPlainObject, trim } from "@orpc/shared";
6
+ import cd from "content-disposition";
7
+ var RPCLink = class {
8
+ fetch;
9
+ rpcSerializer;
10
+ maxURLLength;
11
+ fallbackMethod;
12
+ getMethod;
13
+ getHeaders;
14
+ url;
6
15
  constructor(options) {
7
- this.options = options;
8
16
  this.fetch = options.fetch ?? globalThis.fetch.bind(globalThis);
9
- this.payloadCodec = options.payloadCodec ?? new ORPCPayloadCodec();
17
+ this.rpcSerializer = options.rpcSerializer ?? new RPCSerializer();
18
+ this.maxURLLength = options.maxURLLength ?? 2083;
19
+ this.fallbackMethod = options.fallbackMethod ?? "POST";
20
+ this.url = options.url;
21
+ this.getMethod = async (path, input, context) => {
22
+ return await options.method?.(path, input, context) ?? this.fallbackMethod;
23
+ };
24
+ this.getHeaders = async (path, input, context) => {
25
+ return new Headers(await options.headers?.(path, input, context));
26
+ };
10
27
  }
11
- fetch;
12
- payloadCodec;
13
28
  async call(path, input, options) {
14
29
  const clientContext = options.context;
15
- const url = new URL(`${trim(this.options.url, "/")}/${path.map(encodeURIComponent).join("/")}`);
16
- const method = await this.options.method?.(path, input, clientContext) ?? "POST";
17
- const encoded = this.payloadCodec.encode(input, method);
18
- if (encoded.query) {
19
- for (const [key, value] of encoded.query.entries()) {
20
- url.searchParams.append(key, value);
21
- }
22
- }
23
- const headers = new Headers(encoded.headers);
24
- headers.append(ORPC_HANDLER_HEADER, ORPC_HANDLER_VALUE);
25
- let customHeaders = await this.options.headers?.(path, input, clientContext);
26
- customHeaders = customHeaders instanceof Headers ? customHeaders : new Headers(customHeaders);
27
- for (const [key, value] of customHeaders.entries()) {
28
- headers.append(key, value);
30
+ const encoded = await this.encode(path, input, options);
31
+ if (encoded.body instanceof Blob && !encoded.headers.has("content-disposition")) {
32
+ encoded.headers.set("content-disposition", cd(encoded.body instanceof File ? encoded.body.name : "blob"));
29
33
  }
30
- const response = await this.fetch(url, {
31
- method,
32
- headers,
34
+ const response = await this.fetch(encoded.url, {
35
+ method: encoded.method,
36
+ headers: encoded.headers,
33
37
  body: encoded.body,
34
38
  signal: options.signal
35
39
  }, clientContext);
36
- const decoded = await this.payloadCodec.decode(response);
37
- if (!response.ok) {
38
- const error = ORPCError.fromJSON(decoded) ?? new ORPCError({
39
- status: response.status,
40
- code: "INTERNAL_SERVER_ERROR",
41
- message: "Internal server error",
42
- cause: decoded
43
- });
44
- throw error;
40
+ const body = await fetchReToStandardBody(response);
41
+ const deserialized = (() => {
42
+ try {
43
+ return this.rpcSerializer.deserialize(body);
44
+ } catch (error) {
45
+ if (response.ok) {
46
+ throw new ORPCError("INTERNAL_SERVER_ERROR", {
47
+ message: "Invalid RPC response",
48
+ cause: error
49
+ });
50
+ }
51
+ throw new ORPCError(response.status.toString(), {
52
+ message: response.statusText
53
+ });
54
+ }
55
+ })();
56
+ if (response.ok) {
57
+ return deserialized;
45
58
  }
46
- return decoded;
59
+ throw ORPCError.fromJSON(deserialized);
60
+ }
61
+ async encode(path, input, options) {
62
+ const clientContext = options.context;
63
+ const expectMethod = await this.getMethod(path, input, clientContext);
64
+ const headers = await this.getHeaders(path, input, clientContext);
65
+ const url = new URL(`${trim(this.url, "/")}/${path.map(encodeURIComponent).join("/")}`);
66
+ headers.append("x-orpc-handler", "rpc");
67
+ const serialized = this.rpcSerializer.serialize(input);
68
+ if (expectMethod === "GET" && isPlainObject(serialized)) {
69
+ const tryURL = new URL(url);
70
+ tryURL.searchParams.append("data", JSON.stringify(serialized));
71
+ if (tryURL.toString().length <= this.maxURLLength) {
72
+ return {
73
+ body: void 0,
74
+ method: expectMethod,
75
+ headers,
76
+ url: tryURL
77
+ };
78
+ }
79
+ }
80
+ const method = expectMethod === "GET" ? this.fallbackMethod : expectMethod;
81
+ if (isPlainObject(serialized)) {
82
+ if (!headers.has("content-type")) {
83
+ headers.set("content-type", "application/json");
84
+ }
85
+ return {
86
+ body: JSON.stringify(serialized),
87
+ method,
88
+ headers,
89
+ url
90
+ };
91
+ }
92
+ return {
93
+ body: serialized,
94
+ method,
95
+ headers,
96
+ url
97
+ };
47
98
  }
48
99
  };
49
100
  export {
50
- ORPCLink
101
+ RPCLink
51
102
  };
52
103
  //# sourceMappingURL=fetch.js.map
package/dist/index.js CHANGED
@@ -31,9 +31,12 @@ var DynamicLink = class {
31
31
  };
32
32
 
33
33
  // src/index.ts
34
- export * from "@orpc/shared/error";
34
+ import { isDefinedError, ORPCError, safe } from "@orpc/contract";
35
35
  export {
36
36
  DynamicLink,
37
- createORPCClient
37
+ ORPCError,
38
+ createORPCClient,
39
+ isDefinedError,
40
+ safe
38
41
  };
39
42
  //# sourceMappingURL=index.js.map
@@ -1,21 +1,46 @@
1
- import type { HTTPMethod } from '@orpc/contract';
2
- import type { ProcedureClientOptions } from '@orpc/server';
1
+ import type { ClientOptions, HTTPMethod } from '@orpc/contract';
3
2
  import type { Promisable } from '@orpc/shared';
4
3
  import type { ClientLink } from '../../types';
5
4
  import type { FetchWithContext } from './types';
6
- import { type PublicORPCPayloadCodec } from '@orpc/server/fetch';
7
- export interface ORPCLinkOptions<TClientContext> {
5
+ import { RPCSerializer } from '@orpc/server/standard';
6
+ export interface RPCLinkOptions<TClientContext> {
7
+ /**
8
+ * Base url for all requests.
9
+ */
8
10
  url: string;
9
- method?: (path: readonly string[], input: unknown, context: TClientContext) => Promisable<HTTPMethod | undefined>;
10
- headers?: (path: readonly string[], input: unknown, context: TClientContext) => Promisable<Headers | Record<string, string>>;
11
+ /**
12
+ * The maximum length of the URL.
13
+ *
14
+ * @default 2083
15
+ */
16
+ maxURLLength?: number;
17
+ /**
18
+ * The method used to make the request.
19
+ *
20
+ * @default 'POST'
21
+ */
22
+ method?(path: readonly string[], input: unknown, context: TClientContext): Promisable<HTTPMethod | undefined>;
23
+ /**
24
+ * The method to use when the payload cannot safely pass to the server with method return from method function.
25
+ * GET is not allowed, it's very dangerous.
26
+ *
27
+ * @default 'POST'
28
+ */
29
+ fallbackMethod?: Exclude<HTTPMethod, 'GET'>;
30
+ headers?(path: readonly string[], input: unknown, context: TClientContext): Promisable<Headers | Record<string, string>>;
11
31
  fetch?: FetchWithContext<TClientContext>;
12
- payloadCodec?: PublicORPCPayloadCodec;
32
+ rpcSerializer?: RPCSerializer;
13
33
  }
14
- export declare class ORPCLink<TClientContext> implements ClientLink<TClientContext> {
15
- private readonly options;
34
+ export declare class RPCLink<TClientContext> implements ClientLink<TClientContext> {
16
35
  private readonly fetch;
17
- private readonly payloadCodec;
18
- constructor(options: ORPCLinkOptions<TClientContext>);
19
- call(path: readonly string[], input: unknown, options: ProcedureClientOptions<TClientContext>): Promise<unknown>;
36
+ private readonly rpcSerializer;
37
+ private readonly maxURLLength;
38
+ private readonly fallbackMethod;
39
+ private readonly getMethod;
40
+ private readonly getHeaders;
41
+ private readonly url;
42
+ constructor(options: RPCLinkOptions<TClientContext>);
43
+ call(path: readonly string[], input: unknown, options: ClientOptions<TClientContext>): Promise<unknown>;
44
+ private encode;
20
45
  }
21
46
  //# sourceMappingURL=orpc-link.d.ts.map
@@ -1,5 +1,5 @@
1
- import type { ContractRouter } from '@orpc/contract';
2
- import type { ANY_ROUTER, RouterClient } from '@orpc/server';
1
+ import type { AnyContractRouter, ContractRouterClient } from '@orpc/contract';
2
+ import type { AnyRouter, RouterClient } from '@orpc/server';
3
3
  import type { ClientLink } from './types';
4
4
  export interface createORPCClientOptions {
5
5
  /**
@@ -7,5 +7,5 @@ export interface createORPCClientOptions {
7
7
  */
8
8
  path?: string[];
9
9
  }
10
- export declare function createORPCClient<TRouter extends ANY_ROUTER | ContractRouter, TClientContext = unknown>(link: ClientLink<TClientContext>, options?: createORPCClientOptions): RouterClient<TRouter, TClientContext>;
10
+ export declare function createORPCClient<TRouter extends AnyRouter | AnyContractRouter, TClientContext = unknown>(link: ClientLink<TClientContext>, options?: createORPCClientOptions): TRouter extends AnyRouter ? RouterClient<TRouter, TClientContext> : TRouter extends AnyContractRouter ? ContractRouterClient<TRouter, TClientContext> : never;
11
11
  //# sourceMappingURL=client.d.ts.map
@@ -1,4 +1,4 @@
1
- import type { ProcedureClientOptions } from '@orpc/server';
1
+ import type { ClientOptions } from '@orpc/contract';
2
2
  import type { Promisable } from '@orpc/shared';
3
3
  import type { ClientLink } from './types';
4
4
  /**
@@ -8,6 +8,6 @@ import type { ClientLink } from './types';
8
8
  export declare class DynamicLink<TClientContext> implements ClientLink<TClientContext> {
9
9
  private readonly linkResolver;
10
10
  constructor(linkResolver: (path: readonly string[], input: unknown, context: TClientContext) => Promisable<ClientLink<TClientContext>>);
11
- call(path: readonly string[], input: unknown, options: ProcedureClientOptions<TClientContext>): Promise<unknown>;
11
+ call(path: readonly string[], input: unknown, options: ClientOptions<TClientContext>): Promise<unknown>;
12
12
  }
13
13
  //# sourceMappingURL=dynamic-link.d.ts.map
@@ -2,5 +2,5 @@
2
2
  export * from './client';
3
3
  export * from './dynamic-link';
4
4
  export * from './types';
5
- export * from '@orpc/shared/error';
5
+ export { isDefinedError, ORPCError, safe } from '@orpc/contract';
6
6
  //# sourceMappingURL=index.d.ts.map
@@ -1,5 +1,5 @@
1
- import type { ProcedureClientOptions } from '@orpc/server';
1
+ import type { ClientOptions } from '@orpc/contract';
2
2
  export interface ClientLink<TClientContext> {
3
- call: (path: readonly string[], input: unknown, options: ProcedureClientOptions<TClientContext>) => Promise<unknown>;
3
+ call(path: readonly string[], input: unknown, options: ClientOptions<TClientContext>): Promise<unknown>;
4
4
  }
5
5
  //# sourceMappingURL=types.d.ts.map
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@orpc/client",
3
3
  "type": "module",
4
- "version": "0.0.0-next.2ba0691",
4
+ "version": "0.0.0-next.31590a1",
5
5
  "license": "MIT",
6
6
  "homepage": "https://orpc.unnoq.com",
7
7
  "repository": {
@@ -33,16 +33,15 @@
33
33
  "!**/*.tsbuildinfo",
34
34
  "dist"
35
35
  ],
36
- "peerDependencies": {
37
- "@orpc/contract": "0.0.0-next.2ba0691"
38
- },
39
36
  "dependencies": {
40
- "@orpc/server": "0.0.0-next.2ba0691",
41
- "@orpc/shared": "0.0.0-next.2ba0691"
37
+ "content-disposition": "^0.5.4",
38
+ "@orpc/server": "0.0.0-next.31590a1",
39
+ "@orpc/contract": "0.0.0-next.31590a1",
40
+ "@orpc/shared": "0.0.0-next.31590a1"
42
41
  },
43
42
  "devDependencies": {
44
43
  "zod": "^3.24.1",
45
- "@orpc/openapi": "0.0.0-next.2ba0691"
44
+ "@orpc/openapi": "0.0.0-next.31590a1"
46
45
  },
47
46
  "scripts": {
48
47
  "build": "tsup --clean --sourcemap --entry.index=src/index.ts --entry.fetch=src/adapters/fetch/index.ts --format=esm --onSuccess='tsc -b --noCheck'",