@link-assistant/web-capture 1.7.2 → 1.7.3

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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @link-assistant/web-capture
2
2
 
3
+ ## 1.7.3
4
+
5
+ ### Patch Changes
6
+
7
+ - 237afeb: Fix the JavaScript CLI so strict argument parsing accepts positional capture URLs while still rejecting unknown options.
8
+
3
9
  ## 1.7.2
4
10
 
5
11
  ### Patch Changes
@@ -16,6 +16,12 @@ const config = makeConfig({
16
16
  .usage(
17
17
  'web-capture - Capture web pages as HTML, Markdown, or PNG\n\nUsage:\n web-capture --serve [--port <port>] Start as API server\n web-capture <url> [options] Capture a URL to file/stdout'
18
18
  )
19
+ .command('$0 [url]', 'Capture a URL or start the API server', (yargs) =>
20
+ yargs.positional('url', {
21
+ type: 'string',
22
+ description: 'URL to capture',
23
+ })
24
+ )
19
25
  .option('serve', {
20
26
  alias: 's',
21
27
  type: 'boolean',
@@ -77,6 +83,7 @@ const config = makeConfig({
77
83
  .option('output', {
78
84
  alias: 'o',
79
85
  type: 'string',
86
+ nargs: 1,
80
87
  description:
81
88
  'Output file path (default: stdout for text, auto-generated for images)',
82
89
  })
@@ -232,6 +239,19 @@ const config = makeConfig({
232
239
  },
233
240
  });
234
241
 
242
+ function getUrlArgument() {
243
+ if (typeof config.url === 'string' && config.url.length > 0) {
244
+ return config.url;
245
+ }
246
+
247
+ const separatorIndex = process.argv.indexOf('--');
248
+ if (separatorIndex !== -1 && separatorIndex < process.argv.length - 1) {
249
+ return process.argv[separatorIndex + 1];
250
+ }
251
+
252
+ return null;
253
+ }
254
+
235
255
  async function startServer(port) {
236
256
  // Import the Express app
237
257
  const { app } = await import('../src/index.js');
@@ -824,8 +844,7 @@ async function captureUrl(url, options) {
824
844
  }
825
845
 
826
846
  async function main() {
827
- // Get positional arguments (non-option arguments)
828
- const url = config._ && config._.length > 0 ? config._[0] : null;
847
+ const url = getUrlArgument();
829
848
 
830
849
  if (config.serve) {
831
850
  // Server mode
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@link-assistant/web-capture",
3
- "version": "1.7.2",
3
+ "version": "1.7.3",
4
4
  "description": "CLI and microservice to render web pages as HTML, Markdown, or PNG",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -1,5 +1,6 @@
1
1
  // Unit tests for CLI argument parsing and functionality
2
2
  import { spawn } from 'child_process';
3
+ import http from 'node:http';
3
4
  import { fileURLToPath } from 'url';
4
5
  import { dirname, resolve } from 'path';
5
6
 
@@ -33,6 +34,38 @@ function runCli(args, options = {}) {
33
34
  });
34
35
  }
35
36
 
37
+ function startFixtureServer() {
38
+ return new Promise((resolve, reject) => {
39
+ const server = http.createServer((req, res) => {
40
+ res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
41
+ res.end(
42
+ '<!doctype html><html><body><h1>Issue 68</h1><p>Captured from positional URL.</p></body></html>'
43
+ );
44
+ });
45
+
46
+ server.on('error', reject);
47
+ server.listen(0, '127.0.0.1', () => {
48
+ const { port } = server.address();
49
+ resolve({
50
+ server,
51
+ url: `http://127.0.0.1:${port}/article`,
52
+ });
53
+ });
54
+ });
55
+ }
56
+
57
+ function stopFixtureServer(server) {
58
+ return new Promise((resolve, reject) => {
59
+ server.close((err) => {
60
+ if (err) {
61
+ reject(err);
62
+ return;
63
+ }
64
+ resolve();
65
+ });
66
+ });
67
+ }
68
+
36
69
  describe('CLI', () => {
37
70
  describe('--help', () => {
38
71
  test('shows help message with --help flag', async () => {
@@ -78,12 +111,63 @@ describe('CLI', () => {
78
111
  });
79
112
 
80
113
  describe('URL validation', () => {
114
+ test('accepts a URL as a positional argument', async () => {
115
+ const { server, url } = await startFixtureServer();
116
+
117
+ try {
118
+ const result = await runCli([
119
+ url,
120
+ '--format',
121
+ 'markdown',
122
+ '--output',
123
+ '-',
124
+ ]);
125
+
126
+ expect(result.code).toBe(0);
127
+ expect(result.stderr).not.toContain('Unknown argument');
128
+ expect(result.stdout).toContain('Issue 68');
129
+ expect(result.stdout).toContain('Captured from positional URL.');
130
+ } finally {
131
+ await stopFixtureServer(server);
132
+ }
133
+ }, 15000);
134
+
135
+ test('accepts a URL after the -- argument separator', async () => {
136
+ const { server, url } = await startFixtureServer();
137
+
138
+ try {
139
+ const result = await runCli([
140
+ '--format',
141
+ 'markdown',
142
+ '--output',
143
+ '-',
144
+ '--',
145
+ url,
146
+ ]);
147
+
148
+ expect(result.code).toBe(0);
149
+ expect(result.stderr).not.toContain('Missing URL');
150
+ expect(result.stdout).toContain('Issue 68');
151
+ } finally {
152
+ await stopFixtureServer(server);
153
+ }
154
+ }, 15000);
155
+
156
+ test('rejects unknown options', async () => {
157
+ const result = await runCli([
158
+ '--unknown-option-for-issue-68',
159
+ 'https://example.com',
160
+ ]);
161
+
162
+ expect(result.code).toBe(1);
163
+ expect(result.stderr).toContain('Unknown argument');
164
+ });
165
+
81
166
  test('rejects invalid URL', async () => {
82
- const result = await runCli(['not-a-valid-url-without-dots']);
83
- // Should fail with invalid URL or show help due to strict mode
167
+ const result = await runCli(['http://[invalid-url']);
168
+ // Should fail before trying to capture.
84
169
  expect(result.code).toBe(1);
85
- // Either an error message or help output (from yargs strict mode)
86
- expect(result.stdout.length + result.stderr.length).toBeGreaterThan(0);
170
+ expect(result.stderr).toContain('Invalid URL');
87
171
  }, 15000);
88
172
  });
89
173