UNPKG

bunny-transfer

Version:

Transfer files to and from your storage zones with bunny.net in a single cli command 🐰

3 lines (2 loc) 9.97 kB
import { createRequire } from 'module';const require = createRequire(import.meta.url); import{argument as nt}from"pastel";import it from"react";import{z as he}from"zod";import we from"camelcase-keys";import Pe from"fast-deep-equal/es6/index.js";import w from"fs-extra";import Ee from"is-obj";import v from"p-map";import{parse as Fe}from"smol-toml";import xe from"node:os";var y=xe.cpus().length;function B(e,t){return e.filter((r,o)=>o===e.findIndex(n=>n[t]===r[t]))}import{createBunnyApiClient as ke}from"bunny-sdk";import Ce from"is-obj";function L({firstName:e,lastName:t}){if(e&&t)return`${e} ${t}`;if(t)return t;if(e)return e}async function A({accessKey:e,profile:t=e}){let o=await ke({accessKey:e}).user.get().catch(i=>{throw Ce(i)&&"responseStatusCode"in i&&i.responseStatusCode===401?new Error(`Cannot find user with accessKey "${e}"`):i});if(!o)throw new Error(`Cannot find user with accessKey "${e}"`);let n=L({firstName:o.firstName,lastName:o.lastName});return{accessKey:e,email:o.email,id:o.id,...n&&{name:n},profile:t}}import Se from"node:path";import Ae from"untildify";import{z as g}from"zod";var T="~/.bunny/credentials",k=Se.resolve(Ae(T)),D=g.object({accessKey:g.string(),email:g.string(),id:g.string(),name:g.string().optional(),profile:g.string()});import{stringify as Te}from"smol-toml";function N(e,t){return e.reduce((r,o)=>{let{[t]:n,...i}=o;return(typeof n=="string"||typeof n=="number"||typeof n=="symbol")&&(r[n]=i),r},{})}function K(e){return Te(N(e.map(t=>({...t,profile:t.profile??t.accessKey})),"profile"))}async function O({accessKey:e=[],credentialsPath:t=k,profile:r=[]}={}){let o=typeof e=="string"?[e]:e,n=typeof r=="string"?[r]:r,i=await w.pathExists(t),l=[];if(i){let m=await w.readFile(t,"utf8"),be=Fe(m),E=!1;if(l=await v(Object.entries(be),async([x,F])=>{let Z=D.parse({...Ee(F)?we({...F,profile:x}):{}}),R=await A({accessKey:Z.accessKey,profile:x});return Pe(Z,R)||(E=!0),R},{concurrency:y}),E){let x=K(l);await w.outputFile(t,x)}}let u=await v(o,async m=>A({accessKey:m,profile:m}),{concurrency:y}),c=B([...l,...u],"profile");return n.length>0?c.filter(m=>n.includes(m.profile??"")):c}async function j(e){let t=await O(e);return[...new Set(t.map(o=>o.accessKey))]}import{option as Ze}from"pastel";import{z}from"zod";var I=z.string().trim(),Re=z.preprocess(e=>process.env.BUNNY_ACCESS_KEY&&!e?process.env.BUNNY_ACCESS_KEY.split(","):e,I.or(I.array()).optional().transform(e=>typeof e=="string"?[e]:e)),$=Re.describe(Ze({alias:"k",description:"Bunny API Access Key",valueDescription:"uuid"}));import{option as Be}from"pastel";import{z as M}from"zod";var U=M.string().trim().min(1),Le=M.preprocess(e=>process.env.BUNNY_PROFILE&&!e?process.env.BUNNY_PROFILE.split(","):e,U.or(U.array()).optional().transform(e=>typeof e=="string"?[e]:e)),G=Le.describe(Be({alias:"p",description:"Bunny user profile name",valueDescription:"string"}));import De from"node:path";import{option as Ne}from"pastel";import Ke from"untildify";import{z as W}from"zod";var ve=W.preprocess(e=>{if(process.env.BUNNY_SHARED_CREDENTIALS_FILE&&!e){let t=process.env.BUNNY_SHARED_CREDENTIALS_FILE.trim();return De.resolve(Ke(t))}return e},W.string().trim().optional().default(k)),_=ve.describe(Ne({alias:"c",defaultValueDescription:T,description:"Bunny shared credentials file path",valueDescription:"file-path"}));import{createBunnyApiClient as _e}from"bunny-sdk";import Ve from"chalk";var V=new Intl.ListFormat("en",{style:"long",type:"conjunction"});import Oe from"tildify";var Y=process.cwd();function h(e){return e.startsWith(Y)?"."+e.slice(Y.length):Oe(e)}function d(){}import je from"fs-extra";async function q({absoluteDir:e}){await je.emptyDir(e)}import ze from"p-map";function C(e){if(!e.ok)throw new Error(`${e.status} ${e.statusText}`,{cause:e});return e}import Ie from"camelcase-keys";function S({file:e,folder:t,storageZone:r}){let o=e??t;return o?o.isDirectory?"https://"+r.storageHostname+o.path+o.objectName+"/":"https://"+r.storageHostname+o.path+o.objectName:"https://"+r.storageHostname+"/"+r.name+"/"}async function H({folder:e,storageZone:t}){let r=S({folder:e,storageZone:t});return await fetch(r,{headers:{accesskey:t.password}}).then(C).then(async n=>n.json()).then(n=>Ie(n))}async function J({dispatchTaskEnd:e=d,dispatchTaskStart:t=d,storageZone:r}){let o=await H({storageZone:r});await ze(o,async n=>{let i=`${n.path}${n.objectName}`.replace(`/${n.storageZoneName}`,".");t(i),await fetch(S({file:n,storageZone:r}),{headers:{accesskey:r.password},method:"DELETE"}).then(C),e(i)},{concurrency:y})}import Me from"fs-extra";import Ge from"p-map";function X(e){return typeof e=="number"?Number.isInteger(e):typeof e=="string"&&e.trim()!==""?Number.isInteger(Number(e)):!1}import hr from"tiny-invariant";async function Q({bunnyApiClient:e,id:t}){return e.storagezone.byId(t).get()}import kr from"tiny-invariant";async function ee({bunnyApiClient:e,includeDeleted:t=!1,name:r}){let o=await e.storagezone.get({queryParameters:{includeDeleted:t,page:1,perPage:1,search:r}});return o?.items?.[0]?.name===r?o?.items?.[0]:void 0}import $e from"node:path";import Ue from"untildify";function te(e){return[...new Set(e.map(t=>{if(t.at(0)==="/"||t.startsWith("./")||t.startsWith("../")||t.startsWith("~/")||t==="."||t==="~"){let r=$e.resolve(Ue(t));return r.at(-1)!=="/"?r+"/":r}return t}))]}async function re({bunnyApiClients:e,dispatchTaskEnd:t=d,dispatchTaskStart:r=d,locations:o}){return o=te(o),await Ge(o,async i=>{r(i.at(0)==="/"?h(i):i);let l=await We({bunnyApiClients:e,input:i});return t(i.at(0)==="/"?h(i):i),l},{concurrency:y})}async function We({bunnyApiClients:e,input:t}){if(t.at(0)==="/")return await Me.ensureDir(t),{path:t,type:"local"};if(t.includes("/"))throw new Error(`Location "${t}" is invalid. Local paths must be a relative path starting with "./" or an absolute path starting with "/". Storage zone names cannot include "/".`);for(let r of e){if(X(t)){let n=await Q({bunnyApiClient:r,id:Number.parseInt(t,10)});if(n)return{bunnyApiClient:r,storageZone:n,type:"storage-zone"};continue}let o=await ee({bunnyApiClient:r,name:t});if(o)return{bunnyApiClient:r,storageZone:o,type:"storage-zone"}}throw new Error(`Storage zone "${t}" cannot be found. Does it exist in your account?`)}async function oe({accessKeys:e,dispatchStoryChange:t=d,dispatchTaskEnd:r=d,dispatchTaskStart:o=d,highlight:n=Ve.white,locations:i}){t(`Validate locations ${V.format(i.map(c=>`"${n(h(c))}"`))}`);let l=e.map(c=>_e({accessKey:c})),u=await re({bunnyApiClients:l,dispatchTaskEnd:r,dispatchTaskStart:o,locations:i});for(let c of u)c.type==="local"&&(t(`Empty "${n(h(c.path))}"`),await q({absoluteDir:c.path})),c.type==="storage-zone"&&(t(`Empty "${n(c.storageZone.name)}" storage zone`),await J({dispatchTaskEnd:r,dispatchTaskStart:o,storageZone:c.storageZone}))}function Ye(e){return typeof e=="object"&&e!==null&&"message"in e&&typeof e.message=="string"}function qe(e){if(Ye(e))return e;try{return new Error(JSON.stringify(e))}catch{return new Error(String(e))}}function ne(e){return qe(e).message}import P from"figures";import{Newline as f,Text as s}from"ink";import He from"ink-spinner";import{Fragment as Je,jsx as a,jsxs as p}from"react/jsx-runtime";function ie({feedback:e}){let t=p(Je,{children:[a(f,{}),e.pastStories.map(r=>p(s,{children:[a(s,{color:"green",children:P.tick}),p(s,{color:"white",children:[" ",r]}),a(f,{})]},r))]});return e.error?p(s,{children:[t,p(s,{children:[a(s,{color:"red",children:P.cross}),p(s,{color:"white",children:[" ",e.story]}),a(f,{}),a(f,{}),a(s,{color:"red",children:e.error}),a(f,{})]})]}):e.done?p(s,{children:[t,p(s,{children:[a(s,{color:"magenta",children:P.heart}),a(s,{color:"white",children:" Done"}),a(f,{})]})]}):p(s,{children:[t,p(s,{children:[a(s,{color:"cyan",children:a(He,{})}),p(s,{color:"white",children:[" ",e.story]}),a(s,{children:e.tasks.map(r=>p(s,{children:[a(f,{}),a(s,{color:"gray",children:r})]},r))}),a(f,{})]})]})}import{createAction as b,createReducer as Qe}from"@reduxjs/toolkit";import ae from"react";function se(e){return e.at(0).toUpperCase()+e.slice(1)}function Xe(e,t){return Object.fromEntries(Object.entries(t).map(([r,o])=>[`dispatch${se(r)}`,(...n)=>{e(o(...n))}]))}function ce(e,t,r){let[o,n]=ae.useReducer(e,t),i=ae.useMemo(()=>Xe(n,r),[n,r]);return[o,i]}var pe=b("feedback/epic/end"),me=b("feedback/epic/error"),le=b("feedback/story/change"),de=b("feedback/tasks/end"),fe=b("feedback/tasks/start"),et={epicEnd:pe,epicError:me,storyChange:le,taskEnd:de,taskStart:fe},ye={done:!1,error:null,pastStories:[],story:"",tasks:[]},tt=Qe(ye,e=>e.addCase(le,(t,r)=>{t.story!==""&&t.pastStories.push(t.story),t.story=r.payload}).addCase(fe,(t,r)=>{t.tasks.push(r.payload)}).addCase(de,(t,r)=>{let o=t.tasks.indexOf(r.payload);o!==-1&&t.tasks.splice(o,1)}).addCase(pe,t=>{t.story!==""&&t.pastStories.push(t.story),t.story="",t.done=!0}).addCase(me,(t,r)=>{t.error=r.payload}));function ue(){return ce(tt,ye,et)}import{useApp as rt}from"ink";import ot from"react";function ge(e){let{exit:t}=rt();ot.useEffect(()=>{(e.done||e.error)&&(e.error&&(process.exitCode=1,process.env.CI&&(console.error(e.error),console.error())),t())},[e.done,e.error,t])}import{jsx as at}from"react/jsx-runtime";var xo=he.string().describe(nt({description:"Location to clear all contents",name:"location"})).array(),ko=he.object({accessKey:$,profile:G,sharedCredentialsFile:_});function st({args:e,options:t}){let[r,{dispatchEpicEnd:o,dispatchEpicError:n,dispatchStoryChange:i,dispatchTaskEnd:l,dispatchTaskStart:u}]=ue();return ge(r),it.useEffect(()=>{async function c(){try{i("Validate credentials");let m=await j({accessKey:t.accessKey,credentialsPath:t.sharedCredentialsFile,profile:t.profile});if(m.length===0)throw new Error("No accessKeys found");await oe({accessKeys:m,dispatchStoryChange:i,dispatchTaskEnd:l,dispatchTaskStart:u,locations:e}),o()}catch(m){n(ne(m))}}c()},[e,t.accessKey,t.profile,t.sharedCredentialsFile,o,n,i,l,u]),at(ie,{feedback:r})}export{xo as args,st as default,ko as options};