From b522f3106002ca9a3fddc5fa6008634f96885e3d Mon Sep 17 00:00:00 2001 From: Pamela Chia Date: Wed, 26 Aug 2026 17:34:41 +0800 Subject: [PATCH] refactor(www): extract shared markdown table builder, tighten pricing catalog types --- apps/www/lib/llms.ts | 46 ++++++++----------------- apps/www/lib/pricing-json.ts | 4 +-- packages/shared-data/plans.ts | 3 +- packages/shared-data/pricing-catalog.ts | 14 ++------ 4 files changed, 21 insertions(+), 46 deletions(-) diff --git a/apps/www/lib/llms.ts b/apps/www/lib/llms.ts index c5eab57436b..25acba76173 100644 --- a/apps/www/lib/llms.ts +++ b/apps/www/lib/llms.ts @@ -31,6 +31,16 @@ function pad(str: string, len: number): string { return str.padEnd(len) } +function buildMarkdownTable(headers: string[], dataRows: string[][]): string[] { + const widths = headers.map((h, i) => Math.max(h.length, ...dataRows.map((r) => r[i].length))) + const headerRow = `| ${headers.map((h, i) => pad(h, widths[i])).join(' | ')} |` + const separator = `| ${widths.map((w) => '-'.repeat(w)).join(' | ')} |` + const bodyRows = dataRows.map( + (cells) => `| ${cells.map((c, i) => pad(c, widths[i])).join(' | ')} |` + ) + return [headerRow, separator, ...bodyRows] +} + // --------------------------------------------------------------------------- // Plan Tiers // --------------------------------------------------------------------------- @@ -97,22 +107,12 @@ function buildComputeSection(): string { }) ) - const widths = headers.map((h, i) => Math.max(h.length, ...dataRows.map((r) => r[i].length))) - - const headerRow = `| ${headers.map((h, i) => pad(h, widths[i])).join(' | ')} |` - const separator = `| ${widths.map((w) => '-'.repeat(w)).join(' | ')} |` - const bodyRows = dataRows.map( - (cells) => `| ${cells.map((c, i) => pad(c, widths[i])).join(' | ')} |` - ) - return [ '## Compute Add-Ons', '', 'All projects run on a compute instance. Pro and Team plans include Micro compute in the base price.', '', - headerRow, - separator, - ...bodyRows, + ...buildMarkdownTable(headers, dataRows), '', `Compute is billed hourly. Each project runs its own instance. Pro and Team plans include ${usd(PLAN_BILLING.pro.computeCreditsMonthly)}/month in compute credits (covers one Micro instance). Additional projects add their full compute cost.`, '', @@ -126,7 +126,7 @@ function buildComputeSection(): string { function buildDiskSection(): string { const headers = ['Disk type', 'Max size', 'Size', 'IOPS', 'Throughput', 'Durability'] - const dataRows = Object.values(DISK_PRICING).map((disk) => { + const dataRows = Object.entries(DISK_PRICING).map(([type, disk]) => { const size = disk.includedPerProject ? `${disk.includedPerProject.sizeGb} GB included, then ${usd(disk.perUnitMonth.sizeGb)} per GB` : `${usd(disk.perUnitMonth.sizeGb)} per GB` @@ -139,7 +139,7 @@ function buildDiskSection(): string { : (disk.throughputNote ?? '') return [ - `${disk.displayName} (${disk.type})`, + `${disk.displayName} (${type})`, `${disk.maxSizeTb} TB`, size, iops, @@ -148,14 +148,7 @@ function buildDiskSection(): string { ] }) - const widths = headers.map((h, i) => Math.max(h.length, ...dataRows.map((r) => r[i].length))) - const headerRow = `| ${headers.map((h, i) => pad(h, widths[i])).join(' | ')} |` - const separator = `| ${widths.map((w) => '-'.repeat(w)).join(' | ')} |` - const bodyRows = dataRows.map( - (cells) => `| ${cells.map((c, i) => pad(c, widths[i])).join(' | ')} |` - ) - - return ['## Disk Storage', '', headerRow, separator, ...bodyRows, ''].join('\n') + return ['## Disk Storage', '', ...buildMarkdownTable(headers, dataRows), ''].join('\n') } // --------------------------------------------------------------------------- @@ -192,16 +185,7 @@ function buildAddOnsSection(): string { ], ] - const nameWidth = Math.max('Add-on'.length, ...addOns.map(([n]) => n.length)) - const priceWidth = Math.max('Price'.length, ...addOns.map(([, p]) => p.length)) - - const header = `| ${pad('Add-on', nameWidth)} | ${pad('Price', priceWidth)} |` - const separator = `| ${'-'.repeat(nameWidth)} | ${'-'.repeat(priceWidth)} |` - const rows = addOns.map( - ([name, price]) => `| ${pad(name, nameWidth)} | ${pad(price, priceWidth)} |` - ) - - return ['## Add-Ons', '', header, separator, ...rows, ''].join('\n') + return ['## Add-Ons', '', ...buildMarkdownTable(['Add-on', 'Price'], addOns), ''].join('\n') } // --------------------------------------------------------------------------- diff --git a/apps/www/lib/pricing-json.ts b/apps/www/lib/pricing-json.ts index 59f0df90bb0..a363c7d8831 100644 --- a/apps/www/lib/pricing-json.ts +++ b/apps/www/lib/pricing-json.ts @@ -87,8 +87,8 @@ export function buildPricingJson() { }, disk: { scope: 'per-project', - types: Object.values(DISK_PRICING).map((disk) => ({ - type: disk.type, + types: Object.entries(DISK_PRICING).map(([type, disk]) => ({ + type, name: disk.displayName, maxSizeTb: disk.maxSizeTb, durabilityPercent: disk.durabilityPercent, diff --git a/packages/shared-data/plans.ts b/packages/shared-data/plans.ts index 67b6a3d8806..0f669ab3620 100644 --- a/packages/shared-data/plans.ts +++ b/packages/shared-data/plans.ts @@ -1,6 +1,7 @@ import { ADDONS, gbDisplay, METERS, PLAN_BILLING, qty, usd, withUnit } from './pricing-catalog' +import type { PlanKey } from './pricing-catalog' -export type PlanId = 'free' | 'pro' | 'team' | 'enterprise' +export type PlanId = PlanKey export interface PricingInformation { id: 'tier_free' | 'tier_pro' | 'tier_team' | 'tier_enterprise' diff --git a/packages/shared-data/pricing-catalog.ts b/packages/shared-data/pricing-catalog.ts index 7b35c5e76d1..bdd4fffc880 100644 --- a/packages/shared-data/pricing-catalog.ts +++ b/packages/shared-data/pricing-catalog.ts @@ -26,7 +26,6 @@ export interface DiskAllowance { } export interface DiskTypeEntry { - type: 'gp3' | 'io2' displayName: string tagline: string maxSizeTb: number @@ -36,13 +35,6 @@ export interface DiskTypeEntry { throughputNote?: string } -export interface AddonAvailability { - free: boolean - pro: boolean - team: boolean - enterprise: boolean -} - export function usd(amount: number, decimals?: number): string { return `$${decimals === undefined ? String(amount) : amount.toFixed(decimals)}` } @@ -72,9 +64,8 @@ export const PLAN_BILLING = { enterprise: { priceMonthly: null, computeCreditsMonthly: null, spendCapAvailable: false }, } as const satisfies Record -export const DISK_PRICING: Record<'gp3' | 'io2', DiskTypeEntry> = { +export const DISK_PRICING = { gp3: { - type: 'gp3', displayName: 'General Purpose', tagline: 'Balance between price and performance', maxSizeTb: 16, @@ -83,7 +74,6 @@ export const DISK_PRICING: Record<'gp3' | 'io2', DiskTypeEntry> = { perUnitMonth: { sizeGb: GP3_PER_GB_MONTH, iops: 0.024, throughputMBps: 0.095 }, }, io2: { - type: 'io2', displayName: 'High Performance', tagline: 'For mission critical applications', maxSizeTb: 60, @@ -92,7 +82,7 @@ export const DISK_PRICING: Record<'gp3' | 'io2', DiskTypeEntry> = { perUnitMonth: { sizeGb: 0.195, iops: 0.119, throughputMBps: null }, throughputNote: 'Scales automatically with IOPS', }, -} +} as const satisfies Record<'gp3' | 'io2', DiskTypeEntry> export const METERS = { egress: {