@hapico/cli 0.0.19 → 0.0.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/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.21").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";
@@ -415,7 +434,9 @@ commander_1.program
415
434
  commander_1.program
416
435
  .command("dev")
417
436
  .description("Start the project in development mode")
418
- .action(() => {
437
+ .option('--zversion <version>', 'Zalo version for QR code', '75')
438
+ .action((options) => {
439
+ var _a;
419
440
  const { accessToken } = getStoredToken();
420
441
  if (!accessToken) {
421
442
  console.error(chalk_1.default.red("✗ You need to login first. Use 'hapico login' command."));
@@ -457,7 +478,7 @@ commander_1.program
457
478
  const info = JSON.stringify({
458
479
  id: sessionId,
459
480
  createdAt: new Date().toISOString(),
460
- viewId: getStoredProjectId(pwd),
481
+ viewId: (_a = getStoredProjectId(pwd)) === null || _a === void 0 ? void 0 : _a.projectId,
461
482
  });
462
483
  // Convert info to base64
463
484
  const projectId = Buffer.from(info).toString("base64");
@@ -469,10 +490,44 @@ commander_1.program
469
490
  const room = new RoomState(`view_${projectId}`, []);
470
491
  const fileManager = new FileManager(srcDir);
471
492
  const initialFiles = fileManager.listFiles();
472
- room.files = initialFiles;
493
+ // get tsconfig.json
494
+ const tsconfigPath = path.join(srcDir, "..", "tsconfig.json");
495
+ if (fs.existsSync(tsconfigPath)) {
496
+ const content = fs.readFileSync(tsconfigPath, { encoding: "utf8" });
497
+ initialFiles.push({
498
+ path: "./tsconfig.json",
499
+ content,
500
+ });
501
+ }
502
+ // Remove All binary files
503
+ const supportExtensions = [
504
+ ".ts",
505
+ ".tsx",
506
+ ".js",
507
+ ".jsx",
508
+ ".json",
509
+ ".css",
510
+ ".html",
511
+ ".env",
512
+ ".env.local",
513
+ ".env.development",
514
+ ".env.production",
515
+ ".jsonc",
516
+ ".yml",
517
+ ".yaml",
518
+ ".md",
519
+ ".markdown",
520
+ ".txt",
521
+ ".xml",
522
+ ".config",
523
+ ];
524
+ const filteredFiles = initialFiles.filter((file) => {
525
+ return supportExtensions.some((ext) => file.path.endsWith(ext));
526
+ });
527
+ room.files = filteredFiles;
473
528
  room.connect(async () => {
474
529
  devSpinner.succeed(chalk_1.default.green("Project started in development mode!"));
475
- room.updateState("view", initialFiles);
530
+ room.updateState("view", filteredFiles);
476
531
  fileManager.setOnFileChange((filePath, content) => {
477
532
  var _a;
478
533
  const es5 = (_a = (0, exports.compileES5)(content, filePath)) !== null && _a !== void 0 ? _a : "";
@@ -487,12 +542,12 @@ commander_1.program
487
542
  room.updateState("view", updatedFiles);
488
543
  });
489
544
  // Fetch project info
490
- const projectInfo = getStoredProjectId(pwd);
491
- if (!projectInfo) {
545
+ const store = getStoredProjectId(pwd);
546
+ if (!store.projectId) {
492
547
  console.error(chalk_1.default.red("✗ Project ID not found. Please ensure hapico.config.json exists in the project directory."));
493
548
  return;
494
549
  }
495
- const project = await axios_1.default.get(`https://base.myworkbeast.com/api/views/${projectInfo}`, {
550
+ const project = await axios_1.default.get(`https://base.myworkbeast.com/api/views/${store.projectId}`, {
496
551
  headers: {
497
552
  Authorization: `Bearer ${accessToken}`,
498
553
  "Content-Type": "application/json",
@@ -503,8 +558,9 @@ commander_1.program
503
558
  return;
504
559
  }
505
560
  const projectType = project.data.type || "view";
561
+ const zversion = options.zversion || '75';
506
562
  if (projectType === "zalominiapp") {
507
- qrcode_terminal_1.default.generate(`https://zalo.me/s/3218692650896662017/player/${projectId}?env=TESTING&version=75`, { small: true }, (qrcode) => {
563
+ qrcode_terminal_1.default.generate(`https://zalo.me/s/3218692650896662017/player/${projectId}?env=TESTING&version=${zversion}`, { small: true }, (qrcode) => {
508
564
  console.log(chalk_1.default.cyan("📱 Scan this QR code to connect to the project:"));
509
565
  console.log(qrcode);
510
566
  });
@@ -518,7 +574,7 @@ commander_1.program
518
574
  });
519
575
  });
520
576
  // Hàm tái sử dụng để push mã nguồn lên server
521
- async function pushProject(spinner) {
577
+ async function pushProject(spinner, projectId) {
522
578
  const data = getStoredToken();
523
579
  const { accessToken } = data || {};
524
580
  if (!accessToken) {
@@ -526,11 +582,6 @@ async function pushProject(spinner) {
526
582
  return false;
527
583
  }
528
584
  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
585
  const srcDir = path.join(pwd, "src");
535
586
  if (!fs.existsSync(srcDir)) {
536
587
  spinner.fail(chalk_1.default.red("✗ Source directory 'src' does not exist. Please clone a project first."));
@@ -538,17 +589,30 @@ async function pushProject(spinner) {
538
589
  }
539
590
  const fileManager = new FileManager(srcDir);
540
591
  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
- }
592
+ // Supported files
593
+ const SUPPORT_FILES = [
594
+ "./.env",
595
+ "./.env.local",
596
+ "./.env.development",
597
+ "./.env.production",
598
+ "./package.json",
599
+ "./tsconfig.json",
600
+ ];
601
+ // Include supported files
602
+ SUPPORT_FILES.forEach((relativePath) => {
603
+ var _a;
604
+ const fullPath = path.join(pwd, relativePath);
605
+ if (fs.existsSync(fullPath)) {
606
+ console.log(chalk_1.default.green(`Including ${relativePath} in push for project ${projectId}.`));
607
+ const content = fs.readFileSync(fullPath, { encoding: "utf8" });
608
+ files.push({
609
+ path: relativePath,
610
+ content,
611
+ es5: (_a = (0, exports.compileES5)(content, fullPath)) !== null && _a !== void 0 ? _a : "",
612
+ });
613
+ }
614
+ });
615
+ spinner.text = `Pushing project source code to server for project ${projectId}...`;
552
616
  const apiUrl = `https://base.myworkbeast.com/api/views/${projectId}`;
553
617
  try {
554
618
  await axios_1.default.put(apiUrl, {
@@ -564,7 +628,7 @@ async function pushProject(spinner) {
564
628
  return true;
565
629
  }
566
630
  catch (error) {
567
- spinner.fail(chalk_1.default.red(`✗ Error saving project: ${error.message}`));
631
+ spinner.fail(chalk_1.default.red(`✗ Error saving project ${projectId}: ${error.message}`));
568
632
  return false;
569
633
  }
570
634
  }
@@ -573,9 +637,34 @@ commander_1.program
573
637
  .description("Push the project source code to the server")
574
638
  .action(async () => {
575
639
  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!"));
640
+ const pwd = process.cwd();
641
+ const { projectId, replicate } = getStoredProjectId(pwd);
642
+ if (!projectId) {
643
+ saveSpinner.fail(chalk_1.default.red("✗ Project ID not found. Please ensure hapico.config.json exists in the project directory."));
644
+ return;
645
+ }
646
+ // Push to the main project
647
+ const mainSuccess = await pushProject(saveSpinner, projectId);
648
+ let allSuccess = mainSuccess;
649
+ // Push to replicated projects if replicate array exists
650
+ if (replicate && Array.isArray(replicate) && replicate.length > 0) {
651
+ saveSpinner.text = chalk_1.default.blue("Pushing to replicated projects...");
652
+ for (const repId of replicate) {
653
+ const success = await pushProject(saveSpinner, repId);
654
+ if (!success) {
655
+ allSuccess = false;
656
+ console.warn(chalk_1.default.yellow(`⚠ Failed to push to replicated project ${repId}. Continuing...`));
657
+ }
658
+ else {
659
+ console.log(chalk_1.default.green(`✓ Successfully pushed to replicated project ${repId}.`));
660
+ }
661
+ }
662
+ }
663
+ if (allSuccess) {
664
+ saveSpinner.succeed(chalk_1.default.green("Project source code saved successfully to all projects!"));
665
+ }
666
+ else {
667
+ saveSpinner.warn(chalk_1.default.yellow("Project source code saved with some errors."));
579
668
  }
580
669
  });
581
670
  commander_1.program
@@ -637,26 +726,29 @@ commander_1.program
637
726
  .command("pull")
638
727
  .description("Pull the latest project files from the server")
639
728
  .action(async () => {
729
+ var _a, _b;
640
730
  const token = getStoredToken();
641
731
  if (!token) {
642
732
  console.error(chalk_1.default.red("✗ You need to login first. Use 'hapico login' command."));
643
733
  return;
644
734
  }
645
735
  const pwd = process.cwd();
646
- const projectId = getStoredProjectId(pwd);
736
+ const { projectId } = getStoredProjectId(pwd);
647
737
  if (!projectId) {
648
738
  console.error(chalk_1.default.red("✗ Project ID not found. Please ensure hapico.config.json exists in the project directory."));
649
739
  return;
650
740
  }
651
741
  const apiSpinner = (0, ora_1.default)(chalk_1.default.blue("Fetching latest project files...")).start();
652
742
  try {
653
- const response = await axios_1.default.get(`https://base.myworkbeast.com/api/views/${projectId}`, {
743
+ const response = await axios_1.default.get(`https://base.myworkbeast.com/api/views/v3/${projectId}`, {
654
744
  headers: {
655
- Authorization: `Bearer ${token}`,
745
+ Authorization: `Bearer ${token.accessToken}`,
656
746
  "Content-Type": "application/json",
657
747
  },
658
748
  });
659
- const files = response.data.files || [];
749
+ const code = (_a = response === null || response === void 0 ? void 0 : response.data) === null || _a === void 0 ? void 0 : _a.code;
750
+ const decompressedCode = pako_1.default.inflate(Uint8Array.from(atob(code), (c) => c.charCodeAt(0)), { to: "string" });
751
+ const files = ((_b = (0, exports.tryJSONParse)(decompressedCode)) === null || _b === void 0 ? void 0 : _b.files) || [];
660
752
  const fileManager = new FileManager(path.join(pwd, "src"));
661
753
  fileManager.syncFiles(files);
662
754
  apiSpinner.succeed(chalk_1.default.green("Project files updated successfully!"));
@@ -754,14 +846,15 @@ commander_1.program
754
846
  .replace(/[-:]/g, "")
755
847
  .replace(/\..+/, "");
756
848
  const filename = path.join(migrationsDir, `${index + 1}_migration_${timestamp}.sql`);
757
- fs.writeFileSync(filename, migration.sql, { encoding: "utf8" });
849
+ fs.writeFileSync(filename, migration.sql, {
850
+ encoding: "utf8",
851
+ });
758
852
  });
759
853
  console.log(chalk_1.default.green("✓ Migrations saved successfully!"));
760
854
  // Download project files and save to project
761
855
  let files = [];
762
856
  const apiSpinner = (0, ora_1.default)(chalk_1.default.blue("Fetching project data...")).start();
763
857
  try {
764
- files = response.data.files || [];
765
858
  apiSpinner.succeed(chalk_1.default.green("Project data fetched successfully!"));
766
859
  const saveSpinner = (0, ora_1.default)(chalk_1.default.blue("Saving project files...")).start();
767
860
  files.forEach((file) => {
@@ -920,33 +1013,97 @@ commander_1.program
920
1013
  // hapico publish
921
1014
  commander_1.program.command("publish").action(async () => {
922
1015
  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
1016
  const pwd = process.cwd();
931
- const projectId = getStoredProjectId(pwd);
1017
+ const { projectId, replicate } = getStoredProjectId(pwd);
932
1018
  if (!projectId) {
933
1019
  publishSpinner.fail(chalk_1.default.red("✗ Project ID not found. Please ensure hapico.config.json exists in the project directory."));
934
1020
  return;
935
1021
  }
1022
+ // Step 1: Push source code to main project and replicas
1023
+ const pushSuccess = await pushProject(publishSpinner, projectId);
1024
+ if (!pushSuccess) {
1025
+ return; // Stop if push to main project fails
1026
+ }
1027
+ // Push to replicated projects
1028
+ let allPushSuccess = true;
1029
+ if (replicate && Array.isArray(replicate) && replicate.length > 0) {
1030
+ publishSpinner.text = chalk_1.default.blue("Pushing to replicated projects...");
1031
+ for (const repId of replicate) {
1032
+ const success = await pushProject(publishSpinner, repId);
1033
+ if (!success) {
1034
+ allPushSuccess = false;
1035
+ console.warn(chalk_1.default.yellow(`⚠ Failed to push to replicated project ${repId}. Continuing...`));
1036
+ }
1037
+ else {
1038
+ console.log(chalk_1.default.green(`✓ Successfully pushed to replicated project ${repId}.`));
1039
+ }
1040
+ }
1041
+ }
1042
+ // Step 2: Publish main project
1043
+ const { accessToken } = getStoredToken();
936
1044
  const apiUrl = "https://base.myworkbeast.com/api/views/publish";
1045
+ let allPublishSuccess = true;
937
1046
  try {
938
- await axios_1.default.post(apiUrl, { view_id: parseInt(projectId, 10) }, // Đảm bảo viewId là number
939
- {
1047
+ await axios_1.default.post(apiUrl, { view_id: parseInt(projectId, 10) }, {
940
1048
  headers: {
941
1049
  Authorization: `Bearer ${accessToken}`,
942
1050
  "Content-Type": "application/json",
943
1051
  },
944
1052
  });
945
- publishSpinner.succeed(chalk_1.default.green("Project published successfully!"));
1053
+ publishSpinner.succeed(chalk_1.default.green(`Project ${projectId} published successfully!`));
946
1054
  }
947
1055
  catch (error) {
948
- console.log(error);
949
- publishSpinner.fail(chalk_1.default.red(`✗ Error publishing project: ${error.message}`));
1056
+ allPublishSuccess = false;
1057
+ publishSpinner.fail(chalk_1.default.red(`✗ Error publishing project ${projectId}: ${error.message}`));
1058
+ }
1059
+ // Step 3: Publish replicated projects
1060
+ if (replicate && Array.isArray(replicate) && replicate.length > 0) {
1061
+ publishSpinner.text = chalk_1.default.blue("Publishing replicated projects...");
1062
+ for (const repId of replicate) {
1063
+ try {
1064
+ await axios_1.default.post(apiUrl, { view_id: parseInt(repId, 10) }, {
1065
+ headers: {
1066
+ Authorization: `Bearer ${accessToken}`,
1067
+ "Content-Type": "application/json",
1068
+ },
1069
+ });
1070
+ console.log(chalk_1.default.green(`✓ Successfully published replicated project ${repId}.`));
1071
+ }
1072
+ catch (error) {
1073
+ allPublishSuccess = false;
1074
+ console.warn(chalk_1.default.yellow(`⚠ Error publishing replicated project ${repId}: ${error.message}`));
1075
+ }
1076
+ }
950
1077
  }
1078
+ if (allPushSuccess && allPublishSuccess) {
1079
+ publishSpinner.succeed(chalk_1.default.green("All projects published successfully!"));
1080
+ }
1081
+ else {
1082
+ publishSpinner.warn(chalk_1.default.yellow("Publishing completed with some errors."));
1083
+ }
1084
+ });
1085
+ commander_1.program.command("mirror").action(() => {
1086
+ console.log(chalk_1.default.cyan("🌐 Starting mirror mode..."));
1087
+ const pwd = process.cwd();
1088
+ const srcDir = path.join(pwd, "src");
1089
+ if (!fs.existsSync(srcDir)) {
1090
+ console.error(chalk_1.default.red("✗ Source directory 'src' does not exist. Please clone a project first."));
1091
+ return;
1092
+ }
1093
+ const fileManager = new FileManager(srcDir);
1094
+ const initialFiles = fileManager.listFiles();
1095
+ // Lấy danh sách file và viết ra 1 file .txt
1096
+ let content = ``;
1097
+ (0, lodash_1.map)(initialFiles, (file) => {
1098
+ content += `\`\`\`typescript
1099
+ // Path: ${file.path}
1100
+ ${file.content}
1101
+
1102
+ \`\`\`
1103
+ `;
1104
+ });
1105
+ const outputFile = path.join(pwd, "hapico_files.txt");
1106
+ fs.writeFileSync(outputFile, content, { encoding: "utf8" });
1107
+ console.log(chalk_1.default.green(`✓ File list saved to ${outputFile}`));
951
1108
  });
952
1109
  commander_1.program.parse(process.argv);