From 2a9555d459508991ff17a339568763dd3fccc56a Mon Sep 17 00:00:00 2001 From: Pamela Chia Date: Wed, 15 Apr 2026 00:48:35 +0900 Subject: [PATCH] feat(docs,www): make supabase.com machine-readable for LLM agents (#44670) ## Summary Makes supabase.com machine-readable for LLM agents and AI crawlers. Adds a combined `llms-full.txt` (product overview + docs), curated product overview `.txt` files, auto-generated pricing content, and updates the `llms.txt` index to reference everything. **Why:** supabase.com had `llms.txt` pointing to per-SDK doc files, but no `llms-full.txt` (every competitor has one), no machine-readable marketing content, and no pricing data agents could parse. Agents evaluating Supabase got docs but no product overview or pricing, leading to inaccurate comparisons. ## What's new ### `llms-full.txt` (auto-generated, combines www + docs) - Product Overview section (~500 lines): homepage, all 6 products, pricing - Documentation section (~122K lines): guides, SDK references, CLI reference - 4.4MB total, regenerated on every docs deploy - Reads product `.txt` files from `apps/www/public/llms/` at build time ### Product overview `.txt` files (`apps/www/public/llms/`) - Curated summaries: homepage, database, auth, storage, edge functions, realtime, vector - `pricing.txt` is auto-generated from `packages/shared-data` (plans, pricing, compute add-ons) via `generateLlmsPricing.mjs`, includes full feature comparison matrix - Other product files are hand-maintained (these pages change ~1x/year per git history) - Reminder comments added to all 7 marketing page source files ### `llms.txt` index (auto-generated) - Two sections: Documentation (existing SDK/guide links) and Product Overview (marketing page links) - Links to `llms-full.txt` for bulk ingestion ### Rewrite changes - Added `/llms-full.txt` rewrite to docs app - Scoped `/llms/*.txt` wildcard to only match docs source slugs (guides, js, dart, etc.), so marketing `.txt` files in `www/public/llms/` are served directly ## Changes - `apps/docs/scripts/llms.ts`: generate `llms-full.txt` combining www product content + docs, update `llms.txt` index with marketing links - `apps/www/lib/rewrites.js`: add `llms-full.txt` rewrite, scope docs proxy to known slugs - `apps/www/public/llms/*.txt`: 8 product overview files (7 hand-curated, 1 auto-generated) - `apps/www/scripts/generateLlmsPricing.mjs`: build script generating pricing.txt from shared-data; uses `getPlanValue()` guard in `buildAddOnsSection` and `buildFeatureComparisonSection` to handle missing plan keys defensively - `apps/www/package.json`: add pricing generation to content:build - `apps/www/pages/*.tsx`: reminder comments for LLM content updates ## Testing Tested locally: - [x] `pnpm run build:llms` generates combined `llms-full.txt` (4.4MB) with Product Overview + Documentation sections - [x] `llms.txt` index has Documentation + Product Overview sections with `llms-full.txt` reference - [x] `pricing.txt` auto-generated from shared-data with correct plan tiers, compute add-ons, disk pricing, and feature comparison - [x] Scoped rewrite regex matches docs slugs but not marketing slugs - [x] Marketing `.txt` files served from `public/` Post-deploy verification: - [ ] `curl https://supabase.com/llms-full.txt` returns combined product + docs content - [ ] `curl https://supabase.com/llms/database.txt` returns product overview (not proxied to docs) - [ ] `curl https://supabase.com/llms/guides.txt` still proxies to docs app - [ ] `curl https://supabase.com/llms/pricing.txt` returns auto-generated pricing ## Maintenance | Content | Auto-updates? | Trigger | |---------|--------------|---------| | `llms-full.txt` | Yes | Every docs deploy | | `llms.txt` index | Yes | Every docs deploy | | `pricing.txt` | Yes | Every www build (reads from shared-data) | | Product `.txt` files (7) | No | ~1x/year, reminder comments in source pages | ## Linear - fixes GROWTH-758 ## Summary by CodeRabbit * **Documentation** * Added comprehensive AI-friendly product overviews (Auth, Database, Edge Functions, Realtime, Storage, Vector) and a detailed pricing document with plan comparisons, add-ons, disk tiers, and feature tables * Added a consolidated "full" markdown output that combines curated product overview content with per-source documentation * **Chores** * Build now generates the richer documentation outputs and pricing automatically * Improved routing so only scoped documentation .txt assets are proxied while others are served directly --- apps/docs/.gitignore | 1 + apps/docs/scripts/llms.ts | 110 +++++++- apps/www/lib/redirects.js | 10 + apps/www/lib/rewrites.js | 11 +- apps/www/package.json | 2 +- apps/www/pages/auth.tsx | 2 + apps/www/pages/database.tsx | 2 + apps/www/pages/edge-functions.tsx | 2 + apps/www/pages/index.tsx | 2 + apps/www/pages/modules/vector.tsx | 2 + apps/www/pages/realtime.tsx | 2 + apps/www/pages/storage.tsx | 2 + apps/www/public/llms/auth.txt | 41 +++ apps/www/public/llms/database.txt | 34 +++ apps/www/public/llms/edge-functions.txt | 41 +++ apps/www/public/llms/homepage.txt | 38 +++ apps/www/public/llms/pricing.txt | 210 ++++++++++++++ apps/www/public/llms/realtime.txt | 40 +++ apps/www/public/llms/storage.txt | 38 +++ apps/www/public/llms/vector.txt | 49 ++++ apps/www/scripts/generateLlmsPricing.mjs | 335 +++++++++++++++++++++++ 21 files changed, 958 insertions(+), 16 deletions(-) create mode 100644 apps/www/public/llms/auth.txt create mode 100644 apps/www/public/llms/database.txt create mode 100644 apps/www/public/llms/edge-functions.txt create mode 100644 apps/www/public/llms/homepage.txt create mode 100644 apps/www/public/llms/pricing.txt create mode 100644 apps/www/public/llms/realtime.txt create mode 100644 apps/www/public/llms/storage.txt create mode 100644 apps/www/public/llms/vector.txt create mode 100644 apps/www/scripts/generateLlmsPricing.mjs diff --git a/apps/docs/.gitignore b/apps/docs/.gitignore index 574d054827f..43e6ed578db 100644 --- a/apps/docs/.gitignore +++ b/apps/docs/.gitignore @@ -29,6 +29,7 @@ yarn-error.log* public/sitemap.xml # llms.txt (generated) public/llms.txt +public/llms-full.txt public/llms/ # Generated guide markdown files public/docs/ diff --git a/apps/docs/scripts/llms.ts b/apps/docs/scripts/llms.ts index 95ad95d57ef..1ddaf8f2886 100644 --- a/apps/docs/scripts/llms.ts +++ b/apps/docs/scripts/llms.ts @@ -1,7 +1,8 @@ import './utils/dotenv.js' - import 'dotenv/config' + import fs from 'node:fs/promises' +import path from 'node:path' import { fileURLToPath } from 'node:url' import { isFeatureEnabled } from '../../../packages/common/enabled-features/index.js' @@ -114,36 +115,117 @@ const SOURCES: Source[] = [ }, ] +// Product overview .txt files are hand-curated and live in apps/www/public/llms/. +// These links always point to production since llms.txt is only meaningful in prod. +const PRODUCT_OVERVIEW_LINKS = [ + '- [Supabase Overview](https://supabase.com/llms/homepage.txt)', + '- [Supabase Database](https://supabase.com/llms/database.txt)', + '- [Supabase Auth](https://supabase.com/llms/auth.txt)', + '- [Supabase Storage](https://supabase.com/llms/storage.txt)', + '- [Supabase Edge Functions](https://supabase.com/llms/edge-functions.txt)', + '- [Supabase Realtime](https://supabase.com/llms/realtime.txt)', + '- [Supabase Vector](https://supabase.com/llms/vector.txt)', + '- [Supabase Pricing](https://supabase.com/llms/pricing.txt)', +].join('\n') + async function generateMainLlmsTxt() { const sourceLinks = SOURCES.filter((source) => source.enabled !== false) .map((source) => `- ${toLink(source)}`) .join('\n') - const fullText = `# ${metadataTitle}\n\n${sourceLinks}` - fs.writeFile('public/llms.txt', fullText) + + const fullText = [ + `# ${metadataTitle}`, + '', + 'For the complete documentation in a single file, see [Full Documentation](https://supabase.com/llms-full.txt).', + '', + '## Documentation', + '', + sourceLinks, + '', + '## Product Overview', + '', + PRODUCT_OVERVIEW_LINKS, + ].join('\n') + + await fs.writeFile('public/llms.txt', fullText) } -async function generateSourceLlmsTxt(sourceDefn: Source) { - const source = await sourceDefn.fetch() - const sourceText = source - .map((section) => { - section.process() - return section.extractIndexedContent() - }) - .join('\n\n') - const fullText = sourceDefn.title + '\n\n' + sourceText +// Product overview .txt files live in apps/www/public/llms/, read at build time. +// Order matters: homepage first, pricing last, products alphabetical in between. +const PRODUCT_LLM_FILES = [ + 'homepage.txt', + 'auth.txt', + 'database.txt', + 'edge-functions.txt', + 'realtime.txt', + 'storage.txt', + 'vector.txt', + 'pricing.txt', +] - fs.writeFile(`public/${sourceDefn.relPath}`, fullText) +const PRODUCT_LLMS_DIR = path.resolve( + path.dirname(fileURLToPath(import.meta.url)), + '../../../apps/www/public/llms' +) + +async function readProductLlmContent(): Promise { + const contents = await Promise.all( + PRODUCT_LLM_FILES.map((file) => { + const filePath = path.join(PRODUCT_LLMS_DIR, file) + return fs.readFile(filePath, 'utf-8') + }) + ) + return contents.join('\n\n---\n\n') } async function generateLlmsTxt() { try { await fs.mkdir('public/llms', { recursive: true }) + + const enabledSources = SOURCES.filter((source) => source.enabled !== false) + + // Fetch all sources once, reuse for both per-SDK files and llms-full.txt + const [productContent, ...fetchedSources] = await Promise.all([ + readProductLlmContent(), + ...enabledSources.map(async (sourceDefn) => { + const source = await sourceDefn.fetch() + const sourceText = source + .map((section) => { + section.process() + return section.extractIndexedContent() + }) + .join('\n\n') + return { defn: sourceDefn, text: sourceText } + }), + ]) + await Promise.all([ generateMainLlmsTxt(), - ...SOURCES.filter((source) => source.enabled !== false).map(generateSourceLlmsTxt), + // Per-SDK files + ...fetchedSources.map(({ defn, text }) => + fs.writeFile(`public/${defn.relPath}`, `${defn.title}\n\n${text}`) + ), + // Combined file: product overview + all docs + fs.writeFile( + 'public/llms-full.txt', + [ + '# Supabase', + '', + '## Product Overview', + '', + productContent, + '', + '---', + '', + '## Documentation', + '', + fetchedSources.map(({ defn, text }) => `# ${defn.title}\n\n${text}`).join('\n\n---\n\n'), + ].join('\n') + ), ]) } catch (err) { console.error(err) + throw err } } diff --git a/apps/www/lib/redirects.js b/apps/www/lib/redirects.js index d7efc39d349..32ce2730f50 100644 --- a/apps/www/lib/redirects.js +++ b/apps/www/lib/redirects.js @@ -3107,4 +3107,14 @@ module.exports = [ destination: '/dashboard/redeem?code=:code', permanent: false, }, + { + permanent: true, + source: '/docs/llms.txt', + destination: '/llms.txt', + }, + { + permanent: true, + source: '/docs/llms-full.txt', + destination: '/llms-full.txt', + }, ] diff --git a/apps/www/lib/rewrites.js b/apps/www/lib/rewrites.js index 0e767175e13..6511585856a 100644 --- a/apps/www/lib/rewrites.js +++ b/apps/www/lib/rewrites.js @@ -70,7 +70,16 @@ const rewrites = [ destination: `${process.env.NEXT_PUBLIC_DOCS_URL}/llms.txt`, }, { - source: '/llms/:path(.*\\.txt$)', + source: '/llms-full.txt', + destination: `${process.env.NEXT_PUBLIC_DOCS_URL}/llms-full.txt`, + }, + // Docs-generated LLM source files. Scoped to known doc slugs so that + // marketing .txt files in www/public/llms/ (e.g. database.txt, auth.txt) + // are served directly from public/ without being proxied to the docs app. + // Update this list when adding new SDK sources in apps/docs/scripts/llms.ts. + // Current slugs: guides, js, dart, swift, kotlin, python, csharp, cli + { + source: '/llms/:path((?:guides|js|dart|swift|kotlin|python|csharp|cli)\\.txt)', destination: `${process.env.NEXT_PUBLIC_DOCS_URL}/llms/:path`, }, { source: '/feed.xml', destination: `/rss.xml` }, diff --git a/apps/www/package.json b/apps/www/package.json index 6a65c773c85..4ca07e6094b 100644 --- a/apps/www/package.json +++ b/apps/www/package.json @@ -13,7 +13,7 @@ "clean": "rimraf node_modules", "pretypecheck": "next typegen", "typecheck": "pnpm run content:build && tsc --noEmit", - "content:build": "node scripts/generateStaticContent.mjs", + "content:build": "node scripts/generateStaticContent.mjs && node scripts/generateLlmsPricing.mjs", "test": "vitest --run", "test:watch": "vitest watch", "postbuild": "node ./internals/generate-sitemap.mjs && ./../../scripts/upload-static-assets.sh" diff --git a/apps/www/pages/auth.tsx b/apps/www/pages/auth.tsx index fcf65ee5609..127cd2ffaaf 100644 --- a/apps/www/pages/auth.tsx +++ b/apps/www/pages/auth.tsx @@ -28,6 +28,8 @@ const FeatureColumn = dynamic(() => import('~/components/FeatureColumn')) const APISection = dynamic(() => import('~/components/Sections/APISection')) const GithubExamples = dynamic(() => import('~/components/Sections/GithubExamples')) +// When updating page content, also update public/llms/auth.txt + function AuthPage() { const isMobile = useBreakpoint(768) const { basePath } = useRouter() diff --git a/apps/www/pages/database.tsx b/apps/www/pages/database.tsx index 78ea6d561f1..d7dc586f6f3 100644 --- a/apps/www/pages/database.tsx +++ b/apps/www/pages/database.tsx @@ -37,6 +37,8 @@ const ProductIcon = dynamic(() => import('~/components/ProductIcon')) const APISection = dynamic(() => import('~/components/Sections/APISection')) const GithubExamples = dynamic(() => import('~/components/Sections/GithubExamples')) +// When updating page content, also update public/llms/database.txt + function Database() { // base path for images const { basePath } = useRouter() diff --git a/apps/www/pages/edge-functions.tsx b/apps/www/pages/edge-functions.tsx index 6846bed35ab..403306028fa 100644 --- a/apps/www/pages/edge-functions.tsx +++ b/apps/www/pages/edge-functions.tsx @@ -24,6 +24,8 @@ const ProductsCta = dynamic(() => import('~/components/Sections/ProductsCta2')) const TimedAccordionPanels = dynamic(() => import('~/components/Sections/TimedAccordionPanels')) const TimedAccordionSection = dynamic(() => import('~/components/Sections/TimedAccordionSection')) +// When updating page content, also update public/llms/edge-functions.txt + function EdgeFunctions() { const { basePath } = useRouter() const isXs = useBreakpoint(640) diff --git a/apps/www/pages/index.tsx b/apps/www/pages/index.tsx index e466d15868f..66d499fb054 100644 --- a/apps/www/pages/index.tsx +++ b/apps/www/pages/index.tsx @@ -13,6 +13,8 @@ const TwitterSocialSection = dynamic(() => import('~/components/TwitterSocialSec const OpenSourceSection = dynamic(() => import('~/components/OpenSourceSection')) const CTABanner = dynamic(() => import('components/CTABanner/index')) +// When updating page content, also update public/llms/homepage.txt + const Index = () => { const content = getContent() diff --git a/apps/www/pages/modules/vector.tsx b/apps/www/pages/modules/vector.tsx index 51c9ee2bd52..beef4c41db5 100644 --- a/apps/www/pages/modules/vector.tsx +++ b/apps/www/pages/modules/vector.tsx @@ -19,6 +19,8 @@ const TimedTabsSection = dynamic(() => import('~/components/Sections/TimedTabsSe const ProductsCta = dynamic(() => import('~/components/Sections/ProductsCta')) const EnterpriseCta = dynamic(() => import('~/components/Sections/EnterpriseCta')) +// When updating page content, also update public/llms/vector.txt + function VectorPage() { // base path for images const isXs = useBreakpoint(640) diff --git a/apps/www/pages/realtime.tsx b/apps/www/pages/realtime.tsx index 4cbebc7cd32..6f7d115058b 100644 --- a/apps/www/pages/realtime.tsx +++ b/apps/www/pages/realtime.tsx @@ -23,6 +23,8 @@ import { PRODUCT_NAMES } from 'shared-data/products' const SingleQuote = dynamic(() => import('~/components/Sections/SingleQuote')) +// When updating page content, also update public/llms/realtime.txt + const Cursor = ({ className = '', color = 'none' }) => { return ( import('~/components/CTABanner')) +// When updating page content, also update public/llms/storage.txt + function StoragePage() { // base path for images const { basePath } = useRouter() diff --git a/apps/www/public/llms/auth.txt b/apps/www/public/llms/auth.txt new file mode 100644 index 00000000000..0a9d4e39d71 --- /dev/null +++ b/apps/www/public/llms/auth.txt @@ -0,0 +1,41 @@ +# Supabase Auth + +> Built-in authentication, authorization, and user management for every Supabase project. + +Supabase Auth provides a complete user management system without any external authentication service. It is deeply integrated with Postgres Row Level Security for fine-grained access control, so you can write authorization policies in SQL rather than application code. + +## Key Features + +- **Social login**: 20+ providers out of the box (Google, GitHub, Apple, Azure/Microsoft, Facebook, Twitter, Discord, GitLab, and more), enabled with one click +- **Email/password**: built-in email signup with confirmation, password reset, and email change flows +- **Phone/OTP**: SMS-based authentication with one-time passwords +- **Magic links**: passwordless email authentication +- **Row Level Security**: authorization policies written in SQL, evaluated at the database level, no middleware needed +- **JWT-based sessions**: standard JSON Web Tokens, compatible with any JWT library +- **User management dashboard**: view, create, edit, and delete users from the Supabase Dashboard +- **Enterprise SSO**: SAML 2.0 support for enterprise single sign-on +- **Multi-factor authentication**: TOTP-based MFA for additional account security +- **Custom OAuth scopes**: request additional permissions when using social login providers +- **Server-side auth**: helpers for Next.js, SvelteKit, Remix, and other server frameworks +- **Auth hooks**: customize authentication flows with database functions or Edge Functions + +## Technical Details + +- Protocol: OAuth 2.0, OIDC, SAML 2.0 +- Token format: JWT (access token + refresh token) +- Storage: user data stored in your project's Postgres database (auth schema), not with a third party +- Available in 16+ global regions + +## How It Works + +1. Users authenticate via social provider, email/password, phone, or magic link +2. Supabase Auth issues a JWT containing the user's ID and metadata +3. The JWT is sent with every request to your Supabase project +4. Postgres Row Level Security policies reference the JWT to determine what data the user can access +5. No middleware or application-level authorization code needed + +## Links + +- Documentation: https://supabase.com/docs/guides/auth +- API Reference: https://supabase.com/docs/reference/javascript/auth-signup +- Dashboard: https://supabase.com/dashboard diff --git a/apps/www/public/llms/database.txt b/apps/www/public/llms/database.txt new file mode 100644 index 00000000000..4f0774421f9 --- /dev/null +++ b/apps/www/public/llms/database.txt @@ -0,0 +1,34 @@ +# Supabase Database + +> Every Supabase project is a dedicated Postgres database, trusted by millions of developers. + +Supabase Database gives you a full Postgres database with no compromises. It is not a Postgres-compatible alternative or a shared cluster: every project gets its own dedicated Postgres instance that you can connect to directly with any Postgres client. + +## Key Features + +- **Dedicated Postgres**: each project is an isolated Postgres instance, not a shared tenant +- **100% portable**: bring your own Postgres database, or export and migrate away at any time +- **Auto-generated APIs**: REST (PostgREST) and GraphQL (pg_graphql) APIs generated from your schema, no backend code needed +- **Table Editor**: spreadsheet-like UI for viewing and editing data, with support for relationships, JSON columns, and foreign key lookups +- **SQL Editor**: write and save SQL queries directly in the dashboard, with autocomplete and syntax highlighting +- **Row Level Security**: fine-grained access control using Postgres RLS policies, integrated with Supabase Auth JWTs +- **40+ extensions**: enable Postgres extensions with a single click (pgvector, PostGIS, pg_cron, pg_stat_statements, and more) +- **Database Branching**: create isolated database branches synced with git branches, with Vercel Preview support +- **Read Replicas**: distribute read traffic across replicas in multiple regions for lower latency and higher throughput +- **Realtime**: subscribe to INSERT, UPDATE, DELETE, and other changes via WebSockets +- **Database Webhooks**: trigger Edge Functions or external HTTP endpoints on table events +- **Supabase ETL**: stream Postgres changes to external data warehouses in real time, with no pipelines + +## Technical Details + +- Engine: PostgreSQL (latest stable) +- Connection: direct Postgres connections (connection string), connection pooling via PgBouncer (Supavisor) +- Client libraries: JavaScript, Python, Dart (Flutter), Swift, Kotlin, C# +- Backups: automatic daily backups, Point-in-Time Recovery available as add-on +- Compute: configurable from Micro (2-core ARM) to 16XL+ (64-core ARM and above), with autoscaling options + +## Links + +- Documentation: https://supabase.com/docs/guides/database +- API Reference: https://supabase.com/docs/reference/javascript/select +- Dashboard: https://supabase.com/dashboard diff --git a/apps/www/public/llms/edge-functions.txt b/apps/www/public/llms/edge-functions.txt new file mode 100644 index 00000000000..9f552d319f5 --- /dev/null +++ b/apps/www/public/llms/edge-functions.txt @@ -0,0 +1,41 @@ +# Supabase Edge Functions + +> Globally distributed TypeScript serverless functions, deployed in seconds. + +Supabase Edge Functions let you run server-side TypeScript code at the edge, close to your users. Built on the open source Deno runtime, they integrate seamlessly with the rest of the Supabase platform: database, auth, storage, and realtime. + +## Key Features + +- **Global deployment**: functions run at the edge worldwide for low latency +- **TypeScript-first**: write in TypeScript with full type checking and autocompletion +- **Node.js compatible**: supports Node.js APIs and 2M+ NPM modules +- **Zero configuration**: pre-populated environment variables for accessing your Supabase project (database, auth, storage) +- **Database webhooks**: trigger functions automatically on table INSERT, UPDATE, or DELETE events +- **Regional invocation**: optionally pin functions to run near your database for lower DB latency +- **Built-in observability**: real-time log streaming, SQL-queryable Log Explorer, and metrics dashboards +- **CI/CD**: deploy via Supabase CLI with GitHub Actions support +- **Local development**: hot code reloading, Language Server for autocompletion, same runtime locally and in production + +## Technical Details + +- Runtime: Deno (open source, V8-based) +- Languages: TypeScript, JavaScript +- NPM support: 2M+ modules via npm: specifiers +- Scaling: automatic, no manual tuning +- Security: SSL, Firewall, DDoS protection built in +- Open source: same Edge Runtime runs locally and in production, no vendor lock-in + +## Common Use Cases + +- Webhook handlers (Stripe, GitHub, Twilio) +- Server-side API routes and middleware +- Scheduled tasks (via pg_cron + database webhooks) +- AI/ML inference endpoints +- Email and notification sending +- Third-party API integrations + +## Links + +- Documentation: https://supabase.com/docs/guides/functions +- API Reference: https://supabase.com/docs/reference/javascript/functions-invoke +- Dashboard: https://supabase.com/dashboard diff --git a/apps/www/public/llms/homepage.txt b/apps/www/public/llms/homepage.txt new file mode 100644 index 00000000000..98a9b5be841 --- /dev/null +++ b/apps/www/public/llms/homepage.txt @@ -0,0 +1,38 @@ +# Supabase + +> Build in a weekend. Scale to millions. + +Supabase is an open source Firebase alternative built on Postgres. It provides a complete backend platform for building web and mobile applications, with a suite of integrated tools that work together out of the box. + +## Products + +- **Database**: Every project is a dedicated Postgres database. Fully portable, with auto-generated REST and GraphQL APIs, a Table Editor, SQL Editor, and 40+ preinstalled extensions. +- **Auth**: Built-in authentication and user management with 20+ social login providers, email/password, phone/OTP, and magic links. Authorization via Postgres Row Level Security. +- **Storage**: S3-compatible object storage with a global CDN (285+ cities), image transformations, and three bucket types: files, analytics (Apache Iceberg), and vector. +- **Edge Functions**: Globally distributed TypeScript/Deno serverless functions with Node.js compatibility, 2M+ NPM modules, and built-in observability. +- **Realtime**: WebSocket-based real-time sync with three capabilities: database change listeners, presence (online state), and broadcast (arbitrary messages). +- **Vector**: AI toolkit powered by pgvector for storing, indexing, and querying vector embeddings alongside transactional data in Postgres. + +## Data APIs + +Every Supabase project auto-generates three types of APIs from your database schema: + +- **REST**: Instant CRUD API via PostgREST, no code generation needed +- **GraphQL**: Auto-detected relationships and schema via pg_graphql +- **Realtime**: Subscribe to database changes over WebSockets + +## Key Differentiators + +- Open source: all core tools are open source and self-hostable +- Built on Postgres: industry-standard database, fully portable, no vendor lock-in +- Integrated platform: auth, database, storage, functions, and realtime work together seamlessly +- SOC2 Type 2 compliant +- Available in 16+ global regions + +## Links + +- Website: https://supabase.com +- Documentation: https://supabase.com/docs +- Dashboard: https://supabase.com/dashboard +- GitHub: https://github.com/supabase/supabase +- Pricing: https://supabase.com/pricing diff --git a/apps/www/public/llms/pricing.txt b/apps/www/public/llms/pricing.txt new file mode 100644 index 00000000000..c98dbe1b9e5 --- /dev/null +++ b/apps/www/public/llms/pricing.txt @@ -0,0 +1,210 @@ +# Supabase Pricing + +> Note: this file is auto-generated from packages/shared-data. Do not edit directly. + +> Start for free, scale as you grow. Pay only for what you use. + +Supabase offers four plans: Free, Pro, Team, and Enterprise. All plans include unlimited API requests. + +## How billing works + +Supabase uses organization-based billing. You choose a plan (Pro, Team, or Enterprise) for your organization, then each project within it runs on its own compute instance. The plan subscription covers platform features and usage quotas. Compute is billed separately per project. + +Pro and Team plans include $10/month in compute credits, which covers one Micro instance. Additional projects each add their own compute cost. For example, a Pro org with 2 projects on Micro compute costs: $25 (plan) + $10 (project 1) + $10 (project 2) - $10 (credits) = $35/month. + +For current pricing, visit https://supabase.com/pricing. + +## Plan Tiers + +### Free - from $0/month +- Unlimited API requests +- 50,000 monthly active users +- 500 MB database size (Shared CPU • 500 MB RAM) +- 5 GB egress +- 5 GB cached egress +- 1 GB file storage +- Community support +- Note: Free projects are paused after 1 week of inactivity. Limit of 2 active projects. + +### Pro - from $25/month +- 100,000 monthly active users (then $0.00325 per MAU) +- 8 GB disk size per project (then $0.125 per GB) +- 250 GB egress (then $0.09 per GB) +- 250 GB cached egress (then $0.03 per GB) +- 100 GB file storage (then $0.021 per GB) +- Email support +- Daily backups stored for 7 days +- 7-day log retention +- Add Log Drains (additional $60 per drain, per project) + +### Team - from $599/month +- SOC2 +- Project-scoped and read-only access +- HIPAA available as paid add-on +- SSO for Supabase Dashboard +- Priority email support & SLAs +- Daily backups stored for 14 days +- 28-day log retention + +### Enterprise - custom pricing +- Designated Support manager +- Uptime SLAs +- BYO Cloud supported +- 24×7×365 premium enterprise support +- Private Slack channel +- Custom Security Questionnaires + +## Compute Add-Ons + +All projects run on a compute instance. Pro and Team plans include Micro compute in the base price. + +| Size | $/month | CPU | Dedicated | RAM | Direct Connections | Pooler Connections | +| ------ | ---------- | ----------- | --------- | ------ | ------------------ | ------------------ | +| Micro | $10 | 2-core ARM | No | 1 GB | 60 | 200 | +| Small | $15 | 2-core ARM | No | 2 GB | 90 | 400 | +| Medium | $60 | 2-core ARM | No | 4 GB | 120 | 600 | +| Large | $110 | 2-core ARM | Yes | 8 GB | 160 | 800 | +| XL | $210 | 4-core ARM | Yes | 16 GB | 240 | 1,000 | +| 2XL | $410 | 8-core ARM | Yes | 32 GB | 380 | 1,500 | +| 4XL | $960 | 16-core ARM | Yes | 64 GB | 480 | 3,000 | +| 8XL | $1,870 | 32-core ARM | Yes | 128 GB | 490 | 6,000 | +| 12XL | $2,800 | 48-core ARM | Yes | 192 GB | 500 | 9,000 | +| 16XL | $3,730 | 64-core ARM | Yes | 256 GB | 500 | 12,000 | +| >16XL | Contact Us | Custom | Yes | Custom | Custom | Custom | + +Compute is billed hourly. Each project runs its own instance. Pro and Team plans include $10/month in compute credits (covers one Micro instance). Additional projects add their full compute cost. + +## Disk Storage + +### General Purpose +- Max size: 16 TB +- Size: 8 GB included, then $0.125 per GB +- IOPS: 3,000 IOPS included, then $0.024 per IOPS +- Throughput: 125 MB/s included, then $0.095 per MB/s +- Durability: 99.9% + +### High Performance +- Max size: 60 TB +- Size: $0.195 per GB +- IOPS: $0.119 per IOPS +- Throughput: Scales automatically with IOPS +- Durability: 99.999% + +## Add-Ons + +| Add-on | Price | +| ----------------------------- | --------------------------------------------------------------------------- | +| Point-in-Time Recovery (PITR) | $100 per month per 7 days retention | +| Custom Domain | $10 per domain per month per project add on | +| Database Branching | $0.01344 per branch, per hour | +| Advanced MFA (Phone) | $75 per month for first project, then $10 per month per additional projects | +| SAML/SSO Auth | 50 included, then $0.015 per MAU | +| Log Drains | $60 per drain per month, + $0.20 per million events, + $0.09 per GB egress | +| Image Transformations | 100 origin images included, then $5 per 1000 origin images | + +## Full Feature Comparison + +### Database + +| Feature | Free | Pro | Team | Enterprise | +| --------------------------- | ----------------------------------------- | ------------------------------------------------------- | ------------------------------------------------------- | ----------------------------------------------------------------- | +| Dedicated Postgres Database | Included | Included | Included | Included | +| Unlimited API requests | Included | Included | Included | Included | +| Database size | 500 MB database size per project included | 8 GB disk size per project included, then $0.125 per GB | 8 GB disk size per project included, then $0.125 per GB | Custom | +| Advanced disk config | Not included | Included | Included | Included | +| Automatic backups | Not included | 7 days | 14 days | Custom | +| Point in time recovery | Not included | $100 per month per 7 days retention | $100 per month per 7 days retention | $100 per month per 7 days retention, >28 days retention available | +| Pausing | After 1 week of inactivity | Never | Never | Never | +| Branching | Not included | $0.01344 per branch, per hour | $0.01344 per branch, per hour | Custom | +| Egress | 5 GB included | 250 GB included, then $0.09 per GB | 250 GB included, then $0.09 per GB | Custom | + +### Auth + +| Feature | Free | Pro | Team | Enterprise | +| ------------------------------------ | ------------------------------------------------ | --------------------------------------------------------------------------- | --------------------------------------------------------------------------- | ---------- | +| Total Users | Unlimited | Unlimited | Unlimited | Unlimited | +| MAUs | 50,000 included | 100,000 included, then $0.00325 per MAU | 100,000 included, then $0.00325 per MAU | Custom | +| User data ownership | Included | Included | Included | Included | +| Anonymous Sign-ins | Included | Included | Included | Included | +| Social OAuth providers | Included | Included | Included | Included | +| Custom SMTP server | Included | Included | Included | Included | +| Remove Supabase branding from emails | Not included | Included | Included | Included | +| Auth Audit Logs | 1 hour | 7 days | 28 days | Included | +| Basic Multi-Factor Auth | Included | Included | Included | Included | +| Advanced Multi-Factor Auth - Phone | Not included | $75 per month for first project, then $10 per month per additional projects | $75 per month for first project, then $10 per month per additional projects | Custom | +| Third-Party MAUs | 50,000 included | 100,000 included, then $0.00325 per MAU | 100,000 included, then $0.00325 per MAU | Custom | +| Single Sign-On (SAML 2.0) | Not included | 50 included, then $0.015 per MAU | 50 included, then $0.015 per MAU | Contact Us | +| Leaked password protection | Not included | Included | Included | Included | +| Single session per user | Not included | Included | Included | Included | +| Session timeouts | Not included | Included | Included | Included | +| Auth Hooks | Custom Access Token (JWT), Send custom email/SMS | Custom Access Token (JWT), Send custom email/SMS | All | All | +| Advanced security features | Not included | Not included | Not included | Contact Us | + +### Storage + +| Feature | Free | Pro | Team | Enterprise | +| ------------------------------- | ------------- | ---------------------------------------------------------- | ---------------------------------------------------------- | ---------- | +| Storage | 1 GB included | 100 GB included, then $0.021 per GB | 100 GB included, then $0.021 per GB | Custom | +| Cached Egress | 5 GB included | 250 GB included, then $0.03 per GB | 250 GB included, then $0.03 per GB | Custom | +| Custom access controls | Included | Included | Included | Included | +| Max file upload size | 50 MB | 500 GB | 500 GB | Custom | +| Content Delivery Network | Basic CDN | Smart CDN | Smart CDN | Smart CDN | +| Image Transformations | Not included | 100 origin images included, then $5 per 1000 origin images | 100 origin images included, then $5 per 1000 origin images | Custom | +| Bring your own storage provider | Not included | Not included | Not included | Included | + +### Edge Functions + +| Feature | Free | Pro | Team | Enterprise | +| ----------- | ---------------- | ----------------------------------------- | ----------------------------------------- | ---------- | +| Invocations | 500,000 included | 2 Million included, then $2 per 1 Million | 2 Million included, then $2 per 1 Million | Custom | + +### Realtime + +| Feature | Free | Pro | Team | Enterprise | +| --------------------------- | ------------------ | ------------------------------------------ | ------------------------------------------ | ------------------------------------------------- | +| Postgres Changes | Included | Included | Included | Included | +| Concurrent Peak Connections | 200 included | 500 included, then $10 per 1000 | 500 included, then $10 per 1000 | Custom concurrent connections and volume discount | +| Messages Per Month | 2 Million included | 5 Million included, then $2.50 per Million | 5 Million included, then $2.50 per Million | Volume discounts on messages | +| Max Message Size | 256 KB | 3 MB | 3 MB | Custom | + +### Dashboard + +| Feature | Free | Pro | Team | Enterprise | +| ------------ | --------- | --------- | --------- | ---------- | +| Team members | Unlimited | Unlimited | Unlimited | Unlimited | + +### Platform Security and Compliance + +| Feature | Free | Pro | Team | Enterprise | +| ------------------------------ | ----------------------- | -------------------------------------------------------------------------- | -------------------------------------------------------------------------- | ------------------------------ | +| BYO cloud | Not included | Not included | Not included | Included | +| Log retention (API & Database) | 1 day | 7 days | 28 days | 90 days | +| Log Drain | Not included | $60 per drain per month, + $0.20 per million events, + $0.09 per GB egress | $60 per drain per month, + $0.20 per million events, + $0.09 per GB egress | Custom | +| Platform Audit Logs | Not included | Not included | Included | Included | +| Metrics endpoint | Not included | Included | Included | Included | +| SOC2 | Not included | Not included | Included | Included | +| HIPAA | Not included | Not included | Available as paid add-on | Available as paid add-on | +| AWS PrivateLink | Not included | Not included | Included | Included | +| SSO | Not included | Not included | Contact Us | Contact Us | +| Uptime SLAs | Not included | Not included | Not included | Included | +| Access Roles | Owner, Admin, Developer | Owner, Admin, Developer | Owner, Admin, Developer, Read-only, Predefined project scoped roles | Custom project scoped roles | +| Vanity URLs | Not included | Included | Included | Included | +| Custom Domains | Not included | $10 per domain per month per project add on | $10 per domain per month per project add on | 1, additional $10/domain/month | + +### Support + +| Feature | Free | Pro | Team | Enterprise | +| -------------------------------- | ------------ | ------------ | ------------ | ---------- | +| Community Support | Included | Included | Included | Included | +| Email Support | Not included | Included | Included | Included | +| Email Support SLA | Not included | Not included | Included | Included | +| Designated support | Not included | Not included | Not included | Included | +| On Boarding Support | Not included | Not included | Not included | Included | +| Designated Customer Success Team | Not included | Not included | Not included | Included | +| Security Questionnaire Help | Not included | Not included | Included | Included | + +## Links + +- Pricing page: https://supabase.com/pricing +- Documentation: https://supabase.com/docs/guides/platform/org-based-billing +- Dashboard: https://supabase.com/dashboard diff --git a/apps/www/public/llms/realtime.txt b/apps/www/public/llms/realtime.txt new file mode 100644 index 00000000000..72e8804e641 --- /dev/null +++ b/apps/www/public/llms/realtime.txt @@ -0,0 +1,40 @@ +# Supabase Realtime + +> Sync client state globally over WebSockets in real time. + +Supabase Realtime enables live data synchronization between your database and connected clients. It provides three capabilities: listening to database changes, tracking user presence, and broadcasting arbitrary messages between clients. + +## Capabilities + +### Database Changes +Listen to Postgres INSERT, UPDATE, and DELETE events in real time. Subscribe to specific tables, filter by columns, and receive only the changes you care about. Powered by Postgres logical replication. + +### Presence +Store and synchronize online user state across all connected clients. Track who is online, what page they are viewing, or their cursor position. State is automatically cleaned up when clients disconnect. + +### Broadcast +Send arbitrary messages to all clients subscribed to the same Channel. Useful for typing indicators, live cursors, game state, notifications, or any real-time communication that does not need to be persisted. + +## Technical Details + +- Transport: WebSockets +- Protocol: Phoenix Channels +- Database integration: Postgres logical replication (CDC) +- Client libraries: JavaScript (supabase-js), Dart (Flutter), Swift, Kotlin +- Authorization: Row Level Security policies applied to database change subscriptions +- Scaling: horizontally scalable across multiple nodes + +## Common Use Cases + +- Collaborative editing and live cursors +- Chat and messaging applications +- Live dashboards and analytics +- Multiplayer games +- Notifications and activity feeds +- Auction and bidding systems + +## Links + +- Documentation: https://supabase.com/docs/guides/realtime +- API Reference: https://supabase.com/docs/reference/javascript/subscribe +- Dashboard: https://supabase.com/dashboard diff --git a/apps/www/public/llms/storage.txt b/apps/www/public/llms/storage.txt new file mode 100644 index 00000000000..f552c873282 --- /dev/null +++ b/apps/www/public/llms/storage.txt @@ -0,0 +1,38 @@ +# Supabase Storage + +> S3-compatible object storage with a global CDN, image transformations, and three purpose-built bucket types. + +Supabase Storage is an open source object store that integrates natively with Supabase Auth and Postgres. It supports three bucket types for different workloads: files (general assets), analytics (Apache Iceberg), and vector (embeddings). + +## Key Features + +- **S3-compatible**: standard S3 API, works with existing S3 tools and SDKs +- **Global CDN**: assets served from 285+ cities worldwide for low-latency delivery +- **Image transformations**: resize, crop, and compress images on the fly via URL parameters +- **Row Level Security**: access policies written in SQL using Postgres RLS, integrated with Supabase Auth +- **Dashboard**: drag-and-drop uploads, file browsing, multi-select operations, folder management + +## Bucket Types + +### Files Buckets +For everyday assets and user content: images, videos, documents, PDFs, archives. Served from the global CDN with fine-grained access controls via RLS policies. + +### Analytics Buckets +For large-scale analytical workloads on open table formats (Apache Iceberg). Designed for historical data, time-series data, logs, and ETL outputs. Optionally queryable via Postgres. + +### Vector Buckets +For AI/ML workloads. Store and index vector embeddings with multiple distance metrics, metadata filtering, and fast similarity queries for RAG systems and AI-powered search. + +## Technical Details + +- Protocol: S3-compatible API +- CDN: 285+ global edge locations +- Max file size: 50 MB (Free), 500 GB (Pro/Team/Enterprise), via multipart upload +- Image transformations: resize, crop, format conversion (WebP, AVIF) +- Authorization: Postgres RLS policies on storage.objects table + +## Links + +- Documentation: https://supabase.com/docs/guides/storage +- API Reference: https://supabase.com/docs/reference/javascript/storage-from-upload +- Dashboard: https://supabase.com/dashboard diff --git a/apps/www/public/llms/vector.txt b/apps/www/public/llms/vector.txt new file mode 100644 index 00000000000..457073b7a58 --- /dev/null +++ b/apps/www/public/llms/vector.txt @@ -0,0 +1,49 @@ +# Supabase Vector + +> Store, index, and query vector embeddings in Postgres with pgvector. + +Supabase Vector is an AI toolkit that lets you store vector embeddings alongside your transactional data in the same Postgres database. Powered by the pgvector extension, it eliminates the need for a separate vector database while providing production-grade similarity search. + +## Key Features + +- **pgvector integration**: store, index, and query vector embeddings directly in Postgres +- **Co-located data**: vector embeddings live alongside your relational data, joined with standard SQL +- **Multiple index types**: IVFFlat and HNSW indexes for fast approximate nearest neighbor search +- **Distance metrics**: cosine distance, L2 (Euclidean) distance, max inner product +- **Metadata filtering**: filter similarity queries by any column or JSONB metadata +- **Python client (vecs)**: dedicated Python library for managing collections, upserting vectors, and querying +- **LLM integrations**: works with OpenAI, Hugging Face, Amazon SageMaker, LangChain, and more +- **Edge Functions**: generate embeddings using open source models directly in Edge Functions +- **Self-hostable**: run the full stack on your own infrastructure +- **SOC2 Type 2 compliant**: enterprise-grade security + +## Common Use Cases + +- Semantic search over documents, knowledge bases, or support tickets +- Retrieval-Augmented Generation (RAG) for LLM applications +- Image similarity detection +- Recommendation engines +- Auto-tagging and content classification +- ChatGPT plugins with long-term memory + +## How It Works + +1. Generate embeddings using any model (OpenAI, Hugging Face, Cohere, etc.) +2. Store embeddings in a Postgres table with a `vector` column +3. Create an HNSW or IVFFlat index for fast similarity search +4. Query with `<=>` (cosine), `<->` (L2), or `<#>` (inner product) operators +5. Combine with standard SQL: JOIN, WHERE, GROUP BY for hybrid queries + +## Technical Details + +- Extension: pgvector (open source) +- Max dimensions: 2,000 (HNSW), 16,000+ (flat) +- Index types: HNSW (recommended), IVFFlat +- Scaling: same compute scaling as your Supabase database (Micro to 16XL) +- Backups: automatic daily + PITR available + +## Links + +- Documentation: https://supabase.com/docs/guides/ai +- Python client: https://supabase.com/docs/guides/ai/vecs-python-client +- Dashboard: https://supabase.com/dashboard diff --git a/apps/www/scripts/generateLlmsPricing.mjs b/apps/www/scripts/generateLlmsPricing.mjs new file mode 100644 index 00000000000..55285c7a3e8 --- /dev/null +++ b/apps/www/scripts/generateLlmsPricing.mjs @@ -0,0 +1,335 @@ +// @ts-check + +/** + * Generates apps/www/public/llms/pricing.txt from structured pricing data. + * + * Source data: + * - packages/shared-data/plans.ts (plan tiers, features, cta copy) + * - packages/shared-data/pricing.ts (per-feature plan values) + * - apps/www/data/PricingAddOnTable.json (compute add-on rows) + * - apps/www/components/Pricing/PricingDiskSection.tsx (disk type data - inlined below) + * + * Run: node apps/www/scripts/generateLlmsPricing.mjs + * (Uses tsx for TypeScript imports, falling back to inlined data if unavailable) + */ + +import { execSync } from 'child_process' +import { promises as fs } from 'fs' +import os from 'os' +import path from 'path' +import { fileURLToPath } from 'url' + +const __dirname = path.dirname(fileURLToPath(import.meta.url)) +const repoRoot = path.resolve(__dirname, '../../..') + +// --------------------------------------------------------------------------- +// Load TypeScript source data via tsx +// --------------------------------------------------------------------------- + +// We shell out to tsx to load the TypeScript modules and dump them as JSON. +// tsx is available in the monorepo node_modules. +const tsxBin = path.join(repoRoot, 'node_modules/.bin/tsx') + +const loadTsData = async (tsFile, exportName) => { + const tmpFile = path.join(os.tmpdir(), `generate-llms-pricing-${exportName}-${Date.now()}.ts`) + await fs.writeFile( + tmpFile, + `import { ${exportName} } from '${tsFile}'\nprocess.stdout.write(JSON.stringify(${exportName}))\n`, + 'utf8' + ) + try { + const result = execSync(`"${tsxBin}" "${tmpFile}"`, { encoding: 'utf8', cwd: repoRoot }) + return JSON.parse(result) + } finally { + await fs.unlink(tmpFile).catch(() => {}) + } +} + +const plans = await loadTsData(path.join(repoRoot, 'packages/shared-data/plans.ts'), 'plans') + +const pricing = await loadTsData(path.join(repoRoot, 'packages/shared-data/pricing.ts'), 'pricing') + +const addOnTablePath = path.join(__dirname, '../data/PricingAddOnTable.json') +const addOnTable = JSON.parse(await fs.readFile(addOnTablePath, 'utf8')) + +// --------------------------------------------------------------------------- +// Disk types (sourced from PricingDiskSection.tsx - inlined here for +// portability; keep in sync if that component's data changes) +// --------------------------------------------------------------------------- + +const diskTypes = [ + { + name: 'General Purpose', + maxSize: '16 TB', + size: '8 GB included, then $0.125 per GB', + iops: '3,000 IOPS included, then $0.024 per IOPS', + throughput: '125 MB/s included, then $0.095 per MB/s', + durability: '99.9%', + }, + { + name: 'High Performance', + maxSize: '60 TB', + size: '$0.195 per GB', + iops: '$0.119 per IOPS', + throughput: 'Scales automatically with IOPS', + durability: '99.999%', + }, +] + +// --------------------------------------------------------------------------- +// Helpers +// --------------------------------------------------------------------------- + +const formatPlanValue = (val) => { + if (val === true) return 'Included' + if (val === false) return 'Not included' + if (Array.isArray(val)) return val.join(', ') + return String(val) +} + +const getVal = (row, key) => { + const col = row.columns.find((c) => c.key === key) + if (!col) + throw new Error(`Missing column "${key}" in compute add-on row "${row.title || 'unknown'}"`) + return col.value +} + +const pad = (str, len) => String(str).padEnd(len) + +const getPlanValue = (feature, planId) => { + if (!(planId in feature.plans)) { + throw new Error(`Missing plan value for "${planId}" in feature "${feature.key}"`) + } + return formatPlanValue(feature.plans[planId]) +} + +// --------------------------------------------------------------------------- +// Plan Tiers section +// --------------------------------------------------------------------------- + +const buildPlanTiersSection = () => { + const lines = ['## Plan Tiers', ''] + + for (const plan of plans) { + if (plan.planId === 'enterprise') { + lines.push(`### ${plan.name} - custom pricing`) + } else { + lines.push(`### ${plan.name} - from $${plan.priceMonthly}/month`) + } + + for (const feature of plan.features) { + if (Array.isArray(feature)) { + lines.push(`- ${feature[0]}` + (feature[1] ? ` (${feature[1]})` : '')) + } else { + lines.push(`- ${feature}`) + } + } + + if (plan.footer) { + lines.push(`- Note: ${plan.footer}`) + } + + lines.push('') + } + + return lines.join('\n') +} + +// --------------------------------------------------------------------------- +// Compute Add-Ons section +// --------------------------------------------------------------------------- + +const buildComputeSection = () => { + const rows = addOnTable.database.rows + + const colWidths = { + plan: 'Size'.length, + price: '$/month'.length, + cpu: 'CPU'.length, + dedicated: 'Dedicated'.length, + memory: 'RAM'.length, + direct: 'Direct Connections'.length, + pooler: 'Pooler Connections'.length, + } + + for (const row of rows) { + colWidths.plan = Math.max(colWidths.plan, String(getVal(row, 'plan')).length) + colWidths.price = Math.max(colWidths.price, String(getVal(row, 'pricing')).length) + colWidths.cpu = Math.max(colWidths.cpu, String(getVal(row, 'cpu')).length) + colWidths.dedicated = Math.max( + colWidths.dedicated, + getVal(row, 'dedicated') ? 'Yes'.length : 'No'.length + ) + colWidths.memory = Math.max(colWidths.memory, String(getVal(row, 'memory')).length) + colWidths.direct = Math.max(colWidths.direct, String(getVal(row, 'directConnections')).length) + colWidths.pooler = Math.max(colWidths.pooler, String(getVal(row, 'poolerConnections')).length) + } + + const header = `| ${pad('Size', colWidths.plan)} | ${pad('$/month', colWidths.price)} | ${pad('CPU', colWidths.cpu)} | ${pad('Dedicated', colWidths.dedicated)} | ${pad('RAM', colWidths.memory)} | ${pad('Direct Connections', colWidths.direct)} | ${pad('Pooler Connections', colWidths.pooler)} |` + const separator = `| ${'-'.repeat(colWidths.plan)} | ${'-'.repeat(colWidths.price)} | ${'-'.repeat(colWidths.cpu)} | ${'-'.repeat(colWidths.dedicated)} | ${'-'.repeat(colWidths.memory)} | ${'-'.repeat(colWidths.direct)} | ${'-'.repeat(colWidths.pooler)} |` + + const dataRows = rows.map((row) => { + const plan = String(getVal(row, 'plan')) + const price = String(getVal(row, 'pricing')) + const cpu = String(getVal(row, 'cpu')) + const dedicated = getVal(row, 'dedicated') ? 'Yes' : 'No' + const memory = String(getVal(row, 'memory')) + const direct = String(getVal(row, 'directConnections')) + const pooler = String(getVal(row, 'poolerConnections')) + + return `| ${pad(plan, colWidths.plan)} | ${pad(price, colWidths.price)} | ${pad(cpu, colWidths.cpu)} | ${pad(dedicated, colWidths.dedicated)} | ${pad(memory, colWidths.memory)} | ${pad(direct, colWidths.direct)} | ${pad(pooler, colWidths.pooler)} |` + }) + + return [ + '## Compute Add-Ons', + '', + 'All projects run on a compute instance. Pro and Team plans include Micro compute in the base price.', + '', + header, + separator, + ...dataRows, + '', + 'Compute is billed hourly. Each project runs its own instance. Pro and Team plans include $10/month in compute credits (covers one Micro instance). Additional projects add their full compute cost.', + '', + ].join('\n') +} + +// --------------------------------------------------------------------------- +// Disk Storage section +// --------------------------------------------------------------------------- + +const buildDiskSection = () => { + const lines = ['## Disk Storage', ''] + + for (const disk of diskTypes) { + lines.push(`### ${disk.name}`) + lines.push(`- Max size: ${disk.maxSize}`) + lines.push(`- Size: ${disk.size}`) + lines.push(`- IOPS: ${disk.iops}`) + lines.push(`- Throughput: ${disk.throughput}`) + lines.push(`- Durability: ${disk.durability}`) + lines.push('') + } + + return lines.join('\n') +} + +// --------------------------------------------------------------------------- +// Add-Ons section (from pricing.ts feature values) +// --------------------------------------------------------------------------- + +const findFeature = (category, key) => { + const feature = pricing[category].features.find((f) => f.key === key) + if (!feature) throw new Error(`Missing pricing feature "${key}" in category "${category}"`) + return feature +} + +const buildAddOnsSection = () => { + const addOns = [ + [ + 'Point-in-Time Recovery (PITR)', + getPlanValue(findFeature('database', 'database.pitr'), 'pro'), + ], + ['Custom Domain', getPlanValue(findFeature('security', 'security.customDomains'), 'pro')], + ['Database Branching', getPlanValue(findFeature('database', 'database.branching'), 'pro')], + ['Advanced MFA (Phone)', getPlanValue(findFeature('auth', 'auth.advancedMFAPhone'), 'pro')], + ['SAML/SSO Auth', getPlanValue(findFeature('auth', 'auth.saml'), 'pro')], + ['Log Drains', getPlanValue(findFeature('security', 'security.logDrain'), 'pro')], + [ + 'Image Transformations', + getPlanValue(findFeature('storage', 'storage.transformations'), 'pro'), + ], + ] + + 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 dataRows = addOns.map( + ([name, price]) => `| ${pad(name, nameWidth)} | ${pad(price, priceWidth)} |` + ) + + return ['## Add-Ons', '', header, separator, ...dataRows, ''].join('\n') +} + +// --------------------------------------------------------------------------- +// Full feature comparison (per category from pricing.ts) +// --------------------------------------------------------------------------- + +const buildFeatureComparisonSection = () => { + const planIds = ['free', 'pro', 'team', 'enterprise'] + const planLabels = { free: 'Free', pro: 'Pro', team: 'Team', enterprise: 'Enterprise' } + + const lines = ['## Full Feature Comparison', ''] + + for (const [, category] of Object.entries(pricing)) { + lines.push(`### ${category.title}`, '') + + const nameWidth = Math.max('Feature'.length, ...category.features.map((f) => f.title.length)) + const planWidths = {} + for (const planId of planIds) { + planWidths[planId] = Math.max( + planLabels[planId].length, + ...category.features.map((f) => getPlanValue(f, planId).length) + ) + } + + const headerRow = `| ${pad('Feature', nameWidth)} | ${planIds.map((p) => pad(planLabels[p], planWidths[p])).join(' | ')} |` + const separatorRow = `| ${'-'.repeat(nameWidth)} | ${planIds.map((p) => '-'.repeat(planWidths[p])).join(' | ')} |` + + lines.push(headerRow, separatorRow) + + for (const feature of category.features) { + const cells = planIds.map((p) => pad(getPlanValue(feature, p), planWidths[p])) + lines.push(`| ${pad(feature.title, nameWidth)} | ${cells.join(' | ')} |`) + } + + lines.push('') + } + + return lines.join('\n') +} + +// --------------------------------------------------------------------------- +// Assemble final output +// --------------------------------------------------------------------------- + +const output = [ + '# Supabase Pricing', + '', + '> Note: this file is auto-generated from packages/shared-data. Do not edit directly.', + '', + '> Start for free, scale as you grow. Pay only for what you use.', + '', + 'Supabase offers four plans: Free, Pro, Team, and Enterprise. All plans include unlimited API requests.', + '', + '## How billing works', + '', + 'Supabase uses organization-based billing. You choose a plan (Pro, Team, or Enterprise) for your organization, then each project within it runs on its own compute instance. The plan subscription covers platform features and usage quotas. Compute is billed separately per project.', + '', + 'Pro and Team plans include $10/month in compute credits, which covers one Micro instance. Additional projects each add their own compute cost. For example, a Pro org with 2 projects on Micro compute costs: $25 (plan) + $10 (project 1) + $10 (project 2) - $10 (credits) = $35/month.', + '', + 'For current pricing, visit https://supabase.com/pricing.', + '', + buildPlanTiersSection(), + buildComputeSection(), + buildDiskSection(), + buildAddOnsSection(), + buildFeatureComparisonSection(), + '## Links', + '', + '- Pricing page: https://supabase.com/pricing', + '- Documentation: https://supabase.com/docs/guides/platform/org-based-billing', + '- Dashboard: https://supabase.com/dashboard', + '', +].join('\n') + +// --------------------------------------------------------------------------- +// Write output +// --------------------------------------------------------------------------- + +const outputPath = path.join(__dirname, '../public/llms/pricing.txt') +await fs.writeFile(outputPath, output, 'utf8') + +console.log(`✅ Generated ${outputPath}`)