UNPKG

@cloud-copilot/iam-collect

Version:

Collect IAM information from AWS Accounts

122 lines 3.92 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.parseIfPresent = parseIfPresent; exports.stringifyIfPresent = stringifyIfPresent; exports.consistentStringify = consistentStringify; /** * Parse a JSON string if it is present, otherwise return undefined. * * @param value the JSON string to parse, or undefined if not present * @returns the parsed JSON object, or undefined if value is undefined */ function parseIfPresent(value) { if (value === undefined || value === null || value.trim() === '') { return undefined; } return JSON.parse(value); } /** * Stringify a value to JSON if it is present, otherwise return undefined. * * @param value the value to stringify, or undefined if not present * @returns the JSON string or undefined */ function stringifyIfPresent(value) { if (value === undefined) { return undefined; } if (typeof value === 'string') { return value; } return JSON.stringify(value); } /** * Keys that should be ordered predictably in IAM policy documents. */ const predictableIamKeys = { Version: 1, Statement: 2, Sid: 3, Effect: 4, Action: 5, Principal: 6, Resource: 7, Condition: 8 }; /** * Comparator function to sort IAM keys predictably. * * @param a the first key * @param b the second key * @returns the comparison result using predictable IAM key order, or standard system order otherwise */ function iamComparator(a, b) { const aRank = predictableIamKeys[a]; const bRank = predictableIamKeys[b]; if (aRank !== undefined && bRank !== undefined) { return aRank - bRank; } return a.localeCompare(b); } /** * Consistently stringify a JSON object with predictable key ordering. * This includes consistently ordering array elements. * * IAM policy documents have special predictable key ordering for better semantic readability. * * This intentionally does not handle custom classes or circular references. * * @param node the JSON object to stringify * @param spacer the string to use for one level of indentation * @param startingSpaces the current indentation string * @returns the consistently stringified JSON, or undefined if the input was undefined */ function consistentStringify(node, spacer = ' ', startingSpaces = '') { if (node === undefined) { return undefined; } if (node && node.toJSON && typeof node.toJSON === 'function') { node = node.toJSON(); } if (typeof node === 'number') return isFinite(node) ? '' + node : 'null'; if (typeof node !== 'object') return JSON.stringify(node); if (Array.isArray(node)) { if (node.length === 0) return '[]'; const arrayValues = node.map((v) => consistentStringify(v, spacer, startingSpaces + spacer) || 'null'); arrayValues.sort(); let out = '['; for (let i = 0; i < arrayValues.length; i++) { if (arrayValues[i] === undefined) { continue; } if (i > 0) { out += ','; } out += `\n${startingSpaces + spacer}${arrayValues[i]}`; } return out + `\n${startingSpaces}]`; } if (node === null) return 'null'; const keys = Object.keys(node).sort(iamComparator); if (keys.length === 0) return '{}'; const keySpace = startingSpaces + spacer; let out = `{\n`; let valuePrinted = false; for (let i = 0; i < keys.length; i++) { const key = keys[i]; const value = consistentStringify(node[key], spacer, startingSpaces + spacer); if (!value) continue; if (valuePrinted) out += ',\n'; out += keySpace + JSON.stringify(key) + ': ' + value; valuePrinted = true; } return out + `\n${startingSpaces}}`; } //# sourceMappingURL=json.js.map