@vaiftech/cli 1.5.1 → 1.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +32 -1
- package/dist/{chunk-F5OP4YDB.js → chunk-RBCJFCBE.js} +84 -37
- package/dist/cli.cjs +127 -75
- package/dist/cli.js +41 -36
- package/dist/index.cjs +67 -20
- 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`):
|
|
@@ -241,11 +268,15 @@ vaif keys list # List all keys
|
|
|
241
268
|
|
|
242
269
|
## Environment Variables
|
|
243
270
|
|
|
271
|
+
The CLI automatically loads `.env` files from the current directory. You can set these variables in your `.env` or export them in your shell:
|
|
272
|
+
|
|
244
273
|
| Variable | Description |
|
|
245
274
|
|----------|-------------|
|
|
246
275
|
| `VAIF_API_URL` | API base URL (default: `https://api.vaif.studio`) |
|
|
247
276
|
| `VAIF_TOKEN` | API token (alternative to `vaif login`) |
|
|
248
|
-
| `VAIF_PROJECT_ID` | Default project ID |
|
|
277
|
+
| `VAIF_PROJECT_ID` | Default project ID (also read from `vaif.config.json`) |
|
|
278
|
+
|
|
279
|
+
Project ID resolution order: `--project-id` flag > `vaif.config.json` > `VAIF_PROJECT_ID` env var > login session.
|
|
249
280
|
|
|
250
281
|
## Related Packages
|
|
251
282
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import g from'fs';import w from'path';import M from'dotenv';import K from'pg';import z from'ora';import r from'chalk';import q from'prettier';import
|
|
1
|
+
import g from'fs';import w from'path';import M from'dotenv';import K from'pg';import z from'ora';import r from'chalk';import q from'prettier';import L from'readline';M.config();async function U(n){let t=w.resolve(n);if(!g.existsSync(t))return null;try{let a=g.readFileSync(t,"utf-8"),e=JSON.parse(a);return e.database?.url&&(e.database.url=N(e.database.url)),e.api?.apiKey&&(e.api.apiKey=N(e.api.apiKey)),e}catch{throw new Error(`Failed to parse config file: ${n}`)}}function N(n){return n.replace(/\$\{([^}]+)\}/g,(t,a)=>process.env[a]||t)}async function B(n,t){let a=await n.query(`
|
|
2
2
|
SELECT table_name, table_type
|
|
3
3
|
FROM information_schema.tables
|
|
4
4
|
WHERE table_schema = $1
|
|
@@ -19,7 +19,7 @@ import g from'fs';import w from'path';import M from'dotenv';import K from'pg';im
|
|
|
19
19
|
FROM information_schema.columns
|
|
20
20
|
WHERE table_schema = $1
|
|
21
21
|
ORDER BY table_name, ordinal_position
|
|
22
|
-
`,[t]),
|
|
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 S={smallint:"number",integer:"number",bigint:"string",int2:"number",int4:"number",int8:"string",decimal:"string",numeric:"string",real:"number",float4:"number",float8:"number","double precision":"number",money:"string",boolean:"boolean",bool:"boolean",text:"string",varchar:"string",char:"string",character:"string","character varying":"string",name:"string",citext:"string",date:"string",time:"string",timetz:"string","time without time zone":"string","time with time zone":"string",timestamp:"string",timestamptz:"string","timestamp without time zone":"string","timestamp with time zone":"string",interval:"string",bytea:"Buffer",uuid:"string",json:"unknown",jsonb:"unknown",inet:"string",cidr:"string",macaddr:"string",macaddr8:"string",point:"{ x: number; y: number }",line:"string",lseg:"string",box:"string",path:"string",polygon:"string",circle:"string",ARRAY:"unknown[]"};function Y(n,t){let{data_type:a,udt_name:e,is_nullable:o}=n;if(t.has(e)){let d=t.get(e).map(l=>`"${l}"`).join(" | ");return o==="YES"?`(${d}) | null`:d}if(a==="ARRAY"){let c=e.replace(/^_/,"");if(t.has(c)){let i=t.get(c).map(u=>`"${u}"`).join(" | ");return o==="YES"?`(${i})[] | null`:`(${i})[]`}let d=S[c]||"unknown";return o==="YES"?`${d}[] | null`:`${d}[]`}let s=S[a]||S[e]||"unknown";return o==="YES"&&(s=`${s} | null`),s}function x(n){return n.split(/[_\-\s]+/).map(t=>t.charAt(0).toUpperCase()+t.slice(1).toLowerCase()).join("")}function $(n,t){let a=x(n),e=t.map(o=>` | "${o}"`).join(`
|
|
48
48
|
`);return `export type ${a} =
|
|
49
|
-
${e};`}function G(n,t,a){let e=x(n),
|
|
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",
|
|
@@ -81,6 +81,7 @@ Make sure your database is running and accessible.`))),process.exit(1);}}var b=[
|
|
|
81
81
|
"@types/node": "^22.0.0",
|
|
82
82
|
"@types/react": "^19.0.0",
|
|
83
83
|
"@types/react-dom": "^19.0.0",
|
|
84
|
+
"drizzle-kit": "^0.30.0",
|
|
84
85
|
"typescript": "^5.7.0"
|
|
85
86
|
}
|
|
86
87
|
}
|
|
@@ -150,18 +151,17 @@ body {
|
|
|
150
151
|
-webkit-font-smoothing: antialiased;
|
|
151
152
|
-moz-osx-font-smoothing: grayscale;
|
|
152
153
|
}
|
|
153
|
-
`},{path:"lib/vaif.ts",content:`import {
|
|
154
|
-
import { createServerClient } from "@vaiftech/client/server";
|
|
154
|
+
`},{path:"lib/vaif.ts",content:`import { createVaifClient } from "@vaiftech/client";
|
|
155
155
|
|
|
156
156
|
// Browser client \u2013 safe to use in Client Components
|
|
157
|
-
export const vaif =
|
|
157
|
+
export const vaif = createVaifClient({
|
|
158
158
|
projectId: process.env.NEXT_PUBLIC_VAIF_PROJECT_ID!,
|
|
159
159
|
apiKey: process.env.NEXT_PUBLIC_VAIF_API_KEY!,
|
|
160
160
|
});
|
|
161
161
|
|
|
162
162
|
// Server client \u2013 use in Server Components, Route Handlers, Server Actions
|
|
163
163
|
export function createVaifServer() {
|
|
164
|
-
return
|
|
164
|
+
return createVaifClient({
|
|
165
165
|
projectId: process.env.NEXT_PUBLIC_VAIF_PROJECT_ID!,
|
|
166
166
|
secretKey: process.env.VAIF_SECRET_KEY!,
|
|
167
167
|
});
|
|
@@ -274,7 +274,7 @@ A full-stack Next.js application powered by [VAIF Studio](https://vaif.studio),
|
|
|
274
274
|
|
|
275
275
|
Full documentation is available at <https://docs.vaif.studio>.
|
|
276
276
|
`}],featureFiles:{auth:[{path:"middleware.ts",content:`import { NextResponse, type NextRequest } from "next/server";
|
|
277
|
-
import {
|
|
277
|
+
import { createVaifClient } from "@vaiftech/client";
|
|
278
278
|
import { authMiddleware } from "@vaiftech/auth/nextjs";
|
|
279
279
|
|
|
280
280
|
const protectedRoutes = ["/dashboard", "/settings", "/api/protected"];
|
|
@@ -284,7 +284,7 @@ export async function middleware(request: NextRequest) {
|
|
|
284
284
|
const isProtected = protectedRoutes.some((route) => pathname.startsWith(route));
|
|
285
285
|
if (!isProtected) return NextResponse.next();
|
|
286
286
|
|
|
287
|
-
const vaif =
|
|
287
|
+
const vaif = createVaifClient({
|
|
288
288
|
projectId: process.env.NEXT_PUBLIC_VAIF_PROJECT_ID!,
|
|
289
289
|
secretKey: process.env.VAIF_SECRET_KEY!,
|
|
290
290
|
});
|
|
@@ -527,6 +527,11 @@ export const posts = pgTable("posts", {
|
|
|
527
527
|
});
|
|
528
528
|
`}],functions:[{path:"functions/hello.ts",content:`export default async function handler(req: Request): Promise<Response> {
|
|
529
529
|
const { name } = await req.json().catch(() => ({ name: "World" }));
|
|
530
|
+
|
|
531
|
+
// Secrets set via \\\`vaif secrets set\\\` or the Security > Secrets page
|
|
532
|
+
// are available as environment variables at runtime:
|
|
533
|
+
// const apiKey = process.env.MY_API_KEY;
|
|
534
|
+
|
|
530
535
|
return Response.json({ message: \`Hello, \${name}!\` });
|
|
531
536
|
}
|
|
532
537
|
`}]},dependencies:["@vaiftech/client","@vaiftech/auth","@vaiftech/react","next","react","react-dom"],devDependencies:["@types/node","@types/react","@types/react-dom","typescript"],postInstructions:["cd my-vaif-app","npm install","# Copy .env.local.example to .env.local and add your VAIF credentials","npm run dev"]},"react-spa":{name:"React SPA",description:"Single-page React app with Vite, VAIF client, and provider wrapper",tag:"React + Vite",defaultFeatures:["database","auth"],files:[{path:"package.json",content:`{
|
|
@@ -547,9 +552,11 @@ export const posts = pgTable("posts", {
|
|
|
547
552
|
"react-router-dom": "^7.0.0"
|
|
548
553
|
},
|
|
549
554
|
"devDependencies": {
|
|
555
|
+
"@types/node": "^22.0.0",
|
|
550
556
|
"@types/react": "^19.0.0",
|
|
551
557
|
"@types/react-dom": "^19.0.0",
|
|
552
558
|
"@vitejs/plugin-react": "^4.4.0",
|
|
559
|
+
"drizzle-kit": "^0.30.0",
|
|
553
560
|
"typescript": "^5.7.0",
|
|
554
561
|
"vite": "^6.0.0"
|
|
555
562
|
}
|
|
@@ -637,9 +644,9 @@ function Home() {
|
|
|
637
644
|
</div>
|
|
638
645
|
);
|
|
639
646
|
}
|
|
640
|
-
`},{path:"src/lib/vaif.ts",content:`import {
|
|
647
|
+
`},{path:"src/lib/vaif.ts",content:`import { createVaifClient } from "@vaiftech/client";
|
|
641
648
|
|
|
642
|
-
export const vaif =
|
|
649
|
+
export const vaif = createVaifClient({
|
|
643
650
|
projectId: import.meta.env.VITE_VAIF_PROJECT_ID,
|
|
644
651
|
apiKey: import.meta.env.VITE_VAIF_API_KEY,
|
|
645
652
|
});
|
|
@@ -1036,6 +1043,11 @@ export const posts = pgTable("posts", {
|
|
|
1036
1043
|
});
|
|
1037
1044
|
`}],functions:[{path:"functions/hello.ts",content:`export default async function handler(req: Request): Promise<Response> {
|
|
1038
1045
|
const { name } = await req.json().catch(() => ({ name: "World" }));
|
|
1046
|
+
|
|
1047
|
+
// Secrets set via \\\`vaif secrets set\\\` or the Security > Secrets page
|
|
1048
|
+
// are available as environment variables at runtime:
|
|
1049
|
+
// const apiKey = process.env.MY_API_KEY;
|
|
1050
|
+
|
|
1039
1051
|
return Response.json({ message: \`Hello, \${name}!\` });
|
|
1040
1052
|
}
|
|
1041
1053
|
`}]},dependencies:["@vaiftech/client","@vaiftech/react","react","react-dom","react-router-dom"],devDependencies:["@types/react","@types/react-dom","@vitejs/plugin-react","typescript","vite"],postInstructions:["cd my-vaif-app","npm install","# Copy .env.example to .env and add your VAIF credentials","npm run dev"]},"ios-swift-app":{name:"iOS Swift App",description:"Swift client manager for iOS/macOS apps using Swift Package Manager",tag:"Swift / iOS",defaultFeatures:["database","auth"],files:[{path:"VaifManager.swift",content:`import Foundation
|
|
@@ -1259,7 +1271,9 @@ Full documentation is available at <https://docs.vaif.studio>.
|
|
|
1259
1271
|
"react-native": "~0.76.0"
|
|
1260
1272
|
},
|
|
1261
1273
|
"devDependencies": {
|
|
1274
|
+
"@types/node": "^22.0.0",
|
|
1262
1275
|
"@types/react": "^19.0.0",
|
|
1276
|
+
"drizzle-kit": "^0.30.0",
|
|
1263
1277
|
"typescript": "^5.7.0"
|
|
1264
1278
|
}
|
|
1265
1279
|
}
|
|
@@ -1625,6 +1639,11 @@ export const posts = pgTable("posts", {
|
|
|
1625
1639
|
});
|
|
1626
1640
|
`}],functions:[{path:"functions/hello.ts",content:`export default async function handler(req: Request): Promise<Response> {
|
|
1627
1641
|
const { name } = await req.json().catch(() => ({ name: "World" }));
|
|
1642
|
+
|
|
1643
|
+
// Secrets set via \\\`vaif secrets set\\\` or the Security > Secrets page
|
|
1644
|
+
// are available as environment variables at runtime:
|
|
1645
|
+
// const apiKey = process.env.MY_API_KEY;
|
|
1646
|
+
|
|
1628
1647
|
return Response.json({ message: \`Hello, \${name}!\` });
|
|
1629
1648
|
}
|
|
1630
1649
|
`}]},dependencies:["@vaiftech/sdk-expo","@react-native-async-storage/async-storage","expo","expo-router","react","react-native"],postInstructions:["cd my-vaif-app","npm install","# Copy .env.example to .env and add your VAIF credentials","npx expo start"]},"flutter-app":{name:"Flutter App",description:"Dart/Flutter client setup with environment configuration",tag:"Flutter / Dart",defaultFeatures:["database","auth"],files:[{path:"lib/main.dart",content:`import 'package:flutter/material.dart';
|
|
@@ -2236,6 +2255,7 @@ async def invoke_function(function_name: str, payload: dict = {}):
|
|
|
2236
2255
|
`},{path:"functions/hello.py",content:`"""Example VAIF serverless function."""
|
|
2237
2256
|
|
|
2238
2257
|
import json
|
|
2258
|
+
import os
|
|
2239
2259
|
|
|
2240
2260
|
|
|
2241
2261
|
def handler(request):
|
|
@@ -2243,6 +2263,10 @@ def handler(request):
|
|
|
2243
2263
|
|
|
2244
2264
|
Deploy with: vaif functions deploy
|
|
2245
2265
|
"""
|
|
2266
|
+
# Secrets set via \\\`vaif secrets set\\\` or the Security > Secrets page
|
|
2267
|
+
# are available as environment variables at runtime:
|
|
2268
|
+
# api_key = os.environ.get("MY_API_KEY")
|
|
2269
|
+
|
|
2246
2270
|
try:
|
|
2247
2271
|
body = json.loads(request.body) if request.body else {}
|
|
2248
2272
|
name = body.get("name", "World")
|
|
@@ -2557,6 +2581,11 @@ type HelloResponse struct {
|
|
|
2557
2581
|
}
|
|
2558
2582
|
|
|
2559
2583
|
// HelloHandler is an example VAIF serverless function.
|
|
2584
|
+
//
|
|
2585
|
+
// Secrets set via \\\`vaif secrets set\\\` or the Security > Secrets page
|
|
2586
|
+
// are available as environment variables at runtime:
|
|
2587
|
+
//
|
|
2588
|
+
// apiKey := os.Getenv("MY_API_KEY") // import "os"
|
|
2560
2589
|
func HelloHandler(w http.ResponseWriter, r *http.Request) {
|
|
2561
2590
|
var req HelloRequest
|
|
2562
2591
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil || req.Name == "" {
|
|
@@ -2568,9 +2597,9 @@ func HelloHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
2568
2597
|
Message: fmt.Sprintf("Hello, %s!", req.Name),
|
|
2569
2598
|
})
|
|
2570
2599
|
}
|
|
2571
|
-
`}]},postInstructions:["go mod tidy","# Copy .env.example to .env and add your VAIF credentials","go run main.go"]},"todo-app":{name:"Todo App",description:"Simple React todo app \u2013 great for learning VAIF basics",tag:"React Starter",defaultFeatures:["database"],files:[{path:"src/lib/vaif.ts",content:`import {
|
|
2600
|
+
`}]},postInstructions:["go mod tidy","# Copy .env.example to .env and add your VAIF credentials","go run main.go"]},"todo-app":{name:"Todo App",description:"Simple React todo app \u2013 great for learning VAIF basics",tag:"React Starter",defaultFeatures:["database"],files:[{path:"src/lib/vaif.ts",content:`import { createVaifClient } from "@vaiftech/client";
|
|
2572
2601
|
|
|
2573
|
-
export const vaif =
|
|
2602
|
+
export const vaif = createVaifClient({
|
|
2574
2603
|
projectId: import.meta.env.VITE_VAIF_PROJECT_ID,
|
|
2575
2604
|
apiKey: import.meta.env.VITE_VAIF_API_KEY,
|
|
2576
2605
|
});
|
|
@@ -2752,11 +2781,16 @@ export const posts = pgTable("posts", {
|
|
|
2752
2781
|
});
|
|
2753
2782
|
`}],functions:[{path:"functions/hello.ts",content:`export default async function handler(req: Request): Promise<Response> {
|
|
2754
2783
|
const { name } = await req.json().catch(() => ({ name: "World" }));
|
|
2784
|
+
|
|
2785
|
+
// Secrets set via \\\`vaif secrets set\\\` or the Security > Secrets page
|
|
2786
|
+
// are available as environment variables at runtime:
|
|
2787
|
+
// const apiKey = process.env.MY_API_KEY;
|
|
2788
|
+
|
|
2755
2789
|
return Response.json({ message: \`Hello, \${name}!\` });
|
|
2756
2790
|
}
|
|
2757
|
-
`}]},dependencies:["@vaiftech/client","@vaiftech/react"],postInstructions:["Copy .env.example to .env and fill in your project credentials","Create a 'todos' table in your VAIF dashboard with columns: id (uuid), title (text), done (boolean), created_at (timestamptz)","Import helpers from './lib/vaif' in your components","Run: npx vaif generate to generate TypeScript types"]},"realtime-chat":{name:"Realtime Chat",description:"React chat app with VAIF realtime subscriptions for live messaging",tag:"React + Realtime",defaultFeatures:["database","realtime","auth"],files:[{path:"src/lib/vaif.ts",content:`import {
|
|
2791
|
+
`}]},dependencies:["@vaiftech/client","@vaiftech/react"],postInstructions:["Copy .env.example to .env and fill in your project credentials","Create a 'todos' table in your VAIF dashboard with columns: id (uuid), title (text), done (boolean), created_at (timestamptz)","Import helpers from './lib/vaif' in your components","Run: npx vaif generate to generate TypeScript types"]},"realtime-chat":{name:"Realtime Chat",description:"React chat app with VAIF realtime subscriptions for live messaging",tag:"React + Realtime",defaultFeatures:["database","realtime","auth"],files:[{path:"src/lib/vaif.ts",content:`import { createVaifClient } from "@vaiftech/client";
|
|
2758
2792
|
|
|
2759
|
-
export const vaif =
|
|
2793
|
+
export const vaif = createVaifClient({
|
|
2760
2794
|
projectId: import.meta.env.VITE_VAIF_PROJECT_ID,
|
|
2761
2795
|
apiKey: import.meta.env.VITE_VAIF_API_KEY,
|
|
2762
2796
|
realtime: {
|
|
@@ -3046,20 +3080,24 @@ export const posts = pgTable("posts", {
|
|
|
3046
3080
|
});
|
|
3047
3081
|
`}],functions:[{path:"functions/hello.ts",content:`export default async function handler(req: Request): Promise<Response> {
|
|
3048
3082
|
const { name } = await req.json().catch(() => ({ name: "World" }));
|
|
3083
|
+
|
|
3084
|
+
// Secrets set via \\\`vaif secrets set\\\` or the Security > Secrets page
|
|
3085
|
+
// are available as environment variables at runtime:
|
|
3086
|
+
// const apiKey = process.env.MY_API_KEY;
|
|
3087
|
+
|
|
3049
3088
|
return Response.json({ message: \`Hello, \${name}!\` });
|
|
3050
3089
|
}
|
|
3051
|
-
`}]},dependencies:["@vaiftech/client","@vaiftech/react"],postInstructions:["Copy .env.example to .env and fill in your project credentials","Create a 'messages' table with columns: id (uuid), content (text), user_id (text), username (text), channel_id (text), created_at (timestamptz)","Enable Realtime on the messages table in your VAIF dashboard","Use the useRealtimeMessages hook in your components","Run: npx vaif generate to generate TypeScript types"]},"saas-starter":{name:"SaaS Starter",description:"Full SaaS starter with VAIF auth, team/org support, and server-side helpers",tag:"Next.js SaaS",defaultFeatures:["database","auth","functions"],files:[{path:"lib/vaif.ts",content:`import {
|
|
3052
|
-
import { createServerClient } from "@vaiftech/client/server";
|
|
3090
|
+
`}]},dependencies:["@vaiftech/client","@vaiftech/react"],postInstructions:["Copy .env.example to .env and fill in your project credentials","Create a 'messages' table with columns: id (uuid), content (text), user_id (text), username (text), channel_id (text), created_at (timestamptz)","Enable Realtime on the messages table in your VAIF dashboard","Use the useRealtimeMessages hook in your components","Run: npx vaif generate to generate TypeScript types"]},"saas-starter":{name:"SaaS Starter",description:"Full SaaS starter with VAIF auth, team/org support, and server-side helpers",tag:"Next.js SaaS",defaultFeatures:["database","auth","functions"],files:[{path:"lib/vaif.ts",content:`import { createVaifClient } from "@vaiftech/client";
|
|
3053
3091
|
|
|
3054
3092
|
// Browser client \u2013 use in Client Components
|
|
3055
|
-
export const vaif =
|
|
3093
|
+
export const vaif = createVaifClient({
|
|
3056
3094
|
projectId: process.env.NEXT_PUBLIC_VAIF_PROJECT_ID!,
|
|
3057
3095
|
apiKey: process.env.NEXT_PUBLIC_VAIF_API_KEY!,
|
|
3058
3096
|
});
|
|
3059
3097
|
|
|
3060
3098
|
// Server client \u2013 use in Server Components, Route Handlers, Server Actions
|
|
3061
3099
|
export function createVaifServer() {
|
|
3062
|
-
return
|
|
3100
|
+
return createVaifClient({
|
|
3063
3101
|
projectId: process.env.NEXT_PUBLIC_VAIF_PROJECT_ID!,
|
|
3064
3102
|
secretKey: process.env.VAIF_SECRET_KEY!,
|
|
3065
3103
|
});
|
|
@@ -3352,20 +3390,24 @@ export const posts = pgTable("posts", {
|
|
|
3352
3390
|
});
|
|
3353
3391
|
`}],functions:[{path:"functions/hello.ts",content:`export default async function handler(req: Request): Promise<Response> {
|
|
3354
3392
|
const { name } = await req.json().catch(() => ({ name: "World" }));
|
|
3393
|
+
|
|
3394
|
+
// Secrets set via \\\`vaif secrets set\\\` or the Security > Secrets page
|
|
3395
|
+
// are available as environment variables at runtime:
|
|
3396
|
+
// const apiKey = process.env.MY_API_KEY;
|
|
3397
|
+
|
|
3355
3398
|
return Response.json({ message: \`Hello, \${name}!\` });
|
|
3356
3399
|
}
|
|
3357
|
-
`}]},dependencies:["@vaiftech/client","@vaiftech/auth","@vaiftech/react"],postInstructions:["Copy .env.example to .env.local and fill in your project credentials","Create 'teams' and 'team_members' tables in your VAIF dashboard","Import auth helpers from '@/lib/auth' in your Server Components/Actions","Use requireUser() for authenticated routes and requireTeamRole() for role checks","Run: npx vaif generate to generate TypeScript types"]},"ecommerce-api":{name:"E-commerce API",description:"API-first e-commerce setup with VAIF storage for product images",tag:"Next.js E-commerce",defaultFeatures:["database","auth","storage"],files:[{path:"lib/vaif.ts",content:`import {
|
|
3358
|
-
import { createServerClient } from "@vaiftech/client/server";
|
|
3400
|
+
`}]},dependencies:["@vaiftech/client","@vaiftech/auth","@vaiftech/react"],postInstructions:["Copy .env.example to .env.local and fill in your project credentials","Create 'teams' and 'team_members' tables in your VAIF dashboard","Import auth helpers from '@/lib/auth' in your Server Components/Actions","Use requireUser() for authenticated routes and requireTeamRole() for role checks","Run: npx vaif generate to generate TypeScript types"]},"ecommerce-api":{name:"E-commerce API",description:"API-first e-commerce setup with VAIF storage for product images",tag:"Next.js E-commerce",defaultFeatures:["database","auth","storage"],files:[{path:"lib/vaif.ts",content:`import { createVaifClient } from "@vaiftech/client";
|
|
3359
3401
|
|
|
3360
3402
|
// Browser client
|
|
3361
|
-
export const vaif =
|
|
3403
|
+
export const vaif = createVaifClient({
|
|
3362
3404
|
projectId: process.env.NEXT_PUBLIC_VAIF_PROJECT_ID!,
|
|
3363
3405
|
apiKey: process.env.NEXT_PUBLIC_VAIF_API_KEY!,
|
|
3364
3406
|
});
|
|
3365
3407
|
|
|
3366
3408
|
// Server client
|
|
3367
3409
|
export function createVaifServer() {
|
|
3368
|
-
return
|
|
3410
|
+
return createVaifClient({
|
|
3369
3411
|
projectId: process.env.NEXT_PUBLIC_VAIF_PROJECT_ID!,
|
|
3370
3412
|
secretKey: process.env.VAIF_SECRET_KEY!,
|
|
3371
3413
|
});
|
|
@@ -3618,16 +3660,21 @@ export const posts = pgTable("posts", {
|
|
|
3618
3660
|
});
|
|
3619
3661
|
`}],functions:[{path:"functions/hello.ts",content:`export default async function handler(req: Request): Promise<Response> {
|
|
3620
3662
|
const { name } = await req.json().catch(() => ({ name: "World" }));
|
|
3663
|
+
|
|
3664
|
+
// Secrets set via \\\`vaif secrets set\\\` or the Security > Secrets page
|
|
3665
|
+
// are available as environment variables at runtime:
|
|
3666
|
+
// const apiKey = process.env.MY_API_KEY;
|
|
3667
|
+
|
|
3621
3668
|
return Response.json({ message: \`Hello, \${name}!\` });
|
|
3622
3669
|
}
|
|
3623
|
-
`}]},dependencies:["@vaiftech/client","@vaiftech/auth"],postInstructions:["Copy .env.example to .env.local and fill in your project credentials","Create a 'product-images' storage bucket in your VAIF dashboard","Import storage helpers from '@/lib/storage' in your API routes","Use uploadProductImage() in your product creation flow","Run: npx vaif generate to generate TypeScript types"]}};function ve(){console.log(""),console.log(r.bold("Available project templates")),console.log("");let n=26,t=22;console.log(` ${r.gray("Template".padEnd(n))}${r.gray("Stack".padEnd(t))}${r.gray("Description")}`),console.log(r.gray(" "+"-".repeat(n+t+40)));for(let[a,e]of Object.entries(D))console.log(` ${r.cyan(a.padEnd(n))}${r.yellow(e.tag.padEnd(t))}${r.white(e.description)}`);console.log(""),console.log(r.gray("Usage:")),console.log(r.gray(" npx @vaiftech/cli init --template <name>")),console.log(r.gray(" npx @vaiftech/cli init -t nextjs-fullstack")),console.log(r.gray(" npx @vaiftech/cli init -t react-spa --features auth,database,realtime")),console.log(""),console.log(r.gray("Available features: auth, database, realtime, storage, functions")),console.log("");}async function W(n){if(!process.stdin.isTTY||!process.stdout.isTTY)return n;let t=new Set(n.map(e=>b.findIndex(
|
|
3624
|
-
? Which VAIF features do you want to include?`)),b.forEach((
|
|
3670
|
+
`}]},dependencies:["@vaiftech/client","@vaiftech/auth"],postInstructions:["Copy .env.example to .env.local and fill in your project credentials","Create a 'product-images' storage bucket in your VAIF dashboard","Import storage helpers from '@/lib/storage' in your API routes","Use uploadProductImage() in your product creation flow","Run: npx vaif generate to generate TypeScript types"]}};function ve(){console.log(""),console.log(r.bold("Available project templates")),console.log("");let n=26,t=22;console.log(` ${r.gray("Template".padEnd(n))}${r.gray("Stack".padEnd(t))}${r.gray("Description")}`),console.log(r.gray(" "+"-".repeat(n+t+40)));for(let[a,e]of Object.entries(D))console.log(` ${r.cyan(a.padEnd(n))}${r.yellow(e.tag.padEnd(t))}${r.white(e.description)}`);console.log(""),console.log(r.gray("Usage:")),console.log(r.gray(" npx @vaiftech/cli init --template <name>")),console.log(r.gray(" npx @vaiftech/cli init -t nextjs-fullstack")),console.log(r.gray(" npx @vaiftech/cli init -t react-spa --features auth,database,realtime")),console.log(""),console.log(r.gray("Available features: auth, database, realtime, storage, functions")),console.log("");}async function W(n){if(!process.stdin.isTTY||!process.stdout.isTTY)return n;let t=new Set(n.map(e=>b.findIndex(o=>o.name===e)).filter(e=>e>=0)),a=0;return new Promise(e=>{let o=L.createInterface({input:process.stdin,output:process.stdout});L.emitKeypressEvents(process.stdin,o),process.stdin.setRawMode&&process.stdin.setRawMode(true);function s(){let d=b.length+2;process.stdout.write(`\x1B[${d}A`),c();}function c(){console.log(r.bold(`
|
|
3671
|
+
? Which VAIF features do you want to include?`)),b.forEach((d,l)=>{let i=t.has(l)?r.green("[x]"):"[ ]",u=l===a?r.cyan("> "):" ";console.log(`${u}${i} ${d.label} ${r.gray(`(${d.description})`)}`);}),console.log(r.gray(" (up/down to move, space to toggle, enter to confirm)"));}c(),process.stdin.on("keypress",(d,l)=>{if(l.name==="up"&&a>0)a--,s();else if(l.name==="down"&&a<b.length-1)a++,s();else if(l.name==="space")t.has(a)?t.delete(a):t.add(a),s();else if(l.name==="return"){process.stdin.setRawMode&&process.stdin.setRawMode(false),o.close();let i=[...t].sort().map(u=>b[u].name);e(i.length>0?i:n);}else l.name==="c"&&l.ctrl&&(process.stdin.setRawMode&&process.stdin.setRawMode(false),o.close(),process.exit(0));});})}async function k(n,t={}){let a=D[n];a||(console.log(r.red(`
|
|
3625
3672
|
Unknown template: ${n}`)),console.log(r.yellow(`Run 'vaif templates' to see available templates.
|
|
3626
|
-
`)),process.exit(1));let e;t.features&&t.features.length>0?e=t.features.filter(i=>b.some(
|
|
3627
|
-
`,"utf-8"),console.log(r.green(` merge ${i.path} (added dependencies)`)),
|
|
3673
|
+
`)),process.exit(1));let e;t.features&&t.features.length>0?e=t.features.filter(i=>b.some(u=>u.name===i)):a.featureFiles&&Object.keys(a.featureFiles).length>0?e=await W(a.defaultFeatures??["database","auth"]):e=a.defaultFeatures??[],console.log(""),console.log(r.bold(`Scaffolding ${r.cyan(a.name)} template...`)),e.length>0&&console.log(r.gray(` Features: ${e.join(", ")}`)),console.log("");let o=[...a.files];if(a.featureFiles)for(let i of e){let u=a.featureFiles[i];u&&o.push(...u);}let s=0,c=0;for(let i of o){let u=w.resolve(i.path),f=w.dirname(u);if(g.existsSync(f)||g.mkdirSync(f,{recursive:true}),i.path==="package.json"&&g.existsSync(u)&&!t.force)try{let p=JSON.parse(g.readFileSync(u,"utf-8")),h=JSON.parse(i.content),y=P=>{if(!P)return {};let R={};for(let[j,A]of Object.entries(P))!A.startsWith("workspace:")&&!A.startsWith("link:")&&!A.startsWith("file:")&&(R[j]=A);return R};p.dependencies={...y(p.dependencies),...h.dependencies||{}},p.devDependencies={...y(p.devDependencies),...h.devDependencies||{}},h.scripts&&(p.scripts={...p.scripts||{},...h.scripts}),g.writeFileSync(u,JSON.stringify(p,null,2)+`
|
|
3674
|
+
`,"utf-8"),console.log(r.green(` merge ${i.path} (added dependencies)`)),s++;continue}catch{}if(g.existsSync(u)&&!t.force){console.log(r.yellow(` skip ${i.path} (already exists)`)),c++;continue}g.writeFileSync(u,i.content,"utf-8"),console.log(r.green(` create ${i.path}`)),s++;}console.log(""),s>0&&console.log(r.green(`Created ${s} file${s!==1?"s":""}.`)),c>0&&console.log(r.yellow(`Skipped ${c} file${c!==1?"s":""} (use --force to overwrite).`));let d={auth:{"@vaiftech/auth":"^1.0.0"},database:{},realtime:{},storage:{},functions:{}},l=w.resolve("package.json");if(g.existsSync(l)&&e.length>0)try{let i=JSON.parse(g.readFileSync(l,"utf-8")),u=!1;for(let f of e){let p=d[f];if(p)for(let[h,y]of Object.entries(p))i.dependencies?.[h]||(i.dependencies=i.dependencies||{},i.dependencies[h]=y,u=!0);}u&&g.writeFileSync(l,JSON.stringify(i,null,2)+`
|
|
3628
3675
|
`,"utf-8");}catch{}(a.dependencies?.length||a.devDependencies?.length)&&(console.log(""),console.log(r.bold("Install dependencies:")),a.dependencies?.length&&console.log(r.cyan(` npm install ${a.dependencies.join(" ")}`)),a.devDependencies?.length&&console.log(r.cyan(` npm install -D ${a.devDependencies.join(" ")}`))),console.log(""),console.log(r.bold.green("Project scaffolded successfully!")),console.log(""),console.log(r.bold(" Next steps:")),a.postInstructions.forEach(i=>{console.log(r.gray(` ${i}`));}),console.log(""),console.log(r.gray(" Get your project credentials at https://vaif.studio/app/security/api-keys")),console.log("");}var X={$schema:"https://vaif.studio/schemas/config.json",projectId:"",database:{url:"${DATABASE_URL}",schema:"public"},types:{output:"./src/types/database.ts"},api:{baseUrl:"https://api.vaif.studio"}};async function Se(n){let t=z("Initializing VAIF configuration...").start(),a=w.resolve("vaif.config.json");g.existsSync(a)&&!n.force&&(t.fail("vaif.config.json already exists"),console.log(r.yellow(`
|
|
3629
|
-
Use --force to overwrite existing configuration.`)),process.exit(1));try{if(g.writeFileSync(a,JSON.stringify(X,null,2),"utf-8"),t.succeed("Created vaif.config.json"),n.template){let e=n.features?n.features.split(",").map(
|
|
3676
|
+
Use --force to overwrite existing configuration.`)),process.exit(1));try{if(g.writeFileSync(a,JSON.stringify(X,null,2),"utf-8"),t.succeed("Created vaif.config.json"),n.template){let e=n.features?n.features.split(",").map(o=>o.trim()):void 0;await k(n.template,{force:n.force,features:e});}else {let e=w.resolve(".env.example");if(g.existsSync(e)||(g.writeFileSync(e,`# VAIF Configuration
|
|
3630
3677
|
DATABASE_URL=postgresql://user:password@localhost:5432/database
|
|
3631
3678
|
VAIF_API_KEY=your-api-key
|
|
3632
|
-
`,"utf-8"),console.log(r.gray("Created .env.example"))),n.typescript){let
|
|
3633
|
-
Error: ${e.message}`)),process.exit(1);}}export{U as a,
|
|
3679
|
+
`,"utf-8"),console.log(r.gray("Created .env.example"))),n.typescript){let o=w.resolve("src/types");g.existsSync(o)||(g.mkdirSync(o,{recursive:!0}),console.log(r.gray("Created src/types directory")));}console.log(""),console.log(r.green("VAIF initialized successfully!")),console.log(""),console.log(r.gray("Next steps:")),console.log(r.gray(" 1. Update vaif.config.json with your project ID")),console.log(r.gray(" 2. Set DATABASE_URL in your environment")),console.log(r.gray(" 3. Run: npx vaif generate")),console.log("");}}catch(e){t.fail("Failed to initialize"),e instanceof Error&&console.error(r.red(`
|
|
3680
|
+
Error: ${e.message}`)),process.exit(1);}}export{U as a,ue as b,ve as c,Se as d};
|