@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 +27 -0
- package/dist/{chunk-F5OP4YDB.js → chunk-UQZPP43V.js} +62 -17
- package/dist/cli.cjs +105 -55
- package/dist/cli.js +41 -36
- package/dist/index.cjs +45 -0
- package/dist/index.js +1 -1
- package/package.json +1 -1
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]),
|
|
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]),
|
|
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
|
|
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),
|
|
50
|
-
${
|
|
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
|
-
${
|
|
53
|
+
${s.join(`
|
|
54
54
|
`)}
|
|
55
55
|
}`,i=`export interface ${e}Update {
|
|
56
56
|
${c.join(`
|
|
57
57
|
`)}
|
|
58
|
-
}`;return {base:
|
|
59
|
-
`)}async function
|
|
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
|
|
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(
|
|
3624
|
-
? Which VAIF features do you want to include?`)),b.forEach((
|
|
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(
|
|
3627
|
-
`,"utf-8"),console.log(r.green(` merge ${i.path} (added dependencies)`)),
|
|
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
|
|
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(
|
|
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
|
|
3633
|
-
Error: ${e.message}`)),process.exit(1);}}export{U as a,
|
|
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};
|