prolog-notebook 0.6.4 → 0.6.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/CHANGELOG.md +29 -0
- package/package.json +1 -1
- package/src/browser.js +10 -2
- package/src/build-info.json +2 -2
- package/src/notebook.js +50 -18
- package/src/version.js +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.6.5] — 2026-08-31
|
|
4
|
+
|
|
5
|
+
Both from one field report on a real chapter.
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- **`Attempt to access not innermost query` on a perfectly ordinary Run.** Loading the program
|
|
10
|
+
cells above a query emits `consulted`, which is exactly what `rerun="auto"` waits for — so an
|
|
11
|
+
auto cell's re-run was queued and ran while the manual run was still inside its awaits. It
|
|
12
|
+
asked whether anybody was mid-sequence, saw nobody, and opened its query underneath the one
|
|
13
|
+
about to be opened. One engine allows one open query, and whichever is not innermost cannot
|
|
14
|
+
be stepped, so the reader — who had pressed Run on one cell and touched nothing else — got
|
|
15
|
+
SWI's own words for a stack they are never supposed to meet.
|
|
16
|
+
|
|
17
|
+
A cell now claims the engine when its run **starts**, not when it succeeds. Two supporting
|
|
18
|
+
changes close the same class: the engine's version probe is awaited during boot rather than
|
|
19
|
+
fired beside the reader's first Run, and `all()` holds the session's one slot until the
|
|
20
|
+
worker has answered rather than releasing it up front.
|
|
21
|
+
|
|
22
|
+
- **No way to clear your own answers in a chapter that shipped without any.** The outputs row
|
|
23
|
+
removed itself when the FILE had no saved answers — but that argument dies the moment a
|
|
24
|
+
reader presses Run, and a chapter published unrun is most chapters while an author is still
|
|
25
|
+
writing. The row now exists whenever there is a query cell, and counts what is on the page
|
|
26
|
+
rather than what is in the file: *No outputs yet*, *3 outputs on this page*, *3 outputs
|
|
27
|
+
cleared*.
|
|
28
|
+
|
|
29
|
+
The hide row above it stays keyed to the file, and correctly — hiding is for the chapter's
|
|
30
|
+
saved answers, and a reader's own run is not a spoiler.
|
|
31
|
+
|
|
3
32
|
## [0.6.4] — 2026-08-31
|
|
4
33
|
|
|
5
34
|
### Changed
|
package/package.json
CHANGED
package/src/browser.js
CHANGED
|
@@ -229,8 +229,16 @@ class WorkerQuery {
|
|
|
229
229
|
async all(limit) {
|
|
230
230
|
if (this.done) return { solutions: [], truncated: false };
|
|
231
231
|
const qid = await this.#open();
|
|
232
|
-
|
|
233
|
-
|
|
232
|
+
try {
|
|
233
|
+
return await this.session.send('all', { qid, limit });
|
|
234
|
+
} finally {
|
|
235
|
+
// AFTER THE WORKER HAS ANSWERED, not before. Releasing the session's one
|
|
236
|
+
// slot up front said "nothing is open here" while the engine was still
|
|
237
|
+
// inside the goal, so anything that opened next nested inside a frame the
|
|
238
|
+
// session had already forgotten (869erqvzu). The slot is the claim that
|
|
239
|
+
// this query is the innermost one, and that stays true until it is done.
|
|
240
|
+
this.#finish();
|
|
241
|
+
}
|
|
234
242
|
}
|
|
235
243
|
|
|
236
244
|
async close({ superseded = false } = {}) {
|
package/src/build-info.json
CHANGED
package/src/notebook.js
CHANGED
|
@@ -478,11 +478,18 @@ function mountPageBar(root, options, bus, programs, queries) {
|
|
|
478
478
|
const outputsUnit = bar.querySelector('.unit.outputs');
|
|
479
479
|
const outputsState = bar.querySelector('.outputs-state');
|
|
480
480
|
let refreshOutputs = () => {};
|
|
481
|
-
//
|
|
482
|
-
//
|
|
483
|
-
//
|
|
484
|
-
//
|
|
485
|
-
|
|
481
|
+
// ANY QUERY CELL AT ALL, not just a chapter that shipped answers.
|
|
482
|
+
//
|
|
483
|
+
// This used to go when the FILE had none, on the argument that an unrun
|
|
484
|
+
// chapter is the CLI's business. That argument was wrong the moment a reader
|
|
485
|
+
// pressed Run: a chapter published without answers — which is most of them
|
|
486
|
+
// while an author is still writing — gave them no way to clear the answers
|
|
487
|
+
// they had just produced, and a panel with the row missing reads as a control
|
|
488
|
+
// that has broken rather than one that had nothing to do (869erqw08).
|
|
489
|
+
//
|
|
490
|
+
// The row above stays keyed to the FILE, and correctly: hiding is for the
|
|
491
|
+
// chapter's saved answers, and a reader's own run is not a spoiler.
|
|
492
|
+
if (!queries.length) {
|
|
486
493
|
outputsUnit.remove();
|
|
487
494
|
} else {
|
|
488
495
|
const wipe = document.createElement('button');
|
|
@@ -503,12 +510,14 @@ function mountPageBar(root, options, bus, programs, queries) {
|
|
|
503
510
|
// to give back, so a page of only those leaves restore with no work.
|
|
504
511
|
const restorable = gone.filter((q) => q.hasSaved).length;
|
|
505
512
|
wipe.disabled = back ? restorable === 0 : left.length === 0;
|
|
506
|
-
// COUNTED FROM THE
|
|
507
|
-
//
|
|
508
|
-
//
|
|
509
|
-
outputsState.textContent = gone.length
|
|
510
|
-
? `${plural(
|
|
511
|
-
:
|
|
513
|
+
// COUNTED FROM THE PAGE, which is the only count that stays true for both
|
|
514
|
+
// kinds of chapter: one published with its answers in it, and one still
|
|
515
|
+
// being written where every output on screen is the reader's own.
|
|
516
|
+
outputsState.textContent = gone.length > 0
|
|
517
|
+
? `${plural(gone.length, 'output')} cleared`
|
|
518
|
+
: left.length > 0
|
|
519
|
+
? `${plural(left.length, 'output')} on this page`
|
|
520
|
+
: 'No outputs yet';
|
|
512
521
|
label(wipe, back ? 'restore' : 'erase',
|
|
513
522
|
back ? 'Restore outputs' : 'Clear all outputs');
|
|
514
523
|
wipe.title = back
|
|
@@ -667,12 +676,22 @@ async function boot(options, bus, status) {
|
|
|
667
676
|
bus.booted = true;
|
|
668
677
|
if (!wasBooted) {
|
|
669
678
|
bus.emit({ kind: 'started', at: clock() });
|
|
670
|
-
//
|
|
671
|
-
//
|
|
672
|
-
//
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
679
|
+
// AWAITED, AND THAT IS A CORRECTNESS MATTER RATHER THAN A PREFERENCE.
|
|
680
|
+
//
|
|
681
|
+
// This was fired and forgotten, on the argument that the light going green
|
|
682
|
+
// answers "did it start" and a version number should not hold it back. But
|
|
683
|
+
// asking for the version IS A QUERY, and one engine allows ONE OPEN QUERY
|
|
684
|
+
// (869epzqpc). Firing it alongside the reader's own Run put two opens into
|
|
685
|
+
// the worker's queue, and whichever arrived second nested inside the first —
|
|
686
|
+
// so the cell that started the engine got `Attempt to access not innermost
|
|
687
|
+
// query` while the panel cheerfully displayed the version that had won the
|
|
688
|
+
// race (869erqvzu).
|
|
689
|
+
//
|
|
690
|
+
// The light still goes green first: the event above is emitted before this
|
|
691
|
+
// waits. What it now costs is one flag lookup on the first boot only, and
|
|
692
|
+
// what it buys is that no cell query can ever be in flight beside it.
|
|
693
|
+
const version = await prologVersion(session).catch(() => null);
|
|
694
|
+
if (version) bus.emit({ kind: 'engine-version', version });
|
|
676
695
|
}
|
|
677
696
|
return session;
|
|
678
697
|
}
|
|
@@ -1227,6 +1246,20 @@ function mountQuery(cell, options, bus, { above = [], below = [], prediction = n
|
|
|
1227
1246
|
: whose, 'from');
|
|
1228
1247
|
write(`?- ${goal}.`, 'echo');
|
|
1229
1248
|
setRunning(true);
|
|
1249
|
+
// THE ENGINE IS CLAIMED HERE, BEFORE THE CONSULTS — not after the query is
|
|
1250
|
+
// opened, which is where this used to be (869erqvzu).
|
|
1251
|
+
//
|
|
1252
|
+
// Loading the cells above emits `consulted`, and that is exactly what an
|
|
1253
|
+
// auto cell waits for. Its re-run is queued and runs on the next turn, while
|
|
1254
|
+
// this run is still somewhere inside its awaits — so it looked at a page
|
|
1255
|
+
// where nobody was stepping, saw a free engine, and opened its own query
|
|
1256
|
+
// underneath the one this cell was about to open. The reader, who had
|
|
1257
|
+
// pressed Run on one cell and touched nothing else, got `Attempt to access
|
|
1258
|
+
// not innermost query`.
|
|
1259
|
+
//
|
|
1260
|
+
// A cell is using the engine from the moment it starts, not from the moment
|
|
1261
|
+
// it succeeds. finish() lets go, on every path out of here.
|
|
1262
|
+
bus.stepping.add(cell);
|
|
1230
1263
|
try {
|
|
1231
1264
|
if (!bus.booted) write('starting SWI-Prolog (5.9 MB, first time only)…', 'done');
|
|
1232
1265
|
session = await boot(options, bus);
|
|
@@ -1243,7 +1276,6 @@ function mountQuery(cell, options, bus, { above = [], below = [], prediction = n
|
|
|
1243
1276
|
return;
|
|
1244
1277
|
}
|
|
1245
1278
|
query = session.query(goal);
|
|
1246
|
-
bus.stepping.add(cell);
|
|
1247
1279
|
// A sequence ends when another one starts — one engine, one open query
|
|
1248
1280
|
// (869epzqpc). The reader hears it here, in the cell it happened to, with
|
|
1249
1281
|
// the solutions they did take still under it and Run still lit. Silence was
|
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.
|
|
13
|
+
export const VERSION = '0.6.5';
|
|
14
14
|
|
|
15
15
|
/** The two facts a licence notice is actually made of. */
|
|
16
16
|
export const YEAR = '2026';
|