// s.matchAll(/re/g): every match as its honest string[] slice (match's // rule — [whole, ...captures], nonparticipating captures read undefined), in // the two lowered consumer shapes: the immediate [...spread] + map (the // portless plist scraper) and the direct for-of walk. Empty matches // advance one position (the spec's AdvanceStringIndex). A non-global // regex throws Node's exact TypeError, catchably. const block = "PATH /usr/bin\nHOME /root"; const strings = [...block.matchAll(/([\s\S]*?)<\/string>/g)].map((match) => match[1]); console.log(strings.length, strings.join("|")); for (const match of block.matchAll(/([\s\S]*?)<\/key>\s*([\s\S]*?)<\/string>/g)) { console.log(match.length, match[0].length, match[1], match[2]); } // No match drains to the empty array; empty matches advance. console.log([...("abc".matchAll(/x/g))].length); const empties = [...("ab".matchAll(/z?/g))]; console.log(empties.length); // Nonparticipating captures retain undefined; formatting may use a fallback. for (const m of "a1 b".matchAll(/([ab])(\d)?/g)) { console.log(`[${m[0]}|${m[1]}|${(m[2] as string | undefined) ?? ""}]`); } // The non-global form throws Node's exact TypeError. const re = /never-global/; try { "x".matchAll(re); console.log("no-throw"); } catch (e) { console.log("caught", e instanceof TypeError, (e as Error).message); }