create-openfort 0.1.3 → 0.1.4

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/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.4
4
+
5
+ ### Patch Changes
6
+
7
+ - [#116](https://github.com/openfort-xyz/openfort-react/pull/116) [`ada14ae`](https://github.com/openfort-xyz/openfort-react/commit/ada14aeae32ecf298341782141dce1c0ce0cd7d0) Thanks [@martimayoral](https://github.com/martimayoral)! - add telemetry
8
+
9
+ - [#114](https://github.com/openfort-xyz/openfort-react/pull/114) [`14b68fc`](https://github.com/openfort-xyz/openfort-react/commit/14b68fc78321b5f07731792388a7b988ea073242) Thanks [@martimayoral](https://github.com/martimayoral)! - improve UX
10
+
3
11
  ## 0.1.3
4
12
 
5
13
  ### Patch Changes
package/dist/index.js CHANGED
@@ -1,28 +1,28 @@
1
1
  #!/usr/bin/env node
2
- import we from"node:path";import G from"fs-extra";import*as p from"@clack/prompts";import De from"chalk";import{Command as Fe}from"commander";import M from"node:path";import{fileURLToPath as Ie}from"node:url";var ve=Ie(import.meta.url),xe=M.dirname(ve),_=M.join(xe,"../"),z=` ___ ___ ___ _ _ ___ ___ ___ _____
2
+ import we from"node:path";import j from"fs-extra";import*as p from"@clack/prompts";import Ge from"chalk";import{Command as Ye,Option as je}from"commander";import M from"node:path";import{fileURLToPath as Ie}from"node:url";var ve=Ie(import.meta.url),xe=M.dirname(ve),_=M.join(xe,"../"),z=` ___ ___ ___ _ _ ___ ___ ___ _____
3
3
  / _ \\| _ \\| __| \\| | __/ _ \\| _ \\_ _|
4
- | (_) | _/| _|| . | _| (_) | / | |
4
+ | (_) | _/| _|| | _| (_) | / | |
5
5
  \\___/|_| |___|_|\\_|_| \\___/|_|_\\ |_|
6
- `,S="openfort-project",L="create-openfort";var W=["openfort-ui","headless","firebase"],J=["auto","midnight","minimal","soft","web95","rounded","retro","nouns"];import Ne from"node:path";import ke from"fs-extra";var y=()=>{let e=Ne.join(_,"package.json");return ke.readJSONSync(e).version??"1.0.0"};var T=class extends Error{};var a={error:(...e)=>console.error(...e),warn:(...e)=>console.warn(...e),info:(...e)=>console.log(...e),success:(...e)=>console.log(...e)};import $e,{createHash as Z}from"node:crypto";import Ce from"node:https";import{hostname as Re,userInfo as Ae}from"node:os";var X="phc_HosujvcO5QzmU2MVvZo8AxWV0pplTZJLr3jEd8dRVPE",q="https://analytics.openfort.xyz",Le=()=>{let e=`${Re()}-${Ae().username}`;return Z("sha256").update(e).digest("hex").slice(0,16)},B=class{constructor(){this.enabled=!0;this.send=async({properties:o={},status:i})=>{if(!this.enabled||!X||!q)return;let t={session_id:this.sessionId,cli_version:y(),node_version:process.version,platform:process.platform,cli_status:i,projectId:this.projectId,projectName:this.projectName,template:this.template,...o},n=JSON.stringify({api_key:X,event:"cli_tool_used",distinct_id:this.anonymousId,properties:t}),r=new URL(`${q}/capture/`),s={hostname:r.hostname,port:r.port||443,path:r.pathname,method:"POST",headers:{"Content-Type":"application/json","Content-Length":n.length}};return new Promise(c=>{let l=Ce.request(s,d=>{d.on("data",()=>{}),d.on("end",()=>{c()})});l.on("error",()=>{c()}),l.write(n),l.end()})};this.anonymousId=Le(),this.sessionId=Z("sha256").update($e.randomBytes(16)).digest("hex").slice(0,15)}},h=new B;var x=e=>(e.length>1&&e.endsWith("/")&&(e=e.slice(0,-1)),e);var Be=/^(?:@[a-z0-9-*~][a-z0-9-*._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/,Q=e=>{let o=x(e),i=o.split("/"),t=i.findIndex(r=>r.startsWith("@")),n=i[i.length-1];if(i.findIndex(r=>r.startsWith("@"))!==-1&&(n=i.slice(t).join("/")),!(o==="."||Be.test(n??"")))return"App name must consist of only lowercase alphanumeric characters, '-', and '_'"};var ee="[\\da-f]{8}-[\\da-f]{4}-[\\da-f]{4}-[\\da-f]{4}-[\\da-f]{12}",te=`(test|live)_${ee}`,Ke=new RegExp(`^sk_${te}$`),Ve=new RegExp(`^pk_${te}$`),He=new RegExp(`^${ee}$`),je=/^.{44}$/,P=({label:e,required:o=!0,regex:i,formatHint:t,customCheck:n})=>r=>{if(r!=="-"){if(o&&!r)return`${e} is required`;if(i&&r&&!i.test(r))return`${e} is invalid${t?` (${t})`:""}`;if(n)return n(r)}},ne=P({label:"Openfort Publishable Key",regex:Ve,formatHint:"expected format: pk_test_... or pk_live_..."}),oe=P({label:"Openfort Secret Key",regex:Ke,formatHint:"expected format: sk_test_... or sk_live_..."}),ie=P({label:"Shield Publishable Key",regex:He,formatHint:"expected UUID format"}),re=P({label:"Shield Secret",required:!0}),ae=P({label:"Shield Encryption Share",regex:je,formatHint:"expected 44 characters"}),se=P({label:"API endpoint",customCheck:e=>{try{new URL(e);return}catch{return"API endpoint must be a valid URL"}}}),le=async e=>{try{return!!(await(await fetch(e,{method:"POST",headers:{"Content-Type":"application/json"}})).json()).session}catch{return!1}};var Ue={appName:S,template:"openfort-ui",createBackend:!0},pe=async()=>{let e=new Fe().name(L).description("A CLI for creating Openfort applications with embedded wallets").argument("[dir]","The name of the application, as well as the name of the directory to create").option("--noGit","Explicitly tell the CLI to not initialize a new git repo in the project",!1).option("--noInstall","Explicitly tell the CLI to not run the package manager's install command",!1).option("-y, --default","Bypass the CLI and use all default options to bootstrap a new Openfort app",!1).option("--CI","Boolean value if we're running in CI",!1).option("--template [string]","Specify the template to use").option("--theme [string]","Specify the theme to use (for openfort-ui template)").option("--no-telemetry","Disable sending anonymous usage data",!1).version(y(),"-v, --version","Display the version number").addHelpText("afterAll",`
7
- Learn more about Openfort at ${De.hex("#5B87F5").bold("https://www.openfort.xyz")}
8
- `).parse(process.argv),o=e.args[0],i=e.opts();h.enabled=i.telemetry!==!1,h.send({status:"started"});let t={...Ue,appName:o||S,flags:{noGit:i.noGit||!1,noInstall:i.noInstall||!1,default:i.default||!1,CI:i.CI||!1,template:i.template,theme:i.theme}};if(t.flags.CI)return t.template=i.template||"openfort-ui",t.theme=i.theme,t.createBackend=!1,t.openfortPublishableKey="pk_test_00000000-0000-0000-0000-000000000000",t.shieldPublishableKey="00000000-0000-0000-0000-000000000000",t;if(t.flags.default)return t.template="openfort-ui",t.createBackend=!1,t.openfortPublishableKey="",t.shieldPublishableKey="",t;try{if(process.env.TERM_PROGRAM?.toLowerCase().includes("mintty"))throw a.warn(` WARNING: It looks like you are using MinTTY, which is non-interactive. This is most likely because you are
9
- using Git Bash. If that's that case, please use Git Bash from another terminal, such as Windows Terminal.`),new T("Non-interactive environment");let n=await p.group({...!o&&{name:()=>p.text({message:"What will your project be called?",defaultValue:S,validate:Q})},template:()=>p.select({message:"Select an Openfort template:",options:W.map(r=>({value:r,label:r==="openfort-ui"?"Openfort UI (default)":r,hint:Ge(r)})),initialValue:"openfort-ui"}),createBackend:()=>p.confirm({message:"Do you want to create a backend for automatic account recovery?",initialValue:!0}),apiEndpoint:async({results:r})=>{if(!r.createBackend){let s=await p.confirm({message:"Do you have an existing API endpoint for account recovery?",initialValue:!1});if(p.isCancel(s)&&process.exit(1),s){let c=!1,l="";for(;!c;){let d=await p.text({message:"Enter your API endpoint for creating encryption sessions:",placeholder:"http://localhost:3110/api/protected-create-encryption-session",validate:se});p.isCancel(d)&&process.exit(1),l=d;let u=p.spinner();if(u.start("Testing API endpoint..."),await le(l))u.stop("API endpoint validated!"),c=!0;else{u.stop("API endpoint validation failed"),a.error("The endpoint did not return a valid session response");let g=await p.confirm({message:"Would you like to try another endpoint?",initialValue:!0});(p.isCancel(g)||!g)&&process.exit(1)}}return l}}},theme:({results:r})=>{if(r.template==="openfort-ui")return p.select({message:"Select a theme:",options:J.map(s=>({value:s,label:s==="auto"?"Auto (default)":s})),initialValue:"auto"})},openfortPublishableKey:()=>p.text({message:"Enter your Openfort Publishable Key:",placeholder:"pk_test_...",validate:ne}),openfortSecretKey:({results:r})=>{if(r.createBackend)return p.text({message:"Enter your Openfort Secret Key:",placeholder:"sk_test_...",validate:oe})},shieldPublishableKey:()=>p.text({message:"Enter your Shield Publishable Key:",placeholder:"00000000-0000-0000-0000-000000000000",validate:ie}),shieldEncryptionShare:({results:r})=>{if(r.createBackend)return p.text({message:"Enter your Shield Encryption Share:",placeholder:"Your 44-character encryption share",validate:ae})},shieldSecretKey:({results:r})=>{if(r.createBackend)return p.text({message:"Enter your Shield Secret Key:",placeholder:"Your Shield Secret",validate:re})},...!t.flags.noGit&&{git:()=>p.confirm({message:"Should we initialize a Git repository and stage the changes?",initialValue:!0})}},{onCancel(){process.exit(1)}});return h.template=n.template,h.projectId=n.openfortPublishableKey,{appName:n.name||t.appName,template:n.template,theme:n.theme,createBackend:n.createBackend,apiEndpoint:n.apiEndpoint,openfortPublishableKey:n.openfortPublishableKey,openfortSecretKey:n.openfortSecretKey,shieldPublishableKey:n.shieldPublishableKey,shieldSecretKey:n.shieldSecretKey,shieldEncryptionShare:n.shieldEncryptionShare,flags:{...t.flags,noGit:!n.git||t.flags.noGit}}}catch(n){if(n instanceof T)return a.warn(`
10
- ${L} needs an interactive terminal to provide options`),await p.confirm({message:"Continue scaffolding a default Openfort app?",initialValue:!0})||(a.info("Exiting..."),process.exit(0)),a.info(`Bootstrapping a default Openfort app in ./${t.appName}`),{...t,template:"openfort-ui",createBackend:!1,openfortPublishableKey:"",shieldPublishableKey:""};throw n}};function Ge(e){switch(e){case"openfort-ui":return"Pre-built UI components";case"headless":return"Custom, unstyled components";case"firebase":return"With Firebase authentication";default:return""}}import ge from"node:path";import N from"node:path";import k from"fs-extra";import Ye from"ora";var ce=async({projectDir:e,openfortSecretKey:o,shieldSecretKey:i,shieldApiKey:t,shieldEncryptionShare:n,port:r=3110})=>{let s=Ye("Creating backend...").start();try{let c=N.join(_,"template/backend"),l=N.join(e,"backend");k.copySync(c,l);let d=N.join(l,".env.example"),u=N.join(l,".env");if(k.existsSync(d)){let g=k.readFileSync(d,"utf-8").replace(/OPENFORT_SECRET_KEY=.*/g,`OPENFORT_SECRET_KEY=${o}`).replace(/SHIELD_SECRET_KEY=.*/g,`SHIELD_SECRET_KEY=${i}`).replace(/SHIELD_API_KEY=.*/g,`SHIELD_API_KEY=${t}`).replace(/SHIELD_ENCRYPTION_SHARE=.*/g,`SHIELD_ENCRYPTION_SHARE=${n}`).replace(/PORT=.*/g,`PORT=${r}`);k.writeFileSync(u,g)}await new Promise(m=>{setTimeout(()=>{m(!0)},1e3)}),s.succeed("Backend created successfully!")}catch(c){throw s.fail("Failed to create backend"),a.error(c instanceof Error?c.message:String(c)),c}};import fe from"node:path";import*as E from"@clack/prompts";import b from"chalk";import O from"fs-extra";import Me from"ora";var de=async({projectName:e,projectDir:o,template:i,createBackend:t})=>{a.info("");let n=Me(`Scaffolding in: ${o}...
11
- `).start();if(O.existsSync(o))if(O.readdirSync(o).length===0)e!=="."&&n.info(`${b.cyan.bold(e)} exists but is empty, continuing...
12
- `);else{n.stopAndPersist();let l=await E.select({message:`${b.redBright.bold("Warning:")} ${b.cyan.bold(e)} already exists and isn't empty. How would you like to proceed?`,options:[{label:"Abort installation (recommended)",value:"abort"},{label:"Clear the directory and continue installation",value:"clear"},{label:"Continue installation and overwrite conflicting files",value:"overwrite"}],initialValue:"abort"});(E.isCancel(l)||l==="abort")&&(n.fail("Aborting installation..."),process.exit(1));let d=await E.confirm({message:`Are you sure you want to ${l==="clear"?"clear the directory":"overwrite conflicting files"}?`,initialValue:!1});(E.isCancel(d)||!d)&&(n.fail("Aborting installation..."),process.exit(1)),l==="clear"&&(n.info(`Emptying ${b.cyan.bold(e)} and creating Openfort app..
13
- `),O.emptyDirSync(o))}n.start(),O.mkdirSync(o,{recursive:!0});let r=fe.join(_,"template/openfort-templates",i),s=t?fe.join(o,"frontend"):o;O.copySync(r,s);let c=e==="."?"App":b.cyan.bold(e);n.succeed(`${c} ${b.green("scaffolded successfully!")}
14
- `)};import me from"node:path";import $ from"fs-extra";var ue=({projectDir:e,openfortPublishableKey:o,shieldPublishableKey:i,apiEndpoint:t,theme:n})=>{let r=me.join(e,".env.example"),s=me.join(e,".env");if(!$.existsSync(r)){let d=ze({openfortPublishableKey:o,shieldPublishableKey:i,apiEndpoint:t,theme:n});$.writeFileSync(s,d);return}let l=$.readFileSync(r,"utf-8");l=l.replace(/VITE_OPENFORT_PUBLISHABLE_KEY=.*/g,`VITE_OPENFORT_PUBLISHABLE_KEY=${o}`).replace(/NEXT_PUBLIC_OPENFORT_PUBLISHABLE_KEY=.*/g,`NEXT_PUBLIC_OPENFORT_PUBLISHABLE_KEY=${o}`).replace(/OPENFORT_PUBLISHABLE_KEY=.*/g,`OPENFORT_PUBLISHABLE_KEY=${o}`),l=l.replace(/VITE_SHIELD_PUBLISHABLE_KEY=.*/g,`VITE_SHIELD_PUBLISHABLE_KEY=${i}`).replace(/NEXT_PUBLIC_SHIELD_PUBLISHABLE_KEY=.*/g,`NEXT_PUBLIC_SHIELD_PUBLISHABLE_KEY=${i}`).replace(/SHIELD_PUBLISHABLE_KEY=.*/g,`SHIELD_PUBLISHABLE_KEY=${i}`),t&&(l=l.replace(/VITE_CREATE_ENCRYPTED_SESSION_ENDPOINT=.*/g,`VITE_CREATE_ENCRYPTED_SESSION_ENDPOINT=${t}`).replace(/NEXT_PUBLIC_CREATE_ENCRYPTED_SESSION_ENDPOINT=.*/g,`NEXT_PUBLIC_CREATE_ENCRYPTED_SESSION_ENDPOINT=${t}`).replace(/CREATE_ENCRYPTED_SESSION_ENDPOINT=.*/g,`CREATE_ENCRYPTED_SESSION_ENDPOINT=${t}`)),n&&(l=l.replace(/VITE_OPENFORT_THEME=.*/g,`VITE_OPENFORT_THEME=${n}`).replace(/NEXT_PUBLIC_OPENFORT_THEME=.*/g,`NEXT_PUBLIC_OPENFORT_THEME=${n}`).replace(/OPENFORT_THEME=.*/g,`OPENFORT_THEME=${n}`)),$.writeFileSync(s,l)},ze=({openfortPublishableKey:e,shieldPublishableKey:o,apiEndpoint:i,theme:t})=>{let n=`# Openfort Configuration
6
+ `,b="openfort-project",B="create-openfort";var W=["openfort-ui","headless","firebase"],J=["auto","midnight","minimal","soft","web95","rounded","retro","nouns"];import Ne from"node:path";import ke from"fs-extra";var y=()=>{let e=Ne.join(_,"package.json");return ke.readJSONSync(e).version??"unknown"};var T=class extends Error{};var r={error:(...e)=>console.error(...e),warn:(...e)=>console.warn(...e),info:(...e)=>console.log(...e),success:(...e)=>console.log(...e),debug:(...e)=>console.log(...e)};import $e,{createHash as Q}from"node:crypto";import Ce from"node:https";import{hostname as Re,userInfo as Ae}from"node:os";import Be from"dotenv";Be.config();var X=process.env.POSTHOG_API_KEY,q="https://analytics.openfort.xyz",Le=()=>{let e=`${Re()}-${Ae().username}`;return Q("sha256").update(e).digest("hex").slice(0,16)},L=class{constructor(){this.enabled=!0;this.send=async({properties:o={},status:i})=>{if(r.debug("Sending telemetry...",{status:i,properties:o}),!this.enabled)return;if(!X||!q){r.warn("Telemetry is not configured properly. Please contact openfort developers at support@openfort.xyz.");return}let t={session_id:this.sessionId,cli_version:y(),node_version:process.version,platform:process.platform,cli_status:i,projectId:this.projectId,projectName:this.projectName,template:this.template,...o},n=JSON.stringify({api_key:X,event:"cli_tool_used",distinct_id:this.anonymousId,properties:t}),a=new URL(`${q}/capture/`),s={hostname:a.hostname,port:a.port||443,path:a.pathname,method:"POST",headers:{"Content-Type":"application/json","Content-Length":n.length}};return new Promise(c=>{let l=Ce.request(s,d=>{d.on("data",()=>{r.debug("Telemetry request response received",d.statusMessage)}),d.on("end",()=>{c()})});l.on("error",d=>{r.debug("Telemetry request error",d),c()}),l.write(n),l.end()})};this.anonymousId=Le(),this.sessionId=Q("sha256").update($e.randomBytes(16)).digest("hex").slice(0,15)}},g=new L;var x=e=>(e.length>1&&e.endsWith("/")&&(e=e.slice(0,-1)),e);var Ke=/^(?:@[a-z0-9-*~][a-z0-9-*._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/,Z=e=>{let o=x(e),i=o.split("/"),t=i.findIndex(a=>a.startsWith("@")),n=i[i.length-1];if(i.findIndex(a=>a.startsWith("@"))!==-1&&(n=i.slice(t).join("/")),!(o==="."||Ke.test(n??"")))return"App name must consist of only lowercase alphanumeric characters, '-', and '_'"};var ee="[\\da-f]{8}-[\\da-f]{4}-[\\da-f]{4}-[\\da-f]{4}-[\\da-f]{12}",te=`(test|live)_${ee}`,He=new RegExp(`^sk_${te}$`),Ve=new RegExp(`^pk_${te}$`),De=new RegExp(`^${ee}$`),Fe=/^.{44}$/,S=({label:e,required:o=!0,regex:i,formatHint:t,customCheck:n})=>a=>{if(a!=="-"){if(o&&!a)return`${e} is required`;if(i&&a&&!i.test(a))return`${e} is invalid${t?` (${t})`:""}`;if(n)return n(a)}},ne=S({label:"Openfort Publishable Key",regex:Ve,formatHint:"expected format: pk_test_... or pk_live_..."}),oe=S({label:"Openfort Secret Key",regex:He,formatHint:"expected format: sk_test_... or sk_live_..."}),ie=S({label:"Shield Publishable Key",regex:De,formatHint:"expected UUID format"}),re=S({label:"Shield Secret",required:!0}),ae=S({label:"Shield Encryption Share",regex:Fe,formatHint:"expected 44 characters"}),se=S({label:"API endpoint",customCheck:e=>{try{new URL(e);return}catch{return"API endpoint must be a valid URL"}}}),le=async e=>{try{return!!(await(await fetch(e,{method:"POST",headers:{"Content-Type":"application/json"}})).json()).session}catch{return!1}};var Ue={appName:b,template:"openfort-ui",createBackend:!0},pe=async()=>{let e=new Ye().name(B).description("A CLI for creating Openfort applications with embedded wallets").argument("[dir]","The name of the application, as well as the name of the directory to create").option("--noGit","Explicitly tell the CLI to not initialize a new git repo in the project",!1).option("--noInstall","Explicitly tell the CLI to not run the package manager's install command",!1).option("-y, --default","Bypass the CLI and use all default options to bootstrap a new Openfort app",!1).option("--CI","Boolean value if we're running in CI",!1).option("--template [string]","Specify the template to use").option("--theme [string]","Specify the theme to use (for openfort-ui template)").option("--noTelemetry","Disable sending anonymous usage data",!1).addOption(new je("--debug").hideHelp(!0)).version(y(),"-v, --version","Display the version number").addHelpText("afterAll",`
7
+ Learn more about Openfort at ${Ge.hex("#5B87F5").bold("https://www.openfort.xyz")}
8
+ `).parse(process.argv),o=e.args[0],i=e.opts();i.debug||(r.debug=()=>{}),g.enabled=!i.noTelemetry,r.debug("Telemetry enabled:",g.enabled),g.send({status:"started"});let t={...Ue,appName:o||b,flags:{noGit:i.noGit||!1,noInstall:i.noInstall||!1,default:i.default||!1,CI:i.CI||!1,template:i.template,theme:i.theme}};if(t.flags.CI)return t.template=i.template||"openfort-ui",t.theme=i.theme,t.createBackend=!1,t.openfortPublishableKey="pk_test_00000000-0000-0000-0000-000000000000",t.shieldPublishableKey="00000000-0000-0000-0000-000000000000",t;if(t.flags.default)return t.template="openfort-ui",t.createBackend=!1,t.openfortPublishableKey="",t.shieldPublishableKey="",t;try{if(process.env.TERM_PROGRAM?.toLowerCase().includes("mintty"))throw r.warn(` WARNING: It looks like you are using MinTTY, which is non-interactive. This is most likely because you are
9
+ using Git Bash. If that's that case, please use Git Bash from another terminal, such as Windows Terminal.`),new T("Non-interactive environment");let n=await p.group({...!o&&{name:()=>p.text({message:"What will your project be called?",defaultValue:b,validate:Z})},template:()=>p.select({message:"Select an Openfort template:",options:W.map(a=>({value:a,label:a==="openfort-ui"?"Openfort UI (default)":a,hint:Me(a)})),initialValue:"openfort-ui"}),createBackend:()=>p.confirm({message:"Do you want to create a backend for automatic account recovery?",initialValue:!0}),apiEndpoint:async({results:a})=>{if(!a.createBackend){let s=await p.confirm({message:"Do you have an existing API endpoint for account recovery?",initialValue:!1});if(p.isCancel(s)&&process.exit(1),s){let c=!1,l="";for(;!c;){let d=await p.text({message:"Enter your API endpoint for creating encryption sessions:",placeholder:"http://localhost:3110/api/protected-create-encryption-session",validate:se});p.isCancel(d)&&process.exit(1),l=d;let u=p.spinner();if(u.start("Testing API endpoint..."),await le(l))u.stop("API endpoint validated!"),c=!0;else{u.stop("API endpoint validation failed"),r.error("The endpoint did not return a valid session response");let h=await p.confirm({message:"Would you like to try another endpoint?",initialValue:!0});(p.isCancel(h)||!h)&&process.exit(1)}}return l}}},theme:({results:a})=>{if(a.template==="openfort-ui")return p.select({message:"Select a theme:",options:J.map(s=>({value:s,label:s==="auto"?"Auto (default)":s})),initialValue:"auto"})},openfortPublishableKey:()=>p.text({message:"Enter your Openfort Publishable Key:",placeholder:"pk_test_...",validate:ne}),openfortSecretKey:({results:a})=>{if(a.createBackend)return p.text({message:"Enter your Openfort Secret Key:",placeholder:"sk_test_...",validate:oe})},shieldPublishableKey:()=>p.text({message:"Enter your Shield Publishable Key:",placeholder:"00000000-0000-0000-0000-000000000000",validate:ie}),shieldEncryptionShare:({results:a})=>{if(a.createBackend)return p.text({message:"Enter your Shield Encryption Share:",placeholder:"Your 44-character encryption share",validate:ae})},shieldSecretKey:({results:a})=>{if(a.createBackend)return p.text({message:"Enter your Shield Secret Key:",placeholder:"Your Shield Secret",validate:re})},...!t.flags.noGit&&{git:()=>p.confirm({message:"Should we initialize a Git repository and stage the changes?",initialValue:!0})}},{onCancel(){process.exit(1)}});return g.template=n.template,g.projectId=n.openfortPublishableKey,{appName:n.name||t.appName,template:n.template,theme:n.theme,createBackend:n.createBackend,apiEndpoint:n.apiEndpoint,openfortPublishableKey:n.openfortPublishableKey,openfortSecretKey:n.openfortSecretKey,shieldPublishableKey:n.shieldPublishableKey,shieldSecretKey:n.shieldSecretKey,shieldEncryptionShare:n.shieldEncryptionShare,flags:{...t.flags,noGit:!n.git||t.flags.noGit}}}catch(n){if(n instanceof T)return r.warn(`
10
+ ${B} needs an interactive terminal to provide options`),await p.confirm({message:"Continue scaffolding a default Openfort app?",initialValue:!0})||(r.info("Exiting..."),process.exit(0)),r.info(`Bootstrapping a default Openfort app in ./${t.appName}`),{...t,template:"openfort-ui",createBackend:!1,openfortPublishableKey:"",shieldPublishableKey:""};throw n}};function Me(e){switch(e){case"openfort-ui":return"Pre-built UI components";case"headless":return"Custom, unstyled components";case"firebase":return"With Firebase authentication";default:return""}}import ge from"node:path";import N from"node:path";import k from"fs-extra";import ze from"ora";var ce=async({projectDir:e,openfortSecretKey:o,shieldSecretKey:i,shieldApiKey:t,shieldEncryptionShare:n,port:a=3110})=>{let s=ze("Creating backend...").start();try{let c=N.join(_,"template/backend"),l=N.join(e,"backend");k.copySync(c,l);let d=N.join(l,".env.example"),u=N.join(l,".env");if(k.existsSync(d)){let h=k.readFileSync(d,"utf-8").replace(/OPENFORT_SECRET_KEY=.*/g,`OPENFORT_SECRET_KEY=${o}`).replace(/SHIELD_SECRET_KEY=.*/g,`SHIELD_SECRET_KEY=${i}`).replace(/SHIELD_API_KEY=.*/g,`SHIELD_API_KEY=${t}`).replace(/SHIELD_ENCRYPTION_SHARE=.*/g,`SHIELD_ENCRYPTION_SHARE=${n}`).replace(/PORT=.*/g,`PORT=${a}`);k.writeFileSync(u,h)}await new Promise(m=>setTimeout(m,250)),s.succeed("Backend created successfully!")}catch(c){throw s.fail("Failed to create backend"),r.error(c instanceof Error?c.message:String(c)),c}};import de from"node:path";import*as E from"@clack/prompts";import P from"chalk";import O from"fs-extra";import We from"ora";var fe=async({projectName:e,projectDir:o,template:i,createBackend:t})=>{r.info("");let n=We(`Scaffolding in: ${o}...
11
+ `).start();if(O.existsSync(o))if(O.readdirSync(o).length===0)e!=="."&&n.info(`${P.cyan.bold(e)} exists but is empty, continuing...
12
+ `);else{n.stopAndPersist();let l=await E.select({message:`${P.redBright.bold("Warning:")} ${P.cyan.bold(e)} already exists and isn't empty. How would you like to proceed?`,options:[{label:"Abort installation (recommended)",value:"abort"},{label:"Clear the directory and continue installation",value:"clear"},{label:"Continue installation and overwrite conflicting files",value:"overwrite"}],initialValue:"abort"});(E.isCancel(l)||l==="abort")&&(n.fail("Aborting installation..."),process.exit(1));let d=await E.confirm({message:`Are you sure you want to ${l==="clear"?"clear the directory":"overwrite conflicting files"}?`,initialValue:!1});(E.isCancel(d)||!d)&&(n.fail("Aborting installation..."),process.exit(1)),l==="clear"&&(n.info(`Emptying ${P.cyan.bold(e)} and creating Openfort app..
13
+ `),O.emptyDirSync(o))}n.start(),O.mkdirSync(o,{recursive:!0});let a=de.join(_,"template/openfort-templates",i),s=t?de.join(o,"frontend"):o;O.copySync(a,s);let c=e==="."?"App":P.cyan.bold(e);await new Promise(l=>setTimeout(l,250)),n.succeed(`${c} ${P.green("scaffolded successfully!")}
14
+ `)};import me from"node:path";import $ from"fs-extra";var ue=({projectDir:e,openfortPublishableKey:o,shieldPublishableKey:i,apiEndpoint:t,theme:n})=>{let a=me.join(e,".env.example"),s=me.join(e,".env");if(!$.existsSync(a)){let d=Je({openfortPublishableKey:o,shieldPublishableKey:i,apiEndpoint:t,theme:n});$.writeFileSync(s,d);return}let l=$.readFileSync(a,"utf-8");l=l.replace(/VITE_OPENFORT_PUBLISHABLE_KEY=.*/g,`VITE_OPENFORT_PUBLISHABLE_KEY=${o}`).replace(/NEXT_PUBLIC_OPENFORT_PUBLISHABLE_KEY=.*/g,`NEXT_PUBLIC_OPENFORT_PUBLISHABLE_KEY=${o}`).replace(/OPENFORT_PUBLISHABLE_KEY=.*/g,`OPENFORT_PUBLISHABLE_KEY=${o}`),l=l.replace(/VITE_SHIELD_PUBLISHABLE_KEY=.*/g,`VITE_SHIELD_PUBLISHABLE_KEY=${i}`).replace(/NEXT_PUBLIC_SHIELD_PUBLISHABLE_KEY=.*/g,`NEXT_PUBLIC_SHIELD_PUBLISHABLE_KEY=${i}`).replace(/SHIELD_PUBLISHABLE_KEY=.*/g,`SHIELD_PUBLISHABLE_KEY=${i}`),t&&(l=l.replace(/VITE_CREATE_ENCRYPTED_SESSION_ENDPOINT=.*/g,`VITE_CREATE_ENCRYPTED_SESSION_ENDPOINT=${t}`).replace(/NEXT_PUBLIC_CREATE_ENCRYPTED_SESSION_ENDPOINT=.*/g,`NEXT_PUBLIC_CREATE_ENCRYPTED_SESSION_ENDPOINT=${t}`).replace(/CREATE_ENCRYPTED_SESSION_ENDPOINT=.*/g,`CREATE_ENCRYPTED_SESSION_ENDPOINT=${t}`)),n&&(l=l.replace(/VITE_OPENFORT_THEME=.*/g,`VITE_OPENFORT_THEME=${n}`).replace(/NEXT_PUBLIC_OPENFORT_THEME=.*/g,`NEXT_PUBLIC_OPENFORT_THEME=${n}`).replace(/OPENFORT_THEME=.*/g,`OPENFORT_THEME=${n}`)),$.writeFileSync(s,l)},Je=({openfortPublishableKey:e,shieldPublishableKey:o,apiEndpoint:i,theme:t})=>{let n=`# Openfort Configuration
15
15
  VITE_OPENFORT_PUBLISHABLE_KEY=${e}
16
16
  VITE_SHIELD_PUBLISHABLE_KEY=${o}
17
17
  `;return i&&(n+=`VITE_CREATE_ENCRYPTED_SESSION_ENDPOINT=${i}
18
18
  `),t&&(n+=`VITE_OPENFORT_THEME=${t}
19
- `),n};var he=async({projectName:e,template:o,openfortPublishableKey:i,shieldPublishableKey:t,apiEndpoint:n,theme:r,createBackendOption:s,openfortSecretKey:c,shieldSecretKey:l,shieldApiKey:d,shieldEncryptionShare:u})=>{let m=ge.resolve(process.cwd(),e);await de({projectName:e,projectDir:m,template:o,createBackend:s});let g=s?ge.join(m,"frontend"):m;return ue({projectDir:g,openfortPublishableKey:i,shieldPublishableKey:t,apiEndpoint:n,theme:r}),s&&c&&l&&d&&u&&await ce({projectDir:m,openfortSecretKey:c,shieldSecretKey:l,shieldApiKey:d,shieldEncryptionShare:u}),a.info("All done!"),m};import{execSync as H}from"node:child_process";import K from"node:path";import*as V from"@clack/prompts";import w from"chalk";import{execa as I}from"execa";import ye from"fs-extra";import We from"ora";var Je=e=>{try{return H("git --version",{cwd:e}),!0}catch{return!1}},j=e=>ye.existsSync(K.join(e,".git")),D=async e=>{try{return await I("git",["rev-parse","--is-inside-work-tree"],{cwd:e,stdout:"ignore"}),!0}catch{return!1}},Xe=()=>{let o=H("git --version").toString().trim().split(" ")[2],i=o?.split(".")[0],t=o?.split(".")[1];return{major:Number(i),minor:Number(t)}},qe=()=>H("git config --global init.defaultBranch || echo main").toString().trim(),Ee=async e=>{if(a.info("Initializing Git..."),!Je(e)){a.warn("Git is not installed. Skipping Git initialization.");return}let o=We(`Creating a new git repo...
20
- `).start(),i=j(e),t=await D(e),n=K.parse(e).name;if(t&&i){if(o.stop(),!await V.confirm({message:`${w.redBright.bold("Warning:")} Git is already initialized in "${n}". Initializing a new git repository would delete the previous history. Would you like to continue anyways?`,initialValue:!1})){o.info("Skipping Git initialization.");return}ye.removeSync(K.join(e,".git"))}else if(t&&!i&&(o.stop(),!await V.confirm({message:`${w.redBright.bold("Warning:")} "${n}" is already in a git worktree. Would you still like to initialize a new git repository in this directory?`,initialValue:!1}))){o.info("Skipping Git initialization.");return}try{let r=qe(),{major:s,minor:c}=Xe();s<2||s===2&&c<28?(await I("git",["init"],{cwd:e}),await I("git",["symbolic-ref","HEAD",`refs/heads/${r}`],{cwd:e})):await I("git",["init",`--initial-branch=${r}`],{cwd:e}),await I("git",["add","."],{cwd:e}),o.succeed(`${w.green("Successfully initialized and staged")} ${w.green.bold("git")}
19
+ `),n};var he=async({projectName:e,template:o,openfortPublishableKey:i,shieldPublishableKey:t,apiEndpoint:n,theme:a,createBackendOption:s,openfortSecretKey:c,shieldSecretKey:l,shieldApiKey:d,shieldEncryptionShare:u})=>{let m=ge.resolve(process.cwd(),e);await fe({projectName:e,projectDir:m,template:o,createBackend:s});let h=s?ge.join(m,"frontend"):m;return ue({projectDir:h,openfortPublishableKey:i,shieldPublishableKey:t,apiEndpoint:n,theme:a}),s&&c&&l&&d&&u&&await ce({projectDir:m,openfortSecretKey:c,shieldSecretKey:l,shieldApiKey:d,shieldEncryptionShare:u}),r.info("All done!"),m};import{execSync as V}from"node:child_process";import K from"node:path";import*as H from"@clack/prompts";import w from"chalk";import{execa as I}from"execa";import ye from"fs-extra";import Xe from"ora";var qe=e=>{try{return V("git --version",{cwd:e}),!0}catch{return!1}},D=e=>ye.existsSync(K.join(e,".git")),F=async e=>{try{return await I("git",["rev-parse","--is-inside-work-tree"],{cwd:e,stdout:"ignore"}),!0}catch{return!1}},Qe=()=>{let o=V("git --version").toString().trim().split(" ")[2],i=o?.split(".")[0],t=o?.split(".")[1];return{major:Number(i),minor:Number(t)}},Ze=()=>V("git config --global init.defaultBranch || echo main").toString().trim(),Ee=async e=>{if(r.info("Initializing Git..."),!qe(e)){r.warn("Git is not installed. Skipping Git initialization.");return}let o=Xe(`Creating a new git repo...
20
+ `).start(),i=D(e),t=await F(e),n=K.parse(e).name;if(t&&i){if(o.stop(),!await H.confirm({message:`${w.redBright.bold("Warning:")} Git is already initialized in "${n}". Initializing a new git repository would delete the previous history. Would you like to continue anyways?`,initialValue:!1})){o.info("Skipping Git initialization.");return}ye.removeSync(K.join(e,".git"))}else if(t&&!i&&(o.stop(),!await H.confirm({message:`${w.redBright.bold("Warning:")} "${n}" is already in a git worktree. Would you still like to initialize a new git repository in this directory?`,initialValue:!1}))){o.info("Skipping Git initialization.");return}try{let a=Ze(),{major:s,minor:c}=Qe();s<2||s===2&&c<28?(await I("git",["init"],{cwd:e}),await I("git",["symbolic-ref","HEAD",`refs/heads/${a}`],{cwd:e})):await I("git",["init",`--initial-branch=${a}`],{cwd:e}),await I("git",["add","."],{cwd:e}),o.succeed(`${w.green("Successfully initialized and staged")} ${w.green.bold("git")}
21
21
  `)}catch{o.fail(`${w.bold.red("Failed:")} could not initialize git. Update git to the latest version!
22
- `)}};import F from"node:path";import f from"chalk";import U from"fs-extra";var C=()=>{let e=process.env.npm_config_user_agent;return e?e.startsWith("yarn")?"yarn":e.startsWith("pnpm")?"pnpm":e.startsWith("bun")?"bun":"npm":"npm"};var _e=async({projectName:e=S,projectDir:o,createBackend:i})=>{let t=C();if(a.info(`
23
- ${f.green("Success! Your Openfort project is ready.")}`),a.info(`
24
- Next steps:`),e!=="."&&a.info(` ${f.cyan(`cd ${e}`)}`),i){let n=F.join(o,"backend","node_modules"),r=F.join(o,"frontend","node_modules"),s=!U.existsSync(n)||!U.existsSync(r);a.info(`
25
- ${f.yellow("For the backend:")}`),a.info(` ${f.cyan("cd backend")}`),s&&(t==="yarn"?a.info(` ${f.cyan(t)}`):a.info(` ${f.cyan(`${t} install`)}`)),["npm","bun"].includes(t)?a.info(` ${f.cyan(`${t} run dev`)}`):a.info(` ${f.cyan(`${t} dev`)}`),a.info(`
26
- ${f.yellow("For the frontend (in a new terminal):")}`),a.info(` ${f.cyan("cd frontend")}`),s&&(t==="yarn"?a.info(` ${f.cyan(t)}`):a.info(` ${f.cyan(`${t} install`)}`)),["npm","bun"].includes(t)?a.info(` ${f.cyan(`${t} run dev`)}`):a.info(` ${f.cyan(`${t} dev`)}`)}else{let n=F.join(o,"node_modules");!U.existsSync(n)&&(t==="yarn"?a.info(` ${f.cyan(t)}`):a.info(` ${f.cyan(`${t} install`)}`)),["npm","bun"].includes(t)?a.info(` ${f.cyan(`${t} run dev`)}`):a.info(` ${f.cyan(`${t} dev`)}`)}!await D(o)&&!j(o)&&(a.info(`
27
- ${f.cyan("git init")}`),a.info(` ${f.cyan('git commit -m "initial commit"')}`)),a.info(`
28
- ${f.blue("Learn more at https://www.openfort.xyz/docs")}`)};import Se from"node:path";var Pe=e=>{let i=x(e).split("/"),t=i[i.length-1];if(t==="."){let s=Se.resolve(process.cwd());t=Se.basename(s)}let n=i.findIndex(s=>s.startsWith("@"));i.findIndex(s=>s.startsWith("@"))!==-1&&(t=i.slice(n).join("/"));let r=i.filter(s=>!s.startsWith("@")).join("/");return[t,r]};import Ze from"gradient-string";var Qe=["#ffffff","#000000","#ff3b30"],be=()=>{let e=Ze(Qe),o=C();(o==="yarn"||o==="pnpm")&&console.log(""),console.log(e.multiline(z))};import{execSync as et}from"node:child_process";import tt from"node:https";var Te=e=>{let o=y();o.includes("beta")?(a.warn(" You are using a beta version of create-openfort."),a.warn(" Please report any bugs you encounter.")):o.includes("next")?(a.warn(" You are running create-openfort with the @next tag which is no longer maintained."),a.warn(" Please run the CLI with @latest instead.")):o!==e&&(a.warn(" You are using an outdated version of create-openfort."),a.warn(" Your version:",`${o}.`,"Latest version in the npm registry:",e),a.warn(" Please run the CLI with @latest to get the latest updates."))};function nt(){return new Promise((e,o)=>{tt.get("https://registry.npmjs.org/-/package/create-openfort/dist-tags",i=>{if(i.statusCode===200){let t="";i.on("data",n=>{t+=n}),i.on("end",()=>{e(JSON.parse(t).latest)})}else o()}).on("error",()=>{o()})})}var Oe=()=>nt().catch(()=>{try{return et("npm view create-openfort version").toString().trim()}catch{return null}});var ot=async()=>{let e=await Oe();be(),e&&Te(e);let{appName:o,template:i,theme:t,createBackend:n,apiEndpoint:r,openfortPublishableKey:s,openfortSecretKey:c,shieldPublishableKey:l,shieldSecretKey:d,shieldEncryptionShare:u,flags:{noGit:m}}=await pe(),[g,Y]=Pe(o);h.projectName=g;let v=await he({projectName:Y,template:i,openfortPublishableKey:s,shieldPublishableKey:l,apiEndpoint:r||(n?"http://localhost:3110/api/protected-create-encryption-session":void 0),theme:t,createBackendOption:n,openfortSecretKey:c,shieldSecretKey:d,shieldApiKey:l,shieldEncryptionShare:u}),R=n?we.join(v,"frontend","package.json"):we.join(v,"package.json");if(G.existsSync(R)){let A=G.readJSONSync(R);A.name=g,A.openfortMetadata={initVersion:y(),template:i},G.writeJSONSync(R,A,{spaces:2})}m||await Ee(v),await _e({projectName:Y,projectDir:v,createBackend:n}),h.send({status:"completed"}),process.exit(0)};ot().catch(e=>{a.error("Aborting installation..."),e instanceof Error?a.error(e):a.error("An unknown error has occurred. Please open an issue on GitHub with the below:"),h.send({status:"error",properties:{error:e instanceof Error?e.message:String(e)}}),process.exit(1)});
22
+ `)}};import G from"node:path";import f from"chalk";import Y from"fs-extra";var C=()=>{let e=process.env.npm_config_user_agent;return e?e.startsWith("yarn")?"yarn":e.startsWith("pnpm")?"pnpm":e.startsWith("bun")?"bun":"npm":"npm"};var _e=async({projectName:e=b,projectDir:o,createBackend:i})=>{let t=C();if(r.info(`
23
+ ${f.green("Success! Your Openfort project is ready.")}`),r.info(`
24
+ Next steps:`),e!=="."&&r.info(` ${f.cyan(`cd ${e}`)}`),i){let n=G.join(o,"backend","node_modules"),a=G.join(o,"frontend","node_modules"),s=!Y.existsSync(n)||!Y.existsSync(a);r.info(`
25
+ ${f.yellow("For the backend:")}`),r.info(` ${f.cyan("cd backend")}`),s&&(t==="yarn"?r.info(` ${f.cyan(t)}`):r.info(` ${f.cyan(`${t} install`)}`)),["npm","bun"].includes(t)?r.info(` ${f.cyan(`${t} run dev`)}`):r.info(` ${f.cyan(`${t} dev`)}`),r.info(`
26
+ ${f.yellow("For the frontend (in a new terminal):")}`),r.info(` ${f.cyan("cd frontend")}`),s&&(t==="yarn"?r.info(` ${f.cyan(t)}`):r.info(` ${f.cyan(`${t} install`)}`)),["npm","bun"].includes(t)?r.info(` ${f.cyan(`${t} run dev`)}`):r.info(` ${f.cyan(`${t} dev`)}`)}else{let n=G.join(o,"node_modules");!Y.existsSync(n)&&(t==="yarn"?r.info(` ${f.cyan(t)}`):r.info(` ${f.cyan(`${t} install`)}`)),["npm","bun"].includes(t)?r.info(` ${f.cyan(`${t} run dev`)}`):r.info(` ${f.cyan(`${t} dev`)}`)}!await F(o)&&!D(o)&&(r.info(`
27
+ ${f.cyan("git init")}`),r.info(` ${f.cyan('git commit -m "initial commit"')}`)),r.info(`
28
+ ${f.blue("Learn more at https://www.openfort.xyz/docs")}`)};import be from"node:path";var Se=e=>{let i=x(e).split("/"),t=i[i.length-1];if(t==="."){let s=be.resolve(process.cwd());t=be.basename(s)}let n=i.findIndex(s=>s.startsWith("@"));i.findIndex(s=>s.startsWith("@"))!==-1&&(t=i.slice(n).join("/"));let a=i.filter(s=>!s.startsWith("@")).join("/");return[t,a]};import et from"gradient-string";var tt=["#c3c3c3ff","#ff3b30"],Pe=async()=>{let e=et(tt),o=C();(o==="yarn"||o==="pnpm")&&console.log(""),console.log(e.multiline(z))};import{execSync as nt}from"node:child_process";import ot from"node:https";var Te=e=>{let o=y();o.includes("beta")?(r.warn(" You are using a beta version of create-openfort."),r.warn(" Please report any bugs you encounter.")):o.includes("next")?(r.warn(" You are running create-openfort with the @next tag which is no longer maintained."),r.warn(" Please run the CLI with @latest instead.")):o!==e&&(r.warn(" You are using an outdated version of create-openfort."),r.warn(" Your version:",`${o}.`,"Latest version in the npm registry:",e),r.warn(" Please run the CLI with @latest to get the latest updates."))};function it(){return new Promise((e,o)=>{ot.get("https://registry.npmjs.org/-/package/create-openfort/dist-tags",i=>{if(i.statusCode===200){let t="";i.on("data",n=>{t+=n}),i.on("end",()=>{e(JSON.parse(t).latest)})}else o()}).on("error",()=>{o()})})}var Oe=()=>it().catch(()=>{try{return nt("npm view create-openfort version").toString().trim()}catch{return null}});var rt=async()=>{let e=await Oe();await Pe(),e&&Te(e);let{appName:o,template:i,theme:t,createBackend:n,apiEndpoint:a,openfortPublishableKey:s,openfortSecretKey:c,shieldPublishableKey:l,shieldSecretKey:d,shieldEncryptionShare:u,flags:{noGit:m}}=await pe(),[h,U]=Se(o);g.projectName=h;let v=await he({projectName:U,template:i,openfortPublishableKey:s,shieldPublishableKey:l,apiEndpoint:a||(n?"http://localhost:3110/api/protected-create-encryption-session":void 0),theme:t,createBackendOption:n,openfortSecretKey:c,shieldSecretKey:d,shieldApiKey:l,shieldEncryptionShare:u}),R=n?we.join(v,"frontend","package.json"):we.join(v,"package.json");if(j.existsSync(R)){let A=j.readJSONSync(R);A.name=h,A.openfortMetadata={initVersion:y(),template:i},j.writeJSONSync(R,A,{spaces:2})}m||await Ee(v),await _e({projectName:U,projectDir:v,createBackend:n}),g.send({status:"completed"}),process.exit(0)};rt().catch(e=>{r.error("Aborting installation..."),e instanceof Error?r.error(e):r.error("An unknown error has occurred. Please open an issue on GitHub with the below:"),g.send({status:"error",properties:{error:e instanceof Error?e.message:String(e)}}),process.exit(1)});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-openfort",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "Create Openfort applications with embedded wallets",
5
5
  "license": "MIT",
6
6
  "author": "Openfort (https://www.openfort.xyz)",
@@ -46,13 +46,13 @@
46
46
  "ora": "6.3.1"
47
47
  },
48
48
  "devDependencies": {
49
- "@biomejs/biome": "^2.2.4",
50
49
  "@types/fs-extra": "^11.0.4",
51
50
  "@types/gradient-string": "^1.1.6",
52
51
  "@types/node": "^20.14.10",
53
52
  "tsup": "^6.7.0",
54
53
  "type-fest": "^3.13.1",
55
- "typescript": "^5.8.2"
54
+ "typescript": "^5.8.2",
55
+ "dotenv": "^17.2.3"
56
56
  },
57
57
  "scripts": {
58
58
  "check": "tsc && biome check",