appfunnel 0.12.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -231,49 +231,21 @@ async function fetchStorePrices(projectId, storeId, options) {
231
231
  const data = await response.json();
232
232
  return data.data || [];
233
233
  }
234
- async function publishBuild(projectId, funnelId, manifest, assets, options, promote) {
235
- const formData = new FormData();
236
- formData.set("manifest", JSON.stringify(manifest));
237
- if (funnelId) {
238
- formData.set("funnelId", funnelId);
239
- }
240
- if (promote) {
241
- formData.set("promote", "true");
242
- }
243
- for (const asset of assets) {
244
- formData.append(
245
- "assets",
246
- new Blob([new Uint8Array(asset.content)], { type: asset.contentType }),
247
- asset.path
248
- );
249
- }
250
- try {
251
- const response = await apiFetch(`/project/${projectId}/headless/publish`, {
252
- ...options,
253
- method: "POST",
254
- body: formData
255
- });
256
- return await response.json();
257
- } catch (err) {
258
- if (err instanceof CLIError && err.code === "API_ERROR") {
259
- if (err.statusCode === 413) {
260
- throw new CLIError(
261
- "BUNDLE_TOO_LARGE",
262
- err.message,
263
- "Reduce page bundle sizes. Check for large dependencies."
264
- );
265
- }
266
- if (err.statusCode === 409) {
267
- throw new CLIError(
268
- "FUNNEL_NOT_HEADLESS",
269
- err.message,
270
- "Remove funnelId from config to create a new headless funnel."
271
- );
272
- }
273
- throw new CLIError("PUBLISH_FAILED", err.message);
274
- }
275
- throw err;
276
- }
234
+ async function publishInit(projectId, body, options) {
235
+ const response = await apiFetch(`/project/${projectId}/headless/publish-init`, {
236
+ ...options,
237
+ method: "POST",
238
+ body: JSON.stringify(body)
239
+ });
240
+ return await response.json();
241
+ }
242
+ async function publishFinalize(projectId, body, options) {
243
+ const response = await apiFetch(`/project/${projectId}/headless/publish-finalize`, {
244
+ ...options,
245
+ method: "POST",
246
+ body: JSON.stringify(body)
247
+ });
248
+ return await response.json();
277
249
  }
278
250
  var DEFAULT_API_BASE2;
279
251
  var init_api = __esm({
@@ -455,6 +427,12 @@ async function initCommand(nameArg) {
455
427
  const { renameSync } = await import("fs");
456
428
  renameSync(gitignoreSrc, join2(dir, ".gitignore"));
457
429
  }
430
+ const readmePath = join2(dir, "README.md");
431
+ if (existsSync(readmePath)) {
432
+ let readme = readFileSync2(readmePath, "utf-8");
433
+ readme = readme.replace("__NAME__", name);
434
+ writeFileSync2(readmePath, readme);
435
+ }
458
436
  const configPath = join2(dir, "appfunnel.config.ts");
459
437
  if (existsSync(configPath)) {
460
438
  let config = readFileSync2(configPath, "utf-8");
@@ -485,7 +463,7 @@ ${itemsStr},
485
463
  }
486
464
  writeFileSync2(configPath, config);
487
465
  }
488
- const sdkVersion = `^${"0.12.0"}`;
466
+ const sdkVersion = `^${"0.13.0"}`;
489
467
  writeFileSync2(
490
468
  join2(dir, "package.json"),
491
469
  JSON.stringify(
@@ -731,7 +709,7 @@ var init_config = __esm({
731
709
  import { readFileSync as readFileSync4 } from "fs";
732
710
  import { join as join4 } from "path";
733
711
  function checkVersionCompatibility(cwd) {
734
- const cliVersion = "0.12.0";
712
+ const cliVersion = "0.13.0";
735
713
  const sdkVersion = getSdkVersion(cwd);
736
714
  const [cliMajor, cliMinor] = cliVersion.split(".").map(Number);
737
715
  const [sdkMajor, sdkMinor] = sdkVersion.split(".").map(Number);
@@ -1950,6 +1928,7 @@ async function buildCommand() {
1950
1928
  responses: config.responses || {},
1951
1929
  queryParams: { ...BUILTIN_QUERY_PARAMS, ...config.queryParams },
1952
1930
  data: config.data || {},
1931
+ user: BUILTIN_USER_VARS,
1953
1932
  products: config.products || {},
1954
1933
  defaultLocale: config.defaultLocale,
1955
1934
  assets,
@@ -1972,7 +1951,7 @@ async function buildCommand() {
1972
1951
  if (totalSize > MAX_TOTAL_SIZE) {
1973
1952
  console.log(formatWarning(
1974
1953
  "BUNDLE_TOO_LARGE",
1975
- `Total bundle size (${formatSize(totalSize)}) exceeds the 2MB limit.`,
1954
+ `Total bundle size (${formatSize(totalSize)}) exceeds the 10MB limit.`,
1976
1955
  "This will be rejected on publish. Reduce dependencies or code-split."
1977
1956
  ));
1978
1957
  console.log();
@@ -2081,7 +2060,7 @@ function getSdkVersion2(cwd) {
2081
2060
  return "0.0.0";
2082
2061
  }
2083
2062
  }
2084
- var MAX_TOTAL_SIZE, MAX_PAGE_SIZE, BUILTIN_QUERY_PARAMS;
2063
+ var MAX_TOTAL_SIZE, MAX_PAGE_SIZE, BUILTIN_QUERY_PARAMS, BUILTIN_USER_VARS;
2085
2064
  var init_build = __esm({
2086
2065
  "src/commands/build.ts"() {
2087
2066
  "use strict";
@@ -2094,7 +2073,7 @@ var init_build = __esm({
2094
2073
  init_html();
2095
2074
  init_errors();
2096
2075
  init_errors();
2097
- MAX_TOTAL_SIZE = 2 * 1024 * 1024;
2076
+ MAX_TOTAL_SIZE = 10 * 1024 * 1024;
2098
2077
  MAX_PAGE_SIZE = 500 * 1024;
2099
2078
  BUILTIN_QUERY_PARAMS = {
2100
2079
  utm_source: { type: "string" },
@@ -2103,6 +2082,14 @@ var init_build = __esm({
2103
2082
  utm_content: { type: "string" },
2104
2083
  utm_term: { type: "string" }
2105
2084
  };
2085
+ BUILTIN_USER_VARS = {
2086
+ email: { type: "string" },
2087
+ name: { type: "string" },
2088
+ dateOfBirth: { type: "string" },
2089
+ gender: { type: "string" },
2090
+ stripeCustomerId: { type: "string" },
2091
+ paddleCustomerId: { type: "string" }
2092
+ };
2106
2093
  }
2107
2094
  });
2108
2095
 
@@ -2135,10 +2122,22 @@ __export(publish_exports, {
2135
2122
  });
2136
2123
  import { resolve as resolve4, join as join11 } from "path";
2137
2124
  import { readFileSync as readFileSync10, existsSync as existsSync6 } from "fs";
2125
+ import { execSync } from "child_process";
2138
2126
  import pc9 from "picocolors";
2139
- function getMimeType(path) {
2140
- const ext = path.substring(path.lastIndexOf("."));
2141
- return MIME_TYPES[ext] || "application/octet-stream";
2127
+ import select3 from "@inquirer/select";
2128
+ function tryGit(command, cwd) {
2129
+ try {
2130
+ return execSync(command, { cwd, stdio: ["pipe", "pipe", "ignore"] }).toString().trim() || void 0;
2131
+ } catch {
2132
+ return void 0;
2133
+ }
2134
+ }
2135
+ function getGitMetadata(cwd) {
2136
+ const commitSha = tryGit("git rev-parse HEAD", cwd);
2137
+ if (!commitSha) return {};
2138
+ const branch = tryGit("git rev-parse --abbrev-ref HEAD", cwd);
2139
+ const message = tryGit("git log -1 --pretty=%s", cwd);
2140
+ return { commitSha, branch, message };
2142
2141
  }
2143
2142
  function formatSize2(bytes) {
2144
2143
  if (bytes < 1024) return `${bytes}B`;
@@ -2170,7 +2169,7 @@ async function publishCommand(options) {
2170
2169
  const manifest = JSON.parse(readFileSync10(manifestPath, "utf-8"));
2171
2170
  const assets = manifest.assets || [];
2172
2171
  const s = spinner("Preparing assets...");
2173
- const assetPayloads = [];
2172
+ const assetBuffers = [];
2174
2173
  let totalBytes = 0;
2175
2174
  for (let i = 0; i < assets.length; i++) {
2176
2175
  const asset = assets[i];
@@ -2183,45 +2182,133 @@ async function publishCommand(options) {
2183
2182
  "Run 'appfunnel build' to regenerate."
2184
2183
  );
2185
2184
  }
2186
- const content = readFileSync10(fullPath);
2187
- totalBytes += content.length;
2188
- assetPayloads.push({
2189
- path: asset.path,
2190
- content,
2191
- contentType: getMimeType(asset.path)
2192
- });
2185
+ const body = readFileSync10(fullPath);
2186
+ totalBytes += body.length;
2187
+ assetBuffers.push({ path: asset.path, body, size: body.length });
2193
2188
  s.text = `Preparing assets... ${i + 1}/${assets.length} ${pc9.dim(`(${formatSize2(totalBytes)})`)}`;
2194
2189
  }
2195
- s.text = `Uploading ${assets.length} assets ${pc9.dim(`(${formatSize2(totalBytes)})`)}`;
2196
- const result = await publishBuild(
2190
+ s.stop();
2191
+ const MAX_TOTAL_SIZE2 = 10 * 1024 * 1024;
2192
+ if (totalBytes > MAX_TOTAL_SIZE2) {
2193
+ throw new CLIError(
2194
+ "BUNDLE_TOO_LARGE",
2195
+ `Total bundle size (${formatSize2(totalBytes)}) exceeds the ${formatSize2(MAX_TOTAL_SIZE2)} limit.`,
2196
+ "Reduce page bundle sizes. Check for large dependencies."
2197
+ );
2198
+ }
2199
+ info(`${assets.length} assets prepared ${pc9.dim(`(${formatSize2(totalBytes)})`)}`);
2200
+ let promote = options?.promote ?? false;
2201
+ if (!promote) {
2202
+ promote = await select3({
2203
+ message: "Publish as the new active version?",
2204
+ choices: [
2205
+ { name: "Yes", value: true },
2206
+ { name: "No", value: false }
2207
+ ]
2208
+ });
2209
+ }
2210
+ const git = getGitMetadata(cwd);
2211
+ const initSpinner = spinner("Initializing publish...");
2212
+ let initResult;
2213
+ try {
2214
+ initResult = await publishInit(
2215
+ projectId,
2216
+ {
2217
+ manifest,
2218
+ funnelId: config.funnelId || void 0,
2219
+ promote,
2220
+ commitSha: git.commitSha,
2221
+ branch: git.branch,
2222
+ message: git.message,
2223
+ triggeredBy: creds.email || void 0
2224
+ },
2225
+ { token: creds.token }
2226
+ );
2227
+ } catch (err) {
2228
+ initSpinner.stop();
2229
+ if (err instanceof CLIError && err.code === "API_ERROR") {
2230
+ if (err.statusCode === 413) {
2231
+ throw new CLIError(
2232
+ "BUNDLE_TOO_LARGE",
2233
+ err.message,
2234
+ "Reduce page bundle sizes. Check for large dependencies."
2235
+ );
2236
+ }
2237
+ if (err.statusCode === 409) {
2238
+ throw new CLIError(
2239
+ "FUNNEL_NOT_HEADLESS",
2240
+ err.message,
2241
+ "Remove funnelId from config to create a new headless funnel."
2242
+ );
2243
+ }
2244
+ throw new CLIError("PUBLISH_FAILED", err.message);
2245
+ }
2246
+ throw err;
2247
+ }
2248
+ initSpinner.stop();
2249
+ info(`Build ${pc9.dim(initResult.buildId)} initialized`);
2250
+ const uploadSpinner = spinner(`Uploading 0/${assets.length} assets...`);
2251
+ const uploadInfoMap = new Map(initResult.assets.map((a) => [a.path, a]));
2252
+ let uploaded = 0;
2253
+ let uploadedBytes = 0;
2254
+ const uploadAsset = async (asset) => {
2255
+ const info2 = uploadInfoMap.get(asset.path);
2256
+ if (!info2) {
2257
+ throw new CLIError("PUBLISH_FAILED", `No upload URL for asset: ${asset.path}`);
2258
+ }
2259
+ const response = await fetch(info2.uploadUrl, {
2260
+ method: "PUT",
2261
+ body: new Uint8Array(asset.body),
2262
+ headers: {
2263
+ "Content-Type": info2.contentType
2264
+ }
2265
+ });
2266
+ if (!response.ok) {
2267
+ throw new CLIError(
2268
+ "PUBLISH_FAILED",
2269
+ `Failed to upload ${asset.path}: ${response.status} ${response.statusText}`
2270
+ );
2271
+ }
2272
+ uploaded++;
2273
+ uploadedBytes += asset.size;
2274
+ uploadSpinner.text = `Uploading ${uploaded}/${assets.length} assets... ${pc9.dim(`(${formatSize2(uploadedBytes)}/${formatSize2(totalBytes)})`)}`;
2275
+ };
2276
+ const queue = [...assetBuffers];
2277
+ const workers = [];
2278
+ for (let i = 0; i < Math.min(UPLOAD_CONCURRENCY, queue.length); i++) {
2279
+ workers.push((async () => {
2280
+ while (queue.length > 0) {
2281
+ const asset = queue.shift();
2282
+ await uploadAsset(asset);
2283
+ }
2284
+ })());
2285
+ }
2286
+ await Promise.all(workers);
2287
+ uploadSpinner.stop();
2288
+ info(`${assets.length} assets uploaded ${pc9.dim(`(${formatSize2(totalBytes)})`)}`);
2289
+ const finalizeSpinner = spinner("Finalizing publish...");
2290
+ const result = await publishFinalize(
2197
2291
  projectId,
2198
- config.funnelId || "",
2199
- manifest,
2200
- assetPayloads,
2201
- { token: creds.token },
2202
- options?.promote
2292
+ { buildId: initResult.buildId, funnelId: initResult.funnelId },
2293
+ { token: creds.token }
2203
2294
  );
2204
- s.stop();
2205
- if (result.created && result.funnelId) {
2206
- patchConfigFunnelId(cwd, result.funnelId);
2295
+ finalizeSpinner.stop();
2296
+ if (initResult.created && initResult.funnelId) {
2297
+ patchConfigFunnelId(cwd, initResult.funnelId);
2207
2298
  info(`Funnel created \u2014 funnelId added to appfunnel.config.ts`);
2208
2299
  }
2209
2300
  console.log();
2210
2301
  success(result.activated ? "Published and activated" : "Published successfully");
2211
2302
  console.log();
2212
2303
  console.log(` ${pc9.dim("Build ID:")} ${result.buildId}`);
2213
- if (result.funnelId && !config.funnelId) {
2214
- console.log(` ${pc9.dim("Funnel:")} ${result.funnelId}`);
2304
+ if (initResult.funnelId && !config.funnelId) {
2305
+ console.log(` ${pc9.dim("Funnel:")} ${initResult.funnelId}`);
2215
2306
  }
2216
2307
  console.log(` ${pc9.dim("Dashboard:")} ${pc9.cyan(result.dashboardUrl)}`);
2217
2308
  console.log(` ${pc9.dim("Assets:")} ${assets.length} files ${pc9.dim(`(${formatSize2(totalBytes)})`)}`);
2218
- if (!result.activated) {
2219
- console.log();
2220
- console.log(` ${pc9.dim("Tip:")} Use ${pc9.cyan("--promote")} to activate immediately, or promote from the dashboard.`);
2221
- }
2222
2309
  console.log();
2223
2310
  }
2224
- var MIME_TYPES;
2311
+ var UPLOAD_CONCURRENCY;
2225
2312
  var init_publish = __esm({
2226
2313
  "src/commands/publish.ts"() {
2227
2314
  "use strict";
@@ -2232,17 +2319,7 @@ var init_publish = __esm({
2232
2319
  init_api();
2233
2320
  init_errors();
2234
2321
  init_config_patch();
2235
- MIME_TYPES = {
2236
- ".js": "application/javascript",
2237
- ".css": "text/css",
2238
- ".html": "text/html",
2239
- ".json": "application/json",
2240
- ".svg": "image/svg+xml",
2241
- ".png": "image/png",
2242
- ".jpg": "image/jpeg",
2243
- ".woff2": "font/woff2",
2244
- ".woff": "font/woff"
2245
- };
2322
+ UPLOAD_CONCURRENCY = 10;
2246
2323
  }
2247
2324
  });
2248
2325
 
@@ -2251,7 +2328,7 @@ init_errors();
2251
2328
  import { Command } from "commander";
2252
2329
  import pc10 from "picocolors";
2253
2330
  var program = new Command();
2254
- program.name("appfunnel").description("Build and publish headless AppFunnel projects").version("0.12.0");
2331
+ program.name("appfunnel").description("Build and publish headless AppFunnel projects").version("0.13.0");
2255
2332
  program.command("init").argument("[name]", "Project directory name").description("Create a new AppFunnel project").action(async (name) => {
2256
2333
  const { initCommand: initCommand2 } = await Promise.resolve().then(() => (init_init(), init_exports));
2257
2334
  await initCommand2(name);
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/lib/errors.ts","../src/lib/logger.ts","../src/lib/auth.ts","../src/lib/projects.ts","../src/lib/api.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/lib/computeFlow.ts","../src/commands/routes.ts","../src/commands/build.ts","../src/lib/config-patch.ts","../src/commands/publish.ts","../src/index.ts"],"sourcesContent":["import pc from 'picocolors'\n\nexport type ErrorCode =\n\t| 'AUTH_REQUIRED'\n\t| 'AUTH_EXPIRED'\n\t| 'CONFIG_NOT_FOUND'\n\t| 'INVALID_ROUTE'\n\t| 'UNDEFINED_VARIABLE'\n\t| 'VERSION_MISMATCH'\n\t| 'BUILD_NOT_FOUND'\n\t| 'FUNNEL_NOT_HEADLESS'\n\t| 'BUNDLE_TOO_LARGE'\n\t| 'PAGE_SIZE'\n\t| 'INVALID_PAGE'\n\t| 'NO_PAGES'\n\t| 'NO_PROJECTS'\n\t| 'MISSING_PROJECT_ID'\n\t| 'MISSING_INITIAL_PAGE_KEY'\n\t| 'API_ERROR'\n\t| 'PUBLISH_FAILED'\n\nexport class CLIError extends Error {\n\tcode: ErrorCode\n\thint?: string\n\tstatusCode?: number\n\n\tconstructor(code: ErrorCode, message: string, hint?: string) {\n\t\tsuper(message)\n\t\tthis.name = 'CLIError'\n\t\tthis.code = code\n\t\tthis.hint = hint\n\t}\n}\n\nexport function formatError(err: CLIError): string {\n\tconst lines = [\n\t\t`${pc.red('ERROR')} ${pc.dim(`[${err.code}]`)}: ${err.message}`,\n\t]\n\tif (err.hint) {\n\t\tlines.push(` ${pc.dim('Hint:')} ${err.hint}`)\n\t}\n\treturn lines.join('\\n')\n}\n\nexport function formatWarning(\n\tcode: string,\n\tmessage: string,\n\thint?: string\n): string {\n\tconst lines = [`${pc.yellow('WARNING')} ${pc.dim(`[${code}]`)}: ${message}`]\n\tif (hint) {\n\t\tlines.push(` ${pc.dim('Hint:')} ${hint}`)\n\t}\n\treturn lines.join('\\n')\n}\n","import 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' + __CLI_VERSION__)}`)\n console.log()\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 pc from 'picocolors'\nimport select from '@inquirer/select'\nimport * as log from './logger.js'\nimport { CLIError } from './errors.js'\n\nconst DEFAULT_API_BASE = 'https://api.appfunnel.net'\n\nexport interface Project {\n id: string\n name: string\n role: string\n}\n\nexport async function fetchProjects(token: string): Promise<Project[]> {\n const response = await fetch(`${DEFAULT_API_BASE}/user/projects`, {\n headers: {\n Authorization: token,\n 'Content-Type': 'application/json',\n },\n })\n\n if (!response.ok) {\n throw new CLIError('API_ERROR', 'Failed to fetch projects.')\n }\n\n const body = (await response.json()) as { data: Project[] }\n return body.data\n}\n\n/**\n * Prompt the user to select a project from their available projects.\n * Returns the selected project ID.\n */\nexport async function promptForProject(token: string): Promise<string> {\n const spin = log.spinner('Fetching projects...')\n let projects: Project[]\n try {\n projects = await fetchProjects(token)\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 spin.stop()\n\n if (projects.length === 0) {\n throw new CLIError(\n 'NO_PROJECTS',\n 'No projects found.',\n 'Create a project at https://appfunnel.net first.',\n )\n }\n\n return select({\n message: 'Select a project',\n choices: projects.map((p) => ({\n name: `${p.name} ${pc.dim(`(${p.id})`)}`,\n value: p.id,\n })),\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/* ---------- Store & price listing (for template init) ---------- */\n\nexport interface Store {\n\tid: string\n\tname: string\n\ttype: 'STRIPE' | 'PADDLE'\n\tisTestMode: boolean\n}\n\nexport interface StorePrice {\n\tid: string\n\tstoreId: string\n\tname: string | null\n\tpriceName: string | null\n\tdisplayName: string | null\n\tamount: number\n\tcurrency: string\n\tinterval: string | null\n\tintervalCount: number\n}\n\n/**\n * Fetch all stores for a project.\n */\nexport async function fetchStores(\n\tprojectId: string,\n\toptions: ApiOptions\n): Promise<Store[]> {\n\tconst response = await apiFetch(`/project/${projectId}/stores`, {\n\t\t...options,\n\t\tmethod: 'GET',\n\t})\n\tconst data = (await response.json()) as { data: Store[] }\n\treturn data.data || []\n}\n\n/**\n * Fetch all prices for a store.\n */\nexport async function fetchStorePrices(\n\tprojectId: string,\n\tstoreId: string,\n\toptions: ApiOptions\n): Promise<StorePrice[]> {\n\tconst response = await apiFetch(\n\t\t`/project/${projectId}/stores/${storeId}/prices`,\n\t\t{ ...options, method: 'GET' }\n\t)\n\tconst data = (await response.json()) as { data: StorePrice[] }\n\treturn data.data || []\n}\n\n/**\n * Fetch all prices across all stores for a project.\n * Returns prices annotated with store info for display.\n */\nexport async function fetchAllProjectPrices(\n\tprojectId: string,\n\toptions: ApiOptions\n): Promise<Array<StorePrice & { store: Store }>> {\n\tconst stores = await fetchStores(projectId, options)\n\tconst all: Array<StorePrice & { store: Store }> = []\n\n\tfor (const store of stores) {\n\t\tconst prices = await fetchStorePrices(projectId, store.id, options)\n\t\tfor (const price of prices) {\n\t\t\tall.push({ ...price, store })\n\t\t}\n\t}\n\n\treturn all\n}\n\n/**\n * Publish a headless build.\n * If funnelId is empty, the server auto-creates a new headless funnel + campaign.\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\tpromote?: boolean\n): Promise<{ buildId: string; funnelId: string; created: boolean; activated: boolean; dashboardUrl: string; message: string }> {\n\tconst formData = new FormData()\n\tformData.set('manifest', JSON.stringify(manifest))\n\tif (funnelId) {\n\t\tformData.set('funnelId', funnelId)\n\t}\n\tif (promote) {\n\t\tformData.set('promote', 'true')\n\t}\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; funnelId: string; created: boolean; activated: boolean; dashboardUrl: string; message: 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'Remove funnelId from config to create a new headless funnel.'\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 { cpSync, existsSync, readFileSync, writeFileSync, readdirSync } from 'node:fs'\nimport { join, dirname } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport pc from 'picocolors'\nimport select from '@inquirer/select'\nimport input from '@inquirer/input'\nimport * as log from '../lib/logger.js'\nimport { requireAuth } from '../lib/auth.js'\nimport { fetchProjects, promptForProject } from '../lib/projects.js'\nimport { fetchStores, fetchStorePrices, type Store, type StorePrice } from '../lib/api.js'\n\nconst __dirname = dirname(fileURLToPath(import.meta.url))\n\n/* ---------- Template types ---------- */\n\ninterface TemplateProduct {\n\tid: string\n\tlabel: string\n\tdescription?: string\n\tpaidTrial?: boolean\n}\n\ninterface TemplateConfig {\n\tname: string\n\tdescription: string\n\tproducts: TemplateProduct[]\n}\n\n/* ---------- Helpers ---------- */\n\nfunction getTemplatesDir(): string {\n\tconst dir = join(__dirname, '..', 'templates')\n\tif (!existsSync(dir)) {\n\t\tthrow new Error(`Templates directory not found at ${dir}`)\n\t}\n\treturn dir\n}\n\nfunction listTemplates(): Array<{ dir: string; config: TemplateConfig }> {\n\tconst root = getTemplatesDir()\n\treturn readdirSync(root, { withFileTypes: true })\n\t\t.filter((d) => d.isDirectory())\n\t\t.map((d) => {\n\t\t\tconst configPath = join(root, d.name, 'template.json')\n\t\t\tconst config: TemplateConfig = existsSync(configPath)\n\t\t\t\t? JSON.parse(readFileSync(configPath, 'utf-8'))\n\t\t\t\t: { name: d.name, description: '', products: [] }\n\t\t\treturn { dir: d.name, config }\n\t\t})\n}\n\nconst INTERVAL_LABELS: Record<string, Record<number, string>> = {\n\tday: { 1: 'day', 7: 'week', 14: '2 weeks', 30: 'month' },\n\tweek: { 1: 'week', 2: '2 weeks', 4: 'month', 12: 'quarter', 52: 'year' },\n\tmonth: { 1: 'month', 3: 'quarter', 6: '6 months', 12: 'year' },\n\tyear: { 1: 'year' },\n}\n\nfunction formatInterval(interval: string | null, count: number): string {\n\tif (!interval) return ' one-time'\n\tconst label = INTERVAL_LABELS[interval]?.[count]\n\tif (label) return `/${label}`\n\treturn `/${count} ${interval}${count > 1 ? 's' : ''}`\n}\n\nfunction formatPrice(price: StorePrice): string {\n\tconst amount = (price.amount / 100).toFixed(2)\n\tconst currency = price.currency.toUpperCase()\n\tconst interval = formatInterval(price.interval, price.intervalCount)\n\n\tconst productName = price.name || 'Unnamed product'\n\tconst priceName = price.priceName ? ` — ${price.priceName}` : ''\n\n\treturn `${currency} ${amount}${interval} ${pc.dim(`${productName}${priceName}`)} ${pc.dim(`(${price.id})`)}`\n}\n\nfunction formatStoreName(store: Store): string {\n\tconst test = store.isTestMode ? pc.yellow(' (test)') : ''\n\treturn `${store.name || store.type}${test}`\n}\n\n/* ---------- Main ---------- */\n\nexport async function initCommand(nameArg?: string): Promise<void> {\n\tconst creds = requireAuth()\n\tconst apiOpts = { token: creds.token }\n\n\t// 1. Use provided name or ask interactively\n\tconst name = nameArg?.trim() || await input({\n\t\tmessage: 'Funnel name',\n\t\tvalidate: (value) => {\n\t\t\tif (!value.trim()) return 'Name is required'\n\t\t\tif (!/^[a-z0-9-]+$/.test(value.trim()))\n\t\t\t\treturn 'Use lowercase letters, numbers, and hyphens only'\n\t\t\tif (existsSync(join(process.cwd(), value.trim())))\n\t\t\t\treturn `Directory '${value.trim()}' already exists`\n\t\t\treturn true\n\t\t},\n\t})\n\n\t// Validate name if provided as argument\n\tif (nameArg) {\n\t\tif (!/^[a-z0-9-]+$/.test(name))\n\t\t\tthrow new Error('Name must be lowercase letters, numbers, and hyphens only')\n\t\tif (existsSync(join(process.cwd(), name)))\n\t\t\tthrow new Error(`Directory '${name}' already exists`)\n\t}\n\n\tconst dir = join(process.cwd(), name.trim())\n\n\t// 2. Select project\n\tconst projectId = await promptForProject(creds.token)\n\tconst projects = await fetchProjects(creds.token)\n\tconst project = projects.find((p) => p.id === projectId)!\n\n\t// 3. Select template\n\tconst templates = listTemplates()\n\tconst selectedDir = await select({\n\t\tmessage: 'Choose a template',\n\t\tchoices: templates.map((t) => ({\n\t\t\tname: t.config.description\n\t\t\t\t? `${t.config.name} — ${pc.dim(t.config.description)}`\n\t\t\t\t: t.config.name,\n\t\t\tvalue: t.dir,\n\t\t})),\n\t})\n\tconst chosen = templates.find((t) => t.dir === selectedDir)!\n\n\tconst templateConfig = chosen.config\n\tconst templateDir = join(getTemplatesDir(), chosen.dir)\n\n\t// 3. If template has products, select store + bind prices\n\tconst productBindings: Array<{\n\t\tid: string\n\t\tname: string\n\t\tstorePriceId: string\n\t\ttrialDays?: number\n\t\ttrialStorePriceId?: string\n\t}> = []\n\n\tif (templateConfig.products.length > 0) {\n\t\t// Fetch stores\n\t\tconst storesSpinner = log.spinner('Fetching stores...')\n\t\tconst stores = await fetchStores(projectId, apiOpts)\n\t\tstoresSpinner.stop()\n\n\t\tif (stores.length === 0) {\n\t\t\tlog.warn('No stores found for this project.')\n\t\t\tlog.info('Products will use empty IDs — create a store in the dashboard and update appfunnel.config.ts.')\n\t\t} else {\n\t\t\t// Select store\n\t\t\tlet store: Store\n\t\t\tif (stores.length === 1) {\n\t\t\t\tstore = stores[0]\n\t\t\t\tlog.info(`Using store: ${formatStoreName(store)}`)\n\t\t\t} else {\n\t\t\t\tconst storeId = await select({\n\t\t\t\t\tmessage: 'Choose a store',\n\t\t\t\t\tchoices: stores.map((s) => ({\n\t\t\t\t\t\tname: formatStoreName(s),\n\t\t\t\t\t\tvalue: s.id,\n\t\t\t\t\t})),\n\t\t\t\t})\n\t\t\t\tstore = stores.find((s) => s.id === storeId)!\n\t\t\t}\n\n\t\t\t// Fetch prices from selected store\n\t\t\tconst pricesSpinner = log.spinner('Fetching prices...')\n\t\t\tconst prices = await fetchStorePrices(projectId, store.id, apiOpts)\n\t\t\tpricesSpinner.stop()\n\n\t\t\tif (prices.length === 0) {\n\t\t\t\tlog.warn('No prices found in this store.')\n\t\t\t\tlog.info('Import prices in the dashboard, then update appfunnel.config.ts.')\n\t\t\t} else {\n\t\t\t\tconsole.log()\n\t\t\t\tlog.info(`Configuring ${templateConfig.products.length} product(s)...`)\n\t\t\t\tconsole.log()\n\n\t\t\t\tfor (const product of templateConfig.products) {\n\t\t\t\t\tif (product.description) {\n\t\t\t\t\t\tconsole.log(` ${pc.dim(product.description)}`)\n\t\t\t\t\t}\n\n\t\t\t\t\t// Select main price\n\t\t\t\t\tconst storePriceId = await select({\n\t\t\t\t\t\tmessage: `${product.label} — select a price`,\n\t\t\t\t\t\tchoices: prices.map((p) => ({\n\t\t\t\t\t\t\tname: formatPrice(p),\n\t\t\t\t\t\t\tvalue: p.id,\n\t\t\t\t\t\t})),\n\t\t\t\t\t})\n\n\t\t\t\t\tconst binding: (typeof productBindings)[number] = {\n\t\t\t\t\t\tid: product.id,\n\t\t\t\t\t\tname: product.id,\n\t\t\t\t\t\tstorePriceId,\n\t\t\t\t\t}\n\n\t\t\t\t\t// Select trial price if needed\n\t\t\t\t\tif (product.paidTrial) {\n\t\t\t\t\t\tconst trialStorePriceId = await select({\n\t\t\t\t\t\t\tmessage: `${product.label} — select a trial price`,\n\t\t\t\t\t\t\tchoices: [\n\t\t\t\t\t\t\t\t...prices.map((p) => ({\n\t\t\t\t\t\t\t\t\tname: formatPrice(p),\n\t\t\t\t\t\t\t\t\tvalue: p.id,\n\t\t\t\t\t\t\t\t})),\n\t\t\t\t\t\t\t\t{ name: pc.dim('Skip (no trial)'), value: '' },\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t})\n\n\t\t\t\t\t\tif (trialStorePriceId) {\n\t\t\t\t\t\t\tbinding.trialStorePriceId = trialStorePriceId\n\n\t\t\t\t\t\t\tconst trialDaysStr = await select({\n\t\t\t\t\t\t\t\tmessage: `${product.label} — trial duration`,\n\t\t\t\t\t\t\t\tchoices: [\n\t\t\t\t\t\t\t\t\t{ name: '3 days', value: '3' },\n\t\t\t\t\t\t\t\t\t{ name: '7 days', value: '7' },\n\t\t\t\t\t\t\t\t\t{ name: '14 days', value: '14' },\n\t\t\t\t\t\t\t\t\t{ name: '30 days', value: '30' },\n\t\t\t\t\t\t\t\t\t{ name: '90 days', value: '90' },\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\tbinding.trialDays = parseInt(trialDaysStr, 10)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tproductBindings.push(binding)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// 4. Copy template\n\tconst s = log.spinner(`Creating ${name}...`)\n\n\tcpSync(templateDir, dir, { recursive: true })\n\n\t// Remove template.json from output — it's not part of the user's project\n\tconst templateJsonPath = join(dir, 'template.json')\n\tif (existsSync(templateJsonPath)) {\n\t\tconst { unlinkSync } = await import('node:fs')\n\t\tunlinkSync(templateJsonPath)\n\t}\n\n\t// npm strips .gitignore from published packages, so we ship it as 'gitignore' and rename here\n\tconst gitignoreSrc = join(dir, 'gitignore')\n\tif (existsSync(gitignoreSrc)) {\n\t\tconst { renameSync } = await import('node:fs')\n\t\trenameSync(gitignoreSrc, join(dir, '.gitignore'))\n\t}\n\n\t// 5. Replace placeholders in appfunnel.config.ts\n\tconst configPath = join(dir, 'appfunnel.config.ts')\n\tif (existsSync(configPath)) {\n\t\tlet config = readFileSync(configPath, 'utf-8')\n\t\tconfig = config.replace('__PROJECT_ID__', projectId)\n\t\tconfig = config.replace('__NAME__', name)\n\n\t\t// If we have product bindings, replace the products section\n\t\tif (productBindings.length > 0) {\n\t\t\tconst itemsStr = productBindings\n\t\t\t\t.map((b) => {\n\t\t\t\t\tconst fields = [\n\t\t\t\t\t\t`\\t\\t\\t{ id: '${b.id}'`,\n\t\t\t\t\t\t`name: '${b.name}'`,\n\t\t\t\t\t\t`storePriceId: '${b.storePriceId}'`,\n\t\t\t\t\t]\n\t\t\t\t\tif (b.trialDays) fields.push(`trialDays: ${b.trialDays}`)\n\t\t\t\t\tif (b.trialStorePriceId)\n\t\t\t\t\t\tfields.push(`trialStorePriceId: '${b.trialStorePriceId}'`)\n\t\t\t\t\treturn fields.join(', ') + ' }'\n\t\t\t\t})\n\t\t\t\t.join(',\\n')\n\n\t\t\tconst defaultId = productBindings[0].id\n\t\t\tconfig = config.replace(\n\t\t\t\t/products:\\s*\\{[^}]*items:\\s*\\[[^\\]]*\\][^}]*\\}/s,\n\t\t\t\t`products: {\\n\\t\\titems: [\\n${itemsStr},\\n\\t\\t],\\n\\t\\tdefaultId: '${defaultId}',\\n\\t}`\n\t\t\t)\n\t\t}\n\n\t\twriteFileSync(configPath, config)\n\t}\n\n\t// 6. Generate package.json\n\tconst sdkVersion = `^${__CLI_VERSION__}`\n\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-dev/sdk': sdkVersion,\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: sdkVersion,\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\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 VariableConfig {\n type: string\n default?: unknown\n persist?: boolean\n}\n\nexport interface AppFunnelConfig {\n projectId: string\n name: string\n funnelId?: string\n initialPageKey: string\n defaultLocale?: string\n /** Response variables — stored as answers.* */\n responses?: Record<string, VariableConfig>\n /** Query param variables — stored as query.* */\n queryParams?: Record<string, VariableConfig>\n /** Data variables — stored as data.* */\n data?: Record<string, VariableConfig>\n products?: {\n items: Array<{ id: string; name: string; storePriceId: string; trialDays?: number; trialStorePriceId?: string }>\n defaultId?: string\n }\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-dev\\/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) {\n throw new CLIError(\n 'CONFIG_NOT_FOUND',\n `Invalid config in ${CONFIG_FILE}.`,\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\tversion: string\n}\n\n/**\n * Check that CLI major.minor matches the installed @appfunnel-dev/sdk version.\n */\nexport function checkVersionCompatibility(cwd: string): void {\n\tconst cliVersion = __CLI_VERSION__\n\tconst sdkVersion = getSdkVersion(cwd)\n\n\tconst [cliMajor, cliMinor] = cliVersion.split('.').map(Number)\n\tconst [sdkMajor, sdkMinor] = sdkVersion.split('.').map(Number)\n\n\tif (cliMajor !== sdkMajor || cliMinor !== sdkMinor) {\n\t\tthrow new CLIError(\n\t\t\t'VERSION_MISMATCH',\n\t\t\t`CLI version ${cliVersion} requires @appfunnel-dev/sdk ^${cliMajor}.${cliMinor}.0, but found ${sdkVersion}.`,\n\t\t\t\"Run 'npm install @appfunnel-dev/sdk@latest' to update.\"\n\t\t)\n\t}\n}\n\nfunction getSdkVersion(cwd: string): string {\n\ttry {\n\t\tconst pkgPath = join(\n\t\t\tcwd,\n\t\t\t'node_modules',\n\t\t\t'@appfunnel-dev',\n\t\t\t'sdk',\n\t\t\t'package.json'\n\t\t)\n\t\tconst pkg = JSON.parse(readFileSync(pkgPath, 'utf-8')) as PackageJson\n\t\treturn pkg.version\n\t} catch {\n\t\tthrow new CLIError(\n\t\t\t'VERSION_MISMATCH',\n\t\t\t'@appfunnel-dev/sdk is not installed.',\n\t\t\t\"Run 'npm install @appfunnel-dev/sdk' to install it.\"\n\t\t)\n\t}\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 { existsSync } from 'node:fs'\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 priceData?: Map<string, unknown>\n translations?: Record<string, Record<string, string>>\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 the SDK's Router\n * - Lazy-loads page components\n */\nexport function generateEntrySource(options: EntryOptions): string {\n const { config, pages, pagesDir, funnelTsxPath, isDev, translations } = 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 // Serialize price data as a Map constructor call\n // In production, runtime data (prices, integrations, etc.) is injected via window.__APPFUNNEL_DATA__\n const priceDataCode = isDev\n ? (options.priceData && options.priceData.size > 0\n ? `const priceData = new Map(${JSON.stringify([...options.priceData.entries()])});`\n : `const priceData = undefined;`)\n : `const priceData = (() => {\n const rd = typeof window !== 'undefined' && window.__APPFUNNEL_DATA__;\n if (!rd || !rd.products?.items) return undefined;\n // Build price map from server-injected product data\n const map = new Map();\n for (const item of rd.products.items) {\n if (item.priceData && item.storePriceId) map.set(item.storePriceId, item.priceData);\n if (item.trialPriceData && item.trialStorePriceId) map.set(item.trialStorePriceId, item.trialPriceData);\n }\n return map.size > 0 ? map : undefined;\n})();`\n\n const trackingCode = isDev\n ? `\n// Dev mode: mock tracking — log events to console\nglobalThis.__APPFUNNEL_DEV__ = true;\n`\n : ''\n\n // Import app.css if it exists (Tailwind, global styles, etc.)\n const appCssPath = join(pagesDir, '..', 'app.css').replace(/\\\\/g, '/')\n const hasAppCss = existsSync(join(pagesDir, '..', 'app.css'))\n\n return `\nimport { StrictMode, Component, lazy, Suspense, useState, useCallback, useEffect, useTransition, useDeferredValue, useSyncExternalStore } from 'react'\nimport { createRoot } from 'react-dom/client'\nimport { FunnelProvider, useNavigation } from '@appfunnel-dev/sdk'\n${hasAppCss ? `import '${appCssPath}'` : ''}\nimport FunnelWrapper from '${funnelTsxPath.replace(/\\\\/g, '/')}'\n\n${trackingCode}\n\nconst pageComponents = {\n${pageImports}\n}\n\n${priceDataCode}\n\nconst translations = ${translations ? JSON.stringify(translations) : 'undefined'}\n\nconst config = ${JSON.stringify(fullConfig, null, 2)}\n\nconst keyToSlug = ${JSON.stringify(\n Object.fromEntries(Object.entries(slugMap).map(([s, k]) => [k, s])),\n )}\nconst slugToKey = ${JSON.stringify(slugMap)}\n\nconst DEV_CAMPAIGN_SLUG = 'campaign'\nconst DEFAULT_INITIAL = '${config.initialPageKey}'\n\n/**\n * Parse the URL to extract basePath, campaignSlug, and initial page.\n *\n * URL pattern: /f/<campaignSlug>[/<pageSlug>]\n *\n * In dev, redirects bare / to /f/<projectId> so the URL matches production.\n */\nfunction parseUrl() {\n const parts = window.location.pathname.split('/').filter(Boolean)\n\n // /f/<campaignSlug>[/<pageSlug>]\n if (parts[0] === 'f' && parts.length >= 2) {\n const campaignSlug = parts[1]\n const pageSlug = parts[2] || ''\n const pageKey = pageSlug ? (slugToKey[pageSlug] || '') : ''\n return {\n basePath: '/f/' + campaignSlug,\n campaignSlug,\n initialPage: pageKey || DEFAULT_INITIAL,\n }\n }\n\n // Bare URL → redirect to /f/<slug> in dev\n ${isDev ? `\n window.history.replaceState(null, '', '/f/' + DEV_CAMPAIGN_SLUG)\n return {\n basePath: '/f/' + DEV_CAMPAIGN_SLUG,\n campaignSlug: DEV_CAMPAIGN_SLUG,\n initialPage: DEFAULT_INITIAL,\n }` : `\n return {\n basePath: '',\n campaignSlug: '',\n initialPage: DEFAULT_INITIAL,\n }`}\n}\n\nconst { basePath, campaignSlug, initialPage } = parseUrl()\n\n${isDev ? `\nclass ErrorBoundary extends Component {\n constructor(props) {\n super(props)\n this.state = { error: null }\n }\n static getDerivedStateFromError(error) {\n return { error }\n }\n componentDidCatch(error, info) {\n console.error('[AppFunnel] Render error:', error, info)\n }\n render() {\n if (this.state.error) {\n return (\n <div style={{ padding: '2rem', fontFamily: 'monospace' }}>\n <h2 style={{ color: 'red' }}>AppFunnel Error</h2>\n <pre style={{ whiteSpace: 'pre-wrap', color: '#333' }}>{this.state.error.message}</pre>\n <pre style={{ whiteSpace: 'pre-wrap', color: '#666', fontSize: '12px' }}>{this.state.error.stack}</pre>\n </div>\n )\n }\n return this.props.children\n }\n}\n` : ''}\n\n/**\n * PageRenderer lives inside FunnelProvider so it can use SDK hooks.\n * Subscribes to the router — re-renders when the page changes.\n * Uses useTransition to keep showing the current page while the next one loads.\n */\nfunction PageRenderer() {\n const { currentPage, goToPage } = useNavigation()\n const routerPageKey = currentPage?.key || ''\n\n // Track the displayed page separately so we can transition smoothly\n const [displayedKey, setDisplayedKey] = useState(routerPageKey)\n const [isPending, startTransition] = useTransition()\n\n // When the router's page changes, transition to the new page\n useEffect(() => {\n if (routerPageKey && routerPageKey !== displayedKey) {\n startTransition(() => {\n setDisplayedKey(routerPageKey)\n })\n }\n }, [routerPageKey, displayedKey])\n\n // Sync URL with current page\n const slug = currentPage?.slug || routerPageKey\n useEffect(() => {\n const expectedPath = basePath ? basePath + '/' + slug : '/' + slug\n if (slug && window.location.pathname !== expectedPath) {\n window.history.pushState(null, '', expectedPath)\n }\n }, [slug])\n\n // Handle browser back/forward\n useEffect(() => {\n const handlePopState = () => {\n const path = window.location.pathname.split('/').filter(Boolean).pop() || ''\n const key = slugToKey[path]\n if (key && key !== routerPageKey) {\n goToPage(key)\n }\n }\n window.addEventListener('popstate', handlePopState)\n return () => window.removeEventListener('popstate', handlePopState)\n }, [routerPageKey, goToPage])\n\n const PageComponent = pageComponents[displayedKey]\n\n if (!PageComponent) {\n return <div style={{ padding: '2rem', color: 'red' }}>Page not found: {displayedKey}</div>\n }\n\n return (\n <Suspense fallback={null}>\n <PageComponent />\n </Suspense>\n )\n}\n\n// Runtime data injected by the server (production only)\nconst __rd = typeof window !== 'undefined' && window.__APPFUNNEL_DATA__\n\nfunction App() {\n // In production, merge server-injected integrations into config\n const runtimeConfig = __rd && __rd.integrations\n ? { ...config, integrations: { ...config.integrations, ...__rd.integrations } }\n : config\n\n const sessionData = __rd ? {\n campaignId: __rd.campaignId || '',\n funnelId: __rd.funnelId || config.funnelId || '',\n experimentId: __rd.experimentId || null,\n } : undefined\n\n return (\n <FunnelProvider\n config={runtimeConfig}\n initialPage={initialPage}\n basePath={basePath}\n campaignSlug={campaignSlug}\n priceData={priceData}\n sessionData={sessionData}\n translations={translations}\n >\n <FunnelWrapper>\n <PageRenderer />\n </FunnelWrapper>\n </FunnelProvider>\n )\n}\n\ncreateRoot(document.getElementById('root')).render(\n <StrictMode>\n ${isDev ? '<ErrorBoundary>' : ''}\n <App />\n ${isDev ? '</ErrorBoundary>' : ''}\n </StrictMode>\n)\n\n// Reveal body (the host page may set opacity:0 for a loading transition)\ndocument.body.style.opacity = '1'\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, writeFileSync, mkdirSync, readFileSync, readdirSync } 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 + '.tsx'\n\n/** Working directory for dev/build artifacts (like .next/) */\nconst APPFUNNEL_DIR = '.appfunnel'\n\ninterface PluginOptions {\n cwd: string\n config: AppFunnelConfig\n pages: Record<string, PageDefinition>\n isDev: boolean\n priceData?: Map<string, unknown>\n onPagesChange?: () => Promise<void>\n}\n\n/**\n * Load all locale JSON files from <cwd>/locales/.\n * Returns a TranslationMap: { \"en\": { \"key\": \"value\" }, \"de\": { ... } }\n */\nfunction loadTranslations(cwd: string): Record<string, Record<string, string>> | undefined {\n const localesDir = join(cwd, 'locales')\n if (!existsSync(localesDir)) return undefined\n\n const translations: Record<string, Record<string, string>> = {}\n let hasAny = false\n\n for (const file of readdirSync(localesDir)) {\n if (!file.endsWith('.json')) continue\n const locale = file.replace(/\\.json$/, '')\n try {\n const content = readFileSync(join(localesDir, file), 'utf-8')\n translations[locale] = JSON.parse(content)\n hasAny = true\n } catch {\n // Skip invalid JSON files\n }\n }\n\n return hasAny ? translations : undefined\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 const appfunnelDir = join(cwd, APPFUNNEL_DIR)\n const htmlPath = join(appfunnelDir, 'index.html')\n const localesDir = join(cwd, 'locales')\n\n function getEntrySource(): string {\n return generateEntrySource({\n config,\n pages,\n pagesDir,\n funnelTsxPath,\n isDev,\n priceData: options.priceData,\n translations: loadTranslations(cwd),\n })\n }\n\n return {\n name: 'appfunnel',\n\n config() {\n // Ensure .appfunnel/ exists and write the HTML shell\n mkdirSync(appfunnelDir, { recursive: true })\n writeFileSync(htmlPath, generateHtml(config.name || 'AppFunnel'))\n\n return {\n // Don't let Vite auto-serve index.html — we handle it ourselves\n appType: 'custom',\n resolve: {\n alias: {\n '@': resolve(cwd, 'src'),\n },\n dedupe: ['react', 'react-dom'],\n },\n esbuild: {\n jsx: 'automatic',\n },\n optimizeDeps: {\n include: ['react', 'react-dom', 'react/jsx-runtime'],\n force: true,\n },\n }\n },\n\n resolveId(id) {\n if (id === VIRTUAL_ENTRY_ID || id === '/' + VIRTUAL_ENTRY_ID) {\n return RESOLVED_VIRTUAL_ENTRY_ID\n }\n return null\n },\n\n async load(id) {\n if (id === RESOLVED_VIRTUAL_ENTRY_ID) {\n const { transform } = await import('esbuild')\n const source = getEntrySource()\n const result = await transform(source, {\n loader: 'tsx',\n jsx: 'automatic',\n sourcefile: 'appfunnel-entry.tsx',\n })\n return result.code\n }\n return null\n },\n\n configureServer(devServer) {\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 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 // Watch locales/ for changes → full reload\n if (existsSync(localesDir)) {\n watcher.add(localesDir)\n watcher.on('change', (file) => {\n if (file.startsWith(localesDir) && file.endsWith('.json')) {\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 }\n\n // Since appType is 'custom', we serve HTML ourselves.\n // Registered after Vite internals so /@vite, /node_modules etc. resolve first.\n return () => {\n devServer.middlewares.use(async (req, res, next) => {\n const url = req.url?.split('?')[0] || ''\n\n // Let Vite handle static assets and internal routes\n if (url.includes('.') || url.startsWith('/@') || url.startsWith('/node_modules')) {\n return next()\n }\n\n try {\n const rawHtml = readFileSync(htmlPath, 'utf-8')\n const html = await devServer.transformIndexHtml(req.url || '/', rawHtml)\n res.statusCode = 200\n res.setHeader('Content-Type', 'text/html')\n res.end(html)\n } catch (err) {\n next(err)\n }\n })\n }\n },\n\n transformIndexHtml(html) {\n return html\n },\n }\n}\n","import { readFileSync, writeFileSync } from 'node:fs'\nimport { join } 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'\nimport { promptForProject } from '../lib/projects.js'\nimport { fetchPrices } from '../lib/api.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 const creds = 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 s.stop()\n\n // 4. If no projectId, prompt for one and update the config file\n if (!config.projectId) {\n log.info('No projectId found in appfunnel.config.ts')\n const projectId = await promptForProject(creds.token)\n config.projectId = projectId\n\n // Write projectId back to the config file\n const configPath = join(cwd, 'appfunnel.config.ts')\n const configSource = readFileSync(configPath, 'utf-8')\n let updated: string\n\n if (/projectId:\\s*['\"]/.test(configSource)) {\n // Replace existing projectId value\n updated = configSource.replace(\n /projectId:\\s*['\"].*?['\"]/,\n `projectId: '${projectId}'`,\n )\n } else {\n // Insert projectId after the opening of defineConfig({\n updated = configSource.replace(\n /(defineConfig\\(\\{[\\t ]*\\n)/,\n `$1\\tprojectId: '${projectId}',\\n`,\n )\n }\n\n if (updated !== configSource) {\n writeFileSync(configPath, updated)\n log.success(`Updated projectId in appfunnel.config.ts`)\n } else {\n log.warn(`Could not auto-update appfunnel.config.ts — add projectId: '${projectId}' manually.`)\n }\n }\n\n // 5. Scan and extract pages\n const s2 = log.spinner('Scanning pages...')\n let pageKeys = scanPages(cwd)\n let pages = await extractPageDefinitions(cwd, pageKeys)\n s2.stop()\n\n log.info(`Found ${pageKeys.length} pages: ${pageKeys.join(', ')}`)\n\n // 6. Fetch store prices (cached for dev session)\n let priceData: Map<string, unknown> = new Map()\n if (config.projectId && config.products?.items?.length) {\n try {\n const storePriceIds = [\n ...new Set(\n config.products.items.flatMap((item) =>\n [item.storePriceId, item.trialStorePriceId].filter(Boolean) as string[]\n )\n ),\n ]\n\n log.info(`Fetching ${storePriceIds.length} store prices: ${storePriceIds.join(', ')}`)\n\n const s3 = log.spinner('Fetching store prices...')\n priceData = await fetchPrices(config.projectId, storePriceIds, { token: creds.token })\n s3.stop()\n\n // Check that every requested price was returned\n const missingIds = storePriceIds.filter((id) => !priceData.has(id))\n if (missingIds.length > 0) {\n log.error(`Missing store prices: ${missingIds.join(', ')}`)\n log.error('Make sure these storePriceId values in your config match prices in your project.')\n process.exit(1)\n }\n\n log.success(`Fetched ${priceData.size}/${storePriceIds.length} store prices`)\n } catch (err) {\n log.error(`Failed to fetch store prices: ${(err as Error).message}`)\n process.exit(1)\n }\n }\n\n // 7. Start Vite dev server\n const { createServer } = await import('vite')\n const react = await import('@vitejs/plugin-react')\n\n // Load @tailwindcss/vite from the user's project\n let tailwindPlugin: any = null\n try {\n const { createRequire } = await import('node:module')\n const require = createRequire(join(cwd, 'package.json'))\n const tailwindPath = require.resolve('@tailwindcss/vite')\n const tailwindVite = await import(tailwindPath)\n tailwindPlugin = tailwindVite.default\n } catch {\n // Not installed — skip\n }\n\n const server = await createServer({\n root: cwd,\n server: {\n port,\n strictPort: false,\n },\n plugins: [\n react.default(),\n ...(tailwindPlugin ? [tailwindPlugin()] : []),\n appfunnelPlugin({\n cwd,\n config,\n pages,\n isDev: true,\n priceData,\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 type { PageDefinition } from '../extract/pages.js'\n\ninterface SharedSegment {\n type: 'shared'\n pageKeys: string[]\n}\n\ninterface BranchSegment {\n type: 'branch'\n sourcePageKey: string\n arms: { pageKeys: string[] }[]\n}\n\nexport type FlowSegment = SharedSegment | BranchSegment\n\nexport interface ComputedFunnelFlow {\n initialPageKey: string\n segments: FlowSegment[]\n terminalPageKeys: string[]\n unreachablePageKeys: string[]\n}\n\n/**\n * Compute the segment-based flow through a funnel.\n *\n * Produces shared segments (linear page sequences) and branch segments\n * (where routes diverge, with variable-length arms that reconverge).\n *\n * Ported from api/src/utils/computeFunnelPaths.ts, adapted for CLI PageDefinition format.\n */\nexport function computeFunnelFlow(\n pages: Record<string, PageDefinition>,\n initialPageKey: string,\n): ComputedFunnelFlow {\n const allPageKeys = new Set(Object.keys(pages))\n\n if (allPageKeys.size === 0) {\n return { initialPageKey, segments: [], terminalPageKeys: [], unreachablePageKeys: [] }\n }\n\n // Build adjacency: pageKey → unique ordered target pageKeys\n function getTargets(pageKey: string): string[] {\n const routes = pages[pageKey]?.routes || []\n const seen = new Set<string>()\n const targets: string[] = []\n for (const route of routes) {\n if (allPageKeys.has(route.to) && !seen.has(route.to)) {\n seen.add(route.to)\n targets.push(route.to)\n }\n }\n return targets\n }\n\n // BFS from initial page to find reachable pages\n const reachable = new Set<string>()\n {\n const queue: string[] = []\n if (allPageKeys.has(initialPageKey)) {\n reachable.add(initialPageKey)\n queue.push(initialPageKey)\n }\n while (queue.length > 0) {\n const pageKey = queue.shift()!\n for (const target of getTargets(pageKey)) {\n if (!reachable.has(target)) {\n reachable.add(target)\n queue.push(target)\n }\n }\n }\n }\n\n const unreachablePageKeys: string[] = []\n const terminalPageKeys: string[] = []\n\n for (const pageKey of allPageKeys) {\n if (!reachable.has(pageKey)) {\n unreachablePageKeys.push(pageKey)\n continue\n }\n if (getTargets(pageKey).length === 0) {\n terminalPageKeys.push(pageKey)\n }\n }\n\n // BFS forward from a page to collect all reachable pageKeys with distances\n function bfsForward(start: string): Map<string, number> {\n const dist = new Map<string, number>()\n dist.set(start, 0)\n const queue = [start]\n while (queue.length > 0) {\n const current = queue.shift()!\n const d = dist.get(current)!\n for (const target of getTargets(current)) {\n if (!dist.has(target)) {\n dist.set(target, d + 1)\n queue.push(target)\n }\n }\n }\n return dist\n }\n\n // Find convergence page for branch arms\n function findConvergence(armStarts: string[]): string | null {\n if (armStarts.length === 0) return null\n\n const armReachable = armStarts.map((s) => bfsForward(s))\n const armStartSet = new Set(armStarts)\n\n let candidates = new Set<string>()\n for (const key of armReachable[0].keys()) {\n if (!armStartSet.has(key)) candidates.add(key)\n }\n for (let i = 1; i < armReachable.length; i++) {\n const reachSet = new Set(armReachable[i].keys())\n candidates = new Set([...candidates].filter((k) => reachSet.has(k)))\n }\n\n if (candidates.size === 0) return null\n\n let bestPage: string | null = null\n let bestMinDist = Infinity\n for (const pageKey of candidates) {\n const minDist = Math.min(...armReachable.map((r) => r.get(pageKey) ?? Infinity))\n if (minDist < bestMinDist) {\n bestMinDist = minDist\n bestPage = pageKey\n }\n }\n\n return bestPage\n }\n\n // Walk a branch arm from start to convergence\n function walkArm(start: string, convergence: string | null, globalVisited: Set<string>): string[] {\n if (convergence === null) {\n const pages: string[] = []\n let current = start\n while (true) {\n if (globalVisited.has(current)) break\n globalVisited.add(current)\n pages.push(current)\n const targets = getTargets(current)\n if (targets.length === 0) break\n const next = targets.find((t) => !globalVisited.has(t))\n if (!next) break\n current = next\n }\n return pages\n }\n\n // BFS from start to convergence\n const parent = new Map<string, string | null>()\n parent.set(start, null)\n const queue = [start]\n let found = false\n\n while (queue.length > 0 && !found) {\n const current = queue.shift()!\n for (const target of getTargets(current)) {\n if (!parent.has(target)) {\n parent.set(target, current)\n if (target === convergence) {\n found = true\n break\n }\n queue.push(target)\n }\n }\n }\n\n if (!found) {\n const pages: string[] = []\n let current = start\n while (true) {\n if (globalVisited.has(current)) break\n globalVisited.add(current)\n pages.push(current)\n const targets = getTargets(current)\n if (targets.length === 0) break\n const next = targets.find((t) => !globalVisited.has(t))\n if (!next) break\n current = next\n }\n return pages\n }\n\n const path: string[] = []\n let node: string | null = convergence\n while (node !== null && node !== start) {\n path.unshift(node)\n node = parent.get(node) ?? null\n }\n path.unshift(start)\n if (path[path.length - 1] === convergence) {\n path.pop()\n }\n\n for (const p of path) {\n globalVisited.add(p)\n }\n\n return path\n }\n\n // --- Main walk: build segments ---\n if (!allPageKeys.has(initialPageKey)) {\n return { initialPageKey, segments: [], terminalPageKeys, unreachablePageKeys }\n }\n\n const segments: FlowSegment[] = []\n const globalVisited = new Set<string>()\n let current: string | null = initialPageKey\n let sharedPages: string[] = []\n\n while (current !== null && !globalVisited.has(current)) {\n const targets = getTargets(current)\n\n if (targets.length === 0) {\n sharedPages.push(current)\n globalVisited.add(current)\n break\n }\n\n const uniqueTargets = [...new Set(targets)]\n\n if (uniqueTargets.length === 1) {\n sharedPages.push(current)\n globalVisited.add(current)\n current = uniqueTargets[0]\n continue\n }\n\n // Branch point\n sharedPages.push(current)\n globalVisited.add(current)\n segments.push({ type: 'shared', pageKeys: [...sharedPages] })\n sharedPages = []\n\n const convergence = findConvergence(uniqueTargets)\n\n const arms: { pageKeys: string[] }[] = []\n for (const armStart of uniqueTargets) {\n const armPages = walkArm(armStart, convergence, globalVisited)\n arms.push({ pageKeys: armPages })\n }\n\n segments.push({ type: 'branch', sourcePageKey: current, arms })\n\n if (convergence !== null && !globalVisited.has(convergence)) {\n current = convergence\n } else {\n current = null\n }\n }\n\n if (sharedPages.length > 0) {\n segments.push({ type: 'shared', pageKeys: sharedPages })\n }\n\n return { initialPageKey, segments, terminalPageKeys, unreachablePageKeys }\n}\n","import pc from 'picocolors'\nimport * as log from '../lib/logger.js'\nimport { loadConfig } from '../lib/config.js'\nimport { scanPages, extractPageDefinitions, type PageDefinition } from '../extract/pages.js'\nimport { computeFunnelFlow, type FlowSegment, type ComputedFunnelFlow } from '../lib/computeFlow.js'\n\nexport async function routesCommand(): Promise<void> {\n const cwd = process.cwd()\n\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 const flow = computeFunnelFlow(pages, config.initialPageKey)\n\n console.log()\n console.log(` ${pc.bold(config.name)}`)\n console.log(` ${pc.dim(`${pageKeys.length} pages`)}`)\n console.log()\n\n renderFlow(flow, pages)\n}\n\n// ── Box-drawing characters ─────────────────────────────────────\n\nconst H = '─'\nconst V = '│'\n\nfunction renderFlow(flow: ComputedFunnelFlow, pages: Record<string, PageDefinition>): void {\n const indent = ' '\n\n for (let i = 0; i < flow.segments.length; i++) {\n const segment = flow.segments[i]\n\n if (segment.type === 'shared') {\n for (let j = 0; j < segment.pageKeys.length; j++) {\n const key = segment.pageKeys[j]\n const page = pages[key]\n const isStart = key === flow.initialPageKey\n const isTerminal = flow.terminalPageKeys.includes(key)\n printPageBox(indent, key, page, isStart, isTerminal)\n\n const isLastInSegment = j === segment.pageKeys.length - 1\n const hasNextSegment = i < flow.segments.length - 1\n if ((!isLastInSegment || hasNextSegment) && !isTerminal) {\n printArrow(indent)\n }\n }\n } else {\n renderBranch(indent, segment, pages, flow)\n\n if (i < flow.segments.length - 1) {\n printArrow(indent)\n }\n }\n }\n\n // Unreachable pages\n if (flow.unreachablePageKeys.length > 0) {\n console.log()\n console.log(` ${pc.yellow('⚠')} ${pc.yellow('Unreachable pages:')}`)\n for (const key of flow.unreachablePageKeys) {\n const page = pages[key]\n console.log(` ${pc.dim('•')} ${key}${page?.name ? pc.dim(` (${page.name})`) : ''}`)\n }\n }\n\n console.log()\n}\n\nfunction formatPageLabel(key: string, page: PageDefinition | undefined, isStart: boolean, isTerminal: boolean): string {\n const name = page?.name || key\n const type = page?.type\n const badges: string[] = []\n\n if (isStart) badges.push(pc.green('start'))\n if (isTerminal) badges.push(pc.red('end'))\n if (type && type !== 'default') badges.push(pc.magenta(type))\n\n const label = key === name.toLowerCase().replace(/\\s+/g, '-') ? name : `${name} ${pc.dim(`(${key})`)}`\n const badgeStr = badges.length > 0 ? ' ' + badges.map((b) => pc.dim('[') + b + pc.dim(']')).join(' ') : ''\n\n return label + badgeStr\n}\n\nfunction printPageBox(indent: string, key: string, page: PageDefinition | undefined, isStart: boolean, isTerminal: boolean): void {\n const label = formatPageLabel(key, page, isStart, isTerminal)\n const rawLen = stripAnsi(label).length\n const boxWidth = Math.max(rawLen + 2, 20)\n const padding = boxWidth - rawLen - 2\n\n console.log(`${indent}┌${H.repeat(boxWidth)}┐`)\n console.log(`${indent}│ ${label}${' '.repeat(padding)} │`)\n console.log(`${indent}└${H.repeat(boxWidth)}┘`)\n}\n\nfunction printArrow(indent: string): void {\n console.log(`${indent} ${V}`)\n console.log(`${indent} ▼`)\n}\n\nfunction renderBranch(\n indent: string,\n segment: Extract<FlowSegment, { type: 'branch' }>,\n pages: Record<string, PageDefinition>,\n flow: ComputedFunnelFlow,\n): void {\n const arms = segment.arms\n\n // Compute column widths for each arm\n const colWidths = arms.map((arm) => {\n const maxNameLen = Math.max(\n ...arm.pageKeys.map((key) => {\n const page = pages[key]\n const name = page?.name || key\n const type = page?.type\n // Account for type badge\n const typeLen = type && type !== 'default' ? ` [${type}]`.length : 0\n return name.length + typeLen\n }),\n 8,\n )\n return maxNameLen + 4\n })\n\n // Draw the split line: ├───────┬───────┤\n let splitLine = ''\n for (let a = 0; a < arms.length; a++) {\n const mid = Math.floor(colWidths[a] / 2)\n if (a === 0) {\n splitLine += ' '.repeat(mid) + '├' + H.repeat(colWidths[a] - mid - 1)\n } else if (a === arms.length - 1) {\n splitLine += H.repeat(mid) + '┤' + ' '.repeat(colWidths[a] - mid - 1)\n } else {\n splitLine += H.repeat(mid) + '┬' + H.repeat(colWidths[a] - mid - 1)\n }\n if (a < arms.length - 1) splitLine += H.repeat(3)\n }\n console.log(`${indent}${splitLine}`)\n\n // Draw arm down pipes\n let pipeLine = ''\n for (let a = 0; a < arms.length; a++) {\n const mid = Math.floor(colWidths[a] / 2)\n pipeLine += ' '.repeat(mid) + V + ' '.repeat(colWidths[a] - mid - 1)\n if (a < arms.length - 1) pipeLine += ' '\n }\n console.log(`${indent}${pipeLine}`)\n\n // Draw arm pages row by row\n const maxDepth = Math.max(...arms.map((a) => a.pageKeys.length))\n\n for (let row = 0; row < maxDepth; row++) {\n let line = ''\n for (let a = 0; a < arms.length; a++) {\n const key = arms[a].pageKeys[row]\n const mid = Math.floor(colWidths[a] / 2)\n if (key) {\n const page = pages[key]\n const name = page?.name || key\n const type = page?.type\n const typeBadge = type && type !== 'default' ? ' ' + pc.magenta(`[${type}]`) : ''\n const display = name + typeBadge\n const displayLen = stripAnsi(display).length\n const truncated = displayLen > colWidths[a] - 2\n ? name.slice(0, colWidths[a] - 5) + '...'\n : display\n const truncLen = stripAnsi(truncated).length\n const leftPad = mid - Math.floor(truncLen / 2)\n const rightPad = colWidths[a] - leftPad - truncLen\n line += ' '.repeat(Math.max(0, leftPad)) + pc.bold(truncated) + ' '.repeat(Math.max(0, rightPad))\n } else {\n line += ' '.repeat(colWidths[a])\n }\n if (a < arms.length - 1) line += ' '\n }\n console.log(`${indent}${line}`)\n\n // Draw connector between rows\n if (row < maxDepth - 1) {\n let connector = ''\n for (let a = 0; a < arms.length; a++) {\n const mid = Math.floor(colWidths[a] / 2)\n const hasMore = row + 1 < arms[a].pageKeys.length\n if (hasMore) {\n connector += ' '.repeat(mid) + '▼' + ' '.repeat(colWidths[a] - mid - 1)\n } else {\n connector += ' '.repeat(colWidths[a])\n }\n if (a < arms.length - 1) connector += ' '\n }\n console.log(`${indent}${connector}`)\n }\n }\n\n // Draw the merge line: ├───────┴───────┤\n let mergeLine = ''\n for (let a = 0; a < arms.length; a++) {\n const mid = Math.floor(colWidths[a] / 2)\n if (a === 0) {\n mergeLine += ' '.repeat(mid) + '├' + H.repeat(colWidths[a] - mid - 1)\n } else if (a === arms.length - 1) {\n mergeLine += H.repeat(mid) + '┤' + ' '.repeat(colWidths[a] - mid - 1)\n } else {\n mergeLine += H.repeat(mid) + '┴' + H.repeat(colWidths[a] - mid - 1)\n }\n if (a < arms.length - 1) mergeLine += H.repeat(3)\n }\n console.log(`${indent}${mergeLine}`)\n}\n\nfunction stripAnsi(str: string): string {\n // eslint-disable-next-line no-control-regex\n return str.replace(/\\x1b\\[[0-9;]*m/g, '')\n}\n","import { resolve, join } from 'node:path'\nimport { randomUUID } from 'node:crypto'\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, type AppFunnelConfig } 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\n/** UTM query params — always included, no config needed */\nconst BUILTIN_QUERY_PARAMS: Record<string, { type: string }> = {\n utm_source: { type: 'string' },\n utm_medium: { type: 'string' },\n utm_campaign: { type: 'string' },\n utm_content: { type: 'string' },\n utm_term: { type: 'string' },\n}\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\n if (!config.projectId) {\n s.stop()\n throw new CLIError(\n 'MISSING_PROJECT_ID',\n 'Missing projectId in appfunnel.config.ts.',\n \"Run 'appfunnel dev' first to select a project, or add projectId manually.\",\n )\n }\n\n if (!config.initialPageKey) {\n s.stop()\n throw new CLIError(\n 'MISSING_INITIAL_PAGE_KEY',\n 'Missing initialPageKey in appfunnel.config.ts.',\n \"Set initialPageKey to the page key (filename without .tsx) of your first page.\",\n )\n }\n\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, 'dist')\n const { build } = await import('vite')\n const react = await import('@vitejs/plugin-react')\n\n // Try to load @tailwindcss/vite from the user's project\n let tailwindPlugin: any = null\n try {\n const { createRequire } = await import('node:module')\n const require = createRequire(join(cwd, 'package.json'))\n const tailwindPath = require.resolve('@tailwindcss/vite')\n const tailwindVite = await import(tailwindPath)\n tailwindPlugin = tailwindVite.default\n } catch {\n // Not installed in the user's project — skip\n }\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 base: './',\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-dev/sdk')) {\n return 'vendor-sdk'\n }\n },\n },\n },\n },\n plugins: [\n react.default(),\n ...(tailwindPlugin ? [tailwindPlugin()] : []),\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 buildHash: randomUUID(),\n sdkVersion: getSdkVersion(cwd),\n projectId: config.projectId,\n funnelId: config.funnelId,\n name: config.name,\n initialPageKey: config.initialPageKey,\n pages: { ...config.pages, ...mergedPages },\n routes: { ...config.routes, ...mergedRoutes },\n responses: config.responses || {},\n queryParams: { ...BUILTIN_QUERY_PARAMS, ...config.queryParams },\n data: config.data || {},\n products: config.products || {},\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:')} dist/`)\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) {\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: AppFunnelConfig,\n pages: Record<string, { routes?: Array<{ to: string; when?: unknown }> }>,\n pageKeys: string[],\n): void {\n const allPageKeys = new Set(pageKeys)\n\n // Build full set of known variables from all namespaced sections\n const allVariables = new Set<string>()\n if (config.responses) {\n for (const key of Object.keys(config.responses)) {\n allVariables.add(`answers.${key}`)\n }\n }\n if (config.queryParams) {\n for (const key of Object.keys(config.queryParams)) {\n allVariables.add(`query.${key}`)\n }\n }\n if (config.data) {\n for (const key of Object.keys(config.data)) {\n allVariables.add(`data.${key}`)\n }\n }\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.*\n const prefix = cond.variable.split('.')[0]\n const systemPrefixes = ['page', 'device', 'browser', 'os', 'session', 'system', 'metadata', 'user', 'products', 'card', 'payment', 'stripe', 'subscription']\n if (!systemPrefixes.includes(prefix) && !allVariables.has(cond.variable)) {\n // Suggest the right config section based on the prefix\n const varName = cond.variable.includes('.') ? cond.variable.split('.').slice(1).join('.') : cond.variable\n let hint: string\n if (prefix === 'answers') {\n hint = `Add it to responses in appfunnel.config.ts: responses: { '${varName}': { type: 'string' } }`\n } else if (prefix === 'query') {\n hint = `Add it to queryParams in appfunnel.config.ts: queryParams: { '${varName}': { type: 'string' } }`\n } else if (prefix === 'data') {\n hint = `Add it to data in appfunnel.config.ts: data: { '${varName}': { type: 'string' } }`\n } else {\n hint = `Add it to the appropriate section (responses, queryParams, or data) in appfunnel.config.ts.`\n }\n throw new CLIError(\n 'UNDEFINED_VARIABLE',\n `Route condition in \"${pageKey}\" references variable \"${cond.variable}\" which is not defined.`,\n hint,\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-dev', 'sdk', 'package.json'), 'utf-8'))\n return pkg.version\n } catch {\n return '0.0.0'\n }\n}\n","import { join } from 'node:path'\nimport { readFileSync, writeFileSync } from 'node:fs'\n\n/**\n * Patch appfunnel.config.ts to add a funnelId after the projectId line.\n * Uses simple string replacement — no AST parsing needed.\n */\nexport function patchConfigFunnelId(cwd: string, funnelId: string): void {\n const configPath = join(cwd, 'appfunnel.config.ts')\n let content = readFileSync(configPath, 'utf-8')\n\n // Already has funnelId — don't double-add\n if (content.includes('funnelId')) return\n\n // Insert funnelId after the projectId line\n const patched = content.replace(\n /(projectId:\\s*['\"][^'\"]+['\"],?\\s*\\n)/,\n `$1 funnelId: '${funnelId}',\\n`,\n )\n\n if (patched !== content) {\n writeFileSync(configPath, patched, 'utf-8')\n }\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'\nimport { patchConfigFunnelId } from '../lib/config-patch.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\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\nexport async function publishCommand(options?: { promote?: boolean }): 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 const projectId = config.projectId\n if (!projectId) {\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 // 3. Check build output exists\n const outDir = resolve(cwd, 'dist')\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('Preparing assets...')\n const assetPayloads: Array<{ path: string; content: Buffer; contentType: string }> = []\n let totalBytes = 0\n\n for (let i = 0; i < assets.length; i++) {\n const asset = assets[i]\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 const content = readFileSync(fullPath)\n totalBytes += content.length\n assetPayloads.push({\n path: asset.path,\n content,\n contentType: getMimeType(asset.path),\n })\n s.text = `Preparing assets... ${i + 1}/${assets.length} ${pc.dim(`(${formatSize(totalBytes)})`)}`\n }\n\n // 6. Upload\n s.text = `Uploading ${assets.length} assets ${pc.dim(`(${formatSize(totalBytes)})`)}`\n\n const result = await publishBuild(\n projectId,\n config.funnelId || '',\n manifest,\n assetPayloads,\n { token: creds.token },\n options?.promote,\n )\n\n s.stop()\n\n // 7. If the server created a new funnel, write funnelId back to config\n if (result.created && result.funnelId) {\n patchConfigFunnelId(cwd, result.funnelId)\n log.info(`Funnel created — funnelId added to appfunnel.config.ts`)\n }\n\n // 8. Print result\n console.log()\n log.success(result.activated ? 'Published and activated' : 'Published successfully')\n console.log()\n console.log(` ${pc.dim('Build ID:')} ${result.buildId}`)\n if (result.funnelId && !config.funnelId) {\n console.log(` ${pc.dim('Funnel:')} ${result.funnelId}`)\n }\n console.log(` ${pc.dim('Dashboard:')} ${pc.cyan(result.dashboardUrl)}`)\n console.log(` ${pc.dim('Assets:')} ${assets.length} files ${pc.dim(`(${formatSize(totalBytes)})`)}`)\n if (!result.activated) {\n console.log()\n console.log(` ${pc.dim('Tip:')} Use ${pc.cyan('--promote')} to activate immediately, or promote from the dashboard.`)\n }\n console.log()\n}\n","import { Command } from 'commander'\nimport pc from 'picocolors'\nimport { CLIError, formatError } from './lib/errors.js'\n\nconst program = new Command()\n\nprogram\n .name('appfunnel')\n .description('Build and publish headless AppFunnel projects')\n .version(__CLI_VERSION__)\n\nprogram\n .command('init')\n .argument('[name]', 'Project directory 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('routes')\n .description('Visualize the funnel page flow')\n .action(async () => {\n const { routesCommand } = await import('./commands/routes.js')\n await routesCommand()\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 .option('--promote', 'Activate the build immediately after publishing')\n .action(async (options: { promote?: boolean }) => {\n const { publishCommand } = await import('./commands/publish.js')\n await publishCommand({ promote: options.promote })\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","pc","DEFAULT_API_BASE","error","readFileSync","writeFileSync","join","pc","select","s","resolve","spinner","pc","DEFAULT_API_BASE","existsSync","readFileSync","join","readFileSync","join","readdirSync","readFileSync","existsSync","join","join","existsSync","resolve","join","existsSync","writeFileSync","mkdirSync","readFileSync","readdirSync","readFileSync","writeFileSync","join","pc","createServer","require","current","globalVisited","pages","pc","resolve","join","randomUUID","readFileSync","writeFileSync","readdirSync","pc","require","getSdkVersion","join","readFileSync","writeFileSync","resolve","join","readFileSync","existsSync","pc","formatSize","pc","initCommand","loginCommand","whoamiCommand","devCommand","routesCommand","buildCommand","publishCommand"]}
1
+ {"version":3,"sources":["../src/lib/errors.ts","../src/lib/logger.ts","../src/lib/auth.ts","../src/lib/projects.ts","../src/lib/api.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/lib/computeFlow.ts","../src/commands/routes.ts","../src/commands/build.ts","../src/lib/config-patch.ts","../src/commands/publish.ts","../src/index.ts"],"sourcesContent":["import pc from 'picocolors'\n\nexport type ErrorCode =\n\t| 'AUTH_REQUIRED'\n\t| 'AUTH_EXPIRED'\n\t| 'CONFIG_NOT_FOUND'\n\t| 'INVALID_ROUTE'\n\t| 'UNDEFINED_VARIABLE'\n\t| 'VERSION_MISMATCH'\n\t| 'BUILD_NOT_FOUND'\n\t| 'FUNNEL_NOT_HEADLESS'\n\t| 'BUNDLE_TOO_LARGE'\n\t| 'PAGE_SIZE'\n\t| 'INVALID_PAGE'\n\t| 'NO_PAGES'\n\t| 'NO_PROJECTS'\n\t| 'MISSING_PROJECT_ID'\n\t| 'MISSING_INITIAL_PAGE_KEY'\n\t| 'API_ERROR'\n\t| 'PUBLISH_FAILED'\n\nexport class CLIError extends Error {\n\tcode: ErrorCode\n\thint?: string\n\tstatusCode?: number\n\n\tconstructor(code: ErrorCode, message: string, hint?: string) {\n\t\tsuper(message)\n\t\tthis.name = 'CLIError'\n\t\tthis.code = code\n\t\tthis.hint = hint\n\t}\n}\n\nexport function formatError(err: CLIError): string {\n\tconst lines = [\n\t\t`${pc.red('ERROR')} ${pc.dim(`[${err.code}]`)}: ${err.message}`,\n\t]\n\tif (err.hint) {\n\t\tlines.push(` ${pc.dim('Hint:')} ${err.hint}`)\n\t}\n\treturn lines.join('\\n')\n}\n\nexport function formatWarning(\n\tcode: string,\n\tmessage: string,\n\thint?: string\n): string {\n\tconst lines = [`${pc.yellow('WARNING')} ${pc.dim(`[${code}]`)}: ${message}`]\n\tif (hint) {\n\t\tlines.push(` ${pc.dim('Hint:')} ${hint}`)\n\t}\n\treturn lines.join('\\n')\n}\n","import 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' + __CLI_VERSION__)}`)\n console.log()\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 pc from 'picocolors'\nimport select from '@inquirer/select'\nimport * as log from './logger.js'\nimport { CLIError } from './errors.js'\n\nconst DEFAULT_API_BASE = 'https://api.appfunnel.net'\n\nexport interface Project {\n id: string\n name: string\n role: string\n}\n\nexport async function fetchProjects(token: string): Promise<Project[]> {\n const response = await fetch(`${DEFAULT_API_BASE}/user/projects`, {\n headers: {\n Authorization: token,\n 'Content-Type': 'application/json',\n },\n })\n\n if (!response.ok) {\n throw new CLIError('API_ERROR', 'Failed to fetch projects.')\n }\n\n const body = (await response.json()) as { data: Project[] }\n return body.data\n}\n\n/**\n * Prompt the user to select a project from their available projects.\n * Returns the selected project ID.\n */\nexport async function promptForProject(token: string): Promise<string> {\n const spin = log.spinner('Fetching projects...')\n let projects: Project[]\n try {\n projects = await fetchProjects(token)\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 spin.stop()\n\n if (projects.length === 0) {\n throw new CLIError(\n 'NO_PROJECTS',\n 'No projects found.',\n 'Create a project at https://appfunnel.net first.',\n )\n }\n\n return select({\n message: 'Select a project',\n choices: projects.map((p) => ({\n name: `${p.name} ${pc.dim(`(${p.id})`)}`,\n value: p.id,\n })),\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/* ---------- Store & price listing (for template init) ---------- */\n\nexport interface Store {\n\tid: string\n\tname: string\n\ttype: 'STRIPE' | 'PADDLE'\n\tisTestMode: boolean\n}\n\nexport interface StorePrice {\n\tid: string\n\tstoreId: string\n\tname: string | null\n\tpriceName: string | null\n\tdisplayName: string | null\n\tamount: number\n\tcurrency: string\n\tinterval: string | null\n\tintervalCount: number\n}\n\n/**\n * Fetch all stores for a project.\n */\nexport async function fetchStores(\n\tprojectId: string,\n\toptions: ApiOptions\n): Promise<Store[]> {\n\tconst response = await apiFetch(`/project/${projectId}/stores`, {\n\t\t...options,\n\t\tmethod: 'GET',\n\t})\n\tconst data = (await response.json()) as { data: Store[] }\n\treturn data.data || []\n}\n\n/**\n * Fetch all prices for a store.\n */\nexport async function fetchStorePrices(\n\tprojectId: string,\n\tstoreId: string,\n\toptions: ApiOptions\n): Promise<StorePrice[]> {\n\tconst response = await apiFetch(\n\t\t`/project/${projectId}/stores/${storeId}/prices`,\n\t\t{ ...options, method: 'GET' }\n\t)\n\tconst data = (await response.json()) as { data: StorePrice[] }\n\treturn data.data || []\n}\n\n/**\n * Fetch all prices across all stores for a project.\n * Returns prices annotated with store info for display.\n */\nexport async function fetchAllProjectPrices(\n\tprojectId: string,\n\toptions: ApiOptions\n): Promise<Array<StorePrice & { store: Store }>> {\n\tconst stores = await fetchStores(projectId, options)\n\tconst all: Array<StorePrice & { store: Store }> = []\n\n\tfor (const store of stores) {\n\t\tconst prices = await fetchStorePrices(projectId, store.id, options)\n\t\tfor (const price of prices) {\n\t\t\tall.push({ ...price, store })\n\t\t}\n\t}\n\n\treturn all\n}\n\n/**\n * Initialize a presigned-URL publish. Returns buildId and presigned upload URLs.\n */\nexport async function publishInit(\n\tprojectId: string,\n\tbody: {\n\t\tmanifest: unknown\n\t\tfunnelId?: string\n\t\tpromote?: boolean\n\t\tcommitSha?: string\n\t\tbranch?: string\n\t\tmessage?: string\n\t\ttriggeredBy?: string\n\t},\n\toptions: ApiOptions\n): Promise<{\n\tbuildId: string\n\tfunnelId: string\n\tcreated: boolean\n\tassets: Array<{ path: string; uploadUrl: string; contentType: string }>\n}> {\n\tconst response = await apiFetch(`/project/${projectId}/headless/publish-init`, {\n\t\t...options,\n\t\tmethod: 'POST',\n\t\tbody: JSON.stringify(body),\n\t})\n\treturn (await response.json()) as any\n}\n\n/**\n * Finalize a presigned-URL publish after all assets have been uploaded.\n */\nexport async function publishFinalize(\n\tprojectId: string,\n\tbody: { buildId: string; funnelId: string },\n\toptions: ApiOptions\n): Promise<{\n\tbuildId: string\n\tfunnelId: string\n\tactivated: boolean\n\tdashboardUrl: string\n\tmessage: string\n}> {\n\tconst response = await apiFetch(`/project/${projectId}/headless/publish-finalize`, {\n\t\t...options,\n\t\tmethod: 'POST',\n\t\tbody: JSON.stringify(body),\n\t})\n\treturn (await response.json()) as any\n}\n\n/**\n * Publish a headless build (legacy FormData flow).\n * If funnelId is empty, the server auto-creates a new headless funnel + campaign.\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\tpromote?: boolean,\n\tmetadata?: { commitSha?: string; branch?: string; message?: string; triggeredBy?: string }\n): Promise<{ buildId: string; funnelId: string; created: boolean; activated: boolean; dashboardUrl: string; message: string }> {\n\tconst formData = new FormData()\n\tformData.set('manifest', JSON.stringify(manifest))\n\tif (funnelId) {\n\t\tformData.set('funnelId', funnelId)\n\t}\n\tif (promote) {\n\t\tformData.set('promote', 'true')\n\t}\n\tif (metadata?.commitSha) formData.set('commitSha', metadata.commitSha)\n\tif (metadata?.branch) formData.set('branch', metadata.branch)\n\tif (metadata?.message) formData.set('message', metadata.message)\n\tif (metadata?.triggeredBy) formData.set('triggeredBy', metadata.triggeredBy)\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; funnelId: string; created: boolean; activated: boolean; dashboardUrl: string; message: 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'Remove funnelId from config to create a new headless funnel.'\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 { cpSync, existsSync, readFileSync, writeFileSync, readdirSync } from 'node:fs'\nimport { join, dirname } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport pc from 'picocolors'\nimport select from '@inquirer/select'\nimport input from '@inquirer/input'\nimport * as log from '../lib/logger.js'\nimport { requireAuth } from '../lib/auth.js'\nimport { fetchProjects, promptForProject } from '../lib/projects.js'\nimport { fetchStores, fetchStorePrices, type Store, type StorePrice } from '../lib/api.js'\n\nconst __dirname = dirname(fileURLToPath(import.meta.url))\n\n/* ---------- Template types ---------- */\n\ninterface TemplateProduct {\n\tid: string\n\tlabel: string\n\tdescription?: string\n\tpaidTrial?: boolean\n}\n\ninterface TemplateConfig {\n\tname: string\n\tdescription: string\n\tproducts: TemplateProduct[]\n}\n\n/* ---------- Helpers ---------- */\n\nfunction getTemplatesDir(): string {\n\tconst dir = join(__dirname, '..', 'templates')\n\tif (!existsSync(dir)) {\n\t\tthrow new Error(`Templates directory not found at ${dir}`)\n\t}\n\treturn dir\n}\n\nfunction listTemplates(): Array<{ dir: string; config: TemplateConfig }> {\n\tconst root = getTemplatesDir()\n\treturn readdirSync(root, { withFileTypes: true })\n\t\t.filter((d) => d.isDirectory())\n\t\t.map((d) => {\n\t\t\tconst configPath = join(root, d.name, 'template.json')\n\t\t\tconst config: TemplateConfig = existsSync(configPath)\n\t\t\t\t? JSON.parse(readFileSync(configPath, 'utf-8'))\n\t\t\t\t: { name: d.name, description: '', products: [] }\n\t\t\treturn { dir: d.name, config }\n\t\t})\n}\n\nconst INTERVAL_LABELS: Record<string, Record<number, string>> = {\n\tday: { 1: 'day', 7: 'week', 14: '2 weeks', 30: 'month' },\n\tweek: { 1: 'week', 2: '2 weeks', 4: 'month', 12: 'quarter', 52: 'year' },\n\tmonth: { 1: 'month', 3: 'quarter', 6: '6 months', 12: 'year' },\n\tyear: { 1: 'year' },\n}\n\nfunction formatInterval(interval: string | null, count: number): string {\n\tif (!interval) return ' one-time'\n\tconst label = INTERVAL_LABELS[interval]?.[count]\n\tif (label) return `/${label}`\n\treturn `/${count} ${interval}${count > 1 ? 's' : ''}`\n}\n\nfunction formatPrice(price: StorePrice): string {\n\tconst amount = (price.amount / 100).toFixed(2)\n\tconst currency = price.currency.toUpperCase()\n\tconst interval = formatInterval(price.interval, price.intervalCount)\n\n\tconst productName = price.name || 'Unnamed product'\n\tconst priceName = price.priceName ? ` — ${price.priceName}` : ''\n\n\treturn `${currency} ${amount}${interval} ${pc.dim(`${productName}${priceName}`)} ${pc.dim(`(${price.id})`)}`\n}\n\nfunction formatStoreName(store: Store): string {\n\tconst test = store.isTestMode ? pc.yellow(' (test)') : ''\n\treturn `${store.name || store.type}${test}`\n}\n\n/* ---------- Main ---------- */\n\nexport async function initCommand(nameArg?: string): Promise<void> {\n\tconst creds = requireAuth()\n\tconst apiOpts = { token: creds.token }\n\n\t// 1. Use provided name or ask interactively\n\tconst name = nameArg?.trim() || await input({\n\t\tmessage: 'Funnel name',\n\t\tvalidate: (value) => {\n\t\t\tif (!value.trim()) return 'Name is required'\n\t\t\tif (!/^[a-z0-9-]+$/.test(value.trim()))\n\t\t\t\treturn 'Use lowercase letters, numbers, and hyphens only'\n\t\t\tif (existsSync(join(process.cwd(), value.trim())))\n\t\t\t\treturn `Directory '${value.trim()}' already exists`\n\t\t\treturn true\n\t\t},\n\t})\n\n\t// Validate name if provided as argument\n\tif (nameArg) {\n\t\tif (!/^[a-z0-9-]+$/.test(name))\n\t\t\tthrow new Error('Name must be lowercase letters, numbers, and hyphens only')\n\t\tif (existsSync(join(process.cwd(), name)))\n\t\t\tthrow new Error(`Directory '${name}' already exists`)\n\t}\n\n\tconst dir = join(process.cwd(), name.trim())\n\n\t// 2. Select project\n\tconst projectId = await promptForProject(creds.token)\n\tconst projects = await fetchProjects(creds.token)\n\tconst project = projects.find((p) => p.id === projectId)!\n\n\t// 3. Select template\n\tconst templates = listTemplates()\n\tconst selectedDir = await select({\n\t\tmessage: 'Choose a template',\n\t\tchoices: templates.map((t) => ({\n\t\t\tname: t.config.description\n\t\t\t\t? `${t.config.name} — ${pc.dim(t.config.description)}`\n\t\t\t\t: t.config.name,\n\t\t\tvalue: t.dir,\n\t\t})),\n\t})\n\tconst chosen = templates.find((t) => t.dir === selectedDir)!\n\n\tconst templateConfig = chosen.config\n\tconst templateDir = join(getTemplatesDir(), chosen.dir)\n\n\t// 3. If template has products, select store + bind prices\n\tconst productBindings: Array<{\n\t\tid: string\n\t\tname: string\n\t\tstorePriceId: string\n\t\ttrialDays?: number\n\t\ttrialStorePriceId?: string\n\t}> = []\n\n\tif (templateConfig.products.length > 0) {\n\t\t// Fetch stores\n\t\tconst storesSpinner = log.spinner('Fetching stores...')\n\t\tconst stores = await fetchStores(projectId, apiOpts)\n\t\tstoresSpinner.stop()\n\n\t\tif (stores.length === 0) {\n\t\t\tlog.warn('No stores found for this project.')\n\t\t\tlog.info('Products will use empty IDs — create a store in the dashboard and update appfunnel.config.ts.')\n\t\t} else {\n\t\t\t// Select store\n\t\t\tlet store: Store\n\t\t\tif (stores.length === 1) {\n\t\t\t\tstore = stores[0]\n\t\t\t\tlog.info(`Using store: ${formatStoreName(store)}`)\n\t\t\t} else {\n\t\t\t\tconst storeId = await select({\n\t\t\t\t\tmessage: 'Choose a store',\n\t\t\t\t\tchoices: stores.map((s) => ({\n\t\t\t\t\t\tname: formatStoreName(s),\n\t\t\t\t\t\tvalue: s.id,\n\t\t\t\t\t})),\n\t\t\t\t})\n\t\t\t\tstore = stores.find((s) => s.id === storeId)!\n\t\t\t}\n\n\t\t\t// Fetch prices from selected store\n\t\t\tconst pricesSpinner = log.spinner('Fetching prices...')\n\t\t\tconst prices = await fetchStorePrices(projectId, store.id, apiOpts)\n\t\t\tpricesSpinner.stop()\n\n\t\t\tif (prices.length === 0) {\n\t\t\t\tlog.warn('No prices found in this store.')\n\t\t\t\tlog.info('Import prices in the dashboard, then update appfunnel.config.ts.')\n\t\t\t} else {\n\t\t\t\tconsole.log()\n\t\t\t\tlog.info(`Configuring ${templateConfig.products.length} product(s)...`)\n\t\t\t\tconsole.log()\n\n\t\t\t\tfor (const product of templateConfig.products) {\n\t\t\t\t\tif (product.description) {\n\t\t\t\t\t\tconsole.log(` ${pc.dim(product.description)}`)\n\t\t\t\t\t}\n\n\t\t\t\t\t// Select main price\n\t\t\t\t\tconst storePriceId = await select({\n\t\t\t\t\t\tmessage: `${product.label} — select a price`,\n\t\t\t\t\t\tchoices: prices.map((p) => ({\n\t\t\t\t\t\t\tname: formatPrice(p),\n\t\t\t\t\t\t\tvalue: p.id,\n\t\t\t\t\t\t})),\n\t\t\t\t\t})\n\n\t\t\t\t\tconst binding: (typeof productBindings)[number] = {\n\t\t\t\t\t\tid: product.id,\n\t\t\t\t\t\tname: product.id,\n\t\t\t\t\t\tstorePriceId,\n\t\t\t\t\t}\n\n\t\t\t\t\t// Select trial price if needed\n\t\t\t\t\tif (product.paidTrial) {\n\t\t\t\t\t\tconst trialStorePriceId = await select({\n\t\t\t\t\t\t\tmessage: `${product.label} — select a trial price`,\n\t\t\t\t\t\t\tchoices: [\n\t\t\t\t\t\t\t\t...prices.map((p) => ({\n\t\t\t\t\t\t\t\t\tname: formatPrice(p),\n\t\t\t\t\t\t\t\t\tvalue: p.id,\n\t\t\t\t\t\t\t\t})),\n\t\t\t\t\t\t\t\t{ name: pc.dim('Skip (no trial)'), value: '' },\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t})\n\n\t\t\t\t\t\tif (trialStorePriceId) {\n\t\t\t\t\t\t\tbinding.trialStorePriceId = trialStorePriceId\n\n\t\t\t\t\t\t\tconst trialDaysStr = await select({\n\t\t\t\t\t\t\t\tmessage: `${product.label} — trial duration`,\n\t\t\t\t\t\t\t\tchoices: [\n\t\t\t\t\t\t\t\t\t{ name: '3 days', value: '3' },\n\t\t\t\t\t\t\t\t\t{ name: '7 days', value: '7' },\n\t\t\t\t\t\t\t\t\t{ name: '14 days', value: '14' },\n\t\t\t\t\t\t\t\t\t{ name: '30 days', value: '30' },\n\t\t\t\t\t\t\t\t\t{ name: '90 days', value: '90' },\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\tbinding.trialDays = parseInt(trialDaysStr, 10)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tproductBindings.push(binding)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// 4. Copy template\n\tconst s = log.spinner(`Creating ${name}...`)\n\n\tcpSync(templateDir, dir, { recursive: true })\n\n\t// Remove template.json from output — it's not part of the user's project\n\tconst templateJsonPath = join(dir, 'template.json')\n\tif (existsSync(templateJsonPath)) {\n\t\tconst { unlinkSync } = await import('node:fs')\n\t\tunlinkSync(templateJsonPath)\n\t}\n\n\t// npm strips .gitignore from published packages, so we ship it as 'gitignore' and rename here\n\tconst gitignoreSrc = join(dir, 'gitignore')\n\tif (existsSync(gitignoreSrc)) {\n\t\tconst { renameSync } = await import('node:fs')\n\t\trenameSync(gitignoreSrc, join(dir, '.gitignore'))\n\t}\n\n\t// 5. Replace placeholders in README.md\n\tconst readmePath = join(dir, 'README.md')\n\tif (existsSync(readmePath)) {\n\t\tlet readme = readFileSync(readmePath, 'utf-8')\n\t\treadme = readme.replace('__NAME__', name)\n\t\twriteFileSync(readmePath, readme)\n\t}\n\n\t// 6. Replace placeholders in appfunnel.config.ts\n\tconst configPath = join(dir, 'appfunnel.config.ts')\n\tif (existsSync(configPath)) {\n\t\tlet config = readFileSync(configPath, 'utf-8')\n\t\tconfig = config.replace('__PROJECT_ID__', projectId)\n\t\tconfig = config.replace('__NAME__', name)\n\n\t\t// If we have product bindings, replace the products section\n\t\tif (productBindings.length > 0) {\n\t\t\tconst itemsStr = productBindings\n\t\t\t\t.map((b) => {\n\t\t\t\t\tconst fields = [\n\t\t\t\t\t\t`\\t\\t\\t{ id: '${b.id}'`,\n\t\t\t\t\t\t`name: '${b.name}'`,\n\t\t\t\t\t\t`storePriceId: '${b.storePriceId}'`,\n\t\t\t\t\t]\n\t\t\t\t\tif (b.trialDays) fields.push(`trialDays: ${b.trialDays}`)\n\t\t\t\t\tif (b.trialStorePriceId)\n\t\t\t\t\t\tfields.push(`trialStorePriceId: '${b.trialStorePriceId}'`)\n\t\t\t\t\treturn fields.join(', ') + ' }'\n\t\t\t\t})\n\t\t\t\t.join(',\\n')\n\n\t\t\tconst defaultId = productBindings[0].id\n\t\t\tconfig = config.replace(\n\t\t\t\t/products:\\s*\\{[^}]*items:\\s*\\[[^\\]]*\\][^}]*\\}/s,\n\t\t\t\t`products: {\\n\\t\\titems: [\\n${itemsStr},\\n\\t\\t],\\n\\t\\tdefaultId: '${defaultId}',\\n\\t}`\n\t\t\t)\n\t\t}\n\n\t\twriteFileSync(configPath, config)\n\t}\n\n\t// 6. Generate package.json\n\tconst sdkVersion = `^${__CLI_VERSION__}`\n\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-dev/sdk': sdkVersion,\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: sdkVersion,\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\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 VariableConfig {\n type: string\n default?: unknown\n persist?: boolean\n}\n\nexport interface AppFunnelConfig {\n projectId: string\n name: string\n funnelId?: string\n initialPageKey: string\n defaultLocale?: string\n /** Response variables — stored as answers.* */\n responses?: Record<string, VariableConfig>\n /** Query param variables — stored as query.* */\n queryParams?: Record<string, VariableConfig>\n /** Data variables — stored as data.* */\n data?: Record<string, VariableConfig>\n products?: {\n items: Array<{ id: string; name: string; storePriceId: string; trialDays?: number; trialStorePriceId?: string }>\n defaultId?: string\n }\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-dev\\/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) {\n throw new CLIError(\n 'CONFIG_NOT_FOUND',\n `Invalid config in ${CONFIG_FILE}.`,\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\tversion: string\n}\n\n/**\n * Check that CLI major.minor matches the installed @appfunnel-dev/sdk version.\n */\nexport function checkVersionCompatibility(cwd: string): void {\n\tconst cliVersion = __CLI_VERSION__\n\tconst sdkVersion = getSdkVersion(cwd)\n\n\tconst [cliMajor, cliMinor] = cliVersion.split('.').map(Number)\n\tconst [sdkMajor, sdkMinor] = sdkVersion.split('.').map(Number)\n\n\tif (cliMajor !== sdkMajor || cliMinor !== sdkMinor) {\n\t\tthrow new CLIError(\n\t\t\t'VERSION_MISMATCH',\n\t\t\t`CLI version ${cliVersion} requires @appfunnel-dev/sdk ^${cliMajor}.${cliMinor}.0, but found ${sdkVersion}.`,\n\t\t\t\"Run 'npm install @appfunnel-dev/sdk@latest' to update.\"\n\t\t)\n\t}\n}\n\nfunction getSdkVersion(cwd: string): string {\n\ttry {\n\t\tconst pkgPath = join(\n\t\t\tcwd,\n\t\t\t'node_modules',\n\t\t\t'@appfunnel-dev',\n\t\t\t'sdk',\n\t\t\t'package.json'\n\t\t)\n\t\tconst pkg = JSON.parse(readFileSync(pkgPath, 'utf-8')) as PackageJson\n\t\treturn pkg.version\n\t} catch {\n\t\tthrow new CLIError(\n\t\t\t'VERSION_MISMATCH',\n\t\t\t'@appfunnel-dev/sdk is not installed.',\n\t\t\t\"Run 'npm install @appfunnel-dev/sdk' to install it.\"\n\t\t)\n\t}\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 { existsSync } from 'node:fs'\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 priceData?: Map<string, unknown>\n translations?: Record<string, Record<string, string>>\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 the SDK's Router\n * - Lazy-loads page components\n */\nexport function generateEntrySource(options: EntryOptions): string {\n const { config, pages, pagesDir, funnelTsxPath, isDev, translations } = 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 // Serialize price data as a Map constructor call\n // In production, runtime data (prices, integrations, etc.) is injected via window.__APPFUNNEL_DATA__\n const priceDataCode = isDev\n ? (options.priceData && options.priceData.size > 0\n ? `const priceData = new Map(${JSON.stringify([...options.priceData.entries()])});`\n : `const priceData = undefined;`)\n : `const priceData = (() => {\n const rd = typeof window !== 'undefined' && window.__APPFUNNEL_DATA__;\n if (!rd || !rd.products?.items) return undefined;\n // Build price map from server-injected product data\n const map = new Map();\n for (const item of rd.products.items) {\n if (item.priceData && item.storePriceId) map.set(item.storePriceId, item.priceData);\n if (item.trialPriceData && item.trialStorePriceId) map.set(item.trialStorePriceId, item.trialPriceData);\n }\n return map.size > 0 ? map : undefined;\n})();`\n\n const trackingCode = isDev\n ? `\n// Dev mode: mock tracking — log events to console\nglobalThis.__APPFUNNEL_DEV__ = true;\n`\n : ''\n\n // Import app.css if it exists (Tailwind, global styles, etc.)\n const appCssPath = join(pagesDir, '..', 'app.css').replace(/\\\\/g, '/')\n const hasAppCss = existsSync(join(pagesDir, '..', 'app.css'))\n\n return `\nimport { StrictMode, Component, lazy, Suspense, useState, useCallback, useEffect, useTransition, useDeferredValue, useSyncExternalStore } from 'react'\nimport { createRoot } from 'react-dom/client'\nimport { FunnelProvider, useNavigation } from '@appfunnel-dev/sdk'\n${hasAppCss ? `import '${appCssPath}'` : ''}\nimport FunnelWrapper from '${funnelTsxPath.replace(/\\\\/g, '/')}'\n\n${trackingCode}\n\nconst pageComponents = {\n${pageImports}\n}\n\n${priceDataCode}\n\nconst translations = ${translations ? JSON.stringify(translations) : 'undefined'}\n\nconst config = ${JSON.stringify(fullConfig, null, 2)}\n\nconst keyToSlug = ${JSON.stringify(\n Object.fromEntries(Object.entries(slugMap).map(([s, k]) => [k, s])),\n )}\nconst slugToKey = ${JSON.stringify(slugMap)}\n\nconst DEV_CAMPAIGN_SLUG = 'campaign'\nconst DEFAULT_INITIAL = '${config.initialPageKey}'\n\n/**\n * Parse the URL to extract basePath, campaignSlug, and initial page.\n *\n * URL pattern: /f/<campaignSlug>[/<pageSlug>]\n *\n * In dev, redirects bare / to /f/<projectId> so the URL matches production.\n */\nfunction parseUrl() {\n const parts = window.location.pathname.split('/').filter(Boolean)\n\n // /f/<campaignSlug>[/<pageSlug>]\n if (parts[0] === 'f' && parts.length >= 2) {\n const campaignSlug = parts[1]\n const pageSlug = parts[2] || ''\n const pageKey = pageSlug ? (slugToKey[pageSlug] || '') : ''\n return {\n basePath: '/f/' + campaignSlug,\n campaignSlug,\n initialPage: pageKey || DEFAULT_INITIAL,\n }\n }\n\n // Bare URL → redirect to /f/<slug> in dev\n ${isDev ? `\n window.history.replaceState(null, '', '/f/' + DEV_CAMPAIGN_SLUG)\n return {\n basePath: '/f/' + DEV_CAMPAIGN_SLUG,\n campaignSlug: DEV_CAMPAIGN_SLUG,\n initialPage: DEFAULT_INITIAL,\n }` : `\n return {\n basePath: '',\n campaignSlug: '',\n initialPage: DEFAULT_INITIAL,\n }`}\n}\n\nconst { basePath, campaignSlug, initialPage } = parseUrl()\n\n${isDev ? `\nclass ErrorBoundary extends Component {\n constructor(props) {\n super(props)\n this.state = { error: null }\n }\n static getDerivedStateFromError(error) {\n return { error }\n }\n componentDidCatch(error, info) {\n console.error('[AppFunnel] Render error:', error, info)\n }\n render() {\n if (this.state.error) {\n return (\n <div style={{ padding: '2rem', fontFamily: 'monospace' }}>\n <h2 style={{ color: 'red' }}>AppFunnel Error</h2>\n <pre style={{ whiteSpace: 'pre-wrap', color: '#333' }}>{this.state.error.message}</pre>\n <pre style={{ whiteSpace: 'pre-wrap', color: '#666', fontSize: '12px' }}>{this.state.error.stack}</pre>\n </div>\n )\n }\n return this.props.children\n }\n}\n` : ''}\n\n/**\n * PageRenderer lives inside FunnelProvider so it can use SDK hooks.\n * Subscribes to the router — re-renders when the page changes.\n * Uses useTransition to keep showing the current page while the next one loads.\n */\nfunction PageRenderer() {\n const { currentPage, goToPage } = useNavigation()\n const routerPageKey = currentPage?.key || ''\n\n // Track the displayed page separately so we can transition smoothly\n const [displayedKey, setDisplayedKey] = useState(routerPageKey)\n const [isPending, startTransition] = useTransition()\n\n // When the router's page changes, transition to the new page\n useEffect(() => {\n if (routerPageKey && routerPageKey !== displayedKey) {\n startTransition(() => {\n setDisplayedKey(routerPageKey)\n })\n }\n }, [routerPageKey, displayedKey])\n\n // Sync URL with current page\n const slug = currentPage?.slug || routerPageKey\n useEffect(() => {\n const expectedPath = basePath ? basePath + '/' + slug : '/' + slug\n if (slug && window.location.pathname !== expectedPath) {\n window.history.pushState(null, '', expectedPath)\n }\n }, [slug])\n\n // Handle browser back/forward\n useEffect(() => {\n const handlePopState = () => {\n const path = window.location.pathname.split('/').filter(Boolean).pop() || ''\n const key = slugToKey[path]\n if (key && key !== routerPageKey) {\n goToPage(key)\n }\n }\n window.addEventListener('popstate', handlePopState)\n return () => window.removeEventListener('popstate', handlePopState)\n }, [routerPageKey, goToPage])\n\n const PageComponent = pageComponents[displayedKey]\n\n if (!PageComponent) {\n return <div style={{ padding: '2rem', color: 'red' }}>Page not found: {displayedKey}</div>\n }\n\n return (\n <Suspense fallback={null}>\n <PageComponent />\n </Suspense>\n )\n}\n\n// Runtime data injected by the server (production only)\nconst __rd = typeof window !== 'undefined' && window.__APPFUNNEL_DATA__\n\nfunction App() {\n // In production, merge server-injected integrations into config\n const runtimeConfig = __rd && __rd.integrations\n ? { ...config, integrations: { ...config.integrations, ...__rd.integrations } }\n : config\n\n const sessionData = __rd ? {\n campaignId: __rd.campaignId || '',\n funnelId: __rd.funnelId || config.funnelId || '',\n experimentId: __rd.experimentId || null,\n } : undefined\n\n return (\n <FunnelProvider\n config={runtimeConfig}\n initialPage={initialPage}\n basePath={basePath}\n campaignSlug={campaignSlug}\n priceData={priceData}\n sessionData={sessionData}\n translations={translations}\n >\n <FunnelWrapper>\n <PageRenderer />\n </FunnelWrapper>\n </FunnelProvider>\n )\n}\n\ncreateRoot(document.getElementById('root')).render(\n <StrictMode>\n ${isDev ? '<ErrorBoundary>' : ''}\n <App />\n ${isDev ? '</ErrorBoundary>' : ''}\n </StrictMode>\n)\n\n// Reveal body (the host page may set opacity:0 for a loading transition)\ndocument.body.style.opacity = '1'\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, writeFileSync, mkdirSync, readFileSync, readdirSync } 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 + '.tsx'\n\n/** Working directory for dev/build artifacts (like .next/) */\nconst APPFUNNEL_DIR = '.appfunnel'\n\ninterface PluginOptions {\n cwd: string\n config: AppFunnelConfig\n pages: Record<string, PageDefinition>\n isDev: boolean\n priceData?: Map<string, unknown>\n onPagesChange?: () => Promise<void>\n}\n\n/**\n * Load all locale JSON files from <cwd>/locales/.\n * Returns a TranslationMap: { \"en\": { \"key\": \"value\" }, \"de\": { ... } }\n */\nfunction loadTranslations(cwd: string): Record<string, Record<string, string>> | undefined {\n const localesDir = join(cwd, 'locales')\n if (!existsSync(localesDir)) return undefined\n\n const translations: Record<string, Record<string, string>> = {}\n let hasAny = false\n\n for (const file of readdirSync(localesDir)) {\n if (!file.endsWith('.json')) continue\n const locale = file.replace(/\\.json$/, '')\n try {\n const content = readFileSync(join(localesDir, file), 'utf-8')\n translations[locale] = JSON.parse(content)\n hasAny = true\n } catch {\n // Skip invalid JSON files\n }\n }\n\n return hasAny ? translations : undefined\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 const appfunnelDir = join(cwd, APPFUNNEL_DIR)\n const htmlPath = join(appfunnelDir, 'index.html')\n const localesDir = join(cwd, 'locales')\n\n function getEntrySource(): string {\n return generateEntrySource({\n config,\n pages,\n pagesDir,\n funnelTsxPath,\n isDev,\n priceData: options.priceData,\n translations: loadTranslations(cwd),\n })\n }\n\n return {\n name: 'appfunnel',\n\n config() {\n // Ensure .appfunnel/ exists and write the HTML shell\n mkdirSync(appfunnelDir, { recursive: true })\n writeFileSync(htmlPath, generateHtml(config.name || 'AppFunnel'))\n\n return {\n // Don't let Vite auto-serve index.html — we handle it ourselves\n appType: 'custom',\n resolve: {\n alias: {\n '@': resolve(cwd, 'src'),\n },\n dedupe: ['react', 'react-dom'],\n },\n esbuild: {\n jsx: 'automatic',\n },\n optimizeDeps: {\n include: ['react', 'react-dom', 'react/jsx-runtime'],\n force: true,\n },\n }\n },\n\n resolveId(id) {\n if (id === VIRTUAL_ENTRY_ID || id === '/' + VIRTUAL_ENTRY_ID) {\n return RESOLVED_VIRTUAL_ENTRY_ID\n }\n return null\n },\n\n async load(id) {\n if (id === RESOLVED_VIRTUAL_ENTRY_ID) {\n const { transform } = await import('esbuild')\n const source = getEntrySource()\n const result = await transform(source, {\n loader: 'tsx',\n jsx: 'automatic',\n sourcefile: 'appfunnel-entry.tsx',\n })\n return result.code\n }\n return null\n },\n\n configureServer(devServer) {\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 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 // Watch locales/ for changes → full reload\n if (existsSync(localesDir)) {\n watcher.add(localesDir)\n watcher.on('change', (file) => {\n if (file.startsWith(localesDir) && file.endsWith('.json')) {\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 }\n\n // Since appType is 'custom', we serve HTML ourselves.\n // Registered after Vite internals so /@vite, /node_modules etc. resolve first.\n return () => {\n devServer.middlewares.use(async (req, res, next) => {\n const url = req.url?.split('?')[0] || ''\n\n // Let Vite handle static assets and internal routes\n if (url.includes('.') || url.startsWith('/@') || url.startsWith('/node_modules')) {\n return next()\n }\n\n try {\n const rawHtml = readFileSync(htmlPath, 'utf-8')\n const html = await devServer.transformIndexHtml(req.url || '/', rawHtml)\n res.statusCode = 200\n res.setHeader('Content-Type', 'text/html')\n res.end(html)\n } catch (err) {\n next(err)\n }\n })\n }\n },\n\n transformIndexHtml(html) {\n return html\n },\n }\n}\n","import { readFileSync, writeFileSync } from 'node:fs'\nimport { join } 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'\nimport { promptForProject } from '../lib/projects.js'\nimport { fetchPrices } from '../lib/api.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 const creds = 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 s.stop()\n\n // 4. If no projectId, prompt for one and update the config file\n if (!config.projectId) {\n log.info('No projectId found in appfunnel.config.ts')\n const projectId = await promptForProject(creds.token)\n config.projectId = projectId\n\n // Write projectId back to the config file\n const configPath = join(cwd, 'appfunnel.config.ts')\n const configSource = readFileSync(configPath, 'utf-8')\n let updated: string\n\n if (/projectId:\\s*['\"]/.test(configSource)) {\n // Replace existing projectId value\n updated = configSource.replace(\n /projectId:\\s*['\"].*?['\"]/,\n `projectId: '${projectId}'`,\n )\n } else {\n // Insert projectId after the opening of defineConfig({\n updated = configSource.replace(\n /(defineConfig\\(\\{[\\t ]*\\n)/,\n `$1\\tprojectId: '${projectId}',\\n`,\n )\n }\n\n if (updated !== configSource) {\n writeFileSync(configPath, updated)\n log.success(`Updated projectId in appfunnel.config.ts`)\n } else {\n log.warn(`Could not auto-update appfunnel.config.ts — add projectId: '${projectId}' manually.`)\n }\n }\n\n // 5. Scan and extract pages\n const s2 = log.spinner('Scanning pages...')\n let pageKeys = scanPages(cwd)\n let pages = await extractPageDefinitions(cwd, pageKeys)\n s2.stop()\n\n log.info(`Found ${pageKeys.length} pages: ${pageKeys.join(', ')}`)\n\n // 6. Fetch store prices (cached for dev session)\n let priceData: Map<string, unknown> = new Map()\n if (config.projectId && config.products?.items?.length) {\n try {\n const storePriceIds = [\n ...new Set(\n config.products.items.flatMap((item) =>\n [item.storePriceId, item.trialStorePriceId].filter(Boolean) as string[]\n )\n ),\n ]\n\n log.info(`Fetching ${storePriceIds.length} store prices: ${storePriceIds.join(', ')}`)\n\n const s3 = log.spinner('Fetching store prices...')\n priceData = await fetchPrices(config.projectId, storePriceIds, { token: creds.token })\n s3.stop()\n\n // Check that every requested price was returned\n const missingIds = storePriceIds.filter((id) => !priceData.has(id))\n if (missingIds.length > 0) {\n log.error(`Missing store prices: ${missingIds.join(', ')}`)\n log.error('Make sure these storePriceId values in your config match prices in your project.')\n process.exit(1)\n }\n\n log.success(`Fetched ${priceData.size}/${storePriceIds.length} store prices`)\n } catch (err) {\n log.error(`Failed to fetch store prices: ${(err as Error).message}`)\n process.exit(1)\n }\n }\n\n // 7. Start Vite dev server\n const { createServer } = await import('vite')\n const react = await import('@vitejs/plugin-react')\n\n // Load @tailwindcss/vite from the user's project\n let tailwindPlugin: any = null\n try {\n const { createRequire } = await import('node:module')\n const require = createRequire(join(cwd, 'package.json'))\n const tailwindPath = require.resolve('@tailwindcss/vite')\n const tailwindVite = await import(tailwindPath)\n tailwindPlugin = tailwindVite.default\n } catch {\n // Not installed — skip\n }\n\n const server = await createServer({\n root: cwd,\n server: {\n port,\n strictPort: false,\n },\n plugins: [\n react.default(),\n ...(tailwindPlugin ? [tailwindPlugin()] : []),\n appfunnelPlugin({\n cwd,\n config,\n pages,\n isDev: true,\n priceData,\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 type { PageDefinition } from '../extract/pages.js'\n\ninterface SharedSegment {\n type: 'shared'\n pageKeys: string[]\n}\n\ninterface BranchSegment {\n type: 'branch'\n sourcePageKey: string\n arms: { pageKeys: string[] }[]\n}\n\nexport type FlowSegment = SharedSegment | BranchSegment\n\nexport interface ComputedFunnelFlow {\n initialPageKey: string\n segments: FlowSegment[]\n terminalPageKeys: string[]\n unreachablePageKeys: string[]\n}\n\n/**\n * Compute the segment-based flow through a funnel.\n *\n * Produces shared segments (linear page sequences) and branch segments\n * (where routes diverge, with variable-length arms that reconverge).\n *\n * Ported from api/src/utils/computeFunnelPaths.ts, adapted for CLI PageDefinition format.\n */\nexport function computeFunnelFlow(\n pages: Record<string, PageDefinition>,\n initialPageKey: string,\n): ComputedFunnelFlow {\n const allPageKeys = new Set(Object.keys(pages))\n\n if (allPageKeys.size === 0) {\n return { initialPageKey, segments: [], terminalPageKeys: [], unreachablePageKeys: [] }\n }\n\n // Build adjacency: pageKey → unique ordered target pageKeys\n function getTargets(pageKey: string): string[] {\n const routes = pages[pageKey]?.routes || []\n const seen = new Set<string>()\n const targets: string[] = []\n for (const route of routes) {\n if (allPageKeys.has(route.to) && !seen.has(route.to)) {\n seen.add(route.to)\n targets.push(route.to)\n }\n }\n return targets\n }\n\n // BFS from initial page to find reachable pages\n const reachable = new Set<string>()\n {\n const queue: string[] = []\n if (allPageKeys.has(initialPageKey)) {\n reachable.add(initialPageKey)\n queue.push(initialPageKey)\n }\n while (queue.length > 0) {\n const pageKey = queue.shift()!\n for (const target of getTargets(pageKey)) {\n if (!reachable.has(target)) {\n reachable.add(target)\n queue.push(target)\n }\n }\n }\n }\n\n const unreachablePageKeys: string[] = []\n const terminalPageKeys: string[] = []\n\n for (const pageKey of allPageKeys) {\n if (!reachable.has(pageKey)) {\n unreachablePageKeys.push(pageKey)\n continue\n }\n if (getTargets(pageKey).length === 0) {\n terminalPageKeys.push(pageKey)\n }\n }\n\n // BFS forward from a page to collect all reachable pageKeys with distances\n function bfsForward(start: string): Map<string, number> {\n const dist = new Map<string, number>()\n dist.set(start, 0)\n const queue = [start]\n while (queue.length > 0) {\n const current = queue.shift()!\n const d = dist.get(current)!\n for (const target of getTargets(current)) {\n if (!dist.has(target)) {\n dist.set(target, d + 1)\n queue.push(target)\n }\n }\n }\n return dist\n }\n\n // Find convergence page for branch arms\n function findConvergence(armStarts: string[]): string | null {\n if (armStarts.length === 0) return null\n\n const armReachable = armStarts.map((s) => bfsForward(s))\n const armStartSet = new Set(armStarts)\n\n let candidates = new Set<string>()\n for (const key of armReachable[0].keys()) {\n if (!armStartSet.has(key)) candidates.add(key)\n }\n for (let i = 1; i < armReachable.length; i++) {\n const reachSet = new Set(armReachable[i].keys())\n candidates = new Set([...candidates].filter((k) => reachSet.has(k)))\n }\n\n if (candidates.size === 0) return null\n\n let bestPage: string | null = null\n let bestMinDist = Infinity\n for (const pageKey of candidates) {\n const minDist = Math.min(...armReachable.map((r) => r.get(pageKey) ?? Infinity))\n if (minDist < bestMinDist) {\n bestMinDist = minDist\n bestPage = pageKey\n }\n }\n\n return bestPage\n }\n\n // Walk a branch arm from start to convergence\n function walkArm(start: string, convergence: string | null, globalVisited: Set<string>): string[] {\n if (convergence === null) {\n const pages: string[] = []\n let current = start\n while (true) {\n if (globalVisited.has(current)) break\n globalVisited.add(current)\n pages.push(current)\n const targets = getTargets(current)\n if (targets.length === 0) break\n const next = targets.find((t) => !globalVisited.has(t))\n if (!next) break\n current = next\n }\n return pages\n }\n\n // BFS from start to convergence\n const parent = new Map<string, string | null>()\n parent.set(start, null)\n const queue = [start]\n let found = false\n\n while (queue.length > 0 && !found) {\n const current = queue.shift()!\n for (const target of getTargets(current)) {\n if (!parent.has(target)) {\n parent.set(target, current)\n if (target === convergence) {\n found = true\n break\n }\n queue.push(target)\n }\n }\n }\n\n if (!found) {\n const pages: string[] = []\n let current = start\n while (true) {\n if (globalVisited.has(current)) break\n globalVisited.add(current)\n pages.push(current)\n const targets = getTargets(current)\n if (targets.length === 0) break\n const next = targets.find((t) => !globalVisited.has(t))\n if (!next) break\n current = next\n }\n return pages\n }\n\n const path: string[] = []\n let node: string | null = convergence\n while (node !== null && node !== start) {\n path.unshift(node)\n node = parent.get(node) ?? null\n }\n path.unshift(start)\n if (path[path.length - 1] === convergence) {\n path.pop()\n }\n\n for (const p of path) {\n globalVisited.add(p)\n }\n\n return path\n }\n\n // --- Main walk: build segments ---\n if (!allPageKeys.has(initialPageKey)) {\n return { initialPageKey, segments: [], terminalPageKeys, unreachablePageKeys }\n }\n\n const segments: FlowSegment[] = []\n const globalVisited = new Set<string>()\n let current: string | null = initialPageKey\n let sharedPages: string[] = []\n\n while (current !== null && !globalVisited.has(current)) {\n const targets = getTargets(current)\n\n if (targets.length === 0) {\n sharedPages.push(current)\n globalVisited.add(current)\n break\n }\n\n const uniqueTargets = [...new Set(targets)]\n\n if (uniqueTargets.length === 1) {\n sharedPages.push(current)\n globalVisited.add(current)\n current = uniqueTargets[0]\n continue\n }\n\n // Branch point\n sharedPages.push(current)\n globalVisited.add(current)\n segments.push({ type: 'shared', pageKeys: [...sharedPages] })\n sharedPages = []\n\n const convergence = findConvergence(uniqueTargets)\n\n const arms: { pageKeys: string[] }[] = []\n for (const armStart of uniqueTargets) {\n const armPages = walkArm(armStart, convergence, globalVisited)\n arms.push({ pageKeys: armPages })\n }\n\n segments.push({ type: 'branch', sourcePageKey: current, arms })\n\n if (convergence !== null && !globalVisited.has(convergence)) {\n current = convergence\n } else {\n current = null\n }\n }\n\n if (sharedPages.length > 0) {\n segments.push({ type: 'shared', pageKeys: sharedPages })\n }\n\n return { initialPageKey, segments, terminalPageKeys, unreachablePageKeys }\n}\n","import pc from 'picocolors'\nimport * as log from '../lib/logger.js'\nimport { loadConfig } from '../lib/config.js'\nimport { scanPages, extractPageDefinitions, type PageDefinition } from '../extract/pages.js'\nimport { computeFunnelFlow, type FlowSegment, type ComputedFunnelFlow } from '../lib/computeFlow.js'\n\nexport async function routesCommand(): Promise<void> {\n const cwd = process.cwd()\n\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 const flow = computeFunnelFlow(pages, config.initialPageKey)\n\n console.log()\n console.log(` ${pc.bold(config.name)}`)\n console.log(` ${pc.dim(`${pageKeys.length} pages`)}`)\n console.log()\n\n renderFlow(flow, pages)\n}\n\n// ── Box-drawing characters ─────────────────────────────────────\n\nconst H = '─'\nconst V = '│'\n\nfunction renderFlow(flow: ComputedFunnelFlow, pages: Record<string, PageDefinition>): void {\n const indent = ' '\n\n for (let i = 0; i < flow.segments.length; i++) {\n const segment = flow.segments[i]\n\n if (segment.type === 'shared') {\n for (let j = 0; j < segment.pageKeys.length; j++) {\n const key = segment.pageKeys[j]\n const page = pages[key]\n const isStart = key === flow.initialPageKey\n const isTerminal = flow.terminalPageKeys.includes(key)\n printPageBox(indent, key, page, isStart, isTerminal)\n\n const isLastInSegment = j === segment.pageKeys.length - 1\n const hasNextSegment = i < flow.segments.length - 1\n if ((!isLastInSegment || hasNextSegment) && !isTerminal) {\n printArrow(indent)\n }\n }\n } else {\n renderBranch(indent, segment, pages, flow)\n\n if (i < flow.segments.length - 1) {\n printArrow(indent)\n }\n }\n }\n\n // Unreachable pages\n if (flow.unreachablePageKeys.length > 0) {\n console.log()\n console.log(` ${pc.yellow('⚠')} ${pc.yellow('Unreachable pages:')}`)\n for (const key of flow.unreachablePageKeys) {\n const page = pages[key]\n console.log(` ${pc.dim('•')} ${key}${page?.name ? pc.dim(` (${page.name})`) : ''}`)\n }\n }\n\n console.log()\n}\n\nfunction formatPageLabel(key: string, page: PageDefinition | undefined, isStart: boolean, isTerminal: boolean): string {\n const name = page?.name || key\n const type = page?.type\n const badges: string[] = []\n\n if (isStart) badges.push(pc.green('start'))\n if (isTerminal) badges.push(pc.red('end'))\n if (type && type !== 'default') badges.push(pc.magenta(type))\n\n const label = key === name.toLowerCase().replace(/\\s+/g, '-') ? name : `${name} ${pc.dim(`(${key})`)}`\n const badgeStr = badges.length > 0 ? ' ' + badges.map((b) => pc.dim('[') + b + pc.dim(']')).join(' ') : ''\n\n return label + badgeStr\n}\n\nfunction printPageBox(indent: string, key: string, page: PageDefinition | undefined, isStart: boolean, isTerminal: boolean): void {\n const label = formatPageLabel(key, page, isStart, isTerminal)\n const rawLen = stripAnsi(label).length\n const boxWidth = Math.max(rawLen + 2, 20)\n const padding = boxWidth - rawLen - 2\n\n console.log(`${indent}┌${H.repeat(boxWidth)}┐`)\n console.log(`${indent}│ ${label}${' '.repeat(padding)} │`)\n console.log(`${indent}└${H.repeat(boxWidth)}┘`)\n}\n\nfunction printArrow(indent: string): void {\n console.log(`${indent} ${V}`)\n console.log(`${indent} ▼`)\n}\n\nfunction renderBranch(\n indent: string,\n segment: Extract<FlowSegment, { type: 'branch' }>,\n pages: Record<string, PageDefinition>,\n flow: ComputedFunnelFlow,\n): void {\n const arms = segment.arms\n\n // Compute column widths for each arm\n const colWidths = arms.map((arm) => {\n const maxNameLen = Math.max(\n ...arm.pageKeys.map((key) => {\n const page = pages[key]\n const name = page?.name || key\n const type = page?.type\n // Account for type badge\n const typeLen = type && type !== 'default' ? ` [${type}]`.length : 0\n return name.length + typeLen\n }),\n 8,\n )\n return maxNameLen + 4\n })\n\n // Draw the split line: ├───────┬───────┤\n let splitLine = ''\n for (let a = 0; a < arms.length; a++) {\n const mid = Math.floor(colWidths[a] / 2)\n if (a === 0) {\n splitLine += ' '.repeat(mid) + '├' + H.repeat(colWidths[a] - mid - 1)\n } else if (a === arms.length - 1) {\n splitLine += H.repeat(mid) + '┤' + ' '.repeat(colWidths[a] - mid - 1)\n } else {\n splitLine += H.repeat(mid) + '┬' + H.repeat(colWidths[a] - mid - 1)\n }\n if (a < arms.length - 1) splitLine += H.repeat(3)\n }\n console.log(`${indent}${splitLine}`)\n\n // Draw arm down pipes\n let pipeLine = ''\n for (let a = 0; a < arms.length; a++) {\n const mid = Math.floor(colWidths[a] / 2)\n pipeLine += ' '.repeat(mid) + V + ' '.repeat(colWidths[a] - mid - 1)\n if (a < arms.length - 1) pipeLine += ' '\n }\n console.log(`${indent}${pipeLine}`)\n\n // Draw arm pages row by row\n const maxDepth = Math.max(...arms.map((a) => a.pageKeys.length))\n\n for (let row = 0; row < maxDepth; row++) {\n let line = ''\n for (let a = 0; a < arms.length; a++) {\n const key = arms[a].pageKeys[row]\n const mid = Math.floor(colWidths[a] / 2)\n if (key) {\n const page = pages[key]\n const name = page?.name || key\n const type = page?.type\n const typeBadge = type && type !== 'default' ? ' ' + pc.magenta(`[${type}]`) : ''\n const display = name + typeBadge\n const displayLen = stripAnsi(display).length\n const truncated = displayLen > colWidths[a] - 2\n ? name.slice(0, colWidths[a] - 5) + '...'\n : display\n const truncLen = stripAnsi(truncated).length\n const leftPad = mid - Math.floor(truncLen / 2)\n const rightPad = colWidths[a] - leftPad - truncLen\n line += ' '.repeat(Math.max(0, leftPad)) + pc.bold(truncated) + ' '.repeat(Math.max(0, rightPad))\n } else {\n line += ' '.repeat(colWidths[a])\n }\n if (a < arms.length - 1) line += ' '\n }\n console.log(`${indent}${line}`)\n\n // Draw connector between rows\n if (row < maxDepth - 1) {\n let connector = ''\n for (let a = 0; a < arms.length; a++) {\n const mid = Math.floor(colWidths[a] / 2)\n const hasMore = row + 1 < arms[a].pageKeys.length\n if (hasMore) {\n connector += ' '.repeat(mid) + '▼' + ' '.repeat(colWidths[a] - mid - 1)\n } else {\n connector += ' '.repeat(colWidths[a])\n }\n if (a < arms.length - 1) connector += ' '\n }\n console.log(`${indent}${connector}`)\n }\n }\n\n // Draw the merge line: ├───────┴───────┤\n let mergeLine = ''\n for (let a = 0; a < arms.length; a++) {\n const mid = Math.floor(colWidths[a] / 2)\n if (a === 0) {\n mergeLine += ' '.repeat(mid) + '├' + H.repeat(colWidths[a] - mid - 1)\n } else if (a === arms.length - 1) {\n mergeLine += H.repeat(mid) + '┤' + ' '.repeat(colWidths[a] - mid - 1)\n } else {\n mergeLine += H.repeat(mid) + '┴' + H.repeat(colWidths[a] - mid - 1)\n }\n if (a < arms.length - 1) mergeLine += H.repeat(3)\n }\n console.log(`${indent}${mergeLine}`)\n}\n\nfunction stripAnsi(str: string): string {\n // eslint-disable-next-line no-control-regex\n return str.replace(/\\x1b\\[[0-9;]*m/g, '')\n}\n","import { resolve, join } from 'node:path'\nimport { randomUUID } from 'node:crypto'\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, type AppFunnelConfig } 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 = 10 * 1024 * 1024 // 10MB\nconst MAX_PAGE_SIZE = 500 * 1024 // 500KB\n\n/** UTM query params — always included, no config needed */\nconst BUILTIN_QUERY_PARAMS: Record<string, { type: string }> = {\n utm_source: { type: 'string' },\n utm_medium: { type: 'string' },\n utm_campaign: { type: 'string' },\n utm_content: { type: 'string' },\n utm_term: { type: 'string' },\n}\n\n/** User profile variables — always included, no config needed */\nconst BUILTIN_USER_VARS: Record<string, { type: string }> = {\n email: { type: 'string' },\n name: { type: 'string' },\n dateOfBirth: { type: 'string' },\n gender: { type: 'string' },\n stripeCustomerId: { type: 'string' },\n paddleCustomerId: { type: 'string' },\n}\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\n if (!config.projectId) {\n s.stop()\n throw new CLIError(\n 'MISSING_PROJECT_ID',\n 'Missing projectId in appfunnel.config.ts.',\n \"Run 'appfunnel dev' first to select a project, or add projectId manually.\",\n )\n }\n\n if (!config.initialPageKey) {\n s.stop()\n throw new CLIError(\n 'MISSING_INITIAL_PAGE_KEY',\n 'Missing initialPageKey in appfunnel.config.ts.',\n \"Set initialPageKey to the page key (filename without .tsx) of your first page.\",\n )\n }\n\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, 'dist')\n const { build } = await import('vite')\n const react = await import('@vitejs/plugin-react')\n\n // Try to load @tailwindcss/vite from the user's project\n let tailwindPlugin: any = null\n try {\n const { createRequire } = await import('node:module')\n const require = createRequire(join(cwd, 'package.json'))\n const tailwindPath = require.resolve('@tailwindcss/vite')\n const tailwindVite = await import(tailwindPath)\n tailwindPlugin = tailwindVite.default\n } catch {\n // Not installed in the user's project — skip\n }\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 base: './',\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-dev/sdk')) {\n return 'vendor-sdk'\n }\n },\n },\n },\n },\n plugins: [\n react.default(),\n ...(tailwindPlugin ? [tailwindPlugin()] : []),\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 buildHash: randomUUID(),\n sdkVersion: getSdkVersion(cwd),\n projectId: config.projectId,\n funnelId: config.funnelId,\n name: config.name,\n initialPageKey: config.initialPageKey,\n pages: { ...config.pages, ...mergedPages },\n routes: { ...config.routes, ...mergedRoutes },\n responses: config.responses || {},\n queryParams: { ...BUILTIN_QUERY_PARAMS, ...config.queryParams },\n data: config.data || {},\n user: BUILTIN_USER_VARS,\n products: config.products || {},\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:')} dist/`)\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) {\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 10MB 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: AppFunnelConfig,\n pages: Record<string, { routes?: Array<{ to: string; when?: unknown }> }>,\n pageKeys: string[],\n): void {\n const allPageKeys = new Set(pageKeys)\n\n // Build full set of known variables from all namespaced sections\n const allVariables = new Set<string>()\n if (config.responses) {\n for (const key of Object.keys(config.responses)) {\n allVariables.add(`answers.${key}`)\n }\n }\n if (config.queryParams) {\n for (const key of Object.keys(config.queryParams)) {\n allVariables.add(`query.${key}`)\n }\n }\n if (config.data) {\n for (const key of Object.keys(config.data)) {\n allVariables.add(`data.${key}`)\n }\n }\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.*\n const prefix = cond.variable.split('.')[0]\n const systemPrefixes = ['page', 'device', 'browser', 'os', 'session', 'system', 'metadata', 'user', 'products', 'card', 'payment', 'stripe', 'subscription']\n if (!systemPrefixes.includes(prefix) && !allVariables.has(cond.variable)) {\n // Suggest the right config section based on the prefix\n const varName = cond.variable.includes('.') ? cond.variable.split('.').slice(1).join('.') : cond.variable\n let hint: string\n if (prefix === 'answers') {\n hint = `Add it to responses in appfunnel.config.ts: responses: { '${varName}': { type: 'string' } }`\n } else if (prefix === 'query') {\n hint = `Add it to queryParams in appfunnel.config.ts: queryParams: { '${varName}': { type: 'string' } }`\n } else if (prefix === 'data') {\n hint = `Add it to data in appfunnel.config.ts: data: { '${varName}': { type: 'string' } }`\n } else {\n hint = `Add it to the appropriate section (responses, queryParams, or data) in appfunnel.config.ts.`\n }\n throw new CLIError(\n 'UNDEFINED_VARIABLE',\n `Route condition in \"${pageKey}\" references variable \"${cond.variable}\" which is not defined.`,\n hint,\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-dev', 'sdk', 'package.json'), 'utf-8'))\n return pkg.version\n } catch {\n return '0.0.0'\n }\n}\n","import { join } from 'node:path'\nimport { readFileSync, writeFileSync } from 'node:fs'\n\n/**\n * Patch appfunnel.config.ts to add a funnelId after the projectId line.\n * Uses simple string replacement — no AST parsing needed.\n */\nexport function patchConfigFunnelId(cwd: string, funnelId: string): void {\n const configPath = join(cwd, 'appfunnel.config.ts')\n let content = readFileSync(configPath, 'utf-8')\n\n // Already has funnelId — don't double-add\n if (content.includes('funnelId')) return\n\n // Insert funnelId after the projectId line\n const patched = content.replace(\n /(projectId:\\s*['\"][^'\"]+['\"],?\\s*\\n)/,\n `$1 funnelId: '${funnelId}',\\n`,\n )\n\n if (patched !== content) {\n writeFileSync(configPath, patched, 'utf-8')\n }\n}\n","import { resolve, join } from 'node:path'\nimport { readFileSync, existsSync } from 'node:fs'\nimport { execSync } from 'node:child_process'\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 { publishInit, publishFinalize } from '../lib/api.js'\nimport { CLIError } from '../lib/errors.js'\nimport { patchConfigFunnelId } from '../lib/config-patch.js'\nimport select from '@inquirer/select'\n\nfunction tryGit(command: string, cwd: string): string | undefined {\n try {\n return execSync(command, { cwd, stdio: ['pipe', 'pipe', 'ignore'] }).toString().trim() || undefined\n } catch {\n return undefined\n }\n}\n\nfunction getGitMetadata(cwd: string): { commitSha?: string; branch?: string; message?: string } {\n const commitSha = tryGit('git rev-parse HEAD', cwd)\n if (!commitSha) return {} // not a git repo\n const branch = tryGit('git rev-parse --abbrev-ref HEAD', cwd)\n const message = tryGit('git log -1 --pretty=%s', cwd)\n return { commitSha, branch, message }\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\nconst UPLOAD_CONCURRENCY = 10\n\nexport async function publishCommand(options?: { promote?: boolean }): 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 const projectId = config.projectId\n if (!projectId) {\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 // 3. Check build output exists\n const outDir = resolve(cwd, 'dist')\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('Preparing assets...')\n const assetBuffers: Array<{ path: string; body: Buffer; size: number }> = []\n let totalBytes = 0\n\n for (let i = 0; i < assets.length; i++) {\n const asset = assets[i]\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 const body = readFileSync(fullPath)\n totalBytes += body.length\n\n assetBuffers.push({ path: asset.path, body, size: body.length })\n s.text = `Preparing assets... ${i + 1}/${assets.length} ${pc.dim(`(${formatSize(totalBytes)})`)}`\n }\n\n s.stop()\n\n const MAX_TOTAL_SIZE = 10 * 1024 * 1024 // 10MB\n if (totalBytes > MAX_TOTAL_SIZE) {\n throw new CLIError(\n 'BUNDLE_TOO_LARGE',\n `Total bundle size (${formatSize(totalBytes)}) exceeds the ${formatSize(MAX_TOTAL_SIZE)} limit.`,\n 'Reduce page bundle sizes. Check for large dependencies.',\n )\n }\n\n log.info(`${assets.length} assets prepared ${pc.dim(`(${formatSize(totalBytes)})`)}`)\n\n // 6. Ask whether to promote (unless --promote was passed)\n let promote = options?.promote ?? false\n if (!promote) {\n promote = await select({\n message: 'Publish as the new active version?',\n choices: [\n { name: 'Yes', value: true },\n { name: 'No', value: false },\n ],\n })\n }\n\n // 7. Collect metadata\n const git = getGitMetadata(cwd)\n\n // 8. Initialize publish — get presigned URLs\n const initSpinner = log.spinner('Initializing publish...')\n\n let initResult: Awaited<ReturnType<typeof publishInit>>\n try {\n initResult = await publishInit(\n projectId,\n {\n manifest,\n funnelId: config.funnelId || undefined,\n promote,\n commitSha: git.commitSha,\n branch: git.branch,\n message: git.message,\n triggeredBy: creds.email || undefined,\n },\n { token: creds.token },\n )\n } catch (err) {\n initSpinner.stop()\n if (err instanceof CLIError && err.code === 'API_ERROR') {\n if (err.statusCode === 413) {\n throw new CLIError(\n 'BUNDLE_TOO_LARGE',\n err.message,\n 'Reduce page bundle sizes. Check for large dependencies.',\n )\n }\n if (err.statusCode === 409) {\n throw new CLIError(\n 'FUNNEL_NOT_HEADLESS',\n err.message,\n 'Remove funnelId from config to create a new headless funnel.',\n )\n }\n throw new CLIError('PUBLISH_FAILED', err.message)\n }\n throw err\n }\n\n initSpinner.stop()\n log.info(`Build ${pc.dim(initResult.buildId)} initialized`)\n\n // 9. Upload assets directly to R2 via presigned URLs\n const uploadSpinner = log.spinner(`Uploading 0/${assets.length} assets...`)\n\n // Build a map from path to upload info\n const uploadInfoMap = new Map(initResult.assets.map(a => [a.path, a]))\n\n let uploaded = 0\n let uploadedBytes = 0\n const uploadAsset = async (asset: typeof assetBuffers[0]) => {\n const info = uploadInfoMap.get(asset.path)\n if (!info) {\n throw new CLIError('PUBLISH_FAILED', `No upload URL for asset: ${asset.path}`)\n }\n\n const response = await fetch(info.uploadUrl, {\n method: 'PUT',\n body: new Uint8Array(asset.body),\n headers: {\n 'Content-Type': info.contentType,\n },\n })\n\n if (!response.ok) {\n throw new CLIError(\n 'PUBLISH_FAILED',\n `Failed to upload ${asset.path}: ${response.status} ${response.statusText}`,\n )\n }\n\n uploaded++\n uploadedBytes += asset.size\n uploadSpinner.text = `Uploading ${uploaded}/${assets.length} assets... ${pc.dim(`(${formatSize(uploadedBytes)}/${formatSize(totalBytes)})`)}`\n }\n\n // Upload with concurrency limit\n const queue = [...assetBuffers]\n const workers: Promise<void>[] = []\n for (let i = 0; i < Math.min(UPLOAD_CONCURRENCY, queue.length); i++) {\n workers.push((async () => {\n while (queue.length > 0) {\n const asset = queue.shift()!\n await uploadAsset(asset)\n }\n })())\n }\n await Promise.all(workers)\n\n uploadSpinner.stop()\n log.info(`${assets.length} assets uploaded ${pc.dim(`(${formatSize(totalBytes)})`)}`)\n\n // 10. Finalize publish\n const finalizeSpinner = log.spinner('Finalizing publish...')\n\n const result = await publishFinalize(\n projectId,\n { buildId: initResult.buildId, funnelId: initResult.funnelId },\n { token: creds.token },\n )\n\n finalizeSpinner.stop()\n\n // 11. If the server created a new funnel, write funnelId back to config\n if (initResult.created && initResult.funnelId) {\n patchConfigFunnelId(cwd, initResult.funnelId)\n log.info(`Funnel created — funnelId added to appfunnel.config.ts`)\n }\n\n // 12. Print result\n console.log()\n log.success(result.activated ? 'Published and activated' : 'Published successfully')\n console.log()\n console.log(` ${pc.dim('Build ID:')} ${result.buildId}`)\n if (initResult.funnelId && !config.funnelId) {\n console.log(` ${pc.dim('Funnel:')} ${initResult.funnelId}`)\n }\n console.log(` ${pc.dim('Dashboard:')} ${pc.cyan(result.dashboardUrl)}`)\n console.log(` ${pc.dim('Assets:')} ${assets.length} files ${pc.dim(`(${formatSize(totalBytes)})`)}`)\n console.log()\n}\n","import { Command } from 'commander'\nimport pc from 'picocolors'\nimport { CLIError, formatError } from './lib/errors.js'\n\nconst program = new Command()\n\nprogram\n .name('appfunnel')\n .description('Build and publish headless AppFunnel projects')\n .version(__CLI_VERSION__)\n\nprogram\n .command('init')\n .argument('[name]', 'Project directory 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('routes')\n .description('Visualize the funnel page flow')\n .action(async () => {\n const { routesCommand } = await import('./commands/routes.js')\n await routesCommand()\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 .option('--promote', 'Activate the build immediately after publishing')\n .action(async (options: { promote?: boolean }) => {\n const { publishCommand } = await import('./commands/publish.js')\n await publishCommand({ promote: options.promote })\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","pc","DEFAULT_API_BASE","error","readFileSync","writeFileSync","join","pc","select","s","resolve","spinner","pc","DEFAULT_API_BASE","existsSync","readFileSync","join","readFileSync","join","readdirSync","readFileSync","existsSync","join","join","existsSync","resolve","join","existsSync","writeFileSync","mkdirSync","readFileSync","readdirSync","readFileSync","writeFileSync","join","pc","createServer","require","current","globalVisited","pages","pc","resolve","join","randomUUID","readFileSync","writeFileSync","readdirSync","pc","require","getSdkVersion","join","readFileSync","writeFileSync","resolve","join","readFileSync","existsSync","pc","select","formatSize","MAX_TOTAL_SIZE","info","pc","initCommand","loginCommand","whoamiCommand","devCommand","routesCommand","buildCommand","publishCommand"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "appfunnel",
3
- "version": "0.12.0",
3
+ "version": "0.13.0",
4
4
  "description": "CLI for building and publishing headless AppFunnel projects",
5
5
  "type": "module",
6
6
  "bin": {
@@ -0,0 +1,69 @@
1
+ # __NAME__
2
+
3
+ Built with [AppFunnel Headless SDK](https://appfunnel.net/docs/headless).
4
+
5
+ ## Getting Started
6
+
7
+ ```bash
8
+ npm install
9
+ appfunnel dev
10
+ ```
11
+
12
+ ## Commands
13
+
14
+ | Command | Description |
15
+ |---|---|
16
+ | `appfunnel dev` | Start local dev server with HMR |
17
+ | `appfunnel build` | Build for production |
18
+ | `appfunnel publish` | Deploy to AppFunnel |
19
+
20
+ ## Project Structure
21
+
22
+ ```
23
+ src/
24
+ ├── funnel.tsx # Route definitions & layout
25
+ ├── app.css # Global styles (Tailwind)
26
+ ├── pages/ # One React component per funnel page
27
+ └── components/ # Shared components
28
+ appfunnel.config.ts # Project config, products, variables
29
+ locales/en.json # Localization strings
30
+ ```
31
+
32
+ ## Documentation
33
+
34
+ Full docs: **[appfunnel.net/docs/headless](https://appfunnel.net/docs/headless)**
35
+
36
+ ### Guides
37
+ - [Getting Started](https://appfunnel.net/docs/headless/getting-started) — Setup, project structure, first page
38
+ - [Configuration](https://appfunnel.net/docs/headless/configuration) — `appfunnel.config.ts` reference
39
+ - [Payments](https://appfunnel.net/docs/headless/payments) — Stripe & Paddle integration
40
+ - [CLI Reference](https://appfunnel.net/docs/headless/cli) — All CLI commands
41
+
42
+ ### Hooks
43
+ - [Responses](https://appfunnel.net/docs/headless/hooks/responses) — `useResponse`, `useResponses`
44
+ - [Data](https://appfunnel.net/docs/headless/hooks/data) — `useData`
45
+ - [Query Params](https://appfunnel.net/docs/headless/hooks/query-params) — `useQueryParam`, `useQueryParams`
46
+ - [Navigation](https://appfunnel.net/docs/headless/hooks/navigation) — `useNavigation`
47
+ - [User](https://appfunnel.net/docs/headless/hooks/user) — `useUser`
48
+ - [Products & Payments](https://appfunnel.net/docs/headless/hooks/products-and-payments) — `useProducts`, `useCheckout`
49
+ - [Tracking](https://appfunnel.net/docs/headless/hooks/tracking) — `useTracking`
50
+ - [Localization](https://appfunnel.net/docs/headless/hooks/localization) — `useLocale`, `useTranslation`
51
+ - [Device & Context](https://appfunnel.net/docs/headless/hooks/device) — `useKeyboard`, `useSafeArea`
52
+ - [useFunnel](https://appfunnel.net/docs/headless/hooks/use-funnel) — Low-level funnel access
53
+ - [Variables (Advanced)](https://appfunnel.net/docs/headless/hooks/variables) — `useVariable`, `useVariables`
54
+
55
+ ### Elements
56
+ - [SingleSelect](https://appfunnel.net/docs/headless/elements/single-select) — Single-choice option list
57
+ - [MultiSelect](https://appfunnel.net/docs/headless/elements/multi-select) — Multi-choice option list
58
+ - [ProgressBar](https://appfunnel.net/docs/headless/elements/progress-bar) — Horizontal progress indicator
59
+ - [ProgressCircle](https://appfunnel.net/docs/headless/elements/progress-circle) — Circular progress indicator
60
+ - [Carousel](https://appfunnel.net/docs/headless/elements/carousel) — Swipeable card carousel
61
+ - [Marquee](https://appfunnel.net/docs/headless/elements/marquee) — Auto-scrolling content strip
62
+ - [CountUp](https://appfunnel.net/docs/headless/elements/countup) — Animated number counter
63
+ - [SpinnerWheel](https://appfunnel.net/docs/headless/elements/spinner-wheel) — Prize/discount spinner
64
+ - [Loading](https://appfunnel.net/docs/headless/elements/loading) — Loading screen with steps
65
+ - [Toast](https://appfunnel.net/docs/headless/elements/toast) — Toast notifications
66
+ - [Dialog](https://appfunnel.net/docs/headless/elements/dialog) — Modal dialog
67
+ - [Drawer](https://appfunnel.net/docs/headless/elements/drawer) — Bottom sheet drawer
68
+ - [motion](https://appfunnel.net/docs/headless/elements/motion) — Animation wrapper
69
+ - [Easing Utilities](https://appfunnel.net/docs/headless/elements/easing) — Easing functions