@hediet/linkrpc-infra 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,155 @@
1
+ # `@hediet/linkrpc-infra`
2
+
3
+ Reusable infrastructure protocols and adapters built on LinkRPC.
4
+
5
+ ## Inspection
6
+
7
+ The `@hediet/linkrpc-infra/inspection` entry point provides node identity,
8
+ topology, and traffic clients, graph merging, and multi-service query/watch
9
+ orchestration. `TopologyNetworkClient` can discover topology providers through
10
+ the directory; `NetworkInspectionClient` combines explicitly selected sources
11
+ and their traffic watches.
12
+
13
+ The wire contracts and endpoint-side filtering/buffering stay in
14
+ `@hediet/linkrpc/inspection`. A `LinkRpcConnection` can still enable inspection
15
+ without depending on this package. The low-level channel only exposes raw
16
+ inbound/outbound message observation.
17
+
18
+ Inspection clients previously exported by `@hediet/linkrpc/hub/client` are now
19
+ exported here. Inspection contracts and subscription helpers previously in
20
+ `@hediet/linkrpc/hub/common` are now in `@hediet/linkrpc/inspection`.
21
+
22
+ ## JSON documents
23
+
24
+ The `@hediet/linkrpc-infra/json-document` entry point provides revisioned
25
+ document events and atomic `set`, `remove`, `append`, `splice`, and `insert`
26
+ operations addressed by RFC 6901 JSON Pointers.
27
+
28
+ ## JSON-RPC
29
+
30
+ The `@hediet/linkrpc-infra/json-rpc` entry point provides:
31
+
32
+ - `jsonRpcConnectionInterface`, a transparent duplex JSON-RPC transport
33
+ - client and server adapters for the interface
34
+ - a message-oriented `JsonRpcTransport` abstraction
35
+ - an NDJSON stdio transport
36
+ - a generic adapter from parsed JSON-RPC frames to a LinkRPC message transport
37
+
38
+ The bridge treats JSON-RPC frames as opaque JSON values. Applications remain
39
+ responsible for JSON-RPC method semantics, initialization, and authorization.
40
+
41
+ ### CDP/LSP proof of concept (test-only)
42
+
43
+ The CDP and LSP importers, adapters, and integration harnesses live under
44
+ [`test/protocols`](./test/protocols/). They are **not production APIs**, are not
45
+ exported by this package, and are not included in published JavaScript,
46
+ declarations, or source maps. They exercise the generic library primitives
47
+ against real external protocols.
48
+
49
+ The test helpers `importCdpProtocol(browserProtocol, jsProtocol)` and
50
+ `importLspProtocol(metaModel)` return `{ interfaces, bindings, diagnostics,
51
+ metadata }`. Interfaces have local member names; bindings separately record the
52
+ exact wire method, interface/member, request or notification kind, and message
53
+ direction. CDP domains and LSP method prefixes therefore need not become part of
54
+ each member's name.
55
+
56
+ Use `createProtocolInterfaceDefinition(schema)` to construct recursive Zod
57
+ validators from an imported interface. Unlike the generic reflection fallback
58
+ `interfaceFromSchema`, this validates the imported structural contract.
59
+ Non-normative specialization constraints are exported separately through the
60
+ core `materializeJsonSchema` helper; they are not automatically enforced by
61
+ these structural validators. Review importer diagnostics before treating an
62
+ approximation as an exact protocol model.
63
+
64
+ An endpoint can register several interfaces and bind each to an external prefix
65
+ with `connection.bindBare(definition, { prefix })`. Bundle client addressing
66
+ once with the core `bareInterfaceTarget` helper:
67
+
68
+ ```ts
69
+ const cdpRuntime = bareInterfaceTarget(cdpRuntimeInterface, { prefix: 'Runtime.' });
70
+ const runtime = connection.get(cdpRuntime);
71
+ ```
72
+
73
+ The immutable target contains the interface, bare-addressing mode, and prefix;
74
+ it does not change the interface schema or hash. The existing
75
+ `connection.getBare(definition, { prefix })` API remains available. A CDP
76
+ Runtime contract uses `Runtime.`, while an LSP text-document contract uses
77
+ `textDocument/`. The empty prefix covers lifecycle methods such as `initialize`.
78
+ Protocol-specific targets stay in the POC; they are not package exports.
79
+ Some LSP methods have nested suffixes, such as `semanticTokens/full`, which
80
+ the importer flattens to local names containing `__`. Those methods need
81
+ explicit wire-name mapping in addition to a prefix; the live tests cover
82
+ prefix-compatible members, not universal LSP name routing.
83
+
84
+ The longest matching binding wins. Duplicate prefixes are rejected, and an
85
+ unknown member in the selected interface never falls through to another
86
+ binding. Bindings are reflected by `hubrpc.defaults::listBindings` on the
87
+ LinkRPC endpoint; external CDP and LSP peers need not support reflection.
88
+ Bare clients do not send LinkRPC hashes, handshake messages, or native stream
89
+ controls. External cancellation and progress use the external protocol's own
90
+ notifications.
91
+
92
+ ### POC transports and live verification
93
+
94
+ `createLspChildProcessTransport(child)` adapts the child's stdout/stdin using
95
+ LSP framing; the caller still owns process startup, protocol initialization,
96
+ shutdown, and termination. `createCdpWebSocketTransport(openSocket)` exposes
97
+ `.root` and `.session(sessionId)` transports, remaps outgoing request IDs to
98
+ isolate sessions, and translates CDP envelopes without adding a LinkRPC
99
+ handshake. Closing one session does not terminate other sessions.
100
+
101
+ The pinned CDP and LSP schema corpora, provenance, and licenses live under
102
+ [`conformance/protocols`](../../../conformance/protocols/). Deterministic tests
103
+ read these local snapshots rather than downloading schemas. Update the pins
104
+ explicitly when updating a protocol version.
105
+
106
+ Run the live integration tests from the TypeScript workspace:
107
+
108
+ ```sh
109
+ pnpm --filter @hediet/linkrpc-infra test:interop
110
+ ```
111
+
112
+ These tests execute the generated TypeScript interfaces and their validators against
113
+ an unmodified Node inspector and the pinned `vscode-json-languageserver`.
114
+ They exercise evaluation and CDP events, LSP initialization, document symbols,
115
+ diagnostics, and graceful shutdown. The tests spawn only local processes and
116
+ clean them up; schema or server downloads are not performed during the tests.
117
+ CI runs these tests through `pnpm check`. Core and infra tests are uncached:
118
+ their conformance fixtures live outside the TypeScript workspace's cache root,
119
+ and live interoperability must run even when package builds are cached.
120
+
121
+ ### Generated client/server cross-language checks
122
+
123
+ ```sh
124
+ pnpm --filter @hediet/linkrpc-infra test:generated-interop
125
+ ```
126
+
127
+ This requires a Rust toolchain in addition to Node. It regenerates all CDP/LSP
128
+ interfaces from the pinned source documents, compiles all 74 TypeScript
129
+ interfaces and Rust clients/provider traits/server adapters, and runs both
130
+ TypeScript-client/Rust-server and Rust-client/TypeScript-server combinations.
131
+ Rust generation uses `GenerateRustOptions { generate_server: true,
132
+ ..Default::default() }`, producing a typed `Service` trait and `Server` adapter
133
+ alongside the client. The adapters retain the imported schema and hash rather
134
+ than reconstructing them through the Rust trait macro.
135
+ The cross-language cases use selected real CDP DOM and LSP text-document
136
+ methods, including recursive results, notifications, application errors, and
137
+ schema-hash reflection. They are not substitutes for implementing every
138
+ protocol method.
139
+
140
+ The Rust peer and its Cargo crate live under `test/protocols/rust`; generated
141
+ Rust and build artifacts stay under the repository's `rust/target` directory.
142
+ Code-generation approximation diagnostics are reported and snapshot-tested so
143
+ new fallbacks require explicit review. Some LSP numeric enums and standalone
144
+ constants still lower to `serde_json::Value`; successful compilation does not
145
+ imply that every source constraint has an exact native Rust type. The
146
+ cross-language transport is native JSON-RPC NDJSON over child-process stdio,
147
+ with `DOM.` and `textDocument/` bindings, not CDP WebSocket or LSP
148
+ Content-Length framing. The separate external-server smoke tests cover those
149
+ external transports from TypeScript.
150
+
151
+ ## Logging
152
+
153
+ The `@hediet/linkrpc-infra/logging` entry point defines the protocol-only
154
+ `linkrpc.logging` interface and its revisioned structured-log schemas.
155
+ Implementations remain responsible for storage, retention, and sinks.
@@ -0,0 +1,5 @@
1
+ import { JsonDocumentEdit, RevisionedDocumentEvent, applyJsonDocumentEdit, applyJsonDocumentEdits, jsonDocumentEditSchema, jsonDocumentPatchSchema, parseJsonPointer } from "./json-document/index.js";
2
+ import { CloseAwareMessageTransport, Disposable, JsonRpcConnectionCloseReason, JsonRpcTransport, JsonRpcTransportPair, NdjsonJsonRpcTransportOptions, RegisterJsonRpcConnectionServiceOptions, adaptJsonRpcTransport, assertJsonRpcMessage, connectJsonRpcTransports, connectRawJsonRpcTransport, createNdjsonJsonRpcTransport, jsonRpcConnectionCloseReasonSchema, jsonRpcConnectionInterface, registerJsonRpcConnectionService } from "./json-rpc/index.js";
3
+ import { EmittedLogLevel, LogDocument, LogEntry, LogError, LogLevel, LogStreamEvent, logDocumentSchema, logEntrySchema, logLevelSchema, logStreamEventSchema, loggingInterface } from "./logging/index.js";
4
+ import { NetworkInspectionClient, NetworkInspectionClientOptions, NetworkTopologyGraph, NetworkTrafficCallbacks, NetworkTrafficWatch, NodeInfoClient, SourcedRouteClaim, SourcedTopologyLink, SourcedTopologyNode, TopologyClient, TopologyNetworkClient, TopologyNetworkOptions, TopologyNetworkQueryCallbacks, TopologyNetworkSnapshot, TopologyNetworkWatch, TopologySourceSnapshot, TopologySourceState, TopologyWatch, TopologyWatchCallbacks, TrafficCallbacks, TrafficClient, TrafficWatch, TrafficWatchOptions, TrafficWatchWithPayloadsOptions, mergeTopologyGraphs } from "./inspection/index.js";
5
+ export { CloseAwareMessageTransport, Disposable, EmittedLogLevel, JsonDocumentEdit, JsonRpcConnectionCloseReason, JsonRpcTransport, JsonRpcTransportPair, LogDocument, LogEntry, LogError, LogLevel, LogStreamEvent, NdjsonJsonRpcTransportOptions, NetworkInspectionClient, NetworkInspectionClientOptions, NetworkTopologyGraph, NetworkTrafficCallbacks, NetworkTrafficWatch, NodeInfoClient, RegisterJsonRpcConnectionServiceOptions, RevisionedDocumentEvent, SourcedRouteClaim, SourcedTopologyLink, SourcedTopologyNode, TopologyClient, TopologyNetworkClient, TopologyNetworkOptions, TopologyNetworkQueryCallbacks, TopologyNetworkSnapshot, TopologyNetworkWatch, TopologySourceSnapshot, TopologySourceState, TopologyWatch, TopologyWatchCallbacks, TrafficCallbacks, TrafficClient, TrafficWatch, TrafficWatchOptions, TrafficWatchWithPayloadsOptions, adaptJsonRpcTransport, applyJsonDocumentEdit, applyJsonDocumentEdits, assertJsonRpcMessage, connectJsonRpcTransports, connectRawJsonRpcTransport, createNdjsonJsonRpcTransport, jsonDocumentEditSchema, jsonDocumentPatchSchema, jsonRpcConnectionCloseReasonSchema, jsonRpcConnectionInterface, logDocumentSchema, logEntrySchema, logLevelSchema, logStreamEventSchema, loggingInterface, mergeTopologyGraphs, parseJsonPointer, registerJsonRpcConnectionService };
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ import { applyJsonDocumentEdit, applyJsonDocumentEdits, jsonDocumentEditSchema, jsonDocumentPatchSchema, parseJsonPointer } from "./json-document/index.js";
2
+ import { JsonRpcTransportPair, adaptJsonRpcTransport, assertJsonRpcMessage, connectJsonRpcTransports, connectRawJsonRpcTransport, createNdjsonJsonRpcTransport, jsonRpcConnectionCloseReasonSchema, jsonRpcConnectionInterface, registerJsonRpcConnectionService } from "./json-rpc/index.js";
3
+ import { logDocumentSchema, logEntrySchema, logLevelSchema, logStreamEventSchema, loggingInterface } from "./logging/index.js";
4
+ import { NetworkInspectionClient, NodeInfoClient, TopologyClient, TopologyNetworkClient, TrafficClient, mergeTopologyGraphs } from "./inspection/index.js";
5
+ export { JsonRpcTransportPair, NetworkInspectionClient, NodeInfoClient, TopologyClient, TopologyNetworkClient, TrafficClient, adaptJsonRpcTransport, applyJsonDocumentEdit, applyJsonDocumentEdits, assertJsonRpcMessage, connectJsonRpcTransports, connectRawJsonRpcTransport, createNdjsonJsonRpcTransport, jsonDocumentEditSchema, jsonDocumentPatchSchema, jsonRpcConnectionCloseReasonSchema, jsonRpcConnectionInterface, logDocumentSchema, logEntrySchema, logLevelSchema, logStreamEventSchema, loggingInterface, mergeTopologyGraphs, parseJsonPointer, registerJsonRpcConnectionService };
@@ -0,0 +1,169 @@
1
+ import { LinkRpcConnection } from "@hediet/linkrpc";
2
+ import { NodeInfo, RouteClaim, TopologyGraph, TopologyLink, TopologyNode, TrafficOverflowEvent, TrafficRequestRef, TrafficTransitEvent, TrafficWatchResult } from "@hediet/linkrpc/inspection";
3
+ import { HubDirectoryGraphSnapshot } from "@hediet/linkrpc/hub/common";
4
+ //#region src/inspection/nodeInfoClient.d.ts
5
+ /** Typed convenience client for root and service-scoped node identity. */
6
+ declare class NodeInfoClient<TInCtx = unknown, TOutCtx = unknown> {
7
+ private readonly _connection;
8
+ constructor(_connection: LinkRpcConnection<TInCtx, TOutCtx>);
9
+ getPeer(): Promise<NodeInfo>;
10
+ getForService(serviceId: string): Promise<NodeInfo>;
11
+ }
12
+ //#endregion
13
+ //#region src/inspection/topologyClient.d.ts
14
+ interface TopologyWatch {
15
+ /** Resolves with the initial snapshot after its callback has run. */
16
+ readonly ready: Promise<TopologyGraph>;
17
+ /** Resolves after cancellation and all queued re-fetches complete. */
18
+ readonly done: Promise<void>;
19
+ cancel(reason?: string): Promise<void>;
20
+ }
21
+ interface TopologyWatchCallbacks {
22
+ onGraph(graph: TopologyGraph): void;
23
+ onError?(error: unknown): void;
24
+ }
25
+ /** Snapshot and invalidation-watch client for one inspected service. */
26
+ declare class TopologyClient<TInCtx = unknown, TOutCtx = unknown> {
27
+ private readonly _connection;
28
+ readonly serviceId: string;
29
+ private readonly _timeoutMs;
30
+ constructor(_connection: LinkRpcConnection<TInCtx, TOutCtx>, serviceId: string, _timeoutMs?: number);
31
+ getGraph(): Promise<TopologyGraph>;
32
+ watch(callbacks: TopologyWatchCallbacks): TopologyWatch;
33
+ }
34
+ //#endregion
35
+ //#region src/inspection/trafficClient.d.ts
36
+ interface TrafficCallbacks {
37
+ onTransit(transit: TrafficTransitEvent): void;
38
+ onOverflow?(overflow: TrafficOverflowEvent): void;
39
+ onError?(error: unknown): void;
40
+ }
41
+ interface TrafficWatch {
42
+ readonly done: Promise<TrafficWatchResult>;
43
+ cancel(reason?: string): Promise<void>;
44
+ }
45
+ interface TrafficWatchOptions {
46
+ readonly methodPrefix?: string;
47
+ readonly trafficIgnoreKey?: string;
48
+ readonly focusRequest?: TrafficRequestRef;
49
+ }
50
+ interface TrafficWatchWithPayloadsOptions extends TrafficWatchOptions {
51
+ readonly maxPayloadBytes: number;
52
+ }
53
+ /** Traffic stream client for the endpoint node hosting one service. */
54
+ declare class TrafficClient<TInCtx = unknown, TOutCtx = unknown> {
55
+ private readonly _connection;
56
+ readonly serviceId: string;
57
+ constructor(_connection: LinkRpcConnection<TInCtx, TOutCtx>, serviceId: string);
58
+ watch(options: TrafficWatchOptions, callbacks: TrafficCallbacks): TrafficWatch;
59
+ watchWithPayloads(options: TrafficWatchWithPayloadsOptions, callbacks: TrafficCallbacks): TrafficWatch;
60
+ }
61
+ //#endregion
62
+ //#region src/inspection/topologyGraph.d.ts
63
+ interface SourcedTopologyNode extends TopologyNode {
64
+ readonly sources: readonly string[];
65
+ }
66
+ interface SourcedTopologyLink extends TopologyLink {
67
+ readonly sources: readonly string[];
68
+ }
69
+ interface SourcedRouteClaim extends RouteClaim {
70
+ readonly sources: readonly string[];
71
+ }
72
+ interface NetworkTopologyGraph {
73
+ readonly nodes: readonly SourcedTopologyNode[];
74
+ readonly links: readonly SourcedTopologyLink[];
75
+ readonly routes: readonly SourcedRouteClaim[];
76
+ readonly sources: readonly {
77
+ serviceId: string;
78
+ observerServiceId: string;
79
+ entryNodeId: string;
80
+ }[];
81
+ }
82
+ declare function mergeTopologyGraphs(inputs: readonly {
83
+ source: string;
84
+ graph: TopologyGraph;
85
+ }[]): NetworkTopologyGraph;
86
+ //#endregion
87
+ //#region src/inspection/networkInspectionClient.d.ts
88
+ interface NetworkInspectionClientOptions {
89
+ onGraph?(graph: NetworkTopologyGraph): void;
90
+ onError?(error: unknown, sourceServiceId: string): void;
91
+ }
92
+ interface NetworkTrafficCallbacks {
93
+ onTransit(transit: TrafficTransitEvent, sourceServiceId: string): void;
94
+ onOverflow?(overflow: TrafficOverflowEvent, sourceServiceId: string): void;
95
+ onError?(error: unknown, sourceServiceId: string): void;
96
+ }
97
+ interface NetworkTrafficWatch {
98
+ readonly done: Promise<void>;
99
+ cancel(reason?: string): Promise<void>;
100
+ }
101
+ /**
102
+ * Merges independently observed service graphs. Traffic is deliberately not
103
+ * deduplicated: each source remains identified so consumers can stitch flows.
104
+ */
105
+ declare class NetworkInspectionClient {
106
+ private readonly _connection;
107
+ private readonly _options;
108
+ private readonly _sources;
109
+ private readonly _trafficGroups;
110
+ constructor(_connection: LinkRpcConnection<unknown, unknown>, _options?: NetworkInspectionClientOptions);
111
+ addTopologyService(serviceId: string): Promise<void>;
112
+ removeTopologyService(serviceId: string): Promise<void>;
113
+ getGraph(): NetworkTopologyGraph;
114
+ watchTraffic(options: TrafficWatchOptions | TrafficWatchWithPayloadsOptions, callbacks: NetworkTrafficCallbacks): NetworkTrafficWatch;
115
+ dispose(): Promise<void>;
116
+ }
117
+ //#endregion
118
+ //#region src/inspection/topologyNetworkClient.d.ts
119
+ type TopologySourceState = 'loading' | 'ready' | 'error';
120
+ interface TopologySourceSnapshot {
121
+ readonly serviceId: string;
122
+ readonly state: TopologySourceState;
123
+ readonly graph?: TopologyGraph;
124
+ readonly error?: string;
125
+ }
126
+ interface TopologyNetworkSnapshot {
127
+ readonly revision: number;
128
+ readonly complete: boolean;
129
+ readonly directory?: HubDirectoryGraphSnapshot;
130
+ readonly directoryError?: string;
131
+ readonly sources: readonly TopologySourceSnapshot[];
132
+ readonly graph: NetworkTopologyGraph;
133
+ }
134
+ interface TopologyNetworkOptions {
135
+ /**
136
+ * Fixed topology providers. When omitted, providers are discovered through
137
+ * the directory graph by their `hubrpc.topology` interface.
138
+ */
139
+ readonly sourceServiceIds?: readonly string[];
140
+ readonly maxDepth?: number;
141
+ /** Hard deadline for each directory and topology request. Defaults to 5 seconds. */
142
+ readonly timeoutMs?: number;
143
+ }
144
+ interface TopologyNetworkQueryCallbacks {
145
+ onSnapshot?(snapshot: TopologyNetworkSnapshot): void;
146
+ }
147
+ interface TopologyNetworkWatch {
148
+ readonly snapshot: TopologyNetworkSnapshot;
149
+ readonly ready: Promise<TopologyNetworkSnapshot>;
150
+ readonly done: Promise<void>;
151
+ subscribe(listener: (snapshot: TopologyNetworkSnapshot) => void): () => void;
152
+ cancel(reason?: string): Promise<void>;
153
+ }
154
+ /**
155
+ * Queries or watches a merged topology graph from one, many, or dynamically
156
+ * discovered topology providers. Query mode never registers directory or
157
+ * topology watches; progress snapshots describe its finite request fan-out.
158
+ */
159
+ declare class TopologyNetworkClient {
160
+ private readonly _connection;
161
+ constructor(_connection: LinkRpcConnection<unknown, unknown>);
162
+ query(options?: TopologyNetworkOptions, callbacks?: TopologyNetworkQueryCallbacks): Promise<TopologyNetworkSnapshot>;
163
+ watch(options?: TopologyNetworkOptions): TopologyNetworkWatch;
164
+ private _queryDiscovered;
165
+ private _querySource;
166
+ }
167
+ //#endregion
168
+ export { NetworkInspectionClient, NetworkInspectionClientOptions, NetworkTopologyGraph, NetworkTrafficCallbacks, NetworkTrafficWatch, NodeInfoClient, SourcedRouteClaim, SourcedTopologyLink, SourcedTopologyNode, TopologyClient, TopologyNetworkClient, TopologyNetworkOptions, TopologyNetworkQueryCallbacks, TopologyNetworkSnapshot, TopologyNetworkWatch, TopologySourceSnapshot, TopologySourceState, TopologyWatch, TopologyWatchCallbacks, TrafficCallbacks, TrafficClient, TrafficWatch, TrafficWatchOptions, TrafficWatchWithPayloadsOptions, mergeTopologyGraphs };
169
+ //# sourceMappingURL=index.d.ts.map