v8r 5.1.0 → 6.0.0

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.
package/CHANGELOG.md CHANGED
@@ -1,9 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## 📦 [6.0.0](https://www.npmjs.com/package/v8r/v/6.0.0) - 2026-02-21
4
+
5
+ * **Breaking:** Drop compatibility with node 20
6
+ * This release adds a new installation option: standalone binaries.
7
+ Moving forwards, standalone binaries for Linux, Windows and MacOS available
8
+ for download from https://github.com/chris48s/v8r/releases/latest .
9
+ * Upgrade to latest major versions of core packages (glob, minimatch, p-limit)
10
+
3
11
  ## 📦 [5.1.0](https://www.npmjs.com/package/v8r/v/5.1.0) - 2025-07-20
4
12
 
5
13
  * v8r now pre-warms the cache and fetches schemas in parallel.
6
- This will improve decrease total run time for any run that involves fetching
14
+ This will decrease total run time for any run that involves fetching
7
15
  more than one remote schema, or involves a schema with remote `$ref`s.
8
16
  * Improve handling of empty yaml files.
9
17
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "v8r",
3
- "version": "5.1.0",
3
+ "version": "6.0.0",
4
4
  "description": "A command-line JSON, YAML and TOML validator that's on your wavelength",
5
5
  "scripts": {
6
6
  "test": "V8R_CACHE_NAME=v8r-test c8 --reporter=text mocha \"src/**/*.spec.js\"",
@@ -8,7 +8,9 @@
8
8
  "coverage": "c8 report --reporter=cobertura",
9
9
  "prettier": "prettier --write \"**/*.{js,cjs,mjs}\"",
10
10
  "prettier:check": "prettier --check \"**/*.{js,cjs,mjs}\"",
11
- "v8r": "src/index.js"
11
+ "v8r": "src/index.js",
12
+ "build:bin": "node sea/build.js && $npm_node_execpath --build-sea sea/config.json",
13
+ "build:exe": "node sea/build.js && %npm_node_execpath% --build-sea sea/config-win.json"
12
14
  },
13
15
  "bin": {
14
16
  "v8r": "src/index.js"
@@ -34,7 +36,7 @@
34
36
  "cosmiconfig": "^9.0.0",
35
37
  "decamelize": "^6.0.0",
36
38
  "flat-cache": "^6.1.4",
37
- "glob": "^11.0.0",
39
+ "glob": "^13.0.0",
38
40
  "global-agent": "^3.0.0",
39
41
  "got": "^14.0.0",
40
42
  "ignore": "^7.0.0",
@@ -42,16 +44,18 @@
42
44
  "js-yaml": "^4.0.0",
43
45
  "json5": "^2.2.0",
44
46
  "minimatch": "^10.0.0",
45
- "p-limit": "^6.2.0",
47
+ "p-limit": "^7.0.0",
46
48
  "p-mutex": "^1.0.0",
47
49
  "smol-toml": "^1.0.1",
48
50
  "yargs": "^18.0.0"
49
51
  },
50
52
  "devDependencies": {
53
+ "@eslint/js": "^10.0.1",
51
54
  "c8": "^10.1.2",
52
- "eslint": "^9.9.0",
55
+ "esbuild": "^0.27.3",
56
+ "eslint": "^10.0.1",
53
57
  "eslint-config-prettier": "^10.1.2",
54
- "eslint-plugin-jsdoc": "^51.4.1",
58
+ "eslint-plugin-jsdoc": "^62.7.0",
55
59
  "eslint-plugin-mocha": "^11.0.0",
56
60
  "eslint-plugin-prettier": "^5.0.0",
57
61
  "mocha": "^11.0.1",
@@ -61,7 +65,7 @@
61
65
  "prettier-plugin-jsdoc": "^1.3.0"
62
66
  },
63
67
  "engines": {
64
- "node": ">=20"
68
+ "node": ">=22"
65
69
  },
66
70
  "type": "module",
67
71
  "keywords": [
package/src/ajv.js CHANGED
@@ -1,13 +1,9 @@
1
- import { createRequire } from "node:module";
2
- // TODO: once JSON modules is stable these requires could become imports
3
- // https://nodejs.org/api/esm.html#esm_experimental_json_modules
4
- const require = createRequire(import.meta.url);
5
-
6
1
  import AjvDraft4 from "ajv-draft-04";
7
2
  import Ajv from "ajv";
8
3
  import Ajv2019 from "ajv/dist/2019.js";
9
4
  import Ajv2020 from "ajv/dist/2020.js";
10
5
  import addFormats from "ajv-formats";
6
+ import draft06 from "ajv/lib/refs/json-schema-draft-06.json" with { type: "json" };
11
7
 
12
8
  function _ajvFactory(
13
9
  schema,
@@ -26,9 +22,7 @@ function _ajvFactory(
26
22
  return new AjvDraft4(opts);
27
23
  } else if (schema["$schema"].includes("json-schema.org/draft-06/schema")) {
28
24
  const ajvDraft06 = new Ajv(opts);
29
- ajvDraft06.addMetaSchema(
30
- require("ajv/lib/refs/json-schema-draft-06.json"),
31
- );
25
+ ajvDraft06.addMetaSchema(draft06);
32
26
  return ajvDraft06;
33
27
  } else if (schema["$schema"].includes("json-schema.org/draft-07/schema")) {
34
28
  return new Ajv(opts);
@@ -45,7 +39,7 @@ function _ajvFactory(
45
39
 
46
40
  // hedge our bets as best we can
47
41
  const ajv = new Ajv(opts);
48
- ajv.addMetaSchema(require("ajv/lib/refs/json-schema-draft-06.json"));
42
+ ajv.addMetaSchema(draft06);
49
43
  return ajv;
50
44
 
51
45
  /* TODO:
package/src/bootstrap.js CHANGED
@@ -1,8 +1,3 @@
1
- import { createRequire } from "node:module";
2
- // TODO: once JSON modules is stable these requires could become imports
3
- // https://nodejs.org/api/esm.html#esm_experimental_json_modules
4
- const require = createRequire(import.meta.url);
5
-
6
1
  import fs from "node:fs";
7
2
  import path from "node:path";
8
3
  import { cosmiconfig } from "cosmiconfig";
@@ -15,7 +10,8 @@ import {
15
10
  validateConfigOutputFormats,
16
11
  } from "./config-validators.js";
17
12
  import logger from "./logger.js";
18
- import { loadAllPlugins, resolveUserPlugins } from "./plugins.js";
13
+ import { loadAllPlugins, resolveUserPlugins } from "./plugin-loader.js";
14
+ import manifest from "../package.json" with { type: "json" };
19
15
 
20
16
  async function getCosmiConfig(cosmiconfigOptions) {
21
17
  let configFile;
@@ -127,7 +123,7 @@ function parseArgs(argv, config, documentFormats, outputFormats) {
127
123
  .version(
128
124
  // Workaround for https://github.com/yargs/yargs/issues/1934
129
125
  // TODO: remove once fixed
130
- require("../package.json").version,
126
+ manifest.version,
131
127
  )
132
128
  .option("verbose", {
133
129
  alias: "v",
package/src/cache.js CHANGED
@@ -73,9 +73,11 @@ class Cache {
73
73
  return parsedBody;
74
74
  } catch (error) {
75
75
  if (error.response) {
76
- throw new Error(`Failed fetching ${url}\n${error.response.body}`);
76
+ throw new Error(`Failed fetching ${url}\n${error.response.body}`, {
77
+ cause: error,
78
+ });
77
79
  }
78
- throw new Error(`Failed fetching ${url}`);
80
+ throw new Error(`Failed fetching ${url}`, { cause: error });
79
81
  }
80
82
  }
81
83
 
package/src/cli.js CHANGED
@@ -169,7 +169,7 @@ function resultsToStatusCode(results, ignoreErrors) {
169
169
 
170
170
  function Validator() {
171
171
  return async function (config, plugins) {
172
- let filenames = [];
172
+ let filenames;
173
173
  try {
174
174
  filenames = await getFiles(config.patterns, config.ignorePatternFiles);
175
175
  } catch (e) {
@@ -1,16 +1,11 @@
1
- import { createRequire } from "node:module";
2
- // TODO: once JSON modules is stable these requires could become imports
3
- // https://nodejs.org/api/esm.html#esm_experimental_json_modules
4
- const require = createRequire(import.meta.url);
5
-
6
1
  import Ajv2019 from "ajv/dist/2019.js";
7
2
  import logger from "./logger.js";
8
3
  import { formatErrors } from "./output-formatters.js";
4
+ import configSchema from "../config-schema.json" with { type: "json" };
9
5
 
10
6
  function validateConfigAgainstSchema(configFile) {
11
7
  const ajv = new Ajv2019({ allErrors: true, strict: false });
12
- const schema = require("../config-schema.json");
13
- const validateFn = ajv.compile(schema);
8
+ const validateFn = ajv.compile(configSchema);
14
9
  const valid = validateFn(configFile.config);
15
10
  if (!valid) {
16
11
  logger.log(
package/src/index.js CHANGED
@@ -5,5 +5,4 @@ import { cli } from "./cli.js";
5
5
  import { bootstrap } from "global-agent";
6
6
  bootstrap();
7
7
 
8
- const exitCode = await cli();
9
- process.exit(exitCode);
8
+ cli().then((exitCode) => process.exit(exitCode));
@@ -0,0 +1,99 @@
1
+ import path from "node:path";
2
+ import { isSea } from "node:sea";
3
+ import logger from "./logger.js";
4
+ import { BasePlugin } from "./plugins.js";
5
+ import ParserJson from "./plugins/parser-json.js";
6
+ import ParserJson5 from "./plugins/parser-json5.js";
7
+ import ParserToml from "./plugins/parser-toml.js";
8
+ import ParserYaml from "./plugins/parser-yaml.js";
9
+ import OutputText from "./plugins/output-text.js";
10
+ import OutputJson from "./plugins/output-json.js";
11
+
12
+ function validatePlugin(plugin) {
13
+ if (
14
+ typeof plugin.name !== "string" ||
15
+ !plugin.name.startsWith("v8r-plugin-")
16
+ ) {
17
+ throw new Error(`Plugin ${plugin.name} does not declare a valid name`);
18
+ }
19
+
20
+ if (!(plugin.prototype instanceof BasePlugin)) {
21
+ throw new Error(`Plugin ${plugin.name} does not extend BasePlugin`);
22
+ }
23
+
24
+ for (const prop of Object.getOwnPropertyNames(BasePlugin.prototype)) {
25
+ const method = plugin.prototype[prop];
26
+ const argCount = plugin.prototype[prop].length;
27
+ if (typeof method !== "function") {
28
+ throw new Error(
29
+ `Error loading plugin ${plugin.name}: must have a method called ${method}`,
30
+ );
31
+ }
32
+ const expectedArgs = BasePlugin.prototype[prop].length;
33
+ if (expectedArgs !== argCount) {
34
+ throw new Error(
35
+ `Error loading plugin ${plugin.name}: ${prop} must take exactly ${expectedArgs} arguments`,
36
+ );
37
+ }
38
+ }
39
+ }
40
+
41
+ function resolveUserPlugins(userPlugins) {
42
+ let plugins = [];
43
+ for (let plugin of userPlugins) {
44
+ if (plugin.startsWith("package:")) {
45
+ plugins.push(plugin.slice(8));
46
+ }
47
+ if (plugin.startsWith("file:")) {
48
+ plugins.push(path.resolve(process.cwd(), plugin.slice(5)));
49
+ }
50
+ }
51
+ return plugins;
52
+ }
53
+
54
+ function loadCorePlugins(plugins) {
55
+ plugins.forEach((plugin) => validatePlugin(plugin));
56
+ const loadedPlugins = plugins.map((plugin) => new plugin());
57
+ return loadedPlugins;
58
+ }
59
+
60
+ async function loadUserPlugins(plugins) {
61
+ let loadedPlugins = [];
62
+ for (const plugin of plugins) {
63
+ loadedPlugins.push(await import(plugin));
64
+ }
65
+ loadedPlugins = loadedPlugins.map((plugin) => plugin.default);
66
+
67
+ loadedPlugins.forEach((plugin) => validatePlugin(plugin));
68
+ loadedPlugins = loadedPlugins.map((plugin) => new plugin());
69
+ return loadedPlugins;
70
+ }
71
+
72
+ async function loadAllPlugins(userPlugins) {
73
+ let loadedUserPlugins = [];
74
+ if (isSea() && userPlugins.length > 0) {
75
+ logger.warning(
76
+ "loading plugins is not supported when running as a standalone binary",
77
+ );
78
+ } else {
79
+ loadedUserPlugins = await loadUserPlugins(userPlugins);
80
+ }
81
+
82
+ const corePlugins = [
83
+ ParserJson,
84
+ ParserJson5,
85
+ ParserToml,
86
+ ParserYaml,
87
+ OutputText,
88
+ OutputJson,
89
+ ];
90
+ const loadedCorePlugins = loadCorePlugins(corePlugins);
91
+
92
+ return {
93
+ allLoadedPlugins: loadedUserPlugins.concat(loadedCorePlugins),
94
+ loadedCorePlugins,
95
+ loadedUserPlugins,
96
+ };
97
+ }
98
+
99
+ export { loadAllPlugins, resolveUserPlugins };
package/src/plugins.js CHANGED
@@ -1,5 +1,3 @@
1
- import path from "node:path";
2
-
3
1
  /**
4
2
  * Base class for all v8r plugins.
5
3
  *
@@ -105,90 +103,19 @@ class BasePlugin {
105
103
  }
106
104
 
107
105
  class Document {
106
+ /* eslint-disable jsdoc/reject-any-type */
108
107
  /**
109
108
  * Document is a thin wrapper class for a document we want to validate after
110
109
  * parsing a file
111
110
  *
112
111
  * @param {any} document - The object to be wrapped
113
112
  */
113
+ /* eslint-enable jsdoc/reject-any-type */
114
114
  constructor(document) {
115
115
  this.document = document;
116
116
  }
117
117
  }
118
118
 
119
- function validatePlugin(plugin) {
120
- if (
121
- typeof plugin.name !== "string" ||
122
- !plugin.name.startsWith("v8r-plugin-")
123
- ) {
124
- throw new Error(`Plugin ${plugin.name} does not declare a valid name`);
125
- }
126
-
127
- if (!(plugin.prototype instanceof BasePlugin)) {
128
- throw new Error(`Plugin ${plugin.name} does not extend BasePlugin`);
129
- }
130
-
131
- for (const prop of Object.getOwnPropertyNames(BasePlugin.prototype)) {
132
- const method = plugin.prototype[prop];
133
- const argCount = plugin.prototype[prop].length;
134
- if (typeof method !== "function") {
135
- throw new Error(
136
- `Error loading plugin ${plugin.name}: must have a method called ${method}`,
137
- );
138
- }
139
- const expectedArgs = BasePlugin.prototype[prop].length;
140
- if (expectedArgs !== argCount) {
141
- throw new Error(
142
- `Error loading plugin ${plugin.name}: ${prop} must take exactly ${expectedArgs} arguments`,
143
- );
144
- }
145
- }
146
- }
147
-
148
- function resolveUserPlugins(userPlugins) {
149
- let plugins = [];
150
- for (let plugin of userPlugins) {
151
- if (plugin.startsWith("package:")) {
152
- plugins.push(plugin.slice(8));
153
- }
154
- if (plugin.startsWith("file:")) {
155
- plugins.push(path.resolve(process.cwd(), plugin.slice(5)));
156
- }
157
- }
158
- return plugins;
159
- }
160
-
161
- async function loadPlugins(plugins) {
162
- let loadedPlugins = [];
163
- for (const plugin of plugins) {
164
- loadedPlugins.push(await import(plugin));
165
- }
166
- loadedPlugins = loadedPlugins.map((plugin) => plugin.default);
167
- loadedPlugins.forEach((plugin) => validatePlugin(plugin));
168
- loadedPlugins = loadedPlugins.map((plugin) => new plugin());
169
- return loadedPlugins;
170
- }
171
-
172
- async function loadAllPlugins(userPlugins) {
173
- const loadedUserPlugins = await loadPlugins(userPlugins);
174
-
175
- const corePlugins = [
176
- "./plugins/parser-json.js",
177
- "./plugins/parser-json5.js",
178
- "./plugins/parser-toml.js",
179
- "./plugins/parser-yaml.js",
180
- "./plugins/output-text.js",
181
- "./plugins/output-json.js",
182
- ];
183
- const loadedCorePlugins = await loadPlugins(corePlugins);
184
-
185
- return {
186
- allLoadedPlugins: loadedUserPlugins.concat(loadedCorePlugins),
187
- loadedCorePlugins,
188
- loadedUserPlugins,
189
- };
190
- }
191
-
192
119
  /**
193
120
  * @typedef {object} ValidationResult
194
121
  * @property {string} fileLocation - Path of the document that was validated.
@@ -213,4 +140,4 @@ async function loadAllPlugins(userPlugins) {
213
140
  * @see https://ajv.js.org/api.html#error-objects
214
141
  */
215
142
 
216
- export { BasePlugin, Document, loadAllPlugins, resolveUserPlugins };
143
+ export { BasePlugin, Document };