@vaiftech/cli 1.7.4 → 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.4) — 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
 
@@ -287,7 +290,21 @@ A full-stack Next.js application powered by [VAIF Studio](https://vaif.studio),
287
290
  ## Documentation
288
291
 
289
292
  Full documentation is available at <https://docs.vaif.studio>.
290
- `}],featureFiles:{auth:[{path:"middleware.ts",content:`import { NextResponse, type NextRequest } from "next/server";
293
+ `}],featureFiles:{auth:[{path:"app/page.tsx",content:`import Link from "next/link";
294
+
295
+ export default function Home() {
296
+ return (
297
+ <main style={{ maxWidth: 600, margin: "80px auto", textAlign: "center" }}>
298
+ <h1>Welcome to VAIF</h1>
299
+ <p>Your Next.js app is ready. Start building!</p>
300
+ <p style={{ marginTop: 24 }}>
301
+ <Link href="/login" style={{ marginRight: 16 }}>Log in</Link>
302
+ <Link href="/signup">Sign up</Link>
303
+ </p>
304
+ </main>
305
+ );
306
+ }
307
+ `},{path:"middleware.ts",content:`import { NextResponse, type NextRequest } from "next/server";
291
308
  import { createVaifClient } from "@vaiftech/client";
292
309
  import { authMiddleware } from "@vaiftech/auth/nextjs";
293
310
 
@@ -300,6 +317,7 @@ export async function middleware(request: NextRequest) {
300
317
 
301
318
  const vaif = createVaifClient({
302
319
  baseUrl: process.env.NEXT_PUBLIC_VAIF_API_URL || 'https://api.vaif.studio',
320
+ projectId: process.env.VAIF_PROJECT_ID,
303
321
  apiKey: process.env.VAIF_SECRET_KEY!,
304
322
  });
305
323
 
@@ -662,12 +680,14 @@ function Home() {
662
680
 
663
681
  export const vaif = createVaifClient({
664
682
  baseUrl: import.meta.env.VITE_VAIF_API_URL || 'https://api.vaif.studio',
683
+ projectId: import.meta.env.VITE_VAIF_PROJECT_ID,
665
684
  apiKey: import.meta.env.VITE_VAIF_API_KEY,
666
685
  });
667
686
  `},{path:"src/vite-env.d.ts",content:`/// <reference types="vite/client" />
668
687
 
669
688
  interface ImportMetaEnv {
670
689
  readonly VITE_VAIF_API_URL: string;
690
+ readonly VITE_VAIF_PROJECT_ID: string;
671
691
  readonly VITE_VAIF_API_KEY: string;
672
692
  }
673
693
 
@@ -678,6 +698,7 @@ interface ImportMeta {
678
698
  # Get these values from https://vaif.studio/app/security/api-keys
679
699
 
680
700
  VITE_VAIF_API_URL=https://api.vaif.studio
701
+ VITE_VAIF_PROJECT_ID=your-project-id
681
702
  VITE_VAIF_API_KEY=your-api-key
682
703
  `},{path:".gitignore",content:`node_modules
683
704
  dist
@@ -777,7 +798,33 @@ A single-page React application built with [Vite](https://vite.dev/) and powered
777
798
  ## Documentation
778
799
 
779
800
  Full documentation is available at <https://docs.vaif.studio>.
780
- `}],featureFiles:{auth:[{path:"src/pages/Login.tsx",content:`import { useState } from "react";
801
+ `}],featureFiles:{auth:[{path:"src/App.tsx",content:`import { Routes, Route, Link } from "react-router-dom";
802
+ import Login from "./pages/Login";
803
+ import Signup from "./pages/Signup";
804
+
805
+ export default function App() {
806
+ return (
807
+ <Routes>
808
+ <Route path="/" element={<Home />} />
809
+ <Route path="/login" element={<Login />} />
810
+ <Route path="/signup" element={<Signup />} />
811
+ </Routes>
812
+ );
813
+ }
814
+
815
+ function Home() {
816
+ return (
817
+ <div style={{ maxWidth: 600, margin: "80px auto", textAlign: "center" }}>
818
+ <h1>Welcome to VAIF</h1>
819
+ <p>Your app is ready. Start building!</p>
820
+ <p style={{ marginTop: 24 }}>
821
+ <Link to="/login" style={{ marginRight: 16 }}>Log in</Link>
822
+ <Link to="/signup">Sign up</Link>
823
+ </p>
824
+ </div>
825
+ );
826
+ }
827
+ `},{path:"src/pages/Login.tsx",content:`import { useState } from "react";
781
828
  import { useNavigate } from "react-router-dom";
782
829
  import { vaif } from "../lib/vaif";
783
830
 
@@ -882,21 +929,15 @@ export function AuthGuard({ children, fallback }: AuthGuardProps) {
882
929
  const [authenticated, setAuthenticated] = useState(false);
883
930
 
884
931
  useEffect(() => {
885
- vaif.auth.getSession().then(({ data }) => {
886
- if (data.session) {
932
+ vaif.auth.getUser()
933
+ .then(() => {
887
934
  setAuthenticated(true);
888
- } else {
935
+ setLoading(false);
936
+ })
937
+ .catch(() => {
889
938
  navigate("/login");
890
- }
891
- setLoading(false);
892
- });
893
-
894
- const { data: { subscription } } = vaif.auth.onAuthStateChange((_event, session) => {
895
- setAuthenticated(!!session);
896
- if (!session) navigate("/login");
897
- });
898
-
899
- return () => subscription.unsubscribe();
939
+ setLoading(false);
940
+ });
900
941
  }, [navigate]);
901
942
 
902
943
  if (loading) return fallback ?? <div>Loading...</div>;
@@ -1444,7 +1485,39 @@ A React Native / Expo mobile application powered by [VAIF Studio](https://vaif.s
1444
1485
  ## Documentation
1445
1486
 
1446
1487
  Full documentation is available at <https://docs.vaif.studio>.
1447
- `}],featureFiles:{auth:[{path:"app/(auth)/login.tsx",content:`import { useState } from "react";
1488
+ `}],featureFiles:{auth:[{path:"app/index.tsx",content:`import { View, Text, StyleSheet, TouchableOpacity } from "react-native";
1489
+ import { useRouter } from "expo-router";
1490
+
1491
+ export default function HomeScreen() {
1492
+ const router = useRouter();
1493
+
1494
+ return (
1495
+ <View style={styles.container}>
1496
+ <Text style={styles.title}>Welcome to VAIF</Text>
1497
+ <Text style={styles.subtitle}>Your mobile app is ready. Start building!</Text>
1498
+ <View style={styles.buttons}>
1499
+ <TouchableOpacity style={styles.button} onPress={() => router.push("/(auth)/login")}>
1500
+ <Text style={styles.buttonText}>Log In</Text>
1501
+ </TouchableOpacity>
1502
+ <TouchableOpacity style={[styles.button, styles.secondaryButton]} onPress={() => router.push("/(auth)/signup")}>
1503
+ <Text style={[styles.buttonText, styles.secondaryText]}>Sign Up</Text>
1504
+ </TouchableOpacity>
1505
+ </View>
1506
+ </View>
1507
+ );
1508
+ }
1509
+
1510
+ const styles = StyleSheet.create({
1511
+ container: { flex: 1, alignItems: "center", justifyContent: "center", padding: 24 },
1512
+ title: { fontSize: 28, fontWeight: "bold", marginBottom: 8 },
1513
+ subtitle: { fontSize: 16, color: "#666", textAlign: "center", marginBottom: 32 },
1514
+ buttons: { gap: 12, width: "100%" },
1515
+ button: { backgroundColor: "#0070f3", borderRadius: 8, padding: 14, alignItems: "center" },
1516
+ secondaryButton: { backgroundColor: "transparent", borderWidth: 1, borderColor: "#0070f3" },
1517
+ buttonText: { color: "#fff", fontSize: 16, fontWeight: "600" },
1518
+ secondaryText: { color: "#0070f3" },
1519
+ });
1520
+ `},{path:"app/(auth)/login.tsx",content:`import { useState } from "react";
1448
1521
  import { View, Text, TextInput, TouchableOpacity, StyleSheet, Alert } from "react-native";
1449
1522
  import { useRouter } from "expo-router";
1450
1523
  import { vaif } from "../../lib/vaif";
@@ -2626,6 +2699,7 @@ func HelloHandler(w http.ResponseWriter, r *http.Request) {
2626
2699
 
2627
2700
  export const vaif = createVaifClient({
2628
2701
  baseUrl: import.meta.env.VITE_VAIF_API_URL || 'https://api.vaif.studio',
2702
+ projectId: import.meta.env.VITE_VAIF_PROJECT_ID,
2629
2703
  apiKey: import.meta.env.VITE_VAIF_API_KEY,
2630
2704
  });
2631
2705
 
@@ -2680,6 +2754,7 @@ export async function deleteTodo(id: string): Promise<void> {
2680
2754
  # Get these values from https://vaif.studio/app/security/api-keys
2681
2755
 
2682
2756
  VITE_VAIF_API_URL=https://api.vaif.studio
2757
+ VITE_VAIF_PROJECT_ID=your-project-id
2683
2758
  VITE_VAIF_API_KEY=your-api-key
2684
2759
  `},{path:"README.md",content:`# Todo App \u2014 VAIF Starter
2685
2760
 
@@ -2817,6 +2892,7 @@ export const posts = pgTable("posts", {
2817
2892
 
2818
2893
  export const vaif = createVaifClient({
2819
2894
  baseUrl: import.meta.env.VITE_VAIF_API_URL || 'https://api.vaif.studio',
2895
+ projectId: import.meta.env.VITE_VAIF_PROJECT_ID,
2820
2896
  apiKey: import.meta.env.VITE_VAIF_API_KEY,
2821
2897
  });
2822
2898
 
@@ -2958,6 +3034,7 @@ export function useRealtimeMessages({
2958
3034
  # Get these values from https://vaif.studio/app/security/api-keys
2959
3035
 
2960
3036
  VITE_VAIF_API_URL=https://api.vaif.studio
3037
+ VITE_VAIF_PROJECT_ID=your-project-id
2961
3038
  VITE_VAIF_API_KEY=your-api-key
2962
3039
  `},{path:"README.md",content:`# Realtime Chat \u2014 VAIF Starter
2963
3040
 
@@ -3110,6 +3187,7 @@ export const posts = pgTable("posts", {
3110
3187
  // Browser client \u2013 use in Client Components
3111
3188
  export const vaif = createVaifClient({
3112
3189
  baseUrl: process.env.NEXT_PUBLIC_VAIF_API_URL || 'https://api.vaif.studio',
3190
+ projectId: process.env.NEXT_PUBLIC_VAIF_PROJECT_ID,
3113
3191
  apiKey: process.env.NEXT_PUBLIC_VAIF_API_KEY!,
3114
3192
  });
3115
3193
 
@@ -3117,6 +3195,7 @@ export const vaif = createVaifClient({
3117
3195
  export function createVaifServer() {
3118
3196
  return createVaifClient({
3119
3197
  baseUrl: process.env.NEXT_PUBLIC_VAIF_API_URL || 'https://api.vaif.studio',
3198
+ projectId: process.env.VAIF_PROJECT_ID,
3120
3199
  apiKey: process.env.VAIF_SECRET_KEY!,
3121
3200
  });
3122
3201
  }
@@ -3271,6 +3350,7 @@ export async function requireTeamRole(
3271
3350
  # Get these values from https://vaif.studio/app/security/api-keys \u2192 Project Settings \u2192 API Keys
3272
3351
 
3273
3352
  NEXT_PUBLIC_VAIF_API_URL=https://api.vaif.studio
3353
+ NEXT_PUBLIC_VAIF_PROJECT_ID=your-project-id
3274
3354
  NEXT_PUBLIC_VAIF_API_KEY=your-api-key
3275
3355
  VAIF_SECRET_KEY=your-secret-key
3276
3356
 
@@ -3423,6 +3503,7 @@ export const posts = pgTable("posts", {
3423
3503
  // Browser client
3424
3504
  export const vaif = createVaifClient({
3425
3505
  baseUrl: process.env.NEXT_PUBLIC_VAIF_API_URL || 'https://api.vaif.studio',
3506
+ projectId: process.env.NEXT_PUBLIC_VAIF_PROJECT_ID,
3426
3507
  apiKey: process.env.NEXT_PUBLIC_VAIF_API_KEY!,
3427
3508
  });
3428
3509
 
@@ -3430,6 +3511,7 @@ export const vaif = createVaifClient({
3430
3511
  export function createVaifServer() {
3431
3512
  return createVaifClient({
3432
3513
  baseUrl: process.env.NEXT_PUBLIC_VAIF_API_URL || 'https://api.vaif.studio',
3514
+ projectId: process.env.VAIF_PROJECT_ID,
3433
3515
  apiKey: process.env.VAIF_SECRET_KEY!,
3434
3516
  });
3435
3517
  }
@@ -3546,6 +3628,7 @@ function getContentType(fileName: string): string {
3546
3628
  # Get these values from https://vaif.studio/app/security/api-keys \u2192 Project Settings \u2192 API Keys
3547
3629
 
3548
3630
  NEXT_PUBLIC_VAIF_API_URL=https://api.vaif.studio
3631
+ NEXT_PUBLIC_VAIF_PROJECT_ID=your-project-id
3549
3632
  NEXT_PUBLIC_VAIF_API_KEY=your-api-key
3550
3633
  VAIF_SECRET_KEY=your-secret-key
3551
3634
 
@@ -3691,16 +3774,16 @@ export const posts = pgTable("posts", {
3691
3774
 
3692
3775
  return Response.json({ message: \`Hello, \${name}!\` });
3693
3776
  }
3694
- `}]},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(`
3695
- ? 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(`
3696
- Unknown template: ${a}`)),console.log(c.yellow(`Run 'vaif templates' to see available templates.
3697
- `)),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(`
3698
- 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,S]of Object.entries(k))!S.startsWith("workspace:")&&!S.startsWith("link:")&&!S.startsWith("file:")&&(j[H]=S);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)+`
3699
- `,"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)+`
3700
- `,"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(`
3701
- --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(`
3702
- 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
3703
3786
  DATABASE_URL=postgresql://user:password@localhost:5432/database
3704
3787
  VAIF_API_KEY=your-api-key
3705
- `,"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(`
3706
- 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};