@vaiftech/cli 1.6.3 → 1.6.5

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.6.3) — 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.6.5) — 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
 
@@ -160,6 +160,17 @@ vaif init --typescript # Setup for TypeScript
160
160
  vaif init --force # Overwrite existing config
161
161
  ```
162
162
 
163
+ ### Add Features to Existing Project
164
+
165
+ Add features you didn't include during initial scaffolding:
166
+
167
+ ```bash
168
+ vaif init --template react-spa --add-features functions
169
+ vaif init --template react-spa --add-features functions,storage
170
+ ```
171
+
172
+ This only creates the feature-specific files (e.g., `functions/hello.ts`) without touching existing files.
173
+
163
174
  ### Templates
164
175
 
165
176
  ```bash
@@ -1,10 +1,10 @@
1
- import g from'fs';import w from'path';import M from'dotenv';import K from'pg';import z from'ora';import r from'chalk';import q from'prettier';import U from'readline';M.config();async function V(n){let t=w.resolve(n);if(!g.existsSync(t))return null;try{let a=g.readFileSync(t,"utf-8"),e=JSON.parse(a);return e.database?.url&&(e.database.url=N(e.database.url)),e.api?.apiKey&&(e.api.apiKey=N(e.api.apiKey)),e}catch{throw new Error(`Failed to parse config file: ${n}`)}}function N(n){return n.replace(/\$\{([^}]+)\}/g,(t,a)=>process.env[a]||t)}async function B(n,t){let a=await n.query(`
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(`
2
2
  SELECT table_name, table_type
3
3
  FROM information_schema.tables
4
4
  WHERE table_schema = $1
5
5
  AND table_type = 'BASE TABLE'
6
6
  ORDER BY table_name
7
- `,[t]),e=await n.query(`
7
+ `,[t]),e=await a.query(`
8
8
  SELECT
9
9
  table_name,
10
10
  column_name,
@@ -19,7 +19,7 @@ import g from'fs';import w from'path';import M from'dotenv';import K from'pg';im
19
19
  FROM information_schema.columns
20
20
  WHERE table_schema = $1
21
21
  ORDER BY table_name, ordinal_position
22
- `,[t]),o=await n.query(`
22
+ `,[t]),s=await a.query(`
23
23
  SELECT
24
24
  tc.constraint_name,
25
25
  tc.table_name,
@@ -35,7 +35,7 @@ import g from'fs';import w from'path';import M from'dotenv';import K from'pg';im
35
35
  AND ccu.table_schema = tc.table_schema
36
36
  WHERE tc.constraint_type = 'FOREIGN KEY'
37
37
  AND tc.table_schema = $1
38
- `,[t]),s=await n.query(`
38
+ `,[t]),o=await a.query(`
39
39
  SELECT
40
40
  t.typname as enum_name,
41
41
  e.enumlabel as enum_value
@@ -44,22 +44,22 @@ import g from'fs';import w from'path';import M from'dotenv';import K from'pg';im
44
44
  JOIN pg_namespace n ON n.oid = t.typnamespace
45
45
  WHERE n.nspname = $1
46
46
  ORDER BY t.typname, e.enumsortorder
47
- `,[t]),c=new Map;for(let l of a.rows)c.set(l.table_name,[]);for(let l of e.rows){let i=c.get(l.table_name);i&&i.push(l);}let d=new Map;for(let l of s.rows){let i=d.get(l.enum_name)||[];i.push(l.enum_value),d.set(l.enum_name,i);}return {tables:c,enums:d,foreignKeys:o.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(n,t){let{data_type:a,udt_name:e,is_nullable:o}=n;if(t.has(e)){let d=t.get(e).map(l=>`"${l}"`).join(" | ");return o==="YES"?`(${d}) | null`:d}if(a==="ARRAY"){let c=e.replace(/^_/,"");if(t.has(c)){let i=t.get(c).map(u=>`"${u}"`).join(" | ");return o==="YES"?`(${i})[] | null`:`(${i})[]`}let d=S[c]||"unknown";return o==="YES"?`${d}[] | null`:`${d}[]`}let s=S[a]||S[e]||"unknown";return o==="YES"&&(s=`${s} | null`),s}function x(n){return n.split(/[_\-\s]+/).map(t=>t.charAt(0).toUpperCase()+t.slice(1).toLowerCase()).join("")}function $(n,t){let a=x(n),e=t.map(o=>` | "${o}"`).join(`
48
- `);return `export type ${a} =
49
- ${e};`}function G(n,t,a){let e=x(n),o=[],s=[],c=[];for(let u of t){let f=Y(u,a),p=u.column_name,h=u.column_default!==null||u.is_identity==="YES",y=u.is_nullable==="YES";o.push(` ${p}: ${f};`),h||u.column_name==="id"?s.push(` ${p}?: ${f.replace(" | null","")} | null;`):y?s.push(` ${p}?: ${f};`):s.push(` ${p}: ${f.replace(" | null","")};`),c.push(` ${p}?: ${f.replace(" | null","")} | null;`);}let d=`export interface ${e} {
50
- ${o.join(`
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(`
48
+ `);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(`
51
51
  `)}
52
52
  }`,l=`export interface ${e}Insert {
53
- ${s.join(`
53
+ ${o.join(`
54
54
  `)}
55
- }`,i=`export interface ${e}Update {
55
+ }`,r=`export interface ${e}Update {
56
56
  ${c.join(`
57
57
  `)}
58
- }`;return {base:d,insert:l,update:i}}function H(n,t,a){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[s,c]of t)e.push($(s,c)),e.push("");}e.push("// ============ TABLES ============"),e.push("");let o=[];for(let[s,c]of n){let{base:d,insert:l,update:i}=G(s,c,t);o.push(s),e.push(d),e.push(""),e.push(l),e.push(""),e.push(i),e.push("");}e.push("// ============ DATABASE SCHEMA ============"),e.push(""),e.push("export interface Database {");for(let s of o){let c=x(s);e.push(` ${s}: {`),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(n){let t=z("Loading configuration...").start();try{let a=await V(n.config),e=n.connection||a?.database?.url||process.env.DATABASE_URL;e||(t.fail("No database connection string provided"),console.log(r.yellow(`
60
- Provide a connection string via:`)),console.log(r.gray(" --connection <url>")),console.log(r.gray(" DATABASE_URL environment variable")),console.log(r.gray(" vaif.config.json database.url")),process.exit(1)),t.text="Connecting to database...";let o=new K.Client({connectionString:e});await o.connect(),t.text="Introspecting schema...";let{tables:s,enums:c,foreignKeys:d}=await B(o,n.schema);if(await o.end(),s.size===0){t.warn(`No tables found in schema "${n.schema}"`);return}t.text=`Generating types for ${s.size} tables...`;let l=H(s,c,d),i=await q.format(l,{parser:"typescript",semi:!0,singleQuote:!1,trailingComma:"es5",printWidth:100});if(n.dryRun){t.succeed("Generated types (dry run):"),console.log(""),console.log(r.gray("\u2500".repeat(60))),console.log(i),console.log(r.gray("\u2500".repeat(60)));return}let u=w.resolve(n.output),f=w.dirname(u);g.existsSync(f)||g.mkdirSync(f,{recursive:!0}),g.writeFileSync(u,i,"utf-8"),t.succeed(`Generated types for ${s.size} tables \u2192 ${r.cyan(n.output)}`),console.log(""),console.log(r.green("Generated:")),console.log(r.gray(` Tables: ${s.size}`)),console.log(r.gray(` Enums: ${c.size}`)),console.log(""),console.log(r.gray("Import in your code:")),console.log(r.cyan(` import type { Database, Row, Insert, Update } from "${n.output.replace(/\.ts$/,"")}";`));}catch(a){t.fail("Failed to generate types"),a instanceof Error&&(console.error(r.red(`
61
- Error: ${a.message}`)),a.message.includes("ECONNREFUSED")&&console.log(r.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"}],D={"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:`{
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:`{
63
63
  "name": "my-vaif-app",
64
64
  "private": true,
65
65
  "version": "0.1.0",
@@ -3688,14 +3688,16 @@ export const posts = pgTable("posts", {
3688
3688
 
3689
3689
  return Response.json({ message: \`Hello, \${name}!\` });
3690
3690
  }
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(r.bold("Available project templates")),console.log("");let n=26,t=22;console.log(` ${r.gray("Template".padEnd(n))}${r.gray("Stack".padEnd(t))}${r.gray("Description")}`),console.log(r.gray(" "+"-".repeat(n+t+40)));for(let[a,e]of Object.entries(D))console.log(` ${r.cyan(a.padEnd(n))}${r.yellow(e.tag.padEnd(t))}${r.white(e.description)}`);console.log(""),console.log(r.gray("Usage:")),console.log(r.gray(" npx @vaiftech/cli init --template <name>")),console.log(r.gray(" npx @vaiftech/cli init -t nextjs-fullstack")),console.log(r.gray(" npx @vaiftech/cli init -t react-spa --features auth,database,realtime")),console.log(""),console.log(r.gray("Available features: auth, database, realtime, storage, functions")),console.log("");}async function J(n){if(!process.stdin.isTTY||!process.stdout.isTTY)return n;let t=new Set(n.map(e=>b.findIndex(o=>o.name===e)).filter(e=>e>=0)),a=0;return new Promise(e=>{let o=U.createInterface({input:process.stdin,output:process.stdout});U.emitKeypressEvents(process.stdin,o),process.stdin.setRawMode&&process.stdin.setRawMode(true);function s(){let d=b.length+2;process.stdout.write(`\x1B[${d}A`),c();}function c(){console.log(r.bold(`
3692
- ? Which VAIF features do you want to include?`)),b.forEach((d,l)=>{let i=t.has(l)?r.green("[x]"):"[ ]",u=l===a?r.cyan("> "):" ";console.log(`${u}${i} ${d.label} ${r.gray(`(${d.description})`)}`);}),console.log(r.gray(" (up/down to move, space to toggle, enter to confirm)"));}c(),process.stdin.on("keypress",(d,l)=>{if(l.name==="up"&&a>0)a--,s();else if(l.name==="down"&&a<b.length-1)a++,s();else if(l.name==="space")t.has(a)?t.delete(a):t.add(a),s();else if(l.name==="return"){process.stdin.setRawMode&&process.stdin.setRawMode(false),o.close();let i=[...t].sort().map(u=>b[u].name);e(i.length>0?i:n);}else l.name==="c"&&l.ctrl&&(process.stdin.setRawMode&&process.stdin.setRawMode(false),o.close(),process.exit(0));});})}async function j(n,t={}){let a=D[n];a||(console.log(r.red(`
3693
- Unknown template: ${n}`)),console.log(r.yellow(`Run 'vaif templates' to see available templates.
3694
- `)),process.exit(1));let e;t.features&&t.features.length>0?e=t.features.filter(i=>b.some(u=>u.name===i)):a.featureFiles&&Object.keys(a.featureFiles).length>0?e=await J(a.defaultFeatures??["database","auth"]):e=a.defaultFeatures??[],console.log(""),console.log(r.bold(`Scaffolding ${r.cyan(a.name)} template...`)),e.length>0&&console.log(r.gray(` Features: ${e.join(", ")}`)),console.log("");let o=[...a.files];if(a.featureFiles)for(let i of e){let u=a.featureFiles[i];u&&o.push(...u);}let s=0,c=0;for(let i of o){let u=w.resolve(i.path),f=w.dirname(u);if(g.existsSync(f)||g.mkdirSync(f,{recursive:true}),i.path==="package.json"&&g.existsSync(u)&&!t.force)try{let p=JSON.parse(g.readFileSync(u,"utf-8")),h=JSON.parse(i.content),y=P=>{if(!P)return {};let R={};for(let[k,A]of Object.entries(P))!A.startsWith("workspace:")&&!A.startsWith("link:")&&!A.startsWith("file:")&&(R[k]=A);return R};p.dependencies={...y(p.dependencies),...h.dependencies||{}},p.devDependencies={...y(p.devDependencies),...h.devDependencies||{}},h.scripts&&(p.scripts={...p.scripts||{},...h.scripts}),g.writeFileSync(u,JSON.stringify(p,null,2)+`
3695
- `,"utf-8"),console.log(r.green(` merge ${i.path} (added dependencies)`)),s++;continue}catch{}if(g.existsSync(u)&&!t.force){console.log(r.yellow(` skip ${i.path} (already exists)`)),c++;continue}g.writeFileSync(u,i.content,"utf-8"),console.log(r.green(` create ${i.path}`)),s++;}console.log(""),s>0&&console.log(r.green(`Created ${s} file${s!==1?"s":""}.`)),c>0&&console.log(r.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(g.existsSync(l)&&e.length>0)try{let i=JSON.parse(g.readFileSync(l,"utf-8")),u=!1;for(let f of e){let p=d[f];if(p)for(let[h,y]of Object.entries(p))i.dependencies?.[h]||(i.dependencies=i.dependencies||{},i.dependencies[h]=y,u=!0);}u&&g.writeFileSync(l,JSON.stringify(i,null,2)+`
3696
- `,"utf-8");}catch{}(a.dependencies?.length||a.devDependencies?.length)&&(console.log(""),console.log(r.bold("Install dependencies:")),a.dependencies?.length&&console.log(r.cyan(` npm install ${a.dependencies.join(" ")}`)),a.devDependencies?.length&&console.log(r.cyan(` npm install -D ${a.devDependencies.join(" ")}`))),console.log(""),console.log(r.bold.green("Project scaffolded successfully!")),console.log(""),console.log(r.bold(" Next steps:")),a.postInstructions.forEach(i=>{console.log(r.gray(` ${i}`));}),console.log(""),console.log(r.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(n){let t=z("Initializing VAIF configuration...").start(),a=w.resolve("vaif.config.json");g.existsSync(a)&&!n.force&&(t.fail("vaif.config.json already exists"),console.log(r.yellow(`
3697
- Use --force to overwrite existing configuration.`)),process.exit(1));try{if(g.writeFileSync(a,JSON.stringify(X,null,2),"utf-8"),t.succeed("Created vaif.config.json"),n.template){let e=n.features?n.features.split(",").map(o=>o.trim()):void 0;await j(n.template,{force:n.force,features:e});}else {let e=w.resolve(".env.example");if(g.existsSync(e)||(g.writeFileSync(e,`# VAIF Configuration
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
3698
3700
  DATABASE_URL=postgresql://user:password@localhost:5432/database
3699
3701
  VAIF_API_KEY=your-api-key
3700
- `,"utf-8"),console.log(r.gray("Created .env.example"))),n.typescript){let o=w.resolve("src/types");g.existsSync(o)||(g.mkdirSync(o,{recursive:!0}),console.log(r.gray("Created src/types directory")));}console.log(""),console.log(r.green("VAIF initialized successfully!")),console.log(""),console.log(r.gray("Next steps:")),console.log(r.gray(" 1. Update vaif.config.json with your project ID")),console.log(r.gray(" 2. Set DATABASE_URL in your environment")),console.log(r.gray(" 3. Run: npx vaif generate")),console.log("");}}catch(e){t.fail("Failed to initialize"),e instanceof Error&&console.error(r.red(`
3701
- Error: ${e.message}`)),process.exit(1);}}export{V as a,ue as b,ve as c,Se as d};
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};