@vaiftech/cli 1.7.1 → 1.7.3

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.1) — 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.3) — 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 n from'chalk';import q from'prettier';import D from'readline';M.config();async function N(a){let t=w.resolve(a);if(!h.existsSync(t))return null;try{let i=h.readFileSync(t,"utf-8"),e=JSON.parse(i);return e.database?.url&&(e.database.url=L(e.database.url)),e.api?.apiKey&&(e.api.apiKey=L(e.api.apiKey)),e}catch{throw new Error(`Failed to parse config file: ${a}`)}}function L(a){return a.replace(/\$\{([^}]+)\}/g,(t,i)=>process.env[i]||t)}async function B(a,t){let i=await a.query(`
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 Ne(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 Le(){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(`
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 i.rows)c.set(l.table_name,[]);for(let l of e.rows){let r=c.get(l.table_name);r&&r.push(l);}let d=new Map;for(let l of o.rows){let r=d.get(l.enum_name)||[];r.push(l.enum_value),d.set(l.enum_name,r);}return {tables:c,enums:d,foreignKeys:s.rows}}var T={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:i,udt_name:e,is_nullable:s}=a;if(t.has(e)){let d=t.get(e).map(l=>`"${l}"`).join(" | ");return s==="YES"?`(${d}) | null`:d}if(i==="ARRAY"){let c=e.replace(/^_/,"");if(t.has(c)){let r=t.get(c).map(u=>`"${u}"`).join(" | ");return s==="YES"?`(${r})[] | null`:`(${r})[]`}let d=T[c]||"unknown";return s==="YES"?`${d}[] | null`:`${d}[]`}let o=T[i]||T[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 i=x(a),e=t.map(s=>` | "${s}"`).join(`
48
- `);return `export type ${i} =
49
- ${e};`}function G(a,t,i){let e=x(a),s=[],o=[],c=[];for(let u of t){let f=Y(u,i),p=u.column_name,v=u.column_default!==null||u.is_identity==="YES",y=u.is_nullable==="YES";s.push(` ${p}: ${f};`),v||u.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 d=`export interface ${e} {
50
- ${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 N={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=N[l]||"unknown";return r==="YES"?`${u}[] | null`:`${u}[]`}let o=N[n]||N[e]||"unknown";return r==="YES"&&(o=`${o} | null`),o}function L(a){return a.split(/[_\-\s]+/).map(t=>t.charAt(0).toUpperCase()+t.slice(1).toLowerCase()).join("")}function fe(a,t){let n=L(a),e=t.map(r=>` | "${r}"`).join(`
57
+ `);return `export type ${n} =
58
+ ${e};`}function ge(a,t,n){let e=L(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:d,insert:l,update:r}}function H(a,t,i){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:d,insert:l,update:r}=G(o,c,t);s.push(o),e.push(d),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 ue(a){let t=z("Loading configuration...").start();try{let i=await N(a.config),e=a.connection||i?.database?.url||process.env.DATABASE_URL;(!e||e.includes("${"))&&(t.fail("No database connection string provided"),console.log(n.yellow(`
60
- Provide a connection string via:`)),console.log(n.gray(" --connection postgresql://user:pass@host:5432/db")),console.log(n.gray(" DATABASE_URL environment variable in .env")),console.log(n.gray(" database.url in vaif.config.json")),e?.includes("${")&&(console.log(n.yellow("\nYour vaif.config.json references ${DATABASE_URL} but the env var is not set.")),console.log(n.gray(" Add DATABASE_URL to your .env file or pass --connection directly."))),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:d}=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,d),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(n.gray("\u2500".repeat(60))),console.log(r),console.log(n.gray("\u2500".repeat(60)));return}let u=w.resolve(a.output),f=w.dirname(u);h.existsSync(f)||h.mkdirSync(f,{recursive:!0}),h.writeFileSync(u,r,"utf-8"),t.succeed(`Generated types for ${o.size} tables \u2192 ${n.cyan(a.output)}`),console.log(""),console.log(n.green("Generated:")),console.log(n.gray(` Tables: ${o.size}`)),console.log(n.gray(` Enums: ${c.size}`)),console.log(""),console.log(n.gray("Import in your code:")),console.log(n.cyan(` import type { Database, Row, Insert, Update } from "${a.output.replace(/\.ts$/,"")}";`));}catch(i){t.fail("Failed to generate types"),i instanceof Error&&(console.error(n.red(`
61
- Error: ${i.message}`)),i.message.includes("ECONNREFUSED")&&console.log(n.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"}],k={"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=L(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:`{
63
74
  "name": "my-vaif-app",
64
75
  "private": true,
65
76
  "version": "0.1.0",
@@ -173,10 +184,7 @@ NEXT_PUBLIC_VAIF_API_URL=https://api.vaif.studio
173
184
  NEXT_PUBLIC_VAIF_API_KEY=your-api-key
174
185
  VAIF_SECRET_KEY=your-secret-key
175
186
 
176
- # Database connection (for vaif generate, vaif db push)
177
- # DATABASE_URL=postgresql://user:password@host:5432/dbname
178
-
179
- # CLI uses these (non-prefixed) for vaif db push, vaif secrets, etc.
187
+ # CLI project ID (for vaif generate, vaif pull, vaif secrets, etc.)
180
188
  VAIF_PROJECT_ID=your-project-id
181
189
  `},{path:".gitignore",content:`node_modules
182
190
  .next
@@ -671,9 +679,6 @@ interface ImportMeta {
671
679
 
672
680
  VITE_VAIF_API_URL=https://api.vaif.studio
673
681
  VITE_VAIF_API_KEY=your-api-key
674
-
675
- # Database connection (for vaif generate, vaif db push)
676
- # DATABASE_URL=postgresql://user:password@host:5432/dbname
677
682
  `},{path:".gitignore",content:`node_modules
678
683
  dist
679
684
  .env
@@ -2668,9 +2673,6 @@ export async function deleteTodo(id: string): Promise<void> {
2668
2673
 
2669
2674
  VITE_VAIF_API_URL=https://api.vaif.studio
2670
2675
  VITE_VAIF_API_KEY=your-api-key
2671
-
2672
- # Database connection (for vaif generate, vaif db push)
2673
- # DATABASE_URL=postgresql://user:password@host:5432/dbname
2674
2676
  `},{path:"README.md",content:`# Todo App \u2014 VAIF Starter
2675
2677
 
2676
2678
  A simple React todo application for learning [VAIF Studio](https://vaif.studio) basics, including typed database queries and CRUD operations.
@@ -2949,9 +2951,6 @@ export function useRealtimeMessages({
2949
2951
 
2950
2952
  VITE_VAIF_API_URL=https://api.vaif.studio
2951
2953
  VITE_VAIF_API_KEY=your-api-key
2952
-
2953
- # Database connection (for vaif generate, vaif db push)
2954
- # DATABASE_URL=postgresql://user:password@host:5432/dbname
2955
2954
  `},{path:"README.md",content:`# Realtime Chat \u2014 VAIF Starter
2956
2955
 
2957
2956
  A React chat application with live messaging powered by [VAIF Studio](https://vaif.studio) realtime subscriptions.
@@ -3267,10 +3266,7 @@ NEXT_PUBLIC_VAIF_API_URL=https://api.vaif.studio
3267
3266
  NEXT_PUBLIC_VAIF_API_KEY=your-api-key
3268
3267
  VAIF_SECRET_KEY=your-secret-key
3269
3268
 
3270
- # Database connection (for vaif generate, vaif db push)
3271
- # DATABASE_URL=postgresql://user:password@host:5432/dbname
3272
-
3273
- # CLI uses these (non-prefixed) for vaif db push, vaif secrets, etc.
3269
+ # CLI project ID (for vaif generate, vaif pull, vaif secrets, etc.)
3274
3270
  VAIF_PROJECT_ID=your-project-id
3275
3271
  `},{path:"README.md",content:`# SaaS Starter \u2014 VAIF Studio
3276
3272
 
@@ -3545,10 +3541,7 @@ NEXT_PUBLIC_VAIF_API_URL=https://api.vaif.studio
3545
3541
  NEXT_PUBLIC_VAIF_API_KEY=your-api-key
3546
3542
  VAIF_SECRET_KEY=your-secret-key
3547
3543
 
3548
- # Database connection (for vaif generate, vaif db push)
3549
- # DATABASE_URL=postgresql://user:password@host:5432/dbname
3550
-
3551
- # CLI uses these (non-prefixed) for vaif db push, vaif secrets, etc.
3544
+ # CLI project ID (for vaif generate, vaif pull, vaif secrets, etc.)
3552
3545
  VAIF_PROJECT_ID=your-project-id
3553
3546
  `},{path:"README.md",content:`# E-commerce API \u2014 VAIF Studio
3554
3547
 
@@ -3690,16 +3683,16 @@ export const posts = pgTable("posts", {
3690
3683
 
3691
3684
  return Response.json({ message: \`Hello, \${name}!\` });
3692
3685
  }
3693
- `}]},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(n.bold("Available project templates")),console.log("");let a=26,t=22;console.log(` ${n.gray("Template".padEnd(a))}${n.gray("Stack".padEnd(t))}${n.gray("Description")}`),console.log(n.gray(" "+"-".repeat(a+t+40)));for(let[i,e]of Object.entries(k))console.log(` ${n.cyan(i.padEnd(a))}${n.yellow(e.tag.padEnd(t))}${n.white(e.description)}`);console.log(""),console.log(n.gray("Usage:")),console.log(n.gray(" npx @vaiftech/cli init --template <name>")),console.log(n.gray(" npx @vaiftech/cli init -t nextjs-fullstack")),console.log(n.gray(" npx @vaiftech/cli init -t react-spa --features auth,database,realtime")),console.log(""),console.log(n.gray("Available features: auth, database, realtime, storage, functions")),console.log("");}async function W(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)),i=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 d=b.length+2;process.stdout.write(`\x1B[${d}A`),c();}function c(){console.log(n.bold(`
3694
- ? Which VAIF features do you want to include?`)),b.forEach((d,l)=>{let r=t.has(l)?n.green("[x]"):"[ ]",u=l===i?n.cyan("> "):" ";console.log(`${u}${r} ${d.label} ${n.gray(`(${d.description})`)}`);}),console.log(n.gray(" (up/down to move, space to toggle, enter to confirm)"));}c(),process.stdin.on("keypress",(d,l)=>{if(l.name==="up"&&i>0)i--,o();else if(l.name==="down"&&i<b.length-1)i++,o();else if(l.name==="space")t.has(i)?t.delete(i):t.add(i),o();else if(l.name==="return"){process.stdin.setRawMode&&process.stdin.setRawMode(false),s.close();let r=[...t].sort().map(u=>b[u].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 i=k[a];i||(console.log(n.red(`
3695
- Unknown template: ${a}`)),console.log(n.yellow(`Run 'vaif templates' to see available templates.
3696
- `)),process.exit(1));let e;t.features&&t.features.length>0?e=t.features.filter(r=>b.some(u=>u.name===r)):t.addOnly?(console.log(n.red(`
3697
- No features specified.`)),console.log(n.yellow("Usage: vaif init --template <name> --add-features <features>")),console.log(n.gray("Available features: auth, database, realtime, storage, functions")),process.exit(1)):i.featureFiles&&Object.keys(i.featureFiles).length>0?e=await W(i.defaultFeatures??["database","auth"]):e=i.defaultFeatures??[],t.addOnly?(console.log(""),console.log(n.bold(`Adding features to ${n.cyan(i.name)} project...`)),console.log(n.gray(` Features: ${e.join(", ")}`)),console.log("")):(console.log(""),console.log(n.bold(`Scaffolding ${n.cyan(i.name)} template...`)),e.length>0&&console.log(n.gray(` Features: ${e.join(", ")}`)),console.log(""));let s=t.addOnly?[]:[...i.files];if(i.featureFiles)for(let r of e){let u=i.featureFiles[r];u&&s.push(...u);}let o=0,c=0;for(let r of s){let u=w.resolve(r.path),f=w.dirname(u);if(h.existsSync(f)||h.mkdirSync(f,{recursive:true}),r.path==="package.json"&&h.existsSync(u)&&!t.force)try{let p=JSON.parse(h.readFileSync(u,"utf-8")),v=JSON.parse(r.content),y=R=>{if(!R)return {};let C={};for(let[j,A]of Object.entries(R))!A.startsWith("workspace:")&&!A.startsWith("link:")&&!A.startsWith("file:")&&(C[j]=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(u,JSON.stringify(p,null,2)+`
3698
- `,"utf-8"),console.log(n.green(` merge ${r.path} (added dependencies)`)),o++;continue}catch{}if(h.existsSync(u)&&!t.force){console.log(n.yellow(` skip ${r.path} (already exists)`)),c++;continue}h.writeFileSync(u,r.content,"utf-8"),console.log(n.green(` create ${r.path}`)),o++;}console.log(""),o>0&&console.log(n.green(`Created ${o} file${o!==1?"s":""}.`)),c>0&&console.log(n.yellow(`Skipped ${c} file${c!==1?"s":""} (use --force to overwrite).`));let d={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")),u=!1;for(let f of e){let p=d[f];if(p)for(let[v,y]of Object.entries(p))r.dependencies?.[v]||(r.dependencies=r.dependencies||{},r.dependencies[v]=y,u=!0);}u&&h.writeFileSync(l,JSON.stringify(r,null,2)+`
3699
- `,"utf-8");}catch{}(i.dependencies?.length||i.devDependencies?.length)&&(console.log(""),console.log(n.bold("Install dependencies:")),i.dependencies?.length&&console.log(n.cyan(` npm install ${i.dependencies.join(" ")}`)),i.devDependencies?.length&&console.log(n.cyan(` npm install -D ${i.devDependencies.join(" ")}`))),console.log(""),console.log(n.bold.green("Project scaffolded successfully!")),console.log(""),console.log(n.bold(" Next steps:")),i.postInstructions.forEach(r=>{console.log(n.gray(` ${r}`));}),console.log(""),console.log(n.gray(" Get your project credentials at https://vaif.studio/app/security/api-keys")),console.log("");}var J={$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 Te(a){if(a.addFeatures){a.template||(console.log(n.red(`
3700
- --add-features requires --template to know which template to use.`)),console.log(n.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(),i=w.resolve("vaif.config.json");h.existsSync(i)&&!a.force&&(t.fail("vaif.config.json already exists"),console.log(n.yellow(`
3701
- Use --force to overwrite existing configuration.`)),process.exit(1));try{if(h.writeFileSync(i,JSON.stringify(J,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
3686
+ `}]},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(`
3687
+ ? 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(`
3688
+ Unknown template: ${a}`)),console.log(c.yellow(`Run 'vaif templates' to see available templates.
3689
+ `)),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(`
3690
+ 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)+`
3691
+ `,"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)+`
3692
+ `,"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(`
3693
+ --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(`
3694
+ 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
3702
3695
  DATABASE_URL=postgresql://user:password@localhost:5432/database
3703
3696
  VAIF_API_KEY=your-api-key
3704
- `,"utf-8"),console.log(n.gray("Created .env.example"))),a.typescript){let s=w.resolve("src/types");h.existsSync(s)||(h.mkdirSync(s,{recursive:!0}),console.log(n.gray("Created src/types directory")));}console.log(""),console.log(n.green("VAIF initialized successfully!")),console.log(""),console.log(n.gray("Next steps:")),console.log(n.gray(" 1. Update vaif.config.json with your project ID")),console.log(n.gray(" 2. Set DATABASE_URL in your environment")),console.log(n.gray(" 3. Run: npx vaif generate")),console.log("");}}catch(e){t.fail("Failed to initialize"),e instanceof Error&&console.error(n.red(`
3705
- Error: ${e.message}`)),process.exit(1);}}export{N as a,ue as b,ve as c,Te as d};
3697
+ `,"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(`
3698
+ Error: ${e.message}`)),process.exit(1);}}export{K as a,R as b,Ne as c,Le as d,Ue as e,Ye as f,Qe as g,rt as h};