@vaiftech/cli 1.5.1 → 1.6.0

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`):
@@ -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 T={smallint:"number",integer:"number",bigint:"string",int2:"number",int4:"number",int8:"string",decimal:"string",numeric:"string",real:"number",float4:"number",float8:"number","double precision":"number",money:"string",boolean:"boolean",bool:"boolean",text:"string",varchar:"string",char:"string",character:"string","character varying":"string",name:"string",citext:"string",date:"string",time:"string",timetz:"string","time without time zone":"string","time with time zone":"string",timestamp:"string",timestamptz:"string","timestamp without time zone":"string","timestamp with time zone":"string",interval:"string",bytea:"Buffer",uuid:"string",json:"unknown",jsonb:"unknown",inet:"string",cidr:"string",macaddr:"string",macaddr8:"string",point:"{ x: number; y: number }",line:"string",lseg:"string",box:"string",path:"string",polygon:"string",circle:"string",ARRAY:"unknown[]"};function Y(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=T[c]||"unknown";return o==="YES"?`${d}[] | null`:`${d}[]`}let s=T[a]||T[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",
@@ -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:`{
@@ -1036,6 +1041,11 @@ export const posts = pgTable("posts", {
1036
1041
  });
1037
1042
  `}],functions:[{path:"functions/hello.ts",content:`export default async function handler(req: Request): Promise<Response> {
1038
1043
  const { name } = await req.json().catch(() => ({ name: "World" }));
1044
+
1045
+ // Secrets set via \\\`vaif secrets set\\\` or the Security > Secrets page
1046
+ // are available as environment variables at runtime:
1047
+ // const apiKey = process.env.MY_API_KEY;
1048
+
1039
1049
  return Response.json({ message: \`Hello, \${name}!\` });
1040
1050
  }
1041
1051
  `}]},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
@@ -1625,6 +1635,11 @@ export const posts = pgTable("posts", {
1625
1635
  });
1626
1636
  `}],functions:[{path:"functions/hello.ts",content:`export default async function handler(req: Request): Promise<Response> {
1627
1637
  const { name } = await req.json().catch(() => ({ name: "World" }));
1638
+
1639
+ // Secrets set via \\\`vaif secrets set\\\` or the Security > Secrets page
1640
+ // are available as environment variables at runtime:
1641
+ // const apiKey = process.env.MY_API_KEY;
1642
+
1628
1643
  return Response.json({ message: \`Hello, \${name}!\` });
1629
1644
  }
1630
1645
  `}]},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 +2251,7 @@ async def invoke_function(function_name: str, payload: dict = {}):
2236
2251
  `},{path:"functions/hello.py",content:`"""Example VAIF serverless function."""
2237
2252
 
2238
2253
  import json
2254
+ import os
2239
2255
 
2240
2256
 
2241
2257
  def handler(request):
@@ -2243,6 +2259,10 @@ def handler(request):
2243
2259
 
2244
2260
  Deploy with: vaif functions deploy
2245
2261
  """
2262
+ # Secrets set via \\\`vaif secrets set\\\` or the Security > Secrets page
2263
+ # are available as environment variables at runtime:
2264
+ # api_key = os.environ.get("MY_API_KEY")
2265
+
2246
2266
  try:
2247
2267
  body = json.loads(request.body) if request.body else {}
2248
2268
  name = body.get("name", "World")
@@ -2557,6 +2577,11 @@ type HelloResponse struct {
2557
2577
  }
2558
2578
 
2559
2579
  // HelloHandler is an example VAIF serverless function.
2580
+ //
2581
+ // Secrets set via \\\`vaif secrets set\\\` or the Security > Secrets page
2582
+ // are available as environment variables at runtime:
2583
+ //
2584
+ // apiKey := os.Getenv("MY_API_KEY") // import "os"
2560
2585
  func HelloHandler(w http.ResponseWriter, r *http.Request) {
2561
2586
  var req HelloRequest
2562
2587
  if err := json.NewDecoder(r.Body).Decode(&req); err != nil || req.Name == "" {
@@ -2752,6 +2777,11 @@ export const posts = pgTable("posts", {
2752
2777
  });
2753
2778
  `}],functions:[{path:"functions/hello.ts",content:`export default async function handler(req: Request): Promise<Response> {
2754
2779
  const { name } = await req.json().catch(() => ({ name: "World" }));
2780
+
2781
+ // Secrets set via \\\`vaif secrets set\\\` or the Security > Secrets page
2782
+ // are available as environment variables at runtime:
2783
+ // const apiKey = process.env.MY_API_KEY;
2784
+
2755
2785
  return Response.json({ message: \`Hello, \${name}!\` });
2756
2786
  }
2757
2787
  `}]},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";
@@ -3046,6 +3076,11 @@ export const posts = pgTable("posts", {
3046
3076
  });
3047
3077
  `}],functions:[{path:"functions/hello.ts",content:`export default async function handler(req: Request): Promise<Response> {
3048
3078
  const { name } = await req.json().catch(() => ({ name: "World" }));
3079
+
3080
+ // Secrets set via \\\`vaif secrets set\\\` or the Security > Secrets page
3081
+ // are available as environment variables at runtime:
3082
+ // const apiKey = process.env.MY_API_KEY;
3083
+
3049
3084
  return Response.json({ message: \`Hello, \${name}!\` });
3050
3085
  }
3051
3086
  `}]},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";
@@ -3352,6 +3387,11 @@ export const posts = pgTable("posts", {
3352
3387
  });
3353
3388
  `}],functions:[{path:"functions/hello.ts",content:`export default async function handler(req: Request): Promise<Response> {
3354
3389
  const { name } = await req.json().catch(() => ({ name: "World" }));
3390
+
3391
+ // Secrets set via \\\`vaif secrets set\\\` or the Security > Secrets page
3392
+ // are available as environment variables at runtime:
3393
+ // const apiKey = process.env.MY_API_KEY;
3394
+
3355
3395
  return Response.json({ message: \`Hello, \${name}!\` });
3356
3396
  }
3357
3397
  `}]},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";
@@ -3618,16 +3658,21 @@ export const posts = pgTable("posts", {
3618
3658
  });
3619
3659
  `}],functions:[{path:"functions/hello.ts",content:`export default async function handler(req: Request): Promise<Response> {
3620
3660
  const { name } = await req.json().catch(() => ({ name: "World" }));
3661
+
3662
+ // Secrets set via \\\`vaif secrets set\\\` or the Security > Secrets page
3663
+ // are available as environment variables at runtime:
3664
+ // const apiKey = process.env.MY_API_KEY;
3665
+
3621
3666
  return Response.json({ message: \`Hello, \${name}!\` });
3622
3667
  }
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(`
3668
+ `}]},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=V.createInterface({input:process.stdin,output:process.stdout});V.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(`
3669
+ ? 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(`
3625
3670
  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)+`
3628
- `,"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
3671
+ `)),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[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)+`
3672
+ `,"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)+`
3673
+ `,"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 Te(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(`
3674
+ 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
3630
3675
  DATABASE_URL=postgresql://user:password@localhost:5432/database
3631
3676
  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};
3677
+ `,"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(`
3678
+ Error: ${e.message}`)),process.exit(1);}}export{U as a,ue as b,ve as c,Te as d};