@socketsecurity/cli 0.4.2 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,2 +1,4 @@
1
1
  export * from './info/index.js'
2
2
  export * from './report/index.js'
3
+ export * from './npm/index.js'
4
+ export * from './npx/index.js'
@@ -4,6 +4,7 @@ import chalk from 'chalk'
4
4
  import meow from 'meow'
5
5
  import ora from 'ora'
6
6
 
7
+ import { outputFlags, validationFlags } from '../../flags/index.js'
7
8
  import { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api-helpers.js'
8
9
  import { ChalkOrMarkdown } from '../../utils/chalk-markdown.js'
9
10
  import { InputError } from '../../utils/errors.js'
@@ -47,17 +48,17 @@ export const info = {
47
48
  * @returns {void|CommandContext}
48
49
  */
49
50
  function setupCommand (name, description, argv, importMeta) {
51
+ const flags = {
52
+ ...outputFlags,
53
+ ...validationFlags,
54
+ }
55
+
50
56
  const cli = meow(`
51
57
  Usage
52
58
  $ ${name} <name>
53
59
 
54
60
  Options
55
- ${printFlagList({
56
- '--all': 'Include all issues',
57
- '--json': 'Output result as json',
58
- '--markdown': 'Output result as markdown',
59
- '--strict': 'Exits with an error code if any matching issues are found',
60
- }, 6)}
61
+ ${printFlagList(flags, 6)}
61
62
 
62
63
  Examples
63
64
  $ ${name} webtorrent
@@ -66,26 +67,7 @@ function setupCommand (name, description, argv, importMeta) {
66
67
  argv,
67
68
  description,
68
69
  importMeta,
69
- flags: {
70
- all: {
71
- type: 'boolean',
72
- default: false,
73
- },
74
- json: {
75
- type: 'boolean',
76
- alias: 'j',
77
- default: false,
78
- },
79
- markdown: {
80
- type: 'boolean',
81
- alias: 'm',
82
- default: false,
83
- },
84
- strict: {
85
- type: 'boolean',
86
- default: false,
87
- },
88
- }
70
+ flags
89
71
  })
90
72
 
91
73
  const {
@@ -147,7 +129,7 @@ async function fetchPackageData (pkgName, pkgVersion, { includeAllIssues, strict
147
129
  const result = await handleApiCall(socketSdk.getIssuesByNPMPackage(pkgName, pkgVersion), spinner, 'looking up package')
148
130
 
149
131
  if (result.success === false) {
150
- return handleUnsuccessfulApiResponse(result, spinner)
132
+ return handleUnsuccessfulApiResponse('getIssuesByNPMPackage', result, spinner)
151
133
  }
152
134
 
153
135
  // Conclude the status of the API call
@@ -0,0 +1,22 @@
1
+ import { spawn } from 'child_process'
2
+ import { fileURLToPath } from 'url'
3
+
4
+ const description = 'npm wrapper functionality'
5
+
6
+ /** @type {import('../../utils/meow-with-subcommands').CliSubcommand} */
7
+ export const npm = {
8
+ description,
9
+ run: async (argv, _importMeta, _ctx) => {
10
+ const wrapperPath = fileURLToPath(new URL('../../shadow/npm-cli.cjs', import.meta.url))
11
+ process.exitCode = 1
12
+ spawn(process.execPath, [wrapperPath, ...argv], {
13
+ stdio: 'inherit'
14
+ }).on('exit', (code, signal) => {
15
+ if (signal) {
16
+ process.kill(process.pid, signal)
17
+ } else if (code !== null) {
18
+ process.exit(code)
19
+ }
20
+ })
21
+ }
22
+ }
@@ -0,0 +1,22 @@
1
+ import { spawn } from 'child_process'
2
+ import { fileURLToPath } from 'url'
3
+
4
+ const description = 'npx wrapper functionality'
5
+
6
+ /** @type {import('../../utils/meow-with-subcommands').CliSubcommand} */
7
+ export const npx = {
8
+ description,
9
+ run: async (argv, _importMeta, _ctx) => {
10
+ const wrapperPath = fileURLToPath(new URL('../../shadow/npx-cli.cjs', import.meta.url))
11
+ process.exitCode = 1
12
+ spawn(process.execPath, [wrapperPath, ...argv], {
13
+ stdio: 'inherit'
14
+ }).on('exit', (code, signal) => {
15
+ if (signal) {
16
+ process.kill(process.pid, signal)
17
+ } else if (code !== null) {
18
+ process.exit(code)
19
+ }
20
+ })
21
+ }
22
+ }
@@ -9,9 +9,11 @@ import ora from 'ora'
9
9
  import { ErrorWithCause } from 'pony-cause'
10
10
 
11
11
  import { fetchReportData, formatReportDataOutput } from './view.js'
12
+ import { outputFlags, validationFlags } from '../../flags/index.js'
12
13
  import { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api-helpers.js'
13
14
  import { ChalkOrMarkdown, logSymbols } from '../../utils/chalk-markdown.js'
14
15
  import { InputError } from '../../utils/errors.js'
16
+ import { prepareFlags } from '../../utils/flags.js'
15
17
  import { printFlagList } from '../../utils/formatting.js'
16
18
  import { createDebugLogger } from '../../utils/misc.js'
17
19
  import { getPackageFiles } from '../../utils/path-resolve.js'
@@ -79,6 +81,28 @@ export const create = {
79
81
  * @returns {Promise<void|CommandContext>}
80
82
  */
81
83
  async function setupCommand (name, description, argv, importMeta) {
84
+ const flags = prepareFlags({
85
+ ...outputFlags,
86
+ ...validationFlags,
87
+ debug: {
88
+ type: 'boolean',
89
+ alias: 'd',
90
+ default: false,
91
+ description: 'Output debug information',
92
+ },
93
+ dryRun: {
94
+ type: 'boolean',
95
+ default: false,
96
+ description: 'Only output what will be done without actually doing it',
97
+ },
98
+ view: {
99
+ type: 'boolean',
100
+ alias: 'v',
101
+ default: false,
102
+ description: 'Will wait for and return the created report'
103
+ },
104
+ })
105
+
82
106
  const cli = meow(`
83
107
  Usage
84
108
  $ ${name} <paths-to-package-folders-and-files>
@@ -114,40 +138,7 @@ async function setupCommand (name, description, argv, importMeta) {
114
138
  argv,
115
139
  description,
116
140
  importMeta,
117
- flags: {
118
- all: {
119
- type: 'boolean',
120
- default: false,
121
- },
122
- debug: {
123
- type: 'boolean',
124
- alias: 'd',
125
- default: false,
126
- },
127
- dryRun: {
128
- type: 'boolean',
129
- default: false,
130
- },
131
- json: {
132
- type: 'boolean',
133
- alias: 'j',
134
- default: false,
135
- },
136
- markdown: {
137
- type: 'boolean',
138
- alias: 'm',
139
- default: false,
140
- },
141
- strict: {
142
- type: 'boolean',
143
- default: false,
144
- },
145
- view: {
146
- type: 'boolean',
147
- alias: 'v',
148
- default: false,
149
- },
150
- }
141
+ flags,
151
142
  })
152
143
 
153
144
  const {
@@ -224,7 +215,7 @@ async function createReport (packagePaths, { config, cwd, debugLog, dryRun }) {
224
215
  const result = await handleApiCall(apiCall, spinner, 'creating report')
225
216
 
226
217
  if (result.success === false) {
227
- return handleUnsuccessfulApiResponse(result, spinner)
218
+ return handleUnsuccessfulApiResponse('createReport', result, spinner)
228
219
  }
229
220
 
230
221
  // Conclude the status of the API call
@@ -4,6 +4,7 @@ import chalk from 'chalk'
4
4
  import meow from 'meow'
5
5
  import ora from 'ora'
6
6
 
7
+ import { outputFlags, validationFlags } from '../../flags/index.js'
7
8
  import { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api-helpers.js'
8
9
  import { ChalkOrMarkdown } from '../../utils/chalk-markdown.js'
9
10
  import { InputError } from '../../utils/errors.js'
@@ -28,7 +29,6 @@ export const view = {
28
29
 
29
30
  // Internal functions
30
31
 
31
- // TODO: Share more of the flag setup inbetween the commands
32
32
  /**
33
33
  * @typedef CommandContext
34
34
  * @property {boolean} includeAllIssues
@@ -46,17 +46,17 @@ export const view = {
46
46
  * @returns {void|CommandContext}
47
47
  */
48
48
  function setupCommand (name, description, argv, importMeta) {
49
+ const flags = {
50
+ ...outputFlags,
51
+ ...validationFlags,
52
+ }
53
+
49
54
  const cli = meow(`
50
55
  Usage
51
56
  $ ${name} <report-identifier>
52
57
 
53
58
  Options
54
- ${printFlagList({
55
- '--all': 'Include all issues',
56
- '--json': 'Output result as json',
57
- '--markdown': 'Output result as markdown',
58
- '--strict': 'Exits with an error code if report result is deemed unhealthy',
59
- }, 6)}
59
+ ${printFlagList(flags, 6)}
60
60
 
61
61
  Examples
62
62
  $ ${name} QXU8PmK7LfH608RAwfIKdbcHgwEd_ZeWJ9QEGv05FJUQ
@@ -64,26 +64,7 @@ function setupCommand (name, description, argv, importMeta) {
64
64
  argv,
65
65
  description,
66
66
  importMeta,
67
- flags: {
68
- all: {
69
- type: 'boolean',
70
- default: false,
71
- },
72
- json: {
73
- type: 'boolean',
74
- alias: 'j',
75
- default: false,
76
- },
77
- markdown: {
78
- type: 'boolean',
79
- alias: 'm',
80
- default: false,
81
- },
82
- strict: {
83
- type: 'boolean',
84
- default: false,
85
- },
86
- }
67
+ flags,
87
68
  })
88
69
 
89
70
  // Extract the input
@@ -134,7 +115,7 @@ export async function fetchReportData (reportId, { includeAllIssues, strict }) {
134
115
  const result = await handleApiCall(socketSdk.getReport(reportId), spinner, 'fetching report')
135
116
 
136
117
  if (result.success === false) {
137
- return handleUnsuccessfulApiResponse(result, spinner)
118
+ return handleUnsuccessfulApiResponse('getReport', result, spinner)
138
119
  }
139
120
 
140
121
  // Conclude the status of the API call
@@ -0,0 +1,2 @@
1
+ export { outputFlags } from './output.js'
2
+ export { validationFlags } from './validation.js'
@@ -0,0 +1,16 @@
1
+ import { prepareFlags } from '../utils/flags.js'
2
+
3
+ export const outputFlags = prepareFlags({
4
+ json: {
5
+ type: 'boolean',
6
+ alias: 'j',
7
+ default: false,
8
+ description: 'Output result as json',
9
+ },
10
+ markdown: {
11
+ type: 'boolean',
12
+ alias: 'm',
13
+ default: false,
14
+ description: 'Output result as markdown',
15
+ },
16
+ })
@@ -0,0 +1,14 @@
1
+ import { prepareFlags } from '../utils/flags.js'
2
+
3
+ export const validationFlags = prepareFlags({
4
+ all: {
5
+ type: 'boolean',
6
+ default: false,
7
+ description: 'Include all issues',
8
+ },
9
+ strict: {
10
+ type: 'boolean',
11
+ default: false,
12
+ description: 'Exits with an error code if any matching issues are found',
13
+ },
14
+ })
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env node
2
+ // THIS FILE USES .cjs to get around the extension-free entrypoint problem with ESM
3
+ 'use strict'
4
+ const { spawn } = require('child_process')
5
+ const { realpathSync } = require('fs')
6
+ const path = require('path')
7
+
8
+ const realFilename = realpathSync(__filename)
9
+ const realDirname = path.dirname(realFilename)
10
+
11
+ /**
12
+ */
13
+ async function main () {
14
+ const npmpath = await require('./link.cjs')(path.join(realDirname, 'bin'), 'npm')
15
+ process.exitCode = 1
16
+ const injectionpath = path.join(realDirname, 'npm-injection.cjs')
17
+ spawn(process.execPath, ['--require', injectionpath, npmpath, ...process.argv.slice(2)], {
18
+ stdio: 'inherit'
19
+ }).on('exit', (code, signal) => {
20
+ if (signal) {
21
+ process.kill(process.pid, signal)
22
+ } else if (code !== null) {
23
+ process.exit(code)
24
+ }
25
+ })
26
+ }
27
+ main()
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env node
2
+ // THIS FILE USES .cjs to get around the extension-free entrypoint problem with ESM
3
+ 'use strict'
4
+ const { spawn } = require('child_process')
5
+ const { realpathSync } = require('fs')
6
+ const path = require('path')
7
+
8
+ const realFilename = realpathSync(__filename)
9
+ const realDirname = path.dirname(realFilename)
10
+
11
+ /**
12
+ */
13
+ async function main () {
14
+ const npxpath = await require('./link.cjs')(path.join(realDirname, 'bin'), 'npx')
15
+ process.exitCode = 1
16
+ const injectionpath = path.join(realDirname, 'npm-injection.cjs')
17
+ spawn(process.execPath, ['--require', injectionpath, npxpath, ...process.argv.slice(2)], {
18
+ stdio: 'inherit'
19
+ }).on('exit', (code, signal) => {
20
+ if (signal) {
21
+ process.kill(process.pid, signal)
22
+ } else if (code !== null) {
23
+ process.exit(code)
24
+ }
25
+ })
26
+ }
27
+ main()
@@ -4,12 +4,13 @@ import { ErrorWithCause } from 'pony-cause'
4
4
  import { AuthError } from './errors.js'
5
5
 
6
6
  /**
7
- * @template T
7
+ * @template {import('@socketsecurity/sdk').SocketSdkOperations} T
8
+ * @param {T} _name
8
9
  * @param {import('@socketsecurity/sdk').SocketSdkErrorType<T>} result
9
10
  * @param {import('ora').Ora} spinner
10
11
  * @returns {void}
11
12
  */
12
- export function handleUnsuccessfulApiResponse (result, spinner) {
13
+ export function handleUnsuccessfulApiResponse (_name, result, spinner) {
13
14
  const resultError = 'error' in result && result.error && typeof result.error === 'object' ? result.error : {}
14
15
  const message = 'message' in resultError && typeof resultError.message === 'string' ? resultError.message : 'No error message returned'
15
16
 
@@ -0,0 +1,27 @@
1
+ /**
2
+ * @typedef FlagExtensions
3
+ * @property {string} description
4
+ */
5
+
6
+ /**
7
+ * @template {import('meow').FlagType} Type
8
+ * @template Default
9
+ * @template {boolean} [IsMultiple=false]
10
+ * @typedef {import('meow').Flag<Type, Default, IsMultiple> & FlagExtensions} Flag
11
+ */
12
+
13
+ /** @typedef {Flag<'string', string> | Flag<'string', string[], true>} StringFlag */
14
+ /** @typedef {Flag<'boolean', boolean> | Flag<'boolean', boolean[], true>} BooleanFlag */
15
+ /** @typedef {Flag<'number', number> | Flag<'number', number[], true>} NumberFlag */
16
+ /** @typedef {StringFlag | BooleanFlag | NumberFlag} AnyFlag */
17
+ /** @typedef {Record<string, AnyFlag>} AnyFlags */
18
+
19
+ /**
20
+ * @template {AnyFlags} Flags
21
+ * @param {Flags} flags
22
+ * @returns {Readonly<Flags>}
23
+ */
24
+ export function prepareFlags (flags) {
25
+ // As we can't do "satisfies AnyFlags" in JS yet (+ we add a bonus through Readonly<>)
26
+ return flags
27
+ }
@@ -1,12 +1,23 @@
1
1
  /** @typedef {string|{ description: string }} ListDescription */
2
2
 
3
+ /**
4
+ * @typedef HelpListOptions
5
+ * @property {string} [keyPrefix]
6
+ * @property {number} [padName]
7
+ */
8
+
3
9
  /**
4
10
  * @param {Record<string,ListDescription>} list
5
11
  * @param {number} indent
6
- * @param {number} padName
12
+ * @param {HelpListOptions} options
7
13
  * @returns {string}
8
14
  */
9
- export function printHelpList (list, indent, padName = 18) {
15
+ export function printHelpList (list, indent, options = {}) {
16
+ const {
17
+ keyPrefix = '',
18
+ padName = 18,
19
+ } = options
20
+
10
21
  const names = Object.keys(list).sort()
11
22
 
12
23
  let result = ''
@@ -15,22 +26,22 @@ export function printHelpList (list, indent, padName = 18) {
15
26
  const rawDescription = list[name]
16
27
  const description = (typeof rawDescription === 'object' ? rawDescription.description : rawDescription) || ''
17
28
 
18
- result += ''.padEnd(indent) + name.padEnd(padName) + description + '\n'
29
+ result += ''.padEnd(indent) + (keyPrefix + name).padEnd(padName) + description + '\n'
19
30
  }
20
31
 
21
32
  return result.trim()
22
33
  }
23
34
 
24
35
  /**
25
- * @param {Record<string,ListDescription>} list
36
+ * @param {Record<string, ListDescription>} list
26
37
  * @param {number} indent
27
- * @param {number} padName
38
+ * @param {HelpListOptions} options
28
39
  * @returns {string}
29
40
  */
30
- export function printFlagList (list, indent, padName = 18) {
41
+ export function printFlagList (list, indent, options = {}) {
31
42
  return printHelpList({
32
- '--help': 'Print this help and exits.',
33
- '--version': 'Prints current version and exits.',
43
+ 'help': 'Print this help and exits.',
44
+ 'version': 'Prints current version and exits.',
34
45
  ...list,
35
- }, indent, padName)
46
+ }, indent, { keyPrefix: '--', ...options })
36
47
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@socketsecurity/cli",
3
- "version": "0.4.2",
3
+ "version": "0.5.1",
4
4
  "description": "CLI tool for Socket.dev",
5
5
  "homepage": "http://github.com/SocketDev/socket-cli-js",
6
6
  "repository": {
@@ -15,18 +15,21 @@
15
15
  },
16
16
  "license": "MIT",
17
17
  "engines": {
18
- "node": "^14.18.0 || >=16.0.0"
18
+ "node": "^14.18.0 || ^16.13.0 || >=18.0.0"
19
19
  },
20
20
  "type": "module",
21
21
  "bin": {
22
- "socket": "cli.js"
22
+ "socket": "cli.js",
23
+ "socket-npm": "lib/shadow/npm-cli.cjs",
24
+ "socket-npx": "lib/shadow/npx-cli.cjs"
23
25
  },
24
26
  "files": [
25
27
  "cli.js",
26
28
  "lib/**/*.js"
27
29
  ],
28
30
  "scripts": {
29
- "check:dependency-check": "dependency-check '*.js' 'test/**/*.js' --no-dev",
31
+ "echo": "echo $PATH",
32
+ "check:dependency-check": "dependency-check '*.js' 'lib/shadow/*.cjs' '*.mjs' 'test/**/*.js' --no-dev",
30
33
  "check:installed-check": "installed-check -i eslint-plugin-jsdoc",
31
34
  "check:lint": "eslint --report-unused-disable-directives .",
32
35
  "check:tsc": "tsc",
@@ -45,8 +48,11 @@
45
48
  "@types/mocha": "^10.0.0",
46
49
  "@types/mock-fs": "^4.13.1",
47
50
  "@types/node": "^14.18.31",
51
+ "@types/npm": "^7.19.0",
52
+ "@types/npmcli__arborist": "^5.6.1",
48
53
  "@types/prompts": "^2.4.1",
49
54
  "@types/update-notifier": "^6.0.2",
55
+ "@types/which": "^2.0.2",
50
56
  "@typescript-eslint/eslint-plugin": "^5.51.0",
51
57
  "@typescript-eslint/parser": "^5.51.0",
52
58
  "c8": "^7.12.0",
@@ -76,7 +82,7 @@
76
82
  "dependencies": {
77
83
  "@apideck/better-ajv-errors": "^0.3.6",
78
84
  "@socketsecurity/config": "^2.0.0",
79
- "@socketsecurity/sdk": "^0.5.2",
85
+ "@socketsecurity/sdk": "^0.5.4",
80
86
  "chalk": "^5.1.2",
81
87
  "globby": "^13.1.3",
82
88
  "hpagent": "^1.2.0",
@@ -89,6 +95,7 @@
89
95
  "pony-cause": "^2.1.8",
90
96
  "prompts": "^2.4.2",
91
97
  "terminal-link": "^3.0.0",
92
- "update-notifier": "^6.0.2"
98
+ "update-notifier": "^6.0.2",
99
+ "which": "^3.0.0"
93
100
  }
94
101
  }