javascript-solid-server 0.0.159 → 0.0.160
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/.claude/settings.local.json +4 -1
- package/bin/jss.js +43 -0
- package/package.json +1 -1
- package/test/cli-flag-like-values.test.js +88 -0
|
@@ -358,7 +358,10 @@
|
|
|
358
358
|
"Bash(rm LICENSE.full)",
|
|
359
359
|
"Bash(awk -F: '{print $1,$2}')",
|
|
360
360
|
"Bash(awk -F: '{print $1}')",
|
|
361
|
-
"WebFetch(domain:www.gitfork.app)"
|
|
361
|
+
"WebFetch(domain:www.gitfork.app)",
|
|
362
|
+
"Bash(JSS_SINGLE_USER_PASSWORD=hunter2 timeout 3 node bin/jss.js start --port 4581 --root /tmp/jss-103 --single-user --single-user-name alice --idp)",
|
|
363
|
+
"Bash(JSS_SINGLE_USER_PASSWORD=hunter2 timeout 2 node bin/jss.js start --port 4581 --root /tmp/jss-103 --single-user-name alice --idp)",
|
|
364
|
+
"Bash(JSS_SINGLE_USER_PASSWORD=hunter2 timeout 3 node bin/jss.js start --port 4581 --root /tmp/jss-103-sanity --single-user-name alice --single-user --idp)"
|
|
362
365
|
]
|
|
363
366
|
}
|
|
364
367
|
}
|
package/bin/jss.js
CHANGED
|
@@ -32,6 +32,49 @@ program
|
|
|
32
32
|
.description('JavaScript Solid Server - A minimal, fast, JSON-LD native Solid server')
|
|
33
33
|
.version(pkg.version);
|
|
34
34
|
|
|
35
|
+
/**
|
|
36
|
+
* Convert a camelCase option name back to its kebab-case CLI form for
|
|
37
|
+
* error messages (`singleUserName` → `single-user-name`).
|
|
38
|
+
*/
|
|
39
|
+
function camelToKebab (name) {
|
|
40
|
+
return name.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Reject any option value that looks like another flag (#103).
|
|
45
|
+
*
|
|
46
|
+
* Commander happily consumes the next argv as a value, so
|
|
47
|
+
* `jss start --single-user-name --idp`
|
|
48
|
+
* silently sets `singleUserName="--idp"` and the IdP flag is lost.
|
|
49
|
+
* This validator runs as a `preAction` hook for every subcommand, so
|
|
50
|
+
* any option with a missing value gets a clear error instead of a
|
|
51
|
+
* confusing downstream failure ("issuer has no registration endpoint",
|
|
52
|
+
* "Single-user: --idp" in the banner, etc.).
|
|
53
|
+
*/
|
|
54
|
+
program.hook('preAction', (_thisCommand, actionCommand) => {
|
|
55
|
+
const opts = actionCommand.opts();
|
|
56
|
+
for (const [key, value] of Object.entries(opts)) {
|
|
57
|
+
const flag = camelToKebab(key);
|
|
58
|
+
if (typeof value === 'string' && value.startsWith('--')) {
|
|
59
|
+
console.error(
|
|
60
|
+
`Error: --${flag} value "${value}" looks like a flag, not a value.\n` +
|
|
61
|
+
`Hint: did you forget to provide a value? e.g. --${flag} someValue`
|
|
62
|
+
);
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
// Numeric options (parseInt-coerced like --port) silently produce
|
|
66
|
+
// NaN when given a flag like `--idp`. Catch that too — same root
|
|
67
|
+
// cause, different surface.
|
|
68
|
+
if (typeof value === 'number' && Number.isNaN(value)) {
|
|
69
|
+
console.error(
|
|
70
|
+
`Error: --${flag} got a non-numeric value (parsed as NaN).\n` +
|
|
71
|
+
`Hint: did you forget to provide a number? e.g. --${flag} 8080`
|
|
72
|
+
);
|
|
73
|
+
process.exit(1);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
|
|
35
78
|
/**
|
|
36
79
|
* Start command
|
|
37
80
|
*/
|
package/package.json
CHANGED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Regression tests for #103 — `bin/jss.js` must reject option values
|
|
3
|
+
* that look like flags (e.g. `--single-user-name --idp`) instead of
|
|
4
|
+
* silently using `--idp` as the username and breaking IdP setup.
|
|
5
|
+
*
|
|
6
|
+
* These spawn the CLI as a subprocess and assert exit-code + stderr.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { describe, it } from 'node:test';
|
|
10
|
+
import assert from 'node:assert';
|
|
11
|
+
import { spawnSync } from 'node:child_process';
|
|
12
|
+
import os from 'node:os';
|
|
13
|
+
import path from 'node:path';
|
|
14
|
+
import { fileURLToPath } from 'node:url';
|
|
15
|
+
|
|
16
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
17
|
+
const BIN = path.join(__dirname, '..', 'bin', 'jss.js');
|
|
18
|
+
|
|
19
|
+
// Timeout cap so the suite can never hang if the validator regresses
|
|
20
|
+
// and `start` actually tries to bind a port instead of exiting early.
|
|
21
|
+
const RUN_TIMEOUT_MS = 10_000;
|
|
22
|
+
|
|
23
|
+
function runCli(args) {
|
|
24
|
+
const r = spawnSync(process.execPath, [BIN, ...args], {
|
|
25
|
+
encoding: 'utf8',
|
|
26
|
+
timeout: RUN_TIMEOUT_MS,
|
|
27
|
+
killSignal: 'SIGKILL'
|
|
28
|
+
});
|
|
29
|
+
// spawnSync sets `signal` to the kill signal when timeout fires. Treat
|
|
30
|
+
// that as a hard test failure rather than letting downstream
|
|
31
|
+
// assertions on stderr accidentally pass.
|
|
32
|
+
assert.strictEqual(
|
|
33
|
+
r.signal, null,
|
|
34
|
+
`CLI did not exit within ${RUN_TIMEOUT_MS}ms — likely the preAction ` +
|
|
35
|
+
`validator regressed and \`start\` is actually trying to listen. ` +
|
|
36
|
+
`args: ${JSON.stringify(args)}; partial stderr: ${r.stderr}`
|
|
37
|
+
);
|
|
38
|
+
return r;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
describe('bin/jss.js — flag-like option values (#103)', () => {
|
|
42
|
+
it('rejects `--single-user-name --idp` with a clear error', () => {
|
|
43
|
+
const r = runCli(['start', '--single-user-name', '--idp']);
|
|
44
|
+
assert.notStrictEqual(r.status, 0, 'exit code should be non-zero');
|
|
45
|
+
assert.match(r.stderr, /--single-user-name value "--idp" looks like a flag/);
|
|
46
|
+
assert.match(r.stderr, /Hint: did you forget to provide a value\?/);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('rejects another option swallowing a flag (covers --idp-issuer too)', () => {
|
|
50
|
+
// Commander's behaviour: it greedily consumes the next argv as the
|
|
51
|
+
// value, which is the whole reason the bug exists. We use a flag
|
|
52
|
+
// commander doesn't know about so commander doesn't reroute it
|
|
53
|
+
// through its own argument-count error. The dummy name is
|
|
54
|
+
// collision-proof — if anyone ever adds a real `--bogus-...` flag
|
|
55
|
+
// matching this pattern, the duplication is the bigger problem.
|
|
56
|
+
const FAKE_FLAG = '--__jss103_unlikely_cli_option__';
|
|
57
|
+
const r = runCli(['start', '--idp-issuer', FAKE_FLAG]);
|
|
58
|
+
assert.notStrictEqual(r.status, 0);
|
|
59
|
+
assert.match(
|
|
60
|
+
r.stderr,
|
|
61
|
+
new RegExp(`--idp-issuer value "${FAKE_FLAG}" looks like a flag`)
|
|
62
|
+
);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('rejects `--port --idp` (numeric option → NaN) with helpful error', () => {
|
|
66
|
+
const r = runCli(['start', '--port', '--idp']);
|
|
67
|
+
assert.notStrictEqual(r.status, 0);
|
|
68
|
+
assert.match(r.stderr, /--port got a non-numeric value/);
|
|
69
|
+
assert.match(r.stderr, /Hint: did you forget to provide a number\?/);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('accepts a real value and reaches normal config processing', () => {
|
|
73
|
+
// --print-config exits 0 cleanly after dumping config; this proves
|
|
74
|
+
// the validator doesn't false-positive on legitimate values.
|
|
75
|
+
// Use os.tmpdir() rather than a hard-coded /tmp/... so the test is
|
|
76
|
+
// portable across platforms (and matches the rest of the suite).
|
|
77
|
+
const tmpRoot = path.join(os.tmpdir(), 'jss-103-sanity-doesnotneedtoexist');
|
|
78
|
+
const r = runCli(['start',
|
|
79
|
+
'--port', '4582',
|
|
80
|
+
'--root', tmpRoot,
|
|
81
|
+
'--single-user-name', 'alice',
|
|
82
|
+
'--print-config'
|
|
83
|
+
]);
|
|
84
|
+
assert.strictEqual(r.status, 0,
|
|
85
|
+
`expected clean exit, got ${r.status}; stderr: ${r.stderr}`);
|
|
86
|
+
assert.match(r.stdout, /Configuration:/);
|
|
87
|
+
});
|
|
88
|
+
});
|