Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

v8r

Package Overview
Dependencies
Maintainers
0
Versions
31
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

v8r - npm Package Compare versions

Comparing version 3.1.1 to 4.0.0

src/bootstrap.js

39

CHANGELOG.md
# Changelog
## 📦 [4.0.0](https://www.npmjs.com/package/v8r/v/4.0.0) - 2024-08-19
* **Breaking:** Change to the JSON output format. The `results` key is now an array instead of an object.
In v8r <4, `results` was an object mapping filename to result object. For example:
```json
{
"results": {
"./package.json": {
"fileLocation": "./package.json",
"schemaLocation": "https://json.schemastore.org/package.json",
"valid": true,
"errors": [],
"code": 0
}
}
}
```
In v8r >=4 `results` is now an array of result objects. For example:
```json
{
"results": [
{
"fileLocation": "./package.json",
"schemaLocation": "https://json.schemastore.org/package.json",
"valid": true,
"errors": [],
"code": 0
}
]
}
```
* Plugin system: It is now possible to extend the functionality of v8r by using or writing plugins. See https://chris48s.github.io/v8r/category/plugins/ for further information
* Documentation improvements
## 📦 [3.1.1](https://www.npmjs.com/package/v8r/v/3.1.1) - 2024-08-03

@@ -13,3 +48,3 @@

* Drop compatibility with node 16
* **Breaking:** Drop compatibility with node 16
* Add ability to validate Toml documents

@@ -23,3 +58,3 @@

* Drop compatibility with node 14
* **Breaking:** Drop compatibility with node 14
* Upgrade glob and minimatch to latest versions

@@ -26,0 +61,0 @@ * Tested on node 20

27

config-schema.json

@@ -52,10 +52,4 @@ {

"parser": {
"description": "A custom parser to use for files matching fileMatch instead of trying to infer the correct parser from the filename",
"type": "string",
"enum": [
"json",
"json5",
"toml",
"yaml"
]
"description": "A custom parser to use for files matching fileMatch instead of trying to infer the correct parser from the filename. 'json', 'json5', 'toml' and 'yaml' are always valid. Plugins may define additional values which are valid here.",
"type": "string"
}

@@ -68,8 +62,4 @@ }

"format": {
"description": "Output format for validation results",
"type": "string",
"enum": [
"text",
"json"
]
"description": "Output format for validation results. 'text' and 'json' are always valid. Plugins may define additional values which are valid here.",
"type": "string"
},

@@ -93,4 +83,13 @@ "ignoreErrors": {

"minimum": 0
},
"plugins": {
"type": "array",
"description": "An array of strings describing v8r plugins to load",
"uniqueItems": true,
"items": {
"type": "string",
"pattern": "^(package:|file:)"
}
}
}
}
{
"name": "v8r",
"version": "3.1.1",
"description": "A command-line JSON and YAML validator that's on your wavelength",
"version": "4.0.0",
"description": "A command-line JSON, YAML and TOML validator that's on your wavelength",
"scripts": {
"test": "V8R_CACHE_NAME=v8r-test c8 --reporter=text mocha \"src/**/*.spec.js\"",
"lint": "eslint \"**/*.{js,cjs}\"",
"lint": "eslint \"**/*.{js,cjs,mjs}\"",
"coverage": "c8 report --reporter=cobertura",
"prettier": "prettier --write \"**/*.{js,cjs}\"",
"prettier:check": "prettier --check \"**/*.{js,cjs}\"",
"prettier": "prettier --write \"**/*.{js,cjs,mjs}\"",
"prettier:check": "prettier --check \"**/*.{js,cjs,mjs}\"",
"v8r": "src/index.js"

@@ -16,4 +16,3 @@ },

},
"main": "src/index.js",
"exports": "./src/index.js",
"exports": "./src/public.js",
"files": [

@@ -51,10 +50,12 @@ "src/**/!(*.spec).js",

"c8": "^10.1.2",
"eslint": "^9.2.0",
"eslint": "^9.9.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-jsdoc": "^50.2.2",
"eslint-plugin-mocha": "^10.0.3",
"eslint-plugin-prettier": "^5.0.0",
"mocha": "^10.0.0",
"mocha": "^10.7.3",
"mock-cwd": "^1.0.0",
"nock": "^13.0.4",
"prettier": "^3.0.0"
"prettier": "^3.0.0",
"prettier-plugin-jsdoc": "^1.3.0"
},

@@ -61,0 +62,0 @@ "engines": {

# v8r
![build](https://raw.githubusercontent.com/chris48s/v8r/badges/.badges/main/build-status.svg)
![coverage](https://raw.githubusercontent.com/chris48s/v8r/badges/.badges/main/coverage.svg)
![version](https://raw.githubusercontent.com/chris48s/v8r/badges/.badges/main/package-version.svg)
![license](https://raw.githubusercontent.com/chris48s/v8r/badges/.badges/main/package-license.svg)
![node](https://raw.githubusercontent.com/chris48s/v8r/badges/.badges/main/package-node-version.svg)
[![build](https://raw.githubusercontent.com/chris48s/v8r/badges/.badges/main/build-status.svg)](https://github.com/chris48s/v8r/actions/workflows/build.yml?query=branch%3Amain)
[![coverage](https://raw.githubusercontent.com/chris48s/v8r/badges/.badges/main/coverage.svg)](https://app.codecov.io/gh/chris48s/v8r)
[![version](https://raw.githubusercontent.com/chris48s/v8r/badges/.badges/main/package-version.svg)](https://www.npmjs.com/package/v8r)
[![license](https://raw.githubusercontent.com/chris48s/v8r/badges/.badges/main/package-license.svg)](https://www.npmjs.com/package/v8r)
[![node](https://raw.githubusercontent.com/chris48s/v8r/badges/.badges/main/package-node-version.svg)](https://www.npmjs.com/package/v8r)
v8r is a command-line JSON and YAML validator that uses [Schema Store](https://www.schemastore.org/) to detect a suitable schema for your input files based on the filename.
v8r is a command-line validator that uses [Schema Store](https://www.schemastore.org/) to detect a suitable schema for your input files based on the filename.
## Getting Started
📦 Install the package from [NPM](https://www.npmjs.com/package/v8r)
One-off:
```bash
npx v8r@latest <filename>
```
Local install:
```bash
npm install -g v8r
v8r <filename>
```
## Usage Examples
### Validating files
v8r can validate JSON or YAML files. You can pass filenames or glob patterns:
```bash
# single filename
$ v8r package.json
# multiple files
$ v8r file1.json file2.json
# glob patterns
$ v8r 'dir/*.yml' 'dir/*.yaml'
```
[DigitalOcean's Glob Tool](https://www.digitalocean.com/community/tools/glob) can be used to help construct glob patterns
### Manually specifying a schema
By default, v8r queries [Schema Store](https://www.schemastore.org/) to detect a suitable schema based on the filename.
```bash
# if v8r can't auto-detect a schema for your file..
$ v8r feature.geojson
✖ Could not find a schema to validate feature.geojson
# ..you can specify one using the --schema flag
$ v8r feature.geojson --schema https://json.schemastore.org/geojson
ℹ Validating feature.geojson against schema from https://json.schemastore.org/geojson ...
✔ feature.geojson is valid
```
### Using a custom catlog
Using the `--schema` flag will validate all files matched by the glob pattern against that schema. You can also define a custom [schema catalog](https://json.schemastore.org/schema-catalog.json). v8r will search any custom catalogs before falling back to [Schema Store](https://www.schemastore.org/).
```bash
$ cat > my-catalog.json <<EOF
{ "\$schema": "https://json.schemastore.org/schema-catalog.json",
"version": 1,
"schemas": [ { "name": "geojson",
"description": "geojson",
"url": "https://json.schemastore.org/geojson.json",
"fileMatch": ["*.geojson"] } ] }
EOF
$ v8r feature.geojson -c my-catalog.json
ℹ Found schema in my-catalog.json ...
ℹ Validating feature.geojson against schema from https://json.schemastore.org/geojson ...
✔ feature.geojson is valid
```
This can be used to specify different custom schemas for multiple file patterns.
## Configuration
v8r uses CosmiConfig to search for a configuration. This means you can specify your configuration in any of the following places:
- `package.json`
- `.v8rrc`
- `.v8rrc.json`
- `.v8rrc.yaml`
- `.v8rrc.yml`
- `.v8rrc.js`
- `.v8rrc.cjs`
- `v8r.config.js`
- `v8r.config.cjs`
v8r only searches for a config file in the current working directory.
Example yaml config file:
```yaml
# - One or more filenames or glob patterns describing local file or files to validate
# - overridden by passing one or more positional arguments
patterns: ['*json']
# - Level of verbose logging. 0 is standard, higher numbers are more verbose
# - overridden by passing --verbose / -v
# - default = 0
verbose: 2
# - Exit with code 0 even if an error was encountered. True means a non-zero exit
# code is only issued if validation could be completed successfully and one or
# more files were invalid
# - overridden by passing --ignore-errors
# - default = false
ignoreErrors: true
# - Remove cached HTTP responses older than cacheTtl seconds old.
# Specifying 0 clears and disables cache completely
# - overridden by passing --cache-ttl
# - default = 600
cacheTtl: 86400
# - Output format for validation results
# - overridden by passing --format
# - default = text
format: "json"
# - A custom schema catalog.
# This catalog will be searched ahead of any custom catalogs passed using
# --catalogs or SchemaStore.org
# The format of this is subtly different to the format of a catalog
# passed via --catalogs (which matches the SchemaStore.org format)
customCatalog:
schemas:
- name: Custom Schema # The name of the schema (required)
description: Custom Schema # A description of the schema (optional)
# A Minimatch glob expression for matching up file names with a schema (required)
fileMatch: ["*.geojson"]
# A URL or local file path for the schema location (required)
# Unlike the SchemaStore.org format, which has a `url` key,
# custom catalogs defined in v8r config files have a `location` key
# which can refer to either a URL or local file.
# Relative paths are interpreted as relative to the config file location.
location: foo/bar/geojson-schema.json
# A custom parser to use for files matching fileMatch
# instead of trying to infer the correct parser from the filename (optional)
# This property is specific to custom catalogs defined in v8r config files
parser: json5
```
The config file format is specified more formally in a JSON Schema:
- [machine-readable JSON](config-schema.json)
- [human-readable HTML](https://json-schema-viewer.vercel.app/view?url=https%3A%2F%2Fraw.githubusercontent.com%2Fchris48s%2Fv8r%2Fmain%2Fconfig-schema.json&show_breadcrumbs=on&template_name=flat)
## Configuring a Proxy
It is possible to configure a proxy via [global-agent](https://www.npmjs.com/package/global-agent) using the `GLOBAL_AGENT_HTTP_PROXY` environment variable:
```bash
export GLOBAL_AGENT_HTTP_PROXY=http://myproxy:8888
```
## Exit codes
* v8r always exits with code `0` when:
* The input glob pattern(s) matched one or more files, all input files were validated against a schema, and all input files were **valid**
* `v8r` was called with `--help` or `--version` flags
* By default v8r exits with code `1` when an error was encountered trying to validate one or more input files. For example:
* No suitable schema could be found
* An error was encountered during an HTTP request
* An input file was not JSON or yaml
* etc
This behaviour can be modified using the `--ignore-errors` flag. When invoked with `--ignore-errors` v8r will exit with code `0` even if one of these errors was encountered while attempting validation. A non-zero exit code will only be issued if validation could be completed successfully and the file was invalid.
* v8r always exits with code `97` when:
* There was an error loading a config file
* A config file was loaded but failed validation
* v8r always exits with code `98` when:
* An input glob pattern was invalid
* An input glob pattern was valid but did not match any files
* v8r always exits with code `99` when:
* The input glob pattern matched one or more files, one or more input files were validated against a schema and the input file was **invalid**
## Versioning
v8r follows [semantic versioning](https://semver.org/). For this project, the "API" is defined as:
- CLI flags and options
- CLI exit codes
- The configuration file format
- The native JSON output format
A "breaking change" also includes:
- Dropping compatibility with a major Node JS version
- Dropping compatibility with a JSON Schema draft
## FAQ
### ❓ How does `v8r` decide what schema to validate against if I don't supply one?
💡 `v8r` queries the [Schema Store catalog](https://www.schemastore.org/) to try and find a suitable schema based on the name of the input file.
### ❓ My file is valid, but it doesn't validate against one of the suggested schemas.
💡 `v8r` is a fairly thin layer of glue between [Schema Store](https://www.schemastore.org/) (where the schemas come from) and [ajv](https://www.npmjs.com/package/ajv) (the validation engine). It is likely that this kind of problem is either an issue with the schema or validation engine.
* Schema store issue tracker: https://github.com/SchemaStore/schemastore/issues
* Ajv issue tracker: https://github.com/ajv-validator/ajv/issues
### ❓ What JSON schema versions are compatible?
💡 `v8r` works with JSON schema drafts:
* draft-04
* draft-06
* draft-07
* draft 2019-09
* draft 2020-12
### ❓ Will 100% of the schemas on schemastore.org work with this tool?
💡 No. There are some with [known issues](https://github.com/chris48s/v8r/issues/18)
### ❓ Can `v8r` validate against a local schema?
💡 Yes. The `--schema` flag can be either a path to a local file or a URL. You can also use a [config file](#configuration) to include local schemas in a custom catalog.
📚 Jump into the [Documentation](https://chris48s.github.io/v8r) to get started

@@ -7,10 +7,9 @@ import flatCache from "flat-cache";

import { validate } from "./ajv.js";
import { bootstrap } from "./bootstrap.js";
import { Cache } from "./cache.js";
import { getCatalogs, getMatchForFilename } from "./catalogs.js";
import { getConfig } from "./config.js";
import { getFiles } from "./glob.js";
import { getFromUrlOrFile } from "./io.js";
import logger from "./logger.js";
import { logErrors, resultsToJson } from "./output-formatters.js";
import { parseDocument } from "./parser.js";
import { parseFile } from "./parser.js";

@@ -20,3 +19,3 @@ const EXIT = {

ERROR: 1,
INVALID_CONFIG: 97,
INVALID_CONFIG_OR_PLUGIN: 97,
NOT_FOUND: 98,

@@ -39,3 +38,3 @@ INVALID: 99,

async function validateFile(filename, config, cache) {
async function validateFile(filename, config, plugins, cache) {
logger.info(`Processing ${filename}`);

@@ -61,5 +60,7 @@ let result = {

const data = parseDocument(
const data = parseFile(
plugins,
await fs.promises.readFile(filename, "utf8"),
catalogMatch.parser ? `.${catalogMatch.parser}` : path.extname(filename),
filename,
catalogMatch.parser,
);

@@ -108,3 +109,3 @@

function Validator() {
return async function (config) {
return async function (config, plugins) {
let filenames = [];

@@ -123,10 +124,18 @@ for (const pattern of config.patterns) {

const results = Object.fromEntries(filenames.map((key) => [key, null]));
for (const [filename] of Object.entries(results)) {
results[filename] = await validateFile(filename, config, cache);
let results = [];
for (const filename of filenames) {
const result = await validateFile(filename, config, plugins, cache);
results.push(result);
if (results[filename].valid === false && config.format === "text") {
logErrors(filename, results[filename].errors);
for (const plugin of plugins) {
const message = plugin.getSingleResultLogMessage(
result,
filename,
config.format,
);
if (message != null) {
logger.log(message);
break;
}
}
// else: silence is golden

@@ -136,4 +145,8 @@ cache.resetCounters();

if (config.format === "json") {
resultsToJson(results);
for (const plugin of plugins) {
const message = plugin.getAllResultsLogMessage(results, config.format);
if (message != null) {
logger.log(message);
break;
}
}

@@ -146,9 +159,9 @@

async function cli(config) {
if (!config) {
try {
config = await getConfig(process.argv);
} catch (e) {
logger.error(e.message);
return EXIT.INVALID_CONFIG;
}
let allLoadedPlugins, loadedCorePlugins, loadedUserPlugins;
try {
({ config, allLoadedPlugins, loadedCorePlugins, loadedUserPlugins } =
await bootstrap(process.argv, config));
} catch (e) {
logger.error(e.message);
return EXIT.INVALID_CONFIG_OR_PLUGIN;
}

@@ -159,5 +172,25 @@

/*
Note there is a bit of a chicken and egg problem here.
We have to load the plugins before we can load the config
but this logger.debug() needs to happen AFTER we call logger.setVerbosity().
*/
logger.debug(
`Loaded user plugins: ${JSON.stringify(
loadedUserPlugins.map((plugin) => plugin.constructor.name),
null,
2,
)}`,
);
logger.debug(
`Loaded core plugins: ${JSON.stringify(
loadedCorePlugins.map((plugin) => plugin.constructor.name),
null,
2,
)}`,
);
try {
const validate = new Validator();
return await validate(config);
return await validate(config, allLoadedPlugins);
} catch (e) {

@@ -164,0 +197,0 @@ logger.error(e.message);

@@ -6,3 +6,5 @@ import { glob } from "glob";

try {
return await glob(pattern, { dot: true, dotRelative: true });
let matches = await glob(pattern, { dot: true, dotRelative: true });
matches.sort((a, b) => a.localeCompare(b));
return matches;
} catch (e) {

@@ -9,0 +11,0 @@ logger.error(e.message);

import Ajv from "ajv";
import logger from "./logger.js";
function logErrors(filename, errors) {
function formatErrors(filename, errors) {
const ajv = new Ajv();
logger.log(
return (
ajv.errorsText(errors, {
separator: "\n",
dataVar: filename + "#",
}),
}) + "\n"
);
logger.log("");
}
function resultsToJson(results) {
logger.log(JSON.stringify({ results }, null, 2));
}
export { logErrors, resultsToJson };
export { formatErrors };

@@ -1,22 +0,22 @@

import JSON5 from "json5";
import path from "path";
import yaml from "js-yaml";
import { parse } from "smol-toml";
import { Document } from "./plugins.js";
function parseDocument(contents, format) {
switch (format) {
case ".json":
case ".geojson":
case ".jsonld":
return JSON.parse(contents);
case ".jsonc":
case ".json5":
return JSON5.parse(contents);
case ".yml":
case ".yaml":
return yaml.load(contents);
case ".toml":
return parse(contents);
default:
throw new Error(`Unsupported format ${format}`);
function parseFile(plugins, contents, filename, parser) {
for (const plugin of plugins) {
const result = plugin.parseInputFile(contents, filename, parser);
if (result != null) {
if (!(result instanceof Document)) {
throw new Error(
`Plugin ${plugin.constructor.name} returned an unexpected type from parseInputFile hook. Expected Document, got ${typeof result}`,
);
}
return result.document;
}
}
const errorMessage = parser
? `Unsupported format ${parser}`
: `Unsupported format ${path.extname(filename).slice(1)}`;
throw new Error(errorMessage);
}

@@ -36,2 +36,2 @@

export { parseDocument, parseSchema };
export { parseFile, parseSchema };
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