@qping/plugin-bus 0.1.0 → 0.3.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.
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Hand-written TypeScript protocol types for the plugin message bus v3. These MUST stay
3
+ * byte-for-byte aligned with the C# types in MyTools.Protocol (see the canonical fixtures in
4
+ * MyTools.Protocol.Test/Fixtures/*.json). The drift-prevention self-check
5
+ * (fixtures-selfcheck.mjs) encodes/decodes those fixtures through these types.
6
+ *
7
+ * Field names are camelCase on the wire (System.Text.Json camelCase policy on the C# side).
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.
12
+ */
13
+ export declare const MessageKind: {
14
+ readonly Request: "request";
15
+ readonly Response: "response";
16
+ readonly Event: "event";
17
+ };
18
+ export type MessageKind = (typeof MessageKind)[keyof typeof MessageKind];
19
+ export declare const ErrorCode: {
20
+ readonly ProtocolMismatch: "ProtocolMismatch";
21
+ readonly HandshakeFailed: "HandshakeFailed";
22
+ readonly CapabilityNotDeclared: "CapabilityNotDeclared";
23
+ readonly CapabilityDenied: "CapabilityDenied";
24
+ readonly InvalidPayload: "InvalidPayload";
25
+ readonly MessageTooLarge: "MessageTooLarge";
26
+ readonly RouteNotFound: "RouteNotFound";
27
+ readonly RequestTimeout: "RequestTimeout";
28
+ readonly TooManyRequests: "TooManyRequests";
29
+ readonly TransportDisconnected: "TransportDisconnected";
30
+ readonly PluginUnavailable: "PluginUnavailable";
31
+ readonly InternalError: "InternalError";
32
+ readonly Cancelled: "Cancelled";
33
+ readonly RateLimited: "RateLimited";
34
+ };
35
+ export type ErrorCode = (typeof ErrorCode)[keyof typeof ErrorCode];
36
+ export declare const ProtocolVersion = "3.0";
37
+ export declare const EndpointIds: {
38
+ readonly NodeMain: "node-main";
39
+ readonly Host: "host";
40
+ };
41
+ export declare const Routes: {
42
+ readonly Bus: {
43
+ readonly Handshake: "bus.handshake";
44
+ readonly Ping: "bus.ping";
45
+ readonly Cancel: "bus.cancel";
46
+ readonly Subscribe: "bus.subscribe";
47
+ readonly Unsubscribe: "bus.unsubscribe";
48
+ };
49
+ readonly Prefix: {
50
+ readonly PluginCall: "plugin.call.";
51
+ readonly HostCall: "host.call.";
52
+ readonly PluginEvent: "plugin.event.";
53
+ readonly HostEvent: "host.event.";
54
+ readonly Diagnostics: "diagnostics.";
55
+ };
56
+ readonly PluginCall: {
57
+ readonly Initialize: "plugin.call.initialize";
58
+ readonly Search: "plugin.call.search";
59
+ readonly InvokeAction: "plugin.call.invokeAction";
60
+ };
61
+ readonly HostEvent: {
62
+ readonly Initialize: "host.event.initialize";
63
+ readonly Search: "host.event.search";
64
+ readonly Key: "host.event.key";
65
+ readonly DetailAction: "host.event.detailAction";
66
+ readonly LanguageChanged: "host.event.languageChanged";
67
+ readonly ThemeChanged: "host.event.themeChanged";
68
+ readonly InputActionCaptured: "host.event.inputActionCaptured";
69
+ };
70
+ };
71
+ export declare function pluginCallRoute(method: string): string;
72
+ export declare function hostCallRoute(method: string): string;
73
+ export declare function pluginEventRoute(subjectId: string): string;
74
+ export interface BusError {
75
+ code: ErrorCode;
76
+ message: string;
77
+ retryable: boolean;
78
+ details?: unknown;
79
+ }
80
+ /**
81
+ * The frozen Phase-1 envelope. All fields except correlationId/timeoutMs/error/payload are
82
+ * required; the optional ones are omitted on the wire when null.
83
+ */
84
+ export interface Envelope {
85
+ version: string;
86
+ id: string;
87
+ correlationId?: string | null;
88
+ traceId: string;
89
+ sessionId: string;
90
+ pluginId: string;
91
+ entryId: string;
92
+ endpointId: string;
93
+ kind: MessageKind;
94
+ route: string;
95
+ timeoutMs?: number | null;
96
+ payload?: unknown;
97
+ error?: BusError | null;
98
+ }
99
+ /** Omit null/undefined-valued keys to match the C# WhenWritingNull behavior. */
100
+ export declare function canonicalStringify(value: unknown): string;
101
+ /** Parse + re-canonicalize, returning the canonical JSON string (stable key order via JSON.stringify). */
102
+ export declare function canonicalize(json: string): string;
package/dist/protocol.mjs CHANGED
@@ -1,4 +1,69 @@
1
1
  // src/protocol.ts
2
+ var MessageKind = {
3
+ Request: "request",
4
+ Response: "response",
5
+ Event: "event"
6
+ };
7
+ var ErrorCode = {
8
+ ProtocolMismatch: "ProtocolMismatch",
9
+ HandshakeFailed: "HandshakeFailed",
10
+ CapabilityNotDeclared: "CapabilityNotDeclared",
11
+ CapabilityDenied: "CapabilityDenied",
12
+ InvalidPayload: "InvalidPayload",
13
+ MessageTooLarge: "MessageTooLarge",
14
+ RouteNotFound: "RouteNotFound",
15
+ RequestTimeout: "RequestTimeout",
16
+ TooManyRequests: "TooManyRequests",
17
+ TransportDisconnected: "TransportDisconnected",
18
+ PluginUnavailable: "PluginUnavailable",
19
+ InternalError: "InternalError",
20
+ Cancelled: "Cancelled",
21
+ RateLimited: "RateLimited"
22
+ };
23
+ var ProtocolVersion = "3.0";
24
+ var EndpointIds = {
25
+ NodeMain: "node-main",
26
+ Host: "host"
27
+ };
28
+ var Routes = {
29
+ Bus: {
30
+ Handshake: "bus.handshake",
31
+ Ping: "bus.ping",
32
+ Cancel: "bus.cancel",
33
+ Subscribe: "bus.subscribe",
34
+ Unsubscribe: "bus.unsubscribe"
35
+ },
36
+ Prefix: {
37
+ PluginCall: "plugin.call.",
38
+ HostCall: "host.call.",
39
+ PluginEvent: "plugin.event.",
40
+ HostEvent: "host.event.",
41
+ Diagnostics: "diagnostics."
42
+ },
43
+ PluginCall: {
44
+ Initialize: "plugin.call.initialize",
45
+ Search: "plugin.call.search",
46
+ InvokeAction: "plugin.call.invokeAction"
47
+ },
48
+ HostEvent: {
49
+ Initialize: "host.event.initialize",
50
+ Search: "host.event.search",
51
+ Key: "host.event.key",
52
+ DetailAction: "host.event.detailAction",
53
+ LanguageChanged: "host.event.languageChanged",
54
+ ThemeChanged: "host.event.themeChanged",
55
+ InputActionCaptured: "host.event.inputActionCaptured"
56
+ }
57
+ };
58
+ function pluginCallRoute(method) {
59
+ return method.startsWith(Routes.Prefix.PluginCall) ? method : `${Routes.Prefix.PluginCall}${method}`;
60
+ }
61
+ function hostCallRoute(method) {
62
+ return method.startsWith(Routes.Prefix.HostCall) ? method : `${Routes.Prefix.HostCall}${method}`;
63
+ }
64
+ function pluginEventRoute(subjectId) {
65
+ return subjectId.startsWith(Routes.Prefix.PluginEvent) ? subjectId : `${Routes.Prefix.PluginEvent}${subjectId}`;
66
+ }
2
67
  function canonicalStringify(value) {
3
68
  return JSON.stringify(stripNulls(value));
4
69
  }
@@ -19,6 +84,14 @@ function canonicalize(json) {
19
84
  return canonicalStringify(JSON.parse(json));
20
85
  }
21
86
  export {
87
+ EndpointIds,
88
+ ErrorCode,
89
+ MessageKind,
90
+ ProtocolVersion,
91
+ Routes,
22
92
  canonicalStringify,
23
- canonicalize
93
+ canonicalize,
94
+ hostCallRoute,
95
+ pluginCallRoute,
96
+ pluginEventRoute
24
97
  };
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Handler router for the Node SDK v3. Dispatches inbound plugin.call.* requests to registered
3
+ * handlers, auto-replies to bus.ping, and provides callHost() to invoke host.call.* capabilities
4
+ * and correlate their responses. Mirrors the C# MessageBus routing rules on the Node side.
5
+ */
6
+ import { type Envelope } from "./protocol.ts";
7
+ type Handler = (payload: unknown, context: {
8
+ sessionId: string;
9
+ }) => Promise<unknown> | unknown;
10
+ type Sender = (env: Envelope) => void;
11
+ export declare const DefaultHostCallTimeoutMs = 30000;
12
+ /** Remaining ms of the inbound plugin.call timeout, if currently inside a request. */
13
+ export declare function remainingTimeoutMs(): number | undefined;
14
+ export declare function resolveHostCallTimeoutMs(explicit?: number): number;
15
+ export declare class HandlerRouter {
16
+ private handlers;
17
+ private pendingHostCalls;
18
+ private pluginId;
19
+ private entryId;
20
+ private sessionId;
21
+ private endpointId;
22
+ /** Injected transport send fn; tests can override `router.send` directly. */
23
+ send: Sender;
24
+ constructor(deps: {
25
+ send: Sender;
26
+ });
27
+ /** Sets the bound identity stamped on outbound messages (after handshake). */
28
+ setIdentity(ids: {
29
+ pluginId: string;
30
+ entryId: string;
31
+ sessionId: string;
32
+ endpointId: string;
33
+ }): void;
34
+ handle(route: string, handler: Handler): void;
35
+ /** Dispatches an inbound request/response. Returns once handled. */
36
+ dispatch(env: Envelope): Promise<void>;
37
+ /** Calls a host.call.* capability and resolves with the response payload. */
38
+ callHost(route: string, payload: unknown, timeoutMs?: number): Promise<unknown>;
39
+ private handleHostResponse;
40
+ private responseFor;
41
+ private errorResponseFor;
42
+ }
43
+ export {};
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Returns whether every character in `pattern` occurs in `target` in the same order.
3
+ * Matching is case-insensitive and characters do not need to be adjacent.
4
+ *
5
+ * @example isSubsequence("gthb", "GitHub") // true
6
+ */
7
+ export declare function isSubsequence(pattern: string, target: string): boolean;
@@ -0,0 +1,17 @@
1
+ // src/search.ts
2
+ function isSubsequence(pattern, target) {
3
+ if (!pattern) return true;
4
+ if (!target) return false;
5
+ const needle = pattern.toLowerCase();
6
+ const haystack = target.toLowerCase();
7
+ let patternIndex = 0;
8
+ for (let targetIndex = 0; targetIndex < haystack.length && patternIndex < needle.length; targetIndex += 1) {
9
+ if (haystack[targetIndex] === needle[patternIndex]) {
10
+ patternIndex += 1;
11
+ }
12
+ }
13
+ return patternIndex === needle.length;
14
+ }
15
+ export {
16
+ isSubsequence
17
+ };
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Node-side named-pipe transport for the v3 message bus. Connects to the host's pipe server
3
+ * (created by NamedPipeTransport on the C# side), runs an incremental FrameDecoder read loop,
4
+ * and sends length-prefixed frames. Mirrors MyTools.Host.Transports.NamedPipeTransport.
5
+ */
6
+ import { type Envelope } from "./protocol.ts";
7
+ type MessageHandler = (env: Envelope) => void;
8
+ type DisconnectHandler = () => void;
9
+ export declare class NodeTransport {
10
+ private socket;
11
+ private messageHandlers;
12
+ private disconnectHandlers;
13
+ private closed;
14
+ onMessage(handler: MessageHandler): () => void;
15
+ onDisconnect(handler: DisconnectHandler): void;
16
+ get isConnected(): boolean;
17
+ /** Connects to a Windows named pipe (\\.\pipe\<name>). */
18
+ connect(pipePath: string): Promise<void>;
19
+ /** Serializes an envelope to a length-prefixed frame and writes it. */
20
+ send(env: Envelope): void;
21
+ close(): Promise<void>;
22
+ private handleDisconnect;
23
+ }
24
+ export {};
@@ -0,0 +1,35 @@
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
+ import { mytoolsI18n } from "./i18n.ts";
9
+ import type { MyToolsThemePayload } from "./webTypes.ts";
10
+ export { mytoolsI18n } from "./i18n.ts";
11
+ export { renderHotkeyKeycaps } from "./hotkeyKeycaps.ts";
12
+ export { HostEvents } from "./webTypes.ts";
13
+ export type { MyToolsHostActionDefinition, MyToolsHostDetailActionPayload, MyToolsHostInitializePayload, MyToolsHostKeyPayload, MyToolsHostSearchPayload, MyToolsInputActionCapturedPayload, MyToolsLanguageChangedPayload, MyToolsThemeChangedPayload, MyToolsThemePayload, } from "./webTypes.ts";
14
+ export interface WebBusClient {
15
+ /** Sends plugin.call.<method>. Bare names are prefixed; full routes are left as-is. */
16
+ call<T = unknown>(method: string, payload?: unknown, timeoutMs?: number): Promise<T>;
17
+ on<T = unknown>(route: string, handler: (payload: T) => void): () => void;
18
+ i18n: typeof mytoolsI18n;
19
+ theme: typeof mytoolsTheme;
20
+ close(): void;
21
+ }
22
+ declare const mytoolsTheme: {
23
+ current: string;
24
+ apply(payload: MyToolsThemePayload): void;
25
+ };
26
+ /**
27
+ * Creates a Web bus client. Registers the message listener immediately so host
28
+ * events that arrive before `on()` are buffered and replayed.
29
+ *
30
+ * Handshake is required before call(). Page scripts are bundled as IIFE, so this
31
+ * function is synchronous; handshake runs in the background and gates call().
32
+ */
33
+ export declare function createWebBusClient(options?: {
34
+ timeoutMs?: number;
35
+ }): WebBusClient;