@vaiftech/cli 1.7.0 → 1.7.2
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 +1 -1
- package/dist/{chunk-OB2QEQ2L.js → chunk-KXD5F33V.js} +80 -67
- package/dist/cli.cjs +101 -97
- package/dist/cli.js +28 -37
- package/dist/index.cjs +86 -82
- package/dist/index.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/@vaiftech/cli)
|
|
4
4
|
[](https://opensource.org/licenses/MIT)
|
|
5
5
|
|
|
6
|
-
Command-line tools for [VAIF Studio](https://vaif.studio) (v1.7.
|
|
6
|
+
Command-line tools for [VAIF Studio](https://vaif.studio) (v1.7.2) — scaffold full projects from templates with feature selection, browser-based authentication, manage schemas, deploy functions, generate TypeScript types, and more.
|
|
7
7
|
|
|
8
8
|
## Installation
|
|
9
9
|
|
|
@@ -1,4 +1,13 @@
|
|
|
1
|
-
import
|
|
1
|
+
import y from'fs';import N from'path';import J from'dotenv';import X from'os';import {exec}from'child_process';import P from'ora';import c from'chalk';import q from'readline';import se from'pg';import ce from'prettier';J.config();async function K(a){let t=N.resolve(a);if(!y.existsSync(t))return null;try{let n=y.readFileSync(t,"utf-8"),e=JSON.parse(n);return e.database?.url&&(e.database.url=M(e.database.url)),e.api?.apiKey&&(e.api.apiKey=M(e.api.apiKey)),e}catch{throw new Error(`Failed to parse config file: ${a}`)}}function M(a){return a.replace(/\$\{([^}]+)\}/g,(t,n)=>process.env[n]||t)}var F=N.join(X.homedir(),".vaif"),I=N.join(F,"auth.json"),w=process.env.VAIF_API_URL||"https://api.vaif.studio";function Z(){y.existsSync(F)||y.mkdirSync(F,{recursive:true});}function B(a){Z(),y.writeFileSync(I,JSON.stringify(a,null,2),"utf-8"),y.chmodSync(I,384);}function R(){if(!y.existsSync(I))return null;try{let a=y.readFileSync(I,"utf-8");return JSON.parse(a)}catch{return null}}function ee(a){let t=q.createInterface({input:process.stdin,output:process.stdout});return new Promise(n=>{t.question(a,e=>{t.close(),n(e);});})}function te(a){return new Promise(t=>{let n=q.createInterface({input:process.stdin,output:process.stdout});process.stdin;let r=process.stdout.write.bind(process.stdout),o=false;process.stdout.write=((...l)=>o?true:r(...l)),n.question(a,l=>{o=false,process.stdout.write=r,console.log(""),n.close(),t(l);}),o=true;})}function ae(a){let t=process.platform,n;t==="darwin"?n=`open "${a}"`:t==="win32"?n=`start "" "${a}"`:n=`xdg-open "${a}"`,exec(n,e=>{});}function ne(a){return new Promise(t=>setTimeout(t,a))}async function ie(a){try{let t=await fetch(`${w}/auth/me`,{headers:{Authorization:`Bearer ${a}`}});if(t.ok){let n=await t.json();return {valid:!0,email:n.user?.email||n.email}}return {valid:!1}}catch{return {valid:false}}}async function oe(a){let t=P();t.start("Setting up authentication...");let n,e;try{let u=await fetch(`${w}/auth/cli/authorize`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({})});u.ok||(t.fail("Failed to initiate authentication"),console.log(c.red(`
|
|
2
|
+
Could not connect to VAIF API. Please try again later.`)),process.exit(1));let s=await u.json();n=s.code,e=s.url;}catch{t.fail("Failed to connect to VAIF API"),console.log(c.red(`
|
|
3
|
+
Could not connect to VAIF API.`)),console.log(c.gray("Check your internet connection or try: vaif login --email")),process.exit(1);}t.stop(),console.log(c.cyan(" Opening browser for authentication...")),console.log(""),console.log(c.gray(" If the browser doesn't open, visit this URL:")),console.log(c.white(` ${e}`)),console.log(""),ae(e),t.start("Waiting for browser authentication...");let r=12e4,o=2e3,l=Date.now();for(;Date.now()-l<r;){await ne(o);try{let u=await fetch(`${w}/auth/cli/token`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({code:n})});if(!u.ok){let i=await u.json();(i.error==="ExpiredCode"||i.error==="InvalidCode")&&(t.fail("Authentication expired"),console.log(c.red(`
|
|
4
|
+
The authentication session expired. Please try again.`)),process.exit(1));continue}let s=await u.json();if(s.ok&&s.accessToken){let i={token:s.accessToken,email:s.user?.email,projectId:a,expiresAt:new Date(Date.now()+s.expiresIn*1e3).toISOString()};B(i),t.succeed("Logged in successfully"),console.log(""),s.user?.email&&console.log(c.green(` Authenticated as: ${s.user.email}`)),console.log(c.gray(` Config saved to: ${I}`)),console.log("");return}}catch{}}t.fail("Authentication timed out"),console.log(c.red(`
|
|
5
|
+
Timed out waiting for browser authentication.`)),console.log(c.gray("Try again or use: vaif login --email")),process.exit(1);}async function re(a){console.log(""),console.log(c.bold("VAIF CLI Login")),console.log(c.gray("Enter your VAIF account credentials")),console.log("");let t=await ee(c.cyan(" Email: "));(!t||t.trim()==="")&&(console.log(c.red(`
|
|
6
|
+
No email provided. Login cancelled.`)),process.exit(1));let n=await te(c.cyan(" Password: "));(!n||n.trim()==="")&&(console.log(c.red(`
|
|
7
|
+
No password provided. Login cancelled.`)),process.exit(1));let e=P("Authenticating...").start();try{let r=await fetch(`${w}/auth/cli/login`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({email:t.trim(),password:n})}),o=await r.json();(!r.ok||!o.ok)&&(e.fail("Login failed"),console.log(c.red(`
|
|
8
|
+
${o.message||"Invalid email or password."}`)),process.exit(1));let l={token:o.accessToken,email:o.user?.email,projectId:a,expiresAt:new Date(Date.now()+o.expiresIn*1e3).toISOString()};B(l),e.succeed("Logged in successfully"),console.log(""),o.user?.email&&console.log(c.green(` Authenticated as: ${o.user.email}`)),console.log(c.gray(` Config saved to: ${I}`)),console.log("");}catch{e.fail("Failed to connect to VAIF API"),console.log(c.red(`
|
|
9
|
+
Could not connect to VAIF API. Please try again later.`)),process.exit(1);}}async function Le(a){console.log(""),console.log(c.bold("Welcome to VAIF CLI")),console.log(c.gray("Authenticate to access your VAIF projects")),console.log(""),a.email?await re(a.projectId):await oe(a.projectId),console.log(c.gray("You can now use VAIF CLI commands like:")),console.log(c.gray(" vaif pull - Pull remote schema")),console.log(c.gray(" vaif push - Push schema changes")),console.log(c.gray(" vaif generate - Generate TypeScript types")),console.log("");}async function Ue(){y.existsSync(I)?(y.unlinkSync(I),console.log(c.green("Logged out successfully"))):console.log(c.yellow("Not currently logged in"));}async function Ne(){let a=R();(!a||!a.token)&&(console.log(c.yellow("Not logged in")),console.log(c.gray("Run `vaif login` to authenticate")),process.exit(1));let t=P("Checking authentication...").start(),{valid:n,email:e}=await ie(a.token);n||(t.fail("Session expired"),console.log(c.yellow(`
|
|
10
|
+
Your session has expired. Please login again.`)),process.exit(1)),t.succeed("Authenticated"),console.log(""),console.log(c.green(` Email: ${e||a.email||"Unknown"}`)),a.projectId&&console.log(c.green(` Project: ${a.projectId}`)),console.log("");}var ue=process.env.VAIF_API_URL||"https://api.vaif.studio";async function de(a,t){let n=await fetch(`${ue}/schema-engine/introspect/${t}`,{headers:{Authorization:`Bearer ${a}`,"Content-Type":"application/json"}});if(!n.ok){let u=await n.text();throw new Error(`API introspection failed: ${u}`)}let e=await n.json();if(!e.ok||!e.schemaExists)throw new Error("Project schema does not exist yet. Push a migration first with `vaif db push`.");let r=new Map,o=[];for(let u of e.tables){let s=u.columns.map(i=>({column_name:i.name,data_type:i.type,is_nullable:i.nullable?"YES":"NO",column_default:i.default,udt_name:i.type,is_identity:i.primaryKey&&i.default?.includes("gen_random_uuid")?"YES":"NO",character_maximum_length:null,numeric_precision:null,numeric_scale:null}));r.set(u.name,s);for(let i of u.foreignKeys)o.push({constraint_name:i.constraintName,table_name:u.name,column_name:i.columnName,foreign_table_name:i.refTable,foreign_column_name:i.refColumn});}return {tables:r,enums:new Map,foreignKeys:o}}async function pe(a,t){let n=await a.query(`
|
|
2
11
|
SELECT table_name, table_type
|
|
3
12
|
FROM information_schema.tables
|
|
4
13
|
WHERE table_schema = $1
|
|
@@ -19,7 +28,7 @@ import h from'fs';import w from'path';import M from'dotenv';import K from'pg';im
|
|
|
19
28
|
FROM information_schema.columns
|
|
20
29
|
WHERE table_schema = $1
|
|
21
30
|
ORDER BY table_name, ordinal_position
|
|
22
|
-
`,[t]),
|
|
31
|
+
`,[t]),r=await a.query(`
|
|
23
32
|
SELECT
|
|
24
33
|
tc.constraint_name,
|
|
25
34
|
tc.table_name,
|
|
@@ -44,22 +53,24 @@ import h from'fs';import w from'path';import M from'dotenv';import K from'pg';im
|
|
|
44
53
|
JOIN pg_namespace n ON n.oid = t.typnamespace
|
|
45
54
|
WHERE n.nspname = $1
|
|
46
55
|
ORDER BY t.typname, e.enumsortorder
|
|
47
|
-
`,[t]),
|
|
56
|
+
`,[t]),l=new Map;for(let s of n.rows)l.set(s.table_name,[]);for(let s of e.rows){let i=l.get(s.table_name);i&&i.push(s);}let u=new Map;for(let s of o.rows){let i=u.get(s.enum_name)||[];i.push(s.enum_value),u.set(s.enum_name,i);}return {tables:l,enums:u,foreignKeys:r.rows}}var L={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 me(a,t){let{data_type:n,udt_name:e,is_nullable:r}=a;if(t.has(e)){let u=t.get(e).map(s=>`"${s}"`).join(" | ");return r==="YES"?`(${u}) | null`:u}if(n==="ARRAY"){let l=e.replace(/^_/,"");if(t.has(l)){let i=t.get(l).map(p=>`"${p}"`).join(" | ");return r==="YES"?`(${i})[] | null`:`(${i})[]`}let u=L[l]||"unknown";return r==="YES"?`${u}[] | null`:`${u}[]`}let o=L[n]||L[e]||"unknown";return r==="YES"&&(o=`${o} | null`),o}function U(a){return a.split(/[_\-\s]+/).map(t=>t.charAt(0).toUpperCase()+t.slice(1).toLowerCase()).join("")}function fe(a,t){let n=U(a),e=t.map(r=>` | "${r}"`).join(`
|
|
48
57
|
`);return `export type ${n} =
|
|
49
|
-
${e};`}function
|
|
50
|
-
${
|
|
58
|
+
${e};`}function ge(a,t,n){let e=U(a),r=[],o=[],l=[];for(let p of t){let g=me(p,n),m=p.column_name,h=p.column_default!==null||p.is_identity==="YES",A=p.is_nullable==="YES";r.push(` ${m}: ${g};`),h||p.column_name==="id"?o.push(` ${m}?: ${g.replace(" | null","")} | null;`):A?o.push(` ${m}?: ${g};`):o.push(` ${m}: ${g.replace(" | null","")};`),l.push(` ${m}?: ${g.replace(" | null","")} | null;`);}let u=`export interface ${e} {
|
|
59
|
+
${r.join(`
|
|
51
60
|
`)}
|
|
52
|
-
}`,
|
|
61
|
+
}`,s=`export interface ${e}Insert {
|
|
53
62
|
${o.join(`
|
|
54
63
|
`)}
|
|
55
|
-
}`,
|
|
56
|
-
${
|
|
64
|
+
}`,i=`export interface ${e}Update {
|
|
65
|
+
${l.join(`
|
|
57
66
|
`)}
|
|
58
|
-
}`;return {base:u,insert:
|
|
59
|
-
`)}async function
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
67
|
+
}`;return {base:u,insert:s,update:i}}function he(a,t,n){let e=["/**"," * Auto-generated TypeScript types from database schema"," * Generated by @vaiftech/cli",` * Generated at: ${new Date().toISOString()}`," * "," * DO NOT EDIT MANUALLY - changes will be overwritten"," */",""];if(t.size>0){e.push("// ============ ENUMS ============"),e.push("");for(let[o,l]of t)e.push(fe(o,l)),e.push("");}e.push("// ============ TABLES ============"),e.push("");let r=[];for(let[o,l]of a){let{base:u,insert:s,update:i}=ge(o,l,t);r.push(o),e.push(u),e.push(""),e.push(s),e.push(""),e.push(i),e.push("");}e.push("// ============ DATABASE SCHEMA ============"),e.push(""),e.push("export interface Database {");for(let o of r){let l=U(o);e.push(` ${o}: {`),e.push(` Row: ${l};`),e.push(` Insert: ${l}Insert;`),e.push(` Update: ${l}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(`
|
|
68
|
+
`)}async function Ye(a){let t=P("Loading configuration...").start();try{let n=await K(a.config),e=a.connection||n?.database?.url||process.env.DATABASE_URL,r=e&&!e.includes("${"),o,l,u;if(r){t.text="Connecting to database...";let m=new se.Client({connectionString:e});await m.connect(),t.text="Introspecting schema...",{tables:o,enums:l,foreignKeys:u}=await pe(m,a.schema),await m.end();}else {let m=R();(!m||!m.token)&&(t.fail("No database connection and not logged in"),console.log(c.yellow(`
|
|
69
|
+
Either:`)),console.log(c.gray(" 1. Run `vaif login` to authenticate (no DATABASE_URL needed)")),console.log(c.gray(" 2. Set DATABASE_URL in your .env file")),console.log(c.gray(" 3. Pass --connection postgresql://user:pass@host:5432/db")),process.exit(1));let h=n?.projectId||process.env.VAIF_PROJECT_ID||m.projectId;h||(t.fail("No project ID specified"),console.log(c.yellow(`
|
|
70
|
+
Set projectId in vaif.config.json or use VAIF_PROJECT_ID env var.`)),process.exit(1)),t.text="Introspecting schema via API...",{tables:o,enums:l,foreignKeys:u}=await de(m.token,h);}if(o.size===0){t.warn("No tables found"),console.log(c.yellow(`
|
|
71
|
+
Push a migration first: vaif db push`));return}t.text=`Generating types for ${o.size} tables...`;let s=he(o,l,u),i=await ce.format(s,{parser:"typescript",semi:!0,singleQuote:!1,trailingComma:"es5",printWidth:100});if(a.dryRun){t.succeed("Generated types (dry run):"),console.log(""),console.log(c.gray("\u2500".repeat(60))),console.log(i),console.log(c.gray("\u2500".repeat(60)));return}let p=N.resolve(a.output),g=N.dirname(p);y.existsSync(g)||y.mkdirSync(g,{recursive:!0}),y.writeFileSync(p,i,"utf-8"),t.succeed(`Generated types for ${o.size} tables \u2192 ${c.cyan(a.output)}`),console.log(""),console.log(c.green("Generated:")),console.log(c.gray(` Tables: ${o.size}`)),console.log(c.gray(` Enums: ${l.size}`)),console.log(""),console.log(c.gray("Import in your code:")),console.log(c.cyan(` import type { Database, Row, Insert, Update } from "${a.output.replace(/\.ts$/,"")}";`));}catch(n){t.fail("Failed to generate types"),n instanceof Error&&(console.error(c.red(`
|
|
72
|
+
Error: ${n.message}`)),n.message.includes("ECONNREFUSED")&&console.log(c.yellow(`
|
|
73
|
+
Make sure your database is running and accessible.`))),process.exit(1);}}var _=[{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"}],G={"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
74
|
"name": "my-vaif-app",
|
|
64
75
|
"private": true,
|
|
65
76
|
"version": "0.1.0",
|
|
@@ -155,24 +166,27 @@ body {
|
|
|
155
166
|
|
|
156
167
|
// Browser client \u2013 safe to use in Client Components
|
|
157
168
|
export const vaif = createVaifClient({
|
|
158
|
-
|
|
169
|
+
baseUrl: process.env.NEXT_PUBLIC_VAIF_API_URL || 'https://api.vaif.studio',
|
|
159
170
|
apiKey: process.env.NEXT_PUBLIC_VAIF_API_KEY!,
|
|
160
171
|
});
|
|
161
172
|
|
|
162
173
|
// Server client \u2013 use in Server Components, Route Handlers, Server Actions
|
|
163
174
|
export function createVaifServer() {
|
|
164
175
|
return createVaifClient({
|
|
165
|
-
|
|
166
|
-
|
|
176
|
+
baseUrl: process.env.NEXT_PUBLIC_VAIF_API_URL || 'https://api.vaif.studio',
|
|
177
|
+
apiKey: process.env.VAIF_SECRET_KEY!,
|
|
167
178
|
});
|
|
168
179
|
}
|
|
169
180
|
`},{path:".env.local.example",content:`# VAIF Configuration
|
|
170
181
|
# Get these values from https://vaif.studio/app/security/api-keys \u2192 Project Settings \u2192 API Keys
|
|
171
182
|
|
|
172
|
-
|
|
173
|
-
NEXT_PUBLIC_VAIF_API_KEY=your-
|
|
183
|
+
NEXT_PUBLIC_VAIF_API_URL=https://api.vaif.studio
|
|
184
|
+
NEXT_PUBLIC_VAIF_API_KEY=your-api-key
|
|
174
185
|
VAIF_SECRET_KEY=your-secret-key
|
|
175
186
|
|
|
187
|
+
# Database connection (for vaif generate, vaif db push)
|
|
188
|
+
# DATABASE_URL=postgresql://user:password@host:5432/dbname
|
|
189
|
+
|
|
176
190
|
# CLI uses these (non-prefixed) for vaif db push, vaif secrets, etc.
|
|
177
191
|
VAIF_PROJECT_ID=your-project-id
|
|
178
192
|
`},{path:".gitignore",content:`node_modules
|
|
@@ -288,8 +302,8 @@ export async function middleware(request: NextRequest) {
|
|
|
288
302
|
if (!isProtected) return NextResponse.next();
|
|
289
303
|
|
|
290
304
|
const vaif = createVaifClient({
|
|
291
|
-
|
|
292
|
-
|
|
305
|
+
baseUrl: process.env.NEXT_PUBLIC_VAIF_API_URL || 'https://api.vaif.studio',
|
|
306
|
+
apiKey: process.env.VAIF_SECRET_KEY!,
|
|
293
307
|
});
|
|
294
308
|
|
|
295
309
|
return authMiddleware(vaif, request, {
|
|
@@ -650,13 +664,13 @@ function Home() {
|
|
|
650
664
|
`},{path:"src/lib/vaif.ts",content:`import { createVaifClient } from "@vaiftech/client";
|
|
651
665
|
|
|
652
666
|
export const vaif = createVaifClient({
|
|
653
|
-
|
|
667
|
+
baseUrl: import.meta.env.VITE_VAIF_API_URL || 'https://api.vaif.studio',
|
|
654
668
|
apiKey: import.meta.env.VITE_VAIF_API_KEY,
|
|
655
669
|
});
|
|
656
670
|
`},{path:"src/vite-env.d.ts",content:`/// <reference types="vite/client" />
|
|
657
671
|
|
|
658
672
|
interface ImportMetaEnv {
|
|
659
|
-
readonly
|
|
673
|
+
readonly VITE_VAIF_API_URL: string;
|
|
660
674
|
readonly VITE_VAIF_API_KEY: string;
|
|
661
675
|
}
|
|
662
676
|
|
|
@@ -664,13 +678,13 @@ interface ImportMeta {
|
|
|
664
678
|
readonly env: ImportMetaEnv;
|
|
665
679
|
}
|
|
666
680
|
`},{path:".env.example",content:`# VAIF Configuration
|
|
667
|
-
# Get these values from https://vaif.studio/app/security/api-keys
|
|
681
|
+
# Get these values from https://vaif.studio/app/security/api-keys
|
|
668
682
|
|
|
669
|
-
|
|
670
|
-
VITE_VAIF_API_KEY=your-
|
|
683
|
+
VITE_VAIF_API_URL=https://api.vaif.studio
|
|
684
|
+
VITE_VAIF_API_KEY=your-api-key
|
|
671
685
|
|
|
672
|
-
#
|
|
673
|
-
|
|
686
|
+
# Database connection (for vaif generate, vaif db push)
|
|
687
|
+
# DATABASE_URL=postgresql://user:password@host:5432/dbname
|
|
674
688
|
`},{path:".gitignore",content:`node_modules
|
|
675
689
|
dist
|
|
676
690
|
.env
|
|
@@ -2609,7 +2623,7 @@ func HelloHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
2609
2623
|
`}]},postInstructions:["go mod tidy","# Copy .env.example to .env and add your VAIF credentials","go run main.go"]},"todo-app":{name:"Todo App",description:"Simple React todo app \u2013 great for learning VAIF basics",tag:"React Starter",defaultFeatures:["database"],files:[{path:"src/lib/vaif.ts",content:`import { createVaifClient } from "@vaiftech/client";
|
|
2610
2624
|
|
|
2611
2625
|
export const vaif = createVaifClient({
|
|
2612
|
-
|
|
2626
|
+
baseUrl: import.meta.env.VITE_VAIF_API_URL || 'https://api.vaif.studio',
|
|
2613
2627
|
apiKey: import.meta.env.VITE_VAIF_API_KEY,
|
|
2614
2628
|
});
|
|
2615
2629
|
|
|
@@ -2661,13 +2675,13 @@ export async function deleteTodo(id: string): Promise<void> {
|
|
|
2661
2675
|
if (error) throw error;
|
|
2662
2676
|
}
|
|
2663
2677
|
`},{path:".env.example",content:`# VAIF Configuration
|
|
2664
|
-
# Get these values from https://vaif.studio/app/security/api-keys
|
|
2678
|
+
# Get these values from https://vaif.studio/app/security/api-keys
|
|
2665
2679
|
|
|
2666
|
-
|
|
2667
|
-
VITE_VAIF_API_KEY=your-
|
|
2680
|
+
VITE_VAIF_API_URL=https://api.vaif.studio
|
|
2681
|
+
VITE_VAIF_API_KEY=your-api-key
|
|
2668
2682
|
|
|
2669
|
-
#
|
|
2670
|
-
|
|
2683
|
+
# Database connection (for vaif generate, vaif db push)
|
|
2684
|
+
# DATABASE_URL=postgresql://user:password@host:5432/dbname
|
|
2671
2685
|
`},{path:"README.md",content:`# Todo App \u2014 VAIF Starter
|
|
2672
2686
|
|
|
2673
2687
|
A simple React todo application for learning [VAIF Studio](https://vaif.studio) basics, including typed database queries and CRUD operations.
|
|
@@ -2803,15 +2817,8 @@ export const posts = pgTable("posts", {
|
|
|
2803
2817
|
`}]},dependencies:["@vaiftech/client","@vaiftech/react"],postInstructions:["Copy .env.example to .env and fill in your project credentials","Create a 'todos' table in your VAIF dashboard with columns: id (uuid), title (text), done (boolean), created_at (timestamptz)","Import helpers from './lib/vaif' in your components","Run: npx vaif generate to generate TypeScript types"]},"realtime-chat":{name:"Realtime Chat",description:"React chat app with VAIF realtime subscriptions for live messaging",tag:"React + Realtime",defaultFeatures:["database","realtime","auth"],files:[{path:"src/lib/vaif.ts",content:`import { createVaifClient } from "@vaiftech/client";
|
|
2804
2818
|
|
|
2805
2819
|
export const vaif = createVaifClient({
|
|
2806
|
-
|
|
2820
|
+
baseUrl: import.meta.env.VITE_VAIF_API_URL || 'https://api.vaif.studio',
|
|
2807
2821
|
apiKey: import.meta.env.VITE_VAIF_API_KEY,
|
|
2808
|
-
realtime: {
|
|
2809
|
-
enabled: true,
|
|
2810
|
-
// Automatically reconnect on connection loss
|
|
2811
|
-
reconnect: true,
|
|
2812
|
-
reconnectInterval: 1000,
|
|
2813
|
-
maxReconnectAttempts: 10,
|
|
2814
|
-
},
|
|
2815
2822
|
});
|
|
2816
2823
|
|
|
2817
2824
|
export interface Message {
|
|
@@ -2949,13 +2956,13 @@ export function useRealtimeMessages({
|
|
|
2949
2956
|
return { messages, isLoading, error, refresh };
|
|
2950
2957
|
}
|
|
2951
2958
|
`},{path:".env.example",content:`# VAIF Configuration
|
|
2952
|
-
# Get these values from https://vaif.studio/app/security/api-keys
|
|
2959
|
+
# Get these values from https://vaif.studio/app/security/api-keys
|
|
2953
2960
|
|
|
2954
|
-
|
|
2955
|
-
VITE_VAIF_API_KEY=your-
|
|
2961
|
+
VITE_VAIF_API_URL=https://api.vaif.studio
|
|
2962
|
+
VITE_VAIF_API_KEY=your-api-key
|
|
2956
2963
|
|
|
2957
|
-
#
|
|
2958
|
-
|
|
2964
|
+
# Database connection (for vaif generate, vaif db push)
|
|
2965
|
+
# DATABASE_URL=postgresql://user:password@host:5432/dbname
|
|
2959
2966
|
`},{path:"README.md",content:`# Realtime Chat \u2014 VAIF Starter
|
|
2960
2967
|
|
|
2961
2968
|
A React chat application with live messaging powered by [VAIF Studio](https://vaif.studio) realtime subscriptions.
|
|
@@ -3106,15 +3113,15 @@ export const posts = pgTable("posts", {
|
|
|
3106
3113
|
|
|
3107
3114
|
// Browser client \u2013 use in Client Components
|
|
3108
3115
|
export const vaif = createVaifClient({
|
|
3109
|
-
|
|
3116
|
+
baseUrl: process.env.NEXT_PUBLIC_VAIF_API_URL || 'https://api.vaif.studio',
|
|
3110
3117
|
apiKey: process.env.NEXT_PUBLIC_VAIF_API_KEY!,
|
|
3111
3118
|
});
|
|
3112
3119
|
|
|
3113
3120
|
// Server client \u2013 use in Server Components, Route Handlers, Server Actions
|
|
3114
3121
|
export function createVaifServer() {
|
|
3115
3122
|
return createVaifClient({
|
|
3116
|
-
|
|
3117
|
-
|
|
3123
|
+
baseUrl: process.env.NEXT_PUBLIC_VAIF_API_URL || 'https://api.vaif.studio',
|
|
3124
|
+
apiKey: process.env.VAIF_SECRET_KEY!,
|
|
3118
3125
|
});
|
|
3119
3126
|
}
|
|
3120
3127
|
`},{path:"lib/auth.ts",content:`import { createVaifServer } from "./vaif";
|
|
@@ -3267,10 +3274,13 @@ export async function requireTeamRole(
|
|
|
3267
3274
|
`},{path:".env.example",content:`# VAIF Configuration
|
|
3268
3275
|
# Get these values from https://vaif.studio/app/security/api-keys \u2192 Project Settings \u2192 API Keys
|
|
3269
3276
|
|
|
3270
|
-
|
|
3271
|
-
NEXT_PUBLIC_VAIF_API_KEY=your-
|
|
3277
|
+
NEXT_PUBLIC_VAIF_API_URL=https://api.vaif.studio
|
|
3278
|
+
NEXT_PUBLIC_VAIF_API_KEY=your-api-key
|
|
3272
3279
|
VAIF_SECRET_KEY=your-secret-key
|
|
3273
3280
|
|
|
3281
|
+
# Database connection (for vaif generate, vaif db push)
|
|
3282
|
+
# DATABASE_URL=postgresql://user:password@host:5432/dbname
|
|
3283
|
+
|
|
3274
3284
|
# CLI uses these (non-prefixed) for vaif db push, vaif secrets, etc.
|
|
3275
3285
|
VAIF_PROJECT_ID=your-project-id
|
|
3276
3286
|
`},{path:"README.md",content:`# SaaS Starter \u2014 VAIF Studio
|
|
@@ -3419,15 +3429,15 @@ export const posts = pgTable("posts", {
|
|
|
3419
3429
|
|
|
3420
3430
|
// Browser client
|
|
3421
3431
|
export const vaif = createVaifClient({
|
|
3422
|
-
|
|
3432
|
+
baseUrl: process.env.NEXT_PUBLIC_VAIF_API_URL || 'https://api.vaif.studio',
|
|
3423
3433
|
apiKey: process.env.NEXT_PUBLIC_VAIF_API_KEY!,
|
|
3424
3434
|
});
|
|
3425
3435
|
|
|
3426
3436
|
// Server client
|
|
3427
3437
|
export function createVaifServer() {
|
|
3428
3438
|
return createVaifClient({
|
|
3429
|
-
|
|
3430
|
-
|
|
3439
|
+
baseUrl: process.env.NEXT_PUBLIC_VAIF_API_URL || 'https://api.vaif.studio',
|
|
3440
|
+
apiKey: process.env.VAIF_SECRET_KEY!,
|
|
3431
3441
|
});
|
|
3432
3442
|
}
|
|
3433
3443
|
`},{path:"lib/storage.ts",content:`import { createVaifServer } from "./vaif";
|
|
@@ -3542,10 +3552,13 @@ function getContentType(fileName: string): string {
|
|
|
3542
3552
|
`},{path:".env.example",content:`# VAIF Configuration
|
|
3543
3553
|
# Get these values from https://vaif.studio/app/security/api-keys \u2192 Project Settings \u2192 API Keys
|
|
3544
3554
|
|
|
3545
|
-
|
|
3546
|
-
NEXT_PUBLIC_VAIF_API_KEY=your-
|
|
3555
|
+
NEXT_PUBLIC_VAIF_API_URL=https://api.vaif.studio
|
|
3556
|
+
NEXT_PUBLIC_VAIF_API_KEY=your-api-key
|
|
3547
3557
|
VAIF_SECRET_KEY=your-secret-key
|
|
3548
3558
|
|
|
3559
|
+
# Database connection (for vaif generate, vaif db push)
|
|
3560
|
+
# DATABASE_URL=postgresql://user:password@host:5432/dbname
|
|
3561
|
+
|
|
3549
3562
|
# CLI uses these (non-prefixed) for vaif db push, vaif secrets, etc.
|
|
3550
3563
|
VAIF_PROJECT_ID=your-project-id
|
|
3551
3564
|
`},{path:"README.md",content:`# E-commerce API \u2014 VAIF Studio
|
|
@@ -3688,16 +3701,16 @@ export const posts = pgTable("posts", {
|
|
|
3688
3701
|
|
|
3689
3702
|
return Response.json({ message: \`Hello, \${name}!\` });
|
|
3690
3703
|
}
|
|
3691
|
-
`}]},dependencies:["@vaiftech/client","@vaiftech/auth"],postInstructions:["Copy .env.example to .env.local and fill in your project credentials","Create a 'product-images' storage bucket in your VAIF dashboard","Import storage helpers from '@/lib/storage' in your API routes","Use uploadProductImage() in your product creation flow","Run: npx vaif generate to generate TypeScript types"]}};function
|
|
3692
|
-
? Which VAIF features do you want to include?`)),
|
|
3693
|
-
Unknown template: ${a}`)),console.log(
|
|
3694
|
-
`)),process.exit(1));let e;t.features&&t.features.length>0?e=t.features.filter(
|
|
3695
|
-
No features specified.`)),console.log(
|
|
3696
|
-
`,"utf-8"),console.log(
|
|
3697
|
-
`,"utf-8");}catch{}(n.dependencies?.length||n.devDependencies?.length)&&(console.log(""),console.log(
|
|
3698
|
-
--add-features requires --template to know which template to use.`)),console.log(
|
|
3699
|
-
Use --force to overwrite existing configuration.`)),process.exit(1));try{if(
|
|
3704
|
+
`}]},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 Qe(){console.log(""),console.log(c.bold("Available project templates")),console.log("");let a=26,t=22;console.log(` ${c.gray("Template".padEnd(a))}${c.gray("Stack".padEnd(t))}${c.gray("Description")}`),console.log(c.gray(" "+"-".repeat(a+t+40)));for(let[n,e]of Object.entries(G))console.log(` ${c.cyan(n.padEnd(a))}${c.yellow(e.tag.padEnd(t))}${c.white(e.description)}`);console.log(""),console.log(c.gray("Usage:")),console.log(c.gray(" npx @vaiftech/cli init --template <name>")),console.log(c.gray(" npx @vaiftech/cli init -t nextjs-fullstack")),console.log(c.gray(" npx @vaiftech/cli init -t react-spa --features auth,database,realtime")),console.log(""),console.log(c.gray("Available features: auth, database, realtime, storage, functions")),console.log("");}async function ve(a){if(!process.stdin.isTTY||!process.stdout.isTTY)return a;let t=new Set(a.map(e=>_.findIndex(r=>r.name===e)).filter(e=>e>=0)),n=0;return new Promise(e=>{let r=q.createInterface({input:process.stdin,output:process.stdout});q.emitKeypressEvents(process.stdin,r),process.stdin.setRawMode&&process.stdin.setRawMode(true);function o(){let u=_.length+2;process.stdout.write(`\x1B[${u}A`),l();}function l(){console.log(c.bold(`
|
|
3705
|
+
? Which VAIF features do you want to include?`)),_.forEach((u,s)=>{let i=t.has(s)?c.green("[x]"):"[ ]",p=s===n?c.cyan("> "):" ";console.log(`${p}${i} ${u.label} ${c.gray(`(${u.description})`)}`);}),console.log(c.gray(" (up/down to move, space to toggle, enter to confirm)"));}l(),process.stdin.on("keypress",(u,s)=>{if(s.name==="up"&&n>0)n--,o();else if(s.name==="down"&&n<_.length-1)n++,o();else if(s.name==="space")t.has(n)?t.delete(n):t.add(n),o();else if(s.name==="return"){process.stdin.setRawMode&&process.stdin.setRawMode(false),r.close();let i=[...t].sort().map(p=>_[p].name);e(i.length>0?i:a);}else s.name==="c"&&s.ctrl&&(process.stdin.setRawMode&&process.stdin.setRawMode(false),r.close(),process.exit(0));});})}async function V(a,t={}){let n=G[a];n||(console.log(c.red(`
|
|
3706
|
+
Unknown template: ${a}`)),console.log(c.yellow(`Run 'vaif templates' to see available templates.
|
|
3707
|
+
`)),process.exit(1));let e;t.features&&t.features.length>0?e=t.features.filter(i=>_.some(p=>p.name===i)):t.addOnly?(console.log(c.red(`
|
|
3708
|
+
No features specified.`)),console.log(c.yellow("Usage: vaif init --template <name> --add-features <features>")),console.log(c.gray("Available features: auth, database, realtime, storage, functions")),process.exit(1)):n.featureFiles&&Object.keys(n.featureFiles).length>0?e=await ve(n.defaultFeatures??["database","auth"]):e=n.defaultFeatures??[],t.addOnly?(console.log(""),console.log(c.bold(`Adding features to ${c.cyan(n.name)} project...`)),console.log(c.gray(` Features: ${e.join(", ")}`)),console.log("")):(console.log(""),console.log(c.bold(`Scaffolding ${c.cyan(n.name)} template...`)),e.length>0&&console.log(c.gray(` Features: ${e.join(", ")}`)),console.log(""));let r=t.addOnly?[]:[...n.files];if(n.featureFiles)for(let i of e){let p=n.featureFiles[i];p&&r.push(...p);}let o=0,l=0;for(let i of r){let p=N.resolve(i.path),g=N.dirname(p);if(y.existsSync(g)||y.mkdirSync(g,{recursive:true}),i.path==="package.json"&&y.existsSync(p)&&!t.force)try{let m=JSON.parse(y.readFileSync(p,"utf-8")),h=JSON.parse(i.content),A=k=>{if(!k)return {};let j={};for(let[H,S]of Object.entries(k))!S.startsWith("workspace:")&&!S.startsWith("link:")&&!S.startsWith("file:")&&(j[H]=S);return j};m.dependencies={...A(m.dependencies),...h.dependencies||{}},m.devDependencies={...A(m.devDependencies),...h.devDependencies||{}},h.scripts&&(m.scripts={...m.scripts||{},...h.scripts}),y.writeFileSync(p,JSON.stringify(m,null,2)+`
|
|
3709
|
+
`,"utf-8"),console.log(c.green(` merge ${i.path} (added dependencies)`)),o++;continue}catch{}if(y.existsSync(p)&&!t.force){console.log(c.yellow(` skip ${i.path} (already exists)`)),l++;continue}y.writeFileSync(p,i.content,"utf-8"),console.log(c.green(` create ${i.path}`)),o++;}console.log(""),o>0&&console.log(c.green(`Created ${o} file${o!==1?"s":""}.`)),l>0&&console.log(c.yellow(`Skipped ${l} file${l!==1?"s":""} (use --force to overwrite).`));let u={auth:{"@vaiftech/auth":"^1.0.0"},database:{},realtime:{},storage:{},functions:{}},s=N.resolve("package.json");if(y.existsSync(s)&&e.length>0)try{let i=JSON.parse(y.readFileSync(s,"utf-8")),p=!1;for(let g of e){let m=u[g];if(m)for(let[h,A]of Object.entries(m))i.dependencies?.[h]||(i.dependencies=i.dependencies||{},i.dependencies[h]=A,p=!0);}p&&y.writeFileSync(s,JSON.stringify(i,null,2)+`
|
|
3710
|
+
`,"utf-8");}catch{}(n.dependencies?.length||n.devDependencies?.length)&&(console.log(""),console.log(c.bold("Install dependencies:")),n.dependencies?.length&&console.log(c.cyan(` npm install ${n.dependencies.join(" ")}`)),n.devDependencies?.length&&console.log(c.cyan(` npm install -D ${n.devDependencies.join(" ")}`))),console.log(""),console.log(c.bold.green("Project scaffolded successfully!")),console.log(""),console.log(c.bold(" Next steps:")),n.postInstructions.forEach(i=>{console.log(c.gray(` ${i}`));}),console.log(""),console.log(c.gray(" Get your project credentials at https://vaif.studio/app/security/api-keys")),console.log("");}var be={$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 rt(a){if(a.addFeatures){a.template||(console.log(c.red(`
|
|
3711
|
+
--add-features requires --template to know which template to use.`)),console.log(c.gray("Example: vaif init --template react-spa --add-features functions,storage")),process.exit(1));let e=a.addFeatures.split(",").map(r=>r.trim());await V(a.template,{force:a.force,features:e,addOnly:true});return}let t=P("Initializing VAIF configuration...").start(),n=N.resolve("vaif.config.json");y.existsSync(n)&&!a.force&&(t.fail("vaif.config.json already exists"),console.log(c.yellow(`
|
|
3712
|
+
Use --force to overwrite existing configuration.`)),process.exit(1));try{if(y.writeFileSync(n,JSON.stringify(be,null,2),"utf-8"),t.succeed("Created vaif.config.json"),a.template){let e=a.features?a.features.split(",").map(r=>r.trim()):void 0;await V(a.template,{force:a.force,features:e});}else {let e=N.resolve(".env.example");if(y.existsSync(e)||(y.writeFileSync(e,`# VAIF Configuration
|
|
3700
3713
|
DATABASE_URL=postgresql://user:password@localhost:5432/database
|
|
3701
3714
|
VAIF_API_KEY=your-api-key
|
|
3702
|
-
`,"utf-8"),console.log(
|
|
3703
|
-
Error: ${e.message}`)),process.exit(1);}}export{
|
|
3715
|
+
`,"utf-8"),console.log(c.gray("Created .env.example"))),a.typescript){let r=N.resolve("src/types");y.existsSync(r)||(y.mkdirSync(r,{recursive:!0}),console.log(c.gray("Created src/types directory")));}console.log(""),console.log(c.green("VAIF initialized successfully!")),console.log(""),console.log(c.gray("Next steps:")),console.log(c.gray(" 1. Update vaif.config.json with your project ID")),console.log(c.gray(" 2. Set DATABASE_URL in your environment")),console.log(c.gray(" 3. Run: npx vaif generate")),console.log("");}}catch(e){t.fail("Failed to initialize"),e instanceof Error&&console.error(c.red(`
|
|
3716
|
+
Error: ${e.message}`)),process.exit(1);}}export{K as a,R as b,Le as c,Ue as d,Ne as e,Ye as f,Qe as g,rt as h};
|