|
5 | 5 | */ |
6 | 6 |
|
7 | 7 | import {s} from '../schema'; |
| 8 | +import {t} from '../type'; |
8 | 9 | import {genRandomExample} from '@jsonjoy.com/json-random/lib/examples'; |
9 | 10 | import {RandomJson} from '@jsonjoy.com/json-random'; |
10 | 11 |
|
@@ -76,3 +77,213 @@ export const schemaCategories = { |
76 | 77 | composites: compositeSchemas, |
77 | 78 | all: allSchemas, |
78 | 79 | } as const; |
| 80 | + |
| 81 | +/** |
| 82 | + * User profile schema with nested objects and optional fields |
| 83 | + */ |
| 84 | +export const User = t |
| 85 | + .object({ |
| 86 | + id: t.str, |
| 87 | + name: t.object({ |
| 88 | + first: t.str, |
| 89 | + last: t.str, |
| 90 | + }), |
| 91 | + email: t.String({format: 'ascii'}), |
| 92 | + age: t.Number({gte: 0, lte: 150}), |
| 93 | + verified: t.bool, |
| 94 | + }) |
| 95 | + .opt('avatar', t.String({format: 'ascii'})); |
| 96 | + |
| 97 | +/** |
| 98 | + * Product catalog schema with arrays and formatted numbers |
| 99 | + */ |
| 100 | +export const Product = t.Object( |
| 101 | + t.prop('id', t.String({format: 'ascii'})), |
| 102 | + t.prop('name', t.String({min: 1, max: 100})), |
| 103 | + t.prop('price', t.Number({format: 'f64', gte: 0})), |
| 104 | + t.prop('inStock', t.bool), |
| 105 | + t.prop('categories', t.Array(t.str, {min: 1})), |
| 106 | + t.prop('tags', t.Array(t.str)), |
| 107 | + t.propOpt('description', t.String({max: 1000})), |
| 108 | + t.propOpt('discount', t.Number({gte: 0, lte: 1})), |
| 109 | +); |
| 110 | + |
| 111 | +/** |
| 112 | + * Blog post schema with timestamps and rich content |
| 113 | + */ |
| 114 | +export const BlogPost = t.Object( |
| 115 | + t.prop('id', t.str), |
| 116 | + t.prop('title', t.String({min: 1, max: 200})), |
| 117 | + t.prop('content', t.str), |
| 118 | + t.prop('author', t.Ref<typeof User>('User')), |
| 119 | + t.prop('publishedAt', t.Number({format: 'u64'})), |
| 120 | + t.prop('status', t.enum('draft', 'published', 'archived')), |
| 121 | + t.propOpt('updatedAt', t.Number({format: 'u64'})), |
| 122 | + t.propOpt('tags', t.Array(t.str)), |
| 123 | +); |
| 124 | + |
| 125 | +/** |
| 126 | + * API response schema with discriminated unions |
| 127 | + */ |
| 128 | +export const ApiResponse = t.Or( |
| 129 | + t.object({ |
| 130 | + success: t.Const(true), |
| 131 | + data: t.any, |
| 132 | + timestamp: t.Number({format: 'u64'}), |
| 133 | + }), |
| 134 | + t.object({ |
| 135 | + success: t.Const(false), |
| 136 | + error: t.object({ |
| 137 | + code: t.String({format: 'ascii'}), |
| 138 | + message: t.str, |
| 139 | + }), |
| 140 | + timestamp: t.Number({format: 'u64'}), |
| 141 | + }), |
| 142 | +); |
| 143 | + |
| 144 | +/** |
| 145 | + * File metadata schema with binary data |
| 146 | + */ |
| 147 | +export const FileMetadata = t.Object( |
| 148 | + t.prop('name', t.str), |
| 149 | + t.prop('size', t.Number({format: 'u64', gte: 0})), |
| 150 | + t.prop('mimeType', t.str), |
| 151 | + t.prop('data', t.Binary(t.any)), |
| 152 | + t.prop('checksum', t.String({format: 'ascii', min: 64, max: 64})), |
| 153 | + t.prop('uploadedAt', t.Number({format: 'u64'})), |
| 154 | + t.propOpt('metadata', t.Map(t.str)), |
| 155 | +); |
| 156 | + |
| 157 | +/** |
| 158 | + * Configuration schema with maps and default values |
| 159 | + */ |
| 160 | +export const Configuration = t.Object( |
| 161 | + t.prop('environment', t.enum('development', 'staging', 'production')), |
| 162 | + t.prop( |
| 163 | + 'database', |
| 164 | + t.object({ |
| 165 | + host: t.str, |
| 166 | + port: t.Number({format: 'u16', gte: 1, lte: 65535}), |
| 167 | + name: t.str, |
| 168 | + }), |
| 169 | + ), |
| 170 | + t.prop('features', t.Map(t.bool)), |
| 171 | + t.prop('secrets', t.Map(t.str)), |
| 172 | + t.propOpt( |
| 173 | + 'logging', |
| 174 | + t.object({ |
| 175 | + level: t.enum('debug', 'info', 'warn', 'error'), |
| 176 | + output: t.str, |
| 177 | + }), |
| 178 | + ), |
| 179 | +); |
| 180 | + |
| 181 | +/** |
| 182 | + * Event data schema with tuples and coordinates |
| 183 | + */ |
| 184 | +export const Event = t.Object( |
| 185 | + t.prop('id', t.String({format: 'ascii'})), |
| 186 | + t.prop('type', t.enum('click', 'view', 'purchase', 'signup')), |
| 187 | + t.prop('timestamp', t.Number({format: 'u64'})), |
| 188 | + t.prop('userId', t.maybe(t.str)), |
| 189 | + t.prop('location', t.Tuple([t.Number({format: 'f64'}), t.Number({format: 'f64'})])), |
| 190 | + t.prop('metadata', t.Map(t.Or(t.str, t.num, t.bool))), |
| 191 | + t.propOpt('sessionId', t.str), |
| 192 | +); |
| 193 | + |
| 194 | +/** |
| 195 | + * Contact information schema with formatted strings |
| 196 | + */ |
| 197 | +export const ContactInfo = t.Object( |
| 198 | + t.prop( |
| 199 | + 'name', |
| 200 | + t.object({ |
| 201 | + first: t.String({min: 1}), |
| 202 | + last: t.String({min: 1}), |
| 203 | + }), |
| 204 | + ), |
| 205 | + t.prop('emails', t.Array(t.String({format: 'ascii'}), {min: 1})), |
| 206 | + t.prop('phones', t.Array(t.tuple(t.enum('home', 'work', 'mobile'), t.str))), |
| 207 | + t.propOpt( |
| 208 | + 'address', |
| 209 | + t.object({ |
| 210 | + street: t.str, |
| 211 | + city: t.str, |
| 212 | + country: t.String({format: 'ascii', min: 2, max: 2}), |
| 213 | + postalCode: t.str, |
| 214 | + }), |
| 215 | + ), |
| 216 | + t.propOpt('socialMedia', t.Map(t.String({format: 'ascii'}))), |
| 217 | +); |
| 218 | + |
| 219 | +/** |
| 220 | + * Database record schema with references |
| 221 | + */ |
| 222 | +export const DatabaseRecord = t.Object( |
| 223 | + t.prop('id', t.String({format: 'ascii'})), |
| 224 | + t.prop('createdAt', t.Number({format: 'u64'})), |
| 225 | + t.prop('updatedAt', t.Number({format: 'u64'})), |
| 226 | + t.prop('version', t.Number({format: 'u32', gte: 1})), |
| 227 | + t.prop('createdBy', t.Ref<typeof User>('User')), |
| 228 | + t.propOpt('updatedBy', t.Ref<typeof User>('User')), |
| 229 | + t.propOpt('deletedAt', t.Number({format: 'u64'})), |
| 230 | +); |
| 231 | + |
| 232 | +/** |
| 233 | + * Function type schema |
| 234 | + */ |
| 235 | +export const UserValidator = t.Function( |
| 236 | + t.object({ |
| 237 | + userData: t.any, |
| 238 | + strict: t.bool, |
| 239 | + }), |
| 240 | + t.object({ |
| 241 | + valid: t.bool, |
| 242 | + errors: t.Array(t.str), |
| 243 | + }), |
| 244 | + {title: 'User Validation Function'}, |
| 245 | +); |
| 246 | + |
| 247 | +/** |
| 248 | + * Streaming API schema |
| 249 | + */ |
| 250 | +export const EventStream = t.Function$( |
| 251 | + t.object({ |
| 252 | + filter: t.maybe(t.str), |
| 253 | + limit: t.maybe(t.Number({format: 'u32'})), |
| 254 | + }), |
| 255 | + t.Ref<typeof Event>('Event'), |
| 256 | + {title: 'Event Streaming Function'}, |
| 257 | +); |
| 258 | + |
| 259 | +/** |
| 260 | + * Complex nested schema |
| 261 | + */ |
| 262 | +export const ComplexNested = t.Object( |
| 263 | + t.prop( |
| 264 | + 'data', |
| 265 | + t.Map( |
| 266 | + t.Or( |
| 267 | + t.str, |
| 268 | + t.num, |
| 269 | + t.Array( |
| 270 | + t.Map( |
| 271 | + t.object({ |
| 272 | + key: t.str, |
| 273 | + value: t.Or(t.str, t.num, t.bool, t.nil), |
| 274 | + nested: t.maybe(t.Map(t.any)), |
| 275 | + }), |
| 276 | + ), |
| 277 | + ), |
| 278 | + ), |
| 279 | + ), |
| 280 | + ), |
| 281 | + t.prop( |
| 282 | + 'metadata', |
| 283 | + t.object({ |
| 284 | + version: t.str, |
| 285 | + schema: t.String({format: 'ascii'}), |
| 286 | + checksum: t.String({format: 'ascii'}), |
| 287 | + }), |
| 288 | + ), |
| 289 | +); |
0 commit comments