@marimo-team/islands 0.23.15-dev33 → 0.23.15-dev5

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 (85) hide show
  1. package/dist/{ConnectedDataExplorerComponent-7p7rt9iw.js → ConnectedDataExplorerComponent-CU4fZJzG.js} +4 -4
  2. package/dist/{ErrorBoundary-B_CAG7_e.js → ErrorBoundary-DE6tzZf-.js} +1 -1
  3. package/dist/{ImageComparisonComponent-1i5IBhon.js → ImageComparisonComponent-CHrI72em.js} +129 -173
  4. package/dist/assets/__vite-browser-external-BQCLNwri.js +1 -0
  5. package/dist/assets/worker-DAWRHcPq.js +73 -0
  6. package/dist/{chat-ui-C0wkSwxB.js → chat-ui-DinOvbWu.js} +3111 -3111
  7. package/dist/{code-visibility-BI94mK4u.js → code-visibility-DBuB5MVY.js} +864 -946
  8. package/dist/{debounce-BOD3DbfP.js → constants-T20xxyNf.js} +24 -1
  9. package/dist/{formats-BX3uDQbB.js → formats-Dzx4J_z1.js} +1 -1
  10. package/dist/{glide-data-editor-CwZz71BD.js → glide-data-editor-CjTu7ukN.js} +3 -3
  11. package/dist/{html-to-image-D8Q5SLSJ.js → html-to-image-_wGfk8V-.js} +2241 -2277
  12. package/dist/{input-BGPrFH3g.js → input-DtsN7xm-.js} +2 -2
  13. package/dist/main.js +1201 -1297
  14. package/dist/{mermaid-UdmxG2PZ.js → mermaid-BYqXy_NE.js} +2 -2
  15. package/dist/{process-output-C_A12w9J.js → process-output-Mh4UrjwM.js} +1 -1
  16. package/dist/{reveal-component-ELidGmPF.js → reveal-component-DfOZrkd6.js} +605 -618
  17. package/dist/{spec-DSs9v0xx.js → spec-Cz-Bj1JI.js} +1 -1
  18. package/dist/style.css +1 -1
  19. package/dist/{toDate-CKpRx4TS.js → toDate-CWNNlFEX.js} +4 -3
  20. package/dist/{useAsyncData-BSOyAbac.js → useAsyncData-KfHB8wQR.js} +1 -1
  21. package/dist/{useDeepCompareMemoize-BTLeWzuR.js → useDeepCompareMemoize-nJMtxhm4.js} +1 -1
  22. package/dist/{useLifecycle-C6wHjkhW.js → useLifecycle-DegSo0lV.js} +1 -1
  23. package/dist/{useTheme-Df_vGflw.js → useTheme-6eZ3GOTS.js} +27 -62
  24. package/dist/{vega-component-DNHEV0u0.js → vega-component-DzyyM9fc.js} +6 -6
  25. package/package.json +1 -1
  26. package/src/components/chat/__tests__/chat-utils.test.ts +1 -244
  27. package/src/components/chat/chat-panel.tsx +67 -196
  28. package/src/components/chat/chat-utils.ts +2 -111
  29. package/src/components/editor/SortableCell.tsx +2 -6
  30. package/src/components/editor/actions/pair-with-agent-modal.tsx +64 -20
  31. package/src/components/editor/ai/add-cell-with-ai.tsx +5 -14
  32. package/src/components/editor/ai/ai-completion-editor.tsx +1 -4
  33. package/src/components/editor/cell/cell-context-menu.tsx +0 -7
  34. package/src/components/editor/chrome/panels/packages-panel.tsx +1 -11
  35. package/src/components/editor/columns/cell-column.tsx +4 -34
  36. package/src/components/editor/columns/sortable-column.tsx +4 -24
  37. package/src/components/editor/navigation/__tests__/navigation.test.ts +0 -33
  38. package/src/components/editor/navigation/navigation.ts +1 -8
  39. package/src/components/editor/notebook-cell.tsx +106 -203
  40. package/src/components/editor/renderers/cell-array.tsx +13 -26
  41. package/src/components/editor/renderers/cells-renderer.tsx +2 -8
  42. package/src/components/editor/renderers/slides-layout/__tests__/plugin.test.ts +0 -29
  43. package/src/components/editor/renderers/slides-layout/types.ts +0 -4
  44. package/src/components/editor/renderers/vertical-layout/vertical-layout.tsx +19 -19
  45. package/src/components/slides/reveal-component.tsx +4 -37
  46. package/src/components/slides/slide-form.tsx +0 -72
  47. package/src/core/cells/__tests__/utils.test.ts +0 -59
  48. package/src/core/cells/utils.ts +0 -51
  49. package/src/core/codemirror/completion/__tests__/signature-hint.test.ts +3 -97
  50. package/src/core/codemirror/completion/signature-hint.ts +11 -78
  51. package/src/core/constants.ts +0 -6
  52. package/src/core/islands/__tests__/bridge.test.ts +46 -341
  53. package/src/core/islands/__tests__/parse.test.ts +0 -105
  54. package/src/core/islands/bootstrap.ts +3 -8
  55. package/src/core/islands/bridge.ts +23 -116
  56. package/src/core/islands/components/web-components.tsx +15 -76
  57. package/src/core/islands/constants.ts +0 -1
  58. package/src/core/islands/main.ts +3 -69
  59. package/src/core/islands/parse.ts +23 -70
  60. package/src/core/islands/worker/worker.tsx +57 -145
  61. package/src/core/wasm/worker/bootstrap.ts +20 -113
  62. package/src/css/app/Cell.css +0 -10
  63. package/src/hooks/useHotkey.ts +4 -29
  64. package/src/plugins/impl/anywidget/__tests__/host.test.ts +9 -6
  65. package/src/plugins/impl/anywidget/__tests__/widget-binding.test.ts +61 -22
  66. package/src/plugins/impl/anywidget/model-proxy.ts +13 -0
  67. package/src/plugins/impl/anywidget/widget-binding.ts +34 -4
  68. package/src/plugins/impl/image-comparison/ImageComparisonComponent.tsx +2 -53
  69. package/src/plugins/impl/matplotlib/__tests__/matplotlib-renderer.test.ts +3 -71
  70. package/src/plugins/impl/matplotlib/matplotlib-renderer.ts +0 -1
  71. package/src/theme/useTheme.ts +1 -16
  72. package/dist/assets/__vite-browser-external-CjNAy01L.js +0 -1
  73. package/dist/assets/worker-B3XPCb6y.js +0 -98
  74. package/src/components/chat/__tests__/message-queue.test.tsx +0 -121
  75. package/src/components/editor/__tests__/output-persistence.test.tsx +0 -241
  76. package/src/components/editor/actions/__tests__/pair-with-agent-commands.test.ts +0 -153
  77. package/src/components/editor/actions/pair-with-agent-commands.ts +0 -109
  78. package/src/components/editor/columns/__tests__/cell-column.test.tsx +0 -105
  79. package/src/components/editor/renderers/__tests__/cells-renderer.test.tsx +0 -66
  80. package/src/core/islands/__tests__/main.test.ts +0 -176
  81. package/src/core/islands/components/__tests__/web-components.test.tsx +0 -214
  82. package/src/core/islands/worker/__tests__/controller.test.ts +0 -173
  83. package/src/hooks/__tests__/useHotkey.test.tsx +0 -88
  84. package/src/plugins/impl/image-comparison/__tests__/ImageComparisonComponent.test.tsx +0 -71
  85. package/src/theme/__tests__/useTheme.test.ts +0 -68
@@ -45,22 +45,14 @@ vi.stubGlobal("URL", MockURL);
45
45
  const {
46
46
  mockBridge,
47
47
  mockLoadPackages,
48
- mockReplaceSessionRequest,
49
- mockStopSessionRequest,
50
48
  mockStartSessionRequest,
51
- mockMessageListeners,
52
- mockStoreSet,
53
49
  mockParseMarimoIslandApps,
54
50
  mockCreateMarimoFile,
55
51
  mockGetMarimoExportContext,
56
52
  } = vi.hoisted(() => ({
57
53
  mockBridge: vi.fn(),
58
54
  mockLoadPackages: vi.fn(),
59
- mockReplaceSessionRequest: vi.fn(),
60
- mockStopSessionRequest: vi.fn(),
61
55
  mockStartSessionRequest: vi.fn(),
62
- mockMessageListeners: new Map<string, (payload: never) => void>(),
63
- mockStoreSet: vi.fn(),
64
56
  mockParseMarimoIslandApps: vi.fn<() => TestIslandApp[]>(() => []),
65
57
  mockCreateMarimoFile: vi.fn(),
66
58
  mockGetMarimoExportContext: vi.fn<() => TestExportContext | undefined>(
@@ -74,19 +66,13 @@ vi.mock("@/core/wasm/rpc", () => ({
74
66
  request: {
75
67
  bridge: mockBridge,
76
68
  loadPackages: mockLoadPackages,
77
- replaceSession: mockReplaceSessionRequest,
78
69
  startSession: mockStartSessionRequest,
79
- stopSession: mockStopSessionRequest,
80
70
  },
81
71
  send: {
82
72
  consumerReady: vi.fn(),
83
73
  },
84
74
  },
85
- addMessageListener: vi.fn(
86
- (name: string, listener: (payload: never) => void) => {
87
- mockMessageListeners.set(name, listener);
88
- },
89
- ),
75
+ addMessageListener: vi.fn(),
90
76
  }),
91
77
  }));
92
78
 
@@ -111,11 +97,10 @@ vi.mock("@/core/meta/globals", () => ({
111
97
  }));
112
98
 
113
99
  // Mock the jotai store
114
- vi.mock("@/core/state/jotai", async (importOriginal) => ({
115
- ...(await importOriginal<typeof import("@/core/state/jotai")>()),
100
+ vi.mock("@/core/state/jotai", () => ({
116
101
  store: {
117
102
  get: vi.fn(),
118
- set: mockStoreSet,
103
+ set: vi.fn(),
119
104
  },
120
105
  }));
121
106
 
@@ -130,69 +115,40 @@ describe("IslandsPyodideBridge", () => {
130
115
  mockParseMarimoIslandApps.mockReturnValue([]);
131
116
  mockCreateMarimoFile.mockReset();
132
117
  mockGetMarimoExportContext.mockReturnValue(undefined);
133
- mockMessageListeners.clear();
134
- bridge = new IslandsPyodideBridge();
118
+ bridge = new IslandsPyodideBridge({ autoStartSessions: false });
135
119
  });
136
120
 
137
- function signalWorkerReady() {
138
- const listener = mockMessageListeners.get("ready");
139
- if (!listener) {
140
- throw new Error("Missing worker ready listener");
141
- }
142
- listener({} as never);
143
- }
144
-
145
- function app(id = "app-1"): TestIslandApp {
146
- return {
147
- id,
148
- cells: [{ code: "x = 1", idx: 0, output: "<div>1</div>" }],
149
- };
150
- }
151
-
152
- function setSingleApp(file = "generated app 1", appId = "app-1") {
153
- mockParseMarimoIslandApps.mockReturnValue([app(appId)]);
154
- mockCreateMarimoFile.mockReturnValue(file);
155
- }
156
-
157
- function mockSingleApp(file = "generated app 1", appId = "app-1") {
158
- setSingleApp(file, appId);
159
- signalWorkerReady();
160
- }
161
-
162
- function sendSlider() {
163
- return bridge.sendComponentValues({
164
- objectIds: [uiElementId("slider-1")],
165
- values: [2],
166
- });
167
- }
168
-
169
- async function initializeSingleApp() {
170
- mockSingleApp();
171
- await bridge.initializeApps();
172
- }
173
-
174
- describe("app initialization", () => {
121
+ describe("startSessionsForAllApps", () => {
175
122
  it("should prefer trusted export notebook code when there is exactly one reactive app", async () => {
176
- mockParseMarimoIslandApps.mockReturnValue([app()]);
123
+ mockParseMarimoIslandApps.mockReturnValue([
124
+ {
125
+ id: "app-1",
126
+ cells: [{ code: "x = 1", idx: 0, output: "<div>1</div>" }],
127
+ },
128
+ ]);
177
129
  mockGetMarimoExportContext.mockReturnValue({
178
130
  trusted: true,
179
131
  notebookCode:
180
132
  "import marimo\napp = marimo.App()\n@app.cell\ndef __():\n x = 1\n return",
181
133
  });
182
134
 
183
- signalWorkerReady();
184
- await bridge.initializeApps();
135
+ await (
136
+ bridge as unknown as { startSessionsForAllApps(): Promise<void> }
137
+ ).startSessionsForAllApps();
185
138
 
186
139
  expect(mockCreateMarimoFile).not.toHaveBeenCalled();
187
140
  expect(mockStartSessionRequest).toHaveBeenCalledWith({
188
141
  appId: "app-1",
189
142
  code: "import marimo\napp = marimo.App()\n@app.cell\ndef __():\n x = 1\n return",
190
- sessionGeneration: 1,
191
143
  });
192
144
  });
193
145
 
194
146
  it("should ignore trusted export notebook code for a payload-backed app", async () => {
195
- const payloadApp = { ...app(), payloadBacked: true };
147
+ const payloadApp = {
148
+ id: "app-1",
149
+ payloadBacked: true,
150
+ cells: [{ code: "x = 1", idx: 0, output: "<div>1</div>" }],
151
+ };
196
152
  mockParseMarimoIslandApps.mockReturnValue([payloadApp]);
197
153
  mockGetMarimoExportContext.mockReturnValue({
198
154
  trusted: true,
@@ -200,19 +156,28 @@ describe("IslandsPyodideBridge", () => {
200
156
  });
201
157
  mockCreateMarimoFile.mockReturnValue("generated payload app");
202
158
 
203
- signalWorkerReady();
204
- await bridge.initializeApps();
159
+ await (
160
+ bridge as unknown as { startSessionsForAllApps(): Promise<void> }
161
+ ).startSessionsForAllApps();
205
162
 
206
163
  expect(mockCreateMarimoFile).toHaveBeenCalledWith(payloadApp);
207
164
  expect(mockStartSessionRequest).toHaveBeenCalledWith({
208
165
  appId: "app-1",
209
166
  code: "generated payload app",
210
- sessionGeneration: 1,
211
167
  });
212
168
  });
213
169
 
214
170
  it("should keep synthesized per-app files for multiple reactive apps even when export context exists", async () => {
215
- mockParseMarimoIslandApps.mockReturnValue([app(), app("app-2")]);
171
+ mockParseMarimoIslandApps.mockReturnValue([
172
+ {
173
+ id: "app-1",
174
+ cells: [{ code: "x = 1", idx: 0, output: "<div>1</div>" }],
175
+ },
176
+ {
177
+ id: "app-2",
178
+ cells: [{ code: "y = 2", idx: 0, output: "<div>2</div>" }],
179
+ },
180
+ ]);
216
181
  mockGetMarimoExportContext.mockReturnValue({
217
182
  trusted: true,
218
183
  notebookCode: "full notebook should be ignored",
@@ -221,281 +186,43 @@ describe("IslandsPyodideBridge", () => {
221
186
  .mockReturnValueOnce("generated app 1")
222
187
  .mockReturnValueOnce("generated app 2");
223
188
 
224
- signalWorkerReady();
225
- await bridge.initializeApps();
189
+ await (
190
+ bridge as unknown as { startSessionsForAllApps(): Promise<void> }
191
+ ).startSessionsForAllApps();
226
192
 
227
193
  expect(mockCreateMarimoFile).toHaveBeenCalledTimes(2);
228
194
  expect(mockStartSessionRequest).toHaveBeenNthCalledWith(1, {
229
195
  appId: "app-1",
230
196
  code: "generated app 1",
231
- sessionGeneration: 1,
232
197
  });
233
198
  expect(mockStartSessionRequest).toHaveBeenNthCalledWith(2, {
234
199
  appId: "app-2",
235
200
  code: "generated app 2",
236
- sessionGeneration: 2,
237
- });
238
-
239
- await sendSlider();
240
- await bridge.stopSession();
241
-
242
- expect(mockReplaceSessionRequest).not.toHaveBeenCalled();
243
- expect(mockStopSessionRequest).not.toHaveBeenCalled();
244
- expect(mockBridge).toHaveBeenCalledWith({
245
- appId: "app-1",
246
- functionName: "put_control_request",
247
- payload: {
248
- objectIds: ["slider-1"],
249
- token: "test-uuid-12345",
250
- type: "update-ui-element",
251
- values: [2],
252
- },
253
- sessionGeneration: 1,
254
201
  });
255
202
  });
256
203
 
257
204
  it("should synthesize a file for a single app when no trusted export context is present", async () => {
258
- setSingleApp();
205
+ mockParseMarimoIslandApps.mockReturnValue([
206
+ {
207
+ id: "app-1",
208
+ cells: [{ code: "x = 1", idx: 0, output: "<div>1</div>" }],
209
+ },
210
+ ]);
211
+ mockCreateMarimoFile.mockReturnValue("generated app 1");
259
212
 
260
- signalWorkerReady();
261
- await bridge.initializeApps();
213
+ await (
214
+ bridge as unknown as { startSessionsForAllApps(): Promise<void> }
215
+ ).startSessionsForAllApps();
262
216
 
263
217
  expect(mockCreateMarimoFile).toHaveBeenCalledTimes(1);
264
218
  expect(mockStartSessionRequest).toHaveBeenCalledWith({
265
219
  appId: "app-1",
266
220
  code: "generated app 1",
267
- sessionGeneration: 1,
268
- });
269
- });
270
- });
271
-
272
- describe("app lifecycle", () => {
273
- it("holds controls until the first app session is ready", async () => {
274
- setSingleApp();
275
-
276
- const initialization = bridge.initializeApps();
277
- const control = sendSlider();
278
- await Promise.resolve();
279
-
280
- expect(mockBridge).not.toHaveBeenCalled();
281
-
282
- signalWorkerReady();
283
- await Promise.all([initialization, control]);
284
- expect(mockBridge).toHaveBeenCalledWith(
285
- expect.objectContaining({
286
- appId: "app-1",
287
- sessionGeneration: 1,
288
- }),
289
- );
290
- });
291
-
292
- it("starts the first app, skips duplicates, and replaces changed source", async () => {
293
- mockSingleApp();
294
-
295
- await bridge.initializeApps();
296
- await bridge.initializeApps();
297
- await sendSlider();
298
-
299
- expect(mockStartSessionRequest).toHaveBeenCalledOnce();
300
- expect(mockStartSessionRequest).toHaveBeenCalledWith({
301
- appId: "app-1",
302
- code: "generated app 1",
303
- sessionGeneration: 1,
304
- });
305
- expect(mockReplaceSessionRequest).not.toHaveBeenCalled();
306
- expect(mockBridge).toHaveBeenCalledWith(
307
- expect.objectContaining({
308
- appId: "app-1",
309
- sessionGeneration: 1,
310
- }),
311
- );
312
- mockCreateMarimoFile.mockReturnValue("generated app 2");
313
- await bridge.initializeApps();
314
-
315
- expect(mockReplaceSessionRequest).toHaveBeenCalledOnce();
316
- expect(mockReplaceSessionRequest).toHaveBeenCalledWith({
317
- appId: "app-1",
318
- code: "generated app 2",
319
- sessionGeneration: 2,
320
- });
321
- expect(mockStoreSet).toHaveBeenCalledOnce();
322
- });
323
-
324
- it("stops the matching active app", async () => {
325
- mockSingleApp();
326
- await bridge.initializeApps();
327
-
328
- await bridge.stopSession("other-app");
329
- await bridge.stopSession("app-1");
330
-
331
- expect(mockStopSessionRequest).toHaveBeenCalledOnce();
332
- expect(mockStopSessionRequest).toHaveBeenCalledWith({
333
- appId: "app-1",
334
- sessionGeneration: 1,
335
- });
336
-
337
- mockBridge.mockClear();
338
- const control = sendSlider();
339
- await Promise.resolve();
340
- expect(mockBridge).not.toHaveBeenCalled();
341
-
342
- mockSingleApp("generated app 2");
343
- await bridge.initializeApps();
344
- await control;
345
-
346
- expect(mockBridge).toHaveBeenCalledWith(
347
- expect.objectContaining({ sessionGeneration: 2 }),
348
- );
349
- });
350
-
351
- it("replaces the active session after stop fails", async () => {
352
- mockSingleApp();
353
- await bridge.initializeApps();
354
- mockStopSessionRequest.mockRejectedValueOnce(new Error("stop failed"));
355
-
356
- await expect(bridge.stopSession("app-1")).rejects.toThrow("stop failed");
357
-
358
- mockSingleApp("generated app 2", "app-2");
359
-
360
- await bridge.initializeApps();
361
-
362
- expect(mockStartSessionRequest).toHaveBeenCalledOnce();
363
- expect(mockReplaceSessionRequest).toHaveBeenCalledWith({
364
- appId: "app-2",
365
- code: "generated app 2",
366
- sessionGeneration: 2,
367
- });
368
- });
369
-
370
- it("keeps controls sent during stop scoped to the stopped app", async () => {
371
- mockSingleApp();
372
- await bridge.initializeApps();
373
- mockBridge.mockClear();
374
- let finishStop!: () => void;
375
- mockStopSessionRequest.mockReturnValueOnce(
376
- new Promise<void>((resolve) => {
377
- finishStop = resolve;
378
- }),
379
- );
380
-
381
- const stop = bridge.stopSession("app-1");
382
- await vi.waitFor(() =>
383
- expect(mockStopSessionRequest).toHaveBeenCalledOnce(),
384
- );
385
- const control = sendSlider();
386
- mockSingleApp("generated app 2", "app-2");
387
- const nextInitialization = bridge.initializeApps();
388
-
389
- finishStop();
390
- await Promise.all([stop, control, nextInitialization]);
391
-
392
- expect(mockBridge).toHaveBeenCalledWith(
393
- expect.objectContaining({
394
- appId: "app-1",
395
- sessionGeneration: 1,
396
- }),
397
- );
398
- });
399
-
400
- it("allows initialization to retry after replacement fails", async () => {
401
- mockSingleApp();
402
- await bridge.initializeApps();
403
- mockCreateMarimoFile.mockReturnValue("generated app 2");
404
- mockReplaceSessionRequest.mockRejectedValueOnce(
405
- new Error("replacement failed"),
406
- );
407
-
408
- await expect(bridge.initializeApps()).rejects.toThrow(
409
- "replacement failed",
410
- );
411
- await bridge.initializeApps();
412
-
413
- expect(mockStartSessionRequest).toHaveBeenCalledOnce();
414
- expect(mockReplaceSessionRequest).toHaveBeenCalledTimes(2);
415
- expect(mockReplaceSessionRequest).toHaveBeenLastCalledWith({
416
- appId: "app-1",
417
- code: "generated app 2",
418
- sessionGeneration: 3,
419
221
  });
420
222
  });
421
-
422
- it("scopes controls to the replacement active when they were sent", async () => {
423
- mockSingleApp();
424
- await bridge.initializeApps();
425
- mockBridge.mockClear();
426
- mockCreateMarimoFile.mockReturnValue("generated app 2");
427
- let finishSecondApp!: () => void;
428
- let finishThirdApp!: () => void;
429
- mockReplaceSessionRequest
430
- .mockReturnValueOnce(
431
- new Promise<void>((resolve) => {
432
- finishSecondApp = resolve;
433
- }),
434
- )
435
- .mockReturnValueOnce(
436
- new Promise<void>((resolve) => {
437
- finishThirdApp = resolve;
438
- }),
439
- );
440
-
441
- const secondInitialization = bridge.initializeApps();
442
- await vi.waitFor(() =>
443
- expect(mockReplaceSessionRequest).toHaveBeenCalledOnce(),
444
- );
445
- const control = sendSlider();
446
- mockSingleApp("generated app 3", "app-2");
447
- const thirdInitialization = bridge.initializeApps();
448
-
449
- expect(mockBridge).not.toHaveBeenCalled();
450
-
451
- finishSecondApp();
452
- await vi.waitFor(() => expect(mockBridge).toHaveBeenCalledOnce());
453
- expect(mockBridge).toHaveBeenCalledWith(
454
- expect.objectContaining({
455
- appId: "app-1",
456
- sessionGeneration: 2,
457
- }),
458
- );
459
-
460
- finishThirdApp();
461
- await Promise.all([secondInitialization, thirdInitialization, control]);
462
- });
463
-
464
- it("keeps controls from a failed replacement out of the next app", async () => {
465
- mockSingleApp();
466
- await bridge.initializeApps();
467
- mockBridge.mockClear();
468
- mockCreateMarimoFile.mockReturnValue("generated app 2");
469
- let failReplacement!: (error: Error) => void;
470
- mockReplaceSessionRequest.mockReturnValueOnce(
471
- new Promise<void>((_resolve, reject) => {
472
- failReplacement = reject;
473
- }),
474
- );
475
-
476
- const failedInitialization = bridge.initializeApps();
477
- await vi.waitFor(() =>
478
- expect(mockReplaceSessionRequest).toHaveBeenCalledOnce(),
479
- );
480
- const control = sendSlider();
481
- failReplacement(new Error("replacement failed"));
482
- await expect(failedInitialization).rejects.toThrow("replacement failed");
483
-
484
- mockSingleApp("generated app 3", "app-2");
485
- await Promise.all([bridge.initializeApps(), control]);
486
-
487
- expect(mockBridge).toHaveBeenCalledWith(
488
- expect.objectContaining({
489
- appId: "app-1",
490
- sessionGeneration: 2,
491
- }),
492
- );
493
- });
494
223
  });
495
224
 
496
225
  describe("sendComponentValues", () => {
497
- beforeEach(initializeSingleApp);
498
-
499
226
  it("should include type field and token in control request", async () => {
500
227
  const request = {
501
228
  objectIds: [uiElementId("Hbol-0")],
@@ -505,7 +232,6 @@ describe("IslandsPyodideBridge", () => {
505
232
  await bridge.sendComponentValues(request);
506
233
 
507
234
  expect(mockBridge).toHaveBeenCalledWith({
508
- appId: "app-1",
509
235
  functionName: "put_control_request",
510
236
  payload: {
511
237
  type: "update-ui-element",
@@ -513,7 +239,6 @@ describe("IslandsPyodideBridge", () => {
513
239
  values: [58],
514
240
  token: "test-uuid-12345",
515
241
  },
516
- sessionGeneration: 1,
517
242
  });
518
243
  });
519
244
 
@@ -526,21 +251,17 @@ describe("IslandsPyodideBridge", () => {
526
251
  await bridge.sendComponentValues(request);
527
252
 
528
253
  expect(mockBridge).toHaveBeenCalledWith({
529
- appId: "app-1",
530
254
  functionName: "put_control_request",
531
255
  payload: expect.objectContaining({
532
256
  type: "update-ui-element",
533
257
  objectIds: ["slider-1", "slider-2"],
534
258
  values: [10, 20],
535
259
  }),
536
- sessionGeneration: 1,
537
260
  });
538
261
  });
539
262
  });
540
263
 
541
264
  describe("sendFunctionRequest", () => {
542
- beforeEach(initializeSingleApp);
543
-
544
265
  it("should include type field in control request", async () => {
545
266
  const request = {
546
267
  functionCallId: requestId("call-123"),
@@ -552,7 +273,6 @@ describe("IslandsPyodideBridge", () => {
552
273
  await bridge.sendFunctionRequest(request);
553
274
 
554
275
  expect(mockBridge).toHaveBeenCalledWith({
555
- appId: "app-1",
556
276
  functionName: "put_control_request",
557
277
  payload: {
558
278
  type: "invoke-function",
@@ -561,14 +281,11 @@ describe("IslandsPyodideBridge", () => {
561
281
  functionName: "my_function",
562
282
  args: { x: 1, y: 2 },
563
283
  },
564
- sessionGeneration: 1,
565
284
  });
566
285
  });
567
286
  });
568
287
 
569
288
  describe("sendRun", () => {
570
- beforeEach(initializeSingleApp);
571
-
572
289
  it("should include type field in control request", async () => {
573
290
  const request = {
574
291
  cellIds: [cellId("cell-1"), cellId("cell-2")],
@@ -578,14 +295,12 @@ describe("IslandsPyodideBridge", () => {
578
295
  await bridge.sendRun(request);
579
296
 
580
297
  expect(mockBridge).toHaveBeenCalledWith({
581
- appId: "app-1",
582
298
  functionName: "put_control_request",
583
299
  payload: {
584
300
  type: "execute-cells",
585
301
  cellIds: ["cell-1", "cell-2"],
586
302
  codes: ["print('hello')", "print('world')"],
587
303
  },
588
- sessionGeneration: 1,
589
304
  });
590
305
  });
591
306
 
@@ -598,11 +313,7 @@ describe("IslandsPyodideBridge", () => {
598
313
  await bridge.sendRun(request);
599
314
 
600
315
  // Verify loadPackages was called with joined codes
601
- expect(mockLoadPackages).toHaveBeenCalledWith({
602
- appId: "app-1",
603
- code: "import pandas",
604
- sessionGeneration: 1,
605
- });
316
+ expect(mockLoadPackages).toHaveBeenCalledWith("import pandas");
606
317
 
607
318
  // Verify order: loadPackages should be called before bridge
608
319
  const loadPackagesCallOrder =
@@ -613,8 +324,6 @@ describe("IslandsPyodideBridge", () => {
613
324
  });
614
325
 
615
326
  describe("sendModelValue", () => {
616
- beforeEach(initializeSingleApp);
617
-
618
327
  it("should include type field in control request", async () => {
619
328
  const request = {
620
329
  modelId: widgetModelId("widget-1"),
@@ -629,7 +338,6 @@ describe("IslandsPyodideBridge", () => {
629
338
  await bridge.sendModelValue(request);
630
339
 
631
340
  expect(mockBridge).toHaveBeenCalledWith({
632
- appId: "app-1",
633
341
  functionName: "put_control_request",
634
342
  payload: {
635
343
  type: "model",
@@ -641,14 +349,11 @@ describe("IslandsPyodideBridge", () => {
641
349
  },
642
350
  buffers: [],
643
351
  },
644
- sessionGeneration: 1,
645
352
  });
646
353
  });
647
354
  });
648
355
 
649
356
  describe("control request message format", () => {
650
- beforeEach(initializeSingleApp);
651
-
652
357
  it("should always include the type field required by msgspec", async () => {
653
358
  // Test all methods to ensure they include the type field
654
359
  await bridge.sendComponentValues({ objectIds: [], values: [] });