@ai-ecoverse/slicc-man 1.1.0 → 1.1.2

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
@@ -134,7 +134,7 @@ Pages, the sitemap and the topic index are cached:
134
134
 
135
135
  Each site has its own directory in the cache, named by the base URL's host and port (`www.sliccy.com`, `127.0.0.1-8080`), with one JSON file per page, `sitemap.json` and `index.json`. A cached copy is fresh for the response's `Cache-Control` `max-age` (the site sends 4 hours), and 4 hours without one. A stale copy is revalidated with `If-Modified-Since` (the response's `Last-Modified`): `304` keeps it and makes it fresh again, `200` replaces it, and `404` drops it, so the topic has no page. When the site can't be reached, or answers 5xx, a cached copy is shown however old, after one line on stderr, `<cmd>: <url>: <reason>; showing the copy cached <date>`. With nothing cached, the command prints `<cmd>: <url>: <reason>` and exits 2. The cache is written atomically (a temporary file renamed into place), so two commands at once never see half a page; a cache that can't be written is skipped without a word.
136
136
 
137
- `apropos` and `whatis` read the index: every `/man/<topic>` entry of the sitemap, with its description, the first paragraph of its page, rendered as plain text on one line. The index is built from the cached sitemap and pages, fetching what is missing or stale (pages through the same cache, several at a time), and cached itself under the same rules. A page that fails to load is listed without a description.
137
+ `apropos` and `whatis` read the index: every `/man/<topic>` entry of the sitemap, with its description, the first paragraph of its page, rendered as plain text on one line. The index is built from the cached sitemap and pages, fetching what is missing or stale (pages through the same cache, several at a time), and cached itself under the same rules. A page that fails to load is listed without a description, and an index with a failed page isn't cached, so the next command tries that page again. After an HTTP error (a 404 or a 5xx), the other pages are still fetched; after a network error or a timeout, no more pages are fetched, and the rest come from the cache.
138
138
 
139
139
  `SLICC_MAN_URL` overrides the base URL `https://www.sliccy.com`: pages are `$SLICC_MAN_URL/man/<topic>.md` and the sitemap `$SLICC_MAN_URL/sitemap.xml`, whose entries count by their path, whatever their host. The tests point it at a local server.
140
140
 
package/host/main.mjs CHANGED
@@ -32,6 +32,7 @@ export async function main({
32
32
  columns,
33
33
  fetch = globalThis.fetch,
34
34
  now,
35
+ timeout,
35
36
  }) {
36
37
  const child = { ...env };
37
38
  delete child.SCMAN_INSTALL_DIR;
@@ -41,6 +42,7 @@ export async function main({
41
42
  columns,
42
43
  fetch,
43
44
  now,
45
+ timeout,
44
46
  version: version(),
45
47
  out: (text) => say(fds[1], text),
46
48
  err: (text) => say(fds[2], text),
package/lib/man.mjs CHANGED
@@ -201,7 +201,14 @@ export async function run({ kind = 'man', name, argv, env, io }) {
201
201
  values: parsed.values,
202
202
  width: width(env, io),
203
203
  styled: Boolean(io.tty) && !env.NO_COLOR,
204
- site: site({ url, store: io.store(folder(url)), fetch: io.fetch, now: io.now, name }),
204
+ site: site({
205
+ url,
206
+ store: io.store(folder(url)),
207
+ fetch: io.fetch,
208
+ now: io.now,
209
+ name,
210
+ timeout: io.timeout,
211
+ }),
205
212
  };
206
213
  if (kind !== 'man') return keywords(kind, ctx, parsed);
207
214
  if (parsed.flags.apropos) return keywords('apropos', ctx, parsed);
package/lib/site.mjs CHANGED
@@ -69,7 +69,7 @@ export function topics(xml) {
69
69
  return out;
70
70
  }
71
71
 
72
- export function site({ url, store, fetch, now = Date.now, name }) {
72
+ export function site({ url, store, fetch, now = Date.now, name, timeout = TIMEOUT }) {
73
73
  const root = url.replace(/\/+$/, '');
74
74
 
75
75
  async function load(id) {
@@ -101,7 +101,7 @@ export function site({ url, store, fetch, now = Date.now, name }) {
101
101
  async function request(address, id, entry) {
102
102
  const headers = {};
103
103
  if (entry?.lastModified) headers['if-modified-since'] = entry.lastModified;
104
- const response = await fetch(address, { headers, signal: AbortSignal.timeout(TIMEOUT) });
104
+ const response = await fetch(address, { headers, signal: AbortSignal.timeout(timeout) });
105
105
  if (response.status === 304 && typeof entry?.body === 'string') {
106
106
  await response.body?.cancel();
107
107
  await save(id, { ...entry, fetched: now(), maxAge: age(response.headers) ?? entry.maxAge });
@@ -131,12 +131,20 @@ export function site({ url, store, fetch, now = Date.now, name }) {
131
131
  try {
132
132
  found = await request(address, id, entry);
133
133
  } catch (error) {
134
- found = { why: reason(error) };
134
+ found = { why: reason(error), transport: true };
135
135
  }
136
136
  if (found.status) return found;
137
- if (!kept)
138
- return { status: 'error', message: `${name}: ${address}: ${found.why}`, offline: true };
137
+ const transport = found.transport ? { transport: true } : {};
138
+ if (!kept) {
139
+ return {
140
+ status: 'error',
141
+ message: `${name}: ${address}: ${found.why}`,
142
+ offline: true,
143
+ ...transport,
144
+ };
145
+ }
139
146
  return {
147
+ ...transport,
140
148
  status: 'ok',
141
149
  body: entry.body,
142
150
  stale: true,
@@ -154,12 +162,14 @@ export function site({ url, store, fetch, now = Date.now, name }) {
154
162
  async function build(list, offline) {
155
163
  const entries = new Array(list.length);
156
164
  let next = 0;
157
- let down = offline;
165
+ let down = false;
166
+ let broken = offline;
158
167
  async function worker() {
159
168
  while (next < list.length) {
160
169
  const at = next++;
161
170
  const topic = list[at];
162
- const found = await page(topic, { offline: down });
171
+ const found = await page(topic, { offline: broken });
172
+ if (found.transport) broken = true;
163
173
  if (found.stale || found.offline) down = true;
164
174
  entries[at] = { topic, description: found.status === 'ok' ? describe(found.body) : '' };
165
175
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-ecoverse/slicc-man",
3
- "version": "1.1.0",
3
+ "version": "1.1.2",
4
4
  "description": "man, apropos and whatis for SLICC, and scman for SLICC, macOS, Linux and Windows, reading pages from www.sliccy.com/man",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",