@frontfriend/tailwind 4.0.18 → 4.0.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser.mjs +1 -1
- package/dist/browser.mjs.map +3 -3
- package/dist/cli.js +37 -36
- package/dist/component-recipes.d.ts +14 -0
- package/dist/components-config-schema.js +1 -1
- package/dist/components-config-schema.js.map +3 -3
- package/dist/ffdc.d.ts +11 -1
- package/dist/ffdc.js +1 -1
- package/dist/ffdc.js.map +4 -4
- package/dist/index.js +3 -3
- package/dist/index.js.map +4 -4
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +3 -3
- package/dist/lib/core/api-client.js +2 -2
- package/dist/lib/core/api-client.js.map +3 -3
- package/dist/lib/core/cache-manager.js +6 -4
- package/dist/lib/core/cached-components-config.js +2 -0
- package/dist/lib/core/cached-components-config.js.map +7 -0
- package/dist/lib/core/component-downloader.js +3 -3
- package/dist/lib/core/component-downloader.js.map +3 -3
- package/dist/lib/core/component-recipes.js +8 -0
- package/dist/lib/core/component-recipes.js.map +7 -0
- package/dist/lib/core/components-config-schema.js +1 -1
- package/dist/lib/core/components-config-schema.js.map +3 -3
- package/dist/lib/core/constants.js +1 -1
- package/dist/lib/core/constants.js.map +3 -3
- package/dist/lib/core/credentials.js +2 -2
- package/dist/lib/core/credentials.js.map +3 -3
- package/dist/next.js +1 -1
- package/dist/next.js.map +4 -4
- package/dist/runtime.js +1 -1
- package/dist/runtime.js.map +3 -3
- package/dist/types/index.d.ts +15 -1
- package/dist/vite.js +6 -19
- package/dist/vite.js.map +4 -4
- package/dist/vite.mjs +4 -4
- package/dist/vite.mjs.map +3 -3
- package/package.json +7 -1
- package/scripts/build.js +32 -9
- package/src/theme.css +3 -9
- package/dist/lib/core/default-config-data.js +0 -2
- package/dist/lib/core/default-config-data.js.map +0 -7
- package/dist/lib/core/default-config.js +0 -2
- package/dist/lib/core/default-config.js.map +0 -7
- package/scripts/master-components-config.json +0 -1678
- package/scripts/update-default-config-data.js +0 -891
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var
|
|
2
|
-
`,{mode:384});try{R.chmodSync(k,384)}catch{}}function qe(){return R.existsSync(k)?(R.unlinkSync(k),!0):!1}function ue(n,e=60*1e3){return!(n!=null&&n.accessToken)||!n.expiresAt||Date.now()+e>=n.expiresAt}function j(n){let e=new Error(`${n}. Run \`frontfriend login\` to re-authenticate.`);return e.code="FF_AUTH_REFRESH_FAILED",e}function pe(n,e,t){return new Promise((r,s)=>{let i=new Ne(n),o=i.protocol==="https:"?Oe:Je,l=JSON.stringify(e||{}),c=o.request({hostname:i.hostname,port:i.port,path:`${i.pathname}${i.search}`,method:"POST",headers:{"content-type":"application/json","content-length":Buffer.byteLength(l),...t?{authorization:`Bearer ${t}`}:{}}},a=>{let d="";a.on("data",h=>{d+=h}),a.on("end",()=>{if(a.statusCode>=300&&a.statusCode<400){let g=a.headers.location?` to ${a.headers.location}`:"",f=new Error(`HTTP ${a.statusCode}: redirected${g}`);f.statusCode=a.statusCode,f.body=d,s(f);return}let h=null;try{h=d?JSON.parse(d):null}catch{let f=a.headers["content-type"]||"unknown content-type",w=new Error(`Expected JSON from FrontFriend API at ${n}, but received ${f} (HTTP ${a.statusCode}). Check FF_API_URL or pass --api-url with the FrontFriend app URL.`);if(w.statusCode=a.statusCode,w.body=d,a.statusCode>=400){s(w);return}s(w);return}if(a.statusCode>=400){let g=new Error((h==null?void 0:h.error_description)||(h==null?void 0:h.error)||`HTTP ${a.statusCode}`);g.statusCode=a.statusCode,g.body=h,s(g);return}r(h)})});c.on("error",s),c.write(l),c.end()})}async function ge(n,e=le){if(!(n!=null&&n.refreshToken)||!(n!=null&&n.deviceId))throw j("FrontFriend session expired");let t;try{t=await pe(`${e}/api/plugin/token`,{grant_type:"refresh_token",refresh_token:n.refreshToken,device_id:n.deviceId})}catch(s){throw j((s==null?void 0:s.message)||"Could not refresh FrontFriend session")}if(!(t!=null&&t.access_token)||!(t!=null&&t.refresh_token))throw j("Could not refresh FrontFriend session");let r={accessToken:t.access_token,refreshToken:t.refresh_token,deviceId:t.device_id||n.deviceId,expiresAt:Date.now()+(t.expires_in||900)*1e3,refreshExpiresAt:Date.now()+(t.refresh_expires_in||604800)*1e3,ffApiUrl:e};return fe(r),r}async function Ve(n={}){if(!n.skipCredentials){let e=he();if(e!=null&&e.accessToken||e!=null&&e.refreshToken){let t=n.baseURL||e.ffApiUrl||le;return e.accessToken&&!ue(e)?e.accessToken:(await ge(e,t)).accessToken}}return n.envToken?n.envToken:n.legacyToken?(n.silent||console.warn("\u26A0\uFE0F auth-token in frontfriend.config.js is deprecated. Run `frontfriend login` to store credentials outside the repo."),n.legacyToken):null}ye.exports={getCredentialsPath:Be,readCredentials:he,writeCredentials:fe,deleteCredentials:qe,isExpired:ue,postJson:pe,refreshCredentials:ge,resolveAuthToken:Ve,createAuthRefreshError:j}});var Fe=E((it,$e)=>{var J=require("https"),N=require("http"),{URL:q}=require("url"),{DEFAULT_API_URL:He,LEGACY_API_URL:U,ENV_VARS:B}=A(),{APIError:y}=ee(),{LocalTokenReader:We}=ae(),{resolveAuthToken:Ye}=me(),P="Authentication required. Run `frontfriend login` to re-authenticate.";function we(n,e,t,r){return t?n.get(new q(e),{headers:{authorization:`Bearer ${t}`}},r):n.get(e,r)}function V(n,e=""){var r;let t=((r=n.headers)==null?void 0:r.location)||"";return n.statusCode===401||n.statusCode===403||n.statusCode>=300&&n.statusCode<400&&t.includes("/sign-in")||String(e).trim().startsWith("/sign-in")}function Ce(n,e,t=""){if(V(n,t))return new y(P,n.statusCode,e);let r=n.statusMessage;try{let s=t?JSON.parse(t):null;s!=null&&s.error&&(r=s.error);let i=[...Array.isArray(s==null?void 0:s.cssErrors)?s.cssErrors.slice(0,5).map(o=>`${o.property}: ${o.className}`):[],...Array.isArray(s==null?void 0:s.schemaErrors)?s.schemaErrors.slice(0,5):[]];i.length>0&&(r=`${r} (${i.join("; ")})`)}catch{}return new y(`HTTP ${n.statusCode}: ${r}`,n.statusCode,e)}var H=class{constructor(e,t={}){this.ffId=e,this.baseURL=t.baseURL||process.env[B.FF_API_URL]||He,this.legacyAuthToken=t.authToken||null,this.authToken=null,this.skipCredentials=t.skipCredentials||!1,process.env[B.FF_USE_LOCAL]==="true"&&(console.log(" \u{1F3E0} Using local token reader"),this.localReader=new We(e,t))}async getAuthToken(){return this.authToken=await Ye({baseURL:this.baseURL,envToken:process.env[B.FF_AUTH_TOKEN],legacyToken:this.legacyAuthToken,skipCredentials:this.skipCredentials}),this.authToken}async fetchJson(e,t={}){let r=await this.getAuthToken();return new Promise((s,i)=>{let o=e.startsWith("https")?J:N;we(o,e,r,l=>{let c="";if(l.statusCode>=300){i(Ce(l,e));return}l.on("data",a=>{c+=a}),l.on("end",()=>{try{let a=JSON.parse(c);t.returnHeaders?s({data:a,headers:l.headers}):s(a)}catch(a){if(V(l,c)){i(new y(P,l.statusCode,e));return}i(new y(`Invalid JSON response: ${a.message}`,l.statusCode,e))}}),l.on("error",a=>{i(new y(`Response error: ${a.message}`,l.statusCode,e))})}).on("error",l=>{i(new y(`Network error: ${l.message}`,0,e))})})}async fetchRaw(e){let t=await this.getAuthToken();return new Promise((r,s)=>{let i=e.startsWith("https")?J:N;we(i,e,t,o=>{let l="";if(o.statusCode>=400){s(new y(`HTTP ${o.statusCode}: ${o.statusMessage}`,o.statusCode,e));return}o.on("data",c=>{l+=c}),o.on("end",()=>{r(l)}),o.on("error",c=>{s(new y(`Response error: ${c.message}`,o.statusCode,e))})}).on("error",o=>{s(new y(`Network error: ${o.message}`,0,e))})})}async fetchTokens(){if(this.localReader)return this.localReader.fetchTokens();try{let s=`${this.baseURL}/api/design-systems/${this.ffId}/tokens`,i=await this.fetchJson(s);if(i&&i.tokens){let o=i.tokens;return{global:o["Global Tokens/Default"]||o.global||null,colors:o["Global Colors/Default"]||o.colors||null,semantic:o["Semantic/Light"]||o["semantic/light"]||null,semanticDark:o["Semantic/Dark"]||o["semantic/dark"]||null}}}catch(s){if(s.statusCode===400)throw new y("Design system not synced with Figma. Please sync tokens from Figma before using this design system.",400,`${this.baseURL}/api/design-systems/${this.ffId}/tokens`)}let e=U,t={global:`${e}/${this.ffId}/global-tokens/default.json`,colors:`${e}/${this.ffId}/global-colors/default.json`,semantic:`${e}/${this.ffId}/semantic/light.json`,semanticDark:`${e}/${this.ffId}/semantic/dark.json`},r=await Promise.all([this.fetchJson(t.global).catch(s=>s.statusCode===404?null:Promise.reject(s)),this.fetchJson(t.colors).catch(s=>s.statusCode===404?null:Promise.reject(s)),this.fetchJson(t.semantic).catch(s=>s.statusCode===404?null:Promise.reject(s)),this.fetchJson(t.semanticDark).catch(s=>s.statusCode===404?null:Promise.reject(s))]);return{global:r[0],colors:r[1],semantic:r[2],semanticDark:r[3]}}async fetchProcessedTokens(e={}){if(this.localReader)return this.localReader.fetchProcessedTokens();let t=new q(`${this.baseURL}/api/design-systems/${this.ffId}/processed-tokens`);e.tag&&t.searchParams.append("tag",e.tag),e.version&&t.searchParams.append("version",e.version),e.tailwindV4&&t.searchParams.append("tailwind","v4");let r=t.toString();try{let s=await this.fetchJson(r,{returnHeaders:!0});return s.headers&&(s.data.versionMetadata={version:s.headers["x-frontfriend-version"],tag:s.headers["x-frontfriend-tag"],cached:s.headers["x-frontfriend-cached"]==="true"}),s.data}catch(s){if(s.statusCode===404)return null;throw s.statusCode===400?new y("Design system not synced with Figma. Please sync tokens from Figma before using this design system.",400,r):s}}async sendJson(e,t,r){let s=await this.getAuthToken();if(!s)throw new y(P,401,e);return new Promise((i,o)=>{let l=new q(e),c=e.startsWith("https")?J:N,a=JSON.stringify(r||{}),d=c.request({hostname:l.hostname,port:l.port,path:`${l.pathname}${l.search}`,method:t,headers:{"content-type":"application/json","content-length":Buffer.byteLength(a),...s?{authorization:`Bearer ${s}`}:{}}},h=>{let g="";h.on("data",f=>{g+=f}),h.on("end",()=>{if(h.statusCode>=300){o(Ce(h,e,g));return}if(!g){i(null);return}try{i(JSON.parse(g))}catch(f){if(V(h,g)){o(new y(P,h.statusCode,e));return}o(new y(`Invalid JSON response: ${f.message}`,h.statusCode,e))}})});d.on("error",h=>{o(new y(`Network error: ${h.message}`,0,e))}),d.write(a),d.end()})}async pushComponentsConfig(e,t,r,s={}){let i=`${this.baseURL}/api/design-systems/${this.ffId}/components-config`,o={componentsConfig:e};return typeof t=="string"&&t.length>0&&(o.customCss=t),typeof r=="string"&&r.length>0&&(o.iconLibrary=r),s.themeStudioRecipe&&typeof s.themeStudioRecipe=="object"&&(o.themeStudioRecipe=s.themeStudioRecipe),this.sendJson(i,"PUT",o)}async migrateComponents(e){let t=`${this.baseURL}/api/design-systems/${this.ffId}/migrate`;return this.sendJson(t,"POST",e)}async listMigrationSnapshots(){let e=`${this.baseURL}/api/design-systems/${this.ffId}/migrate/snapshots`;return this.fetchJson(e)}async getMigrationSnapshot(e){let t=`${this.baseURL}/api/design-systems/${this.ffId}/migrate/snapshots/${e}`;return this.fetchJson(t)}async fetchComponentsConfig(){if(this.localReader)return this.localReader.fetchComponentsConfig();let e=`${U}/${this.ffId}/components-config.json`;try{return await this.fetchJson(e)}catch(t){if(t.statusCode===404)return null;throw t}}async fetchFonts(){if(this.localReader)return this.localReader.fetchFonts();let e=`${U}/${this.ffId}/font.json`;try{return await this.fetchJson(e)}catch(t){if(t.statusCode===404)return null;throw t}}async fetchIcons(){if(this.localReader)return this.localReader.fetchIcons();let e=`${U}/${this.ffId}/icons.json`;try{return await this.fetchJson(e)}catch(t){if(t.statusCode===404)return null;throw t}}async fetchVersion(){if(this.localReader)return this.localReader.fetchVersion();let e=`${U}/${this.ffId}/version.json`;try{return await this.fetchJson(e)}catch(t){if(t.statusCode===404)return null;throw t}}async fetchCustomCss(){if(this.localReader)return this.localReader.fetchCustomCss();let e=`${U}/${this.ffId}/custom.css`;try{return await this.fetchRaw(e)}catch(t){if(t.statusCode===404)return null;throw t}}async fetchPlanMetadata(){if(this.localReader)return process.env.FF_DEBUG&&console.log(" \u2192 Using local reader for plan metadata"),this.localReader.fetchPlanMetadata();let e=`${this.baseURL}/api/design-systems/${this.ffId}/processed-tokens`;try{process.env.FF_DEBUG&&console.log(` \u2192 Fetching plan metadata from saas app: ${e}`);let t=await this.fetchJson(e);if(t&&t.metadata){let r={planType:t.metadata.planType||"full",allowedComponents:t.metadata.allowedComponents||null};return process.env.FF_DEBUG&&console.log(` \u2192 Found plan metadata in saas app: ${r.planType}, allowed: ${r.allowedComponents?r.allowedComponents.length+" components":"all"}`),r}}catch(t){if(t.statusCode===404){process.env.FF_DEBUG&&console.log(" \u2192 ff-id not found in saas app, trying legacy server");let r=`${U}/${this.ffId}/metadata`;try{process.env.FF_DEBUG&&console.log(` \u2192 Fetching from legacy server: ${r}`);let s=await this.fetchJson(r),i={planType:s.planType||"full",allowedComponents:s.allowedComponents||null};return process.env.FF_DEBUG&&console.log(` \u2192 Found in legacy server: ${i.planType}, allowed: ${i.allowedComponents?i.allowedComponents.length+" components":"all"}`),i}catch(s){if(s.statusCode===404)return process.env.FF_DEBUG&&console.log(" \u2192 ff-id not found in any server - access denied"),{planType:"denied",allowedComponents:[],error:"Design system not found. Please ensure your ff-id is valid."};throw s}}throw t}}};$e.exports={APIClient:H}});var ze=require("https"),Ke=require("http"),m=require("fs"),u=require("path"),{URL:W}=require("url"),Ee=require("os"),Qe="https://registry.frontfriend.dev",Xe=["legacy","v4"],Y=class{constructor(e={}){this.appRoot=e.appRoot||process.cwd(),this.framework=e.framework||this.detectFramework(),this.config=e.config||{},this.registryGeneration=this.resolveRegistryGeneration(e.registryGeneration),this.outputPath=e.outputPath||this.getDefaultOutputPath(),this.overwrite=e.overwrite||!1,this.downloadedComponents=new Set,this.downloadingComponents=new Set,this.ffId=this.config.ffId||this.config["ff-id"]||null,this.registryUrl=this.resolveRegistryUrl(e.registryUrl),this.planMetadata=null}resolveRegistryUrl(e){let t=this.config.registry,r=this.config.registryUrl||this.config.registryBaseUrl||(typeof t=="object"?t.url||t.baseUrl:void 0);return(e||r||process.env.FF_REGISTRY_URL||Qe).replace(/\/+$/,"")}resolveRegistryGeneration(e){let t=this.config.registry,r=this.config.registryGeneration||this.config.registryVersion||(typeof t=="string"?t:(t==null?void 0:t.generation)||(t==null?void 0:t.version));return this.normalizeRegistryGeneration(e||r||(this.detectTailwindV4()?"v4":"legacy"))}normalizeRegistryGeneration(e){if(e==null||e==="")return"legacy";if(e==="v4"||e===4||e==="4")return"v4";if(e==="legacy")return"legacy";throw new Error(`Unsupported registry generation "${e}". Supported generations: ${Xe.join(", ")}.`)}readPackageJson(){let e=u.join(this.appRoot,"package.json");if(!m.existsSync(e))return null;try{return JSON.parse(m.readFileSync(e,"utf8"))}catch{return null}}getDependencyVersion(e){var r,s,i;let t=this.readPackageJson();return((r=t==null?void 0:t.dependencies)==null?void 0:r[e])||((s=t==null?void 0:t.devDependencies)==null?void 0:s[e])||((i=t==null?void 0:t.peerDependencies)==null?void 0:i[e])||null}getDependencyMajorVersion(e){var s;let t=this.getDependencyVersion(e),r=(s=t==null?void 0:t.match(/(\d+)/))==null?void 0:s[1];return r?Number(r):null}validateRuntimeCompatibility(){if(this.framework!=="react")return;let e=this.getDependencyVersion("react"),t=this.getDependencyMajorVersion("react");if(!(!e||!t)){if(this.registryGeneration==="v4"&&t<19)throw new Error(`Tailwind v4 React registry requires React 19; found react ${e}`);if(this.registryGeneration==="legacy"&&t>=19)throw new Error(`Legacy React registry targets React 18; found react ${e}. Use --registry v4 for React 19 projects.`)}}detectTailwindV4(){let e=this.getDependencyMajorVersion("tailwindcss");return e?e>=4:!1}getRegistryUrl(e,{custom:t=!1,index:r=!1}={}){let s=r?"index.json":`${e}.json`;return t?this.registryGeneration==="v4"?`${this.registryUrl}/custom/v4/${this.ffId}/${this.framework}/${s}`:`${this.registryUrl}/custom/${this.ffId}/${this.framework}/${s}`:this.registryGeneration==="v4"?`${this.registryUrl}/components/v4/${this.framework}/${s}`:`${this.registryUrl}/components/${this.framework}/${s}`}getRegistryDependencyName(e){if(typeof e!="string"||!/^[a-z][a-z0-9+.-]*:\/\//i.test(e))return e;try{let t=new W(e),r=new W(this.registryUrl);if(t.origin!==r.origin)throw new Error(`Unsupported registry dependency URL "${e}". Only Frontfriend registry URLs are supported by this downloader.`);let s=t.pathname.split("/").filter(Boolean).map(a=>decodeURIComponent(a)),i,o,l,c;if(s[0]!=="r")throw new Error(`Unsupported registry dependency URL "${e}". Expected a Frontfriend /r/{generation}/{framework}/{component}.json route.`);if(s[2]==="custom"?[,i,,l,o,c]=s:[,i,o,c]=s,!i||!o||!c||!c.endsWith(".json"))throw new Error(`Unsupported registry dependency URL "${e}". Expected a Frontfriend /r/{generation}/{framework}/{component}.json route.`);if(i!==this.registryGeneration)throw new Error(`Registry dependency URL "${e}" targets generation "${i}" but downloader is using "${this.registryGeneration}".`);if(o!==this.framework)throw new Error(`Registry dependency URL "${e}" targets framework "${o}" but downloader is using "${this.framework}".`);if(l&&l!==this.ffId)throw new Error(`Registry dependency URL "${e}" targets ff-id "${l}" but downloader is using "${this.ffId||"none"}".`);return c.endsWith(".json")?c.slice(0,-5):c}catch(t){if(t.message.startsWith("Unsupported registry dependency URL")||t.message.startsWith("Registry dependency URL"))throw t;return e}}detectFramework(){let e=u.join(this.appRoot,"package.json");if(m.existsSync(e)){let t=JSON.parse(m.readFileSync(e,"utf8")),r={...t.dependencies,...t.devDependencies};if(r.vue)return"vue";if(r.react)return"react"}return"react"}getDefaultOutputPath(){if(this.config.aliases&&this.config.aliases.ui){let t=this.config.aliases.ui.replace(/^@\//,"");return u.join(this.appRoot,t)}return this.framework==="vue"?u.join(this.appRoot,"src/components/ui"):u.join(this.appRoot,"components/ui")}fetchJson(e){let t=process.env.FF_USE_LOCAL==="true",r=process.env.FF_USE_LOCAL_REGISTRY==="true",s=e.includes(this.registryUrl);if(t||r&&s){process.env.FF_DEBUG&&r&&!t&&console.log(" \u2192 Using local registry for components");let i=["../../packages/registry","../../../packages/registry","../../../../packages/registry",u.join(__dirname,"../../../registry"),"packages/registry"],o;for(let a of i){let d=u.isAbsolute(a)?a:u.join(process.cwd(),a);if(m.existsSync(d)){o=d;break}}if(!o)return Promise.reject(new Error(`Local registry not found. Tried paths: ${i.join(", ")}`));let l=e.replace(this.registryUrl,"").replace(/^\//,""),c=u.join(o,...l.split("/"));try{let a=m.readFileSync(c,"utf8");return Promise.resolve(JSON.parse(a))}catch(a){return a.code==="ENOENT"?Promise.reject(new Error("HTTP 404: Not Found")):Promise.reject(a)}}return new Promise((i,o)=>{(new W(e).protocol==="http:"?Ke:ze).get(e,c=>{let a="";c.on("data",d=>{a+=d}),c.on("end",()=>{if(c.statusCode>=400){let d=c.statusMessage;try{let h=JSON.parse(a);d=h.error||h.message||d}catch{}o(new Error(`HTTP ${c.statusCode}: ${d}`));return}try{i(JSON.parse(a))}catch(d){o(new Error(`Invalid JSON response: ${d.message}`))}})}).on("error",c=>{o(c)})})}async getAllComponents(){var e;this.validateRuntimeCompatibility();try{let t=this.getRegistryUrl(null,{index:!0}),s=(await this.fetchJson(t)).components||[];if(this.ffId){try{await this.isComponentAllowed("dummy")}catch{if(this.planMetadata&&this.planMetadata.planType==="denied")throw new Error(this.planMetadata.error||"Access denied. Your ff-id was not found in any server.")}this.planMetadata&&this.planMetadata.planType==="trial"&&this.planMetadata.allowedComponents&&(s=s.filter(i=>this.planMetadata.allowedComponents.includes(i)),process.env.FF_DEBUG&&console.log(` \u2192 Trial plan: filtering to ${s.length} allowed components`))}return s}catch(t){throw t.message.includes("404")&&this.registryGeneration==="v4"?new Error(`Tailwind v4 registry not available for ${this.framework}`):((e=this.planMetadata)==null?void 0:e.planType)==="denied"||t.message.startsWith("Access denied.")?t:new Error(`Failed to fetch component index: ${t.message}`)}}async getCustomComponents(){if(!this.ffId)return process.env.FF_DEBUG&&console.log(" \u2192 No ff-id found, skipping custom components"),[];this.validateRuntimeCompatibility();try{let e=this.getRegistryUrl(null,{custom:!0,index:!0});process.env.FF_DEBUG&&console.log(` \u2192 Fetching custom components from: ${e}`);let t=await this.fetchJson(e);return process.env.FF_DEBUG&&console.log(` \u2192 Found custom components: ${JSON.stringify(t.components)}`),t.components||[]}catch(e){if(!e.message.includes("404"))throw new Error(`Failed to fetch custom component index: ${e.message}`);return process.env.FF_DEBUG&&console.log(` \u2192 No custom components found: ${e.message}`),[]}}async downloadComponents(e){process.env.FF_DEBUG&&console.log(` \u2192 ComponentDownloader initialized with ff-id: ${this.ffId}`);let t={successful:[],failed:[],dependencies:new Set};for(let r of e)try{let s=await this.downloadComponent(r);t.successful.push(r),s.forEach(i=>t.dependencies.add(i))}catch(s){t.failed.push({component:r,error:s.message})}return{successful:t.successful,failed:t.failed,dependencies:Array.from(t.dependencies)}}async isComponentAllowed(e){if(!this.planMetadata&&this.ffId){let{APIClient:t}=Fe(),r=new t(this.ffId,{baseURL:this.config["api-url"]||this.config.apiUrl});try{this.planMetadata=await r.fetchPlanMetadata(),process.env.FF_DEBUG&&console.log(` \u2192 Fetched plan metadata: ${this.planMetadata.planType}, allowed: ${this.planMetadata.allowedComponents?this.planMetadata.allowedComponents.length+" components":"all"}`)}catch(s){console.warn("Failed to fetch plan metadata:",s.message),this.planMetadata={planType:"full",allowedComponents:null}}}if(this.planMetadata&&this.planMetadata.planType==="denied")throw new Error(this.planMetadata.error||"Access denied. Your ff-id was not found in any server.");return!this.planMetadata||this.planMetadata.planType==="full"?!0:this.planMetadata.planType==="trial"&&this.planMetadata.allowedComponents?this.planMetadata.allowedComponents.includes(e):!1}async downloadComponent(e){if(this.validateRuntimeCompatibility(),this.downloadedComponents.has(e))return[];if(this.downloadingComponents.has(e))throw new Error(`Circular registry dependency detected while downloading "${e}"`);this.downloadingComponents.add(e);try{return await this.downloadComponentInternal(e)}finally{this.downloadingComponents.delete(e)}}async downloadComponentInternal(e){if(process.env.FF_DEBUG&&(console.log(` \u2192 Checking if component "${e}" is allowed...`),console.log(` \u2192 FF-ID: ${this.ffId}`)),!await this.isComponentAllowed(e)){let c=`Component "${e}" is not available in your trial plan. Please upgrade to access all components.`;throw console.error(`\u274C ${c}`),new Error(c)}console.log(`\u{1F4E5} Downloading ${e}...`);let r,s=!1;if(this.ffId){let c=this.getRegistryUrl(e,{custom:!0});try{r=await this.fetchJson(c),s=!0,process.env.FF_DEBUG&&console.log(` \u2192 Found custom component for ${e}`)}catch(a){if(!a.message.includes("404"))throw new Error(`Failed to fetch custom component "${e}": ${a.message}`);process.env.FF_DEBUG&&console.log(` \u2192 No custom component found for ${e}, trying standard`)}}if(!r){let c=this.getRegistryUrl(e);try{r=await this.fetchJson(c)}catch(a){throw a.message.includes("404")?this.registryGeneration==="v4"?new Error(`Tailwind v4 component "${e}" not migrated for ${this.framework}`):new Error(`Component "${e}" not found`):new Error(`Failed to fetch ${e}: ${a.message}`)}}if(!r.files||!Array.isArray(r.files))throw new Error(`Invalid component data for ${e}`);let i=[...r.dependencies||[]],o=(r.registryDependencies||[]).map(c=>{try{return{dep:c,dependencyName:this.getRegistryDependencyName(c)}}catch(a){throw this.registryGeneration==="v4"?new Error(`Failed to download required v4 registry dependency "${c}" for "${e}": ${a.message}`):a}}),l=async()=>{if(o.length>0){console.log(` \u{1F4E6} Installing registry dependencies: ${o.map(({dep:c})=>c).join(", ")}`);for(let{dep:c,dependencyName:a}of o)try{(await this.downloadComponent(a)).forEach(h=>i.push(h))}catch(d){if(this.registryGeneration==="v4")throw new Error(`Failed to download required v4 registry dependency "${c}" for "${e}": ${d.message}`);console.warn(` \u26A0\uFE0F Failed to download dependency ${c}: ${d.message}`)}}};this.registryGeneration==="v4"&&await l();for(let c of r.files)await this.writeComponentFile(c);return this.downloadedComponents.add(e),console.log(` \u2713 ${e} downloaded${s&&process.env.FF_DEBUG?" (custom)":""}`),this.registryGeneration!=="v4"&&await l(),i}async writeComponentFile(e){var d,h,g;let t=e.target||e.name||e.path;if(!t)throw new Error("Invalid component file descriptor: missing name/path/target");let r=f=>f.split("/").join(u.sep),s=(f,w,z)=>{let _=u.resolve(f),v=u.resolve(_,r(w)),T=u.relative(_,v);if(T===""||T.startsWith("..")||u.isAbsolute(T))throw new Error(`Unsafe component file path "${t}": resolves outside ${z}`);return v},i,o=(d=e.target)==null?void 0:d.match(/^@([^/]+)\//),l=f=>{var Q;if(f==="ui")return u.resolve(this.outputPath);let v=(((Q=this.config.aliases)==null?void 0:Q[f])||{components:"components",hooks:"hooks",lib:"lib"}[f]||f).replace(/^@\//,"").replace(/^~\//,""),T=u.resolve(this.appRoot),K=u.isAbsolute(v)?u.resolve(v):u.resolve(T,r(v)),S=u.relative(T,K);if(S===""||S.startsWith("..")||u.isAbsolute(S))throw new Error(`Unsafe component file path "${t}": alias @${f} resolves outside app root`);return K};if((h=e.target)!=null&&h.startsWith("@ui/"))i=s(this.outputPath,e.target.replace(/^@ui\//,""),"output root");else if(o){let f=o[1],w=l(f);i=s(w,e.target.replace(new RegExp(`^@${f}/`),""),`@${f} alias root`)}else(g=e.target)!=null&&g.startsWith("~/")?i=s(this.appRoot,e.target.replace(/^~\//,""),"app root"):e.target?i=s(this.appRoot,e.target.replace(/^\/+/,""),"app root"):i=s(this.outputPath,t,"output root");let c=u.dirname(i);if(m.existsSync(c)||m.mkdirSync(c,{recursive:!0}),m.existsSync(i)&&!this.overwrite){console.log(` \u26A0\uFE0F Skipping ${t} (already exists)`);return}let a=e.content;Ee.EOL!==`
|
|
3
|
-
`&&(a=e.content.replace(/\n/g,
|
|
1
|
+
var R=(n,e)=>()=>(e||n((e={exports:{}}).exports,e),e.exports);var T=R((st,X)=>{var Ee="https://app.dev.frontfriend.dev",ve="https://registry-legacy.frontfriend.dev",ke=".cache/frontfriend",Ue={JS:["tokens.js","variables.js","semanticVariables.js","semanticDarkVariables.js","safelist.js","custom.js"],JSON:["fonts.json","icons.json","components-config.json","version.json","versionMetadata.json","metadata.json"]},je={FF_ID:"FF_ID",FF_API_URL:"FF_API_URL",FF_USE_LOCAL:"FF_USE_LOCAL",FF_AUTH_TOKEN:"FF_AUTH_TOKEN"};function Te(n){if(!n)return null;try{let e=new URL(n);return e.hostname.startsWith("app.")?`${e.protocol}//${e.hostname.replace(/^app\./,"registry.")}`:null}catch{return null}}X.exports={DEFAULT_API_URL:Ee,LEGACY_API_URL:ve,CACHE_DIR_NAME:ke,CACHE_TTL_DAYS:7,CACHE_TTL_MS:6048e5,CACHE_FILES:Ue,ENV_VARS:je,deriveRegistryFromApiUrl:Te}});var ee=R((rt,Z)=>{var D=class extends Error{constructor(e,t,r){super(e),this.name="APIError",this.statusCode=t,this.url=r,this.code=`API_${t}`}},L=class extends Error{constructor(e,t){super(e),this.name="CacheError",this.operation=t,this.code=`CACHE_${t.toUpperCase()}`}},I=class extends Error{constructor(e,t){super(e),this.name="ConfigError",this.field=t,this.code=t?`CONFIG_${String(t).toUpperCase()}`:"CONFIG_ERROR"}},b=class extends Error{constructor(e,t){super(e),this.name="ProcessingError",this.token=t,this.code="PROCESSING_ERROR"}};Z.exports={APIError:D,CacheError:L,ConfigError:I,ProcessingError:b}});var M=R((nt,te)=>{var $=require("fs");function Pe(n){try{let e=$.readFileSync(n,"utf8");return JSON.parse(e)}catch(e){if(e.code==="ENOENT")return null;throw e}}function Se(n){try{return $.readFileSync(n,"utf8").replace(/^\uFEFF/,"")}catch(e){if(e.code==="ENOENT")return null;throw e}}function _e(n,e,t=2){let r=JSON.stringify(e,null,t);$.writeFileSync(n,r,"utf8")}function Ae(n,e){$.writeFileSync(n,e,"utf8")}function De(n,e){if(e==null||typeof e=="object"&&Object.keys(e).length===0||Array.isArray(e)&&e.length===0){let r=`module.exports = ${Array.isArray(e)?"[]":"{}"};`;$.writeFileSync(n,r,"utf8")}else{let t=`module.exports = ${JSON.stringify(e,null,2)};`;$.writeFileSync(n,t,"utf8")}}function Le(n){return $.existsSync(n)}function Ie(n){$.mkdirSync(n,{recursive:!0})}function be(n){$.rmSync(n,{recursive:!0,force:!0})}te.exports={readJsonFileSafe:Pe,readFileSafe:Se,writeJsonFile:_e,writeTextFile:Ae,writeModuleExportsFile:De,fileExists:Le,ensureDirectoryExists:Ie,removeDirectory:be}});var re=R((ot,se)=>{var C=require("path"),{fileExists:x}=M();function Me(n,e=process.cwd()){let t=e;for(;t!==C.parse(t).root;){let r=C.join(t,n);if(x(r))return r;t=C.dirname(t)}return null}function xe(n,e=process.cwd(),t=null){let r=[],s=e,i=10;for(let o=0;o<i;o++){r.push(C.join(s,n)),o<3&&r.push(C.join(s,"../".repeat(o+1)+n));let l=C.dirname(s);if(l===s)break;s=l}r.push(C.join(__dirname,"../../../",n));for(let o of r)if(x(o)&&(!t||t(o)))return o;return null}function Ge(n,e=process.cwd()){let t=n.map(r=>C.isAbsolute(r)?r:C.join(e,r));for(let r of t)if(x(r))return r;return null}se.exports={findFileUpward:Me,findDirectoryUpward:xe,findDirectoryWithPaths:Ge}});var ae=R((it,ie)=>{var p=require("path"),{findDirectoryUpward:ne}=re(),{readJsonFileSafe:F,readFileSafe:oe,fileExists:G}=M(),O=class{constructor(e,t={}){if(this.ffId=e,this.tokensBasePath=t.tokensBasePath||this.findTokensPath(),this.folderMap=this.loadFolderMap(),this.projectFolder=this.folderMap[e],!this.projectFolder)throw new Error(`No folder mapping found for ff-id: ${e}`);this.projectPath=p.join(this.tokensBasePath,this.projectFolder)}findTokensPath(){let e=ne("apps/tokens",process.cwd(),t=>G(p.join(t,"folderMap.json")));if(e||(e=ne("tokens",process.cwd(),t=>G(p.join(t,"folderMap.json")))),!e)throw new Error("Could not find tokens directory with folderMap.json");return e}loadFolderMap(){let e=p.join(this.tokensBasePath,"folderMap.json"),t=F(e);if(!t)throw new Error("Failed to load folderMap.json: File not found");return t}async fetchTokens(){let e=p.join(this.projectPath,"figma"),t={global:[p.join(e,"Global Tokens","Default.json"),p.join(e,"global.json")],colors:[p.join(e,"Global Colors","Default.json"),p.join(e,"Global Colors","Light.json")],semantic:[p.join(e,"Semantic","Light.json"),p.join(e,"Semantic","Default.json")],semanticDark:[p.join(e,"Semantic","Dark.json")]},r={};for(let[s,i]of Object.entries(t)){for(let o of i){let l=F(o);if(l!==null){r[s]=l;break}}r[s]||(r[s]=null)}return r}async fetchProcessedTokens(){let e=p.join(this.projectPath,"cache");if(!G(e))return null;let t=o=>{if(o===null)return null;let l={},c={exports:l};return new Function("module","exports",o)(c,l),c.exports},r={tokens:{fileName:"hashedTokens.js",type:"module"},variables:{fileName:"hashedVariables.js",type:"module"},semanticVariables:{fileName:"hashedSemanticVariables.js",type:"module"},semanticDarkVariables:{fileName:"hashedSemanticDarkVariables.js",type:"module"},safelist:{fileName:"safelist.js",type:"module"},custom:{fileName:"custom.js",type:"module"},fonts:{fileName:"fonts.json",type:"json"},icons:{fileName:"icons.json",type:"json"},componentsConfig:{fileName:"encoded-config.json",type:"json"},version:{fileName:"version.json",type:"json"}},s={};for(let[o,{fileName:l,type:c}]of Object.entries(r)){let a=p.join(e,l),d=c==="module"?t(oe(a)):F(a);d!==null&&(s[o]=d)}return Object.values(s).some(o=>o!==null)?s:null}async fetchComponentsConfig(){return F(p.join(this.projectPath,"components-config.json"))}async fetchFonts(){return F(p.join(this.projectPath,"font.json"))}async fetchIcons(){return F(p.join(this.projectPath,"icons.json"))}async fetchVersion(){return F(p.join(this.projectPath,"version.json"))}async fetchCustomCss(){return oe(p.join(this.projectPath,"custom.css"))}async fetchPlanMetadata(){return F(p.join(this.projectPath,"metadata.json"))||{planType:"full",allowedComponents:null}}fetchJson(){throw new Error("fetchJson is not implemented in LocalTokenReader. Use specific fetch methods.")}fetchRaw(){throw new Error("fetchRaw is not implemented in LocalTokenReader. Use specific fetch methods.")}};ie.exports={LocalTokenReader:O}});var me=R((at,ye)=>{var E=require("fs"),ce=require("path"),Oe=require("os"),Je=require("https"),Ne=require("http"),{URL:qe}=require("url"),{DEFAULT_API_URL:le}=T(),de=ce.join(Oe.homedir(),".frontfriend"),v=ce.join(de,"credentials");function Be(){return v}function he(){try{return E.existsSync(v)?JSON.parse(E.readFileSync(v,"utf8")):null}catch{return null}}function fe(n){E.mkdirSync(de,{recursive:!0,mode:448}),E.writeFileSync(v,`${JSON.stringify(n,null,2)}
|
|
2
|
+
`,{mode:384});try{E.chmodSync(v,384)}catch{}}function Ve(){return E.existsSync(v)?(E.unlinkSync(v),!0):!1}function ue(n,e=60*1e3){return!(n!=null&&n.accessToken)||!n.expiresAt||Date.now()+e>=n.expiresAt}function P(n){let e=new Error(`${n}. Run \`frontfriend login\` to re-authenticate.`);return e.code="FF_AUTH_REFRESH_FAILED",e}function pe(n,e,t){return new Promise((r,s)=>{let i=new qe(n),o=i.protocol==="https:"?Je:Ne,l=JSON.stringify(e||{}),c=o.request({hostname:i.hostname,port:i.port,path:`${i.pathname}${i.search}`,method:"POST",headers:{"content-type":"application/json","content-length":Buffer.byteLength(l),...t?{authorization:`Bearer ${t}`}:{}}},a=>{let d="";a.on("data",h=>{d+=h}),a.on("end",()=>{if(a.statusCode>=300&&a.statusCode<400){let g=a.headers.location?` to ${a.headers.location}`:"",f=new Error(`HTTP ${a.statusCode}: redirected${g}`);f.statusCode=a.statusCode,f.body=d,s(f);return}let h=null;try{h=d?JSON.parse(d):null}catch{let f=a.headers["content-type"]||"unknown content-type",w=new Error(`Expected JSON from FrontFriend API at ${n}, but received ${f} (HTTP ${a.statusCode}). Check FF_API_URL or pass --api-url with the FrontFriend app URL.`);if(w.statusCode=a.statusCode,w.body=d,a.statusCode>=400){s(w);return}s(w);return}if(a.statusCode>=400){let g=new Error((h==null?void 0:h.error_description)||(h==null?void 0:h.error)||`HTTP ${a.statusCode}`);g.statusCode=a.statusCode,g.body=h,s(g);return}r(h)})});c.on("error",s),c.write(l),c.end()})}async function ge(n,e=le){if(!(n!=null&&n.refreshToken)||!(n!=null&&n.deviceId))throw P("FrontFriend session expired");let t;try{t=await pe(`${e}/api/plugin/token`,{grant_type:"refresh_token",refresh_token:n.refreshToken,device_id:n.deviceId})}catch(s){throw P((s==null?void 0:s.message)||"Could not refresh FrontFriend session")}if(!(t!=null&&t.access_token)||!(t!=null&&t.refresh_token))throw P("Could not refresh FrontFriend session");let r={accessToken:t.access_token,refreshToken:t.refresh_token,deviceId:t.device_id||n.deviceId,expiresAt:Date.now()+(t.expires_in||900)*1e3,refreshExpiresAt:Date.now()+(t.refresh_expires_in||604800)*1e3,ffApiUrl:e};return fe(r),r}async function He(n={}){if(!n.skipCredentials){let e=he();if(e!=null&&e.accessToken||e!=null&&e.refreshToken){let t=n.baseURL||e.ffApiUrl||le;return e.accessToken&&!ue(e)?e.accessToken:(await ge(e,t)).accessToken}}return n.envToken?n.envToken:n.legacyToken?(n.silent||console.warn("\u26A0\uFE0F auth-token in frontfriend.config.js is deprecated. Run `frontfriend login` to store credentials outside the repo."),n.legacyToken):null}ye.exports={getCredentialsPath:Be,readCredentials:he,writeCredentials:fe,deleteCredentials:Ve,isExpired:ue,postJson:pe,refreshCredentials:ge,resolveAuthToken:He,createAuthRefreshError:P}});var Fe=R((ct,Ce)=>{var J=require("https"),N=require("http"),{URL:B}=require("url"),{DEFAULT_API_URL:We,LEGACY_API_URL:k,ENV_VARS:q}=T(),{APIError:y}=ee(),{LocalTokenReader:Ye}=ae(),{resolveAuthToken:ze}=me(),S="Authentication required. Run `frontfriend login` to re-authenticate.";function we(n,e,t,r){return t?n.get(new B(e),{headers:{authorization:`Bearer ${t}`}},r):n.get(e,r)}function V(n,e=""){var r;let t=((r=n.headers)==null?void 0:r.location)||"";return n.statusCode===401||n.statusCode===403||n.statusCode>=300&&n.statusCode<400&&t.includes("/sign-in")||String(e).trim().startsWith("/sign-in")}function $e(n,e,t=""){if(V(n,t))return new y(S,n.statusCode,e);let r=n.statusMessage;try{let s=t?JSON.parse(t):null;s!=null&&s.error&&(r=s.error);let i=[...Array.isArray(s==null?void 0:s.cssErrors)?s.cssErrors.slice(0,5).map(o=>`${o.property}: ${o.className}`):[],...Array.isArray(s==null?void 0:s.schemaErrors)?s.schemaErrors.slice(0,5):[]];i.length>0&&(r=`${r} (${i.join("; ")})`)}catch{}return new y(`HTTP ${n.statusCode}: ${r}`,n.statusCode,e)}var H=class{constructor(e,t={}){this.ffId=e,this.baseURL=t.baseURL||process.env[q.FF_API_URL]||We,this.legacyAuthToken=t.authToken||null,this.authToken=null,this.skipCredentials=t.skipCredentials||!1,process.env[q.FF_USE_LOCAL]==="true"&&(console.log(" \u{1F3E0} Using local token reader"),this.localReader=new Ye(e,t))}async getAuthToken(){return this.authToken=await ze({baseURL:this.baseURL,envToken:process.env[q.FF_AUTH_TOKEN],legacyToken:this.legacyAuthToken,skipCredentials:this.skipCredentials}),this.authToken}async fetchJson(e,t={}){let r=await this.getAuthToken();return new Promise((s,i)=>{let o=e.startsWith("https")?J:N;we(o,e,r,l=>{let c="";if(l.statusCode>=300){i($e(l,e));return}l.on("data",a=>{c+=a}),l.on("end",()=>{try{let a=JSON.parse(c);t.returnHeaders?s({data:a,headers:l.headers}):s(a)}catch(a){if(V(l,c)){i(new y(S,l.statusCode,e));return}i(new y(`Invalid JSON response: ${a.message}`,l.statusCode,e))}}),l.on("error",a=>{i(new y(`Response error: ${a.message}`,l.statusCode,e))})}).on("error",l=>{i(new y(`Network error: ${l.message}`,0,e))})})}async fetchRaw(e){let t=await this.getAuthToken();return new Promise((r,s)=>{let i=e.startsWith("https")?J:N;we(i,e,t,o=>{let l="";if(o.statusCode>=400){s(new y(`HTTP ${o.statusCode}: ${o.statusMessage}`,o.statusCode,e));return}o.on("data",c=>{l+=c}),o.on("end",()=>{r(l)}),o.on("error",c=>{s(new y(`Response error: ${c.message}`,o.statusCode,e))})}).on("error",o=>{s(new y(`Network error: ${o.message}`,0,e))})})}async fetchTokens(){if(this.localReader)return this.localReader.fetchTokens();try{let s=`${this.baseURL}/api/design-systems/${this.ffId}/tokens`,i=await this.fetchJson(s);if(i&&i.tokens){let o=i.tokens;return{global:o["Global Tokens/Default"]||o.global||null,colors:o["Global Colors/Default"]||o.colors||null,semantic:o["Semantic/Light"]||o["semantic/light"]||null,semanticDark:o["Semantic/Dark"]||o["semantic/dark"]||null}}}catch(s){if(s.statusCode===400)throw new y("Design system not synced with Figma. Please sync tokens from Figma before using this design system.",400,`${this.baseURL}/api/design-systems/${this.ffId}/tokens`)}let e=k,t={global:`${e}/${this.ffId}/global-tokens/default.json`,colors:`${e}/${this.ffId}/global-colors/default.json`,semantic:`${e}/${this.ffId}/semantic/light.json`,semanticDark:`${e}/${this.ffId}/semantic/dark.json`},r=await Promise.all([this.fetchJson(t.global).catch(s=>s.statusCode===404?null:Promise.reject(s)),this.fetchJson(t.colors).catch(s=>s.statusCode===404?null:Promise.reject(s)),this.fetchJson(t.semantic).catch(s=>s.statusCode===404?null:Promise.reject(s)),this.fetchJson(t.semanticDark).catch(s=>s.statusCode===404?null:Promise.reject(s))]);return{global:r[0],colors:r[1],semantic:r[2],semanticDark:r[3]}}async fetchProcessedTokens(e={}){if(this.localReader)return this.localReader.fetchProcessedTokens();let t=new B(`${this.baseURL}/api/design-systems/${this.ffId}/processed-tokens`);e.tag&&t.searchParams.append("tag",e.tag),e.version&&t.searchParams.append("version",e.version),e.tailwindV4&&t.searchParams.append("tailwind","v4");let r=t.toString();try{let s=await this.fetchJson(r,{returnHeaders:!0});return s.headers&&(s.data.versionMetadata={version:s.headers["x-frontfriend-version"],tag:s.headers["x-frontfriend-tag"],cached:s.headers["x-frontfriend-cached"]==="true"}),s.data}catch(s){if(s.statusCode===404)return null;throw s.statusCode===400?new y("Design system not synced with Figma. Please sync tokens from Figma before using this design system.",400,r):s}}async sendJson(e,t,r){let s=await this.getAuthToken();if(!s)throw new y(S,401,e);return new Promise((i,o)=>{let l=new B(e),c=e.startsWith("https")?J:N,a=JSON.stringify(r||{}),d=c.request({hostname:l.hostname,port:l.port,path:`${l.pathname}${l.search}`,method:t,headers:{"content-type":"application/json","content-length":Buffer.byteLength(a),...s?{authorization:`Bearer ${s}`}:{}}},h=>{let g="";h.on("data",f=>{g+=f}),h.on("end",()=>{if(h.statusCode>=300){o($e(h,e,g));return}if(!g){i(null);return}try{i(JSON.parse(g))}catch(f){if(V(h,g)){o(new y(S,h.statusCode,e));return}o(new y(`Invalid JSON response: ${f.message}`,h.statusCode,e))}})});d.on("error",h=>{o(new y(`Network error: ${h.message}`,0,e))}),d.write(a),d.end()})}async pushComponentsConfig(e,t,r,s={}){let i=`${this.baseURL}/api/design-systems/${this.ffId}/components-config`,o={componentsConfig:e};return typeof t=="string"&&t.length>0&&(o.customCss=t),typeof r=="string"&&r.length>0&&(o.iconLibrary=r),s.themeStudioRecipe&&typeof s.themeStudioRecipe=="object"&&(o.themeStudioRecipe=s.themeStudioRecipe),this.sendJson(i,"PUT",o)}async migrateComponents(e){let t=`${this.baseURL}/api/design-systems/${this.ffId}/migrate`;return this.sendJson(t,"POST",e)}async upgradeConfig(e={},t={}){let r=new URLSearchParams;t.dryRun&&r.set("dryRun","1"),t.tag&&r.set("tag",t.tag),t.version&&r.set("version",t.version);let s=r.toString(),i=`${this.baseURL}/api/design-systems/${this.ffId}/config/upgrade-v4${s?`?${s}`:""}`;return this.sendJson(i,"POST",e)}async listMigrationSnapshots(){let e=`${this.baseURL}/api/design-systems/${this.ffId}/migrate/snapshots`;return this.fetchJson(e)}async getMigrationSnapshot(e){let t=`${this.baseURL}/api/design-systems/${this.ffId}/migrate/snapshots/${e}`;return this.fetchJson(t)}async fetchComponentsConfig(){if(this.localReader)return this.localReader.fetchComponentsConfig();let e=`${k}/${this.ffId}/components-config.json`;try{return await this.fetchJson(e)}catch(t){if(t.statusCode===404)return null;throw t}}async fetchFonts(){if(this.localReader)return this.localReader.fetchFonts();let e=`${k}/${this.ffId}/font.json`;try{return await this.fetchJson(e)}catch(t){if(t.statusCode===404)return null;throw t}}async fetchIcons(){if(this.localReader)return this.localReader.fetchIcons();let e=`${k}/${this.ffId}/icons.json`;try{return await this.fetchJson(e)}catch(t){if(t.statusCode===404)return null;throw t}}async fetchVersion(){if(this.localReader)return this.localReader.fetchVersion();let e=`${k}/${this.ffId}/version.json`;try{return await this.fetchJson(e)}catch(t){if(t.statusCode===404)return null;throw t}}async fetchCustomCss(){if(this.localReader)return this.localReader.fetchCustomCss();let e=`${k}/${this.ffId}/custom.css`;try{return await this.fetchRaw(e)}catch(t){if(t.statusCode===404)return null;throw t}}async fetchPlanMetadata(){if(this.localReader)return process.env.FF_DEBUG&&console.log(" \u2192 Using local reader for plan metadata"),this.localReader.fetchPlanMetadata();let e=`${this.baseURL}/api/design-systems/${this.ffId}/processed-tokens`;try{process.env.FF_DEBUG&&console.log(` \u2192 Fetching plan metadata from saas app: ${e}`);let t=await this.fetchJson(e);if(t&&t.metadata){let r={planType:t.metadata.planType||"full",allowedComponents:t.metadata.allowedComponents||null};return process.env.FF_DEBUG&&console.log(` \u2192 Found plan metadata in saas app: ${r.planType}, allowed: ${r.allowedComponents?r.allowedComponents.length+" components":"all"}`),r}}catch(t){if(t.statusCode===404){process.env.FF_DEBUG&&console.log(" \u2192 ff-id not found in saas app, trying legacy server");let r=`${k}/${this.ffId}/metadata`;try{process.env.FF_DEBUG&&console.log(` \u2192 Fetching from legacy server: ${r}`);let s=await this.fetchJson(r),i={planType:s.planType||"full",allowedComponents:s.allowedComponents||null};return process.env.FF_DEBUG&&console.log(` \u2192 Found in legacy server: ${i.planType}, allowed: ${i.allowedComponents?i.allowedComponents.length+" components":"all"}`),i}catch(s){if(s.statusCode===404)return process.env.FF_DEBUG&&console.log(" \u2192 ff-id not found in any server - access denied"),{planType:"denied",allowedComponents:[],error:"Design system not found. Please ensure your ff-id is valid."};throw s}}throw t}}};Ce.exports={APIClient:H}});var Ke=require("https"),Qe=require("http"),m=require("fs"),u=require("path"),{URL:W}=require("url"),Re=require("os"),{deriveRegistryFromApiUrl:Xe}=T(),Ze="https://registry.frontfriend.dev",et=["legacy","v4"],Y=class{constructor(e={}){this.appRoot=e.appRoot||process.cwd(),this.framework=e.framework||this.detectFramework(),this.config=e.config||{},this.registryGeneration=this.resolveRegistryGeneration(e.registryGeneration),this.outputPath=e.outputPath||this.getDefaultOutputPath(),this.overwrite=e.overwrite||!1,this.downloadedComponents=new Set,this.downloadingComponents=new Set,this.ffId=this.config.ffId||this.config["ff-id"]||null,this.registryUrl=this.resolveRegistryUrl(e.registryUrl),this.planMetadata=null}resolveRegistryUrl(e){let t=this.config.registry,r=this.config.registryUrl||this.config.registryBaseUrl||(typeof t=="object"?t.url||t.baseUrl:void 0);return(e||r||process.env.FF_REGISTRY_URL||Xe(this.config["api-url"]||this.config.apiUrl)||Ze).replace(/\/+$/,"")}resolveRegistryGeneration(e){let t=this.config.registry,r=this.config.registryGeneration||this.config.registryVersion||(typeof t=="string"?t:(t==null?void 0:t.generation)||(t==null?void 0:t.version));return this.normalizeRegistryGeneration(e||r||(this.detectTailwindV4()?"v4":"legacy"))}normalizeRegistryGeneration(e){if(e==null||e==="")return"legacy";if(e==="v4"||e===4||e==="4")return"v4";if(e==="legacy")return"legacy";throw new Error(`Unsupported registry generation "${e}". Supported generations: ${et.join(", ")}.`)}readPackageJson(){let e=u.join(this.appRoot,"package.json");if(!m.existsSync(e))return null;try{return JSON.parse(m.readFileSync(e,"utf8"))}catch{return null}}getDependencyVersion(e){var r,s,i;let t=this.readPackageJson();return((r=t==null?void 0:t.dependencies)==null?void 0:r[e])||((s=t==null?void 0:t.devDependencies)==null?void 0:s[e])||((i=t==null?void 0:t.peerDependencies)==null?void 0:i[e])||null}getDependencyMajorVersion(e){var s;let t=this.getDependencyVersion(e),r=(s=t==null?void 0:t.match(/(\d+)/))==null?void 0:s[1];return r?Number(r):null}validateRuntimeCompatibility(){if(this.framework!=="react")return;let e=this.getDependencyVersion("react"),t=this.getDependencyMajorVersion("react");if(!(!e||!t)){if(this.registryGeneration==="v4"&&t<19)throw new Error(`Tailwind v4 React registry requires React 19; found react ${e}`);if(this.registryGeneration==="legacy"&&t>=19)throw new Error(`Legacy React registry targets React 18; found react ${e}. Use --registry v4 for React 19 projects.`)}}detectTailwindV4(){let e=this.getDependencyMajorVersion("tailwindcss");return e?e>=4:!1}getRegistryUrl(e,{custom:t=!1,index:r=!1}={}){let s=r?"index.json":`${e}.json`;return t?this.registryGeneration==="v4"?`${this.registryUrl}/custom/v4/${this.ffId}/${this.framework}/${s}`:`${this.registryUrl}/custom/${this.ffId}/${this.framework}/${s}`:this.registryGeneration==="v4"?`${this.registryUrl}/components/v4/${this.framework}/${s}`:`${this.registryUrl}/components/${this.framework}/${s}`}getRegistryDependencyName(e){if(typeof e!="string"||!/^[a-z][a-z0-9+.-]*:\/\//i.test(e))return e;try{let t=new W(e),r=new W(this.registryUrl);if(t.origin!==r.origin)throw new Error(`Unsupported registry dependency URL "${e}". Only Frontfriend registry URLs are supported by this downloader.`);let s=t.pathname.split("/").filter(Boolean).map(a=>decodeURIComponent(a)),i,o,l,c;if(s[0]!=="r")throw new Error(`Unsupported registry dependency URL "${e}". Expected a Frontfriend /r/{generation}/{framework}/{component}.json route.`);if(s[2]==="custom"?[,i,,l,o,c]=s:[,i,o,c]=s,!i||!o||!c||!c.endsWith(".json"))throw new Error(`Unsupported registry dependency URL "${e}". Expected a Frontfriend /r/{generation}/{framework}/{component}.json route.`);if(i!==this.registryGeneration)throw new Error(`Registry dependency URL "${e}" targets generation "${i}" but downloader is using "${this.registryGeneration}".`);if(o!==this.framework)throw new Error(`Registry dependency URL "${e}" targets framework "${o}" but downloader is using "${this.framework}".`);if(l&&l!==this.ffId)throw new Error(`Registry dependency URL "${e}" targets ff-id "${l}" but downloader is using "${this.ffId||"none"}".`);return c.endsWith(".json")?c.slice(0,-5):c}catch(t){if(t.message.startsWith("Unsupported registry dependency URL")||t.message.startsWith("Registry dependency URL"))throw t;return e}}detectFramework(){let e=u.join(this.appRoot,"package.json");if(m.existsSync(e)){let t=JSON.parse(m.readFileSync(e,"utf8")),r={...t.dependencies,...t.devDependencies};if(r.vue)return"vue";if(r.react)return"react"}return"react"}getDefaultOutputPath(){if(this.config.aliases&&this.config.aliases.ui){let t=this.config.aliases.ui.replace(/^@\//,"");return u.join(this.appRoot,t)}return this.framework==="vue"?u.join(this.appRoot,"src/components/ui"):u.join(this.appRoot,"components/ui")}fetchJson(e){let t=process.env.FF_USE_LOCAL==="true",r=process.env.FF_USE_LOCAL_REGISTRY==="true",s=e.includes(this.registryUrl);if(t||r&&s){process.env.FF_DEBUG&&r&&!t&&console.log(" \u2192 Using local registry for components");let i=["../../packages/registry","../../../packages/registry","../../../../packages/registry",u.join(__dirname,"../../../registry"),"packages/registry"],o;for(let a of i){let d=u.isAbsolute(a)?a:u.join(process.cwd(),a);if(m.existsSync(d)){o=d;break}}if(!o)return Promise.reject(new Error(`Local registry not found. Tried paths: ${i.join(", ")}`));let l=e.replace(this.registryUrl,"").replace(/^\//,""),c=u.join(o,...l.split("/"));try{let a=m.readFileSync(c,"utf8");return Promise.resolve(JSON.parse(a))}catch(a){return a.code==="ENOENT"?Promise.reject(new Error("HTTP 404: Not Found")):Promise.reject(a)}}return new Promise((i,o)=>{(new W(e).protocol==="http:"?Qe:Ke).get(e,c=>{let a="";c.on("data",d=>{a+=d}),c.on("end",()=>{if(c.statusCode>=400){let d=c.statusMessage;try{let h=JSON.parse(a);d=h.error||h.message||d}catch{}o(new Error(`HTTP ${c.statusCode}: ${d}`));return}try{i(JSON.parse(a))}catch(d){o(new Error(`Invalid JSON response: ${d.message}`))}})}).on("error",c=>{o(c)})})}async getAllComponents(){var e;this.validateRuntimeCompatibility();try{let t=this.getRegistryUrl(null,{index:!0}),s=(await this.fetchJson(t)).components||[];if(this.ffId){try{await this.isComponentAllowed("dummy")}catch{if(this.planMetadata&&this.planMetadata.planType==="denied")throw new Error(this.planMetadata.error||"Access denied. Your ff-id was not found in any server.")}this.planMetadata&&this.planMetadata.planType==="trial"&&this.planMetadata.allowedComponents&&(s=s.filter(i=>this.planMetadata.allowedComponents.includes(i)),process.env.FF_DEBUG&&console.log(` \u2192 Trial plan: filtering to ${s.length} allowed components`))}return s}catch(t){throw t.message.includes("404")&&this.registryGeneration==="v4"?new Error(`Tailwind v4 registry not available for ${this.framework}`):((e=this.planMetadata)==null?void 0:e.planType)==="denied"||t.message.startsWith("Access denied.")?t:new Error(`Failed to fetch component index: ${t.message}`)}}async getCustomComponents(){if(!this.ffId)return process.env.FF_DEBUG&&console.log(" \u2192 No ff-id found, skipping custom components"),[];this.validateRuntimeCompatibility();let e;try{let r=this.getRegistryUrl(null,{custom:!0,index:!0});process.env.FF_DEBUG&&console.log(` \u2192 Fetching custom components from: ${r}`),e=await this.fetchJson(r)}catch(r){if(!r.message.includes("404"))throw new Error(`Failed to fetch custom component index: ${r.message}`);return process.env.FF_DEBUG&&console.log(` \u2192 No custom components found: ${r.message}`),[]}let t=e.components||[];if(process.env.FF_DEBUG&&console.log(` \u2192 Found custom components: ${JSON.stringify(t)}`),t.length>0&&typeof t[0]=="object"){let r=t.filter(s=>s.status!=="ready");if(r.length>0)throw new Error(`Custom component(s) not promoted to v4: ${r.map(s=>`${s.name} (${s.status})`).join(", ")}. Promote them to custom/v4 before downloading v4 components.`);return t.map(s=>s.name)}return t}async downloadComponents(e){process.env.FF_DEBUG&&console.log(` \u2192 ComponentDownloader initialized with ff-id: ${this.ffId}`);let t={successful:[],failed:[],dependencies:new Set};for(let r of e)try{let s=await this.downloadComponent(r);t.successful.push(r),s.forEach(i=>t.dependencies.add(i))}catch(s){t.failed.push({component:r,error:s.message})}return{successful:t.successful,failed:t.failed,dependencies:Array.from(t.dependencies)}}async isComponentAllowed(e){if(!this.planMetadata&&this.ffId){let{APIClient:t}=Fe(),r=new t(this.ffId,{baseURL:this.config["api-url"]||this.config.apiUrl});try{this.planMetadata=await r.fetchPlanMetadata(),process.env.FF_DEBUG&&console.log(` \u2192 Fetched plan metadata: ${this.planMetadata.planType}, allowed: ${this.planMetadata.allowedComponents?this.planMetadata.allowedComponents.length+" components":"all"}`)}catch(s){console.warn("Failed to fetch plan metadata:",s.message),this.planMetadata={planType:"full",allowedComponents:null}}}if(this.planMetadata&&this.planMetadata.planType==="denied")throw new Error(this.planMetadata.error||"Access denied. Your ff-id was not found in any server.");return!this.planMetadata||this.planMetadata.planType==="full"?!0:this.planMetadata.planType==="trial"&&this.planMetadata.allowedComponents?this.planMetadata.allowedComponents.includes(e):!1}async downloadComponent(e){if(this.validateRuntimeCompatibility(),this.downloadedComponents.has(e))return[];if(this.downloadingComponents.has(e))throw new Error(`Circular registry dependency detected while downloading "${e}"`);this.downloadingComponents.add(e);try{return await this.downloadComponentInternal(e)}finally{this.downloadingComponents.delete(e)}}async downloadComponentInternal(e){if(process.env.FF_DEBUG&&(console.log(` \u2192 Checking if component "${e}" is allowed...`),console.log(` \u2192 FF-ID: ${this.ffId}`)),!await this.isComponentAllowed(e)){let c=`Component "${e}" is not available in your trial plan. Please upgrade to access all components.`;throw console.error(`\u274C ${c}`),new Error(c)}console.log(`\u{1F4E5} Downloading ${e}...`);let r,s=!1;if(this.ffId){let c=this.getRegistryUrl(e,{custom:!0});try{r=await this.fetchJson(c),s=!0,process.env.FF_DEBUG&&console.log(` \u2192 Found custom component for ${e}`)}catch(a){if(!a.message.includes("404"))throw new Error(`Failed to fetch custom component "${e}": ${a.message}`);process.env.FF_DEBUG&&console.log(` \u2192 No custom component found for ${e}, trying standard`)}}if(!r){let c=this.getRegistryUrl(e);try{r=await this.fetchJson(c)}catch(a){throw a.message.includes("404")?this.registryGeneration==="v4"?new Error(`Tailwind v4 component "${e}" not migrated for ${this.framework}`):new Error(`Component "${e}" not found`):new Error(`Failed to fetch ${e}: ${a.message}`)}}if(!r.files||!Array.isArray(r.files))throw new Error(`Invalid component data for ${e}`);let i=[...r.dependencies||[]],o=(r.registryDependencies||[]).map(c=>{try{return{dep:c,dependencyName:this.getRegistryDependencyName(c)}}catch(a){throw this.registryGeneration==="v4"?new Error(`Failed to download required v4 registry dependency "${c}" for "${e}": ${a.message}`):a}}),l=async()=>{if(o.length>0){console.log(` \u{1F4E6} Installing registry dependencies: ${o.map(({dep:c})=>c).join(", ")}`);for(let{dep:c,dependencyName:a}of o)try{(await this.downloadComponent(a)).forEach(h=>i.push(h))}catch(d){if(this.registryGeneration==="v4")throw new Error(`Failed to download required v4 registry dependency "${c}" for "${e}": ${d.message}`);console.warn(` \u26A0\uFE0F Failed to download dependency ${c}: ${d.message}`)}}};this.registryGeneration==="v4"&&await l();for(let c of r.files)await this.writeComponentFile(c);return this.downloadedComponents.add(e),console.log(` \u2713 ${e} downloaded${s&&process.env.FF_DEBUG?" (custom)":""}`),this.registryGeneration!=="v4"&&await l(),i}async writeComponentFile(e){var d,h,g;let t=e.target||e.name||e.path;if(!t)throw new Error("Invalid component file descriptor: missing name/path/target");let r=f=>f.split("/").join(u.sep),s=(f,w,z)=>{let _=u.resolve(f),U=u.resolve(_,r(w)),j=u.relative(_,U);if(j===""||j.startsWith("..")||u.isAbsolute(j))throw new Error(`Unsafe component file path "${t}": resolves outside ${z}`);return U},i,o=(d=e.target)==null?void 0:d.match(/^@([^/]+)\//),l=f=>{var Q;if(f==="ui")return u.resolve(this.outputPath);let U=(((Q=this.config.aliases)==null?void 0:Q[f])||{components:"components",hooks:"hooks",lib:"lib"}[f]||f).replace(/^@\//,"").replace(/^~\//,""),j=u.resolve(this.appRoot),K=u.isAbsolute(U)?u.resolve(U):u.resolve(j,r(U)),A=u.relative(j,K);if(A===""||A.startsWith("..")||u.isAbsolute(A))throw new Error(`Unsafe component file path "${t}": alias @${f} resolves outside app root`);return K};if((h=e.target)!=null&&h.startsWith("@ui/"))i=s(this.outputPath,e.target.replace(/^@ui\//,""),"output root");else if(o){let f=o[1],w=l(f);i=s(w,e.target.replace(new RegExp(`^@${f}/`),""),`@${f} alias root`)}else(g=e.target)!=null&&g.startsWith("~/")?i=s(this.appRoot,e.target.replace(/^~\//,""),"app root"):e.target?i=s(this.appRoot,e.target.replace(/^\/+/,""),"app root"):i=s(this.outputPath,t,"output root");let c=u.dirname(i);if(m.existsSync(c)||m.mkdirSync(c,{recursive:!0}),m.existsSync(i)&&!this.overwrite){console.log(` \u26A0\uFE0F Skipping ${t} (already exists)`);return}let a=e.content;Re.EOL!==`
|
|
3
|
+
`&&(a=e.content.replace(/\n/g,Re.EOL)),m.writeFileSync(i,a,"utf8")}};module.exports={ComponentDownloader:Y};
|
|
4
4
|
//# sourceMappingURL=component-downloader.js.map
|