diff --git a/lib/entry-points.js b/lib/entry-points.js index 92cbe4dae3..817eb59172 100644 --- a/lib/entry-points.js +++ b/lib/entry-points.js @@ -1301,16 +1301,16 @@ var require_util = __commonJS({ function isStream2(obj) { return obj && typeof obj === "object" && typeof obj.pipe === "function" && typeof obj.on === "function"; } - function isBlobLike(object) { - if (object === null) { + function isBlobLike(object2) { + if (object2 === null) { return false; - } else if (object instanceof Blob2) { + } else if (object2 instanceof Blob2) { return true; - } else if (typeof object !== "object") { + } else if (typeof object2 !== "object") { return false; } else { - const sTag = object[Symbol.toStringTag]; - return (sTag === "Blob" || sTag === "File") && ("stream" in object && typeof object.stream === "function" || "arrayBuffer" in object && typeof object.arrayBuffer === "function"); + const sTag = object2[Symbol.toStringTag]; + return (sTag === "Blob" || sTag === "File") && ("stream" in object2 && typeof object2.stream === "function" || "arrayBuffer" in object2 && typeof object2.arrayBuffer === "function"); } } function buildURL(url2, queryParams) { @@ -1592,8 +1592,8 @@ var require_util = __commonJS({ } ); } - function isFormDataLike(object) { - return object && typeof object === "object" && typeof object.append === "function" && typeof object.delete === "function" && typeof object.get === "function" && typeof object.getAll === "function" && typeof object.has === "function" && typeof object.set === "function" && object[Symbol.toStringTag] === "FormData"; + function isFormDataLike(object2) { + return object2 && typeof object2 === "object" && typeof object2.append === "function" && typeof object2.delete === "function" && typeof object2.get === "function" && typeof object2.getAll === "function" && typeof object2.has === "function" && typeof object2.set === "function" && object2[Symbol.toStringTag] === "FormData"; } function addAbortListener(signal, listener) { if ("addEventListener" in signal) { @@ -4355,8 +4355,8 @@ var require_util2 = __commonJS({ } return "allowed"; } - function isErrorLike(object) { - return object instanceof Error || (object?.constructor?.name === "Error" || object?.constructor?.name === "DOMException"); + function isErrorLike(object2) { + return object2 instanceof Error || (object2?.constructor?.name === "Error" || object2?.constructor?.name === "DOMException"); } function isValidReasonPhrase(statusText) { for (let i = 0; i < statusText.length; ++i) { @@ -4781,7 +4781,7 @@ var require_util2 = __commonJS({ return new FastIterableIterator(target, kind); }; } - function iteratorMixin(name, object, kInternalIterator, keyIndex = 0, valueIndex = 1) { + function iteratorMixin(name, object2, kInternalIterator, keyIndex = 0, valueIndex = 1) { const makeIterator = createIterator(name, kInternalIterator, keyIndex, valueIndex); const properties = { keys: { @@ -4789,7 +4789,7 @@ var require_util2 = __commonJS({ enumerable: true, configurable: true, value: function keys() { - webidl.brandCheck(this, object); + webidl.brandCheck(this, object2); return makeIterator(this, "key"); } }, @@ -4798,7 +4798,7 @@ var require_util2 = __commonJS({ enumerable: true, configurable: true, value: function values() { - webidl.brandCheck(this, object); + webidl.brandCheck(this, object2); return makeIterator(this, "value"); } }, @@ -4807,7 +4807,7 @@ var require_util2 = __commonJS({ enumerable: true, configurable: true, value: function entries() { - webidl.brandCheck(this, object); + webidl.brandCheck(this, object2); return makeIterator(this, "key+value"); } }, @@ -4816,7 +4816,7 @@ var require_util2 = __commonJS({ enumerable: true, configurable: true, value: function forEach(callbackfn, thisArg = globalThis) { - webidl.brandCheck(this, object); + webidl.brandCheck(this, object2); webidl.argumentLengthCheck(arguments, 1, `${name}.forEach`); if (typeof callbackfn !== "function") { throw new TypeError( @@ -4829,7 +4829,7 @@ var require_util2 = __commonJS({ } } }; - return Object.defineProperties(object.prototype, { + return Object.defineProperties(object2.prototype, { ...properties, [Symbol.iterator]: { writable: true, @@ -5229,8 +5229,8 @@ var require_file = __commonJS({ } }; webidl.converters.Blob = webidl.interfaceConverter(Blob2); - function isFileLike(object) { - return object instanceof File2 || object && (typeof object.stream === "function" || typeof object.arrayBuffer === "function") && object[Symbol.toStringTag] === "File"; + function isFileLike(object2) { + return object2 instanceof File2 || object2 && (typeof object2.stream === "function" || typeof object2.arrayBuffer === "function") && object2[Symbol.toStringTag] === "File"; } module2.exports = { FileLike, isFileLike }; } @@ -5678,12 +5678,12 @@ var require_body = __commonJS({ } }); } - function extractBody(object, keepalive = false) { + function extractBody(object2, keepalive = false) { let stream2 = null; - if (object instanceof ReadableStream) { - stream2 = object; - } else if (isBlobLike(object)) { - stream2 = object.stream(); + if (object2 instanceof ReadableStream) { + stream2 = object2; + } else if (isBlobLike(object2)) { + stream2 = object2.stream(); } else { stream2 = new ReadableStream({ async pull(controller) { @@ -5703,17 +5703,17 @@ var require_body = __commonJS({ let source = null; let length = null; let type = null; - if (typeof object === "string") { - source = object; + if (typeof object2 === "string") { + source = object2; type = "text/plain;charset=UTF-8"; - } else if (object instanceof URLSearchParams) { - source = object.toString(); + } else if (object2 instanceof URLSearchParams) { + source = object2.toString(); type = "application/x-www-form-urlencoded;charset=UTF-8"; - } else if (isArrayBuffer(object)) { - source = new Uint8Array(object.slice()); - } else if (ArrayBuffer.isView(object)) { - source = new Uint8Array(object.buffer.slice(object.byteOffset, object.byteOffset + object.byteLength)); - } else if (util3.isFormDataLike(object)) { + } else if (isArrayBuffer(object2)) { + source = new Uint8Array(object2.slice()); + } else if (ArrayBuffer.isView(object2)) { + source = new Uint8Array(object2.buffer.slice(object2.byteOffset, object2.byteOffset + object2.byteLength)); + } else if (util3.isFormDataLike(object2)) { const boundary = `----formdata-undici-0${`${random(1e11)}`.padStart(11, "0")}`; const prefix = `--${boundary}\r Content-Disposition: form-data`; @@ -5723,7 +5723,7 @@ Content-Disposition: form-data`; const rn = new Uint8Array([13, 10]); length = 0; let hasUnknownSizeValue = false; - for (const [name, value] of object) { + for (const [name, value] of object2) { if (typeof value === "string") { const chunk2 = textEncoder.encode(prefix + `; name="${escape3(normalizeLinefeeds(name))}"\r \r @@ -5751,7 +5751,7 @@ Content-Type: ${value.type || "application/octet-stream"}\r if (hasUnknownSizeValue) { length = null; } - source = object; + source = object2; action = async function* () { for (const part of blobParts) { if (part.stream) { @@ -5762,22 +5762,22 @@ Content-Type: ${value.type || "application/octet-stream"}\r } }; type = `multipart/form-data; boundary=${boundary}`; - } else if (isBlobLike(object)) { - source = object; - length = object.size; - if (object.type) { - type = object.type; + } else if (isBlobLike(object2)) { + source = object2; + length = object2.size; + if (object2.type) { + type = object2.type; } - } else if (typeof object[Symbol.asyncIterator] === "function") { + } else if (typeof object2[Symbol.asyncIterator] === "function") { if (keepalive) { throw new TypeError("keepalive"); } - if (util3.isDisturbed(object) || object.locked) { + if (util3.isDisturbed(object2) || object2.locked) { throw new TypeError( "Response body object should not be disturbed or locked" ); } - stream2 = object instanceof ReadableStream ? object : ReadableStreamFrom(object); + stream2 = object2 instanceof ReadableStream ? object2 : ReadableStreamFrom(object2); } if (typeof source === "string" || util3.isBuffer(source)) { length = Buffer.byteLength(source); @@ -5786,7 +5786,7 @@ Content-Type: ${value.type || "application/octet-stream"}\r let iterator2; stream2 = new ReadableStream({ async start() { - iterator2 = action(object)[Symbol.asyncIterator](); + iterator2 = action(object2)[Symbol.asyncIterator](); }, async pull(controller) { const { value, done } = await iterator2.next(); @@ -5814,12 +5814,12 @@ Content-Type: ${value.type || "application/octet-stream"}\r const body = { stream: stream2, source, length }; return [body, type]; } - function safelyExtractBody(object, keepalive = false) { - if (object instanceof ReadableStream) { - assert(!util3.isDisturbed(object), "The body has already been consumed."); - assert(!object.locked, "The stream is locked."); + function safelyExtractBody(object2, keepalive = false) { + if (object2 instanceof ReadableStream) { + assert(!util3.isDisturbed(object2), "The body has already been consumed."); + assert(!object2.locked, "The stream is locked."); } - return extractBody(object, keepalive); + return extractBody(object2, keepalive); } function cloneBody(instance, body) { const [out1, out2] = body.stream.tee(); @@ -5899,12 +5899,12 @@ Content-Type: ${value.type || "application/octet-stream"}\r function mixinBody(prototype) { Object.assign(prototype.prototype, bodyMixinMethods(prototype)); } - async function consumeBody(object, convertBytesToJSValue, instance) { - webidl.brandCheck(object, instance); - if (bodyUnusable(object)) { + async function consumeBody(object2, convertBytesToJSValue, instance) { + webidl.brandCheck(object2, instance); + if (bodyUnusable(object2)) { throw new TypeError("Body is unusable: Body has already been read"); } - throwIfAborted(object[kState]); + throwIfAborted(object2[kState]); const promise = createDeferredPromise(); const errorSteps = (error3) => promise.reject(error3); const successSteps = (data) => { @@ -5914,15 +5914,15 @@ Content-Type: ${value.type || "application/octet-stream"}\r errorSteps(e); } }; - if (object[kState].body == null) { + if (object2[kState].body == null) { successSteps(Buffer.allocUnsafe(0)); return promise.promise; } - await fullyReadBody(object[kState].body, successSteps, errorSteps); + await fullyReadBody(object2[kState].body, successSteps, errorSteps); return promise.promise; } - function bodyUnusable(object) { - const body = object[kState].body; + function bodyUnusable(object2) { + const body = object2[kState].body; return body != null && (body.stream.locked || util3.isDisturbed(body.stream)); } function parseJSONFromBytes(bytes) { @@ -12066,10 +12066,10 @@ var require_headers = __commonJS({ while (j > i && isHTTPWhiteSpaceCharCode(potentialValue.charCodeAt(i))) ++i; return i === 0 && j === potentialValue.length ? potentialValue : potentialValue.substring(i, j); } - function fill(headers, object) { - if (Array.isArray(object)) { - for (let i = 0; i < object.length; ++i) { - const header = object[i]; + function fill(headers, object2) { + if (Array.isArray(object2)) { + for (let i = 0; i < object2.length; ++i) { + const header = object2[i]; if (header.length !== 2) { throw webidl.errors.exception({ header: "Headers constructor", @@ -12078,10 +12078,10 @@ var require_headers = __commonJS({ } appendHeader(headers, header[0], header[1]); } - } else if (typeof object === "object" && object !== null) { - const keys = Object.keys(object); + } else if (typeof object2 === "object" && object2 !== null) { + const keys = Object.keys(object2); for (let i = 0; i < keys.length; ++i) { - appendHeader(headers, keys[i], object[keys[i]]); + appendHeader(headers, keys[i], object2[keys[i]]); } } else { throw webidl.errors.conversionFailed({ @@ -12234,24 +12234,24 @@ var require_headers = __commonJS({ // https://fetch.spec.whatwg.org/#convert-header-names-to-a-sorted-lowercase-set toSortedArray() { const size = this[kHeadersMap].size; - const array = new Array(size); + const array2 = new Array(size); if (size <= 32) { if (size === 0) { - return array; + return array2; } const iterator2 = this[kHeadersMap][Symbol.iterator](); const firstValue = iterator2.next().value; - array[0] = [firstValue[0], firstValue[1].value]; + array2[0] = [firstValue[0], firstValue[1].value]; assert(firstValue[1].value !== null); for (let i = 1, j = 0, right = 0, left = 0, pivot = 0, x, value; i < size; ++i) { value = iterator2.next().value; - x = array[i] = [value[0], value[1].value]; + x = array2[i] = [value[0], value[1].value]; assert(x[1] !== null); left = 0; right = i; while (left < right) { pivot = left + (right - left >> 1); - if (array[pivot][0] <= x[0]) { + if (array2[pivot][0] <= x[0]) { left = pivot + 1; } else { right = pivot; @@ -12260,22 +12260,22 @@ var require_headers = __commonJS({ if (i !== pivot) { j = i; while (j > left) { - array[j] = array[--j]; + array2[j] = array2[--j]; } - array[left] = x; + array2[left] = x; } } if (!iterator2.next().done) { throw new TypeError("Unreachable"); } - return array; + return array2; } else { let i = 0; for (const { 0: name, 1: { value } } of this[kHeadersMap]) { - array[i++] = [name, value]; + array2[i++] = [name, value]; assert(value !== null); } - return array.sort(compareHeaderName); + return array2.sort(compareHeaderName); } } }; @@ -22049,12 +22049,12 @@ var init_universal_user_agent2 = __esm({ }); // node_modules/@octokit/endpoint/dist-bundle/index.js -function lowercaseKeys(object) { - if (!object) { +function lowercaseKeys(object2) { + if (!object2) { return {}; } - return Object.keys(object).reduce((newObj, key) => { - newObj[key.toLowerCase()] = object[key]; + return Object.keys(object2).reduce((newObj, key) => { + newObj[key.toLowerCase()] = object2[key]; return newObj; }, {}); } @@ -22130,11 +22130,11 @@ function extractUrlVariableNames(url2) { } return matches.map(removeNonChars).reduce((a, b) => a.concat(b), []); } -function omit(object, keysToOmit) { +function omit(object2, keysToOmit) { const result = { __proto__: null }; - for (const key of Object.keys(object)) { + for (const key of Object.keys(object2)) { if (keysToOmit.indexOf(key) === -1) { - result[key] = object[key]; + result[key] = object2[key]; } } return result; @@ -29616,9 +29616,9 @@ var require_helpers = __commonJS({ } } function deepMerge(target, src) { - var array = Array.isArray(src); - var dst = array && [] || {}; - if (array) { + var array2 = Array.isArray(src); + var dst = array2 && [] || {}; + if (array2) { target = target || []; dst = dst.concat(target); src.forEach(deepMerger.bind(null, target, dst)); @@ -29647,13 +29647,13 @@ var require_helpers = __commonJS({ exports2.encodePath = function encodePointer(a) { return a.map(pathEncoder).join(""); }; - exports2.getDecimalPlaces = function getDecimalPlaces(number) { + exports2.getDecimalPlaces = function getDecimalPlaces(number2) { var decimalPlaces = 0; - if (isNaN(number)) return decimalPlaces; - if (typeof number !== "number") { - number = Number(number); + if (isNaN(number2)) return decimalPlaces; + if (typeof number2 !== "number") { + number2 = Number(number2); } - var parts = number.toString().split("e"); + var parts = number2.toString().split("e"); if (parts.length === 2) { if (parts[1][0] !== "-") { return decimalPlaces; @@ -29853,11 +29853,11 @@ var require_attribute = __commonJS({ } return result; }; - function getEnumerableProperty(object, key) { - if (Object.hasOwnProperty.call(object, key)) return object[key]; - if (!(key in object)) return; - while (object = Object.getPrototypeOf(object)) { - if (Object.propertyIsEnumerable.call(object, key)) return object[key]; + function getEnumerableProperty(object2, key) { + if (Object.hasOwnProperty.call(object2, key)) return object2[key]; + if (!(key in object2)) return; + while (object2 = Object.getPrototypeOf(object2)) { + if (Object.propertyIsEnumerable.call(object2, key)) return object2[key]; } } validators.propertyNames = function validatePropertyNames(instance, schema, options, ctx) { @@ -41829,7 +41829,7 @@ var require_serializer = __commonJS({ * * @returns A valid serialized Javascript object */ - serialize(mapper, object, objectName, options = { xml: {} }) { + serialize(mapper, object2, objectName, options = { xml: {} }) { const updatedOptions = { xml: { rootName: options.xml.rootName ?? "", @@ -41846,40 +41846,40 @@ var require_serializer = __commonJS({ payload = []; } if (mapper.isConstant) { - object = mapper.defaultValue; + object2 = mapper.defaultValue; } const { required, nullable } = mapper; - if (required && nullable && object === void 0) { + if (required && nullable && object2 === void 0) { throw new Error(`${objectName} cannot be undefined.`); } - if (required && !nullable && (object === void 0 || object === null)) { + if (required && !nullable && (object2 === void 0 || object2 === null)) { throw new Error(`${objectName} cannot be null or undefined.`); } - if (!required && nullable === false && object === null) { + if (!required && nullable === false && object2 === null) { throw new Error(`${objectName} cannot be null.`); } - if (object === void 0 || object === null) { - payload = object; + if (object2 === void 0 || object2 === null) { + payload = object2; } else { if (mapperType.match(/^any$/i) !== null) { - payload = object; + payload = object2; } else if (mapperType.match(/^(Number|String|Boolean|Object|Stream|Uuid)$/i) !== null) { - payload = serializeBasicTypes(mapperType, objectName, object); + payload = serializeBasicTypes(mapperType, objectName, object2); } else if (mapperType.match(/^Enum$/i) !== null) { const enumMapper = mapper; - payload = serializeEnumType(objectName, enumMapper.type.allowedValues, object); + payload = serializeEnumType(objectName, enumMapper.type.allowedValues, object2); } else if (mapperType.match(/^(Date|DateTime|TimeSpan|DateTimeRfc1123|UnixTime)$/i) !== null) { - payload = serializeDateTypes(mapperType, object, objectName); + payload = serializeDateTypes(mapperType, object2, objectName); } else if (mapperType.match(/^ByteArray$/i) !== null) { - payload = serializeByteArrayType(objectName, object); + payload = serializeByteArrayType(objectName, object2); } else if (mapperType.match(/^Base64Url$/i) !== null) { - payload = serializeBase64UrlType(objectName, object); + payload = serializeBase64UrlType(objectName, object2); } else if (mapperType.match(/^Sequence$/i) !== null) { - payload = serializeSequenceType(this, mapper, object, objectName, Boolean(this.isXML), updatedOptions); + payload = serializeSequenceType(this, mapper, object2, objectName, Boolean(this.isXML), updatedOptions); } else if (mapperType.match(/^Dictionary$/i) !== null) { - payload = serializeDictionaryType(this, mapper, object, objectName, Boolean(this.isXML), updatedOptions); + payload = serializeDictionaryType(this, mapper, object2, objectName, Boolean(this.isXML), updatedOptions); } else if (mapperType.match(/^Composite$/i) !== null) { - payload = serializeCompositeType(this, mapper, object, objectName, Boolean(this.isXML), updatedOptions); + payload = serializeCompositeType(this, mapper, object2, objectName, Boolean(this.isXML), updatedOptions); } } return payload; @@ -42119,8 +42119,8 @@ var require_serializer = __commonJS({ } return value; } - function serializeSequenceType(serializer, mapper, object, objectName, isXml, options) { - if (!Array.isArray(object)) { + function serializeSequenceType(serializer, mapper, object2, objectName, isXml, options) { + if (!Array.isArray(object2)) { throw new Error(`${objectName} must be of type Array.`); } let elementType = mapper.type.element; @@ -42131,8 +42131,8 @@ var require_serializer = __commonJS({ elementType = serializer.modelMappers[elementType.type.className] ?? elementType; } const tempArray = []; - for (let i = 0; i < object.length; i++) { - const serializedValue = serializer.serialize(elementType, object[i], objectName, options); + for (let i = 0; i < object2.length; i++) { + const serializedValue = serializer.serialize(elementType, object2[i], objectName, options); if (isXml && elementType.xmlNamespace) { const xmlnsKey = elementType.xmlNamespacePrefix ? `xmlns:${elementType.xmlNamespacePrefix}` : "xmlns"; if (elementType.type.name === "Composite") { @@ -42149,8 +42149,8 @@ var require_serializer = __commonJS({ } return tempArray; } - function serializeDictionaryType(serializer, mapper, object, objectName, isXml, options) { - if (typeof object !== "object") { + function serializeDictionaryType(serializer, mapper, object2, objectName, isXml, options) { + if (typeof object2 !== "object") { throw new Error(`${objectName} must be of type object.`); } const valueType = mapper.type.value; @@ -42158,8 +42158,8 @@ var require_serializer = __commonJS({ throw new Error(`"value" metadata for a Dictionary must be defined in the mapper and it must of type "object" in ${objectName}.`); } const tempDictionary = {}; - for (const key of Object.keys(object)) { - const serializedValue = serializer.serialize(valueType, object[key], objectName, options); + for (const key of Object.keys(object2)) { + const serializedValue = serializer.serialize(valueType, object2[key], objectName, options); tempDictionary[key] = getXmlObjectValue(valueType, serializedValue, isXml, options); } if (isXml && mapper.xmlNamespace) { @@ -42199,11 +42199,11 @@ var require_serializer = __commonJS({ } return modelProps; } - function serializeCompositeType(serializer, mapper, object, objectName, isXml, options) { + function serializeCompositeType(serializer, mapper, object2, objectName, isXml, options) { if (getPolymorphicDiscriminatorRecursively(serializer, mapper)) { - mapper = getPolymorphicMapper(serializer, mapper, object, "clientName"); + mapper = getPolymorphicMapper(serializer, mapper, object2, "clientName"); } - if (object !== void 0 && object !== null) { + if (object2 !== void 0 && object2 !== null) { const payload = {}; const modelProps = resolveModelProperties(serializer, mapper, objectName); for (const key of Object.keys(modelProps)) { @@ -42224,7 +42224,7 @@ var require_serializer = __commonJS({ propName = paths.pop(); for (const pathName of paths) { const childObject = parentObject[pathName]; - if ((childObject === void 0 || childObject === null) && (object[key] !== void 0 && object[key] !== null || propertyMapper.defaultValue !== void 0)) { + if ((childObject === void 0 || childObject === null) && (object2[key] !== void 0 && object2[key] !== null || propertyMapper.defaultValue !== void 0)) { parentObject[pathName] = {}; } parentObject = parentObject[pathName]; @@ -42239,7 +42239,7 @@ var require_serializer = __commonJS({ }; } const propertyObjectName = propertyMapper.serializedName !== "" ? objectName + "." + propertyMapper.serializedName : objectName; - let toSerialize = object[key]; + let toSerialize = object2[key]; const polymorphicDiscriminator = getPolymorphicDiscriminatorRecursively(serializer, mapper); if (polymorphicDiscriminator && polymorphicDiscriminator.clientName === key && (toSerialize === void 0 || toSerialize === null)) { toSerialize = mapper.serializedName; @@ -42261,16 +42261,16 @@ var require_serializer = __commonJS({ const additionalPropertiesMapper = resolveAdditionalProperties(serializer, mapper, objectName); if (additionalPropertiesMapper) { const propNames = Object.keys(modelProps); - for (const clientPropName in object) { + for (const clientPropName in object2) { const isAdditionalProperty = propNames.every((pn) => pn !== clientPropName); if (isAdditionalProperty) { - payload[clientPropName] = serializer.serialize(additionalPropertiesMapper, object[clientPropName], objectName + '["' + clientPropName + '"]', options); + payload[clientPropName] = serializer.serialize(additionalPropertiesMapper, object2[clientPropName], objectName + '["' + clientPropName + '"]', options); } } } return payload; } - return object; + return object2; } function getXmlObjectValue(propertyMapper, serializedValue, isXml, options) { if (!isXml || !propertyMapper.xmlNamespace) { @@ -42454,7 +42454,7 @@ var require_serializer = __commonJS({ } return void 0; } - function getPolymorphicMapper(serializer, mapper, object, polymorphicPropertyName) { + function getPolymorphicMapper(serializer, mapper, object2, polymorphicPropertyName) { const polymorphicDiscriminator = getPolymorphicDiscriminatorRecursively(serializer, mapper); if (polymorphicDiscriminator) { let discriminatorName = polymorphicDiscriminator[polymorphicPropertyName]; @@ -42462,7 +42462,7 @@ var require_serializer = __commonJS({ if (polymorphicPropertyName === "serializedName") { discriminatorName = discriminatorName.replace(/\\/gi, ""); } - const discriminatorValue = object[discriminatorName]; + const discriminatorValue = object2[discriminatorName]; const typeName = mapper.type.uberParent ?? mapper.type.className; if (typeof discriminatorValue === "string" && typeName) { const polymorphicMapper = getIndexDiscriminator(serializer.modelMappers.discriminators, discriminatorValue, typeName); @@ -47295,8 +47295,8 @@ var require_StorageSharedKeyCredentialPolicy = __commonJS({ headersArray.sort((a, b) => { return (0, SharedKeyComparator_js_1.compareHeader)(a.name.toLowerCase(), b.name.toLowerCase()); }); - headersArray = headersArray.filter((value, index2, array) => { - if (index2 > 0 && value.name.toLowerCase() === array[index2 - 1].name.toLowerCase()) { + headersArray = headersArray.filter((value, index2, array2) => { + if (index2 > 0 && value.name.toLowerCase() === array2[index2 - 1].name.toLowerCase()) { return false; } return true; @@ -49047,8 +49047,8 @@ var require_StorageSharedKeyCredentialPolicy2 = __commonJS({ headersArray.sort((a, b) => { return (0, SharedKeyComparator_js_1.compareHeader)(a.name.toLowerCase(), b.name.toLowerCase()); }); - headersArray = headersArray.filter((value, index2, array) => { - if (index2 > 0 && value.name.toLowerCase() === array[index2 - 1].name.toLowerCase()) { + headersArray = headersArray.filter((value, index2, array2) => { + if (index2 > 0 && value.name.toLowerCase() === array2[index2 - 1].name.toLowerCase()) { return false; } return true; @@ -49684,8 +49684,8 @@ var require_StorageSharedKeyCredentialPolicyV2 = __commonJS({ headersArray.sort((a, b) => { return (0, SharedKeyComparator_js_1.compareHeader)(a.name.toLowerCase(), b.name.toLowerCase()); }); - headersArray = headersArray.filter((value, index2, array) => { - if (index2 > 0 && value.name.toLowerCase() === array[index2 - 1].name.toLowerCase()) { + headersArray = headersArray.filter((value, index2, array2) => { + if (index2 > 0 && value.name.toLowerCase() === array2[index2 - 1].name.toLowerCase()) { return false; } return true; @@ -50031,8 +50031,8 @@ var require_StorageSharedKeyCredentialPolicyV22 = __commonJS({ headersArray.sort((a, b) => { return (0, SharedKeyComparator_js_1.compareHeader)(a.name.toLowerCase(), b.name.toLowerCase()); }); - headersArray = headersArray.filter((value, index2, array) => { - if (index2 > 0 && value.name.toLowerCase() === array[index2 - 1].name.toLowerCase()) { + headersArray = headersArray.filter((value, index2, array2) => { + if (index2 > 0 && value.name.toLowerCase() === array2[index2 - 1].name.toLowerCase()) { return false; } return true; @@ -77795,14 +77795,14 @@ var require_reflection_json_writer = __commonJS({ /** * Returns `null` as the default for google.protobuf.NullValue. */ - enum(type, value, fieldName, optional, emitDefaultValues, enumAsInteger) { + enum(type, value, fieldName, optional2, emitDefaultValues, enumAsInteger) { if (type[0] == "google.protobuf.NullValue") - return !emitDefaultValues && !optional ? void 0 : null; + return !emitDefaultValues && !optional2 ? void 0 : null; if (value === void 0) { - assert_1.assert(optional); + assert_1.assert(optional2); return void 0; } - if (value === 0 && !emitDefaultValues && !optional) + if (value === 0 && !emitDefaultValues && !optional2) return void 0; assert_1.assert(typeof value == "number"); assert_1.assert(Number.isInteger(value)); @@ -77817,12 +77817,12 @@ var require_reflection_json_writer = __commonJS({ return options.emitDefaultValues ? null : void 0; return type.internalJsonWrite(value, options); } - scalar(type, value, fieldName, optional, emitDefaultValues) { + scalar(type, value, fieldName, optional2, emitDefaultValues) { if (value === void 0) { - assert_1.assert(optional); + assert_1.assert(optional2); return void 0; } - const ed = emitDefaultValues || optional; + const ed = emitDefaultValues || optional2; switch (type) { // int32, fixed32, uint32: JSON value will be a decimal number. Either numbers or strings are accepted. case reflection_info_1.ScalarType.INT32: @@ -78733,9 +78733,9 @@ var require_enum_object = __commonJS({ if (!isEnumObject(enumObject)) throw new Error("not a typescript enum object"); let values = []; - for (let [name, number] of Object.entries(enumObject)) - if (typeof number == "number") - values.push({ name, number }); + for (let [name, number2] of Object.entries(enumObject)) + if (typeof number2 == "number") + values.push({ name, number: number2 }); return values; } exports2.listEnumValues = listEnumValues; @@ -91326,8 +91326,8 @@ var require_async = __commonJS({ } } var race$1 = awaitify(race, 2); - function reduceRight(array, memo, iteratee, callback) { - var reversed = [...array].reverse(); + function reduceRight(array2, memo, iteratee, callback) { + var reversed = [...array2].reverse(); return reduce$1(reversed, memo, iteratee, callback); } function reflect(fn) { @@ -92751,10 +92751,10 @@ var require_util12 = __commonJS({ return arg == null; } exports2.isNullOrUndefined = isNullOrUndefined; - function isNumber(arg) { + function isNumber2(arg) { return typeof arg === "number"; } - exports2.isNumber = isNumber; + exports2.isNumber = isNumber2; function isString3(arg) { return typeof arg === "string"; } @@ -93098,15 +93098,15 @@ var require_stream_writable = __commonJS({ if (typeof Symbol === "function" && Symbol.hasInstance && typeof Function.prototype[Symbol.hasInstance] === "function") { realHasInstance = Function.prototype[Symbol.hasInstance]; Object.defineProperty(Writable, Symbol.hasInstance, { - value: function(object) { - if (realHasInstance.call(this, object)) return true; + value: function(object2) { + if (realHasInstance.call(this, object2)) return true; if (this !== Writable) return false; - return object && object._writableState instanceof WritableState; + return object2 && object2._writableState instanceof WritableState; } }); } else { - realHasInstance = function(object) { - return object instanceof this; + realHasInstance = function(object2) { + return object2 instanceof this; }; } function Writable(options) { @@ -94701,16 +94701,16 @@ var require_overRest = __commonJS({ function overRest(func, start, transform) { start = nativeMax(start === void 0 ? func.length - 1 : start, 0); return function() { - var args = arguments, index2 = -1, length = nativeMax(args.length - start, 0), array = Array(length); + var args = arguments, index2 = -1, length = nativeMax(args.length - start, 0), array2 = Array(length); while (++index2 < length) { - array[index2] = args[start + index2]; + array2[index2] = args[start + index2]; } index2 = -1; var otherArgs = Array(start + 1); while (++index2 < start) { otherArgs[index2] = args[index2]; } - otherArgs[start] = transform(array); + otherArgs[start] = transform(array2); return apply(func, this, otherArgs); }; } @@ -94924,8 +94924,8 @@ var require_baseIsNative = __commonJS({ // node_modules/lodash/_getValue.js var require_getValue = __commonJS({ "node_modules/lodash/_getValue.js"(exports2, module2) { - function getValue(object, key) { - return object == null ? void 0 : object[key]; + function getValue(object2, key) { + return object2 == null ? void 0 : object2[key]; } module2.exports = getValue; } @@ -94936,8 +94936,8 @@ var require_getNative = __commonJS({ "node_modules/lodash/_getNative.js"(exports2, module2) { var baseIsNative = require_baseIsNative(); var getValue = require_getValue(); - function getNative(object, key) { - var value = getValue(object, key); + function getNative(object2, key) { + var value = getValue(object2, key); return baseIsNative(value) ? value : void 0; } module2.exports = getNative; @@ -95080,13 +95080,13 @@ var require_isIterateeCall = __commonJS({ var isArrayLike = require_isArrayLike(); var isIndex = require_isIndex(); var isObject2 = require_isObject(); - function isIterateeCall(value, index2, object) { - if (!isObject2(object)) { + function isIterateeCall(value, index2, object2) { + if (!isObject2(object2)) { return false; } var type = typeof index2; - if (type == "number" ? isArrayLike(object) && isIndex(index2, object.length) : type == "string" && index2 in object) { - return eq(object[index2], value); + if (type == "number" ? isArrayLike(object2) && isIndex(index2, object2.length) : type == "string" && index2 in object2) { + return eq(object2[index2], value); } return false; } @@ -95310,10 +95310,10 @@ var require_isPrototype = __commonJS({ // node_modules/lodash/_nativeKeysIn.js var require_nativeKeysIn = __commonJS({ "node_modules/lodash/_nativeKeysIn.js"(exports2, module2) { - function nativeKeysIn(object) { + function nativeKeysIn(object2) { var result = []; - if (object != null) { - for (var key in Object(object)) { + if (object2 != null) { + for (var key in Object(object2)) { result.push(key); } } @@ -95331,13 +95331,13 @@ var require_baseKeysIn = __commonJS({ var nativeKeysIn = require_nativeKeysIn(); var objectProto = Object.prototype; var hasOwnProperty = objectProto.hasOwnProperty; - function baseKeysIn(object) { - if (!isObject2(object)) { - return nativeKeysIn(object); + function baseKeysIn(object2) { + if (!isObject2(object2)) { + return nativeKeysIn(object2); } - var isProto = isPrototype(object), result = []; - for (var key in object) { - if (!(key == "constructor" && (isProto || !hasOwnProperty.call(object, key)))) { + var isProto = isPrototype(object2), result = []; + for (var key in object2) { + if (!(key == "constructor" && (isProto || !hasOwnProperty.call(object2, key)))) { result.push(key); } } @@ -95353,8 +95353,8 @@ var require_keysIn = __commonJS({ var arrayLikeKeys = require_arrayLikeKeys(); var baseKeysIn = require_baseKeysIn(); var isArrayLike = require_isArrayLike(); - function keysIn(object) { - return isArrayLike(object) ? arrayLikeKeys(object, true) : baseKeysIn(object); + function keysIn(object2) { + return isArrayLike(object2) ? arrayLikeKeys(object2, true) : baseKeysIn(object2); } module2.exports = keysIn; } @@ -95369,8 +95369,8 @@ var require_defaults = __commonJS({ var keysIn = require_keysIn(); var objectProto = Object.prototype; var hasOwnProperty = objectProto.hasOwnProperty; - var defaults2 = baseRest(function(object, sources) { - object = Object(object); + var defaults2 = baseRest(function(object2, sources) { + object2 = Object(object2); var index2 = -1; var length = sources.length; var guard = length > 2 ? sources[2] : void 0; @@ -95384,13 +95384,13 @@ var require_defaults = __commonJS({ var propsLength = props.length; while (++propsIndex < propsLength) { var key = props[propsIndex]; - var value = object[key]; - if (value === void 0 || eq(value, objectProto[key]) && !hasOwnProperty.call(object, key)) { - object[key] = source[key]; + var value = object2[key]; + if (value === void 0 || eq(value, objectProto[key]) && !hasOwnProperty.call(object2, key)) { + object2[key] = source[key]; } } } - return object; + return object2; }); module2.exports = defaults2; } @@ -99487,10 +99487,10 @@ var require_writable = __commonJS({ } ObjectDefineProperty(Writable, SymbolHasInstance, { __proto__: null, - value: function(object) { - if (FunctionPrototypeSymbolHasInstance(this, object)) return true; + value: function(object2) { + if (FunctionPrototypeSymbolHasInstance(this, object2)) return true; if (this !== Writable) return false; - return object && object._writableState instanceof WritableState; + return object2 && object2._writableState instanceof WritableState; } }); Writable.prototype.pipe = function() { @@ -101524,24 +101524,24 @@ var require_operators = __commonJS({ } }.call(this); } - function toIntegerOrInfinity(number) { - number = Number2(number); - if (NumberIsNaN(number)) { + function toIntegerOrInfinity(number2) { + number2 = Number2(number2); + if (NumberIsNaN(number2)) { return 0; } - if (number < 0) { - throw new ERR_OUT_OF_RANGE("number", ">= 0", number); + if (number2 < 0) { + throw new ERR_OUT_OF_RANGE("number", ">= 0", number2); } - return number; + return number2; } - function drop(number, options = void 0) { + function drop(number2, options = void 0) { if (options != null) { validateObject(options, "options"); } if ((options === null || options === void 0 ? void 0 : options.signal) != null) { validateAbortSignal(options.signal, "options.signal"); } - number = toIntegerOrInfinity(number); + number2 = toIntegerOrInfinity(number2); return async function* drop2() { var _options$signal5; if (options !== null && options !== void 0 && (_options$signal5 = options.signal) !== null && _options$signal5 !== void 0 && _options$signal5.aborted) { @@ -101552,20 +101552,20 @@ var require_operators = __commonJS({ if (options !== null && options !== void 0 && (_options$signal6 = options.signal) !== null && _options$signal6 !== void 0 && _options$signal6.aborted) { throw new AbortError(); } - if (number-- <= 0) { + if (number2-- <= 0) { yield val; } } }.call(this); } - function take(number, options = void 0) { + function take(number2, options = void 0) { if (options != null) { validateObject(options, "options"); } if ((options === null || options === void 0 ? void 0 : options.signal) != null) { validateAbortSignal(options.signal, "options.signal"); } - number = toIntegerOrInfinity(number); + number2 = toIntegerOrInfinity(number2); return async function* take2() { var _options$signal7; if (options !== null && options !== void 0 && (_options$signal7 = options.signal) !== null && _options$signal7 !== void 0 && _options$signal7.aborted) { @@ -101576,10 +101576,10 @@ var require_operators = __commonJS({ if (options !== null && options !== void 0 && (_options$signal8 = options.signal) !== null && _options$signal8 !== void 0 && _options$signal8.aborted) { throw new AbortError(); } - if (number-- > 0) { + if (number2-- > 0) { yield val; } - if (number <= 0) { + if (number2 <= 0) { return; } } @@ -101833,12 +101833,12 @@ var require_ours = __commonJS({ // node_modules/lodash/_arrayPush.js var require_arrayPush = __commonJS({ "node_modules/lodash/_arrayPush.js"(exports2, module2) { - function arrayPush(array, values) { - var index2 = -1, length = values.length, offset = array.length; + function arrayPush(array2, values) { + var index2 = -1, length = values.length, offset = array2.length; while (++index2 < length) { - array[offset + index2] = values[index2]; + array2[offset + index2] = values[index2]; } - return array; + return array2; } module2.exports = arrayPush; } @@ -101863,12 +101863,12 @@ var require_baseFlatten = __commonJS({ "node_modules/lodash/_baseFlatten.js"(exports2, module2) { var arrayPush = require_arrayPush(); var isFlattenable = require_isFlattenable(); - function baseFlatten(array, depth, predicate, isStrict, result) { - var index2 = -1, length = array.length; + function baseFlatten(array2, depth, predicate, isStrict, result) { + var index2 = -1, length = array2.length; predicate || (predicate = isFlattenable); result || (result = []); while (++index2 < length) { - var value = array[index2]; + var value = array2[index2]; if (depth > 0 && predicate(value)) { if (depth > 1) { baseFlatten(value, depth - 1, predicate, isStrict, result); @@ -101889,9 +101889,9 @@ var require_baseFlatten = __commonJS({ var require_flatten = __commonJS({ "node_modules/lodash/flatten.js"(exports2, module2) { var baseFlatten = require_baseFlatten(); - function flatten(array) { - var length = array == null ? 0 : array.length; - return length ? baseFlatten(array, 1) : []; + function flatten(array2) { + var length = array2 == null ? 0 : array2.length; + return length ? baseFlatten(array2, 1) : []; } module2.exports = flatten; } @@ -102018,10 +102018,10 @@ var require_listCacheClear = __commonJS({ var require_assocIndexOf = __commonJS({ "node_modules/lodash/_assocIndexOf.js"(exports2, module2) { var eq = require_eq2(); - function assocIndexOf(array, key) { - var length = array.length; + function assocIndexOf(array2, key) { + var length = array2.length; while (length--) { - if (eq(array[length][0], key)) { + if (eq(array2[length][0], key)) { return length; } } @@ -102290,10 +102290,10 @@ var require_SetCache = __commonJS({ // node_modules/lodash/_baseFindIndex.js var require_baseFindIndex = __commonJS({ "node_modules/lodash/_baseFindIndex.js"(exports2, module2) { - function baseFindIndex(array, predicate, fromIndex, fromRight) { - var length = array.length, index2 = fromIndex + (fromRight ? 1 : -1); + function baseFindIndex(array2, predicate, fromIndex, fromRight) { + var length = array2.length, index2 = fromIndex + (fromRight ? 1 : -1); while (fromRight ? index2-- : ++index2 < length) { - if (predicate(array[index2], index2, array)) { + if (predicate(array2[index2], index2, array2)) { return index2; } } @@ -102316,10 +102316,10 @@ var require_baseIsNaN = __commonJS({ // node_modules/lodash/_strictIndexOf.js var require_strictIndexOf = __commonJS({ "node_modules/lodash/_strictIndexOf.js"(exports2, module2) { - function strictIndexOf(array, value, fromIndex) { - var index2 = fromIndex - 1, length = array.length; + function strictIndexOf(array2, value, fromIndex) { + var index2 = fromIndex - 1, length = array2.length; while (++index2 < length) { - if (array[index2] === value) { + if (array2[index2] === value) { return index2; } } @@ -102335,8 +102335,8 @@ var require_baseIndexOf = __commonJS({ var baseFindIndex = require_baseFindIndex(); var baseIsNaN = require_baseIsNaN(); var strictIndexOf = require_strictIndexOf(); - function baseIndexOf(array, value, fromIndex) { - return value === value ? strictIndexOf(array, value, fromIndex) : baseFindIndex(array, baseIsNaN, fromIndex); + function baseIndexOf(array2, value, fromIndex) { + return value === value ? strictIndexOf(array2, value, fromIndex) : baseFindIndex(array2, baseIsNaN, fromIndex); } module2.exports = baseIndexOf; } @@ -102346,9 +102346,9 @@ var require_baseIndexOf = __commonJS({ var require_arrayIncludes = __commonJS({ "node_modules/lodash/_arrayIncludes.js"(exports2, module2) { var baseIndexOf = require_baseIndexOf(); - function arrayIncludes(array, value) { - var length = array == null ? 0 : array.length; - return !!length && baseIndexOf(array, value, 0) > -1; + function arrayIncludes(array2, value) { + var length = array2 == null ? 0 : array2.length; + return !!length && baseIndexOf(array2, value, 0) > -1; } module2.exports = arrayIncludes; } @@ -102357,10 +102357,10 @@ var require_arrayIncludes = __commonJS({ // node_modules/lodash/_arrayIncludesWith.js var require_arrayIncludesWith = __commonJS({ "node_modules/lodash/_arrayIncludesWith.js"(exports2, module2) { - function arrayIncludesWith(array, value, comparator) { - var index2 = -1, length = array == null ? 0 : array.length; + function arrayIncludesWith(array2, value, comparator) { + var index2 = -1, length = array2 == null ? 0 : array2.length; while (++index2 < length) { - if (comparator(value, array[index2])) { + if (comparator(value, array2[index2])) { return true; } } @@ -102373,10 +102373,10 @@ var require_arrayIncludesWith = __commonJS({ // node_modules/lodash/_arrayMap.js var require_arrayMap = __commonJS({ "node_modules/lodash/_arrayMap.js"(exports2, module2) { - function arrayMap(array, iteratee) { - var index2 = -1, length = array == null ? 0 : array.length, result = Array(length); + function arrayMap(array2, iteratee) { + var index2 = -1, length = array2 == null ? 0 : array2.length, result = Array(length); while (++index2 < length) { - result[index2] = iteratee(array[index2], index2, array); + result[index2] = iteratee(array2[index2], index2, array2); } return result; } @@ -102404,8 +102404,8 @@ var require_baseDifference = __commonJS({ var baseUnary = require_baseUnary(); var cacheHas = require_cacheHas(); var LARGE_ARRAY_SIZE = 200; - function baseDifference(array, values, iteratee, comparator) { - var index2 = -1, includes = arrayIncludes, isCommon = true, length = array.length, result = [], valuesLength = values.length; + function baseDifference(array2, values, iteratee, comparator) { + var index2 = -1, includes = arrayIncludes, isCommon = true, length = array2.length, result = [], valuesLength = values.length; if (!length) { return result; } @@ -102422,7 +102422,7 @@ var require_baseDifference = __commonJS({ } outer: while (++index2 < length) { - var value = array[index2], computed = iteratee == null ? value : iteratee(value); + var value = array2[index2], computed = iteratee == null ? value : iteratee(value); value = comparator || value !== 0 ? value : 0; if (isCommon && computed === computed) { var valuesIndex = valuesLength; @@ -102461,8 +102461,8 @@ var require_difference = __commonJS({ var baseFlatten = require_baseFlatten(); var baseRest = require_baseRest(); var isArrayLikeObject = require_isArrayLikeObject(); - var difference = baseRest(function(array, values) { - return isArrayLikeObject(array) ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true)) : []; + var difference = baseRest(function(array2, values) { + return isArrayLikeObject(array2) ? baseDifference(array2, baseFlatten(values, 1, isArrayLikeObject, true)) : []; }); module2.exports = difference; } @@ -102525,13 +102525,13 @@ var require_baseUniq = __commonJS({ var createSet = require_createSet(); var setToArray = require_setToArray(); var LARGE_ARRAY_SIZE = 200; - function baseUniq(array, iteratee, comparator) { - var index2 = -1, includes = arrayIncludes, length = array.length, isCommon = true, result = [], seen = result; + function baseUniq(array2, iteratee, comparator) { + var index2 = -1, includes = arrayIncludes, length = array2.length, isCommon = true, result = [], seen = result; if (comparator) { isCommon = false; includes = arrayIncludesWith; } else if (length >= LARGE_ARRAY_SIZE) { - var set = iteratee ? null : createSet(array); + var set = iteratee ? null : createSet(array2); if (set) { return setToArray(set); } @@ -102543,7 +102543,7 @@ var require_baseUniq = __commonJS({ } outer: while (++index2 < length) { - var value = array[index2], computed = iteratee ? iteratee(value) : value; + var value = array2[index2], computed = iteratee ? iteratee(value) : value; value = comparator || value !== 0 ? value : 0; if (isCommon && computed === computed) { var seenIndex = seen.length; @@ -105849,7 +105849,7 @@ var require_archiver_utils = __commonJS({ } return dateish; }; - utils.defaults = function(object, source, guard) { + utils.defaults = function(object2, source, guard) { var args = arguments; args[0] = args[0] || {}; return defaults2(...args); @@ -111731,12 +111731,12 @@ var require_dist_node2 = __commonJS({ format: "" } }; - function lowercaseKeys2(object) { - if (!object) { + function lowercaseKeys2(object2) { + if (!object2) { return {}; } - return Object.keys(object).reduce((newObj, key) => { - newObj[key.toLowerCase()] = object[key]; + return Object.keys(object2).reduce((newObj, key) => { + newObj[key.toLowerCase()] = object2[key]; return newObj; }, {}); } @@ -111818,11 +111818,11 @@ var require_dist_node2 = __commonJS({ } return matches.map(removeNonChars2).reduce((a, b) => a.concat(b), []); } - function omit2(object, keysToOmit) { + function omit2(object2, keysToOmit) { const result = { __proto__: null }; - for (const key of Object.keys(object)) { + for (const key of Object.keys(object2)) { if (keysToOmit.indexOf(key) === -1) { - result[key] = object[key]; + result[key] = object2[key]; } } return result; @@ -124893,8 +124893,8 @@ var require_util16 = __commonJS({ parts.push(format.substring(last)); return parts.join(""); }; - util3.formatNumber = function(number, decimals, dec_point, thousands_sep) { - var n = number, c = isNaN(decimals = Math.abs(decimals)) ? 2 : decimals; + util3.formatNumber = function(number2, decimals, dec_point, thousands_sep) { + var n = number2, c = isNaN(decimals = Math.abs(decimals)) ? 2 : decimals; var d = dec_point === void 0 ? "," : dec_point; var t = thousands_sep === void 0 ? "." : thousands_sep, s = n < 0 ? "-" : ""; var i = parseInt(n = Math.abs(+n || 0).toFixed(c), 10) + ""; @@ -141654,7 +141654,7 @@ var nullCoreTag = defineScalarTag("tag:yaml.org,2002:null", { if (NULL_VALUES$1.indexOf(source) !== -1) return null; return NOT_RESOLVED; }, - identify: (object) => object === null, + identify: (object2) => object2 === null, represent: () => "null" }); var nullJsonTag = defineScalarTag("tag:yaml.org,2002:null", { @@ -141664,7 +141664,7 @@ var nullJsonTag = defineScalarTag("tag:yaml.org,2002:null", { if (source === "null" || isExplicit && source === "") return null; return NOT_RESOLVED; }, - identify: (object) => object === null, + identify: (object2) => object2 === null, represent: () => "null" }); var NULL_VALUES = [ @@ -141686,7 +141686,7 @@ var nullYaml11Tag = defineScalarTag("tag:yaml.org,2002:null", { if (NULL_VALUES.indexOf(source) !== -1) return null; return NOT_RESOLVED; }, - identify: (object) => object === null, + identify: (object2) => object2 === null, represent: () => "null" }); var TRUE_VALUES$2 = [ @@ -141712,8 +141712,8 @@ var boolCoreTag = defineScalarTag("tag:yaml.org,2002:bool", { if (FALSE_VALUES$2.indexOf(source) !== -1) return false; return NOT_RESOLVED; }, - identify: (object) => Object.prototype.toString.call(object) === "[object Boolean]", - represent: (object) => object ? "true" : "false" + identify: (object2) => Object.prototype.toString.call(object2) === "[object Boolean]", + represent: (object2) => object2 ? "true" : "false" }); var TRUE_VALUES$1 = ["true"]; var FALSE_VALUES$1 = ["false"]; @@ -141725,8 +141725,8 @@ var boolJsonTag = defineScalarTag("tag:yaml.org,2002:bool", { if (FALSE_VALUES$1.indexOf(source) !== -1) return false; return NOT_RESOLVED; }, - identify: (object) => Object.prototype.toString.call(object) === "[object Boolean]", - represent: (object) => object ? "true" : "false" + identify: (object2) => Object.prototype.toString.call(object2) === "[object Boolean]", + represent: (object2) => object2 ? "true" : "false" }); var TRUE_VALUES = [ "true", @@ -141773,8 +141773,8 @@ var boolYaml11Tag = defineScalarTag("tag:yaml.org,2002:bool", { if (FALSE_VALUES.indexOf(source) !== -1) return false; return NOT_RESOLVED; }, - identify: (object) => Object.prototype.toString.call(object) === "[object Boolean]", - represent: (object) => object ? "true" : "false" + identify: (object2) => Object.prototype.toString.call(object2) === "[object Boolean]", + represent: (object2) => object2 ? "true" : "false" }); var YAML_INTEGER_IMPLICIT_PATTERN$1 = /* @__PURE__ */ new RegExp("^(?:0o[0-7]+|0x[0-9a-fA-F]+|[-+]?[0-9]+)$"); var YAML_INTEGER_EXPLICIT_PATTERN$1 = /* @__PURE__ */ new RegExp("^(?:[-+]?0b[0-1]+|[-+]?0o[0-7]+|[-+]?0x[0-9a-fA-F]+|[-+]?[0-9]+)$"); @@ -141805,8 +141805,8 @@ var intCoreTag = defineScalarTag("tag:yaml.org,2002:int", { ..."0123456789" ], resolve: resolveYamlInteger$2, - identify: (object) => Number.isInteger(object) && !Object.is(object, -0) && object.toString(10).indexOf("e") < 0, - represent: (object) => object.toString(10) + identify: (object2) => Number.isInteger(object2) && !Object.is(object2, -0) && object2.toString(10).indexOf("e") < 0, + represent: (object2) => object2.toString(10) }); var YAML_INTEGER_IMPLICIT_PATTERN = /* @__PURE__ */ new RegExp("^-?(?:0|[1-9][0-9]*)$"); var YAML_INTEGER_EXPLICIT_PATTERN = /* @__PURE__ */ new RegExp("^(?:[-+]?0b[0-1]+|[-+]?0o[0-7]+|[-+]?0x[0-9a-fA-F]+|[-+]?[0-9]+)$"); @@ -141833,8 +141833,8 @@ var intJsonTag = defineScalarTag("tag:yaml.org,2002:int", { implicit: true, implicitFirstChars: ["-", ..."0123456789"], resolve: resolveYamlInteger$1, - identify: (object) => Number.isInteger(object) && !Object.is(object, -0) && object.toString(10).indexOf("e") < 0, - represent: (object) => object.toString(10) + identify: (object2) => Number.isInteger(object2) && !Object.is(object2, -0) && object2.toString(10).indexOf("e") < 0, + represent: (object2) => object2.toString(10) }); var YAML_INTEGER_PATTERN = /* @__PURE__ */ new RegExp("^(?:[-+]?0b[0-1_]+|[-+]?0[0-7_]+|[-+]?0x[0-9a-fA-F_]+|[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+|[-+]?(?:0|[1-9][0-9_]*))$"); function parseYamlInteger(source) { @@ -141867,8 +141867,8 @@ var intYaml11Tag = defineScalarTag("tag:yaml.org,2002:int", { ..."0123456789" ], resolve: resolveYamlInteger, - identify: (object) => Number.isInteger(object) && !Object.is(object, -0) && object.toString(10).indexOf("e") < 0, - represent: (object) => object.toString(10) + identify: (object2) => Number.isInteger(object2) && !Object.is(object2, -0) && object2.toString(10).indexOf("e") < 0, + represent: (object2) => object2.toString(10) }); var YAML_FLOAT_PATTERN$1 = /* @__PURE__ */ new RegExp("^(?:[-+]?[0-9]+(?:\\.[0-9]*)?(?:[eE][-+]?[0-9]+)?|[-+]?\\.[0-9]+(?:[eE][-+]?[0-9]+)?|[-+]?\\.(?:inf|Inf|INF)|\\.(?:nan|NaN|NAN))$"); var YAML_FLOAT_SPECIAL_PATTERN$1 = /* @__PURE__ */ new RegExp("^(?:[-+]?\\.(?:inf|Inf|INF)|\\.(?:nan|NaN|NAN))$"); @@ -141883,12 +141883,12 @@ function resolveYamlFloat$2(source) { if (Number.isFinite(result) || YAML_FLOAT_SPECIAL_PATTERN$1.test(source)) return result; return NOT_RESOLVED; } -function representYamlFloat$2(object) { - if (isNaN(object)) return ".nan"; - if (object === Number.POSITIVE_INFINITY) return ".inf"; - if (object === Number.NEGATIVE_INFINITY) return "-.inf"; - if (Object.is(object, -0)) return "-0.0"; - const result = object.toString(10); +function representYamlFloat$2(object2) { + if (isNaN(object2)) return ".nan"; + if (object2 === Number.POSITIVE_INFINITY) return ".inf"; + if (object2 === Number.NEGATIVE_INFINITY) return "-.inf"; + if (Object.is(object2, -0)) return "-0.0"; + const result = object2.toString(10); return /^[-+]?[0-9]+e/.test(result) ? result.replace("e", ".e") : result; } var floatCoreTag = defineScalarTag("tag:yaml.org,2002:float", { @@ -141900,7 +141900,7 @@ var floatCoreTag = defineScalarTag("tag:yaml.org,2002:float", { ..."0123456789" ], resolve: resolveYamlFloat$2, - identify: (object) => typeof object === "number" && (!Number.isInteger(object) || Object.is(object, -0) || object.toString(10).indexOf("e") >= 0), + identify: (object2) => typeof object2 === "number" && (!Number.isInteger(object2) || Object.is(object2, -0) || object2.toString(10).indexOf("e") >= 0), represent: representYamlFloat$2 }); var YAML_FLOAT_IMPLICIT_PATTERN = /* @__PURE__ */ new RegExp("^-?(?:0|[1-9][0-9]*)(?:\\.[0-9]*)?(?:[eE][-+]?[0-9]+)?$"); @@ -141921,19 +141921,19 @@ function resolveYamlFloat$1(source, isExplicit) { if (Number.isFinite(result)) return result; return NOT_RESOLVED; } -function representYamlFloat$1(object) { - if (isNaN(object)) return ".nan"; - if (object === Number.POSITIVE_INFINITY) return ".inf"; - if (object === Number.NEGATIVE_INFINITY) return "-.inf"; - if (Object.is(object, -0)) return "-0.0"; - const result = object.toString(10); +function representYamlFloat$1(object2) { + if (isNaN(object2)) return ".nan"; + if (object2 === Number.POSITIVE_INFINITY) return ".inf"; + if (object2 === Number.NEGATIVE_INFINITY) return "-.inf"; + if (Object.is(object2, -0)) return "-0.0"; + const result = object2.toString(10); return /^[-+]?[0-9]+e/.test(result) ? result.replace("e", ".e") : result; } var floatJsonTag = defineScalarTag("tag:yaml.org,2002:float", { implicit: true, implicitFirstChars: ["-", ..."0123456789"], resolve: resolveYamlFloat$1, - identify: (object) => typeof object === "number" && (!Number.isInteger(object) || Object.is(object, -0) || object.toString(10).indexOf("e") >= 0), + identify: (object2) => typeof object2 === "number" && (!Number.isInteger(object2) || Object.is(object2, -0) || object2.toString(10).indexOf("e") >= 0), represent: representYamlFloat$1 }); var YAML_FLOAT_PATTERN = /* @__PURE__ */ new RegExp("^(?:[-+]?(?:(?:[0-9][0-9_]*)?\\.[0-9_]*)(?:[eE][-+][0-9]+)?|[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+\\.[0-9_]*|[-+]?\\.(?:inf|Inf|INF)|\\.(?:nan|NaN|NAN))$"); @@ -141953,12 +141953,12 @@ function resolveYamlFloat(source) { if (Number.isFinite(result) || YAML_FLOAT_SPECIAL_PATTERN.test(source)) return result; return NOT_RESOLVED; } -function representYamlFloat(object) { - if (isNaN(object)) return ".nan"; - if (object === Number.POSITIVE_INFINITY) return ".inf"; - if (object === Number.NEGATIVE_INFINITY) return "-.inf"; - if (Object.is(object, -0)) return "-0.0"; - const result = object.toString(10); +function representYamlFloat(object2) { + if (isNaN(object2)) return ".nan"; + if (object2 === Number.POSITIVE_INFINITY) return ".inf"; + if (object2 === Number.NEGATIVE_INFINITY) return "-.inf"; + if (Object.is(object2, -0)) return "-0.0"; + const result = object2.toString(10); return /^[-+]?[0-9]+e/.test(result) ? result.replace("e", ".e") : result; } var floatYaml11Tag = defineScalarTag("tag:yaml.org,2002:float", { @@ -141970,7 +141970,7 @@ var floatYaml11Tag = defineScalarTag("tag:yaml.org,2002:float", { ..."0123456789" ], resolve: resolveYamlFloat, - identify: (object) => typeof object === "number" && (!Number.isInteger(object) || Object.is(object, -0) || object.toString(10).indexOf("e") >= 0), + identify: (object2) => typeof object2 === "number" && (!Number.isInteger(object2) || Object.is(object2, -0) || object2.toString(10).indexOf("e") >= 0), represent: representYamlFloat }); var mergeTag = defineScalarTag("tag:yaml.org,2002:merge", { @@ -141990,14 +141990,14 @@ function resolveYamlBinary(source) { for (let index2 = 0; index2 < binary.length; index2++) result[index2] = binary.charCodeAt(index2); return result; } -function representYamlBinary(object) { +function representYamlBinary(object2) { let binary = ""; - for (let index2 = 0; index2 < object.length; index2++) binary += String.fromCharCode(object[index2]); + for (let index2 = 0; index2 < object2.length; index2++) binary += String.fromCharCode(object2[index2]); return btoa(binary); } var binaryTag = defineScalarTag("tag:yaml.org,2002:binary", { resolve: resolveYamlBinary, - identify: (object) => Object.prototype.toString.call(object) === "[object Uint8Array]", + identify: (object2) => Object.prototype.toString.call(object2) === "[object Uint8Array]", represent: representYamlBinary }); var YAML_DATE_REGEXP = /* @__PURE__ */ new RegExp("^([0-9][0-9][0-9][0-9])-([0-9][0-9])-([0-9][0-9])$"); @@ -142039,8 +142039,8 @@ var timestampTag = defineScalarTag("tag:yaml.org,2002:timestamp", { implicit: true, implicitFirstChars: [..."0123456789"], resolve: resolveYamlTimestamp, - identify: (object) => object instanceof Date, - represent: (object) => object.toISOString() + identify: (object2) => object2 instanceof Date, + represent: (object2) => object2.toISOString() }); var seqTag = defineSequenceTag("tag:yaml.org,2002:seq", { create: () => [], @@ -142053,11 +142053,11 @@ var omapTag = defineSequenceTag("tag:yaml.org,2002:omap", { create: () => [], addItem: (container, item) => { if (Object.prototype.toString.call(item) !== "[object Object]") return "cannot resolve an ordered map item"; - const object = item; - const itemKeys = Object.keys(object); + const object2 = item; + const itemKeys = Object.keys(object2); if (itemKeys.length !== 1) return "cannot resolve an ordered map item"; for (const existing of container) if (Object.prototype.hasOwnProperty.call(existing, itemKeys[0])) return "cannot resolve an ordered map item"; - container.push(object); + container.push(object2); return ""; } }); @@ -142070,10 +142070,10 @@ var pairsTag = defineSequenceTag("tag:yaml.org,2002:pairs", { return ""; } if (Object.prototype.toString.call(item) !== "[object Object]") return "cannot resolve a pairs item"; - const object = item; - const keys = Object.keys(object); + const object2 = item; + const keys = Object.keys(object2); if (keys.length !== 1) return "cannot resolve a pairs item"; - container.push([keys[0], object[keys[0]]]); + container.push([keys[0], object2[keys[0]]]); return ""; } }); @@ -142082,9 +142082,9 @@ function isPlainObject3(data) { const prototype = Object.getPrototypeOf(data); return prototype === null || prototype === Object.prototype; } -function pick(object, keys) { +function pick(object2, keys) { const result = {}; - for (const key of keys) if (object[key] !== void 0) result[key] = object[key]; + for (const key of keys) if (object2[key] !== void 0) result[key] = object2[key]; return result; } var mapTag = defineMappingTag("tag:yaml.org,2002:map", { @@ -142270,12 +142270,12 @@ var realMapTag = defineMappingTag("tag:yaml.org,2002:map", { }); function normalizeKey(key) { if (Array.isArray(key)) { - const array = Array.prototype.slice.call(key); - for (let index2 = 0; index2 < array.length; index2++) { - if (Array.isArray(array[index2])) return null; - if (typeof array[index2] === "object" && Object.prototype.toString.call(array[index2]) === "[object Object]") array[index2] = "[object Object]"; + const array2 = Array.prototype.slice.call(key); + for (let index2 = 0; index2 < array2.length; index2++) { + if (Array.isArray(array2[index2])) return null; + if (typeof array2[index2] === "object" && Object.prototype.toString.call(array2[index2]) === "[object Object]") array2[index2] = "[object Object]"; } - return String(array); + return String(array2); } if (typeof key === "object" && Object.prototype.toString.call(key) === "[object Object]") return "[object Object]"; return String(key); @@ -143784,12 +143784,12 @@ function buildRepresentTypes(schema) { })) ]; } -function matchTag(state, object) { +function matchTag(state, object2) { for (let index2 = 0, length = state.representTypes.length; index2 < length; index2 += 1) { const { tag, implicitTag } = state.representTypes[index2]; - if (tag.identify && tag.identify(object)) { + if (tag.identify && tag.identify(object2)) { let tagName; - if (tag.matchByTagPrefix && tag.representTagName) tagName = tag.representTagName(object); + if (tag.matchByTagPrefix && tag.representTagName) tagName = tag.representTagName(object2); else tagName = tag.tagName; return { tag, @@ -143800,9 +143800,9 @@ function matchTag(state, object) { } return null; } -function build(state, object) { - if (!state.noRefs && object !== null && typeof object === "object") { - const existing = state.refs.get(object); +function build(state, object2) { + if (!state.noRefs && object2 !== null && typeof object2 === "object") { + const existing = state.refs.get(object2); if (existing) { if (existing.anchor === void 0) existing.anchor = `ref_${state.refCounter++}`; return { @@ -143813,11 +143813,11 @@ function build(state, object) { }; } } - const matched = matchTag(state, object); + const matched = matchTag(state, object2); if (!matched) { - if (object === void 0) return INVALID; + if (object2 === void 0) return INVALID; if (state.skipInvalid) return INVALID; - throw new YAMLException(`unacceptable kind of an object to dump ${Object.prototype.toString.call(object)}`); + throw new YAMLException(`unacceptable kind of an object to dump ${Object.prototype.toString.call(object2)}`); } const { tag, tagName, implicitTag } = matched; const nodeTagName = implicitTag ? tagName : tagNameShort(tagName); @@ -143828,11 +143828,11 @@ function build(state, object) { kind: "scalar", tag: nodeTagName, style: style2, - value: tag.represent(object) + value: tag.represent(object2) }; } if (tag.nodeKind === "sequence") { - const container = tag.represent(object); + const container = tag.represent(object2); const style2 = new Style(); style2.tagged = !implicitTag; const node2 = { @@ -143841,7 +143841,7 @@ function build(state, object) { style: style2, items: [] }; - if (!state.noRefs) state.refs.set(object, node2); + if (!state.noRefs) state.refs.set(object2, node2); for (let index2 = 0, length = container.length; index2 < length; index2 += 1) { let item = build(state, container[index2]); if (item === INVALID && container[index2] === void 0) item = build(state, null); @@ -143850,7 +143850,7 @@ function build(state, object) { } return node2; } - const map = tag.represent(object); + const map = tag.represent(object2); const style = new Style(); style.tagged = !implicitTag; const node = { @@ -143859,7 +143859,7 @@ function build(state, object) { style, items: [] }; - if (!state.noRefs) state.refs.set(object, node); + if (!state.noRefs) state.refs.set(object2, node); for (const [objectKey, objectValue] of map) { const key = build(state, objectKey); if (key === INVALID) continue; @@ -144502,35 +144502,162 @@ function isArray(value) { function isString(value) { return typeof value === "string"; } +function isNumber(value) { + return typeof value === "number"; +} function isStringOrUndefined(value) { return value === void 0 || isString(value); } -var string = { - validate: isString, - required: true -}; +function defaultCheck(validate) { + return (arg) => ({ unknownKeys: [], invalidKeys: [], valid: validate(arg) }); +} +function makeValidator(validate, required = true) { + return { + validate, + check: defaultCheck(validate), + required + }; +} +var string = makeValidator(isString); +var number = makeValidator(isNumber); +function array(validator) { + const validate = (val) => { + return isArray(val) && val.every((e) => validator.validate(e)); + }; + return { + validate, + check: (val, opts, path29) => { + const result = successfulCheckSchema(); + if (!isArray(val)) { + result.valid = false; + return result; + } + let index2 = 0; + for (const e of val) { + const elementPath = `${path29}[${index2}]`; + const eResult = validator.check(e, opts, `${elementPath}`); + result.unknownKeys.push(...eResult.unknownKeys); + index2++; + if (!eResult.valid) { + result.valid = false; + result.invalidKeys.push(elementPath); + if (opts.failFast) { + return result; + } + continue; + } + } + return result; + }, + required: true + }; +} +function object(schema) { + return { + validate: (val) => { + return isObject(val) && validateSchema(schema, val); + }, + check: (val, opts, path29) => { + if (!isObject(val)) { + return invalidCheckSchema(); + } + return checkSchema(schema, val, opts, path29); + }, + required: true + }; +} function optionalOrNull(validator) { return { validate: (val) => { return val === void 0 || val === null || validator.validate(val); }, + check: (val, opts, path29) => { + if (val === void 0 || val === null) { + return successfulCheckSchema(); + } + return validator.check(val, opts, path29); + }, + required: false + }; +} +function optional(validator) { + return { + validate: (val) => { + return val === void 0 || validator.validate(val); + }, + check: (val, opts, path29) => { + if (val === void 0) { + return successfulCheckSchema(); + } + return validator.check(val, opts, path29); + }, required: false }; } function validateSchema(schema, obj) { + const result = checkSchema(schema, obj, { failFast: true }); + return result.valid; +} +function successfulCheckSchema() { + return { + valid: true, + unknownKeys: [], + invalidKeys: [] + }; +} +function invalidCheckSchema() { + return { + valid: false, + unknownKeys: [], + invalidKeys: [] + }; +} +function checkSchema(schema, obj, options = {}, path29 = "") { + const result = successfulCheckSchema(); + const inputKeys = new Set(Object.keys(obj)); + const invalidKeys = /* @__PURE__ */ new Set(); for (const [key, validator] of Object.entries(schema)) { const hasKey = key in obj; + inputKeys.delete(key); + invalidKeys.add(key); if (validator.required && !hasKey) { - return false; + result.valid = false; + if (options.failFast) { + return result; + } + continue; } if (validator.required && (obj[key] === void 0 || obj[key] === null)) { - return false; + result.valid = false; + if (options.failFast) { + return result; + } + continue; } - if (hasKey && !validator.validate(obj[key])) { - return false; + if (hasKey) { + const checkResult = validator.check(obj[key], options, `${path29}.${key}`); + result.unknownKeys.push(...checkResult.unknownKeys); + result.invalidKeys.push(...checkResult.invalidKeys); + if (checkResult.invalidKeys.length > 0) { + invalidKeys.delete(key); + } + if (!checkResult.valid) { + result.valid = false; + if (options.failFast) { + return result; + } + continue; + } } + invalidKeys.delete(key); } - return true; + for (const remainingKey of inputKeys) { + result.unknownKeys.push(`${path29}.${remainingKey}`); + } + for (const invalidKey of invalidKeys) { + result.invalidKeys.push(`${path29}.${invalidKey}`); + } + return result; } // src/util.ts @@ -145110,28 +145237,28 @@ async function isBinaryAccessible(binary, logger) { return false; } } -async function asyncFilter(array, predicate) { - const results = await Promise.all(array.map(predicate)); - return array.filter((_2, index2) => results[index2]); +async function asyncFilter(array2, predicate) { + const results = await Promise.all(array2.map(predicate)); + return array2.filter((_2, index2) => results[index2]); } -async function asyncSome(array, predicate) { - const results = await Promise.all(array.map(predicate)); +async function asyncSome(array2, predicate) { + const results = await Promise.all(array2.map(predicate)); return results.some((result) => result); } function isDefined2(value) { return value !== void 0 && value !== null; } -function unsafeEntriesInvariant(object) { - return Object.entries(object).filter( +function unsafeEntriesInvariant(object2) { + return Object.entries(object2).filter( ([_2, val]) => val !== void 0 ); } -function joinAtMost(array, separator, limit) { - if (limit > 0 && array.length > limit) { - array = array.slice(0, limit); - array.push("..."); +function joinAtMost(array2, separator, limit) { + if (limit > 0 && array2.length > limit) { + array2 = array2.slice(0, limit); + array2.push("..."); } - return array.join(separator); + return array2.join(separator); } var Success = class { constructor(value) { @@ -146527,6 +146654,11 @@ var LINKED_CODEQL_VERSION = { tagName: bundleVersion }; var featureConfig = { + ["allow_merge_config_files" /* AllowMergeConfigFiles */]: { + defaultValue: false, + envVar: "CODEQL_ACTION_ALLOW_MERGE_CONFIG_FILES", + minimumVersion: void 0 + }, ["allow_multiple_analysis_kinds" /* AllowMultipleAnalysisKinds */]: { defaultValue: false, envVar: "CODEQL_ACTION_ALLOW_MULTIPLE_ANALYSIS_KINDS", @@ -147552,10 +147684,109 @@ function getDependencyCachingEnabled() { } // src/config/db-config.ts -var path6 = __toESM(require("path")); +var path7 = __toESM(require("path")); var jsonschema = __toESM(require_lib2()); var semver5 = __toESM(require_semver2()); +// src/diagnostics.ts +var import_fs = require("fs"); +var import_path = __toESM(require("path")); +var unwrittenDiagnostics = []; +var unwrittenDefaultLanguageDiagnostics = []; +var diagnosticCounter = 0; +function makeDiagnostic(id, name, data = void 0) { + return { + ...data, + timestamp: data?.timestamp ?? (/* @__PURE__ */ new Date()).toISOString(), + source: { ...data?.source, id, name } + }; +} +function addDiagnostic(config, language, diagnostic) { + const logger = getActionsLogger(); + const databasePath = language ? getCodeQLDatabasePath(config, language) : config.dbLocation; + if ((0, import_fs.existsSync)(databasePath)) { + writeDiagnostic(config, language, diagnostic); + } else { + logger.debug( + `Writing a diagnostic for ${language}, but the database at ${databasePath} does not exist yet.` + ); + unwrittenDiagnostics.push({ diagnostic, language }); + } +} +function addNoLanguageDiagnostic(config, diagnostic) { + if (config !== void 0) { + addDiagnostic( + config, + // Arbitrarily choose the first language. We could also choose all languages, but that + // increases the risk of misinterpreting the data. + config.languages[0], + diagnostic + ); + } else { + unwrittenDefaultLanguageDiagnostics.push(diagnostic); + } +} +function writeDiagnostic(config, language, diagnostic) { + const logger = getActionsLogger(); + const databasePath = language ? getCodeQLDatabasePath(config, language) : config.dbLocation; + const diagnosticsPath = import_path.default.resolve( + databasePath, + "diagnostic", + "codeql-action" + ); + try { + (0, import_fs.mkdirSync)(diagnosticsPath, { recursive: true }); + const uniqueSuffix = (diagnosticCounter++).toString(); + const sanitizedTimestamp = diagnostic.timestamp.replace( + /[^a-zA-Z0-9.-]/g, + "" + ); + const jsonPath = import_path.default.resolve( + diagnosticsPath, + `codeql-action-${sanitizedTimestamp}-${uniqueSuffix}.json` + ); + (0, import_fs.writeFileSync)(jsonPath, JSON.stringify(diagnostic)); + } catch (err) { + logger.warning(`Unable to write diagnostic message to database: ${err}`); + logger.debug(JSON.stringify(diagnostic)); + } +} +function logUnwrittenDiagnostics() { + const logger = getActionsLogger(); + const num = unwrittenDiagnostics.length; + if (num > 0) { + logger.warning( + `${num} diagnostic(s) could not be written to the database and will not appear on the Tool Status Page.` + ); + for (const unwritten of unwrittenDiagnostics) { + logger.debug(JSON.stringify(unwritten.diagnostic)); + } + } +} +function flushDiagnostics(config) { + const logger = getActionsLogger(); + const diagnosticsCount = unwrittenDiagnostics.length + unwrittenDefaultLanguageDiagnostics.length; + logger.debug(`Writing ${diagnosticsCount} diagnostic(s) to database.`); + for (const unwritten of unwrittenDiagnostics) { + writeDiagnostic(config, unwritten.language, unwritten.diagnostic); + } + for (const unwritten of unwrittenDefaultLanguageDiagnostics) { + addNoLanguageDiagnostic(config, unwritten); + } + unwrittenDiagnostics = []; + unwrittenDefaultLanguageDiagnostics = []; +} +function makeTelemetryDiagnostic(id, name, attributes) { + return makeDiagnostic(id, name, { + attributes, + visibility: { + cliSummaryTable: false, + statusPage: false, + telemetry: true + } + }); +} + // src/error-messages.ts var PACKS_PROPERTY = "packs"; function getConfigFileOutsideWorkspaceErrorMessage(configFile) { @@ -147721,6 +147952,81 @@ function isKnownPropertyName(name) { } // src/config/db-config.ts +var ORG_SCHEMA = { + /** An array of model pack names. */ + "model-packs": optional(array(string)) +}; +var DEFAULT_SETUP_SCHEMA = { + org: optional(object(ORG_SCHEMA)) +}; +var DEFAULT_SETUP_CONFIG_SCHEMA = { + "threat-models": optional(array(string)), + "default-setup": optional( + object(DEFAULT_SETUP_SCHEMA) + ) +}; +function mergeDefaultSetupAndUserConfigs(logger, fromConfigInput, fromConfigFile) { + logger.debug( + "Combining configuration files from 'config' and 'config-file' inputs" + ); + const schemaCheckResult = checkSchema( + DEFAULT_SETUP_CONFIG_SCHEMA, + fromConfigInput + ); + if (schemaCheckResult.invalidKeys.length > 0) { + logger.warning( + `Invalid keys in Default Setup configuration: ${schemaCheckResult.invalidKeys.join(", ")}` + ); + addNoLanguageDiagnostic( + void 0, + makeTelemetryDiagnostic( + "codeql-action/invalid-default-setup-config-keys", + "Invalid Default Setup configuration keys", + { + invalidKeys: schemaCheckResult.invalidKeys + } + ) + ); + } + if (schemaCheckResult.unknownKeys.length > 0) { + logger.warning( + `Unrecognised keys in Default Setup configuration: ${schemaCheckResult.unknownKeys.join(", ")}` + ); + addNoLanguageDiagnostic( + void 0, + makeTelemetryDiagnostic( + "codeql-action/unrecognised-default-setup-config-keys", + "Unrecognised Default Setup configuration keys", + { + unrecognisedKeys: schemaCheckResult.unknownKeys + } + ) + ); + } + const threatModels = new Set(fromConfigInput["threat-models"] || []); + for (const configFileThreatModel of fromConfigFile["threat-models"] || []) { + threatModels.add(configFileThreatModel); + } + if (fromConfigFile["default-setup"]) { + logger.warning( + `The 'default-setup' configuration key is not supported in user-supplied configuration files and will be ignored.` + ); + } + const result = { ...fromConfigFile }; + delete result["threat-models"]; + delete result["default-setup"]; + if (fromConfigInput["default-setup"]?.org?.["model-packs"]) { + result["default-setup"] = { + org: { + "model-packs": fromConfigInput["default-setup"].org["model-packs"] + } + }; + } + if (threatModels.size > 0) { + result["threat-models"] = Array.from(threatModels); + } + return result; +} function shouldCombine(inputValue) { return !!inputValue?.trim().startsWith("+"); } @@ -147760,11 +148066,11 @@ function parsePacksSpecification(packStr) { throw new ConfigurationError(getPacksStrInvalid(packStr)); } } - if (packPath && (path6.isAbsolute(packPath) || // Permit using "/" instead of "\" on Windows + if (packPath && (path7.isAbsolute(packPath) || // Permit using "/" instead of "\" on Windows // Use `x.split(y).join(z)` as a polyfill for `x.replaceAll(y, z)` since // if we used a regex we'd need to escape the path separator on Windows // which seems more awkward. - path6.normalize(packPath).split(path6.sep).join("/") !== packPath.split(path6.sep).join("/"))) { + path7.normalize(packPath).split(path7.sep).join("/") !== packPath.split(path7.sep).join("/"))) { throw new ConfigurationError(getPacksStrInvalid(packStr)); } if (!packPath && pathStart) { @@ -148086,105 +148392,6 @@ async function getRemoteConfig(actionState, configFile, apiDetails) { ); } -// src/diagnostics.ts -var import_fs = require("fs"); -var import_path = __toESM(require("path")); -var unwrittenDiagnostics = []; -var unwrittenDefaultLanguageDiagnostics = []; -var diagnosticCounter = 0; -function makeDiagnostic(id, name, data = void 0) { - return { - ...data, - timestamp: data?.timestamp ?? (/* @__PURE__ */ new Date()).toISOString(), - source: { ...data?.source, id, name } - }; -} -function addDiagnostic(config, language, diagnostic) { - const logger = getActionsLogger(); - const databasePath = language ? getCodeQLDatabasePath(config, language) : config.dbLocation; - if ((0, import_fs.existsSync)(databasePath)) { - writeDiagnostic(config, language, diagnostic); - } else { - logger.debug( - `Writing a diagnostic for ${language}, but the database at ${databasePath} does not exist yet.` - ); - unwrittenDiagnostics.push({ diagnostic, language }); - } -} -function addNoLanguageDiagnostic(config, diagnostic) { - if (config !== void 0) { - addDiagnostic( - config, - // Arbitrarily choose the first language. We could also choose all languages, but that - // increases the risk of misinterpreting the data. - config.languages[0], - diagnostic - ); - } else { - unwrittenDefaultLanguageDiagnostics.push(diagnostic); - } -} -function writeDiagnostic(config, language, diagnostic) { - const logger = getActionsLogger(); - const databasePath = language ? getCodeQLDatabasePath(config, language) : config.dbLocation; - const diagnosticsPath = import_path.default.resolve( - databasePath, - "diagnostic", - "codeql-action" - ); - try { - (0, import_fs.mkdirSync)(diagnosticsPath, { recursive: true }); - const uniqueSuffix = (diagnosticCounter++).toString(); - const sanitizedTimestamp = diagnostic.timestamp.replace( - /[^a-zA-Z0-9.-]/g, - "" - ); - const jsonPath = import_path.default.resolve( - diagnosticsPath, - `codeql-action-${sanitizedTimestamp}-${uniqueSuffix}.json` - ); - (0, import_fs.writeFileSync)(jsonPath, JSON.stringify(diagnostic)); - } catch (err) { - logger.warning(`Unable to write diagnostic message to database: ${err}`); - logger.debug(JSON.stringify(diagnostic)); - } -} -function logUnwrittenDiagnostics() { - const logger = getActionsLogger(); - const num = unwrittenDiagnostics.length; - if (num > 0) { - logger.warning( - `${num} diagnostic(s) could not be written to the database and will not appear on the Tool Status Page.` - ); - for (const unwritten of unwrittenDiagnostics) { - logger.debug(JSON.stringify(unwritten.diagnostic)); - } - } -} -function flushDiagnostics(config) { - const logger = getActionsLogger(); - const diagnosticsCount = unwrittenDiagnostics.length + unwrittenDefaultLanguageDiagnostics.length; - logger.debug(`Writing ${diagnosticsCount} diagnostic(s) to database.`); - for (const unwritten of unwrittenDiagnostics) { - writeDiagnostic(config, unwritten.language, unwritten.diagnostic); - } - for (const unwritten of unwrittenDefaultLanguageDiagnostics) { - addNoLanguageDiagnostic(config, unwritten); - } - unwrittenDiagnostics = []; - unwrittenDefaultLanguageDiagnostics = []; -} -function makeTelemetryDiagnostic(id, name, attributes) { - return makeDiagnostic(id, name, { - attributes, - visibility: { - cliSummaryTable: false, - statusPage: false, - telemetry: true - } - }); -} - // src/diff-informed-analysis-utils.ts var fs6 = __toESM(require("fs")); async function getDiffInformedAnalysisBranches(codeql, features, logger) { @@ -149313,32 +149520,69 @@ async function applyIncrementalAnalysisSettings(config, hasDiffRanges, codeql, l }); } } -async function initConfig(actionState, inputs) { - const { logger, features } = actionState; - const { tempDir } = inputs; +async function determineUserConfig(action, tempDir, inputs) { + const validateConfig = await action.features.getValue( + "validate_db_config" /* ValidateDbConfig */ + ); if (inputs.configInput) { - if (inputs.configFile) { - logger.warning( - `Both a config file and config input were provided. Ignoring config file.` + const computedConfigPath = userConfigFromActionPath(tempDir); + const allowMergeConfigs = () => action.features.getValue("allow_merge_config_files" /* AllowMergeConfigFiles */); + if (inputs.configFile && isDefaultSetup(action.env) && await allowMergeConfigs()) { + const fromConfigInput = parseUserConfig( + action.logger, + "`config` input", + inputs.configInput, + validateConfig + ); + const fromConfigFile = await loadUserConfig( + action, + inputs.configFile, + inputs.workspacePath, + inputs.apiDetails, + tempDir + ); + const mergedConfig = mergeDefaultSetupAndUserConfigs( + action.logger, + fromConfigInput, + fromConfigFile + ); + fs9.writeFileSync(computedConfigPath, dump(mergedConfig)); + action.logger.debug( + `Using merged configurations from 'config' input with configuration from '${inputs.configFile}': ${computedConfigPath}` + ); + inputs.configFile = computedConfigPath; + return mergedConfig; + } else { + if (inputs.configFile) { + action.logger.warning( + `Both a config file and config input were provided. Ignoring config file.` + ); + } + fs9.writeFileSync(computedConfigPath, inputs.configInput); + inputs.configFile = computedConfigPath; + action.logger.debug( + `Using config from action input: ${inputs.configFile}` ); } - inputs.configFile = userConfigFromActionPath(tempDir); - fs9.writeFileSync(inputs.configFile, inputs.configInput); - logger.debug(`Using config from action input: ${inputs.configFile}`); } - let userConfig = {}; if (!inputs.configFile) { - logger.debug("No configuration file was provided"); + action.logger.debug("No configuration file was provided"); + return {}; } else { - logger.debug(`Using configuration file: ${inputs.configFile}`); - userConfig = await loadUserConfig( - actionState, + action.logger.debug(`Using configuration file: ${inputs.configFile}`); + return await loadUserConfig( + action, inputs.configFile, inputs.workspacePath, inputs.apiDetails, tempDir ); } +} +async function initConfig(actionState, inputs) { + const { logger, features } = actionState; + const { tempDir } = inputs; + const userConfig = await determineUserConfig(actionState, tempDir, inputs); const config = await initActionState(inputs, userConfig); if (config.analysisKinds.length === 1 && isCodeQualityEnabled(config)) { if (hasQueryCustomisation(config.computedConfig)) { diff --git a/src/config-utils.test.ts b/src/config-utils.test.ts index 1b042480ba..b7251fa720 100644 --- a/src/config-utils.test.ts +++ b/src/config-utils.test.ts @@ -13,6 +13,7 @@ import { CachingKind } from "./caching-utils"; import { createStubCodeQL } from "./codeql"; import { UserConfig } from "./config/db-config"; import * as configUtils from "./config-utils"; +import { ActionsEnvVars } from "./environment"; import * as errorMessages from "./error-messages"; import { Feature } from "./feature-flags"; import { RepositoryProperties } from "./feature-flags/properties"; @@ -36,7 +37,10 @@ import { mockCodeQLVersion, createTestConfig, makeMacro, + RecordingLogger, + DEFAULT_ACTIONS_VARS, initAllState, + callee, } from "./testing-utils"; import { GitHubVariant, @@ -462,6 +466,24 @@ test.serial("load non-existent input", async (t) => { }); }); +/** A non-empty, but fairly minimal configuration file. */ +const simpleConfigFileContents = ` + name: my config + queries: + - uses: ./foo_file`; + +/** A less minimal configuration file. */ +const otherConfigFileContents = ` + name: my config + disable-default-queries: true + queries: + - uses: ./foo + paths-ignore: + - a + - b + paths: + - c/d`; + test.serial("load non-empty input", async (t) => { return await withTmpDir(async (tempDir) => { setupActionsVars(tempDir, tempDir); @@ -476,18 +498,6 @@ test.serial("load non-empty input", async (t) => { }, }); - // Just create a generic config object with non-default values for all fields - const inputFileContents = ` - name: my config - disable-default-queries: true - queries: - - uses: ./foo - paths-ignore: - - a - - b - paths: - - c/d`; - fs.mkdirSync(path.join(tempDir, "foo")); const userConfig: UserConfig = { @@ -514,7 +524,7 @@ test.serial("load non-empty input", async (t) => { }); const languagesInput = "javascript"; - const configFilePath = createConfigFile(inputFileContents, tempDir); + const configFilePath = createConfigFile(otherConfigFileContents, tempDir); const state = initAllState(); const actualConfig = await configUtils.initConfig( @@ -540,14 +550,12 @@ test.serial( "Using config input and file together, config input should be used.", async (t) => { return await withTmpDir(async (tempDir) => { - process.env["RUNNER_TEMP"] = tempDir; - process.env["GITHUB_WORKSPACE"] = tempDir; + setupActionsVars(tempDir, tempDir); - const inputFileContents = ` - name: my config - queries: - - uses: ./foo_file`; - const configFilePath = createConfigFile(inputFileContents, tempDir); + const configFilePath = createConfigFile( + simpleConfigFileContents, + tempDir, + ); const configInput = ` name: my config @@ -576,7 +584,7 @@ test.serial( // Only JS, python packs will be ignored const languagesInput = "javascript"; - const state = initAllState(); + const state = initAllState({ env: util.getEnv() }); const config = await configUtils.initConfig( state, createTestInitConfigInputs({ @@ -2259,3 +2267,328 @@ test("applyIncrementalAnalysisSettings: adds exclusions for diff-informed-only r { exclude: { tags: "exclude-from-incremental" } }, ]); }); + +test("determineUserConfig - empty config when neither input is specified", async (t) => { + await withTmpDir(async (tmpDir) => { + const target = callee(configUtils.determineUserConfig) + .withEnv(util.getEnv(DEFAULT_ACTIONS_VARS)) + .withFeatures([]) + .withArgs( + tmpDir, + createTestInitConfigInputs({ + configInput: undefined, + configFile: undefined, + workspacePath: tmpDir, + }), + ); + + // The returned configuration should be empty. + await target.passes(async (fn) => t.deepEqual(await fn(), {})); + + const logger = target.getLogger(); + // And the fact that no configuration was provided should have been logged, + // but not the messages for the two input sources. + t.true(logger.hasMessage("No configuration file was provided")); + t.false(logger.hasMessage("Using config from action input:")); + t.false(logger.hasMessage("Using configuration file:")); + // And the warning about both inputs should not have been logged. + t.false( + logger.hasMessage( + "Both a config file and config input were provided. Ignoring config file.", + ), + ); + }); +}); + +test("determineUserConfig - loads config file", async (t) => { + await withTmpDir(async (tmpDir) => { + const configFilePath = createConfigFile(simpleConfigFileContents, tmpDir); + const logger = new RecordingLogger(); + const env = util.getEnv(DEFAULT_ACTIONS_VARS); + + const inputs = createTestInitConfigInputs({ + configInput: undefined, + configFile: configFilePath, + workspacePath: tmpDir, + }); + const result = await configUtils.determineUserConfig( + initAllState({ logger, env }), + tmpDir, + inputs, + ); + + // The loaded configuration should match `simpleConfigFileContents`. + t.deepEqual(result, { + name: "my config", + queries: [{ uses: "./foo_file" }], + }); + // The `configFile` input should not have changed. + t.is(inputs.configFile, configFilePath); + // And the path of the input config file should have been logged, while the + // other two origin messages should not have been logged. + t.true(logger.hasMessage(`Using configuration file: ${configFilePath}`)); + t.false(logger.hasMessage("No configuration file was provided")); + t.false(logger.hasMessage("Using config from action input:")); + // But the warning about both inputs should not have been logged. + t.false( + logger.hasMessage( + "Both a config file and config input were provided. Ignoring config file.", + ), + ); + }); +}); + +test("determineUserConfig - loads config input", async (t) => { + await withTmpDir(async (tmpDir) => { + const logger = new RecordingLogger(); + const env = util.getEnv(DEFAULT_ACTIONS_VARS); + const expectedConfigPath = configUtils.userConfigFromActionPath(tmpDir); + + const inputs = createTestInitConfigInputs({ + configInput: simpleConfigFileContents, + configFile: undefined, + workspacePath: tmpDir, + }); + const result = await configUtils.determineUserConfig( + initAllState({ logger, env }), + tmpDir, + inputs, + ); + + // The loaded configuration should match `simpleConfigFileContents`. + t.deepEqual(result, { + name: "my config", + queries: [{ uses: "./foo_file" }], + }); + // The `configFile` input should have been mutated to the generated path. + t.is(inputs.configFile, expectedConfigPath); + // And the input source and path of the generated config file should have been logged, + // while the message about no configuration input should not have been logged. + t.true(logger.hasMessage("Using config from action input:")); + t.true( + logger.hasMessage(`Using configuration file: ${expectedConfigPath}`), + ); + t.false(logger.hasMessage("No configuration file was provided")); + // But the warning about both inputs should not have been logged. + t.false( + logger.hasMessage( + "Both a config file and config input were provided. Ignoring config file.", + ), + ); + }); +}); + +test("determineUserConfig - ignores config file input when both specified", async (t) => { + await withTmpDir(async (tmpDir) => { + const logger = new RecordingLogger(); + const env = util.getEnv(DEFAULT_ACTIONS_VARS); + const configFilePath = createConfigFile(otherConfigFileContents, tmpDir); + const expectedConfigPath = configUtils.userConfigFromActionPath(tmpDir); + + const inputs = createTestInitConfigInputs({ + configInput: simpleConfigFileContents, + configFile: configFilePath, + workspacePath: tmpDir, + }); + const result = await configUtils.determineUserConfig( + initAllState({ logger, env }), + tmpDir, + inputs, + ); + + // The loaded configuration should match `simpleConfigFileContents`. + t.deepEqual(result, { + name: "my config", + queries: [{ uses: "./foo_file" }], + }); + // The `configFile` input should have been mutated to the generated path. + t.is(inputs.configFile, expectedConfigPath); + // And the path of the generated config file should have been logged. + t.true( + logger.hasMessage( + `Using config from action input: ${expectedConfigPath}`, + ), + ); + t.true( + logger.hasMessage(`Using configuration file: ${expectedConfigPath}`), + ); + t.false(logger.hasMessage("No configuration file was provided")); + // And the warning about both inputs should have been logged. + t.true( + logger.hasMessage( + "Both a config file and config input were provided. Ignoring config file.", + ), + ); + }); +}); + +/** A `config` input that we might get from Default Setup. */ +const defaultSetupConfigInput = ` + threat-models: [local, remote] + default-setup: + org: + model-packs: [foo, bar]`; + +test("determineUserConfig - merges configs if FF is enabled in Default Setup", async (t) => { + await withTmpDir(async (tmpDir) => { + const logger = new RecordingLogger(true); + const env = util.getEnv({ + ...DEFAULT_ACTIONS_VARS, + [ActionsEnvVars.GITHUB_EVENT_NAME]: "dynamic", + }); + const configFilePath = createConfigFile(simpleConfigFileContents, tmpDir); + const expectedConfigPath = configUtils.userConfigFromActionPath(tmpDir); + + const inputs = createTestInitConfigInputs({ + configInput: defaultSetupConfigInput, + configFile: configFilePath, + workspacePath: tmpDir, + }); + const result = await configUtils.determineUserConfig( + initAllState({ + logger, + env, + features: createFeatures([Feature.AllowMergeConfigFiles]), + }), + tmpDir, + inputs, + ); + + // The loaded configuration should match the result of merging + // `defaultSetupConfigInput` and `simpleConfigFileContents`. + const expectedConfig = { + name: "my config", + queries: [{ uses: "./foo_file" }], + "threat-models": ["local", "remote"], + "default-setup": { + org: { + "model-packs": ["foo", "bar"], + }, + }, + } satisfies UserConfig; + t.deepEqual(result, expectedConfig); + + // The `configFile` input should have been mutated to the generated path. + t.is(inputs.configFile, expectedConfigPath); + + // Since `result` is the result of merging the configurations in-memory, + // also check whether loading the configuration from disk that was written + // by `determineUserConfig` matches our expectations. + const loadedFromDisk = configUtils.getLocalConfig( + logger, + expectedConfigPath, + false, + ); + t.deepEqual(loadedFromDisk, expectedConfig); + + // And the appropriate origin messages should have been logged. + t.true( + logger.hasMessage( + `Using merged configurations from 'config' input with configuration from '${configFilePath}': ${expectedConfigPath}`, + ), + ); + t.false( + logger.hasMessage(`Using configuration file: ${expectedConfigPath}`), + ); + t.false(logger.hasMessage("No configuration file was provided")); + t.false( + logger.hasMessage( + `Using config from action input: ${expectedConfigPath}`, + ), + ); + t.false( + logger.hasMessage( + "Both a config file and config input were provided. Ignoring config file.", + ), + ); + }); +}); + +test("determineUserConfig - ignores config file input in Default Setup if FF is off", async (t) => { + await withTmpDir(async (tmpDir) => { + const logger = new RecordingLogger(true); + const env = util.getEnv({ + ...DEFAULT_ACTIONS_VARS, + [ActionsEnvVars.GITHUB_EVENT_NAME]: "dynamic", + }); + const configFilePath = createConfigFile(otherConfigFileContents, tmpDir); + const expectedConfigPath = configUtils.userConfigFromActionPath(tmpDir); + + const result = await configUtils.determineUserConfig( + initAllState({ logger, env }), + tmpDir, + createTestInitConfigInputs({ + configInput: simpleConfigFileContents, + configFile: configFilePath, + workspacePath: tmpDir, + }), + ); + + // The loaded configuration should match `simpleConfigFileContents`. + t.deepEqual(result, { + name: "my config", + queries: [{ uses: "./foo_file" }], + }); + // And the path of the generated config file should have been logged. + t.true( + logger.hasMessage( + `Using config from action input: ${expectedConfigPath}`, + ), + ); + t.true( + logger.hasMessage(`Using configuration file: ${expectedConfigPath}`), + ); + t.false(logger.hasMessage("No configuration file was provided")); + // And the warning about both inputs should have been logged. + t.true( + logger.hasMessage( + "Both a config file and config input were provided. Ignoring config file.", + ), + ); + }); +}); + +test("determineUserConfig - ignores config file input outside Default Setup if FF is on", async (t) => { + await withTmpDir(async (tmpDir) => { + const logger = new RecordingLogger(true); + const env = util.getEnv(DEFAULT_ACTIONS_VARS); + const configFilePath = createConfigFile(otherConfigFileContents, tmpDir); + const expectedConfigPath = configUtils.userConfigFromActionPath(tmpDir); + + const result = await configUtils.determineUserConfig( + initAllState({ + logger, + env, + features: createFeatures([Feature.AllowMergeConfigFiles]), + }), + tmpDir, + createTestInitConfigInputs({ + configInput: simpleConfigFileContents, + configFile: configFilePath, + workspacePath: tmpDir, + }), + ); + + // The loaded configuration should match `simpleConfigFileContents`. + t.deepEqual(result, { + name: "my config", + queries: [{ uses: "./foo_file" }], + }); + // And the path of the generated config file should have been logged. + t.true( + logger.hasMessage( + `Using config from action input: ${expectedConfigPath}`, + ), + ); + t.true( + logger.hasMessage(`Using configuration file: ${expectedConfigPath}`), + ); + t.false(logger.hasMessage("No configuration file was provided")); + // And the warning about both inputs should have been logged. + t.true( + logger.hasMessage( + "Both a config file and config input were provided. Ignoring config file.", + ), + ); + }); +}); diff --git a/src/config-utils.ts b/src/config-utils.ts index 747fd19a83..b48d5c1d7c 100644 --- a/src/config-utils.ts +++ b/src/config-utils.ts @@ -10,6 +10,7 @@ import { getActionVersion, getOptionalInput, isAnalyzingPullRequest, + isDefaultSetup, isDynamicWorkflow, } from "./actions-util"; import { @@ -26,6 +27,7 @@ import { calculateAugmentation, ExcludeQueryFilter, generateCodeScanningConfig, + mergeDefaultSetupAndUserConfigs, parseUserConfig, UserConfig, } from "./config/db-config"; @@ -466,6 +468,16 @@ async function downloadCacheWithTime( return { trapCaches, trapCacheDownloadTime }; } +/** + * Loads a CLI configuration file from `configFile`. + * + * @param actionState The Action state. + * @param configFile The address of the configuration file. + * @param workspacePath The workspace path, used to check that the configuration file exists relative to it. + * @param apiDetails Information for how to access the API to fetch remote files. + * @param tempDir The temporary directory which may contain a CodeQL Action-generated configuration file. + * @returns The loaded configuration file, if successful. + */ async function loadUserConfig( actionState: ActionState<["Logger", "Env", "FeatureFlags"]>, configFile: string, @@ -936,7 +948,11 @@ function dbLocationOrDefault( return dbLocation || path.resolve(tempDir, "codeql_databases"); } -function userConfigFromActionPath(tempDir: string): string { +/** + * Gets the path for the CodeQL Action-generated configuration file, + * which is used to store the `config` input. + */ +export function userConfigFromActionPath(tempDir: string): string { return path.resolve(tempDir, "user-config-from-action.yml"); } @@ -997,43 +1013,123 @@ export async function applyIncrementalAnalysisSettings( } /** - * Load and return the config. + * Determines where to load the `UserConfig` for the CLI from and loads it. * - * This will parse the config from the user input if present, or generate - * a default config. The parsed config is then stored to a known location. + * @param inputs The Action inputs. The `configFile` value will be mutated + * if a CodeQL Action-generated file should be used. + * + * @returns The loaded `UserConfig`, which might be empty if no configuration + * was specified. */ -export async function initConfig( - actionState: ActionState<["Logger", "Env", "FeatureFlags"]>, +export async function determineUserConfig( + action: ActionState<["Logger", "Env", "FeatureFlags"]>, + tempDir: string, inputs: InitConfigInputs, -): Promise { - const { logger, features } = actionState; - const { tempDir } = inputs; +): Promise { + const validateConfig = await action.features.getValue( + Feature.ValidateDbConfig, + ); - // if configInput is set, it takes precedence over configFile + // We have the following cases: + // 1. A `config` or `config-file` input is provided, but not both: use the provided one. + // 2. Both are provided and we are in an advanced workflow: ignore the `config-file` input. + // 3. Both are provided and we are in Default Setup: the `config` input uses a limited + // set of options, which are supported by `mergeDefaultSetupAndUserConfigs`, + // and we merge the two configs. if (inputs.configInput) { - if (inputs.configFile) { - logger.warning( - `Both a config file and config input were provided. Ignoring config file.`, + const computedConfigPath = userConfigFromActionPath(tempDir); + + // Get a function which enables us to determine whether the FF that allows us to + // merge supported configuration file properties is enabled. We only execute + // this lazily if the other checks pass. + const allowMergeConfigs = () => + action.features.getValue(Feature.AllowMergeConfigFiles); + + // Check whether we also have a `config-file` input and decide what to do. + if ( + inputs.configFile && + isDefaultSetup(action.env) && + (await allowMergeConfigs()) + ) { + // If the FF is enabled and we are in Default Setup, combine the supported + // configuration file properties and write the result to disk. + const fromConfigInput = parseUserConfig( + action.logger, + "`config` input", + inputs.configInput, + validateConfig, + ); + const fromConfigFile = await loadUserConfig( + action, + inputs.configFile, + inputs.workspacePath, + inputs.apiDetails, + tempDir, + ); + + // Write the merged configuration to disk so that it can be loaded subsequently by + // the CLI or other CodeQL Action steps. + const mergedConfig = mergeDefaultSetupAndUserConfigs( + action.logger, + fromConfigInput, + fromConfigFile, + ); + fs.writeFileSync(computedConfigPath, yaml.dump(mergedConfig)); + action.logger.debug( + `Using merged configurations from 'config' input with configuration from '${inputs.configFile}': ${computedConfigPath}`, + ); + + inputs.configFile = computedConfigPath; + return mergedConfig; + } else { + // If we are in this branch and there is a `config-file` input, then it means + // we didn't meet the conditions for merging the configurations. Warn the user + // that the configuration file will be ignored. + if (inputs.configFile) { + action.logger.warning( + `Both a config file and config input were provided. Ignoring config file.`, + ); + } + + // Write the `config` input straight to disk. + fs.writeFileSync(computedConfigPath, inputs.configInput); + inputs.configFile = computedConfigPath; + action.logger.debug( + `Using config from action input: ${inputs.configFile}`, ); } - inputs.configFile = userConfigFromActionPath(tempDir); - fs.writeFileSync(inputs.configFile, inputs.configInput); - logger.debug(`Using config from action input: ${inputs.configFile}`); } - let userConfig: UserConfig = {}; + // Load whatever configuration file we have, if any. if (!inputs.configFile) { - logger.debug("No configuration file was provided"); + action.logger.debug("No configuration file was provided"); + return {}; } else { - logger.debug(`Using configuration file: ${inputs.configFile}`); - userConfig = await loadUserConfig( - actionState, + action.logger.debug(`Using configuration file: ${inputs.configFile}`); + return await loadUserConfig( + action, inputs.configFile, inputs.workspacePath, inputs.apiDetails, tempDir, ); } +} + +/** + * Load and return the config. + * + * This will parse the config from the user input if present, or generate + * a default config. The parsed config is then stored to a known location. + */ +export async function initConfig( + actionState: ActionState<["Logger", "Env", "FeatureFlags"]>, + inputs: InitConfigInputs, +): Promise { + const { logger, features } = actionState; + const { tempDir } = inputs; + + const userConfig = await determineUserConfig(actionState, tempDir, inputs); const config = await initActionState(inputs, userConfig); @@ -1190,7 +1286,7 @@ function isLocal(configPath: string): boolean { return configPath.indexOf("@") === -1; } -function getLocalConfig( +export function getLocalConfig( logger: Logger, configFile: string, validateConfig: boolean, diff --git a/src/config/db-config.test.ts b/src/config/db-config.test.ts index ca0061e136..63d9d2ffec 100644 --- a/src/config/db-config.test.ts +++ b/src/config/db-config.test.ts @@ -8,6 +8,7 @@ import { getRecordingLogger, LoggedMessage, makeMacro, + RecordingLogger, } from "../testing-utils"; import { ConfigurationError, prettyPrintPack } from "../util"; @@ -488,3 +489,139 @@ test("parseUserConfig - throws no ConfigurationError if validation should fail, ), ); }); + +test("mergeDefaultSetupAndUserConfigs - combines threat models", async (t) => { + const logger = new RecordingLogger(); + const result = dbConfig.mergeDefaultSetupAndUserConfigs( + logger, + { "threat-models": ["a", "b"] }, + { "threat-models": ["local", "remote"] }, + ); + + const threatModels = result["threat-models"]; + + if (t.truthy(threatModels)) { + t.deepEqual(threatModels, ["a", "b", "local", "remote"]); + } +}); + +test("mergeDefaultSetupAndUserConfigs - warns if user-supplied config contains default setup key", async (t) => { + const logger = new RecordingLogger(); + const result = dbConfig.mergeDefaultSetupAndUserConfigs( + logger, + {}, + { "default-setup": {} }, + ); + + // User-supplied value is ignored. + t.deepEqual(result, {}); + + // Warning is logged. + t.true( + logger.hasMessage( + "The 'default-setup' configuration key is not supported in user-supplied configuration files", + ), + ); +}); + +test("mergeDefaultSetupAndUserConfigs - keeps default setup key from 'config' input", async (t) => { + const logger = new RecordingLogger(); + const expected: dbConfig.DefaultSetupConfig = { + org: { "model-packs": ["some-pack"] }, + }; + const result = dbConfig.mergeDefaultSetupAndUserConfigs( + logger, + { "default-setup": expected }, + {}, + ); + + // Result matches the input. + t.deepEqual(result["default-setup"], expected); + + // No warning is logged. + t.false( + logger.hasMessage( + "The 'default-setup' configuration key is not supported in user-supplied configuration files", + ), + ); +}); + +test("mergeDefaultSetupAndUserConfigs - keeps other properties from user-supplied configuration", async (t) => { + const logger = new RecordingLogger(); + const configFile: dbConfig.UserConfig = { + "query-filters": [{ exclude: { a: "b" } }], + "paths-ignore": ["path"], + }; + + const result = dbConfig.mergeDefaultSetupAndUserConfigs( + logger, + {}, + configFile, + ); + + t.deepEqual(result, configFile); +}); + +test("mergeDefaultSetupAndUserConfigs - ignores, but warns about, unknown keys from Default Setup", async (t) => { + const logger = new RecordingLogger(); + const configFile: dbConfig.UserConfig = { + "query-filters": [{ exclude: { a: "b" } }], + "paths-ignore": ["path"], + }; + + const result = dbConfig.mergeDefaultSetupAndUserConfigs( + logger, + { + "default-setup": { + borg: [], + org: { + unknown: "foo", + "model-packs": [], + }, + } as unknown as dbConfig.DefaultSetupConfig, + "paths-ignore": ["other-path"], + }, + configFile, + ); + + t.deepEqual(result, { + ...configFile, + "default-setup": { org: { "model-packs": [] } }, + }); + + const expectedUnrecognisedKeys = [ + ".default-setup.org.unknown", + ".default-setup.borg", + ".paths-ignore", + ].join(", "); + checkExpectedLogMessages(t, logger.messages, [ + `Unrecognised keys in Default Setup configuration: ${expectedUnrecognisedKeys}`, + ]); +}); + +test("mergeDefaultSetupAndUserConfigs - warns about invalid keys from Default Setup", async (t) => { + const logger = new RecordingLogger(); + const configFile: dbConfig.UserConfig = {}; + + const result = dbConfig.mergeDefaultSetupAndUserConfigs( + logger, + { + "default-setup": { + org: { + "model-packs": [123], + }, + } as unknown as dbConfig.DefaultSetupConfig, + }, + configFile, + ); + + t.deepEqual(result, { + ...configFile, + "default-setup": { org: { "model-packs": [123] } }, + }); + + const expectedInvalidKeys = [".default-setup.org.model-packs[0]"].join(", "); + checkExpectedLogMessages(t, logger.messages, [ + `Invalid keys in Default Setup configuration: ${expectedInvalidKeys}`, + ]); +}); diff --git a/src/config/db-config.ts b/src/config/db-config.ts index a84a20f247..582cfe8afc 100644 --- a/src/config/db-config.ts +++ b/src/config/db-config.ts @@ -4,11 +4,16 @@ import * as yaml from "js-yaml"; import * as jsonschema from "jsonschema"; import * as semver from "semver"; +import { + addNoLanguageDiagnostic, + makeTelemetryDiagnostic, +} from "../diagnostics"; import * as errorMessages from "../error-messages"; import { RepositoryProperties, RepositoryPropertyName, } from "../feature-flags/properties"; +import * as json from "../json"; import { Language } from "../languages"; import { Logger } from "../logging"; import { cloneObject, ConfigurationError, prettyPrintPack } from "../util"; @@ -28,6 +33,21 @@ export interface QuerySpec { uses: string; } +const ORG_SCHEMA = { + /** An array of model pack names. */ + "model-packs": json.optional(json.array(json.string)), +} as const satisfies json.Schema; + +/** Not intended to be provided directly by a user. */ +export type OrgType = json.FromSchema; + +const DEFAULT_SETUP_SCHEMA = { + org: json.optional(json.object(ORG_SCHEMA)), +} as const satisfies json.Schema; + +/** Not intended to be provided directly by a user. */ +export type DefaultSetupConfig = json.FromSchema; + /** * Format of the config file supplied by the user. */ @@ -46,6 +66,117 @@ export interface UserConfig { // Set of query filters to include and exclude extra queries based on // codeql query suite `include` and `exclude` properties "query-filters"?: QueryFilter[]; + + /** An array (possibly empty or absent) of threat models to use. */ + "threat-models"?: string[]; + + /** + * Configuration options that are reserved for us in Default Setup and + * not intended to be supplied directly by users. + */ + "default-setup"?: DefaultSetupConfig; +} + +/** A subset of the `UserConfig` schema that is used by Default Setup. */ +const DEFAULT_SETUP_CONFIG_SCHEMA = { + "threat-models": json.optional(json.array(json.string)), + "default-setup": json.optional( + json.object(DEFAULT_SETUP_SCHEMA), + ), +} as const satisfies json.Schema; + +/** + * Merges supported properties from two configuration files. This is intended only for + * use with merging the `config` input provided by Default Setup with a potentially + * richer configuration file provided by a user. + * + * @param logger The logger to use. + * @param fromConfigInput The configuration from Default Setup. + * @param fromConfigFile The user-supplied configuration. + * @returns The combination of both configuration files. + */ +export function mergeDefaultSetupAndUserConfigs( + logger: Logger, + fromConfigInput: UserConfig, + fromConfigFile: UserConfig, +): UserConfig { + logger.debug( + "Combining configuration files from 'config' and 'config-file' inputs", + ); + + // Check for unexpected keys in the configuration from the `config` input + // that was provided by Default Setup. This should only contain the keys + // we would expect to receive from Default Setup. + const schemaCheckResult = json.checkSchema( + DEFAULT_SETUP_CONFIG_SCHEMA, + fromConfigInput as json.UnvalidatedObject, + ); + + // Report any invalid or unrecognised keys. + if (schemaCheckResult.invalidKeys.length > 0) { + logger.warning( + `Invalid keys in Default Setup configuration: ${schemaCheckResult.invalidKeys.join(", ")}`, + ); + addNoLanguageDiagnostic( + undefined, + makeTelemetryDiagnostic( + "codeql-action/invalid-default-setup-config-keys", + "Invalid Default Setup configuration keys", + { + invalidKeys: schemaCheckResult.invalidKeys, + }, + ), + ); + } + if (schemaCheckResult.unknownKeys.length > 0) { + logger.warning( + `Unrecognised keys in Default Setup configuration: ${schemaCheckResult.unknownKeys.join(", ")}`, + ); + addNoLanguageDiagnostic( + undefined, + makeTelemetryDiagnostic( + "codeql-action/unrecognised-default-setup-config-keys", + "Unrecognised Default Setup configuration keys", + { + unrecognisedKeys: schemaCheckResult.unknownKeys, + }, + ), + ); + } + + // Combine all specified threat models from both sources. + const threatModels = new Set(fromConfigInput["threat-models"] || []); + for (const configFileThreatModel of fromConfigFile["threat-models"] || []) { + threatModels.add(configFileThreatModel); + } + + // Warn if there is a 'default-setup' configuration key in the user-supplied configuration, + // since it is not meant to be used and we therefore ignore it here. + if (fromConfigFile["default-setup"]) { + logger.warning( + `The 'default-setup' configuration key is not supported in user-supplied configuration files and will be ignored.`, + ); + } + + // Since we expect the `fromConfigInput` configuration to be provided by Default Setup, + // we expect a limited set of options. Therefore, we base the overall configuration on + // the one provided via the `config-file` input, which may be richer. + const result = { ...fromConfigFile }; + delete result["threat-models"]; + delete result["default-setup"]; + + if (fromConfigInput["default-setup"]?.org?.["model-packs"]) { + result["default-setup"] = { + org: { + "model-packs": fromConfigInput["default-setup"].org["model-packs"], + }, + }; + } + if (threatModels.size > 0) { + result["threat-models"] = Array.from(threatModels); + } + + return result; } /** diff --git a/src/feature-flags.ts b/src/feature-flags.ts index dcc5c9d7bd..05e0b79019 100644 --- a/src/feature-flags.ts +++ b/src/feature-flags.ts @@ -70,6 +70,8 @@ export interface CodeQLDefaultVersionInfo { * Legacy features should end with `_enabled`. */ export enum Feature { + /** Allows supported properties of configuration files to be merged. */ + AllowMergeConfigFiles = "allow_merge_config_files", /** Controls whether we allow multiple values for the `analysis-kinds` input. */ AllowMultipleAnalysisKinds = "allow_multiple_analysis_kinds", AllowToolcacheInput = "allow_toolcache_input", @@ -173,6 +175,11 @@ export type FeatureConfig = { }; export const featureConfig = { + [Feature.AllowMergeConfigFiles]: { + defaultValue: false, + envVar: "CODEQL_ACTION_ALLOW_MERGE_CONFIG_FILES", + minimumVersion: undefined, + }, [Feature.AllowMultipleAnalysisKinds]: { defaultValue: false, envVar: "CODEQL_ACTION_ALLOW_MULTIPLE_ANALYSIS_KINDS", diff --git a/src/json/index.test.ts b/src/json/index.test.ts index eb259c757e..df682fd8b9 100644 --- a/src/json/index.test.ts +++ b/src/json/index.test.ts @@ -67,3 +67,76 @@ test("validateSchema - optional properties are optional", async (t) => { t.true(json.validateSchema(optionalSchema, { optionalKey: "" })); t.true(json.validateSchema(optionalSchema, { optionalKey: "foo" })); }); + +const arraySchema = { + arrayKey: json.array(json.number), +}; + +test("validateSchema - validates arrays", async (t) => { + // Arrays of numeric elements are accepted. + t.true(json.validateSchema(arraySchema, { arrayKey: [] })); + t.true(json.validateSchema(arraySchema, { arrayKey: [4] })); + t.true(json.validateSchema(arraySchema, { arrayKey: [4, 8] })); + t.true(json.validateSchema(arraySchema, { arrayKey: [4, 8, 15] })); + + // Other array elements are not accepted. + t.false(json.validateSchema(arraySchema, { arrayKey: [4, 8, 15, "bar"] })); + t.false(json.validateSchema(arraySchema, { arrayKey: [4, 8, undefined] })); + t.false(json.validateSchema(arraySchema, { arrayKey: [4, 8, 15, null] })); +}); + +const objectSchema = { + objectKey: json.object(arraySchema), +}; + +test("validateSchema - validates objects", async (t) => { + // Objects of the given schema are accepted. + t.true(json.validateSchema(objectSchema, { objectKey: { arrayKey: [] } })); + t.true(json.validateSchema(objectSchema, { objectKey: { arrayKey: [4] } })); + + // Other values are not accepted. + t.false(json.validateSchema(objectSchema, {})); + t.false(json.validateSchema(objectSchema, { objectKey: [] })); + t.false(json.validateSchema(objectSchema, { objectKey: undefined })); + t.false(json.validateSchema(objectSchema, { objectKey: null })); + t.false(json.validateSchema(objectSchema, { objectKey: "foo" })); + t.false(json.validateSchema(objectSchema, { objectKey: 123 })); +}); + +const checkSchemaTestSchema = { + rootKey: json.object(objectSchema), +}; + +test("validateSchema - checkSchema reports unknown keys", async (t) => { + const result = json.checkSchema(checkSchemaTestSchema, { + rootKey: { + objectKey: { + arrayKey: [], + }, + nestedExtraKey: "foo", + }, + extraKey: "bar", + }); + + t.true(result.valid); + t.deepEqual( + result.unknownKeys.sort(), + [".extraKey", ".rootKey.nestedExtraKey"].sort(), + ); +}); + +test("validateSchema - checkSchema reports invalid keys", async (t) => { + const result = json.checkSchema(checkSchemaTestSchema, { + rootKey: { + objectKey: { + arrayKey: ["foo"], + }, + }, + }); + + t.false(result.valid); + t.deepEqual( + result.invalidKeys.sort(), + [".rootKey.objectKey.arrayKey[0]"].sort(), + ); +}); diff --git a/src/json/index.ts b/src/json/index.ts index a55de5b589..590a1eb6b3 100644 --- a/src/json/index.ts +++ b/src/json/index.ts @@ -48,23 +48,101 @@ export function isStringOrUndefined( */ export type Validator = { validate: (val: unknown) => val is T; + check: ( + val: unknown, + opts: CheckSchemaOptions, + path: string, + ) => CheckSchemaResult; required: boolean; }; +function defaultCheck( + validate: (val: unknown) => val is any, +): (arg: unknown) => CheckSchemaResult { + return (arg) => ({ unknownKeys: [], invalidKeys: [], valid: validate(arg) }); +} + +function makeValidator( + validate: (arg: unknown) => arg is T, + required: boolean = true, +) { + return { + validate, + check: defaultCheck(validate), + required, + } as const satisfies Validator; +} + /** Extracts `T` from `Validator`. */ export type UnwrapValidator = V extends Validator ? A : never; /** A validator for string fields in schemas. */ -export const string = { - validate: isString, - required: true, -} as const satisfies Validator; +export const string = makeValidator(isString); /** A validator for number fields in schemas. */ -export const number = { - validate: isNumber, - required: true, -} as const satisfies Validator; +export const number = makeValidator(isNumber); + +/** A validator for arrays. */ +export function array(validator: Validator) { + const validate = (val: unknown) => { + return isArray(val) && val.every((e) => validator.validate(e)); + }; + return { + validate, + check: (val: unknown, opts: CheckSchemaOptions, path: string) => { + const result: CheckSchemaResult = successfulCheckSchema(); + + // The value must be an array. + if (!isArray(val)) { + result.valid = false; + return result; + } + + // Validate all elements of the array. + let index = 0; + for (const e of val) { + const elementPath = `${path}[${index}]`; + const eResult = validator.check(e, opts, `${elementPath}`); + + result.unknownKeys.push(...eResult.unknownKeys); + index++; + + if (!eResult.valid) { + result.valid = false; + result.invalidKeys.push(elementPath); + + if (opts.failFast) { + return result; + } + + continue; + } + } + + return result; + }, + required: true, + } as const satisfies Validator; +} + +/** A validator for objects. */ +export function object< + S extends Schema, + T extends UnvalidatedObject = FromSchema, +>(schema: S) { + return { + validate: (val: unknown) => { + return isObject(val) && validateSchema(schema, val); + }, + check: (val, opts, path) => { + if (!isObject(val)) { + return invalidCheckSchema(); + } + return checkSchema(schema, val, opts, path); + }, + required: true, + } as const satisfies Validator; +} /** * Transforms a validator to be optional, accepting `undefined` or `null` for an @@ -75,6 +153,12 @@ export function optionalOrNull(validator: Validator) { validate: (val: unknown) => { return val === undefined || val === null || validator.validate(val); }, + check: (val, opts, path) => { + if (val === undefined || val === null) { + return successfulCheckSchema(); + } + return validator.check(val, opts, path); + }, required: false, } as const satisfies Validator; } @@ -88,6 +172,12 @@ export function optional(validator: Validator) { validate: (val: unknown): val is T | undefined => { return val === undefined || validator.validate(val); }, + check: (val, opts, path) => { + if (val === undefined) { + return successfulCheckSchema(); + } + return validator.check(val, opts, path); + }, required: false, } as const satisfies Validator; } @@ -117,28 +207,126 @@ export type FromSchema = { * @param obj The object to validate. * @returns Asserts that `obj` is of the `schema`'s type if validation is successful. */ -export function validateSchema( +export function validateSchema< + S extends Schema, + T extends UnvalidatedObject = FromSchema, +>(schema: S, obj: UnvalidatedObject): obj is T { + const result = checkSchema(schema, obj, { failFast: true }); + return result.valid; +} + +export interface CheckSchemaOptions { + /** Whether to stop validation after the first error. */ + failFast?: boolean; +} + +export interface CheckSchemaResult { + /** Whether the `obj` satisfies the schema. */ + valid: boolean; + /** Unknown keys that were found during validation. */ + unknownKeys: string[]; + /** Known keys that failed validation. */ + invalidKeys: string[]; +} + +/** + * Convenience function to produce a `CheckSchemaResult` where `valid: true`. + */ +function successfulCheckSchema(): CheckSchemaResult { + return { + valid: true, + unknownKeys: [], + invalidKeys: [], + }; +} + +/** + * Convenience function to produce a `CheckSchemaResult` where `valid: false`. + */ +function invalidCheckSchema(): CheckSchemaResult { + return { + valid: false, + unknownKeys: [], + invalidKeys: [], + }; +} + +export function checkSchema( schema: S, obj: UnvalidatedObject, -): obj is FromSchema { + options: CheckSchemaOptions = {}, + path: string = "", +): CheckSchemaResult { + const result: CheckSchemaResult = successfulCheckSchema(); + const inputKeys = new Set(Object.keys(obj)); + const invalidKeys = new Set(); + for (const [key, validator] of Object.entries(schema)) { const hasKey = key in obj; + // Remove key from set of unrecognised keys. + inputKeys.delete(key); + + // Add the key to the set of invalid keys. We remove it later once + // it passes validation. + invalidKeys.add(key); + // If the property is required, but absent, fail. if (validator.required && !hasKey) { - return false; + result.valid = false; + + if (options.failFast) { + return result; + } + continue; } // If the property is required, but undefined or null, fail. if (validator.required && (obj[key] === undefined || obj[key] === null)) { - return false; + result.valid = false; + + if (options.failFast) { + return result; + } + continue; } // If the property is present, validate it. - if (hasKey && !validator.validate(obj[key])) { - return false; + if (hasKey) { + const checkResult = validator.check(obj[key], options, `${path}.${key}`); + + result.unknownKeys.push(...checkResult.unknownKeys); + result.invalidKeys.push(...checkResult.invalidKeys); + + // If we have invalid keys from the validator, then that means that + // we have a more specific key than `key`. Remove `key` from the results. + if (checkResult.invalidKeys.length > 0) { + invalidKeys.delete(key); + } + + if (!checkResult.valid) { + result.valid = false; + + if (options.failFast) { + return result; + } + continue; + } } + + // If we reach this point, the key has been successfully validated. + invalidKeys.delete(key); + } + + // If there are any remaining keys in `inputKeys`, add them to `unknownKeys`. + for (const remainingKey of inputKeys) { + result.unknownKeys.push(`${path}.${remainingKey}`); + } + + // If there are any remaining keys in `invalidKeys`, add them to the result. + for (const invalidKey of invalidKeys) { + result.invalidKeys.push(`${path}.${invalidKey}`); } - return true; + return result; }