@trpc/server 11.0.0-rc.417 → 11.0.0-rc.419

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 (95) hide show
  1. package/dist/@trpc/server/index.d.ts +1 -1
  2. package/dist/@trpc/server/index.d.ts.map +1 -1
  3. package/dist/adapters/aws-lambda/getPlanner.d.ts.map +1 -1
  4. package/dist/adapters/next-app-dir/redirect.d.ts.map +1 -1
  5. package/dist/adapters/node-http/incomingMessageToRequest.d.ts +0 -1
  6. package/dist/adapters/node-http/incomingMessageToRequest.d.ts.map +1 -1
  7. package/dist/adapters/node-http/incomingMessageToRequest.js +3 -1
  8. package/dist/adapters/node-http/incomingMessageToRequest.mjs +3 -1
  9. package/dist/adapters/node-http/nodeHTTPRequestHandler.d.ts.map +1 -1
  10. package/dist/adapters/node-http/nodeHTTPRequestHandler.js +30 -7
  11. package/dist/adapters/node-http/nodeHTTPRequestHandler.mjs +30 -7
  12. package/dist/adapters/node-http/types.d.ts +0 -1
  13. package/dist/adapters/node-http/types.d.ts.map +1 -1
  14. package/dist/adapters/standalone.d.ts +0 -1
  15. package/dist/adapters/standalone.d.ts.map +1 -1
  16. package/dist/adapters/ws.d.ts +1 -2
  17. package/dist/adapters/ws.d.ts.map +1 -1
  18. package/dist/adapters/ws.js +98 -81
  19. package/dist/adapters/ws.mjs +98 -81
  20. package/dist/bundle-analysis.json +196 -137
  21. package/dist/index.js +5 -3
  22. package/dist/index.mjs +2 -1
  23. package/dist/observable/observable.d.ts +1 -0
  24. package/dist/observable/observable.d.ts.map +1 -1
  25. package/dist/observable/observable.js +55 -0
  26. package/dist/observable/observable.mjs +55 -1
  27. package/dist/unstable-core-do-not-import/createProxy.d.ts.map +1 -1
  28. package/dist/unstable-core-do-not-import/http/contentType.d.ts +7 -4
  29. package/dist/unstable-core-do-not-import/http/contentType.d.ts.map +1 -1
  30. package/dist/unstable-core-do-not-import/http/contentType.js +55 -17
  31. package/dist/unstable-core-do-not-import/http/contentType.mjs +56 -18
  32. package/dist/unstable-core-do-not-import/http/resolveResponse.d.ts.map +1 -1
  33. package/dist/unstable-core-do-not-import/http/resolveResponse.js +302 -149
  34. package/dist/unstable-core-do-not-import/http/resolveResponse.mjs +301 -148
  35. package/dist/unstable-core-do-not-import/http/types.d.ts +34 -5
  36. package/dist/unstable-core-do-not-import/http/types.d.ts.map +1 -1
  37. package/dist/unstable-core-do-not-import/initTRPC.d.ts +12 -12
  38. package/dist/unstable-core-do-not-import/initTRPC.d.ts.map +1 -1
  39. package/dist/unstable-core-do-not-import/middleware.d.ts +3 -3
  40. package/dist/unstable-core-do-not-import/middleware.d.ts.map +1 -1
  41. package/dist/unstable-core-do-not-import/procedureBuilder.d.ts +3 -1
  42. package/dist/unstable-core-do-not-import/procedureBuilder.d.ts.map +1 -1
  43. package/dist/unstable-core-do-not-import/rootConfig.d.ts +12 -0
  44. package/dist/unstable-core-do-not-import/rootConfig.d.ts.map +1 -1
  45. package/dist/unstable-core-do-not-import/router.d.ts +2 -2
  46. package/dist/unstable-core-do-not-import/router.d.ts.map +1 -1
  47. package/dist/unstable-core-do-not-import/router.js +6 -0
  48. package/dist/unstable-core-do-not-import/router.mjs +6 -0
  49. package/dist/unstable-core-do-not-import/stream/{stream.d.ts → jsonl.d.ts} +5 -5
  50. package/dist/unstable-core-do-not-import/stream/jsonl.d.ts.map +1 -0
  51. package/dist/unstable-core-do-not-import/stream/{stream.js → jsonl.js} +90 -89
  52. package/dist/unstable-core-do-not-import/stream/{stream.mjs → jsonl.mjs} +89 -88
  53. package/dist/unstable-core-do-not-import/stream/sse.d.ts +95 -0
  54. package/dist/unstable-core-do-not-import/stream/sse.d.ts.map +1 -0
  55. package/dist/unstable-core-do-not-import/stream/sse.js +175 -0
  56. package/dist/unstable-core-do-not-import/stream/sse.mjs +169 -0
  57. package/dist/unstable-core-do-not-import/stream/utils/createDeferred.d.ts +18 -0
  58. package/dist/unstable-core-do-not-import/stream/utils/createDeferred.d.ts.map +1 -0
  59. package/dist/unstable-core-do-not-import/stream/utils/createDeferred.js +46 -0
  60. package/dist/unstable-core-do-not-import/stream/utils/createDeferred.mjs +43 -0
  61. package/dist/unstable-core-do-not-import/stream/utils/createReadableStream.d.ts +10 -0
  62. package/dist/unstable-core-do-not-import/stream/utils/createReadableStream.d.ts.map +1 -0
  63. package/dist/unstable-core-do-not-import/stream/utils/createReadableStream.js +31 -0
  64. package/dist/unstable-core-do-not-import/stream/utils/createReadableStream.mjs +29 -0
  65. package/dist/unstable-core-do-not-import/stream/utils/createServer.d.ts +7 -0
  66. package/dist/unstable-core-do-not-import/stream/utils/createServer.d.ts.map +1 -0
  67. package/dist/unstable-core-do-not-import/transformer.d.ts +5 -5
  68. package/dist/unstable-core-do-not-import/utils.d.ts +4 -0
  69. package/dist/unstable-core-do-not-import/utils.d.ts.map +1 -1
  70. package/dist/unstable-core-do-not-import/utils.js +4 -0
  71. package/dist/unstable-core-do-not-import/utils.mjs +4 -1
  72. package/dist/unstable-core-do-not-import.d.ts +2 -1
  73. package/dist/unstable-core-do-not-import.d.ts.map +1 -1
  74. package/dist/unstable-core-do-not-import.js +11 -4
  75. package/dist/unstable-core-do-not-import.mjs +3 -2
  76. package/package.json +3 -3
  77. package/src/@trpc/server/index.ts +1 -0
  78. package/src/adapters/node-http/incomingMessageToRequest.ts +3 -2
  79. package/src/adapters/node-http/nodeHTTPRequestHandler.ts +32 -7
  80. package/src/adapters/ws.ts +101 -75
  81. package/src/observable/observable.ts +63 -0
  82. package/src/unstable-core-do-not-import/http/contentType.ts +78 -21
  83. package/src/unstable-core-do-not-import/http/resolveResponse.ts +331 -164
  84. package/src/unstable-core-do-not-import/http/types.ts +42 -5
  85. package/src/unstable-core-do-not-import/procedureBuilder.ts +8 -1
  86. package/src/unstable-core-do-not-import/rootConfig.ts +12 -0
  87. package/src/unstable-core-do-not-import/router.ts +12 -0
  88. package/src/unstable-core-do-not-import/stream/{stream.ts → jsonl.ts} +99 -85
  89. package/src/unstable-core-do-not-import/stream/sse.ts +291 -0
  90. package/src/unstable-core-do-not-import/stream/utils/createDeferred.ts +48 -0
  91. package/src/unstable-core-do-not-import/stream/utils/createReadableStream.ts +31 -0
  92. package/src/unstable-core-do-not-import/stream/utils/createServer.ts +46 -0
  93. package/src/unstable-core-do-not-import/utils.ts +5 -0
  94. package/src/unstable-core-do-not-import.ts +2 -1
  95. package/dist/unstable-core-do-not-import/stream/stream.d.ts.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/unstable-core-do-not-import/utils.ts"],"names":[],"mappings":"AAAA,gBAAgB;AAChB,eAAO,MAAM,WAAW,eAAwB,CAAC;AACjD,MAAM,MAAM,WAAW,GAAG,OAAO,WAAW,CAAC;AAE7C;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACzE,IAAI,EAAE,KAAK,EACX,GAAG,IAAI,EAAE,OAAO,CAAC,KAAK,CAAC,EAAE,GACxB,KAAK,CAYP;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEzE;AAED,KAAK,KAAK,GAAG,CAAC,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,GAAG,EAAE,OAAO,CAAC,CAAC;AACxE,wBAAgB,UAAU,CAAC,EAAE,EAAE,OAAO,GAAG,EAAE,IAAI,KAAK,CAEnD;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,IAAI,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChE,GAAG,EAAE,IAAI,GACR,IAAI,CAEN;AAKD,wBAAgB,eAAe,CAAC,MAAM,EACpC,KAAK,EAAE,OAAO,GACb,KAAK,IAAI,aAAa,CAAC,MAAM,CAAC,CAIhC"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/unstable-core-do-not-import/utils.ts"],"names":[],"mappings":"AAAA,gBAAgB;AAChB,eAAO,MAAM,WAAW,eAAwB,CAAC;AACjD,MAAM,MAAM,WAAW,GAAG,OAAO,WAAW,CAAC;AAE7C;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACzE,IAAI,EAAE,KAAK,EACX,GAAG,IAAI,EAAE,OAAO,CAAC,KAAK,CAAC,EAAE,GACxB,KAAK,CAYP;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEzE;AAED,KAAK,KAAK,GAAG,CAAC,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,GAAG,EAAE,OAAO,CAAC,CAAC;AACxE,wBAAgB,UAAU,CAAC,EAAE,EAAE,OAAO,GAAG,EAAE,IAAI,KAAK,CAEnD;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,IAAI,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChE,GAAG,EAAE,IAAI,GACR,IAAI,CAEN;AAKD,wBAAgB,eAAe,CAAC,MAAM,EACpC,KAAK,EAAE,OAAO,GACb,KAAK,IAAI,aAAa,CAAC,MAAM,CAAC,CAIhC;AAED;;GAEG;AACH,eAAO,MAAM,GAAG,GAAI,MAAM,MAAM,MAAM,MAAM,KAAG,MAAc,CAAC"}
@@ -35,10 +35,14 @@ const asyncIteratorsSupported = typeof Symbol === 'function' && !!Symbol.asyncIt
35
35
  function isAsyncIterable(value) {
36
36
  return asyncIteratorsSupported && isObject(value) && Symbol.asyncIterator in value;
37
37
  }
38
+ /**
39
+ * Run an IIFE
40
+ */ const run = (fn)=>fn();
38
41
 
39
42
  exports.isAsyncIterable = isAsyncIterable;
40
43
  exports.isFunction = isFunction;
41
44
  exports.isObject = isObject;
42
45
  exports.mergeWithoutOverrides = mergeWithoutOverrides;
43
46
  exports.omitPrototype = omitPrototype;
47
+ exports.run = run;
44
48
  exports.unsetMarker = unsetMarker;
@@ -33,5 +33,8 @@ const asyncIteratorsSupported = typeof Symbol === 'function' && !!Symbol.asyncIt
33
33
  function isAsyncIterable(value) {
34
34
  return asyncIteratorsSupported && isObject(value) && Symbol.asyncIterator in value;
35
35
  }
36
+ /**
37
+ * Run an IIFE
38
+ */ const run = (fn)=>fn();
36
39
 
37
- export { isAsyncIterable, isFunction, isObject, mergeWithoutOverrides, omitPrototype, unsetMarker };
40
+ export { isAsyncIterable, isFunction, isObject, mergeWithoutOverrides, omitPrototype, run, unsetMarker };
@@ -33,5 +33,6 @@ export * from './unstable-core-do-not-import/rpc';
33
33
  export * from './unstable-core-do-not-import/transformer';
34
34
  export * from './unstable-core-do-not-import/types';
35
35
  export * from './unstable-core-do-not-import/utils';
36
- export * from './unstable-core-do-not-import/stream/stream';
36
+ export * from './unstable-core-do-not-import/stream/jsonl';
37
+ export * from './unstable-core-do-not-import/stream/sse';
37
38
  //# sourceMappingURL=unstable-core-do-not-import.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"unstable-core-do-not-import.d.ts","sourceRoot":"","sources":["../src/unstable-core-do-not-import.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,cAAc,mDAAmD,CAAC;AAClE,cAAc,oDAAoD,CAAC;AACnE,cAAc,mDAAmD,CAAC;AAClE,cAAc,2CAA2C,CAAC;AAC1D,cAAc,+CAA+C,CAAC;AAC9D,cAAc,+CAA+C,CAAC;AAC9D,cAAc,mDAAmD,CAAC;AAClE,cAAc,yDAAyD,CAAC;AACxE,cAAc,gDAAgD,CAAC;AAC/D,cAAc,uDAAuD,CAAC;AACtE,cAAc,sDAAsD,CAAC;AACrE,cAAc,oDAAoD,CAAC;AACnE,cAAc,0CAA0C,CAAC;AACzD,cAAc,0CAA0C,CAAC;AACzD,cAAc,wCAAwC,CAAC;AACvD,cAAc,0CAA0C,CAAC;AACzD,cAAc,sCAAsC,CAAC;AACrD,cAAc,yCAAyC,CAAC;AACxD,cAAc,gDAAgD,CAAC;AAC/D,cAAc,0CAA0C,CAAC;AACzD,cAAc,sCAAsC,CAAC;AACrD,cAAc,mCAAmC,CAAC;AAClD,cAAc,2CAA2C,CAAC;AAC1D,cAAc,qCAAqC,CAAC;AACpD,cAAc,qCAAqC,CAAC;AACpD,cAAc,6CAA6C,CAAC"}
1
+ {"version":3,"file":"unstable-core-do-not-import.d.ts","sourceRoot":"","sources":["../src/unstable-core-do-not-import.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,cAAc,mDAAmD,CAAC;AAClE,cAAc,oDAAoD,CAAC;AACnE,cAAc,mDAAmD,CAAC;AAClE,cAAc,2CAA2C,CAAC;AAC1D,cAAc,+CAA+C,CAAC;AAC9D,cAAc,+CAA+C,CAAC;AAC9D,cAAc,mDAAmD,CAAC;AAClE,cAAc,yDAAyD,CAAC;AACxE,cAAc,gDAAgD,CAAC;AAC/D,cAAc,uDAAuD,CAAC;AACtE,cAAc,sDAAsD,CAAC;AACrE,cAAc,oDAAoD,CAAC;AACnE,cAAc,0CAA0C,CAAC;AACzD,cAAc,0CAA0C,CAAC;AACzD,cAAc,wCAAwC,CAAC;AACvD,cAAc,0CAA0C,CAAC;AACzD,cAAc,sCAAsC,CAAC;AACrD,cAAc,yCAAyC,CAAC;AACxD,cAAc,gDAAgD,CAAC;AAC/D,cAAc,0CAA0C,CAAC;AACzD,cAAc,sCAAsC,CAAC;AACrD,cAAc,mCAAmC,CAAC;AAClD,cAAc,2CAA2C,CAAC;AAC1D,cAAc,qCAAqC,CAAC;AACpD,cAAc,qCAAqC,CAAC;AACpD,cAAc,4CAA4C,CAAC;AAC3D,cAAc,0CAA0C,CAAC"}
@@ -22,7 +22,8 @@ var parseTRPCMessage = require('./unstable-core-do-not-import/rpc/parseTRPCMessa
22
22
  var transformer = require('./unstable-core-do-not-import/transformer.js');
23
23
  var types = require('./unstable-core-do-not-import/types.js');
24
24
  var utils = require('./unstable-core-do-not-import/utils.js');
25
- var stream = require('./unstable-core-do-not-import/stream/stream.js');
25
+ var jsonl = require('./unstable-core-do-not-import/stream/jsonl.js');
26
+ var sse = require('./unstable-core-do-not-import/stream/sse.js');
26
27
 
27
28
 
28
29
 
@@ -67,7 +68,13 @@ exports.isFunction = utils.isFunction;
67
68
  exports.isObject = utils.isObject;
68
69
  exports.mergeWithoutOverrides = utils.mergeWithoutOverrides;
69
70
  exports.omitPrototype = utils.omitPrototype;
71
+ exports.run = utils.run;
70
72
  exports.unsetMarker = utils.unsetMarker;
71
- exports.isPromise = stream.isPromise;
72
- exports.jsonlStreamConsumer = stream.jsonlStreamConsumer;
73
- exports.jsonlStreamProducer = stream.jsonlStreamProducer;
73
+ exports.isPromise = jsonl.isPromise;
74
+ exports.jsonlStreamConsumer = jsonl.jsonlStreamConsumer;
75
+ exports.jsonlStreamProducer = jsonl.jsonlStreamProducer;
76
+ exports.isServerSentEventEnvelope = sse.isServerSentEventEnvelope;
77
+ exports.sse = sse.sse;
78
+ exports.sseHeaders = sse.sseHeaders;
79
+ exports.sseStreamConsumer = sse.sseStreamConsumer;
80
+ exports.sseStreamProducer = sse.sseStreamProducer;
@@ -19,5 +19,6 @@ export { TRPC_ERROR_CODES_BY_KEY, TRPC_ERROR_CODES_BY_NUMBER } from './unstable-
19
19
  export { parseTRPCMessage } from './unstable-core-do-not-import/rpc/parseTRPCMessage.mjs';
20
20
  export { defaultTransformer, getDataTransformer, transformResult, transformTRPCResponse } from './unstable-core-do-not-import/transformer.mjs';
21
21
  export { ERROR_SYMBOL } from './unstable-core-do-not-import/types.mjs';
22
- export { isAsyncIterable, isFunction, isObject, mergeWithoutOverrides, omitPrototype, unsetMarker } from './unstable-core-do-not-import/utils.mjs';
23
- export { isPromise, jsonlStreamConsumer, jsonlStreamProducer } from './unstable-core-do-not-import/stream/stream.mjs';
22
+ export { isAsyncIterable, isFunction, isObject, mergeWithoutOverrides, omitPrototype, run, unsetMarker } from './unstable-core-do-not-import/utils.mjs';
23
+ export { isPromise, jsonlStreamConsumer, jsonlStreamProducer } from './unstable-core-do-not-import/stream/jsonl.mjs';
24
+ export { isServerSentEventEnvelope, sse, sseHeaders, sseStreamConsumer, sseStreamProducer } from './unstable-core-do-not-import/stream/sse.mjs';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trpc/server",
3
- "version": "11.0.0-rc.417+94650c185",
3
+ "version": "11.0.0-rc.419+343b6de2e",
4
4
  "description": "The tRPC server library",
5
5
  "author": "KATT",
6
6
  "license": "MIT",
@@ -140,7 +140,7 @@
140
140
  "superstruct": "^1.0.0",
141
141
  "tslib": "^2.5.0",
142
142
  "tsx": "^4.0.0",
143
- "typescript": "^5.4.0",
143
+ "typescript": "^5.5.0",
144
144
  "valibot": "^0.33.0",
145
145
  "ws": "^8.0.0",
146
146
  "yup": "^1.0.0",
@@ -149,5 +149,5 @@
149
149
  "funding": [
150
150
  "https://trpc.io/sponsor"
151
151
  ],
152
- "gitHead": "94650c185b067c86f2e71fb224b1e70f463632b7"
152
+ "gitHead": "343b6de2ea719347bffe8332f37589c66c323349"
153
153
  }
@@ -37,6 +37,7 @@ export {
37
37
  type QueryProcedure as TRPCQueryProcedure,
38
38
  type SubscriptionProcedure as TRPCSubscriptionProcedure,
39
39
  type TRPCBuilder,
40
+ sse,
40
41
  } from '../../unstable-core-do-not-import';
41
42
 
42
43
  export type {
@@ -68,8 +68,9 @@ export function incomingMessageToRequest(
68
68
  const ac = new AbortController();
69
69
  const headers = new Headers(req.headers as any);
70
70
  const url = `http://${headers.get('host')}${req.url}`;
71
-
72
- req.once('aborted', () => ac.abort());
71
+ req.once('aborted', () => {
72
+ ac.abort();
73
+ });
73
74
 
74
75
  const init: RequestInit = {
75
76
  headers,
@@ -10,7 +10,6 @@
10
10
 
11
11
  // @trpc/server
12
12
 
13
- import { Readable } from 'node:stream';
14
13
  import { getTRPCErrorFromUnknown, type AnyRouter } from '../../@trpc/server';
15
14
  import type { ResolveHTTPRequestOptionsContextFn } from '../../@trpc/server/http';
16
15
  import { resolveResponse } from '../../@trpc/server/http';
@@ -56,17 +55,43 @@ export async function nodeHTTPRequestHandler<
56
55
  },
57
56
  });
58
57
 
59
- if (opts.res.statusCode === 200) {
58
+ const { res } = opts;
59
+ if (res.statusCode === 200) {
60
60
  // if the status code is set, we assume that it's been manually overridden
61
- opts.res.statusCode = response.status;
61
+ res.statusCode = response.status;
62
62
  }
63
63
  for (const [key, value] of response.headers) {
64
- opts.res.setHeader(key, value);
64
+ res.setHeader(key, value);
65
65
  }
66
66
  if (response.body) {
67
- Readable.fromWeb(response.body as any).pipe(opts.res);
68
- } else {
69
- opts.res.end();
67
+ const reader = response.body.getReader();
68
+ const onAbort = () => {
69
+ // cancelling the reader will cause the whole stream to be cancelled
70
+ reader.cancel().catch(() => {
71
+ // console.error('reader.cancel() error', err);
72
+ });
73
+ };
74
+ req.signal.addEventListener('abort', onAbort);
75
+
76
+ while (true) {
77
+ const { done, value } = await reader.read();
78
+
79
+ if (done) {
80
+ break;
81
+ }
82
+ if (!res.writable) {
83
+ break;
84
+ }
85
+ if (!res.write(value)) {
86
+ await new Promise<void>((resolve) => {
87
+ res.once('drain', resolve);
88
+ });
89
+ }
90
+ // IMPORTANT - flush the response buffer, otherwise the client will not receive the data until `.end()`
91
+ res.flush?.();
92
+ }
93
+ req.signal.removeEventListener('abort', onAbort);
70
94
  }
95
+ res.end();
71
96
  });
72
97
  }
@@ -16,15 +16,18 @@ import type { BaseHandlerOptions } from '../@trpc/server/http';
16
16
  import { parseTRPCMessage } from '../@trpc/server/rpc';
17
17
  // @trpc/server/rpc
18
18
  import type {
19
- JSONRPC2,
20
19
  TRPCClientOutgoingMessage,
21
20
  TRPCReconnectNotification,
22
21
  TRPCResponseMessage,
23
22
  } from '../@trpc/server/rpc';
24
23
  import { isObservable } from '../observable';
25
- import type { Unsubscribable } from '../observable';
24
+ import { observableToAsyncIterable } from '../observable/observable';
26
25
  // eslint-disable-next-line no-restricted-imports
27
- import type { MaybePromise } from '../unstable-core-do-not-import';
26
+ import {
27
+ isAsyncIterable,
28
+ run,
29
+ type MaybePromise,
30
+ } from '../unstable-core-do-not-import';
28
31
  import type { NodeHTTPCreateContextFnOptions } from './node-http';
29
32
 
30
33
  /**
@@ -88,7 +91,7 @@ export function getWSConnectionHandler<TRouter extends AnyRouter>(
88
91
  const { transformer } = router._def._config;
89
92
 
90
93
  return async (client: ws.WebSocket, req: IncomingMessage) => {
91
- const clientSubscriptions = new Map<number | string, Unsubscribable>();
94
+ const clientSubscriptions = new Map<number | string, AbortController>();
92
95
 
93
96
  function respond(untransformedJSON: TRPCResponseMessage) {
94
97
  client.send(
@@ -98,21 +101,6 @@ export function getWSConnectionHandler<TRouter extends AnyRouter>(
98
101
  );
99
102
  }
100
103
 
101
- function stopSubscription(
102
- subscription: Unsubscribable,
103
- { id, jsonrpc }: JSONRPC2.BaseEnvelope & { id: JSONRPC2.RequestId },
104
- ) {
105
- subscription.unsubscribe();
106
-
107
- respond({
108
- id,
109
- jsonrpc,
110
- result: {
111
- type: 'stopped',
112
- },
113
- });
114
- }
115
-
116
104
  const ctxPromise = createContext?.({ req, res: client });
117
105
  let ctx: inferRouterContext<TRouter> | undefined = undefined;
118
106
 
@@ -126,11 +114,7 @@ export function getWSConnectionHandler<TRouter extends AnyRouter>(
126
114
  });
127
115
  }
128
116
  if (msg.method === 'subscription.stop') {
129
- const sub = clientSubscriptions.get(id);
130
- if (sub) {
131
- stopSubscription(sub, { id, jsonrpc });
132
- }
133
- clientSubscriptions.delete(id);
117
+ clientSubscriptions.get(id)?.abort();
134
118
  return;
135
119
  }
136
120
  const { path, input } = msg.params;
@@ -146,14 +130,7 @@ export function getWSConnectionHandler<TRouter extends AnyRouter>(
146
130
  type,
147
131
  });
148
132
 
149
- if (type === 'subscription') {
150
- if (!isObservable(result)) {
151
- throw new TRPCError({
152
- message: `Subscription ${path} did not return an observable`,
153
- code: 'INTERNAL_SERVER_ERROR',
154
- });
155
- }
156
- } else {
133
+ if (type !== 'subscription') {
157
134
  // send the value as data if the method is not a subscription
158
135
  respond({
159
136
  id,
@@ -166,62 +143,112 @@ export function getWSConnectionHandler<TRouter extends AnyRouter>(
166
143
  return;
167
144
  }
168
145
 
169
- const observable = result;
170
- const sub = observable.subscribe({
171
- next(data) {
172
- respond({
173
- id,
174
- jsonrpc,
175
- result: {
176
- type: 'data',
177
- data,
178
- },
179
- });
180
- },
181
- error(err) {
182
- const error = getTRPCErrorFromUnknown(err);
183
- opts.onError?.({ error, path, type, ctx, req, input });
184
- respond({
185
- id,
186
- jsonrpc,
187
- error: getErrorShape({
188
- config: router._def._config,
189
- error,
190
- type,
191
- path,
192
- input,
193
- ctx,
194
- }),
195
- });
196
- },
197
- complete() {
198
- respond({
199
- id,
200
- jsonrpc,
201
- result: {
202
- type: 'stopped',
203
- },
204
- });
205
- },
206
- });
146
+ if (!isObservable(result) && !isAsyncIterable(result)) {
147
+ throw new TRPCError({
148
+ message: `Subscription ${path} did not return an observable or a AsyncGenerator`,
149
+ code: 'INTERNAL_SERVER_ERROR',
150
+ });
151
+ }
152
+
207
153
  /* istanbul ignore next -- @preserve */
208
154
  if (client.readyState !== WEBSOCKET_OPEN) {
209
155
  // if the client got disconnected whilst initializing the subscription
210
156
  // no need to send stopped message if the client is disconnected
211
- sub.unsubscribe();
157
+
212
158
  return;
213
159
  }
214
160
 
215
161
  /* istanbul ignore next -- @preserve */
216
162
  if (clientSubscriptions.has(id)) {
217
163
  // duplicate request ids for client
218
- stopSubscription(sub, { id, jsonrpc });
164
+
219
165
  throw new TRPCError({
220
166
  message: `Duplicate id ${id}`,
221
167
  code: 'BAD_REQUEST',
222
168
  });
223
169
  }
224
- clientSubscriptions.set(id, sub);
170
+
171
+ const iterable = isObservable(result)
172
+ ? observableToAsyncIterable(result)
173
+ : result;
174
+
175
+ const iterator: AsyncIterator<unknown> =
176
+ iterable[Symbol.asyncIterator]();
177
+ const abortController = new AbortController();
178
+
179
+ const abortPromise = new Promise<'abort'>((resolve) => {
180
+ abortController.signal.onabort = () => resolve('abort');
181
+ });
182
+
183
+ run(async () => {
184
+ while (true) {
185
+ const next = await Promise.race([
186
+ iterator.next().catch(getTRPCErrorFromUnknown),
187
+ abortPromise,
188
+ ]);
189
+
190
+ if (next === 'abort') {
191
+ await iterator.return?.();
192
+ break;
193
+ }
194
+ if (next instanceof Error) {
195
+ const error = getTRPCErrorFromUnknown(next);
196
+ opts.onError?.({ error, path, type, ctx, req, input });
197
+ respond({
198
+ id,
199
+ jsonrpc,
200
+ error: getErrorShape({
201
+ config: router._def._config,
202
+ error,
203
+ type,
204
+ path,
205
+ input,
206
+ ctx,
207
+ }),
208
+ });
209
+ break;
210
+ }
211
+ if (next.done) {
212
+ break;
213
+ }
214
+
215
+ respond({
216
+ id,
217
+ jsonrpc,
218
+ result: {
219
+ type: 'data',
220
+ data: next.value,
221
+ },
222
+ });
223
+ }
224
+
225
+ await iterator.return?.();
226
+ respond({
227
+ id,
228
+ jsonrpc,
229
+ result: {
230
+ type: 'stopped',
231
+ },
232
+ });
233
+ clientSubscriptions.delete(id);
234
+ }).catch((cause) => {
235
+ const error = getTRPCErrorFromUnknown(cause);
236
+ opts.onError?.({ error, path, type, ctx, req, input });
237
+ respond({
238
+ id,
239
+ jsonrpc,
240
+ error: getErrorShape({
241
+ config: router._def._config,
242
+ error,
243
+ type,
244
+ path,
245
+ input,
246
+ ctx,
247
+ }),
248
+ });
249
+ abortController.abort();
250
+ });
251
+ clientSubscriptions.set(id, abortController);
225
252
 
226
253
  respond({
227
254
  id,
@@ -250,7 +277,6 @@ export function getWSConnectionHandler<TRouter extends AnyRouter>(
250
277
  }
251
278
  client.on('message', async (message) => {
252
279
  try {
253
- // eslint-disable-next-line @typescript-eslint/no-base-to-string
254
280
  const msgJSON: unknown = JSON.parse(message.toString());
255
281
  const msgs: unknown[] = Array.isArray(msgJSON) ? msgJSON : [msgJSON];
256
282
  const promises = msgs
@@ -294,7 +320,7 @@ export function getWSConnectionHandler<TRouter extends AnyRouter>(
294
320
 
295
321
  client.once('close', () => {
296
322
  for (const sub of clientSubscriptions.values()) {
297
- sub.unsubscribe();
323
+ sub.abort();
298
324
  }
299
325
  clientSubscriptions.clear();
300
326
  });
@@ -4,6 +4,7 @@ import type {
4
4
  OperatorFunction,
5
5
  TeardownLogic,
6
6
  UnaryFunction,
7
+ Unsubscribable,
7
8
  } from './types';
8
9
 
9
10
  /** @public */
@@ -136,3 +137,65 @@ export function observableToPromise<TValue>(
136
137
  abort: abort!,
137
138
  };
138
139
  }
140
+
141
+ /**
142
+ * @internal
143
+ */
144
+ function observableToReadableStream<TValue>(
145
+ observable: Observable<TValue, unknown>,
146
+ ): ReadableStream<TValue> {
147
+ let unsub: Unsubscribable | null = null;
148
+ return new ReadableStream<TValue>({
149
+ start(controller) {
150
+ unsub = observable.subscribe({
151
+ next(data) {
152
+ controller.enqueue(data);
153
+ },
154
+ error(err) {
155
+ controller.error(err);
156
+ },
157
+ complete() {
158
+ controller.close();
159
+ },
160
+ });
161
+ },
162
+ cancel() {
163
+ unsub?.unsubscribe();
164
+ },
165
+ });
166
+ }
167
+
168
+ export function observableToAsyncIterable<TValue>(
169
+ observable: Observable<TValue, unknown>,
170
+ ): AsyncIterable<TValue> {
171
+ const stream = observableToReadableStream(observable);
172
+
173
+ const reader = stream.getReader();
174
+ const iterator: AsyncIterator<TValue> = {
175
+ async next() {
176
+ const value = await reader.read();
177
+ if (value.done) {
178
+ return {
179
+ value: undefined,
180
+ done: true,
181
+ };
182
+ }
183
+ return {
184
+ value: value.value,
185
+ done: false,
186
+ };
187
+ },
188
+ async return() {
189
+ await reader.cancel();
190
+ return {
191
+ value: undefined,
192
+ done: true,
193
+ };
194
+ },
195
+ };
196
+ return {
197
+ [Symbol.asyncIterator]() {
198
+ return iterator;
199
+ },
200
+ };
201
+ }
@@ -1,16 +1,24 @@
1
1
  import { TRPCError } from '../error/TRPCError';
2
- import type { RootConfig } from '../rootConfig';
2
+ import type { AnyProcedure, ProcedureType } from '../procedure';
3
+ import type { AnyRouter } from '../router';
3
4
  import { isObject, unsetMarker } from '../utils';
4
- import type { TRPCRequestInfo } from './types';
5
+ import type {
6
+ TRPCAcceptHeader,
7
+ TRPCRequestInfo,
8
+ TRPCRequestInfoBase,
9
+ } from './types';
10
+
11
+ type GetRequestInfoOptions = {
12
+ path: string;
13
+ req: Request;
14
+ searchParams: URLSearchParams;
15
+ headers: Headers;
16
+ router: AnyRouter;
17
+ };
5
18
 
6
19
  type ContentTypeHandler = {
7
20
  isMatch: (opts: Request) => boolean;
8
- parse: (opts: {
9
- path: string;
10
- req: Request;
11
- searchParams: URLSearchParams;
12
- config: RootConfig<any>;
13
- }) => TRPCRequestInfo;
21
+ parse: (opts: GetRequestInfoOptions) => TRPCRequestInfo;
14
22
  };
15
23
 
16
24
  /**
@@ -82,7 +90,7 @@ const jsonContentTypeHandler: ContentTypeHandler = {
82
90
 
83
91
  if (!isBatchCall) {
84
92
  return {
85
- 0: opts.config.transformer.input.deserialize(inputs),
93
+ 0: opts.router._def._config.transformer.input.deserialize(inputs),
86
94
  };
87
95
  }
88
96
 
@@ -96,26 +104,74 @@ const jsonContentTypeHandler: ContentTypeHandler = {
96
104
  for (const index of paths.keys()) {
97
105
  const input = inputs[index];
98
106
  if (input !== undefined) {
99
- acc[index] = opts.config.transformer.input.deserialize(input);
107
+ acc[index] =
108
+ opts.router._def._config.transformer.input.deserialize(input);
100
109
  }
101
110
  }
102
111
 
103
112
  return acc;
104
113
  });
105
114
 
106
- return {
107
- isBatchCall,
108
- calls: paths.map((path, index) => ({
115
+ const calls = paths.map((path, index): TRPCRequestInfo['calls'][number] => {
116
+ const procedure: AnyProcedure | null =
117
+ opts.router._def.procedures[path] ?? null;
118
+ return {
109
119
  path,
120
+ procedure,
110
121
  getRawInput: async () => {
111
122
  const inputs = await getInputs.read();
112
- return inputs[index];
123
+ let input = inputs[index];
124
+
125
+ if (procedure?._def.type === 'subscription') {
126
+ const lastEventId =
127
+ opts.headers.get('last-event-id') ??
128
+ opts.searchParams.get('lastEventId') ??
129
+ opts.searchParams.get('Last-Event-Id');
130
+
131
+ if (lastEventId) {
132
+ if (isObject(input)) {
133
+ input = {
134
+ ...input,
135
+ lastEventId: lastEventId,
136
+ };
137
+ } else {
138
+ input ??= {
139
+ lastEventId: lastEventId,
140
+ };
141
+ }
142
+ }
143
+ }
144
+ return input;
113
145
  },
114
146
  result: () => {
115
147
  return getInputs.result()?.[index];
116
148
  },
117
- })),
149
+ };
150
+ });
151
+
152
+ const types = new Set(
153
+ calls.map((call) => call.procedure?._def.type).filter(Boolean),
154
+ );
155
+
156
+ /* istanbul ignore if -- @preserve */
157
+ if (types.size > 1) {
158
+ throw new TRPCError({
159
+ code: 'BAD_REQUEST',
160
+ message: `Cannot mix procedure types in call: ${Array.from(types).join(
161
+ ', ',
162
+ )}`,
163
+ });
164
+ }
165
+ const type: ProcedureType | 'unknown' =
166
+ types.values().next().value ?? 'unknown';
167
+
168
+ const info: TRPCRequestInfoBase = {
169
+ isBatchCall,
170
+ accept: req.headers.get('trpc-accept') as TRPCAcceptHeader | null,
171
+ calls,
172
+ type,
118
173
  };
174
+ return info as TRPCRequestInfo;
119
175
  },
120
176
  };
121
177
 
@@ -137,14 +193,17 @@ const formDataContentTypeHandler: ContentTypeHandler = {
137
193
  return fd;
138
194
  });
139
195
  return {
196
+ accept: null,
140
197
  calls: [
141
198
  {
142
199
  path: opts.path,
143
200
  getRawInput: getInputs.read,
144
201
  result: getInputs.result,
202
+ procedure: opts.router._def.procedures[opts.path] ?? null,
145
203
  },
146
204
  ],
147
205
  isBatchCall: false,
206
+ type: 'mutation',
148
207
  };
149
208
  },
150
209
  };
@@ -173,9 +232,12 @@ const octetStreamContentTypeHandler: ContentTypeHandler = {
173
232
  path: opts.path,
174
233
  getRawInput: getInputs.read,
175
234
  result: getInputs.result,
235
+ procedure: opts.router._def.procedures[opts.path] ?? null,
176
236
  },
177
237
  ],
178
238
  isBatchCall: false,
239
+ accept: null,
240
+ type: 'mutation',
179
241
  };
180
242
  },
181
243
  };
@@ -205,12 +267,7 @@ function getContentTypeHandler(req: Request): ContentTypeHandler {
205
267
  });
206
268
  }
207
269
 
208
- export function getRequestInfo(opts: {
209
- path: string;
210
- req: Request;
211
- searchParams: URLSearchParams;
212
- config: RootConfig<any>;
213
- }): TRPCRequestInfo {
270
+ export function getRequestInfo(opts: GetRequestInfoOptions): TRPCRequestInfo {
214
271
  const handler = getContentTypeHandler(opts.req);
215
272
  return handler.parse(opts);
216
273
  }