velloo 0.6.0 → 0.7.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.
- package/README.md +116 -227
- package/THIRD-PARTY-NOTICES.md +3 -3
- package/canvas/assets/index-BQ5710lu.js +29 -0
- package/canvas/assets/{lucide-all-CH8LmVh0.js → lucide-all-DIRZdJPK.js} +1 -1
- package/canvas/index.html +1 -1
- package/{chunk-56z5xd5b.js → chunk-0e5j6nqs.js} +1 -1
- package/{chunk-esb7n55g.js → chunk-0pxm5v0b.js} +2 -2
- package/{chunk-xtwx2p9g.js → chunk-0vaff40g.js} +1 -1
- package/{chunk-t6qr2ay9.js → chunk-3m28pvzr.js} +1 -1
- package/{chunk-rjjc9vam.js → chunk-4y2xy1kf.js} +2 -2
- package/{chunk-63y5gdzw.js → chunk-531xzfq5.js} +1 -1
- package/{chunk-c4spp880.js → chunk-5s1wjhtj.js} +1 -1
- package/{chunk-8cj4swpr.js → chunk-5x4nw0zz.js} +3 -3
- package/{chunk-k423dkjz.js → chunk-5yhnnhkd.js} +1 -1
- package/{chunk-8vgpffn7.js → chunk-64pex3mw.js} +1 -1
- package/{chunk-x02z2ccm.js → chunk-667ag8ts.js} +1 -1
- package/chunk-7gyzh0vq.js +5 -0
- package/{chunk-jsvkqae2.js → chunk-82x2jkfq.js} +1 -1
- package/{chunk-6813v926.js → chunk-84n18xgx.js} +2 -2
- package/{chunk-hjc5eg5c.js → chunk-abr2hgay.js} +1 -1
- package/{chunk-ff0jfn0v.js → chunk-acsv6m3n.js} +1 -1
- package/{chunk-08mjtg1e.js → chunk-asfgvzse.js} +1 -1
- package/{chunk-mfch0b0t.js → chunk-b8vcbbrg.js} +1 -1
- package/{chunk-ta84ehgg.js → chunk-bms1sg4c.js} +1 -1
- package/chunk-brnkprt5.js +723 -0
- package/{chunk-1gkeepkm.js → chunk-bys7pqdb.js} +1 -1
- package/{chunk-52m26hh4.js → chunk-d0j8dg1e.js} +1 -1
- package/{chunk-ff2vt7cz.js → chunk-d93kpv7s.js} +1 -1
- package/chunk-dvh0b2r4.js +3 -0
- package/{chunk-1td6z6ey.js → chunk-eh2xp4v6.js} +1 -1
- package/{chunk-96qhpth5.js → chunk-ejppcfa5.js} +1 -1
- package/{chunk-chtv0yj6.js → chunk-ex36z3b3.js} +144 -27
- package/{chunk-3trkmgfr.js → chunk-fmcm06xb.js} +1 -1
- package/{chunk-56vfy0db.js → chunk-fvezyap4.js} +1 -1
- package/{chunk-tac2wvc7.js → chunk-g7d065ek.js} +1 -1
- package/{chunk-ab8fga32.js → chunk-g8xrwjvq.js} +2 -2
- package/{chunk-bc7pkjcn.js → chunk-gbq5wv7x.js} +1 -1
- package/chunk-gcce587w.js +3 -0
- package/{chunk-nx09zqgs.js → chunk-gfppsgvv.js} +1 -1
- package/{chunk-qwccndcd.js → chunk-j306gr0c.js} +1 -1
- package/{chunk-2q561dec.js → chunk-j4fn2a2b.js} +1 -1
- package/chunk-jbgde2v9.js +2 -0
- package/{chunk-2ba6edh8.js → chunk-kkvf0xk2.js} +1 -1
- package/{chunk-h85hv05d.js → chunk-md4mpvqc.js} +1 -1
- package/{chunk-hf4adxe0.js → chunk-mdgwdqnq.js} +1 -1
- package/{chunk-w2naqxqt.js → chunk-pjkkp7xa.js} +1 -1
- package/chunk-qp3wahg4.js +2 -0
- package/{chunk-g185gx8a.js → chunk-qqrac4va.js} +1 -1
- package/{chunk-t2x3mztc.js → chunk-qrnkrkkt.js} +1 -1
- package/{chunk-8grzf8q1.js → chunk-rqd4swxx.js} +1 -1
- package/{chunk-dar6wbf2.js → chunk-rxkvjyst.js} +1 -1
- package/chunk-s84vxcdv.js +4 -0
- package/chunk-sjjnm4a1.js +2 -0
- package/chunk-skk6yvnw.js +3 -0
- package/{chunk-pxaxgkaq.js → chunk-sme6hv6f.js} +1 -1
- package/chunk-snak5zpr.js +5 -0
- package/{chunk-hsfpbjpc.js → chunk-srp5gyr1.js} +1 -1
- package/{chunk-eqjtq673.js → chunk-vdbr81a3.js} +1 -1
- package/{chunk-8vam2893.js → chunk-vs8j3x6t.js} +1 -1
- package/{chunk-69qb8p8s.js → chunk-xg71h7nb.js} +1 -1
- package/chunk-xzf52wvn.js +6 -0
- package/{chunk-tbd6wn3q.js → chunk-ykpctq15.js} +1 -1
- package/{chunk-74yxy75r.js → chunk-zv17t1n7.js} +1 -1
- package/cli.js +1 -1
- package/launcher.cjs +3 -1
- package/package.json +1 -1
- package/pkgs/helpers/src/box.tsx +12 -6
- package/pkgs/helpers/src/descriptors.ts +6 -0
- package/pkgs/helpers/src/icon.tsx +5 -3
- package/pkgs/helpers/src/index.ts +2 -2
- package/pkgs/helpers/src/text.tsx +29 -5
- package/pkgs/provider-antd/src/manifest.ts +1 -0
- package/pkgs/provider-chakra/src/manifest.ts +1 -0
- package/pkgs/provider-mui/src/manifest.ts +1 -0
- package/pkgs/provider-none/src/components-inline.tsx +20 -14
- package/pkgs/provider-none/src/components.tsx +2 -4
- package/pkgs/schema/src/config.ts +4 -1
- package/pkgs/schema/src/index.ts +3 -0
- package/pkgs/schema/src/node-identity.ts +6 -1
- package/pkgs/schema/src/node.ts +36 -19
- package/pkgs/schema/src/snippet-resolve.ts +18 -12
- package/pkgs/schema/src/svg-sanitize.ts +3 -0
- package/pkgs/schema/src/typeset.ts +21 -4
- package/pkgs/shadcn-snapshot/dist/manifest.json +6 -0
- package/canvas/assets/index-CnyI3aig.js +0 -29
- package/chunk-2web93qz.js +0 -2
- package/chunk-azeqkms7.js +0 -6
- package/chunk-e2btp1wd.js +0 -3
- package/chunk-ey65err5.js +0 -5
- package/chunk-rk238m15.js +0 -4
- package/chunk-wfhv9jg4.js +0 -2
- package/chunk-wnj432fw.js +0 -3
- package/chunk-yfvc8d2g.js +0 -716
- package/chunk-z73q4dq9.js +0 -2
- package/chunk-ztsckexy.js +0 -5
package/chunk-z73q4dq9.js
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
// @bun
|
|
2
|
-
import{init_default,runInit2}from"./chunk-ab8fga32.js";import"./chunk-mfch0b0t.js";import"./chunk-2ba6edh8.js";import"./chunk-rjjc9vam.js";import"./chunk-k423dkjz.js";import"./chunk-hf4adxe0.js";import"./chunk-8vam2893.js";import"./chunk-jsvkqae2.js";import"./chunk-1td6z6ey.js";import"./chunk-azeqkms7.js";import"./chunk-8vgpffn7.js";import"./chunk-80fjfyha.js";import"./chunk-yfvc8d2g.js";import"./chunk-chtv0yj6.js";import"./chunk-hjc5eg5c.js";import"./chunk-1gkeepkm.js";import"./chunk-rk238m15.js";import"./chunk-ff0jfn0v.js";import"./chunk-t6qr2ay9.js";import"./chunk-s23gtejf.js";import"./chunk-p93x4pn2.js";import"./chunk-8cj4swpr.js";export{init_default as default,runInit2 as runInit};
|
package/chunk-ztsckexy.js
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
// @bun
|
|
2
|
-
import{isCancel,confirm,password,select}from"./chunk-80fjfyha.js";import{protectedSharesAllowed,PROTECTED_SHARES_UNAVAILABLE,teamOnlyAllowed,describeCloudError}from"./chunk-1gkeepkm.js";import"./chunk-8cj4swpr.js";import{closePooledBrowser}from"./chunk-chtv0yj6.js";import{loadDesignFolder,screensForBoards,resolveProviders,findHostTailwindConfig,TailwindJit,extraThemeBlock}from"./chunk-yfvc8d2g.js";import{loadCredential,fetchAccount}from"./chunk-2ba6edh8.js";import{fail}from"./chunk-s23gtejf.js";import{confirmRenderFailures}from"./chunk-08mjtg1e.js";import{publishedDesignSubtitle,listPublishedDesigns,unpublishDesign,listPublishDestinations,designGitEnv,teamChoiceRequired,describePublishError,recommendedPublishSlot,exactPublishSlots,gitContext,resolvePublishTeam,listTeams,publishDesign}from"./chunk-ey65err5.js";import{createOneShotLiveBundler}from"./chunk-74yxy75r.js";import{createProgress}from"./chunk-pjtg3wnm.js";import{DESIGN_ARG_DESCRIPTION,resolveDesign,pickBoards}from"./chunk-8vgpffn7.js";import{defaultCloudUrl,publishedBoardsUrl,billingPageUrl,checkCloudHealth}from"./chunk-hjc5eg5c.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=createOneShotLiveBundler(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};
|