@orpc/server 1.14.5 → 2.0.0-beta.1

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 (78) hide show
  1. package/README.md +75 -136
  2. package/dist/adapters/crossws/index.d.mts +42 -21
  3. package/dist/adapters/crossws/index.d.ts +42 -21
  4. package/dist/adapters/crossws/index.mjs +37 -18
  5. package/dist/adapters/fetch/index.d.mts +83 -67
  6. package/dist/adapters/fetch/index.d.ts +83 -67
  7. package/dist/adapters/fetch/index.mjs +131 -106
  8. package/dist/adapters/message-port/index.d.mts +51 -34
  9. package/dist/adapters/message-port/index.d.ts +51 -34
  10. package/dist/adapters/message-port/index.mjs +73 -38
  11. package/dist/adapters/node/index.d.mts +82 -60
  12. package/dist/adapters/node/index.d.ts +82 -60
  13. package/dist/adapters/node/index.mjs +127 -98
  14. package/dist/adapters/standard/index.d.mts +16 -18
  15. package/dist/adapters/standard/index.d.ts +16 -18
  16. package/dist/adapters/standard/index.mjs +5 -5
  17. package/dist/adapters/standard-peer/index.d.mts +12 -14
  18. package/dist/adapters/standard-peer/index.d.ts +12 -14
  19. package/dist/adapters/standard-peer/index.mjs +2 -21
  20. package/dist/adapters/websocket/index.d.mts +39 -34
  21. package/dist/adapters/websocket/index.d.ts +39 -34
  22. package/dist/adapters/websocket/index.mjs +42 -33
  23. package/dist/extensions/callable.d.mts +10 -0
  24. package/dist/extensions/callable.d.ts +10 -0
  25. package/dist/extensions/callable.mjs +11 -0
  26. package/dist/helpers/index.d.mts +2 -2
  27. package/dist/helpers/index.d.ts +2 -2
  28. package/dist/helpers/index.mjs +1 -1
  29. package/dist/index.d.mts +163 -770
  30. package/dist/index.d.ts +163 -770
  31. package/dist/index.mjs +296 -403
  32. package/dist/plugins/index.d.mts +105 -143
  33. package/dist/plugins/index.d.ts +105 -143
  34. package/dist/plugins/index.mjs +232 -255
  35. package/dist/shared/server.BB_Ik9Ph.d.mts +104 -0
  36. package/dist/shared/server.BL22TloH.d.mts +184 -0
  37. package/dist/shared/server.BL22TloH.d.ts +184 -0
  38. package/dist/shared/server.B_U9y00a.d.mts +66 -0
  39. package/dist/shared/server.BsNNjG5J.d.mts +61 -0
  40. package/dist/shared/server.BwHnWUuN.mjs +222 -0
  41. package/dist/shared/server.CX4vUnDk.mjs +11 -0
  42. package/dist/shared/server.CjOb6ItT.mjs +41 -0
  43. package/dist/shared/server.CrlKQucM.mjs +233 -0
  44. package/dist/shared/server.D_QauotT.mjs +30 -0
  45. package/dist/shared/server.EOHJ3NJr.d.ts +104 -0
  46. package/dist/shared/server.GDpX6Df8.mjs +271 -0
  47. package/dist/shared/server.Pa0F03f_.d.ts +61 -0
  48. package/dist/shared/server.T9F3bzZx.d.ts +66 -0
  49. package/dist/shared/{server.DZ5BIITo.mjs → server.W91HSRkE.mjs} +2 -2
  50. package/package.json +26 -55
  51. package/dist/adapters/aws-lambda/index.d.mts +0 -46
  52. package/dist/adapters/aws-lambda/index.d.ts +0 -46
  53. package/dist/adapters/aws-lambda/index.mjs +0 -40
  54. package/dist/adapters/bun-ws/index.d.mts +0 -36
  55. package/dist/adapters/bun-ws/index.d.ts +0 -36
  56. package/dist/adapters/bun-ws/index.mjs +0 -47
  57. package/dist/adapters/fastify/index.d.mts +0 -53
  58. package/dist/adapters/fastify/index.d.ts +0 -53
  59. package/dist/adapters/fastify/index.mjs +0 -52
  60. package/dist/adapters/ws/index.d.mts +0 -31
  61. package/dist/adapters/ws/index.d.ts +0 -31
  62. package/dist/adapters/ws/index.mjs +0 -37
  63. package/dist/hibernation/index.d.mts +0 -44
  64. package/dist/hibernation/index.d.ts +0 -44
  65. package/dist/hibernation/index.mjs +0 -65
  66. package/dist/shared/server.7cEtMB30.d.ts +0 -74
  67. package/dist/shared/server.B8gYOD5g.d.mts +0 -12
  68. package/dist/shared/server.BqadksTP.d.mts +0 -74
  69. package/dist/shared/server.C8_sRzQB.d.mts +0 -42
  70. package/dist/shared/server.ChUyt5-i.d.mts +0 -32
  71. package/dist/shared/server.ChyoA9XY.d.ts +0 -42
  72. package/dist/shared/server.DEBcqOjg.mjs +0 -418
  73. package/dist/shared/server.EfTOZ2Q7.d.ts +0 -12
  74. package/dist/shared/server.TEVCLCFC.mjs +0 -39
  75. package/dist/shared/server.ZxHCEN1h.mjs +0 -226
  76. package/dist/shared/server.qKsRrdxW.d.mts +0 -193
  77. package/dist/shared/server.qKsRrdxW.d.ts +0 -193
  78. package/dist/shared/server.yoEB3Fx4.d.ts +0 -32
@@ -1,62 +1,17 @@
1
+ import { toArray, sortPlugins, intercept, resolveMaybeOptionalOptions } from '@orpc/shared';
1
2
  import { ORPCError } from '@orpc/client';
2
- import { toArray, intercept, resolveMaybeOptionalOptions } from '@orpc/shared';
3
- import { toStandardLazyRequest, toFetchResponse } from '@orpc/standard-server-fetch';
4
- import { r as resolveFriendlyStandardHandleOptions } from '../../shared/server.DZ5BIITo.mjs';
5
- import { C as CompositeStandardHandlerPlugin, b as StandardRPCHandler } from '../../shared/server.ZxHCEN1h.mjs';
6
- import { S as StrictGetMethodPlugin } from '../../shared/server.TEVCLCFC.mjs';
7
- import '@orpc/client/standard';
8
- import '@orpc/standard-server';
9
- import '../../shared/server.DEBcqOjg.mjs';
3
+ import { toStandardLazyRequest, toFetchResponse } from '@standardserver/fetch';
4
+ import { r as resolveFriendlyStandardHandlerHandleOptions } from '../../shared/server.W91HSRkE.mjs';
5
+ import { C as CSRFGuardHandlerPlugin } from '../../shared/server.D_QauotT.mjs';
6
+ import { R as RPCHandlerCodec, S as StandardHandler } from '../../shared/server.GDpX6Df8.mjs';
7
+ import '@standardserver/core';
8
+ import '../../shared/server.CrlKQucM.mjs';
10
9
  import '@orpc/contract';
11
-
12
- class BodyLimitPlugin {
13
- maxBodySize;
14
- constructor(options) {
15
- this.maxBodySize = options.maxBodySize;
16
- }
17
- initRuntimeAdapter(options) {
18
- options.adapterInterceptors ??= [];
19
- options.adapterInterceptors.push(async (options2) => {
20
- if (!options2.request.body) {
21
- return options2.next();
22
- }
23
- let currentBodySize = 0;
24
- const rawReader = options2.request.body.getReader();
25
- const reader = new ReadableStream({
26
- start: async (controller) => {
27
- try {
28
- if (Number(options2.request.headers.get("content-length")) > this.maxBodySize) {
29
- controller.error(new ORPCError("PAYLOAD_TOO_LARGE"));
30
- return;
31
- }
32
- while (true) {
33
- const { done, value } = await rawReader.read();
34
- if (done) {
35
- break;
36
- }
37
- currentBodySize += value.length;
38
- if (currentBodySize > this.maxBodySize) {
39
- controller.error(new ORPCError("PAYLOAD_TOO_LARGE"));
40
- break;
41
- }
42
- controller.enqueue(value);
43
- }
44
- } finally {
45
- controller.close();
46
- }
47
- }
48
- });
49
- const requestInit = { body: reader, duplex: "half" };
50
- return options2.next({
51
- ...options2,
52
- request: new Request(options2.request, requestInit)
53
- });
54
- });
55
- }
56
- }
10
+ import '../../shared/server.BwHnWUuN.mjs';
57
11
 
58
12
  const ORDERED_SUPPORTED_ENCODINGS = ["gzip", "deflate"];
59
- class CompressionPlugin {
13
+ class BodyCompressionHandlerPlugin {
14
+ name = "~body-compression";
60
15
  encodings;
61
16
  threshold;
62
17
  filter;
@@ -72,42 +27,47 @@ class CompressionPlugin {
72
27
  return options.filter ? options.filter(request, response) : isCompressibleContentType(contentType);
73
28
  };
74
29
  }
75
- initRuntimeAdapter(options) {
76
- options.adapterInterceptors ??= [];
77
- options.adapterInterceptors.unshift(async (options2) => {
78
- const result = await options2.next();
79
- if (!result.matched) {
80
- return result;
81
- }
82
- const response = result.response;
83
- if (response.headers.has("content-encoding") || response.headers.has("transfer-encoding") || isNoTransformCacheControl(response.headers.get("cache-control"))) {
84
- return result;
85
- }
86
- const contentLength = response.headers.get("content-length");
87
- if (contentLength && Number(contentLength) < this.threshold) {
88
- return result;
89
- }
90
- const acceptEncoding = options2.request.headers.get("accept-encoding")?.split(",").map((enc) => enc.trim().split(";")[0]);
91
- const encoding = this.encodings.find((enc) => acceptEncoding?.includes(enc));
92
- if (!response.body || encoding === void 0) {
93
- return result;
94
- }
95
- if (!this.filter(options2.request, response)) {
96
- return result;
97
- }
98
- const compressedBody = response.body.pipeThrough(new CompressionStream(encoding));
99
- const compressedHeaders = new Headers(response.headers);
100
- compressedHeaders.delete("content-length");
101
- compressedHeaders.set("content-encoding", encoding);
102
- return {
103
- ...result,
104
- response: new Response(compressedBody, {
105
- status: response.status,
106
- statusText: response.statusText,
107
- headers: compressedHeaders
108
- })
109
- };
110
- });
30
+ initFetchHandlerOptions(options) {
31
+ return {
32
+ ...options,
33
+ fetchInterceptors: [
34
+ async (interceptorOptions) => {
35
+ const result = await interceptorOptions.next();
36
+ if (!result.matched) {
37
+ return result;
38
+ }
39
+ const response = result.response;
40
+ if (response.headers.has("content-encoding") || response.headers.has("transfer-encoding") || isNoTransformCacheControl(response.headers.get("cache-control"))) {
41
+ return result;
42
+ }
43
+ const contentLength = response.headers.get("content-length");
44
+ if (contentLength && Number(contentLength) < this.threshold) {
45
+ return result;
46
+ }
47
+ const acceptEncoding = interceptorOptions.request.headers.get("accept-encoding")?.split(",").map((enc) => enc.trim().split(";")[0]);
48
+ const encoding = this.encodings.find((enc) => acceptEncoding?.includes(enc));
49
+ if (!response.body || encoding === void 0) {
50
+ return result;
51
+ }
52
+ if (!this.filter(interceptorOptions.request, response)) {
53
+ return result;
54
+ }
55
+ const compressedBody = response.body.pipeThrough(new CompressionStream(encoding));
56
+ const compressedHeaders = new Headers(response.headers);
57
+ compressedHeaders.delete("content-length");
58
+ compressedHeaders.set("content-encoding", encoding);
59
+ return {
60
+ ...result,
61
+ response: new Response(compressedBody, {
62
+ status: response.status,
63
+ statusText: response.statusText,
64
+ headers: compressedHeaders
65
+ })
66
+ };
67
+ },
68
+ ...toArray(options.fetchInterceptors)
69
+ ]
70
+ };
111
71
  }
112
72
  }
113
73
  const COMPRESSIBLE_CONTENT_TYPE_REGEX = /^\s*(?:text\/(?!event-stream(?:[;\s]|$))[^;\s]+|application\/(?:javascript|json|xml|xml-dtd|ecmascript|dart|postscript|rtf|tar|toml|vnd\.dart|vnd\.ms-fontobject|vnd\.ms-opentype|wasm|x-httpd-php|x-javascript|x-ns-proxy-autoconfig|x-sh|x-tar|x-virtualbox-hdd|x-virtualbox-ova|x-virtualbox-ovf|x-virtualbox-vbox|x-virtualbox-vdi|x-virtualbox-vhd|x-virtualbox-vmdk|x-www-form-urlencoded)|font\/(?:otf|ttf)|image\/(?:bmp|vnd\.adobe\.photoshop|vnd\.microsoft\.icon|vnd\.ms-dds|x-icon|x-ms-bmp)|message\/rfc822|model\/gltf-binary|x-shader\/x-fragment|x-shader\/x-vertex|[^;\s]+?\+(?:json|text|xml|yaml))(?:[;\s]|$)/i;
@@ -125,29 +85,90 @@ function isNoTransformCacheControl(cacheControl) {
125
85
  return CACHE_CONTROL_NO_TRANSFORM_REGEX.test(cacheControl);
126
86
  }
127
87
 
128
- class CompositeFetchHandlerPlugin extends CompositeStandardHandlerPlugin {
129
- initRuntimeAdapter(options) {
88
+ class BodyLimitHandlerPlugin {
89
+ name = "~body-limit";
90
+ maxBodySize;
91
+ constructor(options) {
92
+ this.maxBodySize = options.maxBodySize;
93
+ }
94
+ initFetchHandlerOptions(options) {
95
+ return {
96
+ ...options,
97
+ fetchInterceptors: [
98
+ async (interceptorOptions) => {
99
+ if (!interceptorOptions.request.body) {
100
+ return interceptorOptions.next();
101
+ }
102
+ let currentBodySize = 0;
103
+ const rawReader = interceptorOptions.request.body.getReader();
104
+ const body = new ReadableStream({
105
+ start: async (controller) => {
106
+ const reject = async (error) => {
107
+ controller.error(error);
108
+ await rawReader.cancel(error);
109
+ };
110
+ const contentLength = interceptorOptions.request.headers.get("content-length");
111
+ if (contentLength && Number(contentLength) > this.maxBodySize) {
112
+ await reject(new ORPCError("PAYLOAD_TOO_LARGE"));
113
+ return;
114
+ }
115
+ while (true) {
116
+ const { done, value } = await rawReader.read();
117
+ if (done) {
118
+ controller.close();
119
+ return;
120
+ }
121
+ currentBodySize += value.length;
122
+ if (currentBodySize > this.maxBodySize) {
123
+ await reject(new ORPCError("PAYLOAD_TOO_LARGE"));
124
+ return;
125
+ }
126
+ controller.enqueue(value);
127
+ }
128
+ }
129
+ });
130
+ const requestInit = { body, duplex: "half" };
131
+ return interceptorOptions.next({
132
+ ...interceptorOptions,
133
+ request: new Request(interceptorOptions.request, requestInit)
134
+ });
135
+ },
136
+ ...toArray(options.fetchInterceptors)
137
+ ]
138
+ };
139
+ }
140
+ }
141
+
142
+ class CompositeFetchHandlerPlugin {
143
+ name = "~composite/fetch-handler";
144
+ plugins;
145
+ constructor(plugins = []) {
146
+ this.plugins = sortPlugins(plugins);
147
+ }
148
+ initFetchHandlerOptions(options) {
130
149
  for (const plugin of this.plugins) {
131
- plugin.initRuntimeAdapter?.(options);
150
+ if (plugin.initFetchHandlerOptions) {
151
+ options = plugin.initFetchHandlerOptions(options);
152
+ }
132
153
  }
154
+ return options;
133
155
  }
134
156
  }
135
157
 
136
158
  class FetchHandler {
137
159
  constructor(standardHandler, options = {}) {
138
160
  this.standardHandler = standardHandler;
139
- const plugin = new CompositeFetchHandlerPlugin(options.plugins);
140
- plugin.initRuntimeAdapter(options);
141
- this.adapterInterceptors = toArray(options.adapterInterceptors);
142
- this.toFetchResponseOptions = options;
161
+ options = new CompositeFetchHandlerPlugin(options.plugins).initFetchHandlerOptions(options);
162
+ this.fetchInterceptors = options.fetchInterceptors;
163
+ this.toFetchResponseOptions = options.toFetchResponse;
143
164
  }
144
165
  toFetchResponseOptions;
145
- adapterInterceptors;
166
+ fetchInterceptors;
146
167
  async handle(request, ...rest) {
147
168
  return intercept(
148
- this.adapterInterceptors,
169
+ this.fetchInterceptors,
149
170
  {
150
- ...resolveFriendlyStandardHandleOptions(resolveMaybeOptionalOptions(rest)),
171
+ ...resolveFriendlyStandardHandlerHandleOptions(resolveMaybeOptionalOptions(rest)),
151
172
  request,
152
173
  toFetchResponseOptions: this.toFetchResponseOptions
153
174
  },
@@ -168,12 +189,16 @@ class FetchHandler {
168
189
 
169
190
  class RPCHandler extends FetchHandler {
170
191
  constructor(router, options = {}) {
171
- if (options.strictGetMethodPluginEnabled ?? true) {
172
- options.plugins ??= [];
173
- options.plugins.push(new StrictGetMethodPlugin());
192
+ if (options.csrfGuardHandlerPlugin?.enabled !== false) {
193
+ options = {
194
+ ...options,
195
+ plugins: [...toArray(options.plugins), new CSRFGuardHandlerPlugin()]
196
+ };
174
197
  }
175
- super(new StandardRPCHandler(router, options), options);
198
+ const codec = new RPCHandlerCodec(router, options);
199
+ const handler = new StandardHandler(codec, options);
200
+ super(handler, options);
176
201
  }
177
202
  }
178
203
 
179
- export { BodyLimitPlugin, CompositeFetchHandlerPlugin, CompressionPlugin, FetchHandler, RPCHandler };
204
+ export { BodyCompressionHandlerPlugin, BodyLimitHandlerPlugin, CompositeFetchHandlerPlugin, FetchHandler, RPCHandler };
@@ -1,56 +1,73 @@
1
1
  import { SupportedMessagePort } from '@orpc/client/message-port';
2
2
  import { Value, Promisable, MaybeOptionalOptions } from '@orpc/shared';
3
- import { DecodedResponseMessage } from '@orpc/standard-server-peer';
4
- import { C as Context, R as Router } from '../../shared/server.qKsRrdxW.mjs';
5
- import { f as StandardHandler } from '../../shared/server.BqadksTP.mjs';
6
- import { HandleStandardServerPeerMessageOptions } from '../standard-peer/index.mjs';
7
- import { S as StandardRPCHandlerOptions } from '../../shared/server.ChUyt5-i.mjs';
3
+ import { ServerPeer, EncodePeerMessageOptions, DecodePeerMessageOptions } from '@standardserver/peer';
4
+ import { C as Context } from '../../shared/server.BL22TloH.mjs';
5
+ import { S as StandardHandler, a as StandardHandlerOptions } from '../../shared/server.BB_Ik9Ph.mjs';
6
+ import { StandardPeerRequestHandlerOptions } from '../standard-peer/index.mjs';
7
+ import { R as Router } from '../../shared/server.BsNNjG5J.mjs';
8
+ import { R as RPCHandlerCodecOptions } from '../../shared/server.B_U9y00a.mjs';
8
9
  import '@orpc/client';
9
10
  import '@orpc/contract';
10
- import '@orpc/standard-server';
11
- import '../../shared/server.B8gYOD5g.mjs';
12
- import '@orpc/client/standard';
13
- import '../../shared/server.C8_sRzQB.mjs';
11
+ import '@standardserver/core';
14
12
 
13
+ type DecodedResponseMessage = ConstructorParameters<typeof ServerPeer>[0] extends (message: infer TMessage) => unknown ? TMessage : never;
15
14
  interface MessagePortHandlerOptions<_T extends Context> {
16
15
  /**
17
- * By default, oRPC serializes request/response messages to string/binary data before sending over message port.
18
- * If needed, you can define the this option to utilize full power of [MessagePort: postMessage() method](https://developer.mozilla.org/en-US/docs/Web/API/MessagePort/postMessage),
19
- * such as transferring ownership of objects to the other side or support unserializable objects like `OffscreenCanvas`.
16
+ * By default, oRPC encodes peer messages as strings or binary data before sending them over the message port.
17
+ * Use this option to bypass encoding and leverage the full capabilities of the
18
+ * [MessagePort: postMessage() method](https://developer.mozilla.org/en-US/docs/Web/API/MessagePort/postMessage),
19
+ * such as transferring object ownership or supporting non-serializable objects like `OffscreenCanvas` or improving performance.
20
20
  *
21
21
  * @remarks
22
- * - return null | undefined to disable this feature
22
+ * - Returning `null` or `undefined` disables this feature.
23
23
  *
24
- * @warning Make sure your message port supports `transfer` before using this feature.
25
- * @example
26
- * ```ts
27
- * experimental_transfer: (message, port) => {
28
- * const transfer = deepFindTransferableObjects(message) // implement your own logic
29
- * return transfer.length ? transfer : null // only enable when needed
30
- * }
31
- * ```
32
- *
33
- * @see {@link https://orpc.dev/docs/adapters/message-port#transfer Message Port Transfer Docs}
24
+ * @warning Ensure your message port implementation supports `transferable` objects before enabling this.
34
25
  */
35
26
  experimental_transfer?: Value<Promisable<object[] | null | undefined>, [message: DecodedResponseMessage, port: SupportedMessagePort]>;
27
+ /**
28
+ * Options for encoding peer messages. such as `prefix` for distinguishing messages on the same channel..
29
+ */
30
+ encodePeerMessage?: EncodePeerMessageOptions | undefined;
31
+ /**
32
+ * Options for decoding peer messages. such as `prefix` for distinguishing messages on the same channel..
33
+ */
34
+ decodePeerMessage?: DecodePeerMessageOptions | undefined;
36
35
  }
37
36
  declare class MessagePortHandler<T extends Context> {
38
- private readonly standardHandler;
37
+ private readonly handler;
38
+ private readonly peers;
39
39
  private readonly transfer;
40
- constructor(standardHandler: StandardHandler<T>, options?: NoInfer<MessagePortHandlerOptions<T>>);
41
- upgrade(port: SupportedMessagePort, ...rest: MaybeOptionalOptions<HandleStandardServerPeerMessageOptions<T>>): void;
40
+ private readonly encodePeerMessageOptions;
41
+ private readonly decodePeerMessageOptions;
42
+ constructor(handler: StandardHandler<T>, options?: NoInfer<MessagePortHandlerOptions<T>>);
43
+ /**
44
+ * Attaches necessary event listeners to a message port to handle incoming messages and peer management.
45
+ */
46
+ upgrade(port: SupportedMessagePort, ...rest: MaybeOptionalOptions<StandardPeerRequestHandlerOptions<T>>): void;
47
+ /**
48
+ * Handles a single message received from a message port.
49
+ *
50
+ * @warning AVOID calling this method directly if `.upgrade()` is used, as `.upgrade()` already sets up necessary event listeners to call this method for incoming messages and manage peer lifecycle.
51
+ *
52
+ * @param port The message port instance, require consistent instance across messages for proper peer management
53
+ */
54
+ message(port: SupportedMessagePort, data: any, ...rest: MaybeOptionalOptions<StandardPeerRequestHandlerOptions<T>>): Promise<{
55
+ matched: boolean;
56
+ }>;
57
+ /**
58
+ * Called when a message port is closed, to clean up any associated peer state.
59
+ *
60
+ * @warning AVOID calling this method directly if `.upgrade()` is used, as `.upgrade()` already sets up necessary event listeners to call this method for incoming messages and manage peer lifecycle.
61
+ *
62
+ * @param port The message port instance to clean up, must be the same instance used in `.message()` calls to properly clean up
63
+ */
64
+ close(port: SupportedMessagePort): Promise<void>;
42
65
  }
43
66
 
44
- interface RPCHandlerOptions<T extends Context> extends StandardRPCHandlerOptions<T>, MessagePortHandlerOptions<T> {
67
+ interface RPCHandlerOptions<T extends Context> extends StandardHandlerOptions<T>, RPCHandlerCodecOptions<T>, MessagePortHandlerOptions<T> {
45
68
  }
46
- /**
47
- * RPC Handler for common message port implementations.
48
- *
49
- * @see {@link https://orpc.dev/docs/rpc-handler RPC Handler Docs}
50
- * @see {@link https://orpc.dev/docs/adapters/message-port Message Port Adapter Docs}
51
- */
52
69
  declare class RPCHandler<T extends Context> extends MessagePortHandler<T> {
53
- constructor(router: Router<any, T>, options?: NoInfer<RPCHandlerOptions<T>>);
70
+ constructor(router: Router<T>, options?: NoInfer<RPCHandlerOptions<T>>);
54
71
  }
55
72
 
56
73
  export { MessagePortHandler, RPCHandler };
@@ -1,56 +1,73 @@
1
1
  import { SupportedMessagePort } from '@orpc/client/message-port';
2
2
  import { Value, Promisable, MaybeOptionalOptions } from '@orpc/shared';
3
- import { DecodedResponseMessage } from '@orpc/standard-server-peer';
4
- import { C as Context, R as Router } from '../../shared/server.qKsRrdxW.js';
5
- import { f as StandardHandler } from '../../shared/server.7cEtMB30.js';
6
- import { HandleStandardServerPeerMessageOptions } from '../standard-peer/index.js';
7
- import { S as StandardRPCHandlerOptions } from '../../shared/server.yoEB3Fx4.js';
3
+ import { ServerPeer, EncodePeerMessageOptions, DecodePeerMessageOptions } from '@standardserver/peer';
4
+ import { C as Context } from '../../shared/server.BL22TloH.js';
5
+ import { S as StandardHandler, a as StandardHandlerOptions } from '../../shared/server.EOHJ3NJr.js';
6
+ import { StandardPeerRequestHandlerOptions } from '../standard-peer/index.js';
7
+ import { R as Router } from '../../shared/server.Pa0F03f_.js';
8
+ import { R as RPCHandlerCodecOptions } from '../../shared/server.T9F3bzZx.js';
8
9
  import '@orpc/client';
9
10
  import '@orpc/contract';
10
- import '@orpc/standard-server';
11
- import '../../shared/server.EfTOZ2Q7.js';
12
- import '@orpc/client/standard';
13
- import '../../shared/server.ChyoA9XY.js';
11
+ import '@standardserver/core';
14
12
 
13
+ type DecodedResponseMessage = ConstructorParameters<typeof ServerPeer>[0] extends (message: infer TMessage) => unknown ? TMessage : never;
15
14
  interface MessagePortHandlerOptions<_T extends Context> {
16
15
  /**
17
- * By default, oRPC serializes request/response messages to string/binary data before sending over message port.
18
- * If needed, you can define the this option to utilize full power of [MessagePort: postMessage() method](https://developer.mozilla.org/en-US/docs/Web/API/MessagePort/postMessage),
19
- * such as transferring ownership of objects to the other side or support unserializable objects like `OffscreenCanvas`.
16
+ * By default, oRPC encodes peer messages as strings or binary data before sending them over the message port.
17
+ * Use this option to bypass encoding and leverage the full capabilities of the
18
+ * [MessagePort: postMessage() method](https://developer.mozilla.org/en-US/docs/Web/API/MessagePort/postMessage),
19
+ * such as transferring object ownership or supporting non-serializable objects like `OffscreenCanvas` or improving performance.
20
20
  *
21
21
  * @remarks
22
- * - return null | undefined to disable this feature
22
+ * - Returning `null` or `undefined` disables this feature.
23
23
  *
24
- * @warning Make sure your message port supports `transfer` before using this feature.
25
- * @example
26
- * ```ts
27
- * experimental_transfer: (message, port) => {
28
- * const transfer = deepFindTransferableObjects(message) // implement your own logic
29
- * return transfer.length ? transfer : null // only enable when needed
30
- * }
31
- * ```
32
- *
33
- * @see {@link https://orpc.dev/docs/adapters/message-port#transfer Message Port Transfer Docs}
24
+ * @warning Ensure your message port implementation supports `transferable` objects before enabling this.
34
25
  */
35
26
  experimental_transfer?: Value<Promisable<object[] | null | undefined>, [message: DecodedResponseMessage, port: SupportedMessagePort]>;
27
+ /**
28
+ * Options for encoding peer messages. such as `prefix` for distinguishing messages on the same channel..
29
+ */
30
+ encodePeerMessage?: EncodePeerMessageOptions | undefined;
31
+ /**
32
+ * Options for decoding peer messages. such as `prefix` for distinguishing messages on the same channel..
33
+ */
34
+ decodePeerMessage?: DecodePeerMessageOptions | undefined;
36
35
  }
37
36
  declare class MessagePortHandler<T extends Context> {
38
- private readonly standardHandler;
37
+ private readonly handler;
38
+ private readonly peers;
39
39
  private readonly transfer;
40
- constructor(standardHandler: StandardHandler<T>, options?: NoInfer<MessagePortHandlerOptions<T>>);
41
- upgrade(port: SupportedMessagePort, ...rest: MaybeOptionalOptions<HandleStandardServerPeerMessageOptions<T>>): void;
40
+ private readonly encodePeerMessageOptions;
41
+ private readonly decodePeerMessageOptions;
42
+ constructor(handler: StandardHandler<T>, options?: NoInfer<MessagePortHandlerOptions<T>>);
43
+ /**
44
+ * Attaches necessary event listeners to a message port to handle incoming messages and peer management.
45
+ */
46
+ upgrade(port: SupportedMessagePort, ...rest: MaybeOptionalOptions<StandardPeerRequestHandlerOptions<T>>): void;
47
+ /**
48
+ * Handles a single message received from a message port.
49
+ *
50
+ * @warning AVOID calling this method directly if `.upgrade()` is used, as `.upgrade()` already sets up necessary event listeners to call this method for incoming messages and manage peer lifecycle.
51
+ *
52
+ * @param port The message port instance, require consistent instance across messages for proper peer management
53
+ */
54
+ message(port: SupportedMessagePort, data: any, ...rest: MaybeOptionalOptions<StandardPeerRequestHandlerOptions<T>>): Promise<{
55
+ matched: boolean;
56
+ }>;
57
+ /**
58
+ * Called when a message port is closed, to clean up any associated peer state.
59
+ *
60
+ * @warning AVOID calling this method directly if `.upgrade()` is used, as `.upgrade()` already sets up necessary event listeners to call this method for incoming messages and manage peer lifecycle.
61
+ *
62
+ * @param port The message port instance to clean up, must be the same instance used in `.message()` calls to properly clean up
63
+ */
64
+ close(port: SupportedMessagePort): Promise<void>;
42
65
  }
43
66
 
44
- interface RPCHandlerOptions<T extends Context> extends StandardRPCHandlerOptions<T>, MessagePortHandlerOptions<T> {
67
+ interface RPCHandlerOptions<T extends Context> extends StandardHandlerOptions<T>, RPCHandlerCodecOptions<T>, MessagePortHandlerOptions<T> {
45
68
  }
46
- /**
47
- * RPC Handler for common message port implementations.
48
- *
49
- * @see {@link https://orpc.dev/docs/rpc-handler RPC Handler Docs}
50
- * @see {@link https://orpc.dev/docs/adapters/message-port Message Port Adapter Docs}
51
- */
52
69
  declare class RPCHandler<T extends Context> extends MessagePortHandler<T> {
53
- constructor(router: Router<any, T>, options?: NoInfer<RPCHandlerOptions<T>>);
70
+ constructor(router: Router<T>, options?: NoInfer<RPCHandlerOptions<T>>);
54
71
  }
55
72
 
56
73
  export { MessagePortHandler, RPCHandler };
@@ -1,54 +1,89 @@
1
- import { postMessagePortMessage, onMessagePortMessage, onMessagePortClose } from '@orpc/client/message-port';
2
- import { value, resolveMaybeOptionalOptions, isObject } from '@orpc/shared';
3
- import { experimental_ServerPeerWithoutCodec, serializeResponseMessage, encodeResponseMessage, deserializeRequestMessage, decodeRequestMessage } from '@orpc/standard-server-peer';
4
- import { createServerPeerHandleRequestFn } from '../standard-peer/index.mjs';
5
- import { b as StandardRPCHandler } from '../../shared/server.ZxHCEN1h.mjs';
6
- import '../../shared/server.DZ5BIITo.mjs';
7
- import '@orpc/client/standard';
1
+ import { onMessagePortMessage, onMessagePortClose, postMessagePortMessage } from '@orpc/client/message-port';
2
+ import { value, isPlainObject, resolveMaybeOptionalOptions, toStringOrBytes } from '@orpc/shared';
3
+ import { ServerPeer, encodePeerMessage, decodePeerMessage, isClientPeerSendMessage } from '@standardserver/peer';
4
+ import { c as createStandardPeerRequestHandler } from '../../shared/server.CX4vUnDk.mjs';
5
+ import { R as RPCHandlerCodec, S as StandardHandler } from '../../shared/server.GDpX6Df8.mjs';
8
6
  import '@orpc/client';
9
- import '@orpc/standard-server';
10
- import '../../shared/server.DEBcqOjg.mjs';
7
+ import '@standardserver/core';
8
+ import '../../shared/server.CrlKQucM.mjs';
11
9
  import '@orpc/contract';
10
+ import '../../shared/server.BwHnWUuN.mjs';
12
11
 
13
12
  class MessagePortHandler {
14
- constructor(standardHandler, options = {}) {
15
- this.standardHandler = standardHandler;
13
+ constructor(handler, options = {}) {
14
+ this.handler = handler;
16
15
  this.transfer = options.experimental_transfer;
16
+ this.encodePeerMessageOptions = options.encodePeerMessage;
17
+ this.decodePeerMessageOptions = options.decodePeerMessage;
17
18
  }
19
+ peers = /* @__PURE__ */ new WeakMap();
18
20
  transfer;
21
+ encodePeerMessageOptions;
22
+ decodePeerMessageOptions;
23
+ /**
24
+ * Attaches necessary event listeners to a message port to handle incoming messages and peer management.
25
+ */
19
26
  upgrade(port, ...rest) {
20
- const peer = new experimental_ServerPeerWithoutCodec(async (message) => {
21
- const [id, type, payload] = message;
22
- const transfer = await value(this.transfer, message, port);
23
- if (transfer) {
24
- postMessagePortMessage(port, serializeResponseMessage(id, type, payload), transfer);
25
- } else {
26
- postMessagePortMessage(port, await encodeResponseMessage(id, type, payload));
27
- }
28
- });
29
- onMessagePortMessage(port, async (message) => {
30
- const handleFn = createServerPeerHandleRequestFn(this.standardHandler, resolveMaybeOptionalOptions(rest));
31
- if (isObject(message)) {
32
- await peer.message(
33
- deserializeRequestMessage(message),
34
- handleFn
35
- );
36
- } else {
37
- await peer.message(
38
- await decodeRequestMessage(message),
39
- handleFn
40
- );
41
- }
42
- });
43
- onMessagePortClose(port, () => {
44
- peer.close();
45
- });
27
+ onMessagePortMessage(port, (message) => this.message(port, message, ...rest));
28
+ onMessagePortClose(port, () => this.close(port));
29
+ }
30
+ /**
31
+ * Handles a single message received from a message port.
32
+ *
33
+ * @warning AVOID calling this method directly if `.upgrade()` is used, as `.upgrade()` already sets up necessary event listeners to call this method for incoming messages and manage peer lifecycle.
34
+ *
35
+ * @param port The message port instance, require consistent instance across messages for proper peer management
36
+ */
37
+ async message(port, data, ...rest) {
38
+ let peer = this.peers.get(port);
39
+ if (!peer) {
40
+ this.peers.set(port, peer = new ServerPeer(async (message2) => {
41
+ const transfer = await value(this.transfer, message2, port);
42
+ if (transfer) {
43
+ postMessagePortMessage(port, message2, transfer);
44
+ } else {
45
+ postMessagePortMessage(port, await encodePeerMessage(message2, this.encodePeerMessageOptions));
46
+ }
47
+ }));
48
+ }
49
+ if (isPlainObject(data)) {
50
+ await peer.message(
51
+ data,
52
+ createStandardPeerRequestHandler(this.handler, resolveMaybeOptionalOptions(rest))
53
+ );
54
+ return { matched: true };
55
+ }
56
+ const message = await toStringOrBytes(data);
57
+ const result = decodePeerMessage(message, this.decodePeerMessageOptions);
58
+ if (result.matched && isClientPeerSendMessage(result.message)) {
59
+ await peer.message(
60
+ result.message,
61
+ createStandardPeerRequestHandler(this.handler, resolveMaybeOptionalOptions(rest))
62
+ );
63
+ }
64
+ return result;
65
+ }
66
+ /**
67
+ * Called when a message port is closed, to clean up any associated peer state.
68
+ *
69
+ * @warning AVOID calling this method directly if `.upgrade()` is used, as `.upgrade()` already sets up necessary event listeners to call this method for incoming messages and manage peer lifecycle.
70
+ *
71
+ * @param port The message port instance to clean up, must be the same instance used in `.message()` calls to properly clean up
72
+ */
73
+ async close(port) {
74
+ const peer = this.peers.get(port);
75
+ if (peer) {
76
+ await peer.close();
77
+ this.peers.delete(port);
78
+ }
46
79
  }
47
80
  }
48
81
 
49
82
  class RPCHandler extends MessagePortHandler {
50
83
  constructor(router, options = {}) {
51
- super(new StandardRPCHandler(router, options), options);
84
+ const codec = new RPCHandlerCodec(router, options);
85
+ const handler = new StandardHandler(codec, options);
86
+ super(handler, options);
52
87
  }
53
88
  }
54
89