@piying/view-angular-core 1.7.7 → 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 { map, switchMap } from 'rxjs/operators';
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, formConfig } from '@piying/view-angular-core';
6
- import { isString, isNil, intersection, isBoolean, isUndefined, union, uniq } from 'es-toolkit';
7
- import { BehaviorSubject } from 'rxjs';
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 { deepEqual } from 'fast-equals';
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
- function isNumber(value) {
24
- return typeof value === 'number';
25
- }
26
- const anyType = [
27
- 'object',
28
- 'array',
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
- if (isString(schema.description)) {
43
- action.push(v.description(schema.description));
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
- function jsonSchemaToValibot(schema, options) {
71
- return new JsonSchemaToValibot(schema, options).convert();
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;
82
- }
83
- convert() {
84
- return this.#jSchemaToVSchema(clone(this.root));
655
+ function getMetadataAction(schema) {
656
+ const action = [];
657
+ if (isString(schema.title)) {
658
+ action.push(v.title(schema.title));
85
659
  }
86
- #applicatorParse(schema$1) {
87
- let vSchema;
88
- if (schema$1.allOf) {
89
- const result = this.#mergeSchema(schema$1, ...schema$1.allOf);
90
- vSchema = v.pipe(this.#jsonSchemaBase(result.schema, () => result.actionList));
91
- }
92
- else if (schema$1.anyOf) {
93
- vSchema = this.#conditionCreate(schema$1, {
94
- useOr: false,
95
- getChildren: () => schema$1.anyOf,
96
- conditionCheckActionFn(childOriginSchemaList, getActivateList) {
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
- }));
660
+ if (isString(schema.description)) {
661
+ action.push(v.description(schema.description));
662
+ }
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
- else {
255
- vSchema = v.pipe(
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
- return (this.#options?.schemaHandle?.afterResolve?.(vSchema, schema$1) ?? vSchema);
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.#jSchemaToVSchema(schema.not);
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,489 +705,212 @@ class JsonSchemaToValibot {
279
705
  }
280
706
  return actionList;
281
707
  }
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;
708
+ #jsonSchemaBase(schema, getValidationActionList) {
709
+ const types = schema.__resolved.type;
326
710
  // 暂时为只支持一个
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
- }
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 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());
724
+ return this.typeParse(type, schema, [
725
+ ...getValidationActionList(),
726
+ ...actionList,
727
+ ]);
357
728
  }
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 }) => {
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
- 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
- }
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
- // 普通属性
387
- if (schema$1.properties) {
388
- for (const key in schema$1.properties) {
389
- const propJSchema = schema$1.properties[key];
390
- let propData;
391
- if (isBoolean(propJSchema)) {
392
- propData = { optional: false, hasRef: false };
393
- }
394
- else {
395
- const rSchema = this.#resolveSchema2(propJSchema);
396
- propData = {
397
- optional: rSchema.__resolved.type.optional,
398
- hasRef: rSchema.__resolved.hasRef,
399
- };
400
- }
401
- const isRequired = !!schema$1.required?.includes(key);
402
- const wrapperOptional = !isRequired && !propData.optional;
403
- const createRef = () => {
404
- let propVSchema = this.#jSchemaToVSchema(propJSchema);
405
- const depList = schema$1.dependentRequired?.[key];
406
- if (depList) {
407
- propVSchema = v.pipe(propVSchema, jsonActions.patchHooks({
408
- allFieldsResolved: (field) => {
409
- field.form.control.statusChanges.subscribe(() => {
410
- const valid = field.form.control.valid;
411
- depList.map((item) => {
412
- field.form.parent
413
- .get(item)
414
- ?.config$.update((config) => ({
415
- ...config,
416
- required: valid,
417
- }));
418
- });
419
- });
420
- },
421
- }));
756
+ const result = v.safeParse(item, dataset.value);
757
+ if (!result.success) {
758
+ childFailedResult = { index: index, issues: result.issues };
422
759
  }
423
- return propVSchema;
424
- };
425
- childObject[key] = propData.hasRef
426
- ? wrapperOptional
427
- ? v.optional(v.lazy(() => createRef()))
428
- : v.lazy(() => createRef())
429
- : wrapperOptional
430
- ? v.optional(createRef())
431
- : createRef();
432
- }
433
- }
434
- // 附加属性规则
435
- if (isBoolean(schema$1.additionalProperties)) {
436
- mode = schema$1.additionalProperties === false ? 'strict' : mode;
437
- }
438
- else if (schema$1.additionalProperties) {
439
- mode = 'rest';
440
- // rest要符合的规则
441
- defaultRest = this.#jSchemaToVSchema(schema$1.additionalProperties);
442
- }
443
- const patternRestList = [];
444
- if (schema$1.patternProperties) {
445
- for (const key in schema$1.patternProperties) {
446
- const item = this.#jSchemaToVSchema(schema$1.patternProperties[key]);
447
- if (!item) {
448
- throw new Error(`patternProperties->${key}: 定义未找到`);
449
- }
450
- patternRestList.push({
451
- regexp: new RegExp(key),
452
- schema: item,
760
+ return result.success;
453
761
  });
454
- }
455
- }
456
- /** 条件显示 */
457
- const conditionList = [];
458
- if (schema$1.dependentSchemas) {
459
- const depSchemaMap = {};
460
- for (const key in schema$1.dependentSchemas) {
461
- const jSchema = schema$1.dependentSchemas[key];
462
- let vSchema = this.#jSchemaToVSchema(jSchema);
463
- if (!vSchema) {
464
- throw new Error(`依赖->${key}: 定义未找到`);
465
- }
466
- depSchemaMap[key] = vSchema;
467
- vSchema = v.pipe(vSchema, hideWhen({
468
- disabled: true,
469
- listen: (fn, field) => field
470
- .get(['..', '..', 0, key])
471
- .form.control.statusChanges.pipe(map((item) => item === 'VALID')),
472
- }));
473
- conditionList.push(vSchema);
474
- }
475
- actionList.push(v.rawCheck(({ dataset, addIssue }) => {
476
- if (dataset.issues) {
477
- 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
+ });
478
769
  }
479
- Object.keys(schema$1.dependentSchemas).forEach((key) => {
480
- if (dataset.value?.[key] !== undefined) {
481
- const result = v.safeParse(depSchemaMap[key], dataset.value);
482
- if (!result.success) {
483
- for (const item of result.issues) {
484
- addIssue({
485
- ...item,
486
- });
487
- }
488
- }
489
- }
490
- });
491
- }));
492
- }
493
- if (isBoolean(schema$1.propertyNames) && !schema$1.propertyNames) {
494
- actionList.push(v.check(() => false));
495
- }
496
- else if (schema$1.propertyNames) {
497
- const propNameSchema = this.#jSchemaToVSchema(schema$1.propertyNames);
498
- 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 }) => {
499
787
  if (dataset.issues) {
500
788
  return;
501
789
  }
502
- if (dataset.value && typeof dataset.value === 'object') {
503
- for (const key of Object.keys(dataset.value)) {
504
- const result = v.safeParse(propNameSchema, key);
505
- if (!result.success) {
506
- addIssue({
507
- label: `propertyNames:${key}`,
508
- expected: `[match]`,
509
- received: key,
510
- });
511
- }
512
- }
513
- }
514
- }));
515
- }
516
- let schemaDefine;
517
- if (!Object.keys(childObject).length) {
518
- types.optional = true;
519
- }
520
- if (mode === 'default') {
521
- if (conditionList.length) {
522
- schemaDefine = v.pipe(schema.intersect([
523
- v.looseObject(childObject),
524
- v.optional(v.intersect(conditionList)),
525
- ]));
526
- }
527
- else {
528
- schemaDefine = v.pipe(v.looseObject(childObject));
529
- }
530
- }
531
- else if (mode === 'strict') {
532
- if (conditionList.length) {
533
- schemaDefine = v.pipe(schema.intersect([
534
- v.object(childObject),
535
- v.optional(v.intersect(conditionList)),
536
- ]));
537
- }
538
- else {
539
- schemaDefine = v.pipe(v.object(childObject));
540
- }
541
- }
542
- else {
543
- // rest
544
- let restDefine = v.any();
545
- //propCheck patternMapRest addonRest
546
- if (defaultRest && !patternRestList.length) {
547
- restDefine = defaultRest;
548
- }
549
- else {
550
- restDefine = v.any();
551
- }
552
- if (conditionList.length) {
553
- schemaDefine = v.intersect([
554
- v.objectWithRest(childObject, restDefine),
555
- v.optional(v.intersect(conditionList)),
556
- ]);
557
- }
558
- else {
559
- schemaDefine = v.pipe(v.objectWithRest(childObject, restDefine));
560
- }
561
- }
562
- if (schema$1.patternProperties && schema$1.additionalProperties) {
563
- actionList.push(v.rawCheck(({ dataset, addIssue }) => {
564
- if (dataset.issues || dataset.value === undefined) {
565
- return;
566
- }
567
- if (typeof dataset.value === 'object') {
568
- datasetLoop: for (const key in dataset.value) {
569
- if (key in childObject) {
570
- continue;
571
- }
572
- for (const { regexp, schema } of patternRestList) {
573
- const isMatch = regexp.test(key);
574
- if (!isMatch) {
575
- continue;
576
- }
577
- const result = v.safeParse(schema, dataset.value[key]);
578
- if (!result.success) {
579
- addIssue();
580
- }
581
- continue datasetLoop;
582
- }
583
- if (defaultRest) {
584
- const result = v.safeParse(defaultRest, dataset.value[key]);
585
- if (!result.success) {
586
- addIssue();
587
- }
588
- }
589
- }
590
- }
591
- }));
592
- }
593
- return createTypeFn(schemaDefine);
594
- }
595
- case 'array': {
596
- let parent;
597
- const fixedItems = schema$1.prefixItems;
598
- if (isBoolean(schema$1.contains) && !schema$1.contains) {
599
- actionList.push(createImpasseAction('contains', schema$1.contains));
600
- }
601
- else if (schema$1.contains && !isBoolean(schema$1.contains)) {
602
- const containsSchema = this.#jSchemaToVSchema(schema$1.contains);
603
- const minContains = schema$1.minContains ?? 1;
604
- actionList.push(v.check((list) => {
605
- if (Array.isArray(list)) {
606
- const result = list.filter((item) => v.safeParse(containsSchema, item).success);
607
- if (result.length < minContains) {
790
+ // 验证项全为可选,所以需要这里再次验证
791
+ const hasSuccess = childOriginSchemaList.filter((item, index) => {
792
+ const isActive = getActivateList()[index];
793
+ if (!isActive) {
608
794
  return false;
609
795
  }
610
- if (typeof schema$1.maxContains === 'number') {
611
- return result.length <= schema$1.maxContains;
612
- }
613
- return true;
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
+ });
614
805
  }
615
- return false;
616
- }));
617
- }
618
- const jSchemaToVSchema = (schema) => {
619
- const hasRef = this.#schemahasRef(schema);
620
- return hasRef
621
- ? v.lazy(() => this.#jSchemaToVSchema(schema))
622
- : this.#jSchemaToVSchema(schema);
623
- };
624
- if (fixedItems && fixedItems.length) {
625
- const fixedList = fixedItems.map((item) => jSchemaToVSchema(item));
626
- if (schema$1.items) {
627
- const result = jSchemaToVSchema(schema$1.items);
628
- parent = v.tupleWithRest(fixedList, result);
629
- }
630
- else if (schema$1.items === false) {
631
- parent = v.tuple(fixedList);
632
- }
633
- else {
634
- parent = v.looseTuple(fixedList);
635
- }
636
- }
637
- else if (isBoolean(schema$1.items)) {
638
- parent = schema$1.items ? v.array(v.any()) : v.tuple([]);
639
- }
640
- else if (schema$1.items) {
641
- const result = jSchemaToVSchema(schema$1.items);
642
- parent = v.array(result);
643
- }
644
- else {
645
- parent = v.array(v.any());
646
- }
647
- return createTypeFn(parent);
648
- }
649
- default:
650
- throw new Error(`未知类型:${type}`);
651
- }
652
- }
653
- #resolveDefinition(schema) {
654
- if (!schema.$ref) {
655
- return schema;
656
- }
657
- const [uri, pointer] = schema.$ref.split('#/');
658
- if (uri) {
659
- throw Error(`Remote schemas for ${schema.$ref} not supported yet.`);
660
- }
661
- const definition = !pointer
662
- ? null
663
- : pointer
664
- .split('/')
665
- .reduce((def, path) => def?.hasOwnProperty(path) ? def[path] : null, this.root);
666
- if (!definition) {
667
- throw Error(`Cannot find a definition for ${schema.$ref}.`);
668
- }
669
- if (definition.$ref) {
670
- return this.#resolveDefinition(definition);
671
- }
672
- return {
673
- ...definition,
674
- ...['title', 'description', 'default', 'actions'].reduce((annotation, p) => {
675
- if (schema.hasOwnProperty(p)) {
676
- annotation[p] = schema[p];
677
- }
678
- return annotation;
679
- }, {}),
680
- $ref: undefined,
681
- __resolved: {
682
- hasRef: true,
683
- },
684
- };
685
- }
686
- /** todo 当前只能存在一个类型 */
687
- #guessSchemaType(schema) {
688
- let type = schema?.type;
689
- let optional = 'default' in schema;
690
- if (isString(type)) {
691
- 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
+ });
692
816
  }
693
- if (Array.isArray(type)) {
694
- if (type.length === 1) {
695
- return { types: type, optional: optional };
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);
696
841
  }
697
- const nullIndex = type.findIndex((item) => item === 'null');
698
- if (nullIndex !== -1) {
699
- type.splice(nullIndex, 1);
842
+ else {
843
+ const ifSchema = this.#mergeSchema(schema$1, schema$1.if);
844
+ ifVSchema = v.pipe(this.#jsonSchemaBase(ifSchema.schema, () => ifSchema.actionList));
700
845
  }
701
- return {
702
- types: type,
703
- optional: optional || nullIndex !== -1,
704
- };
705
- }
706
- if (schema.items ||
707
- schema.prefixItems ||
708
- isNumber(schema.minContains) ||
709
- isNumber(schema.maxContains) ||
710
- !isNil(schema.contains) ||
711
- isBoolean(schema.uniqueItems)) {
712
- type = 'array';
713
- }
714
- else if (isNumber(schema.minimum) ||
715
- isNumber(schema.maximum) ||
716
- isNumber(schema.exclusiveMaximum) ||
717
- isNumber(schema.exclusiveMinimum) ||
718
- isNumber(schema.multipleOf)) {
719
- type = 'number';
720
- }
721
- else if (isNumber(schema.minLength) ||
722
- isNumber(schema.maxLength) ||
723
- isString(schema.pattern)) {
724
- type = 'string';
725
- }
726
- return type
727
- ? { types: [type], optional: optional }
728
- : { types: anyType, optional: optional };
729
- }
730
- #objectCompatible(schema) {
731
- if ('dependencies' in schema && schema.dependencies) {
732
- const dependencies = schema.dependencies;
733
- const dependentRequiredData = {};
734
- const dependentSchemasData = {};
735
- Object.keys(dependencies).forEach((prop) => {
736
- const dependency = dependencies[prop];
737
- if (Array.isArray(dependency)) {
738
- 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;
739
881
  }
740
- else {
741
- dependentSchemasData[prop] = dependency;
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
+ }
742
889
  }
743
- });
744
- schema.dependentRequired = dependentRequiredData;
745
- schema.dependentSchemas = dependentSchemasData;
890
+ if (!status && elseSchema) {
891
+ const result = v.safeParse(elseSchema, dataset.value);
892
+ if (!result.success) {
893
+ addIssue();
894
+ return;
895
+ }
896
+ }
897
+ }));
746
898
  }
747
- }
748
- #arrayCompatible(schema) {
749
- if (this.root.$schema !== Schema2012 || !isNil(schema.additionalItems)) {
750
- if (!isNil(schema.items) || !isNil(schema.additionalItems)) {
751
- // 2019-09
752
- schema.prefixItems = schema.items;
753
- schema.items = schema.additionalItems;
754
- }
899
+ else {
900
+ vSchema = v.pipe(
901
+ // 通用部分
902
+ this.#jsonSchemaBase(this.resolveSchema2(schema$1), () => this.getValidationActionList(schema$1)));
755
903
  }
756
- return;
904
+ return (this.instance.options?.schemaHandle?.afterResolve?.(vSchema, schema$1) ??
905
+ vSchema);
757
906
  }
758
907
  #mergeSchema(schema, ...list) {
759
908
  let base = clone(schema);
760
909
  let baseKeyList = Object.keys(base);
761
- const actionList = this.#getValidationAction(base);
762
- for (let childSchema of list.filter((item) => !isBoolean(item))) {
763
- childSchema = this.#resolveSchema2(childSchema);
764
- actionList.push(...this.#getValidationAction(childSchema));
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));
765
914
  baseKeyList = union(baseKeyList, Object.keys(childSchema));
766
915
  for (const key of baseKeyList) {
767
916
  switch (key) {
@@ -789,7 +938,7 @@ class JsonSchemaToValibot {
789
938
  }
790
939
  case 'contains': {
791
940
  if (childSchema[key] === false || base[key] === false) {
792
- actionList.push(v.check(() => false));
941
+ actionList.push(createImpasseAction('contains', false));
793
942
  break;
794
943
  }
795
944
  else if (childSchema[key] === true) {
@@ -829,6 +978,10 @@ class JsonSchemaToValibot {
829
978
  }
830
979
  break;
831
980
  }
981
+ case 'required': {
982
+ childSchema.required = union(childSchema.required ?? [], base.required ?? []);
983
+ break;
984
+ }
832
985
  default:
833
986
  childSchema[key] ??= base[key];
834
987
  break;
@@ -836,106 +989,197 @@ class JsonSchemaToValibot {
836
989
  }
837
990
  base = childSchema;
838
991
  }
839
- return { schema: this.#jsonSchemaCompatiable(base), actionList };
840
- }
841
- #resolveSchema2(schema) {
842
- return this.#jsonSchemaCompatiable(this.#resolveDefinition(schema));
843
- }
844
- #schemahasRef(schema) {
845
- if (isBoolean(schema)) {
846
- return false;
847
- }
848
- else {
849
- const result = this.#resolveSchema2(schema);
850
- return result.__resolved.hasRef;
851
- }
852
- }
853
- #parseEnum(schema) {
854
- // 普通枚举
855
- if (schema.enum) {
856
- return {
857
- type: 'enum',
858
- data: {
859
- enum: schema.enum,
860
- },
861
- };
862
- }
863
- else if (schema.const) {
864
- return { type: 'const', data: { const: schema.const } };
865
- }
866
- else if (schema.items && !isBoolean(schema.items)) {
867
- const result = this.#parseEnum(schema.items);
868
- if (result?.data) {
869
- return {
870
- type: 'multiselect',
871
- data: {
872
- items: result.data,
873
- },
874
- };
875
- }
876
- return undefined;
877
- }
878
- return undefined;
992
+ return { schema: base, actionList };
879
993
  }
880
- #intersectSchemaType(a, b) {
881
- const parent = a ? this.#resolveSchema2(a) : undefined;
882
- b = parent ? this.#mergeSchema(parent, b).schema : b;
883
- const child = this.#resolveSchema2(b);
884
- const parentEnum = parent ? this.#parseEnum(parent) : undefined;
885
- const childEnum = this.#parseEnum(child);
886
- if (parentEnum?.data.items && childEnum?.data.items) {
887
- const result = intersection(parentEnum.data.items.enum, childEnum.data.items.enum);
888
- if (result.length) {
889
- return {
890
- type: 'multiselect',
891
- data: {
892
- items: {
893
- enum: result,
894
- },
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
+ });
895
1073
  },
896
- };
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);
897
1091
  }
898
1092
  }
899
- else if (childEnum?.type === 'multiselect') {
900
- return childEnum;
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);
901
1099
  }
902
- // 枚举
903
- if (parentEnum?.data.enum && childEnum?.data.enum) {
904
- const result = intersection(parentEnum.data.enum, childEnum.data.enum);
905
- if (result.length) {
906
- return {
907
- type: 'enum',
908
- data: { enum: result },
909
- };
910
- }
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;
911
1107
  }
912
- else if (childEnum?.data.enum) {
913
- return childEnum;
914
- }
915
- // 常量
916
- if (isNil(parentEnum?.data.const) && !isNil(childEnum?.data.const)) {
917
- return childEnum;
918
- }
919
- // 类型
920
- const typeResult = parent?.__resolved.type.types
921
- ? intersection(parent.__resolved.type.types, child.__resolved.type.types)
922
- : child.__resolved.type.types;
923
- if (typeResult.length) {
924
- delete child['resolved'];
925
- const result = this.#jsonSchemaCompatiable({
926
- ...child,
927
- type: typeResult[0],
928
- });
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
+ };
929
1144
  return {
930
- type: typeResult[0],
931
- data: result,
1145
+ fn,
1146
+ getMulti: () => isMulti,
932
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);
933
1157
  }
934
- return;
1158
+ return {
1159
+ multi: fn$.getMulti(),
1160
+ options: list,
1161
+ };
935
1162
  }
936
1163
  #schemaExtract(schema, ...childList) {
937
- /** 所有子属性key */
938
- const childKeyList = uniq(childList.flatMap((item) => Object.keys(item.properties ?? {})));
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);
939
1183
  if (!childKeyList.length) {
940
1184
  // 无效返回
941
1185
  return;
@@ -946,235 +1190,243 @@ class JsonSchemaToValibot {
946
1190
  };
947
1191
  const childConditionJSchemaList = childList.map(() => ({ properties: {} }));
948
1192
  const conditionKeyList = [];
949
- for (const key of childKeyList) {
950
- const parentItem = schema.properties?.[key];
951
- //如果父级不存在这个属性,并且禁止添加,跳过
952
- // todo 还应该增加额外的匹配
953
- if (!parentItem && schema.additionalProperties === false) {
954
- continue;
955
- }
956
- // 所有子级都存在某个Key
957
- const keyExist = childList.every((item) => {
958
- const propItem = item.properties?.[key];
959
- // todo 对象应该先解析
960
- return propItem && !isBoolean(propItem) && propItem.type !== 'object';
961
- });
962
- if (!keyExist) {
963
- continue;
964
- }
965
- let currentType = undefined;
966
- const childPropList = [];
967
- for (const sub of childList) {
968
- const result = this.#intersectSchemaType(schema?.properties?.[key], sub.properties[key]);
969
- if (!result) {
970
- currentType = undefined;
971
- break;
972
- }
973
- else if (currentType === undefined ||
974
- deepEqual(currentType, result.type)) {
975
- currentType = result.type;
976
- // 枚举
977
- if (result.data.enum ||
978
- 'const' in result.data ||
979
- result.type === 'multiselect') {
980
- childPropList.push(result.data);
981
- }
982
- else {
983
- childPropList.push(result.data);
984
- }
985
- }
986
- else {
987
- break;
988
- }
989
- }
990
- if (currentType) {
991
- conditionKeyList.push(key);
992
- for (let index = 0; index < childConditionJSchemaList.length; index++) {
993
- const schema = childConditionJSchemaList[index];
994
- schema.properties[key] = childPropList[index];
995
- }
996
- if (currentType === 'enum') {
997
- conditionJSchema.properties[key] = {
998
- enum: childPropList.flatMap((item) => item.enum),
999
- };
1000
- }
1001
- else if (currentType === 'const') {
1002
- conditionJSchema.properties[key] = {
1003
- enum: childPropList.flatMap((item) => item.const),
1004
- };
1005
- }
1006
- else if (currentType === 'multiselect') {
1007
- conditionJSchema.properties[key] = {
1008
- type: 'array',
1009
- items: {
1010
- enum: childPropList.flatMap((item) => item.items.enum),
1011
- },
1012
- uniqueItems: true,
1013
- };
1014
- }
1015
- else {
1016
- conditionJSchema.properties[key] = {
1017
- type: currentType,
1018
- };
1019
- }
1020
- }
1021
- }
1022
- return { conditionJSchema, childConditionJSchemaList, conditionKeyList };
1023
- }
1024
- #conditionCreate(schema, options) {
1025
- const resolvedChildList = options
1026
- .getChildren()
1027
- .map((item) => this.#mergeSchema(schema, item));
1028
- const resolvedChildJSchemaList = resolvedChildList.map((item) => item.schema);
1029
- const isObject = [schema, ...resolvedChildJSchemaList].every((item) => item.__resolved.type.types.includes('object'));
1030
- /** 通用子级验证部分,不显示 */
1031
- const childOriginSchemaList = resolvedChildList.map((item) => v.pipe(this.#jsonSchemaBase(item.schema, () => item.actionList)));
1032
- let activateList = [];
1033
- const conditionCheckAction = options.conditionCheckActionFn(childOriginSchemaList, () => activateList);
1034
- // 仅处理object,实现条件显示
1035
- if (isObject) {
1036
- const conditionResult = this.#schemaExtract(schema, ...resolvedChildJSchemaList);
1037
- if (conditionResult) {
1038
- /** 子级的共同部分验证,用于检测是哪个子级,不显示 */
1039
- const childConditionVSchemaList = conditionResult.childConditionJSchemaList.map((schema) => {
1040
- const rSchema = this.#jsonSchemaCompatiable(schema);
1041
- return v.pipe(this.#jsonSchemaBase(rSchema, () => this.#getValidationAction(rSchema)));
1193
+ for (const key of childKeyList) {
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];
1042
1209
  });
1043
- /** 主条件部分,用于显示切换 */
1044
- const conditionVSchema = v.pipe(this.#jsonSchemaBase(this.#jsonSchemaCompatiable(conditionResult.conditionJSchema), () => []), jsonActions.valueChange((fn) => {
1045
- fn().subscribe(({ list: [value], field }) => {
1046
- activateList.length = 0;
1047
- const conditionParent = field.get(['..', 2]).form
1048
- .control;
1049
- const parentAList = [];
1050
- for (let index = 0; index < childConditionVSchemaList.length; index++) {
1051
- const schema = childConditionVSchemaList[index];
1052
- const result = v.safeParse(schema, value);
1053
- activateList.push(result.success);
1054
- field.get(['..', 2, index])?.renderConfig.update((data) => ({
1055
- ...data,
1056
- hidden: !result.success,
1057
- }));
1058
- if (result.success) {
1059
- parentAList.push(conditionParent.children$$()[index]);
1060
- }
1061
- }
1062
- if (!options.useOr) {
1063
- conditionParent.activateControls$.set(parentAList);
1064
- }
1065
- });
1066
- }));
1067
- conditionResult.conditionKeyList.forEach((key) => {
1068
- resolvedChildJSchemaList.forEach((item) => {
1069
- delete item.properties[key];
1070
- });
1071
- 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];
1072
1219
  });
1073
- const baseSchema = v.pipe(this.#jsonSchemaBase(schema, () => this.#getValidationAction(schema)));
1074
- const childVSchemaList = resolvedChildJSchemaList.map((item) =>
1075
- // 验证部分被单独提取出来
1076
- v.pipe(this.#jsonSchemaBase(item, () => [])));
1077
- return v.pipe(options.conditionSchemaFn(baseSchema, conditionVSchema, childVSchemaList), conditionCheckAction);
1078
1220
  }
1079
1221
  }
1080
- const baseSchema = v.pipe(this.#jsonSchemaBase(schema, () => this.#getValidationAction(schema)));
1081
- activateList = childOriginSchemaList.map((_, i) => true);
1082
- return v.pipe(baseSchema, conditionCheckAction);
1222
+ return { conditionJSchema, childConditionJSchemaList, conditionKeyList };
1083
1223
  }
1084
- #getValidationAction(schema) {
1085
- const action = [];
1086
- // string/array
1087
- if (isNumber(schema.minLength) || isNumber(schema.minItems)) {
1088
- action.push(v.minLength(schema.minLength ?? schema.minItems));
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);
1089
1233
  }
1090
- // string/array
1091
- if (isNumber(schema.maxLength) || isNumber(schema.maxItems)) {
1092
- action.push(v.maxLength(schema.maxLength ?? schema.maxItems));
1234
+ else {
1235
+ return define;
1093
1236
  }
1094
- // string
1095
- if (isString(schema.pattern)) {
1096
- action.push(v.regex(new RegExp(schema.pattern)));
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;
1097
1291
  }
1098
- // todo format https://json-schema.org/understanding-json-schema/reference/type#built-in-formats
1099
- // duration idn-email idn-hostname uri-reference iri iri-reference uri-template json-pointer regex
1100
- if (schema.format) {
1101
- switch (schema.format) {
1102
- // case 'date-time': {
1103
- // action.push(v.isoDateTime());
1104
- // break;
1105
- // }
1106
- // case 'time': {
1107
- // action.push(v.isoTime());
1108
- // break;
1109
- // }
1110
- case 'date': {
1111
- action.push(v.isoDate());
1112
- break;
1113
- }
1114
- case 'email': {
1115
- action.push(v.email());
1116
- break;
1117
- }
1118
- case 'ipv4': {
1119
- action.push(v.ipv4());
1120
- break;
1121
- }
1122
- case 'ipv6': {
1123
- action.push(v.ipv6());
1124
- break;
1125
- }
1126
- case 'uuid': {
1127
- action.push(v.uuid());
1128
- break;
1129
- }
1130
- case 'uri': {
1131
- action.push(v.url());
1132
- break;
1133
- }
1134
- default:
1135
- 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;
1136
1309
  }
1137
1310
  }
1138
- // number
1139
- if (isNumber(schema.exclusiveMinimum)) {
1140
- action.push(v.gtValue(schema.exclusiveMinimum));
1311
+ return resolved;
1312
+ }
1313
+ #resolveDefinition(schema) {
1314
+ if (!schema.$ref) {
1315
+ return schema;
1141
1316
  }
1142
- if (isNumber(schema.exclusiveMaximum)) {
1143
- action.push(v.ltValue(schema.exclusiveMaximum));
1317
+ const [uri, pointer] = schema.$ref.split('#/');
1318
+ if (uri) {
1319
+ throw Error(`Remote schemas for ${schema.$ref} not supported yet.`);
1144
1320
  }
1145
- if (isNumber(schema.minimum)) {
1146
- action.push(v.minValue(schema.minimum));
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}.`);
1147
1328
  }
1148
- if (isNumber(schema.maximum)) {
1149
- action.push(v.maxValue(schema.maximum));
1329
+ if (definition.$ref) {
1330
+ return this.#resolveDefinition(definition);
1150
1331
  }
1151
- // number
1152
- if (isNumber(schema.multipleOf)) {
1153
- action.push(v.multipleOf(schema.multipleOf));
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 };
1154
1351
  }
1155
- // array
1156
- if (schema.uniqueItems) {
1157
- action.push(v.check((input) => uniq(input).length === input.length));
1352
+ else if (Array.isArray(schema.enum)) {
1353
+ return { types: ['picklist'], optional: optional };
1158
1354
  }
1159
- // object
1160
- if (isNumber(schema.maxProperties)) {
1161
- action.push(v.maxEntries(schema.maxProperties));
1355
+ let type = schema?.type;
1356
+ if (isString(type)) {
1357
+ return { types: [type], optional: optional };
1162
1358
  }
1163
- // object
1164
- if (isNumber(schema.minProperties)) {
1165
- action.push(v.minEntries(schema.minProperties));
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
+ };
1166
1371
  }
1167
- if (schema.actions) {
1168
- for (const rawAction of schema.actions) {
1169
- const inlineActions = jsonActions[rawAction.name] ??
1170
- this.#options?.customActions?.[rawAction.name];
1171
- if (!inlineActions) {
1172
- throw new Error(`action:[${rawAction.name}]❗`);
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;
1405
+ }
1406
+ else {
1407
+ dependentSchemasData[prop] = dependency;
1173
1408
  }
1174
- action.push(inlineActions.apply(undefined, rawAction.params));
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;
1175
1421
  }
1176
1422
  }
1177
- return action;
1423
+ return;
1424
+ }
1425
+ resolveSchema2(schema) {
1426
+ return this.#jsonSchemaCompatiable(this.#resolveDefinition(schema));
1427
+ }
1428
+ getTypeParser(type) {
1429
+ return TypeMap[type];
1178
1430
  }
1179
1431
  }
1180
1432