@restroom-mw/files
Advanced tools
Comparing version 0.13.1-b5b2bd4.43 to 0.13.1-b611a0f.118
@@ -1,4 +0,64 @@ | ||
export declare const DOWNLOAD = "download the {} and extract it into {}"; | ||
export declare const STORE_RESULT = "store {} in the file {}"; | ||
export declare const READ = "read the content of {}"; | ||
export declare enum Action { | ||
/** | ||
* `Then I download the 'myUrl' and extract it into 'myFolder'`<br><br> | ||
* Download a zip file located at the url *myUrl* and extract it at the path | ||
* *myFolder* under the *FILES_DIR* on the server. | ||
* @param {string} myUrl - Name of the variable containing the url | ||
* @param {string} myFolder - Name of the variable containing the path to | ||
* the folder where the data will be stored | ||
*/ | ||
DOWNLOAD = "download the {} and extract it into {}", | ||
/** | ||
* `Then I store 'myVariable' in the file 'myFile'`<br><br> | ||
* Store the content of the variable *myVariable* in the filesystem at the path | ||
* *myFile* under the *FILES_DIR* directory on the server | ||
* @param {string} myVariable - Name of the variable containing the data to be stored | ||
* @param {string} myFile - Name of the variable containing the path to the file where | ||
* the data will be stored | ||
*/ | ||
STORE_RESULT = "store {} in the file {}", | ||
/** | ||
* `Given I read the content of 'myFile'`<br><br> | ||
* write the content of *myFile*, found under the *FILES_DIR* directory, in the data | ||
* @param {string} myFile - It can be the name of the variable that contains the path | ||
* to the file or the path itself | ||
*/ | ||
READ = "read the content of {}", | ||
/** | ||
* `Given I read the content of 'myFile' and save the output into 'myVariable'`<br><br> | ||
* Write the content of *myFile* in the data under the key *myVariable* | ||
* @param {string} myFile - It can be the name of the variable that contains the path | ||
* to the file or the path itself | ||
* @param {string} myVariable - Name of the variable where the content of the file will be stored | ||
*/ | ||
READ_AND_SAVE = "read the content of {} and save the output into {}", | ||
/** | ||
* `Given I list the content of directory 'dir_path' as 'dir_result'`<br><br> | ||
* Write the list of objects found in *dir_path*, under the *FILES_DIR* directory, in the data, | ||
* the result is an array of dictionaries whose structure is: | ||
* ``` | ||
* { | ||
* atime: '2022-06-13T07:00:34.870Z', | ||
* birthtime: '2022-06-13T07:00:34.870Z', | ||
* blksize: 4096, | ||
* blocks: 8, | ||
* ctime: '2022-08-09T11:09:22.718Z', | ||
* dev: 2055, | ||
* gid: 1000, | ||
* mode: '40755', | ||
* mtime: '2022-08-09T11:09:22.718Z', | ||
* name: 'zencode', | ||
* nlink: 6, | ||
* size: 4096, | ||
* uid: 1000 | ||
* } | ||
* ``` | ||
* In particular, if `mode` starts with `40` the current item is a directory | ||
* | ||
* @param {string} dir_path - Variable name containing the path to the directory | ||
* under the *FILES_DIR* directory | ||
* @param {string} dir_result - Name of the variable where the result will be stored | ||
*/ | ||
LS = "list the content of directory {} as {}" | ||
} | ||
//# sourceMappingURL=actions.d.ts.map |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.READ = exports.STORE_RESULT = exports.DOWNLOAD = void 0; | ||
exports.DOWNLOAD = "download the {} and extract it into {}"; | ||
exports.STORE_RESULT = "store {} in the file {}"; | ||
exports.READ = "read the content of {}"; | ||
exports.Action = void 0; | ||
var Action; | ||
(function (Action) { | ||
/** | ||
* `Then I download the 'myUrl' and extract it into 'myFolder'`<br><br> | ||
* Download a zip file located at the url *myUrl* and extract it at the path | ||
* *myFolder* under the *FILES_DIR* on the server. | ||
* @param {string} myUrl - Name of the variable containing the url | ||
* @param {string} myFolder - Name of the variable containing the path to | ||
* the folder where the data will be stored | ||
*/ | ||
Action["DOWNLOAD"] = "download the {} and extract it into {}"; | ||
/** | ||
* `Then I store 'myVariable' in the file 'myFile'`<br><br> | ||
* Store the content of the variable *myVariable* in the filesystem at the path | ||
* *myFile* under the *FILES_DIR* directory on the server | ||
* @param {string} myVariable - Name of the variable containing the data to be stored | ||
* @param {string} myFile - Name of the variable containing the path to the file where | ||
* the data will be stored | ||
*/ | ||
Action["STORE_RESULT"] = "store {} in the file {}"; | ||
/** | ||
* `Given I read the content of 'myFile'`<br><br> | ||
* write the content of *myFile*, found under the *FILES_DIR* directory, in the data | ||
* @param {string} myFile - It can be the name of the variable that contains the path | ||
* to the file or the path itself | ||
*/ | ||
Action["READ"] = "read the content of {}"; | ||
/** | ||
* `Given I read the content of 'myFile' and save the output into 'myVariable'`<br><br> | ||
* Write the content of *myFile* in the data under the key *myVariable* | ||
* @param {string} myFile - It can be the name of the variable that contains the path | ||
* to the file or the path itself | ||
* @param {string} myVariable - Name of the variable where the content of the file will be stored | ||
*/ | ||
Action["READ_AND_SAVE"] = "read the content of {} and save the output into {}"; | ||
/** | ||
* `Given I list the content of directory 'dir_path' as 'dir_result'`<br><br> | ||
* Write the list of objects found in *dir_path*, under the *FILES_DIR* directory, in the data, | ||
* the result is an array of dictionaries whose structure is: | ||
* ``` | ||
* { | ||
* atime: '2022-06-13T07:00:34.870Z', | ||
* birthtime: '2022-06-13T07:00:34.870Z', | ||
* blksize: 4096, | ||
* blocks: 8, | ||
* ctime: '2022-08-09T11:09:22.718Z', | ||
* dev: 2055, | ||
* gid: 1000, | ||
* mode: '40755', | ||
* mtime: '2022-08-09T11:09:22.718Z', | ||
* name: 'zencode', | ||
* nlink: 6, | ||
* size: 4096, | ||
* uid: 1000 | ||
* } | ||
* ``` | ||
* In particular, if `mode` starts with `40` the current item is a directory | ||
* | ||
* @param {string} dir_path - Variable name containing the path to the directory | ||
* under the *FILES_DIR* directory | ||
* @param {string} dir_result - Name of the variable where the result will be stored | ||
*/ | ||
Action["LS"] = "list the content of directory {} as {}"; | ||
})(Action = exports.Action || (exports.Action = {})); |
@@ -19,22 +19,10 @@ "use strict"; | ||
const fs_1 = __importDefault(require("fs")); | ||
const promises_1 = require("node:fs/promises"); | ||
const os_1 = __importDefault(require("os")); | ||
const extract_zip_1 = __importDefault(require("extract-zip")); | ||
const path_1 = __importDefault(require("path")); | ||
const utils_1 = require("@restroom-mw/utils"); | ||
const actions_1 = require("./actions"); | ||
require("dotenv").config(); | ||
/** | ||
* `download the 'myUrl' and extract it into 'myFolder'` | ||
* | ||
* Download a zip file located at the url `myUrl` and extract it at the path | ||
* `myFolder` on the server. | ||
*/ | ||
const actions_1 = require("./actions"); | ||
/** | ||
* `store 'myVariable' in the file 'myFolder'` | ||
* | ||
* Store the content of the variable `myVariable` in the filesystem at the path | ||
* `myFolder` on the server | ||
*/ | ||
const actions_2 = require("./actions"); | ||
const actions_3 = require("./actions"); | ||
/** | ||
* Base dir to store data for the user | ||
@@ -47,13 +35,3 @@ * @constant | ||
// save path must be subdirs of FILES_DIR | ||
const validatePath = (p) => { | ||
if (!exports.FILES_DIR) | ||
throw new Error(`FILES_DIR is not defined`); | ||
if (exports.FILES_DIR != "/") { | ||
const relative = path_1.default.relative(exports.FILES_DIR, p); | ||
const isSubdir = relative && !relative.startsWith('..') && !path_1.default.isAbsolute(relative); | ||
if (!isSubdir) { | ||
throw new Error(`Result path outside ${exports.FILES_DIR}`); | ||
} | ||
} | ||
}; | ||
const validatePath = (0, utils_1.validateSubdir)(exports.FILES_DIR); | ||
exports.default = (req, res, next) => { | ||
@@ -63,15 +41,74 @@ const rr = new core_1.Restroom(req, res); | ||
const { zencode, keys, data } = params; | ||
if (zencode.match(actions_3.READ)) { | ||
const params = zencode.paramsOf(actions_3.READ); | ||
for (const file of params) { | ||
validatePath(file); | ||
const absoluteFile = path_1.default.join(exports.FILES_DIR, file); | ||
input = rr.combineDataKeys(data, keys); | ||
const readFromFile = (file) => { | ||
let content; | ||
const f = input[file] || file; | ||
const absoluteFile = path_1.default.resolve(path_1.default.join(exports.FILES_DIR, f)); | ||
validatePath(absoluteFile); | ||
try { | ||
content = fs_1.default.readFileSync(absoluteFile, 'utf8'); | ||
} | ||
catch (e) { | ||
// TODO: add not-fatal (warning) log in restroom | ||
//throw new Error(`[FILES] error while reading the file ${absoluteFile}`); | ||
content = "{}"; | ||
} | ||
return JSON.parse(content); | ||
}; | ||
if (zencode.match(actions_1.Action.READ)) { | ||
const params = zencode.paramsOf(actions_1.Action.READ); | ||
for (const f of params) { | ||
Object.assign(data, readFromFile(f)); | ||
} | ||
} | ||
; | ||
if (zencode.match(actions_1.Action.READ_AND_SAVE)) { | ||
const chkParams = zencode.chunkedParamsOf(actions_1.Action.READ_AND_SAVE, 2); | ||
for (const [f, outputVariable] of chkParams) { | ||
data[outputVariable] = readFromFile(f); | ||
} | ||
} | ||
; | ||
if (zencode.match(actions_1.Action.LS)) { | ||
const params = zencode.chunkedParamsOf(actions_1.Action.LS, 2); | ||
const fileStats = {}; | ||
const allLs = (yield Promise.all(params.map(([p, name]) => __awaiter(void 0, void 0, void 0, function* () { | ||
const f = input[p] || p; | ||
validatePath(f); | ||
try { | ||
const content = fs_1.default.readFileSync(absoluteFile, 'utf8'); | ||
Object.assign(data, JSON.parse(content)); | ||
const content = yield (0, promises_1.readdir)(f); | ||
// I am not checking if `name` is used multiple times | ||
fileStats[name] = []; | ||
return content.map((current) => [name, f, current]); | ||
} | ||
catch (e) { | ||
throw new Error(`[FILES] error while reading the file ${absoluteFile}`); | ||
throw new Error(`[FILES] error while reading the file ${f}`); | ||
} | ||
})))).flat(); | ||
// list with all files I want to see the stats of | ||
const allStats = yield Promise.all(allLs.map(([name, p, current]) => __awaiter(void 0, void 0, void 0, function* () { | ||
const currentFile = path_1.default.join(p, current); | ||
const fileStat = yield (0, promises_1.stat)(currentFile); | ||
return [name, current, fileStat]; | ||
}))); | ||
for (const [name, current, currentStat] of allStats) { | ||
// see https://unix.stackexchange.com/questions/317855/file-mode-on-macosx#317907 | ||
// for the meaning of the mode field | ||
fileStats[name].push({ | ||
'name': current, | ||
'mode': currentStat.mode.toString(8), | ||
'dev': currentStat.dev, | ||
'nlink': currentStat.nlink, | ||
'uid': currentStat.uid, | ||
'gid': currentStat.gid, | ||
'size': currentStat.size, | ||
'blksize': currentStat.blksize, | ||
'blocks': currentStat.blocks, | ||
'atime': currentStat.atime.toISOString(), | ||
'mtime': currentStat.mtime.toISOString(), | ||
'ctime': currentStat.ctime.toISOString(), | ||
'birthtime': currentStat.birthtime.toISOString(), | ||
}); | ||
} | ||
Object.assign(data, fileStats); | ||
} | ||
@@ -82,7 +119,9 @@ input = rr.combineDataKeys(data, keys); | ||
const { result, zencode } = params; | ||
if (zencode.match(actions_1.DOWNLOAD)) { | ||
const allPassOutputs = zencode.paramsOf(actions_1.DOWNLOAD); | ||
if (zencode.match(actions_1.Action.DOWNLOAD)) { | ||
const allPassOutputs = zencode.paramsOf(actions_1.Action.DOWNLOAD); | ||
for (let i = 0; i < allPassOutputs.length; i += 2) { | ||
const file = result[allPassOutputs[i]]; | ||
const folder = input[allPassOutputs[i + 1]]; | ||
const file = result[allPassOutputs[i]] || | ||
input[allPassOutputs[i]]; | ||
const folder = result[allPassOutputs[i + 1]] || | ||
input[allPassOutputs[i + 1]]; | ||
if (!file) { | ||
@@ -113,7 +152,9 @@ throw new Error(`[FILES] url not defined`); | ||
} | ||
if (zencode.match(actions_2.STORE_RESULT)) { | ||
const allPassOutputs = zencode.paramsOf(actions_2.STORE_RESULT); | ||
if (zencode.match(actions_1.Action.STORE_RESULT)) { | ||
const allPassOutputs = zencode.paramsOf(actions_1.Action.STORE_RESULT); | ||
for (let i = 0; i < allPassOutputs.length; i += 2) { | ||
const variable = result[allPassOutputs[i]]; | ||
const file = input[allPassOutputs[i + 1]]; | ||
const variable = result[allPassOutputs[i]] || | ||
input[allPassOutputs[i]]; | ||
const file = result[allPassOutputs[i + 1]] || | ||
input[allPassOutputs[i + 1]]; | ||
if (!variable) { | ||
@@ -120,0 +161,0 @@ throw new Error(`[FILES] variable not defined`); |
{ | ||
"name": "@restroom-mw/files", | ||
"version": "0.13.1-b5b2bd4.43+b5b2bd4", | ||
"version": "0.13.1-b611a0f.118+b611a0f", | ||
"description": "Utilities middleware to work with files for Restroom", | ||
@@ -35,3 +35,3 @@ "author": "Alberto Lerda <alberto@dyne.org>", | ||
}, | ||
"gitHead": "b5b2bd49465146236746bda33222fd9afa6f0d15" | ||
"gitHead": "b611a0f61ca34654b70487332cafa914851bfa8c" | ||
} |
@@ -1,3 +0,63 @@ | ||
export const DOWNLOAD = "download the {} and extract it into {}"; | ||
export const STORE_RESULT = "store {} in the file {}"; | ||
export const READ = "read the content of {}"; | ||
export enum Action { | ||
/** | ||
* `Then I download the 'myUrl' and extract it into 'myFolder'`<br><br> | ||
* Download a zip file located at the url *myUrl* and extract it at the path | ||
* *myFolder* under the *FILES_DIR* on the server. | ||
* @param {string} myUrl - Name of the variable containing the url | ||
* @param {string} myFolder - Name of the variable containing the path to | ||
* the folder where the data will be stored | ||
*/ | ||
DOWNLOAD = "download the {} and extract it into {}", | ||
/** | ||
* `Then I store 'myVariable' in the file 'myFile'`<br><br> | ||
* Store the content of the variable *myVariable* in the filesystem at the path | ||
* *myFile* under the *FILES_DIR* directory on the server | ||
* @param {string} myVariable - Name of the variable containing the data to be stored | ||
* @param {string} myFile - Name of the variable containing the path to the file where | ||
* the data will be stored | ||
*/ | ||
STORE_RESULT = "store {} in the file {}", | ||
/** | ||
* `Given I read the content of 'myFile'`<br><br> | ||
* write the content of *myFile*, found under the *FILES_DIR* directory, in the data | ||
* @param {string} myFile - It can be the name of the variable that contains the path | ||
* to the file or the path itself | ||
*/ | ||
READ = "read the content of {}", | ||
/** | ||
* `Given I read the content of 'myFile' and save the output into 'myVariable'`<br><br> | ||
* Write the content of *myFile* in the data under the key *myVariable* | ||
* @param {string} myFile - It can be the name of the variable that contains the path | ||
* to the file or the path itself | ||
* @param {string} myVariable - Name of the variable where the content of the file will be stored | ||
*/ | ||
READ_AND_SAVE = "read the content of {} and save the output into {}", | ||
/** | ||
* `Given I list the content of directory 'dir_path' as 'dir_result'`<br><br> | ||
* Write the list of objects found in *dir_path*, under the *FILES_DIR* directory, in the data, | ||
* the result is an array of dictionaries whose structure is: | ||
* ``` | ||
* { | ||
* atime: '2022-06-13T07:00:34.870Z', | ||
* birthtime: '2022-06-13T07:00:34.870Z', | ||
* blksize: 4096, | ||
* blocks: 8, | ||
* ctime: '2022-08-09T11:09:22.718Z', | ||
* dev: 2055, | ||
* gid: 1000, | ||
* mode: '40755', | ||
* mtime: '2022-08-09T11:09:22.718Z', | ||
* name: 'zencode', | ||
* nlink: 6, | ||
* size: 4096, | ||
* uid: 1000 | ||
* } | ||
* ``` | ||
* In particular, if `mode` starts with `40` the current item is a directory | ||
* | ||
* @param {string} dir_path - Variable name containing the path to the directory | ||
* under the *FILES_DIR* directory | ||
* @param {string} dir_result - Name of the variable where the result will be stored | ||
*/ | ||
LS = "list the content of directory {} as {}", | ||
} |
136
src/index.ts
@@ -5,2 +5,3 @@ import {Restroom} from "@restroom-mw/core"; | ||
import fs from 'fs' | ||
import { readdir, stat } from 'node:fs/promises'; | ||
import os from 'os' | ||
@@ -10,23 +11,7 @@ import extract from 'extract-zip'; | ||
import {ObjectLiteral} from "@restroom-mw/types"; | ||
import {validateSubdir} from "@restroom-mw/utils" | ||
import { Action } from "./actions" | ||
require("dotenv").config(); | ||
/** | ||
* `download the 'myUrl' and extract it into 'myFolder'` | ||
* | ||
* Download a zip file located at the url `myUrl` and extract it at the path | ||
* `myFolder` on the server. | ||
*/ | ||
import {DOWNLOAD} from "./actions"; | ||
/** | ||
* `store 'myVariable' in the file 'myFolder'` | ||
* | ||
* Store the content of the variable `myVariable` in the filesystem at the path | ||
* `myFolder` on the server | ||
*/ | ||
import {STORE_RESULT} from "./actions"; | ||
import {READ} from "./actions"; | ||
/** | ||
@@ -42,35 +27,82 @@ * Base dir to store data for the user | ||
// save path must be subdirs of FILES_DIR | ||
const validatePath = (p: string) => { | ||
if (!FILES_DIR) | ||
throw new Error(`FILES_DIR is not defined`); | ||
const validatePath = validateSubdir(FILES_DIR); | ||
if (FILES_DIR != "/") { | ||
const relative = path.relative(FILES_DIR, p); | ||
const isSubdir = relative && !relative.startsWith('..') && !path.isAbsolute(relative); | ||
if (!isSubdir) { | ||
throw new Error(`Result path outside ${FILES_DIR}`) | ||
} | ||
} | ||
} | ||
export default (req: Request, res: Response, next: NextFunction) => { | ||
const rr = new Restroom(req, res); | ||
rr.onBefore(async (params) => { | ||
const { zencode, keys, data } = params; | ||
input = rr.combineDataKeys(data, keys); | ||
if (zencode.match(READ)) { | ||
const params = zencode.paramsOf(READ); | ||
for(const file of params) { | ||
validatePath(file); | ||
const absoluteFile = path.join(FILES_DIR, file) | ||
try { | ||
const content = fs.readFileSync(absoluteFile, 'utf8') | ||
Object.assign(data, JSON.parse(content)) | ||
} catch(e) { | ||
throw new Error(`[FILES] error while reading the file ${absoluteFile}`); | ||
} | ||
const readFromFile = ( file: string ) => { | ||
let content | ||
const f = input[file] || file; | ||
const absoluteFile = path.resolve(path.join(FILES_DIR, f)); | ||
validatePath(absoluteFile); | ||
try { | ||
content = fs.readFileSync(absoluteFile, 'utf8'); | ||
} catch(e) { | ||
// TODO: add not-fatal (warning) log in restroom | ||
//throw new Error(`[FILES] error while reading the file ${absoluteFile}`); | ||
content = "{}"; | ||
} | ||
return JSON.parse(content); | ||
} | ||
if (zencode.match(Action.READ)) { | ||
const params = zencode.paramsOf(Action.READ); | ||
for(const f of params) { | ||
Object.assign(data, readFromFile(f)); | ||
} | ||
}; | ||
if (zencode.match(Action.READ_AND_SAVE)) { | ||
const chkParams = zencode.chunkedParamsOf(Action.READ_AND_SAVE, 2); | ||
for(const [f, outputVariable] of chkParams) { | ||
data[ outputVariable ] = readFromFile(f); | ||
} | ||
}; | ||
if (zencode.match(Action.LS)) { | ||
const params = zencode.chunkedParamsOf(Action.LS, 2); | ||
const fileStats: Record<string, any> = {} | ||
const allLs = (await Promise.all(params.map( | ||
async ([p, name]: string[]) => { | ||
const f = input[p] || p; | ||
validatePath(f); | ||
try { | ||
const content = await readdir(f) | ||
// I am not checking if `name` is used multiple times | ||
fileStats[name] = [] | ||
return content.map((current) => [name, f, current]); | ||
} catch(e) { | ||
throw new Error(`[FILES] error while reading the file ${f}`); | ||
} | ||
}))).flat() | ||
// list with all files I want to see the stats of | ||
const allStats = await Promise.all(allLs.map(async ([name, p, current]) => { | ||
const currentFile = path.join(p, current) | ||
const fileStat = await stat(currentFile) | ||
return [name, current, fileStat] | ||
})) | ||
for(const [name, current, currentStat] of allStats) { | ||
// see https://unix.stackexchange.com/questions/317855/file-mode-on-macosx#317907 | ||
// for the meaning of the mode field | ||
fileStats[name].push({ | ||
'name': current, | ||
'mode': currentStat.mode.toString(8), | ||
'dev': currentStat.dev, | ||
'nlink': currentStat.nlink, | ||
'uid': currentStat.uid, | ||
'gid': currentStat.gid, | ||
'size': currentStat.size, | ||
'blksize': currentStat.blksize, | ||
'blocks': currentStat.blocks, | ||
'atime': currentStat.atime.toISOString(), | ||
'mtime': currentStat.mtime.toISOString(), | ||
'ctime': currentStat.ctime.toISOString(), | ||
'birthtime': currentStat.birthtime.toISOString(), | ||
}) | ||
} | ||
Object.assign(data, fileStats) | ||
} | ||
input = rr.combineDataKeys(data, keys); | ||
@@ -81,7 +113,9 @@ }); | ||
const {result, zencode} = params; | ||
if (zencode.match(DOWNLOAD)) { | ||
const allPassOutputs = zencode.paramsOf(DOWNLOAD); | ||
if (zencode.match(Action.DOWNLOAD)) { | ||
const allPassOutputs = zencode.paramsOf(Action.DOWNLOAD); | ||
for (let i = 0; i < allPassOutputs.length; i += 2) { | ||
const file = result[allPassOutputs[i]] | ||
const folder = input[allPassOutputs[i + 1]]; | ||
const file = result[allPassOutputs[i]] || | ||
input[allPassOutputs[i]]; | ||
const folder = result[allPassOutputs[i + 1]] || | ||
input[allPassOutputs[i + 1]]; | ||
@@ -114,7 +148,9 @@ if(!file) { | ||
} | ||
if (zencode.match(STORE_RESULT)) { | ||
const allPassOutputs = zencode.paramsOf(STORE_RESULT); | ||
if (zencode.match(Action.STORE_RESULT)) { | ||
const allPassOutputs = zencode.paramsOf(Action.STORE_RESULT); | ||
for (let i = 0; i < allPassOutputs.length; i += 2) { | ||
const variable = result[allPassOutputs[i]] | ||
const file = input[allPassOutputs[i + 1]] | ||
const variable = result[allPassOutputs[i]] || | ||
input[allPassOutputs[i]]; | ||
const file = result[allPassOutputs[i + 1]] || | ||
input[allPassOutputs[i + 1]]; | ||
@@ -121,0 +157,0 @@ if(!variable) { |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Manifest confusion
Supply chain riskThis package has inconsistent metadata. This could be malicious or caused by an error when publishing the package.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Manifest confusion
Supply chain riskThis package has inconsistent metadata. This could be malicious or caused by an error when publishing the package.
Found 1 instance in 1 package
59774
538
2