@vaiftech/cli 1.5.1 → 1.6.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/README.md CHANGED
@@ -218,6 +218,33 @@ vaif keys generate --name "Prod" # Named key
218
218
  vaif keys list # List all keys
219
219
  ```
220
220
 
221
+ ### Secrets
222
+
223
+ Manage encrypted secrets that your VAIF Functions can access at runtime. Secret values are encrypted at rest and injected into function invocations automatically.
224
+
225
+ ```bash
226
+ vaif secrets set API_KEY sk-live-xxx # Create or update a secret
227
+ vaif secrets set CERT --from-file cert.pem # Set secret from file
228
+ vaif secrets list # List secret names (values hidden)
229
+ vaif secrets get API_KEY # Reveal a secret value
230
+ vaif secrets delete API_KEY # Delete a secret
231
+ vaif sec ls # Alias
232
+ ```
233
+
234
+ All secrets commands accept `--project-id` and `--env-id` flags to target a specific project and environment.
235
+
236
+ #### How Functions Access Secrets
237
+
238
+ Secrets are automatically injected into your function's execution context. Access them via `process.env` or the `secrets` parameter depending on your runtime:
239
+
240
+ ```typescript
241
+ // functions/hello.ts
242
+ export default async function handler(req, ctx) {
243
+ const apiKey = ctx.secrets.API_KEY;
244
+ // Use the secret...
245
+ }
246
+ ```
247
+
221
248
  ## Configuration
222
249
 
223
250
  `vaif.config.json` (created by `vaif init`):
@@ -241,11 +268,15 @@ vaif keys list # List all keys
241
268
 
242
269
  ## Environment Variables
243
270
 
271
+ The CLI automatically loads `.env` files from the current directory. You can set these variables in your `.env` or export them in your shell:
272
+
244
273
  | Variable | Description |
245
274
  |----------|-------------|
246
275
  | `VAIF_API_URL` | API base URL (default: `https://api.vaif.studio`) |
247
276
  | `VAIF_TOKEN` | API token (alternative to `vaif login`) |
248
- | `VAIF_PROJECT_ID` | Default project ID |
277
+ | `VAIF_PROJECT_ID` | Default project ID (also read from `vaif.config.json`) |
278
+
279
+ Project ID resolution order: `--project-id` flag > `vaif.config.json` > `VAIF_PROJECT_ID` env var > login session.
249
280
 
250
281
  ## Related Packages
251
282
 
@@ -1,4 +1,4 @@
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 V from'readline';M.config();async function U(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 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 L from'readline';M.config();async function U(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(`
2
2
  SELECT table_name, table_type
3
3
  FROM information_schema.tables
4
4
  WHERE table_schema = $1
@@ -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]),s=await n.query(`
22
+ `,[t]),o=await n.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]),o=await n.query(`
38
+ `,[t]),s=await n.query(`
39
39
  SELECT
40
40
  t.typname as enum_name,
41
41
  e.enumlabel as enum_value
@@ -44,20 +44,20 @@ 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 u=new Map;for(let l of o.rows){let i=u.get(l.enum_name)||[];i.push(l.enum_value),u.set(l.enum_name,i);}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(n,t){let{data_type:a,udt_name:e,is_nullable:s}=n;if(t.has(e)){let u=t.get(e).map(l=>`"${l}"`).join(" | ");return s==="YES"?`(${u}) | null`:u}if(a==="ARRAY"){let c=e.replace(/^_/,"");if(t.has(c)){let i=t.get(c).map(d=>`"${d}"`).join(" | ");return s==="YES"?`(${i})[] | null`:`(${i})[]`}let u=S[c]||"unknown";return s==="YES"?`${u}[] | null`:`${u}[]`}let o=S[a]||S[e]||"unknown";return s==="YES"&&(o=`${o} | null`),o}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(s=>` | "${s}"`).join(`
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
48
  `);return `export type ${a} =
49
- ${e};`}function G(n,t,a){let e=x(n),s=[],o=[],c=[];for(let d of t){let f=Y(d,a),p=d.column_name,h=d.column_default!==null||d.is_identity==="YES",y=d.is_nullable==="YES";s.push(` ${p}: ${f};`),h||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(`
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(`
51
51
  `)}
52
52
  }`,l=`export interface ${e}Insert {
53
- ${o.join(`
53
+ ${s.join(`
54
54
  `)}
55
55
  }`,i=`export interface ${e}Update {
56
56
  ${c.join(`
57
57
  `)}
58
- }`;return {base:u,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[o,c]of t)e.push($(o,c)),e.push("");}e.push("// ============ TABLES ============"),e.push("");let s=[];for(let[o,c]of n){let{base:u,insert:l,update:i}=G(o,c,t);s.push(o),e.push(u),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 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(n){let t=z("Loading configuration...").start();try{let a=await U(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 s=new K.Client({connectionString:e});await s.connect(),t.text="Introspecting schema...";let{tables:o,enums:c,foreignKeys:u}=await B(s,n.schema);if(await s.end(),o.size===0){t.warn(`No tables found in schema "${n.schema}"`);return}t.text=`Generating types for ${o.size} tables...`;let l=H(o,c,u),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 d=w.resolve(n.output),f=w.dirname(d);g.existsSync(f)||g.mkdirSync(f,{recursive:!0}),g.writeFileSync(d,i,"utf-8"),t.succeed(`Generated types for ${o.size} tables \u2192 ${r.cyan(n.output)}`),console.log(""),console.log(r.green("Generated:")),console.log(r.gray(` Tables: ${o.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(`
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 U(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
61
  Error: ${a.message}`)),a.message.includes("ECONNREFUSED")&&console.log(r.yellow(`
62
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:`{
63
63
  "name": "my-vaif-app",
@@ -81,6 +81,7 @@ Make sure your database is running and accessible.`))),process.exit(1);}}var b=[
81
81
  "@types/node": "^22.0.0",
82
82
  "@types/react": "^19.0.0",
83
83
  "@types/react-dom": "^19.0.0",
84
+ "drizzle-kit": "^0.30.0",
84
85
  "typescript": "^5.7.0"
85
86
  }
86
87
  }
@@ -150,18 +151,17 @@ body {
150
151
  -webkit-font-smoothing: antialiased;
151
152
  -moz-osx-font-smoothing: grayscale;
152
153
  }
153
- `},{path:"lib/vaif.ts",content:`import { createClient } from "@vaiftech/client";
154
- import { createServerClient } from "@vaiftech/client/server";
154
+ `},{path:"lib/vaif.ts",content:`import { createVaifClient } from "@vaiftech/client";
155
155
 
156
156
  // Browser client \u2013 safe to use in Client Components
157
- export const vaif = createClient({
157
+ export const vaif = createVaifClient({
158
158
  projectId: process.env.NEXT_PUBLIC_VAIF_PROJECT_ID!,
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
- return createServerClient({
164
+ return createVaifClient({
165
165
  projectId: process.env.NEXT_PUBLIC_VAIF_PROJECT_ID!,
166
166
  secretKey: process.env.VAIF_SECRET_KEY!,
167
167
  });
@@ -274,7 +274,7 @@ A full-stack Next.js application powered by [VAIF Studio](https://vaif.studio),
274
274
 
275
275
  Full documentation is available at <https://docs.vaif.studio>.
276
276
  `}],featureFiles:{auth:[{path:"middleware.ts",content:`import { NextResponse, type NextRequest } from "next/server";
277
- import { createServerClient } from "@vaiftech/client/server";
277
+ import { createVaifClient } from "@vaiftech/client";
278
278
  import { authMiddleware } from "@vaiftech/auth/nextjs";
279
279
 
280
280
  const protectedRoutes = ["/dashboard", "/settings", "/api/protected"];
@@ -284,7 +284,7 @@ export async function middleware(request: NextRequest) {
284
284
  const isProtected = protectedRoutes.some((route) => pathname.startsWith(route));
285
285
  if (!isProtected) return NextResponse.next();
286
286
 
287
- const vaif = createServerClient({
287
+ const vaif = createVaifClient({
288
288
  projectId: process.env.NEXT_PUBLIC_VAIF_PROJECT_ID!,
289
289
  secretKey: process.env.VAIF_SECRET_KEY!,
290
290
  });
@@ -527,6 +527,11 @@ export const posts = pgTable("posts", {
527
527
  });
528
528
  `}],functions:[{path:"functions/hello.ts",content:`export default async function handler(req: Request): Promise<Response> {
529
529
  const { name } = await req.json().catch(() => ({ name: "World" }));
530
+
531
+ // Secrets set via \\\`vaif secrets set\\\` or the Security > Secrets page
532
+ // are available as environment variables at runtime:
533
+ // const apiKey = process.env.MY_API_KEY;
534
+
530
535
  return Response.json({ message: \`Hello, \${name}!\` });
531
536
  }
532
537
  `}]},dependencies:["@vaiftech/client","@vaiftech/auth","@vaiftech/react","next","react","react-dom"],devDependencies:["@types/node","@types/react","@types/react-dom","typescript"],postInstructions:["cd my-vaif-app","npm install","# Copy .env.local.example to .env.local and add your VAIF credentials","npm run dev"]},"react-spa":{name:"React SPA",description:"Single-page React app with Vite, VAIF client, and provider wrapper",tag:"React + Vite",defaultFeatures:["database","auth"],files:[{path:"package.json",content:`{
@@ -547,9 +552,11 @@ export const posts = pgTable("posts", {
547
552
  "react-router-dom": "^7.0.0"
548
553
  },
549
554
  "devDependencies": {
555
+ "@types/node": "^22.0.0",
550
556
  "@types/react": "^19.0.0",
551
557
  "@types/react-dom": "^19.0.0",
552
558
  "@vitejs/plugin-react": "^4.4.0",
559
+ "drizzle-kit": "^0.30.0",
553
560
  "typescript": "^5.7.0",
554
561
  "vite": "^6.0.0"
555
562
  }
@@ -637,9 +644,9 @@ function Home() {
637
644
  </div>
638
645
  );
639
646
  }
640
- `},{path:"src/lib/vaif.ts",content:`import { createClient } from "@vaiftech/client";
647
+ `},{path:"src/lib/vaif.ts",content:`import { createVaifClient } from "@vaiftech/client";
641
648
 
642
- export const vaif = createClient({
649
+ export const vaif = createVaifClient({
643
650
  projectId: import.meta.env.VITE_VAIF_PROJECT_ID,
644
651
  apiKey: import.meta.env.VITE_VAIF_API_KEY,
645
652
  });
@@ -1036,6 +1043,11 @@ export const posts = pgTable("posts", {
1036
1043
  });
1037
1044
  `}],functions:[{path:"functions/hello.ts",content:`export default async function handler(req: Request): Promise<Response> {
1038
1045
  const { name } = await req.json().catch(() => ({ name: "World" }));
1046
+
1047
+ // Secrets set via \\\`vaif secrets set\\\` or the Security > Secrets page
1048
+ // are available as environment variables at runtime:
1049
+ // const apiKey = process.env.MY_API_KEY;
1050
+
1039
1051
  return Response.json({ message: \`Hello, \${name}!\` });
1040
1052
  }
1041
1053
  `}]},dependencies:["@vaiftech/client","@vaiftech/react","react","react-dom","react-router-dom"],devDependencies:["@types/react","@types/react-dom","@vitejs/plugin-react","typescript","vite"],postInstructions:["cd my-vaif-app","npm install","# Copy .env.example to .env and add your VAIF credentials","npm run dev"]},"ios-swift-app":{name:"iOS Swift App",description:"Swift client manager for iOS/macOS apps using Swift Package Manager",tag:"Swift / iOS",defaultFeatures:["database","auth"],files:[{path:"VaifManager.swift",content:`import Foundation
@@ -1259,7 +1271,9 @@ Full documentation is available at <https://docs.vaif.studio>.
1259
1271
  "react-native": "~0.76.0"
1260
1272
  },
1261
1273
  "devDependencies": {
1274
+ "@types/node": "^22.0.0",
1262
1275
  "@types/react": "^19.0.0",
1276
+ "drizzle-kit": "^0.30.0",
1263
1277
  "typescript": "^5.7.0"
1264
1278
  }
1265
1279
  }
@@ -1625,6 +1639,11 @@ export const posts = pgTable("posts", {
1625
1639
  });
1626
1640
  `}],functions:[{path:"functions/hello.ts",content:`export default async function handler(req: Request): Promise<Response> {
1627
1641
  const { name } = await req.json().catch(() => ({ name: "World" }));
1642
+
1643
+ // Secrets set via \\\`vaif secrets set\\\` or the Security > Secrets page
1644
+ // are available as environment variables at runtime:
1645
+ // const apiKey = process.env.MY_API_KEY;
1646
+
1628
1647
  return Response.json({ message: \`Hello, \${name}!\` });
1629
1648
  }
1630
1649
  `}]},dependencies:["@vaiftech/sdk-expo","@react-native-async-storage/async-storage","expo","expo-router","react","react-native"],postInstructions:["cd my-vaif-app","npm install","# Copy .env.example to .env and add your VAIF credentials","npx expo start"]},"flutter-app":{name:"Flutter App",description:"Dart/Flutter client setup with environment configuration",tag:"Flutter / Dart",defaultFeatures:["database","auth"],files:[{path:"lib/main.dart",content:`import 'package:flutter/material.dart';
@@ -2236,6 +2255,7 @@ async def invoke_function(function_name: str, payload: dict = {}):
2236
2255
  `},{path:"functions/hello.py",content:`"""Example VAIF serverless function."""
2237
2256
 
2238
2257
  import json
2258
+ import os
2239
2259
 
2240
2260
 
2241
2261
  def handler(request):
@@ -2243,6 +2263,10 @@ def handler(request):
2243
2263
 
2244
2264
  Deploy with: vaif functions deploy
2245
2265
  """
2266
+ # Secrets set via \\\`vaif secrets set\\\` or the Security > Secrets page
2267
+ # are available as environment variables at runtime:
2268
+ # api_key = os.environ.get("MY_API_KEY")
2269
+
2246
2270
  try:
2247
2271
  body = json.loads(request.body) if request.body else {}
2248
2272
  name = body.get("name", "World")
@@ -2557,6 +2581,11 @@ type HelloResponse struct {
2557
2581
  }
2558
2582
 
2559
2583
  // HelloHandler is an example VAIF serverless function.
2584
+ //
2585
+ // Secrets set via \\\`vaif secrets set\\\` or the Security > Secrets page
2586
+ // are available as environment variables at runtime:
2587
+ //
2588
+ // apiKey := os.Getenv("MY_API_KEY") // import "os"
2560
2589
  func HelloHandler(w http.ResponseWriter, r *http.Request) {
2561
2590
  var req HelloRequest
2562
2591
  if err := json.NewDecoder(r.Body).Decode(&req); err != nil || req.Name == "" {
@@ -2568,9 +2597,9 @@ func HelloHandler(w http.ResponseWriter, r *http.Request) {
2568
2597
  Message: fmt.Sprintf("Hello, %s!", req.Name),
2569
2598
  })
2570
2599
  }
2571
- `}]},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 { createClient } from "@vaiftech/client";
2600
+ `}]},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";
2572
2601
 
2573
- export const vaif = createClient({
2602
+ export const vaif = createVaifClient({
2574
2603
  projectId: import.meta.env.VITE_VAIF_PROJECT_ID,
2575
2604
  apiKey: import.meta.env.VITE_VAIF_API_KEY,
2576
2605
  });
@@ -2752,11 +2781,16 @@ export const posts = pgTable("posts", {
2752
2781
  });
2753
2782
  `}],functions:[{path:"functions/hello.ts",content:`export default async function handler(req: Request): Promise<Response> {
2754
2783
  const { name } = await req.json().catch(() => ({ name: "World" }));
2784
+
2785
+ // Secrets set via \\\`vaif secrets set\\\` or the Security > Secrets page
2786
+ // are available as environment variables at runtime:
2787
+ // const apiKey = process.env.MY_API_KEY;
2788
+
2755
2789
  return Response.json({ message: \`Hello, \${name}!\` });
2756
2790
  }
2757
- `}]},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 { createClient } from "@vaiftech/client";
2791
+ `}]},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";
2758
2792
 
2759
- export const vaif = createClient({
2793
+ export const vaif = createVaifClient({
2760
2794
  projectId: import.meta.env.VITE_VAIF_PROJECT_ID,
2761
2795
  apiKey: import.meta.env.VITE_VAIF_API_KEY,
2762
2796
  realtime: {
@@ -3046,20 +3080,24 @@ export const posts = pgTable("posts", {
3046
3080
  });
3047
3081
  `}],functions:[{path:"functions/hello.ts",content:`export default async function handler(req: Request): Promise<Response> {
3048
3082
  const { name } = await req.json().catch(() => ({ name: "World" }));
3083
+
3084
+ // Secrets set via \\\`vaif secrets set\\\` or the Security > Secrets page
3085
+ // are available as environment variables at runtime:
3086
+ // const apiKey = process.env.MY_API_KEY;
3087
+
3049
3088
  return Response.json({ message: \`Hello, \${name}!\` });
3050
3089
  }
3051
- `}]},dependencies:["@vaiftech/client","@vaiftech/react"],postInstructions:["Copy .env.example to .env and fill in your project credentials","Create a 'messages' table with columns: id (uuid), content (text), user_id (text), username (text), channel_id (text), created_at (timestamptz)","Enable Realtime on the messages table in your VAIF dashboard","Use the useRealtimeMessages hook in your components","Run: npx vaif generate to generate TypeScript types"]},"saas-starter":{name:"SaaS Starter",description:"Full SaaS starter with VAIF auth, team/org support, and server-side helpers",tag:"Next.js SaaS",defaultFeatures:["database","auth","functions"],files:[{path:"lib/vaif.ts",content:`import { createClient } from "@vaiftech/client";
3052
- import { createServerClient } from "@vaiftech/client/server";
3090
+ `}]},dependencies:["@vaiftech/client","@vaiftech/react"],postInstructions:["Copy .env.example to .env and fill in your project credentials","Create a 'messages' table with columns: id (uuid), content (text), user_id (text), username (text), channel_id (text), created_at (timestamptz)","Enable Realtime on the messages table in your VAIF dashboard","Use the useRealtimeMessages hook in your components","Run: npx vaif generate to generate TypeScript types"]},"saas-starter":{name:"SaaS Starter",description:"Full SaaS starter with VAIF auth, team/org support, and server-side helpers",tag:"Next.js SaaS",defaultFeatures:["database","auth","functions"],files:[{path:"lib/vaif.ts",content:`import { createVaifClient } from "@vaiftech/client";
3053
3091
 
3054
3092
  // Browser client \u2013 use in Client Components
3055
- export const vaif = createClient({
3093
+ export const vaif = createVaifClient({
3056
3094
  projectId: process.env.NEXT_PUBLIC_VAIF_PROJECT_ID!,
3057
3095
  apiKey: process.env.NEXT_PUBLIC_VAIF_API_KEY!,
3058
3096
  });
3059
3097
 
3060
3098
  // Server client \u2013 use in Server Components, Route Handlers, Server Actions
3061
3099
  export function createVaifServer() {
3062
- return createServerClient({
3100
+ return createVaifClient({
3063
3101
  projectId: process.env.NEXT_PUBLIC_VAIF_PROJECT_ID!,
3064
3102
  secretKey: process.env.VAIF_SECRET_KEY!,
3065
3103
  });
@@ -3352,20 +3390,24 @@ export const posts = pgTable("posts", {
3352
3390
  });
3353
3391
  `}],functions:[{path:"functions/hello.ts",content:`export default async function handler(req: Request): Promise<Response> {
3354
3392
  const { name } = await req.json().catch(() => ({ name: "World" }));
3393
+
3394
+ // Secrets set via \\\`vaif secrets set\\\` or the Security > Secrets page
3395
+ // are available as environment variables at runtime:
3396
+ // const apiKey = process.env.MY_API_KEY;
3397
+
3355
3398
  return Response.json({ message: \`Hello, \${name}!\` });
3356
3399
  }
3357
- `}]},dependencies:["@vaiftech/client","@vaiftech/auth","@vaiftech/react"],postInstructions:["Copy .env.example to .env.local and fill in your project credentials","Create 'teams' and 'team_members' tables in your VAIF dashboard","Import auth helpers from '@/lib/auth' in your Server Components/Actions","Use requireUser() for authenticated routes and requireTeamRole() for role checks","Run: npx vaif generate to generate TypeScript types"]},"ecommerce-api":{name:"E-commerce API",description:"API-first e-commerce setup with VAIF storage for product images",tag:"Next.js E-commerce",defaultFeatures:["database","auth","storage"],files:[{path:"lib/vaif.ts",content:`import { createClient } from "@vaiftech/client";
3358
- import { createServerClient } from "@vaiftech/client/server";
3400
+ `}]},dependencies:["@vaiftech/client","@vaiftech/auth","@vaiftech/react"],postInstructions:["Copy .env.example to .env.local and fill in your project credentials","Create 'teams' and 'team_members' tables in your VAIF dashboard","Import auth helpers from '@/lib/auth' in your Server Components/Actions","Use requireUser() for authenticated routes and requireTeamRole() for role checks","Run: npx vaif generate to generate TypeScript types"]},"ecommerce-api":{name:"E-commerce API",description:"API-first e-commerce setup with VAIF storage for product images",tag:"Next.js E-commerce",defaultFeatures:["database","auth","storage"],files:[{path:"lib/vaif.ts",content:`import { createVaifClient } from "@vaiftech/client";
3359
3401
 
3360
3402
  // Browser client
3361
- export const vaif = createClient({
3403
+ export const vaif = createVaifClient({
3362
3404
  projectId: process.env.NEXT_PUBLIC_VAIF_PROJECT_ID!,
3363
3405
  apiKey: process.env.NEXT_PUBLIC_VAIF_API_KEY!,
3364
3406
  });
3365
3407
 
3366
3408
  // Server client
3367
3409
  export function createVaifServer() {
3368
- return createServerClient({
3410
+ return createVaifClient({
3369
3411
  projectId: process.env.NEXT_PUBLIC_VAIF_PROJECT_ID!,
3370
3412
  secretKey: process.env.VAIF_SECRET_KEY!,
3371
3413
  });
@@ -3618,16 +3660,21 @@ export const posts = pgTable("posts", {
3618
3660
  });
3619
3661
  `}],functions:[{path:"functions/hello.ts",content:`export default async function handler(req: Request): Promise<Response> {
3620
3662
  const { name } = await req.json().catch(() => ({ name: "World" }));
3663
+
3664
+ // Secrets set via \\\`vaif secrets set\\\` or the Security > Secrets page
3665
+ // are available as environment variables at runtime:
3666
+ // const apiKey = process.env.MY_API_KEY;
3667
+
3621
3668
  return Response.json({ message: \`Hello, \${name}!\` });
3622
3669
  }
3623
- `}]},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 W(n){if(!process.stdin.isTTY||!process.stdout.isTTY)return n;let t=new Set(n.map(e=>b.findIndex(s=>s.name===e)).filter(e=>e>=0)),a=0;return new Promise(e=>{let s=V.createInterface({input:process.stdin,output:process.stdout});V.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(r.bold(`
3624
- ? Which VAIF features do you want to include?`)),b.forEach((u,l)=>{let i=t.has(l)?r.green("[x]"):"[ ]",d=l===a?r.cyan("> "):" ";console.log(`${d}${i} ${u.label} ${r.gray(`(${u.description})`)}`);}),console.log(r.gray(" (up/down to move, space to toggle, enter to confirm)"));}c(),process.stdin.on("keypress",(u,l)=>{if(l.name==="up"&&a>0)a--,o();else if(l.name==="down"&&a<b.length-1)a++,o();else if(l.name==="space")t.has(a)?t.delete(a):t.add(a),o();else if(l.name==="return"){process.stdin.setRawMode&&process.stdin.setRawMode(false),s.close();let i=[...t].sort().map(d=>b[d].name);e(i.length>0?i:n);}else l.name==="c"&&l.ctrl&&(process.stdin.setRawMode&&process.stdin.setRawMode(false),s.close(),process.exit(0));});})}async function j(n,t={}){let a=D[n];a||(console.log(r.red(`
3670
+ `}]},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 W(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=L.createInterface({input:process.stdin,output:process.stdout});L.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(`
3671
+ ? 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 k(n,t={}){let a=D[n];a||(console.log(r.red(`
3625
3672
  Unknown template: ${n}`)),console.log(r.yellow(`Run 'vaif templates' to see available templates.
3626
- `)),process.exit(1));let e;t.features&&t.features.length>0?e=t.features.filter(i=>b.some(d=>d.name===i)):a.featureFiles&&Object.keys(a.featureFiles).length>0?e=await W(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 s=[...a.files];if(a.featureFiles)for(let i of e){let d=a.featureFiles[i];d&&s.push(...d);}let o=0,c=0;for(let i of s){let d=w.resolve(i.path),f=w.dirname(d);if(g.existsSync(f)||g.mkdirSync(f,{recursive:true}),i.path==="package.json"&&g.existsSync(d)&&!t.force)try{let p=JSON.parse(g.readFileSync(d,"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(d,JSON.stringify(p,null,2)+`
3627
- `,"utf-8"),console.log(r.green(` merge ${i.path} (added dependencies)`)),o++;continue}catch{}if(g.existsSync(d)&&!t.force){console.log(r.yellow(` skip ${i.path} (already exists)`)),c++;continue}g.writeFileSync(d,i.content,"utf-8"),console.log(r.green(` create ${i.path}`)),o++;}console.log(""),o>0&&console.log(r.green(`Created ${o} file${o!==1?"s":""}.`)),c>0&&console.log(r.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(g.existsSync(l)&&e.length>0)try{let i=JSON.parse(g.readFileSync(l,"utf-8")),d=!1;for(let f of e){let p=u[f];if(p)for(let[h,y]of Object.entries(p))i.dependencies?.[h]||(i.dependencies=i.dependencies||{},i.dependencies[h]=y,d=!0);}d&&g.writeFileSync(l,JSON.stringify(i,null,2)+`
3673
+ `)),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 W(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[j,A]of Object.entries(P))!A.startsWith("workspace:")&&!A.startsWith("link:")&&!A.startsWith("file:")&&(R[j]=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)+`
3674
+ `,"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)+`
3628
3675
  `,"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(`
3629
- 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(s=>s.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
3676
+ 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 k(n.template,{force:n.force,features:e});}else {let e=w.resolve(".env.example");if(g.existsSync(e)||(g.writeFileSync(e,`# VAIF Configuration
3630
3677
  DATABASE_URL=postgresql://user:password@localhost:5432/database
3631
3678
  VAIF_API_KEY=your-api-key
3632
- `,"utf-8"),console.log(r.gray("Created .env.example"))),n.typescript){let s=w.resolve("src/types");g.existsSync(s)||(g.mkdirSync(s,{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(`
3633
- Error: ${e.message}`)),process.exit(1);}}export{U as a,de as b,ve as c,Se as d};
3679
+ `,"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(`
3680
+ Error: ${e.message}`)),process.exit(1);}}export{U as a,ue as b,ve as c,Se as d};