#!/usr/bin/env node "use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); // src/cli.ts var path4 = __toESM(require("path")); var fs3 = __toESM(require("fs")); var os2 = __toESM(require("os")); var import_child_process2 = require("child_process"); // src/core/platform.ts var os = __toESM(require("os")); var systemPatterns = { linux: "linux", darwin: "(darwin|macos|mac|osx)", win32: "(windows|win)" }; var archPatterns = { x64: "(x86_64|x64|amd64)", arm64: "(aarch64|arm64)" }; function getPlatformInfo(overrides) { const system = (overrides?.system || os.platform()).toLowerCase(); const arch2 = (overrides?.arch || os.arch()).toLowerCase(); return { system, arch: arch2, systemPattern: systemPatterns[system] || system, archPattern: archPatterns[arch2] || arch2 }; } // node_modules/balanced-match/dist/esm/index.js var balanced = (a, b, str) => { const ma = a instanceof RegExp ? maybeMatch(a, str) : a; const mb = b instanceof RegExp ? maybeMatch(b, str) : b; const r = ma !== null && mb != null && range(ma, mb, str); return r && { start: r[0], end: r[1], pre: str.slice(0, r[0]), body: str.slice(r[0] + ma.length, r[1]), post: str.slice(r[1] + mb.length) }; }; var maybeMatch = (reg, str) => { const m = str.match(reg); return m ? m[0] : null; }; var range = (a, b, str) => { let begs, beg, left, right = void 0, result; let ai = str.indexOf(a); let bi = str.indexOf(b, ai + 1); let i = ai; if (ai >= 0 && bi > 0) { if (a === b) { return [ai, bi]; } begs = []; left = str.length; while (i >= 0 && !result) { if (i === ai) { begs.push(i); ai = str.indexOf(a, i + 1); } else if (begs.length === 1) { const r = begs.pop(); if (r !== void 0) result = [r, bi]; } else { beg = begs.pop(); if (beg !== void 0 && beg < left) { left = beg; right = bi; } bi = str.indexOf(b, i + 1); } i = ai < bi && ai >= 0 ? ai : bi; } if (begs.length && right !== void 0) { result = [left, right]; } } return result; }; // node_modules/brace-expansion/dist/esm/index.js var escSlash = "\0SLASH" + Math.random() + "\0"; var escOpen = "\0OPEN" + Math.random() + "\0"; var escClose = "\0CLOSE" + Math.random() + "\0"; var escComma = "\0COMMA" + Math.random() + "\0"; var escPeriod = "\0PERIOD" + Math.random() + "\0"; var escSlashPattern = new RegExp(escSlash, "g"); var escOpenPattern = new RegExp(escOpen, "g"); var escClosePattern = new RegExp(escClose, "g"); var escCommaPattern = new RegExp(escComma, "g"); var escPeriodPattern = new RegExp(escPeriod, "g"); var slashPattern = /\\\\/g; var openPattern = /\\{/g; var closePattern = /\\}/g; var commaPattern = /\\,/g; var periodPattern = /\\\./g; var EXPANSION_MAX = 1e5; function numeric(str) { return !isNaN(str) ? parseInt(str, 10) : str.charCodeAt(0); } function escapeBraces(str) { return str.replace(slashPattern, escSlash).replace(openPattern, escOpen).replace(closePattern, escClose).replace(commaPattern, escComma).replace(periodPattern, escPeriod); } function unescapeBraces(str) { return str.replace(escSlashPattern, "\\").replace(escOpenPattern, "{").replace(escClosePattern, "}").replace(escCommaPattern, ",").replace(escPeriodPattern, "."); } function parseCommaParts(str) { if (!str) { return [""]; } const parts = []; const m = balanced("{", "}", str); if (!m) { return str.split(","); } const { pre, body, post } = m; const p = pre.split(","); p[p.length - 1] += "{" + body + "}"; const postParts = parseCommaParts(post); if (post.length) { ; p[p.length - 1] += postParts.shift(); p.push.apply(p, postParts); } parts.push.apply(parts, p); return parts; } function expand(str, options = {}) { if (!str) { return []; } const { max = EXPANSION_MAX } = options; if (str.slice(0, 2) === "{}") { str = "\\{\\}" + str.slice(2); } return expand_(escapeBraces(str), max, true).map(unescapeBraces); } function embrace(str) { return "{" + str + "}"; } function isPadded(el) { return /^-?0\d/.test(el); } function lte(i, y) { return i <= y; } function gte(i, y) { return i >= y; } function expand_(str, max, isTop) { const expansions = []; const m = balanced("{", "}", str); if (!m) return [str]; const pre = m.pre; const post = m.post.length ? expand_(m.post, max, false) : [""]; if (/\$$/.test(m.pre)) { for (let k = 0; k < post.length && k < max; k++) { const expansion = pre + "{" + m.body + "}" + post[k]; expansions.push(expansion); } } else { const isNumericSequence = /^-?\d+\.\.-?\d+(?:\.\.-?\d+)?$/.test(m.body); const isAlphaSequence = /^[a-zA-Z]\.\.[a-zA-Z](?:\.\.-?\d+)?$/.test(m.body); const isSequence = isNumericSequence || isAlphaSequence; const isOptions = m.body.indexOf(",") >= 0; if (!isSequence && !isOptions) { if (m.post.match(/,(?!,).*\}/)) { str = m.pre + "{" + m.body + escClose + m.post; return expand_(str, max, true); } return [str]; } let n; if (isSequence) { n = m.body.split(/\.\./); } else { n = parseCommaParts(m.body); if (n.length === 1 && n[0] !== void 0) { n = expand_(n[0], max, false).map(embrace); if (n.length === 1) { return post.map((p) => m.pre + n[0] + p); } } } let N; if (isSequence && n[0] !== void 0 && n[1] !== void 0) { const x = numeric(n[0]); const y = numeric(n[1]); const width = Math.max(n[0].length, n[1].length); let incr = n.length === 3 && n[2] !== void 0 ? Math.max(Math.abs(numeric(n[2])), 1) : 1; let test = lte; const reverse = y < x; if (reverse) { incr *= -1; test = gte; } const pad = n.some(isPadded); N = []; for (let i = x; test(i, y); i += incr) { let c; if (isAlphaSequence) { c = String.fromCharCode(i); if (c === "\\") { c = ""; } } else { c = String(i); if (pad) { const need = width - c.length; if (need > 0) { const z = new Array(need + 1).join("0"); if (i < 0) { c = "-" + z + c.slice(1); } else { c = z + c; } } } } N.push(c); } } else { N = []; for (let j = 0; j < n.length; j++) { N.push.apply(N, expand_(n[j], max, false)); } } for (let j = 0; j < N.length; j++) { for (let k = 0; k < post.length && expansions.length < max; k++) { const expansion = pre + N[j] + post[k]; if (!isTop || isSequence || expansion) { expansions.push(expansion); } } } } return expansions; } // node_modules/minimatch/dist/esm/assert-valid-pattern.js var MAX_PATTERN_LENGTH = 1024 * 64; var assertValidPattern = (pattern) => { if (typeof pattern !== "string") { throw new TypeError("invalid pattern"); } if (pattern.length > MAX_PATTERN_LENGTH) { throw new TypeError("pattern is too long"); } }; // node_modules/minimatch/dist/esm/brace-expressions.js var posixClasses = { "[:alnum:]": ["\\p{L}\\p{Nl}\\p{Nd}", true], "[:alpha:]": ["\\p{L}\\p{Nl}", true], "[:ascii:]": ["\\x00-\\x7f", false], "[:blank:]": ["\\p{Zs}\\t", true], "[:cntrl:]": ["\\p{Cc}", true], "[:digit:]": ["\\p{Nd}", true], "[:graph:]": ["\\p{Z}\\p{C}", true, true], "[:lower:]": ["\\p{Ll}", true], "[:print:]": ["\\p{C}", true], "[:punct:]": ["\\p{P}", true], "[:space:]": ["\\p{Z}\\t\\r\\n\\v\\f", true], "[:upper:]": ["\\p{Lu}", true], "[:word:]": ["\\p{L}\\p{Nl}\\p{Nd}\\p{Pc}", true], "[:xdigit:]": ["A-Fa-f0-9", false] }; var braceEscape = (s) => s.replace(/[[\]\\-]/g, "\\$&"); var regexpEscape = (s) => s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); var rangesToString = (ranges) => ranges.join(""); var parseClass = (glob, position) => { const pos = position; if (glob.charAt(pos) !== "[") { throw new Error("not in a brace expression"); } const ranges = []; const negs = []; let i = pos + 1; let sawStart = false; let uflag = false; let escaping = false; let negate = false; let endPos = pos; let rangeStart = ""; WHILE: while (i < glob.length) { const c = glob.charAt(i); if ((c === "!" || c === "^") && i === pos + 1) { negate = true; i++; continue; } if (c === "]" && sawStart && !escaping) { endPos = i + 1; break; } sawStart = true; if (c === "\\") { if (!escaping) { escaping = true; i++; continue; } } if (c === "[" && !escaping) { for (const [cls, [unip, u, neg]] of Object.entries(posixClasses)) { if (glob.startsWith(cls, i)) { if (rangeStart) { return ["$.", false, glob.length - pos, true]; } i += cls.length; if (neg) negs.push(unip); else ranges.push(unip); uflag = uflag || u; continue WHILE; } } } escaping = false; if (rangeStart) { if (c > rangeStart) { ranges.push(braceEscape(rangeStart) + "-" + braceEscape(c)); } else if (c === rangeStart) { ranges.push(braceEscape(c)); } rangeStart = ""; i++; continue; } if (glob.startsWith("-]", i + 1)) { ranges.push(braceEscape(c + "-")); i += 2; continue; } if (glob.startsWith("-", i + 1)) { rangeStart = c; i += 2; continue; } ranges.push(braceEscape(c)); i++; } if (endPos < i) { return ["", false, 0, false]; } if (!ranges.length && !negs.length) { return ["$.", false, glob.length - pos, true]; } if (negs.length === 0 && ranges.length === 1 && /^\\?.$/.test(ranges[0]) && !negate) { const r = ranges[0].length === 2 ? ranges[0].slice(-1) : ranges[0]; return [regexpEscape(r), false, endPos - pos, false]; } const sranges = "[" + (negate ? "^" : "") + rangesToString(ranges) + "]"; const snegs = "[" + (negate ? "" : "^") + rangesToString(negs) + "]"; const comb = ranges.length && negs.length ? "(" + sranges + "|" + snegs + ")" : ranges.length ? sranges : snegs; return [comb, uflag, endPos - pos, true]; }; // node_modules/minimatch/dist/esm/unescape.js var unescape = (s, { windowsPathsNoEscape = false, magicalBraces = true } = {}) => { if (magicalBraces) { return windowsPathsNoEscape ? s.replace(/\[([^/\\])\]/g, "$1") : s.replace(/((?!\\).|^)\[([^/\\])\]/g, "$1$2").replace(/\\([^/])/g, "$1"); } return windowsPathsNoEscape ? s.replace(/\[([^/\\{}])\]/g, "$1") : s.replace(/((?!\\).|^)\[([^/\\{}])\]/g, "$1$2").replace(/\\([^/{}])/g, "$1"); }; // node_modules/minimatch/dist/esm/ast.js var _a; var types = /* @__PURE__ */ new Set(["!", "?", "+", "*", "@"]); var isExtglobType = (c) => types.has(c); var isExtglobAST = (c) => isExtglobType(c.type); var adoptionMap = /* @__PURE__ */ new Map([ ["!", ["@"]], ["?", ["?", "@"]], ["@", ["@"]], ["*", ["*", "+", "?", "@"]], ["+", ["+", "@"]] ]); var adoptionWithSpaceMap = /* @__PURE__ */ new Map([ ["!", ["?"]], ["@", ["?"]], ["+", ["?", "*"]] ]); var adoptionAnyMap = /* @__PURE__ */ new Map([ ["!", ["?", "@"]], ["?", ["?", "@"]], ["@", ["?", "@"]], ["*", ["*", "+", "?", "@"]], ["+", ["+", "@", "?", "*"]] ]); var usurpMap = /* @__PURE__ */ new Map([ ["!", /* @__PURE__ */ new Map([["!", "@"]])], [ "?", /* @__PURE__ */ new Map([ ["*", "*"], ["+", "*"] ]) ], [ "@", /* @__PURE__ */ new Map([ ["!", "!"], ["?", "?"], ["@", "@"], ["*", "*"], ["+", "+"] ]) ], [ "+", /* @__PURE__ */ new Map([ ["?", "*"], ["*", "*"] ]) ] ]); var startNoTraversal = "(?!(?:^|/)\\.\\.?(?:$|/))"; var startNoDot = "(?!\\.)"; var addPatternStart = /* @__PURE__ */ new Set(["[", "."]); var justDots = /* @__PURE__ */ new Set(["..", "."]); var reSpecials = new Set("().*{}+?[]^$\\!"); var regExpEscape = (s) => s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); var qmark = "[^/]"; var star = qmark + "*?"; var starNoEmpty = qmark + "+?"; var ID = 0; var AST = class { type; #root; #hasMagic; #uflag = false; #parts = []; #parent; #parentIndex; #negs; #filledNegs = false; #options; #toString; // set to true if it's an extglob with no children // (which really means one child of '') #emptyExt = false; id = ++ID; get depth() { return (this.#parent?.depth ?? -1) + 1; } [/* @__PURE__ */ Symbol.for("nodejs.util.inspect.custom")]() { return { "@@type": "AST", id: this.id, type: this.type, root: this.#root.id, parent: this.#parent?.id, depth: this.depth, partsLength: this.#parts.length, parts: this.#parts }; } constructor(type, parent, options = {}) { this.type = type; if (type) this.#hasMagic = true; this.#parent = parent; this.#root = this.#parent ? this.#parent.#root : this; this.#options = this.#root === this ? options : this.#root.#options; this.#negs = this.#root === this ? [] : this.#root.#negs; if (type === "!" && !this.#root.#filledNegs) this.#negs.push(this); this.#parentIndex = this.#parent ? this.#parent.#parts.length : 0; } get hasMagic() { if (this.#hasMagic !== void 0) return this.#hasMagic; for (const p of this.#parts) { if (typeof p === "string") continue; if (p.type || p.hasMagic) return this.#hasMagic = true; } return this.#hasMagic; } // reconstructs the pattern toString() { return this.#toString !== void 0 ? this.#toString : !this.type ? this.#toString = this.#parts.map((p) => String(p)).join("") : this.#toString = this.type + "(" + this.#parts.map((p) => String(p)).join("|") + ")"; } #fillNegs() { if (this !== this.#root) throw new Error("should only call on root"); if (this.#filledNegs) return this; this.toString(); this.#filledNegs = true; let n; while (n = this.#negs.pop()) { if (n.type !== "!") continue; let p = n; let pp = p.#parent; while (pp) { for (let i = p.#parentIndex + 1; !pp.type && i < pp.#parts.length; i++) { for (const part of n.#parts) { if (typeof part === "string") { throw new Error("string part in extglob AST??"); } part.copyIn(pp.#parts[i]); } } p = pp; pp = p.#parent; } } return this; } push(...parts) { for (const p of parts) { if (p === "") continue; if (typeof p !== "string" && !(p instanceof _a && p.#parent === this)) { throw new Error("invalid part: " + p); } this.#parts.push(p); } } toJSON() { const ret = this.type === null ? this.#parts.slice().map((p) => typeof p === "string" ? p : p.toJSON()) : [this.type, ...this.#parts.map((p) => p.toJSON())]; if (this.isStart() && !this.type) ret.unshift([]); if (this.isEnd() && (this === this.#root || this.#root.#filledNegs && this.#parent?.type === "!")) { ret.push({}); } return ret; } isStart() { if (this.#root === this) return true; if (!this.#parent?.isStart()) return false; if (this.#parentIndex === 0) return true; const p = this.#parent; for (let i = 0; i < this.#parentIndex; i++) { const pp = p.#parts[i]; if (!(pp instanceof _a && pp.type === "!")) { return false; } } return true; } isEnd() { if (this.#root === this) return true; if (this.#parent?.type === "!") return true; if (!this.#parent?.isEnd()) return false; if (!this.type) return this.#parent?.isEnd(); const pl = this.#parent ? this.#parent.#parts.length : 0; return this.#parentIndex === pl - 1; } copyIn(part) { if (typeof part === "string") this.push(part); else this.push(part.clone(this)); } clone(parent) { const c = new _a(this.type, parent); for (const p of this.#parts) { c.copyIn(p); } return c; } static #parseAST(str, ast, pos, opt, extDepth) { const maxDepth = opt.maxExtglobRecursion ?? 2; let escaping = false; let inBrace = false; let braceStart = -1; let braceNeg = false; if (ast.type === null) { let i2 = pos; let acc2 = ""; while (i2 < str.length) { const c = str.charAt(i2++); if (escaping || c === "\\") { escaping = !escaping; acc2 += c; continue; } if (inBrace) { if (i2 === braceStart + 1) { if (c === "^" || c === "!") { braceNeg = true; } } else if (c === "]" && !(i2 === braceStart + 2 && braceNeg)) { inBrace = false; } acc2 += c; continue; } else if (c === "[") { inBrace = true; braceStart = i2; braceNeg = false; acc2 += c; continue; } const doRecurse = !opt.noext && isExtglobType(c) && str.charAt(i2) === "(" && extDepth <= maxDepth; if (doRecurse) { ast.push(acc2); acc2 = ""; const ext2 = new _a(c, ast); i2 = _a.#parseAST(str, ext2, i2, opt, extDepth + 1); ast.push(ext2); continue; } acc2 += c; } ast.push(acc2); return i2; } let i = pos + 1; let part = new _a(null, ast); const parts = []; let acc = ""; while (i < str.length) { const c = str.charAt(i++); if (escaping || c === "\\") { escaping = !escaping; acc += c; continue; } if (inBrace) { if (i === braceStart + 1) { if (c === "^" || c === "!") { braceNeg = true; } } else if (c === "]" && !(i === braceStart + 2 && braceNeg)) { inBrace = false; } acc += c; continue; } else if (c === "[") { inBrace = true; braceStart = i; braceNeg = false; acc += c; continue; } const doRecurse = !opt.noext && isExtglobType(c) && str.charAt(i) === "(" && /* c8 ignore start - the maxDepth is sufficient here */ (extDepth <= maxDepth || ast && ast.#canAdoptType(c)); if (doRecurse) { const depthAdd = ast && ast.#canAdoptType(c) ? 0 : 1; part.push(acc); acc = ""; const ext2 = new _a(c, part); part.push(ext2); i = _a.#parseAST(str, ext2, i, opt, extDepth + depthAdd); continue; } if (c === "|") { part.push(acc); acc = ""; parts.push(part); part = new _a(null, ast); continue; } if (c === ")") { if (acc === "" && ast.#parts.length === 0) { ast.#emptyExt = true; } part.push(acc); acc = ""; ast.push(...parts, part); return i; } acc += c; } ast.type = null; ast.#hasMagic = void 0; ast.#parts = [str.substring(pos - 1)]; return i; } #canAdoptWithSpace(child) { return this.#canAdopt(child, adoptionWithSpaceMap); } #canAdopt(child, map = adoptionMap) { if (!child || typeof child !== "object" || child.type !== null || child.#parts.length !== 1 || this.type === null) { return false; } const gc = child.#parts[0]; if (!gc || typeof gc !== "object" || gc.type === null) { return false; } return this.#canAdoptType(gc.type, map); } #canAdoptType(c, map = adoptionAnyMap) { return !!map.get(this.type)?.includes(c); } #adoptWithSpace(child, index) { const gc = child.#parts[0]; const blank = new _a(null, gc, this.options); blank.#parts.push(""); gc.push(blank); this.#adopt(child, index); } #adopt(child, index) { const gc = child.#parts[0]; this.#parts.splice(index, 1, ...gc.#parts); for (const p of gc.#parts) { if (typeof p === "object") p.#parent = this; } this.#toString = void 0; } #canUsurpType(c) { const m = usurpMap.get(this.type); return !!m?.has(c); } #canUsurp(child) { if (!child || typeof child !== "object" || child.type !== null || child.#parts.length !== 1 || this.type === null || this.#parts.length !== 1) { return false; } const gc = child.#parts[0]; if (!gc || typeof gc !== "object" || gc.type === null) { return false; } return this.#canUsurpType(gc.type); } #usurp(child) { const m = usurpMap.get(this.type); const gc = child.#parts[0]; const nt = m?.get(gc.type); if (!nt) return false; this.#parts = gc.#parts; for (const p of this.#parts) { if (typeof p === "object") { p.#parent = this; } } this.type = nt; this.#toString = void 0; this.#emptyExt = false; } static fromGlob(pattern, options = {}) { const ast = new _a(null, void 0, options); _a.#parseAST(pattern, ast, 0, options, 0); return ast; } // returns the regular expression if there's magic, or the unescaped // string if not. toMMPattern() { if (this !== this.#root) return this.#root.toMMPattern(); const glob = this.toString(); const [re, body, hasMagic, uflag] = this.toRegExpSource(); const anyMagic = hasMagic || this.#hasMagic || this.#options.nocase && !this.#options.nocaseMagicOnly && glob.toUpperCase() !== glob.toLowerCase(); if (!anyMagic) { return body; } const flags = (this.#options.nocase ? "i" : "") + (uflag ? "u" : ""); return Object.assign(new RegExp(`^${re}$`, flags), { _src: re, _glob: glob }); } get options() { return this.#options; } // returns the string match, the regexp source, whether there's magic // in the regexp (so a regular expression is required) and whether or // not the uflag is needed for the regular expression (for posix classes) // TODO: instead of injecting the start/end at this point, just return // the BODY of the regexp, along with the start/end portions suitable // for binding the start/end in either a joined full-path makeRe context // (where we bind to (^|/), or a standalone matchPart context (where // we bind to ^, and not /). Otherwise slashes get duped! // // In part-matching mode, the start is: // - if not isStart: nothing // - if traversal possible, but not allowed: ^(?!\.\.?$) // - if dots allowed or not possible: ^ // - if dots possible and not allowed: ^(?!\.) // end is: // - if not isEnd(): nothing // - else: $ // // In full-path matching mode, we put the slash at the START of the // pattern, so start is: // - if first pattern: same as part-matching mode // - if not isStart(): nothing // - if traversal possible, but not allowed: /(?!\.\.?(?:$|/)) // - if dots allowed or not possible: / // - if dots possible and not allowed: /(?!\.) // end is: // - if last pattern, same as part-matching mode // - else nothing // // Always put the (?:$|/) on negated tails, though, because that has to be // there to bind the end of the negated pattern portion, and it's easier to // just stick it in now rather than try to inject it later in the middle of // the pattern. // // We can just always return the same end, and leave it up to the caller // to know whether it's going to be used joined or in parts. // And, if the start is adjusted slightly, can do the same there: // - if not isStart: nothing // - if traversal possible, but not allowed: (?:/|^)(?!\.\.?$) // - if dots allowed or not possible: (?:/|^) // - if dots possible and not allowed: (?:/|^)(?!\.) // // But it's better to have a simpler binding without a conditional, for // performance, so probably better to return both start options. // // Then the caller just ignores the end if it's not the first pattern, // and the start always gets applied. // // But that's always going to be $ if it's the ending pattern, or nothing, // so the caller can just attach $ at the end of the pattern when building. // // So the todo is: // - better detect what kind of start is needed // - return both flavors of starting pattern // - attach $ at the end of the pattern when creating the actual RegExp // // Ah, but wait, no, that all only applies to the root when the first pattern // is not an extglob. If the first pattern IS an extglob, then we need all // that dot prevention biz to live in the extglob portions, because eg // +(*|.x*) can match .xy but not .yx. // // So, return the two flavors if it's #root and the first child is not an // AST, otherwise leave it to the child AST to handle it, and there, // use the (?:^|/) style of start binding. // // Even simplified further: // - Since the start for a join is eg /(?!\.) and the start for a part // is ^(?!\.), we can just prepend (?!\.) to the pattern (either root // or start or whatever) and prepend ^ or / at the Regexp construction. toRegExpSource(allowDot) { const dot = allowDot ?? !!this.#options.dot; if (this.#root === this) { this.#flatten(); this.#fillNegs(); } if (!isExtglobAST(this)) { const noEmpty = this.isStart() && this.isEnd() && !this.#parts.some((s) => typeof s !== "string"); const src = this.#parts.map((p) => { const [re, _, hasMagic, uflag] = typeof p === "string" ? _a.#parseGlob(p, this.#hasMagic, noEmpty) : p.toRegExpSource(allowDot); this.#hasMagic = this.#hasMagic || hasMagic; this.#uflag = this.#uflag || uflag; return re; }).join(""); let start2 = ""; if (this.isStart()) { if (typeof this.#parts[0] === "string") { const dotTravAllowed = this.#parts.length === 1 && justDots.has(this.#parts[0]); if (!dotTravAllowed) { const aps = addPatternStart; const needNoTrav = ( // dots are allowed, and the pattern starts with [ or . dot && aps.has(src.charAt(0)) || // the pattern starts with \., and then [ or . src.startsWith("\\.") && aps.has(src.charAt(2)) || // the pattern starts with \.\., and then [ or . src.startsWith("\\.\\.") && aps.has(src.charAt(4)) ); const needNoDot = !dot && !allowDot && aps.has(src.charAt(0)); start2 = needNoTrav ? startNoTraversal : needNoDot ? startNoDot : ""; } } } let end = ""; if (this.isEnd() && this.#root.#filledNegs && this.#parent?.type === "!") { end = "(?:$|\\/)"; } const final2 = start2 + src + end; return [ final2, unescape(src), this.#hasMagic = !!this.#hasMagic, this.#uflag ]; } const repeated = this.type === "*" || this.type === "+"; const start = this.type === "!" ? "(?:(?!(?:" : "(?:"; let body = this.#partsToRegExp(dot); if (this.isStart() && this.isEnd() && !body && this.type !== "!") { const s = this.toString(); const me = this; me.#parts = [s]; me.type = null; me.#hasMagic = void 0; return [s, unescape(this.toString()), false, false]; } let bodyDotAllowed = !repeated || allowDot || dot || !startNoDot ? "" : this.#partsToRegExp(true); if (bodyDotAllowed === body) { bodyDotAllowed = ""; } if (bodyDotAllowed) { body = `(?:${body})(?:${bodyDotAllowed})*?`; } let final = ""; if (this.type === "!" && this.#emptyExt) { final = (this.isStart() && !dot ? startNoDot : "") + starNoEmpty; } else { const close = this.type === "!" ? ( // !() must match something,but !(x) can match '' "))" + (this.isStart() && !dot && !allowDot ? startNoDot : "") + star + ")" ) : this.type === "@" ? ")" : this.type === "?" ? ")?" : this.type === "+" && bodyDotAllowed ? ")" : this.type === "*" && bodyDotAllowed ? `)?` : `)${this.type}`; final = start + body + close; } return [ final, unescape(body), this.#hasMagic = !!this.#hasMagic, this.#uflag ]; } #flatten() { if (!isExtglobAST(this)) { for (const p of this.#parts) { if (typeof p === "object") { p.#flatten(); } } } else { let iterations = 0; let done = false; do { done = true; for (let i = 0; i < this.#parts.length; i++) { const c = this.#parts[i]; if (typeof c === "object") { c.#flatten(); if (this.#canAdopt(c)) { done = false; this.#adopt(c, i); } else if (this.#canAdoptWithSpace(c)) { done = false; this.#adoptWithSpace(c, i); } else if (this.#canUsurp(c)) { done = false; this.#usurp(c); } } } } while (!done && ++iterations < 10); } this.#toString = void 0; } #partsToRegExp(dot) { return this.#parts.map((p) => { if (typeof p === "string") { throw new Error("string type in extglob ast??"); } const [re, _, _hasMagic, uflag] = p.toRegExpSource(dot); this.#uflag = this.#uflag || uflag; return re; }).filter((p) => !(this.isStart() && this.isEnd()) || !!p).join("|"); } static #parseGlob(glob, hasMagic, noEmpty = false) { let escaping = false; let re = ""; let uflag = false; let inStar = false; for (let i = 0; i < glob.length; i++) { const c = glob.charAt(i); if (escaping) { escaping = false; re += (reSpecials.has(c) ? "\\" : "") + c; continue; } if (c === "*") { if (inStar) continue; inStar = true; re += noEmpty && /^[*]+$/.test(glob) ? starNoEmpty : star; hasMagic = true; continue; } else { inStar = false; } if (c === "\\") { if (i === glob.length - 1) { re += "\\\\"; } else { escaping = true; } continue; } if (c === "[") { const [src, needUflag, consumed, magic] = parseClass(glob, i); if (consumed) { re += src; uflag = uflag || needUflag; i += consumed - 1; hasMagic = hasMagic || magic; continue; } } if (c === "?") { re += qmark; hasMagic = true; continue; } re += regExpEscape(c); } return [re, unescape(glob), !!hasMagic, uflag]; } }; _a = AST; // node_modules/minimatch/dist/esm/escape.js var escape = (s, { windowsPathsNoEscape = false, magicalBraces = false } = {}) => { if (magicalBraces) { return windowsPathsNoEscape ? s.replace(/[?*()[\]{}]/g, "[$&]") : s.replace(/[?*()[\]\\{}]/g, "\\$&"); } return windowsPathsNoEscape ? s.replace(/[?*()[\]]/g, "[$&]") : s.replace(/[?*()[\]\\]/g, "\\$&"); }; // node_modules/minimatch/dist/esm/index.js var minimatch = (p, pattern, options = {}) => { assertValidPattern(pattern); if (!options.nocomment && pattern.charAt(0) === "#") { return false; } return new Minimatch(pattern, options).match(p); }; var starDotExtRE = /^\*+([^+@!?*[(]*)$/; var starDotExtTest = (ext2) => (f) => !f.startsWith(".") && f.endsWith(ext2); var starDotExtTestDot = (ext2) => (f) => f.endsWith(ext2); var starDotExtTestNocase = (ext2) => { ext2 = ext2.toLowerCase(); return (f) => !f.startsWith(".") && f.toLowerCase().endsWith(ext2); }; var starDotExtTestNocaseDot = (ext2) => { ext2 = ext2.toLowerCase(); return (f) => f.toLowerCase().endsWith(ext2); }; var starDotStarRE = /^\*+\.\*+$/; var starDotStarTest = (f) => !f.startsWith(".") && f.includes("."); var starDotStarTestDot = (f) => f !== "." && f !== ".." && f.includes("."); var dotStarRE = /^\.\*+$/; var dotStarTest = (f) => f !== "." && f !== ".." && f.startsWith("."); var starRE = /^\*+$/; var starTest = (f) => f.length !== 0 && !f.startsWith("."); var starTestDot = (f) => f.length !== 0 && f !== "." && f !== ".."; var qmarksRE = /^\?+([^+@!?*[(]*)?$/; var qmarksTestNocase = ([$0, ext2 = ""]) => { const noext = qmarksTestNoExt([$0]); if (!ext2) return noext; ext2 = ext2.toLowerCase(); return (f) => noext(f) && f.toLowerCase().endsWith(ext2); }; var qmarksTestNocaseDot = ([$0, ext2 = ""]) => { const noext = qmarksTestNoExtDot([$0]); if (!ext2) return noext; ext2 = ext2.toLowerCase(); return (f) => noext(f) && f.toLowerCase().endsWith(ext2); }; var qmarksTestDot = ([$0, ext2 = ""]) => { const noext = qmarksTestNoExtDot([$0]); return !ext2 ? noext : (f) => noext(f) && f.endsWith(ext2); }; var qmarksTest = ([$0, ext2 = ""]) => { const noext = qmarksTestNoExt([$0]); return !ext2 ? noext : (f) => noext(f) && f.endsWith(ext2); }; var qmarksTestNoExt = ([$0]) => { const len = $0.length; return (f) => f.length === len && !f.startsWith("."); }; var qmarksTestNoExtDot = ([$0]) => { const len = $0.length; return (f) => f.length === len && f !== "." && f !== ".."; }; var defaultPlatform = typeof process === "object" && process ? typeof process.env === "object" && process.env && process.env.__MINIMATCH_TESTING_PLATFORM__ || process.platform : "posix"; var path = { win32: { sep: "\\" }, posix: { sep: "/" } }; var sep = defaultPlatform === "win32" ? path.win32.sep : path.posix.sep; minimatch.sep = sep; var GLOBSTAR = /* @__PURE__ */ Symbol("globstar **"); minimatch.GLOBSTAR = GLOBSTAR; var qmark2 = "[^/]"; var star2 = qmark2 + "*?"; var twoStarDot = "(?:(?!(?:\\/|^)(?:\\.{1,2})($|\\/)).)*?"; var twoStarNoDot = "(?:(?!(?:\\/|^)\\.).)*?"; var filter = (pattern, options = {}) => (p) => minimatch(p, pattern, options); minimatch.filter = filter; var ext = (a, b = {}) => Object.assign({}, a, b); var defaults = (def) => { if (!def || typeof def !== "object" || !Object.keys(def).length) { return minimatch; } const orig = minimatch; const m = (p, pattern, options = {}) => orig(p, pattern, ext(def, options)); return Object.assign(m, { Minimatch: class Minimatch extends orig.Minimatch { constructor(pattern, options = {}) { super(pattern, ext(def, options)); } static defaults(options) { return orig.defaults(ext(def, options)).Minimatch; } }, AST: class AST extends orig.AST { /* c8 ignore start */ constructor(type, parent, options = {}) { super(type, parent, ext(def, options)); } /* c8 ignore stop */ static fromGlob(pattern, options = {}) { return orig.AST.fromGlob(pattern, ext(def, options)); } }, unescape: (s, options = {}) => orig.unescape(s, ext(def, options)), escape: (s, options = {}) => orig.escape(s, ext(def, options)), filter: (pattern, options = {}) => orig.filter(pattern, ext(def, options)), defaults: (options) => orig.defaults(ext(def, options)), makeRe: (pattern, options = {}) => orig.makeRe(pattern, ext(def, options)), braceExpand: (pattern, options = {}) => orig.braceExpand(pattern, ext(def, options)), match: (list, pattern, options = {}) => orig.match(list, pattern, ext(def, options)), sep: orig.sep, GLOBSTAR }); }; minimatch.defaults = defaults; var braceExpand = (pattern, options = {}) => { assertValidPattern(pattern); if (options.nobrace || !/\{(?:(?!\{).)*\}/.test(pattern)) { return [pattern]; } return expand(pattern, { max: options.braceExpandMax }); }; minimatch.braceExpand = braceExpand; var makeRe = (pattern, options = {}) => new Minimatch(pattern, options).makeRe(); minimatch.makeRe = makeRe; var match = (list, pattern, options = {}) => { const mm = new Minimatch(pattern, options); list = list.filter((f) => mm.match(f)); if (mm.options.nonull && !list.length) { list.push(pattern); } return list; }; minimatch.match = match; var globMagic = /[?*]|[+@!]\(.*?\)|\[|\]/; var regExpEscape2 = (s) => s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); var Minimatch = class { options; set; pattern; windowsPathsNoEscape; nonegate; negate; comment; empty; preserveMultipleSlashes; partial; globSet; globParts; nocase; isWindows; platform; windowsNoMagicRoot; maxGlobstarRecursion; regexp; constructor(pattern, options = {}) { assertValidPattern(pattern); options = options || {}; this.options = options; this.maxGlobstarRecursion = options.maxGlobstarRecursion ?? 200; this.pattern = pattern; this.platform = options.platform || defaultPlatform; this.isWindows = this.platform === "win32"; const awe = "allowWindowsEscape"; this.windowsPathsNoEscape = !!options.windowsPathsNoEscape || options[awe] === false; if (this.windowsPathsNoEscape) { this.pattern = this.pattern.replace(/\\/g, "/"); } this.preserveMultipleSlashes = !!options.preserveMultipleSlashes; this.regexp = null; this.negate = false; this.nonegate = !!options.nonegate; this.comment = false; this.empty = false; this.partial = !!options.partial; this.nocase = !!this.options.nocase; this.windowsNoMagicRoot = options.windowsNoMagicRoot !== void 0 ? options.windowsNoMagicRoot : !!(this.isWindows && this.nocase); this.globSet = []; this.globParts = []; this.set = []; this.make(); } hasMagic() { if (this.options.magicalBraces && this.set.length > 1) { return true; } for (const pattern of this.set) { for (const part of pattern) { if (typeof part !== "string") return true; } } return false; } debug(..._) { } make() { const pattern = this.pattern; const options = this.options; if (!options.nocomment && pattern.charAt(0) === "#") { this.comment = true; return; } if (!pattern) { this.empty = true; return; } this.parseNegate(); this.globSet = [...new Set(this.braceExpand())]; if (options.debug) { this.debug = (...args) => console.error(...args); } this.debug(this.pattern, this.globSet); const rawGlobParts = this.globSet.map((s) => this.slashSplit(s)); this.globParts = this.preprocess(rawGlobParts); this.debug(this.pattern, this.globParts); let set = this.globParts.map((s, _, __) => { if (this.isWindows && this.windowsNoMagicRoot) { const isUNC = s[0] === "" && s[1] === "" && (s[2] === "?" || !globMagic.test(s[2])) && !globMagic.test(s[3]); const isDrive = /^[a-z]:/i.test(s[0]); if (isUNC) { return [ ...s.slice(0, 4), ...s.slice(4).map((ss) => this.parse(ss)) ]; } else if (isDrive) { return [s[0], ...s.slice(1).map((ss) => this.parse(ss))]; } } return s.map((ss) => this.parse(ss)); }); this.debug(this.pattern, set); this.set = set.filter((s) => s.indexOf(false) === -1); if (this.isWindows) { for (let i = 0; i < this.set.length; i++) { const p = this.set[i]; if (p[0] === "" && p[1] === "" && this.globParts[i][2] === "?" && typeof p[3] === "string" && /^[a-z]:$/i.test(p[3])) { p[2] = "?"; } } } this.debug(this.pattern, this.set); } // various transforms to equivalent pattern sets that are // faster to process in a filesystem walk. The goal is to // eliminate what we can, and push all ** patterns as far // to the right as possible, even if it increases the number // of patterns that we have to process. preprocess(globParts) { if (this.options.noglobstar) { for (const partset of globParts) { for (let j = 0; j < partset.length; j++) { if (partset[j] === "**") { partset[j] = "*"; } } } } const { optimizationLevel = 1 } = this.options; if (optimizationLevel >= 2) { globParts = this.firstPhasePreProcess(globParts); globParts = this.secondPhasePreProcess(globParts); } else if (optimizationLevel >= 1) { globParts = this.levelOneOptimize(globParts); } else { globParts = this.adjascentGlobstarOptimize(globParts); } return globParts; } // just get rid of adjascent ** portions adjascentGlobstarOptimize(globParts) { return globParts.map((parts) => { let gs = -1; while (-1 !== (gs = parts.indexOf("**", gs + 1))) { let i = gs; while (parts[i + 1] === "**") { i++; } if (i !== gs) { parts.splice(gs, i - gs); } } return parts; }); } // get rid of adjascent ** and resolve .. portions levelOneOptimize(globParts) { return globParts.map((parts) => { parts = parts.reduce((set, part) => { const prev = set[set.length - 1]; if (part === "**" && prev === "**") { return set; } if (part === "..") { if (prev && prev !== ".." && prev !== "." && prev !== "**") { set.pop(); return set; } } set.push(part); return set; }, []); return parts.length === 0 ? [""] : parts; }); } levelTwoFileOptimize(parts) { if (!Array.isArray(parts)) { parts = this.slashSplit(parts); } let didSomething = false; do { didSomething = false; if (!this.preserveMultipleSlashes) { for (let i = 1; i < parts.length - 1; i++) { const p = parts[i]; if (i === 1 && p === "" && parts[0] === "") continue; if (p === "." || p === "") { didSomething = true; parts.splice(i, 1); i--; } } if (parts[0] === "." && parts.length === 2 && (parts[1] === "." || parts[1] === "")) { didSomething = true; parts.pop(); } } let dd = 0; while (-1 !== (dd = parts.indexOf("..", dd + 1))) { const p = parts[dd - 1]; if (p && p !== "." && p !== ".." && p !== "**" && !(this.isWindows && /^[a-z]:$/i.test(p))) { didSomething = true; parts.splice(dd - 1, 2); dd -= 2; } } } while (didSomething); return parts.length === 0 ? [""] : parts; } // First phase: single-pattern processing //
is 1 or more portions //is 1 or more portions // is any portion other than ., .., '', or ** //
is . or '' // // **/.. is *brutal* for filesystem walking performance, because // it effectively resets the recursive walk each time it occurs, // and ** cannot be reduced out by a .. pattern part like a regexp // or most strings (other than .., ., and '') can be. // // /**/..//
/
-> { /..//
/
, /**//
/
} // // -> /// //../
-> /// **/**/ -> **/ // // **/*/ -> */**/ <== not valid because ** doesn't follow // this WOULD be allowed if ** did follow symlinks, or * didn't firstPhasePreProcess(globParts) { let didSomething = false; do { didSomething = false; for (let parts of globParts) { let gs = -1; while (-1 !== (gs = parts.indexOf("**", gs + 1))) { let gss = gs; while (parts[gss + 1] === "**") { gss++; } if (gss > gs) { parts.splice(gs + 1, gss - gs); } let next = parts[gs + 1]; const p = parts[gs + 2]; const p2 = parts[gs + 3]; if (next !== "..") continue; if (!p || p === "." || p === ".." || !p2 || p2 === "." || p2 === "..") { continue; } didSomething = true; parts.splice(gs, 1); const other = parts.slice(0); other[gs] = "**"; globParts.push(other); gs--; } if (!this.preserveMultipleSlashes) { for (let i = 1; i < parts.length - 1; i++) { const p = parts[i]; if (i === 1 && p === "" && parts[0] === "") continue; if (p === "." || p === "") { didSomething = true; parts.splice(i, 1); i--; } } if (parts[0] === "." && parts.length === 2 && (parts[1] === "." || parts[1] === "")) { didSomething = true; parts.pop(); } } let dd = 0; while (-1 !== (dd = parts.indexOf("..", dd + 1))) { const p = parts[dd - 1]; if (p && p !== "." && p !== ".." && p !== "**") { didSomething = true; const needDot = dd === 1 && parts[dd + 1] === "**"; const splin = needDot ? ["."] : []; parts.splice(dd - 1, 2, ...splin); if (parts.length === 0) parts.push(""); dd -= 2; } } } } while (didSomething); return globParts; } // second phase: multi-pattern dedupes // { /*/, //
} -> /*/// { /, /} -> /// { /**/, /} -> /**/// // { /**/, /**//
} -> /**/// ^-- not valid because ** doens't follow symlinks secondPhasePreProcess(globParts) { for (let i = 0; i < globParts.length - 1; i++) { for (let j = i + 1; j < globParts.length; j++) { const matched = this.partsMatch(globParts[i], globParts[j], !this.preserveMultipleSlashes); if (matched) { globParts[i] = []; globParts[j] = matched; break; } } } return globParts.filter((gs) => gs.length); } partsMatch(a, b, emptyGSMatch = false) { let ai = 0; let bi = 0; let result = []; let which = ""; while (ai < a.length && bi < b.length) { if (a[ai] === b[bi]) { result.push(which === "b" ? b[bi] : a[ai]); ai++; bi++; } else if (emptyGSMatch && a[ai] === "**" && b[bi] === a[ai + 1]) { result.push(a[ai]); ai++; } else if (emptyGSMatch && b[bi] === "**" && a[ai] === b[bi + 1]) { result.push(b[bi]); bi++; } else if (a[ai] === "*" && b[bi] && (this.options.dot || !b[bi].startsWith(".")) && b[bi] !== "**") { if (which === "b") return false; which = "a"; result.push(a[ai]); ai++; bi++; } else if (b[bi] === "*" && a[ai] && (this.options.dot || !a[ai].startsWith(".")) && a[ai] !== "**") { if (which === "a") return false; which = "b"; result.push(b[bi]); ai++; bi++; } else { return false; } } return a.length === b.length && result; } parseNegate() { if (this.nonegate) return; const pattern = this.pattern; let negate = false; let negateOffset = 0; for (let i = 0; i < pattern.length && pattern.charAt(i) === "!"; i++) { negate = !negate; negateOffset++; } if (negateOffset) this.pattern = pattern.slice(negateOffset); this.negate = negate; } // set partial to true to test if, for example, // "/a/b" matches the start of "/*/b/*/d" // Partial means, if you run out of file before you run // out of pattern, then that's fine, as long as all // the parts match. matchOne(file, pattern, partial = false) { let fileStartIndex = 0; let patternStartIndex = 0; if (this.isWindows) { const fileDrive = typeof file[0] === "string" && /^[a-z]:$/i.test(file[0]); const fileUNC = !fileDrive && file[0] === "" && file[1] === "" && file[2] === "?" && /^[a-z]:$/i.test(file[3]); const patternDrive = typeof pattern[0] === "string" && /^[a-z]:$/i.test(pattern[0]); const patternUNC = !patternDrive && pattern[0] === "" && pattern[1] === "" && pattern[2] === "?" && typeof pattern[3] === "string" && /^[a-z]:$/i.test(pattern[3]); const fdi = fileUNC ? 3 : fileDrive ? 0 : void 0; const pdi = patternUNC ? 3 : patternDrive ? 0 : void 0; if (typeof fdi === "number" && typeof pdi === "number") { const [fd, pd] = [ file[fdi], pattern[pdi] ]; if (fd.toLowerCase() === pd.toLowerCase()) { pattern[pdi] = fd; patternStartIndex = pdi; fileStartIndex = fdi; } } } const { optimizationLevel = 1 } = this.options; if (optimizationLevel >= 2) { file = this.levelTwoFileOptimize(file); } if (pattern.includes(GLOBSTAR)) { return this.#matchGlobstar(file, pattern, partial, fileStartIndex, patternStartIndex); } return this.#matchOne(file, pattern, partial, fileStartIndex, patternStartIndex); } #matchGlobstar(file, pattern, partial, fileIndex, patternIndex) { const firstgs = pattern.indexOf(GLOBSTAR, patternIndex); const lastgs = pattern.lastIndexOf(GLOBSTAR); const [head, body, tail] = partial ? [ pattern.slice(patternIndex, firstgs), pattern.slice(firstgs + 1), [] ] : [ pattern.slice(patternIndex, firstgs), pattern.slice(firstgs + 1, lastgs), pattern.slice(lastgs + 1) ]; if (head.length) { const fileHead = file.slice(fileIndex, fileIndex + head.length); if (!this.#matchOne(fileHead, head, partial, 0, 0)) { return false; } fileIndex += head.length; patternIndex += head.length; } let fileTailMatch = 0; if (tail.length) { if (tail.length + fileIndex > file.length) return false; let tailStart = file.length - tail.length; if (this.#matchOne(file, tail, partial, tailStart, 0)) { fileTailMatch = tail.length; } else { if (file[file.length - 1] !== "" || fileIndex + tail.length === file.length) { return false; } tailStart--; if (!this.#matchOne(file, tail, partial, tailStart, 0)) { return false; } fileTailMatch = tail.length + 1; } } if (!body.length) { let sawSome = !!fileTailMatch; for (let i2 = fileIndex; i2 < file.length - fileTailMatch; i2++) { const f = String(file[i2]); sawSome = true; if (f === "." || f === ".." || !this.options.dot && f.startsWith(".")) { return false; } } return partial || sawSome; } const bodySegments = [[[], 0]]; let currentBody = bodySegments[0]; let nonGsParts = 0; const nonGsPartsSums = [0]; for (const b of body) { if (b === GLOBSTAR) { nonGsPartsSums.push(nonGsParts); currentBody = [[], 0]; bodySegments.push(currentBody); } else { currentBody[0].push(b); nonGsParts++; } } let i = bodySegments.length - 1; const fileLength = file.length - fileTailMatch; for (const b of bodySegments) { b[1] = fileLength - (nonGsPartsSums[i--] + b[0].length); } return !!this.#matchGlobStarBodySections(file, bodySegments, fileIndex, 0, partial, 0, !!fileTailMatch); } // return false for "nope, not matching" // return null for "not matching, cannot keep trying" #matchGlobStarBodySections(file, bodySegments, fileIndex, bodyIndex, partial, globStarDepth, sawTail) { const bs = bodySegments[bodyIndex]; if (!bs) { for (let i = fileIndex; i < file.length; i++) { sawTail = true; const f = file[i]; if (f === "." || f === ".." || !this.options.dot && f.startsWith(".")) { return false; } } return sawTail; } const [body, after] = bs; while (fileIndex <= after) { const m = this.#matchOne(file.slice(0, fileIndex + body.length), body, partial, fileIndex, 0); if (m && globStarDepth < this.maxGlobstarRecursion) { const sub = this.#matchGlobStarBodySections(file, bodySegments, fileIndex + body.length, bodyIndex + 1, partial, globStarDepth + 1, sawTail); if (sub !== false) { return sub; } } const f = file[fileIndex]; if (f === "." || f === ".." || !this.options.dot && f.startsWith(".")) { return false; } fileIndex++; } return partial || null; } #matchOne(file, pattern, partial, fileIndex, patternIndex) { let fi; let pi; let pl; let fl; for (fi = fileIndex, pi = patternIndex, fl = file.length, pl = pattern.length; fi < fl && pi < pl; fi++, pi++) { this.debug("matchOne loop"); let p = pattern[pi]; let f = file[fi]; this.debug(pattern, p, f); if (p === false || p === GLOBSTAR) { return false; } let hit; if (typeof p === "string") { hit = f === p; this.debug("string match", p, f, hit); } else { hit = p.test(f); this.debug("pattern match", p, f, hit); } if (!hit) return false; } if (fi === fl && pi === pl) { return true; } else if (fi === fl) { return partial; } else if (pi === pl) { return fi === fl - 1 && file[fi] === ""; } else { throw new Error("wtf?"); } } braceExpand() { return braceExpand(this.pattern, this.options); } parse(pattern) { assertValidPattern(pattern); const options = this.options; if (pattern === "**") return GLOBSTAR; if (pattern === "") return ""; let m; let fastTest = null; if (m = pattern.match(starRE)) { fastTest = options.dot ? starTestDot : starTest; } else if (m = pattern.match(starDotExtRE)) { fastTest = (options.nocase ? options.dot ? starDotExtTestNocaseDot : starDotExtTestNocase : options.dot ? starDotExtTestDot : starDotExtTest)(m[1]); } else if (m = pattern.match(qmarksRE)) { fastTest = (options.nocase ? options.dot ? qmarksTestNocaseDot : qmarksTestNocase : options.dot ? qmarksTestDot : qmarksTest)(m); } else if (m = pattern.match(starDotStarRE)) { fastTest = options.dot ? starDotStarTestDot : starDotStarTest; } else if (m = pattern.match(dotStarRE)) { fastTest = dotStarTest; } const re = AST.fromGlob(pattern, this.options).toMMPattern(); if (fastTest && typeof re === "object") { Reflect.defineProperty(re, "test", { value: fastTest }); } return re; } makeRe() { if (this.regexp || this.regexp === false) return this.regexp; const set = this.set; if (!set.length) { this.regexp = false; return this.regexp; } const options = this.options; const twoStar = options.noglobstar ? star2 : options.dot ? twoStarDot : twoStarNoDot; const flags = new Set(options.nocase ? ["i"] : []); let re = set.map((pattern) => { const pp = pattern.map((p) => { if (p instanceof RegExp) { for (const f of p.flags.split("")) flags.add(f); } return typeof p === "string" ? regExpEscape2(p) : p === GLOBSTAR ? GLOBSTAR : p._src; }); pp.forEach((p, i) => { const next = pp[i + 1]; const prev = pp[i - 1]; if (p !== GLOBSTAR || prev === GLOBSTAR) { return; } if (prev === void 0) { if (next !== void 0 && next !== GLOBSTAR) { pp[i + 1] = "(?:\\/|" + twoStar + "\\/)?" + next; } else { pp[i] = twoStar; } } else if (next === void 0) { pp[i - 1] = prev + "(?:\\/|\\/" + twoStar + ")?"; } else if (next !== GLOBSTAR) { pp[i - 1] = prev + "(?:\\/|\\/" + twoStar + "\\/)" + next; pp[i + 1] = GLOBSTAR; } }); const filtered = pp.filter((p) => p !== GLOBSTAR); if (this.partial && filtered.length >= 1) { const prefixes = []; for (let i = 1; i <= filtered.length; i++) { prefixes.push(filtered.slice(0, i).join("/")); } return "(?:" + prefixes.join("|") + ")"; } return filtered.join("/"); }).join("|"); const [open, close] = set.length > 1 ? ["(?:", ")"] : ["", ""]; re = "^" + open + re + close + "$"; if (this.partial) { re = "^(?:\\/|" + open + re.slice(1, -1) + close + ")$"; } if (this.negate) re = "^(?!" + re + ").+$"; try { this.regexp = new RegExp(re, [...flags].join("")); } catch { this.regexp = false; } return this.regexp; } slashSplit(p) { if (this.preserveMultipleSlashes) { return p.split("/"); } else if (this.isWindows && /^\/\/[^/]+/.test(p)) { return ["", ...p.split(/\/+/)]; } else { return p.split(/\/+/); } } match(f, partial = this.partial) { this.debug("match", f, this.pattern); if (this.comment) { return false; } if (this.empty) { return f === ""; } if (f === "/" && partial) { return true; } const options = this.options; if (this.isWindows) { f = f.split("\\").join("/"); } const ff = this.slashSplit(f); this.debug(this.pattern, "split", ff); const set = this.set; this.debug(this.pattern, "set", set); let filename = ff[ff.length - 1]; if (!filename) { for (let i = ff.length - 2; !filename && i >= 0; i--) { filename = ff[i]; } } for (const pattern of set) { let file = ff; if (options.matchBase && pattern.length === 1) { file = [filename]; } const hit = this.matchOne(file, pattern, partial); if (hit) { if (options.flipNegate) { return true; } return !this.negate; } } if (options.flipNegate) { return false; } return this.negate; } static defaults(def) { return minimatch.defaults(def).Minimatch; } }; minimatch.AST = AST; minimatch.Minimatch = Minimatch; minimatch.escape = escape; minimatch.unescape = unescape; // src/core/matcher.ts var knownFileTypes = { archive: "*.{zip,tar.gz,tgz}", package: "*.{deb,pkg,rpm}", linux: "*.{deb,rpm}", macos: "*.pkg", targz: "*.{tgz,tar.gz}" }; function filterByRegex(assets, pattern) { const regex = new RegExp(pattern, "i"); return assets.filter((asset) => regex.test(asset.name)); } function replacePlatformPlaceholders(pattern, platform2) { return pattern.replace(/{{SYSTEM}}/g, platform2.systemPattern).replace(/{{ARCH}}/g, platform2.archPattern); } function getMatchingAsset(assets, platform2, fileName, fileType) { if (fileName && !fileName.startsWith("~")) { const exactMatches = assets.filter((asset) => asset.name === fileName); if (exactMatches.length !== 1) { throw new Error(`Expected exactly one asset to match the provided filename, matched: ${exactMatches.length}`); } return exactMatches[0]; } let fileTypeFilteredAssets = assets; if (fileType) { if (Object.hasOwn(knownFileTypes, fileType)) { const fileTypeGlob = knownFileTypes[fileType]; fileTypeFilteredAssets = assets.filter((asset) => minimatch(asset.name, fileTypeGlob, { nocase: true })); } else if (fileType.startsWith("~")) { const fileTypeRegex = `${fileType.substring(1)}$`; fileTypeFilteredAssets = filterByRegex(assets, fileTypeRegex); } else { const extension = fileType.replace(/^\./, ""); const fileTypeGlob = `*.${extension}`; fileTypeFilteredAssets = assets.filter((asset) => minimatch(asset.name, fileTypeGlob, { nocase: true })); } } if (fileName && fileName.startsWith("~")) { const fileNamePattern = replacePlatformPlaceholders(fileName.substring(1), platform2); const fileNameFilteredAssets = filterByRegex(fileTypeFilteredAssets, fileNamePattern); if (fileNameFilteredAssets.length !== 1) { throw new Error(`Expected exactly one asset to match the filename regex, matched: ${fileNameFilteredAssets.length}`); } return fileNameFilteredAssets[0]; } const defaultPattern = replacePlatformPlaceholders("{{SYSTEM}}[_-]{{ARCH}}", platform2); const defaultFilteredAssets = filterByRegex(fileTypeFilteredAssets, defaultPattern); if (defaultFilteredAssets.length !== 1) { const errorMessage = defaultFilteredAssets.length === 0 ? `No assets matched the default criteria: ${defaultPattern}` : `Multiple assets matched the default criteria: ${defaultFilteredAssets.map((asset) => asset.name).join(", ")}`; throw new Error(errorMessage); } return defaultFilteredAssets[0]; } // src/core/finder.ts var fs = __toESM(require("fs")); var path2 = __toESM(require("path")); function findBinary(dir, pattern, debug, logger) { const items = fs.readdirSync(dir); if (debug) { logger(`Searching for binary in ${dir}...`); items.forEach((item) => logger(` - ${item}`)); } for (const item of items) { const fullPath = path2.join(dir, item); const stat = fs.statSync(fullPath); if (stat.isDirectory()) { const found = findBinary(fullPath, pattern, debug, logger); if (found) return found; } else { let isMatch = false; if (pattern instanceof RegExp) { isMatch = pattern.test(item); } else { isMatch = item === pattern; if (!isMatch && process.platform === "win32" && !pattern.toLowerCase().endsWith(".exe")) { isMatch = item.toLowerCase() === `${pattern.toLowerCase()}.exe`; } } if (isMatch) return fullPath; } } return void 0; } // src/core/downloader.ts function getGithubApiHeaders(token) { const headers = { "Accept": "application/vnd.github.v3+json", "User-Agent": "setup-github-release-action" }; if (token) { headers["Authorization"] = `token ${token}`; } return headers; } async function fetchLatestRelease(repository, token) { const url = `https://api.github.com/repos/${repository}/releases/latest`; const headers = getGithubApiHeaders(token); const response = await fetch(url, { headers }); if (!response.ok) { const errorBody = await response.text(); throw new Error(`Failed to fetch latest release for ${repository}: ${response.statusText}. ${errorBody}`); } return await response.json(); } async function fetchLatestReleaseRaw(repository, token) { const url = `https://api.github.com/repos/${repository}/releases/latest`; const headers = getGithubApiHeaders(token); const response = await fetch(url, { headers }); const body = await response.text(); if (!response.ok) { throw new Error(`Failed to fetch latest release for ${repository}: ${response.statusText}. ${body}`); } return body; } async function downloadAsset(url, destPath, token) { const headers = { "User-Agent": "setup-github-release-action" }; if (token) { headers["Authorization"] = `token ${token}`; } const response = await fetch(url, { headers }); if (!response.ok) { throw new Error(`Failed to download asset: ${response.statusText}`); } const fs4 = await import("fs"); const { Readable } = await import("stream"); const { finished } = await import("stream/promises"); const fileStream = fs4.createWriteStream(destPath); await finished(Readable.fromWeb(response.body).pipe(fileStream)); } // src/core/extractor.ts var import_child_process = require("child_process"); var path3 = __toESM(require("path")); var fs2 = __toESM(require("fs")); async function extractAsset(filePath, destDir) { const ext2 = path3.extname(filePath).toLowerCase(); const name = path3.basename(filePath).toLowerCase(); if (!fs2.existsSync(destDir)) { fs2.mkdirSync(destDir, { recursive: true }); } if (name.endsWith(".tar.gz") || name.endsWith(".tgz") || name.endsWith(".tar")) { const args = ["-xf", filePath, "-C", destDir]; const result = (0, import_child_process.spawnSync)("tar", args); if (result.status !== 0) { throw new Error(`tar failed with status ${result.status}: ${result.stderr.toString()}`); } } else if (name.endsWith(".zip")) { if (process.platform === "win32") { const tarResult = (0, import_child_process.spawnSync)("tar", ["-xf", filePath, "-C", destDir]); if (tarResult.status === 0) return; const escapedFilePath = filePath.replace(/'/g, "''"); const escapedDestDir = destDir.replace(/'/g, "''"); const dotNetCommand = `Add-Type -AssemblyName System.IO.Compression.FileSystem; [System.IO.Compression.ZipFile]::ExtractToDirectory('${escapedFilePath}', '${escapedDestDir}')`; for (const shell of ["pwsh", "powershell"]) { const result = (0, import_child_process.spawnSync)(shell, ["-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", dotNetCommand]); if (result.status === 0) return; } throw new Error(`Extraction failed: Both tar and PowerShell fallback failed. Make sure your system can extract ZIP files.`); } else { const result = (0, import_child_process.spawnSync)("unzip", ["-q", filePath, "-d", destDir]); if (result.status !== 0) { throw new Error(`unzip failed with status ${result.status}: ${result.stderr.toString()}`); } } } else if (name.endsWith(".7z")) { const result = (0, import_child_process.spawnSync)("7z", ["x", filePath, `-o${destDir}`, "-y"]); if (result.status !== 0) { throw new Error(`7z failed with status ${result.status}. Make sure 7z is installed.`); } } else { const destPath = path3.join(destDir, path3.basename(filePath)); fs2.copyFileSync(filePath, destPath); } } // src/cli.ts function usage() { return ` Usage: install-github-release [options] Arguments: repository The GitHub repository (owner/repo) Options: --dry-run [level] Run in test mode (default level: 1) -l, --list [repository] List available assets from latest release and exit -a, --app-name Application name (optional, for output messages) -f, --file-name Asset file name or regex pattern (prefixed with ~) -b, --binary-name Binary name (supports source:destination form) -t, --file-type Known: archive|package|linux|macos|targz Or custom: ~ (end-of-string match) or extension (e.g. zip, .tar.gz) -p, --install-path Custom installation directory -o, --output-directory Only download selected asset to the specified directory -j, --releases-json Download latest release JSON only --system Override detected system for asset matching --arch Override detected architecture for asset matching -k, --token GitHub token -d, --debug Enable debug logging -h, --help Show this help message `; } function ensureOptionValue(argv, index, option) { const value = argv[index + 1]; if (!value || value.startsWith("-")) { throw new Error(`Missing value for ${option}.`); } return value; } function parseCliArgs(argv) { const envDryRun = process.env.TEST_MODE; const dryRunLevelFromEnv = envDryRun && /^\d+$/.test(envDryRun) ? parseInt(envDryRun, 10) : 0; const opts = { releasesJsonOnly: false, listOnly: false, debug: false, help: false, dryRunLevel: dryRunLevelFromEnv, positionals: [] }; for (let i = 0; i < argv.length; i++) { const arg = argv[i]; switch (arg) { case "-h": case "--help": opts.help = true; break; case "--dry-run": { const next = argv[i + 1]; if (next && /^\d+$/.test(next)) { opts.dryRunLevel = parseInt(next, 10); i++; } else { opts.dryRunLevel = 1; } break; } case "-l": case "--list": { opts.listOnly = true; const next = argv[i + 1]; if (next && !next.startsWith("-")) { opts.listRepo = next; i++; } break; } case "-a": case "--app-name": opts.appName = ensureOptionValue(argv, i, arg); i++; break; case "-f": case "--file-name": opts.fileName = ensureOptionValue(argv, i, arg); i++; break; case "-b": case "--binary-name": opts.binaryName = ensureOptionValue(argv, i, arg); i++; break; case "-t": case "--file-type": { const fileType = ensureOptionValue(argv, i, arg); if (!fileType.trim()) { throw new Error(`Unknown asset type: ${fileType}`); } opts.fileType = fileType; i++; break; } case "-p": case "--install-path": opts.installPath = ensureOptionValue(argv, i, arg); i++; break; case "-o": case "--output-directory": opts.outputDirectory = ensureOptionValue(argv, i, arg); i++; break; case "-j": case "--releases-json": opts.releasesJsonOnly = true; break; case "--system": opts.systemOverride = ensureOptionValue(argv, i, arg); i++; break; case "--arch": opts.archOverride = ensureOptionValue(argv, i, arg); i++; break; case "-k": case "--token": opts.token = ensureOptionValue(argv, i, arg); i++; break; case "-d": case "--debug": opts.debug = true; break; default: if (arg.startsWith("-")) { throw new Error(`Unknown option: ${arg}`); } opts.positionals.push(arg); break; } } return opts; } function validateOutputDirectory(outputDirectory) { const resolvedPath = path4.resolve(outputDirectory); if (!fs3.existsSync(resolvedPath) || !fs3.statSync(resolvedPath).isDirectory()) { throw new Error(`Output directory "${resolvedPath}" does not exist.`); } return resolvedPath; } function getInstallDir(installPath) { if (installPath) { return path4.resolve(installPath); } if (process.platform === "win32") { const localAppData = process.env.LOCALAPPDATA || path4.join(os2.homedir(), "AppData", "Local"); return path4.join(localAppData, "bin"); } const isRoot = process.getuid && process.getuid() === 0; if (isRoot) { return "/usr/local/bin"; } const homeBin = path4.join(os2.homedir(), "bin"); if (fs3.existsSync(homeBin)) { return homeBin; } return "/usr/local/bin"; } function installSystemPackage(downloadPath) { const fileName = path4.basename(downloadPath).toLowerCase(); const command = fileName.endsWith(".deb") ? { binary: "dpkg", args: ["-i", downloadPath] } : fileName.endsWith(".pkg") ? { binary: "installer", args: ["-pkg", downloadPath, "-target", "/"] } : fileName.endsWith(".rpm") ? { binary: "rpm", args: ["-i", downloadPath] } : void 0; if (!command) { throw new Error(`Unsupported package type: ${fileName}`); } const isRoot = process.getuid && process.getuid() === 0; const commandToRun = isRoot ? command.binary : "sudo"; const argsToRun = isRoot ? command.args : [command.binary, ...command.args]; const result = (0, import_child_process2.spawnSync)(commandToRun, argsToRun, { stdio: "inherit" }); if (result.status !== 0) { throw new Error(`Failed to install package using ${commandToRun} ${argsToRun.join(" ")}.`); } } async function run() { let tempDir; try { const options = parseCliArgs(process.argv.slice(2)); const repository = options.listRepo || options.positionals[0]; if (options.help || !repository) { console.log(usage()); process.exit(options.help ? 0 : 1); } const token = options.token || process.env.GITHUB_TOKEN; if (options.listOnly) { const release2 = await fetchLatestRelease(repository, token); release2.assets.forEach((asset2) => console.log(`- ${asset2.browser_download_url}`)); process.exit(0); } const toolName = repository.split("/").pop() || repository; const appName = options.appName || toolName.charAt(0).toUpperCase() + toolName.slice(1); const binaryOption = options.binaryName || toolName; const [binarySource, binaryDestination] = binaryOption.includes(":") ? [binaryOption.split(":")[0], binaryOption.split(":")[1]] : [binaryOption, binaryOption]; if (options.releasesJsonOnly) { const rawRelease = await fetchLatestReleaseRaw(repository, token); const outputBase = binaryDestination || toolName; const outputName = `${outputBase}.releases.json`; const outputPath = options.outputDirectory ? path4.join(validateOutputDirectory(options.outputDirectory), outputName) : outputName; fs3.writeFileSync(outputPath, rawRelease, "utf8"); console.log(`Downloaded GitHub releases to ${outputPath}.`); process.exit(0); } const platformInfo = getPlatformInfo({ system: options.systemOverride, arch: options.archOverride }); console.log(`Fetching latest release for ${repository}...`); const release = await fetchLatestRelease(repository, token); const asset = getMatchingAsset(release.assets, platformInfo, options.fileName, options.fileType); const version = release.tag_name.replace(/^v/i, ""); const downloadUrl = asset.browser_download_url; console.log(`Will download '${appName}' version: ${version}`); console.log(`Download URL: "${downloadUrl}".`); if (options.dryRunLevel > 0) { process.exit(0); } if (options.outputDirectory) { const outputDir = validateOutputDirectory(options.outputDirectory); const outputPath = path4.join(outputDir, path4.basename(downloadUrl)); console.log(`Downloading '${appName}' version ${version} to '${outputPath}'...`); await downloadAsset(downloadUrl, outputPath, token); process.exit(0); } tempDir = fs3.mkdtempSync(path4.join(os2.tmpdir(), "setup-gh-release-")); const downloadPath = path4.join(tempDir, asset.name); await downloadAsset(downloadUrl, downloadPath, token); if (/\.(deb|pkg|rpm)$/i.test(asset.name)) { installSystemPackage(downloadPath); console.log("Installation successful!"); process.exit(0); } const extractDir = path4.join(tempDir, "extract"); console.log(`Extracting ${asset.name}...`); await extractAsset(downloadPath, extractDir); let binaryPattern; if (binarySource.startsWith("~")) { binaryPattern = new RegExp(binarySource.substring(1), "i"); } else { binaryPattern = binarySource; } const binaryPath = findBinary(extractDir, binaryPattern, options.debug, console.log); if (!binaryPath) { throw new Error(`Could not find binary "${binarySource}" in the extracted asset.`); } const installDir = getInstallDir(options.installPath); if (!fs3.existsSync(installDir)) { fs3.mkdirSync(installDir, { recursive: true }); } const finalName = binaryDestination || path4.basename(binaryPath); const destPath = path4.join(installDir, finalName); console.log(`Installing ${finalName} to ${destPath}...`); try { fs3.copyFileSync(binaryPath, destPath); } catch (err) { if (err.code === "EBUSY") { throw new Error(`The file ${destPath} is currently in use. Please close any running instances and try again.`); } if (err.code === "EACCES" || err.code === "EPERM") { throw new Error(`Permission denied while installing to ${destPath}. Try running with sudo or as administrator, or use -p to specify a custom path.`); } throw err; } if (process.platform !== "win32") { fs3.chmodSync(destPath, "755"); } console.log("Installation successful!"); process.exit(0); } catch (error) { if (error?.message) { console.error(`Error: ${error.message}`); } else { console.error("Error: Unknown failure."); } process.exit(1); } finally { if (tempDir && fs3.existsSync(tempDir)) { fs3.rmSync(tempDir, { recursive: true, force: true }); } } } run();