@descix/frqtl-node 0.0.0-stage → 0.6.0-p0.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/CLAUDE.md +87 -0
- package/LICENSE.md +664 -0
- package/README.md +84 -2
- package/dist/dist-manifest.json +218 -0
- package/dist/eyes/index.js +109 -0
- package/dist/fat/pkg-node/egpt_wasm.js +1277 -0
- package/dist/fat/pkg-node/egpt_wasm_bg.wasm +0 -0
- package/dist/fat/pkg-node/package.json +12 -0
- package/dist/frqtl/assets/vendor/chroma.mjs +64 -0
- package/dist/frqtl/compat/boot-node.js +34 -0
- package/dist/frqtl/compat/engine.js +127 -0
- package/dist/frqtl/compat/package-root.js +24 -0
- package/dist/frqtl/compat/providers/wasm-engine.js +2499 -0
- package/dist/frqtl/compat/providers/wasm-load-browser.js +134 -0
- package/dist/frqtl/compat/providers/wasm-load-node.js +59 -0
- package/dist/frqtl/compat/renderer/FieldBuilder.js +145 -0
- package/dist/frqtl/compat/renderer/Filters.js +163 -0
- package/dist/frqtl/compat/renderer/RenderFamily.js +84 -0
- package/dist/frqtl/compat/renderer/RenderMesh.js +99 -0
- package/dist/frqtl/compat/renderer/RenderShape.js +154 -0
- package/dist/frqtl/compat/renderer/camera2d-geometry.js +183 -0
- package/dist/frqtl/compat/renderer/captureStill-common.js +258 -0
- package/dist/frqtl/compat/renderer/display-slot.js +96 -0
- package/dist/frqtl/compat/renderer/lo-drawn-center.js +231 -0
- package/dist/frqtl/compat/renderer/orbit-bag.js +216 -0
- package/dist/frqtl/compat/renderer/providers/godrays-layer.js +213 -0
- package/dist/frqtl/compat/renderer/providers/index.js +22 -0
- package/dist/frqtl/compat/renderer/providers/p5-provider.js +861 -0
- package/dist/frqtl/compat/renderer/providers/three-camera-contract.js +160 -0
- package/dist/frqtl/compat/renderer/providers/three-camera-geometry.js +571 -0
- package/dist/frqtl/compat/renderer/providers/three-provider.js +2470 -0
- package/dist/frqtl/compat/renderer/renderCapabilities-common.js +186 -0
- package/dist/frqtl/compat/renderer/renderer-provider-contract.js +238 -0
- package/dist/frqtl/compat/renderer/unprojectStill.js +172 -0
- package/dist/frqtl/compat/vendor.js +196 -0
- package/dist/frqtl/node-entry.js +17 -0
- package/dist/frqtl/objects/FrqtlApp.js +2920 -0
- package/dist/frqtl/objects/ObjectFactory.js +296 -0
- package/dist/frqtl/objects/RecipeContext.js +322 -0
- package/dist/frqtl/objects/collectors/DataCollector.js +116 -0
- package/dist/frqtl/objects/constants/WallBehaviors.js +178 -0
- package/dist/frqtl/objects/geometry/SlabFace.js +258 -0
- package/dist/frqtl/objects/instruments/capture-host.js +453 -0
- package/dist/frqtl/objects/instruments/capture.js +710 -0
- package/dist/frqtl/objects/instruments/hosted-eyes.js +984 -0
- package/dist/frqtl/objects/kinds-entry.js +14 -0
- package/dist/frqtl/objects/kinds.js +190 -0
- package/dist/frqtl/objects/objects/Box.js +273 -0
- package/dist/frqtl/objects/objects/Bullet.js +155 -0
- package/dist/frqtl/objects/objects/CircuitGate.js +380 -0
- package/dist/frqtl/objects/objects/CircuitProbe.js +214 -0
- package/dist/frqtl/objects/objects/DirectedTunnel.js +411 -0
- package/dist/frqtl/objects/objects/EgptFractal.js +164 -0
- package/dist/frqtl/objects/objects/LightSource.js +351 -0
- package/dist/frqtl/objects/objects/QuantumEmitter.js +760 -0
- package/dist/frqtl/objects/objects/Tube.js +471 -0
- package/dist/frqtl/objects/objects/absorbRadiation.js +309 -0
- package/dist/frqtl/objects/objects/actionCompiler.js +1747 -0
- package/dist/frqtl/objects/objects/controlAction.js +89 -0
- package/dist/frqtl/objects/objects/controlBind.js +278 -0
- package/dist/frqtl/objects/objects/directedRedirectProgram.js +120 -0
- package/dist/frqtl/objects/objects/emitterProgram.js +209 -0
- package/dist/frqtl/objects/objects/frqtl-control-slider.js +197 -0
- package/dist/frqtl/objects/objects/frqtl-control-sync.js +272 -0
- package/dist/frqtl/objects/objects/frqtl-control-widgets.js +219 -0
- package/dist/frqtl/objects/objects/frqtl-control-write-queue.js +174 -0
- package/dist/frqtl/objects/objects/makeControlsForObject.js +820 -0
- package/dist/frqtl/objects/objects/makeControlsForRenderSurface.js +161 -0
- package/dist/frqtl/objects/objects/slitLinerScatter.js +261 -0
- package/dist/frqtl/objects/objects/sustainedSource.js +298 -0
- package/dist/frqtl/objects/objects/tagger.js +228 -0
- package/dist/frqtl/objects/schema/engine-scopes.js +468 -0
- package/dist/frqtl/objects/schema/index.js +256 -0
- package/dist/frqtl/objects/schema/scopes.js +457 -0
- package/dist/frqtl/objects/schema/validate-descriptor.js +49 -0
- package/dist/frqtl/objects/schema/valueNormalizer.js +366 -0
- package/dist/frqtl/objects/setup/_collisionMatrixLanes.js +66 -0
- package/dist/frqtl/objects/setup/_setupOptions.js +118 -0
- package/dist/frqtl/objects/setup/_worldInit.js +162 -0
- package/dist/frqtl/objects/setup/setupBlackbodySubstrate.js +242 -0
- package/dist/frqtl/objects/setup/setupCircuitBasic.js +447 -0
- package/dist/frqtl/objects/setup/setupCollisionMatrix.js +188 -0
- package/dist/frqtl/objects/setup/setupConeEmitter3D.js +250 -0
- package/dist/frqtl/objects/setup/setupDoubleSlit.js +326 -0
- package/dist/frqtl/objects/setup/setupDoubleSlit_Laser.js +229 -0
- package/dist/frqtl/objects/setup/setupEmpty.js +59 -0
- package/dist/frqtl/objects/setup/setupFxSmoke.js +209 -0
- package/dist/frqtl/objects/setup/setupG1Race.js +80 -0
- package/dist/frqtl/objects/setup/setupG2Sweep.js +89 -0
- package/dist/frqtl/objects/setup/setupG3Collide.js +177 -0
- package/dist/frqtl/objects/setup/setupLightSourceLab.js +277 -0
- package/dist/frqtl/objects/setup/setupOscillator.js +68 -0
- package/dist/frqtl/objects/setup/setupParticleWalk.js +93 -0
- package/dist/frqtl/objects/setup/setupParticleWalk3D.js +133 -0
- package/dist/frqtl/objects/setup/setupSphereEmitter3D.js +1258 -0
- package/dist/frqtl/objects/setup/setupTraceCircuit.js +239 -0
- package/dist/frqtl/objects/setup/setupTunnelLab.js +240 -0
- package/dist/frqtl/objects/setup/setupTunnelPointExit.js +306 -0
- package/dist/frqtl/objects/setup/setupWaveInterference.js +177 -0
- package/dist/frqtl/objects/v1-replacements.js +29 -0
- package/dist/frqtl/package-core.js +23 -0
- package/dist/frqtl/parts/CameraControls.js +643 -0
- package/dist/frqtl/parts/CameraPath.js +575 -0
- package/dist/frqtl/parts/CollisionActions.js +34 -0
- package/dist/frqtl/parts/CollisionTypes.js +30 -0
- package/dist/frqtl/parts/DetectorWall.js +496 -0
- package/dist/frqtl/parts/FrqtlControlOverlay.js +227 -0
- package/dist/frqtl/parts/FrqtlControlsPanel.js +415 -0
- package/dist/frqtl/parts/FrqtlVision.js +818 -0
- package/dist/frqtl/parts/GraphingDetectorWall.js +489 -0
- package/dist/frqtl/parts/LargeObject.js +405 -0
- package/dist/frqtl/parts/LightColor.js +255 -0
- package/dist/frqtl/parts/LiveChart.js +285 -0
- package/dist/frqtl/parts/LogicGate.js +717 -0
- package/dist/frqtl/parts/OvenBox.js +611 -0
- package/dist/frqtl/parts/PointSource.js +762 -0
- package/dist/frqtl/parts/Rectangle.js +200 -0
- package/dist/frqtl/parts/SlitLightSource.js +234 -0
- package/dist/frqtl/parts/SlitScreen.js +628 -0
- package/dist/frqtl/parts/SphereEmitter.js +946 -0
- package/dist/frqtl/parts/Trace.js +508 -0
- package/dist/frqtl/parts/Tunnel.js +1057 -0
- package/dist/frqtl/parts/Wall.js +1211 -0
- package/dist/frqtl/parts/index.js +33 -0
- package/dist/frqtl/scenes/absorbing-wall-3d.js +33 -0
- package/dist/frqtl/scenes/absorbing-wall.js +32 -0
- package/dist/frqtl/scenes/atomic-model.js +88 -0
- package/dist/frqtl/scenes/big-bang.js +46 -0
- package/dist/frqtl/scenes/blackbody.js +81 -0
- package/dist/frqtl/scenes/circuit-sat.js +127 -0
- package/dist/frqtl/scenes/collision-matrix-3d.js +107 -0
- package/dist/frqtl/scenes/counting-wall-3d.js +36 -0
- package/dist/frqtl/scenes/counting-wall.js +38 -0
- package/dist/frqtl/scenes/double-slit.js +31 -0
- package/dist/frqtl/scenes/dse-one-emitter.js +196 -0
- package/dist/frqtl/scenes/dse-one-slit.js +114 -0
- package/dist/frqtl/scenes/dse-photoelectric.js +70 -0
- package/dist/frqtl/scenes/dse-tunnel-rebuild.js +243 -0
- package/dist/frqtl/scenes/dse-two-emitters.js +196 -0
- package/dist/frqtl/scenes/dse-two-slits.js +132 -0
- package/dist/frqtl/scenes/empty.js +14 -0
- package/dist/frqtl/scenes/laser-buildup.js +49 -0
- package/dist/frqtl/scenes/mirror-3d.js +56 -0
- package/dist/frqtl/scenes/mirror-route.js +140 -0
- package/dist/frqtl/scenes/mirror.js +38 -0
- package/dist/frqtl/scenes/omni-3d.js +27 -0
- package/dist/frqtl/scenes/omni-source.js +22 -0
- package/dist/frqtl/scenes/self-interference.js +39 -0
- package/dist/frqtl/scenes/smoking-gun.js +122 -0
- package/dist/frqtl/scenes/straight-walk.js +22 -0
- package/dist/frqtl/scenes/sun.js +39 -0
- package/dist/frqtl/scenes/two-source.js +37 -0
- package/dist/frqtl/scenes/wave.js +26 -0
- package/dist/shim/bundle/frqnb.js +112 -0
- package/dist/shim/bundle/index.js +121 -0
- package/dist/shim/bundle/indexHtml.js +107 -0
- package/dist/shim/bundle/manifest.js +95 -0
- package/dist/shim/bundle/zip.js +243 -0
- package/dist/shim/bus.js +67 -0
- package/dist/shim/controls/index.js +627 -0
- package/dist/shim/controls/registry.js +274 -0
- package/dist/shim/engine/artifact-shell.js +211 -0
- package/dist/shim/engine/boot-phases.js +44 -0
- package/dist/shim/engine/engine-stack-boot.js +59 -0
- package/dist/shim/engine/router.js +690 -0
- package/dist/shim/experiment-frame/index.js +296 -0
- package/dist/shim/expr/ast-schema.js +369 -0
- package/dist/shim/field-descriptor/index.js +325 -0
- package/dist/shim/frame-data/index.js +224 -0
- package/dist/shim/index.js +67 -0
- package/dist/shim/notebook/canvas-guard.js +82 -0
- package/dist/shim/notebook/catalog.js +35 -0
- package/dist/shim/notebook/format.js +736 -0
- package/dist/shim/notebook/frqtl-setups-importmap.js +81 -0
- package/dist/shim/notebook/kernel.js +671 -0
- package/dist/shim/notebook/knob.js +150 -0
- package/dist/shim/notebook/module-resolver.js +260 -0
- package/dist/shim/notebook/notebook-knobs.js +65 -0
- package/dist/shim/notebook/produced-bindings.js +67 -0
- package/dist/shim/notebook/realm-registry.js +121 -0
- package/dist/shim/notebook/realm-transport.js +73 -0
- package/dist/shim/notebook/setup-builder.js +507 -0
- package/dist/shim/notebook/store.js +159 -0
- package/dist/shim/notebook/vendor-importmap.js +134 -0
- package/dist/shim/notebook/view-builder.js +189 -0
- package/dist/shim/notebook/view-ctx.js +100 -0
- package/dist/shim/notebook/view-handle.js +202 -0
- package/dist/shim/notebook/warmup.js +102 -0
- package/dist/shim/package.json +4 -0
- package/dist/shim/page-params/index.js +213 -0
- package/dist/shim/project/identity.js +112 -0
- package/dist/shim/project/index.js +19 -0
- package/dist/shim/project/link.js +402 -0
- package/dist/shim/project/node-specifier-hook.mjs +35 -0
- package/dist/shim/project/register-node.mjs +17 -0
- package/dist/shim/project/specifiers.js +106 -0
- package/dist/shim/registry/copy-gate.js +110 -0
- package/dist/shim/registry/exhibits.js +455 -0
- package/dist/shim/registry/experiments.js +215 -0
- package/dist/shim/registry/index.js +19 -0
- package/dist/shim/registry/scene-source.js +157 -0
- package/dist/shim/registry/walkthrough.js +804 -0
- package/dist/shim/render/capture-envelope.js +279 -0
- package/dist/shim/render/heat-colormap.js +92 -0
- package/dist/shim/render/render-stages.js +855 -0
- package/dist/shim/render/sun-look.js +33 -0
- package/dist/shim/render-state/apply.js +262 -0
- package/dist/shim/render-state/index.js +74 -0
- package/dist/shim/rpc/index.js +207 -0
- package/dist/shim/runtime/index.js +1023 -0
- package/dist/shim/storage/boot.js +69 -0
- package/dist/shim/storage/index.js +169 -0
- package/dist/shim/storage/local-storage-provider.js +230 -0
- package/dist/shim/storage/make-namespace-store.js +132 -0
- package/dist/shim/storage/modules.js +249 -0
- package/dist/shim/storage/projects.js +150 -0
- package/llms.txt +12 -0
- package/package.json +63 -4
|
@@ -0,0 +1,671 @@
|
|
|
1
|
+
/* Copyright 2023-2026 by Essam Abadir */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @typedef {'idle'|'running'|'ok'|'error'|'stale'} CellStatus
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @typedef {object} CellState
|
|
9
|
+
* @property {CellStatus} status — current execution status.
|
|
10
|
+
* @property {Object<string,number>} lastRunInputVersions — binding versions at last run.
|
|
11
|
+
* @property {Object<string,*>} outputs — the last committed output values.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @typedef {object} BindingEntry
|
|
16
|
+
* @property {*} value — the binding value.
|
|
17
|
+
* @property {number} version — increments on each new commit from any producer.
|
|
18
|
+
* @property {string} producerCellId — id of the cell that last wrote this binding.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @typedef {object} RunAllResult
|
|
23
|
+
* @property {number} total — total runnable cells.
|
|
24
|
+
* @property {number} ran — cells that were attempted.
|
|
25
|
+
* @property {boolean} ok — `true` if all ran successfully; `false` on first error.
|
|
26
|
+
* @property {string|null} failedCellId — the first cell that failed, or `null`.
|
|
27
|
+
* @property {string|null} error — error message of the first failure, or `null`.
|
|
28
|
+
* @property {Array<{cellId:string, result:RunCellResult}>} results — per-cell results.
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* NotebookKernel — per-notebook execution controller (UNIFIED_NOTEBOOK_DESIGN §3).
|
|
33
|
+
*
|
|
34
|
+
* A PER-NOTEBOOK INSTANCE (NOT a module singleton). The pane owns the kernel's
|
|
35
|
+
* lifetime and calls `dispose()` on notebook unload.
|
|
36
|
+
*
|
|
37
|
+
* Responsibilities:
|
|
38
|
+
* - Typed binding store keyed by name (`{ value, version, producerCellId }`).
|
|
39
|
+
* - Per-cell execution state (`idle | running | ok | error | stale`).
|
|
40
|
+
* - Dependency graph (declared `in`/`out`) used ONLY for staleness + run-order
|
|
41
|
+
* hints — it NEVER auto-runs (marks `stale` only).
|
|
42
|
+
*
|
|
43
|
+
* Execution discipline:
|
|
44
|
+
* - `runCell()` calls `Runtime.runCell()` DIRECTLY. The kernel registers ZERO
|
|
45
|
+
* `Bus.on` execution listeners. The ONLY Bus traffic it produces is ONE outbound
|
|
46
|
+
* `notebook:cellstate { cellId, status }` event per state transition.
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* import { parse } from 'lib/shim/notebook/format.js';
|
|
50
|
+
* import { NotebookKernel } from 'lib/shim/notebook/kernel.js';
|
|
51
|
+
*
|
|
52
|
+
* const nb = parse(htmlSource);
|
|
53
|
+
* const kernel = new NotebookKernel(nb);
|
|
54
|
+
*
|
|
55
|
+
* // Run a single cell (FAIL-LOUD if a declared input is not yet produced):
|
|
56
|
+
* const result = await kernel.runCell('c-stat-01');
|
|
57
|
+
*
|
|
58
|
+
* // Run all cells in topological order:
|
|
59
|
+
* const summary = await kernel.runAll();
|
|
60
|
+
*
|
|
61
|
+
* // Tear down when the notebook closes:
|
|
62
|
+
* kernel.dispose();
|
|
63
|
+
*/
|
|
64
|
+
|
|
65
|
+
import { Bus as DefaultBus } from '../bus.js';
|
|
66
|
+
import { Runtime as DefaultRuntime } from '../runtime/index.js';
|
|
67
|
+
|
|
68
|
+
'use strict';
|
|
69
|
+
|
|
70
|
+
var STATUS = {
|
|
71
|
+
IDLE: 'idle',
|
|
72
|
+
RUNNING: 'running',
|
|
73
|
+
OK: 'ok',
|
|
74
|
+
ERROR: 'error',
|
|
75
|
+
STALE: 'stale'
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
export class NotebookKernel {
|
|
79
|
+
/**
|
|
80
|
+
* @param {object} notebook — parsed notebook from `format.parse()`. Must have
|
|
81
|
+
* `cells: [{ id, type, lane, in, out, body }]`.
|
|
82
|
+
* @param {object} [opts]
|
|
83
|
+
* @param {object} [opts.runtime] — shim Runtime (injected for testability).
|
|
84
|
+
* @param {object} [opts.bus] — shim Bus (injected for testability).
|
|
85
|
+
*/
|
|
86
|
+
constructor(notebook, opts) {
|
|
87
|
+
opts = opts || {};
|
|
88
|
+
if (!notebook || !Array.isArray(notebook.cells)) {
|
|
89
|
+
throw new Error('[NotebookKernel] requires a notebook object with a cells[] array.');
|
|
90
|
+
}
|
|
91
|
+
this._runtime = opts.runtime || DefaultRuntime;
|
|
92
|
+
this._bus = opts.bus || DefaultBus;
|
|
93
|
+
// The injected runtime's contract, asserted HERE — at the boundary, once.
|
|
94
|
+
// `resetMathScope` is what bounds the arena (see resetScope() below); a
|
|
95
|
+
// runtime without it would otherwise die with a bare TypeError from inside
|
|
96
|
+
// resetScope(), mid-run, AFTER the binding store was already cleared. This
|
|
97
|
+
// is an assertion, NOT a guard around the call: skipping the reset would
|
|
98
|
+
// run the notebook on an unbounded arena and never say so.
|
|
99
|
+
var missing = ['runCell', 'resetMathScope'].filter(function (m) {
|
|
100
|
+
return typeof this._runtime[m] !== 'function';
|
|
101
|
+
}, this);
|
|
102
|
+
if (missing.length) {
|
|
103
|
+
throw new Error(
|
|
104
|
+
'[NotebookKernel] the injected runtime does not satisfy the kernel\'s contract — ' +
|
|
105
|
+
'missing: ' + missing.join(', ') + '. Pass lib/shim/runtime/index.js\'s `Runtime` ' +
|
|
106
|
+
'(Runtime.runCell, Runtime.resetMathScope), or a stub that implements both.'
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
this._disposed = false;
|
|
110
|
+
|
|
111
|
+
// notebook cells in document order; keep an id→cell index.
|
|
112
|
+
this._cells = notebook.cells.slice();
|
|
113
|
+
this._cellById = new Map();
|
|
114
|
+
for (var i = 0; i < this._cells.length; i++) {
|
|
115
|
+
var c = this._cells[i];
|
|
116
|
+
if (c.id) { this._cellById.set(c.id, c); }
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// bindings: Map<name, { value, kind?, version, producerCellId }>
|
|
120
|
+
this.bindings = new Map();
|
|
121
|
+
|
|
122
|
+
// cellState: Map<cellId, { status, lastRunInputVersions, outputs }>
|
|
123
|
+
this.cellState = new Map();
|
|
124
|
+
for (var j = 0; j < this._cells.length; j++) {
|
|
125
|
+
var cell = this._cells[j];
|
|
126
|
+
if (!cell.id) { continue; }
|
|
127
|
+
this.cellState.set(cell.id, {
|
|
128
|
+
status: STATUS.IDLE,
|
|
129
|
+
lastRunInputVersions: {},
|
|
130
|
+
outputs: {}
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// dependency graph: producerName → Set<consumerCellId>. Derived from
|
|
135
|
+
// declared in/out. Used ONLY for staleness propagation + run hints.
|
|
136
|
+
this._consumersOfName = new Map();
|
|
137
|
+
for (var k = 0; k < this._cells.length; k++) {
|
|
138
|
+
var cc = this._cells[k];
|
|
139
|
+
if (!cc.id || !Array.isArray(cc.in)) { continue; }
|
|
140
|
+
for (var n = 0; n < cc.in.length; n++) {
|
|
141
|
+
var name = cc.in[n];
|
|
142
|
+
if (!this._consumersOfName.has(name)) {
|
|
143
|
+
this._consumersOfName.set(name, new Set());
|
|
144
|
+
}
|
|
145
|
+
this._consumersOfName.get(name).add(cc.id);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/* ── scope ──────────────────────────────────────────────────────────────
|
|
151
|
+
* THE SCOPE RULE (FRAQTL issue #67): a math value's scope IS this kernel's
|
|
152
|
+
* binding scope. On the shipped WASM dist an EGPT value is an arena handle,
|
|
153
|
+
* so if the arena is drained while a binding still names one, that binding is
|
|
154
|
+
* a dead handle and the next cell dies with `arena:stale-handle`. The two
|
|
155
|
+
* facts therefore move TOGETHER, here, in ONE method — they cannot drift.
|
|
156
|
+
* A cell is not a run: `Runtime.runCell()` deliberately does not reset. */
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Begin a fresh value scope: drop every binding AND end the math backend's
|
|
160
|
+
* value scope (arena drain + epoch bump for `wasm-math`, a no-op for
|
|
161
|
+
* `js-reference`).
|
|
162
|
+
*
|
|
163
|
+
* FAIL-LOUD is preserved, not softened: after this call a handle captured
|
|
164
|
+
* before it — in a closure, a chart, a host-held output — is genuinely stale
|
|
165
|
+
* and still throws `arena:stale-handle`. That is the point of bumping the
|
|
166
|
+
* epoch rather than silently reusing slots.
|
|
167
|
+
*
|
|
168
|
+
* Called at the START of `runAll()` (every binding is about to be re-produced
|
|
169
|
+
* by the run itself) and by `dispose()` (notebook unload). A single-cell run
|
|
170
|
+
* does NOT call it — its output has to outlive the cell.
|
|
171
|
+
*
|
|
172
|
+
* WHAT BOUNDS THE ARENA, and the residual this costs. N consecutive Run Alls
|
|
173
|
+
* cost ONE run's residency, not N — each begins a fresh scope (measured:
|
|
174
|
+
* identical `arenaStats().len` across runs 1/2/3 for every shipped notebook;
|
|
175
|
+
* worst case `egpt-test-suite`, 194,370 slots), and unload drains it to 0.
|
|
176
|
+
* BUT repeated MANUAL single-cell runs ACCUMULATE until the next `runAll()` or
|
|
177
|
+
* `dispose()`: the arena has no reclaim finer than the scope, so every
|
|
178
|
+
* INTERMEDIATE of the re-run cell stays resident too, not just its output
|
|
179
|
+
* (measured on a 1000-add cell: 1,002 slots per re-run — 999 dead accumulators
|
|
180
|
+
* and the one live result; 20 re-runs → 21,042, then back to 1,002 at the next
|
|
181
|
+
* Run All). This is NOT a REPL's heap property — a Jupyter kernel retains
|
|
182
|
+
* bindings and collects unreferenced intermediates. Fixing it needs a two-level
|
|
183
|
+
* scope in the arena (a per-cell frame whose intermediates die at cell end, with
|
|
184
|
+
* the cell's declared outputs PROMOTED into the notebook scope) — a retain
|
|
185
|
+
* contract that belongs in `arena.rs` + the facade, never faked on the JS side.
|
|
186
|
+
*
|
|
187
|
+
* @returns {void}
|
|
188
|
+
*/
|
|
189
|
+
resetScope() {
|
|
190
|
+
this.bindings.clear();
|
|
191
|
+
this._runtime.resetMathScope();
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/* ── module set (B2) ─────────────────────────────────────────────────────
|
|
195
|
+
* { name → source } over the notebook's `type="module"` cells (the user-code
|
|
196
|
+
* module SET the cell-as-module resolver builds its in-memory blob closure
|
|
197
|
+
* from when a `js` builder cell imports a notebook-local module). The source is
|
|
198
|
+
* read LIVE from cell.body each call so a just-edited module cell is current
|
|
199
|
+
* (never a cached, stale copy). The pane syncs an open module-cell editor's
|
|
200
|
+
* value back into cell.body before a run (mirroring the js-cell sync), so this
|
|
201
|
+
* reflects author edits. */
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Returns the notebook's in-memory user-code module set.
|
|
205
|
+
*
|
|
206
|
+
* Keys are the `name` attribute values of `type="module"` cells
|
|
207
|
+
* (e.g. `'./setups/wave.js'`); values are the current cell body source.
|
|
208
|
+
* Read LIVE so a just-edited module cell is always current.
|
|
209
|
+
*
|
|
210
|
+
* @returns {Object<string,string>} `{ name → source }` map.
|
|
211
|
+
*/
|
|
212
|
+
moduleSet() {
|
|
213
|
+
var set = {};
|
|
214
|
+
for (var i = 0; i < this._cells.length; i++) {
|
|
215
|
+
var c = this._cells[i];
|
|
216
|
+
if (c.type === 'module' && c.name) {
|
|
217
|
+
set[c.name] = (c.body == null) ? '' : String(c.body);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return set;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/* ── state emit ─────────────────────────────────────────────────────── */
|
|
224
|
+
|
|
225
|
+
// Set a cell's status and emit the single outbound badge event. NEVER
|
|
226
|
+
// subscribes back. NEVER triggers a run.
|
|
227
|
+
_setStatus(cellId, status) {
|
|
228
|
+
if (this._disposed) { return; }
|
|
229
|
+
var st = this.cellState.get(cellId);
|
|
230
|
+
if (!st) { return; }
|
|
231
|
+
st.status = status;
|
|
232
|
+
this._bus.emit('notebook:cellstate', { cellId: cellId, status: status });
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/* ── run one cell ───────────────────────────────────────────────────── */
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Run a single cell by id.
|
|
239
|
+
*
|
|
240
|
+
* 1. Resolve declared inputs from the binding store. A missing input →
|
|
241
|
+
* FAIL-LOUD reject naming the producing cell (upstream is NOT auto-run).
|
|
242
|
+
* 2. Call `Runtime.runCell()` DIRECTLY (a Promise — no Bus round-trip).
|
|
243
|
+
* 3. On `ok`: commit each declared output into bindings (`++version`); mark
|
|
244
|
+
* every consumer of a just-updated name `stale` (NOT executed).
|
|
245
|
+
* 4. Emit `notebook:cellstate { cellId, status }` once per state transition.
|
|
246
|
+
*
|
|
247
|
+
* @param {string} cellId
|
|
248
|
+
* @param {object} [opts]
|
|
249
|
+
* @param {Function} [opts.display]
|
|
250
|
+
* Structured-output sink. The pane passes a DOM-aware display so the cell's
|
|
251
|
+
* output renders in its own region rather than the shared console pane.
|
|
252
|
+
* @returns {Promise<RunCellResult>}
|
|
253
|
+
*
|
|
254
|
+
* @example
|
|
255
|
+
* // Run cell 'c-build-01', which produces a live runner handle:
|
|
256
|
+
* const { ok, outputs } = await kernel.runCell('c-build-01');
|
|
257
|
+
* if (ok) console.log(outputs.dsRun); // the live FrqtlApp
|
|
258
|
+
*/
|
|
259
|
+
runCell(cellId, opts) {
|
|
260
|
+
opts = opts || {};
|
|
261
|
+
if (this._disposed) {
|
|
262
|
+
return Promise.reject(new Error('[NotebookKernel] disposed; cannot run cell "' + cellId + '".'));
|
|
263
|
+
}
|
|
264
|
+
var cell = this._cellById.get(cellId);
|
|
265
|
+
if (!cell) {
|
|
266
|
+
return Promise.reject(new Error('[NotebookKernel] no cell with id "' + cellId + '".'));
|
|
267
|
+
}
|
|
268
|
+
var self = this;
|
|
269
|
+
|
|
270
|
+
// 1. Resolve inputs — FAIL-LOUD on a missing binding, naming the producer.
|
|
271
|
+
var inputs = {};
|
|
272
|
+
var declaredIn = Array.isArray(cell.in) ? cell.in : [];
|
|
273
|
+
var lastRunInputVersions = {};
|
|
274
|
+
for (var i = 0; i < declaredIn.length; i++) {
|
|
275
|
+
var name = declaredIn[i];
|
|
276
|
+
if (!self.bindings.has(name)) {
|
|
277
|
+
var producer = self._findProducerCellId(name);
|
|
278
|
+
var hint = producer
|
|
279
|
+
? 'run cell ' + producer + ' first.'
|
|
280
|
+
: 'no cell in this notebook declares it as an output.';
|
|
281
|
+
var err = new Error(
|
|
282
|
+
'[NotebookKernel] cell "' + cellId + '" declares input "' + name +
|
|
283
|
+
'", not yet produced — ' + hint + ' (FAIL-LOUD; upstream is NOT auto-run.)'
|
|
284
|
+
);
|
|
285
|
+
self._setStatus(cellId, STATUS.ERROR);
|
|
286
|
+
var st0 = self.cellState.get(cellId);
|
|
287
|
+
if (st0) { st0.outputs = {}; }
|
|
288
|
+
return Promise.reject(err);
|
|
289
|
+
}
|
|
290
|
+
var binding = self.bindings.get(name);
|
|
291
|
+
inputs[name] = binding.value;
|
|
292
|
+
lastRunInputVersions[name] = binding.version;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// 2. Execute via Runtime.runCell DIRECTLY (no Bus round-trip).
|
|
296
|
+
self._setStatus(cellId, STATUS.RUNNING);
|
|
297
|
+
return self._runtime.runCell({
|
|
298
|
+
lane: cell.lane || 'math',
|
|
299
|
+
code: cell.body || '',
|
|
300
|
+
id: cellId,
|
|
301
|
+
inputs: inputs,
|
|
302
|
+
// B2: the notebook's user-code module set so a builder cell's
|
|
303
|
+
// `import './setups/x.js'` resolves to the in-memory blob closure.
|
|
304
|
+
modules: self.moduleSet(),
|
|
305
|
+
declaredOutputs: Array.isArray(cell.out) ? cell.out : [],
|
|
306
|
+
// Optional per-cell structured-output sink the pane passes (a DOM-aware
|
|
307
|
+
// display that appends nodes/lines to the cell's output region). The
|
|
308
|
+
// kernel stays display-agnostic — it only forwards the caller's fn.
|
|
309
|
+
display: opts.display || null
|
|
310
|
+
}).then(function (result) {
|
|
311
|
+
if (self._disposed) { return result; }
|
|
312
|
+
var st = self.cellState.get(cellId);
|
|
313
|
+
if (result && result.ok) {
|
|
314
|
+
// 3. Commit outputs (++version), mark consumers stale.
|
|
315
|
+
var declaredOut = Array.isArray(cell.out) ? cell.out : [];
|
|
316
|
+
for (var o = 0; o < declaredOut.length; o++) {
|
|
317
|
+
var outName = declaredOut[o];
|
|
318
|
+
var prev = self.bindings.get(outName);
|
|
319
|
+
var nextVersion = prev ? (prev.version + 1) : 1;
|
|
320
|
+
self.bindings.set(outName, {
|
|
321
|
+
value: result.outputs[outName],
|
|
322
|
+
version: nextVersion,
|
|
323
|
+
producerCellId: cellId
|
|
324
|
+
});
|
|
325
|
+
self._markConsumersStale(outName, cellId);
|
|
326
|
+
}
|
|
327
|
+
if (st) {
|
|
328
|
+
st.outputs = result.outputs || {};
|
|
329
|
+
st.lastRunInputVersions = lastRunInputVersions;
|
|
330
|
+
}
|
|
331
|
+
self._setStatus(cellId, STATUS.OK);
|
|
332
|
+
} else {
|
|
333
|
+
if (st) { st.outputs = {}; }
|
|
334
|
+
self._setStatus(cellId, STATUS.ERROR);
|
|
335
|
+
}
|
|
336
|
+
return result;
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Commit outputs for a view cell (canvas / chart) that does NOT execute through
|
|
342
|
+
* `Runtime.runCell`. The pane calls this when the live runner handle is available
|
|
343
|
+
* (e.g. after a canvas realm boots) so downstream `js` cells can consume it.
|
|
344
|
+
*
|
|
345
|
+
* Mirrors steps 3–4 of `runCell()` (commit `++version`, mark consumers `stale`,
|
|
346
|
+
* set the producing cell `ok`) — one binding-commit authority (DRY).
|
|
347
|
+
* Adds ZERO Bus listeners; never calls Runtime.
|
|
348
|
+
*
|
|
349
|
+
* @param {string} cellId
|
|
350
|
+
* @param {Object<string,*>} outputsObj — `{ name: value, … }` the view cell produces.
|
|
351
|
+
* @returns {void}
|
|
352
|
+
*/
|
|
353
|
+
commitViewOutputs(cellId, outputsObj) {
|
|
354
|
+
if (this._disposed) { return; }
|
|
355
|
+
var st = this.cellState.get(cellId);
|
|
356
|
+
var bag = (outputsObj && typeof outputsObj === 'object') ? outputsObj : {};
|
|
357
|
+
var keys = Object.keys(bag);
|
|
358
|
+
for (var i = 0; i < keys.length; i++) {
|
|
359
|
+
var name = keys[i];
|
|
360
|
+
var prev = this.bindings.get(name);
|
|
361
|
+
var nextVersion = prev ? (prev.version + 1) : 1;
|
|
362
|
+
this.bindings.set(name, {
|
|
363
|
+
value: bag[name],
|
|
364
|
+
version: nextVersion,
|
|
365
|
+
producerCellId: cellId
|
|
366
|
+
});
|
|
367
|
+
this._markConsumersStale(name, cellId);
|
|
368
|
+
}
|
|
369
|
+
if (st) { st.outputs = bag; }
|
|
370
|
+
this._setStatus(cellId, STATUS.OK);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Returns the runnable cells (`js`/`canvas`/`chart`/`view`, not `markdown`)
|
|
375
|
+
* in the order `runAll()` should execute them.
|
|
376
|
+
*
|
|
377
|
+
* A producer of a name runs BEFORE any consumer. Ties and un-declared cells
|
|
378
|
+
* keep document order. A dependency cycle → FAIL-LOUD throw (no safe order).
|
|
379
|
+
* NEVER auto-runs; NEVER mutates bindings.
|
|
380
|
+
*
|
|
381
|
+
* FRQTL-MAINT(runall-excludes-chart-cells) — FIXED. The RUNNABLE map used to be `{js, canvas}` only —
|
|
382
|
+
* `runAll()` reported `total:N ran:N` while silently never running `chart`/
|
|
383
|
+
* `view` cells (`double-slit-rosetta-qft.html`),
|
|
384
|
+
* even though a manual per-cell Run on the same cell works (it routes through
|
|
385
|
+
* the pane's realm-cell runner, `opts.runner`, not `runCell`). `chart`/`view`
|
|
386
|
+
* cells DO declare `in`/`out` bindings today (`_resolveChartRecipe` FAIL-LOUD
|
|
387
|
+
* -throws on an unproduced `in`; `commitViewOutputs` commits a view cell's
|
|
388
|
+
* bound outputs) — so including them here is not just "run one more cell",
|
|
389
|
+
* it lets the SAME topological sort correctly order a chart/view cell AFTER
|
|
390
|
+
* whichever cell produces the binding it reads, exactly like a `js` consumer.
|
|
391
|
+
* The pane's `opts.runner` (lib/components/notebook-pane/index.js `runAll()`)
|
|
392
|
+
* already special-cased `chart` alongside `canvas` — it was only ever missing
|
|
393
|
+
* `view`, fixed in the same change.
|
|
394
|
+
*
|
|
395
|
+
* @returns {Array<object>} ordered cell objects (each has `{ id, type, lane, body, in, out }`).
|
|
396
|
+
* @throws {Error} if a cycle is detected in the declared `in`/`out` graph.
|
|
397
|
+
*/
|
|
398
|
+
runOrder() {
|
|
399
|
+
var RUNNABLE = { js: true, canvas: true, chart: true, view: true };
|
|
400
|
+
// The runnable cells in document order; markdown (and unknown types that
|
|
401
|
+
// never reach Runtime.runCell) are excluded — they are not "run".
|
|
402
|
+
var ordered = [];
|
|
403
|
+
for (var i = 0; i < this._cells.length; i++) {
|
|
404
|
+
var c = this._cells[i];
|
|
405
|
+
if (c.id && RUNNABLE[c.type]) { ordered.push(c); }
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// Build edges producer→consumer over the runnable set, keyed by cell id.
|
|
409
|
+
// docIndex gives a stable document-order tiebreak in the ready queue.
|
|
410
|
+
var docIndex = new Map();
|
|
411
|
+
var indegree = new Map();
|
|
412
|
+
var edgesFrom = new Map(); // producerId → Set<consumerId>
|
|
413
|
+
for (var d = 0; d < ordered.length; d++) {
|
|
414
|
+
docIndex.set(ordered[d].id, d);
|
|
415
|
+
indegree.set(ordered[d].id, 0);
|
|
416
|
+
edgesFrom.set(ordered[d].id, new Set());
|
|
417
|
+
}
|
|
418
|
+
for (var e = 0; e < ordered.length; e++) {
|
|
419
|
+
var consumer = ordered[e];
|
|
420
|
+
var declaredIn = Array.isArray(consumer.in) ? consumer.in : [];
|
|
421
|
+
for (var n = 0; n < declaredIn.length; n++) {
|
|
422
|
+
var producerId = this._findProducerCellId(declaredIn[n]);
|
|
423
|
+
// Only count an edge when the producer is itself a runnable cell in
|
|
424
|
+
// THIS set and is not the consumer itself (a cell that declares the
|
|
425
|
+
// same name in+out is a self-edge — ignore it, not a real dep).
|
|
426
|
+
if (producerId && producerId !== consumer.id && indegree.has(producerId)) {
|
|
427
|
+
var fanout = edgesFrom.get(producerId);
|
|
428
|
+
if (!fanout.has(consumer.id)) {
|
|
429
|
+
fanout.add(consumer.id);
|
|
430
|
+
indegree.set(consumer.id, indegree.get(consumer.id) + 1);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// Kahn's algorithm, ready queue kept in document order (smallest docIndex
|
|
437
|
+
// first) so the output is stable and == document order with no deps.
|
|
438
|
+
var ready = [];
|
|
439
|
+
for (var r = 0; r < ordered.length; r++) {
|
|
440
|
+
if (indegree.get(ordered[r].id) === 0) { ready.push(ordered[r].id); }
|
|
441
|
+
}
|
|
442
|
+
var result = [];
|
|
443
|
+
var byId = this._cellById;
|
|
444
|
+
while (ready.length > 0) {
|
|
445
|
+
ready.sort(function (a, b) { return docIndex.get(a) - docIndex.get(b); });
|
|
446
|
+
var id = ready.shift();
|
|
447
|
+
result.push(byId.get(id));
|
|
448
|
+
edgesFrom.get(id).forEach(function (consumerId) {
|
|
449
|
+
indegree.set(consumerId, indegree.get(consumerId) - 1);
|
|
450
|
+
if (indegree.get(consumerId) === 0) { ready.push(consumerId); }
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
if (result.length !== ordered.length) {
|
|
455
|
+
throw new Error(
|
|
456
|
+
'[NotebookKernel] runAll cannot order cells: a dependency CYCLE exists ' +
|
|
457
|
+
'among declared in/out bindings. (FAIL-LOUD — no safe linear run order.)'
|
|
458
|
+
);
|
|
459
|
+
}
|
|
460
|
+
return result;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
/**
|
|
464
|
+
* Run all runnable cells in topological order (sequential — one at a time).
|
|
465
|
+
*
|
|
466
|
+
* A pure SEQUENCER over `runCell()`. Registers ZERO Bus listeners, adds ZERO
|
|
467
|
+
* executors. Emits one `notebook:runall { index, total, cellId, phase }` per step.
|
|
468
|
+
*
|
|
469
|
+
* FAIL-LOUD stop-on-error: the FIRST cell that fails STOPS the run;
|
|
470
|
+
* `runAll` resolves (does not throw) with `{ ok: false, failedCellId }`.
|
|
471
|
+
*
|
|
472
|
+
* @param {object} [opts]
|
|
473
|
+
* @param {Function} [opts.displayFor]
|
|
474
|
+
* `(cellId: string) → displayFn`. The pane supplies this so each cell's
|
|
475
|
+
* structured output routes to its own region.
|
|
476
|
+
* @param {Function} [opts.onProgress]
|
|
477
|
+
* `({ index, total, cellId, phase }) → void`. Progress callback in addition
|
|
478
|
+
* to the Bus event.
|
|
479
|
+
* @param {Function} [opts.runner]
|
|
480
|
+
* `(cell) → Promise<{ ok, error }> | null`. Pane-supplied runner for cell
|
|
481
|
+
* types the kernel cannot execute via `Runtime.runCell` (e.g. `canvas` cells,
|
|
482
|
+
* which the pane mounts as a live GraphicsPane). When it returns a thenable,
|
|
483
|
+
* that result is used INSTEAD of `runCell` for that cell.
|
|
484
|
+
* @returns {Promise<RunAllResult>}
|
|
485
|
+
*
|
|
486
|
+
* @example
|
|
487
|
+
* const summary = await kernel.runAll({
|
|
488
|
+
* displayFor: (id) => pane.displayFor(id),
|
|
489
|
+
* onProgress: ({ index, total, cellId }) =>
|
|
490
|
+
* console.log(`Running ${index}/${total}: ${cellId}`)
|
|
491
|
+
* });
|
|
492
|
+
* if (!summary.ok) {
|
|
493
|
+
* console.error('Failed at cell:', summary.failedCellId, summary.error);
|
|
494
|
+
* }
|
|
495
|
+
*/
|
|
496
|
+
runAll(opts) {
|
|
497
|
+
opts = opts || {};
|
|
498
|
+
if (this._disposed) {
|
|
499
|
+
return Promise.reject(new Error('[NotebookKernel] disposed; cannot runAll.'));
|
|
500
|
+
}
|
|
501
|
+
var self = this;
|
|
502
|
+
var order;
|
|
503
|
+
try {
|
|
504
|
+
order = self.runOrder();
|
|
505
|
+
} catch (orderErr) {
|
|
506
|
+
return Promise.reject(orderErr);
|
|
507
|
+
}
|
|
508
|
+
// A fresh value scope for the run (#67). Safe by construction: runAll
|
|
509
|
+
// re-produces every binding a runnable cell declares, in an order that
|
|
510
|
+
// puts each producer before its consumers — so nothing in the cleared
|
|
511
|
+
// store is read before it is rewritten. A leftover from an earlier,
|
|
512
|
+
// partial run is exactly what must NOT silently satisfy an input.
|
|
513
|
+
self.resetScope();
|
|
514
|
+
var total = order.length;
|
|
515
|
+
var displayFor = (typeof opts.displayFor === 'function') ? opts.displayFor : null;
|
|
516
|
+
var onProgress = (typeof opts.onProgress === 'function') ? opts.onProgress : null;
|
|
517
|
+
var runner = (typeof opts.runner === 'function') ? opts.runner : null;
|
|
518
|
+
var results = [];
|
|
519
|
+
|
|
520
|
+
function emitProgress(index, cellId, phase) {
|
|
521
|
+
var payload = { index: index, total: total, cellId: cellId, phase: phase };
|
|
522
|
+
if (!self._disposed) { self._bus.emit('notebook:runall', payload); }
|
|
523
|
+
if (onProgress) { onProgress(payload); }
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
// Sequential reduce: await each runCell before the next (chain of .then).
|
|
527
|
+
var chain = Promise.resolve();
|
|
528
|
+
var stopped = { ok: true, failedCellId: null, error: null, ran: 0 };
|
|
529
|
+
|
|
530
|
+
order.forEach(function (cell, idx) {
|
|
531
|
+
chain = chain.then(function () {
|
|
532
|
+
if (self._disposed || !stopped.ok) { return; }
|
|
533
|
+
emitProgress(idx + 1, cell.id, 'running');
|
|
534
|
+
// Pane-supplied runner for cell types the kernel cannot execute via
|
|
535
|
+
// the Runtime runCell path (canvas). If it returns a thenable we use
|
|
536
|
+
// it; otherwise fall through to runCell. NO executor added either way.
|
|
537
|
+
var paneRun = runner ? runner(cell) : null;
|
|
538
|
+
if (paneRun && typeof paneRun.then === 'function') {
|
|
539
|
+
return paneRun.then(function (result) {
|
|
540
|
+
results.push({ cellId: cell.id, result: result });
|
|
541
|
+
stopped.ran += 1;
|
|
542
|
+
if (!result || !result.ok) {
|
|
543
|
+
stopped.ok = false;
|
|
544
|
+
stopped.failedCellId = cell.id;
|
|
545
|
+
stopped.error = (result && result.error)
|
|
546
|
+
? result.error
|
|
547
|
+
: 'cell "' + cell.id + '" did not complete ok.';
|
|
548
|
+
emitProgress(idx + 1, cell.id, 'error');
|
|
549
|
+
} else {
|
|
550
|
+
emitProgress(idx + 1, cell.id, 'ok');
|
|
551
|
+
}
|
|
552
|
+
}, function (err) {
|
|
553
|
+
results.push({ cellId: cell.id, result: { ok: false, error: (err && err.message) ? err.message : String(err) } });
|
|
554
|
+
stopped.ran += 1;
|
|
555
|
+
stopped.ok = false;
|
|
556
|
+
stopped.failedCellId = cell.id;
|
|
557
|
+
stopped.error = (err && err.message) ? err.message : String(err);
|
|
558
|
+
emitProgress(idx + 1, cell.id, 'error');
|
|
559
|
+
});
|
|
560
|
+
}
|
|
561
|
+
var runOpts = displayFor ? { display: displayFor(cell.id) } : {};
|
|
562
|
+
return self.runCell(cell.id, runOpts).then(function (result) {
|
|
563
|
+
results.push({ cellId: cell.id, result: result });
|
|
564
|
+
stopped.ran += 1;
|
|
565
|
+
if (!result || !result.ok) {
|
|
566
|
+
// runCell resolved ok:false (e.g. a missing declared OUTPUT)
|
|
567
|
+
// — STOP here, FAIL-LOUD naming the cell.
|
|
568
|
+
stopped.ok = false;
|
|
569
|
+
stopped.failedCellId = cell.id;
|
|
570
|
+
stopped.error = (result && result.error)
|
|
571
|
+
? result.error
|
|
572
|
+
: 'cell "' + cell.id + '" did not complete ok.';
|
|
573
|
+
emitProgress(idx + 1, cell.id, 'error');
|
|
574
|
+
} else {
|
|
575
|
+
emitProgress(idx + 1, cell.id, 'ok');
|
|
576
|
+
}
|
|
577
|
+
}, function (err) {
|
|
578
|
+
// runCell REJECTED (e.g. a missing declared INPUT, or a thrown
|
|
579
|
+
// body error surfaced as a reject) — STOP here, FAIL-LOUD.
|
|
580
|
+
results.push({ cellId: cell.id, result: { ok: false, error: (err && err.message) ? err.message : String(err) } });
|
|
581
|
+
stopped.ran += 1;
|
|
582
|
+
stopped.ok = false;
|
|
583
|
+
stopped.failedCellId = cell.id;
|
|
584
|
+
stopped.error = (err && err.message) ? err.message : String(err);
|
|
585
|
+
emitProgress(idx + 1, cell.id, 'error');
|
|
586
|
+
});
|
|
587
|
+
});
|
|
588
|
+
});
|
|
589
|
+
|
|
590
|
+
return chain.then(function () {
|
|
591
|
+
return {
|
|
592
|
+
total: total,
|
|
593
|
+
ran: stopped.ran,
|
|
594
|
+
ok: stopped.ok,
|
|
595
|
+
failedCellId: stopped.failedCellId,
|
|
596
|
+
error: stopped.error,
|
|
597
|
+
results: results
|
|
598
|
+
};
|
|
599
|
+
});
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
/* ── staleness (marks only — NEVER auto-runs) ───────────────────────── */
|
|
603
|
+
|
|
604
|
+
// Mark every consumer of `name` stale, except the cell that just produced it.
|
|
605
|
+
_markConsumersStale(name, producerCellId) {
|
|
606
|
+
var consumers = this._consumersOfName.get(name);
|
|
607
|
+
if (!consumers) { return; }
|
|
608
|
+
var self = this;
|
|
609
|
+
consumers.forEach(function (consumerId) {
|
|
610
|
+
if (consumerId === producerCellId) { return; }
|
|
611
|
+
var st = self.cellState.get(consumerId);
|
|
612
|
+
if (!st) { return; }
|
|
613
|
+
// Only a cell that has already run (ok/error) becomes stale; an idle
|
|
614
|
+
// cell stays idle (nothing to invalidate yet).
|
|
615
|
+
if (st.status === STATUS.OK || st.status === STATUS.ERROR) {
|
|
616
|
+
self._setStatus(consumerId, STATUS.STALE);
|
|
617
|
+
}
|
|
618
|
+
});
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
// The first cell (document order) that declares `name` as an output.
|
|
622
|
+
_findProducerCellId(name) {
|
|
623
|
+
for (var i = 0; i < this._cells.length; i++) {
|
|
624
|
+
var c = this._cells[i];
|
|
625
|
+
if (c.id && Array.isArray(c.out) && c.out.indexOf(name) !== -1) {
|
|
626
|
+
return c.id;
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
return null;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
/* ── read accessors ─────────────────────────────────────────────────── */
|
|
633
|
+
|
|
634
|
+
/**
|
|
635
|
+
* @param {string} cellId
|
|
636
|
+
* @returns {CellState|null} current cell state, or `null` if not found.
|
|
637
|
+
*/
|
|
638
|
+
getCellState(cellId) { return this.cellState.get(cellId) || null; }
|
|
639
|
+
|
|
640
|
+
/**
|
|
641
|
+
* @param {string} name — binding name.
|
|
642
|
+
* @returns {BindingEntry|null} the binding entry, or `null` if not yet produced.
|
|
643
|
+
*/
|
|
644
|
+
getBinding(name) { return this.bindings.get(name) || null; }
|
|
645
|
+
|
|
646
|
+
/* ── dispose ────────────────────────────────────────────────────────── */
|
|
647
|
+
|
|
648
|
+
/**
|
|
649
|
+
* Tear down the kernel. The pane calls this on notebook unload.
|
|
650
|
+
*
|
|
651
|
+
* The kernel holds NO Bus listeners to remove (veto #1). `dispose()` marks
|
|
652
|
+
* the instance dead (in-flight `runCell` resolutions become no-ops) and clears
|
|
653
|
+
* all internal maps. Canvas/runner teardown is the pane's responsibility.
|
|
654
|
+
*
|
|
655
|
+
* It also ends the math value scope (`resetScope()`): the bindings that kept
|
|
656
|
+
* those arena values alive are going away with the notebook, so this is what
|
|
657
|
+
* bounds the arena across notebook loads.
|
|
658
|
+
*
|
|
659
|
+
* @returns {void}
|
|
660
|
+
*/
|
|
661
|
+
dispose() {
|
|
662
|
+
this._disposed = true;
|
|
663
|
+
this.resetScope();
|
|
664
|
+
this.cellState.clear();
|
|
665
|
+
this._cellById.clear();
|
|
666
|
+
this._consumersOfName.clear();
|
|
667
|
+
this._cells = [];
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
export { STATUS as NOTEBOOK_CELL_STATUS };
|