@songmu/mdhq 0.0.3 → 0.0.5

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
@@ -21,6 +21,7 @@ mdhq get https://example.com/article
21
21
  mdhq get https://example.com/article https://example.com/another-article
22
22
  cat urls.txt | mdhq get
23
23
  mdhq get --update https://example.com/article
24
+ mdhq get --assets https://example.com/article
24
25
  mdhq get --no-assets https://example.com/article
25
26
  mdhq get --json --header 'Cookie: session=value' https://example.com/article
26
27
  mdhq list
@@ -30,10 +31,13 @@ mdhq root
30
31
 
31
32
  `mdhq get` accepts multiple URLs as arguments or one URL per line on standard
32
33
  input; both sources are merged. It prints one absolute Markdown path per URL
33
- to stdout by default. Warnings are written to stderr. `--json` returns one
34
- result object for a single URL, or an array for multiple URLs. Requests are
35
- limited to eight in parallel, with requests to the same host serialized and
36
- spaced one second apart; image downloads use the same limits.
34
+ to stdout by default. Warnings are written to stderr. `--json` writes one
35
+ compact JSON result object per URL as JSON Lines. Results are written as soon
36
+ as each URL finishes, so parallel requests can produce output in completion
37
+ order rather than input order. If a later request fails, results already
38
+ written to stdout remain available.
39
+ Requests are limited to eight in parallel, with requests to the same host
40
+ serialized and spaced one second apart; image downloads use the same limits.
37
41
 
38
42
  `mdhq list` recursively lists `.md` files below the storage root, one per
39
43
  line, in sorted root-relative form. Use `-p` or `--full-path` to print absolute
@@ -99,7 +103,8 @@ const saved = await getPage({
99
103
 
100
104
  `convertHtml` performs extraction without fetching or writing files.
101
105
  `getPage` fetches, converts, optionally localizes images, adds frontmatter,
102
- and saves the document. Set `assets: false` or use `--no-assets` to keep
106
+ and saves the document. Use `--assets` or `--no-assets` to override the
107
+ configured asset behavior. Set `assets: false` or use `--no-assets` to keep
103
108
  absolute image URLs without creating `_assets`.
104
109
 
105
110
  Saved frontmatter uses Obsidian Web Clipper-compatible names such as `title`,
package/dist/cli.js CHANGED
@@ -65,11 +65,12 @@ export function createProgram(io = process) {
65
65
  .description("Fetch and save web pages.")
66
66
  .argument("[urls...]")
67
67
  .option("--root <path>", "storage root")
68
+ .option("--assets", "download images")
68
69
  .option("--no-assets", "do not download images")
69
70
  .option("--update", "update an existing page")
70
71
  .option("--user-agent <value>", "HTTP User-Agent")
71
72
  .option("--header <header>", "additional HTTP header", collect, [])
72
- .addOption(new Option("--json", "print a structured result"))
73
+ .addOption(new Option("--json", "print results as JSON Lines"))
73
74
  .action(async (urls, options) => {
74
75
  const inputUrls = await readStdinUrls(io.stdin);
75
76
  const requestedUrls = [...(urls ?? []), ...inputUrls]
@@ -79,7 +80,6 @@ export function createProgram(io = process) {
79
80
  throw new MdhqError("INVALID_URL", "At least one URL is required");
80
81
  }
81
82
  const scheduler = new RequestScheduler();
82
- const results = [];
83
83
  let nextIndex = 0;
84
84
  const worker = async () => {
85
85
  while (nextIndex < requestedUrls.length) {
@@ -89,22 +89,31 @@ export function createProgram(io = process) {
89
89
  if (url === undefined) {
90
90
  continue;
91
91
  }
92
- results[index] = await getPage({
92
+ const result = await getPage({
93
93
  url,
94
94
  ...(options.root ? { root: options.root } : {}),
95
- ...(options.assets === false ? { assets: false } : {}),
95
+ ...(options.assets !== undefined ? { assets: options.assets } : {}),
96
96
  update: options.update ?? false,
97
97
  ...(options.userAgent ? { userAgent: options.userAgent } : {}),
98
98
  headers: parseHeaders(options.header),
99
99
  scheduler,
100
100
  onWarning: (warning) => io.stderr.write(`warning: ${warning.message}\n`)
101
101
  });
102
+ io.stdout.write(`${options.json ? JSON.stringify(result) : result.path}\n`);
102
103
  }
103
104
  };
104
- await Promise.all(Array.from({ length: Math.min(8, requestedUrls.length) }, () => worker()));
105
- io.stdout.write(options.json
106
- ? `${JSON.stringify(results.length === 1 ? results[0] : results, null, 2)}\n`
107
- : `${results.map((result) => result.path).join("\n")}\n`);
105
+ const failures = [];
106
+ await Promise.all(Array.from({ length: Math.min(8, requestedUrls.length) }, async () => {
107
+ try {
108
+ await worker();
109
+ }
110
+ catch (error) {
111
+ failures.push(error);
112
+ }
113
+ }));
114
+ if (failures.length > 0) {
115
+ throw failures[0];
116
+ }
108
117
  });
109
118
  program
110
119
  .command("list")
@@ -106,10 +106,11 @@ Otherwise configuration overrides the built-in default.
106
106
  The CLI `--user-agent` option is passed as `GetPageOptions.userAgent` and
107
107
  therefore overrides configuration `userAgent`.
108
108
 
109
- The CLI `--no-assets` option passes `GetPageOptions.assets: false` and
110
- therefore overrides configuration `assets`. When asset localization is
111
- disabled, image destinations remain absolute URLs, the result contains no
112
- asset entries, and mdhq does not create `_assets`.
109
+ The CLI `--assets` and `--no-assets` options pass
110
+ `GetPageOptions.assets: true` and `GetPageOptions.assets: false`,
111
+ respectively, and therefore override configuration `assets`. When asset
112
+ localization is disabled, image destinations remain absolute URLs, the result
113
+ contains no asset entries, and mdhq does not create `_assets`.
113
114
 
114
115
  Generic CLI `--header` values and library `headers` values are appended after
115
116
  mdhq creates its `Accept` and User-Agent headers. A generic `User-Agent` or
@@ -21,13 +21,13 @@ Runtime requirements:
21
21
  The executable provides three subcommands:
22
22
 
23
23
  ```text
24
- mdhq get [options] <url>
24
+ mdhq get [options] [urls...]
25
25
  mdhq list [options]
26
26
  mdhq root [options]
27
27
  ```
28
28
 
29
- `mdhq get` accepts exactly one URL per invocation. Parallel or multi-URL
30
- processing is delegated to external tools such as `xargs`.
29
+ `mdhq get` accepts multiple URL arguments and one URL per line from standard
30
+ input. Both sources are merged, and up to eight URLs are processed in parallel.
31
31
 
32
32
  ### `get` options
33
33
 
@@ -37,22 +37,19 @@ processing is delegated to external tools such as `xargs`.
37
37
  | `--update` | Fetch and replace an existing document with the same URL identity. |
38
38
  | `--user-agent <value>` | Override the default HTTP User-Agent. |
39
39
  | `--header <header>` | Add an HTTP header. The option is repeatable and uses `Name: value` syntax. |
40
- | `--json` | Write a structured result instead of only the Markdown path. |
41
-
42
- On success without `--json`, stdout contains exactly one absolute Markdown
43
- path followed by a newline. Warnings are written to stderr.
44
-
45
- With `--json`, stdout contains an object with this shape:
46
-
47
- ```json
48
- {
49
- "requestedUrl": "https://example.com/start",
50
- "sourceUrl": "https://example.com/article",
51
- "path": "/data/mdhq/example.com/article.md",
52
- "status": "saved",
53
- "assets": [],
54
- "warnings": []
55
- }
40
+ | `--json` | Write one result per line as JSON Lines instead of only the Markdown path. |
41
+
42
+ Without `--json`, stdout receives one absolute Markdown path per requested URL,
43
+ with each path followed by a newline. Each result is written as soon as that
44
+ URL finishes, so parallel requests can produce output in completion order
45
+ rather than input order.
46
+ Warnings are written to stderr.
47
+
48
+ With `--json`, stdout contains one compact JSON object per requested URL,
49
+ also written as soon as the URL finishes. Each line has this shape:
50
+
51
+ ```jsonl
52
+ {"requestedUrl":"https://example.com/start","sourceUrl":"https://example.com/article","path":"/data/mdhq/example.com/article.md","status":"saved","assets":[],"warnings":[]}
56
53
  ```
57
54
 
58
55
  `status` is one of:
@@ -63,7 +60,8 @@ With `--json`, stdout contains an object with this shape:
63
60
  body, including an HTTP 304 response.
64
61
  - `skipped`: an existing same-identity file was kept.
65
62
 
66
- An error is written to stderr and causes exit status `1`.
63
+ An error is written to stderr and causes exit status `1`. Results written to
64
+ stdout before the error remain available to downstream consumers.
67
65
 
68
66
  ### `list`
69
67
 
@@ -508,10 +506,11 @@ For images:
508
506
 
509
507
  Asset localization is enabled by default. It can be disabled with the
510
508
  top-level configuration field `assets: false`, library option
511
- `GetPageOptions.assets: false`, or CLI option `--no-assets`. The library
512
- option and CLI option take precedence over configuration. When disabled,
513
- HTTP(S) image destinations are kept as absolute URLs, the result `assets`
514
- array is empty, and `_assets` is not created.
509
+ `GetPageOptions.assets: false`, or CLI option `--no-assets`. The CLI options
510
+ `--assets` and `--no-assets` explicitly enable or disable localization and
511
+ take precedence over configuration, as does the library option. When
512
+ disabled, HTTP(S) image destinations are kept as absolute URLs, the result
513
+ `assets` array is empty, and `_assets` is not created.
515
514
 
516
515
  mdhq does not rewrite ordinary links to other locally stored Markdown
517
516
  files.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@songmu/mdhq",
3
- "version": "0.0.3",
3
+ "version": "0.0.5",
4
4
  "description": "Save web pages as Markdown in a ghq-inspired filesystem layout.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -69,6 +69,6 @@
69
69
  "@types/node": "^26.4.0",
70
70
  "@types/proper-lockfile": "4.1.4",
71
71
  "typescript": "^7.0.2",
72
- "vitest": "^4.1.11"
72
+ "vitest": "^5.0.0"
73
73
  }
74
74
  }