@platformatic/itc 3.13.1 → 3.15.0

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 (2) hide show
  1. package/lib/index.js +75 -22
  2. package/package.json +1 -1
package/lib/index.js CHANGED
@@ -97,43 +97,89 @@ export function sanitize (data, transferList) {
97
97
  return data
98
98
  }
99
99
 
100
- let sanitized
101
-
102
100
  if (Buffer.isBuffer(data) || data instanceof Uint8Array) {
103
101
  // This will convert as Uint8Array
104
102
  return data
105
- } else if (Array.isArray(data)) {
106
- sanitized = []
103
+ }
104
+
105
+ if (Array.isArray(data)) {
106
+ let sanitized = null
107
+ let needsSanitization = false
107
108
 
108
- for (const value of data) {
109
+ for (let i = 0; i < data.length; i++) {
110
+ const value = data[i]
109
111
  const valueType = typeof value
110
112
 
111
113
  /* c8 ignore next 3 */
112
114
  if (valueType === 'function' || valueType === 'symbol') {
115
+ if (!needsSanitization) {
116
+ sanitized = data.slice(0, i)
117
+ needsSanitization = true
118
+ }
113
119
  continue
114
120
  }
115
121
 
116
- sanitized.push(value && typeof value === 'object' ? sanitize(value, transferList) : value)
122
+ let sanitizedValue = value
123
+ if (value && typeof value === 'object') {
124
+ sanitizedValue = sanitize(value, transferList)
125
+ if (sanitizedValue !== value && !needsSanitization) {
126
+ sanitized = data.slice(0, i)
127
+ needsSanitization = true
128
+ }
129
+ }
130
+
131
+ if (needsSanitization) {
132
+ sanitized.push(sanitizedValue)
133
+ }
117
134
  }
118
- } else {
119
- sanitized = {}
120
135
 
121
- for (const [key, value] of Object.entries(data)) {
122
- const valueType = typeof value
136
+ return needsSanitization ? sanitized : data
137
+ }
123
138
 
124
- if (valueType === 'function' || valueType === 'symbol') {
125
- continue
139
+ // Handle plain objects
140
+ let sanitized = null
141
+ let needsSanitization = false
142
+
143
+ for (const [key, value] of Object.entries(data)) {
144
+ const valueType = typeof value
145
+
146
+ if (valueType === 'function' || valueType === 'symbol') {
147
+ if (!needsSanitization) {
148
+ sanitized = {}
149
+ // Copy all previous properties
150
+ for (const [k] of Object.entries(data)) {
151
+ if (k === key) break
152
+ sanitized[k] = data[k]
153
+ }
154
+ needsSanitization = true
155
+ }
156
+ continue
157
+ }
158
+
159
+ let sanitizedValue = value
160
+ if (value && typeof value === 'object') {
161
+ sanitizedValue = sanitize(value, transferList)
162
+ if (sanitizedValue !== value && !needsSanitization) {
163
+ sanitized = {}
164
+ // Copy all previous properties
165
+ for (const [k] of Object.entries(data)) {
166
+ if (k === key) break
167
+ sanitized[k] = data[k]
168
+ }
169
+ needsSanitization = true
126
170
  }
171
+ }
127
172
 
128
- sanitized[key] = value && typeof value === 'object' ? sanitize(value, transferList) : value
173
+ if (needsSanitization) {
174
+ sanitized[key] = sanitizedValue
129
175
  }
130
176
  }
131
177
 
132
- return sanitized
178
+ return needsSanitization ? sanitized : data
133
179
  }
134
180
 
135
181
  export class ITC extends EventEmitter {
136
- #requestEmitter
182
+ #waitingRequests
137
183
  #handlers
138
184
  #listening
139
185
  #handling
@@ -154,16 +200,13 @@ export class ITC extends EventEmitter {
154
200
  // Without it, it's impossible to know which "side" of the ITC is being used.
155
201
  this.name = name
156
202
  this.port = port
157
- this.#requestEmitter = new EventEmitter()
203
+ this.#waitingRequests = new Map()
158
204
  this.#handlers = new Map()
159
205
  this.#listening = false
160
206
  this.#handling = false
161
207
  this.#closeAfterCurrentRequest = false
162
208
  this.#throwOnMissingHandler = throwOnMissingHandler ?? true
163
209
 
164
- // Make sure the emitter handle a lot of listeners at once before raising a warning
165
- this.#requestEmitter.setMaxListeners(1e3)
166
-
167
210
  /*
168
211
  There some contexts in which a message is sent and the event loop empties up while waiting for a response.
169
212
  For instance @platformatic/astro when doing build with custom commands.
@@ -197,19 +240,24 @@ export class ITC extends EventEmitter {
197
240
  throw new SendBeforeListen()
198
241
  }
199
242
 
243
+ let reqId
200
244
  try {
201
245
  this._enableKeepAlive()
202
246
 
203
247
  const request = generateRequest(name, message)
204
248
  this._send(request, options)
205
249
 
206
- const responsePromise = once(this.#requestEmitter, request.reqId).then(([response]) => response)
250
+ const promiseWithResolvers = Promise.withResolvers()
251
+ reqId = request.reqId
252
+ this.#waitingRequests.set(request.reqId, promiseWithResolvers)
207
253
 
208
- const { error, data } = await Unpromise.race([responsePromise, this.#closePromise])
254
+ const { error, data } = await Unpromise.race([promiseWithResolvers.promise, this.#closePromise])
209
255
 
210
256
  if (error !== null) throw error
211
257
  return data
212
258
  } finally {
259
+ // Clean up the waiting requests map even if an error occurred
260
+ this.#waitingRequests.delete(reqId)
213
261
  this._manageKeepAlive()
214
262
  }
215
263
  }
@@ -342,7 +390,12 @@ export class ITC extends EventEmitter {
342
390
  }
343
391
 
344
392
  _emitResponse (response) {
345
- this.#requestEmitter.emit(response.reqId, response)
393
+ const pending = this.#waitingRequests.get(response.reqId)
394
+ if (!pending) {
395
+ return
396
+ }
397
+
398
+ pending.resolve(response)
346
399
  }
347
400
 
348
401
  _enableKeepAlive () {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platformatic/itc",
3
- "version": "3.13.1",
3
+ "version": "3.15.0",
4
4
  "description": "",
5
5
  "main": "lib/index.js",
6
6
  "type": "module",