Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@recordreplay/recordings-cli

Package Overview
Dependencies
Maintainers
7
Versions
29
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@recordreplay/recordings-cli - npm Package Compare versions

Comparing version 0.8.0 to 0.9.0

2

package.json
{
"name": "@recordreplay/recordings-cli",
"version": "0.8.0",
"version": "0.9.0",
"description": "CLI tool for uploading and managing recordings",

@@ -5,0 +5,0 @@ "bin": {

@@ -49,2 +49,6 @@ # recordings-cli

### process <id>
Upload a recording, and then process it to ensure it can be replayed successfully.
### upload-all

@@ -70,2 +74,6 @@

### update-browsers
Updates any installed browsers used for recording in automation: [playwright](https://www.npmjs.com/package/@recordreplay/playwright), [puppeteer](https://www.npmjs.com/package/@recordreplay/puppeteer), and [cypress](https://www.npmjs.com/package/@recordreplay/cypress).
## Node Module Usage

@@ -97,2 +105,6 @@

### processRecording(id, opts)
Equivalent to `replay-recordings process <id>`, returns a promise that resolves with a recording ID if the upload and processing succeeded, or null if either failed.
### uploadAllRecordings(opts)

@@ -117,1 +129,5 @@

Equivalent to `replay-recordings rm-all`.
### updateBrowsers(opts)
Equivalent to `replay-recordings update-browsers`.

@@ -5,2 +5,3 @@ const { program } = require("commander");

uploadRecording,
processRecording,
uploadAllRecordings,

@@ -11,2 +12,3 @@ viewRecording,

removeAllRecordings,
updateBrowsers,
} = require("./main");

@@ -41,2 +43,19 @@

program
.command("process <id>")
.description("Upload a recording to the remote server and process it.")
.option(
"--directory <dir>",
"Alternate recording directory."
)
.option(
"--server <address>",
"Alternate server to upload recordings to."
)
.option(
"--api-key <key>",
"Authentication API Key"
)
.action(commandProcessRecording);
program
.command("upload-all")

@@ -111,2 +130,11 @@ .description("Upload all recordings to the remote server.")

program
.command("update-browsers")
.description("Update browsers used in automation.")
.option(
"--directory <dir>",
"Alternate recording directory."
)
.action(commandUpdateBrowsers);
program
.parseAsync()

@@ -129,2 +157,7 @@ .catch((err) => {

async function commandProcessRecording(id, opts) {
const recordingId = await processRecording(id, { ...opts, verbose: true });
process.exit(recordingId ? 0 : 1);
}
async function commandUploadAllRecordings(opts) {

@@ -154,1 +187,6 @@ const uploadedAll = await uploadAllRecordings({ ...opts, verbose: true });

}
async function commandUpdateBrowsers(opts) {
await updateBrowsers({ ...opts, verbose: true });
process.exit(0);
}

@@ -19,2 +19,4 @@ const WebSocket = require("ws");

this.socket.on("message", (message) => this.onMessage(message));
this.eventListeners = new Map();
}

@@ -40,6 +42,6 @@

async sendCommand(method, params, data) {
async sendCommand(method, params, data, sessionId) {
const id = this.nextMessageId++;
this.socket.send(
JSON.stringify({ id, method, params, binary: data ? true : undefined })
JSON.stringify({ id, method, params, binary: data ? true : undefined, sessionId })
);

@@ -54,2 +56,6 @@ if (data) {

setEventListener(method, callback) {
this.eventListeners.set(method, callback);
}
onMessage(contents) {

@@ -65,4 +71,6 @@ const msg = JSON.parse(contents);

}
} else if (this.eventListeners.has(msg.method)) {
this.eventListeners.get(msg.method)(msg.params);
} else {
throw new Error("Events NYI");
console.log(`Received event without listener: ${msg.method}`);
}

@@ -69,0 +77,0 @@ }

@@ -13,3 +13,3 @@ // Manage installation of browsers for other NPM packages.

if (["all", "gecko"].includes(kind)) {
await installReplayBrowser("macOS-replay-playwright.tar.xz", "firefox", "firefox");
await installReplayBrowser("macOS-replay-playwright.tar.xz", "playwright", "firefox", "firefox");
}

@@ -19,6 +19,6 @@ break;

if (["all", "gecko"].includes(kind)) {
await installReplayBrowser("linux-replay-playwright.tar.xz", "firefox", "firefox");
await installReplayBrowser("linux-replay-playwright.tar.xz", "playwright", "firefox", "firefox");
}
if (["all", "chromium"].includes(kind)) {
await installReplayBrowser("linux-replay-chromium.tar.xz", "replay-chromium", "chrome-linux");
await installReplayBrowser("linux-replay-chromium.tar.xz", "playwright", "replay-chromium", "chrome-linux");
}

@@ -29,2 +29,15 @@ break;

async function updateBrowsers(opts = {}) {
switch (process.platform) {
case "darwin":
await updateReplayBrowser("macOS-replay-playwright.tar.xz", "playwright", "firefox", "firefox", opts);
break;
case "linux":
await updateReplayBrowser("linux-replay-playwright.tar.xz", "playwright", "firefox", "firefox", opts);
await updateReplayBrowser("linux-replay-chromium.tar.xz", "playwright", "replay-chromium", "chrome-linux", opts);
await updateReplayBrowser("linux-replay-chromium.tar.xz", "puppeteer", "replay-chromium", "chrome-linux", opts);
break;
}
}
function getPlaywrightBrowserPath(kind) {

@@ -44,7 +57,8 @@ const replayDir = getDirectory();

async function installReplayBrowser(name, srcName, dstName) {
// Installs a browser if it isn't already installed.
async function installReplayBrowser(name, subdir, srcName, dstName) {
const replayDir = getDirectory();
const playwrightDir = path.join(replayDir, "playwright");
const browserDir = path.join(replayDir, subdir);
if (fs.existsSync(path.join(playwrightDir, dstName))) {
if (fs.existsSync(path.join(browserDir, dstName))) {
return;

@@ -55,3 +69,3 @@ }

for (const dir of [replayDir, playwrightDir]) {
for (const dir of [replayDir, browserDir]) {
if (!fs.existsSync(dir)) {

@@ -61,11 +75,36 @@ fs.mkdirSync(dir);

}
fs.writeFileSync(path.join(playwrightDir, name), contents);
spawnSync("tar", ["xf", name], { cwd: playwrightDir });
fs.unlinkSync(path.join(playwrightDir, name));
fs.writeFileSync(path.join(browserDir, name), contents);
spawnSync("tar", ["xf", name], { cwd: browserDir });
fs.unlinkSync(path.join(browserDir, name));
if (srcName != dstName) {
fs.renameSync(path.join(playwrightDir, srcName), path.join(playwrightDir, dstName));
fs.renameSync(path.join(browserDir, srcName), path.join(browserDir, dstName));
}
}
// Updates a browser if it is already installed.
async function updateReplayBrowser(name, subdir, srcName, dstName, opts) {
const replayDir = getDirectory(opts);
const browserDir = path.join(replayDir, subdir);
const dstDir = path.join(browserDir, dstName);
if (fs.existsSync(dstDir)) {
// Remove the browser so installReplayBrowser will reinstall it. We don't have a way
// to see that the current browser is up to date.
fs.rmSync(dstDir, { force: true, recursive: true });
} else {
return;
}
if (opts.verbose) {
console.log(`Updating browser ${subdir} ${dstName}...`);
}
await installReplayBrowser(name, subdir, srcName, dstName);
if (opts.verbose) {
console.log(`Updated.`);
}
}
async function downloadReplayFile(downloadFile) {

@@ -105,2 +144,6 @@ const options = {

module.exports = { ensurePlaywrightBrowsersInstalled, getPlaywrightBrowserPath };
module.exports = {
ensurePlaywrightBrowsersInstalled,
getPlaywrightBrowserPath,
updateBrowsers,
};

@@ -7,2 +7,3 @@ const fs = require("fs");

connectionProcessRecording,
connectionWaitForProcessed,
connectionUploadRecording,

@@ -15,2 +16,3 @@ closeConnection,

getPlaywrightBrowserPath,
updateBrowsers,
} = require("./install");

@@ -222,2 +224,6 @@ const { getDirectory, maybeLog } = require("./utils");

maybeLog(verbose, `Starting upload for ${recording.id}...`);
if (recording.status == "uploaded" && recording.recordingId) {
maybeLog(verbose, `Already uploaded: ${recording.recordingId}`);
return recording.recordingId;
}
const reason = uploadSkipReason(recording);

@@ -269,2 +275,36 @@ if (reason) {

async function processUploadedRecording(recordingId, opts) {
const server = getServer(opts);
const { apiKey, verbose } = opts;
maybeLog(verbose, `Processing recording ${recordingId}...`);
if (!(await initConnection(server, apiKey, verbose))) {
maybeLog(verbose, `Processing failed: can't connect to server ${server}`);
return false;
}
try {
const error = await connectionWaitForProcessed(recordingId);
if (error) {
maybeLog(verbose, `Processing failed: ${error}`);
return false;
}
} finally {
closeConnection();
}
maybeLog(verbose, "Finished processing.");
return true;
}
async function processRecording(id, opts = {}) {
const recordingId = await uploadRecording(id, opts);
if (!recordingId) {
return null;
}
const succeeded = await processUploadedRecording(recordingId, opts);
return succeeded ? recordingId : null;
}
async function uploadAllRecordings(opts = {}) {

@@ -400,3 +440,7 @@ const server = getServer(opts);

recordings.forEach(maybeRemoveRecordingFile);
fs.unlinkSync(getRecordingsFile(dir));
const file = getRecordingsFile(dir);
if (fs.existsSync(file)) {
fs.unlinkSync(file);
}
}

@@ -407,2 +451,3 @@

uploadRecording,
processRecording,
uploadAllRecordings,

@@ -413,2 +458,3 @@ viewRecording,

removeAllRecordings,
updateBrowsers,

@@ -415,0 +461,0 @@ // These methods aren't documented or available via the CLI, and are used by other

@@ -20,3 +20,2 @@ const ProtocolClient = require("./client");

onClose() {
maybeLog(verbose, `Server connection closed.`);
resolve(false);

@@ -67,2 +66,24 @@ },

async function connectionWaitForProcessed(recordingId) {
const { sessionId } = await gClient.sendCommand("Recording.createSession", { recordingId });
const waiter = defer();
gClient.setEventListener(
"Recording.sessionError",
({ message }) => waiter.resolve(`session error ${sessionId}: ${message}`)
);
gClient.setEventListener("Session.unprocessedRegions", () => {});
gClient.sendCommand(
"Session.ensureProcessed",
{ level: "basic" },
null,
sessionId
).then(() => waiter.resolve(null));
const error = await waiter.promise;
return error;
}
// Granularity for splitting up a recording into chunks for uploading.

@@ -104,2 +125,3 @@ const ChunkGranularity = 1024 * 1024;

connectionProcessRecording,
connectionWaitForProcessed,
connectionUploadRecording,

@@ -106,0 +128,0 @@ closeConnection,

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc