Socket
Socket
Sign inDemoInstall

update-check

Package Overview
Dependencies
Maintainers
6
Versions
12
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

update-check - npm Package Compare versions

Comparing version 1.0.0 to 1.1.0

238

index.js

@@ -0,2 +1,4 @@

// Native
const {get} = require('https');
const {URL} = require('url');
const {join} = require('path');

@@ -7,104 +9,192 @@ const fs = require('fs');

// Packages
const registryUrl = require('registry-url');
const writeFile = promisify(fs.writeFile);
const mkdir = promisify(fs.mkdir);
const readFile = promisify(fs.readFile);
const compareVersions = (a, b) => a.localeCompare(b, 'en-US', { numeric: true });
const compareVersions = (a, b) => a.localeCompare(b, 'en-US', {numeric: true});
const encode = value => encodeURIComponent(value).replace(/^%40/, '@');
const shouldCheck = async (name, interval) => {
const rootDir = tmpdir();
const subDir = join(rootDir, `update-check`);
const getFile = async (details, distTag) => {
const rootDir = tmpdir();
const subDir = join(rootDir, 'update-check');
if (!fs.existsSync(subDir)) {
mkdir(subDir);
}
if (!fs.existsSync(subDir)) {
mkdir(subDir);
}
const file = join(subDir, `${name}.json`);
const time = Date.now()
let name = `${details.name}-${distTag}.json`;
if (fs.existsSync(file)) {
const content = await readFile(file, 'utf8');
const {lastCheck} = JSON.parse(content);
const nextCheck = lastCheck + interval;
if (details.scope) {
name = `${details.scope}-${name}`;
}
// As long as the time of the next check is in
// the future, we don't need to run it yet.
if (nextCheck > time) {
return false;
}
}
return join(subDir, name);
};
const content = JSON.stringify({
lastCheck: time
});
const evaluateCache = async (file, time, interval) => {
if (fs.existsSync(file)) {
const content = await readFile(file, 'utf8');
const {lastUpdate, latest} = JSON.parse(content);
const nextCheck = lastUpdate + interval;
await writeFile(file, content, 'utf8');
return true;
}
// As long as the time of the next check is in
// the future, we don't need to run it yet
if (nextCheck > time) {
return {
shouldCheck: false,
latest
};
}
}
const getMostRecent = async (name, distTag) => {
const url = `https://registry.npmjs.org/${encode(name)}/${encode(distTag)}`;
return {
shouldCheck: true,
latest: null
};
};
return new Promise((resolve, reject) => get(url, response => {
const {statusCode, headers} = response;
const contentType = headers['content-type'];
const updateCache = async (file, latest, lastUpdate) => {
const content = JSON.stringify({
latest,
lastUpdate
});
let error = null;
await writeFile(file, content, 'utf8');
};
if (statusCode !== 200) {
error = new Error(`Request failed with code ${statusCode}`);
} else if (!/^application\/json/.test(contentType)) {
error = new Error(`Expected application/json but received ${contentType}`);
}
const loadPackage = (url, authInfo) => new Promise((resolve, reject) => {
const options = {
host: url.hostname,
path: url.pathname,
headers: {
accept: 'application/vnd.npm.install-v1+json; q=1.0, application/json; q=0.8, */*'
}
};
if (error) {
reject(error.message);
if (authInfo) {
options.headers.authorization = `${authInfo.type} ${authInfo.token}`;
}
// Consume response data to free up RAM
response.resume();
return;
}
get(options, response => {
const {statusCode} = response;
let rawData = '';
if (statusCode !== 200) {
const error = new Error(`Request failed with code ${statusCode}`);
error.code = statusCode;
response.setEncoding('utf8');
response.on('data', chunk => { rawData += chunk; });
reject(error);
response.on('end', () => {
try {
const parsedData = JSON.parse(rawData);
resolve(parsedData);
} catch (e) {
reject(e.message);
}
});
}).on('error', reject));
}
// Consume response data to free up RAM
response.resume();
return;
}
let rawData = '';
response.setEncoding('utf8');
response.on('data', chunk => {
rawData += chunk;
});
response.on('end', () => {
try {
const parsedData = JSON.parse(rawData);
resolve(parsedData);
} catch (e) {
reject(e);
}
});
}).on('error', reject);
});
const getMostRecent = async ({full, scope}, distTag) => {
const regURL = registryUrl(scope);
// For scoped packages, getting a certain dist tag is not supported
const url = new URL(scope ? full : `${full}/${encode(distTag)}`, regURL);
let version = null;
try {
({version} = await loadPackage(url));
} catch (err) {
// We need to cover:
// 401 or 403 for when we don't have access
// 404 when the package is hidden
if (err.code && String(err.code).startsWith(4)) {
// We only want to load this package for when we
// really need to use the token
const registryAuthToken = require('registry-auth-token');
const authInfo = registryAuthToken(regURL, {recursive: true});
({version} = loadPackage(url, authInfo));
}
throw err;
}
return version;
};
const defaultConfig = {
interval: 3600000,
distTag: 'latest'
}
interval: 3600000,
distTag: 'latest'
};
const getDetails = name => {
const spec = {
full: encode(name)
};
if (name.includes('/')) {
const parts = name.split('/');
spec.scope = parts[0];
spec.name = parts[1];
} else {
spec.scope = null;
spec.name = name;
}
return spec;
};
module.exports = async (pkg, config) => {
if (typeof pkg !== 'object') {
throw new Error('The first parameter should be your package.json file content');
}
if (typeof pkg !== 'object') {
throw new Error('The first parameter should be your package.json file content');
}
const {distTag, interval} = Object.assign({}, defaultConfig, config);
const check = await shouldCheck(pkg.name, interval);
const details = getDetails(pkg.name);
if (check === false) {
return null;
}
console.log('masklol')
if (details.scope && config.distTag) {
throw new Error('For scoped packages, the npm registry does not support getting a certain tag');
}
const mostRecent = await getMostRecent(pkg.name, distTag);
const comparision = compareVersions(pkg.version, mostRecent.version);
const time = Date.now();
const {distTag, interval} = Object.assign({}, defaultConfig, config);
const file = await getFile(details, distTag);
if (comparision === -1) {
return mostRecent;
}
let latest = null;
let shouldCheck = true;
return null;
}
({shouldCheck, latest} = await evaluateCache(file, time, interval));
if (shouldCheck) {
latest = await getMostRecent(details, distTag);
// If we pulled an update, we need to update the cache
await updateCache(file, latest, time);
}
const comparision = compareVersions(pkg.version, latest);
if (comparision === -1) {
return {
latest,
fromCache: !shouldCheck
};
}
return null;
};
{
"name": "update-check",
"version": "1.0.0",
"version": "1.1.0",
"description": "Minimalistic update notifications for command line interfaces",

@@ -32,3 +32,7 @@ "main": "./index.js",

"pre-commit": "lint-staged"
},
"dependencies": {
"registry-auth-token": "3.3.2",
"registry-url": "3.1.0"
}
}

@@ -7,4 +7,2 @@ # update-check

To reduce the bootup time to a minimum, no dependencies and the tiniest amount of code possible is used.
## Usage

@@ -33,3 +31,3 @@

if (update) {
console.log(`The latest version is ${update.version}. Please update!`)
console.log(`The latest version is ${update.latest}. Please update!`)
}

@@ -54,3 +52,3 @@ ```

if (update) {
console.log(`The latest version is ${update.version}. Please update!`)
console.log(`The latest version is ${update.latest}. Please update!`)
}

@@ -57,0 +55,0 @@ ```

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