@vaiftech/cli 1.9.8 → 1.10.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/dist/index.cjs CHANGED
@@ -1,10 +1,10 @@
1
- 'use strict';var b=require('fs'),R=require('path'),G=require('dotenv'),W=require('pg'),X=require('ora'),d=require('chalk'),Q=require('prettier'),H=require('os'),B=require('readline');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var b__default=/*#__PURE__*/_interopDefault(b);var R__default=/*#__PURE__*/_interopDefault(R);var G__default=/*#__PURE__*/_interopDefault(G);var W__default=/*#__PURE__*/_interopDefault(W);var X__default=/*#__PURE__*/_interopDefault(X);var d__default=/*#__PURE__*/_interopDefault(d);var Q__default=/*#__PURE__*/_interopDefault(Q);var H__default=/*#__PURE__*/_interopDefault(H);var B__default=/*#__PURE__*/_interopDefault(B);G__default.default.config();async function S(n){let a=R__default.default.resolve(n);if(!b__default.default.existsSync(a))return null;try{let t=b__default.default.readFileSync(a,"utf-8"),e=JSON.parse(t);return e.database?.url&&(e.database.url=j(e.database.url)),e.api?.apiKey&&(e.api.apiKey=j(e.api.apiKey)),e}catch{throw new Error(`Failed to parse config file: ${n}`)}}function j(n){return n.replace(/\$\{([^}]+)\}/g,(a,t)=>process.env[t]||a)}var J=R__default.default.join(H__default.default.homedir(),".vaif"),M=R__default.default.join(J,"auth.json");process.env.VAIF_API_URL||"https://api.vaif.studio";function z(){if(!b__default.default.existsSync(M))return null;try{let n=b__default.default.readFileSync(M,"utf-8");return JSON.parse(n)}catch{return null}}var Z=process.env.VAIF_API_URL||"https://api.vaif.studio";async function ee(n,a){let t=await fetch(`${Z}/schema-engine/introspect/${a}`,{headers:{Authorization:`Bearer ${n}`,"Content-Type":"application/json"}});if(!t.ok){let s=await t.text();throw new Error(`API introspection failed: ${s}`)}let e=await t.json();if(!e.ok||!e.schemaExists)throw new Error("Project schema does not exist yet. Push a migration first with `vaif db push`.");let i=new Map,o=[];for(let s of e.tables){let r=s.columns.map(l=>({column_name:l.name,data_type:l.type,is_nullable:l.nullable?"YES":"NO",column_default:l.default,udt_name:l.type,is_identity:l.primaryKey&&l.default?.includes("gen_random_uuid")?"YES":"NO",character_maximum_length:null,numeric_precision:null,numeric_scale:null}));i.set(s.name,r);for(let l of s.foreignKeys)o.push({constraint_name:l.constraintName,table_name:s.name,column_name:l.columnName,foreign_table_name:l.refTable,foreign_column_name:l.refColumn});}return {tables:i,enums:new Map,foreignKeys:o}}async function te(n,a){let t=await n.query(`
1
+ 'use strict';var y=require('fs'),b=require('path'),te=require('dotenv'),ie=require('pg'),X=require('ora'),m=require('chalk'),re=require('prettier'),ae=require('os'),G=require('readline');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var y__default=/*#__PURE__*/_interopDefault(y);var b__default=/*#__PURE__*/_interopDefault(b);var te__default=/*#__PURE__*/_interopDefault(te);var ie__default=/*#__PURE__*/_interopDefault(ie);var X__default=/*#__PURE__*/_interopDefault(X);var m__default=/*#__PURE__*/_interopDefault(m);var re__default=/*#__PURE__*/_interopDefault(re);var ae__default=/*#__PURE__*/_interopDefault(ae);var G__default=/*#__PURE__*/_interopDefault(G);te__default.default.config();async function x(n){let t=b__default.default.resolve(n);if(!y__default.default.existsSync(t))return null;try{let a=y__default.default.readFileSync(t,"utf-8"),e=JSON.parse(a);return e.database?.url&&(e.database.url=q(e.database.url)),e.api?.apiKey&&(e.api.apiKey=q(e.api.apiKey)),e}catch{throw new Error(`Failed to parse config file: ${n}`)}}function q(n){return n.replace(/\$\{([^}]+)\}/g,(t,a)=>process.env[a]||t)}var ne=b__default.default.join(ae__default.default.homedir(),".vaif"),z=b__default.default.join(ne,"auth.json");process.env.VAIF_API_URL||"https://api.vaif.studio";function F(){if(!y__default.default.existsSync(z))return null;try{let n=y__default.default.readFileSync(z,"utf-8");return JSON.parse(n)}catch{return null}}var se=process.env.VAIF_API_URL||"https://api.vaif.studio";async function le(n,t){let a=await fetch(`${se}/schema-engine/introspect/${t}`,{headers:{Authorization:`Bearer ${n}`,"Content-Type":"application/json"}});if(!a.ok){let l=await a.text();throw new Error(`API introspection failed: ${l}`)}let e=await a.json();if(!e.ok||!e.schemaExists)throw new Error("Project schema does not exist yet. Push a migration first with `vaif db push`.");let i=new Map,o=[];for(let l of e.tables){let s=l.columns.map(u=>({column_name:u.name,data_type:u.type,is_nullable:u.nullable?"YES":"NO",column_default:u.default,udt_name:u.type,is_identity:u.primaryKey&&u.default?.includes("gen_random_uuid")?"YES":"NO",character_maximum_length:null,numeric_precision:null,numeric_scale:null}));i.set(l.name,s);for(let u of l.foreignKeys)o.push({constraint_name:u.constraintName,table_name:l.name,column_name:u.columnName,foreign_table_name:u.refTable,foreign_column_name:u.refColumn});}return {tables:i,enums:new Map,foreignKeys:o}}async function ce(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
5
5
  AND table_type = 'BASE TABLE'
6
6
  ORDER BY table_name
7
- `,[a]),e=await n.query(`
7
+ `,[t]),e=await n.query(`
8
8
  SELECT
9
9
  table_name,
10
10
  column_name,
@@ -19,7 +19,7 @@
19
19
  FROM information_schema.columns
20
20
  WHERE table_schema = $1
21
21
  ORDER BY table_name, ordinal_position
22
- `,[a]),i=await n.query(`
22
+ `,[t]),i=await n.query(`
23
23
  SELECT
24
24
  tc.constraint_name,
25
25
  tc.table_name,
@@ -35,7 +35,7 @@
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
- `,[a]),o=await n.query(`
38
+ `,[t]),o=await n.query(`
39
39
  SELECT
40
40
  t.typname as enum_name,
41
41
  e.enumlabel as enum_value
@@ -44,24 +44,24 @@
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
- `,[a]),c=new Map;for(let r of t.rows)c.set(r.table_name,[]);for(let r of e.rows){let l=c.get(r.table_name);l&&l.push(r);}let s=new Map;for(let r of o.rows){let l=s.get(r.enum_name)||[];l.push(r.enum_value),s.set(r.enum_name,l);}return {tables:c,enums:s,foreignKeys:i.rows}}var F={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 ae(n,a){let{data_type:t,udt_name:e,is_nullable:i}=n;if(a.has(e)){let s=a.get(e).map(r=>`"${r}"`).join(" | ");return i==="YES"?`(${s}) | null`:s}if(t==="ARRAY"){let c=e.replace(/^_/,"");if(a.has(c)){let l=a.get(c).map(p=>`"${p}"`).join(" | ");return i==="YES"?`(${l})[] | null`:`(${l})[]`}let s=F[c]||"unknown";return i==="YES"?`${s}[] | null`:`${s}[]`}let o=F[t]||F[e]||"unknown";return i==="YES"&&(o=`${o} | null`),o}function P(n){return n.split(/[_\-\s]+/).map(a=>a.charAt(0).toUpperCase()+a.slice(1).toLowerCase()).join("")}function ne(n,a){let t=P(n),e=a.map(i=>` | "${i}"`).join(`
48
- `);return `export type ${t} =
49
- ${e};`}function ie(n,a,t){let e=P(n),i=[],o=[],c=[];for(let p of a){let m=ae(p,t),u=p.column_name,f=p.column_default!==null||p.is_identity==="YES",h=p.is_nullable==="YES";i.push(` ${u}: ${m};`),f||p.column_name==="id"?o.push(` ${u}?: ${m.replace(" | null","")} | null;`):h?o.push(` ${u}?: ${m};`):o.push(` ${u}: ${m.replace(" | null","")};`),c.push(` ${u}?: ${m.replace(" | null","")} | null;`);}let s=`export interface ${e} {
47
+ `,[t]),r=new Map;for(let s of a.rows)r.set(s.table_name,[]);for(let s of e.rows){let u=r.get(s.table_name);u&&u.push(s);}let l=new Map;for(let s of o.rows){let u=l.get(s.enum_name)||[];u.push(s.enum_value),l.set(s.enum_name,u);}return {tables:r,enums:l,foreignKeys:i.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 ue(n,t){let{data_type:a,udt_name:e,is_nullable:i}=n;if(t.has(e)){let l=t.get(e).map(s=>`"${s}"`).join(" | ");return i==="YES"?`(${l}) | null`:l}if(a==="ARRAY"){let r=e.replace(/^_/,"");if(t.has(r)){let u=t.get(r).map(d=>`"${d}"`).join(" | ");return i==="YES"?`(${u})[] | null`:`(${u})[]`}let l=L[r]||"unknown";return i==="YES"?`${l}[] | null`:`${l}[]`}let o=L[a]||L[e]||"unknown";return i==="YES"&&(o=`${o} | null`),o}function N(n){return n.split(/[_\-\s]+/).map(t=>t.charAt(0).toUpperCase()+t.slice(1).toLowerCase()).join("")}function de(n,t){let a=N(n),e=t.map(i=>` | "${i}"`).join(`
48
+ `);return `export type ${a} =
49
+ ${e};`}function pe(n,t,a){let e=N(n),i=[],o=[],r=[];for(let d of t){let p=ue(d,a),c=d.column_name,f=d.column_default!==null||d.is_identity==="YES",I=d.is_nullable==="YES";i.push(` ${c}: ${p};`),f||d.column_name==="id"?o.push(` ${c}?: ${p.replace(" | null","")} | null;`):I?o.push(` ${c}?: ${p};`):o.push(` ${c}: ${p.replace(" | null","")};`),r.push(` ${c}?: ${p.replace(" | null","")} | null;`);}let l=`export interface ${e} {
50
50
  ${i.join(`
51
51
  `)}
52
- }`,r=`export interface ${e}Insert {
52
+ }`,s=`export interface ${e}Insert {
53
53
  ${o.join(`
54
54
  `)}
55
- }`,l=`export interface ${e}Update {
56
- ${c.join(`
55
+ }`,u=`export interface ${e}Update {
56
+ ${r.join(`
57
57
  `)}
58
- }`;return {base:s,insert:r,update:l}}function oe(n,a,t){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(a.size>0){e.push("// ============ ENUMS ============"),e.push("");for(let[o,c]of a)e.push(ne(o,c)),e.push("");}e.push("// ============ TABLES ============"),e.push("");let i=[];for(let[o,c]of n){let{base:s,insert:r,update:l}=ie(o,c,a);i.push(o),e.push(s),e.push(""),e.push(r),e.push(""),e.push(l),e.push("");}e.push("// ============ DATABASE SCHEMA ============"),e.push(""),e.push("export interface Database {");for(let o of i){let c=P(o);e.push(` ${o}: {`),e.push(` Row: ${c};`),e.push(` Insert: ${c}Insert;`),e.push(` Update: ${c}Update;`),e.push(" };");}return e.push("}"),e.push(""),e.push("export type TableName = keyof Database;"),e.push(""),e.push("// ============ HELPER TYPES ============"),e.push(""),e.push('export type Row<T extends TableName> = Database[T]["Row"];'),e.push('export type Insert<T extends TableName> = Database[T]["Insert"];'),e.push('export type Update<T extends TableName> = Database[T]["Update"];'),e.push(""),e.join(`
59
- `)}async function re(n){let a=X__default.default("Loading configuration...").start();try{let t=await S(n.config),e=n.connection||t?.database?.url||process.env.DATABASE_URL,i=e&&!e.includes("${"),o,c,s;if(i){a.text="Connecting to database...";let u=new W__default.default.Client({connectionString:e});await u.connect(),a.text="Introspecting schema...",{tables:o,enums:c,foreignKeys:s}=await te(u,n.schema),await u.end();}else {let u=z();(!u||!u.token)&&(a.fail("No database connection and not logged in"),console.log(d__default.default.yellow(`
60
- Either:`)),console.log(d__default.default.gray(" 1. Run `vaif login` to authenticate (no DATABASE_URL needed)")),console.log(d__default.default.gray(" 2. Set DATABASE_URL in your .env file")),console.log(d__default.default.gray(" 3. Pass --connection postgresql://user:pass@host:5432/db")),process.exit(1));let f=t?.projectId||process.env.VAIF_PROJECT_ID||u.projectId;f||(a.fail("No project ID specified"),console.log(d__default.default.yellow(`
61
- Set projectId in vaif.config.json or use VAIF_PROJECT_ID env var.`)),process.exit(1)),a.text="Introspecting schema via API...",{tables:o,enums:c,foreignKeys:s}=await ee(u.token,f);}if(o.size===0){a.warn("No tables found"),console.log(d__default.default.yellow(`
62
- Push a migration first: vaif db push`));return}a.text=`Generating types for ${o.size} tables...`;let r=oe(o,c,s),l=await Q__default.default.format(r,{parser:"typescript",semi:!0,singleQuote:!1,trailingComma:"es5",printWidth:100});if(n.dryRun){a.succeed("Generated types (dry run):"),console.log(""),console.log(d__default.default.gray("\u2500".repeat(60))),console.log(l),console.log(d__default.default.gray("\u2500".repeat(60)));return}let p=R__default.default.resolve(n.output),m=R__default.default.dirname(p);b__default.default.existsSync(m)||b__default.default.mkdirSync(m,{recursive:!0}),b__default.default.writeFileSync(p,l,"utf-8"),a.succeed(`Generated types for ${o.size} tables \u2192 ${d__default.default.cyan(n.output)}`),console.log(""),console.log(d__default.default.green("Generated:")),console.log(d__default.default.gray(` Tables: ${o.size}`)),console.log(d__default.default.gray(` Enums: ${c.size}`)),console.log(""),console.log(d__default.default.gray("Import in your code:")),console.log(d__default.default.cyan(` import type { Database, Row, Insert, Update } from "${n.output.replace(/\.ts$/,"")}";`));}catch(t){a.fail("Failed to generate types"),t instanceof Error&&(console.error(d__default.default.red(`
63
- Error: ${t.message}`)),t.message.includes("ECONNREFUSED")&&console.log(d__default.default.yellow(`
64
- 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"}],se={"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:`{
58
+ }`;return {base:l,insert:s,update:u}}function me(n,t,a){let e=["/**"," * Auto-generated TypeScript types from database schema"," * Generated by @vaiftech/cli",` * Generated at: ${new Date().toISOString()}`," * "," * DO NOT EDIT MANUALLY - changes will be overwritten"," */",""];if(t.size>0){e.push("// ============ ENUMS ============"),e.push("");for(let[o,r]of t)e.push(de(o,r)),e.push("");}e.push("// ============ TABLES ============"),e.push("");let i=[];for(let[o,r]of n){let{base:l,insert:s,update:u}=pe(o,r,t);i.push(o),e.push(l),e.push(""),e.push(s),e.push(""),e.push(u),e.push("");}e.push("// ============ DATABASE SCHEMA ============"),e.push(""),e.push("export interface Database {");for(let o of i){let r=N(o);e.push(` ${o}: {`),e.push(` Row: ${r};`),e.push(` Insert: ${r}Insert;`),e.push(` Update: ${r}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 fe(n){let t=X__default.default("Loading configuration...").start();try{let a=await x(n.config),e=n.connection||a?.database?.url||process.env.DATABASE_URL,i=e&&!e.includes("${"),o,r,l;if(i){t.text="Connecting to database...";let c=new ie__default.default.Client({connectionString:e});await c.connect(),t.text="Introspecting schema...",{tables:o,enums:r,foreignKeys:l}=await ce(c,n.schema),await c.end();}else {let c=F();(!c||!c.token)&&(t.fail("No database connection and not logged in"),console.log(m__default.default.yellow(`
60
+ Either:`)),console.log(m__default.default.gray(" 1. Run `vaif login` to authenticate (no DATABASE_URL needed)")),console.log(m__default.default.gray(" 2. Set DATABASE_URL in your .env file")),console.log(m__default.default.gray(" 3. Pass --connection postgresql://user:pass@host:5432/db")),process.exit(1));let f=a?.projectId||process.env.VAIF_PROJECT_ID||c.projectId;f||(t.fail("No project ID specified"),console.log(m__default.default.yellow(`
61
+ 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:r,foreignKeys:l}=await le(c.token,f);}if(o.size===0){t.warn("No tables found"),console.log(m__default.default.yellow(`
62
+ Push a migration first: vaif db push`));return}t.text=`Generating types for ${o.size} tables...`;let s=me(o,r,l),u=await re__default.default.format(s,{parser:"typescript",semi:!0,singleQuote:!1,trailingComma:"es5",printWidth:100});if(n.dryRun){t.succeed("Generated types (dry run):"),console.log(""),console.log(m__default.default.gray("\u2500".repeat(60))),console.log(u),console.log(m__default.default.gray("\u2500".repeat(60)));return}let d=b__default.default.resolve(n.output),p=b__default.default.dirname(d);y__default.default.existsSync(p)||y__default.default.mkdirSync(p,{recursive:!0}),y__default.default.writeFileSync(d,u,"utf-8"),t.succeed(`Generated types for ${o.size} tables \u2192 ${m__default.default.cyan(n.output)}`),console.log(""),console.log(m__default.default.green("Generated:")),console.log(m__default.default.gray(` Tables: ${o.size}`)),console.log(m__default.default.gray(` Enums: ${r.size}`)),console.log(""),console.log(m__default.default.gray("Import in your code:")),console.log(m__default.default.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(m__default.default.red(`
63
+ Error: ${a.message}`)),a.message.includes("ECONNREFUSED")&&console.log(m__default.default.yellow(`
64
+ Make sure your database is running and accessible.`))),process.exit(1);}}var S=[{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"}],ge={"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:`{
65
65
  "name": "my-vaif-app",
66
66
  "private": true,
67
67
  "version": "0.1.0",
@@ -171,7 +171,7 @@ export function createVaifServer() {
171
171
  });
172
172
  }
173
173
  `},{path:".env.local.example",content:`# VAIF Configuration
174
- # Get these values from https://vaif.studio/app/security/api-keys \u2192 Project Settings \u2192 API Keys
174
+ # Get these values from https://console.vaif.studio/security/api-keys \u2192 Project Settings \u2192 API Keys
175
175
 
176
176
  NEXT_PUBLIC_VAIF_API_URL=https://api.vaif.studio
177
177
  NEXT_PUBLIC_VAIF_PROJECT_ID=your-project-id
@@ -212,7 +212,7 @@ A full-stack Next.js application powered by [VAIF Studio](https://vaif.studio),
212
212
  cp .env.local.example .env.local
213
213
  \\\`\\\`\\\`
214
214
 
215
- Get your Project ID, API Key, and Secret Key from <https://vaif.studio/app/security/api-keys> under **Project Settings > API Keys**.
215
+ Get your Project ID, API Key, and Secret Key from <https://console.vaif.studio/security/api-keys> under **Project Settings > API Keys**.
216
216
 
217
217
  3. **Install and log in to the VAIF CLI**
218
218
 
@@ -686,7 +686,7 @@ interface ImportMeta {
686
686
  readonly env: ImportMetaEnv;
687
687
  }
688
688
  `},{path:".env.example",content:`# VAIF Configuration
689
- # Get these values from https://vaif.studio/app/security/api-keys
689
+ # Get these values from https://console.vaif.studio/security/api-keys
690
690
 
691
691
  VITE_VAIF_API_URL=https://api.vaif.studio
692
692
  VITE_VAIF_PROJECT_ID=your-project-id
@@ -720,7 +720,7 @@ A single-page React application built with [Vite](https://vite.dev/) and powered
720
720
  cp .env.example .env
721
721
  \\\`\\\`\\\`
722
722
 
723
- Get your Project ID and API Key from <https://vaif.studio/app/security/api-keys> under **Project Settings > API Keys**.
723
+ Get your Project ID and API Key from <https://console.vaif.studio/security/api-keys> under **Project Settings > API Keys**.
724
724
 
725
725
  3. **Install and log in to the VAIF CLI**
726
726
 
@@ -1243,7 +1243,7 @@ An iOS/macOS application powered by [VAIF Studio](https://vaif.studio), using th
1243
1243
  <string>your-anon-key</string>
1244
1244
  \\\`\\\`\\\`
1245
1245
 
1246
- Get your credentials from <https://vaif.studio/app/security/api-keys> under **Project Settings > API Keys**.
1246
+ Get your credentials from <https://console.vaif.studio/security/api-keys> under **Project Settings > API Keys**.
1247
1247
 
1248
1248
  3. **Install and log in to the VAIF CLI** (for type generation)
1249
1249
 
@@ -1372,7 +1372,7 @@ export const vaif = createExpoClient({
1372
1372
  realtime: { enabled: true },
1373
1373
  });
1374
1374
  `},{path:".env.example",content:`# VAIF Configuration
1375
- # Get these values from https://vaif.studio/app/security/api-keys \u2192 Project Settings \u2192 API Keys
1375
+ # Get these values from https://console.vaif.studio/security/api-keys \u2192 Project Settings \u2192 API Keys
1376
1376
 
1377
1377
  EXPO_PUBLIC_VAIF_PROJECT_ID=your-project-id
1378
1378
  EXPO_PUBLIC_VAIF_API_KEY=your-anon-key
@@ -1409,7 +1409,7 @@ A React Native / Expo mobile application powered by [VAIF Studio](https://vaif.s
1409
1409
  cp .env.example .env
1410
1410
  \\\`\\\`\\\`
1411
1411
 
1412
- Get your Project ID and API Key from <https://vaif.studio/app/security/api-keys> under **Project Settings > API Keys**.
1412
+ Get your Project ID and API Key from <https://console.vaif.studio/security/api-keys> under **Project Settings > API Keys**.
1413
1413
 
1414
1414
  3. **Install and log in to the VAIF CLI**
1415
1415
 
@@ -1844,7 +1844,7 @@ A Flutter application powered by [VAIF Studio](https://vaif.studio), with Dart c
1844
1844
  cp .env.example .env
1845
1845
  \\\`\\\`\\\`
1846
1846
 
1847
- Get your Project ID and API Key from <https://vaif.studio/app/security/api-keys> under **Project Settings > API Keys**.
1847
+ Get your Project ID and API Key from <https://console.vaif.studio/security/api-keys> under **Project Settings > API Keys**.
1848
1848
 
1849
1849
  3. **Install and log in to the VAIF CLI** (for schema and type generation)
1850
1850
 
@@ -2102,7 +2102,7 @@ fastapi>=0.110.0
2102
2102
  uvicorn[standard]>=0.27.0
2103
2103
  python-dotenv>=1.0.0
2104
2104
  `},{path:".env.example",content:`# VAIF Configuration
2105
- # Get these values from https://vaif.studio/app/security/api-keys \u2192 Project Settings \u2192 API Keys
2105
+ # Get these values from https://console.vaif.studio/security/api-keys \u2192 Project Settings \u2192 API Keys
2106
2106
 
2107
2107
  VAIF_PROJECT_ID=your-project-id
2108
2108
  VAIF_API_KEY=your-anon-key
@@ -2144,7 +2144,7 @@ A FastAPI backend application powered by [VAIF Studio](https://vaif.studio), wit
2144
2144
  cp .env.example .env
2145
2145
  \\\`\\\`\\\`
2146
2146
 
2147
- Get your Project ID, API Key, and Secret Key from <https://vaif.studio/app/security/api-keys> under **Project Settings > API Keys**.
2147
+ Get your Project ID, API Key, and Secret Key from <https://console.vaif.studio/security/api-keys> under **Project Settings > API Keys**.
2148
2148
 
2149
2149
  4. **Install and log in to the VAIF CLI**
2150
2150
 
@@ -2467,7 +2467,7 @@ A Go backend API powered by [VAIF Studio](https://vaif.studio), with HTTP handle
2467
2467
  cp .env.example .env
2468
2468
  \\\`\\\`\\\`
2469
2469
 
2470
- Get your Project ID, API Key, and Secret Key from <https://vaif.studio/app/security/api-keys> under **Project Settings > API Keys**.
2470
+ Get your Project ID, API Key, and Secret Key from <https://console.vaif.studio/security/api-keys> under **Project Settings > API Keys**.
2471
2471
 
2472
2472
  3. **Install and log in to the VAIF CLI**
2473
2473
 
@@ -2742,7 +2742,7 @@ export async function deleteTodo(id: string): Promise<void> {
2742
2742
  if (error) throw error;
2743
2743
  }
2744
2744
  `},{path:".env.example",content:`# VAIF Configuration
2745
- # Get these values from https://vaif.studio/app/security/api-keys
2745
+ # Get these values from https://console.vaif.studio/security/api-keys
2746
2746
 
2747
2747
  VITE_VAIF_API_URL=https://api.vaif.studio
2748
2748
  VITE_VAIF_PROJECT_ID=your-project-id
@@ -2771,7 +2771,7 @@ A simple React todo application for learning [VAIF Studio](https://vaif.studio)
2771
2771
  cp .env.example .env
2772
2772
  \\\`\\\`\\\`
2773
2773
 
2774
- Get your Project ID and API Key from <https://vaif.studio/app/security/api-keys> under **Project Settings > API Keys**.
2774
+ Get your Project ID and API Key from <https://console.vaif.studio/security/api-keys> under **Project Settings > API Keys**.
2775
2775
 
2776
2776
  3. **Install and log in to the VAIF CLI**
2777
2777
 
@@ -3022,7 +3022,7 @@ export function useRealtimeMessages({
3022
3022
  return { messages, isLoading, error, refresh };
3023
3023
  }
3024
3024
  `},{path:".env.example",content:`# VAIF Configuration
3025
- # Get these values from https://vaif.studio/app/security/api-keys
3025
+ # Get these values from https://console.vaif.studio/security/api-keys
3026
3026
 
3027
3027
  VITE_VAIF_API_URL=https://api.vaif.studio
3028
3028
  VITE_VAIF_PROJECT_ID=your-project-id
@@ -3051,7 +3051,7 @@ A React chat application with live messaging powered by [VAIF Studio](https://va
3051
3051
  cp .env.example .env
3052
3052
  \\\`\\\`\\\`
3053
3053
 
3054
- Get your Project ID and API Key from <https://vaif.studio/app/security/api-keys> under **Project Settings > API Keys**.
3054
+ Get your Project ID and API Key from <https://console.vaif.studio/security/api-keys> under **Project Settings > API Keys**.
3055
3055
 
3056
3056
  3. **Install and log in to the VAIF CLI**
3057
3057
 
@@ -3338,7 +3338,7 @@ export async function requireTeamRole(
3338
3338
  return member as TeamMember;
3339
3339
  }
3340
3340
  `},{path:".env.example",content:`# VAIF Configuration
3341
- # Get these values from https://vaif.studio/app/security/api-keys \u2192 Project Settings \u2192 API Keys
3341
+ # Get these values from https://console.vaif.studio/security/api-keys \u2192 Project Settings \u2192 API Keys
3342
3342
 
3343
3343
  NEXT_PUBLIC_VAIF_API_URL=https://api.vaif.studio
3344
3344
  NEXT_PUBLIC_VAIF_PROJECT_ID=your-project-id
@@ -3371,7 +3371,7 @@ A full SaaS starter kit powered by [VAIF Studio](https://vaif.studio) with authe
3371
3371
  cp .env.example .env.local
3372
3372
  \\\`\\\`\\\`
3373
3373
 
3374
- Get your Project ID, API Key, and Secret Key from <https://vaif.studio/app/security/api-keys> under **Project Settings > API Keys**.
3374
+ Get your Project ID, API Key, and Secret Key from <https://console.vaif.studio/security/api-keys> under **Project Settings > API Keys**.
3375
3375
 
3376
3376
  3. **Install and log in to the VAIF CLI**
3377
3377
 
@@ -3616,7 +3616,7 @@ function getContentType(fileName: string): string {
3616
3616
  return mimeTypes[ext ?? ""] ?? "application/octet-stream";
3617
3617
  }
3618
3618
  `},{path:".env.example",content:`# VAIF Configuration
3619
- # Get these values from https://vaif.studio/app/security/api-keys \u2192 Project Settings \u2192 API Keys
3619
+ # Get these values from https://console.vaif.studio/security/api-keys \u2192 Project Settings \u2192 API Keys
3620
3620
 
3621
3621
  NEXT_PUBLIC_VAIF_API_URL=https://api.vaif.studio
3622
3622
  NEXT_PUBLIC_VAIF_PROJECT_ID=your-project-id
@@ -3649,7 +3649,7 @@ An API-first e-commerce setup powered by [VAIF Studio](https://vaif.studio) with
3649
3649
  cp .env.example .env.local
3650
3650
  \\\`\\\`\\\`
3651
3651
 
3652
- Get your Project ID, API Key, and Secret Key from <https://vaif.studio/app/security/api-keys> under **Project Settings > API Keys**.
3652
+ Get your Project ID, API Key, and Secret Key from <https://console.vaif.studio/security/api-keys> under **Project Settings > API Keys**.
3653
3653
 
3654
3654
  3. **Install and log in to the VAIF CLI**
3655
3655
 
@@ -3765,25 +3765,1365 @@ export const posts = pgTable("posts", {
3765
3765
 
3766
3766
  return Response.json({ message: \`Hello, \${name}!\` });
3767
3767
  }
3768
- `}]},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"]}};async function le(n){if(!process.stdin.isTTY||!process.stdout.isTTY)return n;let a=new Set(n.map(e=>_.findIndex(i=>i.name===e)).filter(e=>e>=0)),t=0;return new Promise(e=>{let i=B__default.default.createInterface({input:process.stdin,output:process.stdout});B__default.default.emitKeypressEvents(process.stdin,i),process.stdin.setRawMode&&process.stdin.setRawMode(true);function o(){let s=_.length+2;process.stdout.write(`\x1B[${s}A`),c();}function c(){console.log(d__default.default.bold(`
3769
- ? Which VAIF features do you want to include?`)),_.forEach((s,r)=>{let l=a.has(r)?d__default.default.green("[x]"):"[ ]",p=r===t?d__default.default.cyan("> "):" ";console.log(`${p}${l} ${s.label} ${d__default.default.gray(`(${s.description})`)}`);}),console.log(d__default.default.gray(" (up/down to move, space to toggle, enter to confirm)"));}c(),process.stdin.on("keypress",(s,r)=>{if(r.name==="up"&&t>0)t--,o();else if(r.name==="down"&&t<_.length-1)t++,o();else if(r.name==="space")a.has(t)?a.delete(t):a.add(t),o();else if(r.name==="return"){process.stdin.setRawMode&&process.stdin.setRawMode(false),i.close();let l=[...a].sort().map(p=>_[p].name);e(l.length>0?l:n);}else r.name==="c"&&r.ctrl&&(process.stdin.setRawMode&&process.stdin.setRawMode(false),i.close(),process.exit(0));});})}async function C(n,a={}){let t=se[n];t||(console.log(d__default.default.red(`
3770
- Unknown template: ${n}`)),console.log(d__default.default.yellow(`Run 'vaif templates' to see available templates.
3771
- `)),process.exit(1));let e;a.features&&a.features.length>0?e=a.features.filter(u=>_.some(f=>f.name===u)):a.addOnly?(console.log(d__default.default.red(`
3772
- No features specified.`)),console.log(d__default.default.yellow("Usage: vaif init --template <name> --add-features <features>")),console.log(d__default.default.gray("Available features: auth, database, realtime, storage, functions")),process.exit(1)):t.featureFiles&&Object.keys(t.featureFiles).length>0?e=await le(t.defaultFeatures??["database","auth"]):e=t.defaultFeatures??[],a.addOnly?(console.log(""),console.log(d__default.default.bold(`Adding features to ${d__default.default.cyan(t.name)} project...`)),console.log(d__default.default.gray(` Features: ${e.join(", ")}`)),console.log("")):(console.log(""),console.log(d__default.default.bold(`Scaffolding ${d__default.default.cyan(t.name)} template...`)),e.length>0&&console.log(d__default.default.gray(` Features: ${e.join(", ")}`)),console.log(""));let i=a.addOnly?[]:[...t.files],o=new Set,c=[];if(t.featureFiles)for(let u of e){let f=t.featureFiles[u];if(f)for(let h of f)o.add(h.path),c.push(h);}let s=i.filter(u=>!o.has(u.path)).concat(c),r=0,l=0;for(let u of s){let f=R__default.default.resolve(u.path),h=R__default.default.dirname(f);if(b__default.default.existsSync(h)||b__default.default.mkdirSync(h,{recursive:true}),u.path==="package.json"&&b__default.default.existsSync(f)&&!a.force)try{let v=JSON.parse(b__default.default.readFileSync(f,"utf-8")),I=JSON.parse(u.content),T=U=>{if(!U)return {};let D={};for(let[q,x]of Object.entries(U))!x.startsWith("workspace:")&&!x.startsWith("link:")&&!x.startsWith("file:")&&(D[q]=x);return D};v.dependencies={...T(v.dependencies),...I.dependencies||{}},v.devDependencies={...T(v.devDependencies),...I.devDependencies||{}},I.scripts&&(v.scripts={...v.scripts||{},...I.scripts}),b__default.default.writeFileSync(f,JSON.stringify(v,null,2)+`
3773
- `,"utf-8"),console.log(d__default.default.green(` merge ${u.path} (added dependencies)`)),r++;continue}catch{}if(b__default.default.existsSync(f)&&!a.force){console.log(d__default.default.yellow(` skip ${u.path} (already exists)`)),l++;continue}b__default.default.writeFileSync(f,u.content,"utf-8"),console.log(d__default.default.green(` create ${u.path}`)),r++;}console.log(""),r>0&&console.log(d__default.default.green(`Created ${r} file${r!==1?"s":""}.`)),l>0&&console.log(d__default.default.yellow(`Skipped ${l} file${l!==1?"s":""} (use --force to overwrite).`));let p={auth:{"@vaiftech/auth":"^1.0.0"},database:{},realtime:{},storage:{},functions:{}},m=R__default.default.resolve("package.json");if(b__default.default.existsSync(m)&&e.length>0)try{let u=JSON.parse(b__default.default.readFileSync(m,"utf-8")),f=!1;for(let h of e){let v=p[h];if(v)for(let[I,T]of Object.entries(v))u.dependencies?.[I]||(u.dependencies=u.dependencies||{},u.dependencies[I]=T,f=!0);}f&&b__default.default.writeFileSync(m,JSON.stringify(u,null,2)+`
3774
- `,"utf-8");}catch{}(t.dependencies?.length||t.devDependencies?.length)&&(console.log(""),console.log(d__default.default.bold("Install dependencies:")),t.dependencies?.length&&console.log(d__default.default.cyan(` npm install ${t.dependencies.join(" ")}`)),t.devDependencies?.length&&console.log(d__default.default.cyan(` npm install -D ${t.devDependencies.join(" ")}`))),console.log(""),console.log(d__default.default.bold.green("Project scaffolded successfully!")),console.log(""),console.log(d__default.default.bold(" Next steps:")),t.postInstructions.forEach(u=>{console.log(d__default.default.gray(` ${u}`));}),console.log(""),console.log(d__default.default.gray(" Get your project credentials at https://vaif.studio/app/security/api-keys")),console.log("");}var ue={$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 pe(n){if(n.addFeatures){n.template||(console.log(d__default.default.red(`
3775
- --add-features requires --template to know which template to use.`)),console.log(d__default.default.gray("Example: vaif init --template react-spa --add-features functions,storage")),process.exit(1));let e=n.addFeatures.split(",").map(i=>i.trim());await C(n.template,{force:n.force,features:e,addOnly:true});return}let a=X__default.default("Initializing VAIF configuration...").start(),t=R__default.default.resolve("vaif.config.json");b__default.default.existsSync(t)&&!n.force&&(a.fail("vaif.config.json already exists"),console.log(d__default.default.yellow(`
3776
- Use --force to overwrite existing configuration.`)),process.exit(1));try{if(b__default.default.writeFileSync(t,JSON.stringify(ue,null,2),"utf-8"),a.succeed("Created vaif.config.json"),n.template){let e=n.features?n.features.split(",").map(i=>i.trim()):void 0;await C(n.template,{force:n.force,features:e});}else {let e=R__default.default.resolve(".env.example");if(b__default.default.existsSync(e)||(b__default.default.writeFileSync(e,`# VAIF Configuration
3768
+ `}]},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"]}};async function he(n){if(!process.stdin.isTTY||!process.stdout.isTTY)return n;let t=new Set(n.map(e=>S.findIndex(i=>i.name===e)).filter(e=>e>=0)),a=0;return new Promise(e=>{let i=G__default.default.createInterface({input:process.stdin,output:process.stdout});G__default.default.emitKeypressEvents(process.stdin,i),process.stdin.setRawMode&&process.stdin.setRawMode(true);function o(){let l=S.length+2;process.stdout.write(`\x1B[${l}A`),r();}function r(){console.log(m__default.default.bold(`
3769
+ ? Which VAIF features do you want to include?`)),S.forEach((l,s)=>{let u=t.has(s)?m__default.default.green("[x]"):"[ ]",d=s===a?m__default.default.cyan("> "):" ";console.log(`${d}${u} ${l.label} ${m__default.default.gray(`(${l.description})`)}`);}),console.log(m__default.default.gray(" (up/down to move, space to toggle, enter to confirm)"));}r(),process.stdin.on("keypress",(l,s)=>{if(s.name==="up"&&a>0)a--,o();else if(s.name==="down"&&a<S.length-1)a++,o();else if(s.name==="space")t.has(a)?t.delete(a):t.add(a),o();else if(s.name==="return"){process.stdin.setRawMode&&process.stdin.setRawMode(false),i.close();let u=[...t].sort().map(d=>S[d].name);e(u.length>0?u:n);}else s.name==="c"&&s.ctrl&&(process.stdin.setRawMode&&process.stdin.setRawMode(false),i.close(),process.exit(0));});})}async function k(n,t={}){let a=ge[n];a||(console.log(m__default.default.red(`
3770
+ Unknown template: ${n}`)),console.log(m__default.default.yellow(`Run 'vaif templates' to see available templates.
3771
+ `)),process.exit(1));let e;t.features&&t.features.length>0?e=t.features.filter(c=>S.some(f=>f.name===c)):t.addOnly?(console.log(m__default.default.red(`
3772
+ No features specified.`)),console.log(m__default.default.yellow("Usage: vaif init --template <name> --add-features <features>")),console.log(m__default.default.gray("Available features: auth, database, realtime, storage, functions")),process.exit(1)):a.featureFiles&&Object.keys(a.featureFiles).length>0?e=await he(a.defaultFeatures??["database","auth"]):e=a.defaultFeatures??[],t.addOnly?(console.log(""),console.log(m__default.default.bold(`Adding features to ${m__default.default.cyan(a.name)} project...`)),console.log(m__default.default.gray(` Features: ${e.join(", ")}`)),console.log("")):(console.log(""),console.log(m__default.default.bold(`Scaffolding ${m__default.default.cyan(a.name)} template...`)),e.length>0&&console.log(m__default.default.gray(` Features: ${e.join(", ")}`)),console.log(""));let i=t.addOnly?[]:[...a.files],o=new Set,r=[];if(a.featureFiles)for(let c of e){let f=a.featureFiles[c];if(f)for(let I of f)o.add(I.path),r.push(I);}let l=i.filter(c=>!o.has(c.path)).concat(r),s=0,u=0;for(let c of l){let f=b__default.default.resolve(c.path),I=b__default.default.dirname(f);if(y__default.default.existsSync(I)||y__default.default.mkdirSync(I,{recursive:true}),c.path==="package.json"&&y__default.default.existsSync(f)&&!t.force)try{let _=JSON.parse(y__default.default.readFileSync(f,"utf-8")),A=JSON.parse(c.content),C=M=>{if(!M)return {};let $={};for(let[Z,P]of Object.entries(M))!P.startsWith("workspace:")&&!P.startsWith("link:")&&!P.startsWith("file:")&&($[Z]=P);return $};_.dependencies={...C(_.dependencies),...A.dependencies||{}},_.devDependencies={...C(_.devDependencies),...A.devDependencies||{}},A.scripts&&(_.scripts={..._.scripts||{},...A.scripts}),y__default.default.writeFileSync(f,JSON.stringify(_,null,2)+`
3773
+ `,"utf-8"),console.log(m__default.default.green(` merge ${c.path} (added dependencies)`)),s++;continue}catch{}if(y__default.default.existsSync(f)&&!t.force){console.log(m__default.default.yellow(` skip ${c.path} (already exists)`)),u++;continue}y__default.default.writeFileSync(f,c.content,"utf-8"),console.log(m__default.default.green(` create ${c.path}`)),s++;}console.log(""),s>0&&console.log(m__default.default.green(`Created ${s} file${s!==1?"s":""}.`)),u>0&&console.log(m__default.default.yellow(`Skipped ${u} file${u!==1?"s":""} (use --force to overwrite).`));let d={auth:{"@vaiftech/auth":"^1.0.0"},database:{},realtime:{},storage:{},functions:{}},p=b__default.default.resolve("package.json");if(y__default.default.existsSync(p)&&e.length>0)try{let c=JSON.parse(y__default.default.readFileSync(p,"utf-8")),f=!1;for(let I of e){let _=d[I];if(_)for(let[A,C]of Object.entries(_))c.dependencies?.[A]||(c.dependencies=c.dependencies||{},c.dependencies[A]=C,f=!0);}f&&y__default.default.writeFileSync(p,JSON.stringify(c,null,2)+`
3774
+ `,"utf-8");}catch{}(a.dependencies?.length||a.devDependencies?.length)&&(console.log(""),console.log(m__default.default.bold("Install dependencies:")),a.dependencies?.length&&console.log(m__default.default.cyan(` npm install ${a.dependencies.join(" ")}`)),a.devDependencies?.length&&console.log(m__default.default.cyan(` npm install -D ${a.devDependencies.join(" ")}`))),console.log(""),console.log(m__default.default.bold.green("Project scaffolded successfully!")),console.log(""),console.log(m__default.default.bold(" Next steps:")),a.postInstructions.forEach(c=>{console.log(m__default.default.gray(` ${c}`));}),console.log(""),console.log(m__default.default.gray(" Get your project credentials at https://console.vaif.studio/security/api-keys")),console.log("");}var w=process.env.VAIF_API_URL||"https://api.vaif.studio";function ve(n){let t=G__default.default.createInterface({input:process.stdin,output:process.stdout});return new Promise(a=>{t.question(n,e=>{t.close(),a(e.trim());});})}function be(n,t,a){return n.projectId||t?.projectId||process.env.VAIF_PROJECT_ID||a.projectId||null}async function Ie(n){let t=X__default.default("Fetching your projects...").start();try{let a=await fetch(`${w}/projects`,{headers:{Authorization:`Bearer ${n}`}});if(!a.ok)return t.fail("Could not fetch projects"),null;let e=await a.json();if(!e||e.length===0)return t.fail("No projects found. Create a project at https://vaif.studio first."),null;if(e.length===1)return t.succeed(`Found project: ${m__default.default.cyan(e[0].name)} (${e[0].id})`),e[0].id;t.succeed(`Found ${e.length} projects
3775
+ `);for(let r=0;r<e.length;r++)console.log(m__default.default.gray(` ${r+1}.`)+` ${m__default.default.white(e[r].name)} ${m__default.default.gray(`(${e[r].id})`)}`);console.log("");let i=await ve(m__default.default.cyan(` Select a project [1-${e.length}]: `)),o=parseInt(i,10)-1;return isNaN(o)||o<0||o>=e.length?(console.log(m__default.default.red(`
3776
+ Invalid selection.`)),null):e[o].id}catch{return t.fail("Could not fetch projects"),null}}function _e(n){if(!n||n.length===0)return "*No tables found. Create tables in the VAIF Studio dashboard.*";let t="";for(let a of n){t+=`### \`${a.name}\`
3777
+
3778
+ `,t+=`| Column | Type | Nullable | Default | Constraints |
3779
+ `,t+=`|--------|------|----------|---------|-------------|
3780
+ `;for(let e of a.columns){let i=[];e.isPrimaryKey&&i.push("PK"),e.nullable||i.push("NOT NULL");let o=a.foreignKeys?.find(r=>r.column===e.name);o&&i.push(`FK \u2192 ${o.foreignTable}.${o.foreignColumn}`),t+=`| ${e.name} | ${e.type} | ${e.nullable?"yes":"no"} | ${e.defaultValue||"-"} | ${i.join(", ")||"-"} |
3781
+ `;}t+=`
3782
+ `;}return t}function Ee(n,t,a){let e=n.slice(0,3);if(e.length===0)return "";let i="";for(let o of e){let r=o.columns.filter(s=>!s.isPrimaryKey&&s.name!=="created_at"&&s.name!=="updated_at"),l=r.slice(0,3).map(s=>` ${s.name}: ${Ae(s)}`).join(`,
3783
+ `);i+=`### \`${o.name}\`
3784
+
3785
+ `,i+="```typescript\n",i+=`// Select all
3786
+ const ${o.name} = await vaif.from("${o.name}").select();
3787
+
3788
+ `,i+=`// Select with filter
3789
+ const filtered = await vaif.from("${o.name}").select().eq("${r[0]?.name||"id"}", value);
3790
+
3791
+ `,i+=`// Insert
3792
+ const created = await vaif.from("${o.name}").insert({
3793
+ ${l}
3794
+ });
3795
+
3796
+ `,i+=`// Update
3797
+ await vaif.from("${o.name}").update(recordId, {
3798
+ ${r[0]?.name||"name"}: newValue
3799
+ });
3800
+
3801
+ `,i+=`// Delete
3802
+ await vaif.from("${o.name}").delete(recordId);
3803
+ `,i+="```\n\n",o===e[0]&&(i+=`#### Direct REST API (fetch)
3804
+
3805
+ `,i+="```typescript\n",i+=`// GET all rows (returns { data: [...], count: N })
3806
+ `,i+=`const res = await fetch("${t}/generated/${o.name}", {
3807
+ `,i+=` headers: { "x-vaif-key": "${a}" },
3808
+ `,i+=`});
3809
+ `,i+=`const { data } = await res.json();
3810
+
3811
+ `,i+=`// GET single row (returns { data: {...} })
3812
+ `,i+=`const res2 = await fetch("${t}/generated/${o.name}/\${id}", {
3813
+ `,i+=` headers: { "x-vaif-key": "${a}" },
3814
+ `,i+=`});
3815
+ `,i+=`const { data: record } = await res2.json();
3816
+ `,i+="```\n\n");}return i}function Ae(n){let t=n.type.toLowerCase();return t.includes("uuid")?'"crypto.randomUUID()"':t.includes("int")||t.includes("serial")?"42":t.includes("bool")?"true":t.includes("timestamp")||t.includes("date")?'"new Date().toISOString()"':t.includes("json")?"{}":t.includes("float")||t.includes("numeric")||t.includes("decimal")||t.includes("double")?"3.14":`"example_${n.name}"`}function Te(n){let{projectId:t,apiKey:a,apiUrl:e,projectName:i,schema:o}=n,r=_e(o.tables),l=Ee(o.tables,e,a);return `# VAIF Studio Backend
3817
+
3818
+ This project uses **VAIF Studio** as its backend. Project: **${i}** (\`${t}\`).
3819
+
3820
+ ## SDK Setup
3821
+
3822
+ \`\`\`bash
3823
+ npm install @vaiftech/client
3824
+ \`\`\`
3825
+
3826
+ \`\`\`typescript
3827
+ import { createVaifClient } from "@vaiftech/client";
3828
+
3829
+ const vaif = createVaifClient({
3830
+ baseUrl: "${e}",
3831
+ projectId: "${t}",
3832
+ apiKey: "${a}",
3833
+ });
3834
+ \`\`\`
3835
+
3836
+ ## Database Schema
3837
+
3838
+ ${r}
3839
+
3840
+ ## CRUD Examples
3841
+
3842
+ ${l}
3843
+
3844
+ ## Authentication (End-User Auth)
3845
+
3846
+ VAIF provides **project-scoped** authentication for your app's end-users. All auth routes are scoped to your project ID.
3847
+
3848
+ ### API Routes
3849
+
3850
+ | Route | Method | Auth | Description |
3851
+ |-------|--------|------|-------------|
3852
+ | \`/projects/${t}/auth/signup\` | POST | None (public) | Register a new user |
3853
+ | \`/projects/${t}/auth/login\` | POST | None (public) | Login with email/password |
3854
+ | \`/projects/${t}/auth/refresh\` | POST | Cookie | Refresh access token |
3855
+
3856
+ ### Signup
3857
+
3858
+ \`\`\`typescript
3859
+ const res = await fetch("${e}/projects/${t}/auth/signup", {
3860
+ method: "POST",
3861
+ headers: { "Content-Type": "application/json" },
3862
+ body: JSON.stringify({
3863
+ email: "user@example.com",
3864
+ password: "securePassword123",
3865
+ metadata: { displayName: "Jane Doe" }, // optional
3866
+ }),
3867
+ });
3868
+ const { accessToken, expiresIn, user } = await res.json();
3869
+ // accessToken: JWT with { sub: userId, email, projectId, type: "project_user" }
3870
+ \`\`\`
3871
+
3872
+ ### Login
3873
+
3874
+ \`\`\`typescript
3875
+ const res = await fetch("${e}/projects/${t}/auth/login", {
3876
+ method: "POST",
3877
+ headers: { "Content-Type": "application/json" },
3878
+ body: JSON.stringify({
3879
+ email: "user@example.com",
3880
+ password: "securePassword123",
3881
+ }),
3882
+ });
3883
+ const { accessToken, expiresIn, user } = await res.json();
3884
+ \`\`\`
3885
+
3886
+ ### Token Refresh
3887
+
3888
+ \`\`\`typescript
3889
+ // Refresh token is stored as httpOnly cookie (project_refresh_token)
3890
+ // and sent automatically with same-origin requests
3891
+ const res = await fetch("${e}/projects/${t}/auth/refresh", {
3892
+ method: "POST",
3893
+ credentials: "include", // sends the httpOnly cookie
3894
+ });
3895
+ const { accessToken, expiresIn, user } = await res.json();
3896
+ \`\`\`
3897
+
3898
+ ### Using Auth in Your App
3899
+
3900
+ \`\`\`typescript
3901
+ // Store the access token and use it for authenticated requests
3902
+ const headers = {
3903
+ Authorization: \\\`Bearer \\\${accessToken}\\\`,
3904
+ "x-vaif-key": "${a}",
3905
+ };
3906
+
3907
+ // The JWT contains: { sub: userId, email, projectId, type: "project_user" }
3908
+ // Use this with RLS to scope data to the current user
3909
+ \`\`\`
3910
+
3911
+ > **Important**: Auth routes are at \`/projects/{projectId}/auth/*\`, NOT \`/auth/*\`. The \`/auth/*\` routes are for VAIF Studio platform accounts, not your app's end-users.
3912
+
3913
+ ## Storage
3914
+
3915
+ \`\`\`typescript
3916
+ // Upload a file
3917
+ const { url } = await vaif.storage.upload("avatars", file, {
3918
+ contentType: "image/png",
3919
+ });
3920
+
3921
+ // Download a file
3922
+ const blob = await vaif.storage.download("avatars", "photo.png");
3923
+
3924
+ // Create a signed URL (expiring)
3925
+ const { signedUrl } = await vaif.storage.createSignedUrl("avatars", "photo.png", {
3926
+ expiresIn: 3600,
3927
+ });
3928
+
3929
+ // List files in a bucket
3930
+ const files = await vaif.storage.list("avatars", { limit: 100 });
3931
+ \`\`\`
3932
+
3933
+ ## Functions
3934
+
3935
+ \`\`\`typescript
3936
+ // Invoke a serverless function
3937
+ const result = await vaif.functions.invoke("send_welcome_email", {
3938
+ body: { userId: "user_123", template: "welcome" },
3939
+ });
3940
+ \`\`\`
3941
+
3942
+ > **Function naming**: Names must be alphanumeric and underscores only (\`^[a-zA-Z0-9_]+$\`). Use \`send_email\` not \`send-email\`.
3943
+
3944
+ ## Realtime
3945
+
3946
+ \`\`\`typescript
3947
+ // Subscribe to a channel
3948
+ const channel = vaif.realtime.channel("my-channel");
3949
+
3950
+ // Listen for postgres changes
3951
+ channel.on("postgres_changes", {
3952
+ event: "INSERT",
3953
+ schema: "public",
3954
+ table: "messages",
3955
+ }, (payload) => {
3956
+ console.log("New message:", payload.new);
3957
+ });
3958
+
3959
+ // Listen for UPDATE events
3960
+ channel.on("postgres_changes", {
3961
+ event: "UPDATE",
3962
+ schema: "public",
3963
+ table: "messages",
3964
+ }, (payload) => {
3965
+ console.log("Updated:", payload.new, "was:", payload.old);
3966
+ });
3967
+
3968
+ // Listen for DELETE events
3969
+ channel.on("postgres_changes", {
3970
+ event: "DELETE",
3971
+ schema: "public",
3972
+ table: "messages",
3973
+ }, (payload) => {
3974
+ console.log("Deleted:", payload.old);
3975
+ });
3976
+
3977
+ // Subscribe to start receiving events
3978
+ channel.subscribe();
3979
+
3980
+ // Unsubscribe when done
3981
+ channel.unsubscribe();
3982
+ \`\`\`
3983
+
3984
+ ## Row-Level Security (RLS)
3985
+
3986
+ Filter data per-user by sending the \`X-VAIF-RLS\` header with your requests. The header format is \`field:value\`, comma-separated for multiple fields.
3987
+
3988
+ \`\`\`typescript
3989
+ // SDK: pass RLS context to scope queries to the current user
3990
+ const posts = await vaif.from("posts").select({
3991
+ headers: { "x-vaif-rls": \`user_id:\${currentUser.id}\` },
3992
+ });
3993
+
3994
+ // Multiple RLS fields (e.g., multi-tenant + user scoping)
3995
+ const data = await vaif.from("documents").select({
3996
+ headers: { "x-vaif-rls": \`org_id:\${orgId},user_id:\${userId}\` },
3997
+ });
3998
+ \`\`\`
3999
+
4000
+ **How it works:**
4001
+ - On **SELECT / UPDATE / DELETE**: RLS fields are added as WHERE conditions (e.g., \`WHERE user_id = $1\`)
4002
+ - On **INSERT**: RLS fields are auto-populated into the record if not already provided
4003
+ - This enables multi-tenant data isolation without database-level RLS policies
4004
+
4005
+ ## Realtime Setup
4006
+
4007
+ Enable realtime on your tables, then subscribe to live changes:
4008
+
4009
+ \`\`\`typescript
4010
+ // 1. Enable realtime on tables via API
4011
+ await fetch("${e}/realtime/install", {
4012
+ method: "POST",
4013
+ headers: {
4014
+ Authorization: \`Bearer \${token}\`,
4015
+ "Content-Type": "application/json",
4016
+ },
4017
+ body: JSON.stringify({
4018
+ projectId: "${t}",
4019
+ tables: ["messages", "notifications"],
4020
+ }),
4021
+ });
4022
+
4023
+ // 2. Subscribe to changes via SDK
4024
+ const channel = vaif.realtime.channel("chat-room");
4025
+
4026
+ channel.on("postgres_changes", {
4027
+ event: "*", // INSERT, UPDATE, DELETE, or * for all
4028
+ schema: "public",
4029
+ table: "messages",
4030
+ }, (payload) => {
4031
+ console.log("Change:", payload.eventType, payload.new);
4032
+ });
4033
+
4034
+ channel.subscribe();
4035
+
4036
+ // 3. Presence: track who's online
4037
+ channel.on("presence", { event: "sync" }, () => {
4038
+ const state = channel.presenceState();
4039
+ console.log("Online users:", Object.keys(state));
4040
+ });
4041
+ channel.track({ user_id: currentUser.id, status: "online" });
4042
+
4043
+ // 4. Broadcast: send ephemeral messages (typing indicators, cursors)
4044
+ channel.send({
4045
+ type: "broadcast",
4046
+ event: "typing",
4047
+ payload: { userId: currentUser.id },
4048
+ });
4049
+
4050
+ // Cleanup
4051
+ channel.unsubscribe();
4052
+ \`\`\`
4053
+
4054
+ ## Storage Policies
4055
+
4056
+ Control who can access storage buckets with RLS-style policies:
4057
+
4058
+ \`\`\`typescript
4059
+ // Create a policy: only the uploader can read their own files
4060
+ await fetch("${e}/storage/buckets/\${bucketId}/policies", {
4061
+ method: "POST",
4062
+ headers: {
4063
+ Authorization: \`Bearer \${token}\`,
4064
+ "Content-Type": "application/json",
4065
+ },
4066
+ body: JSON.stringify({
4067
+ name: "owner_read",
4068
+ operation: "SELECT", // SELECT | INSERT | UPDATE | DELETE | ALL
4069
+ definition: "auth.uid() = owner_id",
4070
+ }),
4071
+ });
4072
+
4073
+ // Create a policy: authenticated users can upload
4074
+ await fetch("${e}/storage/buckets/\${bucketId}/policies", {
4075
+ method: "POST",
4076
+ headers: {
4077
+ Authorization: \`Bearer \${token}\`,
4078
+ "Content-Type": "application/json",
4079
+ },
4080
+ body: JSON.stringify({
4081
+ name: "auth_insert",
4082
+ operation: "INSERT",
4083
+ definition: "auth.uid() IS NOT NULL",
4084
+ }),
4085
+ });
4086
+ \`\`\`
4087
+
4088
+ ## Edge Function Deployment
4089
+
4090
+ Create and deploy serverless functions:
4091
+
4092
+ \`\`\`typescript
4093
+ // 1. Create a function
4094
+ const fn = await fetch("${e}/functions", {
4095
+ method: "POST",
4096
+ headers: {
4097
+ Authorization: \`Bearer \${token}\`,
4098
+ "Content-Type": "application/json",
4099
+ },
4100
+ body: JSON.stringify({
4101
+ projectId: "${t}",
4102
+ name: "send_welcome_email",
4103
+ runtime: "nodejs20", // nodejs20 (default)
4104
+ entrypoint: "index.ts", // default
4105
+ timeoutMs: 10000, // 1000\u201330000ms, default 10000
4106
+ }),
4107
+ });
4108
+
4109
+ // 2. Deploy source code
4110
+ await fetch(\`${e}/functions/\${fn.id}/source\`, {
4111
+ method: "PUT",
4112
+ headers: {
4113
+ Authorization: \`Bearer \${token}\`,
4114
+ "Content-Type": "application/json",
4115
+ },
4116
+ body: JSON.stringify({
4117
+ sourceCode: \`
4118
+ export default async function handler(req, ctx) {
4119
+ const { userId } = req.body;
4120
+ // ctx.secrets contains your encrypted secrets
4121
+ const apiKey = ctx.secrets.SENDGRID_KEY;
4122
+ return { status: "sent", userId };
4123
+ }
4124
+ \`,
4125
+ }),
4126
+ });
4127
+
4128
+ // 3. Invoke the function
4129
+ const result = await vaif.functions.invoke("send_welcome_email", {
4130
+ body: { userId: "user_123" },
4131
+ });
4132
+ \`\`\`
4133
+
4134
+ ### Authenticated Context in Functions
4135
+
4136
+ Access the caller's verified identity via \\\`vaif.auth\\\`:
4137
+
4138
+ \`\`\`typescript
4139
+ export default async function handler(req) {
4140
+ const auth = vaif.auth;
4141
+ // auth.type = 'user' | 'api_key' | 'function'
4142
+ // auth.userId \u2014 User ID (for user/function types)
4143
+ // auth.email \u2014 User email (for user type)
4144
+ // auth.projectId \u2014 Always present
4145
+ // auth.scopes \u2014 API key scopes (for api_key type)
4146
+
4147
+ if (!auth || auth.type !== 'user') {
4148
+ return { statusCode: 401, body: { error: 'Unauthorized' } };
4149
+ }
4150
+
4151
+ return { body: { message: "Hello " + auth.email } };
4152
+ }
4153
+ \`\`\`
4154
+
4155
+ ### Function-to-Function Invocation
4156
+
4157
+ Call other functions from within a handler:
4158
+
4159
+ \`\`\`typescript
4160
+ export default async function handler(req) {
4161
+ const result = await vaif.invoke("send_email", {
4162
+ to: "user@example.com",
4163
+ subject: "Hello",
4164
+ });
4165
+ return { statusCode: 200, body: result };
4166
+ }
4167
+ \`\`\`
4168
+
4169
+ ### Database Triggers
4170
+
4171
+ Fire functions automatically on insert/update/delete events. Configure triggers via the API:
4172
+
4173
+ \`\`\`
4174
+ POST /functions/\\\${functionId}/triggers
4175
+ { "event": "db.insert", "tableName": "orders", "enabled": true }
4176
+ \`\`\`
4177
+
4178
+ ## API Key Management
4179
+
4180
+ API keys are project-scoped and used for data-plane authentication (CRUD, storage, functions).
4181
+
4182
+ \`\`\`typescript
4183
+ // Create a new API key
4184
+ const { key } = await fetch("${e}/projects/${t}/api-keys", {
4185
+ method: "POST",
4186
+ headers: {
4187
+ Authorization: \`Bearer \${token}\`,
4188
+ "Content-Type": "application/json",
4189
+ },
4190
+ body: JSON.stringify({ name: "production-frontend" }),
4191
+ }).then(r => r.json());
4192
+
4193
+ // List keys
4194
+ const keys = await fetch("${e}/projects/${t}/api-keys", {
4195
+ headers: { Authorization: \`Bearer \${token}\` },
4196
+ }).then(r => r.json());
4197
+
4198
+ // Rotate a key (generates new secret, old key stops working)
4199
+ await fetch(\`${e}/projects/${t}/api-keys/\${keyId}/rotate\`, {
4200
+ method: "POST",
4201
+ headers: { Authorization: \`Bearer \${token}\` },
4202
+ });
4203
+
4204
+ // Revoke a key
4205
+ await fetch(\`${e}/projects/${t}/api-keys/\${keyId}/revoke\`, {
4206
+ method: "POST",
4207
+ headers: { Authorization: \`Bearer \${token}\` },
4208
+ });
4209
+ \`\`\`
4210
+
4211
+ ## Secrets & Environment Variables
4212
+
4213
+ Secrets are encrypted at rest and injected into function invocations at runtime.
4214
+
4215
+ \`\`\`typescript
4216
+ // Set a secret (via API)
4217
+ await fetch("${e}/functions/secrets", {
4218
+ method: "POST",
4219
+ headers: {
4220
+ Authorization: \`Bearer \${token}\`,
4221
+ "Content-Type": "application/json",
4222
+ },
4223
+ body: JSON.stringify({
4224
+ projectId: "${t}",
4225
+ key: "STRIPE_SECRET_KEY",
4226
+ value: "sk_live_...",
4227
+ }),
4228
+ });
4229
+
4230
+ // Or use the CLI
4231
+ // vaif secrets set STRIPE_SECRET_KEY sk_live_...
4232
+ // vaif secrets list
4233
+ // vaif secrets delete STRIPE_SECRET_KEY
4234
+ \`\`\`
4235
+
4236
+ **Accessing secrets in functions:**
4237
+
4238
+ \`\`\`typescript
4239
+ export default async function handler(req, ctx) {
4240
+ const stripe = new Stripe(ctx.secrets.STRIPE_SECRET_KEY);
4241
+ // ...
4242
+ }
4243
+ \`\`\`
4244
+
4245
+ ## API Reference Notes
4246
+
4247
+ ### Filter Syntax
4248
+
4249
+ The SDK supports these filter operators:
4250
+
4251
+ | Operator | Description | Example |
4252
+ |----------|-------------|---------|
4253
+ | \`eq\` | Equal | \`.eq("status", "active")\` |
4254
+ | \`neq\` | Not equal | \`.neq("status", "deleted")\` |
4255
+ | \`gt\` | Greater than | \`.gt("age", 18)\` |
4256
+ | \`lt\` | Less than | \`.lt("price", 100)\` |
4257
+ | \`gte\` | Greater than or equal | \`.gte("score", 90)\` |
4258
+ | \`lte\` | Less than or equal | \`.lte("count", 10)\` |
4259
+ | \`in\` | In array | \`.in("role", ["admin", "editor"])\` |
4260
+ | \`like\` | Pattern match (case-sensitive) | \`.like("name", "%john%")\` |
4261
+ | \`ilike\` | Pattern match (case-insensitive) | \`.ilike("name", "%john%")\` |
4262
+ | \`is\` | IS comparison (null, true, false) | \`.is("deleted_at", null)\` |
4263
+
4264
+ ### JSONB Subkey Filters
4265
+
4266
+ Filter on nested JSONB fields using arrow notation:
4267
+
4268
+ | Filter | SQL Generated |
4269
+ |--------|--------------|
4270
+ | \\\`filter[metadata->status]=active\\\` | \\\`metadata->>'status' = 'active'\\\` |
4271
+ | \\\`filter[config->theme.ilike]=%dark%\\\` | \\\`config->>'theme' ILIKE '%dark%'\\\` |
4272
+ | \\\`filter[data->user->role]=admin\\\` | \\\`data->'user'->>'role' = 'admin'\\\` |
4273
+
4274
+ All standard operators work with JSONB paths. The last segment uses \\\`->>\\\` (text extraction).
4275
+
4276
+ ### Compound Filters (AND + OR)
4277
+
4278
+ Combine AND and OR conditions:
4279
+
4280
+ \`\`\`
4281
+ ?filter[status]=active&or_filter[role]=admin&or_filter[role]=moderator
4282
+ \`\`\`
4283
+
4284
+ This generates: \\\`WHERE status = 'active' AND (role = 'admin' OR role = 'moderator')\\\`
4285
+
4286
+ ### Full-Text Search
4287
+
4288
+ \`\`\`typescript
4289
+ const results = await fetch("${e}/generated/posts/search", {
4290
+ method: "POST",
4291
+ headers: { "x-vaif-key": "${a}", "Content-Type": "application/json" },
4292
+ body: JSON.stringify({
4293
+ query: "search term",
4294
+ columns: ["title", "body"],
4295
+ limit: 20,
4296
+ }),
4297
+ });
4298
+ // Results ranked by ts_rank score
4299
+ \`\`\`
4300
+
4301
+ ### Aggregation
4302
+
4303
+ \`\`\`typescript
4304
+ const stats = await fetch("${e}/generated/orders/aggregate", {
4305
+ method: "POST",
4306
+ headers: { "x-vaif-key": "${a}", "Content-Type": "application/json" },
4307
+ body: JSON.stringify({
4308
+ aggregates: [
4309
+ { fn: "count", column: "*" },
4310
+ { fn: "sum", column: "total" },
4311
+ { fn: "avg", column: "total" },
4312
+ ],
4313
+ groupBy: ["status"],
4314
+ }),
4315
+ });
4316
+ \`\`\`
4317
+
4318
+ ### Joins (Foreign Key Includes)
4319
+
4320
+ Include related rows by specifying foreign key columns:
4321
+
4322
+ \`\`\`
4323
+ GET /generated/posts?include=author_id&include=category_id
4324
+ \`\`\`
4325
+
4326
+ Returns posts with \\\`author_id_included\\\` and \\\`category_id_included\\\` objects containing the related rows.
4327
+
4328
+ ### Upsert
4329
+
4330
+ Insert or update on conflict:
4331
+
4332
+ \`\`\`typescript
4333
+ const result = await fetch("${e}/generated/users", {
4334
+ method: "POST",
4335
+ headers: { "x-vaif-key": "${a}", "Content-Type": "application/json" },
4336
+ body: JSON.stringify({
4337
+ email: "alice@example.com",
4338
+ name: "Alice",
4339
+ _upsert: true,
4340
+ _conflictColumns: ["email"],
4341
+ }),
4342
+ });
4343
+ \`\`\`
4344
+
4345
+ ### Pagination
4346
+
4347
+ \`\`\`typescript
4348
+ // Default: limit 20, offset 0
4349
+ const page1 = await vaif.from("posts").select().limit(20).offset(0);
4350
+ const page2 = await vaif.from("posts").select().limit(20).offset(20);
4351
+ \`\`\`
4352
+
4353
+ ### REST API Response Format
4354
+
4355
+ When calling the REST API directly (without the SDK), all data-plane responses are wrapped:
4356
+
4357
+ \`\`\`typescript
4358
+ // GET /generated/{table} \u2192 list
4359
+ { data: [...], count: 5 }
4360
+
4361
+ // GET /generated/{table}/{id} \u2192 single record
4362
+ { data: { id: "...", ... } }
4363
+
4364
+ // POST /generated/{table} \u2192 created record
4365
+ { data: { id: "...", ... } }
4366
+
4367
+ // PATCH /generated/{table}/{id} \u2192 updated record
4368
+ { data: { id: "...", ... } }
4369
+
4370
+ // DELETE /generated/{table}/{id}
4371
+ { ok: true }
4372
+
4373
+ // Error responses
4374
+ { error: "NotFound", message: "...", requestId: "..." }
4375
+ \`\`\`
4376
+
4377
+ The SDK unwraps these automatically, but if you use \`fetch()\` directly, access the data via \`response.data\`.
4378
+
4379
+ ### Numeric/Decimal Column Serialization
4380
+
4381
+ PostgreSQL \`numeric\` and \`decimal\` columns serialize to **JSON strings** (to preserve arbitrary precision). This is standard behavior. Any column typed as \`numeric\` or \`decimal\` will arrive as \`"3.14"\` not \`3.14\`.
4382
+
4383
+ \`\`\`typescript
4384
+ // Wrong \u2014 value is a string, comparison may fail
4385
+ if (item.price > 10.0) { ... }
4386
+
4387
+ // Correct \u2014 parse before arithmetic
4388
+ if (parseFloat(item.price) > 10.0) { ... }
4389
+ \`\`\`
4390
+
4391
+ ### Auth Headers
4392
+
4393
+ VAIF uses **two auth modes** \u2014 choose the right one for each operation:
4394
+
4395
+ | Auth Mode | Header | Used For |
4396
+ |-----------|--------|----------|
4397
+ | **API Key** | \`x-vaif-key: vaif_xxx\` | Data-plane: CRUD (\`/generated/*\`), storage uploads/downloads, function invocation |
4398
+ | **JWT Token** | \`Authorization: Bearer <jwt>\` | Control-plane: schema introspection, project management, function CRUD, bucket creation |
4399
+
4400
+ > **Important**: API keys do NOT work for control-plane endpoints (creating functions, managing buckets, schema changes). Those require a JWT session token. The MCP server handles this automatically by using both auth modes.
4401
+
4402
+ ### MCP Tools (via .mcp.json)
4403
+
4404
+ The \`.mcp.json\` file configures an MCP server that gives Claude Code direct access to your VAIF project. Available tools:
4405
+
4406
+ | Tool | What it does |
4407
+ |------|-------------|
4408
+ | \`list_tables\`, \`describe_table\` | Inspect database schema |
4409
+ | \`get_schema\` | Full schema as JSON |
4410
+ | \`create_tables\` | Create or update tables declaratively |
4411
+ | \`query_rows\` | Query with filters, JSONB paths, pagination |
4412
+ | \`insert_row\`, \`update_row\`, \`delete_row\` | CRUD operations on any table |
4413
+ | \`list_functions\`, \`deploy_function\`, \`invoke_function\` | Function management |
4414
+ | \`get_function_logs\` | Execution history with status filters |
4415
+ | \`set_secret\`, \`list_secrets\`, \`delete_secret\` | Function secrets |
4416
+ | \`list_buckets\`, \`list_files\`, \`get_signed_url\` | Storage operations |
4417
+ | \`enable_realtime\`, \`realtime_status\` | Realtime subscriptions |
4418
+
4419
+ > **Note**: MCP tools are only available to the main Claude Code session, not to spawned sub-agents (Task tool). The main session should handle all VAIF backend operations directly.
4420
+ `}async function W(n){let t=X__default.default(),a=F();(!a||!a.token)&&(console.log(m__default.default.red("Not logged in")),console.log(m__default.default.gray("Run `vaif login` first to authenticate")),process.exit(1));let e=n.config||"vaif.config.json",i=null;try{i=await x(e);}catch{}let o=be(n,i,a);o||(console.log(m__default.default.yellow(`No project ID specified \u2014 fetching your projects...
4421
+ `)),o=await Ie(a.token),o||(console.log(m__default.default.gray(`
4422
+ Tip: pass --project-id <id> or set projectId in vaif.config.json`)),process.exit(1)));let r=b__default.default.resolve(n.outputDir||".");console.log(""),console.log(m__default.default.bold("VAIF Claude Code Setup")),console.log(m__default.default.gray(` Project: ${o}`)),console.log(""),t.start("Fetching database schema...");let l={tables:[]};try{let p=await fetch(`${w}/schema-engine/introspect/${o}`,{headers:{Authorization:`Bearer ${a.token}`}});p.ok?(l=await p.json(),t.succeed(`Fetched schema (${l.tables?.length||0} tables)`)):t.warn("Could not fetch schema \u2014 continuing without it");}catch{t.warn("Could not fetch schema \u2014 continuing without it");}t.start("Fetching project info...");let s=o,u=w;try{let p=await fetch(`${w}/projects/${o}`,{headers:{Authorization:`Bearer ${a.token}`}});if(p.ok){let c=await p.json(),f=c.project||c;s=f.name||o,u=f.apiUrl||w,t.succeed(`Project: ${s}`);}else t.warn("Could not fetch project info \u2014 using defaults");}catch{t.warn("Could not fetch project info \u2014 using defaults");}let d=n.apiKey||i?.api?.apiKey||"";if(!d){t.start("Generating API key for Claude Code...");try{let p=`claude-code-${Date.now()}`,c=await fetch(`${w}/projects/${o}/api-keys`,{method:"POST",headers:{Authorization:`Bearer ${a.token}`,"Content-Type":"application/json"},body:JSON.stringify({name:p,scopes:["crud","realtime","functions","storage"]})});if(c.ok){let f=await c.json();d=f.apiKey||f.key,t.succeed(`Generated API key: ${p}`);}else t.fail("Could not auto-generate API key"),console.log(m__default.default.yellow(" Pass one with --api-key or generate via `vaif keys generate`")),process.exit(1);}catch{t.fail("Could not auto-generate API key"),console.log(m__default.default.yellow(" Pass one with --api-key or generate via `vaif keys generate`")),process.exit(1);}}{t.start("Writing .mcp.json...");let p={mcpServers:{"vaif-studio":{command:"npx",args:["@vaiftech/mcp"],env:{VAIF_API_KEY:d,VAIF_PROJECT_ID:o,VAIF_API_URL:u,VAIF_AUTH_TOKEN:a.token}}}},c=b__default.default.join(r,".mcp.json");y__default.default.writeFileSync(c,JSON.stringify(p,null,2)+`
4423
+ `,"utf-8"),t.succeed(`Written ${m__default.default.cyan(".mcp.json")}`);}if(!n.skipClaudeMd){t.start("Writing CLAUDE.md...");let p=Te({projectId:o,apiKey:d,apiUrl:u,projectName:s,schema:l}),c=b__default.default.join(r,"CLAUDE.md");y__default.default.writeFileSync(c,p,"utf-8"),t.succeed(`Written ${m__default.default.cyan("CLAUDE.md")}`);}console.log(""),console.log(m__default.default.green.bold(" Claude Code integration configured!")),console.log(""),console.log(m__default.default.gray(" MCP Server: ")+m__default.default.white(".mcp.json")),n.skipClaudeMd||console.log(m__default.default.gray(" Context: ")+m__default.default.white("CLAUDE.md")),console.log(""),console.log(m__default.default.bold(" Next steps:")),console.log(m__default.default.gray(" 1. Open this project in Claude Code")),console.log(m__default.default.gray(" 2. The MCP server auto-connects to your VAIF project")),console.log(m__default.default.gray(" 3. Ask Claude to query, modify, or build against your schema")),console.log("");}var R={base:`# VAIF Studio Backend
4424
+
4425
+ This project uses **VAIF Studio** as its backend.
4426
+
4427
+ ## SDK Setup
4428
+
4429
+ \`\`\`bash
4430
+ npm install @vaiftech/client
4431
+ \`\`\`
4432
+
4433
+ \`\`\`typescript
4434
+ import { createVaifClient } from "@vaiftech/client";
4435
+
4436
+ const vaif = createVaifClient({
4437
+ baseUrl: "https://api.vaif.studio",
4438
+ projectId: process.env.VAIF_PROJECT_ID!,
4439
+ apiKey: process.env.VAIF_API_KEY!,
4440
+ });
4441
+ \`\`\`
4442
+
4443
+ ## MCP Server
4444
+
4445
+ This project includes an MCP server for Claude Code integration. Tools available:
4446
+
4447
+ - **Database**: list_tables, describe_table, query_rows, insert_row, update_row, delete_row
4448
+ - **Schema**: get_schema, create_tables
4449
+ - **Storage**: list_buckets, create_bucket, list_files, get_signed_url
4450
+ - **Functions**: list_functions, invoke_function, create_function, deploy_function
4451
+ - **Auth**: list_api_keys, create_api_key
4452
+ - **Realtime**: realtime_status, enable_realtime
4453
+
4454
+ ## Database
4455
+
4456
+ \`\`\`typescript
4457
+ // Query
4458
+ const { data } = await vaif.from("table_name").select().eq("column", value);
4459
+
4460
+ // Insert
4461
+ const { data } = await vaif.from("table_name").insert({ column: value });
4462
+
4463
+ // Update
4464
+ await vaif.from("table_name").update({ column: newValue }).eq("id", recordId);
4465
+
4466
+ // Delete
4467
+ await vaif.from("table_name").delete().eq("id", recordId);
4468
+ \`\`\`
4469
+
4470
+ ## Authentication
4471
+
4472
+ \`\`\`typescript
4473
+ // Sign up
4474
+ await vaif.auth.signUp({ email, password });
4475
+
4476
+ // Sign in
4477
+ const { data } = await vaif.auth.signIn({ email, password });
4478
+
4479
+ // OAuth
4480
+ await vaif.auth.signInWithOAuth({ provider: "google", redirectTo: callbackUrl });
4481
+
4482
+ // Get current user
4483
+ const user = await vaif.auth.getUser();
4484
+ \`\`\`
4485
+
4486
+ ## Storage
4487
+
4488
+ \`\`\`typescript
4489
+ // Upload
4490
+ await vaif.storage.from("bucket").upload("path/file.png", file);
4491
+
4492
+ // Signed URL
4493
+ const { url } = await vaif.storage.from("bucket").createSignedUrl("path/file.png", 3600);
4494
+
4495
+ // List files
4496
+ const { data } = await vaif.storage.from("bucket").list("path/");
4497
+ \`\`\`
4498
+
4499
+ ## Functions
4500
+
4501
+ \`\`\`typescript
4502
+ // Invoke
4503
+ const result = await vaif.functions.invoke("function_name", { body: { key: "value" } });
4504
+ \`\`\`
4505
+
4506
+ > Function names must be alphanumeric + underscores only (\`^[a-zA-Z0-9_]+$\`).
4507
+
4508
+ ## Realtime
4509
+
4510
+ \`\`\`typescript
4511
+ const subscription = vaif.realtime
4512
+ .channel("table_name")
4513
+ .on("INSERT", (payload) => console.log("New:", payload.new))
4514
+ .on("UPDATE", (payload) => console.log("Updated:", payload.new))
4515
+ .on("DELETE", (payload) => console.log("Removed:", payload.old))
4516
+ .subscribe();
4517
+ \`\`\`
4518
+
4519
+ ## Filter Operators
4520
+
4521
+ | Operator | Description | Example |
4522
+ |----------|-------------|---------|
4523
+ | eq | Equal | \`.eq("status", "active")\` |
4524
+ | neq | Not equal | \`.neq("status", "deleted")\` |
4525
+ | gt / gte | Greater than | \`.gt("age", 18)\` |
4526
+ | lt / lte | Less than | \`.lt("price", 100)\` |
4527
+ | in | In array | \`.in("role", ["admin", "editor"])\` |
4528
+ | like / ilike | Pattern match | \`.ilike("name", "%john%")\` |
4529
+ | is | IS NULL check | \`.is("deleted_at", null)\` |
4530
+
4531
+ ## Auth Headers
4532
+
4533
+ | Mode | Header | Used For |
4534
+ |------|--------|----------|
4535
+ | API Key | \`x-vaif-key: vk_xxx\` | Data-plane: CRUD, storage, functions |
4536
+ | JWT Token | \`Authorization: Bearer <jwt>\` | Control-plane: schema, project management |
4537
+
4538
+ ## Environment Variables
4539
+
4540
+ \`\`\`bash
4541
+ VAIF_API_URL=https://api.vaif.studio
4542
+ VAIF_PROJECT_ID=proj_xxx
4543
+ VAIF_API_KEY=vk_xxx
4544
+ \`\`\`
4545
+ `,saas:`# VAIF Studio \u2014 SaaS Backend
4546
+
4547
+ This project uses **VAIF Studio** as its backend for a SaaS application.
4548
+
4549
+ ## SDK Setup
4550
+
4551
+ \`\`\`bash
4552
+ npm install @vaiftech/client @vaiftech/react
4553
+ \`\`\`
4554
+
4555
+ \`\`\`typescript
4556
+ import { createVaifClient } from "@vaiftech/client";
4557
+
4558
+ const vaif = createVaifClient({
4559
+ baseUrl: "https://api.vaif.studio",
4560
+ projectId: process.env.VAIF_PROJECT_ID!,
4561
+ apiKey: process.env.VAIF_API_KEY!,
4562
+ });
4563
+ \`\`\`
4564
+
4565
+ ## Common SaaS Schema Patterns
4566
+
4567
+ ### Multi-Tenant with Organizations
4568
+
4569
+ \`\`\`sql
4570
+ -- Organizations (tenants)
4571
+ CREATE TABLE orgs (
4572
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
4573
+ name TEXT NOT NULL,
4574
+ slug TEXT UNIQUE NOT NULL,
4575
+ plan TEXT DEFAULT 'free',
4576
+ created_at TIMESTAMPTZ DEFAULT now()
4577
+ );
4578
+
4579
+ -- Organization members
4580
+ CREATE TABLE org_members (
4581
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
4582
+ org_id UUID REFERENCES orgs(id) ON DELETE CASCADE,
4583
+ user_id UUID NOT NULL,
4584
+ role TEXT DEFAULT 'member', -- owner, admin, member
4585
+ created_at TIMESTAMPTZ DEFAULT now(),
4586
+ UNIQUE(org_id, user_id)
4587
+ );
4588
+
4589
+ -- Tenant-scoped data
4590
+ CREATE TABLE projects (
4591
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
4592
+ org_id UUID REFERENCES orgs(id) ON DELETE CASCADE,
4593
+ name TEXT NOT NULL,
4594
+ settings JSONB DEFAULT '{}',
4595
+ created_at TIMESTAMPTZ DEFAULT now()
4596
+ );
4597
+ CREATE INDEX idx_projects_org ON projects(org_id);
4598
+ \`\`\`
4599
+
4600
+ ### Row-Level Security for Multi-Tenancy
4601
+
4602
+ \`\`\`typescript
4603
+ // Scope all queries to the current organization
4604
+ const projects = await vaif.from("projects").select().eq("org_id", currentOrgId);
4605
+
4606
+ // Or use RLS headers for automatic scoping
4607
+ const data = await vaif.from("projects").select({
4608
+ headers: { "x-vaif-rls": \\\`org_id:\${currentOrgId}\\\` },
4609
+ });
4610
+ \`\`\`
4611
+
4612
+ ### Auth Flows
4613
+
4614
+ \`\`\`typescript
4615
+ // Sign up \u2192 create org \u2192 add as owner
4616
+ const { data: user } = await vaif.auth.signUp({ email, password });
4617
+ const { data: org } = await vaif.from("orgs").insert({ name: orgName, slug });
4618
+ await vaif.from("org_members").insert({ org_id: org.id, user_id: user.id, role: "owner" });
4619
+ \`\`\`
4620
+
4621
+ ### Billing Integration
4622
+
4623
+ \`\`\`typescript
4624
+ // Store Stripe customer/subscription IDs
4625
+ CREATE TABLE billing_accounts (
4626
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
4627
+ org_id UUID UNIQUE REFERENCES orgs(id),
4628
+ stripe_customer_id TEXT,
4629
+ stripe_subscription_id TEXT,
4630
+ plan TEXT DEFAULT 'free',
4631
+ period_start TIMESTAMPTZ,
4632
+ period_end TIMESTAMPTZ
4633
+ );
4634
+
4635
+ // Webhook handler (VAIF Function)
4636
+ export default async function handler(req, ctx) {
4637
+ const event = req.body;
4638
+ if (event.type === "customer.subscription.updated") {
4639
+ const sub = event.data.object;
4640
+ await vaif.from("billing_accounts")
4641
+ .update({ plan: sub.metadata.plan, period_end: sub.current_period_end })
4642
+ .eq("stripe_subscription_id", sub.id);
4643
+ }
4644
+ return { received: true };
4645
+ }
4646
+ \`\`\`
4647
+
4648
+ ### Invite System
4649
+
4650
+ \`\`\`sql
4651
+ CREATE TABLE invites (
4652
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
4653
+ org_id UUID REFERENCES orgs(id) ON DELETE CASCADE,
4654
+ email TEXT NOT NULL,
4655
+ role TEXT DEFAULT 'member',
4656
+ token TEXT UNIQUE NOT NULL,
4657
+ expires_at TIMESTAMPTZ NOT NULL,
4658
+ accepted_at TIMESTAMPTZ
4659
+ );
4660
+ \`\`\`
4661
+
4662
+ ## API Key Scoping
4663
+
4664
+ \`\`\`
4665
+ x-vaif-key: vk_xxx \u2192 Data-plane (CRUD, storage, functions)
4666
+ Authorization: Bearer <jwt> \u2192 Control-plane (schema, project management)
4667
+ \`\`\`
4668
+
4669
+ ## Environment Variables
4670
+
4671
+ \`\`\`bash
4672
+ VAIF_API_URL=https://api.vaif.studio
4673
+ VAIF_PROJECT_ID=proj_xxx
4674
+ VAIF_API_KEY=vk_xxx
4675
+ STRIPE_SECRET_KEY=sk_live_xxx
4676
+ STRIPE_WEBHOOK_SECRET=whsec_xxx
4677
+ \`\`\`
4678
+ `,mobile:`# VAIF Studio \u2014 Mobile App Backend
4679
+
4680
+ This project uses **VAIF Studio** as its backend for a mobile application (React Native/Expo, Flutter, or Swift).
4681
+
4682
+ ## SDK Setup
4683
+
4684
+ ### React Native / Expo
4685
+ \`\`\`bash
4686
+ npm install @vaiftech/sdk-expo
4687
+ \`\`\`
4688
+
4689
+ \`\`\`typescript
4690
+ import { VaifProvider, useVaif } from "@vaiftech/sdk-expo";
4691
+
4692
+ export default function App() {
4693
+ return (
4694
+ <VaifProvider
4695
+ config={{
4696
+ baseUrl: "https://api.vaif.studio",
4697
+ projectId: "proj_xxx",
4698
+ apiKey: "vk_xxx",
4699
+ }}
4700
+ >
4701
+ <MainApp />
4702
+ </VaifProvider>
4703
+ );
4704
+ }
4705
+ \`\`\`
4706
+
4707
+ ### Flutter / Dart
4708
+ \`\`\`yaml
4709
+ # pubspec.yaml
4710
+ dependencies:
4711
+ vaif_client: ^1.0.0
4712
+ \`\`\`
4713
+
4714
+ \`\`\`dart
4715
+ import 'package:vaif_client/vaif_client.dart';
4716
+
4717
+ final vaif = VaifClient(
4718
+ baseUrl: 'https://api.vaif.studio',
4719
+ projectId: 'proj_xxx',
4720
+ apiKey: 'vk_xxx',
4721
+ );
4722
+ \`\`\`
4723
+
4724
+ ### Swift / iOS
4725
+ \`\`\`swift
4726
+ // Package.swift
4727
+ .package(url: "https://github.com/vaifllc/vaif-swift", from: "0.2.0")
4728
+
4729
+ import VaifClient
4730
+
4731
+ let vaif = VaifClient(
4732
+ baseUrl: "https://api.vaif.studio",
4733
+ projectId: "proj_xxx",
4734
+ apiKey: "vk_xxx"
4735
+ )
4736
+ \`\`\`
4737
+
4738
+ ## Common Mobile Patterns
4739
+
4740
+ ### User Profiles with Avatars
4741
+
4742
+ \`\`\`sql
4743
+ CREATE TABLE profiles (
4744
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
4745
+ user_id UUID UNIQUE NOT NULL,
4746
+ display_name TEXT,
4747
+ avatar_url TEXT,
4748
+ bio TEXT,
4749
+ push_token TEXT,
4750
+ device_type TEXT, -- ios, android
4751
+ last_seen TIMESTAMPTZ DEFAULT now()
4752
+ );
4753
+ \`\`\`
4754
+
4755
+ ### Push Notifications
4756
+
4757
+ \`\`\`typescript
4758
+ // Store push tokens
4759
+ await vaif.from("profiles").update({
4760
+ push_token: expoPushToken,
4761
+ device_type: Platform.OS,
4762
+ }).eq("user_id", userId);
4763
+
4764
+ // Send notification (VAIF Function)
4765
+ export default async function handler(req, ctx) {
4766
+ const { userId, title, body } = req.body;
4767
+ const { data: profile } = await vaif.from("profiles")
4768
+ .select("push_token, device_type")
4769
+ .eq("user_id", userId)
4770
+ .single();
4771
+
4772
+ // Send via Expo Push API or APNs/FCM
4773
+ await fetch("https://exp.host/--/api/v2/push/send", {
4774
+ method: "POST",
4775
+ headers: { "Content-Type": "application/json" },
4776
+ body: JSON.stringify({
4777
+ to: profile.push_token,
4778
+ title, body,
4779
+ }),
4780
+ });
4781
+ return { sent: true };
4782
+ }
4783
+ \`\`\`
4784
+
4785
+ ### Offline-First with Sync
4786
+
4787
+ \`\`\`typescript
4788
+ // Cache data locally, sync when online
4789
+ import AsyncStorage from "@react-native-async-storage/async-storage";
4790
+
4791
+ // Fetch and cache
4792
+ const { data } = await vaif.from("items").select();
4793
+ await AsyncStorage.setItem("items_cache", JSON.stringify(data));
4794
+
4795
+ // Read from cache when offline
4796
+ const cached = JSON.parse(await AsyncStorage.getItem("items_cache") || "[]");
4797
+ \`\`\`
4798
+
4799
+ ### Image Upload from Camera
4800
+
4801
+ \`\`\`typescript
4802
+ import * as ImagePicker from "expo-image-picker";
4803
+
4804
+ const result = await ImagePicker.launchCameraAsync({ quality: 0.8 });
4805
+ if (!result.canceled) {
4806
+ const uri = result.assets[0].uri;
4807
+ const response = await fetch(uri);
4808
+ const blob = await response.blob();
4809
+
4810
+ await vaif.storage.from("avatars").upload(
4811
+ \\\`\${userId}/avatar.jpg\\\`,
4812
+ blob,
4813
+ { contentType: "image/jpeg" }
4814
+ );
4815
+ }
4816
+ \`\`\`
4817
+
4818
+ ### Realtime Chat
4819
+
4820
+ \`\`\`typescript
4821
+ // Subscribe to new messages
4822
+ const subscription = vaif.realtime
4823
+ .channel("messages")
4824
+ .on("INSERT", (payload) => {
4825
+ setMessages(prev => [...prev, payload.new]);
4826
+ })
4827
+ .subscribe();
4828
+
4829
+ // Send a message
4830
+ await vaif.from("messages").insert({
4831
+ room_id: roomId,
4832
+ user_id: userId,
4833
+ content: messageText,
4834
+ });
4835
+ \`\`\`
4836
+
4837
+ ## Auth with Secure Token Storage
4838
+
4839
+ The Expo SDK automatically stores auth tokens in SecureStore (iOS Keychain / Android Keystore).
4840
+
4841
+ \`\`\`typescript
4842
+ const { useAuth } = require("@vaiftech/sdk-expo");
4843
+
4844
+ function LoginScreen() {
4845
+ const { signIn, signUp, user, loading } = useAuth();
4846
+
4847
+ const handleLogin = async () => {
4848
+ const { error } = await signIn({ email, password });
4849
+ if (error) Alert.alert("Error", error.message);
4850
+ };
4851
+ }
4852
+ \`\`\`
4853
+
4854
+ ## Environment Variables
4855
+
4856
+ \`\`\`bash
4857
+ VAIF_API_URL=https://api.vaif.studio
4858
+ VAIF_PROJECT_ID=proj_xxx
4859
+ VAIF_API_KEY=vk_xxx
4860
+ EXPO_PUBLIC_VAIF_PROJECT_ID=proj_xxx
4861
+ EXPO_PUBLIC_VAIF_API_KEY=vk_xxx
4862
+ \`\`\`
4863
+ `,ecommerce:`# VAIF Studio \u2014 E-Commerce Backend
4864
+
4865
+ This project uses **VAIF Studio** as its backend for an e-commerce application.
4866
+
4867
+ ## SDK Setup
4868
+
4869
+ \`\`\`bash
4870
+ npm install @vaiftech/client
4871
+ \`\`\`
4872
+
4873
+ \`\`\`typescript
4874
+ import { createVaifClient } from "@vaiftech/client";
4875
+
4876
+ const vaif = createVaifClient({
4877
+ baseUrl: "https://api.vaif.studio",
4878
+ projectId: process.env.VAIF_PROJECT_ID!,
4879
+ apiKey: process.env.VAIF_API_KEY!,
4880
+ });
4881
+ \`\`\`
4882
+
4883
+ ## E-Commerce Schema
4884
+
4885
+ \`\`\`sql
4886
+ -- Products
4887
+ CREATE TABLE products (
4888
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
4889
+ name TEXT NOT NULL,
4890
+ slug TEXT UNIQUE NOT NULL,
4891
+ description TEXT,
4892
+ price NUMERIC(10,2) NOT NULL,
4893
+ compare_at_price NUMERIC(10,2),
4894
+ currency TEXT DEFAULT 'USD',
4895
+ sku TEXT UNIQUE,
4896
+ inventory_count INTEGER DEFAULT 0,
4897
+ category_id UUID REFERENCES categories(id),
4898
+ images JSONB DEFAULT '[]', -- [{url, alt, position}]
4899
+ metadata JSONB DEFAULT '{}',
4900
+ status TEXT DEFAULT 'draft', -- draft, active, archived
4901
+ created_at TIMESTAMPTZ DEFAULT now(),
4902
+ updated_at TIMESTAMPTZ DEFAULT now()
4903
+ );
4904
+ CREATE INDEX idx_products_category ON products(category_id);
4905
+ CREATE INDEX idx_products_status ON products(status);
4906
+
4907
+ -- Categories
4908
+ CREATE TABLE categories (
4909
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
4910
+ name TEXT NOT NULL,
4911
+ slug TEXT UNIQUE NOT NULL,
4912
+ parent_id UUID REFERENCES categories(id),
4913
+ sort_order INTEGER DEFAULT 0
4914
+ );
4915
+
4916
+ -- Orders
4917
+ CREATE TABLE orders (
4918
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
4919
+ user_id UUID NOT NULL,
4920
+ status TEXT DEFAULT 'pending', -- pending, confirmed, shipped, delivered, cancelled
4921
+ subtotal NUMERIC(10,2) NOT NULL,
4922
+ tax NUMERIC(10,2) DEFAULT 0,
4923
+ shipping NUMERIC(10,2) DEFAULT 0,
4924
+ total NUMERIC(10,2) NOT NULL,
4925
+ currency TEXT DEFAULT 'USD',
4926
+ shipping_address JSONB,
4927
+ billing_address JSONB,
4928
+ stripe_payment_intent_id TEXT,
4929
+ notes TEXT,
4930
+ created_at TIMESTAMPTZ DEFAULT now(),
4931
+ updated_at TIMESTAMPTZ DEFAULT now()
4932
+ );
4933
+ CREATE INDEX idx_orders_user ON orders(user_id);
4934
+ CREATE INDEX idx_orders_status ON orders(status);
4935
+
4936
+ -- Order items
4937
+ CREATE TABLE order_items (
4938
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
4939
+ order_id UUID REFERENCES orders(id) ON DELETE CASCADE,
4940
+ product_id UUID REFERENCES products(id),
4941
+ quantity INTEGER NOT NULL,
4942
+ unit_price NUMERIC(10,2) NOT NULL,
4943
+ total NUMERIC(10,2) NOT NULL
4944
+ );
4945
+ CREATE INDEX idx_order_items_order ON order_items(order_id);
4946
+
4947
+ -- Cart (session-based)
4948
+ CREATE TABLE carts (
4949
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
4950
+ user_id UUID,
4951
+ session_id TEXT,
4952
+ items JSONB DEFAULT '[]', -- [{productId, quantity, price}]
4953
+ created_at TIMESTAMPTZ DEFAULT now(),
4954
+ updated_at TIMESTAMPTZ DEFAULT now()
4955
+ );
4956
+
4957
+ -- Reviews
4958
+ CREATE TABLE reviews (
4959
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
4960
+ product_id UUID REFERENCES products(id) ON DELETE CASCADE,
4961
+ user_id UUID NOT NULL,
4962
+ rating INTEGER CHECK (rating >= 1 AND rating <= 5),
4963
+ title TEXT,
4964
+ body TEXT,
4965
+ created_at TIMESTAMPTZ DEFAULT now()
4966
+ );
4967
+ CREATE INDEX idx_reviews_product ON reviews(product_id);
4968
+ \`\`\`
4969
+
4970
+ ## Common Patterns
4971
+
4972
+ ### Product Listing with Filters
4973
+
4974
+ \`\`\`typescript
4975
+ // Browse products with category filter and pagination
4976
+ const { data: products } = await vaif
4977
+ .from("products")
4978
+ .select("id, name, slug, price, compare_at_price, images, category:categories(name)")
4979
+ .eq("status", "active")
4980
+ .eq("category_id", categoryId)
4981
+ .order("created_at", { ascending: false })
4982
+ .limit(20)
4983
+ .offset(page * 20);
4984
+ \`\`\`
4985
+
4986
+ ### Cart Management
4987
+
4988
+ \`\`\`typescript
4989
+ // Add to cart
4990
+ const cart = await vaif.from("carts").select().eq("user_id", userId).single();
4991
+ const items = [...(cart.data?.items || [])];
4992
+ const existing = items.findIndex(i => i.productId === productId);
4993
+ if (existing >= 0) {
4994
+ items[existing].quantity += quantity;
4995
+ } else {
4996
+ items.push({ productId, quantity, price });
4997
+ }
4998
+ await vaif.from("carts").update({ items, updated_at: new Date().toISOString() }).eq("id", cart.data.id);
4999
+ \`\`\`
5000
+
5001
+ ### Order Creation
5002
+
5003
+ \`\`\`typescript
5004
+ // Create order from cart
5005
+ const { data: order } = await vaif.from("orders").insert({
5006
+ user_id: userId,
5007
+ subtotal, tax, shipping,
5008
+ total: subtotal + tax + shipping,
5009
+ shipping_address: addressData,
5010
+ });
5011
+
5012
+ // Create order items
5013
+ const orderItems = cartItems.map(item => ({
5014
+ order_id: order.id,
5015
+ product_id: item.productId,
5016
+ quantity: item.quantity,
5017
+ unit_price: item.price,
5018
+ total: item.price * item.quantity,
5019
+ }));
5020
+ await vaif.from("order_items").insert(orderItems);
5021
+ \`\`\`
5022
+
5023
+ ### Inventory Management
5024
+
5025
+ \`\`\`typescript
5026
+ // Decrement inventory on order (VAIF Function)
5027
+ export default async function handler(req, ctx) {
5028
+ const { items } = req.body; // [{productId, quantity}]
5029
+ for (const item of items) {
5030
+ const { data: product } = await vaif.from("products")
5031
+ .select("inventory_count")
5032
+ .eq("id", item.productId)
5033
+ .single();
5034
+
5035
+ if (product.inventory_count < item.quantity) {
5036
+ return { error: \\\`Insufficient stock for \${item.productId}\\\` };
5037
+ }
5038
+
5039
+ await vaif.from("products")
5040
+ .update({ inventory_count: product.inventory_count - item.quantity })
5041
+ .eq("id", item.productId);
5042
+ }
5043
+ return { success: true };
5044
+ }
5045
+ \`\`\`
5046
+
5047
+ ### Payment Webhooks
5048
+
5049
+ \`\`\`typescript
5050
+ // Stripe webhook handler (VAIF Function)
5051
+ export default async function handler(req, ctx) {
5052
+ const sig = req.headers["stripe-signature"];
5053
+ const event = stripe.webhooks.constructEvent(req.rawBody, sig, ctx.secrets.STRIPE_WEBHOOK_SECRET);
5054
+
5055
+ switch (event.type) {
5056
+ case "payment_intent.succeeded":
5057
+ await vaif.from("orders")
5058
+ .update({ status: "confirmed" })
5059
+ .eq("stripe_payment_intent_id", event.data.object.id);
5060
+ break;
5061
+ case "payment_intent.payment_failed":
5062
+ await vaif.from("orders")
5063
+ .update({ status: "cancelled" })
5064
+ .eq("stripe_payment_intent_id", event.data.object.id);
5065
+ break;
5066
+ }
5067
+ return { received: true };
5068
+ }
5069
+ \`\`\`
5070
+
5071
+ ### Product Image Upload
5072
+
5073
+ \`\`\`typescript
5074
+ // Upload product images to storage
5075
+ const { url } = await vaif.storage
5076
+ .from("product-images")
5077
+ .upload(\\\`\${productId}/\${fileName}\\\`, file, { contentType: "image/webp" });
5078
+
5079
+ // Update product images array
5080
+ const { data: product } = await vaif.from("products").select("images").eq("id", productId).single();
5081
+ const images = [...(product.images || []), { url, alt: altText, position: product.images.length }];
5082
+ await vaif.from("products").update({ images }).eq("id", productId);
5083
+ \`\`\`
5084
+
5085
+ ## Environment Variables
5086
+
5087
+ \`\`\`bash
5088
+ VAIF_API_URL=https://api.vaif.studio
5089
+ VAIF_PROJECT_ID=proj_xxx
5090
+ VAIF_API_KEY=vk_xxx
5091
+ STRIPE_SECRET_KEY=sk_live_xxx
5092
+ STRIPE_PUBLISHABLE_KEY=pk_live_xxx
5093
+ STRIPE_WEBHOOK_SECRET=whsec_xxx
5094
+ \`\`\`
5095
+
5096
+ > **Note**: \`numeric\` columns (like \`price\`) serialize to JSON strings to preserve precision. Parse with \`parseFloat()\` before arithmetic.
5097
+ `};var Se={$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 we(n){if(n.claude!==void 0){let e=typeof n.claude=="string"?n.claude:null;if(e&&e in R){let o=X__default.default("Generating CLAUDE.md from template...").start(),r=R[e],l=Ce();l&&(r+=`
5098
+ ## Team Conventions
5099
+
5100
+ ${l}
5101
+ `);let s=b__default.default.resolve("CLAUDE.md");y__default.default.existsSync(s)&&!n.force&&(o.fail("CLAUDE.md already exists"),console.log(m__default.default.yellow(`
5102
+ Use --force to overwrite.`)),process.exit(1)),y__default.default.writeFileSync(s,r,"utf-8"),o.succeed(`Created CLAUDE.md (${e} template)`),l&&console.log(m__default.default.gray(" Imported team conventions from existing config files.")),console.log(m__default.default.gray(`
5103
+ Customize the generated CLAUDE.md with your project details.`)),console.log(m__default.default.gray(` For a personalized version from live data, run: vaif claude-setup
5104
+ `));return}e&&!(e in R)&&(console.log(m__default.default.red(`
5105
+ Unknown template type: "${e}"`)),console.log(m__default.default.gray(" Available types: base, saas, mobile, ecommerce")),console.log(m__default.default.gray(` Or omit the type to auto-generate from your live project: vaif init --claude
5106
+ `)),process.exit(1));let i=xe();i?(console.log(m__default.default.gray(`
5107
+ Detected project type: ${m__default.default.cyan(i)}`)),console.log(m__default.default.gray(` Generating from live project data with template context...
5108
+ `))):console.log(m__default.default.gray(`
5109
+ Generating from live project data...
5110
+ `)),await W({});return}if(n.addFeatures){n.template||(console.log(m__default.default.red(`
5111
+ --add-features requires --template to know which template to use.`)),console.log(m__default.default.gray("Example: vaif init --template react-spa --add-features functions,storage")),process.exit(1));let e=n.addFeatures.split(",").map(i=>i.trim());await k(n.template,{force:n.force,features:e,addOnly:true});return}let t=X__default.default("Initializing VAIF configuration...").start(),a=b__default.default.resolve("vaif.config.json");y__default.default.existsSync(a)&&!n.force&&(t.fail("vaif.config.json already exists"),console.log(m__default.default.yellow(`
5112
+ Use --force to overwrite existing configuration.`)),process.exit(1));try{if(y__default.default.writeFileSync(a,JSON.stringify(Se,null,2),"utf-8"),t.succeed("Created vaif.config.json"),n.template){let e=n.features?n.features.split(",").map(i=>i.trim()):void 0;await k(n.template,{force:n.force,features:e});}else {let e=b__default.default.resolve(".env.example");if(y__default.default.existsSync(e)||(y__default.default.writeFileSync(e,`# VAIF Configuration
3777
5113
  DATABASE_URL=postgresql://user:password@localhost:5432/database
3778
5114
  VAIF_API_KEY=your-api-key
3779
- `,"utf-8"),console.log(d__default.default.gray("Created .env.example"))),n.typescript){let i=R__default.default.resolve("src/types");b__default.default.existsSync(i)||(b__default.default.mkdirSync(i,{recursive:!0}),console.log(d__default.default.gray("Created src/types directory")));}console.log(""),console.log(d__default.default.green("VAIF initialized successfully!")),console.log(""),console.log(d__default.default.gray("Next steps:")),console.log(d__default.default.gray(" 1. Update vaif.config.json with your project ID")),console.log(d__default.default.gray(" 2. Set DATABASE_URL in your environment")),console.log(d__default.default.gray(" 3. Run: npx vaif generate")),console.log("");}}catch(e){a.fail("Failed to initialize"),e instanceof Error&&console.error(d__default.default.red(`
3780
- Error: ${e.message}`)),process.exit(1);}}async function at(n){let{connectionString:a,schema:t="public"}=n,e=new W__default.default.Client({connectionString:a});await e.connect();try{let i=await e.query(`
5115
+ `,"utf-8"),console.log(m__default.default.gray("Created .env.example"))),n.typescript){let i=b__default.default.resolve("src/types");y__default.default.existsSync(i)||(y__default.default.mkdirSync(i,{recursive:!0}),console.log(m__default.default.gray("Created src/types directory")));}console.log(""),console.log(m__default.default.green("VAIF initialized successfully!")),console.log(""),console.log(m__default.default.gray("Next steps:")),console.log(m__default.default.gray(" 1. Update vaif.config.json with your project ID")),console.log(m__default.default.gray(" 2. Set DATABASE_URL in your environment")),console.log(m__default.default.gray(" 3. Run: npx vaif generate")),console.log("");}}catch(e){t.fail("Failed to initialize"),e instanceof Error&&console.error(m__default.default.red(`
5116
+ Error: ${e.message}`)),process.exit(1);}}function xe(){try{let n=b__default.default.resolve("package.json");if(!y__default.default.existsSync(n))return null;let t=JSON.parse(y__default.default.readFileSync(n,"utf-8")),a={...t.dependencies,...t.devDependencies};return a.expo||a["react-native"]||a["@vaiftech/sdk-expo"]||y__default.default.existsSync(b__default.default.resolve("pubspec.yaml"))||y__default.default.existsSync(b__default.default.resolve("Package.swift"))||y__default.default.existsSync(b__default.default.resolve("*.xcodeproj"))?"mobile":(a.stripe||a["@stripe/stripe-js"]||a["shopify-api-node"]||a["@shopify/shopify-api"])&&(y__default.default.existsSync(b__default.default.resolve("src/models/product.ts"))||y__default.default.existsSync(b__default.default.resolve("src/models/order.ts"))||y__default.default.existsSync(b__default.default.resolve("src/pages/products"))||y__default.default.existsSync(b__default.default.resolve("src/pages/cart")))?"ecommerce":a.stripe||a["@stripe/stripe-js"]||t.name?.includes("saas")||t.description?.toLowerCase().includes("saas")?"saas":"base"}catch{return null}}function Ce(){let n=[{path:".cursorrules",label:"Cursor Rules"},{path:".github/copilot-instructions.md",label:"GitHub Copilot Instructions"},{path:".windsurfrules",label:"Windsurf Rules"},{path:".clinerules",label:"Cline Rules"}],t=[];for(let a of n){let e=b__default.default.resolve(a.path);if(y__default.default.existsSync(e))try{let i=y__default.default.readFileSync(e,"utf-8").trim();i.length>0&&i.length<1e4&&t.push(`### From ${a.label} (\`${a.path}\`)
5117
+
5118
+ ${i}`);}catch{}}return t.length>0?t.join(`
5119
+
5120
+ `):null}async function Ut(n){let{connectionString:t,schema:a="public"}=n,e=new ie__default.default.Client({connectionString:t});await e.connect();try{let i=await e.query(`
3781
5121
  SELECT table_name, table_type
3782
5122
  FROM information_schema.tables
3783
5123
  WHERE table_schema = $1
3784
5124
  AND table_type = 'BASE TABLE'
3785
5125
  ORDER BY table_name
3786
- `,[t]),o=await e.query(`
5126
+ `,[a]),o=await e.query(`
3787
5127
  SELECT
3788
5128
  table_name,
3789
5129
  column_name,
@@ -3798,7 +5138,7 @@ Error: ${e.message}`)),process.exit(1);}}async function at(n){let{connectionStri
3798
5138
  FROM information_schema.columns
3799
5139
  WHERE table_schema = $1
3800
5140
  ORDER BY table_name, ordinal_position
3801
- `,[t]),c=await e.query(`
5141
+ `,[a]),r=await e.query(`
3802
5142
  SELECT
3803
5143
  t.typname as enum_name,
3804
5144
  e.enumlabel as enum_value
@@ -3807,16 +5147,16 @@ Error: ${e.message}`)),process.exit(1);}}async function at(n){let{connectionStri
3807
5147
  JOIN pg_namespace n ON n.oid = t.typnamespace
3808
5148
  WHERE n.nspname = $1
3809
5149
  ORDER BY t.typname, e.enumsortorder
3810
- `,[t]),s=new Map;for(let p of i.rows)s.set(p.table_name,[]);for(let p of o.rows){let m=s.get(p.table_name);m&&m.push(p);}let r=new Map;for(let p of c.rows){let m=r.get(p.enum_name)||[];m.push(p.enum_value),r.set(p.enum_name,m);}let l=ge(s,r);return Q__default.default.format(l,{parser:"typescript",semi:!0,singleQuote:!1,trailingComma:"es5",printWidth:100})}finally{await e.end();}}var N={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",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",point:"{ x: number; y: number }",ARRAY:"unknown[]"};function fe(n,a){let{data_type:t,udt_name:e,is_nullable:i}=n;if(a.has(e)){let s=a.get(e).map(r=>`"${r}"`).join(" | ");return i==="YES"?`(${s}) | null`:s}if(t==="ARRAY"){let c=e.replace(/^_/,"");if(a.has(c)){let l=a.get(c).map(p=>`"${p}"`).join(" | ");return i==="YES"?`(${l})[] | null`:`(${l})[]`}let s=N[c]||"unknown";return i==="YES"?`${s}[] | null`:`${s}[]`}let o=N[t]||N[e]||"unknown";return i==="YES"&&(o=`${o} | null`),o}function V(n){return n.split(/[_\-\s]+/).map(a=>a.charAt(0).toUpperCase()+a.slice(1).toLowerCase()).join("")}function ge(n,a){let t=["/**"," * 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(a.size>0){t.push("// ============ ENUMS ============"),t.push("");for(let[i,o]of a){let c=V(i),s=o.map(r=>` | "${r}"`).join(`
3811
- `);t.push(`export type ${c} =
3812
- ${s};`),t.push("");}}t.push("// ============ TABLES ============"),t.push("");let e=[];for(let[i,o]of n){e.push(i);let c=V(i),s=[],r=[],l=[];for(let p of o){let m=fe(p,a),u=p.column_name,f=p.column_default!==null||p.is_identity==="YES",h=p.is_nullable==="YES";s.push(` ${u}: ${m};`),f||p.column_name==="id"?r.push(` ${u}?: ${m.replace(" | null","")} | null;`):h?r.push(` ${u}?: ${m};`):r.push(` ${u}: ${m.replace(" | null","")};`),l.push(` ${u}?: ${m.replace(" | null","")} | null;`);}t.push(`export interface ${c} {
3813
- ${s.join(`
5150
+ `,[a]),l=new Map;for(let d of i.rows)l.set(d.table_name,[]);for(let d of o.rows){let p=l.get(d.table_name);p&&p.push(d);}let s=new Map;for(let d of r.rows){let p=s.get(d.enum_name)||[];p.push(d.enum_value),s.set(d.enum_name,p);}let u=Ue(l,s);return re__default.default.format(u,{parser:"typescript",semi:!0,singleQuote:!1,trailingComma:"es5",printWidth:100})}finally{await e.end();}}var j={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",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",point:"{ x: number; y: number }",ARRAY:"unknown[]"};function Re(n,t){let{data_type:a,udt_name:e,is_nullable:i}=n;if(t.has(e)){let l=t.get(e).map(s=>`"${s}"`).join(" | ");return i==="YES"?`(${l}) | null`:l}if(a==="ARRAY"){let r=e.replace(/^_/,"");if(t.has(r)){let u=t.get(r).map(d=>`"${d}"`).join(" | ");return i==="YES"?`(${u})[] | null`:`(${u})[]`}let l=j[r]||"unknown";return i==="YES"?`${l}[] | null`:`${l}[]`}let o=j[a]||j[e]||"unknown";return i==="YES"&&(o=`${o} | null`),o}function O(n){return n.split(/[_\-\s]+/).map(t=>t.charAt(0).toUpperCase()+t.slice(1).toLowerCase()).join("")}function Ue(n,t){let a=["/**"," * 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){a.push("// ============ ENUMS ============"),a.push("");for(let[i,o]of t){let r=O(i),l=o.map(s=>` | "${s}"`).join(`
5151
+ `);a.push(`export type ${r} =
5152
+ ${l};`),a.push("");}}a.push("// ============ TABLES ============"),a.push("");let e=[];for(let[i,o]of n){e.push(i);let r=O(i),l=[],s=[],u=[];for(let d of o){let p=Re(d,t),c=d.column_name,f=d.column_default!==null||d.is_identity==="YES",I=d.is_nullable==="YES";l.push(` ${c}: ${p};`),f||d.column_name==="id"?s.push(` ${c}?: ${p.replace(" | null","")} | null;`):I?s.push(` ${c}?: ${p};`):s.push(` ${c}: ${p.replace(" | null","")};`),u.push(` ${c}?: ${p.replace(" | null","")} | null;`);}a.push(`export interface ${r} {
5153
+ ${l.join(`
3814
5154
  `)}
3815
- }`),t.push(""),t.push(`export interface ${c}Insert {
3816
- ${r.join(`
5155
+ }`),a.push(""),a.push(`export interface ${r}Insert {
5156
+ ${s.join(`
3817
5157
  `)}
3818
- }`),t.push(""),t.push(`export interface ${c}Update {
3819
- ${l.join(`
5158
+ }`),a.push(""),a.push(`export interface ${r}Update {
5159
+ ${u.join(`
3820
5160
  `)}
3821
- }`),t.push("");}t.push("// ============ DATABASE SCHEMA ============"),t.push(""),t.push("export interface Database {");for(let i of e){let o=V(i);t.push(` ${i}: {`),t.push(` Row: ${o};`),t.push(` Insert: ${o}Insert;`),t.push(` Update: ${o}Update;`),t.push(" };");}return t.push("}"),t.push(""),t.push("export type TableName = keyof Database;"),t.push(""),t.push("// ============ HELPER TYPES ============"),t.push(""),t.push('export type Row<T extends TableName> = Database[T]["Row"];'),t.push('export type Insert<T extends TableName> = Database[T]["Insert"];'),t.push('export type Update<T extends TableName> = Database[T]["Update"];'),t.push(""),t.join(`
3822
- `)}exports.generateTypes=re;exports.generateTypesFromConnection=at;exports.initConfig=pe;exports.loadConfig=S;
5161
+ }`),a.push("");}a.push("// ============ DATABASE SCHEMA ============"),a.push(""),a.push("export interface Database {");for(let i of e){let o=O(i);a.push(` ${i}: {`),a.push(` Row: ${o};`),a.push(` Insert: ${o}Insert;`),a.push(` Update: ${o}Update;`),a.push(" };");}return a.push("}"),a.push(""),a.push("export type TableName = keyof Database;"),a.push(""),a.push("// ============ HELPER TYPES ============"),a.push(""),a.push('export type Row<T extends TableName> = Database[T]["Row"];'),a.push('export type Insert<T extends TableName> = Database[T]["Insert"];'),a.push('export type Update<T extends TableName> = Database[T]["Update"];'),a.push(""),a.join(`
5162
+ `)}exports.generateTypes=fe;exports.generateTypesFromConnection=Ut;exports.initConfig=we;exports.loadConfig=x;