@percy/config
Advanced tools
Comparing version 1.12.0 to 1.13.0
@@ -9,5 +9,6 @@ import { merge } from './utils/index.js'; | ||
entries | ||
} = Object; // Recursively walks a schema and collects defaults. When no schema is provided, | ||
} = Object; | ||
// Recursively walks a schema and collects defaults. When no schema is provided, | ||
// the default config schema is used. | ||
function getDefaultsFromSchema(schema) { | ||
@@ -32,3 +33,2 @@ if (!schema || typeof schema.$ref === 'string') { | ||
} | ||
export function getDefaults(overrides = {}) { | ||
@@ -35,0 +35,0 @@ return merge([getDefaultsFromSchema(), overrides], (path, prev, next) => { |
@@ -5,6 +5,8 @@ import load, { search } from './load.js'; | ||
import { merge, normalize, stringify } from './utils/index.js'; | ||
import getDefaults from './defaults.js'; // public config API | ||
import getDefaults from './defaults.js'; | ||
export { load, search, validate, addSchema, migrate, addMigration, getDefaults, merge, normalize, stringify }; // export the namespace by default | ||
// public config API | ||
export { load, search, validate, addSchema, migrate, addMigration, getDefaults, merge, normalize, stringify }; | ||
// export the namespace by default | ||
export * as default from './index.js'; |
@@ -8,11 +8,14 @@ import fs from 'fs'; | ||
import getDefaults from './defaults.js'; | ||
import { inspect, normalize } from './utils/index.js'; // Loaded configuration file cache | ||
import { inspect, normalize } from './utils/index.js'; | ||
export const cache = new Map(); // The cosmiconfig explorer used to load config files | ||
// Loaded configuration file cache | ||
export const cache = new Map(); | ||
// The cosmiconfig explorer used to load config files | ||
export const explorer = cosmiconfigSync('percy', { | ||
cache: false, | ||
searchPlaces: ['package.json', '.percyrc', '.percy.json', '.percy.yaml', '.percy.yml', '.percy.js', '.percy.cjs', 'percy.config.js', 'percy.config.cjs'] | ||
}); // Searches within a provided directory, or loads the provided config path | ||
}); | ||
// Searches within a provided directory, or loads the provided config path | ||
export function search(path) { | ||
@@ -25,3 +28,5 @@ try { | ||
} | ||
} // Finds and loads a config file using cosmiconfig, merges it with optional | ||
} | ||
// Finds and loads a config file using cosmiconfig, merges it with optional | ||
// inputs, validates the combined config according to the schema, and returns | ||
@@ -34,3 +39,2 @@ // the combined config. Loaded config files are cached and reused on next load, | ||
// versions to the latest version while printing a warning. | ||
export function load({ | ||
@@ -44,3 +48,2 @@ path, | ||
var _Array$from; | ||
// load cached config; when no path is specified, get the last config cached | ||
@@ -50,12 +53,11 @@ let config = path ? cache.get(path) : (_Array$from = Array.from(cache)[cache.size - 1]) === null || _Array$from === void 0 ? void 0 : _Array$from[1]; | ||
let errorDebug = print ? 'error' : 'debug'; | ||
let log = logger('config'); // load config or reload cached config | ||
let log = logger('config'); | ||
// load config or reload cached config | ||
if (path !== false && (!config || reload)) { | ||
try { | ||
let result = search(path); | ||
if (result !== null && result !== void 0 && result.config) { | ||
log[infoDebug](`Found config file: ${relative('', result.filepath)}`); | ||
let version = parseInt(result.config.version, 10); | ||
if (Number.isNaN(version)) { | ||
@@ -69,3 +71,2 @@ log.warn('Ignoring config file - missing or invalid version'); | ||
} | ||
config = migrate(result.config); | ||
@@ -82,5 +83,5 @@ cache.set(path, config); | ||
} | ||
} // normalize and merge with overrides then validate | ||
} | ||
// normalize and merge with overrides then validate | ||
config = normalize(config, { | ||
@@ -91,18 +92,14 @@ overrides, | ||
let errors = config && validate(config); | ||
if (errors) { | ||
log.warn('Invalid config:'); | ||
for (let e of errors) log.warn(`- ${e.path}: ${e.message}`); | ||
if (bail) return; | ||
} | ||
if (path !== false && config) { | ||
log[infoDebug](`Using config:\n${inspect(config)}`); | ||
} // merge with defaults | ||
} | ||
// merge with defaults | ||
return getDefaults(config); | ||
} | ||
export default load; |
import logger from '@percy/logger'; | ||
import { get, set, del, map, joinPropertyPath, normalize } from './utils/index.js'; // Global set of registered migrations | ||
import { get, set, del, map, joinPropertyPath, normalize } from './utils/index.js'; | ||
const migrations = new Map(); // Register a migration function for the main config schema by default | ||
// Global set of registered migrations | ||
const migrations = new Map(); | ||
// Register a migration function for the main config schema by default | ||
export function addMigration(migration, schema = '/config') { | ||
@@ -12,15 +14,15 @@ if (Array.isArray(migration)) { | ||
} | ||
if (!migrations.has(schema)) migrations.set(schema, []); | ||
migrations.get(schema).unshift(migration); | ||
return migration; | ||
} // Clear all migration functions | ||
} | ||
// Clear all migration functions | ||
export function clearMigrations() { | ||
migrations.clear(); | ||
addMigration(defaultMigration); | ||
} // The default config migration | ||
} | ||
// The default config migration | ||
addMigration(defaultMigration); | ||
function defaultMigration(config, { | ||
@@ -30,5 +32,5 @@ set | ||
if (config.version !== 2) set('version', 2); | ||
} // Migrate util for deprecated options | ||
} | ||
// Migrate util for deprecated options | ||
function deprecate(config, log, path, options) { | ||
@@ -48,6 +50,6 @@ if (get(config, path) == null) return; | ||
return to ? map(config, path, to) : config; | ||
} // Calls each registered migration function with a normalize provided config | ||
} | ||
// Calls each registered migration function with a normalize provided config | ||
// and util functions for working with the config object | ||
export function migrate(config, schema = '/config') { | ||
@@ -57,3 +59,2 @@ config = normalize(config, { | ||
}) ?? {}; | ||
if (migrations.has(schema)) { | ||
@@ -68,8 +69,7 @@ let log = logger('config'); | ||
}; | ||
for (let migration of migrations.get(schema)) { | ||
migration(config, util); | ||
} // normalize again to adjust for migrations | ||
} | ||
// normalize again to adjust for migrations | ||
config = normalize(config, { | ||
@@ -79,5 +79,4 @@ schema | ||
} | ||
return config; | ||
} | ||
export default migrate; |
@@ -9,19 +9,21 @@ const { | ||
entries | ||
} = Object; // Creates an empty object or array | ||
} = Object; | ||
// Creates an empty object or array | ||
function create(array) { | ||
return array ? [] : {}; | ||
} // Returns true or false if a subject has iterable keys or not | ||
} | ||
// Returns true or false if a subject has iterable keys or not | ||
function hasKeys(subj) { | ||
return isArray(subj) || Object.getPrototypeOf(subj) === Object.prototype; | ||
} // Returns true if the provided key looks like an array key | ||
} | ||
// Returns true if the provided key looks like an array key | ||
const ARRAY_PATH_KEY_REG = /^(\[\d+]|0|[1-9]\d*)$/; | ||
export function isArrayKey(key) { | ||
return isInteger(key) || ARRAY_PATH_KEY_REG.test(key); | ||
} // Split a property path string by dot or array notation | ||
} | ||
// Split a property path string by dot or array notation | ||
export function parsePropertyPath(path) { | ||
@@ -34,21 +36,21 @@ return isArray(path) ? path : path.split('.').reduce((full, part) => { | ||
}, []); | ||
} // Join an array of path parts into a single path string | ||
} | ||
// Join an array of path parts into a single path string | ||
export function joinPropertyPath(path) { | ||
path = !Array.isArray(path) ? path : path.filter(Boolean).map(k => isArrayKey(k) ? `[${k}]` : `.${k}`).join(''); | ||
while ((_path = path) !== null && _path !== void 0 && _path.startsWith('.')) { | ||
var _path; | ||
path = path.substr(1); | ||
} | ||
return path; | ||
} // Gets a value in the object at the path | ||
} | ||
// Gets a value in the object at the path | ||
export function get(object, path, find) { | ||
return parsePropertyPath(path).reduce((target, key) => target === null || target === void 0 ? void 0 : target[key], object); | ||
} // Sets a value to the object at the path creating any necessary nested | ||
} | ||
// Sets a value to the object at the path creating any necessary nested | ||
// objects or arrays along the way | ||
export function set(object, path, value) { | ||
@@ -64,4 +66,5 @@ return parsePropertyPath(path).reduce((target, key, index, path) => { | ||
}, object); | ||
} // Deletes properties from an object at the paths | ||
} | ||
// Deletes properties from an object at the paths | ||
export function del(object, ...paths) { | ||
@@ -78,16 +81,16 @@ return paths.reduce((object, path) => { | ||
}, object); | ||
} // Maps a value from one path to another, deleting the first path | ||
} | ||
// Maps a value from one path to another, deleting the first path | ||
export function map(object, from, to, transform = v => v) { | ||
return set(object, to, transform(parsePropertyPath(from).reduce((target, key, index, path) => { | ||
let value = target === null || target === void 0 ? void 0 : target[key]; | ||
if (index === path.length - 1) { | ||
target === null || target === void 0 ? true : delete target[key]; | ||
} | ||
return value; | ||
}, object))); | ||
} // Steps through an object's properties calling the function with the path and value of each | ||
} | ||
// Steps through an object's properties calling the function with the path and value of each | ||
function walk(object, fn, path = [], visited = new Set()) { | ||
@@ -97,6 +100,4 @@ if (path.length && fn([...path], object) === false) return; | ||
visited.add(object); | ||
if (object != null && typeof object === 'object') { | ||
let isArrayObject = isArray(object); | ||
for (let [key, value] of entries(object)) { | ||
@@ -107,5 +108,5 @@ if (isArrayObject) key = parseInt(key, 10); | ||
} | ||
} // Recursively mutate and filter empty values from arrays and objects | ||
} | ||
// Recursively mutate and filter empty values from arrays and objects | ||
export function filterEmpty(subject) { | ||
@@ -119,3 +120,2 @@ if (typeof subject === 'object') { | ||
} | ||
return subject.length > 0; | ||
@@ -128,3 +128,2 @@ } else { | ||
} | ||
return entries(subject).length > 0; | ||
@@ -135,6 +134,7 @@ } | ||
} | ||
} // Merges source values and returns a new merged value. The map function will be called with a | ||
} | ||
// Merges source values and returns a new merged value. The map function will be called with a | ||
// property's path, previous value, and next value; it should return an array containing any | ||
// replacement path and value; when a replacement value not defined, values will be merged. | ||
export function merge(sources, map) { | ||
@@ -145,32 +145,32 @@ return sources.reduce((target, source, i) => { | ||
var _ctx; | ||
let ctx = get(target, path.slice(0, -1)); | ||
let key = path[path.length - 1]; | ||
let prev = (_ctx = ctx) === null || _ctx === void 0 ? void 0 : _ctx[key]; // maybe map the property path and/or value | ||
let prev = (_ctx = ctx) === null || _ctx === void 0 ? void 0 : _ctx[key]; | ||
let [mapped, next] = (map === null || map === void 0 ? void 0 : map(path, prev, value)) || []; // update the context and path if changed | ||
// maybe map the property path and/or value | ||
let [mapped, next] = (map === null || map === void 0 ? void 0 : map(path, prev, value)) || []; | ||
// update the context and path if changed | ||
if (mapped !== null && mapped !== void 0 && mapped.some((m, i) => m !== path[i])) { | ||
ctx = get(target, mapped.slice(0, -1)); | ||
path = [...mapped]; | ||
} // adjust path to concat array values when necessary | ||
} | ||
// adjust path to concat array values when necessary | ||
if (next !== null && (isArray(ctx) || isInteger(key))) { | ||
var _ctx2; | ||
path.splice(-1, 1, ((_ctx2 = ctx) === null || _ctx2 === void 0 ? void 0 : _ctx2.length) ?? 0); | ||
} // delete prev values | ||
} | ||
// delete prev values | ||
if (next === null || next == null && value === null) { | ||
del(target, path); | ||
} // set the next or default value if there is one | ||
} | ||
// set the next or default value if there is one | ||
if (next != null || next !== null && value != null && !hasKeys(value)) { | ||
set(target ?? (target = create(isSourceArray)), path, next ?? value); | ||
} // do not recurse mapped objects | ||
} | ||
// do not recurse mapped objects | ||
return next === undefined; | ||
@@ -177,0 +177,0 @@ }); |
import merge from './merge.js'; | ||
import { getSchema } from '../validate.js'; // Edge case camelizations | ||
import { getSchema } from '../validate.js'; | ||
const CAMELCASE_MAP = new Map([['css', 'CSS'], ['javascript', 'JavaScript']]); // Regular expression that matches words from boundaries or consecutive casing | ||
// Edge case camelizations | ||
const CAMELCASE_MAP = new Map([['css', 'CSS'], ['javascript', 'JavaScript']]); | ||
const WORD_REG = /[a-z]{2,}|[A-Z]{2,}|[0-9]{2,}|[^-_\s]+?(?=[A-Z0-9-_\s]|$)/g; // Converts kebab-cased and snake_cased strings to camelCase. | ||
// Regular expression that matches words from boundaries or consecutive casing | ||
const WORD_REG = /[a-z]{2,}|[A-Z]{2,}|[0-9]{2,}|[^-_\s]+?(?=[A-Z0-9-_\s]|$)/g; | ||
// Converts kebab-cased and snake_cased strings to camelCase. | ||
export function camelcase(str) { | ||
if (typeof str !== 'string') return str; | ||
return str.match(WORD_REG).reduce((s, w, i) => s + (i ? CAMELCASE_MAP.get(w.toLowerCase()) || w[0].toUpperCase() + w.slice(1).toLowerCase() : w.toLowerCase()), ''); | ||
} // Coverts camelCased and snake_cased strings to kebab-case. | ||
} | ||
// Coverts camelCased and snake_cased strings to kebab-case. | ||
export function kebabcase(str) { | ||
if (typeof str !== 'string') return str; | ||
return Array.from(CAMELCASE_MAP).reduce((str, [word, camel]) => str.replace(camel, `-${word}`), str).match(WORD_REG).join('-').toLowerCase(); | ||
} // Coverts kebab-case and camelCased strings to snake_case. | ||
} | ||
// Coverts kebab-case and camelCased strings to snake_case. | ||
export function snakecase(str) { | ||
if (typeof str !== 'string') return str; | ||
return Array.from(CAMELCASE_MAP).reduce((str, [word, camel]) => str.replace(camel, `_${word}`), str).match(WORD_REG).join('_').toLowerCase(); | ||
} // Removes undefined empty values and renames kebab-case properties to camelCase. Optionally | ||
} | ||
// Removes undefined empty values and renames kebab-case properties to camelCase. Optionally | ||
// allows deep merging with options.overrides, converting keys to kebab-case with options.kebab, | ||
// and normalizing against a schema with options.schema. | ||
export function normalize(object, options) { | ||
var _options, _options2, _options3; | ||
if (typeof options === 'string') options = { | ||
@@ -34,19 +39,16 @@ schema: options | ||
var _options4, _schemas$shift; | ||
let schemas = getSchema((_options4 = options) === null || _options4 === void 0 ? void 0 : _options4.schema, path.map(camelcase)); | ||
let skip = ((_schemas$shift = schemas.shift()) === null || _schemas$shift === void 0 ? void 0 : _schemas$shift.normalize) === false; | ||
let mapped = []; // skip normalizing paths of class instances | ||
let mapped = []; | ||
// skip normalizing paths of class instances | ||
if (!skip && typeof value === 'object' && value !== null && value !== void 0 && value.constructor) { | ||
skip = Object.getPrototypeOf(value) !== Object.prototype; | ||
} | ||
for (let [i, k] of path.entries()) { | ||
var _options5, _options5$skip, _schemas$i; | ||
skip || (skip = (_options5 = options) === null || _options5 === void 0 ? void 0 : (_options5$skip = _options5.skip) === null || _options5$skip === void 0 ? void 0 : _options5$skip.call(_options5, mapped.concat(k))); | ||
var _options5, _options5$skip, _options6, _schemas$i; | ||
skip || (skip = (_options5 = options) === null || _options5 === void 0 ? void 0 : (_options5$skip = (_options6 = _options5).skip) === null || _options5$skip === void 0 ? void 0 : _options5$skip.call(_options6, mapped.concat(k))); | ||
mapped.push(skip ? k : keycase(k)); | ||
skip || (skip = ((_schemas$i = schemas[i]) === null || _schemas$i === void 0 ? void 0 : _schemas$i.normalize) === false); | ||
} | ||
return [mapped]; | ||
@@ -53,0 +55,0 @@ }); |
import util from 'util'; | ||
import YAML from 'yaml'; | ||
import getDefaults from '../defaults.js'; // Provides native util.inspect with common options for printing configs. | ||
import getDefaults from '../defaults.js'; | ||
// Provides native util.inspect with common options for printing configs. | ||
export function inspect(config) { | ||
@@ -10,5 +11,6 @@ return util.inspect(config, { | ||
}); | ||
} // Converts a config to a yaml, json, or js string. When no config is provided, | ||
} | ||
// Converts a config to a yaml, json, or js string. When no config is provided, | ||
// falls back to schema defaults. | ||
export function stringify(format, config = getDefaults()) { | ||
@@ -19,9 +21,6 @@ switch (format) { | ||
return YAML.stringify(config); | ||
case 'json': | ||
return JSON.stringify(config, null, 2) + '\n'; | ||
case 'js': | ||
return `module.exports = ${inspect(config)}\n`; | ||
default: | ||
@@ -28,0 +27,0 @@ throw new Error(`Unsupported format: ${format}`); |
@@ -9,4 +9,5 @@ import AJV from 'ajv/dist/2019.js'; | ||
entries | ||
} = Object; // AJV manages and validates schemas. | ||
} = Object; | ||
// AJV manages and validates schemas. | ||
const ajv = new AJV({ | ||
@@ -47,3 +48,2 @@ strict: false, | ||
} = cxt; | ||
for (let prop of schema) { | ||
@@ -59,4 +59,5 @@ gen.if(AJV._`${data}.${AJV._([prop])} !== undefined`, () => { | ||
}] | ||
}); // Returns a new default schema. | ||
}); | ||
// Returns a new default schema. | ||
function getDefaultSchema() { | ||
@@ -74,24 +75,24 @@ return { | ||
}; | ||
} // Gets the schema object from the AJV schema. If a path is provided, an array of schemas is | ||
} | ||
// Gets the schema object from the AJV schema. If a path is provided, an array of schemas is | ||
// returned, with each index representing the schema of each part of the path (index zero is root). | ||
export function getSchema(name, path, root) { | ||
var _ajv$getSchema, _ref, _schema$properties; | ||
// get the root schema if necessary, resolve it, and return it when there is no path | ||
let schema = typeof name === 'string' ? (_ajv$getSchema = ajv.getSchema(name)) === null || _ajv$getSchema === void 0 ? void 0 : _ajv$getSchema.schema : name; | ||
if (!schema || !path) return schema ?? []; | ||
root ?? (root = schema); // parse and work with one key at a time | ||
root ?? (root = schema); | ||
let [key, ...rest] = path = parsePropertyPath(path); // if the desired schema is one of many, we need to find the best match | ||
// parse and work with one key at a time | ||
let [key, ...rest] = path = parsePropertyPath(path); | ||
// if the desired schema is one of many, we need to find the best match | ||
let many = (_ref = isArray(schema) ? schema : schema === null || schema === void 0 ? void 0 : schema[['anyOf', 'oneOf', 'allOf'].find(p => schema[p])]) === null || _ref === void 0 ? void 0 : _ref.map(p => getSchema(p, path, root)).sort((a, b) => { | ||
var _a$; | ||
return (// the best possible match will match most of the path or loosely match | ||
return ( | ||
// the best possible match will match most of the path or loosely match | ||
b.length - a.length || (((_a$ = a[0]) === null || _a$ === void 0 ? void 0 : _a$.type) === 'object' && (a[0].additionalProperties !== false || a[0].unevaluatedProperties !== false) ? -1 : 1) | ||
); | ||
})[0]; | ||
if (many !== null && many !== void 0 && many.length) { | ||
@@ -116,7 +117,8 @@ return many; | ||
} | ||
} // Adds schemas to the config schema's properties. The config schema is removed, modified, and | ||
} | ||
// Adds schemas to the config schema's properties. The config schema is removed, modified, and | ||
// replaced after the new schemas are added to clear any compiled caches. Existing schemas are | ||
// removed and replaced as well. If a schema has an existing $id, the schema will not be added | ||
// as config schema properties. | ||
export function addSchema(schemas) { | ||
@@ -126,3 +128,2 @@ if (isArray(schemas)) { | ||
} | ||
if (schemas.$id) { | ||
@@ -135,6 +136,4 @@ let { | ||
} | ||
let config = getSchema('/config'); | ||
ajv.removeSchema('/config'); | ||
for (let [key, schema] of entries(schemas)) { | ||
@@ -154,22 +153,21 @@ if (key === '$config') { | ||
} | ||
return ajv.addSchema(config, '/config'); | ||
} // Resets the schema by removing all schemas and inserting a new default schema. | ||
} | ||
// Resets the schema by removing all schemas and inserting a new default schema. | ||
export function resetSchema() { | ||
ajv.removeSchema(); | ||
ajv.addSchema(getDefaultSchema(), '/config'); | ||
} // Adds "a" or "an" to a word for readability. | ||
} | ||
// Adds "a" or "an" to a word for readability. | ||
function a(word) { | ||
if (word === 'undefined' || word === 'null') return word; | ||
return `${'aeiou'.includes(word[0]) ? 'an' : 'a'} ${word}`; | ||
} // Default errors anywhere within these keywords can be confusing | ||
} | ||
// Default errors anywhere within these keywords can be confusing | ||
const HIDE_NESTED_KEYWORDS = ['oneOf', 'anyOf', 'allOf', 'if', 'then', 'else', 'not']; | ||
function shouldHideError(key, path, error) { | ||
var _parentSchema$errors; | ||
let { | ||
@@ -181,12 +179,10 @@ parentSchema, | ||
return !(parentSchema.error || (_parentSchema$errors = parentSchema.errors) !== null && _parentSchema$errors !== void 0 && _parentSchema$errors[keyword]) && HIDE_NESTED_KEYWORDS.some(k => schemaPath.includes(`/${k}`)); | ||
} // Validates data according to the associated schema and returns a list of errors, if any. | ||
} | ||
// Validates data according to the associated schema and returns a list of errors, if any. | ||
export function validate(data, key = '/config') { | ||
if (!ajv.validate(key, data)) { | ||
let errors = new Map(); | ||
for (let error of ajv.errors) { | ||
var _parentSchema$errors2; | ||
let { | ||
@@ -200,4 +196,5 @@ instancePath, | ||
let path = instancePath ? instancePath.substr(1).split('/') : []; | ||
if (shouldHideError(key, path, error)) continue; // generate a custom error message | ||
if (shouldHideError(key, path, error)) continue; | ||
// generate a custom error message | ||
if (parentSchema.error || (_parentSchema$errors2 = parentSchema.errors) !== null && _parentSchema$errors2 !== void 0 && _parentSchema$errors2[keyword]) { | ||
@@ -213,5 +210,5 @@ let custom = parentSchema.error || parentSchema.errors[keyword]; | ||
message = 'unknown property'; | ||
} // fix paths | ||
} | ||
// fix paths | ||
if (params.missingProperty) { | ||
@@ -225,5 +222,5 @@ path.push(params.missingProperty); | ||
path.push(params.disallowedProperty); | ||
} // fix invalid data | ||
} | ||
// fix invalid data | ||
if (keyword === 'minimum') { | ||
@@ -237,7 +234,8 @@ set(data, path, Math.max(error.data, error.schema)); | ||
del(data, path); | ||
} // joined for error messages | ||
} | ||
// joined for error messages | ||
path = joinPropertyPath(path); | ||
path = joinPropertyPath(path); // map one error per path | ||
// map one error per path | ||
errors.set(path, { | ||
@@ -247,7 +245,8 @@ path, | ||
}); | ||
} // filter empty values as a result of scrubbing | ||
} | ||
// filter empty values as a result of scrubbing | ||
filterEmpty(data); | ||
filterEmpty(data); // return an array of errors | ||
// return an array of errors | ||
return Array.from(errors.values()); | ||
@@ -254,0 +253,0 @@ } |
{ | ||
"name": "@percy/config", | ||
"version": "1.12.0", | ||
"version": "1.13.0", | ||
"license": "MIT", | ||
@@ -37,3 +37,3 @@ "repository": { | ||
"dependencies": { | ||
"@percy/logger": "1.12.0", | ||
"@percy/logger": "1.13.0", | ||
"ajv": "^8.6.2", | ||
@@ -46,3 +46,3 @@ "cosmiconfig": "^7.0.0", | ||
}, | ||
"gitHead": "4303b74df91f60e36065141289d2ef2277d1d6fc" | ||
"gitHead": "d2e812d14aa446fa580ffa75144a6280627b5a27" | ||
} |
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
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
34353
781
+ Added@percy/logger@1.13.0(transitive)
- Removed@percy/logger@1.12.0(transitive)
Updated@percy/logger@1.13.0