FRE-600: Fix code review blockers

- Consolidated duplicate UndoManagers to single instance
- Fixed connection promise to only resolve on 'connected' status
- Fixed WebSocketProvider import (WebsocketProvider)
- Added proper doc.destroy() cleanup
- Renamed isPresenceInitialized property to avoid conflict

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
2026-04-25 00:08:01 -04:00
parent 65b552bb08
commit 7c684a42cc
48450 changed files with 5679671 additions and 383 deletions

14
node_modules/jayson/lib/client/browser/index.d.ts generated vendored Normal file
View File

@@ -0,0 +1,14 @@
import * as jayson from '../../..';
type ClientBrowserCallServerFunctionCallback = (err?:Error | null, response?:string) => void;
type ClientBrowserCallServerFunction = (request:string, callback:ClientBrowserCallServerFunctionCallback) => void;
declare class ClientBrowser {
constructor(callServer:ClientBrowserCallServerFunction, options?:jayson.ClientOptions);
request(method: string, params: jayson.RequestParamsLike, id?: jayson.JSONRPCIDLike | null, callback?: jayson.JSONRPCCallbackType): jayson.JSONRPCRequest;
request(method: string, params: jayson.RequestParamsLike, callback?: jayson.JSONRPCCallbackType): jayson.JSONRPCRequest;
request(method: Array<jayson.JSONRPCRequestLike>, callback: jayson.JSONRPCCallbackTypeBatch): Array<jayson.JSONRPCRequest>;
}
export = ClientBrowser;

167
node_modules/jayson/lib/client/browser/index.js generated vendored Normal file
View File

@@ -0,0 +1,167 @@
'use strict';
const uuid = require('uuid').v4;
const generateRequest = require('../../generateRequest');
/**
* Constructor for a Jayson Browser Client that does not depend any node.js core libraries
* @class ClientBrowser
* @param {Function} callServer Method that calls the server, receives the stringified request and a regular node-style callback
* @param {Object} [options]
* @param {Function} [options.reviver] Reviver function for JSON
* @param {Function} [options.replacer] Replacer function for JSON
* @param {Number} [options.version=2] JSON-RPC version to use (1|2)
* @param {Function} [options.generator] Function to use for generating request IDs
* @param {Boolean} [options.notificationIdNull=false] When true, version 2 requests will set id to null instead of omitting it
* @return {ClientBrowser}
*/
const ClientBrowser = function(callServer, options) {
if(!(this instanceof ClientBrowser)) {
return new ClientBrowser(callServer, options);
}
if (!options) {
options = {};
}
this.options = {
reviver: typeof options.reviver !== 'undefined' ? options.reviver : null,
replacer: typeof options.replacer !== 'undefined' ? options.replacer : null,
generator: typeof options.generator !== 'undefined' ? options.generator : function() { return uuid(); },
version: typeof options.version !== 'undefined' ? options.version : 2,
notificationIdNull: typeof options.notificationIdNull === 'boolean' ? options.notificationIdNull : false,
};
this.callServer = callServer;
};
module.exports = ClientBrowser;
/**
* Creates a request and dispatches it if given a callback.
* @param {String|Array} method A batch request if passed an Array, or a method name if passed a String
* @param {Array|Object} [params] Parameters for the method
* @param {String|Number} [id] Optional id. If undefined an id will be generated. If null it creates a notification request
* @param {Function} [callback] Request callback. If specified, executes the request rather than only returning it.
* @throws {TypeError} Invalid parameters
* @return {Object} JSON-RPC 1.0 or 2.0 compatible request
*/
ClientBrowser.prototype.request = function(method, params, id, callback) {
const self = this;
let request = null;
// is this a batch request?
const isBatch = Array.isArray(method) && typeof params === 'function';
if (this.options.version === 1 && isBatch) {
throw new TypeError('JSON-RPC 1.0 does not support batching');
}
// is this a raw request?
const isRaw = !isBatch && method && typeof method === 'object' && typeof params === 'function';
if(isBatch || isRaw) {
callback = params;
request = method;
} else {
if(typeof id === 'function') {
callback = id;
// specifically undefined because "null" is a notification request
id = undefined;
}
const hasCallback = typeof callback === 'function';
try {
request = generateRequest(method, params, id, {
generator: this.options.generator,
version: this.options.version,
notificationIdNull: this.options.notificationIdNull,
});
} catch(err) {
if(hasCallback) {
callback(err);
return;
}
throw err;
}
// no callback means we should just return a raw request
if(!hasCallback) {
return request;
}
}
let message;
try {
message = JSON.stringify(request, this.options.replacer);
} catch(err) {
callback(err);
return;
}
this.callServer(message, function(err, response) {
self._parseResponse(err, response, callback);
});
// always return the raw request
return request;
};
/**
* Parses a response from a server
* @param {Object} err Error to pass on that is unrelated to the actual response
* @param {String} responseText JSON-RPC 1.0 or 2.0 response
* @param {Function} callback Callback that will receive different arguments depending on the amount of parameters
* @private
*/
ClientBrowser.prototype._parseResponse = function(err, responseText, callback) {
if(err) {
callback(err);
return;
}
if(!responseText) {
// empty response text, assume that is correct because it could be a
// notification which jayson does not give any body for
callback();
return;
}
let response;
try {
response = JSON.parse(responseText, this.options.reviver);
} catch(err) {
callback(err);
return;
}
if(callback.length === 3) {
// if callback length is 3, we split callback arguments on error and response
// is batch response?
if(Array.isArray(response)) {
// necessary to split strictly on validity according to spec here
const isError = function(res) {
return typeof res.error !== 'undefined';
};
const isNotError = function (res) {
return !isError(res);
};
callback(null, response.filter(isError), response.filter(isNotError));
return;
} else {
// split regardless of validity
callback(null, response.error, response.result);
return;
}
}
callback(null, response);
};

118
node_modules/jayson/lib/client/http.js generated vendored Normal file
View File

@@ -0,0 +1,118 @@
'use strict';
const http = require('http');
const url = require('url');
const utils = require('../utils');
const Client = require('../client');
const { version } = require('../../package.json');
/**
* Constructor for a Jayson HTTP Client
* @class ClientHttp
* @constructor
* @extends Client
* @param {Object|String} [options] String interpreted as a URL
* @param {String} [options.encoding="utf8"] Encoding to use
* @return {ClientHttp}
*/
const ClientHttp = function(options) {
// accept first parameter as a url string
if(typeof(options) === 'string') {
options = url.parse(options);
}
if(!(this instanceof ClientHttp)) {
return new ClientHttp(options);
}
Client.call(this, options);
const defaults = utils.merge(this.options, {
encoding: 'utf8'
});
this.options = utils.merge(defaults, options || {});
};
require('util').inherits(ClientHttp, Client);
module.exports = ClientHttp;
ClientHttp.prototype._request = function(request, callback) {
const self = this;
// copies options so object can be modified in this context
const options = utils.merge({}, this.options);
utils.JSON.stringify(request, options, function(err, body) {
if(err) {
callback(err);
return;
}
options.method = options.method || 'POST';
const headers = {
'Content-Length': Buffer.byteLength(body, options.encoding),
'Content-Type': 'application/json; charset=utf-8',
Accept: 'application/json',
'User-Agent': `jayson-${version}`,
};
// let user override the headers
options.headers = utils.merge(headers, options.headers || {});
const req = self._getRequestStream(options);
self.emit('http request', req);
req.on('response', function(res) {
self.emit('http response', res, req);
res.setEncoding(options.encoding);
let data = '';
res.on('data', function(chunk) { data += chunk; });
res.on('end', function() {
// assume we have an error
if(res.statusCode < 200 || res.statusCode >= 300) {
// assume the server gave the reason in the body
const err = new Error(data);
err.code = res.statusCode;
callback(err);
} else {
// empty reply
if(!data || typeof(data) !== 'string') {
callback();
return;
}
utils.JSON.parse(data, options, callback);
}
});
});
// abort on timeout
req.on('timeout', function() {
req.abort(); // req.abort causes "error" event
});
// abort on error
req.on('error', function(err) {
self.emit('http error', err);
callback(err);
req.abort();
});
req.end(body);
});
};
/**
* Gets a stream interface to a http server
* @param {Object} options An options object
* @return {require('http').ClientRequest}
* @private
*/
ClientHttp.prototype._getRequestStream = function(options) {
return http.request(options || {});
};

34
node_modules/jayson/lib/client/https.js generated vendored Normal file
View File

@@ -0,0 +1,34 @@
'use strict';
const https = require('https');
const ClientHttp = require('./http');
/**
* Constructor for a Jayson HTTPS Client
* @class ClientHttps
* @constructor
* @extends ClientHttp
* @param {Object|String} [options] String interpreted as a URL
* @param {String} [options.encoding="utf8"] Encoding to use
* @return {ClientHttps}
*/
const ClientHttps = function(options) {
if(!(this instanceof ClientHttps)) {
return new ClientHttps(options);
}
// just proxy to constructor for ClientHttp
ClientHttp.call(this, options);
};
require('util').inherits(ClientHttps, ClientHttp);
module.exports = ClientHttps;
/**
* Gets a stream interface to a https server
* @param {Object} options An options object
* @return {require('https').ClientRequest}
* @private
*/
ClientHttps.prototype._getRequestStream = function(options) {
return https.request(options || {});
};

221
node_modules/jayson/lib/client/index.js generated vendored Normal file
View File

@@ -0,0 +1,221 @@
'use strict';
const events = require('events');
const utils = require('../utils');
/**
* Constructor for a Jayson Client
* @class Client
* @extends require('events').EventEmitter
* @param {Server} [server] An instance of Server (a object with a "call" method")
* @param {Object} [options]
* @param {Function} [options.reviver] Reviver function for JSON
* @param {Function} [options.replacer] Replacer function for JSON
* @param {Number} [options.version=2] JSON-RPC version to use (1|2)
* @param {Boolean} [options.notificationIdNull=false] When true, version 2 requests will set id to null instead of omitting it
* @param {Function} [options.generator] Function to use for generating request IDs
* @return {Client}
*/
const Client = function(server, options) {
if(arguments.length === 1 && utils.isPlainObject(server)) {
options = server;
server = null;
}
if(!(this instanceof Client)) {
return new Client(server, options);
}
const defaults = {
reviver: null,
replacer: null,
generator: utils.generateId,
version: 2,
notificationIdNull: false,
};
this.options = utils.merge(defaults, options || {});
if(server) {
this.server = server;
}
};
require('util').inherits(Client, events.EventEmitter);
module.exports = Client;
/**
* HTTP client constructor
* @type ClientHttp
* @static
*/
Client.http = require('./http');
/**
* HTTPS client constructor
* @type ClientHttps
* @static
*/
Client.https = require('./https');
/**
* TCP client constructor
* @type ClientTcp
* @static
*/
Client.tcp = require('./tcp');
/**
* TLS client constructor
* @type ClientTls
* @static
*/
Client.tls = require('./tls');
/**
* Browser client constructor
* @type ClientBrowser
* @static
*/
Client.browser = require('./browser');
/**
* Websocket client constructor
* @type ClientWebsocket
* @static
*/
Client.websocket = require('./websocket');
/**
* Creates a request and dispatches it if given a callback.
* @param {String|Array} method A batch request if passed an Array, or a method name if passed a String
* @param {Array|Object} params Parameters for the method
* @param {String|Number} [id] Optional id. If undefined an id will be generated. If null it creates a notification request
* @param {Function} [callback] Request callback. If specified, executes the request rather than only returning it.
* @throws {TypeError} Invalid parameters
* @return {Object} JSON-RPC 1.0 or 2.0 compatible request
*/
Client.prototype.request = function(method, params, id, callback) {
const self = this;
let request = null;
// is this a batch request?
const isBatch = Array.isArray(method) && typeof(params) === 'function';
if (this.options.version === 1 && isBatch) {
throw new TypeError('JSON-RPC 1.0 does not support batching');
}
// is this a raw request?
const isRaw = !isBatch && method && typeof(method) === 'object' && typeof(params) === 'function';
if(isBatch || isRaw) {
callback = params;
request = method;
} else {
if(typeof(id) === 'function') {
callback = id;
// specifically undefined because "null" is a notification request
id = undefined;
}
const hasCallback = typeof(callback) === 'function';
try {
request = utils.request(method, params, id, {
generator: this.options.generator,
version: this.options.version,
notificationIdNull: this.options.notificationIdNull,
});
} catch(err) {
if(hasCallback) {
callback(err);
return;
}
throw err;
}
// no callback means we should just return a raw request before sending
if(!hasCallback) {
return request;
}
}
this.emit('request', request);
this._request(request, function(err, response) {
self.emit('response', request, response);
self._parseResponse(err, response, callback);
});
// always return the raw request
return request;
};
/**
* Executes a request on a directly bound server
* @param {Object} request A JSON-RPC 1.0 or 2.0 request
* @param {Function} callback Request callback that will receive the server response as the second argument
* @private
*/
Client.prototype._request = function(request, callback) {
const self = this;
// serializes the request as a JSON string so that we get a copy and can run the replacer as intended
utils.JSON.stringify(request, this.options, function(err, message) {
if(err) {
callback(err);
return;
}
self.server.call(message, function(error, success) {
const response = error || success;
callback(null, response);
});
});
};
/**
* Parses a response from a server, taking care of sugaring
* @param {Object} err Error to pass on that is unrelated to the actual response
* @param {Object} response JSON-RPC 1.0 or 2.0 response
* @param {Function} callback Callback that will receive different arguments depending on the amount of parameters
* @private
*/
Client.prototype._parseResponse = function(err, response, callback) {
if(err) {
callback(err);
return;
}
if(!response || typeof(response) !== 'object') {
callback();
return;
}
if(callback.length === 3) {
// if callback length is 3, we split callback arguments on error and response
// is batch response?
if(Array.isArray(response)) {
// necessary to split strictly on validity according to spec here
const isError = function(res) { return typeof(res.error) !== 'undefined'; };
const isNotError = function(res) { return !isError(res); };
callback(null, response.filter(isError), response.filter(isNotError));
return;
} else {
// split regardless of validity
callback(null, response.error, response.result);
return;
}
}
callback(null, response);
};

92
node_modules/jayson/lib/client/tcp.js generated vendored Normal file
View File

@@ -0,0 +1,92 @@
'use strict';
const net = require('net');
const utils = require('../utils');
const Client = require('../client');
/**
* Constructor for a Jayson TCP Client
* @class ClientTcp
* @constructor
* @extends Client
* @param {Object|String} [options] Object goes into options for net.connect, String goes into options.path. String option argument is NOT recommended.
* @return {ClientTcp}
*/
const ClientTcp = function(options) {
if(typeof(options) === 'string') {
options = {path: options};
}
if(!(this instanceof ClientTcp)) {
return new ClientTcp(options);
}
Client.call(this, options);
const defaults = utils.merge(this.options, {
encoding: 'utf8'
});
this.options = utils.merge(defaults, options || {});
};
require('util').inherits(ClientTcp, Client);
module.exports = ClientTcp;
ClientTcp.prototype._request = function(request, callback) {
const self = this;
// copies options so object can be modified in this context
const options = utils.merge({}, this.options);
const delimiter = options.delimiter || '\n';
utils.JSON.stringify(request, options, function(err, body) {
if(err) {
callback(err);
return;
}
let handled = false;
const conn = net.connect(options, function() {
conn.setEncoding(options.encoding);
// wont get anything for notifications, just end here
if(utils.Request.isNotification(request)) {
handled = true;
conn.end(body + delimiter);
callback();
} else {
utils.parseStream(conn, options, function(err, response) {
handled = true;
conn.end();
if(err) {
callback(err);
return;
}
callback(null, response);
});
conn.write(body + delimiter);
}
});
self.emit('tcp socket', conn);
conn.on('error', function(err) {
self.emit('tcp error', err);
callback(err);
});
conn.on('end', function() {
if(!handled) {
callback();
}
});
});
};

91
node_modules/jayson/lib/client/tls.js generated vendored Normal file
View File

@@ -0,0 +1,91 @@
'use strict';
const tls = require('tls');
const utils = require('../utils');
const Client = require('../client');
/**
* Constructor for a Jayson TLS-encrypted TCP Client
* @class ClientTls
* @constructor
* @extends Client
* @param {Object|String} [options] Object goes into options for tls.connect, String goes into options.path. String option argument is NOT recommended.
* @return {ClientTls}
*/
const ClientTls = function(options) {
if(typeof(options) === 'string') {
options = {path: options};
}
if(!(this instanceof ClientTls)) {
return new ClientTls(options);
}
Client.call(this, options);
const defaults = utils.merge(this.options, {
encoding: 'utf8'
});
this.options = utils.merge(defaults, options || {});
};
require('util').inherits(ClientTls, Client);
module.exports = ClientTls;
ClientTls.prototype._request = function(request, callback) {
const self = this;
// copies options so object can be modified in this context
const options = utils.merge({}, this.options);
utils.JSON.stringify(request, options, function(err, body) {
if(err) {
callback(err);
return;
}
let handled = false;
const conn = tls.connect(options, function() {
conn.setEncoding(options.encoding);
// wont get anything for notifications, just end here
if(utils.Request.isNotification(request)) {
handled = true;
conn.end(body + '\n');
callback();
} else {
utils.parseStream(conn, options, function(err, response) {
handled = true;
conn.end();
if(err) {
callback(err);
return;
}
callback(null, response);
});
conn.write(body + '\n');
}
});
self.emit('tcp socket', conn);
conn.on('error', function(err) {
self.emit('tcp error', err);
callback(err);
});
conn.on('end', function() {
if(!handled) {
callback();
}
});
});
};

135
node_modules/jayson/lib/client/websocket.js generated vendored Normal file
View File

@@ -0,0 +1,135 @@
'use strict';
const WebSocket = require('isomorphic-ws');
const utils = require('../utils');
const delay = require('delay');
const Client = require('../client');
/**
* Constructor for a Jayson Websocket Client
* @class ClientWebsocket
* @constructor
* @extends Client
* @param {Object} [options]
* @param {String} [options.url] When options.ws not provided this will be the URL to open the websocket to
* @param {ws.WebSocket} [options.ws] When not provided will create a WebSocket instance with options.url
* @param {Number} [options.timeout] Will wait this long in ms until callbacking with an error
* @return {ClientWebsocket}
*/
const ClientWebsocket = function(options) {
if(!(this instanceof ClientWebsocket)) {
return new ClientWebsocket(options);
}
Client.call(this, options);
const defaults = utils.merge(this.options, {});
this.options = utils.merge(defaults, options || {});
const self = this;
this.ws = this.options.ws || new WebSocket(this.options.url);
this.outstandingRequests = [];
this.handlers = {};
this.handlers.message = function (str) {
utils.JSON.parse(str, self.options, function(err, response) {
if (err) {
// invalid JSON is ignored
return;
}
if (Array.isArray(response)) {
// we have a batch reply
const matchingRequest = self.outstandingRequests.find(function ([request]) {
if (Array.isArray(request)) {
// a batch is considered matching if at least one response id matches one request id
return response.some(function (resp) {
if (utils.Response.isValidResponse(resp)) {
return request.some(function (req) {
return req.id === resp.id;
});
}
return false;
});
}
});
if (matchingRequest) {
const [ , resolve ] = matchingRequest;
return resolve(response);
}
} else if (utils.Response.isValidResponse(response)) {
const matchingRequest = self.outstandingRequests.find(function ([request]) {
return !Array.isArray(request) && request.id === response.id;
});
if (matchingRequest) {
const [ , resolve ] = matchingRequest;
return resolve(response);
}
}
});
};
this.ws.on('message', this.handlers.message);
};
require('util').inherits(ClientWebsocket, Client);
module.exports = ClientWebsocket;
/**
* @desc Removes all event listeners from Websocket instance which cancels all outstanding requests too
*/
ClientWebsocket.prototype.unlisten = function () {
for (const eventName in this.handlers) {
this.ws.off(eventName, this.handlers[eventName]);
}
};
ClientWebsocket.prototype._request = function(request, callback) {
const self = this;
const { ws, options } = this;
// we have to remove the object representing this request when the promise resolves/rejects
let outstandingItem;
Promise.race([
options.timeout > 0 ? delay(options.timeout).then(function () {
throw new Error('timeout reached after ' + options.timeout + ' ms');
}) : null,
new Promise(function (resolve, reject) {
utils.JSON.stringify(request, options, function(err, body) {
if (err) {
return resolve(err);
}
ws.send(body);
if (utils.Request.isNotification(request)) {
// notifications callback immediately since they don't have a reply
return resolve();
}
outstandingItem = [request, resolve, reject];
self.outstandingRequests.push(outstandingItem);
});
}),
].filter(v => v !== null)).then(function (result) {
removeOutstandingRequest();
callback(null, result);
}).catch(function (err) {
removeOutstandingRequest();
callback(err);
});
function removeOutstandingRequest () {
if (!outstandingItem) {
return;
}
self.outstandingRequests = self.outstandingRequests.filter(v => v !== outstandingItem);
}
};