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

@appium/support

Package Overview
Dependencies
Maintainers
4
Versions
75
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@appium/support - npm Package Compare versions

Comparing version 4.5.0 to 5.0.1

1

build/lib/index.d.ts

@@ -99,3 +99,2 @@ /// <reference types="node" />

export type { ExtractAllOptions, ZipEntry, ZipOptions, ZipCompressionOptions, ZipSourceOptions, } from './zip';
export type { SecureValuePreprocessingRule } from './log-internal';
//# sourceMappingURL=index.d.ts.map

@@ -0,51 +1,14 @@

/**
*
* @param {AppiumLoggerPrefix?} [prefix=null]
* @returns {AppiumLogger}
*/
export function getLogger(prefix?: import("@appium/types").AppiumLoggerPrefix | null | undefined): AppiumLogger;
/** @type {import('@appium/types').AppiumLoggerLevel[]} */
export const LEVELS: import('@appium/types').AppiumLoggerLevel[];
export const log: import("@appium/types").AppiumLogger;
export default log;
export type LoadResult = {
/**
* The list of rule parsing issues (one item per rule).
* Rules with issues are skipped. An empty list is returned if no parsing issues exist.
*/
issues: string[];
/**
* The list of successfully loaded
* replacement rules. The list could be empty if no rules were loaded.
*/
rules: import('./log-internal').SecureValuePreprocessingRule[];
};
export type AppiumLoggerPrefix = import('@appium/types').AppiumLoggerPrefix;
export type AppiumLogger = import('@appium/types').AppiumLogger;
export type AppiumLoggerLevel = import('@appium/types').AppiumLoggerLevel;
export const log: import("@appium/types").AppiumLogger;
/**
*
* @param {import('@appium/logger').Logger} logger
*/
export function patchLogger(logger: import('@appium/logger').Logger): void;
/**
*
* @param {AppiumLoggerPrefix?} prefix
* @returns {AppiumLogger}
*/
export function getLogger(prefix?: AppiumLoggerPrefix | null): AppiumLogger;
/**
* @typedef LoadResult
* @property {string[]} issues The list of rule parsing issues (one item per rule).
* Rules with issues are skipped. An empty list is returned if no parsing issues exist.
* @property {import('./log-internal').SecureValuePreprocessingRule[]} rules The list of successfully loaded
* replacement rules. The list could be empty if no rules were loaded.
*/
/**
* Loads the JSON file containing secure values replacement rules.
* This might be necessary to hide sensitive values that may possibly
* appear in Appium logs.
* Each call to this method replaces the previously loaded rules if any existed.
*
* @param {string|string[]|import('@appium/types').LogFiltersConfig} rulesJsonPath The full path to the JSON file containing
* the replacement rules. Each rule could either be a string to be replaced
* or an object with predefined properties.
* @throws {Error} If the given file cannot be loaded
* @returns {Promise<LoadResult>}
*/
export function loadSecureValuesPreprocessingRules(rulesJsonPath: string | string[] | import('@appium/types').LogFiltersConfig): Promise<LoadResult>;
//# sourceMappingURL=logging.d.ts.map

178

build/lib/logging.js
"use strict";
// @ts-check
var __importDefault = (this && this.__importDefault) || function (mod) {

@@ -7,8 +6,6 @@ return (mod && mod.__esModule) ? mod : { "default": mod };

Object.defineProperty(exports, "__esModule", { value: true });
exports.loadSecureValuesPreprocessingRules = exports.getLogger = exports.patchLogger = exports.log = exports.LEVELS = void 0;
exports.getLogger = exports.log = exports.LEVELS = void 0;
const logger_1 = __importDefault(require("@appium/logger"));
const lodash_1 = __importDefault(require("lodash"));
const util_1 = require("./util");
const moment_1 = __importDefault(require("moment"));
const log_internal_1 = __importDefault(require("./log-internal"));
/** @type {import('@appium/types').AppiumLoggerLevel[]} */

@@ -18,63 +15,49 @@ exports.LEVELS = ['silly', 'verbose', 'debug', 'info', 'http', 'warn', 'error'];

const PREFIX_TIMESTAMP_FORMAT = 'HH-mm-ss:SSS';
// mock log object used in testing mode
let mockLog = {};
for (let level of exports.LEVELS) {
mockLog[level] = () => { };
}
// export a default logger with no prefix
exports.log = getLogger();
// mock log object is used in testing mode to silence the output
const MOCK_LOG = {
unwrap: () => ({
loadSecureValuesPreprocessingRules: () => ({
issues: [],
rules: [],
}),
level: 'verbose',
prefix: '',
log: lodash_1.default.noop,
}),
...(lodash_1.default.fromPairs(exports.LEVELS.map((l) => [l, lodash_1.default.noop]))),
};
/**
*
* @param {import('@appium/logger').Logger} logger
*/
function patchLogger(logger) {
if (!logger.debug) {
logger.addLevel('debug', 1000, { fg: 'blue', bg: 'black' }, 'dbug');
}
}
exports.patchLogger = patchLogger;
/**
*
* @returns {[import('@appium/logger').Logger, boolean]}
*/
function _getLogger() {
// check if the user set the `_TESTING` or `_FORCE_LOGS` flag
const testingMode = process.env._TESTING === '1';
const forceLogMode = process.env._FORCE_LOGS === '1';
// if is possible that there is a logger instance that is already around,
// in which case we want t o use that
const usingGlobalLog = !!global._global_npmlog;
let logger;
if (testingMode && !forceLogMode) {
// in testing mode, use a mock logger object that we can query
logger = mockLog;
}
else {
// otherwise, either use the global, or a new `npmlog` object
logger = global._global_npmlog || logger_1.default;
// The default value is 10000, which causes excessive memory usage
logger.maxRecordSize = MAX_LOG_RECORDS_COUNT;
}
patchLogger(logger);
return [logger, usingGlobalLog];
}
/**
* @param {AppiumLoggerPrefix?} prefix
* @param {boolean} logTimestamp whether to include timestamps into log prefixes
* @returns {string}
*/
function getActualPrefix(prefix, logTimestamp = false) {
const result = (lodash_1.default.isFunction(prefix) ? prefix() : prefix) ?? '';
return logTimestamp ? `[${(0, moment_1.default)().format(PREFIX_TIMESTAMP_FORMAT)}] ${result}` : result;
}
/**
*
* @param {AppiumLoggerPrefix?} prefix
* @param {AppiumLoggerPrefix?} [prefix=null]
* @returns {AppiumLogger}
*/
function getLogger(prefix = null) {
let [logger, usingGlobalLog] = _getLogger();
const [logger, usingGlobalLog] = _getLogger();
// wrap the logger so that we can catch and modify any logging
let wrappedLogger = {
const wrappedLogger = {
unwrap: () => logger,
levels: exports.LEVELS,
prefix,
/**
* Logs given arguments at the error level and returns
* the error object.
*
* @param {...any} args
* @returns {Error}
*/
errorWithException(/** @type {any[]} */ ...args) {
this.error(...args);
// make sure we have an `Error` object. Wrap if necessary
return lodash_1.default.isError(args[0]) ? args[0] : new Error(args.join('\n'));
},
/**
* @deprecated Use {@link errorWithException} instead
* @param {...any} args
* @throws {Error}
*/
errorAndThrow(/** @type {any[]} */ ...args) {
throw this.errorWithException(args);
},
};

@@ -92,29 +75,11 @@ // allow access to the level of the underlying logger

});
const logTimestamp = process.env._LOG_TIMESTAMP === '1';
const isDebugTimestampLoggingEnabled = process.env._LOG_TIMESTAMP === '1';
// add all the levels from `npmlog`, and map to the underlying logger
for (const level of exports.LEVELS) {
wrappedLogger[level] = /** @param {...any} args */ function (...args) {
const actualPrefix = getActualPrefix(this.prefix, logTimestamp);
for (const arg of args) {
const out = lodash_1.default.isError(arg) && arg.stack ? arg.stack : `${arg}`;
for (const line of out.split('\n')) {
// it is necessary to unleak each line because `split` call
// creates "views" to the original string as well as the `substring` one
const unleakedLine = (0, util_1.unleakString)(line);
logger[level](actualPrefix, log_internal_1.default.preprocess(unleakedLine));
}
}
wrappedLogger[level] = /** @param {any[]} args */ function (...args) {
const finalPrefix = getFinalPrefix(this.prefix, isDebugTimestampLoggingEnabled);
// @ts-ignore This is OK
logger[level](finalPrefix, ...args);
};
}
wrappedLogger.errorWithException = function (/** @type {any[]} */ ...args) {
this.error(...args);
// make sure we have an `Error` object. Wrap if necessary
return lodash_1.default.isError(args[0]) ? args[0] : new Error(args.map(util_1.unleakString).join('\n'));
};
/**
* @deprecated Use {@link errorWithException} instead
*/
wrappedLogger.errorAndThrow = function (/** @type {any[]} */ ...args) {
throw this.errorWithException(args);
};
if (!usingGlobalLog) {

@@ -130,32 +95,35 @@ // if we're not using a global log specified from some top-level package,

/**
* @typedef LoadResult
* @property {string[]} issues The list of rule parsing issues (one item per rule).
* Rules with issues are skipped. An empty list is returned if no parsing issues exist.
* @property {import('./log-internal').SecureValuePreprocessingRule[]} rules The list of successfully loaded
* replacement rules. The list could be empty if no rules were loaded.
*
* @returns {[import('@appium/logger').Logger, boolean]}
*/
function _getLogger() {
// check if the user set the `_TESTING` or `_FORCE_LOGS` flag
const testingMode = process.env._TESTING === '1';
const forceLogMode = process.env._FORCE_LOGS === '1';
// if is possible that there is a logger instance that is already around,
// in which case we want t o use that
const useGlobalLog = !!global._global_npmlog;
const logger = testingMode && !forceLogMode
// in testing mode, use a mock logger object that we can query
? MOCK_LOG
// otherwise, either use the global, or a new `npmlog` object
: (global._global_npmlog || logger_1.default);
// The default value is 10000, which causes excessive memory usage
logger.maxRecordSize = MAX_LOG_RECORDS_COUNT;
return [logger, useGlobalLog];
}
/**
* Loads the JSON file containing secure values replacement rules.
* This might be necessary to hide sensitive values that may possibly
* appear in Appium logs.
* Each call to this method replaces the previously loaded rules if any existed.
*
* @param {string|string[]|import('@appium/types').LogFiltersConfig} rulesJsonPath The full path to the JSON file containing
* the replacement rules. Each rule could either be a string to be replaced
* or an object with predefined properties.
* @throws {Error} If the given file cannot be loaded
* @returns {Promise<LoadResult>}
* @param {AppiumLoggerPrefix?} prefix
* @param {boolean} [shouldLogTimestamp=false] whether to include timestamps into log prefixes
* @returns {string}
*/
async function loadSecureValuesPreprocessingRules(rulesJsonPath) {
const issues = await log_internal_1.default.loadRules(rulesJsonPath);
return {
issues,
rules: lodash_1.default.cloneDeep(log_internal_1.default.rules),
};
function getFinalPrefix(prefix, shouldLogTimestamp = false) {
const result = (lodash_1.default.isFunction(prefix) ? prefix() : prefix) ?? '';
if (!shouldLogTimestamp) {
return result;
}
const formattedTimestamp = `[${(0, moment_1.default)().format(PREFIX_TIMESTAMP_FORMAT)}]`;
return result ? `${formattedTimestamp} ${result}` : formattedTimestamp;
}
exports.loadSecureValuesPreprocessingRules = loadSecureValuesPreprocessingRules;
// export a default logger with no prefix
const log = getLogger();
exports.log = log;
exports.default = log;
exports.default = exports.log;
/**

@@ -162,0 +130,0 @@ * @typedef {import('@appium/types').AppiumLoggerPrefix} AppiumLoggerPrefix

@@ -141,11 +141,2 @@ /**

/**
* This function is necessary to workaround unexpected memory leaks
* caused by NodeJS string interning
* behavior described in https://bugs.chromium.org/p/v8/issues/detail?id=2869
*
* @param {any} s - The string to unleak
* @return {string} Either the unleaked string or the original object converted to string
*/
export function unleakString(s: any): string;
/**
* Stringifies the object passed in, converting Buffers into Strings for better

@@ -152,0 +143,0 @@ * display. This mimics JSON.stringify (see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify)

@@ -6,3 +6,3 @@ "use strict";

Object.defineProperty(exports, "__esModule", { value: true });
exports.getLockFileGuard = exports.shellParse = exports.uuidV5 = exports.uuidV4 = exports.uuidV3 = exports.uuidV1 = exports.toInMemoryBase64 = exports.KiB = exports.MiB = exports.GiB = exports.pluralize = exports.jsonStringify = exports.unleakString = exports.quote = exports.coerceVersion = exports.compareVersions = exports.isSameDestination = exports.W3C_WEB_ELEMENT_IDENTIFIER = exports.isSubPath = exports.toReadableSizeString = exports.filterObject = exports.unwrapElement = exports.wrapElement = exports.safeJsonParse = exports.multiResolve = exports.cancellableDelay = exports.localIp = exports.escapeSpecialChars = exports.escapeSpace = exports.hasValue = exports.hasContent = void 0;
exports.getLockFileGuard = exports.shellParse = exports.uuidV5 = exports.uuidV4 = exports.uuidV3 = exports.uuidV1 = exports.toInMemoryBase64 = exports.KiB = exports.MiB = exports.GiB = exports.pluralize = exports.jsonStringify = exports.quote = exports.coerceVersion = exports.compareVersions = exports.isSameDestination = exports.W3C_WEB_ELEMENT_IDENTIFIER = exports.isSubPath = exports.toReadableSizeString = exports.filterObject = exports.unwrapElement = exports.wrapElement = exports.safeJsonParse = exports.multiResolve = exports.cancellableDelay = exports.localIp = exports.escapeSpecialChars = exports.escapeSpace = exports.hasValue = exports.hasContent = void 0;
const bluebird_1 = __importDefault(require("bluebird"));

@@ -355,14 +355,2 @@ const lodash_1 = __importDefault(require("lodash"));

/**
* This function is necessary to workaround unexpected memory leaks
* caused by NodeJS string interning
* behavior described in https://bugs.chromium.org/p/v8/issues/detail?id=2869
*
* @param {any} s - The string to unleak
* @return {string} Either the unleaked string or the original object converted to string
*/
function unleakString(s) {
return ` ${s}`.substring(1);
}
exports.unleakString = unleakString;
/**
* @typedef PluralizeOptions

@@ -369,0 +357,0 @@ * @property {boolean} [inclusive=false] - Whether to prefix with the number (e.g., 3 ducks)

@@ -83,2 +83,1 @@ import * as tempDir from './tempdir';

} from './zip';
export type {SecureValuePreprocessingRule} from './log-internal';

@@ -1,8 +0,4 @@

// @ts-check
import globalLog from '@appium/logger';
import _ from 'lodash';
import {unleakString} from './util';
import moment from 'moment';
import SECURE_VALUES_PREPROCESSOR from './log-internal';

@@ -12,72 +8,53 @@ /** @type {import('@appium/types').AppiumLoggerLevel[]} */

const MAX_LOG_RECORDS_COUNT = 3000;
const PREFIX_TIMESTAMP_FORMAT = 'HH-mm-ss:SSS';
// export a default logger with no prefix
export const log = getLogger();
// mock log object is used in testing mode to silence the output
const MOCK_LOG = {
unwrap: () => ({
loadSecureValuesPreprocessingRules: () => ({
issues: [],
rules: [],
}),
level: 'verbose',
prefix: '',
log: _.noop,
}),
...(_.fromPairs(LEVELS.map((l) => [l, _.noop]))),
};
// mock log object used in testing mode
let mockLog = {};
for (let level of LEVELS) {
mockLog[level] = () => {};
}
/**
*
* @param {import('@appium/logger').Logger} logger
*/
function patchLogger(logger) {
if (!logger.debug) {
logger.addLevel('debug', 1000, {fg: 'blue', bg: 'black'}, 'dbug');
}
}
/**
*
* @returns {[import('@appium/logger').Logger, boolean]}
*/
function _getLogger() {
// check if the user set the `_TESTING` or `_FORCE_LOGS` flag
const testingMode = process.env._TESTING === '1';
const forceLogMode = process.env._FORCE_LOGS === '1';
// if is possible that there is a logger instance that is already around,
// in which case we want t o use that
const usingGlobalLog = !!global._global_npmlog;
let logger;
if (testingMode && !forceLogMode) {
// in testing mode, use a mock logger object that we can query
logger = mockLog;
} else {
// otherwise, either use the global, or a new `npmlog` object
logger = global._global_npmlog || globalLog;
// The default value is 10000, which causes excessive memory usage
logger.maxRecordSize = MAX_LOG_RECORDS_COUNT;
}
patchLogger(logger);
return [logger, usingGlobalLog];
}
/**
* @param {AppiumLoggerPrefix?} prefix
* @param {boolean} logTimestamp whether to include timestamps into log prefixes
* @returns {string}
*/
function getActualPrefix(prefix, logTimestamp = false) {
const result = (_.isFunction(prefix) ? prefix() : prefix) ?? '';
return logTimestamp ? `[${moment().format(PREFIX_TIMESTAMP_FORMAT)}] ${result}` : result;
}
/**
*
* @param {AppiumLoggerPrefix?} prefix
* @param {AppiumLoggerPrefix?} [prefix=null]
* @returns {AppiumLogger}
*/
function getLogger(prefix = null) {
let [logger, usingGlobalLog] = _getLogger();
export function getLogger(prefix = null) {
const [logger, usingGlobalLog] = _getLogger();
// wrap the logger so that we can catch and modify any logging
let wrappedLogger = {
const wrappedLogger = {
unwrap: () => logger,
levels: LEVELS,
prefix,
/**
* Logs given arguments at the error level and returns
* the error object.
*
* @param {...any} args
* @returns {Error}
*/
errorWithException (/** @type {any[]} */ ...args) {
this.error(...args);
// make sure we have an `Error` object. Wrap if necessary
return _.isError(args[0]) ? args[0] : new Error(args.join('\n'));
},
/**
* @deprecated Use {@link errorWithException} instead
* @param {...any} args
* @throws {Error}
*/
errorAndThrow (/** @type {any[]} */ ...args) {
throw this.errorWithException(args);
},
};
// allow access to the level of the underlying logger

@@ -94,31 +71,11 @@ Object.defineProperty(wrappedLogger, 'level', {

});
const logTimestamp = process.env._LOG_TIMESTAMP === '1';
const isDebugTimestampLoggingEnabled = process.env._LOG_TIMESTAMP === '1';
// add all the levels from `npmlog`, and map to the underlying logger
for (const level of LEVELS) {
wrappedLogger[level] = /** @param {...any} args */ function (...args) {
const actualPrefix = getActualPrefix(this.prefix, logTimestamp);
for (const arg of args) {
const out = _.isError(arg) && arg.stack ? arg.stack : `${arg}`;
for (const line of out.split('\n')) {
// it is necessary to unleak each line because `split` call
// creates "views" to the original string as well as the `substring` one
const unleakedLine = unleakString(line);
logger[level](actualPrefix, SECURE_VALUES_PREPROCESSOR.preprocess(unleakedLine));
}
}
wrappedLogger[level] = /** @param {any[]} args */ function (...args) {
const finalPrefix = getFinalPrefix(this.prefix, isDebugTimestampLoggingEnabled);
// @ts-ignore This is OK
logger[level](finalPrefix, ...args);
};
}
wrappedLogger.errorWithException = function (/** @type {any[]} */ ...args) {
this.error(...args);
// make sure we have an `Error` object. Wrap if necessary
return _.isError(args[0]) ? args[0] : new Error(args.map(unleakString).join('\n'));
};
/**
* @deprecated Use {@link errorWithException} instead
*/
wrappedLogger.errorAndThrow = function (/** @type {any[]} */ ...args) {
throw this.errorWithException(args);
};
if (!usingGlobalLog) {

@@ -134,33 +91,36 @@ // if we're not using a global log specified from some top-level package,

/**
* @typedef LoadResult
* @property {string[]} issues The list of rule parsing issues (one item per rule).
* Rules with issues are skipped. An empty list is returned if no parsing issues exist.
* @property {import('./log-internal').SecureValuePreprocessingRule[]} rules The list of successfully loaded
* replacement rules. The list could be empty if no rules were loaded.
*
* @returns {[import('@appium/logger').Logger, boolean]}
*/
function _getLogger() {
// check if the user set the `_TESTING` or `_FORCE_LOGS` flag
const testingMode = process.env._TESTING === '1';
const forceLogMode = process.env._FORCE_LOGS === '1';
// if is possible that there is a logger instance that is already around,
// in which case we want t o use that
const useGlobalLog = !!global._global_npmlog;
const logger = testingMode && !forceLogMode
// in testing mode, use a mock logger object that we can query
? MOCK_LOG
// otherwise, either use the global, or a new `npmlog` object
: (global._global_npmlog || globalLog);
// The default value is 10000, which causes excessive memory usage
logger.maxRecordSize = MAX_LOG_RECORDS_COUNT;
return [logger, useGlobalLog];
}
/**
* Loads the JSON file containing secure values replacement rules.
* This might be necessary to hide sensitive values that may possibly
* appear in Appium logs.
* Each call to this method replaces the previously loaded rules if any existed.
*
* @param {string|string[]|import('@appium/types').LogFiltersConfig} rulesJsonPath The full path to the JSON file containing
* the replacement rules. Each rule could either be a string to be replaced
* or an object with predefined properties.
* @throws {Error} If the given file cannot be loaded
* @returns {Promise<LoadResult>}
* @param {AppiumLoggerPrefix?} prefix
* @param {boolean} [shouldLogTimestamp=false] whether to include timestamps into log prefixes
* @returns {string}
*/
async function loadSecureValuesPreprocessingRules(rulesJsonPath) {
const issues = await SECURE_VALUES_PREPROCESSOR.loadRules(rulesJsonPath);
return {
issues,
rules: _.cloneDeep(SECURE_VALUES_PREPROCESSOR.rules),
};
function getFinalPrefix(prefix, shouldLogTimestamp = false) {
const result = (_.isFunction(prefix) ? prefix() : prefix) ?? '';
if (!shouldLogTimestamp) {
return result;
}
const formattedTimestamp = `[${moment().format(PREFIX_TIMESTAMP_FORMAT)}]`;
return result ? `${formattedTimestamp} ${result}` : formattedTimestamp;
}
// export a default logger with no prefix
const log = getLogger();
export {log, patchLogger, getLogger, loadSecureValuesPreprocessingRules};
export default log;

@@ -167,0 +127,0 @@

@@ -362,14 +362,2 @@ import B from 'bluebird';

/**
* This function is necessary to workaround unexpected memory leaks
* caused by NodeJS string interning
* behavior described in https://bugs.chromium.org/p/v8/issues/detail?id=2869
*
* @param {any} s - The string to unleak
* @return {string} Either the unleaked string or the original object converted to string
*/
function unleakString(s) {
return ` ${s}`.substring(1);
}
/**
* @typedef PluralizeOptions

@@ -559,3 +547,2 @@ * @property {boolean} [inclusive=false] - Whether to prefix with the number (e.g., 3 ducks)

quote,
unleakString,
jsonStringify,

@@ -562,0 +549,0 @@ pluralize,

{
"name": "@appium/support",
"version": "4.5.0",
"version": "5.0.1",
"description": "Support libs used across appium packages",

@@ -44,5 +44,5 @@ "keywords": [

"dependencies": {
"@appium/logger": "^1.3.0",
"@appium/logger": "^1.4.1",
"@appium/tsconfig": "^0.3.3",
"@appium/types": "^0.19.2",
"@appium/types": "^0.20.1",
"@colors/colors": "1.6.0",

@@ -93,3 +93,3 @@ "@types/archiver": "6.0.2",

"teen_process": "2.1.4",
"type-fest": "4.19.0",
"type-fest": "4.20.0",
"uuid": "9.0.1",

@@ -109,3 +109,3 @@ "which": "4.0.0",

},
"gitHead": "d4ce2a82d9b70af7cecf5174ec2521b0911f737d"
"gitHead": "192f1f379301539540aa7882f4b667a802cbc4a8"
}

@@ -54,3 +54,2 @@ # @appium/support

|image-util|Utilities to work with images. Use [sharp](https://github.com/lovell/sharp) under the hood.<br>:bangbang: Node >=18.17 is required to use these utilities|
|log-internal|Utilities needed for internal Appium log config assistance|
|logging|See [the logging section below](#logging)|

@@ -57,0 +56,0 @@ |mjpeg|Helpers needed to implement [MJPEG streaming](https://en.wikipedia.org/wiki/Motion_JPEG#Video_streaming)|

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

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