@yuhere/js-jsonrpc2 2.1.13 → 2.1.14
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/dist/core/llm-tools.js +41 -42
- package/dist/schema/json/index.d.ts +1 -1
- package/dist/schema/json/primitives.d.ts +49 -69
- package/dist/schema/json/primitives.js +70 -76
- package/dist/schema/z/protocol.d.ts +1 -1
- package/htdoc/assets/{index-CqI7vRAS.js → index-mYqtDOwe.js} +160 -78
- package/htdoc/assets/index-mYqtDOwe.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/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>;
|
|
@@ -9,20 +9,36 @@ export type JSONValue = JSONPrimitive | JSONValue[] | {
|
|
|
9
9
|
[key: string]: JSONValue;
|
|
10
10
|
};
|
|
11
11
|
export type SchemaKind = string;
|
|
12
|
-
export interface
|
|
12
|
+
export interface BaseSchemaDescription {
|
|
13
13
|
kind: SchemaKind;
|
|
14
14
|
description?: string;
|
|
15
|
-
/**
|
|
16
|
-
* 该值是否允许为 null。
|
|
17
|
-
*
|
|
18
|
-
* 唯一真相来源:由 `NullableSchema` 及其子类通过 `.nullable(bool)` 设置,
|
|
19
|
-
* `UnknownSchema` / `NullSchema` 不使用该字段(它们本身就接受 null)。
|
|
20
|
-
* `kind` 永远表示值的真实形状,不会被 nullability 污染。
|
|
21
|
-
*/
|
|
22
15
|
nullable?: boolean;
|
|
23
16
|
constraints?: Record<string, unknown>;
|
|
24
|
-
children?: Record<string, unknown>;
|
|
25
17
|
}
|
|
18
|
+
export interface LeafSchemaDescription extends BaseSchemaDescription {
|
|
19
|
+
kind: "string" | "number" | "boolean" | "literal" | "enum" | "unknown" | "null";
|
|
20
|
+
}
|
|
21
|
+
export interface ObjectSchemaDescription extends BaseSchemaDescription {
|
|
22
|
+
kind: "object";
|
|
23
|
+
properties: Record<string, SchemaDescription>;
|
|
24
|
+
}
|
|
25
|
+
export interface ArraySchemaDescription extends BaseSchemaDescription {
|
|
26
|
+
kind: "array";
|
|
27
|
+
element: SchemaDescription;
|
|
28
|
+
}
|
|
29
|
+
export interface RecordSchemaDescription extends BaseSchemaDescription {
|
|
30
|
+
kind: "record";
|
|
31
|
+
value: SchemaDescription;
|
|
32
|
+
}
|
|
33
|
+
export interface UnionSchemaDescription extends BaseSchemaDescription {
|
|
34
|
+
kind: "union";
|
|
35
|
+
options: SchemaDescription[];
|
|
36
|
+
}
|
|
37
|
+
export interface TupleSchemaDescription extends BaseSchemaDescription {
|
|
38
|
+
kind: "tuple";
|
|
39
|
+
elements: SchemaDescription[];
|
|
40
|
+
}
|
|
41
|
+
export type SchemaDescription = LeafSchemaDescription | ObjectSchemaDescription | ArraySchemaDescription | RecordSchemaDescription | UnionSchemaDescription | TupleSchemaDescription;
|
|
26
42
|
export declare class SchemaValidationError extends Error {
|
|
27
43
|
readonly path: string[];
|
|
28
44
|
constructor(message: string, path?: string[]);
|
|
@@ -36,14 +52,13 @@ export declare class SchemaValidationError extends Error {
|
|
|
36
52
|
*/
|
|
37
53
|
export declare abstract class Schema<T> {
|
|
38
54
|
abstract readonly kind: SchemaKind;
|
|
39
|
-
private description
|
|
40
|
-
constructor(description?: string);
|
|
55
|
+
private description;
|
|
41
56
|
describe(description: string): this;
|
|
42
57
|
/**
|
|
43
58
|
* 创建保留原型和现有字段的新实例,供不可变链式方法使用。
|
|
44
59
|
*/
|
|
45
60
|
protected cloneWith(updates: Record<string, unknown>): this;
|
|
46
|
-
getDescription(): string
|
|
61
|
+
getDescription(): string;
|
|
47
62
|
parse(input: unknown): T;
|
|
48
63
|
safeParse(input: unknown): {
|
|
49
64
|
success: true;
|
|
@@ -84,7 +99,7 @@ export declare class StringSchema<K extends string = "string"> extends NullableS
|
|
|
84
99
|
min(value: number): this;
|
|
85
100
|
max(value: number): this;
|
|
86
101
|
protected _parseValue(input: unknown, path: string[]): string;
|
|
87
|
-
protected _describe():
|
|
102
|
+
protected _describe(): LeafSchemaDescription;
|
|
88
103
|
protected validateBound(value: number): void;
|
|
89
104
|
}
|
|
90
105
|
export declare class NumberSchema extends NullableSchema<number> {
|
|
@@ -95,28 +110,22 @@ export declare class NumberSchema extends NullableSchema<number> {
|
|
|
95
110
|
min(value: number): this;
|
|
96
111
|
max(value: number): this;
|
|
97
112
|
protected _parseValue(input: unknown, path: string[]): number;
|
|
98
|
-
protected _describe():
|
|
113
|
+
protected _describe(): LeafSchemaDescription;
|
|
99
114
|
protected validateBound(value: number): void;
|
|
100
115
|
}
|
|
101
116
|
export declare class BooleanSchema extends NullableSchema<boolean> {
|
|
102
117
|
readonly kind: "boolean";
|
|
103
118
|
constructor();
|
|
104
119
|
protected _parseValue(input: unknown, path: string[]): boolean;
|
|
105
|
-
protected _describe():
|
|
120
|
+
protected _describe(): LeafSchemaDescription;
|
|
106
121
|
}
|
|
107
|
-
/**
|
|
108
|
-
* 字面量只支持 string / number / boolean。
|
|
109
|
-
*
|
|
110
|
-
* null 由 `s.null()` 表达;需要「该字面量或 null」时,对字面量调用
|
|
111
|
-
* `.nullable()`。`literal(null)` 会抛 TypeError。
|
|
112
|
-
*/
|
|
113
122
|
export type LiteralValue = string | number | boolean;
|
|
114
123
|
export declare class LiteralSchema<const T extends LiteralValue> extends NullableSchema<T> {
|
|
115
124
|
private readonly expected;
|
|
116
125
|
readonly kind: "literal";
|
|
117
126
|
constructor(expected: T);
|
|
118
127
|
protected _parseValue(input: unknown, path: string[]): T;
|
|
119
|
-
protected _describe():
|
|
128
|
+
protected _describe(): LeafSchemaDescription;
|
|
120
129
|
}
|
|
121
130
|
export declare class ArraySchema<T> extends NullableSchema<T[]> {
|
|
122
131
|
private readonly itemSchema;
|
|
@@ -127,28 +136,25 @@ export declare class ArraySchema<T> extends NullableSchema<T[]> {
|
|
|
127
136
|
min(value: number): this;
|
|
128
137
|
max(value: number): this;
|
|
129
138
|
protected _parseValue(input: unknown, path: string[]): T[];
|
|
130
|
-
protected _describe():
|
|
139
|
+
protected _describe(): ArraySchemaDescription;
|
|
131
140
|
protected validateBound(value: number): void;
|
|
132
141
|
}
|
|
133
142
|
export type ObjectShape = Record<string, Schema<unknown>>;
|
|
134
143
|
type SchemaValue<S> = S extends Schema<infer T> ? T : never;
|
|
135
|
-
type
|
|
136
|
-
[K in keyof S]: SchemaValue<S[K]>;
|
|
137
|
-
};
|
|
138
|
-
type OptionalShapeOutput<S extends ObjectShape> = {
|
|
144
|
+
export type ObjectOutput<S extends ObjectShape> = {
|
|
139
145
|
[K in keyof S]?: SchemaValue<S[K]>;
|
|
140
146
|
};
|
|
141
|
-
export declare class ObjectSchema<S extends ObjectShape
|
|
147
|
+
export declare class ObjectSchema<S extends ObjectShape> extends NullableSchema<ObjectOutput<S>> {
|
|
142
148
|
private readonly shape;
|
|
143
149
|
readonly kind: "object";
|
|
144
|
-
private
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
strict():
|
|
150
|
-
protected _parseValue(input: unknown, path: string[]):
|
|
151
|
-
protected _describe():
|
|
150
|
+
private optionalProps;
|
|
151
|
+
private allowUnknownProps;
|
|
152
|
+
constructor(shape: S);
|
|
153
|
+
optionalProperties(keys: string[]): this;
|
|
154
|
+
allowUnknownProperties(value?: boolean): this;
|
|
155
|
+
strict(value?: boolean): this;
|
|
156
|
+
protected _parseValue(input: unknown, path: string[]): ObjectOutput<S>;
|
|
157
|
+
protected _describe(): ObjectSchemaDescription;
|
|
152
158
|
}
|
|
153
159
|
export declare class RecordSchema<T> extends NullableSchema<Record<string, T>> {
|
|
154
160
|
private readonly valueSchema;
|
|
@@ -159,68 +165,42 @@ export declare class RecordSchema<T> extends NullableSchema<Record<string, T>> {
|
|
|
159
165
|
min(value: number): this;
|
|
160
166
|
max(value: number): this;
|
|
161
167
|
protected _parseValue(input: unknown, path: string[]): Record<string, T>;
|
|
162
|
-
protected _describe():
|
|
168
|
+
protected _describe(): RecordSchemaDescription;
|
|
163
169
|
protected validateBound(value: number): void;
|
|
164
170
|
}
|
|
165
|
-
/**
|
|
166
|
-
* 联合。
|
|
167
|
-
*
|
|
168
|
-
* 传入的 member schemas 会被 clone 并 `nullable(false)`——因为「某个
|
|
169
|
-
* 分支允许 null」这件事在 union 层面没有意义:null 是否允许应通过
|
|
170
|
-
* `union(...).nullable()` 统一表达。
|
|
171
|
-
*/
|
|
172
171
|
export declare class UnionSchema<const S extends readonly [Schema<unknown>, ...Schema<unknown>[]]> extends NullableSchema<SchemaValue<S[number]>> {
|
|
173
172
|
readonly kind: "union";
|
|
174
173
|
private readonly options;
|
|
175
174
|
constructor(schemas: S);
|
|
176
175
|
protected _parseValue(input: unknown, path: string[]): SchemaValue<S[number]>;
|
|
177
|
-
protected _describe():
|
|
176
|
+
protected _describe(): UnionSchemaDescription;
|
|
178
177
|
}
|
|
179
178
|
export declare class TupleSchema<const S extends readonly [Schema<unknown>, ...Schema<unknown>[]]> extends NullableSchema<SchemaValue<S[number]>[]> {
|
|
180
179
|
private readonly schemas;
|
|
181
180
|
readonly kind: "tuple";
|
|
182
181
|
constructor(schemas: S);
|
|
183
182
|
protected _parseValue(input: unknown, path: string[]): SchemaValue<S[number]>[];
|
|
184
|
-
protected _describe():
|
|
183
|
+
protected _describe(): TupleSchemaDescription;
|
|
185
184
|
}
|
|
186
|
-
/**
|
|
187
|
-
* 字符串枚举。
|
|
188
|
-
*
|
|
189
|
-
* 继承 `NullableSchema`,因此可以 `s.enum([...]).nullable()`;
|
|
190
|
-
* 它不包含嵌套 schema,所以不需要像 union 那样处理成员 nullability。
|
|
191
|
-
*/
|
|
192
185
|
export declare class EnumSchema<const T extends readonly [string, ...string[]]> extends NullableSchema<T[number]> {
|
|
193
186
|
private readonly values;
|
|
194
187
|
readonly kind: "enum";
|
|
195
188
|
private readonly allowedValues;
|
|
196
189
|
constructor(values: T);
|
|
197
190
|
protected _parseValue(input: unknown, path: string[]): T[number];
|
|
198
|
-
protected _describe():
|
|
191
|
+
protected _describe(): LeafSchemaDescription;
|
|
199
192
|
}
|
|
200
|
-
/**
|
|
201
|
-
* 校验任意 JSON 值。
|
|
202
|
-
*
|
|
203
|
-
* 不允许 undefined、非有限数字、循环引用等非 JSON 数据。
|
|
204
|
-
* null 本身就在接受范围内,因此不继承 `NullableSchema`,也不提供
|
|
205
|
-
* `.nullable()`——那是冗余的。
|
|
206
|
-
*/
|
|
207
193
|
export declare class UnknownSchema extends Schema<JSONValue> {
|
|
208
194
|
readonly kind: "unknown";
|
|
209
195
|
constructor();
|
|
210
196
|
protected _parse(input: unknown, path: string[]): JSONValue;
|
|
211
|
-
protected _describe():
|
|
197
|
+
protected _describe(): LeafSchemaDescription;
|
|
212
198
|
}
|
|
213
|
-
/**
|
|
214
|
-
* 精确的 null 值。
|
|
215
|
-
*
|
|
216
|
-
* 与 `UnknownSchema` 一样天然表达 null,不需要 `.nullable()`。
|
|
217
|
-
* 想表达「某类型或 null」用 `.nullable()`;想表达「就是 null」用 `s.null()`。
|
|
218
|
-
*/
|
|
219
199
|
export declare class NullSchema extends Schema<null> {
|
|
220
200
|
readonly kind: "null";
|
|
221
201
|
constructor();
|
|
222
202
|
protected _parse(input: unknown, path: string[]): null;
|
|
223
|
-
protected _describe():
|
|
203
|
+
protected _describe(): LeafSchemaDescription;
|
|
224
204
|
}
|
|
225
205
|
export type Infer<T extends Schema<unknown>> = T extends Schema<infer U> ? U : never;
|
|
226
206
|
export declare const primitives: {
|
|
@@ -229,7 +209,7 @@ export declare const primitives: {
|
|
|
229
209
|
boolean: () => BooleanSchema;
|
|
230
210
|
literal: <const T extends LiteralValue>(value: T) => LiteralSchema<T>;
|
|
231
211
|
array: <T>(item: Schema<T>) => ArraySchema<T>;
|
|
232
|
-
object: <S extends ObjectShape>(shape: S) => ObjectSchema<S
|
|
212
|
+
object: <S extends ObjectShape>(shape: S) => ObjectSchema<S>;
|
|
233
213
|
record: <T>(value: Schema<T>) => RecordSchema<T>;
|
|
234
214
|
union: <const S extends readonly [Schema<unknown>, ...Schema<unknown>[]]>(schemas: S) => UnionSchema<S>;
|
|
235
215
|
tuple: <const S extends readonly [Schema<unknown>, ...Schema<unknown>[]]>(schemas: S) => TupleSchema<S>;
|
|
@@ -14,10 +14,7 @@ export class SchemaValidationError extends Error {
|
|
|
14
14
|
* 不需要 `.nullable()`。
|
|
15
15
|
*/
|
|
16
16
|
export class Schema {
|
|
17
|
-
description;
|
|
18
|
-
constructor(description) {
|
|
19
|
-
this.description = description;
|
|
20
|
-
}
|
|
17
|
+
description = "";
|
|
21
18
|
describe(description) {
|
|
22
19
|
return this.cloneWith({ description });
|
|
23
20
|
}
|
|
@@ -118,12 +115,13 @@ export class StringSchema extends NullableSchema {
|
|
|
118
115
|
return input;
|
|
119
116
|
}
|
|
120
117
|
_describe() {
|
|
118
|
+
const constraints = buildConstraints({
|
|
119
|
+
min: this.minimum,
|
|
120
|
+
max: this.maximum,
|
|
121
|
+
});
|
|
121
122
|
return {
|
|
122
123
|
kind: this.kind,
|
|
123
|
-
constraints: {
|
|
124
|
-
...(this.minimum !== undefined ? { min: this.minimum } : {}),
|
|
125
|
-
...(this.maximum !== undefined ? { max: this.maximum } : {}),
|
|
126
|
-
},
|
|
124
|
+
...(constraints ? { constraints } : {}),
|
|
127
125
|
};
|
|
128
126
|
}
|
|
129
127
|
validateBound(value) {
|
|
@@ -160,12 +158,13 @@ export class NumberSchema extends NullableSchema {
|
|
|
160
158
|
return input;
|
|
161
159
|
}
|
|
162
160
|
_describe() {
|
|
161
|
+
const constraints = buildConstraints({
|
|
162
|
+
min: this.minimum,
|
|
163
|
+
max: this.maximum,
|
|
164
|
+
});
|
|
163
165
|
return {
|
|
164
166
|
kind: this.kind,
|
|
165
|
-
constraints: {
|
|
166
|
-
...(this.minimum !== undefined ? { min: this.minimum } : {}),
|
|
167
|
-
...(this.maximum !== undefined ? { max: this.maximum } : {}),
|
|
168
|
-
},
|
|
167
|
+
...(constraints ? { constraints } : {}),
|
|
169
168
|
};
|
|
170
169
|
}
|
|
171
170
|
validateBound(value) {
|
|
@@ -259,13 +258,14 @@ export class ArraySchema extends NullableSchema {
|
|
|
259
258
|
});
|
|
260
259
|
}
|
|
261
260
|
_describe() {
|
|
261
|
+
const constraints = buildConstraints({
|
|
262
|
+
min: this.minimum,
|
|
263
|
+
max: this.maximum,
|
|
264
|
+
});
|
|
262
265
|
return {
|
|
263
266
|
kind: this.kind,
|
|
264
|
-
constraints: {
|
|
265
|
-
|
|
266
|
-
...(this.maximum !== undefined ? { max: this.maximum } : {}),
|
|
267
|
-
},
|
|
268
|
-
children: { items: this.itemSchema.toJSON() },
|
|
267
|
+
...(constraints ? { constraints } : {}),
|
|
268
|
+
element: this.itemSchema.toJSON(),
|
|
269
269
|
};
|
|
270
270
|
}
|
|
271
271
|
validateBound(value) {
|
|
@@ -277,17 +277,29 @@ export class ArraySchema extends NullableSchema {
|
|
|
277
277
|
export class ObjectSchema extends NullableSchema {
|
|
278
278
|
shape;
|
|
279
279
|
kind = "object";
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
280
|
+
optionalProps;
|
|
281
|
+
allowUnknownProps = true;
|
|
282
|
+
constructor(shape) {
|
|
283
|
+
super();
|
|
283
284
|
this.shape = shape;
|
|
284
|
-
this.
|
|
285
|
+
this.optionalProps = Object.keys(shape);
|
|
285
286
|
}
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
287
|
+
optionalProperties(keys) {
|
|
288
|
+
for (const key of keys) {
|
|
289
|
+
if (!Object.prototype.hasOwnProperty.call(this.shape, key)) {
|
|
290
|
+
throw new TypeError(`optionalProperties: unknown property "${key}"`);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
return this.cloneWith({ optionalProps: [...new Set(keys)] });
|
|
294
|
+
}
|
|
295
|
+
allowUnknownProperties(value = true) {
|
|
296
|
+
return this.cloneWith({ allowUnknownProps: value });
|
|
297
|
+
}
|
|
298
|
+
strict(value = true) {
|
|
299
|
+
if (value) {
|
|
300
|
+
return this.optionalProperties([]).allowUnknownProperties(false);
|
|
301
|
+
}
|
|
302
|
+
return this.optionalProperties(Object.keys(this.shape)).allowUnknownProperties(true);
|
|
291
303
|
}
|
|
292
304
|
_parseValue(input, path) {
|
|
293
305
|
if (input === null ||
|
|
@@ -297,16 +309,17 @@ export class ObjectSchema extends NullableSchema {
|
|
|
297
309
|
}
|
|
298
310
|
const source = input;
|
|
299
311
|
const output = {};
|
|
300
|
-
|
|
312
|
+
const optional = new Set(this.optionalProps);
|
|
313
|
+
if (!this.allowUnknownProps) {
|
|
301
314
|
for (const key of Object.keys(source)) {
|
|
302
|
-
if (!(
|
|
315
|
+
if (!Object.prototype.hasOwnProperty.call(this.shape, key)) {
|
|
303
316
|
this.fail(`Unknown property "${key}"`, [...path, key]);
|
|
304
317
|
}
|
|
305
318
|
}
|
|
306
319
|
}
|
|
307
320
|
for (const [key, schema] of Object.entries(this.shape)) {
|
|
308
321
|
if (!Object.prototype.hasOwnProperty.call(source, key)) {
|
|
309
|
-
if (
|
|
322
|
+
if (!optional.has(key)) {
|
|
310
323
|
this.fail(`Missing required property "${key}"`, [...path, key]);
|
|
311
324
|
}
|
|
312
325
|
continue;
|
|
@@ -324,10 +337,9 @@ export class ObjectSchema extends NullableSchema {
|
|
|
324
337
|
throw error;
|
|
325
338
|
}
|
|
326
339
|
}
|
|
327
|
-
|
|
328
|
-
if (!this.isStrict) {
|
|
340
|
+
if (this.allowUnknownProps) {
|
|
329
341
|
for (const [key, value] of Object.entries(source)) {
|
|
330
|
-
if (!(
|
|
342
|
+
if (!Object.prototype.hasOwnProperty.call(this.shape, key)) {
|
|
331
343
|
assertJSONValue(value, [...path, key]);
|
|
332
344
|
output[key] = value;
|
|
333
345
|
}
|
|
@@ -338,13 +350,14 @@ export class ObjectSchema extends NullableSchema {
|
|
|
338
350
|
_describe() {
|
|
339
351
|
return {
|
|
340
352
|
kind: this.kind,
|
|
341
|
-
constraints: {
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
key,
|
|
345
|
-
schema.toJSON(),
|
|
346
|
-
])),
|
|
353
|
+
constraints: {
|
|
354
|
+
optionalProps: [...this.optionalProps],
|
|
355
|
+
allowUnknownProps: this.allowUnknownProps,
|
|
347
356
|
},
|
|
357
|
+
properties: Object.fromEntries(Object.entries(this.shape).map(([key, schema]) => [
|
|
358
|
+
key,
|
|
359
|
+
schema.toJSON(),
|
|
360
|
+
])),
|
|
348
361
|
};
|
|
349
362
|
}
|
|
350
363
|
}
|
|
@@ -396,13 +409,14 @@ export class RecordSchema extends NullableSchema {
|
|
|
396
409
|
return output;
|
|
397
410
|
}
|
|
398
411
|
_describe() {
|
|
412
|
+
const constraints = buildConstraints({
|
|
413
|
+
min: this.minimum,
|
|
414
|
+
max: this.maximum,
|
|
415
|
+
});
|
|
399
416
|
return {
|
|
400
417
|
kind: this.kind,
|
|
401
|
-
constraints: {
|
|
402
|
-
|
|
403
|
-
...(this.maximum !== undefined ? { max: this.maximum } : {}),
|
|
404
|
-
},
|
|
405
|
-
children: { values: this.valueSchema.toJSON() },
|
|
418
|
+
...(constraints ? { constraints } : {}),
|
|
419
|
+
value: this.valueSchema.toJSON(),
|
|
406
420
|
};
|
|
407
421
|
}
|
|
408
422
|
validateBound(value) {
|
|
@@ -411,13 +425,6 @@ export class RecordSchema extends NullableSchema {
|
|
|
411
425
|
}
|
|
412
426
|
}
|
|
413
427
|
}
|
|
414
|
-
/**
|
|
415
|
-
* 联合。
|
|
416
|
-
*
|
|
417
|
-
* 传入的 member schemas 会被 clone 并 `nullable(false)`——因为「某个
|
|
418
|
-
* 分支允许 null」这件事在 union 层面没有意义:null 是否允许应通过
|
|
419
|
-
* `union(...).nullable()` 统一表达。
|
|
420
|
-
*/
|
|
421
428
|
export class UnionSchema extends NullableSchema {
|
|
422
429
|
kind = "union";
|
|
423
430
|
options;
|
|
@@ -452,9 +459,7 @@ export class UnionSchema extends NullableSchema {
|
|
|
452
459
|
_describe() {
|
|
453
460
|
return {
|
|
454
461
|
kind: this.kind,
|
|
455
|
-
|
|
456
|
-
options: this.options.map((schema) => schema.toJSON()),
|
|
457
|
-
},
|
|
462
|
+
options: this.options.map((schema) => schema.toJSON()),
|
|
458
463
|
};
|
|
459
464
|
}
|
|
460
465
|
}
|
|
@@ -483,7 +488,10 @@ export class TupleSchema extends NullableSchema {
|
|
|
483
488
|
}
|
|
484
489
|
catch (error) {
|
|
485
490
|
if (error instanceof SchemaValidationError) {
|
|
486
|
-
|
|
491
|
+
throw new SchemaValidationError(error.message, [
|
|
492
|
+
String(i),
|
|
493
|
+
...error.path,
|
|
494
|
+
]);
|
|
487
495
|
}
|
|
488
496
|
throw error;
|
|
489
497
|
}
|
|
@@ -493,18 +501,10 @@ export class TupleSchema extends NullableSchema {
|
|
|
493
501
|
_describe() {
|
|
494
502
|
return {
|
|
495
503
|
kind: this.kind,
|
|
496
|
-
|
|
497
|
-
items: this.schemas.map((schema) => schema.toJSON()),
|
|
498
|
-
},
|
|
504
|
+
elements: this.schemas.map((schema) => schema.toJSON()),
|
|
499
505
|
};
|
|
500
506
|
}
|
|
501
507
|
}
|
|
502
|
-
/**
|
|
503
|
-
* 字符串枚举。
|
|
504
|
-
*
|
|
505
|
-
* 继承 `NullableSchema`,因此可以 `s.enum([...]).nullable()`;
|
|
506
|
-
* 它不包含嵌套 schema,所以不需要像 union 那样处理成员 nullability。
|
|
507
|
-
*/
|
|
508
508
|
export class EnumSchema extends NullableSchema {
|
|
509
509
|
values;
|
|
510
510
|
kind = "enum";
|
|
@@ -527,13 +527,6 @@ export class EnumSchema extends NullableSchema {
|
|
|
527
527
|
};
|
|
528
528
|
}
|
|
529
529
|
}
|
|
530
|
-
/**
|
|
531
|
-
* 校验任意 JSON 值。
|
|
532
|
-
*
|
|
533
|
-
* 不允许 undefined、非有限数字、循环引用等非 JSON 数据。
|
|
534
|
-
* null 本身就在接受范围内,因此不继承 `NullableSchema`,也不提供
|
|
535
|
-
* `.nullable()`——那是冗余的。
|
|
536
|
-
*/
|
|
537
530
|
export class UnknownSchema extends Schema {
|
|
538
531
|
kind = "unknown";
|
|
539
532
|
constructor() {
|
|
@@ -547,12 +540,6 @@ export class UnknownSchema extends Schema {
|
|
|
547
540
|
return { kind: this.kind };
|
|
548
541
|
}
|
|
549
542
|
}
|
|
550
|
-
/**
|
|
551
|
-
* 精确的 null 值。
|
|
552
|
-
*
|
|
553
|
-
* 与 `UnknownSchema` 一样天然表达 null,不需要 `.nullable()`。
|
|
554
|
-
* 想表达「某类型或 null」用 `.nullable()`;想表达「就是 null」用 `s.null()`。
|
|
555
|
-
*/
|
|
556
543
|
export class NullSchema extends Schema {
|
|
557
544
|
kind = "null";
|
|
558
545
|
constructor() {
|
|
@@ -568,6 +555,13 @@ export class NullSchema extends Schema {
|
|
|
568
555
|
return { kind: this.kind };
|
|
569
556
|
}
|
|
570
557
|
}
|
|
558
|
+
function buildConstraints(values) {
|
|
559
|
+
const entries = Object.entries(values).filter(([, value]) => value !== undefined);
|
|
560
|
+
if (entries.length === 0) {
|
|
561
|
+
return undefined;
|
|
562
|
+
}
|
|
563
|
+
return Object.fromEntries(entries);
|
|
564
|
+
}
|
|
571
565
|
function assertJSONValue(value, path, ancestors = new Set()) {
|
|
572
566
|
if (value === null ||
|
|
573
567
|
typeof value === "string" ||
|
|
@@ -5,4 +5,4 @@ export declare const RPCRequestSchema: import("../json/index.js").ObjectSchema<{
|
|
|
5
5
|
method: import("../json/index.js").StringSchema<"string">;
|
|
6
6
|
params: import("../json/index.js").ArraySchema<import("../json/index.js").JSONValue>;
|
|
7
7
|
id: import("../json/index.js").UnionSchema<readonly [import("../json/index.js").StringSchema<"string">, import("../json/index.js").NumberSchema]>;
|
|
8
|
-
}
|
|
8
|
+
}>;
|