@yuhere/js-jsonrpc2 0.0.0-stage → 2.1.2

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.
Files changed (174) hide show
  1. package/README.md +277 -2
  2. package/SKILL.md +237 -0
  3. package/dist/cli/cli.d.ts +3 -0
  4. package/dist/cli/cli.d.ts.map +1 -0
  5. package/dist/cli/cli.js +132 -0
  6. package/dist/cli/cli.js.map +1 -0
  7. package/dist/cli/demo-methods.d.ts +10 -0
  8. package/dist/cli/demo-methods.d.ts.map +1 -0
  9. package/dist/cli/demo-methods.js +477 -0
  10. package/dist/cli/demo-methods.js.map +1 -0
  11. package/dist/cli/express.d.ts +2 -0
  12. package/dist/cli/express.d.ts.map +1 -0
  13. package/dist/cli/express.js +42 -0
  14. package/dist/cli/express.js.map +1 -0
  15. package/dist/cli/stdio.d.ts +2 -0
  16. package/dist/cli/stdio.d.ts.map +1 -0
  17. package/dist/cli/stdio.js +5 -0
  18. package/dist/cli/stdio.js.map +1 -0
  19. package/dist/core/Repository.d.ts +24 -0
  20. package/dist/core/Repository.d.ts.map +1 -0
  21. package/dist/core/Repository.js +153 -0
  22. package/dist/core/Repository.js.map +1 -0
  23. package/dist/core/convert.d.ts +4 -0
  24. package/dist/core/convert.d.ts.map +1 -0
  25. package/dist/core/convert.js +29 -0
  26. package/dist/core/convert.js.map +1 -0
  27. package/dist/core/descriptor.d.ts +27 -0
  28. package/dist/core/descriptor.d.ts.map +1 -0
  29. package/dist/core/descriptor.js +83 -0
  30. package/dist/core/descriptor.js.map +1 -0
  31. package/dist/core/index.d.ts +4 -0
  32. package/dist/core/index.d.ts.map +1 -0
  33. package/dist/core/index.js +3 -0
  34. package/dist/core/index.js.map +1 -0
  35. package/dist/core/permission.d.ts +4 -0
  36. package/dist/core/permission.d.ts.map +1 -0
  37. package/dist/core/permission.js +18 -0
  38. package/dist/core/permission.js.map +1 -0
  39. package/dist/core/spec-check.d.ts +3 -0
  40. package/dist/core/spec-check.d.ts.map +1 -0
  41. package/dist/core/spec-check.js +11 -0
  42. package/dist/core/spec-check.js.map +1 -0
  43. package/dist/core/system-methods.d.ts +3 -0
  44. package/dist/core/system-methods.d.ts.map +1 -0
  45. package/dist/core/system-methods.js +76 -0
  46. package/dist/core/system-methods.js.map +1 -0
  47. package/dist/errors/RpcError.d.ts +9 -0
  48. package/dist/errors/RpcError.d.ts.map +1 -0
  49. package/dist/errors/RpcError.js +23 -0
  50. package/dist/errors/RpcError.js.map +1 -0
  51. package/dist/errors/codes.d.ts +7 -0
  52. package/dist/errors/codes.d.ts.map +1 -0
  53. package/dist/errors/codes.js +7 -0
  54. package/dist/errors/codes.js.map +1 -0
  55. package/dist/errors/index.d.ts +3 -0
  56. package/dist/errors/index.d.ts.map +1 -0
  57. package/dist/errors/index.js +3 -0
  58. package/dist/errors/index.js.map +1 -0
  59. package/dist/index.d.ts +18 -0
  60. package/dist/index.d.ts.map +1 -0
  61. package/dist/index.js +17 -0
  62. package/dist/index.js.map +1 -0
  63. package/dist/schema/index.d.ts +4 -0
  64. package/dist/schema/index.d.ts.map +1 -0
  65. package/dist/schema/index.js +4 -0
  66. package/dist/schema/index.js.map +1 -0
  67. package/dist/schema/json/index.d.ts +19 -0
  68. package/dist/schema/json/index.d.ts.map +1 -0
  69. package/dist/schema/json/index.js +6 -0
  70. package/dist/schema/json/index.js.map +1 -0
  71. package/dist/schema/json/marker.d.ts +23 -0
  72. package/dist/schema/json/marker.d.ts.map +1 -0
  73. package/dist/schema/json/marker.js +27 -0
  74. package/dist/schema/json/marker.js.map +1 -0
  75. package/dist/schema/json/primitives.d.ts +202 -0
  76. package/dist/schema/json/primitives.d.ts.map +1 -0
  77. package/dist/schema/json/primitives.js +598 -0
  78. package/dist/schema/json/primitives.js.map +1 -0
  79. package/dist/schema/primitives.d.ts +25 -0
  80. package/dist/schema/primitives.d.ts.map +1 -0
  81. package/dist/schema/primitives.js +153 -0
  82. package/dist/schema/primitives.js.map +1 -0
  83. package/dist/schema/rpc.d.ts +21 -0
  84. package/dist/schema/rpc.d.ts.map +1 -0
  85. package/dist/schema/rpc.js +17 -0
  86. package/dist/schema/rpc.js.map +1 -0
  87. package/dist/schema/z/protocol.d.ts +9 -0
  88. package/dist/schema/z/protocol.d.ts.map +1 -0
  89. package/dist/schema/z/protocol.js +9 -0
  90. package/dist/schema/z/protocol.js.map +1 -0
  91. package/dist/schema/z/register.d.ts +9 -0
  92. package/dist/schema/z/register.d.ts.map +1 -0
  93. package/dist/schema/z/register.js +21 -0
  94. package/dist/schema/z/register.js.map +1 -0
  95. package/dist/stream/index.d.ts +3 -0
  96. package/dist/stream/index.d.ts.map +1 -0
  97. package/dist/stream/index.js +2 -0
  98. package/dist/stream/index.js.map +1 -0
  99. package/dist/stream/stringify-stream.d.ts +15 -0
  100. package/dist/stream/stringify-stream.d.ts.map +1 -0
  101. package/dist/stream/stringify-stream.js +322 -0
  102. package/dist/stream/stringify-stream.js.map +1 -0
  103. package/dist/stream/stringify.d.ts +19 -0
  104. package/dist/stream/stringify.d.ts.map +1 -0
  105. package/dist/stream/stringify.js +217 -0
  106. package/dist/stream/stringify.js.map +1 -0
  107. package/dist/transport/express/body.d.ts +9 -0
  108. package/dist/transport/express/body.d.ts.map +1 -0
  109. package/dist/transport/express/body.js +28 -0
  110. package/dist/transport/express/body.js.map +1 -0
  111. package/dist/transport/express/index.d.ts +3 -0
  112. package/dist/transport/express/index.d.ts.map +1 -0
  113. package/dist/transport/express/index.js +2 -0
  114. package/dist/transport/express/index.js.map +1 -0
  115. package/dist/transport/express/middleware.d.ts +12 -0
  116. package/dist/transport/express/middleware.d.ts.map +1 -0
  117. package/dist/transport/express/middleware.js +73 -0
  118. package/dist/transport/express/middleware.js.map +1 -0
  119. package/dist/transport/express/respond.d.ts +4 -0
  120. package/dist/transport/express/respond.d.ts.map +1 -0
  121. package/dist/transport/express/respond.js +125 -0
  122. package/dist/transport/express/respond.js.map +1 -0
  123. package/dist/transport/stdio/index.d.ts +2 -0
  124. package/dist/transport/stdio/index.d.ts.map +1 -0
  125. package/dist/transport/stdio/index.js +2 -0
  126. package/dist/transport/stdio/index.js.map +1 -0
  127. package/dist/transport/stdio/middleware.d.ts +34 -0
  128. package/dist/transport/stdio/middleware.d.ts.map +1 -0
  129. package/dist/transport/stdio/middleware.js +274 -0
  130. package/dist/transport/stdio/middleware.js.map +1 -0
  131. package/dist/transport/ws/index.d.ts +3 -0
  132. package/dist/transport/ws/index.d.ts.map +1 -0
  133. package/dist/transport/ws/index.js +3 -0
  134. package/dist/transport/ws/index.js.map +1 -0
  135. package/dist/transport/ws/middleware.d.ts +25 -0
  136. package/dist/transport/ws/middleware.d.ts.map +1 -0
  137. package/dist/transport/ws/middleware.js +209 -0
  138. package/dist/transport/ws/middleware.js.map +1 -0
  139. package/dist/transport/ws/standalone.d.ts +22 -0
  140. package/dist/transport/ws/standalone.d.ts.map +1 -0
  141. package/dist/transport/ws/standalone.js +39 -0
  142. package/dist/transport/ws/standalone.js.map +1 -0
  143. package/dist/types.d.ts +75 -0
  144. package/dist/types.d.ts.map +1 -0
  145. package/dist/types.js +2 -0
  146. package/dist/types.js.map +1 -0
  147. package/htdoc/assets/index-CQr-lnlD.js +9286 -0
  148. package/htdoc/assets/index-CQr-lnlD.js.map +1 -0
  149. package/htdoc/assets/index-hcP2-9jw.css +729 -0
  150. package/htdoc/index.html +13 -0
  151. package/package.json +89 -3
  152. package/webui/index.html +12 -0
  153. package/webui/src/App.tsx +56 -0
  154. package/webui/src/api/httpTransport.ts +148 -0
  155. package/webui/src/api/paramForm.ts +79 -0
  156. package/webui/src/api/rpc.ts +57 -0
  157. package/webui/src/api/schema.ts +12 -0
  158. package/webui/src/api/types.ts +16 -0
  159. package/webui/src/api/wsTransport.ts +305 -0
  160. package/webui/src/components/MethodDetail.tsx +43 -0
  161. package/webui/src/components/MethodTree.tsx +118 -0
  162. package/webui/src/components/SchemaForm.tsx +696 -0
  163. package/webui/src/components/SchemaView.tsx +151 -0
  164. package/webui/src/components/TestPanel.tsx +279 -0
  165. package/webui/src/components/Toolbar.tsx +70 -0
  166. package/webui/src/hooks/useMethodDetail.ts +61 -0
  167. package/webui/src/hooks/useMethods.ts +27 -0
  168. package/webui/src/main.tsx +13 -0
  169. package/webui/src/styles/geek.css +729 -0
  170. package/webui/src/theme/ThemeProvider.tsx +79 -0
  171. package/webui/src/vite-env.d.ts +1 -0
  172. package/webui/tsconfig.json +34 -0
  173. package/webui/tsconfig.node.json +13 -0
  174. package/webui/vite.config.ts +15 -0
package/README.md CHANGED
@@ -1,3 +1,278 @@
1
- # Temporary Holding Version
1
+ # @yuhere/js-jsonrpc2
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ A TypeScript JSON-RPC 2.0 server library with schema validation, authorization, dependency injection, and multiple transports.
4
+
5
+ ## Features
6
+
7
+ ### Deviations from the JSON-RPC 2.0 spec
8
+
9
+ - **No named parameters.** `params` must be a positional array (or `null`).
10
+ - **No notifications.** Every request must carry an `id`.
11
+ - **No batch requests.** A batch payload is rejected with `INVALID_REQUEST`.
12
+
13
+ ### Extensions
14
+
15
+ - **Permission checks.** Declare `grantTo` on a method and provide a `perm_check`
16
+ injectable; calls without permission fail with `PERMISSION_DENIED (-32604)`.
17
+ - **`system.*` methods.** `system.listMethods`, `system.methodSignature`,
18
+ `system.methodHelp` are registered automatically.
19
+ - **Global error transform.** Map arbitrary thrown errors to `RPCError` via the
20
+ `errorTransform` option.
21
+ - **Streaming JSON serializer.** Responses are serialized with a streaming
22
+ stringifier that supports `Promise`, `Generator` / `AsyncGenerator`, and
23
+ `Buffer` (internal; not exported from the package root).
24
+
25
+ ## Install
26
+
27
+ ```shell
28
+ npm install @yuhere/js-jsonrpc2
29
+ ```
30
+
31
+ `express` is a peer dependency (v4 or v5). Node.js 18+ is required.
32
+
33
+ ## Quick start
34
+
35
+ ```ts
36
+ import express from 'express';
37
+ import { createRepository, createExpressRouter, s } from '@yuhere/js-jsonrpc2';
38
+
39
+ const repository = createRepository({ withSystemMethods: true });
40
+
41
+ repository.register(
42
+ {
43
+ namespace: 'add',
44
+ document: 'addition of 2 numbers',
45
+ signature: [s.number(), s.number(), s.number()],
46
+ },
47
+ (a: number, b: number) => a + b,
48
+ );
49
+
50
+ const app = express();
51
+ app.use('/', createExpressRouter(repository, {}));
52
+ app.listen(5000);
53
+ ```
54
+
55
+ Calling it:
56
+
57
+ ```shell
58
+ curl -s http://localhost:5000/ -H 'Content-Type: application/json' \
59
+ -d '{"jsonrpc":"2.0","method":"add","params":[1,2],"id":1}'
60
+ # => {"jsonrpc":"2.0","id":1,"result":3}
61
+ ```
62
+
63
+ ## Registering methods
64
+
65
+ `Repository.register(descriptor, handler)` takes a descriptor and a handler.
66
+ The descriptor has three fields:
67
+
68
+ | Field | Type | Meaning |
69
+ | --- | --- | --- |
70
+ | `namespace` | `string` | Method name (non-empty). |
71
+ | `document` | `string \| (d) => string \| Promise<string>` | Optional help text for `system.methodHelp`. |
72
+ | `grantTo` | `string[]` | Optional list of permissions required to call the method. |
73
+ | `signature` | `[returnSchema, ...parameterSchema[]]` | Return type first, then parameters. |
74
+
75
+ `signature[0]` is the return schema (`s.Schema` or `s.void()`).
76
+ Every later slot is either a `s.Schema` (client-supplied) or
77
+ `s.injectable(key)` (server-injected). Injectable slots may appear at any
78
+ position except index `0`.
79
+
80
+ ```ts
81
+ import { s } from '@yuhere/js-jsonrpc2';
82
+
83
+ repository.register(
84
+ {
85
+ namespace: 'whoami',
86
+ document: 'returns the caller user agent',
87
+ signature: [
88
+ s.object({ userAgent: s.string().nullable() }),
89
+ s.injectable('request'),
90
+ ],
91
+ },
92
+ (req: { headers: Record<string, string> }) => ({
93
+ userAgent: req.headers['user-agent'] ?? null,
94
+ }),
95
+ );
96
+ ```
97
+
98
+ ## Schemas
99
+
100
+ Schemas are built with the `s` factory. They validate at runtime and derive
101
+ TypeScript types.
102
+
103
+ ```ts
104
+ s.string() // string
105
+ s.string().min(1).max(64) // length-constrained string
106
+ s.number() // finite number
107
+ s.boolean() // boolean
108
+ s.literal('2.0') // exact literal
109
+ s.array(s.string()) // string[]
110
+ s.object({ id: s.number() }) // open object
111
+ s.object({ id: s.number() }).strict() // all props required, no extras
112
+ s.record(s.number()) // Record<string, number>
113
+ s.union([s.string(), s.number()])
114
+ s.tuple([s.string(), s.number()])
115
+ s.enum(['pending', 'success', 'failed'])
116
+ s.unknown() // any JSON value
117
+ s.void() // RPC-only: no return value
118
+ s.injectable('request') // RPC-only: server-injected parameter
119
+ ```
120
+
121
+ Chainable modifiers: `.min()`, `.max()`, `.nullable()`, `.strict()`,
122
+ `.describe(text)`. Validation: `.parse(input)` (throws
123
+ `SchemaValidationError`) and `.safeParse(input)` (returns a result object).
124
+ Introspection: `.toJSON()` returns a `SchemaDescription`.
125
+
126
+ ## Transports
127
+
128
+ ### Express
129
+
130
+ ```ts
131
+ import { createExpressRouter } from '@yuhere/js-jsonrpc2';
132
+
133
+ app.use('/', createExpressRouter(repository, {
134
+ bodyLimit: '1mb',
135
+ useHtdoc: true,
136
+ mkInjectable: (req, res, repo) => ({ request: req, response: res, repository: repo }),
137
+ errorTransform: (error) => /* return RPCError | undefined */ undefined,
138
+ }));
139
+ ```
140
+
141
+ The router handles `POST /` only. `useHtdoc` serves the bundled web UI
142
+ (static assets under `htdoc/`).
143
+
144
+ ### WebSocket
145
+
146
+ Attach to an existing HTTP server:
147
+
148
+ ```ts
149
+ import { createServer } from 'node:http';
150
+ import { attachWebSocket } from '@yuhere/js-jsonrpc2';
151
+
152
+ const server = createServer(app);
153
+ attachWebSocket(server, repository, {
154
+ path: '/',
155
+ mkInjectable: (ws, req, repo) => ({ socket: ws, request: req, repository: repo }),
156
+ });
157
+ server.listen(5000);
158
+ ```
159
+
160
+ Or create a standalone server:
161
+
162
+ ```ts
163
+ import { createWebSocketServer } from '@yuhere/js-jsonrpc2';
164
+
165
+ const { server, wss, close } = await createWebSocketServer({
166
+ port: 8080,
167
+ host: '127.0.0.1',
168
+ });
169
+ ```
170
+
171
+ ### stdio (NDJSON)
172
+
173
+ ```ts
174
+ import { serveStdio } from '@yuhere/js-jsonrpc2';
175
+
176
+ await serveStdio({ repository, framing: 'lines' });
177
+ ```
178
+
179
+ `framing: 'lines'` reads one JSON value per line; `framing: 'json'` accepts
180
+ concatenated JSON values.
181
+
182
+ ## Invoking directly
183
+
184
+ `Repository.invoke(input, options?)` returns an `RPCResponse`
185
+ (`RPCSuccess | RPCFailure`). Useful for tests or custom transports.
186
+
187
+ ```ts
188
+ const response = await repository.invoke(
189
+ { jsonrpc: '2.0', method: 'add', params: [1, 2], id: 1 },
190
+ { injections: { request: fakeReq } },
191
+ );
192
+ ```
193
+
194
+ ## Injectables
195
+
196
+ An injectable factory is passed to the transport as `mkInjectable`. The
197
+ resulting object is available to any signature slot declared as
198
+ `s.injectable(key)`. Built-in defaults:
199
+
200
+ | Transport | Default injectables |
201
+ | --- | --- |
202
+ | Express | `request`, `session`, `response`, `repository` |
203
+ | WebSocket | `socket`, `request`, `repository` |
204
+ | stdio | `repository`, `input`, `output` |
205
+
206
+ Values may be plain values or functions; functions are awaited before
207
+ injection.
208
+
209
+ ## Permission checks
210
+
211
+ When a descriptor declares `grantTo`, the injected `perm_check(grantTo,
212
+ descriptor)` is called:
213
+
214
+ - returns `true` → allowed
215
+ - returns an `RPCError` → re-thrown as-is
216
+ - anything else → `PERMISSION_DENIED (-32604)`
217
+
218
+ ```ts
219
+ app.use('/', createExpressRouter(repository, {
220
+ mkInjectable: (req, res, repo) => ({
221
+ request: req,
222
+ repository: repo,
223
+ perm_check: async (grantTo: string[]) => {
224
+ const roles = (req as any).session?.user?.roles ?? [];
225
+ return roles.some((r: string) => grantTo.includes(r));
226
+ },
227
+ }),
228
+ }));
229
+ ```
230
+
231
+ ## Error transform
232
+
233
+ `errorTransform(error)` may return an `RPCError` to override the default
234
+ `INTERNAL_ERROR` mapping. Return `undefined` to fall back to the default
235
+ behaviour. It is applied only to errors thrown by the method handler, not
236
+ by request validation.
237
+
238
+ ```ts
239
+ createExpressRouter(repository, {
240
+ errorTransform: (error) =>
241
+ error instanceof MyDomainError
242
+ ? new RPCError(-32000, 'Domain error', error.message)
243
+ : undefined,
244
+ });
245
+ ```
246
+
247
+ ## Built-in methods
248
+
249
+ | Method | Description |
250
+ | --- | --- |
251
+ | `system.listMethods` | Returns `string[]` of registered method names. |
252
+ | `system.methodSignature` | Returns the schema list for a method. |
253
+ | `system.methodHelp` | Returns the method's `document` (or `null`). |
254
+
255
+ Disable them with `createRepository({ withSystemMethods: false })`.
256
+
257
+ ## Error codes
258
+
259
+ | Code | Meaning |
260
+ | --- | --- |
261
+ | -32700 | Parse error |
262
+ | -32600 | Invalid Request |
263
+ | -32601 | Method not found |
264
+ | -32602 | Invalid params |
265
+ | -32603 | Internal error |
266
+ | -32604 | Permission denied |
267
+ | -32000..-32099 | Implementation-defined server errors |
268
+
269
+ `codes` is exported from the package root:
270
+
271
+ ```ts
272
+ import { codes } from '@yuhere/js-jsonrpc2';
273
+ codes.INVALID_PARAMS; // -32602
274
+ ```
275
+
276
+ ## License
277
+
278
+ MIT
package/SKILL.md ADDED
@@ -0,0 +1,237 @@
1
+ ---
2
+ name: js-jsonrpc2
3
+ description: Use when building or modifying a JSON-RPC 2.0 server with @yuhere/js-jsonrpc2 — registering methods with schema signatures, wiring the Express/WebSocket/stdio transport, streaming returns, or debugging params, permissions, and error mapping.
4
+ ---
5
+
6
+ # @yuhere/js-jsonrpc2
7
+
8
+ JSON-RPC 2.0 server middleware. Package name `@yuhere/js-jsonrpc2`. Node 18+.
9
+
10
+ Hard constraints (by design, do not work around):
11
+
12
+ - No named params. `params` MUST be a positional array or `null`.
13
+ - No notifications. Every request MUST carry an `id`.
14
+ - No batch. A batch payload returns `-32600 Invalid Request`.
15
+ - No request-side streaming. Only method **return values** can be streamed.
16
+
17
+ ## Register a method
18
+
19
+ 1. Create the repository: `createRepository({ withSystemMethods: true })`.
20
+ Omit the option to get the same default. Pass `false` only to drop `system.*`.
21
+ 2. Call `repository.register(descriptor, handler)`.
22
+ 3. Set `descriptor.namespace` to a non-empty string.
23
+ 4. Set `descriptor.signature` to `[returnSchema, ...parameterSchema[]]`.
24
+ - `signature[0]` MUST be `s.Schema`, `s.void()`, or `s.stream(inner)`.
25
+ Never an injectable.
26
+ - Each later slot MUST be `s.Schema` (client param) or `s.injectable(key)`.
27
+ 5. Write `handler` so its positional args match the non-injectable slots in order;
28
+ injectable slots are filled at their declared positions.
29
+ 6. Use `s.*` factories for schemas: `s.string()`, `s.number()`, `s.boolean()`,
30
+ `s.literal(v)`, `s.array(item)`, `s.object(shape)`, `s.record(v)`,
31
+ `s.union([...])`, `s.tuple([...])`, `s.enum([...])`, `s.unknown()`.
32
+ 7. Chain modifiers before registering: `.min()`, `.max()`, `.nullable()`,
33
+ `.strict()`, `.describe(text)`.
34
+
35
+ ```ts
36
+ import { createRepository, s } from '@yuhere/js-jsonrpc2';
37
+
38
+ const repository = createRepository();
39
+ repository.register(
40
+ { namespace: 'add', document: 'a + b', signature: [s.number(), s.number(), s.number()] },
41
+ (a: number, b: number) => a + b,
42
+ );
43
+ ```
44
+
45
+ ## Stream a return value
46
+
47
+ Declare `s.stream(inner)` as `signature[0]`. The handler MUST return an
48
+ `AsyncGenerator`; anything else fails with `INTERNAL_ERROR` before any chunk is
49
+ sent.
50
+
51
+ 1. Set `signature[0]` to `s.stream(inner)`. `inner` is any `s.Schema`
52
+ (including `s.unknown()`). No stream-of-stream, and `s.stream()` is not
53
+ valid in a parameter slot.
54
+ 2. Make the handler an `async function*` that yields values matching `inner`.
55
+ 3. Do NOT rely on `inner` for runtime chunk validation — chunks are **not**
56
+ validated. `inner` is documentation only; the WebUI uses it to render types.
57
+ 4. Expect frames of shape `RPCStreamChunk`, each a **complete** JSON-RPC-shaped
58
+ object (not nested under `result`):
59
+ `{ jsonrpc, id, done, index?, value?, error? }`.
60
+ - Normal data frame: `{ done: false, index, value }`.
61
+ - Normal end: `{ done: true, index }`.
62
+ - Mid-stream throw: `{ done: true, index?, error: { code, message } }`.
63
+ `id` is always the request id (never `null` for streams).
64
+ 5. The server yields exactly one terminal frame (`done: true`) per stream,
65
+ whether it finished normally or threw. A transport that receives the
66
+ terminal frame ends the response cleanly; it does not treat a terminal
67
+ `error` frame as a transport failure.
68
+ 6. Put cleanup in a `finally` inside the generator; the server calls
69
+ `source.return()` when the client disconnects, so `finally` runs.
70
+
71
+ ```ts
72
+ repository.register(
73
+ {
74
+ namespace: 'logs.tail',
75
+ document: 'stream log lines',
76
+ signature: [s.stream(s.string()), s.string()],
77
+ },
78
+ async function* tail(path: string) {
79
+ for await (const line of follow(path)) yield line;
80
+ },
81
+ );
82
+ ```
83
+
84
+ Reference implementations for every stream shape (finite, infinite, throwing,
85
+ empty, with injectables, `s.unknown()`) live in `dev/express.ts` under the
86
+ `stream.*` namespace.
87
+
88
+ ## Wire a transport
89
+
90
+ Pick exactly one transport per server process.
91
+
92
+ - **Express**: call `app.use('/', createExpressRouter(repository, opts))`.
93
+ The router only handles `POST /`. Options: `mkInjectable`, `errorTransform`,
94
+ `bodyLimit`, `useHtdoc`, `streamFormat`.
95
+ - **WebSocket on existing server**: call `attachWebSocket(server, repository, opts)`.
96
+ Options: `path`, `mkInjectable`, `errorTransform`, `allowOrigin`, `maxPayload`,
97
+ `onConnection`.
98
+ - **Standalone WebSocket**: `await createWebSocketServer({ port, host })`;
99
+ it returns `{ server, wss, close() }`.
100
+ - **stdio**: `await serveStdio({ repository, framing: 'lines' })`.
101
+ Use `framing: 'json'` for concatenated JSON values.
102
+
103
+ All transports support stream returns.
104
+
105
+ - Express emits one `RPCStreamChunk` per NDJSON line (`streamFormat: 'ndjson'`,
106
+ the default). `streamFormat: 'sse'` is reserved for `text/event-stream`;
107
+ currently the NDJSON path is the implemented one — do not depend on SSE
108
+ unless the transport source shows it.
109
+ - WS and stdio emit one `RPCStreamChunk` per message/line.
110
+ - Non-stream responses are serialized with a streaming JSON writer. If
111
+ serialization fails partway, the transport aborts the connection rather than
112
+ sending a truncated body with `200 OK`; if it fails before writing any byte,
113
+ the transport emits an `INTERNAL_ERROR` response instead.
114
+
115
+ Supply `mkInjectable` when the handler needs request/socket context. Defaults:
116
+
117
+ | Transport | Default injectables |
118
+ | --- | --- |
119
+ | Express | `request`, `session`, `response`, `repository` |
120
+ | WebSocket | `socket`, `request`, `repository` |
121
+ | stdio | `repository`, `input`, `output` |
122
+
123
+ Injectable values may be plain values or functions; functions are awaited.
124
+
125
+ ## Enforce permissions
126
+
127
+ 1. Add `grantTo: ['role', ...]` to the descriptor.
128
+ 2. Inject `perm_check(grantTo, descriptor)` via `mkInjectable`.
129
+ 3. Return `true` to allow, return an `RPCError` to re-throw it verbatim, or
130
+ return anything else to get `PERMISSION_DENIED (-32604)`.
131
+ 4. Do not rely on `grantTo` alone — without `perm_check`, the check is skipped.
132
+
133
+ ## Map domain errors
134
+
135
+ Call `errorTransform(error)` inside the transport options. Return an `RPCError`
136
+ to override `INTERNAL_ERROR`, return `undefined` to fall back to the default.
137
+ It applies only to errors thrown by the handler **before** streaming starts,
138
+ not to request validation. Errors thrown **after** the first chunk has been
139
+ sent cannot become `RPCFailure`; they surface as a `{ done: true, error }`
140
+ terminal chunk instead.
141
+
142
+ ```ts
143
+ createExpressRouter(repository, {
144
+ errorTransform: (e) => (e instanceof MyDomainError ? new RPCError(-32000, 'Domain', e.message) : undefined),
145
+ });
146
+ ```
147
+
148
+ ## Invoke directly
149
+
150
+ Call `repository.invoke(input, { injections?, errorTransform? })`. It returns
151
+ `RPCResponse` (`RPCSuccess | RPCStreamSuccess | RPCFailure`) and never throws
152
+ for protocol errors.
153
+
154
+ For streaming methods the result is `RPCStreamSuccess`:
155
+ `{ jsonrpc, id, chunks: AsyncGenerator<RPCStreamChunk> }`. It has no `result`
156
+ and no boolean `stream` flag — branch on the presence of `chunks`:
157
+
158
+ ```ts
159
+ const r = await repository.invoke(input);
160
+ if ('chunks' in r) for await (const frame of r.chunks) { /* frame is RPCStreamChunk */ }
161
+ else if ('error' in r) { /* RPCFailure */ }
162
+ else { /* RPCSuccess, r.result */ }
163
+ ```
164
+
165
+ Consume `chunks` yourself, or hand it to a transport. If a client disconnects
166
+ mid-stream, the transport calls `chunks.return()`, which triggers the handler's
167
+ `finally`.
168
+
169
+ ## Client (`webui`)
170
+
171
+ `RpcClient.call<T>(method, ...params)` returns
172
+ `CallResult<T> = Promise<T> | AsyncGenerator<T>`. It inspects the first
173
+ frame: a `done` field means stream, otherwise the resolved value is the
174
+ ordinary `result`. Use `isStreamResult(x)` to narrow.
175
+
176
+ - `const r = await rpc.call('add', 1, 2)` → `r` is `3`.
177
+ - `const r = await rpc.call('logs.tail', '/var/log/app.log')` → `r` is an
178
+ `AsyncGenerator`; `for await (const line of r) ...`.
179
+ - HTTP transport calls `reader.cancel()` only on early exit (`break` / throw /
180
+ bad first frame), so the connection is aborted and the server's
181
+ `res.on('close')` fires. A clean read lets the connection return to the pool;
182
+ it does not cancel.
183
+ - WS transport routes the first message via a one-shot waiter, then streams
184
+ subsequent frames per `id`.
185
+
186
+ ## Handle errors
187
+
188
+ Read error codes from the exported `codes` object. `RPCError` carries
189
+ `code`, `message`, `data`. Common codes: `-32700` parse, `-32600` invalid
190
+ request, `-32601` method not found, `-32602` invalid params, `-32603` internal,
191
+ `-32604` permission denied.
192
+
193
+ For streams, a mid-stream `RPCError` becomes a terminal chunk with
194
+ `error: { code, message }`; clients re-throw it as `RPCError`.
195
+
196
+ ## Use built-in methods
197
+
198
+ Keep `withSystemMethods` on (default) to expose `system.listMethods`,
199
+ `system.methodSignature`, `system.methodHelp`. Set `document` on a descriptor
200
+ so `system.methodHelp` returns useful text.
201
+
202
+ `system.methodSignature` reports a stream return as
203
+ `{ kind: 'stream', children: { inner: <schema> } }`.
204
+
205
+ ## Common mistakes
206
+
207
+ - **Passing named params or a bare object as `params`.** `convertParams` rejects
208
+ anything that is not an array or `null` with `INVALID_PARAMS`. Always send
209
+ `params: [...]`.
210
+ - **Omitting `id`.** `RPCRequestSchema` requires it; the request fails with
211
+ `INVALID_REQUEST`. Notifications are not supported.
212
+ - **Naming descriptor fields `sign` / `doc`.** The schema is strict; registration
213
+ throws `TypeError`. Use `signature` and `document`.
214
+ - **Putting `s.injectable()` at `signature[0]`.** `buildDescriptor` throws; the
215
+ first slot is the return type.
216
+ - **Forgetting `mkInjectable` for a `s.injectable(key)` slot.** `convertParams`
217
+ throws `INVALID_PARAMS` for the missing key.
218
+ - **Expecting `errorTransform` to catch validation errors.** It only wraps the
219
+ handler call.
220
+ - **Declaring `s.stream(inner)` but returning a plain value from the handler.**
221
+ The server throws `INTERNAL_ERROR` before sending any chunk.
222
+ - **Expecting chunks to be validated against `inner`.** They are not. `inner`
223
+ is documentation only.
224
+ - **Trying to stream `params`.** Not supported; only return values can stream.
225
+ - **Nesting `s.stream()` inside `s.stream()` or inside a parameter slot.**
226
+ `s.stream()` is only valid at `signature[0]`.
227
+ - **Forgetting `finally` in a streaming handler.** When the client disconnects
228
+ the server calls `source.return()`, so `finally` is the only place to close
229
+ DB cursors, files, etc.
230
+ - **Calling `rpc.call()` and assuming `Promise`.** For stream methods it
231
+ resolves to an `AsyncGenerator`. Branch with `isStreamResult(r)`.
232
+ - **Assuming stream frames are nested under `result` or carry a `stream: true`
233
+ flag.** Each frame is a complete `RPCStreamChunk` with `jsonrpc` and `id`.
234
+ Branch on `done` (and `error`), not on a wrapper field.
235
+ - **Treating a terminal `error` frame as a broken connection.** A mid-stream
236
+ throw is delivered as `{ done: true, error }`; the HTTP/WS/stdio response
237
+ itself ends cleanly. Only transport-level failures abort the connection.
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/cli/cli.ts"],"names":[],"mappings":""}
@@ -0,0 +1,132 @@
1
+ #!/usr/bin/env node
2
+ // src/cli.ts
3
+ //
4
+ // `jsonrpc2-demo` 命令入口:启动带完整 demo 方法的 HTTP + WebSocket 服务,
5
+ // 并自动打开 WebUI(默认 http://localhost:5000/)。
6
+ //
7
+ // 环境变量:
8
+ // PORT 监听端口(默认 5000;被占用时自动换随机端口)
9
+ // NO_OPEN 设为 1 时不自动打开浏览器
10
+ // HOST 监听地址(默认 0.0.0.0)
11
+ import { createServer } from 'node:http';
12
+ import { spawn } from 'node:child_process';
13
+ import { existsSync } from 'node:fs';
14
+ import path from 'node:path';
15
+ import { fileURLToPath } from 'node:url';
16
+ import express from 'express';
17
+ import { createRepository } from '../core/Repository';
18
+ import { installDemoMethods } from './demo-methods';
19
+ import { createExpressRouter } from '../transport/express';
20
+ import { attachWebSocket } from '../transport/ws';
21
+ const here = path.dirname(fileURLToPath(import.meta.url));
22
+ function htdocDir() {
23
+ // dist/cli.js → 包根/htdoc
24
+ return path.resolve(here, '..', 'htdoc');
25
+ }
26
+ function openBrowser(url) {
27
+ let cmd;
28
+ let args;
29
+ if (process.platform === 'darwin') {
30
+ cmd = 'open';
31
+ args = [url];
32
+ }
33
+ else if (process.platform === 'win32') {
34
+ // start 的第一个参数会被当作窗口标题,传空字符串占位。
35
+ cmd = 'cmd';
36
+ args = ['/c', 'start', '""', url];
37
+ }
38
+ else {
39
+ cmd = 'xdg-open';
40
+ args = [url];
41
+ }
42
+ try {
43
+ const child = spawn(cmd, args, { stdio: 'ignore', detached: true });
44
+ child.on('error', () => {
45
+ console.log(`(无法自动打开浏览器,请手动访问 ${url})`);
46
+ });
47
+ child.unref();
48
+ }
49
+ catch {
50
+ console.log(`(无法自动打开浏览器,请手动访问 ${url})`);
51
+ }
52
+ }
53
+ function listen(server, host, port) {
54
+ return new Promise((resolve, reject) => {
55
+ const onError = (err) => {
56
+ server.off('listening', onListening);
57
+ if (err.code === 'EADDRINUSE' && port !== 0) {
58
+ console.warn(`端口 ${port} 已被占用,改用随机端口重试。`);
59
+ server.once('error', reject);
60
+ server.once('listening', onListening);
61
+ server.listen(0, host);
62
+ return;
63
+ }
64
+ reject(err);
65
+ };
66
+ const onListening = () => {
67
+ server.off('error', onError);
68
+ const addr = server.address();
69
+ const actual = typeof addr === 'object' && addr ? addr.port : port;
70
+ resolve(actual);
71
+ };
72
+ server.once('error', onError);
73
+ server.once('listening', onListening);
74
+ server.listen(port, host);
75
+ });
76
+ }
77
+ async function main() {
78
+ const repository = createRepository({ withSystemMethods: true });
79
+ installDemoMethods(repository);
80
+ const app = express();
81
+ app.use('/', createExpressRouter(repository, { useHtdoc: true }));
82
+ const server = createServer(app);
83
+ attachWebSocket(server, repository, {
84
+ path: '/',
85
+ mkInjectable: (ws, req, repo) => ({
86
+ socket: ws,
87
+ request: req,
88
+ repository: repo,
89
+ requestId: `ws-${Math.random().toString(36).slice(2, 8)}`,
90
+ }),
91
+ });
92
+ const host = process.env['HOST'] ?? '0.0.0.0';
93
+ const requestedPort = Number(process.env['PORT'] ?? 5000);
94
+ const port = await listen(server, host, requestedPort);
95
+ const url = `http://localhost:${port}/`;
96
+ const wsUrl = `ws://localhost:${port}/`;
97
+ const hasWebUI = existsSync(path.join(htdocDir(), 'index.html'));
98
+ console.log('');
99
+ console.log(' js-jsonrpc2 demo');
100
+ console.log(' ────────────────────────────────────────');
101
+ console.log(` HTTP (POST /) ${url}`);
102
+ console.log(` WebSocket ${wsUrl}`);
103
+ if (hasWebUI) {
104
+ console.log(` WebUI ${url}`);
105
+ }
106
+ else {
107
+ console.log(' WebUI (未构建:请先运行 `npm run build:webui`)');
108
+ }
109
+ console.log('');
110
+ console.log(' 按 Ctrl+C 停止。');
111
+ console.log('');
112
+ if (hasWebUI && process.env['NO_OPEN'] !== '1') {
113
+ openBrowser(url);
114
+ }
115
+ let shuttingDown = false;
116
+ const shutdown = () => {
117
+ if (shuttingDown)
118
+ return;
119
+ shuttingDown = true;
120
+ console.log('\n正在关闭…');
121
+ server.close(() => process.exit(0));
122
+ // 兜底:3 秒内没关完就强退
123
+ setTimeout(() => process.exit(0), 3000).unref();
124
+ };
125
+ process.once('SIGINT', shutdown);
126
+ process.once('SIGTERM', shutdown);
127
+ }
128
+ main().catch((err) => {
129
+ console.error('启动失败:', err);
130
+ process.exit(1);
131
+ });
132
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/cli/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import type { Repository } from '../core/Repository';
2
+ /**
3
+ * 注册一整套用于演示/调试的 RPC 方法。
4
+ * 覆盖:基础类型、字面量/枚举、数组/元组、对象、Record、联合、
5
+ * nullable、unknown、注入参数、各种返回形态、深层嵌套、混合大杂烩、流式返回。
6
+ *
7
+ * 被 dev/express.ts 与 src/cli.ts 共用。
8
+ */
9
+ export declare function installDemoMethods(repository: Repository): void;
10
+ //# sourceMappingURL=demo-methods.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"demo-methods.d.ts","sourceRoot":"","sources":["../../src/cli/demo-methods.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,UAAU,GAAG,IAAI,CAimB/D"}