@n42/cli 0.1.89 → 0.1.96

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/README.md CHANGED
@@ -85,9 +85,11 @@ n42 history <participantId>
85
85
  Example output:
86
86
 
87
87
  ```bash
88
+ Found 2 artefact(s)
89
+
88
90
  DATE PID FILE
89
- 2026-01-30 15:42:18 9915:helger ~/.node42/artefacts/discovery/b91f83e2.svg
90
- 2026-01-30 15:42:10 9915:helger ~/.node42/artefacts/discovery/ba1cbc8d.svg
91
+ 2026-01-30 16:53:28 9930:de81347... 5e0800fc.svg [Open]
92
+ 2026-01-30 16:53:08 9930:de81347... 6eeb73d0.svg [Open]
91
93
  ```
92
94
 
93
95
  #### Available options:
package/docs/DB_USAGE.md CHANGED
@@ -33,7 +33,6 @@ Example `db.json`:
33
33
  {
34
34
  "user": {},
35
35
  "artefacts": [],
36
- "usage": {},
37
36
  }
38
37
  ```
39
38
 
package/jest.config.js ADDED
@@ -0,0 +1,6 @@
1
+ module.exports = {
2
+ testEnvironment: "node",
3
+ verbose: true,
4
+ coverageDirectory: "coverage",
5
+ testMatch: ["**/test/**/*.test.js"]
6
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@n42/cli",
3
- "version": "0.1.89",
3
+ "version": "0.1.96",
4
4
  "description": "Node42 CLI – Command-line interface for Peppol eDelivery path discovery, diagnostics, and tooling",
5
5
  "keywords": [
6
6
  "node42"
package/src/auth.js CHANGED
@@ -3,16 +3,19 @@ const { NODE42_DIR, TOKENS_FILE, API_URL, EP_SIGNIN, EP_REFRESH, EP_ME } = requi
3
3
  const { handleError } = require("./errors");
4
4
  const { getUserWithIndex } = require("./user");
5
5
  const { clearScreen, ask, startSpinner } = require("./utils");
6
+
6
7
  const db = require("./db");
8
+ const pkg = require("../package.json");
9
+ const C = require("./colors");
7
10
 
8
11
 
9
12
  async function login() {
10
- clearScreen("Sign in to Node42");
13
+ clearScreen(`Node42 CLI v${pkg.version}\n\n${C.BOLD}Sign in to your account${C.RESET}`);
11
14
  let user = getUserWithIndex(0);
12
15
 
13
16
  const username = await ask("Username", user.userMail ?? "");
14
17
  const password = await ask("Password", null, true);
15
- //console.log(username + ", " + password);
18
+ console.log();
16
19
 
17
20
  let stopSpinner = startSpinner();
18
21
 
@@ -24,8 +27,7 @@ async function login() {
24
27
 
25
28
  if (!res.ok) {
26
29
  stopSpinner();
27
-
28
- console.error(`Login failed (${res.status}) – Invalid credentials`);
30
+ handleError({ code: "N42E-10108", message: "Signin failed: Invalid credentials"});
29
31
  process.exit(1);
30
32
  }
31
33
 
@@ -34,7 +36,7 @@ async function login() {
34
36
 
35
37
  const { accessToken, refreshToken, idToken } = tokens;
36
38
  if (!accessToken || !refreshToken || !idToken) {
37
- console.error("Invalid auth response");
39
+ handleError({ code: "N42E-6123", message: "Token missing"})
38
40
  process.exit(1);
39
41
  }
40
42
 
@@ -50,14 +52,12 @@ async function login() {
50
52
  stopSpinner();
51
53
 
52
54
  if (!authenticated) {
53
- console.error("Not authenticated");
55
+ handleError({ code: "N42E-10108", message: "Signin failed: Not authenticated"});
54
56
  process.exit(1);
55
57
  }
56
58
 
57
59
  user = getUserWithIndex(0);
58
- console.log(
59
- `Authenticated as ${user.userName} <${user.userMail}> (${user.role})`
60
- );
60
+ console.log(`Authenticated as ${user.userName} <${C.BLUE}${user.userMail}${C.RESET}> ${C.DIM}(${user.role})${C.RESET}\n`);
61
61
  }
62
62
 
63
63
  function logout() {
@@ -69,7 +69,7 @@ function logout() {
69
69
 
70
70
  function loadTokens() {
71
71
  if (!fs.existsSync(TOKENS_FILE)) {
72
- console.error("Tokens missing...\nRun: n42 login");
72
+ handleError({ code: "N42E-9033", message: "Token missing: You are not signed in"})
73
73
  process.exit(1);
74
74
  }
75
75
  return JSON.parse(fs.readFileSync(TOKENS_FILE, "utf8"));
@@ -77,7 +77,7 @@ function loadTokens() {
77
77
 
78
78
  async function checkAuth() {
79
79
  if (!fs.existsSync(TOKENS_FILE)) {
80
- handleError({ code: "N42E-9033", message: "Token missing..."})
80
+ handleError({ code: "N42E-9033", message: "Token missing: You are not signed in"})
81
81
  return false;
82
82
  }
83
83
 
package/src/cli.js CHANGED
@@ -4,7 +4,7 @@ const { Command } = require("commander");
4
4
  const { login, logout, checkAuth } = require("./auth");
5
5
  const { getUserWithIndex, getUserUsage } = require("./user");
6
6
  const { runDiscovery } = require("./discover");
7
- const { clearScreen, startSpinner, validateEnv, validateId, createAppDirs, getArtefactExt, cleanAppDirs } = require("./utils");
7
+ const { clearScreen, startSpinner, validateEnv, validateId, createAppDirs, capitalize, cleanAppDirs } = require("./utils");
8
8
  const { NODE42_DIR, ARTEFACTS_DIR, DEFAULT_OUTPUT, DEFAULT_FORMAT } = require("./config");
9
9
 
10
10
  createAppDirs();
@@ -12,6 +12,7 @@ createAppDirs();
12
12
  const program = new Command();
13
13
  const pkg = require("../package.json");
14
14
  const db = require("./db");
15
+ const C = require("./colors");
15
16
 
16
17
  const fs = require("fs");
17
18
  const path = require("path");
@@ -27,7 +28,7 @@ program
27
28
  .action((shell) => {
28
29
 
29
30
  if (shell !== "bash") {
30
- console.error("Only bash supported");
31
+ console.error(`${C.RED}Only bash supported${C.RESET}`);
31
32
  return;
32
33
  }
33
34
 
@@ -35,9 +36,8 @@ program
35
36
  const dest = path.join(NODE42_DIR, "completion.bash");
36
37
  fs.copyFileSync(src, dest);
37
38
 
38
- console.log(`Completion script saved to ${dest}`);
39
- console.log(`Run this once:`);
40
- console.log(`source ${dest}`);
39
+ console.log(`${C.DIM}Completion script saved to ${dest}${C.RESET}`);
40
+ console.log(`Run: ${C.BOLD}source ${dest}${C.RESET}\n`);
41
41
  });
42
42
 
43
43
  program
@@ -80,21 +80,21 @@ program
80
80
  const user = getUserWithIndex(0);
81
81
  const currentMonth = new Date().toISOString().slice(0, 7);
82
82
  console.log(`Node42 CLI v${pkg.version}
83
- User
84
- ID : ${user.id}
83
+ ${C.BOLD}User${C.RESET}
84
+ ID : ${C.CYAN}${user.id}${C.RESET}
85
85
  Name : ${user.userName}
86
86
  Email : ${user.userMail}
87
87
  Role : ${user.role}
88
88
 
89
- Rate Limits
90
- Discovery : ${user.rateLimits.discovery}
91
- Transactions : ${user.rateLimits.transactions}
92
- Validation : ${user.rateLimits.validation}
89
+ ${C.BOLD}Rate Limits${C.RESET}
90
+ Discovery : ${C.RED}${user.rateLimits.discovery}${C.RESET}
91
+ Transactions : ${C.RED}${user.rateLimits.transactions}${C.RESET}
92
+ Validation : ${C.RED}${user.rateLimits.validation}${C.RESET}
93
93
 
94
- Usage (Current Month)
95
- Discovery : ${user.serviceUsage.discovery[currentMonth] ?? 0}
96
- Transactions : ${user.serviceUsage.transactions[currentMonth] ?? 0}
97
- Validation : ${user.serviceUsage.validation[currentMonth] ?? 0}
94
+ ${C.BOLD}Usage${C.RESET} ${C.DIM}(Current Month)${C.RESET}
95
+ Discovery : ${C.RED}${user.serviceUsage.discovery[currentMonth] ?? 0}${C.RESET}
96
+ Transactions : ${C.RED}${user.serviceUsage.transactions[currentMonth] ?? 0}${C.RESET}
97
+ Validation : ${C.RED}${user.serviceUsage.validation[currentMonth] ?? 0}${C.RESET}
98
98
  `);
99
99
  });
100
100
 
@@ -110,8 +110,9 @@ program
110
110
  usage = 0;
111
111
  }
112
112
 
113
- clearScreen(`Node42 CLI v${pkg.version}`);
114
- console.log(`Usage for ${service} (${currentMonth}): ${usage}`);
113
+ clearScreen(`Node42 CLI v${pkg.version}\n`);
114
+ console.log(`${C.BOLD}${capitalize(service)} usage${C.RESET}`);
115
+ console.log(` • ${currentMonth}: ${C.RED}${usage}${C.RESET}\n`);
115
116
  });
116
117
 
117
118
  program
@@ -163,24 +164,27 @@ program
163
164
 
164
165
  // ---- OUTPUT ----
165
166
  clearScreen(`Node42 CLI v${pkg.version}`);
166
- console.log(`Found ${artefacts.length} artefact(s)${filterInfo}\n`);
167
+ console.log(`Found ${C.RED}${artefacts.length}${C.RESET} artefact(s)${filterInfo}\n`);
167
168
 
168
169
  const DATE = "DATE".padEnd(19);
169
170
  const PID = "PID".padEnd(15);
170
171
  const FILE = "FILE";
171
- console.log(`${DATE} ${PID} ${FILE}`);
172
+ console.log(`${DATE} ${C.CYAN}${PID}${C.RESET} ${FILE}`);
172
173
 
173
174
  for (const item of artefacts) {
174
175
  const d = new Date(item.createdAt);
175
- const dt = d.toISOString().slice(0, 19).replace("T", " ");
176
+ const iso = d.toISOString(); // 2026-01-30T16:53:28.123Z
177
+ const date = iso.slice(0, 10); // 2026-01-30
178
+ const time = iso.slice(11, 19); // 16:53:28
176
179
  const file = path.join(ARTEFACTS_DIR, `${item.file}`);
180
+ const link = `\u001B]8;;file://${file}\u0007Open\u001B]8;;\u0007`
177
181
 
178
182
  let pid = item.participantId;
179
183
  if (!participantId) {
180
184
  pid = pid.length > 15 ? pid.substring(0, 12) + "..." : pid
181
- console.log(`${dt} ${pid.padEnd(15)} ${file}`);
185
+ console.log(`${date} ${C.DIM}${time}${C.RESET} ${C.CYAN}${pid.padEnd(15)}${C.RESET} ${item.file} ${C.BLUE}[${link}]${C.RESET}`);
182
186
  } else {
183
- console.log(`${dt} ${file}`);
187
+ console.log(`${date} ${C.DIM}${time}${C.RESET} ${item.file} ${C.BLUE}[${link}]${C.RESET}`);
184
188
  }
185
189
  }
186
190
 
package/src/colors.js ADDED
@@ -0,0 +1,42 @@
1
+ const RESET = "\x1b[0m";
2
+
3
+ const BLACK = "\x1b[30m";
4
+ const RED = "\x1b[31m";
5
+ const GREEN = "\x1b[32m";
6
+ const YELLOW = "\x1b[33m";
7
+ const BLUE = "\x1b[34m";
8
+ const MAGENTA = "\x1b[35m";
9
+ const CYAN = "\x1b[36m";
10
+ const WHITE = "\x1b[37m";
11
+
12
+ const BOLD = "\x1b[1m";
13
+ const DIM = "\x1b[2m";
14
+ const UNDERLINE = "\x1b[4m";
15
+
16
+ const RED_BOLD = "\x1b[1;31m";
17
+ const GREEN_BOLD = "\x1b[1;32m";
18
+ const YELLOW_BOLD = "\x1b[1;33m";
19
+ const BLUE_BOLD = "\x1b[1;34m";
20
+ const MAGENTA_BOLD = "\x1b[1;35m";
21
+ const CYAN_BOLD = "\x1b[1;36m";
22
+
23
+ module.exports = {
24
+ RESET,
25
+ BLACK,
26
+ RED,
27
+ GREEN,
28
+ YELLOW,
29
+ BLUE,
30
+ MAGENTA,
31
+ CYAN,
32
+ WHITE,
33
+ BOLD,
34
+ DIM,
35
+ UNDERLINE,
36
+ RED_BOLD,
37
+ GREEN_BOLD,
38
+ YELLOW_BOLD,
39
+ BLUE_BOLD,
40
+ MAGENTA_BOLD,
41
+ CYAN_BOLD
42
+ };
package/src/discover.js CHANGED
@@ -2,6 +2,7 @@ const fs = require("fs");
2
2
  const path = require("path");
3
3
  const pkg = require("../package.json");
4
4
  const db = require("./db");
5
+ const C = require("./colors");
5
6
 
6
7
  const { fetchWithAuth } = require("./auth");
7
8
  const { API_URL, EP_DISCOVER, DEFAULT_OUTPUT, DEFAULT_FORMAT, ARTEFACTS_DIR } = require("./config");
@@ -88,7 +89,7 @@ async function runDiscovery(participantId, options) {
88
89
  }
89
90
  };
90
91
 
91
- clearScreen(`Node42 CLI v${pkg.version}`);
92
+ clearScreen(`Node42 CLI v${pkg.version}\n`);
92
93
  const stopSpinner = startSpinner();
93
94
 
94
95
  const url = `${API_URL}/${EP_DISCOVER}?output=${output}&format=${format}`;
@@ -153,9 +154,12 @@ async function runDiscovery(participantId, options) {
153
154
  const file = path.join(ARTEFACTS_DIR, `${fileName}`);
154
155
  fs.writeFileSync(file, svg);
155
156
 
156
- console.log(`Discovery completed`);
157
- console.log(`Usage : ${serviceUsage} / ${rateLimit}`);
158
- console.log(`Artefact : ${file}\n`);
157
+ const link = `\u001B]8;;file://${file}\u0007Open\u001B]8;;\u0007`;
158
+
159
+ console.log(`${C.BOLD}Discovery completed${C.RESET}`);
160
+ console.log(`PID : ${C.CYAN}${participantId}${C.RESET}`);
161
+ console.log(`Artefact : ${fileName} ${C.BLUE}[${link}]${C.RESET}`);
162
+ console.log(`Usage : ${C.RED}${serviceUsage}${C.RESET} ${C.DIM}(${rateLimit})${C.RESET}\n`);
159
163
  return;
160
164
  }
161
165
 
@@ -170,9 +174,12 @@ async function runDiscovery(participantId, options) {
170
174
  const file = path.join(ARTEFACTS_DIR, `${fileName}`);
171
175
  fs.writeFileSync(file, text);
172
176
 
173
- console.log(`Discovery completed`);
174
- console.log(`Usage : ${serviceUsage} / ${rateLimit}`);
175
- console.log(`Artefact : ${file}\n`);
177
+ const link = `\u001B]8;;file://${file}\u0007Open\u001B]8;;\u0007`;
178
+
179
+ console.log(`${C.BOLD}Discovery completed${C.RESET}`);
180
+ console.log(`PID : ${C.CYAN}${participantId}${C.RESET}`);
181
+ console.log(`Artefact : ${fileName} ${C.BLUE}[${link}]${C.RESET}`);
182
+ console.log(`Usage : ${C.RED}${serviceUsage}${C.RESET} ${C.DIM}(${rateLimit})${C.RESET}\n`);
176
183
  return;
177
184
  }
178
185
 
@@ -187,9 +194,12 @@ async function runDiscovery(participantId, options) {
187
194
  const file = path.join(ARTEFACTS_DIR, `${fileName}`);
188
195
  fs.writeFileSync(file, JSON.stringify(json, null, 2));
189
196
 
190
- console.log(`Discovery completed`);
191
- console.log(`Usage : ${serviceUsage} / ${rateLimit}`);
192
- console.log(`Artefact : ${file}\n`);
197
+ const link = `\u001B]8;;file://${file}\u0007Open\u001B]8;;\u0007`;
198
+
199
+ console.log(`${C.BOLD}Discovery completed${C.RESET}`);
200
+ console.log(`PID : ${C.CYAN}${participantId}${C.RESET}`);
201
+ console.log(`Artefact : ${fileName} ${C.BLUE}[${link}]${C.RESET}`);
202
+ console.log(`Usage : ${C.RED}${serviceUsage}${C.RESET} ${C.DIM}(${rateLimit})${C.RESET}\n`);
193
203
  }
194
204
 
195
205
  module.exports = { runDiscovery };
package/src/errors.js CHANGED
@@ -1,5 +1,5 @@
1
1
  const { WWW_URL } = require("./config");
2
-
2
+ const C = require("./colors");
3
3
 
4
4
  function handleError(err) {
5
5
  //console.log(err);
@@ -16,9 +16,9 @@ function handleError(err) {
16
16
  //console.log(url);
17
17
 
18
18
  if (message) {
19
- console.error(`\r${err.message}\nSee details: ${url}\n`);
19
+ console.error(`\r${C.RED}${err.message}${C.RESET}\nSee details: ${C.BOLD}${url}${C.RESET}\n`);
20
20
  } else {
21
- console.error(`\rSee details: ${url}\n`);
21
+ console.error(`\rSee details: ${C.BOLD}${url}${C.RESET}\n`);
22
22
  }
23
23
  }
24
24
 
package/src/utils.js CHANGED
@@ -2,7 +2,9 @@ const fs = require("fs");
2
2
  const inquirer = require("inquirer");
3
3
  const readline = require("readline");
4
4
  const config = require("./config");
5
+ const pkg = require("../package.json");
5
6
  const db = require("./db");
7
+ const C = require("./colors");
6
8
 
7
9
 
8
10
  function clearScreen(text) {
@@ -20,7 +22,7 @@ function ask(question, def, hidden=false) {
20
22
  terminal: true
21
23
  });
22
24
 
23
- const q = def ? `${question} (${def}): ` : `${question}: `;
25
+ const q = def ? `${question} ${C.DIM}(${def})${C.RESET}: ` : `${question}: `;
24
26
 
25
27
  process.stdin.on("data", char => {
26
28
  char = char + "";
@@ -53,7 +55,8 @@ function startSpinner(text = "Working") {
53
55
  let i = 0;
54
56
 
55
57
  const timer = setInterval(() => {
56
- process.stdout.write("\r[" + frames[i++ % frames.length] + "] " + text);
58
+ const frame = frames[i++ % frames.length];
59
+ process.stdout.write(`\r[${C.RED_BOLD}${frame}${C.RESET}] ${text}`);
57
60
  }, 120);
58
61
 
59
62
  return () => {
@@ -102,6 +105,10 @@ function getShortId(id) {
102
105
  return id.slice(0, 8);
103
106
  }
104
107
 
108
+ function capitalize(s) {
109
+ return s.charAt(0).toUpperCase() + s.slice(1);
110
+ }
111
+
105
112
  function createAppDirs(force=false) {
106
113
  fs.mkdirSync(config.NODE42_DIR, { recursive: true });
107
114
  fs.mkdirSync(config.ARTEFACTS_DIR, { recursive: true });
@@ -129,14 +136,23 @@ function cleanAppDirs(options) {
129
136
  all
130
137
  } = options;
131
138
 
139
+ if (Object.keys(options).length === 0) {
140
+ console.log(`${C.RED}Nothing to clean${C.RESET}`);
141
+ return;
142
+ }
143
+
144
+ clearScreen(`Node42 CLI v${pkg.version}\n`);
145
+
146
+ const removed = [];
147
+
132
148
  if ((all || tokens) && fs.existsSync(config.TOKENS_FILE)) {
133
149
  fs.unlinkSync(config.TOKENS_FILE);
134
- console.log("Tokens removed");
150
+ removed.push("tokens");
135
151
  }
136
152
 
137
153
  if ((all || dbFlag) && fs.existsSync(config.DATABASE_FILE)) {
138
154
  fs.unlinkSync(config.DATABASE_FILE);
139
- console.log("Database removed");
155
+ removed.push("database");
140
156
  }
141
157
 
142
158
  if (all || artefacts) {
@@ -148,8 +164,7 @@ function cleanAppDirs(options) {
148
164
  fs.rmSync(config.ARTEFACTS_DIR, { recursive: true, force: true });
149
165
  fs.mkdirSync(config.ARTEFACTS_DIR, { recursive: true });
150
166
  }
151
-
152
- console.log("Artefacts removed");
167
+ removed.push("artefacts");
153
168
  }
154
169
 
155
170
  if (all || transactions) {
@@ -161,8 +176,7 @@ function cleanAppDirs(options) {
161
176
  fs.rmSync(config.TRANSACTIONS_DIR, { recursive: true, force: true });
162
177
  fs.mkdirSync(config.TRANSACTIONS_DIR, { recursive: true });
163
178
  }
164
-
165
- console.log("Transactions removed");
179
+ removed.push("transactions");
166
180
  }
167
181
 
168
182
  if (all || validations) {
@@ -174,10 +188,19 @@ function cleanAppDirs(options) {
174
188
  fs.rmSync(config.VALIDATIONS_DIR, { recursive: true, force: true });
175
189
  fs.mkdirSync(config.VALIDATIONS_DIR, { recursive: true });
176
190
  }
191
+ removed.push("validations");
192
+ }
193
+
194
+ if (removed.length === 0) {
195
+ console.log(`${C.RED}Nothing removed${C.RESET}`);
196
+ } else {
197
+ console.log(`${C.BOLD}Removed ${removed.length} item(s)${C.RESET}`);
198
+ for (const r of removed) {
199
+ console.log(` ${C.RED}• ${r}${C.RESET}`);
200
+ }
177
201
 
178
- console.log("Transactions removed");
202
+ console.log();
179
203
  }
180
-
181
204
  }
182
205
 
183
206
  function buildDocLabel({ scheme, value }) {
@@ -229,4 +252,4 @@ function getArtefactExt(output, format) {
229
252
  }
230
253
  }
231
254
 
232
- module.exports = { clearScreen, startSpinner, ask, buildDocLabel, promptForDocument, validateEnv, validateId, getShortId, createAppDirs, cleanAppDirs, getArtefactExt };
255
+ module.exports = { clearScreen, startSpinner, ask, buildDocLabel, promptForDocument, validateEnv, validateId, getShortId, capitalize, createAppDirs, cleanAppDirs, getArtefactExt };
package/test/auth.test.js CHANGED
@@ -95,7 +95,7 @@ describe("auth", () => {
95
95
  // expected
96
96
  }
97
97
 
98
- expect(console.error.calledWithMatch("Login failed")).to.be.true;
98
+ expect(console.error.calledWithMatch("Signin failed")).to.be.true;
99
99
  expect(process.exit.calledWith(1)).to.be.true;
100
100
  expect(fs.writeFileSync.called).to.be.false;
101
101
  });