#!/usr/bin/env node
import S from"node:fs/promises";import _ from"node:path";import g from"node:path";import C from"node:os";import{fileURLToPath as D}from"node:url";var T={"A — AI":"ai","B — Devtools":"devtools","C — Web Platform":"web","D — Security":"security","E — Repos & Releases":"repos","Cross-Domain Discovery":"cross"},j=g.dirname(D(import.meta.url)),F=g.resolve(j,"..","references","sources.md");function x(e){return e&&(e==="~"?C.homedir():e.startsWith("~/")?g.join(C.homedir(),e.slice(2)):e)}function w(e){return g.resolve(x(e))}function R(e,t="cross"){if(!e)return t;if(/^Cross[-\s]Domain/i.test(e))return"cross";for(let[s,o]of Object.entries(T))if(e===s||e.startsWith(s))return o;return t}var E=["cross","ai","devtools","web","security","repos"],$=[/^Community/i,/^Optional Aggregators/i,/^Validation/i,/^Framework-specific/i,/^Tier 3/i,/^Release Feed Pattern/i,/^Validation Tools/i];function B(e){let t={sources:F,jsonOut:"",domains:"",help:!1};for(let s=0;s<e.length;s+=1){let o=e[s];if(o==="--help"||o==="-h"){t.help=!0;continue}if(o==="--sources"){t.sources=e[s+1]||"",s+=1;continue}if(o==="--json-out"){t.jsonOut=e[s+1]||"",s+=1;continue}if(o==="--domains"){t.domains=e[s+1]||"",s+=1;continue}throw new Error(`Unknown argument: ${o}`)}return t}function P(){console.log(`Usage:
  node skills/whats-new/scripts/catalog-sources.mjs [--sources <sources.md>] [--json-out <catalog.json>] [--domains <ai,devtools,...>]

What it does:
  1. Parses the whats-new sources catalog.
  2. Extracts websites, RSS feeds, validation repos, and custom resources.
  3. Outputs a machine-readable checklist for full coverage research runs.`)}function U(e){return String(e||"").replace(/<br\s*\/?>/gi," ").replace(/`/g,"").replace(/\s+/g," ").trim()}function h(e){let t=String(e||"").replace(/`/g,"").trim(),s=t.match(/\((https?:\/\/[^\s)]+)\)/);if(s)return s[1];let o=t.match(/https?:\/\/[^\s|)]+/);return o?o[0].replace(/[),.;]+$/,""):/^[a-z0-9][\w.-]+\.[a-z]{2,}(\/\S*)?$/.test(t)?`https://${t}`:""}function v(e){if(e.length<2)return[];let t=e.map(i=>i.trim().replace(/^\|/,"").replace(/\|$/,"").split("|").map(r=>U(r))),s=t[0];return t.slice(2).filter(i=>i.some(r=>r)).map(i=>s.reduce((r,c,n)=>(r[c]=i[n]||"",r),{}))}function y(e){let s=e.join(`
`).match(/`([^`]+)`/g)||[],o=[],i=new Set;for(let r of s){let c=r.replace(/`/g,"").trim();h(c)||z(c)||/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(c)&&(i.has(c)||(i.add(c),o.push({repo:c})))}return o}function z(e){let t=String(e||"").trim().toLowerCase();if(!t.includes("/")||!/\.(xml|atom|rss|json)$/.test(t))return!1;let[s]=t.split("/",1);return["daily","weekly","monthly"].includes(s)}function I(e){let t=String(e||"").replace(/`/g,"").trim();if(h(t))return"";let s=t.match(/\b([A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+)\b/);return s?s[1]:""}function N(e){return`https://github.com/${e}/releases.atom`}function W(e){return e.map(t=>t.trim()).filter(t=>t&&!t.startsWith("**")).map(t=>t.replace(/^-\s+/,"").replace(/\*\*/g,"").trim()).filter(t=>!!t.replace(/`[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+`/g,"").replace(/[,\s]/g,"")).filter(Boolean).map(t=>({label:t}))}function M(e){return $.some(t=>t.test(e||""))}function k(e,t,s){return s?(e.has(s)||e.set(s,{id:s,section:t,websites:[],rssFeeds:[],repos:[],customResources:[]}),e.get(s)):null}function d(e,t,s){if(!t)return;let o=s(t);e.some(i=>s(i)===o)||e.push(t)}function b(e,t,s,o){if(!t.length)return;let i=R(s,""),r=k(e,s,i);if(!r)return;let c=v(t);for(let n of c){let a=h(n.URL||n.Url||n.Link||""),m=I(n.Repo||n.Source||n.Name||""),u=n.Source||n.Repo||n.Name||a||m,f=[n.Notes,n.Coverage].filter(Boolean).join(" ").trim();a&&d(r.websites,{name:u,url:a,notes:f,subsection:o||"Websites"},l=>`${l.name}::${l.url}`);let p=U(n.RSS||"");if(p&&p!=="—"&&p!=="-"){let l=h(p);l&&d(r.rssFeeds,{name:u||l,url:l,websiteUrl:a||void 0,subsection:o||"Websites"},A=>A.url)}!a&&m&&d(r.repos,{repo:m,notes:f,subsection:o||"Repos"},l=>l.repo)}}function L(e,t,s,o){if(!t.length||!/^RSS Feeds/i.test(o||""))return;let i=R(s,""),r=k(e,s,i);if(r)for(let c of t){let n=h(c);n&&d(r.rssFeeds,{url:n,subsection:o||"RSS Feeds"},a=>a.url)}}function O(e,t,s,o){if(!t.length)return;let i=R(s,""),r=k(e,s,i);if(!r)return;let c=y(t);for(let n of c)d(r.repos,{...n,subsection:o||"Repos"},a=>a.repo);if(/^Release Feed Pattern/i.test(o||""))for(let n of c)d(r.rssFeeds,{name:n.repo,url:N(n.repo),websiteUrl:`https://github.com/${n.repo}`,subsection:o||"Release Feed Pattern"},a=>a.url);if(!/^Repos/i.test(o||"")&&(/^Custom Resources/i.test(o||"")||M(o)))for(let n of W(t))d(r.customResources,{...n,subsection:o||"Custom Resources"},a=>a.label)}function Z(e){let t=e.split(/\r?\n/),s=new Map,o="",i="",r=[],c=[],n=[],a=!1;function m(){b(s,r,o,i),O(s,c,o,i),L(s,n,o,i),r=[],c=[],n=[]}for(let u of t){let f=u.trim(),p=u.match(/^##\s+(.+)$/),l=u.match(/^###\s+(.+)$/);if(p){m(),o=p[1].trim(),i="";continue}if(l){m(),i=l[1].trim();continue}if(/^```/.test(f)){a?a=!1:(b(s,r,o,i),O(s,c,o,i),r=[],c=[],a=!0);continue}if(a){n.push(u);continue}if(f.startsWith("|")){r.push(u);continue}r.length&&!f.startsWith("|")&&(b(s,r,o,i),r=[]),!(!f||f==="---")&&c.push(u)}return m(),E.map(u=>s.get(u)).filter(Boolean).map(u=>({...u,counts:{websites:u.websites.length,rssFeeds:u.rssFeeds.length,repos:u.repos.length,customResources:u.customResources.length,total:u.websites.length+u.rssFeeds.length+u.repos.length+u.customResources.length}}))}function J(e){let t={},s=0,o=0,i=0,r=0;for(let c of e)t[c.id]=c.counts,s+=c.counts.websites,o+=c.counts.rssFeeds,i+=c.counts.repos,r+=c.counts.customResources;return{totalDomains:e.length,totalWebsites:s,totalRssFeeds:o,totalRepos:i,totalCustomResources:r,totalResources:s+o+i+r,byDomain:t}}async function V(){let e=B(process.argv.slice(2));if(e.help){P();return}let t=w(e.sources),s=await S.readFile(t,"utf8"),o=Z(s),i=new Set(String(e.domains||"").split(",").map(n=>n.trim()).filter(Boolean)),r=i.size?o.filter(n=>i.has(n.id)):o,c={generatedAt:new Date().toISOString(),sourcesPath:t,summary:J(r),domains:r};if(e.jsonOut){let n=w(e.jsonOut);await S.mkdir(_.dirname(n),{recursive:!0}),await S.writeFile(n,JSON.stringify(c,null,2)+`
`,"utf8"),console.log(`Catalog written: ${n}`)}else console.log(JSON.stringify(c,null,2))}V().catch(e=>{console.error(e.message||String(e)),process.exitCode=1});
