create-brainerce-store 1.22.0 → 1.24.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 +92 -49
  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.24.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",
@@ -326,7 +327,7 @@ async function scaffold(options) {
326
327
  fontImport: fontConfig.fontImport,
327
328
  fontVariable: fontConfig.fontVariable,
328
329
  ogLocale,
329
- apiBaseUrl: "https://api.brainerce.com",
330
+ apiBaseUrl: options.apiBaseUrl || "https://api.brainerce.com",
330
331
  i18nEnabled: isMultiLocale,
331
332
  supportedLocales: JSON.stringify(supportedLocales),
332
333
  defaultLocale
@@ -410,47 +411,76 @@ async function copyWithEjs(srcDir, destDir, vars) {
410
411
  }
411
412
 
412
413
  // src/fetch-store-info.ts
413
- var import_https = __toESM(require("https"));
414
- var import_http = __toESM(require("http"));
415
- async function fetchStoreInfo(connectionId, baseUrl = "https://api.brainerce.com") {
414
+ var KNOWN_API_URLS = {
415
+ production: "https://api.brainerce.com",
416
+ staging: "https://api-staging.brainerce.com"
417
+ };
418
+ var ConnectionNotFoundError = class extends Error {
419
+ constructor(connectionId, baseUrl) {
420
+ super(`Connection "${connectionId}" not found at ${baseUrl}`);
421
+ this.code = "NOT_FOUND";
422
+ }
423
+ };
424
+ async function fetchStoreInfo(connectionId, baseUrl = KNOWN_API_URLS.production) {
416
425
  const url = `${baseUrl}/api/vc/${connectionId}/info`;
417
- return new Promise((resolve, reject) => {
418
- const client = url.startsWith("https") ? import_https.default : import_http.default;
419
- const req = client.get(url, { timeout: 1e4 }, (res) => {
420
- let data = "";
421
- res.on("data", (chunk) => {
422
- data += chunk;
423
- });
424
- res.on("end", () => {
425
- if (res.statusCode && res.statusCode >= 400) {
426
- if (res.statusCode === 404) {
427
- reject(new Error(`Connection ID "${connectionId}" not found. Check your dashboard.`));
428
- } else {
429
- reject(new Error(`API returned status ${res.statusCode}`));
430
- }
431
- return;
432
- }
433
- try {
434
- const json = JSON.parse(data);
435
- resolve({
436
- name: json.name || json.storeName || "My Store",
437
- currency: json.currency || "USD",
438
- language: json.language || "en",
439
- ...json.i18n ? { i18n: json.i18n } : {}
440
- });
441
- } catch {
442
- reject(new Error("Invalid response from API"));
443
- }
444
- });
445
- });
446
- req.on("error", (err) => {
447
- reject(new Error(`Failed to connect to Brainerce API: ${err.message}`));
448
- });
449
- req.on("timeout", () => {
450
- req.destroy();
451
- reject(new Error("Request timed out"));
452
- });
453
- });
426
+ const controller = new AbortController();
427
+ const timeout = setTimeout(() => controller.abort(), 1e4);
428
+ let res;
429
+ try {
430
+ res = await fetch(url, { signal: controller.signal });
431
+ } catch (err) {
432
+ if (err.name === "AbortError") {
433
+ throw new Error(`Request to ${baseUrl} timed out`);
434
+ }
435
+ throw new Error(`Failed to connect to ${baseUrl}: ${err.message}`);
436
+ } finally {
437
+ clearTimeout(timeout);
438
+ }
439
+ if (res.status === 404) {
440
+ throw new ConnectionNotFoundError(connectionId, baseUrl);
441
+ }
442
+ if (!res.ok) {
443
+ throw new Error(`${baseUrl} returned status ${res.status}`);
444
+ }
445
+ let json;
446
+ try {
447
+ json = await res.json();
448
+ } catch {
449
+ throw new Error(`Invalid response from ${baseUrl}`);
450
+ }
451
+ return {
452
+ name: json.name || json.storeName || "My Store",
453
+ currency: json.currency || "USD",
454
+ language: json.language || "en",
455
+ ...json.i18n ? { i18n: json.i18n } : {}
456
+ };
457
+ }
458
+ async function resolveStoreInfo(connectionId, candidateUrls) {
459
+ const urls = candidateUrls.filter((u, i) => u && candidateUrls.indexOf(u) === i);
460
+ if (urls.length === 0) {
461
+ throw new Error("No API URLs to try");
462
+ }
463
+ let lastError;
464
+ let allNotFound = true;
465
+ for (let i = 0; i < urls.length; i++) {
466
+ const baseUrl = urls[i];
467
+ try {
468
+ const info = await fetchStoreInfo(connectionId, baseUrl);
469
+ return { info, apiBaseUrl: baseUrl, fellBack: i > 0 };
470
+ } catch (err) {
471
+ lastError = err;
472
+ const isNotFound = err.code === "NOT_FOUND";
473
+ if (!isNotFound) {
474
+ allNotFound = false;
475
+ }
476
+ }
477
+ }
478
+ if (allNotFound) {
479
+ throw new Error(
480
+ `Connection "${connectionId}" not found in any known environment (${urls.join(", ")}). Check your dashboard.`
481
+ );
482
+ }
483
+ throw lastError || new Error("Failed to resolve store info");
454
484
  }
455
485
 
456
486
  // src/utils/logger.ts
@@ -502,9 +532,9 @@ function createSpinner(text) {
502
532
  var pkg = require_package();
503
533
  async function checkForUpdate(name, current) {
504
534
  try {
505
- const https2 = require("https");
535
+ const https = require("https");
506
536
  return await new Promise((resolve) => {
507
- const req = https2.get(
537
+ const req = https.get(
508
538
  `https://registry.npmjs.org/${name}/latest`,
509
539
  { timeout: 3e3 },
510
540
  (res) => {
@@ -531,7 +561,10 @@ async function checkForUpdate(name, current) {
531
561
  }
532
562
  }
533
563
  var program = new import_commander.Command();
534
- program.name("create-brainerce-store").description("Scaffold a production-ready e-commerce storefront connected to Brainerce").version(pkg.version).argument("[project-name]", "Name for the project directory").option("--connection-id <id>", "Brainerce vibe-coded connection ID (vc_*)").option("--language <lang>", "Store language (en, he)").option("--framework <framework>", "Framework to use", "nextjs").option("--theme <theme>", "Theme to apply", "minimal").option("--pkg-manager <manager>", "Package manager (npm, pnpm, yarn, bun)").option("--no-git", "Skip git initialization").option("--no-install", "Skip dependency installation").action(async (projectNameArg, options) => {
564
+ program.name("create-brainerce-store").description("Scaffold a production-ready e-commerce storefront connected to Brainerce").version(pkg.version).argument("[project-name]", "Name for the project directory").option("--connection-id <id>", "Brainerce vibe-coded connection ID (vc_*)").option(
565
+ "--api-url <url>",
566
+ "Brainerce API base URL (overrides BRAINERCE_API_URL env, defaults to https://api.brainerce.com)"
567
+ ).option("--language <lang>", "Store language (en, he)").option("--framework <framework>", "Framework to use", "nextjs").option("--theme <theme>", "Theme to apply", "minimal").option("--pkg-manager <manager>", "Package manager (npm, pnpm, yarn, bun)").option("--no-git", "Skip git initialization").option("--no-install", "Skip dependency installation").action(async (projectNameArg, options) => {
535
568
  try {
536
569
  logger.banner(pkg.version);
537
570
  const updateCheck = checkForUpdate(pkg.name, pkg.version);
@@ -542,6 +575,8 @@ program.name("create-brainerce-store").description("Scaffold a production-ready
542
575
  scaffoldInPlace = true;
543
576
  }
544
577
  let connectionId = options.connectionId;
578
+ const explicitApiUrl = (options.apiUrl || process.env.BRAINERCE_API_URL || "").replace(/\/$/, "");
579
+ const candidateApiUrls = explicitApiUrl ? [explicitApiUrl] : [KNOWN_API_URLS.production, KNOWN_API_URLS.staging];
545
580
  let language = options.language;
546
581
  let framework = options.framework;
547
582
  let theme = options.theme;
@@ -577,16 +612,23 @@ program.name("create-brainerce-store").description("Scaffold a production-ready
577
612
  const spinner = createSpinner("Fetching store info...");
578
613
  spinner.start();
579
614
  let storeInfo;
615
+ let resolvedApiUrl = candidateApiUrls[0];
580
616
  try {
581
- storeInfo = await fetchStoreInfo(connectionId);
617
+ const resolved = await resolveStoreInfo(connectionId, candidateApiUrls);
618
+ storeInfo = resolved.info;
619
+ resolvedApiUrl = resolved.apiBaseUrl;
620
+ const envLabel = resolved.apiBaseUrl === KNOWN_API_URLS.staging ? " [staging]" : resolved.apiBaseUrl === KNOWN_API_URLS.production ? "" : ` [${resolved.apiBaseUrl}]`;
582
621
  const i18nStatus = storeInfo.i18n?.enabled && storeInfo.i18n.supportedLocales.length > 1 ? ` | i18n: ${storeInfo.i18n.supportedLocales.join(", ")}` : "";
583
622
  spinner.succeed(
584
- `Store: "${storeInfo.name}" | ${storeInfo.currency} | ${storeInfo.language}${i18nStatus}`
623
+ `Store: "${storeInfo.name}" | ${storeInfo.currency} | ${storeInfo.language}${i18nStatus}${envLabel}`
585
624
  );
625
+ if (resolved.fellBack) {
626
+ logger.info(` Detected ${envLabel.trim() || "alternate"} environment \u2014 using ${resolved.apiBaseUrl}`);
627
+ }
586
628
  } catch (err) {
587
629
  spinner.fail("Could not fetch store info");
588
630
  logger.warn(
589
- "Using defaults. Make sure the connection ID is correct and the Brainerce API is reachable."
631
+ err instanceof Error ? err.message : "Using defaults. Make sure the connection ID is correct and the Brainerce API is reachable."
590
632
  );
591
633
  storeInfo = { name: projectName, currency: "USD", language: "en" };
592
634
  }
@@ -598,6 +640,7 @@ program.name("create-brainerce-store").description("Scaffold a production-ready
598
640
  await scaffold({
599
641
  projectName,
600
642
  connectionId,
643
+ apiBaseUrl: resolvedApiUrl,
601
644
  framework,
602
645
  theme,
603
646
  storeName: storeInfo.name,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-brainerce-store",
3
- "version": "1.22.0",
3
+ "version": "1.24.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",