@saidulbadhon/jssm-cli 1.6.6 → 1.7.0

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.
Files changed (2) hide show
  1. package/dist/index.js +174 -27
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -4173,15 +4173,11 @@ async function getProjects(host, apiKey) {
4173
4173
  const url = `${host}/projects`;
4174
4174
  return apiRequest(url, apiKey);
4175
4175
  }
4176
- async function pushEnvFile(host, apiKey, project, content, filename = ".env", environment) {
4176
+ async function pushEnvFile(host, apiKey, project, content, filename = ".env") {
4177
4177
  const url = `${host}/projects/${project}/files/push`;
4178
- const body = { content, filename };
4179
- if (environment) {
4180
- body.environment = environment;
4181
- }
4182
4178
  return apiRequest(url, apiKey, {
4183
4179
  method: "POST",
4184
- body: JSON.stringify(body)
4180
+ body: JSON.stringify({ content, filename })
4185
4181
  });
4186
4182
  }
4187
4183
  async function pullEnvFile(host, apiKey, project, filename = ".env") {
@@ -4563,9 +4559,28 @@ Upload to ${projectName}?`,
4563
4559
  }
4564
4560
 
4565
4561
  // src/commands/pull.ts
4566
- import { writeFile as writeFile3, mkdir as mkdir3 } from "fs/promises";
4562
+ import { writeFile as writeFile3, mkdir as mkdir3, readFile as readFile4 } from "fs/promises";
4567
4563
  import { join as join4, dirname } from "path";
4568
4564
  import { existsSync as existsSync2 } from "fs";
4565
+ function calculateLineDiff(oldContent, newContent) {
4566
+ if (!oldContent) {
4567
+ const lines = newContent.split("\n");
4568
+ const nonEmptyLines = lines.filter((line) => line.trim().length > 0).length;
4569
+ return { changedLines: nonEmptyLines, isNewFile: true };
4570
+ }
4571
+ const oldLines = oldContent.split("\n");
4572
+ const newLines = newContent.split("\n");
4573
+ const maxLength = Math.max(oldLines.length, newLines.length);
4574
+ let changedLines = 0;
4575
+ for (let i = 0; i < maxLength; i++) {
4576
+ const oldLine = i < oldLines.length ? oldLines[i] : "";
4577
+ const newLine = i < newLines.length ? newLines[i] : "";
4578
+ if (oldLine !== newLine) {
4579
+ changedLines++;
4580
+ }
4581
+ }
4582
+ return { changedLines, isNewFile: false };
4583
+ }
4569
4584
  async function pullCommand2(args2) {
4570
4585
  const flags = {};
4571
4586
  let pullAll = false;
@@ -4622,13 +4637,28 @@ async function pullCommand2(args2) {
4622
4637
  project,
4623
4638
  output
4624
4639
  );
4640
+ let existingContent = null;
4641
+ if (existsSync2(outputPath)) {
4642
+ try {
4643
+ existingContent = await readFile4(outputPath, "utf-8");
4644
+ } catch {
4645
+ existingContent = null;
4646
+ }
4647
+ }
4648
+ const { changedLines, isNewFile } = calculateLineDiff(
4649
+ existingContent,
4650
+ content
4651
+ );
4625
4652
  const dir = dirname(outputPath);
4626
4653
  if (dir !== process.cwd()) {
4627
4654
  await mkdir3(dir, { recursive: true });
4628
4655
  }
4629
4656
  await writeFile3(outputPath, content, "utf-8");
4630
4657
  const varCount = countVariables(content);
4631
- console.log(`\u2705 Wrote ${output} (${varCount} variables)`);
4658
+ const statusText = isNewFile ? "Created" : "Updated";
4659
+ console.log(
4660
+ `\u2705 ${statusText} ${output} (${varCount} variables, ${changedLines} line${changedLines !== 1 ? "s" : ""} changed)`
4661
+ );
4632
4662
  return;
4633
4663
  }
4634
4664
  console.log(`\u{1F4E5} Checking available files in ${project}...`);
@@ -4729,6 +4759,9 @@ async function pullCommand2(args2) {
4729
4759
  }
4730
4760
  let successCount = 0;
4731
4761
  let failCount = 0;
4762
+ let updatedFilesCount = 0;
4763
+ let newFilesCount = 0;
4764
+ let totalChangedLines = 0;
4732
4765
  for (const filename of selectedFiles) {
4733
4766
  try {
4734
4767
  const outputPath = join4(process.cwd(), filename);
@@ -4738,16 +4771,40 @@ async function pullCommand2(args2) {
4738
4771
  project,
4739
4772
  filename
4740
4773
  );
4774
+ let existingContent = null;
4775
+ if (existsSync2(outputPath)) {
4776
+ try {
4777
+ existingContent = await readFile4(outputPath, "utf-8");
4778
+ } catch {
4779
+ existingContent = null;
4780
+ }
4781
+ }
4782
+ const { changedLines, isNewFile } = calculateLineDiff(
4783
+ existingContent,
4784
+ content
4785
+ );
4741
4786
  const dir = dirname(outputPath);
4742
4787
  if (dir !== process.cwd()) {
4743
4788
  await mkdir3(dir, { recursive: true });
4744
4789
  }
4745
4790
  await writeFile3(outputPath, content, "utf-8");
4746
4791
  const varCount = countVariables(content);
4792
+ if (isNewFile) {
4793
+ newFilesCount++;
4794
+ } else {
4795
+ updatedFilesCount++;
4796
+ }
4797
+ totalChangedLines += changedLines;
4747
4798
  if (selectedFiles.length === 1) {
4748
- console.log(`\u2705 Wrote ${filename} (${varCount} variables)`);
4799
+ const statusText = isNewFile ? "Created" : "Updated";
4800
+ console.log(
4801
+ `\u2705 ${statusText} ${filename} (${varCount} variables, ${changedLines} line${changedLines !== 1 ? "s" : ""} changed)`
4802
+ );
4749
4803
  } else {
4750
- console.log(`\u2705 ${filename}: Pulled (${varCount} variables)`);
4804
+ const statusText = isNewFile ? "Created" : "Updated";
4805
+ console.log(
4806
+ `\u2705 ${filename}: ${statusText} (${varCount} variables, ${changedLines} line${changedLines !== 1 ? "s" : ""} changed)`
4807
+ );
4751
4808
  }
4752
4809
  successCount++;
4753
4810
  } catch (error) {
@@ -4762,9 +4819,29 @@ async function pullCommand2(args2) {
4762
4819
  if (successCount > 0) {
4763
4820
  console.log(` \u2705 Success: ${successCount} file(s)`);
4764
4821
  }
4822
+ if (newFilesCount > 0) {
4823
+ console.log(` \u{1F4C4} New files: ${newFilesCount}`);
4824
+ }
4825
+ if (updatedFilesCount > 0) {
4826
+ console.log(` \u{1F4DD} Updated files: ${updatedFilesCount}`);
4827
+ }
4828
+ if (totalChangedLines > 0) {
4829
+ console.log(` \u{1F4CF} Total lines changed: ${totalChangedLines}`);
4830
+ }
4765
4831
  if (failCount > 0) {
4766
4832
  console.log(` \u274C Failed: ${failCount} file(s)`);
4767
4833
  }
4834
+ } else if (successCount === 1) {
4835
+ if (newFilesCount > 0) {
4836
+ console.log(`
4837
+ \u{1F4CA} Summary: New file created`);
4838
+ } else if (updatedFilesCount > 0) {
4839
+ console.log(`
4840
+ \u{1F4CA} Summary: File updated`);
4841
+ }
4842
+ if (totalChangedLines > 0) {
4843
+ console.log(` \u{1F4CF} Lines changed: ${totalChangedLines}`);
4844
+ }
4768
4845
  }
4769
4846
  if (failCount > 0 && successCount === 0) {
4770
4847
  process.exit(1);
@@ -4783,8 +4860,27 @@ function countVariables(content) {
4783
4860
  }
4784
4861
 
4785
4862
  // src/commands/push.ts
4786
- import { readFile as readFile4 } from "fs/promises";
4863
+ import { readFile as readFile5 } from "fs/promises";
4787
4864
  import { join as join5 } from "path";
4865
+ function calculateLineDiff2(oldContent, newContent) {
4866
+ if (!oldContent) {
4867
+ const lines = newContent.split("\n");
4868
+ const nonEmptyLines = lines.filter((line) => line.trim().length > 0).length;
4869
+ return { changedLines: nonEmptyLines, isNewFile: true };
4870
+ }
4871
+ const oldLines = oldContent.split("\n");
4872
+ const newLines = newContent.split("\n");
4873
+ const maxLength = Math.max(oldLines.length, newLines.length);
4874
+ let changedLines = 0;
4875
+ for (let i = 0; i < maxLength; i++) {
4876
+ const oldLine = i < oldLines.length ? oldLines[i] : "";
4877
+ const newLine = i < newLines.length ? newLines[i] : "";
4878
+ if (oldLine !== newLine) {
4879
+ changedLines++;
4880
+ }
4881
+ }
4882
+ return { changedLines, isNewFile: false };
4883
+ }
4788
4884
  async function pushCommand2(args2) {
4789
4885
  const flags = {};
4790
4886
  for (let i = 0; i < args2.length; i++) {
@@ -4810,7 +4906,6 @@ async function pushCommand2(args2) {
4810
4906
  process.exit(1);
4811
4907
  }
4812
4908
  const project = flags.p || flags.project || config.project;
4813
- const environment = flags.e || flags.env;
4814
4909
  let inputFile = flags.in || flags.input;
4815
4910
  let searchDepth = 10;
4816
4911
  if (flags.depth) {
@@ -4882,27 +4977,27 @@ async function pushCommand2(args2) {
4882
4977
  } else {
4883
4978
  selectedFiles = [inputFile];
4884
4979
  }
4885
- const envSuffix = environment ? ` (env: ${environment})` : "";
4886
4980
  if (selectedFiles.length === 1) {
4887
- console.log(
4888
- `
4889
- \u{1F4E4} Pushing ${selectedFiles[0]} to ${project}${envSuffix}...`
4890
- );
4981
+ console.log(`
4982
+ \u{1F4E4} Pushing ${selectedFiles[0]} to ${project}...`);
4891
4983
  } else {
4892
4984
  console.log(
4893
4985
  `
4894
- \u{1F4E4} Pushing ${selectedFiles.length} files to ${project}${envSuffix}...
4986
+ \u{1F4E4} Pushing ${selectedFiles.length} files to ${project}...
4895
4987
  `
4896
4988
  );
4897
4989
  }
4898
4990
  let successCount = 0;
4899
4991
  let failCount = 0;
4992
+ let updatedFilesCount = 0;
4993
+ let newFilesCount = 0;
4994
+ let totalChangedLines = 0;
4900
4995
  for (const file of selectedFiles) {
4901
4996
  let fileContent = "";
4902
4997
  let fileSize = 0;
4903
4998
  try {
4904
4999
  const inputPath = join5(process.cwd(), file);
4905
- fileContent = await readFile4(inputPath, "utf-8");
5000
+ fileContent = await readFile5(inputPath, "utf-8");
4906
5001
  fileSize = Buffer.byteLength(fileContent, "utf-8");
4907
5002
  const variables = parseVariables(fileContent, file);
4908
5003
  if (variables.length === 0) {
@@ -4928,27 +5023,59 @@ async function pushCommand2(args2) {
4928
5023
  failCount++;
4929
5024
  continue;
4930
5025
  }
4931
- await pushEnvFile(
5026
+ let existingContent = null;
5027
+ try {
5028
+ existingContent = await pullEnvFile(
5029
+ config.host,
5030
+ config.authToken,
5031
+ project,
5032
+ file
5033
+ );
5034
+ } catch {
5035
+ existingContent = null;
5036
+ }
5037
+ const { changedLines, isNewFile } = calculateLineDiff2(
5038
+ existingContent,
5039
+ fileContent
5040
+ );
5041
+ const result = await pushEnvFile(
4932
5042
  config.host,
4933
5043
  config.authToken,
4934
5044
  project,
4935
5045
  fileContent,
4936
- file,
5046
+ file
4937
5047
  // Use full relative path, not just filename
4938
- environment
4939
- // Optional - defaults to "default" on server if not provided
4940
5048
  );
5049
+ const tagInfo = result.environmentTag ? ` [${result.environmentTag}${result.tagSource === "auto" ? " auto" : ""}]` : "";
5050
+ if (isNewFile) {
5051
+ newFilesCount++;
5052
+ } else {
5053
+ updatedFilesCount++;
5054
+ }
5055
+ totalChangedLines += changedLines;
4941
5056
  if (selectedFiles.length === 1) {
5057
+ const statusText = isNewFile ? "Created" : "Updated";
4942
5058
  console.log(
4943
- `\u2705 Pushed ${file} (${variables.length} variables) to ${project}${envSuffix}`
5059
+ `\u2705 ${statusText} ${file} (${variables.length} variables, ${changedLines} line${changedLines !== 1 ? "s" : ""} changed) to ${project}${tagInfo}`
4944
5060
  );
4945
5061
  } else {
4946
- console.log(`\u2705 ${file}: Pushed (${variables.length} variables)`);
5062
+ const statusText = isNewFile ? "Created" : "Updated";
5063
+ console.log(
5064
+ `\u2705 ${file}: ${statusText} (${variables.length} variables, ${changedLines} line${changedLines !== 1 ? "s" : ""} changed)${tagInfo}`
5065
+ );
4947
5066
  }
4948
5067
  successCount++;
4949
5068
  } catch (error) {
4950
5069
  const message = error instanceof Error ? error.message : String(error);
4951
- console.error(`\u274C ${file}: Failed to push - ${message}`);
5070
+ let helpText = "";
5071
+ if (message.includes("Project") && message.includes("not found")) {
5072
+ helpText = `
5073
+ \u{1F4A1} Hint: Make sure the project '${project}' exists. You can create it with: jssm init -p ${project}`;
5074
+ } else if (message.includes("Insufficient permissions")) {
5075
+ helpText = `
5076
+ \u{1F4A1} Hint: You don't have write access to project '${project}'`;
5077
+ }
5078
+ console.error(`\u274C ${file}: Failed to push - ${message}${helpText}`);
4952
5079
  failCount++;
4953
5080
  }
4954
5081
  }
@@ -4956,9 +5083,29 @@ async function pushCommand2(args2) {
4956
5083
  console.log(`
4957
5084
  \u{1F4CA} Summary:`);
4958
5085
  console.log(` \u2705 Success: ${successCount} file(s)`);
5086
+ if (newFilesCount > 0) {
5087
+ console.log(` \u{1F4C4} New files: ${newFilesCount}`);
5088
+ }
5089
+ if (updatedFilesCount > 0) {
5090
+ console.log(` \u{1F4DD} Updated files: ${updatedFilesCount}`);
5091
+ }
5092
+ if (totalChangedLines > 0) {
5093
+ console.log(` \u{1F4CF} Total lines changed: ${totalChangedLines}`);
5094
+ }
4959
5095
  if (failCount > 0) {
4960
5096
  console.log(` \u274C Failed: ${failCount} file(s)`);
4961
5097
  }
5098
+ } else if (successCount === 1) {
5099
+ if (newFilesCount > 0) {
5100
+ console.log(`
5101
+ \u{1F4CA} Summary: New file created`);
5102
+ } else if (updatedFilesCount > 0) {
5103
+ console.log(`
5104
+ \u{1F4CA} Summary: File updated`);
5105
+ }
5106
+ if (totalChangedLines > 0) {
5107
+ console.log(` \u{1F4CF} Lines changed: ${totalChangedLines}`);
5108
+ }
4962
5109
  }
4963
5110
  if (failCount > 0) {
4964
5111
  process.exit(1);
@@ -5883,7 +6030,7 @@ ${import_chalk.default.bold("Usage in Docker:")}
5883
6030
 
5884
6031
  // src/utils/versionCheck.ts
5885
6032
  var PACKAGE_NAME = "@saidulbadhon/jssm-cli";
5886
- var CURRENT_VERSION = "1.6.6";
6033
+ var CURRENT_VERSION = "1.7.0";
5887
6034
  async function getLatestVersion() {
5888
6035
  try {
5889
6036
  const response = await fetch(`https://registry.npmjs.org/${PACKAGE_NAME}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@saidulbadhon/jssm-cli",
3
- "version": "1.6.6",
3
+ "version": "1.7.0",
4
4
  "private": false,
5
5
  "description": "CLI for JSSM - Simple environment variable manager",
6
6
  "author": "Saidul Badhon",