@vaiftech/cli 1.6.6 → 1.7.1

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/dist/index.cjs CHANGED
@@ -1,4 +1,4 @@
1
- 'use strict';var v=require('fs'),w=require('path'),M=require('dotenv'),K=require('pg'),z=require('ora'),d=require('chalk'),q=require('prettier'),j=require('readline');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var v__default=/*#__PURE__*/_interopDefault(v);var w__default=/*#__PURE__*/_interopDefault(w);var M__default=/*#__PURE__*/_interopDefault(M);var K__default=/*#__PURE__*/_interopDefault(K);var z__default=/*#__PURE__*/_interopDefault(z);var d__default=/*#__PURE__*/_interopDefault(d);var q__default=/*#__PURE__*/_interopDefault(q);var j__default=/*#__PURE__*/_interopDefault(j);M__default.default.config();async function E(n){let a=w__default.default.resolve(n);if(!v__default.default.existsSync(a))return null;try{let t=v__default.default.readFileSync(a,"utf-8"),e=JSON.parse(t);return e.database?.url&&(e.database.url=U(e.database.url)),e.api?.apiKey&&(e.api.apiKey=U(e.api.apiKey)),e}catch{throw new Error(`Failed to parse config file: ${n}`)}}function U(n){return n.replace(/\$\{([^}]+)\}/g,(a,t)=>process.env[t]||a)}async function B(n,a){let t=await n.query(`
1
+ 'use strict';var v=require('fs'),w=require('path'),M=require('dotenv'),K=require('pg'),z=require('ora'),d=require('chalk'),q=require('prettier'),k=require('readline');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var v__default=/*#__PURE__*/_interopDefault(v);var w__default=/*#__PURE__*/_interopDefault(w);var M__default=/*#__PURE__*/_interopDefault(M);var K__default=/*#__PURE__*/_interopDefault(K);var z__default=/*#__PURE__*/_interopDefault(z);var d__default=/*#__PURE__*/_interopDefault(d);var q__default=/*#__PURE__*/_interopDefault(q);var k__default=/*#__PURE__*/_interopDefault(k);M__default.default.config();async function E(n){let a=w__default.default.resolve(n);if(!v__default.default.existsSync(a))return null;try{let t=v__default.default.readFileSync(a,"utf-8"),e=JSON.parse(t);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: ${n}`)}}function V(n){return n.replace(/\$\{([^}]+)\}/g,(a,t)=>process.env[t]||a)}async function B(n,a){let t=await n.query(`
2
2
  SELECT table_name, table_type
3
3
  FROM information_schema.tables
4
4
  WHERE table_schema = $1
@@ -46,7 +46,7 @@
46
46
  ORDER BY t.typname, e.enumsortorder
47
47
  `,[a]),c=new Map;for(let l of t.rows)c.set(l.table_name,[]);for(let l of e.rows){let s=c.get(l.table_name);s&&s.push(l);}let u=new Map;for(let l of r.rows){let s=u.get(l.enum_name)||[];s.push(l.enum_value),u.set(l.enum_name,s);}return {tables:c,enums:u,foreignKeys:i.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 $(n,a){let{data_type:t,udt_name:e,is_nullable:i}=n;if(a.has(e)){let u=a.get(e).map(l=>`"${l}"`).join(" | ");return i==="YES"?`(${u}) | null`:u}if(t==="ARRAY"){let c=e.replace(/^_/,"");if(a.has(c)){let s=a.get(c).map(o=>`"${o}"`).join(" | ");return i==="YES"?`(${s})[] | null`:`(${s})[]`}let u=S[c]||"unknown";return i==="YES"?`${u}[] | null`:`${u}[]`}let r=S[t]||S[e]||"unknown";return i==="YES"&&(r=`${r} | null`),r}function x(n){return n.split(/[_\-\s]+/).map(a=>a.charAt(0).toUpperCase()+a.slice(1).toLowerCase()).join("")}function Y(n,a){let t=x(n),e=a.map(i=>` | "${i}"`).join(`
48
48
  `);return `export type ${t} =
49
- ${e};`}function G(n,a,t){let e=x(n),i=[],r=[],c=[];for(let o of a){let p=$(o,t),m=o.column_name,g=o.column_default!==null||o.is_identity==="YES",y=o.is_nullable==="YES";i.push(` ${m}: ${p};`),g||o.column_name==="id"?r.push(` ${m}?: ${p.replace(" | null","")} | null;`):y?r.push(` ${m}?: ${p};`):r.push(` ${m}: ${p.replace(" | null","")};`),c.push(` ${m}?: ${p.replace(" | null","")} | null;`);}let u=`export interface ${e} {
49
+ ${e};`}function G(n,a,t){let e=x(n),i=[],r=[],c=[];for(let o of a){let p=$(o,t),m=o.column_name,g=o.column_default!==null||o.is_identity==="YES",b=o.is_nullable==="YES";i.push(` ${m}: ${p};`),g||o.column_name==="id"?r.push(` ${m}?: ${p.replace(" | null","")} | null;`):b?r.push(` ${m}?: ${p};`):r.push(` ${m}: ${p.replace(" | null","")};`),c.push(` ${m}?: ${p.replace(" | null","")} | null;`);}let u=`export interface ${e} {
50
50
  ${i.join(`
51
51
  `)}
52
52
  }`,l=`export interface ${e}Insert {
@@ -56,10 +56,10 @@ ${r.join(`
56
56
  ${c.join(`
57
57
  `)}
58
58
  }`;return {base:u,insert:l,update:s}}function H(n,a,t){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(a.size>0){e.push("// ============ ENUMS ============"),e.push("");for(let[r,c]of a)e.push(Y(r,c)),e.push("");}e.push("// ============ TABLES ============"),e.push("");let i=[];for(let[r,c]of n){let{base:u,insert:l,update:s}=G(r,c,a);i.push(r),e.push(u),e.push(""),e.push(l),e.push(""),e.push(s),e.push("");}e.push("// ============ DATABASE SCHEMA ============"),e.push(""),e.push("export interface Database {");for(let r of i){let c=x(r);e.push(` ${r}: {`),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 J(n){let a=z__default.default("Loading configuration...").start();try{let t=await E(n.config),e=n.connection||t?.database?.url||process.env.DATABASE_URL;e||(a.fail("No database connection string provided"),console.log(d__default.default.yellow(`
60
- Provide a connection string via:`)),console.log(d__default.default.gray(" --connection <url>")),console.log(d__default.default.gray(" DATABASE_URL environment variable")),console.log(d__default.default.gray(" vaif.config.json database.url")),process.exit(1)),a.text="Connecting to database...";let i=new K__default.default.Client({connectionString:e});await i.connect(),a.text="Introspecting schema...";let{tables:r,enums:c,foreignKeys:u}=await B(i,n.schema);if(await i.end(),r.size===0){a.warn(`No tables found in schema "${n.schema}"`);return}a.text=`Generating types for ${r.size} tables...`;let l=H(r,c,u),s=await q__default.default.format(l,{parser:"typescript",semi:!0,singleQuote:!1,trailingComma:"es5",printWidth:100});if(n.dryRun){a.succeed("Generated types (dry run):"),console.log(""),console.log(d__default.default.gray("\u2500".repeat(60))),console.log(s),console.log(d__default.default.gray("\u2500".repeat(60)));return}let o=w__default.default.resolve(n.output),p=w__default.default.dirname(o);v__default.default.existsSync(p)||v__default.default.mkdirSync(p,{recursive:!0}),v__default.default.writeFileSync(o,s,"utf-8"),a.succeed(`Generated types for ${r.size} tables \u2192 ${d__default.default.cyan(n.output)}`),console.log(""),console.log(d__default.default.green("Generated:")),console.log(d__default.default.gray(` Tables: ${r.size}`)),console.log(d__default.default.gray(` Enums: ${c.size}`)),console.log(""),console.log(d__default.default.gray("Import in your code:")),console.log(d__default.default.cyan(` import type { Database, Row, Insert, Update } from "${n.output.replace(/\.ts$/,"")}";`));}catch(t){a.fail("Failed to generate types"),t instanceof Error&&(console.error(d__default.default.red(`
59
+ `)}async function W(n){let a=z__default.default("Loading configuration...").start();try{let t=await E(n.config),e=n.connection||t?.database?.url||process.env.DATABASE_URL;(!e||e.includes("${"))&&(a.fail("No database connection string provided"),console.log(d__default.default.yellow(`
60
+ Provide a connection string via:`)),console.log(d__default.default.gray(" --connection postgresql://user:pass@host:5432/db")),console.log(d__default.default.gray(" DATABASE_URL environment variable in .env")),console.log(d__default.default.gray(" database.url in vaif.config.json")),e?.includes("${")&&(console.log(d__default.default.yellow("\nYour vaif.config.json references ${DATABASE_URL} but the env var is not set.")),console.log(d__default.default.gray(" Add DATABASE_URL to your .env file or pass --connection directly."))),process.exit(1)),a.text="Connecting to database...";let i=new K__default.default.Client({connectionString:e});await i.connect(),a.text="Introspecting schema...";let{tables:r,enums:c,foreignKeys:u}=await B(i,n.schema);if(await i.end(),r.size===0){a.warn(`No tables found in schema "${n.schema}"`);return}a.text=`Generating types for ${r.size} tables...`;let l=H(r,c,u),s=await q__default.default.format(l,{parser:"typescript",semi:!0,singleQuote:!1,trailingComma:"es5",printWidth:100});if(n.dryRun){a.succeed("Generated types (dry run):"),console.log(""),console.log(d__default.default.gray("\u2500".repeat(60))),console.log(s),console.log(d__default.default.gray("\u2500".repeat(60)));return}let o=w__default.default.resolve(n.output),p=w__default.default.dirname(o);v__default.default.existsSync(p)||v__default.default.mkdirSync(p,{recursive:!0}),v__default.default.writeFileSync(o,s,"utf-8"),a.succeed(`Generated types for ${r.size} tables \u2192 ${d__default.default.cyan(n.output)}`),console.log(""),console.log(d__default.default.green("Generated:")),console.log(d__default.default.gray(` Tables: ${r.size}`)),console.log(d__default.default.gray(` Enums: ${c.size}`)),console.log(""),console.log(d__default.default.gray("Import in your code:")),console.log(d__default.default.cyan(` import type { Database, Row, Insert, Update } from "${n.output.replace(/\.ts$/,"")}";`));}catch(t){a.fail("Failed to generate types"),t instanceof Error&&(console.error(d__default.default.red(`
61
61
  Error: ${t.message}`)),t.message.includes("ECONNREFUSED")&&console.log(d__default.default.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"}],W={"nextjs-fullstack":{name:"Next.js Full-Stack",description:"Next.js app with server/client VAIF client, auth middleware, and React hooks",tag:"Next.js",defaultFeatures:["database","auth"],files:[{path:"package.json",content:`{
62
+ Make sure your database is running and accessible.`))),process.exit(1);}}var y=[{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",
@@ -155,24 +155,27 @@ body {
155
155
 
156
156
  // Browser client \u2013 safe to use in Client Components
157
157
  export const vaif = createVaifClient({
158
- projectId: process.env.NEXT_PUBLIC_VAIF_PROJECT_ID!,
158
+ baseUrl: process.env.NEXT_PUBLIC_VAIF_API_URL || 'https://api.vaif.studio',
159
159
  apiKey: process.env.NEXT_PUBLIC_VAIF_API_KEY!,
160
160
  });
161
161
 
162
162
  // Server client \u2013 use in Server Components, Route Handlers, Server Actions
163
163
  export function createVaifServer() {
164
164
  return createVaifClient({
165
- projectId: process.env.NEXT_PUBLIC_VAIF_PROJECT_ID!,
166
- secretKey: process.env.VAIF_SECRET_KEY!,
165
+ baseUrl: process.env.NEXT_PUBLIC_VAIF_API_URL || 'https://api.vaif.studio',
166
+ apiKey: process.env.VAIF_SECRET_KEY!,
167
167
  });
168
168
  }
169
169
  `},{path:".env.local.example",content:`# VAIF Configuration
170
170
  # Get these values from https://vaif.studio/app/security/api-keys \u2192 Project Settings \u2192 API Keys
171
171
 
172
- NEXT_PUBLIC_VAIF_PROJECT_ID=your-project-id
173
- NEXT_PUBLIC_VAIF_API_KEY=your-anon-key
172
+ NEXT_PUBLIC_VAIF_API_URL=https://api.vaif.studio
173
+ NEXT_PUBLIC_VAIF_API_KEY=your-api-key
174
174
  VAIF_SECRET_KEY=your-secret-key
175
175
 
176
+ # Database connection (for vaif generate, vaif db push)
177
+ # DATABASE_URL=postgresql://user:password@host:5432/dbname
178
+
176
179
  # CLI uses these (non-prefixed) for vaif db push, vaif secrets, etc.
177
180
  VAIF_PROJECT_ID=your-project-id
178
181
  `},{path:".gitignore",content:`node_modules
@@ -288,8 +291,8 @@ export async function middleware(request: NextRequest) {
288
291
  if (!isProtected) return NextResponse.next();
289
292
 
290
293
  const vaif = createVaifClient({
291
- projectId: process.env.NEXT_PUBLIC_VAIF_PROJECT_ID!,
292
- secretKey: process.env.VAIF_SECRET_KEY!,
294
+ baseUrl: process.env.NEXT_PUBLIC_VAIF_API_URL || 'https://api.vaif.studio',
295
+ apiKey: process.env.VAIF_SECRET_KEY!,
293
296
  });
294
297
 
295
298
  return authMiddleware(vaif, request, {
@@ -650,13 +653,13 @@ function Home() {
650
653
  `},{path:"src/lib/vaif.ts",content:`import { createVaifClient } from "@vaiftech/client";
651
654
 
652
655
  export const vaif = createVaifClient({
653
- projectId: import.meta.env.VITE_VAIF_PROJECT_ID,
656
+ baseUrl: import.meta.env.VITE_VAIF_API_URL || 'https://api.vaif.studio',
654
657
  apiKey: import.meta.env.VITE_VAIF_API_KEY,
655
658
  });
656
659
  `},{path:"src/vite-env.d.ts",content:`/// <reference types="vite/client" />
657
660
 
658
661
  interface ImportMetaEnv {
659
- readonly VITE_VAIF_PROJECT_ID: string;
662
+ readonly VITE_VAIF_API_URL: string;
660
663
  readonly VITE_VAIF_API_KEY: string;
661
664
  }
662
665
 
@@ -664,13 +667,13 @@ interface ImportMeta {
664
667
  readonly env: ImportMetaEnv;
665
668
  }
666
669
  `},{path:".env.example",content:`# VAIF Configuration
667
- # Get these values from https://vaif.studio/app/security/api-keys \u2192 Project Settings \u2192 API Keys
670
+ # Get these values from https://vaif.studio/app/security/api-keys
668
671
 
669
- VITE_VAIF_PROJECT_ID=your-project-id
670
- VITE_VAIF_API_KEY=your-anon-key
672
+ VITE_VAIF_API_URL=https://api.vaif.studio
673
+ VITE_VAIF_API_KEY=your-api-key
671
674
 
672
- # CLI uses these (non-prefixed) for vaif db push, vaif secrets, etc.
673
- VAIF_PROJECT_ID=your-project-id
675
+ # Database connection (for vaif generate, vaif db push)
676
+ # DATABASE_URL=postgresql://user:password@host:5432/dbname
674
677
  `},{path:".gitignore",content:`node_modules
675
678
  dist
676
679
  .env
@@ -2609,7 +2612,7 @@ func HelloHandler(w http.ResponseWriter, r *http.Request) {
2609
2612
  `}]},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
2613
 
2611
2614
  export const vaif = createVaifClient({
2612
- projectId: import.meta.env.VITE_VAIF_PROJECT_ID,
2615
+ baseUrl: import.meta.env.VITE_VAIF_API_URL || 'https://api.vaif.studio',
2613
2616
  apiKey: import.meta.env.VITE_VAIF_API_KEY,
2614
2617
  });
2615
2618
 
@@ -2661,13 +2664,13 @@ export async function deleteTodo(id: string): Promise<void> {
2661
2664
  if (error) throw error;
2662
2665
  }
2663
2666
  `},{path:".env.example",content:`# VAIF Configuration
2664
- # Get these values from https://vaif.studio/app/security/api-keys \u2192 Project Settings \u2192 API Keys
2667
+ # Get these values from https://vaif.studio/app/security/api-keys
2665
2668
 
2666
- VITE_VAIF_PROJECT_ID=your-project-id
2667
- VITE_VAIF_API_KEY=your-anon-key
2669
+ VITE_VAIF_API_URL=https://api.vaif.studio
2670
+ VITE_VAIF_API_KEY=your-api-key
2668
2671
 
2669
- # CLI uses these (non-prefixed) for vaif db push, vaif secrets, etc.
2670
- VAIF_PROJECT_ID=your-project-id
2672
+ # Database connection (for vaif generate, vaif db push)
2673
+ # DATABASE_URL=postgresql://user:password@host:5432/dbname
2671
2674
  `},{path:"README.md",content:`# Todo App \u2014 VAIF Starter
2672
2675
 
2673
2676
  A simple React todo application for learning [VAIF Studio](https://vaif.studio) basics, including typed database queries and CRUD operations.
@@ -2803,15 +2806,8 @@ export const posts = pgTable("posts", {
2803
2806
  `}]},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
2807
 
2805
2808
  export const vaif = createVaifClient({
2806
- projectId: import.meta.env.VITE_VAIF_PROJECT_ID,
2809
+ baseUrl: import.meta.env.VITE_VAIF_API_URL || 'https://api.vaif.studio',
2807
2810
  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
2811
  });
2816
2812
 
2817
2813
  export interface Message {
@@ -2949,13 +2945,13 @@ export function useRealtimeMessages({
2949
2945
  return { messages, isLoading, error, refresh };
2950
2946
  }
2951
2947
  `},{path:".env.example",content:`# VAIF Configuration
2952
- # Get these values from https://vaif.studio/app/security/api-keys \u2192 Project Settings \u2192 API Keys
2948
+ # Get these values from https://vaif.studio/app/security/api-keys
2953
2949
 
2954
- VITE_VAIF_PROJECT_ID=your-project-id
2955
- VITE_VAIF_API_KEY=your-anon-key
2950
+ VITE_VAIF_API_URL=https://api.vaif.studio
2951
+ VITE_VAIF_API_KEY=your-api-key
2956
2952
 
2957
- # CLI uses these (non-prefixed) for vaif db push, vaif secrets, etc.
2958
- VAIF_PROJECT_ID=your-project-id
2953
+ # Database connection (for vaif generate, vaif db push)
2954
+ # DATABASE_URL=postgresql://user:password@host:5432/dbname
2959
2955
  `},{path:"README.md",content:`# Realtime Chat \u2014 VAIF Starter
2960
2956
 
2961
2957
  A React chat application with live messaging powered by [VAIF Studio](https://vaif.studio) realtime subscriptions.
@@ -3106,15 +3102,15 @@ export const posts = pgTable("posts", {
3106
3102
 
3107
3103
  // Browser client \u2013 use in Client Components
3108
3104
  export const vaif = createVaifClient({
3109
- projectId: process.env.NEXT_PUBLIC_VAIF_PROJECT_ID!,
3105
+ baseUrl: process.env.NEXT_PUBLIC_VAIF_API_URL || 'https://api.vaif.studio',
3110
3106
  apiKey: process.env.NEXT_PUBLIC_VAIF_API_KEY!,
3111
3107
  });
3112
3108
 
3113
3109
  // Server client \u2013 use in Server Components, Route Handlers, Server Actions
3114
3110
  export function createVaifServer() {
3115
3111
  return createVaifClient({
3116
- projectId: process.env.NEXT_PUBLIC_VAIF_PROJECT_ID!,
3117
- secretKey: process.env.VAIF_SECRET_KEY!,
3112
+ baseUrl: process.env.NEXT_PUBLIC_VAIF_API_URL || 'https://api.vaif.studio',
3113
+ apiKey: process.env.VAIF_SECRET_KEY!,
3118
3114
  });
3119
3115
  }
3120
3116
  `},{path:"lib/auth.ts",content:`import { createVaifServer } from "./vaif";
@@ -3267,10 +3263,13 @@ export async function requireTeamRole(
3267
3263
  `},{path:".env.example",content:`# VAIF Configuration
3268
3264
  # Get these values from https://vaif.studio/app/security/api-keys \u2192 Project Settings \u2192 API Keys
3269
3265
 
3270
- NEXT_PUBLIC_VAIF_PROJECT_ID=your-project-id
3271
- NEXT_PUBLIC_VAIF_API_KEY=your-anon-key
3266
+ NEXT_PUBLIC_VAIF_API_URL=https://api.vaif.studio
3267
+ NEXT_PUBLIC_VAIF_API_KEY=your-api-key
3272
3268
  VAIF_SECRET_KEY=your-secret-key
3273
3269
 
3270
+ # Database connection (for vaif generate, vaif db push)
3271
+ # DATABASE_URL=postgresql://user:password@host:5432/dbname
3272
+
3274
3273
  # CLI uses these (non-prefixed) for vaif db push, vaif secrets, etc.
3275
3274
  VAIF_PROJECT_ID=your-project-id
3276
3275
  `},{path:"README.md",content:`# SaaS Starter \u2014 VAIF Studio
@@ -3419,15 +3418,15 @@ export const posts = pgTable("posts", {
3419
3418
 
3420
3419
  // Browser client
3421
3420
  export const vaif = createVaifClient({
3422
- projectId: process.env.NEXT_PUBLIC_VAIF_PROJECT_ID!,
3421
+ baseUrl: process.env.NEXT_PUBLIC_VAIF_API_URL || 'https://api.vaif.studio',
3423
3422
  apiKey: process.env.NEXT_PUBLIC_VAIF_API_KEY!,
3424
3423
  });
3425
3424
 
3426
3425
  // Server client
3427
3426
  export function createVaifServer() {
3428
3427
  return createVaifClient({
3429
- projectId: process.env.NEXT_PUBLIC_VAIF_PROJECT_ID!,
3430
- secretKey: process.env.VAIF_SECRET_KEY!,
3428
+ baseUrl: process.env.NEXT_PUBLIC_VAIF_API_URL || 'https://api.vaif.studio',
3429
+ apiKey: process.env.VAIF_SECRET_KEY!,
3431
3430
  });
3432
3431
  }
3433
3432
  `},{path:"lib/storage.ts",content:`import { createVaifServer } from "./vaif";
@@ -3542,10 +3541,13 @@ function getContentType(fileName: string): string {
3542
3541
  `},{path:".env.example",content:`# VAIF Configuration
3543
3542
  # Get these values from https://vaif.studio/app/security/api-keys \u2192 Project Settings \u2192 API Keys
3544
3543
 
3545
- NEXT_PUBLIC_VAIF_PROJECT_ID=your-project-id
3546
- NEXT_PUBLIC_VAIF_API_KEY=your-anon-key
3544
+ NEXT_PUBLIC_VAIF_API_URL=https://api.vaif.studio
3545
+ NEXT_PUBLIC_VAIF_API_KEY=your-api-key
3547
3546
  VAIF_SECRET_KEY=your-secret-key
3548
3547
 
3548
+ # Database connection (for vaif generate, vaif db push)
3549
+ # DATABASE_URL=postgresql://user:password@host:5432/dbname
3550
+
3549
3551
  # CLI uses these (non-prefixed) for vaif db push, vaif secrets, etc.
3550
3552
  VAIF_PROJECT_ID=your-project-id
3551
3553
  `},{path:"README.md",content:`# E-commerce API \u2014 VAIF Studio
@@ -3688,19 +3690,19 @@ export const posts = pgTable("posts", {
3688
3690
 
3689
3691
  return Response.json({ message: \`Hello, \${name}!\` });
3690
3692
  }
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"]}};async function X(n){if(!process.stdin.isTTY||!process.stdout.isTTY)return n;let a=new Set(n.map(e=>b.findIndex(i=>i.name===e)).filter(e=>e>=0)),t=0;return new Promise(e=>{let i=j__default.default.createInterface({input:process.stdin,output:process.stdout});j__default.default.emitKeypressEvents(process.stdin,i),process.stdin.setRawMode&&process.stdin.setRawMode(true);function r(){let u=b.length+2;process.stdout.write(`\x1B[${u}A`),c();}function c(){console.log(d__default.default.bold(`
3692
- ? Which VAIF features do you want to include?`)),b.forEach((u,l)=>{let s=a.has(l)?d__default.default.green("[x]"):"[ ]",o=l===t?d__default.default.cyan("> "):" ";console.log(`${o}${s} ${u.label} ${d__default.default.gray(`(${u.description})`)}`);}),console.log(d__default.default.gray(" (up/down to move, space to toggle, enter to confirm)"));}c(),process.stdin.on("keypress",(u,l)=>{if(l.name==="up"&&t>0)t--,r();else if(l.name==="down"&&t<b.length-1)t++,r();else if(l.name==="space")a.has(t)?a.delete(t):a.add(t),r();else if(l.name==="return"){process.stdin.setRawMode&&process.stdin.setRawMode(false),i.close();let s=[...a].sort().map(o=>b[o].name);e(s.length>0?s:n);}else l.name==="c"&&l.ctrl&&(process.stdin.setRawMode&&process.stdin.setRawMode(false),i.close(),process.exit(0));});})}async function F(n,a={}){let t=W[n];t||(console.log(d__default.default.red(`
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"]}};async function X(n){if(!process.stdin.isTTY||!process.stdout.isTTY)return n;let a=new Set(n.map(e=>y.findIndex(i=>i.name===e)).filter(e=>e>=0)),t=0;return new Promise(e=>{let i=k__default.default.createInterface({input:process.stdin,output:process.stdout});k__default.default.emitKeypressEvents(process.stdin,i),process.stdin.setRawMode&&process.stdin.setRawMode(true);function r(){let u=y.length+2;process.stdout.write(`\x1B[${u}A`),c();}function c(){console.log(d__default.default.bold(`
3694
+ ? Which VAIF features do you want to include?`)),y.forEach((u,l)=>{let s=a.has(l)?d__default.default.green("[x]"):"[ ]",o=l===t?d__default.default.cyan("> "):" ";console.log(`${o}${s} ${u.label} ${d__default.default.gray(`(${u.description})`)}`);}),console.log(d__default.default.gray(" (up/down to move, space to toggle, enter to confirm)"));}c(),process.stdin.on("keypress",(u,l)=>{if(l.name==="up"&&t>0)t--,r();else if(l.name==="down"&&t<y.length-1)t++,r();else if(l.name==="space")a.has(t)?a.delete(t):a.add(t),r();else if(l.name==="return"){process.stdin.setRawMode&&process.stdin.setRawMode(false),i.close();let s=[...a].sort().map(o=>y[o].name);e(s.length>0?s:n);}else l.name==="c"&&l.ctrl&&(process.stdin.setRawMode&&process.stdin.setRawMode(false),i.close(),process.exit(0));});})}async function F(n,a={}){let t=J[n];t||(console.log(d__default.default.red(`
3693
3695
  Unknown template: ${n}`)),console.log(d__default.default.yellow(`Run 'vaif templates' to see available templates.
3694
- `)),process.exit(1));let e;a.features&&a.features.length>0?e=a.features.filter(s=>b.some(o=>o.name===s)):a.addOnly?(console.log(d__default.default.red(`
3695
- No features specified.`)),console.log(d__default.default.yellow("Usage: vaif init --template <name> --add-features <features>")),console.log(d__default.default.gray("Available features: auth, database, realtime, storage, functions")),process.exit(1)):t.featureFiles&&Object.keys(t.featureFiles).length>0?e=await X(t.defaultFeatures??["database","auth"]):e=t.defaultFeatures??[],a.addOnly?(console.log(""),console.log(d__default.default.bold(`Adding features to ${d__default.default.cyan(t.name)} project...`)),console.log(d__default.default.gray(` Features: ${e.join(", ")}`)),console.log("")):(console.log(""),console.log(d__default.default.bold(`Scaffolding ${d__default.default.cyan(t.name)} template...`)),e.length>0&&console.log(d__default.default.gray(` Features: ${e.join(", ")}`)),console.log(""));let i=a.addOnly?[]:[...t.files];if(t.featureFiles)for(let s of e){let o=t.featureFiles[s];o&&i.push(...o);}let r=0,c=0;for(let s of i){let o=w__default.default.resolve(s.path),p=w__default.default.dirname(o);if(v__default.default.existsSync(p)||v__default.default.mkdirSync(p,{recursive:true}),s.path==="package.json"&&v__default.default.existsSync(o)&&!a.force)try{let m=JSON.parse(v__default.default.readFileSync(o,"utf-8")),g=JSON.parse(s.content),y=N=>{if(!N)return {};let L={};for(let[k,_]of Object.entries(N))!_.startsWith("workspace:")&&!_.startsWith("link:")&&!_.startsWith("file:")&&(L[k]=_);return L};m.dependencies={...y(m.dependencies),...g.dependencies||{}},m.devDependencies={...y(m.devDependencies),...g.devDependencies||{}},g.scripts&&(m.scripts={...m.scripts||{},...g.scripts}),v__default.default.writeFileSync(o,JSON.stringify(m,null,2)+`
3696
- `,"utf-8"),console.log(d__default.default.green(` merge ${s.path} (added dependencies)`)),r++;continue}catch{}if(v__default.default.existsSync(o)&&!a.force){console.log(d__default.default.yellow(` skip ${s.path} (already exists)`)),c++;continue}v__default.default.writeFileSync(o,s.content,"utf-8"),console.log(d__default.default.green(` create ${s.path}`)),r++;}console.log(""),r>0&&console.log(d__default.default.green(`Created ${r} file${r!==1?"s":""}.`)),c>0&&console.log(d__default.default.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__default.default.resolve("package.json");if(v__default.default.existsSync(l)&&e.length>0)try{let s=JSON.parse(v__default.default.readFileSync(l,"utf-8")),o=!1;for(let p of e){let m=u[p];if(m)for(let[g,y]of Object.entries(m))s.dependencies?.[g]||(s.dependencies=s.dependencies||{},s.dependencies[g]=y,o=!0);}o&&v__default.default.writeFileSync(l,JSON.stringify(s,null,2)+`
3696
+ `)),process.exit(1));let e;a.features&&a.features.length>0?e=a.features.filter(s=>y.some(o=>o.name===s)):a.addOnly?(console.log(d__default.default.red(`
3697
+ No features specified.`)),console.log(d__default.default.yellow("Usage: vaif init --template <name> --add-features <features>")),console.log(d__default.default.gray("Available features: auth, database, realtime, storage, functions")),process.exit(1)):t.featureFiles&&Object.keys(t.featureFiles).length>0?e=await X(t.defaultFeatures??["database","auth"]):e=t.defaultFeatures??[],a.addOnly?(console.log(""),console.log(d__default.default.bold(`Adding features to ${d__default.default.cyan(t.name)} project...`)),console.log(d__default.default.gray(` Features: ${e.join(", ")}`)),console.log("")):(console.log(""),console.log(d__default.default.bold(`Scaffolding ${d__default.default.cyan(t.name)} template...`)),e.length>0&&console.log(d__default.default.gray(` Features: ${e.join(", ")}`)),console.log(""));let i=a.addOnly?[]:[...t.files];if(t.featureFiles)for(let s of e){let o=t.featureFiles[s];o&&i.push(...o);}let r=0,c=0;for(let s of i){let o=w__default.default.resolve(s.path),p=w__default.default.dirname(o);if(v__default.default.existsSync(p)||v__default.default.mkdirSync(p,{recursive:true}),s.path==="package.json"&&v__default.default.existsSync(o)&&!a.force)try{let m=JSON.parse(v__default.default.readFileSync(o,"utf-8")),g=JSON.parse(s.content),b=U=>{if(!U)return {};let N={};for(let[j,A]of Object.entries(U))!A.startsWith("workspace:")&&!A.startsWith("link:")&&!A.startsWith("file:")&&(N[j]=A);return N};m.dependencies={...b(m.dependencies),...g.dependencies||{}},m.devDependencies={...b(m.devDependencies),...g.devDependencies||{}},g.scripts&&(m.scripts={...m.scripts||{},...g.scripts}),v__default.default.writeFileSync(o,JSON.stringify(m,null,2)+`
3698
+ `,"utf-8"),console.log(d__default.default.green(` merge ${s.path} (added dependencies)`)),r++;continue}catch{}if(v__default.default.existsSync(o)&&!a.force){console.log(d__default.default.yellow(` skip ${s.path} (already exists)`)),c++;continue}v__default.default.writeFileSync(o,s.content,"utf-8"),console.log(d__default.default.green(` create ${s.path}`)),r++;}console.log(""),r>0&&console.log(d__default.default.green(`Created ${r} file${r!==1?"s":""}.`)),c>0&&console.log(d__default.default.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__default.default.resolve("package.json");if(v__default.default.existsSync(l)&&e.length>0)try{let s=JSON.parse(v__default.default.readFileSync(l,"utf-8")),o=!1;for(let p of e){let m=u[p];if(m)for(let[g,b]of Object.entries(m))s.dependencies?.[g]||(s.dependencies=s.dependencies||{},s.dependencies[g]=b,o=!0);}o&&v__default.default.writeFileSync(l,JSON.stringify(s,null,2)+`
3697
3699
  `,"utf-8");}catch{}(t.dependencies?.length||t.devDependencies?.length)&&(console.log(""),console.log(d__default.default.bold("Install dependencies:")),t.dependencies?.length&&console.log(d__default.default.cyan(` npm install ${t.dependencies.join(" ")}`)),t.devDependencies?.length&&console.log(d__default.default.cyan(` npm install -D ${t.devDependencies.join(" ")}`))),console.log(""),console.log(d__default.default.bold.green("Project scaffolded successfully!")),console.log(""),console.log(d__default.default.bold(" Next steps:")),t.postInstructions.forEach(s=>{console.log(d__default.default.gray(` ${s}`));}),console.log(""),console.log(d__default.default.gray(" Get your project credentials at https://vaif.studio/app/security/api-keys")),console.log("");}var Z={$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 ee(n){if(n.addFeatures){n.template||(console.log(d__default.default.red(`
3698
3700
  --add-features requires --template to know which template to use.`)),console.log(d__default.default.gray("Example: vaif init --template react-spa --add-features functions,storage")),process.exit(1));let e=n.addFeatures.split(",").map(i=>i.trim());await F(n.template,{force:n.force,features:e,addOnly:true});return}let a=z__default.default("Initializing VAIF configuration...").start(),t=w__default.default.resolve("vaif.config.json");v__default.default.existsSync(t)&&!n.force&&(a.fail("vaif.config.json already exists"),console.log(d__default.default.yellow(`
3699
3701
  Use --force to overwrite existing configuration.`)),process.exit(1));try{if(v__default.default.writeFileSync(t,JSON.stringify(Z,null,2),"utf-8"),a.succeed("Created vaif.config.json"),n.template){let e=n.features?n.features.split(",").map(i=>i.trim()):void 0;await F(n.template,{force:n.force,features:e});}else {let e=w__default.default.resolve(".env.example");if(v__default.default.existsSync(e)||(v__default.default.writeFileSync(e,`# VAIF Configuration
3700
3702
  DATABASE_URL=postgresql://user:password@localhost:5432/database
3701
3703
  VAIF_API_KEY=your-api-key
3702
3704
  `,"utf-8"),console.log(d__default.default.gray("Created .env.example"))),n.typescript){let i=w__default.default.resolve("src/types");v__default.default.existsSync(i)||(v__default.default.mkdirSync(i,{recursive:!0}),console.log(d__default.default.gray("Created src/types directory")));}console.log(""),console.log(d__default.default.green("VAIF initialized successfully!")),console.log(""),console.log(d__default.default.gray("Next steps:")),console.log(d__default.default.gray(" 1. Update vaif.config.json with your project ID")),console.log(d__default.default.gray(" 2. Set DATABASE_URL in your environment")),console.log(d__default.default.gray(" 3. Run: npx vaif generate")),console.log("");}}catch(e){a.fail("Failed to initialize"),e instanceof Error&&console.error(d__default.default.red(`
3703
- Error: ${e.message}`)),process.exit(1);}}async function Ve(n){let{connectionString:a,schema:t="public"}=n,e=new K__default.default.Client({connectionString:a});await e.connect();try{let i=await e.query(`
3705
+ Error: ${e.message}`)),process.exit(1);}}async function Le(n){let{connectionString:a,schema:t="public"}=n,e=new K__default.default.Client({connectionString:a});await e.connect();try{let i=await e.query(`
3704
3706
  SELECT table_name, table_type
3705
3707
  FROM information_schema.tables
3706
3708
  WHERE table_schema = $1
@@ -3732,7 +3734,7 @@ Error: ${e.message}`)),process.exit(1);}}async function Ve(n){let{connectionStri
3732
3734
  ORDER BY t.typname, e.enumsortorder
3733
3735
  `,[t]),u=new Map;for(let o of i.rows)u.set(o.table_name,[]);for(let o of r.rows){let p=u.get(o.table_name);p&&p.push(o);}let l=new Map;for(let o of c.rows){let p=l.get(o.enum_name)||[];p.push(o.enum_value),l.set(o.enum_name,p);}let s=ie(u,l);return q__default.default.format(s,{parser:"typescript",semi:!0,singleQuote:!1,trailingComma:"es5",printWidth:100})}finally{await e.end();}}var P={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",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",point:"{ x: number; y: number }",ARRAY:"unknown[]"};function ne(n,a){let{data_type:t,udt_name:e,is_nullable:i}=n;if(a.has(e)){let u=a.get(e).map(l=>`"${l}"`).join(" | ");return i==="YES"?`(${u}) | null`:u}if(t==="ARRAY"){let c=e.replace(/^_/,"");if(a.has(c)){let s=a.get(c).map(o=>`"${o}"`).join(" | ");return i==="YES"?`(${s})[] | null`:`(${s})[]`}let u=P[c]||"unknown";return i==="YES"?`${u}[] | null`:`${u}[]`}let r=P[t]||P[e]||"unknown";return i==="YES"&&(r=`${r} | null`),r}function C(n){return n.split(/[_\-\s]+/).map(a=>a.charAt(0).toUpperCase()+a.slice(1).toLowerCase()).join("")}function ie(n,a){let t=["/**"," * 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(a.size>0){t.push("// ============ ENUMS ============"),t.push("");for(let[i,r]of a){let c=C(i),u=r.map(l=>` | "${l}"`).join(`
3734
3736
  `);t.push(`export type ${c} =
3735
- ${u};`),t.push("");}}t.push("// ============ TABLES ============"),t.push("");let e=[];for(let[i,r]of n){e.push(i);let c=C(i),u=[],l=[],s=[];for(let o of r){let p=ne(o,a),m=o.column_name,g=o.column_default!==null||o.is_identity==="YES",y=o.is_nullable==="YES";u.push(` ${m}: ${p};`),g||o.column_name==="id"?l.push(` ${m}?: ${p.replace(" | null","")} | null;`):y?l.push(` ${m}?: ${p};`):l.push(` ${m}: ${p.replace(" | null","")};`),s.push(` ${m}?: ${p.replace(" | null","")} | null;`);}t.push(`export interface ${c} {
3737
+ ${u};`),t.push("");}}t.push("// ============ TABLES ============"),t.push("");let e=[];for(let[i,r]of n){e.push(i);let c=C(i),u=[],l=[],s=[];for(let o of r){let p=ne(o,a),m=o.column_name,g=o.column_default!==null||o.is_identity==="YES",b=o.is_nullable==="YES";u.push(` ${m}: ${p};`),g||o.column_name==="id"?l.push(` ${m}?: ${p.replace(" | null","")} | null;`):b?l.push(` ${m}?: ${p};`):l.push(` ${m}: ${p.replace(" | null","")};`),s.push(` ${m}?: ${p.replace(" | null","")} | null;`);}t.push(`export interface ${c} {
3736
3738
  ${u.join(`
3737
3739
  `)}
3738
3740
  }`),t.push(""),t.push(`export interface ${c}Insert {
@@ -3742,4 +3744,4 @@ ${l.join(`
3742
3744
  ${s.join(`
3743
3745
  `)}
3744
3746
  }`),t.push("");}t.push("// ============ DATABASE SCHEMA ============"),t.push(""),t.push("export interface Database {");for(let i of e){let r=C(i);t.push(` ${i}: {`),t.push(` Row: ${r};`),t.push(` Insert: ${r}Insert;`),t.push(` Update: ${r}Update;`),t.push(" };");}return t.push("}"),t.push(""),t.push("export type TableName = keyof Database;"),t.push(""),t.push("// ============ HELPER TYPES ============"),t.push(""),t.push('export type Row<T extends TableName> = Database[T]["Row"];'),t.push('export type Insert<T extends TableName> = Database[T]["Insert"];'),t.push('export type Update<T extends TableName> = Database[T]["Update"];'),t.push(""),t.join(`
3745
- `)}exports.generateTypes=J;exports.generateTypesFromConnection=Ve;exports.initConfig=ee;exports.loadConfig=E;
3747
+ `)}exports.generateTypes=W;exports.generateTypesFromConnection=Le;exports.initConfig=ee;exports.loadConfig=E;
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- export{b as generateTypes,d as initConfig,a as loadConfig}from'./chunk-OB2QEQ2L.js';import T from'pg';import $ from'prettier';async function S(p){let{connectionString:a,schema:e="public"}=p,r=new T.Client({connectionString:a});await r.connect();try{let t=await r.query(`
1
+ export{b as generateTypes,d as initConfig,a as loadConfig}from'./chunk-N7OY2COL.js';import T from'pg';import $ from'prettier';async function S(p){let{connectionString:a,schema:e="public"}=p,r=new T.Client({connectionString:a});await r.connect();try{let t=await r.query(`
2
2
  SELECT table_name, table_type
3
3
  FROM information_schema.tables
4
4
  WHERE table_schema = $1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vaiftech/cli",
3
- "version": "1.6.6",
3
+ "version": "1.7.1",
4
4
  "description": "VAIF CLI - Type generation and development tools",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",