@fireberry/cli 0.0.5-beta.19 → 0.0.5-beta.21
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/dist/api/requests.d.ts +4 -2
- package/dist/api/requests.js +25 -6
- package/dist/api/types.d.ts +2 -0
- package/dist/bin/fireberry.js +10 -0
- package/dist/commands/create.js +3 -1
- package/dist/commands/debug.d.ts +3 -0
- package/dist/commands/debug.js +54 -0
- package/dist/constants/component-types.d.ts +1 -0
- package/dist/constants/component-types.js +1 -0
- package/dist/constants/height-options.d.ts +2 -0
- package/dist/constants/height-options.js +1 -0
- package/dist/utils/components.utils.d.ts +1 -1
- package/dist/utils/components.utils.js +12 -3
- package/package.json +1 -1
- package/src/api/requests.ts +35 -7
- package/src/api/types.ts +2 -0
- package/src/bin/fireberry.ts +11 -0
- package/src/commands/create.ts +4 -2
- package/src/commands/debug.ts +84 -0
- package/src/constants/component-types.ts +2 -0
- package/src/constants/height-options.ts +3 -0
- package/src/templates/manifest.yml +1 -0
- package/src/utils/components.utils.ts +17 -3
package/dist/api/requests.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import "../config/env.js";
|
|
2
|
-
import type {
|
|
3
|
-
export declare const createApp: (
|
|
2
|
+
import type { Manifest, ZippedComponent } from "./types.js";
|
|
3
|
+
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 startDebug: (appId: string, componentId: string, debugUrl: string, manifest: Manifest) => Promise<void>;
|
|
8
|
+
export declare const stopDebug: (appId: string, componentId: string, manifest: Manifest) => Promise<void>;
|
package/dist/api/requests.js
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import "../config/env.js";
|
|
2
|
+
import { BASE_SERVICE_URL } from "../constants/component-types.js";
|
|
2
3
|
import { api } from "./axios.js";
|
|
3
|
-
export const createApp = async (
|
|
4
|
-
const url =
|
|
4
|
+
export const createApp = async (manifest) => {
|
|
5
|
+
const url = `${BASE_SERVICE_URL}/create`;
|
|
5
6
|
try {
|
|
6
|
-
await api.post(url,
|
|
7
|
+
await api.post(url, { manifest });
|
|
7
8
|
}
|
|
8
9
|
catch (error) {
|
|
9
10
|
throw new Error(error instanceof Error ? error.message : "Unknown error");
|
|
10
11
|
}
|
|
11
12
|
};
|
|
12
13
|
export const pushComponents = async (appId, components, manifest) => {
|
|
13
|
-
const url =
|
|
14
|
+
const url = `${BASE_SERVICE_URL}/push`;
|
|
14
15
|
try {
|
|
15
16
|
await api.post(url, { appId, components, manifest });
|
|
16
17
|
}
|
|
@@ -19,7 +20,7 @@ export const pushComponents = async (appId, components, manifest) => {
|
|
|
19
20
|
}
|
|
20
21
|
};
|
|
21
22
|
export const installApp = async (manifest) => {
|
|
22
|
-
const url =
|
|
23
|
+
const url = `${BASE_SERVICE_URL}/install`;
|
|
23
24
|
try {
|
|
24
25
|
await api.post(url, { manifest }, { timeout: 300000 }); // 5 minutes
|
|
25
26
|
}
|
|
@@ -28,7 +29,7 @@ export const installApp = async (manifest) => {
|
|
|
28
29
|
}
|
|
29
30
|
};
|
|
30
31
|
export const deleteApp = async (manifest) => {
|
|
31
|
-
const url =
|
|
32
|
+
const url = `${BASE_SERVICE_URL}/delete`;
|
|
32
33
|
try {
|
|
33
34
|
await api.delete(url, { manifest });
|
|
34
35
|
}
|
|
@@ -36,3 +37,21 @@ export const deleteApp = async (manifest) => {
|
|
|
36
37
|
throw new Error(error instanceof Error ? error.message : "Unknown error");
|
|
37
38
|
}
|
|
38
39
|
};
|
|
40
|
+
export const startDebug = async (appId, componentId, debugUrl, manifest) => {
|
|
41
|
+
const url = `${BASE_SERVICE_URL}/debug`;
|
|
42
|
+
try {
|
|
43
|
+
await api.post(url, { appId, componentId, debugUrl, manifest });
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
throw new Error(error instanceof Error ? error.message : "Unknown error");
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
export const stopDebug = async (appId, componentId, manifest) => {
|
|
50
|
+
const url = `${BASE_SERVICE_URL}/debug`;
|
|
51
|
+
try {
|
|
52
|
+
await api.delete(url, { appId, componentId, manifest });
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
throw new Error(error instanceof Error ? error.message : "Unknown error");
|
|
56
|
+
}
|
|
57
|
+
};
|
package/dist/api/types.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { COMPONENT_TYPE } from "../constants/component-types.js";
|
|
2
|
+
import { HeightOption } from "../constants/height-options.js";
|
|
2
3
|
export interface CreateAppRequest {
|
|
3
4
|
appId: string;
|
|
4
5
|
componentId: string;
|
|
@@ -22,6 +23,7 @@ export interface RecordComponentSettings {
|
|
|
22
23
|
iconName: string;
|
|
23
24
|
iconColor: string;
|
|
24
25
|
objectType: number;
|
|
26
|
+
height: HeightOption;
|
|
25
27
|
}
|
|
26
28
|
export interface GlobalMenuComponentSettings {
|
|
27
29
|
displayName: string;
|
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
|
package/dist/commands/create.js
CHANGED
|
@@ -6,6 +6,7 @@ import { fileURLToPath } from "node:url";
|
|
|
6
6
|
import ora from "ora";
|
|
7
7
|
import chalk from "chalk";
|
|
8
8
|
import { createApp } from "../api/requests.js";
|
|
9
|
+
import { getManifest } from "../utils/components.utils.js";
|
|
9
10
|
const __filename = fileURLToPath(import.meta.url);
|
|
10
11
|
const __dirname = path.dirname(__filename);
|
|
11
12
|
function slugifyName(name) {
|
|
@@ -35,7 +36,6 @@ export async function runCreate({ name }) {
|
|
|
35
36
|
}
|
|
36
37
|
const spinner = ora(`Creating app "${chalk.cyan(appName)}"...`).start();
|
|
37
38
|
try {
|
|
38
|
-
await createApp({ appId, componentId });
|
|
39
39
|
await fs.ensureDir(appDir);
|
|
40
40
|
const templatesDir = path.join(__dirname, "..", "..", "src", "templates");
|
|
41
41
|
const manifestTemplate = await fs.readFile(path.join(templatesDir, "manifest.yml"), "utf-8");
|
|
@@ -47,6 +47,8 @@ export async function runCreate({ name }) {
|
|
|
47
47
|
const htmlContent = htmlTemplate.replace(/{{appName}}/g, appName);
|
|
48
48
|
await fs.writeFile(path.join(appDir, "manifest.yml"), manifestContent);
|
|
49
49
|
await fs.writeFile(path.join(appDir, "index.html"), htmlContent);
|
|
50
|
+
const manifest = await getManifest(appDir);
|
|
51
|
+
await createApp(manifest);
|
|
50
52
|
spinner.succeed(`Successfully created "${chalk.cyan(appName)}" app!`);
|
|
51
53
|
console.log(chalk.gray(`📁 Location: ${appDir}`));
|
|
52
54
|
console.log(chalk.gray(`App ID: ${appId}`));
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import ora from "ora";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import { startDebug, stopDebug } 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 === componentId);
|
|
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
|
+
// Validate component exists
|
|
28
|
+
validateComponentExists(manifest, componentId);
|
|
29
|
+
if (options?.stop) {
|
|
30
|
+
// Stop debugging
|
|
31
|
+
spinner.start("Stopping debug mode...");
|
|
32
|
+
await stopDebug(manifest.app.id, componentId, manifest);
|
|
33
|
+
spinner.succeed(chalk.green(`Debug mode stopped for component: ${componentId}`));
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
// Start debugging
|
|
37
|
+
if (!url) {
|
|
38
|
+
throw new Error("URL is required when starting debug mode.\n" +
|
|
39
|
+
`Usage: fireberry debug ${componentId} localhost:[port]`);
|
|
40
|
+
}
|
|
41
|
+
validateDebugUrl(url);
|
|
42
|
+
spinner.start(`Starting debug mode for component ${componentId}...`);
|
|
43
|
+
await startDebug(manifest.app.id, componentId, url, manifest);
|
|
44
|
+
spinner.succeed(chalk.green(`Debug mode started!\n` +
|
|
45
|
+
` Component: ${componentId}\n` +
|
|
46
|
+
` URL: ${url}\n\n` +
|
|
47
|
+
`To stop debugging, run: ${chalk.cyan(`fireberry debug ${componentId} --stop`)}`));
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
spinner.fail(options?.stop ? "Failed to stop debug mode" : "Failed to start debug mode");
|
|
52
|
+
throw error;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const HEIGHT_OPTIONS = ["S", "M", "L", "XL"];
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Manifest, ZippedComponent, UntypedManifestComponent } from "../api/types.js";
|
|
2
|
-
export declare const getManifest: () => Promise<Manifest>;
|
|
2
|
+
export declare const getManifest: (basePath?: string) => Promise<Manifest>;
|
|
3
3
|
export declare const validateComponentBuild: (componentPath: string, comp: UntypedManifestComponent) => Promise<void>;
|
|
4
4
|
export declare const zipComponentBuild: (componentPath: string, title: string) => Promise<Buffer>;
|
|
5
5
|
export declare const validateManifestComponents: (manifest: Manifest) => Promise<void>;
|
|
@@ -5,10 +5,12 @@ import chalk from "chalk";
|
|
|
5
5
|
import * as tar from "tar";
|
|
6
6
|
import os from "node:os";
|
|
7
7
|
import { COMPONENT_TYPE } from "../constants/component-types.js";
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
import { HEIGHT_OPTIONS } from "../constants/height-options.js";
|
|
9
|
+
export const getManifest = async (basePath) => {
|
|
10
|
+
const manifestPath = path.join(basePath || process.cwd(), "manifest.yml");
|
|
11
|
+
const searchDir = basePath || process.cwd();
|
|
10
12
|
if (!(await fs.pathExists(manifestPath))) {
|
|
11
|
-
throw new Error(`No manifest.yml found at ${chalk.yellow(
|
|
13
|
+
throw new Error(`No manifest.yml found at ${chalk.yellow(searchDir)}.\n` +
|
|
12
14
|
`Please run this command from your Fireberry app directory.`);
|
|
13
15
|
}
|
|
14
16
|
const manifestContent = await fs.readFile(manifestPath, "utf-8");
|
|
@@ -30,6 +32,7 @@ const validateRecordComponentSettings = (comp) => {
|
|
|
30
32
|
"iconName",
|
|
31
33
|
"iconColor",
|
|
32
34
|
"objectType",
|
|
35
|
+
"height",
|
|
33
36
|
];
|
|
34
37
|
for (const fieldName of requiredFields) {
|
|
35
38
|
if (settings[fieldName] === undefined || settings[fieldName] === null) {
|
|
@@ -45,6 +48,12 @@ const validateRecordComponentSettings = (comp) => {
|
|
|
45
48
|
if (typeof settings.objectType !== "number") {
|
|
46
49
|
throw new Error(`Component "${comp.title}" (type: ${COMPONENT_TYPE.RECORD}) setting "objectType" must be a number`);
|
|
47
50
|
}
|
|
51
|
+
if (!settings.height) {
|
|
52
|
+
throw new Error(`Component "${comp.title}" (type: ${COMPONENT_TYPE.RECORD}) setting "height" must be one of: ${HEIGHT_OPTIONS.join(" | ")}`);
|
|
53
|
+
}
|
|
54
|
+
if (!HEIGHT_OPTIONS.includes(settings.height)) {
|
|
55
|
+
throw new Error(`Component "${comp.title}" (type: ${COMPONENT_TYPE.RECORD}) setting "height" must be one of: ${HEIGHT_OPTIONS.join(" | ")}`);
|
|
56
|
+
}
|
|
48
57
|
};
|
|
49
58
|
const validateGlobalMenuComponentSettings = (comp) => {
|
|
50
59
|
const settings = comp.settings;
|
package/package.json
CHANGED
package/src/api/requests.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import "../config/env.js";
|
|
2
|
+
import { BASE_SERVICE_URL } from "../constants/component-types.js";
|
|
2
3
|
import { api } from "./axios.js";
|
|
3
|
-
import type {
|
|
4
|
+
import type { Manifest, ZippedComponent } from "./types.js";
|
|
4
5
|
|
|
5
|
-
export const createApp = async (
|
|
6
|
-
const url =
|
|
6
|
+
export const createApp = async (manifest: Manifest): Promise<void> => {
|
|
7
|
+
const url = `${BASE_SERVICE_URL}/create`;
|
|
7
8
|
try {
|
|
8
|
-
await api.post<void>(url,
|
|
9
|
+
await api.post<void>(url, { manifest });
|
|
9
10
|
} catch (error) {
|
|
10
11
|
throw new Error(error instanceof Error ? error.message : "Unknown error");
|
|
11
12
|
}
|
|
@@ -16,7 +17,7 @@ export const pushComponents = async (
|
|
|
16
17
|
components: ZippedComponent[],
|
|
17
18
|
manifest: Manifest
|
|
18
19
|
): Promise<void> => {
|
|
19
|
-
const url =
|
|
20
|
+
const url = `${BASE_SERVICE_URL}/push`;
|
|
20
21
|
try {
|
|
21
22
|
await api.post<void>(url, { appId, components, manifest });
|
|
22
23
|
} catch (error) {
|
|
@@ -25,7 +26,7 @@ export const pushComponents = async (
|
|
|
25
26
|
};
|
|
26
27
|
|
|
27
28
|
export const installApp = async (manifest: Manifest): Promise<void> => {
|
|
28
|
-
const url =
|
|
29
|
+
const url = `${BASE_SERVICE_URL}/install`;
|
|
29
30
|
try {
|
|
30
31
|
await api.post<void>(url, { manifest }, { timeout: 300000 }); // 5 minutes
|
|
31
32
|
} catch (error) {
|
|
@@ -34,10 +35,37 @@ export const installApp = async (manifest: Manifest): Promise<void> => {
|
|
|
34
35
|
};
|
|
35
36
|
|
|
36
37
|
export const deleteApp = async (manifest: Manifest): Promise<void> => {
|
|
37
|
-
const url =
|
|
38
|
+
const url = `${BASE_SERVICE_URL}/delete`;
|
|
38
39
|
try {
|
|
39
40
|
await api.delete<void>(url, { manifest });
|
|
40
41
|
} catch (error) {
|
|
41
42
|
throw new Error(error instanceof Error ? error.message : "Unknown error");
|
|
42
43
|
}
|
|
43
44
|
};
|
|
45
|
+
|
|
46
|
+
export const startDebug = async (
|
|
47
|
+
appId: string,
|
|
48
|
+
componentId: string,
|
|
49
|
+
debugUrl: string,
|
|
50
|
+
manifest: Manifest
|
|
51
|
+
): Promise<void> => {
|
|
52
|
+
const url = `${BASE_SERVICE_URL}/debug`;
|
|
53
|
+
try {
|
|
54
|
+
await api.post<void>(url, { appId, componentId, debugUrl, manifest });
|
|
55
|
+
} catch (error) {
|
|
56
|
+
throw new Error(error instanceof Error ? error.message : "Unknown error");
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export const stopDebug = async (
|
|
61
|
+
appId: string,
|
|
62
|
+
componentId: string,
|
|
63
|
+
manifest: Manifest
|
|
64
|
+
): Promise<void> => {
|
|
65
|
+
const url = `${BASE_SERVICE_URL}/debug`;
|
|
66
|
+
try {
|
|
67
|
+
await api.delete<void>(url, { appId, componentId, manifest });
|
|
68
|
+
} catch (error) {
|
|
69
|
+
throw new Error(error instanceof Error ? error.message : "Unknown error");
|
|
70
|
+
}
|
|
71
|
+
};
|
package/src/api/types.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { COMPONENT_TYPE } from "../constants/component-types.js";
|
|
2
|
+
import { HeightOption } from "../constants/height-options.js";
|
|
2
3
|
|
|
3
4
|
export interface CreateAppRequest {
|
|
4
5
|
appId: string;
|
|
@@ -27,6 +28,7 @@ export interface RecordComponentSettings {
|
|
|
27
28
|
iconName: string;
|
|
28
29
|
iconColor: string;
|
|
29
30
|
objectType: number;
|
|
31
|
+
height: HeightOption;
|
|
30
32
|
}
|
|
31
33
|
|
|
32
34
|
export interface GlobalMenuComponentSettings {
|
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
|
package/src/commands/create.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { fileURLToPath } from "node:url";
|
|
|
6
6
|
import ora from "ora";
|
|
7
7
|
import chalk from "chalk";
|
|
8
8
|
import { createApp } from "../api/requests.js";
|
|
9
|
+
import { getManifest } from "../utils/components.utils.js";
|
|
9
10
|
|
|
10
11
|
const __filename = fileURLToPath(import.meta.url);
|
|
11
12
|
const __dirname = path.dirname(__filename);
|
|
@@ -50,8 +51,6 @@ export async function runCreate({ name }: CreateOptions): Promise<void> {
|
|
|
50
51
|
const spinner = ora(`Creating app "${chalk.cyan(appName)}"...`).start();
|
|
51
52
|
|
|
52
53
|
try {
|
|
53
|
-
await createApp({ appId, componentId });
|
|
54
|
-
|
|
55
54
|
await fs.ensureDir(appDir);
|
|
56
55
|
|
|
57
56
|
const templatesDir = path.join(__dirname, "..", "..", "src", "templates");
|
|
@@ -73,6 +72,9 @@ export async function runCreate({ name }: CreateOptions): Promise<void> {
|
|
|
73
72
|
|
|
74
73
|
await fs.writeFile(path.join(appDir, "manifest.yml"), manifestContent);
|
|
75
74
|
await fs.writeFile(path.join(appDir, "index.html"), htmlContent);
|
|
75
|
+
const manifest = await getManifest(appDir);
|
|
76
|
+
|
|
77
|
+
await createApp(manifest);
|
|
76
78
|
|
|
77
79
|
spinner.succeed(`Successfully created "${chalk.cyan(appName)}" app!`);
|
|
78
80
|
console.log(chalk.gray(`📁 Location: ${appDir}`));
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import ora from "ora";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import { startDebug, stopDebug } from "../api/requests.js";
|
|
4
|
+
import { getManifest } from "../utils/components.utils.js";
|
|
5
|
+
|
|
6
|
+
function validateDebugUrl(url: string): void {
|
|
7
|
+
const localhostPattern = /^localhost:\d+$/;
|
|
8
|
+
|
|
9
|
+
if (!localhostPattern.test(url)) {
|
|
10
|
+
throw new Error(
|
|
11
|
+
"Invalid URL format. URL must be in format: localhost:[port] (e.g., localhost:3000)\n" +
|
|
12
|
+
"Do not include http:// or https://"
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function validateComponentExists(manifest: any, componentId: string): void {
|
|
18
|
+
const component = manifest.components?.find(
|
|
19
|
+
(comp: any) => comp.id === componentId
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
if (!component) {
|
|
23
|
+
throw new Error(
|
|
24
|
+
`Component with ID "${componentId}" not found in manifest.\n` +
|
|
25
|
+
`Available components:\n` +
|
|
26
|
+
manifest.components
|
|
27
|
+
?.map((comp: any) => ` - ${comp.title} (${comp.id})`)
|
|
28
|
+
.join("\n")
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export async function runDebug(
|
|
34
|
+
componentId: string,
|
|
35
|
+
url?: string,
|
|
36
|
+
options?: { stop?: boolean }
|
|
37
|
+
): Promise<void> {
|
|
38
|
+
const spinner = ora("Loading manifest...").start();
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
const manifest = await getManifest();
|
|
42
|
+
spinner.succeed("Manifest loaded");
|
|
43
|
+
|
|
44
|
+
// Validate component exists
|
|
45
|
+
validateComponentExists(manifest, componentId);
|
|
46
|
+
|
|
47
|
+
if (options?.stop) {
|
|
48
|
+
// Stop debugging
|
|
49
|
+
spinner.start("Stopping debug mode...");
|
|
50
|
+
await stopDebug(manifest.app.id, componentId, manifest);
|
|
51
|
+
spinner.succeed(
|
|
52
|
+
chalk.green(`Debug mode stopped for component: ${componentId}`)
|
|
53
|
+
);
|
|
54
|
+
} else {
|
|
55
|
+
// Start debugging
|
|
56
|
+
if (!url) {
|
|
57
|
+
throw new Error(
|
|
58
|
+
"URL is required when starting debug mode.\n" +
|
|
59
|
+
`Usage: fireberry debug ${componentId} localhost:[port]`
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
validateDebugUrl(url);
|
|
64
|
+
|
|
65
|
+
spinner.start(`Starting debug mode for component ${componentId}...`);
|
|
66
|
+
await startDebug(manifest.app.id, componentId, url, manifest);
|
|
67
|
+
spinner.succeed(
|
|
68
|
+
chalk.green(
|
|
69
|
+
`Debug mode started!\n` +
|
|
70
|
+
` Component: ${componentId}\n` +
|
|
71
|
+
` URL: ${url}\n\n` +
|
|
72
|
+
`To stop debugging, run: ${chalk.cyan(
|
|
73
|
+
`fireberry debug ${componentId} --stop`
|
|
74
|
+
)}`
|
|
75
|
+
)
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
} catch (error) {
|
|
79
|
+
spinner.fail(
|
|
80
|
+
options?.stop ? "Failed to stop debug mode" : "Failed to start debug mode"
|
|
81
|
+
);
|
|
82
|
+
throw error;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -13,13 +13,15 @@ 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
17
|
|
|
17
|
-
export const getManifest = async (): Promise<Manifest> => {
|
|
18
|
-
const manifestPath = path.join(process.cwd(), "manifest.yml");
|
|
18
|
+
export const getManifest = async (basePath?: string): Promise<Manifest> => {
|
|
19
|
+
const manifestPath = path.join(basePath || process.cwd(), "manifest.yml");
|
|
20
|
+
const searchDir = basePath || process.cwd();
|
|
19
21
|
|
|
20
22
|
if (!(await fs.pathExists(manifestPath))) {
|
|
21
23
|
throw new Error(
|
|
22
|
-
`No manifest.yml found at ${chalk.yellow(
|
|
24
|
+
`No manifest.yml found at ${chalk.yellow(searchDir)}.\n` +
|
|
23
25
|
`Please run this command from your Fireberry app directory.`
|
|
24
26
|
);
|
|
25
27
|
}
|
|
@@ -58,6 +60,7 @@ const validateRecordComponentSettings = (
|
|
|
58
60
|
"iconName",
|
|
59
61
|
"iconColor",
|
|
60
62
|
"objectType",
|
|
63
|
+
"height",
|
|
61
64
|
];
|
|
62
65
|
|
|
63
66
|
for (const fieldName of requiredFields) {
|
|
@@ -85,6 +88,17 @@ const validateRecordComponentSettings = (
|
|
|
85
88
|
`Component "${comp.title}" (type: ${COMPONENT_TYPE.RECORD}) setting "objectType" must be a number`
|
|
86
89
|
);
|
|
87
90
|
}
|
|
91
|
+
if (!settings.height) {
|
|
92
|
+
throw new Error(
|
|
93
|
+
`Component "${comp.title}" (type: ${COMPONENT_TYPE.RECORD}) setting "height" must be one of: ${HEIGHT_OPTIONS.join(" | ")}`
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (!HEIGHT_OPTIONS.includes(settings.height as any)) {
|
|
98
|
+
throw new Error(
|
|
99
|
+
`Component "${comp.title}" (type: ${COMPONENT_TYPE.RECORD}) setting "height" must be one of: ${HEIGHT_OPTIONS.join(" | ")}`
|
|
100
|
+
);
|
|
101
|
+
}
|
|
88
102
|
};
|
|
89
103
|
|
|
90
104
|
const validateGlobalMenuComponentSettings = (
|