@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 +2 -2
- package/dist/api/requests.d.ts +1 -0
- package/dist/api/requests.js +9 -0
- package/dist/api/types.d.ts +2 -1
- package/dist/bin/fireberry.js +10 -0
- package/dist/commands/debug.d.ts +3 -0
- package/dist/commands/debug.js +51 -0
- package/dist/utils/components.utils.js +5 -2
- package/package.json +1 -1
- package/src/api/requests.ts +13 -0
- package/src/api/types.ts +2 -1
- package/src/bin/fireberry.ts +11 -0
- package/src/commands/debug.ts +86 -0
- package/src/utils/components.utils.ts +16 -6
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
|
-
|
|
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 `
|
|
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)
|
package/dist/api/requests.d.ts
CHANGED
|
@@ -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>;
|
package/dist/api/requests.js
CHANGED
|
@@ -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
|
+
};
|
package/dist/api/types.d.ts
CHANGED
|
@@ -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
|
-
|
|
33
|
+
iconName: string;
|
|
33
34
|
width: "S" | "M" | "L";
|
|
34
35
|
}
|
|
35
36
|
export type ComponentSettings = RecordComponentSettings | GlobalMenuComponentSettings | SideMenuComponentSettings;
|
package/dist/bin/fireberry.js
CHANGED
|
@@ -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,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.
|
|
76
|
-
throw new Error(`Component "${comp.title}" (type: ${COMPONENT_TYPE.SIDE_MENU}) setting "
|
|
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
package/src/api/requests.ts
CHANGED
|
@@ -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
|
-
|
|
40
|
+
iconName: string;
|
|
40
41
|
width: "S" | "M" | "L";
|
|
41
42
|
}
|
|
42
43
|
|
package/src/bin/fireberry.ts
CHANGED
|
@@ -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: ${
|
|
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
|
|
99
|
+
if (!HEIGHT_OPTIONS.includes(settings.height as HeightOption)) {
|
|
98
100
|
throw new Error(
|
|
99
|
-
`Component "${comp.title}" (type: ${
|
|
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.
|
|
153
|
+
if (!settings.iconName) {
|
|
144
154
|
throw new Error(
|
|
145
|
-
`Component "${comp.title}" (type: ${COMPONENT_TYPE.SIDE_MENU}) setting "
|
|
155
|
+
`Component "${comp.title}" (type: ${COMPONENT_TYPE.SIDE_MENU}) setting "iconName" must be a string`
|
|
146
156
|
);
|
|
147
157
|
}
|
|
148
158
|
|