flowbook-python 0.1.0__py3-none-any.whl → 0.1.2__py3-none-any.whl
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.
- flowbook/_version.py +1 -1
- flowbook/kernel/flowbook_kernel.py +102 -18
- flowbook/kernel/notebook_state.py +22 -0
- flowbook/kernel/protocol.py +8 -2
- flowbook/kernel/reproducibility_enforcer.py +25 -0
- flowbook/kernel/tests/test_meaningful_edit.py +243 -0
- flowbook/mcp/session.py +35 -11
- {flowbook_python-0.1.0.data → flowbook_python-0.1.2.data}/data/share/jupyter/labextensions/flowbook/package.json +2 -2
- {flowbook_python-0.1.0.data → flowbook_python-0.1.2.data}/data/share/jupyter/labextensions/flowbook/schemas/flowbook/package.json.orig +1 -1
- flowbook_python-0.1.0.data/data/share/jupyter/labextensions/flowbook/static/873.3ca7ae352f965bccc339.js → flowbook_python-0.1.2.data/data/share/jupyter/labextensions/flowbook/static/873.3edec525c5c79ec55cd6.js +1 -1
- flowbook_python-0.1.0.data/data/share/jupyter/labextensions/flowbook/static/remoteEntry.97e370ce33befeb5451b.js → flowbook_python-0.1.2.data/data/share/jupyter/labextensions/flowbook/static/remoteEntry.9c89c62fdbe6db112e2d.js +1 -1
- {flowbook_python-0.1.0.dist-info → flowbook_python-0.1.2.dist-info}/METADATA +5 -2
- {flowbook_python-0.1.0.dist-info → flowbook_python-0.1.2.dist-info}/RECORD +26 -25
- {flowbook_python-0.1.0.dist-info → flowbook_python-0.1.2.dist-info}/WHEEL +1 -1
- {flowbook_python-0.1.0.data → flowbook_python-0.1.2.data}/data/etc/jupyter/jupyter_server_config.d/flowbook.json +0 -0
- {flowbook_python-0.1.0.data → flowbook_python-0.1.2.data}/data/share/jupyter/labextensions/flowbook/install.json +0 -0
- {flowbook_python-0.1.0.data → flowbook_python-0.1.2.data}/data/share/jupyter/labextensions/flowbook/schemas/flowbook/plugin.json +0 -0
- {flowbook_python-0.1.0.data → flowbook_python-0.1.2.data}/data/share/jupyter/labextensions/flowbook/static/728.b1df4bca1a3305d0d0a7.js +0 -0
- {flowbook_python-0.1.0.data → flowbook_python-0.1.2.data}/data/share/jupyter/labextensions/flowbook/static/905.94c2bfb401597cc2a103.js +0 -0
- {flowbook_python-0.1.0.data → flowbook_python-0.1.2.data}/data/share/jupyter/labextensions/flowbook/static/905.94c2bfb401597cc2a103.js.LICENSE.txt +0 -0
- {flowbook_python-0.1.0.data → flowbook_python-0.1.2.data}/data/share/jupyter/labextensions/flowbook/static/951.ba84389925d6a0676e79.js +0 -0
- {flowbook_python-0.1.0.data → flowbook_python-0.1.2.data}/data/share/jupyter/labextensions/flowbook/static/style.js +0 -0
- {flowbook_python-0.1.0.data → flowbook_python-0.1.2.data}/data/share/jupyter/labextensions/flowbook/static/third-party-licenses.json +0 -0
- {flowbook_python-0.1.0.data → flowbook_python-0.1.2.data}/data/share/jupyter/nbi_extensions/flowbook/extension.json +0 -0
- {flowbook_python-0.1.0.dist-info → flowbook_python-0.1.2.dist-info}/entry_points.txt +0 -0
- {flowbook_python-0.1.0.dist-info → flowbook_python-0.1.2.dist-info}/licenses/LICENSE +0 -0
flowbook/_version.py
CHANGED
|
@@ -342,6 +342,7 @@ See checkpoint.py sections 13-14 for implementation details.
|
|
|
342
342
|
================================================================================
|
|
343
343
|
"""
|
|
344
344
|
|
|
345
|
+
import ast
|
|
345
346
|
import os
|
|
346
347
|
import re
|
|
347
348
|
import time
|
|
@@ -525,7 +526,7 @@ class FlowbookKernel(BaseFlowbookKernel, Magics):
|
|
|
525
526
|
if msg_type == "notebook_structure":
|
|
526
527
|
self._process_structure_update(msg["cell_order"])
|
|
527
528
|
elif msg_type == "cell_edited":
|
|
528
|
-
self._process_cell_edit(msg["cell_id"])
|
|
529
|
+
self._process_cell_edit(msg["cell_id"], msg.get("source"))
|
|
529
530
|
elif msg_type == "continue_after_violation":
|
|
530
531
|
self._set_continue_after_violation(msg["enabled"])
|
|
531
532
|
elif msg_type == "sync":
|
|
@@ -579,26 +580,94 @@ class FlowbookKernel(BaseFlowbookKernel, Magics):
|
|
|
579
580
|
log(f"[structure_update] ERROR: {e}")
|
|
580
581
|
log(f"[structure_update] Traceback: {traceback.format_exc()}")
|
|
581
582
|
|
|
582
|
-
def
|
|
583
|
-
"""
|
|
583
|
+
def _source_fingerprint(self, source: str) -> Optional[str]:
|
|
584
|
+
"""Canonical AST fingerprint of a cell's source ([Inst-Edit]).
|
|
585
|
+
|
|
586
|
+
Runs the source through IPython's input transformer so magics/`!`
|
|
587
|
+
commands become valid Python, then returns ``ast.dump`` of the parsed
|
|
588
|
+
tree. This is insensitive to comments, blank lines, indentation, and
|
|
589
|
+
source positions, so cosmetic edits produce an identical fingerprint.
|
|
590
|
+
Returns None when the (possibly partial) source cannot be parsed.
|
|
591
|
+
"""
|
|
592
|
+
try:
|
|
593
|
+
transformed = self.shell.input_transformer_manager.transform_cell(source)
|
|
594
|
+
return ast.dump(ast.parse(transformed))
|
|
595
|
+
except (SyntaxError, ValueError):
|
|
596
|
+
return None
|
|
597
|
+
|
|
598
|
+
def _send_cell_edit_metadata(self, cell_id: str, stale_cells: list, icon: str, text: str) -> None:
|
|
599
|
+
"""Emit metadata + status reflecting a cell's post-edit staleness."""
|
|
600
|
+
staleness_reasons = self._enforcer._notebook_state.get_all_reasons()
|
|
601
|
+
metadata = ReproducibilityMetadata(
|
|
602
|
+
cell_id=cell_id,
|
|
603
|
+
execution_seq=self._enforcer.seq_counter,
|
|
604
|
+
read_locs=[],
|
|
605
|
+
write_locs=[],
|
|
606
|
+
changed_locs=[],
|
|
607
|
+
stale_cells=stale_cells,
|
|
608
|
+
cell_order=self._enforcer.cell_order,
|
|
609
|
+
staleness_reasons=staleness_reasons,
|
|
610
|
+
)
|
|
611
|
+
self._send_flowbook_message(build_metadata_message(metadata))
|
|
612
|
+
self._send_flowbook_message(build_status_message(icon, text, cell_id=cell_id))
|
|
613
|
+
|
|
614
|
+
def _process_cell_edit(self, cell_id: str, source: Optional[str] = None) -> None:
|
|
615
|
+
"""Process a cell_edited command ([Inst-Edit]).
|
|
616
|
+
|
|
617
|
+
Compares the edited source's AST fingerprint against the one captured when
|
|
618
|
+
the cell last executed. If they match, the edit is cosmetic
|
|
619
|
+
(whitespace/comments) or a round-trip back to the last-run source, so
|
|
620
|
+
CODE_CHANGED is cleared (the cell returns to clean unless stale for another
|
|
621
|
+
reason). Otherwise — including unparseable or sourceless edits — the cell is
|
|
622
|
+
marked stale, preserving the conservative legacy behavior.
|
|
623
|
+
"""
|
|
584
624
|
if not cell_id:
|
|
585
625
|
return
|
|
586
|
-
|
|
587
|
-
if
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
626
|
+
|
|
627
|
+
new_fp = self._source_fingerprint(source) if source is not None else None
|
|
628
|
+
old_fp = self._enforcer.get_fingerprint(cell_id)
|
|
629
|
+
|
|
630
|
+
def _fp_repr(fp: Optional[str]) -> str:
|
|
631
|
+
if fp is None:
|
|
632
|
+
return "None"
|
|
633
|
+
return f"<{len(fp)}c #{hash(fp) & 0xffffffff:08x}>"
|
|
634
|
+
|
|
635
|
+
log(
|
|
636
|
+
f"[Inst-Edit] cell={cell_id} source_present={source is not None} "
|
|
637
|
+
f"new_fp={_fp_repr(new_fp)} old_fp={_fp_repr(old_fp)} "
|
|
638
|
+
f"match={new_fp is not None and old_fp is not None and new_fp == old_fp}"
|
|
639
|
+
)
|
|
640
|
+
|
|
641
|
+
if new_fp is not None and old_fp is not None and new_fp == old_fp:
|
|
642
|
+
# Source semantically matches the last execution — not stale due to code.
|
|
643
|
+
stale_before = set(self._enforcer.get_stale_cells())
|
|
644
|
+
stale_cells = self._enforcer.clear_code_changed(cell_id)
|
|
645
|
+
changed = set(stale_cells) != stale_before
|
|
646
|
+
log(
|
|
647
|
+
f"[Inst-Edit] cell={cell_id} DECISION=cosmetic/revert -> clear_code_changed "
|
|
648
|
+
f"(status_changed={changed}, stale_now={stale_cells})"
|
|
598
649
|
)
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
650
|
+
if changed:
|
|
651
|
+
self._send_cell_edit_metadata(
|
|
652
|
+
cell_id, stale_cells, "↩️", "Edit reverted to last run, cleared"
|
|
653
|
+
)
|
|
654
|
+
return
|
|
655
|
+
|
|
656
|
+
if source is not None and new_fp is None:
|
|
657
|
+
reason = "unparseable"
|
|
658
|
+
elif source is None:
|
|
659
|
+
reason = "no-source"
|
|
660
|
+
else:
|
|
661
|
+
reason = "ast-differs"
|
|
662
|
+
stale_cells = self._enforcer.mark_cell_edited(cell_id)
|
|
663
|
+
marked = cell_id in stale_cells
|
|
664
|
+
log(
|
|
665
|
+
f"[Inst-Edit] cell={cell_id} DECISION=meaningful ({reason}) -> mark_cell_edited "
|
|
666
|
+
f"(marked_stale={marked}, stale_now={stale_cells})"
|
|
667
|
+
)
|
|
668
|
+
if marked:
|
|
669
|
+
self._send_cell_edit_metadata(
|
|
670
|
+
cell_id, stale_cells, "✏️", "Cell edited, marked stale"
|
|
602
671
|
)
|
|
603
672
|
|
|
604
673
|
def _set_continue_after_violation(self, enabled: bool) -> None:
|
|
@@ -1263,6 +1332,10 @@ class FlowbookKernel(BaseFlowbookKernel, Magics):
|
|
|
1263
1332
|
if cell_meta and "cell_order" in cell_meta:
|
|
1264
1333
|
self._enforcer.set_cell_order(cell_meta["cell_order"])
|
|
1265
1334
|
|
|
1335
|
+
# Capture the raw source (before magic stripping) for the [Inst-Edit]
|
|
1336
|
+
# fingerprint, so it matches the source the frontend/MCP send on edit.
|
|
1337
|
+
original_code = code
|
|
1338
|
+
|
|
1266
1339
|
# Check for notebook_structure magic (parse and remove if present)
|
|
1267
1340
|
code = self._process_structure_magic(code)
|
|
1268
1341
|
|
|
@@ -1470,6 +1543,17 @@ class FlowbookKernel(BaseFlowbookKernel, Magics):
|
|
|
1470
1543
|
error(f"Reproducibility truncation: {err.message}")
|
|
1471
1544
|
self._send_truncation_details(err.detail["truncation_details"])
|
|
1472
1545
|
|
|
1546
|
+
# [Inst-Edit] baseline: record the fingerprint of the source that
|
|
1547
|
+
# just committed, so future edits can be classified as meaningful
|
|
1548
|
+
# or cosmetic. Reached only on the committed path (rejected
|
|
1549
|
+
# executions return above).
|
|
1550
|
+
_baseline_fp = self._source_fingerprint(original_code)
|
|
1551
|
+
self._enforcer.set_fingerprint(self._cell_id, _baseline_fp)
|
|
1552
|
+
log(
|
|
1553
|
+
f"[Inst-Edit] cell={self._cell_id} stored baseline fingerprint "
|
|
1554
|
+
f"{'None' if _baseline_fp is None else f'<{len(_baseline_fp)}c #{hash(_baseline_fp) & 0xffffffff:08x}>'}"
|
|
1555
|
+
)
|
|
1556
|
+
|
|
1473
1557
|
# Display results (no longer skip on violations since we don't reject)
|
|
1474
1558
|
skip_display = False
|
|
1475
1559
|
if (
|
|
@@ -75,6 +75,11 @@ class NotebookState:
|
|
|
75
75
|
execution_seq: Dict[str, int] = field(default_factory=dict) # cell_id -> seq number
|
|
76
76
|
structural_reads_values: Dict[str, Dict[str, Dict[str, str]]] = field(default_factory=dict) # cell_id -> var -> attr -> value
|
|
77
77
|
typed_changes: Dict[str, List["Change"]] = field(default_factory=dict) # cell_id -> changes
|
|
78
|
+
# Canonical AST fingerprint of the source last executed for each cell.
|
|
79
|
+
# Used by [Inst-Edit] to decide whether a source edit is meaningful: an edit
|
|
80
|
+
# whose fingerprint matches this baseline is treated as cosmetic (clears
|
|
81
|
+
# CODE_CHANGED) rather than marking the cell stale.
|
|
82
|
+
fingerprints: Dict[str, str] = field(default_factory=dict) # cell_id -> AST fingerprint
|
|
78
83
|
|
|
79
84
|
# =========================================================================
|
|
80
85
|
# Status Access
|
|
@@ -106,6 +111,21 @@ class NotebookState:
|
|
|
106
111
|
"""Get all reasons for a cell's staleness."""
|
|
107
112
|
return self.get_status(cell_id).reasons
|
|
108
113
|
|
|
114
|
+
def set_fingerprint(self, cell_id: str, fingerprint: Optional[str]) -> None:
|
|
115
|
+
"""Store the AST fingerprint of the source last executed for a cell.
|
|
116
|
+
|
|
117
|
+
Passing None (unparseable source) clears any stored fingerprint, which
|
|
118
|
+
makes subsequent edits fall through to the conservative mark-stale path.
|
|
119
|
+
"""
|
|
120
|
+
if fingerprint is None:
|
|
121
|
+
self.fingerprints.pop(cell_id, None)
|
|
122
|
+
else:
|
|
123
|
+
self.fingerprints[cell_id] = fingerprint
|
|
124
|
+
|
|
125
|
+
def get_fingerprint(self, cell_id: str) -> Optional[str]:
|
|
126
|
+
"""Return the AST fingerprint of the source last executed for a cell."""
|
|
127
|
+
return self.fingerprints.get(cell_id)
|
|
128
|
+
|
|
109
129
|
def clear_pre_execution_reasons(self, cell_id: str) -> None:
|
|
110
130
|
"""Clear pre-execution reasons (NEVER_EXECUTED, CODE_CHANGED) from a cell.
|
|
111
131
|
|
|
@@ -487,6 +507,7 @@ class NotebookState:
|
|
|
487
507
|
self.execution_seq.pop(deleted_cell, None)
|
|
488
508
|
self.structural_reads_values.pop(deleted_cell, None)
|
|
489
509
|
self.typed_changes.pop(deleted_cell, None)
|
|
510
|
+
self.fingerprints.pop(deleted_cell, None)
|
|
490
511
|
if deleted_cell in self.cell_order:
|
|
491
512
|
self.cell_order.remove(deleted_cell)
|
|
492
513
|
|
|
@@ -594,6 +615,7 @@ class NotebookState:
|
|
|
594
615
|
self.execution_seq.clear()
|
|
595
616
|
self.structural_reads_values.clear()
|
|
596
617
|
self.typed_changes.clear()
|
|
618
|
+
self.fingerprints.clear()
|
|
597
619
|
|
|
598
620
|
# =========================================================================
|
|
599
621
|
# Debug/Inspection
|
flowbook/kernel/protocol.py
CHANGED
|
@@ -113,11 +113,17 @@ def build_notebook_structure_message(cell_order: List[str]) -> dict:
|
|
|
113
113
|
}
|
|
114
114
|
|
|
115
115
|
|
|
116
|
-
def build_cell_edited_message(cell_id: str) -> dict:
|
|
117
|
-
"""Build a cell_edited message.
|
|
116
|
+
def build_cell_edited_message(cell_id: str, source: Optional[str] = None) -> dict:
|
|
117
|
+
"""Build a cell_edited message.
|
|
118
|
+
|
|
119
|
+
The optional source is the cell's current text; the kernel uses it to decide
|
|
120
|
+
whether the edit is meaningful (AST changed) or cosmetic (whitespace/comments).
|
|
121
|
+
Omitting it preserves the legacy always-mark-stale behavior.
|
|
122
|
+
"""
|
|
118
123
|
return {
|
|
119
124
|
"type": CELL_EDITED,
|
|
120
125
|
"cell_id": cell_id,
|
|
126
|
+
"source": source,
|
|
121
127
|
}
|
|
122
128
|
|
|
123
129
|
|
|
@@ -2054,6 +2054,31 @@ class ReproducibilityEnforcer:
|
|
|
2054
2054
|
|
|
2055
2055
|
return self.get_stale_cells()
|
|
2056
2056
|
|
|
2057
|
+
def set_fingerprint(self, cell_id: str, fingerprint: Optional[str]) -> None:
|
|
2058
|
+
"""Store the AST fingerprint of the source last executed for a cell.
|
|
2059
|
+
|
|
2060
|
+
Used by [Inst-Edit] to distinguish meaningful edits from cosmetic ones
|
|
2061
|
+
(whitespace/comments). Computed by the kernel, which owns source access.
|
|
2062
|
+
"""
|
|
2063
|
+
self._notebook_state.set_fingerprint(cell_id, fingerprint)
|
|
2064
|
+
|
|
2065
|
+
def get_fingerprint(self, cell_id: str) -> Optional[str]:
|
|
2066
|
+
"""Return the AST fingerprint of the source last executed for a cell."""
|
|
2067
|
+
return self._notebook_state.get_fingerprint(cell_id)
|
|
2068
|
+
|
|
2069
|
+
def clear_code_changed(self, cell_id: str) -> List[str]:
|
|
2070
|
+
"""Clear the CODE_CHANGED reason for a cell (symmetric [Inst-Edit]).
|
|
2071
|
+
|
|
2072
|
+
Called when an edit brings a cell's source back to an AST identical to
|
|
2073
|
+
what it last executed. Removes the CODE_CHANGED/NEVER_EXECUTED pre-execution
|
|
2074
|
+
reasons, marking the cell clean only if no other staleness reason remains
|
|
2075
|
+
(e.g. FORWARD_STALE from an upstream cell is preserved).
|
|
2076
|
+
|
|
2077
|
+
Returns current stale cells list.
|
|
2078
|
+
"""
|
|
2079
|
+
self._notebook_state.clear_pre_execution_reasons(cell_id)
|
|
2080
|
+
return self.get_stale_cells()
|
|
2081
|
+
|
|
2057
2082
|
def get_execution_records_size(self) -> int:
|
|
2058
2083
|
"""
|
|
2059
2084
|
Calculate approximate memory size of execution records in bytes.
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tests for the meaningful-edit refinement of [Inst-Edit].
|
|
3
|
+
|
|
4
|
+
A source edit only marks a cell stale when it changes the cell's meaning. Edits
|
|
5
|
+
that touch only whitespace, blank lines, indentation, or comments are cosmetic
|
|
6
|
+
and leave the cell's status untouched; an edit that brings the source back to an
|
|
7
|
+
AST identical to the last execution clears the CODE_CHANGED reason.
|
|
8
|
+
|
|
9
|
+
Covers:
|
|
10
|
+
- FlowbookKernel._source_fingerprint (AST canonicalization, magics, syntax errors)
|
|
11
|
+
- NotebookState fingerprint storage (set/get/clear/delete)
|
|
12
|
+
- ReproducibilityEnforcer.clear_code_changed (symmetric clear)
|
|
13
|
+
- FlowbookKernel._process_cell_edit (end-to-end edit classification)
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
import pytest
|
|
17
|
+
|
|
18
|
+
from flowbook.kernel.flowbook_kernel import FlowbookKernel
|
|
19
|
+
from flowbook.kernel.notebook_state import NotebookState
|
|
20
|
+
from flowbook.kernel.models import Reason, ReasonType
|
|
21
|
+
from flowbook.kernel.tests.conftest import ReproducibilityTestHelper
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _make_kernel(enforcer=None):
|
|
25
|
+
"""Build a FlowbookKernel shell with just enough wiring to test edits.
|
|
26
|
+
|
|
27
|
+
Uses __new__ to skip the heavy IPython kernel init, then attaches a real
|
|
28
|
+
IPython InteractiveShell (its input_transformer_manager makes magics
|
|
29
|
+
fingerprint correctly) and captures outgoing flowbook messages.
|
|
30
|
+
"""
|
|
31
|
+
from IPython.core.interactiveshell import InteractiveShell
|
|
32
|
+
|
|
33
|
+
kernel = FlowbookKernel.__new__(FlowbookKernel)
|
|
34
|
+
kernel.shell = InteractiveShell.instance()
|
|
35
|
+
|
|
36
|
+
sent = []
|
|
37
|
+
kernel._send_flowbook_message = lambda msg: sent.append(msg)
|
|
38
|
+
kernel._sent_messages = sent
|
|
39
|
+
if enforcer is not None:
|
|
40
|
+
kernel._enforcer = enforcer
|
|
41
|
+
return kernel
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
# ---------------------------------------------------------------------------
|
|
45
|
+
# _source_fingerprint
|
|
46
|
+
# ---------------------------------------------------------------------------
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class TestSourceFingerprint:
|
|
50
|
+
def setup_method(self):
|
|
51
|
+
self.kernel = _make_kernel()
|
|
52
|
+
|
|
53
|
+
def fp(self, src):
|
|
54
|
+
return self.kernel._source_fingerprint(src)
|
|
55
|
+
|
|
56
|
+
def test_comment_only_edit_is_equal(self):
|
|
57
|
+
assert self.fp("x = 1 # original") == self.fp("x = 1 # changed comment")
|
|
58
|
+
|
|
59
|
+
def test_added_comment_line_is_equal(self):
|
|
60
|
+
assert self.fp("x = 1") == self.fp("# leading note\nx = 1")
|
|
61
|
+
|
|
62
|
+
def test_whitespace_and_blank_lines_equal(self):
|
|
63
|
+
assert self.fp("x=1") == self.fp("x = 1 ")
|
|
64
|
+
assert self.fp("x = 1") == self.fp("\n\nx = 1\n\n")
|
|
65
|
+
|
|
66
|
+
def test_reindented_block_is_equal(self):
|
|
67
|
+
a = "def f():\n return 1"
|
|
68
|
+
b = "def f():\n return 1" # different indent width, same structure
|
|
69
|
+
assert self.fp(a) == self.fp(b)
|
|
70
|
+
|
|
71
|
+
def test_string_literal_change_differs(self):
|
|
72
|
+
assert self.fp('x = "hello"') != self.fp('x = "world"')
|
|
73
|
+
|
|
74
|
+
def test_real_code_change_differs(self):
|
|
75
|
+
assert self.fp("x = 1") != self.fp("x = 2")
|
|
76
|
+
assert self.fp("x = 1") != self.fp("y = 1")
|
|
77
|
+
|
|
78
|
+
def test_underscore_numeric_literal_is_equal(self):
|
|
79
|
+
assert self.fp("x = 1000") == self.fp("x = 1_000")
|
|
80
|
+
|
|
81
|
+
def test_int_vs_float_literal_differs(self):
|
|
82
|
+
assert self.fp("x = 1") != self.fp("x = 1.0")
|
|
83
|
+
|
|
84
|
+
def test_magics_are_parseable_and_stable(self):
|
|
85
|
+
a = "%timeout 5\nx = 1 # a"
|
|
86
|
+
b = "%timeout 5\nx = 1 # b"
|
|
87
|
+
assert self.fp(a) is not None
|
|
88
|
+
assert self.fp(a) == self.fp(b)
|
|
89
|
+
|
|
90
|
+
def test_shell_command_parseable(self):
|
|
91
|
+
assert self.fp("!ls -la") is not None
|
|
92
|
+
|
|
93
|
+
def test_syntax_error_returns_none(self):
|
|
94
|
+
assert self.fp("x = ") is None
|
|
95
|
+
assert self.fp("def f(:\n pass") is None
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
# ---------------------------------------------------------------------------
|
|
99
|
+
# NotebookState fingerprint storage
|
|
100
|
+
# ---------------------------------------------------------------------------
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
class TestNotebookStateFingerprints:
|
|
104
|
+
def test_set_and_get(self):
|
|
105
|
+
ns = NotebookState()
|
|
106
|
+
ns.set_fingerprint("a", "FP")
|
|
107
|
+
assert ns.get_fingerprint("a") == "FP"
|
|
108
|
+
|
|
109
|
+
def test_get_unknown_is_none(self):
|
|
110
|
+
ns = NotebookState()
|
|
111
|
+
assert ns.get_fingerprint("missing") is None
|
|
112
|
+
|
|
113
|
+
def test_set_none_clears(self):
|
|
114
|
+
ns = NotebookState()
|
|
115
|
+
ns.set_fingerprint("a", "FP")
|
|
116
|
+
ns.set_fingerprint("a", None)
|
|
117
|
+
assert ns.get_fingerprint("a") is None
|
|
118
|
+
|
|
119
|
+
def test_delete_pops_fingerprint(self):
|
|
120
|
+
ns = NotebookState()
|
|
121
|
+
ns.cell_order = ["a"]
|
|
122
|
+
ns.set_fingerprint("a", "FP")
|
|
123
|
+
ns.handle_delete("a")
|
|
124
|
+
assert ns.get_fingerprint("a") is None
|
|
125
|
+
|
|
126
|
+
def test_clear_drops_fingerprints(self):
|
|
127
|
+
ns = NotebookState()
|
|
128
|
+
ns.set_fingerprint("a", "FP")
|
|
129
|
+
ns.clear()
|
|
130
|
+
assert ns.get_fingerprint("a") is None
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
# ---------------------------------------------------------------------------
|
|
134
|
+
# Enforcer.clear_code_changed
|
|
135
|
+
# ---------------------------------------------------------------------------
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
class TestClearCodeChanged:
|
|
139
|
+
def test_clear_only_code_changed_goes_clean(self):
|
|
140
|
+
helper = ReproducibilityTestHelper()
|
|
141
|
+
helper.set_cell_order(["a"])
|
|
142
|
+
helper.execute_cell("a", {}, {"x": 1}, writes={"x"})
|
|
143
|
+
sdc = helper.sdc
|
|
144
|
+
sdc._notebook_state.set_stale("a", {Reason(ReasonType.CODE_CHANGED)})
|
|
145
|
+
assert "a" in sdc.get_stale_cells()
|
|
146
|
+
|
|
147
|
+
stale = sdc.clear_code_changed("a")
|
|
148
|
+
assert "a" not in stale
|
|
149
|
+
assert sdc._notebook_state.is_clean("a")
|
|
150
|
+
|
|
151
|
+
def test_clear_preserves_other_reason(self):
|
|
152
|
+
helper = ReproducibilityTestHelper()
|
|
153
|
+
helper.set_cell_order(["a"])
|
|
154
|
+
helper.execute_cell("a", {}, {"x": 1}, writes={"x"})
|
|
155
|
+
sdc = helper.sdc
|
|
156
|
+
sdc._notebook_state.set_stale(
|
|
157
|
+
"a",
|
|
158
|
+
{
|
|
159
|
+
Reason(ReasonType.CODE_CHANGED),
|
|
160
|
+
Reason(ReasonType.FORWARD_STALE, loc="x", cell_id="z"),
|
|
161
|
+
},
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
stale = sdc.clear_code_changed("a")
|
|
165
|
+
assert "a" in stale # FORWARD_STALE keeps it stale
|
|
166
|
+
reasons = {r.type for r in sdc._notebook_state.get_reasons("a")}
|
|
167
|
+
assert ReasonType.CODE_CHANGED not in reasons
|
|
168
|
+
assert ReasonType.FORWARD_STALE in reasons
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
# ---------------------------------------------------------------------------
|
|
172
|
+
# _process_cell_edit end-to-end
|
|
173
|
+
# ---------------------------------------------------------------------------
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def _setup_executed_cell(source="x = 1"):
|
|
177
|
+
"""Return (kernel, enforcer) with cell 'a' executed and fingerprinted."""
|
|
178
|
+
helper = ReproducibilityTestHelper()
|
|
179
|
+
helper.set_cell_order(["a"])
|
|
180
|
+
helper.execute_cell("a", {}, {"x": 1}, writes={"x"})
|
|
181
|
+
sdc = helper.sdc
|
|
182
|
+
kernel = _make_kernel(enforcer=sdc)
|
|
183
|
+
sdc.set_fingerprint("a", kernel._source_fingerprint(source))
|
|
184
|
+
return kernel, sdc
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
class TestProcessCellEdit:
|
|
188
|
+
def test_cosmetic_edit_stays_clean_no_message(self):
|
|
189
|
+
kernel, sdc = _setup_executed_cell("x = 1")
|
|
190
|
+
kernel._process_cell_edit("a", "x = 1 # just a comment")
|
|
191
|
+
assert sdc._notebook_state.is_clean("a")
|
|
192
|
+
assert kernel._sent_messages == []
|
|
193
|
+
|
|
194
|
+
def test_whitespace_edit_stays_clean(self):
|
|
195
|
+
kernel, sdc = _setup_executed_cell("x = 1")
|
|
196
|
+
kernel._process_cell_edit("a", "\n\nx=1\n")
|
|
197
|
+
assert sdc._notebook_state.is_clean("a")
|
|
198
|
+
assert kernel._sent_messages == []
|
|
199
|
+
|
|
200
|
+
def test_meaningful_edit_marks_stale_and_emits(self):
|
|
201
|
+
kernel, sdc = _setup_executed_cell("x = 1")
|
|
202
|
+
kernel._process_cell_edit("a", "x = 2")
|
|
203
|
+
assert "a" in sdc.get_stale_cells()
|
|
204
|
+
assert any(m.get("type") == "metadata" for m in kernel._sent_messages)
|
|
205
|
+
|
|
206
|
+
def test_syntax_error_marks_stale(self):
|
|
207
|
+
kernel, sdc = _setup_executed_cell("x = 1")
|
|
208
|
+
kernel._process_cell_edit("a", "x = ")
|
|
209
|
+
assert "a" in sdc.get_stale_cells()
|
|
210
|
+
|
|
211
|
+
def test_revert_to_last_run_clears_staleness(self):
|
|
212
|
+
kernel, sdc = _setup_executed_cell("x = 1")
|
|
213
|
+
# First, a meaningful edit marks it stale.
|
|
214
|
+
kernel._process_cell_edit("a", "x = 2")
|
|
215
|
+
assert "a" in sdc.get_stale_cells()
|
|
216
|
+
kernel._sent_messages.clear()
|
|
217
|
+
# Editing back to source with the last-run AST clears CODE_CHANGED.
|
|
218
|
+
kernel._process_cell_edit("a", "x = 1 # back to original")
|
|
219
|
+
assert sdc._notebook_state.is_clean("a")
|
|
220
|
+
assert any(m.get("type") == "metadata" for m in kernel._sent_messages)
|
|
221
|
+
|
|
222
|
+
def test_cosmetic_edit_preserves_other_staleness(self):
|
|
223
|
+
kernel, sdc = _setup_executed_cell("x = 1")
|
|
224
|
+
sdc._notebook_state.set_stale(
|
|
225
|
+
"a", {Reason(ReasonType.FORWARD_STALE, loc="x", cell_id="z")}
|
|
226
|
+
)
|
|
227
|
+
kernel._process_cell_edit("a", "x = 1 # comment only")
|
|
228
|
+
# Still stale for the upstream reason; no spurious status flash.
|
|
229
|
+
assert "a" in sdc.get_stale_cells()
|
|
230
|
+
reasons = {r.type for r in sdc._notebook_state.get_reasons("a")}
|
|
231
|
+
assert ReasonType.FORWARD_STALE in reasons
|
|
232
|
+
assert kernel._sent_messages == []
|
|
233
|
+
|
|
234
|
+
def test_unexecuted_cell_without_fingerprint_marks_stale(self):
|
|
235
|
+
# No record / no fingerprint -> falls through to conservative mark.
|
|
236
|
+
helper = ReproducibilityTestHelper()
|
|
237
|
+
helper.set_cell_order(["a"])
|
|
238
|
+
helper.execute_cell("a", {}, {"x": 1}, writes={"x"})
|
|
239
|
+
sdc = helper.sdc
|
|
240
|
+
kernel = _make_kernel(enforcer=sdc)
|
|
241
|
+
# No set_fingerprint -> get_fingerprint is None -> mark stale on any edit.
|
|
242
|
+
kernel._process_cell_edit("a", "x = 1 # comment")
|
|
243
|
+
assert "a" in sdc.get_stale_cells()
|
flowbook/mcp/session.py
CHANGED
|
@@ -958,17 +958,41 @@ class NotebookSession:
|
|
|
958
958
|
# Editing
|
|
959
959
|
# ------------------------------------------------------------------
|
|
960
960
|
|
|
961
|
-
def _mark_cell_edited(self, cell_id: str) -> None:
|
|
962
|
-
"""
|
|
963
|
-
|
|
961
|
+
def _mark_cell_edited(self, cell_id: str, new_source: Optional[str] = None) -> None:
|
|
962
|
+
"""Notify the kernel of a source edit ([Inst-Edit]), if previously executed.
|
|
963
|
+
|
|
964
|
+
When ``new_source`` is provided, the kernel classifies the edit using the
|
|
965
|
+
cell's AST fingerprint (meaningful vs cosmetic) and we reconcile
|
|
966
|
+
``_stale_cells`` from its authoritative reply. When omitted (e.g. the
|
|
967
|
+
refactoring tools, which always change semantics), the kernel marks the
|
|
968
|
+
cell stale and we mirror that optimistically — preserving legacy behavior.
|
|
969
|
+
"""
|
|
970
|
+
if cell_id not in self.executed_cells:
|
|
971
|
+
return
|
|
972
|
+
|
|
973
|
+
result = KernelHelper.execute_code(
|
|
974
|
+
self.kernel_client,
|
|
975
|
+
"",
|
|
976
|
+
timeout=10,
|
|
977
|
+
store_history=False,
|
|
978
|
+
flowbook_msg={"type": "cell_edited", "cell_id": cell_id, "source": new_source},
|
|
979
|
+
)
|
|
980
|
+
|
|
981
|
+
if new_source is None:
|
|
982
|
+
# Legacy/refactor path: the edit is always meaningful.
|
|
964
983
|
self._stale_cells.add(cell_id)
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
984
|
+
return
|
|
985
|
+
|
|
986
|
+
# Source provided: the kernel decides. It emits a metadata message only
|
|
987
|
+
# when the edit changes the cell's status; reconcile from that reply.
|
|
988
|
+
for fb in result.get("flowbook_messages", []):
|
|
989
|
+
if isinstance(fb, dict) and fb.get("type") == "metadata":
|
|
990
|
+
if cell_id in set(fb.get("stale_cells", [])):
|
|
991
|
+
self._stale_cells.add(cell_id)
|
|
992
|
+
else:
|
|
993
|
+
self._stale_cells.discard(cell_id)
|
|
994
|
+
return
|
|
995
|
+
# No metadata reply → cosmetic no-op; leave staleness unchanged.
|
|
972
996
|
|
|
973
997
|
def edit_cell(self, cell_id: str, new_source: str) -> Dict[str, Any]:
|
|
974
998
|
"""Update a cell's source and mark it stale if previously executed."""
|
|
@@ -978,7 +1002,7 @@ class NotebookSession:
|
|
|
978
1002
|
old_source = get_cell_source(cell)
|
|
979
1003
|
set_cell_source(cell, new_source)
|
|
980
1004
|
|
|
981
|
-
self._mark_cell_edited(cell_id)
|
|
1005
|
+
self._mark_cell_edited(cell_id, new_source)
|
|
982
1006
|
self._put_contents_api()
|
|
983
1007
|
|
|
984
1008
|
return {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "flowbook",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Reproducibility enforcement for Jupyter notebooks: a JupyterLab extension that enforces rerun consistency by tracking variable- and column-level dependencies between cells.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"jupyter",
|
|
@@ -139,7 +139,7 @@
|
|
|
139
139
|
"outputDir": "flowbook/labextension",
|
|
140
140
|
"schemaDir": "schema",
|
|
141
141
|
"_build": {
|
|
142
|
-
"load": "static/remoteEntry.
|
|
142
|
+
"load": "static/remoteEntry.9c89c62fdbe6db112e2d.js",
|
|
143
143
|
"extension": "./extension",
|
|
144
144
|
"style": "./style"
|
|
145
145
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "flowbook",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Reproducibility enforcement for Jupyter notebooks: a JupyterLab extension that enforces rerun consistency by tracking variable- and column-level dependencies between cells.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"jupyter",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";(self.webpackChunkflowbook=self.webpackChunkflowbook||[]).push([[873],{873(e,t,o){o.r(t),o.d(t,{default:()=>ee});var n=o(615),s=o(602);class l{constructor(e){this._kernelChanged=new s.Signal(this),this._notebookKernels=new Map,this._tracker=e,this._setupTracking()}get kernelChanged(){return this._kernelChanged}getKernelType(e){const t=e.sessionContext.session?.kernel?.name;return this._classifyKernel(t)}isFlowbookKernel(e){return"flowbook_kernel"===this.getKernelType(e)}_classifyKernel(e){return e?"flowbook_kernel"===e?"flowbook_kernel":"other":"none"}_setupTracking(){this._tracker.forEach(e=>{this._monitorNotebook(e)}),this._tracker.widgetAdded.connect((e,t)=>{this._monitorNotebook(t)})}_monitorNotebook(e){const t=e.context.path,o=this.getKernelType(e);this._notebookKernels.set(t,o),e.sessionContext.kernelChanged.connect(()=>{const o=this._notebookKernels.get(t)||"none",n=this.getKernelType(e);o!==n&&(this._notebookKernels.set(t,n),this._kernelChanged.emit({notebook:e,previousKernel:o,currentKernel:n}))}),e.disposed.connect(()=>{this._notebookKernels.delete(t)})}}var a=o(247),r=o(345),i=o(338);function c(e){const t=[],o=e.content.widgets;for(let e=0;e<o.length;e++)"code"===o[e].model.type&&t.push(o[e].model.id);return t}function d(e,t){if(e<0)throw new Error(`Index must be non-negative (got: ${e})`);let o;if(e<26)o="@"+String.fromCharCode("A".charCodeAt(0)+e);else if(e<702){const t=e-26;o="@"+String.fromCharCode("A".charCodeAt(0)+Math.floor(t/26))+String.fromCharCode("A".charCodeAt(0)+t%26)}else{if(!(e<18278))throw new Error(`Index ${e} is too large (max supported: 18277 for @ZZZ)`);{const t=e-702;o="@"+String.fromCharCode("A".charCodeAt(0)+Math.floor(t/676))+String.fromCharCode("A".charCodeAt(0)+Math.floor(t/26%26))+String.fromCharCode("A".charCodeAt(0)+t%26)}}return t?`${o} / ${t.slice(-4)}`:o}function h(e,t){const o=t.indexOf(e);if(-1===o)return e;try{return d(o,e)}catch(t){return e}}function u(e){return void 0!==e.var_name?e.var_name:"string"==typeof e.qualifier?e.qualifier:void 0}function m(e){const t=new Map,o={var:"Var",col:"Col",cols:"Cols",rows:"Rows",file:"File"};for(const n of e){const e=o[n.type]||n.type,s=u(n);if(s){let o=t.get(s);o||(o={types:new Map},t.set(s,o));let l=o.types.get(e);l||(l=[],o.types.set(e,l)),"col"===n.type&&l.push(n.name)}else{let o=t.get(n.name);o||(o={types:new Map},t.set(n.name,o)),o.types.has(e)||o.types.set(e,[])}}return t}function _(e){return 0===e.size?r.createElement("span",{className:"flowbook-none"}," None"):r.createElement("ul",{className:"flowbook-variable-list"},Array.from(e.entries()).map(([e,t])=>{if(!Array.from(t.types.values()).some(e=>e.length>0)){const o=Array.from(t.types.keys())[0],n=o&&"Var"!==o?` (${o})`:"";return r.createElement("li",{key:e},r.createElement("code",null,e,n&&r.createElement("span",{style:{color:"#888",fontSize:"0.9em"}},n)))}return r.createElement("li",{key:e},r.createElement("code",null,e),r.createElement("ul",{className:"flowbook-loc-sublist"},Array.from(t.types.entries()).map(([e,t])=>0===t.length?r.createElement("li",{key:e},r.createElement("span",{className:"flowbook-loc-type"},e)):r.createElement("li",{key:e},r.createElement("span",{className:"flowbook-loc-type"},e,":")," ",r.createElement("code",null,t.sort().join(", "))))))}))}const f=({metadata:e,cellId:t,currentCellOrder:o})=>{if(!e)return r.createElement("div",{className:"flowbook-metadata-empty"},r.createElement("p",null,"No reproducibility metadata available."),r.createElement("p",null,"Execute a cell to see dependency tracking."));const n=e.stale_cells.length>0,s=function(e){const t=new Map,o={var:"Var",col:"Col",cols:"Cols",rows:"Rows",file:"File"};for(const n of e){const e=o[n.type]||n.type,s=u(n);if(s){let o=t.get(s);o||(o={types:new Map},t.set(s,o));let l=o.types.get(e);l||(l=[],o.types.set(e,l)),"col"===n.type&&l.push(n.name)}else{let o=t.get(n.name);o||(o={types:new Map},t.set(n.name,o)),o.types.has(e)||o.types.set(e,[])}}return t}(e.read_locs||[]),l=m(e.write_locs||[]),a=m(e.changed_locs||[]),i=function(e,t){if(e.length!==t.length)return!1;for(let o=0;o<e.length;o++)if(e[o].type!==t[o].type||e[o].name!==t[o].name||e[o].qualifier!==t[o].qualifier)return!1;return!0}(e.write_locs||[],e.changed_locs||[]);return r.createElement("div",{className:"flowbook-metadata-content"},t&&r.createElement(r.Fragment,null,r.createElement("div",{className:"flowbook-metadata-header"},"Cell: ",h(t,o)),r.createElement("div",{className:"flowbook-metadata-section"},r.createElement("div",{className:"flowbook-metadata-item"},r.createElement("span",{style:{fontSize:"0.85em",color:"#666"}},"Id: "),r.createElement("code",{style:{fontSize:"0.85em",color:"#666"}},t))),r.createElement("div",{className:"flowbook-metadata-divider"})),r.createElement("div",{className:"flowbook-metadata-section"},r.createElement("div",{className:"flowbook-metadata-item"},r.createElement("strong",null,"Execution #:")," ",e.execution_seq)),e.errors&&e.errors.length>0&&r.createElement(r.Fragment,null,r.createElement("div",{className:"flowbook-metadata-divider"}),r.createElement("div",{className:"flowbook-metadata-section flowbook-error-section"},r.createElement("div",{className:"flowbook-metadata-item"},r.createElement("strong",{style:{color:"#d32f2f"}},"Errors:"),r.createElement("ul",{className:"flowbook-error-list",style:{margin:"4px 0",paddingLeft:"16px"}},e.errors.map((e,t)=>{return r.createElement("li",{key:t,style:{marginBottom:"6px"}},r.createElement("div",{style:{fontWeight:600,color:"#d32f2f",fontSize:"0.9em"}},{no_read_and_write:"Read And Write Same Location",write_before_read:"Undefined Variable",no_read_before_write:"Forward Contamination",no_write_after_read:"Backward Mutation",unrecoverable_mutation:"Unrecoverable Mutation"}[n=e.error_type]||n),r.createElement("div",{style:{fontSize:"0.85em",color:"#333"}},e.message),e.causer_cell&&r.createElement("div",{style:{fontSize:"0.85em",color:"#666"}},"Conflicts with:"," ",r.createElement("code",null,h(e.causer_cell,o))));var n}))))),(void 0!==e.execute_duration_ms||void 0!==e.code_duration_ms||void 0!==e.state_duration_ms||void 0!==e.check_duration_ms)&&r.createElement(r.Fragment,null,r.createElement("div",{className:"flowbook-metadata-divider"}),r.createElement("div",{className:"flowbook-metadata-section"},r.createElement("div",{className:"flowbook-metadata-item"},r.createElement("strong",null,"Timing:"),r.createElement("ul",{className:"flowbook-timing-list"},void 0!==e.execute_duration_ms&&r.createElement("li",null,"Execute:"," ",r.createElement("code",null,e.execute_duration_ms.toFixed(0)," ms")),void 0!==e.code_duration_ms&&r.createElement("li",null,"Code: ",r.createElement("code",null,e.code_duration_ms.toFixed(0)," ms")),void 0!==e.state_duration_ms&&r.createElement("li",null,"State:"," ",r.createElement("code",null,e.state_duration_ms.toFixed(0)," ms")),void 0!==e.check_duration_ms&&r.createElement("li",null,"Check:"," ",r.createElement("code",null,e.check_duration_ms.toFixed(0)," ms")))))),r.createElement("div",{className:"flowbook-metadata-divider"}),r.createElement("div",{className:"flowbook-metadata-section"},r.createElement("div",{className:"flowbook-metadata-item"},r.createElement("strong",null,"Reads:"),_(s))),r.createElement("div",{className:"flowbook-metadata-divider"}),r.createElement("div",{className:"flowbook-metadata-section"},r.createElement("div",{className:"flowbook-metadata-item"},r.createElement("strong",null,i?"Writes:":"Writes (Intended):"),_(l))),!i&&r.createElement(r.Fragment,null,r.createElement("div",{className:"flowbook-metadata-divider"}),r.createElement("div",{className:"flowbook-metadata-section"},r.createElement("div",{className:"flowbook-metadata-item"},r.createElement("strong",null,"Changed (Actual):"),_(a)))),n&&r.createElement(r.Fragment,null,r.createElement("div",{className:"flowbook-metadata-divider"}),r.createElement("div",{className:"flowbook-metadata-section flowbook-stale-section"},r.createElement("div",{className:"flowbook-metadata-item"},r.createElement("strong",null,"Stale Cells:"),r.createElement("ul",{className:"flowbook-cell-list flowbook-stale"},e.stale_cells.map((e,t)=>r.createElement("li",{key:t},r.createElement("code",null,h(e,o)))))))),e.structural_warnings&&e.structural_warnings.length>0&&r.createElement(r.Fragment,null,r.createElement("div",{className:"flowbook-metadata-divider"}),r.createElement("div",{className:"flowbook-metadata-section flowbook-warning-section"},r.createElement("div",{className:"flowbook-warning-header"},"Structural Warnings"),r.createElement("div",{className:"flowbook-warning-content"},r.createElement("ul",{className:"flowbook-warning-list"},e.structural_warnings.map((e,t)=>r.createElement("li",{key:t},e)))))))},p=({icon:e,text:t,cellRef:o})=>e||t?r.createElement("div",{className:"flowbook-status-header"},r.createElement("div",{style:{fontSize:"0.7em",color:"#888",textTransform:"uppercase",letterSpacing:"0.05em",marginBottom:"2px"}},"Last Execution"),r.createElement("div",{style:{fontSize:"0.85em",fontFamily:"var(--jp-code-font-family, monospace)"}},o&&r.createElement("strong",{style:{marginRight:"6px"}},o),e," ",t)):null;class g extends a.Widget{constructor(){super(),this._metadata=null,this._cellId=null,this._currentCellOrder=[],this._statusIcon=null,this._statusText=null,this._statusCellRef=null,this._root=null,this.id="flowbook-metadata-panel",this.addClass("flowbook-metadata-panel"),this.title.label="FlowBook",this.title.closable=!0,this.title.caption="FlowBook cell metadata",this.render()}render(){this._root||(this._root=(0,i.H)(this.node)),this._root.render(r.createElement(r.Fragment,null,r.createElement(p,{icon:this._statusIcon,text:this._statusText,cellRef:this._statusCellRef}),r.createElement(f,{metadata:this._metadata,cellId:this._cellId,currentCellOrder:this._currentCellOrder})))}updateMetadata(e,t,o){this._metadata=e,this._cellId=t,this._currentCellOrder=o,this.render()}updateStatus(e,t,o){this._statusIcon=e,this._statusText=t,this._statusCellRef=o?h(o,this._currentCellOrder):null,this.render()}clear(){this._metadata=null,this._cellId=null,this._currentCellOrder=[],this.render()}dispose(){this.isDisposed||(this._root?.unmount(),this._root=null,super.dispose())}}var k=o(395);function b(e){return void 0!==e.var_name?e.var_name:"string"==typeof e.qualifier?e.qualifier:void 0}function w(e,t){return"number"==typeof e.qualifier&&"number"==typeof t.qualifier?e.qualifier===t.qualifier:b(e)===b(t)}function y(e,t){switch(e.type){case"var":return"var"===t.type&&e.name===t.name;case"col":return"col"===t.type?w(e,t)&&e.name===t.name:"cols"===t.type&&w(e,t);case"cols":return("col"===t.type||"cols"===t.type)&&w(e,t);case"rows":return("col"===t.type||"rows"===t.type)&&w(e,t);case"file":return"file"===t.type&&e.name===t.name;default:return!1}}function v(e){const t=b(e);return t?`${t}.${e.name}`:e.name}function C(e){return e}o(740);const x={bulge:function({id:e,sourceX:t,sourceY:o,targetX:n,targetY:s,style:l,markerEnd:a,label:i,labelStyle:c,data:d}){const h=d?.bulge||50,u=Math.max(t,n)+h,m=o+.25*(s-o),_=o+.75*(s-o),f=`M ${t},${o} C ${u},${m} ${u},${_} ${n},${s}`,p=.125*t+.375*u+.375*u+.125*n,g=.125*o+.375*m+.375*_+.125*s,b=i?String(i):"",w=6.5*b.length;return r.createElement(r.Fragment,null,r.createElement(k.BaseEdge,{id:e,path:f,style:l,markerEnd:a}),b&&r.createElement("g",{transform:`translate(${p}, ${g})`},r.createElement("rect",{x:-w/2-4,y:-11,width:w+8,height:22,rx:3,ry:3,fill:"white",fillOpacity:.95,stroke:"var(--jp-border-color2, #ddd)",strokeWidth:.5}),r.createElement("text",{style:c,textAnchor:"middle",dominantBaseline:"central",className:"react-flow__edge-text"},b)))}},E={cell:function({data:e}){return r.createElement("div",{className:e.className},r.createElement(k.Handle,{type:"target",position:k.Position.Top,id:"center-top",style:{opacity:0}}),r.createElement(k.Handle,{type:"source",position:k.Position.Bottom,id:"center-bottom",style:{opacity:0}}),r.createElement(k.Handle,{type:"source",position:k.Position.Right,id:"right-source",style:{opacity:0}}),r.createElement(k.Handle,{type:"target",position:k.Position.Right,id:"right-target",style:{opacity:0}}),e.label)}},N={type:k.MarkerType.ArrowClosed,color:"#999",width:12,height:12},S={type:k.MarkerType.ArrowClosed,color:"#2472c8",width:14,height:14},M={type:k.MarkerType.ArrowClosed,color:"#dc3545",width:14,height:14};const $=({cellData:e})=>{const[t,o]=r.useState(null),{nodes:n,allEdges:s}=r.useMemo(()=>function(e){return{nodes:e.map((e,t)=>{let o="flowbook-dep-node";return e.hasError?o+=" flowbook-dep-node-error":e.isStale?o+=" flowbook-dep-node-stale":e.isExecuted||(o+=" flowbook-dep-node-unexecuted"),{id:e.cellId,type:"cell",position:{x:40,y:80*t},data:{label:e.label,className:o}}}),allEdges:function(e){const t=[];for(let o=0;o<e.length;o++)if(e[o].isExecuted)for(let n=o+1;n<e.length;n++){if(!e[n].isExecuted)continue;const s=n-o,l=[],a=new Set;for(const t of e[o].writeLocs)for(const o of e[n].readLocs)if(y(t,o)){const e=v(o);a.has(e)||(a.add(e),l.push(e))}l.length>0&&t.push({sourceId:e[o].cellId,targetId:e[n].cellId,kind:"data_dependency",locations:l,span:s,lane:0})}const o=new Map;for(let t=0;t<e.length;t++)o.set(e[t].cellId,t);const n=new Set;for(const s of e)for(const e of s.violations){if(!e.causer_cell)continue;const l=e.causer_cell.startsWith("@")?e.causer_cell.slice(1):e.causer_cell,a=o.get(s.cellId),r=o.get(l);if(void 0===a||void 0===r)continue;let i,c;"no_write_after_read"===e.error_type?(i=s.cellId,c=l):"no_read_before_write"===e.error_type?(i=l,c=s.cellId):(i=s.cellId,c=l);const d=`${i}-${c}-violation`;if(n.has(d))continue;n.add(d);const h=o.get(i)??0,u=o.get(c)??0,m=Math.abs(h-u);t.push({sourceId:i,targetId:c,kind:"violation",locations:e.locations,span:m||1,lane:0})}t.sort((e,t)=>e.span-t.span);for(let e=0;e<t.length;e++)t[e].lane=e+1;const s=[];for(let t=0;t<e.length-1;t++)s.push({sourceId:e[t].cellId,targetId:e[t+1].cellId,kind:"program_order",locations:[],span:1,lane:0});return s.concat(t)}(e).map(e=>{const t=`${e.sourceId}-${e.targetId}-${e.kind}`;if("program_order"===e.kind)return{id:t,source:e.sourceId,target:e.targetId,sourceHandle:"center-bottom",targetHandle:"center-top",type:"straight",style:{stroke:"#999",strokeWidth:1},markerEnd:N,data:{edgeKind:"program_order"}};const o=40+25*e.lane,n="violation"===e.kind;return{id:t,source:e.sourceId,target:e.targetId,sourceHandle:"right-source",targetHandle:"right-target",type:"bulge",data:{bulge:o,edgeKind:e.kind},label:(s=e.locations,0===s.length?"":s.length<=3?s.join(", "):s.slice(0,3).join(", ")+", ..."),labelStyle:{fontSize:10,fontFamily:"monospace",fill:n?"#dc3545":void 0},style:n?{stroke:"#dc3545",strokeWidth:2,strokeDasharray:"5,5"}:{stroke:"#2472c8",strokeWidth:2},markerEnd:n?M:S};var s})}}(e),[e]),l=r.useMemo(()=>s.filter(e=>{const o=e.data?.edgeKind;return"program_order"===o||"violation"===o||null!==t&&(e.source===t||e.target===t)}),[s,t]),a=r.useCallback((e,t)=>{o(t.id)},[]),i=r.useCallback(()=>{o(null)},[]);return r.createElement(k.ReactFlow,{nodes:n,edges:l,nodeTypes:E,edgeTypes:x,onNodeMouseEnter:a,onNodeMouseLeave:i,nodesDraggable:!1,nodesConnectable:!1,elementsSelectable:!1,fitView:!0,fitViewOptions:{padding:.15,minZoom:.5,maxZoom:1.2},minZoom:.3,maxZoom:2},r.createElement(k.Controls,{showInteractive:!1}))},I=({cellData:e})=>0===e.length?r.createElement("div",{className:"flowbook-metadata-empty"},r.createElement("p",null,"No cells to display."),r.createElement("p",null,"Execute cells to see dependencies.")):r.createElement("div",{className:"flowbook-dependencies-graph",style:{width:"100%",height:"100%",position:"relative"}},r.createElement("div",{style:{position:"absolute",top:0,left:0,right:0,bottom:0}},r.createElement(k.ReactFlowProvider,null,r.createElement($,{cellData:e}))));class A extends a.Widget{constructor(){super(),this._cellData=[],this._root=null,this.id="flowbook-dependencies-panel",this.addClass("flowbook-dependencies-panel"),this.title.label="Dependencies",this.title.closable=!0,this.title.caption="Cell dependency graph",this.render()}render(){this._root||(this._root=(0,i.H)(this.node)),this._root.render(r.createElement(I,{cellData:this._cellData}))}updateGraph(e){this._cellData=e,this.render()}clear(){this._cellData=[],this.render()}dispose(){this.isDisposed||(this._root?.unmount(),this._root=null,super.dispose())}}class R{constructor(e){this._staleCells=new Set,this._stalenessReasons=new Map,this._stalenessChanged=new s.Signal(this),this._notebook=e,this._setupKernelRestartListener()}get stalenessChanged(){return this._stalenessChanged}get staleCells(){return this._staleCells}isCellStale(e){return this._staleCells.has(e)}setReasons(e,t){this._stalenessReasons.set(e,t)}getReasons(e){return this._stalenessReasons.get(e)||[]}setReason(e,t){this._stalenessReasons.set(e,[t])}getReason(e){const t=this._stalenessReasons.get(e);return t&&t.length>0?t[0]:void 0}updateFromMetadata(e){const t=new Set(this._staleCells);this._staleCells=new Set(e.stale_cells);const o=new Set(e.stale_cells),n=[...o].filter(e=>!t.has(e)),s=[...t].filter(e=>!o.has(e));for(const e of s)this._stalenessReasons.delete(e);let l=!1;if(e.staleness_reasons)for(const[t,o]of Object.entries(e.staleness_reasons)){const e=this._stalenessReasons.get(t);JSON.stringify(e||[])!==JSON.stringify(o)&&(l=!0),this._stalenessReasons.set(t,o)}(n.length>0||s.length>0||l)&&this._stalenessChanged.emit({added:n,removed:s,current:[...this._staleCells]})}clear(){const e=[...this._staleCells];this._staleCells.clear(),this._stalenessReasons.clear(),e.length>0&&this._stalenessChanged.emit({added:[],removed:e,current:[]})}_setupKernelRestartListener(){this._notebook.sessionContext.statusChanged.connect((e,t)=>{"restarting"!==t&&"autorestarting"!==t||this.clear()})}dispose(){this._staleCells.clear(),this._stalenessReasons.clear()}}function O(e){return"message"in e}class W{updateStalenessNotice(e,t,o,n){if("code"!==e.model.type)return;const s=e.model.outputs,l=e.model.getMetadata("flowbook"),a=l?.errors&&l.errors.length>0;let r=!1;for(let e=0;e<s.length;e++){const t=C(s.get(e).toJSON());if(!0===t.metadata?.flowbook_violation_notice){r=!0;break}}if(a||r)return void this._removeNoticesByKey(s,"flowbook_staleness_notice");const i=s.length>0&&!0===s.get(0).toJSON().metadata?.flowbook_staleness_notice;if(t){const t=o.getReason(e.model.id)||{type:"unknown",message:"Dependencies changed"};if("never_executed"===t.type)return void(i&&this._removeNoticesByKey(s,"flowbook_staleness_notice"));const l=this.formatStalenessMessage(t,n,e.model.id),a=l.replace(/`([^`]+)`/g,"<code>$1</code>"),r="writer_conflict"===t.type?"Unresolved Violation":"",c=r?`⚠️ ${r}: ${l}`:`⚠️ ${l}`,d={output_type:"display_data",data:{"text/html":r?`<div class="flowbook-staleness-notice">⚠️ <b>${r}</b>: ${a} </div>`:`<div class="flowbook-staleness-notice">⚠️ ${a} </div>`,"text/plain":c},metadata:{flowbook_staleness_notice:!0}};if(i){const e=s.get(0).toJSON().data?.["text/plain"];if(e===c)return}const h=[d];for(let e=0;e<s.length;e++){const t=s.get(e).toJSON();C(t).metadata?.flowbook_staleness_notice||h.push(t)}s.fromJSON(h)}else i&&this._removeNoticesByKey(s,"flowbook_staleness_notice")}formatStalenessMessage(e,t,o){return O(e)?this._formatFrontendReason(e,t,o):this._formatBackendReason(e,t,o)}updateStalenessMetadata(e,t){const o=e.model.getMetadata("flowbook_staleness");if(!o)return;if(!O(o)||!o.causing_cell)return;const n=this.formatStalenessMessage(o,t,e.model.id);n!==o.message&&e.model.setMetadata("flowbook_staleness",{...o,message:n})}_formatFrontendReason(e,t,o){if("source_edited"===e.type)return"Source code was edited";if(!e.causing_cell)return e.message;const n=t.indexOf(e.causing_cell),s=t.indexOf(o),l=n<0,a=l?"a deleted cell":d(n),r=!l&&s>=0&&n<s?" above":l?"":" below",i=[];if(e.variables)for(const t of e.variables)i.push("`"+t+"`");if(e.columns)for(const[t,o]of Object.entries(e.columns))for(const e of o)i.push("`"+t+"."+e+"`");return"writer_conflict"===e.type&&i.length>0?`Writes ${i.join(", ")} already read by ${a}${r}`:i.length>0?`${i.join(", ")} modified by ${a}${r}`:"unknown"===e.type?`Dependencies modified by ${a}`:e.message}_formatBackendReason(e,t,o){const n="cell_id"in e?e.cell_id:void 0,s="loc"in e?e.loc:void 0,l=t.indexOf(o);let a="",r="",i=!1;if(n){const e=t.indexOf(n);i=e<0,a=i?"a deleted cell":d(e),!i&&l>=0&&(r=e<l?" above":" below")}switch(e.type){case"never_executed":return"Cell has never been executed";case"code_changed":return"Source code was edited";case"forward_stale":return s&&a?`\`${s}\` modified by ${a}${r}`:a?`Input modified by ${a}${r}`:"Input was modified";case"write_overlap":return s&&a?`\`${s}\` also written by ${a}`:a?`Writes conflict with ${a}`:"Write conflict detected";case"backward_stale":return s&&a?`\`${s}\` write conflict with ${a}`:"Write conflict detected";case"no_read_before_write":return s&&a?`Reads \`${s}\` written by ${a} ${r}`:"Reads value written by another cell";case"order_changed":return"Cell order changed";case"no_write_after_read":return s&&a?`Writes \`${s}\` already read by ${a} ${r}`:a?`Writes variable already read by ${a} ${r}`:"Writes variable already read by another cell";default:return"Cell is stale"}}_removeNoticesByKey(e,t){const o=[];let n=!1;for(let s=0;s<e.length;s++){const l=e.get(s).toJSON();C(l).metadata?.[t]?n=!0:o.push(l)}n&&e.fromJSON(o)}}class F{updateViolationNotice(e,t){if("code"!==e.model.type)return!1;const o=e.model.outputs,n=e.model.getMetadata("flowbook"),s=n?.errors;let l=!1,a="";for(let e=0;e<o.length;e++){const t=C(o.get(e).toJSON());if(!0===t.metadata?.flowbook_violation_notice){l=!0,a=t.data?.["text/plain"]||"";break}}if(s&&s.length>0){const{noticeOutput:e,plainText:n}=this._buildViolationNotice(s,t);if(l&&a===n)return!0;const r=[e];for(let e=0;e<o.length;e++){const t=o.get(e).toJSON(),n=C(t),s=!0===n.metadata?.flowbook_violation_notice,l=!0===n.metadata?.flowbook_staleness_notice,a="error"===t.output_type&&("ReproducibilityError"===n.ename||"ReproducibilityViolation"===n.ename),i="display_data"===t.output_type&&n.metadata?.predicate_violation;s||l||a||i||r.push(t)}return o.fromJSON(r),!0}if(l){const e=[];for(let t=0;t<o.length;t++){const n=o.get(t).toJSON(),s=C(n);s.metadata?.flowbook_violation_notice||e.push(n)}o.fromJSON(e)}return!1}_buildViolationNotice(e,t){const o=new Map;for(const n of e){let e=null;if(n.causer_cell&&"string"==typeof n.causer_cell){const o=n.causer_cell.startsWith("@")?n.causer_cell.slice(1):n.causer_cell,s=t.indexOf(o);e=s>=0?d(s):"a deleted cell"}const s=[...n.locations].sort().join(","),l=`${n.error_type}:${s}`;o.has(l)||o.set(l,{errorType:n.error_type,locs:n.locations,causers:[]}),e&&!o.get(l).causers.includes(e)&&o.get(l).causers.push(e)}const n=[],s=[];for(const e of o.values()){const t=e.locs.map(e=>"`"+e+"`").join(", ").replace(/`([^`]+)`/g,"<code>$1</code>"),o=e.causers.join(", ");let l;switch(e.errorType){case"no_write_after_read":l=o?`Writes ${t} already read by ${o}`:`Writes ${t} already read by cell above`;for(const t of e.locs)if(t.includes(".")){const[e,o]=t.split(".");l+=`<br>Use <code>${e}["${o}"]</code> = ... for full-column assignment`}break;case"no_read_before_write":l=o?`Reads ${t} written by ${o} below`:`Reads ${t} written by cell below`;break;case"no_read_and_write":l=`Reads and writes ${t}`;break;case"write_before_read":l=`${t} not defined by any cell above`;break;case"unrecoverable_mutation":l=`${t} was modified in place, which violates rerun consistency`;break;default:l=`Violation on ${t}`}n.push(l),s.push(l.replace(/<code>([^<]+)<\/code>/g,"`$1`"))}const l=n.map(e=>`<div>❌ ${e}</div>`).join(""),a=s.map(e=>`❌ ${e}`).join("\n");return{noticeOutput:{output_type:"display_data",data:{"text/html":`<div class="flowbook-error-notice">${l}</div>`,"text/plain":a},metadata:{flowbook_violation_notice:!0,flowbook_predicate_accepted:e[0].accepted,flowbook_violation_count:e.length}},plainText:a}}}class L{constructor(e,t){this._dependenciesPanel=null,this._depPanelFrameId=null,this._stalenessManagers=new Map,this._pendingRestartUpdate=new Set,this._executedInSession=new Map,this._monitoredNotebooks=new Set,this._stalenessNotice=new W,this._violationNotice=new F,this._isDisposed=!1,this._tracker=e,this._panel=t,this._initialize()}setDependenciesPanel(e){this._dependenciesPanel=e}refreshDependencies(){null!==this._depPanelFrameId&&cancelAnimationFrame(this._depPanelFrameId),this._depPanelFrameId=requestAnimationFrame(()=>{this._depPanelFrameId=null;const e=this._tracker.currentWidget;if(e){const t=this.getStalenessManager(e),o=c(e);this._updateDependenciesPanel(e,t,o)}})}updateStatus(e,t,o){this._panel.updateStatus(e,t,o)}getStalenessManager(e){const t=e.context.path;let o=this._stalenessManagers.get(t);return o||(o=new R(e),this._stalenessManagers.set(t,o),o.stalenessChanged.connect(()=>{this._updateAllCells(e)}),e.disposed.connect(()=>{o?.dispose(),this._stalenessManagers.delete(t),this._monitoredNotebooks.delete(t)})),o}updateCell(e,t,o,n){const s=e.model.id,l=t.isCellStale(s),a=this._executedInSession.get(n),r=void 0!==e.model.getMetadata("flowbook"),i=!(a&&a.has(s)||r),c=e.model.sharedModel.getSource(),d=!c||""===c.trim();if(e.node.classList.remove("flowbook-cell-stale"),e.node.classList.remove("flowbook-cell-unexecuted"),e.node.classList.remove("flowbook-cell-error"),d?this._stalenessNotice.updateStalenessNotice(e,!1,t,o):l?(e.node.classList.add("flowbook-cell-stale"),this._stalenessNotice.updateStalenessNotice(e,!0,t,o)):i?(e.node.classList.add("flowbook-cell-unexecuted"),this._stalenessNotice.updateStalenessNotice(e,!1,t,o)):this._stalenessNotice.updateStalenessNotice(e,!1,t,o),this._violationNotice.updateViolationNotice(e,o)&&e.node.classList.add("flowbook-cell-error"),this._stalenessNotice.updateStalenessMetadata(e,o),this._tracker.activeCell===e){const t=e.model.getMetadata("flowbook"),n=this._tracker.currentWidget;t&&n&&this._panel.updateMetadata(t,s,o)}}dispose(){if(!this._isDisposed){this._isDisposed=!0,this._tracker.currentChanged.disconnect(this._onNotebookChanged,this),this._tracker.activeCellChanged.disconnect(this._onActiveCellChanged,this),n.NotebookActions.executed.disconnect(this._onExecuted,this),null!==this._depPanelFrameId&&cancelAnimationFrame(this._depPanelFrameId);for(const e of this._stalenessManagers.values())e.dispose();this._stalenessManagers.clear(),this._monitoredNotebooks.clear(),this._executedInSession.clear()}}_initialize(){this._tracker.currentChanged.connect(this._onNotebookChanged,this),this._tracker.activeCellChanged.connect(this._onActiveCellChanged,this),n.NotebookActions.executed.connect(this._onExecuted,this),this._tracker.currentWidget&&this._monitorNotebook(this._tracker.currentWidget)}_onNotebookChanged(e,t){t&&this._monitorNotebook(t)}_onActiveCellChanged(e,t){const o=e.currentWidget;if(o)if(t&&"code"===t.model.type){const e=t.model.getMetadata("flowbook"),n=t.model.id,s=c(o);e?this._panel.updateMetadata(e,n,s):this._panel.clear()}else this._panel.clear();else this._panel.clear()}_onExecuted(e,t){const o=this._tracker.currentWidget;if(!o||o.content!==t.notebook)return;const n=o.context.path;let s=this._executedInSession.get(n);s||(s=new Set,this._executedInSession.set(n,s)),s.add(t.cell.model.id)}_monitorNotebook(e){const t=e.context.path;this._monitoredNotebooks.has(t)?this._updateAllCells(e):(this._monitoredNotebooks.add(t),this._updateAllCells(e),e.content.model?.cells.changed.connect(()=>{this._updateAllCells(e),this._updatePanelWithCurrentCellOrder(e)}),e.sessionContext.statusChanged.connect((o,n)=>{"restarting"===n||"autorestarting"===n?(this._pendingRestartUpdate.add(t),this._executedInSession.delete(t),this._clearAllFlowbookMetadata(e)):"idle"===n&&this._pendingRestartUpdate.has(t)&&(this._pendingRestartUpdate.delete(t),this._updateAllCells(e))}))}_updatePanelWithCurrentCellOrder(e){const t=this._tracker.activeCell;if(!t||"code"!==t.model.type)return;const o=t.model.getMetadata("flowbook");if(o){const n=t.model.id,s=c(e);this._panel.updateMetadata(o,n,s)}}_updateAllCells(e){const t=this.getStalenessManager(e),o=c(e),n=e.content.widgets,s=e.context.path;n.forEach(e=>{"code"===e.model.type&&this.updateCell(e,t,o,s)}),this.refreshDependencies()}_updateDependenciesPanel(e,t,o){if(!this._dependenciesPanel)return;const n=[],s=e.content.widgets;for(let e=0;e<s.length;e++){const l=s[e];if("code"!==l.model.type)continue;const a=l.model.id,r=o.indexOf(a);if(r<0)continue;const i=l.model.getMetadata("flowbook"),c=i?.errors||[];let h;try{h=d(r)}catch{h=a}n.push({cellId:a,index:r,label:h,readLocs:i?.read_locs||[],writeLocs:i?.write_locs||[],isStale:t.isCellStale(a),isExecuted:void 0!==i,hasError:c.length>0,violations:c})}n.sort((e,t)=>e.index-t.index),this._dependenciesPanel.updateGraph(n)}_clearAllFlowbookMetadata(e){this.getStalenessManager(e).clear(),e.content.widgets.forEach(e=>{if("code"!==e.model.type)return;e.model.deleteMetadata("flowbook"),e.model.deleteMetadata("flowbook_staleness");const t=e.model.outputs,o=[];for(let e=0;e<t.length;e++){const n=t.get(e).toJSON(),s=C(n);s.metadata?.flowbook_staleness_notice||s.metadata?.flowbook_violation_notice||o.push(n)}o.length!==t.length&&t.fromJSON(o),e.node.classList.remove("flowbook-stale-cell"),e.node.classList.remove("flowbook-unexecuted-cell"),e.node.classList.remove("flowbook-cell-stale"),e.node.classList.remove("flowbook-cell-unexecuted"),e.node.classList.remove("flowbook-cell-error")})}}class D{constructor(e,t){this._editTimers=new Map,this._executedCells=new Set,this._attachedKernel=null,this._listenedCellIds=new Set,this._comm=null,this._isDisposed=!1,this._pendingViolations=[],this._tracker=e,this._highlighter=t,this._setupHooks()}dispose(){if(!this._isDisposed){this._isDisposed=!0,n.NotebookActions.executed.disconnect(this._onCellExecuted,this),n.NotebookActions.executionScheduled.disconnect(this._onExecutionScheduled,this),this._tracker.currentChanged.disconnect(this._setupCellEditListener,this),this._tracker.currentChanged.disconnect(this._setupComm,this);for(const e of this._editTimers.values())clearTimeout(e);if(this._editTimers.clear(),this._comm){try{this._comm.close()}catch{}this._comm=null}this._attachedKernel=null}}sendCommand(e){this._comm?this._comm.send(e):console.warn("ReproducibilityExecutionHook: No comm channel, cannot send command:",e)}_setupHooks(){n.NotebookActions.executed.connect(this._onCellExecuted,this),n.NotebookActions.executionScheduled.connect(this._onExecutionScheduled,this),this._tracker.currentChanged.connect(this._setupCellEditListener,this),this._tracker.currentChanged.connect(this._setupComm,this),this._tracker.currentWidget&&(this._setupCellEditListener(),this._setupComm())}_setupCellEditListener(){const e=this._tracker.currentWidget;if(!e)return;const t=e.content;for(let e=0;e<t.widgets.length;e++)this._attachCellEditListener(t.widgets[e]);t.model?.cells.changed.connect((o,n)=>{for(let e=0;e<t.widgets.length;e++)this._attachCellEditListener(t.widgets[e]);"add"!==n.type&&"remove"!==n.type||this._sendNotebookStructure(e)})}_sendNotebookStructure(e){const t=c(e);t.length>0&&this.sendCommand({type:"notebook_structure",cell_order:t})}_attachCellEditListener(e){if("code"!==e.model.type)return;const t=e.model.id;this._listenedCellIds.has(t)||(this._listenedCellIds.add(t),e.model.sharedModel.changed.connect((e,o)=>{o.sourceChange&&this._onCellContentChanged(t)}))}_onCellContentChanged(e){if(!this._executedCells.has(e))return;const t=this._editTimers.get(e);t&&clearTimeout(t);const o=setTimeout(()=>{this._sendCellEdited(e),this._editTimers.delete(e)},1e3);this._editTimers.set(e,o)}_sendCellEdited(e){this.sendCommand({type:"cell_edited",cell_id:e})}_setupComm(){const e=this._tracker.currentWidget;e&&(this._connectComm(e),e.sessionContext.kernelChanged.connect(()=>{this._attachedKernel=null,this._connectComm(e)}),e.sessionContext.statusChanged.connect((t,o)=>{"restarting"===o?(this._attachedKernel=null,this._comm=null):"idle"===o&&null===this._comm&&this._connectComm(e)}))}_connectComm(e){const t=e.sessionContext.session?.kernel;t&&t!==this._attachedKernel&&(this._attachedKernel=t,this._comm=t.createComm("flowbook"),this._comm.onMsg=this._onCommMessage.bind(this),this._comm.open())}_onCommMessage(e){const t=e.content.data;if(!t||!t.type)return;const o=this._tracker.currentWidget;if(o)switch(t.type){case"metadata":{const{type:e,...n}=t,s=n;if(s.cell_id){const e=this._findCell(o,s.cell_id);if(e){e.model.setMetadata("flowbook",s);const t=this._getCurrentCellOrder(o),n=this._highlighter.getStalenessManager(o);this._highlighter.updateCell(e,n,t,o.context.path),this._highlighter.refreshDependencies()}}this._processMetadataUpdate(o,s);break}case"violation":{const{type:e,...o}=t,n=o;this._pendingViolations.push(n);break}case"status":this._highlighter.updateStatus(t.icon,t.text,t.cell_id)}}_onExecutionScheduled(e,t){const{notebook:o,cell:n}=t,s=this._tracker.currentWidget;if(!s||s.content!==o)return;const l=n.model.id,a=this._editTimers.get(l);a&&(clearTimeout(a),this._editTimers.delete(l));const r=c(s);r.length>0&&this.sendCommand({type:"notebook_structure",cell_order:r})}_onCellExecuted(e,t){const{notebook:o,cell:n}=t;if("code"!==n.model.type)return;this._executedCells.add(n.model.id);const s=this._tracker.currentWidget;if(!s||s.content!==o)return;const l=n.model.id;this._pendingViolations=this._pendingViolations.filter(e=>e.cell_id!==l);const a=this._getCurrentCellOrder(s),r=this._highlighter.getStalenessManager(s);this._highlighter.updateCell(n,r,a,s.context.path),this._highlighter.refreshDependencies()}_getCurrentCellOrder(e){return c(e)}_processMetadataUpdate(e,t){const o=this._highlighter.getStalenessManager(e);try{const n=new Set(o.staleCells),s=new Set(t.stale_cells),l=this._getCurrentCellOrder(e),a=[...s].filter(e=>!n.has(e));for(const n of a){const s=this._findCell(e,n);if(s){const e=s.model.sharedModel.getSource();if(!e||""===e.trim())continue}const a=t.staleness_reasons?.[n];let r;r=a&&a.length>0?this._backendReasonToFrontend(a[0],l):this._computeStalenessReason(e,n,t,l),o.setReason(n,r),s&&s.model.setMetadata("flowbook_staleness",r)}const r=[...n].filter(e=>!s.has(e));for(const t of r){const o=this._findCell(e,t);o&&o.model.deleteMetadata("flowbook_staleness")}}catch(e){console.error("ReproducibilityExecutionHook: Error computing staleness reasons:",e)}o.updateFromMetadata(t)}_backendReasonToFrontend(e,t){const o=e.cell_id,n=e.loc;let s="";if(o){const e=t.indexOf(o);s=e>=0?d(e):o}switch(e.type){case"never_executed":return{type:"unknown",message:"Cell has never been executed"};case"code_changed":return{type:"source_edited",message:"Source code was edited"};case"forward_stale":return n&&s?{type:"variable_modified",causing_cell:o,variables:[n],message:`\`${n}\` was modified by ${s}`}:{type:"variable_modified",causing_cell:o,message:s?`Input modified by ${s}`:"Input was modified"};case"write_overlap":return n&&s?{type:"writer_conflict",causing_cell:o,variables:[n],message:`Write overlap: \`${n}\` also written by ${s}`}:{type:"writer_conflict",causing_cell:o,message:s?`Write overlap with ${s}`:"Write overlap detected"};case"backward_stale":return n&&s?{type:"writer_conflict",causing_cell:o,variables:[n],message:`Write conflict on \`${n}\` with ${s}`}:{type:"writer_conflict",causing_cell:o,message:"Write conflict detected"};case"no_read_before_write":return n&&s?{type:"unknown",causing_cell:o,message:`Reads \`${n}\` from later cell ${s} (forward contamination)`}:{type:"unknown",causing_cell:o,message:"Reads from a later cell"};case"order_changed":return{type:"unknown",message:"Cell order changed"};case"no_write_after_read":return n&&s?{type:"variable_modified",causing_cell:o,variables:[n],message:`Wrote \`${n}\` read by earlier cell ${s} (backward mutation)`}:{type:"unknown",causing_cell:o,message:s?`Wrote to variable read by ${s}`:"Backward mutation detected"};default:return{type:"unknown",causing_cell:o,message:s?`Dependencies changed by ${s}`:"Cell is stale"}}}_computeStalenessReason(e,t,o,n){const s=o.cell_id;if(t===s&&(!o.changed_locs||0===o.changed_locs.length))return{type:"source_edited",causing_cell:s,message:"Source code was edited"};const l=this._findCell(e,t),a=l?.model.metadata,r=a?.flowbook,i=n.indexOf(s),c=i>=0?d(i):s,h=function(e,t){const o=[];for(const n of t)for(const t of e)if(y(t,n)){o.push(n);break}return o}(o.changed_locs||[],r?.read_locs||[]);if(h.length>0){const e=h.map(e=>"`"+v(e)+"`");return{type:"variable_modified",causing_cell:s,variables:h.map(e=>v(e)),message:`${e.join(", ")} modified by ${c}`}}const u=r?.write_locs||[],m=o.read_locs||[],_=[],f=new Set;for(const e of m){const t=`${e.type}:${e.qualifier||""}:${e.name}`;if(!f.has(t))for(const o of u)if(y(o,e)){_.push(v(e)),f.add(t);break}}if(_.length>0){const e=_.map(e=>"`"+e+"`");return{type:"writer_conflict",causing_cell:s,variables:_,message:`Writes ${e.join(", ")}, which was read by ${c}`}}return{type:"unknown",causing_cell:s,message:`Dependencies changed by ${c}`}}_findCell(e,t){const o=e.content.widgets;for(let e=0;e<o.length;e++)if(o[e].model.id===t)return o[e];return null}}class K{constructor(){this._overlays=new Map,this._observers=new Map,this._notebooks=new Map,this._listeners=new Map,this._domListeners=new Map}startMonitoring(e,t){this._overlays.set(e,new Map),this._notebooks.set(e,t),this.updateAllOverlays(e,t);const o=new MutationObserver(o=>{for(const n of o)if("childList"===n.type&&n.addedNodes.length>0)for(const o of n.addedNodes)if(o instanceof HTMLElement&&(o.classList.contains("jp-InputArea-editor")||o.querySelector(".jp-InputArea-editor")))return void this.updateAllOverlays(e,t)});o.observe(t.content.node,{childList:!0,subtree:!0}),this._observers.set(e,o);const n=[],s=()=>{this.updateAllOverlays(e,t)};t.content.model?.cells.changed.connect(s),n.push({signal:t.content.model?.cells.changed,callback:s}),this._listeners.set(e,n);const l=t.content.node,a=()=>{requestAnimationFrame(()=>{this.updateAllOverlays(e,t)})},r=[];for(const e of["cut","paste"])l.addEventListener(e,a,!0),r.push({event:e,handler:a});const i=e=>{const t=e;(t.metaKey||t.ctrlKey)&&"z"===t.key&&a()};l.addEventListener("keydown",i,!0),r.push({event:"keydown",handler:i}),this._domListeners.set(e,{node:l,handlers:r})}stopMonitoring(e){const t=this._observers.get(e);t&&(t.disconnect(),this._observers.delete(e));const o=this._overlays.get(e);o&&(o.forEach(e=>{e.remove()}),this._overlays.delete(e)),this._notebooks.delete(e);const n=this._listeners.get(e);n&&(n.forEach(({signal:e,callback:t})=>{try{e?.disconnect(t)}catch(e){}}),this._listeners.delete(e));const s=this._domListeners.get(e);if(s){for(const{event:e,handler:t}of s.handlers)s.node.removeEventListener(e,t,!0);this._domListeners.delete(e)}}updateAllOverlays(e,t){const o=this._overlays.get(e);if(!o)return;const n=new Map;let s=0;t.content.widgets.forEach(e=>{"code"===e.model.type&&(n.set(e.model.id,s),s++)}),t.content.widgets.forEach(e=>{if("code"===e.model.type){const t=e.model.id,s=n.get(t);if(void 0===s)return;const l=o.get(t);if(l&&l.parentNode)return void(l.textContent=d(s));const a=e.node.querySelector(".jp-InputArea-editor");if(a){l&&l.remove();const e=this.createOverlay(s);a.style.position="relative",a.appendChild(e),o.set(t,e)}}})}createOverlay(e){const t=document.createElement("div");return t.className="flowbook-cell-index-overlay",t.textContent=d(e),t}}var T=o(537),P=o(651);class H{constructor(e){this._highlighter=null,this._kernelDetector=e}setHighlighter(e){this._highlighter=e}createNew(e,t){const o=new T.ToolbarButton({icon:P.stepIntoIcon,tooltip:"Run all stale and unrun cells",onClick:async()=>{await this._runAllActionable(e)}});o.node.style.display="none",e.toolbar.insertItem(10,"flowbook-run-next-stale",o);const n=()=>{const t=this._kernelDetector.isFlowbookKernel(e);o.node.style.display=t?"":"none"};return e.sessionContext.ready.then(()=>{n()}),e.sessionContext.kernelChanged.connect(()=>{n()}),{dispose:()=>{o.dispose()},get isDisposed(){return o.isDisposed}}}async _runAllActionable(e){const t=e.content;for(let o=0;o<500;o++){let o=new Set;this._highlighter&&(o=this._highlighter.getStalenessManager(e).staleCells);let s=-1;const l=t.widgets;for(let e=0;e<l.length;e++){const t=l[e];if("code"!==t.model.type)continue;const n=t.model,a=n.sharedModel.getSource();if(!a||""===a.trim())continue;const r=t.model.id;if(o.has(r)||null===n.executionCount){s=e;break}}if(s<0)break;t.activeCellIndex=s,t.scrollToCell(l[s]),await n.NotebookActions.run(t,e.sessionContext);const a=l[s],r=a.model.outputs;let i=!1;if(r)for(let e=0;e<r.length;e++){const t=r.get(e);if(t&&"error"===t.type){i=!0;break}}if(i)break;const c=a.model.getMetadata("flowbook");if(c?.errors&&c.errors.length>0)break}}}var j=o(697);function q(e,t){const o=e.content.widgets;let n=0;for(let e=0;e<o.length;e++)if("code"===o[e].model.type){if(n===t)return e;n++}throw new Error(`Code cell index ${t} out of range (have ${n} code cells)`)}function B(e){const t=e.model?.outputs;if(!t)return"";const o=[];for(let e=0;e<t.length;e++){const n=t.get(e);if(!n)continue;const s=n.type;if("stream"===s)o.push(n.data?.["text/plain"]||n.text||"");else if("execute_result"===s||"display_data"===s){const e=n.data?.["text/plain"];e&&o.push(e)}else if("error"===s){const e=n.traceback;if(e){const t=String.fromCharCode(27),n=new RegExp(t+"\\[[0-9;]*m","g");o.push(e.join("\n").replace(n,""))}else o.push(`${n.ename}: ${n.evalue}`)}}return o.join("\n")}function J(e,t){const o=e.content.widgets[t];if(!o||"code"!==o.model.type)return!1;const n=o.model.outputs;if(!n)return!1;for(let e=0;e<n.length;e++){const t=n.get(e);if(t&&"error"===t.type)return!0}return!1}let V=null,z=null,U=null,Z=null;function G(e,t,o,n){V=e,z=t,U=o,Z=n}function X(){const e=Z?.currentWidget;if(!e)throw new Error("No active notebook");return e}function Y(){if(!V)throw new Error("FlowBook not active");return V.getStalenessManager(X())}class Q{constructor(e,t,o,n){this._panel=null,this._dependenciesPanel=null,this._highlighter=null,this._executionHook=null,this._isActive=!1,this._activeNotebookPath=null,this._statusListenerNotebook=null,this._app=e,this._tracker=t,this._kernelDetector=o,this._cellIndexManager=new K,this._toolbarExtension=n,this._setupKernelChangeListener(),this._checkCurrentNotebook()}_setupKernelChangeListener(){this._kernelDetector.kernelChanged.connect((e,t)=>{"flowbook_kernel"===t.currentKernel?this._activate():"flowbook_kernel"===t.previousKernel&&this._deactivate()}),this._tracker.currentChanged.connect(()=>{this._checkCurrentNotebook()})}_checkCurrentNotebook(){const e=this._tracker.currentWidget;e&&(e.sessionContext.ready.then(()=>{this._kernelDetector.isFlowbookKernel(e)?this._activate():this._deactivate()}),this._statusListenerNotebook&&this._statusListenerNotebook!==e&&this._statusListenerNotebook.sessionContext.statusChanged.disconnect(this._onStatusChanged,this),this._statusListenerNotebook!==e&&(e.sessionContext.statusChanged.connect(this._onStatusChanged,this),this._statusListenerNotebook=e))}_onStatusChanged(){const e=this._statusListenerNotebook;if(!e)return;const t=this._kernelDetector.isFlowbookKernel(e);t&&!this._isActive?this._activate():t&&this._isActive?this._writeKernelDiscovery(e):!t&&this._isActive&&this._deactivate()}_activate(){if(this._isActive)return;this._panel=new g,this._app.shell.add(this._panel,"right",{rank:510}),this._dependenciesPanel=new A,this._app.shell.add(this._dependenciesPanel,"right",{rank:520}),this._highlighter=new L(this._tracker,this._panel),this._highlighter.setDependenciesPanel(this._dependenciesPanel),this._toolbarExtension.setHighlighter(this._highlighter),this._executionHook=new D(this._tracker,this._highlighter),this._highlighter.refreshDependencies(),G(this._highlighter,this._executionHook,this._kernelDetector,this._tracker);const e=this._tracker.currentWidget;e&&(this._activeNotebookPath=e.context.path,this._cellIndexManager.startMonitoring(this._activeNotebookPath,e),this._syncInitialState(e),this._writeKernelDiscovery(e)),this._isActive=!0}_syncInitialState(e){if(!this._executionHook)return;const t=c(e);0!==t.length&&(this._executionHook.sendCommand({type:"notebook_structure",cell_order:t}),this._executionHook.sendCommand({type:"sync"}))}_writeKernelDiscovery(e){const t=e.sessionContext.session;if(!t||!t.kernel)return;const o=e.context.path,n=t.kernel.id;(async function(e="",t={}){const o=j.ServerConnection.makeSettings(),n=`${o.baseUrl}flowbook/${e}`;let s;try{s=await j.ServerConnection.makeRequest(n,t,o)}catch(e){throw new j.ServerConnection.NetworkError(e)}let l=await s.text();if(l.length>0)try{l=JSON.parse(l)}catch{}if(!s.ok){const e=l.error||l.message||l;throw new j.ServerConnection.ResponseError(s,e)}return l})(`kernel-discovery/${encodeURIComponent(o)}`,{method:"PUT",body:JSON.stringify({kernel_name:t.kernel.name,connection_file:`kernel-${n}.json`,pid:0})}).catch(()=>{})}_deactivate(){this._isActive&&(this._activeNotebookPath&&(this._cellIndexManager.stopMonitoring(this._activeNotebookPath),this._activeNotebookPath=null),this._panel&&(this._panel.dispose(),this._panel=null),this._dependenciesPanel&&(this._dependenciesPanel.dispose(),this._dependenciesPanel=null),this._highlighter&&(this._highlighter.dispose(),this._highlighter=null),this._executionHook&&(this._executionHook.dispose(),this._executionHook=null),G(null,null,null,null),this._isActive=!1)}}const ee=[{id:"flowbook:plugin",autoStart:!0,requires:[n.INotebookTracker],activate:(e,t)=>{window.app=e;const o=new l(t);!function(e,t,o){e.commands.addCommand("flowbook:exec-restore",{label:"Run with upstream state",isEnabled:()=>{try{const e=t.currentWidget;if(!e)return!1;if(!o.isFlowbookKernel(e))return!1;const n=e.content.activeCell;if(!n||"code"!==n.model.type)return!1;const s=n.model.getMetadata("flowbook"),l=s?.errors;return void 0!==l&&l.some(e=>"no_read_before_write"===e.error_type)}catch(e){return console.error("FlowBook exec-restore isEnabled error:",e),!1}},isVisible:()=>{try{const e=t.currentWidget;return!!e&&o.isFlowbookKernel(e)}catch{return!1}},execute:async()=>{const o=t.currentWidget;if(!o)return;const n=o.content.activeCell;if(!n||"code"!==n.model.type)return;const s=n.model.id,l=o.sessionContext.session;if(!l||!l.kernel)return;const a=l.kernel.requestExecute({code:`%exec_restore ${s}`,silent:!0,store_history:!1});await a.done,await e.commands.execute("notebook:run-cell")}})}(e,t,o),function(e,t,o){Z=t,U=o,e.commands.addCommand("flowbook:is-active",{execute:()=>{const e=t.currentWidget;if(!e||!U)return{active:!1};const o=U.isFlowbookKernel(e);return{active:o,kernel:o?"flowbook_kernel":e.sessionContext.session?.kernel?.name||"unknown"}}}),e.commands.addCommand("flowbook:get-metadata",{execute:e=>{const t=X(),o=e.cellIndex,n=q(t,o),s=t.content.widgets[n],l=s.model.getMetadata("flowbook");return{label:d(o),cell_id:s.model.id,...l||{},stale_cells_labels:l?.stale_cells?.map(e=>{const o=c(t).indexOf(e);return o>=0?d(o):e})}}}),e.commands.addCommand("flowbook:get-stale-cells",{execute:()=>{const e=X(),t=Y(),o=t.staleCells,n=c(e),s=[];return n.forEach((e,n)=>{if(o.has(e)){const o=t.getReason(e),l=o&&"message"in o?o.message:o?.type||"stale";s.push({index:n,label:d(n),cell_id:e,reason:l})}}),s}}),e.commands.addCommand("flowbook:get-next-actionable",{execute:()=>{const e=X(),t=e.content.widgets,o=V?Y().staleCells:new Set;let n=0,s=null,l=null;for(let a=0;a<t.length;a++){const r=t[a];if("code"!==r.model.type)continue;const i=r.model,c=i.sharedModel.getSource(),h=r.model.id,u=n;if(n++,!c||""===c.trim())continue;const m=r.model.getMetadata("flowbook");if(m?.errors&&m.errors.length>0)return{index:u,label:d(u),cell_id:h,reason:"error",error:m.errors[0].message};if(J(e,a))return{index:u,label:d(u),cell_id:h,reason:"error"};!s&&o.has(h)&&(s={index:u,label:d(u),cell_id:h,reason:"stale"}),l||null!==i.executionCount||(l={index:u,label:d(u),cell_id:h,reason:"unexecuted"})}return s||l||{done:!0}}}),e.commands.addCommand("flowbook:get-status",{execute:()=>{const e=X(),t=e.content.widgets,o=V?Y().staleCells:new Set;let n=0,s=0,l=0,a=0,r=0;for(let i=0;i<t.length;i++){const c=t[i];if("code"!==c.model.type)continue;n++;const d=c.model,h=d.sharedModel.getSource();if(!h||""===h.trim()){r++;continue}null!==d.executionCount&&s++,o.has(c.model.id)&&l++;const u=c.model.getMetadata("flowbook");u?.errors&&u.errors.length>0&&a++,J(e,i)&&a++}const i=n-r;return{total_code_cells:n,non_empty:i,executed:s,stale:l,clean:s-l,with_errors:a,reproducible:i>0&&s===i&&0===l&&0===a}}}),e.commands.addCommand("flowbook:get-cell-count",{execute:()=>{const e=X().content.widgets;let t=0,o=0;for(let n=0;n<e.length;n++)"code"===e[n].model.type?t++:"markdown"===e[n].model.type&&o++;return{total:e.length,code_cells:t,markdown_cells:o}}}),e.commands.addCommand("flowbook:get-cell",{execute:e=>{const t=X(),o=e.cellIndex,n=q(t,o),s=t.content.widgets[n],l=d(o),a=s.model,r=s.model.getMetadata("flowbook");return{label:l,cell_id:s.model.id,cell_type:s.model.type,source:a.sharedModel.getSource(),execution_count:a.executionCount,outputs_text:B(s),flowbook_meta:r||null}}}),e.commands.addCommand("flowbook:get-cell-output",{execute:e=>{const t=X(),o=e.cellIndex,n=q(t,o),s=t.content.widgets[n];return{label:d(o),outputs_text:B(s)}}}),e.commands.addCommand("flowbook:edit-cell-source",{execute:e=>{const t=X(),o=e.cellIndex,n=e.source,s=q(t,o),l=t.content.widgets[s];return l.model.sharedModel.setSource(n),{label:d(o),cell_id:l.model.id}}}),e.commands.addCommand("flowbook:move-cell",{execute:e=>{const t=X(),o=e.fromIndex,n=e.toIndex,s=q(t,o),l=q(t,n),a=t.content.model?.sharedModel;if(!a)throw new Error("No shared model");return a.moveCell(s,l),{label:d(o),newIndex:n}}}),e.commands.addCommand("flowbook:notify-structure",{execute:()=>{const e=X();if(z){const t=c(e);z.sendCommand({type:"notebook_structure",cell_order:t})}}}),e.commands.addCommand("flowbook:set-continue-after-violation",{execute:e=>{const t=e.enabled;return z&&z.sendCommand({type:"continue_after_violation",enabled:t}),{enabled:t}}}),e.commands.addCommand("flowbook:run-cell",{execute:async e=>{const t=X(),o=e.cellIndex,s=q(t,o),l=d(o);t.content.activeCellIndex=s,await n.NotebookActions.run(t.content,t.sessionContext);const a=t.content.widgets[s],r=a.model.getMetadata("flowbook"),i=J(t,s),c=function(e,t){const o=e.content.widgets[t];if(!o||"code"!==o.model.type)return[];const n=o.model.getMetadata("flowbook");return n?.errors||[]}(t,s);return{label:l,cell_id:a.model.id,status:i?"error":c.length>0?"violation":"ok",outputs_text:B(a),flowbook_meta:r||null,errors:c}}}),e.commands.addCommand("flowbook:run-actionable-cells",{execute:async()=>{X();const t=[];let o=0;for(;o<500;)try{const n=await e.commands.execute("flowbook:get-next-actionable");if(n.done)break;const s=n.index,l=n.label,a=await e.commands.execute("flowbook:run-cell",{cellIndex:s});if(o++,t.push({label:l,status:a.status,outputs_preview:(a.outputs_text||"").slice(0,200)}),"error"===a.status)break;if("violation"===a.status)break}catch(e){console.error("Error in run-actionable-cells:",e);break}return{results:t,cells_run:o,summary:await e.commands.execute("flowbook:get-status")}}})}(e,t,o);const s=new H(o);e.docRegistry.addWidgetExtension("Notebook",s),new Q(e,t,o,s)}}]}}]);
|
|
1
|
+
"use strict";(self.webpackChunkflowbook=self.webpackChunkflowbook||[]).push([[873],{873(e,t,o){o.r(t),o.d(t,{default:()=>ee});var n=o(830),s=o(602);class l{constructor(e){this._kernelChanged=new s.Signal(this),this._notebookKernels=new Map,this._tracker=e,this._setupTracking()}get kernelChanged(){return this._kernelChanged}getKernelType(e){const t=e.sessionContext.session?.kernel?.name;return this._classifyKernel(t)}isFlowbookKernel(e){return"flowbook_kernel"===this.getKernelType(e)}_classifyKernel(e){return e?"flowbook_kernel"===e?"flowbook_kernel":"other":"none"}_setupTracking(){this._tracker.forEach(e=>{this._monitorNotebook(e)}),this._tracker.widgetAdded.connect((e,t)=>{this._monitorNotebook(t)})}_monitorNotebook(e){const t=e.context.path,o=this.getKernelType(e);this._notebookKernels.set(t,o),e.sessionContext.kernelChanged.connect(()=>{const o=this._notebookKernels.get(t)||"none",n=this.getKernelType(e);o!==n&&(this._notebookKernels.set(t,n),this._kernelChanged.emit({notebook:e,previousKernel:o,currentKernel:n}))}),e.disposed.connect(()=>{this._notebookKernels.delete(t)})}}var a=o(247),r=o(345),i=o(338);function c(e){const t=[],o=e.content.widgets;for(let e=0;e<o.length;e++)"code"===o[e].model.type&&t.push(o[e].model.id);return t}function d(e,t){if(e<0)throw new Error(`Index must be non-negative (got: ${e})`);let o;if(e<26)o="@"+String.fromCharCode("A".charCodeAt(0)+e);else if(e<702){const t=e-26;o="@"+String.fromCharCode("A".charCodeAt(0)+Math.floor(t/26))+String.fromCharCode("A".charCodeAt(0)+t%26)}else{if(!(e<18278))throw new Error(`Index ${e} is too large (max supported: 18277 for @ZZZ)`);{const t=e-702;o="@"+String.fromCharCode("A".charCodeAt(0)+Math.floor(t/676))+String.fromCharCode("A".charCodeAt(0)+Math.floor(t/26%26))+String.fromCharCode("A".charCodeAt(0)+t%26)}}return t?`${o} / ${t.slice(-4)}`:o}function h(e,t){const o=t.indexOf(e);if(-1===o)return e;try{return d(o,e)}catch(t){return e}}function u(e){return void 0!==e.var_name?e.var_name:"string"==typeof e.qualifier?e.qualifier:void 0}function m(e){const t=new Map,o={var:"Var",col:"Col",cols:"Cols",rows:"Rows",file:"File"};for(const n of e){const e=o[n.type]||n.type,s=u(n);if(s){let o=t.get(s);o||(o={types:new Map},t.set(s,o));let l=o.types.get(e);l||(l=[],o.types.set(e,l)),"col"===n.type&&l.push(n.name)}else{let o=t.get(n.name);o||(o={types:new Map},t.set(n.name,o)),o.types.has(e)||o.types.set(e,[])}}return t}function _(e){return 0===e.size?r.createElement("span",{className:"flowbook-none"}," None"):r.createElement("ul",{className:"flowbook-variable-list"},Array.from(e.entries()).map(([e,t])=>{if(!Array.from(t.types.values()).some(e=>e.length>0)){const o=Array.from(t.types.keys())[0],n=o&&"Var"!==o?` (${o})`:"";return r.createElement("li",{key:e},r.createElement("code",null,e,n&&r.createElement("span",{style:{color:"#888",fontSize:"0.9em"}},n)))}return r.createElement("li",{key:e},r.createElement("code",null,e),r.createElement("ul",{className:"flowbook-loc-sublist"},Array.from(t.types.entries()).map(([e,t])=>0===t.length?r.createElement("li",{key:e},r.createElement("span",{className:"flowbook-loc-type"},e)):r.createElement("li",{key:e},r.createElement("span",{className:"flowbook-loc-type"},e,":")," ",r.createElement("code",null,t.sort().join(", "))))))}))}const f=({metadata:e,cellId:t,currentCellOrder:o})=>{if(!e)return r.createElement("div",{className:"flowbook-metadata-empty"},r.createElement("p",null,"No reproducibility metadata available."),r.createElement("p",null,"Execute a cell to see dependency tracking."));const n=e.stale_cells.length>0,s=function(e){const t=new Map,o={var:"Var",col:"Col",cols:"Cols",rows:"Rows",file:"File"};for(const n of e){const e=o[n.type]||n.type,s=u(n);if(s){let o=t.get(s);o||(o={types:new Map},t.set(s,o));let l=o.types.get(e);l||(l=[],o.types.set(e,l)),"col"===n.type&&l.push(n.name)}else{let o=t.get(n.name);o||(o={types:new Map},t.set(n.name,o)),o.types.has(e)||o.types.set(e,[])}}return t}(e.read_locs||[]),l=m(e.write_locs||[]),a=m(e.changed_locs||[]),i=function(e,t){if(e.length!==t.length)return!1;for(let o=0;o<e.length;o++)if(e[o].type!==t[o].type||e[o].name!==t[o].name||e[o].qualifier!==t[o].qualifier)return!1;return!0}(e.write_locs||[],e.changed_locs||[]);return r.createElement("div",{className:"flowbook-metadata-content"},t&&r.createElement(r.Fragment,null,r.createElement("div",{className:"flowbook-metadata-header"},"Cell: ",h(t,o)),r.createElement("div",{className:"flowbook-metadata-section"},r.createElement("div",{className:"flowbook-metadata-item"},r.createElement("span",{style:{fontSize:"0.85em",color:"#666"}},"Id: "),r.createElement("code",{style:{fontSize:"0.85em",color:"#666"}},t))),r.createElement("div",{className:"flowbook-metadata-divider"})),r.createElement("div",{className:"flowbook-metadata-section"},r.createElement("div",{className:"flowbook-metadata-item"},r.createElement("strong",null,"Execution #:")," ",e.execution_seq)),e.errors&&e.errors.length>0&&r.createElement(r.Fragment,null,r.createElement("div",{className:"flowbook-metadata-divider"}),r.createElement("div",{className:"flowbook-metadata-section flowbook-error-section"},r.createElement("div",{className:"flowbook-metadata-item"},r.createElement("strong",{style:{color:"#d32f2f"}},"Errors:"),r.createElement("ul",{className:"flowbook-error-list",style:{margin:"4px 0",paddingLeft:"16px"}},e.errors.map((e,t)=>{return r.createElement("li",{key:t,style:{marginBottom:"6px"}},r.createElement("div",{style:{fontWeight:600,color:"#d32f2f",fontSize:"0.9em"}},{no_read_and_write:"Read And Write Same Location",write_before_read:"Undefined Variable",no_read_before_write:"Forward Contamination",no_write_after_read:"Backward Mutation",unrecoverable_mutation:"Unrecoverable Mutation"}[n=e.error_type]||n),r.createElement("div",{style:{fontSize:"0.85em",color:"#333"}},e.message),e.causer_cell&&r.createElement("div",{style:{fontSize:"0.85em",color:"#666"}},"Conflicts with:"," ",r.createElement("code",null,h(e.causer_cell,o))));var n}))))),(void 0!==e.execute_duration_ms||void 0!==e.code_duration_ms||void 0!==e.state_duration_ms||void 0!==e.check_duration_ms)&&r.createElement(r.Fragment,null,r.createElement("div",{className:"flowbook-metadata-divider"}),r.createElement("div",{className:"flowbook-metadata-section"},r.createElement("div",{className:"flowbook-metadata-item"},r.createElement("strong",null,"Timing:"),r.createElement("ul",{className:"flowbook-timing-list"},void 0!==e.execute_duration_ms&&r.createElement("li",null,"Execute:"," ",r.createElement("code",null,e.execute_duration_ms.toFixed(0)," ms")),void 0!==e.code_duration_ms&&r.createElement("li",null,"Code: ",r.createElement("code",null,e.code_duration_ms.toFixed(0)," ms")),void 0!==e.state_duration_ms&&r.createElement("li",null,"State:"," ",r.createElement("code",null,e.state_duration_ms.toFixed(0)," ms")),void 0!==e.check_duration_ms&&r.createElement("li",null,"Check:"," ",r.createElement("code",null,e.check_duration_ms.toFixed(0)," ms")))))),r.createElement("div",{className:"flowbook-metadata-divider"}),r.createElement("div",{className:"flowbook-metadata-section"},r.createElement("div",{className:"flowbook-metadata-item"},r.createElement("strong",null,"Reads:"),_(s))),r.createElement("div",{className:"flowbook-metadata-divider"}),r.createElement("div",{className:"flowbook-metadata-section"},r.createElement("div",{className:"flowbook-metadata-item"},r.createElement("strong",null,i?"Writes:":"Writes (Intended):"),_(l))),!i&&r.createElement(r.Fragment,null,r.createElement("div",{className:"flowbook-metadata-divider"}),r.createElement("div",{className:"flowbook-metadata-section"},r.createElement("div",{className:"flowbook-metadata-item"},r.createElement("strong",null,"Changed (Actual):"),_(a)))),n&&r.createElement(r.Fragment,null,r.createElement("div",{className:"flowbook-metadata-divider"}),r.createElement("div",{className:"flowbook-metadata-section flowbook-stale-section"},r.createElement("div",{className:"flowbook-metadata-item"},r.createElement("strong",null,"Stale Cells:"),r.createElement("ul",{className:"flowbook-cell-list flowbook-stale"},e.stale_cells.map((e,t)=>r.createElement("li",{key:t},r.createElement("code",null,h(e,o)))))))),e.structural_warnings&&e.structural_warnings.length>0&&r.createElement(r.Fragment,null,r.createElement("div",{className:"flowbook-metadata-divider"}),r.createElement("div",{className:"flowbook-metadata-section flowbook-warning-section"},r.createElement("div",{className:"flowbook-warning-header"},"Structural Warnings"),r.createElement("div",{className:"flowbook-warning-content"},r.createElement("ul",{className:"flowbook-warning-list"},e.structural_warnings.map((e,t)=>r.createElement("li",{key:t},e)))))))},p=({icon:e,text:t,cellRef:o})=>e||t?r.createElement("div",{className:"flowbook-status-header"},r.createElement("div",{style:{fontSize:"0.7em",color:"#888",textTransform:"uppercase",letterSpacing:"0.05em",marginBottom:"2px"}},"Last Execution"),r.createElement("div",{style:{fontSize:"0.85em",fontFamily:"var(--jp-code-font-family, monospace)"}},o&&r.createElement("strong",{style:{marginRight:"6px"}},o),e," ",t)):null;class g extends a.Widget{constructor(){super(),this._metadata=null,this._cellId=null,this._currentCellOrder=[],this._statusIcon=null,this._statusText=null,this._statusCellRef=null,this._root=null,this.id="flowbook-metadata-panel",this.addClass("flowbook-metadata-panel"),this.title.label="FlowBook",this.title.closable=!0,this.title.caption="FlowBook cell metadata",this.render()}render(){this._root||(this._root=(0,i.H)(this.node)),this._root.render(r.createElement(r.Fragment,null,r.createElement(p,{icon:this._statusIcon,text:this._statusText,cellRef:this._statusCellRef}),r.createElement(f,{metadata:this._metadata,cellId:this._cellId,currentCellOrder:this._currentCellOrder})))}updateMetadata(e,t,o){this._metadata=e,this._cellId=t,this._currentCellOrder=o,this.render()}updateStatus(e,t,o){this._statusIcon=e,this._statusText=t,this._statusCellRef=o?h(o,this._currentCellOrder):null,this.render()}clear(){this._metadata=null,this._cellId=null,this._currentCellOrder=[],this.render()}dispose(){this.isDisposed||(this._root?.unmount(),this._root=null,super.dispose())}}var k=o(395);function b(e){return void 0!==e.var_name?e.var_name:"string"==typeof e.qualifier?e.qualifier:void 0}function w(e,t){return"number"==typeof e.qualifier&&"number"==typeof t.qualifier?e.qualifier===t.qualifier:b(e)===b(t)}function y(e,t){switch(e.type){case"var":return"var"===t.type&&e.name===t.name;case"col":return"col"===t.type?w(e,t)&&e.name===t.name:"cols"===t.type&&w(e,t);case"cols":return("col"===t.type||"cols"===t.type)&&w(e,t);case"rows":return("col"===t.type||"rows"===t.type)&&w(e,t);case"file":return"file"===t.type&&e.name===t.name;default:return!1}}function v(e){const t=b(e);return t?`${t}.${e.name}`:e.name}function C(e){return e}o(740);const x={bulge:function({id:e,sourceX:t,sourceY:o,targetX:n,targetY:s,style:l,markerEnd:a,label:i,labelStyle:c,data:d}){const h=d?.bulge||50,u=Math.max(t,n)+h,m=o+.25*(s-o),_=o+.75*(s-o),f=`M ${t},${o} C ${u},${m} ${u},${_} ${n},${s}`,p=.125*t+.375*u+.375*u+.125*n,g=.125*o+.375*m+.375*_+.125*s,b=i?String(i):"",w=6.5*b.length;return r.createElement(r.Fragment,null,r.createElement(k.BaseEdge,{id:e,path:f,style:l,markerEnd:a}),b&&r.createElement("g",{transform:`translate(${p}, ${g})`},r.createElement("rect",{x:-w/2-4,y:-11,width:w+8,height:22,rx:3,ry:3,fill:"white",fillOpacity:.95,stroke:"var(--jp-border-color2, #ddd)",strokeWidth:.5}),r.createElement("text",{style:c,textAnchor:"middle",dominantBaseline:"central",className:"react-flow__edge-text"},b)))}},E={cell:function({data:e}){return r.createElement("div",{className:e.className},r.createElement(k.Handle,{type:"target",position:k.Position.Top,id:"center-top",style:{opacity:0}}),r.createElement(k.Handle,{type:"source",position:k.Position.Bottom,id:"center-bottom",style:{opacity:0}}),r.createElement(k.Handle,{type:"source",position:k.Position.Right,id:"right-source",style:{opacity:0}}),r.createElement(k.Handle,{type:"target",position:k.Position.Right,id:"right-target",style:{opacity:0}}),e.label)}},N={type:k.MarkerType.ArrowClosed,color:"#999",width:12,height:12},S={type:k.MarkerType.ArrowClosed,color:"#2472c8",width:14,height:14},M={type:k.MarkerType.ArrowClosed,color:"#dc3545",width:14,height:14};const $=({cellData:e})=>{const[t,o]=r.useState(null),{nodes:n,allEdges:s}=r.useMemo(()=>function(e){return{nodes:e.map((e,t)=>{let o="flowbook-dep-node";return e.hasError?o+=" flowbook-dep-node-error":e.isStale?o+=" flowbook-dep-node-stale":e.isExecuted||(o+=" flowbook-dep-node-unexecuted"),{id:e.cellId,type:"cell",position:{x:40,y:80*t},data:{label:e.label,className:o}}}),allEdges:function(e){const t=[];for(let o=0;o<e.length;o++)if(e[o].isExecuted)for(let n=o+1;n<e.length;n++){if(!e[n].isExecuted)continue;const s=n-o,l=[],a=new Set;for(const t of e[o].writeLocs)for(const o of e[n].readLocs)if(y(t,o)){const e=v(o);a.has(e)||(a.add(e),l.push(e))}l.length>0&&t.push({sourceId:e[o].cellId,targetId:e[n].cellId,kind:"data_dependency",locations:l,span:s,lane:0})}const o=new Map;for(let t=0;t<e.length;t++)o.set(e[t].cellId,t);const n=new Set;for(const s of e)for(const e of s.violations){if(!e.causer_cell)continue;const l=e.causer_cell.startsWith("@")?e.causer_cell.slice(1):e.causer_cell,a=o.get(s.cellId),r=o.get(l);if(void 0===a||void 0===r)continue;let i,c;"no_write_after_read"===e.error_type?(i=s.cellId,c=l):"no_read_before_write"===e.error_type?(i=l,c=s.cellId):(i=s.cellId,c=l);const d=`${i}-${c}-violation`;if(n.has(d))continue;n.add(d);const h=o.get(i)??0,u=o.get(c)??0,m=Math.abs(h-u);t.push({sourceId:i,targetId:c,kind:"violation",locations:e.locations,span:m||1,lane:0})}t.sort((e,t)=>e.span-t.span);for(let e=0;e<t.length;e++)t[e].lane=e+1;const s=[];for(let t=0;t<e.length-1;t++)s.push({sourceId:e[t].cellId,targetId:e[t+1].cellId,kind:"program_order",locations:[],span:1,lane:0});return s.concat(t)}(e).map(e=>{const t=`${e.sourceId}-${e.targetId}-${e.kind}`;if("program_order"===e.kind)return{id:t,source:e.sourceId,target:e.targetId,sourceHandle:"center-bottom",targetHandle:"center-top",type:"straight",style:{stroke:"#999",strokeWidth:1},markerEnd:N,data:{edgeKind:"program_order"}};const o=40+25*e.lane,n="violation"===e.kind;return{id:t,source:e.sourceId,target:e.targetId,sourceHandle:"right-source",targetHandle:"right-target",type:"bulge",data:{bulge:o,edgeKind:e.kind},label:(s=e.locations,0===s.length?"":s.length<=3?s.join(", "):s.slice(0,3).join(", ")+", ..."),labelStyle:{fontSize:10,fontFamily:"monospace",fill:n?"#dc3545":void 0},style:n?{stroke:"#dc3545",strokeWidth:2,strokeDasharray:"5,5"}:{stroke:"#2472c8",strokeWidth:2},markerEnd:n?M:S};var s})}}(e),[e]),l=r.useMemo(()=>s.filter(e=>{const o=e.data?.edgeKind;return"program_order"===o||"violation"===o||null!==t&&(e.source===t||e.target===t)}),[s,t]),a=r.useCallback((e,t)=>{o(t.id)},[]),i=r.useCallback(()=>{o(null)},[]);return r.createElement(k.ReactFlow,{nodes:n,edges:l,nodeTypes:E,edgeTypes:x,onNodeMouseEnter:a,onNodeMouseLeave:i,nodesDraggable:!1,nodesConnectable:!1,elementsSelectable:!1,fitView:!0,fitViewOptions:{padding:.15,minZoom:.5,maxZoom:1.2},minZoom:.3,maxZoom:2},r.createElement(k.Controls,{showInteractive:!1}))},I=({cellData:e})=>0===e.length?r.createElement("div",{className:"flowbook-metadata-empty"},r.createElement("p",null,"No cells to display."),r.createElement("p",null,"Execute cells to see dependencies.")):r.createElement("div",{className:"flowbook-dependencies-graph",style:{width:"100%",height:"100%",position:"relative"}},r.createElement("div",{style:{position:"absolute",top:0,left:0,right:0,bottom:0}},r.createElement(k.ReactFlowProvider,null,r.createElement($,{cellData:e}))));class A extends a.Widget{constructor(){super(),this._cellData=[],this._root=null,this.id="flowbook-dependencies-panel",this.addClass("flowbook-dependencies-panel"),this.title.label="Dependencies",this.title.closable=!0,this.title.caption="Cell dependency graph",this.render()}render(){this._root||(this._root=(0,i.H)(this.node)),this._root.render(r.createElement(I,{cellData:this._cellData}))}updateGraph(e){this._cellData=e,this.render()}clear(){this._cellData=[],this.render()}dispose(){this.isDisposed||(this._root?.unmount(),this._root=null,super.dispose())}}class R{constructor(e){this._staleCells=new Set,this._stalenessReasons=new Map,this._stalenessChanged=new s.Signal(this),this._notebook=e,this._setupKernelRestartListener()}get stalenessChanged(){return this._stalenessChanged}get staleCells(){return this._staleCells}isCellStale(e){return this._staleCells.has(e)}setReasons(e,t){this._stalenessReasons.set(e,t)}getReasons(e){return this._stalenessReasons.get(e)||[]}setReason(e,t){this._stalenessReasons.set(e,[t])}getReason(e){const t=this._stalenessReasons.get(e);return t&&t.length>0?t[0]:void 0}updateFromMetadata(e){const t=new Set(this._staleCells);this._staleCells=new Set(e.stale_cells);const o=new Set(e.stale_cells),n=[...o].filter(e=>!t.has(e)),s=[...t].filter(e=>!o.has(e));for(const e of s)this._stalenessReasons.delete(e);let l=!1;if(e.staleness_reasons)for(const[t,o]of Object.entries(e.staleness_reasons)){const e=this._stalenessReasons.get(t);JSON.stringify(e||[])!==JSON.stringify(o)&&(l=!0),this._stalenessReasons.set(t,o)}(n.length>0||s.length>0||l)&&this._stalenessChanged.emit({added:n,removed:s,current:[...this._staleCells]})}clear(){const e=[...this._staleCells];this._staleCells.clear(),this._stalenessReasons.clear(),e.length>0&&this._stalenessChanged.emit({added:[],removed:e,current:[]})}_setupKernelRestartListener(){this._notebook.sessionContext.statusChanged.connect((e,t)=>{"restarting"!==t&&"autorestarting"!==t||this.clear()})}dispose(){this._staleCells.clear(),this._stalenessReasons.clear()}}function O(e){return"message"in e}class W{updateStalenessNotice(e,t,o,n){if("code"!==e.model.type)return;const s=e.model.outputs,l=e.model.getMetadata("flowbook"),a=l?.errors&&l.errors.length>0;let r=!1;for(let e=0;e<s.length;e++){const t=C(s.get(e).toJSON());if(!0===t.metadata?.flowbook_violation_notice){r=!0;break}}if(a||r)return void this._removeNoticesByKey(s,"flowbook_staleness_notice");const i=s.length>0&&!0===s.get(0).toJSON().metadata?.flowbook_staleness_notice;if(t){const t=o.getReason(e.model.id)||{type:"unknown",message:"Dependencies changed"};if("never_executed"===t.type)return void(i&&this._removeNoticesByKey(s,"flowbook_staleness_notice"));const l=this.formatStalenessMessage(t,n,e.model.id),a=l.replace(/`([^`]+)`/g,"<code>$1</code>"),r="writer_conflict"===t.type?"Unresolved Violation":"",c=r?`⚠️ ${r}: ${l}`:`⚠️ ${l}`,d={output_type:"display_data",data:{"text/html":r?`<div class="flowbook-staleness-notice">⚠️ <b>${r}</b>: ${a} </div>`:`<div class="flowbook-staleness-notice">⚠️ ${a} </div>`,"text/plain":c},metadata:{flowbook_staleness_notice:!0}};if(i){const e=s.get(0).toJSON().data?.["text/plain"];if(e===c)return}const h=[d];for(let e=0;e<s.length;e++){const t=s.get(e).toJSON();C(t).metadata?.flowbook_staleness_notice||h.push(t)}s.fromJSON(h)}else i&&this._removeNoticesByKey(s,"flowbook_staleness_notice")}formatStalenessMessage(e,t,o){return O(e)?this._formatFrontendReason(e,t,o):this._formatBackendReason(e,t,o)}updateStalenessMetadata(e,t){const o=e.model.getMetadata("flowbook_staleness");if(!o)return;if(!O(o)||!o.causing_cell)return;const n=this.formatStalenessMessage(o,t,e.model.id);n!==o.message&&e.model.setMetadata("flowbook_staleness",{...o,message:n})}_formatFrontendReason(e,t,o){if("source_edited"===e.type)return"Source code was edited";if(!e.causing_cell)return e.message;const n=t.indexOf(e.causing_cell),s=t.indexOf(o),l=n<0,a=l?"a deleted cell":d(n),r=!l&&s>=0&&n<s?" above":l?"":" below",i=[];if(e.variables)for(const t of e.variables)i.push("`"+t+"`");if(e.columns)for(const[t,o]of Object.entries(e.columns))for(const e of o)i.push("`"+t+"."+e+"`");return"writer_conflict"===e.type&&i.length>0?`Writes ${i.join(", ")} already read by ${a}${r}`:i.length>0?`${i.join(", ")} modified by ${a}${r}`:"unknown"===e.type?`Dependencies modified by ${a}`:e.message}_formatBackendReason(e,t,o){const n="cell_id"in e?e.cell_id:void 0,s="loc"in e?e.loc:void 0,l=t.indexOf(o);let a="",r="",i=!1;if(n){const e=t.indexOf(n);i=e<0,a=i?"a deleted cell":d(e),!i&&l>=0&&(r=e<l?" above":" below")}switch(e.type){case"never_executed":return"Cell has never been executed";case"code_changed":return"Source code was edited";case"forward_stale":return s&&a?`\`${s}\` modified by ${a}${r}`:a?`Input modified by ${a}${r}`:"Input was modified";case"write_overlap":return s&&a?`\`${s}\` also written by ${a}`:a?`Writes conflict with ${a}`:"Write conflict detected";case"backward_stale":return s&&a?`\`${s}\` write conflict with ${a}`:"Write conflict detected";case"no_read_before_write":return s&&a?`Reads \`${s}\` written by ${a} ${r}`:"Reads value written by another cell";case"order_changed":return"Cell order changed";case"no_write_after_read":return s&&a?`Writes \`${s}\` already read by ${a} ${r}`:a?`Writes variable already read by ${a} ${r}`:"Writes variable already read by another cell";default:return"Cell is stale"}}_removeNoticesByKey(e,t){const o=[];let n=!1;for(let s=0;s<e.length;s++){const l=e.get(s).toJSON();C(l).metadata?.[t]?n=!0:o.push(l)}n&&e.fromJSON(o)}}class F{updateViolationNotice(e,t){if("code"!==e.model.type)return!1;const o=e.model.outputs,n=e.model.getMetadata("flowbook"),s=n?.errors;let l=!1,a="";for(let e=0;e<o.length;e++){const t=C(o.get(e).toJSON());if(!0===t.metadata?.flowbook_violation_notice){l=!0,a=t.data?.["text/plain"]||"";break}}if(s&&s.length>0){const{noticeOutput:e,plainText:n}=this._buildViolationNotice(s,t);if(l&&a===n)return!0;const r=[e];for(let e=0;e<o.length;e++){const t=o.get(e).toJSON(),n=C(t),s=!0===n.metadata?.flowbook_violation_notice,l=!0===n.metadata?.flowbook_staleness_notice,a="error"===t.output_type&&("ReproducibilityError"===n.ename||"ReproducibilityViolation"===n.ename),i="display_data"===t.output_type&&n.metadata?.predicate_violation;s||l||a||i||r.push(t)}return o.fromJSON(r),!0}if(l){const e=[];for(let t=0;t<o.length;t++){const n=o.get(t).toJSON(),s=C(n);s.metadata?.flowbook_violation_notice||e.push(n)}o.fromJSON(e)}return!1}_buildViolationNotice(e,t){const o=new Map;for(const n of e){let e=null;if(n.causer_cell&&"string"==typeof n.causer_cell){const o=n.causer_cell.startsWith("@")?n.causer_cell.slice(1):n.causer_cell,s=t.indexOf(o);e=s>=0?d(s):"a deleted cell"}const s=[...n.locations].sort().join(","),l=`${n.error_type}:${s}`;o.has(l)||o.set(l,{errorType:n.error_type,locs:n.locations,causers:[]}),e&&!o.get(l).causers.includes(e)&&o.get(l).causers.push(e)}const n=[],s=[];for(const e of o.values()){const t=e.locs.map(e=>"`"+e+"`").join(", ").replace(/`([^`]+)`/g,"<code>$1</code>"),o=e.causers.join(", ");let l;switch(e.errorType){case"no_write_after_read":l=o?`Writes ${t} already read by ${o}`:`Writes ${t} already read by cell above`;for(const t of e.locs)if(t.includes(".")){const[e,o]=t.split(".");l+=`<br>Use <code>${e}["${o}"]</code> = ... for full-column assignment`}break;case"no_read_before_write":l=o?`Reads ${t} written by ${o} below`:`Reads ${t} written by cell below`;break;case"no_read_and_write":l=`Reads and writes ${t}`;break;case"write_before_read":l=`${t} not defined by any cell above`;break;case"unrecoverable_mutation":l=`${t} was modified in place, which violates rerun consistency`;break;default:l=`Violation on ${t}`}n.push(l),s.push(l.replace(/<code>([^<]+)<\/code>/g,"`$1`"))}const l=n.map(e=>`<div>❌ ${e}</div>`).join(""),a=s.map(e=>`❌ ${e}`).join("\n");return{noticeOutput:{output_type:"display_data",data:{"text/html":`<div class="flowbook-error-notice">${l}</div>`,"text/plain":a},metadata:{flowbook_violation_notice:!0,flowbook_predicate_accepted:e[0].accepted,flowbook_violation_count:e.length}},plainText:a}}}class L{constructor(e,t){this._dependenciesPanel=null,this._depPanelFrameId=null,this._stalenessManagers=new Map,this._pendingRestartUpdate=new Set,this._executedInSession=new Map,this._monitoredNotebooks=new Set,this._stalenessNotice=new W,this._violationNotice=new F,this._isDisposed=!1,this._tracker=e,this._panel=t,this._initialize()}setDependenciesPanel(e){this._dependenciesPanel=e}refreshDependencies(){null!==this._depPanelFrameId&&cancelAnimationFrame(this._depPanelFrameId),this._depPanelFrameId=requestAnimationFrame(()=>{this._depPanelFrameId=null;const e=this._tracker.currentWidget;if(e){const t=this.getStalenessManager(e),o=c(e);this._updateDependenciesPanel(e,t,o)}})}updateStatus(e,t,o){this._panel.updateStatus(e,t,o)}getStalenessManager(e){const t=e.context.path;let o=this._stalenessManagers.get(t);return o||(o=new R(e),this._stalenessManagers.set(t,o),o.stalenessChanged.connect(()=>{this._updateAllCells(e)}),e.disposed.connect(()=>{o?.dispose(),this._stalenessManagers.delete(t),this._monitoredNotebooks.delete(t)})),o}updateCell(e,t,o,n){const s=e.model.id,l=t.isCellStale(s),a=this._executedInSession.get(n),r=void 0!==e.model.getMetadata("flowbook"),i=!(a&&a.has(s)||r),c=e.model.sharedModel.getSource(),d=!c||""===c.trim();if(e.node.classList.remove("flowbook-cell-stale"),e.node.classList.remove("flowbook-cell-unexecuted"),e.node.classList.remove("flowbook-cell-error"),d?this._stalenessNotice.updateStalenessNotice(e,!1,t,o):l?(e.node.classList.add("flowbook-cell-stale"),this._stalenessNotice.updateStalenessNotice(e,!0,t,o)):i?(e.node.classList.add("flowbook-cell-unexecuted"),this._stalenessNotice.updateStalenessNotice(e,!1,t,o)):this._stalenessNotice.updateStalenessNotice(e,!1,t,o),this._violationNotice.updateViolationNotice(e,o)&&e.node.classList.add("flowbook-cell-error"),this._stalenessNotice.updateStalenessMetadata(e,o),this._tracker.activeCell===e){const t=e.model.getMetadata("flowbook"),n=this._tracker.currentWidget;t&&n&&this._panel.updateMetadata(t,s,o)}}dispose(){if(!this._isDisposed){this._isDisposed=!0,this._tracker.currentChanged.disconnect(this._onNotebookChanged,this),this._tracker.activeCellChanged.disconnect(this._onActiveCellChanged,this),n.NotebookActions.executed.disconnect(this._onExecuted,this),null!==this._depPanelFrameId&&cancelAnimationFrame(this._depPanelFrameId);for(const e of this._stalenessManagers.values())e.dispose();this._stalenessManagers.clear(),this._monitoredNotebooks.clear(),this._executedInSession.clear()}}_initialize(){this._tracker.currentChanged.connect(this._onNotebookChanged,this),this._tracker.activeCellChanged.connect(this._onActiveCellChanged,this),n.NotebookActions.executed.connect(this._onExecuted,this),this._tracker.currentWidget&&this._monitorNotebook(this._tracker.currentWidget)}_onNotebookChanged(e,t){t&&this._monitorNotebook(t)}_onActiveCellChanged(e,t){const o=e.currentWidget;if(o)if(t&&"code"===t.model.type){const e=t.model.getMetadata("flowbook"),n=t.model.id,s=c(o);e?this._panel.updateMetadata(e,n,s):this._panel.clear()}else this._panel.clear();else this._panel.clear()}_onExecuted(e,t){const o=this._tracker.currentWidget;if(!o||o.content!==t.notebook)return;const n=o.context.path;let s=this._executedInSession.get(n);s||(s=new Set,this._executedInSession.set(n,s)),s.add(t.cell.model.id)}_monitorNotebook(e){const t=e.context.path;this._monitoredNotebooks.has(t)?this._updateAllCells(e):(this._monitoredNotebooks.add(t),this._updateAllCells(e),e.content.model?.cells.changed.connect(()=>{this._updateAllCells(e),this._updatePanelWithCurrentCellOrder(e)}),e.sessionContext.statusChanged.connect((o,n)=>{"restarting"===n||"autorestarting"===n?(this._pendingRestartUpdate.add(t),this._executedInSession.delete(t),this._clearAllFlowbookMetadata(e)):"idle"===n&&this._pendingRestartUpdate.has(t)&&(this._pendingRestartUpdate.delete(t),this._updateAllCells(e))}))}_updatePanelWithCurrentCellOrder(e){const t=this._tracker.activeCell;if(!t||"code"!==t.model.type)return;const o=t.model.getMetadata("flowbook");if(o){const n=t.model.id,s=c(e);this._panel.updateMetadata(o,n,s)}}_updateAllCells(e){const t=this.getStalenessManager(e),o=c(e),n=e.content.widgets,s=e.context.path;n.forEach(e=>{"code"===e.model.type&&this.updateCell(e,t,o,s)}),this.refreshDependencies()}_updateDependenciesPanel(e,t,o){if(!this._dependenciesPanel)return;const n=[],s=e.content.widgets;for(let e=0;e<s.length;e++){const l=s[e];if("code"!==l.model.type)continue;const a=l.model.id,r=o.indexOf(a);if(r<0)continue;const i=l.model.getMetadata("flowbook"),c=i?.errors||[];let h;try{h=d(r)}catch{h=a}n.push({cellId:a,index:r,label:h,readLocs:i?.read_locs||[],writeLocs:i?.write_locs||[],isStale:t.isCellStale(a),isExecuted:void 0!==i,hasError:c.length>0,violations:c})}n.sort((e,t)=>e.index-t.index),this._dependenciesPanel.updateGraph(n)}_clearAllFlowbookMetadata(e){this.getStalenessManager(e).clear(),e.content.widgets.forEach(e=>{if("code"!==e.model.type)return;e.model.deleteMetadata("flowbook"),e.model.deleteMetadata("flowbook_staleness");const t=e.model.outputs,o=[];for(let e=0;e<t.length;e++){const n=t.get(e).toJSON(),s=C(n);s.metadata?.flowbook_staleness_notice||s.metadata?.flowbook_violation_notice||o.push(n)}o.length!==t.length&&t.fromJSON(o),e.node.classList.remove("flowbook-stale-cell"),e.node.classList.remove("flowbook-unexecuted-cell"),e.node.classList.remove("flowbook-cell-stale"),e.node.classList.remove("flowbook-cell-unexecuted"),e.node.classList.remove("flowbook-cell-error")})}}class D{constructor(e,t){this._editTimers=new Map,this._executedCells=new Set,this._attachedKernel=null,this._listenedCellIds=new Set,this._comm=null,this._isDisposed=!1,this._pendingViolations=[],this._tracker=e,this._highlighter=t,this._setupHooks()}dispose(){if(!this._isDisposed){this._isDisposed=!0,n.NotebookActions.executed.disconnect(this._onCellExecuted,this),n.NotebookActions.executionScheduled.disconnect(this._onExecutionScheduled,this),this._tracker.currentChanged.disconnect(this._setupCellEditListener,this),this._tracker.currentChanged.disconnect(this._setupComm,this);for(const e of this._editTimers.values())clearTimeout(e);if(this._editTimers.clear(),this._comm){try{this._comm.close()}catch{}this._comm=null}this._attachedKernel=null}}sendCommand(e){this._comm?this._comm.send(e):console.warn("ReproducibilityExecutionHook: No comm channel, cannot send command:",e)}_setupHooks(){n.NotebookActions.executed.connect(this._onCellExecuted,this),n.NotebookActions.executionScheduled.connect(this._onExecutionScheduled,this),this._tracker.currentChanged.connect(this._setupCellEditListener,this),this._tracker.currentChanged.connect(this._setupComm,this),this._tracker.currentWidget&&(this._setupCellEditListener(),this._setupComm())}_setupCellEditListener(){const e=this._tracker.currentWidget;if(!e)return;const t=e.content;for(let e=0;e<t.widgets.length;e++)this._attachCellEditListener(t.widgets[e]);t.model?.cells.changed.connect((o,n)=>{for(let e=0;e<t.widgets.length;e++)this._attachCellEditListener(t.widgets[e]);"add"!==n.type&&"remove"!==n.type||this._sendNotebookStructure(e)})}_sendNotebookStructure(e){const t=c(e);t.length>0&&this.sendCommand({type:"notebook_structure",cell_order:t})}_attachCellEditListener(e){if("code"!==e.model.type)return;const t=e.model.id;if(this._listenedCellIds.has(t))return;this._listenedCellIds.add(t);const o=e.model;o.sharedModel.changed.connect((e,n)=>{n.sourceChange&&this._onCellContentChanged(t,o)})}_onCellContentChanged(e,t){if(!this._executedCells.has(e))return;const o=this._editTimers.get(e);o&&clearTimeout(o);const n=setTimeout(()=>{this._sendCellEdited(e,t),this._editTimers.delete(e)},1e3);this._editTimers.set(e,n)}_sendCellEdited(e,t){const o=t.sharedModel.getSource();this.sendCommand({type:"cell_edited",cell_id:e,source:o})}_setupComm(){const e=this._tracker.currentWidget;e&&(this._connectComm(e),e.sessionContext.kernelChanged.connect(()=>{this._attachedKernel=null,this._connectComm(e)}),e.sessionContext.statusChanged.connect((t,o)=>{"restarting"===o?(this._attachedKernel=null,this._comm=null):"idle"===o&&null===this._comm&&this._connectComm(e)}))}_connectComm(e){const t=e.sessionContext.session?.kernel;t&&t!==this._attachedKernel&&(this._attachedKernel=t,this._comm=t.createComm("flowbook"),this._comm.onMsg=this._onCommMessage.bind(this),this._comm.open())}_onCommMessage(e){const t=e.content.data;if(!t||!t.type)return;const o=this._tracker.currentWidget;if(o)switch(t.type){case"metadata":{const{type:e,...n}=t,s=n;if(s.cell_id){const e=this._findCell(o,s.cell_id);if(e){e.model.setMetadata("flowbook",s);const t=this._getCurrentCellOrder(o),n=this._highlighter.getStalenessManager(o);this._highlighter.updateCell(e,n,t,o.context.path),this._highlighter.refreshDependencies()}}this._processMetadataUpdate(o,s);break}case"violation":{const{type:e,...o}=t,n=o;this._pendingViolations.push(n);break}case"status":this._highlighter.updateStatus(t.icon,t.text,t.cell_id)}}_onExecutionScheduled(e,t){const{notebook:o,cell:n}=t,s=this._tracker.currentWidget;if(!s||s.content!==o)return;const l=n.model.id,a=this._editTimers.get(l);a&&(clearTimeout(a),this._editTimers.delete(l));const r=c(s);r.length>0&&this.sendCommand({type:"notebook_structure",cell_order:r})}_onCellExecuted(e,t){const{notebook:o,cell:n}=t;if("code"!==n.model.type)return;this._executedCells.add(n.model.id);const s=this._tracker.currentWidget;if(!s||s.content!==o)return;const l=n.model.id;this._pendingViolations=this._pendingViolations.filter(e=>e.cell_id!==l);const a=this._getCurrentCellOrder(s),r=this._highlighter.getStalenessManager(s);this._highlighter.updateCell(n,r,a,s.context.path),this._highlighter.refreshDependencies()}_getCurrentCellOrder(e){return c(e)}_processMetadataUpdate(e,t){const o=this._highlighter.getStalenessManager(e);try{const n=new Set(o.staleCells),s=new Set(t.stale_cells),l=this._getCurrentCellOrder(e),a=[...s].filter(e=>!n.has(e));for(const n of a){const s=this._findCell(e,n);if(s){const e=s.model.sharedModel.getSource();if(!e||""===e.trim())continue}const a=t.staleness_reasons?.[n];let r;r=a&&a.length>0?this._backendReasonToFrontend(a[0],l):this._computeStalenessReason(e,n,t,l),o.setReason(n,r),s&&s.model.setMetadata("flowbook_staleness",r)}const r=[...n].filter(e=>!s.has(e));for(const t of r){const o=this._findCell(e,t);o&&o.model.deleteMetadata("flowbook_staleness")}}catch(e){console.error("ReproducibilityExecutionHook: Error computing staleness reasons:",e)}o.updateFromMetadata(t)}_backendReasonToFrontend(e,t){const o=e.cell_id,n=e.loc;let s="";if(o){const e=t.indexOf(o);s=e>=0?d(e):o}switch(e.type){case"never_executed":return{type:"unknown",message:"Cell has never been executed"};case"code_changed":return{type:"source_edited",message:"Source code was edited"};case"forward_stale":return n&&s?{type:"variable_modified",causing_cell:o,variables:[n],message:`\`${n}\` was modified by ${s}`}:{type:"variable_modified",causing_cell:o,message:s?`Input modified by ${s}`:"Input was modified"};case"write_overlap":return n&&s?{type:"writer_conflict",causing_cell:o,variables:[n],message:`Write overlap: \`${n}\` also written by ${s}`}:{type:"writer_conflict",causing_cell:o,message:s?`Write overlap with ${s}`:"Write overlap detected"};case"backward_stale":return n&&s?{type:"writer_conflict",causing_cell:o,variables:[n],message:`Write conflict on \`${n}\` with ${s}`}:{type:"writer_conflict",causing_cell:o,message:"Write conflict detected"};case"no_read_before_write":return n&&s?{type:"unknown",causing_cell:o,message:`Reads \`${n}\` from later cell ${s} (forward contamination)`}:{type:"unknown",causing_cell:o,message:"Reads from a later cell"};case"order_changed":return{type:"unknown",message:"Cell order changed"};case"no_write_after_read":return n&&s?{type:"variable_modified",causing_cell:o,variables:[n],message:`Wrote \`${n}\` read by earlier cell ${s} (backward mutation)`}:{type:"unknown",causing_cell:o,message:s?`Wrote to variable read by ${s}`:"Backward mutation detected"};default:return{type:"unknown",causing_cell:o,message:s?`Dependencies changed by ${s}`:"Cell is stale"}}}_computeStalenessReason(e,t,o,n){const s=o.cell_id;if(t===s&&(!o.changed_locs||0===o.changed_locs.length))return{type:"source_edited",causing_cell:s,message:"Source code was edited"};const l=this._findCell(e,t),a=l?.model.metadata,r=a?.flowbook,i=n.indexOf(s),c=i>=0?d(i):s,h=function(e,t){const o=[];for(const n of t)for(const t of e)if(y(t,n)){o.push(n);break}return o}(o.changed_locs||[],r?.read_locs||[]);if(h.length>0){const e=h.map(e=>"`"+v(e)+"`");return{type:"variable_modified",causing_cell:s,variables:h.map(e=>v(e)),message:`${e.join(", ")} modified by ${c}`}}const u=r?.write_locs||[],m=o.read_locs||[],_=[],f=new Set;for(const e of m){const t=`${e.type}:${e.qualifier||""}:${e.name}`;if(!f.has(t))for(const o of u)if(y(o,e)){_.push(v(e)),f.add(t);break}}if(_.length>0){const e=_.map(e=>"`"+e+"`");return{type:"writer_conflict",causing_cell:s,variables:_,message:`Writes ${e.join(", ")}, which was read by ${c}`}}return{type:"unknown",causing_cell:s,message:`Dependencies changed by ${c}`}}_findCell(e,t){const o=e.content.widgets;for(let e=0;e<o.length;e++)if(o[e].model.id===t)return o[e];return null}}class K{constructor(){this._overlays=new Map,this._observers=new Map,this._notebooks=new Map,this._listeners=new Map,this._domListeners=new Map}startMonitoring(e,t){this._overlays.set(e,new Map),this._notebooks.set(e,t),this.updateAllOverlays(e,t);const o=new MutationObserver(o=>{for(const n of o)if("childList"===n.type&&n.addedNodes.length>0)for(const o of n.addedNodes)if(o instanceof HTMLElement&&(o.classList.contains("jp-InputArea-editor")||o.querySelector(".jp-InputArea-editor")))return void this.updateAllOverlays(e,t)});o.observe(t.content.node,{childList:!0,subtree:!0}),this._observers.set(e,o);const n=[],s=()=>{this.updateAllOverlays(e,t)};t.content.model?.cells.changed.connect(s),n.push({signal:t.content.model?.cells.changed,callback:s}),this._listeners.set(e,n);const l=t.content.node,a=()=>{requestAnimationFrame(()=>{this.updateAllOverlays(e,t)})},r=[];for(const e of["cut","paste"])l.addEventListener(e,a,!0),r.push({event:e,handler:a});const i=e=>{const t=e;(t.metaKey||t.ctrlKey)&&"z"===t.key&&a()};l.addEventListener("keydown",i,!0),r.push({event:"keydown",handler:i}),this._domListeners.set(e,{node:l,handlers:r})}stopMonitoring(e){const t=this._observers.get(e);t&&(t.disconnect(),this._observers.delete(e));const o=this._overlays.get(e);o&&(o.forEach(e=>{e.remove()}),this._overlays.delete(e)),this._notebooks.delete(e);const n=this._listeners.get(e);n&&(n.forEach(({signal:e,callback:t})=>{try{e?.disconnect(t)}catch(e){}}),this._listeners.delete(e));const s=this._domListeners.get(e);if(s){for(const{event:e,handler:t}of s.handlers)s.node.removeEventListener(e,t,!0);this._domListeners.delete(e)}}updateAllOverlays(e,t){const o=this._overlays.get(e);if(!o)return;const n=new Map;let s=0;t.content.widgets.forEach(e=>{"code"===e.model.type&&(n.set(e.model.id,s),s++)}),t.content.widgets.forEach(e=>{if("code"===e.model.type){const t=e.model.id,s=n.get(t);if(void 0===s)return;const l=o.get(t);if(l&&l.parentNode)return void(l.textContent=d(s));const a=e.node.querySelector(".jp-InputArea-editor");if(a){l&&l.remove();const e=this.createOverlay(s);a.style.position="relative",a.appendChild(e),o.set(t,e)}}})}createOverlay(e){const t=document.createElement("div");return t.className="flowbook-cell-index-overlay",t.textContent=d(e),t}}var T=o(516),P=o(562);class H{constructor(e){this._highlighter=null,this._kernelDetector=e}setHighlighter(e){this._highlighter=e}createNew(e,t){const o=new T.ToolbarButton({icon:P.stepIntoIcon,tooltip:"Run all stale and unrun cells",onClick:async()=>{await this._runAllActionable(e)}});o.node.style.display="none",e.toolbar.insertItem(10,"flowbook-run-next-stale",o);const n=()=>{const t=this._kernelDetector.isFlowbookKernel(e);o.node.style.display=t?"":"none"};return e.sessionContext.ready.then(()=>{n()}),e.sessionContext.kernelChanged.connect(()=>{n()}),{dispose:()=>{o.dispose()},get isDisposed(){return o.isDisposed}}}async _runAllActionable(e){const t=e.content;for(let o=0;o<500;o++){let o=new Set;this._highlighter&&(o=this._highlighter.getStalenessManager(e).staleCells);let s=-1;const l=t.widgets;for(let e=0;e<l.length;e++){const t=l[e];if("code"!==t.model.type)continue;const n=t.model,a=n.sharedModel.getSource();if(!a||""===a.trim())continue;const r=t.model.id;if(o.has(r)||null===n.executionCount){s=e;break}}if(s<0)break;t.activeCellIndex=s,t.scrollToCell(l[s]),await n.NotebookActions.run(t,e.sessionContext);const a=l[s],r=a.model.outputs;let i=!1;if(r)for(let e=0;e<r.length;e++){const t=r.get(e);if(t&&"error"===t.type){i=!0;break}}if(i)break;const c=a.model.getMetadata("flowbook");if(c?.errors&&c.errors.length>0)break}}}var j=o(892);function q(e,t){const o=e.content.widgets;let n=0;for(let e=0;e<o.length;e++)if("code"===o[e].model.type){if(n===t)return e;n++}throw new Error(`Code cell index ${t} out of range (have ${n} code cells)`)}function B(e){const t=e.model?.outputs;if(!t)return"";const o=[];for(let e=0;e<t.length;e++){const n=t.get(e);if(!n)continue;const s=n.type;if("stream"===s)o.push(n.data?.["text/plain"]||n.text||"");else if("execute_result"===s||"display_data"===s){const e=n.data?.["text/plain"];e&&o.push(e)}else if("error"===s){const e=n.traceback;if(e){const t=String.fromCharCode(27),n=new RegExp(t+"\\[[0-9;]*m","g");o.push(e.join("\n").replace(n,""))}else o.push(`${n.ename}: ${n.evalue}`)}}return o.join("\n")}function J(e,t){const o=e.content.widgets[t];if(!o||"code"!==o.model.type)return!1;const n=o.model.outputs;if(!n)return!1;for(let e=0;e<n.length;e++){const t=n.get(e);if(t&&"error"===t.type)return!0}return!1}let V=null,z=null,U=null,Z=null;function G(e,t,o,n){V=e,z=t,U=o,Z=n}function X(){const e=Z?.currentWidget;if(!e)throw new Error("No active notebook");return e}function Y(){if(!V)throw new Error("FlowBook not active");return V.getStalenessManager(X())}class Q{constructor(e,t,o,n){this._panel=null,this._dependenciesPanel=null,this._highlighter=null,this._executionHook=null,this._isActive=!1,this._activeNotebookPath=null,this._statusListenerNotebook=null,this._app=e,this._tracker=t,this._kernelDetector=o,this._cellIndexManager=new K,this._toolbarExtension=n,this._setupKernelChangeListener(),this._checkCurrentNotebook()}_setupKernelChangeListener(){this._kernelDetector.kernelChanged.connect((e,t)=>{"flowbook_kernel"===t.currentKernel?this._activate():"flowbook_kernel"===t.previousKernel&&this._deactivate()}),this._tracker.currentChanged.connect(()=>{this._checkCurrentNotebook()})}_checkCurrentNotebook(){const e=this._tracker.currentWidget;e&&(e.sessionContext.ready.then(()=>{this._kernelDetector.isFlowbookKernel(e)?this._activate():this._deactivate()}),this._statusListenerNotebook&&this._statusListenerNotebook!==e&&this._statusListenerNotebook.sessionContext.statusChanged.disconnect(this._onStatusChanged,this),this._statusListenerNotebook!==e&&(e.sessionContext.statusChanged.connect(this._onStatusChanged,this),this._statusListenerNotebook=e))}_onStatusChanged(){const e=this._statusListenerNotebook;if(!e)return;const t=this._kernelDetector.isFlowbookKernel(e);t&&!this._isActive?this._activate():t&&this._isActive?this._writeKernelDiscovery(e):!t&&this._isActive&&this._deactivate()}_activate(){if(this._isActive)return;this._panel=new g,this._app.shell.add(this._panel,"right",{rank:510}),this._dependenciesPanel=new A,this._app.shell.add(this._dependenciesPanel,"right",{rank:520}),this._highlighter=new L(this._tracker,this._panel),this._highlighter.setDependenciesPanel(this._dependenciesPanel),this._toolbarExtension.setHighlighter(this._highlighter),this._executionHook=new D(this._tracker,this._highlighter),this._highlighter.refreshDependencies(),G(this._highlighter,this._executionHook,this._kernelDetector,this._tracker);const e=this._tracker.currentWidget;e&&(this._activeNotebookPath=e.context.path,this._cellIndexManager.startMonitoring(this._activeNotebookPath,e),this._syncInitialState(e),this._writeKernelDiscovery(e)),this._isActive=!0}_syncInitialState(e){if(!this._executionHook)return;const t=c(e);0!==t.length&&(this._executionHook.sendCommand({type:"notebook_structure",cell_order:t}),this._executionHook.sendCommand({type:"sync"}))}_writeKernelDiscovery(e){const t=e.sessionContext.session;if(!t||!t.kernel)return;const o=e.context.path,n=t.kernel.id;(async function(e="",t={}){const o=j.ServerConnection.makeSettings(),n=`${o.baseUrl}flowbook/${e}`;let s;try{s=await j.ServerConnection.makeRequest(n,t,o)}catch(e){throw new j.ServerConnection.NetworkError(e)}let l=await s.text();if(l.length>0)try{l=JSON.parse(l)}catch{}if(!s.ok){const e=l.error||l.message||l;throw new j.ServerConnection.ResponseError(s,e)}return l})(`kernel-discovery/${encodeURIComponent(o)}`,{method:"PUT",body:JSON.stringify({kernel_name:t.kernel.name,connection_file:`kernel-${n}.json`,pid:0})}).catch(()=>{})}_deactivate(){this._isActive&&(this._activeNotebookPath&&(this._cellIndexManager.stopMonitoring(this._activeNotebookPath),this._activeNotebookPath=null),this._panel&&(this._panel.dispose(),this._panel=null),this._dependenciesPanel&&(this._dependenciesPanel.dispose(),this._dependenciesPanel=null),this._highlighter&&(this._highlighter.dispose(),this._highlighter=null),this._executionHook&&(this._executionHook.dispose(),this._executionHook=null),G(null,null,null,null),this._isActive=!1)}}const ee=[{id:"flowbook:plugin",autoStart:!0,requires:[n.INotebookTracker],activate:(e,t)=>{window.app=e;const o=new l(t);!function(e,t,o){e.commands.addCommand("flowbook:exec-restore",{label:"Run with upstream state",isEnabled:()=>{try{const e=t.currentWidget;if(!e)return!1;if(!o.isFlowbookKernel(e))return!1;const n=e.content.activeCell;if(!n||"code"!==n.model.type)return!1;const s=n.model.getMetadata("flowbook"),l=s?.errors;return void 0!==l&&l.some(e=>"no_read_before_write"===e.error_type)}catch(e){return console.error("FlowBook exec-restore isEnabled error:",e),!1}},isVisible:()=>{try{const e=t.currentWidget;return!!e&&o.isFlowbookKernel(e)}catch{return!1}},execute:async()=>{const o=t.currentWidget;if(!o)return;const n=o.content.activeCell;if(!n||"code"!==n.model.type)return;const s=n.model.id,l=o.sessionContext.session;if(!l||!l.kernel)return;const a=l.kernel.requestExecute({code:`%exec_restore ${s}`,silent:!0,store_history:!1});await a.done,await e.commands.execute("notebook:run-cell")}})}(e,t,o),function(e,t,o){Z=t,U=o,e.commands.addCommand("flowbook:is-active",{execute:()=>{const e=t.currentWidget;if(!e||!U)return{active:!1};const o=U.isFlowbookKernel(e);return{active:o,kernel:o?"flowbook_kernel":e.sessionContext.session?.kernel?.name||"unknown"}}}),e.commands.addCommand("flowbook:get-metadata",{execute:e=>{const t=X(),o=e.cellIndex,n=q(t,o),s=t.content.widgets[n],l=s.model.getMetadata("flowbook");return{label:d(o),cell_id:s.model.id,...l||{},stale_cells_labels:l?.stale_cells?.map(e=>{const o=c(t).indexOf(e);return o>=0?d(o):e})}}}),e.commands.addCommand("flowbook:get-stale-cells",{execute:()=>{const e=X(),t=Y(),o=t.staleCells,n=c(e),s=[];return n.forEach((e,n)=>{if(o.has(e)){const o=t.getReason(e),l=o&&"message"in o?o.message:o?.type||"stale";s.push({index:n,label:d(n),cell_id:e,reason:l})}}),s}}),e.commands.addCommand("flowbook:get-next-actionable",{execute:()=>{const e=X(),t=e.content.widgets,o=V?Y().staleCells:new Set;let n=0,s=null,l=null;for(let a=0;a<t.length;a++){const r=t[a];if("code"!==r.model.type)continue;const i=r.model,c=i.sharedModel.getSource(),h=r.model.id,u=n;if(n++,!c||""===c.trim())continue;const m=r.model.getMetadata("flowbook");if(m?.errors&&m.errors.length>0)return{index:u,label:d(u),cell_id:h,reason:"error",error:m.errors[0].message};if(J(e,a))return{index:u,label:d(u),cell_id:h,reason:"error"};!s&&o.has(h)&&(s={index:u,label:d(u),cell_id:h,reason:"stale"}),l||null!==i.executionCount||(l={index:u,label:d(u),cell_id:h,reason:"unexecuted"})}return s||l||{done:!0}}}),e.commands.addCommand("flowbook:get-status",{execute:()=>{const e=X(),t=e.content.widgets,o=V?Y().staleCells:new Set;let n=0,s=0,l=0,a=0,r=0;for(let i=0;i<t.length;i++){const c=t[i];if("code"!==c.model.type)continue;n++;const d=c.model,h=d.sharedModel.getSource();if(!h||""===h.trim()){r++;continue}null!==d.executionCount&&s++,o.has(c.model.id)&&l++;const u=c.model.getMetadata("flowbook");u?.errors&&u.errors.length>0&&a++,J(e,i)&&a++}const i=n-r;return{total_code_cells:n,non_empty:i,executed:s,stale:l,clean:s-l,with_errors:a,reproducible:i>0&&s===i&&0===l&&0===a}}}),e.commands.addCommand("flowbook:get-cell-count",{execute:()=>{const e=X().content.widgets;let t=0,o=0;for(let n=0;n<e.length;n++)"code"===e[n].model.type?t++:"markdown"===e[n].model.type&&o++;return{total:e.length,code_cells:t,markdown_cells:o}}}),e.commands.addCommand("flowbook:get-cell",{execute:e=>{const t=X(),o=e.cellIndex,n=q(t,o),s=t.content.widgets[n],l=d(o),a=s.model,r=s.model.getMetadata("flowbook");return{label:l,cell_id:s.model.id,cell_type:s.model.type,source:a.sharedModel.getSource(),execution_count:a.executionCount,outputs_text:B(s),flowbook_meta:r||null}}}),e.commands.addCommand("flowbook:get-cell-output",{execute:e=>{const t=X(),o=e.cellIndex,n=q(t,o),s=t.content.widgets[n];return{label:d(o),outputs_text:B(s)}}}),e.commands.addCommand("flowbook:edit-cell-source",{execute:e=>{const t=X(),o=e.cellIndex,n=e.source,s=q(t,o),l=t.content.widgets[s];return l.model.sharedModel.setSource(n),{label:d(o),cell_id:l.model.id}}}),e.commands.addCommand("flowbook:move-cell",{execute:e=>{const t=X(),o=e.fromIndex,n=e.toIndex,s=q(t,o),l=q(t,n),a=t.content.model?.sharedModel;if(!a)throw new Error("No shared model");return a.moveCell(s,l),{label:d(o),newIndex:n}}}),e.commands.addCommand("flowbook:notify-structure",{execute:()=>{const e=X();if(z){const t=c(e);z.sendCommand({type:"notebook_structure",cell_order:t})}}}),e.commands.addCommand("flowbook:set-continue-after-violation",{execute:e=>{const t=e.enabled;return z&&z.sendCommand({type:"continue_after_violation",enabled:t}),{enabled:t}}}),e.commands.addCommand("flowbook:run-cell",{execute:async e=>{const t=X(),o=e.cellIndex,s=q(t,o),l=d(o);t.content.activeCellIndex=s,await n.NotebookActions.run(t.content,t.sessionContext);const a=t.content.widgets[s],r=a.model.getMetadata("flowbook"),i=J(t,s),c=function(e,t){const o=e.content.widgets[t];if(!o||"code"!==o.model.type)return[];const n=o.model.getMetadata("flowbook");return n?.errors||[]}(t,s);return{label:l,cell_id:a.model.id,status:i?"error":c.length>0?"violation":"ok",outputs_text:B(a),flowbook_meta:r||null,errors:c}}}),e.commands.addCommand("flowbook:run-actionable-cells",{execute:async()=>{X();const t=[];let o=0;for(;o<500;)try{const n=await e.commands.execute("flowbook:get-next-actionable");if(n.done)break;const s=n.index,l=n.label,a=await e.commands.execute("flowbook:run-cell",{cellIndex:s});if(o++,t.push({label:l,status:a.status,outputs_preview:(a.outputs_text||"").slice(0,200)}),"error"===a.status)break;if("violation"===a.status)break}catch(e){console.error("Error in run-actionable-cells:",e);break}return{results:t,cells_run:o,summary:await e.commands.execute("flowbook:get-status")}}})}(e,t,o);const s=new H(o);e.docRegistry.addWidgetExtension("Notebook",s),new Q(e,t,o,s)}}]}}]);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var _JUPYTERLAB;(()=>{"use strict";var e,r,t,o,n,a,i,l,u,f,s,d,c,p,h,v,b,m,g,y,w,k,S,j={935(e,r,t){var o={"./index":()=>Promise.all([t.e(951),t.e(638),t.e(873)]).then(()=>()=>t(873)),"./extension":()=>Promise.all([t.e(951),t.e(638),t.e(873)]).then(()=>()=>t(873)),"./style":()=>t.e(728).then(()=>()=>t(728))},n=(e,r)=>(t.R=r,r=t.o(o,e)?o[e]():Promise.resolve().then(()=>{throw new Error('Module "'+e+'" does not exist in container.')}),t.R=void 0,r),a=(e,r)=>{if(t.S){var o="default",n=t.S[o];if(n&&n!==e)throw new Error("Container initialization failed as it has already been initialized with a different share scope");return t.S[o]=e,t.I(o,r)}};t.d(r,{get:()=>n,init:()=>a})}},P={};function E(e){var r=P[e];if(void 0!==r)return r.exports;var t=P[e]={id:e,exports:{}};return j[e](t,t.exports,E),t.exports}E.m=j,E.c=P,E.n=e=>{var r=e&&e.__esModule?()=>e.default:()=>e;return E.d(r,{a:r}),r},E.d=(e,r)=>{for(var t in r)E.o(r,t)&&!E.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:r[t]})},E.f={},E.e=e=>Promise.all(Object.keys(E.f).reduce((r,t)=>(E.f[t](e,r),r),[])),E.u=e=>e+"."+{728:"b1df4bca1a3305d0d0a7",873:"
|
|
1
|
+
var _JUPYTERLAB;(()=>{"use strict";var e,r,t,o,n,a,i,l,u,f,s,d,c,p,h,v,b,m,g,y,w,k,S,j={935(e,r,t){var o={"./index":()=>Promise.all([t.e(951),t.e(638),t.e(873)]).then(()=>()=>t(873)),"./extension":()=>Promise.all([t.e(951),t.e(638),t.e(873)]).then(()=>()=>t(873)),"./style":()=>t.e(728).then(()=>()=>t(728))},n=(e,r)=>(t.R=r,r=t.o(o,e)?o[e]():Promise.resolve().then(()=>{throw new Error('Module "'+e+'" does not exist in container.')}),t.R=void 0,r),a=(e,r)=>{if(t.S){var o="default",n=t.S[o];if(n&&n!==e)throw new Error("Container initialization failed as it has already been initialized with a different share scope");return t.S[o]=e,t.I(o,r)}};t.d(r,{get:()=>n,init:()=>a})}},P={};function E(e){var r=P[e];if(void 0!==r)return r.exports;var t=P[e]={id:e,exports:{}};return j[e](t,t.exports,E),t.exports}E.m=j,E.c=P,E.n=e=>{var r=e&&e.__esModule?()=>e.default:()=>e;return E.d(r,{a:r}),r},E.d=(e,r)=>{for(var t in r)E.o(r,t)&&!E.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:r[t]})},E.f={},E.e=e=>Promise.all(Object.keys(E.f).reduce((r,t)=>(E.f[t](e,r),r),[])),E.u=e=>e+"."+{728:"b1df4bca1a3305d0d0a7",873:"3edec525c5c79ec55cd6",905:"94c2bfb401597cc2a103",951:"ba84389925d6a0676e79"}[e]+".js?v="+{728:"b1df4bca1a3305d0d0a7",873:"3edec525c5c79ec55cd6",905:"94c2bfb401597cc2a103",951:"ba84389925d6a0676e79"}[e],E.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),E.o=(e,r)=>Object.prototype.hasOwnProperty.call(e,r),e={},r="flowbook:",E.l=(t,o,n,a)=>{if(e[t])e[t].push(o);else{var i,l;if(void 0!==n)for(var u=document.getElementsByTagName("script"),f=0;f<u.length;f++){var s=u[f];if(s.getAttribute("src")==t||s.getAttribute("data-webpack")==r+n){i=s;break}}i||(l=!0,(i=document.createElement("script")).charset="utf-8",E.nc&&i.setAttribute("nonce",E.nc),i.setAttribute("data-webpack",r+n),i.src=t),e[t]=[o];var d=(r,o)=>{i.onerror=i.onload=null,clearTimeout(c);var n=e[t];if(delete e[t],i.parentNode&&i.parentNode.removeChild(i),n&&n.forEach(e=>e(o)),r)return r(o)},c=setTimeout(d.bind(null,void 0,{type:"timeout",target:i}),12e4);i.onerror=d.bind(null,i.onerror),i.onload=d.bind(null,i.onload),l&&document.head.appendChild(i)}},E.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},(()=>{E.S={};var e={},r={};E.I=(t,o)=>{o||(o=[]);var n=r[t];if(n||(n=r[t]={}),!(o.indexOf(n)>=0)){if(o.push(n),e[t])return e[t];E.o(E.S,t)||(E.S[t]={});var a=E.S[t],i="flowbook",l=(e,r,t,o)=>{var n=a[e]=a[e]||{},l=n[r];(!l||!l.loaded&&(!o!=!l.eager?o:i>l.from))&&(n[r]={get:t,from:i,eager:!!o})},u=[];return"default"===t&&(l("@xyflow/react","12.10.2",()=>Promise.all([E.e(905),E.e(638)]).then(()=>()=>E(905))),l("flowbook","0.1.2",()=>Promise.all([E.e(951),E.e(638),E.e(873)]).then(()=>()=>E(873)))),e[t]=u.length?Promise.all(u).then(()=>e[t]=1):1}}})(),(()=>{var e;E.g.importScripts&&(e=E.g.location+"");var r=E.g.document;if(!e&&r&&(r.currentScript&&"SCRIPT"===r.currentScript.tagName.toUpperCase()&&(e=r.currentScript.src),!e)){var t=r.getElementsByTagName("script");if(t.length)for(var o=t.length-1;o>-1&&(!e||!/^http(s?):/.test(e));)e=t[o--].src}if(!e)throw new Error("Automatic publicPath is not supported in this browser");e=e.replace(/^blob:/,"").replace(/#.*$/,"").replace(/\?.*$/,"").replace(/\/[^\/]+$/,"/"),E.p=e})(),t=e=>{var r=e=>e.split(".").map(e=>+e==e?+e:e),t=/^([^-+]+)?(?:-([^+]+))?(?:\+(.+))?$/.exec(e),o=t[1]?r(t[1]):[];return t[2]&&(o.length++,o.push.apply(o,r(t[2]))),t[3]&&(o.push([]),o.push.apply(o,r(t[3]))),o},o=(e,r)=>{e=t(e),r=t(r);for(var o=0;;){if(o>=e.length)return o<r.length&&"u"!=(typeof r[o])[0];var n=e[o],a=(typeof n)[0];if(o>=r.length)return"u"==a;var i=r[o],l=(typeof i)[0];if(a!=l)return"o"==a&&"n"==l||"s"==l||"u"==a;if("o"!=a&&"u"!=a&&n!=i)return n<i;o++}},n=e=>{var r=e[0],t="";if(1===e.length)return"*";if(r+.5){t+=0==r?">=":-1==r?"<":1==r?"^":2==r?"~":r>0?"=":"!=";for(var o=1,a=1;a<e.length;a++)o--,t+="u"==(typeof(l=e[a]))[0]?"-":(o>0?".":"")+(o=2,l);return t}var i=[];for(a=1;a<e.length;a++){var l=e[a];i.push(0===l?"not("+u()+")":1===l?"("+u()+" || "+u()+")":2===l?i.pop()+" "+i.pop():n(l))}return u();function u(){return i.pop().replace(/^\((.+)\)$/,"$1")}},a=(e,r)=>{if(0 in e){r=t(r);var o=e[0],n=o<0;n&&(o=-o-1);for(var i=0,l=1,u=!0;;l++,i++){var f,s,d=l<e.length?(typeof e[l])[0]:"";if(i>=r.length||"o"==(s=(typeof(f=r[i]))[0]))return!u||("u"==d?l>o&&!n:""==d!=n);if("u"==s){if(!u||"u"!=d)return!1}else if(u)if(d==s)if(l<=o){if(f!=e[l])return!1}else{if(n?f>e[l]:f<e[l])return!1;f!=e[l]&&(u=!1)}else if("s"!=d&&"n"!=d){if(n||l<=o)return!1;u=!1,l--}else{if(l<=o||s<d!=n)return!1;u=!1}else"s"!=d&&"n"!=d&&(u=!1,l--)}}var c=[],p=c.pop.bind(c);for(i=1;i<e.length;i++){var h=e[i];c.push(1==h?p()|p():2==h?p()&p():h?a(h,r):!p())}return!!p()},i=(e,r)=>e&&E.o(e,r),l=e=>(e.loaded=1,e.get()),u=e=>Object.keys(e).reduce((r,t)=>(e[t].eager&&(r[t]=e[t]),r),{}),f=(e,r,t,n)=>{var i=n?u(e[r]):e[r];return(r=Object.keys(i).reduce((e,r)=>!a(t,r)||e&&!o(e,r)?e:r,0))&&i[r]},s=(e,r,t)=>{var n=t?u(e[r]):e[r];return Object.keys(n).reduce((e,r)=>!e||!n[e].loaded&&o(e,r)?r:e,0)},d=(e,r,t,o)=>"Unsatisfied version "+t+" from "+(t&&e[r][t].from)+" of shared singleton module "+r+" (required "+n(o)+")",c=(e,r,t,o,a)=>{var i=e[t];return"No satisfying version ("+n(o)+")"+(a?" for eager consumption":"")+" of shared module "+t+" found in shared scope "+r+".\nAvailable versions: "+Object.keys(i).map(e=>e+" from "+i[e].from).join(", ")},p=e=>{throw new Error(e)},h=e=>{"undefined"!=typeof console&&console.warn&&console.warn(e)},b=(e,r,t)=>t?t():((e,r)=>p("Shared module "+r+" doesn't exist in shared scope "+e))(e,r),m=(v=e=>function(r,t,o,n,a){var i=E.I(r);return i&&i.then&&!o?i.then(e.bind(e,r,E.S[r],t,!1,n,a)):e(r,E.S[r],t,o,n,a)})((e,r,t,o,n,a)=>{if(!i(r,t))return b(e,t,a);var u=f(r,t,n,o);return u?l(u):a?a():void p(c(r,e,t,n,o))}),g=v((e,r,t,o,n,u)=>{if(!i(r,t))return b(e,t,u);var f=s(r,t,o);return a(n,f)||h(d(r,t,f,n)),l(r[t][f])}),y={},w={345:()=>g("default","react",!1,[1,18,2,0]),628:()=>g("default","react-dom",!1,[1,18,2,0]),247:()=>g("default","@lumino/widgets",!1,[1,2,3,1,,"alpha",1]),395:()=>m("default","@xyflow/react",!1,[1,12,10,1],()=>E.e(905).then(()=>()=>E(905))),516:()=>g("default","@jupyterlab/apputils",!1,[1,4,6,8]),562:()=>g("default","@jupyterlab/ui-components",!1,[1,4,5,8]),602:()=>g("default","@lumino/signaling",!1,[1,2,0,0]),830:()=>g("default","@jupyterlab/notebook",!1,[1,4,5,8]),892:()=>g("default","@jupyterlab/services",!1,[1,7,5,8])},k={638:[345,628],873:[247,395,516,562,602,830,892]},S={},E.f.consumes=(e,r)=>{E.o(k,e)&&k[e].forEach(e=>{if(E.o(y,e))return r.push(y[e]);if(!S[e]){var t=r=>{y[e]=0,E.m[e]=t=>{delete E.c[e],t.exports=r()}};S[e]=!0;var o=r=>{delete y[e],E.m[e]=t=>{throw delete E.c[e],r}};try{var n=w[e]();n.then?r.push(y[e]=n.then(t).catch(o)):t(n)}catch(e){o(e)}}})},(()=>{var e={952:0};E.f.j=(r,t)=>{var o=E.o(e,r)?e[r]:void 0;if(0!==o)if(o)t.push(o[2]);else if(638!=r){var n=new Promise((t,n)=>o=e[r]=[t,n]);t.push(o[2]=n);var a=E.p+E.u(r),i=new Error;E.l(a,t=>{if(E.o(e,r)&&(0!==(o=e[r])&&(e[r]=void 0),o)){var n=t&&("load"===t.type?"missing":t.type),a=t&&t.target&&t.target.src;i.message="Loading chunk "+r+" failed.\n("+n+": "+a+")",i.name="ChunkLoadError",i.type=n,i.request=a,o[1](i)}},"chunk-"+r,r)}else e[r]=0};var r=(r,t)=>{var o,n,[a,i,l]=t,u=0;if(a.some(r=>0!==e[r])){for(o in i)E.o(i,o)&&(E.m[o]=i[o]);l&&l(E)}for(r&&r(t);u<a.length;u++)n=a[u],E.o(e,n)&&e[n]&&e[n][0](),e[n]=0},t=self.webpackChunkflowbook=self.webpackChunkflowbook||[];t.forEach(r.bind(null,0)),t.push=r.bind(null,t.push.bind(t))})(),E.nc=void 0;var x=E(935);(_JUPYTERLAB=void 0===_JUPYTERLAB?{}:_JUPYTERLAB).flowbook=x})();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: flowbook-python
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: Reproducibility enforcement for Jupyter notebooks: a JupyterLab extension that enforces rerun consistency by tracking variable- and column-level dependencies between cells.
|
|
5
5
|
Project-URL: Homepage, https://github.com/stephenfreund/FlowBook
|
|
6
6
|
Project-URL: Bug Tracker, https://github.com/stephenfreund/FlowBook/issues
|
|
@@ -97,7 +97,7 @@ Description-Content-Type: text/markdown
|
|
|
97
97
|
**Reproducibility enforcement for Jupyter notebooks.**
|
|
98
98
|
|
|
99
99
|
FlowBook is a JupyterLab extension that enforces _rerun consistency_:
|
|
100
|
-
re-executing
|
|
100
|
+
re-executing any cell from the current state would produce a result
|
|
101
101
|
consistent with a top-to-bottom execution of the notebook,
|
|
102
102
|
regardless of which cells have been run, modified, and rerun.
|
|
103
103
|
Cells whose inputs may have changed are marked _stale_,
|
|
@@ -108,6 +108,9 @@ When every cell is _clean_ — executed and rerun consistent —
|
|
|
108
108
|
the notebook is guaranteed reproducible: running it top-to-bottom from
|
|
109
109
|
an empty store yields exactly the outputs currently recorded.
|
|
110
110
|
|
|
111
|
+
For technical details and a complete evaluation, see our arXiv paper: [FlowBook: Enforcing Reproducibility in Computational Notebooks
|
|
112
|
+
](https://arxiv.org/abs/2605.01560).
|
|
113
|
+
|
|
111
114
|
## Quick Start
|
|
112
115
|
|
|
113
116
|
Install FlowBook using `pip`:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
flowbook/__init__.py,sha256=zNgknOhDjL5J82yiQ6LJxpZcnHLl7B_PndouThTRt_4,3052
|
|
2
2
|
flowbook/__main__.py,sha256=d90JE7Gz6DyjkQ-dwiU3duFA_Kzf2hKKNQJuNI8EHpM,1053
|
|
3
|
-
flowbook/_version.py,sha256=
|
|
3
|
+
flowbook/_version.py,sha256=vYZ6rLVUw05xbWIGie5W7cVk18sWwVN72cGSZ2ngSYU,171
|
|
4
4
|
flowbook/handlers.py,sha256=kT36OTOMcmO7FHjFSHai_nLDiB-PwcnynONAEB3MjRM,774
|
|
5
5
|
flowbook/kernel_discovery.py,sha256=EXyneUXHTkMu2_otjudRS3WG300GRFBbiIadolG9ogo,3635
|
|
6
6
|
flowbook/baseline_kernel/__init__.py,sha256=z3dSM5AdJ8n0IaroWSYRY32Uhc-3AxlE-Eq-7cH_ehA,817
|
|
@@ -42,13 +42,13 @@ flowbook/kernel/access_events.py,sha256=CSrgGa7jbJUnB06ibfmNnppNGSaAilHon_i4nXQE
|
|
|
42
42
|
flowbook/kernel/change_detector.py,sha256=lnnkjUqCm5u6PNPvEUBFALmCK90lrW-IYwxdZD8BNKQ,17634
|
|
43
43
|
flowbook/kernel/changes.py,sha256=YKKQ_29OdLv-j2SFIzTN2Ym2PV0G4Wb8StJgE9tIBqc,7346
|
|
44
44
|
flowbook/kernel/flowbook_client.py,sha256=4xgs57HEETHOeH9yiNYdjfLAMCg70D7vZv-fd9dCfLA,2396
|
|
45
|
-
flowbook/kernel/flowbook_kernel.py,sha256=
|
|
45
|
+
flowbook/kernel/flowbook_kernel.py,sha256=29e66thKX94uRs4AGJZ_JlelxdiekgMu5WxYcjkkvzo,83669
|
|
46
46
|
flowbook/kernel/loc_ids.py,sha256=oNAu03yvA_5GefB5z0S0mXjbqf-zWdYZFnTu8VhzJf4,6215
|
|
47
47
|
flowbook/kernel/locations.py,sha256=Y68sxUkL7tGBmNT3zSFDQbMAtXp0Y3hczMQbNDE3PpE,29732
|
|
48
48
|
flowbook/kernel/models.py,sha256=j8WGBsuopdN1j0pmdypkpGe926waNpw5JyrnmcMbiO8,14268
|
|
49
|
-
flowbook/kernel/notebook_state.py,sha256=
|
|
50
|
-
flowbook/kernel/protocol.py,sha256=
|
|
51
|
-
flowbook/kernel/reproducibility_enforcer.py,sha256=
|
|
49
|
+
flowbook/kernel/notebook_state.py,sha256=sIispCoLHQgCH1lFcN2oKBaxNx-OA-DXOGhKhz-17go,27140
|
|
50
|
+
flowbook/kernel/protocol.py,sha256=CATA84--K--yo6Bz6LClag-ueyMVDr-XL6dHru1nYZs,6994
|
|
51
|
+
flowbook/kernel/reproducibility_enforcer.py,sha256=tepxmSsDX8gc6GO8EFJibRhWGUcFlSIH3cl1mahyszo,122556
|
|
52
52
|
flowbook/kernel/kernelspec/kernel.json,sha256=a5n7ZxmD_4XY3jWQAroPN03ayT7AeUZNjS8MGUviG8U,181
|
|
53
53
|
flowbook/kernel/kernelspec/logo-32x32.png,sha256=Pv-uhxm8eL001GREpTXqQJ5wS-VGDd4V-yAtZFvWV0M,2273
|
|
54
54
|
flowbook/kernel/kernelspec/logo-64x64.png,sha256=P6sxTssjbTg4O8kU3wmups583z0sU_1ALesRhritHCY,6495
|
|
@@ -70,6 +70,7 @@ flowbook/kernel/tests/test_litmus.py,sha256=qGGpgTEbJ80CfFvTuPj-Sp7O5JpYZMaMDqhv
|
|
|
70
70
|
flowbook/kernel/tests/test_loc_ids.py,sha256=T6o00bRTpfjgcczMXUr4WAHNNDncdReI4LfqtWi81c0,16406
|
|
71
71
|
flowbook/kernel/tests/test_locations.py,sha256=rCs0VTMpo6E4FQnHQTPtlkxCU5TqFGms163xUnZ7zi0,30740
|
|
72
72
|
flowbook/kernel/tests/test_locset_integration.py,sha256=DpRR66leQBJlkXBiIw-92NQ-Ajw7LlOt7gmJMv8PVVI,31243
|
|
73
|
+
flowbook/kernel/tests/test_meaningful_edit.py,sha256=B3osU6-hhJSDz15o2BWzfyjFGkItnj408OuRtJNci-g,9290
|
|
73
74
|
flowbook/kernel/tests/test_measure_rerun_overhead.py,sha256=guTjvSBd0-nHf3k13Gg7F3-uot5YAVpzmWjKeffHdxs,8310
|
|
74
75
|
flowbook/kernel/tests/test_notebook_state.py,sha256=QuBGDI7Cb73-6EQj3-v4MLySexuBlb9nSWoniWkW0ac,41484
|
|
75
76
|
flowbook/kernel/tests/test_optimizations.py,sha256=IERPWPqG9Z98isBsuLcenlREY2vFejBH1R5rZyPx1U0,16927
|
|
@@ -200,7 +201,7 @@ flowbook/kernel_support/tests/test_virtual_fs.py,sha256=vRTtdG-0Hn56iF0-cItKN4a9
|
|
|
200
201
|
flowbook/mcp/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
201
202
|
flowbook/mcp/jupyter_config.py,sha256=lI7ddMrQF3APHr33py2j36jNa--GFblRf_M8CRHCEDw,3252
|
|
202
203
|
flowbook/mcp/server.py,sha256=Sywz5bH-Si0t98ndT2w4GeKosCnE-UHw5TwtU7YpZs0,29502
|
|
203
|
-
flowbook/mcp/session.py,sha256=
|
|
204
|
+
flowbook/mcp/session.py,sha256=eFrrsVFSOBQ-fG_ba8TzcoXD0WyGnrO-viZEQXLYX0Q,58214
|
|
204
205
|
flowbook/mcp/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
205
206
|
flowbook/mcp/tests/test_cell_validation.py,sha256=uE3f12bxKE9r2SJoxw3L_LWE67C33oaT9wfw11Mw44E,1924
|
|
206
207
|
flowbook/mcp/tests/test_contents_api.py,sha256=ue_b9l-hlT-0PBtH1-ejq8SMBxlSzWhzFk8sOSBbrRM,16625
|
|
@@ -293,22 +294,22 @@ flowbook/util/tests/test_gpu_memory.py,sha256=bXsmiVwQ7RNAea4cHjgMzrJvFIk4Og3Fyv
|
|
|
293
294
|
flowbook/util/tests/test_liveness.py,sha256=nnkpLKs1fP_qzSI8rfwWz4ADi8G89JVC21pNLfkDvns,23733
|
|
294
295
|
flowbook/util/tests/test_notebook_analysis.py,sha256=KJoNd0YCM6tS50FLY8z-hxo2XuBGt4rhb04-lZsDNNM,15793
|
|
295
296
|
flowbook/util/tests/test_notebook_to_python.py,sha256=LQj1j7s2HQ-YViC-8PVhlbBWUdmcazIKYPKp1r77CUg,11855
|
|
296
|
-
flowbook_python-0.1.
|
|
297
|
-
flowbook_python-0.1.
|
|
298
|
-
flowbook_python-0.1.
|
|
299
|
-
flowbook_python-0.1.
|
|
300
|
-
flowbook_python-0.1.
|
|
301
|
-
flowbook_python-0.1.
|
|
302
|
-
flowbook_python-0.1.
|
|
303
|
-
flowbook_python-0.1.
|
|
304
|
-
flowbook_python-0.1.
|
|
305
|
-
flowbook_python-0.1.
|
|
306
|
-
flowbook_python-0.1.
|
|
307
|
-
flowbook_python-0.1.
|
|
308
|
-
flowbook_python-0.1.
|
|
309
|
-
flowbook_python-0.1.
|
|
310
|
-
flowbook_python-0.1.
|
|
311
|
-
flowbook_python-0.1.
|
|
312
|
-
flowbook_python-0.1.
|
|
313
|
-
flowbook_python-0.1.
|
|
314
|
-
flowbook_python-0.1.
|
|
297
|
+
flowbook_python-0.1.2.data/data/etc/jupyter/jupyter_server_config.d/flowbook.json,sha256=SBWUy6yIzlYxOUoH8b7g8LpbVFA8riSsI24a49D75pQ,83
|
|
298
|
+
flowbook_python-0.1.2.data/data/share/jupyter/labextensions/flowbook/package.json,sha256=ncXCj3BNYZqEu85H0lSgdqE8GOmkut15gSyxDXrjoOg,6813
|
|
299
|
+
flowbook_python-0.1.2.data/data/share/jupyter/labextensions/flowbook/schemas/flowbook/package.json.orig,sha256=eRm_LCwxkBarB8GQQqkZC37jm3E0jCAiNLNbcT06pMk,7761
|
|
300
|
+
flowbook_python-0.1.2.data/data/share/jupyter/labextensions/flowbook/schemas/flowbook/plugin.json,sha256=gyFTNNZO3TS3IUf2ag4qEp0MhHJdIIJW83fSDBNlAyQ,374
|
|
301
|
+
flowbook_python-0.1.2.data/data/share/jupyter/labextensions/flowbook/static/728.b1df4bca1a3305d0d0a7.js,sha256=sd9LyhozBdDQpztInmq2HI1Bz5huwIqZ4GKzYi_MhuQ,10788
|
|
302
|
+
flowbook_python-0.1.2.data/data/share/jupyter/labextensions/flowbook/static/873.3edec525c5c79ec55cd6.js,sha256=Pt7FJcXHnsVc1snJcZIEqH4WVwoLijEvRhfckg-Cxus,51178
|
|
303
|
+
flowbook_python-0.1.2.data/data/share/jupyter/labextensions/flowbook/static/905.94c2bfb401597cc2a103.js,sha256=kE4f1HMB4ZBv4xB39qZJYsQMObKEOoDZ8tUqih_5lmc,185362
|
|
304
|
+
flowbook_python-0.1.2.data/data/share/jupyter/labextensions/flowbook/static/905.94c2bfb401597cc2a103.js.LICENSE.txt,sha256=4IQjmq-Rys7RAELeOr57IxiUlcHYzf8E29jgR73coC4,781
|
|
305
|
+
flowbook_python-0.1.2.data/data/share/jupyter/labextensions/flowbook/static/951.ba84389925d6a0676e79.js,sha256=uoQ4mSXWoGdueau47-_g6oLgVyeg-wK0-eZk6uFYKcg,23080
|
|
306
|
+
flowbook_python-0.1.2.data/data/share/jupyter/labextensions/flowbook/static/remoteEntry.9c89c62fdbe6db112e2d.js,sha256=nInGL9vm2xEuLXuz7QzmBy8NIgFfnvw33XCCulASiRc,7814
|
|
307
|
+
flowbook_python-0.1.2.data/data/share/jupyter/labextensions/flowbook/static/style.js,sha256=u-pYuJa5_OIRLxiyYEueJa-VX5iYpdwH__UT7GgbxHY,151
|
|
308
|
+
flowbook_python-0.1.2.data/data/share/jupyter/labextensions/flowbook/static/third-party-licenses.json,sha256=SMb6b-Owv3hnXj-G5s_QwC0bh3hofMgnEqWgXUS50w4,19656
|
|
309
|
+
flowbook_python-0.1.2.data/data/share/jupyter/nbi_extensions/flowbook/extension.json,sha256=YEDOYVN9pn6U-W_Lu1L_NT9OyJgkF8N8QvjeOECOwkU,59
|
|
310
|
+
flowbook_python-0.1.2.data/data/share/jupyter/labextensions/flowbook/install.json,sha256=Fv3Z6UBNFAzHSNYXCzXPLFEIg9QAYGspssc9ZQ-SkYY,191
|
|
311
|
+
flowbook_python-0.1.2.dist-info/METADATA,sha256=Xrv82nZiFJXAyODDudncqO42vs_UoY3A1dTlWVS7GKc,10463
|
|
312
|
+
flowbook_python-0.1.2.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
|
|
313
|
+
flowbook_python-0.1.2.dist-info/entry_points.txt,sha256=exIG8mzfGijcgNChot4kJQwfLTGVqOjoizjvkr1Ntn0,601
|
|
314
|
+
flowbook_python-0.1.2.dist-info/licenses/LICENSE,sha256=vzQ5CiRavPNATuWPTc47miSh54LsYzpmUTyej6JVDd4,1522
|
|
315
|
+
flowbook_python-0.1.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|