@bravostudioai/react 0.1.32 → 0.1.35

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/README.md CHANGED
@@ -7,6 +7,7 @@
7
7
  [![React](https://img.shields.io/badge/React-18%2B%20%7C%2019%2B-blue.svg)](https://reactjs.org/)
8
8
 
9
9
  **The Figma → React Pipeline:**
10
+
10
11
  - šŸŽØ Design in Figma → Stream to typed React components
11
12
  - šŸ¤– Perfect for MCP (Model Context Protocol) apps
12
13
  - šŸ”’ 100% TypeScript with full IntelliSense support
@@ -16,16 +17,16 @@
16
17
 
17
18
  **At a Glance:**
18
19
 
19
- | Feature | Included |
20
- |---------|----------|
21
- | TypeScript Interfaces | āœ… Yes |
22
- | Form Handling | āœ… Yes |
23
- | Event Handlers | āœ… Yes |
24
- | List/Array Props | āœ… Yes |
25
- | Dropdown Controls | āœ… Yes |
26
- | Auto Documentation | āœ… Yes |
27
- | Production Bundles | āœ… Yes |
28
- | Live Updates | āœ… Yes (via Pusher) |
20
+ | Feature | Included |
21
+ | --------------------- | ------------------- |
22
+ | TypeScript Interfaces | āœ… Yes |
23
+ | Form Handling | āœ… Yes |
24
+ | Event Handlers | āœ… Yes |
25
+ | List/Array Props | āœ… Yes |
26
+ | Dropdown Controls | āœ… Yes |
27
+ | Auto Documentation | āœ… Yes |
28
+ | Production Bundles | āœ… Yes |
29
+ | Live Updates | āœ… Yes (via Pusher) |
29
30
 
30
31
  ## Table of Contents
31
32
 
@@ -88,6 +89,7 @@ npm install @bravostudioai/react
88
89
  ```
89
90
 
90
91
  **Requirements:**
92
+
91
93
  - Node.js 16+
92
94
  - React 18.2.0+ or 19.0.0+
93
95
  - TypeScript (recommended)
@@ -124,7 +126,7 @@ src/components/
124
126
  ### Use Your Generated Component
125
127
 
126
128
  ```tsx
127
- import { PageName } from './components/YourApp/PageName';
129
+ import { PageName } from "./components/YourApp/PageName";
128
130
 
129
131
  function MCPApp() {
130
132
  return (
@@ -132,9 +134,9 @@ function MCPApp() {
132
134
  headline="Welcome to our app"
133
135
  items={[
134
136
  { title: "Item 1", image: "https://..." },
135
- { title: "Item 2", image: "https://..." }
137
+ { title: "Item 2", image: "https://..." },
136
138
  ]}
137
- onSubmitClick={() => console.log('Submitted!')}
139
+ onSubmitClick={() => console.log("Submitted!")}
138
140
  />
139
141
  );
140
142
  }
@@ -187,7 +189,7 @@ Use it like this:
187
189
  <Page
188
190
  timeline={[
189
191
  { title: "Step 1", description: "...", image: "..." },
190
- { title: "Step 2", description: "...", image: "..." }
192
+ { title: "Step 2", description: "...", image: "..." },
191
193
  ]}
192
194
  timelineCurrentIndex={currentSlide}
193
195
  onTimelineIndexChange={setCurrentSlide}
@@ -215,8 +217,8 @@ Your form handler receives clean, typed data:
215
217
  ```tsx
216
218
  <Page
217
219
  onContactFormSubmit={(data) => {
218
- console.log(data.name); // Type-safe!
219
- console.log(data.email); // Type-safe!
220
+ console.log(data.name); // Type-safe!
221
+ console.log(data.email); // Type-safe!
220
222
  console.log(data.message); // Type-safe!
221
223
  }}
222
224
  />
@@ -243,15 +245,21 @@ export interface DeelSalaryCalculatorProps {
243
245
 
244
246
  // Three interconnected dropdowns
245
247
  contractTypeSelectInput?: string;
246
- contractTypeSelectInputOptions?: Array<string | { value: string; label: string }>;
248
+ contractTypeSelectInputOptions?: Array<
249
+ string | { value: string; label: string }
250
+ >;
247
251
  onContractTypeSelectInputChange?: (value: string) => void;
248
252
 
249
253
  contractCountrySelectInput?: string;
250
- contractCountrySelectInputOptions?: Array<string | { value: string; label: string }>;
254
+ contractCountrySelectInputOptions?: Array<
255
+ string | { value: string; label: string }
256
+ >;
251
257
  onContractCountrySelectInputChange?: (value: string) => void;
252
258
 
253
259
  contractSalarySelectInput?: string;
254
- contractSalarySelectInputOptions?: Array<string | { value: string; label: string }>;
260
+ contractSalarySelectInputOptions?: Array<
261
+ string | { value: string; label: string }
262
+ >;
255
263
  onContractSalarySelectInputChange?: (value: string) => void;
256
264
 
257
265
  onBookButtonClick?: () => void;
@@ -262,22 +270,22 @@ export interface DeelSalaryCalculatorProps {
262
270
 
263
271
  ```tsx
264
272
  function SalaryCalculator() {
265
- const [contractType, setContractType] = useState('full-time');
266
- const [country, setCountry] = useState('US');
267
- const [salary, setSalary] = useState('100000');
273
+ const [contractType, setContractType] = useState("full-time");
274
+ const [country, setCountry] = useState("US");
275
+ const [salary, setSalary] = useState("100000");
268
276
 
269
277
  return (
270
278
  <DeelSalaryCalculator
271
279
  contractTypeSelectInput={contractType}
272
- contractTypeSelectInputOptions={['full-time', 'contractor', 'part-time']}
280
+ contractTypeSelectInputOptions={["full-time", "contractor", "part-time"]}
273
281
  onContractTypeSelectInputChange={setContractType}
274
282
  contractCountrySelectInput={country}
275
- contractCountrySelectInputOptions={['US', 'UK', 'CA', 'AU']}
283
+ contractCountrySelectInputOptions={["US", "UK", "CA", "AU"]}
276
284
  onContractCountrySelectInputChange={setCountry}
277
285
  contractSalarySelectInput={salary}
278
- contractSalarySelectInputOptions={['50000', '75000', '100000', '150000']}
286
+ contractSalarySelectInputOptions={["50000", "75000", "100000", "150000"]}
279
287
  onContractSalarySelectInputChange={setSalary}
280
- onBookButtonClick={() => console.log('Booking consultation')}
288
+ onBookButtonClick={() => console.log("Booking consultation")}
281
289
  />
282
290
  );
283
291
  }
@@ -324,15 +332,43 @@ Bravo Studio AI processes your Figma designs and the generator:
324
332
 
325
333
  ### Generate Components
326
334
 
335
+ Stream your Figma designs into React components.
336
+
327
337
  ```bash
328
- # Generate all pages from an app
329
- npx @bravostudioai/react generate <appId> <outputPath>
338
+ npx @bravostudioai/react generate <appId> [pageId] <outputPath> [options]
339
+ ```
330
340
 
331
- # Generate a specific page
332
- npx @bravostudioai/react generate <appId> <pageId> <outputPath>
341
+ **Arguments:**
342
+
343
+ - `appId`: The ID of your Bravo Studio application
344
+ - `pageId` (optional): The ID of a specific page to generate. If omitted, all pages in the app are generated.
345
+ - `outputPath`: Directory where the generated components will be saved.
346
+
347
+ **Options:**
348
+
349
+ - `--mode <mode>`: Controls data fetching strategy. Options: `dynamic` (default), `optimistic`, `production`.
350
+
351
+ #### Deployment Modes
352
+
353
+ The `--mode` flag is powerful. It lets you decide how your components handle data:
354
+
355
+ | Mode | Description | Best For |
356
+ | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------- |
357
+ | **dynamic** | **(Default)** Component fetches fresh data from Bravo servers on mount. Ensures users always see the latest design updates without redeploying. | Live apps requiring real-time design updates. |
358
+ | **optimistic** | **(Hybrid)** Bundles the latest data at build time for instant initial render, then checks for updates in the background. If updates exist, it refreshes silently. | Production apps where detailed performance and freshness are both critical. |
359
+ | **production** | **(Offline)** Bundles all data at build time. No network requests to Bravo servers. The component is "frozen" until you regenerate it. | Offline-first apps, high-security environments, or stable releases. |
360
+
361
+ #### Examples
333
362
 
334
- # Generate with bundled production data (offline mode)
335
- npx @bravostudioai/react generate <appId> <pageId> <outputPath> --production
363
+ ```bash
364
+ # Generate all pages in Dynamic mode (Default)
365
+ npx @bravostudioai/react generate <appId> ./src/components
366
+
367
+ # Generate a specific page in Optimistic mode (Instant load + Live updates)
368
+ npx @bravostudioai/react generate <appId> <pageId> ./src/components --mode optimistic
369
+
370
+ # Generate for Production (Zero network requests)
371
+ npx @bravostudioai/react generate <appId> <pageId> ./src/components --mode production
336
372
  ```
337
373
 
338
374
  ### Download Raw Data
@@ -459,17 +495,21 @@ MCP applications need real UI components that AI agents can reason about and man
459
495
  Here's what the generator creates for a real-world salary calculator:
460
496
 
461
497
  **Generated TypeScript Interface:**
498
+
462
499
  ```typescript
463
500
  export interface DeelSalaryCalculatorProps {
464
501
  estimatedCost?: string;
465
502
  contractTypeSelectInput?: string;
466
- contractTypeSelectInputOptions?: Array<string | { value: string; label: string }>;
503
+ contractTypeSelectInputOptions?: Array<
504
+ string | { value: string; label: string }
505
+ >;
467
506
  onContractTypeSelectInputChange?: (value: string) => void;
468
507
  // ... and more typed props
469
508
  }
470
509
  ```
471
510
 
472
511
  **Generated Component:**
512
+
473
513
  ```typescript
474
514
  export function DeelSalaryCalculator(props: DeelSalaryCalculatorProps) {
475
515
  const handleAction = (payload: any) => {
@@ -486,7 +526,11 @@ export function DeelSalaryCalculator(props: DeelSalaryCalculatorProps) {
486
526
  <EncoreApp
487
527
  appId="..."
488
528
  pageId="..."
489
- data={{ /* auto-mapped props */ }}
529
+ data={
530
+ {
531
+ /* auto-mapped props */
532
+ }
533
+ }
490
534
  onAction={handleAction}
491
535
  />
492
536
  );
@@ -521,25 +565,30 @@ The generated code looks like code you'd write by hand. Clean, idiomatic React.
521
565
  ### Common Issues
522
566
 
523
567
  **Q: The generated component shows "Failed to load"**
568
+
524
569
  - Ensure your `appId` and `pageId` are correct
525
570
  - Check that you have network access to the Bravo Studio AI API
526
571
  - For offline use, use `--production` flag during generation
527
572
 
528
573
  **Q: TypeScript errors in generated code**
574
+
529
575
  - Make sure you have `@types/react` installed
530
576
  - Verify your TypeScript version is 5.0+
531
577
  - Try regenerating the component with the latest package version
532
578
 
533
579
  **Q: Props not updating the component**
580
+
534
581
  - Remember that data bindings need to be properly tagged in your Figma design
535
582
  - Check the generated README.md for the exact prop names
536
583
  - Verify the component is receiving props correctly using React DevTools
537
584
 
538
585
  **Q: How do I get my App ID and Page ID?**
586
+
539
587
  - Contact your Bravo Studio AI team for access credentials
540
588
  - IDs are provided when your Figma designs are processed through Bravo Studio AI
541
589
 
542
590
  **Q: Can I use this with regular Figma files?**
591
+
543
592
  - Figma files must be processed through Bravo Studio AI first
544
593
  - This adds the necessary metadata and structure for component generation
545
594
 
@@ -568,7 +617,7 @@ npm list @bravostudioai/react
568
617
  Or programmatically:
569
618
 
570
619
  ```typescript
571
- import { VERSION } from '@bravostudioai/react/version';
620
+ import { VERSION } from "@bravostudioai/react/version";
572
621
  console.log(VERSION);
573
622
  ```
574
623
 
@@ -1,10 +1,10 @@
1
- import { mkdir as c, writeFile as d } from "fs/promises";
2
- import { join as p } from "path";
3
- import { existsSync as f } from "fs";
1
+ import { mkdir as a, writeFile as c } from "fs/promises";
2
+ import { join as f } from "path";
3
+ import { existsSync as d } from "fs";
4
4
  import m from "../../_virtual/main.js";
5
- import { CONST_APPS_SERVICE_URL as u, CONST_COMPONENTS_CDN_URL as E } from "../../packages/encore-lib/constants.js";
5
+ import { CONST_APPS_SERVICE_URL as u, CONST_COMPONENTS_CDN_URL as $ } from "../../packages/encore-lib/constants.js";
6
6
  m.config();
7
- const i = process.env.VITE_APPS_SERVICE_URL || u, h = E;
7
+ const l = process.env.VITE_APPS_SERVICE_URL || u, p = $;
8
8
  async function w(e, r) {
9
9
  const o = await fetch(e, {
10
10
  headers: r || {}
@@ -15,68 +15,51 @@ async function w(e, r) {
15
15
  );
16
16
  return await o.text();
17
17
  }
18
- async function $({
18
+ async function E({
19
19
  appId: e,
20
20
  pageId: r,
21
21
  targetPath: o
22
22
  }) {
23
- console.log(`Downloading Encore files for app: ${e}, page: ${r}`), console.log(`Target path: ${o}`), f(o) || (await c(o, { recursive: !0 }), console.log(`Created directory: ${o}`));
23
+ console.log(`Downloading Encore files for app: ${e}, page: ${r}`), console.log(`Target path: ${o}`), d(o) || (await a(o, { recursive: !0 }), console.log(`Created directory: ${o}`));
24
24
  const s = [
25
25
  {
26
- url: `${i}/devices/apps/${e}`,
26
+ url: `${l}/devices/apps/${e}`,
27
27
  filename: "app.json",
28
28
  headers: { "x-app-clientrendered": "true" }
29
29
  },
30
30
  {
31
- url: `${i}/devices/apps/${e}/node/${r}`,
31
+ url: `${l}/devices/apps/${e}/node/${r}`,
32
32
  filename: "page.json",
33
33
  headers: { "x-app-clientrendered": "true" }
34
34
  },
35
35
  {
36
- url: `${h}/${e}/draft/components/${r}.js`,
36
+ url: `${p}/${e}/draft/components/${r}.js`,
37
37
  filename: "component.js"
38
38
  }
39
39
  ];
40
40
  for (const n of s)
41
41
  try {
42
42
  console.log(`Downloading ${n.filename} from ${n.url}...`);
43
- const a = await w(n.url, n.headers), t = p(o, n.filename);
44
- await d(t, a, "utf-8"), console.log(`āœ“ Saved ${n.filename} to ${t}`);
45
- } catch (a) {
46
- throw console.error(`āœ— Failed to download ${n.filename} at :`, a), a;
43
+ const i = await w(n.url, n.headers), t = f(o, n.filename);
44
+ await c(t, i, "utf-8"), console.log(`āœ“ Saved ${n.filename} to ${t}`);
45
+ } catch (i) {
46
+ throw console.error(`āœ— Failed to download ${n.filename} at :`, i), i;
47
47
  }
48
48
  console.log(`
49
49
  āœ“ All files downloaded successfully!`);
50
50
  }
51
- function l() {
52
- console.log(`
53
- Usage: download-bravo.ts <appId> <pageId> <targetPath>
54
-
55
- Arguments:
56
- appId The Encore app ID
57
- pageId The Encore page ID
58
- targetPath Path where files should be saved
59
-
60
- Environment variables:
61
- APPS_SERVICE_URL Base URL for the apps service
62
-
63
- Example:
64
- download-bravo.ts my-app-id my-page-id ./bravo-files
65
- APPS_SERVICE_URL=https://api.example.com download-bravo.ts my-app-id my-page-id ./bravo-files
66
- `);
67
- }
68
- async function R(e) {
69
- (e.length < 3 || e.includes("--help") || e.includes("-h")) && (l(), process.exit(e.includes("--help") || e.includes("-h") ? 0 : 1));
70
- const [r, o, s] = e;
71
- (!r || !o || !s) && (console.error("Error: Missing required arguments"), l(), process.exit(1));
51
+ async function y(e, r, o) {
52
+ (!e || !r || !o) && (console.error(
53
+ "Error: Missing required arguments. Usage: download <appId> <pageId> <targetPath>"
54
+ ), process.exit(1));
72
55
  try {
73
- await $({ appId: r, pageId: o, targetPath: s });
74
- } catch (n) {
56
+ await E({ appId: e, pageId: r, targetPath: o });
57
+ } catch (s) {
75
58
  console.error(`
76
- Error:`, n instanceof Error ? n.message : n), process.exit(1);
59
+ Error:`, s instanceof Error ? s.message : s), process.exit(1);
77
60
  }
78
61
  }
79
62
  export {
80
- R as runDownload
63
+ y as runDownload
81
64
  };
82
65
  //# sourceMappingURL=download.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"download.js","sources":["../../../src/cli/commands/download.ts"],"sourcesContent":["import { writeFile, mkdir } from \"fs/promises\";\nimport { join } from \"path\";\nimport { existsSync } from \"fs\";\nimport dotenv from \"dotenv\";\nimport {\n CONST_APPS_SERVICE_URL,\n CONST_COMPONENTS_CDN_URL,\n} from \"../../../constants\";\n\ndotenv.config();\n\n// Default apps service URL (can be overridden with APPS_SERVICE_URL env var)\nconst APPS_SERVICE_URL =\n process.env.VITE_APPS_SERVICE_URL || CONST_APPS_SERVICE_URL;\nconst COMPONENTS_CDN_URL = CONST_COMPONENTS_CDN_URL;\n\ninterface DownloadOptions {\n appId: string;\n pageId: string;\n targetPath: string;\n}\n\nasync function downloadFile(\n url: string,\n headers?: Record<string, string>\n): Promise<string> {\n const response = await fetch(url, {\n headers: headers || {},\n });\n\n if (!response.ok) {\n throw new Error(\n `Failed to download ${url}: ${response.status} ${response.statusText}`\n );\n }\n\n return await response.text();\n}\n\nasync function downloadEncoreFiles({\n appId,\n pageId,\n targetPath,\n}: DownloadOptions) {\n console.log(`Downloading Encore files for app: ${appId}, page: ${pageId}`);\n console.log(`Target path: ${targetPath}`);\n\n // Ensure target directory exists\n if (!existsSync(targetPath)) {\n await mkdir(targetPath, { recursive: true });\n console.log(`Created directory: ${targetPath}`);\n }\n\n const files: Array<{\n url: string;\n filename: string;\n headers?: Record<string, string>;\n }> = [\n {\n url: `${APPS_SERVICE_URL}/devices/apps/${appId}`,\n filename: \"app.json\",\n headers: { \"x-app-clientrendered\": \"true\" },\n },\n {\n url: `${APPS_SERVICE_URL}/devices/apps/${appId}/node/${pageId}`,\n filename: \"page.json\",\n headers: { \"x-app-clientrendered\": \"true\" },\n },\n {\n url: `${COMPONENTS_CDN_URL}/${appId}/draft/components/${pageId}.js`,\n filename: \"component.js\",\n },\n ];\n\n for (const file of files) {\n try {\n console.log(`Downloading ${file.filename} from ${file.url}...`);\n const content = await downloadFile(file.url, file.headers);\n const filePath = join(targetPath, file.filename);\n await writeFile(filePath, content, \"utf-8\");\n console.log(`āœ“ Saved ${file.filename} to ${filePath}`);\n } catch (error) {\n console.error(`āœ— Failed to download ${file.filename} at :`, error);\n throw error;\n }\n }\n\n console.log(\"\\nāœ“ All files downloaded successfully!\");\n}\n\nfunction printUsage() {\n console.log(`\nUsage: download-bravo.ts <appId> <pageId> <targetPath>\n\nArguments:\n appId The Encore app ID\n pageId The Encore page ID\n targetPath Path where files should be saved\n\nEnvironment variables:\n APPS_SERVICE_URL Base URL for the apps service\n\nExample:\n download-bravo.ts my-app-id my-page-id ./bravo-files\n APPS_SERVICE_URL=https://api.example.com download-bravo.ts my-app-id my-page-id ./bravo-files\n`);\n}\n\nexport async function runDownload(args: string[]) {\n if (args.length < 3 || args.includes(\"--help\") || args.includes(\"-h\")) {\n printUsage();\n process.exit(args.includes(\"--help\") || args.includes(\"-h\") ? 0 : 1);\n }\n\n const [appId, pageId, targetPath] = args;\n\n if (!appId || !pageId || !targetPath) {\n console.error(\"Error: Missing required arguments\");\n printUsage();\n process.exit(1);\n }\n\n try {\n await downloadEncoreFiles({ appId, pageId, targetPath });\n } catch (error) {\n console.error(\"\\nError:\", error instanceof Error ? error.message : error);\n process.exit(1);\n }\n}\n"],"names":["dotenv","APPS_SERVICE_URL","CONST_APPS_SERVICE_URL","COMPONENTS_CDN_URL","CONST_COMPONENTS_CDN_URL","downloadFile","url","headers","response","downloadEncoreFiles","appId","pageId","targetPath","existsSync","mkdir","files","file","content","filePath","join","writeFile","error","printUsage","runDownload","args"],"mappings":";;;;;AASAA,EAAO,OAAA;AAGP,MAAMC,IACJ,QAAQ,IAAI,yBAAyBC,GACjCC,IAAqBC;AAQ3B,eAAeC,EACbC,GACAC,GACiB;AACjB,QAAMC,IAAW,MAAM,MAAMF,GAAK;AAAA,IAChC,SAASC,KAAW,CAAA;AAAA,EAAC,CACtB;AAED,MAAI,CAACC,EAAS;AACZ,UAAM,IAAI;AAAA,MACR,sBAAsBF,CAAG,KAAKE,EAAS,MAAM,IAAIA,EAAS,UAAU;AAAA,IAAA;AAIxE,SAAO,MAAMA,EAAS,KAAA;AACxB;AAEA,eAAeC,EAAoB;AAAA,EACjC,OAAAC;AAAA,EACA,QAAAC;AAAA,EACA,YAAAC;AACF,GAAoB;AAClB,UAAQ,IAAI,qCAAqCF,CAAK,WAAWC,CAAM,EAAE,GACzE,QAAQ,IAAI,gBAAgBC,CAAU,EAAE,GAGnCC,EAAWD,CAAU,MACxB,MAAME,EAAMF,GAAY,EAAE,WAAW,IAAM,GAC3C,QAAQ,IAAI,sBAAsBA,CAAU,EAAE;AAGhD,QAAMG,IAID;AAAA,IACH;AAAA,MACE,KAAK,GAAGd,CAAgB,iBAAiBS,CAAK;AAAA,MAC9C,UAAU;AAAA,MACV,SAAS,EAAE,wBAAwB,OAAA;AAAA,IAAO;AAAA,IAE5C;AAAA,MACE,KAAK,GAAGT,CAAgB,iBAAiBS,CAAK,SAASC,CAAM;AAAA,MAC7D,UAAU;AAAA,MACV,SAAS,EAAE,wBAAwB,OAAA;AAAA,IAAO;AAAA,IAE5C;AAAA,MACE,KAAK,GAAGR,CAAkB,IAAIO,CAAK,qBAAqBC,CAAM;AAAA,MAC9D,UAAU;AAAA,IAAA;AAAA,EACZ;AAGF,aAAWK,KAAQD;AACjB,QAAI;AACF,cAAQ,IAAI,eAAeC,EAAK,QAAQ,SAASA,EAAK,GAAG,KAAK;AAC9D,YAAMC,IAAU,MAAMZ,EAAaW,EAAK,KAAKA,EAAK,OAAO,GACnDE,IAAWC,EAAKP,GAAYI,EAAK,QAAQ;AAC/C,YAAMI,EAAUF,GAAUD,GAAS,OAAO,GAC1C,QAAQ,IAAI,WAAWD,EAAK,QAAQ,OAAOE,CAAQ,EAAE;AAAA,IACvD,SAASG,GAAO;AACd,oBAAQ,MAAM,wBAAwBL,EAAK,QAAQ,SAASK,CAAK,GAC3DA;AAAA,IACR;AAGF,UAAQ,IAAI;AAAA,qCAAwC;AACtD;AAEA,SAASC,IAAa;AACpB,UAAQ,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAcb;AACD;AAEA,eAAsBC,EAAYC,GAAgB;AAChD,GAAIA,EAAK,SAAS,KAAKA,EAAK,SAAS,QAAQ,KAAKA,EAAK,SAAS,IAAI,OAClEF,EAAA,GACA,QAAQ,KAAKE,EAAK,SAAS,QAAQ,KAAKA,EAAK,SAAS,IAAI,IAAI,IAAI,CAAC;AAGrE,QAAM,CAACd,GAAOC,GAAQC,CAAU,IAAIY;AAEpC,GAAI,CAACd,KAAS,CAACC,KAAU,CAACC,OACxB,QAAQ,MAAM,mCAAmC,GACjDU,EAAA,GACA,QAAQ,KAAK,CAAC;AAGhB,MAAI;AACF,UAAMb,EAAoB,EAAE,OAAAC,GAAO,QAAAC,GAAQ,YAAAC,GAAY;AAAA,EACzD,SAASS,GAAO;AACd,YAAQ,MAAM;AAAA,SAAYA,aAAiB,QAAQA,EAAM,UAAUA,CAAK,GACxE,QAAQ,KAAK,CAAC;AAAA,EAChB;AACF;"}
1
+ {"version":3,"file":"download.js","sources":["../../../src/cli/commands/download.ts"],"sourcesContent":["import { writeFile, mkdir } from \"fs/promises\";\nimport { join } from \"path\";\nimport { existsSync } from \"fs\";\nimport dotenv from \"dotenv\";\nimport {\n CONST_APPS_SERVICE_URL,\n CONST_COMPONENTS_CDN_URL,\n} from \"../../../constants\";\n\ndotenv.config();\n\n// Default apps service URL (can be overridden with APPS_SERVICE_URL env var)\nconst APPS_SERVICE_URL =\n process.env.VITE_APPS_SERVICE_URL || CONST_APPS_SERVICE_URL;\nconst COMPONENTS_CDN_URL = CONST_COMPONENTS_CDN_URL;\n\ninterface DownloadOptions {\n appId: string;\n pageId: string;\n targetPath: string;\n}\n\nasync function downloadFile(\n url: string,\n headers?: Record<string, string>\n): Promise<string> {\n const response = await fetch(url, {\n headers: headers || {},\n });\n\n if (!response.ok) {\n throw new Error(\n `Failed to download ${url}: ${response.status} ${response.statusText}`\n );\n }\n\n return await response.text();\n}\n\nasync function downloadEncoreFiles({\n appId,\n pageId,\n targetPath,\n}: DownloadOptions) {\n console.log(`Downloading Encore files for app: ${appId}, page: ${pageId}`);\n console.log(`Target path: ${targetPath}`);\n\n // Ensure target directory exists\n if (!existsSync(targetPath)) {\n await mkdir(targetPath, { recursive: true });\n console.log(`Created directory: ${targetPath}`);\n }\n\n const files: Array<{\n url: string;\n filename: string;\n headers?: Record<string, string>;\n }> = [\n {\n url: `${APPS_SERVICE_URL}/devices/apps/${appId}`,\n filename: \"app.json\",\n headers: { \"x-app-clientrendered\": \"true\" },\n },\n {\n url: `${APPS_SERVICE_URL}/devices/apps/${appId}/node/${pageId}`,\n filename: \"page.json\",\n headers: { \"x-app-clientrendered\": \"true\" },\n },\n {\n url: `${COMPONENTS_CDN_URL}/${appId}/draft/components/${pageId}.js`,\n filename: \"component.js\",\n },\n ];\n\n for (const file of files) {\n try {\n console.log(`Downloading ${file.filename} from ${file.url}...`);\n const content = await downloadFile(file.url, file.headers);\n const filePath = join(targetPath, file.filename);\n await writeFile(filePath, content, \"utf-8\");\n console.log(`āœ“ Saved ${file.filename} to ${filePath}`);\n } catch (error) {\n console.error(`āœ— Failed to download ${file.filename} at :`, error);\n throw error;\n }\n }\n\n console.log(\"\\nāœ“ All files downloaded successfully!\");\n}\n\nexport async function runDownload(\n appId: string,\n pageId: string,\n targetPath: string\n) {\n if (!appId || !pageId || !targetPath) {\n console.error(\n \"Error: Missing required arguments. Usage: download <appId> <pageId> <targetPath>\"\n );\n process.exit(1);\n }\n\n try {\n await downloadEncoreFiles({ appId, pageId, targetPath });\n } catch (error) {\n console.error(\"\\nError:\", error instanceof Error ? error.message : error);\n process.exit(1);\n }\n}\n"],"names":["dotenv","APPS_SERVICE_URL","CONST_APPS_SERVICE_URL","COMPONENTS_CDN_URL","CONST_COMPONENTS_CDN_URL","downloadFile","url","headers","response","downloadEncoreFiles","appId","pageId","targetPath","existsSync","mkdir","files","file","content","filePath","join","writeFile","error","runDownload"],"mappings":";;;;;AASAA,EAAO,OAAA;AAGP,MAAMC,IACJ,QAAQ,IAAI,yBAAyBC,GACjCC,IAAqBC;AAQ3B,eAAeC,EACbC,GACAC,GACiB;AACjB,QAAMC,IAAW,MAAM,MAAMF,GAAK;AAAA,IAChC,SAASC,KAAW,CAAA;AAAA,EAAC,CACtB;AAED,MAAI,CAACC,EAAS;AACZ,UAAM,IAAI;AAAA,MACR,sBAAsBF,CAAG,KAAKE,EAAS,MAAM,IAAIA,EAAS,UAAU;AAAA,IAAA;AAIxE,SAAO,MAAMA,EAAS,KAAA;AACxB;AAEA,eAAeC,EAAoB;AAAA,EACjC,OAAAC;AAAA,EACA,QAAAC;AAAA,EACA,YAAAC;AACF,GAAoB;AAClB,UAAQ,IAAI,qCAAqCF,CAAK,WAAWC,CAAM,EAAE,GACzE,QAAQ,IAAI,gBAAgBC,CAAU,EAAE,GAGnCC,EAAWD,CAAU,MACxB,MAAME,EAAMF,GAAY,EAAE,WAAW,IAAM,GAC3C,QAAQ,IAAI,sBAAsBA,CAAU,EAAE;AAGhD,QAAMG,IAID;AAAA,IACH;AAAA,MACE,KAAK,GAAGd,CAAgB,iBAAiBS,CAAK;AAAA,MAC9C,UAAU;AAAA,MACV,SAAS,EAAE,wBAAwB,OAAA;AAAA,IAAO;AAAA,IAE5C;AAAA,MACE,KAAK,GAAGT,CAAgB,iBAAiBS,CAAK,SAASC,CAAM;AAAA,MAC7D,UAAU;AAAA,MACV,SAAS,EAAE,wBAAwB,OAAA;AAAA,IAAO;AAAA,IAE5C;AAAA,MACE,KAAK,GAAGR,CAAkB,IAAIO,CAAK,qBAAqBC,CAAM;AAAA,MAC9D,UAAU;AAAA,IAAA;AAAA,EACZ;AAGF,aAAWK,KAAQD;AACjB,QAAI;AACF,cAAQ,IAAI,eAAeC,EAAK,QAAQ,SAASA,EAAK,GAAG,KAAK;AAC9D,YAAMC,IAAU,MAAMZ,EAAaW,EAAK,KAAKA,EAAK,OAAO,GACnDE,IAAWC,EAAKP,GAAYI,EAAK,QAAQ;AAC/C,YAAMI,EAAUF,GAAUD,GAAS,OAAO,GAC1C,QAAQ,IAAI,WAAWD,EAAK,QAAQ,OAAOE,CAAQ,EAAE;AAAA,IACvD,SAASG,GAAO;AACd,oBAAQ,MAAM,wBAAwBL,EAAK,QAAQ,SAASK,CAAK,GAC3DA;AAAA,IACR;AAGF,UAAQ,IAAI;AAAA,qCAAwC;AACtD;AAEA,eAAsBC,EACpBZ,GACAC,GACAC,GACA;AACA,GAAI,CAACF,KAAS,CAACC,KAAU,CAACC,OACxB,QAAQ;AAAA,IACN;AAAA,EAAA,GAEF,QAAQ,KAAK,CAAC;AAGhB,MAAI;AACF,UAAMH,EAAoB,EAAE,OAAAC,GAAO,QAAAC,GAAQ,YAAAC,GAAY;AAAA,EACzD,SAASS,GAAO;AACd,YAAQ,MAAM;AAAA,SAAYA,aAAiB,QAAQA,EAAM,UAAUA,CAAK,GACxE,QAAQ,KAAK,CAAC;AAAA,EAChB;AACF;"}