@hapico/cli 0.0.18 → 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");
@@ -54,6 +54,7 @@ const crypto_1 = require("crypto");
54
54
  const child_process_1 = require("child_process");
55
55
  const util_1 = require("util");
56
56
  const chalk_1 = __importDefault(require("chalk"));
57
+ const pako_1 = __importDefault(require("pako"));
57
58
  // Promisify exec for async usage
58
59
  const execPromise = (0, util_1.promisify)(child_process_1.exec);
59
60
  // Directory to store the token and project config
@@ -101,12 +102,27 @@ const getStoredProjectId = (projectDir) => {
101
102
  if (fs.existsSync(configFile)) {
102
103
  const data = fs.readFileSync(configFile, { encoding: "utf8" });
103
104
  const json = JSON.parse(data);
104
- return json.projectId || null;
105
+ return {
106
+ projectId: json.projectId || null,
107
+ replicate: json.replicate || [],
108
+ };
105
109
  }
106
- return null;
110
+ return {
111
+ projectId: null,
112
+ replicate: [],
113
+ };
107
114
  };
108
115
  // Khởi tạo cache bằng Map để lưu trữ kết quả compile
109
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;
110
126
  const compileES5 = (code, filePath) => {
111
127
  if (filePath && !filePath.endsWith(".ts") && !filePath.endsWith(".tsx")) {
112
128
  return code;
@@ -127,6 +143,7 @@ const compileES5 = (code, filePath) => {
127
143
  return result.code;
128
144
  }
129
145
  catch (error) {
146
+ console.log(chalk_1.default.red(`Error compiling code: ${error === null || error === void 0 ? void 0 : error.message}`));
130
147
  return "";
131
148
  }
132
149
  };
@@ -249,24 +266,38 @@ class RoomState {
249
266
  if (this.reconnectTimeout) {
250
267
  clearTimeout(this.reconnectTimeout);
251
268
  }
252
- this.ws = new ws_1.WebSocket(`https://ws2.myworkbeast.com/ws?room=${this.roomId}`);
253
- this.ws.onopen = () => {
269
+ this.ws = new ws_1.WebSocket(`wss://ws3.myworkbeast.com/ws?room=${this.roomId}`);
270
+ // Set binaryType to 'arraybuffer' to handle binary data
271
+ this.ws.binaryType = "arraybuffer";
272
+ this.ws.on("open", () => {
254
273
  console.log(chalk_1.default.green(`Connected to room: ${this.roomId}`));
255
274
  this.isConnected = true;
256
275
  this.reconnectAttempts = 0;
257
276
  onConnected === null || onConnected === void 0 ? void 0 : onConnected(); // Call the onConnected callback if provided
258
- };
259
- this.ws.onclose = () => {
277
+ });
278
+ this.ws.on("close", () => {
260
279
  console.log(chalk_1.default.yellow(`⚠ Disconnected from room: ${this.roomId}`));
261
280
  this.isConnected = false;
262
281
  this.reconnectAttempts++;
263
282
  const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30000);
264
283
  console.log(chalk_1.default.yellow(`Attempting to reconnect in ${delay / 1000}s...`));
265
284
  this.reconnectTimeout = setTimeout(() => this.connect(onConnected), delay);
266
- };
285
+ });
267
286
  this.ws.on("message", (data) => {
268
287
  try {
269
- const jsonStr = typeof data === "string" ? data : data.toString();
288
+ let jsonStr;
289
+ if (data instanceof ArrayBuffer) {
290
+ jsonStr = pako_1.default.inflate(data, { to: "string" });
291
+ }
292
+ else if (typeof data === "string") {
293
+ jsonStr = data;
294
+ }
295
+ else if (Buffer.isBuffer(data)) {
296
+ jsonStr = pako_1.default.inflate(data, { to: "string" });
297
+ }
298
+ else {
299
+ jsonStr = data.toString(); // Fallback nếu không nén
300
+ }
270
301
  const parsedData = JSON.parse(jsonStr);
271
302
  const includes = [
272
303
  "view",
@@ -316,10 +347,12 @@ class RoomState {
316
347
  }
317
348
  updateState(key, value) {
318
349
  if (this.ws && this.ws.readyState === ws_1.WebSocket.OPEN) {
319
- this.ws.send(JSON.stringify({
350
+ const message = JSON.stringify({
320
351
  type: "update",
321
352
  state: { [key]: value },
322
- }));
353
+ });
354
+ const compressed = pako_1.default.deflate(message); // Nén dữ liệu
355
+ this.ws.send(compressed); // Gửi binary
323
356
  }
324
357
  }
325
358
  disconnect() {
@@ -327,7 +360,7 @@ class RoomState {
327
360
  clearTimeout(this.reconnectTimeout);
328
361
  }
329
362
  if (this.ws) {
330
- this.ws.onclose = null; // Prevent reconnect on intentional close
363
+ this.ws.removeAllListeners("close"); // Prevent reconnect on intentional close
331
364
  this.ws.close();
332
365
  }
333
366
  }
@@ -338,11 +371,12 @@ class RoomState {
338
371
  return this.isConnected;
339
372
  }
340
373
  }
341
- commander_1.program.version("0.0.18").description("Hapico CLI for project management");
374
+ commander_1.program.version("0.0.20").description("Hapico CLI for project management");
342
375
  commander_1.program
343
376
  .command("clone <id>")
344
377
  .description("Clone a project by ID")
345
378
  .action(async (id) => {
379
+ var _a, _b;
346
380
  const { accessToken } = getStoredToken();
347
381
  if (!accessToken) {
348
382
  console.error(chalk_1.default.red("✗ You need to login first. Use 'hapico login' command."));
@@ -357,7 +391,9 @@ commander_1.program
357
391
  const apiSpinner = (0, ora_1.default)(chalk_1.default.blue("Fetching project data...")).start();
358
392
  try {
359
393
  const response = await axios_1.default.get(`https://base.myworkbeast.com/api/views/${id}`);
360
- 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) || [];
361
397
  apiSpinner.succeed(chalk_1.default.green("Project data fetched successfully!"));
362
398
  const templateSpinner = (0, ora_1.default)(chalk_1.default.blue("Downloading template...")).start();
363
399
  const TEMPLATE_URL = "https://files.hcm04.vstorage.vngcloud.vn/assets/template_zalominiapp_devmode.zip";
@@ -399,6 +435,7 @@ commander_1.program
399
435
  .command("dev")
400
436
  .description("Start the project in development mode")
401
437
  .action(() => {
438
+ var _a;
402
439
  const { accessToken } = getStoredToken();
403
440
  if (!accessToken) {
404
441
  console.error(chalk_1.default.red("✗ You need to login first. Use 'hapico login' command."));
@@ -440,7 +477,7 @@ commander_1.program
440
477
  const info = JSON.stringify({
441
478
  id: sessionId,
442
479
  createdAt: new Date().toISOString(),
443
- viewId: getStoredProjectId(pwd),
480
+ viewId: (_a = getStoredProjectId(pwd)) === null || _a === void 0 ? void 0 : _a.projectId,
444
481
  });
445
482
  // Convert info to base64
446
483
  const projectId = Buffer.from(info).toString("base64");
@@ -452,10 +489,44 @@ commander_1.program
452
489
  const room = new RoomState(`view_${projectId}`, []);
453
490
  const fileManager = new FileManager(srcDir);
454
491
  const initialFiles = fileManager.listFiles();
455
- 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;
456
527
  room.connect(async () => {
457
528
  devSpinner.succeed(chalk_1.default.green("Project started in development mode!"));
458
- room.updateState("view", initialFiles);
529
+ room.updateState("view", filteredFiles);
459
530
  fileManager.setOnFileChange((filePath, content) => {
460
531
  var _a;
461
532
  const es5 = (_a = (0, exports.compileES5)(content, filePath)) !== null && _a !== void 0 ? _a : "";
@@ -470,12 +541,12 @@ commander_1.program
470
541
  room.updateState("view", updatedFiles);
471
542
  });
472
543
  // Fetch project info
473
- const projectInfo = getStoredProjectId(pwd);
474
- if (!projectInfo) {
544
+ const store = getStoredProjectId(pwd);
545
+ if (!store.projectId) {
475
546
  console.error(chalk_1.default.red("✗ Project ID not found. Please ensure hapico.config.json exists in the project directory."));
476
547
  return;
477
548
  }
478
- 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}`, {
479
550
  headers: {
480
551
  Authorization: `Bearer ${accessToken}`,
481
552
  "Content-Type": "application/json",
@@ -487,7 +558,7 @@ commander_1.program
487
558
  }
488
559
  const projectType = project.data.type || "view";
489
560
  if (projectType === "zalominiapp") {
490
- qrcode_terminal_1.default.generate(`https://zalo.me/s/3218692650896662017/player/${projectId}`, { small: true }, (qrcode) => {
561
+ qrcode_terminal_1.default.generate(`https://zalo.me/s/3218692650896662017/player/${projectId}?env=TESTING&version=75`, { small: true }, (qrcode) => {
491
562
  console.log(chalk_1.default.cyan("📱 Scan this QR code to connect to the project:"));
492
563
  console.log(qrcode);
493
564
  });
@@ -500,59 +571,99 @@ commander_1.program
500
571
  }
501
572
  });
502
573
  });
503
- commander_1.program
504
- .command("push")
505
- .description("Push the project source code to the server")
506
- .action(() => {
574
+ // Hàm tái sử dụng để push mã nguồn lên server
575
+ async function pushProject(spinner, projectId) {
507
576
  const data = getStoredToken();
508
577
  const { accessToken } = data || {};
509
578
  if (!accessToken) {
510
- console.error(chalk_1.default.red("✗ You need to login first. Use 'hapico login' command."));
511
- return;
579
+ spinner.fail(chalk_1.default.red("✗ You need to login first. Use 'hapico login' command."));
580
+ return false;
512
581
  }
513
- const saveSpinner = (0, ora_1.default)(chalk_1.default.blue("Saving project source code...")).start();
514
582
  const pwd = process.cwd();
515
- const projectId = getStoredProjectId(pwd);
516
- if (!projectId) {
517
- saveSpinner.fail(chalk_1.default.red("✗ Project ID not found. Please ensure hapico.config.json exists in the project directory."));
518
- return;
519
- }
520
583
  const srcDir = path.join(pwd, "src");
521
584
  if (!fs.existsSync(srcDir)) {
522
- saveSpinner.fail(chalk_1.default.red("✗ Source directory 'src' does not exist. Please clone a project first."));
523
- return;
585
+ spinner.fail(chalk_1.default.red("✗ Source directory 'src' does not exist. Please clone a project first."));
586
+ return false;
524
587
  }
525
588
  const fileManager = new FileManager(srcDir);
526
589
  const files = fileManager.listFiles();
527
- // Nếu có file .env
528
- const envFile = path.join(pwd, ".env");
529
- console.log(chalk_1.default.cyan(`🔍 Checking for .env file at ${envFile}`));
530
- if (fs.existsSync(envFile)) {
531
- console.log(chalk_1.default.green(".env file found, including in push."));
532
- const content = fs.readFileSync(envFile, { encoding: "utf8" });
533
- files.push({
534
- path: "./.env",
535
- content,
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}...`;
614
+ const apiUrl = `https://base.myworkbeast.com/api/views/${projectId}`;
615
+ try {
616
+ await axios_1.default.put(apiUrl, {
617
+ code: JSON.stringify({
618
+ files,
619
+ }),
620
+ }, {
621
+ headers: {
622
+ Authorization: `Bearer ${accessToken}`,
623
+ "Content-Type": "application/json",
624
+ },
536
625
  });
626
+ return true;
627
+ }
628
+ catch (error) {
629
+ spinner.fail(chalk_1.default.red(`✗ Error saving project ${projectId}: ${error.message}`));
630
+ return false;
631
+ }
632
+ }
633
+ commander_1.program
634
+ .command("push")
635
+ .description("Push the project source code to the server")
636
+ .action(async () => {
637
+ const saveSpinner = (0, ora_1.default)(chalk_1.default.blue("Saving project source code...")).start();
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."));
537
666
  }
538
- const apiUrl = `https://base.myworkbeast.com/api/views/${projectId}`;
539
- axios_1.default
540
- .put(apiUrl, {
541
- code: JSON.stringify({
542
- files,
543
- }),
544
- }, {
545
- headers: {
546
- Authorization: `Bearer ${accessToken}`,
547
- "Content-Type": "application/json",
548
- },
549
- })
550
- .then(() => {
551
- saveSpinner.succeed(chalk_1.default.green("Project source code saved successfully!"));
552
- })
553
- .catch((error) => {
554
- saveSpinner.fail(chalk_1.default.red(`✗ Error saving project: ${error.message}`));
555
- });
556
667
  });
557
668
  commander_1.program
558
669
  .command("login")
@@ -613,26 +724,29 @@ commander_1.program
613
724
  .command("pull")
614
725
  .description("Pull the latest project files from the server")
615
726
  .action(async () => {
727
+ var _a, _b;
616
728
  const token = getStoredToken();
617
729
  if (!token) {
618
730
  console.error(chalk_1.default.red("✗ You need to login first. Use 'hapico login' command."));
619
731
  return;
620
732
  }
621
733
  const pwd = process.cwd();
622
- const projectId = getStoredProjectId(pwd);
734
+ const { projectId } = getStoredProjectId(pwd);
623
735
  if (!projectId) {
624
736
  console.error(chalk_1.default.red("✗ Project ID not found. Please ensure hapico.config.json exists in the project directory."));
625
737
  return;
626
738
  }
627
739
  const apiSpinner = (0, ora_1.default)(chalk_1.default.blue("Fetching latest project files...")).start();
628
740
  try {
629
- 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}`, {
630
742
  headers: {
631
- Authorization: `Bearer ${token}`,
743
+ Authorization: `Bearer ${token.accessToken}`,
632
744
  "Content-Type": "application/json",
633
745
  },
634
746
  });
635
- 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) || [];
636
750
  const fileManager = new FileManager(path.join(pwd, "src"));
637
751
  fileManager.syncFiles(files);
638
752
  apiSpinner.succeed(chalk_1.default.green("Project files updated successfully!"));
@@ -730,14 +844,15 @@ commander_1.program
730
844
  .replace(/[-:]/g, "")
731
845
  .replace(/\..+/, "");
732
846
  const filename = path.join(migrationsDir, `${index + 1}_migration_${timestamp}.sql`);
733
- fs.writeFileSync(filename, migration.sql, { encoding: "utf8" });
847
+ fs.writeFileSync(filename, migration.sql, {
848
+ encoding: "utf8",
849
+ });
734
850
  });
735
851
  console.log(chalk_1.default.green("✓ Migrations saved successfully!"));
736
852
  // Download project files and save to project
737
853
  let files = [];
738
854
  const apiSpinner = (0, ora_1.default)(chalk_1.default.blue("Fetching project data...")).start();
739
855
  try {
740
- files = response.data.files || [];
741
856
  apiSpinner.succeed(chalk_1.default.green("Project data fetched successfully!"));
742
857
  const saveSpinner = (0, ora_1.default)(chalk_1.default.blue("Saving project files...")).start();
743
858
  files.forEach((file) => {
@@ -893,4 +1008,100 @@ commander_1.program
893
1008
  });
894
1009
  }
895
1010
  });
1011
+ // hapico publish
1012
+ commander_1.program.command("publish").action(async () => {
1013
+ const publishSpinner = (0, ora_1.default)(chalk_1.default.blue("Publishing to Hapico...")).start();
1014
+ const pwd = process.cwd();
1015
+ const { projectId, replicate } = getStoredProjectId(pwd);
1016
+ if (!projectId) {
1017
+ publishSpinner.fail(chalk_1.default.red("✗ Project ID not found. Please ensure hapico.config.json exists in the project directory."));
1018
+ return;
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();
1042
+ const apiUrl = "https://base.myworkbeast.com/api/views/publish";
1043
+ let allPublishSuccess = true;
1044
+ try {
1045
+ await axios_1.default.post(apiUrl, { view_id: parseInt(projectId, 10) }, {
1046
+ headers: {
1047
+ Authorization: `Bearer ${accessToken}`,
1048
+ "Content-Type": "application/json",
1049
+ },
1050
+ });
1051
+ publishSpinner.succeed(chalk_1.default.green(`Project ${projectId} published successfully!`));
1052
+ }
1053
+ catch (error) {
1054
+ allPublishSuccess = false;
1055
+ publishSpinner.fail(chalk_1.default.red(`✗ Error publishing project ${projectId}: ${error.message}`));
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}`));
1106
+ });
896
1107
  commander_1.program.parse(process.argv);
package/bun.lock CHANGED
@@ -11,6 +11,7 @@
11
11
  "lodash": "^4.17.21",
12
12
  "open": "^10.2.0",
13
13
  "ora": "^8.2.0",
14
+ "pako": "^2.1.0",
14
15
  "prettier": "^3.6.2",
15
16
  "qrcode-terminal": "^0.12.0",
16
17
  "unzipper": "^0.12.3",
@@ -22,6 +23,7 @@
22
23
  "@types/commander": "^2.12.5",
23
24
  "@types/lodash": "^4.17.20",
24
25
  "@types/node": "^24.1.0",
26
+ "@types/pako": "^2.0.4",
25
27
  "@types/qrcode-terminal": "^0.12.2",
26
28
  "@types/unzipper": "^0.10.11",
27
29
  "@types/ws": "^8.18.1",
@@ -88,6 +90,8 @@
88
90
 
89
91
  "@types/node": ["@types/node@24.1.0", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w=="],
90
92
 
93
+ "@types/pako": ["@types/pako@2.0.4", "", {}, "sha512-VWDCbrLeVXJM9fihYodcLiIv0ku+AlOa/TQ1SvYOaBuyrSKgEcro95LJyIsJ4vSo6BXIxOKxiJAat04CmST9Fw=="],
94
+
91
95
  "@types/qrcode-terminal": ["@types/qrcode-terminal@0.12.2", "", {}, "sha512-v+RcIEJ+Uhd6ygSQ0u5YYY7ZM+la7GgPbs0V/7l/kFs2uO4S8BcIUEMoP7za4DNIqNnUD5npf0A/7kBhrCKG5Q=="],
92
96
 
93
97
  "@types/unzipper": ["@types/unzipper@0.10.11", "", { "dependencies": { "@types/node": "*" } }, "sha512-D25im2zjyMCcgL9ag6N46+wbtJBnXIr7SI4zHf9eJD2Dw2tEB5e+p5MYkrxKIVRscs5QV0EhtU9rgXSPx90oJg=="],
@@ -218,6 +222,8 @@
218
222
 
219
223
  "ora": ["ora@8.2.0", "", { "dependencies": { "chalk": "^5.3.0", "cli-cursor": "^5.0.0", "cli-spinners": "^2.9.2", "is-interactive": "^2.0.0", "is-unicode-supported": "^2.0.0", "log-symbols": "^6.0.0", "stdin-discarder": "^0.2.2", "string-width": "^7.2.0", "strip-ansi": "^7.1.0" } }, "sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw=="],
220
224
 
225
+ "pako": ["pako@2.1.0", "", {}, "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug=="],
226
+
221
227
  "prettier": ["prettier@3.6.2", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ=="],
222
228
 
223
229
  "process-nextick-args": ["process-nextick-args@2.0.1", "", {}, "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="],