@hapico/cli 0.0.19 → 0.0.20

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/bin/index.js CHANGED
@@ -37,7 +37,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
37
37
  return (mod && mod.__esModule) ? mod : { "default": mod };
38
38
  };
39
39
  Object.defineProperty(exports, "__esModule", { value: true });
40
- exports.compileES5 = void 0;
40
+ exports.compileES5 = exports.tryJSONParse = void 0;
41
41
  /* eslint-disable @typescript-eslint/no-explicit-any */
42
42
  const lodash_1 = require("lodash");
43
43
  const commander_1 = require("commander");
@@ -102,12 +102,27 @@ const getStoredProjectId = (projectDir) => {
102
102
  if (fs.existsSync(configFile)) {
103
103
  const data = fs.readFileSync(configFile, { encoding: "utf8" });
104
104
  const json = JSON.parse(data);
105
- return json.projectId || null;
105
+ return {
106
+ projectId: json.projectId || null,
107
+ replicate: json.replicate || [],
108
+ };
106
109
  }
107
- return null;
110
+ return {
111
+ projectId: null,
112
+ replicate: [],
113
+ };
108
114
  };
109
115
  // Khởi tạo cache bằng Map để lưu trữ kết quả compile
110
116
  const compileCache = new Map();
117
+ const tryJSONParse = (str) => {
118
+ try {
119
+ return JSON.parse(str);
120
+ }
121
+ catch (_a) {
122
+ return null;
123
+ }
124
+ };
125
+ exports.tryJSONParse = tryJSONParse;
111
126
  const compileES5 = (code, filePath) => {
112
127
  if (filePath && !filePath.endsWith(".ts") && !filePath.endsWith(".tsx")) {
113
128
  return code;
@@ -128,6 +143,7 @@ const compileES5 = (code, filePath) => {
128
143
  return result.code;
129
144
  }
130
145
  catch (error) {
146
+ console.log(chalk_1.default.red(`Error compiling code: ${error === null || error === void 0 ? void 0 : error.message}`));
131
147
  return "";
132
148
  }
133
149
  };
@@ -252,7 +268,7 @@ class RoomState {
252
268
  }
253
269
  this.ws = new ws_1.WebSocket(`wss://ws3.myworkbeast.com/ws?room=${this.roomId}`);
254
270
  // Set binaryType to 'arraybuffer' to handle binary data
255
- this.ws.binaryType = 'arraybuffer';
271
+ this.ws.binaryType = "arraybuffer";
256
272
  this.ws.on("open", () => {
257
273
  console.log(chalk_1.default.green(`Connected to room: ${this.roomId}`));
258
274
  this.isConnected = true;
@@ -271,13 +287,13 @@ class RoomState {
271
287
  try {
272
288
  let jsonStr;
273
289
  if (data instanceof ArrayBuffer) {
274
- jsonStr = pako_1.default.inflate(data, { to: 'string' });
290
+ jsonStr = pako_1.default.inflate(data, { to: "string" });
275
291
  }
276
292
  else if (typeof data === "string") {
277
293
  jsonStr = data;
278
294
  }
279
295
  else if (Buffer.isBuffer(data)) {
280
- jsonStr = pako_1.default.inflate(data, { to: 'string' });
296
+ jsonStr = pako_1.default.inflate(data, { to: "string" });
281
297
  }
282
298
  else {
283
299
  jsonStr = data.toString(); // Fallback nếu không nén
@@ -355,11 +371,12 @@ class RoomState {
355
371
  return this.isConnected;
356
372
  }
357
373
  }
358
- commander_1.program.version("0.0.19").description("Hapico CLI for project management");
374
+ commander_1.program.version("0.0.20").description("Hapico CLI for project management");
359
375
  commander_1.program
360
376
  .command("clone <id>")
361
377
  .description("Clone a project by ID")
362
378
  .action(async (id) => {
379
+ var _a, _b;
363
380
  const { accessToken } = getStoredToken();
364
381
  if (!accessToken) {
365
382
  console.error(chalk_1.default.red("✗ You need to login first. Use 'hapico login' command."));
@@ -374,7 +391,9 @@ commander_1.program
374
391
  const apiSpinner = (0, ora_1.default)(chalk_1.default.blue("Fetching project data...")).start();
375
392
  try {
376
393
  const response = await axios_1.default.get(`https://base.myworkbeast.com/api/views/${id}`);
377
- files = response.data.files || [];
394
+ const code = (_a = response === null || response === void 0 ? void 0 : response.data) === null || _a === void 0 ? void 0 : _a.code;
395
+ const decompressedCode = pako_1.default.inflate(Uint8Array.from(atob(code), (c) => c.charCodeAt(0)), { to: "string" });
396
+ files = ((_b = (0, exports.tryJSONParse)(decompressedCode)) === null || _b === void 0 ? void 0 : _b.files) || [];
378
397
  apiSpinner.succeed(chalk_1.default.green("Project data fetched successfully!"));
379
398
  const templateSpinner = (0, ora_1.default)(chalk_1.default.blue("Downloading template...")).start();
380
399
  const TEMPLATE_URL = "https://files.hcm04.vstorage.vngcloud.vn/assets/template_zalominiapp_devmode.zip";
@@ -416,6 +435,7 @@ commander_1.program
416
435
  .command("dev")
417
436
  .description("Start the project in development mode")
418
437
  .action(() => {
438
+ var _a;
419
439
  const { accessToken } = getStoredToken();
420
440
  if (!accessToken) {
421
441
  console.error(chalk_1.default.red("✗ You need to login first. Use 'hapico login' command."));
@@ -457,7 +477,7 @@ commander_1.program
457
477
  const info = JSON.stringify({
458
478
  id: sessionId,
459
479
  createdAt: new Date().toISOString(),
460
- viewId: getStoredProjectId(pwd),
480
+ viewId: (_a = getStoredProjectId(pwd)) === null || _a === void 0 ? void 0 : _a.projectId,
461
481
  });
462
482
  // Convert info to base64
463
483
  const projectId = Buffer.from(info).toString("base64");
@@ -469,10 +489,44 @@ commander_1.program
469
489
  const room = new RoomState(`view_${projectId}`, []);
470
490
  const fileManager = new FileManager(srcDir);
471
491
  const initialFiles = fileManager.listFiles();
472
- room.files = initialFiles;
492
+ // get tsconfig.json
493
+ const tsconfigPath = path.join(srcDir, "..", "tsconfig.json");
494
+ if (fs.existsSync(tsconfigPath)) {
495
+ const content = fs.readFileSync(tsconfigPath, { encoding: "utf8" });
496
+ initialFiles.push({
497
+ path: "./tsconfig.json",
498
+ content,
499
+ });
500
+ }
501
+ // Remove All binary files
502
+ const supportExtensions = [
503
+ ".ts",
504
+ ".tsx",
505
+ ".js",
506
+ ".jsx",
507
+ ".json",
508
+ ".css",
509
+ ".html",
510
+ ".env",
511
+ ".env.local",
512
+ ".env.development",
513
+ ".env.production",
514
+ ".jsonc",
515
+ ".yml",
516
+ ".yaml",
517
+ ".md",
518
+ ".markdown",
519
+ ".txt",
520
+ ".xml",
521
+ ".config",
522
+ ];
523
+ const filteredFiles = initialFiles.filter((file) => {
524
+ return supportExtensions.some((ext) => file.path.endsWith(ext));
525
+ });
526
+ room.files = filteredFiles;
473
527
  room.connect(async () => {
474
528
  devSpinner.succeed(chalk_1.default.green("Project started in development mode!"));
475
- room.updateState("view", initialFiles);
529
+ room.updateState("view", filteredFiles);
476
530
  fileManager.setOnFileChange((filePath, content) => {
477
531
  var _a;
478
532
  const es5 = (_a = (0, exports.compileES5)(content, filePath)) !== null && _a !== void 0 ? _a : "";
@@ -487,12 +541,12 @@ commander_1.program
487
541
  room.updateState("view", updatedFiles);
488
542
  });
489
543
  // Fetch project info
490
- const projectInfo = getStoredProjectId(pwd);
491
- if (!projectInfo) {
544
+ const store = getStoredProjectId(pwd);
545
+ if (!store.projectId) {
492
546
  console.error(chalk_1.default.red("✗ Project ID not found. Please ensure hapico.config.json exists in the project directory."));
493
547
  return;
494
548
  }
495
- const project = await axios_1.default.get(`https://base.myworkbeast.com/api/views/${projectInfo}`, {
549
+ const project = await axios_1.default.get(`https://base.myworkbeast.com/api/views/${store.projectId}`, {
496
550
  headers: {
497
551
  Authorization: `Bearer ${accessToken}`,
498
552
  "Content-Type": "application/json",
@@ -518,7 +572,7 @@ commander_1.program
518
572
  });
519
573
  });
520
574
  // Hàm tái sử dụng để push mã nguồn lên server
521
- async function pushProject(spinner) {
575
+ async function pushProject(spinner, projectId) {
522
576
  const data = getStoredToken();
523
577
  const { accessToken } = data || {};
524
578
  if (!accessToken) {
@@ -526,11 +580,6 @@ async function pushProject(spinner) {
526
580
  return false;
527
581
  }
528
582
  const pwd = process.cwd();
529
- const projectId = getStoredProjectId(pwd);
530
- if (!projectId) {
531
- spinner.fail(chalk_1.default.red("✗ Project ID not found. Please ensure hapico.config.json exists in the project directory."));
532
- return false;
533
- }
534
583
  const srcDir = path.join(pwd, "src");
535
584
  if (!fs.existsSync(srcDir)) {
536
585
  spinner.fail(chalk_1.default.red("✗ Source directory 'src' does not exist. Please clone a project first."));
@@ -538,17 +587,30 @@ async function pushProject(spinner) {
538
587
  }
539
588
  const fileManager = new FileManager(srcDir);
540
589
  const files = fileManager.listFiles();
541
- // Nếu có file .env
542
- const envFile = path.join(pwd, ".env");
543
- console.log(chalk_1.default.cyan(`🔍 Checking for .env file at ${envFile}`));
544
- if (fs.existsSync(envFile)) {
545
- console.log(chalk_1.default.green(".env file found, including in push."));
546
- const content = fs.readFileSync(envFile, { encoding: "utf8" });
547
- files.push({
548
- path: "./.env",
549
- content,
550
- });
551
- }
590
+ // Supported files
591
+ const SUPPORT_FILES = [
592
+ "./.env",
593
+ "./.env.local",
594
+ "./.env.development",
595
+ "./.env.production",
596
+ "./package.json",
597
+ "./tsconfig.json",
598
+ ];
599
+ // Include supported files
600
+ SUPPORT_FILES.forEach((relativePath) => {
601
+ var _a;
602
+ const fullPath = path.join(pwd, relativePath);
603
+ if (fs.existsSync(fullPath)) {
604
+ console.log(chalk_1.default.green(`Including ${relativePath} in push for project ${projectId}.`));
605
+ const content = fs.readFileSync(fullPath, { encoding: "utf8" });
606
+ files.push({
607
+ path: relativePath,
608
+ content,
609
+ es5: (_a = (0, exports.compileES5)(content, fullPath)) !== null && _a !== void 0 ? _a : "",
610
+ });
611
+ }
612
+ });
613
+ spinner.text = `Pushing project source code to server for project ${projectId}...`;
552
614
  const apiUrl = `https://base.myworkbeast.com/api/views/${projectId}`;
553
615
  try {
554
616
  await axios_1.default.put(apiUrl, {
@@ -564,7 +626,7 @@ async function pushProject(spinner) {
564
626
  return true;
565
627
  }
566
628
  catch (error) {
567
- spinner.fail(chalk_1.default.red(`✗ Error saving project: ${error.message}`));
629
+ spinner.fail(chalk_1.default.red(`✗ Error saving project ${projectId}: ${error.message}`));
568
630
  return false;
569
631
  }
570
632
  }
@@ -573,9 +635,34 @@ commander_1.program
573
635
  .description("Push the project source code to the server")
574
636
  .action(async () => {
575
637
  const saveSpinner = (0, ora_1.default)(chalk_1.default.blue("Saving project source code...")).start();
576
- const success = await pushProject(saveSpinner);
577
- if (success) {
578
- saveSpinner.succeed(chalk_1.default.green("Project source code saved successfully!"));
638
+ const pwd = process.cwd();
639
+ const { projectId, replicate } = getStoredProjectId(pwd);
640
+ if (!projectId) {
641
+ saveSpinner.fail(chalk_1.default.red("✗ Project ID not found. Please ensure hapico.config.json exists in the project directory."));
642
+ return;
643
+ }
644
+ // Push to the main project
645
+ const mainSuccess = await pushProject(saveSpinner, projectId);
646
+ let allSuccess = mainSuccess;
647
+ // Push to replicated projects if replicate array exists
648
+ if (replicate && Array.isArray(replicate) && replicate.length > 0) {
649
+ saveSpinner.text = chalk_1.default.blue("Pushing to replicated projects...");
650
+ for (const repId of replicate) {
651
+ const success = await pushProject(saveSpinner, repId);
652
+ if (!success) {
653
+ allSuccess = false;
654
+ console.warn(chalk_1.default.yellow(`⚠ Failed to push to replicated project ${repId}. Continuing...`));
655
+ }
656
+ else {
657
+ console.log(chalk_1.default.green(`✓ Successfully pushed to replicated project ${repId}.`));
658
+ }
659
+ }
660
+ }
661
+ if (allSuccess) {
662
+ saveSpinner.succeed(chalk_1.default.green("Project source code saved successfully to all projects!"));
663
+ }
664
+ else {
665
+ saveSpinner.warn(chalk_1.default.yellow("Project source code saved with some errors."));
579
666
  }
580
667
  });
581
668
  commander_1.program
@@ -637,26 +724,29 @@ commander_1.program
637
724
  .command("pull")
638
725
  .description("Pull the latest project files from the server")
639
726
  .action(async () => {
727
+ var _a, _b;
640
728
  const token = getStoredToken();
641
729
  if (!token) {
642
730
  console.error(chalk_1.default.red("✗ You need to login first. Use 'hapico login' command."));
643
731
  return;
644
732
  }
645
733
  const pwd = process.cwd();
646
- const projectId = getStoredProjectId(pwd);
734
+ const { projectId } = getStoredProjectId(pwd);
647
735
  if (!projectId) {
648
736
  console.error(chalk_1.default.red("✗ Project ID not found. Please ensure hapico.config.json exists in the project directory."));
649
737
  return;
650
738
  }
651
739
  const apiSpinner = (0, ora_1.default)(chalk_1.default.blue("Fetching latest project files...")).start();
652
740
  try {
653
- const response = await axios_1.default.get(`https://base.myworkbeast.com/api/views/${projectId}`, {
741
+ const response = await axios_1.default.get(`https://base.myworkbeast.com/api/views/v3/${projectId}`, {
654
742
  headers: {
655
- Authorization: `Bearer ${token}`,
743
+ Authorization: `Bearer ${token.accessToken}`,
656
744
  "Content-Type": "application/json",
657
745
  },
658
746
  });
659
- const files = response.data.files || [];
747
+ const code = (_a = response === null || response === void 0 ? void 0 : response.data) === null || _a === void 0 ? void 0 : _a.code;
748
+ const decompressedCode = pako_1.default.inflate(Uint8Array.from(atob(code), (c) => c.charCodeAt(0)), { to: "string" });
749
+ const files = ((_b = (0, exports.tryJSONParse)(decompressedCode)) === null || _b === void 0 ? void 0 : _b.files) || [];
660
750
  const fileManager = new FileManager(path.join(pwd, "src"));
661
751
  fileManager.syncFiles(files);
662
752
  apiSpinner.succeed(chalk_1.default.green("Project files updated successfully!"));
@@ -754,14 +844,15 @@ commander_1.program
754
844
  .replace(/[-:]/g, "")
755
845
  .replace(/\..+/, "");
756
846
  const filename = path.join(migrationsDir, `${index + 1}_migration_${timestamp}.sql`);
757
- fs.writeFileSync(filename, migration.sql, { encoding: "utf8" });
847
+ fs.writeFileSync(filename, migration.sql, {
848
+ encoding: "utf8",
849
+ });
758
850
  });
759
851
  console.log(chalk_1.default.green("✓ Migrations saved successfully!"));
760
852
  // Download project files and save to project
761
853
  let files = [];
762
854
  const apiSpinner = (0, ora_1.default)(chalk_1.default.blue("Fetching project data...")).start();
763
855
  try {
764
- files = response.data.files || [];
765
856
  apiSpinner.succeed(chalk_1.default.green("Project data fetched successfully!"));
766
857
  const saveSpinner = (0, ora_1.default)(chalk_1.default.blue("Saving project files...")).start();
767
858
  files.forEach((file) => {
@@ -920,33 +1011,97 @@ commander_1.program
920
1011
  // hapico publish
921
1012
  commander_1.program.command("publish").action(async () => {
922
1013
  const publishSpinner = (0, ora_1.default)(chalk_1.default.blue("Publishing to Hapico...")).start();
923
- // Bước 1: Push mã nguồn lên server (tái sử dụng hàm pushProject)
924
- const pushSuccess = await pushProject(publishSpinner);
925
- if (!pushSuccess) {
926
- return; // Dừng nếu push thất bại
927
- }
928
- // Bước 2: Gọi API để publish
929
- const { accessToken } = getStoredToken();
930
1014
  const pwd = process.cwd();
931
- const projectId = getStoredProjectId(pwd);
1015
+ const { projectId, replicate } = getStoredProjectId(pwd);
932
1016
  if (!projectId) {
933
1017
  publishSpinner.fail(chalk_1.default.red("✗ Project ID not found. Please ensure hapico.config.json exists in the project directory."));
934
1018
  return;
935
1019
  }
1020
+ // Step 1: Push source code to main project and replicas
1021
+ const pushSuccess = await pushProject(publishSpinner, projectId);
1022
+ if (!pushSuccess) {
1023
+ return; // Stop if push to main project fails
1024
+ }
1025
+ // Push to replicated projects
1026
+ let allPushSuccess = true;
1027
+ if (replicate && Array.isArray(replicate) && replicate.length > 0) {
1028
+ publishSpinner.text = chalk_1.default.blue("Pushing to replicated projects...");
1029
+ for (const repId of replicate) {
1030
+ const success = await pushProject(publishSpinner, repId);
1031
+ if (!success) {
1032
+ allPushSuccess = false;
1033
+ console.warn(chalk_1.default.yellow(`⚠ Failed to push to replicated project ${repId}. Continuing...`));
1034
+ }
1035
+ else {
1036
+ console.log(chalk_1.default.green(`✓ Successfully pushed to replicated project ${repId}.`));
1037
+ }
1038
+ }
1039
+ }
1040
+ // Step 2: Publish main project
1041
+ const { accessToken } = getStoredToken();
936
1042
  const apiUrl = "https://base.myworkbeast.com/api/views/publish";
1043
+ let allPublishSuccess = true;
937
1044
  try {
938
- await axios_1.default.post(apiUrl, { view_id: parseInt(projectId, 10) }, // Đảm bảo viewId là number
939
- {
1045
+ await axios_1.default.post(apiUrl, { view_id: parseInt(projectId, 10) }, {
940
1046
  headers: {
941
1047
  Authorization: `Bearer ${accessToken}`,
942
1048
  "Content-Type": "application/json",
943
1049
  },
944
1050
  });
945
- publishSpinner.succeed(chalk_1.default.green("Project published successfully!"));
1051
+ publishSpinner.succeed(chalk_1.default.green(`Project ${projectId} published successfully!`));
946
1052
  }
947
1053
  catch (error) {
948
- console.log(error);
949
- publishSpinner.fail(chalk_1.default.red(`✗ Error publishing project: ${error.message}`));
1054
+ allPublishSuccess = false;
1055
+ publishSpinner.fail(chalk_1.default.red(`✗ Error publishing project ${projectId}: ${error.message}`));
950
1056
  }
1057
+ // Step 3: Publish replicated projects
1058
+ if (replicate && Array.isArray(replicate) && replicate.length > 0) {
1059
+ publishSpinner.text = chalk_1.default.blue("Publishing replicated projects...");
1060
+ for (const repId of replicate) {
1061
+ try {
1062
+ await axios_1.default.post(apiUrl, { view_id: parseInt(repId, 10) }, {
1063
+ headers: {
1064
+ Authorization: `Bearer ${accessToken}`,
1065
+ "Content-Type": "application/json",
1066
+ },
1067
+ });
1068
+ console.log(chalk_1.default.green(`✓ Successfully published replicated project ${repId}.`));
1069
+ }
1070
+ catch (error) {
1071
+ allPublishSuccess = false;
1072
+ console.warn(chalk_1.default.yellow(`⚠ Error publishing replicated project ${repId}: ${error.message}`));
1073
+ }
1074
+ }
1075
+ }
1076
+ if (allPushSuccess && allPublishSuccess) {
1077
+ publishSpinner.succeed(chalk_1.default.green("All projects published successfully!"));
1078
+ }
1079
+ else {
1080
+ publishSpinner.warn(chalk_1.default.yellow("Publishing completed with some errors."));
1081
+ }
1082
+ });
1083
+ commander_1.program.command("mirror").action(() => {
1084
+ console.log(chalk_1.default.cyan("🌐 Starting mirror mode..."));
1085
+ const pwd = process.cwd();
1086
+ const srcDir = path.join(pwd, "src");
1087
+ if (!fs.existsSync(srcDir)) {
1088
+ console.error(chalk_1.default.red("✗ Source directory 'src' does not exist. Please clone a project first."));
1089
+ return;
1090
+ }
1091
+ const fileManager = new FileManager(srcDir);
1092
+ const initialFiles = fileManager.listFiles();
1093
+ // Lấy danh sách file và viết ra 1 file .txt
1094
+ let content = ``;
1095
+ (0, lodash_1.map)(initialFiles, (file) => {
1096
+ content += `\`\`\`typescript
1097
+ // Path: ${file.path}
1098
+ ${file.content}
1099
+
1100
+ \`\`\`
1101
+ `;
1102
+ });
1103
+ const outputFile = path.join(pwd, "hapico_files.txt");
1104
+ fs.writeFileSync(outputFile, content, { encoding: "utf8" });
1105
+ console.log(chalk_1.default.green(`✓ File list saved to ${outputFile}`));
951
1106
  });
952
1107
  commander_1.program.parse(process.argv);