@piying/view-angular-core 1.7.8 → 1.8.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.
|
@@ -1,135 +1,795 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import rfdc from 'rfdc';
|
|
1
|
+
import { isString, uniq, isBoolean, isUndefined, isNil, intersection, union, difference } from 'es-toolkit';
|
|
3
2
|
import * as v from 'valibot';
|
|
4
3
|
import * as jsonActions from '@piying/view-angular-core';
|
|
5
|
-
import { hideWhen,
|
|
6
|
-
import {
|
|
7
|
-
import { BehaviorSubject,
|
|
4
|
+
import { hideWhen, patchInputs, asControl, setComponent } from '@piying/view-angular-core';
|
|
5
|
+
import { computed } from '@angular/core';
|
|
6
|
+
import { merge, map, BehaviorSubject, switchMap, combineLatest } from 'rxjs';
|
|
8
7
|
import { schema } from '@piying/valibot-visit';
|
|
9
|
-
import
|
|
8
|
+
import rfdc from 'rfdc';
|
|
10
9
|
|
|
11
|
-
function createImpasseAction(key, value) {
|
|
12
|
-
return v.rawCheck(({ dataset, addIssue }) => {
|
|
13
|
-
if (dataset.issues) {
|
|
14
|
-
return;
|
|
15
|
-
}
|
|
16
|
-
addIssue({
|
|
17
|
-
label: `impasse:${key}`,
|
|
18
|
-
expected: '[no validation conflict]',
|
|
19
|
-
received: value,
|
|
20
|
-
});
|
|
21
|
-
});
|
|
22
|
-
}
|
|
23
10
|
function isNumber(value) {
|
|
24
11
|
return typeof value === 'number';
|
|
25
12
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
13
|
+
|
|
14
|
+
var ChildKind;
|
|
15
|
+
(function (ChildKind) {
|
|
16
|
+
ChildKind[ChildKind["child"] = 0] = "child";
|
|
17
|
+
ChildKind[ChildKind["condition"] = 1] = "condition";
|
|
18
|
+
})(ChildKind || (ChildKind = {}));
|
|
19
|
+
class TypeContext {
|
|
20
|
+
parent;
|
|
21
|
+
parentObject;
|
|
22
|
+
level = 0;
|
|
23
|
+
instance;
|
|
24
|
+
}
|
|
25
|
+
class BaseTypeService {
|
|
26
|
+
static index = 0;
|
|
27
|
+
context = new TypeContext();
|
|
28
|
+
parent;
|
|
29
|
+
instance;
|
|
30
|
+
schema;
|
|
31
|
+
name;
|
|
32
|
+
index = 0;
|
|
33
|
+
instanceNamePrefix$$ = computed(() => `${this.index}-type`);
|
|
34
|
+
constructor(instance, schema) {
|
|
35
|
+
this.context.instance = this;
|
|
36
|
+
this.instance = instance;
|
|
37
|
+
this.schema = schema;
|
|
41
38
|
}
|
|
42
|
-
|
|
43
|
-
|
|
39
|
+
getAllActionList(actionList) {
|
|
40
|
+
return [...actionList, ...this.getExtraActionList()];
|
|
44
41
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
42
|
+
isOptional() {
|
|
43
|
+
return this.schema.__resolved.type.optional;
|
|
44
|
+
}
|
|
45
|
+
parseBase(actionList) {
|
|
46
|
+
const Define = v.pipe(this.getBaseDefine(), ...this.getAllActionList(actionList));
|
|
47
|
+
const OptDefine = this.isOptional()
|
|
48
|
+
? v.optional(Define, this.schema.default)
|
|
49
|
+
: Define;
|
|
50
|
+
return OptDefine;
|
|
51
|
+
}
|
|
52
|
+
parse(actionList) {
|
|
53
|
+
const OptDefine = this.parseBase(actionList);
|
|
54
|
+
return (this.instance.options?.schemaHandle?.type?.afterResolve(OptDefine, this.schema, this.name) ?? OptDefine);
|
|
55
|
+
}
|
|
56
|
+
getBaseDefine() {
|
|
57
|
+
throw new Error('');
|
|
58
|
+
}
|
|
59
|
+
getValidationActionList(schema) {
|
|
60
|
+
const action = [];
|
|
61
|
+
// string/array
|
|
62
|
+
if (isNumber(schema.minLength) || isNumber(schema.minItems)) {
|
|
63
|
+
action.push(v.minLength(schema.minLength ?? schema.minItems));
|
|
64
|
+
}
|
|
65
|
+
// string/array
|
|
66
|
+
if (isNumber(schema.maxLength) || isNumber(schema.maxItems)) {
|
|
67
|
+
action.push(v.maxLength(schema.maxLength ?? schema.maxItems));
|
|
68
|
+
}
|
|
69
|
+
// string
|
|
70
|
+
if (isString(schema.pattern)) {
|
|
71
|
+
action.push(v.regex(new RegExp(schema.pattern)));
|
|
72
|
+
}
|
|
73
|
+
// todo format https://json-schema.org/understanding-json-schema/reference/type#built-in-formats
|
|
74
|
+
// duration idn-email idn-hostname uri-reference iri iri-reference uri-template json-pointer regex
|
|
75
|
+
if (schema.format) {
|
|
76
|
+
switch (schema.format) {
|
|
77
|
+
// case 'date-time': {
|
|
78
|
+
// action.push(v.isoDateTime());
|
|
79
|
+
// break;
|
|
80
|
+
// }
|
|
81
|
+
// case 'time': {
|
|
82
|
+
// action.push(v.isoTime());
|
|
83
|
+
// break;
|
|
84
|
+
// }
|
|
85
|
+
case 'date': {
|
|
86
|
+
action.push(v.isoDate());
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
case 'email': {
|
|
90
|
+
action.push(v.email());
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
case 'ipv4': {
|
|
94
|
+
action.push(v.ipv4());
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
case 'ipv6': {
|
|
98
|
+
action.push(v.ipv6());
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
101
|
+
case 'uuid': {
|
|
102
|
+
action.push(v.uuid());
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
case 'uri': {
|
|
106
|
+
action.push(v.url());
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
default:
|
|
110
|
+
break;
|
|
58
111
|
}
|
|
59
|
-
|
|
60
|
-
|
|
112
|
+
}
|
|
113
|
+
// number
|
|
114
|
+
if (isNumber(schema.exclusiveMinimum)) {
|
|
115
|
+
action.push(v.gtValue(schema.exclusiveMinimum));
|
|
116
|
+
}
|
|
117
|
+
if (isNumber(schema.exclusiveMaximum)) {
|
|
118
|
+
action.push(v.ltValue(schema.exclusiveMaximum));
|
|
119
|
+
}
|
|
120
|
+
if (isNumber(schema.minimum)) {
|
|
121
|
+
action.push(v.minValue(schema.minimum));
|
|
122
|
+
}
|
|
123
|
+
if (isNumber(schema.maximum)) {
|
|
124
|
+
action.push(v.maxValue(schema.maximum));
|
|
125
|
+
}
|
|
126
|
+
// number
|
|
127
|
+
if (isNumber(schema.multipleOf)) {
|
|
128
|
+
action.push(v.multipleOf(schema.multipleOf));
|
|
129
|
+
}
|
|
130
|
+
// array
|
|
131
|
+
if (schema.uniqueItems) {
|
|
132
|
+
action.push(v.check((input) => uniq(input).length === input.length));
|
|
133
|
+
}
|
|
134
|
+
// object
|
|
135
|
+
if (isNumber(schema.maxProperties)) {
|
|
136
|
+
action.push(v.maxEntries(schema.maxProperties));
|
|
137
|
+
}
|
|
138
|
+
// object
|
|
139
|
+
if (isNumber(schema.minProperties)) {
|
|
140
|
+
action.push(v.minEntries(schema.minProperties));
|
|
141
|
+
}
|
|
142
|
+
if (schema.actions) {
|
|
143
|
+
for (const rawAction of schema.actions) {
|
|
144
|
+
const inlineActions = jsonActions[rawAction.name] ??
|
|
145
|
+
this.instance.options?.customActions?.[rawAction.name];
|
|
146
|
+
if (!inlineActions) {
|
|
147
|
+
throw new Error(`action:[${rawAction.name}]❗`);
|
|
148
|
+
}
|
|
149
|
+
action.push(inlineActions(...rawAction.params));
|
|
61
150
|
}
|
|
62
151
|
}
|
|
63
|
-
return
|
|
64
|
-
|
|
65
|
-
|
|
152
|
+
return action;
|
|
153
|
+
}
|
|
154
|
+
getExtraActionList() {
|
|
155
|
+
return [];
|
|
156
|
+
}
|
|
157
|
+
typeParse(type, schema, actionList, options) {
|
|
158
|
+
const instance = this.getTypeParse(type, schema, options);
|
|
159
|
+
return instance.parse(actionList);
|
|
160
|
+
}
|
|
161
|
+
getTypeParse(type, schema, options) {
|
|
162
|
+
const Parser = this.instance.getTypeParser(type);
|
|
163
|
+
const instance = new Parser(this.instance, schema);
|
|
164
|
+
instance.index = ++BaseTypeService.index;
|
|
165
|
+
instance.context.parent = this.context;
|
|
166
|
+
if (instance.context.level) {
|
|
167
|
+
instance.context.parentObject = this.context.parentObject;
|
|
168
|
+
}
|
|
169
|
+
if (options?.kind === ChildKind.child) {
|
|
170
|
+
if (instance.context.level === 0) {
|
|
171
|
+
instance.context.parentObject = instance.context;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
else if (options?.kind === ChildKind.condition) {
|
|
175
|
+
instance.context.level++;
|
|
176
|
+
instance.context.parentObject = this.context.parentObject ?? this.context;
|
|
177
|
+
}
|
|
178
|
+
return instance;
|
|
179
|
+
}
|
|
180
|
+
commonTypeParse(schema, options) {
|
|
181
|
+
// const resolved = this.resolveSchema2(schema as any);
|
|
182
|
+
return this.typeParse('common', schema, [], options);
|
|
183
|
+
}
|
|
184
|
+
resolveSchema2(schema) {
|
|
185
|
+
return this.instance.resolveSchema2(schema);
|
|
186
|
+
}
|
|
187
|
+
schemahasRef(schema) {
|
|
188
|
+
if (isBoolean(schema)) {
|
|
189
|
+
return false;
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
const result = this.resolveSchema2(schema);
|
|
193
|
+
return result.__resolved.hasRef;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
getFixedChild() {
|
|
197
|
+
let context = this.context;
|
|
198
|
+
let childObject = {};
|
|
199
|
+
while (context?.level) {
|
|
200
|
+
context = context.parentObject;
|
|
201
|
+
const instance = context?.instance;
|
|
202
|
+
childObject = { ...childObject, ...instance.childObject };
|
|
203
|
+
}
|
|
204
|
+
return childObject;
|
|
205
|
+
}
|
|
206
|
+
getFixedChildBy(key) {
|
|
207
|
+
let context = this.context;
|
|
208
|
+
while (context?.level) {
|
|
209
|
+
context = context.parentObject;
|
|
210
|
+
const instance = context?.instance;
|
|
211
|
+
if (instance.name === 'object') {
|
|
212
|
+
const maybeName = instance.getFixedChildPath(key);
|
|
213
|
+
if (maybeName) {
|
|
214
|
+
return maybeName;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
throw new Error('');
|
|
66
219
|
}
|
|
67
|
-
return { value: a ?? b };
|
|
68
220
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
221
|
+
|
|
222
|
+
class NumberTypeService extends BaseTypeService {
|
|
223
|
+
name = 'number';
|
|
224
|
+
getBaseDefine() {
|
|
225
|
+
return v.number();
|
|
226
|
+
}
|
|
72
227
|
}
|
|
73
|
-
|
|
74
|
-
class
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
constructor(root, options) {
|
|
79
|
-
this.root = root;
|
|
80
|
-
this.#options = options;
|
|
81
|
-
root.$schema ??= Schema2012;
|
|
228
|
+
|
|
229
|
+
class IntegerTypeService extends NumberTypeService {
|
|
230
|
+
name = 'integer';
|
|
231
|
+
getBaseDefine() {
|
|
232
|
+
return v.number();
|
|
82
233
|
}
|
|
83
|
-
|
|
84
|
-
return
|
|
234
|
+
getExtraActionList() {
|
|
235
|
+
return [v.integer()];
|
|
85
236
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
class BooleanTypeService extends BaseTypeService {
|
|
240
|
+
name = 'boolean';
|
|
241
|
+
getBaseDefine() {
|
|
242
|
+
return v.boolean();
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
class StringTypeService extends BaseTypeService {
|
|
247
|
+
name = 'string';
|
|
248
|
+
getBaseDefine() {
|
|
249
|
+
return v.string();
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
class NullTypeService extends BaseTypeService {
|
|
254
|
+
name = 'number';
|
|
255
|
+
parseBase(actionList) {
|
|
256
|
+
const Define = v.pipe(v.optional(v.null(), null), ...this.getAllActionList(actionList));
|
|
257
|
+
return Define;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
class ConstTypeService extends BaseTypeService {
|
|
262
|
+
name = 'const';
|
|
263
|
+
parseBase(actionList) {
|
|
264
|
+
const value = this.schema.const;
|
|
265
|
+
const Define = v.pipe(v.optional(v.literal(value), value), ...this.getAllActionList(actionList));
|
|
266
|
+
return Define;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
class ObjectTypeService extends BaseTypeService {
|
|
271
|
+
name = 'object';
|
|
272
|
+
actionList = [];
|
|
273
|
+
#optional = false;
|
|
274
|
+
childObject = {};
|
|
275
|
+
fixedActionList = [];
|
|
276
|
+
// fixedObjName!: string;
|
|
277
|
+
isOptional() {
|
|
278
|
+
return super.isOptional() || this.#optional;
|
|
279
|
+
}
|
|
280
|
+
getExtraActionList() {
|
|
281
|
+
return this.actionList;
|
|
282
|
+
}
|
|
283
|
+
getFixedChildPath(key) {
|
|
284
|
+
if (key in this.childObject) {
|
|
285
|
+
return this.instanceNamePrefix$$();
|
|
91
286
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
287
|
+
return undefined;
|
|
288
|
+
}
|
|
289
|
+
getBaseDefine() {
|
|
290
|
+
const fixedObjName = `${this.instanceNamePrefix$$()}-fixed`;
|
|
291
|
+
const actionList = this.actionList;
|
|
292
|
+
const schema$1 = this.schema;
|
|
293
|
+
const childObject = {};
|
|
294
|
+
this.childObject = childObject;
|
|
295
|
+
/** 附加 */
|
|
296
|
+
let defaultRest;
|
|
297
|
+
let mode = 'default';
|
|
298
|
+
if (schema$1.dependentRequired) {
|
|
299
|
+
actionList.push(v.rawCheck(({ dataset, addIssue }) => {
|
|
300
|
+
if (dataset.issues) {
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
Object.keys(schema$1.dependentRequired).forEach((key) => {
|
|
304
|
+
if (dataset.value?.[key] !== undefined) {
|
|
305
|
+
for (const reqKey of schema$1.dependentRequired[key]) {
|
|
306
|
+
if (dataset.value[reqKey] === undefined) {
|
|
307
|
+
addIssue({
|
|
308
|
+
label: `dependentRequired:${key}=>${reqKey}`,
|
|
309
|
+
expected: '[required]',
|
|
310
|
+
received: 'undefined',
|
|
311
|
+
});
|
|
112
312
|
}
|
|
113
|
-
return result.success;
|
|
114
|
-
});
|
|
115
|
-
if (!hasSuccess) {
|
|
116
|
-
const extMessage = childFailedResult?.issues
|
|
117
|
-
?.map((item) => item.message)
|
|
118
|
-
.join(',') ?? '';
|
|
119
|
-
addIssue({
|
|
120
|
-
label: `anyOf:${childFailedResult?.index ?? ''}:${extMessage}`,
|
|
121
|
-
});
|
|
122
313
|
}
|
|
314
|
+
}
|
|
315
|
+
});
|
|
316
|
+
}));
|
|
317
|
+
}
|
|
318
|
+
let hasRequiredKey = false;
|
|
319
|
+
// 普通属性
|
|
320
|
+
if (schema$1.properties) {
|
|
321
|
+
for (const key in schema$1.properties) {
|
|
322
|
+
const propJSchema = schema$1.properties[key];
|
|
323
|
+
let propData;
|
|
324
|
+
if (isBoolean(propJSchema)) {
|
|
325
|
+
propData = { optional: false, hasRef: false };
|
|
326
|
+
}
|
|
327
|
+
else {
|
|
328
|
+
const rSchema = this.resolveSchema2(propJSchema);
|
|
329
|
+
propData = {
|
|
330
|
+
optional: rSchema.__resolved.type.optional,
|
|
331
|
+
hasRef: rSchema.__resolved.hasRef,
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
const isRequired = !!schema$1.required?.includes(key);
|
|
335
|
+
const wrapperOptional = !isRequired && !propData.optional;
|
|
336
|
+
if (isRequired && !propData.optional) {
|
|
337
|
+
hasRequiredKey = true;
|
|
338
|
+
}
|
|
339
|
+
const createRef = () => {
|
|
340
|
+
let propVSchema = this.commonTypeParse(propJSchema, {
|
|
341
|
+
kind: ChildKind.child,
|
|
123
342
|
});
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
343
|
+
const depList = schema$1.dependentRequired?.[key];
|
|
344
|
+
if (depList) {
|
|
345
|
+
propVSchema = v.pipe(propVSchema, jsonActions.patchHooks({
|
|
346
|
+
allFieldsResolved: (field) => {
|
|
347
|
+
field.form.control.statusChanges.subscribe(() => {
|
|
348
|
+
const valid = field.form.control.valid;
|
|
349
|
+
depList.map((item) => {
|
|
350
|
+
field.form.parent.get(item)?.config$.update((config) => ({
|
|
351
|
+
...config,
|
|
352
|
+
required: valid,
|
|
353
|
+
}));
|
|
354
|
+
});
|
|
355
|
+
});
|
|
356
|
+
},
|
|
357
|
+
}));
|
|
358
|
+
}
|
|
359
|
+
return propVSchema;
|
|
360
|
+
};
|
|
361
|
+
childObject[key] = propData.hasRef
|
|
362
|
+
? wrapperOptional
|
|
363
|
+
? v.optional(v.lazy(() => createRef()))
|
|
364
|
+
: v.lazy(() => createRef())
|
|
365
|
+
: wrapperOptional
|
|
366
|
+
? v.optional(createRef())
|
|
367
|
+
: createRef();
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
// 附加属性规则
|
|
371
|
+
if (isBoolean(schema$1.additionalProperties)) {
|
|
372
|
+
mode = schema$1.additionalProperties === false ? 'strict' : mode;
|
|
373
|
+
}
|
|
374
|
+
else if (schema$1.additionalProperties) {
|
|
375
|
+
mode = 'rest';
|
|
376
|
+
// rest要符合的规则
|
|
377
|
+
defaultRest = this.commonTypeParse(schema$1.additionalProperties, {
|
|
378
|
+
kind: ChildKind.condition,
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
const patternRestList = [];
|
|
382
|
+
if (schema$1.patternProperties) {
|
|
383
|
+
for (const key in schema$1.patternProperties) {
|
|
384
|
+
const item = this.commonTypeParse(schema$1.patternProperties[key], {
|
|
385
|
+
kind: ChildKind.condition,
|
|
386
|
+
});
|
|
387
|
+
if (!item) {
|
|
388
|
+
throw new Error(`patternProperties->${key}: 定义未找到`);
|
|
389
|
+
}
|
|
390
|
+
patternRestList.push({
|
|
391
|
+
regexp: new RegExp(key),
|
|
392
|
+
schema: item,
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
/** 条件显示 */
|
|
397
|
+
const conditionList = [];
|
|
398
|
+
if (schema$1.dependentSchemas) {
|
|
399
|
+
const depSchemaMap = {};
|
|
400
|
+
for (const key in schema$1.dependentSchemas) {
|
|
401
|
+
const jSchema = schema$1.dependentSchemas[key];
|
|
402
|
+
let vSchema = this.commonTypeParse(jSchema, {
|
|
403
|
+
kind: ChildKind.condition,
|
|
404
|
+
});
|
|
405
|
+
if (!vSchema) {
|
|
406
|
+
throw new Error(`依赖->${key}: 定义未找到`);
|
|
407
|
+
}
|
|
408
|
+
depSchemaMap[key] = vSchema;
|
|
409
|
+
vSchema = v.pipe(vSchema, jsonActions.renderConfig({ hidden: true }), hideWhen({
|
|
410
|
+
disabled: true,
|
|
411
|
+
listen: (fn, field) => {
|
|
412
|
+
const controlField = field.get(['..', '..', 0, key]).form
|
|
413
|
+
.control;
|
|
414
|
+
return merge(controlField.valueChanges, controlField.statusChanges).pipe(map(() => !(controlField.valid && controlField.value !== undefined)));
|
|
415
|
+
},
|
|
416
|
+
}));
|
|
417
|
+
conditionList.push(vSchema);
|
|
418
|
+
}
|
|
419
|
+
actionList.push(v.rawCheck(({ dataset, addIssue }) => {
|
|
420
|
+
if (dataset.issues) {
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
Object.keys(schema$1.dependentSchemas).forEach((key) => {
|
|
424
|
+
if (dataset.value?.[key] !== undefined) {
|
|
425
|
+
const result = v.safeParse(depSchemaMap[key], dataset.value);
|
|
426
|
+
if (!result.success) {
|
|
427
|
+
for (const item of result.issues) {
|
|
428
|
+
addIssue({
|
|
429
|
+
...item,
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
});
|
|
435
|
+
}));
|
|
436
|
+
}
|
|
437
|
+
if (isBoolean(schema$1.propertyNames) && !schema$1.propertyNames) {
|
|
438
|
+
actionList.push(v.check(() => false));
|
|
439
|
+
}
|
|
440
|
+
else if (schema$1.propertyNames) {
|
|
441
|
+
const propNameSchema = this.commonTypeParse(schema$1.propertyNames);
|
|
442
|
+
actionList.push(v.rawCheck(({ dataset, addIssue }) => {
|
|
443
|
+
if (dataset.issues) {
|
|
444
|
+
return;
|
|
445
|
+
}
|
|
446
|
+
if (dataset.value && typeof dataset.value === 'object') {
|
|
447
|
+
for (const key of Object.keys(dataset.value)) {
|
|
448
|
+
const result = v.safeParse(propNameSchema, key);
|
|
449
|
+
if (!result.success) {
|
|
450
|
+
addIssue({
|
|
451
|
+
label: `propertyNames:${key}`,
|
|
452
|
+
expected: `[match]`,
|
|
453
|
+
received: key,
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
}));
|
|
459
|
+
}
|
|
460
|
+
let schemaDefine;
|
|
461
|
+
if (!Object.keys(childObject).length || !hasRequiredKey) {
|
|
462
|
+
this.#optional = true;
|
|
463
|
+
}
|
|
464
|
+
if (mode === 'default') {
|
|
465
|
+
if (conditionList.length) {
|
|
466
|
+
schemaDefine = schema.intersect([
|
|
467
|
+
v.pipe(v.looseObject(childObject), jsonActions.setAlias(fixedObjName)),
|
|
468
|
+
v.optional(v.intersect(conditionList)),
|
|
469
|
+
]);
|
|
470
|
+
}
|
|
471
|
+
else {
|
|
472
|
+
schemaDefine = v.pipe(v.looseObject(childObject), jsonActions.setAlias(fixedObjName));
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
else if (mode === 'strict') {
|
|
476
|
+
if (conditionList.length) {
|
|
477
|
+
schemaDefine = v.pipe(schema.intersect([
|
|
478
|
+
v.pipe(v.object(childObject), jsonActions.setAlias(fixedObjName)),
|
|
479
|
+
v.optional(v.intersect(conditionList)),
|
|
480
|
+
]));
|
|
481
|
+
}
|
|
482
|
+
else {
|
|
483
|
+
schemaDefine = v.pipe(v.object(childObject), jsonActions.setAlias(fixedObjName));
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
else {
|
|
487
|
+
// rest
|
|
488
|
+
let restDefine = v.any();
|
|
489
|
+
//propCheck patternMapRest addonRest
|
|
490
|
+
if (defaultRest && !patternRestList.length) {
|
|
491
|
+
restDefine = defaultRest;
|
|
492
|
+
}
|
|
493
|
+
else {
|
|
494
|
+
restDefine = v.any();
|
|
495
|
+
}
|
|
496
|
+
if (conditionList.length) {
|
|
497
|
+
schemaDefine = v.intersect([
|
|
498
|
+
v.pipe(v.objectWithRest(childObject, restDefine), jsonActions.setAlias(fixedObjName)),
|
|
499
|
+
v.optional(v.intersect(conditionList)),
|
|
500
|
+
]);
|
|
501
|
+
}
|
|
502
|
+
else {
|
|
503
|
+
schemaDefine = v.pipe(v.objectWithRest(childObject, restDefine), jsonActions.setAlias(fixedObjName));
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
if (schema$1.patternProperties && schema$1.additionalProperties) {
|
|
507
|
+
actionList.push(v.rawCheck(({ dataset, addIssue }) => {
|
|
508
|
+
if (dataset.issues || dataset.value === undefined) {
|
|
509
|
+
return;
|
|
510
|
+
}
|
|
511
|
+
if (typeof dataset.value === 'object') {
|
|
512
|
+
datasetLoop: for (const key in dataset.value) {
|
|
513
|
+
if (key in childObject) {
|
|
514
|
+
continue;
|
|
515
|
+
}
|
|
516
|
+
for (const { regexp, schema } of patternRestList) {
|
|
517
|
+
const isMatch = regexp.test(key);
|
|
518
|
+
if (!isMatch) {
|
|
519
|
+
continue;
|
|
520
|
+
}
|
|
521
|
+
const result = v.safeParse(schema, dataset.value[key]);
|
|
522
|
+
if (!result.success) {
|
|
523
|
+
addIssue();
|
|
524
|
+
}
|
|
525
|
+
continue datasetLoop;
|
|
526
|
+
}
|
|
527
|
+
if (defaultRest) {
|
|
528
|
+
const result = v.safeParse(defaultRest, dataset.value[key]);
|
|
529
|
+
if (!result.success) {
|
|
530
|
+
addIssue();
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
}));
|
|
536
|
+
}
|
|
537
|
+
return schemaDefine;
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
function createImpasseAction(key, value) {
|
|
542
|
+
return v.rawCheck(({ dataset, addIssue }) => {
|
|
543
|
+
if (dataset.issues) {
|
|
544
|
+
return;
|
|
545
|
+
}
|
|
546
|
+
addIssue({
|
|
547
|
+
label: `impasse:${key}`,
|
|
548
|
+
expected: '[no validation conflict]',
|
|
549
|
+
received: value,
|
|
550
|
+
});
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
class ArrayTypeService extends BaseTypeService {
|
|
555
|
+
name = 'array';
|
|
556
|
+
actionList = [];
|
|
557
|
+
getExtraActionList() {
|
|
558
|
+
return this.actionList;
|
|
559
|
+
}
|
|
560
|
+
getBaseDefine() {
|
|
561
|
+
const actionList = this.actionList;
|
|
562
|
+
const schema = this.schema;
|
|
563
|
+
let parent;
|
|
564
|
+
const fixedItems = schema.prefixItems;
|
|
565
|
+
if (isBoolean(schema.contains) && !schema.contains) {
|
|
566
|
+
actionList.push(createImpasseAction('contains', schema.contains));
|
|
567
|
+
}
|
|
568
|
+
else if (schema.contains && !isBoolean(schema.contains)) {
|
|
569
|
+
const containsSchema = this.commonTypeParse(schema.contains);
|
|
570
|
+
const minContains = schema.minContains ?? 1;
|
|
571
|
+
actionList.push(v.check((list) => {
|
|
572
|
+
if (Array.isArray(list)) {
|
|
573
|
+
const result = list.filter((item) => v.safeParse(containsSchema, item).success);
|
|
574
|
+
if (result.length < minContains) {
|
|
575
|
+
return false;
|
|
576
|
+
}
|
|
577
|
+
if (typeof schema.maxContains === 'number') {
|
|
578
|
+
return result.length <= schema.maxContains;
|
|
579
|
+
}
|
|
580
|
+
return true;
|
|
581
|
+
}
|
|
582
|
+
return false;
|
|
583
|
+
}));
|
|
584
|
+
}
|
|
585
|
+
const jSchemaToVSchema = (schema) => {
|
|
586
|
+
const hasRef = this.schemahasRef(schema);
|
|
587
|
+
return hasRef
|
|
588
|
+
? v.lazy(() => this.commonTypeParse(schema))
|
|
589
|
+
: this.commonTypeParse(schema);
|
|
590
|
+
};
|
|
591
|
+
if (fixedItems && fixedItems.length) {
|
|
592
|
+
const fixedList = fixedItems.map((item) => jSchemaToVSchema(item));
|
|
593
|
+
if (schema.items) {
|
|
594
|
+
const result = jSchemaToVSchema(schema.items);
|
|
595
|
+
parent = v.tupleWithRest(fixedList, result);
|
|
596
|
+
}
|
|
597
|
+
else if (schema.items === false) {
|
|
598
|
+
parent = v.tuple(fixedList);
|
|
599
|
+
}
|
|
600
|
+
else {
|
|
601
|
+
parent = v.looseTuple(fixedList);
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
else if (isBoolean(schema.items)) {
|
|
605
|
+
parent = schema.items ? v.array(v.any()) : v.tuple([]);
|
|
606
|
+
}
|
|
607
|
+
else if (schema.items) {
|
|
608
|
+
const result = jSchemaToVSchema(schema.items);
|
|
609
|
+
parent = v.array(result);
|
|
610
|
+
}
|
|
611
|
+
else {
|
|
612
|
+
parent = v.array(v.any());
|
|
613
|
+
}
|
|
614
|
+
return parent;
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
const alwaysTrueDefine = v.pipe(v.any(), jsonActions.setComponent('always-true'));
|
|
619
|
+
const alwaysFalseDefine = v.pipe(v.any(), v.check((value) => isUndefined(value)));
|
|
620
|
+
function getBooleanDefine(value) {
|
|
621
|
+
return value ? alwaysTrueDefine : alwaysFalseDefine;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
const clone = rfdc({ proto: false, circles: false });
|
|
625
|
+
|
|
626
|
+
function toFixedList(data, labelFn = (a) => a) {
|
|
627
|
+
return data.map((item) => {
|
|
628
|
+
return { label: labelFn(item), value: item };
|
|
629
|
+
});
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
function arrayIntersection(a, b) {
|
|
633
|
+
if (!isNil(a) && !isNil(b)) {
|
|
634
|
+
a = Array.isArray(a) ? a : [a];
|
|
635
|
+
b = Array.isArray(b) ? b : [b];
|
|
636
|
+
if (a.length && b.length) {
|
|
637
|
+
const result = intersection(a, b);
|
|
638
|
+
if (result.length === 0) {
|
|
639
|
+
return {
|
|
640
|
+
action: createImpasseAction('applicator'),
|
|
641
|
+
value: undefined,
|
|
642
|
+
};
|
|
643
|
+
}
|
|
644
|
+
else {
|
|
645
|
+
return { value: result };
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
return {
|
|
649
|
+
value: a.length ? a : b,
|
|
650
|
+
};
|
|
651
|
+
}
|
|
652
|
+
return { value: a ?? b };
|
|
653
|
+
}
|
|
654
|
+
function getMetadataAction(schema) {
|
|
655
|
+
const action = [];
|
|
656
|
+
if (isString(schema.title)) {
|
|
657
|
+
action.push(v.title(schema.title));
|
|
658
|
+
}
|
|
659
|
+
if (isString(schema.description)) {
|
|
660
|
+
action.push(v.description(schema.description));
|
|
661
|
+
}
|
|
662
|
+
return action;
|
|
663
|
+
}
|
|
664
|
+
class CommonTypeService extends BaseTypeService {
|
|
665
|
+
name = 'common';
|
|
666
|
+
cacheSchema = new WeakMap();
|
|
667
|
+
parse(actionList) {
|
|
668
|
+
return this.jSchemaToVSchema2(this.schema);
|
|
669
|
+
}
|
|
670
|
+
jSchemaToVSchema2(input) {
|
|
671
|
+
if (isBoolean(input)) {
|
|
672
|
+
return getBooleanDefine(input);
|
|
673
|
+
}
|
|
674
|
+
if (this.cacheSchema.has(input)) {
|
|
675
|
+
return this.cacheSchema.get(input);
|
|
676
|
+
}
|
|
677
|
+
const schema = this.resolveSchema2(input);
|
|
678
|
+
this.schema = schema;
|
|
679
|
+
const actionList = this.#applicatorNot(schema);
|
|
680
|
+
const result = actionList.length
|
|
681
|
+
? v.pipe(this.#applicatorParse(schema), ...actionList)
|
|
682
|
+
: this.#applicatorParse(schema);
|
|
683
|
+
this.cacheSchema.set(input, result);
|
|
684
|
+
return result;
|
|
685
|
+
}
|
|
686
|
+
#applicatorNot(schema) {
|
|
687
|
+
const actionList = [];
|
|
688
|
+
if (isBoolean(schema.not)) {
|
|
689
|
+
if (schema.not) {
|
|
690
|
+
actionList.push(createImpasseAction('not', schema.not));
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
else if (schema.not) {
|
|
694
|
+
const vSchema = this.#jsonSchemaBase(this.resolveSchema2(schema.not), () => []);
|
|
695
|
+
actionList.push(v.rawCheck(({ dataset, addIssue }) => {
|
|
696
|
+
if (dataset.issues) {
|
|
697
|
+
return;
|
|
698
|
+
}
|
|
699
|
+
const result = v.safeParse(vSchema, dataset.value);
|
|
700
|
+
if (result.success) {
|
|
701
|
+
addIssue({ label: `applicator:not` });
|
|
702
|
+
}
|
|
703
|
+
}));
|
|
704
|
+
}
|
|
705
|
+
return actionList;
|
|
706
|
+
}
|
|
707
|
+
#jsonSchemaBase(schema, getValidationActionList) {
|
|
708
|
+
const types = schema.__resolved.type;
|
|
709
|
+
// 暂时为只支持一个
|
|
710
|
+
const type = types.types[0];
|
|
711
|
+
const actionList = getMetadataAction(schema);
|
|
712
|
+
switch (type) {
|
|
713
|
+
case 'picklist': {
|
|
714
|
+
return this.typeParse('__fixedList', {
|
|
715
|
+
type: '__fixedList',
|
|
716
|
+
data: { options: [toFixedList(schema.enum)], multi: false },
|
|
717
|
+
}, [...getValidationActionList(), ...actionList]);
|
|
718
|
+
}
|
|
719
|
+
case 'const':
|
|
720
|
+
case '__fixedList':
|
|
721
|
+
case 'object':
|
|
722
|
+
case 'null':
|
|
723
|
+
case 'string':
|
|
724
|
+
case 'boolean':
|
|
725
|
+
case 'integer':
|
|
726
|
+
case 'number': {
|
|
727
|
+
return this.typeParse(type, schema, [
|
|
728
|
+
...getValidationActionList(),
|
|
729
|
+
...actionList,
|
|
730
|
+
]);
|
|
731
|
+
}
|
|
732
|
+
case 'array': {
|
|
733
|
+
if (schema.items && !isBoolean(schema.items)) {
|
|
734
|
+
let result = this.getOptions([this.resolveSchema2(schema)]);
|
|
735
|
+
if (result) {
|
|
736
|
+
return this.typeParse('__fixedList', { type: '__fixedList', data: result }, [...getValidationActionList(), ...actionList]);
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
return this.typeParse(type, schema, [
|
|
740
|
+
...getValidationActionList(),
|
|
741
|
+
...actionList,
|
|
742
|
+
]);
|
|
743
|
+
}
|
|
744
|
+
default:
|
|
745
|
+
throw new Error(`未知类型:${type}`);
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
#applicatorParse(schema$1) {
|
|
749
|
+
let vSchema;
|
|
750
|
+
if (schema$1.allOf) {
|
|
751
|
+
const result = this.#mergeSchema(schema$1, ...schema$1.allOf);
|
|
752
|
+
vSchema = v.pipe(this.#jsonSchemaBase(result.schema, () => result.actionList));
|
|
753
|
+
}
|
|
754
|
+
else if (schema$1.anyOf) {
|
|
755
|
+
vSchema = this.#conditionCreate(schema$1, {
|
|
756
|
+
useOr: false,
|
|
757
|
+
getChildren: () => schema$1.anyOf,
|
|
758
|
+
conditionCheckActionFn(childOriginSchemaList, getActivateList) {
|
|
759
|
+
return v.rawCheck(({ dataset, addIssue }) => {
|
|
760
|
+
if (dataset.issues) {
|
|
761
|
+
return;
|
|
762
|
+
}
|
|
763
|
+
let childFailedResult;
|
|
764
|
+
// 验证项全为可选,所以需要这里再次验证
|
|
765
|
+
const hasSuccess = childOriginSchemaList.some((item, index) => {
|
|
766
|
+
const isActive = getActivateList()[index];
|
|
767
|
+
if (!isActive) {
|
|
768
|
+
childFailedResult ??= { index: index };
|
|
769
|
+
return false;
|
|
770
|
+
}
|
|
771
|
+
const result = v.safeParse(item, dataset.value);
|
|
772
|
+
if (!result.success) {
|
|
773
|
+
childFailedResult = { index: index, issues: result.issues };
|
|
774
|
+
}
|
|
775
|
+
return result.success;
|
|
776
|
+
});
|
|
777
|
+
if (!hasSuccess) {
|
|
778
|
+
const extMessage = childFailedResult?.issues
|
|
779
|
+
?.map((item) => item.message)
|
|
780
|
+
.join(',') ?? '';
|
|
781
|
+
addIssue({
|
|
782
|
+
label: `anyOf:${childFailedResult?.index ?? ''}:${extMessage}`,
|
|
783
|
+
});
|
|
784
|
+
}
|
|
785
|
+
});
|
|
786
|
+
},
|
|
787
|
+
conditionSchemaFn: (baseSchema, conditionVSchema, childSchemaList) => v.pipe(v.intersect([
|
|
788
|
+
conditionVSchema,
|
|
789
|
+
baseSchema,
|
|
790
|
+
v.pipe(schema.intersect(childSchemaList.map((item) => v.pipe(v.optional(item), jsonActions.renderConfig({ hidden: true })))), jsonActions.setAlias(`${this.instanceNamePrefix$$()}-cond-display`)),
|
|
791
|
+
]), jsonActions.setComponent('anyOf-condition')),
|
|
792
|
+
});
|
|
133
793
|
}
|
|
134
794
|
else if (schema$1.oneOf) {
|
|
135
795
|
vSchema = this.#conditionCreate(schema$1, {
|
|
@@ -154,8 +814,8 @@ class JsonSchemaToValibot {
|
|
|
154
814
|
if (hasSuccess.length !== 1) {
|
|
155
815
|
addIssue({
|
|
156
816
|
label: `oneOf`,
|
|
157
|
-
expected: '1',
|
|
158
|
-
received: `${hasSuccess.length}
|
|
817
|
+
expected: '1📏',
|
|
818
|
+
received: `${hasSuccess.length}📏`,
|
|
159
819
|
});
|
|
160
820
|
}
|
|
161
821
|
});
|
|
@@ -176,8 +836,8 @@ class JsonSchemaToValibot {
|
|
|
176
836
|
*/
|
|
177
837
|
const useThen$ = new BehaviorSubject(undefined);
|
|
178
838
|
const baseSchema = v.pipe(this.#jsonSchemaBase(schema$1, () => [
|
|
179
|
-
...this
|
|
180
|
-
hideWhen({
|
|
839
|
+
...this.getValidationActionList(schema$1),
|
|
840
|
+
jsonActions.hideWhen({
|
|
181
841
|
disabled: false,
|
|
182
842
|
listen: (fn) => fn({}).pipe(map(({ list: [value], field }) => {
|
|
183
843
|
const isThen = isBoolean(schema$1.if)
|
|
@@ -201,7 +861,7 @@ class JsonSchemaToValibot {
|
|
|
201
861
|
function hideAction(isThen) {
|
|
202
862
|
return [
|
|
203
863
|
jsonActions.renderConfig({ hidden: true }),
|
|
204
|
-
hideWhen({
|
|
864
|
+
jsonActions.hideWhen({
|
|
205
865
|
disabled: true,
|
|
206
866
|
listen(fn) {
|
|
207
867
|
return fn({ list: [['..', 0]] }).pipe(switchMap(({ list: [] }) => useThen$), map((a) => (a === undefined ? true : isThen ? !a : a)));
|
|
@@ -230,7 +890,7 @@ class JsonSchemaToValibot {
|
|
|
230
890
|
baseSchema,
|
|
231
891
|
thenSchema ?? baseSchema,
|
|
232
892
|
elseSchema ?? baseSchema,
|
|
233
|
-
].filter(Boolean)), formConfig({ disableOrUpdateActivate: true }), v.rawCheck(({ dataset, addIssue }) => {
|
|
893
|
+
].filter(Boolean)), jsonActions.formConfig({ disableOrUpdateActivate: true }), v.rawCheck(({ dataset, addIssue }) => {
|
|
234
894
|
if (dataset.issues) {
|
|
235
895
|
return;
|
|
236
896
|
}
|
|
@@ -254,523 +914,18 @@ class JsonSchemaToValibot {
|
|
|
254
914
|
else {
|
|
255
915
|
vSchema = v.pipe(
|
|
256
916
|
// 通用部分
|
|
257
|
-
this.#jsonSchemaBase(this
|
|
258
|
-
}
|
|
259
|
-
return (this.#options?.schemaHandle?.afterResolve?.(vSchema, schema$1) ?? vSchema);
|
|
260
|
-
}
|
|
261
|
-
#applicatorNot(schema) {
|
|
262
|
-
const actionList = [];
|
|
263
|
-
if (isBoolean(schema.not)) {
|
|
264
|
-
if (schema.not) {
|
|
265
|
-
actionList.push(createImpasseAction('not', schema.not));
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
else if (schema.not) {
|
|
269
|
-
const vSchema = this.#jSchemaToVSchema(schema.not);
|
|
270
|
-
actionList.push(v.rawCheck(({ dataset, addIssue }) => {
|
|
271
|
-
if (dataset.issues) {
|
|
272
|
-
return;
|
|
273
|
-
}
|
|
274
|
-
const result = v.safeParse(vSchema, dataset.value);
|
|
275
|
-
if (result.success) {
|
|
276
|
-
addIssue({ label: `applicator:not` });
|
|
277
|
-
}
|
|
278
|
-
}));
|
|
917
|
+
this.#jsonSchemaBase(this.resolveSchema2(schema$1), () => this.getValidationActionList(schema$1)));
|
|
279
918
|
}
|
|
280
|
-
return
|
|
281
|
-
|
|
282
|
-
#jSchemaToVSchema(input) {
|
|
283
|
-
if (isBoolean(input)) {
|
|
284
|
-
return input
|
|
285
|
-
? v.pipe(v.any(), jsonActions.setComponent('always-true'))
|
|
286
|
-
: v.pipe(v.any(), v.check((value) => isUndefined(value)));
|
|
287
|
-
}
|
|
288
|
-
if (this.cacheSchema.has(input)) {
|
|
289
|
-
return this.cacheSchema.get(input);
|
|
290
|
-
}
|
|
291
|
-
const schema = this.#resolveSchema2(input);
|
|
292
|
-
const actionList = this.#applicatorNot(schema);
|
|
293
|
-
const result = actionList.length
|
|
294
|
-
? v.pipe(this.#applicatorParse(schema), ...actionList)
|
|
295
|
-
: this.#applicatorParse(schema);
|
|
296
|
-
this.cacheSchema.set(input, result);
|
|
297
|
-
return result;
|
|
298
|
-
}
|
|
299
|
-
#jsonSchemaCompatiable(schema) {
|
|
300
|
-
if ('__resolved' in schema && schema.__resolved.isResolved) {
|
|
301
|
-
return schema;
|
|
302
|
-
}
|
|
303
|
-
const resolved = schema;
|
|
304
|
-
const type = this.#guessSchemaType(resolved);
|
|
305
|
-
resolved.__resolved = { ...resolved.__resolved, type, isResolved: true };
|
|
306
|
-
if (type.types.includes('object')) {
|
|
307
|
-
this.#objectCompatible(resolved);
|
|
308
|
-
}
|
|
309
|
-
if (type.types.includes('array')) {
|
|
310
|
-
this.#arrayCompatible(resolved);
|
|
311
|
-
}
|
|
312
|
-
if (type.types.includes('number') || type.types.includes('integer')) {
|
|
313
|
-
if (resolved.exclusiveMaximum === true) {
|
|
314
|
-
resolved.exclusiveMaximum = resolved.maximum;
|
|
315
|
-
delete resolved.maximum;
|
|
316
|
-
}
|
|
317
|
-
if (resolved.exclusiveMinimum === true) {
|
|
318
|
-
resolved.exclusiveMinimum = resolved.minimum;
|
|
319
|
-
delete resolved.minimum;
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
return resolved;
|
|
323
|
-
}
|
|
324
|
-
#jsonSchemaBase(schema$1, getValidationActionList) {
|
|
325
|
-
const types = schema$1.__resolved.type;
|
|
326
|
-
// 暂时为只支持一个
|
|
327
|
-
let type = types.types[0];
|
|
328
|
-
const actionList = getMetadataAction(schema$1);
|
|
329
|
-
const createTypeFn = (input) => {
|
|
330
|
-
const result = v.pipe(input, ...actionList, ...getValidationActionList());
|
|
331
|
-
const result2 = types.optional
|
|
332
|
-
? v.optional(result, schema$1.default)
|
|
333
|
-
: result;
|
|
334
|
-
return (this.#options?.schemaHandle?.type?.afterResolve(result2, schema$1, type) ?? result2);
|
|
335
|
-
};
|
|
336
|
-
if (!isNil(schema$1.const)) {
|
|
337
|
-
type = 'const';
|
|
338
|
-
return createTypeFn(v.literal(schema$1.const));
|
|
339
|
-
}
|
|
340
|
-
if (Array.isArray(schema$1.enum)) {
|
|
341
|
-
type = 'enum';
|
|
342
|
-
return createTypeFn(v.picklist(schema$1.enum));
|
|
343
|
-
}
|
|
344
|
-
switch (type) {
|
|
345
|
-
case 'number': {
|
|
346
|
-
return createTypeFn(v.number());
|
|
347
|
-
}
|
|
348
|
-
case 'integer': {
|
|
349
|
-
actionList.push(v.integer());
|
|
350
|
-
return createTypeFn(v.number());
|
|
351
|
-
}
|
|
352
|
-
case 'boolean': {
|
|
353
|
-
return createTypeFn(v.boolean());
|
|
354
|
-
}
|
|
355
|
-
case 'string': {
|
|
356
|
-
return createTypeFn(v.string());
|
|
357
|
-
}
|
|
358
|
-
case 'null': {
|
|
359
|
-
return createTypeFn(v.optional(v.null(), null));
|
|
360
|
-
}
|
|
361
|
-
case 'object': {
|
|
362
|
-
const childObject = {};
|
|
363
|
-
/** 附加 */
|
|
364
|
-
let defaultRest;
|
|
365
|
-
let mode = 'default';
|
|
366
|
-
if (schema$1.dependentRequired) {
|
|
367
|
-
actionList.push(v.rawCheck(({ dataset, addIssue }) => {
|
|
368
|
-
if (dataset.issues) {
|
|
369
|
-
return;
|
|
370
|
-
}
|
|
371
|
-
Object.keys(schema$1.dependentRequired).forEach((key) => {
|
|
372
|
-
if (dataset.value?.[key] !== undefined) {
|
|
373
|
-
for (const reqKey of schema$1.dependentRequired[key]) {
|
|
374
|
-
if (dataset.value[reqKey] === undefined) {
|
|
375
|
-
addIssue({
|
|
376
|
-
label: `dependentRequired:${key}=>${reqKey}`,
|
|
377
|
-
expected: '[required]',
|
|
378
|
-
received: 'undefined',
|
|
379
|
-
});
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
});
|
|
384
|
-
}));
|
|
385
|
-
}
|
|
386
|
-
let hasRequiredKey = false;
|
|
387
|
-
// 普通属性
|
|
388
|
-
if (schema$1.properties) {
|
|
389
|
-
for (const key in schema$1.properties) {
|
|
390
|
-
const propJSchema = schema$1.properties[key];
|
|
391
|
-
let propData;
|
|
392
|
-
if (isBoolean(propJSchema)) {
|
|
393
|
-
propData = { optional: false, hasRef: false };
|
|
394
|
-
}
|
|
395
|
-
else {
|
|
396
|
-
const rSchema = this.#resolveSchema2(propJSchema);
|
|
397
|
-
propData = {
|
|
398
|
-
optional: rSchema.__resolved.type.optional,
|
|
399
|
-
hasRef: rSchema.__resolved.hasRef,
|
|
400
|
-
};
|
|
401
|
-
}
|
|
402
|
-
const isRequired = !!schema$1.required?.includes(key);
|
|
403
|
-
const wrapperOptional = !isRequired && !propData.optional;
|
|
404
|
-
if (isRequired && !propData.optional) {
|
|
405
|
-
hasRequiredKey = true;
|
|
406
|
-
}
|
|
407
|
-
const createRef = () => {
|
|
408
|
-
let propVSchema = this.#jSchemaToVSchema(propJSchema);
|
|
409
|
-
const depList = schema$1.dependentRequired?.[key];
|
|
410
|
-
if (depList) {
|
|
411
|
-
propVSchema = v.pipe(propVSchema, jsonActions.patchHooks({
|
|
412
|
-
allFieldsResolved: (field) => {
|
|
413
|
-
field.form.control.statusChanges.subscribe(() => {
|
|
414
|
-
const valid = field.form.control.valid;
|
|
415
|
-
depList.map((item) => {
|
|
416
|
-
field.form.parent
|
|
417
|
-
.get(item)
|
|
418
|
-
?.config$.update((config) => ({
|
|
419
|
-
...config,
|
|
420
|
-
required: valid,
|
|
421
|
-
}));
|
|
422
|
-
});
|
|
423
|
-
});
|
|
424
|
-
},
|
|
425
|
-
}));
|
|
426
|
-
}
|
|
427
|
-
return propVSchema;
|
|
428
|
-
};
|
|
429
|
-
childObject[key] = propData.hasRef
|
|
430
|
-
? wrapperOptional
|
|
431
|
-
? v.optional(v.lazy(() => createRef()))
|
|
432
|
-
: v.lazy(() => createRef())
|
|
433
|
-
: wrapperOptional
|
|
434
|
-
? v.optional(createRef())
|
|
435
|
-
: createRef();
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
// 附加属性规则
|
|
439
|
-
if (isBoolean(schema$1.additionalProperties)) {
|
|
440
|
-
mode = schema$1.additionalProperties === false ? 'strict' : mode;
|
|
441
|
-
}
|
|
442
|
-
else if (schema$1.additionalProperties) {
|
|
443
|
-
mode = 'rest';
|
|
444
|
-
// rest要符合的规则
|
|
445
|
-
defaultRest = this.#jSchemaToVSchema(schema$1.additionalProperties);
|
|
446
|
-
}
|
|
447
|
-
const patternRestList = [];
|
|
448
|
-
if (schema$1.patternProperties) {
|
|
449
|
-
for (const key in schema$1.patternProperties) {
|
|
450
|
-
const item = this.#jSchemaToVSchema(schema$1.patternProperties[key]);
|
|
451
|
-
if (!item) {
|
|
452
|
-
throw new Error(`patternProperties->${key}: 定义未找到`);
|
|
453
|
-
}
|
|
454
|
-
patternRestList.push({
|
|
455
|
-
regexp: new RegExp(key),
|
|
456
|
-
schema: item,
|
|
457
|
-
});
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
/** 条件显示 */
|
|
461
|
-
const conditionList = [];
|
|
462
|
-
if (schema$1.dependentSchemas) {
|
|
463
|
-
const depSchemaMap = {};
|
|
464
|
-
for (const key in schema$1.dependentSchemas) {
|
|
465
|
-
const jSchema = schema$1.dependentSchemas[key];
|
|
466
|
-
let vSchema = this.#jSchemaToVSchema(jSchema);
|
|
467
|
-
if (!vSchema) {
|
|
468
|
-
throw new Error(`依赖->${key}: 定义未找到`);
|
|
469
|
-
}
|
|
470
|
-
depSchemaMap[key] = vSchema;
|
|
471
|
-
vSchema = v.pipe(vSchema, jsonActions.renderConfig({ hidden: true }), hideWhen({
|
|
472
|
-
disabled: true,
|
|
473
|
-
listen: (fn, field) => {
|
|
474
|
-
let controlField = field.get(['..', '..', 0, key]).form
|
|
475
|
-
.control;
|
|
476
|
-
return merge(controlField.valueChanges, controlField.statusChanges).pipe(map(() => {
|
|
477
|
-
return !(controlField.valid && controlField.value !== undefined);
|
|
478
|
-
}));
|
|
479
|
-
},
|
|
480
|
-
}));
|
|
481
|
-
conditionList.push(vSchema);
|
|
482
|
-
}
|
|
483
|
-
actionList.push(v.rawCheck(({ dataset, addIssue }) => {
|
|
484
|
-
if (dataset.issues) {
|
|
485
|
-
return;
|
|
486
|
-
}
|
|
487
|
-
Object.keys(schema$1.dependentSchemas).forEach((key) => {
|
|
488
|
-
if (dataset.value?.[key] !== undefined) {
|
|
489
|
-
const result = v.safeParse(depSchemaMap[key], dataset.value);
|
|
490
|
-
if (!result.success) {
|
|
491
|
-
for (const item of result.issues) {
|
|
492
|
-
addIssue({
|
|
493
|
-
...item,
|
|
494
|
-
});
|
|
495
|
-
}
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
|
-
});
|
|
499
|
-
}));
|
|
500
|
-
}
|
|
501
|
-
if (isBoolean(schema$1.propertyNames) && !schema$1.propertyNames) {
|
|
502
|
-
actionList.push(v.check(() => false));
|
|
503
|
-
}
|
|
504
|
-
else if (schema$1.propertyNames) {
|
|
505
|
-
const propNameSchema = this.#jSchemaToVSchema(schema$1.propertyNames);
|
|
506
|
-
actionList.push(v.rawCheck(({ dataset, addIssue }) => {
|
|
507
|
-
if (dataset.issues) {
|
|
508
|
-
return;
|
|
509
|
-
}
|
|
510
|
-
if (dataset.value && typeof dataset.value === 'object') {
|
|
511
|
-
for (const key of Object.keys(dataset.value)) {
|
|
512
|
-
const result = v.safeParse(propNameSchema, key);
|
|
513
|
-
if (!result.success) {
|
|
514
|
-
addIssue({
|
|
515
|
-
label: `propertyNames:${key}`,
|
|
516
|
-
expected: `[match]`,
|
|
517
|
-
received: key,
|
|
518
|
-
});
|
|
519
|
-
}
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
}));
|
|
523
|
-
}
|
|
524
|
-
let schemaDefine;
|
|
525
|
-
if (!Object.keys(childObject).length || !hasRequiredKey) {
|
|
526
|
-
types.optional = true;
|
|
527
|
-
}
|
|
528
|
-
if (mode === 'default') {
|
|
529
|
-
if (conditionList.length) {
|
|
530
|
-
schemaDefine = v.pipe(schema.intersect([
|
|
531
|
-
v.looseObject(childObject),
|
|
532
|
-
v.optional(v.intersect(conditionList)),
|
|
533
|
-
]));
|
|
534
|
-
}
|
|
535
|
-
else {
|
|
536
|
-
schemaDefine = v.pipe(v.looseObject(childObject));
|
|
537
|
-
}
|
|
538
|
-
}
|
|
539
|
-
else if (mode === 'strict') {
|
|
540
|
-
if (conditionList.length) {
|
|
541
|
-
schemaDefine = v.pipe(schema.intersect([
|
|
542
|
-
v.object(childObject),
|
|
543
|
-
v.optional(v.intersect(conditionList)),
|
|
544
|
-
]));
|
|
545
|
-
}
|
|
546
|
-
else {
|
|
547
|
-
schemaDefine = v.pipe(v.object(childObject));
|
|
548
|
-
}
|
|
549
|
-
}
|
|
550
|
-
else {
|
|
551
|
-
// rest
|
|
552
|
-
let restDefine = v.any();
|
|
553
|
-
//propCheck patternMapRest addonRest
|
|
554
|
-
if (defaultRest && !patternRestList.length) {
|
|
555
|
-
restDefine = defaultRest;
|
|
556
|
-
}
|
|
557
|
-
else {
|
|
558
|
-
restDefine = v.any();
|
|
559
|
-
}
|
|
560
|
-
if (conditionList.length) {
|
|
561
|
-
schemaDefine = v.intersect([
|
|
562
|
-
v.objectWithRest(childObject, restDefine),
|
|
563
|
-
v.optional(v.intersect(conditionList)),
|
|
564
|
-
]);
|
|
565
|
-
}
|
|
566
|
-
else {
|
|
567
|
-
schemaDefine = v.pipe(v.objectWithRest(childObject, restDefine));
|
|
568
|
-
}
|
|
569
|
-
}
|
|
570
|
-
if (schema$1.patternProperties && schema$1.additionalProperties) {
|
|
571
|
-
actionList.push(v.rawCheck(({ dataset, addIssue }) => {
|
|
572
|
-
if (dataset.issues || dataset.value === undefined) {
|
|
573
|
-
return;
|
|
574
|
-
}
|
|
575
|
-
if (typeof dataset.value === 'object') {
|
|
576
|
-
datasetLoop: for (const key in dataset.value) {
|
|
577
|
-
if (key in childObject) {
|
|
578
|
-
continue;
|
|
579
|
-
}
|
|
580
|
-
for (const { regexp, schema } of patternRestList) {
|
|
581
|
-
const isMatch = regexp.test(key);
|
|
582
|
-
if (!isMatch) {
|
|
583
|
-
continue;
|
|
584
|
-
}
|
|
585
|
-
const result = v.safeParse(schema, dataset.value[key]);
|
|
586
|
-
if (!result.success) {
|
|
587
|
-
addIssue();
|
|
588
|
-
}
|
|
589
|
-
continue datasetLoop;
|
|
590
|
-
}
|
|
591
|
-
if (defaultRest) {
|
|
592
|
-
const result = v.safeParse(defaultRest, dataset.value[key]);
|
|
593
|
-
if (!result.success) {
|
|
594
|
-
addIssue();
|
|
595
|
-
}
|
|
596
|
-
}
|
|
597
|
-
}
|
|
598
|
-
}
|
|
599
|
-
}));
|
|
600
|
-
}
|
|
601
|
-
return createTypeFn(schemaDefine);
|
|
602
|
-
}
|
|
603
|
-
case 'array': {
|
|
604
|
-
let parent;
|
|
605
|
-
const fixedItems = schema$1.prefixItems;
|
|
606
|
-
if (isBoolean(schema$1.contains) && !schema$1.contains) {
|
|
607
|
-
actionList.push(createImpasseAction('contains', schema$1.contains));
|
|
608
|
-
}
|
|
609
|
-
else if (schema$1.contains && !isBoolean(schema$1.contains)) {
|
|
610
|
-
const containsSchema = this.#jSchemaToVSchema(schema$1.contains);
|
|
611
|
-
const minContains = schema$1.minContains ?? 1;
|
|
612
|
-
actionList.push(v.check((list) => {
|
|
613
|
-
if (Array.isArray(list)) {
|
|
614
|
-
const result = list.filter((item) => v.safeParse(containsSchema, item).success);
|
|
615
|
-
if (result.length < minContains) {
|
|
616
|
-
return false;
|
|
617
|
-
}
|
|
618
|
-
if (typeof schema$1.maxContains === 'number') {
|
|
619
|
-
return result.length <= schema$1.maxContains;
|
|
620
|
-
}
|
|
621
|
-
return true;
|
|
622
|
-
}
|
|
623
|
-
return false;
|
|
624
|
-
}));
|
|
625
|
-
}
|
|
626
|
-
const jSchemaToVSchema = (schema) => {
|
|
627
|
-
const hasRef = this.#schemahasRef(schema);
|
|
628
|
-
return hasRef
|
|
629
|
-
? v.lazy(() => this.#jSchemaToVSchema(schema))
|
|
630
|
-
: this.#jSchemaToVSchema(schema);
|
|
631
|
-
};
|
|
632
|
-
if (fixedItems && fixedItems.length) {
|
|
633
|
-
const fixedList = fixedItems.map((item) => jSchemaToVSchema(item));
|
|
634
|
-
if (schema$1.items) {
|
|
635
|
-
const result = jSchemaToVSchema(schema$1.items);
|
|
636
|
-
parent = v.tupleWithRest(fixedList, result);
|
|
637
|
-
}
|
|
638
|
-
else if (schema$1.items === false) {
|
|
639
|
-
parent = v.tuple(fixedList);
|
|
640
|
-
}
|
|
641
|
-
else {
|
|
642
|
-
parent = v.looseTuple(fixedList);
|
|
643
|
-
}
|
|
644
|
-
}
|
|
645
|
-
else if (isBoolean(schema$1.items)) {
|
|
646
|
-
parent = schema$1.items ? v.array(v.any()) : v.tuple([]);
|
|
647
|
-
}
|
|
648
|
-
else if (schema$1.items) {
|
|
649
|
-
const result = jSchemaToVSchema(schema$1.items);
|
|
650
|
-
parent = v.array(result);
|
|
651
|
-
}
|
|
652
|
-
else {
|
|
653
|
-
parent = v.array(v.any());
|
|
654
|
-
}
|
|
655
|
-
return createTypeFn(parent);
|
|
656
|
-
}
|
|
657
|
-
default:
|
|
658
|
-
throw new Error(`未知类型:${type}`);
|
|
659
|
-
}
|
|
660
|
-
}
|
|
661
|
-
#resolveDefinition(schema) {
|
|
662
|
-
if (!schema.$ref) {
|
|
663
|
-
return schema;
|
|
664
|
-
}
|
|
665
|
-
const [uri, pointer] = schema.$ref.split('#/');
|
|
666
|
-
if (uri) {
|
|
667
|
-
throw Error(`Remote schemas for ${schema.$ref} not supported yet.`);
|
|
668
|
-
}
|
|
669
|
-
const definition = !pointer
|
|
670
|
-
? null
|
|
671
|
-
: pointer
|
|
672
|
-
.split('/')
|
|
673
|
-
.reduce((def, path) => def?.hasOwnProperty(path) ? def[path] : null, this.root);
|
|
674
|
-
if (!definition) {
|
|
675
|
-
throw Error(`Cannot find a definition for ${schema.$ref}.`);
|
|
676
|
-
}
|
|
677
|
-
if (definition.$ref) {
|
|
678
|
-
return this.#resolveDefinition(definition);
|
|
679
|
-
}
|
|
680
|
-
return {
|
|
681
|
-
...definition,
|
|
682
|
-
...['title', 'description', 'default', 'actions'].reduce((annotation, p) => {
|
|
683
|
-
if (schema.hasOwnProperty(p)) {
|
|
684
|
-
annotation[p] = schema[p];
|
|
685
|
-
}
|
|
686
|
-
return annotation;
|
|
687
|
-
}, {}),
|
|
688
|
-
$ref: undefined,
|
|
689
|
-
__resolved: {
|
|
690
|
-
hasRef: true,
|
|
691
|
-
},
|
|
692
|
-
};
|
|
693
|
-
}
|
|
694
|
-
/** todo 当前只能存在一个类型 */
|
|
695
|
-
#guessSchemaType(schema) {
|
|
696
|
-
let type = schema?.type;
|
|
697
|
-
let optional = 'default' in schema;
|
|
698
|
-
if (isString(type)) {
|
|
699
|
-
return { types: [type], optional: optional };
|
|
700
|
-
}
|
|
701
|
-
if (Array.isArray(type)) {
|
|
702
|
-
if (type.length === 1) {
|
|
703
|
-
return { types: type, optional: optional };
|
|
704
|
-
}
|
|
705
|
-
const nullIndex = type.findIndex((item) => item === 'null');
|
|
706
|
-
if (nullIndex !== -1) {
|
|
707
|
-
type.splice(nullIndex, 1);
|
|
708
|
-
}
|
|
709
|
-
return {
|
|
710
|
-
types: type,
|
|
711
|
-
optional: optional || nullIndex !== -1,
|
|
712
|
-
};
|
|
713
|
-
}
|
|
714
|
-
if (schema.items ||
|
|
715
|
-
schema.prefixItems ||
|
|
716
|
-
isNumber(schema.minContains) ||
|
|
717
|
-
isNumber(schema.maxContains) ||
|
|
718
|
-
!isNil(schema.contains) ||
|
|
719
|
-
isBoolean(schema.uniqueItems)) {
|
|
720
|
-
type = 'array';
|
|
721
|
-
}
|
|
722
|
-
else if (isNumber(schema.minimum) ||
|
|
723
|
-
isNumber(schema.maximum) ||
|
|
724
|
-
isNumber(schema.exclusiveMaximum) ||
|
|
725
|
-
isNumber(schema.exclusiveMinimum) ||
|
|
726
|
-
isNumber(schema.multipleOf)) {
|
|
727
|
-
type = 'number';
|
|
728
|
-
}
|
|
729
|
-
else if (isNumber(schema.minLength) ||
|
|
730
|
-
isNumber(schema.maxLength) ||
|
|
731
|
-
isString(schema.pattern)) {
|
|
732
|
-
type = 'string';
|
|
733
|
-
}
|
|
734
|
-
return type
|
|
735
|
-
? { types: [type], optional: optional }
|
|
736
|
-
: { types: anyType, optional: optional };
|
|
737
|
-
}
|
|
738
|
-
#objectCompatible(schema) {
|
|
739
|
-
if ('dependencies' in schema && schema.dependencies) {
|
|
740
|
-
const dependencies = schema.dependencies;
|
|
741
|
-
const dependentRequiredData = {};
|
|
742
|
-
const dependentSchemasData = {};
|
|
743
|
-
Object.keys(dependencies).forEach((prop) => {
|
|
744
|
-
const dependency = dependencies[prop];
|
|
745
|
-
if (Array.isArray(dependency)) {
|
|
746
|
-
dependentRequiredData[prop] = dependency;
|
|
747
|
-
}
|
|
748
|
-
else {
|
|
749
|
-
dependentSchemasData[prop] = dependency;
|
|
750
|
-
}
|
|
751
|
-
});
|
|
752
|
-
schema.dependentRequired = dependentRequiredData;
|
|
753
|
-
schema.dependentSchemas = dependentSchemasData;
|
|
754
|
-
delete schema.dependencies;
|
|
755
|
-
}
|
|
756
|
-
}
|
|
757
|
-
#arrayCompatible(schema) {
|
|
758
|
-
if (this.root.$schema !== Schema2012 || !isNil(schema.additionalItems)) {
|
|
759
|
-
if (!isNil(schema.items) || !isNil(schema.additionalItems)) {
|
|
760
|
-
// 2019-09
|
|
761
|
-
schema.prefixItems = schema.items;
|
|
762
|
-
schema.items = schema.additionalItems;
|
|
763
|
-
}
|
|
764
|
-
}
|
|
765
|
-
return;
|
|
919
|
+
return (this.instance.options?.schemaHandle?.afterResolve?.(vSchema, schema$1) ??
|
|
920
|
+
vSchema);
|
|
766
921
|
}
|
|
767
922
|
#mergeSchema(schema, ...list) {
|
|
768
923
|
let base = clone(schema);
|
|
769
924
|
let baseKeyList = Object.keys(base);
|
|
770
|
-
const actionList = this
|
|
771
|
-
for (
|
|
772
|
-
childSchema = this
|
|
773
|
-
actionList.push(...this
|
|
925
|
+
const actionList = this.getValidationActionList(base);
|
|
926
|
+
for (const rawChildSchema of list.filter((item) => !isBoolean(item))) {
|
|
927
|
+
const childSchema = this.resolveSchema2(rawChildSchema);
|
|
928
|
+
actionList.push(...this.getValidationActionList(childSchema));
|
|
774
929
|
baseKeyList = union(baseKeyList, Object.keys(childSchema));
|
|
775
930
|
for (const key of baseKeyList) {
|
|
776
931
|
switch (key) {
|
|
@@ -798,7 +953,7 @@ class JsonSchemaToValibot {
|
|
|
798
953
|
}
|
|
799
954
|
case 'contains': {
|
|
800
955
|
if (childSchema[key] === false || base[key] === false) {
|
|
801
|
-
actionList.push(
|
|
956
|
+
actionList.push(createImpasseAction('contains', false));
|
|
802
957
|
break;
|
|
803
958
|
}
|
|
804
959
|
else if (childSchema[key] === true) {
|
|
@@ -838,6 +993,10 @@ class JsonSchemaToValibot {
|
|
|
838
993
|
}
|
|
839
994
|
break;
|
|
840
995
|
}
|
|
996
|
+
case 'required': {
|
|
997
|
+
childSchema.required = union(childSchema.required ?? [], base.required ?? []);
|
|
998
|
+
break;
|
|
999
|
+
}
|
|
841
1000
|
default:
|
|
842
1001
|
childSchema[key] ??= base[key];
|
|
843
1002
|
break;
|
|
@@ -845,192 +1004,10 @@ class JsonSchemaToValibot {
|
|
|
845
1004
|
}
|
|
846
1005
|
base = childSchema;
|
|
847
1006
|
}
|
|
848
|
-
return { schema:
|
|
849
|
-
}
|
|
850
|
-
#resolveSchema2(schema) {
|
|
851
|
-
return this.#jsonSchemaCompatiable(this.#resolveDefinition(schema));
|
|
852
|
-
}
|
|
853
|
-
#schemahasRef(schema) {
|
|
854
|
-
if (isBoolean(schema)) {
|
|
855
|
-
return false;
|
|
856
|
-
}
|
|
857
|
-
else {
|
|
858
|
-
const result = this.#resolveSchema2(schema);
|
|
859
|
-
return result.__resolved.hasRef;
|
|
860
|
-
}
|
|
861
|
-
}
|
|
862
|
-
#parseEnum(schema) {
|
|
863
|
-
// 普通枚举
|
|
864
|
-
if (schema.enum) {
|
|
865
|
-
return {
|
|
866
|
-
type: 'enum',
|
|
867
|
-
data: {
|
|
868
|
-
enum: schema.enum,
|
|
869
|
-
},
|
|
870
|
-
};
|
|
871
|
-
}
|
|
872
|
-
else if (schema.const) {
|
|
873
|
-
return { type: 'const', data: { const: schema.const } };
|
|
874
|
-
}
|
|
875
|
-
else if (schema.items && !isBoolean(schema.items)) {
|
|
876
|
-
const result = this.#parseEnum(schema.items);
|
|
877
|
-
if (result?.data) {
|
|
878
|
-
return {
|
|
879
|
-
type: 'multiselect',
|
|
880
|
-
data: {
|
|
881
|
-
items: result.data,
|
|
882
|
-
},
|
|
883
|
-
};
|
|
884
|
-
}
|
|
885
|
-
return undefined;
|
|
886
|
-
}
|
|
887
|
-
return undefined;
|
|
888
|
-
}
|
|
889
|
-
#intersectSchemaType(a, b) {
|
|
890
|
-
const parent = a ? this.#resolveSchema2(a) : undefined;
|
|
891
|
-
b = parent ? this.#mergeSchema(parent, b).schema : b;
|
|
892
|
-
const child = this.#resolveSchema2(b);
|
|
893
|
-
const parentEnum = parent ? this.#parseEnum(parent) : undefined;
|
|
894
|
-
const childEnum = this.#parseEnum(child);
|
|
895
|
-
if (parentEnum?.data.items && childEnum?.data.items) {
|
|
896
|
-
const result = intersection(parentEnum.data.items.enum, childEnum.data.items.enum);
|
|
897
|
-
if (result.length) {
|
|
898
|
-
return {
|
|
899
|
-
type: 'multiselect',
|
|
900
|
-
data: {
|
|
901
|
-
items: {
|
|
902
|
-
enum: result,
|
|
903
|
-
},
|
|
904
|
-
},
|
|
905
|
-
};
|
|
906
|
-
}
|
|
907
|
-
}
|
|
908
|
-
else if (childEnum?.type === 'multiselect') {
|
|
909
|
-
return childEnum;
|
|
910
|
-
}
|
|
911
|
-
// 枚举
|
|
912
|
-
if (parentEnum?.data.enum && childEnum?.data.enum) {
|
|
913
|
-
const result = intersection(parentEnum.data.enum, childEnum.data.enum);
|
|
914
|
-
if (result.length) {
|
|
915
|
-
return {
|
|
916
|
-
type: 'enum',
|
|
917
|
-
data: { enum: result },
|
|
918
|
-
};
|
|
919
|
-
}
|
|
920
|
-
}
|
|
921
|
-
else if (childEnum?.data.enum) {
|
|
922
|
-
return childEnum;
|
|
923
|
-
}
|
|
924
|
-
// 常量
|
|
925
|
-
if (isNil(parentEnum?.data.const) && !isNil(childEnum?.data.const)) {
|
|
926
|
-
return childEnum;
|
|
927
|
-
}
|
|
928
|
-
// 类型
|
|
929
|
-
const typeResult = parent?.__resolved.type.types
|
|
930
|
-
? intersection(parent.__resolved.type.types, child.__resolved.type.types)
|
|
931
|
-
: child.__resolved.type.types;
|
|
932
|
-
if (typeResult.length) {
|
|
933
|
-
delete child['resolved'];
|
|
934
|
-
const result = this.#jsonSchemaCompatiable({
|
|
935
|
-
...child,
|
|
936
|
-
type: typeResult[0],
|
|
937
|
-
});
|
|
938
|
-
return {
|
|
939
|
-
type: typeResult[0],
|
|
940
|
-
data: result,
|
|
941
|
-
};
|
|
942
|
-
}
|
|
943
|
-
return;
|
|
944
|
-
}
|
|
945
|
-
#schemaExtract(schema, ...childList) {
|
|
946
|
-
/** 所有子属性key */
|
|
947
|
-
const childKeyList = uniq(childList.flatMap((item) => Object.keys(item.properties ?? {})));
|
|
948
|
-
if (!childKeyList.length) {
|
|
949
|
-
// 无效返回
|
|
950
|
-
return;
|
|
951
|
-
}
|
|
952
|
-
const conditionJSchema = {
|
|
953
|
-
properties: {},
|
|
954
|
-
additionalProperties: false,
|
|
955
|
-
};
|
|
956
|
-
const childConditionJSchemaList = childList.map(() => ({ properties: {} }));
|
|
957
|
-
const conditionKeyList = [];
|
|
958
|
-
for (const key of childKeyList) {
|
|
959
|
-
const parentItem = schema.properties?.[key];
|
|
960
|
-
//如果父级不存在这个属性,并且禁止添加,跳过
|
|
961
|
-
// todo 还应该增加额外的匹配
|
|
962
|
-
if (!parentItem && schema.additionalProperties === false) {
|
|
963
|
-
continue;
|
|
964
|
-
}
|
|
965
|
-
// 所有子级都存在某个Key
|
|
966
|
-
const keyExist = childList.every((item) => {
|
|
967
|
-
const propItem = item.properties?.[key];
|
|
968
|
-
// todo 对象应该先解析
|
|
969
|
-
return propItem && !isBoolean(propItem) && propItem.type !== 'object';
|
|
970
|
-
});
|
|
971
|
-
if (!keyExist) {
|
|
972
|
-
continue;
|
|
973
|
-
}
|
|
974
|
-
let currentType = undefined;
|
|
975
|
-
const childPropList = [];
|
|
976
|
-
for (const sub of childList) {
|
|
977
|
-
const result = this.#intersectSchemaType(schema?.properties?.[key], sub.properties[key]);
|
|
978
|
-
if (!result) {
|
|
979
|
-
currentType = undefined;
|
|
980
|
-
break;
|
|
981
|
-
}
|
|
982
|
-
else if (currentType === undefined ||
|
|
983
|
-
deepEqual(currentType, result.type)) {
|
|
984
|
-
currentType = result.type;
|
|
985
|
-
// 枚举
|
|
986
|
-
if (result.data.enum ||
|
|
987
|
-
'const' in result.data ||
|
|
988
|
-
result.type === 'multiselect') {
|
|
989
|
-
childPropList.push(result.data);
|
|
990
|
-
}
|
|
991
|
-
else {
|
|
992
|
-
childPropList.push(result.data);
|
|
993
|
-
}
|
|
994
|
-
}
|
|
995
|
-
else {
|
|
996
|
-
break;
|
|
997
|
-
}
|
|
998
|
-
}
|
|
999
|
-
if (currentType) {
|
|
1000
|
-
conditionKeyList.push(key);
|
|
1001
|
-
for (let index = 0; index < childConditionJSchemaList.length; index++) {
|
|
1002
|
-
const schema = childConditionJSchemaList[index];
|
|
1003
|
-
schema.properties[key] = childPropList[index];
|
|
1004
|
-
}
|
|
1005
|
-
if (currentType === 'enum') {
|
|
1006
|
-
conditionJSchema.properties[key] = {
|
|
1007
|
-
enum: childPropList.flatMap((item) => item.enum),
|
|
1008
|
-
};
|
|
1009
|
-
}
|
|
1010
|
-
else if (currentType === 'const') {
|
|
1011
|
-
conditionJSchema.properties[key] = {
|
|
1012
|
-
enum: childPropList.flatMap((item) => item.const),
|
|
1013
|
-
};
|
|
1014
|
-
}
|
|
1015
|
-
else if (currentType === 'multiselect') {
|
|
1016
|
-
conditionJSchema.properties[key] = {
|
|
1017
|
-
type: 'array',
|
|
1018
|
-
items: {
|
|
1019
|
-
enum: childPropList.flatMap((item) => item.items.enum),
|
|
1020
|
-
},
|
|
1021
|
-
uniqueItems: true,
|
|
1022
|
-
};
|
|
1023
|
-
}
|
|
1024
|
-
else {
|
|
1025
|
-
conditionJSchema.properties[key] = {
|
|
1026
|
-
type: currentType,
|
|
1027
|
-
};
|
|
1028
|
-
}
|
|
1029
|
-
}
|
|
1030
|
-
}
|
|
1031
|
-
return { conditionJSchema, childConditionJSchemaList, conditionKeyList };
|
|
1007
|
+
return { schema: base, actionList };
|
|
1032
1008
|
}
|
|
1033
1009
|
#conditionCreate(schema, options) {
|
|
1010
|
+
const fixedKeyList = Object.keys(this.getFixedChild());
|
|
1034
1011
|
const resolvedChildList = options
|
|
1035
1012
|
.getChildren()
|
|
1036
1013
|
.map((item) => this.#mergeSchema(schema, item));
|
|
@@ -1044,146 +1021,428 @@ class JsonSchemaToValibot {
|
|
|
1044
1021
|
if (isObject) {
|
|
1045
1022
|
const conditionResult = this.#schemaExtract(schema, ...resolvedChildJSchemaList);
|
|
1046
1023
|
if (conditionResult) {
|
|
1024
|
+
const conditionKeyList = conditionResult.conditionKeyList;
|
|
1025
|
+
const excludeFixedConditionList = difference(conditionResult.conditionKeyList, fixedKeyList);
|
|
1047
1026
|
/** 子级的共同部分验证,用于检测是哪个子级,不显示 */
|
|
1048
1027
|
const childConditionVSchemaList = conditionResult.childConditionJSchemaList.map((schema) => {
|
|
1049
|
-
const rSchema = this
|
|
1050
|
-
return v.pipe(this.#jsonSchemaBase(rSchema, () => this
|
|
1028
|
+
const rSchema = this.resolveSchema2(schema);
|
|
1029
|
+
return v.pipe(this.#jsonSchemaBase(rSchema, () => this.getValidationActionList(rSchema)));
|
|
1051
1030
|
});
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1031
|
+
if (fixedKeyList.length) {
|
|
1032
|
+
for (const key of fixedKeyList) {
|
|
1033
|
+
delete conditionResult.conditionJSchema.properties?.[key];
|
|
1034
|
+
conditionResult.childConditionJSchemaList.forEach((schema) => {
|
|
1035
|
+
delete schema.properties?.[key];
|
|
1036
|
+
});
|
|
1037
|
+
}
|
|
1038
|
+
conditionResult.conditionKeyList = excludeFixedConditionList;
|
|
1039
|
+
}
|
|
1040
|
+
const checkAction = jsonActions.patchHooks({
|
|
1041
|
+
allFieldsResolved: (field) => {
|
|
1042
|
+
const displayName = ['..', 2];
|
|
1043
|
+
const list = [];
|
|
1044
|
+
const currenConditionSchema = field;
|
|
1045
|
+
list.push(currenConditionSchema.form.control.valueChanges);
|
|
1046
|
+
if (fixedKeyList.length) {
|
|
1047
|
+
list.push(combineLatest(fixedKeyList.map((key) => {
|
|
1048
|
+
const instanceNamePrefix = this.getFixedChildBy(key);
|
|
1049
|
+
return field.get([
|
|
1050
|
+
'#',
|
|
1051
|
+
`@${instanceNamePrefix}-fixed`,
|
|
1052
|
+
key,
|
|
1053
|
+
]).form.control.valueChanges;
|
|
1054
|
+
})).pipe(map((list) => list.reduce((obj, cur, i) => {
|
|
1055
|
+
obj[fixedKeyList[i]] = cur;
|
|
1056
|
+
return obj;
|
|
1057
|
+
}, {}))));
|
|
1073
1058
|
}
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1059
|
+
combineLatest(list)
|
|
1060
|
+
.pipe(map((list) => list.reduce((obj, cur) => {
|
|
1061
|
+
obj = { ...obj, ...cur };
|
|
1062
|
+
return obj;
|
|
1063
|
+
}, {})))
|
|
1064
|
+
.subscribe((value) => {
|
|
1065
|
+
activateList.length = 0;
|
|
1066
|
+
const conditionDisplay = field.get([...displayName]).form
|
|
1067
|
+
.control;
|
|
1068
|
+
const parentAList = [];
|
|
1069
|
+
for (let index = 0; index < childConditionVSchemaList.length; index++) {
|
|
1070
|
+
const schema = childConditionVSchemaList[index];
|
|
1071
|
+
const result = v.safeParse(schema, value);
|
|
1072
|
+
activateList.push(result.success);
|
|
1073
|
+
// todo key需要查询
|
|
1074
|
+
field
|
|
1075
|
+
.get([...displayName, index])
|
|
1076
|
+
?.renderConfig.update((data) => ({
|
|
1077
|
+
...data,
|
|
1078
|
+
hidden: !result.success,
|
|
1079
|
+
}));
|
|
1080
|
+
if (result.success) {
|
|
1081
|
+
parentAList.push(conditionDisplay.children$$()[index]);
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
if (!options.useOr) {
|
|
1085
|
+
conditionDisplay.activateControls$.set(parentAList);
|
|
1086
|
+
}
|
|
1087
|
+
});
|
|
1088
|
+
},
|
|
1089
|
+
});
|
|
1090
|
+
// todo 修改支持父级监听
|
|
1091
|
+
// todo 主条件要过滤
|
|
1092
|
+
/** 主条件部分,用于显示切换 */
|
|
1093
|
+
const conditionVSchema = v.pipe(this.#jsonSchemaBase(this.resolveSchema2(conditionResult.conditionJSchema), () => []), checkAction);
|
|
1094
|
+
conditionKeyList.forEach((key) => {
|
|
1077
1095
|
resolvedChildJSchemaList.forEach((item) => {
|
|
1078
1096
|
delete item.properties[key];
|
|
1079
1097
|
});
|
|
1080
1098
|
delete schema.properties?.[key];
|
|
1081
1099
|
});
|
|
1082
|
-
const baseSchema = v.pipe(this.#jsonSchemaBase(schema, () => this
|
|
1100
|
+
const baseSchema = v.pipe(this.#jsonSchemaBase(schema, () => this.getValidationActionList(schema)));
|
|
1101
|
+
// todo 解析条件也要过滤
|
|
1083
1102
|
const childVSchemaList = resolvedChildJSchemaList.map((item) =>
|
|
1084
1103
|
// 验证部分被单独提取出来
|
|
1085
1104
|
v.pipe(this.#jsonSchemaBase(item, () => [])));
|
|
1086
|
-
return v.pipe(options.conditionSchemaFn(baseSchema, conditionVSchema, childVSchemaList), conditionCheckAction);
|
|
1105
|
+
return v.pipe(options.conditionSchemaFn(baseSchema, v.pipe(conditionVSchema, jsonActions.setAlias(`${this.instanceNamePrefix$$()}-cond`)), childVSchemaList), conditionCheckAction);
|
|
1087
1106
|
}
|
|
1088
1107
|
}
|
|
1089
|
-
const
|
|
1108
|
+
const result = this.getOptions(resolvedChildJSchemaList);
|
|
1109
|
+
if (result) {
|
|
1110
|
+
const instance = this.getTypeParse('__fixedList', {
|
|
1111
|
+
data: result,
|
|
1112
|
+
type: '__fixedList',
|
|
1113
|
+
});
|
|
1114
|
+
activateList = childOriginSchemaList.map((_, i) => true);
|
|
1115
|
+
return v.pipe(instance.parse([]), conditionCheckAction);
|
|
1116
|
+
}
|
|
1117
|
+
const baseSchema = v.pipe(this.#jsonSchemaBase(schema, () => this.getValidationActionList(schema)));
|
|
1090
1118
|
activateList = childOriginSchemaList.map((_, i) => true);
|
|
1091
1119
|
return v.pipe(baseSchema, conditionCheckAction);
|
|
1092
1120
|
}
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
if (isNumber(schema.minLength) || isNumber(schema.minItems)) {
|
|
1097
|
-
action.push(v.minLength(schema.minLength ?? schema.minItems));
|
|
1098
|
-
}
|
|
1099
|
-
// string/array
|
|
1100
|
-
if (isNumber(schema.maxLength) || isNumber(schema.maxItems)) {
|
|
1101
|
-
action.push(v.maxLength(schema.maxLength ?? schema.maxItems));
|
|
1102
|
-
}
|
|
1103
|
-
// string
|
|
1104
|
-
if (isString(schema.pattern)) {
|
|
1105
|
-
action.push(v.regex(new RegExp(schema.pattern)));
|
|
1121
|
+
getOptions(childList) {
|
|
1122
|
+
if (!childList.length) {
|
|
1123
|
+
return;
|
|
1106
1124
|
}
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
case 'date': {
|
|
1120
|
-
action.push(v.isoDate());
|
|
1121
|
-
break;
|
|
1125
|
+
const fn2 = () => {
|
|
1126
|
+
let data = {
|
|
1127
|
+
multi: undefined,
|
|
1128
|
+
uniqueItems: true,
|
|
1129
|
+
};
|
|
1130
|
+
const fn = (schema) => {
|
|
1131
|
+
let options;
|
|
1132
|
+
let multi2 = false;
|
|
1133
|
+
if (!isUndefined(schema.const)) {
|
|
1134
|
+
options = [
|
|
1135
|
+
{ value: schema.const, label: schema.title ?? schema.const },
|
|
1136
|
+
];
|
|
1122
1137
|
}
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1138
|
+
else if (schema.enum) {
|
|
1139
|
+
if (schema.enum.length === 1) {
|
|
1140
|
+
options = [
|
|
1141
|
+
{
|
|
1142
|
+
value: schema.enum[0],
|
|
1143
|
+
label: schema.title ?? schema.enum[0],
|
|
1144
|
+
},
|
|
1145
|
+
];
|
|
1146
|
+
}
|
|
1147
|
+
options = schema.enum.map((item) => ({ label: item, value: item }));
|
|
1126
1148
|
}
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1149
|
+
else if (schema.items && !isBoolean(schema.items)) {
|
|
1150
|
+
const items = this.resolveSchema2(schema.items);
|
|
1151
|
+
options = fn2().fn(items);
|
|
1152
|
+
multi2 = true;
|
|
1153
|
+
data.uniqueItems &&= !!schema.uniqueItems;
|
|
1130
1154
|
}
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
break;
|
|
1155
|
+
if (!options) {
|
|
1156
|
+
return undefined;
|
|
1134
1157
|
}
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
break;
|
|
1158
|
+
if (data.multi === undefined) {
|
|
1159
|
+
data.multi = multi2;
|
|
1138
1160
|
}
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
break;
|
|
1161
|
+
else if (data.multi !== multi2) {
|
|
1162
|
+
throw new Error(`options multi conflict`);
|
|
1142
1163
|
}
|
|
1143
|
-
|
|
1144
|
-
|
|
1164
|
+
return options;
|
|
1165
|
+
};
|
|
1166
|
+
return {
|
|
1167
|
+
fn,
|
|
1168
|
+
getData: () => data,
|
|
1169
|
+
};
|
|
1170
|
+
};
|
|
1171
|
+
const list = [];
|
|
1172
|
+
const fn$ = fn2();
|
|
1173
|
+
for (const schema of childList) {
|
|
1174
|
+
const item = fn$.fn(schema);
|
|
1175
|
+
if (!item) {
|
|
1176
|
+
return undefined;
|
|
1145
1177
|
}
|
|
1178
|
+
list.push(item);
|
|
1146
1179
|
}
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1180
|
+
let data = fn$.getData();
|
|
1181
|
+
return {
|
|
1182
|
+
uniqueItems: data.uniqueItems,
|
|
1183
|
+
multi: data.multi,
|
|
1184
|
+
options: list,
|
|
1185
|
+
};
|
|
1186
|
+
}
|
|
1187
|
+
#schemaExtract(schema, ...childList) {
|
|
1188
|
+
const childKeyList = childList.reduce((cur, item) => {
|
|
1189
|
+
if (cur && !cur.length) {
|
|
1190
|
+
return cur;
|
|
1191
|
+
}
|
|
1192
|
+
const keyList = Object.keys(item.properties ?? {}).filter((key) => {
|
|
1193
|
+
const propItem = item.properties[key];
|
|
1194
|
+
if (isBoolean(propItem)) {
|
|
1195
|
+
return false;
|
|
1196
|
+
}
|
|
1197
|
+
const resolved = this.resolveSchema2(propItem);
|
|
1198
|
+
return !resolved.__resolved.type.types.includes('object');
|
|
1199
|
+
});
|
|
1200
|
+
if (!cur) {
|
|
1201
|
+
return keyList;
|
|
1202
|
+
}
|
|
1203
|
+
else {
|
|
1204
|
+
return intersection(cur, keyList);
|
|
1205
|
+
}
|
|
1206
|
+
}, undefined);
|
|
1207
|
+
if (!childKeyList.length) {
|
|
1208
|
+
// 无效返回
|
|
1209
|
+
return;
|
|
1150
1210
|
}
|
|
1151
|
-
|
|
1152
|
-
|
|
1211
|
+
const conditionJSchema = {
|
|
1212
|
+
properties: {},
|
|
1213
|
+
additionalProperties: false,
|
|
1214
|
+
};
|
|
1215
|
+
const childConditionJSchemaList = childList.map(() => ({ properties: {} }));
|
|
1216
|
+
const conditionKeyList = [];
|
|
1217
|
+
for (const key of childKeyList) {
|
|
1218
|
+
const parentItem = schema.properties?.[key];
|
|
1219
|
+
//如果父级不存在这个属性,并且禁止添加,跳过
|
|
1220
|
+
// todo 还应该增加额外的匹配
|
|
1221
|
+
if (!parentItem && schema.additionalProperties === false) {
|
|
1222
|
+
continue;
|
|
1223
|
+
}
|
|
1224
|
+
const optionsResult = this.getOptions(childList.map((item) => this.resolveSchema2(item.properties[key])));
|
|
1225
|
+
if (optionsResult) {
|
|
1226
|
+
conditionKeyList.push(key);
|
|
1227
|
+
conditionJSchema.properties[key] = {
|
|
1228
|
+
type: '__fixedList',
|
|
1229
|
+
data: optionsResult,
|
|
1230
|
+
};
|
|
1231
|
+
childConditionJSchemaList.forEach((item, i) => {
|
|
1232
|
+
item.properties[key] = childList[i].properties[key];
|
|
1233
|
+
});
|
|
1234
|
+
}
|
|
1235
|
+
else {
|
|
1236
|
+
conditionKeyList.push(key);
|
|
1237
|
+
conditionJSchema.properties[key] = {
|
|
1238
|
+
type: this.resolveSchema2(childList[0].properties[key])
|
|
1239
|
+
.__resolved.type.types[0],
|
|
1240
|
+
};
|
|
1241
|
+
childConditionJSchemaList.forEach((item, i) => {
|
|
1242
|
+
item.properties[key] = childList[i].properties[key];
|
|
1243
|
+
});
|
|
1244
|
+
}
|
|
1153
1245
|
}
|
|
1154
|
-
|
|
1155
|
-
|
|
1246
|
+
return { conditionJSchema, childConditionJSchemaList, conditionKeyList };
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
|
|
1250
|
+
class ListTypeService extends BaseTypeService {
|
|
1251
|
+
name = '__fixedList';
|
|
1252
|
+
parse(actionList) {
|
|
1253
|
+
const context = this.schema['data'];
|
|
1254
|
+
const define = v.picklist(context.options.flat().map((option) => option.value));
|
|
1255
|
+
if (context.multi) {
|
|
1256
|
+
return v.pipe(v.array(define), patchInputs({ options: context.options }), asControl(), setComponent(context.uniqueItems ? 'multiselect' : 'multiselect-repeat'));
|
|
1156
1257
|
}
|
|
1157
|
-
|
|
1158
|
-
|
|
1258
|
+
else {
|
|
1259
|
+
return v.pipe(define, patchInputs({ options: context.options }));
|
|
1159
1260
|
}
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1263
|
+
|
|
1264
|
+
const anyType = [
|
|
1265
|
+
'object',
|
|
1266
|
+
'array',
|
|
1267
|
+
'string',
|
|
1268
|
+
'number',
|
|
1269
|
+
'boolean',
|
|
1270
|
+
'null',
|
|
1271
|
+
'integer',
|
|
1272
|
+
];
|
|
1273
|
+
// 应该传入定制
|
|
1274
|
+
function jsonSchemaToValibot(schema, options) {
|
|
1275
|
+
return new JsonSchemaToValibot(schema, options).convert();
|
|
1276
|
+
}
|
|
1277
|
+
const Schema2012 = 'https://json-schema.org/draft/2020-12/schema';
|
|
1278
|
+
const TypeMap = {
|
|
1279
|
+
number: NumberTypeService,
|
|
1280
|
+
integer: IntegerTypeService,
|
|
1281
|
+
boolean: BooleanTypeService,
|
|
1282
|
+
string: StringTypeService,
|
|
1283
|
+
null: NullTypeService,
|
|
1284
|
+
const: ConstTypeService,
|
|
1285
|
+
object: ObjectTypeService,
|
|
1286
|
+
array: ArrayTypeService,
|
|
1287
|
+
common: CommonTypeService,
|
|
1288
|
+
__fixedList: ListTypeService,
|
|
1289
|
+
};
|
|
1290
|
+
class JsonSchemaToValibot {
|
|
1291
|
+
root;
|
|
1292
|
+
options;
|
|
1293
|
+
cacheSchema = new WeakMap();
|
|
1294
|
+
constructor(root, options) {
|
|
1295
|
+
this.root = root;
|
|
1296
|
+
this.options = options;
|
|
1297
|
+
root.$schema ??= Schema2012;
|
|
1298
|
+
}
|
|
1299
|
+
convert() {
|
|
1300
|
+
const Service = TypeMap['common'];
|
|
1301
|
+
const instance = new Service(this, this.root);
|
|
1302
|
+
return instance.parse([]);
|
|
1303
|
+
}
|
|
1304
|
+
#jsonSchemaCompatiable(schema) {
|
|
1305
|
+
if ('__resolved' in schema && schema.__resolved.isResolved) {
|
|
1306
|
+
return schema;
|
|
1163
1307
|
}
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1308
|
+
const resolved = schema;
|
|
1309
|
+
const type = this.#guessSchemaType(resolved);
|
|
1310
|
+
resolved.__resolved = { ...resolved.__resolved, type, isResolved: true };
|
|
1311
|
+
if (type.types.includes('object')) {
|
|
1312
|
+
this.#objectCompatible(resolved);
|
|
1167
1313
|
}
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
action.push(v.maxEntries(schema.maxProperties));
|
|
1314
|
+
if (type.types.includes('array')) {
|
|
1315
|
+
this.#arrayCompatible(resolved);
|
|
1171
1316
|
}
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1317
|
+
if (type.types.includes('number') || type.types.includes('integer')) {
|
|
1318
|
+
if (resolved.exclusiveMaximum === true) {
|
|
1319
|
+
resolved.exclusiveMaximum = resolved.maximum;
|
|
1320
|
+
delete resolved.maximum;
|
|
1321
|
+
}
|
|
1322
|
+
if (resolved.exclusiveMinimum === true) {
|
|
1323
|
+
resolved.exclusiveMinimum = resolved.minimum;
|
|
1324
|
+
delete resolved.minimum;
|
|
1325
|
+
}
|
|
1175
1326
|
}
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1327
|
+
return resolved;
|
|
1328
|
+
}
|
|
1329
|
+
#resolveDefinition(schema) {
|
|
1330
|
+
if (!schema.$ref) {
|
|
1331
|
+
return schema;
|
|
1332
|
+
}
|
|
1333
|
+
const [uri, pointer] = schema.$ref.split('#/');
|
|
1334
|
+
if (uri) {
|
|
1335
|
+
throw Error(`Remote schemas for ${schema.$ref} not supported yet.`);
|
|
1336
|
+
}
|
|
1337
|
+
const definition = !pointer
|
|
1338
|
+
? null
|
|
1339
|
+
: pointer
|
|
1340
|
+
.split('/')
|
|
1341
|
+
.reduce((def, path) => def?.hasOwnProperty(path) ? def[path] : null, this.root);
|
|
1342
|
+
if (!definition) {
|
|
1343
|
+
throw Error(`Cannot find a definition for ${schema.$ref}.`);
|
|
1344
|
+
}
|
|
1345
|
+
if (definition.$ref) {
|
|
1346
|
+
return this.#resolveDefinition(definition);
|
|
1347
|
+
}
|
|
1348
|
+
return {
|
|
1349
|
+
...definition,
|
|
1350
|
+
...['title', 'description', 'default', 'actions'].reduce((annotation, p) => {
|
|
1351
|
+
if (schema.hasOwnProperty(p)) {
|
|
1352
|
+
annotation[p] = schema[p];
|
|
1182
1353
|
}
|
|
1183
|
-
|
|
1354
|
+
return annotation;
|
|
1355
|
+
}, {}),
|
|
1356
|
+
$ref: undefined,
|
|
1357
|
+
__resolved: {
|
|
1358
|
+
hasRef: true,
|
|
1359
|
+
},
|
|
1360
|
+
};
|
|
1361
|
+
}
|
|
1362
|
+
/** todo 当前只能存在一个类型 */
|
|
1363
|
+
#guessSchemaType(schema) {
|
|
1364
|
+
const optional = 'default' in schema;
|
|
1365
|
+
if (!isUndefined(schema.const)) {
|
|
1366
|
+
return { types: ['const'], optional: optional };
|
|
1367
|
+
}
|
|
1368
|
+
else if (Array.isArray(schema.enum)) {
|
|
1369
|
+
return { types: ['picklist'], optional: optional };
|
|
1370
|
+
}
|
|
1371
|
+
let type = schema?.type;
|
|
1372
|
+
if (isString(type)) {
|
|
1373
|
+
return { types: [type], optional: optional };
|
|
1374
|
+
}
|
|
1375
|
+
if (Array.isArray(type)) {
|
|
1376
|
+
if (type.length === 1) {
|
|
1377
|
+
return { types: type, optional: optional };
|
|
1184
1378
|
}
|
|
1379
|
+
const nullIndex = type.findIndex((item) => item === 'null');
|
|
1380
|
+
if (nullIndex !== -1) {
|
|
1381
|
+
type.splice(nullIndex, 1);
|
|
1382
|
+
}
|
|
1383
|
+
return {
|
|
1384
|
+
types: type,
|
|
1385
|
+
optional: optional || nullIndex !== -1,
|
|
1386
|
+
};
|
|
1185
1387
|
}
|
|
1186
|
-
|
|
1388
|
+
if (schema.items ||
|
|
1389
|
+
schema.prefixItems ||
|
|
1390
|
+
isNumber(schema.minContains) ||
|
|
1391
|
+
isNumber(schema.maxContains) ||
|
|
1392
|
+
!isNil(schema.contains) ||
|
|
1393
|
+
isBoolean(schema.uniqueItems)) {
|
|
1394
|
+
type = 'array';
|
|
1395
|
+
}
|
|
1396
|
+
else if (isNumber(schema.minimum) ||
|
|
1397
|
+
isNumber(schema.maximum) ||
|
|
1398
|
+
isNumber(schema.exclusiveMaximum) ||
|
|
1399
|
+
isNumber(schema.exclusiveMinimum) ||
|
|
1400
|
+
isNumber(schema.multipleOf)) {
|
|
1401
|
+
type = 'number';
|
|
1402
|
+
}
|
|
1403
|
+
else if (isNumber(schema.minLength) ||
|
|
1404
|
+
isNumber(schema.maxLength) ||
|
|
1405
|
+
isString(schema.pattern)) {
|
|
1406
|
+
type = 'string';
|
|
1407
|
+
}
|
|
1408
|
+
return type
|
|
1409
|
+
? { types: [type], optional: optional }
|
|
1410
|
+
: { types: anyType, optional: optional };
|
|
1411
|
+
}
|
|
1412
|
+
#objectCompatible(schema) {
|
|
1413
|
+
if ('dependencies' in schema && schema.dependencies) {
|
|
1414
|
+
const dependencies = schema.dependencies;
|
|
1415
|
+
const dependentRequiredData = {};
|
|
1416
|
+
const dependentSchemasData = {};
|
|
1417
|
+
Object.keys(dependencies).forEach((prop) => {
|
|
1418
|
+
const dependency = dependencies[prop];
|
|
1419
|
+
if (Array.isArray(dependency)) {
|
|
1420
|
+
dependentRequiredData[prop] = dependency;
|
|
1421
|
+
}
|
|
1422
|
+
else {
|
|
1423
|
+
dependentSchemasData[prop] = dependency;
|
|
1424
|
+
}
|
|
1425
|
+
});
|
|
1426
|
+
schema.dependentRequired = dependentRequiredData;
|
|
1427
|
+
schema.dependentSchemas = dependentSchemasData;
|
|
1428
|
+
delete schema.dependencies;
|
|
1429
|
+
}
|
|
1430
|
+
}
|
|
1431
|
+
#arrayCompatible(schema) {
|
|
1432
|
+
if (this.root.$schema !== Schema2012 || !isNil(schema.additionalItems)) {
|
|
1433
|
+
if (!isNil(schema.items) || !isNil(schema.additionalItems)) {
|
|
1434
|
+
// 2019-09
|
|
1435
|
+
schema.prefixItems = schema.items;
|
|
1436
|
+
schema.items = schema.additionalItems;
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
return;
|
|
1440
|
+
}
|
|
1441
|
+
resolveSchema2(schema) {
|
|
1442
|
+
return this.#jsonSchemaCompatiable(this.#resolveDefinition(schema));
|
|
1443
|
+
}
|
|
1444
|
+
getTypeParser(type) {
|
|
1445
|
+
return TypeMap[type];
|
|
1187
1446
|
}
|
|
1188
1447
|
}
|
|
1189
1448
|
|