signalk-plotterext-bus 0.7.2 → 0.7.3

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 CHANGED
@@ -114,6 +114,7 @@ may change.
114
114
  | `routes.badRef` | `route.show(ref)` was given a ref that resolves to no stored route. |
115
115
  | `routes.badRequest` | Malformed params — e.g. fewer than two points, a non-numeric `position`, or non-string name/description metadata. |
116
116
  | `routes.saveFailed` | The host attempted to persist the route but the server rejected the write. Distinct from `saveCancelled`. |
117
+ | `routes.deleteFailed` | The host attempted to delete the stored route but the server rejected the request. |
117
118
  | `routes.saveCancelled` | The user dismissed the host's save dialog without saving. |
118
119
  | `routes.notSupported` | The host does not implement this route operation. |
119
120
 
@@ -5,7 +5,7 @@ import {
5
5
  RPC_ERRORS,
6
6
  RpcError,
7
7
  matchesAny
8
- } from "./chunk-VBS4I6NW.js";
8
+ } from "./chunk-LVGWVV2W.js";
9
9
 
10
10
  // src/host.ts
11
11
  var HostConnection = class {
@@ -103,4 +103,4 @@ var HostConnection = class {
103
103
  export {
104
104
  HostConnection
105
105
  };
106
- //# sourceMappingURL=chunk-W5VF4P6R.js.map
106
+ //# sourceMappingURL=chunk-KZCRGKFF.js.map
@@ -315,4 +315,4 @@ export {
315
315
  messagePort,
316
316
  BusEndpoint
317
317
  };
318
- //# sourceMappingURL=chunk-VBS4I6NW.js.map
318
+ //# sourceMappingURL=chunk-LVGWVV2W.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/protocol.ts","../src/wildcard.ts","../src/codec.ts","../src/port.ts","../src/endpoint.ts"],"sourcesContent":["/**\n * Wire protocol types and constants for the Signal K plotter extension bus.\n *\n * The wire format is JSON-RPC 2.0 (https://www.jsonrpc.org/specification)\n * inside a routing envelope: `{ bus: \"plotterExt/1\", msg: <JSON-RPC object> }`.\n * Calls are JSON-RPC requests; events are JSON-RPC notifications whose\n * `method` is a hierarchical dot-separated event name.\n */\n\nexport const BUS_ID = 'plotterExt/1'\n\nexport type JsonRpcId = string | number\n\nexport interface JsonRpcRequest {\n jsonrpc: '2.0'\n id: JsonRpcId\n method: string\n params?: unknown\n}\n\nexport interface JsonRpcNotification {\n jsonrpc: '2.0'\n method: string\n params?: unknown\n}\n\nexport interface JsonRpcErrorObject {\n code: number\n message: string\n data?: { reason?: string; [key: string]: unknown }\n}\n\nexport interface JsonRpcSuccessResponse {\n jsonrpc: '2.0'\n id: JsonRpcId\n result: unknown\n}\n\nexport interface JsonRpcErrorResponse {\n jsonrpc: '2.0'\n id: JsonRpcId | null\n error: JsonRpcErrorObject\n}\n\nexport type JsonRpcResponse = JsonRpcSuccessResponse | JsonRpcErrorResponse\n\nexport type JsonRpcMessage =\n | JsonRpcRequest\n | JsonRpcNotification\n | JsonRpcResponse\n\nexport interface Envelope {\n bus: typeof BUS_ID\n msg: JsonRpcMessage\n}\n\n/**\n * JSON-RPC reserved codes for protocol errors, plus implementation-defined\n * codes (-32000..-32099 range) used by this package. Host API errors should\n * use HOST_ERROR with a stable string identifier in `error.data.reason`.\n */\nexport const RPC_ERRORS = {\n PARSE_ERROR: -32700,\n INVALID_REQUEST: -32600,\n METHOD_NOT_FOUND: -32601,\n INVALID_PARAMS: -32602,\n INTERNAL_ERROR: -32603,\n HOST_ERROR: -32000,\n TIMEOUT: -32001,\n CONNECTION_CLOSED: -32002\n} as const\n\nexport class RpcError extends Error {\n readonly code: number\n readonly data?: { reason?: string; [key: string]: unknown }\n\n constructor(\n message: string,\n opts: {\n code?: number\n reason?: string\n data?: Record<string, unknown>\n } = {}\n ) {\n super(message)\n this.name = 'RpcError'\n this.code = opts.code ?? RPC_ERRORS.HOST_ERROR\n const data: Record<string, unknown> = { ...(opts.data ?? {}) }\n if (opts.reason !== undefined) data.reason = opts.reason\n this.data = Object.keys(data).length > 0 ? data : undefined\n }\n\n get reason(): string | undefined {\n return typeof this.data?.reason === 'string' ? this.data.reason : undefined\n }\n\n toErrorObject(): JsonRpcErrorObject {\n return {\n code: this.code,\n message: this.message,\n ...(this.data ? { data: this.data } : {})\n }\n }\n\n static fromErrorObject(err: JsonRpcErrorObject): RpcError {\n return new RpcError(err.message, { code: err.code, data: err.data })\n }\n\n /** Normalize any thrown value into an RpcError suitable for the wire. */\n static from(err: unknown): RpcError {\n if (err instanceof RpcError) return err\n if (err instanceof Error) {\n return new RpcError(err.message, { code: RPC_ERRORS.INTERNAL_ERROR })\n }\n return new RpcError(String(err), { code: RPC_ERRORS.INTERNAL_ERROR })\n }\n}\n\n/** Reserved event names used to establish a connection. */\nexport const EVENT_READY = 'bus.ready'\nexport const EVENT_HANDSHAKE = 'bus.handshake'\n\nexport type ContextKind = 'panel' | 'widget' | 'background'\n\nexport interface HandshakeContext {\n kind: ContextKind\n /** Manifest-local contribution id. */\n id: string\n /** Host-assigned unique id for this placed instance (widgets). */\n instanceId?: string | null\n /** Widget instance a configuration panel was opened for. */\n targetInstance?: string | null\n /** Manifest-local widget id of the target instance (configuration panels). */\n targetWidget?: string | null\n}\n\nexport interface Handshake {\n host: string\n hostVersion: string\n apiVersion: string\n capabilities: string[]\n context: HandshakeContext\n}\n\n/** Payload of an `sk.<path>` Signal K value event. */\nexport interface SignalKValueEvent {\n path: string\n value: unknown\n timestamp?: string\n $source?: string\n}\n\nexport type StateScope = 'instance' | 'extension'\n\n/** Payload of a `state.changed` host event. */\nexport interface StateChangedEvent {\n scope: StateScope\n instanceId?: string | null\n keys: string[]\n}\n\n/**\n * Types for the `routes` capability — the routes the host currently has visible\n * on the chart (drafts plus stored routes the user is displaying), each\n * addressed by an opaque host-assigned `routeId`. Extensions read/write them and\n * follow lifecycle + mutation events (`route.visible` / `route.dirty` /\n * `route.saved` / `route.hidden`). Two orthogonal flags travel with a route:\n * `saved` (backed by a persisted resource) and `dirty` (has pending unsaved\n * changes). See the Plotter Extensions API spec, \"Live routes\".\n */\n\n/** A single point in a route edit buffer (`[lon, lat, alt?]`). */\nexport interface RoutePoint {\n position: [number, number, number?]\n name?: string\n description?: string\n}\n\n/** Snapshot of a route — result of `route.get`. */\nexport interface RouteData {\n routeId: string\n name: string | null\n /** Route-level description (distinct from a waypoint's RoutePoint.description). */\n description: string | null\n /** Monotonic revision; increments on every mutation. */\n rev: number\n /** Whether the route is backed by a persisted routes resource. */\n saved: boolean\n /** Whether the in-memory route has pending unsaved changes. */\n dirty: boolean\n points: RoutePoint[]\n}\n\n/** Summary entry in a `route.list` result (the visible set). */\nexport interface RouteSummary {\n routeId: string\n name: string | null\n rev: number\n pointCount: number\n saved: boolean\n dirty: boolean\n}\n\n/**\n * Payload of a `route.visible` host event — a route entered the visible set\n * (became rendered on the chart). A freshly drawn/created draft arrives\n * `saved:false, dirty:true`; a stored route brought into view arrives\n * `saved:true, dirty:false`.\n */\nexport interface RouteVisibleEvent {\n routeId: string\n rev: number\n name: string | null\n pointCount: number\n saved: boolean\n dirty: boolean\n}\n\n/**\n * Payload of a `route.hidden` host event — a route left the visible set.\n * `saved:true` ⇒ a stored route was made invisible (the resource is untouched);\n * `saved:false` ⇒ an unsaved draft was deleted (gone for good).\n */\nexport interface RouteHiddenEvent {\n routeId: string\n rev: number\n saved: boolean\n}\n\n/**\n * Payload of a `route.dirty` host event — the conformance-floor catch-all for\n * any structural/bulk change the host does not express granularly. A subscriber\n * should re-seed via `route.get`.\n */\nexport interface RouteDirtyEvent {\n routeId: string\n rev: number\n reason?: string\n}\n\n/**\n * Payload of a `route.saved` host event — the route's current state was\n * persisted to the routes resource collection. Arrives `saved:true,\n * dirty:false`; the route stays visible under the same `routeId`.\n */\nexport interface RouteSavedEvent {\n routeId: string\n rev: number\n /** Stored resource id of the persisted route. */\n href: string\n /** The route's name as persisted (may have just been set in the host's save\n * dialog), so followers can update a label without re-fetching. */\n name: string | null\n saved: boolean\n dirty: boolean\n}\n\n/** Stable `error.data.reason` strings for `route.*` host-method failures. */\nexport type RouteErrorReason =\n | 'routes.unknownId'\n | 'routes.badRequest'\n | 'routes.badRef'\n | 'routes.saveFailed'\n | 'routes.deleteFailed'\n | 'routes.saveCancelled'\n | 'routes.notSupported'\n","/**\n * eventemitter2-style wildcard matching for dot-separated event names.\n *\n * - `*` matches exactly one segment.\n * - `**` matches zero or more segments (any remainder when trailing).\n */\nexport function matchesPattern(pattern: string, name: string): boolean {\n if (pattern === name) return true\n return match(pattern.split('.'), 0, name.split('.'), 0)\n}\n\nfunction match(p: string[], pi: number, n: string[], ni: number): boolean {\n while (pi < p.length) {\n const seg = p[pi]\n if (seg === '**') {\n if (pi === p.length - 1) return true\n for (let skip = ni; skip <= n.length; skip++) {\n if (match(p, pi + 1, n, skip)) return true\n }\n return false\n }\n if (ni >= n.length) return false\n if (seg !== '*' && seg !== n[ni]) return false\n pi++\n ni++\n }\n return ni === n.length\n}\n\nexport function matchesAny(patterns: Iterable<string>, name: string): boolean {\n for (const pattern of patterns) {\n if (matchesPattern(pattern, name)) return true\n }\n return false\n}\n","import {\n BUS_ID,\n Envelope,\n JsonRpcMessage,\n JsonRpcNotification,\n JsonRpcRequest,\n JsonRpcResponse\n} from './protocol'\n\nexport function wrap(msg: JsonRpcMessage): Envelope {\n return { bus: BUS_ID, msg }\n}\n\n/**\n * Returns the JSON-RPC message inside a bus envelope, or null when the data\n * is not valid protocol traffic (so unrelated postMessage noise is ignored).\n */\nexport function unwrap(data: unknown): JsonRpcMessage | null {\n if (typeof data !== 'object' || data === null) return null\n const env = data as Record<string, unknown>\n if (env.bus !== BUS_ID) return null\n return isJsonRpcMessage(env.msg) ? env.msg : null\n}\n\nexport function isJsonRpcMessage(v: unknown): v is JsonRpcMessage {\n if (typeof v !== 'object' || v === null) return false\n const m = v as Record<string, unknown>\n if (m.jsonrpc !== '2.0') return false\n if (typeof m.method === 'string') {\n // Request (with id) or notification (without).\n return (\n m.id === undefined ||\n typeof m.id === 'string' ||\n typeof m.id === 'number'\n )\n }\n // Response: id required (null allowed for unroutable errors), exactly one\n // of result / error.\n const idOk =\n typeof m.id === 'string' || typeof m.id === 'number' || m.id === null\n if (!idOk) return false\n const hasResult = 'result' in m\n const err = m.error as Record<string, unknown> | undefined\n const hasError =\n typeof err === 'object' &&\n err !== null &&\n typeof err.code === 'number' &&\n typeof err.message === 'string'\n return hasResult ? !('error' in m) : hasError\n}\n\nexport function isRequest(msg: JsonRpcMessage): msg is JsonRpcRequest {\n return 'method' in msg && 'id' in msg && msg.id !== undefined\n}\n\nexport function isNotification(\n msg: JsonRpcMessage\n): msg is JsonRpcNotification {\n return 'method' in msg && (!('id' in msg) || msg.id === undefined)\n}\n\nexport function isResponse(msg: JsonRpcMessage): msg is JsonRpcResponse {\n return !('method' in msg)\n}\n","/**\n * Transport abstraction. The bus runs over anything that can post and\n * receive structured-cloneable data: window.postMessage between a host page\n * and an iframe, a MessageChannel port, or a test double.\n */\nexport interface BusPort {\n post(data: unknown): void\n /** Register a receive handler; returns an unlisten function. */\n listen(handler: (data: unknown) => void): () => void\n}\n\nexport interface WindowPortOptions {\n /**\n * targetOrigin for outgoing postMessage and required origin of incoming\n * messages. Defaults to the listening window's own origin (the baseline\n * deployment is same-origin). Pass '*' to disable origin checks.\n */\n origin?: string\n /** Window whose 'message' events are listened to. Defaults to globalThis. */\n listenWindow?: Window\n}\n\n/**\n * A port over window.postMessage to a peer window. On the host side the peer\n * is an iframe's contentWindow; on the extension side it is window.parent.\n * Only messages whose source is the peer (and whose origin matches) are\n * delivered.\n */\nexport function windowPort(peer: Window, opts: WindowPortOptions = {}): BusPort {\n const listenWindow =\n opts.listenWindow ?? (globalThis as unknown as Window)\n const origin = opts.origin ?? listenWindow.location?.origin ?? '*'\n return {\n post(data) {\n peer.postMessage(data, origin)\n },\n listen(handler) {\n const fn = (ev: MessageEvent) => {\n if (ev.source !== peer) return\n if (origin !== '*' && ev.origin !== origin) return\n handler(ev.data)\n }\n listenWindow.addEventListener('message', fn as EventListener)\n return () =>\n listenWindow.removeEventListener('message', fn as EventListener)\n }\n }\n}\n\ninterface MessagePortLike {\n postMessage(data: unknown): void\n addEventListener(type: 'message', fn: (ev: { data: unknown }) => void): void\n removeEventListener(\n type: 'message',\n fn: (ev: { data: unknown }) => void\n ): void\n start?(): void\n}\n\n/** A port over a MessageChannel/MessagePort (browser or Node). */\nexport function messagePort(port: MessagePortLike): BusPort {\n return {\n post(data) {\n port.postMessage(data)\n },\n listen(handler) {\n const fn = (ev: { data: unknown }) => handler(ev.data)\n port.addEventListener('message', fn)\n port.start?.()\n return () => port.removeEventListener('message', fn)\n }\n }\n}\n","import {\n isNotification,\n isRequest,\n isResponse,\n unwrap,\n wrap\n} from './codec'\nimport {\n JsonRpcId,\n JsonRpcMessage,\n JsonRpcResponse,\n RPC_ERRORS,\n RpcError\n} from './protocol'\nimport { BusPort } from './port'\nimport { matchesAny } from './wildcard'\n\nexport interface MethodContext {\n endpoint: BusEndpoint\n}\n\nexport type MethodHandler = (\n params: unknown,\n ctx: MethodContext\n) => unknown | Promise<unknown>\n\nexport type EventHandler = (name: string, params: unknown) => void\n\nexport interface BusEndpointOptions {\n port: BusPort\n /** Default timeout for outgoing calls. */\n callTimeoutMs?: number\n /** Reporter for handler/dispatch errors. Defaults to console.warn. */\n onError?: (err: unknown) => void\n}\n\ninterface PendingCall {\n resolve: (value: unknown) => void\n reject: (err: RpcError) => void\n timer: ReturnType<typeof setTimeout> | null\n}\n\nconst DEFAULT_CALL_TIMEOUT_MS = 10_000\n\n/**\n * One end of the bus: sends/receives envelopes over a BusPort, dispatches\n * incoming requests to registered methods, correlates responses to pending\n * calls by per-call nonce, and fans incoming notifications out to wildcard\n * event handlers. Symmetric — both host and extension build on this.\n */\nexport class BusEndpoint {\n readonly callTimeoutMs: number\n\n private readonly port: BusPort\n private readonly unlisten: () => void\n private readonly onError: (err: unknown) => void\n private readonly pending = new Map<JsonRpcId, PendingCall>()\n private readonly methods = new Map<string, MethodHandler>()\n private readonly eventHandlers = new Set<{\n patterns: string[]\n fn: EventHandler\n }>()\n private readonly idPrefix = Math.random().toString(36).slice(2, 8)\n private seq = 0\n private closed = false\n\n constructor(opts: BusEndpointOptions) {\n this.port = opts.port\n this.callTimeoutMs = opts.callTimeoutMs ?? DEFAULT_CALL_TIMEOUT_MS\n this.onError =\n opts.onError ??\n ((err) => console.warn('[plotterext-bus]', err))\n this.unlisten = this.port.listen((data) => this.onData(data))\n }\n\n registerMethod(name: string, handler: MethodHandler): void {\n this.methods.set(name, handler)\n }\n\n unregisterMethod(name: string): void {\n this.methods.delete(name)\n }\n\n /**\n * Handle incoming notifications whose names match any of the wildcard\n * patterns. Returns an unsubscribe function. This is local dispatch only;\n * telling the peer which events to forward is a separate concern\n * (`events.subscribe`).\n */\n onEvent(patterns: string[], fn: EventHandler): () => void {\n const entry = { patterns, fn }\n this.eventHandlers.add(entry)\n return () => this.eventHandlers.delete(entry)\n }\n\n /** Send a notification (an event) to the peer. */\n notify(method: string, params?: unknown): void {\n this.send({ jsonrpc: '2.0', method, ...(params !== undefined ? { params } : {}) })\n }\n\n /** Call a method on the peer; resolves with its result. */\n call(\n method: string,\n params?: unknown,\n opts: { timeoutMs?: number } = {}\n ): Promise<unknown> {\n if (this.closed) {\n return Promise.reject(\n new RpcError('Bus endpoint is closed', {\n code: RPC_ERRORS.CONNECTION_CLOSED,\n reason: 'CLOSED'\n })\n )\n }\n const id = `${this.idPrefix}-${++this.seq}`\n const timeoutMs = opts.timeoutMs ?? this.callTimeoutMs\n return new Promise<unknown>((resolve, reject) => {\n const timer =\n timeoutMs > 0\n ? setTimeout(() => {\n this.pending.delete(id)\n reject(\n new RpcError(`Call timed out after ${timeoutMs}ms: ${method}`, {\n code: RPC_ERRORS.TIMEOUT,\n reason: 'TIMEOUT'\n })\n )\n }, timeoutMs)\n : null\n this.pending.set(id, { resolve, reject, timer })\n this.send({\n jsonrpc: '2.0',\n id,\n method,\n ...(params !== undefined ? { params } : {})\n })\n })\n }\n\n close(): void {\n if (this.closed) return\n this.closed = true\n this.unlisten()\n for (const [, p] of this.pending) {\n if (p.timer) clearTimeout(p.timer)\n p.reject(\n new RpcError('Bus endpoint closed', {\n code: RPC_ERRORS.CONNECTION_CLOSED,\n reason: 'CLOSED'\n })\n )\n }\n this.pending.clear()\n this.eventHandlers.clear()\n }\n\n private send(msg: JsonRpcMessage): void {\n if (this.closed) return\n this.port.post(wrap(msg))\n }\n\n private onData(data: unknown): void {\n const msg = unwrap(data)\n if (!msg) return\n if (isResponse(msg)) {\n this.onResponse(msg)\n } else if (isRequest(msg)) {\n void this.onRequest(msg)\n } else if (isNotification(msg)) {\n this.onNotification(msg.method, msg.params)\n }\n }\n\n private onResponse(msg: JsonRpcResponse): void {\n if (msg.id === null) return\n const p = this.pending.get(msg.id)\n if (!p) return\n this.pending.delete(msg.id)\n if (p.timer) clearTimeout(p.timer)\n if ('error' in msg) {\n p.reject(RpcError.fromErrorObject(msg.error))\n } else {\n p.resolve(msg.result)\n }\n }\n\n private async onRequest(msg: {\n id: JsonRpcId\n method: string\n params?: unknown\n }): Promise<void> {\n const handler = this.methods.get(msg.method)\n if (!handler) {\n this.send({\n jsonrpc: '2.0',\n id: msg.id,\n error: {\n code: RPC_ERRORS.METHOD_NOT_FOUND,\n message: `Method not found: ${msg.method}`\n }\n })\n return\n }\n try {\n const result = await handler(msg.params, { endpoint: this })\n this.send({\n jsonrpc: '2.0',\n id: msg.id,\n result: result === undefined ? null : result\n })\n } catch (err) {\n this.send({\n jsonrpc: '2.0',\n id: msg.id,\n error: RpcError.from(err).toErrorObject()\n })\n }\n }\n\n private onNotification(name: string, params: unknown): void {\n for (const entry of [...this.eventHandlers]) {\n if (matchesAny(entry.patterns, name)) {\n try {\n entry.fn(name, params)\n } catch (err) {\n this.onError(err)\n }\n }\n }\n }\n}\n"],"mappings":"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","names":[]}
@@ -5,7 +5,7 @@ import {
5
5
  RPC_ERRORS,
6
6
  RpcError,
7
7
  windowPort
8
- } from "./chunk-VBS4I6NW.js";
8
+ } from "./chunk-LVGWVV2W.js";
9
9
 
10
10
  // src/extension.ts
11
11
  var ExtensionClient = class {
@@ -196,4 +196,4 @@ export {
196
196
  ExtensionClient,
197
197
  connectExtension
198
198
  };
199
- //# sourceMappingURL=chunk-YZVRR7LW.js.map
199
+ //# sourceMappingURL=chunk-YL2LWXNX.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/extension.ts","../src/protocol.ts","../src/codec.ts","../src/wildcard.ts","../src/endpoint.ts","../src/port.ts"],"sourcesContent":["import { BusEndpoint, EventHandler } from './endpoint'\nimport { BusPort, windowPort } from './port'\nimport {\n EVENT_HANDSHAKE,\n EVENT_READY,\n Handshake,\n HandshakeContext,\n RouteData,\n RoutePoint,\n RouteSummary,\n RPC_ERRORS,\n RpcError,\n SignalKValueEvent,\n StateScope\n} from './protocol'\n\nexport interface ConnectOptions {\n /** Transport. Defaults to window.postMessage to window.parent. */\n port?: BusPort\n /** Interval for re-sending bus.ready until the handshake arrives. */\n readyIntervalMs?: number\n /** How long to wait for the host handshake before rejecting. */\n timeoutMs?: number\n /** Default timeout for host API calls. */\n callTimeoutMs?: number\n onError?: (err: unknown) => void\n}\n\nexport type Unsubscribe = () => Promise<void>\n\n/**\n * The extension side of the bus: one per iframe context (panel, widget, or\n * background runtime). Create via connectExtension().\n */\nexport class ExtensionClient {\n readonly handshake: Handshake\n readonly endpoint: BusEndpoint\n\n constructor(endpoint: BusEndpoint, handshake: Handshake) {\n this.endpoint = endpoint\n this.handshake = handshake\n }\n\n get context(): HandshakeContext {\n return this.handshake.context\n }\n\n get apiVersion(): string {\n return this.handshake.apiVersion\n }\n\n get capabilities(): string[] {\n return this.handshake.capabilities\n }\n\n hasCapability(id: string): boolean {\n return this.handshake.capabilities.includes(id)\n }\n\n /** Call a host API method. */\n call(\n method: string,\n params?: unknown,\n opts?: { timeoutMs?: number }\n ): Promise<unknown> {\n return this.endpoint.call(method, params, opts)\n }\n\n /** Send a notification to the host. */\n notify(method: string, params?: unknown): void {\n this.endpoint.notify(method, params)\n }\n\n /**\n * Subscribe to host events matching wildcard patterns. Registers both the\n * host-side forwarding subscription and local dispatch; the returned\n * function tears down both.\n */\n async subscribe(\n patterns: string[],\n handler: EventHandler\n ): Promise<Unsubscribe> {\n const off = this.endpoint.onEvent(patterns, handler)\n let subscriptionId: string\n try {\n const result = (await this.call('events.subscribe', { patterns })) as {\n subscriptionId: string\n }\n subscriptionId = result.subscriptionId\n } catch (err) {\n off()\n throw err\n }\n return async () => {\n off()\n await this.call('events.unsubscribe', { subscriptionId }).catch(() => {\n // Best-effort: the host may already have dropped the connection.\n })\n }\n }\n\n /** Host-persisted key/value state (see spec: State Storage). */\n readonly state = {\n get: async (\n keys?: string[],\n scope?: StateScope\n ): Promise<Record<string, unknown>> => {\n const result = (await this.call('state.get', {\n ...(scope ? { scope } : {}),\n ...(keys ? { keys } : {})\n })) as { values: Record<string, unknown> }\n return result.values ?? {}\n },\n set: async (\n values: Record<string, unknown>,\n scope?: StateScope\n ): Promise<void> => {\n await this.call('state.set', {\n ...(scope ? { scope } : {}),\n values\n })\n }\n }\n\n /** Signal K data relayed by the host (capabilities signalk.stream / .put). */\n readonly signalk = {\n /**\n * Subscribe to Signal K path values. The host publishes them as\n * `sk.<path>` events; this helper hides the event-name mapping and\n * establishes both the event-forwarding subscription and the host's\n * upstream Signal K subscription.\n */\n subscribe: async (\n paths: string[],\n handler: (ev: SignalKValueEvent) => void\n ): Promise<Unsubscribe> => {\n const patterns = paths.map((p) => `sk.${p}`)\n const offEvents = await this.subscribe(patterns, (_name, params) =>\n handler(params as SignalKValueEvent)\n )\n let subscriptionId: string\n try {\n const result = (await this.call('signalk.subscribe', { paths })) as {\n subscriptionId: string\n }\n subscriptionId = result.subscriptionId\n } catch (err) {\n await offEvents()\n throw err\n }\n return async () => {\n await offEvents()\n await this.call('signalk.unsubscribe', { subscriptionId }).catch(\n () => {}\n )\n }\n },\n put: (path: string, value: unknown): Promise<unknown> => {\n return this.call('signalk.put', { path, value })\n }\n }\n\n /**\n * The host's visible routes (capability `routes`). Thin **typed** wrappers over\n * the host's `route.*` methods — each just delegates to `this.call(...)`, so a\n * plain-JS extension can call `client.call('route.replace', …)` directly and a\n * TypeScript extension gets the typed `client.route.replace(…)` sugar with no\n * behavioural difference. Follow lifecycle + mutations by subscribing to\n * `route.**` events (`RouteVisibleEvent` / `RouteDirtyEvent` / `RouteSavedEvent`\n * / `RouteHiddenEvent`). Further operations (rename/point ops) extend this\n * surface as the capability fills out.\n */\n readonly route = {\n list: async (): Promise<RouteSummary[]> => {\n const result = (await this.call('route.list')) as {\n routes?: RouteSummary[]\n }\n return result.routes ?? []\n },\n create: async (opts: {\n points: RoutePoint[]\n name?: string\n description?: string\n }): Promise<{ routeId: string; rev: number }> => {\n // A route needs at least two points to form a segment; the host rejects\n // fewer with routes.badRequest.\n return (await this.call('route.create', opts)) as {\n routeId: string\n rev: number\n }\n },\n get: async (routeId: string): Promise<RouteData> => {\n return (await this.call('route.get', { routeId })) as RouteData\n },\n replace: async (\n routeId: string,\n points: RoutePoint[]\n ): Promise<{ rev: number }> => {\n return (await this.call('route.replace', { routeId, points })) as {\n rev: number\n }\n },\n save: async (\n routeId: string,\n opts?: { name?: string; description?: string; dialog?: boolean }\n ): Promise<{ href: string; rev: number }> => {\n return (await this.call('route.save', {\n routeId,\n ...(opts ?? {})\n })) as { href: string; rev: number }\n },\n show: async (ref: string): Promise<{ routeId: string; rev: number }> => {\n return (await this.call('route.show', { ref })) as {\n routeId: string\n rev: number\n }\n },\n hide: async (routeId: string): Promise<void> => {\n await this.call('route.hide', { routeId })\n },\n delete: async (routeId: string): Promise<void> => {\n await this.call('route.delete', { routeId })\n }\n }\n\n close(): void {\n this.endpoint.close()\n }\n}\n\n/**\n * Connect to the host from inside an extension iframe. Sends `bus.ready`\n * (repeating until answered) and resolves once the host's `bus.handshake`\n * arrives.\n */\nexport function connectExtension(\n opts: ConnectOptions = {}\n): Promise<ExtensionClient> {\n // Default transport: postMessage to the embedding window. Origin checks\n // are relaxed to '*' on the extension side because the host page's origin\n // may legitimately differ from the extension asset origin (e.g. a host dev\n // server embedding extension assets served by the Signal K server); the\n // peer-source check in windowPort still applies, and the host side\n // enforces a strict origin for the extension's frame.\n const port =\n opts.port ??\n windowPort((globalThis as unknown as Window).parent as Window, {\n origin: '*'\n })\n const endpoint = new BusEndpoint({\n port,\n callTimeoutMs: opts.callTimeoutMs,\n onError: opts.onError\n })\n return new Promise<ExtensionClient>((resolve, reject) => {\n let done = false\n const off = endpoint.onEvent([EVENT_HANDSHAKE], (_name, params) => {\n if (done) return\n done = true\n cleanup()\n resolve(new ExtensionClient(endpoint, params as Handshake))\n })\n const interval = setInterval(\n () => endpoint.notify(EVENT_READY),\n opts.readyIntervalMs ?? 250\n )\n const timeout = setTimeout(() => {\n if (done) return\n done = true\n cleanup()\n endpoint.close()\n reject(\n new RpcError('Timed out waiting for host handshake', {\n code: RPC_ERRORS.TIMEOUT,\n reason: 'HANDSHAKE_TIMEOUT'\n })\n )\n }, opts.timeoutMs ?? 10_000)\n const cleanup = () => {\n off()\n clearInterval(interval)\n clearTimeout(timeout)\n }\n endpoint.notify(EVENT_READY)\n })\n}\n\nexport * from './protocol'\nexport * from './wildcard'\nexport { BusEndpoint } from './endpoint'\nexport type { MethodHandler, MethodContext, EventHandler } from './endpoint'\nexport * from './port'\n","/**\n * Wire protocol types and constants for the Signal K plotter extension bus.\n *\n * The wire format is JSON-RPC 2.0 (https://www.jsonrpc.org/specification)\n * inside a routing envelope: `{ bus: \"plotterExt/1\", msg: <JSON-RPC object> }`.\n * Calls are JSON-RPC requests; events are JSON-RPC notifications whose\n * `method` is a hierarchical dot-separated event name.\n */\n\nexport const BUS_ID = 'plotterExt/1'\n\nexport type JsonRpcId = string | number\n\nexport interface JsonRpcRequest {\n jsonrpc: '2.0'\n id: JsonRpcId\n method: string\n params?: unknown\n}\n\nexport interface JsonRpcNotification {\n jsonrpc: '2.0'\n method: string\n params?: unknown\n}\n\nexport interface JsonRpcErrorObject {\n code: number\n message: string\n data?: { reason?: string; [key: string]: unknown }\n}\n\nexport interface JsonRpcSuccessResponse {\n jsonrpc: '2.0'\n id: JsonRpcId\n result: unknown\n}\n\nexport interface JsonRpcErrorResponse {\n jsonrpc: '2.0'\n id: JsonRpcId | null\n error: JsonRpcErrorObject\n}\n\nexport type JsonRpcResponse = JsonRpcSuccessResponse | JsonRpcErrorResponse\n\nexport type JsonRpcMessage =\n | JsonRpcRequest\n | JsonRpcNotification\n | JsonRpcResponse\n\nexport interface Envelope {\n bus: typeof BUS_ID\n msg: JsonRpcMessage\n}\n\n/**\n * JSON-RPC reserved codes for protocol errors, plus implementation-defined\n * codes (-32000..-32099 range) used by this package. Host API errors should\n * use HOST_ERROR with a stable string identifier in `error.data.reason`.\n */\nexport const RPC_ERRORS = {\n PARSE_ERROR: -32700,\n INVALID_REQUEST: -32600,\n METHOD_NOT_FOUND: -32601,\n INVALID_PARAMS: -32602,\n INTERNAL_ERROR: -32603,\n HOST_ERROR: -32000,\n TIMEOUT: -32001,\n CONNECTION_CLOSED: -32002\n} as const\n\nexport class RpcError extends Error {\n readonly code: number\n readonly data?: { reason?: string; [key: string]: unknown }\n\n constructor(\n message: string,\n opts: {\n code?: number\n reason?: string\n data?: Record<string, unknown>\n } = {}\n ) {\n super(message)\n this.name = 'RpcError'\n this.code = opts.code ?? RPC_ERRORS.HOST_ERROR\n const data: Record<string, unknown> = { ...(opts.data ?? {}) }\n if (opts.reason !== undefined) data.reason = opts.reason\n this.data = Object.keys(data).length > 0 ? data : undefined\n }\n\n get reason(): string | undefined {\n return typeof this.data?.reason === 'string' ? this.data.reason : undefined\n }\n\n toErrorObject(): JsonRpcErrorObject {\n return {\n code: this.code,\n message: this.message,\n ...(this.data ? { data: this.data } : {})\n }\n }\n\n static fromErrorObject(err: JsonRpcErrorObject): RpcError {\n return new RpcError(err.message, { code: err.code, data: err.data })\n }\n\n /** Normalize any thrown value into an RpcError suitable for the wire. */\n static from(err: unknown): RpcError {\n if (err instanceof RpcError) return err\n if (err instanceof Error) {\n return new RpcError(err.message, { code: RPC_ERRORS.INTERNAL_ERROR })\n }\n return new RpcError(String(err), { code: RPC_ERRORS.INTERNAL_ERROR })\n }\n}\n\n/** Reserved event names used to establish a connection. */\nexport const EVENT_READY = 'bus.ready'\nexport const EVENT_HANDSHAKE = 'bus.handshake'\n\nexport type ContextKind = 'panel' | 'widget' | 'background'\n\nexport interface HandshakeContext {\n kind: ContextKind\n /** Manifest-local contribution id. */\n id: string\n /** Host-assigned unique id for this placed instance (widgets). */\n instanceId?: string | null\n /** Widget instance a configuration panel was opened for. */\n targetInstance?: string | null\n /** Manifest-local widget id of the target instance (configuration panels). */\n targetWidget?: string | null\n}\n\nexport interface Handshake {\n host: string\n hostVersion: string\n apiVersion: string\n capabilities: string[]\n context: HandshakeContext\n}\n\n/** Payload of an `sk.<path>` Signal K value event. */\nexport interface SignalKValueEvent {\n path: string\n value: unknown\n timestamp?: string\n $source?: string\n}\n\nexport type StateScope = 'instance' | 'extension'\n\n/** Payload of a `state.changed` host event. */\nexport interface StateChangedEvent {\n scope: StateScope\n instanceId?: string | null\n keys: string[]\n}\n\n/**\n * Types for the `routes` capability — the routes the host currently has visible\n * on the chart (drafts plus stored routes the user is displaying), each\n * addressed by an opaque host-assigned `routeId`. Extensions read/write them and\n * follow lifecycle + mutation events (`route.visible` / `route.dirty` /\n * `route.saved` / `route.hidden`). Two orthogonal flags travel with a route:\n * `saved` (backed by a persisted resource) and `dirty` (has pending unsaved\n * changes). See the Plotter Extensions API spec, \"Live routes\".\n */\n\n/** A single point in a route edit buffer (`[lon, lat, alt?]`). */\nexport interface RoutePoint {\n position: [number, number, number?]\n name?: string\n description?: string\n}\n\n/** Snapshot of a route — result of `route.get`. */\nexport interface RouteData {\n routeId: string\n name: string | null\n /** Route-level description (distinct from a waypoint's RoutePoint.description). */\n description: string | null\n /** Monotonic revision; increments on every mutation. */\n rev: number\n /** Whether the route is backed by a persisted routes resource. */\n saved: boolean\n /** Whether the in-memory route has pending unsaved changes. */\n dirty: boolean\n points: RoutePoint[]\n}\n\n/** Summary entry in a `route.list` result (the visible set). */\nexport interface RouteSummary {\n routeId: string\n name: string | null\n rev: number\n pointCount: number\n saved: boolean\n dirty: boolean\n}\n\n/**\n * Payload of a `route.visible` host event — a route entered the visible set\n * (became rendered on the chart). A freshly drawn/created draft arrives\n * `saved:false, dirty:true`; a stored route brought into view arrives\n * `saved:true, dirty:false`.\n */\nexport interface RouteVisibleEvent {\n routeId: string\n rev: number\n name: string | null\n pointCount: number\n saved: boolean\n dirty: boolean\n}\n\n/**\n * Payload of a `route.hidden` host event — a route left the visible set.\n * `saved:true` ⇒ a stored route was made invisible (the resource is untouched);\n * `saved:false` ⇒ an unsaved draft was deleted (gone for good).\n */\nexport interface RouteHiddenEvent {\n routeId: string\n rev: number\n saved: boolean\n}\n\n/**\n * Payload of a `route.dirty` host event — the conformance-floor catch-all for\n * any structural/bulk change the host does not express granularly. A subscriber\n * should re-seed via `route.get`.\n */\nexport interface RouteDirtyEvent {\n routeId: string\n rev: number\n reason?: string\n}\n\n/**\n * Payload of a `route.saved` host event — the route's current state was\n * persisted to the routes resource collection. Arrives `saved:true,\n * dirty:false`; the route stays visible under the same `routeId`.\n */\nexport interface RouteSavedEvent {\n routeId: string\n rev: number\n /** Stored resource id of the persisted route. */\n href: string\n /** The route's name as persisted (may have just been set in the host's save\n * dialog), so followers can update a label without re-fetching. */\n name: string | null\n saved: boolean\n dirty: boolean\n}\n\n/** Stable `error.data.reason` strings for `route.*` host-method failures. */\nexport type RouteErrorReason =\n | 'routes.unknownId'\n | 'routes.badRequest'\n | 'routes.badRef'\n | 'routes.saveFailed'\n | 'routes.saveCancelled'\n | 'routes.notSupported'\n","import {\n BUS_ID,\n Envelope,\n JsonRpcMessage,\n JsonRpcNotification,\n JsonRpcRequest,\n JsonRpcResponse\n} from './protocol'\n\nexport function wrap(msg: JsonRpcMessage): Envelope {\n return { bus: BUS_ID, msg }\n}\n\n/**\n * Returns the JSON-RPC message inside a bus envelope, or null when the data\n * is not valid protocol traffic (so unrelated postMessage noise is ignored).\n */\nexport function unwrap(data: unknown): JsonRpcMessage | null {\n if (typeof data !== 'object' || data === null) return null\n const env = data as Record<string, unknown>\n if (env.bus !== BUS_ID) return null\n return isJsonRpcMessage(env.msg) ? env.msg : null\n}\n\nexport function isJsonRpcMessage(v: unknown): v is JsonRpcMessage {\n if (typeof v !== 'object' || v === null) return false\n const m = v as Record<string, unknown>\n if (m.jsonrpc !== '2.0') return false\n if (typeof m.method === 'string') {\n // Request (with id) or notification (without).\n return (\n m.id === undefined ||\n typeof m.id === 'string' ||\n typeof m.id === 'number'\n )\n }\n // Response: id required (null allowed for unroutable errors), exactly one\n // of result / error.\n const idOk =\n typeof m.id === 'string' || typeof m.id === 'number' || m.id === null\n if (!idOk) return false\n const hasResult = 'result' in m\n const err = m.error as Record<string, unknown> | undefined\n const hasError =\n typeof err === 'object' &&\n err !== null &&\n typeof err.code === 'number' &&\n typeof err.message === 'string'\n return hasResult ? !('error' in m) : hasError\n}\n\nexport function isRequest(msg: JsonRpcMessage): msg is JsonRpcRequest {\n return 'method' in msg && 'id' in msg && msg.id !== undefined\n}\n\nexport function isNotification(\n msg: JsonRpcMessage\n): msg is JsonRpcNotification {\n return 'method' in msg && (!('id' in msg) || msg.id === undefined)\n}\n\nexport function isResponse(msg: JsonRpcMessage): msg is JsonRpcResponse {\n return !('method' in msg)\n}\n","/**\n * eventemitter2-style wildcard matching for dot-separated event names.\n *\n * - `*` matches exactly one segment.\n * - `**` matches zero or more segments (any remainder when trailing).\n */\nexport function matchesPattern(pattern: string, name: string): boolean {\n if (pattern === name) return true\n return match(pattern.split('.'), 0, name.split('.'), 0)\n}\n\nfunction match(p: string[], pi: number, n: string[], ni: number): boolean {\n while (pi < p.length) {\n const seg = p[pi]\n if (seg === '**') {\n if (pi === p.length - 1) return true\n for (let skip = ni; skip <= n.length; skip++) {\n if (match(p, pi + 1, n, skip)) return true\n }\n return false\n }\n if (ni >= n.length) return false\n if (seg !== '*' && seg !== n[ni]) return false\n pi++\n ni++\n }\n return ni === n.length\n}\n\nexport function matchesAny(patterns: Iterable<string>, name: string): boolean {\n for (const pattern of patterns) {\n if (matchesPattern(pattern, name)) return true\n }\n return false\n}\n","import {\n isNotification,\n isRequest,\n isResponse,\n unwrap,\n wrap\n} from './codec'\nimport {\n JsonRpcId,\n JsonRpcMessage,\n JsonRpcResponse,\n RPC_ERRORS,\n RpcError\n} from './protocol'\nimport { BusPort } from './port'\nimport { matchesAny } from './wildcard'\n\nexport interface MethodContext {\n endpoint: BusEndpoint\n}\n\nexport type MethodHandler = (\n params: unknown,\n ctx: MethodContext\n) => unknown | Promise<unknown>\n\nexport type EventHandler = (name: string, params: unknown) => void\n\nexport interface BusEndpointOptions {\n port: BusPort\n /** Default timeout for outgoing calls. */\n callTimeoutMs?: number\n /** Reporter for handler/dispatch errors. Defaults to console.warn. */\n onError?: (err: unknown) => void\n}\n\ninterface PendingCall {\n resolve: (value: unknown) => void\n reject: (err: RpcError) => void\n timer: ReturnType<typeof setTimeout> | null\n}\n\nconst DEFAULT_CALL_TIMEOUT_MS = 10_000\n\n/**\n * One end of the bus: sends/receives envelopes over a BusPort, dispatches\n * incoming requests to registered methods, correlates responses to pending\n * calls by per-call nonce, and fans incoming notifications out to wildcard\n * event handlers. Symmetric — both host and extension build on this.\n */\nexport class BusEndpoint {\n readonly callTimeoutMs: number\n\n private readonly port: BusPort\n private readonly unlisten: () => void\n private readonly onError: (err: unknown) => void\n private readonly pending = new Map<JsonRpcId, PendingCall>()\n private readonly methods = new Map<string, MethodHandler>()\n private readonly eventHandlers = new Set<{\n patterns: string[]\n fn: EventHandler\n }>()\n private readonly idPrefix = Math.random().toString(36).slice(2, 8)\n private seq = 0\n private closed = false\n\n constructor(opts: BusEndpointOptions) {\n this.port = opts.port\n this.callTimeoutMs = opts.callTimeoutMs ?? DEFAULT_CALL_TIMEOUT_MS\n this.onError =\n opts.onError ??\n ((err) => console.warn('[plotterext-bus]', err))\n this.unlisten = this.port.listen((data) => this.onData(data))\n }\n\n registerMethod(name: string, handler: MethodHandler): void {\n this.methods.set(name, handler)\n }\n\n unregisterMethod(name: string): void {\n this.methods.delete(name)\n }\n\n /**\n * Handle incoming notifications whose names match any of the wildcard\n * patterns. Returns an unsubscribe function. This is local dispatch only;\n * telling the peer which events to forward is a separate concern\n * (`events.subscribe`).\n */\n onEvent(patterns: string[], fn: EventHandler): () => void {\n const entry = { patterns, fn }\n this.eventHandlers.add(entry)\n return () => this.eventHandlers.delete(entry)\n }\n\n /** Send a notification (an event) to the peer. */\n notify(method: string, params?: unknown): void {\n this.send({ jsonrpc: '2.0', method, ...(params !== undefined ? { params } : {}) })\n }\n\n /** Call a method on the peer; resolves with its result. */\n call(\n method: string,\n params?: unknown,\n opts: { timeoutMs?: number } = {}\n ): Promise<unknown> {\n if (this.closed) {\n return Promise.reject(\n new RpcError('Bus endpoint is closed', {\n code: RPC_ERRORS.CONNECTION_CLOSED,\n reason: 'CLOSED'\n })\n )\n }\n const id = `${this.idPrefix}-${++this.seq}`\n const timeoutMs = opts.timeoutMs ?? this.callTimeoutMs\n return new Promise<unknown>((resolve, reject) => {\n const timer =\n timeoutMs > 0\n ? setTimeout(() => {\n this.pending.delete(id)\n reject(\n new RpcError(`Call timed out after ${timeoutMs}ms: ${method}`, {\n code: RPC_ERRORS.TIMEOUT,\n reason: 'TIMEOUT'\n })\n )\n }, timeoutMs)\n : null\n this.pending.set(id, { resolve, reject, timer })\n this.send({\n jsonrpc: '2.0',\n id,\n method,\n ...(params !== undefined ? { params } : {})\n })\n })\n }\n\n close(): void {\n if (this.closed) return\n this.closed = true\n this.unlisten()\n for (const [, p] of this.pending) {\n if (p.timer) clearTimeout(p.timer)\n p.reject(\n new RpcError('Bus endpoint closed', {\n code: RPC_ERRORS.CONNECTION_CLOSED,\n reason: 'CLOSED'\n })\n )\n }\n this.pending.clear()\n this.eventHandlers.clear()\n }\n\n private send(msg: JsonRpcMessage): void {\n if (this.closed) return\n this.port.post(wrap(msg))\n }\n\n private onData(data: unknown): void {\n const msg = unwrap(data)\n if (!msg) return\n if (isResponse(msg)) {\n this.onResponse(msg)\n } else if (isRequest(msg)) {\n void this.onRequest(msg)\n } else if (isNotification(msg)) {\n this.onNotification(msg.method, msg.params)\n }\n }\n\n private onResponse(msg: JsonRpcResponse): void {\n if (msg.id === null) return\n const p = this.pending.get(msg.id)\n if (!p) return\n this.pending.delete(msg.id)\n if (p.timer) clearTimeout(p.timer)\n if ('error' in msg) {\n p.reject(RpcError.fromErrorObject(msg.error))\n } else {\n p.resolve(msg.result)\n }\n }\n\n private async onRequest(msg: {\n id: JsonRpcId\n method: string\n params?: unknown\n }): Promise<void> {\n const handler = this.methods.get(msg.method)\n if (!handler) {\n this.send({\n jsonrpc: '2.0',\n id: msg.id,\n error: {\n code: RPC_ERRORS.METHOD_NOT_FOUND,\n message: `Method not found: ${msg.method}`\n }\n })\n return\n }\n try {\n const result = await handler(msg.params, { endpoint: this })\n this.send({\n jsonrpc: '2.0',\n id: msg.id,\n result: result === undefined ? null : result\n })\n } catch (err) {\n this.send({\n jsonrpc: '2.0',\n id: msg.id,\n error: RpcError.from(err).toErrorObject()\n })\n }\n }\n\n private onNotification(name: string, params: unknown): void {\n for (const entry of [...this.eventHandlers]) {\n if (matchesAny(entry.patterns, name)) {\n try {\n entry.fn(name, params)\n } catch (err) {\n this.onError(err)\n }\n }\n }\n }\n}\n","/**\n * Transport abstraction. The bus runs over anything that can post and\n * receive structured-cloneable data: window.postMessage between a host page\n * and an iframe, a MessageChannel port, or a test double.\n */\nexport interface BusPort {\n post(data: unknown): void\n /** Register a receive handler; returns an unlisten function. */\n listen(handler: (data: unknown) => void): () => void\n}\n\nexport interface WindowPortOptions {\n /**\n * targetOrigin for outgoing postMessage and required origin of incoming\n * messages. Defaults to the listening window's own origin (the baseline\n * deployment is same-origin). Pass '*' to disable origin checks.\n */\n origin?: string\n /** Window whose 'message' events are listened to. Defaults to globalThis. */\n listenWindow?: Window\n}\n\n/**\n * A port over window.postMessage to a peer window. On the host side the peer\n * is an iframe's contentWindow; on the extension side it is window.parent.\n * Only messages whose source is the peer (and whose origin matches) are\n * delivered.\n */\nexport function windowPort(peer: Window, opts: WindowPortOptions = {}): BusPort {\n const listenWindow =\n opts.listenWindow ?? (globalThis as unknown as Window)\n const origin = opts.origin ?? listenWindow.location?.origin ?? '*'\n return {\n post(data) {\n peer.postMessage(data, origin)\n },\n listen(handler) {\n const fn = (ev: MessageEvent) => {\n if (ev.source !== peer) return\n if (origin !== '*' && ev.origin !== origin) return\n handler(ev.data)\n }\n listenWindow.addEventListener('message', fn as EventListener)\n return () =>\n listenWindow.removeEventListener('message', fn as EventListener)\n }\n }\n}\n\ninterface MessagePortLike {\n postMessage(data: unknown): void\n addEventListener(type: 'message', fn: (ev: { data: unknown }) => void): void\n removeEventListener(\n type: 'message',\n fn: (ev: { data: unknown }) => void\n ): void\n start?(): void\n}\n\n/** A port over a MessageChannel/MessagePort (browser or Node). */\nexport function messagePort(port: MessagePortLike): BusPort {\n return {\n post(data) {\n port.postMessage(data)\n },\n listen(handler) {\n const fn = (ev: { data: unknown }) => handler(ev.data)\n port.addEventListener('message', fn)\n port.start?.()\n return () => port.removeEventListener('message', fn)\n }\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/extension.ts","../src/protocol.ts","../src/codec.ts","../src/wildcard.ts","../src/endpoint.ts","../src/port.ts"],"sourcesContent":["import { BusEndpoint, EventHandler } from './endpoint'\nimport { BusPort, windowPort } from './port'\nimport {\n EVENT_HANDSHAKE,\n EVENT_READY,\n Handshake,\n HandshakeContext,\n RouteData,\n RoutePoint,\n RouteSummary,\n RPC_ERRORS,\n RpcError,\n SignalKValueEvent,\n StateScope\n} from './protocol'\n\nexport interface ConnectOptions {\n /** Transport. Defaults to window.postMessage to window.parent. */\n port?: BusPort\n /** Interval for re-sending bus.ready until the handshake arrives. */\n readyIntervalMs?: number\n /** How long to wait for the host handshake before rejecting. */\n timeoutMs?: number\n /** Default timeout for host API calls. */\n callTimeoutMs?: number\n onError?: (err: unknown) => void\n}\n\nexport type Unsubscribe = () => Promise<void>\n\n/**\n * The extension side of the bus: one per iframe context (panel, widget, or\n * background runtime). Create via connectExtension().\n */\nexport class ExtensionClient {\n readonly handshake: Handshake\n readonly endpoint: BusEndpoint\n\n constructor(endpoint: BusEndpoint, handshake: Handshake) {\n this.endpoint = endpoint\n this.handshake = handshake\n }\n\n get context(): HandshakeContext {\n return this.handshake.context\n }\n\n get apiVersion(): string {\n return this.handshake.apiVersion\n }\n\n get capabilities(): string[] {\n return this.handshake.capabilities\n }\n\n hasCapability(id: string): boolean {\n return this.handshake.capabilities.includes(id)\n }\n\n /** Call a host API method. */\n call(\n method: string,\n params?: unknown,\n opts?: { timeoutMs?: number }\n ): Promise<unknown> {\n return this.endpoint.call(method, params, opts)\n }\n\n /** Send a notification to the host. */\n notify(method: string, params?: unknown): void {\n this.endpoint.notify(method, params)\n }\n\n /**\n * Subscribe to host events matching wildcard patterns. Registers both the\n * host-side forwarding subscription and local dispatch; the returned\n * function tears down both.\n */\n async subscribe(\n patterns: string[],\n handler: EventHandler\n ): Promise<Unsubscribe> {\n const off = this.endpoint.onEvent(patterns, handler)\n let subscriptionId: string\n try {\n const result = (await this.call('events.subscribe', { patterns })) as {\n subscriptionId: string\n }\n subscriptionId = result.subscriptionId\n } catch (err) {\n off()\n throw err\n }\n return async () => {\n off()\n await this.call('events.unsubscribe', { subscriptionId }).catch(() => {\n // Best-effort: the host may already have dropped the connection.\n })\n }\n }\n\n /** Host-persisted key/value state (see spec: State Storage). */\n readonly state = {\n get: async (\n keys?: string[],\n scope?: StateScope\n ): Promise<Record<string, unknown>> => {\n const result = (await this.call('state.get', {\n ...(scope ? { scope } : {}),\n ...(keys ? { keys } : {})\n })) as { values: Record<string, unknown> }\n return result.values ?? {}\n },\n set: async (\n values: Record<string, unknown>,\n scope?: StateScope\n ): Promise<void> => {\n await this.call('state.set', {\n ...(scope ? { scope } : {}),\n values\n })\n }\n }\n\n /** Signal K data relayed by the host (capabilities signalk.stream / .put). */\n readonly signalk = {\n /**\n * Subscribe to Signal K path values. The host publishes them as\n * `sk.<path>` events; this helper hides the event-name mapping and\n * establishes both the event-forwarding subscription and the host's\n * upstream Signal K subscription.\n */\n subscribe: async (\n paths: string[],\n handler: (ev: SignalKValueEvent) => void\n ): Promise<Unsubscribe> => {\n const patterns = paths.map((p) => `sk.${p}`)\n const offEvents = await this.subscribe(patterns, (_name, params) =>\n handler(params as SignalKValueEvent)\n )\n let subscriptionId: string\n try {\n const result = (await this.call('signalk.subscribe', { paths })) as {\n subscriptionId: string\n }\n subscriptionId = result.subscriptionId\n } catch (err) {\n await offEvents()\n throw err\n }\n return async () => {\n await offEvents()\n await this.call('signalk.unsubscribe', { subscriptionId }).catch(\n () => {}\n )\n }\n },\n put: (path: string, value: unknown): Promise<unknown> => {\n return this.call('signalk.put', { path, value })\n }\n }\n\n /**\n * The host's visible routes (capability `routes`). Thin **typed** wrappers over\n * the host's `route.*` methods — each just delegates to `this.call(...)`, so a\n * plain-JS extension can call `client.call('route.replace', …)` directly and a\n * TypeScript extension gets the typed `client.route.replace(…)` sugar with no\n * behavioural difference. Follow lifecycle + mutations by subscribing to\n * `route.**` events (`RouteVisibleEvent` / `RouteDirtyEvent` / `RouteSavedEvent`\n * / `RouteHiddenEvent`). Further operations (rename/point ops) extend this\n * surface as the capability fills out.\n */\n readonly route = {\n list: async (): Promise<RouteSummary[]> => {\n const result = (await this.call('route.list')) as {\n routes?: RouteSummary[]\n }\n return result.routes ?? []\n },\n create: async (opts: {\n points: RoutePoint[]\n name?: string\n description?: string\n }): Promise<{ routeId: string; rev: number }> => {\n // A route needs at least two points to form a segment; the host rejects\n // fewer with routes.badRequest.\n return (await this.call('route.create', opts)) as {\n routeId: string\n rev: number\n }\n },\n get: async (routeId: string): Promise<RouteData> => {\n return (await this.call('route.get', { routeId })) as RouteData\n },\n replace: async (\n routeId: string,\n points: RoutePoint[]\n ): Promise<{ rev: number }> => {\n return (await this.call('route.replace', { routeId, points })) as {\n rev: number\n }\n },\n save: async (\n routeId: string,\n opts?: { name?: string; description?: string; dialog?: boolean }\n ): Promise<{ href: string; rev: number }> => {\n return (await this.call('route.save', {\n routeId,\n ...(opts ?? {})\n })) as { href: string; rev: number }\n },\n show: async (ref: string): Promise<{ routeId: string; rev: number }> => {\n return (await this.call('route.show', { ref })) as {\n routeId: string\n rev: number\n }\n },\n hide: async (routeId: string): Promise<void> => {\n await this.call('route.hide', { routeId })\n },\n delete: async (routeId: string): Promise<void> => {\n await this.call('route.delete', { routeId })\n }\n }\n\n close(): void {\n this.endpoint.close()\n }\n}\n\n/**\n * Connect to the host from inside an extension iframe. Sends `bus.ready`\n * (repeating until answered) and resolves once the host's `bus.handshake`\n * arrives.\n */\nexport function connectExtension(\n opts: ConnectOptions = {}\n): Promise<ExtensionClient> {\n // Default transport: postMessage to the embedding window. Origin checks\n // are relaxed to '*' on the extension side because the host page's origin\n // may legitimately differ from the extension asset origin (e.g. a host dev\n // server embedding extension assets served by the Signal K server); the\n // peer-source check in windowPort still applies, and the host side\n // enforces a strict origin for the extension's frame.\n const port =\n opts.port ??\n windowPort((globalThis as unknown as Window).parent as Window, {\n origin: '*'\n })\n const endpoint = new BusEndpoint({\n port,\n callTimeoutMs: opts.callTimeoutMs,\n onError: opts.onError\n })\n return new Promise<ExtensionClient>((resolve, reject) => {\n let done = false\n const off = endpoint.onEvent([EVENT_HANDSHAKE], (_name, params) => {\n if (done) return\n done = true\n cleanup()\n resolve(new ExtensionClient(endpoint, params as Handshake))\n })\n const interval = setInterval(\n () => endpoint.notify(EVENT_READY),\n opts.readyIntervalMs ?? 250\n )\n const timeout = setTimeout(() => {\n if (done) return\n done = true\n cleanup()\n endpoint.close()\n reject(\n new RpcError('Timed out waiting for host handshake', {\n code: RPC_ERRORS.TIMEOUT,\n reason: 'HANDSHAKE_TIMEOUT'\n })\n )\n }, opts.timeoutMs ?? 10_000)\n const cleanup = () => {\n off()\n clearInterval(interval)\n clearTimeout(timeout)\n }\n endpoint.notify(EVENT_READY)\n })\n}\n\nexport * from './protocol'\nexport * from './wildcard'\nexport { BusEndpoint } from './endpoint'\nexport type { MethodHandler, MethodContext, EventHandler } from './endpoint'\nexport * from './port'\n","/**\n * Wire protocol types and constants for the Signal K plotter extension bus.\n *\n * The wire format is JSON-RPC 2.0 (https://www.jsonrpc.org/specification)\n * inside a routing envelope: `{ bus: \"plotterExt/1\", msg: <JSON-RPC object> }`.\n * Calls are JSON-RPC requests; events are JSON-RPC notifications whose\n * `method` is a hierarchical dot-separated event name.\n */\n\nexport const BUS_ID = 'plotterExt/1'\n\nexport type JsonRpcId = string | number\n\nexport interface JsonRpcRequest {\n jsonrpc: '2.0'\n id: JsonRpcId\n method: string\n params?: unknown\n}\n\nexport interface JsonRpcNotification {\n jsonrpc: '2.0'\n method: string\n params?: unknown\n}\n\nexport interface JsonRpcErrorObject {\n code: number\n message: string\n data?: { reason?: string; [key: string]: unknown }\n}\n\nexport interface JsonRpcSuccessResponse {\n jsonrpc: '2.0'\n id: JsonRpcId\n result: unknown\n}\n\nexport interface JsonRpcErrorResponse {\n jsonrpc: '2.0'\n id: JsonRpcId | null\n error: JsonRpcErrorObject\n}\n\nexport type JsonRpcResponse = JsonRpcSuccessResponse | JsonRpcErrorResponse\n\nexport type JsonRpcMessage =\n | JsonRpcRequest\n | JsonRpcNotification\n | JsonRpcResponse\n\nexport interface Envelope {\n bus: typeof BUS_ID\n msg: JsonRpcMessage\n}\n\n/**\n * JSON-RPC reserved codes for protocol errors, plus implementation-defined\n * codes (-32000..-32099 range) used by this package. Host API errors should\n * use HOST_ERROR with a stable string identifier in `error.data.reason`.\n */\nexport const RPC_ERRORS = {\n PARSE_ERROR: -32700,\n INVALID_REQUEST: -32600,\n METHOD_NOT_FOUND: -32601,\n INVALID_PARAMS: -32602,\n INTERNAL_ERROR: -32603,\n HOST_ERROR: -32000,\n TIMEOUT: -32001,\n CONNECTION_CLOSED: -32002\n} as const\n\nexport class RpcError extends Error {\n readonly code: number\n readonly data?: { reason?: string; [key: string]: unknown }\n\n constructor(\n message: string,\n opts: {\n code?: number\n reason?: string\n data?: Record<string, unknown>\n } = {}\n ) {\n super(message)\n this.name = 'RpcError'\n this.code = opts.code ?? RPC_ERRORS.HOST_ERROR\n const data: Record<string, unknown> = { ...(opts.data ?? {}) }\n if (opts.reason !== undefined) data.reason = opts.reason\n this.data = Object.keys(data).length > 0 ? data : undefined\n }\n\n get reason(): string | undefined {\n return typeof this.data?.reason === 'string' ? this.data.reason : undefined\n }\n\n toErrorObject(): JsonRpcErrorObject {\n return {\n code: this.code,\n message: this.message,\n ...(this.data ? { data: this.data } : {})\n }\n }\n\n static fromErrorObject(err: JsonRpcErrorObject): RpcError {\n return new RpcError(err.message, { code: err.code, data: err.data })\n }\n\n /** Normalize any thrown value into an RpcError suitable for the wire. */\n static from(err: unknown): RpcError {\n if (err instanceof RpcError) return err\n if (err instanceof Error) {\n return new RpcError(err.message, { code: RPC_ERRORS.INTERNAL_ERROR })\n }\n return new RpcError(String(err), { code: RPC_ERRORS.INTERNAL_ERROR })\n }\n}\n\n/** Reserved event names used to establish a connection. */\nexport const EVENT_READY = 'bus.ready'\nexport const EVENT_HANDSHAKE = 'bus.handshake'\n\nexport type ContextKind = 'panel' | 'widget' | 'background'\n\nexport interface HandshakeContext {\n kind: ContextKind\n /** Manifest-local contribution id. */\n id: string\n /** Host-assigned unique id for this placed instance (widgets). */\n instanceId?: string | null\n /** Widget instance a configuration panel was opened for. */\n targetInstance?: string | null\n /** Manifest-local widget id of the target instance (configuration panels). */\n targetWidget?: string | null\n}\n\nexport interface Handshake {\n host: string\n hostVersion: string\n apiVersion: string\n capabilities: string[]\n context: HandshakeContext\n}\n\n/** Payload of an `sk.<path>` Signal K value event. */\nexport interface SignalKValueEvent {\n path: string\n value: unknown\n timestamp?: string\n $source?: string\n}\n\nexport type StateScope = 'instance' | 'extension'\n\n/** Payload of a `state.changed` host event. */\nexport interface StateChangedEvent {\n scope: StateScope\n instanceId?: string | null\n keys: string[]\n}\n\n/**\n * Types for the `routes` capability — the routes the host currently has visible\n * on the chart (drafts plus stored routes the user is displaying), each\n * addressed by an opaque host-assigned `routeId`. Extensions read/write them and\n * follow lifecycle + mutation events (`route.visible` / `route.dirty` /\n * `route.saved` / `route.hidden`). Two orthogonal flags travel with a route:\n * `saved` (backed by a persisted resource) and `dirty` (has pending unsaved\n * changes). See the Plotter Extensions API spec, \"Live routes\".\n */\n\n/** A single point in a route edit buffer (`[lon, lat, alt?]`). */\nexport interface RoutePoint {\n position: [number, number, number?]\n name?: string\n description?: string\n}\n\n/** Snapshot of a route — result of `route.get`. */\nexport interface RouteData {\n routeId: string\n name: string | null\n /** Route-level description (distinct from a waypoint's RoutePoint.description). */\n description: string | null\n /** Monotonic revision; increments on every mutation. */\n rev: number\n /** Whether the route is backed by a persisted routes resource. */\n saved: boolean\n /** Whether the in-memory route has pending unsaved changes. */\n dirty: boolean\n points: RoutePoint[]\n}\n\n/** Summary entry in a `route.list` result (the visible set). */\nexport interface RouteSummary {\n routeId: string\n name: string | null\n rev: number\n pointCount: number\n saved: boolean\n dirty: boolean\n}\n\n/**\n * Payload of a `route.visible` host event — a route entered the visible set\n * (became rendered on the chart). A freshly drawn/created draft arrives\n * `saved:false, dirty:true`; a stored route brought into view arrives\n * `saved:true, dirty:false`.\n */\nexport interface RouteVisibleEvent {\n routeId: string\n rev: number\n name: string | null\n pointCount: number\n saved: boolean\n dirty: boolean\n}\n\n/**\n * Payload of a `route.hidden` host event — a route left the visible set.\n * `saved:true` ⇒ a stored route was made invisible (the resource is untouched);\n * `saved:false` ⇒ an unsaved draft was deleted (gone for good).\n */\nexport interface RouteHiddenEvent {\n routeId: string\n rev: number\n saved: boolean\n}\n\n/**\n * Payload of a `route.dirty` host event — the conformance-floor catch-all for\n * any structural/bulk change the host does not express granularly. A subscriber\n * should re-seed via `route.get`.\n */\nexport interface RouteDirtyEvent {\n routeId: string\n rev: number\n reason?: string\n}\n\n/**\n * Payload of a `route.saved` host event — the route's current state was\n * persisted to the routes resource collection. Arrives `saved:true,\n * dirty:false`; the route stays visible under the same `routeId`.\n */\nexport interface RouteSavedEvent {\n routeId: string\n rev: number\n /** Stored resource id of the persisted route. */\n href: string\n /** The route's name as persisted (may have just been set in the host's save\n * dialog), so followers can update a label without re-fetching. */\n name: string | null\n saved: boolean\n dirty: boolean\n}\n\n/** Stable `error.data.reason` strings for `route.*` host-method failures. */\nexport type RouteErrorReason =\n | 'routes.unknownId'\n | 'routes.badRequest'\n | 'routes.badRef'\n | 'routes.saveFailed'\n | 'routes.deleteFailed'\n | 'routes.saveCancelled'\n | 'routes.notSupported'\n","import {\n BUS_ID,\n Envelope,\n JsonRpcMessage,\n JsonRpcNotification,\n JsonRpcRequest,\n JsonRpcResponse\n} from './protocol'\n\nexport function wrap(msg: JsonRpcMessage): Envelope {\n return { bus: BUS_ID, msg }\n}\n\n/**\n * Returns the JSON-RPC message inside a bus envelope, or null when the data\n * is not valid protocol traffic (so unrelated postMessage noise is ignored).\n */\nexport function unwrap(data: unknown): JsonRpcMessage | null {\n if (typeof data !== 'object' || data === null) return null\n const env = data as Record<string, unknown>\n if (env.bus !== BUS_ID) return null\n return isJsonRpcMessage(env.msg) ? env.msg : null\n}\n\nexport function isJsonRpcMessage(v: unknown): v is JsonRpcMessage {\n if (typeof v !== 'object' || v === null) return false\n const m = v as Record<string, unknown>\n if (m.jsonrpc !== '2.0') return false\n if (typeof m.method === 'string') {\n // Request (with id) or notification (without).\n return (\n m.id === undefined ||\n typeof m.id === 'string' ||\n typeof m.id === 'number'\n )\n }\n // Response: id required (null allowed for unroutable errors), exactly one\n // of result / error.\n const idOk =\n typeof m.id === 'string' || typeof m.id === 'number' || m.id === null\n if (!idOk) return false\n const hasResult = 'result' in m\n const err = m.error as Record<string, unknown> | undefined\n const hasError =\n typeof err === 'object' &&\n err !== null &&\n typeof err.code === 'number' &&\n typeof err.message === 'string'\n return hasResult ? !('error' in m) : hasError\n}\n\nexport function isRequest(msg: JsonRpcMessage): msg is JsonRpcRequest {\n return 'method' in msg && 'id' in msg && msg.id !== undefined\n}\n\nexport function isNotification(\n msg: JsonRpcMessage\n): msg is JsonRpcNotification {\n return 'method' in msg && (!('id' in msg) || msg.id === undefined)\n}\n\nexport function isResponse(msg: JsonRpcMessage): msg is JsonRpcResponse {\n return !('method' in msg)\n}\n","/**\n * eventemitter2-style wildcard matching for dot-separated event names.\n *\n * - `*` matches exactly one segment.\n * - `**` matches zero or more segments (any remainder when trailing).\n */\nexport function matchesPattern(pattern: string, name: string): boolean {\n if (pattern === name) return true\n return match(pattern.split('.'), 0, name.split('.'), 0)\n}\n\nfunction match(p: string[], pi: number, n: string[], ni: number): boolean {\n while (pi < p.length) {\n const seg = p[pi]\n if (seg === '**') {\n if (pi === p.length - 1) return true\n for (let skip = ni; skip <= n.length; skip++) {\n if (match(p, pi + 1, n, skip)) return true\n }\n return false\n }\n if (ni >= n.length) return false\n if (seg !== '*' && seg !== n[ni]) return false\n pi++\n ni++\n }\n return ni === n.length\n}\n\nexport function matchesAny(patterns: Iterable<string>, name: string): boolean {\n for (const pattern of patterns) {\n if (matchesPattern(pattern, name)) return true\n }\n return false\n}\n","import {\n isNotification,\n isRequest,\n isResponse,\n unwrap,\n wrap\n} from './codec'\nimport {\n JsonRpcId,\n JsonRpcMessage,\n JsonRpcResponse,\n RPC_ERRORS,\n RpcError\n} from './protocol'\nimport { BusPort } from './port'\nimport { matchesAny } from './wildcard'\n\nexport interface MethodContext {\n endpoint: BusEndpoint\n}\n\nexport type MethodHandler = (\n params: unknown,\n ctx: MethodContext\n) => unknown | Promise<unknown>\n\nexport type EventHandler = (name: string, params: unknown) => void\n\nexport interface BusEndpointOptions {\n port: BusPort\n /** Default timeout for outgoing calls. */\n callTimeoutMs?: number\n /** Reporter for handler/dispatch errors. Defaults to console.warn. */\n onError?: (err: unknown) => void\n}\n\ninterface PendingCall {\n resolve: (value: unknown) => void\n reject: (err: RpcError) => void\n timer: ReturnType<typeof setTimeout> | null\n}\n\nconst DEFAULT_CALL_TIMEOUT_MS = 10_000\n\n/**\n * One end of the bus: sends/receives envelopes over a BusPort, dispatches\n * incoming requests to registered methods, correlates responses to pending\n * calls by per-call nonce, and fans incoming notifications out to wildcard\n * event handlers. Symmetric — both host and extension build on this.\n */\nexport class BusEndpoint {\n readonly callTimeoutMs: number\n\n private readonly port: BusPort\n private readonly unlisten: () => void\n private readonly onError: (err: unknown) => void\n private readonly pending = new Map<JsonRpcId, PendingCall>()\n private readonly methods = new Map<string, MethodHandler>()\n private readonly eventHandlers = new Set<{\n patterns: string[]\n fn: EventHandler\n }>()\n private readonly idPrefix = Math.random().toString(36).slice(2, 8)\n private seq = 0\n private closed = false\n\n constructor(opts: BusEndpointOptions) {\n this.port = opts.port\n this.callTimeoutMs = opts.callTimeoutMs ?? DEFAULT_CALL_TIMEOUT_MS\n this.onError =\n opts.onError ??\n ((err) => console.warn('[plotterext-bus]', err))\n this.unlisten = this.port.listen((data) => this.onData(data))\n }\n\n registerMethod(name: string, handler: MethodHandler): void {\n this.methods.set(name, handler)\n }\n\n unregisterMethod(name: string): void {\n this.methods.delete(name)\n }\n\n /**\n * Handle incoming notifications whose names match any of the wildcard\n * patterns. Returns an unsubscribe function. This is local dispatch only;\n * telling the peer which events to forward is a separate concern\n * (`events.subscribe`).\n */\n onEvent(patterns: string[], fn: EventHandler): () => void {\n const entry = { patterns, fn }\n this.eventHandlers.add(entry)\n return () => this.eventHandlers.delete(entry)\n }\n\n /** Send a notification (an event) to the peer. */\n notify(method: string, params?: unknown): void {\n this.send({ jsonrpc: '2.0', method, ...(params !== undefined ? { params } : {}) })\n }\n\n /** Call a method on the peer; resolves with its result. */\n call(\n method: string,\n params?: unknown,\n opts: { timeoutMs?: number } = {}\n ): Promise<unknown> {\n if (this.closed) {\n return Promise.reject(\n new RpcError('Bus endpoint is closed', {\n code: RPC_ERRORS.CONNECTION_CLOSED,\n reason: 'CLOSED'\n })\n )\n }\n const id = `${this.idPrefix}-${++this.seq}`\n const timeoutMs = opts.timeoutMs ?? this.callTimeoutMs\n return new Promise<unknown>((resolve, reject) => {\n const timer =\n timeoutMs > 0\n ? setTimeout(() => {\n this.pending.delete(id)\n reject(\n new RpcError(`Call timed out after ${timeoutMs}ms: ${method}`, {\n code: RPC_ERRORS.TIMEOUT,\n reason: 'TIMEOUT'\n })\n )\n }, timeoutMs)\n : null\n this.pending.set(id, { resolve, reject, timer })\n this.send({\n jsonrpc: '2.0',\n id,\n method,\n ...(params !== undefined ? { params } : {})\n })\n })\n }\n\n close(): void {\n if (this.closed) return\n this.closed = true\n this.unlisten()\n for (const [, p] of this.pending) {\n if (p.timer) clearTimeout(p.timer)\n p.reject(\n new RpcError('Bus endpoint closed', {\n code: RPC_ERRORS.CONNECTION_CLOSED,\n reason: 'CLOSED'\n })\n )\n }\n this.pending.clear()\n this.eventHandlers.clear()\n }\n\n private send(msg: JsonRpcMessage): void {\n if (this.closed) return\n this.port.post(wrap(msg))\n }\n\n private onData(data: unknown): void {\n const msg = unwrap(data)\n if (!msg) return\n if (isResponse(msg)) {\n this.onResponse(msg)\n } else if (isRequest(msg)) {\n void this.onRequest(msg)\n } else if (isNotification(msg)) {\n this.onNotification(msg.method, msg.params)\n }\n }\n\n private onResponse(msg: JsonRpcResponse): void {\n if (msg.id === null) return\n const p = this.pending.get(msg.id)\n if (!p) return\n this.pending.delete(msg.id)\n if (p.timer) clearTimeout(p.timer)\n if ('error' in msg) {\n p.reject(RpcError.fromErrorObject(msg.error))\n } else {\n p.resolve(msg.result)\n }\n }\n\n private async onRequest(msg: {\n id: JsonRpcId\n method: string\n params?: unknown\n }): Promise<void> {\n const handler = this.methods.get(msg.method)\n if (!handler) {\n this.send({\n jsonrpc: '2.0',\n id: msg.id,\n error: {\n code: RPC_ERRORS.METHOD_NOT_FOUND,\n message: `Method not found: ${msg.method}`\n }\n })\n return\n }\n try {\n const result = await handler(msg.params, { endpoint: this })\n this.send({\n jsonrpc: '2.0',\n id: msg.id,\n result: result === undefined ? null : result\n })\n } catch (err) {\n this.send({\n jsonrpc: '2.0',\n id: msg.id,\n error: RpcError.from(err).toErrorObject()\n })\n }\n }\n\n private onNotification(name: string, params: unknown): void {\n for (const entry of [...this.eventHandlers]) {\n if (matchesAny(entry.patterns, name)) {\n try {\n entry.fn(name, params)\n } catch (err) {\n this.onError(err)\n }\n }\n }\n }\n}\n","/**\n * Transport abstraction. The bus runs over anything that can post and\n * receive structured-cloneable data: window.postMessage between a host page\n * and an iframe, a MessageChannel port, or a test double.\n */\nexport interface BusPort {\n post(data: unknown): void\n /** Register a receive handler; returns an unlisten function. */\n listen(handler: (data: unknown) => void): () => void\n}\n\nexport interface WindowPortOptions {\n /**\n * targetOrigin for outgoing postMessage and required origin of incoming\n * messages. Defaults to the listening window's own origin (the baseline\n * deployment is same-origin). Pass '*' to disable origin checks.\n */\n origin?: string\n /** Window whose 'message' events are listened to. Defaults to globalThis. */\n listenWindow?: Window\n}\n\n/**\n * A port over window.postMessage to a peer window. On the host side the peer\n * is an iframe's contentWindow; on the extension side it is window.parent.\n * Only messages whose source is the peer (and whose origin matches) are\n * delivered.\n */\nexport function windowPort(peer: Window, opts: WindowPortOptions = {}): BusPort {\n const listenWindow =\n opts.listenWindow ?? (globalThis as unknown as Window)\n const origin = opts.origin ?? listenWindow.location?.origin ?? '*'\n return {\n post(data) {\n peer.postMessage(data, origin)\n },\n listen(handler) {\n const fn = (ev: MessageEvent) => {\n if (ev.source !== peer) return\n if (origin !== '*' && ev.origin !== origin) return\n handler(ev.data)\n }\n listenWindow.addEventListener('message', fn as EventListener)\n return () =>\n listenWindow.removeEventListener('message', fn as EventListener)\n }\n }\n}\n\ninterface MessagePortLike {\n postMessage(data: unknown): void\n addEventListener(type: 'message', fn: (ev: { data: unknown }) => void): void\n removeEventListener(\n type: 'message',\n fn: (ev: { data: unknown }) => void\n ): void\n start?(): void\n}\n\n/** A port over a MessageChannel/MessagePort (browser or Node). */\nexport function messagePort(port: MessagePortLike): BusPort {\n return {\n post(data) {\n port.postMessage(data)\n },\n listen(handler) {\n const fn = (ev: { data: unknown }) => handler(ev.data)\n port.addEventListener('message', fn)\n port.start?.()\n return () => port.removeEventListener('message', fn)\n }\n }\n}\n"],"mappings":"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","names":[]}
@@ -1,5 +1,5 @@
1
- import { f as BusPort, H as Handshake, d as BusEndpoint, j as HandshakeContext, i as EventHandler, z as StateScope, S as SignalKValueEvent, v as RouteSummary, t as RoutePoint, p as RouteData } from './wildcard-BHRv-WAr.cjs';
2
- export { B as BUS_ID, C as ContextKind, g as EVENT_HANDSHAKE, h as EVENT_READY, E as Envelope, k as JsonRpcErrorObject, l as JsonRpcErrorResponse, m as JsonRpcId, J as JsonRpcMessage, a as JsonRpcNotification, b as JsonRpcRequest, c as JsonRpcResponse, n as JsonRpcSuccessResponse, M as MethodContext, o as MethodHandler, R as RPC_ERRORS, q as RouteDirtyEvent, r as RouteErrorReason, s as RouteHiddenEvent, u as RouteSavedEvent, w as RouteVisibleEvent, x as RpcError, y as StateChangedEvent, W as WindowPortOptions, A as matchesAny, D as matchesPattern, F as messagePort, G as windowPort } from './wildcard-BHRv-WAr.cjs';
1
+ import { f as BusPort, H as Handshake, d as BusEndpoint, j as HandshakeContext, i as EventHandler, z as StateScope, S as SignalKValueEvent, v as RouteSummary, t as RoutePoint, p as RouteData } from './wildcard-B5Dp4MA1.cjs';
2
+ export { B as BUS_ID, C as ContextKind, g as EVENT_HANDSHAKE, h as EVENT_READY, E as Envelope, k as JsonRpcErrorObject, l as JsonRpcErrorResponse, m as JsonRpcId, J as JsonRpcMessage, a as JsonRpcNotification, b as JsonRpcRequest, c as JsonRpcResponse, n as JsonRpcSuccessResponse, M as MethodContext, o as MethodHandler, R as RPC_ERRORS, q as RouteDirtyEvent, r as RouteErrorReason, s as RouteHiddenEvent, u as RouteSavedEvent, w as RouteVisibleEvent, x as RpcError, y as StateChangedEvent, W as WindowPortOptions, A as matchesAny, D as matchesPattern, F as messagePort, G as windowPort } from './wildcard-B5Dp4MA1.cjs';
3
3
 
4
4
  interface ConnectOptions {
5
5
  /** Transport. Defaults to window.postMessage to window.parent. */
@@ -1,5 +1,5 @@
1
- import { f as BusPort, H as Handshake, d as BusEndpoint, j as HandshakeContext, i as EventHandler, z as StateScope, S as SignalKValueEvent, v as RouteSummary, t as RoutePoint, p as RouteData } from './wildcard-BHRv-WAr.js';
2
- export { B as BUS_ID, C as ContextKind, g as EVENT_HANDSHAKE, h as EVENT_READY, E as Envelope, k as JsonRpcErrorObject, l as JsonRpcErrorResponse, m as JsonRpcId, J as JsonRpcMessage, a as JsonRpcNotification, b as JsonRpcRequest, c as JsonRpcResponse, n as JsonRpcSuccessResponse, M as MethodContext, o as MethodHandler, R as RPC_ERRORS, q as RouteDirtyEvent, r as RouteErrorReason, s as RouteHiddenEvent, u as RouteSavedEvent, w as RouteVisibleEvent, x as RpcError, y as StateChangedEvent, W as WindowPortOptions, A as matchesAny, D as matchesPattern, F as messagePort, G as windowPort } from './wildcard-BHRv-WAr.js';
1
+ import { f as BusPort, H as Handshake, d as BusEndpoint, j as HandshakeContext, i as EventHandler, z as StateScope, S as SignalKValueEvent, v as RouteSummary, t as RoutePoint, p as RouteData } from './wildcard-B5Dp4MA1.js';
2
+ export { B as BUS_ID, C as ContextKind, g as EVENT_HANDSHAKE, h as EVENT_READY, E as Envelope, k as JsonRpcErrorObject, l as JsonRpcErrorResponse, m as JsonRpcId, J as JsonRpcMessage, a as JsonRpcNotification, b as JsonRpcRequest, c as JsonRpcResponse, n as JsonRpcSuccessResponse, M as MethodContext, o as MethodHandler, R as RPC_ERRORS, q as RouteDirtyEvent, r as RouteErrorReason, s as RouteHiddenEvent, u as RouteSavedEvent, w as RouteVisibleEvent, x as RpcError, y as StateChangedEvent, W as WindowPortOptions, A as matchesAny, D as matchesPattern, F as messagePort, G as windowPort } from './wildcard-B5Dp4MA1.js';
3
3
 
4
4
  interface ConnectOptions {
5
5
  /** Transport. Defaults to window.postMessage to window.parent. */
package/dist/extension.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  ExtensionClient,
3
3
  connectExtension
4
- } from "./chunk-YZVRR7LW.js";
4
+ } from "./chunk-YL2LWXNX.js";
5
5
  import {
6
6
  BUS_ID,
7
7
  BusEndpoint,
@@ -13,7 +13,7 @@ import {
13
13
  matchesPattern,
14
14
  messagePort,
15
15
  windowPort
16
- } from "./chunk-VBS4I6NW.js";
16
+ } from "./chunk-LVGWVV2W.js";
17
17
  export {
18
18
  BUS_ID,
19
19
  BusEndpoint,
package/dist/host.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/host.ts","../src/protocol.ts","../src/codec.ts","../src/wildcard.ts","../src/endpoint.ts","../src/port.ts"],"sourcesContent":["import { BusEndpoint, MethodHandler } from './endpoint'\nimport { BusPort } from './port'\nimport {\n EVENT_HANDSHAKE,\n EVENT_READY,\n HandshakeContext,\n RPC_ERRORS,\n RpcError\n} from './protocol'\nimport { matchesAny } from './wildcard'\n\nexport interface HostInfo {\n host: string\n hostVersion: string\n apiVersion: string\n capabilities: string[]\n}\n\nexport interface HostConnectionOptions {\n port: BusPort\n hostInfo: HostInfo\n context: HandshakeContext\n /** Host API methods (e.g. 'state.get', 'signalk.subscribe', 'map.center'). */\n methods?: Record<string, MethodHandler>\n callTimeoutMs?: number\n onError?: (err: unknown) => void\n /**\n * Called whenever the union of subscribed event patterns changes, with the\n * current pattern list. Lets the host start/stop upstream work lazily.\n */\n onSubscriptionsChanged?: (patterns: string[]) => void\n}\n\n/**\n * The host side of one extension context (one iframe). Sends the handshake\n * in reply to `bus.ready`, dispatches host API methods, and tracks the\n * context's event subscriptions so `publish()` only forwards events the\n * context asked for.\n */\nexport class HostConnection {\n readonly endpoint: BusEndpoint\n readonly context: HandshakeContext\n\n private readonly hostInfo: HostInfo\n private readonly subs = new Map<string, string[]>()\n private readonly onSubscriptionsChanged?: (patterns: string[]) => void\n private subSeq = 0\n\n constructor(opts: HostConnectionOptions) {\n this.hostInfo = opts.hostInfo\n this.context = opts.context\n this.onSubscriptionsChanged = opts.onSubscriptionsChanged\n this.endpoint = new BusEndpoint({\n port: opts.port,\n callTimeoutMs: opts.callTimeoutMs,\n onError: opts.onError\n })\n for (const [name, handler] of Object.entries(opts.methods ?? {})) {\n this.endpoint.registerMethod(name, handler)\n }\n this.endpoint.registerMethod('events.subscribe', (params) =>\n this.handleSubscribe(params)\n )\n this.endpoint.registerMethod('events.unsubscribe', (params) =>\n this.handleUnsubscribe(params)\n )\n this.endpoint.onEvent([EVENT_READY], () => this.sendHandshake())\n }\n\n registerMethod(name: string, handler: MethodHandler): void {\n this.endpoint.registerMethod(name, handler)\n }\n\n /**\n * Publish an event to this context. Delivered only when the context has a\n * matching subscription; returns whether it was delivered.\n */\n publish(eventName: string, params?: unknown): boolean {\n if (!this.hasSubscriber(eventName)) return false\n this.endpoint.notify(eventName, params)\n return true\n }\n\n hasSubscriber(eventName: string): boolean {\n for (const [, patterns] of this.subs) {\n if (matchesAny(patterns, eventName)) return true\n }\n return false\n }\n\n /** Union of currently subscribed patterns. */\n subscribedPatterns(): string[] {\n const all = new Set<string>()\n for (const [, patterns] of this.subs) {\n for (const p of patterns) all.add(p)\n }\n return [...all]\n }\n\n close(): void {\n this.endpoint.close()\n this.subs.clear()\n }\n\n private sendHandshake(): void {\n this.endpoint.notify(EVENT_HANDSHAKE, {\n ...this.hostInfo,\n context: this.context\n })\n }\n\n private handleSubscribe(params: unknown): { subscriptionId: string } {\n const patterns = (params as { patterns?: unknown })?.patterns\n if (\n !Array.isArray(patterns) ||\n patterns.length === 0 ||\n !patterns.every((p) => typeof p === 'string' && p.length > 0)\n ) {\n throw new RpcError('events.subscribe requires a non-empty patterns array', {\n code: RPC_ERRORS.INVALID_PARAMS,\n reason: 'INVALID_PATTERNS'\n })\n }\n const subscriptionId = `sub-${++this.subSeq}`\n this.subs.set(subscriptionId, patterns as string[])\n this.onSubscriptionsChanged?.(this.subscribedPatterns())\n return { subscriptionId }\n }\n\n private handleUnsubscribe(params: unknown): Record<string, never> {\n const id = (params as { subscriptionId?: unknown })?.subscriptionId\n if (typeof id !== 'string' || !this.subs.has(id)) {\n throw new RpcError('Unknown subscriptionId', {\n code: RPC_ERRORS.INVALID_PARAMS,\n reason: 'UNKNOWN_SUBSCRIPTION'\n })\n }\n this.subs.delete(id)\n this.onSubscriptionsChanged?.(this.subscribedPatterns())\n return {}\n }\n}\n\nexport * from './protocol'\nexport * from './wildcard'\nexport { BusEndpoint } from './endpoint'\nexport type { MethodHandler, MethodContext, EventHandler } from './endpoint'\nexport * from './port'\n","/**\n * Wire protocol types and constants for the Signal K plotter extension bus.\n *\n * The wire format is JSON-RPC 2.0 (https://www.jsonrpc.org/specification)\n * inside a routing envelope: `{ bus: \"plotterExt/1\", msg: <JSON-RPC object> }`.\n * Calls are JSON-RPC requests; events are JSON-RPC notifications whose\n * `method` is a hierarchical dot-separated event name.\n */\n\nexport const BUS_ID = 'plotterExt/1'\n\nexport type JsonRpcId = string | number\n\nexport interface JsonRpcRequest {\n jsonrpc: '2.0'\n id: JsonRpcId\n method: string\n params?: unknown\n}\n\nexport interface JsonRpcNotification {\n jsonrpc: '2.0'\n method: string\n params?: unknown\n}\n\nexport interface JsonRpcErrorObject {\n code: number\n message: string\n data?: { reason?: string; [key: string]: unknown }\n}\n\nexport interface JsonRpcSuccessResponse {\n jsonrpc: '2.0'\n id: JsonRpcId\n result: unknown\n}\n\nexport interface JsonRpcErrorResponse {\n jsonrpc: '2.0'\n id: JsonRpcId | null\n error: JsonRpcErrorObject\n}\n\nexport type JsonRpcResponse = JsonRpcSuccessResponse | JsonRpcErrorResponse\n\nexport type JsonRpcMessage =\n | JsonRpcRequest\n | JsonRpcNotification\n | JsonRpcResponse\n\nexport interface Envelope {\n bus: typeof BUS_ID\n msg: JsonRpcMessage\n}\n\n/**\n * JSON-RPC reserved codes for protocol errors, plus implementation-defined\n * codes (-32000..-32099 range) used by this package. Host API errors should\n * use HOST_ERROR with a stable string identifier in `error.data.reason`.\n */\nexport const RPC_ERRORS = {\n PARSE_ERROR: -32700,\n INVALID_REQUEST: -32600,\n METHOD_NOT_FOUND: -32601,\n INVALID_PARAMS: -32602,\n INTERNAL_ERROR: -32603,\n HOST_ERROR: -32000,\n TIMEOUT: -32001,\n CONNECTION_CLOSED: -32002\n} as const\n\nexport class RpcError extends Error {\n readonly code: number\n readonly data?: { reason?: string; [key: string]: unknown }\n\n constructor(\n message: string,\n opts: {\n code?: number\n reason?: string\n data?: Record<string, unknown>\n } = {}\n ) {\n super(message)\n this.name = 'RpcError'\n this.code = opts.code ?? RPC_ERRORS.HOST_ERROR\n const data: Record<string, unknown> = { ...(opts.data ?? {}) }\n if (opts.reason !== undefined) data.reason = opts.reason\n this.data = Object.keys(data).length > 0 ? data : undefined\n }\n\n get reason(): string | undefined {\n return typeof this.data?.reason === 'string' ? this.data.reason : undefined\n }\n\n toErrorObject(): JsonRpcErrorObject {\n return {\n code: this.code,\n message: this.message,\n ...(this.data ? { data: this.data } : {})\n }\n }\n\n static fromErrorObject(err: JsonRpcErrorObject): RpcError {\n return new RpcError(err.message, { code: err.code, data: err.data })\n }\n\n /** Normalize any thrown value into an RpcError suitable for the wire. */\n static from(err: unknown): RpcError {\n if (err instanceof RpcError) return err\n if (err instanceof Error) {\n return new RpcError(err.message, { code: RPC_ERRORS.INTERNAL_ERROR })\n }\n return new RpcError(String(err), { code: RPC_ERRORS.INTERNAL_ERROR })\n }\n}\n\n/** Reserved event names used to establish a connection. */\nexport const EVENT_READY = 'bus.ready'\nexport const EVENT_HANDSHAKE = 'bus.handshake'\n\nexport type ContextKind = 'panel' | 'widget' | 'background'\n\nexport interface HandshakeContext {\n kind: ContextKind\n /** Manifest-local contribution id. */\n id: string\n /** Host-assigned unique id for this placed instance (widgets). */\n instanceId?: string | null\n /** Widget instance a configuration panel was opened for. */\n targetInstance?: string | null\n /** Manifest-local widget id of the target instance (configuration panels). */\n targetWidget?: string | null\n}\n\nexport interface Handshake {\n host: string\n hostVersion: string\n apiVersion: string\n capabilities: string[]\n context: HandshakeContext\n}\n\n/** Payload of an `sk.<path>` Signal K value event. */\nexport interface SignalKValueEvent {\n path: string\n value: unknown\n timestamp?: string\n $source?: string\n}\n\nexport type StateScope = 'instance' | 'extension'\n\n/** Payload of a `state.changed` host event. */\nexport interface StateChangedEvent {\n scope: StateScope\n instanceId?: string | null\n keys: string[]\n}\n\n/**\n * Types for the `routes` capability — the routes the host currently has visible\n * on the chart (drafts plus stored routes the user is displaying), each\n * addressed by an opaque host-assigned `routeId`. Extensions read/write them and\n * follow lifecycle + mutation events (`route.visible` / `route.dirty` /\n * `route.saved` / `route.hidden`). Two orthogonal flags travel with a route:\n * `saved` (backed by a persisted resource) and `dirty` (has pending unsaved\n * changes). See the Plotter Extensions API spec, \"Live routes\".\n */\n\n/** A single point in a route edit buffer (`[lon, lat, alt?]`). */\nexport interface RoutePoint {\n position: [number, number, number?]\n name?: string\n description?: string\n}\n\n/** Snapshot of a route — result of `route.get`. */\nexport interface RouteData {\n routeId: string\n name: string | null\n /** Route-level description (distinct from a waypoint's RoutePoint.description). */\n description: string | null\n /** Monotonic revision; increments on every mutation. */\n rev: number\n /** Whether the route is backed by a persisted routes resource. */\n saved: boolean\n /** Whether the in-memory route has pending unsaved changes. */\n dirty: boolean\n points: RoutePoint[]\n}\n\n/** Summary entry in a `route.list` result (the visible set). */\nexport interface RouteSummary {\n routeId: string\n name: string | null\n rev: number\n pointCount: number\n saved: boolean\n dirty: boolean\n}\n\n/**\n * Payload of a `route.visible` host event — a route entered the visible set\n * (became rendered on the chart). A freshly drawn/created draft arrives\n * `saved:false, dirty:true`; a stored route brought into view arrives\n * `saved:true, dirty:false`.\n */\nexport interface RouteVisibleEvent {\n routeId: string\n rev: number\n name: string | null\n pointCount: number\n saved: boolean\n dirty: boolean\n}\n\n/**\n * Payload of a `route.hidden` host event — a route left the visible set.\n * `saved:true` ⇒ a stored route was made invisible (the resource is untouched);\n * `saved:false` ⇒ an unsaved draft was deleted (gone for good).\n */\nexport interface RouteHiddenEvent {\n routeId: string\n rev: number\n saved: boolean\n}\n\n/**\n * Payload of a `route.dirty` host event — the conformance-floor catch-all for\n * any structural/bulk change the host does not express granularly. A subscriber\n * should re-seed via `route.get`.\n */\nexport interface RouteDirtyEvent {\n routeId: string\n rev: number\n reason?: string\n}\n\n/**\n * Payload of a `route.saved` host event — the route's current state was\n * persisted to the routes resource collection. Arrives `saved:true,\n * dirty:false`; the route stays visible under the same `routeId`.\n */\nexport interface RouteSavedEvent {\n routeId: string\n rev: number\n /** Stored resource id of the persisted route. */\n href: string\n /** The route's name as persisted (may have just been set in the host's save\n * dialog), so followers can update a label without re-fetching. */\n name: string | null\n saved: boolean\n dirty: boolean\n}\n\n/** Stable `error.data.reason` strings for `route.*` host-method failures. */\nexport type RouteErrorReason =\n | 'routes.unknownId'\n | 'routes.badRequest'\n | 'routes.badRef'\n | 'routes.saveFailed'\n | 'routes.saveCancelled'\n | 'routes.notSupported'\n","import {\n BUS_ID,\n Envelope,\n JsonRpcMessage,\n JsonRpcNotification,\n JsonRpcRequest,\n JsonRpcResponse\n} from './protocol'\n\nexport function wrap(msg: JsonRpcMessage): Envelope {\n return { bus: BUS_ID, msg }\n}\n\n/**\n * Returns the JSON-RPC message inside a bus envelope, or null when the data\n * is not valid protocol traffic (so unrelated postMessage noise is ignored).\n */\nexport function unwrap(data: unknown): JsonRpcMessage | null {\n if (typeof data !== 'object' || data === null) return null\n const env = data as Record<string, unknown>\n if (env.bus !== BUS_ID) return null\n return isJsonRpcMessage(env.msg) ? env.msg : null\n}\n\nexport function isJsonRpcMessage(v: unknown): v is JsonRpcMessage {\n if (typeof v !== 'object' || v === null) return false\n const m = v as Record<string, unknown>\n if (m.jsonrpc !== '2.0') return false\n if (typeof m.method === 'string') {\n // Request (with id) or notification (without).\n return (\n m.id === undefined ||\n typeof m.id === 'string' ||\n typeof m.id === 'number'\n )\n }\n // Response: id required (null allowed for unroutable errors), exactly one\n // of result / error.\n const idOk =\n typeof m.id === 'string' || typeof m.id === 'number' || m.id === null\n if (!idOk) return false\n const hasResult = 'result' in m\n const err = m.error as Record<string, unknown> | undefined\n const hasError =\n typeof err === 'object' &&\n err !== null &&\n typeof err.code === 'number' &&\n typeof err.message === 'string'\n return hasResult ? !('error' in m) : hasError\n}\n\nexport function isRequest(msg: JsonRpcMessage): msg is JsonRpcRequest {\n return 'method' in msg && 'id' in msg && msg.id !== undefined\n}\n\nexport function isNotification(\n msg: JsonRpcMessage\n): msg is JsonRpcNotification {\n return 'method' in msg && (!('id' in msg) || msg.id === undefined)\n}\n\nexport function isResponse(msg: JsonRpcMessage): msg is JsonRpcResponse {\n return !('method' in msg)\n}\n","/**\n * eventemitter2-style wildcard matching for dot-separated event names.\n *\n * - `*` matches exactly one segment.\n * - `**` matches zero or more segments (any remainder when trailing).\n */\nexport function matchesPattern(pattern: string, name: string): boolean {\n if (pattern === name) return true\n return match(pattern.split('.'), 0, name.split('.'), 0)\n}\n\nfunction match(p: string[], pi: number, n: string[], ni: number): boolean {\n while (pi < p.length) {\n const seg = p[pi]\n if (seg === '**') {\n if (pi === p.length - 1) return true\n for (let skip = ni; skip <= n.length; skip++) {\n if (match(p, pi + 1, n, skip)) return true\n }\n return false\n }\n if (ni >= n.length) return false\n if (seg !== '*' && seg !== n[ni]) return false\n pi++\n ni++\n }\n return ni === n.length\n}\n\nexport function matchesAny(patterns: Iterable<string>, name: string): boolean {\n for (const pattern of patterns) {\n if (matchesPattern(pattern, name)) return true\n }\n return false\n}\n","import {\n isNotification,\n isRequest,\n isResponse,\n unwrap,\n wrap\n} from './codec'\nimport {\n JsonRpcId,\n JsonRpcMessage,\n JsonRpcResponse,\n RPC_ERRORS,\n RpcError\n} from './protocol'\nimport { BusPort } from './port'\nimport { matchesAny } from './wildcard'\n\nexport interface MethodContext {\n endpoint: BusEndpoint\n}\n\nexport type MethodHandler = (\n params: unknown,\n ctx: MethodContext\n) => unknown | Promise<unknown>\n\nexport type EventHandler = (name: string, params: unknown) => void\n\nexport interface BusEndpointOptions {\n port: BusPort\n /** Default timeout for outgoing calls. */\n callTimeoutMs?: number\n /** Reporter for handler/dispatch errors. Defaults to console.warn. */\n onError?: (err: unknown) => void\n}\n\ninterface PendingCall {\n resolve: (value: unknown) => void\n reject: (err: RpcError) => void\n timer: ReturnType<typeof setTimeout> | null\n}\n\nconst DEFAULT_CALL_TIMEOUT_MS = 10_000\n\n/**\n * One end of the bus: sends/receives envelopes over a BusPort, dispatches\n * incoming requests to registered methods, correlates responses to pending\n * calls by per-call nonce, and fans incoming notifications out to wildcard\n * event handlers. Symmetric — both host and extension build on this.\n */\nexport class BusEndpoint {\n readonly callTimeoutMs: number\n\n private readonly port: BusPort\n private readonly unlisten: () => void\n private readonly onError: (err: unknown) => void\n private readonly pending = new Map<JsonRpcId, PendingCall>()\n private readonly methods = new Map<string, MethodHandler>()\n private readonly eventHandlers = new Set<{\n patterns: string[]\n fn: EventHandler\n }>()\n private readonly idPrefix = Math.random().toString(36).slice(2, 8)\n private seq = 0\n private closed = false\n\n constructor(opts: BusEndpointOptions) {\n this.port = opts.port\n this.callTimeoutMs = opts.callTimeoutMs ?? DEFAULT_CALL_TIMEOUT_MS\n this.onError =\n opts.onError ??\n ((err) => console.warn('[plotterext-bus]', err))\n this.unlisten = this.port.listen((data) => this.onData(data))\n }\n\n registerMethod(name: string, handler: MethodHandler): void {\n this.methods.set(name, handler)\n }\n\n unregisterMethod(name: string): void {\n this.methods.delete(name)\n }\n\n /**\n * Handle incoming notifications whose names match any of the wildcard\n * patterns. Returns an unsubscribe function. This is local dispatch only;\n * telling the peer which events to forward is a separate concern\n * (`events.subscribe`).\n */\n onEvent(patterns: string[], fn: EventHandler): () => void {\n const entry = { patterns, fn }\n this.eventHandlers.add(entry)\n return () => this.eventHandlers.delete(entry)\n }\n\n /** Send a notification (an event) to the peer. */\n notify(method: string, params?: unknown): void {\n this.send({ jsonrpc: '2.0', method, ...(params !== undefined ? { params } : {}) })\n }\n\n /** Call a method on the peer; resolves with its result. */\n call(\n method: string,\n params?: unknown,\n opts: { timeoutMs?: number } = {}\n ): Promise<unknown> {\n if (this.closed) {\n return Promise.reject(\n new RpcError('Bus endpoint is closed', {\n code: RPC_ERRORS.CONNECTION_CLOSED,\n reason: 'CLOSED'\n })\n )\n }\n const id = `${this.idPrefix}-${++this.seq}`\n const timeoutMs = opts.timeoutMs ?? this.callTimeoutMs\n return new Promise<unknown>((resolve, reject) => {\n const timer =\n timeoutMs > 0\n ? setTimeout(() => {\n this.pending.delete(id)\n reject(\n new RpcError(`Call timed out after ${timeoutMs}ms: ${method}`, {\n code: RPC_ERRORS.TIMEOUT,\n reason: 'TIMEOUT'\n })\n )\n }, timeoutMs)\n : null\n this.pending.set(id, { resolve, reject, timer })\n this.send({\n jsonrpc: '2.0',\n id,\n method,\n ...(params !== undefined ? { params } : {})\n })\n })\n }\n\n close(): void {\n if (this.closed) return\n this.closed = true\n this.unlisten()\n for (const [, p] of this.pending) {\n if (p.timer) clearTimeout(p.timer)\n p.reject(\n new RpcError('Bus endpoint closed', {\n code: RPC_ERRORS.CONNECTION_CLOSED,\n reason: 'CLOSED'\n })\n )\n }\n this.pending.clear()\n this.eventHandlers.clear()\n }\n\n private send(msg: JsonRpcMessage): void {\n if (this.closed) return\n this.port.post(wrap(msg))\n }\n\n private onData(data: unknown): void {\n const msg = unwrap(data)\n if (!msg) return\n if (isResponse(msg)) {\n this.onResponse(msg)\n } else if (isRequest(msg)) {\n void this.onRequest(msg)\n } else if (isNotification(msg)) {\n this.onNotification(msg.method, msg.params)\n }\n }\n\n private onResponse(msg: JsonRpcResponse): void {\n if (msg.id === null) return\n const p = this.pending.get(msg.id)\n if (!p) return\n this.pending.delete(msg.id)\n if (p.timer) clearTimeout(p.timer)\n if ('error' in msg) {\n p.reject(RpcError.fromErrorObject(msg.error))\n } else {\n p.resolve(msg.result)\n }\n }\n\n private async onRequest(msg: {\n id: JsonRpcId\n method: string\n params?: unknown\n }): Promise<void> {\n const handler = this.methods.get(msg.method)\n if (!handler) {\n this.send({\n jsonrpc: '2.0',\n id: msg.id,\n error: {\n code: RPC_ERRORS.METHOD_NOT_FOUND,\n message: `Method not found: ${msg.method}`\n }\n })\n return\n }\n try {\n const result = await handler(msg.params, { endpoint: this })\n this.send({\n jsonrpc: '2.0',\n id: msg.id,\n result: result === undefined ? null : result\n })\n } catch (err) {\n this.send({\n jsonrpc: '2.0',\n id: msg.id,\n error: RpcError.from(err).toErrorObject()\n })\n }\n }\n\n private onNotification(name: string, params: unknown): void {\n for (const entry of [...this.eventHandlers]) {\n if (matchesAny(entry.patterns, name)) {\n try {\n entry.fn(name, params)\n } catch (err) {\n this.onError(err)\n }\n }\n }\n }\n}\n","/**\n * Transport abstraction. The bus runs over anything that can post and\n * receive structured-cloneable data: window.postMessage between a host page\n * and an iframe, a MessageChannel port, or a test double.\n */\nexport interface BusPort {\n post(data: unknown): void\n /** Register a receive handler; returns an unlisten function. */\n listen(handler: (data: unknown) => void): () => void\n}\n\nexport interface WindowPortOptions {\n /**\n * targetOrigin for outgoing postMessage and required origin of incoming\n * messages. Defaults to the listening window's own origin (the baseline\n * deployment is same-origin). Pass '*' to disable origin checks.\n */\n origin?: string\n /** Window whose 'message' events are listened to. Defaults to globalThis. */\n listenWindow?: Window\n}\n\n/**\n * A port over window.postMessage to a peer window. On the host side the peer\n * is an iframe's contentWindow; on the extension side it is window.parent.\n * Only messages whose source is the peer (and whose origin matches) are\n * delivered.\n */\nexport function windowPort(peer: Window, opts: WindowPortOptions = {}): BusPort {\n const listenWindow =\n opts.listenWindow ?? (globalThis as unknown as Window)\n const origin = opts.origin ?? listenWindow.location?.origin ?? '*'\n return {\n post(data) {\n peer.postMessage(data, origin)\n },\n listen(handler) {\n const fn = (ev: MessageEvent) => {\n if (ev.source !== peer) return\n if (origin !== '*' && ev.origin !== origin) return\n handler(ev.data)\n }\n listenWindow.addEventListener('message', fn as EventListener)\n return () =>\n listenWindow.removeEventListener('message', fn as EventListener)\n }\n }\n}\n\ninterface MessagePortLike {\n postMessage(data: unknown): void\n addEventListener(type: 'message', fn: (ev: { data: unknown }) => void): void\n removeEventListener(\n type: 'message',\n fn: (ev: { data: unknown }) => void\n ): void\n start?(): void\n}\n\n/** A port over a MessageChannel/MessagePort (browser or Node). */\nexport function messagePort(port: MessagePortLike): BusPort {\n return {\n post(data) {\n port.postMessage(data)\n },\n listen(handler) {\n const fn = (ev: { data: unknown }) => handler(ev.data)\n port.addEventListener('message', fn)\n port.start?.()\n return () => port.removeEventListener('message', fn)\n }\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/host.ts","../src/protocol.ts","../src/codec.ts","../src/wildcard.ts","../src/endpoint.ts","../src/port.ts"],"sourcesContent":["import { BusEndpoint, MethodHandler } from './endpoint'\nimport { BusPort } from './port'\nimport {\n EVENT_HANDSHAKE,\n EVENT_READY,\n HandshakeContext,\n RPC_ERRORS,\n RpcError\n} from './protocol'\nimport { matchesAny } from './wildcard'\n\nexport interface HostInfo {\n host: string\n hostVersion: string\n apiVersion: string\n capabilities: string[]\n}\n\nexport interface HostConnectionOptions {\n port: BusPort\n hostInfo: HostInfo\n context: HandshakeContext\n /** Host API methods (e.g. 'state.get', 'signalk.subscribe', 'map.center'). */\n methods?: Record<string, MethodHandler>\n callTimeoutMs?: number\n onError?: (err: unknown) => void\n /**\n * Called whenever the union of subscribed event patterns changes, with the\n * current pattern list. Lets the host start/stop upstream work lazily.\n */\n onSubscriptionsChanged?: (patterns: string[]) => void\n}\n\n/**\n * The host side of one extension context (one iframe). Sends the handshake\n * in reply to `bus.ready`, dispatches host API methods, and tracks the\n * context's event subscriptions so `publish()` only forwards events the\n * context asked for.\n */\nexport class HostConnection {\n readonly endpoint: BusEndpoint\n readonly context: HandshakeContext\n\n private readonly hostInfo: HostInfo\n private readonly subs = new Map<string, string[]>()\n private readonly onSubscriptionsChanged?: (patterns: string[]) => void\n private subSeq = 0\n\n constructor(opts: HostConnectionOptions) {\n this.hostInfo = opts.hostInfo\n this.context = opts.context\n this.onSubscriptionsChanged = opts.onSubscriptionsChanged\n this.endpoint = new BusEndpoint({\n port: opts.port,\n callTimeoutMs: opts.callTimeoutMs,\n onError: opts.onError\n })\n for (const [name, handler] of Object.entries(opts.methods ?? {})) {\n this.endpoint.registerMethod(name, handler)\n }\n this.endpoint.registerMethod('events.subscribe', (params) =>\n this.handleSubscribe(params)\n )\n this.endpoint.registerMethod('events.unsubscribe', (params) =>\n this.handleUnsubscribe(params)\n )\n this.endpoint.onEvent([EVENT_READY], () => this.sendHandshake())\n }\n\n registerMethod(name: string, handler: MethodHandler): void {\n this.endpoint.registerMethod(name, handler)\n }\n\n /**\n * Publish an event to this context. Delivered only when the context has a\n * matching subscription; returns whether it was delivered.\n */\n publish(eventName: string, params?: unknown): boolean {\n if (!this.hasSubscriber(eventName)) return false\n this.endpoint.notify(eventName, params)\n return true\n }\n\n hasSubscriber(eventName: string): boolean {\n for (const [, patterns] of this.subs) {\n if (matchesAny(patterns, eventName)) return true\n }\n return false\n }\n\n /** Union of currently subscribed patterns. */\n subscribedPatterns(): string[] {\n const all = new Set<string>()\n for (const [, patterns] of this.subs) {\n for (const p of patterns) all.add(p)\n }\n return [...all]\n }\n\n close(): void {\n this.endpoint.close()\n this.subs.clear()\n }\n\n private sendHandshake(): void {\n this.endpoint.notify(EVENT_HANDSHAKE, {\n ...this.hostInfo,\n context: this.context\n })\n }\n\n private handleSubscribe(params: unknown): { subscriptionId: string } {\n const patterns = (params as { patterns?: unknown })?.patterns\n if (\n !Array.isArray(patterns) ||\n patterns.length === 0 ||\n !patterns.every((p) => typeof p === 'string' && p.length > 0)\n ) {\n throw new RpcError('events.subscribe requires a non-empty patterns array', {\n code: RPC_ERRORS.INVALID_PARAMS,\n reason: 'INVALID_PATTERNS'\n })\n }\n const subscriptionId = `sub-${++this.subSeq}`\n this.subs.set(subscriptionId, patterns as string[])\n this.onSubscriptionsChanged?.(this.subscribedPatterns())\n return { subscriptionId }\n }\n\n private handleUnsubscribe(params: unknown): Record<string, never> {\n const id = (params as { subscriptionId?: unknown })?.subscriptionId\n if (typeof id !== 'string' || !this.subs.has(id)) {\n throw new RpcError('Unknown subscriptionId', {\n code: RPC_ERRORS.INVALID_PARAMS,\n reason: 'UNKNOWN_SUBSCRIPTION'\n })\n }\n this.subs.delete(id)\n this.onSubscriptionsChanged?.(this.subscribedPatterns())\n return {}\n }\n}\n\nexport * from './protocol'\nexport * from './wildcard'\nexport { BusEndpoint } from './endpoint'\nexport type { MethodHandler, MethodContext, EventHandler } from './endpoint'\nexport * from './port'\n","/**\n * Wire protocol types and constants for the Signal K plotter extension bus.\n *\n * The wire format is JSON-RPC 2.0 (https://www.jsonrpc.org/specification)\n * inside a routing envelope: `{ bus: \"plotterExt/1\", msg: <JSON-RPC object> }`.\n * Calls are JSON-RPC requests; events are JSON-RPC notifications whose\n * `method` is a hierarchical dot-separated event name.\n */\n\nexport const BUS_ID = 'plotterExt/1'\n\nexport type JsonRpcId = string | number\n\nexport interface JsonRpcRequest {\n jsonrpc: '2.0'\n id: JsonRpcId\n method: string\n params?: unknown\n}\n\nexport interface JsonRpcNotification {\n jsonrpc: '2.0'\n method: string\n params?: unknown\n}\n\nexport interface JsonRpcErrorObject {\n code: number\n message: string\n data?: { reason?: string; [key: string]: unknown }\n}\n\nexport interface JsonRpcSuccessResponse {\n jsonrpc: '2.0'\n id: JsonRpcId\n result: unknown\n}\n\nexport interface JsonRpcErrorResponse {\n jsonrpc: '2.0'\n id: JsonRpcId | null\n error: JsonRpcErrorObject\n}\n\nexport type JsonRpcResponse = JsonRpcSuccessResponse | JsonRpcErrorResponse\n\nexport type JsonRpcMessage =\n | JsonRpcRequest\n | JsonRpcNotification\n | JsonRpcResponse\n\nexport interface Envelope {\n bus: typeof BUS_ID\n msg: JsonRpcMessage\n}\n\n/**\n * JSON-RPC reserved codes for protocol errors, plus implementation-defined\n * codes (-32000..-32099 range) used by this package. Host API errors should\n * use HOST_ERROR with a stable string identifier in `error.data.reason`.\n */\nexport const RPC_ERRORS = {\n PARSE_ERROR: -32700,\n INVALID_REQUEST: -32600,\n METHOD_NOT_FOUND: -32601,\n INVALID_PARAMS: -32602,\n INTERNAL_ERROR: -32603,\n HOST_ERROR: -32000,\n TIMEOUT: -32001,\n CONNECTION_CLOSED: -32002\n} as const\n\nexport class RpcError extends Error {\n readonly code: number\n readonly data?: { reason?: string; [key: string]: unknown }\n\n constructor(\n message: string,\n opts: {\n code?: number\n reason?: string\n data?: Record<string, unknown>\n } = {}\n ) {\n super(message)\n this.name = 'RpcError'\n this.code = opts.code ?? RPC_ERRORS.HOST_ERROR\n const data: Record<string, unknown> = { ...(opts.data ?? {}) }\n if (opts.reason !== undefined) data.reason = opts.reason\n this.data = Object.keys(data).length > 0 ? data : undefined\n }\n\n get reason(): string | undefined {\n return typeof this.data?.reason === 'string' ? this.data.reason : undefined\n }\n\n toErrorObject(): JsonRpcErrorObject {\n return {\n code: this.code,\n message: this.message,\n ...(this.data ? { data: this.data } : {})\n }\n }\n\n static fromErrorObject(err: JsonRpcErrorObject): RpcError {\n return new RpcError(err.message, { code: err.code, data: err.data })\n }\n\n /** Normalize any thrown value into an RpcError suitable for the wire. */\n static from(err: unknown): RpcError {\n if (err instanceof RpcError) return err\n if (err instanceof Error) {\n return new RpcError(err.message, { code: RPC_ERRORS.INTERNAL_ERROR })\n }\n return new RpcError(String(err), { code: RPC_ERRORS.INTERNAL_ERROR })\n }\n}\n\n/** Reserved event names used to establish a connection. */\nexport const EVENT_READY = 'bus.ready'\nexport const EVENT_HANDSHAKE = 'bus.handshake'\n\nexport type ContextKind = 'panel' | 'widget' | 'background'\n\nexport interface HandshakeContext {\n kind: ContextKind\n /** Manifest-local contribution id. */\n id: string\n /** Host-assigned unique id for this placed instance (widgets). */\n instanceId?: string | null\n /** Widget instance a configuration panel was opened for. */\n targetInstance?: string | null\n /** Manifest-local widget id of the target instance (configuration panels). */\n targetWidget?: string | null\n}\n\nexport interface Handshake {\n host: string\n hostVersion: string\n apiVersion: string\n capabilities: string[]\n context: HandshakeContext\n}\n\n/** Payload of an `sk.<path>` Signal K value event. */\nexport interface SignalKValueEvent {\n path: string\n value: unknown\n timestamp?: string\n $source?: string\n}\n\nexport type StateScope = 'instance' | 'extension'\n\n/** Payload of a `state.changed` host event. */\nexport interface StateChangedEvent {\n scope: StateScope\n instanceId?: string | null\n keys: string[]\n}\n\n/**\n * Types for the `routes` capability — the routes the host currently has visible\n * on the chart (drafts plus stored routes the user is displaying), each\n * addressed by an opaque host-assigned `routeId`. Extensions read/write them and\n * follow lifecycle + mutation events (`route.visible` / `route.dirty` /\n * `route.saved` / `route.hidden`). Two orthogonal flags travel with a route:\n * `saved` (backed by a persisted resource) and `dirty` (has pending unsaved\n * changes). See the Plotter Extensions API spec, \"Live routes\".\n */\n\n/** A single point in a route edit buffer (`[lon, lat, alt?]`). */\nexport interface RoutePoint {\n position: [number, number, number?]\n name?: string\n description?: string\n}\n\n/** Snapshot of a route — result of `route.get`. */\nexport interface RouteData {\n routeId: string\n name: string | null\n /** Route-level description (distinct from a waypoint's RoutePoint.description). */\n description: string | null\n /** Monotonic revision; increments on every mutation. */\n rev: number\n /** Whether the route is backed by a persisted routes resource. */\n saved: boolean\n /** Whether the in-memory route has pending unsaved changes. */\n dirty: boolean\n points: RoutePoint[]\n}\n\n/** Summary entry in a `route.list` result (the visible set). */\nexport interface RouteSummary {\n routeId: string\n name: string | null\n rev: number\n pointCount: number\n saved: boolean\n dirty: boolean\n}\n\n/**\n * Payload of a `route.visible` host event — a route entered the visible set\n * (became rendered on the chart). A freshly drawn/created draft arrives\n * `saved:false, dirty:true`; a stored route brought into view arrives\n * `saved:true, dirty:false`.\n */\nexport interface RouteVisibleEvent {\n routeId: string\n rev: number\n name: string | null\n pointCount: number\n saved: boolean\n dirty: boolean\n}\n\n/**\n * Payload of a `route.hidden` host event — a route left the visible set.\n * `saved:true` ⇒ a stored route was made invisible (the resource is untouched);\n * `saved:false` ⇒ an unsaved draft was deleted (gone for good).\n */\nexport interface RouteHiddenEvent {\n routeId: string\n rev: number\n saved: boolean\n}\n\n/**\n * Payload of a `route.dirty` host event — the conformance-floor catch-all for\n * any structural/bulk change the host does not express granularly. A subscriber\n * should re-seed via `route.get`.\n */\nexport interface RouteDirtyEvent {\n routeId: string\n rev: number\n reason?: string\n}\n\n/**\n * Payload of a `route.saved` host event — the route's current state was\n * persisted to the routes resource collection. Arrives `saved:true,\n * dirty:false`; the route stays visible under the same `routeId`.\n */\nexport interface RouteSavedEvent {\n routeId: string\n rev: number\n /** Stored resource id of the persisted route. */\n href: string\n /** The route's name as persisted (may have just been set in the host's save\n * dialog), so followers can update a label without re-fetching. */\n name: string | null\n saved: boolean\n dirty: boolean\n}\n\n/** Stable `error.data.reason` strings for `route.*` host-method failures. */\nexport type RouteErrorReason =\n | 'routes.unknownId'\n | 'routes.badRequest'\n | 'routes.badRef'\n | 'routes.saveFailed'\n | 'routes.deleteFailed'\n | 'routes.saveCancelled'\n | 'routes.notSupported'\n","import {\n BUS_ID,\n Envelope,\n JsonRpcMessage,\n JsonRpcNotification,\n JsonRpcRequest,\n JsonRpcResponse\n} from './protocol'\n\nexport function wrap(msg: JsonRpcMessage): Envelope {\n return { bus: BUS_ID, msg }\n}\n\n/**\n * Returns the JSON-RPC message inside a bus envelope, or null when the data\n * is not valid protocol traffic (so unrelated postMessage noise is ignored).\n */\nexport function unwrap(data: unknown): JsonRpcMessage | null {\n if (typeof data !== 'object' || data === null) return null\n const env = data as Record<string, unknown>\n if (env.bus !== BUS_ID) return null\n return isJsonRpcMessage(env.msg) ? env.msg : null\n}\n\nexport function isJsonRpcMessage(v: unknown): v is JsonRpcMessage {\n if (typeof v !== 'object' || v === null) return false\n const m = v as Record<string, unknown>\n if (m.jsonrpc !== '2.0') return false\n if (typeof m.method === 'string') {\n // Request (with id) or notification (without).\n return (\n m.id === undefined ||\n typeof m.id === 'string' ||\n typeof m.id === 'number'\n )\n }\n // Response: id required (null allowed for unroutable errors), exactly one\n // of result / error.\n const idOk =\n typeof m.id === 'string' || typeof m.id === 'number' || m.id === null\n if (!idOk) return false\n const hasResult = 'result' in m\n const err = m.error as Record<string, unknown> | undefined\n const hasError =\n typeof err === 'object' &&\n err !== null &&\n typeof err.code === 'number' &&\n typeof err.message === 'string'\n return hasResult ? !('error' in m) : hasError\n}\n\nexport function isRequest(msg: JsonRpcMessage): msg is JsonRpcRequest {\n return 'method' in msg && 'id' in msg && msg.id !== undefined\n}\n\nexport function isNotification(\n msg: JsonRpcMessage\n): msg is JsonRpcNotification {\n return 'method' in msg && (!('id' in msg) || msg.id === undefined)\n}\n\nexport function isResponse(msg: JsonRpcMessage): msg is JsonRpcResponse {\n return !('method' in msg)\n}\n","/**\n * eventemitter2-style wildcard matching for dot-separated event names.\n *\n * - `*` matches exactly one segment.\n * - `**` matches zero or more segments (any remainder when trailing).\n */\nexport function matchesPattern(pattern: string, name: string): boolean {\n if (pattern === name) return true\n return match(pattern.split('.'), 0, name.split('.'), 0)\n}\n\nfunction match(p: string[], pi: number, n: string[], ni: number): boolean {\n while (pi < p.length) {\n const seg = p[pi]\n if (seg === '**') {\n if (pi === p.length - 1) return true\n for (let skip = ni; skip <= n.length; skip++) {\n if (match(p, pi + 1, n, skip)) return true\n }\n return false\n }\n if (ni >= n.length) return false\n if (seg !== '*' && seg !== n[ni]) return false\n pi++\n ni++\n }\n return ni === n.length\n}\n\nexport function matchesAny(patterns: Iterable<string>, name: string): boolean {\n for (const pattern of patterns) {\n if (matchesPattern(pattern, name)) return true\n }\n return false\n}\n","import {\n isNotification,\n isRequest,\n isResponse,\n unwrap,\n wrap\n} from './codec'\nimport {\n JsonRpcId,\n JsonRpcMessage,\n JsonRpcResponse,\n RPC_ERRORS,\n RpcError\n} from './protocol'\nimport { BusPort } from './port'\nimport { matchesAny } from './wildcard'\n\nexport interface MethodContext {\n endpoint: BusEndpoint\n}\n\nexport type MethodHandler = (\n params: unknown,\n ctx: MethodContext\n) => unknown | Promise<unknown>\n\nexport type EventHandler = (name: string, params: unknown) => void\n\nexport interface BusEndpointOptions {\n port: BusPort\n /** Default timeout for outgoing calls. */\n callTimeoutMs?: number\n /** Reporter for handler/dispatch errors. Defaults to console.warn. */\n onError?: (err: unknown) => void\n}\n\ninterface PendingCall {\n resolve: (value: unknown) => void\n reject: (err: RpcError) => void\n timer: ReturnType<typeof setTimeout> | null\n}\n\nconst DEFAULT_CALL_TIMEOUT_MS = 10_000\n\n/**\n * One end of the bus: sends/receives envelopes over a BusPort, dispatches\n * incoming requests to registered methods, correlates responses to pending\n * calls by per-call nonce, and fans incoming notifications out to wildcard\n * event handlers. Symmetric — both host and extension build on this.\n */\nexport class BusEndpoint {\n readonly callTimeoutMs: number\n\n private readonly port: BusPort\n private readonly unlisten: () => void\n private readonly onError: (err: unknown) => void\n private readonly pending = new Map<JsonRpcId, PendingCall>()\n private readonly methods = new Map<string, MethodHandler>()\n private readonly eventHandlers = new Set<{\n patterns: string[]\n fn: EventHandler\n }>()\n private readonly idPrefix = Math.random().toString(36).slice(2, 8)\n private seq = 0\n private closed = false\n\n constructor(opts: BusEndpointOptions) {\n this.port = opts.port\n this.callTimeoutMs = opts.callTimeoutMs ?? DEFAULT_CALL_TIMEOUT_MS\n this.onError =\n opts.onError ??\n ((err) => console.warn('[plotterext-bus]', err))\n this.unlisten = this.port.listen((data) => this.onData(data))\n }\n\n registerMethod(name: string, handler: MethodHandler): void {\n this.methods.set(name, handler)\n }\n\n unregisterMethod(name: string): void {\n this.methods.delete(name)\n }\n\n /**\n * Handle incoming notifications whose names match any of the wildcard\n * patterns. Returns an unsubscribe function. This is local dispatch only;\n * telling the peer which events to forward is a separate concern\n * (`events.subscribe`).\n */\n onEvent(patterns: string[], fn: EventHandler): () => void {\n const entry = { patterns, fn }\n this.eventHandlers.add(entry)\n return () => this.eventHandlers.delete(entry)\n }\n\n /** Send a notification (an event) to the peer. */\n notify(method: string, params?: unknown): void {\n this.send({ jsonrpc: '2.0', method, ...(params !== undefined ? { params } : {}) })\n }\n\n /** Call a method on the peer; resolves with its result. */\n call(\n method: string,\n params?: unknown,\n opts: { timeoutMs?: number } = {}\n ): Promise<unknown> {\n if (this.closed) {\n return Promise.reject(\n new RpcError('Bus endpoint is closed', {\n code: RPC_ERRORS.CONNECTION_CLOSED,\n reason: 'CLOSED'\n })\n )\n }\n const id = `${this.idPrefix}-${++this.seq}`\n const timeoutMs = opts.timeoutMs ?? this.callTimeoutMs\n return new Promise<unknown>((resolve, reject) => {\n const timer =\n timeoutMs > 0\n ? setTimeout(() => {\n this.pending.delete(id)\n reject(\n new RpcError(`Call timed out after ${timeoutMs}ms: ${method}`, {\n code: RPC_ERRORS.TIMEOUT,\n reason: 'TIMEOUT'\n })\n )\n }, timeoutMs)\n : null\n this.pending.set(id, { resolve, reject, timer })\n this.send({\n jsonrpc: '2.0',\n id,\n method,\n ...(params !== undefined ? { params } : {})\n })\n })\n }\n\n close(): void {\n if (this.closed) return\n this.closed = true\n this.unlisten()\n for (const [, p] of this.pending) {\n if (p.timer) clearTimeout(p.timer)\n p.reject(\n new RpcError('Bus endpoint closed', {\n code: RPC_ERRORS.CONNECTION_CLOSED,\n reason: 'CLOSED'\n })\n )\n }\n this.pending.clear()\n this.eventHandlers.clear()\n }\n\n private send(msg: JsonRpcMessage): void {\n if (this.closed) return\n this.port.post(wrap(msg))\n }\n\n private onData(data: unknown): void {\n const msg = unwrap(data)\n if (!msg) return\n if (isResponse(msg)) {\n this.onResponse(msg)\n } else if (isRequest(msg)) {\n void this.onRequest(msg)\n } else if (isNotification(msg)) {\n this.onNotification(msg.method, msg.params)\n }\n }\n\n private onResponse(msg: JsonRpcResponse): void {\n if (msg.id === null) return\n const p = this.pending.get(msg.id)\n if (!p) return\n this.pending.delete(msg.id)\n if (p.timer) clearTimeout(p.timer)\n if ('error' in msg) {\n p.reject(RpcError.fromErrorObject(msg.error))\n } else {\n p.resolve(msg.result)\n }\n }\n\n private async onRequest(msg: {\n id: JsonRpcId\n method: string\n params?: unknown\n }): Promise<void> {\n const handler = this.methods.get(msg.method)\n if (!handler) {\n this.send({\n jsonrpc: '2.0',\n id: msg.id,\n error: {\n code: RPC_ERRORS.METHOD_NOT_FOUND,\n message: `Method not found: ${msg.method}`\n }\n })\n return\n }\n try {\n const result = await handler(msg.params, { endpoint: this })\n this.send({\n jsonrpc: '2.0',\n id: msg.id,\n result: result === undefined ? null : result\n })\n } catch (err) {\n this.send({\n jsonrpc: '2.0',\n id: msg.id,\n error: RpcError.from(err).toErrorObject()\n })\n }\n }\n\n private onNotification(name: string, params: unknown): void {\n for (const entry of [...this.eventHandlers]) {\n if (matchesAny(entry.patterns, name)) {\n try {\n entry.fn(name, params)\n } catch (err) {\n this.onError(err)\n }\n }\n }\n }\n}\n","/**\n * Transport abstraction. The bus runs over anything that can post and\n * receive structured-cloneable data: window.postMessage between a host page\n * and an iframe, a MessageChannel port, or a test double.\n */\nexport interface BusPort {\n post(data: unknown): void\n /** Register a receive handler; returns an unlisten function. */\n listen(handler: (data: unknown) => void): () => void\n}\n\nexport interface WindowPortOptions {\n /**\n * targetOrigin for outgoing postMessage and required origin of incoming\n * messages. Defaults to the listening window's own origin (the baseline\n * deployment is same-origin). Pass '*' to disable origin checks.\n */\n origin?: string\n /** Window whose 'message' events are listened to. Defaults to globalThis. */\n listenWindow?: Window\n}\n\n/**\n * A port over window.postMessage to a peer window. On the host side the peer\n * is an iframe's contentWindow; on the extension side it is window.parent.\n * Only messages whose source is the peer (and whose origin matches) are\n * delivered.\n */\nexport function windowPort(peer: Window, opts: WindowPortOptions = {}): BusPort {\n const listenWindow =\n opts.listenWindow ?? (globalThis as unknown as Window)\n const origin = opts.origin ?? listenWindow.location?.origin ?? '*'\n return {\n post(data) {\n peer.postMessage(data, origin)\n },\n listen(handler) {\n const fn = (ev: MessageEvent) => {\n if (ev.source !== peer) return\n if (origin !== '*' && ev.origin !== origin) return\n handler(ev.data)\n }\n listenWindow.addEventListener('message', fn as EventListener)\n return () =>\n listenWindow.removeEventListener('message', fn as EventListener)\n }\n }\n}\n\ninterface MessagePortLike {\n postMessage(data: unknown): void\n addEventListener(type: 'message', fn: (ev: { data: unknown }) => void): void\n removeEventListener(\n type: 'message',\n fn: (ev: { data: unknown }) => void\n ): void\n start?(): void\n}\n\n/** A port over a MessageChannel/MessagePort (browser or Node). */\nexport function messagePort(port: MessagePortLike): BusPort {\n return {\n post(data) {\n port.postMessage(data)\n },\n listen(handler) {\n const fn = (ev: { data: unknown }) => handler(ev.data)\n port.addEventListener('message', fn)\n port.start?.()\n return () => port.removeEventListener('message', fn)\n }\n }\n}\n"],"mappings":"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","names":[]}
package/dist/host.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { d as BusEndpoint, j as HandshakeContext, f as BusPort, o as MethodHandler } from './wildcard-BHRv-WAr.cjs';
2
- export { B as BUS_ID, C as ContextKind, g as EVENT_HANDSHAKE, h as EVENT_READY, E as Envelope, i as EventHandler, H as Handshake, k as JsonRpcErrorObject, l as JsonRpcErrorResponse, m as JsonRpcId, J as JsonRpcMessage, a as JsonRpcNotification, b as JsonRpcRequest, c as JsonRpcResponse, n as JsonRpcSuccessResponse, M as MethodContext, R as RPC_ERRORS, p as RouteData, q as RouteDirtyEvent, r as RouteErrorReason, s as RouteHiddenEvent, t as RoutePoint, u as RouteSavedEvent, v as RouteSummary, w as RouteVisibleEvent, x as RpcError, S as SignalKValueEvent, y as StateChangedEvent, z as StateScope, W as WindowPortOptions, A as matchesAny, D as matchesPattern, F as messagePort, G as windowPort } from './wildcard-BHRv-WAr.cjs';
1
+ import { d as BusEndpoint, j as HandshakeContext, f as BusPort, o as MethodHandler } from './wildcard-B5Dp4MA1.cjs';
2
+ export { B as BUS_ID, C as ContextKind, g as EVENT_HANDSHAKE, h as EVENT_READY, E as Envelope, i as EventHandler, H as Handshake, k as JsonRpcErrorObject, l as JsonRpcErrorResponse, m as JsonRpcId, J as JsonRpcMessage, a as JsonRpcNotification, b as JsonRpcRequest, c as JsonRpcResponse, n as JsonRpcSuccessResponse, M as MethodContext, R as RPC_ERRORS, p as RouteData, q as RouteDirtyEvent, r as RouteErrorReason, s as RouteHiddenEvent, t as RoutePoint, u as RouteSavedEvent, v as RouteSummary, w as RouteVisibleEvent, x as RpcError, S as SignalKValueEvent, y as StateChangedEvent, z as StateScope, W as WindowPortOptions, A as matchesAny, D as matchesPattern, F as messagePort, G as windowPort } from './wildcard-B5Dp4MA1.cjs';
3
3
 
4
4
  interface HostInfo {
5
5
  host: string;
package/dist/host.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { d as BusEndpoint, j as HandshakeContext, f as BusPort, o as MethodHandler } from './wildcard-BHRv-WAr.js';
2
- export { B as BUS_ID, C as ContextKind, g as EVENT_HANDSHAKE, h as EVENT_READY, E as Envelope, i as EventHandler, H as Handshake, k as JsonRpcErrorObject, l as JsonRpcErrorResponse, m as JsonRpcId, J as JsonRpcMessage, a as JsonRpcNotification, b as JsonRpcRequest, c as JsonRpcResponse, n as JsonRpcSuccessResponse, M as MethodContext, R as RPC_ERRORS, p as RouteData, q as RouteDirtyEvent, r as RouteErrorReason, s as RouteHiddenEvent, t as RoutePoint, u as RouteSavedEvent, v as RouteSummary, w as RouteVisibleEvent, x as RpcError, S as SignalKValueEvent, y as StateChangedEvent, z as StateScope, W as WindowPortOptions, A as matchesAny, D as matchesPattern, F as messagePort, G as windowPort } from './wildcard-BHRv-WAr.js';
1
+ import { d as BusEndpoint, j as HandshakeContext, f as BusPort, o as MethodHandler } from './wildcard-B5Dp4MA1.js';
2
+ export { B as BUS_ID, C as ContextKind, g as EVENT_HANDSHAKE, h as EVENT_READY, E as Envelope, i as EventHandler, H as Handshake, k as JsonRpcErrorObject, l as JsonRpcErrorResponse, m as JsonRpcId, J as JsonRpcMessage, a as JsonRpcNotification, b as JsonRpcRequest, c as JsonRpcResponse, n as JsonRpcSuccessResponse, M as MethodContext, R as RPC_ERRORS, p as RouteData, q as RouteDirtyEvent, r as RouteErrorReason, s as RouteHiddenEvent, t as RoutePoint, u as RouteSavedEvent, v as RouteSummary, w as RouteVisibleEvent, x as RpcError, S as SignalKValueEvent, y as StateChangedEvent, z as StateScope, W as WindowPortOptions, A as matchesAny, D as matchesPattern, F as messagePort, G as windowPort } from './wildcard-B5Dp4MA1.js';
3
3
 
4
4
  interface HostInfo {
5
5
  host: string;
package/dist/host.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  HostConnection
3
- } from "./chunk-W5VF4P6R.js";
3
+ } from "./chunk-KZCRGKFF.js";
4
4
  import {
5
5
  BUS_ID,
6
6
  BusEndpoint,
@@ -12,7 +12,7 @@ import {
12
12
  matchesPattern,
13
13
  messagePort,
14
14
  windowPort
15
- } from "./chunk-VBS4I6NW.js";
15
+ } from "./chunk-LVGWVV2W.js";
16
16
  export {
17
17
  BUS_ID,
18
18
  BusEndpoint,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/protocol.ts","../src/wildcard.ts","../src/codec.ts","../src/port.ts","../src/endpoint.ts","../src/host.ts","../src/extension.ts"],"sourcesContent":["export * from './protocol'\nexport * from './wildcard'\nexport * from './codec'\nexport * from './port'\nexport { BusEndpoint } from './endpoint'\nexport type {\n BusEndpointOptions,\n MethodHandler,\n MethodContext,\n EventHandler\n} from './endpoint'\nexport { HostConnection } from './host'\nexport type { HostInfo, HostConnectionOptions } from './host'\nexport { ExtensionClient, connectExtension } from './extension'\nexport type { ConnectOptions, Unsubscribe } from './extension'\n","/**\n * Wire protocol types and constants for the Signal K plotter extension bus.\n *\n * The wire format is JSON-RPC 2.0 (https://www.jsonrpc.org/specification)\n * inside a routing envelope: `{ bus: \"plotterExt/1\", msg: <JSON-RPC object> }`.\n * Calls are JSON-RPC requests; events are JSON-RPC notifications whose\n * `method` is a hierarchical dot-separated event name.\n */\n\nexport const BUS_ID = 'plotterExt/1'\n\nexport type JsonRpcId = string | number\n\nexport interface JsonRpcRequest {\n jsonrpc: '2.0'\n id: JsonRpcId\n method: string\n params?: unknown\n}\n\nexport interface JsonRpcNotification {\n jsonrpc: '2.0'\n method: string\n params?: unknown\n}\n\nexport interface JsonRpcErrorObject {\n code: number\n message: string\n data?: { reason?: string; [key: string]: unknown }\n}\n\nexport interface JsonRpcSuccessResponse {\n jsonrpc: '2.0'\n id: JsonRpcId\n result: unknown\n}\n\nexport interface JsonRpcErrorResponse {\n jsonrpc: '2.0'\n id: JsonRpcId | null\n error: JsonRpcErrorObject\n}\n\nexport type JsonRpcResponse = JsonRpcSuccessResponse | JsonRpcErrorResponse\n\nexport type JsonRpcMessage =\n | JsonRpcRequest\n | JsonRpcNotification\n | JsonRpcResponse\n\nexport interface Envelope {\n bus: typeof BUS_ID\n msg: JsonRpcMessage\n}\n\n/**\n * JSON-RPC reserved codes for protocol errors, plus implementation-defined\n * codes (-32000..-32099 range) used by this package. Host API errors should\n * use HOST_ERROR with a stable string identifier in `error.data.reason`.\n */\nexport const RPC_ERRORS = {\n PARSE_ERROR: -32700,\n INVALID_REQUEST: -32600,\n METHOD_NOT_FOUND: -32601,\n INVALID_PARAMS: -32602,\n INTERNAL_ERROR: -32603,\n HOST_ERROR: -32000,\n TIMEOUT: -32001,\n CONNECTION_CLOSED: -32002\n} as const\n\nexport class RpcError extends Error {\n readonly code: number\n readonly data?: { reason?: string; [key: string]: unknown }\n\n constructor(\n message: string,\n opts: {\n code?: number\n reason?: string\n data?: Record<string, unknown>\n } = {}\n ) {\n super(message)\n this.name = 'RpcError'\n this.code = opts.code ?? RPC_ERRORS.HOST_ERROR\n const data: Record<string, unknown> = { ...(opts.data ?? {}) }\n if (opts.reason !== undefined) data.reason = opts.reason\n this.data = Object.keys(data).length > 0 ? data : undefined\n }\n\n get reason(): string | undefined {\n return typeof this.data?.reason === 'string' ? this.data.reason : undefined\n }\n\n toErrorObject(): JsonRpcErrorObject {\n return {\n code: this.code,\n message: this.message,\n ...(this.data ? { data: this.data } : {})\n }\n }\n\n static fromErrorObject(err: JsonRpcErrorObject): RpcError {\n return new RpcError(err.message, { code: err.code, data: err.data })\n }\n\n /** Normalize any thrown value into an RpcError suitable for the wire. */\n static from(err: unknown): RpcError {\n if (err instanceof RpcError) return err\n if (err instanceof Error) {\n return new RpcError(err.message, { code: RPC_ERRORS.INTERNAL_ERROR })\n }\n return new RpcError(String(err), { code: RPC_ERRORS.INTERNAL_ERROR })\n }\n}\n\n/** Reserved event names used to establish a connection. */\nexport const EVENT_READY = 'bus.ready'\nexport const EVENT_HANDSHAKE = 'bus.handshake'\n\nexport type ContextKind = 'panel' | 'widget' | 'background'\n\nexport interface HandshakeContext {\n kind: ContextKind\n /** Manifest-local contribution id. */\n id: string\n /** Host-assigned unique id for this placed instance (widgets). */\n instanceId?: string | null\n /** Widget instance a configuration panel was opened for. */\n targetInstance?: string | null\n /** Manifest-local widget id of the target instance (configuration panels). */\n targetWidget?: string | null\n}\n\nexport interface Handshake {\n host: string\n hostVersion: string\n apiVersion: string\n capabilities: string[]\n context: HandshakeContext\n}\n\n/** Payload of an `sk.<path>` Signal K value event. */\nexport interface SignalKValueEvent {\n path: string\n value: unknown\n timestamp?: string\n $source?: string\n}\n\nexport type StateScope = 'instance' | 'extension'\n\n/** Payload of a `state.changed` host event. */\nexport interface StateChangedEvent {\n scope: StateScope\n instanceId?: string | null\n keys: string[]\n}\n\n/**\n * Types for the `routes` capability — the routes the host currently has visible\n * on the chart (drafts plus stored routes the user is displaying), each\n * addressed by an opaque host-assigned `routeId`. Extensions read/write them and\n * follow lifecycle + mutation events (`route.visible` / `route.dirty` /\n * `route.saved` / `route.hidden`). Two orthogonal flags travel with a route:\n * `saved` (backed by a persisted resource) and `dirty` (has pending unsaved\n * changes). See the Plotter Extensions API spec, \"Live routes\".\n */\n\n/** A single point in a route edit buffer (`[lon, lat, alt?]`). */\nexport interface RoutePoint {\n position: [number, number, number?]\n name?: string\n description?: string\n}\n\n/** Snapshot of a route — result of `route.get`. */\nexport interface RouteData {\n routeId: string\n name: string | null\n /** Route-level description (distinct from a waypoint's RoutePoint.description). */\n description: string | null\n /** Monotonic revision; increments on every mutation. */\n rev: number\n /** Whether the route is backed by a persisted routes resource. */\n saved: boolean\n /** Whether the in-memory route has pending unsaved changes. */\n dirty: boolean\n points: RoutePoint[]\n}\n\n/** Summary entry in a `route.list` result (the visible set). */\nexport interface RouteSummary {\n routeId: string\n name: string | null\n rev: number\n pointCount: number\n saved: boolean\n dirty: boolean\n}\n\n/**\n * Payload of a `route.visible` host event — a route entered the visible set\n * (became rendered on the chart). A freshly drawn/created draft arrives\n * `saved:false, dirty:true`; a stored route brought into view arrives\n * `saved:true, dirty:false`.\n */\nexport interface RouteVisibleEvent {\n routeId: string\n rev: number\n name: string | null\n pointCount: number\n saved: boolean\n dirty: boolean\n}\n\n/**\n * Payload of a `route.hidden` host event — a route left the visible set.\n * `saved:true` ⇒ a stored route was made invisible (the resource is untouched);\n * `saved:false` ⇒ an unsaved draft was deleted (gone for good).\n */\nexport interface RouteHiddenEvent {\n routeId: string\n rev: number\n saved: boolean\n}\n\n/**\n * Payload of a `route.dirty` host event — the conformance-floor catch-all for\n * any structural/bulk change the host does not express granularly. A subscriber\n * should re-seed via `route.get`.\n */\nexport interface RouteDirtyEvent {\n routeId: string\n rev: number\n reason?: string\n}\n\n/**\n * Payload of a `route.saved` host event — the route's current state was\n * persisted to the routes resource collection. Arrives `saved:true,\n * dirty:false`; the route stays visible under the same `routeId`.\n */\nexport interface RouteSavedEvent {\n routeId: string\n rev: number\n /** Stored resource id of the persisted route. */\n href: string\n /** The route's name as persisted (may have just been set in the host's save\n * dialog), so followers can update a label without re-fetching. */\n name: string | null\n saved: boolean\n dirty: boolean\n}\n\n/** Stable `error.data.reason` strings for `route.*` host-method failures. */\nexport type RouteErrorReason =\n | 'routes.unknownId'\n | 'routes.badRequest'\n | 'routes.badRef'\n | 'routes.saveFailed'\n | 'routes.saveCancelled'\n | 'routes.notSupported'\n","/**\n * eventemitter2-style wildcard matching for dot-separated event names.\n *\n * - `*` matches exactly one segment.\n * - `**` matches zero or more segments (any remainder when trailing).\n */\nexport function matchesPattern(pattern: string, name: string): boolean {\n if (pattern === name) return true\n return match(pattern.split('.'), 0, name.split('.'), 0)\n}\n\nfunction match(p: string[], pi: number, n: string[], ni: number): boolean {\n while (pi < p.length) {\n const seg = p[pi]\n if (seg === '**') {\n if (pi === p.length - 1) return true\n for (let skip = ni; skip <= n.length; skip++) {\n if (match(p, pi + 1, n, skip)) return true\n }\n return false\n }\n if (ni >= n.length) return false\n if (seg !== '*' && seg !== n[ni]) return false\n pi++\n ni++\n }\n return ni === n.length\n}\n\nexport function matchesAny(patterns: Iterable<string>, name: string): boolean {\n for (const pattern of patterns) {\n if (matchesPattern(pattern, name)) return true\n }\n return false\n}\n","import {\n BUS_ID,\n Envelope,\n JsonRpcMessage,\n JsonRpcNotification,\n JsonRpcRequest,\n JsonRpcResponse\n} from './protocol'\n\nexport function wrap(msg: JsonRpcMessage): Envelope {\n return { bus: BUS_ID, msg }\n}\n\n/**\n * Returns the JSON-RPC message inside a bus envelope, or null when the data\n * is not valid protocol traffic (so unrelated postMessage noise is ignored).\n */\nexport function unwrap(data: unknown): JsonRpcMessage | null {\n if (typeof data !== 'object' || data === null) return null\n const env = data as Record<string, unknown>\n if (env.bus !== BUS_ID) return null\n return isJsonRpcMessage(env.msg) ? env.msg : null\n}\n\nexport function isJsonRpcMessage(v: unknown): v is JsonRpcMessage {\n if (typeof v !== 'object' || v === null) return false\n const m = v as Record<string, unknown>\n if (m.jsonrpc !== '2.0') return false\n if (typeof m.method === 'string') {\n // Request (with id) or notification (without).\n return (\n m.id === undefined ||\n typeof m.id === 'string' ||\n typeof m.id === 'number'\n )\n }\n // Response: id required (null allowed for unroutable errors), exactly one\n // of result / error.\n const idOk =\n typeof m.id === 'string' || typeof m.id === 'number' || m.id === null\n if (!idOk) return false\n const hasResult = 'result' in m\n const err = m.error as Record<string, unknown> | undefined\n const hasError =\n typeof err === 'object' &&\n err !== null &&\n typeof err.code === 'number' &&\n typeof err.message === 'string'\n return hasResult ? !('error' in m) : hasError\n}\n\nexport function isRequest(msg: JsonRpcMessage): msg is JsonRpcRequest {\n return 'method' in msg && 'id' in msg && msg.id !== undefined\n}\n\nexport function isNotification(\n msg: JsonRpcMessage\n): msg is JsonRpcNotification {\n return 'method' in msg && (!('id' in msg) || msg.id === undefined)\n}\n\nexport function isResponse(msg: JsonRpcMessage): msg is JsonRpcResponse {\n return !('method' in msg)\n}\n","/**\n * Transport abstraction. The bus runs over anything that can post and\n * receive structured-cloneable data: window.postMessage between a host page\n * and an iframe, a MessageChannel port, or a test double.\n */\nexport interface BusPort {\n post(data: unknown): void\n /** Register a receive handler; returns an unlisten function. */\n listen(handler: (data: unknown) => void): () => void\n}\n\nexport interface WindowPortOptions {\n /**\n * targetOrigin for outgoing postMessage and required origin of incoming\n * messages. Defaults to the listening window's own origin (the baseline\n * deployment is same-origin). Pass '*' to disable origin checks.\n */\n origin?: string\n /** Window whose 'message' events are listened to. Defaults to globalThis. */\n listenWindow?: Window\n}\n\n/**\n * A port over window.postMessage to a peer window. On the host side the peer\n * is an iframe's contentWindow; on the extension side it is window.parent.\n * Only messages whose source is the peer (and whose origin matches) are\n * delivered.\n */\nexport function windowPort(peer: Window, opts: WindowPortOptions = {}): BusPort {\n const listenWindow =\n opts.listenWindow ?? (globalThis as unknown as Window)\n const origin = opts.origin ?? listenWindow.location?.origin ?? '*'\n return {\n post(data) {\n peer.postMessage(data, origin)\n },\n listen(handler) {\n const fn = (ev: MessageEvent) => {\n if (ev.source !== peer) return\n if (origin !== '*' && ev.origin !== origin) return\n handler(ev.data)\n }\n listenWindow.addEventListener('message', fn as EventListener)\n return () =>\n listenWindow.removeEventListener('message', fn as EventListener)\n }\n }\n}\n\ninterface MessagePortLike {\n postMessage(data: unknown): void\n addEventListener(type: 'message', fn: (ev: { data: unknown }) => void): void\n removeEventListener(\n type: 'message',\n fn: (ev: { data: unknown }) => void\n ): void\n start?(): void\n}\n\n/** A port over a MessageChannel/MessagePort (browser or Node). */\nexport function messagePort(port: MessagePortLike): BusPort {\n return {\n post(data) {\n port.postMessage(data)\n },\n listen(handler) {\n const fn = (ev: { data: unknown }) => handler(ev.data)\n port.addEventListener('message', fn)\n port.start?.()\n return () => port.removeEventListener('message', fn)\n }\n }\n}\n","import {\n isNotification,\n isRequest,\n isResponse,\n unwrap,\n wrap\n} from './codec'\nimport {\n JsonRpcId,\n JsonRpcMessage,\n JsonRpcResponse,\n RPC_ERRORS,\n RpcError\n} from './protocol'\nimport { BusPort } from './port'\nimport { matchesAny } from './wildcard'\n\nexport interface MethodContext {\n endpoint: BusEndpoint\n}\n\nexport type MethodHandler = (\n params: unknown,\n ctx: MethodContext\n) => unknown | Promise<unknown>\n\nexport type EventHandler = (name: string, params: unknown) => void\n\nexport interface BusEndpointOptions {\n port: BusPort\n /** Default timeout for outgoing calls. */\n callTimeoutMs?: number\n /** Reporter for handler/dispatch errors. Defaults to console.warn. */\n onError?: (err: unknown) => void\n}\n\ninterface PendingCall {\n resolve: (value: unknown) => void\n reject: (err: RpcError) => void\n timer: ReturnType<typeof setTimeout> | null\n}\n\nconst DEFAULT_CALL_TIMEOUT_MS = 10_000\n\n/**\n * One end of the bus: sends/receives envelopes over a BusPort, dispatches\n * incoming requests to registered methods, correlates responses to pending\n * calls by per-call nonce, and fans incoming notifications out to wildcard\n * event handlers. Symmetric — both host and extension build on this.\n */\nexport class BusEndpoint {\n readonly callTimeoutMs: number\n\n private readonly port: BusPort\n private readonly unlisten: () => void\n private readonly onError: (err: unknown) => void\n private readonly pending = new Map<JsonRpcId, PendingCall>()\n private readonly methods = new Map<string, MethodHandler>()\n private readonly eventHandlers = new Set<{\n patterns: string[]\n fn: EventHandler\n }>()\n private readonly idPrefix = Math.random().toString(36).slice(2, 8)\n private seq = 0\n private closed = false\n\n constructor(opts: BusEndpointOptions) {\n this.port = opts.port\n this.callTimeoutMs = opts.callTimeoutMs ?? DEFAULT_CALL_TIMEOUT_MS\n this.onError =\n opts.onError ??\n ((err) => console.warn('[plotterext-bus]', err))\n this.unlisten = this.port.listen((data) => this.onData(data))\n }\n\n registerMethod(name: string, handler: MethodHandler): void {\n this.methods.set(name, handler)\n }\n\n unregisterMethod(name: string): void {\n this.methods.delete(name)\n }\n\n /**\n * Handle incoming notifications whose names match any of the wildcard\n * patterns. Returns an unsubscribe function. This is local dispatch only;\n * telling the peer which events to forward is a separate concern\n * (`events.subscribe`).\n */\n onEvent(patterns: string[], fn: EventHandler): () => void {\n const entry = { patterns, fn }\n this.eventHandlers.add(entry)\n return () => this.eventHandlers.delete(entry)\n }\n\n /** Send a notification (an event) to the peer. */\n notify(method: string, params?: unknown): void {\n this.send({ jsonrpc: '2.0', method, ...(params !== undefined ? { params } : {}) })\n }\n\n /** Call a method on the peer; resolves with its result. */\n call(\n method: string,\n params?: unknown,\n opts: { timeoutMs?: number } = {}\n ): Promise<unknown> {\n if (this.closed) {\n return Promise.reject(\n new RpcError('Bus endpoint is closed', {\n code: RPC_ERRORS.CONNECTION_CLOSED,\n reason: 'CLOSED'\n })\n )\n }\n const id = `${this.idPrefix}-${++this.seq}`\n const timeoutMs = opts.timeoutMs ?? this.callTimeoutMs\n return new Promise<unknown>((resolve, reject) => {\n const timer =\n timeoutMs > 0\n ? setTimeout(() => {\n this.pending.delete(id)\n reject(\n new RpcError(`Call timed out after ${timeoutMs}ms: ${method}`, {\n code: RPC_ERRORS.TIMEOUT,\n reason: 'TIMEOUT'\n })\n )\n }, timeoutMs)\n : null\n this.pending.set(id, { resolve, reject, timer })\n this.send({\n jsonrpc: '2.0',\n id,\n method,\n ...(params !== undefined ? { params } : {})\n })\n })\n }\n\n close(): void {\n if (this.closed) return\n this.closed = true\n this.unlisten()\n for (const [, p] of this.pending) {\n if (p.timer) clearTimeout(p.timer)\n p.reject(\n new RpcError('Bus endpoint closed', {\n code: RPC_ERRORS.CONNECTION_CLOSED,\n reason: 'CLOSED'\n })\n )\n }\n this.pending.clear()\n this.eventHandlers.clear()\n }\n\n private send(msg: JsonRpcMessage): void {\n if (this.closed) return\n this.port.post(wrap(msg))\n }\n\n private onData(data: unknown): void {\n const msg = unwrap(data)\n if (!msg) return\n if (isResponse(msg)) {\n this.onResponse(msg)\n } else if (isRequest(msg)) {\n void this.onRequest(msg)\n } else if (isNotification(msg)) {\n this.onNotification(msg.method, msg.params)\n }\n }\n\n private onResponse(msg: JsonRpcResponse): void {\n if (msg.id === null) return\n const p = this.pending.get(msg.id)\n if (!p) return\n this.pending.delete(msg.id)\n if (p.timer) clearTimeout(p.timer)\n if ('error' in msg) {\n p.reject(RpcError.fromErrorObject(msg.error))\n } else {\n p.resolve(msg.result)\n }\n }\n\n private async onRequest(msg: {\n id: JsonRpcId\n method: string\n params?: unknown\n }): Promise<void> {\n const handler = this.methods.get(msg.method)\n if (!handler) {\n this.send({\n jsonrpc: '2.0',\n id: msg.id,\n error: {\n code: RPC_ERRORS.METHOD_NOT_FOUND,\n message: `Method not found: ${msg.method}`\n }\n })\n return\n }\n try {\n const result = await handler(msg.params, { endpoint: this })\n this.send({\n jsonrpc: '2.0',\n id: msg.id,\n result: result === undefined ? null : result\n })\n } catch (err) {\n this.send({\n jsonrpc: '2.0',\n id: msg.id,\n error: RpcError.from(err).toErrorObject()\n })\n }\n }\n\n private onNotification(name: string, params: unknown): void {\n for (const entry of [...this.eventHandlers]) {\n if (matchesAny(entry.patterns, name)) {\n try {\n entry.fn(name, params)\n } catch (err) {\n this.onError(err)\n }\n }\n }\n }\n}\n","import { BusEndpoint, MethodHandler } from './endpoint'\nimport { BusPort } from './port'\nimport {\n EVENT_HANDSHAKE,\n EVENT_READY,\n HandshakeContext,\n RPC_ERRORS,\n RpcError\n} from './protocol'\nimport { matchesAny } from './wildcard'\n\nexport interface HostInfo {\n host: string\n hostVersion: string\n apiVersion: string\n capabilities: string[]\n}\n\nexport interface HostConnectionOptions {\n port: BusPort\n hostInfo: HostInfo\n context: HandshakeContext\n /** Host API methods (e.g. 'state.get', 'signalk.subscribe', 'map.center'). */\n methods?: Record<string, MethodHandler>\n callTimeoutMs?: number\n onError?: (err: unknown) => void\n /**\n * Called whenever the union of subscribed event patterns changes, with the\n * current pattern list. Lets the host start/stop upstream work lazily.\n */\n onSubscriptionsChanged?: (patterns: string[]) => void\n}\n\n/**\n * The host side of one extension context (one iframe). Sends the handshake\n * in reply to `bus.ready`, dispatches host API methods, and tracks the\n * context's event subscriptions so `publish()` only forwards events the\n * context asked for.\n */\nexport class HostConnection {\n readonly endpoint: BusEndpoint\n readonly context: HandshakeContext\n\n private readonly hostInfo: HostInfo\n private readonly subs = new Map<string, string[]>()\n private readonly onSubscriptionsChanged?: (patterns: string[]) => void\n private subSeq = 0\n\n constructor(opts: HostConnectionOptions) {\n this.hostInfo = opts.hostInfo\n this.context = opts.context\n this.onSubscriptionsChanged = opts.onSubscriptionsChanged\n this.endpoint = new BusEndpoint({\n port: opts.port,\n callTimeoutMs: opts.callTimeoutMs,\n onError: opts.onError\n })\n for (const [name, handler] of Object.entries(opts.methods ?? {})) {\n this.endpoint.registerMethod(name, handler)\n }\n this.endpoint.registerMethod('events.subscribe', (params) =>\n this.handleSubscribe(params)\n )\n this.endpoint.registerMethod('events.unsubscribe', (params) =>\n this.handleUnsubscribe(params)\n )\n this.endpoint.onEvent([EVENT_READY], () => this.sendHandshake())\n }\n\n registerMethod(name: string, handler: MethodHandler): void {\n this.endpoint.registerMethod(name, handler)\n }\n\n /**\n * Publish an event to this context. Delivered only when the context has a\n * matching subscription; returns whether it was delivered.\n */\n publish(eventName: string, params?: unknown): boolean {\n if (!this.hasSubscriber(eventName)) return false\n this.endpoint.notify(eventName, params)\n return true\n }\n\n hasSubscriber(eventName: string): boolean {\n for (const [, patterns] of this.subs) {\n if (matchesAny(patterns, eventName)) return true\n }\n return false\n }\n\n /** Union of currently subscribed patterns. */\n subscribedPatterns(): string[] {\n const all = new Set<string>()\n for (const [, patterns] of this.subs) {\n for (const p of patterns) all.add(p)\n }\n return [...all]\n }\n\n close(): void {\n this.endpoint.close()\n this.subs.clear()\n }\n\n private sendHandshake(): void {\n this.endpoint.notify(EVENT_HANDSHAKE, {\n ...this.hostInfo,\n context: this.context\n })\n }\n\n private handleSubscribe(params: unknown): { subscriptionId: string } {\n const patterns = (params as { patterns?: unknown })?.patterns\n if (\n !Array.isArray(patterns) ||\n patterns.length === 0 ||\n !patterns.every((p) => typeof p === 'string' && p.length > 0)\n ) {\n throw new RpcError('events.subscribe requires a non-empty patterns array', {\n code: RPC_ERRORS.INVALID_PARAMS,\n reason: 'INVALID_PATTERNS'\n })\n }\n const subscriptionId = `sub-${++this.subSeq}`\n this.subs.set(subscriptionId, patterns as string[])\n this.onSubscriptionsChanged?.(this.subscribedPatterns())\n return { subscriptionId }\n }\n\n private handleUnsubscribe(params: unknown): Record<string, never> {\n const id = (params as { subscriptionId?: unknown })?.subscriptionId\n if (typeof id !== 'string' || !this.subs.has(id)) {\n throw new RpcError('Unknown subscriptionId', {\n code: RPC_ERRORS.INVALID_PARAMS,\n reason: 'UNKNOWN_SUBSCRIPTION'\n })\n }\n this.subs.delete(id)\n this.onSubscriptionsChanged?.(this.subscribedPatterns())\n return {}\n }\n}\n\nexport * from './protocol'\nexport * from './wildcard'\nexport { BusEndpoint } from './endpoint'\nexport type { MethodHandler, MethodContext, EventHandler } from './endpoint'\nexport * from './port'\n","import { BusEndpoint, EventHandler } from './endpoint'\nimport { BusPort, windowPort } from './port'\nimport {\n EVENT_HANDSHAKE,\n EVENT_READY,\n Handshake,\n HandshakeContext,\n RouteData,\n RoutePoint,\n RouteSummary,\n RPC_ERRORS,\n RpcError,\n SignalKValueEvent,\n StateScope\n} from './protocol'\n\nexport interface ConnectOptions {\n /** Transport. Defaults to window.postMessage to window.parent. */\n port?: BusPort\n /** Interval for re-sending bus.ready until the handshake arrives. */\n readyIntervalMs?: number\n /** How long to wait for the host handshake before rejecting. */\n timeoutMs?: number\n /** Default timeout for host API calls. */\n callTimeoutMs?: number\n onError?: (err: unknown) => void\n}\n\nexport type Unsubscribe = () => Promise<void>\n\n/**\n * The extension side of the bus: one per iframe context (panel, widget, or\n * background runtime). Create via connectExtension().\n */\nexport class ExtensionClient {\n readonly handshake: Handshake\n readonly endpoint: BusEndpoint\n\n constructor(endpoint: BusEndpoint, handshake: Handshake) {\n this.endpoint = endpoint\n this.handshake = handshake\n }\n\n get context(): HandshakeContext {\n return this.handshake.context\n }\n\n get apiVersion(): string {\n return this.handshake.apiVersion\n }\n\n get capabilities(): string[] {\n return this.handshake.capabilities\n }\n\n hasCapability(id: string): boolean {\n return this.handshake.capabilities.includes(id)\n }\n\n /** Call a host API method. */\n call(\n method: string,\n params?: unknown,\n opts?: { timeoutMs?: number }\n ): Promise<unknown> {\n return this.endpoint.call(method, params, opts)\n }\n\n /** Send a notification to the host. */\n notify(method: string, params?: unknown): void {\n this.endpoint.notify(method, params)\n }\n\n /**\n * Subscribe to host events matching wildcard patterns. Registers both the\n * host-side forwarding subscription and local dispatch; the returned\n * function tears down both.\n */\n async subscribe(\n patterns: string[],\n handler: EventHandler\n ): Promise<Unsubscribe> {\n const off = this.endpoint.onEvent(patterns, handler)\n let subscriptionId: string\n try {\n const result = (await this.call('events.subscribe', { patterns })) as {\n subscriptionId: string\n }\n subscriptionId = result.subscriptionId\n } catch (err) {\n off()\n throw err\n }\n return async () => {\n off()\n await this.call('events.unsubscribe', { subscriptionId }).catch(() => {\n // Best-effort: the host may already have dropped the connection.\n })\n }\n }\n\n /** Host-persisted key/value state (see spec: State Storage). */\n readonly state = {\n get: async (\n keys?: string[],\n scope?: StateScope\n ): Promise<Record<string, unknown>> => {\n const result = (await this.call('state.get', {\n ...(scope ? { scope } : {}),\n ...(keys ? { keys } : {})\n })) as { values: Record<string, unknown> }\n return result.values ?? {}\n },\n set: async (\n values: Record<string, unknown>,\n scope?: StateScope\n ): Promise<void> => {\n await this.call('state.set', {\n ...(scope ? { scope } : {}),\n values\n })\n }\n }\n\n /** Signal K data relayed by the host (capabilities signalk.stream / .put). */\n readonly signalk = {\n /**\n * Subscribe to Signal K path values. The host publishes them as\n * `sk.<path>` events; this helper hides the event-name mapping and\n * establishes both the event-forwarding subscription and the host's\n * upstream Signal K subscription.\n */\n subscribe: async (\n paths: string[],\n handler: (ev: SignalKValueEvent) => void\n ): Promise<Unsubscribe> => {\n const patterns = paths.map((p) => `sk.${p}`)\n const offEvents = await this.subscribe(patterns, (_name, params) =>\n handler(params as SignalKValueEvent)\n )\n let subscriptionId: string\n try {\n const result = (await this.call('signalk.subscribe', { paths })) as {\n subscriptionId: string\n }\n subscriptionId = result.subscriptionId\n } catch (err) {\n await offEvents()\n throw err\n }\n return async () => {\n await offEvents()\n await this.call('signalk.unsubscribe', { subscriptionId }).catch(\n () => {}\n )\n }\n },\n put: (path: string, value: unknown): Promise<unknown> => {\n return this.call('signalk.put', { path, value })\n }\n }\n\n /**\n * The host's visible routes (capability `routes`). Thin **typed** wrappers over\n * the host's `route.*` methods — each just delegates to `this.call(...)`, so a\n * plain-JS extension can call `client.call('route.replace', …)` directly and a\n * TypeScript extension gets the typed `client.route.replace(…)` sugar with no\n * behavioural difference. Follow lifecycle + mutations by subscribing to\n * `route.**` events (`RouteVisibleEvent` / `RouteDirtyEvent` / `RouteSavedEvent`\n * / `RouteHiddenEvent`). Further operations (rename/point ops) extend this\n * surface as the capability fills out.\n */\n readonly route = {\n list: async (): Promise<RouteSummary[]> => {\n const result = (await this.call('route.list')) as {\n routes?: RouteSummary[]\n }\n return result.routes ?? []\n },\n create: async (opts: {\n points: RoutePoint[]\n name?: string\n description?: string\n }): Promise<{ routeId: string; rev: number }> => {\n // A route needs at least two points to form a segment; the host rejects\n // fewer with routes.badRequest.\n return (await this.call('route.create', opts)) as {\n routeId: string\n rev: number\n }\n },\n get: async (routeId: string): Promise<RouteData> => {\n return (await this.call('route.get', { routeId })) as RouteData\n },\n replace: async (\n routeId: string,\n points: RoutePoint[]\n ): Promise<{ rev: number }> => {\n return (await this.call('route.replace', { routeId, points })) as {\n rev: number\n }\n },\n save: async (\n routeId: string,\n opts?: { name?: string; description?: string; dialog?: boolean }\n ): Promise<{ href: string; rev: number }> => {\n return (await this.call('route.save', {\n routeId,\n ...(opts ?? {})\n })) as { href: string; rev: number }\n },\n show: async (ref: string): Promise<{ routeId: string; rev: number }> => {\n return (await this.call('route.show', { ref })) as {\n routeId: string\n rev: number\n }\n },\n hide: async (routeId: string): Promise<void> => {\n await this.call('route.hide', { routeId })\n },\n delete: async (routeId: string): Promise<void> => {\n await this.call('route.delete', { routeId })\n }\n }\n\n close(): void {\n this.endpoint.close()\n }\n}\n\n/**\n * Connect to the host from inside an extension iframe. Sends `bus.ready`\n * (repeating until answered) and resolves once the host's `bus.handshake`\n * arrives.\n */\nexport function connectExtension(\n opts: ConnectOptions = {}\n): Promise<ExtensionClient> {\n // Default transport: postMessage to the embedding window. Origin checks\n // are relaxed to '*' on the extension side because the host page's origin\n // may legitimately differ from the extension asset origin (e.g. a host dev\n // server embedding extension assets served by the Signal K server); the\n // peer-source check in windowPort still applies, and the host side\n // enforces a strict origin for the extension's frame.\n const port =\n opts.port ??\n windowPort((globalThis as unknown as Window).parent as Window, {\n origin: '*'\n })\n const endpoint = new BusEndpoint({\n port,\n callTimeoutMs: opts.callTimeoutMs,\n onError: opts.onError\n })\n return new Promise<ExtensionClient>((resolve, reject) => {\n let done = false\n const off = endpoint.onEvent([EVENT_HANDSHAKE], (_name, params) => {\n if (done) return\n done = true\n cleanup()\n resolve(new ExtensionClient(endpoint, params as Handshake))\n })\n const interval = setInterval(\n () => endpoint.notify(EVENT_READY),\n opts.readyIntervalMs ?? 250\n )\n const timeout = setTimeout(() => {\n if (done) return\n done = true\n cleanup()\n endpoint.close()\n reject(\n new RpcError('Timed out waiting for host handshake', {\n code: RPC_ERRORS.TIMEOUT,\n reason: 'HANDSHAKE_TIMEOUT'\n })\n )\n }, opts.timeoutMs ?? 10_000)\n const cleanup = () => {\n off()\n clearInterval(interval)\n clearTimeout(timeout)\n }\n endpoint.notify(EVENT_READY)\n })\n}\n\nexport * from './protocol'\nexport * from './wildcard'\nexport { BusEndpoint } from './endpoint'\nexport type { MethodHandler, MethodContext, EventHandler } from './endpoint'\nexport * from './port'\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/protocol.ts","../src/wildcard.ts","../src/codec.ts","../src/port.ts","../src/endpoint.ts","../src/host.ts","../src/extension.ts"],"sourcesContent":["export * from './protocol'\nexport * from './wildcard'\nexport * from './codec'\nexport * from './port'\nexport { BusEndpoint } from './endpoint'\nexport type {\n BusEndpointOptions,\n MethodHandler,\n MethodContext,\n EventHandler\n} from './endpoint'\nexport { HostConnection } from './host'\nexport type { HostInfo, HostConnectionOptions } from './host'\nexport { ExtensionClient, connectExtension } from './extension'\nexport type { ConnectOptions, Unsubscribe } from './extension'\n","/**\n * Wire protocol types and constants for the Signal K plotter extension bus.\n *\n * The wire format is JSON-RPC 2.0 (https://www.jsonrpc.org/specification)\n * inside a routing envelope: `{ bus: \"plotterExt/1\", msg: <JSON-RPC object> }`.\n * Calls are JSON-RPC requests; events are JSON-RPC notifications whose\n * `method` is a hierarchical dot-separated event name.\n */\n\nexport const BUS_ID = 'plotterExt/1'\n\nexport type JsonRpcId = string | number\n\nexport interface JsonRpcRequest {\n jsonrpc: '2.0'\n id: JsonRpcId\n method: string\n params?: unknown\n}\n\nexport interface JsonRpcNotification {\n jsonrpc: '2.0'\n method: string\n params?: unknown\n}\n\nexport interface JsonRpcErrorObject {\n code: number\n message: string\n data?: { reason?: string; [key: string]: unknown }\n}\n\nexport interface JsonRpcSuccessResponse {\n jsonrpc: '2.0'\n id: JsonRpcId\n result: unknown\n}\n\nexport interface JsonRpcErrorResponse {\n jsonrpc: '2.0'\n id: JsonRpcId | null\n error: JsonRpcErrorObject\n}\n\nexport type JsonRpcResponse = JsonRpcSuccessResponse | JsonRpcErrorResponse\n\nexport type JsonRpcMessage =\n | JsonRpcRequest\n | JsonRpcNotification\n | JsonRpcResponse\n\nexport interface Envelope {\n bus: typeof BUS_ID\n msg: JsonRpcMessage\n}\n\n/**\n * JSON-RPC reserved codes for protocol errors, plus implementation-defined\n * codes (-32000..-32099 range) used by this package. Host API errors should\n * use HOST_ERROR with a stable string identifier in `error.data.reason`.\n */\nexport const RPC_ERRORS = {\n PARSE_ERROR: -32700,\n INVALID_REQUEST: -32600,\n METHOD_NOT_FOUND: -32601,\n INVALID_PARAMS: -32602,\n INTERNAL_ERROR: -32603,\n HOST_ERROR: -32000,\n TIMEOUT: -32001,\n CONNECTION_CLOSED: -32002\n} as const\n\nexport class RpcError extends Error {\n readonly code: number\n readonly data?: { reason?: string; [key: string]: unknown }\n\n constructor(\n message: string,\n opts: {\n code?: number\n reason?: string\n data?: Record<string, unknown>\n } = {}\n ) {\n super(message)\n this.name = 'RpcError'\n this.code = opts.code ?? RPC_ERRORS.HOST_ERROR\n const data: Record<string, unknown> = { ...(opts.data ?? {}) }\n if (opts.reason !== undefined) data.reason = opts.reason\n this.data = Object.keys(data).length > 0 ? data : undefined\n }\n\n get reason(): string | undefined {\n return typeof this.data?.reason === 'string' ? this.data.reason : undefined\n }\n\n toErrorObject(): JsonRpcErrorObject {\n return {\n code: this.code,\n message: this.message,\n ...(this.data ? { data: this.data } : {})\n }\n }\n\n static fromErrorObject(err: JsonRpcErrorObject): RpcError {\n return new RpcError(err.message, { code: err.code, data: err.data })\n }\n\n /** Normalize any thrown value into an RpcError suitable for the wire. */\n static from(err: unknown): RpcError {\n if (err instanceof RpcError) return err\n if (err instanceof Error) {\n return new RpcError(err.message, { code: RPC_ERRORS.INTERNAL_ERROR })\n }\n return new RpcError(String(err), { code: RPC_ERRORS.INTERNAL_ERROR })\n }\n}\n\n/** Reserved event names used to establish a connection. */\nexport const EVENT_READY = 'bus.ready'\nexport const EVENT_HANDSHAKE = 'bus.handshake'\n\nexport type ContextKind = 'panel' | 'widget' | 'background'\n\nexport interface HandshakeContext {\n kind: ContextKind\n /** Manifest-local contribution id. */\n id: string\n /** Host-assigned unique id for this placed instance (widgets). */\n instanceId?: string | null\n /** Widget instance a configuration panel was opened for. */\n targetInstance?: string | null\n /** Manifest-local widget id of the target instance (configuration panels). */\n targetWidget?: string | null\n}\n\nexport interface Handshake {\n host: string\n hostVersion: string\n apiVersion: string\n capabilities: string[]\n context: HandshakeContext\n}\n\n/** Payload of an `sk.<path>` Signal K value event. */\nexport interface SignalKValueEvent {\n path: string\n value: unknown\n timestamp?: string\n $source?: string\n}\n\nexport type StateScope = 'instance' | 'extension'\n\n/** Payload of a `state.changed` host event. */\nexport interface StateChangedEvent {\n scope: StateScope\n instanceId?: string | null\n keys: string[]\n}\n\n/**\n * Types for the `routes` capability — the routes the host currently has visible\n * on the chart (drafts plus stored routes the user is displaying), each\n * addressed by an opaque host-assigned `routeId`. Extensions read/write them and\n * follow lifecycle + mutation events (`route.visible` / `route.dirty` /\n * `route.saved` / `route.hidden`). Two orthogonal flags travel with a route:\n * `saved` (backed by a persisted resource) and `dirty` (has pending unsaved\n * changes). See the Plotter Extensions API spec, \"Live routes\".\n */\n\n/** A single point in a route edit buffer (`[lon, lat, alt?]`). */\nexport interface RoutePoint {\n position: [number, number, number?]\n name?: string\n description?: string\n}\n\n/** Snapshot of a route — result of `route.get`. */\nexport interface RouteData {\n routeId: string\n name: string | null\n /** Route-level description (distinct from a waypoint's RoutePoint.description). */\n description: string | null\n /** Monotonic revision; increments on every mutation. */\n rev: number\n /** Whether the route is backed by a persisted routes resource. */\n saved: boolean\n /** Whether the in-memory route has pending unsaved changes. */\n dirty: boolean\n points: RoutePoint[]\n}\n\n/** Summary entry in a `route.list` result (the visible set). */\nexport interface RouteSummary {\n routeId: string\n name: string | null\n rev: number\n pointCount: number\n saved: boolean\n dirty: boolean\n}\n\n/**\n * Payload of a `route.visible` host event — a route entered the visible set\n * (became rendered on the chart). A freshly drawn/created draft arrives\n * `saved:false, dirty:true`; a stored route brought into view arrives\n * `saved:true, dirty:false`.\n */\nexport interface RouteVisibleEvent {\n routeId: string\n rev: number\n name: string | null\n pointCount: number\n saved: boolean\n dirty: boolean\n}\n\n/**\n * Payload of a `route.hidden` host event — a route left the visible set.\n * `saved:true` ⇒ a stored route was made invisible (the resource is untouched);\n * `saved:false` ⇒ an unsaved draft was deleted (gone for good).\n */\nexport interface RouteHiddenEvent {\n routeId: string\n rev: number\n saved: boolean\n}\n\n/**\n * Payload of a `route.dirty` host event — the conformance-floor catch-all for\n * any structural/bulk change the host does not express granularly. A subscriber\n * should re-seed via `route.get`.\n */\nexport interface RouteDirtyEvent {\n routeId: string\n rev: number\n reason?: string\n}\n\n/**\n * Payload of a `route.saved` host event — the route's current state was\n * persisted to the routes resource collection. Arrives `saved:true,\n * dirty:false`; the route stays visible under the same `routeId`.\n */\nexport interface RouteSavedEvent {\n routeId: string\n rev: number\n /** Stored resource id of the persisted route. */\n href: string\n /** The route's name as persisted (may have just been set in the host's save\n * dialog), so followers can update a label without re-fetching. */\n name: string | null\n saved: boolean\n dirty: boolean\n}\n\n/** Stable `error.data.reason` strings for `route.*` host-method failures. */\nexport type RouteErrorReason =\n | 'routes.unknownId'\n | 'routes.badRequest'\n | 'routes.badRef'\n | 'routes.saveFailed'\n | 'routes.deleteFailed'\n | 'routes.saveCancelled'\n | 'routes.notSupported'\n","/**\n * eventemitter2-style wildcard matching for dot-separated event names.\n *\n * - `*` matches exactly one segment.\n * - `**` matches zero or more segments (any remainder when trailing).\n */\nexport function matchesPattern(pattern: string, name: string): boolean {\n if (pattern === name) return true\n return match(pattern.split('.'), 0, name.split('.'), 0)\n}\n\nfunction match(p: string[], pi: number, n: string[], ni: number): boolean {\n while (pi < p.length) {\n const seg = p[pi]\n if (seg === '**') {\n if (pi === p.length - 1) return true\n for (let skip = ni; skip <= n.length; skip++) {\n if (match(p, pi + 1, n, skip)) return true\n }\n return false\n }\n if (ni >= n.length) return false\n if (seg !== '*' && seg !== n[ni]) return false\n pi++\n ni++\n }\n return ni === n.length\n}\n\nexport function matchesAny(patterns: Iterable<string>, name: string): boolean {\n for (const pattern of patterns) {\n if (matchesPattern(pattern, name)) return true\n }\n return false\n}\n","import {\n BUS_ID,\n Envelope,\n JsonRpcMessage,\n JsonRpcNotification,\n JsonRpcRequest,\n JsonRpcResponse\n} from './protocol'\n\nexport function wrap(msg: JsonRpcMessage): Envelope {\n return { bus: BUS_ID, msg }\n}\n\n/**\n * Returns the JSON-RPC message inside a bus envelope, or null when the data\n * is not valid protocol traffic (so unrelated postMessage noise is ignored).\n */\nexport function unwrap(data: unknown): JsonRpcMessage | null {\n if (typeof data !== 'object' || data === null) return null\n const env = data as Record<string, unknown>\n if (env.bus !== BUS_ID) return null\n return isJsonRpcMessage(env.msg) ? env.msg : null\n}\n\nexport function isJsonRpcMessage(v: unknown): v is JsonRpcMessage {\n if (typeof v !== 'object' || v === null) return false\n const m = v as Record<string, unknown>\n if (m.jsonrpc !== '2.0') return false\n if (typeof m.method === 'string') {\n // Request (with id) or notification (without).\n return (\n m.id === undefined ||\n typeof m.id === 'string' ||\n typeof m.id === 'number'\n )\n }\n // Response: id required (null allowed for unroutable errors), exactly one\n // of result / error.\n const idOk =\n typeof m.id === 'string' || typeof m.id === 'number' || m.id === null\n if (!idOk) return false\n const hasResult = 'result' in m\n const err = m.error as Record<string, unknown> | undefined\n const hasError =\n typeof err === 'object' &&\n err !== null &&\n typeof err.code === 'number' &&\n typeof err.message === 'string'\n return hasResult ? !('error' in m) : hasError\n}\n\nexport function isRequest(msg: JsonRpcMessage): msg is JsonRpcRequest {\n return 'method' in msg && 'id' in msg && msg.id !== undefined\n}\n\nexport function isNotification(\n msg: JsonRpcMessage\n): msg is JsonRpcNotification {\n return 'method' in msg && (!('id' in msg) || msg.id === undefined)\n}\n\nexport function isResponse(msg: JsonRpcMessage): msg is JsonRpcResponse {\n return !('method' in msg)\n}\n","/**\n * Transport abstraction. The bus runs over anything that can post and\n * receive structured-cloneable data: window.postMessage between a host page\n * and an iframe, a MessageChannel port, or a test double.\n */\nexport interface BusPort {\n post(data: unknown): void\n /** Register a receive handler; returns an unlisten function. */\n listen(handler: (data: unknown) => void): () => void\n}\n\nexport interface WindowPortOptions {\n /**\n * targetOrigin for outgoing postMessage and required origin of incoming\n * messages. Defaults to the listening window's own origin (the baseline\n * deployment is same-origin). Pass '*' to disable origin checks.\n */\n origin?: string\n /** Window whose 'message' events are listened to. Defaults to globalThis. */\n listenWindow?: Window\n}\n\n/**\n * A port over window.postMessage to a peer window. On the host side the peer\n * is an iframe's contentWindow; on the extension side it is window.parent.\n * Only messages whose source is the peer (and whose origin matches) are\n * delivered.\n */\nexport function windowPort(peer: Window, opts: WindowPortOptions = {}): BusPort {\n const listenWindow =\n opts.listenWindow ?? (globalThis as unknown as Window)\n const origin = opts.origin ?? listenWindow.location?.origin ?? '*'\n return {\n post(data) {\n peer.postMessage(data, origin)\n },\n listen(handler) {\n const fn = (ev: MessageEvent) => {\n if (ev.source !== peer) return\n if (origin !== '*' && ev.origin !== origin) return\n handler(ev.data)\n }\n listenWindow.addEventListener('message', fn as EventListener)\n return () =>\n listenWindow.removeEventListener('message', fn as EventListener)\n }\n }\n}\n\ninterface MessagePortLike {\n postMessage(data: unknown): void\n addEventListener(type: 'message', fn: (ev: { data: unknown }) => void): void\n removeEventListener(\n type: 'message',\n fn: (ev: { data: unknown }) => void\n ): void\n start?(): void\n}\n\n/** A port over a MessageChannel/MessagePort (browser or Node). */\nexport function messagePort(port: MessagePortLike): BusPort {\n return {\n post(data) {\n port.postMessage(data)\n },\n listen(handler) {\n const fn = (ev: { data: unknown }) => handler(ev.data)\n port.addEventListener('message', fn)\n port.start?.()\n return () => port.removeEventListener('message', fn)\n }\n }\n}\n","import {\n isNotification,\n isRequest,\n isResponse,\n unwrap,\n wrap\n} from './codec'\nimport {\n JsonRpcId,\n JsonRpcMessage,\n JsonRpcResponse,\n RPC_ERRORS,\n RpcError\n} from './protocol'\nimport { BusPort } from './port'\nimport { matchesAny } from './wildcard'\n\nexport interface MethodContext {\n endpoint: BusEndpoint\n}\n\nexport type MethodHandler = (\n params: unknown,\n ctx: MethodContext\n) => unknown | Promise<unknown>\n\nexport type EventHandler = (name: string, params: unknown) => void\n\nexport interface BusEndpointOptions {\n port: BusPort\n /** Default timeout for outgoing calls. */\n callTimeoutMs?: number\n /** Reporter for handler/dispatch errors. Defaults to console.warn. */\n onError?: (err: unknown) => void\n}\n\ninterface PendingCall {\n resolve: (value: unknown) => void\n reject: (err: RpcError) => void\n timer: ReturnType<typeof setTimeout> | null\n}\n\nconst DEFAULT_CALL_TIMEOUT_MS = 10_000\n\n/**\n * One end of the bus: sends/receives envelopes over a BusPort, dispatches\n * incoming requests to registered methods, correlates responses to pending\n * calls by per-call nonce, and fans incoming notifications out to wildcard\n * event handlers. Symmetric — both host and extension build on this.\n */\nexport class BusEndpoint {\n readonly callTimeoutMs: number\n\n private readonly port: BusPort\n private readonly unlisten: () => void\n private readonly onError: (err: unknown) => void\n private readonly pending = new Map<JsonRpcId, PendingCall>()\n private readonly methods = new Map<string, MethodHandler>()\n private readonly eventHandlers = new Set<{\n patterns: string[]\n fn: EventHandler\n }>()\n private readonly idPrefix = Math.random().toString(36).slice(2, 8)\n private seq = 0\n private closed = false\n\n constructor(opts: BusEndpointOptions) {\n this.port = opts.port\n this.callTimeoutMs = opts.callTimeoutMs ?? DEFAULT_CALL_TIMEOUT_MS\n this.onError =\n opts.onError ??\n ((err) => console.warn('[plotterext-bus]', err))\n this.unlisten = this.port.listen((data) => this.onData(data))\n }\n\n registerMethod(name: string, handler: MethodHandler): void {\n this.methods.set(name, handler)\n }\n\n unregisterMethod(name: string): void {\n this.methods.delete(name)\n }\n\n /**\n * Handle incoming notifications whose names match any of the wildcard\n * patterns. Returns an unsubscribe function. This is local dispatch only;\n * telling the peer which events to forward is a separate concern\n * (`events.subscribe`).\n */\n onEvent(patterns: string[], fn: EventHandler): () => void {\n const entry = { patterns, fn }\n this.eventHandlers.add(entry)\n return () => this.eventHandlers.delete(entry)\n }\n\n /** Send a notification (an event) to the peer. */\n notify(method: string, params?: unknown): void {\n this.send({ jsonrpc: '2.0', method, ...(params !== undefined ? { params } : {}) })\n }\n\n /** Call a method on the peer; resolves with its result. */\n call(\n method: string,\n params?: unknown,\n opts: { timeoutMs?: number } = {}\n ): Promise<unknown> {\n if (this.closed) {\n return Promise.reject(\n new RpcError('Bus endpoint is closed', {\n code: RPC_ERRORS.CONNECTION_CLOSED,\n reason: 'CLOSED'\n })\n )\n }\n const id = `${this.idPrefix}-${++this.seq}`\n const timeoutMs = opts.timeoutMs ?? this.callTimeoutMs\n return new Promise<unknown>((resolve, reject) => {\n const timer =\n timeoutMs > 0\n ? setTimeout(() => {\n this.pending.delete(id)\n reject(\n new RpcError(`Call timed out after ${timeoutMs}ms: ${method}`, {\n code: RPC_ERRORS.TIMEOUT,\n reason: 'TIMEOUT'\n })\n )\n }, timeoutMs)\n : null\n this.pending.set(id, { resolve, reject, timer })\n this.send({\n jsonrpc: '2.0',\n id,\n method,\n ...(params !== undefined ? { params } : {})\n })\n })\n }\n\n close(): void {\n if (this.closed) return\n this.closed = true\n this.unlisten()\n for (const [, p] of this.pending) {\n if (p.timer) clearTimeout(p.timer)\n p.reject(\n new RpcError('Bus endpoint closed', {\n code: RPC_ERRORS.CONNECTION_CLOSED,\n reason: 'CLOSED'\n })\n )\n }\n this.pending.clear()\n this.eventHandlers.clear()\n }\n\n private send(msg: JsonRpcMessage): void {\n if (this.closed) return\n this.port.post(wrap(msg))\n }\n\n private onData(data: unknown): void {\n const msg = unwrap(data)\n if (!msg) return\n if (isResponse(msg)) {\n this.onResponse(msg)\n } else if (isRequest(msg)) {\n void this.onRequest(msg)\n } else if (isNotification(msg)) {\n this.onNotification(msg.method, msg.params)\n }\n }\n\n private onResponse(msg: JsonRpcResponse): void {\n if (msg.id === null) return\n const p = this.pending.get(msg.id)\n if (!p) return\n this.pending.delete(msg.id)\n if (p.timer) clearTimeout(p.timer)\n if ('error' in msg) {\n p.reject(RpcError.fromErrorObject(msg.error))\n } else {\n p.resolve(msg.result)\n }\n }\n\n private async onRequest(msg: {\n id: JsonRpcId\n method: string\n params?: unknown\n }): Promise<void> {\n const handler = this.methods.get(msg.method)\n if (!handler) {\n this.send({\n jsonrpc: '2.0',\n id: msg.id,\n error: {\n code: RPC_ERRORS.METHOD_NOT_FOUND,\n message: `Method not found: ${msg.method}`\n }\n })\n return\n }\n try {\n const result = await handler(msg.params, { endpoint: this })\n this.send({\n jsonrpc: '2.0',\n id: msg.id,\n result: result === undefined ? null : result\n })\n } catch (err) {\n this.send({\n jsonrpc: '2.0',\n id: msg.id,\n error: RpcError.from(err).toErrorObject()\n })\n }\n }\n\n private onNotification(name: string, params: unknown): void {\n for (const entry of [...this.eventHandlers]) {\n if (matchesAny(entry.patterns, name)) {\n try {\n entry.fn(name, params)\n } catch (err) {\n this.onError(err)\n }\n }\n }\n }\n}\n","import { BusEndpoint, MethodHandler } from './endpoint'\nimport { BusPort } from './port'\nimport {\n EVENT_HANDSHAKE,\n EVENT_READY,\n HandshakeContext,\n RPC_ERRORS,\n RpcError\n} from './protocol'\nimport { matchesAny } from './wildcard'\n\nexport interface HostInfo {\n host: string\n hostVersion: string\n apiVersion: string\n capabilities: string[]\n}\n\nexport interface HostConnectionOptions {\n port: BusPort\n hostInfo: HostInfo\n context: HandshakeContext\n /** Host API methods (e.g. 'state.get', 'signalk.subscribe', 'map.center'). */\n methods?: Record<string, MethodHandler>\n callTimeoutMs?: number\n onError?: (err: unknown) => void\n /**\n * Called whenever the union of subscribed event patterns changes, with the\n * current pattern list. Lets the host start/stop upstream work lazily.\n */\n onSubscriptionsChanged?: (patterns: string[]) => void\n}\n\n/**\n * The host side of one extension context (one iframe). Sends the handshake\n * in reply to `bus.ready`, dispatches host API methods, and tracks the\n * context's event subscriptions so `publish()` only forwards events the\n * context asked for.\n */\nexport class HostConnection {\n readonly endpoint: BusEndpoint\n readonly context: HandshakeContext\n\n private readonly hostInfo: HostInfo\n private readonly subs = new Map<string, string[]>()\n private readonly onSubscriptionsChanged?: (patterns: string[]) => void\n private subSeq = 0\n\n constructor(opts: HostConnectionOptions) {\n this.hostInfo = opts.hostInfo\n this.context = opts.context\n this.onSubscriptionsChanged = opts.onSubscriptionsChanged\n this.endpoint = new BusEndpoint({\n port: opts.port,\n callTimeoutMs: opts.callTimeoutMs,\n onError: opts.onError\n })\n for (const [name, handler] of Object.entries(opts.methods ?? {})) {\n this.endpoint.registerMethod(name, handler)\n }\n this.endpoint.registerMethod('events.subscribe', (params) =>\n this.handleSubscribe(params)\n )\n this.endpoint.registerMethod('events.unsubscribe', (params) =>\n this.handleUnsubscribe(params)\n )\n this.endpoint.onEvent([EVENT_READY], () => this.sendHandshake())\n }\n\n registerMethod(name: string, handler: MethodHandler): void {\n this.endpoint.registerMethod(name, handler)\n }\n\n /**\n * Publish an event to this context. Delivered only when the context has a\n * matching subscription; returns whether it was delivered.\n */\n publish(eventName: string, params?: unknown): boolean {\n if (!this.hasSubscriber(eventName)) return false\n this.endpoint.notify(eventName, params)\n return true\n }\n\n hasSubscriber(eventName: string): boolean {\n for (const [, patterns] of this.subs) {\n if (matchesAny(patterns, eventName)) return true\n }\n return false\n }\n\n /** Union of currently subscribed patterns. */\n subscribedPatterns(): string[] {\n const all = new Set<string>()\n for (const [, patterns] of this.subs) {\n for (const p of patterns) all.add(p)\n }\n return [...all]\n }\n\n close(): void {\n this.endpoint.close()\n this.subs.clear()\n }\n\n private sendHandshake(): void {\n this.endpoint.notify(EVENT_HANDSHAKE, {\n ...this.hostInfo,\n context: this.context\n })\n }\n\n private handleSubscribe(params: unknown): { subscriptionId: string } {\n const patterns = (params as { patterns?: unknown })?.patterns\n if (\n !Array.isArray(patterns) ||\n patterns.length === 0 ||\n !patterns.every((p) => typeof p === 'string' && p.length > 0)\n ) {\n throw new RpcError('events.subscribe requires a non-empty patterns array', {\n code: RPC_ERRORS.INVALID_PARAMS,\n reason: 'INVALID_PATTERNS'\n })\n }\n const subscriptionId = `sub-${++this.subSeq}`\n this.subs.set(subscriptionId, patterns as string[])\n this.onSubscriptionsChanged?.(this.subscribedPatterns())\n return { subscriptionId }\n }\n\n private handleUnsubscribe(params: unknown): Record<string, never> {\n const id = (params as { subscriptionId?: unknown })?.subscriptionId\n if (typeof id !== 'string' || !this.subs.has(id)) {\n throw new RpcError('Unknown subscriptionId', {\n code: RPC_ERRORS.INVALID_PARAMS,\n reason: 'UNKNOWN_SUBSCRIPTION'\n })\n }\n this.subs.delete(id)\n this.onSubscriptionsChanged?.(this.subscribedPatterns())\n return {}\n }\n}\n\nexport * from './protocol'\nexport * from './wildcard'\nexport { BusEndpoint } from './endpoint'\nexport type { MethodHandler, MethodContext, EventHandler } from './endpoint'\nexport * from './port'\n","import { BusEndpoint, EventHandler } from './endpoint'\nimport { BusPort, windowPort } from './port'\nimport {\n EVENT_HANDSHAKE,\n EVENT_READY,\n Handshake,\n HandshakeContext,\n RouteData,\n RoutePoint,\n RouteSummary,\n RPC_ERRORS,\n RpcError,\n SignalKValueEvent,\n StateScope\n} from './protocol'\n\nexport interface ConnectOptions {\n /** Transport. Defaults to window.postMessage to window.parent. */\n port?: BusPort\n /** Interval for re-sending bus.ready until the handshake arrives. */\n readyIntervalMs?: number\n /** How long to wait for the host handshake before rejecting. */\n timeoutMs?: number\n /** Default timeout for host API calls. */\n callTimeoutMs?: number\n onError?: (err: unknown) => void\n}\n\nexport type Unsubscribe = () => Promise<void>\n\n/**\n * The extension side of the bus: one per iframe context (panel, widget, or\n * background runtime). Create via connectExtension().\n */\nexport class ExtensionClient {\n readonly handshake: Handshake\n readonly endpoint: BusEndpoint\n\n constructor(endpoint: BusEndpoint, handshake: Handshake) {\n this.endpoint = endpoint\n this.handshake = handshake\n }\n\n get context(): HandshakeContext {\n return this.handshake.context\n }\n\n get apiVersion(): string {\n return this.handshake.apiVersion\n }\n\n get capabilities(): string[] {\n return this.handshake.capabilities\n }\n\n hasCapability(id: string): boolean {\n return this.handshake.capabilities.includes(id)\n }\n\n /** Call a host API method. */\n call(\n method: string,\n params?: unknown,\n opts?: { timeoutMs?: number }\n ): Promise<unknown> {\n return this.endpoint.call(method, params, opts)\n }\n\n /** Send a notification to the host. */\n notify(method: string, params?: unknown): void {\n this.endpoint.notify(method, params)\n }\n\n /**\n * Subscribe to host events matching wildcard patterns. Registers both the\n * host-side forwarding subscription and local dispatch; the returned\n * function tears down both.\n */\n async subscribe(\n patterns: string[],\n handler: EventHandler\n ): Promise<Unsubscribe> {\n const off = this.endpoint.onEvent(patterns, handler)\n let subscriptionId: string\n try {\n const result = (await this.call('events.subscribe', { patterns })) as {\n subscriptionId: string\n }\n subscriptionId = result.subscriptionId\n } catch (err) {\n off()\n throw err\n }\n return async () => {\n off()\n await this.call('events.unsubscribe', { subscriptionId }).catch(() => {\n // Best-effort: the host may already have dropped the connection.\n })\n }\n }\n\n /** Host-persisted key/value state (see spec: State Storage). */\n readonly state = {\n get: async (\n keys?: string[],\n scope?: StateScope\n ): Promise<Record<string, unknown>> => {\n const result = (await this.call('state.get', {\n ...(scope ? { scope } : {}),\n ...(keys ? { keys } : {})\n })) as { values: Record<string, unknown> }\n return result.values ?? {}\n },\n set: async (\n values: Record<string, unknown>,\n scope?: StateScope\n ): Promise<void> => {\n await this.call('state.set', {\n ...(scope ? { scope } : {}),\n values\n })\n }\n }\n\n /** Signal K data relayed by the host (capabilities signalk.stream / .put). */\n readonly signalk = {\n /**\n * Subscribe to Signal K path values. The host publishes them as\n * `sk.<path>` events; this helper hides the event-name mapping and\n * establishes both the event-forwarding subscription and the host's\n * upstream Signal K subscription.\n */\n subscribe: async (\n paths: string[],\n handler: (ev: SignalKValueEvent) => void\n ): Promise<Unsubscribe> => {\n const patterns = paths.map((p) => `sk.${p}`)\n const offEvents = await this.subscribe(patterns, (_name, params) =>\n handler(params as SignalKValueEvent)\n )\n let subscriptionId: string\n try {\n const result = (await this.call('signalk.subscribe', { paths })) as {\n subscriptionId: string\n }\n subscriptionId = result.subscriptionId\n } catch (err) {\n await offEvents()\n throw err\n }\n return async () => {\n await offEvents()\n await this.call('signalk.unsubscribe', { subscriptionId }).catch(\n () => {}\n )\n }\n },\n put: (path: string, value: unknown): Promise<unknown> => {\n return this.call('signalk.put', { path, value })\n }\n }\n\n /**\n * The host's visible routes (capability `routes`). Thin **typed** wrappers over\n * the host's `route.*` methods — each just delegates to `this.call(...)`, so a\n * plain-JS extension can call `client.call('route.replace', …)` directly and a\n * TypeScript extension gets the typed `client.route.replace(…)` sugar with no\n * behavioural difference. Follow lifecycle + mutations by subscribing to\n * `route.**` events (`RouteVisibleEvent` / `RouteDirtyEvent` / `RouteSavedEvent`\n * / `RouteHiddenEvent`). Further operations (rename/point ops) extend this\n * surface as the capability fills out.\n */\n readonly route = {\n list: async (): Promise<RouteSummary[]> => {\n const result = (await this.call('route.list')) as {\n routes?: RouteSummary[]\n }\n return result.routes ?? []\n },\n create: async (opts: {\n points: RoutePoint[]\n name?: string\n description?: string\n }): Promise<{ routeId: string; rev: number }> => {\n // A route needs at least two points to form a segment; the host rejects\n // fewer with routes.badRequest.\n return (await this.call('route.create', opts)) as {\n routeId: string\n rev: number\n }\n },\n get: async (routeId: string): Promise<RouteData> => {\n return (await this.call('route.get', { routeId })) as RouteData\n },\n replace: async (\n routeId: string,\n points: RoutePoint[]\n ): Promise<{ rev: number }> => {\n return (await this.call('route.replace', { routeId, points })) as {\n rev: number\n }\n },\n save: async (\n routeId: string,\n opts?: { name?: string; description?: string; dialog?: boolean }\n ): Promise<{ href: string; rev: number }> => {\n return (await this.call('route.save', {\n routeId,\n ...(opts ?? {})\n })) as { href: string; rev: number }\n },\n show: async (ref: string): Promise<{ routeId: string; rev: number }> => {\n return (await this.call('route.show', { ref })) as {\n routeId: string\n rev: number\n }\n },\n hide: async (routeId: string): Promise<void> => {\n await this.call('route.hide', { routeId })\n },\n delete: async (routeId: string): Promise<void> => {\n await this.call('route.delete', { routeId })\n }\n }\n\n close(): void {\n this.endpoint.close()\n }\n}\n\n/**\n * Connect to the host from inside an extension iframe. Sends `bus.ready`\n * (repeating until answered) and resolves once the host's `bus.handshake`\n * arrives.\n */\nexport function connectExtension(\n opts: ConnectOptions = {}\n): Promise<ExtensionClient> {\n // Default transport: postMessage to the embedding window. Origin checks\n // are relaxed to '*' on the extension side because the host page's origin\n // may legitimately differ from the extension asset origin (e.g. a host dev\n // server embedding extension assets served by the Signal K server); the\n // peer-source check in windowPort still applies, and the host side\n // enforces a strict origin for the extension's frame.\n const port =\n opts.port ??\n windowPort((globalThis as unknown as Window).parent as Window, {\n origin: '*'\n })\n const endpoint = new BusEndpoint({\n port,\n callTimeoutMs: opts.callTimeoutMs,\n onError: opts.onError\n })\n return new Promise<ExtensionClient>((resolve, reject) => {\n let done = false\n const off = endpoint.onEvent([EVENT_HANDSHAKE], (_name, params) => {\n if (done) return\n done = true\n cleanup()\n resolve(new ExtensionClient(endpoint, params as Handshake))\n })\n const interval = setInterval(\n () => endpoint.notify(EVENT_READY),\n opts.readyIntervalMs ?? 250\n )\n const timeout = setTimeout(() => {\n if (done) return\n done = true\n cleanup()\n endpoint.close()\n reject(\n new RpcError('Timed out waiting for host handshake', {\n code: RPC_ERRORS.TIMEOUT,\n reason: 'HANDSHAKE_TIMEOUT'\n })\n )\n }, opts.timeoutMs ?? 10_000)\n const cleanup = () => {\n off()\n clearInterval(interval)\n clearTimeout(timeout)\n }\n endpoint.notify(EVENT_READY)\n })\n}\n\nexport * from './protocol'\nexport * from './wildcard'\nexport { BusEndpoint } from './endpoint'\nexport type { MethodHandler, MethodContext, EventHandler } from './endpoint'\nexport * from './port'\n"],"mappings":"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","names":[]}
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { J as JsonRpcMessage, a as JsonRpcNotification, b as JsonRpcRequest, c as JsonRpcResponse, E as Envelope } from './wildcard-BHRv-WAr.cjs';
2
- export { B as BUS_ID, d as BusEndpoint, e as BusEndpointOptions, f as BusPort, C as ContextKind, g as EVENT_HANDSHAKE, h as EVENT_READY, i as EventHandler, H as Handshake, j as HandshakeContext, k as JsonRpcErrorObject, l as JsonRpcErrorResponse, m as JsonRpcId, n as JsonRpcSuccessResponse, M as MethodContext, o as MethodHandler, R as RPC_ERRORS, p as RouteData, q as RouteDirtyEvent, r as RouteErrorReason, s as RouteHiddenEvent, t as RoutePoint, u as RouteSavedEvent, v as RouteSummary, w as RouteVisibleEvent, x as RpcError, S as SignalKValueEvent, y as StateChangedEvent, z as StateScope, W as WindowPortOptions, A as matchesAny, D as matchesPattern, F as messagePort, G as windowPort } from './wildcard-BHRv-WAr.cjs';
1
+ import { J as JsonRpcMessage, a as JsonRpcNotification, b as JsonRpcRequest, c as JsonRpcResponse, E as Envelope } from './wildcard-B5Dp4MA1.cjs';
2
+ export { B as BUS_ID, d as BusEndpoint, e as BusEndpointOptions, f as BusPort, C as ContextKind, g as EVENT_HANDSHAKE, h as EVENT_READY, i as EventHandler, H as Handshake, j as HandshakeContext, k as JsonRpcErrorObject, l as JsonRpcErrorResponse, m as JsonRpcId, n as JsonRpcSuccessResponse, M as MethodContext, o as MethodHandler, R as RPC_ERRORS, p as RouteData, q as RouteDirtyEvent, r as RouteErrorReason, s as RouteHiddenEvent, t as RoutePoint, u as RouteSavedEvent, v as RouteSummary, w as RouteVisibleEvent, x as RpcError, S as SignalKValueEvent, y as StateChangedEvent, z as StateScope, W as WindowPortOptions, A as matchesAny, D as matchesPattern, F as messagePort, G as windowPort } from './wildcard-B5Dp4MA1.cjs';
3
3
  export { HostConnection, HostConnectionOptions, HostInfo } from './host.cjs';
4
4
  export { ConnectOptions, ExtensionClient, Unsubscribe, connectExtension } from './extension.cjs';
5
5
 
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { J as JsonRpcMessage, a as JsonRpcNotification, b as JsonRpcRequest, c as JsonRpcResponse, E as Envelope } from './wildcard-BHRv-WAr.js';
2
- export { B as BUS_ID, d as BusEndpoint, e as BusEndpointOptions, f as BusPort, C as ContextKind, g as EVENT_HANDSHAKE, h as EVENT_READY, i as EventHandler, H as Handshake, j as HandshakeContext, k as JsonRpcErrorObject, l as JsonRpcErrorResponse, m as JsonRpcId, n as JsonRpcSuccessResponse, M as MethodContext, o as MethodHandler, R as RPC_ERRORS, p as RouteData, q as RouteDirtyEvent, r as RouteErrorReason, s as RouteHiddenEvent, t as RoutePoint, u as RouteSavedEvent, v as RouteSummary, w as RouteVisibleEvent, x as RpcError, S as SignalKValueEvent, y as StateChangedEvent, z as StateScope, W as WindowPortOptions, A as matchesAny, D as matchesPattern, F as messagePort, G as windowPort } from './wildcard-BHRv-WAr.js';
1
+ import { J as JsonRpcMessage, a as JsonRpcNotification, b as JsonRpcRequest, c as JsonRpcResponse, E as Envelope } from './wildcard-B5Dp4MA1.js';
2
+ export { B as BUS_ID, d as BusEndpoint, e as BusEndpointOptions, f as BusPort, C as ContextKind, g as EVENT_HANDSHAKE, h as EVENT_READY, i as EventHandler, H as Handshake, j as HandshakeContext, k as JsonRpcErrorObject, l as JsonRpcErrorResponse, m as JsonRpcId, n as JsonRpcSuccessResponse, M as MethodContext, o as MethodHandler, R as RPC_ERRORS, p as RouteData, q as RouteDirtyEvent, r as RouteErrorReason, s as RouteHiddenEvent, t as RoutePoint, u as RouteSavedEvent, v as RouteSummary, w as RouteVisibleEvent, x as RpcError, S as SignalKValueEvent, y as StateChangedEvent, z as StateScope, W as WindowPortOptions, A as matchesAny, D as matchesPattern, F as messagePort, G as windowPort } from './wildcard-B5Dp4MA1.js';
3
3
  export { HostConnection, HostConnectionOptions, HostInfo } from './host.js';
4
4
  export { ConnectOptions, ExtensionClient, Unsubscribe, connectExtension } from './extension.js';
5
5
 
package/dist/index.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  HostConnection
3
- } from "./chunk-W5VF4P6R.js";
3
+ } from "./chunk-KZCRGKFF.js";
4
4
  import {
5
5
  ExtensionClient,
6
6
  connectExtension
7
- } from "./chunk-YZVRR7LW.js";
7
+ } from "./chunk-YL2LWXNX.js";
8
8
  import {
9
9
  BUS_ID,
10
10
  BusEndpoint,
@@ -22,7 +22,7 @@ import {
22
22
  unwrap,
23
23
  windowPort,
24
24
  wrap
25
- } from "./chunk-VBS4I6NW.js";
25
+ } from "./chunk-LVGWVV2W.js";
26
26
  export {
27
27
  BUS_ID,
28
28
  BusEndpoint,
@@ -293,7 +293,7 @@ interface RouteSavedEvent {
293
293
  dirty: boolean;
294
294
  }
295
295
  /** Stable `error.data.reason` strings for `route.*` host-method failures. */
296
- type RouteErrorReason = 'routes.unknownId' | 'routes.badRequest' | 'routes.badRef' | 'routes.saveFailed' | 'routes.saveCancelled' | 'routes.notSupported';
296
+ type RouteErrorReason = 'routes.unknownId' | 'routes.badRequest' | 'routes.badRef' | 'routes.saveFailed' | 'routes.deleteFailed' | 'routes.saveCancelled' | 'routes.notSupported';
297
297
 
298
298
  /**
299
299
  * eventemitter2-style wildcard matching for dot-separated event names.
@@ -293,7 +293,7 @@ interface RouteSavedEvent {
293
293
  dirty: boolean;
294
294
  }
295
295
  /** Stable `error.data.reason` strings for `route.*` host-method failures. */
296
- type RouteErrorReason = 'routes.unknownId' | 'routes.badRequest' | 'routes.badRef' | 'routes.saveFailed' | 'routes.saveCancelled' | 'routes.notSupported';
296
+ type RouteErrorReason = 'routes.unknownId' | 'routes.badRequest' | 'routes.badRef' | 'routes.saveFailed' | 'routes.deleteFailed' | 'routes.saveCancelled' | 'routes.notSupported';
297
297
 
298
298
  /**
299
299
  * eventemitter2-style wildcard matching for dot-separated event names.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "signalk-plotterext-bus",
3
- "version": "0.7.2",
3
+ "version": "0.7.3",
4
4
  "description": "Reference implementation of the Signal K plotter extension message bus: JSON-RPC 2.0 over postMessage with wildcard event subscriptions",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/protocol.ts","../src/wildcard.ts","../src/codec.ts","../src/port.ts","../src/endpoint.ts"],"sourcesContent":["/**\n * Wire protocol types and constants for the Signal K plotter extension bus.\n *\n * The wire format is JSON-RPC 2.0 (https://www.jsonrpc.org/specification)\n * inside a routing envelope: `{ bus: \"plotterExt/1\", msg: <JSON-RPC object> }`.\n * Calls are JSON-RPC requests; events are JSON-RPC notifications whose\n * `method` is a hierarchical dot-separated event name.\n */\n\nexport const BUS_ID = 'plotterExt/1'\n\nexport type JsonRpcId = string | number\n\nexport interface JsonRpcRequest {\n jsonrpc: '2.0'\n id: JsonRpcId\n method: string\n params?: unknown\n}\n\nexport interface JsonRpcNotification {\n jsonrpc: '2.0'\n method: string\n params?: unknown\n}\n\nexport interface JsonRpcErrorObject {\n code: number\n message: string\n data?: { reason?: string; [key: string]: unknown }\n}\n\nexport interface JsonRpcSuccessResponse {\n jsonrpc: '2.0'\n id: JsonRpcId\n result: unknown\n}\n\nexport interface JsonRpcErrorResponse {\n jsonrpc: '2.0'\n id: JsonRpcId | null\n error: JsonRpcErrorObject\n}\n\nexport type JsonRpcResponse = JsonRpcSuccessResponse | JsonRpcErrorResponse\n\nexport type JsonRpcMessage =\n | JsonRpcRequest\n | JsonRpcNotification\n | JsonRpcResponse\n\nexport interface Envelope {\n bus: typeof BUS_ID\n msg: JsonRpcMessage\n}\n\n/**\n * JSON-RPC reserved codes for protocol errors, plus implementation-defined\n * codes (-32000..-32099 range) used by this package. Host API errors should\n * use HOST_ERROR with a stable string identifier in `error.data.reason`.\n */\nexport const RPC_ERRORS = {\n PARSE_ERROR: -32700,\n INVALID_REQUEST: -32600,\n METHOD_NOT_FOUND: -32601,\n INVALID_PARAMS: -32602,\n INTERNAL_ERROR: -32603,\n HOST_ERROR: -32000,\n TIMEOUT: -32001,\n CONNECTION_CLOSED: -32002\n} as const\n\nexport class RpcError extends Error {\n readonly code: number\n readonly data?: { reason?: string; [key: string]: unknown }\n\n constructor(\n message: string,\n opts: {\n code?: number\n reason?: string\n data?: Record<string, unknown>\n } = {}\n ) {\n super(message)\n this.name = 'RpcError'\n this.code = opts.code ?? RPC_ERRORS.HOST_ERROR\n const data: Record<string, unknown> = { ...(opts.data ?? {}) }\n if (opts.reason !== undefined) data.reason = opts.reason\n this.data = Object.keys(data).length > 0 ? data : undefined\n }\n\n get reason(): string | undefined {\n return typeof this.data?.reason === 'string' ? this.data.reason : undefined\n }\n\n toErrorObject(): JsonRpcErrorObject {\n return {\n code: this.code,\n message: this.message,\n ...(this.data ? { data: this.data } : {})\n }\n }\n\n static fromErrorObject(err: JsonRpcErrorObject): RpcError {\n return new RpcError(err.message, { code: err.code, data: err.data })\n }\n\n /** Normalize any thrown value into an RpcError suitable for the wire. */\n static from(err: unknown): RpcError {\n if (err instanceof RpcError) return err\n if (err instanceof Error) {\n return new RpcError(err.message, { code: RPC_ERRORS.INTERNAL_ERROR })\n }\n return new RpcError(String(err), { code: RPC_ERRORS.INTERNAL_ERROR })\n }\n}\n\n/** Reserved event names used to establish a connection. */\nexport const EVENT_READY = 'bus.ready'\nexport const EVENT_HANDSHAKE = 'bus.handshake'\n\nexport type ContextKind = 'panel' | 'widget' | 'background'\n\nexport interface HandshakeContext {\n kind: ContextKind\n /** Manifest-local contribution id. */\n id: string\n /** Host-assigned unique id for this placed instance (widgets). */\n instanceId?: string | null\n /** Widget instance a configuration panel was opened for. */\n targetInstance?: string | null\n /** Manifest-local widget id of the target instance (configuration panels). */\n targetWidget?: string | null\n}\n\nexport interface Handshake {\n host: string\n hostVersion: string\n apiVersion: string\n capabilities: string[]\n context: HandshakeContext\n}\n\n/** Payload of an `sk.<path>` Signal K value event. */\nexport interface SignalKValueEvent {\n path: string\n value: unknown\n timestamp?: string\n $source?: string\n}\n\nexport type StateScope = 'instance' | 'extension'\n\n/** Payload of a `state.changed` host event. */\nexport interface StateChangedEvent {\n scope: StateScope\n instanceId?: string | null\n keys: string[]\n}\n\n/**\n * Types for the `routes` capability — the routes the host currently has visible\n * on the chart (drafts plus stored routes the user is displaying), each\n * addressed by an opaque host-assigned `routeId`. Extensions read/write them and\n * follow lifecycle + mutation events (`route.visible` / `route.dirty` /\n * `route.saved` / `route.hidden`). Two orthogonal flags travel with a route:\n * `saved` (backed by a persisted resource) and `dirty` (has pending unsaved\n * changes). See the Plotter Extensions API spec, \"Live routes\".\n */\n\n/** A single point in a route edit buffer (`[lon, lat, alt?]`). */\nexport interface RoutePoint {\n position: [number, number, number?]\n name?: string\n description?: string\n}\n\n/** Snapshot of a route — result of `route.get`. */\nexport interface RouteData {\n routeId: string\n name: string | null\n /** Route-level description (distinct from a waypoint's RoutePoint.description). */\n description: string | null\n /** Monotonic revision; increments on every mutation. */\n rev: number\n /** Whether the route is backed by a persisted routes resource. */\n saved: boolean\n /** Whether the in-memory route has pending unsaved changes. */\n dirty: boolean\n points: RoutePoint[]\n}\n\n/** Summary entry in a `route.list` result (the visible set). */\nexport interface RouteSummary {\n routeId: string\n name: string | null\n rev: number\n pointCount: number\n saved: boolean\n dirty: boolean\n}\n\n/**\n * Payload of a `route.visible` host event — a route entered the visible set\n * (became rendered on the chart). A freshly drawn/created draft arrives\n * `saved:false, dirty:true`; a stored route brought into view arrives\n * `saved:true, dirty:false`.\n */\nexport interface RouteVisibleEvent {\n routeId: string\n rev: number\n name: string | null\n pointCount: number\n saved: boolean\n dirty: boolean\n}\n\n/**\n * Payload of a `route.hidden` host event — a route left the visible set.\n * `saved:true` ⇒ a stored route was made invisible (the resource is untouched);\n * `saved:false` ⇒ an unsaved draft was deleted (gone for good).\n */\nexport interface RouteHiddenEvent {\n routeId: string\n rev: number\n saved: boolean\n}\n\n/**\n * Payload of a `route.dirty` host event — the conformance-floor catch-all for\n * any structural/bulk change the host does not express granularly. A subscriber\n * should re-seed via `route.get`.\n */\nexport interface RouteDirtyEvent {\n routeId: string\n rev: number\n reason?: string\n}\n\n/**\n * Payload of a `route.saved` host event — the route's current state was\n * persisted to the routes resource collection. Arrives `saved:true,\n * dirty:false`; the route stays visible under the same `routeId`.\n */\nexport interface RouteSavedEvent {\n routeId: string\n rev: number\n /** Stored resource id of the persisted route. */\n href: string\n /** The route's name as persisted (may have just been set in the host's save\n * dialog), so followers can update a label without re-fetching. */\n name: string | null\n saved: boolean\n dirty: boolean\n}\n\n/** Stable `error.data.reason` strings for `route.*` host-method failures. */\nexport type RouteErrorReason =\n | 'routes.unknownId'\n | 'routes.badRequest'\n | 'routes.badRef'\n | 'routes.saveFailed'\n | 'routes.saveCancelled'\n | 'routes.notSupported'\n","/**\n * eventemitter2-style wildcard matching for dot-separated event names.\n *\n * - `*` matches exactly one segment.\n * - `**` matches zero or more segments (any remainder when trailing).\n */\nexport function matchesPattern(pattern: string, name: string): boolean {\n if (pattern === name) return true\n return match(pattern.split('.'), 0, name.split('.'), 0)\n}\n\nfunction match(p: string[], pi: number, n: string[], ni: number): boolean {\n while (pi < p.length) {\n const seg = p[pi]\n if (seg === '**') {\n if (pi === p.length - 1) return true\n for (let skip = ni; skip <= n.length; skip++) {\n if (match(p, pi + 1, n, skip)) return true\n }\n return false\n }\n if (ni >= n.length) return false\n if (seg !== '*' && seg !== n[ni]) return false\n pi++\n ni++\n }\n return ni === n.length\n}\n\nexport function matchesAny(patterns: Iterable<string>, name: string): boolean {\n for (const pattern of patterns) {\n if (matchesPattern(pattern, name)) return true\n }\n return false\n}\n","import {\n BUS_ID,\n Envelope,\n JsonRpcMessage,\n JsonRpcNotification,\n JsonRpcRequest,\n JsonRpcResponse\n} from './protocol'\n\nexport function wrap(msg: JsonRpcMessage): Envelope {\n return { bus: BUS_ID, msg }\n}\n\n/**\n * Returns the JSON-RPC message inside a bus envelope, or null when the data\n * is not valid protocol traffic (so unrelated postMessage noise is ignored).\n */\nexport function unwrap(data: unknown): JsonRpcMessage | null {\n if (typeof data !== 'object' || data === null) return null\n const env = data as Record<string, unknown>\n if (env.bus !== BUS_ID) return null\n return isJsonRpcMessage(env.msg) ? env.msg : null\n}\n\nexport function isJsonRpcMessage(v: unknown): v is JsonRpcMessage {\n if (typeof v !== 'object' || v === null) return false\n const m = v as Record<string, unknown>\n if (m.jsonrpc !== '2.0') return false\n if (typeof m.method === 'string') {\n // Request (with id) or notification (without).\n return (\n m.id === undefined ||\n typeof m.id === 'string' ||\n typeof m.id === 'number'\n )\n }\n // Response: id required (null allowed for unroutable errors), exactly one\n // of result / error.\n const idOk =\n typeof m.id === 'string' || typeof m.id === 'number' || m.id === null\n if (!idOk) return false\n const hasResult = 'result' in m\n const err = m.error as Record<string, unknown> | undefined\n const hasError =\n typeof err === 'object' &&\n err !== null &&\n typeof err.code === 'number' &&\n typeof err.message === 'string'\n return hasResult ? !('error' in m) : hasError\n}\n\nexport function isRequest(msg: JsonRpcMessage): msg is JsonRpcRequest {\n return 'method' in msg && 'id' in msg && msg.id !== undefined\n}\n\nexport function isNotification(\n msg: JsonRpcMessage\n): msg is JsonRpcNotification {\n return 'method' in msg && (!('id' in msg) || msg.id === undefined)\n}\n\nexport function isResponse(msg: JsonRpcMessage): msg is JsonRpcResponse {\n return !('method' in msg)\n}\n","/**\n * Transport abstraction. The bus runs over anything that can post and\n * receive structured-cloneable data: window.postMessage between a host page\n * and an iframe, a MessageChannel port, or a test double.\n */\nexport interface BusPort {\n post(data: unknown): void\n /** Register a receive handler; returns an unlisten function. */\n listen(handler: (data: unknown) => void): () => void\n}\n\nexport interface WindowPortOptions {\n /**\n * targetOrigin for outgoing postMessage and required origin of incoming\n * messages. Defaults to the listening window's own origin (the baseline\n * deployment is same-origin). Pass '*' to disable origin checks.\n */\n origin?: string\n /** Window whose 'message' events are listened to. Defaults to globalThis. */\n listenWindow?: Window\n}\n\n/**\n * A port over window.postMessage to a peer window. On the host side the peer\n * is an iframe's contentWindow; on the extension side it is window.parent.\n * Only messages whose source is the peer (and whose origin matches) are\n * delivered.\n */\nexport function windowPort(peer: Window, opts: WindowPortOptions = {}): BusPort {\n const listenWindow =\n opts.listenWindow ?? (globalThis as unknown as Window)\n const origin = opts.origin ?? listenWindow.location?.origin ?? '*'\n return {\n post(data) {\n peer.postMessage(data, origin)\n },\n listen(handler) {\n const fn = (ev: MessageEvent) => {\n if (ev.source !== peer) return\n if (origin !== '*' && ev.origin !== origin) return\n handler(ev.data)\n }\n listenWindow.addEventListener('message', fn as EventListener)\n return () =>\n listenWindow.removeEventListener('message', fn as EventListener)\n }\n }\n}\n\ninterface MessagePortLike {\n postMessage(data: unknown): void\n addEventListener(type: 'message', fn: (ev: { data: unknown }) => void): void\n removeEventListener(\n type: 'message',\n fn: (ev: { data: unknown }) => void\n ): void\n start?(): void\n}\n\n/** A port over a MessageChannel/MessagePort (browser or Node). */\nexport function messagePort(port: MessagePortLike): BusPort {\n return {\n post(data) {\n port.postMessage(data)\n },\n listen(handler) {\n const fn = (ev: { data: unknown }) => handler(ev.data)\n port.addEventListener('message', fn)\n port.start?.()\n return () => port.removeEventListener('message', fn)\n }\n }\n}\n","import {\n isNotification,\n isRequest,\n isResponse,\n unwrap,\n wrap\n} from './codec'\nimport {\n JsonRpcId,\n JsonRpcMessage,\n JsonRpcResponse,\n RPC_ERRORS,\n RpcError\n} from './protocol'\nimport { BusPort } from './port'\nimport { matchesAny } from './wildcard'\n\nexport interface MethodContext {\n endpoint: BusEndpoint\n}\n\nexport type MethodHandler = (\n params: unknown,\n ctx: MethodContext\n) => unknown | Promise<unknown>\n\nexport type EventHandler = (name: string, params: unknown) => void\n\nexport interface BusEndpointOptions {\n port: BusPort\n /** Default timeout for outgoing calls. */\n callTimeoutMs?: number\n /** Reporter for handler/dispatch errors. Defaults to console.warn. */\n onError?: (err: unknown) => void\n}\n\ninterface PendingCall {\n resolve: (value: unknown) => void\n reject: (err: RpcError) => void\n timer: ReturnType<typeof setTimeout> | null\n}\n\nconst DEFAULT_CALL_TIMEOUT_MS = 10_000\n\n/**\n * One end of the bus: sends/receives envelopes over a BusPort, dispatches\n * incoming requests to registered methods, correlates responses to pending\n * calls by per-call nonce, and fans incoming notifications out to wildcard\n * event handlers. Symmetric — both host and extension build on this.\n */\nexport class BusEndpoint {\n readonly callTimeoutMs: number\n\n private readonly port: BusPort\n private readonly unlisten: () => void\n private readonly onError: (err: unknown) => void\n private readonly pending = new Map<JsonRpcId, PendingCall>()\n private readonly methods = new Map<string, MethodHandler>()\n private readonly eventHandlers = new Set<{\n patterns: string[]\n fn: EventHandler\n }>()\n private readonly idPrefix = Math.random().toString(36).slice(2, 8)\n private seq = 0\n private closed = false\n\n constructor(opts: BusEndpointOptions) {\n this.port = opts.port\n this.callTimeoutMs = opts.callTimeoutMs ?? DEFAULT_CALL_TIMEOUT_MS\n this.onError =\n opts.onError ??\n ((err) => console.warn('[plotterext-bus]', err))\n this.unlisten = this.port.listen((data) => this.onData(data))\n }\n\n registerMethod(name: string, handler: MethodHandler): void {\n this.methods.set(name, handler)\n }\n\n unregisterMethod(name: string): void {\n this.methods.delete(name)\n }\n\n /**\n * Handle incoming notifications whose names match any of the wildcard\n * patterns. Returns an unsubscribe function. This is local dispatch only;\n * telling the peer which events to forward is a separate concern\n * (`events.subscribe`).\n */\n onEvent(patterns: string[], fn: EventHandler): () => void {\n const entry = { patterns, fn }\n this.eventHandlers.add(entry)\n return () => this.eventHandlers.delete(entry)\n }\n\n /** Send a notification (an event) to the peer. */\n notify(method: string, params?: unknown): void {\n this.send({ jsonrpc: '2.0', method, ...(params !== undefined ? { params } : {}) })\n }\n\n /** Call a method on the peer; resolves with its result. */\n call(\n method: string,\n params?: unknown,\n opts: { timeoutMs?: number } = {}\n ): Promise<unknown> {\n if (this.closed) {\n return Promise.reject(\n new RpcError('Bus endpoint is closed', {\n code: RPC_ERRORS.CONNECTION_CLOSED,\n reason: 'CLOSED'\n })\n )\n }\n const id = `${this.idPrefix}-${++this.seq}`\n const timeoutMs = opts.timeoutMs ?? this.callTimeoutMs\n return new Promise<unknown>((resolve, reject) => {\n const timer =\n timeoutMs > 0\n ? setTimeout(() => {\n this.pending.delete(id)\n reject(\n new RpcError(`Call timed out after ${timeoutMs}ms: ${method}`, {\n code: RPC_ERRORS.TIMEOUT,\n reason: 'TIMEOUT'\n })\n )\n }, timeoutMs)\n : null\n this.pending.set(id, { resolve, reject, timer })\n this.send({\n jsonrpc: '2.0',\n id,\n method,\n ...(params !== undefined ? { params } : {})\n })\n })\n }\n\n close(): void {\n if (this.closed) return\n this.closed = true\n this.unlisten()\n for (const [, p] of this.pending) {\n if (p.timer) clearTimeout(p.timer)\n p.reject(\n new RpcError('Bus endpoint closed', {\n code: RPC_ERRORS.CONNECTION_CLOSED,\n reason: 'CLOSED'\n })\n )\n }\n this.pending.clear()\n this.eventHandlers.clear()\n }\n\n private send(msg: JsonRpcMessage): void {\n if (this.closed) return\n this.port.post(wrap(msg))\n }\n\n private onData(data: unknown): void {\n const msg = unwrap(data)\n if (!msg) return\n if (isResponse(msg)) {\n this.onResponse(msg)\n } else if (isRequest(msg)) {\n void this.onRequest(msg)\n } else if (isNotification(msg)) {\n this.onNotification(msg.method, msg.params)\n }\n }\n\n private onResponse(msg: JsonRpcResponse): void {\n if (msg.id === null) return\n const p = this.pending.get(msg.id)\n if (!p) return\n this.pending.delete(msg.id)\n if (p.timer) clearTimeout(p.timer)\n if ('error' in msg) {\n p.reject(RpcError.fromErrorObject(msg.error))\n } else {\n p.resolve(msg.result)\n }\n }\n\n private async onRequest(msg: {\n id: JsonRpcId\n method: string\n params?: unknown\n }): Promise<void> {\n const handler = this.methods.get(msg.method)\n if (!handler) {\n this.send({\n jsonrpc: '2.0',\n id: msg.id,\n error: {\n code: RPC_ERRORS.METHOD_NOT_FOUND,\n message: `Method not found: ${msg.method}`\n }\n })\n return\n }\n try {\n const result = await handler(msg.params, { endpoint: this })\n this.send({\n jsonrpc: '2.0',\n id: msg.id,\n result: result === undefined ? null : result\n })\n } catch (err) {\n this.send({\n jsonrpc: '2.0',\n id: msg.id,\n error: RpcError.from(err).toErrorObject()\n })\n }\n }\n\n private onNotification(name: string, params: unknown): void {\n for (const entry of [...this.eventHandlers]) {\n if (matchesAny(entry.patterns, name)) {\n try {\n entry.fn(name, params)\n } catch (err) {\n this.onError(err)\n }\n }\n }\n }\n}\n"],"mappings":"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","names":[]}