fork of hey-api/openapi-ts because I need some additional things
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

refactor: valibot plugin nullish

Lubos ea6f3865 a55a5e12

+1288 -674
+5
.changeset/brown-ideas-matter.md
··· 1 + --- 2 + "@hey-api/shared": patch 3 + --- 4 + 5 + **internal**: export schema walker interfaces
+9
.changeset/clean-singers-stand.md
··· 1 + --- 2 + "@hey-api/openapi-ts": minor 3 + --- 4 + 5 + **plugin(valibot)**: remove `enum.nodes.nullable` resolver node 6 + 7 + ### Removed resolver node 8 + 9 + Valibot plugin no longer exposes the `enum.nodes.nullable` node. It was refactored so that nullable values are handled outside of resolvers.
+5
.changeset/stale-falcons-raise.md
··· 1 + --- 2 + "@hey-api/openapi-ts": patch 3 + --- 4 + 5 + **plugin(valibot)**: use `.nullable()` and `.nullish()` methods
+6
docs/openapi-ts/migrating.md
··· 7 7 8 8 While we try to avoid breaking changes, sometimes it's unavoidable in order to offer you the latest features. This page lists changes that require updates to your code. If you run into a problem with migration, please [open an issue](https://github.com/hey-api/openapi-ts/issues). 9 9 10 + ## v0.93.0 11 + 12 + ### Removed resolver node 13 + 14 + Valibot and Zod plugins no longer expose the `enum.nodes.nullable` node. Both plugins were refactored so that nullable values are handled outside of resolvers. 15 + 10 16 ## v0.92.0 11 17 12 18 ### Updated Symbol interface
+24 -13
packages/openapi-python/src/plugins/pydantic/shared/export.ts
··· 1 - import type { Symbol } from '@hey-api/codegen-core'; 2 - import type { IR } from '@hey-api/shared'; 1 + import { applyNaming, pathToName } from '@hey-api/shared'; 3 2 4 3 // import { createSchemaComment } from '../../../plugins/shared/utils/schema'; 5 4 import { $ } from '../../../py-dsl'; 5 + import type { ProcessorContext } from './processor'; 6 6 // import { identifiers } from '../v2/constants'; 7 7 // import { pipesToNode } from './pipes'; 8 8 import type { Ast, IrSchemaToAstOptions } from './types'; 9 9 10 10 export function exportAst({ 11 - // ast, 11 + meta, 12 + naming, 13 + namingAnchor, 14 + path, 12 15 plugin, 13 - // schema, 14 - // state, 15 - symbol, 16 - }: IrSchemaToAstOptions & { 17 - ast: Ast; 18 - schema: IR.SchemaObject; 19 - symbol: Symbol; 20 - }): void { 21 - // const v = plugin.external('valibot.v'); 22 - const classDef = $.class(symbol); 16 + tags, 17 + }: Pick<IrSchemaToAstOptions, 'state'> & 18 + ProcessorContext & { 19 + ast: Ast; 20 + }): void { 21 + const name = pathToName(path, { anchor: namingAnchor }); 22 + const symbol = plugin.symbol(applyNaming(name, naming), { 23 + meta: { 24 + category: 'schema', 25 + path, 26 + tags, 27 + tool: 'pydantic', 28 + ...meta, 29 + }, 30 + }); 31 + 32 + const baseModel = plugin.external('pydantic.BaseModel'); 33 + const classDef = $.class(symbol).extends(baseModel); 23 34 // .export() 24 35 // .$if(plugin.config.comments && createSchemaComment(schema), (c, v) => c.doc(v)) 25 36 // .$if(state.hasLazyExpression['~ref'], (c) =>
+16
packages/openapi-python/src/plugins/pydantic/shared/processor.ts
··· 1 + import type { 2 + IR, 3 + NamingConfig, 4 + SchemaProcessorContext, 5 + SchemaProcessorResult, 6 + } from '@hey-api/shared'; 7 + 8 + import type { IrSchemaToAstOptions } from './types'; 9 + 10 + export type ProcessorContext = Pick<IrSchemaToAstOptions, 'plugin'> & 11 + SchemaProcessorContext & { 12 + naming: NamingConfig; 13 + schema: IR.SchemaObject; 14 + }; 15 + 16 + export type ProcessorResult = SchemaProcessorResult<ProcessorContext>;
+2 -1
packages/openapi-python/src/plugins/pydantic/shared/types.ts
··· 3 3 4 4 import type { $ } from '../../../py-dsl'; 5 5 import type { PydanticPlugin } from '../types'; 6 + import type { ProcessorContext } from './processor'; 6 7 7 8 export type Ast = { 8 9 /** ··· 32 33 /** The plugin instance. */ 33 34 plugin: PydanticPlugin['Instance']; 34 35 /** Optional schema extractor function. */ 35 - schemaExtractor?: SchemaExtractor; 36 + schemaExtractor?: SchemaExtractor<ProcessorContext>; 36 37 /** The plugin state references. */ 37 38 state: Refs<PluginState>; 38 39 };
+32 -52
packages/openapi-python/src/plugins/pydantic/v2/plugin.ts
··· 1 1 import type { SymbolMeta } from '@hey-api/codegen-core'; 2 - import { fromRef, ref, refs } from '@hey-api/codegen-core'; 2 + import { fromRef, ref } from '@hey-api/codegen-core'; 3 3 import type { IR, SchemaWithType } from '@hey-api/shared'; 4 - import { applyNaming, deduplicateSchema, pathToJsonPointer, refToName } from '@hey-api/shared'; 4 + import { deduplicateSchema, pathToJsonPointer } from '@hey-api/shared'; 5 5 6 6 // import { $ } from '../../../py-dsl'; 7 - import { exportAst } from '../shared/export'; 8 - import type { Ast, IrSchemaToAstOptions, PluginState } from '../shared/types'; 7 + import type { Ast, IrSchemaToAstOptions } from '../shared/types'; 9 8 import type { PydanticPlugin } from '../types'; 9 + import { createProcessor } from './processor'; 10 10 import { irSchemaWithTypeToAst } from './toAst'; 11 11 12 12 export function irSchemaToAst({ ··· 25 25 resource: 'definition', 26 26 resourceId: pathToJsonPointer(fromRef(state.path)), 27 27 }, 28 + naming: plugin.config.definitions, 28 29 path: fromRef(state.path), 30 + plugin, 29 31 schema, 30 32 }); 31 33 if (extracted !== schema) schema = extracted; ··· 119 121 }; 120 122 } 121 123 122 - function handleComponent({ 123 - plugin, 124 - schema, 125 - state, 126 - }: IrSchemaToAstOptions & { 127 - schema: IR.SchemaObject; 128 - }): void { 129 - const $ref = pathToJsonPointer(fromRef(state.path)); 130 - const baseName = refToName($ref); 131 - const symbol = plugin.symbol(applyNaming(baseName, plugin.config.definitions), { 132 - meta: { 133 - category: 'schema', 134 - path: fromRef(state.path), 135 - resource: 'definition', 136 - resourceId: $ref, 137 - tags: fromRef(state.tags), 138 - tool: 'pydantic', 139 - }, 140 - }); 141 - const ast = irSchemaToAst({ 142 - plugin, 143 - schema, 144 - state, 145 - }); 146 - exportAst({ 147 - ast, 148 - plugin, 149 - schema, 150 - state, 151 - symbol, 152 - }); 153 - } 154 - 155 124 export const handlerV2: PydanticPlugin['Handler'] = ({ plugin }) => { 156 125 plugin.symbol('Any', { 157 126 external: 'typing', ··· 202 171 }, 203 172 }); 204 173 174 + const processor = createProcessor(plugin); 175 + 205 176 plugin.forEach('operation', 'parameter', 'requestBody', 'schema', 'webhook', (event) => { 206 - const state = refs<PluginState>({ 207 - hasLazyExpression: false, 208 - path: event._path, 209 - tags: event.tags, 210 - }); 211 - 212 177 switch (event.type) { 213 178 case 'parameter': 214 - handleComponent({ 215 - // baseName: event.name, 179 + processor.process({ 180 + meta: { 181 + resource: 'definition', 182 + resourceId: pathToJsonPointer(event._path), 183 + }, 184 + naming: plugin.config.definitions, 185 + path: event._path, 216 186 plugin, 217 187 schema: event.parameter.schema, 218 - state, 188 + tags: event.tags, 219 189 }); 220 190 break; 221 191 case 'requestBody': 222 - handleComponent({ 223 - // baseName: event.name, 192 + processor.process({ 193 + meta: { 194 + resource: 'definition', 195 + resourceId: pathToJsonPointer(event._path), 196 + }, 197 + naming: plugin.config.definitions, 198 + path: event._path, 224 199 plugin, 225 200 schema: event.requestBody.schema, 226 - state, 201 + tags: event.tags, 227 202 }); 228 203 break; 229 204 case 'schema': 230 - handleComponent({ 231 - // baseName: event.name, 205 + processor.process({ 206 + meta: { 207 + resource: 'definition', 208 + resourceId: pathToJsonPointer(event._path), 209 + }, 210 + naming: plugin.config.definitions, 211 + path: event._path, 232 212 plugin, 233 213 schema: event.schema, 234 - state, 214 + tags: event.tags, 235 215 }); 236 216 break; 237 217 }
+58
packages/openapi-python/src/plugins/pydantic/v2/processor.ts
··· 1 + import { refs } from '@hey-api/codegen-core'; 2 + import type { IR } from '@hey-api/shared'; 3 + import { createSchemaProcessor, pathToJsonPointer } from '@hey-api/shared'; 4 + 5 + import { exportAst } from '../shared/export'; 6 + import type { ProcessorContext, ProcessorResult } from '../shared/processor'; 7 + import type { PluginState } from '../shared/types'; 8 + import type { PydanticPlugin } from '../types'; 9 + import { irSchemaToAst } from './plugin'; 10 + 11 + export function createProcessor(plugin: PydanticPlugin['Instance']): ProcessorResult { 12 + const processor = createSchemaProcessor(); 13 + 14 + const hooks = [plugin.config['~hooks']?.schemas, plugin.context.config.parser.hooks.schemas]; 15 + 16 + function extractor(ctx: ProcessorContext): IR.SchemaObject { 17 + if (processor.hasEmitted(ctx.path)) { 18 + return ctx.schema; 19 + } 20 + 21 + for (const hook of hooks) { 22 + const result = hook?.shouldExtract?.(ctx); 23 + if (result) { 24 + process({ 25 + namingAnchor: processor.context.anchor, 26 + tags: processor.context.tags, 27 + ...ctx, 28 + }); 29 + return { $ref: pathToJsonPointer(ctx.path) }; 30 + } 31 + } 32 + 33 + return ctx.schema; 34 + } 35 + 36 + function process(ctx: ProcessorContext): void { 37 + if (!processor.markEmitted(ctx.path)) return; 38 + 39 + processor.withContext({ anchor: ctx.namingAnchor, tags: ctx.tags }, () => { 40 + const state = refs<PluginState>({ 41 + hasLazyExpression: false, 42 + path: ctx.path, 43 + tags: ctx.tags, 44 + }); 45 + 46 + const ast = irSchemaToAst({ 47 + plugin, 48 + schema: ctx.schema, 49 + schemaExtractor: extractor, 50 + state, 51 + }); 52 + 53 + exportAst({ ...ctx, ast, plugin, state }); 54 + }); 55 + } 56 + 57 + return { process }; 58 + }
+4 -4
packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/valibot/default/valibot.gen.ts
··· 197 197 * This is a model with one string property 198 198 */ 199 199 export const vModelWithNullableString = v.object({ 200 - nullableProp: v.optional(v.union([v.string(), v.null()])), 201 - nullableRequiredProp: v.union([v.string(), v.null()]) 200 + nullableProp: v.nullish(v.string()), 201 + nullableRequiredProp: v.nullable(v.string()) 202 202 }); 203 203 204 204 /** ··· 581 581 parameterStringWithDefault: v.optional(v.string(), 'Hello World!'), 582 582 parameterStringWithEmptyDefault: v.optional(v.string(), ''), 583 583 parameterStringWithNoDefault: v.string(), 584 - parameterStringNullableWithNoDefault: v.optional(v.union([v.string(), v.null()])), 585 - parameterStringNullableWithDefault: v.optional(v.union([v.string(), v.null()]), null) 584 + parameterStringNullableWithNoDefault: v.nullish(v.string()), 585 + parameterStringNullableWithDefault: v.nullish(v.string(), null) 586 586 }) 587 587 }); 588 588
+75 -89
packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/valibot/default/valibot.gen.ts
··· 81 81 /** 82 82 * This is a simple string 83 83 */ 84 - export const vSimpleStringWithPattern = v.union([v.pipe(v.string(), v.maxLength(64), v.regex(/^[a-zA-Z0-9_]*$/)), v.null()]); 84 + export const vSimpleStringWithPattern = v.nullable(v.pipe(v.string(), v.maxLength(64), v.regex(/^[a-zA-Z0-9_]*$/))); 85 85 86 86 /** 87 87 * This is a simple enum with strings ··· 243 243 * This is a model with one string property 244 244 */ 245 245 export const vModelWithNullableString = v.object({ 246 - nullableProp1: v.optional(v.union([v.string(), v.null()])), 247 - nullableRequiredProp1: v.union([v.string(), v.null()]), 248 - nullableProp2: v.optional(v.union([v.string(), v.null()])), 249 - nullableRequiredProp2: v.union([v.string(), v.null()]), 246 + nullableProp1: v.nullish(v.string()), 247 + nullableRequiredProp1: v.nullable(v.string()), 248 + nullableProp2: v.nullish(v.string()), 249 + nullableRequiredProp2: v.nullable(v.string()), 250 250 'foo_bar-enum': v.optional(v.picklist([ 251 251 'Success', 252 252 'Warning', ··· 425 425 * This is a model with nested 'any of' property with a type null 426 426 */ 427 427 export const vCompositionWithNestedAnyAndTypeNull = v.object({ 428 - propA: v.optional(v.union([v.array(v.union([vModelWithDictionary, v.null()])), v.array(v.union([vModelWithArray, v.null()]))])) 428 + propA: v.optional(v.union([v.array(v.nullable(vModelWithDictionary)), v.array(v.nullable(vModelWithArray))])) 429 429 }); 430 430 431 431 export const v3eNum1Период = v.picklist(['Bird', 'Dog']); ··· 436 436 * This is a model with one property with a 'any of' relationship where the options are not $ref 437 437 */ 438 438 export const vCompositionWithNestedAnyOfAndNull = v.object({ 439 - propA: v.optional(v.union([v.array(v.unknown()), v.null()])) 439 + propA: v.nullish(v.array(v.unknown())) 440 440 }); 441 441 442 442 /** 443 443 * This is a model with one property with a 'one of' relationship 444 444 */ 445 445 export const vCompositionWithOneOfAndNullable = v.object({ 446 - propA: v.optional(v.union([ 446 + propA: v.nullish(v.union([ 447 447 v.object({ 448 448 boolean: v.optional(v.boolean()) 449 449 }), 450 450 vModelWithEnum, 451 451 vModelWithArray, 452 - vModelWithDictionary, 453 - v.null() 452 + vModelWithDictionary 454 453 ])) 455 454 }); 456 455 ··· 479 478 * This is a model with one property with a 'all of' relationship 480 479 */ 481 480 export const vCompositionWithAllOfAndNullable = v.object({ 482 - propA: v.optional(v.union([v.intersect([ 483 - v.object({ 484 - boolean: v.optional(v.boolean()) 485 - }), 486 - vModelWithEnum, 487 - vModelWithArray, 488 - vModelWithDictionary 489 - ]), v.null()])) 481 + propA: v.nullish(v.intersect([ 482 + v.object({ 483 + boolean: v.optional(v.boolean()) 484 + }), 485 + vModelWithEnum, 486 + vModelWithArray, 487 + vModelWithDictionary 488 + ])) 490 489 }); 491 490 492 491 /** 493 492 * This is a model with one property with a 'any of' relationship 494 493 */ 495 494 export const vCompositionWithAnyOfAndNullable = v.object({ 496 - propA: v.optional(v.union([ 495 + propA: v.nullish(v.union([ 497 496 v.object({ 498 497 boolean: v.optional(v.boolean()) 499 498 }), 500 499 vModelWithEnum, 501 500 vModelWithArray, 502 - vModelWithDictionary, 503 - v.null() 501 + vModelWithDictionary 504 502 ])) 505 503 }); 506 504 ··· 527 525 export const vModelWithProperties = v.object({ 528 526 required: v.string(), 529 527 requiredAndReadOnly: v.pipe(v.string(), v.readonly()), 530 - requiredAndNullable: v.union([v.string(), v.null()]), 528 + requiredAndNullable: v.nullable(v.string()), 531 529 string: v.optional(v.string()), 532 530 number: v.optional(v.number()), 533 531 boolean: v.optional(v.boolean()), ··· 550 548 * This is a model with one nested property 551 549 */ 552 550 export const vModelWithNestedProperties = v.object({ 553 - first: v.pipe(v.union([v.pipe(v.object({ 554 - second: v.pipe(v.union([v.pipe(v.object({ 555 - third: v.pipe(v.union([v.pipe(v.string(), v.readonly()), v.null()]), v.readonly()) 556 - }), v.readonly()), v.null()]), v.readonly()) 557 - }), v.readonly()), v.null()]), v.readonly()) 551 + first: v.nullable(v.pipe(v.object({ 552 + second: v.nullable(v.pipe(v.object({ 553 + third: v.nullable(v.pipe(v.string(), v.readonly())) 554 + }), v.readonly())) 555 + }), v.readonly())) 558 556 }); 559 557 560 558 /** ··· 665 663 }, v.unknown()); 666 664 667 665 export const vNestedAnyOfArraysNullable = v.object({ 668 - nullableArray: v.optional(v.union([v.array(v.unknown()), v.null()])) 666 + nullableArray: v.nullish(v.array(v.unknown())) 669 667 }); 670 668 671 669 /** 672 670 * An object that can be null 673 671 */ 674 - export const vNullableObject = v.optional(v.union([v.object({ 675 - foo: v.optional(v.string()) 676 - }), v.null()]), null); 672 + export const vNullableObject = v.nullish(v.object({ 673 + foo: v.optional(v.string()) 674 + }), null); 677 675 678 676 /** 679 677 * Some % character ··· 747 745 export const vModelWithPrefixItemsConstantSizeArray = v.array(v.unknown()); 748 746 749 747 export const vModelWithAnyOfConstantSizeArrayNullable = v.tuple([ 750 - v.union([ 751 - v.number(), 752 - v.null(), 753 - v.string() 754 - ]), 755 - v.union([ 756 - v.number(), 757 - v.null(), 758 - v.string() 759 - ]), 760 - v.union([ 761 - v.number(), 762 - v.null(), 763 - v.string() 764 - ]) 748 + v.nullable(v.union([v.number(), v.string()])), 749 + v.nullable(v.union([v.number(), v.string()])), 750 + v.nullable(v.union([v.number(), v.string()])) 765 751 ]); 766 752 767 753 export const vModelWithAnyOfConstantSizeArrayAndIntersect = v.tuple([v.intersect([v.number(), v.string()]), v.intersect([v.number(), v.string()])]); ··· 860 846 861 847 export const vGenericSchemaDuplicateIssue1SystemBoolean = v.strictObject({ 862 848 item: v.optional(v.boolean()), 863 - error: v.optional(v.union([v.string(), v.null()])), 849 + error: v.nullish(v.string()), 864 850 hasError: v.optional(v.pipe(v.boolean(), v.readonly())), 865 851 data: v.optional(v.strictObject({})) 866 852 }); 867 853 868 854 export const vGenericSchemaDuplicateIssue1SystemString = v.strictObject({ 869 - item: v.optional(v.union([v.string(), v.null()])), 870 - error: v.optional(v.union([v.string(), v.null()])), 855 + item: v.nullish(v.string()), 856 + error: v.nullish(v.string()), 871 857 hasError: v.optional(v.pipe(v.boolean(), v.readonly())) 872 858 }); 873 859 ··· 887 873 */ 888 874 export const vModelWithPropertiesWritable = v.object({ 889 875 required: v.string(), 890 - requiredAndNullable: v.union([v.string(), v.null()]), 876 + requiredAndNullable: v.nullable(v.string()), 891 877 string: v.optional(v.string()), 892 878 number: v.optional(v.number()), 893 879 boolean: v.optional(v.boolean()), ··· 943 929 944 930 export const vGenericSchemaDuplicateIssue1SystemBooleanWritable = v.strictObject({ 945 931 item: v.optional(v.boolean()), 946 - error: v.optional(v.union([v.string(), v.null()])), 932 + error: v.nullish(v.string()), 947 933 data: v.optional(v.strictObject({})) 948 934 }); 949 935 950 936 export const vGenericSchemaDuplicateIssue1SystemStringWritable = v.strictObject({ 951 - item: v.optional(v.union([v.string(), v.null()])), 952 - error: v.optional(v.union([v.string(), v.null()])) 937 + item: v.nullish(v.string()), 938 + error: v.nullish(v.string()) 953 939 }); 954 940 955 941 /** ··· 962 948 }), v.strictObject({ 963 949 bar: vNonAsciiStringæøåÆøÅöôêÊ字符串 964 950 })]), v.object({ 965 - baz: v.union([v.pipe(v.number(), v.integer(), v.minValue(0), v.maxValue(65535, 'Invalid value: Expected uint16 to be <= 65535')), v.null()]), 951 + baz: v.nullable(v.pipe(v.number(), v.integer(), v.minValue(0), v.maxValue(65535, 'Invalid value: Expected uint16 to be <= 65535'))), 966 952 qux: v.pipe(v.number(), v.integer(), v.minValue(0), v.maxValue(255, 'Invalid value: Expected uint8 to be <= 255')) 967 953 })]); 968 954 969 955 export const vModelWithOneOfAndProperties = v.intersect([v.union([vSimpleParameter, vNonAsciiStringæøåÆøÅöôêÊ字符串]), v.object({ 970 - baz: v.union([v.pipe(v.number(), v.integer(), v.minValue(0), v.maxValue(65535, 'Invalid value: Expected uint16 to be <= 65535')), v.null()]), 956 + baz: v.nullable(v.pipe(v.number(), v.integer(), v.minValue(0), v.maxValue(65535, 'Invalid value: Expected uint16 to be <= 65535'))), 971 957 qux: v.pipe(v.number(), v.integer(), v.minValue(0), v.maxValue(255, 'Invalid value: Expected uint8 to be <= 255')) 972 958 })]); 973 959 ··· 1102 1088 path: v.optional(v.never()), 1103 1089 query: v.optional(v.never()), 1104 1090 headers: v.object({ 1105 - parameter: v.union([vDeprecatedModel, v.null()]) 1091 + parameter: v.nullable(vDeprecatedModel) 1106 1092 }) 1107 1093 }); 1108 1094 1109 1095 export const vCallWithParametersData = v.object({ 1110 - body: v.union([v.record(v.string(), v.unknown()), v.null()]), 1096 + body: v.nullable(v.record(v.string(), v.unknown())), 1111 1097 path: v.object({ 1112 - parameterPath: v.union([v.string(), v.null()]), 1113 - 'api-version': v.union([v.string(), v.null()]) 1098 + parameterPath: v.nullable(v.string()), 1099 + 'api-version': v.nullable(v.string()) 1114 1100 }), 1115 1101 query: v.object({ 1116 1102 foo_ref_enum: v.optional(vModelWithNestedArrayEnumsDataFoo), 1117 1103 foo_all_of_enum: vModelWithNestedArrayEnumsDataFoo, 1118 - cursor: v.union([v.string(), v.null()]) 1104 + cursor: v.nullable(v.string()) 1119 1105 }), 1120 1106 headers: v.object({ 1121 - parameterHeader: v.union([v.string(), v.null()]) 1107 + parameterHeader: v.nullable(v.string()) 1122 1108 }) 1123 1109 }); 1124 1110 1125 1111 export const vCallWithWeirdParameterNamesData = v.object({ 1126 - body: v.union([vModelWithString, v.null()]), 1112 + body: v.nullable(vModelWithString), 1127 1113 path: v.object({ 1128 1114 'parameter.path.1': v.optional(v.string()), 1129 1115 'parameter-path-2': v.optional(v.string()), 1130 1116 'PARAMETER-PATH-3': v.optional(v.string()), 1131 - 'api-version': v.union([v.string(), v.null()]) 1117 + 'api-version': v.nullable(v.string()) 1132 1118 }), 1133 1119 query: v.object({ 1134 1120 default: v.optional(v.string()), 1135 - 'parameter-query': v.union([v.string(), v.null()]) 1121 + 'parameter-query': v.nullable(v.string()) 1136 1122 }), 1137 1123 headers: v.object({ 1138 - 'parameter.header': v.union([v.string(), v.null()]) 1124 + 'parameter.header': v.nullable(v.string()) 1139 1125 }) 1140 1126 }); 1141 1127 ··· 1149 1135 1150 1136 export const vPostCallWithOptionalParamData = v.object({ 1151 1137 body: v.optional(v.object({ 1152 - offset: v.optional(v.union([v.number(), v.null()])) 1138 + offset: v.nullish(v.number()) 1153 1139 })), 1154 1140 path: v.optional(v.never()), 1155 1141 query: v.object({ ··· 1179 1165 body: v.optional(v.never()), 1180 1166 path: v.optional(v.never()), 1181 1167 query: v.optional(v.object({ 1182 - parameterString: v.optional(v.union([v.optional(v.string(), 'Hello World!'), v.null()]), 'Hello World!'), 1183 - parameterNumber: v.optional(v.union([v.optional(v.number(), 123), v.null()]), 123), 1184 - parameterBoolean: v.optional(v.union([v.optional(v.boolean(), true), v.null()]), true), 1168 + parameterString: v.nullish(v.string(), 'Hello World!'), 1169 + parameterNumber: v.nullish(v.number(), 123), 1170 + parameterBoolean: v.nullish(v.boolean(), true), 1185 1171 parameterEnum: v.optional(v.picklist([ 1186 1172 'Success', 1187 1173 'Warning', 1188 1174 'Error' 1189 1175 ])), 1190 - parameterModel: v.optional(v.union([vModelWithString, v.null()])) 1176 + parameterModel: v.nullish(vModelWithString) 1191 1177 })) 1192 1178 }); 1193 1179 ··· 1217 1203 parameterStringWithDefault: v.optional(v.string(), 'Hello World!'), 1218 1204 parameterStringWithEmptyDefault: v.optional(v.string(), ''), 1219 1205 parameterStringWithNoDefault: v.string(), 1220 - parameterStringNullableWithNoDefault: v.optional(v.union([v.string(), v.null()])), 1221 - parameterStringNullableWithDefault: v.optional(v.union([v.string(), v.null()]), null) 1206 + parameterStringNullableWithNoDefault: v.nullish(v.string()), 1207 + parameterStringNullableWithDefault: v.nullish(v.string(), null) 1222 1208 }) 1223 1209 }); 1224 1210 ··· 1320 1306 body: v.optional(v.never()), 1321 1307 path: v.optional(v.never()), 1322 1308 query: v.object({ 1323 - parameterArrayCSV: v.union([v.array(v.string()), v.null()]), 1324 - parameterArraySSV: v.union([v.array(v.string()), v.null()]), 1325 - parameterArrayTSV: v.union([v.array(v.string()), v.null()]), 1326 - parameterArrayPipes: v.union([v.array(v.string()), v.null()]), 1327 - parameterArrayMulti: v.union([v.array(v.string()), v.null()]) 1309 + parameterArrayCSV: v.nullable(v.array(v.string())), 1310 + parameterArraySSV: v.nullable(v.array(v.string())), 1311 + parameterArrayTSV: v.nullable(v.array(v.string())), 1312 + parameterArrayPipes: v.nullable(v.array(v.string())), 1313 + parameterArrayMulti: v.nullable(v.array(v.string())) 1328 1314 }) 1329 1315 }); 1330 1316 ··· 1335 1321 })), 1336 1322 query: v.object({ 1337 1323 parameterNumber: v.optional(v.number(), 123), 1338 - parameterString: v.optional(v.union([v.optional(v.string(), 'default'), v.null()]), 'default'), 1339 - parameterBoolean: v.optional(v.union([v.optional(v.boolean(), true), v.null()]), true), 1340 - parameterObject: v.optional(v.union([v.record(v.string(), v.unknown()), v.null()]), null), 1341 - parameterArray: v.union([v.array(v.string()), v.null()]), 1342 - parameterDictionary: v.union([v.record(v.string(), v.unknown()), v.null()]), 1324 + parameterString: v.nullish(v.string(), 'default'), 1325 + parameterBoolean: v.nullish(v.boolean(), true), 1326 + parameterObject: v.nullish(v.record(v.string(), v.unknown()), null), 1327 + parameterArray: v.nullable(v.array(v.string())), 1328 + parameterDictionary: v.nullable(v.record(v.string(), v.unknown())), 1343 1329 parameterEnum: v.picklist([ 1344 1330 'Success', 1345 1331 'Warning', ··· 1358 1344 export const vUploadFileData = v.object({ 1359 1345 body: v.string(), 1360 1346 path: v.object({ 1361 - 'api-version': v.union([v.string(), v.null()]) 1347 + 'api-version': v.nullable(v.string()) 1362 1348 }), 1363 1349 query: v.optional(v.never()) 1364 1350 }); ··· 1419 1405 export const vMultipartRequestData = v.object({ 1420 1406 body: v.optional(v.object({ 1421 1407 content: v.optional(v.string()), 1422 - data: v.optional(v.union([vModelWithString, v.null()])) 1408 + data: v.nullish(vModelWithString) 1423 1409 })), 1424 1410 path: v.optional(v.never()), 1425 1411 query: v.optional(v.never()) ··· 1427 1413 1428 1414 export const vComplexParamsData = v.object({ 1429 1415 body: v.optional(v.object({ 1430 - key: v.pipe(v.union([v.pipe(v.pipe(v.string(), v.maxLength(64), v.regex(/^[a-zA-Z0-9_]*$/)), v.readonly()), v.null()]), v.readonly()), 1431 - name: v.union([v.pipe(v.string(), v.maxLength(255)), v.null()]), 1416 + key: v.nullable(v.pipe(v.pipe(v.string(), v.maxLength(64), v.regex(/^[a-zA-Z0-9_]*$/)), v.readonly())), 1417 + name: v.nullable(v.pipe(v.string(), v.maxLength(255))), 1432 1418 enabled: v.optional(v.boolean(), true), 1433 1419 type: v.picklist([ 1434 1420 'Monkey', 1435 1421 'Horse', 1436 1422 'Bird' 1437 1423 ]), 1438 - listOfModels: v.optional(v.union([v.array(vModelWithString), v.null()])), 1439 - listOfStrings: v.optional(v.union([v.array(v.string()), v.null()])), 1424 + listOfModels: v.nullish(v.array(vModelWithString)), 1425 + listOfStrings: v.nullish(v.array(v.string())), 1440 1426 parameters: v.union([ 1441 1427 vModelWithString, 1442 1428 vModelWithEnum, ··· 1445 1431 ]), 1446 1432 user: v.optional(v.pipe(v.object({ 1447 1433 id: v.optional(v.pipe(v.pipe(v.number(), v.integer(), v.minValue(-2147483648, 'Invalid value: Expected int32 to be >= -2147483648'), v.maxValue(2147483647, 'Invalid value: Expected int32 to be <= 2147483647')), v.readonly())), 1448 - name: v.optional(v.pipe(v.union([v.pipe(v.string(), v.readonly()), v.null()]), v.readonly())) 1434 + name: v.nullish(v.pipe(v.string(), v.readonly())) 1449 1435 }), v.readonly())) 1450 1436 })), 1451 1437 path: v.object({
+6 -6
packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/validators/valibot.gen.ts
··· 4 4 5 5 export const vBaz = v.optional(v.pipe(v.pipe(v.string(), v.regex(/foo\nbar/)), v.readonly()), 'baz'); 6 6 7 - export const vFoo: v.GenericSchema = v.optional(v.union([v.object({ 8 - foo: v.optional(v.pipe(v.string(), v.regex(/^\d{3}-\d{2}-\d{4}$/))), 9 - bar: v.optional(v.lazy(() => vBar)), 10 - baz: v.optional(v.array(v.lazy(() => vFoo))), 11 - qux: v.optional(v.pipe(v.number(), v.integer(), v.gtValue(0)), 0) 12 - }), v.null()]), null); 7 + export const vFoo: v.GenericSchema = v.nullish(v.object({ 8 + foo: v.optional(v.pipe(v.string(), v.regex(/^\d{3}-\d{2}-\d{4}$/))), 9 + bar: v.optional(v.lazy(() => vBar)), 10 + baz: v.optional(v.array(v.lazy(() => vFoo))), 11 + qux: v.optional(v.pipe(v.number(), v.integer(), v.gtValue(0)), 0) 12 + }), null); 13 13 14 14 export const vBar = v.object({ 15 15 foo: v.optional(vFoo)
+78 -93
packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/valibot/default/valibot.gen.ts
··· 81 81 /** 82 82 * This is a simple string 83 83 */ 84 - export const vSimpleStringWithPattern = v.union([v.pipe(v.string(), v.maxLength(64), v.regex(/^[a-zA-Z0-9_]*$/)), v.null()]); 84 + export const vSimpleStringWithPattern = v.nullable(v.pipe(v.string(), v.maxLength(64), v.regex(/^[a-zA-Z0-9_]*$/))); 85 85 86 86 /** 87 87 * This is a simple enum with strings ··· 148 148 export const vArrayWithAnyOfProperties = v.array(v.unknown()); 149 149 150 150 export const vAnyOfAnyAndNull = v.object({ 151 - data: v.optional(v.union([v.unknown(), v.null()])) 151 + data: v.nullish(v.unknown()) 152 152 }); 153 153 154 154 /** ··· 243 243 * This is a model with one string property 244 244 */ 245 245 export const vModelWithNullableString = v.object({ 246 - nullableProp1: v.optional(v.union([v.string(), v.null()])), 247 - nullableRequiredProp1: v.union([v.string(), v.null()]), 248 - nullableProp2: v.optional(v.union([v.string(), v.null()])), 249 - nullableRequiredProp2: v.union([v.string(), v.null()]), 246 + nullableProp1: v.nullish(v.string()), 247 + nullableRequiredProp1: v.nullable(v.string()), 248 + nullableProp2: v.nullish(v.string()), 249 + nullableRequiredProp2: v.nullable(v.string()), 250 250 'foo_bar-enum': v.optional(v.picklist([ 251 251 'Success', 252 252 'Warning', ··· 436 436 * This is a model with one property with a 'any of' relationship where the options are not $ref 437 437 */ 438 438 export const vCompositionWithNestedAnyOfAndNull = v.object({ 439 - propA: v.optional(v.union([v.array(v.unknown()), v.null()])) 439 + propA: v.nullish(v.array(v.unknown())) 440 440 }); 441 441 442 442 /** 443 443 * This is a model with one property with a 'one of' relationship 444 444 */ 445 445 export const vCompositionWithOneOfAndNullable = v.object({ 446 - propA: v.optional(v.union([ 446 + propA: v.nullish(v.union([ 447 447 v.object({ 448 448 boolean: v.optional(v.boolean()) 449 449 }), 450 450 vModelWithEnum, 451 451 vModelWithArray, 452 - vModelWithDictionary, 453 - v.null() 452 + vModelWithDictionary 454 453 ])) 455 454 }); 456 455 ··· 479 478 * This is a model with one property with a 'all of' relationship 480 479 */ 481 480 export const vCompositionWithAllOfAndNullable = v.object({ 482 - propA: v.optional(v.union([v.intersect([ 483 - v.object({ 484 - boolean: v.optional(v.boolean()) 485 - }), 486 - vModelWithEnum, 487 - vModelWithArray, 488 - vModelWithDictionary 489 - ]), v.null()])) 481 + propA: v.nullish(v.intersect([ 482 + v.object({ 483 + boolean: v.optional(v.boolean()) 484 + }), 485 + vModelWithEnum, 486 + vModelWithArray, 487 + vModelWithDictionary 488 + ])) 490 489 }); 491 490 492 491 /** 493 492 * This is a model with one property with a 'any of' relationship 494 493 */ 495 494 export const vCompositionWithAnyOfAndNullable = v.object({ 496 - propA: v.optional(v.union([ 495 + propA: v.nullish(v.union([ 497 496 v.object({ 498 497 boolean: v.optional(v.boolean()) 499 498 }), 500 499 vModelWithEnum, 501 500 vModelWithArray, 502 - vModelWithDictionary, 503 - v.null() 501 + vModelWithDictionary 504 502 ])) 505 503 }); 506 504 ··· 527 525 export const vModelWithProperties = v.object({ 528 526 required: v.string(), 529 527 requiredAndReadOnly: v.pipe(v.string(), v.readonly()), 530 - requiredAndNullable: v.union([v.string(), v.null()]), 528 + requiredAndNullable: v.nullable(v.string()), 531 529 string: v.optional(v.string()), 532 530 number: v.optional(v.number()), 533 531 boolean: v.optional(v.boolean()), ··· 550 548 * This is a model with one nested property 551 549 */ 552 550 export const vModelWithNestedProperties = v.object({ 553 - first: v.pipe(v.union([v.pipe(v.object({ 554 - second: v.pipe(v.union([v.pipe(v.object({ 555 - third: v.pipe(v.union([v.pipe(v.string(), v.readonly()), v.null()]), v.readonly()) 556 - }), v.readonly()), v.null()]), v.readonly()) 557 - }), v.readonly()), v.null()]), v.readonly()) 551 + first: v.nullable(v.pipe(v.object({ 552 + second: v.nullable(v.pipe(v.object({ 553 + third: v.nullable(v.pipe(v.string(), v.readonly())) 554 + }), v.readonly())) 555 + }), v.readonly())) 558 556 }); 559 557 560 558 /** ··· 665 663 }, v.unknown()); 666 664 667 665 export const vNestedAnyOfArraysNullable = v.object({ 668 - nullableArray: v.optional(v.union([v.array(v.unknown()), v.null()])) 666 + nullableArray: v.nullish(v.array(v.unknown())) 669 667 }); 670 668 671 669 /** 672 670 * An object that can be null 673 671 */ 674 - export const vNullableObject = v.optional(v.union([v.object({ 675 - foo: v.optional(v.string()) 676 - }), v.null()]), null); 672 + export const vNullableObject = v.nullish(v.object({ 673 + foo: v.optional(v.string()) 674 + }), null); 677 675 678 676 /** 679 677 * Some % character ··· 751 749 ]); 752 750 753 751 export const vModelWithAnyOfConstantSizeArrayNullable = v.tuple([ 754 - v.union([ 755 - v.number(), 756 - v.null(), 757 - v.string() 758 - ]), 759 - v.union([ 760 - v.number(), 761 - v.null(), 762 - v.string() 763 - ]), 764 - v.union([ 765 - v.number(), 766 - v.null(), 767 - v.string() 768 - ]) 752 + v.nullable(v.union([v.number(), v.string()])), 753 + v.nullable(v.union([v.number(), v.string()])), 754 + v.nullable(v.union([v.number(), v.string()])) 769 755 ]); 770 756 771 757 export const vModelWithAnyOfConstantSizeArrayAndIntersect = v.tuple([v.intersect([v.number(), v.string()]), v.intersect([v.number(), v.string()])]); ··· 864 850 865 851 export const vGenericSchemaDuplicateIssue1SystemBoolean = v.strictObject({ 866 852 item: v.optional(v.boolean()), 867 - error: v.optional(v.union([v.string(), v.null()])), 853 + error: v.nullish(v.string()), 868 854 hasError: v.optional(v.pipe(v.boolean(), v.readonly())), 869 855 data: v.optional(v.strictObject({})) 870 856 }); 871 857 872 858 export const vGenericSchemaDuplicateIssue1SystemString = v.strictObject({ 873 - item: v.optional(v.union([v.string(), v.null()])), 874 - error: v.optional(v.union([v.string(), v.null()])), 859 + item: v.nullish(v.string()), 860 + error: v.nullish(v.string()), 875 861 hasError: v.optional(v.pipe(v.boolean(), v.readonly())) 876 862 }); 877 863 ··· 897 883 */ 898 884 export const vModelWithPropertiesWritable = v.object({ 899 885 required: v.string(), 900 - requiredAndNullable: v.union([v.string(), v.null()]), 886 + requiredAndNullable: v.nullable(v.string()), 901 887 string: v.optional(v.string()), 902 888 number: v.optional(v.number()), 903 889 boolean: v.optional(v.boolean()), ··· 953 939 954 940 export const vGenericSchemaDuplicateIssue1SystemBooleanWritable = v.strictObject({ 955 941 item: v.optional(v.boolean()), 956 - error: v.optional(v.union([v.string(), v.null()])), 942 + error: v.nullish(v.string()), 957 943 data: v.optional(v.strictObject({})) 958 944 }); 959 945 960 946 export const vGenericSchemaDuplicateIssue1SystemStringWritable = v.strictObject({ 961 - item: v.optional(v.union([v.string(), v.null()])), 962 - error: v.optional(v.union([v.string(), v.null()])) 947 + item: v.nullish(v.string()), 948 + error: v.nullish(v.string()) 963 949 }); 964 950 965 951 /** ··· 972 958 }), v.strictObject({ 973 959 bar: vNonAsciiStringæøåÆøÅöôêÊ字符串 974 960 })]), v.object({ 975 - baz: v.union([v.pipe(v.number(), v.integer(), v.minValue(0), v.maxValue(65535, 'Invalid value: Expected uint16 to be <= 65535')), v.null()]), 961 + baz: v.nullable(v.pipe(v.number(), v.integer(), v.minValue(0), v.maxValue(65535, 'Invalid value: Expected uint16 to be <= 65535'))), 976 962 qux: v.pipe(v.number(), v.integer(), v.minValue(0), v.maxValue(255, 'Invalid value: Expected uint8 to be <= 255')) 977 963 })]); 978 964 979 965 export const vModelWithOneOfAndProperties = v.intersect([v.union([vSimpleParameter, vNonAsciiStringæøåÆøÅöôêÊ字符串]), v.object({ 980 - baz: v.union([v.pipe(v.number(), v.integer(), v.minValue(0), v.maxValue(65535, 'Invalid value: Expected uint16 to be <= 65535')), v.null()]), 966 + baz: v.nullable(v.pipe(v.number(), v.integer(), v.minValue(0), v.maxValue(65535, 'Invalid value: Expected uint16 to be <= 65535'))), 981 967 qux: v.pipe(v.number(), v.integer(), v.minValue(0), v.maxValue(255, 'Invalid value: Expected uint8 to be <= 255')) 982 968 })]); 983 969 ··· 1118 1104 path: v.optional(v.never()), 1119 1105 query: v.optional(v.never()), 1120 1106 headers: v.object({ 1121 - parameter: v.union([vDeprecatedModel, v.null()]) 1107 + parameter: v.nullable(vDeprecatedModel) 1122 1108 }) 1123 1109 }); 1124 1110 1125 1111 export const vCallWithParametersData = v.object({ 1126 - body: v.union([v.record(v.string(), v.unknown()), v.null()]), 1112 + body: v.nullable(v.record(v.string(), v.unknown())), 1127 1113 path: v.object({ 1128 - parameterPath: v.union([v.string(), v.null()]), 1129 - 'api-version': v.union([v.string(), v.null()]) 1114 + parameterPath: v.nullable(v.string()), 1115 + 'api-version': v.nullable(v.string()) 1130 1116 }), 1131 1117 query: v.object({ 1132 1118 foo_ref_enum: v.optional(vModelWithNestedArrayEnumsDataFoo), 1133 1119 foo_all_of_enum: vModelWithNestedArrayEnumsDataFoo, 1134 - cursor: v.union([v.string(), v.null()]) 1120 + cursor: v.nullable(v.string()) 1135 1121 }), 1136 1122 headers: v.object({ 1137 - parameterHeader: v.union([v.string(), v.null()]) 1123 + parameterHeader: v.nullable(v.string()) 1138 1124 }) 1139 1125 }); 1140 1126 1141 1127 export const vCallWithWeirdParameterNamesData = v.object({ 1142 - body: v.union([vModelWithString, v.null()]), 1128 + body: v.nullable(vModelWithString), 1143 1129 path: v.object({ 1144 1130 'parameter.path.1': v.optional(v.string()), 1145 1131 'parameter-path-2': v.optional(v.string()), 1146 1132 'PARAMETER-PATH-3': v.optional(v.string()), 1147 - 'api-version': v.union([v.string(), v.null()]) 1133 + 'api-version': v.nullable(v.string()) 1148 1134 }), 1149 1135 query: v.object({ 1150 1136 default: v.optional(v.string()), 1151 - 'parameter-query': v.union([v.string(), v.null()]) 1137 + 'parameter-query': v.nullable(v.string()) 1152 1138 }), 1153 1139 headers: v.object({ 1154 - 'parameter.header': v.union([v.string(), v.null()]) 1140 + 'parameter.header': v.nullable(v.string()) 1155 1141 }) 1156 1142 }); 1157 1143 ··· 1165 1151 1166 1152 export const vPostCallWithOptionalParamData = v.object({ 1167 1153 body: v.optional(v.object({ 1168 - offset: v.optional(v.union([v.number(), v.null()])) 1154 + offset: v.nullish(v.number()) 1169 1155 })), 1170 1156 path: v.optional(v.never()), 1171 1157 query: v.object({ ··· 1195 1181 body: v.optional(v.never()), 1196 1182 path: v.optional(v.never()), 1197 1183 query: v.optional(v.object({ 1198 - parameterString: v.optional(v.union([v.optional(v.string(), 'Hello World!'), v.null()]), 'Hello World!'), 1199 - parameterNumber: v.optional(v.union([v.optional(v.number(), 123), v.null()]), 123), 1200 - parameterBoolean: v.optional(v.union([v.optional(v.boolean(), true), v.null()]), true), 1184 + parameterString: v.nullish(v.string(), 'Hello World!'), 1185 + parameterNumber: v.nullish(v.number(), 123), 1186 + parameterBoolean: v.nullish(v.boolean(), true), 1201 1187 parameterEnum: v.optional(v.picklist([ 1202 1188 'Success', 1203 1189 'Warning', 1204 1190 'Error' 1205 1191 ])), 1206 - parameterModel: v.optional(v.union([v.optional(vModelWithString, { prop: 'Hello World!' }), v.null()])) 1192 + parameterModel: v.nullish(vModelWithString) 1207 1193 })) 1208 1194 }); 1209 1195 ··· 1233 1219 parameterStringWithDefault: v.optional(v.string(), 'Hello World!'), 1234 1220 parameterStringWithEmptyDefault: v.optional(v.string(), ''), 1235 1221 parameterStringWithNoDefault: v.string(), 1236 - parameterStringNullableWithNoDefault: v.optional(v.union([v.string(), v.null()])), 1237 - parameterStringNullableWithDefault: v.optional(v.union([v.string(), v.null()]), null) 1222 + parameterStringNullableWithNoDefault: v.nullish(v.string()), 1223 + parameterStringNullableWithDefault: v.nullish(v.string(), null) 1238 1224 }) 1239 1225 }); 1240 1226 ··· 1336 1322 body: v.optional(v.never()), 1337 1323 path: v.optional(v.never()), 1338 1324 query: v.object({ 1339 - parameterArrayCSV: v.union([v.array(v.string()), v.null()]), 1340 - parameterArraySSV: v.union([v.array(v.string()), v.null()]), 1341 - parameterArrayTSV: v.union([v.array(v.string()), v.null()]), 1342 - parameterArrayPipes: v.union([v.array(v.string()), v.null()]), 1343 - parameterArrayMulti: v.union([v.array(v.string()), v.null()]) 1325 + parameterArrayCSV: v.nullable(v.array(v.string())), 1326 + parameterArraySSV: v.nullable(v.array(v.string())), 1327 + parameterArrayTSV: v.nullable(v.array(v.string())), 1328 + parameterArrayPipes: v.nullable(v.array(v.string())), 1329 + parameterArrayMulti: v.nullable(v.array(v.string())) 1344 1330 }) 1345 1331 }); 1346 1332 ··· 1351 1337 })), 1352 1338 query: v.object({ 1353 1339 parameterNumber: v.optional(v.number(), 123), 1354 - parameterString: v.optional(v.union([v.optional(v.string(), 'default'), v.null()]), 'default'), 1355 - parameterBoolean: v.optional(v.union([v.optional(v.boolean(), true), v.null()]), true), 1356 - parameterObject: v.optional(v.union([v.record(v.string(), v.unknown()), v.null()]), null), 1357 - parameterArray: v.union([v.array(v.string()), v.null()]), 1358 - parameterDictionary: v.union([v.record(v.string(), v.unknown()), v.null()]), 1359 - parameterEnum: v.union([ 1340 + parameterString: v.nullish(v.string(), 'default'), 1341 + parameterBoolean: v.nullish(v.boolean(), true), 1342 + parameterObject: v.nullish(v.record(v.string(), v.unknown()), null), 1343 + parameterArray: v.nullable(v.array(v.string())), 1344 + parameterDictionary: v.nullable(v.record(v.string(), v.unknown())), 1345 + parameterEnum: v.nullable(v.union([ 1360 1346 v.literal('Success'), 1361 1347 v.literal('Warning'), 1362 - v.literal('Error'), 1363 - v.null() 1364 - ]) 1348 + v.literal('Error') 1349 + ])) 1365 1350 }) 1366 1351 }); 1367 1352 ··· 1375 1360 export const vUploadFileData = v.object({ 1376 1361 body: v.string(), 1377 1362 path: v.object({ 1378 - 'api-version': v.union([v.string(), v.null()]) 1363 + 'api-version': v.nullable(v.string()) 1379 1364 }), 1380 1365 query: v.optional(v.never()) 1381 1366 }); ··· 1436 1421 export const vMultipartRequestData = v.object({ 1437 1422 body: v.optional(v.object({ 1438 1423 content: v.optional(v.string()), 1439 - data: v.optional(v.union([vModelWithString, v.null()])) 1424 + data: v.nullish(vModelWithString) 1440 1425 })), 1441 1426 path: v.optional(v.never()), 1442 1427 query: v.optional(v.never()) ··· 1444 1429 1445 1430 export const vComplexParamsData = v.object({ 1446 1431 body: v.optional(v.object({ 1447 - key: v.pipe(v.union([v.pipe(v.pipe(v.string(), v.maxLength(64), v.regex(/^[a-zA-Z0-9_]*$/)), v.readonly()), v.null()]), v.readonly()), 1448 - name: v.union([v.pipe(v.string(), v.maxLength(255)), v.null()]), 1432 + key: v.nullable(v.pipe(v.pipe(v.string(), v.maxLength(64), v.regex(/^[a-zA-Z0-9_]*$/)), v.readonly())), 1433 + name: v.nullable(v.pipe(v.string(), v.maxLength(255))), 1449 1434 enabled: v.optional(v.boolean(), true), 1450 1435 type: v.picklist([ 1451 1436 'Monkey', 1452 1437 'Horse', 1453 1438 'Bird' 1454 1439 ]), 1455 - listOfModels: v.optional(v.union([v.array(vModelWithString), v.null()])), 1456 - listOfStrings: v.optional(v.union([v.array(v.string()), v.null()])), 1440 + listOfModels: v.nullish(v.array(vModelWithString)), 1441 + listOfStrings: v.nullish(v.array(v.string())), 1457 1442 parameters: v.union([ 1458 1443 vModelWithString, 1459 1444 vModelWithEnum, ··· 1462 1447 ]), 1463 1448 user: v.optional(v.pipe(v.object({ 1464 1449 id: v.optional(v.pipe(v.pipe(v.number(), v.integer(), v.minValue(-2147483648, 'Invalid value: Expected int32 to be >= -2147483648'), v.maxValue(2147483647, 'Invalid value: Expected int32 to be <= 2147483647')), v.readonly())), 1465 - name: v.optional(v.pipe(v.union([v.pipe(v.string(), v.readonly()), v.null()]), v.readonly())) 1450 + name: v.nullish(v.pipe(v.string(), v.readonly())) 1466 1451 }), v.readonly())) 1467 1452 })), 1468 1453 path: v.object({
+1 -1
packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/validators-circular-ref-2/valibot.gen.ts
··· 3 3 import * as v from 'valibot'; 4 4 5 5 export const vBar: v.GenericSchema = v.strictObject({ 6 - bar: v.union([v.array(v.lazy(() => vBar)), v.null()]) 6 + bar: v.nullable(v.array(v.lazy(() => vBar))) 7 7 }); 8 8 9 9 export const vFoo = v.strictObject({
+6 -6
packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/validators-metadata/valibot.gen.ts
··· 11 11 /** 12 12 * This is Foo schema. 13 13 */ 14 - export const vFoo: v.GenericSchema = v.optional(v.union([v.object({ 15 - foo: v.optional(v.pipe(v.pipe(v.string(), v.regex(/^\d{3}-\d{2}-\d{4}$/)), v.metadata({ description: 'This is foo property.' }))), 16 - bar: v.optional(v.lazy(() => vBar)), 17 - baz: v.optional(v.pipe(v.array(v.lazy(() => vFoo)), v.metadata({ description: 'This is baz property.' }))), 18 - qux: v.optional(v.pipe(v.pipe(v.number(), v.integer(), v.gtValue(0)), v.metadata({ description: 'This is qux property.' })), 0) 19 - }), v.null()]), null); 14 + export const vFoo: v.GenericSchema = v.nullish(v.object({ 15 + foo: v.optional(v.pipe(v.pipe(v.string(), v.regex(/^\d{3}-\d{2}-\d{4}$/)), v.metadata({ description: 'This is foo property.' }))), 16 + bar: v.optional(v.lazy(() => vBar)), 17 + baz: v.optional(v.pipe(v.array(v.lazy(() => vFoo)), v.metadata({ description: 'This is baz property.' }))), 18 + qux: v.optional(v.pipe(v.pipe(v.number(), v.integer(), v.gtValue(0)), v.metadata({ description: 'This is qux property.' })), 0) 19 + }), null); 20 20 21 21 /** 22 22 * This is Bar schema.
+6 -6
packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/validators-types/valibot.gen.ts
··· 11 11 /** 12 12 * This is Foo schema. 13 13 */ 14 - export const vFoo: v.GenericSchema = v.optional(v.union([v.object({ 15 - foo: v.optional(v.pipe(v.string(), v.regex(/^\d{3}-\d{2}-\d{4}$/))), 16 - bar: v.optional(v.lazy(() => vBar)), 17 - baz: v.optional(v.array(v.lazy(() => vFoo))), 18 - qux: v.optional(v.pipe(v.number(), v.integer(), v.gtValue(0)), 0) 19 - }), v.null()]), null); 14 + export const vFoo: v.GenericSchema = v.nullish(v.object({ 15 + foo: v.optional(v.pipe(v.string(), v.regex(/^\d{3}-\d{2}-\d{4}$/))), 16 + bar: v.optional(v.lazy(() => vBar)), 17 + baz: v.optional(v.array(v.lazy(() => vFoo))), 18 + qux: v.optional(v.pipe(v.number(), v.integer(), v.gtValue(0)), 0) 19 + }), null); 20 20 21 21 /** 22 22 * This is Bar schema.
+6 -6
packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/validators/valibot.gen.ts
··· 11 11 /** 12 12 * This is Foo schema. 13 13 */ 14 - export const vFoo: v.GenericSchema = v.optional(v.union([v.object({ 15 - foo: v.optional(v.pipe(v.string(), v.regex(/^\d{3}-\d{2}-\d{4}$/))), 16 - bar: v.optional(v.lazy(() => vBar)), 17 - baz: v.optional(v.array(v.lazy(() => vFoo))), 18 - qux: v.optional(v.pipe(v.number(), v.integer(), v.gtValue(0)), 0) 19 - }), v.null()]), null); 14 + export const vFoo: v.GenericSchema = v.nullish(v.object({ 15 + foo: v.optional(v.pipe(v.string(), v.regex(/^\d{3}-\d{2}-\d{4}$/))), 16 + bar: v.optional(v.lazy(() => vBar)), 17 + baz: v.optional(v.array(v.lazy(() => vFoo))), 18 + qux: v.optional(v.pipe(v.number(), v.integer(), v.gtValue(0)), 0) 19 + }), null); 20 20 21 21 /** 22 22 * This is Bar schema.
+13 -6
packages/openapi-ts/src/plugins/valibot/resolvers/types.ts
··· 1 1 import type { Refs, Symbol } from '@hey-api/codegen-core'; 2 - import type { IR, Plugin, SchemaWithType } from '@hey-api/shared'; 2 + import type { 3 + IR, 4 + Plugin, 5 + SchemaResult, 6 + SchemaVisitorContext, 7 + SchemaWithType, 8 + Walker, 9 + } from '@hey-api/shared'; 3 10 4 11 import type { MaybeBigInt, ShouldCoerceToBigInt } from '../../../plugins/shared/utils/coerce'; 5 12 import type { GetIntegerLimit } from '../../../plugins/shared/utils/formats'; 6 13 import type { $, DollarTsDsl } from '../../../ts-dsl'; 7 14 import type { Pipe, PipeResult, Pipes, PipesUtils } from '../shared/pipes'; 8 15 import type { Ast, IrSchemaToAstOptions, PluginState } from '../shared/types'; 16 + import type { ValibotPlugin } from '../types'; 9 17 10 18 export type Resolvers = Plugin.Resolvers<{ 11 19 /** ··· 70 78 type ValidatorResolver = (ctx: ValidatorResolverContext) => PipeResult | null | undefined; 71 79 72 80 type BaseContext = DollarTsDsl & 73 - Pick<IrSchemaToAstOptions, 'plugin' | 'schemaExtractor'> & { 81 + Pick<IrSchemaToAstOptions, 'plugin'> & { 74 82 /** 75 83 * Functions for working with pipes. 76 84 */ ··· 116 124 */ 117 125 isNullable: boolean; 118 126 }; 119 - /** 120 - * Returns a nullable wrapper if the enum includes null, undefined otherwise. 121 - */ 122 - nullable: (ctx: EnumResolverContext) => PipeResult | undefined; 123 127 }; 124 128 schema: SchemaWithType<'enum'>; 125 129 /** ··· 152 156 } 153 157 154 158 export interface ObjectResolverContext extends BaseContext { 159 + applyModifiers: (result: SchemaResult<Ast>, opts: { optional?: boolean }) => Ast; 155 160 /** 156 161 * Nodes used to build different parts of the object schema. 157 162 */ ··· 172 177 ast: Partial<Omit<Ast, 'typeName'>>; 173 178 state: Refs<PluginState>; 174 179 }; 180 + walk: Walker<Ast, ValibotPlugin['Instance']>; 181 + walkerCtx: SchemaVisitorContext<ValibotPlugin['Instance']>; 175 182 } 176 183 177 184 export interface StringResolverContext extends BaseContext {
+2 -4
packages/openapi-ts/src/plugins/valibot/shared/types.ts
··· 1 1 import type { Refs, SymbolMeta } from '@hey-api/codegen-core'; 2 - import type { IR, SchemaExtractor } from '@hey-api/shared'; 2 + import type { IR, Walker } from '@hey-api/shared'; 3 3 import type ts from 'typescript'; 4 4 5 5 import type { ValibotPlugin } from '../types'; 6 6 import type { Pipes } from './pipes'; 7 - import type { ProcessorContext } from './processor'; 8 7 9 8 export type Ast = { 10 9 hasLazyExpression?: boolean; ··· 15 14 export type IrSchemaToAstOptions = { 16 15 /** The plugin instance. */ 17 16 plugin: ValibotPlugin['Instance']; 18 - /** Optional schema extractor function. */ 19 - schemaExtractor?: SchemaExtractor<ProcessorContext>; 20 17 /** The plugin state references. */ 21 18 state: Refs<PluginState>; 19 + walk: Walker<Ast, ValibotPlugin['Instance']>; 22 20 }; 23 21 24 22 export type PluginState = Pick<Required<SymbolMeta>, 'path'> &
+1 -150
packages/openapi-ts/src/plugins/valibot/v1/plugin.ts
··· 1 - import type { SymbolMeta } from '@hey-api/codegen-core'; 2 - import { fromRef, ref } from '@hey-api/codegen-core'; 3 - import type { IR, SchemaWithType } from '@hey-api/shared'; 4 - import { deduplicateSchema, pathToJsonPointer } from '@hey-api/shared'; 1 + import { pathToJsonPointer } from '@hey-api/shared'; 5 2 6 - import { maybeBigInt } from '../../../plugins/shared/utils/coerce'; 7 - import { $ } from '../../../ts-dsl'; 8 3 import { irOperationToAst } from '../shared/operation'; 9 - import { pipesToNode } from '../shared/pipes'; 10 - import type { Ast, IrSchemaToAstOptions } from '../shared/types'; 11 4 import { irWebhookToAst } from '../shared/webhook'; 12 5 import type { ValibotPlugin } from '../types'; 13 - import { identifiers } from './constants'; 14 6 import { createProcessor } from './processor'; 15 - import { irSchemaWithTypeToAst } from './toAst'; 16 - 17 - export function irSchemaToAst({ 18 - optional, 19 - plugin, 20 - schema, 21 - schemaExtractor, 22 - state, 23 - }: IrSchemaToAstOptions & { 24 - /** 25 - * Accept `optional` to handle optional object properties. We can't handle 26 - * this inside the object function because `.optional()` must come before 27 - * `.default()` which is handled in this function. 28 - */ 29 - optional?: boolean; 30 - schema: IR.SchemaObject; 31 - }): Ast { 32 - if (schemaExtractor && !schema.$ref) { 33 - const extracted = schemaExtractor({ 34 - meta: { 35 - resource: 'definition', 36 - resourceId: pathToJsonPointer(fromRef(state.path)), 37 - }, 38 - naming: plugin.config.definitions, 39 - path: fromRef(state.path), 40 - plugin, 41 - schema, 42 - }); 43 - if (extracted !== schema) schema = extracted; 44 - } 45 - 46 - const ast: Ast = { 47 - pipes: [], 48 - }; 49 - 50 - const v = plugin.external('valibot.v'); 51 - 52 - if (schema.$ref) { 53 - const query: SymbolMeta = { 54 - category: 'schema', 55 - resource: 'definition', 56 - resourceId: schema.$ref, 57 - tool: 'valibot', 58 - }; 59 - const refSymbol = plugin.referenceSymbol(query); 60 - if (plugin.isSymbolRegistered(query)) { 61 - const ref = $(refSymbol); 62 - ast.pipes.push(ref); 63 - } else { 64 - const lazyExpression = $(v) 65 - .attr(identifiers.schemas.lazy) 66 - .call($.func().do($(refSymbol).return())); 67 - ast.pipes.push(lazyExpression); 68 - state.hasLazyExpression['~ref'] = true; 69 - } 70 - } else if (schema.type) { 71 - const typeAst = irSchemaWithTypeToAst({ 72 - plugin, 73 - schema: schema as SchemaWithType, 74 - schemaExtractor, 75 - state, 76 - }); 77 - ast.typeName = typeAst.anyType; 78 - ast.pipes.push(typeAst.expression); 79 - 80 - if (plugin.config.metadata && schema.description) { 81 - const expression = $(v) 82 - .attr(identifiers.actions.metadata) 83 - .call($.object().prop('description', $.literal(schema.description))); 84 - ast.pipes.push(expression); 85 - } 86 - } else if (schema.items) { 87 - schema = deduplicateSchema({ schema }); 88 - 89 - if (schema.items) { 90 - const itemsAst = schema.items.map((item, index) => { 91 - const itemAst = irSchemaToAst({ 92 - plugin, 93 - schema: item, 94 - schemaExtractor, 95 - state: { 96 - ...state, 97 - path: ref([...fromRef(state.path), 'items', index]), 98 - }, 99 - }); 100 - return pipesToNode(itemAst.pipes, plugin); 101 - }); 102 - 103 - if (schema.logicalOperator === 'and') { 104 - const intersectExpression = $(v) 105 - .attr(identifiers.schemas.intersect) 106 - .call($.array(...itemsAst)); 107 - ast.pipes.push(intersectExpression); 108 - } else { 109 - const unionExpression = $(v) 110 - .attr(identifiers.schemas.union) 111 - .call($.array(...itemsAst)); 112 - ast.pipes.push(unionExpression); 113 - } 114 - } else { 115 - const schemaPipes = irSchemaToAst({ plugin, schema, state }); 116 - ast.pipes.push(...schemaPipes.pipes); 117 - } 118 - } else { 119 - // catch-all fallback for failed schemas 120 - const typeAst = irSchemaWithTypeToAst({ 121 - plugin, 122 - schema: { 123 - type: 'unknown', 124 - }, 125 - schemaExtractor, 126 - state, 127 - }); 128 - ast.typeName = typeAst.anyType; 129 - ast.pipes.push(typeAst.expression); 130 - } 131 - 132 - if (ast.pipes.length) { 133 - if (schema.accessScope === 'read') { 134 - const readonlyExpression = $(v).attr(identifiers.actions.readonly).call(); 135 - ast.pipes.push(readonlyExpression); 136 - } 137 - 138 - if (schema.default !== undefined) { 139 - ast.pipes = [ 140 - $(v) 141 - .attr(identifiers.schemas.optional) 142 - .call( 143 - pipesToNode(ast.pipes, plugin), 144 - schema.type === 'integer' || schema.type === 'number' 145 - ? maybeBigInt(schema.default, schema.format) 146 - : $.fromValue(schema.default), 147 - ), 148 - ]; 149 - } else if (optional) { 150 - ast.pipes = [$(v).attr(identifiers.schemas.optional).call(pipesToNode(ast.pipes, plugin))]; 151 - } 152 - } 153 - 154 - return ast as Ast; 155 - } 156 7 157 8 export const handlerV1: ValibotPlugin['Handler'] = ({ plugin }) => { 158 9 plugin.symbol('v', {
+18 -6
packages/openapi-ts/src/plugins/valibot/v1/processor.ts
··· 1 - import { refs } from '@hey-api/codegen-core'; 1 + import { ref, refs } from '@hey-api/codegen-core'; 2 2 import type { IR } from '@hey-api/shared'; 3 - import { createSchemaProcessor, pathToJsonPointer } from '@hey-api/shared'; 3 + import { createSchemaProcessor, createSchemaWalker, pathToJsonPointer } from '@hey-api/shared'; 4 4 5 5 import { exportAst } from '../shared/export'; 6 6 import type { ProcessorContext, ProcessorResult } from '../shared/processor'; 7 7 import type { PluginState } from '../shared/types'; 8 8 import type { ValibotPlugin } from '../types'; 9 - import { irSchemaToAst } from './plugin'; 9 + import { createVisitor } from './walker'; 10 10 11 11 export function createProcessor(plugin: ValibotPlugin['Instance']): ProcessorResult { 12 12 const processor = createSchemaProcessor(); ··· 43 43 tags: ctx.tags, 44 44 }); 45 45 46 - const ast = irSchemaToAst({ 47 - plugin, 48 - schema: ctx.schema, 46 + const visitor = createVisitor({ 49 47 schemaExtractor: extractor, 50 48 state, 51 49 }); 50 + const walk = createSchemaWalker(visitor); 51 + 52 + const result = walk(ctx.schema, { 53 + path: ref(ctx.path), 54 + plugin, 55 + }); 56 + const ast = 57 + visitor.applyModifiers(result, { 58 + path: ref(ctx.path), 59 + plugin, 60 + }) ?? result.expression; 61 + if (result.hasLazyExpression) { 62 + state.hasLazyExpression['~ref'] = true; 63 + } 52 64 53 65 exportAst({ ...ctx, ast, plugin, state }); 54 66 });
+19 -15
packages/openapi-ts/src/plugins/valibot/v1/toAst/array.ts
··· 1 - import { fromRef, ref } from '@hey-api/codegen-core'; 2 - import type { SchemaWithType } from '@hey-api/shared'; 3 - import { deduplicateSchema } from '@hey-api/shared'; 1 + import type { SchemaResult, SchemaWithType } from '@hey-api/shared'; 2 + import { childContext, deduplicateSchema } from '@hey-api/shared'; 4 3 5 4 import { $ } from '../../../../ts-dsl'; 6 5 import { pipesToNode } from '../../shared/pipes'; 7 6 import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; 8 7 import { identifiers } from '../constants'; 9 - import { irSchemaToAst } from '../plugin'; 10 8 import { unknownToAst } from './unknown'; 11 9 12 10 export function arrayToAst( 13 11 options: IrSchemaToAstOptions & { 12 + applyModifiers: (result: SchemaResult<Ast>, opts: { optional?: boolean }) => Ast; 14 13 schema: SchemaWithType<'array'>; 15 14 }, 16 15 ): Omit<Ast, 'typeName'> { 17 - const { plugin } = options; 16 + const { applyModifiers, plugin, walk } = options; 18 17 let { schema } = options; 19 18 20 19 const result: Omit<Ast, 'typeName'> = { ··· 39 38 40 39 // at least one item is guaranteed 41 40 const itemExpressions = schema.items!.map((item, index) => { 42 - const itemAst = irSchemaToAst({ 43 - ...options, 44 - schema: item, 45 - state: { 46 - ...options.state, 47 - path: ref([...fromRef(options.state.path), 'items', index]), 48 - }, 49 - }); 50 - if (itemAst.hasLazyExpression) { 41 + const itemResult = walk( 42 + item, 43 + childContext( 44 + { 45 + path: options.state.path, 46 + plugin: options.plugin, 47 + }, 48 + 'items', 49 + index, 50 + ), 51 + ); 52 + if (itemResult.hasLazyExpression) { 51 53 result.hasLazyExpression = true; 52 54 } 53 - return pipesToNode(itemAst.pipes, plugin); 55 + 56 + const finalExpr = applyModifiers(itemResult, { optional: false }); 57 + return pipesToNode(finalExpr.pipes, plugin); 54 58 }); 55 59 56 60 if (itemExpressions.length === 1) {
+1 -1
packages/openapi-ts/src/plugins/valibot/v1/toAst/boolean.ts
··· 8 8 export function booleanToAst({ 9 9 plugin, 10 10 schema, 11 - }: IrSchemaToAstOptions & { 11 + }: Pick<IrSchemaToAstOptions, 'plugin'> & { 12 12 schema: SchemaWithType<'boolean'>; 13 13 }): ReturnType<typeof $.call | typeof $.expr> { 14 14 const pipes: Array<ReturnType<typeof $.call>> = [];
+2 -18
packages/openapi-ts/src/plugins/valibot/v1/toAst/enum.ts
··· 38 38 .call($.array(...enumMembers)); 39 39 } 40 40 41 - function nullableNode(ctx: EnumResolverContext): PipeResult | undefined { 42 - const { symbols } = ctx; 43 - const { v } = symbols; 44 - const { isNullable } = ctx.nodes.items(ctx); 45 - if (!isNullable) return; 46 - const currentNode = ctx.pipes.toNode(ctx.pipes.current, ctx.plugin); 47 - return $(v).attr(identifiers.schemas.nullable).call(currentNode); 48 - } 49 - 50 41 function enumResolver(ctx: EnumResolverContext): PipeResult { 51 42 const { enumMembers } = ctx.nodes.items(ctx); 52 43 ··· 57 48 const baseExpression = ctx.nodes.base(ctx); 58 49 ctx.pipes.push(ctx.pipes.current, baseExpression); 59 50 60 - const nullableExpression = ctx.nodes.nullable(ctx); 61 - if (nullableExpression) { 62 - return nullableExpression; 63 - } 64 - 65 51 return ctx.pipes.current; 66 52 } 67 53 ··· 69 55 plugin, 70 56 schema, 71 57 state, 72 - }: IrSchemaToAstOptions & { 58 + }: Pick<IrSchemaToAstOptions, 'plugin' | 'state'> & { 73 59 schema: SchemaWithType<'enum'>; 74 60 }): Pipe { 75 61 const v = plugin.external('valibot.v'); 76 62 77 63 const { enumMembers } = itemsNode({ 78 64 $, 79 - nodes: { base: baseNode, items: itemsNode, nullable: nullableNode }, 65 + nodes: { base: baseNode, items: itemsNode }, 80 66 pipes: { ...pipes, current: [] }, 81 67 plugin, 82 68 schema, ··· 90 76 schema: { 91 77 type: 'unknown', 92 78 }, 93 - state, 94 79 }); 95 80 } 96 81 ··· 99 84 nodes: { 100 85 base: baseNode, 101 86 items: itemsNode, 102 - nullable: nullableNode, 103 87 }, 104 88 pipes: { 105 89 ...pipes,
-130
packages/openapi-ts/src/plugins/valibot/v1/toAst/index.ts
··· 1 - import type { SchemaWithType } from '@hey-api/shared'; 2 - 3 - import { shouldCoerceToBigInt } from '../../../../plugins/shared/utils/coerce'; 4 - import type { $ } from '../../../../ts-dsl'; 5 - import { pipesToNode } from '../../shared/pipes'; 6 - import type { IrSchemaToAstOptions } from '../../shared/types'; 7 - import { arrayToAst } from './array'; 8 - import { booleanToAst } from './boolean'; 9 - import { enumToAst } from './enum'; 10 - import { neverToAst } from './never'; 11 - import { nullToAst } from './null'; 12 - import { numberToNode } from './number'; 13 - import { objectToAst } from './object'; 14 - import { stringToNode } from './string'; 15 - import { tupleToAst } from './tuple'; 16 - import { undefinedToAst } from './undefined'; 17 - import { unknownToAst } from './unknown'; 18 - import { voidToAst } from './void'; 19 - 20 - export function irSchemaWithTypeToAst({ 21 - schema, 22 - ...args 23 - }: IrSchemaToAstOptions & { 24 - schema: SchemaWithType; 25 - }): { 26 - anyType?: string; 27 - expression: ReturnType<typeof $.call | typeof $.expr>; 28 - } { 29 - switch (schema.type) { 30 - case 'array': 31 - return { 32 - expression: pipesToNode( 33 - arrayToAst({ 34 - ...args, 35 - schema: schema as SchemaWithType<'array'>, 36 - }).pipes, 37 - args.plugin, 38 - ), 39 - }; 40 - case 'boolean': 41 - return { 42 - expression: booleanToAst({ 43 - ...args, 44 - schema: schema as SchemaWithType<'boolean'>, 45 - }), 46 - }; 47 - case 'enum': 48 - return { 49 - expression: enumToAst({ 50 - ...args, 51 - schema: schema as SchemaWithType<'enum'>, 52 - }), 53 - }; 54 - case 'integer': 55 - case 'number': 56 - return { 57 - expression: numberToNode({ 58 - ...args, 59 - schema: schema as SchemaWithType<'integer' | 'number'>, 60 - }), 61 - }; 62 - case 'never': 63 - return { 64 - expression: neverToAst({ 65 - ...args, 66 - schema: schema as SchemaWithType<'never'>, 67 - }), 68 - }; 69 - case 'null': 70 - return { 71 - expression: nullToAst({ 72 - ...args, 73 - schema: schema as SchemaWithType<'null'>, 74 - }), 75 - }; 76 - case 'object': 77 - return { 78 - expression: pipesToNode( 79 - objectToAst({ 80 - ...args, 81 - schema: schema as SchemaWithType<'object'>, 82 - }).pipes, 83 - args.plugin, 84 - ), 85 - }; 86 - case 'string': 87 - return { 88 - expression: shouldCoerceToBigInt(schema.format) 89 - ? numberToNode({ 90 - ...args, 91 - schema: { ...schema, type: 'number' }, 92 - }) 93 - : stringToNode({ 94 - ...args, 95 - schema: schema as SchemaWithType<'string'>, 96 - }), 97 - }; 98 - case 'tuple': 99 - return { 100 - expression: pipesToNode( 101 - tupleToAst({ 102 - ...args, 103 - schema: schema as SchemaWithType<'tuple'>, 104 - }).pipes, 105 - args.plugin, 106 - ), 107 - }; 108 - case 'undefined': 109 - return { 110 - expression: undefinedToAst({ 111 - ...args, 112 - schema: schema as SchemaWithType<'undefined'>, 113 - }), 114 - }; 115 - case 'unknown': 116 - return { 117 - expression: unknownToAst({ 118 - ...args, 119 - schema: schema as SchemaWithType<'unknown'>, 120 - }), 121 - }; 122 - case 'void': 123 - return { 124 - expression: voidToAst({ 125 - ...args, 126 - schema: schema as SchemaWithType<'void'>, 127 - }), 128 - }; 129 - } 130 - }
+1 -1
packages/openapi-ts/src/plugins/valibot/v1/toAst/never.ts
··· 6 6 7 7 export function neverToAst({ 8 8 plugin, 9 - }: IrSchemaToAstOptions & { 9 + }: Pick<IrSchemaToAstOptions, 'plugin'> & { 10 10 schema: SchemaWithType<'never'>; 11 11 }) { 12 12 const v = plugin.external('valibot.v');
+1 -1
packages/openapi-ts/src/plugins/valibot/v1/toAst/null.ts
··· 6 6 7 7 export function nullToAst({ 8 8 plugin, 9 - }: IrSchemaToAstOptions & { 9 + }: Pick<IrSchemaToAstOptions, 'plugin'> & { 10 10 schema: SchemaWithType<'null'>; 11 11 }) { 12 12 const v = plugin.external('valibot.v');
+1 -1
packages/openapi-ts/src/plugins/valibot/v1/toAst/number.ts
··· 108 108 export function numberToNode({ 109 109 plugin, 110 110 schema, 111 - }: IrSchemaToAstOptions & { 111 + }: Pick<IrSchemaToAstOptions, 'plugin'> & { 112 112 schema: SchemaWithType<'integer' | 'number'>; 113 113 }): Pipe { 114 114 const ctx: NumberResolverContext = {
+44 -36
packages/openapi-ts/src/plugins/valibot/v1/toAst/object.ts
··· 1 - import { fromRef, ref } from '@hey-api/codegen-core'; 2 - import type { SchemaWithType } from '@hey-api/shared'; 1 + import type { SchemaResult, SchemaVisitorContext, SchemaWithType, Walker } from '@hey-api/shared'; 2 + import { childContext } from '@hey-api/shared'; 3 3 4 4 import { $ } from '../../../../ts-dsl'; 5 5 import type { ObjectResolverContext } from '../../resolvers'; 6 6 import type { Pipe, PipeResult } from '../../shared/pipes'; 7 7 import { pipes } from '../../shared/pipes'; 8 8 import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; 9 + import type { ValibotPlugin } from '../../types'; 9 10 import { identifiers } from '../constants'; 10 - import { irSchemaToAst } from '../plugin'; 11 11 12 - function additionalPropertiesNode(ctx: ObjectResolverContext): Pipe | null | undefined { 13 - const { plugin, schema } = ctx; 12 + type WalkerCtx = SchemaVisitorContext<ValibotPlugin['Instance']>; 13 + 14 + interface ObjectToAstOptions extends IrSchemaToAstOptions { 15 + applyModifiers: (result: SchemaResult<Ast>, opts: { optional?: boolean }) => Ast; 16 + schema: SchemaWithType<'object'>; 17 + walk: Walker<Ast, ValibotPlugin['Instance']>; 18 + walkerCtx: WalkerCtx; 19 + } 20 + 21 + type ExtendedContext = ObjectResolverContext & { 22 + applyModifiers: ObjectToAstOptions['applyModifiers']; 23 + walk: ObjectToAstOptions['walk']; 24 + walkerCtx: ObjectToAstOptions['walkerCtx']; 25 + }; 26 + 27 + function additionalPropertiesNode(ctx: ExtendedContext): Pipe | null | undefined { 28 + const { plugin, schema, walk, walkerCtx } = ctx; 14 29 15 30 if (!schema.additionalProperties || !schema.additionalProperties.type) return; 16 31 if (schema.additionalProperties.type === 'never') return null; 17 32 18 - const additionalAst = irSchemaToAst({ 19 - ...ctx, 20 - schema: schema.additionalProperties, 21 - state: { 22 - ...ctx.utils.state, 23 - path: ref([...fromRef(ctx.utils.state.path), 'additionalProperties']), 24 - }, 25 - }); 26 - if (additionalAst.hasLazyExpression) ctx.utils.ast.hasLazyExpression = true; 27 - return pipes.toNode(additionalAst.pipes, plugin); 33 + const additionalResult = walk( 34 + schema.additionalProperties, 35 + childContext(walkerCtx, 'additionalProperties'), 36 + ); 37 + if (additionalResult.hasLazyExpression) ctx.utils.ast.hasLazyExpression = true; 38 + return pipes.toNode(additionalResult.expression.pipes, plugin); 28 39 } 29 40 30 - function baseNode(ctx: ObjectResolverContext): PipeResult { 41 + function baseNode(ctx: ExtendedContext): PipeResult { 31 42 const { nodes, symbols } = ctx; 32 43 const { v } = symbols; 33 44 ··· 51 62 return $(v).attr(identifiers.schemas.object).call(shape); 52 63 } 53 64 54 - function objectResolver(ctx: ObjectResolverContext): PipeResult { 65 + function objectResolver(ctx: ExtendedContext): PipeResult { 55 66 // TODO: parser - handle constants 56 67 return ctx.nodes.base(ctx); 57 68 } 58 69 59 - function shapeNode(ctx: ObjectResolverContext): ReturnType<typeof $.object> { 60 - const { plugin, schema } = ctx; 70 + function shapeNode(ctx: ExtendedContext): ReturnType<typeof $.object> { 71 + const { applyModifiers, plugin, schema, walk, walkerCtx } = ctx; 61 72 const shape = $.object().pretty(); 62 73 63 74 for (const name in schema.properties) { 64 75 const property = schema.properties[name]!; 76 + const isOptional = !schema.required?.includes(name); 65 77 66 - const propertyAst = irSchemaToAst({ 67 - ...ctx, 68 - optional: !schema.required?.includes(name), 69 - schema: property, 70 - state: { 71 - ...ctx.utils.state, 72 - path: ref([...fromRef(ctx.utils.state.path), 'properties', name]), 73 - }, 78 + const propertyResult = walk(property, childContext(walkerCtx, 'properties', name)); 79 + if (propertyResult.hasLazyExpression) ctx.utils.ast.hasLazyExpression = true; 80 + 81 + const ast = applyModifiers(propertyResult, { 82 + optional: isOptional, 74 83 }); 75 - if (propertyAst.hasLazyExpression) ctx.utils.ast.hasLazyExpression = true; 76 - shape.prop(name, pipes.toNode(propertyAst.pipes, plugin)); 84 + 85 + shape.prop(name, pipes.toNode(ast.pipes, plugin)); 77 86 } 78 87 79 88 return shape; 80 89 } 81 90 82 - export function objectToAst( 83 - options: IrSchemaToAstOptions & { 84 - schema: SchemaWithType<'object'>; 85 - }, 86 - ): Omit<Ast, 'typeName'> { 87 - const { plugin } = options; 88 - const ctx: ObjectResolverContext = { 91 + export function objectToAst(options: ObjectToAstOptions): Omit<Ast, 'typeName'> { 92 + const { applyModifiers, plugin, walk, walkerCtx } = options; 93 + const ctx: ExtendedContext = { 89 94 ...options, 90 95 $, 96 + applyModifiers, 91 97 nodes: { 92 98 additionalProperties: additionalPropertiesNode, 93 99 base: baseNode, ··· 104 110 ast: {}, 105 111 state: options.state, 106 112 }, 113 + walk, 114 + walkerCtx, 107 115 }; 108 116 const resolver = plugin.config['~resolvers']?.object; 109 117 const node = resolver?.(ctx) ?? objectResolver(ctx);
+1 -1
packages/openapi-ts/src/plugins/valibot/v1/toAst/string.ts
··· 101 101 export function stringToNode({ 102 102 plugin, 103 103 schema, 104 - }: IrSchemaToAstOptions & { 104 + }: Pick<IrSchemaToAstOptions, 'plugin'> & { 105 105 schema: SchemaWithType<'string'>; 106 106 }): Pipe { 107 107 const ctx: StringResolverContext = {
+19 -14
packages/openapi-ts/src/plugins/valibot/v1/toAst/tuple.ts
··· 1 - import { fromRef, ref } from '@hey-api/codegen-core'; 2 - import type { SchemaWithType } from '@hey-api/shared'; 1 + import type { SchemaResult, SchemaWithType } from '@hey-api/shared'; 2 + import { childContext } from '@hey-api/shared'; 3 3 4 4 import { $ } from '../../../../ts-dsl'; 5 5 import { pipesToNode } from '../../shared/pipes'; 6 6 import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; 7 7 import { identifiers } from '../constants'; 8 - import { irSchemaToAst } from '../plugin'; 9 8 import { unknownToAst } from './unknown'; 10 9 11 10 export function tupleToAst( 12 11 options: IrSchemaToAstOptions & { 12 + applyModifiers: (result: SchemaResult<Ast>, opts: { optional?: boolean }) => Ast; 13 13 schema: SchemaWithType<'tuple'>; 14 14 }, 15 15 ): Omit<Ast, 'typeName'> { 16 - const { plugin, schema } = options; 16 + const { applyModifiers, plugin, schema, walk } = options; 17 17 18 18 const result: Partial<Omit<Ast, 'typeName'>> = {}; 19 19 ··· 33 33 34 34 if (schema.items) { 35 35 const tupleElements = schema.items.map((item, index) => { 36 - const schemaPipes = irSchemaToAst({ 37 - ...options, 38 - schema: item, 39 - state: { 40 - ...options.state, 41 - path: ref([...fromRef(options.state.path), 'items', index]), 42 - }, 43 - }); 44 - if (schemaPipes.hasLazyExpression) { 36 + const itemResult = walk( 37 + item, 38 + childContext( 39 + { 40 + path: options.state.path, 41 + plugin: options.plugin, 42 + }, 43 + 'items', 44 + index, 45 + ), 46 + ); 47 + if (itemResult.hasLazyExpression) { 45 48 result.hasLazyExpression = true; 46 49 } 47 - return pipesToNode(schemaPipes.pipes, plugin); 50 + 51 + const finalExpr = applyModifiers(itemResult, { optional: false }); 52 + return pipesToNode(finalExpr.pipes, plugin); 48 53 }); 49 54 result.pipes = [ 50 55 $(v)
+1 -1
packages/openapi-ts/src/plugins/valibot/v1/toAst/undefined.ts
··· 6 6 7 7 export function undefinedToAst({ 8 8 plugin, 9 - }: IrSchemaToAstOptions & { 9 + }: Pick<IrSchemaToAstOptions, 'plugin'> & { 10 10 schema: SchemaWithType<'undefined'>; 11 11 }) { 12 12 const v = plugin.external('valibot.v');
+1 -1
packages/openapi-ts/src/plugins/valibot/v1/toAst/unknown.ts
··· 6 6 7 7 export function unknownToAst({ 8 8 plugin, 9 - }: IrSchemaToAstOptions & { 9 + }: Pick<IrSchemaToAstOptions, 'plugin'> & { 10 10 schema: SchemaWithType<'unknown'>; 11 11 }) { 12 12 const v = plugin.external('valibot.v');
+1 -1
packages/openapi-ts/src/plugins/valibot/v1/toAst/void.ts
··· 6 6 7 7 export function voidToAst({ 8 8 plugin, 9 - }: IrSchemaToAstOptions & { 9 + }: Pick<IrSchemaToAstOptions, 'plugin'> & { 10 10 schema: SchemaWithType<'void'>; 11 11 }) { 12 12 const v = plugin.external('valibot.v');
+358
packages/openapi-ts/src/plugins/valibot/v1/walker.ts
··· 1 + import type { Refs, SymbolMeta } from '@hey-api/codegen-core'; 2 + import { fromRef } from '@hey-api/codegen-core'; 3 + import type { IR, SchemaExtractor, SchemaResult, SchemaVisitor } from '@hey-api/shared'; 4 + import { pathToJsonPointer } from '@hey-api/shared'; 5 + 6 + import { $ } from '../../../ts-dsl'; 7 + import { maybeBigInt, shouldCoerceToBigInt } from '../../shared/utils/coerce'; 8 + import { pipesToNode } from '../shared/pipes'; 9 + import type { ProcessorContext } from '../shared/processor'; 10 + import type { Ast, PluginState } from '../shared/types'; 11 + import type { ValibotPlugin } from '../types'; 12 + import { identifiers } from './constants'; 13 + import { arrayToAst } from './toAst/array'; 14 + import { booleanToAst } from './toAst/boolean'; 15 + import { enumToAst } from './toAst/enum'; 16 + import { neverToAst } from './toAst/never'; 17 + import { nullToAst } from './toAst/null'; 18 + import { numberToNode } from './toAst/number'; 19 + import { objectToAst } from './toAst/object'; 20 + import { stringToNode } from './toAst/string'; 21 + import { tupleToAst } from './toAst/tuple'; 22 + import { undefinedToAst } from './toAst/undefined'; 23 + import { unknownToAst } from './toAst/unknown'; 24 + import { voidToAst } from './toAst/void'; 25 + 26 + export interface VisitorConfig { 27 + /** Optional schema extractor function. */ 28 + schemaExtractor?: SchemaExtractor<ProcessorContext>; 29 + /** The plugin state references. */ 30 + state: Refs<PluginState>; 31 + } 32 + 33 + function getDefaultValue(result: SchemaResult<Ast>) { 34 + return result.format ? maybeBigInt(result.default, result.format) : $.fromValue(result.default); 35 + } 36 + 37 + export function createVisitor( 38 + config: VisitorConfig, 39 + ): SchemaVisitor<Ast, ValibotPlugin['Instance']> { 40 + const { schemaExtractor, state } = config; 41 + return { 42 + applyModifiers(result, ctx, options = {}) { 43 + const { optional } = options; 44 + const v = ctx.plugin.external('valibot.v'); 45 + const pipes = [...result.expression.pipes]; 46 + 47 + if (result.readonly) { 48 + pipes.push($(v).attr(identifiers.actions.readonly).call()); 49 + } 50 + 51 + const hasDefault = result.default !== undefined; 52 + const needsOptional = optional || hasDefault; 53 + const needsNullable = result.nullable; 54 + const innerNode = pipesToNode(pipes, ctx.plugin); 55 + 56 + if (needsOptional && needsNullable) { 57 + if (hasDefault) { 58 + return { 59 + pipes: [ 60 + $(v).attr(identifiers.schemas.nullish).call(innerNode, getDefaultValue(result)), 61 + ], 62 + }; 63 + } 64 + return { pipes: [$(v).attr(identifiers.schemas.nullish).call(innerNode)] }; 65 + } 66 + 67 + if (needsOptional) { 68 + if (hasDefault) { 69 + return { 70 + pipes: [ 71 + $(v).attr(identifiers.schemas.optional).call(innerNode, getDefaultValue(result)), 72 + ], 73 + }; 74 + } 75 + return { pipes: [$(v).attr(identifiers.schemas.optional).call(innerNode)] }; 76 + } 77 + 78 + if (needsNullable) { 79 + return { pipes: [$(v).attr(identifiers.schemas.nullable).call(innerNode)] }; 80 + } 81 + 82 + return { pipes }; 83 + }, 84 + array(schema, ctx, walk) { 85 + const applyModifiers = (result: SchemaResult<Ast>, opts: { optional?: boolean }) => 86 + this.applyModifiers(result, ctx, opts); 87 + const ast = arrayToAst({ 88 + ...ctx, 89 + applyModifiers, 90 + schema, 91 + state, 92 + walk, 93 + }); 94 + return { 95 + expression: ast, 96 + hasLazyExpression: state.hasLazyExpression['~ref'], 97 + nullable: false, 98 + readonly: false, 99 + }; 100 + }, 101 + boolean(schema, ctx) { 102 + const pipe = booleanToAst({ ...ctx, schema }); 103 + return { 104 + expression: { pipes: [pipe] }, 105 + nullable: false, 106 + readonly: false, 107 + }; 108 + }, 109 + enum(schema, ctx) { 110 + const pipe = enumToAst({ ...ctx, schema, state }); 111 + const hasNull = 112 + schema.items?.some((item) => item.type === 'null' || item.const === null) ?? false; 113 + return { 114 + expression: { pipes: [pipe] }, 115 + nullable: hasNull, 116 + readonly: false, 117 + }; 118 + }, 119 + integer(schema, ctx) { 120 + const pipe = numberToNode({ ...ctx, schema }); 121 + return { 122 + expression: { pipes: [pipe] }, 123 + format: schema.format, 124 + nullable: false, 125 + readonly: false, 126 + }; 127 + }, 128 + intercept(schema, ctx, walk) { 129 + if (schemaExtractor && !schema.$ref) { 130 + const extracted = schemaExtractor({ 131 + meta: { 132 + resource: 'definition', 133 + resourceId: pathToJsonPointer(fromRef(ctx.path)), 134 + }, 135 + naming: ctx.plugin.config.definitions, 136 + path: fromRef(ctx.path), 137 + plugin: ctx.plugin, 138 + schema, 139 + }); 140 + if (extracted !== schema) { 141 + return walk(extracted, ctx); 142 + } 143 + } 144 + }, 145 + intersection(items, schemas, ctx) { 146 + const v = ctx.plugin.external('valibot.v'); 147 + const hasAnyLazy = items.some((item) => item.hasLazyExpression); 148 + const itemNodes = items.map((item) => pipesToNode(item.expression.pipes, ctx.plugin)); 149 + 150 + return { 151 + expression: { 152 + pipes: [ 153 + $(v) 154 + .attr(identifiers.schemas.intersect) 155 + .call($.array(...itemNodes)), 156 + ], 157 + }, 158 + hasLazyExpression: hasAnyLazy, 159 + nullable: items.some((i) => i.nullable), 160 + readonly: items.some((i) => i.readonly), 161 + }; 162 + }, 163 + never(schema, ctx) { 164 + const pipe = neverToAst({ ...ctx, schema }); 165 + return { 166 + expression: { pipes: [pipe] }, 167 + nullable: false, 168 + readonly: false, 169 + }; 170 + }, 171 + null(schema, ctx) { 172 + const pipe = nullToAst({ ...ctx, schema }); 173 + return { 174 + expression: { pipes: [pipe] }, 175 + nullable: false, 176 + readonly: false, 177 + }; 178 + }, 179 + number(schema, ctx) { 180 + const pipe = numberToNode({ ...ctx, schema }); 181 + return { 182 + expression: { pipes: [pipe] }, 183 + format: schema.format, 184 + nullable: false, 185 + readonly: false, 186 + }; 187 + }, 188 + object(schema, ctx, walk) { 189 + const applyModifiers = (result: SchemaResult<Ast>, opts: { optional?: boolean }) => 190 + this.applyModifiers(result, ctx, opts); 191 + const ast = objectToAst({ 192 + applyModifiers, 193 + plugin: ctx.plugin, 194 + schema, 195 + state, 196 + walk, 197 + walkerCtx: ctx, 198 + }); 199 + return { 200 + expression: ast, 201 + hasLazyExpression: ast.hasLazyExpression ?? false, 202 + nullable: false, 203 + readonly: false, 204 + }; 205 + }, 206 + postProcess(result, schema, ctx) { 207 + if (ctx.plugin.config.metadata && schema.description) { 208 + const v = ctx.plugin.external('valibot.v'); 209 + const metadataExpr = $(v) 210 + .attr(identifiers.actions.metadata) 211 + .call($.object().prop('description', $.literal(schema.description))); 212 + return { 213 + ...result, 214 + expression: { pipes: [...result.expression.pipes, metadataExpr] }, 215 + }; 216 + } 217 + return result; 218 + }, 219 + reference($ref, schema, ctx) { 220 + const v = ctx.plugin.external('valibot.v'); 221 + const query: SymbolMeta = { 222 + category: 'schema', 223 + resource: 'definition', 224 + resourceId: $ref, 225 + tool: 'valibot', 226 + }; 227 + 228 + const refSymbol = ctx.plugin.referenceSymbol(query); 229 + 230 + if (ctx.plugin.isSymbolRegistered(query)) { 231 + return { 232 + expression: { pipes: [$(refSymbol)] }, 233 + nullable: false, 234 + readonly: false, 235 + }; 236 + } 237 + 238 + state.hasLazyExpression['~ref'] = true; 239 + return { 240 + expression: { 241 + pipes: [ 242 + $(v) 243 + .attr(identifiers.schemas.lazy) 244 + .call($.func().do($(refSymbol).return())), 245 + ], 246 + }, 247 + hasLazyExpression: true, 248 + nullable: false, 249 + readonly: false, 250 + }; 251 + }, 252 + string(schema, ctx) { 253 + if (shouldCoerceToBigInt(schema.format)) { 254 + const pipe = numberToNode({ 255 + plugin: ctx.plugin, 256 + schema: { ...schema, type: 'number' }, 257 + }); 258 + return { 259 + expression: { pipes: [pipe] }, 260 + nullable: false, 261 + readonly: false, 262 + }; 263 + } 264 + 265 + const pipe = stringToNode({ ...ctx, schema }); 266 + return { 267 + expression: { pipes: [pipe] }, 268 + nullable: false, 269 + readonly: false, 270 + }; 271 + }, 272 + tuple(schema, ctx, walk) { 273 + const applyModifiers = (result: SchemaResult<Ast>, opts: { optional?: boolean }) => 274 + this.applyModifiers(result, ctx, opts); 275 + const ast = tupleToAst({ 276 + ...ctx, 277 + applyModifiers, 278 + schema, 279 + state, 280 + walk, 281 + }); 282 + return { 283 + expression: ast, 284 + hasLazyExpression: state.hasLazyExpression['~ref'], 285 + nullable: false, 286 + readonly: false, 287 + }; 288 + }, 289 + undefined(schema, ctx) { 290 + const pipe = undefinedToAst({ ...ctx, schema }); 291 + return { 292 + expression: { pipes: [pipe] }, 293 + nullable: false, 294 + readonly: false, 295 + }; 296 + }, 297 + union(items, schemas, ctx) { 298 + const v = ctx.plugin.external('valibot.v'); 299 + const hasAnyLazy = items.some((item) => item.hasLazyExpression); 300 + 301 + const hasNull = schemas.some((s) => s.type === 'null') || items.some((i) => i.nullable); 302 + 303 + const nonNullItems: typeof items = []; 304 + const nonNullSchemas: Array<IR.SchemaObject> = []; 305 + 306 + items.forEach((item, index) => { 307 + const schema = schemas[index]!; 308 + if (schema.type !== 'null') { 309 + nonNullItems.push(item); 310 + nonNullSchemas.push(schema); 311 + } 312 + }); 313 + 314 + // Build union expression WITHOUT null - null is tracked via nullable flag 315 + let expression: Ast; 316 + if (nonNullItems.length === 0) { 317 + // Union was only null 318 + expression = { pipes: [$(v).attr(identifiers.schemas.null).call()] }; 319 + } else if (nonNullItems.length === 1) { 320 + // Single non-null item 321 + expression = nonNullItems[0]!.expression; 322 + } else { 323 + // Multiple non-null items 324 + const itemNodes = nonNullItems.map((i) => pipesToNode(i.expression.pipes, ctx.plugin)); 325 + expression = { 326 + pipes: [ 327 + $(v) 328 + .attr(identifiers.schemas.union) 329 + .call($.array(...itemNodes)), 330 + ], 331 + }; 332 + } 333 + 334 + return { 335 + expression, 336 + hasLazyExpression: hasAnyLazy, 337 + nullable: hasNull, 338 + readonly: false, 339 + }; 340 + }, 341 + unknown(schema, ctx) { 342 + const pipe = unknownToAst({ ...ctx, schema }); 343 + return { 344 + expression: { pipes: [pipe] }, 345 + nullable: false, 346 + readonly: false, 347 + }; 348 + }, 349 + void(schema, ctx) { 350 + const pipe = voidToAst({ ...ctx, schema }); 351 + return { 352 + expression: { pipes: [pipe] }, 353 + nullable: false, 354 + readonly: false, 355 + }; 356 + }, 357 + }; 358 + }
+1
packages/openapi-ts/src/plugins/zod/constants.ts
··· 39 39 never: 'never', 40 40 null: 'null', 41 41 nullable: 'nullable', 42 + nullish: 'nullish', 42 43 number: 'number', 43 44 object: 'object', 44 45 optional: 'optional',
+9 -7
packages/shared/src/index.ts
··· 58 58 parameterWithPagination, 59 59 } from './ir/parameter'; 60 60 export { deduplicateSchema } from './ir/schema'; 61 + export type { 62 + SchemaExtractor, 63 + SchemaProcessor, 64 + SchemaProcessorContext, 65 + SchemaProcessorResult, 66 + } from './ir/schema-processor'; 67 + export { createSchemaProcessor } from './ir/schema-processor'; 68 + export type { SchemaResult, SchemaVisitor, SchemaVisitorContext, Walker } from './ir/schema-walker'; 69 + export { childContext, createSchemaWalker } from './ir/schema-walker'; 61 70 export type { IR } from './ir/types'; 62 71 export { addItemsToSchema } from './ir/utils'; 63 72 export { parseOpenApiSpec } from './openApi'; ··· 87 96 OpenApiSchemaObject, 88 97 } from './openApi/types'; 89 98 export type { Hooks } from './parser/hooks'; 90 - export type { 91 - SchemaExtractor, 92 - SchemaProcessor, 93 - SchemaProcessorContext, 94 - SchemaProcessorResult, 95 - } from './plugins/schema-processor'; 96 - export { createSchemaProcessor } from './plugins/schema-processor'; 97 99 export type { SchemaWithType } from './plugins/shared/types/schema'; 98 100 export { definePluginConfig, mappers } from './plugins/shared/utils/config'; 99 101 export type { PluginInstanceTypes } from './plugins/shared/utils/instance';
+283
packages/shared/src/ir/schema-walker.ts
··· 1 + import type { Ref } from '@hey-api/codegen-core'; 2 + import { fromRef, ref } from '@hey-api/codegen-core'; 3 + 4 + import type { SchemaWithType } from '../plugins/shared/types/schema'; 5 + import { deduplicateSchema } from './schema'; 6 + import type { IR } from './types'; 7 + 8 + /** 9 + * Result returned by visitor methods. Contains the expression plus metadata 10 + * needed for modifier application. 11 + */ 12 + export interface SchemaResult<TExpr> { 13 + /** Default value from schema, if any. */ 14 + default?: unknown; 15 + /** The core schema expression, WITHOUT optional/nullable/default applied. */ 16 + expression: TExpr; 17 + /** The original schema format. */ 18 + format?: string; 19 + /** For libraries that need lazy evaluation. */ 20 + hasLazyExpression?: boolean; 21 + /** Plugin-specific metadata bucket. */ 22 + meta?: Record<string, unknown>; 23 + /** Does this schema explicitly allow null? */ 24 + nullable: boolean; 25 + /** Is this schema read-only? */ 26 + readonly: boolean; 27 + } 28 + 29 + /** 30 + * Context passed to all visitor methods. 31 + */ 32 + export interface SchemaVisitorContext<TPlugin = unknown> { 33 + /** Current path in the schema tree. */ 34 + path: Ref<ReadonlyArray<string | number>>; 35 + /** The plugin instance. */ 36 + plugin: TPlugin; 37 + } 38 + 39 + /** 40 + * The walk function signature. 41 + */ 42 + export type Walker<TExpr, TPlugin = unknown> = ( 43 + schema: IR.SchemaObject, 44 + ctx: SchemaVisitorContext<TPlugin>, 45 + ) => SchemaResult<TExpr>; 46 + 47 + /** 48 + * The visitor interface. Plugins implement this to define how schemas 49 + * are transformed into their target representation. 50 + */ 51 + export interface SchemaVisitor<TExpr, TPlugin = unknown> { 52 + /** 53 + * Apply modifiers (optional/nullable/nullish/default) to a schema result. 54 + */ 55 + applyModifiers( 56 + result: SchemaResult<TExpr>, 57 + ctx: SchemaVisitorContext<TPlugin>, 58 + context?: { 59 + /** Is this property optional? */ 60 + optional?: boolean; 61 + }, 62 + ): TExpr; 63 + array( 64 + schema: SchemaWithType<'array'>, 65 + ctx: SchemaVisitorContext<TPlugin>, 66 + walk: Walker<TExpr, TPlugin>, 67 + ): SchemaResult<TExpr>; 68 + boolean( 69 + schema: SchemaWithType<'boolean'>, 70 + ctx: SchemaVisitorContext<TPlugin>, 71 + ): SchemaResult<TExpr>; 72 + enum( 73 + schema: SchemaWithType<'enum'>, 74 + ctx: SchemaVisitorContext<TPlugin>, 75 + walk: Walker<TExpr, TPlugin>, 76 + ): SchemaResult<TExpr>; 77 + integer( 78 + schema: SchemaWithType<'integer'>, 79 + ctx: SchemaVisitorContext<TPlugin>, 80 + ): SchemaResult<TExpr>; 81 + /** 82 + * Called before any dispatch logic. Return a result to short-circuit, 83 + * or undefined to continue normal dispatch. 84 + */ 85 + intercept?( 86 + schema: IR.SchemaObject, 87 + ctx: SchemaVisitorContext<TPlugin>, 88 + walk: Walker<TExpr, TPlugin>, 89 + ): SchemaResult<TExpr> | undefined; 90 + /** 91 + * Handle intersection types. Receives already-walked child results. 92 + */ 93 + intersection( 94 + items: Array<SchemaResult<TExpr>>, 95 + schemas: ReadonlyArray<IR.SchemaObject>, 96 + ctx: SchemaVisitorContext<TPlugin>, 97 + ): SchemaResult<TExpr>; 98 + never(schema: SchemaWithType<'never'>, ctx: SchemaVisitorContext<TPlugin>): SchemaResult<TExpr>; 99 + null(schema: SchemaWithType<'null'>, ctx: SchemaVisitorContext<TPlugin>): SchemaResult<TExpr>; 100 + number(schema: SchemaWithType<'number'>, ctx: SchemaVisitorContext<TPlugin>): SchemaResult<TExpr>; 101 + object( 102 + schema: SchemaWithType<'object'>, 103 + ctx: SchemaVisitorContext<TPlugin>, 104 + walk: Walker<TExpr, TPlugin>, 105 + ): SchemaResult<TExpr>; 106 + /** 107 + * Called after each typed schema visitor returns. 108 + */ 109 + postProcess?( 110 + result: SchemaResult<TExpr>, 111 + schema: IR.SchemaObject, 112 + ctx: SchemaVisitorContext<TPlugin>, 113 + ): SchemaResult<TExpr>; 114 + /** 115 + * Handle $ref to another schema. 116 + */ 117 + reference( 118 + $ref: string, 119 + schema: IR.SchemaObject, 120 + ctx: SchemaVisitorContext<TPlugin>, 121 + ): SchemaResult<TExpr>; 122 + string(schema: SchemaWithType<'string'>, ctx: SchemaVisitorContext<TPlugin>): SchemaResult<TExpr>; 123 + tuple( 124 + schema: SchemaWithType<'tuple'>, 125 + ctx: SchemaVisitorContext<TPlugin>, 126 + walk: Walker<TExpr, TPlugin>, 127 + ): SchemaResult<TExpr>; 128 + undefined( 129 + schema: SchemaWithType<'undefined'>, 130 + ctx: SchemaVisitorContext<TPlugin>, 131 + ): SchemaResult<TExpr>; 132 + /** 133 + * Handle union types. Receives already-walked child results. 134 + */ 135 + union( 136 + items: Array<SchemaResult<TExpr>>, 137 + schemas: ReadonlyArray<IR.SchemaObject>, 138 + ctx: SchemaVisitorContext<TPlugin>, 139 + ): SchemaResult<TExpr>; 140 + unknown( 141 + schema: SchemaWithType<'unknown'>, 142 + ctx: SchemaVisitorContext<TPlugin>, 143 + ): SchemaResult<TExpr>; 144 + void(schema: SchemaWithType<'void'>, ctx: SchemaVisitorContext<TPlugin>): SchemaResult<TExpr>; 145 + } 146 + 147 + /** 148 + * Create a schema walker from a visitor. 149 + * 150 + * The walker handles: 151 + * - Dispatch order ($ref → type → items → fallback) 152 + * - Deduplication of union/intersection schemas 153 + * - Path tracking for child schemas 154 + * - Calling the appropriate visitor method 155 + */ 156 + export function createSchemaWalker<TExpr, TPlugin = unknown>( 157 + visitor: SchemaVisitor<TExpr, TPlugin>, 158 + ): Walker<TExpr, TPlugin> { 159 + const walk: Walker<TExpr, TPlugin> = (schema, ctx) => { 160 + // escape hatch 161 + if (visitor.intercept) { 162 + const intercepted = visitor.intercept(schema, ctx, walk); 163 + if (intercepted !== undefined) { 164 + return intercepted; 165 + } 166 + } 167 + 168 + const baseResult = { 169 + default: schema.default, 170 + readonly: schema.accessScope === 'read', 171 + }; 172 + 173 + if (schema.$ref) { 174 + const result = visitor.reference(schema.$ref, schema, ctx); 175 + return { 176 + ...result, 177 + default: result.default ?? baseResult.default, 178 + readonly: result.readonly || baseResult.readonly, 179 + }; 180 + } 181 + 182 + if (schema.type) { 183 + let result = visitTyped(schema as SchemaWithType, ctx, visitor, walk); 184 + if (visitor.postProcess) { 185 + result = visitor.postProcess(result, schema, ctx); 186 + } 187 + return { 188 + ...result, 189 + default: result.default ?? baseResult.default, 190 + readonly: result.readonly || baseResult.readonly, 191 + }; 192 + } 193 + 194 + if (schema.items) { 195 + const deduplicated = deduplicateSchema({ schema }); 196 + 197 + // deduplication might collapse to a single schema 198 + if (!deduplicated.items) { 199 + return walk(deduplicated, ctx); 200 + } 201 + 202 + const itemResults = deduplicated.items.map((item, index) => 203 + walk(item, { 204 + ...ctx, 205 + path: ref([...fromRef(ctx.path), 'items', index]), 206 + }), 207 + ); 208 + 209 + const result = 210 + deduplicated.logicalOperator === 'and' 211 + ? visitor.intersection(itemResults, deduplicated.items, ctx) 212 + : visitor.union(itemResults, deduplicated.items, ctx); 213 + 214 + return { 215 + ...result, 216 + default: result.default ?? baseResult.default, 217 + readonly: result.readonly || baseResult.readonly, 218 + }; 219 + } 220 + 221 + // fallback 222 + const result = visitor.unknown({ type: 'unknown' }, ctx); 223 + return { 224 + ...result, 225 + default: result.default ?? baseResult.default, 226 + readonly: result.readonly || baseResult.readonly, 227 + }; 228 + }; 229 + 230 + return walk; 231 + } 232 + 233 + /** 234 + * Dispatch to the appropriate visitor method based on schema type. 235 + */ 236 + function visitTyped<TExpr, TPlugin>( 237 + schema: SchemaWithType, 238 + ctx: SchemaVisitorContext<TPlugin>, 239 + visitor: SchemaVisitor<TExpr, TPlugin>, 240 + walk: Walker<TExpr, TPlugin>, 241 + ): SchemaResult<TExpr> { 242 + switch (schema.type) { 243 + case 'array': 244 + return visitor.array(schema as SchemaWithType<'array'>, ctx, walk); 245 + case 'boolean': 246 + return visitor.boolean(schema as SchemaWithType<'boolean'>, ctx); 247 + case 'enum': 248 + return visitor.enum(schema as SchemaWithType<'enum'>, ctx, walk); 249 + case 'integer': 250 + return visitor.integer(schema as SchemaWithType<'integer'>, ctx); 251 + case 'never': 252 + return visitor.never(schema as SchemaWithType<'never'>, ctx); 253 + case 'null': 254 + return visitor.null(schema as SchemaWithType<'null'>, ctx); 255 + case 'number': 256 + return visitor.number(schema as SchemaWithType<'number'>, ctx); 257 + case 'object': 258 + return visitor.object(schema as SchemaWithType<'object'>, ctx, walk); 259 + case 'string': 260 + return visitor.string(schema as SchemaWithType<'string'>, ctx); 261 + case 'tuple': 262 + return visitor.tuple(schema as SchemaWithType<'tuple'>, ctx, walk); 263 + case 'undefined': 264 + return visitor.undefined(schema as SchemaWithType<'undefined'>, ctx); 265 + case 'unknown': 266 + return visitor.unknown(schema as SchemaWithType<'unknown'>, ctx); 267 + case 'void': 268 + return visitor.void(schema as SchemaWithType<'void'>, ctx); 269 + } 270 + } 271 + 272 + /** 273 + * Helper to create a child context with an extended path. 274 + */ 275 + export function childContext<TPlugin>( 276 + ctx: SchemaVisitorContext<TPlugin>, 277 + ...segments: ReadonlyArray<string | number> 278 + ): SchemaVisitorContext<TPlugin> { 279 + return { 280 + ...ctx, 281 + path: ref([...fromRef(ctx.path), ...segments]), 282 + }; 283 + }
+1 -1
packages/shared/src/parser/hooks.ts
··· 1 1 import type { Node, Symbol, SymbolIn } from '@hey-api/codegen-core'; 2 2 3 + import type { SchemaProcessorContext } from '../ir/schema-processor'; 3 4 import type { IROperationObject } from '../ir/types'; 4 - import type { SchemaProcessorContext } from '../plugins/schema-processor'; 5 5 import type { PluginInstance } from '../plugins/shared/utils/instance'; 6 6 7 7 export type Hooks = {
+1 -1
packages/shared/src/plugins/schema-processor.ts packages/shared/src/ir/schema-processor.ts
··· 1 - import type { IR } from '../ir/types'; 2 1 import { pathToJsonPointer } from '../utils/ref'; 2 + import type { IR } from './types'; 3 3 4 4 export interface SchemaProcessor { 5 5 /** Current inherited context (set by withContext) */
+1 -1
pyproject.toml
··· 4 4 requires-python = ">=3.9" 5 5 6 6 [dependency-groups] 7 - dev = ["httpx>=0.27", "mypy>=1.15", "ruff>=0.11"] 7 + dev = ["httpx>=0.27", "mypy>=1.15", "pydantic>=2.0", "ruff>=0.11"] 8 8 9 9 [tool.mypy] 10 10 python_version = "3.9"
+169
uv.lock
··· 3 3 requires-python = ">=3.9" 4 4 5 5 [[package]] 6 + name = "annotated-types" 7 + version = "0.7.0" 8 + source = { registry = "https://pypi.org/simple" } 9 + sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" } 10 + wheels = [ 11 + { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" }, 12 + ] 13 + 14 + [[package]] 6 15 name = "anyio" 7 16 version = "4.12.1" 8 17 source = { registry = "https://pypi.org/simple" } ··· 55 64 dev = [ 56 65 { name = "httpx" }, 57 66 { name = "mypy" }, 67 + { name = "pydantic" }, 58 68 { name = "ruff" }, 59 69 ] 60 70 ··· 64 74 dev = [ 65 75 { name = "httpx", specifier = ">=0.27" }, 66 76 { name = "mypy", specifier = ">=1.15" }, 77 + { name = "pydantic", specifier = ">=2.0" }, 67 78 { name = "ruff", specifier = ">=0.11" }, 68 79 ] 69 80 ··· 258 269 ] 259 270 260 271 [[package]] 272 + name = "pydantic" 273 + version = "2.12.5" 274 + source = { registry = "https://pypi.org/simple" } 275 + dependencies = [ 276 + { name = "annotated-types" }, 277 + { name = "pydantic-core" }, 278 + { name = "typing-extensions" }, 279 + { name = "typing-inspection" }, 280 + ] 281 + sdist = { url = "https://files.pythonhosted.org/packages/69/44/36f1a6e523abc58ae5f928898e4aca2e0ea509b5aa6f6f392a5d882be928/pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49", size = 821591, upload-time = "2025-11-26T15:11:46.471Z" } 282 + wheels = [ 283 + { url = "https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d", size = 463580, upload-time = "2025-11-26T15:11:44.605Z" }, 284 + ] 285 + 286 + [[package]] 287 + name = "pydantic-core" 288 + version = "2.41.5" 289 + source = { registry = "https://pypi.org/simple" } 290 + dependencies = [ 291 + { name = "typing-extensions" }, 292 + ] 293 + sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952, upload-time = "2025-11-04T13:43:49.098Z" } 294 + wheels = [ 295 + { url = "https://files.pythonhosted.org/packages/c6/90/32c9941e728d564b411d574d8ee0cf09b12ec978cb22b294995bae5549a5/pydantic_core-2.41.5-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:77b63866ca88d804225eaa4af3e664c5faf3568cea95360d21f4725ab6e07146", size = 2107298, upload-time = "2025-11-04T13:39:04.116Z" }, 296 + { url = "https://files.pythonhosted.org/packages/fb/a8/61c96a77fe28993d9a6fb0f4127e05430a267b235a124545d79fea46dd65/pydantic_core-2.41.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dfa8a0c812ac681395907e71e1274819dec685fec28273a28905df579ef137e2", size = 1901475, upload-time = "2025-11-04T13:39:06.055Z" }, 297 + { url = "https://files.pythonhosted.org/packages/5d/b6/338abf60225acc18cdc08b4faef592d0310923d19a87fba1faf05af5346e/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5921a4d3ca3aee735d9fd163808f5e8dd6c6972101e4adbda9a4667908849b97", size = 1918815, upload-time = "2025-11-04T13:39:10.41Z" }, 298 + { url = "https://files.pythonhosted.org/packages/d1/1c/2ed0433e682983d8e8cba9c8d8ef274d4791ec6a6f24c58935b90e780e0a/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e25c479382d26a2a41b7ebea1043564a937db462816ea07afa8a44c0866d52f9", size = 2065567, upload-time = "2025-11-04T13:39:12.244Z" }, 299 + { url = "https://files.pythonhosted.org/packages/b3/24/cf84974ee7d6eae06b9e63289b7b8f6549d416b5c199ca2d7ce13bbcf619/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f547144f2966e1e16ae626d8ce72b4cfa0caedc7fa28052001c94fb2fcaa1c52", size = 2230442, upload-time = "2025-11-04T13:39:13.962Z" }, 300 + { url = "https://files.pythonhosted.org/packages/fd/21/4e287865504b3edc0136c89c9c09431be326168b1eb7841911cbc877a995/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f52298fbd394f9ed112d56f3d11aabd0d5bd27beb3084cc3d8ad069483b8941", size = 2350956, upload-time = "2025-11-04T13:39:15.889Z" }, 301 + { url = "https://files.pythonhosted.org/packages/a8/76/7727ef2ffa4b62fcab916686a68a0426b9b790139720e1934e8ba797e238/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:100baa204bb412b74fe285fb0f3a385256dad1d1879f0a5cb1499ed2e83d132a", size = 2068253, upload-time = "2025-11-04T13:39:17.403Z" }, 302 + { url = "https://files.pythonhosted.org/packages/d5/8c/a4abfc79604bcb4c748e18975c44f94f756f08fb04218d5cb87eb0d3a63e/pydantic_core-2.41.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:05a2c8852530ad2812cb7914dc61a1125dc4e06252ee98e5638a12da6cc6fb6c", size = 2177050, upload-time = "2025-11-04T13:39:19.351Z" }, 303 + { url = "https://files.pythonhosted.org/packages/67/b1/de2e9a9a79b480f9cb0b6e8b6ba4c50b18d4e89852426364c66aa82bb7b3/pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:29452c56df2ed968d18d7e21f4ab0ac55e71dc59524872f6fc57dcf4a3249ed2", size = 2147178, upload-time = "2025-11-04T13:39:21Z" }, 304 + { url = "https://files.pythonhosted.org/packages/16/c1/dfb33f837a47b20417500efaa0378adc6635b3c79e8369ff7a03c494b4ac/pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:d5160812ea7a8a2ffbe233d8da666880cad0cbaf5d4de74ae15c313213d62556", size = 2341833, upload-time = "2025-11-04T13:39:22.606Z" }, 305 + { url = "https://files.pythonhosted.org/packages/47/36/00f398642a0f4b815a9a558c4f1dca1b4020a7d49562807d7bc9ff279a6c/pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:df3959765b553b9440adfd3c795617c352154e497a4eaf3752555cfb5da8fc49", size = 2321156, upload-time = "2025-11-04T13:39:25.843Z" }, 306 + { url = "https://files.pythonhosted.org/packages/7e/70/cad3acd89fde2010807354d978725ae111ddf6d0ea46d1ea1775b5c1bd0c/pydantic_core-2.41.5-cp310-cp310-win32.whl", hash = "sha256:1f8d33a7f4d5a7889e60dc39856d76d09333d8a6ed0f5f1190635cbec70ec4ba", size = 1989378, upload-time = "2025-11-04T13:39:27.92Z" }, 307 + { url = "https://files.pythonhosted.org/packages/76/92/d338652464c6c367e5608e4488201702cd1cbb0f33f7b6a85a60fe5f3720/pydantic_core-2.41.5-cp310-cp310-win_amd64.whl", hash = "sha256:62de39db01b8d593e45871af2af9e497295db8d73b085f6bfd0b18c83c70a8f9", size = 2013622, upload-time = "2025-11-04T13:39:29.848Z" }, 308 + { url = "https://files.pythonhosted.org/packages/e8/72/74a989dd9f2084b3d9530b0915fdda64ac48831c30dbf7c72a41a5232db8/pydantic_core-2.41.5-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:a3a52f6156e73e7ccb0f8cced536adccb7042be67cb45f9562e12b319c119da6", size = 2105873, upload-time = "2025-11-04T13:39:31.373Z" }, 309 + { url = "https://files.pythonhosted.org/packages/12/44/37e403fd9455708b3b942949e1d7febc02167662bf1a7da5b78ee1ea2842/pydantic_core-2.41.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7f3bf998340c6d4b0c9a2f02d6a400e51f123b59565d74dc60d252ce888c260b", size = 1899826, upload-time = "2025-11-04T13:39:32.897Z" }, 310 + { url = "https://files.pythonhosted.org/packages/33/7f/1d5cab3ccf44c1935a359d51a8a2a9e1a654b744b5e7f80d41b88d501eec/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:378bec5c66998815d224c9ca994f1e14c0c21cb95d2f52b6021cc0b2a58f2a5a", size = 1917869, upload-time = "2025-11-04T13:39:34.469Z" }, 311 + { url = "https://files.pythonhosted.org/packages/6e/6a/30d94a9674a7fe4f4744052ed6c5e083424510be1e93da5bc47569d11810/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e7b576130c69225432866fe2f4a469a85a54ade141d96fd396dffcf607b558f8", size = 2063890, upload-time = "2025-11-04T13:39:36.053Z" }, 312 + { url = "https://files.pythonhosted.org/packages/50/be/76e5d46203fcb2750e542f32e6c371ffa9b8ad17364cf94bb0818dbfb50c/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6cb58b9c66f7e4179a2d5e0f849c48eff5c1fca560994d6eb6543abf955a149e", size = 2229740, upload-time = "2025-11-04T13:39:37.753Z" }, 313 + { url = "https://files.pythonhosted.org/packages/d3/ee/fed784df0144793489f87db310a6bbf8118d7b630ed07aa180d6067e653a/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:88942d3a3dff3afc8288c21e565e476fc278902ae4d6d134f1eeda118cc830b1", size = 2350021, upload-time = "2025-11-04T13:39:40.94Z" }, 314 + { url = "https://files.pythonhosted.org/packages/c8/be/8fed28dd0a180dca19e72c233cbf58efa36df055e5b9d90d64fd1740b828/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f31d95a179f8d64d90f6831d71fa93290893a33148d890ba15de25642c5d075b", size = 2066378, upload-time = "2025-11-04T13:39:42.523Z" }, 315 + { url = "https://files.pythonhosted.org/packages/b0/3b/698cf8ae1d536a010e05121b4958b1257f0b5522085e335360e53a6b1c8b/pydantic_core-2.41.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c1df3d34aced70add6f867a8cf413e299177e0c22660cc767218373d0779487b", size = 2175761, upload-time = "2025-11-04T13:39:44.553Z" }, 316 + { url = "https://files.pythonhosted.org/packages/b8/ba/15d537423939553116dea94ce02f9c31be0fa9d0b806d427e0308ec17145/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4009935984bd36bd2c774e13f9a09563ce8de4abaa7226f5108262fa3e637284", size = 2146303, upload-time = "2025-11-04T13:39:46.238Z" }, 317 + { url = "https://files.pythonhosted.org/packages/58/7f/0de669bf37d206723795f9c90c82966726a2ab06c336deba4735b55af431/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:34a64bc3441dc1213096a20fe27e8e128bd3ff89921706e83c0b1ac971276594", size = 2340355, upload-time = "2025-11-04T13:39:48.002Z" }, 318 + { url = "https://files.pythonhosted.org/packages/e5/de/e7482c435b83d7e3c3ee5ee4451f6e8973cff0eb6007d2872ce6383f6398/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c9e19dd6e28fdcaa5a1de679aec4141f691023916427ef9bae8584f9c2fb3b0e", size = 2319875, upload-time = "2025-11-04T13:39:49.705Z" }, 319 + { url = "https://files.pythonhosted.org/packages/fe/e6/8c9e81bb6dd7560e33b9053351c29f30c8194b72f2d6932888581f503482/pydantic_core-2.41.5-cp311-cp311-win32.whl", hash = "sha256:2c010c6ded393148374c0f6f0bf89d206bf3217f201faa0635dcd56bd1520f6b", size = 1987549, upload-time = "2025-11-04T13:39:51.842Z" }, 320 + { url = "https://files.pythonhosted.org/packages/11/66/f14d1d978ea94d1bc21fc98fcf570f9542fe55bfcc40269d4e1a21c19bf7/pydantic_core-2.41.5-cp311-cp311-win_amd64.whl", hash = "sha256:76ee27c6e9c7f16f47db7a94157112a2f3a00e958bc626e2f4ee8bec5c328fbe", size = 2011305, upload-time = "2025-11-04T13:39:53.485Z" }, 321 + { url = "https://files.pythonhosted.org/packages/56/d8/0e271434e8efd03186c5386671328154ee349ff0354d83c74f5caaf096ed/pydantic_core-2.41.5-cp311-cp311-win_arm64.whl", hash = "sha256:4bc36bbc0b7584de96561184ad7f012478987882ebf9f9c389b23f432ea3d90f", size = 1972902, upload-time = "2025-11-04T13:39:56.488Z" }, 322 + { url = "https://files.pythonhosted.org/packages/5f/5d/5f6c63eebb5afee93bcaae4ce9a898f3373ca23df3ccaef086d0233a35a7/pydantic_core-2.41.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7", size = 2110990, upload-time = "2025-11-04T13:39:58.079Z" }, 323 + { url = "https://files.pythonhosted.org/packages/aa/32/9c2e8ccb57c01111e0fd091f236c7b371c1bccea0fa85247ac55b1e2b6b6/pydantic_core-2.41.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0", size = 1896003, upload-time = "2025-11-04T13:39:59.956Z" }, 324 + { url = "https://files.pythonhosted.org/packages/68/b8/a01b53cb0e59139fbc9e4fda3e9724ede8de279097179be4ff31f1abb65a/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69", size = 1919200, upload-time = "2025-11-04T13:40:02.241Z" }, 325 + { url = "https://files.pythonhosted.org/packages/38/de/8c36b5198a29bdaade07b5985e80a233a5ac27137846f3bc2d3b40a47360/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed2e99c456e3fadd05c991f8f437ef902e00eedf34320ba2b0842bd1c3ca3a75", size = 2052578, upload-time = "2025-11-04T13:40:04.401Z" }, 326 + { url = "https://files.pythonhosted.org/packages/00/b5/0e8e4b5b081eac6cb3dbb7e60a65907549a1ce035a724368c330112adfdd/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65840751b72fbfd82c3c640cff9284545342a4f1eb1586ad0636955b261b0b05", size = 2208504, upload-time = "2025-11-04T13:40:06.072Z" }, 327 + { url = "https://files.pythonhosted.org/packages/77/56/87a61aad59c7c5b9dc8caad5a41a5545cba3810c3e828708b3d7404f6cef/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e536c98a7626a98feb2d3eaf75944ef6f3dbee447e1f841eae16f2f0a72d8ddc", size = 2335816, upload-time = "2025-11-04T13:40:07.835Z" }, 328 + { url = "https://files.pythonhosted.org/packages/0d/76/941cc9f73529988688a665a5c0ecff1112b3d95ab48f81db5f7606f522d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eceb81a8d74f9267ef4081e246ffd6d129da5d87e37a77c9bde550cb04870c1c", size = 2075366, upload-time = "2025-11-04T13:40:09.804Z" }, 329 + { url = "https://files.pythonhosted.org/packages/d3/43/ebef01f69baa07a482844faaa0a591bad1ef129253ffd0cdaa9d8a7f72d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d38548150c39b74aeeb0ce8ee1d8e82696f4a4e16ddc6de7b1d8823f7de4b9b5", size = 2171698, upload-time = "2025-11-04T13:40:12.004Z" }, 330 + { url = "https://files.pythonhosted.org/packages/b1/87/41f3202e4193e3bacfc2c065fab7706ebe81af46a83d3e27605029c1f5a6/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c23e27686783f60290e36827f9c626e63154b82b116d7fe9adba1fda36da706c", size = 2132603, upload-time = "2025-11-04T13:40:13.868Z" }, 331 + { url = "https://files.pythonhosted.org/packages/49/7d/4c00df99cb12070b6bccdef4a195255e6020a550d572768d92cc54dba91a/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:482c982f814460eabe1d3bb0adfdc583387bd4691ef00b90575ca0d2b6fe2294", size = 2329591, upload-time = "2025-11-04T13:40:15.672Z" }, 332 + { url = "https://files.pythonhosted.org/packages/cc/6a/ebf4b1d65d458f3cda6a7335d141305dfa19bdc61140a884d165a8a1bbc7/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bfea2a5f0b4d8d43adf9d7b8bf019fb46fdd10a2e5cde477fbcb9d1fa08c68e1", size = 2319068, upload-time = "2025-11-04T13:40:17.532Z" }, 333 + { url = "https://files.pythonhosted.org/packages/49/3b/774f2b5cd4192d5ab75870ce4381fd89cf218af999515baf07e7206753f0/pydantic_core-2.41.5-cp312-cp312-win32.whl", hash = "sha256:b74557b16e390ec12dca509bce9264c3bbd128f8a2c376eaa68003d7f327276d", size = 1985908, upload-time = "2025-11-04T13:40:19.309Z" }, 334 + { url = "https://files.pythonhosted.org/packages/86/45/00173a033c801cacf67c190fef088789394feaf88a98a7035b0e40d53dc9/pydantic_core-2.41.5-cp312-cp312-win_amd64.whl", hash = "sha256:1962293292865bca8e54702b08a4f26da73adc83dd1fcf26fbc875b35d81c815", size = 2020145, upload-time = "2025-11-04T13:40:21.548Z" }, 335 + { url = "https://files.pythonhosted.org/packages/f9/22/91fbc821fa6d261b376a3f73809f907cec5ca6025642c463d3488aad22fb/pydantic_core-2.41.5-cp312-cp312-win_arm64.whl", hash = "sha256:1746d4a3d9a794cacae06a5eaaccb4b8643a131d45fbc9af23e353dc0a5ba5c3", size = 1976179, upload-time = "2025-11-04T13:40:23.393Z" }, 336 + { url = "https://files.pythonhosted.org/packages/87/06/8806241ff1f70d9939f9af039c6c35f2360cf16e93c2ca76f184e76b1564/pydantic_core-2.41.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9", size = 2120403, upload-time = "2025-11-04T13:40:25.248Z" }, 337 + { url = "https://files.pythonhosted.org/packages/94/02/abfa0e0bda67faa65fef1c84971c7e45928e108fe24333c81f3bfe35d5f5/pydantic_core-2.41.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34", size = 1896206, upload-time = "2025-11-04T13:40:27.099Z" }, 338 + { url = "https://files.pythonhosted.org/packages/15/df/a4c740c0943e93e6500f9eb23f4ca7ec9bf71b19e608ae5b579678c8d02f/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0", size = 1919307, upload-time = "2025-11-04T13:40:29.806Z" }, 339 + { url = "https://files.pythonhosted.org/packages/9a/e3/6324802931ae1d123528988e0e86587c2072ac2e5394b4bc2bc34b61ff6e/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:03ca43e12fab6023fc79d28ca6b39b05f794ad08ec2feccc59a339b02f2b3d33", size = 2063258, upload-time = "2025-11-04T13:40:33.544Z" }, 340 + { url = "https://files.pythonhosted.org/packages/c9/d4/2230d7151d4957dd79c3044ea26346c148c98fbf0ee6ebd41056f2d62ab5/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc799088c08fa04e43144b164feb0c13f9a0bc40503f8df3e9fde58a3c0c101e", size = 2214917, upload-time = "2025-11-04T13:40:35.479Z" }, 341 + { url = "https://files.pythonhosted.org/packages/e6/9f/eaac5df17a3672fef0081b6c1bb0b82b33ee89aa5cec0d7b05f52fd4a1fa/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:97aeba56665b4c3235a0e52b2c2f5ae9cd071b8a8310ad27bddb3f7fb30e9aa2", size = 2332186, upload-time = "2025-11-04T13:40:37.436Z" }, 342 + { url = "https://files.pythonhosted.org/packages/cf/4e/35a80cae583a37cf15604b44240e45c05e04e86f9cfd766623149297e971/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586", size = 2073164, upload-time = "2025-11-04T13:40:40.289Z" }, 343 + { url = "https://files.pythonhosted.org/packages/bf/e3/f6e262673c6140dd3305d144d032f7bd5f7497d3871c1428521f19f9efa2/pydantic_core-2.41.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b93590ae81f7010dbe380cdeab6f515902ebcbefe0b9327cc4804d74e93ae69d", size = 2179146, upload-time = "2025-11-04T13:40:42.809Z" }, 344 + { url = "https://files.pythonhosted.org/packages/75/c7/20bd7fc05f0c6ea2056a4565c6f36f8968c0924f19b7d97bbfea55780e73/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740", size = 2137788, upload-time = "2025-11-04T13:40:44.752Z" }, 345 + { url = "https://files.pythonhosted.org/packages/3a/8d/34318ef985c45196e004bc46c6eab2eda437e744c124ef0dbe1ff2c9d06b/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:6561e94ba9dacc9c61bce40e2d6bdc3bfaa0259d3ff36ace3b1e6901936d2e3e", size = 2340133, upload-time = "2025-11-04T13:40:46.66Z" }, 346 + { url = "https://files.pythonhosted.org/packages/9c/59/013626bf8c78a5a5d9350d12e7697d3d4de951a75565496abd40ccd46bee/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858", size = 2324852, upload-time = "2025-11-04T13:40:48.575Z" }, 347 + { url = "https://files.pythonhosted.org/packages/1a/d9/c248c103856f807ef70c18a4f986693a46a8ffe1602e5d361485da502d20/pydantic_core-2.41.5-cp313-cp313-win32.whl", hash = "sha256:650ae77860b45cfa6e2cdafc42618ceafab3a2d9a3811fcfbd3bbf8ac3c40d36", size = 1994679, upload-time = "2025-11-04T13:40:50.619Z" }, 348 + { url = "https://files.pythonhosted.org/packages/9e/8b/341991b158ddab181cff136acd2552c9f35bd30380422a639c0671e99a91/pydantic_core-2.41.5-cp313-cp313-win_amd64.whl", hash = "sha256:79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11", size = 2019766, upload-time = "2025-11-04T13:40:52.631Z" }, 349 + { url = "https://files.pythonhosted.org/packages/73/7d/f2f9db34af103bea3e09735bb40b021788a5e834c81eedb541991badf8f5/pydantic_core-2.41.5-cp313-cp313-win_arm64.whl", hash = "sha256:3f84d5c1b4ab906093bdc1ff10484838aca54ef08de4afa9de0f5f14d69639cd", size = 1981005, upload-time = "2025-11-04T13:40:54.734Z" }, 350 + { url = "https://files.pythonhosted.org/packages/ea/28/46b7c5c9635ae96ea0fbb779e271a38129df2550f763937659ee6c5dbc65/pydantic_core-2.41.5-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a", size = 2119622, upload-time = "2025-11-04T13:40:56.68Z" }, 351 + { url = "https://files.pythonhosted.org/packages/74/1a/145646e5687e8d9a1e8d09acb278c8535ebe9e972e1f162ed338a622f193/pydantic_core-2.41.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14", size = 1891725, upload-time = "2025-11-04T13:40:58.807Z" }, 352 + { url = "https://files.pythonhosted.org/packages/23/04/e89c29e267b8060b40dca97bfc64a19b2a3cf99018167ea1677d96368273/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1", size = 1915040, upload-time = "2025-11-04T13:41:00.853Z" }, 353 + { url = "https://files.pythonhosted.org/packages/84/a3/15a82ac7bd97992a82257f777b3583d3e84bdb06ba6858f745daa2ec8a85/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66", size = 2063691, upload-time = "2025-11-04T13:41:03.504Z" }, 354 + { url = "https://files.pythonhosted.org/packages/74/9b/0046701313c6ef08c0c1cf0e028c67c770a4e1275ca73131563c5f2a310a/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869", size = 2213897, upload-time = "2025-11-04T13:41:05.804Z" }, 355 + { url = "https://files.pythonhosted.org/packages/8a/cd/6bac76ecd1b27e75a95ca3a9a559c643b3afcd2dd62086d4b7a32a18b169/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2", size = 2333302, upload-time = "2025-11-04T13:41:07.809Z" }, 356 + { url = "https://files.pythonhosted.org/packages/4c/d2/ef2074dc020dd6e109611a8be4449b98cd25e1b9b8a303c2f0fca2f2bcf7/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375", size = 2064877, upload-time = "2025-11-04T13:41:09.827Z" }, 357 + { url = "https://files.pythonhosted.org/packages/18/66/e9db17a9a763d72f03de903883c057b2592c09509ccfe468187f2a2eef29/pydantic_core-2.41.5-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553", size = 2180680, upload-time = "2025-11-04T13:41:12.379Z" }, 358 + { url = "https://files.pythonhosted.org/packages/d3/9e/3ce66cebb929f3ced22be85d4c2399b8e85b622db77dad36b73c5387f8f8/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90", size = 2138960, upload-time = "2025-11-04T13:41:14.627Z" }, 359 + { url = "https://files.pythonhosted.org/packages/a6/62/205a998f4327d2079326b01abee48e502ea739d174f0a89295c481a2272e/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07", size = 2339102, upload-time = "2025-11-04T13:41:16.868Z" }, 360 + { url = "https://files.pythonhosted.org/packages/3c/0d/f05e79471e889d74d3d88f5bd20d0ed189ad94c2423d81ff8d0000aab4ff/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb", size = 2326039, upload-time = "2025-11-04T13:41:18.934Z" }, 361 + { url = "https://files.pythonhosted.org/packages/ec/e1/e08a6208bb100da7e0c4b288eed624a703f4d129bde2da475721a80cab32/pydantic_core-2.41.5-cp314-cp314-win32.whl", hash = "sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23", size = 1995126, upload-time = "2025-11-04T13:41:21.418Z" }, 362 + { url = "https://files.pythonhosted.org/packages/48/5d/56ba7b24e9557f99c9237e29f5c09913c81eeb2f3217e40e922353668092/pydantic_core-2.41.5-cp314-cp314-win_amd64.whl", hash = "sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf", size = 2015489, upload-time = "2025-11-04T13:41:24.076Z" }, 363 + { url = "https://files.pythonhosted.org/packages/4e/bb/f7a190991ec9e3e0ba22e4993d8755bbc4a32925c0b5b42775c03e8148f9/pydantic_core-2.41.5-cp314-cp314-win_arm64.whl", hash = "sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0", size = 1977288, upload-time = "2025-11-04T13:41:26.33Z" }, 364 + { url = "https://files.pythonhosted.org/packages/92/ed/77542d0c51538e32e15afe7899d79efce4b81eee631d99850edc2f5e9349/pydantic_core-2.41.5-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a", size = 2120255, upload-time = "2025-11-04T13:41:28.569Z" }, 365 + { url = "https://files.pythonhosted.org/packages/bb/3d/6913dde84d5be21e284439676168b28d8bbba5600d838b9dca99de0fad71/pydantic_core-2.41.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3", size = 1863760, upload-time = "2025-11-04T13:41:31.055Z" }, 366 + { url = "https://files.pythonhosted.org/packages/5a/f0/e5e6b99d4191da102f2b0eb9687aaa7f5bea5d9964071a84effc3e40f997/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c", size = 1878092, upload-time = "2025-11-04T13:41:33.21Z" }, 367 + { url = "https://files.pythonhosted.org/packages/71/48/36fb760642d568925953bcc8116455513d6e34c4beaa37544118c36aba6d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612", size = 2053385, upload-time = "2025-11-04T13:41:35.508Z" }, 368 + { url = "https://files.pythonhosted.org/packages/20/25/92dc684dd8eb75a234bc1c764b4210cf2646479d54b47bf46061657292a8/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d", size = 2218832, upload-time = "2025-11-04T13:41:37.732Z" }, 369 + { url = "https://files.pythonhosted.org/packages/e2/09/f53e0b05023d3e30357d82eb35835d0f6340ca344720a4599cd663dca599/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9", size = 2327585, upload-time = "2025-11-04T13:41:40Z" }, 370 + { url = "https://files.pythonhosted.org/packages/aa/4e/2ae1aa85d6af35a39b236b1b1641de73f5a6ac4d5a7509f77b814885760c/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660", size = 2041078, upload-time = "2025-11-04T13:41:42.323Z" }, 371 + { url = "https://files.pythonhosted.org/packages/cd/13/2e215f17f0ef326fc72afe94776edb77525142c693767fc347ed6288728d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9", size = 2173914, upload-time = "2025-11-04T13:41:45.221Z" }, 372 + { url = "https://files.pythonhosted.org/packages/02/7a/f999a6dcbcd0e5660bc348a3991c8915ce6599f4f2c6ac22f01d7a10816c/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3", size = 2129560, upload-time = "2025-11-04T13:41:47.474Z" }, 373 + { url = "https://files.pythonhosted.org/packages/3a/b1/6c990ac65e3b4c079a4fb9f5b05f5b013afa0f4ed6780a3dd236d2cbdc64/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf", size = 2329244, upload-time = "2025-11-04T13:41:49.992Z" }, 374 + { url = "https://files.pythonhosted.org/packages/d9/02/3c562f3a51afd4d88fff8dffb1771b30cfdfd79befd9883ee094f5b6c0d8/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470", size = 2331955, upload-time = "2025-11-04T13:41:54.079Z" }, 375 + { url = "https://files.pythonhosted.org/packages/5c/96/5fb7d8c3c17bc8c62fdb031c47d77a1af698f1d7a406b0f79aaa1338f9ad/pydantic_core-2.41.5-cp314-cp314t-win32.whl", hash = "sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa", size = 1988906, upload-time = "2025-11-04T13:41:56.606Z" }, 376 + { url = "https://files.pythonhosted.org/packages/22/ed/182129d83032702912c2e2d8bbe33c036f342cc735737064668585dac28f/pydantic_core-2.41.5-cp314-cp314t-win_amd64.whl", hash = "sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c", size = 1981607, upload-time = "2025-11-04T13:41:58.889Z" }, 377 + { url = "https://files.pythonhosted.org/packages/9f/ed/068e41660b832bb0b1aa5b58011dea2a3fe0ba7861ff38c4d4904c1c1a99/pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008", size = 1974769, upload-time = "2025-11-04T13:42:01.186Z" }, 378 + { url = "https://files.pythonhosted.org/packages/54/db/160dffb57ed9a3705c4cbcbff0ac03bdae45f1ca7d58ab74645550df3fbd/pydantic_core-2.41.5-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:8bfeaf8735be79f225f3fefab7f941c712aaca36f1128c9d7e2352ee1aa87bdf", size = 2107999, upload-time = "2025-11-04T13:42:03.885Z" }, 379 + { url = "https://files.pythonhosted.org/packages/a3/7d/88e7de946f60d9263cc84819f32513520b85c0f8322f9b8f6e4afc938383/pydantic_core-2.41.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:346285d28e4c8017da95144c7f3acd42740d637ff41946af5ce6e5e420502dd5", size = 1929745, upload-time = "2025-11-04T13:42:06.075Z" }, 380 + { url = "https://files.pythonhosted.org/packages/d5/c2/aef51e5b283780e85e99ff19db0f05842d2d4a8a8cd15e63b0280029b08f/pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a75dafbf87d6276ddc5b2bf6fae5254e3d0876b626eb24969a574fff9149ee5d", size = 1920220, upload-time = "2025-11-04T13:42:08.457Z" }, 381 + { url = "https://files.pythonhosted.org/packages/c7/97/492ab10f9ac8695cd76b2fdb24e9e61f394051df71594e9bcc891c9f586e/pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7b93a4d08587e2b7e7882de461e82b6ed76d9026ce91ca7915e740ecc7855f60", size = 2067296, upload-time = "2025-11-04T13:42:10.817Z" }, 382 + { url = "https://files.pythonhosted.org/packages/ec/23/984149650e5269c59a2a4c41d234a9570adc68ab29981825cfaf4cfad8f4/pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e8465ab91a4bd96d36dde3263f06caa6a8a6019e4113f24dc753d79a8b3a3f82", size = 2231548, upload-time = "2025-11-04T13:42:13.843Z" }, 383 + { url = "https://files.pythonhosted.org/packages/71/0c/85bcbb885b9732c28bec67a222dbed5ed2d77baee1f8bba2002e8cd00c5c/pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:299e0a22e7ae2b85c1a57f104538b2656e8ab1873511fd718a1c1c6f149b77b5", size = 2362571, upload-time = "2025-11-04T13:42:16.208Z" }, 384 + { url = "https://files.pythonhosted.org/packages/c0/4a/412d2048be12c334003e9b823a3fa3d038e46cc2d64dd8aab50b31b65499/pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:707625ef0983fcfb461acfaf14de2067c5942c6bb0f3b4c99158bed6fedd3cf3", size = 2068175, upload-time = "2025-11-04T13:42:18.911Z" }, 385 + { url = "https://files.pythonhosted.org/packages/73/f4/c58b6a776b502d0a5540ad02e232514285513572060f0d78f7832ca3c98b/pydantic_core-2.41.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f41eb9797986d6ebac5e8edff36d5cef9de40def462311b3eb3eeded1431e425", size = 2177203, upload-time = "2025-11-04T13:42:22.578Z" }, 386 + { url = "https://files.pythonhosted.org/packages/ed/ae/f06ea4c7e7a9eead3d165e7623cd2ea0cb788e277e4f935af63fc98fa4e6/pydantic_core-2.41.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0384e2e1021894b1ff5a786dbf94771e2986ebe2869533874d7e43bc79c6f504", size = 2148191, upload-time = "2025-11-04T13:42:24.89Z" }, 387 + { url = "https://files.pythonhosted.org/packages/c1/57/25a11dcdc656bf5f8b05902c3c2934ac3ea296257cc4a3f79a6319e61856/pydantic_core-2.41.5-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:f0cd744688278965817fd0839c4a4116add48d23890d468bc436f78beb28abf5", size = 2343907, upload-time = "2025-11-04T13:42:27.683Z" }, 388 + { url = "https://files.pythonhosted.org/packages/96/82/e33d5f4933d7a03327c0c43c65d575e5919d4974ffc026bc917a5f7b9f61/pydantic_core-2.41.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:753e230374206729bf0a807954bcc6c150d3743928a73faffee51ac6557a03c3", size = 2322174, upload-time = "2025-11-04T13:42:30.776Z" }, 389 + { url = "https://files.pythonhosted.org/packages/81/45/4091be67ce9f469e81656f880f3506f6a5624121ec5eb3eab37d7581897d/pydantic_core-2.41.5-cp39-cp39-win32.whl", hash = "sha256:873e0d5b4fb9b89ef7c2d2a963ea7d02879d9da0da8d9d4933dee8ee86a8b460", size = 1990353, upload-time = "2025-11-04T13:42:33.111Z" }, 390 + { url = "https://files.pythonhosted.org/packages/44/8a/a98aede18db6e9cd5d66bcacd8a409fcf8134204cdede2e7de35c5a2c5ef/pydantic_core-2.41.5-cp39-cp39-win_amd64.whl", hash = "sha256:e4f4a984405e91527a0d62649ee21138f8e3d0ef103be488c1dc11a80d7f184b", size = 2015698, upload-time = "2025-11-04T13:42:35.484Z" }, 391 + { url = "https://files.pythonhosted.org/packages/11/72/90fda5ee3b97e51c494938a4a44c3a35a9c96c19bba12372fb9c634d6f57/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:b96d5f26b05d03cc60f11a7761a5ded1741da411e7fe0909e27a5e6a0cb7b034", size = 2115441, upload-time = "2025-11-04T13:42:39.557Z" }, 392 + { url = "https://files.pythonhosted.org/packages/1f/53/8942f884fa33f50794f119012dc6a1a02ac43a56407adaac20463df8e98f/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:634e8609e89ceecea15e2d61bc9ac3718caaaa71963717bf3c8f38bfde64242c", size = 1930291, upload-time = "2025-11-04T13:42:42.169Z" }, 393 + { url = "https://files.pythonhosted.org/packages/79/c8/ecb9ed9cd942bce09fc888ee960b52654fbdbede4ba6c2d6e0d3b1d8b49c/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:93e8740d7503eb008aa2df04d3b9735f845d43ae845e6dcd2be0b55a2da43cd2", size = 1948632, upload-time = "2025-11-04T13:42:44.564Z" }, 394 + { url = "https://files.pythonhosted.org/packages/2e/1b/687711069de7efa6af934e74f601e2a4307365e8fdc404703afc453eab26/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f15489ba13d61f670dcc96772e733aad1a6f9c429cc27574c6cdaed82d0146ad", size = 2138905, upload-time = "2025-11-04T13:42:47.156Z" }, 395 + { url = "https://files.pythonhosted.org/packages/09/32/59b0c7e63e277fa7911c2fc70ccfb45ce4b98991e7ef37110663437005af/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd", size = 2110495, upload-time = "2025-11-04T13:42:49.689Z" }, 396 + { url = "https://files.pythonhosted.org/packages/aa/81/05e400037eaf55ad400bcd318c05bb345b57e708887f07ddb2d20e3f0e98/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc", size = 1915388, upload-time = "2025-11-04T13:42:52.215Z" }, 397 + { url = "https://files.pythonhosted.org/packages/6e/0d/e3549b2399f71d56476b77dbf3cf8937cec5cd70536bdc0e374a421d0599/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56", size = 1942879, upload-time = "2025-11-04T13:42:56.483Z" }, 398 + { url = "https://files.pythonhosted.org/packages/f7/07/34573da085946b6a313d7c42f82f16e8920bfd730665de2d11c0c37a74b5/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b", size = 2139017, upload-time = "2025-11-04T13:42:59.471Z" }, 399 + { url = "https://files.pythonhosted.org/packages/e6/b0/1a2aa41e3b5a4ba11420aba2d091b2d17959c8d1519ece3627c371951e73/pydantic_core-2.41.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b5819cd790dbf0c5eb9f82c73c16b39a65dd6dd4d1439dcdea7816ec9adddab8", size = 2103351, upload-time = "2025-11-04T13:43:02.058Z" }, 400 + { url = "https://files.pythonhosted.org/packages/a4/ee/31b1f0020baaf6d091c87900ae05c6aeae101fa4e188e1613c80e4f1ea31/pydantic_core-2.41.5-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:5a4e67afbc95fa5c34cf27d9089bca7fcab4e51e57278d710320a70b956d1b9a", size = 1925363, upload-time = "2025-11-04T13:43:05.159Z" }, 401 + { url = "https://files.pythonhosted.org/packages/e1/89/ab8e86208467e467a80deaca4e434adac37b10a9d134cd2f99b28a01e483/pydantic_core-2.41.5-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ece5c59f0ce7d001e017643d8d24da587ea1f74f6993467d85ae8a5ef9d4f42b", size = 2135615, upload-time = "2025-11-04T13:43:08.116Z" }, 402 + { url = "https://files.pythonhosted.org/packages/99/0a/99a53d06dd0348b2008f2f30884b34719c323f16c3be4e6cc1203b74a91d/pydantic_core-2.41.5-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:16f80f7abe3351f8ea6858914ddc8c77e02578544a0ebc15b4c2e1a0e813b0b2", size = 2175369, upload-time = "2025-11-04T13:43:12.49Z" }, 403 + { url = "https://files.pythonhosted.org/packages/6d/94/30ca3b73c6d485b9bb0bc66e611cff4a7138ff9736b7e66bcf0852151636/pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:33cb885e759a705b426baada1fe68cbb0a2e68e34c5d0d0289a364cf01709093", size = 2144218, upload-time = "2025-11-04T13:43:15.431Z" }, 404 + { url = "https://files.pythonhosted.org/packages/87/57/31b4f8e12680b739a91f472b5671294236b82586889ef764b5fbc6669238/pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:c8d8b4eb992936023be7dee581270af5c6e0697a8559895f527f5b7105ecd36a", size = 2329951, upload-time = "2025-11-04T13:43:18.062Z" }, 405 + { url = "https://files.pythonhosted.org/packages/7d/73/3c2c8edef77b8f7310e6fb012dbc4b8551386ed575b9eb6fb2506e28a7eb/pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:242a206cd0318f95cd21bdacff3fcc3aab23e79bba5cac3db5a841c9ef9c6963", size = 2318428, upload-time = "2025-11-04T13:43:20.679Z" }, 406 + { url = "https://files.pythonhosted.org/packages/2f/02/8559b1f26ee0d502c74f9cca5c0d2fd97e967e083e006bbbb4e97f3a043a/pydantic_core-2.41.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d3a978c4f57a597908b7e697229d996d77a6d3c94901e9edee593adada95ce1a", size = 2147009, upload-time = "2025-11-04T13:43:23.286Z" }, 407 + { url = "https://files.pythonhosted.org/packages/5f/9b/1b3f0e9f9305839d7e84912f9e8bfbd191ed1b1ef48083609f0dabde978c/pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b2379fa7ed44ddecb5bfe4e48577d752db9fc10be00a6b7446e9663ba143de26", size = 2101980, upload-time = "2025-11-04T13:43:25.97Z" }, 408 + { url = "https://files.pythonhosted.org/packages/a4/ed/d71fefcb4263df0da6a85b5d8a7508360f2f2e9b3bf5814be9c8bccdccc1/pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:266fb4cbf5e3cbd0b53669a6d1b039c45e3ce651fd5442eff4d07c2cc8d66808", size = 1923865, upload-time = "2025-11-04T13:43:28.763Z" }, 409 + { url = "https://files.pythonhosted.org/packages/ce/3a/626b38db460d675f873e4444b4bb030453bbe7b4ba55df821d026a0493c4/pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58133647260ea01e4d0500089a8c4f07bd7aa6ce109682b1426394988d8aaacc", size = 2134256, upload-time = "2025-11-04T13:43:31.71Z" }, 410 + { url = "https://files.pythonhosted.org/packages/83/d9/8412d7f06f616bbc053d30cb4e5f76786af3221462ad5eee1f202021eb4e/pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:287dad91cfb551c363dc62899a80e9e14da1f0e2b6ebde82c806612ca2a13ef1", size = 2174762, upload-time = "2025-11-04T13:43:34.744Z" }, 411 + { url = "https://files.pythonhosted.org/packages/55/4c/162d906b8e3ba3a99354e20faa1b49a85206c47de97a639510a0e673f5da/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:03b77d184b9eb40240ae9fd676ca364ce1085f203e1b1256f8ab9984dca80a84", size = 2143141, upload-time = "2025-11-04T13:43:37.701Z" }, 412 + { url = "https://files.pythonhosted.org/packages/1f/f2/f11dd73284122713f5f89fc940f370d035fa8e1e078d446b3313955157fe/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:a668ce24de96165bb239160b3d854943128f4334822900534f2fe947930e5770", size = 2330317, upload-time = "2025-11-04T13:43:40.406Z" }, 413 + { url = "https://files.pythonhosted.org/packages/88/9d/b06ca6acfe4abb296110fb1273a4d848a0bfb2ff65f3ee92127b3244e16b/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f14f8f046c14563f8eb3f45f499cc658ab8d10072961e07225e507adb700e93f", size = 2316992, upload-time = "2025-11-04T13:43:43.602Z" }, 414 + { url = "https://files.pythonhosted.org/packages/36/c7/cfc8e811f061c841d7990b0201912c3556bfeb99cdcb7ed24adc8d6f8704/pydantic_core-2.41.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:56121965f7a4dc965bff783d70b907ddf3d57f6eba29b6d2e5dabfaf07799c51", size = 2145302, upload-time = "2025-11-04T13:43:46.64Z" }, 415 + ] 416 + 417 + [[package]] 261 418 name = "ruff" 262 419 version = "0.14.14" 263 420 source = { registry = "https://pypi.org/simple" } ··· 345 502 wheels = [ 346 503 { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, 347 504 ] 505 + 506 + [[package]] 507 + name = "typing-inspection" 508 + version = "0.4.2" 509 + source = { registry = "https://pypi.org/simple" } 510 + dependencies = [ 511 + { name = "typing-extensions" }, 512 + ] 513 + sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" } 514 + wheels = [ 515 + { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" }, 516 + ]