@marimo-team/islands 0.25.3-dev104 → 0.25.3-dev105

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.
@@ -135,7 +135,7 @@ function F(e) {
135
135
  }
136
136
  //#endregion
137
137
  //#region src/components/editor/boundary/ErrorBoundary.tsx
138
- var I = (0, T.lazy)(() => import("./feedback-button-CfEYDdvF.js").then((e) => ({ default: e.FeedbackModal }))), L = (e) => {
138
+ var I = (0, T.lazy)(() => import("./feedback-button-0HAIFve4.js").then((e) => ({ default: e.FeedbackModal }))), L = (e) => {
139
139
  let t = (0, w.c)(2), n;
140
140
  return t[0] === e.children ? n = t[1] : (n = /* @__PURE__ */ (0, j.jsx)(O, {
141
141
  FallbackComponent: R,
@@ -18,7 +18,7 @@ function x() {
18
18
  return e.warn("Failed to get version from mount config"), null;
19
19
  }
20
20
  }
21
- var S = x() || "0.25.3-dev104", C = u(S), w = u(!0);
21
+ var S = x() || "0.25.3-dev105", C = u(S), w = u(!0);
22
22
  u(null);
23
23
  //#endregion
24
24
  //#region src/core/wasm/state.ts
@@ -12,7 +12,7 @@ import { t as lt } from "./check-P35dc2dH.js";
12
12
  import { A as ut, C as dt, M as ft, O as pt, S as mt, _ as ht, b as gt, c as _t, d as vt, g as yt, h as bt, i as xt, l as St, m as Ct, n as wt, p as Tt, s as Et, t as Dt, u as Ot, v as kt, y as At } from "./select-B0LRDH0g.js";
13
13
  import { $ as jt, $t as Mt, A as Nt, At as Pt, B as Ft, Bt as It, Ct as Lt, D as Rt, Dt as zt, E as Bt, Et as Vt, F as Ht, Ft as Ut, G as Wt, Gt, H as Kt, Ht as qt, I as Jt, It as Yt, J as Xt, Jt as Zt, Kt as Qt, L as $t, Lt as en, M as tn, Mt as nn, N as rn, Nt as an, O as on, Ot as sn, P as cn, Pt as ln, Q as un, Qt as dn, R as fn, Rt as pn, S as mn, St as hn, T as gn, Tt as _n, U as vn, Ut as yn, V as bn, Vt as xn, W as Sn, Wt as Cn, X as wn, Xt as Tn, Y as En, Yt as Dn, Z as On, Zt as kn, _ as An, _n as jn, _t as Mn, a as Nn, an as Pn, at as Fn, b as In, bt as Ln, cn as Rn, ct as zn, d as Bn, dn as Vn, en as Hn, et as Un, f as Wn, fn as Gn, ft as Kn, gn as qn, gt as Jn, h as Yn, hn as Xn, ht as Zn, i as Qn, in as $n, it as er, j as tr, jt as nr, k as rr, kt as ir, l as ar, ln as or, lt as sr, mn as cr, mt as lr, n as ur, nn as dr, nt as fr, o as pr, ot as mr, pn as hr, pt as gr, qt as _r, rn as vr, rt as yr, s as br, sn as xr, st as Sr, t as Cr, tt as wr, u as Tr, ut as Er, vn as Dr, vt as Or, w as kr, wt as Ar, x as jr, xt as Mr, y as Nr, yn as Pr, yt as Fr, z as Ir, zt as Lr } from "./dropdown-menu-B_7xD2X4.js";
14
14
  import { a as Rr, c as zr, d as Br, i as Vr, l as Hr, n as Ur, o as Wr, r as Gr, t as Kr, v as qr, x as Jr, y as Yr } from "./toDate-DKH0_kMo.js";
15
- import { n as Xr, t as Zr } from "./ErrorBoundary-BSX5FnuK.js";
15
+ import { n as Xr, t as Zr } from "./ErrorBoundary-CkptjJEU.js";
16
16
  import { t as Qr } from "./clsx-CoeGtWvn.js";
17
17
  import { t as $r } from "./react-dom-DUmH0GXv.js";
18
18
  import { t as ei } from "./jsx-runtime-3BD1kT0s.js";
@@ -2,7 +2,7 @@ import { a as e } from "./rolldown-runtime-Cb76FRpK.js";
2
2
  import { t } from "./react-sHtZdjeP.js";
3
3
  import { t as n } from "./react-dom-DUmH0GXv.js";
4
4
  import { c as r, l as i, u as a } from "./data-grid-types-0PJ-9N5n.js";
5
- import { a as o, i as s, n as c, r as l } from "./glide-data-editor-CuC6Qmmd.js";
5
+ import { a as o, i as s, n as c, r as l } from "./glide-data-editor-q0CzLqHy.js";
6
6
  //#region ../node_modules/.pnpm/@glideapps+glide-data-grid@6.0.4-alpha24_lodash@4.17.21_marked@15.0.12_react-dom@19.3.0_27cffe7f1bc30287d59ac27db3240c5b/node_modules/@glideapps/glide-data-grid/dist/esm/internal/data-grid-overlay-editor/data-grid-overlay-editor-style.js
7
7
  var u = /* @__PURE__ */ e(t(), 1), d = /* @__PURE__ */ e(n(), 1), f = /*#__PURE__*/ o("div")({
8
8
  name: "DataGridOverlayEditorStyle",
@@ -5,12 +5,12 @@ import { t as i } from "./compiler-runtime-CldRdSsv.js";
5
5
  import { t as a } from "./copy-icon-CPoxUG7G.js";
6
6
  import { Dt as o, G as s, It as c, K as l, Rt as u, V as d, dn as f, h as p, i as m, n as h } from "./error-entries-m7wjcSR-.js";
7
7
  import { b as g } from "./select-B0LRDH0g.js";
8
- import { n as ee, t as te } from "./globals-D7jFtSFG.js";
8
+ import { n as ee, t as te } from "./globals-XxWFgM2b.js";
9
9
  import { t as _ } from "./jsx-runtime-3BD1kT0s.js";
10
10
  import { C as v, _ as ne, i as y } from "./useTheme-CfeaVw5D.js";
11
11
  import { a as re, i as ie, n as ae, r as oe } from "./dialog-C1PcpCqX.js";
12
12
  import { t as se } from "./useAsyncData-D0mLJstx.js";
13
- import { t as ce } from "./code-visibility-azRFwO8n.js";
13
+ import { t as ce } from "./code-visibility-Djtm07c9.js";
14
14
  //#region src/components/ui/skeleton.tsx
15
15
  var b = /* @__PURE__ */ e(r(), 1), x = i(), S = _(), le = (e) => {
16
16
  let n = (0, x.c)(8), r, i;
@@ -6,7 +6,7 @@ import { i as s, n as c, r as l } from "./copy-icon-CPoxUG7G.js";
6
6
  import { C as u, c as d, i as f, n as p, s as m, t as h, x as g } from "./select-B0LRDH0g.js";
7
7
  import { d as _, h as v, i as y, l as b, n as x, o as S, s as C, t as w, u as T } from "./dropdown-menu-B_7xD2X4.js";
8
8
  import { c as E, d as D, l as O, n as k, o as A, s as ee, t as j, u as te } from "./glide-utils-Bt8_pBa2.js";
9
- import { n as ne, t as re } from "./ErrorBoundary-BSX5FnuK.js";
9
+ import { n as ne, t as re } from "./ErrorBoundary-CkptjJEU.js";
10
10
  import { t as ie } from "./react-dom-DUmH0GXv.js";
11
11
  import { t as M } from "./jsx-runtime-3BD1kT0s.js";
12
12
  import { t as N } from "./label-BRnUC0NX.js";
@@ -7062,7 +7062,7 @@ function rs(e, t, n, r, i) {
7062
7062
  }
7063
7063
  //#endregion
7064
7064
  //#region ../node_modules/.pnpm/@glideapps+glide-data-grid@6.0.4-alpha24_lodash@4.17.21_marked@15.0.12_react-dom@19.3.0_27cffe7f1bc30287d59ac27db3240c5b/node_modules/@glideapps/glide-data-grid/dist/esm/data-editor/data-editor.js
7065
- var is = /* @__PURE__ */ e(_r(), 1), as = /* @__PURE__ */ e(xr(), 1), os = K.lazy(async () => await import("./data-grid-overlay-editor-BvWRCxAv.js")), ss = 0;
7065
+ var is = /* @__PURE__ */ e(_r(), 1), as = /* @__PURE__ */ e(xr(), 1), os = K.lazy(async () => await import("./data-grid-overlay-editor-CDoqD-jC.js")), ss = 0;
7066
7066
  function cs(e) {
7067
7067
  return (0, is.default)((0, as.default)((0, as.default)(e).filter((e) => e.span !== void 0).map((e) => (0, Za.default)((e.span?.[0] ?? 0) + 1, (e.span?.[1] ?? 0) + 1))));
7068
7068
  }
@@ -9293,7 +9293,7 @@ function Vs(e, t, n, r, i) {
9293
9293
  }
9294
9294
  //#endregion
9295
9295
  //#region ../node_modules/.pnpm/@glideapps+glide-data-grid@6.0.4-alpha24_lodash@4.17.21_marked@15.0.12_react-dom@19.3.0_27cffe7f1bc30287d59ac27db3240c5b/node_modules/@glideapps/glide-data-grid/dist/esm/cells/number-cell.js
9296
- var Hs = K.lazy(async () => await import("./number-overlay-editor-CCzf5gxX.js")), Us = {
9296
+ var Hs = K.lazy(async () => await import("./number-overlay-editor-C41yshYb.js")), Us = {
9297
9297
  getAccessibilityString: (e) => e.data?.toString() ?? "",
9298
9298
  kind: W.Number,
9299
9299
  needsHover: (e) => e.hoverEffect === !0,
@@ -1,6 +1,6 @@
1
1
  import { h as e } from "./tooltip-CKO1pHJo.js";
2
2
  import { _ as t } from "./useTheme-CfeaVw5D.js";
3
- import { r as n } from "./code-visibility-azRFwO8n.js";
3
+ import { r as n } from "./code-visibility-Djtm07c9.js";
4
4
  var r = e("triangle-alert", [
5
5
  ["path", {
6
6
  d: "m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3",
package/dist/main.js CHANGED
@@ -7,7 +7,7 @@ import { C as w, F as T, G as E, I as D, J as O, K as k, L as A, P as j, R as M,
7
7
  import { a as oe, d as se, f as ce, h as V, i as le, n as ue, o as de, r as fe, t as pe } from "./tooltip-CKO1pHJo.js";
8
8
  import { $t as me, At as he, B as ge, Bt as _e, D as ve, Dt as ye, E as be, Et as xe, Ft as Se, Gt as Ce, J as we, Jt as Te, Kt as Ee, M as De, Mt as Oe, N as ke, O as Ae, Ot as je, Q as Me, R as Ne, S as Pe, Ut as Fe, V as Ie, Vt as Le, W as Re, Y as ze, Yt as Be, Z as Ve, _ as He, b as Ue, bt as We, cn as Ge, dt as Ke, en as qe, f as Je, fn as Ye, ft as Xe, g as Ze, gn as Qe, gt as $e, h as et, hn as tt, ht as nt, in as rt, j as it, jt as at, k as ot, kt as st, ln as ct, m as lt, nn as ut, nt as dt, o as ft, p as pt, pn as mt, q as ht, qt as gt, rt as _t, s as vt, sn as yt, st as bt, tn as xt, u as St, un as Ct, ut as wt, v as Tt, wt as Et, y as Dt, z as Ot, zt as kt } from "./error-entries-m7wjcSR-.js";
9
9
  import { D as At, E as jt, O as Mt, S as Nt, T as Pt, _ as Ft, a as It, c as Lt, d as Rt, f as zt, g as Bt, h as Vt, i as Ht, l as Ut, m as Wt, n as Gt, o as Kt, p as qt, r as Jt, s as Yt, t as Xt, u as Zt, v as Qt, w as $t, x as en, y as tn } from "./spec-CcT0slVc.js";
10
- import { $ as nn, $t as rn, A as an, At as on, B as sn, Bt as cn, C as ln, Ct as un, D as dn, Dt as fn, E as pn, Et as mn, F as hn, Ft as gn, G as _n, Gt as vn, H as yn, Ht as bn, I as xn, It as Sn, J as Cn, Jt as wn, K as Tn, Kt as En, L as Dn, Lt as On, M as kn, Mt as An, N as jn, Nt as Mn, O as Nn, Ot as Pn, P as Fn, Pt as In, Qt as Ln, R as Rn, Rt as zn, S as Bn, St as Vn, T as Hn, Tt as Un, U as Wn, Ut as Gn, V as Kn, Vt as qn, W as Jn, Wt as Yn, Xt as Xn, Y as Zn, Yt as Qn, Zt as $n, _ as er, _t as tr, at as nr, b as rr, c as ir, cn as ar, ct as or, d as sr, dn as cr, dt as lr, en as ur, et as dr, f as fr, fn as pr, ft as mr, g as hr, gt as gr, h as _r, ht as vr, in as yr, it as br, j as xr, jt as Sr, k as Cr, kt as wr, ln as Tr, lt as Er, m as Dr, mn as Or, mt as kr, nn as Ar, nt as jr, on as Mr, ot as Nr, p as Pr, pn as Fr, pt as Ir, q as Lr, qt as Rr, r as zr, rn as Br, rt as Vr, sn as Hr, st as Ur, t as Wr, tn as Gr, tt as Kr, u as qr, un as Jr, ut as Yr, v as Xr, vt as Zr, w as Qr, wt as $r, x as ei, xt as ti, y as ni, z as ri, zt as ii } from "./common-D7dvNADC.js";
10
+ import { $ as nn, $t as rn, A as an, At as on, B as sn, Bt as cn, C as ln, Ct as un, D as dn, Dt as fn, E as pn, Et as mn, F as hn, Ft as gn, G as _n, Gt as vn, H as yn, Ht as bn, I as xn, It as Sn, J as Cn, Jt as wn, K as Tn, Kt as En, L as Dn, Lt as On, M as kn, Mt as An, N as jn, Nt as Mn, O as Nn, Ot as Pn, P as Fn, Pt as In, Qt as Ln, R as Rn, Rt as zn, S as Bn, St as Vn, T as Hn, Tt as Un, U as Wn, Ut as Gn, V as Kn, Vt as qn, W as Jn, Wt as Yn, Xt as Xn, Y as Zn, Yt as Qn, Zt as $n, _ as er, _t as tr, at as nr, b as rr, c as ir, cn as ar, ct as or, d as sr, dn as cr, dt as lr, en as ur, et as dr, f as fr, fn as pr, ft as mr, g as hr, gt as gr, h as _r, ht as vr, in as yr, it as br, j as xr, jt as Sr, k as Cr, kt as wr, ln as Tr, lt as Er, m as Dr, mn as Or, mt as kr, nn as Ar, nt as jr, on as Mr, ot as Nr, p as Pr, pn as Fr, pt as Ir, q as Lr, qt as Rr, r as zr, rn as Br, rt as Vr, sn as Hr, st as Ur, t as Wr, tn as Gr, tt as Kr, u as qr, un as Jr, ut as Yr, v as Xr, vt as Zr, w as Qr, wt as $r, x as ei, xt as ti, y as ni, z as ri, zt as ii } from "./common-B_mI5VC0.js";
11
11
  import { c as ai, i as oi, n as si, o as ci, t as li } from "./useLifecycle-Dum8WSXq.js";
12
12
  import { t as ui } from "./check-P35dc2dH.js";
13
13
  import { A as di, C as fi, D as pi, E as mi, M as hi, O as gi, S as _i, T as vi, a as yi, b as bi, c as xi, i as Si, k as Ci, l as wi, n as Ti, o as Ei, r as Di, s as Oi, t as ki, w as Ai, x as ji } from "./select-B0LRDH0g.js";
@@ -15,8 +15,8 @@ import { At as Mi, C as Ni, Dt as Pi, J as Fi, K as Ii, R as Li, S as Ri, U as z
15
15
  import { C as ua, _ as da, a as fa, b as pa, c as ma, f as ha, h as ga, m as _a, p as va, s as ya, u as ba, x as xa, y as Sa } from "./toDate-DKH0_kMo.js";
16
16
  import { a as Ca, c as wa, d as Ta, f as Ea, i as Da, l as Oa, n as ka, o as Aa, r as ja, s as Ma, t as Na } from "./process-output-DIMk7xHI.js";
17
17
  import { a as Pa, c as Fa, d as Ia, i as La, l as Ra, r as za, u as Ba } from "./glide-utils-Bt8_pBa2.js";
18
- import { n as Va, t as Ha } from "./ErrorBoundary-BSX5FnuK.js";
19
- import { n as Ua, t as Wa } from "./globals-D7jFtSFG.js";
18
+ import { n as Va, t as Ha } from "./ErrorBoundary-CkptjJEU.js";
19
+ import { n as Ua, t as Wa } from "./globals-XxWFgM2b.js";
20
20
  import { t as Ga } from "./react-dom-DUmH0GXv.js";
21
21
  import { t as Ka } from "./jsx-runtime-3BD1kT0s.js";
22
22
  import { B as qa, C as H, Ct as Ja, D as Ya, E as Xa, F as U, G as Za, H as Qa, I as $a, L as eo, M as W, N as to, O as no, P as ro, Q as io, R as ao, S as G, St as oo, Tt as so, U as co, W as lo, Y as uo, Z as fo, _ as po, _t as mo, a as ho, b as go, bt as _o, c as vo, d as yo, f as bo, g as xo, h as So, ht as Co, i as wo, j as K, k as To, l as Eo, m as Do, n as Oo, o as ko, p as Ao, r as jo, s as Mo, u as No, ut as Po, v as Fo, vt as Io, w as Lo, wt as Ro, x as zo, xt as Bo, y as Vo, yt as Ho } from "./use-restore-focus-DBCITLeF.js";
@@ -39,7 +39,7 @@ import { t as Ms } from "./useAsyncData-D0mLJstx.js";
39
39
  import { i as Ns, n as Ps, r as Fs } from "./formats-V8ifA0Bk.js";
40
40
  import { D as Is, O as Ls } from "./vega-loader.browser-B9Pz0EGP.js";
41
41
  import { a as Rs, n as zs } from "./loader-Bm8cy3e8.js";
42
- import { c as Bs, d as Vs, i as Hs, l as Us, o as Ws, s as Gs, t as Ks, u as qs } from "./code-visibility-azRFwO8n.js";
42
+ import { c as Bs, d as Vs, i as Hs, l as Us, o as Ws, s as Gs, t as Ks, u as qs } from "./code-visibility-Djtm07c9.js";
43
43
  import { t as Js } from "./useDeepCompareMemoize-BpPWi3G3.js";
44
44
  import { t as Ys } from "./esm-D2gTsXEV.js";
45
45
  //#region ../node_modules/.pnpm/iconify-icon@2.3.0/node_modules/iconify-icon/dist/iconify-icon.mjs
@@ -10578,7 +10578,7 @@ function VC(e, t) {
10578
10578
  }
10579
10579
  //#endregion
10580
10580
  //#region src/plugins/impl/DataEditorPlugin.tsx
10581
- var HC = J.lazy(() => import("./glide-data-editor-CuC6Qmmd.js").then((e) => e.t)), UC = wS("marimo-data-editor", { cssStyles: [qS] }).withData(W({
10581
+ var HC = J.lazy(() => import("./glide-data-editor-q0CzLqHy.js").then((e) => e.t)), UC = wS("marimo-data-editor", { cssStyles: [qS] }).withData(W({
10582
10582
  initialValue: W({ edits: G(W({
10583
10583
  rowIdx: K(),
10584
10584
  columnId: U(),
@@ -30305,7 +30305,7 @@ function VR(e) {
30305
30305
  }
30306
30306
  //#endregion
30307
30307
  //#region src/components/editor/renderers/slides-layout/slides-layout.tsx
30308
- var HR = J.lazy(() => import("./reveal-component-ZUn41jSB.js")), UR = (e) => {
30308
+ var HR = J.lazy(() => import("./reveal-component-gMNFwxnS.js")), UR = (e) => {
30309
30309
  let t = (0, X.c)(33), { layout: n, setLayout: r, cells: i, mode: a } = e, o = ns(da), s = a === "read" || o, c = ns(He) > 1, [l, u] = (0, J.useState)(null), d;
30310
30310
  t[0] !== i || t[1] !== n ? (d = zP(i, n), t[0] = i, t[1] = n, t[2] = d) : d = t[2];
30311
30311
  let { slideCells: f, skippedIds: p, noOutputIds: m, slideTypes: h, startCellIndex: g } = d, _;
@@ -1,6 +1,6 @@
1
1
  import { a as e } from "./rolldown-runtime-Cb76FRpK.js";
2
2
  import { t } from "./react-sHtZdjeP.js";
3
- import { a as n } from "./glide-data-editor-CuC6Qmmd.js";
3
+ import { a as n } from "./glide-data-editor-q0CzLqHy.js";
4
4
  //#region ../node_modules/.pnpm/@glideapps+glide-data-grid@6.0.4-alpha24_lodash@4.17.21_marked@15.0.12_react-dom@19.3.0_27cffe7f1bc30287d59ac27db3240c5b/node_modules/@glideapps/glide-data-grid/dist/esm/internal/data-grid-overlay-editor/private/number-overlay-editor-style.js
5
5
  var r = /* @__PURE__ */ e(t()), i = /*#__PURE__*/ n("div")({
6
6
  name: "NumberOverlayEditorStyle",
@@ -4,14 +4,14 @@ import { t as o } from "./react-sHtZdjeP.js";
4
4
  import { t as s } from "./compiler-runtime-CldRdSsv.js";
5
5
  import { h as c, t as l } from "./tooltip-CKO1pHJo.js";
6
6
  import { O as u } from "./error-entries-m7wjcSR-.js";
7
- import { Q as d, X as f, Z as p, an as m, cn as h, i as g, in as _, l as v, n as y, r as b, t as x, u as S } from "./common-D7dvNADC.js";
7
+ import { Q as d, X as f, Z as p, an as m, cn as h, i as g, in as _, l as v, n as y, r as b, t as x, u as S } from "./common-B_mI5VC0.js";
8
8
  import { M as C } from "./select-B0LRDH0g.js";
9
9
  import { q as ee } from "./dropdown-menu-B_7xD2X4.js";
10
10
  import { _ as w } from "./toDate-DKH0_kMo.js";
11
11
  import { t as T } from "./jsx-runtime-3BD1kT0s.js";
12
12
  import { q as E } from "./use-restore-focus-DBCITLeF.js";
13
13
  import { A as D, C as O, b as k } from "./useTheme-CfeaVw5D.js";
14
- import { a as A, n as te, t as ne } from "./code-visibility-azRFwO8n.js";
14
+ import { a as A, n as te, t as ne } from "./code-visibility-Djtm07c9.js";
15
15
  /* empty css */
16
16
  var j = c("sticky-note", [["path", {
17
17
  d: "M21 9a2.4 2.4 0 0 0-.706-1.706l-3.588-3.588A2.4 2.4 0 0 0 15 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2z",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marimo-team/islands",
3
- "version": "0.25.3-dev104",
3
+ "version": "0.25.3-dev105",
4
4
  "main": "dist/main.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "type": "module",
@@ -0,0 +1,167 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import { createNotebookReducer, NotebookEvents } from "@marimo-team/client";
4
+ import * as Protocol from "@marimo-team/client/protocol";
5
+ import { afterEach, assert, describe, expect, it, vi } from "vitest";
6
+ import { document as documentResource } from "../resources/document";
7
+ import {
8
+ createStaticNotebookBackend,
9
+ type StaticNotebookSnapshot,
10
+ } from "../static";
11
+ import { fixtureEvents } from "./fixtures";
12
+
13
+ function snapshot(): StaticNotebookSnapshot {
14
+ const events = fixtureEvents("notebook-events.sse");
15
+ const notebook = events.find((event) => event.event === "notebook")?.data
16
+ .notebook;
17
+ const document = events.find((event) => event.event === "document")?.data
18
+ .document;
19
+ assert(notebook && document);
20
+ return {
21
+ notebook,
22
+ document,
23
+ cells: events.flatMap((event) =>
24
+ event.event === "cell" ? [event.data.cell] : [],
25
+ ),
26
+ };
27
+ }
28
+
29
+ const streams: NotebookEvents[] = [];
30
+ afterEach(async () => {
31
+ const closed = streams.splice(0);
32
+ closed.forEach((stream) => stream.close());
33
+ await Promise.all(closed.map((stream) => stream.closed));
34
+ vi.useRealTimers();
35
+ });
36
+
37
+ async function read(
38
+ backend: ReturnType<typeof createStaticNotebookBackend>,
39
+ cursor?: string,
40
+ ) {
41
+ const stream = new NotebookEvents(backend.transport, { cursor });
42
+ streams.push(stream);
43
+ const events: Protocol.EventOutput[] = [];
44
+ await new Promise<void>((resolve, reject) => {
45
+ stream.subscribe((event) => {
46
+ events.push(Protocol.Event.parse(event));
47
+ if (event.event === "ready") {
48
+ resolve();
49
+ }
50
+ }, reject);
51
+ });
52
+ return { stream, events };
53
+ }
54
+
55
+ describe("static notebook backend", () => {
56
+ it("supplies a complete document through the normal event reader and reducer", async () => {
57
+ const source = snapshot();
58
+ const { stream, events } = await read(createStaticNotebookBackend(source));
59
+ const reducer = createNotebookReducer({ document: documentResource });
60
+ const state = events.reduce(reducer.reduce, reducer.initial);
61
+ expect(events.map((event) => event.event)).toEqual([
62
+ "notebook",
63
+ "cell",
64
+ "cell",
65
+ "cell",
66
+ "document",
67
+ "ready",
68
+ ]);
69
+ expect(state.notebook).toEqual({ ...source.notebook, operations: [] });
70
+ expect(state.resources.document.current).toEqual({
71
+ revision: source.document,
72
+ cells: new Map(source.cells.map((cell) => [cell.id, cell])),
73
+ });
74
+ expect(stream.status).toBe("connected");
75
+ });
76
+
77
+ it("preserves withheld source and its revision metadata", async () => {
78
+ const source = snapshot();
79
+ const cells = source.cells.map((cell) => ({ ...cell, code: null }));
80
+ const { events } = await read(
81
+ createStaticNotebookBackend({ ...source, cells }),
82
+ );
83
+ expect(
84
+ events
85
+ .filter((event) => event.event === "cell")
86
+ .map((event) => event.data.cell),
87
+ ).toEqual(cells);
88
+ expect(
89
+ events.find((event) => event.event === "document")?.data.document,
90
+ ).toEqual(source.document);
91
+ });
92
+
93
+ it("serves an empty document as available", async () => {
94
+ const source = snapshot();
95
+ const document = { ...source.document, order: [] };
96
+ const { events } = await read(
97
+ createStaticNotebookBackend({ ...source, document, cells: [] }),
98
+ );
99
+ const reducer = createNotebookReducer({ document: documentResource });
100
+ expect(
101
+ events.reduce(reducer.reduce, reducer.initial).resources.document.current,
102
+ ).toEqual({
103
+ revision: document,
104
+ cells: new Map(),
105
+ });
106
+ });
107
+
108
+ it("keeps independent readers open without polling or reconnecting", async () => {
109
+ vi.useFakeTimers();
110
+ const backend = createStaticNotebookBackend(snapshot());
111
+ const first = await read(backend);
112
+ const second = await read(backend);
113
+ first.stream.close();
114
+ await first.stream.closed;
115
+ await vi.advanceTimersByTimeAsync(60_000);
116
+ expect(second.events).toEqual(first.events);
117
+ expect(second.stream.status).toBe("connected");
118
+ expect(vi.getTimerCount()).toBe(0);
119
+ });
120
+
121
+ it("resets a resume cursor before replaying its snapshot", async () => {
122
+ const { events } = await read(
123
+ createStaticNotebookBackend(snapshot()),
124
+ "previous-10",
125
+ );
126
+ expect(events[0]).toEqual({ event: "reset", data: {} });
127
+ expect(events.at(-1)).toEqual({ event: "ready", id: "static", data: {} });
128
+ });
129
+
130
+ it("advertises no mutations and returns a protocol failure for direct edits", async () => {
131
+ const backend = createStaticNotebookBackend(snapshot());
132
+ expect(await backend.client.edit([])).toEqual({
133
+ ok: false,
134
+ status: 501,
135
+ problem: {
136
+ type: "tag:marimo.io,2026:host/unsupported-operation",
137
+ title: "Unsupported operation",
138
+ status: 501,
139
+ detail: "Static notebooks do not support editing",
140
+ },
141
+ });
142
+ });
143
+
144
+ it("respects cancellation before opening, during delivery, and for commands", async () => {
145
+ const backend = createStaticNotebookBackend(snapshot());
146
+ const before = new AbortController();
147
+ before.abort();
148
+ const open = vi.fn();
149
+ const message = vi.fn();
150
+ await backend.transport(
151
+ { cursor: null, signal: before.signal },
152
+ { open, message },
153
+ );
154
+ expect(open).not.toHaveBeenCalled();
155
+ expect(message).not.toHaveBeenCalled();
156
+ await expect(
157
+ backend.client.edit([], { signal: before.signal }),
158
+ ).rejects.toBe(before.signal.reason);
159
+ const during = new AbortController();
160
+ message.mockImplementation(() => during.abort());
161
+ await backend.transport(
162
+ { cursor: null, signal: during.signal },
163
+ { open, message },
164
+ );
165
+ expect(message).toHaveBeenCalledOnce();
166
+ });
167
+ });
@@ -0,0 +1,10 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import type { NotebookClient, NotebookTransport } from "@marimo-team/client";
4
+
5
+ /** Events and commands for the same notebook, independent of React or a replica. */
6
+ export interface NotebookBackend {
7
+ readonly transport: NotebookTransport;
8
+ /** Method presence is not permission; the notebook advertises its operations. */
9
+ readonly client: Pick<NotebookClient, "edit">;
10
+ }
@@ -0,0 +1,70 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import type * as Protocol from "@marimo-team/client/protocol";
4
+ import type { NotebookBackend } from "./backend";
5
+
6
+ /** A document snapshot with its own identity and revision metadata. */
7
+ export interface StaticNotebookSnapshot {
8
+ readonly notebook: Omit<Protocol.Notebook, "operations">;
9
+ readonly document: Protocol.Document;
10
+ readonly cells: readonly Protocol.Cell[];
11
+ }
12
+
13
+ /**
14
+ * Serve a document without a server, runtime, or mutation operations.
15
+ * Callers and readers must treat the snapshot records as immutable.
16
+ */
17
+ export function createStaticNotebookBackend(
18
+ snapshot: StaticNotebookSnapshot,
19
+ ): NotebookBackend {
20
+ const frames: Protocol.Event[] = [
21
+ {
22
+ event: "notebook",
23
+ data: { notebook: { ...snapshot.notebook, operations: [] } },
24
+ },
25
+ ...snapshot.cells.map((cell): Protocol.CellEvent => ({
26
+ event: "cell",
27
+ data: { cell },
28
+ })),
29
+ { event: "document", data: { document: snapshot.document } },
30
+ { event: "ready", id: "static", data: {} },
31
+ ];
32
+
33
+ return {
34
+ transport: ({ cursor, signal }, { open, message }) =>
35
+ new Promise((resolve) => {
36
+ if (signal.aborted) {
37
+ resolve();
38
+ return;
39
+ }
40
+ // Finishing would ask NotebookEvents to reconnect. A snapshot stays open
41
+ // until its reader closes, even though there are no more updates.
42
+ signal.addEventListener("abort", () => resolve(), { once: true });
43
+ open();
44
+ if (cursor !== null && !signal.aborted) {
45
+ message({ event: "reset", data: {} });
46
+ }
47
+ for (const frame of frames) {
48
+ if (signal.aborted) {
49
+ return;
50
+ }
51
+ message(frame);
52
+ }
53
+ }),
54
+ client: {
55
+ edit: async (_changes, options) => {
56
+ options?.signal?.throwIfAborted();
57
+ return {
58
+ ok: false,
59
+ status: 501,
60
+ problem: {
61
+ type: "tag:marimo.io,2026:host/unsupported-operation",
62
+ title: "Unsupported operation",
63
+ status: 501,
64
+ detail: "Static notebooks do not support editing",
65
+ },
66
+ };
67
+ },
68
+ },
69
+ };
70
+ }