prolog-notebook 0.6.2 → 0.6.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,36 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.6.3] — 2026-08-30
4
+
5
+ `prolog-notebook build lists.prolog.md --check-update` answered *unknown option* — on a flag the
6
+ help listed under no command, exactly as though it were global.
7
+
8
+ ### Fixed
9
+
10
+ - **`--check-update` works with every command**, and forces the check past the daily TTL, which
11
+ is the whole point of asking. It was accepted by `execute` alone.
12
+
13
+ - **…and it answers down a pipe.** The check was skipped entirely without a terminal, so an
14
+ explicit check in a script reported nothing — indistinguishable from *you are up to date*,
15
+ which is the one thing an update notice must never look like. A terminal is needed to **offer**
16
+ an upgrade, never to **report** one.
17
+
18
+ - **`build --port 90` was accepted and ignored.** `view` and `build` share a parser, so each
19
+ quietly took the other's flags. A flag that is read and thrown away is worse than one refused:
20
+ it looks like it worked.
21
+
22
+ - **`clear` never looked for an update.** 0.5.1 established that every command doing real work
23
+ goes through one door, before the work; `clear` shipped afterwards and missed it, while
24
+ rewriting the author's notebook.
25
+
26
+ ### Changed
27
+
28
+ - **The help lists each option under the command that takes it.** Only `--check-update`,
29
+ `--version` and `-h` are listed as working anywhere, because only those three do.
30
+
31
+ - **A misplaced option says where it lives** — `--limit belongs to execute, not to view` —
32
+ rather than `unknown option` about a flag that is real and two lines further up.
33
+
3
34
  ## [0.6.2] — 2026-08-30
4
35
 
5
36
  From a field report: a built chapter opened in Chrome and Safari showed no controls and a Run
package/README.md CHANGED
@@ -132,16 +132,24 @@ chapter.prolog.md: 4 answers removed
132
132
  `clear` empties every output block and touches nothing else; `execute` fills them in again from
133
133
  the engine. A chapter with no answers is a valid chapter — one that has not been executed yet.
134
134
 
135
+ Each option belongs to a command, and typing one under the wrong command tells you which:
136
+
137
+ | flag | on | |
138
+ |---|---|---|
139
+ | `--limit <n>` | `execute` | solutions to take from one query before stopping. Default 100. |
140
+ | `--stdout` | `execute`, `clear` | print the result instead of writing the file |
141
+ | `--quiet` | `execute`, `clear` | report only failures |
142
+ | `--out <dir>` | `build` | where it writes. Default `<file>-site` |
143
+ | `--port <n>` | `view` | what it listens on. Default 8777, and it takes another if that one is busy |
144
+ | `--no-open` | `view` | print the URL instead of opening a browser |
145
+
146
+ Three work anywhere, because they are about the tool rather than about a notebook:
147
+
135
148
  | flag | |
136
149
  |---|---|
137
- | `--limit <n>` | solutions to take from one query before stopping. Default 100. |
138
- | `--stdout` | print the result instead of writing the file |
139
- | `--quiet` | report only failures |
140
- | `--out <dir>` | where `build` writes. Default `<file>-site` |
141
- | `--port <n>` | what `view` listens on. Default 8777, and it takes another if that one is busy |
142
- | `--no-open` | `view` prints the URL instead of opening a browser |
143
150
  | `--version` | the tool's version, **the SWI-Prolog version it will run your chapters with**, and the copyright |
144
- | `--check-update` | ask npm whether a newer one exists, and say so either way |
151
+ | `--check-update` | ask npm whether a newer one exists, and say so either way — forced, whatever the daily check thinks, and answered even down a pipe |
152
+ | `-h`, `--help` | the same list, from the command itself |
145
153
 
146
154
  ```sh
147
155
  prolog-notebook upgrade # fetch the latest
@@ -37,29 +37,66 @@ for (const stream of [process.stdout, process.stderr]) {
37
37
  // src/version.js, where a page can import it too.
38
38
  const require = createRequire(import.meta.url);
39
39
 
40
+ /**
41
+ * EACH OPTION UNDER THE COMMAND THAT TAKES IT (869erqra0).
42
+ *
43
+ * They were listed in one flat `Options` block with no owner, which reads as a
44
+ * promise that every one of them works everywhere — so `build --check-update`
45
+ * was a reasonable thing to type and `unknown option` was a strange thing to be
46
+ * told. Only three of them are actually the tool's rather than a command's, and
47
+ * now only those three are listed as such.
48
+ */
40
49
  const USAGE = `prolog-notebook — Jupyter-style notebooks for Prolog
41
50
 
42
51
  prolog-notebook view <file.prolog.md> read it in a browser, cells and all
52
+ --port <n> what it listens on (default 8777)
53
+ --no-open print the URL instead of opening a browser
54
+
43
55
  prolog-notebook build <file.prolog.md> write a page you can host or send
56
+ --out <dir> where it writes (default: <file>-site)
57
+
44
58
  prolog-notebook execute <file.prolog.md>... run every query, write the answers in
59
+ --limit <n> solutions to take from one query before stopping (default ${DEFAULT_LIMIT})
60
+ --stdout print the result instead of writing the file
61
+ --quiet report only failures
62
+
45
63
  prolog-notebook clear <file.prolog.md>... take the answers back out
64
+ --stdout print the result instead of writing the file
65
+ --quiet report only failures
66
+
46
67
  prolog-notebook upgrade fetch the latest version
47
68
 
48
- Options
49
- --limit <n> solutions to take from one query before stopping (default ${DEFAULT_LIMIT})
50
- --stdout print the result instead of writing the file
51
- --quiet report only failures
52
- --out <dir> where build writes (default: <file>-site)
53
- --port <n> what view listens on (default 8777)
54
- --no-open view prints the URL instead of opening a browser
55
- --version version, engine and copyright
69
+ Anywhere
56
70
  --check-update ask npm whether a newer one exists, and say so either way
71
+ --version version, engine and copyright
57
72
  -h, --help this
58
73
 
59
74
  A query that stops at the limit is written without a terminator, which is the
60
75
  format's way of saying the search was never exhausted. Nothing is invented.
61
76
  `;
62
77
 
78
+ /**
79
+ * Which command each option belongs to, so a misplaced one can say where it lives.
80
+ *
81
+ * `unknown option "--limit"` is true and unhelpful when the flag is real and two
82
+ * lines further up the same help. This costs a lookup table and saves a re-read.
83
+ */
84
+ const BELONGS_TO = {
85
+ '--limit': 'execute',
86
+ '--stdout': 'execute and clear',
87
+ '--quiet': 'execute and clear',
88
+ '--out': 'build',
89
+ '--port': 'view',
90
+ '--no-open': 'view',
91
+ };
92
+
93
+ function unknownOption(arg, command) {
94
+ const home = BELONGS_TO[arg];
95
+ return home && home !== command
96
+ ? `${arg} belongs to ${home}, not to ${command}\n`
97
+ : `unknown option "${arg}"\n`;
98
+ }
99
+
63
100
  /**
64
101
  * A runaway goal hangs this process — the engine is in-process here, so there is
65
102
  * no thread left to notice (869ejgyax). Stated rather than implied, because the
@@ -158,11 +195,18 @@ function canAsk() {
158
195
  * to a newer version, null to carry on here.
159
196
  */
160
197
  async function upgradeFirst({ quiet = false, asked = false } = {}) {
161
- if (!canAsk() || (quiet && !asked)) return null;
198
+ // ASKED OUTRIGHT ALWAYS ANSWERS (869erqra0). A terminal is needed to OFFER the
199
+ // upgrade, never to report one: `--check-update` down a pipe used to check
200
+ // nothing and say nothing, which is indistinguishable from "you are up to
201
+ // date" — the one thing this must never look like.
202
+ if (!asked && (!canAsk() || quiet)) return null;
162
203
  const ahead = await updateNotice({ version: VERSION, force: asked })
163
204
  .catch(() => ({ message: null, newer: null }));
164
205
  if (ahead.message) process.stderr.write(`${ahead.message}\n`);
165
- if (!ahead.newer || !(await confirm('Update and continue on the new version?'))) return null;
206
+ // The notice is printed above whatever happens next; only the question needs
207
+ // somebody at the other end to answer it.
208
+ if (!ahead.newer || !canAsk()) return null;
209
+ if (!(await confirm('Update and continue on the new version?'))) return null;
166
210
  if ((await upgrade(ahead.newer)) !== 0) {
167
211
  process.stderr.write('Carrying on with the version you have.\n');
168
212
  return null;
@@ -213,8 +257,8 @@ async function main(argv) {
213
257
  }
214
258
 
215
259
  const command = args.shift();
216
- if (command === 'view' || command === 'build') return page(command, args);
217
- if (command === 'clear') return clear(args);
260
+ if (command === 'view' || command === 'build') return page(command, args, asked);
261
+ if (command === 'clear') return clear(args, asked);
218
262
  if (command === 'upgrade') {
219
263
  const { message, newer } = await updateNotice({ version: VERSION, force: true });
220
264
  if (message) process.stderr.write(`${message}\n`);
@@ -246,7 +290,7 @@ async function main(argv) {
246
290
  else if (arg === '--quiet') options.quiet = true;
247
291
  else if (arg === '--check-update') { /* handled above, and not a file */ }
248
292
  else if (arg.startsWith('-')) {
249
- process.stderr.write(`unknown option "${arg}"\n\n${USAGE}`);
293
+ process.stderr.write(unknownOption(arg, 'execute'));
250
294
  return 2;
251
295
  } else files.push(arg);
252
296
  }
@@ -259,7 +303,9 @@ async function main(argv) {
259
303
 
260
304
  const jump = await upgradeFirst({ quiet: options.quiet, asked });
261
305
  if (jump !== null) return jump;
262
- checked = canAsk() && (!options.quiet || asked);
306
+ // Whatever upgradeFirst has just reported must not be reported again below.
307
+ // Asked outright, it always reports now, terminal or not.
308
+ checked = asked || (canAsk() && !options.quiet);
263
309
 
264
310
  // STARTED NOW, READ AT THE END. The registry is somebody else's machine on
265
311
  // somebody else's network, and none of that should stand between the reader
@@ -309,14 +355,16 @@ async function main(argv) {
309
355
  * No engine, no network and no update check: this is a text operation on a file
310
356
  * the reader already has.
311
357
  */
312
- async function clear(args) {
358
+ async function clear(args, asked = false) {
313
359
  const options = { stdout: false, quiet: false };
314
360
  const files = [];
315
361
  for (const arg of args) {
316
362
  if (arg === '--stdout') options.stdout = true;
317
363
  else if (arg === '--quiet') options.quiet = true;
364
+ // Handled by the caller, and not a file.
365
+ else if (arg === '--check-update') { /* global */ }
318
366
  else if (arg.startsWith('-')) {
319
- process.stderr.write(`unknown option "${arg}"\n`);
367
+ process.stderr.write(unknownOption(arg, 'clear'));
320
368
  return 2;
321
369
  } else files.push(arg);
322
370
  }
@@ -325,6 +373,13 @@ async function clear(args) {
325
373
  return 2;
326
374
  }
327
375
 
376
+ // THE SAME DOOR AS EVERY OTHER COMMAND THAT DOES REAL WORK (869erqra0). 0.5.1
377
+ // put `view` and `build` through it and this command shipped afterwards, so it
378
+ // never looked. Emptying somebody's notebook is as real as work gets here, and
379
+ // before the work is the only point at which a newer version changes anything.
380
+ const jump = await upgradeFirst({ quiet: options.quiet, asked });
381
+ if (jump !== null) return jump;
382
+
328
383
  let status = 0;
329
384
  for (const file of files) {
330
385
  let source;
@@ -361,11 +416,20 @@ async function clear(args) {
361
416
  * whole reason a built page is readable before any engine arrives. Use `run` to
362
417
  * put them there.
363
418
  */
364
- async function page(command, args) {
419
+ async function page(command, args, asked = false) {
365
420
  const options = { out: null, port: 8777, open: true };
366
421
  const files = [];
367
422
  while (args.length) {
368
423
  const arg = args.shift();
424
+ // ONE PARSER, TWO COMMANDS, AND THEY DO NOT TAKE THE SAME FLAGS. `view` and
425
+ // `build` share this function, so `build --port 90` was quietly accepted and
426
+ // ignored while the help said --port was view's (869erqra0). A flag that is
427
+ // read and thrown away is worse than one that is refused: it looks like it
428
+ // worked.
429
+ if (BELONGS_TO[arg] && BELONGS_TO[arg] !== command) {
430
+ process.stderr.write(unknownOption(arg, command));
431
+ return 2;
432
+ }
369
433
  if (arg === '--out') options.out = args.shift();
370
434
  else if (arg === '--port') {
371
435
  options.port = Number(args.shift());
@@ -374,8 +438,10 @@ async function page(command, args) {
374
438
  return 2;
375
439
  }
376
440
  } else if (arg === '--no-open') options.open = false;
441
+ // Handled by the caller, and not a file.
442
+ else if (arg === '--check-update') { /* global */ }
377
443
  else if (arg.startsWith('-')) {
378
- process.stderr.write(`unknown option "${arg}"\n`);
444
+ process.stderr.write(unknownOption(arg, command));
379
445
  return 2;
380
446
  } else files.push(arg);
381
447
  }
@@ -387,7 +453,7 @@ async function page(command, args) {
387
453
  // The same offer the execute path makes, and for the same reason: a server about to
388
454
  // start, or a directory about to be written, is work that a newer version
389
455
  // should be doing.
390
- const jump = await upgradeFirst();
456
+ const jump = await upgradeFirst({ asked });
391
457
  if (jump !== null) return jump;
392
458
 
393
459
  const file = files[0];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prolog-notebook",
3
- "version": "0.6.2",
3
+ "version": "0.6.3",
4
4
  "description": "Jupyter-style notebooks for Prolog. Runs in the browser, installs nothing.",
5
5
  "type": "module",
6
6
  "main": "./src/node.js",
@@ -1,4 +1,4 @@
1
1
  {
2
- "commit": "bdbce1b",
3
- "built": "2026-08-30 22:41:37 UTC"
2
+ "commit": "2d089b9",
3
+ "built": "2026-08-30 22:55:39 UTC"
4
4
  }
package/src/version.js CHANGED
@@ -10,7 +10,7 @@
10
10
  export const NAME = 'Prolog Notebook';
11
11
 
12
12
  /** Must equal package.json's `version` — test/run.test.mjs enforces it. */
13
- export const VERSION = '0.6.2';
13
+ export const VERSION = '0.6.3';
14
14
 
15
15
  /** The two facts a licence notice is actually made of. */
16
16
  export const YEAR = '2026';