@procwire/transport 0.1.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/LICENSE +21 -0
- package/README.md +844 -0
- package/dist/channel/builder.d.ts +68 -0
- package/dist/channel/builder.d.ts.map +1 -0
- package/dist/channel/builder.js +120 -0
- package/dist/channel/builder.js.map +1 -0
- package/dist/channel/index.d.ts +6 -0
- package/dist/channel/index.d.ts.map +1 -0
- package/dist/channel/index.js +6 -0
- package/dist/channel/index.js.map +1 -0
- package/dist/channel/quickstart.d.ts +94 -0
- package/dist/channel/quickstart.d.ts.map +1 -0
- package/dist/channel/quickstart.js +104 -0
- package/dist/channel/quickstart.js.map +1 -0
- package/dist/channel/request-channel.d.ts +119 -0
- package/dist/channel/request-channel.d.ts.map +1 -0
- package/dist/channel/request-channel.js +476 -0
- package/dist/channel/request-channel.js.map +1 -0
- package/dist/channel/types.d.ts +226 -0
- package/dist/channel/types.d.ts.map +1 -0
- package/dist/channel/types.js +2 -0
- package/dist/channel/types.js.map +1 -0
- package/dist/framing/index.d.ts +4 -0
- package/dist/framing/index.d.ts.map +1 -0
- package/dist/framing/index.js +4 -0
- package/dist/framing/index.js.map +1 -0
- package/dist/framing/length-prefixed.d.ts +55 -0
- package/dist/framing/length-prefixed.d.ts.map +1 -0
- package/dist/framing/length-prefixed.js +102 -0
- package/dist/framing/length-prefixed.js.map +1 -0
- package/dist/framing/line-delimited.d.ts +61 -0
- package/dist/framing/line-delimited.d.ts.map +1 -0
- package/dist/framing/line-delimited.js +94 -0
- package/dist/framing/line-delimited.js.map +1 -0
- package/dist/framing/types.d.ts +35 -0
- package/dist/framing/types.d.ts.map +1 -0
- package/dist/framing/types.js +2 -0
- package/dist/framing/types.js.map +1 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +26 -0
- package/dist/index.js.map +1 -0
- package/dist/process/handle.d.ts +64 -0
- package/dist/process/handle.d.ts.map +1 -0
- package/dist/process/handle.js +107 -0
- package/dist/process/handle.js.map +1 -0
- package/dist/process/index.d.ts +37 -0
- package/dist/process/index.d.ts.map +1 -0
- package/dist/process/index.js +37 -0
- package/dist/process/index.js.map +1 -0
- package/dist/process/manager.d.ts +58 -0
- package/dist/process/manager.d.ts.map +1 -0
- package/dist/process/manager.js +360 -0
- package/dist/process/manager.js.map +1 -0
- package/dist/process/types.d.ts +322 -0
- package/dist/process/types.d.ts.map +1 -0
- package/dist/process/types.js +2 -0
- package/dist/process/types.js.map +1 -0
- package/dist/protocol/index.d.ts +4 -0
- package/dist/protocol/index.d.ts.map +1 -0
- package/dist/protocol/index.js +6 -0
- package/dist/protocol/index.js.map +1 -0
- package/dist/protocol/jsonrpc.d.ts +146 -0
- package/dist/protocol/jsonrpc.d.ts.map +1 -0
- package/dist/protocol/jsonrpc.js +288 -0
- package/dist/protocol/jsonrpc.js.map +1 -0
- package/dist/protocol/simple.d.ts +139 -0
- package/dist/protocol/simple.d.ts.map +1 -0
- package/dist/protocol/simple.js +297 -0
- package/dist/protocol/simple.js.map +1 -0
- package/dist/protocol/types.d.ts +117 -0
- package/dist/protocol/types.d.ts.map +1 -0
- package/dist/protocol/types.js +2 -0
- package/dist/protocol/types.js.map +1 -0
- package/dist/serialization/index.d.ts +5 -0
- package/dist/serialization/index.d.ts.map +1 -0
- package/dist/serialization/index.js +5 -0
- package/dist/serialization/index.js.map +1 -0
- package/dist/serialization/json.d.ts +66 -0
- package/dist/serialization/json.d.ts.map +1 -0
- package/dist/serialization/json.js +66 -0
- package/dist/serialization/json.js.map +1 -0
- package/dist/serialization/raw.d.ts +38 -0
- package/dist/serialization/raw.d.ts.map +1 -0
- package/dist/serialization/raw.js +41 -0
- package/dist/serialization/raw.js.map +1 -0
- package/dist/serialization/registry.d.ts +91 -0
- package/dist/serialization/registry.d.ts.map +1 -0
- package/dist/serialization/registry.js +119 -0
- package/dist/serialization/registry.js.map +1 -0
- package/dist/serialization/types.d.ts +27 -0
- package/dist/serialization/types.d.ts.map +1 -0
- package/dist/serialization/types.js +2 -0
- package/dist/serialization/types.js.map +1 -0
- package/dist/transport/factory.d.ts +139 -0
- package/dist/transport/factory.d.ts.map +1 -0
- package/dist/transport/factory.js +162 -0
- package/dist/transport/factory.js.map +1 -0
- package/dist/transport/index.d.ts +6 -0
- package/dist/transport/index.d.ts.map +1 -0
- package/dist/transport/index.js +9 -0
- package/dist/transport/index.js.map +1 -0
- package/dist/transport/socket-server.d.ts +48 -0
- package/dist/transport/socket-server.d.ts.map +1 -0
- package/dist/transport/socket-server.js +215 -0
- package/dist/transport/socket-server.js.map +1 -0
- package/dist/transport/socket-transport.d.ts +67 -0
- package/dist/transport/socket-transport.d.ts.map +1 -0
- package/dist/transport/socket-transport.js +193 -0
- package/dist/transport/socket-transport.js.map +1 -0
- package/dist/transport/stdio-transport.d.ts +94 -0
- package/dist/transport/stdio-transport.d.ts.map +1 -0
- package/dist/transport/stdio-transport.js +234 -0
- package/dist/transport/stdio-transport.js.map +1 -0
- package/dist/transport/types.d.ts +131 -0
- package/dist/transport/types.d.ts.map +1 -0
- package/dist/transport/types.js +2 -0
- package/dist/transport/types.js.map +1 -0
- package/dist/utils/assert.d.ts +16 -0
- package/dist/utils/assert.d.ts.map +1 -0
- package/dist/utils/assert.js +31 -0
- package/dist/utils/assert.js.map +1 -0
- package/dist/utils/disposables.d.ts +38 -0
- package/dist/utils/disposables.d.ts.map +1 -0
- package/dist/utils/disposables.js +59 -0
- package/dist/utils/disposables.js.map +1 -0
- package/dist/utils/errors.d.ts +43 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/errors.js +69 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/events.d.ts +58 -0
- package/dist/utils/events.d.ts.map +1 -0
- package/dist/utils/events.js +95 -0
- package/dist/utils/events.js.map +1 -0
- package/dist/utils/index.d.ts +8 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +8 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/pipe-path.d.ts +48 -0
- package/dist/utils/pipe-path.d.ts.map +1 -0
- package/dist/utils/pipe-path.js +89 -0
- package/dist/utils/pipe-path.js.map +1 -0
- package/dist/utils/platform.d.ts +16 -0
- package/dist/utils/platform.d.ts.map +1 -0
- package/dist/utils/platform.js +22 -0
- package/dist/utils/platform.js.map +1 -0
- package/dist/utils/time.d.ts +38 -0
- package/dist/utils/time.d.ts.map +1 -0
- package/dist/utils/time.js +55 -0
- package/dist/utils/time.js.map +1 -0
- package/package.json +85 -0
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import type { EventMap } from "../utils/events.js";
|
|
2
|
+
import type { Unsubscribe } from "../utils/disposables.js";
|
|
3
|
+
import type { Transport, TransportServer } from "../transport/types.js";
|
|
4
|
+
import type { FramingCodec } from "../framing/types.js";
|
|
5
|
+
import type { SerializationCodec } from "../serialization/types.js";
|
|
6
|
+
import type { Protocol, RequestId, ProtocolDataError } from "../protocol/types.js";
|
|
7
|
+
/**
|
|
8
|
+
* Request handler function.
|
|
9
|
+
* @template TReq - Request data type
|
|
10
|
+
* @template TRes - Response data type
|
|
11
|
+
*/
|
|
12
|
+
export type RequestHandler<TReq = unknown, TRes = unknown> = (request: TReq) => TRes | Promise<TRes>;
|
|
13
|
+
/**
|
|
14
|
+
* Notification handler function.
|
|
15
|
+
* @template TNotif - Notification data type
|
|
16
|
+
*/
|
|
17
|
+
export type NotificationHandler<TNotif = unknown> = (notification: TNotif) => void;
|
|
18
|
+
/**
|
|
19
|
+
* Response accessor for interpreting protocol-specific response messages.
|
|
20
|
+
* Abstracts away protocol differences for generic channel implementation.
|
|
21
|
+
*/
|
|
22
|
+
export interface ResponseAccessor {
|
|
23
|
+
/**
|
|
24
|
+
* Extracts request ID from a response message.
|
|
25
|
+
* @returns Request ID or undefined if message is not a response
|
|
26
|
+
*/
|
|
27
|
+
getResponseId(message: unknown): RequestId | undefined;
|
|
28
|
+
/**
|
|
29
|
+
* Checks if response message represents an error.
|
|
30
|
+
*/
|
|
31
|
+
isErrorResponse(message: unknown): boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Extracts result data from success response.
|
|
34
|
+
*/
|
|
35
|
+
getResult(message: unknown): unknown;
|
|
36
|
+
/**
|
|
37
|
+
* Extracts error data from error response.
|
|
38
|
+
*/
|
|
39
|
+
getError(message: unknown): ProtocolDataError | unknown;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Middleware hook for channel operations.
|
|
43
|
+
* Useful for logging, metrics, debugging, and transformation.
|
|
44
|
+
*/
|
|
45
|
+
export interface ChannelMiddleware {
|
|
46
|
+
/**
|
|
47
|
+
* Called before sending a request.
|
|
48
|
+
*/
|
|
49
|
+
onOutgoingRequest?(request: unknown): void | Promise<void>;
|
|
50
|
+
/**
|
|
51
|
+
* Called after receiving a response.
|
|
52
|
+
*/
|
|
53
|
+
onIncomingResponse?(response: unknown): void | Promise<void>;
|
|
54
|
+
/**
|
|
55
|
+
* Called when receiving an incoming request.
|
|
56
|
+
*/
|
|
57
|
+
onIncomingRequest?(request: unknown): void | Promise<void>;
|
|
58
|
+
/**
|
|
59
|
+
* Called before sending a response.
|
|
60
|
+
*/
|
|
61
|
+
onOutgoingResponse?(response: unknown): void | Promise<void>;
|
|
62
|
+
/**
|
|
63
|
+
* Called when an error occurs.
|
|
64
|
+
*/
|
|
65
|
+
onError?(error: Error): void | Promise<void>;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Channel events map.
|
|
69
|
+
*/
|
|
70
|
+
export interface ChannelEvents extends EventMap {
|
|
71
|
+
/**
|
|
72
|
+
* Fired when channel starts (connects).
|
|
73
|
+
*/
|
|
74
|
+
start: void;
|
|
75
|
+
/**
|
|
76
|
+
* Fired when channel closes.
|
|
77
|
+
*/
|
|
78
|
+
close: void;
|
|
79
|
+
/**
|
|
80
|
+
* Fired when channel error occurs.
|
|
81
|
+
*/
|
|
82
|
+
error: Error;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Channel options for configuration.
|
|
86
|
+
* @template TReq - Request data type
|
|
87
|
+
* @template TRes - Response data type
|
|
88
|
+
* @template TNotif - Notification data type
|
|
89
|
+
*/
|
|
90
|
+
export interface ChannelOptions<TReq = unknown, TRes = unknown, TNotif = unknown> {
|
|
91
|
+
/**
|
|
92
|
+
* Underlying transport.
|
|
93
|
+
*/
|
|
94
|
+
transport: Transport;
|
|
95
|
+
/**
|
|
96
|
+
* Framing codec for message boundaries.
|
|
97
|
+
*/
|
|
98
|
+
framing: FramingCodec;
|
|
99
|
+
/**
|
|
100
|
+
* Serialization codec for data encoding.
|
|
101
|
+
*/
|
|
102
|
+
serialization: SerializationCodec;
|
|
103
|
+
/**
|
|
104
|
+
* Protocol layer for request/response.
|
|
105
|
+
*/
|
|
106
|
+
protocol: Protocol<TReq, TRes, TNotif>;
|
|
107
|
+
/**
|
|
108
|
+
* Default request timeout in milliseconds (default: 30000).
|
|
109
|
+
*/
|
|
110
|
+
timeout?: number;
|
|
111
|
+
/**
|
|
112
|
+
* Response accessor for interpreting response messages.
|
|
113
|
+
* If not provided, auto-detected based on protocol name.
|
|
114
|
+
*/
|
|
115
|
+
responseAccessor?: ResponseAccessor;
|
|
116
|
+
/**
|
|
117
|
+
* Middleware hooks for logging, metrics, debugging.
|
|
118
|
+
*/
|
|
119
|
+
middleware?: ChannelMiddleware[];
|
|
120
|
+
/**
|
|
121
|
+
* Maximum number of inbound frames to buffer before backpressure (optional).
|
|
122
|
+
*/
|
|
123
|
+
maxInboundFrames?: number;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* High-level communication channel combining all layers.
|
|
127
|
+
* Provides request/response and notification patterns.
|
|
128
|
+
*
|
|
129
|
+
* @template TReq - Request data type (wire format)
|
|
130
|
+
* @template TRes - Response data type (wire format)
|
|
131
|
+
* @template TNotif - Notification data type (wire format)
|
|
132
|
+
*/
|
|
133
|
+
export interface Channel<TReq = unknown, TRes = unknown, TNotif = unknown> {
|
|
134
|
+
/**
|
|
135
|
+
* Returns true if channel is connected and ready.
|
|
136
|
+
*/
|
|
137
|
+
readonly isConnected: boolean;
|
|
138
|
+
/**
|
|
139
|
+
* Starts the channel (connects transport).
|
|
140
|
+
*/
|
|
141
|
+
start(): Promise<void>;
|
|
142
|
+
/**
|
|
143
|
+
* Closes the channel gracefully.
|
|
144
|
+
*/
|
|
145
|
+
close(): Promise<void>;
|
|
146
|
+
/**
|
|
147
|
+
* Sends a request and waits for response.
|
|
148
|
+
* @param method - Method name
|
|
149
|
+
* @param params - Optional parameters
|
|
150
|
+
* @param timeout - Optional timeout override (ms)
|
|
151
|
+
* @returns Promise resolving to response result
|
|
152
|
+
* @throws {TimeoutError} if request times out
|
|
153
|
+
* @throws {ProtocolError} if response is an error
|
|
154
|
+
*/
|
|
155
|
+
request(method: string, params?: unknown, timeout?: number): Promise<unknown>;
|
|
156
|
+
/**
|
|
157
|
+
* Sends a notification (fire-and-forget, no response expected).
|
|
158
|
+
* @param method - Method name
|
|
159
|
+
* @param params - Optional parameters
|
|
160
|
+
*/
|
|
161
|
+
notify(method: string, params?: unknown): Promise<void>;
|
|
162
|
+
/**
|
|
163
|
+
* Registers handler for incoming requests.
|
|
164
|
+
* @returns Unsubscribe function
|
|
165
|
+
*/
|
|
166
|
+
onRequest(handler: RequestHandler<TReq, TRes>): Unsubscribe;
|
|
167
|
+
/**
|
|
168
|
+
* Registers handler for incoming notifications.
|
|
169
|
+
* @returns Unsubscribe function
|
|
170
|
+
*/
|
|
171
|
+
onNotification(handler: NotificationHandler<TNotif>): Unsubscribe;
|
|
172
|
+
/**
|
|
173
|
+
* Subscribes to channel events.
|
|
174
|
+
* @returns Unsubscribe function
|
|
175
|
+
*/
|
|
176
|
+
on<K extends keyof ChannelEvents>(event: K, handler: (data: ChannelEvents[K]) => void): Unsubscribe;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Server-side channel factory options.
|
|
180
|
+
*/
|
|
181
|
+
export interface ChannelServerOptions<TReq = unknown, TRes = unknown, TNotif = unknown> {
|
|
182
|
+
/**
|
|
183
|
+
* Transport server for accepting connections.
|
|
184
|
+
*/
|
|
185
|
+
server: TransportServer;
|
|
186
|
+
/**
|
|
187
|
+
* Framing codec factory (creates instance per connection).
|
|
188
|
+
*/
|
|
189
|
+
createFraming: () => FramingCodec;
|
|
190
|
+
/**
|
|
191
|
+
* Serialization codec (can be shared across connections).
|
|
192
|
+
*/
|
|
193
|
+
serialization: SerializationCodec;
|
|
194
|
+
/**
|
|
195
|
+
* Protocol factory (creates instance per connection).
|
|
196
|
+
*/
|
|
197
|
+
createProtocol: () => Protocol<TReq, TRes, TNotif>;
|
|
198
|
+
/**
|
|
199
|
+
* Default request timeout in milliseconds (optional).
|
|
200
|
+
*/
|
|
201
|
+
timeout?: number;
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Server-side channel manager.
|
|
205
|
+
* Accepts connections and creates Channel instances.
|
|
206
|
+
*/
|
|
207
|
+
export interface ChannelServer<TReq = unknown, TRes = unknown, TNotif = unknown> {
|
|
208
|
+
/**
|
|
209
|
+
* Returns true if server is listening.
|
|
210
|
+
*/
|
|
211
|
+
readonly isListening: boolean;
|
|
212
|
+
/**
|
|
213
|
+
* Starts listening for connections.
|
|
214
|
+
*/
|
|
215
|
+
listen(address: string | number): Promise<void>;
|
|
216
|
+
/**
|
|
217
|
+
* Stops the server and closes all channels.
|
|
218
|
+
*/
|
|
219
|
+
close(): Promise<void>;
|
|
220
|
+
/**
|
|
221
|
+
* Subscribes to new channel connections.
|
|
222
|
+
* @returns Unsubscribe function
|
|
223
|
+
*/
|
|
224
|
+
onConnection(handler: (channel: Channel<TReq, TRes, TNotif>) => void): Unsubscribe;
|
|
225
|
+
}
|
|
226
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/channel/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/channel/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/framing/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/framing/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { FramingCodec } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Options for LengthPrefixedFraming.
|
|
4
|
+
*/
|
|
5
|
+
export interface LengthPrefixedFramingOptions {
|
|
6
|
+
/**
|
|
7
|
+
* Maximum message size in bytes (default: 32MB).
|
|
8
|
+
* Prevents DoS from malicious large length headers.
|
|
9
|
+
*/
|
|
10
|
+
maxMessageSize?: number;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Length-prefixed framing codec.
|
|
14
|
+
* Format: [4-byte length (uint32 BE)][payload]
|
|
15
|
+
*
|
|
16
|
+
* Each frame starts with a 4-byte big-endian unsigned integer
|
|
17
|
+
* indicating the payload length, followed by the payload itself.
|
|
18
|
+
*
|
|
19
|
+
* Handles partial headers, partial payloads, and multiple frames per chunk.
|
|
20
|
+
* Supports zero-length frames.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* const framing = new LengthPrefixedFraming();
|
|
24
|
+
* const encoded = framing.encode(Buffer.from('hello')); // [0,0,0,5,'h','e','l','l','o']
|
|
25
|
+
* const frames = framing.decode(encoded); // [Buffer<'hello'>]
|
|
26
|
+
*/
|
|
27
|
+
export declare class LengthPrefixedFraming implements FramingCodec {
|
|
28
|
+
private static readonly HEADER_SIZE;
|
|
29
|
+
private readonly maxMessageSize;
|
|
30
|
+
private buffer;
|
|
31
|
+
private expectedLength;
|
|
32
|
+
constructor(options?: LengthPrefixedFramingOptions);
|
|
33
|
+
/**
|
|
34
|
+
* Encodes a payload with length prefix.
|
|
35
|
+
*/
|
|
36
|
+
encode(payload: Buffer): Buffer;
|
|
37
|
+
/**
|
|
38
|
+
* Decodes incoming chunk and extracts complete frames.
|
|
39
|
+
* Buffers partial headers and payloads.
|
|
40
|
+
*/
|
|
41
|
+
decode(chunk: Buffer): Buffer[];
|
|
42
|
+
/**
|
|
43
|
+
* Resets internal buffer state.
|
|
44
|
+
*/
|
|
45
|
+
reset(): void;
|
|
46
|
+
/**
|
|
47
|
+
* Returns true if there is buffered partial data.
|
|
48
|
+
*/
|
|
49
|
+
hasBufferedData(): boolean;
|
|
50
|
+
/**
|
|
51
|
+
* Returns current buffer size in bytes.
|
|
52
|
+
*/
|
|
53
|
+
getBufferSize(): number;
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=length-prefixed.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"length-prefixed.d.ts","sourceRoot":"","sources":["../../src/framing/length-prefixed.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C;;GAEG;AACH,MAAM,WAAW,4BAA4B;IAC3C;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;;;;;;;;;GAcG;AACH,qBAAa,qBAAsB,YAAW,YAAY;IACxD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAK;IAExC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IACxC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,cAAc,CAAgB;gBAE1B,OAAO,GAAE,4BAAiC;IAMtD;;OAEG;IACH,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAM/B;;;OAGG;IACH,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE;IA0D/B;;OAEG;IACH,KAAK,IAAI,IAAI;IAKb;;OAEG;IACH,eAAe,IAAI,OAAO;IAI1B;;OAEG;IACH,aAAa,IAAI,MAAM;CAGxB"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { FramingError } from "../utils/errors.js";
|
|
2
|
+
/**
|
|
3
|
+
* Length-prefixed framing codec.
|
|
4
|
+
* Format: [4-byte length (uint32 BE)][payload]
|
|
5
|
+
*
|
|
6
|
+
* Each frame starts with a 4-byte big-endian unsigned integer
|
|
7
|
+
* indicating the payload length, followed by the payload itself.
|
|
8
|
+
*
|
|
9
|
+
* Handles partial headers, partial payloads, and multiple frames per chunk.
|
|
10
|
+
* Supports zero-length frames.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* const framing = new LengthPrefixedFraming();
|
|
14
|
+
* const encoded = framing.encode(Buffer.from('hello')); // [0,0,0,5,'h','e','l','l','o']
|
|
15
|
+
* const frames = framing.decode(encoded); // [Buffer<'hello'>]
|
|
16
|
+
*/
|
|
17
|
+
export class LengthPrefixedFraming {
|
|
18
|
+
static HEADER_SIZE = 4;
|
|
19
|
+
maxMessageSize;
|
|
20
|
+
buffer;
|
|
21
|
+
expectedLength;
|
|
22
|
+
constructor(options = {}) {
|
|
23
|
+
this.maxMessageSize = options.maxMessageSize ?? 32 * 1024 * 1024; // 32MB
|
|
24
|
+
this.buffer = Buffer.allocUnsafe(0);
|
|
25
|
+
this.expectedLength = null;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Encodes a payload with length prefix.
|
|
29
|
+
*/
|
|
30
|
+
encode(payload) {
|
|
31
|
+
const header = Buffer.allocUnsafe(LengthPrefixedFraming.HEADER_SIZE);
|
|
32
|
+
header.writeUInt32BE(payload.length, 0);
|
|
33
|
+
return Buffer.concat([header, payload]);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Decodes incoming chunk and extracts complete frames.
|
|
37
|
+
* Buffers partial headers and payloads.
|
|
38
|
+
*/
|
|
39
|
+
decode(chunk) {
|
|
40
|
+
// Append chunk to buffer
|
|
41
|
+
this.buffer = Buffer.concat([this.buffer, chunk]);
|
|
42
|
+
const frames = [];
|
|
43
|
+
// Process all complete frames in buffer
|
|
44
|
+
while (true) {
|
|
45
|
+
// Do we need to read the header?
|
|
46
|
+
if (this.expectedLength === null) {
|
|
47
|
+
// Need at least HEADER_SIZE bytes
|
|
48
|
+
if (this.buffer.length < LengthPrefixedFraming.HEADER_SIZE) {
|
|
49
|
+
break; // Wait for more data
|
|
50
|
+
}
|
|
51
|
+
// Read length from header
|
|
52
|
+
const length = this.buffer.readUInt32BE(0);
|
|
53
|
+
// Validate length
|
|
54
|
+
if (length > this.maxMessageSize) {
|
|
55
|
+
const error = new FramingError(`Message length ${length} exceeds maximum ${this.maxMessageSize}`);
|
|
56
|
+
this.reset();
|
|
57
|
+
throw error;
|
|
58
|
+
}
|
|
59
|
+
this.expectedLength = length;
|
|
60
|
+
this.buffer = this.buffer.subarray(LengthPrefixedFraming.HEADER_SIZE);
|
|
61
|
+
}
|
|
62
|
+
// Do we have the complete payload?
|
|
63
|
+
if (this.buffer.length < this.expectedLength) {
|
|
64
|
+
break; // Wait for more data
|
|
65
|
+
}
|
|
66
|
+
// Extract frame
|
|
67
|
+
const frame = this.buffer.subarray(0, this.expectedLength);
|
|
68
|
+
frames.push(frame);
|
|
69
|
+
// Move past this frame
|
|
70
|
+
this.buffer = this.buffer.subarray(this.expectedLength);
|
|
71
|
+
this.expectedLength = null;
|
|
72
|
+
}
|
|
73
|
+
// Validate buffer doesn't grow unbounded
|
|
74
|
+
const maxBufferSize = LengthPrefixedFraming.HEADER_SIZE + this.maxMessageSize;
|
|
75
|
+
if (this.buffer.length > maxBufferSize) {
|
|
76
|
+
const error = new FramingError(`Buffer size ${this.buffer.length} exceeds maximum ${maxBufferSize}`);
|
|
77
|
+
this.reset();
|
|
78
|
+
throw error;
|
|
79
|
+
}
|
|
80
|
+
return frames;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Resets internal buffer state.
|
|
84
|
+
*/
|
|
85
|
+
reset() {
|
|
86
|
+
this.buffer = Buffer.allocUnsafe(0);
|
|
87
|
+
this.expectedLength = null;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Returns true if there is buffered partial data.
|
|
91
|
+
*/
|
|
92
|
+
hasBufferedData() {
|
|
93
|
+
return this.buffer.length > 0 || this.expectedLength !== null;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Returns current buffer size in bytes.
|
|
97
|
+
*/
|
|
98
|
+
getBufferSize() {
|
|
99
|
+
return this.buffer.length;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=length-prefixed.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"length-prefixed.js","sourceRoot":"","sources":["../../src/framing/length-prefixed.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { FramingCodec } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Options for LineDelimitedFraming.
|
|
4
|
+
*/
|
|
5
|
+
export interface LineDelimitedFramingOptions {
|
|
6
|
+
/**
|
|
7
|
+
* Delimiter byte (default: 0x0A = '\n').
|
|
8
|
+
*/
|
|
9
|
+
delimiter?: number;
|
|
10
|
+
/**
|
|
11
|
+
* Maximum buffer size before throwing error (default: 8MB).
|
|
12
|
+
* Prevents DoS from infinitely long lines without delimiter.
|
|
13
|
+
*/
|
|
14
|
+
maxBufferSize?: number;
|
|
15
|
+
/**
|
|
16
|
+
* Whether to strip delimiter from decoded frames (default: true).
|
|
17
|
+
*/
|
|
18
|
+
stripDelimiter?: boolean;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Line-delimited framing codec.
|
|
22
|
+
* Format: {payload}{delimiter}
|
|
23
|
+
*
|
|
24
|
+
* Each frame is terminated by a delimiter byte (default newline).
|
|
25
|
+
* Handles partial chunks and multiple frames per chunk.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* const framing = new LineDelimitedFraming();
|
|
29
|
+
* const encoded = framing.encode(Buffer.from('hello')); // Buffer<'hello\n'>
|
|
30
|
+
* const frames = framing.decode(Buffer.from('world\n')); // [Buffer<'world'>]
|
|
31
|
+
*/
|
|
32
|
+
export declare class LineDelimitedFraming implements FramingCodec {
|
|
33
|
+
private readonly delimiter;
|
|
34
|
+
private readonly maxBufferSize;
|
|
35
|
+
private readonly stripDelimiter;
|
|
36
|
+
private buffer;
|
|
37
|
+
constructor(options?: LineDelimitedFramingOptions);
|
|
38
|
+
/**
|
|
39
|
+
* Encodes a payload with delimiter.
|
|
40
|
+
* If payload already ends with delimiter, does not add another one.
|
|
41
|
+
*/
|
|
42
|
+
encode(payload: Buffer): Buffer;
|
|
43
|
+
/**
|
|
44
|
+
* Decodes incoming chunk and extracts complete frames.
|
|
45
|
+
* Buffers partial data until delimiter is found.
|
|
46
|
+
*/
|
|
47
|
+
decode(chunk: Buffer): Buffer[];
|
|
48
|
+
/**
|
|
49
|
+
* Resets internal buffer state.
|
|
50
|
+
*/
|
|
51
|
+
reset(): void;
|
|
52
|
+
/**
|
|
53
|
+
* Returns true if there is buffered partial data.
|
|
54
|
+
*/
|
|
55
|
+
hasBufferedData(): boolean;
|
|
56
|
+
/**
|
|
57
|
+
* Returns current buffer size in bytes.
|
|
58
|
+
*/
|
|
59
|
+
getBufferSize(): number;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=line-delimited.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"line-delimited.d.ts","sourceRoot":"","sources":["../../src/framing/line-delimited.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C;;GAEG;AACH,MAAM,WAAW,2BAA2B;IAC1C;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;OAEG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED;;;;;;;;;;;GAWG;AACH,qBAAa,oBAAqB,YAAW,YAAY;IACvD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAU;IACzC,OAAO,CAAC,MAAM,CAAS;gBAEX,OAAO,GAAE,2BAAgC;IAOrD;;;OAGG;IACH,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAa/B;;;OAGG;IACH,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE;IA0C/B;;OAEG;IACH,KAAK,IAAI,IAAI;IAIb;;OAEG;IACH,eAAe,IAAI,OAAO;IAI1B;;OAEG;IACH,aAAa,IAAI,MAAM;CAGxB"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { FramingError } from "../utils/errors.js";
|
|
2
|
+
/**
|
|
3
|
+
* Line-delimited framing codec.
|
|
4
|
+
* Format: {payload}{delimiter}
|
|
5
|
+
*
|
|
6
|
+
* Each frame is terminated by a delimiter byte (default newline).
|
|
7
|
+
* Handles partial chunks and multiple frames per chunk.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* const framing = new LineDelimitedFraming();
|
|
11
|
+
* const encoded = framing.encode(Buffer.from('hello')); // Buffer<'hello\n'>
|
|
12
|
+
* const frames = framing.decode(Buffer.from('world\n')); // [Buffer<'world'>]
|
|
13
|
+
*/
|
|
14
|
+
export class LineDelimitedFraming {
|
|
15
|
+
delimiter;
|
|
16
|
+
maxBufferSize;
|
|
17
|
+
stripDelimiter;
|
|
18
|
+
buffer;
|
|
19
|
+
constructor(options = {}) {
|
|
20
|
+
this.delimiter = options.delimiter ?? 0x0a; // '\n'
|
|
21
|
+
this.maxBufferSize = options.maxBufferSize ?? 8 * 1024 * 1024; // 8MB
|
|
22
|
+
this.stripDelimiter = options.stripDelimiter ?? true;
|
|
23
|
+
this.buffer = Buffer.allocUnsafe(0);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Encodes a payload with delimiter.
|
|
27
|
+
* If payload already ends with delimiter, does not add another one.
|
|
28
|
+
*/
|
|
29
|
+
encode(payload) {
|
|
30
|
+
// Check if payload already ends with delimiter
|
|
31
|
+
if (payload.length > 0 && payload[payload.length - 1] === this.delimiter) {
|
|
32
|
+
return payload;
|
|
33
|
+
}
|
|
34
|
+
// Add delimiter
|
|
35
|
+
const result = Buffer.allocUnsafe(payload.length + 1);
|
|
36
|
+
payload.copy(result, 0);
|
|
37
|
+
result[payload.length] = this.delimiter;
|
|
38
|
+
return result;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Decodes incoming chunk and extracts complete frames.
|
|
42
|
+
* Buffers partial data until delimiter is found.
|
|
43
|
+
*/
|
|
44
|
+
decode(chunk) {
|
|
45
|
+
// Append chunk to buffer
|
|
46
|
+
this.buffer = Buffer.concat([this.buffer, chunk]);
|
|
47
|
+
// Check buffer size limit
|
|
48
|
+
if (this.buffer.length > this.maxBufferSize) {
|
|
49
|
+
const error = new FramingError(`Buffer size ${this.buffer.length} exceeds maximum ${this.maxBufferSize}`);
|
|
50
|
+
this.reset();
|
|
51
|
+
throw error;
|
|
52
|
+
}
|
|
53
|
+
const frames = [];
|
|
54
|
+
let searchStart = 0;
|
|
55
|
+
// Find all complete frames (terminated by delimiter)
|
|
56
|
+
while (searchStart < this.buffer.length) {
|
|
57
|
+
const delimiterIndex = this.buffer.indexOf(this.delimiter, searchStart);
|
|
58
|
+
if (delimiterIndex === -1) {
|
|
59
|
+
// No more delimiters found
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
// Extract frame
|
|
63
|
+
const frameEnd = this.stripDelimiter ? delimiterIndex : delimiterIndex + 1;
|
|
64
|
+
const frame = this.buffer.subarray(searchStart, frameEnd);
|
|
65
|
+
frames.push(frame);
|
|
66
|
+
// Move past delimiter
|
|
67
|
+
searchStart = delimiterIndex + 1;
|
|
68
|
+
}
|
|
69
|
+
// Keep remaining data in buffer
|
|
70
|
+
if (searchStart > 0) {
|
|
71
|
+
this.buffer = this.buffer.subarray(searchStart);
|
|
72
|
+
}
|
|
73
|
+
return frames;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Resets internal buffer state.
|
|
77
|
+
*/
|
|
78
|
+
reset() {
|
|
79
|
+
this.buffer = Buffer.allocUnsafe(0);
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Returns true if there is buffered partial data.
|
|
83
|
+
*/
|
|
84
|
+
hasBufferedData() {
|
|
85
|
+
return this.buffer.length > 0;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Returns current buffer size in bytes.
|
|
89
|
+
*/
|
|
90
|
+
getBufferSize() {
|
|
91
|
+
return this.buffer.length;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=line-delimited.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"line-delimited.js","sourceRoot":"","sources":["../../src/framing/line-delimited.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAwBlD;;;;;;;;;;;GAWG;AACH,MAAM,OAAO,oBAAoB;IACd,SAAS,CAAS;IAClB,aAAa,CAAS;IACtB,cAAc,CAAU;IACjC,MAAM,CAAS;IAEvB,YAAY,UAAuC,EAAE;QACnD,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,IAAI,CAAC,CAAC,OAAO;QACnD,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC,aAAa,IAAI,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,MAAM;QACrE,IAAI,CAAC,cAAc,GAAG,OAAO,CAAC,cAAc,IAAI,IAAI,CAAC;QACrD,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;IACtC,CAAC;IAED;;;OAGG;IACH,MAAM,CAAC,OAAe;QACpB,+CAA+C;QAC/C,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,KAAK,IAAI,CAAC,SAAS,EAAE,CAAC;YACzE,OAAO,OAAO,CAAC;QACjB,CAAC;QAED,gBAAgB;QAChB,MAAM,MAAM,GAAG,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QACtD,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QACxB,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC;QACxC,OAAO,MAAM,CAAC;IAChB,CAAC;IAED;;;OAGG;IACH,MAAM,CAAC,KAAa;QAClB,yBAAyB;QACzB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC;QAElD,0BAA0B;QAC1B,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC;YAC5C,MAAM,KAAK,GAAG,IAAI,YAAY,CAC5B,eAAe,IAAI,CAAC,MAAM,CAAC,MAAM,oBAAoB,IAAI,CAAC,aAAa,EAAE,CAC1E,CAAC;YACF,IAAI,CAAC,KAAK,EAAE,CAAC;YACb,MAAM,KAAK,CAAC;QACd,CAAC;QAED,MAAM,MAAM,GAAa,EAAE,CAAC;QAC5B,IAAI,WAAW,GAAG,CAAC,CAAC;QAEpB,qDAAqD;QACrD,OAAO,WAAW,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;YACxC,MAAM,cAAc,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;YAExE,IAAI,cAAc,KAAK,CAAC,CAAC,EAAE,CAAC;gBAC1B,2BAA2B;gBAC3B,MAAM;YACR,CAAC;YAED,gBAAgB;YAChB,MAAM,QAAQ,GAAG,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,cAAc,GAAG,CAAC,CAAC;YAC3E,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;YAC1D,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAEnB,sBAAsB;YACtB,WAAW,GAAG,cAAc,GAAG,CAAC,CAAC;QACnC,CAAC;QAED,gCAAgC;QAChC,IAAI,WAAW,GAAG,CAAC,EAAE,CAAC;YACpB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;QAClD,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;IAED;;OAEG;IACH,KAAK;QACH,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;IACtC,CAAC;IAED;;OAEG;IACH,eAAe;QACb,OAAO,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC;IAChC,CAAC;IAED;;OAEG;IACH,aAAa;QACX,OAAO,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC;IAC5B,CAAC;CACF"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Framing codec interface for message boundary detection in byte streams.
|
|
3
|
+
* Implementations: line-delimited, length-prefixed, etc.
|
|
4
|
+
*/
|
|
5
|
+
export interface FramingCodec {
|
|
6
|
+
/**
|
|
7
|
+
* Encodes a message payload into a framed buffer.
|
|
8
|
+
* @param payload - Raw message data
|
|
9
|
+
* @returns Framed buffer ready for transmission
|
|
10
|
+
*/
|
|
11
|
+
encode(payload: Buffer): Buffer;
|
|
12
|
+
/**
|
|
13
|
+
* Decodes incoming chunk and extracts complete messages.
|
|
14
|
+
* May buffer partial data internally.
|
|
15
|
+
*
|
|
16
|
+
* @param chunk - Incoming data chunk
|
|
17
|
+
* @returns Array of complete message payloads (may be empty if buffering)
|
|
18
|
+
*/
|
|
19
|
+
decode(chunk: Buffer): Buffer[];
|
|
20
|
+
/**
|
|
21
|
+
* Resets internal decoder state and clears buffers.
|
|
22
|
+
* Used for error recovery or connection restart.
|
|
23
|
+
*/
|
|
24
|
+
reset(): void;
|
|
25
|
+
/**
|
|
26
|
+
* Returns true if decoder has buffered partial data.
|
|
27
|
+
*/
|
|
28
|
+
hasBufferedData(): boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Returns current buffer size (bytes).
|
|
31
|
+
* Useful for monitoring and debugging.
|
|
32
|
+
*/
|
|
33
|
+
getBufferSize(): number;
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/framing/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B;;;;OAIG;IACH,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC;IAEhC;;;;;;OAMG;IACH,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAEhC;;;OAGG;IACH,KAAK,IAAI,IAAI,CAAC;IAEd;;OAEG;IACH,eAAe,IAAI,OAAO,CAAC;IAE3B;;;OAGG;IACH,aAAa,IAAI,MAAM,CAAC;CACzB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/framing/types.ts"],"names":[],"mappings":""}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @procwire/transport
|
|
3
|
+
*
|
|
4
|
+
* Core IPC transport library with zero runtime dependencies.
|
|
5
|
+
* Provides modular, type-safe building blocks for inter-process communication.
|
|
6
|
+
*
|
|
7
|
+
* Architecture layers (bottom to top):
|
|
8
|
+
* - Transport: Raw byte transfer (stdio, pipes, sockets)
|
|
9
|
+
* - Framing: Message boundary detection
|
|
10
|
+
* - Serialization: Object <-> binary conversion
|
|
11
|
+
* - Protocol: Request/response messaging
|
|
12
|
+
* - Channel: High-level communication API
|
|
13
|
+
* - Process: Child process lifecycle management
|
|
14
|
+
*
|
|
15
|
+
* @packageDocumentation
|
|
16
|
+
*/
|
|
17
|
+
export * from "./transport/index.js";
|
|
18
|
+
export * from "./framing/index.js";
|
|
19
|
+
export * from "./serialization/index.js";
|
|
20
|
+
export * from "./protocol/index.js";
|
|
21
|
+
export * from "./channel/index.js";
|
|
22
|
+
export * from "./process/index.js";
|
|
23
|
+
export * from "./utils/index.js";
|
|
24
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,cAAc,sBAAsB,CAAC;AACrC,cAAc,oBAAoB,CAAC;AACnC,cAAc,0BAA0B,CAAC;AACzC,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AAGnC,cAAc,kBAAkB,CAAC"}
|