|
1 | 1 | /** |
2 | | - * Fixture schemas for testing random value generation. |
3 | | - * These schemas represent different JSON Type configurations that can be used |
4 | | - * across multiple test modules. |
| 2 | + * Fixture schemas for testing using the type builder. |
| 3 | + * These schemas represent realistic scenarios covering all node types. |
5 | 4 | */ |
6 | 5 |
|
7 | | -import {s} from '../schema'; |
| 6 | +import {t} from '../type'; |
8 | 7 |
|
9 | 8 | /** |
10 | | - * Basic primitive type schemas |
| 9 | + * User profile schema with nested objects and optional fields |
| 10 | + */ |
| 11 | +export const User = t |
| 12 | + .object({ |
| 13 | + id: t.str, |
| 14 | + name: t.object({ |
| 15 | + first: t.str, |
| 16 | + last: t.str, |
| 17 | + }), |
| 18 | + email: t.String({format: 'ascii'}), |
| 19 | + age: t.Number({gte: 0, lte: 150}), |
| 20 | + verified: t.bool, |
| 21 | + }) |
| 22 | + .opt('avatar', t.String({format: 'ascii'})); |
| 23 | + |
| 24 | +/** |
| 25 | + * Product catalog schema with arrays and formatted numbers |
| 26 | + */ |
| 27 | +export const Product = t.Object( |
| 28 | + t.prop('id', t.String({format: 'ascii'})), |
| 29 | + t.prop('name', t.String({min: 1, max: 100})), |
| 30 | + t.prop('price', t.Number({format: 'f64', gte: 0})), |
| 31 | + t.prop('inStock', t.bool), |
| 32 | + t.prop('categories', t.Array(t.str, {min: 1})), |
| 33 | + t.prop('tags', t.Array(t.str)), |
| 34 | + t.propOpt('description', t.String({max: 1000})), |
| 35 | + t.propOpt('discount', t.Number({gte: 0, lte: 1})), |
| 36 | +); |
| 37 | + |
| 38 | +/** |
| 39 | + * Blog post schema with timestamps and rich content |
| 40 | + */ |
| 41 | +export const BlogPost = t.Object( |
| 42 | + t.prop('id', t.str), |
| 43 | + t.prop('title', t.String({min: 1, max: 200})), |
| 44 | + t.prop('content', t.str), |
| 45 | + t.prop('author', t.Ref<typeof User>('User')), |
| 46 | + t.prop('publishedAt', t.Number({format: 'u64'})), |
| 47 | + t.prop('status', t.enum('draft', 'published', 'archived')), |
| 48 | + t.propOpt('updatedAt', t.Number({format: 'u64'})), |
| 49 | + t.propOpt('tags', t.Array(t.str)), |
| 50 | +); |
| 51 | + |
| 52 | +/** |
| 53 | + * API response schema with discriminated unions |
| 54 | + */ |
| 55 | +export const ApiResponse = t.Or( |
| 56 | + t.object({ |
| 57 | + success: t.Const(true), |
| 58 | + data: t.any, |
| 59 | + timestamp: t.Number({format: 'u64'}), |
| 60 | + }), |
| 61 | + t.object({ |
| 62 | + success: t.Const(false), |
| 63 | + error: t.object({ |
| 64 | + code: t.String({format: 'ascii'}), |
| 65 | + message: t.str, |
| 66 | + }), |
| 67 | + timestamp: t.Number({format: 'u64'}), |
| 68 | + }), |
| 69 | +); |
| 70 | + |
| 71 | +/** |
| 72 | + * File metadata schema with binary data |
| 73 | + */ |
| 74 | +export const FileMetadata = t.Object( |
| 75 | + t.prop('name', t.str), |
| 76 | + t.prop('size', t.Number({format: 'u64', gte: 0})), |
| 77 | + t.prop('mimeType', t.str), |
| 78 | + t.prop('data', t.Binary(t.any)), |
| 79 | + t.prop('checksum', t.String({format: 'ascii', min: 64, max: 64})), |
| 80 | + t.prop('uploadedAt', t.Number({format: 'u64'})), |
| 81 | + t.propOpt('metadata', t.Map(t.str)), |
| 82 | +); |
| 83 | + |
| 84 | +/** |
| 85 | + * Configuration schema with maps and default values |
| 86 | + */ |
| 87 | +export const Configuration = t.Object( |
| 88 | + t.prop('environment', t.enum('development', 'staging', 'production')), |
| 89 | + t.prop( |
| 90 | + 'database', |
| 91 | + t.object({ |
| 92 | + host: t.str, |
| 93 | + port: t.Number({format: 'u16', gte: 1, lte: 65535}), |
| 94 | + name: t.str, |
| 95 | + }), |
| 96 | + ), |
| 97 | + t.prop('features', t.Map(t.bool)), |
| 98 | + t.prop('secrets', t.Map(t.str)), |
| 99 | + t.propOpt( |
| 100 | + 'logging', |
| 101 | + t.object({ |
| 102 | + level: t.enum('debug', 'info', 'warn', 'error'), |
| 103 | + output: t.str, |
| 104 | + }), |
| 105 | + ), |
| 106 | +); |
| 107 | + |
| 108 | +/** |
| 109 | + * Event data schema with tuples and coordinates |
| 110 | + */ |
| 111 | +export const Event = t.Object( |
| 112 | + t.prop('id', t.String({format: 'ascii'})), |
| 113 | + t.prop('type', t.enum('click', 'view', 'purchase', 'signup')), |
| 114 | + t.prop('timestamp', t.Number({format: 'u64'})), |
| 115 | + t.prop('userId', t.maybe(t.str)), |
| 116 | + t.prop('location', t.Tuple([t.Number({format: 'f64'}), t.Number({format: 'f64'})])), |
| 117 | + t.prop('metadata', t.Map(t.Or(t.str, t.num, t.bool))), |
| 118 | + t.propOpt('sessionId', t.str), |
| 119 | +); |
| 120 | + |
| 121 | +/** |
| 122 | + * Contact information schema with formatted strings |
| 123 | + */ |
| 124 | +export const ContactInfo = t.Object( |
| 125 | + t.prop( |
| 126 | + 'name', |
| 127 | + t.object({ |
| 128 | + first: t.String({min: 1}), |
| 129 | + last: t.String({min: 1}), |
| 130 | + }), |
| 131 | + ), |
| 132 | + t.prop('emails', t.Array(t.String({format: 'ascii'}), {min: 1})), |
| 133 | + t.prop('phones', t.Array(t.tuple(t.enum('home', 'work', 'mobile'), t.str))), |
| 134 | + t.propOpt( |
| 135 | + 'address', |
| 136 | + t.object({ |
| 137 | + street: t.str, |
| 138 | + city: t.str, |
| 139 | + country: t.String({format: 'ascii', min: 2, max: 2}), |
| 140 | + postalCode: t.str, |
| 141 | + }), |
| 142 | + ), |
| 143 | + t.propOpt('socialMedia', t.Map(t.String({format: 'ascii'}))), |
| 144 | +); |
| 145 | + |
| 146 | +/** |
| 147 | + * Database record schema with references |
| 148 | + */ |
| 149 | +export const DatabaseRecord = t.Object( |
| 150 | + t.prop('id', t.String({format: 'ascii'})), |
| 151 | + t.prop('createdAt', t.Number({format: 'u64'})), |
| 152 | + t.prop('updatedAt', t.Number({format: 'u64'})), |
| 153 | + t.prop('version', t.Number({format: 'u32', gte: 1})), |
| 154 | + t.prop('createdBy', t.Ref<typeof User>('User')), |
| 155 | + t.propOpt('updatedBy', t.Ref<typeof User>('User')), |
| 156 | + t.propOpt('deletedAt', t.Number({format: 'u64'})), |
| 157 | +); |
| 158 | + |
| 159 | +/** |
| 160 | + * Function type schema |
| 161 | + */ |
| 162 | +export const UserValidator = t.Function( |
| 163 | + t.object({ |
| 164 | + userData: t.any, |
| 165 | + strict: t.bool, |
| 166 | + }), |
| 167 | + t.object({ |
| 168 | + valid: t.bool, |
| 169 | + errors: t.Array(t.str), |
| 170 | + }), |
| 171 | + {title: 'User Validation Function'}, |
| 172 | +); |
| 173 | + |
| 174 | +/** |
| 175 | + * Streaming API schema |
| 176 | + */ |
| 177 | +export const EventStream = t.Function$( |
| 178 | + t.object({ |
| 179 | + filter: t.maybe(t.str), |
| 180 | + limit: t.maybe(t.Number({format: 'u32'})), |
| 181 | + }), |
| 182 | + t.Ref<typeof Event>('Event'), |
| 183 | + {title: 'Event Streaming Function'}, |
| 184 | +); |
| 185 | + |
| 186 | +/** |
| 187 | + * Complex nested schema |
| 188 | + */ |
| 189 | +export const ComplexNested = t.Object( |
| 190 | + t.prop( |
| 191 | + 'data', |
| 192 | + t.Map( |
| 193 | + t.Or( |
| 194 | + t.str, |
| 195 | + t.num, |
| 196 | + t.Array( |
| 197 | + t.Map( |
| 198 | + t.object({ |
| 199 | + key: t.str, |
| 200 | + value: t.Or(t.str, t.num, t.bool, t.nil), |
| 201 | + nested: t.maybe(t.Map(t.any)), |
| 202 | + }), |
| 203 | + ), |
| 204 | + ), |
| 205 | + ), |
| 206 | + ), |
| 207 | + ), |
| 208 | + t.prop( |
| 209 | + 'metadata', |
| 210 | + t.object({ |
| 211 | + version: t.str, |
| 212 | + schema: t.String({format: 'ascii'}), |
| 213 | + checksum: t.String({format: 'ascii'}), |
| 214 | + }), |
| 215 | + ), |
| 216 | +); |
| 217 | + |
| 218 | +/** |
| 219 | + * Basic primitive type schemas for backward compatibility |
11 | 220 | */ |
12 | 221 | export const primitiveSchemas = { |
13 | | - string: s.String(), |
14 | | - stringWithMinMax: s.String({min: 5, max: 10}), |
15 | | - number: s.Number(), |
16 | | - numberWithFormat: s.Number({format: 'u32'}), |
17 | | - numberWithRange: s.Number({gte: 0, lte: 100}), |
18 | | - boolean: s.Boolean(), |
19 | | - const: s.Const('fixed-value' as const), |
20 | | - any: s.Any(), |
| 222 | + string: t.str.getSchema(), |
| 223 | + stringWithMinMax: t.String({min: 5, max: 10}).getSchema(), |
| 224 | + number: t.num.getSchema(), |
| 225 | + numberWithFormat: t.Number({format: 'u32'}).getSchema(), |
| 226 | + numberWithRange: t.Number({gte: 0, lte: 100}).getSchema(), |
| 227 | + boolean: t.bool.getSchema(), |
| 228 | + const: t.Const('fixed-value' as const).getSchema(), |
| 229 | + any: t.any.getSchema(), |
21 | 230 | } as const; |
22 | 231 |
|
23 | 232 | /** |
24 | | - * Complex composite type schemas |
| 233 | + * Complex composite type schemas for backward compatibility |
25 | 234 | */ |
26 | 235 | export const compositeSchemas = { |
27 | | - simpleArray: s.Array(s.String()), |
28 | | - arrayWithBounds: s.Array(s.Number(), {min: 2, max: 5}), |
29 | | - simpleObject: s.Object([s.prop('id', s.String()), s.prop('name', s.String()), s.prop('active', s.Boolean())]), |
30 | | - objectWithOptionalFields: s.Object([ |
31 | | - s.prop('id', s.String()), |
32 | | - s.propOpt('name', s.String()), |
33 | | - s.propOpt('count', s.Number()), |
34 | | - ]), |
35 | | - nestedObject: s.Object([ |
36 | | - s.prop( |
37 | | - 'user', |
38 | | - s.Object([ |
39 | | - s.prop('id', s.Number()), |
40 | | - s.prop('profile', s.Object([s.prop('name', s.String()), s.prop('email', s.String())])), |
41 | | - ]), |
42 | | - ), |
43 | | - s.prop('tags', s.Array(s.String())), |
44 | | - ]), |
45 | | - tuple: s.Tuple([s.String(), s.Number(), s.Boolean()]), |
46 | | - map: s.Map(s.String()), |
47 | | - mapWithComplexValue: s.Map(s.Object([s.prop('value', s.Number()), s.prop('label', s.String())])), |
48 | | - union: s.Or(s.String(), s.Number(), s.Boolean()), |
49 | | - complexUnion: s.Or( |
50 | | - s.String(), |
51 | | - s.Object([s.prop('type', s.Const('object' as const)), s.prop('data', s.Any())]), |
52 | | - s.Array(s.Number()), |
53 | | - ), |
54 | | - binary: s.bin, |
| 236 | + simpleArray: t.Array(t.str).getSchema(), |
| 237 | + arrayWithBounds: t.Array(t.num, {min: 2, max: 5}).getSchema(), |
| 238 | + simpleObject: t.Object(t.prop('id', t.str), t.prop('name', t.str), t.prop('active', t.bool)).getSchema(), |
| 239 | + objectWithOptionalFields: t |
| 240 | + .Object(t.prop('id', t.str), t.propOpt('name', t.str), t.propOpt('count', t.num)) |
| 241 | + .getSchema(), |
| 242 | + nestedObject: t |
| 243 | + .Object( |
| 244 | + t.prop( |
| 245 | + 'user', |
| 246 | + t.Object(t.prop('id', t.num), t.prop('profile', t.Object(t.prop('name', t.str), t.prop('email', t.str)))), |
| 247 | + ), |
| 248 | + t.prop('tags', t.Array(t.str)), |
| 249 | + ) |
| 250 | + .getSchema(), |
| 251 | + tuple: t.tuple(t.str, t.num, t.bool).getSchema(), |
| 252 | + map: t.Map(t.str).getSchema(), |
| 253 | + mapWithComplexValue: t.Map(t.Object(t.prop('value', t.num), t.prop('label', t.str))).getSchema(), |
| 254 | + union: t.Or(t.str, t.num, t.bool).getSchema(), |
| 255 | + complexUnion: t |
| 256 | + .Or(t.str, t.Object(t.prop('type', t.Const('object' as const)), t.prop('data', t.any)), t.Array(t.num)) |
| 257 | + .getSchema(), |
| 258 | + binary: t.bin.getSchema(), |
55 | 259 | } as const; |
56 | 260 |
|
57 | 261 | /** |
|
0 commit comments