@trpc/server 11.0.0-alpha-tmp-subscription-connection-state.489 → 11.0.0-alpha-tmp-12-06-react.665

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 (274) hide show
  1. package/dist/@trpc/server/http.d.ts +1 -1
  2. package/dist/@trpc/server/http.d.ts.map +1 -1
  3. package/dist/@trpc/server/index.d.ts +1 -1
  4. package/dist/@trpc/server/index.d.ts.map +1 -1
  5. package/dist/adapters/aws-lambda/getPlanner.d.ts.map +1 -1
  6. package/dist/adapters/aws-lambda/getPlanner.js +2 -2
  7. package/dist/adapters/aws-lambda/getPlanner.mjs +2 -2
  8. package/dist/adapters/aws-lambda/index.d.ts.map +1 -1
  9. package/dist/adapters/aws-lambda/index.js +2 -0
  10. package/dist/adapters/aws-lambda/index.mjs +2 -0
  11. package/dist/adapters/express.d.ts.map +1 -1
  12. package/dist/adapters/express.js +27 -7
  13. package/dist/adapters/express.mjs +27 -7
  14. package/dist/adapters/fastify/fastifyRequestHandler.js +3 -1
  15. package/dist/adapters/fastify/fastifyRequestHandler.mjs +3 -1
  16. package/dist/adapters/fastify/fastifyTRPCPlugin.d.ts.map +1 -1
  17. package/dist/adapters/fastify/fastifyTRPCPlugin.js +9 -2
  18. package/dist/adapters/fastify/fastifyTRPCPlugin.mjs +10 -3
  19. package/dist/adapters/fetch/fetchRequestHandler.js +9 -8
  20. package/dist/adapters/fetch/fetchRequestHandler.mjs +9 -8
  21. package/dist/adapters/next-app-dir/nextAppDirCaller.d.ts.map +1 -1
  22. package/dist/adapters/next-app-dir/nextAppDirCaller.js +10 -5
  23. package/dist/adapters/next-app-dir/nextAppDirCaller.mjs +10 -5
  24. package/dist/adapters/next-app-dir/notFound.js +2 -0
  25. package/dist/adapters/next-app-dir/notFound.mjs +2 -0
  26. package/dist/adapters/next-app-dir/redirect.js +16 -1
  27. package/dist/adapters/next-app-dir/redirect.mjs +16 -1
  28. package/dist/adapters/next-app-dir/rethrowNextErrors.d.ts.map +1 -1
  29. package/dist/adapters/next-app-dir/rethrowNextErrors.js +4 -3
  30. package/dist/adapters/next-app-dir/rethrowNextErrors.mjs +4 -3
  31. package/dist/adapters/next.d.ts.map +1 -1
  32. package/dist/adapters/next.js +23 -30
  33. package/dist/adapters/next.mjs +24 -31
  34. package/dist/adapters/node-http/incomingMessageToRequest.d.ts +2 -1
  35. package/dist/adapters/node-http/incomingMessageToRequest.d.ts.map +1 -1
  36. package/dist/adapters/node-http/incomingMessageToRequest.js +83 -40
  37. package/dist/adapters/node-http/incomingMessageToRequest.mjs +83 -41
  38. package/dist/adapters/node-http/index.js +2 -0
  39. package/dist/adapters/node-http/index.mjs +2 -2
  40. package/dist/adapters/node-http/nodeHTTPRequestHandler.d.ts +7 -0
  41. package/dist/adapters/node-http/nodeHTTPRequestHandler.d.ts.map +1 -1
  42. package/dist/adapters/node-http/nodeHTTPRequestHandler.js +70 -58
  43. package/dist/adapters/node-http/nodeHTTPRequestHandler.mjs +70 -59
  44. package/dist/adapters/node-http/writeResponse.d.ts +18 -0
  45. package/dist/adapters/node-http/writeResponse.d.ts.map +1 -0
  46. package/dist/adapters/node-http/writeResponse.js +82 -0
  47. package/dist/adapters/node-http/writeResponse.mjs +79 -0
  48. package/dist/adapters/standalone.d.ts +5 -2
  49. package/dist/adapters/standalone.d.ts.map +1 -1
  50. package/dist/adapters/standalone.js +25 -13
  51. package/dist/adapters/standalone.mjs +26 -14
  52. package/dist/adapters/ws.d.ts +14 -4
  53. package/dist/adapters/ws.d.ts.map +1 -1
  54. package/dist/adapters/ws.js +217 -101
  55. package/dist/adapters/ws.mjs +217 -102
  56. package/dist/bundle-analysis.json +412 -310
  57. package/dist/http.js +2 -4
  58. package/dist/http.mjs +2 -2
  59. package/dist/index.js +3 -0
  60. package/dist/index.mjs +3 -1
  61. package/dist/node_modules/.pnpm/@rollup_plugin-typescript@12.1.1_rollup@4.27.4_tslib@2.8.1_typescript@5.7.2/node_modules/tslib/tslib.es6.js +73 -0
  62. package/dist/observable/behaviorSubject.d.ts +15 -0
  63. package/dist/observable/behaviorSubject.d.ts.map +1 -0
  64. package/dist/observable/behaviorSubject.js +40 -0
  65. package/dist/observable/behaviorSubject.mjs +38 -0
  66. package/dist/observable/index.d.ts +3 -3
  67. package/dist/observable/index.d.ts.map +1 -1
  68. package/dist/observable/index.js +5 -0
  69. package/dist/observable/index.mjs +3 -2
  70. package/dist/observable/observable.d.ts +2 -1
  71. package/dist/observable/observable.d.ts.map +1 -1
  72. package/dist/observable/observable.js +31 -8
  73. package/dist/observable/observable.mjs +31 -8
  74. package/dist/observable/operators.d.ts +2 -0
  75. package/dist/observable/operators.d.ts.map +1 -1
  76. package/dist/observable/operators.js +35 -0
  77. package/dist/observable/operators.mjs +34 -1
  78. package/dist/rpc.js +2 -0
  79. package/dist/rpc.mjs +2 -0
  80. package/dist/shared.js +2 -0
  81. package/dist/shared.mjs +2 -0
  82. package/dist/unstable-core-do-not-import/clientish/inference.d.ts.map +1 -1
  83. package/dist/unstable-core-do-not-import/clientish/inferrable.d.ts.map +1 -1
  84. package/dist/unstable-core-do-not-import/clientish/serialize.d.ts +2 -2
  85. package/dist/unstable-core-do-not-import/clientish/serialize.d.ts.map +1 -1
  86. package/dist/unstable-core-do-not-import/createProxy.js +1 -1
  87. package/dist/unstable-core-do-not-import/createProxy.mjs +1 -1
  88. package/dist/unstable-core-do-not-import/error/TRPCError.d.ts +1 -1
  89. package/dist/unstable-core-do-not-import/error/TRPCError.js +16 -1
  90. package/dist/unstable-core-do-not-import/error/TRPCError.mjs +16 -1
  91. package/dist/unstable-core-do-not-import/error/formatter.js +1 -1
  92. package/dist/unstable-core-do-not-import/error/formatter.mjs +1 -1
  93. package/dist/unstable-core-do-not-import/error/getErrorShape.js +2 -2
  94. package/dist/unstable-core-do-not-import/error/getErrorShape.mjs +2 -2
  95. package/dist/unstable-core-do-not-import/http/contentType.js +3 -3
  96. package/dist/unstable-core-do-not-import/http/contentType.mjs +3 -3
  97. package/dist/unstable-core-do-not-import/http/getHTTPStatusCode.d.ts +6 -1
  98. package/dist/unstable-core-do-not-import/http/getHTTPStatusCode.d.ts.map +1 -1
  99. package/dist/unstable-core-do-not-import/http/getHTTPStatusCode.js +28 -0
  100. package/dist/unstable-core-do-not-import/http/getHTTPStatusCode.mjs +25 -1
  101. package/dist/unstable-core-do-not-import/http/isAbortError.d.ts +4 -0
  102. package/dist/unstable-core-do-not-import/http/isAbortError.d.ts.map +1 -0
  103. package/dist/unstable-core-do-not-import/http/isAbortError.js +9 -0
  104. package/dist/unstable-core-do-not-import/http/isAbortError.mjs +7 -0
  105. package/dist/unstable-core-do-not-import/http/resolveResponse.d.ts.map +1 -1
  106. package/dist/unstable-core-do-not-import/http/resolveResponse.js +180 -96
  107. package/dist/unstable-core-do-not-import/http/resolveResponse.mjs +182 -98
  108. package/dist/unstable-core-do-not-import/http/types.d.ts +1 -1
  109. package/dist/unstable-core-do-not-import/initTRPC.d.ts +11 -11
  110. package/dist/unstable-core-do-not-import/initTRPC.d.ts.map +1 -1
  111. package/dist/unstable-core-do-not-import/initTRPC.js +11 -11
  112. package/dist/unstable-core-do-not-import/initTRPC.mjs +11 -11
  113. package/dist/unstable-core-do-not-import/middleware.d.ts +2 -1
  114. package/dist/unstable-core-do-not-import/middleware.d.ts.map +1 -1
  115. package/dist/unstable-core-do-not-import/middleware.js +2 -2
  116. package/dist/unstable-core-do-not-import/middleware.mjs +2 -2
  117. package/dist/unstable-core-do-not-import/parser.d.ts +5 -1
  118. package/dist/unstable-core-do-not-import/parser.d.ts.map +1 -1
  119. package/dist/unstable-core-do-not-import/parser.js +4 -0
  120. package/dist/unstable-core-do-not-import/parser.mjs +4 -0
  121. package/dist/unstable-core-do-not-import/procedure.d.ts +8 -2
  122. package/dist/unstable-core-do-not-import/procedure.d.ts.map +1 -1
  123. package/dist/unstable-core-do-not-import/procedureBuilder.d.ts +31 -12
  124. package/dist/unstable-core-do-not-import/procedureBuilder.d.ts.map +1 -1
  125. package/dist/unstable-core-do-not-import/procedureBuilder.js +33 -39
  126. package/dist/unstable-core-do-not-import/procedureBuilder.mjs +33 -39
  127. package/dist/unstable-core-do-not-import/rootConfig.d.ts +17 -17
  128. package/dist/unstable-core-do-not-import/rootConfig.d.ts.map +1 -1
  129. package/dist/unstable-core-do-not-import/router.d.ts +4 -3
  130. package/dist/unstable-core-do-not-import/router.d.ts.map +1 -1
  131. package/dist/unstable-core-do-not-import/router.js +6 -5
  132. package/dist/unstable-core-do-not-import/router.mjs +6 -5
  133. package/dist/unstable-core-do-not-import/rpc/codes.d.ts +2 -9
  134. package/dist/unstable-core-do-not-import/rpc/codes.d.ts.map +1 -1
  135. package/dist/unstable-core-do-not-import/rpc/envelopes.d.ts +7 -10
  136. package/dist/unstable-core-do-not-import/rpc/envelopes.d.ts.map +1 -1
  137. package/dist/unstable-core-do-not-import/rpc/parseTRPCMessage.js +2 -2
  138. package/dist/unstable-core-do-not-import/rpc/parseTRPCMessage.mjs +2 -2
  139. package/dist/unstable-core-do-not-import/stream/jsonl.d.ts +25 -14
  140. package/dist/unstable-core-do-not-import/stream/jsonl.d.ts.map +1 -1
  141. package/dist/unstable-core-do-not-import/stream/jsonl.js +439 -318
  142. package/dist/unstable-core-do-not-import/stream/jsonl.mjs +439 -318
  143. package/dist/unstable-core-do-not-import/stream/sse.d.ts +58 -18
  144. package/dist/unstable-core-do-not-import/stream/sse.d.ts.map +1 -1
  145. package/dist/unstable-core-do-not-import/stream/sse.js +297 -122
  146. package/dist/unstable-core-do-not-import/stream/sse.mjs +298 -123
  147. package/dist/unstable-core-do-not-import/stream/sse.types.d.ts +31 -0
  148. package/dist/unstable-core-do-not-import/stream/sse.types.d.ts.map +1 -0
  149. package/dist/unstable-core-do-not-import/stream/tracked.d.ts +2 -2
  150. package/dist/unstable-core-do-not-import/stream/tracked.d.ts.map +1 -1
  151. package/dist/unstable-core-do-not-import/stream/tracked.js +1 -1
  152. package/dist/unstable-core-do-not-import/stream/tracked.mjs +1 -1
  153. package/dist/unstable-core-do-not-import/stream/utils/asyncIterable.d.ts +17 -0
  154. package/dist/unstable-core-do-not-import/stream/utils/asyncIterable.d.ts.map +1 -0
  155. package/dist/unstable-core-do-not-import/stream/utils/asyncIterable.js +171 -0
  156. package/dist/unstable-core-do-not-import/stream/utils/asyncIterable.mjs +167 -0
  157. package/dist/unstable-core-do-not-import/stream/utils/createDeferred.d.ts +0 -11
  158. package/dist/unstable-core-do-not-import/stream/utils/createDeferred.d.ts.map +1 -1
  159. package/dist/unstable-core-do-not-import/stream/utils/createDeferred.js +0 -29
  160. package/dist/unstable-core-do-not-import/stream/utils/createDeferred.mjs +1 -29
  161. package/dist/unstable-core-do-not-import/stream/utils/disposable.d.ts +17 -0
  162. package/dist/unstable-core-do-not-import/stream/utils/disposable.d.ts.map +1 -0
  163. package/dist/unstable-core-do-not-import/stream/utils/disposable.js +44 -0
  164. package/dist/unstable-core-do-not-import/stream/utils/disposable.mjs +41 -0
  165. package/dist/unstable-core-do-not-import/stream/utils/readableStreamFrom.d.ts +8 -0
  166. package/dist/unstable-core-do-not-import/stream/utils/readableStreamFrom.d.ts.map +1 -0
  167. package/dist/unstable-core-do-not-import/stream/utils/readableStreamFrom.js +25 -0
  168. package/dist/unstable-core-do-not-import/stream/utils/readableStreamFrom.mjs +23 -0
  169. package/dist/unstable-core-do-not-import/stream/utils/timerResource.d.ts +5 -0
  170. package/dist/unstable-core-do-not-import/stream/utils/timerResource.d.ts.map +1 -0
  171. package/dist/unstable-core-do-not-import/stream/utils/timerResource.js +26 -0
  172. package/dist/unstable-core-do-not-import/stream/utils/timerResource.mjs +23 -0
  173. package/dist/unstable-core-do-not-import/stream/utils/withPing.d.ts +7 -0
  174. package/dist/unstable-core-do-not-import/stream/utils/withPing.d.ts.map +1 -0
  175. package/dist/unstable-core-do-not-import/stream/utils/withPing.js +130 -0
  176. package/dist/unstable-core-do-not-import/stream/utils/withPing.mjs +127 -0
  177. package/dist/unstable-core-do-not-import/transformer.d.ts +2 -5
  178. package/dist/unstable-core-do-not-import/transformer.d.ts.map +1 -1
  179. package/dist/unstable-core-do-not-import/transformer.js +1 -1
  180. package/dist/unstable-core-do-not-import/transformer.mjs +1 -1
  181. package/dist/unstable-core-do-not-import/types.d.ts +26 -3
  182. package/dist/unstable-core-do-not-import/types.d.ts.map +1 -1
  183. package/dist/unstable-core-do-not-import/utils.d.ts +15 -0
  184. package/dist/unstable-core-do-not-import/utils.d.ts.map +1 -1
  185. package/dist/unstable-core-do-not-import/utils.js +49 -1
  186. package/dist/unstable-core-do-not-import/utils.mjs +45 -2
  187. package/dist/unstable-core-do-not-import.d.ts +3 -2
  188. package/dist/unstable-core-do-not-import.d.ts.map +1 -1
  189. package/dist/unstable-core-do-not-import.js +14 -7
  190. package/dist/unstable-core-do-not-import.mjs +5 -6
  191. package/dist/vendor/unpromise/index.d.ts +3 -0
  192. package/dist/vendor/unpromise/index.d.ts.map +1 -0
  193. package/dist/vendor/unpromise/types.d.ts +28 -0
  194. package/dist/vendor/unpromise/types.d.ts.map +1 -0
  195. package/dist/vendor/unpromise/unpromise.d.ts +121 -0
  196. package/dist/vendor/unpromise/unpromise.d.ts.map +1 -0
  197. package/dist/vendor/unpromise/unpromise.js +292 -0
  198. package/dist/vendor/unpromise/unpromise.mjs +289 -0
  199. package/package.json +20 -17
  200. package/src/@trpc/server/http.ts +0 -2
  201. package/src/@trpc/server/index.ts +2 -0
  202. package/src/adapters/aws-lambda/getPlanner.ts +2 -2
  203. package/src/adapters/aws-lambda/index.ts +5 -6
  204. package/src/adapters/express.ts +21 -9
  205. package/src/adapters/fastify/fastifyRequestHandler.ts +1 -1
  206. package/src/adapters/fastify/fastifyTRPCPlugin.ts +16 -5
  207. package/src/adapters/fetch/fetchRequestHandler.ts +2 -2
  208. package/src/adapters/next-app-dir/nextAppDirCaller.ts +3 -0
  209. package/src/adapters/next-app-dir/rethrowNextErrors.ts +3 -2
  210. package/src/adapters/next.ts +29 -34
  211. package/src/adapters/node-http/incomingMessageToRequest.ts +110 -40
  212. package/src/adapters/node-http/nodeHTTPRequestHandler.ts +85 -63
  213. package/src/adapters/node-http/writeResponse.ts +94 -0
  214. package/src/adapters/standalone.ts +35 -18
  215. package/src/adapters/ws.ts +111 -57
  216. package/src/observable/behaviorSubject.ts +55 -0
  217. package/src/observable/index.ts +22 -3
  218. package/src/observable/observable.ts +32 -14
  219. package/src/observable/operators.ts +47 -0
  220. package/src/unstable-core-do-not-import/clientish/inference.ts +4 -4
  221. package/src/unstable-core-do-not-import/clientish/inferrable.ts +6 -6
  222. package/src/unstable-core-do-not-import/clientish/serialize.ts +5 -5
  223. package/src/unstable-core-do-not-import/http/getHTTPStatusCode.ts +37 -4
  224. package/src/unstable-core-do-not-import/http/isAbortError.ts +7 -0
  225. package/src/unstable-core-do-not-import/http/resolveResponse.ts +185 -90
  226. package/src/unstable-core-do-not-import/http/types.ts +1 -1
  227. package/src/unstable-core-do-not-import/initTRPC.ts +14 -20
  228. package/src/unstable-core-do-not-import/middleware.ts +5 -4
  229. package/src/unstable-core-do-not-import/parser.ts +17 -6
  230. package/src/unstable-core-do-not-import/procedure.ts +17 -2
  231. package/src/unstable-core-do-not-import/procedureBuilder.ts +137 -122
  232. package/src/unstable-core-do-not-import/rootConfig.ts +20 -20
  233. package/src/unstable-core-do-not-import/router.ts +15 -16
  234. package/src/unstable-core-do-not-import/rpc/codes.ts +2 -10
  235. package/src/unstable-core-do-not-import/rpc/envelopes.ts +7 -12
  236. package/src/unstable-core-do-not-import/stream/jsonl.ts +287 -296
  237. package/src/unstable-core-do-not-import/stream/sse.ts +338 -171
  238. package/src/unstable-core-do-not-import/stream/sse.types.ts +44 -0
  239. package/src/unstable-core-do-not-import/stream/tracked.ts +5 -11
  240. package/src/unstable-core-do-not-import/stream/utils/asyncIterable.ts +89 -0
  241. package/src/unstable-core-do-not-import/stream/utils/createDeferred.ts +0 -36
  242. package/src/unstable-core-do-not-import/stream/utils/disposable.ts +52 -0
  243. package/src/unstable-core-do-not-import/stream/utils/readableStreamFrom.ts +28 -0
  244. package/src/unstable-core-do-not-import/stream/utils/timerResource.ts +29 -0
  245. package/src/unstable-core-do-not-import/stream/utils/withPing.ts +47 -0
  246. package/src/unstable-core-do-not-import/transformer.ts +1 -1
  247. package/src/unstable-core-do-not-import/types.ts +35 -10
  248. package/src/unstable-core-do-not-import/utils.ts +56 -1
  249. package/src/unstable-core-do-not-import.ts +3 -2
  250. package/src/vendor/unpromise/ATTRIBUTION.txt +1 -0
  251. package/src/vendor/unpromise/LICENSE +20 -0
  252. package/src/vendor/unpromise/index.ts +7 -0
  253. package/src/vendor/unpromise/types.ts +55 -0
  254. package/src/vendor/unpromise/unpromise.ts +380 -0
  255. package/dist/unstable-core-do-not-import/http/batchStreamFormatter.d.ts +0 -24
  256. package/dist/unstable-core-do-not-import/http/batchStreamFormatter.d.ts.map +0 -1
  257. package/dist/unstable-core-do-not-import/http/batchStreamFormatter.js +0 -32
  258. package/dist/unstable-core-do-not-import/http/batchStreamFormatter.mjs +0 -30
  259. package/dist/unstable-core-do-not-import/http/toURL.d.ts +0 -2
  260. package/dist/unstable-core-do-not-import/http/toURL.d.ts.map +0 -1
  261. package/dist/unstable-core-do-not-import/http/toURL.js +0 -8
  262. package/dist/unstable-core-do-not-import/http/toURL.mjs +0 -6
  263. package/dist/unstable-core-do-not-import/stream/utils/createReadableStream.d.ts +0 -10
  264. package/dist/unstable-core-do-not-import/stream/utils/createReadableStream.d.ts.map +0 -1
  265. package/dist/unstable-core-do-not-import/stream/utils/createReadableStream.js +0 -31
  266. package/dist/unstable-core-do-not-import/stream/utils/createReadableStream.mjs +0 -29
  267. package/dist/unstable-core-do-not-import/stream/utils/createServer.d.ts +0 -7
  268. package/dist/unstable-core-do-not-import/stream/utils/createServer.d.ts.map +0 -1
  269. package/dist/unstable-core-do-not-import/types.js +0 -11
  270. package/dist/unstable-core-do-not-import/types.mjs +0 -9
  271. package/src/unstable-core-do-not-import/http/batchStreamFormatter.ts +0 -29
  272. package/src/unstable-core-do-not-import/http/toURL.ts +0 -7
  273. package/src/unstable-core-do-not-import/stream/utils/createReadableStream.ts +0 -31
  274. package/src/unstable-core-do-not-import/stream/utils/createServer.ts +0 -44
@@ -1,10 +1,19 @@
1
+ import { Unpromise } from '../../vendor/unpromise';
1
2
  import { getTRPCErrorFromUnknown } from '../error/TRPCError';
2
- import { run } from '../utils';
3
- import type { ConsumerOnError } from './jsonl';
3
+ import { isAbortError } from '../http/isAbortError';
4
+ import type { MaybePromise } from '../types';
5
+ import { identity, run } from '../utils';
6
+ import type { EventSourceLike } from './sse.types';
4
7
  import type { inferTrackedOutput } from './tracked';
5
8
  import { isTrackedEnvelope } from './tracked';
6
- import { createTimeoutPromise } from './utils/createDeferred';
7
- import { createReadableStream } from './utils/createReadableStream';
9
+ import { takeWithGrace, withMaxDuration } from './utils/asyncIterable';
10
+ import { makeAsyncResource } from './utils/disposable';
11
+ import { readableStreamFrom } from './utils/readableStreamFrom';
12
+ import {
13
+ disposablePromiseTimerResult,
14
+ timerResource,
15
+ } from './utils/timerResource';
16
+ import { PING_SYM, withPing } from './utils/withPing';
8
17
 
9
18
  type Serialize = (value: any) => any;
10
19
  type Deserialize = (value: any) => any;
@@ -12,7 +21,7 @@ type Deserialize = (value: any) => any;
12
21
  /**
13
22
  * @internal
14
23
  */
15
- export interface PingOptions {
24
+ export interface SSEPingOptions {
16
25
  /**
17
26
  * Enable ping comments sent from the server
18
27
  * @default false
@@ -25,14 +34,22 @@ export interface PingOptions {
25
34
  intervalMs?: number;
26
35
  }
27
36
 
28
- export interface SSEStreamProducerOptions {
37
+ export interface SSEClientOptions {
38
+ /**
39
+ * Timeout and reconnect after inactivity in milliseconds
40
+ * @default undefined
41
+ */
42
+ reconnectAfterInactivityMs?: number;
43
+ }
44
+
45
+ export interface SSEStreamProducerOptions<TValue = unknown> {
29
46
  serialize?: Serialize;
30
- data: AsyncIterable<unknown>;
47
+ data: AsyncIterable<TValue>;
48
+
31
49
  maxDepth?: number;
32
- ping?: PingOptions;
50
+ ping?: SSEPingOptions;
33
51
  /**
34
52
  * Maximum duration in milliseconds for the request before ending the stream
35
- * Only useful for serverless runtimes
36
53
  * @default undefined
37
54
  */
38
55
  maxDurationMs?: number;
@@ -43,120 +60,127 @@ export interface SSEStreamProducerOptions {
43
60
  */
44
61
  emitAndEndImmediately?: boolean;
45
62
  formatError?: (opts: { error: unknown }) => unknown;
63
+ /**
64
+ * Client-specific options - these will be sent to the client as part of the first message
65
+ * @default {}
66
+ */
67
+ client?: SSEClientOptions;
46
68
  }
47
69
 
70
+ const PING_EVENT = 'ping';
48
71
  const SERIALIZED_ERROR_EVENT = 'serialized-error';
72
+ const CONNECTED_EVENT = 'connected';
49
73
 
50
- type SSEvent = Partial<{
51
- id: string;
74
+ interface SSEvent {
75
+ id?: string;
52
76
  data: unknown;
53
- comment: string;
54
- event: string;
55
- }>;
77
+ comment?: string;
78
+ event?: string;
79
+ }
56
80
  /**
57
81
  *
58
82
  * @see https://html.spec.whatwg.org/multipage/server-sent-events.html
59
83
  */
60
- export function sseStreamProducer(opts: SSEStreamProducerOptions) {
61
- const stream = createReadableStream<SSEvent>();
62
- stream.controller.enqueue({
63
- comment: 'connected',
64
- });
84
+ export function sseStreamProducer<TValue = unknown>(
85
+ opts: SSEStreamProducerOptions<TValue>,
86
+ ) {
87
+ const { serialize = identity } = opts;
65
88
 
66
- const { serialize = (v) => v } = opts;
67
-
68
- const ping: Required<PingOptions> = {
89
+ const ping: Required<SSEPingOptions> = {
69
90
  enabled: opts.ping?.enabled ?? false,
70
91
  intervalMs: opts.ping?.intervalMs ?? 1000,
71
92
  };
93
+ const client: SSEClientOptions = opts.client ?? {};
72
94
 
73
- run(async () => {
74
- const iterator = opts.data[Symbol.asyncIterator]();
75
-
76
- const closedPromise = stream.cancelledPromise.then(() => 'closed' as const);
77
- const maxDurationPromise = createTimeoutPromise(
78
- opts.maxDurationMs ?? Infinity,
79
- 'maxDuration' as const,
95
+ if (
96
+ ping.enabled &&
97
+ client.reconnectAfterInactivityMs &&
98
+ ping.intervalMs > client.reconnectAfterInactivityMs
99
+ ) {
100
+ throw new Error(
101
+ `Ping interval must be less than client reconnect interval to prevent unnecessary reconnection - ping.intervalMs: ${ping.intervalMs} client.reconnectAfterInactivityMs: ${client.reconnectAfterInactivityMs}`,
80
102
  );
103
+ }
81
104
 
82
- let nextPromise = iterator.next();
105
+ async function* generator(): AsyncIterable<SSEvent, void> {
106
+ yield {
107
+ event: CONNECTED_EVENT,
108
+ data: JSON.stringify(client),
109
+ };
83
110
 
84
- while (true) {
85
- const pingPromise = createTimeoutPromise(
86
- ping.enabled ? ping.intervalMs : Infinity,
87
- 'ping' as const,
88
- );
89
- const next = await Promise.race([
90
- nextPromise.catch(getTRPCErrorFromUnknown),
91
- pingPromise.promise,
92
- closedPromise,
93
- maxDurationPromise.promise,
94
- ]);
95
-
96
- pingPromise.clear();
97
- if (next === 'closed') {
98
- break;
99
- }
100
- if (next === 'maxDuration') {
101
- break;
102
- }
111
+ type TIteratorValue = Awaited<TValue> | typeof PING_SYM;
103
112
 
104
- if (next === 'ping') {
105
- stream.controller.enqueue({
106
- comment: 'ping',
107
- });
108
- continue;
109
- }
113
+ let iterable: AsyncIterable<TValue | typeof PING_SYM> = opts.data;
110
114
 
111
- if (next instanceof Error) {
112
- const data = opts.formatError
113
- ? opts.formatError({ error: next })
114
- : null;
115
- stream.controller.enqueue({
116
- event: SERIALIZED_ERROR_EVENT,
117
- data: JSON.stringify(serialize(data)),
118
- });
119
- break;
120
- }
121
- if (next.done) {
122
- break;
123
- }
115
+ if (opts.emitAndEndImmediately) {
116
+ iterable = takeWithGrace(iterable, {
117
+ count: 1,
118
+ gracePeriodMs: 1,
119
+ });
120
+ }
124
121
 
125
- const value = next.value;
122
+ if (
123
+ opts.maxDurationMs &&
124
+ opts.maxDurationMs > 0 &&
125
+ opts.maxDurationMs !== Infinity
126
+ ) {
127
+ iterable = withMaxDuration(iterable, {
128
+ maxDurationMs: opts.maxDurationMs,
129
+ });
130
+ }
126
131
 
127
- const chunk: SSEvent = isTrackedEnvelope(value)
128
- ? {
129
- id: value[0],
130
- data: value[1],
131
- }
132
- : {
133
- data: value,
134
- };
135
- if ('data' in chunk) {
136
- chunk.data = JSON.stringify(serialize(chunk.data));
137
- }
132
+ if (ping.enabled && ping.intervalMs !== Infinity && ping.intervalMs > 0) {
133
+ iterable = withPing(iterable, ping.intervalMs);
134
+ }
138
135
 
139
- stream.controller.enqueue(chunk);
136
+ // We need those declarations outside the loop for garbage collection reasons. If they were
137
+ // declared inside, they would not be freed until the next value is present.
138
+ let value: null | TIteratorValue;
139
+ let chunk: null | SSEvent;
140
140
 
141
- if (opts.emitAndEndImmediately) {
142
- // end the stream in the next tick so that we can send a few more events from the queue
143
- setTimeout(maxDurationPromise.resolve, 1);
141
+ for await (value of iterable) {
142
+ if (value === PING_SYM) {
143
+ yield { event: PING_EVENT, data: '' };
144
+ continue;
144
145
  }
145
146
 
146
- nextPromise = iterator.next();
147
+ chunk = isTrackedEnvelope(value)
148
+ ? { id: value[0], data: value[1] }
149
+ : { data: value };
150
+
151
+ chunk.data = JSON.stringify(serialize(chunk.data));
152
+
153
+ yield chunk;
154
+
155
+ // free up references for garbage collection
156
+ value = null;
157
+ chunk = null;
147
158
  }
148
- maxDurationPromise.clear();
149
- await iterator.return?.();
159
+ }
160
+
161
+ async function* generatorWithErrorHandling(): AsyncIterable<SSEvent, void> {
150
162
  try {
151
- stream.controller.close();
152
- } catch {}
153
- }).catch((error) => {
154
- return stream.controller.error(error);
155
- });
163
+ yield* generator();
164
+ } catch (cause) {
165
+ if (isAbortError(cause)) {
166
+ // ignore abort errors, send any other errors
167
+ return;
168
+ }
169
+ // `err` must be caused by `opts.data`, `JSON.stringify` or `serialize`.
170
+ // So, a user error in any case.
171
+ const error = getTRPCErrorFromUnknown(cause);
172
+ const data = opts.formatError?.({ error }) ?? null;
173
+ yield {
174
+ event: SERIALIZED_ERROR_EVENT,
175
+ data: JSON.stringify(serialize(data)),
176
+ };
177
+ }
178
+ }
179
+ const stream = readableStreamFrom(generatorWithErrorHandling());
156
180
 
157
- return stream.readable.pipeThrough(
158
- new TransformStream<SSEvent, string>({
159
- transform(chunk, controller) {
181
+ return stream.pipeThrough(
182
+ new TransformStream({
183
+ transform(chunk, controller: TransformStreamDefaultController<string>) {
160
184
  if ('event' in chunk) {
161
185
  controller.enqueue(`event: ${chunk.event}\n`);
162
186
  }
@@ -175,100 +199,243 @@ export function sseStreamProducer(opts: SSEStreamProducerOptions) {
175
199
  );
176
200
  }
177
201
 
178
- type ConsumerStreamResult<TData> =
179
- | {
180
- ok: true;
181
- data: inferTrackedOutput<TData>;
182
- }
183
- | {
184
- ok: false;
185
- error: unknown;
186
- };
202
+ interface ConsumerStreamResultBase<TConfig extends ConsumerConfig> {
203
+ eventSource: InstanceType<TConfig['EventSource']> | null;
204
+ }
205
+
206
+ interface ConsumerStreamResultData<TConfig extends ConsumerConfig>
207
+ extends ConsumerStreamResultBase<TConfig> {
208
+ type: 'data';
209
+ data: inferTrackedOutput<TConfig['data']>;
210
+ }
211
+
212
+ interface ConsumerStreamResultError<TConfig extends ConsumerConfig>
213
+ extends ConsumerStreamResultBase<TConfig> {
214
+ type: 'serialized-error';
215
+ error: TConfig['error'];
216
+ }
217
+
218
+ interface ConsumerStreamResultConnecting<TConfig extends ConsumerConfig>
219
+ extends ConsumerStreamResultBase<TConfig> {
220
+ type: 'connecting';
221
+ event: EventSourceLike.EventOf<TConfig['EventSource']> | null;
222
+ }
223
+ interface ConsumerStreamResultTimeout<TConfig extends ConsumerConfig>
224
+ extends ConsumerStreamResultBase<TConfig> {
225
+ type: 'timeout';
226
+ ms: number;
227
+ }
228
+ interface ConsumerStreamResultPing<TConfig extends ConsumerConfig>
229
+ extends ConsumerStreamResultBase<TConfig> {
230
+ type: 'ping';
231
+ }
232
+
233
+ interface ConsumerStreamResultConnected<TConfig extends ConsumerConfig>
234
+ extends ConsumerStreamResultBase<TConfig> {
235
+ type: 'connected';
236
+ options: SSEClientOptions;
237
+ }
238
+
239
+ type ConsumerStreamResult<TConfig extends ConsumerConfig> =
240
+ | ConsumerStreamResultData<TConfig>
241
+ | ConsumerStreamResultError<TConfig>
242
+ | ConsumerStreamResultConnecting<TConfig>
243
+ | ConsumerStreamResultTimeout<TConfig>
244
+ | ConsumerStreamResultPing<TConfig>
245
+ | ConsumerStreamResultConnected<TConfig>;
246
+
247
+ export interface SSEStreamConsumerOptions<TConfig extends ConsumerConfig> {
248
+ url: () => MaybePromise<string>;
249
+ init: () =>
250
+ | MaybePromise<EventSourceLike.InitDictOf<TConfig['EventSource']>>
251
+ | undefined;
252
+ signal: AbortSignal;
253
+ deserialize?: Deserialize;
254
+ EventSource: TConfig['EventSource'];
255
+ }
256
+
257
+ interface ConsumerConfig {
258
+ data: unknown;
259
+ error: unknown;
260
+ EventSource: EventSourceLike.AnyConstructor;
261
+ }
262
+
263
+ async function withTimeout<T>(opts: {
264
+ promise: Promise<T>;
265
+ timeoutMs: number;
266
+ onTimeout: () => Promise<NoInfer<T>>;
267
+ }): Promise<T> {
268
+ using timeoutPromise = timerResource(opts.timeoutMs);
269
+ const res = await Unpromise.race([opts.promise, timeoutPromise.start()]);
270
+
271
+ if (res === disposablePromiseTimerResult) {
272
+ return await opts.onTimeout();
273
+ }
274
+ return res;
275
+ }
187
276
 
188
277
  /**
189
278
  * @see https://html.spec.whatwg.org/multipage/server-sent-events.html
190
279
  */
191
- export function sseStreamConsumer<TData>(opts: {
192
- from: EventSource;
193
- onError?: ConsumerOnError;
194
- deserialize?: Deserialize;
195
- }): AsyncIterable<ConsumerStreamResult<TData>> {
280
+ export function sseStreamConsumer<TConfig extends ConsumerConfig>(
281
+ opts: SSEStreamConsumerOptions<TConfig>,
282
+ ): AsyncIterable<ConsumerStreamResult<TConfig>> {
196
283
  const { deserialize = (v) => v } = opts;
197
- const eventSource = opts.from;
198
284
 
199
- const stream = createReadableStream<MessageEvent>();
285
+ let clientOptions: SSEClientOptions = {};
286
+
287
+ const signal = opts.signal;
288
+
289
+ let _es: InstanceType<TConfig['EventSource']> | null = null;
290
+
291
+ const createStream = () =>
292
+ new ReadableStream<ConsumerStreamResult<TConfig>>({
293
+ async start(controller) {
294
+ const [url, init] = await Promise.all([opts.url(), opts.init()]);
295
+ const eventSource = (_es = new opts.EventSource(
296
+ url,
297
+ init,
298
+ ) as InstanceType<TConfig['EventSource']>);
200
299
 
201
- const transform = new TransformStream<
202
- MessageEvent,
203
- ConsumerStreamResult<TData>
204
- >({
205
- async transform(chunk, controller) {
206
- const data = deserialize(JSON.parse(chunk.data));
207
- if (chunk.type === SERIALIZED_ERROR_EVENT) {
208
300
  controller.enqueue({
209
- ok: false,
210
- error: data,
301
+ type: 'connecting',
302
+ eventSource: _es,
303
+ event: null,
211
304
  });
212
- return;
213
- }
214
- // console.debug('transforming', chunk.type, chunk.data);
215
- const def: SSEvent = {
216
- data,
217
- };
218
305
 
219
- if (chunk.lastEventId) {
220
- def.id = chunk.lastEventId;
221
- }
306
+ eventSource.addEventListener(CONNECTED_EVENT, (_msg) => {
307
+ const msg = _msg as EventSourceLike.MessageEvent;
222
308
 
223
- controller.enqueue({
224
- ok: true,
225
- data: def as inferTrackedOutput<TData>,
226
- });
227
- },
228
- });
309
+ const options: SSEClientOptions = JSON.parse(msg.data);
229
310
 
230
- eventSource.addEventListener('message', (msg) => {
231
- stream.controller.enqueue(msg);
232
- });
233
- eventSource.addEventListener(SERIALIZED_ERROR_EVENT, (msg) => {
234
- stream.controller.enqueue(msg);
235
- });
236
- eventSource.addEventListener('error', (cause) => {
237
- if (eventSource.readyState === EventSource.CLOSED) {
238
- stream.controller.error(cause);
239
- }
240
- });
311
+ clientOptions = options;
312
+ controller.enqueue({
313
+ type: 'connected',
314
+ options,
315
+ eventSource,
316
+ });
317
+ });
241
318
 
242
- const readable = stream.readable.pipeThrough(transform);
243
- return {
244
- [Symbol.asyncIterator]() {
245
- const reader = readable.getReader();
246
-
247
- const iterator: AsyncIterator<ConsumerStreamResult<TData>> = {
248
- async next() {
249
- const value = await reader.read();
250
- if (value.done) {
251
- return {
252
- value: undefined,
253
- done: true,
254
- };
319
+ eventSource.addEventListener(SERIALIZED_ERROR_EVENT, (_msg) => {
320
+ const msg = _msg as EventSourceLike.MessageEvent;
321
+
322
+ controller.enqueue({
323
+ type: 'serialized-error',
324
+ error: deserialize(JSON.parse(msg.data)),
325
+ eventSource,
326
+ });
327
+ });
328
+ eventSource.addEventListener(PING_EVENT, () => {
329
+ controller.enqueue({
330
+ type: 'ping',
331
+ eventSource,
332
+ });
333
+ });
334
+ eventSource.addEventListener('error', (event) => {
335
+ if (eventSource.readyState === EventSource.CLOSED) {
336
+ controller.error(event);
337
+ } else {
338
+ controller.enqueue({
339
+ type: 'connecting',
340
+ eventSource,
341
+ event,
342
+ });
255
343
  }
256
- return {
257
- value: value.value,
258
- done: false,
344
+ });
345
+ eventSource.addEventListener('message', (_msg) => {
346
+ const msg = _msg as EventSourceLike.MessageEvent;
347
+
348
+ const chunk = deserialize(JSON.parse(msg.data));
349
+
350
+ const def: SSEvent = {
351
+ data: chunk,
259
352
  };
353
+ if (msg.lastEventId) {
354
+ def.id = msg.lastEventId;
355
+ }
356
+ controller.enqueue({
357
+ type: 'data',
358
+ data: def as inferTrackedOutput<TConfig['data']>,
359
+ eventSource,
360
+ });
361
+ });
362
+
363
+ const onAbort = () => {
364
+ controller.close();
365
+ eventSource.close();
366
+ };
367
+ if (signal.aborted) {
368
+ onAbort();
369
+ } else {
370
+ signal.addEventListener('abort', onAbort);
371
+ }
372
+ },
373
+ cancel() {
374
+ _es?.close();
375
+ },
376
+ });
377
+
378
+ const getStreamResource = () => {
379
+ let stream = createStream();
380
+ let reader = stream.getReader();
381
+
382
+ async function dispose() {
383
+ await reader.cancel();
384
+ _es = null;
385
+ }
386
+
387
+ return makeAsyncResource(
388
+ {
389
+ read() {
390
+ return reader.read();
260
391
  },
261
- async return() {
262
- reader.releaseLock();
263
- return {
264
- value: undefined,
265
- done: true,
266
- };
392
+ async recreate() {
393
+ await dispose();
394
+
395
+ stream = createStream();
396
+ reader = stream.getReader();
267
397
  },
268
- };
269
- return iterator;
270
- },
398
+ },
399
+ dispose,
400
+ );
271
401
  };
402
+
403
+ return run(async function* () {
404
+ await using stream = getStreamResource();
405
+
406
+ while (true) {
407
+ let promise = stream.read();
408
+
409
+ const timeoutMs = clientOptions.reconnectAfterInactivityMs;
410
+ if (timeoutMs) {
411
+ promise = withTimeout({
412
+ promise,
413
+ timeoutMs,
414
+ onTimeout: async () => {
415
+ const res: Awaited<typeof promise> = {
416
+ value: {
417
+ type: 'timeout',
418
+ ms: timeoutMs,
419
+ eventSource: _es,
420
+ },
421
+ done: false,
422
+ };
423
+ // Close and release old reader
424
+ await stream.recreate();
425
+
426
+ return res;
427
+ },
428
+ });
429
+ }
430
+
431
+ const result = await promise;
432
+
433
+ if (result.done) {
434
+ return result.value;
435
+ }
436
+ yield result.value;
437
+ }
438
+ });
272
439
  }
273
440
 
274
441
  export const sseHeaders = {
@@ -0,0 +1,44 @@
1
+ /* eslint-disable @typescript-eslint/no-namespace */
2
+
3
+ /**
4
+ * @internal
5
+ */
6
+ export namespace EventSourceLike {
7
+ export interface InitDict {
8
+ withCredentials?: boolean;
9
+ }
10
+
11
+ export interface MessageEvent extends Event {
12
+ data: any;
13
+ lastEventId?: string;
14
+ }
15
+ export interface Event {}
16
+
17
+ type EventSourceListenerLike = (event: Event) => void;
18
+
19
+ export type AnyConstructorLike<TInit extends InitDict> = new (
20
+ url: string,
21
+ eventSourceInitDict?: TInit,
22
+ ) => Instance;
23
+
24
+ export interface Instance {
25
+ readonly CLOSED: number;
26
+ readonly CONNECTING: number;
27
+ readonly OPEN: number;
28
+
29
+ addEventListener(type: string, listener: EventSourceListenerLike): void;
30
+ removeEventListener(type: string, listener: EventSourceListenerLike): void;
31
+ close: () => void;
32
+
33
+ readyState: number;
34
+ }
35
+
36
+ export type AnyConstructor = AnyConstructorLike<any>;
37
+
38
+ export type ListenerOf<T extends AnyConstructor> = Parameters<
39
+ InstanceType<T>['addEventListener']
40
+ >[1];
41
+ export type EventOf<T extends AnyConstructor> = Parameters<ListenerOf<T>>[0];
42
+ export type InitDictOf<T extends AnyConstructor> =
43
+ ConstructorParameters<T>[1];
44
+ }
@@ -1,11 +1,11 @@
1
- const trackedSymbol = Symbol('TrackedEnvelope');
1
+ const trackedSymbol = Symbol();
2
2
 
3
3
  type TrackedId = string & {
4
4
  __brand: 'TrackedId';
5
5
  };
6
6
  export type TrackedEnvelope<TData> = [TrackedId, TData, typeof trackedSymbol];
7
7
 
8
- type Tracked<TData> = {
8
+ type TrackedData<TData> = {
9
9
  /**
10
10
  * The id of the message to keep track of in case the connection gets lost
11
11
  */
@@ -19,10 +19,7 @@ type Tracked<TData> = {
19
19
  * Produce a typed server-sent event message
20
20
  * @deprecated use `tracked(id, data)` instead
21
21
  */
22
- export function sse<TData>(event: {
23
- id: string;
24
- data: TData;
25
- }): TrackedEnvelope<TData> {
22
+ export function sse<TData>(event: { id: string; data: TData }) {
26
23
  return tracked(event.id, event.data);
27
24
  }
28
25
 
@@ -48,8 +45,5 @@ export function tracked<TData>(
48
45
  return [id as TrackedId, data, trackedSymbol];
49
46
  }
50
47
 
51
- export type inferTrackedOutput<TData> = TData extends TrackedEnvelope<
52
- infer $Data
53
- >
54
- ? Tracked<$Data>
55
- : TData;
48
+ export type inferTrackedOutput<TData> =
49
+ TData extends TrackedEnvelope<infer $Data> ? TrackedData<$Data> : TData;