@procwire/bun-client 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,153 @@
1
+ /**
2
+ * Client class - Child-side API for Procwire IPC.
3
+ *
4
+ * This is the Bun.js optimized version using Bun.listen() for named pipe server.
5
+ *
6
+ * RESPONSIBILITIES:
7
+ * - Register method handlers
8
+ * - Register events
9
+ * - Create named pipe server
10
+ * - Send $init to parent
11
+ * - Handle incoming requests
12
+ * - Emit events to parent
13
+ *
14
+ * @module
15
+ */
16
+ import { EventEmitter } from "node:events";
17
+ import type { MethodDefinition, EventDefinition, MethodHandler, ClientOptions } from "./types.js";
18
+ import { BunDrainWaiter } from "./drain-waiter.js";
19
+ type BunSocket = Awaited<ReturnType<typeof Bun.connect>>;
20
+ /**
21
+ * Client - Child-side API for Procwire IPC.
22
+ *
23
+ * This is the Bun.js optimized version.
24
+ *
25
+ * @example
26
+ * ```typescript
27
+ * const client = new Client()
28
+ * .handle('query', async (data, ctx) => {
29
+ * const results = await search(data);
30
+ * ctx.respond(results);
31
+ * })
32
+ * .handle('insert', async (data, ctx) => {
33
+ * ctx.ack({ accepted: true });
34
+ * await processInBackground(data);
35
+ * })
36
+ * .event('progress');
37
+ *
38
+ * await client.start();
39
+ *
40
+ * // Emit events to parent
41
+ * client.emitEvent('progress', { percent: 50 });
42
+ * ```
43
+ */
44
+ export declare class Client extends EventEmitter {
45
+ private _defaultCodec;
46
+ private _methods;
47
+ private _events;
48
+ private _server;
49
+ private _socket;
50
+ private _frameBuffer;
51
+ private _methodNameToId;
52
+ private _methodIdToName;
53
+ private _eventNameToId;
54
+ private _abortCallbacks;
55
+ private _activeContexts;
56
+ private _started;
57
+ private readonly _headerPool;
58
+ private _headerPoolIndex;
59
+ private _drainWaiter;
60
+ constructor(options?: ClientOptions);
61
+ /**
62
+ * Register a method handler.
63
+ *
64
+ * @param method - Method name
65
+ * @param handler - Handler function
66
+ * @param options - Method configuration
67
+ * @returns this for chaining
68
+ *
69
+ * @example
70
+ * ```typescript
71
+ * client.handle('query', async (data, ctx) => {
72
+ * ctx.respond({ results: [] });
73
+ * });
74
+ * ```
75
+ */
76
+ handle<TData = unknown>(method: string, handler: MethodHandler<TData>, options?: Partial<MethodDefinition>): this;
77
+ /**
78
+ * Register an event that can be emitted to parent.
79
+ *
80
+ * @param name - Event name
81
+ * @param options - Event configuration
82
+ * @returns this for chaining
83
+ *
84
+ * @example
85
+ * ```typescript
86
+ * client.event('progress');
87
+ * client.event('status', { codec: arrowCodec });
88
+ * ```
89
+ */
90
+ event(name: string, options?: Partial<EventDefinition>): this;
91
+ /**
92
+ * Start the client.
93
+ *
94
+ * Creates named pipe server using Bun.listen(), waits for server to be ready,
95
+ * then sends $init to parent.
96
+ *
97
+ * @example
98
+ * ```typescript
99
+ * await client.start();
100
+ * // Client is now ready to receive requests
101
+ * ```
102
+ */
103
+ start(): Promise<void>;
104
+ /**
105
+ * Emit an event to parent.
106
+ *
107
+ * @param eventName - Event name (must be registered with .event())
108
+ * @param data - Event data
109
+ * @returns Promise that resolves when the event has been written
110
+ * and socket buffer has drained (if backpressure occurred).
111
+ *
112
+ * @example
113
+ * ```typescript
114
+ * await client.emitEvent('progress', { percent: 50 });
115
+ * ```
116
+ */
117
+ emitEvent(eventName: string, data: unknown): Promise<void>;
118
+ /**
119
+ * Graceful shutdown.
120
+ */
121
+ shutdown(): Promise<void>;
122
+ /**
123
+ * Whether client is connected to parent.
124
+ */
125
+ get connected(): boolean;
126
+ /**
127
+ * @internal Acquire a header buffer from the ring pool.
128
+ */
129
+ _acquireHeaderBuffer(): Buffer;
130
+ /**
131
+ * @internal Get the drain waiter instance.
132
+ */
133
+ _getDrainWaiter(): BunDrainWaiter | null;
134
+ /**
135
+ * @internal Get the socket instance.
136
+ */
137
+ _getSocket(): BunSocket | null;
138
+ private _generatePipePath;
139
+ private _createPipeServer;
140
+ private _sendInit;
141
+ private _handleFrame;
142
+ private _handleAbort;
143
+ private _sendErrorResponse;
144
+ /**
145
+ * Send a frame with proper backpressure handling.
146
+ * Used for emitting events to parent.
147
+ *
148
+ * Bun sockets don't have cork/uncork, so we concatenate buffers.
149
+ */
150
+ private _sendFrame;
151
+ }
152
+ export {};
153
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAW3C,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAGlG,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAInD,KAAK,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,OAAO,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC;AAEzD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,qBAAa,MAAO,SAAQ,YAAY;IACtC,OAAO,CAAC,aAAa,CAAQ;IAC7B,OAAO,CAAC,QAAQ,CAAwE;IACxF,OAAO,CAAC,OAAO,CAAsC;IAErD,OAAO,CAAC,OAAO,CAA0B;IACzC,OAAO,CAAC,OAAO,CAA0B;IACzC,OAAO,CAAC,YAAY,CAA4B;IAEhD,OAAO,CAAC,eAAe,CAA6B;IACpD,OAAO,CAAC,eAAe,CAA6B;IACpD,OAAO,CAAC,cAAc,CAA6B;IAEnD,OAAO,CAAC,eAAe,CAAsC;IAC7D,OAAO,CAAC,eAAe,CAAyC;IAChE,OAAO,CAAC,QAAQ,CAAS;IAGzB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAE1B;IACF,OAAO,CAAC,gBAAgB,CAAK;IAG7B,OAAO,CAAC,YAAY,CAA+B;gBAEvC,OAAO,CAAC,EAAE,aAAa;IASnC;;;;;;;;;;;;;;OAcG;IACH,MAAM,CAAC,KAAK,GAAG,OAAO,EACpB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,aAAa,CAAC,KAAK,CAAC,EAC7B,OAAO,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,GAClC,IAAI;IAiBP;;;;;;;;;;;;OAYG;IACH,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,GAAG,IAAI;IAY7D;;;;;;;;;;;OAWG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IA8B5B;;;;;;;;;;;;OAYG;IACG,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAgBhE;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAQ/B;;OAEG;IACH,IAAI,SAAS,IAAI,OAAO,CAEvB;IAMD;;OAEG;IACH,oBAAoB,IAAI,MAAM;IAM9B;;OAEG;IACH,eAAe,IAAI,cAAc,GAAG,IAAI;IAIxC;;OAEG;IACH,UAAU,IAAI,SAAS,GAAG,IAAI;IAQ9B,OAAO,CAAC,iBAAiB;IAOzB,OAAO,CAAC,iBAAiB;IA2CzB,OAAO,CAAC,SAAS;IAqCjB,OAAO,CAAC,YAAY;IAyEpB,OAAO,CAAC,YAAY;IAqBpB,OAAO,CAAC,kBAAkB;IAsB1B;;;;;OAKG;YACW,UAAU;CA6BzB"}
package/dist/client.js ADDED
@@ -0,0 +1,423 @@
1
+ /**
2
+ * Client class - Child-side API for Procwire IPC.
3
+ *
4
+ * This is the Bun.js optimized version using Bun.listen() for named pipe server.
5
+ *
6
+ * RESPONSIBILITIES:
7
+ * - Register method handlers
8
+ * - Register events
9
+ * - Create named pipe server
10
+ * - Send $init to parent
11
+ * - Handle incoming requests
12
+ * - Emit events to parent
13
+ *
14
+ * @module
15
+ */
16
+ import { EventEmitter } from "node:events";
17
+ import { FrameBuffer, Flags, encodeHeaderInto, HEADER_SIZE, HEADER_POOL_SIZE, ABORT_METHOD_ID, } from "@procwire/protocol";
18
+ import { msgpackCodec, codecDeserialize } from "@procwire/codecs";
19
+ import { RequestContextImpl } from "./request-context.js";
20
+ import { ClientErrors } from "./errors.js";
21
+ import { BunDrainWaiter } from "./drain-waiter.js";
22
+ /**
23
+ * Client - Child-side API for Procwire IPC.
24
+ *
25
+ * This is the Bun.js optimized version.
26
+ *
27
+ * @example
28
+ * ```typescript
29
+ * const client = new Client()
30
+ * .handle('query', async (data, ctx) => {
31
+ * const results = await search(data);
32
+ * ctx.respond(results);
33
+ * })
34
+ * .handle('insert', async (data, ctx) => {
35
+ * ctx.ack({ accepted: true });
36
+ * await processInBackground(data);
37
+ * })
38
+ * .event('progress');
39
+ *
40
+ * await client.start();
41
+ *
42
+ * // Emit events to parent
43
+ * client.emitEvent('progress', { percent: 50 });
44
+ * ```
45
+ */
46
+ export class Client extends EventEmitter {
47
+ _defaultCodec;
48
+ _methods = new Map();
49
+ _events = new Map();
50
+ _server = null;
51
+ _socket = null;
52
+ _frameBuffer = null;
53
+ _methodNameToId = new Map();
54
+ _methodIdToName = new Map();
55
+ _eventNameToId = new Map();
56
+ _abortCallbacks = new Map();
57
+ _activeContexts = new Map();
58
+ _started = false;
59
+ // Ring buffer for headers (OPT-02: allocation-free sends)
60
+ _headerPool = Array.from({ length: HEADER_POOL_SIZE }, () => Buffer.allocUnsafe(HEADER_SIZE));
61
+ _headerPoolIndex = 0;
62
+ // OPT-04: Backpressure tracking via BunDrainWaiter
63
+ _drainWaiter = null;
64
+ constructor(options) {
65
+ super();
66
+ this._defaultCodec = options?.defaultCodec ?? msgpackCodec;
67
+ }
68
+ // ═══════════════════════════════════════════════════════════════════════════
69
+ // BUILDER API
70
+ // ═══════════════════════════════════════════════════════════════════════════
71
+ /**
72
+ * Register a method handler.
73
+ *
74
+ * @param method - Method name
75
+ * @param handler - Handler function
76
+ * @param options - Method configuration
77
+ * @returns this for chaining
78
+ *
79
+ * @example
80
+ * ```typescript
81
+ * client.handle('query', async (data, ctx) => {
82
+ * ctx.respond({ results: [] });
83
+ * });
84
+ * ```
85
+ */
86
+ handle(method, handler, options) {
87
+ if (this._started) {
88
+ throw ClientErrors.cannotAddHandlerAfterStart();
89
+ }
90
+ this._methods.set(method, {
91
+ def: {
92
+ response: options?.response ?? "result",
93
+ codec: options?.codec ?? this._defaultCodec,
94
+ cancellable: options?.cancellable ?? false,
95
+ },
96
+ handler: handler,
97
+ });
98
+ return this;
99
+ }
100
+ /**
101
+ * Register an event that can be emitted to parent.
102
+ *
103
+ * @param name - Event name
104
+ * @param options - Event configuration
105
+ * @returns this for chaining
106
+ *
107
+ * @example
108
+ * ```typescript
109
+ * client.event('progress');
110
+ * client.event('status', { codec: arrowCodec });
111
+ * ```
112
+ */
113
+ event(name, options) {
114
+ if (this._started) {
115
+ throw ClientErrors.cannotAddEventAfterStart();
116
+ }
117
+ this._events.set(name, {
118
+ codec: options?.codec ?? this._defaultCodec,
119
+ });
120
+ return this;
121
+ }
122
+ /**
123
+ * Start the client.
124
+ *
125
+ * Creates named pipe server using Bun.listen(), waits for server to be ready,
126
+ * then sends $init to parent.
127
+ *
128
+ * @example
129
+ * ```typescript
130
+ * await client.start();
131
+ * // Client is now ready to receive requests
132
+ * ```
133
+ */
134
+ async start() {
135
+ if (this._started) {
136
+ throw ClientErrors.alreadyStarted();
137
+ }
138
+ this._started = true;
139
+ // Assign IDs to methods and events
140
+ let methodId = 1;
141
+ for (const name of this._methods.keys()) {
142
+ this._methodNameToId.set(name, methodId);
143
+ this._methodIdToName.set(methodId, name);
144
+ methodId++;
145
+ }
146
+ let eventId = 1;
147
+ for (const name of this._events.keys()) {
148
+ this._eventNameToId.set(name, eventId);
149
+ eventId++;
150
+ }
151
+ // Create pipe path
152
+ const pipePath = this._generatePipePath();
153
+ // Create server and wait for listen
154
+ await this._createPipeServer(pipePath);
155
+ // Send $init to parent (via stdout, JSON-RPC control plane)
156
+ this._sendInit(pipePath);
157
+ }
158
+ /**
159
+ * Emit an event to parent.
160
+ *
161
+ * @param eventName - Event name (must be registered with .event())
162
+ * @param data - Event data
163
+ * @returns Promise that resolves when the event has been written
164
+ * and socket buffer has drained (if backpressure occurred).
165
+ *
166
+ * @example
167
+ * ```typescript
168
+ * await client.emitEvent('progress', { percent: 50 });
169
+ * ```
170
+ */
171
+ async emitEvent(eventName, data) {
172
+ if (!this._socket) {
173
+ throw ClientErrors.notConnected();
174
+ }
175
+ const eventId = this._eventNameToId.get(eventName);
176
+ if (eventId === undefined) {
177
+ throw ClientErrors.unknownEvent(eventName);
178
+ }
179
+ const eventDef = this._events.get(eventName);
180
+ const codec = eventDef.codec ?? this._defaultCodec;
181
+ await this._sendFrame(eventId, 0, data, codec, Flags.DIRECTION_TO_PARENT);
182
+ }
183
+ /**
184
+ * Graceful shutdown.
185
+ */
186
+ async shutdown() {
187
+ this._socket?.end();
188
+ this._server?.stop(true);
189
+ this._drainWaiter?.clear();
190
+ this._socket = null;
191
+ this._server = null;
192
+ }
193
+ /**
194
+ * Whether client is connected to parent.
195
+ */
196
+ get connected() {
197
+ return this._socket !== null;
198
+ }
199
+ // ═══════════════════════════════════════════════════════════════════════════
200
+ // INTERNAL API (for RequestContextImpl)
201
+ // ═══════════════════════════════════════════════════════════════════════════
202
+ /**
203
+ * @internal Acquire a header buffer from the ring pool.
204
+ */
205
+ _acquireHeaderBuffer() {
206
+ const buffer = this._headerPool[this._headerPoolIndex];
207
+ this._headerPoolIndex = (this._headerPoolIndex + 1) % HEADER_POOL_SIZE;
208
+ return buffer;
209
+ }
210
+ /**
211
+ * @internal Get the drain waiter instance.
212
+ */
213
+ _getDrainWaiter() {
214
+ return this._drainWaiter;
215
+ }
216
+ /**
217
+ * @internal Get the socket instance.
218
+ */
219
+ _getSocket() {
220
+ return this._socket;
221
+ }
222
+ // ═══════════════════════════════════════════════════════════════════════════
223
+ // PRIVATE: Initialization
224
+ // ═══════════════════════════════════════════════════════════════════════════
225
+ _generatePipePath() {
226
+ const id = Math.random().toString(36).slice(2, 10);
227
+ return process.platform === "win32"
228
+ ? `\\\\.\\pipe\\procwire-${process.pid}-${id}`
229
+ : `/tmp/procwire-${process.pid}-${id}.sock`;
230
+ }
231
+ _createPipeServer(pipePath) {
232
+ return new Promise((resolve, reject) => {
233
+ try {
234
+ // Create server using Bun.listen with unix socket
235
+ this._server = Bun.listen({
236
+ unix: pipePath,
237
+ socket: {
238
+ open: (socket) => {
239
+ // Parent connected
240
+ this._socket = socket;
241
+ this._drainWaiter = new BunDrainWaiter();
242
+ this._frameBuffer = new FrameBuffer();
243
+ },
244
+ data: (_socket, data) => {
245
+ // Handle incoming data
246
+ if (!this._frameBuffer)
247
+ return;
248
+ const frames = this._frameBuffer.push(data);
249
+ for (const frame of frames) {
250
+ this._handleFrame(frame);
251
+ }
252
+ },
253
+ error: (_socket, err) => {
254
+ this.emit("error", err);
255
+ },
256
+ close: (_socket) => {
257
+ this._drainWaiter?.clear();
258
+ this.emit("disconnected");
259
+ },
260
+ drain: (_socket) => {
261
+ // Backpressure released
262
+ this._drainWaiter?.onDrain();
263
+ },
264
+ },
265
+ });
266
+ // Server is listening
267
+ resolve();
268
+ }
269
+ catch (error) {
270
+ reject(error);
271
+ }
272
+ });
273
+ }
274
+ _sendInit(pipePath) {
275
+ const schema = {
276
+ methods: Object.fromEntries(Array.from(this._methods.entries()).map(([name, { def }]) => [
277
+ name,
278
+ {
279
+ id: this._methodNameToId.get(name),
280
+ response: def.response,
281
+ },
282
+ ])),
283
+ events: Object.fromEntries(Array.from(this._events.keys()).map((name) => [
284
+ name,
285
+ { id: this._eventNameToId.get(name) },
286
+ ])),
287
+ };
288
+ const initMessage = {
289
+ jsonrpc: "2.0",
290
+ method: "$init",
291
+ params: {
292
+ pipe: pipePath,
293
+ schema,
294
+ version: "2.0.0",
295
+ },
296
+ };
297
+ // Write to stdout (JSON-RPC control plane)
298
+ console.log(JSON.stringify(initMessage));
299
+ }
300
+ // ═══════════════════════════════════════════════════════════════════════════
301
+ // PRIVATE: Frame Handling
302
+ // ═══════════════════════════════════════════════════════════════════════════
303
+ _handleFrame(frame) {
304
+ const { header } = frame;
305
+ // Abort signal (reserved methodId)
306
+ if (header.methodId === ABORT_METHOD_ID) {
307
+ this._handleAbort(header.requestId);
308
+ return;
309
+ }
310
+ // Request from parent
311
+ const methodName = this._methodIdToName.get(header.methodId);
312
+ if (!methodName) {
313
+ // Unknown method - send error response
314
+ this._sendErrorResponse(header.requestId, header.methodId, `Unknown method ID: ${header.methodId}`);
315
+ return;
316
+ }
317
+ const methodEntry = this._methods.get(methodName);
318
+ if (!methodEntry)
319
+ return;
320
+ const { def, handler } = methodEntry;
321
+ const codec = def.codec ?? this._defaultCodec;
322
+ const data = codecDeserialize(codec, frame);
323
+ // Create request context
324
+ const ctx = new RequestContextImpl(header.requestId, methodName, header.methodId, codec, this._socket, this._abortCallbacks, () => this._acquireHeaderBuffer(), this._drainWaiter);
325
+ // Track active context for abort handling
326
+ this._activeContexts.set(header.requestId, ctx);
327
+ // Call handler
328
+ try {
329
+ const result = handler(data, ctx);
330
+ if (result instanceof Promise) {
331
+ result
332
+ .catch((err) => {
333
+ if (!ctx.responded) {
334
+ // ctx.error() is async - fire and forget with error handling
335
+ ctx.error(err).catch(() => {
336
+ /* ignore - socket may be closed */
337
+ });
338
+ }
339
+ })
340
+ .finally(() => {
341
+ this._activeContexts.delete(header.requestId);
342
+ });
343
+ }
344
+ else {
345
+ this._activeContexts.delete(header.requestId);
346
+ }
347
+ }
348
+ catch (err) {
349
+ if (!ctx.responded) {
350
+ // ctx.error() is async - fire and forget with error handling
351
+ ctx.error(err).catch(() => {
352
+ /* ignore - socket may be closed */
353
+ });
354
+ }
355
+ this._activeContexts.delete(header.requestId);
356
+ }
357
+ }
358
+ _handleAbort(requestId) {
359
+ // Mark context as aborted
360
+ const ctx = this._activeContexts.get(requestId);
361
+ if (ctx) {
362
+ ctx._markAborted();
363
+ }
364
+ // Call abort callbacks
365
+ const callbacks = this._abortCallbacks.get(requestId);
366
+ if (callbacks) {
367
+ for (const cb of callbacks) {
368
+ try {
369
+ cb();
370
+ }
371
+ catch {
372
+ /* ignore */
373
+ }
374
+ }
375
+ this._abortCallbacks.delete(requestId);
376
+ }
377
+ }
378
+ _sendErrorResponse(requestId, methodId, message) {
379
+ if (!this._socket)
380
+ return;
381
+ const payload = this._defaultCodec.serialize(message);
382
+ const headerBuf = this._acquireHeaderBuffer();
383
+ encodeHeaderInto(headerBuf, {
384
+ methodId,
385
+ flags: Flags.IS_RESPONSE | Flags.IS_ERROR | Flags.DIRECTION_TO_PARENT,
386
+ requestId,
387
+ payloadLength: payload.length,
388
+ });
389
+ // Bun doesn't have cork/uncork, concatenate for atomic write
390
+ const combined = Buffer.concat([headerBuf, payload]);
391
+ this._socket.write(combined);
392
+ }
393
+ // ═══════════════════════════════════════════════════════════════════════════
394
+ // PRIVATE: Frame Sending
395
+ // ═══════════════════════════════════════════════════════════════════════════
396
+ /**
397
+ * Send a frame with proper backpressure handling.
398
+ * Used for emitting events to parent.
399
+ *
400
+ * Bun sockets don't have cork/uncork, so we concatenate buffers.
401
+ */
402
+ async _sendFrame(methodId, requestId, data, codec, flags) {
403
+ if (!this._socket || !this._drainWaiter)
404
+ return;
405
+ const payload = codec.serialize(data);
406
+ const headerBuf = this._acquireHeaderBuffer();
407
+ encodeHeaderInto(headerBuf, {
408
+ methodId,
409
+ flags,
410
+ requestId,
411
+ payloadLength: payload.length,
412
+ });
413
+ // Bun doesn't have cork/uncork, concatenate for atomic write
414
+ const combined = Buffer.concat([headerBuf, payload]);
415
+ const canContinue = this._socket.write(combined);
416
+ // OPT-04: Wait AFTER write if backpressure
417
+ if (!canContinue) {
418
+ this._drainWaiter.markNeedsDrain();
419
+ await this._drainWaiter.waitForDrain();
420
+ }
421
+ }
422
+ }
423
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,74 @@
1
+ /**
2
+ * BunDrainWaiter - Singleton pattern for Bun socket drain waiting.
3
+ *
4
+ * PROBLEM: In Bun, socket.write() returns a boolean indicating if more data
5
+ * can be written. When it returns false, you need to wait for the drain
6
+ * callback before writing more data.
7
+ *
8
+ * SOLUTION: This class maintains pending drain waiters and resolves them
9
+ * when the drain callback is called.
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * const drainWaiter = new BunDrainWaiter();
14
+ *
15
+ * // In socket handlers:
16
+ * socket: {
17
+ * drain(socket) {
18
+ * drainWaiter.onDrain();
19
+ * }
20
+ * }
21
+ *
22
+ * // When writing:
23
+ * const canContinue = socket.write(data);
24
+ * if (!canContinue) {
25
+ * drainWaiter.markNeedsDrain();
26
+ * await drainWaiter.waitForDrain();
27
+ * }
28
+ * ```
29
+ *
30
+ * @module
31
+ */
32
+ /**
33
+ * Singleton drain waiter for Bun sockets.
34
+ *
35
+ * Allows multiple concurrent requests to wait for drain
36
+ * with a simple callback-based pattern.
37
+ */
38
+ export declare class BunDrainWaiter {
39
+ private _waiters;
40
+ private _needsDrain;
41
+ private _closed;
42
+ /**
43
+ * Call this from the socket's drain handler.
44
+ * Resolves all pending waiters.
45
+ */
46
+ onDrain(): void;
47
+ /**
48
+ * Mark that drain is needed (socket.write returned false).
49
+ */
50
+ markNeedsDrain(): void;
51
+ /**
52
+ * Check if drain is needed.
53
+ */
54
+ get needsDrain(): boolean;
55
+ /**
56
+ * Wait for socket drain.
57
+ *
58
+ * Call markNeedsDrain() before calling this if socket.write returned false.
59
+ *
60
+ * @throws {Error} If socket is closed while waiting
61
+ */
62
+ waitForDrain(): Promise<void>;
63
+ /**
64
+ * Clear all pending waiters.
65
+ * Call this on socket close/disconnect.
66
+ */
67
+ clear(): void;
68
+ /**
69
+ * Mark socket as closed but don't clear waiters yet.
70
+ * They will be cleared when they complete.
71
+ */
72
+ markClosed(): void;
73
+ }
74
+ //# sourceMappingURL=drain-waiter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"drain-waiter.d.ts","sourceRoot":"","sources":["../src/drain-waiter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH;;;;;GAKG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAA8B;IAC9C,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,OAAO,CAAS;IAExB;;;OAGG;IACH,OAAO,IAAI,IAAI;IASf;;OAEG;IACH,cAAc,IAAI,IAAI;IAItB;;OAEG;IACH,IAAI,UAAU,IAAI,OAAO,CAExB;IAED;;;;;;OAMG;IACG,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IAiBnC;;;OAGG;IACH,KAAK,IAAI,IAAI;IAUb;;;OAGG;IACH,UAAU,IAAI,IAAI;CAGnB"}
@@ -0,0 +1,108 @@
1
+ /**
2
+ * BunDrainWaiter - Singleton pattern for Bun socket drain waiting.
3
+ *
4
+ * PROBLEM: In Bun, socket.write() returns a boolean indicating if more data
5
+ * can be written. When it returns false, you need to wait for the drain
6
+ * callback before writing more data.
7
+ *
8
+ * SOLUTION: This class maintains pending drain waiters and resolves them
9
+ * when the drain callback is called.
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * const drainWaiter = new BunDrainWaiter();
14
+ *
15
+ * // In socket handlers:
16
+ * socket: {
17
+ * drain(socket) {
18
+ * drainWaiter.onDrain();
19
+ * }
20
+ * }
21
+ *
22
+ * // When writing:
23
+ * const canContinue = socket.write(data);
24
+ * if (!canContinue) {
25
+ * drainWaiter.markNeedsDrain();
26
+ * await drainWaiter.waitForDrain();
27
+ * }
28
+ * ```
29
+ *
30
+ * @module
31
+ */
32
+ /**
33
+ * Singleton drain waiter for Bun sockets.
34
+ *
35
+ * Allows multiple concurrent requests to wait for drain
36
+ * with a simple callback-based pattern.
37
+ */
38
+ export class BunDrainWaiter {
39
+ _waiters = new Set();
40
+ _needsDrain = false;
41
+ _closed = false;
42
+ /**
43
+ * Call this from the socket's drain handler.
44
+ * Resolves all pending waiters.
45
+ */
46
+ onDrain() {
47
+ this._needsDrain = false;
48
+ // Resolve all waiters
49
+ for (const resolve of this._waiters) {
50
+ resolve();
51
+ }
52
+ this._waiters.clear();
53
+ }
54
+ /**
55
+ * Mark that drain is needed (socket.write returned false).
56
+ */
57
+ markNeedsDrain() {
58
+ this._needsDrain = true;
59
+ }
60
+ /**
61
+ * Check if drain is needed.
62
+ */
63
+ get needsDrain() {
64
+ return this._needsDrain;
65
+ }
66
+ /**
67
+ * Wait for socket drain.
68
+ *
69
+ * Call markNeedsDrain() before calling this if socket.write returned false.
70
+ *
71
+ * @throws {Error} If socket is closed while waiting
72
+ */
73
+ async waitForDrain() {
74
+ // Fast path: no backpressure
75
+ if (!this._needsDrain) {
76
+ return;
77
+ }
78
+ // Socket closed check
79
+ if (this._closed) {
80
+ throw new Error("Socket closed during backpressure wait");
81
+ }
82
+ // Add this request to waiters
83
+ return new Promise((resolve) => {
84
+ this._waiters.add(resolve);
85
+ });
86
+ }
87
+ /**
88
+ * Clear all pending waiters.
89
+ * Call this on socket close/disconnect.
90
+ */
91
+ clear() {
92
+ this._needsDrain = false;
93
+ this._closed = true;
94
+ // Resolve all waiters so they don't hang
95
+ for (const resolve of this._waiters) {
96
+ resolve();
97
+ }
98
+ this._waiters.clear();
99
+ }
100
+ /**
101
+ * Mark socket as closed but don't clear waiters yet.
102
+ * They will be cleared when they complete.
103
+ */
104
+ markClosed() {
105
+ this._closed = true;
106
+ }
107
+ }
108
+ //# sourceMappingURL=drain-waiter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"drain-waiter.js","sourceRoot":"","sources":["../src/drain-waiter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH;;;;;GAKG;AACH,MAAM,OAAO,cAAc;IACjB,QAAQ,GAAoB,IAAI,GAAG,EAAE,CAAC;IACtC,WAAW,GAAG,KAAK,CAAC;IACpB,OAAO,GAAG,KAAK,CAAC;IAExB;;;OAGG;IACH,OAAO;QACL,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC;QACzB,sBAAsB;QACtB,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YACpC,OAAO,EAAE,CAAC;QACZ,CAAC;QACD,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;IACxB,CAAC;IAED;;OAEG;IACH,cAAc;QACZ,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;IAC1B,CAAC;IAED;;OAEG;IACH,IAAI,UAAU;QACZ,OAAO,IAAI,CAAC,WAAW,CAAC;IAC1B,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,YAAY;QAChB,6BAA6B;QAC7B,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;YACtB,OAAO;QACT,CAAC;QAED,sBAAsB;QACtB,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;QAC5D,CAAC;QAED,8BAA8B;QAC9B,OAAO,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;YACnC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAC7B,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;;OAGG;IACH,KAAK;QACH,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC;QACzB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACpB,yCAAyC;QACzC,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YACpC,OAAO,EAAE,CAAC;QACZ,CAAC;QACD,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;IACxB,CAAC;IAED;;;OAGG;IACH,UAAU;QACR,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;IACtB,CAAC;CACF"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Error types and factory functions for @procwire-bun/client.
3
+ *
4
+ * @module
5
+ */
6
+ /**
7
+ * Base error class for all Procwire client errors.
8
+ */
9
+ export declare class ProcwireClientError extends Error {
10
+ constructor(message: string);
11
+ }
12
+ /**
13
+ * Factory functions for Client-related errors.
14
+ */
15
+ export declare const ClientErrors: {
16
+ /** Cannot add handlers after start */
17
+ readonly cannotAddHandlerAfterStart: () => ProcwireClientError;
18
+ /** Cannot add events after start */
19
+ readonly cannotAddEventAfterStart: () => ProcwireClientError;
20
+ /** Client already started */
21
+ readonly alreadyStarted: () => ProcwireClientError;
22
+ /** Client not connected */
23
+ readonly notConnected: () => ProcwireClientError;
24
+ /** Unknown event name */
25
+ readonly unknownEvent: (eventName: string) => ProcwireClientError;
26
+ /** Response already sent */
27
+ readonly responseAlreadySent: () => ProcwireClientError;
28
+ };
29
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;GAEG;AACH,qBAAa,mBAAoB,SAAQ,KAAK;gBAChC,OAAO,EAAE,MAAM;CAI5B;AAED;;GAEG;AACH,eAAO,MAAM,YAAY;IACvB,sCAAsC;;IAGtC,oCAAoC;;IAGpC,6BAA6B;;IAG7B,2BAA2B;;IAG3B,yBAAyB;uCACC,MAAM;IAEhC,4BAA4B;;CAEpB,CAAC"}
package/dist/errors.js ADDED
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Error types and factory functions for @procwire-bun/client.
3
+ *
4
+ * @module
5
+ */
6
+ /**
7
+ * Base error class for all Procwire client errors.
8
+ */
9
+ export class ProcwireClientError extends Error {
10
+ constructor(message) {
11
+ super(message);
12
+ this.name = "ProcwireClientError";
13
+ }
14
+ }
15
+ /**
16
+ * Factory functions for Client-related errors.
17
+ */
18
+ export const ClientErrors = {
19
+ /** Cannot add handlers after start */
20
+ cannotAddHandlerAfterStart: () => new ProcwireClientError("Cannot add handlers after start()"),
21
+ /** Cannot add events after start */
22
+ cannotAddEventAfterStart: () => new ProcwireClientError("Cannot add events after start()"),
23
+ /** Client already started */
24
+ alreadyStarted: () => new ProcwireClientError("Client already started"),
25
+ /** Client not connected */
26
+ notConnected: () => new ProcwireClientError("Client not connected"),
27
+ /** Unknown event name */
28
+ unknownEvent: (eventName) => new ProcwireClientError(`Unknown event: ${eventName}`),
29
+ /** Response already sent */
30
+ responseAlreadySent: () => new ProcwireClientError("Response already sent"),
31
+ };
32
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;GAEG;AACH,MAAM,OAAO,mBAAoB,SAAQ,KAAK;IAC5C,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAC;IACpC,CAAC;CACF;AAED;;GAEG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,sCAAsC;IACtC,0BAA0B,EAAE,GAAG,EAAE,CAAC,IAAI,mBAAmB,CAAC,mCAAmC,CAAC;IAE9F,oCAAoC;IACpC,wBAAwB,EAAE,GAAG,EAAE,CAAC,IAAI,mBAAmB,CAAC,iCAAiC,CAAC;IAE1F,6BAA6B;IAC7B,cAAc,EAAE,GAAG,EAAE,CAAC,IAAI,mBAAmB,CAAC,wBAAwB,CAAC;IAEvE,2BAA2B;IAC3B,YAAY,EAAE,GAAG,EAAE,CAAC,IAAI,mBAAmB,CAAC,sBAAsB,CAAC;IAEnE,yBAAyB;IACzB,YAAY,EAAE,CAAC,SAAiB,EAAE,EAAE,CAAC,IAAI,mBAAmB,CAAC,kBAAkB,SAAS,EAAE,CAAC;IAE3F,4BAA4B;IAC5B,mBAAmB,EAAE,GAAG,EAAE,CAAC,IAAI,mBAAmB,CAAC,uBAAuB,CAAC;CACnE,CAAC"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * @procwire-bun/client - Child-side API for Procwire IPC (Bun.js optimized).
3
+ *
4
+ * This package provides the client-side implementation for child processes
5
+ * to communicate with the parent process using Procwire's binary protocol.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * import { Client } from '@procwire-bun/client';
10
+ *
11
+ * const client = new Client()
12
+ * .handle('query', async (data, ctx) => {
13
+ * const results = await search(data);
14
+ * ctx.respond(results);
15
+ * })
16
+ * .handle('insert', async (data, ctx) => {
17
+ * ctx.ack({ accepted: true });
18
+ * await processInBackground(data);
19
+ * })
20
+ * .event('progress');
21
+ *
22
+ * await client.start();
23
+ *
24
+ * // Emit events to parent
25
+ * client.emitEvent('progress', { percent: 50 });
26
+ * ```
27
+ *
28
+ * @module
29
+ */
30
+ export { Client } from "./client.js";
31
+ export { RequestContextImpl } from "./request-context.js";
32
+ export { BunDrainWaiter } from "./drain-waiter.js";
33
+ export { ProcwireClientError, ClientErrors } from "./errors.js";
34
+ export type { ResponseType, MethodDefinition, EventDefinition, ClientOptions, MethodHandler, RequestContext, } from "./types.js";
35
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChE,YAAY,EACV,YAAY,EACZ,gBAAgB,EAChB,eAAe,EACf,aAAa,EACb,aAAa,EACb,cAAc,GACf,MAAM,YAAY,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,34 @@
1
+ /**
2
+ * @procwire-bun/client - Child-side API for Procwire IPC (Bun.js optimized).
3
+ *
4
+ * This package provides the client-side implementation for child processes
5
+ * to communicate with the parent process using Procwire's binary protocol.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * import { Client } from '@procwire-bun/client';
10
+ *
11
+ * const client = new Client()
12
+ * .handle('query', async (data, ctx) => {
13
+ * const results = await search(data);
14
+ * ctx.respond(results);
15
+ * })
16
+ * .handle('insert', async (data, ctx) => {
17
+ * ctx.ack({ accepted: true });
18
+ * await processInBackground(data);
19
+ * })
20
+ * .event('progress');
21
+ *
22
+ * await client.start();
23
+ *
24
+ * // Emit events to parent
25
+ * client.emitEvent('progress', { percent: 50 });
26
+ * ```
27
+ *
28
+ * @module
29
+ */
30
+ export { Client } from "./client.js";
31
+ export { RequestContextImpl } from "./request-context.js";
32
+ export { BunDrainWaiter } from "./drain-waiter.js";
33
+ export { ProcwireClientError, ClientErrors } from "./errors.js";
34
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * RequestContext implementation for method handlers.
3
+ *
4
+ * This is the Bun.js optimized version using Bun socket API.
5
+ *
6
+ * @module
7
+ */
8
+ import type { Codec } from "@procwire/codecs";
9
+ import type { RequestContext } from "./types.js";
10
+ import type { BunDrainWaiter } from "./drain-waiter.js";
11
+ type BunSocket = Awaited<ReturnType<typeof Bun.connect>>;
12
+ /**
13
+ * Internal implementation of RequestContext.
14
+ *
15
+ * Passed to method handlers to allow sending responses.
16
+ * All response methods are async to properly handle socket backpressure.
17
+ */
18
+ export declare class RequestContextImpl implements RequestContext {
19
+ readonly requestId: number;
20
+ readonly method: string;
21
+ private readonly _methodId;
22
+ private readonly _codec;
23
+ private readonly _socket;
24
+ private readonly _abortCallbacks;
25
+ private readonly _acquireHeader;
26
+ private readonly _drainWaiter;
27
+ private _aborted;
28
+ private _responded;
29
+ constructor(requestId: number, method: string, _methodId: number, _codec: Codec, _socket: BunSocket, _abortCallbacks: Map<number, Set<() => void>>, _acquireHeader: () => Buffer, _drainWaiter: BunDrainWaiter);
30
+ get aborted(): boolean;
31
+ /**
32
+ * Whether a response has been sent.
33
+ * @internal
34
+ */
35
+ get responded(): boolean;
36
+ onAbort(callback: () => void): void;
37
+ respond(data: unknown): Promise<void>;
38
+ ack(data?: unknown): Promise<void>;
39
+ chunk(data: unknown): Promise<void>;
40
+ end(): Promise<void>;
41
+ error(err: Error | string): Promise<void>;
42
+ /**
43
+ * Mark context as aborted.
44
+ * @internal Called by Client when abort frame received.
45
+ */
46
+ _markAborted(): void;
47
+ private _ensureNotResponded;
48
+ /**
49
+ * Send response data with proper backpressure handling.
50
+ *
51
+ * Bun sockets don't have cork/uncork, so we concatenate buffers for atomic write.
52
+ */
53
+ private _sendResponse;
54
+ private _cleanup;
55
+ }
56
+ export {};
57
+ //# sourceMappingURL=request-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request-context.d.ts","sourceRoot":"","sources":["../src/request-context.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAGxD,KAAK,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,OAAO,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC;AAEzD;;;;;GAKG;AACH,qBAAa,kBAAmB,YAAW,cAAc;aAKrC,SAAS,EAAE,MAAM;aACjB,MAAM,EAAE,MAAM;IAC9B,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,eAAe;IAChC,OAAO,CAAC,QAAQ,CAAC,cAAc;IAC/B,OAAO,CAAC,QAAQ,CAAC,YAAY;IAX/B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,UAAU,CAAS;gBAGT,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,KAAK,EACb,OAAO,EAAE,SAAS,EAClB,eAAe,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,IAAI,CAAC,CAAC,EAC7C,cAAc,EAAE,MAAM,MAAM,EAC5B,YAAY,EAAE,cAAc;IAG/C,IAAI,OAAO,IAAI,OAAO,CAErB;IAED;;;OAGG;IACH,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED,OAAO,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,IAAI;IAS7B,OAAO,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAOrC,GAAG,CAAC,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAUlC,KAAK,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAInC,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;IAUpB,KAAK,CAAC,GAAG,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAW/C;;;OAGG;IACH,YAAY,IAAI,IAAI;IAIpB,OAAO,CAAC,mBAAmB;IAM3B;;;;OAIG;YACW,aAAa;IA4B3B,OAAO,CAAC,QAAQ;CAGjB"}
@@ -0,0 +1,128 @@
1
+ /**
2
+ * RequestContext implementation for method handlers.
3
+ *
4
+ * This is the Bun.js optimized version using Bun socket API.
5
+ *
6
+ * @module
7
+ */
8
+ import { Flags, encodeHeaderInto } from "@procwire/protocol";
9
+ import { ClientErrors } from "./errors.js";
10
+ /**
11
+ * Internal implementation of RequestContext.
12
+ *
13
+ * Passed to method handlers to allow sending responses.
14
+ * All response methods are async to properly handle socket backpressure.
15
+ */
16
+ export class RequestContextImpl {
17
+ requestId;
18
+ method;
19
+ _methodId;
20
+ _codec;
21
+ _socket;
22
+ _abortCallbacks;
23
+ _acquireHeader;
24
+ _drainWaiter;
25
+ _aborted = false;
26
+ _responded = false;
27
+ constructor(requestId, method, _methodId, _codec, _socket, _abortCallbacks, _acquireHeader, _drainWaiter) {
28
+ this.requestId = requestId;
29
+ this.method = method;
30
+ this._methodId = _methodId;
31
+ this._codec = _codec;
32
+ this._socket = _socket;
33
+ this._abortCallbacks = _abortCallbacks;
34
+ this._acquireHeader = _acquireHeader;
35
+ this._drainWaiter = _drainWaiter;
36
+ }
37
+ get aborted() {
38
+ return this._aborted;
39
+ }
40
+ /**
41
+ * Whether a response has been sent.
42
+ * @internal
43
+ */
44
+ get responded() {
45
+ return this._responded;
46
+ }
47
+ onAbort(callback) {
48
+ let callbacks = this._abortCallbacks.get(this.requestId);
49
+ if (!callbacks) {
50
+ callbacks = new Set();
51
+ this._abortCallbacks.set(this.requestId, callbacks);
52
+ }
53
+ callbacks.add(callback);
54
+ }
55
+ async respond(data) {
56
+ this._ensureNotResponded();
57
+ this._responded = true;
58
+ await this._sendResponse(data, Flags.IS_RESPONSE | Flags.DIRECTION_TO_PARENT);
59
+ this._cleanup();
60
+ }
61
+ async ack(data) {
62
+ this._ensureNotResponded();
63
+ this._responded = true;
64
+ await this._sendResponse(data ?? null, Flags.IS_RESPONSE | Flags.IS_ACK | Flags.DIRECTION_TO_PARENT);
65
+ this._cleanup();
66
+ }
67
+ async chunk(data) {
68
+ await this._sendResponse(data, Flags.IS_RESPONSE | Flags.IS_STREAM | Flags.DIRECTION_TO_PARENT);
69
+ }
70
+ async end() {
71
+ this._ensureNotResponded();
72
+ this._responded = true;
73
+ await this._sendResponse(null, Flags.IS_RESPONSE | Flags.IS_STREAM | Flags.STREAM_END | Flags.DIRECTION_TO_PARENT);
74
+ this._cleanup();
75
+ }
76
+ async error(err) {
77
+ this._ensureNotResponded();
78
+ this._responded = true;
79
+ const message = err instanceof Error ? err.message : err;
80
+ await this._sendResponse(message, Flags.IS_RESPONSE | Flags.IS_ERROR | Flags.DIRECTION_TO_PARENT);
81
+ this._cleanup();
82
+ }
83
+ /**
84
+ * Mark context as aborted.
85
+ * @internal Called by Client when abort frame received.
86
+ */
87
+ _markAborted() {
88
+ this._aborted = true;
89
+ }
90
+ _ensureNotResponded() {
91
+ if (this._responded) {
92
+ throw ClientErrors.responseAlreadySent();
93
+ }
94
+ }
95
+ /**
96
+ * Send response data with proper backpressure handling.
97
+ *
98
+ * Bun sockets don't have cork/uncork, so we concatenate buffers for atomic write.
99
+ */
100
+ async _sendResponse(data, flags) {
101
+ // Empty payload cases:
102
+ // 1. STREAM_END frames (null data)
103
+ // 2. ACK without data (null/undefined data with IS_ACK flag)
104
+ // Don't serialize null - just use empty buffer (required for rawCodec compatibility)
105
+ const isStreamEnd = (flags & Flags.STREAM_END) !== 0;
106
+ const isEmptyAck = (flags & Flags.IS_ACK) !== 0 && data == null;
107
+ const payload = isStreamEnd || isEmptyAck ? Buffer.alloc(0) : this._codec.serialize(data);
108
+ const headerBuf = this._acquireHeader();
109
+ encodeHeaderInto(headerBuf, {
110
+ methodId: this._methodId,
111
+ flags,
112
+ requestId: this.requestId,
113
+ payloadLength: payload.length,
114
+ });
115
+ // Bun doesn't have cork/uncork, concatenate for atomic write
116
+ const combined = Buffer.concat([headerBuf, payload]);
117
+ const canContinue = this._socket.write(combined);
118
+ // OPT-04: Wait AFTER write if backpressure
119
+ if (!canContinue) {
120
+ this._drainWaiter.markNeedsDrain();
121
+ await this._drainWaiter.waitForDrain();
122
+ }
123
+ }
124
+ _cleanup() {
125
+ this._abortCallbacks.delete(this.requestId);
126
+ }
127
+ }
128
+ //# sourceMappingURL=request-context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request-context.js","sourceRoot":"","sources":["../src/request-context.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Type definitions for @procwire-bun/client.
3
+ *
4
+ * @module
5
+ */
6
+ import type { Codec } from "@procwire/codecs";
7
+ /**
8
+ * Response type for methods.
9
+ */
10
+ export type ResponseType = "result" | "stream" | "ack" | "none";
11
+ /**
12
+ * Method definition for registration.
13
+ */
14
+ export interface MethodDefinition {
15
+ /** Expected response type */
16
+ response: ResponseType;
17
+ /** Codec for serialization (defaults to msgpack) */
18
+ codec?: Codec;
19
+ /** Can be cancelled via AbortSignal? */
20
+ cancellable?: boolean;
21
+ }
22
+ /**
23
+ * Event definition for registration.
24
+ */
25
+ export interface EventDefinition {
26
+ /** Codec for serialization (defaults to msgpack) */
27
+ codec?: Codec;
28
+ }
29
+ /**
30
+ * Client configuration options.
31
+ */
32
+ export interface ClientOptions {
33
+ /** Default codec for methods and events */
34
+ defaultCodec?: Codec;
35
+ }
36
+ /**
37
+ * Method handler function type.
38
+ */
39
+ export type MethodHandler<TData = unknown> = (data: TData, ctx: RequestContext) => void | Promise<void>;
40
+ /**
41
+ * Request context passed to method handlers.
42
+ *
43
+ * Provides methods to send responses back to parent.
44
+ *
45
+ * All response methods are async to properly handle backpressure.
46
+ * Always await these methods to prevent deadlocks with large payloads.
47
+ */
48
+ export interface RequestContext {
49
+ /** Request ID for correlation */
50
+ readonly requestId: number;
51
+ /** Method name being handled */
52
+ readonly method: string;
53
+ /** Was request aborted by parent? */
54
+ readonly aborted: boolean;
55
+ /**
56
+ * Register callback to be called when request is aborted.
57
+ */
58
+ onAbort(callback: () => void): void;
59
+ /**
60
+ * Send full response to parent.
61
+ * Sets IS_RESPONSE flag.
62
+ *
63
+ * @returns Promise that resolves when the response has been written
64
+ * and socket buffer has drained (if backpressure occurred).
65
+ */
66
+ respond(data: unknown): Promise<void>;
67
+ /**
68
+ * Send acknowledgment to parent.
69
+ * Sets IS_RESPONSE | IS_ACK flags.
70
+ *
71
+ * @returns Promise that resolves when the response has been written
72
+ * and socket buffer has drained (if backpressure occurred).
73
+ */
74
+ ack(data?: unknown): Promise<void>;
75
+ /**
76
+ * Send stream chunk to parent.
77
+ * Sets IS_RESPONSE | IS_STREAM flags.
78
+ *
79
+ * @returns Promise that resolves when the chunk has been written
80
+ * and socket buffer has drained (if backpressure occurred).
81
+ */
82
+ chunk(data: unknown): Promise<void>;
83
+ /**
84
+ * End stream.
85
+ * Sets IS_RESPONSE | IS_STREAM | STREAM_END flags.
86
+ *
87
+ * @returns Promise that resolves when the end marker has been written
88
+ * and socket buffer has drained (if backpressure occurred).
89
+ */
90
+ end(): Promise<void>;
91
+ /**
92
+ * Send error response to parent.
93
+ * Sets IS_RESPONSE | IS_ERROR flags.
94
+ *
95
+ * @returns Promise that resolves when the error has been written
96
+ * and socket buffer has drained (if backpressure occurred).
97
+ */
98
+ error(err: Error | string): Promise<void>;
99
+ }
100
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAE9C;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,QAAQ,GAAG,KAAK,GAAG,MAAM,CAAC;AAEhE;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,6BAA6B;IAC7B,QAAQ,EAAE,YAAY,CAAC;IACvB,oDAAoD;IACpD,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,wCAAwC;IACxC,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,oDAAoD;IACpD,KAAK,CAAC,EAAE,KAAK,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,2CAA2C;IAC3C,YAAY,CAAC,EAAE,KAAK,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,MAAM,aAAa,CAAC,KAAK,GAAG,OAAO,IAAI,CAC3C,IAAI,EAAE,KAAK,EACX,GAAG,EAAE,cAAc,KAChB,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAE1B;;;;;;;GAOG;AACH,MAAM,WAAW,cAAc;IAC7B,iCAAiC;IACjC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAE3B,gCAAgC;IAChC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAExB,qCAAqC;IACrC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAE1B;;OAEG;IACH,OAAO,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,IAAI,CAAC;IAEpC;;;;;;OAMG;IACH,OAAO,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEtC;;;;;;OAMG;IACH,GAAG,CAAC,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEnC;;;;;;OAMG;IACH,KAAK,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEpC;;;;;;OAMG;IACH,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAErB;;;;;;OAMG;IACH,KAAK,CAAC,GAAG,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3C"}
package/dist/types.js ADDED
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Type definitions for @procwire-bun/client.
3
+ *
4
+ * @module
5
+ */
6
+ export {};
7
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "@procwire/bun-client",
3
+ "version": "1.0.0",
4
+ "description": "Child-side client for Procwire IPC (Bun.js optimized)",
5
+ "type": "module",
6
+ "sideEffects": false,
7
+ "license": "MIT",
8
+ "engines": {
9
+ "bun": ">=1.0"
10
+ },
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/index.d.ts",
14
+ "default": "./dist/index.js"
15
+ }
16
+ },
17
+ "main": "./dist/index.js",
18
+ "types": "./dist/index.d.ts",
19
+ "files": [
20
+ "dist"
21
+ ],
22
+ "scripts": {
23
+ "build": "tsc -p tsconfig.build.json",
24
+ "typecheck": "tsc -p tsconfig.json --noEmit",
25
+ "test": "bun test",
26
+ "clean": "rimraf dist \"*.tsbuildinfo\""
27
+ },
28
+ "dependencies": {
29
+ "@procwire/protocol": "workspace:*",
30
+ "@procwire/codecs": "workspace:*"
31
+ },
32
+ "devDependencies": {
33
+ "@types/bun": "latest",
34
+ "rimraf": "^6.0.1",
35
+ "typescript": "^5.9.3"
36
+ },
37
+ "keywords": [
38
+ "ipc",
39
+ "procwire",
40
+ "client",
41
+ "child-process",
42
+ "bun"
43
+ ]
44
+ }