create-brainerce-store 1.23.0 → 1.25.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.
Files changed (2) hide show
  1. package/dist/index.js +110 -27
  2. package/package.json +3 -2
package/dist/index.js CHANGED
@@ -31,7 +31,7 @@ var require_package = __commonJS({
31
31
  "package.json"(exports2, module2) {
32
32
  module2.exports = {
33
33
  name: "create-brainerce-store",
34
- version: "1.22.0",
34
+ version: "1.25.0",
35
35
  description: "Scaffold a production-ready e-commerce storefront connected to Brainerce",
36
36
  bin: {
37
37
  "create-brainerce-store": "dist/index.js"
@@ -44,7 +44,8 @@ var require_package = __commonJS({
44
44
  scripts: {
45
45
  build: "tsup src/index.ts --format cjs --dts --clean",
46
46
  dev: "tsup src/index.ts --format cjs --dts --watch",
47
- clean: "rimraf dist"
47
+ clean: "rimraf dist",
48
+ prepublishOnly: "npm run build"
48
49
  },
49
50
  dependencies: {
50
51
  chalk: "^4.1.2",
@@ -173,6 +174,17 @@ async function runInteractive(defaults) {
173
174
  return error || true;
174
175
  }
175
176
  });
177
+ } else {
178
+ questions.push({
179
+ type: "text",
180
+ name: "projectName",
181
+ message: "Project name:",
182
+ initial: defaults.projectName,
183
+ validate: (value) => {
184
+ const error = validateProjectName(value);
185
+ return error || true;
186
+ }
187
+ });
176
188
  }
177
189
  if (!defaults.connectionId) {
178
190
  questions.push({
@@ -410,7 +422,17 @@ async function copyWithEjs(srcDir, destDir, vars) {
410
422
  }
411
423
 
412
424
  // src/fetch-store-info.ts
413
- async function fetchStoreInfo(connectionId, baseUrl = "https://api.brainerce.com") {
425
+ var KNOWN_API_URLS = {
426
+ production: "https://api.brainerce.com",
427
+ staging: "https://api-staging.brainerce.com"
428
+ };
429
+ var ConnectionNotFoundError = class extends Error {
430
+ constructor(connectionId, baseUrl) {
431
+ super(`Connection "${connectionId}" not found at ${baseUrl}`);
432
+ this.code = "NOT_FOUND";
433
+ }
434
+ };
435
+ async function fetchStoreInfo(connectionId, baseUrl = KNOWN_API_URLS.production) {
414
436
  const url = `${baseUrl}/api/vc/${connectionId}/info`;
415
437
  const controller = new AbortController();
416
438
  const timeout = setTimeout(() => controller.abort(), 1e4);
@@ -419,23 +441,23 @@ async function fetchStoreInfo(connectionId, baseUrl = "https://api.brainerce.com
419
441
  res = await fetch(url, { signal: controller.signal });
420
442
  } catch (err) {
421
443
  if (err.name === "AbortError") {
422
- throw new Error("Request timed out");
444
+ throw new Error(`Request to ${baseUrl} timed out`);
423
445
  }
424
- throw new Error(`Failed to connect to Brainerce API: ${err.message}`);
446
+ throw new Error(`Failed to connect to ${baseUrl}: ${err.message}`);
425
447
  } finally {
426
448
  clearTimeout(timeout);
427
449
  }
428
450
  if (res.status === 404) {
429
- throw new Error(`Connection ID "${connectionId}" not found. Check your dashboard.`);
451
+ throw new ConnectionNotFoundError(connectionId, baseUrl);
430
452
  }
431
453
  if (!res.ok) {
432
- throw new Error(`API returned status ${res.status}`);
454
+ throw new Error(`${baseUrl} returned status ${res.status}`);
433
455
  }
434
456
  let json;
435
457
  try {
436
458
  json = await res.json();
437
459
  } catch {
438
- throw new Error("Invalid response from API");
460
+ throw new Error(`Invalid response from ${baseUrl}`);
439
461
  }
440
462
  return {
441
463
  name: json.name || json.storeName || "My Store",
@@ -444,6 +466,33 @@ async function fetchStoreInfo(connectionId, baseUrl = "https://api.brainerce.com
444
466
  ...json.i18n ? { i18n: json.i18n } : {}
445
467
  };
446
468
  }
469
+ async function resolveStoreInfo(connectionId, candidateUrls) {
470
+ const urls = candidateUrls.filter((u, i) => u && candidateUrls.indexOf(u) === i);
471
+ if (urls.length === 0) {
472
+ throw new Error("No API URLs to try");
473
+ }
474
+ let lastError;
475
+ let allNotFound = true;
476
+ for (let i = 0; i < urls.length; i++) {
477
+ const baseUrl = urls[i];
478
+ try {
479
+ const info = await fetchStoreInfo(connectionId, baseUrl);
480
+ return { info, apiBaseUrl: baseUrl, fellBack: i > 0 };
481
+ } catch (err) {
482
+ lastError = err;
483
+ const isNotFound = err.code === "NOT_FOUND";
484
+ if (!isNotFound) {
485
+ allNotFound = false;
486
+ }
487
+ }
488
+ }
489
+ if (allNotFound) {
490
+ throw new Error(
491
+ `Connection "${connectionId}" not found in any known environment (${urls.join(", ")}). Check your dashboard.`
492
+ );
493
+ }
494
+ throw lastError || new Error("Failed to resolve store info");
495
+ }
447
496
 
448
497
  // src/utils/logger.ts
449
498
  var import_chalk = __toESM(require("chalk"));
@@ -537,18 +586,48 @@ program.name("create-brainerce-store").description("Scaffold a production-ready
537
586
  scaffoldInPlace = true;
538
587
  }
539
588
  let connectionId = options.connectionId;
540
- const apiBaseUrl = (options.apiUrl || process.env.BRAINERCE_API_URL || "https://api.brainerce.com").replace(/\/$/, "");
589
+ const explicitApiUrl = (options.apiUrl || process.env.BRAINERCE_API_URL || "").replace(/\/$/, "");
590
+ const candidateApiUrls = explicitApiUrl ? [explicitApiUrl] : [KNOWN_API_URLS.production, KNOWN_API_URLS.staging];
541
591
  let language = options.language;
542
592
  let framework = options.framework;
543
593
  let theme = options.theme;
544
594
  let pkgManager = options.pkgManager;
545
595
  const skipGit = options.git === false;
546
596
  const skipInstall = options.install === false;
597
+ let storeInfo = null;
598
+ let resolvedApiUrl = candidateApiUrls[0];
599
+ if (connectionId) {
600
+ const connError2 = validateConnectionId(connectionId);
601
+ if (connError2) {
602
+ logger.error(connError2);
603
+ process.exit(1);
604
+ }
605
+ const prefetchSpinner = createSpinner("Fetching store info...");
606
+ prefetchSpinner.start();
607
+ try {
608
+ const resolved = await resolveStoreInfo(connectionId, candidateApiUrls);
609
+ storeInfo = resolved.info;
610
+ resolvedApiUrl = resolved.apiBaseUrl;
611
+ const envLabel = resolved.apiBaseUrl === KNOWN_API_URLS.staging ? " [staging]" : resolved.apiBaseUrl === KNOWN_API_URLS.production ? "" : ` [${resolved.apiBaseUrl}]`;
612
+ const i18nStatus = storeInfo.i18n?.enabled && storeInfo.i18n.supportedLocales.length > 1 ? ` | i18n: ${storeInfo.i18n.supportedLocales.join(", ")}` : "";
613
+ prefetchSpinner.succeed(
614
+ `Store: "${storeInfo.name}" | ${storeInfo.currency} | ${storeInfo.language}${i18nStatus}${envLabel}`
615
+ );
616
+ } catch (err) {
617
+ prefetchSpinner.fail("Could not fetch store info");
618
+ logger.warn(
619
+ err instanceof Error ? err.message : "Using defaults. Make sure the connection ID is correct and the Brainerce API is reachable."
620
+ );
621
+ }
622
+ }
623
+ const slugify = (s) => s.toLowerCase().trim().replace(/[^a-z0-9\u0590-\u05FF]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 50);
624
+ const projectNameDefault = projectName || (storeInfo ? slugify(storeInfo.name) : void 0);
625
+ const languageDefault = language || storeInfo?.language;
547
626
  if (!projectName || !connectionId || !language) {
548
627
  const answers = await runInteractive({
549
- projectName,
628
+ projectName: projectNameDefault,
550
629
  connectionId,
551
- language,
630
+ language: languageDefault,
552
631
  framework,
553
632
  theme,
554
633
  pkgManager
@@ -570,21 +649,25 @@ program.name("create-brainerce-store").description("Scaffold a production-ready
570
649
  logger.error(connError);
571
650
  process.exit(1);
572
651
  }
573
- const spinner = createSpinner("Fetching store info...");
574
- spinner.start();
575
- let storeInfo;
576
- try {
577
- storeInfo = await fetchStoreInfo(connectionId, apiBaseUrl);
578
- const i18nStatus = storeInfo.i18n?.enabled && storeInfo.i18n.supportedLocales.length > 1 ? ` | i18n: ${storeInfo.i18n.supportedLocales.join(", ")}` : "";
579
- spinner.succeed(
580
- `Store: "${storeInfo.name}" | ${storeInfo.currency} | ${storeInfo.language}${i18nStatus}`
581
- );
582
- } catch (err) {
583
- spinner.fail("Could not fetch store info");
584
- logger.warn(
585
- "Using defaults. Make sure the connection ID is correct and the Brainerce API is reachable."
586
- );
587
- storeInfo = { name: projectName, currency: "USD", language: "en" };
652
+ if (!storeInfo) {
653
+ const spinner = createSpinner("Fetching store info...");
654
+ spinner.start();
655
+ try {
656
+ const resolved = await resolveStoreInfo(connectionId, candidateApiUrls);
657
+ storeInfo = resolved.info;
658
+ resolvedApiUrl = resolved.apiBaseUrl;
659
+ const envLabel = resolved.apiBaseUrl === KNOWN_API_URLS.staging ? " [staging]" : resolved.apiBaseUrl === KNOWN_API_URLS.production ? "" : ` [${resolved.apiBaseUrl}]`;
660
+ const i18nStatus = storeInfo.i18n?.enabled && storeInfo.i18n.supportedLocales.length > 1 ? ` | i18n: ${storeInfo.i18n.supportedLocales.join(", ")}` : "";
661
+ spinner.succeed(
662
+ `Store: "${storeInfo.name}" | ${storeInfo.currency} | ${storeInfo.language}${i18nStatus}${envLabel}`
663
+ );
664
+ } catch (err) {
665
+ spinner.fail("Could not fetch store info");
666
+ logger.warn(
667
+ err instanceof Error ? err.message : "Using defaults. Make sure the connection ID is correct and the Brainerce API is reachable."
668
+ );
669
+ storeInfo = { name: projectName, currency: "USD", language: "en" };
670
+ }
588
671
  }
589
672
  if (!pkgManager) {
590
673
  pkgManager = detectPackageManager();
@@ -594,7 +677,7 @@ program.name("create-brainerce-store").description("Scaffold a production-ready
594
677
  await scaffold({
595
678
  projectName,
596
679
  connectionId,
597
- apiBaseUrl,
680
+ apiBaseUrl: resolvedApiUrl,
598
681
  framework,
599
682
  theme,
600
683
  storeName: storeInfo.name,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-brainerce-store",
3
- "version": "1.23.0",
3
+ "version": "1.25.0",
4
4
  "description": "Scaffold a production-ready e-commerce storefront connected to Brainerce",
5
5
  "bin": {
6
6
  "create-brainerce-store": "dist/index.js"
@@ -13,7 +13,8 @@
13
13
  "scripts": {
14
14
  "build": "tsup src/index.ts --format cjs --dts --clean",
15
15
  "dev": "tsup src/index.ts --format cjs --dts --watch",
16
- "clean": "rimraf dist"
16
+ "clean": "rimraf dist",
17
+ "prepublishOnly": "npm run build"
17
18
  },
18
19
  "dependencies": {
19
20
  "chalk": "^4.1.2",