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