@orpc/client 0.0.0-next.31590a1 → 0.0.0-next.32cb70c

Sign up to get free protection for your applications and to get access to all the features.
package/dist/fetch.js CHANGED
@@ -1,12 +1,10 @@
1
1
  // src/adapters/fetch/orpc-link.ts
2
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";
3
+ import { ORPCPayloadCodec } from "@orpc/server/fetch";
4
+ import { ORPC_HANDLER_HEADER, ORPC_HANDLER_VALUE, trim } from "@orpc/shared";
7
5
  var RPCLink = class {
8
6
  fetch;
9
- rpcSerializer;
7
+ payloadCodec;
10
8
  maxURLLength;
11
9
  fallbackMethod;
12
10
  getMethod;
@@ -14,7 +12,7 @@ var RPCLink = class {
14
12
  url;
15
13
  constructor(options) {
16
14
  this.fetch = options.fetch ?? globalThis.fetch.bind(globalThis);
17
- this.rpcSerializer = options.rpcSerializer ?? new RPCSerializer();
15
+ this.payloadCodec = options.payloadCodec ?? new ORPCPayloadCodec();
18
16
  this.maxURLLength = options.maxURLLength ?? 2083;
19
17
  this.fallbackMethod = options.fallbackMethod ?? "POST";
20
18
  this.url = options.url;
@@ -28,73 +26,59 @@ var RPCLink = class {
28
26
  async call(path, input, options) {
29
27
  const clientContext = options.context;
30
28
  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"));
33
- }
34
29
  const response = await this.fetch(encoded.url, {
35
30
  method: encoded.method,
36
31
  headers: encoded.headers,
37
32
  body: encoded.body,
38
33
  signal: options.signal
39
34
  }, clientContext);
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
- });
35
+ const decoded = await this.payloadCodec.decode(response);
36
+ if (!response.ok) {
37
+ if (ORPCError.isValidJSON(decoded)) {
38
+ throw ORPCError.fromJSON(decoded);
54
39
  }
55
- })();
56
- if (response.ok) {
57
- return deserialized;
40
+ throw new ORPCError("INTERNAL_SERVER_ERROR", {
41
+ status: response.status,
42
+ message: "Internal server error",
43
+ cause: decoded
44
+ });
58
45
  }
59
- throw ORPCError.fromJSON(deserialized);
46
+ return decoded;
60
47
  }
61
48
  async encode(path, input, options) {
62
49
  const clientContext = options.context;
63
50
  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
- };
51
+ const methods = /* @__PURE__ */ new Set([expectMethod, this.fallbackMethod]);
52
+ const baseHeaders = await this.getHeaders(path, input, clientContext);
53
+ const baseUrl = new URL(`${trim(this.url, "/")}/${path.map(encodeURIComponent).join("/")}`);
54
+ baseHeaders.append(ORPC_HANDLER_HEADER, ORPC_HANDLER_VALUE);
55
+ for (const method of methods) {
56
+ const url = new URL(baseUrl);
57
+ const headers = new Headers(baseHeaders);
58
+ const encoded = this.payloadCodec.encode(input, method, this.fallbackMethod);
59
+ if (encoded.query) {
60
+ for (const [key, value] of encoded.query.entries()) {
61
+ url.searchParams.append(key, value);
62
+ }
78
63
  }
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");
64
+ if (url.toString().length > this.maxURLLength) {
65
+ continue;
66
+ }
67
+ if (encoded.headers) {
68
+ for (const [key, value] of encoded.headers.entries()) {
69
+ headers.append(key, value);
70
+ }
84
71
  }
85
72
  return {
86
- body: JSON.stringify(serialized),
87
- method,
73
+ url,
88
74
  headers,
89
- url
75
+ method: encoded.method,
76
+ body: encoded.body
90
77
  };
91
78
  }
92
- return {
93
- body: serialized,
94
- method,
95
- headers,
96
- url
97
- };
79
+ throw new ORPCError("BAD_REQUEST", {
80
+ message: "Cannot encode the request, please check the url length or payload."
81
+ });
98
82
  }
99
83
  };
100
84
  export {
@@ -2,7 +2,7 @@ import type { ClientOptions, HTTPMethod } from '@orpc/contract';
2
2
  import type { Promisable } from '@orpc/shared';
3
3
  import type { ClientLink } from '../../types';
4
4
  import type { FetchWithContext } from './types';
5
- import { RPCSerializer } from '@orpc/server/standard';
5
+ import { type PublicORPCPayloadCodec } from '@orpc/server/fetch';
6
6
  export interface RPCLinkOptions<TClientContext> {
7
7
  /**
8
8
  * Base url for all requests.
@@ -22,18 +22,18 @@ export interface RPCLinkOptions<TClientContext> {
22
22
  method?(path: readonly string[], input: unknown, context: TClientContext): Promisable<HTTPMethod | undefined>;
23
23
  /**
24
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.
25
+ * Do not use GET as fallback method, it's very dangerous.
26
26
  *
27
27
  * @default 'POST'
28
28
  */
29
- fallbackMethod?: Exclude<HTTPMethod, 'GET'>;
29
+ fallbackMethod?: HTTPMethod;
30
30
  headers?(path: readonly string[], input: unknown, context: TClientContext): Promisable<Headers | Record<string, string>>;
31
31
  fetch?: FetchWithContext<TClientContext>;
32
- rpcSerializer?: RPCSerializer;
32
+ payloadCodec?: PublicORPCPayloadCodec;
33
33
  }
34
34
  export declare class RPCLink<TClientContext> implements ClientLink<TClientContext> {
35
35
  private readonly fetch;
36
- private readonly rpcSerializer;
36
+ private readonly payloadCodec;
37
37
  private readonly maxURLLength;
38
38
  private readonly fallbackMethod;
39
39
  private readonly getMethod;
@@ -1,4 +1,4 @@
1
- import type { AnyContractRouter, ContractRouterClient } from '@orpc/contract';
1
+ import type { ContractRouter, ContractRouterClient } from '@orpc/contract';
2
2
  import type { AnyRouter, RouterClient } from '@orpc/server';
3
3
  import type { ClientLink } from './types';
4
4
  export interface createORPCClientOptions {
@@ -7,5 +7,5 @@ export interface createORPCClientOptions {
7
7
  */
8
8
  path?: string[];
9
9
  }
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;
10
+ export declare function createORPCClient<TRouter extends AnyRouter | ContractRouter<any>, TClientContext = unknown>(link: ClientLink<TClientContext>, options?: createORPCClientOptions): TRouter extends ContractRouter<any> ? ContractRouterClient<TRouter, TClientContext> : TRouter extends AnyRouter ? RouterClient<TRouter, TClientContext> : never;
11
11
  //# sourceMappingURL=client.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.31590a1",
4
+ "version": "0.0.0-next.32cb70c",
5
5
  "license": "MIT",
6
6
  "homepage": "https://orpc.unnoq.com",
7
7
  "repository": {
@@ -34,14 +34,13 @@
34
34
  "dist"
35
35
  ],
36
36
  "dependencies": {
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"
37
+ "@orpc/contract": "0.0.0-next.32cb70c",
38
+ "@orpc/server": "0.0.0-next.32cb70c",
39
+ "@orpc/shared": "0.0.0-next.32cb70c"
41
40
  },
42
41
  "devDependencies": {
43
42
  "zod": "^3.24.1",
44
- "@orpc/openapi": "0.0.0-next.31590a1"
43
+ "@orpc/openapi": "0.0.0-next.32cb70c"
45
44
  },
46
45
  "scripts": {
47
46
  "build": "tsup --clean --sourcemap --entry.index=src/index.ts --entry.fetch=src/adapters/fetch/index.ts --format=esm --onSuccess='tsc -b --noCheck'",