@marimo-team/frontend 0.25.3-dev80 → 0.25.3-dev82
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/{CellStatus-cD3S0wEA.js → CellStatus-DXroYLOP.js} +1 -1
- package/dist/assets/{ErrorBoundary-DJhsFCsX.js → ErrorBoundary-DaQ9THH_.js} +2 -2
- package/dist/assets/{JsonOutput-Pm6OCP3J.js → JsonOutput-VxzHyItt.js} +1 -1
- package/dist/assets/{MarimoErrorOutput-DlEjLCZ-.js → MarimoErrorOutput-D-hLaDA-.js} +1 -1
- package/dist/assets/{RenderHTML-CfQ6i3QV.js → RenderHTML-DjOEZL25.js} +1 -1
- package/dist/assets/{RunButton-DvuvkgrG.js → RunButton-CuhNTo5_.js} +1 -1
- package/dist/assets/{add-cell-with-ai-By51zSlC.js → add-cell-with-ai-BbVntTh7.js} +1 -1
- package/dist/assets/{add-connection-dialog-D7EYSraq.js → add-connection-dialog-Qf63aKV6.js} +1 -1
- package/dist/assets/{agent-panel-aCWa3Yai.js → agent-panel-DMOfF3qF.js} +1 -1
- package/dist/assets/{ai-model-dropdown-bwTRyFn4.js → ai-model-dropdown-B15l0R62.js} +2 -2
- package/dist/assets/{app-config-button-DGhsM0xw.js → app-config-button-BIMB8qjD.js} +1 -1
- package/dist/assets/{blob-e6VlOmQP.js → blob-De9GR2s6.js} +1 -1
- package/dist/assets/{cell-editor-M8dEnDzk.js → cell-editor-Di0cSFhC.js} +32 -11
- package/dist/assets/{cell-link-DFqfW-Ym.js → cell-link-DR3W2HC7.js} +1 -1
- package/dist/assets/{cells-T9_Y4Jlv.js → cells-styV7IX8.js} +6 -6
- package/dist/assets/{chat-display-DXn2Yrjr.js → chat-display-BzdYdN39.js} +1 -1
- package/dist/assets/{chat-panel-D3iUyZId.js → chat-panel-De3YzkSF.js} +1 -1
- package/dist/assets/{chat-ui-YuJtv3UJ.js → chat-ui-NTNapdtP.js} +1 -1
- package/dist/assets/{code-visibility-CV3S4xuf.js → code-visibility-csc3tgA0.js} +1 -1
- package/dist/assets/{column-preview-C6uXRJry.js → column-preview-DkH-oD8f.js} +1 -1
- package/dist/assets/{command-palette-CGNUQdgW.js → command-palette-Dg_xstl0.js} +1 -1
- package/dist/assets/{common-DeeLgjWQ.js → common-D7VAS6rd.js} +1 -1
- package/dist/assets/{components-DyAF3_Nf.js → components-CNmvC781.js} +1 -1
- package/dist/assets/config-BQT0uYUo.js +1 -0
- package/dist/assets/{config-CjQ4WJnJ.js → config-BbRbT9Ww.js} +1 -1
- package/dist/assets/{datasource-gqFgG9Qd.js → datasource-C3yfeDa2.js} +1 -1
- package/dist/assets/{dependency-graph-panel-B3T9kSTs.js → dependency-graph-panel-CjoA6L0w.js} +1 -1
- package/dist/assets/{documentation-panel-BBpGbT-f.js → documentation-panel-DHWFOiG1.js} +1 -1
- package/dist/assets/{download-C8Gw-D0Y.js → download-BrCpRZKN.js} +1 -1
- package/dist/assets/{edit-page-CBftLPze.js → edit-page-BPMo_x-T.js} +4 -4
- package/dist/assets/{error-entries-uyqOG5tg.js → error-entries-BkEAmmnD.js} +1 -1
- package/dist/assets/{error-panel-Cli5zPWi.js → error-panel-BhTfPA_i.js} +1 -1
- package/dist/assets/feedback-button-BAcClv7c.js +1 -0
- package/dist/assets/{feedback-button-CvrwBGr7.js → feedback-button-D9InK87G.js} +1 -1
- package/dist/assets/{file-explorer-panel-BSU5ultY.js → file-explorer-panel-BSd_ZbUC.js} +1 -1
- package/dist/assets/{file-name-input-CDrRlIiX.js → file-name-input-RHURglL_.js} +1 -1
- package/dist/assets/{floating-outline-GQD9km0W.js → floating-outline-Cm49rYYe.js} +1 -1
- package/dist/assets/{gallery-page-B-5H1fEt.js → gallery-page-DdMdOtdv.js} +1 -1
- package/dist/assets/{glide-data-editor-B1kpoetH.js → glide-data-editor-eiSlEUyR.js} +1 -1
- package/dist/assets/{home-page-3qsPTb4v.js → home-page-DwWvBB0R.js} +1 -1
- package/dist/assets/{hooks-BrFYiTJS.js → hooks-CHeUZHPp.js} +1 -1
- package/dist/assets/{index-D0IWODMU.js → index-DckSn2w6.js} +8 -8
- package/dist/assets/{layout-CGC8ajpe.js → layout-_lp06T0C.js} +3 -3
- package/dist/assets/{logs-panel-CdUc5vtA.js → logs-panel-CAAk72Gb.js} +1 -1
- package/dist/assets/{markdown-renderer-pKc_bFac.js → markdown-renderer-CAyCgogC.js} +1 -1
- package/dist/assets/{name-cell-input-csH3Cy54.js → name-cell-input-DA8hU9XT.js} +1 -1
- package/dist/assets/{outline-panel-DhsHYTuo.js → outline-panel-Cc5oQ4LL.js} +1 -1
- package/dist/assets/{packages-panel-BtkXVz3L.js → packages-panel-DTfKez6I.js} +1 -1
- package/dist/assets/{panel-accordion-state-Bco6nsUI.js → panel-accordion-state-BZHex38W.js} +1 -1
- package/dist/assets/panels-DU-SaRHy.js +105 -0
- package/dist/assets/{process-output-B2-bT4cq.js → process-output-CrDc_O1D.js} +1 -1
- package/dist/assets/{readonly-code-display-Dsp6vWPh.js → readonly-code-display-BR-_jUj8.js} +1 -1
- package/dist/assets/{readonly-python-code-B81AjzXM.js → readonly-python-code-D7jcy9ds.js} +1 -1
- package/dist/assets/{reveal-component-Cxo3hLGA.js → reveal-component-eLftBwOe.js} +1 -1
- package/dist/assets/{run-page-CLP1s8uX.js → run-page-mURyATDJ.js} +1 -1
- package/dist/assets/{runtime-details-state-CznIe40L.js → runtime-details-state-hb7A1jgR.js} +1 -1
- package/dist/assets/{scratchpad-panel-yHIW63VQ.js → scratchpad-panel-B_gEnoqj.js} +1 -1
- package/dist/assets/{session-panel-C7-wmZhc.js → session-panel-DmDjaPsN.js} +1 -1
- package/dist/assets/{snippets-panel-BARpOPyS.js → snippets-panel-o9KL5jMi.js} +1 -1
- package/dist/assets/state-BkAqJoW7.js +3 -0
- package/dist/assets/{state-Bye6rCo4.js → state-Os7LSUUZ.js} +1 -1
- package/dist/assets/{terminal-Bhcn_y1N.js → terminal-BI85khrS.js} +1 -1
- package/dist/assets/{tracing-DDohxo24.js → tracing-AsNGuDsh.js} +1 -1
- package/dist/assets/{tracing-panel-DYhGDAo5.js → tracing-panel-CBcmea6L.js} +2 -2
- package/dist/assets/{tree-actions-Cb-nUK1F.js → tree-actions-Cx5EDpA6.js} +1 -1
- package/dist/assets/{useAddCell-Bielffe0.js → useAddCell-uJJYtgwa.js} +1 -1
- package/dist/assets/{useBoolean-CMgj9hnq.js → useBoolean-8wlrTeus.js} +1 -1
- package/dist/assets/{useCellActionButton-DAcQhse_.js → useCellActionButton-CmQ-OcPa.js} +1 -1
- package/dist/assets/{useDeleteCell-BV4VemCv.js → useDeleteCell-CXgOCDqY.js} +1 -1
- package/dist/assets/{useDependencyPanelTab-PtXIVh53.js → useDependencyPanelTab-Ch6gm7uM.js} +1 -1
- package/dist/assets/{useInstallPackage-C7neMPwh.js → useInstallPackage-CYQoMdMQ.js} +1 -1
- package/dist/assets/{useNotebookActions-DZBB98fP.js → useNotebookActions-DGShHZ_1.js} +1 -1
- package/dist/assets/{useRunCells-BFeZMi7f.js → useRunCells-DAfDmOMC.js} +1 -1
- package/dist/assets/{useSplitCell-DllkNFke.js → useSplitCell-NLULgcq7.js} +1 -1
- package/dist/assets/{vega-component-D7JFthDD.js → vega-component-BKJU3uaB.js} +1 -1
- package/dist/index.html +24 -24
- package/package.json +1 -1
- package/src/core/host/AGENTS.md +6 -5
- package/src/core/host/__tests__/notebook-client.test.ts +368 -350
- package/src/core/host/connection.ts +1 -40
- package/src/core/host/notebook-client.ts +129 -186
- package/src/core/wasm/bridge.ts +0 -8
- package/src/core/websocket/__tests__/connection-state.test.ts +0 -12
- package/src/core/websocket/__tests__/useNotebookClient.hook.test.tsx +133 -59
- package/src/core/websocket/__tests__/useNotebookSession.hook.test.tsx +152 -250
- package/src/core/websocket/connection-state.ts +0 -10
- package/src/core/websocket/transports/__tests__/notebook.test.ts +502 -0
- package/src/core/websocket/transports/notebook.ts +213 -0
- package/src/core/websocket/useNotebookClient.ts +28 -17
- package/dist/assets/config-DzdxZb17.js +0 -1
- package/dist/assets/feedback-button-DknbRCN_.js +0 -1
- package/dist/assets/panels-D1i80TGn.js +0 -107
- package/dist/assets/state-BRYi026f.js +0 -3
- package/dist/assets/ws-BXWY541V.js +0 -22
- package/src/core/host/__tests__/events.test.ts +0 -49
- package/src/core/host/events.ts +0 -68
- package/src/core/websocket/__tests__/createConnectionTransport.test.ts +0 -49
- package/src/core/websocket/transports/__tests__/basic.test.ts +0 -275
- package/src/core/websocket/transports/__tests__/sse.test.ts +0 -352
- package/src/core/websocket/transports/__tests__/transport.test.ts +0 -308
- package/src/core/websocket/transports/__tests__/ws.test.ts +0 -125
- package/src/core/websocket/transports/basic.ts +0 -102
- package/src/core/websocket/transports/create.ts +0 -31
- package/src/core/websocket/transports/sse.ts +0 -320
- package/src/core/websocket/transports/transport.ts +0 -50
- package/src/core/websocket/transports/ws.ts +0 -98
|
@@ -5,52 +5,48 @@ import { act, renderHook } from "@testing-library/react";
|
|
|
5
5
|
import { createStore, Provider } from "jotai";
|
|
6
6
|
import type { PropsWithChildren } from "react";
|
|
7
7
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
8
|
+
import type { NotebookTransport } from "@marimo-team/client";
|
|
8
9
|
import { ConnectionState } from "../types";
|
|
9
|
-
import {
|
|
10
|
-
import
|
|
10
|
+
import { Deferred } from "@/utils/Deferred";
|
|
11
|
+
import { createNotebookTransport } from "../transports/notebook";
|
|
11
12
|
import { useNotebookClient } from "../useNotebookClient";
|
|
12
13
|
import { fixtureMessages } from "../../host/__tests__/fixtures";
|
|
13
14
|
import { agentAttachmentsAtom } from "../../attachments/state";
|
|
14
15
|
import { notebookClientAtom } from "../state";
|
|
15
16
|
|
|
16
|
-
vi.mock("../transports/
|
|
17
|
+
vi.mock("../transports/notebook", () => ({ createNotebookTransport: vi.fn() }));
|
|
17
18
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}),
|
|
31
|
-
send: vi.fn(),
|
|
32
|
-
addEventListener: events.addEventListener.bind(events),
|
|
33
|
-
removeEventListener: events.removeEventListener.bind(events),
|
|
34
|
-
};
|
|
19
|
+
/** A transport whose connections the test drives. */
|
|
20
|
+
function makeTransport() {
|
|
21
|
+
const opened: {
|
|
22
|
+
signal: AbortSignal;
|
|
23
|
+
open(): void;
|
|
24
|
+
receive(message: unknown): void;
|
|
25
|
+
}[] = [];
|
|
26
|
+
const transport: NotebookTransport = ({ signal }, handlers) =>
|
|
27
|
+
new Promise((resolve) => {
|
|
28
|
+
opened.push({ signal, open: handlers.open, receive: handlers.message });
|
|
29
|
+
signal.addEventListener("abort", () => resolve(), { once: true });
|
|
30
|
+
});
|
|
35
31
|
return {
|
|
36
32
|
transport,
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
new MessageEvent("message", { data: JSON.stringify(message) }),
|
|
40
|
-
),
|
|
33
|
+
opened,
|
|
34
|
+
/** Open the latest connection and deliver a host-framed notification. */
|
|
41
35
|
notify: () =>
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
data: { marimo: { op: "reconnected" } },
|
|
47
|
-
}),
|
|
48
|
-
}),
|
|
49
|
-
),
|
|
36
|
+
opened.at(-1)?.receive({
|
|
37
|
+
event: "marimo",
|
|
38
|
+
data: { marimo: { op: "reconnected" } },
|
|
39
|
+
}),
|
|
50
40
|
};
|
|
51
41
|
}
|
|
52
42
|
|
|
53
|
-
function setup(
|
|
43
|
+
function setup(
|
|
44
|
+
strict = false,
|
|
45
|
+
readiness: {
|
|
46
|
+
waitUntilReady?: () => Promise<void>;
|
|
47
|
+
checkHealth?: () => Promise<boolean>;
|
|
48
|
+
} = {},
|
|
49
|
+
) {
|
|
54
50
|
const store = createStore();
|
|
55
51
|
const options = {
|
|
56
52
|
static: false,
|
|
@@ -60,6 +56,7 @@ function setup(strict = false) {
|
|
|
60
56
|
waitUntilReady: async () => undefined,
|
|
61
57
|
checkHealth: async () => true,
|
|
62
58
|
onNotification: vi.fn(),
|
|
59
|
+
...readiness,
|
|
63
60
|
};
|
|
64
61
|
const wrapper = ({ children }: PropsWithChildren) => (
|
|
65
62
|
<Provider store={store}>{children}</Provider>
|
|
@@ -73,19 +70,97 @@ function setup(strict = false) {
|
|
|
73
70
|
}
|
|
74
71
|
|
|
75
72
|
beforeEach(() => {
|
|
76
|
-
vi.mocked(
|
|
73
|
+
vi.mocked(createNotebookTransport).mockReset();
|
|
77
74
|
});
|
|
78
75
|
|
|
79
76
|
describe("useNotebookClient", () => {
|
|
77
|
+
it.each([true, false])(
|
|
78
|
+
"rechecks health after failed startup (healthy: %s)",
|
|
79
|
+
async (healthy) => {
|
|
80
|
+
const fake = makeTransport();
|
|
81
|
+
vi.mocked(createNotebookTransport).mockImplementation(
|
|
82
|
+
({ url }) =>
|
|
83
|
+
async (attempt, handlers) => {
|
|
84
|
+
if (typeof url === "function") {
|
|
85
|
+
await url();
|
|
86
|
+
}
|
|
87
|
+
if (!attempt.signal.aborted) {
|
|
88
|
+
return fake.transport(attempt, handlers);
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
);
|
|
92
|
+
const waitUntilReady = vi
|
|
93
|
+
.fn()
|
|
94
|
+
.mockRejectedValue(new Error("Startup gave up"));
|
|
95
|
+
const checkHealth = vi.fn().mockResolvedValue(healthy);
|
|
96
|
+
const { result, store, unmount } = setup(false, {
|
|
97
|
+
waitUntilReady,
|
|
98
|
+
checkHealth,
|
|
99
|
+
});
|
|
100
|
+
await act(async () => Promise.resolve());
|
|
101
|
+
expect(store.get(result.current.connectionAtom).state).toBe(
|
|
102
|
+
ConnectionState.CLOSED,
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
await act(async () => result.current.reconnect());
|
|
106
|
+
expect({
|
|
107
|
+
healthChecks: checkHealth.mock.calls.length,
|
|
108
|
+
initialWaits: waitUntilReady.mock.calls.length,
|
|
109
|
+
connections: fake.opened.length,
|
|
110
|
+
state: store.get(result.current.connectionAtom).state,
|
|
111
|
+
}).toEqual({
|
|
112
|
+
healthChecks: 1,
|
|
113
|
+
initialWaits: 1,
|
|
114
|
+
connections: healthy ? 1 : 0,
|
|
115
|
+
state: healthy ? ConnectionState.CONNECTING : ConnectionState.CLOSED,
|
|
116
|
+
});
|
|
117
|
+
act(() => fake.opened.at(-1)?.open());
|
|
118
|
+
expect(store.get(result.current.connectionAtom).state).toBe(
|
|
119
|
+
healthy ? ConnectionState.OPEN : ConnectionState.CLOSED,
|
|
120
|
+
);
|
|
121
|
+
unmount();
|
|
122
|
+
},
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
it("waits for initial readiness without reviving an unmounted connection", async () => {
|
|
126
|
+
const ready = new Deferred<void>();
|
|
127
|
+
const resolvedUrl = vi.fn();
|
|
128
|
+
vi.mocked(createNotebookTransport).mockImplementation(
|
|
129
|
+
({ url }) =>
|
|
130
|
+
async () => {
|
|
131
|
+
if (typeof url === "function") {
|
|
132
|
+
await url();
|
|
133
|
+
resolvedUrl();
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
);
|
|
137
|
+
const { result, store, unmount } = setup(false, {
|
|
138
|
+
waitUntilReady: () => ready.promise,
|
|
139
|
+
});
|
|
140
|
+
await act(async () => Promise.resolve());
|
|
141
|
+
expect(resolvedUrl).not.toHaveBeenCalled();
|
|
142
|
+
expect(store.get(result.current.connectionAtom)).toEqual({
|
|
143
|
+
state: ConnectionState.CONNECTING,
|
|
144
|
+
});
|
|
145
|
+
unmount();
|
|
146
|
+
await act(async () => ready.resolve());
|
|
147
|
+
expect(store.get(result.current.connectionAtom)).toEqual({
|
|
148
|
+
state: ConnectionState.NOT_STARTED,
|
|
149
|
+
});
|
|
150
|
+
expect(store.get(notebookClientAtom)).toBeNull();
|
|
151
|
+
});
|
|
152
|
+
|
|
80
153
|
it("exposes host presence to the UI without waiting for kernel-ready", async () => {
|
|
81
|
-
const
|
|
82
|
-
vi.mocked(
|
|
154
|
+
const fake = makeTransport();
|
|
155
|
+
vi.mocked(createNotebookTransport).mockReturnValue(fake.transport);
|
|
83
156
|
const { result, store, unmount } = setup();
|
|
84
157
|
await act(async () => Promise.resolve());
|
|
85
158
|
expect(store.get(notebookClientAtom)).toBe(result.current);
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
159
|
+
const [connection] = fake.opened;
|
|
160
|
+
act(() => {
|
|
161
|
+
connection.open();
|
|
162
|
+
fixtureMessages("notebook-events.sse").forEach(connection.receive);
|
|
163
|
+
});
|
|
89
164
|
expect(store.get(agentAttachmentsAtom)).toEqual([
|
|
90
165
|
{
|
|
91
166
|
id: "a2",
|
|
@@ -101,49 +176,48 @@ describe("useNotebookClient", () => {
|
|
|
101
176
|
});
|
|
102
177
|
|
|
103
178
|
it("uses current callbacks and URL providers without reconnecting on a rerender", async () => {
|
|
104
|
-
const
|
|
105
|
-
vi.mocked(
|
|
179
|
+
const fake = makeTransport();
|
|
180
|
+
vi.mocked(createNotebookTransport).mockReturnValue(fake.transport);
|
|
106
181
|
const { rerender, options, unmount } = setup();
|
|
107
182
|
await act(async () => Promise.resolve());
|
|
108
183
|
const onNotification = vi.fn();
|
|
109
184
|
rerender({ ...options, onNotification, url: () => "ws://redirected/ws" });
|
|
110
|
-
act(() =>
|
|
185
|
+
act(() => fake.notify());
|
|
111
186
|
expect(options.onNotification).not.toHaveBeenCalled();
|
|
112
187
|
expect(onNotification).toHaveBeenCalledExactlyOnceWith({
|
|
113
188
|
data: { op: "reconnected" },
|
|
114
189
|
});
|
|
115
|
-
expect(
|
|
116
|
-
|
|
190
|
+
expect(createNotebookTransport).toHaveBeenCalledOnce();
|
|
191
|
+
const [{ url }] = vi.mocked(createNotebookTransport).mock.calls[0];
|
|
192
|
+
expect(String(await (url as () => Promise<string>)())).toBe(
|
|
117
193
|
"ws://redirected/ws",
|
|
118
194
|
);
|
|
119
195
|
unmount();
|
|
120
196
|
});
|
|
121
197
|
|
|
122
|
-
it("
|
|
123
|
-
const
|
|
124
|
-
vi.mocked(
|
|
198
|
+
it("closes every stream under Strict Mode and only follows the active lifetime", async () => {
|
|
199
|
+
const fakes: ReturnType<typeof makeTransport>[] = [];
|
|
200
|
+
vi.mocked(createNotebookTransport).mockImplementation(() => {
|
|
125
201
|
const next = makeTransport();
|
|
126
|
-
|
|
202
|
+
fakes.push(next);
|
|
127
203
|
return next.transport;
|
|
128
204
|
});
|
|
129
205
|
const { result, store, options, unmount } = setup(true);
|
|
130
206
|
await act(async () => Promise.resolve());
|
|
131
|
-
expect(
|
|
132
|
-
expect(
|
|
133
|
-
|
|
134
|
-
|
|
207
|
+
expect(fakes).toHaveLength(2);
|
|
208
|
+
expect(fakes[0].opened.every((attempt) => attempt.signal.aborted)).toBe(
|
|
209
|
+
true,
|
|
210
|
+
);
|
|
211
|
+
const [active] = fakes[1].opened;
|
|
212
|
+
act(() => active.open());
|
|
135
213
|
expect(store.get(result.current.connectionAtom)).toEqual({
|
|
136
214
|
state: ConnectionState.OPEN,
|
|
137
215
|
});
|
|
138
|
-
act(() =>
|
|
139
|
-
for (const transport of transports) {
|
|
140
|
-
transport.notify();
|
|
141
|
-
}
|
|
142
|
-
});
|
|
216
|
+
act(() => fakes.forEach((fake) => fake.notify()));
|
|
143
217
|
expect(options.onNotification).toHaveBeenCalledOnce();
|
|
144
218
|
unmount();
|
|
145
|
-
expect(
|
|
219
|
+
expect(active.signal.aborted).toBe(true);
|
|
146
220
|
await result.current.reconnect();
|
|
147
|
-
expect(
|
|
221
|
+
expect(fakes[1].opened).toHaveLength(1);
|
|
148
222
|
});
|
|
149
223
|
});
|