@marimo-team/islands 0.25.3-dev100 → 0.25.3-dev102

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.
Files changed (28) hide show
  1. package/dist/{ErrorBoundary-GScQVnJ8.js → ErrorBoundary-CTqzlCgA.js} +1 -1
  2. package/dist/{code-visibility-DnqM5oW_.js → code-visibility-BXe6iFL5.js} +1 -1
  3. package/dist/{common-D2HSo8Gy.js → common-DKpWKeru.js} +1 -1
  4. package/dist/{data-grid-overlay-editor-BgIMQs9G.js → data-grid-overlay-editor-D-lYQf1c.js} +1 -1
  5. package/dist/{feedback-button-BXcB6llx.js → feedback-button-CoLi84rn.js} +2 -2
  6. package/dist/{glide-data-editor-S-_J-7SO.js → glide-data-editor-bMB-b4l2.js} +3 -3
  7. package/dist/{globals-D4vBwcD6.js → globals-CmP7eFM1.js} +1 -1
  8. package/dist/main.js +6 -6
  9. package/dist/{number-overlay-editor-CUrUm7Np.js → number-overlay-editor-JFjUfbs3.js} +1 -1
  10. package/dist/{reveal-component-CFzdsOg8.js → reveal-component-BT1D05hU.js} +2 -2
  11. package/package.json +1 -1
  12. package/src/core/document/__tests__/lifecycle-scenarios.test.ts +61 -0
  13. package/src/core/document/__tests__/model.test.ts +6 -331
  14. package/src/core/document/__tests__/protocol-fixtures.ts +38 -0
  15. package/src/core/document/__tests__/recovery-scenarios.test.ts +230 -0
  16. package/src/core/document/__tests__/scenario-fixture.ts +42 -0
  17. package/src/core/document/__tests__/scenarios.test.ts +205 -0
  18. package/src/core/document/__tests__/state.test.ts +5 -13
  19. package/src/core/document/__tests__/submission.test.ts +24 -167
  20. package/src/core/document/__tests__/test-host.test.ts +92 -0
  21. package/src/core/document/__tests__/test-host.ts +247 -0
  22. package/src/core/document/__tests__/test-notebook.ts +139 -0
  23. package/src/core/document/__tests__/user.ts +61 -0
  24. package/src/core/document/index.ts +3 -1
  25. package/src/core/document/model.ts +7 -8
  26. package/src/core/document/select.ts +32 -0
  27. package/src/core/document/state.ts +3 -30
  28. package/src/core/document/sync.ts +29 -25
@@ -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-BXcB6llx.js").then((e) => ({ default: e.FeedbackModal }))), L = (e) => {
138
+ var I = (0, T.lazy)(() => import("./feedback-button-CoLi84rn.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-dev100", C = u(S), w = u(!0);
21
+ var S = x() || "0.25.3-dev102", 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-GScQVnJ8.js";
15
+ import { n as Xr, t as Zr } from "./ErrorBoundary-CTqzlCgA.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-S-_J-7SO.js";
5
+ import { a as o, i as s, n as c, r as l } from "./glide-data-editor-bMB-b4l2.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-TAO6kEhF.js";
7
7
  import { b as g } from "./select-B0LRDH0g.js";
8
- import { n as ee, t as te } from "./globals-D4vBwcD6.js";
8
+ import { n as ee, t as te } from "./globals-CmP7eFM1.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-DnqM5oW_.js";
13
+ import { t as ce } from "./code-visibility-BXe6iFL5.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-GScQVnJ8.js";
9
+ import { n as ne, t as re } from "./ErrorBoundary-CTqzlCgA.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-BgIMQs9G.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-D-lYQf1c.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-CUrUm7Np.js")), Us = {
9296
+ var Hs = K.lazy(async () => await import("./number-overlay-editor-JFjUfbs3.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-DnqM5oW_.js";
3
+ import { r as n } from "./code-visibility-BXe6iFL5.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-TAO6kEhF.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-D2HSo8Gy.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-DKpWKeru.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-DD-oSvZk.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-GScQVnJ8.js";
19
- import { n as Ua, t as Wa } from "./globals-D4vBwcD6.js";
18
+ import { n as Va, t as Ha } from "./ErrorBoundary-CTqzlCgA.js";
19
+ import { n as Ua, t as Wa } from "./globals-CmP7eFM1.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-DnqM5oW_.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-BXe6iFL5.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-S-_J-7SO.js").then((e) => e.t)), UC = wS("marimo-data-editor", { cssStyles: [qS] }).withData(W({
10581
+ var HC = J.lazy(() => import("./glide-data-editor-bMB-b4l2.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-CFzdsOg8.js")), UR = (e) => {
30308
+ var HR = J.lazy(() => import("./reveal-component-BT1D05hU.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-S-_J-7SO.js";
3
+ import { a as n } from "./glide-data-editor-bMB-b4l2.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-TAO6kEhF.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-D2HSo8Gy.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-DKpWKeru.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-DnqM5oW_.js";
14
+ import { a as A, n as te, t as ne } from "./code-visibility-BXe6iFL5.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-dev100",
3
+ "version": "0.25.3-dev102",
4
4
  "main": "dist/main.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "type": "module",
@@ -0,0 +1,61 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import { vi } from "vitest";
4
+ import { test } from "./scenario-fixture";
5
+
6
+ test("closing a session before the debounce prevents submission", async ({
7
+ expect,
8
+ session: { user, notebook, controller },
9
+ }) => {
10
+ user.changeCode("c3", "local");
11
+ const reason = new Error("Editing session ended");
12
+ controller.abort(reason);
13
+ await vi.advanceTimersByTimeAsync(1000);
14
+ expect(notebook.edit).not.toHaveBeenCalled();
15
+ expect(vi.getTimerCount()).toBe(0);
16
+ await expect(user.sync()).rejects.toBe(reason);
17
+ });
18
+
19
+ test("closing a session before a queued flush starts prevents submission", async ({
20
+ expect,
21
+ session: { user, notebook, controller },
22
+ }) => {
23
+ user.changeCode("c3", "local");
24
+ const flushed = user.sync();
25
+ const reason = new Error("Editing session ended");
26
+ controller.abort(reason);
27
+ await expect(flushed).rejects.toBe(reason);
28
+ expect(notebook.edit).not.toHaveBeenCalled();
29
+ expect(vi.getTimerCount()).toBe(0);
30
+ });
31
+
32
+ test.for(["request", "confirmation", "retry"] as const)(
33
+ "closing a session during %s stops synchronization",
34
+ async (phase, { expect, session: { user, host, notebook, controller } }) => {
35
+ user.changeCode("c3", "local");
36
+ const flushed = user.sync();
37
+ const request = await host.nextRequest();
38
+ if (phase === "confirmation") {
39
+ request.process();
40
+ request.deliverResponse();
41
+ } else if (phase === "retry") {
42
+ request.loseResponse();
43
+ }
44
+ await vi.advanceTimersByTimeAsync(0);
45
+ const reason = new Error("Editing session ended");
46
+ controller.abort(reason);
47
+ await expect(flushed).rejects.toBe(reason);
48
+ if (phase === "request") {
49
+ expect(request.signal.aborted).toBe(true);
50
+ }
51
+
52
+ host.changeCell("c1", { code: "later" });
53
+ host.deliverSnapshot();
54
+ user.changeCode("c3", "after closing");
55
+ await vi.advanceTimersByTimeAsync(1000);
56
+ expect(notebook.edit).toHaveBeenCalledOnce();
57
+ expect(vi.getTimerCount()).toBe(0);
58
+ await expect(user.sync()).rejects.toBe(reason);
59
+ await expect(user.retry()).rejects.toBe(reason);
60
+ },
61
+ );
@@ -67,8 +67,8 @@ function setup() {
67
67
  unmounted,
68
68
  edit: (code: string) =>
69
69
  store.set(model.editAtom, (tx) => tx.setCode("c1", code)),
70
- state: () => store.get(model.syncStateAtom),
71
- value: () => store.get(model.cellAtom("c1")),
70
+ status: () => store.get(model.syncStatusAtom),
71
+ value: () => store.get(model.documentAtom)?.cells.get("c1"),
72
72
  };
73
73
  }
74
74
 
@@ -101,71 +101,8 @@ describe("document synchronization", () => {
101
101
  expect(vi.getTimerCount()).toBe(0);
102
102
  });
103
103
 
104
- it("releases unsent reversals and flushes an unchanged document without a request", async () => {
105
- const { store, source, sent, edit, model, state, value } = setup();
106
- edit("temporary");
107
- edit("x = 1");
108
- expect(state()).toEqual({ status: "idle" });
109
- const remote = withCells(document(), { c1: cell("c1", "remote") });
110
- store.set(source, remote);
111
- expect(value()?.code).toBe("remote");
112
- await expect(model.flush()).resolves.toEqual(remote.revision);
113
- await vi.advanceTimersByTimeAsync(1000);
114
- expect(sent).not.toHaveBeenCalled();
115
- });
116
-
117
- it("flushes edits made after an earlier drain finishes but before its promise settles", async () => {
118
- const { store, source, sent, replies, edit, model } = setup();
119
- const first = model.flush();
120
- await Promise.resolve();
121
- edit("new code");
122
- const second = model.flush();
123
- const settled = vi.fn();
124
- void second.then(settled, settled);
125
-
126
- await vi.advanceTimersByTimeAsync(0);
127
- expect(sent).toHaveBeenCalledOnce();
128
- expect(settled).not.toHaveBeenCalled();
129
- expect(sent.mock.calls[0][0].changes).toMatchObject([
130
- { cell: { id: "c1", code: "new code" } },
131
- ]);
132
-
133
- const accepted = withCells(document(), {
134
- c1: cell("c1", "new code", { etag: "accepted", version: 2 }),
135
- });
136
- replies[0].resolve(response(accepted));
137
- store.set(source, accepted);
138
- await expect(second).resolves.toEqual(accepted.revision);
139
- await expect(first).resolves.toEqual(document().revision);
140
- });
141
-
142
- it("manually retries an uncertain request with its original identity", async () => {
143
- const { store, source, sent, replies, edit, model } = setup();
144
- edit("local");
145
- const flushed = model.flush();
146
- expect(model.flush()).toBe(flushed);
147
- const rejected = expectFailure(flushed, "offline");
148
- await vi.advanceTimersByTimeAsync(0);
149
- replies[0].reject(new Error("offline"));
150
- await vi.advanceTimersByTimeAsync(500);
151
- replies[1].reject(new Error("offline"));
152
- await vi.advanceTimersByTimeAsync(1000);
153
- replies[2].reject(new Error("offline"));
154
- await rejected;
155
- const retried = model.retry();
156
- await vi.advanceTimersByTimeAsync(0);
157
- expect(sent.mock.calls[3][0]).toMatchObject({
158
- idempotencyKey: sent.mock.calls[0][0].idempotencyKey,
159
- changes: sent.mock.calls[0][0].changes,
160
- });
161
- const accepted = withCells(document(), { c1: cell("c1", "local") });
162
- replies[3].resolve(response(accepted));
163
- store.set(source, accepted);
164
- await expect(retried).resolves.toEqual(accepted.revision);
165
- });
166
-
167
104
  it("fails without repeating a request when the response names another notebook", async () => {
168
- const { sent, replies, edit, model, state } = setup();
105
+ const { sent, replies, edit, model, status } = setup();
169
106
  edit("local");
170
107
  const rejected = expectFailure(model.flush(), "another notebook");
171
108
  await vi.advanceTimersByTimeAsync(0);
@@ -173,260 +110,9 @@ describe("document synchronization", () => {
173
110
  await rejected;
174
111
  await vi.advanceTimersByTimeAsync(5000);
175
112
  expect(sent).toHaveBeenCalledOnce();
176
- expect(state()).toMatchObject({ status: "error" });
177
- });
178
-
179
- it("debounces typing and waits for streamed confirmation before sending newer edits", async () => {
180
- const { store, source, sent, replies, edit, model, state, value } = setup();
181
- edit("first");
182
- await vi.advanceTimersByTimeAsync(300);
183
- edit("sent");
184
- await vi.advanceTimersByTimeAsync(399);
185
- expect(sent).not.toHaveBeenCalled();
186
- await vi.advanceTimersByTimeAsync(1);
187
- expect(sent).toHaveBeenCalledOnce();
188
- expect(sent.mock.calls[0][0].changes[0]).toMatchObject({
189
- cell: { code: "sent" },
190
- });
191
- edit("newer");
192
- const accepted = withCells(document(), {
193
- c1: cell("c1", "sent", { etag: "accepted" }),
194
- });
195
- replies[0].resolve(response(accepted));
196
- await vi.advanceTimersByTimeAsync(0);
197
- expect(state()).toEqual({ status: "confirming" });
198
- await vi.advanceTimersByTimeAsync(1000);
199
- expect(sent).toHaveBeenCalledOnce();
200
- store.set(source, accepted);
201
- await vi.advanceTimersByTimeAsync(0);
202
- expect(sent).toHaveBeenCalledTimes(2);
203
- expect(sent.mock.calls[1][0].changes[0]).toMatchObject({
204
- base_etag: "accepted",
205
- cell: { code: "newer" },
206
- });
207
- const latest = withCells(accepted, { c1: cell("c1", "newer") });
208
- const flushed = model.flush();
209
- replies[1].resolve(response(latest));
210
- store.set(source, latest);
211
- await expect(flushed).resolves.toEqual(latest.revision);
212
- expect(state()).toEqual({ status: "idle" });
213
- expect(value()?.code).toBe("newer");
214
- });
215
-
216
- it.each(["response", "snapshot"] as const)(
217
- "confirms a reset when %s arrives first without opening a stream",
218
- async (first) => {
219
- const { store, source, sent, replies, edit, model } = setup();
220
- edit("sent");
221
- const flushed = model.flush();
222
- await vi.advanceTimersByTimeAsync(0);
223
- store.set(source, null);
224
- const accepted = withCells(document(), { c1: cell("c1", "sent") });
225
- const later = withCells(accepted, { c2: cell("c2", "remote") });
226
- if (first === "response") {
227
- replies[0].resolve(response(accepted));
228
- } else {
229
- store.set(source, later);
230
- }
231
- await vi.advanceTimersByTimeAsync(0);
232
- expect(store.get(model.syncStateAtom)).toEqual({
233
- status: first === "response" ? "confirming" : "sending",
234
- });
235
- if (first === "response") {
236
- store.set(source, later);
237
- } else {
238
- replies[0].resolve(response(accepted));
239
- }
240
- await expect(flushed).resolves.toEqual(later.revision);
241
- expect(sent).toHaveBeenCalledOnce();
242
- expect(store.get(model.syncStateAtom)).toEqual({ status: "idle" });
243
- },
244
- );
245
-
246
- it("retries a lost response with the captured body and key, preserving subsequent typing", async () => {
247
- const { store, source, sent, replies, edit, model, value } = setup();
248
- edit("sent");
249
- const flushed = model.flush();
250
- await vi.advanceTimersByTimeAsync(0);
251
- const accepted = withCells(document(), {
252
- c1: cell("c1", "sent", { etag: "accepted" }),
253
- });
254
- store.set(source, accepted);
255
- edit("newer");
256
- replies[0].reject(new Error("response lost"));
257
- await vi.advanceTimersByTimeAsync(500);
258
- expect(sent).toHaveBeenCalledTimes(2);
259
- const original = sent.mock.calls[0][0];
260
- const retry = sent.mock.calls[1][0];
261
- expect(retry.idempotencyKey).toBe(original.idempotencyKey);
262
- expect(retry.changes).toBe(original.changes);
263
- replies[1].resolve(response(accepted));
264
- await vi.advanceTimersByTimeAsync(0);
265
- expect(sent).toHaveBeenCalledTimes(3);
266
- expect(sent.mock.calls[2][0].idempotencyKey).not.toBe(
267
- original.idempotencyKey,
268
- );
269
- const latest = withCells(accepted, { c1: cell("c1", "newer") });
270
- store.set(source, latest);
271
- replies[2].resolve(response(latest));
272
- await expect(flushed).resolves.toEqual(latest.revision);
273
- expect(value()?.code).toBe("newer");
274
- });
275
-
276
- it("exposes conflicts and only retries explicitly against a fresh confirmed revision", async () => {
277
- const { store, source, sent, replies, edit, model, state, value } = setup();
278
- edit("local");
279
- const flushed = model.flush();
280
- const rejected = expectFailure(flushed, "conflict");
281
- await vi.advanceTimersByTimeAsync(0);
282
- const remote = withCells(document(), {
283
- c1: cell("c1", "remote", { etag: "remote" }),
284
- });
285
- const problem: Protocol.Problem = {
286
- type: "tag:marimo.io,2026:host/conflict",
287
- title: "conflict",
288
- status: 409,
289
- edit: {
290
- change: 0,
291
- cell: remote.cells.get("c1")!,
292
- document: remote.revision,
293
- },
294
- };
295
- replies[0].resolve({ ok: false, status: 409, problem });
296
- await rejected;
297
- expect(state()).toMatchObject({
298
- status: "error",
299
- problem,
300
- retryable: true,
301
- });
302
- await expect(model.retry()).rejects.toThrow("Wait for the host");
303
- edit("updated local");
304
- store.set(source, remote);
305
- await vi.advanceTimersByTimeAsync(2000);
306
- expect(sent).toHaveBeenCalledOnce();
307
- expect(value()?.code).toBe("updated local");
308
- const retry = model.retry();
309
- await vi.advanceTimersByTimeAsync(0);
310
- expect(sent.mock.calls[1][0].changes[0]).toMatchObject({
311
- base_etag: "remote",
312
- cell: { code: "updated local" },
313
- });
314
- const accepted = withCells(remote, { c1: cell("c1", "updated local") });
315
- replies[1].resolve(response(accepted));
316
- store.set(source, accepted);
317
- await expect(retry).resolves.toEqual(accepted.revision);
113
+ expect(status()).toMatchObject({ status: "error" });
318
114
  });
319
115
 
320
- it("retains a draft after remote deletion and never sends the other edits alone", async () => {
321
- const { store, source, model, sent, replies, edit } = setup();
322
- edit("keep this");
323
- const flushed = model.flush();
324
- const rejected = expectFailure(flushed, "removed");
325
- await vi.advanceTimersByTimeAsync(0);
326
- const removed = withCells(document(), { c1: null });
327
- store.set(source, removed);
328
- replies[0].resolve({
329
- ok: false,
330
- status: 409,
331
- problem: {
332
- type: "tag:marimo.io,2026:host/conflict",
333
- title: "removed",
334
- status: 409,
335
- edit: { change: 0, cell: null, document: removed.revision },
336
- },
337
- });
338
- await rejected;
339
- store.set(model.editAtom, (tx) => tx.setCode("c2", "other edit"));
340
- await expect(model.retry()).rejects.toThrow("unavailable");
341
- expect(sent).toHaveBeenCalledOnce();
342
- store.set(source, withCells(removed, { c1: cell("c1", "restored") }));
343
- expect(store.get(model.cellAtom("c1"))?.code).toBe("keep this");
344
- });
345
-
346
- it("bounds retries and never replays an uncertain write after receipt expiry", async () => {
347
- const { sent, replies, edit, model, state } = setup();
348
- edit("local");
349
- const flushed = model.flush();
350
- const rejected = expectFailure(flushed, "offline");
351
- await vi.advanceTimersByTimeAsync(0);
352
- replies[0].reject(new Error("offline"));
353
- await vi.advanceTimersByTimeAsync(500);
354
- replies[1].reject(new Error("offline"));
355
- await vi.advanceTimersByTimeAsync(1000);
356
- replies[2].reject(new Error("offline"));
357
- await rejected;
358
- expect(state()).toMatchObject({ status: "error", retryable: true });
359
- await vi.advanceTimersByTimeAsync(60_000);
360
- await expect(model.retry()).rejects.toThrow("retry window expired");
361
- expect(sent).toHaveBeenCalledTimes(3);
362
- expect(state()).toMatchObject({ status: "error", retryable: false });
363
- });
364
-
365
- it("times out a hung attempt, aborts it, and retries the same request", async () => {
366
- const { store, source, sent, replies, edit, model } = setup();
367
- edit("local");
368
- const flushed = model.flush();
369
- await vi.advanceTimersByTimeAsync(10_500);
370
- expect(sent).toHaveBeenCalledTimes(2);
371
- expect(sent.mock.calls[0][0].signal.aborted).toBe(true);
372
- expect(sent.mock.calls[1][0].idempotencyKey).toBe(
373
- sent.mock.calls[0][0].idempotencyKey,
374
- );
375
- const accepted = withCells(document(), { c1: cell("c1", "local") });
376
- replies[1].resolve(response(accepted));
377
- store.set(source, accepted);
378
- await expect(flushed).resolves.toEqual(accepted.revision);
379
- });
380
-
381
- it("aborts before the debounce without sending any edits", async () => {
382
- const { sent, edit, model, controller } = setup();
383
- edit("local");
384
- controller.abort();
385
- await vi.advanceTimersByTimeAsync(1000);
386
- expect(sent).not.toHaveBeenCalled();
387
- expect(vi.getTimerCount()).toBe(0);
388
- await expect(model.flush()).rejects.toThrow("aborted");
389
- });
390
-
391
- it.each(["request", "confirmation", "retry"] as const)(
392
- "aborts during %s without leaving work or subscriptions",
393
- async (phase) => {
394
- const {
395
- store,
396
- source,
397
- sent,
398
- replies,
399
- edit,
400
- model,
401
- controller,
402
- unmounted,
403
- } = setup();
404
- edit("local");
405
- const flushed = model.flush();
406
- const reason = new Error("Editing session ended");
407
- const rejected = expectAbort(flushed, reason);
408
- await vi.advanceTimersByTimeAsync(0);
409
- if (phase === "confirmation") {
410
- replies[0].resolve(
411
- response(withCells(document(), { c1: cell("c1", "local") })),
412
- );
413
- }
414
- if (phase === "retry") {
415
- replies[0].reject(new Error("offline"));
416
- }
417
- await vi.advanceTimersByTimeAsync(0);
418
- controller.abort(reason);
419
- await rejected;
420
- expect(unmounted).toHaveBeenCalledOnce();
421
- expect(sent).toHaveBeenCalledOnce();
422
- store.set(source, withCells(document(), { c1: cell("c1", "later") }));
423
- edit("after disposal");
424
- expect(vi.getTimerCount()).toBe(0);
425
- await expect(model.flush()).rejects.toBe(reason);
426
- await expect(model.retry()).rejects.toBe(reason);
427
- },
428
- );
429
-
430
116
  it("ignores a late response and source update after its lifetime ends", async () => {
431
117
  const {
432
118
  store,
@@ -458,19 +144,8 @@ describe("document synchronization", () => {
458
144
  expect(vi.getTimerCount()).toBe(0);
459
145
  });
460
146
 
461
- it("rejects a queued flush when aborted before submission", async () => {
462
- const { model, edit, sent, controller } = setup();
463
- edit("local");
464
- const reason = new Error("Editing session ended");
465
- const rejected = expectAbort(model.flush(), reason);
466
- controller.abort(reason);
467
- await rejected;
468
- expect(sent).not.toHaveBeenCalled();
469
- expect(vi.getTimerCount()).toBe(0);
470
- });
471
-
472
147
  it("aborts an in-flight edit when notebook identity changes", async () => {
473
- const { store, source, sent, edit, model, state } = setup();
148
+ const { store, source, sent, edit, model, status } = setup();
474
149
  edit("local");
475
150
  const flushed = model.flush();
476
151
  const rejected = expectFailure(flushed, "notebook changed");
@@ -478,6 +153,6 @@ describe("document synchronization", () => {
478
153
  store.set(source, document("n2"));
479
154
  await rejected;
480
155
  expect(sent.mock.calls[0][0].signal.aborted).toBe(true);
481
- expect(state()).toMatchObject({ status: "error", retryable: false });
156
+ expect(status()).toMatchObject({ status: "error", retryable: false });
482
157
  });
483
158
  });
@@ -0,0 +1,38 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import { readFileSync } from "node:fs";
4
+ import NodeUrl from "node:url";
5
+ import type * as Protocol from "@marimo-team/client/protocol";
6
+ import { invariant } from "@/utils/invariant";
7
+ import { fixtureEvents } from "../../host/__tests__/fixtures";
8
+ import type { Document } from "../../host/resources/document";
9
+
10
+ export function protocolFixture(name: string): unknown {
11
+ return JSON.parse(
12
+ readFileSync(
13
+ new NodeUrl.URL(
14
+ `../../../../../protocols/host/fixtures/${name}`,
15
+ import.meta.url,
16
+ ),
17
+ "utf8",
18
+ ),
19
+ );
20
+ }
21
+
22
+ export function initialDocument(): Document {
23
+ const cells = new Map<Protocol.CellId, Protocol.Cell>();
24
+ let revision: Protocol.Document | undefined;
25
+ for (const event of fixtureEvents("notebook-events.sse")) {
26
+ if (event.event === "ready") {
27
+ break;
28
+ }
29
+ if (event.event === "cell") {
30
+ cells.set(event.data.cell.id, event.data.cell);
31
+ }
32
+ if (event.event === "document") {
33
+ revision = event.data.document;
34
+ }
35
+ }
36
+ invariant(revision !== undefined, "Fixture has no document");
37
+ return { revision, cells };
38
+ }