appfunnel 0.13.0 → 0.15.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
@@ -261,25 +261,47 @@ var init_exports = {};
261
261
  __export(init_exports, {
262
262
  initCommand: () => initCommand
263
263
  });
264
- import { cpSync, existsSync, readFileSync as readFileSync2, writeFileSync as writeFileSync2, readdirSync } from "fs";
265
- import { join as join2, dirname } from "path";
266
- import { fileURLToPath } from "url";
264
+ import { existsSync, readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "fs";
265
+ import { join as join2 } from "path";
266
+ import { Readable } from "stream";
267
+ import { extract } from "tar";
267
268
  import pc4 from "picocolors";
268
269
  import select2 from "@inquirer/select";
269
270
  import input from "@inquirer/input";
270
- function getTemplatesDir() {
271
- const dir = join2(__dirname, "..", "templates");
272
- if (!existsSync(dir)) {
273
- throw new Error(`Templates directory not found at ${dir}`);
274
- }
275
- return dir;
276
- }
277
- function listTemplates() {
278
- const root = getTemplatesDir();
279
- return readdirSync(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => {
280
- const configPath = join2(root, d.name, "template.json");
281
- const config = existsSync(configPath) ? JSON.parse(readFileSync2(configPath, "utf-8")) : { name: d.name, description: "", products: [] };
282
- return { dir: d.name, config };
271
+ async function fetchTemplateIndex() {
272
+ const url = `https://raw.githubusercontent.com/${TEMPLATES_REPO}/main/index.json`;
273
+ const res = await fetch(url);
274
+ if (!res.ok) throw new Error(`Failed to fetch template index: ${res.status}`);
275
+ const index = await res.json();
276
+ const templates = await Promise.all(
277
+ index.map(async (entry) => {
278
+ const configUrl = `https://raw.githubusercontent.com/${TEMPLATES_REPO}/main/${entry.dir}/template.json`;
279
+ const configRes = await fetch(configUrl);
280
+ const config = configRes.ok ? await configRes.json() : { name: entry.name, description: entry.description, products: [] };
281
+ return { dir: entry.dir, config };
282
+ })
283
+ );
284
+ return templates;
285
+ }
286
+ async function downloadTemplate(templateDir, destDir) {
287
+ const tarballUrl = `https://api.github.com/repos/${TEMPLATES_REPO}/tarball/main`;
288
+ const res = await fetch(tarballUrl, {
289
+ headers: { Accept: "application/vnd.github+json" }
290
+ });
291
+ if (!res.ok) throw new Error(`Failed to download template: ${res.status}`);
292
+ mkdirSync2(destDir, { recursive: true });
293
+ const nodeStream = Readable.fromWeb(res.body);
294
+ await new Promise((resolve5, reject) => {
295
+ nodeStream.pipe(
296
+ extract({
297
+ cwd: destDir,
298
+ strip: 2,
299
+ filter: (path) => {
300
+ const parts = path.split("/");
301
+ return parts.length > 2 && parts[1] === templateDir;
302
+ }
303
+ })
304
+ ).on("finish", resolve5).on("error", reject);
283
305
  });
284
306
  }
285
307
  function formatInterval(interval, count) {
@@ -324,7 +346,9 @@ async function initCommand(nameArg) {
324
346
  const projectId = await promptForProject(creds.token);
325
347
  const projects = await fetchProjects(creds.token);
326
348
  const project = projects.find((p) => p.id === projectId);
327
- const templates = listTemplates();
349
+ const templateSpinner = spinner("Fetching templates...");
350
+ const templates = await fetchTemplateIndex();
351
+ templateSpinner.stop();
328
352
  const selectedDir = await select2({
329
353
  message: "Choose a template",
330
354
  choices: templates.map((t) => ({
@@ -334,7 +358,6 @@ async function initCommand(nameArg) {
334
358
  });
335
359
  const chosen = templates.find((t) => t.dir === selectedDir);
336
360
  const templateConfig = chosen.config;
337
- const templateDir = join2(getTemplatesDir(), chosen.dir);
338
361
  const productBindings = [];
339
362
  if (templateConfig.products.length > 0) {
340
363
  const storesSpinner = spinner("Fetching stores...");
@@ -416,7 +439,7 @@ async function initCommand(nameArg) {
416
439
  }
417
440
  }
418
441
  const s = spinner(`Creating ${name}...`);
419
- cpSync(templateDir, dir, { recursive: true });
442
+ await downloadTemplate(selectedDir, dir);
420
443
  const templateJsonPath = join2(dir, "template.json");
421
444
  if (existsSync(templateJsonPath)) {
422
445
  const { unlinkSync } = await import("fs");
@@ -463,7 +486,7 @@ ${itemsStr},
463
486
  }
464
487
  writeFileSync2(configPath, config);
465
488
  }
466
- const sdkVersion = `^${"0.13.0"}`;
489
+ const sdkVersion = `^${"0.15.0"}`;
467
490
  writeFileSync2(
468
491
  join2(dir, "package.json"),
469
492
  JSON.stringify(
@@ -506,7 +529,7 @@ ${itemsStr},
506
529
  console.log(` ${pc4.dim("appfunnel dev")}`);
507
530
  console.log();
508
531
  }
509
- var __dirname, INTERVAL_LABELS;
532
+ var TEMPLATES_REPO, INTERVAL_LABELS;
510
533
  var init_init = __esm({
511
534
  "src/commands/init.ts"() {
512
535
  "use strict";
@@ -514,7 +537,7 @@ var init_init = __esm({
514
537
  init_auth();
515
538
  init_projects();
516
539
  init_api();
517
- __dirname = dirname(fileURLToPath(import.meta.url));
540
+ TEMPLATES_REPO = "appfunnel/templates";
518
541
  INTERVAL_LABELS = {
519
542
  day: { 1: "day", 7: "week", 14: "2 weeks", 30: "month" },
520
543
  week: { 1: "week", 2: "2 weeks", 4: "month", 12: "quarter", 52: "year" },
@@ -709,7 +732,7 @@ var init_config = __esm({
709
732
  import { readFileSync as readFileSync4 } from "fs";
710
733
  import { join as join4 } from "path";
711
734
  function checkVersionCompatibility(cwd) {
712
- const cliVersion = "0.13.0";
735
+ const cliVersion = "0.15.0";
713
736
  const sdkVersion = getSdkVersion(cwd);
714
737
  const [cliMajor, cliMinor] = cliVersion.split(".").map(Number);
715
738
  const [sdkMajor, sdkMinor] = sdkVersion.split(".").map(Number);
@@ -748,7 +771,7 @@ var init_version = __esm({
748
771
  });
749
772
 
750
773
  // src/extract/pages.ts
751
- import { readdirSync as readdirSync2, readFileSync as readFileSync5, existsSync as existsSync3 } from "fs";
774
+ import { readdirSync, readFileSync as readFileSync5, existsSync as existsSync3 } from "fs";
752
775
  import { join as join5, basename } from "path";
753
776
  function scanPages(cwd) {
754
777
  const pagesDir = resolvePagesDir(cwd);
@@ -759,7 +782,7 @@ function scanPages(cwd) {
759
782
  "Create src/pages/ and add at least one .tsx page file."
760
783
  );
761
784
  }
762
- const files = readdirSync2(pagesDir).filter((f) => f.endsWith(".tsx") && !f.startsWith("_")).map((f) => basename(f, ".tsx")).sort();
785
+ const files = readdirSync(pagesDir).filter((f) => f.endsWith(".tsx") && !f.startsWith("_")).map((f) => basename(f, ".tsx")).sort();
763
786
  if (files.length === 0) {
764
787
  throw new CLIError(
765
788
  "NO_PAGES",
@@ -1132,13 +1155,13 @@ var init_html = __esm({
1132
1155
 
1133
1156
  // src/vite/plugin.ts
1134
1157
  import { resolve as resolve2, join as join7 } from "path";
1135
- import { existsSync as existsSync5, writeFileSync as writeFileSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync6, readdirSync as readdirSync3 } from "fs";
1158
+ import { existsSync as existsSync5, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync6, readdirSync as readdirSync2 } from "fs";
1136
1159
  function loadTranslations(cwd) {
1137
1160
  const localesDir = join7(cwd, "locales");
1138
1161
  if (!existsSync5(localesDir)) return void 0;
1139
1162
  const translations = {};
1140
1163
  let hasAny = false;
1141
- for (const file of readdirSync3(localesDir)) {
1164
+ for (const file of readdirSync2(localesDir)) {
1142
1165
  if (!file.endsWith(".json")) continue;
1143
1166
  const locale = file.replace(/\.json$/, "");
1144
1167
  try {
@@ -1172,7 +1195,7 @@ function appfunnelPlugin(options) {
1172
1195
  return {
1173
1196
  name: "appfunnel",
1174
1197
  config() {
1175
- mkdirSync2(appfunnelDir, { recursive: true });
1198
+ mkdirSync3(appfunnelDir, { recursive: true });
1176
1199
  writeFileSync3(htmlPath, generateHtml(config.name || "AppFunnel"));
1177
1200
  return {
1178
1201
  // Don't let Vite auto-serve index.html — we handle it ourselves
@@ -1813,7 +1836,7 @@ __export(build_exports, {
1813
1836
  });
1814
1837
  import { resolve as resolve3, join as join9 } from "path";
1815
1838
  import { randomUUID as randomUUID2 } from "crypto";
1816
- import { readFileSync as readFileSync8, writeFileSync as writeFileSync5, statSync, readdirSync as readdirSync4 } from "fs";
1839
+ import { readFileSync as readFileSync8, writeFileSync as writeFileSync5, statSync, readdirSync as readdirSync3 } from "fs";
1817
1840
  import pc8 from "picocolors";
1818
1841
  async function buildCommand() {
1819
1842
  const cwd = process.cwd();
@@ -2034,7 +2057,7 @@ function validateConditionVariables(condition, pageKey, allVariables) {
2034
2057
  function collectAssets(outDir) {
2035
2058
  const assets = [];
2036
2059
  function walk(dir, prefix = "") {
2037
- for (const entry of readdirSync4(dir, { withFileTypes: true })) {
2060
+ for (const entry of readdirSync3(dir, { withFileTypes: true })) {
2038
2061
  const relPath = prefix ? `${prefix}/${entry.name}` : entry.name;
2039
2062
  const fullPath = join9(dir, entry.name);
2040
2063
  if (entry.isDirectory()) {
@@ -2328,7 +2351,7 @@ init_errors();
2328
2351
  import { Command } from "commander";
2329
2352
  import pc10 from "picocolors";
2330
2353
  var program = new Command();
2331
- program.name("appfunnel").description("Build and publish headless AppFunnel projects").version("0.13.0");
2354
+ program.name("appfunnel").description("Build and publish headless AppFunnel projects").version("0.15.0");
2332
2355
  program.command("init").argument("[name]", "Project directory name").description("Create a new AppFunnel project").action(async (name) => {
2333
2356
  const { initCommand: initCommand2 } = await Promise.resolve().then(() => (init_init(), init_exports));
2334
2357
  await initCommand2(name);