@orpc/client 2.0.0-beta.43 → 2.0.0-beta.45

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.
Files changed (29) hide show
  1. package/README.md +56 -57
  2. package/dist/adapters/fetch/index.d.mts +3 -3
  3. package/dist/adapters/fetch/index.d.ts +3 -3
  4. package/dist/adapters/fetch/index.mjs +9 -3
  5. package/dist/adapters/message-port/index.d.mts +2 -2
  6. package/dist/adapters/message-port/index.d.ts +2 -2
  7. package/dist/adapters/message-port/index.mjs +3 -3
  8. package/dist/adapters/standard/index.d.mts +8 -8
  9. package/dist/adapters/standard/index.d.ts +8 -8
  10. package/dist/adapters/standard/index.mjs +3 -3
  11. package/dist/adapters/websocket/index.d.mts +9 -9
  12. package/dist/adapters/websocket/index.d.ts +9 -9
  13. package/dist/adapters/websocket/index.mjs +3 -3
  14. package/dist/index.d.mts +9 -20
  15. package/dist/index.d.ts +9 -20
  16. package/dist/index.mjs +3 -3
  17. package/dist/plugins/index.d.mts +51 -37
  18. package/dist/plugins/index.d.ts +51 -37
  19. package/dist/plugins/index.mjs +20 -10
  20. package/dist/shared/{client.Dw9_40WO.d.mts → client.BoHJeq15.d.mts} +36 -11
  21. package/dist/shared/{client.Dw9_40WO.d.ts → client.BoHJeq15.d.ts} +36 -11
  22. package/dist/shared/{client.Bstqyaro.mjs → client.Bpyj1NM2.mjs} +11 -8
  23. package/dist/shared/{client.DiHfJmHK.d.ts → client.CkwtFnrc.d.mts} +5 -5
  24. package/dist/shared/{client.BjbdW5Rj.d.mts → client.DBDKICdk.d.ts} +5 -5
  25. package/dist/shared/{client.BPa_Mmuh.d.mts → client.DqIWeNhQ.d.mts} +7 -8
  26. package/dist/shared/{client.BPa_Mmuh.d.ts → client.DqIWeNhQ.d.ts} +7 -8
  27. package/dist/shared/{client.DD2eNne_.mjs → client.LF04eOvL.mjs} +24 -6
  28. package/dist/shared/{client.DpkGsZt-.mjs → client._wG5INmC.mjs} +69 -50
  29. package/package.json +8 -8
@@ -28,7 +28,7 @@ interface RPCJsonSerializerHandler {
28
28
  *
29
29
  * @default false
30
30
  */
31
- isTerminal?: boolean;
31
+ isTerminal?: boolean | undefined;
32
32
  }
33
33
  interface RPCJsonSerializerOptions {
34
34
  /**
@@ -88,13 +88,13 @@ declare class RPCJsonSerializer {
88
88
  private readonly handlerEntries;
89
89
  private readonly omitUndefinedProperties;
90
90
  constructor(options?: RPCJsonSerializerOptions);
91
- serialize(data: unknown): RPCJsonSerialization;
91
+ serializeJson(data: unknown): RPCJsonSerialization;
92
92
  /**
93
93
  * `segments` is a shared mutable stack (push/pop while walking),
94
94
  * so it must be copied before being stored in `meta` or `maps`.
95
95
  */
96
96
  private serializeValue;
97
- deserialize(serialized: RPCJsonSerialization): unknown;
97
+ deserializeJson(serialized: RPCJsonSerialization): unknown;
98
98
  }
99
99
 
100
100
  interface RPCSerializerSerializeOptions {
@@ -104,7 +104,7 @@ interface RPCSerializerSerializeOptions {
104
104
  *
105
105
  * @default true
106
106
  */
107
- useFormDataForBlobFields?: boolean;
107
+ useFormDataForBlobFields?: boolean | undefined;
108
108
  }
109
109
  interface RPCSerializerOptions extends RPCJsonSerializerOptions {
110
110
  /**
@@ -118,14 +118,13 @@ interface RPCSerializerOptions extends RPCJsonSerializerOptions {
118
118
  *
119
119
  * @see {@link https://orpc.dev/docs/rpc/serializer | RPC Serializer}
120
120
  */
121
- declare class RPCSerializer {
122
- private readonly jsonSerializer;
121
+ declare class RPCSerializer extends RPCJsonSerializer {
123
122
  private readonly defaultSerializeOptions;
124
123
  constructor(options?: RPCSerializerOptions);
125
124
  serialize(data: unknown, options?: RPCSerializerSerializeOptions): StandardBody;
126
- private serializeValue;
125
+ private serializePayload;
127
126
  deserialize(data: StandardBody): unknown;
128
- private deserializeValue;
127
+ private deserializePayload;
129
128
  }
130
129
 
131
130
  export { RPCJsonSerializer as b, RPCSerializer as e };
@@ -28,7 +28,7 @@ interface RPCJsonSerializerHandler {
28
28
  *
29
29
  * @default false
30
30
  */
31
- isTerminal?: boolean;
31
+ isTerminal?: boolean | undefined;
32
32
  }
33
33
  interface RPCJsonSerializerOptions {
34
34
  /**
@@ -88,13 +88,13 @@ declare class RPCJsonSerializer {
88
88
  private readonly handlerEntries;
89
89
  private readonly omitUndefinedProperties;
90
90
  constructor(options?: RPCJsonSerializerOptions);
91
- serialize(data: unknown): RPCJsonSerialization;
91
+ serializeJson(data: unknown): RPCJsonSerialization;
92
92
  /**
93
93
  * `segments` is a shared mutable stack (push/pop while walking),
94
94
  * so it must be copied before being stored in `meta` or `maps`.
95
95
  */
96
96
  private serializeValue;
97
- deserialize(serialized: RPCJsonSerialization): unknown;
97
+ deserializeJson(serialized: RPCJsonSerialization): unknown;
98
98
  }
99
99
 
100
100
  interface RPCSerializerSerializeOptions {
@@ -104,7 +104,7 @@ interface RPCSerializerSerializeOptions {
104
104
  *
105
105
  * @default true
106
106
  */
107
- useFormDataForBlobFields?: boolean;
107
+ useFormDataForBlobFields?: boolean | undefined;
108
108
  }
109
109
  interface RPCSerializerOptions extends RPCJsonSerializerOptions {
110
110
  /**
@@ -118,14 +118,13 @@ interface RPCSerializerOptions extends RPCJsonSerializerOptions {
118
118
  *
119
119
  * @see {@link https://orpc.dev/docs/rpc/serializer | RPC Serializer}
120
120
  */
121
- declare class RPCSerializer {
122
- private readonly jsonSerializer;
121
+ declare class RPCSerializer extends RPCJsonSerializer {
123
122
  private readonly defaultSerializeOptions;
124
123
  constructor(options?: RPCSerializerOptions);
125
124
  serialize(data: unknown, options?: RPCSerializerSerializeOptions): StandardBody;
126
- private serializeValue;
125
+ private serializePayload;
127
126
  deserialize(data: StandardBody): unknown;
128
- private deserializeValue;
127
+ private deserializePayload;
129
128
  }
130
129
 
131
130
  export { RPCJsonSerializer as b, RPCSerializer as e };
@@ -1,4 +1,4 @@
1
- import { resolveMaybeOptionalOptions, getConstructors } from '@orpc/shared';
1
+ import { resolveMaybeOptionalOptions, getConstructors, isPlainObject } from '@orpc/shared';
2
2
 
3
3
  const COMMON_ERROR_STATUS_MAP = {
4
4
  BAD_REQUEST: 400,
@@ -48,7 +48,7 @@ class ORPCError extends Error {
48
48
  data;
49
49
  constructor(code, ...rest) {
50
50
  const options = resolveMaybeOptionalOptions(rest);
51
- const message = options.message ?? code.split("_").map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
51
+ const message = options.message ?? codeToMessage(code);
52
52
  super(message, options);
53
53
  this.code = code;
54
54
  this.data = options.data;
@@ -85,13 +85,31 @@ class ORPCError extends Error {
85
85
  return false;
86
86
  }
87
87
  }
88
- class MalformedResponseError extends Error {
89
- name = "MalformedResponseError";
88
+ class ResponseError extends Error {
89
+ name = "ResponseError";
90
90
  response;
91
91
  constructor(options) {
92
- super(options.message, options);
92
+ super(options.message ?? `Received a ${options.response.status} response.`, options);
93
93
  this.response = options.response;
94
94
  }
95
95
  }
96
+ class MalformedResponseError extends ResponseError {
97
+ name = "MalformedResponseError";
98
+ constructor(options) {
99
+ super({ ...options, message: options.message ?? inferMalformedResponseMessage(options.response) });
100
+ }
101
+ }
102
+ const INFERRED_MESSAGE_MAX_LENGTH = 256;
103
+ function inferMalformedResponseMessage(response) {
104
+ const candidate = isPlainObject(response.body) ? response.body.message : response.body;
105
+ if (typeof candidate === "string" && candidate.length > 0 && candidate.length <= INFERRED_MESSAGE_MAX_LENGTH) {
106
+ return candidate;
107
+ }
108
+ const commonCode = Object.entries(COMMON_ERROR_STATUS_MAP).find(([, status]) => status === response.status)?.[0];
109
+ return commonCode ? codeToMessage(commonCode) : "Malformed oRPC Response";
110
+ }
111
+ function codeToMessage(code) {
112
+ return code.split("_").map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
113
+ }
96
114
 
97
- export { COMMON_ERROR_STATUS_MAP as C, MalformedResponseError as M, ORPCError as O };
115
+ export { COMMON_ERROR_STATUS_MAP as C, MalformedResponseError as M, ORPCError as O, ResponseError as R };
@@ -1,6 +1,6 @@
1
- import { isPlainObject, wrapAsyncIterator, isTypescriptObject, NullProtoObj, copyOnWrite, isAsyncIteratorObject, stringifyJSON } from '@orpc/shared';
1
+ import { isPlainObject, wrapAsyncIterator, isTypescriptObject, NullProtoObj, parseBigIntString, DEFAULT_MAX_BIGINT_DIGITS, copyOnWrite, isAsyncIteratorObject, stringifyJSON } from '@orpc/shared';
2
2
  import { getEventMeta, withEventMeta, ErrorEvent } from '@standard-server/core';
3
- import { O as ORPCError, M as MalformedResponseError, C as COMMON_ERROR_STATUS_MAP } from './client.DD2eNne_.mjs';
3
+ import { O as ORPCError } from './client.LF04eOvL.mjs';
4
4
 
5
5
  function isDefinedError(error) {
6
6
  return error instanceof ORPCError && error.defined;
@@ -26,29 +26,6 @@ function createORPCErrorFromJson(json, options = {}) {
26
26
  error.defined = json.defined;
27
27
  return error;
28
28
  }
29
- function createORPCErrorFromMalformedResponse(options) {
30
- const error = new ORPCError("MALFORMED_ORPC_RESPONSE", {
31
- message: options.message ?? inferMalformedResponseMessage(options.response),
32
- data: options.response
33
- });
34
- error.cause = new MalformedResponseError({ ...options, message: error.message });
35
- return error;
36
- }
37
- const INFERRED_MESSAGE_MIN_LENGTH = 1;
38
- const INFERRED_MESSAGE_MAX_LENGTH = 256;
39
- function isInferableMessage(text) {
40
- return text.length >= INFERRED_MESSAGE_MIN_LENGTH && text.length <= INFERRED_MESSAGE_MAX_LENGTH;
41
- }
42
- function inferMalformedResponseMessage(response) {
43
- if (typeof response.body === "string" && isInferableMessage(response.body)) {
44
- return response.body;
45
- }
46
- if (isPlainObject(response.body) && typeof response.body.message === "string" && isInferableMessage(response.body.message)) {
47
- return response.body.message;
48
- }
49
- const commonCode = Object.entries(COMMON_ERROR_STATUS_MAP).find(([, status]) => status === response.status)?.[0];
50
- return commonCode?.split("_").map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
51
- }
52
29
  function cloneORPCError(error) {
53
30
  const cloned = new ORPCError(error.code, {
54
31
  message: error.message,
@@ -95,6 +72,16 @@ function assertSerializedType(ok, type, expected) {
95
72
  throw invalidSerializedData(`type "${type}" expects ${expected}.`);
96
73
  }
97
74
  }
75
+ function isWalkable(value) {
76
+ return Array.isArray(value) || isPlainObject(value);
77
+ }
78
+ function walkInto(parent, key, original, restored) {
79
+ const value = parent[key];
80
+ if (!isWalkable(value) || restored?.has(value)) {
81
+ throw invalidSerializedData(`segment "${key}" cannot be walked into.`);
82
+ }
83
+ return value === original ? copyOnWrite(parent, key, original) : value;
84
+ }
98
85
  const DEFAULT_RPC_JSON_SERIALIZER_HANDLERS = {
99
86
  undefined: {
100
87
  condition(data) {
@@ -118,7 +105,11 @@ const DEFAULT_RPC_JSON_SERIALIZER_HANDLERS = {
118
105
  },
119
106
  deserialize(serialized) {
120
107
  assertSerializedType(typeof serialized === "string", "bigint", "a string");
121
- return BigInt(serialized);
108
+ const value = parseBigIntString(serialized, DEFAULT_MAX_BIGINT_DIGITS);
109
+ if (value === void 0) {
110
+ throw invalidSerializedData(`type "bigint" expects a decimal integer string of at most ${DEFAULT_MAX_BIGINT_DIGITS} digits.`);
111
+ }
112
+ return value;
122
113
  },
123
114
  isTerminal: true
124
115
  },
@@ -239,7 +230,7 @@ class RPCJsonSerializer {
239
230
  this.inlineBuiltInHandlers = inlineBuiltInHandlers;
240
231
  this.handlerEntries = handlerEntries;
241
232
  }
242
- serialize(data) {
233
+ serializeJson(data) {
243
234
  let meta = [];
244
235
  const maps = [];
245
236
  const blobs = [];
@@ -354,7 +345,7 @@ class RPCJsonSerializer {
354
345
  }
355
346
  return data;
356
347
  }
357
- deserialize(serialized) {
348
+ deserializeJson(serialized) {
358
349
  const ref = { json: serialized.json };
359
350
  if (serialized.blobs?.length) {
360
351
  for (let i = 0; i < serialized.maps.length; i++) {
@@ -368,7 +359,7 @@ class RPCJsonSerializer {
368
359
  let preSegment = "json";
369
360
  for (let j = 0; j < segments.length; j++) {
370
361
  original = original[preSegment];
371
- currentRef = copyOnWrite(currentRef, preSegment, original);
362
+ currentRef = walkInto(currentRef, preSegment, original);
372
363
  preSegment = segments[j];
373
364
  if (!Object.hasOwn(currentRef, preSegment)) {
374
365
  throw invalidSerializedData(`segment "${preSegment}" does not exist.`);
@@ -377,36 +368,64 @@ class RPCJsonSerializer {
377
368
  currentRef[preSegment] = blob;
378
369
  }
379
370
  }
380
- if (serialized.meta) {
381
- for (const item of serialized.meta) {
371
+ const meta = serialized.meta;
372
+ if (meta) {
373
+ let restored;
374
+ const chain = meta.length === 1 ? void 0 : [];
375
+ let chainDepth = 0;
376
+ for (const item of meta) {
382
377
  const type = item[0];
383
378
  const handler = this.handlers[type];
384
379
  if (handler === void 0) {
385
380
  throw invalidSerializedData(`type "${type}" is not supported.`);
386
381
  }
382
+ const depth = item.length - 1;
383
+ const shared = Math.min(depth, chainDepth);
384
+ let d = 0;
387
385
  let original = serialized;
388
386
  let currentRef = ref;
389
387
  let preSegment = "json";
390
- for (let i = 1; i < item.length; i++) {
388
+ if (chain !== void 0 && shared > 0) {
389
+ d = 1;
390
+ while (d < shared && chain[3 * d] === item[d]) {
391
+ d++;
392
+ }
393
+ currentRef = chain[3 * d - 2];
394
+ original = chain[3 * d - 1];
395
+ preSegment = item[d];
396
+ if (!Object.hasOwn(currentRef, preSegment)) {
397
+ throw invalidSerializedData(`segment "${preSegment}" does not exist.`);
398
+ }
399
+ }
400
+ while (d < depth) {
391
401
  original = original[preSegment];
392
- currentRef = copyOnWrite(currentRef, preSegment, original);
393
- preSegment = item[i];
402
+ currentRef = walkInto(currentRef, preSegment, original, restored);
403
+ if (chain !== void 0) {
404
+ chain[3 * d] = preSegment;
405
+ chain[3 * d + 1] = currentRef;
406
+ chain[3 * d + 2] = original;
407
+ }
408
+ preSegment = item[++d];
394
409
  if (!Object.hasOwn(currentRef, preSegment)) {
395
410
  throw invalidSerializedData(`segment "${preSegment}" does not exist.`);
396
411
  }
397
412
  }
398
- currentRef[preSegment] = handler.deserialize(currentRef[preSegment]);
413
+ chainDepth = depth;
414
+ const value = handler.deserialize(currentRef[preSegment]);
415
+ currentRef[preSegment] = value;
416
+ if (isWalkable(value)) {
417
+ (restored ??= /* @__PURE__ */ new Set()).add(value);
418
+ }
399
419
  }
400
420
  }
401
421
  return ref.json;
402
422
  }
403
423
  }
404
424
 
405
- class RPCSerializer {
406
- jsonSerializer;
425
+ class RPCSerializer extends RPCJsonSerializer {
407
426
  defaultSerializeOptions;
408
427
  constructor(options = {}) {
409
- this.jsonSerializer = new RPCJsonSerializer(options);
428
+ super(options);
410
429
  this.defaultSerializeOptions = options.serialize;
411
430
  }
412
431
  serialize(data, options = {}) {
@@ -419,19 +438,19 @@ class RPCSerializer {
419
438
  if (result.value === void 0) {
420
439
  return result;
421
440
  }
422
- return { done: result.done, value: this.serializeValue(result.value, false) };
441
+ return { done: result.done, value: this.serializePayload(result.value, false) };
423
442
  },
424
443
  mapError: (e) => new ErrorEvent(
425
- this.serializeValue(toORPCError(e).toJSON(), false),
444
+ this.serializePayload(toORPCError(e).toJSON(), false),
426
445
  { cause: e }
427
446
  )
428
447
  });
429
448
  }
430
449
  const useFormDataForBlobs = options.useFormDataForBlobFields ?? this.defaultSerializeOptions?.useFormDataForBlobFields ?? true;
431
- return this.serializeValue(data, useFormDataForBlobs);
450
+ return this.serializePayload(data, useFormDataForBlobs);
432
451
  }
433
- serializeValue(data, useFormDataForBlobs) {
434
- const { json, meta, maps, blobs } = this.jsonSerializer.serialize(data);
452
+ serializePayload(data, useFormDataForBlobs) {
453
+ const { json, meta, maps, blobs } = this.serializeJson(data);
435
454
  if (!useFormDataForBlobs || !blobs?.length) {
436
455
  return { json, meta };
437
456
  }
@@ -452,13 +471,13 @@ class RPCSerializer {
452
471
  if (result.value === void 0) {
453
472
  return result;
454
473
  }
455
- return { done: result.done, value: this.deserializeValue(result.value) };
474
+ return { done: result.done, value: this.deserializePayload(result.value) };
456
475
  },
457
476
  mapError: (e) => {
458
477
  if (!(e instanceof ErrorEvent)) {
459
478
  return e;
460
479
  }
461
- const deserialized = this.deserializeValue(e.data);
480
+ const deserialized = this.deserializePayload(e.data);
462
481
  if (isORPCErrorJson(deserialized)) {
463
482
  return createORPCErrorFromJson(deserialized, { cause: e });
464
483
  }
@@ -466,11 +485,11 @@ class RPCSerializer {
466
485
  }
467
486
  });
468
487
  }
469
- return this.deserializeValue(data);
488
+ return this.deserializePayload(data);
470
489
  }
471
- deserializeValue(data) {
490
+ deserializePayload(data) {
472
491
  if (!(data instanceof FormData)) {
473
- return this.jsonSerializer.deserialize(data);
492
+ return this.deserializeJson(data);
474
493
  }
475
494
  const serialized = JSON.parse(data.get("data"));
476
495
  const blobs = [];
@@ -479,8 +498,8 @@ class RPCSerializer {
479
498
  blobs[Number(key)] = value;
480
499
  }
481
500
  }
482
- return this.jsonSerializer.deserialize({ ...serialized, blobs });
501
+ return this.deserializeJson({ ...serialized, blobs });
483
502
  }
484
503
  }
485
504
 
486
- export { RPCSerializer as R, createORPCErrorFromJson as a, isDefinedError as b, createORPCErrorFromMalformedResponse as c, RPCJsonSerializer as d, cloneORPCError as e, isORPCErrorJson as i, toORPCError as t, wrapAsyncIteratorPreservingEventMeta as w };
505
+ export { RPCSerializer as R, isDefinedError as a, RPCJsonSerializer as b, createORPCErrorFromJson as c, cloneORPCError as d, isORPCErrorJson as i, toORPCError as t, wrapAsyncIteratorPreservingEventMeta as w };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@orpc/client",
3
3
  "type": "module",
4
- "version": "2.0.0-beta.43",
4
+ "version": "2.0.0-beta.45",
5
5
  "description": "Consume oRPC and OpenAPI APIs with end-to-end type safety over HTTP, WebSocket, or message ports",
6
6
  "license": "MIT",
7
7
  "funding": [
@@ -63,15 +63,15 @@
63
63
  "dist"
64
64
  ],
65
65
  "dependencies": {
66
- "@orpc/shared": "2.0.0-beta.43",
67
- "@standard-server/core": "~0.10.2",
68
- "@standard-server/fetch": "~0.10.2",
69
- "@standard-server/peer": "~0.10.2"
66
+ "@orpc/shared": "2.0.0-beta.45",
67
+ "@standard-server/core": "~0.11.0",
68
+ "@standard-server/fetch": "~0.11.0",
69
+ "@standard-server/peer": "~0.11.0"
70
70
  },
71
71
  "devDependencies": {
72
- "@types/ws": "^8.18.1",
73
- "ws": "^8.21.3",
74
- "zod": "^4.5.4"
72
+ "@types/ws": "^8.18.2",
73
+ "ws": "^8.22.0",
74
+ "zod": "^4.6.7"
75
75
  },
76
76
  "scripts": {
77
77
  "type:check": "tsc -b"