@n42/cli 0.2.42 → 0.2.72

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.
@@ -0,0 +1,36 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main, master]
6
+ pull_request:
7
+ branches: [main, master]
8
+ workflow_dispatch:
9
+
10
+ jobs:
11
+ test:
12
+ runs-on: ubuntu-latest
13
+
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+
17
+ - name: Setup Node.js
18
+ uses: actions/setup-node@v4
19
+ with:
20
+ node-version: '20'
21
+ cache: 'npm'
22
+
23
+ - name: Install dependencies
24
+ run: npm ci
25
+
26
+ - name: Run ESLint
27
+ run: npx --no-install eslint src
28
+
29
+ - name: Run tests with coverage
30
+ run: npm run test:coverage
31
+
32
+ - name: Upload coverage to Codecov
33
+ uses: codecov/codecov-action@v4
34
+ with:
35
+ token: ${{ secrets.CODECOV_TOKEN }}
36
+ fail_ci_if_error: true
@@ -0,0 +1,66 @@
1
+ name: CLI Tests on npm Installations
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ inputs:
6
+ version:
7
+ description: 'Version of @n42/cli to test (e.g. 0.4.2 or 0.2.42)'
8
+ required: true
9
+ type: string
10
+
11
+ jobs:
12
+ test:
13
+ runs-on: ubuntu-latest
14
+ strategy:
15
+ matrix:
16
+ node-version: [20.x, 22.x, 'lts/*']
17
+
18
+ steps:
19
+ - name: Checkout repository
20
+ uses: actions/checkout@v4
21
+
22
+ - name: Set up Node.js ${{ matrix.node-version }}
23
+ uses: actions/setup-node@v4
24
+ with:
25
+ node-version: ${{ matrix.node-version }}
26
+ cache: 'npm'
27
+
28
+ # ---- Global install (fresh) ----
29
+ - name: Global install fresh @n42/cli@${{ inputs.version }}
30
+ run: |
31
+ npm install -g @n42/cli@${{ inputs.version }}
32
+ echo "Installed version:"
33
+ n42 --version
34
+ # Run your validation script (assumes it exists in repo)
35
+ node test/asserts/validate_tests.js || echo "Validation failed - check logs"
36
+
37
+ # ---- Global upgrade to latest ----
38
+ - name: Global upgrade to latest @n42/cli
39
+ run: |
40
+ npm install -g @n42/cli
41
+ echo "Upgraded version:"
42
+ n42 --version
43
+ node test/asserts/validate_tests.js || echo "Validation failed after upgrade"
44
+
45
+ # ---- Local install (fresh) in isolated dir ----
46
+ - name: Local install fresh @n42/cli@${{ inputs.version }}
47
+ run: |
48
+ mkdir local-fresh && cd local-fresh
49
+ npm init -y
50
+ npm install @n42/cli@${{ inputs.version }}
51
+ echo "Local fresh version:"
52
+ npx n42 --version
53
+ node ../test/asserts/validate_tests.js || echo "Local validation failed"
54
+ cd ..
55
+
56
+ # ---- Local upgrade to latest ----
57
+ - name: Local upgrade to latest @n42/cli
58
+ run: |
59
+ mkdir local-upgrade && cd local-upgrade
60
+ npm init -y
61
+ npm install @n42/cli@${{ inputs.version }}
62
+ npm install @n42/cli
63
+ echo "Local upgraded version:"
64
+ npx n42 --version
65
+ node ../test/asserts/validate_tests.js || echo "Local upgrade validation failed"
66
+ cd ..
package/README.md CHANGED
@@ -1,18 +1,23 @@
1
+ [![codecov](https://codecov.io/gh/node42-dev/node42-cli/graph/badge.svg)](https://codecov.io/gh/node42-dev/node42-cli)
2
+ [![CI](https://github.com/node42-dev/node42-cli/actions/workflows/ci.yaml/badge.svg)](https://github.com/node42-dev/node42-cli/actions/workflows/ci.yaml)
3
+ [![npm](https://img.shields.io/npm/v/@n42/cli.svg)](https://www.npmjs.com/package/@n42/cli)
4
+ [![Swagger](https://img.shields.io/badge/Swagger-Discovery%20API-green)](https://node42.dev/docs/discovery)
5
+
1
6
  # Node42 CLI
2
7
 
3
- Command-line interface for **eDelivery discovery, diagnostics, and
4
- validation**, with support for the Peppol network.
8
+ Command-line frontend for Node42 **eDelivery path discovery**, diagnostics and validation with support for the Peppol network.
9
+
10
+ The Node42 CLI is a **scriptable command-line client** to the **Node42 WebUI and API**,
11
+ built for **system integrators**, **service providers**, and **operators** who need fast,
12
+ repeatable insight into eDelivery routing, SML/SMK, SMP resolution, and Access Point behavior.
5
13
 
6
- The Node42 CLI is designed for **system integrators, service providers,
7
- and operators** who need fast, repeatable insight into eDelivery
8
- routing, SML/SMK, SMP resolution, and Access Point behavior.
14
+ It exposes the **same capabilities as the Node42 WebUI** but optimized for automation
15
+ and local analysis.
9
16
 
10
17
  While Node42's toolset **includes** modules capable of constructing and **sending
11
18
  standards-compliant messages**, it is **intended for diagnostics**, validation,
12
19
  and testing — not for production message exchange.
13
20
 
14
-
15
-
16
21
  ## Features
17
22
 
18
23
  - Peppol eDelivery path discovery
package/package.json CHANGED
@@ -1,10 +1,14 @@
1
1
  {
2
2
  "name": "@n42/cli",
3
- "version": "0.2.42",
3
+ "version": "0.2.72",
4
4
  "description": "Node42 CLI – Command-line interface for Peppol eDelivery path discovery, diagnostics, and tooling",
5
5
  "keywords": [
6
- "node42", "peppol", "edelivery", "diagnostics",
7
- "cli", "api"
6
+ "node42",
7
+ "peppol",
8
+ "edelivery",
9
+ "diagnostics",
10
+ "cli",
11
+ "api"
8
12
  ],
9
13
  "homepage": "https://github.com/node42-dev/node42-cli",
10
14
  "repository": {
@@ -19,7 +23,22 @@
19
23
  },
20
24
  "scripts": {
21
25
  "lint": "eslint src",
22
- "test": "mocha"
26
+ "test": "mocha",
27
+ "test:coverage": "c8 --reporter=text-summary --reporter=lcov mocha",
28
+ "test:watch": "mocha --watch",
29
+ "build": "esbuild src/cli.js --bundle --platform=node --target=node20 --outfile=dist/n42 --format=cjs --minify --banner:js='#!/usr/bin/env node'"
30
+ },
31
+ "c8": {
32
+ "exclude": [
33
+ "src/assets/**",
34
+ "src/completion/**",
35
+ "src/config.js",
36
+ "src/colors.js"
37
+ ],
38
+ "check-coverage": true,
39
+ "lines": 70,
40
+ "functions": 70,
41
+ "branches": 70
23
42
  },
24
43
  "engines": {
25
44
  "node": ">=18"
@@ -27,10 +46,14 @@
27
46
  "dependencies": {
28
47
  "commander": "^11.1.0",
29
48
  "inquirer": "^8.2.7",
30
- "open": "^11.0.0"
49
+ "open": "^11.0.0",
50
+ "xmldom": "^0.6.0",
51
+ "xpath": "^0.0.34"
31
52
  },
32
53
  "devDependencies": {
54
+ "c8": "^10.1.3",
33
55
  "chai": "^4.5.0",
56
+ "esbuild": "^0.27.2",
34
57
  "eslint": "^9.39.2",
35
58
  "globals": "^17.2.0",
36
59
  "mocha": "^11.3.0",
@@ -28,6 +28,6 @@
28
28
  </div>
29
29
  </div>
30
30
  </div>
31
- <script src="../../assets/wrapper.js" defer></script>
31
+ <script src="../../assets/discover.js" defer></script>
32
32
  </body>
33
33
  </html>
@@ -0,0 +1,49 @@
1
+ #xmldata {
2
+ background: #fff;
3
+ padding: 6px 12px 6px 10px;
4
+ color: #9ca3af;
5
+ font-family: monospace;
6
+ white-space: pre-wrap;
7
+ overflow-x: auto;
8
+ cursor: default;
9
+ }
10
+
11
+ .xml-info0 {
12
+ background: #e5e7eb;
13
+ color: #374151;
14
+ border-left: 4px solid #9ca3af;
15
+ padding-left: 2px;
16
+ cursor: pointer;
17
+ }
18
+
19
+ .xml-info1 {
20
+ background: #e0f2fe;
21
+ color: #111827;
22
+ border-left: 4px solid #38bdf8;
23
+ padding-left: 2px;
24
+ cursor: pointer;
25
+ }
26
+
27
+ .xml-info2 {
28
+ background: #f3e8ff;
29
+ color: #111827;
30
+ border-left: 4px solid #a855f7;
31
+ padding-left: 2px;
32
+ cursor: pointer;
33
+ }
34
+
35
+ .xml-warning {
36
+ background: #fef3c7;
37
+ color: #111827;
38
+ border-left: 4px solid #f59e0b;
39
+ padding-left: 2px;
40
+ cursor: pointer;
41
+ }
42
+
43
+ .xml-error {
44
+ background: #fee2e2;
45
+ color: #111827;
46
+ border-left: 4px solid #dc2626;
47
+ padding-left: 2px;
48
+ cursor: pointer;
49
+ }
@@ -0,0 +1,27 @@
1
+ <!doctype html>
2
+ <html>
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <title>Node42</title>
6
+ <link rel="stylesheet" href="../../assets/wrapper-light.css">
7
+ <link rel="stylesheet" href="../../assets/validator-light.css">
8
+ </head>
9
+ <body>
10
+ <div id="app-header">
11
+ <div class="header-left">
12
+ <img id="appBtn" src="../../assets/node42-logo.svg" alt="Node42" onclick="window.open('https://www.node42.dev', '_blank');">
13
+ </div>
14
+ <div class="header-right"></div>
15
+ </div>
16
+ <div id="page">
17
+ <div id="app-shell">
18
+ <div id="timeline"></div>
19
+ <div id="bubble" class="bubble" data-uuid="/--UUID--/">
20
+ <div id="xmldata"><!-- XML --></div>
21
+ <div class="bubble-time"><!-- TIME --></div>
22
+ </div>
23
+ </div>
24
+ </div>
25
+ </div>
26
+ </body>
27
+ </html>
@@ -95,6 +95,13 @@ body {
95
95
  display: block;
96
96
  }
97
97
 
98
+ .bubble-title {
99
+ font-size: 17px;
100
+ font-weight: 600;
101
+ margin-bottom: 2px;
102
+ text-align: center;
103
+ }
104
+
98
105
  .bubble-time {
99
106
  position: absolute;
100
107
  bottom: -24px;
package/src/cli.js CHANGED
@@ -4,6 +4,7 @@ const { Command } = require("commander");
4
4
  const { login, logout, checkAuth, setApiKey, getApiKey, removeApiKey } = require("./auth");
5
5
  const { getUserWithIndex, getUserUsage } = require("./user");
6
6
  const { runDiscovery } = require("./discover");
7
+ const { runValidation } = require("./validator");
7
8
  const { startSpinner, validateEnv, validateId, createAppDirs, capitalize, cleanAppDirs } = require("./utils");
8
9
  const { NODE42_DIR, ARTEFACTS_DIR, DEFAULT_OUTPUT, DEFAULT_FORMAT } = require("./config");
9
10
 
@@ -40,6 +41,16 @@ program
40
41
  console.log(`Run: ${C.BOLD}source ${dest}${C.RESET}\n`);
41
42
  });
42
43
 
44
+ program
45
+ .command("login")
46
+ .description("Authenticate using username and password and store tokens locally")
47
+ .action(login);
48
+
49
+ program
50
+ .command("logout")
51
+ .description("Terminate user session and delete all local tokens")
52
+ .action(logout);
53
+
43
54
  program
44
55
  .command("apikey")
45
56
  .description("Manage API key authentication")
@@ -76,29 +87,6 @@ program
76
87
  console.log(apiKey ? `${C.RED}API key configured${C.RESET}\n` : `${C.RED}No API key configured${C.RESET}\n`);
77
88
  });
78
89
 
79
- program
80
- .command("login")
81
- .description("Authenticate using username and password and store tokens locally")
82
- .action(login);
83
-
84
- program
85
- .command("logout")
86
- .description("Terminate user session and delete all local tokens")
87
- .action(logout);
88
-
89
- program
90
- .command("clean")
91
- .description("Remove locally stored artefacts and cache")
92
- .option("--tokens", "Remove stored authentication tokens")
93
- .option("--artefacts", "Remove artefacts")
94
- .option("--transactions", "Remove transactions")
95
- .option("--validations", "Remove validations")
96
- .option("--db", "Remove local database")
97
- .option("--all", "Wipe all local data")
98
- .action((options)=> {
99
- cleanAppDirs(options);
100
- });
101
-
102
90
  program
103
91
  .command("me")
104
92
  .description("Returns identity and service usage for the authenticated user.")
@@ -148,6 +136,19 @@ program
148
136
  console.log(` • ${currentMonth}: ${C.RED}${usage}${C.RESET}\n`);
149
137
  });
150
138
 
139
+ program
140
+ .command("clean")
141
+ .description("Remove locally stored artefacts and cache")
142
+ .option("--tokens", "Remove stored authentication tokens")
143
+ .option("--artefacts", "Remove artefacts")
144
+ .option("--transactions", "Remove transactions")
145
+ .option("--validations", "Remove validations")
146
+ .option("--db", "Remove local database")
147
+ .option("--all", "Wipe all local data")
148
+ .action((options)=> {
149
+ cleanAppDirs(options);
150
+ });
151
+
151
152
  program
152
153
  .command("history [participantId]")
153
154
  .description("Show local history with filtering")
@@ -260,4 +261,26 @@ discover
260
261
  runDiscovery(participantId, options);
261
262
  });
262
263
 
264
+ const validate = program
265
+ .command("validate")
266
+ .description("Run document validation using configurable rulesets");
267
+
268
+ validate
269
+ .command("peppol <document>")
270
+ .description("Validate a document against Peppol validation rulesets")
271
+ .option("-r, --ruleset <ruleset>", "Validation ruleset to use (latest | current | legacy)", "current")
272
+ .option("--location", "Include XPath location for each validation assertion", true)
273
+ .option("--runtime", "Include execution time in the validation output", false)
274
+ .action((document, options) => {
275
+ if (!fs.existsSync(document)) {
276
+ console.error("Couldn't find a valid document at the selected path");
277
+ process.exit(1);
278
+ }
279
+
280
+ const xmlDoc = fs.readFileSync(document, "utf8");
281
+ const docName = path.basename(document);
282
+
283
+ runValidation(docName, xmlDoc, options);
284
+ });
285
+
263
286
  program.parse(process.argv);
@@ -3,7 +3,7 @@ _n42_completions()
3
3
  local cur prev words cword
4
4
  _init_completion || return
5
5
 
6
- local commands="login logout me usage history discover clean"
6
+ local commands="login logout apikey me usage clean history discover validate"
7
7
 
8
8
  if [[ $cword -eq 1 ]]; then
9
9
  COMPREPLY=( $(compgen -W "$commands" -- "$cur") )
@@ -16,6 +16,12 @@ _n42_completions()
16
16
  return
17
17
  fi
18
18
 
19
+ # validate peppol
20
+ if [[ ${words[1]} == "validate" && $cword -eq 2 ]]; then
21
+ COMPREPLY=( $(compgen -W "peppol" -- "$cur") )
22
+ return
23
+ fi
24
+
19
25
  # usage discovery|validation|transactions
20
26
  if [[ ${words[1]} == "usage" && $cword -eq 2 ]]; then
21
27
  COMPREPLY=( $(compgen -W "discovery validation transactions" -- "$cur") )
package/src/config.js CHANGED
@@ -6,6 +6,8 @@ const config = {
6
6
  API_URL: "https://api.node42.dev",
7
7
  WWW_URL: "https://www.node42.dev",
8
8
 
9
+ VALIDATOR_URL: "https://validator.node42.dev",
10
+
9
11
  API_TIMEOUT_MS: 30000,
10
12
 
11
13
  NODE42_DIR: path.join(os.homedir(), ".node42"),
@@ -24,7 +26,8 @@ const config = {
24
26
  EP_SIGNIN: "auth/signin",
25
27
  EP_REFRESH: "auth/refresh",
26
28
  EP_ME: "users/me",
27
- EP_DISCOVER: "discover/peppol"
29
+ EP_DISCOVER: "discover/peppol",
30
+ EP_VALIDATE: "validate"
28
31
  };
29
32
 
30
33
  config.ARTEFACTS_DIR = path.join(config.NODE42_DIR, "artefacts", "discovery");
package/src/discover.js CHANGED
@@ -43,7 +43,7 @@ function wrapSvg(fileId, refId, svg) {
43
43
  minute: "2-digit"
44
44
  });
45
45
 
46
- const templateFile = path.join(NODE42_DIR, "assets/wrapper.html.template");
46
+ const templateFile = path.join(NODE42_DIR, "assets/discover.html.template");
47
47
  const template = fs.readFileSync(templateFile, "utf8");
48
48
 
49
49
  html = template.replace("<!-- SVG -->", svg);
package/src/utils.js CHANGED
@@ -10,7 +10,7 @@ const pkg = require("../package.json");
10
10
  const db = require("./db");
11
11
  const C = require("./colors");
12
12
 
13
-
13
+ /* c8 ignore next */
14
14
  function writeHeader(text, clearScreen=false) {
15
15
  if (clearScreen) {
16
16
  process.stdout.write("\x1Bc");
@@ -22,6 +22,7 @@ function writeHeader(text, clearScreen=false) {
22
22
  }
23
23
  }
24
24
 
25
+ /* c8 ignore next */
25
26
  function ask(question, def, hidden=false) {
26
27
  return new Promise(resolve => {
27
28
  const rl = readline.createInterface({
@@ -58,6 +59,7 @@ function ask(question, def, hidden=false) {
58
59
  });
59
60
  }
60
61
 
62
+ /* c8 ignore next */
61
63
  function startSpinner(text = "Working") {
62
64
  const frames = ["-", "\\", "|", "/"];
63
65
  let i = 0;
@@ -73,6 +75,7 @@ function startSpinner(text = "Working") {
73
75
  };
74
76
  }
75
77
 
78
+ /* c8 ignore next */
76
79
  async function promptForDocument(docs) {
77
80
  const { document } = await inquirer.prompt([
78
81
  {
@@ -109,10 +112,12 @@ function validateId(type, id) {
109
112
  }
110
113
  }
111
114
 
115
+ /* c8 ignore next */
112
116
  function getShortId(id) {
113
117
  return id.slice(0, 8);
114
118
  }
115
119
 
120
+ /* c8 ignore next */
116
121
  function capitalize(s) {
117
122
  return s.charAt(0).toUpperCase() + s.slice(1);
118
123
  }