@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 +84 -35
- package/dist/cli/commands/download.js +22 -39
- package/dist/cli/commands/download.js.map +1 -1
- package/dist/cli/commands/generate.js +140 -161
- package/dist/cli/commands/generate.js.map +1 -1
- package/dist/cli.js +21 -24
- package/dist/cli.js.map +1 -1
- package/dist/codegen/generator.js +126 -125
- package/dist/codegen/generator.js.map +1 -1
- package/dist/components/EncoreApp.js +157 -133
- package/dist/components/EncoreApp.js.map +1 -1
- package/dist/src/cli/commands/download.d.ts +1 -1
- package/dist/src/cli/commands/download.d.ts.map +1 -1
- package/dist/src/cli/commands/generate.d.ts +1 -1
- package/dist/src/cli/commands/generate.d.ts.map +1 -1
- package/dist/src/codegen/generator.d.ts +1 -1
- package/dist/src/codegen/generator.d.ts.map +1 -1
- package/dist/src/components/EncoreApp.d.ts +3 -1
- package/dist/src/components/EncoreApp.d.ts.map +1 -1
- package/dist/src/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/package.json +2 -1
- package/src/cli/commands/download.ts +8 -28
- package/src/cli/commands/generate.ts +44 -45
- package/src/cli/index.ts +49 -32
- package/src/codegen/generator.ts +6 -3
- package/src/components/EncoreApp.tsx +111 -21
- package/src/version.ts +1 -1
package/README.md
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
[](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
|
|
20
|
-
|
|
21
|
-
| TypeScript Interfaces | ā
Yes
|
|
22
|
-
| Form Handling
|
|
23
|
-
| Event Handlers
|
|
24
|
-
| List/Array Props
|
|
25
|
-
| Dropdown Controls
|
|
26
|
-
| Auto Documentation
|
|
27
|
-
| Production Bundles
|
|
28
|
-
| Live Updates
|
|
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
|
|
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(
|
|
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);
|
|
219
|
-
console.log(data.email);
|
|
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<
|
|
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<
|
|
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<
|
|
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(
|
|
266
|
-
const [country, setCountry] = useState(
|
|
267
|
-
const [salary, setSalary] = useState(
|
|
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={[
|
|
280
|
+
contractTypeSelectInputOptions={["full-time", "contractor", "part-time"]}
|
|
273
281
|
onContractTypeSelectInputChange={setContractType}
|
|
274
282
|
contractCountrySelectInput={country}
|
|
275
|
-
contractCountrySelectInputOptions={[
|
|
283
|
+
contractCountrySelectInputOptions={["US", "UK", "CA", "AU"]}
|
|
276
284
|
onContractCountrySelectInputChange={setCountry}
|
|
277
285
|
contractSalarySelectInput={salary}
|
|
278
|
-
contractSalarySelectInputOptions={[
|
|
286
|
+
contractSalarySelectInputOptions={["50000", "75000", "100000", "150000"]}
|
|
279
287
|
onContractSalarySelectInputChange={setSalary}
|
|
280
|
-
onBookButtonClick={() => console.log(
|
|
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
|
-
|
|
329
|
-
|
|
338
|
+
npx @bravostudioai/react generate <appId> [pageId] <outputPath> [options]
|
|
339
|
+
```
|
|
330
340
|
|
|
331
|
-
|
|
332
|
-
|
|
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
|
-
|
|
335
|
-
|
|
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<
|
|
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={
|
|
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
|
|
620
|
+
import { VERSION } from "@bravostudioai/react/version";
|
|
572
621
|
console.log(VERSION);
|
|
573
622
|
```
|
|
574
623
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { mkdir as
|
|
2
|
-
import { join as
|
|
3
|
-
import { existsSync as
|
|
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
|
|
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
|
|
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}`),
|
|
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: `${
|
|
26
|
+
url: `${l}/devices/apps/${e}`,
|
|
27
27
|
filename: "app.json",
|
|
28
28
|
headers: { "x-app-clientrendered": "true" }
|
|
29
29
|
},
|
|
30
30
|
{
|
|
31
|
-
url: `${
|
|
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: `${
|
|
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
|
|
44
|
-
await
|
|
45
|
-
} catch (
|
|
46
|
-
throw console.error(`ā Failed to download ${n.filename} at :`,
|
|
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
|
|
52
|
-
console.
|
|
53
|
-
Usage: download
|
|
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
|
|
74
|
-
} catch (
|
|
56
|
+
await E({ appId: e, pageId: r, targetPath: o });
|
|
57
|
+
} catch (s) {
|
|
75
58
|
console.error(`
|
|
76
|
-
Error:`,
|
|
59
|
+
Error:`, s instanceof Error ? s.message : s), process.exit(1);
|
|
77
60
|
}
|
|
78
61
|
}
|
|
79
62
|
export {
|
|
80
|
-
|
|
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\
|
|
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;"}
|