Comparing version 0.8.1 to 1.0.0
@@ -6,5 +6,7 @@ const NetworkError = require("./util/NetworkError") | ||
const Lists = require("./Lists") | ||
const Comments = require("./Comments") | ||
const Images = require("./Images") | ||
const Search = require("./Search") | ||
const Lists = require("./Lists") | ||
const Tags = require("./Tags") | ||
@@ -16,3 +18,3 @@ /** | ||
/** | ||
* A JavaScript client for Derpibooru API | ||
* JavaScript bindings for Derpibooru API | ||
* | ||
@@ -35,34 +37,56 @@ * @param {object} [options = {}] – client options | ||
/** | ||
* Creates a request handler for /images.json | ||
* Creates a request handler for /api/v1/json/search | ||
* | ||
* @return {Images} | ||
* @param {string | string[]} [query = []] – query params | ||
* | ||
* @return {Search} | ||
* | ||
* @public | ||
*/ | ||
images() { | ||
return new Images({link: this.__link, dinky: this}) | ||
search(...query) { | ||
return new Search({link: this.__link, dinky: this, query}) | ||
} | ||
/** | ||
* Creates a request handler for /search.json | ||
* Creates a request handler for /api/v1/json/comments | ||
* | ||
* @param {string | string[]} [tags = []] – a tag or a list of tags | ||
* @return {Comments} | ||
* | ||
* @return {Search} | ||
* @public | ||
*/ | ||
comments() { | ||
return new Comments({link: this.__link, dinky: this}) | ||
} | ||
/** | ||
* Creates a request handler for /api/v1/json/tags | ||
* | ||
* @return {Tags} | ||
* | ||
* @public | ||
*/ | ||
search(...tags) { | ||
return new Search({link: this.__link, dinky: this, tags}) | ||
tags() { | ||
return new Tags({link: this.__link, dinky: this}) | ||
} | ||
/** | ||
* Creates a new Lists instance to give you ability | ||
* to send requests to `/lists.json` and bunch of shortcuts | ||
* to search for specific images categories on `/search.json` | ||
* Creates a request handler for /api/v1/json/images | ||
* | ||
* @return {Images} | ||
* | ||
* @public | ||
*/ | ||
images() { | ||
return new Images({link: this.__link, dinky: this}) | ||
} | ||
/** | ||
* Creates a List class that contains a bunch of shortcuts for Search. | ||
* | ||
* @return {Lists} | ||
* | ||
* @public | ||
*/ | ||
lists() { | ||
return new Lists({link: this.__link, dinky: this}) | ||
return new Lists({dinky: this}) | ||
} | ||
@@ -69,0 +93,0 @@ } |
@@ -1,2 +0,2 @@ | ||
const Request = require("./Request") | ||
const Entities = require("./Entities") | ||
@@ -6,3 +6,3 @@ /** | ||
*/ | ||
class Images extends Request { | ||
class Images extends Entities { | ||
/** | ||
@@ -18,14 +18,25 @@ * @param {object} options | ||
/** | ||
* Takes an image by given ID. | ||
* Creates a new Search request that to /api/v1/json/search/images | ||
* | ||
* @param {number} id – image ID on Derpibooru | ||
* @param {string | string[]} [query = []] – query params | ||
* | ||
* @return {Promise<object>} | ||
* @return {Search} | ||
* | ||
* @public | ||
*/ | ||
async id(id) { | ||
this._path.push(id) | ||
search(...args) { | ||
return super.search(...args).images() | ||
} | ||
return this.exec() | ||
/** | ||
* Get a featured image | ||
* | ||
* @params {object} options | ||
* | ||
* @return {Promise<object>} | ||
*/ | ||
featured(options) { | ||
this._path.push("featured") | ||
return this.exec(options) | ||
} | ||
@@ -32,0 +43,0 @@ } |
@@ -1,26 +0,16 @@ | ||
const Request = require("./Request") | ||
class Lists extends Request { | ||
constructor({dinky, link}) { | ||
super({dinky, link, path: "lists"}) | ||
/** | ||
* @api private | ||
*/ | ||
class Lists { | ||
constructor({dinky}) { | ||
this._dinky = dinky | ||
} | ||
/* | ||
* Creates a request that gets 3 images lists (top scoring, | ||
* all time top scoring, top commented) for given period. | ||
* | ||
* @param {string} period – Sampling period, specified in weeks, | ||
* days, or hours | ||
* | ||
* @return {Lists} | ||
*/ | ||
last(period) { | ||
this._query.set("last", period) | ||
return this | ||
} | ||
/** | ||
* Creates a Search request that gets top scoring images of last 3 days. | ||
* The most rated images will be at the top of the list. | ||
* | ||
* @return {Search} | ||
* | ||
* @public | ||
*/ | ||
@@ -39,2 +29,4 @@ topScoring() { | ||
* @return {Search} | ||
* | ||
* @public | ||
*/ | ||
@@ -50,2 +42,4 @@ topScoringAllTime() { | ||
* @return {Search} | ||
* | ||
* @public | ||
*/ | ||
@@ -52,0 +46,0 @@ topCommented() { |
@@ -15,5 +15,19 @@ const {URLSearchParams} = require("url") | ||
/** | ||
* Query params storage. | ||
* Unlike URLSearchParams this class keeps every entry as is, | ||
* so you would be able to read them from the store. | ||
* | ||
* @api private | ||
*/ | ||
class Query extends Map { | ||
/** | ||
* Adds a new antry to the store | ||
* | ||
* @param {string} key | ||
* @param {any} value | ||
* | ||
* @return {Query} | ||
* | ||
* @public | ||
*/ | ||
set(key, value) { | ||
@@ -25,2 +39,11 @@ assertKey(key) | ||
/** | ||
* Returns an antry by its name if so exists. | ||
* | ||
* @param {string} name | ||
* | ||
* @return {any} | ||
* | ||
* @public | ||
*/ | ||
get(key) { | ||
@@ -32,2 +55,11 @@ assertKey(key) | ||
/** | ||
* Checks if an entry associated key exists | ||
* | ||
* @param {string} key | ||
* | ||
* @return {boolean} | ||
* | ||
* @public | ||
*/ | ||
has(key) { | ||
@@ -39,2 +71,11 @@ assertKey(key) | ||
/** | ||
* Removes an entry associated with given key | ||
* | ||
* @param {string} key | ||
* | ||
* @return {void} | ||
* | ||
* @public | ||
*/ | ||
delete(key) { | ||
@@ -46,2 +87,9 @@ assertKey(key) | ||
/** | ||
* Casts query params to a string | ||
* | ||
* @return {string} | ||
* | ||
* @public | ||
*/ | ||
toString() { | ||
@@ -51,3 +99,3 @@ const entries = [] | ||
for (const [key, value] of this) { | ||
// Omit all nullish keys because they're not necessary | ||
// Omit all nullish and falsy keys as of they aren't necessary | ||
if (value || value === 0) { | ||
@@ -58,7 +106,3 @@ entries.push([key, value]) | ||
// Use URLSearchParams with Query class here because looks like | ||
// it casts all given data types to string, but we need to save them | ||
// as we need more flexibility for query search parameters | ||
// For example: having an access to arrays as-is allows us to pass | ||
// more data into it. See Search#tags() method as an example | ||
// Use URLSearchParams to stringify entries | ||
return new URLSearchParams(entries).toString() | ||
@@ -65,0 +109,0 @@ } |
const Query = require("./Query") | ||
const {isArray} = Array | ||
/** | ||
@@ -42,3 +44,3 @@ * @api private | ||
*/ | ||
this._path = [path] | ||
this._path = isArray(path) ? path : [path] | ||
@@ -45,0 +47,0 @@ this.exec = this.exec.bind(this) |
@@ -1,2 +0,1 @@ | ||
const waterfall = require("./util/waterfall") | ||
const flat = require("./util/flat") | ||
@@ -6,3 +5,3 @@ | ||
const isArray = Array.isArray | ||
const {isArray} = Array | ||
@@ -17,13 +16,85 @@ /** | ||
* @param {Function} options.link | ||
* @param {string[] | string} [options.tags = []] | ||
* @param {string[] | string} [options.query = []] | ||
*/ | ||
constructor({dinky, link, tags = []}) { | ||
constructor({dinky, link, query = []}) { | ||
super({dinky, link, path: "search"}) | ||
if (tags.length > 0) { | ||
this.tags(...tags) | ||
if (query.length > 0) { | ||
this.query(...query) | ||
} | ||
this.__type = "images" | ||
} | ||
/** | ||
* Sets the search type to given value | ||
* | ||
* @param {string} searchType | ||
* | ||
* @return {Search} | ||
* | ||
* @private | ||
*/ | ||
__setType(searchType) { | ||
this.__type = searchType | ||
return this | ||
} | ||
/** | ||
* Sets Search type to "comments" | ||
* | ||
* @return {Search} | ||
* | ||
* @public | ||
*/ | ||
comments() { | ||
return this.__setType("comments") | ||
} | ||
/** | ||
* Sets Search type to "galleries" | ||
* | ||
* @return {Search} | ||
* | ||
* @public | ||
*/ | ||
galleries() { | ||
return this.__setType("galleries") | ||
} | ||
/** | ||
* Sets Search type to "posts" | ||
* | ||
* @return {Search} | ||
* | ||
* @public | ||
*/ | ||
posts() { | ||
return this.__setType("posts") | ||
} | ||
/** | ||
* Sets Search type to "tags" | ||
* | ||
* @return {Search} | ||
* | ||
* @public | ||
*/ | ||
tags() { | ||
return this.__setType("tags") | ||
} | ||
/** | ||
* Sets Search type to "images" | ||
* | ||
* @return {Search} | ||
* | ||
* @public | ||
*/ | ||
images() { | ||
return this.__setType("images") | ||
} | ||
/** | ||
* Appends a tag or a list of tags to the current search request | ||
@@ -36,3 +107,3 @@ * | ||
*/ | ||
tags(...list) { | ||
query(...list) { | ||
list = flat(list) | ||
@@ -64,5 +135,7 @@ | ||
* @return {Search} | ||
* | ||
* @public | ||
*/ | ||
faves() { | ||
return this.tags("my:faves") | ||
return this.query("my:faves") | ||
} | ||
@@ -76,5 +149,7 @@ | ||
* @return {Search} | ||
* | ||
* @public | ||
*/ | ||
watched() { | ||
return this.tags("my:watched") | ||
return this.query("my:watched") | ||
} | ||
@@ -88,5 +163,7 @@ | ||
* @return {Search} | ||
* | ||
* @public | ||
*/ | ||
upvotes() { | ||
return this.tags("my:upvotes") | ||
return this.query("my:upvotes") | ||
} | ||
@@ -100,5 +177,7 @@ | ||
* @return {Search} | ||
* | ||
* @public | ||
*/ | ||
downvotes() { | ||
return this.tags("my:downvotes") | ||
return this.query("my:downvotes") | ||
} | ||
@@ -112,5 +191,7 @@ | ||
* @return {Search} | ||
* | ||
* @public | ||
*/ | ||
uploads() { | ||
return this.tags("my:uploads") | ||
return this.query("my:uploads") | ||
} | ||
@@ -124,5 +205,7 @@ | ||
* @return {Search} | ||
* | ||
* @public | ||
*/ | ||
favedBy(user) { | ||
return this.tags(`faved_by:${user}`) | ||
return this.query(`faved_by:${user}`) | ||
} | ||
@@ -136,5 +219,7 @@ | ||
* @return {Search} | ||
* | ||
* @public | ||
*/ | ||
uploadedBy(user) { | ||
return this.tags(`uploader:${user}`) | ||
return this.query(`uploader:${user}`) | ||
} | ||
@@ -153,3 +238,3 @@ | ||
limit(value) { | ||
this._query.set("perpage", value) | ||
this._query.set("per_page", value) | ||
@@ -192,3 +277,3 @@ return this | ||
* | ||
* @return {Request} | ||
* @return {Search} | ||
* | ||
@@ -206,3 +291,3 @@ * @public | ||
* | ||
* @return {Request} | ||
* @return {Search} | ||
* | ||
@@ -218,3 +303,3 @@ * @public | ||
/** | ||
* Adds a param to surt result by given field. | ||
* Adds a param to sort result by given field. | ||
* | ||
@@ -224,2 +309,4 @@ * @param {string} field | ||
* @return {Search} | ||
* | ||
* @public | ||
*/ | ||
@@ -235,18 +322,8 @@ sortBy(field) { | ||
* | ||
* @return {Promise<object | undefined>} | ||
* @return {Search} | ||
* | ||
* @public | ||
*/ | ||
async random() { | ||
this._query.set("random_image", true) | ||
const resolve = image => { | ||
if (image && image.id != null) { | ||
return this._dinky.images().id(image.id) | ||
} | ||
return undefined | ||
} | ||
return waterfall([this.exec, resolve]) | ||
random() { | ||
return this.sortBy("random") | ||
} | ||
@@ -265,3 +342,3 @@ | ||
this._query.set("q", params.join(",")) | ||
} else if (this._query.has("random_image")) { | ||
} else if (this._query.get("sf") === "random") { | ||
// Add wildcard when searching for random image, | ||
@@ -272,27 +349,4 @@ // but no tags has been set | ||
const limit = this._query.get("perpage") | ||
if (limit != null) { | ||
// Turn off the rule because we need to check value of any type | ||
// eslint-disable-next-line no-restricted-globals | ||
if (isNaN(limit)) { | ||
throw new TypeError("You must specify the limit amount as number.") | ||
} | ||
this._path.push(this.__type) | ||
if (limit <= 1 || limit >= 50) { | ||
throw new RangeError("Limit must be a value in range between 1 and 50.") | ||
} | ||
} | ||
const min = this._query.get("min_score") | ||
// eslint-disable-next-line no-restricted-globals | ||
if (min != null && isNaN(min)) { | ||
throw new TypeError("You must specify minimal score as a number.") | ||
} | ||
const max = this._query.get("max_score") | ||
// eslint-disable-next-line no-restricted-globals | ||
if (max != null && isNaN(max)) { | ||
throw new TypeError("You must specify maximal score as a number.") | ||
} | ||
return super.exec(options) | ||
@@ -299,0 +353,0 @@ } |
@@ -1,11 +0,14 @@ | ||
const isArray = Array.isArray | ||
const {isArray} = Array | ||
/** | ||
* @api private | ||
*/ | ||
function flat(array, depth = 1) { | ||
const walk = (prev, next) => ( | ||
const step = (prev, next) => ( | ||
prev.concat(isArray(next) && depth > 0 ? flat(next, depth - 1) : [next]) | ||
) | ||
return array.reduce(walk, []) | ||
return array.reduce(step, []) | ||
} | ||
module.exports = flat |
@@ -0,3 +1,6 @@ | ||
/** | ||
* @api private | ||
*/ | ||
const isFunction = value => typeof value === "function" | ||
module.exports = isFunction |
@@ -0,3 +1,6 @@ | ||
/** | ||
* @api private | ||
*/ | ||
const isNumber = value => typeof value === "number" | ||
module.exports = isNumber |
@@ -0,1 +1,4 @@ | ||
/** | ||
* @api private | ||
*/ | ||
const getType = val => ( | ||
@@ -5,2 +8,5 @@ Object.prototype.toString.call(val).slice(8, -1).toLowerCase() | ||
/** | ||
* @api private | ||
*/ | ||
function isPlainObject(val) { | ||
@@ -7,0 +13,0 @@ if (getType(val) !== "object") { |
@@ -0,3 +1,6 @@ | ||
/** | ||
* @api private | ||
*/ | ||
const isString = value => typeof value === "string" | ||
module.exports = isString |
@@ -6,2 +6,3 @@ const {parse, format} = require("url") | ||
const cast = require("./castDates") | ||
const partial = require("./partial") | ||
@@ -12,4 +13,6 @@ const right = require("./partialRight") | ||
// TODO: Make version configurable | ||
const base = parse("https://derpibooru.org/api/v1/json") | ||
const toCamelCase = right(camelCase, {deep: true}) | ||
const base = parse("https://derpibooru.org") | ||
const defaults = { | ||
@@ -20,3 +23,6 @@ key: undefined, | ||
async function receiveData(response) { | ||
/** | ||
* @api private | ||
*/ | ||
function receiveData(response) { | ||
if (response.ok === false) { | ||
@@ -56,2 +62,3 @@ throw new NetworkError(`Network error: ${response.status}`, response) | ||
return async function link(path, search, params = {}) { | ||
path = [base.pathname, ...path].filter(Boolean) | ||
params = {...options, ...params} | ||
@@ -67,7 +74,7 @@ | ||
const pathname = `${path.join("/").replace(/\/{2,}/g, "/")}.json` | ||
const pathname = path.join("/").replace(/\/{2,}/g, "/") | ||
const url = format({...base, pathname, search: search.toString()}) | ||
const sendRequest = partial(fetch, url, {method: "get"}) | ||
return waterfall([sendRequest, receiveData, toCamelCase]) | ||
return waterfall([sendRequest, receiveData, toCamelCase, cast]) | ||
} | ||
@@ -74,0 +81,0 @@ } |
@@ -0,1 +1,4 @@ | ||
/** | ||
* @api public | ||
*/ | ||
class NetworkError extends Error { | ||
@@ -2,0 +5,0 @@ constructor(message, response) { |
{ | ||
"name": "dinky.js", | ||
"version": "0.8.1", | ||
"version": "1.0.0", | ||
"description": "JavaScript bindings for Derpibooru API", | ||
"main": "lib/Dinky", | ||
"repository": "https://github.com/octet-stream/dinky", | ||
"repository": "octet-stream/dinky", | ||
"author": "Nick K. <io@octetstream.me>", | ||
"license": "MIT", | ||
"engines": { | ||
"node": ">=8 < 9 || >= 10 < 11 || >= 12" | ||
"node": ">= 10 < 11 || >= 12" | ||
}, | ||
@@ -26,26 +26,20 @@ "keywords": [ | ||
}, | ||
"ava": { | ||
"files": [ | ||
"test/**/*.js", | ||
"!test/helper/**" | ||
] | ||
}, | ||
"devDependencies": { | ||
"@octetstream/eslint-config": "4.0.0", | ||
"ava": "2.4.0", | ||
"codecov": "3.6.1", | ||
"eslint": "6.6.0", | ||
"eslint-plugin-ava": "9.0.0", | ||
"eslint-plugin-markdown": "1.0.1", | ||
"fetch-mock": "8.0.0-alpha.14", | ||
"husky": "3.0.9", | ||
"lint-staged": "9.4.2", | ||
"nyc": "14.1.1", | ||
"ava": "3.5.1", | ||
"codecov": "3.6.5", | ||
"eslint": "6.8.0", | ||
"eslint-plugin-ava": "10.2.0", | ||
"eslint-plugin-markdown": "1.0.2", | ||
"fetch-mock": "9.3.1", | ||
"husky": "4.2.3", | ||
"lint-staged": "10.0.9", | ||
"nyc": "15.0.0", | ||
"proxyquire": "2.1.3", | ||
"sinon": "7.5.0" | ||
"sinon": "9.0.1" | ||
}, | ||
"dependencies": { | ||
"camelcase-keys": "6.1.0", | ||
"camelcase-keys": "6.2.1", | ||
"node-fetch": "2.6.0" | ||
} | ||
} |
133
readme.md
@@ -39,4 +39,4 @@ # dinky.js | ||
// The following request will return the 1th uploaded image from Derpibooru. | ||
// Equivalent to https://derpibooru.org/images/0.json request | ||
dinky().images().id(0).then(console.log) | ||
// Equivalent to https://derpibooru.org/api/v1/json/images/0 request | ||
dinky().images().getById(0).then(console.log) | ||
``` | ||
@@ -50,6 +50,5 @@ | ||
// You can specify tags right in the .search() method | ||
// The following example is equivalent of these requests: | ||
// https://derpibooru.org/search.json?q=artist:rainbow,safe&random_image=true | ||
// ...and then this one: | ||
// https://derpibooru.org/images/<received image id>.json | ||
// The following example is equivalent of this requests: | ||
// https://derpibooru.org/api/v1/json/search/images | ||
// ?q=artist:rainbow,safe&sf=random | ||
dinky().search(["artist:rainbow", "safe"]).random() | ||
@@ -66,11 +65,13 @@ .then(console.log) | ||
(async function() { | ||
const search = dinky() | ||
const random = dinky() | ||
.search(["scootaloo", "princess luna", "safe", "sleepless in ponyville"]) | ||
.minScore(200) | ||
.random() | ||
.limit(1) | ||
// Will search for random image with parameters from above | ||
await search.random() | ||
await random | ||
// ...and once more | ||
await search.random() | ||
await random | ||
}()).catch(console.error) | ||
@@ -138,22 +139,62 @@ ``` | ||
Creates a request handler for `/images.json` | ||
Creates a request handler for `/api/v1/json/images` | ||
##### `search([tags]) -> {Search}` | ||
##### `search([query]) -> {Search}` | ||
Creates a request handler for `/search.json`. This method takes a list of tags | ||
Creates a request handler for `/api/v1/json/search/images`. This method takes a list of query params | ||
- **{string | string[]}** [tags = []] – a tag or a list of tags and returns Search instance | ||
- **{string | string[]}** [query = []] – a tag or a list of query params and returns Search instance | ||
### `class Images > Request` | ||
### `class Images > Entities` | ||
##### `constructor() -> {Images}` | ||
Creates a request handler for `/images.json` | ||
Creates a request handler for `/api/v1/json/images` | ||
#### Instance methods | ||
##### `id(id) -> {Promise<object>}` | ||
##### `search([query]) -> {Search}` | ||
Creates a new Search request that points to `/api/v1/json/search/images`. | ||
##### `getById(id) -> {Promise<object>}` | ||
Returns an image with given ID | ||
##### `featured() -> {Promise<object>}` | ||
Returns featured image | ||
### `class Comments > Entities` | ||
##### `constructor() -> {Comments}` | ||
Creates a request handler for `/api/v1/json/comments`. | ||
#### Instance methods | ||
##### `search([query]) -> {Search}` | ||
Creates a new Search request that points to `/api/v1/json/search/comments`. | ||
##### `getById(id) -> {Promise<object>}` | ||
Returns a comment with given ID | ||
### `class Tags > Entities` | ||
##### `constructor() -> {Tags}` | ||
Creates a request handler for `/api/v1/json/tags`. | ||
#### Instance methods | ||
##### `search([query]) -> {Search}` | ||
Creates a new Search request that points to `/api/v1/json/search/tags`. | ||
##### `getById(id) -> {Promise<object>}` | ||
Returns a tag with given ID | ||
### `class Search > Request` | ||
@@ -163,15 +204,35 @@ | ||
Creates a request handler for `/search.json`. | ||
Creates a request handler for `/api/v1/json/search`. | ||
#### Instance methods | ||
##### `tags([list]) -> {Search}` | ||
##### `comments() -> {Search}` | ||
Appends a tag or a list of tags to the current search request. | ||
This method will not apply the `q=` parameter to the request for query when no tags has been set. | ||
Sets Search type to "comments" | ||
- **{string | string[]}** [list = []] – a tag or a list of tags you want to append | ||
##### `galleries() -> {Search}` | ||
##### `faves() -> {Seatch}` | ||
Sets Search type to "galleries" | ||
##### `posts() -> {Search}` | ||
Sets Search type to "posts" | ||
##### `tags() -> {Search}` | ||
Sets Search type to "tags" | ||
##### `images() -> {Search}` | ||
Sets Search type to "images" | ||
##### `query([list]) -> {Search}` | ||
Appends list of query params to the current search request. | ||
This method will not apply the `q=` parameter to if called without arguments. | ||
- **{string | string[]}** [list = []] – list of query params you want to append | ||
##### `faves() -> {Search}` | ||
Sets my:faves param to the search request. | ||
@@ -236,3 +297,3 @@ | ||
##### `random() -> {Promise<object>}` | ||
##### `random() -> {Search}` | ||
@@ -245,12 +306,4 @@ If been called, the API will return random image | ||
Creates a new Lists instance to give you ability to send requests to `/lists.json` | ||
and bunch of shortcuts to search for specific images categories on `/search.json` | ||
Provides a bunch of shortcuts for `Search` request | ||
##### `last(period) -> {Lists}` | ||
Creates a request that gets 3 images lists (top scoring, all time top scoring, top commented) | ||
for given period. | ||
- **{string}** period – Sampling period, specified in weeks, days, or hours | ||
##### `topScoring() -> {Search}` | ||
@@ -271,2 +324,18 @@ | ||
### `class Entities > Request` | ||
##### `constructor() -> {Entities}` | ||
Creates an Entity providing a few common methods for `Images`, `Comments` and `Tags` | ||
#### Instance methods | ||
##### `search([query]) -> {Search}` | ||
Creates a new Search request that points to `/api/v1/json/search`. | ||
##### `getById(id) -> {Promise<object>}` | ||
Finds an entity by given ID | ||
### `class Request` | ||
@@ -273,0 +342,0 @@ |
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
No repository
Supply chain riskPackage does not have a linked source code repository. Without this field, a package will have no reference to the location of the source code use to generate 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
No repository
Supply chain riskPackage does not have a linked source code repository. Without this field, a package will have no reference to the location of the source code use to generate the package.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
33144
27
957
1
399
+ Addedcamelcase-keys@6.2.1(transitive)
- Removedcamelcase-keys@6.1.0(transitive)
Updatedcamelcase-keys@6.2.1