@procwire/client 1.0.0 → 1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Sebastian Webdev
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,246 @@
1
+ # @procwire/client
2
+
3
+ Child-side API for Procwire IPC.
4
+
5
+ ## Highlights
6
+
7
+ - **Client** - Fluent builder for registering handlers
8
+ - **RequestContext** - `respond`, `ack`, `chunk`, `end`, `error`
9
+ - **Event emission** to parent process
10
+ - **Cancellation** via `ctx.aborted` and `ctx.onAbort()`
11
+ - **Async response methods** - backpressure-safe
12
+ - **~2.5 GB/s throughput** on named pipes
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ npm install @procwire/client
18
+ ```
19
+
20
+ **Requirements:** Node.js >= 22
21
+
22
+ **Dependencies:** `@procwire/protocol`, `@procwire/codecs`
23
+
24
+ ## Quick Start
25
+
26
+ ```typescript
27
+ import { Client } from "@procwire/client";
28
+
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
+ ## API Reference
47
+
48
+ ### Client
49
+
50
+ Fluent builder for registering method handlers and events.
51
+
52
+ ```typescript
53
+ const client = new Client(options?)
54
+ .handle(name, handler, definition?)
55
+ .event(name, definition?)
56
+ .start();
57
+ ```
58
+
59
+ #### Constructor Options
60
+
61
+ ```typescript
62
+ interface ClientOptions {
63
+ defaultCodec?: Codec; // Default codec for all methods/events
64
+ }
65
+ ```
66
+
67
+ #### `.handle(name, handler, definition?)`
68
+
69
+ Register a method handler.
70
+
71
+ ```typescript
72
+ client.handle("process", async (data, ctx) => {
73
+ // Handle request and send response
74
+ ctx.respond(result);
75
+ }, {
76
+ response: "result", // "result" | "stream" | "ack" | "none"
77
+ codec: msgpackCodec, // Optional, defaults to msgpack
78
+ cancellable: true, // Support AbortSignal from parent
79
+ });
80
+ ```
81
+
82
+ #### `.event(name, definition?)`
83
+
84
+ Register an event that can be emitted to parent.
85
+
86
+ ```typescript
87
+ client.event("progress", { codec: msgpackCodec });
88
+ ```
89
+
90
+ #### `.start()`
91
+
92
+ Start listening for requests from parent.
93
+
94
+ ```typescript
95
+ await client.start();
96
+ // Client is now ready to receive requests
97
+ ```
98
+
99
+ #### `.emitEvent(name, data)`
100
+
101
+ Emit an event to the parent process.
102
+
103
+ ```typescript
104
+ client.emitEvent("progress", { percent: 75 });
105
+ ```
106
+
107
+ ### RequestContext
108
+
109
+ Passed to method handlers to send responses back to parent.
110
+
111
+ ```typescript
112
+ interface RequestContext {
113
+ readonly requestId: number; // For correlation
114
+ readonly method: string; // Method being handled
115
+ readonly aborted: boolean; // Was request aborted?
116
+
117
+ onAbort(callback: () => void): void; // Abort callback
118
+
119
+ respond(data: unknown): Promise<void>; // Full response
120
+ ack(data?: unknown): Promise<void>; // Acknowledgment only
121
+ chunk(data: unknown): Promise<void>; // Stream chunk
122
+ end(): Promise<void>; // End stream
123
+ error(err: Error | string): Promise<void>; // Error response
124
+ }
125
+ ```
126
+
127
+ **Important:** All response methods are async to handle backpressure. Always `await` them.
128
+
129
+ ### Response Patterns
130
+
131
+ #### Single Response (`result`)
132
+
133
+ ```typescript
134
+ client.handle("query", async (data, ctx) => {
135
+ const result = await processQuery(data);
136
+ await ctx.respond(result);
137
+ }, { response: "result" });
138
+ ```
139
+
140
+ #### Streaming Response (`stream`)
141
+
142
+ ```typescript
143
+ client.handle("generate", async (data, ctx) => {
144
+ for (const item of generateItems(data)) {
145
+ await ctx.chunk(item);
146
+ }
147
+ await ctx.end();
148
+ }, { response: "stream" });
149
+ ```
150
+
151
+ #### Acknowledgment (`ack`)
152
+
153
+ ```typescript
154
+ client.handle("enqueue", async (data, ctx) => {
155
+ await ctx.ack({ queued: true, position: 42 });
156
+ // Continue processing after acknowledgment
157
+ await processInBackground(data);
158
+ }, { response: "ack" });
159
+ ```
160
+
161
+ #### Fire-and-Forget (`none`)
162
+
163
+ ```typescript
164
+ client.handle("log", (data, ctx) => {
165
+ logger.info(data);
166
+ // No response needed
167
+ }, { response: "none" });
168
+ ```
169
+
170
+ #### Error Response
171
+
172
+ ```typescript
173
+ client.handle("validate", async (data, ctx) => {
174
+ try {
175
+ const result = validate(data);
176
+ await ctx.respond(result);
177
+ } catch (e) {
178
+ await ctx.error(e);
179
+ }
180
+ });
181
+ ```
182
+
183
+ ### Cancellation
184
+
185
+ Handle request cancellation from parent.
186
+
187
+ ```typescript
188
+ client.handle("longTask", async (data, ctx) => {
189
+ const resources = await acquireResources();
190
+
191
+ // Register cleanup on abort
192
+ ctx.onAbort(() => {
193
+ resources.release();
194
+ });
195
+
196
+ // Check abort status periodically
197
+ for (const item of items) {
198
+ if (ctx.aborted) {
199
+ return; // Stop processing
200
+ }
201
+ await ctx.chunk(process(item));
202
+ }
203
+
204
+ await ctx.end();
205
+ }, { response: "stream", cancellable: true });
206
+ ```
207
+
208
+ ### Error Handling
209
+
210
+ ```typescript
211
+ import { ProcwireClientError, ClientErrors } from "@procwire/client";
212
+
213
+ // Error factories
214
+ ClientErrors.methodNotFound("unknown"); // Unknown method called
215
+ ClientErrors.handlerError("process", err); // Handler threw error
216
+ ClientErrors.alreadyStarted(); // start() called twice
217
+ ```
218
+
219
+ ## Architecture
220
+
221
+ ```
222
+ ┌─────────────────────────────────────────┐
223
+ │ Parent Process │
224
+ │ ┌───────────────────────────────────┐ │
225
+ │ │ Module (uses @procwire/core) │ │
226
+ │ │ - send(), stream(), onEvent() │ │
227
+ │ └───────────────┬───────────────────┘ │
228
+ └──────────────────┼──────────────────────┘
229
+
230
+ ┌──────────────┴──────────────┐
231
+ │ Control: stdio (JSON-RPC) │
232
+ │ Data: named pipe (BINARY) │
233
+ └──────────────┬──────────────┘
234
+
235
+ ┌──────────────────┼──────────────────────┐
236
+ │ Child Process │
237
+ │ ┌───────────────┴───────────────────┐ │
238
+ │ │ Client (uses @procwire/client) │ │
239
+ │ │ - handle(), event(), emitEvent() │ │
240
+ │ └───────────────────────────────────┘ │
241
+ └─────────────────────────────────────────┘
242
+ ```
243
+
244
+ ## License
245
+
246
+ MIT
package/dist/index.d.ts CHANGED
@@ -25,7 +25,7 @@
25
25
  * client.emitEvent('progress', { percent: 50 });
26
26
  * ```
27
27
  *
28
- * @module
28
+ * @module @procwire/client
29
29
  */
30
30
  export { Client } from "./client.js";
31
31
  export { RequestContextImpl } from "./request-context.js";
package/dist/index.js CHANGED
@@ -25,7 +25,7 @@
25
25
  * client.emitEvent('progress', { percent: 50 });
26
26
  * ```
27
27
  *
28
- * @module
28
+ * @module @procwire/client
29
29
  */
30
30
  export { Client } from "./client.js";
31
31
  export { RequestContextImpl } from "./request-context.js";
package/package.json CHANGED
@@ -1,9 +1,29 @@
1
1
  {
2
2
  "name": "@procwire/client",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Child-side client for Procwire IPC",
5
+ "keywords": [
6
+ "ipc",
7
+ "procwire",
8
+ "client",
9
+ "child-process"
10
+ ],
11
+ "author": "Sebastian Webdev",
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "git+https://github.com/SebastianWebdev/procwire.git",
15
+ "directory": "packages/client"
16
+ },
17
+ "bugs": {
18
+ "url": "https://github.com/SebastianWebdev/procwire/issues"
19
+ },
20
+ "homepage": "https://www.procwire.dev",
5
21
  "type": "module",
6
22
  "sideEffects": false,
23
+ "license": "MIT",
24
+ "engines": {
25
+ "node": ">=22"
26
+ },
7
27
  "exports": {
8
28
  ".": {
9
29
  "types": "./dist/index.d.ts",
@@ -13,26 +33,28 @@
13
33
  "main": "./dist/index.js",
14
34
  "types": "./dist/index.d.ts",
15
35
  "files": [
16
- "dist"
36
+ "dist",
37
+ "README.md",
38
+ "LICENSE"
17
39
  ],
18
- "scripts": {
19
- "build": "tsc -p tsconfig.build.json",
20
- "typecheck": "tsc -p tsconfig.json --noEmit",
21
- "test": "vitest run",
22
- "clean": "rm -rf dist"
40
+ "publishConfig": {
41
+ "access": "public",
42
+ "provenance": true
23
43
  },
24
44
  "dependencies": {
25
- "@procwire/protocol": "workspace:*",
26
- "@procwire/codecs": "workspace:*"
45
+ "@procwire/protocol": "1.0.1",
46
+ "@procwire/codecs": "1.0.1"
27
47
  },
28
48
  "devDependencies": {
49
+ "@types/node": "^22.0.0",
50
+ "rimraf": "^6.0.1",
51
+ "typescript": "^5.9.3",
29
52
  "vitest": "^2.1.9"
30
53
  },
31
- "keywords": [
32
- "ipc",
33
- "procwire",
34
- "client",
35
- "child-process"
36
- ],
37
- "license": "MIT"
38
- }
54
+ "scripts": {
55
+ "clean": "rimraf dist \"*.tsbuildinfo\"",
56
+ "typecheck": "tsc -p tsconfig.json --noEmit",
57
+ "build": "tsc -p tsconfig.build.json",
58
+ "test": "vitest run"
59
+ }
60
+ }