@kearisp/cli
Advanced tools
Comparing version 1.0.7 to 2.0.0
@@ -1,3 +0,2 @@ | ||
import { Cli } from "./makes"; | ||
export * from "./makes"; | ||
export * from "./types"; | ||
export { Cli }; |
@@ -17,5 +17,3 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.Cli = void 0; | ||
const makes_1 = require("./makes"); | ||
Object.defineProperty(exports, "Cli", { enumerable: true, get: function () { return makes_1.Cli; } }); | ||
__exportStar(require("./makes"), exports); | ||
__exportStar(require("./types"), exports); |
@@ -1,10 +0,12 @@ | ||
import { Logger } from "../types"; | ||
import { Command } from "./Command"; | ||
declare class Cli extends Command { | ||
protected scriptPath: string; | ||
constructor(logger?: Logger); | ||
declare class Cli { | ||
protected name: string; | ||
protected commands: Command[]; | ||
completionScript(): string; | ||
protected parseCommand(command: string): string[]; | ||
run(argv: string[]): Promise<any>; | ||
protected parseCommand(command: string, index: number): string[]; | ||
command(name: string): Command; | ||
protected process(parts: string[]): Promise<string | void>; | ||
protected complete(parts: string[]): Promise<string[]>; | ||
run(argv: string[]): Promise<string | void>; | ||
} | ||
export { Cli }; |
@@ -6,12 +6,14 @@ "use strict"; | ||
const Path = require("path"); | ||
const InvalidError_1 = require("../errors/InvalidError"); | ||
const Command_1 = require("./Command"); | ||
const Logger_1 = require("./Logger"); | ||
const utils_1 = require("../utils"); | ||
class Cli extends Command_1.Command { | ||
constructor(logger) { | ||
super("", logger); | ||
class Cli { | ||
constructor() { | ||
this.commands = []; | ||
} | ||
completionScript() { | ||
return (0, utils_1.generateCompletion)(this._command); | ||
return (0, utils_1.generateCompletion)(this.name); | ||
} | ||
parseCommand(command) { | ||
parseCommand(command, index) { | ||
const parts = []; | ||
@@ -48,55 +50,76 @@ let current = "", quotes = "", escape = 0; | ||
} | ||
return parts; | ||
const [, ...args] = parts; | ||
return args.length < index ? [...args, ""] : args; | ||
} | ||
async run(argv) { | ||
const [, scriptPath, ...rest] = argv; | ||
this.scriptPath = scriptPath; | ||
this._command = Path.basename(scriptPath); | ||
const parts = [this._command, ...rest]; | ||
if (parts.indexOf("--completion") > -1 || parts.indexOf("--compbash") > -1 || parts.indexOf("--compgen") > -1) { | ||
const completion = new Command_1.Command(`${this._command} <index> <prev> <command>`, this._logger); | ||
const promise = new Promise((resolve) => { | ||
completion | ||
.option("completion", { | ||
type: "boolean" | ||
}) | ||
.option("compbash", { | ||
type: "boolean" | ||
}) | ||
.option("compgen", { | ||
type: "boolean" | ||
}) | ||
.action((options, index, prev, command) => { | ||
resolve([index, command]); | ||
return "1"; | ||
}); | ||
}); | ||
command(name) { | ||
let command = this.commands.find((command) => { | ||
return command.name === name; | ||
}); | ||
if (!command) { | ||
command = new Command_1.Command(name); | ||
this.commands.push(command); | ||
} | ||
return command; | ||
} | ||
async process(parts) { | ||
for (const command of this.commands) { | ||
try { | ||
const res = await completion.process(parts); | ||
if (res === "1") { | ||
const [index, command] = await promise; | ||
const p = this.parseCommand(command); | ||
const res = await this.complete(p.length - 1 < parseInt(index) ? [...p, ""] : p); | ||
return res | ||
.map((predict) => { | ||
if (/\s/.test(predict)) { | ||
return predict.replace(/\s/g, "\\ "); | ||
} | ||
return predict; | ||
}) | ||
.join(OS.EOL); | ||
const input = command.parse(parts); | ||
return command.emit(this.name, input); | ||
} | ||
catch (err) { | ||
if (!(err instanceof InvalidError_1.InvalidError)) { | ||
Logger_1.Logger.error(err.message); | ||
} | ||
} | ||
} | ||
throw new Error("Invalid command"); | ||
} | ||
async complete(parts) { | ||
let predicts = []; | ||
for (const command of this.commands) { | ||
try { | ||
const res = await command.complete(parts); | ||
predicts = [ | ||
...predicts, | ||
...res | ||
]; | ||
} | ||
catch (err) { | ||
this.error(err.message); | ||
} | ||
return ""; | ||
} | ||
const res = await this.process(parts); | ||
if (res === null) { | ||
throw new Error("Command not found"); | ||
} | ||
return res; | ||
return predicts; | ||
} | ||
async run(argv) { | ||
const [, scriptPath, ...parts] = argv; | ||
this.name = Path.basename(scriptPath); | ||
this.command(`complete <index> <prev> <command>`) | ||
.help({ | ||
description: "Generate completion script", | ||
disabled: true | ||
}) | ||
.option("compbash", { | ||
type: "boolean" | ||
}) | ||
.option("compgen", { | ||
type: "boolean" | ||
}) | ||
.option("compzsh", { | ||
type: "boolean" | ||
}) | ||
.action(async (input) => { | ||
const parts = this.parseCommand(input.argument("command"), parseInt(input.argument("index"))); | ||
const res = await this.complete(parts); | ||
return res | ||
.map((predict) => { | ||
if (/\s/.test(predict)) { | ||
return predict.replace(/\s/g, "\\ "); | ||
} | ||
return predict; | ||
}) | ||
.join(OS.EOL); | ||
}); | ||
return this.process(parts); | ||
} | ||
} | ||
exports.Cli = Cli; |
@@ -1,2 +0,3 @@ | ||
import { Logger, Option } from "../types"; | ||
import { Option } from "../types"; | ||
import { CommandInput } from "./CommandInput"; | ||
type HelpParams = false | { | ||
@@ -12,46 +13,25 @@ disabled?: boolean; | ||
isOption?: boolean; | ||
action: (options: any, ...args: (string | string[])[]) => string[] | Promise<string[]>; | ||
action: (input: CommandInput) => string[] | Promise<string[]>; | ||
}; | ||
type Action = (options: any, ...args: (string | string[])[]) => void | string | Promise<void | string>; | ||
declare class Command { | ||
type Action = (input: CommandInput) => void | string | Promise<void | string>; | ||
export declare class Command { | ||
protected _command: string; | ||
protected _help: boolean; | ||
protected _description: string; | ||
protected _commands: Command[]; | ||
protected _options: Option[]; | ||
protected _action: Action; | ||
protected _completions: Completion[]; | ||
protected _logger?: Logger; | ||
constructor(command: string, logger?: Logger); | ||
getName(): string; | ||
command(name: string): Command; | ||
constructor(command: string); | ||
get name(): string; | ||
option(name: string, params: OptionParams): this; | ||
protected getOptionSettings(name?: string, alias?: string): Option; | ||
help(params: HelpParams): this; | ||
completion(name: Completion["name"], handle: Completion["action"]): this; | ||
action(action: Action): void; | ||
parse(parts: string[], partial?: boolean): { | ||
args: (string | string[])[]; | ||
options: { | ||
[name: string]: string | number | boolean; | ||
}; | ||
command: string; | ||
part: string; | ||
parts?: undefined; | ||
} | { | ||
args: (string | string[])[]; | ||
options: { | ||
[name: string]: string | number | boolean; | ||
}; | ||
parts: string[]; | ||
command?: undefined; | ||
part?: undefined; | ||
}; | ||
process(parts: string[], parentOptions?: any, parentArgs?: (string | string[])[]): any; | ||
predictCommand(command: string, part: string, options?: any, args?: (string | string[])[]): Promise<string[]>; | ||
action(action: Action): this; | ||
parse(parts: string[]): CommandInput; | ||
emit(name: string, input: CommandInput): Promise<string | void>; | ||
protected predictCommand(command: string, part: string, input: CommandInput): Promise<string[]>; | ||
protected predictOption(part: string): Promise<string[]>; | ||
complete(parts: string[]): Promise<string[]>; | ||
protected log(...args: any[]): void; | ||
protected info(...args: any[]): void; | ||
protected warn(...args: any[]): void; | ||
protected error(...args: any[]): void; | ||
} | ||
export { Command }; | ||
export {}; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.Command = void 0; | ||
const OS = require("os"); | ||
const utils_1 = require("../utils"); | ||
const regOption = /^-(?:-(\w[\w\d_-]*)|(\w))$/; | ||
const regOptionWithValue = /^-(?:-(\w[\w\d_-]*)|(\w))=(.*)$/; | ||
const regShortMultipleOption = /^-(\w+)$/; | ||
const InvalidError_1 = require("../errors/InvalidError"); | ||
const Parser_1 = require("./Parser"); | ||
const CommandInput_1 = require("./CommandInput"); | ||
class Command { | ||
constructor(command, logger) { | ||
this._commands = []; | ||
constructor(command) { | ||
this._options = []; | ||
this._completions = []; | ||
this._command = command; | ||
this._logger = logger; | ||
this._help = true; | ||
} | ||
getName() { | ||
get name() { | ||
return this._command; | ||
} | ||
command(name) { | ||
let command = this._commands.find((command) => { | ||
return command.getName() === name; | ||
}); | ||
if (!command) { | ||
command = new Command(name, this._logger); | ||
this._commands.push(command); | ||
} | ||
return command; | ||
} | ||
option(name, params) { | ||
const { type = "boolean", default: defaultValue, ...rest } = params || {}; | ||
this._options.push({ | ||
name, | ||
type: type, | ||
default: type === "boolean" | ||
? typeof defaultValue === "boolean" ? defaultValue : false | ||
: defaultValue, | ||
...rest | ||
}); | ||
const { type = "boolean", default: defaultValue, help = true, ...rest } = params || {}; | ||
this._options = [ | ||
...this._options.filter((option) => { | ||
return option.name !== name; | ||
}), | ||
{ | ||
name, | ||
help, | ||
type: type, | ||
default: type === "boolean" | ||
? typeof defaultValue === "boolean" ? defaultValue : false | ||
: defaultValue, | ||
...rest | ||
} | ||
]; | ||
return this; | ||
} | ||
getOptionSettings(name, alias) { | ||
return this._options.find((option) => { | ||
return (name && option.name === name) || (alias && option.alias === alias); | ||
}); | ||
} | ||
help(params) { | ||
@@ -51,3 +52,4 @@ const { disabled = false, description = "" } = typeof params === "boolean" ? { | ||
alias: "h", | ||
description: "Help" | ||
description: "Help", | ||
help: false | ||
}); | ||
@@ -66,164 +68,106 @@ } | ||
this._action = action; | ||
return this; | ||
} | ||
parse(parts, partial = false) { | ||
parse(parts) { | ||
const commands = this._command | ||
? this._command.split(/\s+/g) | ||
: []; | ||
const args = []; | ||
const args = {}; | ||
const options = {}; | ||
let current = 0; | ||
const parser = new Parser_1.Parser(parts); | ||
for (const command of commands) { | ||
let part = parts[current] || ""; | ||
let isLast = current === parts.length - 1; | ||
if ((0, utils_1.isSpread)(command)) { | ||
let value = []; | ||
while (current < parts.length) { | ||
isLast = current === parts.length - 1; | ||
if (partial && isLast) { | ||
args.push(value); | ||
return { | ||
args, | ||
options, | ||
command, | ||
part: parts[current] | ||
}; | ||
} | ||
value.push(parts[current]); | ||
current++; | ||
if (parser.isSpread(command)) { | ||
const name = parser.parseSpreadCommand(command); | ||
const values = []; | ||
while (!parser.eol) { | ||
values.push(parser.part); | ||
parser.next(); | ||
} | ||
args.push(value); | ||
args[name] = values; | ||
parser.next(); | ||
} | ||
else if ((0, utils_1.isCommand)(command)) { | ||
const regExp = (0, utils_1.generateCommandRegExp)(command); | ||
if (!regExp) { | ||
throw new Error(`Something wrong: ${command}`); | ||
else if (parser.isCommand(command)) { | ||
const res = parser.getArguments(command); | ||
for (const name in res) { | ||
args[name] = res[name]; | ||
} | ||
if (partial && isLast) { | ||
const partExp = (0, utils_1.generateCommandRegExp)(command, true); | ||
if (partExp.test(part)) { | ||
return { | ||
args, | ||
options, | ||
command, | ||
part | ||
}; | ||
} | ||
} | ||
if (!regExp.test(part)) { | ||
return null; | ||
} | ||
const [, ...match] = regExp.exec(part) || []; | ||
args.push(...match); | ||
current++; | ||
parser.next(); | ||
} | ||
let isOption = true; | ||
do { | ||
part = parts[current] || ""; | ||
if (regOption.test(part)) { | ||
const [, name, alias] = regOption.exec(part) || []; | ||
const option = this._options.find((option) => { | ||
return (name && option.name === name) || (alias && option.alias === alias); | ||
}); | ||
if (!option) { | ||
throw new Error(`Option "${name || alias}" is not defined (1)`); | ||
else { | ||
throw new InvalidError_1.InvalidError("Invalid command"); | ||
} | ||
while (parser.isOption()) { | ||
if (parser.isRegOption()) { | ||
const { name, alias } = parser.parseOption(); | ||
const option = this.getOptionSettings(name, alias); | ||
if (option) { | ||
switch (option.type) { | ||
case "boolean": | ||
options[option.name] = true; | ||
break; | ||
case "number": | ||
parser.next(); | ||
options[option.name] = parseFloat(parser.part); | ||
break; | ||
case "string": | ||
parser.next(); | ||
options[option.name] = parser.part; | ||
break; | ||
} | ||
} | ||
switch (option.type) { | ||
case "boolean": | ||
options[option.name] = true; | ||
break; | ||
case "number": | ||
options[option.name] = parseFloat(parts[++current]); | ||
break; | ||
case "string": | ||
options[option.name] = parts[++current]; | ||
break; | ||
} | ||
current++; | ||
} | ||
else if (regOptionWithValue.test(part)) { | ||
const [, name, alias, value] = regOptionWithValue.exec(part) || []; | ||
const option = this._options.find((option) => { | ||
if (name) { | ||
return option.name === name; | ||
else if (parser.isOptionWithValue()) { | ||
const { name, alias, value } = parser.parseOptionWithValue(); | ||
const option = this.getOptionSettings(name, alias); | ||
if (option) { | ||
switch (option.type) { | ||
case "boolean": | ||
options[option.name] = true; | ||
break; | ||
case "number": | ||
options[option.name] = parseFloat(value); | ||
break; | ||
case "string": | ||
options[option.name] = value; | ||
break; | ||
} | ||
if (alias) { | ||
return option.alias === alias; | ||
} | ||
return false; | ||
}); | ||
if (!option) { | ||
throw new Error(`Option not found ${name || alias} (2)`); | ||
} | ||
switch (option.type) { | ||
case "boolean": | ||
options[option.name] = ["true", "1"].includes(value); | ||
break; | ||
case "string": | ||
options[option.name] = value; | ||
break; | ||
case "number": | ||
options[option.name] = parseFloat(value); | ||
break; | ||
} | ||
current++; | ||
} | ||
else if (regShortMultipleOption.test(part)) { | ||
const [, alias] = regShortMultipleOption.exec(part) || []; | ||
this._options.filter((option) => { | ||
if (!option.alias) { | ||
return false; | ||
} | ||
return alias.split("").includes(option.alias); | ||
}).forEach((option) => { | ||
if (option.type === "boolean") { | ||
options[option.name] = true; | ||
} | ||
}); | ||
current++; | ||
isOption = true; | ||
} | ||
else { | ||
isOption = false; | ||
} | ||
} while (isOption); | ||
parser.next(); | ||
} | ||
} | ||
return { | ||
args, | ||
options, | ||
parts: parts.slice(current) | ||
}; | ||
if (!parser.eol) { | ||
throw new InvalidError_1.InvalidError("Haven't ended"); | ||
} | ||
return new CommandInput_1.CommandInput(args, options); | ||
} | ||
async process(parts, parentOptions = {}, parentArgs = []) { | ||
const res = this.parse(parts); | ||
if (!res) { | ||
return null; | ||
async emit(name, input) { | ||
if (this._help && input.option("help")) { | ||
const options = this._options.filter((option) => { | ||
return option.help; | ||
}); | ||
return [ | ||
"", | ||
`Usage: ${name} ${this.name}`, | ||
"", | ||
...this._description ? [ | ||
this._description, | ||
"" | ||
] : [], | ||
...options.length > 0 ? [ | ||
"Options:", | ||
...options.map((option) => { | ||
const alias = option.alias ? `-${option.alias},` : " "; | ||
return ` ${alias} --${option.name}\t\t${option.description}`; | ||
}), | ||
"" | ||
] : [] | ||
].join(OS.EOL); | ||
} | ||
const { args, parts: childParts } = res; | ||
const options = { | ||
...parentOptions, | ||
...res.options | ||
}; | ||
if (this._action) { | ||
if (this._help && options.help) { | ||
const optionsDescription = this._options.map((option) => { | ||
const { alias, name, description = "" } = option; | ||
const aliasDescription = alias ? `, -${alias}` : ""; | ||
return ` --${name}${aliasDescription} - ${description}`; | ||
}).join("\n"); | ||
return `Help:\n ${this._description || ""}\n\n${optionsDescription}\n`; | ||
} | ||
return (await this._action(options, ...parentArgs, ...args)) || ""; | ||
if (!this._action) { | ||
throw new Error("Command without action"); | ||
} | ||
for (const command of this._commands) { | ||
const res = await command.process(childParts, options, [ | ||
...parentArgs, | ||
...args | ||
]); | ||
if (res !== null) { | ||
return res; | ||
} | ||
} | ||
return null; | ||
return this._action(input); | ||
} | ||
async predictCommand(command, part, options = {}, args = []) { | ||
async predictCommand(command, part, input) { | ||
const comAttrReq = /^<([\w_-]+)>(.*)?$/; | ||
@@ -279,3 +223,3 @@ const comAttrOpt = /^\[([\w_-]+)](.*)?$/; | ||
if (exitCount > 100) { | ||
this.warn("Emergency exit", { | ||
console.warn("Emergency exit", { | ||
restCommand | ||
@@ -291,3 +235,3 @@ }); | ||
if (completion) { | ||
const predicts = await completion.action(options, ...args); | ||
const predicts = await Promise.resolve(completion.action(input)); | ||
resPredicts = predicts.reduce((res, predict) => { | ||
@@ -317,45 +261,50 @@ return [ | ||
} | ||
async predictOption(part) { | ||
const [, dash, name] = /^(--?)(\w+)?/.exec(part) || []; | ||
return this._options.reduce((res, option) => { | ||
if (dash === "-") { | ||
res.push(`-${option.alias}`, `--${option.name}`); | ||
} | ||
else if (dash === "--") { | ||
res.push(`--${option.name}`); | ||
} | ||
return res; | ||
}, []); | ||
} | ||
async complete(parts) { | ||
const res = this.parse(parts, true); | ||
if (!res) { | ||
return null; | ||
if (!this._help) { | ||
return []; | ||
} | ||
const { args, options, parts: childParts, part, command } = res; | ||
if (command && typeof part !== "undefined") { | ||
return this.predictCommand(command, part, options, args); | ||
} | ||
const predicts = []; | ||
for (const command of this._commands) { | ||
const res = await command.complete(childParts); | ||
if (res) { | ||
predicts.push(...res); | ||
const commands = this._command | ||
? this._command.split(/\s+/g) | ||
: []; | ||
const parser = new Parser_1.Parser(parts); | ||
const args = {}; | ||
const options = {}; | ||
for (const command of commands) { | ||
if (parser.isSpread(command)) { | ||
} | ||
else if (!parser.isLast && parser.isCommand(command)) { | ||
const partArguments = parser.getArguments(command); | ||
for (const name in partArguments) { | ||
args[name] = partArguments[name]; | ||
} | ||
parser.next(); | ||
} | ||
else if (parser.isLast && parser.isCommand(command, true)) { | ||
return this.predictCommand(command, parser.part, new CommandInput_1.CommandInput(args, options)); | ||
} | ||
else { | ||
throw new InvalidError_1.InvalidError("Error"); | ||
} | ||
while (parser.isOption(true)) { | ||
if (parser.isLast && parser.isCommand(command)) { | ||
return this.predictOption(parser.part); | ||
} | ||
parser.next(); | ||
} | ||
} | ||
return predicts; | ||
return []; | ||
} | ||
log(...args) { | ||
if (!this._logger) { | ||
return; | ||
} | ||
this._logger.log(...args); | ||
} | ||
info(...args) { | ||
if (!this._logger) { | ||
return; | ||
} | ||
this._logger.info(...args); | ||
} | ||
warn(...args) { | ||
if (!this._logger) { | ||
return; | ||
} | ||
this._logger.warn(...args); | ||
} | ||
error(...args) { | ||
if (!this._logger) { | ||
return; | ||
} | ||
this._logger.error(...args); | ||
} | ||
} | ||
exports.Command = Command; |
export * from "./Cli"; | ||
export * from "./Command"; | ||
export * from "./CommandInput"; | ||
export * from "./Logger"; | ||
export * from "./Parser"; |
@@ -18,1 +18,5 @@ "use strict"; | ||
__exportStar(require("./Cli"), exports); | ||
__exportStar(require("./Command"), exports); | ||
__exportStar(require("./CommandInput"), exports); | ||
__exportStar(require("./Logger"), exports); | ||
__exportStar(require("./Parser"), exports); |
@@ -1,2 +0,1 @@ | ||
export * from "./Logger"; | ||
export * from "./Option"; |
@@ -17,3 +17,2 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
__exportStar(require("./Logger"), exports); | ||
__exportStar(require("./Option"), exports); |
export type Option = { | ||
name: string; | ||
type: "string" | "boolean" | "number"; | ||
help?: boolean; | ||
alias?: string; | ||
@@ -5,0 +6,0 @@ description?: string; |
@@ -9,3 +9,3 @@ "use strict"; | ||
_${name}_completion() { | ||
compadd -- \`${name} --compzsh --compgen "\${CURRENT}" "\${words[CURRENT - 1]}" \${BUFFER}\` | ||
compadd -- \`${name} complete --compzsh --compgen "\${CURRENT}" "\${words[CURRENT - 1]}" \${BUFFER}\` | ||
} | ||
@@ -20,3 +20,3 @@ | ||
local OPTIONS=$(${name} --compbash --compgen "$((COMP_CWORD - (nb_colon * 2)))" "$prev" "\${COMP_LINE}") | ||
local OPTIONS=$(${name} complete --compbash --compgen "$((COMP_CWORD - (nb_colon * 2)))" "$prev" "\${COMP_LINE}") | ||
@@ -47,3 +47,3 @@ COMPREPLY=() | ||
si="$IFS" | ||
if ! IFS=$'\n' reply=($(${name} --compzsh --compgen "\${cword}" "\${words[cword - 1]}" \${line})); then | ||
if ! IFS=$'\n' reply=($(${name} complete --compzsh --compgen "\${cword}" "\${words[cword - 1]}" \${line})); then | ||
local ret=$? | ||
@@ -50,0 +50,0 @@ IFS="$si" |
{ | ||
"name": "@kearisp/cli", | ||
"version": "1.0.7", | ||
"version": "2.0.0", | ||
"license": "MIT", | ||
@@ -23,6 +23,9 @@ "author": "Kris Papercut <krispcut@gmail.com>", | ||
"prepare": "npm run build", | ||
"watch": "tsc --watch", | ||
"watch": "KP_LOG=disable tsc --watch", | ||
"watch:test": "KP_LOG=log jest --colors --watchAll", | ||
"watch:test-cli": "KP_LOG=log jest --colors --watchAll --runTestsByPath ./src/makes/Cli.spec.ts", | ||
"watch:test-command": "KP_LOG=log jest --colors --watchAll --runTestsByPath ./src/makes/Command.spec.ts", | ||
"watch:test-parser": "KP_LOG=log jest --colors --watchAll --runTestsByPath ./src/makes/Parser.spec.ts", | ||
"build": "tsc", | ||
"test": "jest --colors", | ||
"test:watch": "jest --colors --watch" | ||
"test": "KP_LOG=none jest --colors" | ||
}, | ||
@@ -35,8 +38,10 @@ "dependencies": { | ||
"@types/jest": "^29.5.12", | ||
"@types/node": "^20.11.16", | ||
"@types/node": "^20.12.7", | ||
"date-fns": "^3.6.0", | ||
"fs": "^0.0.1-security", | ||
"jest": "^29.7.0", | ||
"ts-jest": "^29.1.2", | ||
"ts-node": "^10.9.2", | ||
"typescript": "^5.3.3" | ||
"typescript": "^5.4.5" | ||
} | ||
} |
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 2 instances in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
38989
36
984
8
3