@n42/cli 0.2.32 → 0.2.71
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/.github/workflows/ci.yaml +36 -0
- package/.github/workflows/cli-test-npm.yaml +66 -0
- package/README.md +30 -10
- package/package.json +28 -5
- package/src/assets/{wrapper.html.template → discover.html.template} +1 -1
- package/src/assets/validator-light.css +49 -0
- package/src/assets/validator.html.template +27 -0
- package/src/assets/wrapper-light.css +7 -0
- package/src/auth.js +60 -5
- package/src/cli.js +71 -13
- package/src/completion/bash.sh +7 -1
- package/src/config.js +4 -1
- package/src/discover.js +1 -1
- package/src/errors.js +4 -2
- package/src/utils.js +6 -1
- package/src/validator.js +286 -0
- package/test/asserts/validate_tests.js +28 -0
- package/test/cli.test.js +83 -0
- package/test/discover.test.js +112 -0
- package/test/errors.test.js +61 -0
- package/test/user.test.js +103 -0
- package/test/utils.test.js +105 -7
- package/test/validator.test.js +66 -0
- package/jest.config.js +0 -6
- package/src/browser.js +0 -11
- /package/src/assets/{wrapper.js → discover.js} +0 -0
|
@@ -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,13 +1,22 @@
|
|
|
1
|
-
|
|
1
|
+
[](https://codecov.io/gh/node42-dev/node42-cli)
|
|
2
|
+
[](https://github.com/node42-dev/node42-cli/actions/workflows/ci.yaml)
|
|
3
|
+
[](https://www.npmjs.com/package/@n42/cli)
|
|
4
|
+
[](https://node42.dev/docs/discovery)
|
|
2
5
|
|
|
3
|
-
|
|
4
|
-
validation**, with support for the Peppol network.
|
|
6
|
+
# Node42 CLI
|
|
5
7
|
|
|
6
|
-
|
|
7
|
-
and operators** who need fast, repeatable insight into eDelivery
|
|
8
|
-
routing, SML/SMK, SMP resolution, and Access Point behavior.
|
|
8
|
+
Command-line frontend for Node42 **eDelivery path discovery**, diagnostics and validation with support for the Peppol network.
|
|
9
9
|
|
|
10
|
-
|
|
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.
|
|
13
|
+
|
|
14
|
+
It exposes the **same capabilities as the Node42 WebUI** but optimized for automation
|
|
15
|
+
and local analysis.
|
|
16
|
+
|
|
17
|
+
While Node42's toolset **includes** modules capable of constructing and **sending
|
|
18
|
+
standards-compliant messages**, it is **intended for diagnostics**, validation,
|
|
19
|
+
and testing — not for production message exchange.
|
|
11
20
|
|
|
12
21
|
## Features
|
|
13
22
|
|
|
@@ -128,9 +137,15 @@ Artefacts are stored under:
|
|
|
128
137
|
|
|
129
138
|
## Error Handling
|
|
130
139
|
|
|
131
|
-
Errors are printed with a clickable reference link
|
|
140
|
+
Errors are printed with a clickable reference link.
|
|
132
141
|
|
|
133
|
-
|
|
142
|
+
Example output:
|
|
143
|
+
|
|
144
|
+
``` bash
|
|
145
|
+
Error: 9031 [View details]
|
|
146
|
+
|
|
147
|
+
Invalid token: the authorization token provided is invalid
|
|
148
|
+
```
|
|
134
149
|
|
|
135
150
|
## Security
|
|
136
151
|
|
|
@@ -139,4 +154,9 @@ Errors are printed with a clickable reference link:
|
|
|
139
154
|
|
|
140
155
|
## License
|
|
141
156
|
|
|
142
|
-
MIT License
|
|
157
|
+
MIT License
|
|
158
|
+
|
|
159
|
+
## Author
|
|
160
|
+
|
|
161
|
+
Alex Olsson \
|
|
162
|
+
Node42
|
package/package.json
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@n42/cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.71",
|
|
4
4
|
"description": "Node42 CLI – Command-line interface for Peppol eDelivery path discovery, diagnostics, and tooling",
|
|
5
5
|
"keywords": [
|
|
6
|
-
"node42",
|
|
7
|
-
"
|
|
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",
|
|
@@ -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>
|
package/src/auth.js
CHANGED
|
@@ -7,11 +7,54 @@ const { ask, startSpinner } = require("./utils");
|
|
|
7
7
|
const db = require("./db");
|
|
8
8
|
const C = require("./colors");
|
|
9
9
|
|
|
10
|
+
function setApiKey(userId, key) {
|
|
11
|
+
if (!key) return;
|
|
12
|
+
|
|
13
|
+
const database = db.load();
|
|
14
|
+
|
|
15
|
+
const u = database.user.find(x => x.id === userId);
|
|
16
|
+
if (!u) return;
|
|
17
|
+
|
|
18
|
+
u.apiKey = {
|
|
19
|
+
"value": key,
|
|
20
|
+
"createdAt": Date.now()
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
db.save(database);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function getApiKey(userId) {
|
|
27
|
+
const database = db.load();
|
|
28
|
+
|
|
29
|
+
const u = database.user.find(x => x.id === userId);
|
|
30
|
+
if (!u || !u.apiKey) return null;
|
|
31
|
+
|
|
32
|
+
return u.apiKey.value;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function removeApiKey(userId) {
|
|
36
|
+
const database = db.load();
|
|
37
|
+
|
|
38
|
+
const u = database.user.find(x => x.id === userId);
|
|
39
|
+
if (!u || !u.apiKey) return false;
|
|
40
|
+
|
|
41
|
+
delete u.apiKey;
|
|
42
|
+
db.save(database);
|
|
43
|
+
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
10
46
|
|
|
11
47
|
async function login() {
|
|
12
48
|
console.log(`${C.BOLD}Sign in to your account${C.RESET}`);
|
|
13
49
|
let user = getUserWithIndex(0);
|
|
14
50
|
|
|
51
|
+
const apiKey = getApiKey(user.id);
|
|
52
|
+
if (apiKey) {
|
|
53
|
+
console.log(`\n${C.RED}API key authentication is configured.${C.RESET}`);
|
|
54
|
+
console.log(`Login is not required.\n`);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
15
58
|
const username = await ask("Username", user.userMail ?? "");
|
|
16
59
|
const password = await ask("Password", null, true);
|
|
17
60
|
console.log();
|
|
@@ -158,21 +201,33 @@ async function refreshSession() {
|
|
|
158
201
|
}
|
|
159
202
|
|
|
160
203
|
async function fetchWithAuth(url, options = {}) {
|
|
204
|
+
const user = getUserWithIndex(0);
|
|
205
|
+
const apiKey = user ? getApiKey(user.id) : null;
|
|
206
|
+
|
|
161
207
|
let { accessToken } = loadTokens();
|
|
162
|
-
|
|
208
|
+
|
|
209
|
+
if (!accessToken && !apiKey) {
|
|
163
210
|
handleError({ code: "N42E-9032" });
|
|
164
|
-
return;
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (apiKey) {
|
|
215
|
+
console.log(`${C.DIM}Authenticating with API key.${C.RESET}\n`);
|
|
165
216
|
}
|
|
166
217
|
|
|
167
218
|
const res = await fetch(url, {
|
|
168
219
|
...options,
|
|
169
220
|
headers: {
|
|
170
221
|
...(options.headers || {}),
|
|
171
|
-
...(accessToken
|
|
222
|
+
...(accessToken
|
|
223
|
+
? { Authorization: `Bearer ${accessToken}` }
|
|
224
|
+
: apiKey
|
|
225
|
+
? { "X-Api-Key": apiKey }
|
|
226
|
+
: {})
|
|
172
227
|
}
|
|
173
228
|
});
|
|
174
229
|
|
|
175
|
-
if (res.status !== 401) {
|
|
230
|
+
if (apiKey || res.status !== 401) {
|
|
176
231
|
return res;
|
|
177
232
|
}
|
|
178
233
|
|
|
@@ -191,4 +246,4 @@ async function fetchWithAuth(url, options = {}) {
|
|
|
191
246
|
});
|
|
192
247
|
}
|
|
193
248
|
|
|
194
|
-
module.exports = { login, logout, loadTokens, checkAuth, fetchWithAuth };
|
|
249
|
+
module.exports = { setApiKey, getApiKey, removeApiKey, login, logout, loadTokens, checkAuth, fetchWithAuth };
|
package/src/cli.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
const { Command } = require("commander");
|
|
4
|
-
const { login, logout, checkAuth } = require("./auth");
|
|
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
|
|
|
@@ -51,16 +52,39 @@ program
|
|
|
51
52
|
.action(logout);
|
|
52
53
|
|
|
53
54
|
program
|
|
54
|
-
.command("
|
|
55
|
-
.description("
|
|
56
|
-
.option("--
|
|
57
|
-
.option("--
|
|
58
|
-
.
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
55
|
+
.command("apikey")
|
|
56
|
+
.description("Manage API key authentication")
|
|
57
|
+
.option("--set <key>", "Authenticate using an API key")
|
|
58
|
+
.option("--remove", "Remove stored API key")
|
|
59
|
+
.action((options) => {
|
|
60
|
+
const user = getUserWithIndex(0);
|
|
61
|
+
if (!user) {
|
|
62
|
+
console.error(`${C.RED}No local user context found${C.RESET}`);
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
console.log(`${C.BOLD}Node42 Account${C.RESET} (${user.userMail})\n`);
|
|
67
|
+
|
|
68
|
+
if (options.set) {
|
|
69
|
+
setApiKey(user.id, options.set);
|
|
70
|
+
|
|
71
|
+
if (getApiKey(user.id) === options.set) {
|
|
72
|
+
console.log(`${C.GREEN}API key authentication configured${C.RESET}\n`);
|
|
73
|
+
} else {
|
|
74
|
+
console.log(`${C.RED}API key configuration failed${C.RESET}\n`);
|
|
75
|
+
}
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (options.remove) {
|
|
80
|
+
const removed = removeApiKey(user.id);
|
|
81
|
+
console.log(removed ? `${C.RED}API key removed${C.RESET}\n` : `${C.RED}No API key configured${C.RESET}\n`);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// default: show status
|
|
86
|
+
const apiKey = getApiKey(user.id);
|
|
87
|
+
console.log(apiKey ? `${C.RED}API key configured${C.RESET}\n` : `${C.RED}No API key configured${C.RESET}\n`);
|
|
64
88
|
});
|
|
65
89
|
|
|
66
90
|
program
|
|
@@ -78,9 +102,8 @@ program
|
|
|
78
102
|
|
|
79
103
|
const user = getUserWithIndex(0);
|
|
80
104
|
const currentMonth = new Date().toISOString().slice(0, 7);
|
|
81
|
-
console.log(`Node42 Account
|
|
105
|
+
console.log(`Node42 Account: ${C.BOLD}${user.id}${C.RESET}
|
|
82
106
|
${C.BOLD}User${C.RESET}
|
|
83
|
-
ID : ${C.CYAN}${user.id}${C.RESET}
|
|
84
107
|
Name : ${user.userName}
|
|
85
108
|
Email : ${user.userMail}
|
|
86
109
|
Role : ${user.role}
|
|
@@ -113,6 +136,19 @@ program
|
|
|
113
136
|
console.log(` • ${currentMonth}: ${C.RED}${usage}${C.RESET}\n`);
|
|
114
137
|
});
|
|
115
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
|
+
|
|
116
152
|
program
|
|
117
153
|
.command("history [participantId]")
|
|
118
154
|
.description("Show local history with filtering")
|
|
@@ -225,4 +261,26 @@ discover
|
|
|
225
261
|
runDiscovery(participantId, options);
|
|
226
262
|
});
|
|
227
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
|
+
|
|
228
286
|
program.parse(process.argv);
|
package/src/completion/bash.sh
CHANGED
|
@@ -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
|
|
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/
|
|
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/errors.js
CHANGED
|
@@ -15,10 +15,12 @@ function handleError(err) {
|
|
|
15
15
|
: `${WWW_URL}/errors`;
|
|
16
16
|
//console.log(url);
|
|
17
17
|
|
|
18
|
+
const link = `\u001B]8;;${url}\u0007View details\u001B]8;;\u0007`;
|
|
19
|
+
|
|
18
20
|
if (message) {
|
|
19
|
-
console.error(`\r${C.
|
|
21
|
+
console.error(`\r${C.BOLD}Error: ${code}${C.RESET} ${C.BLUE}[${link}]${C.RESET}\n\n${C.RED}${err.message}${C.RESET}\n`);
|
|
20
22
|
} else {
|
|
21
|
-
console.error(`\
|
|
23
|
+
console.error(`\r${C.BOLD}Error: ${code}${C.RESET} ${C.BLUE}[${url}]${C.RESET}\n\nFor details, see the documentation.\n`);
|
|
22
24
|
}
|
|
23
25
|
}
|
|
24
26
|
|
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
|
}
|