New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

losant-cli

Package Overview
Dependencies
Maintainers
4
Versions
12
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

losant-cli - npm Package Compare versions

Comparing version 1.3.1 to 1.3.2

8

commands/experience/version.js

@@ -10,2 +10,8 @@ const helpLines = `

$ losant experience version v1.0.1 -d "updated home page"
Create a new experience version with a description
$ losant experience version v1.0.1 -d "updated home page"
Create a new experience version associated with specific domain IDs or names
$ losant experience version v1.0.1 -o "653981225c401f279a221eaa,653981225c401f279a221eab,*.foo.bar"
Create a new experience version associated with specific slug IDs or names
$ losant experience version v1.0.1 -s "653981225c401f279a221ebb,mypersonalslug,653981225c401f279a221ebc"
`;

@@ -19,3 +25,5 @@

.option('-d, --description <description>', 'a description to attach to this version')
.option('-o --domainIds <domainIds>', 'a comma separated list of domain IDs or names')
.option('-s --slugIds <slugIds>', 'a comma separated list of slug IDs or names')
.action(require('../../lib/experience-version'));
};

2

commands/login/index.js

@@ -34,3 +34,3 @@ const error = require('error/typed');

{ type: 'password', name: 'password', message: 'Enter Losant password:' },
{ type: 'input', name: 'twoFactorCode', message: 'Enter two-factor auth code (if applicable):' }
{ type: 'input', name: 'twoFactorCode', message: 'Enter multi-factor auth code (if applicable):' }
]);

@@ -37,0 +37,0 @@ if (!email || !password) {

@@ -12,8 +12,13 @@ const p = require('commander');

.description('Create a User API Token in your Losant account, then set it here to configure the command line tool.')
.argument('[token]', 'The API token to set (if not passed, user will be prompted)')
.showHelpAfterError()
.action(async () => {
// prompt the user to input a token
const { token: apiToken } = await inquirer.prompt([
{ type: 'input', name: 'token', message: 'Enter a Losant User API token:' }
]);
.action(async (token) => {
let apiToken = token;
if (!apiToken) {
// prompt the user to input a token
const res = await inquirer.prompt([
{ type: 'input', name: 'token', message: 'Enter a Losant User API token:' }
]);
apiToken = res.token;
}
try {

@@ -20,0 +25,0 @@ const api = await getApi({ apiToken });

const inquirer = require('inquirer');
const {
capitalize, keys, compact
capitalize, keys, compact, map, trim, isNotNil, uniq
} = require('omnibelt');

@@ -12,2 +12,3 @@ const { loadConfig, logResult, logError, log } = require('./utils');

const mapTrim = map(trim);

@@ -84,2 +85,46 @@ const listVersions = async (api, applicationId, filter, endpointDomain) => {

const promptForSlugsAndDomains = async (currentDomains, currentSlugs) => {
const domainChoiceMappings = choices('domainName', 'version', currentDomains);
const domainChoices = keys(domainChoiceMappings);
const slugChoiceMappings = choices('slug', 'version', currentSlugs);
const slugChoices = keys(slugChoiceMappings);
const questions = [];
if (domainChoices.length) {
questions.push({ type: 'checkbox', name: 'domains', message: 'Select Experience Domains to point at this version', choices: domainChoices });
}
if (slugChoices.length) {
questions.push({ type: 'checkbox', name: 'slugs', message: 'Select Experience Slugs to point at this version', choices: slugChoices });
}
let domainIds = [], slugIds = [];
if (questions.length) {
const { domains, slugs } = await inquirer.prompt(questions) || {};
if (domains?.length) {
domainIds = domains.map((domain) => domainChoiceMappings[domain]);
}
if (slugs?.length) {
slugIds = slugs.map((slug) => slugChoiceMappings[slug]);
}
}
return { domainIds, slugIds };
};
const createIdAndNameMap = (data, fieldName) => {
const idOrNameMap = new Map();
data.forEach((d) => {
idOrNameMap.set(d[fieldName], d.id);
idOrNameMap.set(d.id, d.id);
});
return idOrNameMap;
};
const givenNamesToIds = (given, nameOrIdMap, errMsgs, type) => {
return given.map((idOrName) => {
if (!nameOrIdMap.has(idOrName)) {
errMsgs.push(`${type} ${idOrName} was not found in the current list.`);
return;
}
return nameOrIdMap.get(idOrName);
}).filter(isNotNil);
};
module.exports = async (version, opts = {}) => {

@@ -91,25 +136,26 @@ const { apiToken, applicationId, api, endpointDomain } = await loadConfig();

} else {
const domainChoiceMappings = choices('domainName', 'version', await getExperiencePart(api, 'domain', { applicationId }));
const domainChoices = keys(domainChoiceMappings);
const slugChoiceMappings = choices('slug', 'version', await getExperiencePart(api, 'slug', { applicationId }));
const slugChoices = keys(slugChoiceMappings);
const questions = [];
if (domainChoices.length) {
questions.push({ type: 'checkbox', name: 'domains', message: 'Select Experience Domains to point at this version', choices: domainChoices });
}
if (slugChoices.length) {
questions.push({ type: 'checkbox', name: 'slugs', message: 'Select Experience Slugs to point at this version', choices: slugChoices });
}
let domainIds, slugIds;
if (questions.length) {
const { domains, slugs } = await inquirer.prompt(questions) || {};
if (domains?.length) {
domainIds = domains.map((domain) => domainChoiceMappings[domain]);
const currentDomains = await getExperiencePart(api, 'domain', { applicationId });
const currentSlugs = await getExperiencePart(api, 'slug', { applicationId });
const versionInfo = { description: opts.description };
if (isNotNil(opts.slugIds) || isNotNil(opts.domainIds)) {
const domainMap = createIdAndNameMap(currentDomains, 'domainName');
const slugMap = createIdAndNameMap(currentSlugs, 'slug');
const givenSlugs = opts.slugIds?.length ? mapTrim(opts.slugIds.split(',')) : [];
const givenDomains = opts.domainIds?.length ? mapTrim(opts.domainIds.split(',')) : [];
const errorMsgs = [];
versionInfo.slugIds = uniq(givenNamesToIds(givenSlugs, slugMap, errorMsgs, 'Slug'));
versionInfo.domainIds = uniq(givenNamesToIds(givenDomains, domainMap, errorMsgs, 'Domain'));
if (errorMsgs.length) {
errorMsgs.forEach((msg) => {
logError(msg);
});
return;
}
if (slugs?.length) {
slugIds = slugs.map((slug) => slugChoiceMappings[slug]);
}
} else {
const selected = await promptForSlugsAndDomains(currentDomains, currentSlugs);
versionInfo.domainIds = selected.domainIds;
versionInfo.slugIds = selected.slugIds;
}
await createVersion(api, applicationId, version, { description: opts.description, domainIds, slugIds });
await createVersion(api, applicationId, version, versionInfo);
}
};

@@ -46,3 +46,3 @@ const paginateRequest = require('./paginate-request');

if (!apiToken || !applicationId) { return; }
let meta = await loadLocalMeta(commandType) || {};
let meta = await loadLocalMeta(commandType);
if (opts.reset && !opts.dryRun) {

@@ -49,0 +49,0 @@ // currently this can only be used by experience

@@ -18,3 +18,2 @@ const paginateRequest = require('./paginate-request');

const { isEmpty, mergeRight, values, keys } = require('omnibelt');
const { buildMetaDataObj } = require('./meta-data-helpers');

@@ -29,3 +28,3 @@ const getUploader = ({

const api = config.api;
const meta = await loadLocalMeta(commandType) || {};
const meta = await loadLocalMeta(commandType);
let items;

@@ -68,30 +67,37 @@ try {

}
const uploadResults = await allSettledSerial(async (stat) => {
const statsByType = {
unmodified: [],
deleted: [],
potentialUpdates: []
};
values(localStatusByFile).forEach((stat) => {
if (statsByType[stat.status]) {
return statsByType[stat.status].push(stat);
}
statsByType.potentialUpdates.push(stat);
});
statsByType.unmodified.forEach((stat) => {
logProcessing(stat.file);
if (stat.status === 'unmodified') {
return logResult('unmodified', stat.file);
}
if (stat.status === 'deleted') {
if (!opts.dryRun) {
try {
return logResult('unmodified', stat.file);
});
const deleteResults = await allSettledSerial(async (stat) => {
logProcessing(stat.file);
const remoteStatus = remoteStatusByFile[stat.file];
if (!opts.dryRun) {
try {
if (remoteStatus.status !== 'deleted') {
await api[singular(apiType)].delete(await getDeleteQuery(stat, config));
} catch (err) {
const message = `An error ocurred when trying to remove ${stat.file} with the message ${err.message}`;
return logError(message);
}
delete meta[stat.file];
} catch (err) {
const message = `An error ocurred when trying to remove ${stat.file} with the message ${err.message}`;
return logError(message);
}
return logResult('deleted', stat.file, 'yellow');
delete meta[stat.file];
}
const remoteStatus = remoteStatusByFile[stat.file] || {};
if (
(stat === 'added' && remoteStatus === 'added') ||
(stat === 'modified' && remoteStatus === 'modified')
) {
if (stat.localMd5 === remoteStatus.remoteMd5) {
return logResult('deleted', stat.file, 'yellow');
}, statsByType.deleted);
meta[stat.file] = buildMetaDataObj({ remoteStatus, localStatus: stat });
return logResult('uploaded', stat.file, 'green');
}
}
const uploadResults = await allSettledSerial(async (stat) => {
logProcessing(stat.file);
if (!opts.dryRun) {

@@ -125,4 +131,4 @@ let result;

return logResult('uploaded', stat.file, 'green');
}, values(localStatusByFile));
uploadResults.forEach((result) => {
}, statsByType.potentialUpdates);
[ ...deleteResults, ...uploadResults ].forEach((result) => {
// this should only occur on unhandled rejections any api error should have already logged and resolved the promise

@@ -129,0 +135,0 @@ if (result.state !== 'fulfilled') {

@@ -238,3 +238,11 @@ const getApi = require('./get-api');

const mapFile = path.resolve(dir, `${type}.yml`);
return (await pathExists(mapFile)) ? yaml.safeLoad(await readFile(mapFile)) : null;
if (await pathExists(mapFile)) {
return yaml.safeLoad(await readFile(mapFile));
}
if (process.env.NODE_ENV !== 'test') {
utils.logError(`Could not find meta data file ${type}.yml, please re-configure your directory.`);
process.exit(1);
} else {
return {};
}
},

@@ -267,3 +275,3 @@

const statusByFile = {};
const meta = await utils.loadLocalMeta(type) || {};
const meta = await utils.loadLocalMeta(type);
const metaFiles = new Set(keys(meta));

@@ -338,3 +346,3 @@ const dirPattern = path.resolve(path.join(dir, globPattern));

const statusByFile = {};
const meta = await utils.loadLocalMeta(type) || {};
const meta = await utils.loadLocalMeta(type);
const metaByFile = new Map();

@@ -417,4 +425,4 @@ const metaFiles = new Set();

if (
(localStatus === 'added' && (remoteStatus === 'removed' || remoteStatus === 'unmodified' || remoteStatus === 'modified')) ||
((localStatus === 'removed' || localStatus === 'unmodified' || localStatus === 'modified') && (remoteStatus === 'added' || remoteStatus === 'missing')) ||
(localStatus === 'added' && (remoteStatus === 'deleted' || remoteStatus === 'unmodified' || remoteStatus === 'modified')) ||
((localStatus === 'deleted' || localStatus === 'unmodified' || localStatus === 'modified') && (remoteStatus === 'added' || remoteStatus === 'missing')) ||
(localStatus === 'missing' && remoteStatus !== 'added')

@@ -463,3 +471,2 @@ ) {

allFiles.forEach((file) => {
// console.log(`${file} matching dirPattern ${dirPattern} :: ${!minimatch(file, dirPattern)}`);
if (!minimatch(file.split(path.sep).join(path.posix.sep), dirPattern.split(path.sep).join(path.posix.sep))) {

@@ -466,0 +473,0 @@ delete remoteStatusByFile[file];

{
"name": "losant-cli",
"version": "1.3.1",
"version": "1.3.2",
"description": "Losant Command Line Interface",

@@ -20,3 +20,3 @@ "license": "MIT",

"engines": {
"node": ">=14"
"node": ">=16"
},

@@ -38,3 +38,3 @@ "scripts": {

"@rjhilgefort/export-dir": "^2.0.0",
"axios": "^1.4.0",
"axios": "^1.5.1",
"chalk": "^4.1.1",

@@ -44,3 +44,3 @@ "chokidar": "^3.5.3",

"commander": "^10.0.1",
"csv-stringify": "^6.3.3",
"csv-stringify": "^6.4.4",
"death": "^1.1.0",

@@ -51,15 +51,15 @@ "error": "^7.2.0",

"fs-extra": "^11.1.1",
"glob": "^10.2.2",
"glob": "10.2.2",
"inquirer": "^8.2.3",
"js-yaml": "^3.14.1",
"jsonwebtoken": "^9.0.0",
"jsonwebtoken": "^9.0.2",
"lodash-template": "^1.0.0",
"losant-rest": "2.14.0",
"losant-rest": "2.14.1",
"mime-types": "^2.1.30",
"minimatch": "^9.0.0",
"minimatch": "^9.0.3",
"moment": "^2.29.1",
"omnibelt": "^3.1.1",
"omnibelt": "^3.1.2",
"pad": "^3.2.0",
"proper-lockfile": "^4.1.2",
"rollbar": "^2.21.1",
"rollbar": "^2.26.2",
"sanitize-filename": "^1.6.2",

@@ -72,5 +72,5 @@ "single-line-log": "^1.1.2",

"husky": "^8.0.3",
"lint-staged": "^13.2.2",
"lint-staged": "13.2.2",
"mocha": "^10.2.0",
"nock": "^13.3.1",
"nock": "^13.3.6",
"should": "^13.2.3",

@@ -77,0 +77,0 @@ "sinon": "^15.0.4"

@@ -35,4 +35,3 @@ # Losant CLI

Before you run any other commands, you must run `losant login` to authenticate with your Losant account. This command checks to see if your account is linked to a Single Sign-On (SSO) provider. If so, the command will prompt for a User Token; otherwise it will prompt for the password (and optionally your two-factor code) for your Losant account. After either is given successfully, the command will store
the authentication token on your computer. With this command, you can optionally set `LOSANT_API_URL` as an environment variable; e.g. `LOSANT_API_URL=<api.private.install> losant login`. By default the CLI will use `https://api.losant.com` as the API URL. This will allow you to log in across Losant installations. If you are logged in to multiple Losant installations when you configure a directory, you will be asked which API token to use to access the application. From then on, any request for that application will use the same API URL.
Before you run any other commands, you must run `losant login` to authenticate with your Losant account. This command checks to see if your account is linked to a Single Sign-On (SSO) provider. If so, the command will prompt for a User Token; otherwise it will prompt for the password (and optionally your multi-factor code) for your Losant account. After either is given successfully, the command will store the authentication token on your computer. With this command, you can optionally set `LOSANT_API_URL` as an environment variable; e.g. `LOSANT_API_URL=<api.private.install> losant login`. By default the CLI will use `https://api.losant.com` as the API URL. This will allow you to log in across Losant installations. If you are logged in to multiple Losant installations when you configure a directory, you will be asked which API token to use to access the application. From then on, any request for that application will use the same API URL.

@@ -39,0 +38,0 @@ ### Set-token

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