 e89f2f4b7b
			
		
	
	e89f2f4b7b
	
	
	
		
			
			Created 10 detailed GitHub issues covering: - Project activation and management UI (#1-2) - Worker node coordination and visualization (#3-4) - Automated GitHub repository scanning (#5) - Intelligent model-to-issue matching (#6) - Multi-model task execution system (#7) - N8N workflow integration (#8) - Hive-Bzzz P2P bridge (#9) - Peer assistance protocol (#10) Each issue includes detailed specifications, acceptance criteria, technical implementation notes, and dependency mapping. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
		
			
				
	
	
		
			1001 lines
		
	
	
		
			38 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			1001 lines
		
	
	
		
			38 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| import expand from 'brace-expansion';
 | |
| import { assertValidPattern } from './assert-valid-pattern.js';
 | |
| import { AST } from './ast.js';
 | |
| import { escape } from './escape.js';
 | |
| import { unescape } from './unescape.js';
 | |
| export const minimatch = (p, pattern, options = {}) => {
 | |
|     assertValidPattern(pattern);
 | |
|     // shortcut: comments match nothing.
 | |
|     if (!options.nocomment && pattern.charAt(0) === '#') {
 | |
|         return false;
 | |
|     }
 | |
|     return new Minimatch(pattern, options).match(p);
 | |
| };
 | |
| // Optimized checking for the most common glob patterns.
 | |
| const starDotExtRE = /^\*+([^+@!?\*\[\(]*)$/;
 | |
| const starDotExtTest = (ext) => (f) => !f.startsWith('.') && f.endsWith(ext);
 | |
| const starDotExtTestDot = (ext) => (f) => f.endsWith(ext);
 | |
| const starDotExtTestNocase = (ext) => {
 | |
|     ext = ext.toLowerCase();
 | |
|     return (f) => !f.startsWith('.') && f.toLowerCase().endsWith(ext);
 | |
| };
 | |
| const starDotExtTestNocaseDot = (ext) => {
 | |
|     ext = ext.toLowerCase();
 | |
|     return (f) => f.toLowerCase().endsWith(ext);
 | |
| };
 | |
| const starDotStarRE = /^\*+\.\*+$/;
 | |
| const starDotStarTest = (f) => !f.startsWith('.') && f.includes('.');
 | |
| const starDotStarTestDot = (f) => f !== '.' && f !== '..' && f.includes('.');
 | |
| const dotStarRE = /^\.\*+$/;
 | |
| const dotStarTest = (f) => f !== '.' && f !== '..' && f.startsWith('.');
 | |
| const starRE = /^\*+$/;
 | |
| const starTest = (f) => f.length !== 0 && !f.startsWith('.');
 | |
| const starTestDot = (f) => f.length !== 0 && f !== '.' && f !== '..';
 | |
| const qmarksRE = /^\?+([^+@!?\*\[\(]*)?$/;
 | |
| const qmarksTestNocase = ([$0, ext = '']) => {
 | |
|     const noext = qmarksTestNoExt([$0]);
 | |
|     if (!ext)
 | |
|         return noext;
 | |
|     ext = ext.toLowerCase();
 | |
|     return (f) => noext(f) && f.toLowerCase().endsWith(ext);
 | |
| };
 | |
| const qmarksTestNocaseDot = ([$0, ext = '']) => {
 | |
|     const noext = qmarksTestNoExtDot([$0]);
 | |
|     if (!ext)
 | |
|         return noext;
 | |
|     ext = ext.toLowerCase();
 | |
|     return (f) => noext(f) && f.toLowerCase().endsWith(ext);
 | |
| };
 | |
| const qmarksTestDot = ([$0, ext = '']) => {
 | |
|     const noext = qmarksTestNoExtDot([$0]);
 | |
|     return !ext ? noext : (f) => noext(f) && f.endsWith(ext);
 | |
| };
 | |
| const qmarksTest = ([$0, ext = '']) => {
 | |
|     const noext = qmarksTestNoExt([$0]);
 | |
|     return !ext ? noext : (f) => noext(f) && f.endsWith(ext);
 | |
| };
 | |
| const qmarksTestNoExt = ([$0]) => {
 | |
|     const len = $0.length;
 | |
|     return (f) => f.length === len && !f.startsWith('.');
 | |
| };
 | |
| const qmarksTestNoExtDot = ([$0]) => {
 | |
|     const len = $0.length;
 | |
|     return (f) => f.length === len && f !== '.' && f !== '..';
 | |
| };
 | |
| /* c8 ignore start */
 | |
| const defaultPlatform = (typeof process === 'object' && process
 | |
|     ? (typeof process.env === 'object' &&
 | |
|         process.env &&
 | |
|         process.env.__MINIMATCH_TESTING_PLATFORM__) ||
 | |
|         process.platform
 | |
|     : 'posix');
 | |
| const path = {
 | |
|     win32: { sep: '\\' },
 | |
|     posix: { sep: '/' },
 | |
| };
 | |
| /* c8 ignore stop */
 | |
| export const sep = defaultPlatform === 'win32' ? path.win32.sep : path.posix.sep;
 | |
| minimatch.sep = sep;
 | |
| export const GLOBSTAR = Symbol('globstar **');
 | |
| minimatch.GLOBSTAR = GLOBSTAR;
 | |
| // any single thing other than /
 | |
| // don't need to escape / when using new RegExp()
 | |
| const qmark = '[^/]';
 | |
| // * => any number of characters
 | |
| const star = qmark + '*?';
 | |
| // ** when dots are allowed.  Anything goes, except .. and .
 | |
| // not (^ or / followed by one or two dots followed by $ or /),
 | |
| // followed by anything, any number of times.
 | |
| const twoStarDot = '(?:(?!(?:\\/|^)(?:\\.{1,2})($|\\/)).)*?';
 | |
| // not a ^ or / followed by a dot,
 | |
| // followed by anything, any number of times.
 | |
| const twoStarNoDot = '(?:(?!(?:\\/|^)\\.).)*?';
 | |
| export const filter = (pattern, options = {}) => (p) => minimatch(p, pattern, options);
 | |
| minimatch.filter = filter;
 | |
| const ext = (a, b = {}) => Object.assign({}, a, b);
 | |
| export const 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: GLOBSTAR,
 | |
|     });
 | |
| };
 | |
| minimatch.defaults = defaults;
 | |
| // Brace expansion:
 | |
| // a{b,c}d -> abd acd
 | |
| // a{b,}c -> abc ac
 | |
| // a{0..3}d -> a0d a1d a2d a3d
 | |
| // a{b,c{d,e}f}g -> abg acdfg acefg
 | |
| // a{b,c}d{e,f}g -> abdeg acdeg abdeg abdfg
 | |
| //
 | |
| // Invalid sets are not expanded.
 | |
| // a{2..}b -> a{2..}b
 | |
| // a{b}c -> a{b}c
 | |
| export const braceExpand = (pattern, options = {}) => {
 | |
|     assertValidPattern(pattern);
 | |
|     // Thanks to Yeting Li <https://github.com/yetingli> for
 | |
|     // improving this regexp to avoid a ReDOS vulnerability.
 | |
|     if (options.nobrace || !/\{(?:(?!\{).)*\}/.test(pattern)) {
 | |
|         // shortcut. no need to expand.
 | |
|         return [pattern];
 | |
|     }
 | |
|     return expand(pattern);
 | |
| };
 | |
| minimatch.braceExpand = braceExpand;
 | |
| // parse a component of the expanded set.
 | |
| // At this point, no pattern may contain "/" in it
 | |
| // so we're going to return a 2d array, where each entry is the full
 | |
| // pattern, split on '/', and then turned into a regular expression.
 | |
| // A regexp is made at the end which joins each array with an
 | |
| // escaped /, and another full one which joins each regexp with |.
 | |
| //
 | |
| // Following the lead of Bash 4.1, note that "**" only has special meaning
 | |
| // when it is the *only* thing in a path portion.  Otherwise, any series
 | |
| // of * is equivalent to a single *.  Globstar behavior is enabled by
 | |
| // default, and can be disabled by setting options.noglobstar.
 | |
| export const makeRe = (pattern, options = {}) => new Minimatch(pattern, options).makeRe();
 | |
| minimatch.makeRe = makeRe;
 | |
| export const 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;
 | |
| // replace stuff like \* with *
 | |
| const globMagic = /[?*]|[+@!]\(.*?\)|\[|\]/;
 | |
| const regExpEscape = (s) => s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
 | |
| export class Minimatch {
 | |
|     options;
 | |
|     set;
 | |
|     pattern;
 | |
|     windowsPathsNoEscape;
 | |
|     nonegate;
 | |
|     negate;
 | |
|     comment;
 | |
|     empty;
 | |
|     preserveMultipleSlashes;
 | |
|     partial;
 | |
|     globSet;
 | |
|     globParts;
 | |
|     nocase;
 | |
|     isWindows;
 | |
|     platform;
 | |
|     windowsNoMagicRoot;
 | |
|     regexp;
 | |
|     constructor(pattern, options = {}) {
 | |
|         assertValidPattern(pattern);
 | |
|         options = options || {};
 | |
|         this.options = options;
 | |
|         this.pattern = pattern;
 | |
|         this.platform = options.platform || defaultPlatform;
 | |
|         this.isWindows = this.platform === 'win32';
 | |
|         this.windowsPathsNoEscape =
 | |
|             !!options.windowsPathsNoEscape || options.allowWindowsEscape === 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 !== undefined
 | |
|                 ? options.windowsNoMagicRoot
 | |
|                 : !!(this.isWindows && this.nocase);
 | |
|         this.globSet = [];
 | |
|         this.globParts = [];
 | |
|         this.set = [];
 | |
|         // make the set of regexps etc.
 | |
|         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;
 | |
|         // empty patterns and comments match nothing.
 | |
|         if (!options.nocomment && pattern.charAt(0) === '#') {
 | |
|             this.comment = true;
 | |
|             return;
 | |
|         }
 | |
|         if (!pattern) {
 | |
|             this.empty = true;
 | |
|             return;
 | |
|         }
 | |
|         // step 1: figure out negation, etc.
 | |
|         this.parseNegate();
 | |
|         // step 2: expand braces
 | |
|         this.globSet = [...new Set(this.braceExpand())];
 | |
|         if (options.debug) {
 | |
|             this.debug = (...args) => console.error(...args);
 | |
|         }
 | |
|         this.debug(this.pattern, this.globSet);
 | |
|         // step 3: now we have a set, so turn each one into a series of
 | |
|         // path-portion matching patterns.
 | |
|         // These will be regexps, except in the case of "**", which is
 | |
|         // set to the GLOBSTAR object for globstar behavior,
 | |
|         // and will not contain any / characters
 | |
|         //
 | |
|         // First, we preprocess to make the glob pattern sets a bit simpler
 | |
|         // and deduped.  There are some perf-killing patterns that can cause
 | |
|         // problems with a glob walk, but we can simplify them down a bit.
 | |
|         const rawGlobParts = this.globSet.map(s => this.slashSplit(s));
 | |
|         this.globParts = this.preprocess(rawGlobParts);
 | |
|         this.debug(this.pattern, this.globParts);
 | |
|         // glob --> regexps
 | |
|         let set = this.globParts.map((s, _, __) => {
 | |
|             if (this.isWindows && this.windowsNoMagicRoot) {
 | |
|                 // check if it's a drive or unc path.
 | |
|                 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);
 | |
|         // filter out everything that didn't compile properly.
 | |
|         this.set = set.filter(s => s.indexOf(false) === -1);
 | |
|         // do not treat the ? in UNC paths as magic
 | |
|         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 we're not in globstar mode, then turn all ** into *
 | |
|         if (this.options.noglobstar) {
 | |
|             for (let i = 0; i < globParts.length; i++) {
 | |
|                 for (let j = 0; j < globParts[i].length; j++) {
 | |
|                     if (globParts[i][j] === '**') {
 | |
|                         globParts[i][j] = '*';
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         const { optimizationLevel = 1 } = this.options;
 | |
|         if (optimizationLevel >= 2) {
 | |
|             // aggressive optimization for the purpose of fs walking
 | |
|             globParts = this.firstPhasePreProcess(globParts);
 | |
|             globParts = this.secondPhasePreProcess(globParts);
 | |
|         }
 | |
|         else if (optimizationLevel >= 1) {
 | |
|             // just basic optimizations to remove some .. parts
 | |
|             globParts = this.levelOneOptimize(globParts);
 | |
|         }
 | |
|         else {
 | |
|             // just collapse multiple ** portions into one
 | |
|             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;
 | |
|             // <pre>/<e>/<rest> -> <pre>/<rest>
 | |
|             if (!this.preserveMultipleSlashes) {
 | |
|                 for (let i = 1; i < parts.length - 1; i++) {
 | |
|                     const p = parts[i];
 | |
|                     // don't squeeze out UNC patterns
 | |
|                     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();
 | |
|                 }
 | |
|             }
 | |
|             // <pre>/<p>/../<rest> -> <pre>/<rest>
 | |
|             let dd = 0;
 | |
|             while (-1 !== (dd = parts.indexOf('..', dd + 1))) {
 | |
|                 const p = parts[dd - 1];
 | |
|                 if (p && p !== '.' && p !== '..' && p !== '**') {
 | |
|                     didSomething = true;
 | |
|                     parts.splice(dd - 1, 2);
 | |
|                     dd -= 2;
 | |
|                 }
 | |
|             }
 | |
|         } while (didSomething);
 | |
|         return parts.length === 0 ? [''] : parts;
 | |
|     }
 | |
|     // First phase: single-pattern processing
 | |
|     // <pre> is 1 or more portions
 | |
|     // <rest> is 1 or more portions
 | |
|     // <p> is any portion other than ., .., '', or **
 | |
|     // <e> 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.
 | |
|     //
 | |
|     // <pre>/**/../<p>/<p>/<rest> -> {<pre>/../<p>/<p>/<rest>,<pre>/**/<p>/<p>/<rest>}
 | |
|     // <pre>/<e>/<rest> -> <pre>/<rest>
 | |
|     // <pre>/<p>/../<rest> -> <pre>/<rest>
 | |
|     // **/**/<rest> -> **/<rest>
 | |
|     //
 | |
|     // **/*/<rest> -> */**/<rest> <== 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;
 | |
|             // <pre>/**/../<p>/<p>/<rest> -> {<pre>/../<p>/<p>/<rest>,<pre>/**/<p>/<p>/<rest>}
 | |
|             for (let parts of globParts) {
 | |
|                 let gs = -1;
 | |
|                 while (-1 !== (gs = parts.indexOf('**', gs + 1))) {
 | |
|                     let gss = gs;
 | |
|                     while (parts[gss + 1] === '**') {
 | |
|                         // <pre>/**/**/<rest> -> <pre>/**/<rest>
 | |
|                         gss++;
 | |
|                     }
 | |
|                     // eg, if gs is 2 and gss is 4, that means we have 3 **
 | |
|                     // parts, and can remove 2 of them.
 | |
|                     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;
 | |
|                     // edit parts in place, and push the new one
 | |
|                     parts.splice(gs, 1);
 | |
|                     const other = parts.slice(0);
 | |
|                     other[gs] = '**';
 | |
|                     globParts.push(other);
 | |
|                     gs--;
 | |
|                 }
 | |
|                 // <pre>/<e>/<rest> -> <pre>/<rest>
 | |
|                 if (!this.preserveMultipleSlashes) {
 | |
|                     for (let i = 1; i < parts.length - 1; i++) {
 | |
|                         const p = parts[i];
 | |
|                         // don't squeeze out UNC patterns
 | |
|                         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();
 | |
|                     }
 | |
|                 }
 | |
|                 // <pre>/<p>/../<rest> -> <pre>/<rest>
 | |
|                 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
 | |
|     // {<pre>/*/<rest>,<pre>/<p>/<rest>} -> <pre>/*/<rest>
 | |
|     // {<pre>/<rest>,<pre>/<rest>} -> <pre>/<rest>
 | |
|     // {<pre>/**/<rest>,<pre>/<rest>} -> <pre>/**/<rest>
 | |
|     //
 | |
|     // {<pre>/**/<rest>,<pre>/**/<p>/<rest>} -> <pre>/**/<rest>
 | |
|     // ^-- 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;
 | |
|             }
 | |
|         }
 | |
|         // if we fall out of the loop, it means they two are identical
 | |
|         // as long as their lengths match
 | |
|         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) {
 | |
|         const options = this.options;
 | |
|         // UNC paths like //?/X:/... can match X:/... and vice versa
 | |
|         // Drive letters in absolute drive or unc paths are always compared
 | |
|         // case-insensitively.
 | |
|         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 : undefined;
 | |
|             const pdi = patternUNC ? 3 : patternDrive ? 0 : undefined;
 | |
|             if (typeof fdi === 'number' && typeof pdi === 'number') {
 | |
|                 const [fd, pd] = [file[fdi], pattern[pdi]];
 | |
|                 if (fd.toLowerCase() === pd.toLowerCase()) {
 | |
|                     pattern[pdi] = fd;
 | |
|                     if (pdi > fdi) {
 | |
|                         pattern = pattern.slice(pdi);
 | |
|                     }
 | |
|                     else if (fdi > pdi) {
 | |
|                         file = file.slice(fdi);
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         // resolve and reduce . and .. portions in the file as well.
 | |
|         // dont' need to do the second phase, because it's only one string[]
 | |
|         const { optimizationLevel = 1 } = this.options;
 | |
|         if (optimizationLevel >= 2) {
 | |
|             file = this.levelTwoFileOptimize(file);
 | |
|         }
 | |
|         this.debug('matchOne', this, { file, pattern });
 | |
|         this.debug('matchOne', file.length, pattern.length);
 | |
|         for (var fi = 0, pi = 0, fl = file.length, pl = pattern.length; fi < fl && pi < pl; fi++, pi++) {
 | |
|             this.debug('matchOne loop');
 | |
|             var p = pattern[pi];
 | |
|             var f = file[fi];
 | |
|             this.debug(pattern, p, f);
 | |
|             // should be impossible.
 | |
|             // some invalid regexp stuff in the set.
 | |
|             /* c8 ignore start */
 | |
|             if (p === false) {
 | |
|                 return false;
 | |
|             }
 | |
|             /* c8 ignore stop */
 | |
|             if (p === GLOBSTAR) {
 | |
|                 this.debug('GLOBSTAR', [pattern, p, f]);
 | |
|                 // "**"
 | |
|                 // a/**/b/**/c would match the following:
 | |
|                 // a/b/x/y/z/c
 | |
|                 // a/x/y/z/b/c
 | |
|                 // a/b/x/b/x/c
 | |
|                 // a/b/c
 | |
|                 // To do this, take the rest of the pattern after
 | |
|                 // the **, and see if it would match the file remainder.
 | |
|                 // If so, return success.
 | |
|                 // If not, the ** "swallows" a segment, and try again.
 | |
|                 // This is recursively awful.
 | |
|                 //
 | |
|                 // a/**/b/**/c matching a/b/x/y/z/c
 | |
|                 // - a matches a
 | |
|                 // - doublestar
 | |
|                 //   - matchOne(b/x/y/z/c, b/**/c)
 | |
|                 //     - b matches b
 | |
|                 //     - doublestar
 | |
|                 //       - matchOne(x/y/z/c, c) -> no
 | |
|                 //       - matchOne(y/z/c, c) -> no
 | |
|                 //       - matchOne(z/c, c) -> no
 | |
|                 //       - matchOne(c, c) yes, hit
 | |
|                 var fr = fi;
 | |
|                 var pr = pi + 1;
 | |
|                 if (pr === pl) {
 | |
|                     this.debug('** at the end');
 | |
|                     // a ** at the end will just swallow the rest.
 | |
|                     // We have found a match.
 | |
|                     // however, it will not swallow /.x, unless
 | |
|                     // options.dot is set.
 | |
|                     // . and .. are *never* matched by **, for explosively
 | |
|                     // exponential reasons.
 | |
|                     for (; fi < fl; fi++) {
 | |
|                         if (file[fi] === '.' ||
 | |
|                             file[fi] === '..' ||
 | |
|                             (!options.dot && file[fi].charAt(0) === '.'))
 | |
|                             return false;
 | |
|                     }
 | |
|                     return true;
 | |
|                 }
 | |
|                 // ok, let's see if we can swallow whatever we can.
 | |
|                 while (fr < fl) {
 | |
|                     var swallowee = file[fr];
 | |
|                     this.debug('\nglobstar while', file, fr, pattern, pr, swallowee);
 | |
|                     // XXX remove this slice.  Just pass the start index.
 | |
|                     if (this.matchOne(file.slice(fr), pattern.slice(pr), partial)) {
 | |
|                         this.debug('globstar found match!', fr, fl, swallowee);
 | |
|                         // found a match.
 | |
|                         return true;
 | |
|                     }
 | |
|                     else {
 | |
|                         // can't swallow "." or ".." ever.
 | |
|                         // can only swallow ".foo" when explicitly asked.
 | |
|                         if (swallowee === '.' ||
 | |
|                             swallowee === '..' ||
 | |
|                             (!options.dot && swallowee.charAt(0) === '.')) {
 | |
|                             this.debug('dot detected!', file, fr, pattern, pr);
 | |
|                             break;
 | |
|                         }
 | |
|                         // ** swallows a segment, and continue.
 | |
|                         this.debug('globstar swallow a segment, and continue');
 | |
|                         fr++;
 | |
|                     }
 | |
|                 }
 | |
|                 // no match was found.
 | |
|                 // However, in partial mode, we can't say this is necessarily over.
 | |
|                 /* c8 ignore start */
 | |
|                 if (partial) {
 | |
|                     // ran out of file
 | |
|                     this.debug('\n>>> no match, partial?', file, fr, pattern, pr);
 | |
|                     if (fr === fl) {
 | |
|                         return true;
 | |
|                     }
 | |
|                 }
 | |
|                 /* c8 ignore stop */
 | |
|                 return false;
 | |
|             }
 | |
|             // something other than **
 | |
|             // non-magic patterns just have to match exactly
 | |
|             // patterns with magic have been turned into regexps.
 | |
|             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;
 | |
|         }
 | |
|         // Note: ending in / means that we'll get a final ""
 | |
|         // at the end of the pattern.  This can only match a
 | |
|         // corresponding "" at the end of the file.
 | |
|         // If the file ends in /, then it can only match a
 | |
|         // a pattern that ends in /, unless the pattern just
 | |
|         // doesn't have any more for it. But, a/b/ should *not*
 | |
|         // match "a/b/*", even though "" matches against the
 | |
|         // [^/]*? pattern, except in partial mode, where it might
 | |
|         // simply not be reached yet.
 | |
|         // However, a/b/ should still satisfy a/*
 | |
|         // now either we fell off the end of the pattern, or we're done.
 | |
|         if (fi === fl && pi === pl) {
 | |
|             // ran out of pattern and filename at the same time.
 | |
|             // an exact hit!
 | |
|             return true;
 | |
|         }
 | |
|         else if (fi === fl) {
 | |
|             // ran out of file, but still had pattern left.
 | |
|             // this is ok if we're doing the match as part of
 | |
|             // a glob fs traversal.
 | |
|             return partial;
 | |
|         }
 | |
|         else if (pi === pl) {
 | |
|             // ran out of pattern, still have file left.
 | |
|             // this is only acceptable if we're on the very last
 | |
|             // empty segment of a file with a trailing slash.
 | |
|             // a/* should match a/b/
 | |
|             return fi === fl - 1 && file[fi] === '';
 | |
|             /* c8 ignore start */
 | |
|         }
 | |
|         else {
 | |
|             // should be unreachable.
 | |
|             throw new Error('wtf?');
 | |
|         }
 | |
|         /* c8 ignore stop */
 | |
|     }
 | |
|     braceExpand() {
 | |
|         return braceExpand(this.pattern, this.options);
 | |
|     }
 | |
|     parse(pattern) {
 | |
|         assertValidPattern(pattern);
 | |
|         const options = this.options;
 | |
|         // shortcuts
 | |
|         if (pattern === '**')
 | |
|             return GLOBSTAR;
 | |
|         if (pattern === '')
 | |
|             return '';
 | |
|         // far and away, the most common glob pattern parts are
 | |
|         // *, *.*, and *.<ext>  Add a fast check method for those.
 | |
|         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') {
 | |
|             // Avoids overriding in frozen environments
 | |
|             Reflect.defineProperty(re, 'test', { value: fastTest });
 | |
|         }
 | |
|         return re;
 | |
|     }
 | |
|     makeRe() {
 | |
|         if (this.regexp || this.regexp === false)
 | |
|             return this.regexp;
 | |
|         // at this point, this.set is a 2d array of partial
 | |
|         // pattern strings, or "**".
 | |
|         //
 | |
|         // It's better to use .match().  This function shouldn't
 | |
|         // be used, really, but it's pretty convenient sometimes,
 | |
|         // when you just want to work with a regex.
 | |
|         const set = this.set;
 | |
|         if (!set.length) {
 | |
|             this.regexp = false;
 | |
|             return this.regexp;
 | |
|         }
 | |
|         const options = this.options;
 | |
|         const twoStar = options.noglobstar
 | |
|             ? star
 | |
|             : options.dot
 | |
|                 ? twoStarDot
 | |
|                 : twoStarNoDot;
 | |
|         const flags = new Set(options.nocase ? ['i'] : []);
 | |
|         // regexpify non-globstar patterns
 | |
|         // if ** is only item, then we just do one twoStar
 | |
|         // if ** is first, and there are more, prepend (\/|twoStar\/)? to next
 | |
|         // if ** is last, append (\/twoStar|) to previous
 | |
|         // if ** is in the middle, append (\/|\/twoStar\/) to previous
 | |
|         // then filter out GLOBSTAR symbols
 | |
|         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'
 | |
|                     ? regExpEscape(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 === undefined) {
 | |
|                     if (next !== undefined && next !== GLOBSTAR) {
 | |
|                         pp[i + 1] = '(?:\\/|' + twoStar + '\\/)?' + next;
 | |
|                     }
 | |
|                     else {
 | |
|                         pp[i] = twoStar;
 | |
|                     }
 | |
|                 }
 | |
|                 else if (next === undefined) {
 | |
|                     pp[i - 1] = prev + '(?:\\/|' + twoStar + ')?';
 | |
|                 }
 | |
|                 else if (next !== GLOBSTAR) {
 | |
|                     pp[i - 1] = prev + '(?:\\/|\\/' + twoStar + '\\/)' + next;
 | |
|                     pp[i + 1] = GLOBSTAR;
 | |
|                 }
 | |
|             });
 | |
|             return pp.filter(p => p !== GLOBSTAR).join('/');
 | |
|         })
 | |
|             .join('|');
 | |
|         // need to wrap in parens if we had more than one thing with |,
 | |
|         // otherwise only the first will be anchored to ^ and the last to $
 | |
|         const [open, close] = set.length > 1 ? ['(?:', ')'] : ['', ''];
 | |
|         // must match entire pattern
 | |
|         // ending in a * or ** will make it less strict.
 | |
|         re = '^' + open + re + close + '$';
 | |
|         // can match anything, as long as it's not this.
 | |
|         if (this.negate)
 | |
|             re = '^(?!' + re + ').+$';
 | |
|         try {
 | |
|             this.regexp = new RegExp(re, [...flags].join(''));
 | |
|             /* c8 ignore start */
 | |
|         }
 | |
|         catch (ex) {
 | |
|             // should be impossible
 | |
|             this.regexp = false;
 | |
|         }
 | |
|         /* c8 ignore stop */
 | |
|         return this.regexp;
 | |
|     }
 | |
|     slashSplit(p) {
 | |
|         // if p starts with // on windows, we preserve that
 | |
|         // so that UNC paths aren't broken.  Otherwise, any number of
 | |
|         // / characters are coalesced into one, unless
 | |
|         // preserveMultipleSlashes is set to true.
 | |
|         if (this.preserveMultipleSlashes) {
 | |
|             return p.split('/');
 | |
|         }
 | |
|         else if (this.isWindows && /^\/\/[^\/]+/.test(p)) {
 | |
|             // add an extra '' for the one we lose
 | |
|             return ['', ...p.split(/\/+/)];
 | |
|         }
 | |
|         else {
 | |
|             return p.split(/\/+/);
 | |
|         }
 | |
|     }
 | |
|     match(f, partial = this.partial) {
 | |
|         this.debug('match', f, this.pattern);
 | |
|         // short-circuit in the case of busted things.
 | |
|         // comments, etc.
 | |
|         if (this.comment) {
 | |
|             return false;
 | |
|         }
 | |
|         if (this.empty) {
 | |
|             return f === '';
 | |
|         }
 | |
|         if (f === '/' && partial) {
 | |
|             return true;
 | |
|         }
 | |
|         const options = this.options;
 | |
|         // windows: need to use /, not \
 | |
|         if (this.isWindows) {
 | |
|             f = f.split('\\').join('/');
 | |
|         }
 | |
|         // treat the test path as a set of pathparts.
 | |
|         const ff = this.slashSplit(f);
 | |
|         this.debug(this.pattern, 'split', ff);
 | |
|         // just ONE of the pattern sets in this.set needs to match
 | |
|         // in order for it to be valid.  If negating, then just one
 | |
|         // match means that we have failed.
 | |
|         // Either way, return on the first hit.
 | |
|         const set = this.set;
 | |
|         this.debug(this.pattern, 'set', set);
 | |
|         // Find the basename of the path by looking for the last non-empty segment
 | |
|         let filename = ff[ff.length - 1];
 | |
|         if (!filename) {
 | |
|             for (let i = ff.length - 2; !filename && i >= 0; i--) {
 | |
|                 filename = ff[i];
 | |
|             }
 | |
|         }
 | |
|         for (let i = 0; i < set.length; i++) {
 | |
|             const pattern = set[i];
 | |
|             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;
 | |
|             }
 | |
|         }
 | |
|         // didn't get any hits.  this is success if it's a negative
 | |
|         // pattern, failure otherwise.
 | |
|         if (options.flipNegate) {
 | |
|             return false;
 | |
|         }
 | |
|         return this.negate;
 | |
|     }
 | |
|     static defaults(def) {
 | |
|         return minimatch.defaults(def).Minimatch;
 | |
|     }
 | |
| }
 | |
| /* c8 ignore start */
 | |
| export { AST } from './ast.js';
 | |
| export { escape } from './escape.js';
 | |
| export { unescape } from './unescape.js';
 | |
| /* c8 ignore stop */
 | |
| minimatch.AST = AST;
 | |
| minimatch.Minimatch = Minimatch;
 | |
| minimatch.escape = escape;
 | |
| minimatch.unescape = unescape;
 | |
| //# sourceMappingURL=index.js.map
 |