@fireberry/cli 0.2.0 → 0.2.2

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/CLAUDE.md CHANGED
@@ -107,7 +107,7 @@ Component utilities in [src/utils/components.utils.ts](src/utils/components.util
107
107
  # For global-menu type:
108
108
  displayName: "Menu Name"
109
109
  # For side-menu type:
110
- icon: "icon-name"
110
+ iconName: "icon-name"
111
111
  width: "S" | "M" | "L"
112
112
  ```
113
113
 
@@ -150,7 +150,7 @@ Component paths in manifest.yml are relative to the current working directory, n
150
150
  - **Component Settings Validation**: Each component type has required settings that are validated during `push`:
151
151
  - `record`: Must have `iconName`, `iconColor`, and `objectType`
152
152
  - `global-menu`: Must have `displayName`
153
- - `side-menu`: Must have `icon` and `width` (S/M/L)
153
+ - `side-menu`: Must have `iconName` and `width` (S/M/L)
154
154
  - **Build Zipping**: Single files are wrapped in a directory before tar.gz creation
155
155
  - **Template Location**: Templates are resolved from `src/templates/` at compile time, copied to `dist/templates/`
156
156
  - **Delete Safety**: Delete command requires user confirmation before executing (cannot be undone)
@@ -4,3 +4,4 @@ export declare const createApp: (manifest: Manifest) => Promise<void>;
4
4
  export declare const pushComponents: (appId: string, components: ZippedComponent[], manifest: Manifest) => Promise<void>;
5
5
  export declare const installApp: (manifest: Manifest) => Promise<void>;
6
6
  export declare const deleteApp: (manifest: Manifest) => Promise<void>;
7
+ export declare const updateDebug: (componentId: string, manifest: Manifest, debugUrl?: string) => Promise<void>;
@@ -37,3 +37,12 @@ export const deleteApp = async (manifest) => {
37
37
  throw new Error(error instanceof Error ? error.message : "Unknown error");
38
38
  }
39
39
  };
40
+ export const updateDebug = async (componentId, manifest, debugUrl) => {
41
+ const url = `${BASE_SERVICE_URL}/debug`;
42
+ try {
43
+ await api.post(url, { componentId, debugUrl, manifest });
44
+ }
45
+ catch (error) {
46
+ throw new Error(error instanceof Error ? error.message : "Unknown error");
47
+ }
48
+ };
@@ -27,9 +27,10 @@ export interface RecordComponentSettings {
27
27
  }
28
28
  export interface GlobalMenuComponentSettings {
29
29
  displayName: string;
30
+ iconName?: string;
30
31
  }
31
32
  export interface SideMenuComponentSettings {
32
- icon: string;
33
+ iconName: string;
33
34
  width: "S" | "M" | "L";
34
35
  }
35
36
  export type ComponentSettings = RecordComponentSettings | GlobalMenuComponentSettings | SideMenuComponentSettings;
@@ -7,6 +7,7 @@ import packageJson from "../../package.json" with { type: "json" };
7
7
  import { runPush } from "../commands/push.js";
8
8
  import { runInstall } from "../commands/install.js";
9
9
  import { runDelete } from "../commands/delete.js";
10
+ import { runDebug } from "../commands/debug.js";
10
11
  const program = new Command();
11
12
  program
12
13
  .name("fireberry")
@@ -44,6 +45,15 @@ program
44
45
  .action(async () => {
45
46
  await runDelete();
46
47
  });
48
+ program
49
+ .command("debug")
50
+ .argument("<component-id>", "Component ID to debug")
51
+ .argument("[url]", "Debug URL in format localhost:[port]")
52
+ .option("--stop", "Stop debugging the component")
53
+ .description("Start or stop debugging a component")
54
+ .action(async (componentId, url, options) => {
55
+ await runDebug(componentId, url, options);
56
+ });
47
57
  program.parseAsync(process.argv).catch((err) => {
48
58
  const errorMessage = err instanceof Error
49
59
  ? err.message
@@ -0,0 +1,3 @@
1
+ export declare function runDebug(componentId: string, url?: string, options?: {
2
+ stop?: boolean;
3
+ }): Promise<void>;
@@ -0,0 +1,51 @@
1
+ import ora from "ora";
2
+ import chalk from "chalk";
3
+ import { updateDebug } from "../api/requests.js";
4
+ import { getManifest } from "../utils/components.utils.js";
5
+ function validateDebugUrl(url) {
6
+ const localhostPattern = /^localhost:\d+$/;
7
+ if (!localhostPattern.test(url)) {
8
+ throw new Error("Invalid URL format. URL must be in format: localhost:[port] (e.g., localhost:3000)\n" +
9
+ "Do not include http:// or https://");
10
+ }
11
+ }
12
+ function validateComponentExists(manifest, componentId) {
13
+ const component = manifest.components?.find((comp) => comp.id.toLowerCase() === componentId.toLowerCase());
14
+ if (!component) {
15
+ throw new Error(`Component with ID "${componentId}" not found in manifest.\n` +
16
+ `Available components:\n` +
17
+ manifest.components
18
+ ?.map((comp) => ` - ${comp.title} (${comp.id})`)
19
+ .join("\n"));
20
+ }
21
+ }
22
+ export async function runDebug(componentId, url, options) {
23
+ const spinner = ora("Loading manifest...").start();
24
+ try {
25
+ const manifest = await getManifest();
26
+ spinner.succeed("Manifest loaded");
27
+ validateComponentExists(manifest, componentId);
28
+ if (options?.stop) {
29
+ spinner.start("Stopping debug mode...");
30
+ await updateDebug(componentId, manifest);
31
+ spinner.succeed(chalk.green(`Debug mode stopped for component: ${componentId}`));
32
+ }
33
+ else {
34
+ if (!url) {
35
+ throw new Error("URL is required when starting debug mode.\n" +
36
+ `Usage: fireberry debug ${componentId} localhost:[port]`);
37
+ }
38
+ validateDebugUrl(url);
39
+ spinner.start(`Starting debug mode for component ${componentId}...`);
40
+ await updateDebug(componentId, manifest, url);
41
+ spinner.succeed(chalk.green(`Debug mode started!\n` +
42
+ ` Component: ${componentId}\n` +
43
+ ` URL: ${url}\n\n` +
44
+ `To stop debugging, run: ${chalk.cyan(`fireberry debug ${componentId} --stop`)}`));
45
+ }
46
+ }
47
+ catch (error) {
48
+ spinner.fail(options?.stop ? "Failed to stop debug mode" : "Failed to start debug mode");
49
+ throw error;
50
+ }
51
+ }
@@ -66,14 +66,17 @@ const validateGlobalMenuComponentSettings = (comp) => {
66
66
  if (typeof settings.displayName !== "string") {
67
67
  throw new Error(`Component "${comp.title}" (type: ${COMPONENT_TYPE.GLOBAL_MENU}) setting "displayName" must be a string`);
68
68
  }
69
+ if (settings.iconName && typeof settings.iconName !== "string") {
70
+ throw new Error(`Component "${comp.title}" (type: ${COMPONENT_TYPE.GLOBAL_MENU}) setting "iconName" must be a string`);
71
+ }
69
72
  };
70
73
  const validateSideMenuComponentSettings = (comp) => {
71
74
  const settings = comp.settings;
72
75
  if (!settings) {
73
76
  throw new Error(`Component "${comp.title}" (type: ${COMPONENT_TYPE.SIDE_MENU}) is missing required settings`);
74
77
  }
75
- if (!settings.icon) {
76
- throw new Error(`Component "${comp.title}" (type: ${COMPONENT_TYPE.SIDE_MENU}) setting "icon" must be a string`);
78
+ if (!settings.iconName) {
79
+ throw new Error(`Component "${comp.title}" (type: ${COMPONENT_TYPE.SIDE_MENU}) setting "iconName" must be a string`);
77
80
  }
78
81
  if (!settings.width) {
79
82
  throw new Error(`Component "${comp.title}" (type: ${COMPONENT_TYPE.SIDE_MENU}) setting "width" must be a S | M | L`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fireberry/cli",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "Fireberry CLI tool",
5
5
  "type": "module",
6
6
  "author": "",
@@ -42,3 +42,16 @@ export const deleteApp = async (manifest: Manifest): Promise<void> => {
42
42
  throw new Error(error instanceof Error ? error.message : "Unknown error");
43
43
  }
44
44
  };
45
+
46
+ export const updateDebug = async (
47
+ componentId: string,
48
+ manifest: Manifest,
49
+ debugUrl?: string
50
+ ): Promise<void> => {
51
+ const url = `${BASE_SERVICE_URL}/debug`;
52
+ try {
53
+ await api.post<void>(url, { componentId, debugUrl, manifest });
54
+ } catch (error) {
55
+ throw new Error(error instanceof Error ? error.message : "Unknown error");
56
+ }
57
+ };
package/src/api/types.ts CHANGED
@@ -33,10 +33,11 @@ export interface RecordComponentSettings {
33
33
 
34
34
  export interface GlobalMenuComponentSettings {
35
35
  displayName: string;
36
+ iconName?: string;
36
37
  }
37
38
 
38
39
  export interface SideMenuComponentSettings {
39
- icon: string;
40
+ iconName: string;
40
41
  width: "S" | "M" | "L";
41
42
  }
42
43
 
@@ -7,6 +7,7 @@ import packageJson from "../../package.json" with { type: "json" };
7
7
  import { runPush } from "../commands/push.js";
8
8
  import { runInstall } from "../commands/install.js";
9
9
  import { runDelete } from "../commands/delete.js";
10
+ import { runDebug } from "../commands/debug.js";
10
11
 
11
12
  const program = new Command();
12
13
 
@@ -52,6 +53,16 @@ program
52
53
  await runDelete();
53
54
  });
54
55
 
56
+ program
57
+ .command("debug")
58
+ .argument("<component-id>", "Component ID to debug")
59
+ .argument("[url]", "Debug URL in format localhost:[port]")
60
+ .option("--stop", "Stop debugging the component")
61
+ .description("Start or stop debugging a component")
62
+ .action(async (componentId: string, url?: string, options?: { stop?: boolean }) => {
63
+ await runDebug(componentId, url, options);
64
+ });
65
+
55
66
  program.parseAsync(process.argv).catch((err: unknown) => {
56
67
  const errorMessage = err instanceof Error
57
68
  ? err.message
@@ -0,0 +1,86 @@
1
+ import ora from "ora";
2
+ import chalk from "chalk";
3
+ import { updateDebug } from "../api/requests.js";
4
+ import { getManifest } from "../utils/components.utils.js";
5
+ import { Manifest, ManifestComponent } from "../api/types.js";
6
+
7
+ function validateDebugUrl(url: string): void {
8
+ const localhostPattern = /^localhost:\d+$/;
9
+
10
+ if (!localhostPattern.test(url)) {
11
+ throw new Error(
12
+ "Invalid URL format. URL must be in format: localhost:[port] (e.g., localhost:3000)\n" +
13
+ "Do not include http:// or https://"
14
+ );
15
+ }
16
+ }
17
+
18
+ function validateComponentExists(
19
+ manifest: Manifest,
20
+ componentId: string
21
+ ): void {
22
+ const component = manifest.components?.find(
23
+ (comp: ManifestComponent) =>
24
+ comp.id.toLowerCase() === componentId.toLowerCase()
25
+ );
26
+
27
+ if (!component) {
28
+ throw new Error(
29
+ `Component with ID "${componentId}" not found in manifest.\n` +
30
+ `Available components:\n` +
31
+ manifest.components
32
+ ?.map((comp: ManifestComponent) => ` - ${comp.title} (${comp.id})`)
33
+ .join("\n")
34
+ );
35
+ }
36
+ }
37
+
38
+ export async function runDebug(
39
+ componentId: string,
40
+ url?: string,
41
+ options?: { stop?: boolean }
42
+ ): Promise<void> {
43
+ const spinner = ora("Loading manifest...").start();
44
+
45
+ try {
46
+ const manifest = await getManifest();
47
+ spinner.succeed("Manifest loaded");
48
+
49
+ validateComponentExists(manifest, componentId);
50
+
51
+ if (options?.stop) {
52
+ spinner.start("Stopping debug mode...");
53
+ await updateDebug(componentId, manifest);
54
+ spinner.succeed(
55
+ chalk.green(`Debug mode stopped for component: ${componentId}`)
56
+ );
57
+ } else {
58
+ if (!url) {
59
+ throw new Error(
60
+ "URL is required when starting debug mode.\n" +
61
+ `Usage: fireberry debug ${componentId} localhost:[port]`
62
+ );
63
+ }
64
+
65
+ validateDebugUrl(url);
66
+
67
+ spinner.start(`Starting debug mode for component ${componentId}...`);
68
+ await updateDebug(componentId, manifest, url);
69
+ spinner.succeed(
70
+ chalk.green(
71
+ `Debug mode started!\n` +
72
+ ` Component: ${componentId}\n` +
73
+ ` URL: ${url}\n\n` +
74
+ `To stop debugging, run: ${chalk.cyan(
75
+ `fireberry debug ${componentId} --stop`
76
+ )}`
77
+ )
78
+ );
79
+ }
80
+ } catch (error) {
81
+ spinner.fail(
82
+ options?.stop ? "Failed to stop debug mode" : "Failed to start debug mode"
83
+ );
84
+ throw error;
85
+ }
86
+ }
@@ -13,7 +13,7 @@ import {
13
13
  SideMenuComponentSettings,
14
14
  } from "../api/types.js";
15
15
  import { COMPONENT_TYPE } from "../constants/component-types.js";
16
- import { HEIGHT_OPTIONS } from "../constants/height-options.js";
16
+ import { HEIGHT_OPTIONS, HeightOption } from "../constants/height-options.js";
17
17
 
18
18
  export const getManifest = async (basePath?: string): Promise<Manifest> => {
19
19
  const manifestPath = path.join(basePath || process.cwd(), "manifest.yml");
@@ -90,13 +90,17 @@ const validateRecordComponentSettings = (
90
90
  }
91
91
  if (!settings.height) {
92
92
  throw new Error(
93
- `Component "${comp.title}" (type: ${COMPONENT_TYPE.RECORD}) setting "height" must be one of: ${HEIGHT_OPTIONS.join(" | ")}`
93
+ `Component "${comp.title}" (type: ${
94
+ COMPONENT_TYPE.RECORD
95
+ }) setting "height" must be one of: ${HEIGHT_OPTIONS.join(" | ")}`
94
96
  );
95
97
  }
96
98
 
97
- if (!HEIGHT_OPTIONS.includes(settings.height as any)) {
99
+ if (!HEIGHT_OPTIONS.includes(settings.height as HeightOption)) {
98
100
  throw new Error(
99
- `Component "${comp.title}" (type: ${COMPONENT_TYPE.RECORD}) setting "height" must be one of: ${HEIGHT_OPTIONS.join(" | ")}`
101
+ `Component "${comp.title}" (type: ${
102
+ COMPONENT_TYPE.RECORD
103
+ }) setting "height" must be one of: ${HEIGHT_OPTIONS.join(" | ")}`
100
104
  );
101
105
  }
102
106
  };
@@ -125,6 +129,12 @@ const validateGlobalMenuComponentSettings = (
125
129
  `Component "${comp.title}" (type: ${COMPONENT_TYPE.GLOBAL_MENU}) setting "displayName" must be a string`
126
130
  );
127
131
  }
132
+
133
+ if (settings.iconName && typeof settings.iconName !== "string") {
134
+ throw new Error(
135
+ `Component "${comp.title}" (type: ${COMPONENT_TYPE.GLOBAL_MENU}) setting "iconName" must be a string`
136
+ );
137
+ }
128
138
  };
129
139
 
130
140
  const validateSideMenuComponentSettings = (
@@ -140,9 +150,9 @@ const validateSideMenuComponentSettings = (
140
150
  );
141
151
  }
142
152
 
143
- if (!settings.icon) {
153
+ if (!settings.iconName) {
144
154
  throw new Error(
145
- `Component "${comp.title}" (type: ${COMPONENT_TYPE.SIDE_MENU}) setting "icon" must be a string`
155
+ `Component "${comp.title}" (type: ${COMPONENT_TYPE.SIDE_MENU}) setting "iconName" must be a string`
146
156
  );
147
157
  }
148
158