@revstackhq/cli 0.0.0-dev-20260226064743 → 0.0.0-dev-20260227092523

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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/commands/init.ts"],"sourcesContent":["/**\n * @file commands/init.ts\n * @description Scaffolds a new `revstack.config.ts` in the current directory.\n * Generates a starter config with the immutable Default Guest Plan and\n * a sample Pro plan using type-safe helpers from @revstackhq/core.\n */\n\nimport { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { spawnSync } from \"node:child_process\";\nimport { fileURLToPath } from \"node:url\";\nimport ora from \"ora\";\n\nconst STARTER_FEATURES = `import { defineFeature } from \"@revstackhq/core\";\n\nexport const features = {\n seats: defineFeature({\n name: \"Seats\",\n type: \"static\",\n unit_type: \"count\",\n }),\n ai_tokens: defineFeature({\n name: \"AI Tokens\",\n type: \"metered\",\n unit_type: \"count\",\n }),\n};\n`;\n\nconst STARTER_PLANS = `import { definePlan } from \"@revstackhq/core\";\nimport { features } from \"./features\";\n\nexport const plans = {\n // DO NOT DELETE: Automatically created default plan for guests.\n default: definePlan<typeof features>({\n name: \"Default\",\n description: \"Automatically created default plan for guests.\",\n is_default: true,\n is_public: false,\n type: \"free\",\n features: {},\n }),\n pro: definePlan<typeof features>({\n name: \"Pro\",\n description: \"For professional teams.\",\n is_default: false,\n is_public: true,\n type: \"paid\",\n prices: [\n {\n amount: 2900,\n currency: \"USD\",\n billing_interval: \"monthly\",\n trial_period_days: 14,\n },\n {\n amount: 29000,\n currency: \"USD\",\n billing_interval: \"yearly\",\n trial_period_days: 14,\n }\n ],\n features: {\n seats: { value_limit: 5, is_hard_limit: true },\n ai_tokens: { value_limit: 1000, reset_period: \"monthly\" },\n },\n }),\n};\n`;\n\nconst STARTER_CONFIG = `import { defineConfig } from \"@revstackhq/core\";\nimport { features } from \"./revstack/features\";\nimport { plans } from \"./revstack/plans\";\n\nexport default defineConfig({\n features,\n plans,\n});\n`;\n\nexport const initCommand = new Command(\"init\")\n .description(\"Scaffold a new revstack.config.ts in the current directory\")\n .action(async () => {\n const cwd = process.cwd();\n const configPath = path.resolve(cwd, \"revstack.config.ts\");\n const revstackDir = path.resolve(cwd, \"revstack\");\n const featuresPath = path.resolve(revstackDir, \"features.ts\");\n const plansPath = path.resolve(revstackDir, \"plans.ts\");\n\n if (fs.existsSync(configPath)) {\n console.log(\n \"\\n\" +\n chalk.yellow(\" ⚠ revstack.config.ts already exists.\\n\") +\n chalk.dim(\" Delete it first if you want to start fresh.\\n\"),\n );\n process.exit(1);\n }\n\n // Step 1: Create revstack directory and files\n if (!fs.existsSync(revstackDir)) {\n fs.mkdirSync(revstackDir, { recursive: true });\n }\n fs.writeFileSync(featuresPath, STARTER_FEATURES, \"utf-8\");\n fs.writeFileSync(plansPath, STARTER_PLANS, \"utf-8\");\n fs.writeFileSync(configPath, STARTER_CONFIG, \"utf-8\");\n\n // Step 2: Detect package manager & verify package.json\n let packageManager = \"npm\";\n if (fs.existsSync(path.resolve(cwd, \"pnpm-lock.yaml\"))) {\n packageManager = \"pnpm\";\n } else if (fs.existsSync(path.resolve(cwd, \"yarn.lock\"))) {\n packageManager = \"yarn\";\n } else if (fs.existsSync(path.resolve(cwd, \"package-lock.json\"))) {\n packageManager = \"npm\";\n }\n\n const packageJsonPath = path.resolve(cwd, \"package.json\");\n if (!fs.existsSync(packageJsonPath)) {\n // Create a default package.json if it doesn't exist\n try {\n spawnSync(\"npm\", [\"init\", \"-y\"], { cwd, stdio: \"ignore\", shell: true });\n } catch (err) {\n // Ignore initialization errors; the install command may still work or provide a better error.\n }\n }\n\n // Step 3: Install @revstackhq/core\n const spinner = ora(\"Installing @revstackhq/core...\").start();\n let installFailed = false;\n\n try {\n const cliDir = path.dirname(fileURLToPath(import.meta.url));\n const pkgJsonPath = path.resolve(cliDir, \"../../package.json\");\n let cliVersion = \"dev\";\n try {\n const pkgData = fs.readFileSync(pkgJsonPath, \"utf-8\");\n cliVersion = JSON.parse(pkgData).version;\n } catch (e) {\n // Fallback\n }\n\n const tag = cliVersion.includes(\"dev\") ? `@${cliVersion}` : \"@latest\";\n const pkgName = `@revstackhq/core${tag}`;\n\n const installArgs =\n packageManager === \"yarn\"\n ? [\"add\", pkgName]\n : packageManager === \"pnpm\"\n ? [\"add\", pkgName]\n : [\"install\", pkgName];\n\n let result = spawnSync(packageManager, installArgs, { cwd, shell: true });\n if (result.error || result.status !== 0) {\n if (packageManager === \"pnpm\") {\n result = spawnSync(\"pnpm\", [\"add\", \"-w\", pkgName], {\n cwd,\n shell: true,\n });\n } else if (packageManager === \"yarn\") {\n result = spawnSync(\"yarn\", [\"add\", \"-W\", pkgName], {\n cwd,\n shell: true,\n });\n }\n }\n\n if (result.error || result.status !== 0) {\n throw new Error(\n \"Install failed: \" +\n (result.stderr\n ? result.stderr.toString()\n : result.stdout\n ? result.stdout.toString()\n : \"Unknown error\"),\n );\n }\n spinner.succeed(\"Dependencies installed\");\n } catch (err: any) {\n installFailed = true;\n spinner.fail(\n \"Failed to install @revstackhq/core automatically (\" +\n packageManager +\n \"). Reason: \" +\n err.message,\n );\n }\n\n // Step 4: Final Success Message\n console.log(\n \"\\n\" +\n chalk.green(\" ✔ Created revstack config structure\\n\") +\n \"\\n\" +\n chalk.dim(\" Includes the \") +\n chalk.white(\"Default Guest Plan\") +\n chalk.dim(\" (required by Revstack).\\n\") +\n \"\\n\" +\n chalk.dim(\" Next steps:\\n\") +\n (installFailed\n ? chalk.dim(\" 0. \") +\n chalk.white(\n \"Run \" +\n chalk.bold(packageManager + \" install @revstackhq/core\") +\n \" manually\\n\",\n )\n : \"\") +\n chalk.dim(\" 1. \") +\n chalk.white(\"Edit the config to match your billing model\\n\") +\n chalk.dim(\" 2. \") +\n chalk.white(\"Run \") +\n chalk.bold(\"revstack login\") +\n chalk.white(\" to authenticate\\n\") +\n chalk.dim(\" 3. \") +\n chalk.white(\"Run \") +\n chalk.bold(\"revstack push\") +\n chalk.white(\" to deploy\\n\"),\n );\n });\n"],"mappings":";;;AAOA,SAAS,eAAe;AACxB,OAAO,WAAW;AAClB,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,SAAS,iBAAiB;AAC1B,SAAS,qBAAqB;AAC9B,OAAO,SAAS;AAEhB,IAAM,mBAAmB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAgBzB,IAAM,gBAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAyCtB,IAAM,iBAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAUhB,IAAM,cAAc,IAAI,QAAQ,MAAM,EAC1C,YAAY,4DAA4D,EACxE,OAAO,YAAY;AAClB,QAAM,MAAM,QAAQ,IAAI;AACxB,QAAM,aAAa,KAAK,QAAQ,KAAK,oBAAoB;AACzD,QAAM,cAAc,KAAK,QAAQ,KAAK,UAAU;AAChD,QAAM,eAAe,KAAK,QAAQ,aAAa,aAAa;AAC5D,QAAM,YAAY,KAAK,QAAQ,aAAa,UAAU;AAEtD,MAAI,GAAG,WAAW,UAAU,GAAG;AAC7B,YAAQ;AAAA,MACN,OACE,MAAM,OAAO,+CAA0C,IACvD,MAAM,IAAI,mDAAmD;AAAA,IACjE;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB;AAGA,MAAI,CAAC,GAAG,WAAW,WAAW,GAAG;AAC/B,OAAG,UAAU,aAAa,EAAE,WAAW,KAAK,CAAC;AAAA,EAC/C;AACA,KAAG,cAAc,cAAc,kBAAkB,OAAO;AACxD,KAAG,cAAc,WAAW,eAAe,OAAO;AAClD,KAAG,cAAc,YAAY,gBAAgB,OAAO;AAGpD,MAAI,iBAAiB;AACrB,MAAI,GAAG,WAAW,KAAK,QAAQ,KAAK,gBAAgB,CAAC,GAAG;AACtD,qBAAiB;AAAA,EACnB,WAAW,GAAG,WAAW,KAAK,QAAQ,KAAK,WAAW,CAAC,GAAG;AACxD,qBAAiB;AAAA,EACnB,WAAW,GAAG,WAAW,KAAK,QAAQ,KAAK,mBAAmB,CAAC,GAAG;AAChE,qBAAiB;AAAA,EACnB;AAEA,QAAM,kBAAkB,KAAK,QAAQ,KAAK,cAAc;AACxD,MAAI,CAAC,GAAG,WAAW,eAAe,GAAG;AAEnC,QAAI;AACF,gBAAU,OAAO,CAAC,QAAQ,IAAI,GAAG,EAAE,KAAK,OAAO,UAAU,OAAO,KAAK,CAAC;AAAA,IACxE,SAAS,KAAK;AAAA,IAEd;AAAA,EACF;AAGA,QAAM,UAAU,IAAI,gCAAgC,EAAE,MAAM;AAC5D,MAAI,gBAAgB;AAEpB,MAAI;AACF,UAAM,SAAS,KAAK,QAAQ,cAAc,YAAY,GAAG,CAAC;AAC1D,UAAM,cAAc,KAAK,QAAQ,QAAQ,oBAAoB;AAC7D,QAAI,aAAa;AACjB,QAAI;AACF,YAAM,UAAU,GAAG,aAAa,aAAa,OAAO;AACpD,mBAAa,KAAK,MAAM,OAAO,EAAE;AAAA,IACnC,SAAS,GAAG;AAAA,IAEZ;AAEA,UAAM,MAAM,WAAW,SAAS,KAAK,IAAI,IAAI,UAAU,KAAK;AAC5D,UAAM,UAAU,mBAAmB,GAAG;AAEtC,UAAM,cACJ,mBAAmB,SACf,CAAC,OAAO,OAAO,IACf,mBAAmB,SACjB,CAAC,OAAO,OAAO,IACf,CAAC,WAAW,OAAO;AAE3B,QAAI,SAAS,UAAU,gBAAgB,aAAa,EAAE,KAAK,OAAO,KAAK,CAAC;AACxE,QAAI,OAAO,SAAS,OAAO,WAAW,GAAG;AACvC,UAAI,mBAAmB,QAAQ;AAC7B,iBAAS,UAAU,QAAQ,CAAC,OAAO,MAAM,OAAO,GAAG;AAAA,UACjD;AAAA,UACA,OAAO;AAAA,QACT,CAAC;AAAA,MACH,WAAW,mBAAmB,QAAQ;AACpC,iBAAS,UAAU,QAAQ,CAAC,OAAO,MAAM,OAAO,GAAG;AAAA,UACjD;AAAA,UACA,OAAO;AAAA,QACT,CAAC;AAAA,MACH;AAAA,IACF;AAEA,QAAI,OAAO,SAAS,OAAO,WAAW,GAAG;AACvC,YAAM,IAAI;AAAA,QACR,sBACG,OAAO,SACJ,OAAO,OAAO,SAAS,IACvB,OAAO,SACL,OAAO,OAAO,SAAS,IACvB;AAAA,MACV;AAAA,IACF;AACA,YAAQ,QAAQ,wBAAwB;AAAA,EAC1C,SAAS,KAAU;AACjB,oBAAgB;AAChB,YAAQ;AAAA,MACN,uDACE,iBACA,gBACA,IAAI;AAAA,IACR;AAAA,EACF;AAGA,UAAQ;AAAA,IACN,OACE,MAAM,MAAM,8CAAyC,IACrD,OACA,MAAM,IAAI,mBAAmB,IAC7B,MAAM,MAAM,oBAAoB,IAChC,MAAM,IAAI,4BAA4B,IACtC,OACA,MAAM,IAAI,mBAAmB,KAC5B,gBACG,MAAM,IAAI,SAAS,IACnB,MAAM;AAAA,MACJ,SACE,MAAM,KAAK,iBAAiB,2BAA2B,IACvD;AAAA,IACJ,IACA,MACJ,MAAM,IAAI,SAAS,IACnB,MAAM,MAAM,+CAA+C,IAC3D,MAAM,IAAI,SAAS,IACnB,MAAM,MAAM,MAAM,IAClB,MAAM,KAAK,gBAAgB,IAC3B,MAAM,MAAM,oBAAoB,IAChC,MAAM,IAAI,SAAS,IACnB,MAAM,MAAM,MAAM,IAClB,MAAM,KAAK,eAAe,IAC1B,MAAM,MAAM,cAAc;AAAA,EAC9B;AACF,CAAC;","names":[]}
1
+ {"version":3,"sources":["../../src/commands/init.ts","../../src/commands/templates/starter.ts","../../src/commands/templates/b2b-saas.ts","../../src/commands/templates/usage-based.ts","../../src/commands/templates/index.ts"],"sourcesContent":["/**\n * @file commands/init.ts\n * @description Scaffolds a new `revstack.config.ts` in the current directory.\n * Generates a starter config with the immutable Default Guest Plan and\n * a sample Pro plan using type-safe helpers from @revstackhq/core.\n */\n\nimport { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { spawnSync } from \"node:child_process\";\nimport { fileURLToPath } from \"node:url\";\nimport ora from \"ora\";\n\nimport { TEMPLATES } from \"./templates/index\";\n\nexport const initCommand = new Command(\"init\")\n .description(\"Scaffold a new revstack.config.ts in the current directory\")\n .option(\n \"-t, --template <name>\",\n \"Choose a starting template (starter, b2b-saas, usage-based)\",\n \"starter\",\n )\n .action(async (options) => {\n const templateName = options.template || \"starter\";\n const template = TEMPLATES[templateName];\n\n if (!template) {\n console.log(\n chalk.red(\n `\\n ✖ Unknown template \"${templateName}\". Available templates: ${Object.keys(TEMPLATES).join(\", \")}\\n`,\n ),\n );\n process.exit(1);\n }\n const cwd = process.cwd();\n const configPath = path.resolve(cwd, \"revstack.config.ts\");\n\n if (fs.existsSync(configPath)) {\n console.log(\n \"\\n\" +\n chalk.yellow(\" ⚠ revstack.config.ts already exists.\\n\") +\n chalk.dim(\" Delete it first if you want to start fresh.\\n\"),\n );\n process.exit(1);\n }\n\n // Step 1: Create revstack directory and config files\n const revstackDir = path.resolve(cwd, \"revstack\");\n if (!fs.existsSync(revstackDir)) {\n fs.mkdirSync(revstackDir, { recursive: true });\n }\n\n fs.writeFileSync(\n path.resolve(revstackDir, \"features.ts\"),\n template.features,\n \"utf-8\",\n );\n fs.writeFileSync(\n path.resolve(revstackDir, \"addons.ts\"),\n template.addons,\n \"utf-8\",\n );\n fs.writeFileSync(\n path.resolve(revstackDir, \"plans.ts\"),\n template.plans,\n \"utf-8\",\n );\n fs.writeFileSync(\n path.resolve(revstackDir, \"index.ts\"),\n template.index,\n \"utf-8\",\n );\n fs.writeFileSync(configPath, template.root, \"utf-8\");\n\n // Step 2: Detect package manager & verify package.json\n let packageManager = \"npm\";\n if (fs.existsSync(path.resolve(cwd, \"pnpm-lock.yaml\"))) {\n packageManager = \"pnpm\";\n } else if (fs.existsSync(path.resolve(cwd, \"yarn.lock\"))) {\n packageManager = \"yarn\";\n } else if (fs.existsSync(path.resolve(cwd, \"package-lock.json\"))) {\n packageManager = \"npm\";\n }\n\n const packageJsonPath = path.resolve(cwd, \"package.json\");\n if (!fs.existsSync(packageJsonPath)) {\n // Create a default package.json if it doesn't exist\n try {\n spawnSync(\"npm\", [\"init\", \"-y\"], { cwd, stdio: \"ignore\", shell: true });\n } catch (err) {\n // Ignore initialization errors; the install command may still work or provide a better error.\n }\n }\n\n // Step 3: Install @revstackhq/core\n const spinner = ora(\"Installing @revstackhq/core...\").start();\n let installFailed = false;\n\n try {\n const cliDir = path.dirname(fileURLToPath(import.meta.url));\n const pkgJsonPath = path.resolve(cliDir, \"../../package.json\");\n let cliVersion = \"dev\";\n try {\n const pkgData = fs.readFileSync(pkgJsonPath, \"utf-8\");\n cliVersion = JSON.parse(pkgData).version;\n } catch (e) {\n // Fallback\n }\n\n const tag = cliVersion.includes(\"dev\") ? `@${cliVersion}` : \"@latest\";\n const pkgName = `@revstackhq/core${tag}`;\n\n const installArgs =\n packageManager === \"yarn\"\n ? [\"add\", pkgName]\n : packageManager === \"pnpm\"\n ? [\"add\", pkgName]\n : [\"install\", pkgName];\n\n let result = spawnSync(packageManager, installArgs, { cwd, shell: true });\n if (result.error || result.status !== 0) {\n if (packageManager === \"pnpm\") {\n result = spawnSync(\"pnpm\", [\"add\", \"-w\", pkgName], {\n cwd,\n shell: true,\n });\n } else if (packageManager === \"yarn\") {\n result = spawnSync(\"yarn\", [\"add\", \"-W\", pkgName], {\n cwd,\n shell: true,\n });\n }\n }\n\n if (result.error || result.status !== 0) {\n throw new Error(\n \"Install failed: \" +\n (result.stderr\n ? result.stderr.toString()\n : result.stdout\n ? result.stdout.toString()\n : \"Unknown error\"),\n );\n }\n spinner.succeed(\"Dependencies installed\");\n } catch (err: any) {\n installFailed = true;\n spinner.fail(\n \"Failed to install @revstackhq/core automatically (\" +\n packageManager +\n \"). Reason: \" +\n err.message,\n );\n }\n\n // Step 4: Final Success Message\n console.log(\n \"\\n\" +\n chalk.green(\" ✔ Created revstack config structure\\n\") +\n \"\\n\" +\n chalk.dim(\" Includes the \") +\n chalk.white(\"Default Guest Plan\") +\n chalk.dim(\" (required by Revstack).\\n\") +\n \"\\n\" +\n chalk.dim(\" Next steps:\\n\") +\n (installFailed\n ? chalk.dim(\" 0. \") +\n chalk.white(\n \"Run \" +\n chalk.bold(packageManager + \" install @revstackhq/core\") +\n \" manually\\n\",\n )\n : \"\") +\n chalk.dim(\" 1. \") +\n chalk.white(\"Edit the config to match your billing model\\n\") +\n chalk.dim(\" 2. \") +\n chalk.white(\"Run \") +\n chalk.bold(\"revstack login\") +\n chalk.white(\" to authenticate\\n\") +\n chalk.dim(\" 3. \") +\n chalk.white(\"Run \") +\n chalk.bold(\"revstack push\") +\n chalk.white(\" to deploy\\n\"),\n );\n });\n","export interface TemplateConfig {\r\n features: string;\r\n addons: string;\r\n plans: string;\r\n index: string;\r\n root: string;\r\n}\r\n\r\nexport const starter: TemplateConfig = {\r\n features: `import { defineFeature } from \"@revstackhq/core\";\r\n\r\nexport const features = {\r\n seats: defineFeature({ name: \"Seats\", type: \"static\", unit_type: \"count\" }),\r\n priority_support: defineFeature({ name: \"Priority Support\", type: \"boolean\", unit_type: \"custom\" }),\r\n};\r\n`,\r\n addons: `import { defineAddon } from \"@revstackhq/core\";\r\nimport { features } from \"./features\";\r\n\r\nexport const addons = {\r\n extra_seats: defineAddon<typeof features>({\r\n name: \"5 Extra Seats\",\r\n description: \"Add 5 more team members to your workspace.\",\r\n type: \"recurring\",\r\n prices: [\r\n { amount: 1500, currency: \"USD\", billing_interval: \"monthly\" }\r\n ],\r\n features: {\r\n seats: { value_limit: 5, type: \"increment\", is_hard_limit: false },\r\n }\r\n }),\r\n vip_support: defineAddon<typeof features>({\r\n name: \"Priority Support\",\r\n description: \"24/7 Slack channel support.\",\r\n type: \"recurring\",\r\n prices: [\r\n { amount: 9900, currency: \"USD\", billing_interval: \"monthly\" }\r\n ],\r\n features: {\r\n priority_support: { has_access: true },\r\n }\r\n })\r\n};\r\n`,\r\n plans: `import { definePlan } from \"@revstackhq/core\";\r\nimport { features } from \"./features\";\r\n\r\nexport const plans = {\r\n // DO NOT DELETE: Automatically created default plan for guests.\r\n default: definePlan<typeof features>({\r\n name: \"Default\",\r\n description: \"Automatically created default plan for guests.\",\r\n is_default: true,\r\n is_public: false,\r\n type: \"free\",\r\n features: {},\r\n }),\r\n pro: definePlan<typeof features>({\r\n name: \"Pro\",\r\n description: \"For professional teams.\",\r\n is_default: false,\r\n is_public: true,\r\n type: \"paid\",\r\n available_addons: [\"extra_seats\", \"vip_support\"],\r\n prices: [\r\n { amount: 2900, currency: \"USD\", billing_interval: \"monthly\", trial_period_days: 14 }\r\n ],\r\n features: {\r\n seats: { value_limit: 5, is_hard_limit: true },\r\n },\r\n }),\r\n};\r\n`,\r\n index: `import { defineConfig } from \"@revstackhq/core\";\r\nimport { features } from \"./features\";\r\nimport { addons } from \"./addons\";\r\nimport { plans } from \"./plans\";\r\n\r\nexport default defineConfig({\r\n features,\r\n addons,\r\n plans,\r\n});\r\n`,\r\n root: `import config from \"./revstack\";\r\n\r\nexport default config;\r\n`,\r\n};\r\n","import { TemplateConfig } from \"./starter\";\r\n\r\nexport const b2bSaas: TemplateConfig = {\r\n features: `import { defineFeature } from \"@revstackhq/core\";\r\n\r\nexport const features = {\r\n active_users: defineFeature({ name: \"Active Users\", type: \"static\", unit_type: \"count\" }),\r\n api_access: defineFeature({ name: \"API Access\", type: \"boolean\", unit_type: \"custom\" }),\r\n custom_domain: defineFeature({ name: \"Custom Domain\", type: \"boolean\", unit_type: \"custom\" }),\r\n};\r\n`,\r\n addons: `import { defineAddon } from \"@revstackhq/core\";\r\nimport { features } from \"./features\";\r\n\r\nexport const addons = {\r\n extra_users: defineAddon<typeof features>({\r\n name: \"10 Extra Users\",\r\n description: \"Add 10 more active users to your workspace.\",\r\n type: \"recurring\",\r\n prices: [\r\n { amount: 5000, currency: \"USD\", billing_interval: \"monthly\" }\r\n ],\r\n features: {\r\n active_users: { value_limit: 10, type: \"increment\", is_hard_limit: true },\r\n }\r\n }),\r\n dedicated_support: defineAddon<typeof features>({\r\n name: \"Dedicated Support\",\r\n description: \"Enterprise SLA with 1-hour response time.\",\r\n type: \"recurring\",\r\n prices: [\r\n { amount: 49900, currency: \"USD\", billing_interval: \"monthly\" }\r\n ],\r\n features: {}\r\n })\r\n};\r\n`,\r\n plans: `import { definePlan } from \"@revstackhq/core\";\r\nimport { features } from \"./features\";\r\n\r\nexport const plans = {\r\n default: definePlan<typeof features>({\r\n name: \"Default\",\r\n description: \"Automatically created default plan for guests.\",\r\n is_default: true,\r\n is_public: false,\r\n type: \"free\",\r\n features: {},\r\n }),\r\n startup: definePlan<typeof features>({\r\n name: \"Startup\",\r\n description: \"For small teams getting started.\",\r\n is_default: false,\r\n is_public: true,\r\n type: \"paid\",\r\n available_addons: [\"extra_users\"],\r\n prices: [\r\n { amount: 9900, currency: \"USD\", billing_interval: \"monthly\" }\r\n ],\r\n features: {\r\n active_users: { value_limit: 10, is_hard_limit: true },\r\n api_access: { value_bool: false },\r\n custom_domain: { value_bool: false },\r\n },\r\n }),\r\n enterprise: definePlan<typeof features>({\r\n name: \"Enterprise\",\r\n description: \"Advanced features for scale.\",\r\n is_default: false,\r\n is_public: true,\r\n type: \"paid\",\r\n available_addons: [\"extra_users\", \"dedicated_support\"],\r\n prices: [\r\n { amount: 49900, currency: \"USD\", billing_interval: \"monthly\" }\r\n ],\r\n features: {\r\n active_users: { value_limit: 100, is_hard_limit: false },\r\n api_access: { value_bool: true },\r\n custom_domain: { value_bool: true },\r\n },\r\n }),\r\n};\r\n`,\r\n index: `import { defineConfig } from \"@revstackhq/core\";\r\nimport { features } from \"./features\";\r\nimport { addons } from \"./addons\";\r\nimport { plans } from \"./plans\";\r\n\r\nexport default defineConfig({\r\n features,\r\n addons,\r\n plans,\r\n});\r\n`,\r\n root: `import config from \"./revstack\";\r\n\r\nexport default config;\r\n`,\r\n};\r\n","import { TemplateConfig } from \"./starter\";\r\n\r\nexport const usageBased: TemplateConfig = {\r\n features: `import { defineFeature } from \"@revstackhq/core\";\r\n\r\nexport const features = {\r\n api_requests: defineFeature({ name: \"API Requests\", type: \"metered\", unit_type: \"requests\" }),\r\n storage_gb: defineFeature({ name: \"Storage (GB)\", type: \"metered\", unit_type: \"custom\" }),\r\n};\r\n`,\r\n addons: `import { defineAddon } from \"@revstackhq/core\";\r\nimport { features } from \"./features\";\r\n\r\nexport const addons = {\r\n premium_support: defineAddon<typeof features>({\r\n name: \"Premium Support\",\r\n description: \"24/7 dedicated support.\",\r\n type: \"recurring\",\r\n prices: [\r\n { amount: 20000, currency: \"USD\", billing_interval: \"monthly\" }\r\n ],\r\n features: {}\r\n })\r\n};\r\n`,\r\n plans: `import { definePlan } from \"@revstackhq/core\";\r\nimport { features } from \"./features\";\r\n\r\nexport const plans = {\r\n default: definePlan<typeof features>({\r\n name: \"Default\",\r\n description: \"Automatically created default plan for guests.\",\r\n is_default: true,\r\n is_public: false,\r\n type: \"free\",\r\n features: {},\r\n }),\r\n pay_as_you_go: definePlan<typeof features>({\r\n name: \"Pay As You Go\",\r\n description: \"Flexible usage-based pricing.\",\r\n is_default: false,\r\n is_public: true,\r\n type: \"paid\",\r\n available_addons: [\"premium_support\"],\r\n prices: [\r\n { amount: 0, currency: \"USD\", billing_interval: \"monthly\" } // Base platform fee\r\n ],\r\n features: {\r\n api_requests: { value_limit: 10000, is_hard_limit: false, reset_period: \"monthly\" }, // 10k free requests per month\r\n storage_gb: { value_limit: 5, is_hard_limit: false, reset_period: \"never\" }, // 5GB free storage lifetime\r\n },\r\n }),\r\n};\r\n`,\r\n index: `import { defineConfig } from \"@revstackhq/core\";\r\nimport { features } from \"./features\";\r\nimport { addons } from \"./addons\";\r\nimport { plans } from \"./plans\";\r\n\r\nexport default defineConfig({\r\n features,\r\n addons,\r\n plans,\r\n});\r\n`,\r\n root: `import config from \"./revstack\";\r\n\r\nexport default config;\r\n`,\r\n};\r\n","import { starter } from \"./starter\";\r\nimport { b2bSaas } from \"./b2b-saas\";\r\nimport { usageBased } from \"./usage-based\";\r\nimport type { TemplateConfig } from \"./starter\";\r\n\r\nexport const TEMPLATES: Record<string, TemplateConfig> = {\r\n starter: starter,\r\n \"b2b-saas\": b2bSaas,\r\n \"usage-based\": usageBased,\r\n};\r\n\r\nexport type { TemplateConfig };\r\n"],"mappings":"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","names":[]}
@@ -54,6 +54,7 @@ async function loadLocalConfig(cwd) {
54
54
  }
55
55
 
56
56
  // src/commands/push.ts
57
+ import { validateConfig, RevstackValidationError } from "@revstackhq/core";
57
58
  var API_BASE = "https://app.revstack.dev";
58
59
  var DIFF_ICONS = {
59
60
  added: chalk2.green(" + "),
@@ -96,6 +97,37 @@ function requireAuth() {
96
97
  var pushCommand = new Command("push").description("Push your local billing config to Revstack Cloud").option("-e, --env <environment>", "Target environment", "test").action(async (options) => {
97
98
  const apiKey = requireAuth();
98
99
  const config = await loadLocalConfig(process.cwd());
100
+ const validationSpinner = ora({
101
+ text: "Validating billing configuration...",
102
+ prefixText: " "
103
+ }).start();
104
+ try {
105
+ validateConfig(config);
106
+ validationSpinner.succeed("Configuration validated");
107
+ } catch (error) {
108
+ if (error instanceof RevstackValidationError || error?.name === "RevstackValidationError") {
109
+ validationSpinner.fail("Configuration invalid");
110
+ console.error(
111
+ chalk2.red(
112
+ "\n \u2716 The billing configuration contains business logic errors:\n"
113
+ )
114
+ );
115
+ for (const err of error.errors || []) {
116
+ console.error(chalk2.red(` \u2022 ${err}`));
117
+ }
118
+ console.log();
119
+ process.exit(1);
120
+ }
121
+ validationSpinner.fail("Validation failed");
122
+ console.error(
123
+ chalk2.red(
124
+ `
125
+ An unexpected error occurred during validation: ${error?.message || String(error)}
126
+ `
127
+ )
128
+ );
129
+ process.exit(1);
130
+ }
99
131
  const spinner = ora({
100
132
  text: "Calculating diff...",
101
133
  prefixText: " "
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/commands/push.ts","../../src/utils/auth.ts","../../src/utils/config-loader.ts"],"sourcesContent":["/**\n * @file commands/push.ts\n * @description The core deployment command. Loads the local config, sends it\n * to Revstack Cloud for diffing, presents the changes, and (upon confirmation)\n * pushes the config to production.\n */\n\nimport { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport prompts from \"prompts\";\nimport ora from \"ora\";\nimport { getApiKey } from \"@/utils/auth\";\nimport { loadLocalConfig } from \"@/utils/config-loader\";\n\n// ─── Types ───────────────────────────────────────────────────\n\ninterface DiffEntry {\n action: \"added\" | \"removed\" | \"updated\";\n entity: string;\n id: string;\n message: string;\n}\n\ninterface DiffResponse {\n diff: DiffEntry[];\n canPush: boolean;\n blockedReason?: string;\n}\n\n// ─── Helpers ─────────────────────────────────────────────────\n\nconst API_BASE = \"https://app.revstack.dev\";\n\nconst DIFF_ICONS: Record<DiffEntry[\"action\"], string> = {\n added: chalk.green(\" + \"),\n removed: chalk.red(\" − \"),\n updated: chalk.yellow(\" ~ \"),\n};\n\nconst DIFF_COLORS: Record<DiffEntry[\"action\"], (text: string) => string> = {\n added: chalk.green,\n removed: chalk.red,\n updated: chalk.yellow,\n};\n\nfunction printDiff(diff: DiffEntry[]): void {\n if (diff.length === 0) {\n console.log(\n chalk.dim(\"\\n No changes detected. Your config is up to date.\\n\"),\n );\n return;\n }\n\n console.log(chalk.bold(\"\\n Changes:\\n\"));\n\n for (const entry of diff) {\n const icon = DIFF_ICONS[entry.action];\n const color = DIFF_COLORS[entry.action];\n const label = chalk.dim(`[${entry.entity}]`);\n console.log(\n `${icon}${color(entry.id)} ${label} ${chalk.white(entry.message)}`,\n );\n }\n\n console.log();\n}\n\nfunction requireAuth(): string {\n const apiKey = getApiKey();\n\n if (!apiKey) {\n console.error(\n \"\\n\" +\n chalk.red(\" ✖ Not authenticated.\\n\") +\n chalk.dim(\" Run \") +\n chalk.bold(\"revstack login\") +\n chalk.dim(\" first.\\n\"),\n );\n process.exit(1);\n }\n\n return apiKey;\n}\n\n// ─── Command ─────────────────────────────────────────────────\n\nexport const pushCommand = new Command(\"push\")\n .description(\"Push your local billing config to Revstack Cloud\")\n .option(\"-e, --env <environment>\", \"Target environment\", \"test\")\n .action(async (options: { env: string }) => {\n const apiKey = requireAuth();\n const config = await loadLocalConfig(process.cwd());\n\n // ── Step 1: Compute diff ──────────────────────────────────\n\n const spinner = ora({\n text: \"Calculating diff...\",\n prefixText: \" \",\n }).start();\n\n let diffResponse: DiffResponse;\n\n try {\n const res = await fetch(`${API_BASE}/api/v1/cli/diff`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify({ env: options.env, config }),\n });\n\n if (!res.ok) {\n spinner.fail(\"Failed to calculate diff\");\n console.error(\n chalk.red(`\\n API returned ${res.status}: ${res.statusText}\\n`),\n );\n process.exit(1);\n }\n\n diffResponse = (await res.json()) as DiffResponse;\n spinner.succeed(\"Diff calculated\");\n } catch (error: unknown) {\n spinner.fail(\"Failed to reach Revstack Cloud\");\n console.error(chalk.red(`\\n ${(error as Error).message}\\n`));\n process.exit(1);\n }\n\n // ── Step 2: Present diff ──────────────────────────────────\n\n printDiff(diffResponse.diff);\n\n if (diffResponse.diff.length === 0) {\n return;\n }\n\n // ── Step 3: Check if push is allowed ──────────────────────\n\n if (!diffResponse.canPush) {\n console.log(\n chalk.red(\" ✖ Push is blocked.\\n\") +\n chalk.dim(\n ` ${diffResponse.blockedReason ?? \"The server rejected this configuration.\"}\\n`,\n ),\n );\n process.exit(1);\n }\n\n // ── Step 4: Confirm ───────────────────────────────────────\n\n const envLabel =\n options.env === \"production\"\n ? chalk.red.bold(options.env)\n : chalk.cyan.bold(options.env);\n\n const { confirm } = await prompts({\n type: \"confirm\",\n name: \"confirm\",\n message: `Apply these changes to ${envLabel}?`,\n initial: false,\n });\n\n if (!confirm) {\n console.log(chalk.dim(\"\\n Push cancelled.\\n\"));\n return;\n }\n\n // ── Step 5: Push ──────────────────────────────────────────\n\n const pushSpinner = ora({\n text: `Pushing to ${options.env}...`,\n prefixText: \" \",\n }).start();\n\n try {\n const res = await fetch(`${API_BASE}/api/v1/cli/push`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify({ env: options.env, config }),\n });\n\n if (!res.ok) {\n pushSpinner.fail(\"Push failed\");\n console.error(\n chalk.red(`\\n API returned ${res.status}: ${res.statusText}\\n`),\n );\n process.exit(1);\n }\n\n pushSpinner.succeed(\"Pushed successfully\");\n console.log(\n \"\\n\" +\n chalk.green(\" ✔ Config deployed to \") +\n envLabel +\n \"\\n\" +\n chalk.dim(\" Changes are now live.\\n\"),\n );\n } catch (error: unknown) {\n pushSpinner.fail(\"Push failed\");\n console.error(chalk.red(`\\n ${(error as Error).message}\\n`));\n process.exit(1);\n }\n });\n","/**\n * @file utils/auth.ts\n * @description Manages global Revstack credentials stored at ~/.revstack/credentials.json.\n * Provides simple get/set helpers for the API key used by all authenticated commands.\n */\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport os from \"node:os\";\n\nconst CREDENTIALS_DIR = path.join(os.homedir(), \".revstack\");\nconst CREDENTIALS_FILE = path.join(CREDENTIALS_DIR, \"credentials.json\");\n\ninterface Credentials {\n apiKey: string;\n}\n\n/**\n * Persist an API key to the global credentials file.\n * Creates `~/.revstack/` if it doesn't exist.\n */\nexport function setApiKey(key: string): void {\n if (!fs.existsSync(CREDENTIALS_DIR)) {\n fs.mkdirSync(CREDENTIALS_DIR, { recursive: true });\n }\n\n const credentials: Credentials = { apiKey: key };\n fs.writeFileSync(\n CREDENTIALS_FILE,\n JSON.stringify(credentials, null, 2),\n \"utf-8\",\n );\n}\n\n/**\n * Read the stored API key, or return `null` if none is configured.\n */\nexport function getApiKey(): string | null {\n if (!fs.existsSync(CREDENTIALS_FILE)) {\n return null;\n }\n\n try {\n const raw = fs.readFileSync(CREDENTIALS_FILE, \"utf-8\");\n const credentials: Credentials = JSON.parse(raw);\n return credentials.apiKey ?? null;\n } catch {\n return null;\n }\n}\n\n/**\n * Remove stored credentials. Used by `revstack logout`.\n */\nexport function clearApiKey(): void {\n if (fs.existsSync(CREDENTIALS_FILE)) {\n fs.unlinkSync(CREDENTIALS_FILE);\n }\n}\n","/**\n * @file utils/config-loader.ts\n * @description Loads and evaluates the user's `revstack.config.ts` at runtime\n * using jiti (just-in-time TypeScript compilation). Returns a sanitized,\n * JSON-safe representation of the config for network transmission.\n */\n\nimport { createJiti } from \"jiti\";\nimport path from \"node:path\";\nimport chalk from \"chalk\";\n\n/**\n * Load the `revstack.config.ts` from the given directory.\n *\n * @param cwd - The directory to search for `revstack.config.ts`.\n * @returns The parsed and sanitized configuration object.\n */\nexport async function loadLocalConfig(\n cwd: string,\n): Promise<Record<string, unknown>> {\n const configPath = path.resolve(cwd, \"revstack.config.ts\");\n\n try {\n const jiti = createJiti(cwd);\n const module = (await jiti.import(configPath)) as Record<string, unknown>;\n const config = (module.default ?? module) as Record<string, unknown>;\n\n // Sanitize: strip functions, class instances, and non-serializable data.\n // This ensures we only send plain JSON to the Revstack Cloud API.\n return JSON.parse(JSON.stringify(config));\n } catch (error: unknown) {\n const err = error as NodeJS.ErrnoException;\n\n if (\n err.code === \"ERR_MODULE_NOT_FOUND\" ||\n err.code === \"ENOENT\" ||\n err.code === \"MODULE_NOT_FOUND\"\n ) {\n console.error(\n chalk.red(\n \"\\n ✖ Could not find revstack.config.ts in the current directory.\\n\",\n ) +\n chalk.dim(\" Run \") +\n chalk.bold(\"revstack init\") +\n chalk.dim(\" to create one.\\n\"),\n );\n } else {\n console.error(\n chalk.red(\"\\n ✖ Failed to parse revstack.config.ts\\n\") +\n chalk.dim(\" \" + (err.message ?? String(error))) +\n \"\\n\",\n );\n }\n\n process.exit(1);\n }\n}\n"],"mappings":"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","names":["chalk","path","chalk"]}
1
+ {"version":3,"sources":["../../src/commands/push.ts","../../src/utils/auth.ts","../../src/utils/config-loader.ts"],"sourcesContent":["/**\n * @file commands/push.ts\n * @description The core deployment command. Loads the local config, sends it\n * to Revstack Cloud for diffing, presents the changes, and (upon confirmation)\n * pushes the config to production.\n */\n\nimport { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport prompts from \"prompts\";\nimport ora from \"ora\";\nimport { getApiKey } from \"@/utils/auth\";\nimport { loadLocalConfig } from \"@/utils/config-loader\";\nimport { validateConfig, RevstackValidationError } from \"@revstackhq/core\";\n\n// ─── Types ───────────────────────────────────────────────────\n\ninterface DiffEntry {\n action: \"added\" | \"removed\" | \"updated\";\n entity: string;\n id: string;\n message: string;\n}\n\ninterface DiffResponse {\n diff: DiffEntry[];\n canPush: boolean;\n blockedReason?: string;\n}\n\n// ─── Helpers ─────────────────────────────────────────────────\n\nconst API_BASE = \"https://app.revstack.dev\";\n\nconst DIFF_ICONS: Record<DiffEntry[\"action\"], string> = {\n added: chalk.green(\" + \"),\n removed: chalk.red(\" − \"),\n updated: chalk.yellow(\" ~ \"),\n};\n\nconst DIFF_COLORS: Record<DiffEntry[\"action\"], (text: string) => string> = {\n added: chalk.green,\n removed: chalk.red,\n updated: chalk.yellow,\n};\n\nfunction printDiff(diff: DiffEntry[]): void {\n if (diff.length === 0) {\n console.log(\n chalk.dim(\"\\n No changes detected. Your config is up to date.\\n\"),\n );\n return;\n }\n\n console.log(chalk.bold(\"\\n Changes:\\n\"));\n\n for (const entry of diff) {\n const icon = DIFF_ICONS[entry.action];\n const color = DIFF_COLORS[entry.action];\n const label = chalk.dim(`[${entry.entity}]`);\n console.log(\n `${icon}${color(entry.id)} ${label} ${chalk.white(entry.message)}`,\n );\n }\n\n console.log();\n}\n\nfunction requireAuth(): string {\n const apiKey = getApiKey();\n\n if (!apiKey) {\n console.error(\n \"\\n\" +\n chalk.red(\" ✖ Not authenticated.\\n\") +\n chalk.dim(\" Run \") +\n chalk.bold(\"revstack login\") +\n chalk.dim(\" first.\\n\"),\n );\n process.exit(1);\n }\n\n return apiKey;\n}\n\n// ─── Command ─────────────────────────────────────────────────\n\nexport const pushCommand = new Command(\"push\")\n .description(\"Push your local billing config to Revstack Cloud\")\n .option(\"-e, --env <environment>\", \"Target environment\", \"test\")\n .action(async (options: { env: string }) => {\n const apiKey = requireAuth();\n const config = await loadLocalConfig(process.cwd());\n\n // ── Step 1: Validate config ────────────────────────────────\n\n const validationSpinner = ora({\n text: \"Validating billing configuration...\",\n prefixText: \" \",\n }).start();\n\n try {\n validateConfig(config as any); // cast to match RevstackConfig expected by validateConfig\n validationSpinner.succeed(\"Configuration validated\");\n } catch (error: any) {\n if (\n error instanceof RevstackValidationError ||\n error?.name === \"RevstackValidationError\"\n ) {\n validationSpinner.fail(\"Configuration invalid\");\n console.error(\n chalk.red(\n \"\\n ✖ The billing configuration contains business logic errors:\\n\",\n ),\n );\n for (const err of error.errors || []) {\n console.error(chalk.red(` • ${err}`));\n }\n console.log();\n process.exit(1);\n }\n\n validationSpinner.fail(\"Validation failed\");\n console.error(\n chalk.red(\n `\\n An unexpected error occurred during validation: ${error?.message || String(error)}\\n`,\n ),\n );\n process.exit(1);\n }\n\n // ── Step 2: Compute diff ──────────────────────────────────\n\n const spinner = ora({\n text: \"Calculating diff...\",\n prefixText: \" \",\n }).start();\n\n let diffResponse: DiffResponse;\n\n try {\n const res = await fetch(`${API_BASE}/api/v1/cli/diff`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify({ env: options.env, config }),\n });\n\n if (!res.ok) {\n spinner.fail(\"Failed to calculate diff\");\n console.error(\n chalk.red(`\\n API returned ${res.status}: ${res.statusText}\\n`),\n );\n process.exit(1);\n }\n\n diffResponse = (await res.json()) as DiffResponse;\n spinner.succeed(\"Diff calculated\");\n } catch (error: unknown) {\n spinner.fail(\"Failed to reach Revstack Cloud\");\n console.error(chalk.red(`\\n ${(error as Error).message}\\n`));\n process.exit(1);\n }\n\n // ── Step 2: Present diff ──────────────────────────────────\n\n printDiff(diffResponse.diff);\n\n if (diffResponse.diff.length === 0) {\n return;\n }\n\n // ── Step 3: Check if push is allowed ──────────────────────\n\n if (!diffResponse.canPush) {\n console.log(\n chalk.red(\" ✖ Push is blocked.\\n\") +\n chalk.dim(\n ` ${diffResponse.blockedReason ?? \"The server rejected this configuration.\"}\\n`,\n ),\n );\n process.exit(1);\n }\n\n // ── Step 4: Confirm ───────────────────────────────────────\n\n const envLabel =\n options.env === \"production\"\n ? chalk.red.bold(options.env)\n : chalk.cyan.bold(options.env);\n\n const { confirm } = await prompts({\n type: \"confirm\",\n name: \"confirm\",\n message: `Apply these changes to ${envLabel}?`,\n initial: false,\n });\n\n if (!confirm) {\n console.log(chalk.dim(\"\\n Push cancelled.\\n\"));\n return;\n }\n\n // ── Step 5: Push ──────────────────────────────────────────\n\n const pushSpinner = ora({\n text: `Pushing to ${options.env}...`,\n prefixText: \" \",\n }).start();\n\n try {\n const res = await fetch(`${API_BASE}/api/v1/cli/push`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify({ env: options.env, config }),\n });\n\n if (!res.ok) {\n pushSpinner.fail(\"Push failed\");\n console.error(\n chalk.red(`\\n API returned ${res.status}: ${res.statusText}\\n`),\n );\n process.exit(1);\n }\n\n pushSpinner.succeed(\"Pushed successfully\");\n console.log(\n \"\\n\" +\n chalk.green(\" ✔ Config deployed to \") +\n envLabel +\n \"\\n\" +\n chalk.dim(\" Changes are now live.\\n\"),\n );\n } catch (error: unknown) {\n pushSpinner.fail(\"Push failed\");\n console.error(chalk.red(`\\n ${(error as Error).message}\\n`));\n process.exit(1);\n }\n });\n","/**\n * @file utils/auth.ts\n * @description Manages global Revstack credentials stored at ~/.revstack/credentials.json.\n * Provides simple get/set helpers for the API key used by all authenticated commands.\n */\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport os from \"node:os\";\n\nconst CREDENTIALS_DIR = path.join(os.homedir(), \".revstack\");\nconst CREDENTIALS_FILE = path.join(CREDENTIALS_DIR, \"credentials.json\");\n\ninterface Credentials {\n apiKey: string;\n}\n\n/**\n * Persist an API key to the global credentials file.\n * Creates `~/.revstack/` if it doesn't exist.\n */\nexport function setApiKey(key: string): void {\n if (!fs.existsSync(CREDENTIALS_DIR)) {\n fs.mkdirSync(CREDENTIALS_DIR, { recursive: true });\n }\n\n const credentials: Credentials = { apiKey: key };\n fs.writeFileSync(\n CREDENTIALS_FILE,\n JSON.stringify(credentials, null, 2),\n \"utf-8\",\n );\n}\n\n/**\n * Read the stored API key, or return `null` if none is configured.\n */\nexport function getApiKey(): string | null {\n if (!fs.existsSync(CREDENTIALS_FILE)) {\n return null;\n }\n\n try {\n const raw = fs.readFileSync(CREDENTIALS_FILE, \"utf-8\");\n const credentials: Credentials = JSON.parse(raw);\n return credentials.apiKey ?? null;\n } catch {\n return null;\n }\n}\n\n/**\n * Remove stored credentials. Used by `revstack logout`.\n */\nexport function clearApiKey(): void {\n if (fs.existsSync(CREDENTIALS_FILE)) {\n fs.unlinkSync(CREDENTIALS_FILE);\n }\n}\n","/**\n * @file utils/config-loader.ts\n * @description Loads and evaluates the user's `revstack.config.ts` at runtime\n * using jiti (just-in-time TypeScript compilation). Returns a sanitized,\n * JSON-safe representation of the config for network transmission.\n */\n\nimport { createJiti } from \"jiti\";\nimport path from \"node:path\";\nimport chalk from \"chalk\";\n\n/**\n * Load the `revstack.config.ts` from the given directory.\n *\n * @param cwd - The directory to search for `revstack.config.ts`.\n * @returns The parsed and sanitized configuration object.\n */\nexport async function loadLocalConfig(\n cwd: string,\n): Promise<Record<string, unknown>> {\n const configPath = path.resolve(cwd, \"revstack.config.ts\");\n\n try {\n const jiti = createJiti(cwd);\n const module = (await jiti.import(configPath)) as Record<string, unknown>;\n const config = (module.default ?? module) as Record<string, unknown>;\n\n // Sanitize: strip functions, class instances, and non-serializable data.\n // This ensures we only send plain JSON to the Revstack Cloud API.\n return JSON.parse(JSON.stringify(config));\n } catch (error: unknown) {\n const err = error as NodeJS.ErrnoException;\n\n if (\n err.code === \"ERR_MODULE_NOT_FOUND\" ||\n err.code === \"ENOENT\" ||\n err.code === \"MODULE_NOT_FOUND\"\n ) {\n console.error(\n chalk.red(\n \"\\n ✖ Could not find revstack.config.ts in the current directory.\\n\",\n ) +\n chalk.dim(\" Run \") +\n chalk.bold(\"revstack init\") +\n chalk.dim(\" to create one.\\n\"),\n );\n } else {\n console.error(\n chalk.red(\"\\n ✖ Failed to parse revstack.config.ts\\n\") +\n chalk.dim(\" \" + (err.message ?? String(error))) +\n \"\\n\",\n );\n }\n\n process.exit(1);\n }\n}\n"],"mappings":"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","names":["chalk","path","chalk"]}
@@ -0,0 +1,5 @@
1
+ import { TemplateConfig } from './starter.js';
2
+
3
+ declare const b2bSaas: TemplateConfig;
4
+
5
+ export { b2bSaas };
@@ -0,0 +1,104 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/commands/templates/b2b-saas.ts
4
+ var b2bSaas = {
5
+ features: `import { defineFeature } from "@revstackhq/core";
6
+
7
+ export const features = {
8
+ active_users: defineFeature({ name: "Active Users", type: "static", unit_type: "count" }),
9
+ api_access: defineFeature({ name: "API Access", type: "boolean", unit_type: "custom" }),
10
+ custom_domain: defineFeature({ name: "Custom Domain", type: "boolean", unit_type: "custom" }),
11
+ };
12
+ `,
13
+ addons: `import { defineAddon } from "@revstackhq/core";
14
+ import { features } from "./features";
15
+
16
+ export const addons = {
17
+ extra_users: defineAddon<typeof features>({
18
+ name: "10 Extra Users",
19
+ description: "Add 10 more active users to your workspace.",
20
+ type: "recurring",
21
+ prices: [
22
+ { amount: 5000, currency: "USD", billing_interval: "monthly" }
23
+ ],
24
+ features: {
25
+ active_users: { value_limit: 10, type: "increment", is_hard_limit: true },
26
+ }
27
+ }),
28
+ dedicated_support: defineAddon<typeof features>({
29
+ name: "Dedicated Support",
30
+ description: "Enterprise SLA with 1-hour response time.",
31
+ type: "recurring",
32
+ prices: [
33
+ { amount: 49900, currency: "USD", billing_interval: "monthly" }
34
+ ],
35
+ features: {}
36
+ })
37
+ };
38
+ `,
39
+ plans: `import { definePlan } from "@revstackhq/core";
40
+ import { features } from "./features";
41
+
42
+ export const plans = {
43
+ default: definePlan<typeof features>({
44
+ name: "Default",
45
+ description: "Automatically created default plan for guests.",
46
+ is_default: true,
47
+ is_public: false,
48
+ type: "free",
49
+ features: {},
50
+ }),
51
+ startup: definePlan<typeof features>({
52
+ name: "Startup",
53
+ description: "For small teams getting started.",
54
+ is_default: false,
55
+ is_public: true,
56
+ type: "paid",
57
+ available_addons: ["extra_users"],
58
+ prices: [
59
+ { amount: 9900, currency: "USD", billing_interval: "monthly" }
60
+ ],
61
+ features: {
62
+ active_users: { value_limit: 10, is_hard_limit: true },
63
+ api_access: { value_bool: false },
64
+ custom_domain: { value_bool: false },
65
+ },
66
+ }),
67
+ enterprise: definePlan<typeof features>({
68
+ name: "Enterprise",
69
+ description: "Advanced features for scale.",
70
+ is_default: false,
71
+ is_public: true,
72
+ type: "paid",
73
+ available_addons: ["extra_users", "dedicated_support"],
74
+ prices: [
75
+ { amount: 49900, currency: "USD", billing_interval: "monthly" }
76
+ ],
77
+ features: {
78
+ active_users: { value_limit: 100, is_hard_limit: false },
79
+ api_access: { value_bool: true },
80
+ custom_domain: { value_bool: true },
81
+ },
82
+ }),
83
+ };
84
+ `,
85
+ index: `import { defineConfig } from "@revstackhq/core";
86
+ import { features } from "./features";
87
+ import { addons } from "./addons";
88
+ import { plans } from "./plans";
89
+
90
+ export default defineConfig({
91
+ features,
92
+ addons,
93
+ plans,
94
+ });
95
+ `,
96
+ root: `import config from "./revstack";
97
+
98
+ export default config;
99
+ `
100
+ };
101
+ export {
102
+ b2bSaas
103
+ };
104
+ //# sourceMappingURL=b2b-saas.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/commands/templates/b2b-saas.ts"],"sourcesContent":["import { TemplateConfig } from \"./starter\";\r\n\r\nexport const b2bSaas: TemplateConfig = {\r\n features: `import { defineFeature } from \"@revstackhq/core\";\r\n\r\nexport const features = {\r\n active_users: defineFeature({ name: \"Active Users\", type: \"static\", unit_type: \"count\" }),\r\n api_access: defineFeature({ name: \"API Access\", type: \"boolean\", unit_type: \"custom\" }),\r\n custom_domain: defineFeature({ name: \"Custom Domain\", type: \"boolean\", unit_type: \"custom\" }),\r\n};\r\n`,\r\n addons: `import { defineAddon } from \"@revstackhq/core\";\r\nimport { features } from \"./features\";\r\n\r\nexport const addons = {\r\n extra_users: defineAddon<typeof features>({\r\n name: \"10 Extra Users\",\r\n description: \"Add 10 more active users to your workspace.\",\r\n type: \"recurring\",\r\n prices: [\r\n { amount: 5000, currency: \"USD\", billing_interval: \"monthly\" }\r\n ],\r\n features: {\r\n active_users: { value_limit: 10, type: \"increment\", is_hard_limit: true },\r\n }\r\n }),\r\n dedicated_support: defineAddon<typeof features>({\r\n name: \"Dedicated Support\",\r\n description: \"Enterprise SLA with 1-hour response time.\",\r\n type: \"recurring\",\r\n prices: [\r\n { amount: 49900, currency: \"USD\", billing_interval: \"monthly\" }\r\n ],\r\n features: {}\r\n })\r\n};\r\n`,\r\n plans: `import { definePlan } from \"@revstackhq/core\";\r\nimport { features } from \"./features\";\r\n\r\nexport const plans = {\r\n default: definePlan<typeof features>({\r\n name: \"Default\",\r\n description: \"Automatically created default plan for guests.\",\r\n is_default: true,\r\n is_public: false,\r\n type: \"free\",\r\n features: {},\r\n }),\r\n startup: definePlan<typeof features>({\r\n name: \"Startup\",\r\n description: \"For small teams getting started.\",\r\n is_default: false,\r\n is_public: true,\r\n type: \"paid\",\r\n available_addons: [\"extra_users\"],\r\n prices: [\r\n { amount: 9900, currency: \"USD\", billing_interval: \"monthly\" }\r\n ],\r\n features: {\r\n active_users: { value_limit: 10, is_hard_limit: true },\r\n api_access: { value_bool: false },\r\n custom_domain: { value_bool: false },\r\n },\r\n }),\r\n enterprise: definePlan<typeof features>({\r\n name: \"Enterprise\",\r\n description: \"Advanced features for scale.\",\r\n is_default: false,\r\n is_public: true,\r\n type: \"paid\",\r\n available_addons: [\"extra_users\", \"dedicated_support\"],\r\n prices: [\r\n { amount: 49900, currency: \"USD\", billing_interval: \"monthly\" }\r\n ],\r\n features: {\r\n active_users: { value_limit: 100, is_hard_limit: false },\r\n api_access: { value_bool: true },\r\n custom_domain: { value_bool: true },\r\n },\r\n }),\r\n};\r\n`,\r\n index: `import { defineConfig } from \"@revstackhq/core\";\r\nimport { features } from \"./features\";\r\nimport { addons } from \"./addons\";\r\nimport { plans } from \"./plans\";\r\n\r\nexport default defineConfig({\r\n features,\r\n addons,\r\n plans,\r\n});\r\n`,\r\n root: `import config from \"./revstack\";\r\n\r\nexport default config;\r\n`,\r\n};\r\n"],"mappings":";;;AAEO,IAAM,UAA0B;AAAA,EACrC,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQV,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA0BR,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA8CP,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWP,MAAM;AAAA;AAAA;AAAA;AAIR;","names":[]}
@@ -0,0 +1,5 @@
1
+ import { TemplateConfig } from './starter.js';
2
+
3
+ declare const TEMPLATES: Record<string, TemplateConfig>;
4
+
5
+ export { TEMPLATES, TemplateConfig };
@@ -0,0 +1,264 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/commands/templates/starter.ts
4
+ var starter = {
5
+ features: `import { defineFeature } from "@revstackhq/core";
6
+
7
+ export const features = {
8
+ seats: defineFeature({ name: "Seats", type: "static", unit_type: "count" }),
9
+ priority_support: defineFeature({ name: "Priority Support", type: "boolean", unit_type: "custom" }),
10
+ };
11
+ `,
12
+ addons: `import { defineAddon } from "@revstackhq/core";
13
+ import { features } from "./features";
14
+
15
+ export const addons = {
16
+ extra_seats: defineAddon<typeof features>({
17
+ name: "5 Extra Seats",
18
+ description: "Add 5 more team members to your workspace.",
19
+ type: "recurring",
20
+ prices: [
21
+ { amount: 1500, currency: "USD", billing_interval: "monthly" }
22
+ ],
23
+ features: {
24
+ seats: { value_limit: 5, type: "increment", is_hard_limit: false },
25
+ }
26
+ }),
27
+ vip_support: defineAddon<typeof features>({
28
+ name: "Priority Support",
29
+ description: "24/7 Slack channel support.",
30
+ type: "recurring",
31
+ prices: [
32
+ { amount: 9900, currency: "USD", billing_interval: "monthly" }
33
+ ],
34
+ features: {
35
+ priority_support: { has_access: true },
36
+ }
37
+ })
38
+ };
39
+ `,
40
+ plans: `import { definePlan } from "@revstackhq/core";
41
+ import { features } from "./features";
42
+
43
+ export const plans = {
44
+ // DO NOT DELETE: Automatically created default plan for guests.
45
+ default: definePlan<typeof features>({
46
+ name: "Default",
47
+ description: "Automatically created default plan for guests.",
48
+ is_default: true,
49
+ is_public: false,
50
+ type: "free",
51
+ features: {},
52
+ }),
53
+ pro: definePlan<typeof features>({
54
+ name: "Pro",
55
+ description: "For professional teams.",
56
+ is_default: false,
57
+ is_public: true,
58
+ type: "paid",
59
+ available_addons: ["extra_seats", "vip_support"],
60
+ prices: [
61
+ { amount: 2900, currency: "USD", billing_interval: "monthly", trial_period_days: 14 }
62
+ ],
63
+ features: {
64
+ seats: { value_limit: 5, is_hard_limit: true },
65
+ },
66
+ }),
67
+ };
68
+ `,
69
+ index: `import { defineConfig } from "@revstackhq/core";
70
+ import { features } from "./features";
71
+ import { addons } from "./addons";
72
+ import { plans } from "./plans";
73
+
74
+ export default defineConfig({
75
+ features,
76
+ addons,
77
+ plans,
78
+ });
79
+ `,
80
+ root: `import config from "./revstack";
81
+
82
+ export default config;
83
+ `
84
+ };
85
+
86
+ // src/commands/templates/b2b-saas.ts
87
+ var b2bSaas = {
88
+ features: `import { defineFeature } from "@revstackhq/core";
89
+
90
+ export const features = {
91
+ active_users: defineFeature({ name: "Active Users", type: "static", unit_type: "count" }),
92
+ api_access: defineFeature({ name: "API Access", type: "boolean", unit_type: "custom" }),
93
+ custom_domain: defineFeature({ name: "Custom Domain", type: "boolean", unit_type: "custom" }),
94
+ };
95
+ `,
96
+ addons: `import { defineAddon } from "@revstackhq/core";
97
+ import { features } from "./features";
98
+
99
+ export const addons = {
100
+ extra_users: defineAddon<typeof features>({
101
+ name: "10 Extra Users",
102
+ description: "Add 10 more active users to your workspace.",
103
+ type: "recurring",
104
+ prices: [
105
+ { amount: 5000, currency: "USD", billing_interval: "monthly" }
106
+ ],
107
+ features: {
108
+ active_users: { value_limit: 10, type: "increment", is_hard_limit: true },
109
+ }
110
+ }),
111
+ dedicated_support: defineAddon<typeof features>({
112
+ name: "Dedicated Support",
113
+ description: "Enterprise SLA with 1-hour response time.",
114
+ type: "recurring",
115
+ prices: [
116
+ { amount: 49900, currency: "USD", billing_interval: "monthly" }
117
+ ],
118
+ features: {}
119
+ })
120
+ };
121
+ `,
122
+ plans: `import { definePlan } from "@revstackhq/core";
123
+ import { features } from "./features";
124
+
125
+ export const plans = {
126
+ default: definePlan<typeof features>({
127
+ name: "Default",
128
+ description: "Automatically created default plan for guests.",
129
+ is_default: true,
130
+ is_public: false,
131
+ type: "free",
132
+ features: {},
133
+ }),
134
+ startup: definePlan<typeof features>({
135
+ name: "Startup",
136
+ description: "For small teams getting started.",
137
+ is_default: false,
138
+ is_public: true,
139
+ type: "paid",
140
+ available_addons: ["extra_users"],
141
+ prices: [
142
+ { amount: 9900, currency: "USD", billing_interval: "monthly" }
143
+ ],
144
+ features: {
145
+ active_users: { value_limit: 10, is_hard_limit: true },
146
+ api_access: { value_bool: false },
147
+ custom_domain: { value_bool: false },
148
+ },
149
+ }),
150
+ enterprise: definePlan<typeof features>({
151
+ name: "Enterprise",
152
+ description: "Advanced features for scale.",
153
+ is_default: false,
154
+ is_public: true,
155
+ type: "paid",
156
+ available_addons: ["extra_users", "dedicated_support"],
157
+ prices: [
158
+ { amount: 49900, currency: "USD", billing_interval: "monthly" }
159
+ ],
160
+ features: {
161
+ active_users: { value_limit: 100, is_hard_limit: false },
162
+ api_access: { value_bool: true },
163
+ custom_domain: { value_bool: true },
164
+ },
165
+ }),
166
+ };
167
+ `,
168
+ index: `import { defineConfig } from "@revstackhq/core";
169
+ import { features } from "./features";
170
+ import { addons } from "./addons";
171
+ import { plans } from "./plans";
172
+
173
+ export default defineConfig({
174
+ features,
175
+ addons,
176
+ plans,
177
+ });
178
+ `,
179
+ root: `import config from "./revstack";
180
+
181
+ export default config;
182
+ `
183
+ };
184
+
185
+ // src/commands/templates/usage-based.ts
186
+ var usageBased = {
187
+ features: `import { defineFeature } from "@revstackhq/core";
188
+
189
+ export const features = {
190
+ api_requests: defineFeature({ name: "API Requests", type: "metered", unit_type: "requests" }),
191
+ storage_gb: defineFeature({ name: "Storage (GB)", type: "metered", unit_type: "custom" }),
192
+ };
193
+ `,
194
+ addons: `import { defineAddon } from "@revstackhq/core";
195
+ import { features } from "./features";
196
+
197
+ export const addons = {
198
+ premium_support: defineAddon<typeof features>({
199
+ name: "Premium Support",
200
+ description: "24/7 dedicated support.",
201
+ type: "recurring",
202
+ prices: [
203
+ { amount: 20000, currency: "USD", billing_interval: "monthly" }
204
+ ],
205
+ features: {}
206
+ })
207
+ };
208
+ `,
209
+ plans: `import { definePlan } from "@revstackhq/core";
210
+ import { features } from "./features";
211
+
212
+ export const plans = {
213
+ default: definePlan<typeof features>({
214
+ name: "Default",
215
+ description: "Automatically created default plan for guests.",
216
+ is_default: true,
217
+ is_public: false,
218
+ type: "free",
219
+ features: {},
220
+ }),
221
+ pay_as_you_go: definePlan<typeof features>({
222
+ name: "Pay As You Go",
223
+ description: "Flexible usage-based pricing.",
224
+ is_default: false,
225
+ is_public: true,
226
+ type: "paid",
227
+ available_addons: ["premium_support"],
228
+ prices: [
229
+ { amount: 0, currency: "USD", billing_interval: "monthly" } // Base platform fee
230
+ ],
231
+ features: {
232
+ api_requests: { value_limit: 10000, is_hard_limit: false, reset_period: "monthly" }, // 10k free requests per month
233
+ storage_gb: { value_limit: 5, is_hard_limit: false, reset_period: "never" }, // 5GB free storage lifetime
234
+ },
235
+ }),
236
+ };
237
+ `,
238
+ index: `import { defineConfig } from "@revstackhq/core";
239
+ import { features } from "./features";
240
+ import { addons } from "./addons";
241
+ import { plans } from "./plans";
242
+
243
+ export default defineConfig({
244
+ features,
245
+ addons,
246
+ plans,
247
+ });
248
+ `,
249
+ root: `import config from "./revstack";
250
+
251
+ export default config;
252
+ `
253
+ };
254
+
255
+ // src/commands/templates/index.ts
256
+ var TEMPLATES = {
257
+ starter,
258
+ "b2b-saas": b2bSaas,
259
+ "usage-based": usageBased
260
+ };
261
+ export {
262
+ TEMPLATES
263
+ };
264
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/commands/templates/starter.ts","../../../src/commands/templates/b2b-saas.ts","../../../src/commands/templates/usage-based.ts","../../../src/commands/templates/index.ts"],"sourcesContent":["export interface TemplateConfig {\r\n features: string;\r\n addons: string;\r\n plans: string;\r\n index: string;\r\n root: string;\r\n}\r\n\r\nexport const starter: TemplateConfig = {\r\n features: `import { defineFeature } from \"@revstackhq/core\";\r\n\r\nexport const features = {\r\n seats: defineFeature({ name: \"Seats\", type: \"static\", unit_type: \"count\" }),\r\n priority_support: defineFeature({ name: \"Priority Support\", type: \"boolean\", unit_type: \"custom\" }),\r\n};\r\n`,\r\n addons: `import { defineAddon } from \"@revstackhq/core\";\r\nimport { features } from \"./features\";\r\n\r\nexport const addons = {\r\n extra_seats: defineAddon<typeof features>({\r\n name: \"5 Extra Seats\",\r\n description: \"Add 5 more team members to your workspace.\",\r\n type: \"recurring\",\r\n prices: [\r\n { amount: 1500, currency: \"USD\", billing_interval: \"monthly\" }\r\n ],\r\n features: {\r\n seats: { value_limit: 5, type: \"increment\", is_hard_limit: false },\r\n }\r\n }),\r\n vip_support: defineAddon<typeof features>({\r\n name: \"Priority Support\",\r\n description: \"24/7 Slack channel support.\",\r\n type: \"recurring\",\r\n prices: [\r\n { amount: 9900, currency: \"USD\", billing_interval: \"monthly\" }\r\n ],\r\n features: {\r\n priority_support: { has_access: true },\r\n }\r\n })\r\n};\r\n`,\r\n plans: `import { definePlan } from \"@revstackhq/core\";\r\nimport { features } from \"./features\";\r\n\r\nexport const plans = {\r\n // DO NOT DELETE: Automatically created default plan for guests.\r\n default: definePlan<typeof features>({\r\n name: \"Default\",\r\n description: \"Automatically created default plan for guests.\",\r\n is_default: true,\r\n is_public: false,\r\n type: \"free\",\r\n features: {},\r\n }),\r\n pro: definePlan<typeof features>({\r\n name: \"Pro\",\r\n description: \"For professional teams.\",\r\n is_default: false,\r\n is_public: true,\r\n type: \"paid\",\r\n available_addons: [\"extra_seats\", \"vip_support\"],\r\n prices: [\r\n { amount: 2900, currency: \"USD\", billing_interval: \"monthly\", trial_period_days: 14 }\r\n ],\r\n features: {\r\n seats: { value_limit: 5, is_hard_limit: true },\r\n },\r\n }),\r\n};\r\n`,\r\n index: `import { defineConfig } from \"@revstackhq/core\";\r\nimport { features } from \"./features\";\r\nimport { addons } from \"./addons\";\r\nimport { plans } from \"./plans\";\r\n\r\nexport default defineConfig({\r\n features,\r\n addons,\r\n plans,\r\n});\r\n`,\r\n root: `import config from \"./revstack\";\r\n\r\nexport default config;\r\n`,\r\n};\r\n","import { TemplateConfig } from \"./starter\";\r\n\r\nexport const b2bSaas: TemplateConfig = {\r\n features: `import { defineFeature } from \"@revstackhq/core\";\r\n\r\nexport const features = {\r\n active_users: defineFeature({ name: \"Active Users\", type: \"static\", unit_type: \"count\" }),\r\n api_access: defineFeature({ name: \"API Access\", type: \"boolean\", unit_type: \"custom\" }),\r\n custom_domain: defineFeature({ name: \"Custom Domain\", type: \"boolean\", unit_type: \"custom\" }),\r\n};\r\n`,\r\n addons: `import { defineAddon } from \"@revstackhq/core\";\r\nimport { features } from \"./features\";\r\n\r\nexport const addons = {\r\n extra_users: defineAddon<typeof features>({\r\n name: \"10 Extra Users\",\r\n description: \"Add 10 more active users to your workspace.\",\r\n type: \"recurring\",\r\n prices: [\r\n { amount: 5000, currency: \"USD\", billing_interval: \"monthly\" }\r\n ],\r\n features: {\r\n active_users: { value_limit: 10, type: \"increment\", is_hard_limit: true },\r\n }\r\n }),\r\n dedicated_support: defineAddon<typeof features>({\r\n name: \"Dedicated Support\",\r\n description: \"Enterprise SLA with 1-hour response time.\",\r\n type: \"recurring\",\r\n prices: [\r\n { amount: 49900, currency: \"USD\", billing_interval: \"monthly\" }\r\n ],\r\n features: {}\r\n })\r\n};\r\n`,\r\n plans: `import { definePlan } from \"@revstackhq/core\";\r\nimport { features } from \"./features\";\r\n\r\nexport const plans = {\r\n default: definePlan<typeof features>({\r\n name: \"Default\",\r\n description: \"Automatically created default plan for guests.\",\r\n is_default: true,\r\n is_public: false,\r\n type: \"free\",\r\n features: {},\r\n }),\r\n startup: definePlan<typeof features>({\r\n name: \"Startup\",\r\n description: \"For small teams getting started.\",\r\n is_default: false,\r\n is_public: true,\r\n type: \"paid\",\r\n available_addons: [\"extra_users\"],\r\n prices: [\r\n { amount: 9900, currency: \"USD\", billing_interval: \"monthly\" }\r\n ],\r\n features: {\r\n active_users: { value_limit: 10, is_hard_limit: true },\r\n api_access: { value_bool: false },\r\n custom_domain: { value_bool: false },\r\n },\r\n }),\r\n enterprise: definePlan<typeof features>({\r\n name: \"Enterprise\",\r\n description: \"Advanced features for scale.\",\r\n is_default: false,\r\n is_public: true,\r\n type: \"paid\",\r\n available_addons: [\"extra_users\", \"dedicated_support\"],\r\n prices: [\r\n { amount: 49900, currency: \"USD\", billing_interval: \"monthly\" }\r\n ],\r\n features: {\r\n active_users: { value_limit: 100, is_hard_limit: false },\r\n api_access: { value_bool: true },\r\n custom_domain: { value_bool: true },\r\n },\r\n }),\r\n};\r\n`,\r\n index: `import { defineConfig } from \"@revstackhq/core\";\r\nimport { features } from \"./features\";\r\nimport { addons } from \"./addons\";\r\nimport { plans } from \"./plans\";\r\n\r\nexport default defineConfig({\r\n features,\r\n addons,\r\n plans,\r\n});\r\n`,\r\n root: `import config from \"./revstack\";\r\n\r\nexport default config;\r\n`,\r\n};\r\n","import { TemplateConfig } from \"./starter\";\r\n\r\nexport const usageBased: TemplateConfig = {\r\n features: `import { defineFeature } from \"@revstackhq/core\";\r\n\r\nexport const features = {\r\n api_requests: defineFeature({ name: \"API Requests\", type: \"metered\", unit_type: \"requests\" }),\r\n storage_gb: defineFeature({ name: \"Storage (GB)\", type: \"metered\", unit_type: \"custom\" }),\r\n};\r\n`,\r\n addons: `import { defineAddon } from \"@revstackhq/core\";\r\nimport { features } from \"./features\";\r\n\r\nexport const addons = {\r\n premium_support: defineAddon<typeof features>({\r\n name: \"Premium Support\",\r\n description: \"24/7 dedicated support.\",\r\n type: \"recurring\",\r\n prices: [\r\n { amount: 20000, currency: \"USD\", billing_interval: \"monthly\" }\r\n ],\r\n features: {}\r\n })\r\n};\r\n`,\r\n plans: `import { definePlan } from \"@revstackhq/core\";\r\nimport { features } from \"./features\";\r\n\r\nexport const plans = {\r\n default: definePlan<typeof features>({\r\n name: \"Default\",\r\n description: \"Automatically created default plan for guests.\",\r\n is_default: true,\r\n is_public: false,\r\n type: \"free\",\r\n features: {},\r\n }),\r\n pay_as_you_go: definePlan<typeof features>({\r\n name: \"Pay As You Go\",\r\n description: \"Flexible usage-based pricing.\",\r\n is_default: false,\r\n is_public: true,\r\n type: \"paid\",\r\n available_addons: [\"premium_support\"],\r\n prices: [\r\n { amount: 0, currency: \"USD\", billing_interval: \"monthly\" } // Base platform fee\r\n ],\r\n features: {\r\n api_requests: { value_limit: 10000, is_hard_limit: false, reset_period: \"monthly\" }, // 10k free requests per month\r\n storage_gb: { value_limit: 5, is_hard_limit: false, reset_period: \"never\" }, // 5GB free storage lifetime\r\n },\r\n }),\r\n};\r\n`,\r\n index: `import { defineConfig } from \"@revstackhq/core\";\r\nimport { features } from \"./features\";\r\nimport { addons } from \"./addons\";\r\nimport { plans } from \"./plans\";\r\n\r\nexport default defineConfig({\r\n features,\r\n addons,\r\n plans,\r\n});\r\n`,\r\n root: `import config from \"./revstack\";\r\n\r\nexport default config;\r\n`,\r\n};\r\n","import { starter } from \"./starter\";\r\nimport { b2bSaas } from \"./b2b-saas\";\r\nimport { usageBased } from \"./usage-based\";\r\nimport type { TemplateConfig } from \"./starter\";\r\n\r\nexport const TEMPLATES: Record<string, TemplateConfig> = {\r\n starter: starter,\r\n \"b2b-saas\": b2bSaas,\r\n \"usage-based\": usageBased,\r\n};\r\n\r\nexport type { TemplateConfig };\r\n"],"mappings":";;;AAQO,IAAM,UAA0B;AAAA,EACrC,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOV,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA4BR,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA6BP,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWP,MAAM;AAAA;AAAA;AAAA;AAIR;;;ACtFO,IAAM,UAA0B;AAAA,EACrC,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQV,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA0BR,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA8CP,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWP,MAAM;AAAA;AAAA;AAAA;AAIR;;;AChGO,IAAM,aAA6B;AAAA,EACxC,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOV,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAeR,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA6BP,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWP,MAAM;AAAA;AAAA;AAAA;AAIR;;;AChEO,IAAM,YAA4C;AAAA,EACvD;AAAA,EACA,YAAY;AAAA,EACZ,eAAe;AACjB;","names":[]}
@@ -0,0 +1,10 @@
1
+ interface TemplateConfig {
2
+ features: string;
3
+ addons: string;
4
+ plans: string;
5
+ index: string;
6
+ root: string;
7
+ }
8
+ declare const starter: TemplateConfig;
9
+
10
+ export { type TemplateConfig, starter };