@marimo-team/frontend 0.25.3-dev23 → 0.25.3-dev24
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/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import { describe, expect, it } from "vitest";
|
|
4
4
|
import type { NotebookEvent } from "../events";
|
|
5
5
|
import type { Resource } from "../resources/resource";
|
|
6
|
-
import { createStream
|
|
6
|
+
import { createStream } from "../stream";
|
|
7
7
|
import { fixtureEvents } from "./fixtures";
|
|
8
8
|
|
|
9
9
|
interface CellIds {
|
|
@@ -54,17 +54,15 @@ const revisions: Resource<Revisions> = {
|
|
|
54
54
|
},
|
|
55
55
|
};
|
|
56
56
|
|
|
57
|
-
|
|
58
|
-
cells
|
|
59
|
-
revisions: Revisions;
|
|
57
|
+
function createTestStream() {
|
|
58
|
+
return createStream({ cells, revisions });
|
|
60
59
|
}
|
|
61
|
-
type State = StreamState<Resources>;
|
|
62
60
|
|
|
63
61
|
function replay(
|
|
64
|
-
stream:
|
|
62
|
+
stream: ReturnType<typeof createTestStream>,
|
|
65
63
|
events: readonly NotebookEvent[],
|
|
66
|
-
from
|
|
67
|
-
)
|
|
64
|
+
from = stream.initial,
|
|
65
|
+
) {
|
|
68
66
|
return events.reduce((state, event) => stream.reduce(state, event), from);
|
|
69
67
|
}
|
|
70
68
|
|
|
@@ -77,7 +75,7 @@ if (cellEvent?.event !== "cell") {
|
|
|
77
75
|
|
|
78
76
|
describe("notebook stream", () => {
|
|
79
77
|
it("reads the snapshot, goes live at ready, and rebuilds after reset", () => {
|
|
80
|
-
const stream =
|
|
78
|
+
const stream = createTestStream();
|
|
81
79
|
const partial = replay(stream, snapshot.slice(0, ready));
|
|
82
80
|
expect(partial).toMatchObject({
|
|
83
81
|
phase: "snapshot",
|
|
@@ -105,7 +103,7 @@ describe("notebook stream", () => {
|
|
|
105
103
|
});
|
|
106
104
|
|
|
107
105
|
it("keeps resource state by reference when an event does not apply", () => {
|
|
108
|
-
const stream =
|
|
106
|
+
const stream = createTestStream();
|
|
109
107
|
const state = replay(stream, snapshot);
|
|
110
108
|
const runtime = snapshot.find((event) => event.event === "runtime");
|
|
111
109
|
const unhandled = stream.reduce(state, { ...runtime!, id: "100" });
|
|
@@ -121,7 +119,7 @@ describe("notebook stream", () => {
|
|
|
121
119
|
});
|
|
122
120
|
|
|
123
121
|
it("clears resources when its notebook is removed and ignores other notebooks", () => {
|
|
124
|
-
const stream =
|
|
122
|
+
const stream = createTestStream();
|
|
125
123
|
const state = replay(stream, snapshot);
|
|
126
124
|
const time = "2026-10-01T09:30:00Z";
|
|
127
125
|
expect(
|
|
@@ -141,7 +139,7 @@ describe("notebook stream", () => {
|
|
|
141
139
|
});
|
|
142
140
|
|
|
143
141
|
it("rejects objects from another notebook", () => {
|
|
144
|
-
const stream =
|
|
142
|
+
const stream = createTestStream();
|
|
145
143
|
const state = replay(stream, snapshot.slice(0, ready));
|
|
146
144
|
const notebook = snapshot[0];
|
|
147
145
|
if (notebook.event !== "notebook") {
|
|
@@ -162,7 +160,7 @@ describe("notebook stream", () => {
|
|
|
162
160
|
});
|
|
163
161
|
|
|
164
162
|
it("records each live event's cursor and resumes in place", () => {
|
|
165
|
-
const stream =
|
|
163
|
+
const stream = createTestStream();
|
|
166
164
|
const partial = replay(stream, snapshot.slice(0, ready));
|
|
167
165
|
expect(stream.resume(partial)).toEqual({
|
|
168
166
|
...stream.initial,
|
|
@@ -195,7 +193,7 @@ describe("notebook stream", () => {
|
|
|
195
193
|
});
|
|
196
194
|
|
|
197
195
|
it("requires a cursor on live events other than reset and notifications", () => {
|
|
198
|
-
const stream =
|
|
196
|
+
const stream = createTestStream();
|
|
199
197
|
const state = replay(stream, snapshot);
|
|
200
198
|
expect(() => stream.reduce(state, cellEvent)).toThrow("cursor");
|
|
201
199
|
expect(
|
|
@@ -27,7 +27,7 @@ interface PendingDocument {
|
|
|
27
27
|
* A snapshot's records are pending until `ready`. A live revision is pending
|
|
28
28
|
* from its first cell until its `document` event.
|
|
29
29
|
*/
|
|
30
|
-
|
|
30
|
+
interface DocumentState {
|
|
31
31
|
readonly current: Document | null;
|
|
32
32
|
readonly pending: PendingDocument | null;
|
|
33
33
|
}
|
|
@@ -2,36 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
import type * as Protocol from "../protocol";
|
|
4
4
|
|
|
5
|
-
/** An event name from the protocol. */
|
|
6
|
-
export type EventName = Protocol.EventOutput["event"];
|
|
7
|
-
|
|
8
|
-
/** The decoded event with the given name. */
|
|
9
|
-
export type EventOf<Name extends EventName> = Extract<
|
|
10
|
-
Protocol.EventOutput,
|
|
11
|
-
{ event: Name }
|
|
12
|
-
>;
|
|
13
|
-
|
|
14
5
|
/**
|
|
15
6
|
* Events the stream handles itself.
|
|
16
7
|
*
|
|
17
8
|
* They identify the notebook, discard state, or carry a native
|
|
18
9
|
* notification. Resources do not receive them.
|
|
19
10
|
*/
|
|
20
|
-
|
|
21
|
-
| "reset"
|
|
22
|
-
| "notebook"
|
|
23
|
-
| "notebook.removed"
|
|
24
|
-
| "marimo";
|
|
11
|
+
type StreamEventName = "reset" | "notebook" | "notebook.removed" | "marimo";
|
|
25
12
|
|
|
26
13
|
/** Events a resource may handle. `ready` marks the end of the snapshot. */
|
|
27
|
-
export type
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
14
|
+
export type ResourceEvent = Exclude<
|
|
15
|
+
Protocol.EventOutput,
|
|
16
|
+
{ event: StreamEventName }
|
|
17
|
+
>;
|
|
31
18
|
|
|
32
19
|
/** What a handler may know about the stream beyond the event. */
|
|
33
20
|
export interface HandlerContext {
|
|
34
|
-
|
|
21
|
+
/** `snapshot` until `ready` arrives and `live` after it. */
|
|
22
|
+
readonly phase: "snapshot" | "live";
|
|
35
23
|
}
|
|
36
24
|
|
|
37
25
|
/**
|
|
@@ -39,10 +27,10 @@ export interface HandlerContext {
|
|
|
39
27
|
*
|
|
40
28
|
* A handler returns `state` when the event does not apply to it.
|
|
41
29
|
*/
|
|
42
|
-
|
|
43
|
-
readonly [Name in
|
|
30
|
+
type EventHandlers<State> = {
|
|
31
|
+
readonly [Name in ResourceEvent["event"]]?: (
|
|
44
32
|
state: State,
|
|
45
|
-
event:
|
|
33
|
+
event: Extract<ResourceEvent, { event: Name }>,
|
|
46
34
|
context: HandlerContext,
|
|
47
35
|
) => State;
|
|
48
36
|
};
|
package/src/core/host/stream.ts
CHANGED
|
@@ -3,15 +3,13 @@
|
|
|
3
3
|
import type { NotebookEvent } from "./events";
|
|
4
4
|
import type * as Protocol from "./protocol";
|
|
5
5
|
import type {
|
|
6
|
-
EventOf,
|
|
7
6
|
HandlerContext,
|
|
8
7
|
Resource,
|
|
9
|
-
|
|
10
|
-
StreamPhase,
|
|
8
|
+
ResourceEvent,
|
|
11
9
|
} from "./resources/resource";
|
|
12
10
|
|
|
13
11
|
/** A resource for each entry of a state map under the same name. */
|
|
14
|
-
|
|
12
|
+
type Resources<States> = {
|
|
15
13
|
readonly [Name in keyof States]: Resource<States[Name]>;
|
|
16
14
|
};
|
|
17
15
|
|
|
@@ -21,8 +19,8 @@ export type Resources<States> = {
|
|
|
21
19
|
* Holds the phase, the notebook identity, the resume cursor, and the state
|
|
22
20
|
* of each resource.
|
|
23
21
|
*/
|
|
24
|
-
|
|
25
|
-
readonly phase:
|
|
22
|
+
interface StreamState<States> {
|
|
23
|
+
readonly phase: HandlerContext["phase"];
|
|
26
24
|
/**
|
|
27
25
|
* The notebook the stream describes.
|
|
28
26
|
*
|
|
@@ -40,7 +38,7 @@ export interface StreamState<States> {
|
|
|
40
38
|
}
|
|
41
39
|
|
|
42
40
|
/** A reducer over the events of one notebook stream. */
|
|
43
|
-
|
|
41
|
+
interface Stream<States> {
|
|
44
42
|
readonly initial: StreamState<States>;
|
|
45
43
|
/**
|
|
46
44
|
* Apply one event.
|
|
@@ -71,7 +69,7 @@ const UNCURSORED = new Set<NotebookEvent["event"]>([
|
|
|
71
69
|
* Undefined for objects that do not name one.
|
|
72
70
|
*/
|
|
73
71
|
function notebookIdOf(
|
|
74
|
-
event:
|
|
72
|
+
event: ResourceEvent,
|
|
75
73
|
): Protocol.NotebookIdOutput | undefined {
|
|
76
74
|
switch (event.event) {
|
|
77
75
|
case "document":
|
|
@@ -127,7 +125,7 @@ export function createStream<States extends object>(
|
|
|
127
125
|
|
|
128
126
|
function fanOut(
|
|
129
127
|
state: StreamState<States>,
|
|
130
|
-
event:
|
|
128
|
+
event: ResourceEvent,
|
|
131
129
|
): StreamState<States> {
|
|
132
130
|
const context: HandlerContext = { phase: state.phase };
|
|
133
131
|
let next = state.resources;
|