@base44-preview/cli 0.0.1-pr.18.d6d2f8e → 0.0.1-pr.20.e0a0f93
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/cli/index.js +144 -7
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { Command } from "commander";
|
|
3
3
|
import chalk from "chalk";
|
|
4
|
-
import { cancel, group, intro, log, select, spinner, text } from "@clack/prompts";
|
|
4
|
+
import { cancel, confirm, group, intro, isCancel, log, select, spinner, text } from "@clack/prompts";
|
|
5
5
|
import pWaitFor from "p-wait-for";
|
|
6
6
|
import { z } from "zod";
|
|
7
7
|
import ky from "ky";
|
|
@@ -27,14 +27,12 @@ const DeviceCodeResponseSchema = z.object({
|
|
|
27
27
|
device_code: z.string().min(1, "Device code cannot be empty"),
|
|
28
28
|
user_code: z.string().min(1, "User code cannot be empty"),
|
|
29
29
|
verification_uri: z.url("Invalid verification URL"),
|
|
30
|
-
verification_uri_complete: z.url("Invalid complete verification URL"),
|
|
31
30
|
expires_in: z.number().int().positive("Expires in must be a positive integer"),
|
|
32
31
|
interval: z.number().int().positive("Interval in must be a positive integer")
|
|
33
32
|
}).transform((data) => ({
|
|
34
33
|
deviceCode: data.device_code,
|
|
35
34
|
userCode: data.user_code,
|
|
36
35
|
verificationUri: data.verification_uri,
|
|
37
|
-
verificationUriComplete: data.verification_uri_complete,
|
|
38
36
|
expiresIn: data.expires_in,
|
|
39
37
|
interval: data.interval
|
|
40
38
|
}));
|
|
@@ -690,11 +688,13 @@ async function runCommand(commandFn, options) {
|
|
|
690
688
|
* The spinner is automatically started, and stopped on both success and error.
|
|
691
689
|
*
|
|
692
690
|
* @param startMessage - Message to show when spinner starts
|
|
693
|
-
* @param operation - The async operation to execute
|
|
691
|
+
* @param operation - The async operation to execute. Receives an updateMessage function
|
|
692
|
+
* to update the spinner text during long-running operations.
|
|
694
693
|
* @param options - Optional configuration for success/error messages
|
|
695
694
|
* @returns The result of the operation
|
|
696
695
|
*
|
|
697
696
|
* @example
|
|
697
|
+
* // Simple usage
|
|
698
698
|
* const data = await runTask(
|
|
699
699
|
* "Fetching data...",
|
|
700
700
|
* async () => {
|
|
@@ -706,12 +706,27 @@ async function runCommand(commandFn, options) {
|
|
|
706
706
|
* errorMessage: "Failed to fetch data",
|
|
707
707
|
* }
|
|
708
708
|
* );
|
|
709
|
+
*
|
|
710
|
+
* @example
|
|
711
|
+
* // With progress updates
|
|
712
|
+
* const result = await runTask(
|
|
713
|
+
* "Processing files...",
|
|
714
|
+
* async (updateMessage) => {
|
|
715
|
+
* for (const file of files) {
|
|
716
|
+
* updateMessage(`Processing ${file.name}...`);
|
|
717
|
+
* await process(file);
|
|
718
|
+
* }
|
|
719
|
+
* return files.length;
|
|
720
|
+
* },
|
|
721
|
+
* { successMessage: "All files processed" }
|
|
722
|
+
* );
|
|
709
723
|
*/
|
|
710
724
|
async function runTask(startMessage, operation, options) {
|
|
711
725
|
const s = spinner();
|
|
712
726
|
s.start(startMessage);
|
|
727
|
+
const updateMessage = (message) => s.message(message);
|
|
713
728
|
try {
|
|
714
|
-
const result = await operation();
|
|
729
|
+
const result = await operation(updateMessage);
|
|
715
730
|
s.stop(options?.successMessage || startMessage);
|
|
716
731
|
return result;
|
|
717
732
|
} catch (error) {
|
|
@@ -740,13 +755,13 @@ async function generateAndDisplayDeviceCode() {
|
|
|
740
755
|
successMessage: "Device code generated",
|
|
741
756
|
errorMessage: "Failed to generate device code"
|
|
742
757
|
});
|
|
743
|
-
log.info(`
|
|
758
|
+
log.info(`Verification code: ${chalk.bold(deviceCodeResponse.userCode)}\nPlease confirm this code at: ${deviceCodeResponse.verificationUri}`);
|
|
744
759
|
return deviceCodeResponse;
|
|
745
760
|
}
|
|
746
761
|
async function waitForAuthentication(deviceCode, expiresIn, interval) {
|
|
747
762
|
let tokenResponse;
|
|
748
763
|
try {
|
|
749
|
-
await runTask("Waiting for
|
|
764
|
+
await runTask("Waiting for authentication...", async () => {
|
|
750
765
|
await pWaitFor(async () => {
|
|
751
766
|
const result = await getTokenFromDeviceCode(deviceCode);
|
|
752
767
|
if (result !== null) {
|
|
@@ -826,6 +841,102 @@ const showProjectCommand = new Command("show-project").description("Display proj
|
|
|
826
841
|
await runCommand(showProject);
|
|
827
842
|
});
|
|
828
843
|
|
|
844
|
+
//#endregion
|
|
845
|
+
//#region src/core/site/schema.ts
|
|
846
|
+
/**
|
|
847
|
+
* Represents a single file to be deployed.
|
|
848
|
+
* Contains the relative path and base64-encoded content.
|
|
849
|
+
*/
|
|
850
|
+
const SiteFileSchema = z.object({
|
|
851
|
+
path: z.string(),
|
|
852
|
+
content: z.string()
|
|
853
|
+
});
|
|
854
|
+
/**
|
|
855
|
+
* Response from the deploy API endpoint.
|
|
856
|
+
*/
|
|
857
|
+
const DeployResponseSchema = z.object({ url: z.string().url() });
|
|
858
|
+
|
|
859
|
+
//#endregion
|
|
860
|
+
//#region src/core/site/config.ts
|
|
861
|
+
async function readSiteFile(outputDir, relativePath) {
|
|
862
|
+
const content = (await readFile(join(outputDir, relativePath))).toString("base64");
|
|
863
|
+
await new Promise((resolve$1) => setTimeout(resolve$1, 20));
|
|
864
|
+
return {
|
|
865
|
+
path: relativePath,
|
|
866
|
+
content
|
|
867
|
+
};
|
|
868
|
+
}
|
|
869
|
+
async function getSiteFilePaths(outputDir) {
|
|
870
|
+
return await globby("**/*", {
|
|
871
|
+
cwd: outputDir,
|
|
872
|
+
onlyFiles: true,
|
|
873
|
+
absolute: false
|
|
874
|
+
});
|
|
875
|
+
}
|
|
876
|
+
/**
|
|
877
|
+
* Reads site files one by one, yielding each file as it's read.
|
|
878
|
+
* Useful for showing progress during file reading.
|
|
879
|
+
*
|
|
880
|
+
* @param outputDir - The directory containing built site files
|
|
881
|
+
* @param filePaths - Array of relative file paths to read
|
|
882
|
+
* @yields Each file as it's read
|
|
883
|
+
*
|
|
884
|
+
* @example
|
|
885
|
+
* const paths = await getSiteFilePaths("./dist");
|
|
886
|
+
* for await (const file of readSiteFilesStream("./dist", paths)) {
|
|
887
|
+
* console.log(`Read: ${file.path}`);
|
|
888
|
+
* }
|
|
889
|
+
*/
|
|
890
|
+
async function* readSiteFilesStream(outputDir, filePaths) {
|
|
891
|
+
for (const relativePath of filePaths) yield await readSiteFile(outputDir, relativePath);
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
//#endregion
|
|
895
|
+
//#region src/core/site/api.ts
|
|
896
|
+
/**
|
|
897
|
+
* Uploads site files to the Base44 hosting API.
|
|
898
|
+
*
|
|
899
|
+
* @param files - Array of files with base64-encoded content to upload
|
|
900
|
+
* @returns Deploy response with the site URL
|
|
901
|
+
*/
|
|
902
|
+
async function uploadSite(files) {
|
|
903
|
+
await new Promise((resolve$1) => setTimeout(resolve$1, 2e3));
|
|
904
|
+
return { url: "https://example.base44.app" };
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
//#endregion
|
|
908
|
+
//#region src/core/site/deploy.ts
|
|
909
|
+
/**
|
|
910
|
+
* Deploys a site from the given output directory to Base44 hosting.
|
|
911
|
+
* Reads files one by one with progress callback, validates, and uploads to the API.
|
|
912
|
+
*
|
|
913
|
+
* @param outputDir - The directory containing built site files (e.g., "./dist")
|
|
914
|
+
* @param onProgress - Optional callback called for each file read
|
|
915
|
+
* @returns Deploy response with the site URL
|
|
916
|
+
* @throws Error if no files found in the output directory
|
|
917
|
+
*
|
|
918
|
+
* @example
|
|
919
|
+
* const { url } = await deploySite("./dist", (progress) => {
|
|
920
|
+
* console.log(`Reading ${progress.current}/${progress.total}: ${progress.path}`);
|
|
921
|
+
* });
|
|
922
|
+
*/
|
|
923
|
+
async function deploySite(outputDir, onProgress) {
|
|
924
|
+
const filePaths = await getSiteFilePaths(outputDir);
|
|
925
|
+
if (filePaths.length === 0) throw new Error(`No files found in output directory: ${outputDir}. Make sure to build your project first.`);
|
|
926
|
+
const files = [];
|
|
927
|
+
let current = 0;
|
|
928
|
+
for await (const file of readSiteFilesStream(outputDir, filePaths)) {
|
|
929
|
+
current++;
|
|
930
|
+
onProgress?.({
|
|
931
|
+
total: filePaths.length,
|
|
932
|
+
current,
|
|
933
|
+
path: file.path
|
|
934
|
+
});
|
|
935
|
+
files.push(file);
|
|
936
|
+
}
|
|
937
|
+
return await uploadSite(files);
|
|
938
|
+
}
|
|
939
|
+
|
|
829
940
|
//#endregion
|
|
830
941
|
//#region src/cli/commands/entities/push.ts
|
|
831
942
|
async function pushEntitiesAction() {
|
|
@@ -901,6 +1012,31 @@ const createCommand = new Command("create").description("Create a new Base44 pro
|
|
|
901
1012
|
await runCommand(create, { fullBanner: true });
|
|
902
1013
|
});
|
|
903
1014
|
|
|
1015
|
+
//#endregion
|
|
1016
|
+
//#region src/cli/commands/site/deploy.ts
|
|
1017
|
+
async function deployAction() {
|
|
1018
|
+
const { project } = await readProjectConfig();
|
|
1019
|
+
if (!project.site?.outputDirectory) throw new Error("No site configuration found. Please add 'site.outputDirectory' to your config.jsonc");
|
|
1020
|
+
const outputDir = resolve(project.root, project.site.outputDirectory);
|
|
1021
|
+
const shouldDeploy = await confirm({ message: `Deploy site from ${project.site.outputDirectory}?` });
|
|
1022
|
+
if (isCancel(shouldDeploy) || !shouldDeploy) {
|
|
1023
|
+
log.warn("Deployment cancelled");
|
|
1024
|
+
return;
|
|
1025
|
+
}
|
|
1026
|
+
const result = await runTask("Reading site files...", async (updateMessage) => {
|
|
1027
|
+
return await deploySite(outputDir, (progress) => {
|
|
1028
|
+
updateMessage(`Reading files (${progress.current}/${progress.total}): ${progress.path}`);
|
|
1029
|
+
});
|
|
1030
|
+
}, {
|
|
1031
|
+
successMessage: "Site deployed successfully",
|
|
1032
|
+
errorMessage: "Deployment failed"
|
|
1033
|
+
});
|
|
1034
|
+
log.success(`Site deployed to: ${result.url}`);
|
|
1035
|
+
}
|
|
1036
|
+
const siteDeployCommand = new Command("site").description("Manage site deployments").addCommand(new Command("deploy").description("Deploy built site files to Base44 hosting").action(async () => {
|
|
1037
|
+
await runCommand(deployAction);
|
|
1038
|
+
}));
|
|
1039
|
+
|
|
904
1040
|
//#endregion
|
|
905
1041
|
//#region package.json
|
|
906
1042
|
var version = "0.0.1";
|
|
@@ -915,6 +1051,7 @@ program.addCommand(logoutCommand);
|
|
|
915
1051
|
program.addCommand(createCommand);
|
|
916
1052
|
program.addCommand(showProjectCommand);
|
|
917
1053
|
program.addCommand(entitiesPushCommand);
|
|
1054
|
+
program.addCommand(siteDeployCommand);
|
|
918
1055
|
program.parse();
|
|
919
1056
|
|
|
920
1057
|
//#endregion
|
package/package.json
CHANGED