@yuhere/js-jsonrpc2 2.1.13 → 2.1.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +118 -9
- package/SKILL.md +95 -4
- package/dist/cli/demo-methods.js +77 -0
- package/dist/core/llm-tools.js +41 -42
- package/dist/schema/json/index.d.ts +1 -1
- package/dist/schema/json/primitives.d.ts +56 -72
- package/dist/schema/json/primitives.js +86 -79
- package/dist/schema/z/protocol.d.ts +1 -1
- package/htdoc/assets/{index-CqI7vRAS.js → index-WpdfQ_L9.js} +166 -94
- package/htdoc/assets/index-WpdfQ_L9.js.map +1 -0
- package/htdoc/index.html +1 -1
- package/package.json +1 -1
- package/htdoc/assets/index-CqI7vRAS.js.map +0 -1
package/README.md
CHANGED
|
@@ -72,7 +72,7 @@ The descriptor has three fields:
|
|
|
72
72
|
| `grantTo` | `string[]` | Optional list of permissions required to call the method. |
|
|
73
73
|
| `signature` | `[returnSchema, ...parameterSchema[]]` | Return type first, then parameters. |
|
|
74
74
|
|
|
75
|
-
`signature[0]` is the return schema (`s.Schema` or `s.
|
|
75
|
+
`signature[0]` is the return schema (`s.Schema`, `s.void()`, or `s.stream(inner)`).
|
|
76
76
|
Every later slot is either a `s.Schema` (client-supplied) or
|
|
77
77
|
`s.injectable(key)` (server-injected). Injectable slots may appear at any
|
|
78
78
|
position except index `0`.
|
|
@@ -97,8 +97,12 @@ repository.register(
|
|
|
97
97
|
|
|
98
98
|
## Schemas
|
|
99
99
|
|
|
100
|
-
Schemas
|
|
101
|
-
|
|
100
|
+
Schemas serve two roles:
|
|
101
|
+
|
|
102
|
+
1. **Description** — document the RPC signature (`toJSON` / `system.methodSignature` / WebUI).
|
|
103
|
+
2. **Validation** — check client parameters at invoke time (`parse` / `safeParse`, used by `convertParams`).
|
|
104
|
+
|
|
105
|
+
They are built with the `s` factory. They validate at runtime (unless turned off) and derive TypeScript types.
|
|
102
106
|
|
|
103
107
|
```ts
|
|
104
108
|
s.string() // string
|
|
@@ -108,29 +112,134 @@ s.boolean() // boolean
|
|
|
108
112
|
s.literal('2.0') // exact literal (string | number | boolean only)
|
|
109
113
|
s.null() // exactly null
|
|
110
114
|
s.array(s.string()) // string[]
|
|
111
|
-
s.object({ id: s.number() }) // open object
|
|
112
|
-
s.object({ id: s.number() }).strict() // all props required, no extras
|
|
115
|
+
s.object({ id: s.number() }) // open object (default: all props optional, extra props kept)
|
|
116
|
+
s.object({ id: s.number() }).strict() // all declared props required, no extras
|
|
113
117
|
s.record(s.number()) // Record<string, number>
|
|
114
118
|
s.union([s.string(), s.number()])
|
|
115
119
|
s.tuple([s.string(), s.number()])
|
|
116
120
|
s.enum(['pending', 'success', 'failed'])
|
|
117
121
|
s.unknown() // any JSON value (already includes null)
|
|
118
122
|
s.void() // RPC-only: no return value
|
|
123
|
+
s.stream(s.string()) // RPC-only: streaming return (AsyncGenerator)
|
|
119
124
|
s.injectable('request') // RPC-only: server-injected parameter
|
|
120
125
|
```
|
|
121
126
|
|
|
122
|
-
Chainable modifiers
|
|
123
|
-
|
|
124
|
-
|
|
127
|
+
### Chainable modifiers
|
|
128
|
+
|
|
129
|
+
| Modifier | Applies to | Meaning |
|
|
130
|
+
| --- | --- | --- |
|
|
131
|
+
| `.min(n)` / `.max(n)` | string (length), number (bounds), array / record (size) | Inclusive bounds. |
|
|
132
|
+
| `.nullable()` / `.nullable(true)` | most schemas | Allow `null`. Immutable; returns a new schema. |
|
|
133
|
+
| `.nullable(false)` | same | Clear a previous nullable flag. |
|
|
134
|
+
| `.describe(text)` | all schemas | Human-readable description (shown in WebUI / docs). |
|
|
135
|
+
| `.validate(false)` / `.validate(true)` | all schemas | Toggle runtime validation. Default is **on**. When off, `parse` / `safeParse` accept any value and return it unchanged; the schema still describes the type for signatures and the WebUI. |
|
|
136
|
+
| `.optionalProperties(keys)` | object | Whitelist of declared keys that may be missing. |
|
|
137
|
+
| `.allowUnknownProperties(bool)` | object | Whether undeclared keys are kept (`true`) or rejected (`false`). |
|
|
138
|
+
| `.strict()` / `.strict(true)` | object | Shortcut: no missing declared keys + reject extras. |
|
|
139
|
+
| `.strict(false)` | object | Restore the default permissive object config. |
|
|
140
|
+
|
|
141
|
+
Validation: `.parse(input)` (throws `SchemaValidationError` when validation is on)
|
|
142
|
+
and `.safeParse(input)` (returns a result object).
|
|
125
143
|
Introspection: `.toJSON()` returns a `SchemaDescription`.
|
|
126
144
|
|
|
127
|
-
|
|
145
|
+
### Disabling validation
|
|
146
|
+
|
|
147
|
+
Schemas always participate in **signature description**. Runtime **parameter
|
|
148
|
+
checks** can be turned off per schema when the server trusts the caller or the
|
|
149
|
+
shape is only documentary:
|
|
150
|
+
|
|
151
|
+
```ts
|
|
152
|
+
// Still appears as string in system.methodSignature / WebUI,
|
|
153
|
+
// but convertParams will not reject non-strings.
|
|
154
|
+
s.string().validate(false)
|
|
155
|
+
|
|
156
|
+
// Nested: only the inner schema skips checks
|
|
157
|
+
s.object({
|
|
158
|
+
id: s.number(),
|
|
159
|
+
payload: s.unknown().validate(false),
|
|
160
|
+
})
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
- Default is `validate: true` (omitted from `toJSON()` to keep descriptions small).
|
|
164
|
+
- `.validate(false)` writes `validate: false` into the description.
|
|
165
|
+
- `.validate(true)` restores the default; the flag is not emitted.
|
|
166
|
+
- Calls are immutable (same pattern as `.nullable()`).
|
|
167
|
+
|
|
168
|
+
Return-type slots are not run through `convertParams`; turning validation off on
|
|
169
|
+
a return schema only affects direct `.parse` / `.safeParse` use and the
|
|
170
|
+
description flag.
|
|
171
|
+
|
|
172
|
+
### Object defaults and strictness
|
|
173
|
+
|
|
174
|
+
By default an object schema is **permissive**:
|
|
175
|
+
|
|
176
|
+
- every declared property may be omitted;
|
|
177
|
+
- undeclared properties are allowed and preserved (if they are valid JSON).
|
|
178
|
+
|
|
179
|
+
```ts
|
|
180
|
+
const open = s.object({ name: s.string(), age: s.number() });
|
|
181
|
+
open.parse({}); // {}
|
|
182
|
+
open.parse({ name: 'Ada', extra: 1 }); // { name: 'Ada', extra: 1 }
|
|
183
|
+
|
|
184
|
+
const closed = open.strict();
|
|
185
|
+
closed.parse({ name: 'Ada', age: 30 }); // ok
|
|
186
|
+
// missing age → error; extra keys → error
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Finer control without the all-or-nothing `strict()`:
|
|
190
|
+
|
|
191
|
+
```ts
|
|
192
|
+
// only `age` may be missing; still allow unknown keys
|
|
193
|
+
s.object({ name: s.string(), age: s.number() }).optionalProperties(['age']);
|
|
194
|
+
|
|
195
|
+
// all declared keys may be missing, but reject extras
|
|
196
|
+
s.object({ name: s.string() }).allowUnknownProperties(false);
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
`optionalProperties` rejects key names that are not in the shape (throws
|
|
200
|
+
`TypeError`).
|
|
201
|
+
|
|
202
|
+
### Nullability notes
|
|
128
203
|
|
|
129
204
|
- `.nullable()` / `.nullable(true)` allow `null`; `.nullable(false)` clears it. All calls are immutable.
|
|
130
205
|
- `s.unknown()` and `s.null()` do not expose `.nullable()`; they already accept / are `null`.
|
|
131
206
|
- `s.literal(v)` accepts `string | number | boolean`. Use `s.null()` for exact null, or `.nullable()` for "literal or null".
|
|
132
207
|
- `s.union([...])` strips member-level nullability; only the union's own `.nullable()` decides whether `null` is accepted.
|
|
133
208
|
|
|
209
|
+
### `toJSON()` / `SchemaDescription`
|
|
210
|
+
|
|
211
|
+
Descriptions are a discriminated union on `kind`. There is **no** generic
|
|
212
|
+
`children` bag for ordinary schemas:
|
|
213
|
+
|
|
214
|
+
| kind | Child field(s) |
|
|
215
|
+
| --- | --- |
|
|
216
|
+
| `array` | `element` (single schema) |
|
|
217
|
+
| `record` | `value` (single schema) |
|
|
218
|
+
| `object` | `properties` (map) |
|
|
219
|
+
| `union` | `options` (array) |
|
|
220
|
+
| `tuple` | `elements` (array) |
|
|
221
|
+
|
|
222
|
+
Object constraints are reported as:
|
|
223
|
+
|
|
224
|
+
```ts
|
|
225
|
+
constraints: {
|
|
226
|
+
optionalProps: string[]; // keys allowed to be missing
|
|
227
|
+
allowUnknownProps: boolean; // whether extra keys are kept
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
Empty constraint objects are omitted (no `constraints: {}`).
|
|
232
|
+
|
|
233
|
+
When validation is disabled, the description includes `validate: false`.
|
|
234
|
+
The default enabled state is not serialized.
|
|
235
|
+
|
|
236
|
+
Stream returns from `system.methodSignature` / `signatureOf` currently use a
|
|
237
|
+
small exception for the marker shape:
|
|
238
|
+
|
|
239
|
+
```ts
|
|
240
|
+
{ kind: 'stream', inner: <SchemaDescription> }
|
|
241
|
+
```
|
|
242
|
+
|
|
134
243
|
## Transports
|
|
135
244
|
|
|
136
245
|
### Express
|
package/SKILL.md
CHANGED
|
@@ -30,8 +30,26 @@ Hard constraints (by design, do not work around):
|
|
|
30
30
|
`s.literal(v)` (`v` is `string | number | boolean` only), `s.null()`,
|
|
31
31
|
`s.array(item)`, `s.object(shape)`, `s.record(v)`, `s.union([...])`,
|
|
32
32
|
`s.tuple([...])`, `s.enum([...])`, `s.unknown()`.
|
|
33
|
-
7. Chain modifiers before registering:
|
|
34
|
-
`.
|
|
33
|
+
7. Chain modifiers before registering:
|
|
34
|
+
- Bounds / docs: `.min()`, `.max()`, `.describe(text)`.
|
|
35
|
+
- Nullability: `.nullable()` / `.nullable(true)` / `.nullable(false)` (immutable).
|
|
36
|
+
- Validation gate: `.validate(true|false)` (immutable; default **on**).
|
|
37
|
+
- Schemas serve two roles: **describe** the RPC signature and **validate**
|
|
38
|
+
client params at runtime.
|
|
39
|
+
- `.validate(false)` keeps the schema in `signature` / `system.methodSignature`
|
|
40
|
+
/ WebUI / docs, but `parse` / `safeParse` become no-ops (return the input
|
|
41
|
+
unchanged). `convertParams` therefore will not reject bad values for that
|
|
42
|
+
slot.
|
|
43
|
+
- Prefer this only when a param is intentionally untrusted-schema or
|
|
44
|
+
validated elsewhere; do not use it as a substitute for `s.unknown()`.
|
|
45
|
+
- Object shape control:
|
|
46
|
+
- Default object is **permissive**: every declared key may be missing;
|
|
47
|
+
undeclared keys are kept if they are valid JSON.
|
|
48
|
+
- `.optionalProperties(keys)` — whitelist of declared keys that may be
|
|
49
|
+
missing (unknown names throw `TypeError`).
|
|
50
|
+
- `.allowUnknownProperties(bool)` — keep (`true`) or reject (`false`) extras.
|
|
51
|
+
- `.strict()` / `.strict(true)` — no missing declared keys + reject extras.
|
|
52
|
+
- `.strict(false)` — restore the default permissive config.
|
|
35
53
|
8. Nullability rules:
|
|
36
54
|
- `.nullable()` / `.nullable(true)` allow `null`; `.nullable(false)` clears it. Calls are immutable.
|
|
37
55
|
- `s.unknown()` and `s.null()` already handle `null`; they do not expose `.nullable()`.
|
|
@@ -46,6 +64,57 @@ repository.register(
|
|
|
46
64
|
{ namespace: 'add', document: 'a + b', signature: [s.number(), s.number(), s.number()] },
|
|
47
65
|
(a: number, b: number) => a + b,
|
|
48
66
|
);
|
|
67
|
+
|
|
68
|
+
// Describe shape for clients / WebUI, but skip runtime checks on this param:
|
|
69
|
+
repository.register(
|
|
70
|
+
{
|
|
71
|
+
namespace: 'legacy.passthrough',
|
|
72
|
+
signature: [
|
|
73
|
+
s.unknown(),
|
|
74
|
+
s.object({ id: s.number(), tags: s.array(s.string()) }).validate(false),
|
|
75
|
+
],
|
|
76
|
+
},
|
|
77
|
+
(payload) => payload,
|
|
78
|
+
);
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Schema descriptions (`toJSON` / `system.methodSignature`)
|
|
82
|
+
|
|
83
|
+
Ordinary schemas use a discriminated union on `kind`. There is **no** generic
|
|
84
|
+
`children` bag:
|
|
85
|
+
|
|
86
|
+
| kind | Child field(s) |
|
|
87
|
+
| --- | --- |
|
|
88
|
+
| `array` | `element` |
|
|
89
|
+
| `record` | `value` |
|
|
90
|
+
| `object` | `properties` |
|
|
91
|
+
| `union` | `options` |
|
|
92
|
+
| `tuple` | `elements` |
|
|
93
|
+
|
|
94
|
+
Object constraints in the description:
|
|
95
|
+
|
|
96
|
+
```ts
|
|
97
|
+
constraints: {
|
|
98
|
+
optionalProps: string[];
|
|
99
|
+
allowUnknownProps: boolean;
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Empty constraint objects are omitted (no `constraints: {}`).
|
|
104
|
+
|
|
105
|
+
When validation is turned off, the description includes an explicit flag
|
|
106
|
+
(default is on and omitted to keep noise low):
|
|
107
|
+
|
|
108
|
+
```ts
|
|
109
|
+
{ kind: 'object', validate: false, properties: { ... } }
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Do **not** read legacy paths such as `children.items` or `children.values`.
|
|
113
|
+
|
|
114
|
+
Stream returns are the exception (marker path in `signatureOf`):
|
|
115
|
+
|
|
116
|
+
```ts
|
|
117
|
+
{ kind: 'stream', inner: <SchemaDescription> }
|
|
49
118
|
```
|
|
50
119
|
|
|
51
120
|
## Stream a return value
|
|
@@ -205,8 +274,17 @@ Keep `withSystemMethods` on (default) to expose `system.listMethods`,
|
|
|
205
274
|
`system.methodSignature`, `system.methodHelp`. Set `document` on a descriptor
|
|
206
275
|
so `system.methodHelp` returns useful text.
|
|
207
276
|
|
|
208
|
-
`system.methodSignature` reports
|
|
209
|
-
`
|
|
277
|
+
`system.methodSignature` reports ordinary containers with semantic fields
|
|
278
|
+
(`element` / `value` / `properties` / `options` / `elements`). A stream return
|
|
279
|
+
is reported as:
|
|
280
|
+
|
|
281
|
+
```ts
|
|
282
|
+
{ kind: 'stream', children: { inner: <schema> } }
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
Schemas with `.validate(false)` appear with `validate: false` in the signature
|
|
286
|
+
description so clients and the WebUI can show that the server will not enforce
|
|
287
|
+
that slot at runtime.
|
|
210
288
|
|
|
211
289
|
## Common mistakes
|
|
212
290
|
|
|
@@ -253,3 +331,16 @@ so `system.methodHelp` returns useful text.
|
|
|
253
331
|
- **Calling `s.literal(null)`.** `literal` only accepts `string | number |
|
|
254
332
|
boolean`; construction throws `TypeError`. Use `s.null()` for exact null, or
|
|
255
333
|
`.nullable()` for "literal or null".
|
|
334
|
+
- **Treating default `s.object(...)` as strict.** By default all declared keys
|
|
335
|
+
may be missing and unknown keys are kept. Use `.strict()` or
|
|
336
|
+
`.optionalProperties` / `.allowUnknownProperties` for tighter rules.
|
|
337
|
+
- **Reading schema descriptions via `children.items` / `children.values`.**
|
|
338
|
+
Those paths are gone. Use `element`, `value`, `properties`, `options`,
|
|
339
|
+
`elements`. Only stream markers still use `children.inner`.
|
|
340
|
+
- **Assuming `.validate(false)` still type-checks or coerces.** It only skips
|
|
341
|
+
runtime validation; TypeScript types and the published description stay as
|
|
342
|
+
declared. Use `s.unknown()` when the value truly has no schema.
|
|
343
|
+
- **Turning off validation on the return schema and expecting the server to
|
|
344
|
+
enforce the handler result.** Return schemas are for description / WebUI;
|
|
345
|
+
the server does not re-validate handler return values against them (except
|
|
346
|
+
stream declaration checks for `AsyncGenerator`).
|
package/dist/cli/demo-methods.js
CHANGED
|
@@ -472,4 +472,81 @@ export function installDemoMethods(repository) {
|
|
|
472
472
|
await new Promise((r) => setTimeout(r, 100));
|
|
473
473
|
}
|
|
474
474
|
});
|
|
475
|
+
// ─────────────────────────────────────────────────────────────
|
|
476
|
+
// 14. validate(false):只描述、不校验
|
|
477
|
+
// ─────────────────────────────────────────────────────────────
|
|
478
|
+
// Schema 同时用于签名描述与运行时校验。默认两者都开;
|
|
479
|
+
// .validate(false) 后 convertParams 仍接受任意 JSON,但
|
|
480
|
+
// system.methodSignature / WebUI 仍展示完整类型信息。
|
|
481
|
+
// 14.1 对比:同一形状,一边校验一边不校验
|
|
482
|
+
repository.register({
|
|
483
|
+
namespace: 'validate.on',
|
|
484
|
+
document: '默认开启校验:params[0] 必须是非空字符串,否则 INVALID_PARAMS。',
|
|
485
|
+
signature: [
|
|
486
|
+
s.string().describe('回显'),
|
|
487
|
+
s.string().min(1).max(32).describe('非空短字符串'),
|
|
488
|
+
],
|
|
489
|
+
}, (msg) => msg);
|
|
490
|
+
repository.register({
|
|
491
|
+
namespace: 'validate.off',
|
|
492
|
+
document: '与 validate.on 同形,但参数 schema 关闭了运行时校验。' +
|
|
493
|
+
'任意 JSON 都能进 handler;类型信息仍出现在 methodSignature / WebUI。',
|
|
494
|
+
signature: [
|
|
495
|
+
s.unknown().describe('原样返回(可能并非 string)'),
|
|
496
|
+
s
|
|
497
|
+
.string()
|
|
498
|
+
.min(1)
|
|
499
|
+
.max(32)
|
|
500
|
+
.describe('文档上仍是非空短字符串')
|
|
501
|
+
.validate(false),
|
|
502
|
+
],
|
|
503
|
+
}, (msg) => msg);
|
|
504
|
+
// 14.2 对象:严格描述 + 关闭校验(信任上游已洗过数据)
|
|
505
|
+
repository.register({
|
|
506
|
+
namespace: 'validate.offObject',
|
|
507
|
+
document: '对象签名写了 strict,但 validate(false):缺字段 / 多余字段 / 类型错误都不会在 convertParams 阶段失败。',
|
|
508
|
+
signature: [
|
|
509
|
+
s.unknown(),
|
|
510
|
+
s
|
|
511
|
+
.object({
|
|
512
|
+
id: s.number().describe('用户 ID'),
|
|
513
|
+
name: s.string().min(1).describe('名称'),
|
|
514
|
+
})
|
|
515
|
+
.strict()
|
|
516
|
+
.describe('文档上的严格用户对象')
|
|
517
|
+
.validate(false),
|
|
518
|
+
],
|
|
519
|
+
}, (payload) => payload);
|
|
520
|
+
// 14.3 嵌套:仅内层关闭校验
|
|
521
|
+
repository.register({
|
|
522
|
+
namespace: 'validate.nestedPartial',
|
|
523
|
+
document: '外层 array 仍校验「必须是数组」;元素 schema 关闭校验,元素内容任意。',
|
|
524
|
+
signature: [
|
|
525
|
+
s.array(s.unknown()).describe('原样返回'),
|
|
526
|
+
s
|
|
527
|
+
.array(s
|
|
528
|
+
.object({
|
|
529
|
+
code: s.string().min(1),
|
|
530
|
+
qty: s.number().min(0),
|
|
531
|
+
})
|
|
532
|
+
.strict()
|
|
533
|
+
.validate(false))
|
|
534
|
+
.min(1)
|
|
535
|
+
.describe('至少 1 个元素;元素内部不校验'),
|
|
536
|
+
],
|
|
537
|
+
}, (items) => items);
|
|
538
|
+
// 14.4 可再打开:链式 validate(false).validate(true) 与默认等价(演示可撤销)
|
|
539
|
+
repository.register({
|
|
540
|
+
namespace: 'validate.reenabled',
|
|
541
|
+
document: '先 validate(false) 再 validate(true),效果与默认开启校验相同。',
|
|
542
|
+
signature: [
|
|
543
|
+
s.number().describe('原样数字'),
|
|
544
|
+
s
|
|
545
|
+
.number()
|
|
546
|
+
.min(0)
|
|
547
|
+
.describe('非负数字')
|
|
548
|
+
.validate(false)
|
|
549
|
+
.validate(true),
|
|
550
|
+
],
|
|
551
|
+
}, (n) => n);
|
|
475
552
|
}
|
package/dist/core/llm-tools.js
CHANGED
|
@@ -50,12 +50,12 @@ function convert(desc) {
|
|
|
50
50
|
out.minItems = c.min;
|
|
51
51
|
if (c.max !== undefined)
|
|
52
52
|
out.maxItems = c.max;
|
|
53
|
-
out.items = convert(desc.
|
|
53
|
+
out.items = convert(desc.element);
|
|
54
54
|
break;
|
|
55
55
|
}
|
|
56
56
|
case 'tuple': {
|
|
57
57
|
out.type = 'array';
|
|
58
|
-
const items = desc.
|
|
58
|
+
const items = desc.elements;
|
|
59
59
|
out.items = items.map(convert);
|
|
60
60
|
out.minItems = items.length;
|
|
61
61
|
out.maxItems = items.length;
|
|
@@ -63,19 +63,19 @@ function convert(desc) {
|
|
|
63
63
|
}
|
|
64
64
|
case 'object': {
|
|
65
65
|
out.type = 'object';
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
66
|
+
const optional = new Set((desc.constraints
|
|
67
|
+
?.optionalProps) ?? []);
|
|
68
|
+
const allowUnknown = (desc.constraints
|
|
69
|
+
?.allowUnknownProps) ?? true;
|
|
70
|
+
const props = desc.properties;
|
|
69
71
|
out.properties = {};
|
|
70
72
|
for (const [k, v] of Object.entries(props))
|
|
71
73
|
out.properties[k] = convert(v);
|
|
72
|
-
|
|
73
|
-
|
|
74
|
+
const required = Object.keys(props).filter((k) => !optional.has(k));
|
|
75
|
+
if (required.length > 0)
|
|
76
|
+
out.required = required;
|
|
77
|
+
if (!allowUnknown)
|
|
74
78
|
out.additionalProperties = false;
|
|
75
|
-
}
|
|
76
|
-
else {
|
|
77
|
-
out.additionalProperties = true;
|
|
78
|
-
}
|
|
79
79
|
break;
|
|
80
80
|
}
|
|
81
81
|
case 'record': {
|
|
@@ -85,12 +85,11 @@ function convert(desc) {
|
|
|
85
85
|
out.minProperties = c.min;
|
|
86
86
|
if (c.max !== undefined)
|
|
87
87
|
out.maxProperties = c.max;
|
|
88
|
-
out.additionalProperties = convert(desc.
|
|
88
|
+
out.additionalProperties = convert(desc.value);
|
|
89
89
|
break;
|
|
90
90
|
}
|
|
91
91
|
case 'union': {
|
|
92
|
-
|
|
93
|
-
out.anyOf = options.map(convert);
|
|
92
|
+
out.anyOf = desc.options.map(convert);
|
|
94
93
|
break;
|
|
95
94
|
}
|
|
96
95
|
case 'unknown': {
|
|
@@ -101,8 +100,10 @@ function convert(desc) {
|
|
|
101
100
|
out.type = 'null';
|
|
102
101
|
break;
|
|
103
102
|
}
|
|
104
|
-
default:
|
|
105
|
-
|
|
103
|
+
default: {
|
|
104
|
+
const _exhaustive = desc;
|
|
105
|
+
throw new Error(`Unsupported schema kind for JSON Schema: ${JSON.stringify(_exhaustive)}`);
|
|
106
|
+
}
|
|
106
107
|
}
|
|
107
108
|
if (desc.nullable) {
|
|
108
109
|
if (desc.kind === 'unknown' || desc.kind === 'null') {
|
|
@@ -150,7 +151,6 @@ function renderSchema(schema, indent = 0) {
|
|
|
150
151
|
if (c)
|
|
151
152
|
head += ` \`${c}\``;
|
|
152
153
|
lines.push(head);
|
|
153
|
-
const ch = schema.children ?? {};
|
|
154
154
|
const pushChild = (label, child) => {
|
|
155
155
|
const sub = renderSchema(child, indent + 1);
|
|
156
156
|
if (sub.length) {
|
|
@@ -158,36 +158,35 @@ function renderSchema(schema, indent = 0) {
|
|
|
158
158
|
lines.push(...sub);
|
|
159
159
|
}
|
|
160
160
|
};
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
if (Array.isArray(ch.options)) {
|
|
173
|
-
ch.options.forEach((opt, i) => {
|
|
174
|
-
if (isSchema(opt))
|
|
175
|
-
pushChild(`option[${i}]`, opt);
|
|
176
|
-
});
|
|
177
|
-
}
|
|
178
|
-
if (ch.properties && typeof ch.properties === 'object') {
|
|
179
|
-
for (const [k, v] of Object.entries(ch.properties)) {
|
|
180
|
-
if (isSchema(v))
|
|
161
|
+
switch (schema.kind) {
|
|
162
|
+
case 'array': {
|
|
163
|
+
pushChild('element', schema.element);
|
|
164
|
+
break;
|
|
165
|
+
}
|
|
166
|
+
case 'record': {
|
|
167
|
+
pushChild('value', schema.value);
|
|
168
|
+
break;
|
|
169
|
+
}
|
|
170
|
+
case 'object': {
|
|
171
|
+
for (const [k, v] of Object.entries(schema.properties)) {
|
|
181
172
|
pushChild(k, v);
|
|
173
|
+
}
|
|
174
|
+
break;
|
|
175
|
+
}
|
|
176
|
+
case 'union': {
|
|
177
|
+
schema.options.forEach((opt, i) => pushChild(`option[${i}]`, opt));
|
|
178
|
+
break;
|
|
182
179
|
}
|
|
180
|
+
case 'tuple': {
|
|
181
|
+
schema.elements.forEach((it, i) => pushChild(`[${i}]`, it));
|
|
182
|
+
break;
|
|
183
|
+
}
|
|
184
|
+
default:
|
|
185
|
+
// 叶子类型:无子节点。
|
|
186
|
+
break;
|
|
183
187
|
}
|
|
184
|
-
if (isSchema(ch.values))
|
|
185
|
-
pushChild('values', ch.values);
|
|
186
188
|
return lines;
|
|
187
189
|
}
|
|
188
|
-
function isSchema(v) {
|
|
189
|
-
return !!v && typeof v === 'object' && typeof v.kind === 'string';
|
|
190
|
-
}
|
|
191
190
|
/** 渲染返回值槽位(Schema / VoidMarker / StreamMarker)。 */
|
|
192
191
|
function renderReturnSlot(slot) {
|
|
193
192
|
if (slot instanceof VoidMarker)
|
|
@@ -6,7 +6,7 @@ export declare const s: {
|
|
|
6
6
|
boolean: () => import("./primitives.js").BooleanSchema;
|
|
7
7
|
literal: <const T extends import("./primitives.js").LiteralValue>(value: T) => import("./primitives.js").LiteralSchema<T>;
|
|
8
8
|
array: <T>(item: import("./primitives.js").Schema<T>) => import("./primitives.js").ArraySchema<T>;
|
|
9
|
-
object: <S extends import("./primitives.js").ObjectShape>(shape: S) => import("./primitives.js").ObjectSchema<S
|
|
9
|
+
object: <S extends import("./primitives.js").ObjectShape>(shape: S) => import("./primitives.js").ObjectSchema<S>;
|
|
10
10
|
record: <T>(value: import("./primitives.js").Schema<T>) => import("./primitives.js").RecordSchema<T>;
|
|
11
11
|
union: <const S extends readonly [import("./primitives.js").Schema<unknown>, ...import("./primitives.js").Schema<unknown>[]]>(schemas: S) => import("./primitives.js").UnionSchema<S>;
|
|
12
12
|
tuple: <const S extends readonly [import("./primitives.js").Schema<unknown>, ...import("./primitives.js").Schema<unknown>[]]>(schemas: S) => import("./primitives.js").TupleSchema<S>;
|