@qping/plugin-bus 0.1.0 → 0.2.0

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/src/protocol.ts CHANGED
@@ -6,25 +6,90 @@
6
6
  *
7
7
  * Field names are camelCase on the wire (System.Text.Json camelCase policy on the C# side).
8
8
  * Null fields are omitted on the wire (WhenWritingNull).
9
+ *
10
+ * Runtime constants mirror MyTools.Protocol (MessageKindWire, Routes, EndpointIds,
11
+ * ProtocolVersion.CurrentWire). Do not re-hardcode those strings in SDK source.
9
12
  */
10
13
 
11
- export type MessageKind = "request" | "response" | "event";
14
+ export const MessageKind = {
15
+ Request: "request",
16
+ Response: "response",
17
+ Event: "event",
18
+ } as const;
19
+ export type MessageKind = (typeof MessageKind)[keyof typeof MessageKind];
20
+
21
+ export const ErrorCode = {
22
+ ProtocolMismatch: "ProtocolMismatch",
23
+ HandshakeFailed: "HandshakeFailed",
24
+ CapabilityNotDeclared: "CapabilityNotDeclared",
25
+ CapabilityDenied: "CapabilityDenied",
26
+ InvalidPayload: "InvalidPayload",
27
+ MessageTooLarge: "MessageTooLarge",
28
+ RouteNotFound: "RouteNotFound",
29
+ RequestTimeout: "RequestTimeout",
30
+ TooManyRequests: "TooManyRequests",
31
+ TransportDisconnected: "TransportDisconnected",
32
+ PluginUnavailable: "PluginUnavailable",
33
+ InternalError: "InternalError",
34
+ Cancelled: "Cancelled",
35
+ RateLimited: "RateLimited",
36
+ } as const;
37
+ export type ErrorCode = (typeof ErrorCode)[keyof typeof ErrorCode];
38
+
39
+ export const ProtocolVersion = "3.0";
40
+
41
+ export const EndpointIds = {
42
+ NodeMain: "node-main",
43
+ Host: "host",
44
+ } as const;
45
+
46
+ export const Routes = {
47
+ Bus: {
48
+ Handshake: "bus.handshake",
49
+ Ping: "bus.ping",
50
+ Cancel: "bus.cancel",
51
+ Subscribe: "bus.subscribe",
52
+ Unsubscribe: "bus.unsubscribe",
53
+ },
54
+ Prefix: {
55
+ PluginCall: "plugin.call.",
56
+ HostCall: "host.call.",
57
+ PluginEvent: "plugin.event.",
58
+ HostEvent: "host.event.",
59
+ Diagnostics: "diagnostics.",
60
+ },
61
+ PluginCall: {
62
+ Initialize: "plugin.call.initialize",
63
+ Search: "plugin.call.search",
64
+ InvokeAction: "plugin.call.invokeAction",
65
+ },
66
+ HostEvent: {
67
+ Initialize: "host.event.initialize",
68
+ Search: "host.event.search",
69
+ Key: "host.event.key",
70
+ LanguageChanged: "host.event.languageChanged",
71
+ ThemeChanged: "host.event.themeChanged",
72
+ InputActionCaptured: "host.event.inputActionCaptured",
73
+ },
74
+ } as const;
12
75
 
13
- export type ErrorCode =
14
- | "ProtocolMismatch"
15
- | "HandshakeFailed"
16
- | "CapabilityNotDeclared"
17
- | "CapabilityDenied"
18
- | "InvalidPayload"
19
- | "MessageTooLarge"
20
- | "RouteNotFound"
21
- | "RequestTimeout"
22
- | "TooManyRequests"
23
- | "TransportDisconnected"
24
- | "PluginUnavailable"
25
- | "InternalError"
26
- | "Cancelled"
27
- | "RateLimited";
76
+ export function pluginCallRoute(method: string): string {
77
+ return method.startsWith(Routes.Prefix.PluginCall)
78
+ ? method
79
+ : `${Routes.Prefix.PluginCall}${method}`;
80
+ }
81
+
82
+ export function hostCallRoute(method: string): string {
83
+ return method.startsWith(Routes.Prefix.HostCall)
84
+ ? method
85
+ : `${Routes.Prefix.HostCall}${method}`;
86
+ }
87
+
88
+ export function pluginEventRoute(subjectId: string): string {
89
+ return subjectId.startsWith(Routes.Prefix.PluginEvent)
90
+ ? subjectId
91
+ : `${Routes.Prefix.PluginEvent}${subjectId}`;
92
+ }
28
93
 
29
94
  export interface BusError {
30
95
  code: ErrorCode;
@@ -38,7 +103,7 @@ export interface BusError {
38
103
  * required; the optional ones are omitted on the wire when null.
39
104
  */
40
105
  export interface Envelope {
41
- version: string; // e.g. "3.0"
106
+ version: string; // e.g. ProtocolVersion
42
107
  id: string;
43
108
  correlationId?: string | null;
44
109
  traceId: string;
package/src/router.ts CHANGED
@@ -4,8 +4,17 @@
4
4
  * and correlate their responses. Mirrors the C# MessageBus routing rules on the Node side.
5
5
  */
6
6
 
7
+ import { AsyncLocalStorage } from "node:async_hooks";
7
8
  import { randomBytes } from "node:crypto";
8
- import type { Envelope, BusError } from "./protocol.ts";
9
+ import {
10
+ type Envelope,
11
+ type BusError,
12
+ EndpointIds,
13
+ ErrorCode,
14
+ MessageKind,
15
+ ProtocolVersion,
16
+ Routes,
17
+ } from "./protocol.ts";
9
18
 
10
19
  type Handler = (payload: unknown) => Promise<unknown> | unknown;
11
20
  type Sender = (env: Envelope) => void;
@@ -16,13 +25,46 @@ interface PendingHostCall {
16
25
  route: string;
17
26
  }
18
27
 
28
+ type RequestScope = { deadlineMs: number | null };
29
+
30
+ const requestScope = new AsyncLocalStorage<RequestScope>();
31
+
32
+ export const DefaultHostCallTimeoutMs = 30_000;
33
+
34
+ /** Remaining ms of the inbound plugin.call timeout, if currently inside a request. */
35
+ export function remainingTimeoutMs(): number | undefined {
36
+ const scope = requestScope.getStore();
37
+ if (!scope || scope.deadlineMs == null) {
38
+ return undefined;
39
+ }
40
+
41
+ return scope.deadlineMs - Date.now();
42
+ }
43
+
44
+ export function resolveHostCallTimeoutMs(explicit?: number): number {
45
+ const remaining = remainingTimeoutMs();
46
+ if (explicit != null) {
47
+ return remaining == null ? explicit : Math.min(explicit, remaining);
48
+ }
49
+
50
+ return remaining ?? DefaultHostCallTimeoutMs;
51
+ }
52
+
53
+ function deadlineFromTimeoutMs(timeoutMs: number | null | undefined): number | null {
54
+ if (typeof timeoutMs !== "number" || timeoutMs <= 0) {
55
+ return null;
56
+ }
57
+
58
+ return Date.now() + timeoutMs;
59
+ }
60
+
19
61
  export class HandlerRouter {
20
62
  private handlers = new Map<string, Handler>();
21
63
  private pendingHostCalls = new Map<string, PendingHostCall>();
22
64
  private pluginId = "p";
23
65
  private entryId = "e";
24
66
  private sessionId = "s";
25
- private endpointId = "node-main";
67
+ private endpointId: string = EndpointIds.NodeMain;
26
68
 
27
69
  /** Injected transport send fn; tests can override `router.send` directly. */
28
70
  send: Sender;
@@ -45,48 +87,54 @@ export class HandlerRouter {
45
87
 
46
88
  /** Dispatches an inbound request/response. Returns once handled. */
47
89
  async dispatch(env: Envelope): Promise<void> {
48
- if (env.kind === "response") {
90
+ if (env.kind === MessageKind.Response) {
49
91
  this.handleHostResponse(env);
50
92
  return;
51
93
  }
52
- if (env.kind !== "request") return;
94
+ if (env.kind !== MessageKind.Request) return;
53
95
 
54
96
  // bus.ping is always auto-replied; it does not occupy a handler slot.
55
- if (env.route === "bus.ping") {
97
+ if (env.route === Routes.Bus.Ping) {
56
98
  this.send(this.responseFor(env, { ok: true }));
57
99
  return;
58
100
  }
59
101
 
60
102
  const handler = this.handlers.get(env.route);
61
103
  if (!handler) {
62
- this.send(this.errorResponseFor(env, "RouteNotFound", `route '${env.route}' has no handler`));
104
+ this.send(this.errorResponseFor(env, ErrorCode.RouteNotFound, `route '${env.route}' has no handler`));
63
105
  return;
64
106
  }
65
107
 
108
+ const deadlineMs = deadlineFromTimeoutMs(env.timeoutMs);
66
109
  try {
67
- const result = await handler(env.payload);
110
+ const result = await requestScope.run({ deadlineMs }, () => handler(env.payload));
68
111
  this.send(this.responseFor(env, result ?? {}));
69
112
  } catch (err) {
70
113
  const message = err instanceof Error ? err.message : String(err);
71
- this.send(this.errorResponseFor(env, "InternalError", message));
114
+ this.send(this.errorResponseFor(env, ErrorCode.InternalError, message));
72
115
  }
73
116
  }
74
117
 
75
118
  /** Calls a host.call.* capability and resolves with the response payload. */
76
- callHost(route: string, payload: unknown, timeoutMs = 30000): Promise<unknown> {
119
+ callHost(route: string, payload: unknown, timeoutMs?: number): Promise<unknown> {
120
+ const effectiveTimeoutMs = resolveHostCallTimeoutMs(timeoutMs);
121
+ if (effectiveTimeoutMs <= 0) {
122
+ return Promise.reject(new Error(`host call ${route} timed out (no time remaining)`));
123
+ }
124
+
77
125
  return new Promise((resolve, reject) => {
78
126
  const id = randomBytesHex();
79
127
  const req: Envelope = {
80
- version: "3.0",
128
+ version: ProtocolVersion,
81
129
  id,
82
130
  traceId: id,
83
131
  sessionId: this.sessionId,
84
132
  pluginId: this.pluginId,
85
133
  entryId: this.entryId,
86
134
  endpointId: this.endpointId,
87
- kind: "request",
135
+ kind: MessageKind.Request,
88
136
  route,
89
- timeoutMs,
137
+ timeoutMs: effectiveTimeoutMs,
90
138
  payload,
91
139
  };
92
140
  const pending: PendingHostCall = { resolve, reject, route };
@@ -94,10 +142,9 @@ export class HandlerRouter {
94
142
  const timer = setTimeout(() => {
95
143
  if (this.pendingHostCalls.has(id)) {
96
144
  this.pendingHostCalls.delete(id);
97
- reject(new Error(`host call ${route} timed out after ${timeoutMs}ms`));
145
+ reject(new Error(`host call ${route} timed out after ${effectiveTimeoutMs}ms`));
98
146
  }
99
- }, timeoutMs);
100
- // Clear the timer when settled.
147
+ }, effectiveTimeoutMs);
101
148
  const origResolve = pending.resolve;
102
149
  const origReject = pending.reject;
103
150
  pending.resolve = (v) => { clearTimeout(timer); origResolve(v); };
@@ -120,7 +167,7 @@ export class HandlerRouter {
120
167
 
121
168
  private responseFor(req: Envelope, payload: unknown): Envelope {
122
169
  return {
123
- version: "3.0",
170
+ version: ProtocolVersion,
124
171
  id: randomBytesHex(),
125
172
  correlationId: req.id,
126
173
  traceId: req.traceId,
@@ -128,13 +175,13 @@ export class HandlerRouter {
128
175
  pluginId: req.pluginId,
129
176
  entryId: req.entryId,
130
177
  endpointId: this.endpointId,
131
- kind: "response",
178
+ kind: MessageKind.Response,
132
179
  route: req.route,
133
180
  payload,
134
181
  };
135
182
  }
136
183
 
137
- private errorResponseFor(req: Envelope, code: string, message: string): Envelope {
184
+ private errorResponseFor(req: Envelope, code: BusError["code"], message: string): Envelope {
138
185
  return {
139
186
  ...this.responseFor(req, null),
140
187
  payload: undefined,
package/src/transport.ts CHANGED
@@ -17,8 +17,9 @@ export class NodeTransport {
17
17
  private disconnectHandlers = new Set<DisconnectHandler>();
18
18
  private closed = false;
19
19
 
20
- onMessage(handler: MessageHandler): void {
20
+ onMessage(handler: MessageHandler): () => void {
21
21
  this.messageHandlers.add(handler);
22
+ return () => { this.messageHandlers.delete(handler); };
22
23
  }
23
24
 
24
25
  onDisconnect(handler: DisconnectHandler): void {
@@ -0,0 +1,285 @@
1
+ /**
2
+ * v3 Web SDK: speaks protocol envelopes over chrome.webview.postMessage.
3
+ * Host stamps identity; the page does not supply plugin/entry/session ids.
4
+ *
5
+ * Connections start with bus.handshake. Subsequent plugin.call.* envelopes use the
6
+ * negotiated version. call("refresh") is sent as plugin.call.refresh.
7
+ */
8
+
9
+ import { mytoolsI18n } from "./i18n.ts";
10
+ import {
11
+ type Envelope,
12
+ type BusError,
13
+ MessageKind,
14
+ ProtocolVersion,
15
+ Routes,
16
+ pluginCallRoute,
17
+ } from "./protocol.ts";
18
+ import type { MyToolsThemePayload } from "./webTypes.ts";
19
+
20
+ export { mytoolsI18n } from "./i18n.ts";
21
+ export { HostEvents } from "./webTypes.ts";
22
+ export type {
23
+ MyToolsHostInitializePayload,
24
+ MyToolsHostKeyPayload,
25
+ MyToolsHostSearchPayload,
26
+ MyToolsInputActionCapturedPayload,
27
+ MyToolsLanguageChangedPayload,
28
+ MyToolsThemeChangedPayload,
29
+ MyToolsThemePayload,
30
+ } from "./webTypes.ts";
31
+
32
+ type Pending = {
33
+ resolve: (value: unknown) => void;
34
+ reject: (err: Error) => void;
35
+ };
36
+
37
+ export interface WebBusClient {
38
+ /** Sends plugin.call.<method>. Bare names are prefixed; full routes are left as-is. */
39
+ call<T = unknown>(method: string, payload?: unknown, timeoutMs?: number): Promise<T>;
40
+ on<T = unknown>(route: string, handler: (payload: T) => void): () => void;
41
+ i18n: typeof mytoolsI18n;
42
+ theme: typeof mytoolsTheme;
43
+ close(): void;
44
+ }
45
+
46
+ const HandshakeTimeoutMs = 8_000;
47
+
48
+ const mytoolsTheme = {
49
+ current: "dark",
50
+ apply(payload: MyToolsThemePayload): void {
51
+ if (typeof payload.theme === "string") {
52
+ this.current = payload.theme;
53
+ }
54
+ const root = typeof document !== "undefined" ? document.documentElement : null;
55
+ if (!root) {
56
+ return;
57
+ }
58
+ if (typeof payload.theme === "string") {
59
+ root.setAttribute("data-theme", payload.theme);
60
+ root.style.colorScheme = payload.theme;
61
+ }
62
+ if (payload.themeTokens) {
63
+ for (const [key, value] of Object.entries(payload.themeTokens)) {
64
+ if (typeof value === "string") {
65
+ root.style.setProperty(key, value);
66
+ }
67
+ }
68
+ }
69
+ },
70
+ };
71
+
72
+ function hasWebView(): boolean {
73
+ return !!(typeof window !== "undefined" && (window as any).chrome?.webview);
74
+ }
75
+
76
+ function post(env: Envelope): void {
77
+ if (!hasWebView()) throw new Error("chrome.webview is not available");
78
+ (window as any).chrome.webview.postMessage(env);
79
+ }
80
+
81
+ function randomId(): string {
82
+ const bytes = new Uint8Array(16);
83
+ crypto.getRandomValues(bytes);
84
+ return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
85
+ }
86
+
87
+ function parseMessage(data: unknown): Envelope | null {
88
+ if (typeof data === "string") {
89
+ try {
90
+ data = JSON.parse(data);
91
+ } catch {
92
+ return null;
93
+ }
94
+ }
95
+ if (!data || typeof data !== "object") return null;
96
+ const env = data as Envelope;
97
+ if (typeof env.kind !== "string" || typeof env.route !== "string") return null;
98
+ return env;
99
+ }
100
+
101
+ function applyHostSideEffects(env: Envelope): void {
102
+ const payload = env.payload;
103
+ if (!payload || typeof payload !== "object") return;
104
+ if (env.route === Routes.HostEvent.Initialize) {
105
+ mytoolsI18n.configure(payload);
106
+ mytoolsI18n.apply();
107
+ mytoolsTheme.apply(payload as MyToolsThemePayload);
108
+ } else if (env.route === Routes.HostEvent.LanguageChanged) {
109
+ mytoolsI18n.configure(payload);
110
+ mytoolsI18n.apply();
111
+ } else if (env.route === Routes.HostEvent.ThemeChanged) {
112
+ mytoolsTheme.apply(payload as MyToolsThemePayload);
113
+ }
114
+ }
115
+
116
+ function negotiatedVersionFrom(payload: unknown): string {
117
+ if (payload && typeof payload === "object" && "negotiatedVersion" in payload) {
118
+ const value = (payload as { negotiatedVersion?: unknown }).negotiatedVersion;
119
+ if (typeof value === "string" && value.length > 0) return value;
120
+ }
121
+ return ProtocolVersion;
122
+ }
123
+
124
+ /**
125
+ * Creates a Web bus client. Registers the message listener immediately so host
126
+ * events that arrive before `on()` are buffered and replayed.
127
+ *
128
+ * Handshake is required before call(). Page scripts are bundled as IIFE, so this
129
+ * function is synchronous; handshake runs in the background and gates call().
130
+ */
131
+ export function createWebBusClient(options?: {
132
+ timeoutMs?: number;
133
+ }): WebBusClient {
134
+ const pending = new Map<string, Pending>();
135
+ const eventHandlers = new Set<(env: Envelope) => void>();
136
+ const lastByRoute = new Map<string, Envelope>();
137
+ const defaultTimeout = options?.timeoutMs ?? 30_000;
138
+ let wireVersion = ProtocolVersion;
139
+ let handshakeError: Error | null = null;
140
+
141
+ const onMessage = (event: MessageEvent) => {
142
+ const env = parseMessage(event.data);
143
+ if (!env) return;
144
+ if (env.kind === MessageKind.Response && env.correlationId) {
145
+ const p = pending.get(env.correlationId);
146
+ if (!p) return;
147
+ pending.delete(env.correlationId);
148
+ if (env.error) {
149
+ p.reject(new Error(`${(env.error as BusError).code}: ${(env.error as BusError).message}`));
150
+ } else {
151
+ p.resolve(env.payload);
152
+ }
153
+ return;
154
+ }
155
+ if (env.kind === MessageKind.Event) {
156
+ lastByRoute.set(env.route, env);
157
+ applyHostSideEffects(env);
158
+ for (const h of eventHandlers) h(env);
159
+ }
160
+ };
161
+
162
+ let handshakeDone: Promise<void> = Promise.resolve();
163
+ if (hasWebView()) {
164
+ (window as any).chrome.webview.addEventListener("message", onMessage);
165
+ handshakeDone = handshake(HandshakeTimeoutMs)
166
+ .then((version) => {
167
+ wireVersion = version;
168
+ })
169
+ .catch((err: unknown) => {
170
+ handshakeError = err instanceof Error ? err : new Error(String(err));
171
+ throw handshakeError;
172
+ });
173
+ }
174
+
175
+ function ensureHandshaken(): Promise<void> {
176
+ if (handshakeError) return Promise.reject(handshakeError);
177
+ return handshakeDone.then(() => {
178
+ if (handshakeError) throw handshakeError;
179
+ });
180
+ }
181
+
182
+ function call<T = unknown>(method: string, payload?: unknown, timeoutMs = defaultTimeout): Promise<T> {
183
+ const route = pluginCallRoute(method);
184
+ return ensureHandshaken().then(
185
+ () =>
186
+ new Promise<T>((resolve, reject) => {
187
+ const id = randomId();
188
+ const timer = window.setTimeout(() => {
189
+ pending.delete(id);
190
+ reject(new Error(`request timed out: ${route}`));
191
+ }, timeoutMs);
192
+ pending.set(id, {
193
+ resolve: (v) => {
194
+ window.clearTimeout(timer);
195
+ resolve(v as T);
196
+ },
197
+ reject: (e) => {
198
+ window.clearTimeout(timer);
199
+ reject(e);
200
+ },
201
+ });
202
+ post({
203
+ version: wireVersion,
204
+ id,
205
+ traceId: id,
206
+ sessionId: "",
207
+ pluginId: "",
208
+ entryId: "",
209
+ endpointId: "",
210
+ kind: MessageKind.Request,
211
+ route,
212
+ timeoutMs,
213
+ payload: payload ?? {},
214
+ });
215
+ }),
216
+ );
217
+ }
218
+
219
+ function subscribe(handler: (env: Envelope) => void): () => void {
220
+ eventHandlers.add(handler);
221
+ for (const env of lastByRoute.values()) handler(env);
222
+ return () => {
223
+ eventHandlers.delete(handler);
224
+ };
225
+ }
226
+
227
+ return {
228
+ call,
229
+ on: (route, handler) =>
230
+ subscribe((env) => {
231
+ if (env.route === route) handler((env.payload ?? {}) as never);
232
+ }),
233
+ i18n: mytoolsI18n,
234
+ theme: mytoolsTheme,
235
+ close: () => {
236
+ if (hasWebView()) {
237
+ (window as any).chrome.webview.removeEventListener("message", onMessage);
238
+ }
239
+ pending.clear();
240
+ eventHandlers.clear();
241
+ lastByRoute.clear();
242
+ },
243
+ };
244
+ }
245
+
246
+ function handshake(timeoutMs: number): Promise<string> {
247
+ const id = randomId();
248
+ return new Promise((resolve, reject) => {
249
+ const timer = window.setTimeout(() => {
250
+ cleanup();
251
+ reject(new Error("bus.handshake timed out"));
252
+ }, timeoutMs);
253
+
254
+ const onMessage = (event: MessageEvent) => {
255
+ const env = parseMessage(event.data);
256
+ if (!env || env.kind !== MessageKind.Response || env.correlationId !== id) return;
257
+ cleanup();
258
+ if (env.error) {
259
+ reject(new Error(`${env.error.code}: ${env.error.message}`));
260
+ } else {
261
+ resolve(negotiatedVersionFrom(env.payload));
262
+ }
263
+ };
264
+
265
+ const cleanup = () => {
266
+ window.clearTimeout(timer);
267
+ (window as any).chrome.webview.removeEventListener("message", onMessage);
268
+ };
269
+
270
+ (window as any).chrome.webview.addEventListener("message", onMessage);
271
+ post({
272
+ version: ProtocolVersion,
273
+ id,
274
+ traceId: id,
275
+ sessionId: "",
276
+ pluginId: "",
277
+ entryId: "",
278
+ endpointId: "web",
279
+ kind: MessageKind.Request,
280
+ route: Routes.Bus.Handshake,
281
+ timeoutMs,
282
+ payload: { version: ProtocolVersion, supportedVersions: [ProtocolVersion] },
283
+ });
284
+ });
285
+ }
@@ -0,0 +1,52 @@
1
+ import { Routes } from "./protocol.ts";
2
+
3
+ export interface MyToolsHostInitializePayload {
4
+ protocolVersion: string;
5
+ pluginId: string;
6
+ version?: string;
7
+ itemId: string;
8
+ query: string;
9
+ keyword: string;
10
+ initialState: unknown;
11
+ locale: string;
12
+ fallbackLocale: string;
13
+ translationRevision: string;
14
+ messages: Record<string, string>;
15
+ theme?: string;
16
+ themeTokens?: Record<string, string>;
17
+ }
18
+
19
+ export interface MyToolsLanguageChangedPayload {
20
+ locale: string;
21
+ fallbackLocale: string;
22
+ translationRevision: string;
23
+ messages: Record<string, string>;
24
+ }
25
+
26
+ export interface MyToolsThemeChangedPayload {
27
+ theme: string;
28
+ themeTokens: Record<string, string>;
29
+ }
30
+
31
+ export interface MyToolsHostSearchPayload {
32
+ query: string;
33
+ }
34
+
35
+ export interface MyToolsHostKeyPayload {
36
+ key: string;
37
+ }
38
+
39
+ export interface MyToolsInputActionCapturedPayload {
40
+ requestId: string;
41
+ cancelled?: boolean;
42
+ kind?: "hotkey" | "mouse";
43
+ hotKey?: string | null;
44
+ mouseButton?: string | null;
45
+ }
46
+
47
+ export interface MyToolsThemePayload {
48
+ theme?: string;
49
+ themeTokens?: Record<string, string>;
50
+ }
51
+
52
+ export const HostEvents = Routes.HostEvent;
@@ -1 +0,0 @@
1
- export * from "../src/bootstrap.ts";
@@ -1 +0,0 @@
1
- export * from "../src/protocol.ts";
package/dist/server.d.mts DELETED
@@ -1 +0,0 @@
1
- export * from "../src/server.ts";