velloo 0.3.0 → 0.4.0

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.
Files changed (68) hide show
  1. package/canvas/assets/index-CSSyvzzo.css +2 -0
  2. package/canvas/assets/index-MXiDRs_Q.js +29 -0
  3. package/canvas/assets/lucide-all-BSrW0hTo.js +1 -0
  4. package/canvas/index.html +2 -2
  5. package/{chunk-bpeezw7w.js → chunk-09385g5r.js} +1 -1
  6. package/{chunk-52hxv18g.js → chunk-0r80246c.js} +1 -1
  7. package/{chunk-k9219kmj.js → chunk-1tdh4h14.js} +1 -1
  8. package/{chunk-as08hzp3.js → chunk-25j8vgpa.js} +1 -1
  9. package/{chunk-3tmsrvaj.js → chunk-33r6266k.js} +1 -1
  10. package/{chunk-d9p6s05n.js → chunk-38ev3cp3.js} +1 -1
  11. package/chunk-3gbmq4jz.js +2 -0
  12. package/{chunk-9nhw49vv.js → chunk-3kqtny5t.js} +1 -1
  13. package/{chunk-td5z2sm4.js → chunk-46xykc71.js} +1 -1
  14. package/chunk-4rvmpsda.js +3 -0
  15. package/{chunk-7bvvbsqy.js → chunk-5qd2ykvw.js} +1 -1
  16. package/{chunk-49cgk5s2.js → chunk-6qfyem23.js} +1 -1
  17. package/{chunk-y5r2w2z3.js → chunk-6z0kpe65.js} +1 -1
  18. package/{chunk-nycs0z7h.js → chunk-7cktk5qr.js} +1 -1
  19. package/chunk-8g5ghy46.js +5 -0
  20. package/chunk-8krqgzny.js +2 -0
  21. package/{chunk-jx11jvq8.js → chunk-8sfx5x8k.js} +1 -1
  22. package/chunk-8yadb6xz.js +5 -0
  23. package/{chunk-k6n249vb.js → chunk-8yz604tw.js} +1 -1
  24. package/{chunk-dsks52m7.js → chunk-c5zjen06.js} +1 -1
  25. package/{chunk-bvmtq0vn.js → chunk-cdxy4xzs.js} +1 -1
  26. package/chunk-cgzhcvsj.js +2 -0
  27. package/{chunk-k7sehkm8.js → chunk-e56eb38j.js} +1 -1
  28. package/{chunk-5ezzc1h2.js → chunk-egccxmed.js} +5 -5
  29. package/chunk-gsne0ap4.js +2 -0
  30. package/chunk-hzjzmkp6.js +6 -0
  31. package/{chunk-wa6qamwh.js → chunk-jtsn41cs.js} +1 -1
  32. package/{chunk-4nqr089t.js → chunk-k10n8na7.js} +1 -1
  33. package/{chunk-1jzhw3p4.js → chunk-n5acf0s3.js} +1 -1
  34. package/chunk-nc5k1ez4.js +2 -0
  35. package/chunk-njhdn9j0.js +4 -0
  36. package/{chunk-kz4423th.js → chunk-q4d60wr3.js} +1 -1
  37. package/{chunk-9g4tr0r6.js → chunk-rsy246re.js} +1 -1
  38. package/{chunk-wzngjjeb.js → chunk-syg6k8m0.js} +1 -1
  39. package/{chunk-wcqqx6tz.js → chunk-t4zss1tk.js} +1 -1
  40. package/{chunk-n9kcxaj7.js → chunk-t8xk4thc.js} +1 -1
  41. package/{chunk-7k7dpj5y.js → chunk-tkvb3wb8.js} +1 -1
  42. package/{chunk-cxzkkn12.js → chunk-tqwe5tc3.js} +1 -1
  43. package/{chunk-7njj1qmq.js → chunk-v0syg2hx.js} +1 -1
  44. package/chunk-va9v9y5m.js +2 -0
  45. package/{chunk-3g3dqej3.js → chunk-vbe2r4kv.js} +1 -1
  46. package/{chunk-qgwhd4n6.js → chunk-vh4144az.js} +1 -1
  47. package/{chunk-m3czg1tt.js → chunk-y0rwf1ze.js} +1 -1
  48. package/{chunk-maz6z7tz.js → chunk-yjmawrv5.js} +1 -1
  49. package/{chunk-yeh9gb5w.js → chunk-ys3s5gaa.js} +1 -1
  50. package/{chunk-qdnb2t6a.js → chunk-z1r0sc3k.js} +1 -1
  51. package/{chunk-1n5kmsxb.js → chunk-zkn1m8tw.js} +1 -1
  52. package/cli.js +1 -1
  53. package/package.json +1 -1
  54. package/canvas/assets/index-CqXW6xA_.js +0 -29
  55. package/canvas/assets/index-gB0StCrx.css +0 -2
  56. package/canvas/assets/lucide-all-C_CNpsMs.js +0 -1
  57. package/chunk-5wv5a52y.js +0 -2
  58. package/chunk-6nzqhfqq.js +0 -5
  59. package/chunk-6zz9kfbd.js +0 -3
  60. package/chunk-788t8y5x.js +0 -2
  61. package/chunk-9f22qnge.js +0 -2
  62. package/chunk-hfzg98c8.js +0 -4
  63. package/chunk-n9t36h0q.js +0 -2
  64. package/chunk-nr3ej3qf.js +0 -5
  65. package/chunk-t5bb30zy.js +0 -3
  66. package/chunk-tbmkq7wy.js +0 -6
  67. package/chunk-tc4mzzfw.js +0 -2
  68. package/chunk-xyfed1j0.js +0 -2
@@ -0,0 +1,5 @@
1
+ // @bun
2
+ import{ok,err,PROTECTED_SHARES_UNAVAILABLE,PublishDestinationsResponseSchema,LinkResponseSchema,LinkAccessResponseSchema,VersionUploadResponseSchema,PublishedDesignsResponseSchema,TeamsResponseSchema,unreachable,protocolViolation,uploadRaceLost,readFailure,cloudFailure,httpFailureFrom,boardLimitFrom,describeBoardLimit,describeCloudError,cloudJson,cloudFetch}from"./chunk-gsne0ap4.js";import{prettifyError,string,number,boolean,array,object,strictObject,record,literal,AnnotationSchema,CanvasNoteSchema,BoardSchema,ExtensionSchema,ViewportPresetSchema,LibrarySchema,ScreenSchema,SnippetSchema,sanitizeSvgMarkup,ThemeSchema,ViewportSchema}from"./chunk-nte0t7v3.js";import{buildBoardComposite,MAX_CONCURRENT_RENDERS,renderScreen,captureScreenshot}from"./chunk-c4fym47a.js";import{writeJsonAtomic,managedDesignId,localDesignOf,recordedDesignName,orderedBoards,activeBoards,registryForScreen,renderPassForScreen,liveExtensions,LiveBundler,createPublishMount}from"./chunk-egccxmed.js";import{withAssetServer}from"./chunk-0zen7bzb.js";import{checkCloudHealth}from"./chunk-25j8vgpa.js";var VERSION_UPLOAD_TIMEOUT_MS=60000,VERSION_UPLOAD_RETRIES=1;async function listPublishDestinations(opts){let query=new URLSearchParams({folderId:opts.folderId});if(opts.teamId)query.set("teamId",opts.teamId);return cloudFetch(`${opts.baseUrl}/v1/publish-destinations?${query}`,PublishDestinationsResponseSchema,{operation:"listing publish destinations",token:opts.token})}async function uploadLinkBundle(opts){let{baseUrl,token,form}=opts,authorized={authorization:`Bearer ${token}`},createRes=await fetch(`${baseUrl}/v1/links`,{method:"POST",headers:authorized,body:JSON.stringify(opts.link)}).catch((error)=>error);if(!(createRes instanceof Response))return err(unreachable(createRes,{url:baseUrl}));if(createRes.status!==200&&createRes.status!==201)return err(await httpFailureFrom("link creation",createRes));let created=createRes.status===201,createdLink=await cloudJson(createRes,LinkResponseSchema,"link creation");if(!createdLink.ok)return createdLink;let link=createdLink.value;if(opts.link.publishMode==="new"&&!created)return err(protocolViolation("the cloud reused a link even though a new publish destination was requested"));if(opts.link.publishMode==="update"&&created)return await fetch(`${baseUrl}/v1/links/${link.slug}`,{method:"DELETE",headers:authorized}).catch(()=>{}),err(protocolViolation("the cloud created a link even though an existing destination was selected"));if(!created){let accessRes=await fetch(`${baseUrl}/v1/links/${link.slug}/access`,{method:"PUT",headers:{...authorized,"content-type":"application/json"},body:JSON.stringify({visibility:opts.link.visibility,password:opts.link.password??null,passwordExpiresAt:opts.link.password?opts.link.passwordExpiresAt??null:null,expectedVersionId:opts.link.expectedVersionId??null,...opts.link.audience?.length?{audience:opts.link.audience}:{},...opts.link.publicComments!==void 0?{publicComments:opts.link.publicComments}:{}})});if(!accessRes.ok)return err(await httpFailureFrom("privacy update",accessRes));let access=await cloudJson(accessRes,LinkAccessResponseSchema,"privacy update");if(!access.ok)return access;link={...link,...access.value}}if(form.append("publishMode",opts.link.publishMode),opts.link.expectedVersionId)form.append("expectedVersionId",opts.link.expectedVersionId);let attempt=await uploadVersionWithRetry({url:`${baseUrl}/v1/links/${link.slug}/versions`,headers:authorized,form,timeoutMs:opts.uploadTimeoutMs??VERSION_UPLOAD_TIMEOUT_MS,retries:opts.uploadRetries??VERSION_UPLOAD_RETRIES});if(!attempt.ok)return attempt;let upload=attempt.value,uploadRes=upload.response;if(uploadRes.status!==201){let{detail,code}=await readFailure(uploadRes);if(upload.transientFailures>0&&uploadRes.status===409&&/slot changed|already has a version/i.test(detail))return err(uploadRaceLost());if(created)await fetch(`${baseUrl}/v1/links/${link.slug}`,{method:"DELETE",headers:authorized}).catch(()=>{});return err(cloudFailure("upload",uploadRes.status,detail,code))}let parsed=await cloudJson(uploadRes,VersionUploadResponseSchema,"upload");if(!parsed.ok)return parsed;let uploaded=parsed.value;return ok({link:{slug:link.slug,visibility:link.visibility??opts.link.visibility,passwordProtected:link.passwordProtected??opts.link.password!=null,...link.audience!==void 0?{audience:link.audience}:{},...link.publicComments!==void 0?{publicComments:link.publicComments}:{}},created,files:uploaded.files,bytes:uploaded.bytes,shareUrl:uploaded.url.startsWith("http")?uploaded.url:`${baseUrl}${uploaded.url}`,...uploaded.tier!==void 0?{tier:uploaded.tier}:{},...uploaded.history!==void 0?{history:uploaded.history}:{}})}async function uploadVersionWithRetry(opts){let attempts=Math.max(1,Math.floor(opts.retries)+1),transientFailures=0,lastNetworkError="unknown network error",lastTimedOut=!1;for(let attempt=1;attempt<=attempts;attempt+=1){let controller=new AbortController,timedOut=!1,timer=setTimeout(()=>{timedOut=!0,controller.abort()},Math.max(1,opts.timeoutMs));try{let response=await fetch(opts.url,{method:"POST",headers:opts.headers,body:opts.form,signal:controller.signal});if(response.status<500||attempt===attempts)return ok({response,transientFailures});transientFailures+=1,await response.body?.cancel().catch(()=>{})}catch(error){if(transientFailures+=1,lastTimedOut=timedOut,lastNetworkError=error instanceof Error?error.message:String(error),attempt===attempts){let retried=attempts>1?` after ${attempts} attempts`:"";return err(timedOut?unreachable(`timed out after ${formatDuration(opts.timeoutMs)}${retried}`,{url:opts.url,timedOut:!0}):unreachable(`version upload${retried}: ${lastNetworkError}`,{url:opts.url}))}}finally{clearTimeout(timer)}}return err(lastTimedOut?unreachable(`timed out after ${formatDuration(opts.timeoutMs)}`,{url:opts.url,timedOut:!0}):unreachable(`version upload: ${lastNetworkError}`,{url:opts.url}))}function formatDuration(ms){return ms>=1000?`${Math.round(ms/1000)}s`:`${Math.max(1,Math.round(ms))}ms`}var cloudUnhealthy=(detail)=>({kind:"CloudUnhealthy",detail}),noScreens=(root)=>({kind:"NoScreens",root}),noBoardScreens=()=>({kind:"NoBoardScreens"}),bundleInvalid=(detail)=>({kind:"BundleInvalid",detail}),teamNotFound=(requested)=>({kind:"TeamNotFound",requested}),teamAmbiguous=(requested)=>({kind:"TeamAmbiguous",requested}),teamNotPublishable=(requested,reviewer)=>({kind:"TeamNotPublishable",requested,reviewer}),noPublishableTeam=(reviewer)=>({kind:"NoPublishableTeam",reviewer}),teamChoiceRequired=(names)=>({kind:"TeamChoiceRequired",names}),REVIEWERS_CANNOT_PUBLISH="reviewers can view and comment on boards, but not publish them \u2014 to publish, ask an owner or admin to make you a member",FREE_PLAN_OWNER_ONLY="on the Free plan only the organization owner publishes \u2014 ask the owner to renew, or leave the organization to publish on your own";function describePublishError(error){switch(error.kind){case"CloudUnhealthy":return error.detail;case"NoScreens":return`no screens found in ${error.root} \u2014 is this a velloo design folder?`;case"NoBoardScreens":return"the selected boards have no screens.";case"BundleInvalid":return`velloo built an invalid design bundle and did not upload it: ${error.detail}. This is a bug \u2014 please report it.`;case"TeamNotFound":return`no team named or identified by '${error.requested}'`;case"TeamAmbiguous":return`more than one team is named '${error.requested}'; pass its UUID`;case"TeamNotPublishable":return error.reviewer?REVIEWERS_CANNOT_PUBLISH:`you can't publish to '${error.requested}' \u2014 you can only publish to teams you're on`;case"NoPublishableTeam":return error.reviewer?REVIEWERS_CANNOT_PUBLISH:FREE_PLAN_OWNER_ONLY;case"TeamChoiceRequired":return`you can publish to more than one team \u2014 choose one (${error.names.join(", ")}) with --team`;case"HttpFailure":{if(error.status===403&&/protected shares/i.test(error.detail))return`${PROTECTED_SHARES_UNAVAILABLE}. Publish publicly, or upgrade your plan from your cloud's billing page`;let limit=boardLimitFrom(error);if(limit)return`${describeBoardLimit(limit)}. Run \`velloo publish list\` to see them, or upgrade your plan from your cloud's billing page`;return describeCloudError(error)}default:return describeCloudError(error)}}import{execFileSync}from"child_process";import{readFile}from"fs/promises";import{join}from"path";var DESIGN_BUNDLE_FORMAT=1,BundleScreenshotsSchema=object({cover:string().optional(),screens:record(string(),string()).optional(),boards:record(string(),string()).optional()}),DesignBundleMetaSchema=object({formatVersion:number().int().positive().optional(),title:string().optional(),screens:array(object({id:string(),name:string().optional()})).optional(),boards:array(object({id:string()})).optional(),screenshots:BundleScreenshotsSchema.optional()});var DesignBundleSchema=strictObject({formatVersion:literal(DESIGN_BUNDLE_FORMAT),title:string(),viewport:ViewportSchema,defaultLibrary:string(),libraries:record(string(),LibrarySchema),extensions:record(string(),ExtensionSchema),viewportPresets:array(ViewportPresetSchema),theme:ThemeSchema,themes:record(string(),ThemeSchema),customCss:string(),snippets:array(SnippetSchema),screens:array(ScreenSchema),boards:array(BoardSchema),annotations:record(string(),array(AnnotationSchema)),notes:record(string(),array(CanvasNoteSchema)),live:boolean(),snapshotCssPath:string(),bundlePath:string().optional(),screenshots:object({cover:string(),screens:record(string(),string()),boards:record(string(),string())}).optional()});import{homedir}from"os";import{delimiter,dirname,resolve}from"path";function designGitEnv(folder){let ceilings=[homedir()];if(managedDesignId(folder)||localDesignOf(folder))ceilings.push(dirname(resolve(folder)));let inherited=process.env.GIT_CEILING_DIRECTORIES;if(inherited)ceilings.push(inherited);return{...process.env,GIT_CEILING_DIRECTORIES:ceilings.join(delimiter)}}var MAX_SCREENSHOT_BYTES=4194304,MAX_SCREENSHOT_COUNT=120,DOWNSCALE_WIDTH=1280;function isBrowserMissing(err){return err instanceof Error&&err.name==="BrowserMissingError"}async function captureBundleScreenshots(opts){let{screens,boards,viewport,renderHtml,capture,warn,progress}=opts,selectedScreens=opts.screenIds?screens.filter((screen)=>opts.screenIds?.has(screen.id)):screens,selectedBoards=opts.boardIds?boards.filter((board)=>opts.boardIds?.has(board.id)):boards,manifest={cover:"",screens:{},boards:{}},total=selectedScreens.length+selectedBoards.filter((board)=>board.frames.length>0).length,done=0,advance=()=>{done+=1,progress?.(done,total)},shoot=async(html,vp,fullPage)=>{let png=await capture({html,viewport:vp,fullPage,deviceScaleFactor:1});if(png.byteLength>MAX_SCREENSHOT_BYTES&&DOWNSCALE_WIDTH<vp.w)png=await capture({html,viewport:vp,fullPage,deviceScaleFactor:DOWNSCALE_WIDTH/vp.w});return png.byteLength<=MAX_SCREENSHOT_BYTES?png:null},jobs=[];for(let screen of selectedScreens)jobs.push({run:async()=>{try{let png=await shoot(await renderHtml(screen),viewport,!0);if(!png)return warn(`screenshot of screen "${screen.id}" exceeds 4MB even downscaled \u2014 skipped`),null;return{kind:"screen",id:screen.id,path:`screenshots/${screen.id}.png`,bytes:png}}catch(err){if(isBrowserMissing(err))throw err;return warn(`screenshot of screen "${screen.id}" failed \u2014 skipped (${message(err)})`),null}finally{advance()}}});let screenById=new Map(screens.map((screen)=>[screen.id,screen]));for(let board of selectedBoards){if(board.frames.length===0)continue;jobs.push({run:async()=>{try{let framed=(await Promise.all(board.frames.map(async(frame)=>{let screen=screenById.get(frame.screen);return screen?{frame,html:await renderHtml(screen,board.theme,frame.scheme??"light")}:null}))).filter((entry)=>entry!==null);if(framed.length===0)return null;let composite=buildBoardComposite(framed),png=await shoot(composite.html,composite.viewport,!1);if(!png)return warn(`screenshot of board "${board.id}" exceeds 4MB even downscaled \u2014 skipped`),null;return{kind:"board",id:board.id,path:`screenshots/${board.id}.png`,bytes:png}}catch(err){if(isBrowserMissing(err))throw err;return warn(`screenshot of board "${board.id}" failed \u2014 skipped (${message(err)})`),null}finally{advance()}}})}let shotLimit=MAX_SCREENSHOT_COUNT-1,results=Array.from({length:jobs.length},()=>null),nextJob=0,completedShots=0,reservedShots=0,browserMissing=!1,worker=async()=>{while(!browserMissing&&nextJob<jobs.length&&completedShots+reservedShots<shotLimit){let index=nextJob++;reservedShots+=1;try{let shot=await jobs[index]?.run();if(shot)results[index]=shot,completedShots+=1}catch(err){if(isBrowserMissing(err))browserMissing=!0;else throw err}finally{reservedShots-=1}}},concurrency=Math.max(1,Math.min(MAX_CONCURRENT_RENDERS,Math.floor(opts.concurrency??MAX_CONCURRENT_RENDERS)));if(await Promise.all(Array.from({length:Math.min(concurrency,jobs.length)},worker)),browserMissing)return warn("no headless browser available \u2014 publishing without screenshots"),null;if(nextJob<jobs.length)warn(`screenshot limit (${MAX_SCREENSHOT_COUNT}) reached \u2014 remaining shots skipped`);let files=[];for(let shot of results){if(!shot)continue;if(files.push({path:shot.path,bytes:shot.bytes}),shot.kind==="screen")manifest.screens[shot.id]=shot.path;else manifest.boards[shot.id]=shot.path}let coverSource=selectedBoards.map((b)=>manifest.boards[b.id]).find((p)=>p!==void 0)??selectedScreens.map((s)=>manifest.screens[s.id]).find((p)=>p!==void 0);if(!coverSource)return null;let source=files.find((f)=>f.path===coverSource);if(!source)return null;return manifest.cover="screenshots/cover.png",files.push({path:manifest.cover,bytes:source.bytes}),{manifest,files}}function message(err){let msg=err instanceof Error?err.message:String(err);return msg.split(`
3
+ `)[0]??msg}var normalizedBoardIds=(boardIds)=>[...new Set(boardIds)].sort();function publishSlotMismatches(slot,current){let mismatches=[];if(!slot.context.contextKnown)mismatches.push("older publish has no board-selection context");if(!(current.candidateTeamIds?slot.teamId!==null&&current.candidateTeamIds.includes(slot.teamId):slot.teamId===current.teamId))mismatches.push("team differs");if(JSON.stringify(normalizedBoardIds(slot.context.boardIds))!==JSON.stringify(normalizedBoardIds(current.boardIds)))mismatches.push("board selection differs");if(slot.context.repo!==current.repo)mismatches.push("repository differs");else if(current.repo!==null){if(!slot.context.branch||!current.branch)mismatches.push("branch is unavailable");else if(slot.context.branch!==current.branch)mismatches.push("branch differs")}return mismatches}function recommendedPublishSlot(slots,current){return exactPublishSlots(slots,current)[0]??null}function exactPublishSlots(slots,current){return slots.filter((slot)=>publishSlotMismatches(slot,current).length===0).sort((left,right)=>publishedAt(right.lastPublishedAt)-publishedAt(left.lastPublishedAt))}var publishedAt=(value)=>{let parsed=value?Date.parse(value):Number.NaN;return Number.isFinite(parsed)?parsed:Number.NEGATIVE_INFINITY},PUBLISH_VIEWPORT={w:1440,h:900};function defaultPublishTitle(folderRoot){return`${recordedDesignName(folderRoot)??folderRoot.split("/").filter(Boolean).pop()} designs`}async function ensureFolderId(folder,existing,report){if(existing)return existing;let path=join(folder,".design","config.json"),raw=JSON.parse(await readFile(path,"utf8")),folderId=crypto.randomUUID();return await writeJsonAtomic(path,{...raw,folderId}),report({kind:"note",message:"assigned this folder a cloud id (.design/config.json folderId) \u2014 commit it so every clone finds the folder's share links."}),folderId}function reportProvenance(git,report){if(git.repo){report({kind:"info",message:git.branch?`source: ${git.repo} on ${git.branch}`:`source: ${git.repo} (detached HEAD \u2014 no branch recorded)`});return}report({kind:"info",message:git.branch?`source: branch ${git.branch} \u2014 no git remote, publishing without a repository`:"source: not a git repository \u2014 publishing without repository or branch"})}function gitProbe(folder){return{encoding:"utf8",stdio:["ignore","pipe","ignore"],env:designGitEnv(folder)}}function gitCommitSha(folder){try{let probe=gitProbe(folder);if(execFileSync("git",["-C",folder,"status","--porcelain"],probe).trim().length>0)return null;return execFileSync("git",["-C",folder,"rev-parse","HEAD"],probe).trim()}catch{return null}}function normalizeRemote(url){let rest=url.trim().replace(/\.git\/?$/,"");if(/^[./~]/.test(rest)||/^[a-z]:[\\/]/i.test(rest)||rest.startsWith("file://"))return null;let scp=/^(?:[\w.-]+@)?([\w.-]+):(?!\/\/)(.+)$/.exec(rest);if(scp?.[1]&&scp[2])return`${scp[1]}/${scp[2].replace(/^\/+/,"")}`;try{let parsed=new URL(rest.includes("://")?rest:`https://${rest}`);if(!parsed.hostname||!parsed.pathname||parsed.pathname==="/")return null;return`${parsed.hostname}${parsed.pathname.replace(/\/+$/,"")}`}catch{return null}}function gitContext(folder){let probe=gitProbe(folder),run=(args)=>{try{return execFileSync("git",["-C",folder,...args],probe).trim()||null}catch{return null}},branchRaw=run(["rev-parse","--abbrev-ref","HEAD"]),remote=run(["remote","get-url","origin"]);return{repo:remote?normalizeRemote(remote):null,branch:branchRaw==="HEAD"?null:branchRaw}}function publishableTeams(teams){return teams.filter((team)=>team.canPublish!==!1)}function resolvePublishTeam(teams,requested){if(teams.length===0)return requested?err(teamNotFound(requested)):ok({kind:"personal"});let reviewer=teams.some((team)=>team.role==="reviewer"),publishable=publishableTeams(teams);if(requested){let named=(candidates)=>candidates.filter((team)=>team.id===requested||team.name.toLowerCase()===requested.toLowerCase()),exact=named(publishable);if(exact.length>1)return err(teamAmbiguous(requested));let[team]=exact;if(team)return ok({kind:"team",team});return err(named(teams).length>0?teamNotPublishable(requested,reviewer):publishable.length===0?noPublishableTeam(reviewer):teamNotFound(requested))}let[only,...rest]=publishable;if(!only)return err(noPublishableTeam(reviewer));return rest.length===0?ok({kind:"team",team:only}):ok({kind:"choose",teams:publishable})}async function listTeams(baseUrl,token){let body=await cloudFetch(`${baseUrl}/v1/teams/mine`,TeamsResponseSchema,{operation:"listing teams",token});return body.ok?ok(body.value.teams):body}function selectBoards(design,boardIds){if(!boardIds||boardIds.length===0)return activeBoards(design).map(([,board])=>board);let wanted=new Set(boardIds);return orderedBoards(design).map(([,board])=>board).filter((board)=>wanted.has(board.id))}async function publishDesign(cloud,pipeline,request,report=()=>{}){let{folder:design}=pipeline,{root,config}=design;report({kind:"step",step:"check",message:"checking the cloud"});let health=await checkCloudHealth(cloud.baseUrl);if(health.status==="unreachable")return err(unreachable(health.reason,{url:cloud.baseUrl}));if(health.status==="unhealthy")return err(cloudUnhealthy(health.detail));if(design.screens.size===0)return err(noScreens(root));report({kind:"step",step:"select",message:"collecting boards"});let boards=selectBoards(design,request.boardIds),screenIds=Boolean(request.boardIds&&request.boardIds.length>0)?new Set(boards.flatMap((b)=>b.frames.map((f)=>f.screen))):null,screens=screenIds?[...design.screens.values()].filter((s)=>screenIds.has(s.id)):[...design.screens.values()];if(screens.length===0)return err(noBoardScreens());let viewport=request.viewport??PUBLISH_VIEWPORT,title=request.title?.trim()||defaultPublishTitle(root),form=new FormData,bundleFiles=0,bundleBytes=0,addFile=(path,data,type)=>{if(typeof data==="string"?data.length===0:data.byteLength===0)return report({kind:"warn",message:`skipped empty file: ${path}`}),!1;return form.append("file",new File([data],path,type?{type}:void 0)),bundleFiles+=1,bundleBytes+=typeof data==="string"?new TextEncoder().encode(data).byteLength:data.byteLength,!0},liveExt=liveExtensions(config.extensions),bundler=Object.keys(liveExt).length>0?pipeline.liveBundler??createPublishBundler(root,config):null;report({kind:"step",step:"styles",message:"compiling styles"});let snapshotCss=await pipeline.snapshotCss(),live=!1,liveCode=null;if(bundler){report({kind:"step",step:"bundle",message:"bundling live components"});let bundle=await bundler.build();for(let e of bundle.errors)report({kind:"warn",message:`live-island: ${e.message}`});if(addFile("bundle.js",bundle.code,"text/javascript"))liveCode=bundle.code,live=!0}report({kind:"step",step:"capture",message:"capturing previews"});let mount=createPublishMount(pipeline.folder,pipeline.providers,pipeline.defaultProvider),shots=await withAssetServer(root,liveCode,(baseHref)=>{let htmlCache=new Map,renderHtml=async(screen,themeName,scheme="light")=>{let theme=(themeName?design.themes.get(themeName):void 0)??design.theme,key=`${screen.id}\x00${theme.name}\x00${scheme}`,cached=htmlCache.get(key);if(cached)return cached;let canvasBundle=await mount.forScreen(screen,theme,scheme==="dark"),rendering=renderScreen(screen,theme,{viewport,snapshotCss,registry:registryForScreen(screen,pipeline.providers,pipeline.defaultProvider,config.extensions??{}),renderPass:renderPassForScreen(screen,pipeline.providers,pipeline.defaultProvider,theme),snippets:design.snippets,customCss:design.customCss,dark:scheme==="dark",baseHref,...live?{liveBundleUrl:"/live/bundle.js"}:{},...canvasBundle?{canvasBundle}:{}}).then(({html})=>html);return htmlCache.set(key,rendering),rendering};return captureBundleScreenshots({screens,boards,...request.screenshotSelection?{screenIds:request.screenshotSelection.screenIds,boardIds:request.screenshotSelection.boardIds}:{},viewport,renderHtml,capture:async(req)=>(await captureScreenshot({html:req.html,viewport:req.viewport,fullPage:req.fullPage,deviceScaleFactor:req.deviceScaleFactor})).png,warn:(message)=>report({kind:"warn",message}),progress:(done,total)=>report({kind:"capture",done,total})})},mount.serve);for(let f of shots?.files??[])addFile(f.path,f.bytes,"image/png");let annotations=Object.fromEntries([...design.annotations].filter(([screenId,list])=>list.length>0&&screens.some((s)=>s.id===screenId))),notes=Object.fromEntries([...design.notes].filter(([boardId,list])=>list.length>0&&boards.some((b)=>b.id===boardId))),designDoc={formatVersion:DESIGN_BUNDLE_FORMAT,title,viewport,defaultLibrary:config.defaultLibrary,libraries:config.libraries,extensions:config.extensions??{},viewportPresets:config.viewportPresets??[],theme:design.theme,themes:Object.fromEntries(design.themes),customCss:design.customCss,snippets:[...design.snippets.values()],screens,boards,annotations,notes,live,snapshotCssPath:"snapshot.css",...live?{bundlePath:"bundle.js"}:{},...shots?{screenshots:shots.manifest}:{}},validated=DesignBundleSchema.safeParse(designDoc);if(!validated.success)return err(bundleInvalid(prettifyError(validated.error)));addFile("design.json",JSON.stringify(designDoc),"application/json"),addFile("snapshot.css",snapshotCss||`/* this folder uses no CSS framework */
4
+ `,"text/css");let assetRefs=new Set,assetRe=/\/assets\/[A-Za-z0-9._@\-/]+/g;for(let source of[...screens,...design.snippets.values()])for(let m of JSON.stringify(source).matchAll(assetRe))assetRefs.add(m[0]);for(let ref of assetRefs){let rel=ref.replace(/^\//,"");try{let bytes=await readFile(join(root,rel));addFile(rel,/\.svg$/i.test(rel)?Buffer.from(sanitizeSvgMarkup(bytes.toString("utf8")),"utf8"):bytes)}catch{report({kind:"warn",message:`asset not found: ${rel}`})}}let commitSha=gitCommitSha(root);if(commitSha)form.append("commitSha",commitSha);let git=request.provenance??gitContext(root);if(reportProvenance(git,report),git.repo)form.append("gitRepo",git.repo);if(git.branch)form.append("gitBranch",git.branch);let folderId=await ensureFolderId(root,config.folderId,report);report({kind:"step",step:"upload",message:`uploading ${bundleFiles} file${bundleFiles===1?"":"s"} (${formatBytes(bundleBytes)})`});let uploaded=await uploadLinkBundle({baseUrl:cloud.baseUrl,token:cloud.token,link:{...request.destination.slug?{slug:request.destination.slug}:{},folderId,publishMode:request.destination.mode,...request.destination.mode==="update"?{expectedVersionId:request.destination.expectedVersionId}:{},title,visibility:request.visibility,...request.teamId?{teamId:request.teamId}:{},...request.audience?.length?{audience:request.audience}:{},...request.publicComments!==void 0?{publicComments:request.publicComments}:{},...request.password?{password:request.password}:{},...request.passwordExpiresAt?{passwordExpiresAt:request.passwordExpiresAt}:{}},form});if(!uploaded.ok)return uploaded;let upload=uploaded.value;return ok({shareUrl:upload.shareUrl,slug:upload.link.slug,visibility:upload.link.visibility,passwordProtected:upload.link.passwordProtected,...upload.link.audience!==void 0?{audience:upload.link.audience}:{},...upload.link.publicComments!==void 0?{publicComments:upload.link.publicComments}:{},files:upload.files,bytes:upload.bytes,screenshots:shots?.files.length??0,boards:boards.length,screens:screens.length,commitSha,created:upload.created,...upload.tier!==void 0?{tier:upload.tier}:{},...upload.history!==void 0?{history:upload.history}:{}})}function formatBytes(bytes){if(bytes>=1048576)return`${(bytes/1048576).toFixed(1)} MB`;if(bytes>=1024)return`${Math.round(bytes/1024)} KB`;return`${bytes} B`}function createPublishBundler(root,config){return new LiveBundler(root,()=>config,()=>liveExtensions(config.extensions),!0,!0)}function publishedDesignSubtitle(design){let published=design.lastPublishedAt?`Published ${design.lastPublishedAt}`:"Publish time unavailable",who=design.ownerEmail?.trim()||design.ownerName?.trim(),publisher=who?`by ${who}`:"publisher unavailable",repo=design.git?.repo?.trim(),branch=design.git?.branch?.trim(),source=repo?`repo ${repo}${branch?` (${branch})`:""}`:"repository unavailable";return`${published} \xB7 ${publisher} \xB7 ${source}`}async function listPublishedDesigns(opts){let body=await cloudFetch(`${opts.baseUrl}/v1/links`,PublishedDesignsResponseSchema,{operation:"listing published designs",token:opts.token});if(!body.ok)return body;return ok((body.value.links??[]).filter((link)=>link.published).map((link)=>({...link,url:link.url.startsWith("http")?link.url:`${opts.baseUrl}${link.url}`})))}async function unpublishDesign(opts){let res=await fetch(`${opts.baseUrl}/v1/links/${encodeURIComponent(opts.slug)}`,{method:"DELETE",headers:{authorization:`Bearer ${opts.token}`}}).catch((error)=>error);if(!(res instanceof Response))return err(unreachable(res,{url:opts.baseUrl}));if(!res.ok)return err(await httpFailureFrom("unpublishing the design",res));return ok(void 0)}
5
+ export{publishedDesignSubtitle,listPublishedDesigns,unpublishDesign,listPublishDestinations,designGitEnv,teamChoiceRequired,describePublishError,recommendedPublishSlot,exactPublishSlots,PUBLISH_VIEWPORT,gitContext,publishableTeams,resolvePublishTeam,listTeams,publishDesign,createPublishBundler};
@@ -0,0 +1,2 @@
1
+ // @bun
2
+ import"./chunk-gsne0ap4.js";import"./chunk-nte0t7v3.js";import{normalizeCloudUrl,loadCredential,listCredentials,deleteCredential,clearCredentials,revokeCredential}from"./chunk-njhdn9j0.js";import{defaultCloudUrl}from"./chunk-25j8vgpa.js";import{defineCommand}from"./chunk-2smsg2ct.js";var logout_default=defineCommand({meta:{name:"logout",description:"Log out of velloo-cloud (signs the CLI token out and removes it)"},args:{url:{type:"string",description:"velloo-cloud base URL (default: $VELLOO_CLOUD_URL or the built-in default)"},all:{type:"boolean",default:!1,description:"Remove saved credentials for every cloud"}},async run({args}){if(args.all){await Promise.all(Object.entries(await listCredentials()).map(([cloudUrl,credential])=>revokeCredential(cloudUrl,credential.token)));let count=await clearCredentials();console.log(count>0?`velloo logout: removed credentials for ${count} cloud${count===1?"":"s"}`:"velloo logout: no saved credentials");return}let cloudUrl=args.url?normalizeCloudUrl(args.url):defaultCloudUrl(),existing=await loadCredential(cloudUrl);if(existing)await revokeCredential(cloudUrl,existing.token);if(await deleteCredential(cloudUrl))console.log(`velloo logout: logged out of ${cloudUrl}${existing?.email?` (${existing.email})`:""}`);else console.log(`velloo logout: not logged in to ${cloudUrl}`)}});export{logout_default as default};
@@ -1,5 +1,5 @@
1
1
  // @bun
2
- import{normalizeRepoManifest,CURRENT_SCHEMA_VERSION,schemaVersionOf,planMigration}from"./chunk-nte0t7v3.js";import{localDesignOf,findOwningManifest,recordedDesignName,loadDesignFolder}from"./chunk-5ezzc1h2.js";import{TOOL_VERSION}from"./chunk-kz4423th.js";import{existsSync,realpathSync}from"fs";import{readdir,readFile,writeFile}from"fs/promises";import{join,resolve}from"path";function isObject(value){return typeof value==="object"&&value!==null&&!Array.isArray(value)}var isAnnotationSidecar=(file)=>file.endsWith(".annotations.json"),isThemeDocument=(file)=>file.endsWith(".json");async function planRewrites(folder,dir,matches,transform){let files=[];try{files=await readdir(join(folder,dir))}catch{return[]}let out=[];for(let file of files.sort()){if(!matches(file))continue;let rel=join(dir,file),prev=await readFile(join(folder,rel),"utf8"),next=`${JSON.stringify(transform(JSON.parse(prev)),null,2)}
2
+ import{normalizeRepoManifest,CURRENT_SCHEMA_VERSION,schemaVersionOf,planMigration}from"./chunk-nte0t7v3.js";import{localDesignOf,findOwningManifest,recordedDesignName,loadDesignFolder}from"./chunk-egccxmed.js";import{TOOL_VERSION}from"./chunk-q4d60wr3.js";import{existsSync,realpathSync}from"fs";import{readdir,readFile,writeFile}from"fs/promises";import{join,resolve}from"path";function isObject(value){return typeof value==="object"&&value!==null&&!Array.isArray(value)}var isAnnotationSidecar=(file)=>file.endsWith(".annotations.json"),isThemeDocument=(file)=>file.endsWith(".json");async function planRewrites(folder,dir,matches,transform){let files=[];try{files=await readdir(join(folder,dir))}catch{return[]}let out=[];for(let file of files.sort()){if(!matches(file))continue;let rel=join(dir,file),prev=await readFile(join(folder,rel),"utf8"),next=`${JSON.stringify(transform(JSON.parse(prev)),null,2)}
3
3
  `;if(next!==prev)out.push({rel,next})}return out}async function upgradeFolder(folder,opts={}){let configPath=join(folder,".design","config.json"),rawConfig=JSON.parse(await readFile(configPath,"utf8")),from=schemaVersionOf(rawConfig),manifest=await findOwningManifest(folder).catch(()=>null),run=planMigration(rawConfig,{name:legacyNameOf(folder,manifest)}),manifestFiles=manifest?.legacy?[manifest.path]:[];if(run.applied.length===0){if(manifest?.legacy&&!opts.dryRun)await migrateManifest(manifest);return{from,to:CURRENT_SCHEMA_VERSION,applied:[],changedFiles:manifestFiles}}let rewrites=[...await planRewrites(folder,"screens",isAnnotationSidecar,(parsed)=>Array.isArray(parsed)?parsed.map((e)=>typeof e==="object"&&e!==null?run.annotation(e):e):parsed),...await planRewrites(folder,"theme",isThemeDocument,(parsed)=>isObject(parsed)?run.theme(parsed):parsed)],nextConfig={...run.config,toolVersion:TOOL_VERSION},changedFiles=[join(".design","config.json"),...rewrites.map((r)=>r.rel),...manifestFiles];if(opts.dryRun)return{from,to:CURRENT_SCHEMA_VERSION,applied:run.applied,changedFiles};await writeFile(configPath,`${JSON.stringify(nextConfig,null,2)}
4
4
  `,"utf8");for(let r of rewrites)await writeFile(join(folder,r.rel),r.next,"utf8");if(manifest?.legacy)await migrateManifest(manifest);return await loadDesignFolder(folder),{from,to:CURRENT_SCHEMA_VERSION,applied:run.applied,changedFiles}}function legacyNameOf(folder,manifest){let local=localDesignOf(folder);if(local)return local.legacyName;let target=realOr(folder);for(let[path,name]of manifest?.legacyNames??[])if(realOr(path)===target)return name;return}function realOr(path){return existsSync(path)?realpathSync(path):resolve(path)}async function migrateManifest(manifest){for(let[path,name]of manifest.legacyNames){if(recordedDesignName(path))continue;let configPath=join(path,".design","config.json"),raw;try{raw=JSON.parse(await readFile(configPath,"utf8"))}catch{continue}if(isObject(raw))await writeFile(configPath,`${JSON.stringify({...raw,name},null,2)}
5
5
  `)}let raw=JSON.parse(await readFile(manifest.path,"utf8"));await writeFile(manifest.path,`${JSON.stringify(normalizeRepoManifest(raw).manifest,null,2)}
@@ -0,0 +1,5 @@
1
+ // @bun
2
+ import{isCancel,confirm,password,select}from"./chunk-80fjfyha.js";import{protectedSharesAllowed,PROTECTED_SHARES_UNAVAILABLE,teamOnlyAllowed,describeCloudError}from"./chunk-gsne0ap4.js";import"./chunk-nte0t7v3.js";import{closePooledBrowser}from"./chunk-c4fym47a.js";import{loadDesignFolder,screensForBoards,resolveProviders,findHostTailwindConfig,TailwindJit,extraThemeBlock}from"./chunk-egccxmed.js";import{loadCredential,fetchAccount}from"./chunk-njhdn9j0.js";import{fail}from"./chunk-s23gtejf.js";import{confirmRenderFailures}from"./chunk-y0rwf1ze.js";import{publishedDesignSubtitle,listPublishedDesigns,unpublishDesign,listPublishDestinations,designGitEnv,teamChoiceRequired,describePublishError,recommendedPublishSlot,exactPublishSlots,gitContext,resolvePublishTeam,listTeams,publishDesign,createPublishBundler}from"./chunk-8g5ghy46.js";import{createProgress}from"./chunk-pjtg3wnm.js";import{DESIGN_ARG_DESCRIPTION,resolveDesign,pickBoards}from"./chunk-46xykc71.js";import{defaultCloudUrl,publishedBoardsUrl,billingPageUrl,checkCloudHealth}from"./chunk-25j8vgpa.js";import{withSubcommands}from"./chunk-94vx1yb0.js";import{defineCommand}from"./chunk-2smsg2ct.js";import{join as join2}from"path";import{execFileSync}from"child_process";import{realpathSync}from"fs";import{join,relative,resolve,sep}from"path";function plainJsonStem(path,dir){let prefix=`${dir}/`;if(!path.startsWith(prefix)||!path.endsWith(".json"))return null;let stem=path.slice(prefix.length,-5);if(stem.includes("/")||stem.includes("."))return null;return stem}function classifyChanges(changes){let set={screens:new Map,global:!1,snippets:new Set,assets:new Set};for(let{status,path}of changes){let screen=plainJsonStem(path,"screens");if(screen){set.screens.set(screen,status);continue}if(path.startsWith("theme/")||path===".design/config.json"){set.global=!0;continue}let snippet=plainJsonStem(path,"snippets");if(snippet){set.snippets.add(snippet);continue}if(path.startsWith("assets/"))set.assets.add(`/${path}`)}return set}function snippetIdsIn(value,acc){if(Array.isArray(value)){for(let v of value)snippetIdsIn(v,acc);return}if(value!==null&&typeof value==="object"){let id=value.$snippet;if(typeof id==="string")acc.add(id);for(let v of Object.values(value))snippetIdsIn(v,acc)}}function snippetsUsedByScreen(screen,snippets){let used=new Set;snippetIdsIn(screen.tree,used);let grew=!0;while(grew){grew=!1;for(let id of[...used]){let body=snippets.get(id);if(!body)continue;let inner=new Set;snippetIdsIn(body,inner);for(let innerId of inner)if(!used.has(innerId))used.add(innerId),grew=!0}}return used}function detectChangedScreens(changes,afterScreens,afterSnippets){let out=new Map;for(let[id,status]of changes.screens)out.set(id,{id,status:status==="A"?"added":status==="D"?"deleted":"modified",reasons:["screen"]});let markModified=(id,reason)=>{let existing=out.get(id);if(existing){if(!existing.reasons.includes(reason))existing.reasons.push(reason);return}out.set(id,{id,status:"modified",reasons:[reason]})};if(changes.global||changes.snippets.size>0||changes.assets.size>0)for(let[id,screen]of afterScreens){if(changes.global)markModified(id,"theme");if(changes.snippets.size===0&&changes.assets.size===0)continue;let used=snippetsUsedByScreen(screen,afterSnippets);for(let snippetId of changes.snippets)if(used.has(snippetId))markModified(id,`snippet:${snippetId}`);if(changes.assets.size>0){let haystack=JSON.stringify(screen);for(let snippetId of used){let body=afterSnippets.get(snippetId);if(body)haystack+=JSON.stringify(body)}for(let asset of changes.assets)if(haystack.includes(asset))markModified(id,`asset:${asset}`)}}return[...out.values()].sort((a,b)=>a.id.localeCompare(b.id))}function git(repo,args){return execFileSync("git",["-C",repo,...args],{encoding:"utf8",maxBuffer:67108864,stdio:["ignore","pipe","pipe"]})}function resolveGitContext(designFolder,base,head){let repoRoot;try{repoRoot=execFileSync("git",["-C",designFolder,"rev-parse","--show-toplevel"],{encoding:"utf8",stdio:["ignore","pipe","pipe"],env:designGitEnv(designFolder)}).trim()}catch{throw Error(`design folder ${designFolder} is not in a git repository, so there are no refs to compare`)}let designRel=relative(realpathSync.native(repoRoot),realpathSync.native(resolve(designFolder))).split(sep).join("/");if(designRel.startsWith(".."))throw Error(`design folder ${designFolder} is outside the git repo at ${repoRoot}`);let rev=(ref)=>{try{return git(repoRoot,["rev-parse","--verify",`${ref}^{commit}`]).trim()}catch{throw Error(`cannot resolve git ref '${ref}'`)}},baseSha=rev(base),headSha=rev(head),currentSha=rev("HEAD");return{repoRoot,designRel,baseSha,headSha,headIsWorktree:headSha===currentSha}}function diffDesignFolder(ctx){let args=["diff","--name-status","--no-renames",ctx.baseSha];if(!ctx.headIsWorktree)args.push(ctx.headSha);if(ctx.designRel!=="")args.push("--",ctx.designRel);let output=git(ctx.repoRoot,args),prefix=ctx.designRel===""?"":`${ctx.designRel}/`,out=[];for(let line of output.split(`
3
+ `)){if(line.trim()==="")continue;let tab=line.indexOf("\t");if(tab===-1)continue;let code=line.slice(0,tab).trim(),repoPath=line.slice(tab+1);if(!repoPath.startsWith(prefix))continue;let status=code==="A"?"A":code==="D"?"D":code==="M"||code==="T"?"M":null;if(!status)continue;out.push({status,path:repoPath.slice(prefix.length)})}return out}function changedBoardId(path){if(!path.startsWith("boards/")||!path.endsWith(".json"))return null;let id=path.slice(7,-5);return id.includes("/")||id.includes(".")?null:id}function selectChangedPreviews(changes,screens,snippets,boards){let availableScreens=new Map(screens.map((screen)=>[screen.id,screen])),screenIds=new Set(detectChangedScreens(classifyChanges(changes),availableScreens,snippets).filter((change)=>change.status!=="deleted"&&availableScreens.has(change.id)).map((change)=>change.id)),directlyChangedBoards=new Set(changes.map((change)=>changedBoardId(change.path)).filter((id)=>id!==null)),boardIds=new Set(boards.filter((board)=>directlyChangedBoards.has(board.id)||board.frames.some((frame)=>screenIds.has(frame.screen))).map((board)=>board.id));return{screenIds,boardIds}}function changedPreviewsSince(designFolder,baseRef,screens,snippets,boards){let git=resolveGitContext(designFolder,baseRef,"HEAD");return selectChangedPreviews(diffDesignFolder(git),screens,snippets,boards)}async function resolveCloud(command,args){let baseUrl=args.url?args.url.replace(/\/+$/,""):defaultCloudUrl(),token=args.token??process.env.VELLOO_CLOUD_TOKEN??(await loadCredential(baseUrl))?.token;if(!token)fail(command,"not logged in. Run `velloo login` first.");return{baseUrl,token}}function cloudFailure(command,error){fail(command,describeCloudError(error))}async function listPublished(args){let{baseUrl,token}=await resolveCloud("publish list",args),listed=await listPublishedDesigns({baseUrl,token});if(!listed.ok)cloudFailure("publish list",listed.error);let designs=listed.value;if(designs.length===0){console.log("velloo publish: no published designs."),console.log(` ${await publishedBoardsUrl(baseUrl)}`);return}console.log(`velloo publish: ${designs.length} design${designs.length===1?"":"s"}`);for(let design of designs)console.log(`
4
+ ${design.title?.trim()||"Untitled design"}`),console.log(` ${publishedDesignAccess(design)}`),console.log(` ${publishedDesignSubtitle(design)}`),console.log(` ${design.url}`);console.log(`
5
+ Manage in velloo-cloud: ${await publishedBoardsUrl(baseUrl)}`)}async function removePublished(args){let{baseUrl,token}=await resolveCloud("publish remove",args),interactive=Boolean(process.stdin.isTTY);if(!interactive&&!args.shareUrl)fail("publish remove","pass the full share URL and --yes when there is no interactive terminal");if(!interactive&&args.yes!==!0)fail("publish remove","refusing to remove a published design without --yes");let listed=await listPublishedDesigns({baseUrl,token});if(!listed.ok)cloudFailure("publish remove",listed.error);let designs=listed.value,manageable=designs.filter((design)=>design.canManage);if(manageable.length===0)fail("publish remove",`there are no published designs you can remove. Manage boards at ${await publishedBoardsUrl(baseUrl)}`);let target;if(args.shareUrl){if(target=designFromReference(args.shareUrl,designs),!target)fail("publish remove",`that share URL is not in your published designs. Manage boards at ${await publishedBoardsUrl(baseUrl)}`);if(!target.canManage)fail("publish remove","you do not have permission to remove that design")}else{let value=await select({message:"Design to unpublish",options:manageable.map((design)=>({value:design.slug,label:design.title?.trim()||"Untitled design",hint:publishedDesignSubtitle(design)}))});if(target=resolveUnpublishSelection(isCancel(value)?null:String(value),manageable),!target)fail("publish remove","cancelled")}if(args.yes!==!0){let approved=await confirm({message:`Unpublish \u201C${target.title?.trim()||"Untitled design"}\u201D? Its share URL will stop working.`,initialValue:!1});if(isCancel(approved)||!approved)fail("publish remove","cancelled")}let removed=await unpublishDesign({baseUrl,token,slug:target.slug});if(!removed.ok)cloudFailure("publish remove",removed.error);console.log(`velloo publish: removed ${target.title?.trim()||"Untitled design"}`),console.log(` ${target.url}`)}function publishedDesignAccess(design){let teamOnly=design.audience?.find((entry)=>entry.type==="team"),access=design.visibility==="private"?teamOnly?`only ${teamOnly.name||design.teamName||"its team"}`:"private":"public",parts=[design.passwordProtected?`${access}, password protected`:access],team=design.teamName?.trim();if(team)parts.push(`team ${team}`);if(design.publicComments&&(design.visibility==="public"||design.passwordProtected))parts.push("public comments on");return parts.join(" \xB7 ")}function resolveUnpublishSelection(value,designs){return value===null?null:designs.find((design)=>design.slug===value)??null}function designFromReference(reference,designs){if(!/^https?:\/\//i.test(reference.trim()))return null;let normalize=(raw)=>{try{let url=new URL(raw.trim());return url.search="",url.hash="",url.toString().replace(/\/+$/,"")}catch{return null}},normalized=normalize(reference);if(normalized===null)return null;return designs.find((design)=>normalize(design.url)===normalized)??null}function upgradeHint(upgradeUrl){return upgradeUrl?`upgrade at ${upgradeUrl}`:"upgrade from your cloud's billing page"}function privacyFlagsError(args,protectedShares=!0,upgradeUrl){if(args.visibility!==void 0&&args.visibility!=="public"&&args.visibility!=="private")return`--visibility must be 'public' or 'private', got '${args.visibility}'`;if(args.private&&args.public)return"--private and --public cannot be used together";if(args.private&&args.visibility==="public")return"--private conflicts with --visibility public";if(args.public&&args.visibility==="private")return"--public conflicts with --visibility private";let teamOnly=args["team-only"]===!0;if(teamOnly&&(args.public||args.visibility==="public"))return"--team-only is a private link; it cannot be combined with --public";if((args.private===!0||args.visibility==="private"||args.password===!0||teamOnly)&&!protectedShares)return`${PROTECTED_SHARES_UNAVAILABLE}. Publish with --public, or ${upgradeHint(upgradeUrl)}`;return null}async function resolvePublishPrivacy(args,interactive,{protectedShares=!0,upgradeUrl,team,offerTeamOnly=!1,choose=promptForPrivacyMode,askPublicComments=promptForPublicComments,warn=()=>{}}={}){let invalid=privacyFlagsError(args,protectedShares,upgradeUrl);if(invalid)throw Error(invalid);let explicit=args.visibility!==void 0||args.private===!0||args.public===!0||args.password===!0||args["team-only"]===!0,choice;if(explicit){let teamOnly=args["team-only"]===!0;if(teamOnly&&!team)throw Error("--team-only needs an organization team to publish into");choice={visibility:teamOnly||args.private||args.visibility==="private"?"private":"public",password:args.password===!0,teamOnly}}else{if(!interactive)throw Error("privacy mode is required without a terminal; pass --public, --private, --team-only, --password, or --visibility");let mode=await choose({protectedShares,upgradeUrl,...offerTeamOnly&&team?{teamName:team.name}:{}});if(typeof mode==="symbol")throw Error("cancelled");if(mode!=="public"&&!protectedShares)throw Error(PROTECTED_SHARES_UNAVAILABLE);choice=mode==="private"||mode==="team"?{visibility:"private",password:!1,teamOnly:mode==="team"}:{visibility:"public",password:mode==="password",teamOnly:!1}}let reachableOutside=choice.visibility==="public"||choice.password,flag=args["public-comments"];if(flag!==void 0){if(reachableOutside)return{...choice,publicComments:flag};if(flag)warn("--public-comments ignored: only people in your organization can open a private link");return choice}if(!explicit&&reachableOutside){let answer=await askPublicComments();if(typeof answer==="symbol")throw Error("cancelled");return{...choice,publicComments:answer}}return choice}async function promptForPrivacyMode({protectedShares,upgradeUrl,teamName}){let locked=protectedShares?{}:{disabled:!0,hint:"requires a paid plan"};return select({message:protectedShares?"Who should be able to view this publish?":`Who should be able to view this publish? (free plan: public links \u2014 ${upgradeHint(upgradeUrl)})`,initialValue:"public",options:[{value:"public",label:"Public",hint:"anyone with the link"},{value:"private",label:"Private",hint:"everyone in your organization",...locked},...teamName?[{value:"team",label:`Only ${teamName}`,hint:"that team, plus your organization's owner and admins",...locked}]:[],{value:"password",label:"Password protected",hint:"anyone with the password",...locked}]})}function promptForPublicComments(){return confirm({message:"Let people outside your organization comment? (they'll give their name)",initialValue:!1})}var CLOUD_ARGS={url:{type:"string",description:"velloo-cloud base URL (default: $VELLOO_CLOUD_URL or the built-in default)"},token:{type:"string",description:"velloo-cloud access token (default: $VELLOO_CLOUD_TOKEN)"}},list=defineCommand({meta:{name:"list",description:"List the designs you've published"},args:CLOUD_ARGS,run:({args})=>listPublished(args)}),remove=defineCommand({meta:{name:"remove",description:"Take a published design down"},args:{"share-url":{type:"positional",required:!1,description:"The share URL to take down (default: pick one interactively)"},yes:{type:"boolean",description:"Skip the confirmation (required without an interactive terminal)"},...CLOUD_ARGS},run:({args})=>removePublished({...args,shareUrl:args["share-url"]})}),publish=defineCommand({meta:{name:"publish",description:"Publish a design as a velloo-cloud share link"},args:{design:{type:"positional",required:!1,description:DESIGN_ARG_DESCRIPTION},yes:{type:"boolean",description:"Publish even when a screen's component fails to render (required without an interactive terminal)"},...CLOUD_ARGS,slug:{type:"string",description:"Custom slug for --new (default: server-generated)"},new:{type:"boolean",description:"Create a separate publish link instead of updating an existing slot"},update:{type:"boolean",description:"Update the exact matching published design"},title:{type:"string",description:"Link title (default: the design's name)"},boards:{type:"string",description:"Comma-separated board ids to publish (default: pick interactively; all when non-interactive)"},visibility:{type:"string",description:"public | private \u2014 explicit alternative to the interactive privacy prompt"},public:{type:"boolean",description:"Publish for anyone with the link; skips the privacy prompt"},private:{type:"boolean",description:"Publish for everyone in your organization; skips the privacy prompt"},"team-only":{type:"boolean",description:"Publish for the board's team only (plus the organization's owner and admins); skips the privacy prompt"},"public-comments":{type:"boolean",description:"Let people outside your organization comment on a public or password link (--no-public-comments turns it off)"},password:{type:"boolean",description:"Password-protect the link; anyone with the password can view. Prompts for it, or reads $VELLOO_SHARE_PASSWORD"},"password-expires":{type:"string",description:"Stop accepting the password after this date (YYYY-MM-DD or ISO timestamp)"},team:{type:"string",description:"Organization team to publish into, by name or UUID (default: your only team; asked when you can publish to more than one)"},w:{type:"string",description:"Viewport width in px (default: 1440)"},h:{type:"string",description:"Viewport height in px (default: 900)"},"changed-since":{type:"string",description:"Capture only screen/board previews affected since this git ref (the full design still publishes)"}},async run({args}){let folder=await resolveDesign(args.design,"publish"),baseUrl=args.url?args.url.replace(/\/+$/,""):defaultCloudUrl(),token=args.token??process.env.VELLOO_CLOUD_TOKEN??(await loadCredential(baseUrl))?.token;if(!token)fail("publish","not logged in. Run `velloo login` (or pass --token / VELLOO_CLOUD_TOKEN).");let interactive=Boolean(process.stdin.isTTY),viewport={w:args.w?Number(args.w):1440,h:args.h?Number(args.h):900},design=await loadDesignFolder(folder),config=design.config,health=await checkCloudHealth(baseUrl);if(health.status==="unreachable")fail("publish",`cannot reach ${baseUrl} (${health.reason}). Is velloo-cloud up? (bun cloud:up)`);if(health.status==="unhealthy")fail("publish",health.detail);let account=await fetchAccount(baseUrl,token,5000),protectedShares=protectedSharesAllowed(account.status==="ok"?account.account.tier:void 0),upgradeUrl=protectedShares?void 0:await billingPageUrl(baseUrl),flagsError=privacyFlagsError(args,protectedShares,upgradeUrl);if(flagsError)fail("publish",flagsError);let listedTeams=await listTeams(baseUrl,token),teamsUnsupported=!listedTeams.ok&&listedTeams.error.kind==="HttpFailure"&&listedTeams.error.status===404&&!args.team;if(!listedTeams.ok&&!teamsUnsupported)fail("publish",describePublishError(listedTeams.error));let teams=listedTeams.ok?listedTeams.value:[],teamResolution=resolvePublishTeam(teams,args.team);if(!teamResolution.ok)fail("publish",describePublishError(teamResolution.error));let resolvedTeam=teamResolution.value,knownTeam=resolvedTeam.kind==="team"?resolvedTeam.team:void 0,selected=await pickBoards(folder,args.boards,interactive,"publish");if(selected===null){console.log("velloo publish: no boards selected \u2014 nothing was published.");return}let provenance=gitContext(folder),selectedBoardIds=new Set(selected.map((board)=>board.id)),selectedBoards=[...design.boards.values()].filter((board)=>selectedBoardIds.has(board.id)),screenshotSelection=args["changed-since"]?(()=>{try{return changedPreviewsSince(folder,args["changed-since"],[...design.screens.values()],design.snippets,selectedBoards)}catch(error){fail("publish",`cannot determine previews changed since '${args["changed-since"]}': ${error instanceof Error?error.message:String(error)}`)}})():void 0,listed=await(async()=>{if(!config.folderId||args.new===!0)return{effectiveTeamId:knownTeam?.id??null,slots:[]};let destinations=await listPublishDestinations({baseUrl,token,folderId:config.folderId,...knownTeam?{teamId:knownTeam.id}:{}});if(!destinations.ok)fail("publish",describePublishError(destinations.error));return destinations.value})(),source={boardIds:selected.map((board)=>board.id),teamId:listed.effectiveTeamId,...resolvedTeam.kind==="choose"?{candidateTeamIds:resolvedTeam.teams.map((team)=>team.id)}:{},...provenance},destination=await choosePublishDestination({slots:listed.slots,source,interactive,createNew:args.new===!0,updateExisting:args.update===!0,customSlug:args.slug,manageUrl:await publishedBoardsUrl(baseUrl)}),team=await publishTeamFor({resolution:resolvedTeam,destination,slots:listed.slots,teams,interactive}).catch((error)=>fail("publish",error instanceof Error?error.message:String(error))),tier=account.status==="ok"?account.account.tier:void 0,privacy=await resolvePublishPrivacy(args,interactive,{protectedShares,...upgradeUrl?{upgradeUrl}:{},team,offerTeamOnly:teams.length>1&&teamOnlyAllowed(tier),warn:(message)=>console.log(`velloo publish: ${message}`)}).catch((error)=>fail("publish",error instanceof Error?error.message:String(error))),visibility=privacy.visibility,password=privacy.password?await readSharePassword():void 0,passwordExpiresAt=resolvePasswordExpiry(args["password-expires"],password!=null),{providers,defaultProvider}=await resolveProviders(config,folder);await confirmRenderFailures("publish",{folder:design,providers,defaultProvider},source.boardIds.length>0?screensForBoards(design,source.boardIds):[...design.screens.values()],args.yes===!0);let bundler=createPublishBundler(folder,config),jit=new TailwindJit(Object.values(providers),join2(folder,"screens"),void 0,()=>extraThemeBlock(design),()=>bundler.hostSourceDirs(),()=>findHostTailwindConfig(folder,config.hostApp),config.styling?.framework),progress=createProgress(),report=createPublishReporter(progress),outcome;try{let published=await publishDesign({baseUrl,token},{folder:design,providers,defaultProvider,snapshotCss:()=>jit.build(),liveBundler:bundler},{boardIds:selected.map((b)=>b.id),...args.title?{title:args.title}:{},visibility,...password?{password}:{},...passwordExpiresAt?{passwordExpiresAt}:{},destination,...team?{teamId:team.id}:{},...privacy.teamOnly&&team?{audience:[{type:"team",id:team.id}]}:{},...privacy.publicComments!==void 0?{publicComments:privacy.publicComments}:{},provenance,viewport,...screenshotSelection?{screenshotSelection}:{}},report);if(!published.ok)progress.fail("publish failed"),fail("publish",describePublishError(published.error));outcome=published.value,progress.succeed("published design")}catch(error){progress.fail("publish failed"),fail("publish",error instanceof Error?error.message:String(error))}finally{await closePooledBrowser()}console.log(`velloo publish: ${outcome.files} files, ${Math.round(outcome.bytes/1024)} KB${outcome.screenshots>0?`, ${outcome.screenshots} screenshots`:""}${outcome.commitSha?`, commit ${outcome.commitSha.slice(0,7)}`:""}`),console.log(` ${outcome.shareUrl}`);for(let line of describePublishedAccess(outcome,{teamName:team?.name,publicComments:privacy.publicComments}))console.log(` ${line}`);let history=outcome.history;if(history&&!history.retained&&history.pruned>0)console.log(" replaced the previous version \u2014 the free plan keeps only the latest."),console.log(` Upgrade your plan to keep version history and revisit past publishes: ${await billingPageUrl(baseUrl)}`);else if(history?.retained&&history.versions>1)console.log(` history: ${history.versions} versions kept \u2014 pin any of them from ${outcome.shareUrl}v/<version>/`)}}),publish_default=withSubcommands(publish,{list,remove});function resolvePublishDestinationChoice(value,slots){if(value===null)return null;if(value==="new")return{mode:"new"};let slug=value.replace(/^update:/,""),slot=slots.find((candidate)=>candidate.slug===slug);if(!slot)return null;return{mode:"update",slug:slot.slug,expectedVersionId:slot.latestVersionId}}async function choosePublishDestination(opts){if(opts.createNew&&opts.updateExisting)fail("publish","choose either --new or --update, not both");if(opts.customSlug&&!opts.createNew)fail("publish","--slug names a new link; use it together with --new");let matching=exactPublishSlots(opts.slots,opts.source);if(opts.updateExisting){let slot=matching[0];if(!slot)fail("publish",`there is no exact published-design match to update. Manage existing boards at ${opts.manageUrl}`);return{mode:"update",slug:slot.slug,expectedVersionId:slot.latestVersionId}}if(opts.createNew)return{mode:"new",...opts.customSlug?{slug:opts.customSlug}:{}};if(matching.length===0)return(opts.log??console.log)("velloo publish: no matching published design \u2014 a new link will be created."),{mode:"new"};if(!opts.interactive)fail("publish","this folder already has published designs; pass --update or --new explicitly");let recommended=recommendedPublishSlot(opts.slots,opts.source),latestMatch=matching[0],value=await select({message:"Publish destination",initialValue:recommended?`update:${recommended.slug}`:"new",options:[...latestMatch?[{value:`update:${latestMatch.slug}`,label:`Update ${latestMatch.title||"Untitled design"}`,hint:"latest exact match for this team, board selection, and source"}]:[],{value:"new",label:"Create a new link",hint:recommended?"keep the matching design separate":"recommended for this source"}]}),destination=resolvePublishDestinationChoice(isCancel(value)?null:String(value),matching);if(!destination)fail("publish",isCancel(value)?"cancelled":"selected publish slot is no longer available");return destination}function createPublishReporter(progress){let latestCapture;return(event)=>{if(event.kind==="capture"){latestCapture=`capturing previews ${event.done}/${event.total}`,progress.step(latestCapture,{transient:!0});return}if(event.kind==="step"){if(latestCapture)progress.step(latestCapture);latestCapture=void 0,progress.step(event.message);return}progress.log(event.message)}}async function readSharePassword(){let fromEnv=process.env.VELLOO_SHARE_PASSWORD?.trim();if(fromEnv){if(fromEnv.length<3)fail("publish","VELLOO_SHARE_PASSWORD must be at least 3 characters");return fromEnv}if(!process.stdin.isTTY)fail("publish","--password needs a terminal to prompt \u2014 set $VELLOO_SHARE_PASSWORD instead");let entered=await password({message:"Password for this share link",validate:(value)=>(value??"").length<3?"At least 3 characters.":void 0});if(isCancel(entered))fail("publish","cancelled");return entered}function resolvePasswordExpiry(raw,hasPassword){if(!raw)return;if(!hasPassword)fail("publish","--password-expires needs --password");let value=/^\d{4}-\d{2}-\d{2}$/.test(raw)?`${raw}T23:59:59`:raw,when=new Date(value);if(Number.isNaN(when.getTime()))fail("publish",`--password-expires must be a date (YYYY-MM-DD) or ISO timestamp, got '${raw}'`);return when.toISOString()}async function publishTeamFor(opts){let{resolution,destination}=opts;if(destination.mode==="update"){let slotTeamId=opts.slots.find((slot)=>slot.slug===destination.slug)?.teamId;if(slotTeamId)return opts.teams.find((team)=>team.id===slotTeamId)??{id:slotTeamId,name:"its team"}}if(resolution.kind==="personal")return;if(resolution.kind==="team")return resolution.team;if(!opts.interactive)throw Error(describePublishError(teamChoiceRequired(resolution.teams.map((team)=>team.name))));let picked=await(opts.pick??promptForTeam)(resolution.teams),team=isCancel(picked)?void 0:resolution.teams.find((candidate)=>candidate.id===picked);if(!team)throw Error("cancelled");return team}function promptForTeam(teams){return select({message:"Publish to which team?",initialValue:(teams.find((team)=>team.isDefault)??teams[0])?.id,options:teams.map((team)=>({value:team.id,label:team.name}))})}function describePublishedAccess(outcome,context={}){let teamAudience=outcome.audience?.find((entry)=>entry.type==="team"),lines=[];if(outcome.visibility==="private"){let who=teamAudience?`only ${teamAudience.name||context.teamName||"its team"}`:"anyone signed in at your organization";lines.push(outcome.passwordProtected?`private \u2014 ${teamAudience?who:"your organization"}, or anyone with the password`:`private \u2014 ${who}`)}else lines.push(outcome.passwordProtected?"public \u2014 anyone with the link and the password":"public \u2014 anyone with the link");if(context.teamName)lines.push(`team: ${context.teamName}`);let reachableOutside=outcome.visibility==="public"||outcome.passwordProtected,publicComments=outcome.publicComments??context.publicComments;if(reachableOutside&&publicComments!==void 0)lines.push(publicComments?"comments: open to people outside your organization (they give their name)":"comments: your organization only");return lines}export{publish_default as default};
@@ -1,5 +1,5 @@
1
1
  // @bun
2
- import{resolveColors,DEFAULT_TYPESET_NAME,typesetScale}from"./chunk-nte0t7v3.js";import{helpersRegistry,parse_default,formatRgb,STYLE_PROP,catalogFromManifest,identifierRef}from"./chunk-5ezzc1h2.js";import{_objectWithoutPropertiesLoose,_extends,_assertThisInitialized,_setPrototypeOf}from"./chunk-h6gchare.js";import{resolveProviderSrcDir}from"./chunk-fjbzk4sw.js";import{require_react_development,require_react_dom}from"./chunk-c4fym47a.js";import{__toESM,__commonJS}from"./chunk-2smsg2ct.js";import{ANTD_VERSION}from"./chunk-0t68szgr.js";var require_classnames=__commonJS(function(exports,module){/*!
2
+ import{resolveColors,DEFAULT_TYPESET_NAME,typesetScale}from"./chunk-nte0t7v3.js";import{helpersRegistry,parse_default,formatRgb,STYLE_PROP,catalogFromManifest,identifierRef}from"./chunk-egccxmed.js";import{_objectWithoutPropertiesLoose,_extends,_assertThisInitialized,_setPrototypeOf}from"./chunk-h6gchare.js";import{resolveProviderSrcDir}from"./chunk-fjbzk4sw.js";import{require_react_development,require_react_dom}from"./chunk-c4fym47a.js";import{__toESM,__commonJS}from"./chunk-2smsg2ct.js";import{ANTD_VERSION}from"./chunk-0t68szgr.js";var require_classnames=__commonJS(function(exports,module){/*!
3
3
  Copyright (c) 2018 Jed Watson.
4
4
  Licensed under the MIT License (MIT), see
5
5
  http://jedwatson.github.io/classnames
@@ -1,2 +1,2 @@
1
1
  // @bun
2
- import{TAILWIND_CLASSNAME,helperSourcePath}from"./chunk-5ezzc1h2.js";import{snapshotVersion2,entryCssPath2,componentsDir2}from"./chunk-cvn689d5.js";import{registry2}from"./chunk-3g3dqej3.js";import"./chunk-c6hywdvq.js";import{readFile}from"fs/promises";import{join}from"path";async function readManifest(cacheDir){let raw=await readFile(join(cacheDir,"manifest.json"),"utf8");return JSON.parse(raw)}import{existsSync as existsSync4}from"fs";import{join as join5}from"path";import{existsSync as existsSync3,readFileSync as readFileSync3}from"fs";import{join as join4}from"path";import{existsSync,readFileSync}from"fs";import{join as join2}from"path";function exportsName(source,id){let name=id.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");if(new RegExp(`\\bexport\\s+(?:async\\s+)?(?:function|const|let|var|class)\\s+${name}\\b`).test(source))return!0;for(let match of source.matchAll(/\bexport\s*\{([^}]*)\}/g)){let clause=match[1]??"";for(let part of clause.split(",")){let local=part.trim().split(/\s+as\s+/);if((local.length>1?local[1]:local[0])?.trim()===id)return!0}}return/\bexport\s+\*\s+from\b/.test(source)}function hostComponentFile(uiDir,addName,id){for(let extension of["tsx","ts","jsx","js"]){let path=join2(uiDir,`${addName}.${extension}`);if(!existsSync(path))continue;try{return exportsName(readFileSync(path,"utf8"),id)?path:null}catch{return null}}return null}import{existsSync as existsSync2,readdirSync,readFileSync as readFileSync2}from"fs";import{join as join3,resolve}from"path";function kebab(id){return id.replace(/(?<!^)(?=[A-Z])/g,"-").toLowerCase()}function addNameIndex(manifest){let byId=new Map(manifest.flatMap((c)=>c.registryName?[[c.id,c.registryName]]:[]));return(id)=>byId.get(id)??kebab(id)}function findUiDir(hostAppRoot){let candidates=[];try{let cj=JSON.parse(readFileSync2(join3(hostAppRoot,"components.json"),"utf8")),fromAlias=(alias,suffix="")=>{if(!alias)return;let srcBase=existsSync2(join3(hostAppRoot,"src"))?"src":"",rel=alias.replace(/^@\//,srcBase?`${srcBase}/`:"")+suffix;candidates.push(resolve(hostAppRoot,rel))};fromAlias(cj.aliases?.ui),fromAlias(cj.aliases?.components,"/ui")}catch{}candidates.push(resolve(hostAppRoot,"src/components/ui"),resolve(hostAppRoot,"components/ui"),resolve(hostAppRoot,"app/components/ui"));for(let dir of candidates)if(existsSync2(dir))return dir;return null}function installedAddNames(hostAppRoot){let dir=findUiDir(hostAppRoot);if(!dir)return new Set;try{return new Set(readdirSync(dir).filter((f)=>f.endsWith(".tsx")||f.endsWith(".ts")).map((f)=>f.replace(/\.(tsx|ts)$/,"")))}catch{return new Set}}function enrichManifestFromHost(manifest,uiDir){if(!uiDir)return manifest;let addNameOf=addNameIndex(manifest),byFile=new Map,read=(addName)=>{if(byFile.has(addName))return byFile.get(addName)??null;for(let ext of["tsx","ts","jsx","js"]){let path=join4(uiDir,`${addName}.${ext}`);if(!existsSync3(path))continue;let source=readFileSync3(path,"utf8");return byFile.set(addName,source),source}return byFile.set(addName,""),null};return manifest.map((descriptor)=>{if(descriptor.source==="velloo")return descriptor;let source=read(addNameOf(descriptor.id));if(!source||!exportsName(source,descriptor.id))return descriptor;let additions=[...variantPropsFor(source,descriptor.id===descriptor.family),...declaredPropsFor(source,descriptor.id)];if(additions.length===0)return descriptor;let props=[...descriptor.props];for(let addition of additions){let index=props.findIndex((prop)=>prop.name===addition.name);if(index>=0)props[index]={...props[index],...addition};else props.push(addition)}return{...descriptor,props,designModeNotes:appendNote(descriptor.designModeNotes,"Props include variants detected from the app's component source.")}})}function appendNote(current,next){return current?`${current} ${next}`:next}function variantPropsFor(source,primary){let out=[];if(!primary)return out;let variantsAt=source.indexOf("variants:");if(variantsAt<0)return out;let open=source.indexOf("{",variantsAt),block=balancedBlock(source,open);if(!block)return out;for(let[name,value]of objectEntries(block.slice(1,-1))){let values=objectEntries(value.slice(1,-1)).map(([key])=>key);if(values.length===0)continue;out.push({name,type:`${values.map((value)=>JSON.stringify(value)).join(" | ")} | undefined`,optional:!0,control:"enum",enumValues:values,...defaultVariant(source,name)?{defaultValue:defaultVariant(source,name)}:{}})}return out}function defaultVariant(source,name){let at=source.indexOf("defaultVariants:");if(at<0)return;let open=source.indexOf("{",at),block=balancedBlock(source,open);if(!block)return;return objectEntries(block.slice(1,-1)).find(([key])=>key===name)?.[1].replace(/["'`\s]/g,"")}function declaredPropsFor(source,componentId){let match=[new RegExp(`interface\\s+${componentId}Props(?:\\s+extends[^\\{]+)?\\s*\\{`,"m"),new RegExp(`type\\s+${componentId}Props\\s*=.*?&\\s*\\{`,"m")].map((pattern)=>pattern.exec(source)).find(Boolean);if(!match||match.index===void 0)return[];let open=source.indexOf("{",match.index),block=balancedBlock(source,open);if(!block)return[];let out=[],property=/(?:^|[;{\n])\s*([A-Za-z_$][\w$]*)(\?)?\s*:\s*([^;\n}]+)/gm;for(let found of block.slice(1,-1).matchAll(property)){let name=found[1],raw=found[3]?.trim();if(!name||!raw||name==="children")continue;let enumValues=[...raw.matchAll(/["']([^"']+)["']/g)].map((value)=>value[1]),optional=found[2]==="?"||raw.includes("undefined");out.push({name,type:optional&&!raw.includes("undefined")?`${raw} | undefined`:raw,optional,control:enumValues.length>0?"enum":/boolean/.test(raw)?"boolean":/number/.test(raw)?"number":"string",...enumValues.length>0?{enumValues}:{}})}return out}function balancedBlock(source,open){if(open<0||source[open]!=="{")return null;let depth=0,quote="";for(let index=open;index<source.length;index++){let char=source[index],prev=source[index-1];if(quote){if(char===quote&&prev!=="\\")quote="";continue}if(char==='"'||char==="'"||char==="`"){quote=char;continue}if(char==="{")depth++;if(char==="}"&&--depth===0)return source.slice(open,index+1)}return null}function objectEntries(body){let out=[],index=0;while(index<body.length){while(index<body.length&&/[\s,]/.test(body[index]))index++;let keyMatch=/^(?:["']([^"']+)["']|([A-Za-z_$][\w$-]*))\s*:/.exec(body.slice(index));if(!keyMatch)break;let key=keyMatch[1]??keyMatch[2];if(!key)break;index+=keyMatch[0].length;while(/\s/.test(body[index]??""))index++;if(body[index]==="{"){let block=balancedBlock(body,index);if(!block)break;out.push([key,block]),index+=block.length;continue}let end=body.indexOf(",",index),value=body.slice(index,end<0?body.length:end).trim();out.push([key,value]),index=end<0?body.length:end+1}return out}function createProvider(opts={}){let cacheDir=opts.cacheDir,plannedUiDir=cacheDir?join5(cacheDir,"ui"):void 0,hostUiDir=()=>plannedUiDir&&existsSync4(plannedUiDir)?plannedUiDir:opts.hostAppRoot?findUiDir(opts.hostAppRoot):null,componentsDir=componentsDir2,version=opts.version??snapshotVersion2,hostAppRoot=opts.hostAppRoot,loadManifest=async()=>{if(cacheDir&&existsSync4(join5(cacheDir,"manifest.json")))return enrichManifestFromHost(await readManifest(cacheDir),hostUiDir());let{loadManifest:snapshotManifest}=await import("./chunk-cxzkkn12.js");return enrichManifestFromHost(await snapshotManifest(),hostUiDir())};return{id:"shadcn-upstream",version,componentsDir,styleEntryPath:entryCssPath2,registry:registry2,loadManifest,label:`shadcn (upstream @ ${version})`,styleChannel:TAILWIND_CLASSNAME,styleChannels:["tailwind-classname"],mcpIntro:()=>['**This folder targets shadcn/ui with Tailwind.** The canvas imports client-safe components directly from the app, uses named canvas-safe adaptations for portal/state-heavy families, and falls back per component when a host file is missing or cannot compile. Velloo helpers (`Box`, `Heading`, `Text`, `Icon`, `Image`, `Gradient`, `Layer`, `SVG`, `Divider`, `Placeholder`, `Prose`) remain canvas primitives. Style through `update_props { style: "flex gap-4 p-6" }` \u2014 a Tailwind className string. Call `list_components` for the catalog and `component_status` before making a fidelity claim.',""],catalog:async()=>{let manifest=await loadManifest(),addNameOf=addNameIndex(manifest),shadcn=manifest.filter((c)=>c.source!=="velloo"),present=hostAppRoot?installedAddNames(hostAppRoot):new Set;return shadcn.map((c)=>{let addName=addNameOf(c.id);return{id:c.id,installed:present.has(addName),importPath:`@/components/ui/${addName}`}})},ownedModules:{dirs:()=>{let uiDir=hostUiDir();return uiDir?[uiDir]:[]}},canvasBundleSpec:{styleRuntime:{kind:"none"},components:async(ids)=>{let manifest=await loadManifest(),addNameOf=addNameIndex(manifest),sourceById=new Map(manifest.map((entry)=>[entry.id,entry.source])),uiDir=hostUiDir();return ids.flatMap((id)=>{if(sourceById.get(id)==="velloo"){let path=helperSourcePath(id);return path?[{id,sources:[{importPath:path,exportName:id,fidelity:"fallback",note:"Velloo helper used alongside repo-backed shadcn components."}]}]:[]}if(!sourceById.has(id))return[];let addName=addNameOf(id),snapshotPath=join5(componentsDir2,"ui",`${addName}.tsx`);if(CANVAS_ADAPTED_FAMILIES.has(addName))return[{id,sources:[{importPath:snapshotPath,exportName:id,fidelity:"adapted",note:"The app component uses portals or runtime state; Velloo renders its canvas-safe counterpart inline."}]}];let hostPath=uiDir?hostComponentFile(uiDir,addName,id):null;return[{id,sources:[...hostPath?[{importPath:hostPath,exportName:id,fidelity:"exact",preflight:!0,note:"Rendered directly from the app's shadcn source file."}]:[],{importPath:snapshotPath,exportName:id,fidelity:"fallback",note:hostPath?"The app source did not compile for the browser canvas; using the bundled canvas fallback.":"The app does not install this component under this name (it may be renamed in its own file); using the bundled canvas fallback."}]}]})},sourceDirs:()=>{let uiDir=hostUiDir()??plannedUiDir;return uiDir?[uiDir]:[]}}}}var CANVAS_ADAPTED_FAMILIES=new Set(["alert-dialog","calendar","carousel","chart","dialog","dropdown-menu","popover","select","sheet","sonner","tooltip"]);export{createProvider};
2
+ import{TAILWIND_CLASSNAME,helperSourcePath}from"./chunk-egccxmed.js";import{snapshotVersion2,entryCssPath2,componentsDir2}from"./chunk-cvn689d5.js";import{registry2}from"./chunk-vbe2r4kv.js";import"./chunk-c6hywdvq.js";import{readFile}from"fs/promises";import{join}from"path";async function readManifest(cacheDir){let raw=await readFile(join(cacheDir,"manifest.json"),"utf8");return JSON.parse(raw)}import{existsSync as existsSync4}from"fs";import{join as join5}from"path";import{existsSync as existsSync3,readFileSync as readFileSync3}from"fs";import{join as join4}from"path";import{existsSync,readFileSync}from"fs";import{join as join2}from"path";function exportsName(source,id){let name=id.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");if(new RegExp(`\\bexport\\s+(?:async\\s+)?(?:function|const|let|var|class)\\s+${name}\\b`).test(source))return!0;for(let match of source.matchAll(/\bexport\s*\{([^}]*)\}/g)){let clause=match[1]??"";for(let part of clause.split(",")){let local=part.trim().split(/\s+as\s+/);if((local.length>1?local[1]:local[0])?.trim()===id)return!0}}return/\bexport\s+\*\s+from\b/.test(source)}function hostComponentFile(uiDir,addName,id){for(let extension of["tsx","ts","jsx","js"]){let path=join2(uiDir,`${addName}.${extension}`);if(!existsSync(path))continue;try{return exportsName(readFileSync(path,"utf8"),id)?path:null}catch{return null}}return null}import{existsSync as existsSync2,readdirSync,readFileSync as readFileSync2}from"fs";import{join as join3,resolve}from"path";function kebab(id){return id.replace(/(?<!^)(?=[A-Z])/g,"-").toLowerCase()}function addNameIndex(manifest){let byId=new Map(manifest.flatMap((c)=>c.registryName?[[c.id,c.registryName]]:[]));return(id)=>byId.get(id)??kebab(id)}function findUiDir(hostAppRoot){let candidates=[];try{let cj=JSON.parse(readFileSync2(join3(hostAppRoot,"components.json"),"utf8")),fromAlias=(alias,suffix="")=>{if(!alias)return;let srcBase=existsSync2(join3(hostAppRoot,"src"))?"src":"",rel=alias.replace(/^@\//,srcBase?`${srcBase}/`:"")+suffix;candidates.push(resolve(hostAppRoot,rel))};fromAlias(cj.aliases?.ui),fromAlias(cj.aliases?.components,"/ui")}catch{}candidates.push(resolve(hostAppRoot,"src/components/ui"),resolve(hostAppRoot,"components/ui"),resolve(hostAppRoot,"app/components/ui"));for(let dir of candidates)if(existsSync2(dir))return dir;return null}function installedAddNames(hostAppRoot){let dir=findUiDir(hostAppRoot);if(!dir)return new Set;try{return new Set(readdirSync(dir).filter((f)=>f.endsWith(".tsx")||f.endsWith(".ts")).map((f)=>f.replace(/\.(tsx|ts)$/,"")))}catch{return new Set}}function enrichManifestFromHost(manifest,uiDir){if(!uiDir)return manifest;let addNameOf=addNameIndex(manifest),byFile=new Map,read=(addName)=>{if(byFile.has(addName))return byFile.get(addName)??null;for(let ext of["tsx","ts","jsx","js"]){let path=join4(uiDir,`${addName}.${ext}`);if(!existsSync3(path))continue;let source=readFileSync3(path,"utf8");return byFile.set(addName,source),source}return byFile.set(addName,""),null};return manifest.map((descriptor)=>{if(descriptor.source==="velloo")return descriptor;let source=read(addNameOf(descriptor.id));if(!source||!exportsName(source,descriptor.id))return descriptor;let additions=[...variantPropsFor(source,descriptor.id===descriptor.family),...declaredPropsFor(source,descriptor.id)];if(additions.length===0)return descriptor;let props=[...descriptor.props];for(let addition of additions){let index=props.findIndex((prop)=>prop.name===addition.name);if(index>=0)props[index]={...props[index],...addition};else props.push(addition)}return{...descriptor,props,designModeNotes:appendNote(descriptor.designModeNotes,"Props include variants detected from the app's component source.")}})}function appendNote(current,next){return current?`${current} ${next}`:next}function variantPropsFor(source,primary){let out=[];if(!primary)return out;let variantsAt=source.indexOf("variants:");if(variantsAt<0)return out;let open=source.indexOf("{",variantsAt),block=balancedBlock(source,open);if(!block)return out;for(let[name,value]of objectEntries(block.slice(1,-1))){let values=objectEntries(value.slice(1,-1)).map(([key])=>key);if(values.length===0)continue;out.push({name,type:`${values.map((value)=>JSON.stringify(value)).join(" | ")} | undefined`,optional:!0,control:"enum",enumValues:values,...defaultVariant(source,name)?{defaultValue:defaultVariant(source,name)}:{}})}return out}function defaultVariant(source,name){let at=source.indexOf("defaultVariants:");if(at<0)return;let open=source.indexOf("{",at),block=balancedBlock(source,open);if(!block)return;return objectEntries(block.slice(1,-1)).find(([key])=>key===name)?.[1].replace(/["'`\s]/g,"")}function declaredPropsFor(source,componentId){let match=[new RegExp(`interface\\s+${componentId}Props(?:\\s+extends[^\\{]+)?\\s*\\{`,"m"),new RegExp(`type\\s+${componentId}Props\\s*=.*?&\\s*\\{`,"m")].map((pattern)=>pattern.exec(source)).find(Boolean);if(!match||match.index===void 0)return[];let open=source.indexOf("{",match.index),block=balancedBlock(source,open);if(!block)return[];let out=[],property=/(?:^|[;{\n])\s*([A-Za-z_$][\w$]*)(\?)?\s*:\s*([^;\n}]+)/gm;for(let found of block.slice(1,-1).matchAll(property)){let name=found[1],raw=found[3]?.trim();if(!name||!raw||name==="children")continue;let enumValues=[...raw.matchAll(/["']([^"']+)["']/g)].map((value)=>value[1]),optional=found[2]==="?"||raw.includes("undefined");out.push({name,type:optional&&!raw.includes("undefined")?`${raw} | undefined`:raw,optional,control:enumValues.length>0?"enum":/boolean/.test(raw)?"boolean":/number/.test(raw)?"number":"string",...enumValues.length>0?{enumValues}:{}})}return out}function balancedBlock(source,open){if(open<0||source[open]!=="{")return null;let depth=0,quote="";for(let index=open;index<source.length;index++){let char=source[index],prev=source[index-1];if(quote){if(char===quote&&prev!=="\\")quote="";continue}if(char==='"'||char==="'"||char==="`"){quote=char;continue}if(char==="{")depth++;if(char==="}"&&--depth===0)return source.slice(open,index+1)}return null}function objectEntries(body){let out=[],index=0;while(index<body.length){while(index<body.length&&/[\s,]/.test(body[index]))index++;let keyMatch=/^(?:["']([^"']+)["']|([A-Za-z_$][\w$-]*))\s*:/.exec(body.slice(index));if(!keyMatch)break;let key=keyMatch[1]??keyMatch[2];if(!key)break;index+=keyMatch[0].length;while(/\s/.test(body[index]??""))index++;if(body[index]==="{"){let block=balancedBlock(body,index);if(!block)break;out.push([key,block]),index+=block.length;continue}let end=body.indexOf(",",index),value=body.slice(index,end<0?body.length:end).trim();out.push([key,value]),index=end<0?body.length:end+1}return out}function createProvider(opts={}){let cacheDir=opts.cacheDir,plannedUiDir=cacheDir?join5(cacheDir,"ui"):void 0,hostUiDir=()=>plannedUiDir&&existsSync4(plannedUiDir)?plannedUiDir:opts.hostAppRoot?findUiDir(opts.hostAppRoot):null,componentsDir=componentsDir2,version=opts.version??snapshotVersion2,hostAppRoot=opts.hostAppRoot,loadManifest=async()=>{if(cacheDir&&existsSync4(join5(cacheDir,"manifest.json")))return enrichManifestFromHost(await readManifest(cacheDir),hostUiDir());let{loadManifest:snapshotManifest}=await import("./chunk-tqwe5tc3.js");return enrichManifestFromHost(await snapshotManifest(),hostUiDir())};return{id:"shadcn-upstream",version,componentsDir,styleEntryPath:entryCssPath2,registry:registry2,loadManifest,label:`shadcn (upstream @ ${version})`,styleChannel:TAILWIND_CLASSNAME,styleChannels:["tailwind-classname"],mcpIntro:()=>['**This folder targets shadcn/ui with Tailwind.** The canvas imports client-safe components directly from the app, uses named canvas-safe adaptations for portal/state-heavy families, and falls back per component when a host file is missing or cannot compile. Velloo helpers (`Box`, `Heading`, `Text`, `Icon`, `Image`, `Gradient`, `Layer`, `SVG`, `Divider`, `Placeholder`, `Prose`) remain canvas primitives. Style through `update_props { style: "flex gap-4 p-6" }` \u2014 a Tailwind className string. Call `list_components` for the catalog and `component_status` before making a fidelity claim.',""],catalog:async()=>{let manifest=await loadManifest(),addNameOf=addNameIndex(manifest),shadcn=manifest.filter((c)=>c.source!=="velloo"),present=hostAppRoot?installedAddNames(hostAppRoot):new Set;return shadcn.map((c)=>{let addName=addNameOf(c.id);return{id:c.id,installed:present.has(addName),importPath:`@/components/ui/${addName}`}})},ownedModules:{dirs:()=>{let uiDir=hostUiDir();return uiDir?[uiDir]:[]}},canvasBundleSpec:{styleRuntime:{kind:"none"},components:async(ids)=>{let manifest=await loadManifest(),addNameOf=addNameIndex(manifest),sourceById=new Map(manifest.map((entry)=>[entry.id,entry.source])),uiDir=hostUiDir();return ids.flatMap((id)=>{if(sourceById.get(id)==="velloo"){let path=helperSourcePath(id);return path?[{id,sources:[{importPath:path,exportName:id,fidelity:"fallback",note:"Velloo helper used alongside repo-backed shadcn components."}]}]:[]}if(!sourceById.has(id))return[];let addName=addNameOf(id),snapshotPath=join5(componentsDir2,"ui",`${addName}.tsx`);if(CANVAS_ADAPTED_FAMILIES.has(addName))return[{id,sources:[{importPath:snapshotPath,exportName:id,fidelity:"adapted",note:"The app component uses portals or runtime state; Velloo renders its canvas-safe counterpart inline."}]}];let hostPath=uiDir?hostComponentFile(uiDir,addName,id):null;return[{id,sources:[...hostPath?[{importPath:hostPath,exportName:id,fidelity:"exact",preflight:!0,note:"Rendered directly from the app's shadcn source file."}]:[],{importPath:snapshotPath,exportName:id,fidelity:"fallback",note:hostPath?"The app source did not compile for the browser canvas; using the bundled canvas fallback.":"The app does not install this component under this name (it may be renamed in its own file); using the bundled canvas fallback."}]}]})},sourceDirs:()=>{let uiDir=hostUiDir()??plannedUiDir;return uiDir?[uiDir]:[]}}}}var CANVAS_ADAPTED_FAMILIES=new Set(["alert-dialog","calendar","carousel","chart","dialog","dropdown-menu","popover","select","sheet","sonner","tooltip"]);export{createProvider};