foldkit 0.75.1 → 0.76.1
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/README.md +2 -1
- package/dist/{devtools → devTools}/overlay-styles.d.ts.map +1 -1
- package/dist/{devtools → devTools}/overlay.d.ts +3 -3
- package/dist/{devtools → devTools}/overlay.d.ts.map +1 -1
- package/dist/{devtools → devTools}/overlay.js +4 -18
- package/dist/devTools/protocol.d.ts +288 -0
- package/dist/devTools/protocol.d.ts.map +1 -0
- package/dist/devTools/protocol.js +121 -0
- package/dist/devTools/public.d.ts +2 -0
- package/dist/devTools/public.d.ts.map +1 -0
- package/dist/devTools/public.js +1 -0
- package/dist/devTools/serialize.d.ts +19 -0
- package/dist/devTools/serialize.d.ts.map +1 -0
- package/dist/devTools/serialize.js +34 -0
- package/dist/{devtools → devTools}/store.d.ts +2 -2
- package/dist/{devtools → devTools}/store.d.ts.map +1 -1
- package/dist/{devtools → devTools}/store.js +1 -1
- package/dist/devTools/webSocketBridge.d.ts +24 -0
- package/dist/devTools/webSocketBridge.d.ts.map +1 -0
- package/dist/devTools/webSocketBridge.js +163 -0
- package/dist/runtime/public.d.ts +1 -1
- package/dist/runtime/runtime.d.ts +21 -10
- package/dist/runtime/runtime.d.ts.map +1 -1
- package/dist/runtime/runtime.js +30 -21
- package/package.json +5 -1
- /package/dist/{devtools → devTools}/overlay-styles.d.ts +0 -0
- /package/dist/{devtools → devTools}/overlay-styles.js +0 -0
package/README.md
CHANGED
|
@@ -149,7 +149,8 @@ Foldkit is a complete system, not a collection of libraries you stitch together.
|
|
|
149
149
|
- **UI Components**: Accessible, keyboard-friendly primitives covering Button, Checkbox, Combobox, Dialog, Disclosure, DragAndDrop, Fieldset, Input, Listbox, Menu, Popover, RadioGroup, Select, Switch, Tabs, Textarea, and Transition. Every component is a Submodel with a typed `ViewConfig`, domain-event callbacks like `onSelected`, `onClosed`, and `onToggled`, and `className` plus `attributes` props on every slot for styling and extension. Animated components share a `Transition` Submodel that coordinates CSS enter and leave animations.
|
|
150
150
|
- **Field Validation**: Per-field validation state modeled as a discriminated union. Define rules as data, apply them in update, and the Model tracks the result.
|
|
151
151
|
- **Virtual DOM**: Declarative views powered by [Snabbdom](https://github.com/snabbdom/snabbdom), with lazy memoization and fast, keyed diffing. Views are plain functions of your Model.
|
|
152
|
-
- **DevTools**: Built-in overlay for inspecting Messages, Model state, and Commands. Time-travel mode
|
|
152
|
+
- **DevTools**: Built-in overlay for inspecting Messages, Model state, and Commands. Time-travel mode rewinds your UI to any past Model, Inspect mode browses snapshots without pausing, and Submodel drill-in filtering scopes the Message list to any nested module.
|
|
153
|
+
- **DevTools MCP**: Expose a running Foldkit app to AI agents over the Model Context Protocol. Agents read the current Model, list and inspect Message history, rewind the UI to any past Model, and dispatch Messages into the runtime. The runtime's own Message Schema is published as JSON Schema so the agent discovers exactly what it can dispatch, and every payload is validated against the Schema before it reaches your update function. One command sets it up: `npx @foldkit/devtools-mcp init`.
|
|
153
154
|
- **Crash View and Reporting**: Configure `crash.view` to render a custom fallback UI when the update loop throws. A `crash.report` callback fires first with the error, Model, and triggering Message, so you can ship it straight to Sentry or your logger.
|
|
154
155
|
- **Story Testing**: Exercise the update function directly. Send Messages, resolve Commands inline with `resolve` and `resolveAll`, and assert with focused helpers: `Story.model`, `Story.expectHasCommands`, `Story.expectExactCommands`, `Story.expectNoCommands`, and `Story.expectOutMessage`. No mocking libraries, no fake timers.
|
|
155
156
|
- **Scene Testing**: Drive your app the way a user does. Scene renders your real view, then clicks buttons, types into inputs, presses keys, and asserts on what's on screen. Accessible locators (`role`, `label`, `placeholder`, `altText`, `title`, `testId`, `displayValue`) with full options (`name`, `level`, `checked`, `selected`, `pressed`, `expanded`, `disabled`), multi-match `Scene.all` with `Scene.filter` and `Scene.nth`, scoped steps via `Scene.inside`, pointer events, event bubbling, and Vitest matchers like `toHaveText`, `toBeVisible`, `toHaveAccessibleName`, and `toHaveCount`. API parity with React Testing Library and Playwright, without a browser.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"overlay-styles.d.ts","sourceRoot":"","sources":["../../src/
|
|
1
|
+
{"version":3,"file":"overlay-styles.d.ts","sourceRoot":"","sources":["../../src/devTools/overlay-styles.ts"],"names":[],"mappings":"AAAA,QAAA,MAAM,aAAa,q3UAimBlB,CAAA;AAED,OAAO,EAAE,aAAa,EAAE,CAAA"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Effect, HashSet, Option } from 'effect';
|
|
2
2
|
import * as Command from '../command/index.js';
|
|
3
|
-
import type {
|
|
4
|
-
import { type
|
|
3
|
+
import type { DevToolsMode, DevToolsPosition } from '../runtime/runtime.js';
|
|
4
|
+
import { type DevToolsStore } from './store.js';
|
|
5
5
|
export declare const JumpTo: Command.CommandDefinition<"JumpTo", {
|
|
6
6
|
readonly _tag: "CompletedJump";
|
|
7
7
|
}>;
|
|
@@ -34,5 +34,5 @@ export declare const UnlockScroll: Command.CommandDefinition<"UnlockScroll", {
|
|
|
34
34
|
export declare const ScrollToTop: Command.CommandDefinition<"ScrollToTop", {
|
|
35
35
|
readonly _tag: "ScrolledToTop";
|
|
36
36
|
}>;
|
|
37
|
-
export declare const createOverlay: (store:
|
|
37
|
+
export declare const createOverlay: (store: DevToolsStore, position: DevToolsPosition, mode: DevToolsMode, maybeBanner: Option.Option<string>) => Effect.Effect<void, never, never>;
|
|
38
38
|
//# sourceMappingURL=overlay.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"overlay.d.ts","sourceRoot":"","sources":["../../src/
|
|
1
|
+
{"version":3,"file":"overlay.d.ts","sourceRoot":"","sources":["../../src/devTools/overlay.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,MAAM,EAGN,OAAO,EAGP,MAAM,EASP,MAAM,QAAQ,CAAA;AAEf,OAAO,KAAK,OAAO,MAAM,qBAAqB,CAAA;AAK9C,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAQ3E,OAAO,EACL,KAAK,aAAa,EAInB,MAAM,YAAY,CAAA;AAuOnB,eAAO,MAAM,MAAM;;EAA0C,CAAA;AAC7D,eAAO,MAAM,YAAY;;;;;;EAGxB,CAAA;AACD,eAAO,MAAM,aAAa;;;;;;EAGzB,CAAA;AACD,eAAO,MAAM,MAAM;;EAA4C,CAAA;AAC/D,eAAO,MAAM,KAAK;;EAA0C,CAAA;AAC5D,eAAO,MAAM,UAAU;;EAA6C,CAAA;AACpE,eAAO,MAAM,YAAY;;EAAiD,CAAA;AAC1E,eAAO,MAAM,WAAW;;EAA+C,CAAA;AAgzCvE,eAAO,MAAM,aAAa,GACxB,OAAO,aAAa,EACpB,UAAU,gBAAgB,EAC1B,MAAM,YAAY,EAClB,aAAa,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,sCAkDhC,CAAA"}
|
|
@@ -11,6 +11,7 @@ import { lockScroll, unlockScroll } from '../task/scrollLock.js';
|
|
|
11
11
|
import * as Listbox from '../ui/listbox/public.js';
|
|
12
12
|
import * as Tabs from '../ui/tabs/public.js';
|
|
13
13
|
import { overlayStyles } from './overlay-styles.js';
|
|
14
|
+
import { toInspectableValue } from './serialize.js';
|
|
14
15
|
import { INIT_INDEX, } from './store.js';
|
|
15
16
|
// MODEL
|
|
16
17
|
const DisplayEntry = S.Struct({
|
|
@@ -143,21 +144,6 @@ const toDisplayState = (state) => ({
|
|
|
143
144
|
pausedAtIndex: state.pausedAtIndex,
|
|
144
145
|
});
|
|
145
146
|
const isExpandable = (value) => Predicate.isObject(value);
|
|
146
|
-
/** Convert DOM-class instances (File, Blob, Date, URL) to plain-object
|
|
147
|
-
* representations so the tree renderer's key-enumeration walk can see their
|
|
148
|
-
* meaningful data, which otherwise lives on the prototype as getters and
|
|
149
|
-
* is invisible to `Object.keys`. Recurses through arrays and records so
|
|
150
|
-
* the transform applies at every level. File is matched before Blob
|
|
151
|
-
* because File extends Blob. */
|
|
152
|
-
const toInspectableValue = (value) => M.value(value).pipe(M.when(M.instanceOf(File), file => ({
|
|
153
|
-
name: file.name,
|
|
154
|
-
size: file.size,
|
|
155
|
-
type: file.type,
|
|
156
|
-
lastModified: file.lastModified,
|
|
157
|
-
})), M.when(M.instanceOf(Blob), blob => ({
|
|
158
|
-
size: blob.size,
|
|
159
|
-
type: blob.type,
|
|
160
|
-
})), M.when(M.instanceOf(Date), date => date.toISOString()), M.when(M.instanceOf(URL), ({ href }) => href), M.when(Array.isArray, Array_.map(toInspectableValue)), M.when(Predicate.isReadonlyRecord, Record.map(toInspectableValue)), M.orElse(Function.identity));
|
|
161
147
|
const Tagged = S.Struct({ _tag: S.String });
|
|
162
148
|
const isTagged = S.is(Tagged);
|
|
163
149
|
const objectPreview = (value) => pipe(value, Record.keys, Array_.filter(key => key !== '_tag'), Array_.match({
|
|
@@ -353,8 +339,8 @@ const SubscriptionDeps = S.Struct({
|
|
|
353
339
|
});
|
|
354
340
|
const makeOverlaySubscriptions = (store) => makeSubscriptions(SubscriptionDeps)({
|
|
355
341
|
storeUpdates: {
|
|
356
|
-
modelToDependencies: (
|
|
357
|
-
dependenciesToStream:
|
|
342
|
+
modelToDependencies: () => true,
|
|
343
|
+
dependenciesToStream: () => Stream.concat(Stream.fromEffect(SubscriptionRef.get(store.stateRef).pipe(Effect.map(state => ReceivedStoreUpdate(toDisplayState(state))))), Stream.map(store.stateRef.changes, state => ReceivedStoreUpdate(toDisplayState(state)))),
|
|
358
344
|
},
|
|
359
345
|
mobileBreakpoint: {
|
|
360
346
|
modelToDependencies: () => null,
|
|
@@ -894,7 +880,7 @@ export const createOverlay = (store, position, mode, maybeBanner) => Effect.gen(
|
|
|
894
880
|
view: makeView(position, mode, maybeBanner),
|
|
895
881
|
container,
|
|
896
882
|
subscriptions: makeOverlaySubscriptions(store),
|
|
897
|
-
|
|
883
|
+
devTools: false,
|
|
898
884
|
});
|
|
899
885
|
yield* Effect.forkDaemon(overlayRuntime());
|
|
900
886
|
});
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
import { Schema as S } from 'effect';
|
|
2
|
+
/** A serialized history entry as it appears on the wire. */
|
|
3
|
+
export declare const SerializedEntry: S.Struct<{
|
|
4
|
+
index: typeof S.Number;
|
|
5
|
+
tag: typeof S.String;
|
|
6
|
+
message: typeof S.Unknown;
|
|
7
|
+
commandNames: S.Array$<typeof S.String>;
|
|
8
|
+
timestamp: typeof S.Number;
|
|
9
|
+
isModelChanged: typeof S.Boolean;
|
|
10
|
+
changedPaths: S.Array$<typeof S.String>;
|
|
11
|
+
affectedPaths: S.Array$<typeof S.String>;
|
|
12
|
+
}>;
|
|
13
|
+
/** A serialized history entry suitable for transmission over the WS protocol. */
|
|
14
|
+
export type SerializedEntry = typeof SerializedEntry.Type;
|
|
15
|
+
/** Metadata about a single keyframe. The index identifies the point in history where the runtime can replay back to. */
|
|
16
|
+
export declare const KeyframeInfo: S.Struct<{
|
|
17
|
+
index: typeof S.Number;
|
|
18
|
+
}>;
|
|
19
|
+
/** Metadata about a single keyframe. */
|
|
20
|
+
export type KeyframeInfo = typeof KeyframeInfo.Type;
|
|
21
|
+
/** Metadata about a connected browser runtime. The `maybeMessageSchema` carries the runtime's app-level Message Schema as JSON Schema for `dispatch_message` validation, when the runtime exposes one via `DevToolsConfig.Message`. */
|
|
22
|
+
export declare const RuntimeInfo: S.Struct<{
|
|
23
|
+
connectionId: typeof S.String;
|
|
24
|
+
url: typeof S.String;
|
|
25
|
+
title: typeof S.String;
|
|
26
|
+
maybeMessageSchema: S.Option<typeof S.Unknown>;
|
|
27
|
+
}>;
|
|
28
|
+
/** Metadata about a connected browser runtime. */
|
|
29
|
+
export type RuntimeInfo = typeof RuntimeInfo.Type;
|
|
30
|
+
/** Request the current Model snapshot. */
|
|
31
|
+
export declare const RequestGetModel: import("../schema/index.js").CallableTaggedStruct<"RequestGetModel", {}>;
|
|
32
|
+
/** Request recent history entries, optionally starting from a given index. */
|
|
33
|
+
export declare const RequestListMessages: import("../schema/index.js").CallableTaggedStruct<"RequestListMessages", {
|
|
34
|
+
limit: typeof S.Number;
|
|
35
|
+
maybeSinceIndex: S.Option<typeof S.Number>;
|
|
36
|
+
}>;
|
|
37
|
+
/** Request a single history entry by index, including before/after Model snapshots. */
|
|
38
|
+
export declare const RequestGetMessage: import("../schema/index.js").CallableTaggedStruct<"RequestGetMessage", {
|
|
39
|
+
index: typeof S.Number;
|
|
40
|
+
}>;
|
|
41
|
+
/** Request the list of available keyframes. */
|
|
42
|
+
export declare const RequestListKeyframes: import("../schema/index.js").CallableTaggedStruct<"RequestListKeyframes", {}>;
|
|
43
|
+
/** Request the runtime jump to a specific keyframe. The runtime is paused on success; resume via RequestResume. */
|
|
44
|
+
export declare const RequestReplayToKeyframe: import("../schema/index.js").CallableTaggedStruct<"RequestReplayToKeyframe", {
|
|
45
|
+
keyframeIndex: typeof S.Number;
|
|
46
|
+
}>;
|
|
47
|
+
/** Request the runtime resume normal execution from a paused state. */
|
|
48
|
+
export declare const RequestResume: import("../schema/index.js").CallableTaggedStruct<"RequestResume", {}>;
|
|
49
|
+
/** Request the runtime dispatch a Message at the current state. The payload is opaque to the protocol; the runtime validates against the app's Message Schema. */
|
|
50
|
+
export declare const RequestDispatchMessage: import("../schema/index.js").CallableTaggedStruct<"RequestDispatchMessage", {
|
|
51
|
+
message: typeof S.Unknown;
|
|
52
|
+
}>;
|
|
53
|
+
/** Request the list of currently connected browser runtimes. Handled by the Vite plugin, not forwarded to a runtime. */
|
|
54
|
+
export declare const RequestListRuntimes: import("../schema/index.js").CallableTaggedStruct<"RequestListRuntimes", {}>;
|
|
55
|
+
/** A request from the MCP server. RequestListRuntimes is handled at the Vite plugin layer; all other requests are routed to a browser runtime. */
|
|
56
|
+
export declare const Request: S.Union<[import("../schema/index.js").CallableTaggedStruct<"RequestGetModel", {}>, import("../schema/index.js").CallableTaggedStruct<"RequestListMessages", {
|
|
57
|
+
limit: typeof S.Number;
|
|
58
|
+
maybeSinceIndex: S.Option<typeof S.Number>;
|
|
59
|
+
}>, import("../schema/index.js").CallableTaggedStruct<"RequestGetMessage", {
|
|
60
|
+
index: typeof S.Number;
|
|
61
|
+
}>, import("../schema/index.js").CallableTaggedStruct<"RequestListKeyframes", {}>, import("../schema/index.js").CallableTaggedStruct<"RequestReplayToKeyframe", {
|
|
62
|
+
keyframeIndex: typeof S.Number;
|
|
63
|
+
}>, import("../schema/index.js").CallableTaggedStruct<"RequestResume", {}>, import("../schema/index.js").CallableTaggedStruct<"RequestDispatchMessage", {
|
|
64
|
+
message: typeof S.Unknown;
|
|
65
|
+
}>, import("../schema/index.js").CallableTaggedStruct<"RequestListRuntimes", {}>]>;
|
|
66
|
+
/** A request from the MCP server. */
|
|
67
|
+
export type Request = typeof Request.Type;
|
|
68
|
+
/** Response carrying the current Model snapshot. */
|
|
69
|
+
export declare const ResponseModel: import("../schema/index.js").CallableTaggedStruct<"ResponseModel", {
|
|
70
|
+
model: typeof S.Unknown;
|
|
71
|
+
}>;
|
|
72
|
+
/** Response carrying a page of history entries. `maybeNextIndex` is `Some` when more entries are available beyond this page (pass it as `RequestListMessages.maybeSinceIndex` to fetch the next page) and `None` when this page reaches the current end of history. */
|
|
73
|
+
export declare const ResponseMessages: import("../schema/index.js").CallableTaggedStruct<"ResponseMessages", {
|
|
74
|
+
entries: S.Array$<S.Struct<{
|
|
75
|
+
index: typeof S.Number;
|
|
76
|
+
tag: typeof S.String;
|
|
77
|
+
message: typeof S.Unknown;
|
|
78
|
+
commandNames: S.Array$<typeof S.String>;
|
|
79
|
+
timestamp: typeof S.Number;
|
|
80
|
+
isModelChanged: typeof S.Boolean;
|
|
81
|
+
changedPaths: S.Array$<typeof S.String>;
|
|
82
|
+
affectedPaths: S.Array$<typeof S.String>;
|
|
83
|
+
}>>;
|
|
84
|
+
maybeNextIndex: S.Option<typeof S.Number>;
|
|
85
|
+
}>;
|
|
86
|
+
/** Response carrying a single history entry with surrounding Model snapshots. */
|
|
87
|
+
export declare const ResponseMessage: import("../schema/index.js").CallableTaggedStruct<"ResponseMessage", {
|
|
88
|
+
entry: S.Struct<{
|
|
89
|
+
index: typeof S.Number;
|
|
90
|
+
tag: typeof S.String;
|
|
91
|
+
message: typeof S.Unknown;
|
|
92
|
+
commandNames: S.Array$<typeof S.String>;
|
|
93
|
+
timestamp: typeof S.Number;
|
|
94
|
+
isModelChanged: typeof S.Boolean;
|
|
95
|
+
changedPaths: S.Array$<typeof S.String>;
|
|
96
|
+
affectedPaths: S.Array$<typeof S.String>;
|
|
97
|
+
}>;
|
|
98
|
+
modelBefore: typeof S.Unknown;
|
|
99
|
+
modelAfter: typeof S.Unknown;
|
|
100
|
+
}>;
|
|
101
|
+
/** Response carrying the list of available keyframes. */
|
|
102
|
+
export declare const ResponseKeyframes: import("../schema/index.js").CallableTaggedStruct<"ResponseKeyframes", {
|
|
103
|
+
keyframes: S.Array$<S.Struct<{
|
|
104
|
+
index: typeof S.Number;
|
|
105
|
+
}>>;
|
|
106
|
+
}>;
|
|
107
|
+
/** Response confirming a successful replay. The runtime is paused at this Model. */
|
|
108
|
+
export declare const ResponseReplayed: import("../schema/index.js").CallableTaggedStruct<"ResponseReplayed", {
|
|
109
|
+
model: typeof S.Unknown;
|
|
110
|
+
}>;
|
|
111
|
+
/** Response confirming the runtime resumed normal execution. */
|
|
112
|
+
export declare const ResponseResumed: import("../schema/index.js").CallableTaggedStruct<"ResponseResumed", {}>;
|
|
113
|
+
/** Response confirming a Message was dispatched. The `acceptedAtIndex` is the absolute history index where the entry is predicted to land. Computed from the runtime's history length at dispatch time. The Message reaches the runtime's update loop asynchronously, so concurrent Messages produced by the runtime itself could in principle shift ordering; in practice the bridge is the only external dispatch source and the runtime queue serializes Messages, so this index is reliable for correlation. */
|
|
114
|
+
export declare const ResponseDispatched: import("../schema/index.js").CallableTaggedStruct<"ResponseDispatched", {
|
|
115
|
+
acceptedAtIndex: typeof S.Number;
|
|
116
|
+
}>;
|
|
117
|
+
/** Response carrying the list of connected runtimes. */
|
|
118
|
+
export declare const ResponseRuntimes: import("../schema/index.js").CallableTaggedStruct<"ResponseRuntimes", {
|
|
119
|
+
runtimes: S.Array$<S.Struct<{
|
|
120
|
+
connectionId: typeof S.String;
|
|
121
|
+
url: typeof S.String;
|
|
122
|
+
title: typeof S.String;
|
|
123
|
+
maybeMessageSchema: S.Option<typeof S.Unknown>;
|
|
124
|
+
}>>;
|
|
125
|
+
}>;
|
|
126
|
+
/** Response carrying an error reason for a failed Request. */
|
|
127
|
+
export declare const ResponseError: import("../schema/index.js").CallableTaggedStruct<"ResponseError", {
|
|
128
|
+
reason: typeof S.String;
|
|
129
|
+
}>;
|
|
130
|
+
/** A response replying to a Request. */
|
|
131
|
+
export declare const Response: S.Union<[import("../schema/index.js").CallableTaggedStruct<"ResponseModel", {
|
|
132
|
+
model: typeof S.Unknown;
|
|
133
|
+
}>, import("../schema/index.js").CallableTaggedStruct<"ResponseMessages", {
|
|
134
|
+
entries: S.Array$<S.Struct<{
|
|
135
|
+
index: typeof S.Number;
|
|
136
|
+
tag: typeof S.String;
|
|
137
|
+
message: typeof S.Unknown;
|
|
138
|
+
commandNames: S.Array$<typeof S.String>;
|
|
139
|
+
timestamp: typeof S.Number;
|
|
140
|
+
isModelChanged: typeof S.Boolean;
|
|
141
|
+
changedPaths: S.Array$<typeof S.String>;
|
|
142
|
+
affectedPaths: S.Array$<typeof S.String>;
|
|
143
|
+
}>>;
|
|
144
|
+
maybeNextIndex: S.Option<typeof S.Number>;
|
|
145
|
+
}>, import("../schema/index.js").CallableTaggedStruct<"ResponseMessage", {
|
|
146
|
+
entry: S.Struct<{
|
|
147
|
+
index: typeof S.Number;
|
|
148
|
+
tag: typeof S.String;
|
|
149
|
+
message: typeof S.Unknown;
|
|
150
|
+
commandNames: S.Array$<typeof S.String>;
|
|
151
|
+
timestamp: typeof S.Number;
|
|
152
|
+
isModelChanged: typeof S.Boolean;
|
|
153
|
+
changedPaths: S.Array$<typeof S.String>;
|
|
154
|
+
affectedPaths: S.Array$<typeof S.String>;
|
|
155
|
+
}>;
|
|
156
|
+
modelBefore: typeof S.Unknown;
|
|
157
|
+
modelAfter: typeof S.Unknown;
|
|
158
|
+
}>, import("../schema/index.js").CallableTaggedStruct<"ResponseKeyframes", {
|
|
159
|
+
keyframes: S.Array$<S.Struct<{
|
|
160
|
+
index: typeof S.Number;
|
|
161
|
+
}>>;
|
|
162
|
+
}>, import("../schema/index.js").CallableTaggedStruct<"ResponseReplayed", {
|
|
163
|
+
model: typeof S.Unknown;
|
|
164
|
+
}>, import("../schema/index.js").CallableTaggedStruct<"ResponseResumed", {}>, import("../schema/index.js").CallableTaggedStruct<"ResponseDispatched", {
|
|
165
|
+
acceptedAtIndex: typeof S.Number;
|
|
166
|
+
}>, import("../schema/index.js").CallableTaggedStruct<"ResponseRuntimes", {
|
|
167
|
+
runtimes: S.Array$<S.Struct<{
|
|
168
|
+
connectionId: typeof S.String;
|
|
169
|
+
url: typeof S.String;
|
|
170
|
+
title: typeof S.String;
|
|
171
|
+
maybeMessageSchema: S.Option<typeof S.Unknown>;
|
|
172
|
+
}>>;
|
|
173
|
+
}>, import("../schema/index.js").CallableTaggedStruct<"ResponseError", {
|
|
174
|
+
reason: typeof S.String;
|
|
175
|
+
}>]>;
|
|
176
|
+
/** A response replying to a Request. */
|
|
177
|
+
export type Response = typeof Response.Type;
|
|
178
|
+
/** A new browser runtime connected. The `runtime.maybeMessageSchema` field carries the runtime's Message Schema (as JSON Schema) when the runtime exposes one via `DevToolsConfig.Message`. */
|
|
179
|
+
export declare const EventConnected: import("../schema/index.js").CallableTaggedStruct<"EventConnected", {
|
|
180
|
+
runtime: S.Struct<{
|
|
181
|
+
connectionId: typeof S.String;
|
|
182
|
+
url: typeof S.String;
|
|
183
|
+
title: typeof S.String;
|
|
184
|
+
maybeMessageSchema: S.Option<typeof S.Unknown>;
|
|
185
|
+
}>;
|
|
186
|
+
}>;
|
|
187
|
+
/** A previously connected runtime disconnected. */
|
|
188
|
+
export declare const EventDisconnected: import("../schema/index.js").CallableTaggedStruct<"EventDisconnected", {
|
|
189
|
+
connectionId: typeof S.String;
|
|
190
|
+
}>;
|
|
191
|
+
/** A runtime lifecycle event used by the Vite plugin to track which browser tabs are connected. Not forwarded to MCP clients. */
|
|
192
|
+
export declare const Event: S.Union<[import("../schema/index.js").CallableTaggedStruct<"EventConnected", {
|
|
193
|
+
runtime: S.Struct<{
|
|
194
|
+
connectionId: typeof S.String;
|
|
195
|
+
url: typeof S.String;
|
|
196
|
+
title: typeof S.String;
|
|
197
|
+
maybeMessageSchema: S.Option<typeof S.Unknown>;
|
|
198
|
+
}>;
|
|
199
|
+
}>, import("../schema/index.js").CallableTaggedStruct<"EventDisconnected", {
|
|
200
|
+
connectionId: typeof S.String;
|
|
201
|
+
}>]>;
|
|
202
|
+
/** A runtime lifecycle event. */
|
|
203
|
+
export type Event = typeof Event.Type;
|
|
204
|
+
/** A wire frame carrying a Request from the MCP server. The id is opaque, used only by the MCP server to correlate the matching Response. The maybeConnectionId routes the request to a specific runtime when present. */
|
|
205
|
+
export declare const RequestFrame: S.Struct<{
|
|
206
|
+
id: typeof S.String;
|
|
207
|
+
maybeConnectionId: S.Option<typeof S.String>;
|
|
208
|
+
request: S.Union<[import("../schema/index.js").CallableTaggedStruct<"RequestGetModel", {}>, import("../schema/index.js").CallableTaggedStruct<"RequestListMessages", {
|
|
209
|
+
limit: typeof S.Number;
|
|
210
|
+
maybeSinceIndex: S.Option<typeof S.Number>;
|
|
211
|
+
}>, import("../schema/index.js").CallableTaggedStruct<"RequestGetMessage", {
|
|
212
|
+
index: typeof S.Number;
|
|
213
|
+
}>, import("../schema/index.js").CallableTaggedStruct<"RequestListKeyframes", {}>, import("../schema/index.js").CallableTaggedStruct<"RequestReplayToKeyframe", {
|
|
214
|
+
keyframeIndex: typeof S.Number;
|
|
215
|
+
}>, import("../schema/index.js").CallableTaggedStruct<"RequestResume", {}>, import("../schema/index.js").CallableTaggedStruct<"RequestDispatchMessage", {
|
|
216
|
+
message: typeof S.Unknown;
|
|
217
|
+
}>, import("../schema/index.js").CallableTaggedStruct<"RequestListRuntimes", {}>]>;
|
|
218
|
+
}>;
|
|
219
|
+
/** A wire frame carrying a Request from the MCP server. */
|
|
220
|
+
export type RequestFrame = typeof RequestFrame.Type;
|
|
221
|
+
/** A wire frame carrying a Response, correlated to a Request by id. */
|
|
222
|
+
export declare const ResponseFrame: S.Struct<{
|
|
223
|
+
id: typeof S.String;
|
|
224
|
+
response: S.Union<[import("../schema/index.js").CallableTaggedStruct<"ResponseModel", {
|
|
225
|
+
model: typeof S.Unknown;
|
|
226
|
+
}>, import("../schema/index.js").CallableTaggedStruct<"ResponseMessages", {
|
|
227
|
+
entries: S.Array$<S.Struct<{
|
|
228
|
+
index: typeof S.Number;
|
|
229
|
+
tag: typeof S.String;
|
|
230
|
+
message: typeof S.Unknown;
|
|
231
|
+
commandNames: S.Array$<typeof S.String>;
|
|
232
|
+
timestamp: typeof S.Number;
|
|
233
|
+
isModelChanged: typeof S.Boolean;
|
|
234
|
+
changedPaths: S.Array$<typeof S.String>;
|
|
235
|
+
affectedPaths: S.Array$<typeof S.String>;
|
|
236
|
+
}>>;
|
|
237
|
+
maybeNextIndex: S.Option<typeof S.Number>;
|
|
238
|
+
}>, import("../schema/index.js").CallableTaggedStruct<"ResponseMessage", {
|
|
239
|
+
entry: S.Struct<{
|
|
240
|
+
index: typeof S.Number;
|
|
241
|
+
tag: typeof S.String;
|
|
242
|
+
message: typeof S.Unknown;
|
|
243
|
+
commandNames: S.Array$<typeof S.String>;
|
|
244
|
+
timestamp: typeof S.Number;
|
|
245
|
+
isModelChanged: typeof S.Boolean;
|
|
246
|
+
changedPaths: S.Array$<typeof S.String>;
|
|
247
|
+
affectedPaths: S.Array$<typeof S.String>;
|
|
248
|
+
}>;
|
|
249
|
+
modelBefore: typeof S.Unknown;
|
|
250
|
+
modelAfter: typeof S.Unknown;
|
|
251
|
+
}>, import("../schema/index.js").CallableTaggedStruct<"ResponseKeyframes", {
|
|
252
|
+
keyframes: S.Array$<S.Struct<{
|
|
253
|
+
index: typeof S.Number;
|
|
254
|
+
}>>;
|
|
255
|
+
}>, import("../schema/index.js").CallableTaggedStruct<"ResponseReplayed", {
|
|
256
|
+
model: typeof S.Unknown;
|
|
257
|
+
}>, import("../schema/index.js").CallableTaggedStruct<"ResponseResumed", {}>, import("../schema/index.js").CallableTaggedStruct<"ResponseDispatched", {
|
|
258
|
+
acceptedAtIndex: typeof S.Number;
|
|
259
|
+
}>, import("../schema/index.js").CallableTaggedStruct<"ResponseRuntimes", {
|
|
260
|
+
runtimes: S.Array$<S.Struct<{
|
|
261
|
+
connectionId: typeof S.String;
|
|
262
|
+
url: typeof S.String;
|
|
263
|
+
title: typeof S.String;
|
|
264
|
+
maybeMessageSchema: S.Option<typeof S.Unknown>;
|
|
265
|
+
}>>;
|
|
266
|
+
}>, import("../schema/index.js").CallableTaggedStruct<"ResponseError", {
|
|
267
|
+
reason: typeof S.String;
|
|
268
|
+
}>]>;
|
|
269
|
+
}>;
|
|
270
|
+
/** A wire frame carrying a Response, correlated to a Request by id. */
|
|
271
|
+
export type ResponseFrame = typeof ResponseFrame.Type;
|
|
272
|
+
/** A wire frame carrying a runtime lifecycle event from the bridge to the Vite plugin. */
|
|
273
|
+
export declare const EventFrame: S.Struct<{
|
|
274
|
+
maybeConnectionId: S.Option<typeof S.String>;
|
|
275
|
+
event: S.Union<[import("../schema/index.js").CallableTaggedStruct<"EventConnected", {
|
|
276
|
+
runtime: S.Struct<{
|
|
277
|
+
connectionId: typeof S.String;
|
|
278
|
+
url: typeof S.String;
|
|
279
|
+
title: typeof S.String;
|
|
280
|
+
maybeMessageSchema: S.Option<typeof S.Unknown>;
|
|
281
|
+
}>;
|
|
282
|
+
}>, import("../schema/index.js").CallableTaggedStruct<"EventDisconnected", {
|
|
283
|
+
connectionId: typeof S.String;
|
|
284
|
+
}>]>;
|
|
285
|
+
}>;
|
|
286
|
+
/** A wire frame carrying a runtime lifecycle event. */
|
|
287
|
+
export type EventFrame = typeof EventFrame.Type;
|
|
288
|
+
//# sourceMappingURL=protocol.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../../src/devTools/protocol.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { Schema as S } from 'effect';
|
|
2
|
+
import { ts } from '../schema/index.js';
|
|
3
|
+
// SHARED
|
|
4
|
+
/** A serialized history entry as it appears on the wire. */
|
|
5
|
+
export const SerializedEntry = S.Struct({
|
|
6
|
+
index: S.Number,
|
|
7
|
+
tag: S.String,
|
|
8
|
+
message: S.Unknown,
|
|
9
|
+
commandNames: S.Array(S.String),
|
|
10
|
+
timestamp: S.Number,
|
|
11
|
+
isModelChanged: S.Boolean,
|
|
12
|
+
changedPaths: S.Array(S.String),
|
|
13
|
+
affectedPaths: S.Array(S.String),
|
|
14
|
+
});
|
|
15
|
+
/** Metadata about a single keyframe. The index identifies the point in history where the runtime can replay back to. */
|
|
16
|
+
export const KeyframeInfo = S.Struct({
|
|
17
|
+
index: S.Number,
|
|
18
|
+
});
|
|
19
|
+
/** Metadata about a connected browser runtime. The `maybeMessageSchema` carries the runtime's app-level Message Schema as JSON Schema for `dispatch_message` validation, when the runtime exposes one via `DevToolsConfig.Message`. */
|
|
20
|
+
export const RuntimeInfo = S.Struct({
|
|
21
|
+
connectionId: S.String,
|
|
22
|
+
url: S.String,
|
|
23
|
+
title: S.String,
|
|
24
|
+
maybeMessageSchema: S.Option(S.Unknown),
|
|
25
|
+
});
|
|
26
|
+
// REQUEST
|
|
27
|
+
/** Request the current Model snapshot. */
|
|
28
|
+
export const RequestGetModel = ts('RequestGetModel');
|
|
29
|
+
/** Request recent history entries, optionally starting from a given index. */
|
|
30
|
+
export const RequestListMessages = ts('RequestListMessages', {
|
|
31
|
+
limit: S.Number,
|
|
32
|
+
maybeSinceIndex: S.Option(S.Number),
|
|
33
|
+
});
|
|
34
|
+
/** Request a single history entry by index, including before/after Model snapshots. */
|
|
35
|
+
export const RequestGetMessage = ts('RequestGetMessage', {
|
|
36
|
+
index: S.Number,
|
|
37
|
+
});
|
|
38
|
+
/** Request the list of available keyframes. */
|
|
39
|
+
export const RequestListKeyframes = ts('RequestListKeyframes');
|
|
40
|
+
/** Request the runtime jump to a specific keyframe. The runtime is paused on success; resume via RequestResume. */
|
|
41
|
+
export const RequestReplayToKeyframe = ts('RequestReplayToKeyframe', {
|
|
42
|
+
keyframeIndex: S.Number,
|
|
43
|
+
});
|
|
44
|
+
/** Request the runtime resume normal execution from a paused state. */
|
|
45
|
+
export const RequestResume = ts('RequestResume');
|
|
46
|
+
/** Request the runtime dispatch a Message at the current state. The payload is opaque to the protocol; the runtime validates against the app's Message Schema. */
|
|
47
|
+
export const RequestDispatchMessage = ts('RequestDispatchMessage', {
|
|
48
|
+
message: S.Unknown,
|
|
49
|
+
});
|
|
50
|
+
/** Request the list of currently connected browser runtimes. Handled by the Vite plugin, not forwarded to a runtime. */
|
|
51
|
+
export const RequestListRuntimes = ts('RequestListRuntimes');
|
|
52
|
+
/** A request from the MCP server. RequestListRuntimes is handled at the Vite plugin layer; all other requests are routed to a browser runtime. */
|
|
53
|
+
export const Request = S.Union(RequestGetModel, RequestListMessages, RequestGetMessage, RequestListKeyframes, RequestReplayToKeyframe, RequestResume, RequestDispatchMessage, RequestListRuntimes);
|
|
54
|
+
// RESPONSE
|
|
55
|
+
/** Response carrying the current Model snapshot. */
|
|
56
|
+
export const ResponseModel = ts('ResponseModel', {
|
|
57
|
+
model: S.Unknown,
|
|
58
|
+
});
|
|
59
|
+
/** Response carrying a page of history entries. `maybeNextIndex` is `Some` when more entries are available beyond this page (pass it as `RequestListMessages.maybeSinceIndex` to fetch the next page) and `None` when this page reaches the current end of history. */
|
|
60
|
+
export const ResponseMessages = ts('ResponseMessages', {
|
|
61
|
+
entries: S.Array(SerializedEntry),
|
|
62
|
+
maybeNextIndex: S.Option(S.Number),
|
|
63
|
+
});
|
|
64
|
+
/** Response carrying a single history entry with surrounding Model snapshots. */
|
|
65
|
+
export const ResponseMessage = ts('ResponseMessage', {
|
|
66
|
+
entry: SerializedEntry,
|
|
67
|
+
modelBefore: S.Unknown,
|
|
68
|
+
modelAfter: S.Unknown,
|
|
69
|
+
});
|
|
70
|
+
/** Response carrying the list of available keyframes. */
|
|
71
|
+
export const ResponseKeyframes = ts('ResponseKeyframes', {
|
|
72
|
+
keyframes: S.Array(KeyframeInfo),
|
|
73
|
+
});
|
|
74
|
+
/** Response confirming a successful replay. The runtime is paused at this Model. */
|
|
75
|
+
export const ResponseReplayed = ts('ResponseReplayed', {
|
|
76
|
+
model: S.Unknown,
|
|
77
|
+
});
|
|
78
|
+
/** Response confirming the runtime resumed normal execution. */
|
|
79
|
+
export const ResponseResumed = ts('ResponseResumed');
|
|
80
|
+
/** Response confirming a Message was dispatched. The `acceptedAtIndex` is the absolute history index where the entry is predicted to land. Computed from the runtime's history length at dispatch time. The Message reaches the runtime's update loop asynchronously, so concurrent Messages produced by the runtime itself could in principle shift ordering; in practice the bridge is the only external dispatch source and the runtime queue serializes Messages, so this index is reliable for correlation. */
|
|
81
|
+
export const ResponseDispatched = ts('ResponseDispatched', {
|
|
82
|
+
acceptedAtIndex: S.Number,
|
|
83
|
+
});
|
|
84
|
+
/** Response carrying the list of connected runtimes. */
|
|
85
|
+
export const ResponseRuntimes = ts('ResponseRuntimes', {
|
|
86
|
+
runtimes: S.Array(RuntimeInfo),
|
|
87
|
+
});
|
|
88
|
+
/** Response carrying an error reason for a failed Request. */
|
|
89
|
+
export const ResponseError = ts('ResponseError', {
|
|
90
|
+
reason: S.String,
|
|
91
|
+
});
|
|
92
|
+
/** A response replying to a Request. */
|
|
93
|
+
export const Response = S.Union(ResponseModel, ResponseMessages, ResponseMessage, ResponseKeyframes, ResponseReplayed, ResponseResumed, ResponseDispatched, ResponseRuntimes, ResponseError);
|
|
94
|
+
// EVENT
|
|
95
|
+
/** A new browser runtime connected. The `runtime.maybeMessageSchema` field carries the runtime's Message Schema (as JSON Schema) when the runtime exposes one via `DevToolsConfig.Message`. */
|
|
96
|
+
export const EventConnected = ts('EventConnected', {
|
|
97
|
+
runtime: RuntimeInfo,
|
|
98
|
+
});
|
|
99
|
+
/** A previously connected runtime disconnected. */
|
|
100
|
+
export const EventDisconnected = ts('EventDisconnected', {
|
|
101
|
+
connectionId: S.String,
|
|
102
|
+
});
|
|
103
|
+
/** A runtime lifecycle event used by the Vite plugin to track which browser tabs are connected. Not forwarded to MCP clients. */
|
|
104
|
+
export const Event = S.Union(EventConnected, EventDisconnected);
|
|
105
|
+
// FRAME
|
|
106
|
+
/** A wire frame carrying a Request from the MCP server. The id is opaque, used only by the MCP server to correlate the matching Response. The maybeConnectionId routes the request to a specific runtime when present. */
|
|
107
|
+
export const RequestFrame = S.Struct({
|
|
108
|
+
id: S.String,
|
|
109
|
+
maybeConnectionId: S.Option(S.String),
|
|
110
|
+
request: Request,
|
|
111
|
+
});
|
|
112
|
+
/** A wire frame carrying a Response, correlated to a Request by id. */
|
|
113
|
+
export const ResponseFrame = S.Struct({
|
|
114
|
+
id: S.String,
|
|
115
|
+
response: Response,
|
|
116
|
+
});
|
|
117
|
+
/** A wire frame carrying a runtime lifecycle event from the bridge to the Vite plugin. */
|
|
118
|
+
export const EventFrame = S.Struct({
|
|
119
|
+
maybeConnectionId: S.Option(S.String),
|
|
120
|
+
event: Event,
|
|
121
|
+
});
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export { EventConnected, EventDisconnected, EventFrame, Event, KeyframeInfo, RequestDispatchMessage, RequestFrame, RequestGetMessage, RequestGetModel, RequestListKeyframes, RequestListMessages, RequestListRuntimes, RequestReplayToKeyframe, RequestResume, Request, ResponseDispatched, ResponseError, ResponseFrame, ResponseKeyframes, ResponseMessage, ResponseMessages, ResponseModel, ResponseReplayed, ResponseResumed, ResponseRuntimes, Response, RuntimeInfo, SerializedEntry, } from './protocol.js';
|
|
2
|
+
//# sourceMappingURL=public.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../src/devTools/public.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EACd,iBAAiB,EACjB,UAAU,EACV,KAAK,EACL,YAAY,EACZ,sBAAsB,EACtB,YAAY,EACZ,iBAAiB,EACjB,eAAe,EACf,oBAAoB,EACpB,mBAAmB,EACnB,mBAAmB,EACnB,uBAAuB,EACvB,aAAa,EACb,OAAO,EACP,kBAAkB,EAClB,aAAa,EACb,aAAa,EACb,iBAAiB,EACjB,eAAe,EACf,gBAAgB,EAChB,aAAa,EACb,gBAAgB,EAChB,eAAe,EACf,gBAAgB,EAChB,QAAQ,EACR,WAAW,EACX,eAAe,GAChB,MAAM,eAAe,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { EventConnected, EventDisconnected, EventFrame, Event, KeyframeInfo, RequestDispatchMessage, RequestFrame, RequestGetMessage, RequestGetModel, RequestListKeyframes, RequestListMessages, RequestListRuntimes, RequestReplayToKeyframe, RequestResume, Request, ResponseDispatched, ResponseError, ResponseFrame, ResponseKeyframes, ResponseMessage, ResponseMessages, ResponseModel, ResponseReplayed, ResponseResumed, ResponseRuntimes, Response, RuntimeInfo, SerializedEntry, } from './protocol.js';
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { SerializedEntry } from './protocol.js';
|
|
2
|
+
import type { HistoryEntry } from './store.js';
|
|
3
|
+
/**
|
|
4
|
+
* Convert DOM-class instances (File, Blob, Date, URL) to plain-object
|
|
5
|
+
* representations so the tree renderer's key-enumeration walk can see their
|
|
6
|
+
* meaningful data, which otherwise lives on the prototype as getters and
|
|
7
|
+
* is invisible to `Object.keys`. Recurses through arrays and records so
|
|
8
|
+
* the transform applies at every level. File is matched before Blob
|
|
9
|
+
* because File extends Blob.
|
|
10
|
+
*/
|
|
11
|
+
export declare const toInspectableValue: (value: unknown) => unknown;
|
|
12
|
+
/**
|
|
13
|
+
* Convert a `HistoryEntry` plus its absolute index into the wire-friendly
|
|
14
|
+
* `SerializedEntry` shape. Flattens the diff's `HashSet` path collections to
|
|
15
|
+
* plain string arrays for JSON transmission and runs the message body through
|
|
16
|
+
* `toInspectableValue` so DOM-class instances become inspectable objects.
|
|
17
|
+
*/
|
|
18
|
+
export declare const toSerializedEntry: (entry: HistoryEntry, index: number) => SerializedEntry;
|
|
19
|
+
//# sourceMappingURL=serialize.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serialize.d.ts","sourceRoot":"","sources":["../../src/devTools/serialize.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AACpD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAE9C;;;;;;;GAOG;AACH,eAAO,MAAM,kBAAkB,GAAI,OAAO,OAAO,KAAG,OAiBjD,CAAA;AAEH;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,GAC5B,OAAO,YAAY,EACnB,OAAO,MAAM,KACZ,eASD,CAAA"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Array as Array_, Function, HashSet, Match as M, Predicate, Record, } from 'effect';
|
|
2
|
+
/**
|
|
3
|
+
* Convert DOM-class instances (File, Blob, Date, URL) to plain-object
|
|
4
|
+
* representations so the tree renderer's key-enumeration walk can see their
|
|
5
|
+
* meaningful data, which otherwise lives on the prototype as getters and
|
|
6
|
+
* is invisible to `Object.keys`. Recurses through arrays and records so
|
|
7
|
+
* the transform applies at every level. File is matched before Blob
|
|
8
|
+
* because File extends Blob.
|
|
9
|
+
*/
|
|
10
|
+
export const toInspectableValue = (value) => M.value(value).pipe(M.when(M.instanceOf(File), file => ({
|
|
11
|
+
name: file.name,
|
|
12
|
+
size: file.size,
|
|
13
|
+
type: file.type,
|
|
14
|
+
lastModified: file.lastModified,
|
|
15
|
+
})), M.when(M.instanceOf(Blob), blob => ({
|
|
16
|
+
size: blob.size,
|
|
17
|
+
type: blob.type,
|
|
18
|
+
})), M.when(M.instanceOf(Date), date => date.toISOString()), M.when(M.instanceOf(URL), ({ href }) => href), M.when(Array.isArray, Array_.map(toInspectableValue)), M.when(Predicate.isReadonlyRecord, Record.map(toInspectableValue)), M.orElse(Function.identity));
|
|
19
|
+
/**
|
|
20
|
+
* Convert a `HistoryEntry` plus its absolute index into the wire-friendly
|
|
21
|
+
* `SerializedEntry` shape. Flattens the diff's `HashSet` path collections to
|
|
22
|
+
* plain string arrays for JSON transmission and runs the message body through
|
|
23
|
+
* `toInspectableValue` so DOM-class instances become inspectable objects.
|
|
24
|
+
*/
|
|
25
|
+
export const toSerializedEntry = (entry, index) => ({
|
|
26
|
+
index,
|
|
27
|
+
tag: entry.tag,
|
|
28
|
+
message: toInspectableValue(entry.message),
|
|
29
|
+
commandNames: entry.commandNames,
|
|
30
|
+
timestamp: entry.timestamp,
|
|
31
|
+
isModelChanged: entry.isModelChanged,
|
|
32
|
+
changedPaths: HashSet.toValues(entry.diff.changedPaths),
|
|
33
|
+
affectedPaths: HashSet.toValues(entry.diff.affectedPaths),
|
|
34
|
+
});
|
|
@@ -28,8 +28,8 @@ export type Bridge = Readonly<{
|
|
|
28
28
|
render: (model: unknown) => Effect.Effect<void>;
|
|
29
29
|
getCurrentModel: Effect.Effect<unknown>;
|
|
30
30
|
}>;
|
|
31
|
-
export declare const
|
|
32
|
-
export type
|
|
31
|
+
export declare const createDevToolsStore: (bridge: Bridge, maxEntries?: number) => Effect.Effect<DevToolsStore>;
|
|
32
|
+
export type DevToolsStore = Readonly<{
|
|
33
33
|
recordInit: (model: unknown, commandNames: ReadonlyArray<string>) => Effect.Effect<void>;
|
|
34
34
|
recordMessage: (message: Readonly<{
|
|
35
35
|
_tag: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/devTools/store.ts"],"names":[],"mappings":"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"}
|
|
@@ -73,7 +73,7 @@ const emptyState = {
|
|
|
73
73
|
isPaused: false,
|
|
74
74
|
pausedAtIndex: 0,
|
|
75
75
|
};
|
|
76
|
-
export const
|
|
76
|
+
export const createDevToolsStore = (bridge, maxEntries = DEFAULT_MAX_ENTRIES) => Effect.gen(function* () {
|
|
77
77
|
const stateRef = yield* SubscriptionRef.make(emptyState);
|
|
78
78
|
const replayToIndex = (state, index) => {
|
|
79
79
|
const segmentStart = Math.floor(index / KEYFRAME_INTERVAL) * KEYFRAME_INTERVAL;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Effect, Option, Schema as S } from 'effect';
|
|
2
|
+
import { type DevToolsStore } from './store.js';
|
|
3
|
+
type Hot = NonNullable<ImportMeta['hot']>;
|
|
4
|
+
/**
|
|
5
|
+
* Start the browser-side WebSocket bridge that exposes a Foldkit runtime's
|
|
6
|
+
* DevToolsStore to an external MCP server (via the Vite plugin relay).
|
|
7
|
+
*
|
|
8
|
+
* Emits `EventConnected` on startup so the relay tracks this runtime.
|
|
9
|
+
* Listens on the request channel for `RequestFrame`s targeted at this
|
|
10
|
+
* connection's id and replies with the matching `ResponseFrame`. Emits
|
|
11
|
+
* `EventDisconnected` on tab close or HMR module dispose so the relay can
|
|
12
|
+
* remove this runtime from its connected set.
|
|
13
|
+
*
|
|
14
|
+
* `dispatch` enqueues a Message into the runtime's message queue; the bridge
|
|
15
|
+
* uses it to fulfill `RequestDispatchMessage` after the payload is validated
|
|
16
|
+
* against `maybeMessageSchema`. When `maybeMessageSchema` is `None`, dispatch
|
|
17
|
+
* requests are rejected with an informative error.
|
|
18
|
+
*
|
|
19
|
+
* Production-safe: callers must check `import.meta.hot` is defined before
|
|
20
|
+
* invoking this. The function assumes a live HMR connection.
|
|
21
|
+
*/
|
|
22
|
+
export declare const startWebSocketBridge: (store: DevToolsStore, hot: Hot, dispatch: (message: unknown) => Effect.Effect<void>, maybeMessageSchema: Option.Option<S.Schema<any, any, never>>) => Effect.Effect<void>;
|
|
23
|
+
export {};
|
|
24
|
+
//# sourceMappingURL=webSocketBridge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webSocketBridge.d.ts","sourceRoot":"","sources":["../../src/devTools/webSocketBridge.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,MAAM,EAKN,MAAM,EAGN,MAAM,IAAI,CAAC,EAGZ,MAAM,QAAQ,CAAA;AAuBf,OAAO,EAAE,KAAK,aAAa,EAAc,MAAM,YAAY,CAAA;AAE3D,KAAK,GAAG,GAAG,WAAW,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAA;AAczC;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,oBAAoB,GAC/B,OAAO,aAAa,EACpB,KAAK,GAAG,EACR,UAAU,CAAC,OAAO,EAAE,OAAO,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,EACnD,oBAAoB,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC,KAC3D,MAAM,CAAC,MAAM,CAAC,IAAI,CAgFjB,CAAA"}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { Array, Cause, Effect, Either, HashMap, JSONSchema, Match, Option, Order, Runtime, Schema as S, SubscriptionRef, pipe, } from 'effect';
|
|
2
|
+
import { OptionExt } from '../effectExtensions/index.js';
|
|
3
|
+
import { EventConnected, EventDisconnected, KeyframeInfo, RequestFrame, ResponseDispatched, ResponseError, ResponseKeyframes, ResponseMessage, ResponseMessages, ResponseModel, ResponseReplayed, ResponseResumed, RuntimeInfo, } from './protocol.js';
|
|
4
|
+
import { toInspectableValue, toSerializedEntry } from './serialize.js';
|
|
5
|
+
import { INIT_INDEX } from './store.js';
|
|
6
|
+
const REQUEST_CHANNEL = 'foldkit:devTools:request';
|
|
7
|
+
const RESPONSE_CHANNEL = 'foldkit:devTools:response';
|
|
8
|
+
const EVENT_CHANNEL = 'foldkit:devTools:event';
|
|
9
|
+
const generateConnectionId = () => `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
|
|
10
|
+
const currentAbsoluteIndex = (entriesLength, startIndex) => (entriesLength === 0 ? INIT_INDEX : startIndex + entriesLength - 1);
|
|
11
|
+
/**
|
|
12
|
+
* Start the browser-side WebSocket bridge that exposes a Foldkit runtime's
|
|
13
|
+
* DevToolsStore to an external MCP server (via the Vite plugin relay).
|
|
14
|
+
*
|
|
15
|
+
* Emits `EventConnected` on startup so the relay tracks this runtime.
|
|
16
|
+
* Listens on the request channel for `RequestFrame`s targeted at this
|
|
17
|
+
* connection's id and replies with the matching `ResponseFrame`. Emits
|
|
18
|
+
* `EventDisconnected` on tab close or HMR module dispose so the relay can
|
|
19
|
+
* remove this runtime from its connected set.
|
|
20
|
+
*
|
|
21
|
+
* `dispatch` enqueues a Message into the runtime's message queue; the bridge
|
|
22
|
+
* uses it to fulfill `RequestDispatchMessage` after the payload is validated
|
|
23
|
+
* against `maybeMessageSchema`. When `maybeMessageSchema` is `None`, dispatch
|
|
24
|
+
* requests are rejected with an informative error.
|
|
25
|
+
*
|
|
26
|
+
* Production-safe: callers must check `import.meta.hot` is defined before
|
|
27
|
+
* invoking this. The function assumes a live HMR connection.
|
|
28
|
+
*/
|
|
29
|
+
export const startWebSocketBridge = (store, hot, dispatch, maybeMessageSchema) => Effect.gen(function* () {
|
|
30
|
+
const connectionId = generateConnectionId();
|
|
31
|
+
const runtime = yield* Effect.runtime();
|
|
32
|
+
const maybeJsonMessageSchema = Option.map(maybeMessageSchema, schema => JSONSchema.make(schema));
|
|
33
|
+
const sendEvent = (event) => {
|
|
34
|
+
hot.send(EVENT_CHANNEL, {
|
|
35
|
+
maybeConnectionId: Option.some(connectionId),
|
|
36
|
+
event,
|
|
37
|
+
});
|
|
38
|
+
};
|
|
39
|
+
const sendResponse = (id, response) => {
|
|
40
|
+
const frame = { id, response };
|
|
41
|
+
hot.send(RESPONSE_CHANNEL, frame);
|
|
42
|
+
};
|
|
43
|
+
sendEvent(EventConnected({
|
|
44
|
+
runtime: RuntimeInfo.make({
|
|
45
|
+
connectionId,
|
|
46
|
+
url: window.location.href,
|
|
47
|
+
title: document.title,
|
|
48
|
+
maybeMessageSchema: maybeJsonMessageSchema,
|
|
49
|
+
}),
|
|
50
|
+
}));
|
|
51
|
+
const handleRequest = (id, request) => Effect.gen(function* () {
|
|
52
|
+
const response = yield* dispatchRequest(store, dispatch, maybeMessageSchema, request);
|
|
53
|
+
sendResponse(id, response);
|
|
54
|
+
});
|
|
55
|
+
const handleRequestFrame = (frame) => {
|
|
56
|
+
const decoded = S.decodeUnknownEither(RequestFrame)(frame);
|
|
57
|
+
Either.match(decoded, {
|
|
58
|
+
onLeft: error => {
|
|
59
|
+
console.warn('[foldkit:devTools] malformed request frame', error);
|
|
60
|
+
},
|
|
61
|
+
onRight: ({ id, maybeConnectionId, request }) => {
|
|
62
|
+
const isForUs = Option.exists(maybeConnectionId, targetId => targetId === connectionId);
|
|
63
|
+
if (!isForUs) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
Runtime.runFork(runtime)(handleRequest(id, request));
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
};
|
|
70
|
+
hot.on(REQUEST_CHANNEL, handleRequestFrame);
|
|
71
|
+
let hasEmittedDisconnect = false;
|
|
72
|
+
const emitDisconnect = () => {
|
|
73
|
+
if (hasEmittedDisconnect) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
hasEmittedDisconnect = true;
|
|
77
|
+
sendEvent(EventDisconnected({ connectionId }));
|
|
78
|
+
};
|
|
79
|
+
hot.dispose(() => {
|
|
80
|
+
emitDisconnect();
|
|
81
|
+
hot.off(REQUEST_CHANNEL, handleRequestFrame);
|
|
82
|
+
});
|
|
83
|
+
window.addEventListener('beforeunload', emitDisconnect, { once: true });
|
|
84
|
+
});
|
|
85
|
+
const dispatchRequest = (store, dispatch, maybeMessageSchema, request) => Match.value(request).pipe(Match.tagsExhaustive({
|
|
86
|
+
RequestGetModel: () => Effect.gen(function* () {
|
|
87
|
+
const state = yield* SubscriptionRef.get(store.stateRef);
|
|
88
|
+
const index = currentAbsoluteIndex(state.entries.length, state.startIndex);
|
|
89
|
+
return yield* pipe(index, store.getModelAtIndex, Effect.map(model => ResponseModel({ model: toInspectableValue(model) })), Effect.catchAllCause(cause => Effect.succeed(ResponseError({
|
|
90
|
+
reason: `Failed to read current Model: ${Cause.pretty(cause)}`,
|
|
91
|
+
}))));
|
|
92
|
+
}),
|
|
93
|
+
RequestListMessages: ({ limit, maybeSinceIndex }) => Effect.gen(function* () {
|
|
94
|
+
const state = yield* SubscriptionRef.get(store.stateRef);
|
|
95
|
+
const startAbsolute = Option.getOrElse(maybeSinceIndex, () => state.startIndex);
|
|
96
|
+
const startRelative = Math.max(0, startAbsolute - state.startIndex);
|
|
97
|
+
const sliced = pipe(state.entries, Array.drop(startRelative), Array.take(limit), Array.map((entry, sliceIndex) => toSerializedEntry(entry, state.startIndex + startRelative + sliceIndex)));
|
|
98
|
+
const totalCount = state.startIndex + state.entries.length;
|
|
99
|
+
const nextIndex = state.startIndex + startRelative + sliced.length;
|
|
100
|
+
const maybeNextIndex = OptionExt.when(nextIndex < totalCount, nextIndex);
|
|
101
|
+
return ResponseMessages({ entries: sliced, maybeNextIndex });
|
|
102
|
+
}),
|
|
103
|
+
RequestGetMessage: ({ index }) => Effect.gen(function* () {
|
|
104
|
+
const state = yield* SubscriptionRef.get(store.stateRef);
|
|
105
|
+
const relative = index - state.startIndex;
|
|
106
|
+
const maybeEntry = Array.get(state.entries, relative);
|
|
107
|
+
return yield* Option.match(maybeEntry, {
|
|
108
|
+
onNone: () => Effect.succeed(ResponseError({
|
|
109
|
+
reason: `No entry at index ${index} (have ${state.startIndex} to ${state.startIndex + state.entries.length - 1})`,
|
|
110
|
+
})),
|
|
111
|
+
onSome: entry => pipe({
|
|
112
|
+
modelBefore: store.getModelAtIndex(index - 1),
|
|
113
|
+
modelAfter: store.getModelAtIndex(index),
|
|
114
|
+
}, Effect.all, Effect.map(({ modelBefore, modelAfter }) => ResponseMessage({
|
|
115
|
+
entry: toSerializedEntry(entry, index),
|
|
116
|
+
modelBefore: toInspectableValue(modelBefore),
|
|
117
|
+
modelAfter: toInspectableValue(modelAfter),
|
|
118
|
+
})), Effect.catchAllCause(cause => Effect.succeed(ResponseError({
|
|
119
|
+
reason: `Failed to read Models around index ${index}: ${Cause.pretty(cause)}`,
|
|
120
|
+
})))),
|
|
121
|
+
});
|
|
122
|
+
}),
|
|
123
|
+
RequestListKeyframes: () => Effect.gen(function* () {
|
|
124
|
+
const state = yield* SubscriptionRef.get(store.stateRef);
|
|
125
|
+
const sortedKeyframeIndices = pipe(state.keyframes, HashMap.keys, Array.fromIterable, Array.sort(Order.number));
|
|
126
|
+
const indicesWithInit = Option.match(state.maybeInitModel, {
|
|
127
|
+
onNone: () => sortedKeyframeIndices,
|
|
128
|
+
onSome: () => [INIT_INDEX, ...sortedKeyframeIndices],
|
|
129
|
+
});
|
|
130
|
+
const keyframes = indicesWithInit.map(index => KeyframeInfo.make({ index }));
|
|
131
|
+
return ResponseKeyframes({ keyframes });
|
|
132
|
+
}),
|
|
133
|
+
RequestReplayToKeyframe: ({ keyframeIndex }) => pipe(Effect.gen(function* () {
|
|
134
|
+
yield* store.jumpTo(keyframeIndex);
|
|
135
|
+
const model = yield* store.getModelAtIndex(keyframeIndex);
|
|
136
|
+
return ResponseReplayed({ model: toInspectableValue(model) });
|
|
137
|
+
}), Effect.catchAllCause(cause => Effect.succeed(ResponseError({
|
|
138
|
+
reason: `Failed to replay to keyframe ${keyframeIndex}: ${Cause.pretty(cause)}`,
|
|
139
|
+
})))),
|
|
140
|
+
RequestResume: () => Effect.gen(function* () {
|
|
141
|
+
yield* store.resume;
|
|
142
|
+
return ResponseResumed();
|
|
143
|
+
}).pipe(Effect.catchAllCause(cause => Effect.succeed(ResponseError({
|
|
144
|
+
reason: `Failed to resume: ${Cause.pretty(cause)}`,
|
|
145
|
+
})))),
|
|
146
|
+
RequestDispatchMessage: ({ message }) => Option.match(maybeMessageSchema, {
|
|
147
|
+
onNone: () => Effect.succeed(ResponseError({
|
|
148
|
+
reason: 'Cannot dispatch: DevToolsConfig.Message not configured. Pass your Message Schema to enable dispatch.',
|
|
149
|
+
})),
|
|
150
|
+
onSome: messageSchema => Effect.gen(function* () {
|
|
151
|
+
const decodedMessage = yield* S.decodeUnknown(messageSchema)(message);
|
|
152
|
+
const stateBefore = yield* SubscriptionRef.get(store.stateRef);
|
|
153
|
+
const acceptedAtIndex = stateBefore.startIndex + stateBefore.entries.length;
|
|
154
|
+
yield* dispatch(decodedMessage);
|
|
155
|
+
return ResponseDispatched({ acceptedAtIndex });
|
|
156
|
+
}).pipe(Effect.catchAll(error => Effect.succeed(ResponseError({
|
|
157
|
+
reason: `Invalid Message: ${error instanceof Error ? error.message : String(error)}\n\nReceived (typeof ${typeof message}): ${JSON.stringify(message)}`,
|
|
158
|
+
})))),
|
|
159
|
+
}),
|
|
160
|
+
RequestListRuntimes: () => Effect.succeed(ResponseError({
|
|
161
|
+
reason: 'RequestListRuntimes is plugin-handled and should not reach the runtime bridge',
|
|
162
|
+
})),
|
|
163
|
+
}));
|
package/dist/runtime/public.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { makeProgram, run } from './runtime.js';
|
|
2
|
-
export type { RoutingConfig, CrashConfig, CrashContext, RoutingProgramConfigWithFlags, RoutingProgramConfig, ProgramConfigWithFlags, ProgramConfig, ProgramInit, RoutingProgramInit, MakeRuntimeReturn, Visibility, SlowViewContext, SlowViewConfig, FreezeModelConfig,
|
|
2
|
+
export type { RoutingConfig, CrashConfig, CrashContext, RoutingProgramConfigWithFlags, RoutingProgramConfig, ProgramConfigWithFlags, ProgramConfig, ProgramInit, RoutingProgramInit, MakeRuntimeReturn, Visibility, SlowViewContext, SlowViewConfig, FreezeModelConfig, DevToolsConfig, } from './runtime.js';
|
|
3
3
|
export { UrlRequest } from './urlRequest.js';
|
|
4
4
|
export type { Internal, External } from './urlRequest.js';
|
|
5
5
|
//# sourceMappingURL=public.d.ts.map
|
|
@@ -5,31 +5,42 @@ import { Url } from '../url/index.js';
|
|
|
5
5
|
import type { ManagedResources } from './managedResource.js';
|
|
6
6
|
import type { Subscriptions } from './subscription.js';
|
|
7
7
|
import { UrlRequest } from './urlRequest.js';
|
|
8
|
-
/** Position of the
|
|
9
|
-
export type
|
|
8
|
+
/** Position of the DevTools badge and panel on screen. */
|
|
9
|
+
export type DevToolsPosition = 'BottomRight' | 'BottomLeft' | 'TopRight' | 'TopLeft';
|
|
10
10
|
/** Controls when a feature is shown. */
|
|
11
11
|
export type Visibility = 'Development' | 'Always';
|
|
12
|
-
/** Controls
|
|
12
|
+
/** Controls DevTools interaction mode.
|
|
13
13
|
*
|
|
14
14
|
* - `'Inspect'`: Messages stream in and clicking a row shows its state snapshot without pausing the app.
|
|
15
15
|
* - `'TimeTravel'`: Clicking a row pauses the app at that historical state. Resume to continue.
|
|
16
16
|
*/
|
|
17
|
-
export type
|
|
17
|
+
export type DevToolsMode = 'Inspect' | 'TimeTravel';
|
|
18
18
|
/**
|
|
19
|
-
*
|
|
19
|
+
* DevTools configuration.
|
|
20
20
|
*
|
|
21
|
-
* Pass `false` to disable
|
|
21
|
+
* Pass `false` to disable DevTools entirely.
|
|
22
22
|
*
|
|
23
23
|
* - `show`: `'Development'` (default) enables in dev mode only, `'Always'` enables in all environments including production.
|
|
24
24
|
* - `position`: Where the badge and panel appear. Defaults to `'BottomRight'`.
|
|
25
25
|
* - `mode`: `'TimeTravel'` (default) enables full time-travel debugging. `'Inspect'` allows browsing state snapshots without pausing the app.
|
|
26
26
|
* - `banner`: Optional text shown as a banner at the top of the panel.
|
|
27
27
|
*/
|
|
28
|
-
export type
|
|
28
|
+
export type DevToolsConfig = false | Readonly<{
|
|
29
29
|
show?: Visibility;
|
|
30
|
-
position?:
|
|
31
|
-
mode?:
|
|
30
|
+
position?: DevToolsPosition;
|
|
31
|
+
mode?: DevToolsMode;
|
|
32
32
|
banner?: string;
|
|
33
|
+
/**
|
|
34
|
+
* The application's `Message` Schema. When provided and the running app
|
|
35
|
+
* is connected to the Foldkit DevTools MCP server, AI agents can dispatch
|
|
36
|
+
* Messages into the runtime. The Schema validates inbound dispatch
|
|
37
|
+
* payloads at the bridge boundary, and its JSON Schema representation is
|
|
38
|
+
* exposed to the MCP server so agents discover the available variants.
|
|
39
|
+
*
|
|
40
|
+
* Without this field, `RequestDispatchMessage` is rejected with an
|
|
41
|
+
* informative error.
|
|
42
|
+
*/
|
|
43
|
+
Message?: Schema.Schema<any, any, never>;
|
|
33
44
|
}>;
|
|
34
45
|
/** Context provided to the slow view callback when a view exceeds the time budget. */
|
|
35
46
|
export type SlowViewContext<Model, Message> = Readonly<{
|
|
@@ -114,7 +125,7 @@ type BaseProgramConfig<Model, Message, StreamDepsMap extends Schema.Struct<Schem
|
|
|
114
125
|
managedResources?: ManagedResources<Model, Message, ManagedResourceServices>;
|
|
115
126
|
/** Derives the document title from the current model. Called after every render. */
|
|
116
127
|
title?: (model: Model) => string;
|
|
117
|
-
|
|
128
|
+
devTools?: DevToolsConfig;
|
|
118
129
|
}>;
|
|
119
130
|
/** Configuration for `makeProgram` with flags and URL routing. */
|
|
120
131
|
export type RoutingProgramConfigWithFlags<Model, Message, StreamDepsMap extends Schema.Struct<Schema.Struct.Fields>, Flags, Resources = never, ManagedResourceServices = never> = BaseProgramConfig<Model, Message, StreamDepsMap, Resources, ManagedResourceServices> & Readonly<{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../src/runtime/runtime.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,OAAO,EACP,MAAM,EAGN,KAAK,EAEL,MAAM,EAMN,MAAM,EAKP,MAAM,QAAQ,CAAA;AAGf,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../src/runtime/runtime.ts"],"names":[],"mappings":"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"}
|
package/dist/runtime/runtime.js
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import { BrowserRuntime } from '@effect/platform-browser';
|
|
2
2
|
import { Array, Cause, Context, Effect, Either, Function, Layer, Match, Option, Predicate, Queue, Record, Ref, Runtime, Schema, Stream, SubscriptionRef, Tuple, pipe, } from 'effect';
|
|
3
3
|
import { h } from 'snabbdom';
|
|
4
|
-
import { createOverlay } from '../
|
|
5
|
-
import {
|
|
4
|
+
import { createOverlay } from '../devTools/overlay.js';
|
|
5
|
+
import { createDevToolsStore } from '../devTools/store.js';
|
|
6
|
+
import { startWebSocketBridge } from '../devTools/webSocketBridge.js';
|
|
6
7
|
import { fromString as urlFromString } from '../url/index.js';
|
|
7
8
|
import { patch, toVNode } from '../vdom.js';
|
|
8
9
|
import { addBfcacheRestoreListener, addNavigationEventListeners, } from './browserListeners.js';
|
|
9
10
|
import { defaultCrashView, noOpDispatch } from './crashUI.js';
|
|
10
11
|
import { deepFreeze } from './deepFreeze.js';
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
const
|
|
12
|
+
const DEFAULT_DEV_TOOLS_SHOW = 'Development';
|
|
13
|
+
const DEFAULT_DEV_TOOLS_POSITION = 'BottomRight';
|
|
14
|
+
const DEFAULT_DEV_TOOLS_MODE = 'TimeTravel';
|
|
14
15
|
const DEFAULT_SLOW_VIEW_SHOW = 'Development';
|
|
15
16
|
const DEFAULT_SLOW_VIEW_THRESHOLD_MS = 16;
|
|
16
17
|
const DEFAULT_FREEZE_MODEL_SHOW = 'Development';
|
|
@@ -29,7 +30,7 @@ const defaultSlowViewCallback = (context) => {
|
|
|
29
30
|
/** Effect service tag that provides message dispatching to the view layer. */
|
|
30
31
|
export class Dispatch extends Context.Tag('@foldkit/Dispatch')() {
|
|
31
32
|
}
|
|
32
|
-
const makeRuntime = ({ Model, flags: resolveFlags, init, update, view, subscriptions, container, routing: routingConfig, crash, slowView, freezeModel, resources, managedResources, title,
|
|
33
|
+
const makeRuntime = ({ Model, flags: resolveFlags, init, update, view, subscriptions, container, routing: routingConfig, crash, slowView, freezeModel, resources, managedResources, title, devTools, }) => {
|
|
33
34
|
const resolvedSlowView = pipe(slowView ?? {}, Option.liftPredicate(config => config !== false), Option.filter(config => Match.value(config.show ?? DEFAULT_SLOW_VIEW_SHOW).pipe(Match.when('Always', () => true), Match.when('Development', () => !!import.meta.hot), Match.exhaustive)), Option.map(config => ({
|
|
34
35
|
thresholdMs: config.thresholdMs ?? DEFAULT_SLOW_VIEW_THRESHOLD_MS,
|
|
35
36
|
onSlowView: config.onSlowView ?? defaultSlowViewCallback,
|
|
@@ -90,7 +91,7 @@ const makeRuntime = ({ Model, flags: resolveFlags, init, update, view, subscript
|
|
|
90
91
|
const maybeCurrentVNodeRef = yield* Ref.make(Option.none());
|
|
91
92
|
const currentMessageRef = yield* Ref.make(Option.none());
|
|
92
93
|
const maybeRuntimeRef = yield* Ref.make(Option.none());
|
|
93
|
-
const
|
|
94
|
+
const maybeDevToolsStoreRef = yield* Ref.make(Option.none());
|
|
94
95
|
/** `true` while a render is patching the DOM. Synchronous events
|
|
95
96
|
* that fire as a side-effect of DOM mutations (e.g. Chrome firing
|
|
96
97
|
* `blur` when a focused element is removed) call `dispatchSync`; if
|
|
@@ -108,7 +109,7 @@ const makeRuntime = ({ Model, flags: resolveFlags, init, update, view, subscript
|
|
|
108
109
|
const nextModel = maybeFreezeModel(nextModelRaw);
|
|
109
110
|
if (currentModel !== nextModel) {
|
|
110
111
|
yield* Ref.set(modelRef, nextModel);
|
|
111
|
-
const isPaused = yield* pipe(
|
|
112
|
+
const isPaused = yield* pipe(maybeDevToolsStoreRef, Ref.get, Effect.flatMap(Option.match({
|
|
112
113
|
onNone: () => Effect.succeed(false),
|
|
113
114
|
onSome: ({ stateRef }) => SubscriptionRef.get(stateRef).pipe(Effect.map(({ isPaused }) => isPaused)),
|
|
114
115
|
})));
|
|
@@ -123,8 +124,8 @@ const makeRuntime = ({ Model, flags: resolveFlags, init, update, view, subscript
|
|
|
123
124
|
yield* Effect.forEach(
|
|
124
125
|
/* eslint-disable-next-line @typescript-eslint/consistent-type-assertions */
|
|
125
126
|
commands, command => Effect.forkDaemon(command.effect.pipe(Effect.withSpan(command.name), provideAllResources, Effect.flatMap(enqueueMessage))));
|
|
126
|
-
const
|
|
127
|
-
yield* Option.match(
|
|
127
|
+
const maybeDevToolsStore = yield* Ref.get(maybeDevToolsStoreRef);
|
|
128
|
+
yield* Option.match(maybeDevToolsStore, {
|
|
128
129
|
onNone: () => Effect.void,
|
|
129
130
|
onSome: store => store.recordMessage(
|
|
130
131
|
/* eslint-disable-next-line @typescript-eslint/consistent-type-assertions */
|
|
@@ -201,14 +202,14 @@ const makeRuntime = ({ Model, flags: resolveFlags, init, update, view, subscript
|
|
|
201
202
|
const runtime = yield* Effect.runtime();
|
|
202
203
|
yield* Ref.set(maybeRuntimeRef, Option.some(runtime));
|
|
203
204
|
const isInIframe = window.self !== window.top;
|
|
204
|
-
const
|
|
205
|
-
position: config.position ??
|
|
206
|
-
mode: config.mode ??
|
|
205
|
+
const resolvedDevTools = pipe(devTools ?? {}, Option.liftPredicate(config => config !== false), Option.filter(config => Match.value(config.show ?? DEFAULT_DEV_TOOLS_SHOW).pipe(Match.when('Always', () => true), Match.when('Development', () => !!import.meta.hot && !isInIframe), Match.exhaustive)), Option.map(config => ({
|
|
206
|
+
position: config.position ?? DEFAULT_DEV_TOOLS_POSITION,
|
|
207
|
+
mode: config.mode ?? DEFAULT_DEV_TOOLS_MODE,
|
|
207
208
|
maybeBanner: Option.fromNullable(config.banner),
|
|
208
209
|
})));
|
|
209
|
-
if (Option.isSome(
|
|
210
|
-
const { position, mode, maybeBanner } =
|
|
211
|
-
const
|
|
210
|
+
if (Option.isSome(resolvedDevTools)) {
|
|
211
|
+
const { position, mode, maybeBanner } = resolvedDevTools.value;
|
|
212
|
+
const devToolsStore = yield* createDevToolsStore({
|
|
212
213
|
replay: (model, message) => maybeFreezeModel(
|
|
213
214
|
/* eslint-disable-next-line @typescript-eslint/consistent-type-assertions */
|
|
214
215
|
Tuple.getFirst(update(model, message))),
|
|
@@ -217,9 +218,17 @@ const makeRuntime = ({ Model, flags: resolveFlags, init, update, view, subscript
|
|
|
217
218
|
render(model, Option.none()),
|
|
218
219
|
getCurrentModel: Ref.get(modelRef),
|
|
219
220
|
});
|
|
220
|
-
yield* Ref.set(
|
|
221
|
-
yield*
|
|
222
|
-
yield* createOverlay(
|
|
221
|
+
yield* Ref.set(maybeDevToolsStoreRef, Option.some(devToolsStore));
|
|
222
|
+
yield* devToolsStore.recordInit(initModel, Array.map(initCommands, ({ name }) => name));
|
|
223
|
+
yield* createOverlay(devToolsStore, position, mode, maybeBanner);
|
|
224
|
+
if (import.meta.hot) {
|
|
225
|
+
const maybeMessageSchema = devTools !== undefined && devTools !== false
|
|
226
|
+
? Option.fromNullable(devTools.Message)
|
|
227
|
+
: Option.none();
|
|
228
|
+
yield* startWebSocketBridge(devToolsStore, import.meta.hot,
|
|
229
|
+
/* eslint-disable-next-line @typescript-eslint/consistent-type-assertions */
|
|
230
|
+
message => enqueueMessage(message), maybeMessageSchema);
|
|
231
|
+
}
|
|
223
232
|
}
|
|
224
233
|
yield* render(initModel, Option.none());
|
|
225
234
|
addBfcacheRestoreListener();
|
|
@@ -348,8 +357,8 @@ export function makeProgram(config) {
|
|
|
348
357
|
managedResources: config.managedResources,
|
|
349
358
|
}),
|
|
350
359
|
...(config.title && { title: config.title }),
|
|
351
|
-
...(Predicate.isNotUndefined(config.
|
|
352
|
-
|
|
360
|
+
...(Predicate.isNotUndefined(config.devTools) && {
|
|
361
|
+
devTools: config.devTools,
|
|
353
362
|
}),
|
|
354
363
|
};
|
|
355
364
|
/* eslint-disable @typescript-eslint/consistent-type-assertions */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "foldkit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.76.1",
|
|
4
4
|
"description": "A frontend framework for TypeScript, built on Effect, using The Elm Architecture",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -166,6 +166,10 @@
|
|
|
166
166
|
"./url": {
|
|
167
167
|
"types": "./dist/url/public.d.ts",
|
|
168
168
|
"import": "./dist/url/public.js"
|
|
169
|
+
},
|
|
170
|
+
"./devtools-protocol": {
|
|
171
|
+
"types": "./dist/devTools/public.d.ts",
|
|
172
|
+
"import": "./dist/devTools/public.js"
|
|
169
173
|
}
|
|
170
174
|
},
|
|
171
175
|
"files": [
|
|
File without changes
|
|
File without changes
|