@vaiftech/cli 1.7.0 → 1.7.2

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.0) — 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.2) — 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
 
@@ -1,4 +1,13 @@
1
- import h from'fs';import w from'path';import M from'dotenv';import K from'pg';import z from'ora';import i from'chalk';import q from'prettier';import D from'readline';M.config();async function L(a){let t=w.resolve(a);if(!h.existsSync(t))return null;try{let n=h.readFileSync(t,"utf-8"),e=JSON.parse(n);return e.database?.url&&(e.database.url=V(e.database.url)),e.api?.apiKey&&(e.api.apiKey=V(e.api.apiKey)),e}catch{throw new Error(`Failed to parse config file: ${a}`)}}function V(a){return a.replace(/\$\{([^}]+)\}/g,(t,n)=>process.env[n]||t)}async function B(a,t){let n=await a.query(`
1
+ import y from'fs';import N 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=N.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=N.join(X.homedir(),".vaif"),I=N.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 Ue(){y.existsSync(I)?(y.unlinkSync(I),console.log(c.green("Logged out successfully"))):console.log(c.yellow("Not currently logged in"));}async function Ne(){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(`
2
11
  SELECT table_name, table_type
3
12
  FROM information_schema.tables
4
13
  WHERE table_schema = $1
@@ -19,7 +28,7 @@ import h from'fs';import w from'path';import M from'dotenv';import K from'pg';im
19
28
  FROM information_schema.columns
20
29
  WHERE table_schema = $1
21
30
  ORDER BY table_name, ordinal_position
22
- `,[t]),s=await a.query(`
31
+ `,[t]),r=await a.query(`
23
32
  SELECT
24
33
  tc.constraint_name,
25
34
  tc.table_name,
@@ -44,22 +53,24 @@ import h from'fs';import w from'path';import M from'dotenv';import K from'pg';im
44
53
  JOIN pg_namespace n ON n.oid = t.typnamespace
45
54
  WHERE n.nspname = $1
46
55
  ORDER BY t.typname, e.enumsortorder
47
- `,[t]),c=new Map;for(let l of n.rows)c.set(l.table_name,[]);for(let l of e.rows){let r=c.get(l.table_name);r&&r.push(l);}let u=new Map;for(let l of o.rows){let r=u.get(l.enum_name)||[];r.push(l.enum_value),u.set(l.enum_name,r);}return {tables:c,enums:u,foreignKeys:s.rows}}var S={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 Y(a,t){let{data_type:n,udt_name:e,is_nullable:s}=a;if(t.has(e)){let u=t.get(e).map(l=>`"${l}"`).join(" | ");return s==="YES"?`(${u}) | null`:u}if(n==="ARRAY"){let c=e.replace(/^_/,"");if(t.has(c)){let r=t.get(c).map(d=>`"${d}"`).join(" | ");return s==="YES"?`(${r})[] | null`:`(${r})[]`}let u=S[c]||"unknown";return s==="YES"?`${u}[] | null`:`${u}[]`}let o=S[n]||S[e]||"unknown";return s==="YES"&&(o=`${o} | null`),o}function x(a){return a.split(/[_\-\s]+/).map(t=>t.charAt(0).toUpperCase()+t.slice(1).toLowerCase()).join("")}function $(a,t){let n=x(a),e=t.map(s=>` | "${s}"`).join(`
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 U(a){return a.split(/[_\-\s]+/).map(t=>t.charAt(0).toUpperCase()+t.slice(1).toLowerCase()).join("")}function fe(a,t){let n=U(a),e=t.map(r=>` | "${r}"`).join(`
48
57
  `);return `export type ${n} =
49
- ${e};`}function G(a,t,n){let e=x(a),s=[],o=[],c=[];for(let d of t){let f=Y(d,n),p=d.column_name,v=d.column_default!==null||d.is_identity==="YES",y=d.is_nullable==="YES";s.push(` ${p}: ${f};`),v||d.column_name==="id"?o.push(` ${p}?: ${f.replace(" | null","")} | null;`):y?o.push(` ${p}?: ${f};`):o.push(` ${p}: ${f.replace(" | null","")};`),c.push(` ${p}?: ${f.replace(" | null","")} | null;`);}let u=`export interface ${e} {
50
- ${s.join(`
58
+ ${e};`}function ge(a,t,n){let e=U(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} {
59
+ ${r.join(`
51
60
  `)}
52
- }`,l=`export interface ${e}Insert {
61
+ }`,s=`export interface ${e}Insert {
53
62
  ${o.join(`
54
63
  `)}
55
- }`,r=`export interface ${e}Update {
56
- ${c.join(`
64
+ }`,i=`export interface ${e}Update {
65
+ ${l.join(`
57
66
  `)}
58
- }`;return {base:u,insert:l,update:r}}function H(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,c]of t)e.push($(o,c)),e.push("");}e.push("// ============ TABLES ============"),e.push("");let s=[];for(let[o,c]of a){let{base:u,insert:l,update:r}=G(o,c,t);s.push(o),e.push(u),e.push(""),e.push(l),e.push(""),e.push(r),e.push("");}e.push("// ============ DATABASE SCHEMA ============"),e.push(""),e.push("export interface Database {");for(let o of s){let c=x(o);e.push(` ${o}: {`),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(`
59
- `)}async function de(a){let t=z("Loading configuration...").start();try{let n=await L(a.config),e=a.connection||n?.database?.url||process.env.DATABASE_URL;e||(t.fail("No database connection string provided"),console.log(i.yellow(`
60
- Provide a connection string via:`)),console.log(i.gray(" --connection <url>")),console.log(i.gray(" DATABASE_URL environment variable")),console.log(i.gray(" vaif.config.json database.url")),process.exit(1)),t.text="Connecting to database...";let s=new K.Client({connectionString:e});await s.connect(),t.text="Introspecting schema...";let{tables:o,enums:c,foreignKeys:u}=await B(s,a.schema);if(await s.end(),o.size===0){t.warn(`No tables found in schema "${a.schema}"`);return}t.text=`Generating types for ${o.size} tables...`;let l=H(o,c,u),r=await q.format(l,{parser:"typescript",semi:!0,singleQuote:!1,trailingComma:"es5",printWidth:100});if(a.dryRun){t.succeed("Generated types (dry run):"),console.log(""),console.log(i.gray("\u2500".repeat(60))),console.log(r),console.log(i.gray("\u2500".repeat(60)));return}let d=w.resolve(a.output),f=w.dirname(d);h.existsSync(f)||h.mkdirSync(f,{recursive:!0}),h.writeFileSync(d,r,"utf-8"),t.succeed(`Generated types for ${o.size} tables \u2192 ${i.cyan(a.output)}`),console.log(""),console.log(i.green("Generated:")),console.log(i.gray(` Tables: ${o.size}`)),console.log(i.gray(` Enums: ${c.size}`)),console.log(""),console.log(i.gray("Import in your code:")),console.log(i.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(i.red(`
61
- Error: ${n.message}`)),n.message.includes("ECONNREFUSED")&&console.log(i.yellow(`
62
- Make sure your database is running and accessible.`))),process.exit(1);}}var b=[{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"}],j={"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: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=U(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=N.resolve(a.output),g=N.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:`{
63
74
  "name": "my-vaif-app",
64
75
  "private": true,
65
76
  "version": "0.1.0",
@@ -155,24 +166,27 @@ body {
155
166
 
156
167
  // Browser client \u2013 safe to use in Client Components
157
168
  export const vaif = createVaifClient({
158
- projectId: process.env.NEXT_PUBLIC_VAIF_PROJECT_ID!,
169
+ baseUrl: process.env.NEXT_PUBLIC_VAIF_API_URL || 'https://api.vaif.studio',
159
170
  apiKey: process.env.NEXT_PUBLIC_VAIF_API_KEY!,
160
171
  });
161
172
 
162
173
  // Server client \u2013 use in Server Components, Route Handlers, Server Actions
163
174
  export function createVaifServer() {
164
175
  return createVaifClient({
165
- projectId: process.env.NEXT_PUBLIC_VAIF_PROJECT_ID!,
166
- secretKey: process.env.VAIF_SECRET_KEY!,
176
+ baseUrl: process.env.NEXT_PUBLIC_VAIF_API_URL || 'https://api.vaif.studio',
177
+ apiKey: process.env.VAIF_SECRET_KEY!,
167
178
  });
168
179
  }
169
180
  `},{path:".env.local.example",content:`# VAIF Configuration
170
181
  # Get these values from https://vaif.studio/app/security/api-keys \u2192 Project Settings \u2192 API Keys
171
182
 
172
- NEXT_PUBLIC_VAIF_PROJECT_ID=your-project-id
173
- NEXT_PUBLIC_VAIF_API_KEY=your-anon-key
183
+ NEXT_PUBLIC_VAIF_API_URL=https://api.vaif.studio
184
+ NEXT_PUBLIC_VAIF_API_KEY=your-api-key
174
185
  VAIF_SECRET_KEY=your-secret-key
175
186
 
187
+ # Database connection (for vaif generate, vaif db push)
188
+ # DATABASE_URL=postgresql://user:password@host:5432/dbname
189
+
176
190
  # CLI uses these (non-prefixed) for vaif db push, vaif secrets, etc.
177
191
  VAIF_PROJECT_ID=your-project-id
178
192
  `},{path:".gitignore",content:`node_modules
@@ -288,8 +302,8 @@ export async function middleware(request: NextRequest) {
288
302
  if (!isProtected) return NextResponse.next();
289
303
 
290
304
  const vaif = createVaifClient({
291
- projectId: process.env.NEXT_PUBLIC_VAIF_PROJECT_ID!,
292
- secretKey: process.env.VAIF_SECRET_KEY!,
305
+ baseUrl: process.env.NEXT_PUBLIC_VAIF_API_URL || 'https://api.vaif.studio',
306
+ apiKey: process.env.VAIF_SECRET_KEY!,
293
307
  });
294
308
 
295
309
  return authMiddleware(vaif, request, {
@@ -650,13 +664,13 @@ function Home() {
650
664
  `},{path:"src/lib/vaif.ts",content:`import { createVaifClient } from "@vaiftech/client";
651
665
 
652
666
  export const vaif = createVaifClient({
653
- projectId: import.meta.env.VITE_VAIF_PROJECT_ID,
667
+ baseUrl: import.meta.env.VITE_VAIF_API_URL || 'https://api.vaif.studio',
654
668
  apiKey: import.meta.env.VITE_VAIF_API_KEY,
655
669
  });
656
670
  `},{path:"src/vite-env.d.ts",content:`/// <reference types="vite/client" />
657
671
 
658
672
  interface ImportMetaEnv {
659
- readonly VITE_VAIF_PROJECT_ID: string;
673
+ readonly VITE_VAIF_API_URL: string;
660
674
  readonly VITE_VAIF_API_KEY: string;
661
675
  }
662
676
 
@@ -664,13 +678,13 @@ interface ImportMeta {
664
678
  readonly env: ImportMetaEnv;
665
679
  }
666
680
  `},{path:".env.example",content:`# VAIF Configuration
667
- # Get these values from https://vaif.studio/app/security/api-keys \u2192 Project Settings \u2192 API Keys
681
+ # Get these values from https://vaif.studio/app/security/api-keys
668
682
 
669
- VITE_VAIF_PROJECT_ID=your-project-id
670
- VITE_VAIF_API_KEY=your-anon-key
683
+ VITE_VAIF_API_URL=https://api.vaif.studio
684
+ VITE_VAIF_API_KEY=your-api-key
671
685
 
672
- # CLI uses these (non-prefixed) for vaif db push, vaif secrets, etc.
673
- VAIF_PROJECT_ID=your-project-id
686
+ # Database connection (for vaif generate, vaif db push)
687
+ # DATABASE_URL=postgresql://user:password@host:5432/dbname
674
688
  `},{path:".gitignore",content:`node_modules
675
689
  dist
676
690
  .env
@@ -2609,7 +2623,7 @@ func HelloHandler(w http.ResponseWriter, r *http.Request) {
2609
2623
  `}]},postInstructions:["go mod tidy","# Copy .env.example to .env and add your VAIF credentials","go run main.go"]},"todo-app":{name:"Todo App",description:"Simple React todo app \u2013 great for learning VAIF basics",tag:"React Starter",defaultFeatures:["database"],files:[{path:"src/lib/vaif.ts",content:`import { createVaifClient } from "@vaiftech/client";
2610
2624
 
2611
2625
  export const vaif = createVaifClient({
2612
- projectId: import.meta.env.VITE_VAIF_PROJECT_ID,
2626
+ baseUrl: import.meta.env.VITE_VAIF_API_URL || 'https://api.vaif.studio',
2613
2627
  apiKey: import.meta.env.VITE_VAIF_API_KEY,
2614
2628
  });
2615
2629
 
@@ -2661,13 +2675,13 @@ export async function deleteTodo(id: string): Promise<void> {
2661
2675
  if (error) throw error;
2662
2676
  }
2663
2677
  `},{path:".env.example",content:`# VAIF Configuration
2664
- # Get these values from https://vaif.studio/app/security/api-keys \u2192 Project Settings \u2192 API Keys
2678
+ # Get these values from https://vaif.studio/app/security/api-keys
2665
2679
 
2666
- VITE_VAIF_PROJECT_ID=your-project-id
2667
- VITE_VAIF_API_KEY=your-anon-key
2680
+ VITE_VAIF_API_URL=https://api.vaif.studio
2681
+ VITE_VAIF_API_KEY=your-api-key
2668
2682
 
2669
- # CLI uses these (non-prefixed) for vaif db push, vaif secrets, etc.
2670
- VAIF_PROJECT_ID=your-project-id
2683
+ # Database connection (for vaif generate, vaif db push)
2684
+ # DATABASE_URL=postgresql://user:password@host:5432/dbname
2671
2685
  `},{path:"README.md",content:`# Todo App \u2014 VAIF Starter
2672
2686
 
2673
2687
  A simple React todo application for learning [VAIF Studio](https://vaif.studio) basics, including typed database queries and CRUD operations.
@@ -2803,15 +2817,8 @@ export const posts = pgTable("posts", {
2803
2817
  `}]},dependencies:["@vaiftech/client","@vaiftech/react"],postInstructions:["Copy .env.example to .env and fill in your project credentials","Create a 'todos' table in your VAIF dashboard with columns: id (uuid), title (text), done (boolean), created_at (timestamptz)","Import helpers from './lib/vaif' in your components","Run: npx vaif generate to generate TypeScript types"]},"realtime-chat":{name:"Realtime Chat",description:"React chat app with VAIF realtime subscriptions for live messaging",tag:"React + Realtime",defaultFeatures:["database","realtime","auth"],files:[{path:"src/lib/vaif.ts",content:`import { createVaifClient } from "@vaiftech/client";
2804
2818
 
2805
2819
  export const vaif = createVaifClient({
2806
- projectId: import.meta.env.VITE_VAIF_PROJECT_ID,
2820
+ baseUrl: import.meta.env.VITE_VAIF_API_URL || 'https://api.vaif.studio',
2807
2821
  apiKey: import.meta.env.VITE_VAIF_API_KEY,
2808
- realtime: {
2809
- enabled: true,
2810
- // Automatically reconnect on connection loss
2811
- reconnect: true,
2812
- reconnectInterval: 1000,
2813
- maxReconnectAttempts: 10,
2814
- },
2815
2822
  });
2816
2823
 
2817
2824
  export interface Message {
@@ -2949,13 +2956,13 @@ export function useRealtimeMessages({
2949
2956
  return { messages, isLoading, error, refresh };
2950
2957
  }
2951
2958
  `},{path:".env.example",content:`# VAIF Configuration
2952
- # Get these values from https://vaif.studio/app/security/api-keys \u2192 Project Settings \u2192 API Keys
2959
+ # Get these values from https://vaif.studio/app/security/api-keys
2953
2960
 
2954
- VITE_VAIF_PROJECT_ID=your-project-id
2955
- VITE_VAIF_API_KEY=your-anon-key
2961
+ VITE_VAIF_API_URL=https://api.vaif.studio
2962
+ VITE_VAIF_API_KEY=your-api-key
2956
2963
 
2957
- # CLI uses these (non-prefixed) for vaif db push, vaif secrets, etc.
2958
- VAIF_PROJECT_ID=your-project-id
2964
+ # Database connection (for vaif generate, vaif db push)
2965
+ # DATABASE_URL=postgresql://user:password@host:5432/dbname
2959
2966
  `},{path:"README.md",content:`# Realtime Chat \u2014 VAIF Starter
2960
2967
 
2961
2968
  A React chat application with live messaging powered by [VAIF Studio](https://vaif.studio) realtime subscriptions.
@@ -3106,15 +3113,15 @@ export const posts = pgTable("posts", {
3106
3113
 
3107
3114
  // Browser client \u2013 use in Client Components
3108
3115
  export const vaif = createVaifClient({
3109
- projectId: process.env.NEXT_PUBLIC_VAIF_PROJECT_ID!,
3116
+ baseUrl: process.env.NEXT_PUBLIC_VAIF_API_URL || 'https://api.vaif.studio',
3110
3117
  apiKey: process.env.NEXT_PUBLIC_VAIF_API_KEY!,
3111
3118
  });
3112
3119
 
3113
3120
  // Server client \u2013 use in Server Components, Route Handlers, Server Actions
3114
3121
  export function createVaifServer() {
3115
3122
  return createVaifClient({
3116
- projectId: process.env.NEXT_PUBLIC_VAIF_PROJECT_ID!,
3117
- secretKey: process.env.VAIF_SECRET_KEY!,
3123
+ baseUrl: process.env.NEXT_PUBLIC_VAIF_API_URL || 'https://api.vaif.studio',
3124
+ apiKey: process.env.VAIF_SECRET_KEY!,
3118
3125
  });
3119
3126
  }
3120
3127
  `},{path:"lib/auth.ts",content:`import { createVaifServer } from "./vaif";
@@ -3267,10 +3274,13 @@ export async function requireTeamRole(
3267
3274
  `},{path:".env.example",content:`# VAIF Configuration
3268
3275
  # Get these values from https://vaif.studio/app/security/api-keys \u2192 Project Settings \u2192 API Keys
3269
3276
 
3270
- NEXT_PUBLIC_VAIF_PROJECT_ID=your-project-id
3271
- NEXT_PUBLIC_VAIF_API_KEY=your-anon-key
3277
+ NEXT_PUBLIC_VAIF_API_URL=https://api.vaif.studio
3278
+ NEXT_PUBLIC_VAIF_API_KEY=your-api-key
3272
3279
  VAIF_SECRET_KEY=your-secret-key
3273
3280
 
3281
+ # Database connection (for vaif generate, vaif db push)
3282
+ # DATABASE_URL=postgresql://user:password@host:5432/dbname
3283
+
3274
3284
  # CLI uses these (non-prefixed) for vaif db push, vaif secrets, etc.
3275
3285
  VAIF_PROJECT_ID=your-project-id
3276
3286
  `},{path:"README.md",content:`# SaaS Starter \u2014 VAIF Studio
@@ -3419,15 +3429,15 @@ export const posts = pgTable("posts", {
3419
3429
 
3420
3430
  // Browser client
3421
3431
  export const vaif = createVaifClient({
3422
- projectId: process.env.NEXT_PUBLIC_VAIF_PROJECT_ID!,
3432
+ baseUrl: process.env.NEXT_PUBLIC_VAIF_API_URL || 'https://api.vaif.studio',
3423
3433
  apiKey: process.env.NEXT_PUBLIC_VAIF_API_KEY!,
3424
3434
  });
3425
3435
 
3426
3436
  // Server client
3427
3437
  export function createVaifServer() {
3428
3438
  return createVaifClient({
3429
- projectId: process.env.NEXT_PUBLIC_VAIF_PROJECT_ID!,
3430
- secretKey: process.env.VAIF_SECRET_KEY!,
3439
+ baseUrl: process.env.NEXT_PUBLIC_VAIF_API_URL || 'https://api.vaif.studio',
3440
+ apiKey: process.env.VAIF_SECRET_KEY!,
3431
3441
  });
3432
3442
  }
3433
3443
  `},{path:"lib/storage.ts",content:`import { createVaifServer } from "./vaif";
@@ -3542,10 +3552,13 @@ function getContentType(fileName: string): string {
3542
3552
  `},{path:".env.example",content:`# VAIF Configuration
3543
3553
  # Get these values from https://vaif.studio/app/security/api-keys \u2192 Project Settings \u2192 API Keys
3544
3554
 
3545
- NEXT_PUBLIC_VAIF_PROJECT_ID=your-project-id
3546
- NEXT_PUBLIC_VAIF_API_KEY=your-anon-key
3555
+ NEXT_PUBLIC_VAIF_API_URL=https://api.vaif.studio
3556
+ NEXT_PUBLIC_VAIF_API_KEY=your-api-key
3547
3557
  VAIF_SECRET_KEY=your-secret-key
3548
3558
 
3559
+ # Database connection (for vaif generate, vaif db push)
3560
+ # DATABASE_URL=postgresql://user:password@host:5432/dbname
3561
+
3549
3562
  # CLI uses these (non-prefixed) for vaif db push, vaif secrets, etc.
3550
3563
  VAIF_PROJECT_ID=your-project-id
3551
3564
  `},{path:"README.md",content:`# E-commerce API \u2014 VAIF Studio
@@ -3688,16 +3701,16 @@ export const posts = pgTable("posts", {
3688
3701
 
3689
3702
  return Response.json({ message: \`Hello, \${name}!\` });
3690
3703
  }
3691
- `}]},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 ve(){console.log(""),console.log(i.bold("Available project templates")),console.log("");let a=26,t=22;console.log(` ${i.gray("Template".padEnd(a))}${i.gray("Stack".padEnd(t))}${i.gray("Description")}`),console.log(i.gray(" "+"-".repeat(a+t+40)));for(let[n,e]of Object.entries(j))console.log(` ${i.cyan(n.padEnd(a))}${i.yellow(e.tag.padEnd(t))}${i.white(e.description)}`);console.log(""),console.log(i.gray("Usage:")),console.log(i.gray(" npx @vaiftech/cli init --template <name>")),console.log(i.gray(" npx @vaiftech/cli init -t nextjs-fullstack")),console.log(i.gray(" npx @vaiftech/cli init -t react-spa --features auth,database,realtime")),console.log(""),console.log(i.gray("Available features: auth, database, realtime, storage, functions")),console.log("");}async function J(a){if(!process.stdin.isTTY||!process.stdout.isTTY)return a;let t=new Set(a.map(e=>b.findIndex(s=>s.name===e)).filter(e=>e>=0)),n=0;return new Promise(e=>{let s=D.createInterface({input:process.stdin,output:process.stdout});D.emitKeypressEvents(process.stdin,s),process.stdin.setRawMode&&process.stdin.setRawMode(true);function o(){let u=b.length+2;process.stdout.write(`\x1B[${u}A`),c();}function c(){console.log(i.bold(`
3692
- ? Which VAIF features do you want to include?`)),b.forEach((u,l)=>{let r=t.has(l)?i.green("[x]"):"[ ]",d=l===n?i.cyan("> "):" ";console.log(`${d}${r} ${u.label} ${i.gray(`(${u.description})`)}`);}),console.log(i.gray(" (up/down to move, space to toggle, enter to confirm)"));}c(),process.stdin.on("keypress",(u,l)=>{if(l.name==="up"&&n>0)n--,o();else if(l.name==="down"&&n<b.length-1)n++,o();else if(l.name==="space")t.has(n)?t.delete(n):t.add(n),o();else if(l.name==="return"){process.stdin.setRawMode&&process.stdin.setRawMode(false),s.close();let r=[...t].sort().map(d=>b[d].name);e(r.length>0?r:a);}else l.name==="c"&&l.ctrl&&(process.stdin.setRawMode&&process.stdin.setRawMode(false),s.close(),process.exit(0));});})}async function F(a,t={}){let n=j[a];n||(console.log(i.red(`
3693
- Unknown template: ${a}`)),console.log(i.yellow(`Run 'vaif templates' to see available templates.
3694
- `)),process.exit(1));let e;t.features&&t.features.length>0?e=t.features.filter(r=>b.some(d=>d.name===r)):t.addOnly?(console.log(i.red(`
3695
- No features specified.`)),console.log(i.yellow("Usage: vaif init --template <name> --add-features <features>")),console.log(i.gray("Available features: auth, database, realtime, storage, functions")),process.exit(1)):n.featureFiles&&Object.keys(n.featureFiles).length>0?e=await J(n.defaultFeatures??["database","auth"]):e=n.defaultFeatures??[],t.addOnly?(console.log(""),console.log(i.bold(`Adding features to ${i.cyan(n.name)} project...`)),console.log(i.gray(` Features: ${e.join(", ")}`)),console.log("")):(console.log(""),console.log(i.bold(`Scaffolding ${i.cyan(n.name)} template...`)),e.length>0&&console.log(i.gray(` Features: ${e.join(", ")}`)),console.log(""));let s=t.addOnly?[]:[...n.files];if(n.featureFiles)for(let r of e){let d=n.featureFiles[r];d&&s.push(...d);}let o=0,c=0;for(let r of s){let d=w.resolve(r.path),f=w.dirname(d);if(h.existsSync(f)||h.mkdirSync(f,{recursive:true}),r.path==="package.json"&&h.existsSync(d)&&!t.force)try{let p=JSON.parse(h.readFileSync(d,"utf-8")),v=JSON.parse(r.content),y=R=>{if(!R)return {};let C={};for(let[k,A]of Object.entries(R))!A.startsWith("workspace:")&&!A.startsWith("link:")&&!A.startsWith("file:")&&(C[k]=A);return C};p.dependencies={...y(p.dependencies),...v.dependencies||{}},p.devDependencies={...y(p.devDependencies),...v.devDependencies||{}},v.scripts&&(p.scripts={...p.scripts||{},...v.scripts}),h.writeFileSync(d,JSON.stringify(p,null,2)+`
3696
- `,"utf-8"),console.log(i.green(` merge ${r.path} (added dependencies)`)),o++;continue}catch{}if(h.existsSync(d)&&!t.force){console.log(i.yellow(` skip ${r.path} (already exists)`)),c++;continue}h.writeFileSync(d,r.content,"utf-8"),console.log(i.green(` create ${r.path}`)),o++;}console.log(""),o>0&&console.log(i.green(`Created ${o} file${o!==1?"s":""}.`)),c>0&&console.log(i.yellow(`Skipped ${c} file${c!==1?"s":""} (use --force to overwrite).`));let u={auth:{"@vaiftech/auth":"^1.0.0"},database:{},realtime:{},storage:{},functions:{}},l=w.resolve("package.json");if(h.existsSync(l)&&e.length>0)try{let r=JSON.parse(h.readFileSync(l,"utf-8")),d=!1;for(let f of e){let p=u[f];if(p)for(let[v,y]of Object.entries(p))r.dependencies?.[v]||(r.dependencies=r.dependencies||{},r.dependencies[v]=y,d=!0);}d&&h.writeFileSync(l,JSON.stringify(r,null,2)+`
3697
- `,"utf-8");}catch{}(n.dependencies?.length||n.devDependencies?.length)&&(console.log(""),console.log(i.bold("Install dependencies:")),n.dependencies?.length&&console.log(i.cyan(` npm install ${n.dependencies.join(" ")}`)),n.devDependencies?.length&&console.log(i.cyan(` npm install -D ${n.devDependencies.join(" ")}`))),console.log(""),console.log(i.bold.green("Project scaffolded successfully!")),console.log(""),console.log(i.bold(" Next steps:")),n.postInstructions.forEach(r=>{console.log(i.gray(` ${r}`));}),console.log(""),console.log(i.gray(" Get your project credentials at https://vaif.studio/app/security/api-keys")),console.log("");}var X={$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 Se(a){if(a.addFeatures){a.template||(console.log(i.red(`
3698
- --add-features requires --template to know which template to use.`)),console.log(i.gray("Example: vaif init --template react-spa --add-features functions,storage")),process.exit(1));let e=a.addFeatures.split(",").map(s=>s.trim());await F(a.template,{force:a.force,features:e,addOnly:true});return}let t=z("Initializing VAIF configuration...").start(),n=w.resolve("vaif.config.json");h.existsSync(n)&&!a.force&&(t.fail("vaif.config.json already exists"),console.log(i.yellow(`
3699
- Use --force to overwrite existing configuration.`)),process.exit(1));try{if(h.writeFileSync(n,JSON.stringify(X,null,2),"utf-8"),t.succeed("Created vaif.config.json"),a.template){let e=a.features?a.features.split(",").map(s=>s.trim()):void 0;await F(a.template,{force:a.force,features:e});}else {let e=w.resolve(".env.example");if(h.existsSync(e)||(h.writeFileSync(e,`# VAIF Configuration
3704
+ `}]},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(`
3705
+ ? 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(`
3706
+ Unknown template: ${a}`)),console.log(c.yellow(`Run 'vaif templates' to see available templates.
3707
+ `)),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(`
3708
+ 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=N.resolve(i.path),g=N.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)+`
3709
+ `,"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=N.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)+`
3710
+ `,"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(`
3711
+ --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=N.resolve("vaif.config.json");y.existsSync(n)&&!a.force&&(t.fail("vaif.config.json already exists"),console.log(c.yellow(`
3712
+ 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=N.resolve(".env.example");if(y.existsSync(e)||(y.writeFileSync(e,`# VAIF Configuration
3700
3713
  DATABASE_URL=postgresql://user:password@localhost:5432/database
3701
3714
  VAIF_API_KEY=your-api-key
3702
- `,"utf-8"),console.log(i.gray("Created .env.example"))),a.typescript){let s=w.resolve("src/types");h.existsSync(s)||(h.mkdirSync(s,{recursive:!0}),console.log(i.gray("Created src/types directory")));}console.log(""),console.log(i.green("VAIF initialized successfully!")),console.log(""),console.log(i.gray("Next steps:")),console.log(i.gray(" 1. Update vaif.config.json with your project ID")),console.log(i.gray(" 2. Set DATABASE_URL in your environment")),console.log(i.gray(" 3. Run: npx vaif generate")),console.log("");}}catch(e){t.fail("Failed to initialize"),e instanceof Error&&console.error(i.red(`
3703
- Error: ${e.message}`)),process.exit(1);}}export{L as a,de as b,ve as c,Se as d};
3715
+ `,"utf-8"),console.log(c.gray("Created .env.example"))),a.typescript){let r=N.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(`
3716
+ Error: ${e.message}`)),process.exit(1);}}export{K as a,R as b,Le as c,Ue as d,Ne as e,Ye as f,Qe as g,rt as h};