@vaiftech/cli 1.7.5 → 1.7.6

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 CHANGED
@@ -3,7 +3,7 @@
3
3
  [![npm version](https://img.shields.io/npm/v/@vaiftech/cli)](https://www.npmjs.com/package/@vaiftech/cli)
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
5
5
 
6
- Command-line tools for [VAIF Studio](https://vaif.studio) (v1.7.5) — scaffold full projects from templates with feature selection, browser-based authentication, manage schemas, deploy functions, generate TypeScript types, and more.
6
+ Command-line tools for [VAIF Studio](https://vaif.studio) (v1.7.6) — scaffold full projects from templates with feature selection, browser-based authentication, manage schemas, deploy functions, generate TypeScript types, and more.
7
7
 
8
8
  ## Installation
9
9
 
@@ -286,6 +286,8 @@ The CLI automatically loads `.env` files from the current directory. You can set
286
286
  | `VAIF_API_URL` | API base URL (default: `https://api.vaif.studio`) |
287
287
  | `VAIF_TOKEN` | API token (alternative to `vaif login`) |
288
288
  | `VAIF_PROJECT_ID` | Default project ID (also read from `vaif.config.json`) |
289
+ | `VITE_VAIF_PROJECT_ID` | Project ID for Vite-based templates (React SPA) |
290
+ | `NEXT_PUBLIC_VAIF_PROJECT_ID` | Project ID for Next.js templates |
289
291
 
290
292
  Project ID resolution order: `--project-id` flag > `vaif.config.json` > `VAIF_PROJECT_ID` env var > login session.
291
293
 
@@ -1,13 +1,13 @@
1
- import y from'fs';import U from'path';import J from'dotenv';import X from'os';import {exec}from'child_process';import P from'ora';import c from'chalk';import q from'readline';import se from'pg';import ce from'prettier';J.config();async function K(a){let t=U.resolve(a);if(!y.existsSync(t))return null;try{let n=y.readFileSync(t,"utf-8"),e=JSON.parse(n);return e.database?.url&&(e.database.url=M(e.database.url)),e.api?.apiKey&&(e.api.apiKey=M(e.api.apiKey)),e}catch{throw new Error(`Failed to parse config file: ${a}`)}}function M(a){return a.replace(/\$\{([^}]+)\}/g,(t,n)=>process.env[n]||t)}var F=U.join(X.homedir(),".vaif"),I=U.join(F,"auth.json"),w=process.env.VAIF_API_URL||"https://api.vaif.studio";function Z(){y.existsSync(F)||y.mkdirSync(F,{recursive:true});}function B(a){Z(),y.writeFileSync(I,JSON.stringify(a,null,2),"utf-8"),y.chmodSync(I,384);}function R(){if(!y.existsSync(I))return null;try{let a=y.readFileSync(I,"utf-8");return JSON.parse(a)}catch{return null}}function ee(a){let t=q.createInterface({input:process.stdin,output:process.stdout});return new Promise(n=>{t.question(a,e=>{t.close(),n(e);});})}function te(a){return new Promise(t=>{let n=q.createInterface({input:process.stdin,output:process.stdout});process.stdin;let r=process.stdout.write.bind(process.stdout),o=false;process.stdout.write=((...l)=>o?true:r(...l)),n.question(a,l=>{o=false,process.stdout.write=r,console.log(""),n.close(),t(l);}),o=true;})}function ae(a){let t=process.platform,n;t==="darwin"?n=`open "${a}"`:t==="win32"?n=`start "" "${a}"`:n=`xdg-open "${a}"`,exec(n,e=>{});}function ne(a){return new Promise(t=>setTimeout(t,a))}async function ie(a){try{let t=await fetch(`${w}/auth/me`,{headers:{Authorization:`Bearer ${a}`}});if(t.ok){let n=await t.json();return {valid:!0,email:n.user?.email||n.email}}return {valid:!1}}catch{return {valid:false}}}async function oe(a){let t=P();t.start("Setting up authentication...");let n,e;try{let u=await fetch(`${w}/auth/cli/authorize`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({})});u.ok||(t.fail("Failed to initiate authentication"),console.log(c.red(`
2
- Could not connect to VAIF API. Please try again later.`)),process.exit(1));let s=await u.json();n=s.code,e=s.url;}catch{t.fail("Failed to connect to VAIF API"),console.log(c.red(`
3
- Could not connect to VAIF API.`)),console.log(c.gray("Check your internet connection or try: vaif login --email")),process.exit(1);}t.stop(),console.log(c.cyan(" Opening browser for authentication...")),console.log(""),console.log(c.gray(" If the browser doesn't open, visit this URL:")),console.log(c.white(` ${e}`)),console.log(""),ae(e),t.start("Waiting for browser authentication...");let r=12e4,o=2e3,l=Date.now();for(;Date.now()-l<r;){await ne(o);try{let u=await fetch(`${w}/auth/cli/token`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({code:n})});if(!u.ok){let i=await u.json();(i.error==="ExpiredCode"||i.error==="InvalidCode")&&(t.fail("Authentication expired"),console.log(c.red(`
4
- The authentication session expired. Please try again.`)),process.exit(1));continue}let s=await u.json();if(s.ok&&s.accessToken){let i={token:s.accessToken,email:s.user?.email,projectId:a,expiresAt:new Date(Date.now()+s.expiresIn*1e3).toISOString()};B(i),t.succeed("Logged in successfully"),console.log(""),s.user?.email&&console.log(c.green(` Authenticated as: ${s.user.email}`)),console.log(c.gray(` Config saved to: ${I}`)),console.log("");return}}catch{}}t.fail("Authentication timed out"),console.log(c.red(`
5
- Timed out waiting for browser authentication.`)),console.log(c.gray("Try again or use: vaif login --email")),process.exit(1);}async function re(a){console.log(""),console.log(c.bold("VAIF CLI Login")),console.log(c.gray("Enter your VAIF account credentials")),console.log("");let t=await ee(c.cyan(" Email: "));(!t||t.trim()==="")&&(console.log(c.red(`
6
- No email provided. Login cancelled.`)),process.exit(1));let n=await te(c.cyan(" Password: "));(!n||n.trim()==="")&&(console.log(c.red(`
7
- No password provided. Login cancelled.`)),process.exit(1));let e=P("Authenticating...").start();try{let r=await fetch(`${w}/auth/cli/login`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({email:t.trim(),password:n})}),o=await r.json();(!r.ok||!o.ok)&&(e.fail("Login failed"),console.log(c.red(`
8
- ${o.message||"Invalid email or password."}`)),process.exit(1));let l={token:o.accessToken,email:o.user?.email,projectId:a,expiresAt:new Date(Date.now()+o.expiresIn*1e3).toISOString()};B(l),e.succeed("Logged in successfully"),console.log(""),o.user?.email&&console.log(c.green(` Authenticated as: ${o.user.email}`)),console.log(c.gray(` Config saved to: ${I}`)),console.log("");}catch{e.fail("Failed to connect to VAIF API"),console.log(c.red(`
9
- Could not connect to VAIF API. Please try again later.`)),process.exit(1);}}async function Le(a){console.log(""),console.log(c.bold("Welcome to VAIF CLI")),console.log(c.gray("Authenticate to access your VAIF projects")),console.log(""),a.email?await re(a.projectId):await oe(a.projectId),console.log(c.gray("You can now use VAIF CLI commands like:")),console.log(c.gray(" vaif pull - Pull remote schema")),console.log(c.gray(" vaif push - Push schema changes")),console.log(c.gray(" vaif generate - Generate TypeScript types")),console.log("");}async function Ne(){y.existsSync(I)?(y.unlinkSync(I),console.log(c.green("Logged out successfully"))):console.log(c.yellow("Not currently logged in"));}async function Ue(){let a=R();(!a||!a.token)&&(console.log(c.yellow("Not logged in")),console.log(c.gray("Run `vaif login` to authenticate")),process.exit(1));let t=P("Checking authentication...").start(),{valid:n,email:e}=await ie(a.token);n||(t.fail("Session expired"),console.log(c.yellow(`
10
- Your session has expired. Please login again.`)),process.exit(1)),t.succeed("Authenticated"),console.log(""),console.log(c.green(` Email: ${e||a.email||"Unknown"}`)),a.projectId&&console.log(c.green(` Project: ${a.projectId}`)),console.log("");}var ue=process.env.VAIF_API_URL||"https://api.vaif.studio";async function de(a,t){let n=await fetch(`${ue}/schema-engine/introspect/${t}`,{headers:{Authorization:`Bearer ${a}`,"Content-Type":"application/json"}});if(!n.ok){let u=await n.text();throw new Error(`API introspection failed: ${u}`)}let e=await n.json();if(!e.ok||!e.schemaExists)throw new Error("Project schema does not exist yet. Push a migration first with `vaif db push`.");let r=new Map,o=[];for(let u of e.tables){let s=u.columns.map(i=>({column_name:i.name,data_type:i.type,is_nullable:i.nullable?"YES":"NO",column_default:i.default,udt_name:i.type,is_identity:i.primaryKey&&i.default?.includes("gen_random_uuid")?"YES":"NO",character_maximum_length:null,numeric_precision:null,numeric_scale:null}));r.set(u.name,s);for(let i of u.foreignKeys)o.push({constraint_name:i.constraintName,table_name:u.name,column_name:i.columnName,foreign_table_name:i.refTable,foreign_column_name:i.refColumn});}return {tables:r,enums:new Map,foreignKeys:o}}async function pe(a,t){let n=await a.query(`
1
+ import b from'fs';import k from'path';import Z from'dotenv';import ee from'os';import {exec}from'child_process';import L from'ora';import l from'chalk';import $ from'readline';import ue from'pg';import pe from'prettier';Z.config();async function q(a){let t=k.resolve(a);if(!b.existsSync(t))return null;try{let n=b.readFileSync(t,"utf-8"),e=JSON.parse(n);return e.database?.url&&(e.database.url=B(e.database.url)),e.api?.apiKey&&(e.api.apiKey=B(e.api.apiKey)),e}catch{throw new Error(`Failed to parse config file: ${a}`)}}function B(a){return a.replace(/\$\{([^}]+)\}/g,(t,n)=>process.env[n]||t)}var C=k.join(ee.homedir(),".vaif"),_=k.join(C,"auth.json"),R=process.env.VAIF_API_URL||"https://api.vaif.studio";function ae(){b.existsSync(C)||b.mkdirSync(C,{recursive:true});}function G(a){ae(),b.writeFileSync(_,JSON.stringify(a,null,2),"utf-8"),b.chmodSync(_,384);}function V(){if(!b.existsSync(_))return null;try{let a=b.readFileSync(_,"utf-8");return JSON.parse(a)}catch{return null}}function ne(a){let t=$.createInterface({input:process.stdin,output:process.stdout});return new Promise(n=>{t.question(a,e=>{t.close(),n(e);});})}function ie(a){return new Promise(t=>{let n=$.createInterface({input:process.stdin,output:process.stdout});process.stdin;let r=process.stdout.write.bind(process.stdout),i=false;process.stdout.write=((...c)=>i?true:r(...c)),n.question(a,c=>{i=false,process.stdout.write=r,console.log(""),n.close(),t(c);}),i=true;})}function oe(a){let t=process.platform,n;t==="darwin"?n=`open "${a}"`:t==="win32"?n=`start "" "${a}"`:n=`xdg-open "${a}"`,exec(n,e=>{});}function re(a){return new Promise(t=>setTimeout(t,a))}async function se(a){try{let t=await fetch(`${R}/auth/me`,{headers:{Authorization:`Bearer ${a}`}});if(t.ok){let n=await t.json();return {valid:!0,email:n.user?.email||n.email}}return {valid:!1}}catch{return {valid:false}}}async function le(a){let t=L();t.start("Setting up authentication...");let n,e;try{let u=await fetch(`${R}/auth/cli/authorize`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({})});u.ok||(t.fail("Failed to initiate authentication"),console.log(l.red(`
2
+ Could not connect to VAIF API. Please try again later.`)),process.exit(1));let o=await u.json();n=o.code,e=o.url;}catch{t.fail("Failed to connect to VAIF API"),console.log(l.red(`
3
+ Could not connect to VAIF API.`)),console.log(l.gray("Check your internet connection or try: vaif login --email")),process.exit(1);}t.stop(),console.log(l.cyan(" Opening browser for authentication...")),console.log(""),console.log(l.gray(" If the browser doesn't open, visit this URL:")),console.log(l.white(` ${e}`)),console.log(""),oe(e),t.start("Waiting for browser authentication...");let r=12e4,i=2e3,c=Date.now();for(;Date.now()-c<r;){await re(i);try{let u=await fetch(`${R}/auth/cli/token`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({code:n})});if(!u.ok){let s=await u.json();(s.error==="ExpiredCode"||s.error==="InvalidCode")&&(t.fail("Authentication expired"),console.log(l.red(`
4
+ The authentication session expired. Please try again.`)),process.exit(1));continue}let o=await u.json();if(o.ok&&o.accessToken){let s={token:o.accessToken,email:o.user?.email,projectId:a,expiresAt:new Date(Date.now()+o.expiresIn*1e3).toISOString()};G(s),t.succeed("Logged in successfully"),console.log(""),o.user?.email&&console.log(l.green(` Authenticated as: ${o.user.email}`)),console.log(l.gray(` Config saved to: ${_}`)),console.log("");return}}catch{}}t.fail("Authentication timed out"),console.log(l.red(`
5
+ Timed out waiting for browser authentication.`)),console.log(l.gray("Try again or use: vaif login --email")),process.exit(1);}async function ce(a){console.log(""),console.log(l.bold("VAIF CLI Login")),console.log(l.gray("Enter your VAIF account credentials")),console.log("");let t=await ne(l.cyan(" Email: "));(!t||t.trim()==="")&&(console.log(l.red(`
6
+ No email provided. Login cancelled.`)),process.exit(1));let n=await ie(l.cyan(" Password: "));(!n||n.trim()==="")&&(console.log(l.red(`
7
+ No password provided. Login cancelled.`)),process.exit(1));let e=L("Authenticating...").start();try{let r=await fetch(`${R}/auth/cli/login`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({email:t.trim(),password:n})}),i=await r.json();(!r.ok||!i.ok)&&(e.fail("Login failed"),console.log(l.red(`
8
+ ${i.message||"Invalid email or password."}`)),process.exit(1));let c={token:i.accessToken,email:i.user?.email,projectId:a,expiresAt:new Date(Date.now()+i.expiresIn*1e3).toISOString()};G(c),e.succeed("Logged in successfully"),console.log(""),i.user?.email&&console.log(l.green(` Authenticated as: ${i.user.email}`)),console.log(l.gray(` Config saved to: ${_}`)),console.log("");}catch{e.fail("Failed to connect to VAIF API"),console.log(l.red(`
9
+ Could not connect to VAIF API. Please try again later.`)),process.exit(1);}}async function Ue(a){console.log(""),console.log(l.bold("Welcome to VAIF CLI")),console.log(l.gray("Authenticate to access your VAIF projects")),console.log(""),a.email?await ce(a.projectId):await le(a.projectId),console.log(l.gray("You can now use VAIF CLI commands like:")),console.log(l.gray(" vaif pull - Pull remote schema")),console.log(l.gray(" vaif push - Push schema changes")),console.log(l.gray(" vaif generate - Generate TypeScript types")),console.log("");}async function De(){b.existsSync(_)?(b.unlinkSync(_),console.log(l.green("Logged out successfully"))):console.log(l.yellow("Not currently logged in"));}async function ke(){let a=V();(!a||!a.token)&&(console.log(l.yellow("Not logged in")),console.log(l.gray("Run `vaif login` to authenticate")),process.exit(1));let t=L("Checking authentication...").start(),{valid:n,email:e}=await se(a.token);n||(t.fail("Session expired"),console.log(l.yellow(`
10
+ Your session has expired. Please login again.`)),process.exit(1)),t.succeed("Authenticated"),console.log(""),console.log(l.green(` Email: ${e||a.email||"Unknown"}`)),a.projectId&&console.log(l.green(` Project: ${a.projectId}`)),console.log("");}var me=process.env.VAIF_API_URL||"https://api.vaif.studio";async function fe(a,t){let n=await fetch(`${me}/schema-engine/introspect/${t}`,{headers:{Authorization:`Bearer ${a}`,"Content-Type":"application/json"}});if(!n.ok){let u=await n.text();throw new Error(`API introspection failed: ${u}`)}let e=await n.json();if(!e.ok||!e.schemaExists)throw new Error("Project schema does not exist yet. Push a migration first with `vaif db push`.");let r=new Map,i=[];for(let u of e.tables){let o=u.columns.map(s=>({column_name:s.name,data_type:s.type,is_nullable:s.nullable?"YES":"NO",column_default:s.default,udt_name:s.type,is_identity:s.primaryKey&&s.default?.includes("gen_random_uuid")?"YES":"NO",character_maximum_length:null,numeric_precision:null,numeric_scale:null}));r.set(u.name,o);for(let s of u.foreignKeys)i.push({constraint_name:s.constraintName,table_name:u.name,column_name:s.columnName,foreign_table_name:s.refTable,foreign_column_name:s.refColumn});}return {tables:r,enums:new Map,foreignKeys:i}}async function ge(a,t){let n=await a.query(`
11
11
  SELECT table_name, table_type
12
12
  FROM information_schema.tables
13
13
  WHERE table_schema = $1
@@ -44,7 +44,7 @@ Your session has expired. Please login again.`)),process.exit(1)),t.succeed("Aut
44
44
  AND ccu.table_schema = tc.table_schema
45
45
  WHERE tc.constraint_type = 'FOREIGN KEY'
46
46
  AND tc.table_schema = $1
47
- `,[t]),o=await a.query(`
47
+ `,[t]),i=await a.query(`
48
48
  SELECT
49
49
  t.typname as enum_name,
50
50
  e.enumlabel as enum_value
@@ -53,24 +53,24 @@ Your session has expired. Please login again.`)),process.exit(1)),t.succeed("Aut
53
53
  JOIN pg_namespace n ON n.oid = t.typnamespace
54
54
  WHERE n.nspname = $1
55
55
  ORDER BY t.typname, e.enumsortorder
56
- `,[t]),l=new Map;for(let s of n.rows)l.set(s.table_name,[]);for(let s of e.rows){let i=l.get(s.table_name);i&&i.push(s);}let u=new Map;for(let s of o.rows){let i=u.get(s.enum_name)||[];i.push(s.enum_value),u.set(s.enum_name,i);}return {tables:l,enums:u,foreignKeys:r.rows}}var L={smallint:"number",integer:"number",bigint:"string",int2:"number",int4:"number",int8:"string",decimal:"string",numeric:"string",real:"number",float4:"number",float8:"number","double precision":"number",money:"string",boolean:"boolean",bool:"boolean",text:"string",varchar:"string",char:"string",character:"string","character varying":"string",name:"string",citext:"string",date:"string",time:"string",timetz:"string","time without time zone":"string","time with time zone":"string",timestamp:"string",timestamptz:"string","timestamp without time zone":"string","timestamp with time zone":"string",interval:"string",bytea:"Buffer",uuid:"string",json:"unknown",jsonb:"unknown",inet:"string",cidr:"string",macaddr:"string",macaddr8:"string",point:"{ x: number; y: number }",line:"string",lseg:"string",box:"string",path:"string",polygon:"string",circle:"string",ARRAY:"unknown[]"};function me(a,t){let{data_type:n,udt_name:e,is_nullable:r}=a;if(t.has(e)){let u=t.get(e).map(s=>`"${s}"`).join(" | ");return r==="YES"?`(${u}) | null`:u}if(n==="ARRAY"){let l=e.replace(/^_/,"");if(t.has(l)){let i=t.get(l).map(p=>`"${p}"`).join(" | ");return r==="YES"?`(${i})[] | null`:`(${i})[]`}let u=L[l]||"unknown";return r==="YES"?`${u}[] | null`:`${u}[]`}let o=L[n]||L[e]||"unknown";return r==="YES"&&(o=`${o} | null`),o}function N(a){return a.split(/[_\-\s]+/).map(t=>t.charAt(0).toUpperCase()+t.slice(1).toLowerCase()).join("")}function fe(a,t){let n=N(a),e=t.map(r=>` | "${r}"`).join(`
56
+ `,[t]),c=new Map;for(let o of n.rows)c.set(o.table_name,[]);for(let o of e.rows){let s=c.get(o.table_name);s&&s.push(o);}let u=new Map;for(let o of i.rows){let s=u.get(o.enum_name)||[];s.push(o.enum_value),u.set(o.enum_name,s);}return {tables:c,enums:u,foreignKeys:r.rows}}var U={smallint:"number",integer:"number",bigint:"string",int2:"number",int4:"number",int8:"string",decimal:"string",numeric:"string",real:"number",float4:"number",float8:"number","double precision":"number",money:"string",boolean:"boolean",bool:"boolean",text:"string",varchar:"string",char:"string",character:"string","character varying":"string",name:"string",citext:"string",date:"string",time:"string",timetz:"string","time without time zone":"string","time with time zone":"string",timestamp:"string",timestamptz:"string","timestamp without time zone":"string","timestamp with time zone":"string",interval:"string",bytea:"Buffer",uuid:"string",json:"unknown",jsonb:"unknown",inet:"string",cidr:"string",macaddr:"string",macaddr8:"string",point:"{ x: number; y: number }",line:"string",lseg:"string",box:"string",path:"string",polygon:"string",circle:"string",ARRAY:"unknown[]"};function he(a,t){let{data_type:n,udt_name:e,is_nullable:r}=a;if(t.has(e)){let u=t.get(e).map(o=>`"${o}"`).join(" | ");return r==="YES"?`(${u}) | null`:u}if(n==="ARRAY"){let c=e.replace(/^_/,"");if(t.has(c)){let s=t.get(c).map(f=>`"${f}"`).join(" | ");return r==="YES"?`(${s})[] | null`:`(${s})[]`}let u=U[c]||"unknown";return r==="YES"?`${u}[] | null`:`${u}[]`}let i=U[n]||U[e]||"unknown";return r==="YES"&&(i=`${i} | null`),i}function D(a){return a.split(/[_\-\s]+/).map(t=>t.charAt(0).toUpperCase()+t.slice(1).toLowerCase()).join("")}function ve(a,t){let n=D(a),e=t.map(r=>` | "${r}"`).join(`
57
57
  `);return `export type ${n} =
58
- ${e};`}function ge(a,t,n){let e=N(a),r=[],o=[],l=[];for(let p of t){let g=me(p,n),m=p.column_name,h=p.column_default!==null||p.is_identity==="YES",A=p.is_nullable==="YES";r.push(` ${m}: ${g};`),h||p.column_name==="id"?o.push(` ${m}?: ${g.replace(" | null","")} | null;`):A?o.push(` ${m}?: ${g};`):o.push(` ${m}: ${g.replace(" | null","")};`),l.push(` ${m}?: ${g.replace(" | null","")} | null;`);}let u=`export interface ${e} {
58
+ ${e};`}function ye(a,t,n){let e=D(a),r=[],i=[],c=[];for(let f of t){let h=he(f,n),d=f.column_name,m=f.column_default!==null||f.is_identity==="YES",I=f.is_nullable==="YES";r.push(` ${d}: ${h};`),m||f.column_name==="id"?i.push(` ${d}?: ${h.replace(" | null","")} | null;`):I?i.push(` ${d}?: ${h};`):i.push(` ${d}: ${h.replace(" | null","")};`),c.push(` ${d}?: ${h.replace(" | null","")} | null;`);}let u=`export interface ${e} {
59
59
  ${r.join(`
60
60
  `)}
61
- }`,s=`export interface ${e}Insert {
62
- ${o.join(`
61
+ }`,o=`export interface ${e}Insert {
62
+ ${i.join(`
63
63
  `)}
64
- }`,i=`export interface ${e}Update {
65
- ${l.join(`
64
+ }`,s=`export interface ${e}Update {
65
+ ${c.join(`
66
66
  `)}
67
- }`;return {base:u,insert:s,update:i}}function he(a,t,n){let e=["/**"," * Auto-generated TypeScript types from database schema"," * Generated by @vaiftech/cli",` * Generated at: ${new Date().toISOString()}`," * "," * DO NOT EDIT MANUALLY - changes will be overwritten"," */",""];if(t.size>0){e.push("// ============ ENUMS ============"),e.push("");for(let[o,l]of t)e.push(fe(o,l)),e.push("");}e.push("// ============ TABLES ============"),e.push("");let r=[];for(let[o,l]of a){let{base:u,insert:s,update:i}=ge(o,l,t);r.push(o),e.push(u),e.push(""),e.push(s),e.push(""),e.push(i),e.push("");}e.push("// ============ DATABASE SCHEMA ============"),e.push(""),e.push("export interface Database {");for(let o of r){let l=N(o);e.push(` ${o}: {`),e.push(` Row: ${l};`),e.push(` Insert: ${l}Insert;`),e.push(` Update: ${l}Update;`),e.push(" };");}return e.push("}"),e.push(""),e.push("export type TableName = keyof Database;"),e.push(""),e.push("// ============ HELPER TYPES ============"),e.push(""),e.push('export type Row<T extends TableName> = Database[T]["Row"];'),e.push('export type Insert<T extends TableName> = Database[T]["Insert"];'),e.push('export type Update<T extends TableName> = Database[T]["Update"];'),e.push(""),e.join(`
68
- `)}async function Ye(a){let t=P("Loading configuration...").start();try{let n=await K(a.config),e=a.connection||n?.database?.url||process.env.DATABASE_URL,r=e&&!e.includes("${"),o,l,u;if(r){t.text="Connecting to database...";let m=new se.Client({connectionString:e});await m.connect(),t.text="Introspecting schema...",{tables:o,enums:l,foreignKeys:u}=await pe(m,a.schema),await m.end();}else {let m=R();(!m||!m.token)&&(t.fail("No database connection and not logged in"),console.log(c.yellow(`
69
- Either:`)),console.log(c.gray(" 1. Run `vaif login` to authenticate (no DATABASE_URL needed)")),console.log(c.gray(" 2. Set DATABASE_URL in your .env file")),console.log(c.gray(" 3. Pass --connection postgresql://user:pass@host:5432/db")),process.exit(1));let h=n?.projectId||process.env.VAIF_PROJECT_ID||m.projectId;h||(t.fail("No project ID specified"),console.log(c.yellow(`
70
- Set projectId in vaif.config.json or use VAIF_PROJECT_ID env var.`)),process.exit(1)),t.text="Introspecting schema via API...",{tables:o,enums:l,foreignKeys:u}=await de(m.token,h);}if(o.size===0){t.warn("No tables found"),console.log(c.yellow(`
71
- Push a migration first: vaif db push`));return}t.text=`Generating types for ${o.size} tables...`;let s=he(o,l,u),i=await ce.format(s,{parser:"typescript",semi:!0,singleQuote:!1,trailingComma:"es5",printWidth:100});if(a.dryRun){t.succeed("Generated types (dry run):"),console.log(""),console.log(c.gray("\u2500".repeat(60))),console.log(i),console.log(c.gray("\u2500".repeat(60)));return}let p=U.resolve(a.output),g=U.dirname(p);y.existsSync(g)||y.mkdirSync(g,{recursive:!0}),y.writeFileSync(p,i,"utf-8"),t.succeed(`Generated types for ${o.size} tables \u2192 ${c.cyan(a.output)}`),console.log(""),console.log(c.green("Generated:")),console.log(c.gray(` Tables: ${o.size}`)),console.log(c.gray(` Enums: ${l.size}`)),console.log(""),console.log(c.gray("Import in your code:")),console.log(c.cyan(` import type { Database, Row, Insert, Update } from "${a.output.replace(/\.ts$/,"")}";`));}catch(n){t.fail("Failed to generate types"),n instanceof Error&&(console.error(c.red(`
72
- Error: ${n.message}`)),n.message.includes("ECONNREFUSED")&&console.log(c.yellow(`
73
- Make sure your database is running and accessible.`))),process.exit(1);}}var _=[{name:"database",label:"Database",description:"CRUD queries, type-safe operations"},{name:"auth",label:"Authentication",description:"login, signup, OAuth, sessions"},{name:"realtime",label:"Realtime",description:"live subscriptions, presence"},{name:"storage",label:"Storage",description:"file uploads, signed URLs"},{name:"functions",label:"Functions",description:"serverless function calls"}],G={"nextjs-fullstack":{name:"Next.js Full-Stack",description:"Next.js app with server/client VAIF client, auth middleware, and React hooks",tag:"Next.js",defaultFeatures:["database","auth"],files:[{path:"package.json",content:`{
67
+ }`;return {base:u,insert:o,update:s}}function be(a,t,n){let e=["/**"," * Auto-generated TypeScript types from database schema"," * Generated by @vaiftech/cli",` * Generated at: ${new Date().toISOString()}`," * "," * DO NOT EDIT MANUALLY - changes will be overwritten"," */",""];if(t.size>0){e.push("// ============ ENUMS ============"),e.push("");for(let[i,c]of t)e.push(ve(i,c)),e.push("");}e.push("// ============ TABLES ============"),e.push("");let r=[];for(let[i,c]of a){let{base:u,insert:o,update:s}=ye(i,c,t);r.push(i),e.push(u),e.push(""),e.push(o),e.push(""),e.push(s),e.push("");}e.push("// ============ DATABASE SCHEMA ============"),e.push(""),e.push("export interface Database {");for(let i of r){let c=D(i);e.push(` ${i}: {`),e.push(` Row: ${c};`),e.push(` Insert: ${c}Insert;`),e.push(` Update: ${c}Update;`),e.push(" };");}return e.push("}"),e.push(""),e.push("export type TableName = keyof Database;"),e.push(""),e.push("// ============ HELPER TYPES ============"),e.push(""),e.push('export type Row<T extends TableName> = Database[T]["Row"];'),e.push('export type Insert<T extends TableName> = Database[T]["Insert"];'),e.push('export type Update<T extends TableName> = Database[T]["Update"];'),e.push(""),e.join(`
68
+ `)}async function He(a){let t=L("Loading configuration...").start();try{let n=await q(a.config),e=a.connection||n?.database?.url||process.env.DATABASE_URL,r=e&&!e.includes("${"),i,c,u;if(r){t.text="Connecting to database...";let d=new ue.Client({connectionString:e});await d.connect(),t.text="Introspecting schema...",{tables:i,enums:c,foreignKeys:u}=await ge(d,a.schema),await d.end();}else {let d=V();(!d||!d.token)&&(t.fail("No database connection and not logged in"),console.log(l.yellow(`
69
+ Either:`)),console.log(l.gray(" 1. Run `vaif login` to authenticate (no DATABASE_URL needed)")),console.log(l.gray(" 2. Set DATABASE_URL in your .env file")),console.log(l.gray(" 3. Pass --connection postgresql://user:pass@host:5432/db")),process.exit(1));let m=n?.projectId||process.env.VAIF_PROJECT_ID||d.projectId;m||(t.fail("No project ID specified"),console.log(l.yellow(`
70
+ Set projectId in vaif.config.json or use VAIF_PROJECT_ID env var.`)),process.exit(1)),t.text="Introspecting schema via API...",{tables:i,enums:c,foreignKeys:u}=await fe(d.token,m);}if(i.size===0){t.warn("No tables found"),console.log(l.yellow(`
71
+ Push a migration first: vaif db push`));return}t.text=`Generating types for ${i.size} tables...`;let o=be(i,c,u),s=await pe.format(o,{parser:"typescript",semi:!0,singleQuote:!1,trailingComma:"es5",printWidth:100});if(a.dryRun){t.succeed("Generated types (dry run):"),console.log(""),console.log(l.gray("\u2500".repeat(60))),console.log(s),console.log(l.gray("\u2500".repeat(60)));return}let f=k.resolve(a.output),h=k.dirname(f);b.existsSync(h)||b.mkdirSync(h,{recursive:!0}),b.writeFileSync(f,s,"utf-8"),t.succeed(`Generated types for ${i.size} tables \u2192 ${l.cyan(a.output)}`),console.log(""),console.log(l.green("Generated:")),console.log(l.gray(` Tables: ${i.size}`)),console.log(l.gray(` Enums: ${c.size}`)),console.log(""),console.log(l.gray("Import in your code:")),console.log(l.cyan(` import type { Database, Row, Insert, Update } from "${a.output.replace(/\.ts$/,"")}";`));}catch(n){t.fail("Failed to generate types"),n instanceof Error&&(console.error(l.red(`
72
+ Error: ${n.message}`)),n.message.includes("ECONNREFUSED")&&console.log(l.yellow(`
73
+ Make sure your database is running and accessible.`))),process.exit(1);}}var T=[{name:"database",label:"Database",description:"CRUD queries, type-safe operations"},{name:"auth",label:"Authentication",description:"login, signup, OAuth, sessions"},{name:"realtime",label:"Realtime",description:"live subscriptions, presence"},{name:"storage",label:"Storage",description:"file uploads, signed URLs"},{name:"functions",label:"Functions",description:"serverless function calls"}],W={"nextjs-fullstack":{name:"Next.js Full-Stack",description:"Next.js app with server/client VAIF client, auth middleware, and React hooks",tag:"Next.js",defaultFeatures:["database","auth"],files:[{path:"package.json",content:`{
74
74
  "name": "my-vaif-app",
75
75
  "private": true,
76
76
  "version": "0.1.0",
@@ -167,6 +167,7 @@ body {
167
167
  // Browser client \u2013 safe to use in Client Components
168
168
  export const vaif = createVaifClient({
169
169
  baseUrl: process.env.NEXT_PUBLIC_VAIF_API_URL || 'https://api.vaif.studio',
170
+ projectId: process.env.NEXT_PUBLIC_VAIF_PROJECT_ID,
170
171
  apiKey: process.env.NEXT_PUBLIC_VAIF_API_KEY!,
171
172
  });
172
173
 
@@ -174,6 +175,7 @@ export const vaif = createVaifClient({
174
175
  export function createVaifServer() {
175
176
  return createVaifClient({
176
177
  baseUrl: process.env.NEXT_PUBLIC_VAIF_API_URL || 'https://api.vaif.studio',
178
+ projectId: process.env.VAIF_PROJECT_ID,
177
179
  apiKey: process.env.VAIF_SECRET_KEY!,
178
180
  });
179
181
  }
@@ -181,6 +183,7 @@ export function createVaifServer() {
181
183
  # Get these values from https://vaif.studio/app/security/api-keys \u2192 Project Settings \u2192 API Keys
182
184
 
183
185
  NEXT_PUBLIC_VAIF_API_URL=https://api.vaif.studio
186
+ NEXT_PUBLIC_VAIF_PROJECT_ID=your-project-id
184
187
  NEXT_PUBLIC_VAIF_API_KEY=your-api-key
185
188
  VAIF_SECRET_KEY=your-secret-key
186
189
 
@@ -314,6 +317,7 @@ export async function middleware(request: NextRequest) {
314
317
 
315
318
  const vaif = createVaifClient({
316
319
  baseUrl: process.env.NEXT_PUBLIC_VAIF_API_URL || 'https://api.vaif.studio',
320
+ projectId: process.env.VAIF_PROJECT_ID,
317
321
  apiKey: process.env.VAIF_SECRET_KEY!,
318
322
  });
319
323
 
@@ -676,12 +680,14 @@ function Home() {
676
680
 
677
681
  export const vaif = createVaifClient({
678
682
  baseUrl: import.meta.env.VITE_VAIF_API_URL || 'https://api.vaif.studio',
683
+ projectId: import.meta.env.VITE_VAIF_PROJECT_ID,
679
684
  apiKey: import.meta.env.VITE_VAIF_API_KEY,
680
685
  });
681
686
  `},{path:"src/vite-env.d.ts",content:`/// <reference types="vite/client" />
682
687
 
683
688
  interface ImportMetaEnv {
684
689
  readonly VITE_VAIF_API_URL: string;
690
+ readonly VITE_VAIF_PROJECT_ID: string;
685
691
  readonly VITE_VAIF_API_KEY: string;
686
692
  }
687
693
 
@@ -692,6 +698,7 @@ interface ImportMeta {
692
698
  # Get these values from https://vaif.studio/app/security/api-keys
693
699
 
694
700
  VITE_VAIF_API_URL=https://api.vaif.studio
701
+ VITE_VAIF_PROJECT_ID=your-project-id
695
702
  VITE_VAIF_API_KEY=your-api-key
696
703
  `},{path:".gitignore",content:`node_modules
697
704
  dist
@@ -2692,6 +2699,7 @@ func HelloHandler(w http.ResponseWriter, r *http.Request) {
2692
2699
 
2693
2700
  export const vaif = createVaifClient({
2694
2701
  baseUrl: import.meta.env.VITE_VAIF_API_URL || 'https://api.vaif.studio',
2702
+ projectId: import.meta.env.VITE_VAIF_PROJECT_ID,
2695
2703
  apiKey: import.meta.env.VITE_VAIF_API_KEY,
2696
2704
  });
2697
2705
 
@@ -2746,6 +2754,7 @@ export async function deleteTodo(id: string): Promise<void> {
2746
2754
  # Get these values from https://vaif.studio/app/security/api-keys
2747
2755
 
2748
2756
  VITE_VAIF_API_URL=https://api.vaif.studio
2757
+ VITE_VAIF_PROJECT_ID=your-project-id
2749
2758
  VITE_VAIF_API_KEY=your-api-key
2750
2759
  `},{path:"README.md",content:`# Todo App \u2014 VAIF Starter
2751
2760
 
@@ -2883,6 +2892,7 @@ export const posts = pgTable("posts", {
2883
2892
 
2884
2893
  export const vaif = createVaifClient({
2885
2894
  baseUrl: import.meta.env.VITE_VAIF_API_URL || 'https://api.vaif.studio',
2895
+ projectId: import.meta.env.VITE_VAIF_PROJECT_ID,
2886
2896
  apiKey: import.meta.env.VITE_VAIF_API_KEY,
2887
2897
  });
2888
2898
 
@@ -3024,6 +3034,7 @@ export function useRealtimeMessages({
3024
3034
  # Get these values from https://vaif.studio/app/security/api-keys
3025
3035
 
3026
3036
  VITE_VAIF_API_URL=https://api.vaif.studio
3037
+ VITE_VAIF_PROJECT_ID=your-project-id
3027
3038
  VITE_VAIF_API_KEY=your-api-key
3028
3039
  `},{path:"README.md",content:`# Realtime Chat \u2014 VAIF Starter
3029
3040
 
@@ -3176,6 +3187,7 @@ export const posts = pgTable("posts", {
3176
3187
  // Browser client \u2013 use in Client Components
3177
3188
  export const vaif = createVaifClient({
3178
3189
  baseUrl: process.env.NEXT_PUBLIC_VAIF_API_URL || 'https://api.vaif.studio',
3190
+ projectId: process.env.NEXT_PUBLIC_VAIF_PROJECT_ID,
3179
3191
  apiKey: process.env.NEXT_PUBLIC_VAIF_API_KEY!,
3180
3192
  });
3181
3193
 
@@ -3183,6 +3195,7 @@ export const vaif = createVaifClient({
3183
3195
  export function createVaifServer() {
3184
3196
  return createVaifClient({
3185
3197
  baseUrl: process.env.NEXT_PUBLIC_VAIF_API_URL || 'https://api.vaif.studio',
3198
+ projectId: process.env.VAIF_PROJECT_ID,
3186
3199
  apiKey: process.env.VAIF_SECRET_KEY!,
3187
3200
  });
3188
3201
  }
@@ -3337,6 +3350,7 @@ export async function requireTeamRole(
3337
3350
  # Get these values from https://vaif.studio/app/security/api-keys \u2192 Project Settings \u2192 API Keys
3338
3351
 
3339
3352
  NEXT_PUBLIC_VAIF_API_URL=https://api.vaif.studio
3353
+ NEXT_PUBLIC_VAIF_PROJECT_ID=your-project-id
3340
3354
  NEXT_PUBLIC_VAIF_API_KEY=your-api-key
3341
3355
  VAIF_SECRET_KEY=your-secret-key
3342
3356
 
@@ -3489,6 +3503,7 @@ export const posts = pgTable("posts", {
3489
3503
  // Browser client
3490
3504
  export const vaif = createVaifClient({
3491
3505
  baseUrl: process.env.NEXT_PUBLIC_VAIF_API_URL || 'https://api.vaif.studio',
3506
+ projectId: process.env.NEXT_PUBLIC_VAIF_PROJECT_ID,
3492
3507
  apiKey: process.env.NEXT_PUBLIC_VAIF_API_KEY!,
3493
3508
  });
3494
3509
 
@@ -3496,6 +3511,7 @@ export const vaif = createVaifClient({
3496
3511
  export function createVaifServer() {
3497
3512
  return createVaifClient({
3498
3513
  baseUrl: process.env.NEXT_PUBLIC_VAIF_API_URL || 'https://api.vaif.studio',
3514
+ projectId: process.env.VAIF_PROJECT_ID,
3499
3515
  apiKey: process.env.VAIF_SECRET_KEY!,
3500
3516
  });
3501
3517
  }
@@ -3612,6 +3628,7 @@ function getContentType(fileName: string): string {
3612
3628
  # Get these values from https://vaif.studio/app/security/api-keys \u2192 Project Settings \u2192 API Keys
3613
3629
 
3614
3630
  NEXT_PUBLIC_VAIF_API_URL=https://api.vaif.studio
3631
+ NEXT_PUBLIC_VAIF_PROJECT_ID=your-project-id
3615
3632
  NEXT_PUBLIC_VAIF_API_KEY=your-api-key
3616
3633
  VAIF_SECRET_KEY=your-secret-key
3617
3634
 
@@ -3757,16 +3774,16 @@ export const posts = pgTable("posts", {
3757
3774
 
3758
3775
  return Response.json({ message: \`Hello, \${name}!\` });
3759
3776
  }
3760
- `}]},dependencies:["@vaiftech/client","@vaiftech/auth"],postInstructions:["Copy .env.example to .env.local and fill in your project credentials","Create a 'product-images' storage bucket in your VAIF dashboard","Import storage helpers from '@/lib/storage' in your API routes","Use uploadProductImage() in your product creation flow","Run: npx vaif generate to generate TypeScript types"]}};function Qe(){console.log(""),console.log(c.bold("Available project templates")),console.log("");let a=26,t=22;console.log(` ${c.gray("Template".padEnd(a))}${c.gray("Stack".padEnd(t))}${c.gray("Description")}`),console.log(c.gray(" "+"-".repeat(a+t+40)));for(let[n,e]of Object.entries(G))console.log(` ${c.cyan(n.padEnd(a))}${c.yellow(e.tag.padEnd(t))}${c.white(e.description)}`);console.log(""),console.log(c.gray("Usage:")),console.log(c.gray(" npx @vaiftech/cli init --template <name>")),console.log(c.gray(" npx @vaiftech/cli init -t nextjs-fullstack")),console.log(c.gray(" npx @vaiftech/cli init -t react-spa --features auth,database,realtime")),console.log(""),console.log(c.gray("Available features: auth, database, realtime, storage, functions")),console.log("");}async function ve(a){if(!process.stdin.isTTY||!process.stdout.isTTY)return a;let t=new Set(a.map(e=>_.findIndex(r=>r.name===e)).filter(e=>e>=0)),n=0;return new Promise(e=>{let r=q.createInterface({input:process.stdin,output:process.stdout});q.emitKeypressEvents(process.stdin,r),process.stdin.setRawMode&&process.stdin.setRawMode(true);function o(){let u=_.length+2;process.stdout.write(`\x1B[${u}A`),l();}function l(){console.log(c.bold(`
3761
- ? Which VAIF features do you want to include?`)),_.forEach((u,s)=>{let i=t.has(s)?c.green("[x]"):"[ ]",p=s===n?c.cyan("> "):" ";console.log(`${p}${i} ${u.label} ${c.gray(`(${u.description})`)}`);}),console.log(c.gray(" (up/down to move, space to toggle, enter to confirm)"));}l(),process.stdin.on("keypress",(u,s)=>{if(s.name==="up"&&n>0)n--,o();else if(s.name==="down"&&n<_.length-1)n++,o();else if(s.name==="space")t.has(n)?t.delete(n):t.add(n),o();else if(s.name==="return"){process.stdin.setRawMode&&process.stdin.setRawMode(false),r.close();let i=[...t].sort().map(p=>_[p].name);e(i.length>0?i:a);}else s.name==="c"&&s.ctrl&&(process.stdin.setRawMode&&process.stdin.setRawMode(false),r.close(),process.exit(0));});})}async function V(a,t={}){let n=G[a];n||(console.log(c.red(`
3762
- Unknown template: ${a}`)),console.log(c.yellow(`Run 'vaif templates' to see available templates.
3763
- `)),process.exit(1));let e;t.features&&t.features.length>0?e=t.features.filter(i=>_.some(p=>p.name===i)):t.addOnly?(console.log(c.red(`
3764
- No features specified.`)),console.log(c.yellow("Usage: vaif init --template <name> --add-features <features>")),console.log(c.gray("Available features: auth, database, realtime, storage, functions")),process.exit(1)):n.featureFiles&&Object.keys(n.featureFiles).length>0?e=await ve(n.defaultFeatures??["database","auth"]):e=n.defaultFeatures??[],t.addOnly?(console.log(""),console.log(c.bold(`Adding features to ${c.cyan(n.name)} project...`)),console.log(c.gray(` Features: ${e.join(", ")}`)),console.log("")):(console.log(""),console.log(c.bold(`Scaffolding ${c.cyan(n.name)} template...`)),e.length>0&&console.log(c.gray(` Features: ${e.join(", ")}`)),console.log(""));let r=t.addOnly?[]:[...n.files];if(n.featureFiles)for(let i of e){let p=n.featureFiles[i];p&&r.push(...p);}let o=0,l=0;for(let i of r){let p=U.resolve(i.path),g=U.dirname(p);if(y.existsSync(g)||y.mkdirSync(g,{recursive:true}),i.path==="package.json"&&y.existsSync(p)&&!t.force)try{let m=JSON.parse(y.readFileSync(p,"utf-8")),h=JSON.parse(i.content),A=k=>{if(!k)return {};let j={};for(let[H,x]of Object.entries(k))!x.startsWith("workspace:")&&!x.startsWith("link:")&&!x.startsWith("file:")&&(j[H]=x);return j};m.dependencies={...A(m.dependencies),...h.dependencies||{}},m.devDependencies={...A(m.devDependencies),...h.devDependencies||{}},h.scripts&&(m.scripts={...m.scripts||{},...h.scripts}),y.writeFileSync(p,JSON.stringify(m,null,2)+`
3765
- `,"utf-8"),console.log(c.green(` merge ${i.path} (added dependencies)`)),o++;continue}catch{}if(y.existsSync(p)&&!t.force){console.log(c.yellow(` skip ${i.path} (already exists)`)),l++;continue}y.writeFileSync(p,i.content,"utf-8"),console.log(c.green(` create ${i.path}`)),o++;}console.log(""),o>0&&console.log(c.green(`Created ${o} file${o!==1?"s":""}.`)),l>0&&console.log(c.yellow(`Skipped ${l} file${l!==1?"s":""} (use --force to overwrite).`));let u={auth:{"@vaiftech/auth":"^1.0.0"},database:{},realtime:{},storage:{},functions:{}},s=U.resolve("package.json");if(y.existsSync(s)&&e.length>0)try{let i=JSON.parse(y.readFileSync(s,"utf-8")),p=!1;for(let g of e){let m=u[g];if(m)for(let[h,A]of Object.entries(m))i.dependencies?.[h]||(i.dependencies=i.dependencies||{},i.dependencies[h]=A,p=!0);}p&&y.writeFileSync(s,JSON.stringify(i,null,2)+`
3766
- `,"utf-8");}catch{}(n.dependencies?.length||n.devDependencies?.length)&&(console.log(""),console.log(c.bold("Install dependencies:")),n.dependencies?.length&&console.log(c.cyan(` npm install ${n.dependencies.join(" ")}`)),n.devDependencies?.length&&console.log(c.cyan(` npm install -D ${n.devDependencies.join(" ")}`))),console.log(""),console.log(c.bold.green("Project scaffolded successfully!")),console.log(""),console.log(c.bold(" Next steps:")),n.postInstructions.forEach(i=>{console.log(c.gray(` ${i}`));}),console.log(""),console.log(c.gray(" Get your project credentials at https://vaif.studio/app/security/api-keys")),console.log("");}var be={$schema:"https://vaif.studio/schemas/config.json",projectId:"",database:{url:"${DATABASE_URL}",schema:"public"},types:{output:"./src/types/database.ts"},api:{baseUrl:"https://api.vaif.studio"}};async function rt(a){if(a.addFeatures){a.template||(console.log(c.red(`
3767
- --add-features requires --template to know which template to use.`)),console.log(c.gray("Example: vaif init --template react-spa --add-features functions,storage")),process.exit(1));let e=a.addFeatures.split(",").map(r=>r.trim());await V(a.template,{force:a.force,features:e,addOnly:true});return}let t=P("Initializing VAIF configuration...").start(),n=U.resolve("vaif.config.json");y.existsSync(n)&&!a.force&&(t.fail("vaif.config.json already exists"),console.log(c.yellow(`
3768
- Use --force to overwrite existing configuration.`)),process.exit(1));try{if(y.writeFileSync(n,JSON.stringify(be,null,2),"utf-8"),t.succeed("Created vaif.config.json"),a.template){let e=a.features?a.features.split(",").map(r=>r.trim()):void 0;await V(a.template,{force:a.force,features:e});}else {let e=U.resolve(".env.example");if(y.existsSync(e)||(y.writeFileSync(e,`# VAIF Configuration
3777
+ `}]},dependencies:["@vaiftech/client","@vaiftech/auth"],postInstructions:["Copy .env.example to .env.local and fill in your project credentials","Create a 'product-images' storage bucket in your VAIF dashboard","Import storage helpers from '@/lib/storage' in your API routes","Use uploadProductImage() in your product creation flow","Run: npx vaif generate to generate TypeScript types"]}};function tt(){console.log(""),console.log(l.bold("Available project templates")),console.log("");let a=26,t=22;console.log(` ${l.gray("Template".padEnd(a))}${l.gray("Stack".padEnd(t))}${l.gray("Description")}`),console.log(l.gray(" "+"-".repeat(a+t+40)));for(let[n,e]of Object.entries(W))console.log(` ${l.cyan(n.padEnd(a))}${l.yellow(e.tag.padEnd(t))}${l.white(e.description)}`);console.log(""),console.log(l.gray("Usage:")),console.log(l.gray(" npx @vaiftech/cli init --template <name>")),console.log(l.gray(" npx @vaiftech/cli init -t nextjs-fullstack")),console.log(l.gray(" npx @vaiftech/cli init -t react-spa --features auth,database,realtime")),console.log(""),console.log(l.gray("Available features: auth, database, realtime, storage, functions")),console.log("");}async function Ie(a){if(!process.stdin.isTTY||!process.stdout.isTTY)return a;let t=new Set(a.map(e=>T.findIndex(r=>r.name===e)).filter(e=>e>=0)),n=0;return new Promise(e=>{let r=$.createInterface({input:process.stdin,output:process.stdout});$.emitKeypressEvents(process.stdin,r),process.stdin.setRawMode&&process.stdin.setRawMode(true);function i(){let u=T.length+2;process.stdout.write(`\x1B[${u}A`),c();}function c(){console.log(l.bold(`
3778
+ ? Which VAIF features do you want to include?`)),T.forEach((u,o)=>{let s=t.has(o)?l.green("[x]"):"[ ]",f=o===n?l.cyan("> "):" ";console.log(`${f}${s} ${u.label} ${l.gray(`(${u.description})`)}`);}),console.log(l.gray(" (up/down to move, space to toggle, enter to confirm)"));}c(),process.stdin.on("keypress",(u,o)=>{if(o.name==="up"&&n>0)n--,i();else if(o.name==="down"&&n<T.length-1)n++,i();else if(o.name==="space")t.has(n)?t.delete(n):t.add(n),i();else if(o.name==="return"){process.stdin.setRawMode&&process.stdin.setRawMode(false),r.close();let s=[...t].sort().map(f=>T[f].name);e(s.length>0?s:a);}else o.name==="c"&&o.ctrl&&(process.stdin.setRawMode&&process.stdin.setRawMode(false),r.close(),process.exit(0));});})}async function j(a,t={}){let n=W[a];n||(console.log(l.red(`
3779
+ Unknown template: ${a}`)),console.log(l.yellow(`Run 'vaif templates' to see available templates.
3780
+ `)),process.exit(1));let e;t.features&&t.features.length>0?e=t.features.filter(d=>T.some(m=>m.name===d)):t.addOnly?(console.log(l.red(`
3781
+ No features specified.`)),console.log(l.yellow("Usage: vaif init --template <name> --add-features <features>")),console.log(l.gray("Available features: auth, database, realtime, storage, functions")),process.exit(1)):n.featureFiles&&Object.keys(n.featureFiles).length>0?e=await Ie(n.defaultFeatures??["database","auth"]):e=n.defaultFeatures??[],t.addOnly?(console.log(""),console.log(l.bold(`Adding features to ${l.cyan(n.name)} project...`)),console.log(l.gray(` Features: ${e.join(", ")}`)),console.log("")):(console.log(""),console.log(l.bold(`Scaffolding ${l.cyan(n.name)} template...`)),e.length>0&&console.log(l.gray(` Features: ${e.join(", ")}`)),console.log(""));let r=t.addOnly?[]:[...n.files],i=new Set,c=[];if(n.featureFiles)for(let d of e){let m=n.featureFiles[d];if(m)for(let I of m)i.add(I.path),c.push(I);}let u=r.filter(d=>!i.has(d.path)).concat(c),o=0,s=0;for(let d of u){let m=k.resolve(d.path),I=k.dirname(m);if(b.existsSync(I)||b.mkdirSync(I,{recursive:true}),d.path==="package.json"&&b.existsSync(m)&&!t.force)try{let y=JSON.parse(b.readFileSync(m,"utf-8")),E=JSON.parse(d.content),S=M=>{if(!M)return {};let K={};for(let[X,w]of Object.entries(M))!w.startsWith("workspace:")&&!w.startsWith("link:")&&!w.startsWith("file:")&&(K[X]=w);return K};y.dependencies={...S(y.dependencies),...E.dependencies||{}},y.devDependencies={...S(y.devDependencies),...E.devDependencies||{}},E.scripts&&(y.scripts={...y.scripts||{},...E.scripts}),b.writeFileSync(m,JSON.stringify(y,null,2)+`
3782
+ `,"utf-8"),console.log(l.green(` merge ${d.path} (added dependencies)`)),o++;continue}catch{}if(b.existsSync(m)&&!t.force){console.log(l.yellow(` skip ${d.path} (already exists)`)),s++;continue}b.writeFileSync(m,d.content,"utf-8"),console.log(l.green(` create ${d.path}`)),o++;}console.log(""),o>0&&console.log(l.green(`Created ${o} file${o!==1?"s":""}.`)),s>0&&console.log(l.yellow(`Skipped ${s} file${s!==1?"s":""} (use --force to overwrite).`));let f={auth:{"@vaiftech/auth":"^1.0.0"},database:{},realtime:{},storage:{},functions:{}},h=k.resolve("package.json");if(b.existsSync(h)&&e.length>0)try{let d=JSON.parse(b.readFileSync(h,"utf-8")),m=!1;for(let I of e){let y=f[I];if(y)for(let[E,S]of Object.entries(y))d.dependencies?.[E]||(d.dependencies=d.dependencies||{},d.dependencies[E]=S,m=!0);}m&&b.writeFileSync(h,JSON.stringify(d,null,2)+`
3783
+ `,"utf-8");}catch{}(n.dependencies?.length||n.devDependencies?.length)&&(console.log(""),console.log(l.bold("Install dependencies:")),n.dependencies?.length&&console.log(l.cyan(` npm install ${n.dependencies.join(" ")}`)),n.devDependencies?.length&&console.log(l.cyan(` npm install -D ${n.devDependencies.join(" ")}`))),console.log(""),console.log(l.bold.green("Project scaffolded successfully!")),console.log(""),console.log(l.bold(" Next steps:")),n.postInstructions.forEach(d=>{console.log(l.gray(` ${d}`));}),console.log(""),console.log(l.gray(" Get your project credentials at https://vaif.studio/app/security/api-keys")),console.log("");}var _e={$schema:"https://vaif.studio/schemas/config.json",projectId:"",database:{url:"${DATABASE_URL}",schema:"public"},types:{output:"./src/types/database.ts"},api:{baseUrl:"https://api.vaif.studio"}};async function ct(a){if(a.addFeatures){a.template||(console.log(l.red(`
3784
+ --add-features requires --template to know which template to use.`)),console.log(l.gray("Example: vaif init --template react-spa --add-features functions,storage")),process.exit(1));let e=a.addFeatures.split(",").map(r=>r.trim());await j(a.template,{force:a.force,features:e,addOnly:true});return}let t=L("Initializing VAIF configuration...").start(),n=k.resolve("vaif.config.json");b.existsSync(n)&&!a.force&&(t.fail("vaif.config.json already exists"),console.log(l.yellow(`
3785
+ Use --force to overwrite existing configuration.`)),process.exit(1));try{if(b.writeFileSync(n,JSON.stringify(_e,null,2),"utf-8"),t.succeed("Created vaif.config.json"),a.template){let e=a.features?a.features.split(",").map(r=>r.trim()):void 0;await j(a.template,{force:a.force,features:e});}else {let e=k.resolve(".env.example");if(b.existsSync(e)||(b.writeFileSync(e,`# VAIF Configuration
3769
3786
  DATABASE_URL=postgresql://user:password@localhost:5432/database
3770
3787
  VAIF_API_KEY=your-api-key
3771
- `,"utf-8"),console.log(c.gray("Created .env.example"))),a.typescript){let r=U.resolve("src/types");y.existsSync(r)||(y.mkdirSync(r,{recursive:!0}),console.log(c.gray("Created src/types directory")));}console.log(""),console.log(c.green("VAIF initialized successfully!")),console.log(""),console.log(c.gray("Next steps:")),console.log(c.gray(" 1. Update vaif.config.json with your project ID")),console.log(c.gray(" 2. Set DATABASE_URL in your environment")),console.log(c.gray(" 3. Run: npx vaif generate")),console.log("");}}catch(e){t.fail("Failed to initialize"),e instanceof Error&&console.error(c.red(`
3772
- Error: ${e.message}`)),process.exit(1);}}export{K as a,R as b,Le as c,Ne as d,Ue as e,Ye as f,Qe as g,rt as h};
3788
+ `,"utf-8"),console.log(l.gray("Created .env.example"))),a.typescript){let r=k.resolve("src/types");b.existsSync(r)||(b.mkdirSync(r,{recursive:!0}),console.log(l.gray("Created src/types directory")));}console.log(""),console.log(l.green("VAIF initialized successfully!")),console.log(""),console.log(l.gray("Next steps:")),console.log(l.gray(" 1. Update vaif.config.json with your project ID")),console.log(l.gray(" 2. Set DATABASE_URL in your environment")),console.log(l.gray(" 3. Run: npx vaif generate")),console.log("");}}catch(e){t.fail("Failed to initialize"),e instanceof Error&&console.error(l.red(`
3789
+ Error: ${e.message}`)),process.exit(1);}}export{q as a,V as b,Ue as c,De as d,ke as e,He as f,tt as g,ct as h};