@trpc/server 11.0.0-rc.599 → 11.0.0-rc.601

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 (38) hide show
  1. package/dist/adapters/ws.d.ts.map +1 -1
  2. package/dist/adapters/ws.js +10 -2
  3. package/dist/adapters/ws.mjs +10 -2
  4. package/dist/bundle-analysis.json +116 -103
  5. package/dist/unstable-core-do-not-import/procedureBuilder.d.ts +1 -1
  6. package/dist/unstable-core-do-not-import/procedureBuilder.d.ts.map +1 -1
  7. package/dist/unstable-core-do-not-import/stream/jsonl.d.ts.map +1 -1
  8. package/dist/unstable-core-do-not-import/stream/jsonl.js +3 -2
  9. package/dist/unstable-core-do-not-import/stream/jsonl.mjs +3 -2
  10. package/dist/unstable-core-do-not-import/stream/sse.d.ts.map +1 -1
  11. package/dist/unstable-core-do-not-import/stream/sse.js +9 -2
  12. package/dist/unstable-core-do-not-import/stream/sse.mjs +9 -2
  13. package/dist/unstable-core-do-not-import/stream/utils/asyncIterable.d.ts.map +1 -1
  14. package/dist/unstable-core-do-not-import/stream/utils/asyncIterable.js +11 -2
  15. package/dist/unstable-core-do-not-import/stream/utils/asyncIterable.mjs +11 -2
  16. package/dist/unstable-core-do-not-import/stream/utils/withPing.d.ts.map +1 -1
  17. package/dist/unstable-core-do-not-import/stream/utils/withPing.js +6 -2
  18. package/dist/unstable-core-do-not-import/stream/utils/withPing.mjs +6 -2
  19. package/dist/vendor/unpromise/index.d.ts +3 -0
  20. package/dist/vendor/unpromise/index.d.ts.map +1 -0
  21. package/dist/vendor/unpromise/types.d.ts +28 -0
  22. package/dist/vendor/unpromise/types.d.ts.map +1 -0
  23. package/dist/vendor/unpromise/unpromise.d.ts +121 -0
  24. package/dist/vendor/unpromise/unpromise.d.ts.map +1 -0
  25. package/dist/vendor/unpromise/unpromise.js +274 -0
  26. package/dist/vendor/unpromise/unpromise.mjs +271 -0
  27. package/package.json +4 -4
  28. package/src/adapters/ws.ts +17 -2
  29. package/src/unstable-core-do-not-import/procedureBuilder.ts +1 -1
  30. package/src/unstable-core-do-not-import/stream/jsonl.ts +3 -2
  31. package/src/unstable-core-do-not-import/stream/sse.ts +13 -2
  32. package/src/unstable-core-do-not-import/stream/utils/asyncIterable.ts +11 -2
  33. package/src/unstable-core-do-not-import/stream/utils/withPing.ts +6 -2
  34. package/src/vendor/unpromise/ATTRIBUTION.txt +1 -0
  35. package/src/vendor/unpromise/LICENSE +20 -0
  36. package/src/vendor/unpromise/index.ts +7 -0
  37. package/src/vendor/unpromise/types.ts +55 -0
  38. package/src/vendor/unpromise/unpromise.ts +378 -0
@@ -0,0 +1,274 @@
1
+ 'use strict';
2
+
3
+ /* eslint-disable @typescript-eslint/unbound-method */ /* eslint-disable @typescript-eslint/return-await */ /* eslint-disable @typescript-eslint/promise-function-async */ /** Memory safe (weakmapped) cache of the ProxyPromise for each Promise,
4
+ * which is retained for the lifetime of the original Promise.
5
+ */ const subscribableCache = new WeakMap();
6
+ /** A NOOP function allowing a consistent interface for settled
7
+ * SubscribedPromises (settled promises are not subscribed - they resolve
8
+ * immediately). */ const NOOP = ()=>{};
9
+ let _toStringTag = Symbol.toStringTag;
10
+ /**
11
+ * Every `Promise<T>` can be shadowed by a single `ProxyPromise<T>`. It is
12
+ * created once, cached and reused throughout the lifetime of the Promise. Get a
13
+ * Promise's ProxyPromise using `Unpromise.proxy(promise)`.
14
+ *
15
+ * The `ProxyPromise<T>` attaches handlers to the original `Promise<T>`
16
+ * `.then()` and `.catch()` just once. Promises derived from it use a
17
+ * subscription- (and unsubscription-) based mechanism that monitors these
18
+ * handlers.
19
+ *
20
+ * Every time you call `.subscribe()`, `.then()` `.catch()` or `.finally()` on a
21
+ * `ProxyPromise<T>` it returns a `SubscribedPromise<T>` having an additional
22
+ * `unsubscribe()` method. Calling `unsubscribe()` detaches reference chains
23
+ * from the original, potentially long-lived Promise, eliminating memory leaks.
24
+ *
25
+ * This approach can eliminate the memory leaks that otherwise come about from
26
+ * repeated `race()` or `any()` calls invoking `.then()` and `.catch()` multiple
27
+ * times on the same long-lived native Promise (subscriptions which can never be
28
+ * cleaned up).
29
+ *
30
+ * `Unpromise.race(promises)` is a reference implementation of `Promise.race`
31
+ * avoiding memory leaks when using long-lived unsettled Promises.
32
+ *
33
+ * `Unpromise.any(promises)` is a reference implementation of `Promise.any`
34
+ * avoiding memory leaks when using long-lived unsettled Promises.
35
+ *
36
+ * `Unpromise.resolve(promise)` returns an ephemeral `SubscribedPromise<T>` for
37
+ * any given `Promise<T>` facilitating arbitrary async/await patterns. Behind
38
+ * the scenes, `resolve` is implemented simply as
39
+ * `Unpromise.proxy(promise).subscribe()`. Don't forget to call `.unsubscribe()`
40
+ * to tidy up!
41
+ *
42
+ */ class Unpromise {
43
+ /** Create a promise that mitigates uncontrolled subscription to a long-lived
44
+ * Promise via .then() and .catch() - otherwise a source of memory leaks.
45
+ *
46
+ * The returned promise has an `unsubscribe()` method which can be called when
47
+ * the Promise is no longer being tracked by application logic, and which
48
+ * ensures that there is no reference chain from the original promise to the
49
+ * new one, and therefore no memory leak.
50
+ *
51
+ * If original promise has not yet settled, this adds a new unique promise
52
+ * that listens to then/catch events, along with an `unsubscribe()` method to
53
+ * detach it.
54
+ *
55
+ * If original promise has settled, then creates a new Promise.resolve() or
56
+ * Promise.reject() and provided unsubscribe is a noop.
57
+ *
58
+ * If you call `unsubscribe()` before the returned Promise has settled, it
59
+ * will never settle.
60
+ */ subscribe() {
61
+ // in all cases we will combine some promise with its unsubscribe function
62
+ let promise;
63
+ let unsubscribe;
64
+ const { settlement } = this;
65
+ if (settlement === null) {
66
+ // not yet settled - subscribe new promise. Expect eventual settlement
67
+ if (this.subscribers === null) {
68
+ // invariant - it is not settled, so it must have subscribers
69
+ throw new Error("Unpromise settled but still has subscribers");
70
+ }
71
+ const subscriber = withResolvers();
72
+ this.subscribers = listWithMember(this.subscribers, subscriber);
73
+ promise = subscriber.promise;
74
+ unsubscribe = ()=>{
75
+ if (this.subscribers !== null) {
76
+ this.subscribers = listWithoutMember(this.subscribers, subscriber);
77
+ }
78
+ };
79
+ } else {
80
+ // settled - don't create subscribed promise. Just resolve or reject
81
+ const { status } = settlement;
82
+ if (status === "fulfilled") {
83
+ promise = Promise.resolve(settlement.value);
84
+ } else {
85
+ promise = Promise.reject(settlement.reason);
86
+ }
87
+ unsubscribe = NOOP;
88
+ }
89
+ // extend promise signature with the extra method
90
+ return Object.assign(promise, {
91
+ unsubscribe
92
+ });
93
+ }
94
+ /** STANDARD PROMISE METHODS (but returning a SubscribedPromise) */ then(onfulfilled, onrejected) {
95
+ const subscribed = this.subscribe();
96
+ const { unsubscribe } = subscribed;
97
+ return Object.assign(subscribed.then(onfulfilled, onrejected), {
98
+ unsubscribe
99
+ });
100
+ }
101
+ catch(onrejected) {
102
+ const subscribed = this.subscribe();
103
+ const { unsubscribe } = subscribed;
104
+ return Object.assign(subscribed.catch(onrejected), {
105
+ unsubscribe
106
+ });
107
+ }
108
+ finally(onfinally) {
109
+ const subscribed = this.subscribe();
110
+ const { unsubscribe } = subscribed;
111
+ return Object.assign(subscribed.finally(onfinally), {
112
+ unsubscribe
113
+ });
114
+ }
115
+ /** Unpromise STATIC METHODS */ /** Create or Retrieve the proxy Unpromise (a re-used Unpromise for the VM lifetime
116
+ * of the provided Promise reference) */ static proxy(promise) {
117
+ const cached = Unpromise.getSubscribablePromise(promise);
118
+ return typeof cached !== "undefined" ? cached : Unpromise.createSubscribablePromise(promise);
119
+ }
120
+ /** Create and store an Unpromise keyed by an original Promise. */ static createSubscribablePromise(promise) {
121
+ const created = new Unpromise(promise);
122
+ subscribableCache.set(promise, created); // resolve promise to unpromise
123
+ subscribableCache.set(created, created); // resolve the unpromise to itself
124
+ return created;
125
+ }
126
+ /** Retrieve a previously-created Unpromise keyed by an original Promise. */ static getSubscribablePromise(promise) {
127
+ return subscribableCache.get(promise);
128
+ }
129
+ /** Promise STATIC METHODS */ /** Lookup the Unpromise for this promise, and derive a SubscribedPromise from
130
+ * it (that can be later unsubscribed to eliminate Memory leaks) */ static resolve(value) {
131
+ const promise = typeof value === "object" && value !== null && "then" in value && typeof value.then === "function" ? value : Promise.resolve(value);
132
+ return Unpromise.proxy(promise).subscribe();
133
+ }
134
+ static async any(values) {
135
+ const valuesArray = Array.isArray(values) ? values : [
136
+ ...values
137
+ ];
138
+ const subscribedPromises = valuesArray.map(Unpromise.resolve);
139
+ try {
140
+ return await Promise.any(subscribedPromises);
141
+ } finally{
142
+ subscribedPromises.forEach(({ unsubscribe })=>{
143
+ unsubscribe();
144
+ });
145
+ }
146
+ }
147
+ static async race(values) {
148
+ const valuesArray = Array.isArray(values) ? values : [
149
+ ...values
150
+ ];
151
+ const subscribedPromises = valuesArray.map(Unpromise.resolve);
152
+ try {
153
+ return await Promise.race(subscribedPromises);
154
+ } finally{
155
+ subscribedPromises.forEach(({ unsubscribe })=>{
156
+ unsubscribe();
157
+ });
158
+ }
159
+ }
160
+ /** Create a race of SubscribedPromises that will fulfil to a single winning
161
+ * Promise (in a 1-Tuple). Eliminates memory leaks from long-lived promises
162
+ * accumulating .then() and .catch() subscribers. Allows simple logic to
163
+ * consume the result, like...
164
+ * ```ts
165
+ * const [ winner ] = await Unpromise.race([ promiseA, promiseB ]);
166
+ * if(winner === promiseB){
167
+ * const result = await promiseB;
168
+ * // do the thing
169
+ * }
170
+ * ```
171
+ * */ static async raceReferences(promises) {
172
+ // map each promise to an eventual 1-tuple containing itself
173
+ const selfPromises = promises.map(resolveSelfTuple);
174
+ // now race them. They will fulfil to a readonly [P] or reject.
175
+ try {
176
+ return await Promise.race(selfPromises);
177
+ } finally{
178
+ for (const promise of selfPromises){
179
+ // unsubscribe proxy promises when the race is over to mitigate memory leaks
180
+ promise.unsubscribe();
181
+ }
182
+ }
183
+ }
184
+ constructor(arg){
185
+ /** Promises expecting eventual settlement (unless unsubscribed first). This list is deleted
186
+ * after the original promise settles - no further notifications will be issued. */ this.subscribers = [];
187
+ /** The Promise's settlement (recorded when it fulfils or rejects). This is consulted when
188
+ * calling .subscribe() .then() .catch() .finally() to see if an immediately-resolving Promise
189
+ * can be returned, and therefore subscription can be bypassed. */ this.settlement = null;
190
+ /** TOSTRING SUPPORT */ this[_toStringTag] = "Unpromise";
191
+ // handle either a Promise or a Promise executor function
192
+ if (typeof arg === "function") {
193
+ this.promise = new Promise(arg);
194
+ } else {
195
+ this.promise = arg;
196
+ }
197
+ // subscribe for eventual fulfilment and rejection
198
+ // handle PromiseLike objects (that at least have .then)
199
+ const thenReturn = this.promise.then((value)=>{
200
+ // atomically record fulfilment and detach subscriber list
201
+ const { subscribers } = this;
202
+ this.subscribers = null;
203
+ this.settlement = {
204
+ status: "fulfilled",
205
+ value
206
+ };
207
+ // notify fulfilment to subscriber list
208
+ subscribers?.forEach(({ resolve })=>{
209
+ resolve(value);
210
+ });
211
+ });
212
+ // handle Promise (that also have a .catch behaviour)
213
+ if ("catch" in thenReturn) {
214
+ thenReturn.catch((reason)=>{
215
+ // atomically record rejection and detach subscriber list
216
+ const { subscribers } = this;
217
+ this.subscribers = null;
218
+ this.settlement = {
219
+ status: "rejected",
220
+ reason
221
+ };
222
+ // notify rejection to subscriber list
223
+ subscribers?.forEach(({ reject })=>{
224
+ reject(reason);
225
+ });
226
+ });
227
+ }
228
+ }
229
+ }
230
+ /** Promises a 1-tuple containing the original promise when it resolves. Allows
231
+ * awaiting the eventual Promise ***reference*** (easy to destructure and
232
+ * exactly compare with ===). Avoids resolving to the Promise ***value*** (which
233
+ * may be ambiguous and therefore hard to identify as the winner of a race).
234
+ * You can call unsubscribe on the Promise to mitigate memory leaks.
235
+ * */ function resolveSelfTuple(promise) {
236
+ return Unpromise.proxy(promise).then(()=>[
237
+ promise
238
+ ]);
239
+ }
240
+ /** VENDORED (Future) PROMISE UTILITIES */ /** Reference implementation of https://github.com/tc39/proposal-promise-with-resolvers */ function withResolvers() {
241
+ let resolve;
242
+ let reject;
243
+ const promise = new Promise((_resolve, _reject)=>{
244
+ resolve = _resolve;
245
+ reject = _reject;
246
+ });
247
+ return {
248
+ promise,
249
+ resolve,
250
+ reject
251
+ };
252
+ }
253
+ /** IMMUTABLE LIST OPERATIONS */ function listWithMember(arr, member) {
254
+ return [
255
+ ...arr,
256
+ member
257
+ ];
258
+ }
259
+ function listWithoutIndex(arr, index) {
260
+ return [
261
+ ...arr.slice(0, index),
262
+ ...arr.slice(index + 1)
263
+ ];
264
+ }
265
+ function listWithoutMember(arr, member) {
266
+ const index = arr.indexOf(member);
267
+ if (index !== -1) {
268
+ return listWithoutIndex(arr, index);
269
+ }
270
+ return arr;
271
+ }
272
+
273
+ exports.Unpromise = Unpromise;
274
+ exports.resolveSelfTuple = resolveSelfTuple;
@@ -0,0 +1,271 @@
1
+ /* eslint-disable @typescript-eslint/unbound-method */ /* eslint-disable @typescript-eslint/return-await */ /* eslint-disable @typescript-eslint/promise-function-async */ /** Memory safe (weakmapped) cache of the ProxyPromise for each Promise,
2
+ * which is retained for the lifetime of the original Promise.
3
+ */ const subscribableCache = new WeakMap();
4
+ /** A NOOP function allowing a consistent interface for settled
5
+ * SubscribedPromises (settled promises are not subscribed - they resolve
6
+ * immediately). */ const NOOP = ()=>{};
7
+ let _toStringTag = Symbol.toStringTag;
8
+ /**
9
+ * Every `Promise<T>` can be shadowed by a single `ProxyPromise<T>`. It is
10
+ * created once, cached and reused throughout the lifetime of the Promise. Get a
11
+ * Promise's ProxyPromise using `Unpromise.proxy(promise)`.
12
+ *
13
+ * The `ProxyPromise<T>` attaches handlers to the original `Promise<T>`
14
+ * `.then()` and `.catch()` just once. Promises derived from it use a
15
+ * subscription- (and unsubscription-) based mechanism that monitors these
16
+ * handlers.
17
+ *
18
+ * Every time you call `.subscribe()`, `.then()` `.catch()` or `.finally()` on a
19
+ * `ProxyPromise<T>` it returns a `SubscribedPromise<T>` having an additional
20
+ * `unsubscribe()` method. Calling `unsubscribe()` detaches reference chains
21
+ * from the original, potentially long-lived Promise, eliminating memory leaks.
22
+ *
23
+ * This approach can eliminate the memory leaks that otherwise come about from
24
+ * repeated `race()` or `any()` calls invoking `.then()` and `.catch()` multiple
25
+ * times on the same long-lived native Promise (subscriptions which can never be
26
+ * cleaned up).
27
+ *
28
+ * `Unpromise.race(promises)` is a reference implementation of `Promise.race`
29
+ * avoiding memory leaks when using long-lived unsettled Promises.
30
+ *
31
+ * `Unpromise.any(promises)` is a reference implementation of `Promise.any`
32
+ * avoiding memory leaks when using long-lived unsettled Promises.
33
+ *
34
+ * `Unpromise.resolve(promise)` returns an ephemeral `SubscribedPromise<T>` for
35
+ * any given `Promise<T>` facilitating arbitrary async/await patterns. Behind
36
+ * the scenes, `resolve` is implemented simply as
37
+ * `Unpromise.proxy(promise).subscribe()`. Don't forget to call `.unsubscribe()`
38
+ * to tidy up!
39
+ *
40
+ */ class Unpromise {
41
+ /** Create a promise that mitigates uncontrolled subscription to a long-lived
42
+ * Promise via .then() and .catch() - otherwise a source of memory leaks.
43
+ *
44
+ * The returned promise has an `unsubscribe()` method which can be called when
45
+ * the Promise is no longer being tracked by application logic, and which
46
+ * ensures that there is no reference chain from the original promise to the
47
+ * new one, and therefore no memory leak.
48
+ *
49
+ * If original promise has not yet settled, this adds a new unique promise
50
+ * that listens to then/catch events, along with an `unsubscribe()` method to
51
+ * detach it.
52
+ *
53
+ * If original promise has settled, then creates a new Promise.resolve() or
54
+ * Promise.reject() and provided unsubscribe is a noop.
55
+ *
56
+ * If you call `unsubscribe()` before the returned Promise has settled, it
57
+ * will never settle.
58
+ */ subscribe() {
59
+ // in all cases we will combine some promise with its unsubscribe function
60
+ let promise;
61
+ let unsubscribe;
62
+ const { settlement } = this;
63
+ if (settlement === null) {
64
+ // not yet settled - subscribe new promise. Expect eventual settlement
65
+ if (this.subscribers === null) {
66
+ // invariant - it is not settled, so it must have subscribers
67
+ throw new Error("Unpromise settled but still has subscribers");
68
+ }
69
+ const subscriber = withResolvers();
70
+ this.subscribers = listWithMember(this.subscribers, subscriber);
71
+ promise = subscriber.promise;
72
+ unsubscribe = ()=>{
73
+ if (this.subscribers !== null) {
74
+ this.subscribers = listWithoutMember(this.subscribers, subscriber);
75
+ }
76
+ };
77
+ } else {
78
+ // settled - don't create subscribed promise. Just resolve or reject
79
+ const { status } = settlement;
80
+ if (status === "fulfilled") {
81
+ promise = Promise.resolve(settlement.value);
82
+ } else {
83
+ promise = Promise.reject(settlement.reason);
84
+ }
85
+ unsubscribe = NOOP;
86
+ }
87
+ // extend promise signature with the extra method
88
+ return Object.assign(promise, {
89
+ unsubscribe
90
+ });
91
+ }
92
+ /** STANDARD PROMISE METHODS (but returning a SubscribedPromise) */ then(onfulfilled, onrejected) {
93
+ const subscribed = this.subscribe();
94
+ const { unsubscribe } = subscribed;
95
+ return Object.assign(subscribed.then(onfulfilled, onrejected), {
96
+ unsubscribe
97
+ });
98
+ }
99
+ catch(onrejected) {
100
+ const subscribed = this.subscribe();
101
+ const { unsubscribe } = subscribed;
102
+ return Object.assign(subscribed.catch(onrejected), {
103
+ unsubscribe
104
+ });
105
+ }
106
+ finally(onfinally) {
107
+ const subscribed = this.subscribe();
108
+ const { unsubscribe } = subscribed;
109
+ return Object.assign(subscribed.finally(onfinally), {
110
+ unsubscribe
111
+ });
112
+ }
113
+ /** Unpromise STATIC METHODS */ /** Create or Retrieve the proxy Unpromise (a re-used Unpromise for the VM lifetime
114
+ * of the provided Promise reference) */ static proxy(promise) {
115
+ const cached = Unpromise.getSubscribablePromise(promise);
116
+ return typeof cached !== "undefined" ? cached : Unpromise.createSubscribablePromise(promise);
117
+ }
118
+ /** Create and store an Unpromise keyed by an original Promise. */ static createSubscribablePromise(promise) {
119
+ const created = new Unpromise(promise);
120
+ subscribableCache.set(promise, created); // resolve promise to unpromise
121
+ subscribableCache.set(created, created); // resolve the unpromise to itself
122
+ return created;
123
+ }
124
+ /** Retrieve a previously-created Unpromise keyed by an original Promise. */ static getSubscribablePromise(promise) {
125
+ return subscribableCache.get(promise);
126
+ }
127
+ /** Promise STATIC METHODS */ /** Lookup the Unpromise for this promise, and derive a SubscribedPromise from
128
+ * it (that can be later unsubscribed to eliminate Memory leaks) */ static resolve(value) {
129
+ const promise = typeof value === "object" && value !== null && "then" in value && typeof value.then === "function" ? value : Promise.resolve(value);
130
+ return Unpromise.proxy(promise).subscribe();
131
+ }
132
+ static async any(values) {
133
+ const valuesArray = Array.isArray(values) ? values : [
134
+ ...values
135
+ ];
136
+ const subscribedPromises = valuesArray.map(Unpromise.resolve);
137
+ try {
138
+ return await Promise.any(subscribedPromises);
139
+ } finally{
140
+ subscribedPromises.forEach(({ unsubscribe })=>{
141
+ unsubscribe();
142
+ });
143
+ }
144
+ }
145
+ static async race(values) {
146
+ const valuesArray = Array.isArray(values) ? values : [
147
+ ...values
148
+ ];
149
+ const subscribedPromises = valuesArray.map(Unpromise.resolve);
150
+ try {
151
+ return await Promise.race(subscribedPromises);
152
+ } finally{
153
+ subscribedPromises.forEach(({ unsubscribe })=>{
154
+ unsubscribe();
155
+ });
156
+ }
157
+ }
158
+ /** Create a race of SubscribedPromises that will fulfil to a single winning
159
+ * Promise (in a 1-Tuple). Eliminates memory leaks from long-lived promises
160
+ * accumulating .then() and .catch() subscribers. Allows simple logic to
161
+ * consume the result, like...
162
+ * ```ts
163
+ * const [ winner ] = await Unpromise.race([ promiseA, promiseB ]);
164
+ * if(winner === promiseB){
165
+ * const result = await promiseB;
166
+ * // do the thing
167
+ * }
168
+ * ```
169
+ * */ static async raceReferences(promises) {
170
+ // map each promise to an eventual 1-tuple containing itself
171
+ const selfPromises = promises.map(resolveSelfTuple);
172
+ // now race them. They will fulfil to a readonly [P] or reject.
173
+ try {
174
+ return await Promise.race(selfPromises);
175
+ } finally{
176
+ for (const promise of selfPromises){
177
+ // unsubscribe proxy promises when the race is over to mitigate memory leaks
178
+ promise.unsubscribe();
179
+ }
180
+ }
181
+ }
182
+ constructor(arg){
183
+ /** Promises expecting eventual settlement (unless unsubscribed first). This list is deleted
184
+ * after the original promise settles - no further notifications will be issued. */ this.subscribers = [];
185
+ /** The Promise's settlement (recorded when it fulfils or rejects). This is consulted when
186
+ * calling .subscribe() .then() .catch() .finally() to see if an immediately-resolving Promise
187
+ * can be returned, and therefore subscription can be bypassed. */ this.settlement = null;
188
+ /** TOSTRING SUPPORT */ this[_toStringTag] = "Unpromise";
189
+ // handle either a Promise or a Promise executor function
190
+ if (typeof arg === "function") {
191
+ this.promise = new Promise(arg);
192
+ } else {
193
+ this.promise = arg;
194
+ }
195
+ // subscribe for eventual fulfilment and rejection
196
+ // handle PromiseLike objects (that at least have .then)
197
+ const thenReturn = this.promise.then((value)=>{
198
+ // atomically record fulfilment and detach subscriber list
199
+ const { subscribers } = this;
200
+ this.subscribers = null;
201
+ this.settlement = {
202
+ status: "fulfilled",
203
+ value
204
+ };
205
+ // notify fulfilment to subscriber list
206
+ subscribers?.forEach(({ resolve })=>{
207
+ resolve(value);
208
+ });
209
+ });
210
+ // handle Promise (that also have a .catch behaviour)
211
+ if ("catch" in thenReturn) {
212
+ thenReturn.catch((reason)=>{
213
+ // atomically record rejection and detach subscriber list
214
+ const { subscribers } = this;
215
+ this.subscribers = null;
216
+ this.settlement = {
217
+ status: "rejected",
218
+ reason
219
+ };
220
+ // notify rejection to subscriber list
221
+ subscribers?.forEach(({ reject })=>{
222
+ reject(reason);
223
+ });
224
+ });
225
+ }
226
+ }
227
+ }
228
+ /** Promises a 1-tuple containing the original promise when it resolves. Allows
229
+ * awaiting the eventual Promise ***reference*** (easy to destructure and
230
+ * exactly compare with ===). Avoids resolving to the Promise ***value*** (which
231
+ * may be ambiguous and therefore hard to identify as the winner of a race).
232
+ * You can call unsubscribe on the Promise to mitigate memory leaks.
233
+ * */ function resolveSelfTuple(promise) {
234
+ return Unpromise.proxy(promise).then(()=>[
235
+ promise
236
+ ]);
237
+ }
238
+ /** VENDORED (Future) PROMISE UTILITIES */ /** Reference implementation of https://github.com/tc39/proposal-promise-with-resolvers */ function withResolvers() {
239
+ let resolve;
240
+ let reject;
241
+ const promise = new Promise((_resolve, _reject)=>{
242
+ resolve = _resolve;
243
+ reject = _reject;
244
+ });
245
+ return {
246
+ promise,
247
+ resolve,
248
+ reject
249
+ };
250
+ }
251
+ /** IMMUTABLE LIST OPERATIONS */ function listWithMember(arr, member) {
252
+ return [
253
+ ...arr,
254
+ member
255
+ ];
256
+ }
257
+ function listWithoutIndex(arr, index) {
258
+ return [
259
+ ...arr.slice(0, index),
260
+ ...arr.slice(index + 1)
261
+ ];
262
+ }
263
+ function listWithoutMember(arr, member) {
264
+ const index = arr.indexOf(member);
265
+ if (index !== -1) {
266
+ return listWithoutIndex(arr, index);
267
+ }
268
+ return arr;
269
+ }
270
+
271
+ export { Unpromise, resolveSelfTuple };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trpc/server",
3
- "version": "11.0.0-rc.599+e16cd8d7c",
3
+ "version": "11.0.0-rc.601+5a96855d9",
4
4
  "description": "The tRPC server library",
5
5
  "author": "KATT",
6
6
  "license": "MIT",
@@ -18,7 +18,7 @@
18
18
  "dev": "pnpm build --watch",
19
19
  "codegen-entrypoints": "tsx entrypoints.script.ts",
20
20
  "benchmark": "tsc --project tsconfig.benchmark.json",
21
- "lint": "eslint --cache --ext \".js,.ts,.tsx\" --ignore-path ../../.gitignore src",
21
+ "lint": "eslint --cache --ext \".js,.ts,.tsx\" --ignore-path ../../.gitignore --ignore-pattern 'src/vendor' src",
22
22
  "ts-watch": "tsc --watch"
23
23
  },
24
24
  "exports": {
@@ -121,7 +121,7 @@
121
121
  "@types/aws-lambda": "^8.10.137",
122
122
  "@types/express": "^4.17.17",
123
123
  "@types/hash-sum": "^1.0.0",
124
- "@types/node": "^20.10.0",
124
+ "@types/node": "^22.0.0",
125
125
  "@types/react": "^18.3.1",
126
126
  "@types/react-dom": "^18.3.0",
127
127
  "@types/ws": "^8.2.0",
@@ -149,5 +149,5 @@
149
149
  "funding": [
150
150
  "https://trpc.io/sponsor"
151
151
  ],
152
- "gitHead": "e16cd8d7cee12eaf1545273858407f0947ca97fc"
152
+ "gitHead": "5a96855d9f2f19dd2a8a9d43f82cf64ac40cb640"
153
153
  }
@@ -33,6 +33,7 @@ import {
33
33
  run,
34
34
  type MaybePromise,
35
35
  } from '../unstable-core-do-not-import';
36
+ import { Unpromise } from '../vendor/unpromise';
36
37
  import type { NodeHTTPCreateContextFnOptions } from './node-http';
37
38
 
38
39
  /**
@@ -282,8 +283,18 @@ export function getWSConnectionHandler<TRouter extends AnyRouter>(
282
283
  });
283
284
 
284
285
  run(async () => {
286
+ // We need those declarations outside the loop for garbage collection reasons. If they
287
+ // were declared inside, they would not be freed until the next value is present.
288
+ let next:
289
+ | null
290
+ | TRPCError
291
+ | Awaited<
292
+ typeof abortPromise | ReturnType<(typeof iterator)['next']>
293
+ >;
294
+ let result: null | TRPCResultMessage<unknown>['result'];
295
+
285
296
  while (true) {
286
- const next = await Promise.race([
297
+ next = await Unpromise.race([
287
298
  iterator.next().catch(getTRPCErrorFromUnknown),
288
299
  abortPromise,
289
300
  ]);
@@ -313,7 +324,7 @@ export function getWSConnectionHandler<TRouter extends AnyRouter>(
313
324
  break;
314
325
  }
315
326
 
316
- const result: TRPCResultMessage<unknown>['result'] = {
327
+ result = {
317
328
  type: 'data',
318
329
  data: next.value,
319
330
  };
@@ -332,6 +343,10 @@ export function getWSConnectionHandler<TRouter extends AnyRouter>(
332
343
  jsonrpc,
333
344
  result,
334
345
  });
346
+
347
+ // free up references for garbage collection
348
+ next = null;
349
+ result = null;
335
350
  }
336
351
 
337
352
  await iterator.return?.();
@@ -358,7 +358,7 @@ export interface ProcedureBuilder<
358
358
  * Subscription procedure
359
359
  * @see https://trpc.io/docs/v11/server/subscriptions
360
360
  */
361
- subscription<$Output extends AsyncGenerator<any, void, any>>(
361
+ subscription<$Output extends AsyncIterable<any, void, any>>(
362
362
  resolver: ProcedureResolver<
363
363
  TContext,
364
364
  TMeta,
@@ -1,3 +1,4 @@
1
+ import { Unpromise } from '../../vendor/unpromise';
1
2
  import { getTRPCErrorFromUnknown } from '../error/TRPCError';
2
3
  import { isAsyncIterable, isFunction, isObject, run } from '../utils';
3
4
  import type { Deferred } from './utils/createDeferred';
@@ -148,7 +149,7 @@ function createBatchStreamProducer(opts: ProducerOptions) {
148
149
  const idx = counter++ as ChunkIndex;
149
150
  pending.add(idx);
150
151
 
151
- Promise.race([promise, stream.cancelledPromise])
152
+ Unpromise.race([promise, stream.cancelledPromise])
152
153
  .then((it) => {
153
154
  if (isCancelledStreamResult(it)) {
154
155
  return;
@@ -191,7 +192,7 @@ function createBatchStreamProducer(opts: ProducerOptions) {
191
192
  const iterator = iterable[Symbol.asyncIterator]();
192
193
 
193
194
  while (true) {
194
- const next = await Promise.race([
195
+ const next = await Unpromise.race([
195
196
  iterator.next().catch(getTRPCErrorFromUnknown),
196
197
  stream.cancelledPromise,
197
198
  ]);