appfunnel 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/errors.ts","../src/lib/logger.ts","../src/lib/auth.ts","../src/commands/init.ts","../src/commands/login.ts","../src/commands/whoami.ts","../src/lib/config.ts","../src/lib/version.ts","../src/extract/pages.ts","../src/vite/entry.ts","../src/vite/html.ts","../src/vite/plugin.ts","../src/commands/dev.ts","../src/commands/build.ts","../src/lib/api.ts","../src/commands/publish.ts","../src/index.ts"],"sourcesContent":["import pc from 'picocolors'\n\nexport type ErrorCode =\n | 'AUTH_REQUIRED'\n | 'AUTH_EXPIRED'\n | 'CONFIG_NOT_FOUND'\n | 'INVALID_ROUTE'\n | 'UNDEFINED_VARIABLE'\n | 'VERSION_MISMATCH'\n | 'BUILD_NOT_FOUND'\n | 'FUNNEL_NOT_HEADLESS'\n | 'BUNDLE_TOO_LARGE'\n | 'PAGE_SIZE'\n | 'INVALID_PAGE'\n | 'NO_PAGES'\n | 'NO_PROJECTS'\n | 'API_ERROR'\n | 'PUBLISH_FAILED'\n\nexport class CLIError extends Error {\n code: ErrorCode\n hint?: string\n statusCode?: number\n\n constructor(code: ErrorCode, message: string, hint?: string) {\n super(message)\n this.name = 'CLIError'\n this.code = code\n this.hint = hint\n }\n}\n\nexport function formatError(err: CLIError): string {\n const lines = [\n `${pc.red('ERROR')} ${pc.dim(`[${err.code}]`)}: ${err.message}`,\n ]\n if (err.hint) {\n lines.push(` ${pc.dim('Hint:')} ${err.hint}`)\n }\n return lines.join('\\n')\n}\n\nexport function formatWarning(code: string, message: string, hint?: string): string {\n const lines = [\n `${pc.yellow('WARNING')} ${pc.dim(`[${code}]`)}: ${message}`,\n ]\n if (hint) {\n lines.push(` ${pc.dim('Hint:')} ${hint}`)\n }\n return lines.join('\\n')\n}\n","import { readFileSync } from 'node:fs'\nimport pc from 'picocolors'\nimport ora, { type Ora } from 'ora'\n\nexport function success(msg: string): void {\n console.log(`${pc.green('✓')} ${msg}`)\n}\n\nexport function error(msg: string): void {\n console.error(`${pc.red('✗')} ${msg}`)\n}\n\nexport function warn(msg: string): void {\n console.warn(`${pc.yellow('!')} ${msg}`)\n}\n\nexport function info(msg: string): void {\n console.log(`${pc.blue('ℹ')} ${msg}`)\n}\n\nexport function dim(msg: string): void {\n console.log(pc.dim(msg))\n}\n\nexport function spinner(msg: string): Ora {\n return ora({ text: msg, color: 'cyan' }).start()\n}\n\nexport function banner(): void {\n console.log()\n console.log(` ${pc.bold('appfunnel')} ${pc.dim('v' + getVersion())}`)\n console.log()\n}\n\nfunction getVersion(): string {\n try {\n const pkg = JSON.parse(\n readFileSync(new URL('../../package.json', import.meta.url), 'utf-8'),\n )\n return pkg.version\n } catch {\n return '0.0.0'\n }\n}\n","import { readFileSync, writeFileSync, mkdirSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { homedir } from 'node:os'\nimport { CLIError } from './errors.js'\n\nexport interface Credentials {\n token: string\n userId: string\n email: string\n expiresAt: string\n}\n\nconst CREDENTIALS_PATH = join(homedir(), '.appfunnelrc')\n\nexport function readCredentials(): Credentials | null {\n try {\n const raw = readFileSync(CREDENTIALS_PATH, 'utf-8')\n const data = JSON.parse(raw)\n if (!data.token) return null\n return data as Credentials\n } catch {\n return null\n }\n}\n\nexport function writeCredentials(creds: Credentials): void {\n const dir = homedir()\n mkdirSync(dir, { recursive: true })\n writeFileSync(CREDENTIALS_PATH, JSON.stringify(creds, null, 2) + '\\n', 'utf-8')\n}\n\nexport function requireAuth(): Credentials {\n const creds = readCredentials()\n if (!creds) {\n throw new CLIError(\n 'AUTH_REQUIRED',\n 'Not logged in.',\n \"Run 'appfunnel login' to authenticate.\",\n )\n }\n\n if (creds.expiresAt) {\n const expiresAt = new Date(creds.expiresAt)\n if (expiresAt < new Date()) {\n throw new CLIError(\n 'AUTH_EXPIRED',\n 'Token expired.',\n \"Run 'appfunnel login' to re-authenticate.\",\n )\n }\n }\n\n return creds\n}\n","import { mkdirSync, writeFileSync, existsSync } from 'node:fs'\nimport { join } from 'node:path'\nimport pc from 'picocolors'\nimport select from '@inquirer/select'\nimport * as log from '../lib/logger.js'\nimport { requireAuth } from '../lib/auth.js'\nimport { CLIError } from '../lib/errors.js'\n\nconst DEFAULT_API_BASE = 'https://api.appfunnel.net'\n\ninterface Project {\n\tid: string\n\tname: string\n\trole: string\n}\n\nasync function fetchProjects(token: string): Promise<Project[]> {\n\tconst response = await fetch(`${DEFAULT_API_BASE}/user/projects`, {\n\t\theaders: {\n\t\t\tAuthorization: token,\n\t\t\t'Content-Type': 'application/json',\n\t\t},\n\t})\n\n\tif (!response.ok) {\n\t\tthrow new CLIError('API_ERROR', 'Failed to fetch projects.')\n\t}\n\n\tconst body = (await response.json()) as { data: Project[] }\n\treturn body.data\n}\n\nexport async function initCommand(name: string): Promise<void> {\n\tconst creds = requireAuth()\n\n\tconst dir = join(process.cwd(), name)\n\n\tif (existsSync(dir)) {\n\t\tlog.error(`Directory '${name}' already exists.`)\n\t\tprocess.exit(1)\n\t}\n\n\tconst spin = log.spinner('Fetching projects…')\n\tlet projects: Project[]\n\ttry {\n\t\tprojects = await fetchProjects(creds.token)\n\t} catch (err) {\n\t\tspin.stop()\n\t\tif (err instanceof CLIError) throw err\n\t\tthrow new CLIError(\n\t\t\t'API_ERROR',\n\t\t\t'Failed to reach the API. Check your internet connection.'\n\t\t)\n\t}\n\tspin.stop()\n\n\tif (projects.length === 0) {\n\t\tthrow new CLIError(\n\t\t\t'NO_PROJECTS',\n\t\t\t'No projects found.',\n\t\t\t'Create a project at https://appfunnel.net first.'\n\t\t)\n\t}\n\n\tconst projectId = await select({\n\t\tmessage: 'Select a project',\n\t\tchoices: projects.map((p) => ({\n\t\t\tname: `${p.name} ${pc.dim(`(${p.id})`)}`,\n\t\t\tvalue: p.id,\n\t\t})),\n\t})\n\n\tconst project = projects.find((p) => p.id === projectId)!\n\n\tconst s = log.spinner(`Creating ${name}...`)\n\n\t// Create directory structure\n\tmkdirSync(join(dir, 'src', 'pages'), { recursive: true })\n\tmkdirSync(join(dir, 'src', 'components'), { recursive: true })\n\tmkdirSync(join(dir, 'locales'), { recursive: true })\n\n\t// package.json\n\twriteFileSync(\n\t\tjoin(dir, 'package.json'),\n\t\tJSON.stringify(\n\t\t\t{\n\t\t\t\tname,\n\t\t\t\tversion: '0.1.0',\n\t\t\t\tprivate: true,\n\t\t\t\ttype: 'module',\n\t\t\t\tscripts: {\n\t\t\t\t\tdev: 'appfunnel dev',\n\t\t\t\t\tbuild: 'appfunnel build',\n\t\t\t\t\tpublish: 'appfunnel publish',\n\t\t\t\t},\n\t\t\t\tdependencies: {\n\t\t\t\t\t'@appfunnel/sdk': '^0.1.0',\n\t\t\t\t\treact: '^18.3.0',\n\t\t\t\t\t'react-dom': '^18.3.0',\n\t\t\t\t},\n\t\t\t\tdevDependencies: {\n\t\t\t\t\tappfunnel: '^0.1.0',\n\t\t\t\t\ttypescript: '^5.4.0',\n\t\t\t\t\t'@types/react': '^18.2.0',\n\t\t\t\t\t'@types/react-dom': '^18.2.0',\n\t\t\t\t\tvite: '^6.0.0',\n\t\t\t\t\t'@vitejs/plugin-react': '^4.0.0',\n\t\t\t\t\ttailwindcss: '^4.0.0',\n\t\t\t\t\t'@tailwindcss/vite': '^4.0.0',\n\t\t\t\t},\n\t\t\t},\n\t\t\tnull,\n\t\t\t2\n\t\t) + '\\n'\n\t)\n\n\t// tsconfig.json\n\twriteFileSync(\n\t\tjoin(dir, 'tsconfig.json'),\n\t\tJSON.stringify(\n\t\t\t{\n\t\t\t\tcompilerOptions: {\n\t\t\t\t\ttarget: 'ES2020',\n\t\t\t\t\tmodule: 'ESNext',\n\t\t\t\t\tmoduleResolution: 'bundler',\n\t\t\t\t\tjsx: 'react-jsx',\n\t\t\t\t\tstrict: true,\n\t\t\t\t\tesModuleInterop: true,\n\t\t\t\t\tskipLibCheck: true,\n\t\t\t\t\tpaths: {\n\t\t\t\t\t\t'@/*': ['./src/*'],\n\t\t\t\t\t},\n\t\t\t\t\tbaseUrl: '.',\n\t\t\t\t},\n\t\t\t\tinclude: ['src'],\n\t\t\t},\n\t\t\tnull,\n\t\t\t2\n\t\t) + '\\n'\n\t)\n\n\t// tailwind + css\n\twriteFileSync(join(dir, 'src', 'app.css'), `@import \"tailwindcss\";\\n`)\n\n\t// appfunnel.config.ts\n\twriteFileSync(\n\t\tjoin(dir, 'appfunnel.config.ts'),\n\t\t`import { defineConfig } from '@appfunnel/sdk'\n\nexport default defineConfig({\n projectId: '${projectId}',\n name: '${name}',\n defaultLocale: 'en',\n\n variables: {\n 'answers.goal': { type: 'string' },\n },\n\n queryParams: ['utm_source', 'utm_medium', 'utm_campaign'],\n\n products: {\n items: [],\n },\n})\n`\n\t)\n\n\t// funnel.tsx\n\twriteFileSync(\n\t\tjoin(dir, 'src', 'funnel.tsx'),\n\t\t`import './app.css'\n\nexport default function Funnel({ children }: { children: React.ReactNode }) {\n return (\n <div className=\"min-h-screen\">\n {children}\n </div>\n )\n}\n`\n\t)\n\n\t// Example page\n\twriteFileSync(\n\t\tjoin(dir, 'src', 'pages', 'index.tsx'),\n\t\t`import { definePage, useVariable, useNavigation } from '@appfunnel/sdk'\n\nexport const page = definePage({\n name: 'Landing',\n type: 'default',\n routes: [],\n})\n\nexport default function Landing() {\n const [goal, setGoal] = useVariable<string>('answers.goal')\n const { goToNextPage } = useNavigation()\n\n return (\n <div className=\"flex min-h-screen items-center justify-center p-4\">\n <div className=\"w-full max-w-md space-y-6\">\n <h1 className=\"text-3xl font-bold text-center\">Welcome</h1>\n <input\n type=\"text\"\n value={goal}\n onChange={(e) => setGoal(e.target.value)}\n placeholder=\"What's your goal?\"\n className=\"w-full rounded-xl border p-4 text-lg\"\n />\n <button\n onClick={goToNextPage}\n disabled={!goal.trim()}\n className=\"w-full rounded-xl bg-blue-600 py-4 text-lg font-bold text-white disabled:opacity-50\"\n >\n Continue\n </button>\n </div>\n </div>\n )\n}\n`\n\t)\n\n\t// locales/en.json\n\twriteFileSync(\n\t\tjoin(dir, 'locales', 'en.json'),\n\t\tJSON.stringify({ welcome: 'Welcome' }, null, 2) + '\\n'\n\t)\n\n\t// .gitignore\n\twriteFileSync(\n\t\tjoin(dir, '.gitignore'),\n\t\t`node_modules\ndist\n.appfunnel\n`\n\t)\n\n\ts.stop()\n\n\tconsole.log()\n\tlog.success(`Created ${pc.bold(name)} for project ${pc.bold(project.name)}`)\n\tconsole.log()\n\tconsole.log(` ${pc.dim('cd')} ${name}`)\n\tconsole.log(` ${pc.dim('npm install')}`)\n\tconsole.log(` ${pc.dim('appfunnel dev')}`)\n\tconsole.log()\n}\n","import { createServer } from 'node:http'\nimport { randomUUID } from 'node:crypto'\nimport open from 'open'\nimport * as log from '../lib/logger.js'\nimport { writeCredentials } from '../lib/auth.js'\n\nconst AUTH_BASE_URL = 'https://appfunnel.net'\nconst TIMEOUT_MS = 120_000 // 2 minutes\n\nexport async function loginCommand(): Promise<void> {\n\tconst state = randomUUID()\n\n\treturn new Promise<void>((resolve, reject) => {\n\t\tconst server = createServer((req, res) => {\n\t\t\tconst url = new URL(req.url || '/', `http://localhost`)\n\t\t\tif (url.pathname !== '/callback') {\n\t\t\t\tres.writeHead(404)\n\t\t\t\tres.end('Not found')\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tconst token = url.searchParams.get('token')\n\t\t\tconst returnedState = url.searchParams.get('state')\n\t\t\tconst userId = url.searchParams.get('userId') || ''\n\t\t\tconst email = url.searchParams.get('email') || ''\n\t\t\tconst expiresAt = url.searchParams.get('expiresAt') || ''\n\n\t\t\tif (returnedState !== state) {\n\t\t\t\tres.writeHead(400)\n\t\t\t\tres.end('Invalid state parameter. Please try again.')\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif (!token) {\n\t\t\t\tres.writeHead(400)\n\t\t\t\tres.end('No token received. Please try again.')\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// Store credentials\n\t\t\twriteCredentials({ token, userId, email, expiresAt })\n\n\t\t\t// Send success page\n\t\t\tres.writeHead(200, { 'Content-Type': 'text/html' })\n\t\t\tres.end(`\n <!DOCTYPE html>\n <html>\n <body style=\"font-family: system-ui; display: flex; align-items: center; justify-content: center; height: 100vh; margin: 0;\">\n <div style=\"text-align: center;\">\n <h1>Logged in!</h1>\n <p>You can close this tab and return to the terminal.</p>\n </div>\n </body>\n </html>\n `)\n\n\t\t\t// Clean up\n\t\t\tspinner.stop()\n\t\t\tlog.success(`Logged in as ${email || userId}`)\n\t\t\tserver.close()\n\t\t\tresolve()\n\t\t})\n\n\t\t// Listen on random port\n\t\tserver.listen(0, '127.0.0.1', () => {\n\t\t\tconst addr = server.address()\n\t\t\tif (!addr || typeof addr === 'string') {\n\t\t\t\treject(new Error('Failed to start local server'))\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tconst port = addr.port\n\t\t\tconst authUrl = `${AUTH_BASE_URL}/cli/authorize?port=${port}&state=${state}`\n\n\t\t\tlog.info('Opening browser for authentication...')\n\t\t\topen(authUrl).catch(() => {\n\t\t\t\tlog.warn(`Could not open browser. Please visit:\\n ${authUrl}`)\n\t\t\t})\n\t\t})\n\n\t\tconst spinner = log.spinner('Waiting for authentication...')\n\n\t\t// Timeout\n\t\tconst timeout = setTimeout(() => {\n\t\t\tspinner.stop()\n\t\t\tserver.close()\n\t\t\treject(new Error('Authentication timed out. Please try again.'))\n\t\t}, TIMEOUT_MS)\n\n\t\tserver.on('close', () => clearTimeout(timeout))\n\t})\n}\n","import pc from 'picocolors'\nimport { requireAuth } from '../lib/auth.js'\nimport { CLIError } from '../lib/errors.js'\nimport * as logger from '../lib/logger.js'\n\nconst DEFAULT_API_BASE = 'https://api.appfunnel.net'\n\nexport async function whoamiCommand(): Promise<void> {\n const creds = requireAuth()\n\n const spin = logger.spinner('Verifying credentials…')\n\n try {\n const response = await fetch(`${DEFAULT_API_BASE}/user`, {\n headers: {\n Authorization: creds.token,\n 'Content-Type': 'application/json',\n },\n })\n\n if (!response.ok) {\n spin.stop()\n throw new CLIError(\n 'AUTH_EXPIRED',\n 'Token is no longer valid.',\n \"Run 'appfunnel login' to re-authenticate.\",\n )\n }\n\n const user = (await response.json()) as { email: string; id: string }\n spin.stop()\n\n logger.success(`Logged in as ${pc.bold(user.email)}`)\n logger.info(`User ID: ${pc.dim(user.id)}`)\n\n if (creds.expiresAt) {\n const expiresAt = new Date(creds.expiresAt)\n logger.info(`Token expires: ${pc.dim(expiresAt.toLocaleString())}`)\n }\n } catch (err) {\n spin.stop()\n if (err instanceof CLIError) throw err\n throw new CLIError(\n 'API_ERROR',\n 'Failed to reach the API. Check your internet connection.',\n )\n }\n}\n","import { existsSync, readFileSync } from 'node:fs'\nimport { join, resolve } from 'node:path'\nimport { CLIError } from './errors.js'\n\nexport interface AppFunnelConfig {\n projectId: string\n name: string\n funnelId?: string\n initialPage?: string\n defaultLocale?: string\n variables: Record<string, { type: string; default?: unknown; persist?: boolean }>\n queryParams?: string[]\n products?: {\n items: Array<{ id: string; name: string; storePriceId: string }>\n defaultId?: string\n }\n settings?: { apiBaseUrl?: string }\n integrations?: Record<string, Record<string, unknown>>\n pages?: Record<string, { name: string; type: string; slug?: string }>\n routes?: Record<string, Array<{ to: string; when?: unknown }>>\n}\n\nconst CONFIG_FILE = 'appfunnel.config.ts'\n\n/**\n * Load and evaluate the appfunnel.config.ts file using esbuild (a Vite dep).\n */\nexport async function loadConfig(cwd: string): Promise<AppFunnelConfig> {\n const configPath = join(cwd, CONFIG_FILE)\n\n if (!existsSync(configPath)) {\n throw new CLIError(\n 'CONFIG_NOT_FOUND',\n `No ${CONFIG_FILE} found in ${cwd}.`,\n \"Run 'appfunnel init' to create a new project, or cd into your project directory.\",\n )\n }\n\n // Use esbuild (already installed as a Vite dep) to transpile TS → JS\n const { transform } = await import('esbuild')\n const raw = readFileSync(configPath, 'utf-8')\n\n const result = await transform(raw, {\n loader: 'ts',\n format: 'esm',\n target: 'es2022',\n })\n\n // Evaluate the transpiled code\n // Strip the import of defineConfig since it's just a passthrough\n const code = result.code\n .replace(/import\\s*\\{[^}]*\\}\\s*from\\s*['\"]@appfunnel\\/sdk['\"]\\s*;?/g, '')\n .replace(/\\bdefineConfig\\s*\\(/g, '(')\n\n // Use dynamic import via data URI\n const dataUri = `data:text/javascript;base64,${Buffer.from(code).toString('base64')}`\n const mod = await import(dataUri)\n const config = mod.default as AppFunnelConfig\n\n if (!config || !config.projectId) {\n throw new CLIError(\n 'CONFIG_NOT_FOUND',\n `Invalid config in ${CONFIG_FILE}: missing projectId.`,\n 'Make sure your config exports a valid object with defineConfig().',\n )\n }\n\n return config\n}\n\n/**\n * Resolve the absolute path to the project's src directory.\n */\nexport function resolveSrcDir(cwd: string): string {\n return resolve(cwd, 'src')\n}\n\n/**\n * Resolve the absolute path to the pages directory.\n */\nexport function resolvePagesDir(cwd: string): string {\n return resolve(cwd, 'src', 'pages')\n}\n","import { readFileSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { CLIError } from './errors.js'\n\ninterface PackageJson {\n version: string\n}\n\n/**\n * Check that CLI major.minor matches the installed @appfunnel/sdk version.\n */\nexport function checkVersionCompatibility(cwd: string): void {\n const cliVersion = getCliVersion()\n const sdkVersion = getSdkVersion(cwd)\n\n const [cliMajor, cliMinor] = cliVersion.split('.').map(Number)\n const [sdkMajor, sdkMinor] = sdkVersion.split('.').map(Number)\n\n if (cliMajor !== sdkMajor || cliMinor !== sdkMinor) {\n throw new CLIError(\n 'VERSION_MISMATCH',\n `CLI version ${cliVersion} requires @appfunnel/sdk ^${cliMajor}.${cliMinor}.0, but found ${sdkVersion}.`,\n \"Run 'npm install @appfunnel/sdk@latest' to update.\",\n )\n }\n}\n\nfunction getCliVersion(): string {\n try {\n const pkg = JSON.parse(\n readFileSync(new URL('../../package.json', import.meta.url), 'utf-8'),\n ) as PackageJson\n return pkg.version\n } catch {\n return '0.0.0'\n }\n}\n\nfunction getSdkVersion(cwd: string): string {\n try {\n const pkgPath = join(cwd, 'node_modules', '@appfunnel', 'sdk', 'package.json')\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8')) as PackageJson\n return pkg.version\n } catch {\n throw new CLIError(\n 'VERSION_MISMATCH',\n '@appfunnel/sdk is not installed.',\n \"Run 'npm install @appfunnel/sdk' to install it.\",\n )\n }\n}\n","import { readdirSync, readFileSync, existsSync } from 'node:fs'\nimport { join, basename } from 'node:path'\nimport { resolvePagesDir } from '../lib/config.js'\nimport { CLIError } from '../lib/errors.js'\nimport type ts from 'typescript'\n\nexport interface PageDefinition {\n name: string\n type?: string\n slug?: string\n routes?: Array<{ to: string; when?: unknown }>\n}\n\n/**\n * Scan src/pages/ for .tsx files and return page keys.\n */\nexport function scanPages(cwd: string): string[] {\n const pagesDir = resolvePagesDir(cwd)\n\n if (!existsSync(pagesDir)) {\n throw new CLIError(\n 'NO_PAGES',\n 'No src/pages/ directory found.',\n 'Create src/pages/ and add at least one .tsx page file.',\n )\n }\n\n const files = readdirSync(pagesDir)\n .filter((f) => f.endsWith('.tsx') && !f.startsWith('_'))\n .map((f) => basename(f, '.tsx'))\n .sort()\n\n if (files.length === 0) {\n throw new CLIError(\n 'NO_PAGES',\n 'No page files found in src/pages/.',\n 'Add .tsx files to src/pages/. Each file is a funnel page.',\n )\n }\n\n return files\n}\n\n/**\n * Extract definePage() metadata from each page file using the TypeScript compiler API.\n */\nexport async function extractPageDefinitions(\n cwd: string,\n pageKeys: string[],\n): Promise<Record<string, PageDefinition>> {\n const ts = await import('typescript') as typeof import('typescript')\n const pagesDir = resolvePagesDir(cwd)\n const result: Record<string, PageDefinition> = {}\n\n for (const key of pageKeys) {\n const filePath = join(pagesDir, `${key}.tsx`)\n const source = readFileSync(filePath, 'utf-8')\n\n const definition = extractDefinePage(ts, source, filePath)\n if (definition) {\n result[key] = definition\n } else {\n // Page without definePage() — use defaults\n result[key] = {\n name: key,\n type: 'default',\n }\n }\n }\n\n return result\n}\n\n/**\n * Parse a single page file and extract the definePage() argument.\n */\nfunction extractDefinePage(\n ts: typeof import('typescript'),\n source: string,\n fileName: string,\n): PageDefinition | null {\n const sourceFile = ts.createSourceFile(\n fileName,\n source,\n ts.ScriptTarget.Latest,\n true,\n ts.ScriptKind.TSX,\n )\n\n let definition: PageDefinition | null = null\n\n function visit(node: ts.Node): void {\n // Look for: export const page = definePage({ ... })\n if (ts.isVariableStatement(node)) {\n const isExported = node.modifiers?.some(\n (m) => m.kind === ts.SyntaxKind.ExportKeyword,\n )\n if (!isExported) return\n\n for (const decl of node.declarationList.declarations) {\n if (\n ts.isIdentifier(decl.name) &&\n decl.name.text === 'page' &&\n decl.initializer &&\n ts.isCallExpression(decl.initializer)\n ) {\n const call = decl.initializer\n const callee = call.expression\n\n // Check if callee is \"definePage\"\n if (ts.isIdentifier(callee) && callee.text === 'definePage') {\n const arg = call.arguments[0]\n if (arg && ts.isObjectLiteralExpression(arg)) {\n definition = extractObjectLiteral(ts, arg) as unknown as PageDefinition\n }\n }\n }\n }\n }\n\n ts.forEachChild(node, visit)\n }\n\n ts.forEachChild(sourceFile, visit)\n return definition\n}\n\n/**\n * Recursively extract a TypeScript object literal into a plain JS object.\n */\nfunction extractObjectLiteral(\n ts: typeof import('typescript'),\n node: ts.ObjectLiteralExpression,\n): Record<string, unknown> {\n const result: Record<string, unknown> = {}\n\n for (const prop of node.properties) {\n if (!ts.isPropertyAssignment(prop)) continue\n if (!ts.isIdentifier(prop.name) && !ts.isStringLiteral(prop.name)) continue\n\n const key = ts.isIdentifier(prop.name) ? prop.name.text : prop.name.text\n result[key] = extractValue(ts, prop.initializer)\n }\n\n return result\n}\n\n/**\n * Extract a static value from a TypeScript AST node.\n */\nfunction extractValue(\n ts: typeof import('typescript'),\n node: ts.Expression,\n): unknown {\n // String literal\n if (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node)) {\n return node.text\n }\n\n // Numeric literal\n if (ts.isNumericLiteral(node)) {\n return Number(node.text)\n }\n\n // Boolean / null / undefined\n if (node.kind === ts.SyntaxKind.TrueKeyword) return true\n if (node.kind === ts.SyntaxKind.FalseKeyword) return false\n if (node.kind === ts.SyntaxKind.NullKeyword) return null\n if (node.kind === ts.SyntaxKind.UndefinedKeyword) return undefined\n\n // Array literal\n if (ts.isArrayLiteralExpression(node)) {\n return node.elements.map((el) => extractValue(ts, el))\n }\n\n // Object literal\n if (ts.isObjectLiteralExpression(node)) {\n return extractObjectLiteral(ts, node)\n }\n\n // Prefix unary (negative numbers)\n if (ts.isPrefixUnaryExpression(node) && node.operator === ts.SyntaxKind.MinusToken) {\n const operand = extractValue(ts, node.operand)\n if (typeof operand === 'number') return -operand\n }\n\n // Can't statically evaluate — return undefined\n return undefined\n}\n","import { join } from 'node:path'\nimport type { AppFunnelConfig } from '../lib/config.js'\nimport type { PageDefinition } from '../extract/pages.js'\n\ninterface EntryOptions {\n config: AppFunnelConfig\n pages: Record<string, PageDefinition>\n pagesDir: string\n funnelTsxPath: string\n isDev: boolean\n}\n\n/**\n * Generate the virtual entry module source code.\n *\n * This creates a mini SPA that:\n * - Mounts FunnelProvider once\n * - Wraps with the user's funnel.tsx\n * - Client-side routes between pages via pushState\n * - Lazy-loads page components\n */\nexport function generateEntrySource(options: EntryOptions): string {\n const { config, pages, pagesDir, funnelTsxPath, isDev } = options\n const pageKeys = Object.keys(pages)\n\n // Merge definePage() metadata into config\n const mergedPages: Record<string, unknown> = {}\n const mergedRoutes: Record<string, unknown> = {}\n\n for (const [key, def] of Object.entries(pages)) {\n mergedPages[key] = {\n name: def.name || key,\n type: def.type || 'default',\n slug: def.slug || key,\n }\n if (def.routes) {\n mergedRoutes[key] = def.routes\n }\n }\n\n // Override config pages/routes with extracted ones\n const fullConfig = {\n ...config,\n pages: { ...config.pages, ...mergedPages },\n routes: { ...config.routes, ...mergedRoutes },\n }\n\n // Generate lazy imports for each page\n const pageImports = pageKeys\n .map(\n (key) =>\n ` '${key}': lazy(() => import('${join(pagesDir, key + '.tsx').replace(/\\\\/g, '/')}')),`,\n )\n .join('\\n')\n\n // Build slug → key mapping\n const slugMap: Record<string, string> = {}\n for (const [key, def] of Object.entries(pages)) {\n slugMap[def.slug || key] = key\n }\n\n const trackingCode = isDev\n ? `\n// Dev mode: mock tracking — log events to console\nconst originalFetch = globalThis.fetch;\nglobalThis.__APPFUNNEL_DEV__ = true;\n`\n : ''\n\n return `\nimport { StrictMode, lazy, Suspense, useState, useEffect, useCallback } from 'react'\nimport { createRoot } from 'react-dom/client'\nimport { FunnelProvider } from '@appfunnel/sdk/internal'\nimport FunnelWrapper from '${funnelTsxPath.replace(/\\\\/g, '/')}'\n\n${trackingCode}\n\nconst pages = {\n${pageImports}\n}\n\nconst config = ${JSON.stringify(fullConfig, null, 2)}\n\nconst slugToKey = ${JSON.stringify(slugMap)}\nconst keyToSlug = ${JSON.stringify(\n Object.fromEntries(Object.entries(slugMap).map(([s, k]) => [k, s])),\n )}\n\nfunction getInitialPage() {\n const path = window.location.pathname.split('/').filter(Boolean).pop() || ''\n return slugToKey[path] || '${config.initialPage || pageKeys[0] || 'index'}'\n}\n\nfunction App() {\n const [currentPage, setCurrentPage] = useState(getInitialPage)\n\n useEffect(() => {\n const handlePopState = () => {\n const path = window.location.pathname.split('/').filter(Boolean).pop() || ''\n const pageKey = slugToKey[path]\n if (pageKey && pageKey !== currentPage) {\n setCurrentPage(pageKey)\n }\n }\n window.addEventListener('popstate', handlePopState)\n return () => window.removeEventListener('popstate', handlePopState)\n }, [currentPage])\n\n // Expose navigation to FunnelProvider's router\n useEffect(() => {\n window.__APPFUNNEL_NAVIGATE__ = (pageKey) => {\n setCurrentPage(pageKey)\n const slug = keyToSlug[pageKey] || pageKey\n window.history.pushState(null, '', '/' + slug)\n }\n return () => { delete window.__APPFUNNEL_NAVIGATE__ }\n }, [])\n\n const PageComponent = pages[currentPage]\n\n if (!PageComponent) {\n return <div style={{ padding: '2rem', color: 'red' }}>Page not found: {currentPage}</div>\n }\n\n return (\n <FunnelProvider\n config={config}\n initialPage={currentPage}\n apiBaseUrl={${isDev ? \"''\" : \"config.settings?.apiBaseUrl || ''\"}}\n >\n <FunnelWrapper>\n <Suspense fallback={null}>\n <PageComponent />\n </Suspense>\n </FunnelWrapper>\n </FunnelProvider>\n )\n}\n\ncreateRoot(document.getElementById('root')).render(\n <StrictMode>\n <App />\n </StrictMode>\n)\n`\n}\n","/**\n * Generate the index.html template for the dev server and production build.\n */\nexport function generateHtml(title: string = 'AppFunnel'): string {\n return `<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <title>${title}</title>\n </head>\n <body>\n <div id=\"root\"></div>\n <script type=\"module\" src=\"/@appfunnel/entry\"></script>\n </body>\n</html>`\n}\n","import { resolve, join } from 'node:path'\nimport { existsSync } from 'node:fs'\nimport type { Plugin, ViteDevServer } from 'vite'\nimport type { AppFunnelConfig } from '../lib/config.js'\nimport type { PageDefinition } from '../extract/pages.js'\nimport { generateEntrySource } from './entry.js'\nimport { generateHtml } from './html.js'\n\nconst VIRTUAL_ENTRY_ID = '@appfunnel/entry'\nconst RESOLVED_VIRTUAL_ENTRY_ID = '\\0' + VIRTUAL_ENTRY_ID\nconst VIRTUAL_HTML_ID = '@appfunnel/index.html'\n\ninterface PluginOptions {\n cwd: string\n config: AppFunnelConfig\n pages: Record<string, PageDefinition>\n isDev: boolean\n onPagesChange?: () => Promise<void>\n}\n\nexport function appfunnelPlugin(options: PluginOptions): Plugin {\n const { cwd, config, isDev } = options\n let pages = options.pages\n const pagesDir = resolve(cwd, 'src', 'pages')\n const funnelTsxPath = resolve(cwd, 'src', 'funnel.tsx')\n\n let server: ViteDevServer | undefined\n\n function getEntrySource(): string {\n return generateEntrySource({\n config,\n pages,\n pagesDir,\n funnelTsxPath,\n isDev,\n })\n }\n\n return {\n name: 'appfunnel',\n\n config() {\n return {\n resolve: {\n alias: {\n '@': resolve(cwd, 'src'),\n },\n },\n // Ensure we can import .tsx files\n esbuild: {\n jsx: 'automatic',\n },\n optimizeDeps: {\n include: ['react', 'react-dom', 'react/jsx-runtime'],\n },\n }\n },\n\n resolveId(id) {\n if (id === VIRTUAL_ENTRY_ID) {\n return RESOLVED_VIRTUAL_ENTRY_ID\n }\n return null\n },\n\n load(id) {\n if (id === RESOLVED_VIRTUAL_ENTRY_ID) {\n return getEntrySource()\n }\n return null\n },\n\n configureServer(devServer) {\n server = devServer\n\n // Watch for page file additions/removals\n const watcher = devServer.watcher\n watcher.add(pagesDir)\n\n const handlePagesChange = async () => {\n if (options.onPagesChange) {\n await options.onPagesChange()\n }\n // Invalidate the virtual module and trigger full reload\n const mod = devServer.moduleGraph.getModuleById(RESOLVED_VIRTUAL_ENTRY_ID)\n if (mod) {\n devServer.moduleGraph.invalidateModule(mod)\n }\n devServer.ws.send({ type: 'full-reload' })\n }\n\n watcher.on('add', (file) => {\n if (file.startsWith(pagesDir) && file.endsWith('.tsx')) {\n handlePagesChange()\n }\n })\n\n watcher.on('unlink', (file) => {\n if (file.startsWith(pagesDir) && file.endsWith('.tsx')) {\n handlePagesChange()\n }\n })\n\n // Watch appfunnel.config.ts for changes → full reload\n const configPath = join(cwd, 'appfunnel.config.ts')\n if (existsSync(configPath)) {\n watcher.add(configPath)\n watcher.on('change', (file) => {\n if (file === configPath) {\n devServer.ws.send({ type: 'full-reload' })\n }\n })\n }\n\n // SPA fallback middleware — serve index.html for all routes\n return () => {\n devServer.middlewares.use((req, res, next) => {\n // Skip Vite internal routes and static assets\n if (\n req.url?.startsWith('/@') ||\n req.url?.startsWith('/node_modules') ||\n req.url?.startsWith('/src') ||\n req.url?.includes('.')\n ) {\n return next()\n }\n\n // Serve the HTML shell for all other routes\n const html = generateHtml(config.name || 'AppFunnel')\n devServer.transformIndexHtml(req.url || '/', html).then((transformed) => {\n res.statusCode = 200\n res.setHeader('Content-Type', 'text/html')\n res.end(transformed)\n }).catch(next)\n })\n }\n },\n\n // For production build: inject the HTML as the input\n transformIndexHtml(html) {\n return html\n },\n }\n}\n","import { resolve } from 'node:path'\nimport pc from 'picocolors'\nimport * as log from '../lib/logger.js'\nimport { requireAuth } from '../lib/auth.js'\nimport { loadConfig, resolvePagesDir } from '../lib/config.js'\nimport { checkVersionCompatibility } from '../lib/version.js'\nimport { scanPages, extractPageDefinitions } from '../extract/pages.js'\nimport { appfunnelPlugin } from '../vite/plugin.js'\n\nexport async function devCommand(options: { port?: number }): Promise<void> {\n const cwd = process.cwd()\n const port = options.port || 5173\n\n // 1. Auth check\n requireAuth()\n\n // 2. Version check\n checkVersionCompatibility(cwd)\n\n // 3. Load config\n const s = log.spinner('Loading config...')\n const config = await loadConfig(cwd)\n\n // 4. Scan and extract pages\n let pageKeys = scanPages(cwd)\n let pages = await extractPageDefinitions(cwd, pageKeys)\n s.stop()\n\n log.info(`Found ${pageKeys.length} pages: ${pageKeys.join(', ')}`)\n\n // 5. Start Vite dev server\n const { createServer } = await import('vite')\n const react = await import('@vitejs/plugin-react')\n\n const server = await createServer({\n root: cwd,\n server: {\n port,\n strictPort: false,\n },\n plugins: [\n react.default(),\n appfunnelPlugin({\n cwd,\n config,\n pages,\n isDev: true,\n async onPagesChange() {\n // Re-scan pages when files are added/removed\n pageKeys = scanPages(cwd)\n pages = await extractPageDefinitions(cwd, pageKeys)\n log.info(`Pages updated: ${pageKeys.join(', ')}`)\n },\n }),\n ],\n css: {\n postcss: cwd,\n },\n })\n\n await server.listen()\n\n const address = server.resolvedUrls?.local?.[0] || `http://localhost:${port}`\n\n console.log()\n console.log(` ${pc.bold(config.name || 'AppFunnel')} dev server`)\n console.log()\n console.log(` ${pc.dim('Local:')} ${pc.cyan(address)}`)\n console.log(` ${pc.dim('Pages:')} ${pageKeys.length}`)\n console.log(` ${pc.dim('Tracking:')} ${pc.yellow('mocked (console)')}`)\n console.log()\n console.log(` ${pc.dim('Press')} ${pc.bold('Ctrl+C')} ${pc.dim('to stop')}`)\n console.log()\n}\n","import { resolve, join } from 'node:path'\nimport { readFileSync, writeFileSync, mkdirSync, statSync, readdirSync } from 'node:fs'\nimport pc from 'picocolors'\nimport * as log from '../lib/logger.js'\nimport { requireAuth } from '../lib/auth.js'\nimport { loadConfig } from '../lib/config.js'\nimport { checkVersionCompatibility } from '../lib/version.js'\nimport { scanPages, extractPageDefinitions } from '../extract/pages.js'\nimport { appfunnelPlugin } from '../vite/plugin.js'\nimport { generateHtml } from '../vite/html.js'\nimport { formatWarning } from '../lib/errors.js'\nimport { CLIError } from '../lib/errors.js'\n\nconst MAX_TOTAL_SIZE = 2 * 1024 * 1024 // 2MB\nconst MAX_PAGE_SIZE = 500 * 1024 // 500KB\n\nexport async function buildCommand(): Promise<void> {\n const cwd = process.cwd()\n\n // 1. Auth + version check\n requireAuth()\n checkVersionCompatibility(cwd)\n\n // 2. Load config + pages\n const s = log.spinner('Analyzing pages...')\n const config = await loadConfig(cwd)\n const pageKeys = scanPages(cwd)\n const pages = await extractPageDefinitions(cwd, pageKeys)\n s.stop()\n\n // 3. Validate routes\n validateRoutes(config, pages, pageKeys)\n\n log.info(`Building ${pageKeys.length} pages...`)\n\n // 4. Build with Vite\n const outDir = resolve(cwd, '.appfunnel')\n const { build } = await import('vite')\n const react = await import('@vitejs/plugin-react')\n\n // Write index.html for Vite to use as entry\n const htmlPath = resolve(cwd, 'index.html')\n const htmlContent = generateHtml(config.name || 'AppFunnel')\n writeFileSync(htmlPath, htmlContent)\n\n try {\n await build({\n root: cwd,\n build: {\n outDir,\n emptyOutDir: true,\n sourcemap: false,\n minify: 'esbuild',\n rollupOptions: {\n output: {\n manualChunks(id) {\n if (id.includes('node_modules/react')) {\n return 'vendor-react'\n }\n if (id.includes('@appfunnel/sdk')) {\n return 'vendor-sdk'\n }\n },\n },\n },\n },\n plugins: [\n react.default(),\n appfunnelPlugin({\n cwd,\n config,\n pages,\n isDev: false,\n }),\n ],\n css: {\n postcss: cwd,\n },\n logLevel: 'warn',\n })\n } finally {\n // Clean up the temporary index.html\n try {\n const { unlinkSync } = await import('node:fs')\n unlinkSync(htmlPath)\n } catch { /* ignore */ }\n }\n\n // 5. Generate manifest\n const mergedPages: Record<string, unknown> = {}\n const mergedRoutes: Record<string, unknown> = {}\n\n for (const [key, def] of Object.entries(pages)) {\n mergedPages[key] = {\n name: def.name || key,\n type: def.type || 'default',\n slug: def.slug || key,\n }\n if (def.routes) {\n mergedRoutes[key] = def.routes\n }\n }\n\n // Collect asset info\n const assets = collectAssets(outDir)\n const totalSize = assets.reduce((sum, a) => sum + a.size, 0)\n\n const manifest = {\n version: 1,\n sdkVersion: getSdkVersion(cwd),\n projectId: config.projectId,\n funnelId: config.funnelId,\n pages: { ...config.pages, ...mergedPages },\n routes: { ...config.routes, ...mergedRoutes },\n variables: config.variables,\n queryParams: config.queryParams || [],\n defaultLocale: config.defaultLocale,\n assets,\n totalSize,\n }\n\n writeFileSync(join(outDir, 'manifest.json'), JSON.stringify(manifest, null, 2) + '\\n')\n\n // 6. Print report\n console.log()\n log.success('Build complete')\n console.log()\n console.log(` ${pc.dim('Output:')} .appfunnel/`)\n console.log(` ${pc.dim('Pages:')} ${pageKeys.length}`)\n console.log(` ${pc.dim('Size:')} ${formatSize(totalSize)}`)\n console.log()\n\n // Print per-asset sizes\n for (const asset of assets.filter(a => a.path.endsWith('.js'))) {\n const sizeStr = formatSize(asset.size)\n const isOver = asset.size > MAX_PAGE_SIZE\n console.log(` ${isOver ? pc.yellow('!') : pc.dim('·')} ${pc.dim(asset.path)} ${isOver ? pc.yellow(sizeStr) : pc.dim(sizeStr)}`)\n }\n console.log()\n\n // Warn on size limits\n if (totalSize > MAX_TOTAL_SIZE) {\n console.log(formatWarning(\n 'BUNDLE_TOO_LARGE',\n `Total bundle size (${formatSize(totalSize)}) exceeds the 2MB limit.`,\n 'This will be rejected on publish. Reduce dependencies or code-split.',\n ))\n console.log()\n }\n\n for (const asset of assets) {\n if (asset.size > MAX_PAGE_SIZE && asset.path.endsWith('.js')) {\n console.log(formatWarning(\n 'PAGE_SIZE',\n `${asset.path} is ${formatSize(asset.size)} (limit: 500KB).`,\n 'Consider reducing dependencies in this page.',\n ))\n }\n }\n}\n\nfunction validateRoutes(\n config: { variables: Record<string, unknown>; routes?: Record<string, unknown[]> },\n pages: Record<string, { routes?: Array<{ to: string; when?: unknown }> }>,\n pageKeys: string[],\n): void {\n const allPageKeys = new Set(pageKeys)\n const allVariables = new Set(Object.keys(config.variables))\n\n // Check routes from definePage()\n for (const [pageKey, def] of Object.entries(pages)) {\n if (!def.routes) continue\n for (const route of def.routes) {\n if (!allPageKeys.has(route.to)) {\n throw new CLIError(\n 'INVALID_ROUTE',\n `Page \"${pageKey}\" routes to \"${route.to}\" which does not exist.`,\n `Available pages: ${pageKeys.join(', ')}. Check src/pages/${pageKey}.tsx.`,\n )\n }\n\n // Validate condition variable references\n if (route.when) {\n validateConditionVariables(route.when, pageKey, allVariables)\n }\n }\n }\n}\n\nfunction validateConditionVariables(\n condition: unknown,\n pageKey: string,\n allVariables: Set<string>,\n): void {\n if (!condition || typeof condition !== 'object') return\n\n const cond = condition as Record<string, unknown>\n\n // Simple condition\n if (cond.variable && typeof cond.variable === 'string') {\n // Skip system variables (page.*, device.*, etc.) and user.* / query.*\n const prefix = cond.variable.split('.')[0]\n const systemPrefixes = ['page', 'device', 'browser', 'os', 'session', 'system', 'metadata', 'user', 'query', 'products', 'card', 'payment', 'stripe', 'subscription']\n if (!systemPrefixes.includes(prefix) && !allVariables.has(cond.variable)) {\n throw new CLIError(\n 'UNDEFINED_VARIABLE',\n `Route condition in \"${pageKey}\" references variable \"${cond.variable}\" which is not defined.`,\n `Add it to variables in appfunnel.config.ts: '${cond.variable}': { type: 'string' }`,\n )\n }\n }\n\n // Condition group\n if (Array.isArray(cond.rules)) {\n for (const rule of cond.rules) {\n validateConditionVariables(rule, pageKey, allVariables)\n }\n }\n}\n\nfunction collectAssets(outDir: string): Array<{ path: string; size: number }> {\n const assets: Array<{ path: string; size: number }> = []\n\n function walk(dir: string, prefix: string = ''): void {\n for (const entry of readdirSync(dir, { withFileTypes: true })) {\n const relPath = prefix ? `${prefix}/${entry.name}` : entry.name\n const fullPath = join(dir, entry.name)\n if (entry.isDirectory()) {\n walk(fullPath, relPath)\n } else if (entry.name !== 'manifest.json' && !entry.name.startsWith('.')) {\n assets.push({ path: relPath, size: statSync(fullPath).size })\n }\n }\n }\n\n walk(outDir)\n return assets\n}\n\nfunction formatSize(bytes: number): string {\n if (bytes < 1024) return `${bytes}B`\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`\n return `${(bytes / (1024 * 1024)).toFixed(2)}MB`\n}\n\nfunction getSdkVersion(cwd: string): string {\n try {\n const pkg = JSON.parse(readFileSync(join(cwd, 'node_modules', '@appfunnel', 'sdk', 'package.json'), 'utf-8'))\n return pkg.version\n } catch {\n return '0.0.0'\n }\n}\n","import { CLIError } from './errors.js'\n\nconst DEFAULT_API_BASE = 'https://api.appfunnel.net'\n\ninterface ApiOptions {\n\ttoken: string\n\tapiBaseUrl?: string\n}\n\nasync function apiFetch(\n\tpath: string,\n\toptions: ApiOptions & RequestInit\n): Promise<Response> {\n\tconst { token, apiBaseUrl, ...fetchOpts } = options\n\tconst base = apiBaseUrl || DEFAULT_API_BASE\n\tconst url = `${base}${path}`\n\n\t// Don't set Content-Type for FormData — let the browser set the boundary\n\tconst isFormData = fetchOpts.body instanceof FormData\n\tconst headers: Record<string, string> = {\n\t\tAuthorization: token,\n\t\t...((fetchOpts.headers as Record<string, string>) || {}),\n\t}\n\tif (!isFormData) {\n\t\theaders['Content-Type'] = 'application/json'\n\t}\n\n\tconst response = await fetch(url, {\n\t\t...fetchOpts,\n\t\theaders,\n\t})\n\n\tif (!response.ok) {\n\t\tconst body = await response.text().catch(() => '')\n\t\tlet message = `API request failed: ${response.status} ${response.statusText}`\n\t\ttry {\n\t\t\tconst parsed = JSON.parse(body)\n\t\t\tif (parsed.error) message = parsed.error\n\t\t\tif (parsed.message) message = parsed.message\n\t\t} catch {\n\t\t\t/* ignore parse errors */\n\t\t}\n\t\tconst error = new CLIError('API_ERROR', message)\n\t\terror.statusCode = response.status\n\t\tthrow error\n\t}\n\n\treturn response\n}\n\n/**\n * Fetch enriched price data for all store price IDs.\n */\nexport async function fetchPrices(\n\tprojectId: string,\n\tstorePriceIds: string[],\n\toptions: ApiOptions\n): Promise<Map<string, unknown>> {\n\tif (storePriceIds.length === 0) return new Map()\n\n\tconst response = await apiFetch(`/project/${projectId}/headless/prices`, {\n\t\t...options,\n\t\tmethod: 'POST',\n\t\tbody: JSON.stringify({ storePriceIds }),\n\t})\n\n\tconst data = (await response.json()) as { prices: Record<string, unknown> }\n\treturn new Map(Object.entries(data.prices || {}))\n}\n\n/**\n * Create a new headless funnel under a project.\n */\nexport async function createHeadlessFunnel(\n\tprojectId: string,\n\tname: string,\n\toptions: ApiOptions\n): Promise<{ funnelId: string; identifier: string }> {\n\tconst response = await apiFetch(`/project/${projectId}/headless/funnels`, {\n\t\t...options,\n\t\tmethod: 'POST',\n\t\tbody: JSON.stringify({ name }),\n\t})\n\n\treturn (await response.json()) as { funnelId: string; identifier: string }\n}\n\n/**\n * Publish a headless build.\n */\nexport async function publishBuild(\n\tprojectId: string,\n\tfunnelId: string,\n\tmanifest: unknown,\n\tassets: Array<{ path: string; content: Buffer; contentType: string }>,\n\toptions: ApiOptions\n): Promise<{ buildId: string; url: string }> {\n\t// Use multipart form data for assets\n\tconst formData = new FormData()\n\tformData.set('manifest', JSON.stringify(manifest))\n\tformData.set('funnelId', funnelId)\n\n\tfor (const asset of assets) {\n\t\tformData.append(\n\t\t\t'assets',\n\t\t\tnew Blob([new Uint8Array(asset.content)], { type: asset.contentType }),\n\t\t\tasset.path\n\t\t)\n\t}\n\n\ttry {\n\t\tconst response = await apiFetch(`/project/${projectId}/headless/publish`, {\n\t\t\t...options,\n\t\t\tmethod: 'POST',\n\t\t\tbody: formData,\n\t\t})\n\t\treturn (await response.json()) as { buildId: string; url: string }\n\t} catch (err) {\n\t\tif (err instanceof CLIError && err.code === 'API_ERROR') {\n\t\t\tif (err.statusCode === 413) {\n\t\t\t\tthrow new CLIError(\n\t\t\t\t\t'BUNDLE_TOO_LARGE',\n\t\t\t\t\terr.message,\n\t\t\t\t\t'Reduce page bundle sizes. Check for large dependencies.'\n\t\t\t\t)\n\t\t\t}\n\t\t\tif (err.statusCode === 409) {\n\t\t\t\tthrow new CLIError(\n\t\t\t\t\t'FUNNEL_NOT_HEADLESS',\n\t\t\t\t\terr.message,\n\t\t\t\t\t'Create a new headless funnel from the dashboard, or remove funnelId from config.'\n\t\t\t\t)\n\t\t\t}\n\t\t\tthrow new CLIError('PUBLISH_FAILED', err.message)\n\t\t}\n\t\tthrow err\n\t}\n}\n","import { resolve, join } from 'node:path'\nimport { readFileSync, existsSync } from 'node:fs'\nimport pc from 'picocolors'\nimport * as log from '../lib/logger.js'\nimport { requireAuth } from '../lib/auth.js'\nimport { loadConfig } from '../lib/config.js'\nimport { checkVersionCompatibility } from '../lib/version.js'\nimport { publishBuild } from '../lib/api.js'\nimport { CLIError } from '../lib/errors.js'\n\nconst MIME_TYPES: Record<string, string> = {\n '.js': 'application/javascript',\n '.css': 'text/css',\n '.html': 'text/html',\n '.json': 'application/json',\n '.svg': 'image/svg+xml',\n '.png': 'image/png',\n '.jpg': 'image/jpeg',\n '.woff2': 'font/woff2',\n '.woff': 'font/woff',\n}\n\nfunction getMimeType(path: string): string {\n const ext = path.substring(path.lastIndexOf('.'))\n return MIME_TYPES[ext] || 'application/octet-stream'\n}\n\nexport async function publishCommand(): Promise<void> {\n const cwd = process.cwd()\n\n // 1. Auth + version check\n const creds = requireAuth()\n checkVersionCompatibility(cwd)\n\n // 2. Load config\n const config = await loadConfig(cwd)\n\n // 3. Check build output exists\n const outDir = resolve(cwd, '.appfunnel')\n const manifestPath = join(outDir, 'manifest.json')\n\n if (!existsSync(manifestPath)) {\n throw new CLIError(\n 'BUILD_NOT_FOUND',\n 'No build output found.',\n \"Run 'appfunnel build' first.\",\n )\n }\n\n // 4. Read manifest\n const manifest = JSON.parse(readFileSync(manifestPath, 'utf-8'))\n const assets: Array<{ path: string; size: number }> = manifest.assets || []\n\n // 5. Read all asset files\n const s = log.spinner('Uploading build...')\n const assetPayloads: Array<{ path: string; content: Buffer; contentType: string }> = []\n\n for (const asset of assets) {\n const fullPath = join(outDir, asset.path)\n if (!existsSync(fullPath)) {\n s.stop()\n throw new CLIError(\n 'BUILD_NOT_FOUND',\n `Build asset missing: ${asset.path}`,\n \"Run 'appfunnel build' to regenerate.\",\n )\n }\n assetPayloads.push({\n path: asset.path,\n content: readFileSync(fullPath),\n contentType: getMimeType(asset.path),\n })\n }\n\n // 6. Publish\n const projectId = config.projectId\n const funnelId = config.funnelId\n\n if (!projectId) {\n s.stop()\n throw new CLIError(\n 'CONFIG_NOT_FOUND',\n 'No projectId in appfunnel.config.ts.',\n 'Add projectId to your config. You can find it in the dashboard.',\n )\n }\n\n if (!funnelId) {\n s.stop()\n throw new CLIError(\n 'CONFIG_NOT_FOUND',\n 'No funnelId in appfunnel.config.ts.',\n 'Add funnelId to your config, or create a new funnel from the dashboard.',\n )\n }\n\n const result = await publishBuild(\n projectId,\n funnelId,\n manifest,\n assetPayloads,\n { token: creds.token },\n )\n\n s.stop()\n\n // 7. Print result\n console.log()\n log.success('Published successfully')\n console.log()\n console.log(` ${pc.dim('Build ID:')} ${result.buildId}`)\n console.log(` ${pc.dim('URL:')} ${pc.cyan(result.url)}`)\n console.log(` ${pc.dim('Assets:')} ${assets.length} files`)\n console.log()\n}\n","#!/usr/bin/env node\nimport { readFileSync } from 'node:fs'\nimport { Command } from 'commander'\nimport pc from 'picocolors'\nimport { CLIError, formatError } from './lib/errors.js'\n\nfunction getCliVersion(): string {\n try {\n const pkg = JSON.parse(\n readFileSync(new URL('../package.json', import.meta.url), 'utf-8'),\n )\n return pkg.version\n } catch {\n return '0.0.0'\n }\n}\n\nconst program = new Command()\n\nprogram\n .name('appfunnel')\n .description('Build and publish headless AppFunnel projects')\n .version(getCliVersion())\n\nprogram\n .command('init <name>')\n .description('Create a new AppFunnel project')\n .action(async (name: string) => {\n const { initCommand } = await import('./commands/init.js')\n await initCommand(name)\n })\n\nprogram\n .command('login')\n .description('Authenticate with AppFunnel')\n .action(async () => {\n const { loginCommand } = await import('./commands/login.js')\n await loginCommand()\n })\n\nprogram\n .command('whoami')\n .description('Show the currently authenticated user')\n .action(async () => {\n const { whoamiCommand } = await import('./commands/whoami.js')\n await whoamiCommand()\n })\n\nprogram\n .command('dev')\n .description('Start the development server')\n .option('-p, --port <port>', 'Port number', '5173')\n .action(async (options: { port: string }) => {\n const { devCommand } = await import('./commands/dev.js')\n await devCommand({ port: parseInt(options.port, 10) })\n })\n\nprogram\n .command('build')\n .description('Build the funnel for production')\n .action(async () => {\n const { buildCommand } = await import('./commands/build.js')\n await buildCommand()\n })\n\nprogram\n .command('publish')\n .description('Publish the build to AppFunnel')\n .action(async () => {\n const { publishCommand } = await import('./commands/publish.js')\n await publishCommand()\n })\n\n// Global error handler\nprogram.hook('postAction', () => {})\n\nasync function main() {\n try {\n await program.parseAsync(process.argv)\n } catch (err) {\n if (err instanceof CLIError) {\n console.error(formatError(err))\n process.exit(1)\n }\n // Unknown error\n console.error(`${pc.red('ERROR')}: ${err instanceof Error ? err.message : String(err)}`)\n if (err instanceof Error && err.stack) {\n console.error(pc.dim(err.stack))\n }\n process.exit(1)\n }\n}\n\nmain()\n"],"mappings":"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","names":["pc","readFileSync","mkdirSync","writeFileSync","join","pc","resolve","spinner","pc","DEFAULT_API_BASE","existsSync","readFileSync","join","readFileSync","join","readFileSync","existsSync","join","join","resolve","join","existsSync","pc","createServer","resolve","join","readFileSync","writeFileSync","readdirSync","pc","getSdkVersion","DEFAULT_API_BASE","error","resolve","join","readFileSync","existsSync","pc","readFileSync","pc","getCliVersion","initCommand","loginCommand","whoamiCommand","devCommand","buildCommand","publishCommand"]}
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "appfunnel",
3
+ "version": "0.1.0",
4
+ "description": "CLI for building and publishing headless AppFunnel projects",
5
+ "type": "module",
6
+ "bin": {
7
+ "appfunnel": "dist/index.js"
8
+ },
9
+ "files": [
10
+ "dist"
11
+ ],
12
+ "engines": {
13
+ "node": ">=18"
14
+ },
15
+ "keywords": [
16
+ "appfunnel",
17
+ "funnel",
18
+ "cli"
19
+ ],
20
+ "homepage": "https://appfunnel.net",
21
+ "scripts": {
22
+ "build": "tsup",
23
+ "dev": "tsup --watch",
24
+ "typecheck": "tsc --noEmit",
25
+ "prepublishOnly": "npm run build"
26
+ },
27
+ "dependencies": {
28
+ "@inquirer/select": "^4.4.2",
29
+ "commander": "^13.0.0",
30
+ "open": "^10.1.0",
31
+ "ora": "^8.2.0",
32
+ "picocolors": "^1.1.0"
33
+ },
34
+ "peerDependencies": {
35
+ "@vitejs/plugin-react": "^4.0.0",
36
+ "typescript": "^5.0.0",
37
+ "vite": "^5.0.0 || ^6.0.0"
38
+ },
39
+ "devDependencies": {
40
+ "@types/node": "^22.0.0",
41
+ "@vitejs/plugin-react": "^4.0.0",
42
+ "tsup": "^8.0.0",
43
+ "typescript": "^5.4.0",
44
+ "vite": "^6.0.0"
45
+ },
46
+ "license": "MIT"
47
+ }